How to compute decibel (dB) of Amplitude from Medi

2019-04-11 21:00发布

问题:

I have a code to compute real-time dB Amplitude of AudioRecord. The code works well for computing dB Amplitude. After recording, I save that it to wav file. Now, I want to playback that file and recompute the dB Amplitude. However, I cannot achieve similar result before. Could you help me to fix it. This is my code to compute dB Amplitude when recording and playback.

1.Compute dB amplitude when recording

bufferSize = AudioRecord.getMinBufferSize(16000, AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT);
record = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT, bufferSize);
audioBuffer = new short[bufferSize];
readSize=record.read(audioBuffer, 0, audioBuffer.length);
double amplitude = 0;
double sum=0;
for (int i = 0; i < readSize; i++) {
     sum += audioBuffer[i] * audioBuffer[i];
}
amplitude = sum / readSize;
dbAmp=20.0 *Math.log10(amplitude/32767.0);

2.Assume that the file output is ouput.wav. I used MediaPlayer to playback and compute Amplitude

String filePath = Environment.getExternalStorageDirectory().getPath() +"/" +"output.wav";
mPlayer = new  MediaPlayer();
mPlayer.setDataSource(filePath);
mPlayer.prepare();
mPlayer.start();
mVisualizerView.link(mPlayer);

In which, mVisualizerView is Visualizer class. The class has link function such as

 public void link(MediaPlayer player)
  {
    // Create the Visualizer object and attach it to our media player.
    mVisualizer = new Visualizer(player.getAudioSessionId());
    mVisualizer.setScalingMode(Visualizer.SCALING_MODE_NORMALIZED);
    mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
    // Pass through Visualizer data to VisualizerView
    Visualizer.OnDataCaptureListener captureListener = new Visualizer.OnDataCaptureListener()
    {
      @Override
      public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes,
          int samplingRate)
      {       
        updateVisualizer(bytes);
      }
      @Override
      public void onFftDataCapture(Visualizer visualizer, byte[] bytes,
          int samplingRate)
      {     
        updateVisualizerFFT(bytes);
      }
    };
    mVisualizer.setDataCaptureListener(captureListener,
        Visualizer.getMaxCaptureRate() / 2, true, true);
    player.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
    {
      @Override
      public void onCompletion(MediaPlayer mediaPlayer)
      {
        mVisualizer.setEnabled(false);
      }
    });
  } 

As my task, I will recompute dbAmp from bytes in functions updateVisualizer or updateVisualizerFFT

   public void updateVisualizer(byte[] bytes) {
    dbAmp = computedbAmp(bytes); 
    mBytes = bytes;
    invalidate();
  }
  public void updateVisualizerFFT(byte[] bytes) {
    dbAmp = computedbAmp(bytes);
    mFFTBytes = bytes;
    invalidate();
  }
  public double computedbAmp(byte[] audioData) {
        //System.out.println("::::: audioData :::::"+audioData);
      double amplitude = 0;
      for (int i = 0; i < audioData.length/2; i++) {
          double y = (audioData[i*2] | audioData[i*2+1] << 8) / 32768.0;
          // depending on your endianness:
          // double y = (audioData[i*2]<<8 | audioData[i*2+1]) / 32768.0
          amplitude += Math.abs(y);
      }
      amplitude = amplitude / audioData.length / 2;
      return amplitude;
    }

Currently, I apply some way to compute dB amplitude from bytes. However, they are not correct. Could you help me to fix it or suggest to me the solution to compute it? Thanks

My expected solution such as Sensor Box for Android

回答1:

As mentioned in the comments you are not using the same computation for both. Also, I don't think either method is correct.

From your code in the first example it looks like you are trying to compute the RMS which is the sqrt(sumOfSquares/N) and then convert to dB.

The second sample is sumOfAbs/N not converted to dB

Another very minor issue is that in one case you divide by 32767 and the other 32768. Both should be 32768.

For part one do something like this:

double sum=0;
for (int i = 0; i < readSize; i++) {
    double y = audioBuffer[i] / 32768.0;
    sum += y * y;
}
double rms = Math.sqrt(sum / readSize);
dbAmp=20.0 *Math.log10(rms);

And for part 2:

double sum=0;
for (int i = 0; i < audioData.length/2; i++) {
    double y = (audioData[i*2] | audioData[i*2+1] << 8) / 32768.0;
    sum += y * y;
}
double rms = Math.sqrt(sum / audioData.length/2);
dbAmp = 20.0*Math.log10(rms);

Notice the two are almost exactly identical with the exception of cracking open the byte array. This should be a clue to you to find a way to factor out this function and then you won't run into this kind of problem in the future.

Edit:

One more thing I forgot to mention. There is a bit of open debate on this matter but depending on your application you might want your dBFS result to be sine calibrated. What I mean that is you were to run the computation on a single full scale sine wave as I've written it you would get a rms value of 0.7071 (1/sqrt(2)), or -3dBFS. If you want a full scale sine to hit exactly zero dBFS you need to multiply the rms value by sqrt(2).



回答2:

As question said that first case worked well. Hence, I assumed first case was correct and used it as reference to edit his second case. From comment of jaket, we can modify the second case as

  double sum=0;
  for (int i = 0; i < audioData.length/2; i++) {
      double y = (audioData[i*2] | audioData[i*2+1] << 8);
      sum += y*y;
  }
  double rms = sum / audioData.length/2;
  double dbAmp = 20.0*Math.log10(rms/32768.0);
  return dbAmp;

I think it will be same result with first case. Hope it help