Working with samples

Hi all,

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.

Do you actually want to delete every 1000th sample and end up with 99000 samples OR do you still want every 1000th sample to still be there but have a value of Zero (0).

Resampling 100000 samples to 99000 would just resample and leave 99000 samples sounding same as before if played at correct rate…???

Hi @AudyMusik

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))))

(process *track*)

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.

1 Like

Hi Steve,
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.

Thanks very much.


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.

1 Like

I think Audacity is still limited to 1 CPU core, (even if you have 4).

1 Like

Impressive ! ! … I didn’t know stuff like this was going on in the background ! !

That seems a bit slow for just zeroing a few samples.
How long does this take on your machine?

;maxlen 1000000

(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)

No idea, Audacity immediately crashed.


Running the AppImage if that makes any difference.

Which AppImage version? (There have been a couple of recent versions that crash).

Details below:

I must add that it’s been pretty stable.
Other than the few crashes caused by me when I was learning how to code LADSPA plugins,
this is really the only other time that I can remember.

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.

The version number will be something like:
Audacity 3.1.3
You will find the version number in “Help menu > About Audacity”.

Which code are you referring to? (The last code that I posted does not have a variable called num, but I thought that was what you were referring to.)

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).




As regards, the code, I was referring to the original code you supplied.
I didn’t know about (truncate len), so did it the other way, (round (abs ( …
I have now tried both ways and yes, same result.

You also asked about how long it takes for your last code snippet,
pretty much instantaneous.
It does not crash as long as the samples are less than 1M (approx 22 secs at 44.1 KHz).

The moment they exceed that, irrespective of which code I use, it will crash.

The ;maxlen line should prevent crashing as it should limit the number of samples. However, be aware of this bug: Nyquist `;maxlen` setting persists beyond the current effect instance · Issue #4561 · audacity/audacity · GitHub

Thanks for the heads up Steve.
So I will rather do a conditional check in the code for sample num greater than 1M and also zero.