Plugin request or maybe just a hint in the correct direction

Hi all

I’m completely new to this overall topic of audacity and to be honest and I thought that my “demand” was easy to fulfill, but it turned out that it’s not as easy as I expected.
I want to pan (or fade) the left and right channel of a stereo audiotrack to switch every x seconds, where x could be something between 1 an 10 seconds.
so “left-center-right-center-left-center…”
I tried several plugins already like fade in, fade out, or pan random and the butterfly plugins, but non of them provided the result I would like to have, or at least as “easy”.I could work with fade in, fade out, by selecting every 2 seconds of the signal and making a fade out on the one and fade in on the other, but this would last hours to do so:(
Random Pan is going in the right direction, but it’s random… I also tried to “read” the nyquist code of this plugin, but I struggle to find out where the “randomness” is coming from and to change it according to my needs, to pan on a fixed time.
so maybe someone can point me to the right direction, either with an existing plugin or a way, to change the random pan according to my needs. The other option would be if someone would be so kind and provide a plugin for this :blush: :blush: :unamused:

There is a plug-in called “Panning (LFO)” that is similar to that, but the panning is regular. Missing features - Audacity Support

I think that this will do what you want, but if you would like to modify it in any way, please ask and we should be able to help.

That plug-in was written by David R Sky (who sadly passed away in 2009). He wrote a lot of the early Nyquist plug-ins for Audacity, but many of his plug-ins are not particularly easy to read.

The code that creates the randomness is coming from here:

(setq signal (sum 0.5 (mult 0.5 
(normalize maxwidth (lowpass2 (noise) maxspeed)))))

In particular is the function (noise) Nyquist Functions
If you select part of a track, open the “Nyquist Prompt” (in the Effect menu) and enter the code:

(noise)

you will see that it creates random (white) noise.
The function lowpass2 is a low-pass filter. It reduces high frequencies from the sound, which in this case is the noise.
Try this code with various values for “frequency”:

(setq frequency 100)
(lowpass2 (noise) frequency)

You will see that the smaller the “frequency” number, the lower the frequency of the noise, but also the smaller the output waveform.
In order to give a big enough waveform, it is normalized. Simply put, the noise is amplified to a suitable level.
Amplification is produced by multiplying the sound by a “gain” factor.
You can see this work by trying different values for “gain” in the following code:

(setq frequency 100)
(setq gain 5.5)
(setf lf-noise (lowpass2 (noise) frequency))
(mult lf-noise gain)

Does that give you some insight to how the “random” part works?

thank you very much for pointing out the Panning LFO plugin, it does nearly exactly what I was looking for. I’ll try to review the code a little bit, to better understand the relation between the hz and “real” secconds between the switchover. I’m not sure if it was just me using the settings wrong, but is there also the option to define how long the sound should stay on each side after the pan? like “switchovertime: 6 seconds”, “remaintime” 2 seconds…
Again thank you for your patience with a total beginner in this area :smiley:, I just want to help a girlfriend of mine a little bit, who is even less “familiar” with the stuff at all :slight_smile:

In that plug-in, no, but if linear panning is OK then it would be quite easy to code.

The underlying mechanism for panning is to push the sound over to one side of the stereo mix and then over to the other side.
As an example, if you have a stereo track and you want it to be panned hard left…

Stereo tracks are passed to Nyquist as an array of 2 sounds. The variable (symbol) used for this array is “S” (Nyquist is not case sensitive so it can be upper-case or lower-case “S”).
The first element in the array is the left channel. The second element is the right channel.
The elements of an array are numbered from 0 (zero).

To access the element of an array, you would use the function AREF.

So the left channel of the array “S” can be accessed with (aref s 0) and the right channel with (aref s 1).

In order to return a stereo sound to Audacity, an array of two sounds must be returned.
The easiest way to do that is with the function VECTOR.
Thus, to swap the left and right channels of a stereo track you can use:

(vector (aref s 1)(aref s 0))

In the simplest case, if we want to pan a stereo track to the left, we can simply silence the right channel.
An easy way to make a sound silent is to multiply it by zero.

(vector (aref s 0) (mult (aref s 1) 0))

Similarly we can pan all the way to the right:

(vector (mult (aref s 0) 0) (aref s 1))

In order to gradually pan from one side to another, we need our “multiplication factor” to vary over time. We can do this by creating a control signal.

Here we run into a little peculiarity of Nyquist in Audacity. For effects, time is, by default, relative to the length of the selection. Thus a duration of “1” is the length of the selection. We can override that behaviour if we need to (and we will later) by specifying that we want to use “absolute” time rather than relative to the selection, but for now let’s stick with relative to the selection.

There are a series of functions called “piece-wise linear approximations” (Nyquist Functions)
We don’t need to consider all of the variations right now - for this example we will just use the PWLV variant.

PWLV can create a control signal from a series of coordinates (“break points”) in a similar fashion to “Envelope Points” when using the Envelope Tool in Audacity. Each “break point” has a time and a value. The control signal extrapolates from one point to the next in a straight line (hence linear approximation).
For PWLV, each break point has a time, followed by a value (amplitude), except that the initial time is assumed to be zero.
Thus to create an “envelope” that goes from zero at time = zero to 1 at time = 1:

(pwlv 0 1 1)

That’s a tricky example because it’s all 0’s and 1’s, so let’s change it a little and make it go from 0.1 at time zero to 0.9 at time = 1

(pwlv 0.1  1  0.9)

So then we can multiply our sound by the control signal. First we assign a symbol (name) to the control signal, then multiply the sound by the control signal.

(setf envelope (pwlv 0.1 1 0.9))   ; "envelope" is the symbol we have used for the control signal
(vector
  (mult (aref s 0) envelope)
  (mult (aref s 1) envelope))

That’s fine as far as it goes, but to get a panning effect, we want the right channel to fade in the opposite direction.
For a linear pan from 90% right to 90% left.

(setf left-channel (pwlv 0.1 1 0.9))
(setf right-channel (pwlv 0.9 1 0.1))
(vector
  (mult (aref s 0) left-channel)
  (mult (aref s 1) right-channel))

or for a linear pan the other way:

(setf left-channel (pwlv 0.9 1 0.1))
(setf right-channel (pwlv 0.1 1 0.9))
(vector
  (mult (aref s 0) left-channel)
  (mult (aref s 1) right-channel))

— more to follow —

Where were we up to?
Ah yes, we’ve panned a stereo track from one side to the other. Does that all make sense so far?

So now you want to pan over 6 seconds, stay at that position for 2 seconds, then pan back … and so on.

When we previously used PWLV, we defined the break point times relative to the selection length. If we now want to specify the times in seconds, we can either calculate the relative time from the number of seconds as a proportion of the selection length, or, easier, we specify that we want to use “absolute time”. For the latter option there is a function ABS-ENV.

When we multiply two sounds (or a sound and a control signal), the length of the result is the shorter of the two sounds.

Try applying this code to a mono track that is longer than 10 seconds:

(setq env (abs-env (pwlv 1 2 1 3 0 5 0.1 10 1)))
(mult s env)

In this case “S” is a “sound”. Audacity sends mono tracks to Nyquist as a “sound” and stereo tracks as “an array of 2 sounds”.

Because we have wrapped the PWLV function in ABS-ENV, the times are in seconds, thus you should see that the result of the code is that:
From 0 to 2 seconds, the amplitude remains at 100 % (linear value 1.0)
From 2 to 3 seconds the amplitude fades to silence.
From 3 to 5 seconds the amplitude rises to 10 % (0.1 linear)
From 5 to 10 seconds the amplitude rises from 10 % to 100 % (0.1 to 1.0)
At 10 seconds the track stops (we have reached the end of the control signal).

What we want to do now is to create a control signal that rises from 0 to 1 over 6 seconds, stays at 1 for 2 seconds, falls back to 0 over the next 6 seconds, stays there for 2 seconds… and so on.
If the control signal is as long, or longer than the selected audio, then we can apply the envelope to the audio and the output will be the same length as the audio (the shorter sound).

It’s going to be a pain to manually type all of those control points, so let’s code a loop to work them out for us and place them in a list:

(setq pan 6.0)              ; pan time in seconds
(setq pause 2.0)            ; pause time in seconds
(setq max-pan 0.9)          ; maximum pan
(setq min-pan 0.1)          ; minimum pan
(setq dur (get-duration 1)) ; the duration of the selection
(setf bp-list (list min-pan))     ; initialise the break-point list

(do ((pos 0))               ; initial position is 0 seconds
    ; stop when >= length of selection.
    ((>= pos dur))
  (setq pos (+ pos pan))
  (push pos bp-list)        ; time
  (push max-pan bp-list)    ; pan value
  (setq pos (+ pos pause))
  (push pos bp-list)        ; time
  (push max-pan bp-list)    ; pan
  (setq pos (+ pos pan))
  (push pos bp-list)        ; time
  (push min-pan bp-list)    ; pan
  (setq pos (+ pos pause))
  (push pos bp-list)        ; time
  (push min-pan bp-list))   ; pan

; put the bp-list the right way round
(setf bp-list (reverse bp-list))

;; make the pan envelope for the left channel
(setf left-env
  (abs-env (pwlv-list bp-list)))

(mult left-env s)

Note that PWLV-LIST is much the same as PWLV except that it takes its arguments (parameters) from a list rather than explicitly typing in each value.

So all that we need to do now is to make the envelope for the right channel, and apply them to the stereo track.
In fact, since we want the right envelope to be the “opposite” of the left envelope, we can simply turn it upside down (multiply by -1) and add 1.0 to bring the range back to between 0 and 1

(setf right-env (sum 1 (mult -1 left-env)))

Putting it all together:

(setq pan 6.0)              ; pan time in seconds
(setq pause 2.0)            ; pause time in seconds
(setq max-pan 0.9)          ; maximum pan
(setq min-pan 0.1)          ; minimum pan
(setq dur (get-duration 1)) ; the duration of the selection
(setf bp-list (list min-pan))     ; initialise the break-point list

(do ((pos 0))               ; initial position is 0 seconds
    ; stop when >= length of selection.
    ((>= pos dur))
  (setq pos (+ pos pan))
  (push pos bp-list)        ; time
  (push max-pan bp-list)    ; pan value
  (setq pos (+ pos pause))
  (push pos bp-list)        ; time
  (push max-pan bp-list)    ; pan
  (setq pos (+ pos pan))
  (push pos bp-list)        ; time
  (push min-pan bp-list)    ; pan
  (setq pos (+ pos pause))
  (push pos bp-list)        ; time
  (push min-pan bp-list))   ; pan

; put the bp-list the right way round
(setf bp-list (reverse bp-list))

;; make the pan envelope for the left channel
(setf left-env
  (abs-env (pwlv-list bp-list)))

(setf right-env (sum 1 (mult -1 left-env)))

(vector
  (mult left-env (aref s 0))
  (mult right-env (aref s 1)))

This code could have been written somewhat more efficiently, but hopefully this was not too difficult to follow.

wow … just … wow … I’ve never received such a detailed and helpful reply to any of my questions since I started browsing in the web ~ 16 years ago …
impressive and awsome :slight_smile:

I’ll check out the code more in detail. thank you very much for your support …

Do let me know how you get on :wink: