Instantly share code, notes, and snippets.

What would you like to do?
Audacity Plugin for Keeping Second Track Volume Around First Track
;nyquist plug-in
;version 4
;type effect
;name "Test"
;action "Testing..."
;codetype lisp
;;debugflags trace ;Set debugging yes or no (; = yes, ;; = no)
(defconstant DB_DIFF 6) ;The level of difference in loudness you want
(defconstant CHANGE_THRESHOLD 0.01) ; the level of change needed to be "different" when converting from a song to an array
;Now a bit of a misnomer, abs-lowpass was meant to approximate the loudness of a song
;Now it works by squaring the wave, doubling it, taking the square root, and passing through a lowpass filter
;Essentially, it takes the absolute value times the square root of 2 passed through the filter
(defun abs-lowpass (sound &optional (freq 1))
"Gets the result of running the absolute values of the samples filtered through a lowpass filter"
(if (arrayp sound) ;Check for stereo, if so call functions separately and group
(abs-lowpass (aref sound 0) freq)
(abs-lowpass (aref sound 1) freq))
(lp (snd-sqrt (prod sound sound 2)) freq) ;If mono, approximate loudness
;Converts a sound wave into something pwl-list can use
(defun make-piecewise (sound &optional (THRESHOLD CHANGE_THRESHOLD))
"Returns an array of the form time1, value1, time2, value2, ... for the times sound changes loudness"
(if (arrayp sound) ;Check for stereo or mono
(vector ;If stereo, split and recombine
(make-piecewise (aref sound 0) THRESHOLD)
(make-piecewise (aref sound 1) THRESHOLD))
(let ((s (snd-copy sound)) ;If mono, copy the sound and process it
(r '()) ;Get our empty list for filling
(p 99999) ;Choose an absurd value to force the first sample to be far off
(n 0) ;Set our counter to 0
(c 0)) ;Set our current value to 0
(loop ;Iterate over the samples
(if (eq (setf c (snd-fetch s)) nil) ;If this is the last one
(return r) ;Then return
(when (> (abs (- p c)) THRESHOLD) ;Otherwise, if we have passed the threshold
(setf p c) ;Update the previous level
(setf r (append r (list (/ n LEN) c)))) ;And add the sample to the list
(setf n (+ 1 n)) ;Increment our counter, for timing purposes
(setf r (append r (list 1 p))) ;Add the very end of the list, since audacity wants the last point at 0
(setf r (append r (list 1))) ;Define the last point, just '(1)
;Makes sound roughly dif db quieter than a track stored as a list in arr
(defun quiet-track (sound arr dif)
(let* ((s1 (control-srate-abs *SOUND-SRATE* (pwl-list (aref arr 0)))) ;Convert the list back to sound
(s2 (control-srate-abs *SOUND-SRATE* (pwl-list (aref arr 1))))
(ars (vector s1 s2)) ;Make it stereo
(difference (vector dif dif))) ;Used for making us quieter by dif decibels
;Now we actually modify the sound by multiplying the linear difference in db, offset by dif, by the sound wave
(prod (db-to-linear (diff (linear-to-db ars) (sum (linear-to-db (abs-lowpass sound)) difference))) sound)
;Processing code for Track1
(defun Track1 ()
(setf arr (make-piecewise (abs-lowpass *TRACK*))) ;Get the loudness of the first track and store it as a list
(putprop '*SCRATCH* arr 'jsjwt) ;Put that list in the global variable *SCRATCH* so the second track can use it
*TRACK* ;Return the same sound so nothing changes
;Processing code for Track2
(defun Track2 ()
(setf t1sl (get '*SCRATCH* 'jsjwt)) ;Get the track one loudness list
(remprop '*SCRATCH* 'jsjwt) ;clean up the global variable *SCRATCH*
(quiet-track *TRACK* t1sl DB_DIFF) ;Now make us quiter than track 1
;This is the code that gets run below
(if (arrayp *TRACK*) ;Curse the user if we aren't modifying a stereo track
(if (eq (get '*TRACK* 'INDEX) 1) ; Test if we are on the first track
(Track1) ;If so, run Track1
(Track2));Else run Track2
(print "Not Stereo!"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment