Nyquist Transformation Environment

Using Nyquist scripts in Audacity.
Post and download new plug-ins.

If you require help using Audacity, please post on the forum board relevant to your operating system:
Windows
Mac OS X
GNU/Linux and Unix-like

Nyquist Transformation Environment

Permanent link to this post Posted by edgar-rft » Mon May 24, 2010 8:18 pm

... continued from the Nyquist Cookie Monster discussion:

edgar-rft wrote:The cookie monster idea is stolen from:
David Touretzky - COMMON LISP: A Gentle Introduction to Symbolic Computation
Get a copy of the book for free at http://www.cs.cmu.edu/~dst/LispBook/

stevethefiddle wrote:The book looks good also ... Shame it doesn't cover *warp* (which I find one of the most confusing aspects of Nyquist), but I realise that's specific to Nyquist.

edgar: I've started a new discussion here because this will become a rather lengthy topic...

The Nyquist transformation environment is probably the most difficult part of Nyquist to understand and I do not even want to say that I myself have fully understood all the details. Here is what happens inside Audacity, as seen from the Nyquist Lisp level:

Code: Select all
;; the Nyquist interpreter starts

(load "../audacity/nyquist/init.lsp")

;; the following values are set by
;; the Audacity Nyquist interface
;; at every start of a Nyquist plugin

