ACX Check: Help to Understand its Code

Hello everybody,

let’s start with a brief introduction: I am new to this forum and I am trying to learn Nyquist. I have no experience in LISP or SAL, I know a thing or two about C and its derivates (like C++ and C#). I am just a bit surprised about the lack of a good beginner’s guide, you know the real basics (for dummies).

Anyway, so I took the basic suggestion and started analyzing some previous plugins and due to my frequent use of ACX Check, this is where I started. Unfortunately it is written in LISP, but new plugins are written with SAL syntax.

Now, in ACX Check there is a function containing itself the function progv for which there is not SAL equivalent:

defun fmt-pretty (FMT &rest stuff)
  (progv '(*float-format*) (list FMT)
    (apply #'format stuff)))

I tried to understand what it does exactly yet failed. I know it is only used to format the results for display.

If someone could take this function and explain it step by step and maybe even code it in new SAL syntax, I’d really appreciate it. Please remember, I am new to Nyquist, so please use as many words as you like. :wink:

Thanks in advance!

Quick note: If your ACX Check succeeds on a mono show, the user display is relatively simple and concise.

But if you do everything wrong in stereo the panel reads like a small book. Make fresh coffee.


Screen Shot 2019-08-16 at 20.19.20.png
Koz

Thanks for taking the time but for this display and distinguishing between mono and stereo, there are other functions, like analyze-stereo and analyze-mono and the main function analyze. My guess is it has something to do with floating point to do. It always appears when the results are displayed in linear instead of dB.

But I do not want to understand its function bit its code. What does progv do, where does list get its values from, what does apply #'format do?

Did you find the Nyquist page in the Audacity manual? Nyquist - Audacity Manual

Also: Introduction to Nyquist and Lisp Programming - Audacity Manual

There’s an introduction section in the Nyquist manual: Introduction and Overview though this focuses mainly on SAL syntax, whereas Nyquist plug-ins and documentation about Nyquist in Audacity mostly uses LISP syntax.

There’s also an increasing collection of Nyquist documentation in the Audacity wiki: Missing features - Audacity Support

You need to look these things up in the manuals.
For standard XLISP functions, the XLISP manual is the best place to look. Here’s the language reference page - it’s definitely worth bookmarking this page. If you are learning to program in Nyquist, you will use it a lot: XLISP Language Reference

In this case you will find: XLISP progv
See also:
PROG: XLISP prog
PROGN: XLISP progn

For functions that are specific to Nyquist (most of the audio functions), use the Nyquist manual index: Index

Other useful bookmarks:
http://dept-info.labri.fr/~idurand/enseignement/lst-info/PFS/Common/Strandh-Tutorial/indentation.html
https://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/xlisp/xlisp-man/xlisp-man-index.htm

(defun fmt-pretty (FMT &rest stuff)
  (progv '(*float-format*) (list FMT)
    (apply #'format stuff)))

This code is cryptic. Let’s break it down a line at a time:

.

(defun fmt-pretty (FMT &rest stuff)

Defines a function called “fmt-pretty” with arguments “FMT” and “stuff”.
“&rest” says that “stuff” may be zero or more values (see: https://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/xlisp/xlisp-ref/xlisp-ref-223.htm)

.

(progv '(*float-format*) (list FMT)

This sets float-format to the value of FMT.
float-format is a global variable used by Nyquist to specify the printed decimal format. See: https://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/xlisp/xlisp-ref/xlisp-ref-119.htm
This could have been written more simply as:

(setf *float-format* FMT)

but I assume that the author wanted to ensure that float-format was only set to FMT within the scope of his function.
If he simply used (setf float-format FMT), then float-format would be set globally to the value of FMT.

.

(apply #'format stuff)

This applies the function FORMAT (https://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/xlisp/xlisp-ref/xlisp-ref-121.htm) to the elements of the list “stuff”.

.

Here’s an example of how it works:

(defun fmt-pretty (FMT &rest stuff)
  (progv '(*float-format*) (list FMT)
    (apply #'format stuff)))

(setq FMT-val "%.3f")  			;format directive = 3 decimal places
(setf val 1.23456789012345)		;set val to a number with lots of decimals
(fmt-pretty FMT-val t "~a" val)		;call the function "fmt-pretty, passing 4 values

The final line calls the function “fmt-pretty” with 4 arguments:

  • FMT-val, evaluates to the “3 decimal places” format directive. This is a string value.
  • t, the destination. This is the boolean value “true”.
  • “~a”, a string. Note that this contains the format directive placeholder “~a”.
  • val, evaluates to the decimal number 1.23456789012345

The first two lines of the function “fmt-pretty” are explained above.
The third line of the function expands “stuff” and applies the function FORMAT to the arguments. In other words, the final line expands to:

(format t "~a" val)

Because float-format has (temporarily) been set to “%.3f”, (format t “~a” val) prints val (that has a value of 1.23456789012345) with three decimal places. The destination “true”, sets the output of format to standard-output, which in Audacity is the debug output.

.

If you paste this code into the Nyquist Prompt and click the Debug button, you should see 1.235 printed in the debug window that opens. (note that the final digit has been rounded up to “5”).

(defun fmt-pretty (FMT &rest stuff)
  (progv '(*float-format*) (list FMT)
    (apply #'format stuff)))

(setq FMT-val "%.3f")  			;format directive = 3 decimal places
(setf val 1.23456789012345)		;set val to a number with lots of decimals
(fmt-pretty FMT-val t "~a" val)		;call the function "fmt-pretty, passing 4 values

I think this version would be a little less cryptic:

(defun fmt-pretty (dformat &rest args)
  ;;Like 'format' with an additional decimal specifier 'dformat'.
  (prog2 
    (setf *float-format* dformat)
    (apply #'format args)
    (setf *float-format* "%g")))

I would also have probably given the function a different name, such as format-dec, indicating the similarity with the normal format function.

Thanks a lot, steve, for taking your time. I’ll look into it. Once I’ve succeeded transforming the function into SAL syntax I’ll post my code in this thread.

As for your links to bookmark: some I knew already but not all of them. So thanks for these too!

Just to clarify for everyone, for it to actually work the code also needs the final line:

(defun fmt-pretty (FMT &rest stuff)
  (progv '(*float-format*) (list FMT)
    (apply #'format stuff)))

(setq FMT-val "%.3f")  			;format directive = 3 decimal places
(setf val 1.23456789012345)		;set val to a number with lots of decimals
(fmt-pretty FMT-val t "~a" val)		;call the function "fmt-pretty, passing 4 values

(format nil "~a" val)

Two points: the final line only works for me with nil instead of a t. And it prints 1.23457 - that is with 5 decimal places instead of 3.

Indeed your rewritten functions works also:

(defun fmt-pretty (dformat &rest args)
  ;;Like 'format' with an additional decimal specifier 'dformat'.
  (prog2 
    (setf *float-format* dformat)
    (apply #'format args)
    (setf *float-format* "%g")))

(setq FMT-val "%.3f")  			;format directive = 3 decimal places
(setf val 1.23456789012345)		;set val to a number with lots of decimals
(fmt-pretty FMT-val t "~a" val)		;call the function "fmt-pretty, passing 4 values

(format nil "~a" val)

As for SAL, you mentioned that most plugins are written using LISP but the latest Audacity and Nyquist recommends SAL, doesn’t it?

I tried to write the code using SAL but the # in prog2 > apply throws an error:

define function fmt-pretty ((dformat &rest args)
  prog2(setf(*float-format*, dformat), apply(#'format, args), setf(*float-format*, "%g")))

setq (FMT-val, "%.3f")
setf (val, 1.23456789012345)
fmt-pretty (FMT-val, t, "~a", val)

format (nil, "~a", val, return)

I admit I am having trouble with the nested prog2 in the fmt-pretty function.

Audacity does not make any recommendation. Choose whichever you prefer. When code is written in SAL, Nyquist reads the commands, compiles them into Lisp, and evaluates the commands. If you write in LISP, then you cut out the conversion step.

I would recommend LISP syntax, based on the practicality that help is more readily available for LISP. I’m not able to help with SAL.

Thanks, LISP it is then for the time being.

Back to the function:

Does this mean that whenever I want to print/return a RMS or some similar analysis of sound I need to tell it how to do that - exponential or decimal? Or only when the source could be a floating point format (like 32bit floating point WAVE)? But I’ll find out about it, I’ll ask again when I’ve gotten that far.

My initial question of this thread (and a few follow-up questions) has been answered for now! Thanks you very much, Steve!

As you can see in image that you posted:





The author of this plug-in wanted to print linear values with 6 decimal places, and dB value with only one decimal place. The “fmt-pretty” function allows him to specify the number of decimal places within his print function. If he just used the ordinary (format ) function, then numeric values would all be printed with the default “%g” decimal formatting. This has no affect on the calculations, it is purely a matter of how the values are printed in the output messages.

We should be clear the red lines are something I cooked up in Photoshop. They’re not part of the display service.

Koz