Skip to content

Commit 53c02e6

Browse files
committed
try again
1 parent 7232349 commit 53c02e6

File tree

2 files changed

+51
-40
lines changed

2 files changed

+51
-40
lines changed

lib/services/ai_service.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,19 @@ class AIService {
151151

152152
/// Handle voice input state changes
153153
void _handleVoiceInputState(VoiceInputState state) {
154-
debugPrint('AIService: Voice input state: ${state.status}');
154+
debugPrint('AIService: Voice input state: ${state.status}, hasAudio: ${state.audioData != null}');
155155

156-
if (state.status == VoiceInputStatus.complete && state.audioData != null) {
156+
// Process audio when recording completes with audio data
157+
if ((state.status == VoiceInputStatus.complete ||
158+
state.status == VoiceInputStatus.stopped) &&
159+
state.audioData != null) {
157160
// Process the recorded audio
158161
_processRecordedAudio(state.audioData!, state.source);
162+
} else if (state.status == VoiceInputStatus.stopped && state.audioData == null) {
163+
// Recording stopped but no audio captured
164+
debugPrint('AIService: Recording stopped with no audio data');
165+
_isProcessing = false;
166+
_showErrorMessage('No audio captured');
159167
}
160168
}
161169

lib/services/voice_input_service.dart

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import 'dart:async';
22
import 'dart:typed_data';
33
import 'package:agixt/services/bluetooth_manager.dart';
4+
import 'package:agixt/services/bluetooth_reciever.dart';
45
import 'package:agixt/services/watch_service.dart';
56
import 'package:agixt/services/wake_word_service.dart';
7+
import 'package:agixt/utils/lc3.dart';
68
import 'package:flutter/foundation.dart';
7-
import 'package:flutter/services.dart';
89
import 'package:flutter_sound/flutter_sound.dart';
910
import 'package:path_provider/path_provider.dart';
1011
import 'package:shared_preferences/shared_preferences.dart';
@@ -33,10 +34,8 @@ class VoiceInputService {
3334
final FlutterSoundRecorder _recorder = FlutterSoundRecorder();
3435
bool _recorderInitialized = false;
3536

36-
// Method channel for glasses microphone
37-
static const MethodChannel _glassesAudioChannel = MethodChannel(
38-
'dev.agixt.agixt/glasses_audio',
39-
);
37+
// Bluetooth receiver for glasses audio
38+
final BluetoothReciever _bluetoothReciever = BluetoothReciever.singleton;
4039

4140
// State
4241
bool _isRecording = false;
@@ -75,8 +74,8 @@ class VoiceInputService {
7574
// Initialize phone recorder
7675
await _initializeRecorder();
7776

78-
// Set up glasses audio callback
79-
_glassesAudioChannel.setMethodCallHandler(_handleGlassesAudioCall);
77+
// Note: Glasses audio is handled by BluetoothReciever.voiceCollectorAI
78+
// No additional setup needed here
8079

8180
// Note: Wake word callback is handled by AIService which coordinates
8281
// the full flow (recording -> transcription -> AGiXT -> response)
@@ -99,29 +98,9 @@ class VoiceInputService {
9998
}
10099
}
101100

102-
/// Handle method calls from native glasses audio code
103-
Future<dynamic> _handleGlassesAudioCall(MethodCall call) async {
104-
switch (call.method) {
105-
case 'onAudioData':
106-
final audioData = call.arguments['audioData'] as Uint8List?;
107-
if (audioData != null &&
108-
_isRecording &&
109-
_activeSource == VoiceInputSource.glasses) {
110-
_audioChunkController.add(audioData);
111-
}
112-
return true;
113-
114-
case 'onRecordingComplete':
115-
final audioData = call.arguments['audioData'] as Uint8List?;
116-
if (_isRecording && _activeSource == VoiceInputSource.glasses) {
117-
await _handleRecordingComplete(audioData);
118-
}
119-
return true;
120-
121-
default:
122-
return null;
123-
}
124-
}
101+
// Note: Glasses audio is collected by BluetoothReciever.voiceCollectorAI
102+
// when the mic is enabled. We get the data via getAllDataAndReset() in
103+
// _stopGlassesRecording().
125104

126105
// Note: Wake word detection is handled by AIService which listens to
127106
// WakeWordService.eventStream and coordinates the full voice input flow.
@@ -235,19 +214,26 @@ class VoiceInputService {
235214
/// Start recording from glasses microphone
236215
Future<bool> _startGlassesRecording(Duration maxDuration) async {
237216
try {
217+
// Reset voice collector buffer before starting
218+
_bluetoothReciever.voiceCollectorAI.reset();
219+
_bluetoothReciever.voiceCollectorAI.isRecording = true;
220+
238221
// Enable microphone on glasses
239222
await _bluetoothManager.setMicrophone(true);
223+
debugPrint('VoiceInputService: Glasses mic enabled, recording started');
240224

241225
// Set up auto-stop timer
242226
Timer(maxDuration, () {
243227
if (_isRecording && _activeSource == VoiceInputSource.glasses) {
228+
debugPrint('VoiceInputService: Auto-stopping glasses recording after $maxDuration');
244229
stopRecording();
245230
}
246231
});
247232

248233
return true;
249234
} catch (e) {
250235
debugPrint('VoiceInputService: Error starting glasses recording: $e');
236+
_bluetoothReciever.voiceCollectorAI.isRecording = false;
251237
return false;
252238
}
253239
}
@@ -337,11 +323,13 @@ class VoiceInputService {
337323
final source = _activeSource;
338324
_activeSource = null;
339325

326+
// Use 'complete' status when we have audio data so AIService processes it
340327
_stateController.add(
341328
VoiceInputState(
342329
isRecording: false,
343330
source: source,
344-
status: VoiceInputStatus.stopped,
331+
status: audioData != null ? VoiceInputStatus.complete : VoiceInputStatus.stopped,
332+
audioData: audioData,
345333
),
346334
);
347335

@@ -356,18 +344,33 @@ class VoiceInputService {
356344
/// Stop glasses recording
357345
Future<Uint8List?> _stopGlassesRecording() async {
358346
try {
347+
// Stop recording and disable mic
348+
_bluetoothReciever.voiceCollectorAI.isRecording = false;
359349
await _bluetoothManager.setMicrophone(false);
360350

361-
// Get recorded audio from native code
362-
final result = await _glassesAudioChannel.invokeMethod(
363-
'getRecordedAudio',
364-
);
365-
if (result is Uint8List) {
366-
return result;
351+
// Get all collected voice data (LC3 encoded)
352+
final lc3Data = await _bluetoothReciever.voiceCollectorAI.getAllDataAndReset();
353+
354+
if (lc3Data.isEmpty) {
355+
debugPrint('VoiceInputService: No voice data collected from glasses');
356+
return null;
367357
}
368-
return null;
358+
359+
debugPrint('VoiceInputService: Got ${lc3Data.length} bytes of LC3 data from glasses');
360+
361+
// Decode LC3 to PCM
362+
final pcmData = await LC3.decodeLC3(Uint8List.fromList(lc3Data));
363+
364+
if (pcmData.isEmpty) {
365+
debugPrint('VoiceInputService: LC3 decode returned empty PCM data');
366+
return null;
367+
}
368+
369+
debugPrint('VoiceInputService: Decoded to ${pcmData.length} bytes of PCM audio');
370+
return pcmData;
369371
} catch (e) {
370372
debugPrint('VoiceInputService: Error stopping glasses recording: $e');
373+
_bluetoothReciever.voiceCollectorAI.isRecording = false;
371374
return null;
372375
}
373376
}

0 commit comments

Comments
 (0)