Skip to content

Commit 450db60

Browse files
committed
feat(android): Add audio source config.
1 parent a00b303 commit 450db60

File tree

10 files changed

+148
-90
lines changed

10 files changed

+148
-90
lines changed

record_android/android/src/main/kotlin/com/llfbandit/record/methodcall/MethodCallHandlerImpl.kt

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package com.llfbandit.record.methodcall
22

33
import android.app.Activity
44
import android.content.Context
5+
import android.media.MediaRecorder
6+
import android.os.Build
57
import com.llfbandit.record.Utils
68
import com.llfbandit.record.permission.PermissionManager
79
import com.llfbandit.record.record.RecordConfig
8-
import com.llfbandit.record.record.bluetooth.BluetoothReceiver
910
import com.llfbandit.record.record.device.DeviceUtils
1011
import com.llfbandit.record.record.format.AudioFormats
1112
import io.flutter.plugin.common.BinaryMessenger
@@ -127,6 +128,30 @@ class MethodCallHandlerImpl(
127128
private fun getRecordConfig(call: MethodCall): RecordConfig {
128129
val androidConfig = call.argument("androidConfig") as Map<*, *>?
129130

131+
val audioSource: Int = when(androidConfig?.get("audioSource")) {
132+
"defaultSource" -> MediaRecorder.AudioSource.DEFAULT
133+
"mic" -> MediaRecorder.AudioSource.MIC
134+
"voiceUplink" -> MediaRecorder.AudioSource.VOICE_UPLINK
135+
"voiceDownlink" -> MediaRecorder.AudioSource.VOICE_DOWNLINK
136+
"voiceCall" -> MediaRecorder.AudioSource.VOICE_CALL
137+
"camcorder" -> MediaRecorder.AudioSource.CAMCORDER
138+
"voiceRecognition" -> MediaRecorder.AudioSource.VOICE_RECOGNITION
139+
"voiceCommunication" -> MediaRecorder.AudioSource.VOICE_COMMUNICATION
140+
"remoteSubMix" -> MediaRecorder.AudioSource.REMOTE_SUBMIX
141+
"unprocessed" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
142+
MediaRecorder.AudioSource.UNPROCESSED
143+
} else {
144+
MediaRecorder.AudioSource.DEFAULT
145+
}
146+
"voicePerformance" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
147+
MediaRecorder.AudioSource.VOICE_PERFORMANCE
148+
} else {
149+
MediaRecorder.AudioSource.DEFAULT
150+
}
151+
152+
else -> MediaRecorder.AudioSource.DEFAULT
153+
}
154+
130155
return RecordConfig(
131156
call.argument("path"),
132157
Utils.firstNonNull(call.argument("encoder"), "aacLc"),
@@ -139,7 +164,8 @@ class MethodCallHandlerImpl(
139164
Utils.firstNonNull(call.argument("noiseSuppress"), false),
140165
Utils.firstNonNull(androidConfig?.get("useLegacy") as Boolean?, false),
141166
Utils.firstNonNull(androidConfig?.get("muteAudio") as Boolean?, false),
142-
Utils.firstNonNull(androidConfig?.get("manageBluetoothAudio") as Boolean?, true),
167+
Utils.firstNonNull(androidConfig?.get("manageBluetooth") as Boolean?, true),
168+
audioSource
143169
)
144170
}
145171
}

record_android/android/src/main/kotlin/com/llfbandit/record/methodcall/RecorderWrapper.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ internal class RecorderWrapper(
148148
}
149149

