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 | |
by BrowserUk (Patriarch) on Nov 10, 2016 at 19:16 UTC | |
by John-Robie (Novice) on Nov 11, 2016 at 14:07 UTC | |
by Anonymous Monk on Nov 11, 2016 at 14:03 UTC |