Question

I've got an application which records audio from the microphone and then performs some post-processing on the audio in realtime, so I must use the AudioRecord class instead of the standard MediaRecorder. My code for recording is as such:

DataOutputStream dataOutputStreamInstance = new DataOutputStream(bufferedStreamInstance);
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);

int bufferSize = AudioRecord.getMinBufferSize((int)sampleRate, channelConfiguration, DEFAULT_AUDIO_ENCODING) * 2;
short[] microphoneBuffer = new short[bufferSize];
float[] processingBuffer = new float[bufferSize];
short[] outputBuffer = new short[bufferSize];

AudioRecord microphoneRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);

microphoneRecorder.startRecording();
while(isRecording) {
  synchronized(mutex) { ... check for pause condition, wait, etc. ... }
  int numSamplesRead = microphoneRecorder.read(microphoneBuffer, 0, bufferSize);

  // Convert 16-bit short data to floating point
  getFloatingPointBufferFromPcmData(microphoneBuffer, processingBuffer, bufferSize);

  doProcessingStuff(processingBuffer, bufferSize);

  if(numSamplesRead == AudioRecord.ERROR_INVALID_OPERATION) {
    throw new IllegalStateException("read() returned AudioRecord.ERROR_INVALID_OPERATION");
  }
  else if(numSamplesRead == AudioRecord.ERROR_BAD_VALUE) {
    throw new IllegalStateException("read() returned AudioRecord.ERROR_BAD_VALUE");
  }

  try {
    // Dump the output to the target file in 16-bit short format
    getShortPcmBufferFromFloatingPointData(processingBuffer, outputBuffer, bufferSize);
    for(int bufferIndex = 0; bufferIndex < numSamplesRead; bufferIndex++) {
      dataOutputStreamInstance.writeShort(outputBuffer[bufferIndex]);
    }
  }
  catch(Exception e) {
    Log.e("MyApp", "Error while writing audio data to file: " + e.getMessage());
    e.getStackTrace();
  }
}

microphoneRecorder.stop();

The above code works fine, and I can actually record audio from the device, and I hear my voice and such. The problem is that after some seconds, a very strange pattern of distortion starts to emerge until the entire signal is drowned out. Here's a screenshot of a recording of silence which I made by putting some tape over the mic and let the app record for a minute or so:

Screenshot of distortion in wave file

The original wave file can be downloaded here.

The problem is definitely not due to my effect processing code, as I have tried commenting it out and get the same results in both cases. I have scoured the web for other code or people who might be experiencing similar problems, but haven't found anything.

Was it helpful?

Solution 3

Bah, actually the problem was no fault of Android's -- it was caused by the software I was using to convert the raw PCM data to WAV format. There is evidently some error in endian conversion, since the ARM chips are big endian and WAV is little endian. When we tried opening the raw PCM files in Audacity, they looked fine.

OTHER TIPS

I don't know Android SDK at all, but getFloatingPointBufferFromPcmData and getShortPcmBufferFromFloatingPointData don't look like standard API functions, despite the lovely naming convention. :)

Did you write these yourself? Perhaps they are using shared state and accumulating results across loop iterations? If these are your implementation, please share the code of these so that we may help you identify the actual problem.

There is also the possibility that you're writing out the PCM data in the wrong format (number of bits, endianness) and that your audio editor is interpreting the data according to a different format resulting in incorrectly decoded audio data which appears deceptively as though there is some accumulation effect happening.

If neither of these inquiries leads you to solve your issue, then my next recommendation would be to create a new microphoneBuffer instance for each loop iteration instead of using a single instance across the while loop.

Again, I'm no Android SDK expert, so these are just general pieces of advice resulting from years of experience dealing with virtually all kinds of APIs and implementation details thereof.

Hope that helps diagnose your issue!

With silence there might be an automatic gain control that is increasing the input gain beyond reason, trying to find "something" for you to record (and of course finding the noise floor)

What happens if you set your PC speakers to play a nice audio frequency sinusoid - does the noise still come up or do you continue to record the sine wave?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top