150150
private fun createRecorder(config: RecordConfig): IRecorder {
151-
if (config.manageBluetoothAudio) {
151+
if (config.manageBluetooth) {
152152
maybeStartBluetooth(config)
153153
}
154154

record_android/android/src/main/kotlin/com/llfbandit/record/record/PCMReader.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class PCMReader(
9090

9191
val reader = try {
9292
AudioRecord(
93-
MediaRecorder.AudioSource.VOICE_COMMUNICATION,
93+
config.audioSource,
9494
sampleRate,
9595
channels,
9696
audioFormat,

record_android/android/src/main/kotlin/com/llfbandit/record/record/RecordConfig.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.llfbandit.record.record
22

33
import android.media.AudioDeviceInfo
4+
import android.media.MediaRecorder
45

56
class RecordConfig(
67
val path: String?,
@@ -14,7 +15,8 @@ class RecordConfig(
1415
val noiseSuppress: Boolean = false,
1516
val useLegacy: Boolean = false,
1617
val muteAudio: Boolean = false,
17-
val manageBluetoothAudio: Boolean = true,
18+
val manageBluetooth: Boolean = true,
19+
val audioSource: Int = MediaRecorder.AudioSource.DEFAULT
1820
) {
1921
val numChannels: Int = 2.coerceAtMost(1.coerceAtLeast(numChannels))
2022
}

record_android/android/src/main/kotlin/com/llfbandit/record/record/recorder/MediaRecorder.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class MediaRecorder(
4646
MediaRecorder(context)
4747
}
4848

49-
recorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT)
49+
recorder.setAudioSource(config.audioSource)
5050
recorder.setAudioEncodingBitRate(config.bitRate)
5151
recorder.setAudioSamplingRate(config.sampleRate)
5252
recorder.setAudioChannels(2.coerceAtMost(1.coerceAtLeast(config.numChannels)))
@@ -184,8 +184,8 @@ class MediaRecorder(
184184
private fun updateState(state: RecordState) {
185185
when (state) {
186186
RecordState.PAUSE -> {
187-
mIsRecording =true
188-
mIsPaused =true
187+
mIsRecording = true
188+
mIsPaused = true
189189
recorderStateStreamHandler.sendStateEvent(RecordState.PAUSE.id)
190190
}
191191

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/// Android specific configuration for recording.
2+
class AndroidRecordConfig {
3+
/// Uses Android MediaRecorder if [true].
4+
///
5+
/// Uses advanced recorder with media codecs and additionnal features
6+
/// by default.
7+
final bool useLegacy;
8+
9+
/// If [true], this will mute all audio streams like alarms, music, ring, ...
10+
///
11+
/// This is useful when you want to record audio without any background noise.
12+
///
13+
/// The streams are restored to their previous state after recording is stopped
14+
/// and will stay at current state on pause/resume.
15+
///
16+
/// Use at your own risks!
17+
final bool muteAudio;
18+
19+
/// Try to start a bluetooth audio connection to a headset (Bluetooth SCO).
20+
/// Defaults to [true].
21+
final bool manageBluetooth;
22+
23+
/// Defines the audio source.
24+
/// An audio source defines both a default physical source of audio signal, and a recording configuration.
25+
///
26+
/// Depending of the constructor some effects are available or not depending of this source.
27+
///
28+
/// Most of the time, you should use [AndroidAudioSource.defaultSource] or [AndroidAudioSource.mic].
29+
final AndroidAudioSource audioSource;
30+
31+
const AndroidRecordConfig({
32+
this.useLegacy = false,
33+
this.muteAudio = false,
34+
this.manageBluetooth = true,
35+
this.audioSource = AndroidAudioSource.defaultSource,
36+
});
37+
38+
Map<String, dynamic> toMap() {
39+
return {
40+
'useLegacy': useLegacy,
41+
'muteAudio': muteAudio,
42+
'manageBluetooth': manageBluetooth,
43+
'audioSource': audioSource.name,
44+
};
45+
}
46+
}
47+
48+
// Constants for Android for setting specific audio source types
49+
// https://developer.android.com/reference/kotlin/android/media/MediaRecorder.AudioSource
50+
enum AndroidAudioSource {
51+
defaultSource,
52+
mic,
53+
voiceUplink,
54+
voiceDownlink,
55+
voiceCall,
56+
camcorder,
57+
voiceRecognition,
58+
voiceCommunication,
59+
remoteSubMix,
60+
unprocessed,
61+
voicePerformance,
62+
}

record_platform_interface/lib/src/types/ios_audio_category_option.dart

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/// iOS specific configuration for recording.
2+
class IosRecordConfig {
3+
/// Constants that specify optional audio behaviors.
4+
/// https://developer.apple.com/documentation/avfaudio/avaudiosession/categoryoptions
5+
final List<IosAudioCategoryOption> categoryOptions;
6+
7+
/// Manage the shared AVAudioSession (defaults to `true`).
8+
/// Set this to false if another plugin is already managing the AVAudioSession.
9+
/// If false, audioCategories config will have no effect.
10+
final bool manageAudioSession;
11+
12+
const IosRecordConfig({
13+
this.categoryOptions = const [
14+
IosAudioCategoryOption.defaultToSpeaker,
15+
IosAudioCategoryOption.allowBluetooth,
16+
IosAudioCategoryOption.allowBluetoothA2DP,
17+
],
18+
this.manageAudioSession = true,
19+
});
20+
Map<String, dynamic> toMap() {
21+
return {
22+
"categoryOptions": categoryOptions.map((e) => e.name).join(','),
23+
"manageAudioSession": manageAudioSession,
24+
};
25+
}
26+
}
27+
28+
/// Constants that specify optional audio behaviors.
29+
/// https://developer.apple.com/documentation/avfaudio/avaudiosession/categoryoptions
30+
enum IosAudioCategoryOption {
31+
mixWithOthers,
32+
duckOthers,
33+
allowBluetooth,
34+
defaultToSpeaker,
35+
36+
/// available from iOS 9.0
37+
interruptSpokenAudioAndMixWithOthers,
38+
39+
/// available from iOS 10.0
40+
allowBluetoothA2DP,
41+
42+
/// available from iOS 10.0
43+
allowAirPlay,
44+
45+
/// available from iOS 14.5
46+
overrideMutedMicrophoneInterruption
47+
}

record_platform_interface/lib/src/types/record_config.dart

Lines changed: 1 addition & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class RecordConfig {
6464
this.echoCancel = false,
6565
this.noiseSuppress = false,
6666
this.androidConfig = const AndroidRecordConfig(),
67-
this.iosConfig = const IosRecordConfig(),
67+
this.iosConfig = const IosRecordConfig(),
6868
});
6969

7070
Map<String, dynamic> toMap() {
@@ -82,67 +82,3 @@ class RecordConfig {
8282
};
8383
}
8484
}
85-
86-
/// Android specific configuration for recording.
87-
class AndroidRecordConfig {
88-
/// Uses Android MediaRecorder if [true].
89-
///
90-
/// Uses advanced recorder with media codecs and additionnal features
91-
/// by default.
92-
final bool useLegacy;
93-
94-
/// If [true], this will mute all audio streams like alarms, music, ring, ...
95-
///
96-
/// This is useful when you want to record audio without any background noise.
97-
///
98-
/// The streams are restored to their previous state after recording is stopped
99-
/// and will stay at current state on pause/resume.
100-
///
101-
/// Use at your own risks!
102-
final bool muteAudio;
103-
104-
/// Try to start a bluetooth audio connection to a headset.
105-
/// Defaults to [true].
106-
final bool manageBluetoothAudio;
107-
108-
const AndroidRecordConfig({
109-
this.useLegacy = false,
110-
this.muteAudio = false,
111-
this.manageBluetoothAudio = true,
112-
});
113-
114-
Map<String, dynamic> toMap() {
115-
return {
116-
'useLegacy': useLegacy,
117-
'muteAudio': muteAudio,
118-
'manageBluetoothAudio': manageBluetoothAudio,
119-
};
120-
}
121-
}
122-
123-
/// iOS specific configuration for recording.
124-
class IosRecordConfig {
125-
/// Constants that specify optional audio behaviors.
126-
/// https://developer.apple.com/documentation/avfaudio/avaudiosession/categoryoptions
127-
final List<IosAudioCategoryOption> categoryOptions;
128-
/// Manage the shared AVAudioSession (defaults to `true`).
129-
/// Set this to false if another plugin is already managing the AVAudioSession.
130-
/// If false, audioCategories config will have no effect.
131-
final bool manageAudioSession;
132-
133-
const IosRecordConfig({
134-
this.categoryOptions = const [
135-
IosAudioCategoryOption.defaultToSpeaker,
136-
IosAudioCategoryOption.allowBluetooth,
137-
IosAudioCategoryOption.allowBluetoothA2DP,
138-
],
139-
this.manageAudioSession = true,
140-
});
141-
Map<String, dynamic> toMap() {
142-
return {
143-
"categoryOptions": categoryOptions.map((e) => e.name).join(','),
144-
"manageAudioSession": manageAudioSession,
145-
};
146-
}
147-
148-
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export 'package:record_platform_interface/src/types/amplitude.dart';
2+
export 'package:record_platform_interface/src/types/android_record_config.dart';
23
export 'package:record_platform_interface/src/types/audio_encoder.dart';
34
export 'package:record_platform_interface/src/types/input_device.dart';
4-
export 'package:record_platform_interface/src/types/ios_audio_category_option.dart';
5+
export 'package:record_platform_interface/src/types/ios_record_config.dart';
56
export 'package:record_platform_interface/src/types/record_config.dart';
67
export 'package:record_platform_interface/src/types/record_state.dart';

0 commit comments

Comments
 (0)