Automatic removal of mouth smacks, again

UPDATE 29 May 2014: Started another topic for a major new version here. http://forum.audacityteam.org/viewtopic.php?f=42&t=79278

Continuing this discussion.
https://forum.audacityteam.org/t/automatic-removal-of-mouth-smacks/31481/1

I am developing a tool to remove the natural click noises that happen during speech and I am pleased with what can be done with the default settings.

Here’s a tip for before and after comparisons: duplicate the track, select one track, and apply the tool with “Isolate” as the action choice. Then listen to the tracks together for “after,” or the first track solo for “before.” Use shift-space to loop part of the track, and click the solo button on and off.

I thought it a good time to post by latest version of things. Many changes both to click detection, and also to the repair which is much better now. And some workarounds for a certain Nyquist crash! Yes, the code has ballooned a bit.

There are various option switches near the top of the code as setq’s that are not cluttering up the user interface. Various experiments there but I am undecided about their value.

Not sure it’s quite ready for the plug-ins board. I don’t know if my half educated method of band pass filtering with certain convolutions is really a better idea than the Chebyshev filters based in biquads that I do not understand so well and were also lately posted on the board, if those could compute faster. Time to dig into the mathematics.
DeClicker.ny (53 KB)

The detection seems pretty good.

I had a problem when using more extreme settings.
Here’s the debug output (I had to snip some of the data because there is too much for a forum post.)

"SFT3F window: 2226292 samples 7574 periods 150.031 Hz"
error: In SND-AVG, stepsize is too big
Function: #<Subr-SND-AVG: #96073c8>
Arguments:
  #<Sound: #b333cec0>
  2226293
  2226293
  1
Function: #<FSubr-LET*: #96059a0>
Arguments:
  ((SRATE (SND-SRATE WINDOW)) (WINDOW-SIZE (SND-LENGTH WINDOW NY:ALL)) (WINDOW-DUR (/ WINDOW-SIZE SRATE)) (PERIOD (/ FREQUENCY)) (N-PERIODS (ROUND (/ WINDOW-DUR PERIOD))) (NEW-FREQUENCY (/ N-PERIODS WINDOW-DUR)) (COSINES (IF (PLUSP N-PERIODS) (COSINE-PERIODS SRATE NEW-FREQUENCY N-PERIODS) 1)) (SCALAR (/ (IF (ODDP N-PERIODS) -1 1) (* WINDOW-SIZE (SND-FETCH (SND-AVG (PROD COSINES WINDOW) WINDOW-SIZE WINDOW-SIZE OP-AVERAGE))))))
  (PROD SCALAR WINDOW)
Function: #<Closure-NORMALIZE-CONVOLUTION-WINDOW: #954ff90>
Arguments:
  #<Sound: #b3340080>
  150.031
