I solved it I am pleased to report and my app now works on all Android
phones Nexus One's LG's, HTC etc:
At last I can answer a question on this forum instead of just posting
them :-) The solution it seems is to request chunks of data from the
record device of size minBufferSize which you get using this code:
static
{
minBufferSize = AudioRecord.getMinBufferSize(sampleRate,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
buffer = new byte[minBufferSize];
}
If there isnt minBufferSize bytes left, then you have to request the
amount thats left to read. See how I calculate toRead below.
bufferReadResult will return exactly how many bytes were read and this
is what you use to advance your global buffer counter. Interestingly,
minBufferSize varies quite a bit on the phones I have tested. I copy
the 16 bit PCM audio data into an array of floats as I perform DSP on
the buffer at a later stage (in C++). If anyone is interested in how I
do this let me know and I'd be happy to explain. Here is the full code
for my Record AyncTask class. If anything is not clear, let me know:
package org.tunepal;
import android.app.AlertDialog;
import android.media.AudioRecord;
import android.os.AsyncTask;
import android.media.*;
/**
*
* @author Bryan Duggan
*/
public class Recorder extends AsyncTask<Void, Integer, Void>
{
static int sampleRate = 22050;
static int recordTime = 12;
static int numSamples = sampleRate * recordTime;
public static float[] signal = new float[numSamples];
static int minBufferSize;
public static byte[] buffer;
static
{
minBufferSize = AudioRecord.getMinBufferSize(sampleRate,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
buffer = new byte[minBufferSize];
}
String message;
boolean error = false;
AudioRecord audioRecord = null;
private RecordActivity context = null;
/**
* @return the recordActivity
*/
public RecordActivity getRecordActivity()
{
return context;
}
/**
* @param recordActivity the recordActivity to set
*/
public void setRecordActivity(RecordActivity recordActivity)
{
this.context = recordActivity;
}
public Recorder(RecordActivity recordActivity)
{
this.context = recordActivity;
}
@Override
protected void onPostExecute(Void arg)
{
context.progress.setProgress(0);
context.imgRecord.setImageResource(R.drawable.micro_off);
context.txtStatus.setText("");
if (!error)
{
context.finishedRecording();
}
else
{
AlertDialog.Builder builder = new
AlertDialog.Builder(context);
builder.setTitle("Tunepal");
builder.setMessage(message);
builder.setPositiveButton("Ok", null);
builder.setCancelable(true);
builder.show();
}
}
@Override
protected void onPreExecute()
{
context.imgRecord.setImageResource(R.drawable.micro_no);
context.progress.setProgress(0);
context.progress.setMax(sampleRate * recordTime * 2);
context.txtStatus.setText("");
}
@Override
protected void onCancelled()
{
context.progress.setProgress(0);
context.imgRecord.setImageResource(R.drawable.micro_off);
context.txtStatus.setText("");
doneRecording();
}
public synchronized void doneRecording()
{
if (audioRecord != null)
{
try
{
audioRecord.stop();
} catch (IllegalStateException e)
{
System.out.println("ALREADY STOPPED");
}
audioRecord.release();
audioRecord = null;
}
}
protected void onProgressUpdate(Integer... progress) {
context.progress.setProgress(progress[0].intValue());
}
@Override
protected Void doInBackground(Void... arg0)
{
try
{
System.out.println("Recording started");
audioRecord = null;
System.gc();
int totalBytes = sampleRate * recordTime * 2;
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
Config.getInt("SAMPLE_RATE"), AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, totalBytes);
audioRecord.startRecording();
int startAt = 0;
error = false;
// read in blocks
while (startAt < totalBytes)
{
int toRead = 0;
if (startAt + minBufferSize >= totalBytes)
{
toRead = totalBytes - startAt;
}
else
{
toRead = minBufferSize;
}
int bufferReadResult = audioRecord.read(buffer, 0,
toRead);
if (bufferReadResult ==
AudioRecord.ERROR_INVALID_OPERATION)
{
message = "Sorry! Tunepal had a problem recording:
ERROR_INVALID_OPERATION";
error = true;
break;
}
if (bufferReadResult == AudioRecord.ERROR_BAD_VALUE)
{
message = "Sorry! Tunepal had a problem recording:
ERROR_BAD_VALUE";
error = true;
break;
}
System.out.println(bufferReadResult + " bytes read");
if (!isCancelled())
{
int signalStart = startAt / 2; // // In samples rather
than bytes
int frameSize = bufferReadResult / 2; // In samples
// Copy the frame of audio from the buffer into the
global float buffer
for (int i = 0; i < frameSize; i++)
{
int signalIndex = signalStart + i;
signal[signalIndex] = ((buffer[(i * 2) + 1] << 8)
+ buffer[i * 2]);
}
startAt += bufferReadResult;
publishProgress(new Integer(startAt));
}
else
{
break;
}
}
if (! isCancelled())
{
doneRecording();
}
error = false;
}
catch (Exception e)
{
message = e.getMessage();
error = true;
}
return null;
}
void setContext(RecordActivity aThis)
{
context = aThis;
}
}
--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en