From 5fdd50f89e5ca9ec0347570ddf33a6aba905a0af Mon Sep 17 00:00:00 2001 From: habibialireza Date: Wed, 17 Jun 2026 11:35:03 +0200 Subject: [PATCH] microphone gain control added to app --- lib/open_earable_flutter.dart | 1 + .../capabilities/microphone_gain_manager.dart | 70 +++++++++++++++++++ lib/src/models/devices/open_earable_v2.dart | 35 ++++++++++ 3 files changed, 106 insertions(+) create mode 100644 lib/src/models/capabilities/microphone_gain_manager.dart diff --git a/lib/open_earable_flutter.dart b/lib/open_earable_flutter.dart index a9cc926..a2ebce1 100644 --- a/lib/open_earable_flutter.dart +++ b/lib/open_earable_flutter.dart @@ -60,6 +60,7 @@ export 'src/models/capabilities/jingle_player.dart'; export 'src/models/capabilities/audio_player_controls.dart'; export 'src/models/capabilities/storage_path_audio_player.dart'; export 'src/models/capabilities/audio_mode_manager.dart'; +export 'src/models/capabilities/microphone_gain_manager.dart'; export 'src/models/capabilities/microphone_manager.dart'; export 'src/models/capabilities/power_saving_mode_manager.dart'; export 'src/models/capabilities/stereo_device.dart'; diff --git a/lib/src/models/capabilities/microphone_gain_manager.dart b/lib/src/models/capabilities/microphone_gain_manager.dart new file mode 100644 index 0000000..78b9f97 --- /dev/null +++ b/lib/src/models/capabilities/microphone_gain_manager.dart @@ -0,0 +1,70 @@ +abstract class MicrophoneGainManager { + Future getMicrophoneGain(); + + Future setMicrophoneGain(MicrophoneGain gain); +} + +class MicrophoneGain { + static const int maxGainRegister = 0x00; + static const int zeroDbRegister = 0x40; + static const int minGainRegister = 0xFE; + static const int muteRegister = 0xFF; + static const int defaultRegister = 0x20; + static const double stepDb = 0.375; + static const double maxGainDb = 24.0; + static const double minGainDb = -71.25; + + final int leftRegister; + final int rightRegister; + + const MicrophoneGain({ + required this.leftRegister, + required this.rightRegister, + }) : assert(leftRegister >= 0 && leftRegister <= muteRegister), + assert(rightRegister >= 0 && rightRegister <= muteRegister); + + const MicrophoneGain.stereo(int register) + : this(leftRegister: register, rightRegister: register); + + const MicrophoneGain.muted() + : this.stereo(muteRegister); + + bool get isMuted => + leftRegister == muteRegister && rightRegister == muteRegister; + + double? get leftDb => registerToDb(leftRegister); + + double? get rightDb => registerToDb(rightRegister); + + MicrophoneGain copyWith({ + int? leftRegister, + int? rightRegister, + }) { + return MicrophoneGain( + leftRegister: leftRegister ?? this.leftRegister, + rightRegister: rightRegister ?? this.rightRegister, + ); + } + + static double? registerToDb(int register) { + _validateRegister(register); + if (register == muteRegister) { + return null; + } + return maxGainDb - (register * stepDb); + } + + static int dbToRegister(double db) { + final clampedDb = db.clamp(minGainDb, maxGainDb).toDouble(); + return ((maxGainDb - clampedDb) / stepDb) + .round() + .clamp(maxGainRegister, minGainRegister) + .toInt(); + } + + static void _validateRegister(int register) { + if (register < 0 || register > muteRegister) { + throw RangeError.range(register, 0, muteRegister, 'register'); + } + } +} diff --git a/lib/src/models/devices/open_earable_v2.dart b/lib/src/models/devices/open_earable_v2.dart index 235c4fd..1a01c82 100644 --- a/lib/src/models/devices/open_earable_v2.dart +++ b/lib/src/models/devices/open_earable_v2.dart @@ -33,6 +33,8 @@ const String _micSelectCharacteristicUuid = "0x1410df97-5f68-4ebb-a7c7-5e0fb9ae7557"; const String _audioModeCharacteristicUuid = "0x1410df96-5f68-4ebb-a7c7-5e0fb9ae7557"; +const String _dmicGainCharacteristicUuid = + "1410df99-5f68-4ebb-a7c7-5e0fb9ae7557"; const String _buttonServiceUuid = "29c10bdc-4773-11ee-be56-0242ac120002"; const String _buttonCharacteristicUuid = "29c10f38-4773-11ee-be56-0242ac120002"; @@ -76,6 +78,7 @@ class OpenEarableV2 extends BluetoothWearable DeviceFirmwareVersion, DeviceHardwareVersion, MicrophoneManager, + MicrophoneGainManager, AudioModeManager, EdgeRecorderManager, ButtonManager, @@ -460,6 +463,38 @@ class OpenEarableV2 extends BluetoothWearable return availableMicrophones.firstWhere((mic) => mic.id == microphoneId); } + // MARK: MicrophoneGainManager + + @override + Future getMicrophoneGain() async { + final gainBytes = await bleManager.read( + deviceId: deviceId, + serviceId: _audioConfigServiceUuid, + characteristicId: _dmicGainCharacteristicUuid, + ); + + if (gainBytes.length != 2) { + throw StateError( + 'Microphone gain characteristic expected 2 values, but got ${gainBytes.length}', + ); + } + + return MicrophoneGain( + leftRegister: gainBytes[0], + rightRegister: gainBytes[1], + ); + } + + @override + Future setMicrophoneGain(MicrophoneGain gain) { + return bleManager.write( + deviceId: deviceId, + serviceId: _audioConfigServiceUuid, + characteristicId: _dmicGainCharacteristicUuid, + byteData: [gain.leftRegister, gain.rightRegister], + ); + } + // MARK: AudioModeManager @override