A weighting again

Hello, my first post on the forum.

Some years ago on the mailing list I made some analysis plugins based on Mikael O’s A weighting plugin. But I’ve lost them and I’m recreating them from old emails…

But this base plugin that started it all no longer works - it should return two labels with Leq and LMax, but instead it applies the a-weighting filter to the audio. I’ve been through it line by line with a two channel version that works, but I can’t work out what’s going on.

If you’ve set “type analyze:” it shouldn’t return audio at all anyway, should it? Can anyone help me?



;nyquist plug-in
;version 1
;type analyze
;name "Equivalent and maximum dB(A)..."
;action "Calc. A-weighted equivalent level (LAeq) and maximum level with time weighting FAST (LAFmax)..."

; Mikael Ogren, mr.ogren@...
; 2007-01-12
; Licensed under GPL, no warranty, use at your own risk...

; Calibration so that a 1000 Hz tone with amplitude 1.0 gives 94 dB
(setq calibration (+ 94 28.2))

; A-weighting by Edgar (thanks!)
(setq sa (lp (lp (hp (hp (hp (hp s 20.6) 20.6) 107.7) 737.9) 12200) 12200) )

; Exponential time-weighting filter FAST (125 ms)
; snd-avg is used to downsample to 100 Hz (by averaging over 441 samples)
; This only works for 44.1 kHz sampling frequency, perhaps someone can help out here
; by making a more general approach that works for all sampl. frq?
; The filtering part is OK for all frequencies, but the "441" constant is not.
; -
; The constant 0.000001 is to avoid clipping at filtered squared pressure > 1.0
(setq saf2(mult 0.000001(snd-avg (snd-biquad (mult sa sa ) 1 0 0 (exp (/ 1 (mult (snd-srate sa) - 0.125))) 0 0 0) 441 441 OP-AVERAGE)))

; Length of the downsampled pressure squared signal
(setq mlength(snd-length saf2 99999999999))

; Calc. the equivalent level
(setq leq(+ calibration (* 0.5 (linear-to-db (snd-maxsamp (snd-avg saf2 mlength mlength OP-AVERAGE) )))));

; Calc. the maximum level
(setq lmax
(+ calibration (* 0.5 (linear-to-db (snd-maxsamp saf2)))));

; Set the output format to 3 digits (example: 53.3 dB)
(setq *float-format* "%#3.3g");

; Output result as a label track (or append into existing label track)
(setq u (format NIL "LAeq= ~A  LAFmax= ~A" leq lmax))
(list (list 0.0 u))

Line 24

(setq saf2(mult 0.000001(snd-avg (snd-biquad (mult sa sa ) 1 0 0 (exp (/ 1 (mult (snd-srate sa) - 0.125))) 0 0 0) 441 441 OP-AVERAGE)))

it should have:

-0.125

not

- 0.125

(there should be no space between the “-” and “0.125”)

That particular expression would be better written as:

(exp (/ (* -0.125 (snd-srate sa))))

It’s really quite bad style to write huge long lines like that which then require a paragraph of explanation, and, as you’ve found, it makes the code difficult to debug.

To answer a question in the code comment:
The 441 could be replaced by

(setf step (round (/ *sound-srate* 100.0)))

Then use “step” instead of 441.

Thank you so much, it’s working again.

Yes the long lines also make it difficult to understand what’s going on.

The code should also look like this:

;nyquist plug-in
;version 1
;type analyze
;name "Equivalent and maximum dB(A)..."
;action "Calc. A-weighted equivalent level (LAeq) and maximum level with time weighting FAST (LAFmax)..."

; Mikael Ogren, mr.ogren@...
; 2007-01-12
; Licensed under GPL, no warranty, use at your own risk...
;
; Set the output format to 3 digits (example: 53.3 dB)
(setq *float-format* "%#3.3g");
; A-Weighting Filter 
(defun cascade (s freqs &aux (f (car freqs)))
  (cond
    ((null f) s)
    ((minusp f) (cascade (lp s (- f)) (cdr freqs)))
    (t (cascade (hp s f) (cdr freqs)))))
;;
;; Main 
(let* (
   ; Calibration so that a 1000 Hz tone with amplitude 1.0
   ; gives 94 dB
   (calibration (+ 94 28.2))
   ; A-weighting frequencies by Edgar (thanks!)
   (freqs '(20.6 20.6 107.7 737.9 -12200 -12200))
   (sa (cascade s freqs))
   (step (round (/ *sound-srate* 100.0)))
   (saf2 (snd-avg (biquad (prod sa sa) 
                          1e-6   0 0 1   (exp (/ -8 (snd-srate sa))) 0)
                  step step OP-AVERAGE))
   ; Length of the downsampled pressure squared signal
   (mlength(snd-length saf2 ny:all))
   ; Calc. the equivalent level
   (leq (+ calibration (* 0.5 (linear-to-db (snd-maxsamp (snd-avg saf2 mlength mlength OP-AVERAGE))))))
   ; Calc. the maximum level
   (lmax (+ calibration (* 0.5 (linear-to-db (snd-maxsamp saf2)))));
   ; Output result as a label track (or append into existing label track)
   (u (format nil "LAeq= ~A  LAFmax= ~A" leq lmax)))
 u; Sends output to message box if next is commented out
; (list (list 0.0 u))
); end let*

The 0.000001 gain is completely in the biquad section and the exp term is also simplified.
The filtering is “out-sourced”.
Is the filter correct, has any one checked against all frequencies?
(lp and hp are not “normal” filter types)
I think mlength can get too high for long audio and snd-avg won’t therefore return a valuable result for LAeq.

Thanks Robert, that is much more readable :slight_smile:

Also to mention, it helps a lot if variable names are meaningful. For example, what are “SA” and “SAF2”?
It is good to keep variable names reasonably brief, but sufficiently long to be clear what they are.

I’ve previously checked the A-weighting filter from Edgar-rft and it is a reasonably close approximation. I’ve not checked the biquad expression.

Hi!

Thanks for shaping up my old code!

Just a brief note that I have checked the code vs a class 1 professional analyzer, with results within +/- 0.1 dB, for sampling frequencies 44.1 and 48 kHz.

sa - the variable name is from the fact that Nyquist uses “s” as variable name for the sound. “sa” - A-weighted sound, “saf” - A-weighted with time weighting FAST.