display RMS intensity in value

Hello to somebody that reads this.
I made a small plugin to take a measurement of RMS intensity.
Finally it does the same thing than vu-meter does in real time, exept that you can get the value written and modulate measurement duration. So very simple idea, but maybe to simple to be thought…
I needed it because my purpose was to evaluate the loudness of a compression effect (or limiter) that make the sound louder in RMS, or/and compare it with some professionnal mixed tracks to understand how they work, if i used it to much or not, etc…
It would be fine to integrate this functionnality in audacity (developer if you read this)


;nyquist plug-in
;version 3
;type analyze
;name "measure RMS Level"
;action "Track RMS Level measurement"
;info "Can be helpfull to manage compression, Limiting (like how many i made my soud louder with the same peak, etc...), by Gabriel D. (France)"



; Mono mode

(defun monomes (signal)

(setq smplrate *sound-srate*)
(setq mono-nb-sample-tot (snd-length signal (* 44100 1200)))     ; use snd-length function wich return sample number
(setq mono-lenght-tot (/ (float mono-nb-sample-tot) smplrate) )  ; my variable names are confusing because it
                                                                 ; look like function name (i comment)

(setq mono-avg2 0)

(setq mono-s2 (rms signal))                                      ; using rms function wich return a sound containing
(setq mono-nb-sample (snd-length mono-s2 (* 44100 1200)))        ; rms values
(setq mono-lenght (/ (float mono-nb-sample) smplrate) )          ; number of samples in the sound generated by rms 
                                                                 ; function
(setq count 2 )                                                  ; initialize counter of while

(setq mono-sample (snd-fetch mono-s2) )
(setq mono-sample2 (snd-fetch mono-s2) )                         ; initialize variables of while
(setq mono-sample3 (snd-fetch mono-s2) )                         ; accessing sound samples with snd-fetch function

(setq mono-sample4 (snd-fetch mono-s2) )
(setq mono-sample5 (snd-fetch mono-s2) )
(setq mono-avg2 (/ (+ mono-sample4 mono-sample5) 2))             ; pour initialiser mono-avg2



(while (< count (- (/ mono-nb-sample 3) 1) )                     ; calculate a mean value of all rms samples values
                                                                 ; previously outputed.
                                                                 ; it's correct to do this way because rms is an
                                                                 ; absolute value of an average, so not usefull to
(setq mono-avg1 (/ (+ mono-sample mono-sample2 mono-sample3) 3)) ; calculate root and square if already absolute
(setq mono-avg2 (/ (+ mono-avg1 mono-avg2) 2))

(setq mono-sample (snd-fetch mono-s2) )                          ; take 3 samples at a step of count
(setq mono-sample2 (snd-fetch mono-s2) )                         ; and then calculate average at the beginning while
(setq mono-sample3 (snd-fetch mono-s2) )                         ; then 2nd mean calculation with a "remaining"
                                                                 ; variable 
                                                                 ; mono-avg2 that contain final value (linear)
(setq count (+ count 1) )                                                                

)

(setq db-mono-avg2 (linear-to-db mono-avg2 ))                   ; using linear-to-db function to convert in dB.
                                                                ; formula is 20*log(x), not 10*log(x)


(setq meslimit (truncate (/ (* 275 44100) smplrate)) )          ; set limit to big: 12127500 sample max
(setq minlim (truncate (/ meslimit 60)) )                       ; can't explain why it go crazy uper
(setq seclim (- meslimit (* minlim 60)) )                       ; convert in time

(if (> mono-lenght-tot meslimit)                                ; display error to big

(format nil "Selection must be less than ~amin ~as~% (or ~a sec), ~%for ~a Hz sample rate. ~%~%Please, take smaller selection." minlim seclim meslimit smplrate)

(format nil                                                     ; display result

       "RMS INTENSITY LEVEL MESURE
(Mono Mode)~%~%
Track selection RMS Level:   ~%~a  dB ~%
Time lenght of the selection:   ~%~a  secondes     
Number of samples in selection:   ~%~a  ~%
Normal values should be between 0 and -50 dB.  " db-mono-avg2 mono-lenght-tot mono-nb-sample-tot) )

)



; Stereo mode

(defun stereomes (signal)

;; Left Channel part of stereo mode --------------------------------

(setq left-signal (aref signal 0))                                ; loading left channel (stereo sound is an array)
(setq smplrate *sound-srate*)
(setq left-nb-sample-tot (snd-length left-signal (* 44100 1200))) ; in a sound variable using aref. left indice=0
(setq left-lenght1 (/ (float left-nb-sample-tot) smplrate) )      ; use snd-length function wich return sample number

(setq left-avg2 0)

(setq left-s2 (rms left-signal))                                  ; using rms function wich return a sound containing
(setq left-nb-sample (snd-length left-s2 (* 44100 1200)))         ; rms values
(setq leftplouf (/ (float left-nb-sample) smplrate) )             ; number of samples in the sound  
                                                                  ; previously generated by rms function
(setq left-count 2 )                                              ; initialize counter of while


(setq left-sample (snd-fetch left-s2) )
(setq left-sample2 (snd-fetch left-s2) )                          ; initialize variables of while
(setq left-sample3 (snd-fetch left-s2) )                          ; accessing sound samples with                   
                                                                  ; snd-fetch function
(setq left-sample4 (snd-fetch left-s2) )
(setq left-sample5 (snd-fetch left-s2) )
(setq left-avg2 (/ (+ left-sample4 left-sample5) 2))              ; pour initialiser left-avg2


(while (< left-count (- (/ left-nb-sample 3) 1) )                 ; calculate a mean value of all rms samples value
                                                                  ; previously outputed.
                                                                  ; it's correct to do this way because rms is an
                                                                  ; absolute value of an average, so not usefull to
(setq left-avg1 (/ (+ left-sample left-sample2 left-sample3) 3))  ; calculate root and square if already absolute
(setq left-avg2 (/ (+ left-avg1 left-avg2) 2))

(setq left-sample (snd-fetch left-s2) )                           ; take 3 samples at a step of left-count
(setq left-sample2 (snd-fetch left-s2) )                          ; and then calculate average at the beginning while
(setq left-sample3 (snd-fetch left-s2) )                          ; then 2nd mean calculation with a "remaining"
                                                                  ; variable
                                                                  ; left-avg2 that contain final value (linear)
(setq left-count (+ left-count 1) )

)

(setq left-db-avg2 (linear-to-db left-avg2 ))                     ; using linear-to-db function to convert in dB.
                                                                  ; formula is 20*log(x), not 10*log(x)



;; Right Channel part of stereo mode --------------------------------

(setq right-signal (aref signal 1))                                  ; loading left channel (stereo sound is an array) 
(setq right-nb-sample-tot (snd-length right-signal (* 44100 1200)))  ; in a sound variable using aref. right indice=1
(setq right-lenght1 (/ (float right-nb-sample-tot) smplrate) )          ; use snd-length function wich return sample
                                                                     ; number
(setq right-avg2 0)

(setq right-s2 (rms right-signal))                                   ; using rms function wich return a sound
(setq right-nb-sample (snd-length right-s2 (* 44100 1200)))          ; containing rms values
(setq rightplouf (/ (float right-nb-sample) smplrate) )                 ; number of samples in the sound
                                                                     ; previously generated by rms function
(setq right-count 2 )                                                ; initialize counter of while


(setq right-sample (snd-fetch right-s2) )
(setq right-sample2 (snd-fetch right-s2) )                           ; initialize variables of while
(setq right-sample3 (snd-fetch right-s2) )                           ; accessing sound samples with
                                                                     ; snd-fetch function
(setq right-sample4 (snd-fetch right-s2) )
(setq right-sample5 (snd-fetch right-s2) )
(setq right-avg2 (/ (+ right-sample4 right-sample5) 2))              ; pour initialiser right-avg2


(while (< right-count (- (/ right-nb-sample 3) 1) )                  ; calculate a mean value of all rms samples value
                                                                     ; previously outputed.
                                                                     ; it's correct to do this way because rms is an
                                                                     ; absolute value of an average, so not usefull to
(setq right-avg1 (/ (+ right-sample right-sample2 right-sample3) 3)) ; calculate root and square if already absolute
(setq right-avg2 (/ (+ right-avg1 right-avg2) 2))

(setq right-sample (snd-fetch right-s2) )                            ; take 3 samples at a step of right-count
(setq right-sample2 (snd-fetch right-s2) )                           ; and then calculate average at the beginning while
(setq right-sample3 (snd-fetch right-s2) )                           ; then 2nd mean calculation with a "remaining"
                                                                     ; variable
                                                                     ; right-avg2 that contain final value (linear)
(setq right-count (+ right-count 1) )

)

(setq right-db-avg2 (linear-to-db right-avg2 ))                      ; using linear-to-db function to convert in dB.
                                                                     ; formula is 20*log(x), not 10*log(x)


(setq meslimit (truncate (/ (* 275 44100) smplrate)) )               ; set limit to big: 12127500 sample max
(setq minlim (truncate (/ meslimit 60)) )                            ; can't explain why it go crazy uper
(setq seclim (- meslimit (* minlim 60)) ) 


(if (> right-lenght1 meslimit)                                            ; display error to big

(format nil "Selection must be less than ~amin ~as~% (or ~a sec), ~%for ~a Hz sample rate. ~%~%Please, take smaller selection." minlim seclim meslimit smplrate)

(format nil                                                          ; display stereo result
               "RMS INTENSITY LEVEL MESURE
(Stereo Mode)~%~%
Left Channel selection RMS Level:   ~%~a  dB ~%
Right Channel selection RMS Level:   ~%~a  dB  ~%
Time lenght of the selection:   ~%~a  secondes     
Number of samples in selection:   ~%~a  ~%
Normal values should be between 0 and -50 dB.  " left-db-avg2 right-db-avg2 right-lenght1 right-nb-sample-tot) )

)




; Main program ----------------------------------


(if (arrayp s)          ; test if sound is stereo or mono
                        ; (Is it an array?)
(setq nbch 2)           ; if stereo nbch=2

(setq nbch 1) )         ; if mono nbch=1



; Apply functions previously defined

(if (= nbch 1)

 (monomes s)
 (stereomes s)

)

You need to be careful about working with long selections. Bail out before processing rather than processing and then warning. When I tested this with an hour of audio, AI got the warning, but then Audacity appeared to freeze for quite a long time.

If you are using a recent version of Audacity (recommended), then there is a much simpler way to approach this. It is also much faster, and safe to use on long selections:

;nyquist plug-in
;version 4
;type analyze
;name "RMS"
;author "Steve Daulton"
;copyright "Released under terms of the GNU General Public License version 2"

(defun stereo-rms(ar)
  "Stereo RMS is the root mean of all (samples ^ 2) [both channels]"
  (let ((left-mean-sq (* (aref ar 0)(aref ar 0)))
        (right-mean-sq (* (aref ar 1)(aref ar 1))))
    (sqrt (/ (+ left-mean-sq right-mean-sq) 2.0))))

(let ((rms (get '*selection* 'rms)))
  (if (arrayp rms)
      (format nil "Left: \t~a dB~%~
                  Right: \t~a dB~%~
                  Stereo: \t~a dB"
                  (linear-to-db (aref rms 0))
                  (linear-to-db (aref rms 1))
                  (linear-to-db (stereo-rms rms)))
      (format nil "Mono: \t~a dB" (linear-to-db rms))))

There’s a load more handy features for version 4 plug-ins documented here: http://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference#Version_4_Property_Lists

Ok thanks, for sure now it will be correct with your “official” approach.
However i putted a limit to prevent taking long selections, it’s weird that it didnt work…

i wonder if it was truly usefull to write so much, for a so small light solution