Error using the predefined sixteenth note constant

Hi folks,

I’m new here. My name is Adam and I have just started writing my own nyquist plugins for Audacity. I am an experienced software developer although I only have limited experience with lisp and no prior experience with nyquist.

I’m trying to use the predefined note duration constants in a generation plugin that I’m working on and I am getting an error when I use the sixteenth note constant ‘s’. I suspect it is because there is a conflict between the constant symbol name ‘s’ and the audacity sound field ‘s’ but I’m not experienced enough to be sure. Has anybody else noticed this?

At the moment, I’m just taking user input for a note length and converting it to a numerical value for use later down the track. All the other note length constants are fine, it is just the sixteenth note one that is an issue.
The code for the control is as follows:

;control div "Division" choice "Whole,Half,Quarter,Eighth,Sixteenth,Whole Dotted,Half Dotted,Quarter Dotted,Eighth Dotted,Sixteenth Dotted,Whole Triplet,Half Triplet,Quarter Triplet,Eighth Triplet,Sixteenth Triplet" 2

The code for the conversion of user selected value to numeric duration is as follows:

;; determine the note length
(case div
	( 0 (setf noteLen w))	;; Whole
	( 1 (setf noteLen h))	;; Half
	( 2 (setf noteLen q))	;; Quarter
	( 3 (setf noteLen i))	;; Eighth
	( 4 (setf noteLen s))	;; Sixteenth
	( 5 (setf noteLen wd))	;; Whole Dotted
	( 6 (setf noteLen hd))	;; Half Dotted
	( 7 (setf noteLen qd))	;; Quarter Dotted
	( 8 (setf noteLen id))	;; Eighth Dotted
	( 9 (setf noteLen sd))	;; Sixteenth Dotted
	(10 (setf noteLen wt))	;; Whole Triplet
	(11 (setf noteLen ht))	;; Half Triplet
	(12 (setf noteLen qt))	;; Quarter Triplet
	(13 (setf noteLen it))	;; Eighth Triplet
	(14 (setf noteLen st))	;; Sixteenth Triplet
	(T (setf noteLen q))	;; Default - quarter
)

;; debugging value of noteLen
(error "noteLen = ~a" noteLen)

The debug error statement comes back with the correct values for all selected options except sixteenth which causes a “Nyquist returned no audio” message.

I know I can easily get around this by hard coding the value for sixteenth notes but I’d really like to know why this is occurring. Any insight or comment would be most appreciated.

EDIT: I just realised I should have mentioned that this is in version 1.3.12-beta running on WinXP.

Cheers,
Adam-V

We don’t get may programmers. They’re in short supply. Are you comfortable? Do you want some coffee? Extra pillow?

The others will be along. They’re eight time zones east of here.
Koz

This is clearly a bug introduced by the Audacity Nyquist interface, that nobody has noticed until now. (Is this really true or does anybody know a bug report about this in the last ten to fifteen years? Nobody has ever used the Nyquist duration symbols?)

The reason is that the Audacity Nyquist interface uses the “s” variable to store the Audacity sound object, blindlessly overwriting the Nyquist “sixteenth” value, so using the “s” symbol for a sixteenth note indeed signals an error (just tested with Audacity_2.0).

At the beginning of “nyquist.lsp” the symbol-value of the “s” symbol is defined as a duration of 0.25 duration time:

(setq s 0.25)

But this is pitilessly clobbered by the sound object in the Audacity Nyquist interface, before the plugin code is executed.

The problem is that with Nyquist in Audacity, it’s not a really good idea to re-define “s” to the original 0.25 value in the plugin code, because Audacity expects a sound in the “s” variable to re-insert the sound from the “s” variable into the Audacity audio track, so re-defining “s” to anything other than a sound will make the “Nyquist did not return audio” window appear if Audacity finds no sound in the “s” variable after the the plugin code has finished.

A quick solution could be to take any free Nyquist symbol, bind it to a symbol-value of 0.25, and then use this symbol instead of “s” as a shorthand for a sixteenth note. Here is how this can be done in a few examples.

  1. How to find out if a symbol is free. Open the Audacity “Effect > Nyquist prompt” and type for example:
(print (boundp 'x))

Then press the “Debug” button. If in the “Debug” window the expression from above prints NIL (= false), then the symbol-value is unbound (= free), if it prints T (= true) then the symbol-value is already used for any other purpose (= not free).

  1. Using a different symbol for sixteen notes. With my Audacity_2.0 on Debian Linux, the expression from above it prints NIL (= symbol-value is free), so I can use “x” as a replacement for “s” by writing:
(setf x 0.25)
;; from here on I can use x instead of s for a sixteenth note

I would be interested what the others think about this. IMO it’s an incredible mega-bug if really nobody (including me) ever has noticed this before. I also do not think that re-defining “s” to the Nyquist sixteenth note would be a really good idea because thist would surely break 100% of all existing Audacity Nyquist plugins. We probably need a good replacement variable for the sixteenth note. If MIDI in Audacity one day will work this might become a problem.

The question is: What would be a good new shorthand for sixteen notes?

  • edgar

Thanks Edgar.

I proved this is the case by changing the symbol name in nyquist.lsp to “ss” and it travelled nicely.

I have to say I’m not a big fan of using single (or even double) letter symbol or variable names for anything other than scratch values, I much prefer to use more descriptive names which are less likely to be re-used. Perhaps prefixing the note duration symbols with something like “dur-” to indicate it is a duration?

Cheers,
Adam-V

Use of these predefined constants is most likely to be with Generate type plug-ins, and with Generate plug-ins “s” has the value 0.25. It’s only with Process and Analyze type plug-ins that “s” stores the Audacity sound object.

Sorry Steve, but this is wrong. The Audacity Nyquist interface always uses the “s” variable as a sound object, no matter what type of plugin. In a “generate” plugin, the “s” variable is initially clobbered by the Audacity Nyquist interface with a value of NIL (= no value), and Audacity expects the “s” variable to contain the generated sound if the plugin finishes. Example:

;nyquist plug-in
;version 3
;type generate
;name "Test-Generator.."
;action "Test-Generator..."

;control dummy "dummy" int "" 0 0 0

(print s)
(setq s 0.25)

Prints NIL and a “Nyquist returned the value: 0.250000” window appears instead of inserting a sound. If I do not set the “s” variable to 0.25 and let it be NIL (the value from the Audacity Nyquist interface), then the “Nyquist did not return audio” window appears. In none of these cases the “s” variable can be used as a shorthand for a sixteenth note in Audacity Nyquist plugin code as originally defined in “nyquist.lsp”.

The only working (but pretty awkward) workaround I know is:

;nyquist plug-in
;version 3
;type generate
;name "Test- Generator.."
;action "Test- Generator..."

;control dummy "dummy" int "" 0 0 0

(let ((s 0.25))
  ;; inside let s has a value of 0.25
  (osc 60 s))
;; after let s is the sound object returned by osc

This would force people to write hard-coded let-wrappers whenever they need the “s” duration symbol. People would also be forced to track the variable scope in their head while writing plugin code. Not a real problem for experienced programmers, but a nightmare for newbies.

Tested with Audacity_2.0.

Hmm … partly wrong.
Running your test code in a new project, the first time the code is run it prints 0.25, then it gets clobbered.

This does at least give a workaround for the case of Generate plug-ins by setting s to 0.25 near the start of the plug-in code.

It would appear to be so. :open_mouth:

What if “s” was re-defined to the Nyquist sixteenth note only for Generate type plug-ins?

Trust me to be the difficult one…

I realise I’m new here and I’m not wishing to upset anyone but wouldn’t anyone writing a tempo based processing plugin and wanting to use the sixteenth note constant still be in trouble in this case?

Cheers,
Adam-V

This is an interesting observation, I hadn’t noticed this before. (But I also hadn’t noticed that the sixteenth “s” doesn’t work for nearly than ten years, so what do you expect?)

Audacity Generate plugins never expect sound input from an Audacity audio track, so this would be a possibility (and another complication of the Audacity Nyquist interface, too). But this would probably be a better solution than using a different name for the sixteenth duration because it keeps compatibility to existing Audacity plugins as well as to the original CMU Nyquist code.

Possible problems can arise when newbies (like me, in the early beginnings) write imperative code like:

(setf s (osc 60))      ; generate a 440Hz sine wave
(setf s (mult s 0.5))  ; change the amplitude of the sine wave

The code from above works so far, but here is what happens if you try to stretch the osc sound to the length of a sixteenth note:

(setf s (osc 60))           ; generate a 440Hz sine wave
(setf s (stretch-abs s s))  ; try to stretch the osc sound

Question: What s is s? Is s in stretch-abs still valid as a sixteenth note at all? This leads to endless confusion.

Here I agree with Adam-V:

Or as Edgar (= me) would say: Crypticism was invented by idiots. In the first moment you think it’s faster, but in fact it complicates and slows down everything. Write a program using cryptic names, then try to read your own code a few months later, and you will see what happens. Lisp gives the possibility to write self-explaining code by using descriptive symbol names. Use this possibility.

Trying to be objective: The short Nyquist note and duration names clearly help if you try to compose a whole song via Nyquist code (and that’s the only reason why Nyquist note and duration symbols use cryptic symbol-names).

But the real “s” problem is caused by the combination of two facts:

  • XLISP has no support for packages. All Lisp symbols are stored in the same obarray and it’s very easy to clobber them with no further notification.


  • The variables, used by the Audacity Nyquist interface, are by far too cryptic.

Unfortunately it’s not easy to add a package system (using different obarrays) to the XLISP interpreter, because the entire internal Lisp infrastructure needs to be rewritten for this, and implementing a package system on the Lisp level (using several hash tables) is much too slow.

The typical oldschool Lisp solution for Lisps with no package system is to prefix the symbol name with the package name. That’s why all Nyquist low-level sound function names start with “snd-…”. The proper solution would be to prefix all Lisp symbols used by the Audacity Nyquist interface with “audacity-…” or something similar, but unfortunately it’s much too late to change this, because the cryptic variable names from the Audacity Nyquist interface are meanwhile used in hundreds of Audacity Nyquist plugins.

The problem is that there is a second (original) Nyquist version at the CMU:

We should try to keep compatibility with the original Nyquist by all means. If Nyquist in Audacity would be the only Nyquist implementation it would be easy to redefine everything as wanted or needed. If Nyquist symbol names are changed, they should be either changed in both Nyquist implementations or in none of both. That’s the main reason for this somewhat over-complicated discussion here.

But please do not feel disencouraged, all sorts of suggestions are always welcome. :stuck_out_tongue:

Summary: At the moment I think it would be best to change the Audacity Nyquist interface so that in Generate plugins the initial value of “s” is always 0.25 and not NIL in subsequent invocations. This is a realistic amount of work with relatively low risk to introduce other unwanted side-effects.

I realise fixing bugs of this nature in mature code that has many uncontrolled dependencies is virtually impossible. Fixing it for generation type plug-ins (where it is most likely to be be used) will certainly go a long way and hopefully the existence of this thread here will give people a “heads up” to the duplicated symbol issue if wishing to use constants in a processing plug-in.

Is there any known bugs documentation for the Audacity implementation of Nyquist?

Cheers,
Adam-V

Also “s” quickly becomes a very familiar variable for anyone writing plug-ins.
I suppose these duration constants should be documented here: Missing features - Audacity Support with an accompanying warning about the conflict.

I think this is most of them: http://alturl.com/8boa2

Some source code investigations later:

The reason for the “s” value of NIL in subsequent plugin invocations is that after a Nyquist plugin has finished, the “s” variable is set to NIL, then the XLISP garbage collector is called to free the memory of the Nyquist sound object. This explains why in the first invocation the “s” variable still has the “sixteenth” value, because with Generate plugins the “s” variable is only changed when the plugin is finished, but not if the plugin is started.

At start time of an Audacity Nyquist plugin the “s” variable is set to the sound object from the Audacity audio track, but only for Process (Audacity Effect menu) and Analyze plugins, but not for Generate plugins. This gives the perfect opportunity to set the value to the Nyquist sixteenth value at the plugin start time for Generate plugins.

To make this work, add the following code:

/* restore the Nyquist sixteenth note symbol for Generate plugins */
if (GetEffectFlags() & INSERT_EFFECT) {
   cmd += wxT("(setf s 0.25)n");
}

File “src/effects/nyquist/Nyquist.cpp”, function EffectNyquist::ProcessOne, line 674 ff (scoll down to see where the code must be added):

bool EffectNyquist::ProcessOne()
{
   nyx_rval rval;

   if (GetEffectFlags() & INSERT_EFFECT) {
      nyx_set_audio_params(mCurTrack[0]->GetRate(), 0);
   }
   else {
      nyx_set_audio_params(mCurTrack[0]->GetRate(), mCurLen);

      nyx_set_input_audio(StaticGetCallback, (void *)this,
                          mCurNumChannels,
                          mCurLen, mCurTrack[0]->GetRate());
   }

   wxString cmd;

   if (mDebug) {
      cmd += wxT("(setf *tracenable* T)n");
      if (mExternal) {
         cmd += wxT("(setf *breakenable* T)n");
      }
   }

   /* restore the Nyquist sixteenth note symbol for Generate plugins */
   if (GetEffectFlags() & INSERT_EFFECT) {
      cmd += wxT("(setf s 0.25)n");
   }

   for (unsigned int j = 0; j < mControls.GetCount(); j++) {
      if (mControls[j].type == NYQ_CTRL_REAL) {

      /* much more code omitted here */

   }
}

With this code modification Audacity will use 0.25 as the default value for the “s” variable, but only in Generate plugins (or at least this works with my Audacity_2.0 on Debian Linux). Process and Analyze plugins use the sound object from the Audacity audio track as default value for the “s” variable.

Sorry, still no patch file available.

That seems to work, though I’m also testing on Debian (Squeeze) so it’ll need testing on other platforms.
I’ve made a patch from your code (attached) which I’ll post on Bugzilla with a bug report.

(Another problem with using single letter variables is it makes them hard to search for in files)
nyquist-16th.patch (598 Bytes)

Ever tried to search for the “s” variable in the Audacity C/C++ code? Found millions of “s” words I never heard before in only a few lines of code.

Thanx for the patch BTW. :stuck_out_tongue:

YES! I was looking for where the duration values were set :imp:
Just as I was giving up you produced the patch code.

Well you did the hard work, so I thought I could save you a little job :slight_smile:

I’ve posted a bug report and the patch to Bugzilla http://bugzilla.audacityteam.org/show_bug.cgi?id=490

Thanks to the carefully managed directory and file structure of the Audacity source code the functions of the Audacity Nyquist interface are spread over several files at completely different locations:

  • The variables are set in “audacity/lib-src/libnyquist/nyx.c” (very old C code from Dominik Mazzoni).


  • The plugin code is parsed in “audacity/src/effects/Nyquist/Nyquist.cpp”, more recent C++ code, calling the C code in “nyx.c” above.

Unbelievable but true, the mess was really much worse a few years ago. Roger has cleaned-up a lot when he implemented the SAL parser functions in “Nyquist.cpp” and the MIDI stuff.

Important to remember: Search for “setvalue(xlenter(” to find all ocurrences where Nyquist variables are set in the C code. Unfortunately the plugin-code parser in “Nyquist.cpp” uses wx-widgets string functions to produce Nyquist code, so there is no other way than to read and riddle yourself.

Still found unanswered questions:

Theoretically yes, but the time-related computations work differently in Generate and Process/Analyze plugins. In Generate plugins, the default time unit is one second (same as in CMU Nyquist), while in Process/Analyze plugins the default time unit equals to the duration of the Audacity selection.

This means in practice that in a Generate plugin the Nyquist “s” sixteenth value means “a sixteenth note in 4/4-measure” while in a Process/Analyze plugin this would mean “a quarter (not a sixteenth!) of the duration of the Audacity selection”.

This differency is because if for example in a Process plugin an envelope is applied to an Audacity selection (“Fade In” / “Fade Out” Effect) the user expects the envelope to have the same duration as the selected part of the Audacity audio track, that’s why a duration of 1.0 in a Process/Analyze plugin equals the time of the selected piece of audio, so an “default” envelope with a default duration of 1.0 has automatically the same duration as the selected Audacity sound. The same way 0.5 means “half duration of the Audacity selection” and 2.0 means “double duration of the audacity selection”. Audio effects that modify existing sounds can be implemented much easier that way.

On the other hand a value of a “one sixteenth note” has no real meaning anymore in such a context, a sixteenth of a selected sound would be 1/16=0.0625, not 0.25 like a sixteenth note in 4/4-measure notation. It is still possible to use “notes” with Process/Analyze plugins but you then must re-compute the seconds from the Audacity Nyquist variables, and from the seconds then compute the durations of the notes.

See the Audacity Wiki [u]Nyquist Variables[/u] for an example how this works.

In a Generate plugin, where the main purpose most often is to produce sequences of notes or sounds, it is more important to have a constant time reference, so you can use a defined measure and program “notes” of different durations like you are used to do with pencil and paper. This was what Nyquist originally was made for.

As you can see, time has a strangely different meaning in music composition compared to the processing of a microphone recording of exactly the same piece of music. Such strange things happen very often in the audio world, and that’s the reason why it is interesting.

  • edgar

Thank you both for looking into this issue. I look forward to sharing some plug-ins with the world in the near future.

Cheers,
Adam-V