Function: #<FSubr-LET*: #96059a0>
Arguments:
  ((SRATE (SND-SRATE SND)) (STEP-DUR (/ STEP-SIZE SRATE)) (BLOCK-SIZE (* STEPS-PER-BLOCK STEP-SIZE)) (BLOCK-DUR (* STEPS-PER-BLOCK STEP-DUR)) (FREQUENCY (/ (+ BAND-BOTTOM BAND-TOP) 2)) (WIDTH (- BAND-TOP BAND-BOTTOM)) (BIN-NUMBER (IF USE-LARGE-WINDOWS (LET* ((DATA (CDR (ASSOC USE-WINDOW-CHOICE WINDOW-FUNCTION-LIST))) (MINIMUM-BIN (/ WIDTH (WINDOW-FUNCTION-3DB-WIDTH DATA)))) (MAX 1 (TRUNCATE (/ FREQUENCY MINIMUM-BIN)))) 1)) (BIN-FREQUENCY (/ FREQUENCY BIN-NUMBER)) (UNNORMALIZED-WINDOW (IF USE-DOUBLE-CONVOLUTION (COMPUTE-DOUBLE-CONVOLUTION-WINDOW SRATE BAND-BOTTOM BAND-TOP) (COMPUTE-CONVOLUTION-WINDOW SRATE BIN-FREQUENCY BIN-NUMBER))) (WINDOW (NORMALIZE-CONVOLUTION-WINDOW UNNORMALIZED-WINDOW FREQUENCY)) (CORRECTIONS (COMPUTE-THRESHOLD-CORRECTIONS WINDOW BIN-FREQUENCY BIN-NUMBER BLOCK-SIZE RELATIVE-THRESHOLD ABSOLUTE-THRESHOLD)) (RELATIVE-THRESHOLD-CORRECTION (CAR CORRECTIONS)) (ABSOLUTE-THRESHOLD-CORRECTION (CDR CORRECTIONS)) (_ (WHEN DEBUG-MESSAGES (PRINT (FORMAT NIL "Correct thresholds ~A by ~A and ~A by ~A" RELATIVE-THRESHOLD RELATIVE-THRESHOLD-CORRECTION ABSOLUTE-THRESHOLD ABSOLUTE-THRESHOLD-CORRECTION)))) (_ (WHEN DEBUG-CONVOLUTION-WINDOW (RETURN-FROM MAKE-CHANNEL-RESULTS WINDOW))) (WINDOW-SIZE (SND-LENGTH WINDOW NY:ALL)) (WINDOW-DUR (/ WINDOW-SIZE SRATE)) (N-PIECES (IF USE-CRASH-WORKAROUND (1+ (TRUNCATE (/ WINDOW-SIZE STEP-SIZE))) 1)) (_ (WHEN (AND DEBUG-MESSAGES (> N-PIECES 1)) (PRINT (FORMAT NIL "window pieces for band at ~A Hz: ~A" BAND-BOTTOM N-PIECES)))) (DOWNSAMPLE-FACTOR 1) (CONV1 (IF (> DOWNSAMPLE-FACTOR 1) (LET ((LESSER-SRATE (/ SRATE DOWNSAMPLE-FACTOR))) (PROD DOWNSAMPLE-FACTOR (FORCE-SRATE SRATE (CONVOLVE-BY-PIECES (SND-AVG SND DOWNSAMPLE-FACTOR DOWNSAMPLE-FACTOR OP-AVERAGE) (SND-AVG WINDOW DOWNSAMPLE-FACTOR DOWNSAMPLE-FACTOR OP-AVERAGE) N-PIECES)))) (CONVOLVE-BY-PIECES SND WINDOW N-PIECES))) (HALF-WINDOW-DUR (/ (/ WINDOW-SIZE 2) SRATE)) (CONVOLUTION (SND-XFORM CONV1 (SND-SRATE CONV1) (SND-T0 CONV1) (+ (SND-T0 CONV1) HALF-WINDOW-DUR) MAX-STOP-TIME 1)) (_ (WHEN DEBUG-CONVOLUTION (RETURN-FROM MAKE-CHANNEL-RESULTS CONVOLUTION))) (SIG (SUM (DB-TO-LINEAR -100) (SND-AVG CONVOLUTION BLOCK-SIZE STEP-SIZE OP-PEAK))) (_ (WHEN DEBUG-PEAKS (RETURN-FROM MAKE-CHANNEL-RESULTS (FORCE-SRATE SRATE SIG)))) (DENOMINATOR (SHIFT-TIME SIG BLOCK-DUR)) (QUOTIENT-SND (PROD SIG (RECIP DENOMINATOR))) (_ (WHEN DEBUG-RATIO (RETURN-FROM MAKE-CHANNEL-RESULTS (SUM (SND-CONST 0 0 SRATE BLOCK-DUR) (FORCE-SRATE SRATE QUOTIENT-SND))))))
  (MAKE-SINGLE-FREQUENCY-CLICK-ITERATOR SRATE SIG QUOTIENT-SND STEPS-PER-BLOCK STEP-DUR RELATIVE-THRESHOLD RELATIVE-THRESHOLD-CORRECTION (* ABSOLUTE-THRESHOLD ABSOLUTE-THRESHOLD-CORRECTION) FREQUENCY)
Function: #<Closure-CLICK-DETAIL-FINDER: #9527a2c>
Arguments:
  #<Sound: #b333d658>
  150
  150.062
  100
  0
  1.1885
  0.001
Function: #<FSubr-PROGN: #960783c>
Arguments:
  (CLICK-DETAIL-FINDER SND BOTTOM TOP STEPS-PER-BLOCK STEP-SIZE (IF USE-LATE-STEEPNESS-TEST (SQRT RELATIVE-THRESHOLD) RELATIVE-THRESHOLD) ABSOLUTE-THRESHOLD)
