Well, I sent this topic to the mailing list but I did some research myself and found that Nyquist is better equipped for higher level operations than for sample-by-sample processing. However this particular idea should be possible with the following functions:
This thread gives a great start:
Especially note the framework code for sample-by-sample processing which I’ll cite here:
;name "DSP Effect..."
;action "Performing DSP Effect..."
;control dummy "" int "" 0 0 0
;; the dummy slider above is only to open a effect window
;; where you can press the "Debug" button in case of trouble.
;; Just write TWO semicolons at the beginning of the "control"
;; line if you want to disable the effect window temporarily.
;; define a dsp class
(setf dsp-class (send class :new '(copy-of-sound)))
;; initial function of dsp class
(send dsp-class :answer :isnew '(sound)
'((setf copy-of-sound (snd-copy sound))))
;; method to be executed with every call to dsp-class
(send dsp-class :answer :next '()
'((let ((current-sample (snd-fetch copy-of-sound)))
;; "cond" checks for end-of-samples condition
;; replace the following line with your own function(s)
(* 0.5 current-sample)
;; define a dsp function for mono signals
(defun dsp (sound)
(setf obj (send dsp-class :new sound))
(snd-fromobject (snd-t0 sound) (snd-srate sound) obj)))
;; add automatic handling of mono/stereo tracks. Processes both stereo
;; channels one after the other. To process both channels simultaneously
;; or whole blocks of samples the object code above needs to be rewritten.
(if (arrayp s)
(dsp (aref s 0))
(dsp (aref s 1)))
As I said, I personally can’t program in Nyquist/Lisp but using the above code it should be straightforward. Here I’ll attempt to outline the code structure I have in mind for the “inversed polarity fixer”. Being inexperienced with the language I may make mistakes, so correct me where I’m wrong.
**Current-sample is a function that returns the numerical value of the current sample, that is the y which I used in my previous posts. We also need a symbol (Lisp-variable, right?) that holds the y-value of the sample just before the one in current-sample, because both values are required to calculate the Δ_y_ which is used to identify a clipping area by its large slope. I’ll call this symbol previoussample and I suggest to copy current-sample’s value to previoussample right before switching to the next sample routine.
Here’s the code structure I have in my head, but it may be quite wrong:**
1 WHILE currentsample
2 IF (currentsample-previoussample) < limit
3 SET peakheight = previoussample
4 WHILE (currentsample-previoussample) < -limit
5 SET currentsample = peakheight
6 NEXTSAMPLE... somehow
9 return the sample unmodified
Running you through it:
1 and 12 ensure the routine is carried out from the first to the last selected audio sample.
2 checks the Δ_y_ which I mentioned before, used to find a large negative slope (limit being a negative, user-set variable). If more negative than limit the modification code from 3 to 7 is run, otherwise (8) nothing is done and we go on with the next sample (9).
3 writes the y-value of the last ‘normal’ sample (see picture above) to a storage symbol because it will be used to set the next samples, thus creating the straight red line (see picture in previous post). This value comes from previoussample but must be stored because previoussample will changes value when proceeding to the next sample.
4-7 sets the negative clipping samples to the highest normal sample known, thus creating a ‘normal’ positive clipping line (the red line) that can be dealt with by regular clip fixing plugins. When currentsample-previoussample (the Δ_y_) suddenly gets mighty positive, the end of the clip is reached and the action is stopped.
How to tell the script to go to the NEXTSAMPLE, i.e. read a new sample value with current-sample? How does this fit in the Nyquist DSP framework code I cited above? Where in the code should the previoussample symbol be set? Can I also remember a sample’s location and go back to it, instead of just going to the next… next…? The latter would allow an observation-first principle, where we first check if there’s an end to the negative clipping area (with high slope) before changing any sample values, instead of rushing on as soon as we find the start and looking for the end on the way.
Some additional thoughts on user parameters.
limit: as seen in the pseudo-code. Any number from -2 to 0 that determines the required shift between two samples for the pair to be identified as starting point of a clip. It’s -2 to 0 because Audacity waves are put on a scale between -1 and 1, making the maximum drop -2. Using a -2 limit means the plugin does nothing, using a 0 limit makes it go nuts.
maxduration: amount of samples that each negative clipping area is allowed to contain. In practice this defines the maximum amount of samples that the algorithm will look ahead to find the very high slope (marking the end of the clip) after having found the very low slope (start of the clip). Not implemented in the pseudo-code because this fits in the observation-first principle as described above.
Hopefully this all is specific enough for someone to work with, otherwise I may try doing it myself. But either way I’ll need some advice though