AudioRecord在Android L开发者预览中录制间歇性声音

AudioRecord在Android L开发者预览中录制间歇性声音,android,audio,android-5.0-lollipop,audiorecord,Android,Audio,Android 5.0 Lollipop,Audiorecord,我用PCM16LE格式的录音带录音,8000Hz,1声道。它在Android版本2.3.3-4.4中录制ok,但在Android L(5.0)开发者预览版(在nexus 5、nexus 7和emulator上)中录制奇怪的间歇声音 以下是录制的声音样本(前半段录音,后半段回放): 我试着用不同的采样率(400016000)和8比特播放录制的声音,但声音总是断断续续的。这个声音有什么问题 我正在使用此AudioRecordTask使用getAudioRecord()录制音频以初始化输入(操作期间

我用PCM16LE格式的录音带录音,8000Hz,1声道。它在Android版本2.3.3-4.4中录制ok,但在Android L(5.0)开发者预览版(在nexus 5、nexus 7和emulator上)中录制奇怪的间歇声音

以下是录制的声音样本(前半段录音,后半段回放):

我试着用不同的采样率(400016000)和8比特播放录制的声音,但声音总是断断续续的。这个声音有什么问题

我正在使用此AudioRecordTask使用getAudioRecord()录制音频以初始化输入(操作期间没有返回错误;接收大小与internalBufferSize值相等的音频块):

公共最终整数抽样率=8000;
私人录音getAudioRecord(){
int internalBufferSize=AudioRecord.getMinBufferSize(采样率,
单声道中的AudioFormat.CHANNEL\u,
AudioFormat.ENCODING_PCM_16BIT);//返回640
internalBufferSize=8000;//还尝试了返回值(640)和值256030000-无更改
最终int源;
if(Build.VERSION.SDK_INT<11){
SOURCE=MediaRecorder.AudioSource.MIC;
}否则{
SOURCE=MediaRecorder.AudioSource.VOICE_通信;
}
音频记录=新的音频记录(来源,
抽样率,
单声道中的AudioFormat.CHANNEL\u,
AudioFormat.ENCODING_PCM_16位,
内部缓冲区大小);
int state=record.getState();
if(state!=AudioRecord.state_已初始化){
试一试{
record.release();
}捕获(例外e){
}
返回null;
}
if(record.getState()==android.media.AudioRecord.STATE\u已初始化){
record.startRecording();
}否则{
record.release();
返回null;
}
返回记录;
}
私有类AudioRecordTask扩展了AsyncTask{
最终整数部分缓冲区大小=采样率;
最终整数必需缓冲区大小=15*部分缓冲区大小*Short.SIZE/8;
final int FULL\u BUFFER\u SIZE=必要的\u BUFFER\u SIZE*2;//XXX:*2用于系统返回的数据超过需要的情况
短发;
int mTotalSize;
int mTotalSizeInBytes;
布尔mResult;
私有对象mLock=新对象();
@凌驾
受保护的void onPreExecute()
{
错误记录=正确;
mBuffer=新短[满缓冲区大小];
mTotalSize=0;
mTotalSizeInBytes=0;
mResult=false;
}
@凌驾
受保护的Void doInBackground(Void…arg0){
已同步(mLock){
android.os.Process.setThreadPriority(android.os.Process.THREAD\u PRIORITY\u emergency\u AUDIO);
AudioRecord=getAudioRecord();
if(记录==null){
mResult=false;
返回null;
}
用于(int i=0;i<15*100;i++){//XXX:*100以记录足够的数据(系统可以返回小于所需的数据)
int datalen=record.read(mBuffer、mTotalSize、部分缓冲区大小);
如果(数据长度>0){
mTotalSize+=datalen;
mTotalSizeInBytes=mTotalSize*2;
}否则{
Log.w(“,”error“+datalen+”,在AudioRecord.read中);
}
如果(isCancelled()| | mTotalSizeInBytes>必要的缓冲区大小){
打破
}
}
if(record.getRecordingState()==AudioRecord.RECORDSTATE\u录制){
record.stop();
}
record.release();
mResult=true;
返回null;
}
}
@凌驾
受保护的void onPostExecute(void r){
已同步(mLock){
错误记录=错误;
鳍();
}
}
@凌驾
受保护的void onCancelled(){
//XXX:在旧的机器人(如2.3.3)上,当doInBackground仍在运行时被取消调用
已同步(mLock){
错误记录=错误;
如果(马波特){
返回;
}
鳍();
}
}
私人财务部(){
如果(mResult&&mTotalSizeInBytes>0){
SendRecordedAudioServer(mBuffer、mTotalSize、mTotalSizeInBytes);
}否则{
错误(空);
}
}
}

这是Android L开发者预览版中的一个bug:

AudioRecord.read for short[]缓冲区参数以字节为单位返回值,而不是以short为单位返回值


作为一种解决方法,请使用AudioRecord.read和byte[]buffer。

这在5.0版中似乎已修复,但存在一个相关问题,即偏移量参数错误地加倍-请参阅尝试提交修补程序时,我被告知该修补程序在内部也已修复,但不知道它何时会出现在发行版中。我刚刚陷入了这个问题(我想)在5.0.2中。这是否意味着,如果使用byte[]缓冲区读取,包括offset参数,所有都可以正常工作,还是仍然加倍了?当使用byte[]缓冲区时,包括offset,所有都可以(在5.0和5.0.1中是这样,在5.0.2上还没有测试)偏移量参数的不同问题存在于5.0-5.02之间,但在5.1中似乎已修复
public final int SAMPLING_RATE = 8000;

private AudioRecord getAudioRecord() {
    int internalBufferSize = AudioRecord.getMinBufferSize(SAMPLING_RATE,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT); //returns 640

    internalBufferSize = 8000; //also tried returned value (640) and values 2560, 30000 - no changes 

    final int SOURCE;
    if (Build.VERSION.SDK_INT < 11) {
        SOURCE = MediaRecorder.AudioSource.MIC;
    } else {
        SOURCE = MediaRecorder.AudioSource.VOICE_COMMUNICATION;
    }

    AudioRecord record = new AudioRecord(SOURCE,
            SAMPLING_RATE,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT,
            internalBufferSize);

    int state = record.getState();
    if (state != AudioRecord.STATE_INITIALIZED) {
        try {
            record.release();
        } catch (Exception e) {
        }
        return null;
    }

    if (record.getState() == android.media.AudioRecord.STATE_INITIALIZED) {
        record.startRecording();
    } else {
        record.release();
        return null;
    }
    return record;
}

private class AudioRecordTask extends AsyncTask<Void, Void, Void> {
    final int PARTIAL_BUFFER_SIZE = SAMPLING_RATE;
    final int NECESSARY_BUFFER_SIZE = 15 * PARTIAL_BUFFER_SIZE * Short.SIZE / 8;
    final int FULL_BUFFER_SIZE = NECESSARY_BUFFER_SIZE * 2; //XXX: * 2 for the case when system returns more data than needed
    short[] mBuffer;
    int mTotalSize;
    int mTotalSizeInBytes;
    boolean mResult;
    private Object mLock = new Object();

    @Override
    protected void onPreExecute()
    {
        mIsRecording = true;

        mBuffer = new short[FULL_BUFFER_SIZE];
        mTotalSize = 0;
        mTotalSizeInBytes = 0;
        mResult = false;
    }

    @Override
    protected Void doInBackground(Void... arg0) {
        synchronized (mLock) {
            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
            AudioRecord record = getAudioRecord();
            if (record == null) {
                mResult = false;
                return null;
            }

            for (int i = 0; i < 15 * 100; i++) { //XXX: * 100 to record enough data (system can return lesser than needed)
                int datalen = record.read(mBuffer, mTotalSize, PARTIAL_BUFFER_SIZE);
                if (datalen > 0) {
                    mTotalSize += datalen;
                    mTotalSizeInBytes = mTotalSize*2;
                } else {
                    Log.w("", "error " + datalen + " in AudioRecord.read");
                }
                if (isCancelled() || mTotalSizeInBytes > NECESSARY_BUFFER_SIZE) {
                    break;
                }
            }
            if (record.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
                record.stop();
            }

            record.release();
            mResult = true;
            return null;
        }
    }

    @Override
    protected void onPostExecute(Void r) {
        synchronized (mLock) {
            mIsRecording = false;
            fin();
        }
    }

    @Override
    protected void onCancelled() {
        //XXX: on old Androids (e.g. 2.3.3) onCancelled being called while doInBackground is still running
        synchronized (mLock) {
            mIsRecording = false;
            if (mAbort) {
                return;
            }
            fin();
        }
    }

    private void fin() {
        if (mResult && mTotalSizeInBytes > 0) {
            sendRecordedAudioToServer(mBuffer, mTotalSize, mTotalSizeInBytes);
        } else {
            showError(null);
        }
    }
}