I have written a small app that draws a circle on a surfaceview that is proportional to the amount of sound reaching the phones mic (the louder the sound the bigger the circle).
Everything is working fine. I can start things going at the press of a button, but then I go into an infinite loop (by design). This causes the UI to hang. I would like to arrange things so that my app runs and keeps responding to mic input but the UI is still responsi9ve (ie so I can press a button (the back button perhaps?) to stop the app.).
At the moment the app till android thinks it hasn't responded in a long time (ANR?) and puts up a message box so I can quit it.
How do I avoid this? I know I should use a new thread, but what is the simplest way?
public class MainActivity extends ActionBarActivity {
private static final int RECORDER_SAMPLERATE = 44100;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private static final int BUFFSIZE = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);
private AudioRecord recorder = null;
private SurfaceView mySurface;
private static Handler handler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mySurface = (SurfaceView) findViewById(R.id.surfaceView);
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING, BUFFSIZE);
recorder.startRecording();
}
private void respondToMIC() {
Canvas myCanvas;
Paint paint = new Paint();
paint.setARGB(255, 255, 100, 100);
short sData[] = new short[BUFFSIZE/2];
while (true)
{
int read = recorder.read(sData, 0, BUFFSIZE/2);
int min = 0;
int max = 0;
for (int j=0; j< read; j++)
{
if (sData[j] < min) min = sData[j];
if (sData[j] > max) max = sData[j];
}
myCanvas = mySurface.getHolder().lockCanvas();
myCanvas.drawColor(Color.WHITE);
myCanvas.drawCircle(myCanvas.getWidth()/2, myCanvas.getHeight()/2, ((myCanvas.getHeight()/2)*Math.abs(max-min)/65000), paint);
mySurface.getHolder().unlockCanvasAndPost(myCanvas);
}
}
}
<SurfaceView
android:layout_width="fill_parent"
android:layout_height="221dp"
android:id="@+id/surfaceView"
android:layout_gravity="center" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="surface"
android:id="@+id/button3"
android:onClick="respondToMIC"
android:layout_gravity="center_horizontal|bottom" />
</FrameLayout>
You can use a AsyncTask to handle the recorder buffer in background and not block the UI task:
public class MainActivity extends ActionBarActivity {
private static final int RECORDER_SAMPLERATE = 44100;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private static final int BUFFSIZE = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);
private AudioRecord recorder = null;
private SurfaceView mySurface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mySurface = (SurfaceView) findViewById(R.id.surfaceView);
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING, BUFFSIZE);
recorder.startRecording();
}
private void respondToMIC() {
new AudioRecordTask().execute();
}
private class AudioRecordTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
Canvas myCanvas;
Paint paint = new Paint();
paint.setARGB(255, 255, 100, 100);
short sData[] = new short[BUFFSIZE / 2];
// Run while the Activity is not finished.
while (!isFinishing()) {
int read = recorder.read(sData, 0, BUFFSIZE / 2);
int min = 0;
int max = 0;
for (int j = 0; j < read; j++) {
if (sData[j] < min) min = sData[j];
if (sData[j] > max) max = sData[j];
}
myCanvas = mySurface.getHolder().lockCanvas();
myCanvas.drawColor(Color.WHITE);
myCanvas.drawCircle(myCanvas.getWidth() / 2, myCanvas.getHeight() / 2, ((myCanvas.getHeight() / 2) * Math.abs(max - min) / 65000), paint);
mySurface.getHolder().unlockCanvasAndPost(myCanvas);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
}
@Override
protected void onPreExecute() {
}
@Override
protected void onProgressUpdate(Void... values) {
}
}
}
This may very well contain errors but give it a go:
Thread micThread;
private void respondToMIC() {
if (micThread == null) {
micThread = new Thread(new micRunnable());
micThread.start();
} else {
// if you want the same button to shut it down
stopRespondingToMic();
}
}
private void stopRespondingToMic() {
if (micThread != null && micThread.isAlive()) {
micThread.interrupt();
micThread = null;
}
}
@Override
protected void onPause() {
stopRespondingToMic();
}
private class micRunnable implements Runnable {
@Override
public void run() {
Canvas myCanvas;
Paint paint = new Paint();
paint.setARGB(255, 255, 100, 100);
short sData[] = new short[BUFFSIZE/2];
while (true)
{
int read = recorder.read(sData, 0, BUFFSIZE/2);
int min = 0;
int max = 0;
for (int j=0; j< read; j++)
{
if (sData[j] < min) min = sData[j];
if (sData[j] > max) max = sData[j];
}
myCanvas = mySurface.getHolder().lockCanvas();
myCanvas.drawColor(Color.WHITE);
myCanvas.drawCircle(myCanvas.getWidth()/2, myCanvas.getHeight()/2, ((myCanvas.getHeight()/2)*Math.abs(max-min)/65000), paint);
mySurface.getHolder().unlockCanvasAndPost(myCanvas);
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.