Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add audioManagerMode and setSpeakerphoneOn support in record_android to fix Echo Cancellation on Samsung S20 #454

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion record/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 5 additions & 0 deletions record_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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"),
Expand All @@ -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
)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.llfbandit.record.record

import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.media.MediaRecorder

class RecordConfig(
Expand All @@ -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))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)?) {
Expand Down Expand Up @@ -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)
}
}
2 changes: 1 addition & 1 deletion record_android/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
30 changes: 30 additions & 0 deletions record_platform_interface/lib/src/types/android_record_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -32,19 +37,32 @@ 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<String, dynamic> toMap() {
return {
'useLegacy': useLegacy,
'muteAudio': muteAudio,
'manageBluetooth': manageBluetooth,
'setSpeakerphoneOn': setSpeakerphoneOn,
'audioSource': audioSource.name,
'audioManagerMode': audioManagerMode.name,
};
}
}
Expand All @@ -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,
}