Reputation: 1679
I've been working on an app that could playback audio from the microphone in real time. It sets up an AudioRecorder, that inits without any errors. However, it just returns a bunch of zeors, or a lot of number close to the max value of short, when performing read operations. I'm really stuck, it would be very kind if anyone could help me. This is my code:
public class AudioIn extends Thread {
public static final int ERROR_RECORD_INIT = -1;
public static final int ERROR_RECORD_NOTIFICATION = -2;
public static final int ERROR_RECORD_READ = -3;
public static final int SUCCESS = 0;
public static final int audioFrequency = 44100;
public static final int channelConfig = AudioFormat.CHANNEL_IN_MONO;
public static final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
final int ShortsReadPerCycle = 1024;
private boolean capture = true;
private AudioRecord recorder;
private int effectiveCaptureBufferSize;
private short[] buffer;
private AudioInHandler handler;
public AudioIn()
{
int minDeviceBuffer = AudioRecord.getMinBufferSize(audioFrequency, channelConfig, audioFormat);
Log.d("AudioIn", "Minimum device capture buffer is: " + Integer.toString(minDeviceBuffer) + " bytes");
effectiveCaptureBufferSize = minDeviceBuffer;
Log.d("AudioIn", "Setting capture buffer size to " + effectiveCaptureBufferSize + " bytes");
}
public void close()
{
capture = false;
}
public int samplesPerBuffer()
{
return effectiveCaptureBufferSize / 2;
}
@Override
public void run() {
// TODO Auto-generated method stub
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
try
{
recorder = new AudioRecord(AudioSource.MIC, audioFrequency, channelConfig, audioFormat, effectiveCaptureBufferSize);
}
catch (Throwable t)
{
handler.onError(ERROR_RECORD_INIT);
return;
}
recorder.startRecording();
while(capture)
{
buffer = new short[ShortsReadPerCycle];
int shortsRead = recorder.read(buffer, 0, buffer.length);
if (shortsRead < 0)
{
new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
handler.onError(ERROR_RECORD_READ);
}
}.run();
this.close();
}
else
{
new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
handler.onNewData(buffer);
}
}.run();
}
}
Log.d("AudioIn.run()", "Stopping AudioRecord...");
recorder.stop();
Log.d("AudioIn.run()", "Stopped AudioRecord, now releasing...");
recorder.release();
Log.d("AudioIn.run()", "AudioRecord released");
recorder = null;
}
public void setHandler(AudioInHandler handler) {
this.handler = handler;
}
}
Upvotes: 0
Views: 3221
Reputation: 46
Calling read() on AudioRecord once does not ensure the whole short[] to be filled. You should check the returned "shortsRead" for actual short read and keep calling read() until the short[] is filled. Sample code given below.
private void readFully(short[] data, int off, int length) {
int read;
while (length > 0) {
read = mRec.read(data, off, length);
length -= read;
off += read;
}
}
In your recording loop
while (!released) {
// fill the pktBuf
short[] pktBuf = new short[pktSize];
readFully(pktBuf, 0, pktSize);
// Do something
}
In this way, everytime a call to read() is made, we increment the offset by "read" and decrement the length remaining by "read", keep reading until the length remaining reaches 0. You will then get a filled short[] with recorded audio data.
Similarly, when you are writing data into an AudioTrack, you have to do the same thing "writeFully()", to ensure the whole short[] is written into the AudioTrack.
Hope it helps.
Upvotes: 3