Function: #<FSubr-SETF: #9603980>
Arguments:
  G294
  (LAST (RPLACD G294 (CONS (PROGN (CLICK-DETAIL-FINDER SND BOTTOM TOP STEPS-PER-BLOCK STEP-SIZE (IF USE-LATE-STEEPNESS-TEST (SQRT RELATIVE-THRESHOLD) RELATIVE-THRESHOLD) ABSOLUTE-THRESHOLD)) NIL)))
Function: #<FSubr-DO: #96071c4>
Arguments:
  ((BOTTOM (POP FREQUENCY-LIST) TOP) (TOP (POP FREQUENCY-LIST) (POP FREQUENCY-LIST)))
  ((NOT TOP))
  (PUSH-BACK (PROGN (CLICK-DETAIL-FINDER SND BOTTOM TOP STEPS-PER-BLOCK STEP-SIZE (IF USE-LATE-STEEPNESS-TEST (SQRT RELATIVE-THRESHOLD) RELATIVE-THRESHOLD) ABSOLUTE-THRESHOLD)) G294)
Function: #<FSubr-BLOCK: #96065ac>
Arguments:
  NIL
  (DO ((BOTTOM (POP FREQUENCY-LIST) TOP) (TOP (POP FREQUENCY-LIST) (POP FREQUENCY-LIST))) ((NOT TOP)) (PUSH-BACK (PROGN (CLICK-DETAIL-FINDER SND BOTTOM TOP STEPS-PER-BLOCK STEP-SIZE (IF USE-LATE-STEEPNESS-TEST (SQRT RELATIVE-THRESHOLD) RELATIVE-THRESHOLD) ABSOLUTE-THRESHOLD)) G294))
Function: #<FSubr-LET*: #96059a0>
Arguments:
  ((G292 NIL) (G293 (OR G292 (LIST NIL))) (G294 G293))
  (BLOCK NIL (DO ((BOTTOM (POP FREQUENCY-LIST) TOP) (TOP (POP FREQUENCY-LIST) (POP FREQUENCY-LIST))) ((NOT TOP)) (PUSH-BACK (PROGN (CLICK-DETAIL-FINDER SND BOTTOM TOP STEPS-PER-BLOCK STEP-SIZE (IF USE-LATE-STEEPNESS-TEST (SQRT RELATIVE-THRESHOLD) RELATIVE-THRESHOLD) ABSOLUTE-THRESHOLD)) G294)))
  (OR G292 (CDR G293))
Function: #<FSubr-LET*: #96059a0>
Arguments:
  ((CLICK-DETAIL-FINDERS (APPLY (FUNCTION VECTOR) (DO-BUILD-LIST ((BOTTOM (POP FREQUENCY-LIST) TOP) (TOP (POP FREQUENCY-LIST) (POP FREQUENCY-LIST))) (NOT TOP) (CLICK-DETAIL-FINDER SND BOTTOM TOP STEPS-PER-BLOCK STEP-SIZE (IF USE-LATE-STEEPNESS-TEST (SQRT RELATIVE-THRESHOLD) RELATIVE-THRESHOLD) ABSOLUTE-THRESHOLD)))) (INTERLEAVED-FINDER (INTERLEAVE-CLICK-DETAIL-FINDERS CLICK-DETAIL-FINDERS (SND-T0 SND) (/ STEP-SIZE (SND-SRATE SND)) LIMIT-TIME-STEP)))
  (COMBINE-CLICK-DETAILS INTERLEAVED-FINDER (IF USE-LATE-STEEPNESS-TEST RELATIVE-THRESHOLD NIL) TAG COMBINE-UNEQUAL-LENGTHS)
Function: #<Closure-MAKE-CLICK-ITERATOR: #951ec00>
Arguments:
  #<Sound: #b333d658>
  (150 150.062 150.125 150.187 150.25 150.312 150.375 150.437 150.5 150.563 150.625 150.688 150.75 150.813 150.876 150.939 151.001 151.064 151.127
... <snip - lots of data here>...
9528.4 9532.37 9536.33 9540.3 9544.27 9548.24 9552.21 9556.18 9560.16 9564.13 9568.11 9572.09 9576.07 9580.06 9584.04 9588.03 9592.02 9596.01 9600)
  100
  0
  1.41254
  0.001
  NIL
  0.0245014
