Skip to content
Draft
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
28 changes: 26 additions & 2 deletions open_wearable/lib/view_models/sensor_recorder_provider_io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import '../models/sensor_streams.dart';
/// - Recording status (`isRecording`, `recordingStart`, etc.).
/// - Recorder access used by recorder UI pages.
class SensorRecorderProvider with ChangeNotifier {
static const Duration _microphoneConfigurationSettleDelay =
Duration(milliseconds: 300);

final Map<Wearable, Map<Sensor, Recorder>> _recorders = {};
final Map<String, String> _recordingFilepathsBySensorIdentity = {};
Future<void> _pendingSynchronization = Future<void>.value();
Expand All @@ -51,9 +54,30 @@ class SensorRecorderProvider with ChangeNotifier {

InputDevice? _selectedBLEDevice;

/// Label for the currently selected BLE microphone input, when available.
String? get selectedBLEDeviceLabel => _selectedBLEDevice?.label;

int _microphoneConfigurationRevision = 0;
int get microphoneConfigurationRevision => _microphoneConfigurationRevision;

bool _isBLEMicrophoneStreamingEnabled = false;
bool get isBLEMicrophoneStreamingEnabled => _isBLEMicrophoneStreamingEnabled;

void notifyMicrophoneConfigurationChanged() {
_bumpMicrophoneConfigurationRevision();
Future<void>.delayed(_microphoneConfigurationSettleDelay, () {
if (_disposed) {
return;
}
_bumpMicrophoneConfigurationRevision();
});
}

void _bumpMicrophoneConfigurationRevision() {
_microphoneConfigurationRevision++;
notifyListeners();
}

// Path for temporary streaming file
String? _streamingPath;
bool _isStreamingActive = false;
Expand Down Expand Up @@ -133,7 +157,7 @@ class SensorRecorderProvider with ChangeNotifier {
.onAmplitudeChanged(const Duration(milliseconds: 100))
.listen((amp) {
final normalized = (amp.current + 50) / 50;
_waveformData.add(normalized.clamp(0.0, 2.0));
_waveformData.add(normalized.clamp(0.0, 1.0));

if (_waveformData.length > 100) {
_waveformData.removeAt(0);
Expand Down Expand Up @@ -295,7 +319,7 @@ class SensorRecorderProvider with ChangeNotifier {
.onAmplitudeChanged(const Duration(milliseconds: 100))
.listen((amp) {
final normalized = (amp.current + 50) / 50;
_waveformData.add(normalized.clamp(0.0, 2.0));
_waveformData.add(normalized.clamp(0.0, 1.0));

if (_waveformData.length > 100) {
_waveformData.removeAt(0);
Expand Down
11 changes: 11 additions & 0 deletions open_wearable/lib/view_models/sensor_recorder_provider_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,20 @@ class SensorRecorderProvider with ChangeNotifier {
int get waveformRevision => _waveformRevision;
List<double> get waveformData => List.unmodifiable(_waveformData);

/// Web does not expose a selected BLE microphone input.
String? get selectedBLEDeviceLabel => null;

int _microphoneConfigurationRevision = 0;
int get microphoneConfigurationRevision => _microphoneConfigurationRevision;

bool _isBLEMicrophoneStreamingEnabled = false;
bool get isBLEMicrophoneStreamingEnabled => _isBLEMicrophoneStreamingEnabled;

void notifyMicrophoneConfigurationChanged() {
_microphoneConfigurationRevision++;
notifyListeners();
}

Future<bool> startBLEMicrophoneStream() async {
logger.w('BLE microphone streaming is not supported on web.');
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:open_earable_flutter/open_earable_flutter.dart';
import 'package:open_wearable/view_models/sensor_recorder_provider_facade.dart';
import 'package:provider/provider.dart';

import 'stereo_pair_option_selector.dart';

Expand Down Expand Up @@ -30,6 +32,13 @@ class MicrophoneSelectionWidget extends StatelessWidget {
readSelection: (manager) => manager.getMicrophone(),
applySelection: (manager, microphone) async {
manager.setMicrophone(microphone);
try {
context
.read<SensorRecorderProvider>()
.notifyMicrophoneConfigurationChanged();
} catch (_) {
// The selector can be embedded outside the recorder provider tree.
}
},
optionsFor: (manager) => manager.availableMicrophones,
supportsOption: (manager, microphone) => manager.availableMicrophones.any(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ class SensorConfigurationView extends StatelessWidget {
final recorderProvider =
Provider.of<SensorRecorderProvider>(context, listen: false);
bool shouldEnableMicrophoneStreaming = false;
bool didApplyMicrophoneConfiguration = false;

for (final target in targets) {
final primaryEntriesToApply = _entriesToApplyForProvider(target.provider);
Expand All @@ -292,6 +293,7 @@ class SensorConfigurationView extends StatelessWidget {
final SensorConfiguration config = entry.$1;
final SensorConfigurationValue value = entry.$2;
if (config.name.toLowerCase().contains('microphone')) {
didApplyMicrophoneConfiguration = true;
final options =
target.provider.getSelectedConfigurationOptions(config);
if (options.any((opt) => opt is StreamSensorConfigOption)) {
Expand All @@ -306,6 +308,9 @@ class SensorConfigurationView extends StatelessWidget {
for (final entry in mirroredEntriesToApply) {
final SensorConfiguration config = entry.$1;
final SensorConfigurationValue value = entry.$2;
if (config.name.toLowerCase().contains('microphone')) {
didApplyMicrophoneConfiguration = true;
}
config.setConfiguration(value);
}

Expand All @@ -322,6 +327,10 @@ class SensorConfigurationView extends StatelessWidget {
);
}

if (didApplyMicrophoneConfiguration) {
recorderProvider.notifyMicrophoneConfigurationChanged();
}

if (!context.mounted) {
return;
}
Expand Down
Loading
Loading