From: Bernd Beuster
Subject: Loudness War
Date: 
Message-ID: <f4hlin$lbc$1@online.de>
After reading http://slashdot.org/articles/07/06/09/0526201.shtml I
wrote a small hack in order to check some of my CDs.


(defun wave-file-rms-db (file-name &optional number-samples)
  "Calculate relative RMS level of WAVE file in dB \(EBU recommendation:
-18 dB\)."
  (let* ((n         (or number-samples (wave-file-number-samples
file-name)))
	 (rms       (wave-file-rms file-name n))
	 (0dB-level (/ (expt 2 15) (sqrt 2.0))))
    (* 20.0 (log (/ rms 0dB-level) 10))))


(defun wave-file-rms (file-name &optional number-samples)
  "Calculate absolute RMS level of WAVE file."
  (let* ((n (or number-samples (wave-file-number-samples file-name))))
    (with-open-file (stream file-name :direction :input :element-type
'(signed-byte 16))
      (file-position stream (/ 44 2))  ; advance to data chunks audio data
      (sqrt (/ (loop repeat n
		 for sample = (read-byte stream)
		 summing (* sample sample))
	       n)))))


(defun wave-file-number-samples (file-name)
  "Return number of 16-bit samples of WAVE file."
  (with-open-file (stream file-name :direction :input :element-type
'(unsigned-byte 32))
    (file-position stream (/ 40 4))  ; advanve to data chunk length of
audio data
    (/ (read-byte stream) 2)))       ; divide by 2 because 16-bit


E.g. I have the same Led Zeppelin song on to different CDs:

CD #1: -15.2 dB
CD #2: -12.1 dB

Worst track I found is "The Who: I can't explain" with -6.06 dB!


Unfortunately wave-file-rms-db conses a lot.

-- 
Bernd

From: Thomas A. Russ
Subject: Re: Loudness War
Date: 
Message-ID: <ymi4ples2yj.fsf@sevak.isi.edu>
Bernd Beuster <·············@lycos.de> writes:

> After reading http://slashdot.org/articles/07/06/09/0526201.shtml I
> wrote a small hack in order to check some of my CDs.
> 
> 
> (defun wave-file-rms-db (file-name &optional number-samples)
>   "Calculate relative RMS level of WAVE file in dB \(EBU recommendation:
> -18 dB\)."
>   (let* ((n         (or number-samples (wave-file-number-samples
> file-name)))
> 	 (rms       (wave-file-rms file-name n))
> 	 (0dB-level (/ (expt 2 15) (sqrt 2.0))))
>     (* 20.0 (log (/ rms 0dB-level) 10))))

Well, for starters on this one, I would use the default value feature of
the lambda list instead of the OR for the value.  I would also predefine
a constant for the 0db-level:

(defconstant +0db-level+ (/ (expt 2 15) (sqrt 2.0)))

(defun wave-file-rms-db (file-name 
                         &optional (number-samples (wave-file-number-samples file-name)))
  "Calculate relative RMS level of WAVE file in dB \(EBU recommendation:
-18 dB\)."
   (* 20.0 (log (/ (wave-file-rms file-name number-samples) 10))))


There are a couple of other minor optimizations possible as well, such
as multiplying by 0.1 rather than dividing by 10.  (You may get some
floating point rounding errors but for your purposes this shouldn't
unacceptable).


> (defun wave-file-rms (file-name &optional number-samples)
>   "Calculate absolute RMS level of WAVE file."
>   (let* ((n (or number-samples (wave-file-number-samples file-name))))
>     (with-open-file (stream file-name :direction :input :element-type
> '(signed-byte 16))
>       (file-position stream (/ 44 2))  ; advance to data chunks audio data
>       (sqrt (/ (loop repeat n
> 		 for sample = (read-byte stream)
> 		 summing (* sample sample))
> 	       n)))))


> Unfortunately wave-file-rms-db conses a lot.

The real key to this is to make sure you can perform the calculation
using non-boxed values as much as possible.  That will require adding
type declarations to your code and compiling with speed high and safety
low.  One of the problems with that approach is that you are summing the
square of the 16-bit samples, so it is unlikely that will fit in a
FIXNUM on a 32-bit lisp implementation.  And using BIGNUMs will pretty
much force consing.  Boxing floats will also promote consing.

So, you need to try to figure out a way to avoid the consing.  Some
lisps may be able to use 32-bit integers, suitably declared, but adding
two 32-bit integers can also cause you to overrun the efficiently
representable value space.

Unfortunately, right at the moment, I can't think of a way around this,
but perhaps some of the more knowledgable numeric Lispers can help out.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Bernd Beuster
Subject: Re: Loudness War
Date: 
Message-ID: <f4kbaj$mgc$1@online.de>
Thomas A. Russ schrieb:
> (defconstant +0db-level+ (/ (expt 2 15) (sqrt 2.0)))

It can be done without constant definition also.

(let* (0dB-level #.(/ (expt 2 15) (sqrt 2.0)))
...

> So, you need to try to figure out a way to avoid the consing.  Some
> lisps may be able to use 32-bit integers, suitably declared, but adding
> two 32-bit integers can also cause you to overrun the efficiently
> representable value space.
> 
> Unfortunately, right at the moment, I can't think of a way around this,
> but perhaps some of the more knowledgable numeric Lispers can help out.

Switching to floating point would help, but unfortunately adding
millions of small FP numbers to a large sum is very inaccurate.

Run time is maximal ten seconds for a track, that's acceptable for me.

-- 
Bernd
From: Joe Marshall
Subject: Re: Loudness War
Date: 
Message-ID: <1181605707.548132.210160@i38g2000prf.googlegroups.com>
On Jun 11, 1:29 pm, Bernd Beuster <·············@lycos.de> wrote:

> Switching to floating point would help, but unfortunately adding
> millions of small FP numbers to a large sum is very inaccurate.

This is a blanket statement that may or may not be true.
As a counter-example, imagine adding 1 billion integers in
the range [0 - 100).  Assuming double-precision, your answer
would be exactly correct.