16

How can I do audio recordings using android.media.AudioRecord without any smartphone-manufacturer-dependent fancy signal processing like automatic gain control (AGC) and/or equalization, noise suppression, echo cancellation, ... just the pure microphone signal?

Background

MediaRecorder.AudioSource provides nine constants,

  • DEFAULT and MIC initially being there,
  • VOICE_UPLINK, VOICE_DOWNLINK, and VOICE_CALL added in API level 4,
  • CAMCORDER and VOICE_RECOGNITION added in API 7,
  • VOICE_COMMUNICATION added in API 11,
  • REMOTE_SUBMIX added in API 19 but not available to third-party applications.

But none of them does a clean job across all smartphones. Rather, I have to find out myself it seems, which device uses which combinations of signal processing blocks for which MediaRecorder.AudioSource constant.

Would be nice to have a tenth constant like PURE_MIC added in API level 20.

But as long as this is not available, what can I do instead?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Hartmut Pfitzinger
  • 2,304
  • 3
  • 28
  • 48

4 Answers4

8

Short answer is "Nothing".

The AudioSources correspond to various logical audio input devices depending on the accessories that you have connected to the phone and the current use-case, which in turn corresponds to physical devices (primary built-in mic, secondary mic, wired headset mic, etc) with different tunings.

Each such combination of physical device and tuning is trimmed by the OEM to meet both external requirements (e.g. CTS, operator requirements, etc) and internal acoustic requirements set by the OEM itself. This process may cause the introduction of various filters - such as AGC, noise suppression, equalization, etc - into the audio input path at the hardware codec or multimedia DSP level.

While a PURE_MIC source might be useful in for some applications, it's not something that's available today.
On many devices you can control things like microphone gain, and possibly even the filter chain, by using amixer to write to the hardware codec's ALSA controls. However, this would obviously be a very platform-specific approach, and I also suspect that you have to be running as either the root or audio user to be allowed to do this.

Michael
  • 57,169
  • 9
  • 80
  • 125
  • 1
    I would agree for DEFAULT, MIC, VOICE_*, but I understood that VOICE_RECOGNITION was especially introduced to avoid additional signal processing (which some of the OEMs obviously ignored). Is there another way, e.g. a 'magic' combination of audioManager.setParameters calls? – Hartmut Pfitzinger Jan 17 '13 at 18:19
  • I work with the SW side of audio, so I'm not involved in the tuning process. But I would expect that a clean speech signal (without background noise, clipping, etc) is desired in the VOICE_RECOGNITION case for it to be usable for speech-to-text processing. Therefore I would guess that at least a noise suppressor and an AGC / multiband compressor would be used. – Michael Jan 17 '13 at 18:45
  • 3
    ASR generally would benefit from pure audio as it does its own quasi-AGC in the feature vector extraction and usually is trained with noise. The document "Android 4.1 Compatibility Definition" (Sep 7, 2012), Sec.5.3 states clearly: With VOICE_RECOGNITION, Noise reduction processing and Automatic gain control "MUST be disabled." The manufacturers hopefully change that for Android >=4.1, then PURE_MIC is not necessary. But I have my doubts. So, still my question: Is there another way? – Hartmut Pfitzinger Jan 18 '13 at 08:32
  • 1
    Did you find out the way to obtain pure audio from the MIC without any processing? – Anonymous Me Apr 29 '15 at 19:59
  • 4
    More than 2 years after my question and some more API levels later, I still don't see any other way than try&error. But I would still be happy if someone could add a 2nd answer with different content. – Hartmut Pfitzinger Apr 30 '15 at 05:59
3

Some devices add AGC effect to the sound input tract by default. Therefore, you need to obtain reference to corresponding AudioEffect object and force it to disable.

First, obtain AutomaticGainControl object linked to the AudioRecord audio session, and then just set it disabled:

if (AutomaticGainControl.isAvailable()) {
    AutomaticGainControl agc = AutomaticGainControl.create(
            myAudioRecord.getAudioSessionId()
        );
    agc.setEnabled(false);
}
AterLux
  • 4,566
  • 2
  • 10
  • 13
  • thank you. I tried this for a large number of devices but without success. Would you please name some of these devices and Android version where this method works? – Hartmut Pfitzinger Apr 18 '16 at 13:11
3

Note: Most of the audio sources (including DEFAULT) apply processing to the audio signal. To record raw audio select UNPROCESSED. Some devices do not support unprocessed input. Call AudioManager.getProperty("PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED") first to verify it's available. If it is not, try using VOICE_RECOGNITION instead, which does not employ AGC or noise suppression. You can use UNPROCESSED as an audio source even when the property is not supported, but there is no guarantee whether the signal will be unprocessed or not in that case.

Android documentation Link https://developer.android.com/guide/topics/media/mediarecorder.html#example

    AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
    if(audioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED) !=null)
        mRecorder.setAudioSource(MediaRecorder.AudioSource.UNPROCESSED);
    else
        mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_RECOGNITION);
Arun Shankar
  • 2,295
  • 16
  • 20
0

MIC should be fine, and for the rest you need to know if they are supported.

I've made a class for this:

enum class AudioSource(val audioSourceValue: Int, val minApi: Int) {
    VOICE_CALL(MediaRecorder.AudioSource.VOICE_CALL, 4), DEFAULT(MediaRecorder.AudioSource.DEFAULT, 1), MIC(MediaRecorder.AudioSource.MIC, 1),
    VOICE_COMMUNICATION(MediaRecorder.AudioSource.VOICE_COMMUNICATION, 11), CAMCORDER(MediaRecorder.AudioSource.CAMCORDER, 7),
    VOICE_RECOGNITION(MediaRecorder.AudioSource.VOICE_RECOGNITION, 7),
    VOICE_UPLINK(MediaRecorder.AudioSource.VOICE_UPLINK, 4), VOICE_DOWNLINK(MediaRecorder.AudioSource.VOICE_DOWNLINK, 4),
    @TargetApi(Build.VERSION_CODES.KITKAT)
    REMOTE_SUBMIX(MediaRecorder.AudioSource.REMOTE_SUBMIX, 19),
    @TargetApi(Build.VERSION_CODES.N)
    UNPROCESSED(MediaRecorder.AudioSource.UNPROCESSED, 24);

    fun isSupported(context: Context): Boolean =
            when {
                Build.VERSION.SDK_INT < minApi -> false
                this != UNPROCESSED -> true
                else -> {
                    val audioManager: AudioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
                    Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && "true" == audioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED)
                }
            }

    companion object {
        fun getAllSupportedValues(context: Context): ArrayList<AudioSource> {
            val values = AudioSource.values()
            val result = ArrayList<AudioSource>(values.size)
            for (value in values)
                if (value.isSupported(context))
                    result.add(value)
            return result
        }
    }

}
android developer
  • 114,585
  • 152
  • 739
  • 1,270