Android的AudioRecord无法初始化。

9

我正在尝试实现一个监听麦克风输入(具体来说是呼吸)并基于其显示数据的应用程序。我正在使用Android类AudioRecord,但在尝试实例化AudioRecord时出现了三个错误。

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.

我发现了这个非常棒的帖子:AudioRecord对象未初始化 我借鉴了被接受的答案中尝试所有采样率、音频格式和通道配置来解决问题的代码,但没有帮助,无论哪种设置都会出现上述错误。 我还根据线程中的一个答案在几个地方添加了对AudioRecord.release()的调用,但没有任何区别。
这是我的代码:
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;
}

我还添加了

<uses-permission android:name="android.permission.RECORD_AUDIO"/>

根据此线程中其他答案的建议,将标签添加到我的清单文件中,作为 manifest 标记的子级和 application 标记的同级。在添加该标签后,我已经重新构建了项目。

当我在 Nexus 5 手机(不是模拟器)上调试时,我发现了这些解决方案,但不幸的是它们似乎对我没有用。在调用 AudioRecord 的构造函数时出现这些错误。我已经多次重启手机以尝试释放麦克风,但无济于事。该项目基于 Android 4.4,并且我的手机当前运行的是 Android 6.0.1。

请问还有什么我可以尝试的,或者我可能错过了什么?谢谢!

1个回答

19

我自己找到了答案,这与权限有关。

问题在于我的手机上运行的是API版本23(Android 6.0.1),它不再仅使用清单文件来处理权限。从版本23开始,权限在运行时授予。我添加了一个方法,确保在运行时请求权限,当我在手机上允许一次后,它就可以工作了。

您可以使用我从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
    } }

在创建AudioRecord之前,我会从我的主Activity的onCreate()方法中调用requestRecordAudioPermission()。


1
你救了我的一天!错误日志完全与权限无关,确实是新版本23引起的问题。谢谢你。 - Damien Praca
2
很高兴能帮到其他人 :) - anon
非常感谢。在这个问题上已经花费了几天的时间。 - sharry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接