Multiple notches with Nyquist prompt?

Installed–the 1.3.7 noise removal is 100x better than 1.2.6. It leaves the empty portions silent.
Still, I’d like to know if it’s possible to do what I wanted–that is, to have a filter do repeated actions as I described above… The hum is still audible as a background to the actual voice portions.

Any tips on the settings of noise removal? We have noise reduction, frequency smoothing, and attack time.

Thanks, D.

I’ve not tested on Audacity 1.2 (I’ve not got it installed), but on Audacity 1.3 they definitely do work (I’ve just tested them all).
To use them, select some audio, then from the effects menu select “Nyquist Prompt”. Then copy and paste the code from any one of the examples into the Nyquist Prompt dialogue box and click OK.

To get a debugging output, click on “Debug” instead of “OK”.

Brilliant!

Oh dear, the problem is with 1.2.

I hereby recommend to all users:

UPGRADE TO 1.3! It is head and shoulders above 1.2.

Thanks again for your help. This is exactly what I need to process our files satisfactorily.

–D

I have written a (multi-notch) mains-hum remover (dehummer) plug-in for Audacity, “inspired by” the code posted above.

If you anyone wants to try my dehummer plug-in you can download a copy from this link and place it in Audacity’s plug-in folder and restart Audacity,
the plug-in should appear in the (unsorted) effects list as “50/60Hz dehummer 1.0”

Pros:
It completely removes 50Hz or 60Hz mains-hum harmonics:
(the results are very good when extracting audio buried in hum).

Cons:
Mono only: (have to split stereo tracks and apply to each track separately).
It is slow: takes approximately 1/5th actual duration of track to remove hum.
Adds unintentional reverberation effect, (not unpleasant), and Gibbs ringing.

Nice one Trebor, good to see some interest in Nyquist.

A few comments on the code - looking at the business end of it (I’ve replaced the controls with your default values):

(setq choice 0)
(setq a 12)
(setq v 20)
(setf freq (cond((= choice 0) 50) ((= choice 1) 60)))

(setq que (/ 10000 (* a a)))
(setq anti (/ 10000 (* v v)))
(setq mysound s)
(setq r *sound-srate*)
(setq iter (truncate (/ (/ r freq) 2)))
(setq d (/ iter anti))

(dotimes (i iter mysound) 
(setf mysound (notch2 mysound (* freq (1+ i)) (* que (1+ (/ i d))))))

That’s a really complicated equation for calculating “Q”
(with a sample rate of 48kHz…)

Did you realise that since “que”, “i” and “d” are all integers, “(* que (1+ (/ i d)))” jumps up in steps of 69?

Also, 480 iterations seems rather excessive and takes the harmonics right up to 24kHz. I can see the logic in going up to the Nyquist frequency, but I would argue that in practice the last few hundred iterations are removing a much greater percentage of the “music” than the “noise”. Also, reducing the number of frequency bands will speed up processing and reduce ringing. (and yes, the removal of all of those harmonics is audible - if you process a section of audio that does not have hum, then listen through good speakers or headphones you can hear the difference between the processed and unprocessed sections)

The other thing that leads to ringing is that you are using such high Q values. While it is desirable to keep the notch filter tight, high Q values cause ringing, so there is a trade off to be made. You probably notice that if you test the plug-in with the default values on “pure hum”, it takes a while before the filter “kicks in”. Lower Q values will cause less ringing, but the notches will be wider, but if you limit the effect to lower frequencies where the hum is likely to be more noticeable, then lower Q values should not present a problem.

To make the plug-in work with stereo…
Stereo tracks in Audacity consist of an array with two elements (the left and right channels).
You can test for an array with (arrayp sound)
The two elements (left and right) are accessed with (aref sound 0) and (aref sound 1)
To return a stereo sound to Audacity, use “vector”

You will commonly see code like this:

