High Level:
I use the same encoder in two different ways.
Way #1: record raw audio and save to an entire file, then compress file after it is all finished. RESULT Flawless audio
Way #2: record raw audio, encode it frame by frame. RESULT Audible skipping
Why does way #2 cause skipping?
Low Level
Code for way #1 (writing to FileOutputStream fos, and compress after all writing is finished)
public void writeSample(short[] buf) throws IOException {
byte[] byteArray = Util.toByteArray(buf,false);
bytesWritten += byteArray.length;
fos.write(byteArray);
}
Code for way #2
public void writeSample(short[] buf) throws IOException {
byte[] byteArray = Util.toByteArray(buf,false);
bytesWritten += byteArray.length;
encoder.encode(byteArray);
}
ALMOST PERFECT:
The length of the short buf in way #2 is 15360. Since this is an odd sized number, I employed this technique: but there is stil a slight audible skipping:
ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
byte[] readme = new byte[4096];
int count = bais.read(readme);
while ( count != -1 ) {
System.out.println("READING :"+count+ " bytes");
if(bais.available() < 4096) {
System.out.println ("LESS THAN 4096 available: "+bais.available());
byte[] remain = new byte[bais.available()];
bais.read(remain);
aacEncoder.encode(remain);
break;
}
aacEncoder.encode(readme);
count = bais.read(readme);
}
Here’s the deal: when the encoder encodes, it doesn’t always take the same amount of time. Sometimes it just stashes some data away for later, and sometimes it actually does a whole lot of number crunching. It has to wait for enough audio to encode an entire MP3 “frame” or it will just stash data.
Every time there’s a call for new data that requires number crunching, if it takes longer than the amount of time the audio represents, there’s a risk that a dropout will occur.
The solution is to have your record thread fill a buffer and have second thread do all the work that might slow things down, or take an unpredictable amount of time. That includes encoding and writing to the file.
For your buffer, if you are targeting android 2.3 or later, you can use the piped I/O. This is not technically a ringbuffer because it blocks, but in my experience it works well enough. (This api is available in earlier versions of android, but you can’t set the buffer size. Grrr!)
You might find this link helpful for understanding how the audio IO actually works conceptually: http://blog.bjornroche.com/2011/11/slides-from-fundamentals-of-audio.html