From 4606c68f89eb68cc35d752d3771b85608e16c7e1 Mon Sep 17 00:00:00 2001 From: Keyur Maru Date: Tue, 24 Dec 2024 03:52:58 -0800 Subject: [PATCH 1/2] feat(android): add support for setting `AudioManager` modes and enabling speakerphone - Introduced `audioManagerMode` in `MethodCallHandlerImpl` to map configuration values to `AudioManager` modes. - Updated `RecordConfig` to include new parameters: - `audioManagerMode` (default: `MODE_NORMAL`). - `setSpeakerphoneOn` (default: `false`). - Modified `AudioRecorder` to: - Configure `AudioManager` mode during initialization if `audioManagerMode` is specified. - Enable speakerphone if `setSpeakerphoneOn` is `true`. - Updated Dart `AndroidRecordConfig`: - Added `audioManagerMode` with an enum for supported modes. - Added `setSpeakerphoneOn` for controlling speakerphone behavior. - Updated `toMap` method to serialize new properties. Reason for changes: - Switching `AudioManager` modes (e.g., to `MODE_IN_COMMUNICATION`) helps address acoustic echo cancellation issues, particularly on devices like the Samsung S20. - Enabling the speakerphone is useful for scenarios where external playback is needed or for devices with echo issues in recording. - These additions provide greater flexibility for managing audio configurations, improving compatibility across a broader range of devices. Signed-off-by: Keyur Maru --- .../methodcall/MethodCallHandlerImpl.kt | 16 +++++++++- .../llfbandit/record/record/RecordConfig.kt | 6 +++- .../record/record/recorder/AudioRecorder.kt | 30 +++++++++++++++++++ .../lib/src/types/android_record_config.dart | 30 +++++++++++++++++++ 4 files changed, 80 insertions(+), 2 deletions(-) diff --git a/record_android/android/src/main/kotlin/com/llfbandit/record/methodcall/MethodCallHandlerImpl.kt b/record_android/android/src/main/kotlin/com/llfbandit/record/methodcall/MethodCallHandlerImpl.kt index d43a212f..c66454bb 100644 --- a/record_android/android/src/main/kotlin/com/llfbandit/record/methodcall/MethodCallHandlerImpl.kt +++ b/record_android/android/src/main/kotlin/com/llfbandit/record/methodcall/MethodCallHandlerImpl.kt @@ -1,6 +1,7 @@ package com.llfbandit.record.methodcall import android.content.Context +import android.media.AudioManager import android.media.MediaRecorder import android.os.Build import com.llfbandit.record.Utils @@ -142,6 +143,17 @@ class MethodCallHandlerImpl( else -> MediaRecorder.AudioSource.DEFAULT } + val audioManagerMode: Int = when(androidConfig?.get("audioManagerMode")) { + "modeNormal" -> AudioManager.MODE_NORMAL + "modeRingtone" -> AudioManager.MODE_RINGTONE + "modeInCall" -> AudioManager.MODE_IN_CALL + "modeInCommunication" -> AudioManager.MODE_IN_COMMUNICATION + "modeCallScreening" -> AudioManager.MODE_CALL_SCREENING + "modeCallRedirect" -> AudioManager.MODE_CALL_REDIRECT + "modeCommunicationRedirect" -> AudioManager.MODE_COMMUNICATION_REDIRECT + else -> AudioManager.MODE_NORMAL + } + return RecordConfig( call.argument("path"), Utils.firstNonNull(call.argument("encoder"), "aacLc"), @@ -155,7 +167,9 @@ class MethodCallHandlerImpl( Utils.firstNonNull(androidConfig?.get("useLegacy") as Boolean?, false), Utils.firstNonNull(androidConfig?.get("muteAudio") as Boolean?, false), Utils.firstNonNull(androidConfig?.get("manageBluetooth") as Boolean?, true), - audioSource + Utils.firstNonNull(androidConfig?.get("setSpeakerphoneOn") as Boolean?, false), + audioSource, + audioManagerMode ) } } \ No newline at end of file diff --git a/record_android/android/src/main/kotlin/com/llfbandit/record/record/RecordConfig.kt b/record_android/android/src/main/kotlin/com/llfbandit/record/record/RecordConfig.kt index 985d0895..8c927072 100644 --- a/record_android/android/src/main/kotlin/com/llfbandit/record/record/RecordConfig.kt +++ b/record_android/android/src/main/kotlin/com/llfbandit/record/record/RecordConfig.kt @@ -1,6 +1,7 @@ package com.llfbandit.record.record import android.media.AudioDeviceInfo +import android.media.AudioManager import android.media.MediaRecorder class RecordConfig( @@ -16,7 +17,10 @@ class RecordConfig( val useLegacy: Boolean = false, val muteAudio: Boolean = false, val manageBluetooth: Boolean = true, - val audioSource: Int = MediaRecorder.AudioSource.DEFAULT + val setSpeakerphoneOn: Boolean = false, + val audioSource: Int = MediaRecorder.AudioSource.DEFAULT, + val audioManagerMode: Int = AudioManager.MODE_NORMAL + ) { val numChannels: Int = 2.coerceAtMost(1.coerceAtLeast(numChannels)) } diff --git a/record_android/android/src/main/kotlin/com/llfbandit/record/record/recorder/AudioRecorder.kt b/record_android/android/src/main/kotlin/com/llfbandit/record/record/recorder/AudioRecorder.kt index 3e80f97c..ef4783b5 100644 --- a/record_android/android/src/main/kotlin/com/llfbandit/record/record/recorder/AudioRecorder.kt +++ b/record_android/android/src/main/kotlin/com/llfbandit/record/record/recorder/AudioRecorder.kt @@ -66,6 +66,12 @@ class AudioRecorder( if (config.muteAudio) { muteAudio(true) } + if (config.audioManagerMode != AudioManager.MODE_NORMAL) { + setupAudioManagerMode() + } + if (config.setSpeakerphoneOn) { + setSpeakerphoneOn() + } } override fun stop(stopCb: ((path: String?) -> Unit)?) { @@ -154,4 +160,28 @@ class AudioRecorder( muteSettings[stream] = audioManager.getStreamVolume(stream) } } + + private fun setupAudioManagerMode() { + val config = this.config ?: return + if (config.audioManagerMode == null) { + return + } + if (config.audioManagerMode == AudioManager.MODE_NORMAL) { + return + } + val audioManager = appContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager + audioManager.setMode(config.audioManagerMode ?: AudioManager.MODE_NORMAL) + } + + private fun setSpeakerphoneOn() { + val config = this.config + if (config == null) { + return + } + if (config.setSpeakerphoneOn == false) { + return + } + val audioManager = appContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager + audioManager.setSpeakerphoneOn(true) + } } \ No newline at end of file diff --git a/record_platform_interface/lib/src/types/android_record_config.dart b/record_platform_interface/lib/src/types/android_record_config.dart index aa4c8ffa..53ecc186 100644 --- a/record_platform_interface/lib/src/types/android_record_config.dart +++ b/record_platform_interface/lib/src/types/android_record_config.dart @@ -24,6 +24,11 @@ class AndroidRecordConfig { /// Defaults to [true]. final bool manageBluetooth; + /// Set the speakerphone on. + /// If [true], this will set the speakerphone on. + /// Useful on devices with echo cancellation issues. + final bool setSpeakerphoneOn; + /// Defines the audio source. /// An audio source defines both a default physical source of audio signal, and a recording configuration. /// @@ -32,11 +37,22 @@ class AndroidRecordConfig { /// Most of the time, you should use [AndroidAudioSource.defaultSource] or [AndroidAudioSource.mic]. final AndroidAudioSource audioSource; + /// Defines the audio manager mode. + /// This is used to set the audio manager mode before recording. + /// + /// Switching to [AudioManagerMode.modeInCommunication] can help to resolve + /// acoustic echo cancellation issues on some devices (Tested on Samsung S20). + /// + /// Defaults to [AudioManagerMode.modeNormal]. + final AudioManagerMode audioManagerMode; + const AndroidRecordConfig({ this.useLegacy = false, this.muteAudio = false, this.manageBluetooth = true, + this.setSpeakerphoneOn = false, this.audioSource = AndroidAudioSource.defaultSource, + this.audioManagerMode = AudioManagerMode.modeNormal, }); Map toMap() { @@ -44,7 +60,9 @@ class AndroidRecordConfig { 'useLegacy': useLegacy, 'muteAudio': muteAudio, 'manageBluetooth': manageBluetooth, + 'setSpeakerphoneOn': setSpeakerphoneOn, 'audioSource': audioSource.name, + 'audioManagerMode': audioManagerMode.name, }; } } @@ -64,3 +82,15 @@ enum AndroidAudioSource { unprocessed, voicePerformance, } + +/// Constants for Android for setting specific audio manager modes +/// https://developer.android.com/reference/kotlin/android/media/AudioManager#setmode +enum AudioManagerMode { + modeNormal, + modeRingtone, + modeInCall, + modeInCommunication, + modeCallScreening, + modeCallRedirect, + modeCommunicationRedirect, +} From 38a28961152c76a678b6cd1b939532b14a26c9f9 Mon Sep 17 00:00:00 2001 From: Keyur Maru Date: Tue, 24 Dec 2024 03:57:40 -0800 Subject: [PATCH 2/2] chore: bump `record_android` to v1.3.1 - Updated `record_android` to v1.3.1 in `pubspec.yaml`. - Includes support for `audioManagerMode` and `setSpeakerphoneOn` for improved echo cancellation and external playback scenarios. Signed-off-by: Keyur Maru --- record/pubspec.yaml | 2 +- record_android/CHANGELOG.md | 5 +++++ record_android/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/record/pubspec.yaml b/record/pubspec.yaml index ebbb2824..6aa662da 100644 --- a/record/pubspec.yaml +++ b/record/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: record_web: ^1.1.4 record_windows: ^1.0.4 record_linux: '>=0.5.0 <1.0.0' - record_android: ^1.3.0 + record_android: ^1.3.1 record_darwin: ^1.2.0 dev_dependencies: diff --git a/record_android/CHANGELOG.md b/record_android/CHANGELOG.md index bbfda2b5..9cc7d516 100644 --- a/record_android/CHANGELOG.md +++ b/record_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.3.1 +* feat: Add `audioManagerMode` support to configure `AudioManager` modes for improved device compatibility for acoustic echo cancellation. +* feat: Add `setSpeakerphoneOn` option to enable speakerphone, addressing echo issues and enabling external playback scenarios. +* feat: Update Dart `AndroidRecordConfig` to support new options (`audioManagerMode` and `setSpeakerphoneOn`). + ## 1.3.0 * feat: Add audio source config. * feat: Add manageBluetooth (SCO) option. diff --git a/record_android/pubspec.yaml b/record_android/pubspec.yaml index 752feca4..c6f669db 100644 --- a/record_android/pubspec.yaml +++ b/record_android/pubspec.yaml @@ -1,6 +1,6 @@ name: record_android description: Android specific implementation for record package called by record_platform_interface. -version: 1.3.0 +version: 1.3.1 homepage: https://github.com/llfbandit/record/tree/master/record_android environment: