I'm trying to implement an app that listens to microphone input (specifically, breathing), and presents data based on it. I'm using the Android class AudioRecord, and when trying to instantiate AudioRecord I get three errors.
AudioRecord: AudioFlinger could not create record track, status: -1
AudioRecord-JNI: Error creating AudioRecord instance: initialization check failed with status -1.
android.media.AudioRecord: Error code -20 when initializing native AudioRecord object.
I found this excellent thread: AudioRecord object not initializing
I have borrowed the code from the accepted answer that tries all sample rates, audio formats and channel configurations to try to solve the problem, but it didn't help, I get the above errors for all settings. I have also added a call to AudioRecord.release() on several places according to one of the answers in the thread but it made no difference.
This is my code:
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;
public class SoundMeter {
private AudioRecord ar = null;
private int minSize;
private static int[] mSampleRates = new int[] { 8000, 11025, 22050, 32000, 44100 };
public boolean start() {
ar = findAudioRecord();
if(ar != null){
ar.startRecording();
return true;
}
else{
Log.e("SoundMeter", "ERROR, could not create audio recorder");
return false;
}
}
public void stop() {
if (ar != null) {
ar.stop();
ar.release();
}
}
public double getAmplitude() {
short[] buffer = new short[minSize];
ar.read(buffer, 0, minSize);
int max = 0;
for (short s : buffer)
{
if (Math.abs(s) > max)
{
max = Math.abs(s);
}
}
return max;
}
public AudioRecord findAudioRecord() {
for (int rate : mSampleRates) {
for (short audioFormat : new short[] { AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_PCM_FLOAT }) {
for (short channelConfig : new short[] { AudioFormat.CHANNEL_IN_MONO, AudioFormat.CHANNEL_IN_STEREO }) {
try {
Log.d("SoundMeter", "Attempting rate " + rate + "Hz, bits: " + audioFormat + ", channel: " + channelConfig);
int bufferSize = AudioRecord.getMinBufferSize(rate, channelConfig, audioFormat);
if (bufferSize != AudioRecord.ERROR_BAD_VALUE) {
// check if we can instantiate and have a success
Log.d("SoundMeter", "Found a supported bufferSize, attempting to instantiate");
AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, rate, channelConfig, audioFormat, bufferSize);
if (recorder.getState() == AudioRecord.STATE_INITIALIZED){
minSize = bufferSize;
return recorder;
}
else
recorder.release();
}
} catch (Exception e) {
Log.e("SoundMeter", rate + " Exception, keep trying.", e);
}
}
}
}
return null;
}
}
I have also added the
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
tag to my manifest file, as a child to the manifest tag and a sibling to the application tag according to one of the other answers in the thread mentioned above. I have rebuilt the project after adding this tag.
These are the solutions I find when googling the problem, but they unfortunately don't seem to do it for me. I am debugging on my Nexus 5 phone (not an emulator). These errors appear upon calling the contructor of AudioRecord. I have rebooted my phone several times to try to release the microphone, to no avail. The project is based on Android 4.4, and my phone is currently running Android 6.0.1.
Would highly appreciate some tips on what else I can try, what I could have missed. Thank you!
I found the answer myself. It had to do with permissions.
The problem was that I am running API version 23 (Android 6.0.1) on my phone, which no longer uses only the manifest file to handle permissions. From version 23, permissions are granted in run-time instead. I added a method that makes sure to request the permission in run-time, and when I had allowed it once on my phone, it worked.
You can use this code that I copied from http://web.archive.org/web/20160111030837/http://developer.android.com/training/permissions/requesting.html —
private void requestRecordAudioPermission() { //check API version, do nothing if API version < 23! int currentapiVersion = android.os.Build.VERSION.SDK_INT; if (currentapiVersion > android.os.Build.VERSION_CODES.LOLLIPOP){ if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) { // Show an expanation to the user *asynchronously* -- don't block // this thread waiting for the user's response! After the user // sees the explanation, try again to request the permission. } else { // No explanation needed, we can request the permission. ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 1); } } } } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case 1: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted, yay! Do the // contacts-related task you need to do. Log.d("Activity", "Granted!"); } else { // permission denied, boo! Disable the // functionality that depends on this permission. Log.d("Activity", "Denied!"); finish(); } return; } // other 'case' lines to check for other // permissions this app might request } }
I then call requestRecordAudioPermission() from the onCreate() method in my main activity before creating the AudioRecord.