How to compute SPL from sound inputed from a microphone.

Is the snd-avg function written in lisp? I tried looking for the source code and couldn’t find it.

No, I think it’s written in C, but I know so little about C programming that I wouldn’t know what to look for to find it.

It looks like there is some demand for this type of plug-in

Hmm, is that really applicable? It seems to be a shortcut for doing processing on one or two channels, and returning audio in the same format (zipping the two channels back together again afterward). I’m outputting text, and the output should be different depending on whether it’s mono or stereo.

For stereo tracks, are you wanting to output data for the combined channels, or “left channel data” / “right channel data”?

It should show each track individually. In my other script, if both tracks are identical, it says so, and then displays one set of statistics. I don’t really care if it does this or not though.

Then you could use (multichan-expand …), though it would probably be simpler to use (if (arrayp s)(analyze-stereo s)(analyze-mono s))

By wrapping up the analysis code into a function will remove the need for duplicate code.
This is the type of thing that I’m thinking of:

(defun analyze (s-in) ; code that does the analysis and returns data as a string
  (format NIL "Peak level ~a dB~%"
  (linear-to-db (peak s-in ny:all))))

(defun analyze-mono (input) ; for mono tracks
  (format NIL "Mono track.~%~a~%" (analyze input)))

(defun analyze-stereo (input) ; for stereo tracks
  (format NIL "Stereo track.~%Left Channel: ~aRight Channel: ~a~%" (analyze (aref input 0))(analyze (aref input 1))))

(if (arrayp s)(analyze-stereo s)(analyze-mono s))

You know anything about writing a replacement? I’m not sure how to work with individual samples here.

(snd-fetch-array sound len step)
Reads sequential arrays of samples from sound, returning either an array of FLONUMs or NIL when the sound terminates.

(snd-samples sound limit)
Converts the samples into a lisp array.

So I could read chunks of data from the sound, and calculate the average of each array using some regular lisp array average function?

I think you are already pretty close to achieving what you want, but I’m not sure exactly what you want.
I’m also hoping that you know all of the ins-and-outs of weighted measurements because my theory knowledge gets a bit shaky there.

Yeah I understand what I’m doing and what i want, I just need help with the lisp/nyquist part.

Oh good :slight_smile:
So what is this “replacement” thing that you want?

You can read chunks of data from the sound, but I’m not aware of a lisp array average function, so you would need to write one. Would that be better in some way to what you were doing before with snd-avg ?
Handling sounds on a sample level (such as reading samples into an array and then processing the array) tends to be slow and memory intensive in Nyquist. There are ways to keep the memory usage down to reasonable levels, but doing so can be quite tricky, and there is still the problem of slowness. Functions such as snd-avg are very much faster.

This is my first experience with lisp and I think I hate it. :slight_smile:

(snd-aargh (experience lisp me))

You do have a text editor that matches parentheses don’t you?
I started off using one that didn’t and I had mismatched parentheses on every other line.
Automatic syntax highlighting also helps a lot.

Do you have these bookmarks?

@ endolith - I’ve posted a plug-in based on the information in this thread here:

Latest version is here:

I gave up on doing the average calculation myself, and asked for help, but it doesn’t look promising:


Wait a second. Where did I get the idea that snd-avg does absolute value first and then computes the average?

If I select a full cycle of a sine wave and enter

(snd-maxsamp (snd-avg s (round len) (round len) OP-AVERAGE))

in the Nyquist prompt, it outputs a value close to 0. That would only occur if it was doing the actual average, which is what I wanted all along. :imp: / :smiley:

Edit again:

Ohhhhhhh. It does the average, and then does the absolute value. So it will always give a positive result even if the average is negative. That’s different.

That’s not as bad, though. I could probably test whether it’s negative or positive by subtracting it from the waveform and then testing if the new average is 0. If it’s not, the actual average is negative, and I need to go back and add it to the waveform instead. Convoluted, but workable. :smiley:



Here is how to get the actual average:

(snd-fetch (snd-avg s (round len) (round len) OP-AVERAGE))

This produces a negative number for negative samples and a positive number for positive samples.

(Still, the fact that no one (including 42 people on Stack Overflow) caught this in a month I think says something about the ease of using Nyquist…) :smiley:

I am going to go watch kitten videos and then finish the plug-in!

it works!
Nyquist measurement plug-in.png

Congratulations :slight_smile:

I think that it would be worth shifting this topic to the Nyquist board - OK with you or would you prefer to leave it here for now?

The two topics could possibly be merged, though I’m not sure if the design goals of the plug-ins are quite the same, so it may be better to keep them separate. This topic talks about SPL measurement, where I’m more concerned with just signal measurement. Is your end-result still concerning SPL measurement?

Unsurprisingly, since we’re both using Edgar’s A-weighted filter, the measurement results of both of our plug-ins are very close, though the “fudge factor” that you have used is still a little bit out. I’m not sure about the discrepancies that we both get at low, and particularly at high frequencies compared with the on-line calculator:
I am assuming that it is due to the A-weighted filter being slightly off from the theoretical curve. What do you think?
I also assume that the on-line calculator is accurate - how confident are you?
I’ve been considering trying a multi-band “graphic” equalizer instead of the nested lp/hp filters. An FFT filter would probably be the best solution, but that’s a bit beyond my programming abilities at the moment.

Re. SPL microphone measurements - these are normally displayed as a short term rolling average aren’t they? Would it suit your purposes better to output a series of rolling average values as labels, or as a text file, rather than just one average RMS/RMS(A) value?

I just stumbled across this thread.
These plug-ins apply A-weighting to calculate Leq using Nyquist on Audacity.
I would like to apply ITU-R 468 weighting to calculate Leq(m) that is being used for cinema audio.
May someone with better Lisp skills please add this weighting to the Plug-In?

If it is useful - In another project I used this Java code to approximate the CCIR-468 weighting:
double a = -1.6340227568380614E+00;
double b = 3.6688993539969197E-02;
double c = -3.2174728467255310E-04;
double d = 8.5347564286741839E-07;
double f = -8.8977482523917058E+02;
double g = 3.5955923228057013E+03;
double h = -2.5802576185275590E+04;
double Offset = 4.8555576743674642E+01;

temp += a * Math.pow(freq, 0.5);
temp += b * freq;
temp += c * Math.pow(freq, 1.5);
temp += d * Math.pow(freq, 2.0);
temp += f * Math.pow(freq, -0.5);
temp += g * Math.pow(freq, -1.0);
temp += h * Math.pow(freq, -2.0);
temp += Offset;
return temp;

Thank you and
Andreas Weller

ITU-R 468 weighting would be a lot more difficult to implement than A-weighting because it also takes into account dynamics:

Can you briefly explain how you calibrated for the absolute level? Is it as simple as recording a 1KHz sound wave at 94db and having that in your “project” file?