(if (arrayp sound) ;; if sound is an array
(vector (aref function() 0) (aref function() 1)) ;; create an array
(function()) ;; else

If you turn your code into a function, then it can be called once for a mono track, or twice for a stereo track.
(One more thought - mains hum can often cause some amount of DC off-set, so adding a low frequency high pass filter could be a good idea)
Something like this:

(setq setfreq 50)
(setq iter 40)

(defun dehum (mysound freq itr)
   (dotimes (i itr mysound)
   (setf mysound (notch2 (hp mysound 20) (* freq (1+ i)) (* 2 (1+ i))))))

(if (arrayp s)
   (vector
      (dehum (aref s 0) setfreq iter)
      (dehum (aref s 1) setfreq iter))
(dehum s setfreq iter))

Despite my unfamiliarity with LISP, (I now know why they named it after a speech impediment :smiley: ),
and this being my first attempt at a plug-in, my Dehummer does exactly what is says on the tin :
here is a link to a before and after example (mp3) … http://www.sendspace.com/file/nxvejc

I did try reducing the number of loops, to the first 100 harmonics (5KHz), but I could still clearly hear the high frequency harmonics of the mains hum, so unfortunately the 480 loops (at 48000 sample rate) are necessary. (I’m in the UK these numbers relate to 50Hz mains).

Thank you for the tips on Nyquist stereo, I will try make dehummer 2.0 a stereo version.
Mains dehummer 2-0.zip (658 Bytes)

I have a voice recording (of a conference session) with a hum in it, and as presuming it’s a 60 Hz hum. I think that it’s a problem with the cable between my microphones (in a Sound Professionals Otterbox) and the recorder (Marantz PMD-620). I’ve haven’t been able to figure out the Spectum Analyzer, but using the Notch Filter at 60 Hz does seem to make things better, so I’ve been proceeding up the harmonics. The commands suggested for pasting into the Nyquist Prompt haven’t been working for me.

Looking at this as an iteration (in this example I will use increasingly narrow notches for higher harmonics):
You want to remove 60 Hz from the sound “s”
(notch2 s 60 25)
And you want to remove 120 Hz from the sound that has had 60 Hz removed:
(notch2 (notch2 s 60 10) 120 20)
And you want to remove 180 Hz from that:
(notch2 (notch2 (notch2 s 60 10) 120 20) 180 30)

I first tried this in Audacity 1.2.6, and see that it’s not supposed to work there. I’ve now installed Audacity 1.3.7 .

In Effect … Nyquist Prompt … I pasted in …

(notch2 (notch2 (notch2 s 60 10) 120 20) 180 30)

… and by choosing the Debug button, got the following message:

error: bad argument type - #(#<Sound: #35c23a0> #<Sound: #35c2628>)
Function: #<Subr-SND-SRATE: #2180fa8>
Arguments:
#(#<Sound: #35c23a0> #<Sound: #35c2628>)
Function: #<FSubr-LET*: #2183e48>
Arguments:
((W (* 2 PI (/ HZ (SND-SRATE X)))) (CW (COS W)) (SW (SIN W)) (ALPHA (* SW (SINH (/ 0.5 Q)))) (A0 (+ 1 ALPHA)) (A1 (* -2 CW)) (A2 (- 1 ALPHA)) (B0 1) (B1 (* -2 CW)) (B2 1))
(BIQUAD-M X B0 B1 B2 A0 A1 A2)
Function: #<Closure-NOTCH2: #36c4730>
Arguments:
#(#<Sound: #35c23a0> #<Sound: #35c2628>)
60
10
1> [ gc: total 18640, 3682 free; samples 1KB, 0KB free ]

Since doing Notch Filter manually requires a Q between 0.01 and 5, I instead tried …

(notch2 (notch2 (notch2 s 60 5) 120 4) 180 3)

… and got the following response:

error: bad argument type - #(#<Sound: #35c23a0> #<Sound: #35c2628>)
Function: #<Subr-SND-SRATE: #2180fa8>
Arguments:
#(#<Sound: #35c23a0> #<Sound: #35c2628>)
Function: #<FSubr-LET*: #2183e48>
Arguments:
((W (* 2 PI (/ HZ (SND-SRATE X)))) (CW (COS W)) (SW (SIN W)) (ALPHA (* SW (SINH (/ 0.5 Q)))) (A0 (+ 1 ALPHA)) (A1 (* -2 CW)) (A2 (- 1 ALPHA)) (B0 1) (B1 (* -2 CW)) (B2 1))
(BIQUAD-M X B0 B1 B2 A0 A1 A2)
Function: #<Closure-NOTCH2: #36c4730>
Arguments:
#(#<Sound: #35c23a0> #<Sound: #35c2628>)
60
10
1> [ gc: total 18640, 3682 free; samples 1KB, 0KB free ]

… which could very well be the same thing. (I haven’t looked closely).

So … I’m applying the Notch Filter manually.

I first tried 60 Hz with a Q of 0.01 … which was funny, because that’s enough to remove all of the audio, so there’s nothing left.

As an experiment, I reloaded the MP3 file, and iterated in the following way:
60Hz Q=0.5
120Hz Q=0.75
180Hz Q=1.00
240Hz Q=1.00
300Hz Q=1.00
360Hz Q=1.00

The first iteration (at 60 Hz) removed the most amount of hum, so “distracting” became “annoying”. After five more the iterations, I can still hear hum … and I can’t detect that the sound is getting any better. I’ll publish the audio as it is, because I’ve got other tracks to do, and should move on.

It would be easier if I didn’t have to do six manual passes, though …

As an experiment, I reloaded the MP3 file, and iterated in the following way:
60Hz Q=0.5
120Hz Q=0.75
180Hz Q=1.00
240Hz Q=1.00
300Hz Q=1.00
360Hz Q=1.00

The first iteration (at 60 Hz) removed the most amount of hum, so “distracting” became “annoying”.

I’ve been doing some more experimenting in A/B comparisons. Since the notch filter should impact the sound as little as possible – except for the hum – I’ve settled on:

60Hz Q=1.0
120Hz Q=1.0
180Hz Q=2.0
240Hz Q=3.0

I tried 300Hz both at Q=4.0 and Q=5.0, and noticed that the sound turning funny. The hum is still there … but I’m not sure that I can find a way to remove it entirely.

Daviding, try pasting this into the Nyquist prompt to remove 60Hz mains hum harmonics

(setq a 15)
(setq v 20)
(setq freq 60); this is 60Hz USA
(setq que (/ 10000 (* a a)))
(setq anti (/ 10000 (* v v)))
(setq mysound s)
(setq r sound-srate)
(setq iter (truncate (/ (/ r freq) 2)))
(setq d (/ iter anti))
(dotimes (i iter mysound)
(setf mysound (notch2 mysound (* freq (1+ i)) (* que (1+ (/ i d))))))

That’s because the “notch2” function only works on mono tracks.

There is some information earlier in this thread about how to apply mono functions to stereo tracks, but the easiest way is to split the stereo track first (Click on the track name and select “Split Stereo Track” from the drop down menu). You can then select the two single channel (left and right) tracks and apply the notch2 function.

About the Q parameter - the higher the number, the more narrow the notch, but the more ringing you will get. Q values up to about 20 will produce pretty narrow notches with little ringing. Values over 40 will produce very narrow notches, but ringing will start to become evident.

As promised earlier in this thread, here is a link to “50/60Hz Dehummer 2.0” dehummer plugin which can now cope with stereo tracks.
It takes a helluva long time to dehum though: about 1/3rd the actual duration of the stereo track at 48000 sample rate.
(If possible reduce your project rate from 48000 to speed up the dehumming process, e.g. reduce to 22050 for speech only recording).

If you prefer to paste code into the Nyquist prompt, rather than use the plug-in, here it is for removal of 60Hz hum harmonics…

(setq  a 15)
(setq  v 20)
(setq  freq 60); 60Hz in North America, (use 50Hz in europe).
(setq que (/ 10000 (* a a)))
(setq anti (/ 10000 (* v v)))
(setq mysound s)
(setq r *sound-srate*)
(setq iter (truncate (/ (/ r freq) 2)))
(setq d (/ iter anti))

(defun dehum (mysound freq iter)
(dotimes (i iter mysound) 
(setf mysound (notch2 mysound (* freq (1+ i)) (* que (1+ (/ i d)))))))

(if (arrayp s)
(vector (dehum (aref s 0) freq iter)
(dehum (aref s 1) freq iter))
(dehum s freq iter))

[Re: Q “jumps up in steps of 69”. I did try altering the code so Q went up in smaller steps, but this made no audible difference so I reverted to the original code which was slightly quicker].
Mains dehummer 2-0.zip (658 Bytes)

Update
I have found a way to remove the Gibbs ringing created by my multi notch mains dehummer.
I made a stereo pair of the original mono track which had mains hum, and the dehummed version which has no hum but Gibbs ringing.
Then I used the centre pan isolation of Extraboy which gave me the frequencies common to both tracks, giving me a result without hum or ringing.

Here is an alteration of this programme which a) allows you to choose your own base frequency as well ( uselful for me since the frequency of the pwer supply for my laptop included both hum and internal transformer noise of 3015 Hz annd its harmonics)b) Makes sure that the variables are floats not integers, so the problem of the “69” steps in Q is gone, and c)allows you to limit the number of harmonics which you cancel ( by choosing the fraction of the full spectrum you want to cover). I assume that Trebor meant to release his under the GPL. Certainly all of my changes are released under the GPL assuming that I really do have permission to use his code in this way.

