Can the performance of Nyquist in Audacity be improved ?

Is there any way of improving the performance of Nyquist plugins in Audacity,
e.g. by giving it more memory to work with , (i.e. increase “buffer size” ).

The reason I ask is when using Paul-L’s excellent plugins processing time is slow if many “frequency-bands” are selected.
It’s not a plugin bug : it’s just that the process is very computationally demanding.

[ with the settings I’m using processing-time is a multiple of playback-time :cry: ]

Does CPU use and memory use go up dramatically, or is little memory used?

There is also a Nyquist-in-Audacity problem with too much memory being used: .


CPU increases from about 5% to about 50%, no obvious increase in use of memory …
DeClicker processing 1 second of audio  (with 25 frequency bands).gif
To be fair , with 25 bands selected it’s like running 25 compressors simultaneously, so it’s understandable it’s going to take longer than the typical plugin , but processing takes about 10x playback time on my [old] computer.

I suspect better hardware is the only solution , (harnessing a GPU would be nice ).

The efficiency of Nyquist plug-ins is largely down to the plug-in design. Nyquist is an interpreted language, so loop structures tend to be slow (the code has to be re-evaluated each time). Nyquist includes a large number of DSP primitives which are written by computer in highly optimised C code. Code that can perform most of its processing using the DSP primitives should run reasonably quickly (on my machine, about 5 to 10% of real time). By necessity, Paul’s plug-in has to loop many thousands of times, so it is not surprisingly rather slow. There are also memory issues due to the very simple way that Audacity interfaces with Nyquist. To significantly speed up a plug-in like the one you are referring to, the core DSP would need to be written in a compiled language such as C or C++ so that the plug-in spends less time in LISP loops.

Yes, better hardware will improve things :wink:
If you are on a single processor machine, background processes and applications running in the background will have a stronger impact on performance than on a multi-core or multi-processor machine.

I’ve yet to try Paul-L’s plugins on Linux , which isn’t slowed by the anti-virus, (as on Windows), running in the background. I’ll do a bench-mark test : Paul-L’s plugins on Windows versus Linux and report back ,
( but going Linux isn’t going to give an order-of-magnitude speed-improvement on the same hardware ).

You’ll need to be using a recent version of Audacity or it will probably be an order of magnitude slower!
Building Audacity on Debian based systems is pretty easy, so no reason to not have the latest version.

Trebor, there are these XLisp functions, which you could call from the Nyquist Prompt effect:

gc() [SAL]
(gc) [LISP] - force garbage collection
returns - nil

expand(num) [SAL]
(expand num) [LISP] - expand memory by adding segments
num - the number of segments to add
returns - the number of segments added

alloc(num) [SAL]
(alloc num) [LISP] - change number of nodes to allocate in each segment
num - the number of nodes to allocate
returns - the old number of nodes to allocate

info() [SAL]
(info) [LISP] - show information about memory usage.
returns - nil

room() [SAL]
(room) [LISP] - show memory allocation statistics
returns - nil

I suspect some judicious

(expand x)


(alloc y)

at the prompt, for some wise values of x and y, might improve performance a very long declicking. But I have never got around to this experiment.

I was quoting this.

As I have mentioned elsewhere, Nyquist runs on a very old Lisp interpreter – the code goes back to the 80’s. Its garbage collector is mark-and-sweep, not a modern generational one. A radical idea would be rewriting that.

“Mark and sweep” collection does work proportional to the total memory allocation, each time collection happens. Whereas generational collection only does work proportional to the amount of live, non-garbage data in memory.

Short of that, I also believe the interpreter itself causes much garbage to need collection. Some less radical hacks to the interpreter might send interpreter garbage straight to the free list for reallocation, so garbage collections would be less frequent.

This would make a difference I think in code like mine that has inner loops in Lisp where none of the C routines were right for it.

Such things would be changes not to Audacity proper, but to the Nyquist project which it includes and has its official source repository somewhere else. I don’t know how the team would feel about forking Nyquist.

“I know a guy” who might be eager to try this out, but unfortunately he is even more eager right now to do other features…

I think you should discuss such things with Roger Dannenberg.


Going on the response from previous times this has been discussed, it is preferred to keep “Audacity Nyquist” and “CMU Nyquist” fairly close in step.

There are two ways that can happen. Either Audacity waits for changes “upstream”, or CMU Nyquist merges changes from Audacity Nyquist. Both approaches can and do happen, but to date there have been few Audacity developers other than Roger that are particularly interested in Nyquist. If we get an active developer that is interested in Nyquist, then I think that will be highly beneficial to both projects.

As Gale has just this moment posted, such things need to be discussed with Roger Dannenberg (who is both the originator / maintainer of Nyquist, and a co-founders of Audacity).

I don’t think that Roger would be opposed to any changes in the memory management of Nyquist in Audacity.
He’s already complained about it in the comments to his two-fold Normalize plug-in.
Also, he’s written a Xlisp-based extention to the garbage collection which might be sufficient for stand-alone Nyquist but not for Nyq-inAud.

Pointers please, Robert? I am curious now.

That’s to do with an implementation issue in Audacity rather than in Nyquist itself.
“S” is bound to the sound, and Nyquist plug-ins only get one bite at the cherry. In order to normalize, the entire sound needs to be analysed so that we can get the peak level, and we need to hang onto the sound (in memory) so that we can then amplify it. Ideally we should be able to access the sound block-wise whenever we need them, like internal effects do. (also on the agenda for “version 4”).

Here’s the article: Debugging Nyquist Plug-Ins

Thanks for the link

I actually meant a discussion (which was probably the base for this article).
However, I can’t locate it; I don’t think that it was here on the forum.
It’s essentially the same though.

The question came up many times on the old Nyquist mailing list.
This is one such occasion:

The discussions eventually led to that article from rbd (the link in my previous post)

What does this refer to?

I mean that the basic XLisp garbage collection routine is extended to meet the requirements of sound manipulation.
In other words: “GC-HOOK” links to “NY:GC-HOOK”.

        (COND ((< (* FREE-CELLS 2) HEAP-SIZE)
                      (SHOULD-BE-FREE (/ HOW-MANY-NOT-FREE 2))
                      (HOW-MANY-MORE (- SHOULD-BE-FREE FREE-CELLS))
                      (EXPAND-AMOUNT (/ HOW-MANY-MORE 2000)))
                     (COND ((> EXPAND-AMOUNT 0)
                            (IF *GC-FLAG*
                                (FORMAT T
                                        "[ny:gc-hook allocating ~A more cells] "
                                        (* EXPAND-AMOUNT 2000)))
                            (EXPAND EXPAND-AMOUNT)))))))

This does of course not replace a “modern” low-level collector.