Old user ( Paul2) but since I have chnged email account and the new forum does not accept old passwords,
I have created a new user name Paul-2.
EDIT: My old account has been restored.
Anyways, I know Nyquist is not really made for working at the sample level, but if I really need to, which is the
best way (or let’s say the least bad way) to do this?
I understand about the speed penalties.
Example, let’s say I want to delete (make 0) every 1000th sample in a selection of 100 000 samples.
In C, it’s pretty easy, create a loop and once the count is reached, manipulate the sample. Reset the loop and repeat.
How could this be achieved in Xlisp?
The above is just a simple example but serves to illustrate what I’m after.
Sorry wrong terminology on my part, don’t want to delete the samples, want to make them zero value.
Again, this is just a very simple example, in reality I may want to make say a nth order moving average filter
or something else, which makes things easier if I can code at the sample level.
No, Lisp wouldn’t be my programming language, I have used C etc in the past but not in Audacity…??
I would have thought the process would be similar… put your 100000 is an Array or List and count your way through them…?? I would be interested in the result and will keep checking to see what others can fill in.
For very short audio clips / audio selections, working with individual samples is very easy in Nyquist. It becomes more complex with longer selections (more than about a million samples).
For a very short selection, say 100,000 samples, you can capture the samples in an array:
(setf num 100000)
(setf step num)
(setf ar (snd-fetch-array *track* num step))
You can then loop through the array, making whatever changes you require:
(dotimes (i num)
(if (= (rem i 100) 0)
(setf (aref ar i) 0)))
Then to get a sound back from the array:
(snd-from-array 0 *sound-srate* ar)
For longer selections you can repeatedly take chunks of audio as arrays. For example, a very inefficient way to amplify a mono track by a ratio of 0.8:
(setf blen 10000) ; block length
(setf step blen)
(setf blocklen (/ blen *sound-srate*))
(defun process-block (ar)
;; Inefficient way to amplify by a ratio of 0.8
(dotimes (j blen ar)
(setf (aref ar j) (* (aref ar j) 0.8))))
(defun process (sig)
(setf out (s-rest 0))
(setf t0 0.0)
(setf blocks (truncate (/ len blen)))
(dotimes (i blocks out)
(setf ar (snd-fetch-array sig blen step))
(setf ar (process-block ar))
(setf out (sim out
(at-abs t0 (cue (snd-from-array t0 *sound-srate* ar)))))
(setf t0 (+ t0 blocklen))))
Note that the sound is reassembled in memory, and Nyquist is limited to about 2 GB RAM, so this is not suitable for very long tracks.
A better way is to create an iterator object, but Nyquist’s OOP is much harder to get to grips with. There’s an example of this approach in the Sample Data Export plug-in. One reason that this approach is better for DSP (apart from being not so slow) is that it avoids storing the entire sound in RAM.
That is exactly what I needed as a starting point,
the “snd-fetch-array” and “snd-from-array”.
Sometimes it’s much easier just to “prototype” an idea using Nyquist
than going thru the whole process of compiling a LADSPA plugin.
Once I get the idea proven/sort-of-working, I then make a LADSPA plugin.
With your code as a starting point, I set the array to 1 million samples and my sample rate was 44.1 KHz, hence 22 secs of audio.
Took a tad over 2 seconds to process on an i7 laptop with 16GB RAM running Audacity 3.1.3 on Debian, both 64 bit.
You can clearly see the result on the bottom track where the samples go to zero.
Congratulations. Looks good.
Yes Nyquist is great for prototyping. I prototyped Audacity’s Pink and Brownian noise generators, the Bass and Treble effect, and the Distortion effect, with Nyquist before converting them to built-in effects.
That seems a bit slow for just zeroing a few samples.
How long does this take on your machine?
(defun zero-n (sig period)
(let* ((ln (truncate len))
(ar (snd-fetch-array sig ln ln))
(srate (snd-srate sig)))
(do ((i period (+ i period)))
((>= i ln))
(setf (aref ar i) 0))
(snd-from-array 0 srate ar)))
;; Set every 1000th sample to zero
(multichan-expand #'zero-n *track* 1000)
(The ;maxlen line limits the length of the selection to 1 million samples)
I must also mention that even though VST (for Linux) and LV2 support are enabled, I have never successfully
managed to get any VST or LV2 plugin to work, but that is something for another topic/thread.
Going back to the code that crashes Audacity, I think I’ve found the solution.
For selections shorter than 1M samples, if there are less than that amount of samples in the track…crash.
What seems to fix it is:
(setf num (round (abs (* (get '*selection* 'end) *sound-srate*))))
Then as a safety check, if variable “num” is greater than 1M samples, warn and bail out gracefully with a print statement.
This is only whilst prototyping since I will ultimately be converting to a LADSPA plugin.
That should return approximately the same value as the Nyquist value of LEN. It is a useful workaround if you need the length of the selection (in samples) in a ;type generate plug-in (len is not set in generators because Nyquist time is independent of Audacity time). In other types of plug-in, len should be preferred.
Implementation note: len returns a float even though it will always be an integer value, so you can get the integer value as (truncate len).