Function: #<FSubr-LET*: #96059a0>
Arguments:
  ((T0 (SND-T0 SND)) (SRATE (SND-SRATE SND)) (CLICK-ITERATOR (MAKE-CLICK-ITERATOR SND FREQUENCY-LIST CONTROL-STEPS-PER-BLOCK STEP-SIZE (DB-TO-LINEAR CONTROL-RELATIVE-THRESHOLD) (DB-TO-LINEAR CONTROL-ABSOLUTE-THRESHOLD) TAG (/ (GET-DURATION 1) 100))))
  (IF (< CONTROL-ACTION-CHOICE 2) CLICK-ITERATOR (LET* ((STEP-DUR (/ STEP-SIZE SRATE)) (CROSSFADE-INTERVAL (/ (ROUND (* (/ CONTROL-CROSSFADE-INTERVAL 1000) SRATE)) SRATE)) (CLICK-FIXER (EQ-BANDS-CLICK-FIXER FREQUENCY-LIST CONTROL-STEPS-PER-BLOCK STEP-DUR SRATE CROSSFADE-INTERVAL NIL)) (DIFFERENCES (COMPUTE-DIFFERENCES SND CLICK-ITERATOR CLICK-FIXER CONTROL-STEPS-PER-BLOCK STEP-SIZE CROSSFADE-INTERVAL))) (CASE CONTROL-ACTION-CHOICE (2 (SUM (SND-CONST 0 T0 SRATE (/ (SND-LENGTH SND NY:ALL) SRATE)) DIFFERENCES)) (3 (SUM SND DIFFERENCES)))))
Function: #<Closure-MAKE-CHANNEL-RESULTS: #94b96e8>
Arguments:
  #<Sound: #b333d658>
  (150 150.062 150.125 150.187 150.25 150.312 150.375 150.437 150.5 150.563 150.625 150.688 150.75 150.813 150.876 150.939 151.001 151.064 151.127 151.19 
... <snip - lots of data here>...
9528.4 9532.37 9536.33 9540.3 9544.27 9548.24 9552.21 9556.18 9560.16 9564.13 9568.11 9572.09 9576.07 9580.06 9584.04 9588.03 9592.02 9596.01 9600)
  0
  NIL
Function: #<FSubr-LET: #9603ae8>
Arguments:
  ((CHANNEL-RESULTS (MAKE-CHANNEL-RESULTS SND FREQUENCY-LIST STEP-SIZE NIL)))
  (IF (SOUNDP CHANNEL-RESULTS) CHANNEL-RESULTS (MAKE-LABELS CHANNEL-RESULTS (/ STEP-SIZE (SND-SRATE SND)) (= CONTROL-ACTION-CHOICE 1)))
Function: #<Closure-MAKE-MONO-RESULTS: #94b9418>
Arguments:
  #<Sound: #b333d658>
  (150 150.062 150.125 150.187 150.25 150.312 150.375 150.437 150.5 150.563 150.625 150.688 150.75 150.813 150.876 150.939 151.001 151.064 151.127 151.19 
... <snip - lots of data here>...
9528.4 9532.37 9536.33 9540.3 9544.27 9548.24 9552.21 9556.18 9560.16 9564.13 9568.11 9572.09 9576.07 9580.06 9584.04 9588.03 9592.02 9596.01 9600)
  0
Function: #<FSubr-IF: #9607170>
Arguments:
  (ARRAYP S)
  (MAKE-STEREO-RESULTS S FREQUENCY-LIST STEP-SIZE)
  (MAKE-MONO-RESULTS S FREQUENCY-LIST STEP-SIZE)
Function: #<FSubr-OR: #9607128>
Arguments:
  (IF (ARRAYP S) (MAKE-STEREO-RESULTS S FREQUENCY-LIST STEP-SIZE) (MAKE-MONO-RESULTS S FREQUENCY-LIST STEP-SIZE))
  "Found no labels"
Function: #<FSubr-LET*: #96059a0>
Arguments:
  ((FREQUENCY-LIST (FIND-FREQUENCIES)) (SRATE (SND-SRATE (IF (ARRAYP S) (AREF S 0) S))) (STEP-SIZE (TRUNCATE (* SRATE (/ CONTROL-SEPARATION (* 1000 CONTROL-STEPS-PER-BLOCK))))))
  (OR (IF (ARRAYP S) (MAKE-STEREO-RESULTS S FREQUENCY-LIST STEP-SIZE) (MAKE-MONO-RESULTS S FREQUENCY-LIST STEP-SIZE)) "Found no labels")