;nyquist plug-in
;version 1
;type process
;name "50/60Hz dehummer 2.0"
;action "Removing Hum harmonics, ( this may take some time ) ..."
;info:  Unintentional reverb effect. Gibbs ringing on transients.
;info:  if you find a cure for the reverb please email me: uvw111-reverb@yahoo.co.uk

;control choice "Select mains frequency" int "0=50Hz (UK)  1=60Hz (USA) 2=choice" 0 0 2
;control bfreq "Base frequency" real " 10-10000 Hz " 50.0 10.0 10000.0
;control a "Amount of hum removed" real "  1-100 base Q=(100/a)^2" 15 1 100
;control v "Anti-reverberation setting" real "1-100 " 25 1 100
;control frac "Fraction of spectr" real " 0.01 to 1 " 1.0 .01 1.0

(setf freq (cond ((= choice 0) 50.0) ((= choice 1) 60.0) ((= choice 2) bfreq)))

(setq que (/ 10000.0 (* a a)))
(setq anti (/ 10000.0 (* v v)))
(setq mysound s)
(setq r *sound-srate*)
(setq itern (truncate (* (/ (/ r freq) 4) 2)))
(setq d (/ (float itern) anti))
(setq iter (truncate (* (float itern) frac)))

(defun dehum (mysound freq iter)
(dotimes (i iter mysound )
(setf mysound (notch2 mysound (* freq (1+ i)) (* que (1+ (/ i d)))))))

