problem with SAL

Hello Sirs. Somebody could indicate to me how we write in lisp langage this code:
define function fm-lfo(amp, freq)
return sound-srate-abs(default-control-srate,
amp * hzosc(freq))
Beforehand thank you.

As a literal translation it would be:

(defun fm-lfo (amp, freq)
  (sound-srate-abs *default-control-srate*
    (* amp (hzosc freq))))

but due to the peculiar environment in Audacity that may not be exactly what you need. Could you give some context to your question - what are you trying to do?

The ‘*’ must of course be replaced by ‘mult’ or ‘prod’ since we’re dealing with sounds.
In general: the return command can be omitted - the closing parenthesis indicates the last expression of the function.

(defun x (args) ... <last expression>)[code]

Hello. I thank Steve for its help. I try to use in the console Nyquist of Audacity 2.03 the code following SAL:
define function fm-lfo(amp, freq)
return sound-srate-abs(default-control-srate,
amp * hzosc(freq))

play fmosc(g5, fm-lfo(pwl(1, 500), 6)) ~ 2

I wrote:
(setf amp 1 )
(setf freq 440)

(defun fm-lfo (amp, freq)
(sound-srate-abs default-control-srate
(*amp hzosc freq )) )
(defun mytone ()
(fmosc (hz-to-step 440))

(play (mult (mytone fm-lfo) ( pwl 1 500) 6) )

The problem is that this code does not work. Furthermore, I do not understand to what corresponds “amp”?

Looks like scrambled eggs.
Amp is the amplitude (= 1, i.e. maximum currently).
‘(*amp’ should be ‘(mult amp …’
But ther’s a bunch of other mistakes as well. the parens do not match and fm-osc has too few arguments (probably those from behind ‘play’.

When posting code to the forum, please use the “Code” button above the message composing box to create “code tags”, then put your code between the opening and closing code tags like this:

[code]
// code goes here...

[/code]

Translating this SAL code:

define function fm-lfo(amp, freq)
return sound-srate-abs(*default-control-srate*,
amp * hzosc(freq))

play fmosc(g5, fm-lfo(pwl(1, 500), 6)) ~ 2

into LISP syntax gives this:

(defun fm-lfo (amp freq)
  (sound-srate-abs *default-control-srate*
    (mult amp (hzosc freq))))

(play
  (fmosc G5 (fm-lfo (pwl 1 500) 6)))

See how, in LISP, every function is in the form ( )

Nyquist is case insensitive, but built in functions are generally written in lower case. I’ve changed “g5” to “G5” (upper case G) because it is a predefined constant in Nyquist (it has the value 79) and I think that upper case helps it to stand out from the regular code so that I don’t confuse it with a normal variable. Many of the built-in variables in Nyquist have names that start and end with an asterisk, such as default-control-srate.

If the above code were to be used to generate sound in Audacity, the PLAY function in the last line could be omitted. The returned value (the sound) from (fmosc G5 (fm-lfo (pwl 1 500) 6)) would then be the “return value” from Nyquist and, because it is a sound, Audacity would put that into an audio track.

Standard XLISP functions are documented, with simple examples, in the XLISP manual: XLISP Language Reference
For the functions that are specific to Nyquist you will need the Nyquist manual, but there are less examples here: Index

Whether you use Nyquist in Audacity, or the stand alone version, you will find a useful introduction to Nyquist programming here in the Nyquist Plug-ins Reference Missing features - Audacity Support

Hello Sirs. I try to write in LISP this code noted in Sal:

define function fm(p, a, c-m, mod-ratio)
  return a * fmosc(p, mod-ratio * step-to-hz(p) *
                      hzosc(step-to-hz(p) *
                            recip(c-m)))

play fm(gs2, pwl(0.3, 1, 0.7, 1, 1), 0.5,
        pwlv(0.5, 0.5, 10, 1, 2)) ~ 2
return s

I wrote:

(defun fm (p a c-m mod-ratio)
(return a (fmosc p mod-ratio) (step-to-hz p)
(hzosc ( step-to-hz p) 
(recip c-m)))

(stretch 1
 (scale 0.1 (mult ( fm  gs4   (pwl 0.3 1 0.7  1 1) 0.5)  (pwlv 0.5 0.5 10 1 2) ))))

My code is bad because it does not generate a sound in the console Nyquist. would you have the kindness to indicate to me where are the errors? Beforehand thank you.

Do you want to run the code in stand-alone Nyquist, or in the Nyquist Prompt effect in Audacity?

Hello sir.
I would want to use this code in the Nyquist Prompt in Audacity.

The first thing is that you don’t use “return” in LISP syntax. Functions always return the last result of the function.

define function fm(p, a, c-m, mod-ratio)

This is the start of the function. The name of the function is fm and the arguments (parameters) are “p, a, c-m and mod-ratio”. As you have correctly written, in LISP syntax this would be:

(defun fm (p a c-m mod-ratio)



  return a * fmosc(p, mod-ratio * step-to-hz(p) *
                      hzosc(step-to-hz(p) *
                            recip(c-m)))

This is the body of the function. As already stated, we don’t use “return” in LISP syntax. The rest of this is just one long command that could be written as:

a * fmosc (p, mod-ratio * step-to-hz(p) * hzosc (step-to-hz(p) * recip(c-m)

The line indentation is just to make it more readable.

Breaking this down into sections we can look at the innermost functions first:

step-to-hz(p)



recip(c-m)

In LISP the above would be:

(step-to-hz p)



(recip c-m)

Stepping out a bit further,

hzosc (step-to-hz(p) *  recip(c-m))

then becomes:

(hzosc (* (step-to-hz p)(recip c-m)))

which we can write as:

(hzosc (* (step-to-hz p)
          (recip c-m)))

so that we can see that (step-to-hz p) is multiplied by (recip c-m) and the result of that is then used as the argument in “hzosc”.

The next part gets a bit complicated, but we can see that fmosc has 2 arguments. The first is p and the second is:

(* mod-ratio
  (step-to-hz p)
  (hzosc (* (step-to-hz p)
         (recip c-m))))

and if we put those together we get:

(fmosc p (* mod-ratio
           (step-to-hz p)
           (hzosc (* (step-to-hz p)
                  (recip c-m)))))

which is all multiplied by a:
So the complete function is:

(defun fm (p a c-m mod-ratio)
  (* a
    (fmosc p (* mod-ratio
               (step-to-hz p)
               (hzosc (* (step-to-hz p)
                      (recip c-m)))))))

In the next part we are calling the above function, so we need to supply the arguments “p, a, c-m and mod-ratio
p is: gs2 [though because it is a predefined constant I prefer to write it in upper case GS2]
a is: (pwl 0.3 1 0.7 1 1)
c-m is: 0.5
mod-ratio is: (pwlv 0.5 0.5 10 1 2)

Putting this together the command becomes:

(fm GS2
    (pwl 0.3 1 0.7 1 1)
    0.5
    (pwlv 0.5 0.5 10 1 2))

We don’t need to use “play” because we are going to return the result to the selected Audacity track.
So that just leaves us with the time stretch “~2”
which we can do with (stretch 2.0 )

Put the whole thing together and we have:

(defun fm (p a c-m mod-ratio)
  (* a
    (fmosc p (* mod-ratio
                   (step-to-hz p)
                   (hzosc (* (step-to-hz p)
                          (recip c-m)))))))


(stretch 2.0 (fm GS2
    (pwl 0.3 1 0.7 1 1)
    0.5
    (pwlv 0.5 0.5 10 1 2)))

However, there is a problem here - “P” and “c-m” are “sounds” and we cannot multiply sounds with “*”. we have to use “mult” for sounds:

(defun fm (p a c-m mod-ratio)
  (mult a
    (fmosc p (mult mod-ratio
                   (step-to-hz p)
                   (hzosc (* (step-to-hz p)
                          (recip c-m)))))))

(stretch 2.0
  (fm GS2
      (pwl 0.3 1 0.7 1 1)
      0.5
      (pwlv 0.5 0.5 10 1 2)))

The only other thing that we will probably want to change is that the Nyquist Prompt is an “effect” and “effects” in Audacity have a default stretch factor such that the selection is always “1”. If we want the output to be 2 seconds (rather than 2 times the selection length) we must use stretch-abs rather than just stretch.

(defun fm (p a c-m mod-ratio)
  (mult a
    (fmosc p (mult mod-ratio
                   (step-to-hz p)
                   (hzosc (* (step-to-hz p)
                          (recip c-m)))))))

(stretch-abs 2.0
  (fm GS2
      (pwl 0.3 1 0.7 1 1)
      0.5
      (pwlv 0.5 0.5 10 1 2)))

I thank Steve for these very precise comments.
I have 3 questions to ask.

  1. The code SAL is preceded by the following text:“…The C:M ratio is based on frequencies, so we convert pitch p to Hz using step-to-hz…”
    My question is the following one: is it the code according to whom realizes this calculation?
[code](step-to-hz p) (hzosc (* (step-to-hz p)
  1. The comment so continues:“…Since this yields the carrier frequency, we divide by C:M to get the modulator frequency, but…”
    My second question is: Would the ideal have was to divide the value into Hz of GS2 by the value of the ratio to generate the carrier frequency (207.652: 0.5 = 415.30)?

  2. But! “… but there is no divide operation for signals (signals that contain a zero would raise a divide-by-zero error, so division is discouraged).”
    My third question is: Does the signal correspond to a value expressed in hertz?

Beforehand, thank you for comments.

I’m not sure that I can comment on the intentions of the code author, so I’ll just comment on what I see in the code.

(defun fm (p a c-m mod-ratio)
  (mult a
    (fmosc p (mult mod-ratio
                   (step-to-hz p)
                   (hzosc (* (step-to-hz p)
                          (recip c-m)))))))

Function fm takes 4 parameters, and I’m guessing these are:
pitch [p]
amplitude [a]
and then two parameters that relate to the carrier:modulation ratio [c-m and mod-ratio]

“amplitude” [a] is easy to understand - it simply multiplies the “fmosc” by “a”.

fmosc takes 2 parameters:
pitch [p]
modulation [rest of the expression]

For “fmosc” “p” is the pitch in steps, but they are saying that for their “modulation” expression they want the pitch expressed as a frequency.
Pitch “p” as a frequency can be calculated with (step-to-hz p).

So in pseudo-code the “modulation” expression is basically:
mod-ratio * fp * (hzosc (fp / c-m)
where “fp” is the pitch frequency in Hz.

Regarding the final part of that expression, If both “p” and “c-m” are numbers (as they are in the example - where p=GS2=44.0 and c-m=0.5) then rather than:
(hzosc (* (step-to-hz p) (recip c-m)
we could simply write:
(hzosc (/ (step-to-hz p) c-m))

However, if the function fm is intended to support values “c-m” that is a sound, then the code must be:
(hzosc (mult (step-to-hz p) (recip c-m)))
taking care that c-m is never zero.

Hello sirs.
I thank steve for these comments. I have an other code noted in Sal.

define function vibtone(pitch, vibamp)
  return fmosc(pitch,
               step-to-hz(pitch) * vibamp * 0.01 * lfo(6),
               *saw-table*)

play vibtone(e5, 5)

The author of the code indicates: ;; making vibrato a percentage of the fundamental frequency
I try to use this code in the Nyquist prompt in Audacity. I wrote:

(defun vibtone (pitch vibamp)
 (fmosc pitch (* (step-to-hz  pitch)
 (* (vibamp  0.01) (  lfo 6)  *saw-table*) ))  ))

(abs-env   (vibtone  e5  5))

By reading comment of the Nyquist prompt, I observe that I did not define the value of vibamp.

error: unbound function - VIBAMP
if continued: try evaluating symbol again
Function: #<Closure-VIBTONE: #75e16c0>
Arguments:
76
5
Function: #<FSubr-PROGV: #75e6ec0>
Arguments:
(QUOTE (WARP LOUD TRANSPOSE SUSTAIN START STOP CONTROL-SRATE SOUND-SRATE))
(LIST (QUOTE (0 1 NIL)) 0 0 1 MIN-START-TIME MAX-STOP-TIME DEFAULT-CONTROL-SRATE DEFAULT-SOUND-SRATE)
(VIBTONE E5 5)
1>
My code is thus false; do you see how we can solve this problem?
Beforehand thank you for your comments.

Nyquist thinks that VIBAMP is an undefined function but the original SAL code is using VIBAMP as a variable.

In LISP, functions always start with an open bracket, followed by the function name, then the arguments, then a close bracket:
(function )

SAL

a * b * c,

LISP

(* a b c)

The function name in this example is “*”. A, B and C are the arguments.

Hello sirs. I thank Steve.
I thus wrote the part of the code which corresponds in (* a b c).
a = the function (here, I believe that it is (step-to-hz pitch) and two arguments: b = vibamp and c = 0.01
I wrote:

(fmosc pitch(*(step-to-hz pitch) vibamp 0.01) (lfo 6 *saw-table*)))

The problem also, is the “variable” word in Sal. I did not manage to know if a variable in Sal is the equivalent of an argument in LISP.
On the other hand, if I write

(abs-env   (vibtone e5) 5 )

the Nyquist prompt in Audacity indicate: error: too many arguments
Function: #<Closure-ABS-ENV: #7625248>
Arguments:
(VIBTONE E5)
5
1>
Thus, there is another problem of synthaxe in my code. I think that it is situated at the level of the function and its 2 arguments. How do we have to write a function the name of which is (* (hz-to-step pitch)?
Beforehand thank you for your answer.

A “variable” is simply a symbol (a letter, word or combination of letters and numbers) that represents a value.
Example:
(setq test 42)
“setq” is a function that sets the value of a variable (places a value on a symbol)
“test” is the symbol (the variable).
“42” is the value.

“test” and “42” are the two “arguments” for the function “setq”.

The value assigned to a “variable” can change:
(setq test 42) ; test = 42
(setq test 21) ; test = 21
(setq test 101) ; test = 101

abs-env takes just one argument which is a “behaviour” (loosely, a “behaviour” generates a sound).

(abs-env (vibtone e5) 5 )
This is incorrect because you are passing two arguments to abs-env.
(vibtone e5) is the first argument.
5 is the second argument.

I thank steve for these comments. I thus corrected a part of the code in LISP. I wrote:

(setq vibamp 0.01)(setq pitch 76)


(defun vibtone (pitch vibamp)
 (fmosc pitch  (* (step-to-hz pitch vibamp) (lfo 6 10 *saw-table*)  )))

It allows to associate two arguments (76 and 0.01) with the function step-to-hz
On the other hand, if I write at the end of the code (vibtone E5 5) the Nyquist prompt indicates: error: too many arguments
Function: #<Closure-STEP-TO-HZ: #754fe40>
Arguments:
76
5
0.01
Function: #<Closure-VIBTONE: #a3b1e70>
Arguments:
76
5
1>
How can we use so many arguments simultaneously?

There’s two times “pitch” in the function declaration after “fmosc”.
Delete the first one. For the second one, you should write “(hz-to-step pitch)”, otherwise, the input (which is in Hz, I guess) would be converted again and lead to a negative value (when Pitch is low).

No it doesn’t.
You don’t see an error in that code because you are not calling the function “vibtone”, and until you do Nyquist will not see the error.

Yes, because now you are calling the function vibtone, Nyquist tries to read the vibtone function and so sees the error.

The error is that “step-to-hz” only takes one argument but you are supplying two arguments.

step-to-hz simply converts a MIDI note number (“steps”) to frequency (Hz).

What you have written is a bit like saying “How much is 23 5 US dollars in Euro?” That does not make sense. You can ask “How much is 23 US dollars in Euro?” or “How much is $5 in Euro?” or even, “How much is (23 + 5) dollars in Euro?” but “dollars-to-euro” takes exactly one argument, which is the number of dollars that you want to convert.
Similarly, “step-to-hz” takes exactly one argument which is the “step value” that you want to convert.

I thank the moderators for their comments.
If I do not call the vibtone function it is maybe because I did not specify the role of vibamp. I would want that Nyquist understands that if I write (vibamp 0.01) it wants to say: vibamp is a variable and 0.01 is a value chosen for vibamp.
But also, it means that with vibamp we just select a part of the value of step-to-hz; and it is this part which produces the vibrato.
I believe that it corresponds has: multiply the value (of vibamp) by 624.25 and divide by 100.
Can be my reasoning is - it false.