Function: #<FSubr-COND: #9605e5c>
Arguments:
  ((AND USE-LARGE-WINDOWS (/= CONTROL-BAND-TYPE 0)) "Large windows not recommended for linear frequency step")
  ((= CONTROL-FREQUENCY-BOUND1 CONTROL-FREQUENCY-BOUND2) "Please specify a nonempty frequency range")
  (T (LET* ((FREQUENCY-LIST (FIND-FREQUENCIES)) (SRATE (SND-SRATE (IF (ARRAYP S) (AREF S 0) S))) (STEP-SIZE (TRUNCATE (* SRATE (/ CONTROL-SEPARATION (* 1000 CONTROL-STEPS-PER-BLOCK)))))) (OR (IF (ARRAYP S) (MAKE-STEREO-RESULTS S FREQUENCY-LIST STEP-SIZE) (MAKE-MONO-RESULTS S FREQUENCY-LIST STEP-SIZE)) "Found no labels")))
1>

These were the settings that caused the error:
error-condition.png

I hope you think the repair is good enough. Repair uses eq-band with constant values, not curves.

The error you got was because of the extreme setting for the number of bands.

I’ve not tested much but as long as the repaired sections occur during quiet sections it seems to work pretty well (which I guess will usually be the case for “mouth smacks”). In music it can make unpleasant “holes” in the sound, so I think that an implementation for music would require a different repair algorithm (but that’s outside of the scope of this plug-in).

For a “release” version this will need to be limited to a safe value, but that’s a minor issue. It’s coming along great and when finished I think it will be very popular with people making audio books.

I have had success removing some very obvious clicks that are not during silence but are superposed on the vowels. I can also remove the rattles that afflict some sibilant sounds, thus removing low frequency noise from high frequency signal. There are also lip closing sounds as in “sp” and “sm” which can be distracting things in the range of hundreds of Hz, and many of those are mitigated.

In case of music, maybe drumbeats would be wrongly repaired if block size is longer than the beat. Maybe a growling bass guitar would make many false positives too if the pitch is below the reciprocal of block length, a Hz value. Perhaps different setting could be found that work for different tracks.

As for the extreme value error, perhaps I should eliminate the choice of linear steps and specify the range by low frequency, step in semitones, and number of steps: no high value. (Some effects have connected sliders that are not independent but I don’t know that Nyquist supports that.) Then a lower bound on semitones and on low frequency. Because I believe the bad extreme case depends on both of those. The upper limit on number of bands to avoid the eror would depend on two other things, low and high frequency, not one.

Not yet, but we hope to have that in version 4 plug-ins. Don’t hold your breath though, it could be quite a long wait before we get version 4.

Could perhaps the “number of bands” control be replaced by a “frequency resolution” control (not necessarily those words) so that it is a scale from 0 to 100, minimum number of bands to maximum number of bands? You could then have the maximum (100 on the scale) translate into a safe maximum for the other dependent settings. Just an idea.

Most users don’t read the manual (sad fact of life :wink:) so controls need to be as self explanatory as possible, even (especially) for technophobes.

Indeed. I already eliminated choices in repair method and used what I thought best – for each click, a composition of eq-band calls with constant parameters, not curves. Just let the crossfade make transitions. Variable parameter eq-band in Nyquist is suspect anyway, I understand, and highpass and lowpass don’t let me use all the information I gather about the noisiness of each band.

I could eliminate stepping by Hz and always go by semitones because that is the more sensible choice for generating the parameters for (logarithmically) equal width bands fixed by eq-band. Even if you are only using the tool for detection. One less mystifying control.

I hope the rest of the controls are self explanatory though many, and I thought I sequenced them according to what the user would most likely want to vary.

steve, please share examples of bad sounding repairs of music. I would like to make this tool more broadly useful. Maybe it is already and I just need to suggest better settings.

One of the advantages of making a tool that is for a specific job (in this case removing mouth smacks) there is often a lot of optimisation that can be done for that specific task. So, for example, if you find that one control always works best when set between 45% and 55% you can probably do without that control and hard code it to 50%.

One way that I’ve done this in some of my plug-ins is to comment out “advanced” controls like this:

;;control var3 "Advanced parameter" real "" 50 0 100

(when (not (boundp 'var3))(setq var3 50.0))

This way the full featured code remains intact and can be reused in an “advanced” or otherwise modified version.

Here’s a before / after example. The click is attenuated, but it leaves a “hole” in the sound:

I tried adjusting some of the settings but go the error:

"SFT3F window: 228857 samples 780 periods 150.303 Hz"
"Correct thresholds 1.6788 by 1 and 0.001 by 1"
"window pieces for band at 150 Hz: 1041"
error: argument stack overflow

I think it was the “Number of bands” that triggered this error, but I’ve not got time to investigate further right now.

I took a close look at this example. There are just seven anomalous samples making a very audible click. Is this a natural noise or some sort of tape noise? I suppose it’s the latter and my tool is just not well adapted to that. But the Repair effect handles it just fine.

By way of comparison, this is the “Repair” effect (before and after):

By the way, that is not a criticism of the excellent work that you have done.

I would like to make this tool more broadly useful.

That occurred to me as well. This is the “Paul L Mouth Smack Filter.” The chances of making it work with somebody else’s mouth geometry may be remote. You can certainly make sliders and adjustments so the tool is valuable to multiple different people, but where are you going to get the testers and what are you going to call the sliders? As far as I know, there is not a wide agreement on the name of each mouth noise (“plucking tongue snap,” “epiglottal vacuum release”), and the other option, what I call the “Geek Option,” describes in math what the tool does. Programmers love this one. “Nyquist Differential Velocity Derivation [higher/lower]?”

There was a room echo generator program available a while back and I called them on it. Adjusting the trig calculation functions in the program is not useful. I want a slider for “Room Size.”

If you do manage to resolve all that, you will be a hero to plucking tongue snappers everywhere.

Koz

I think that is what we’re aiming for :slight_smile:

Oh, none taken. Just discovering limitations of these methods.

What do you think caused the seven sample glitch in your example? A defect of old media, not a real but undesirable sound picked up by the microphone? Repair might help the first, then, and my tool the other. Neither replaces the other.

I don’t think this is especially tuned to me and my voice. I think I have good defaults that I invite you to just try if you have no patience for the sliders.

I don’t think I have found optimal settings, but pretty good ones.

But who knows, I haven’t tried it with women’s and children’s voices.

Here is a brief example of what I can fix, and I’m very pleased to fix it automatically. “Before” is in the left channel, “after” in the right. Zoom in about 0.44 seconds. There is an irritating high frequency click that got mostly filtered away, enough to be inaudible to my ears.

But this is about 240 samples, or 6ms, long, and it’s a “real world” oscillating and decaying sound riding on the vowel that I want to hear. (A bubble of spittle popping under the tongue or some such icky happening.)

It’s an “angry” looking, dense blue scribble if you zoom out to fit 1/10 second on the screen, but it’s actually a very long and low-amplitude defect, compared with Steve’s example of just seven bad samples.

Use shift-click to loop the selection and hear the difference.

If you look at spectrogram views of Steve’s example, which I don’t fix well, and my example, which I do, you can see a difference (and I suggest in Edit, Preferences, spectrograms you choose 256 and Blackman-Harris):

In both cases, the click appears as a bright, narrow, vertical stripe, but in my example, that stripe has a rather definite bottom at about 3kHz, below which the sound just seems to oscillate like the surrounding parts, so those frequencies are left mostly untouched by filtering.

But the anomaly in Steve’s recording is so sharp and narrow that the stripe has no bottom. (“Convolving with the delta function is an identity.” Cosines of all frequencies react to it. Hand wave, hand wave.) Zoom on low frequencies and you still see it. So my automatic detection and filtering tries to attenuate all frequencies and you get a drop-out.

I’m not doing exactly what spectrogram view does mathematically, but something like it, when detecting what to fix.

Now I wonder if the detection methods I use might be combined with the repair method of the Repair effect so that you could indeed have a tool that fixes glitches of this kind without zooming in and precise selection. That wouldn’t be suited to my purposes, but heck, it might even all be rolled up in one tool. Fix things that are “loud” in all frequencies at once with Repair, and things that are loud in only some frequencies with eq-band.

Except I don’t know what the method of Repair is, and it’s not in easily readable .ny code.