(setf s             <sound-from-Audacity-selection>
      len           <number-of-samples-in-Audacity-selection>
      *sound-srate* <sample-frequency-of-Audacity-track>
      *warp*        '(0 <length-of-Audacity-selection-in-seconds> nil))

;; --- Nyquist Plugin Code ---

;; first the variables from the 'control' lines
;; in the Nyquist plugin header are assigned

;; then the plugin code runs

;; --- end of Nyquist Plugin Code ---

;; after the plugin code is finished
;; the 's' sound [or a string or a labels list]
;; is returned to Audacity

I think the main difference between CMU Nyquist and Nyquist in Audacity is that in CMU Nyquist, when the code starts to run, no time and no samples exist, so everything can be set-up from scratch. That's why things like *warp* feel much more 'natural' in CMU Nyquist than in Audacity, where in most cases samples already exist (at least in 'process' and 'analyze' plugins) and also *warp* already has a predefined value, depending on the length of the Audacity selection. In Audacity *warp* does not equal to 1.0 seconds, instead a *warp* value of 1.0 equals to the length of the Audacity selection in seconds.

See also Audacity and Nyquist -> Nyquist Variables

From the *warp* perspective, the Audacity Nyquist plugin code is called like:

Code: Select all
(stretch-abs <length-of-Audacity-selection-in-seconds>
  ;; plugin-code
)

This way the length of the Audacity selection is applied to the behaviour of all Nyquist functions in the plugin.

I remember very well that I had relly heavy problems in the beginning to make the examples from the 'Time warp' and 'Time stretch' sections of the Nyquist manual work in Audacity, but maybe there are some better examples to work on? I would like to find a way how the Nyquist 'warp' and 'stretch' behaviours could be explained more suitable for the work with Audacity, where Nyquist must work under much more restricted conditions than at the CMU.

Any further questions, ideas or examples?
edgar-rft
 
Posts: 347
Joined: Sun Jan 20, 2008 12:03 am
Operating System: Please select

Re: Nyquist Transformation Environment

Permanent link to this post Posted by steve » Tue May 25, 2010 12:23 am

edgar-rft wrote:Any further questions, ideas or examples?


An example from a conversation in another forum thread:

Looking at these two bits of code, one might be tempted to think that they do the same thing:
Code: Select all
(setq mysound
(sim s (at 0.5 (cue s))))
(stretch-abs 1 (cue mysound))

Code: Select all
(defun mysound (s-in)
(sim s (at 0.5 (cue s-in))))
(stretch-abs 1 (mysound s))

However, they don't.

In the case of the first code;
If the length of "s" is L, then the length of "mysound" is 1.5(L)
(stretch-abs 1 ...) does not stretch the sound to 1 second, it maps each unit of logical time maps to 1 unit of real time.
(http://www.audacity-forum.de/download/e ... l#index410)
So the logical time is 1.5(L), and mapping each unit of logical time to one unit of real time will produce a length of 1.5(L)

In the second piece of code;
(stretch-abs ...) is applied to the (mysound) function, which means that the "0.5" in the (at 0.5 ...) function is mapped to 0.5 seconds.
So the length of the returned audio is L + (0.5 seconds)
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)
steve
Site Admin
 
Posts: 47011
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: Nyquist Transformation Environment

Permanent link to this post Posted by edgar-rft » Tue May 25, 2010 11:39 am

Attempt of an answer...

Example 1

Code: Select all
(setq mysound (sim s (at 0.5 (cue s))))
(stretch-abs 1 (cue mysound))

steve: If the length of "s" is L, then the length of "mysound" is 1.5(L)
(stretch-abs 1 ...) does not stretch the sound to 1 second, it maps each unit of logical time maps to 1 unit of real time.

edgar: attempt of an explanation...

Code: Select all
;; The first line is computed with the global *warp* time-stretch,
;; where a value of 1.0 equals to the length of the Audacity sound.
;; The start-time of (AT 0.5 ...) will be:
;;   0.5 * <length-of-Audacity-sound>
;; SIM will produce a sound with a total length of:
;;   1.5 * <length-of-Audacity-sound>
;;
(setq mysound (sim s (at 0.5 (cue s))))

;; Here the *warp* time-stretch value to (cue mysound) is 1.0,
;; where the value of 1.0 equals to the length of one second.
;; The (cue mysound) object already has a length of 1.5 seconds,
;; so the sound object returned by (stretch-abs 1 ...) will have
;; a total length of 1.0 * 1.5 seconds.
;;
(stretch-abs 1 (cue mysound))

Until here we have a sound with a total length of 1.5 seconds. But when the 1.5-seconds sound gets returned to Audacity, the build-in STRETCH-ABS in the Audacity Nyquist interface will stretch the 1.5-seconds sound to '1.5 * <length-of-Audacity-sound>', so the effective code from the Audacity perspective works like this:

Code: Select all
(stretch-abs <length-of-Audacity-sound>
  (setq mysound (sim s (at 0.5 (cue s)))))

(stretch-abs <length-of-Audacity-sound>
  (stretch-abs 1 (cue mysound)))

That's why, as the final result, a sound with a length of '1.5 * <length-of-Audacity-sound>' will get inserted into the Audacity track.

The problem here is: Because of the build-in STRETCH-ABS in the Audacity Nyquist interface, to the Nyquist programmer it seems as if STRETCH-ABS produces 'mysterious' results when used in Audacity Nyquist plugin code.

To me myself, understanding the effects caused by the build-in *WARP* and STRETCH-ABS behaviours in the Audacity Nyquist interface was the most difficult part when I tried to find out how to work with Nyquist in Audacity some years ago, but once I understood how this works, the mystery of the Nyquist time behaviours had been disappeared..

Example 2

Code: Select all
(defun mysound (s-in)
  (sim s (at 0.5 (cue s-in))))

(stretch-abs 1 (mysound s))

steve: (stretch-abs ...) is applied to the (mysound) function,
which means that the "0.5" in the (at 0.5 ...) function is mapped to 0.5 seconds.
So the length of the returned audio is L + (0.5 seconds)

edgar: again an attempt of some kind of explanation...

Code: Select all
;; The MYSOUND function is called by STRETCH-ABS [below]
;; with a the *warp* time-stretch value of 1.0 seconds.
;; The start-time of (AT 0.5 ...) will be:
;;   0.5 * 1.0 seconds
;; SIM will produce a sound with a total length of:
;;   <length-of-Audacity-sound> + (0.5 * 1.0) seconds
;;
(defun mysound (s-in)
  (sim s (at 0.5 (cue s-in))))

;; Here (stretch-abs 1 ...) stretches (mysound s) to a length of
;;   1.0 * (<length-of-Audacity-sound> + 0.5 seconds)
;;
(stretch-abs 1 (mysound s))

With the 'defun' call resolved, the code from the Audacity perspective looks like this:

Code: Select all
(stretch-abs <length-of-Audacity-sound>
  (stretch-abs 1 (sim s (at 0.5 (cue s-in))))

Here again, the stretch effect of (stretch-abs 1 ...) gets counter-acted by the build-in STRETCH-ABS in the Audacity Nyquist interface.

Considerations

The problem with the build-in *WARP* and STRETCH-ABS is not so easy as one might think:

When using Nyquist for music composition [that's what Nyquist was made for] or in Audacity in 'generate' plugins, imagine a simple Nyquist code line like:

Code: Select all
(osc 60)

A Nyquist programmer expects this code to produce a sound with the exact length of the Audacity selection, but this is only possible if the *warp* time-stretch value is bound to the length of the Audacity selection, so the resulting sound can be stretched to a fitting length by the build-in STRETCH-ABS function in the Audacity Nyquist interface.

With a *warp* time-stretch value bound to 1.0 seconds and without the build-in STRETCH-ABS function the code from above would have the effect that the Audacity selection would be changed by the plugin code to a length of one second as soon as the sound gets returned to Audacity, what is clearly not the result that an Audacity user expects from using a Nyquist plugin.

So the build-in *WARP* and STRETCH-ABS are twofold, they are very useful for music composition and Audacity 'generate' plugins, but on the other side you have to be very careful if you try to time-shift parts of an Audacity selection in Nyquist 'process' plugins [Audacity 'Effect' menu].

The *warp* variable has a second important purpose, it synchronizes the time behaviour of sounds and envelopes, which in Nyquist have different sample-rates. But this is a different discussion, it has nothing to do with the questions above, but is also important to know and to keep in mind.

Preliminary Conclusion

I think it would be very helpful to develope or document some 'standard strategies' for using time behaviours in Audacity 'process' plugins. Here are some ideas I used in the past in several plugins.

If you need behaviours in seconds, Roger suggested to use the Nyquist ABS-ENV macro:

Code: Select all
(abs-env (osc 60))

This will produce a sine-wave of exactly one second length, but it will also produce the nasty effect of changing the length of the Audacity selection as soon as the sound gets returned to Audacity. But ABS-ENV could help to solve the problems with AT. There are also some other ABS functions like AT-ABS etc., which also must be investigated for their exact behaviour in Audacity.

Warning: the *one-second* example has turned out to be nonsense, see below...

A way to compute a 'real second' with Nyquist in Audacity:

Code: Select all
;;      len           (get-duration 1.0)
;;  -------------  =  ------------------
;;  *sound-srate*        *one-second*

(setq *one-second* (/ len (* *sound-srate* (get-duration 1.0))))

If you copy the following code into the text window of the Audacity Nyquist prompt:

Code: Select all
(setq *one-second* (/ len (* *sound-srate* (get-duration 1.0))))

(stretch-abs *one-second* (osc 60))

It will produce the same result like ABS-ENV above.


Any further ideas? What are the most difficult problems in practical Nyquist programming with Audacity?
Last edited by edgar-rft on Tue May 25, 2010 7:22 pm, edited 1 time in total.
edgar-rft
 
Posts: 347
Joined: Sun Jan 20, 2008 12:03 am
Operating System: Please select

Re: Nyquist Transformation Environment

Permanent link to this post Posted by dondiego929 » Tue May 25, 2010 4:47 pm

This probably won't help you (or anyone) much, but you can input:
(PRINT (LIST *WARP*))
at the Nyquist Prompt and press DEBUG and you can see the contents of *WARP*.

From what I know, *WARP* is just a variable that contains a list/table filled with three pieces of data, two of which are supposedly ALWAYS 0 and NIL (in Audacity), and the other is a/the length of selected audio. You can change this last variable (which is actually the middle data in the list) using the AT command. Still haven't figured out how doing this helps other functions (such as SIM) "move the audio" or "auto-expand" the timeline or what (QUOTE *WARP*) does in XLISP, but that's about it. :cry:
dondiego929
 
Posts: 54
Joined: Mon May 17, 2010 4:40 am
Operating System: Please select

Re: Nyquist Transformation Environment

Permanent link to this post Posted by rbd » Tue May 25, 2010 4:54 pm

Interesting discussion and thanks for posting a pointer to this. It might help to think of the environment as a set of implicit parameters that are passed to *every* function. User-defined functions implicitly get the parameters and pass them on to any functions that are called. The environment manipulation functions like STRETCH and ABS-ENV also receive the implicit functions but they manipulate them before passing them on to the enclosed expression. Technically, these "implicit parameters" are more like global variables, but there's some special support in XLISP so that when you leave STRETCH or ABS-ENV or whatever, the previous value is automatically restored (this is called dynamic scoping). The important thing to remember is that the environment is manipulated *before* computing audio (you can't simply make a short sine tone and then stretch it later, you have to know how long to make it going in). Another way to think about this is that if you think of evaluating nested expressions and applying function calls as descending into deeper and deeper levels until you hit a primitive that actually computes data, then the environment is manipulated on the way down, used by the primitives, and then restored on the way back up. It's not part of signal manipulation but rather setting up (implicit) parameters needed by the signal manipulation primitives (in particular start time and duration).

The previous example of *one-second* seems odd. Isn't *one-second* always equal to 1?

I like the idea of some examples for common situations. It seems like the most common will be
  • How do you turn off the stretch factor and use seconds?

  • If you turn off stretch, how do you retain the original selection length?
rbd
 
Posts: 6
Joined: Tue May 25, 2010 4:16 pm
Operating System: Please select

Re: Nyquist Transformation Environment

Permanent link to this post Posted by rbd » Tue May 25, 2010 5:13 pm

dondiego929 wrote:... two of which are supposedly ALWAYS 0 and NIL (in Audacity)


This is true at the top level where Audacity invokes your plug-in, but your plug-in can change the environment, so these values may not be 0 and NIL everywhere within your plug-in. You should *never* use these values directly.

Audacity does not completely respect the Nyquist environment, e.g. when you make a selection, Audacity pretends like the beginning of the selection is at time 0. I'm not sure if this was a wise decision, but that's how it works. Also, if you return a sound that starts at, say time=1, Audacity will treat the beginning of the sound as the beginning of the selection. This might also be a bad decision, but again, that's how it works.

One way to experiment with this is to have your plug-in return silence plus something interesting. E.g. (S-REST) will just replace the selection with silence, and (SUM (S-REST) (MY-SOUND)) will add (MY-SOUND) to the silence. This is different from just returning (MY-SOUND) because if (MY-SOUND) starts at 1.0, Nyquist will compute 1.0s of zeros from (S-REST) *followed by* the shifted sound. You'll actually see your sound appear at an offset from the start of the selection. To work in seconds, you probably want to do something like: (SUM (S-REST) (ABS-ENV (MY-SOUND))). Note that if (MY-SOUND) extends beyond the selection, Audacity will not truncate the samples but insert them. There are ways to truncate a sound if you want to maintain the selection size, but this post is too long already...
rbd
 
Posts: 6
Joined: Tue May 25, 2010 4:16 pm
Operating System: Please select

Re: Nyquist Transformation Environment

Permanent link to this post Posted by edgar-rft » Tue May 25, 2010 6:44 pm

Warning: this is wrong what I had written here, see below...

rbd wrote:The previous example of *one-second* seems odd. Isn't *one-second* always equal to 1?

Unfortunately no, in Audacity 1.0 is always equal to the length of the Audacity selection in seconds, but never to 1.0 seconds, or if, then only by chance. This problem is introduced by the build-in *WARP* and STRETCH-ABS behaviour and the non-1.0 value is exactly the eternal source of confusion.

In Audacity *one-second* always must be recomputed from (get-duration 1.0) [the length of the Audacity selection in seconds], LEN [the number of samples in the Audacity selection] and *sound-srate* [the sample frequency of the Audacity track]. Computing absolute time values in seconds with Nyquist in Audacity is pure horror, because every time value depends on at least three other values.

That's also exactly the time-shift problem with Nyquist in Audacity.


Examples for time-shift effects

I would be very happy to find a solution, where I can write:

(time-shift start end offset sound) ; or any other argument ordering

where START, END, and OFFSET are either seconds or fractions of the selection-length. The samples from START to END then get time-shifted by the value of OFFSET. This way arbitrary delays could be implemented very easy, where negative time-shifts need special care because Nyquist cannot handle t0 values less than zero [which never appear in music composition].

Another common question is trimming a certain amount of seconds from the beginning, end or both ends of a sound in seconds. This is also a time-shift issue because if you trim sound from the beginning you must time-shift the new start-time backwards to t0.

But I would first like to wait for other effect suggestions...
Last edited by edgar-rft on Tue May 25, 2010 7:06 pm, edited 1 time in total.
edgar-rft
 
Posts: 347
Joined: Sun Jan 20, 2008 12:03 am
Operating System: Please select

Re: Nyquist Transformation Environment

Permanent link to this post Posted by steve » Tue May 25, 2010 6:53 pm

edgar-rft wrote:A Nyquist programmer expects this code to produce a sound with the exact length of the Audacity selection, but this is only possible if the *warp* time-stretch value is bound to the length of the Audacity selection, so the resulting sound can be stretched to a fitting length by the build-in STRETCH-ABS function in the Audacity Nyquist interface.


So if you have (osc 60) in a Generate plug-in the *warp* time-stretch value is NOT bound to the length of the Audacity selection, but in a Process plug-in it IS bound to the length of the Audacity selection. Is that right?

edgar-rft wrote:If you copy the following code into the text window of the Audacity Nyquist prompt:

Code: Select all
(setq *one-second* (/ len (* *sound-srate* (get-duration 1.0))))
(stretch-abs *one-second* (osc 60))

It will produce the same result like ABS-ENV above.


Shouldn't that be:
Code: Select all
(setq *one-second* (/ (* (get-duration 1.0) *sound-srate*) len))
(stretch-abs *one-second* (osc 60))

not that it matters in this case, as both (/ len (* *sound-srate* (get-duration 1.0))) and (/ (* (get-duration 1.) *sound-srate*) len) evaluate to 1,
but if we were to calculate half a second using (get-duration 0.5), then it would need to be
(setq *half-second* (/ (* (get-duration 0.5) *sound-srate*) len))


edgar-rft wrote:Any further ideas? What are the most difficult problems in practical Nyquist programming with Audacity?

Stretching sounds from the Audacity track?
Something like this?
Code: Select all
(setq dur 2.0)

(force-srate *sound-srate* (stretch-abs (/ dur (get-duration 1.0))(sound s)))
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)
steve
Site Admin
 
Posts: 47011
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: Nyquist Transformation Environment

Permanent link to this post Posted by edgar-rft » Tue May 25, 2010 7:13 pm

stevethefiddle wrote:Shouldn't that be:

Code: Select all
(setq *one-second* (/ (* (get-duration 1.0) *sound-srate*) len))
(stretch-abs *one-second* (osc 60))

not that it matters in this case, as both (/ len (* *sound-srate* (get-duration 1.0))) and (/ (* (get-duration 1.) *sound-srate*) len) evaluate to 1,
but if we were to calculate half a second using (get-duration 0.5), then it would need to be
(setq *half-second* (/ (* (get-duration 0.5) *sound-srate*) len))

Sorry for my nonsense writing: Nyquist in Audacity had been fixed and I haven't noticed. I had no sound for nearly a year because of eternal Ubuntu problems and I'm obviosly not up-to-date with Audacity. I have added RED NOTES to some of my writing above, which turned out to be WRONG!

I will now re-read the whole stuff again...
edgar-rft
 
Posts: 347
Joined: Sun Jan 20, 2008 12:03 am
Operating System: Please select

Re: Nyquist Transformation Environment

Permanent link to this post Posted by edgar-rft » Tue May 25, 2010 8:12 pm

Before this starts to become an esoteric meta-discussion [littered with bugs introduced by my own writing] I will sit down and try to write some possibly simple code examples, tested with Audacity CVS HEAD to make sure I will not again warm-up bugs which already had been fixed.

rbd wrote:It seems like the most common will be:
- How do you turn off the stretch factor and use seconds?
- If you turn off stretch, how do you retain the original selection length?

Yes, I think these are the two most important issues.

stevethefiddle wrote:Stretching sounds from the Audacity track? Something like this:
Code: Select all
(setq dur 2.0)
(force-srate *sound-srate*
  (stretch-abs (/ dur (get-duration 1.0))
    (sound s)))

Counter-question: do you know the Vinal Scratch Tutorial? This would be fun to write an Audacity plugin out of it! Or is there already one from David Sky?
edgar-rft
 
Posts: 347
Joined: Sun Jan 20, 2008 12:03 am
Operating System: Please select

Next

Return to Nyquist



Who is online

Users browsing this forum: No registered users and 2 guests