This sets the code to run as a “process” type of plug-in (an “Effect”), and creates the two controls
;type process
;control res "Time resolution" float "seconds" 0.1 0.01 10
;control mode "Follow peak or RMS level" choice "Peak,RMS" 0
This is a function that averages two channels of a stereo sound to create a mono sound
(defun mono (sig)
(setf sig (s-abs sig))
(if (arrayp sig)
(mult 0.5 (sum (aref sig 0)(aref sig 1)))
sig))
This sets “step” to “Time resolution” in samples (“res” was in seconds)
(setf step (truncate (* res *sound-srate*)))
The “mode” control has a value of 0 for the first choice (peak) or 1 for the second choice (RMS)
The SND_AVG function requires a value of 1 (op-average) or 2 (op-peak)
(setf op (- 2 mode))
Test for some conditions (if something is true, do something)
(cond
Just basic error checking
((> (get-duration 1) 1800)
(format nil "Error.~%~%Selection too long.~%~
Reduce the selection to 30 mins maximum"))
((< (length (get '*selection* 'tracks)) 2)
(format nil "Error.~%~%This effect requires at least 2 tracks to be selected.~%~
The amplitude envelope is copied from the first~%~
selected track, then applied to subsequent tracks."))
((< (get-duration 0.5) res)
(format nil "Error.~%~%The 'Time Resolution' should be considerably~%~
shorter than the length of the selection.~%~
The absolute maximum allowed 'Time Resolution' is~%~
half of the selection length."))
By this point, no errors have been found, and this is the first track, so grab the envelope and jot it down on a scratch-pad. Nyquist plug-in effects process one track at a time, and nearly everything is reset at the end of processing a track. The exception is “scratch” which can retain its value from one track to the next, so we temporarily put the envelope into scratch. (More about this envelope later).
((= (get '*track* 'index) 1)
(setf *scratch*
(snd-copy (snd-avg (mono *track*) step step op)))
; return *track* to prevent audio from being released from *scratch*.
*track*) ;*track*)
“T” means “true”. This is the final part of the COND statement, so if none of the other tests have been “true”, then do this.
Basically what we are doing is amplifying (“multiplying”) the selected track (which is NOT the first track) by the envelope. Because our envelope does not quite start from the beginning of the track, the position of the envelope is padded a little at the start and shifted slightly to the right so that the peaks in the envelope will line up with peaks in the first track. Then, if this is the final selected track, we delete scratch so that it is not hanging around in RAM.
(T
(let ((env *scratch*)
(offset (* res (/ (1+ mode) 2.0)))
(initial-amp (snd-fetch (snd-copy *scratch*))))
; release *scratch* when we're finished
(if (= (get '*track* 'index)(length (get '*selection* 'tracks)))
(setf *scratch* '*unbound*))
(mult *track*
(sim
(abs-env (pwlv 0 offset initial-amp))
(at-abs offset (cue env)))))))
Creating the envelope:
(setf *scratch*
(snd-copy (snd-avg (mono *track*) step step op)))
; return *track* to prevent audio from being released from *scratch*.
*track*) ;*track*)
Looks like I corrected an error here. Anything after a semi-colon is treated as a “comment” and is ignored by Nyquist, so ignore that final ;track)
(setf *scratch*
(snd-copy (snd-avg (mono *track*) step step op)))
; return *track* to prevent audio from being released from *scratch*.
*track*)
We are setting scratch to have the value of (snd-copy (snd-avg (mono track) step step op)))
In Nyquist, a “value” may be a number, or a character, or a “string” (text), or a “sound” or one of several other data types. In this case, the value of scratch will be a sound (though one with a very low sample rate).
scratch should retain its value, but due to memory management in Nyquist, if scratch is given a “sound” value, we have to reference that sound when Nyquist returns at the end of the track, otherwise the sound is deleted. With other data types you don’t need to do that.
We use SND-COPY to prevent the track from being damaged by SND-AVG.
If, for example, “op” = 2 (the same as “op-peak”), then (snd-avg (mono track) step step op) first converts track (the track audio) to mono, then finds the peak value of the first “step” samples, saves that to scratch, the moves forward by “step” samples and finds the peak level of the next “step” samples, and so on for the rest of the track. scratch thus ends up as a “sound” with one sample value for each “step” samples of the original track. In other words, it follows the peaks of the original track.
When we multiply the second track by scratch, because scratch has a very low sample rate, it is still the same length as the original selection, even though it has far less samples. Where the sound of scratch has a value of 1 (0 dB), the second track is amplified (multiplied) by 1, so clearly it remains at its original level. Where scratch has a value of 0.5, the second track is amplified by 0.5 (half its original level). Thus the second track as amplified by an amount that follows the contour of the first track.
Does that help?