Multiple notches with Nyquist prompt?

I am removing a hum from some voice recordings. I find that if I notch the file at 60, 120, 180, 300, 420 … all the way to 1740 hz or so, the hum is fairly fully removed, without much loss of quality. How can I do all of them at one go? If I make a list in the Nyquist Prompt window, it seems to handle only the first one… what I want is as follows:

(notch2 s 60 25)
(notch2 s 120 10)
(notch2 s 173 25)
(notch2 s 180 25)
(notch2 s 300 40)
(notch2 s 420 40)
(notch2 s 540 50)
(notch2 s 660 50)
(notch2 s 780 60)
(notch2 s 900 60)
(notch2 s 1020 60)
(notch2 s 1140 60)
(notch2 s 1260 60)
(notch2 s 1380 60)
(notch2 s 1500 60)
(notch2 s 1620 60)
(notch2 s 1740 60)
(notch2 s 1860 60)

I trust there’s a simple way to do this… Thanks for your help. (don’t know why the 173 shows up; in the plot spectrum it is there.)

That’s using the shortcut tools. You could do it manually by defining three points. Instead of notch=400, you define 399=-0db, 400=-40dB 401= 0dB. You can put as many of those in a formula as you wish.

Make a sloppy notch filter with the graphic tools, save the filter, open it up and see what the code reads like. I’m no programmer and that’s how I did a simple curve once, and you can put as many of those points in as you want.

It’s possible that there is a “Q” restriction to filters like that, so they don’t really come out notches, but it’s worth a go.

You know that’s how Noise Removal works right? You expose the Noise Removal Tool to hum by itself and it will build the filter for you. It will try to remove everything in that hum sample (profile) from the following show.

And I would do this in Audacity 1.3. The 1.2 Noise Removal tools were pretty dreadful.

Koz

The Nyquist notch filter provides a very deep and steep notch. When you remove hum with a notch filter, you are removing that frequency from everything, the music as well as the noise. For this reason you should avoid using it more than necessary. If you need to remove harmonics all the way up to 1.8 kHz, then you have a serious hum problem and you should address it by using shielded cable (shielded twisted pair and balanced circuits if necessary). Going up to 240 Hz should be more than sufficient.

(I’m curious how you worked out what Q values to use).

Back to the question in hand:

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)

Let me write that more clearly:

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

Of course for dealing with harmonics you could always use a loop construct such as:

(setq mysound s)
(setq q 2)         ; set the base Q for the filter
(setq iter 4)     ; set the number of iterations
(setq freq 60)  ; set base frequency

; start the DO loop
(dotimes (i iter mysound)   
(setf mysound (notch2 mysound (* freq (1+ i)) (* q (1+ i))))
)                     ;end of loop

Thanks to both of you.

I set the Q values by trial and error, checking the plotted spectrum to see if a too-deep notch was being cut, or not enough. It is approximate, of course.

Well, unfortunately neither of the code pieces you so kindly wrote could succeed; the first returns no audio, the second, only “returns the value: 60”. And having no experience with scripting, I cannot debug. I thought one could write a filter that could apply various actions to a selection successively–that is, first notch the whole selection at 60, then when that is finished, start again and notch the whole selection at 120, etc. So I can run it, walk away, and come back to have the hum gone.

Umm, I am still using 1.2.6. I gather from Koz’s comment that I should probably upgrade to 1.3. Yes, I haven’t been using the built-in sound removal, because it simply leaves too many artifacts, even at the low setting. If the 1.3 version does well, then perhaps that is the easiest solution. But there is probably some additional room noise, in addition to the hum, that may vary from part to part.

The hummy recordings are voice recordings, of 8 lectures delivered at an outside facility. Our own recordings are generally hum-free; this place had an old hummy system, and I’m just trying to salvage the recordings we have…

Thanks again for your help. In the meantime, I shall upgrade to 1.3.

–D

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: http://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/devel/audacity-nyquist-en.htm#stereo 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.