Search code examples
androidformatcallrecord

Android Recording Call parameters


Im developing a DTMF decoder. What I need is to record a voice call and then extract the frecuency range. Everything is working ok but there are some android versions in which I get the following error when I set up the audio source

"Invalid capture preset 3 for AudioAttributes"

In order to get the right parameters I have developed an algorithm:

private static final int[] FREQUENCY = {8000, 11025, 16000, 22050, 44100}; // 44100 is guaranteed to work in all devices

private static final int[] CHANNEL_CONFIGURATION = {AudioFormat.CHANNEL_IN_MONO,
                                                    AudioFormat.CHANNEL_IN_STEREO};

private static final int[] AUDIO_ENCODING = {AudioFormat.ENCODING_DEFAULT,
                                             AudioFormat.ENCODING_PCM_8BIT,
                                             AudioFormat.ENCODING_PCM_16BIT};

for (int i = 0; i < FREQUENCY.length && !found; i ++) {
        for (int j = 0; j < CHANNEL_CONFIGURATION.length && !found; j ++) {
            for (int k = 0; k < AUDIO_ENCODING.length && !found; k ++) {
                try {
                    bufferSize = AudioRecord.getMinBufferSize(FREQUENCY[i], CHANNEL_CONFIGURATION[j], AUDIO_ENCODING[k]);
                    if (bufferSize != AudioRecord.ERROR_BAD_VALUE && bufferSize != AudioRecord.ERROR) {
                        audioRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_DOWNLINK, FREQUENCY[i], CHANNEL_CONFIGURATION[j], AUDIO_ENCODING[k], bufferSize);
                        found = true;
                    }
                } catch (Exception e) {
                    Log.e(TAG, e.toString());
                }
            }
        }
    }

There are no correct parameters found for api 19 or 22 in order to set up an AudioRecord. In every case an exception is raised. I'm quite locked with this. Im not thinking about to use MediaRecoder class because I can not read a buffer directly from the recoder and this is critical for the dtmf decoding proccess. I have also seen some dtmf open source decoder but all of them have this problem


Solution

  • Some android versions have this feature disabled. If you have the android source code you can make it works. Im currently working with cyanogenmod so I have customized the AudioAttributes.java class in order to not to raise an exception when it happens.

    We only have to changhe the setCapturePreset() method in AudioAttributes.java by adding all Audio sources that we want within the switch/case structure.

    This is the original:

    /**
         * @hide
         * Sets the capture preset.
         * Use this audio attributes configuration method when building an {@link AudioRecord}
         * instance with {@link AudioRecord#AudioRecord(AudioAttributes, AudioFormat, int)}.
         * @param preset one of {@link MediaRecorder.AudioSource#DEFAULT},
         *     {@link MediaRecorder.AudioSource#MIC}, {@link MediaRecorder.AudioSource#CAMCORDER},
         *     {@link MediaRecorder.AudioSource#VOICE_RECOGNITION} or
         *     {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION}.
         * @return the same Builder instance.
         */
        @SystemApi
        public Builder setCapturePreset(int preset) {
            switch (preset) {
                case MediaRecorder.AudioSource.DEFAULT:
                case MediaRecorder.AudioSource.MIC:
                case MediaRecorder.AudioSource.CAMCORDER:
                case MediaRecorder.AudioSource.VOICE_RECOGNITION:
                case MediaRecorder.AudioSource.VOICE_COMMUNICATION:
                    mSource = preset;
                    break;
                default:
                    Log.e(TAG, "Invalid capture preset " + preset + " for AudioAttributes");
            }
            return this;
        }
    

    And I replaced with this:

    /**
         * @hide
         * Sets the capture preset.
         * Use this audio attributes configuration method when building an {@link AudioRecord}
         * instance with {@link AudioRecord#AudioRecord(AudioAttributes, AudioFormat, int)}.
         * @param preset one of {@link MediaRecorder.AudioSource#DEFAULT},
         *     {@link MediaRecorder.AudioSource#MIC}, {@link MediaRecorder.AudioSource#CAMCORDER},
         *     {@link MediaRecorder.AudioSource#VOICE_RECOGNITION} or
         *     {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION}.
         * @return the same Builder instance.
         */
        @SystemApi
        public Builder setCapturePreset(int preset) {
            switch (preset) {
                case MediaRecorder.AudioSource.DEFAULT:
                case MediaRecorder.AudioSource.MIC:
                case MediaRecorder.AudioSource.CAMCORDER:
                case MediaRecorder.AudioSource.VOICE_RECOGNITION:
                case MediaRecorder.AudioSource.VOICE_COMMUNICATION:
                case MediaRecorder.AudioSource.VOICE_DOWNLINK:
                case MediaRecorder.AudioSource.VOICE_UPLINK:
                case MediaRecorder.AudioSource.VOICE_CALL:
                    mSource = preset;
                    break;
                default:
                    Log.e(TAG, "Invalid capture preset " + preset + " for AudioAttributes");
            }
            return this;
        }