/**
*
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.
*
*/
package org.puredata.android.io;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Process;
/**
*
* AudioRecordWrapper is a wrapper for {@link AudioRecord}. It is an auxiliary class for {@link AudioWrapper};
* the purpose of the bizarre queuing mechanism is to work around the AudioRecord.read blocking problem on Droid X,
* without messing things up on other devices.
*
* @author Peter Brinkmann (peter.brinkmann@gmail.com)
*
*/
public class AudioRecordWrapper {
private static final int ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private final AudioRecord rec;
private final int bufSizeShorts;
private final BlockingQueue<short[]> queue = new SynchronousQueue<short[]>();
private Thread inputThread = null;
public AudioRecordWrapper(int sampleRate, int inChannels, int bufferSizePerChannel) throws IOException {
int channelConfig = VersionedAudioFormat.getInFormat(inChannels);
bufSizeShorts = inChannels * bufferSizePerChannel;
int bufSizeBytes = 2 * bufSizeShorts;
int recSizeBytes = 2 * bufSizeBytes;
int minRecSizeBytes = AudioRecord.getMinBufferSize(sampleRate, channelConfig, ENCODING);
if (minRecSizeBytes <= 0) {
throw new IOException("bad AudioRecord parameters; sr: " + sampleRate + ", ch: " + inChannels + ", bufSize: " + bufferSizePerChannel);
}
while (recSizeBytes < minRecSizeBytes) recSizeBytes += bufSizeBytes;
rec = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, ENCODING, recSizeBytes);
if (rec != null && rec.getState() != AudioRecord.STATE_INITIALIZED) {
rec.release();
throw new IOException("unable to initialize AudioRecord instance for sr: " + sampleRate + ", ch: " + inChannels + ", bufSize: " + bufferSizePerChannel);
}
}
public synchronized void start() {
inputThread = new Thread() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
rec.startRecording();
short buf[] = new short[bufSizeShorts];
short auxBuf[] = new short[bufSizeShorts];
while (!Thread.interrupted()) {
int nRead = 0;
while (nRead < bufSizeShorts && !Thread.interrupted()) {
nRead += rec.read(buf, nRead, bufSizeShorts - nRead);
}
if (nRead < bufSizeShorts) break;
try {
queue.put(buf);
} catch (InterruptedException e) {
break;
}
short tmp[] = buf;
buf = auxBuf;
auxBuf = tmp;
}
rec.stop();
};
};
inputThread.start();
}
public synchronized void stop() {
if (inputThread == null) return;
inputThread.interrupt();
try {
inputThread.join();
} catch (InterruptedException e) {
// do nothing
}
inputThread = null;
}
public synchronized void release() {
stop();
rec.release();
queue.clear();
}
public short[] poll() {
return queue.poll();
}
public short[] take() throws InterruptedException {
return queue.take();
}
}