Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AudioRecord records intermittent sound in Android L Developer Preview

I'm recording sound with AudioRecord in PCM16LE format, 8000Hz, 1channel. It records ok in Android versions 2.3.3-4.4.4, but records strange intermittent sound in Android L(5.0) Developer Preview (on nexus 5, nexus 7 and emulator).

Here is the sample of recorded sound (the first half - recording, the second half - playback): https://www.dropbox.com/s/3wcgufua5pphwtt/android_l_sound_record_error.m4a?dl=0

I tried to play recorded sound using different sample rate (4000, 16000) and as 8bit but sound keeps to be intermittent. What the problem could be with this sound?

I'm using this AudioRecordTask to record audio with getAudioRecord() for initializing input (no errors returned during operation; receiving audio chunks sized equally to internalBufferSize value):

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);
        }
    }
}
like image 633
Anton Malyshev Avatar asked Oct 07 '14 12:10

Anton Malyshev


1 Answers

It's a bug in Android L Developer Preview: https://code.google.com/p/android-developer-preview/issues/detail?id=1492

AudioRecord.read for short[] buffer argument returns value in bytes instead of value in shorts.

As a workaround use AudioRecord.read with byte[] buffer.

like image 128
Anton Malyshev Avatar answered Oct 16 '22 18:10

Anton Malyshev