Reputation: 25
I'm developing a Flutter application that implements a conversation feature using audio recording and playback. I'm encountering an issue where the audio playback fails after navigating away from and back to the page containing this feature. Here's the behavior I'm observing:
When I leave the page, I call the dispose() method on my ConversationHandler class. Upon returning to the page and initiating the first conversation, I receive the following error:
🆘 Error playing audio: Connection aborted
I/flutter (17214): 🎙️ Switching to listening mode
I/flutter (17214): 🆘 Error playing audio: Connection aborted
I/flutter (17214): 🎙️ Switching to listening mode
I/flutter (17214): 🆘 Error playing audio: Connection aborted
I/flutter (17214): 🎙️ Switching to listening mode
Despite this error, the first conversation attempt seems to work. However, on subsequent attempts, the application gets stuck in the "Listening" state and doesn't progress.
On subsequent attempts, the application appears to get stuck in the "Listening" state. I observe the following logs:
🎵 Sending audio chunk of size: 2560
I/flutter (17214): 🎵 Sending audio chunk of size: 2560
I/flutter (17214): 🎵 Sending audio chunk of size: 2560
I/flutter (17214): 🔄 Raw message from Deepgram: {"type":"Results","channel_index":[0,1],"duration":3.02,"start":0.0,"is_final":true,"speech_final":false,"channel":{"alternatives":[{"transcript":"","confidence":0.0,"words":[]}]},"metadata":{"request_id":"03448721-acf0-4eac-a1c7-d1fcc67f97ba","model_info":{"name":"general","version":"2024-01-26.8851","arch":"base"},"model_uuid":"1ed36bac-f71c-4f3f-a31f-02fd6525c489"},"from_finalize":false}
I/flutter (17214): 🔄 Decoded response: {type: Results, channel_index: [0, 1], duration: 3.02, start: 0.0, is_final: true, speech_final: false, channel: {alternatives: [{transcript: , confidence: 0.0, words: []}]}, metadata: {request_id: 03448721-acf0-4eac-a1c7-d1fcc67f97ba, model_info: {name: general, version: 2024-01-26.8851, arch: base}, model_uuid: 1ed36bac-f71c-4f3f-a31f-02fd6525c489}, from_finalize: false}
Additionally, I sometimes observe the following behavior in the logs:
🎵 Sending audio chunk of size: 2560
I/flutter (17214): 🎵 Sending audio chunk of size: 2560
I/flutter (17214): 🏁 Deepgram WebSocket connection closed
I/flutter (17214): 🎵 Sending audio chunk of size: 2560
The Deepgram WebSocket connection appears to be closing unexpectedly, but the app continues to send audio chunks.
I'm using the just_audio package for audio playback. Here are the relevant parts of my ConversationHandler class:
class ConversationHandler {
// ... (other properties)
late AudioRecorder _recorder;
late IOWebSocketChannel _deepgramChannel;
late IO.Socket _conversationSocket;
late IO.Socket _audioSocket;
late AudioPlayer _audioPlayer;
ConversationState _state = ConversationState.idle;
String _currentTranscript = '';
late Stream<Uint8List> _audioStream;
StreamSubscription<Uint8List>? _audioStreamSubscription;
bool _isInitialized = false;
// ... (constructor and other methods)
Future<void> startConversation() async {
if (!_isInitialized) {
print('❗ ConversationHandler not initialized');
_updateStatus("Oops! Something went wrong. Please try again.");
return;
}
if (_state != ConversationState.idle) {
print('❗ Cannot start conversation: not in idle state');
return;
}
_updateStatus("Listening...");
await _switchToListeningMode();
}
Future<void> _switchToListeningMode() async {
print('🎙️ Switching to listening mode');
_state = ConversationState.listening;
try {
bool hasPermission = await _recorder.hasPermission();
if (!hasPermission) {
print('❌ Microphone permission not granted');
_updateStatus("Microphone access needed. Please grant permission.");
return;
}
_audioStream = await _recorder.startStream(const RecordConfig(
encoder: AudioEncoder.pcm16bits,
numChannels: 1,
sampleRate: 16000,
));
print('🎙️ Audio stream started');
_initializeDeepgram();
_audioStreamSubscription = _audioStream.listen(
(Uint8List audioChunk) {
if (_state == ConversationState.listening) {
if (audioChunk.isNotEmpty) {
print('🎵 Sending audio chunk of size: ${audioChunk.length}');
_deepgramChannel.sink.add(audioChunk);
_lastAudioDetected = DateTime.now();
} else {
print('❗ Audio chunk is empty');
}
}
},
onError: (error) {
print('🆘 Error in audio stream: $error');
_updateStatus("Oops! Something went wrong. Please try again.");
},
);
} catch (e) {
print('🆘 Error starting audio stream: $e');
_updateStatus("Oops! Something went wrong. Please try again.");
}
}
void _initializeDeepgram() {
final deepgramUrl = 'wss://api.deepgram.com/v1/listen?encoding=linear16&sample_rate=16000&channels=1&endpointing=500';
try {
_deepgramChannel = IOWebSocketChannel.connect(
Uri.parse(deepgramUrl),
headers: {
'Authorization': 'Token $deepgramApiKey',
},
);
print('🌐 Deepgram WebSocket connected');
} catch (e) {
print('🆘 Error connecting to Deepgram WebSocket: $e');
_updateStatus("Oops! Something went wrong. Please try again.");
return;
}
_deepgramChannel.stream.listen(
(message) {
try {
final response = jsonDecode(message);
print('🔄 Raw message from Deepgram: $message');
print('🔄 Decoded response: $response');
if (response['type'] == 'Results' &&
response['channel'] != null &&
response['channel']['alternatives'] != null &&
response['channel']['alternatives'].isNotEmpty) {
final isFinal = response['is_final'] ?? false;
final transcript = response['channel']['alternatives'][0]['transcript'] as String? ?? '';
if (isFinal && transcript.isNotEmpty) {
_currentTranscript += ' ' + transcript;
print('📝 Current transcript: $_currentTranscript');
if (response['speech_final'] == true) {
_handlePause();
}
}
}
} catch (e) {
print('🆘 Error processing Deepgram response: $e');
_updateStatus("Oops! Something went wrong. Please try again.");
}
},
onError: (error) {
print('🆘 Deepgram WebSocket error: $error');
_updateStatus("Oops! Something went wrong. Please try again.");
},
onDone: () {
print('🏁 Deepgram WebSocket connection closed');
},
);
}
Future<void> _playAudio(Uint8List audioBuffer) async {
print('Playing audiooo');
try {
final tempDir = await getTemporaryDirectory();
print('tempr gotten');
final tempFile = File('${tempDir.path}/temp_audio.mp3');
await tempFile.writeAsBytes(audioBuffer);
print('gotten temp fileeee');
await _audioPlayer.setFilePath(tempFile.path);
await _audioPlayer.play();
await _audioPlayer.playerStateStream.firstWhere(
(state) => state.processingState == ProcessingState.completed
);
print('🔊 Audio playback completed');
} catch (e) {
print('🆘 Error playing audio: $e');
_updateStatus("Oops! Something went wrong. Please try again.");
}
}
Future<void> stopConversation() async {
print('🛑 Stopping conversation');
_state = ConversationState.idle;
_audioStreamSubscription?.cancel();
await _recorder.stop();
_deepgramChannel.sink.close();
await _audioPlayer.stop();
_updateStatus("Conversation ended.");
}
Future<void> dispose() async {
print('♻️ Disposing ConversationHandler');
await stopConversation();
await _conversationSocket.disconnect();
await _audioSocket.disconnect();
await _audioPlayer.dispose();
}
}
My questions are:
What could be causing the "Connection aborted" error when trying to play audio after page navigation? Why does the application appear to get stuck in the "Listening" state on subsequent conversation attempts, with empty transcripts being received from Deepgram? How can I properly reinitialize or reset the audio player, recorder, and conversation handler when returning to the page to ensure consistent behavior? Why is the Deepgram WebSocket connection closing unexpectedly, and how can I ensure it remains open or properly reinitialize it when it closes?
Any insights or suggestions would be greatly appreciated. Thank you in advance for your help!
Upvotes: 0
Views: 32