in reply to Measuring the sound level (dB(A)) with PERL

Unfortunately, my guess is that you've hit a hardware limitation: microphones have a range of volumes they can measure, beyond which they clamp.

The sound waves induce an electrical current inside the microphone; this electrical current (or the voltage that the said current creates resistor) is converted (using an ADC: analog-to-digital-converter) into a sequence of 1s and 0s: specifically, for your 8bit system, eight 1s and 0s, which can encode a number from 0 to 255 (or -128 to +127, depending on the ADC; though usually it's 0..255 for hardware plugged into computers). So, every instant, you are reading back an integer from 0 to 255. If the sound is too strong for the given microphone at a particular instant (if the voltage or current goes beyond the range that the ADC can convert), the ADC will return a 0 or a 255, depending.

Sound is actually made up as waves; if you were to measure a pure tone of one frequency, and plot it vs time, you would see a sine-wave of discrete integers, centered near 127.5. The center is the "zero sound pressure" point... so silence should give individual codes at 127-128. When you have sound that covers about 90% of the range of the mic+ADC, you will get codes from 12 to 242, with a peak amplitude of 115 codes -- it goes above and below the center by 115 codes (this would be a peak-to-peak amplitude of 230, or an rms amplitude of about 81 codes). If your sound just barely hit the extreme edges of your mic+ADC, you would get codes of 0 to 255, with an amplitude of about 127-128. If your sound was too loud, you would get codes of 0 to 255, with flat sections at code 0 and 255. See http://i.imgur.com/lGREL42.png for examples.

Aside from the clamping, your algorithm is a bit funny: you are finding the average value (the sum of the individual codes, divided by count of codes), and normalizing that to fraction of the fullscale range (ie, divided by 255), but calling that the amplitude. For any sinusoid or sum of sinusoids (ie, sound), the average will be about 127.5 as long as you measure (close to) a full cycle.

Really, what you need to do is eliminate the DC offset (subtract the average), and then find the RMS deviation from that average. As a shortcut, because you know the center will be near 127.5, just assume that as the center. Then you can some the square of the measured v minus the theoretical center value, and every 8000 samples calculate the RMS amplitude.

... my $sse = 0; # sum of squared error ("error" = distance from the av +erage) while(1) { if ($cnt == 8000) { # the amplitude >= 0 <= 1 ### my $amplitude=$value / $cnt / 255; my $mse = $sse / $cnt; # MSE = mean squared error = avera +ge of the sum-of-square-distances my $rms = sqrt($mse); # RMS = root mean squared error = +a kind of "average power" measurement for the whole wave my $scaled_rms = $rms / 255; # convert from codes to fracti +on of fullscale # calculated dbFS,rms = 20 * log10($rms) my $dbFSrms = 20 * log10($scaled_rms); # this is the numb +er of dB_RMS from the fullscale of your mic+ADC my $dba = $dbFSrms - $DBA_MIC_FS; # you will need to + find out your mic+ADC's fullscale capability, in dBa or dBv or dBmW print "Calculated dB(FS)_rms and dB(A)_rms: $dbFSrms dBFS, $db +a dBa\n"; # reset the count and SSE for the next 8k samples $cnt = 0; $sse = 0; } my $buffer; # read one byte read($fh, $buffer, 1); if(defined($buffer)) { # get an unsigned char my $v = unpack("C", $buffer); # print "Byte read: $v\n"; # sum the squared distance from the theoretical mean $sse += ($v-127.5)**2; $cnt++; } }

Update: strike the first paragraph guess; I wrote that before fully reading the original code, but forgot to delete it. There still might be clamping going on, but it wasn't the primary problem.

Replies are listed 'Best First'.
Re^2: Measuring the sound level (dB(A)) with PERL
by John-Robie (Novice) on Nov 10, 2016 at 12:06 UTC

    Thank you very much! You really made my day!

    This was the missing link to my question. I just integrated your code and am able to see quite precise values in the range from 60dB (A) to 100 dB(A). Therefore, a corrective value for $DBA_MIC_FS was integrated in the upper part of the code.

    my $DBA_MIC_FS = -110;

    Now, the values are similar compared to a standard sound meter Class 2. There are some problems with the USB mic, though: There are no smaller values as 60 db(A)… this was tested on Windows with the same mic… no lower values than 60 dB(A) :-(

    This leads to the idea, that USB mics are not built to measure sound pressure levels precisely, but more to record human voice. Additionally considering the good advice from stevieb, USB mics seem to be a dead end for noise measurements (ot this 10 EUR mic is just not good enough for this purpose)

    So, there will be a test with another USB mic and if there are similar problems, I might switch to analog mics with ADC technology. But this is out of the scope of this forum, I suppose...

      There are no smaller values as 60 db(A)� this was tested on Windows with the same mic� no lower values than 60 dB(A) :-(

      Does the mic in question have a voice activation feature? (If so, can you turn it off!)


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
      In the absence of evidence, opinion is indistinguishable from prejudice.
        No, it does not even have a CD or software shipped with the mic. I will move to the analoge schema now...
        I,m afriad not.