(if (arrayp s)
(vector (dehum (aref s 0) freq iter)
(dehum (aref s 1) freq iter))
(dehum s freq iter))

I’ve just tested that out briefly but there seems to be a problem if you increase the “Amount of hum removed”.

I’m bumping this for two reasons.

First, the problem I had earlier in another thread amounted to the same issue of how to stick more than one line of code in the Nyquist prompt.
The dotimes function is very useful.

Second, I would like to point out that the first try worked in Audacity 1.3 but not Audacity 1.2 because the 1.3 version of the Nyquist prompt will process multiple channels with code that is written for one channel. It is no longer necessary to test for an array. I suppose this improvement is necessary in order to process multichannel audio. I notice that when using arrayp, there was no followup test the see if there were more than two channels.

Stephen

That’s a very good point Stephen, but worth pointing out that this does not apply to all functions. It applies to all of the biquad filter functions (highpass, lowpass, notch …) and is very convenient.

A bit of background information.
Audio data is passed from Audacity tracks to Nyquist as the variable “s”.
If the Audacity track is a mono track then “s” is a variable of type “sound”.
If the Audacity track is a multi-channel track (currently that means 2 channels) then “s” is an array. The array has the same number of elements as there channels (in current versions of Audacity that will be 2). Each element is of type “sound”.
So for a mono track, “s” is a sound.
For a stereo track, “s” is an array. Each element of the array is a sound. (aref s 0) contains the sound from the left channel and (aref s 1) contains the sound from the right channel.

If you look in the file …/audacity/nyquist/dspprims.lsp you can see how this has been done. For example, looking at the code for the two-pole high-pass filter:

From Audacity 1.2.6

; two-pole highpass
(defun highpass2 (x hz &optional (q 0.7071))
  (let* ((w (* 2.0 Pi (/ hz (snd-srate x))))
         (cw (cos w))
         (sw (sin w))
         (alpha (* sw (sinh (/ 0.5 q))))
         (a0 (+ 1.0 alpha))
         (a1 (* -2.0 cw))
         (a2 (- 1.0 alpha))
         (b1 (- -1.0 cw))
         (b0 (* -0.5 b1))
         (b2 b0))
    (biquad-m x b0 b1 b2 a0 a1 a2)))

Without going into too much detail, we can see that this is a fairly straightforward implementation of the low level function “biquad-m”.
The function biquad-m works on sounds. If we pass an array to biquad-m, then it will fail because it is the wrong argument type. If we pass an array to highpass2 (as the local variable “x”), then it will fail because the array will be passed as an argument to biquad-m and, as we have just said, it is the wrong argument type - it needs to be a sound not an array.

From Audacity 1.3.12

; two-pole highpass
(defun highpass2 (x hz &optional (q 0.7071))
  (multichan-expand #'nyq:highpass2 x hz q))

(defun nyq:highpass2 (x hz q)
  (let* ((w (* 2.0 Pi (/ hz (snd-srate x))))
         (cw (cos w))
         (sw (sin w))
         (alpha (* sw (sinh (/ 0.5 q))))
         (a0 (+ 1.0 alpha))
         (a1 (* -2.0 cw))
         (a2 (- 1.0 alpha))
         (b1 (- -1.0 cw))
         (b0 (* -0.5 b1))
         (b2 b0))
    (nyq:biquad-m x b0 b1 b2 a0 a1 a2)))

Here we can see that the function nyq:highpass2 is identical to the old highpass2 function.
The difference is that the new highpass2 function uses multichan-expand.

As shown here: Audacity and Nyquist multichan-expand “expands” multi-channel sounds (arrays) and sends each sound from the array in turn to the function. The data that is returned from each element is mapped back into the appropriate element of the array. This works in a similar way to the function mapcar (in fact it uses the function mapcar).
So if you send an array of sounds (a multi-channel sound) to a function using multichan-expand, then each sound element is sent to the function, and the overall result is an array containing the processed sounds.

The nice thing about using multichan-expand is that if you send a mono sound to a function using multichan-expand, the the overall result is a sound (not an array).

This makes it very easy to add Audacity 1.2.x compatibility to many new filter type plug-ins.
Although (highpass2 s freq) will work with stereo sounds in Audacity 1.3.12 but not in Audacity 1.2.6, if we change that to (multichan-expand #'highpass2 s freq) then it will work in both versions.

It applies to all of the biquad filter functions (highpass, lowpass, notch …) and is very convenient.

Am I using a biquad function in this example:

(scale (/ 3 1.0 4) s)

Because that works on multichannel audio in Audacity 1.3 in the Nyquist prompt as well.

I think you are wrong. I think the environment changed so that you can write for a single channel and it will automatically be applied to every channel without having to test for multichannels in the code.

The ‘1.0’ is needed in order to convert the expression to float instead of integer.

No you’re not using a biquad function in that example, but I think you may have misunderstood what I meant.
In Audacity 1.2.6 some of the filter functions (such as highpass2, notch2,…) only worked for mono tracks, but in Audacity 1.3.12 the code for many filter functions (including highpass2, notch2,…) has been changed in dspprims.lsp so as to accommodate multichannel sounds (as described in my previous post).

I did not intend to suggest that only biquad-m functions worked with multi-channel sounds. Many other functions also work with multi-channel sounds.
My intention was to indicate that not all functions will work with multi-channel sounds in Audacity 1.3.12, and to illustrate why, in the specific case of highpass2 Audacity 1.3.12 is now able to process multi-channel sounds.

Some functions could be used on multi-channel sounds in Audacity 1.2.6. For example, (scale (/ 3 1.0 4) s) will work equally well for mono or stereo tracks in Audacity 1.2.6 or 1.3.12.

An example of a function that does not work in Audacity 1.3.12 with multi-channel sounds is (snd-srate s)

That works fine, but it might perhaps be more straightforward to write (scale (/ 3 4.0) s) or perhaps (scale (/ 3.0 4.0) s). (If either the 3 or the 4 are floats then the division will be calculated as a float.)

That works fine, but it might perhaps be more straightforward to write (scale (/ 3 4.0) s) or perhaps (scale (/ 3.0 4.0) s). (If either the 3 or the 4 are floats then the division will be calculated as a float.)

The Nyquist XLisp 2.0 manual recommended doing it that way; using 1.0 as the float does emphasis that I am trying to treat integers as floats. It’s less typing that putting a zero after all of them and seems less arbitrary than choosing which one in the ratio I want to make a float.

http://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/xlisp/xlisp-ref/xlisp-ref-011.htm

Note: An easy way to force a sequence of integers to be divided as floating point numbers is to insert the number 1.0 after the first argument in the list of arguments to the divider function.

Stephen

I think that’s just a little “useful tip” from Edgar (the author) rather than a general recommendation.

In practice (in plug-ins) you would often be using variables rather than numbers. Taking your example:

(scale (/ 3 1.0 4) s)

If we new in advance that we wanted to use 3/4 (which we would need to know if we were to use numbers) then we would probably just write:

(scale 0.75 s)

However, if we wanted to use (for example) a quarter of some value (var):

here’s the basic code:

(scale (/ var 4) s)

but we know that if var is an integer then we have a problem.

Try this code:

(setq var 3)
(scale (/ var 4) s)

because var is an integer less than 4, var/4 =0
so the result is silence.

There are several solutions to this problem.
We could use Edgar’s tip and write:

(setq var 3)
(scale (/ var 1.0 4) s)

or we could ensure that var is a flonum (a floating point number)

(setq var 3.0)
(scale (/ var 4) s)

If we don’t know if var is an integer or a flonum, then we can either force var to be a flonum

(setq var (float var))

or we can force a floating point calculation with either

(scale (/ var 1.0 4) s)

or

(scale (/ var 4.0) s)

(personal opinion)
When setting a variable to a flonum value, I would always add .0 if the value was a whole number.
When setting a variable to an integer value I would always write the value as an integer (no decimal point).
This way it is easy to see whether the value is a flonum or an integer.

There’s a couple of ways that flonums can be converted to integers - you can either use the function ‘round’ or the function ‘truncate’
Truncate simply knocks off the decimal fraction to leave the integer, so (truncate 3.5) will return ‘3’ (which is an integer value).
http://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/xlisp/xlisp-ref/xlisp-ref-289.htm

I’ve not seen the ‘round’ function documented anywhere, but what it does is to round the value to the nearest integer, so (round 3.5) returns 4 (.5 is rounded up to the next integer value). The ‘round’ function is only available in recent versions of Audacity.

Example code for ‘truncate’ and ‘round’ functions (use the Debug button to see the output if using the Nyquist Prompt effect)

(setq var 0)
(dotimes (i 20)
   (setq var (+ var 0.1))
   (format T "var = ~a~%" var)
   (format T "(truncate var) = ~a~%" (truncate var))
   (format T "(round var) = ~a~%~%" (round var)))

(here’s a trick question - what’s going on when var = 1.0 ?)

For converting from integer to flonum the usual method would be to use (float var), but there are lots of alternative ways, for example:

(setq *float-format* "%f")
(setq var 3)

(format T "var = ~a~%" var)
(format T "(float var) = ~a~%" (float var))
(format T "(* 1.0 var) = ~a~%" (* 1.0 var))
(format T "(+ 0.0 var) = ~a~%" (+ 0.0 var))
(format T "(/ var 1.0) = ~a~%" (/ var 1.0))

I’m sure that you could think of some other ways.

BTW, (format T “string”) sends the string to the standard-output which by default for Audacity is the debug screen.
http://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/xlisp/xlisp-ref/xlisp-ref-121.htm