refactor: remove server audio transcription and related fallback logic, retaining only on-device speech-to-text functionality

This commit is contained in:
cogwheel0
2025-08-25 20:56:33 +05:30
parent fa9fa8dd1b
commit ac21ec6493
4 changed files with 56 additions and 355 deletions

View File

@@ -3,8 +3,7 @@ import 'package:record/record.dart';
import 'package:flutter/widgets.dart';
import 'dart:async';
import 'dart:io' show Platform;
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
// Removed path imports as server transcription fallback was removed
import 'package:stts/stts.dart';
// Lightweight replacement for previous stt.LocaleName used across the UI
@@ -175,16 +174,9 @@ class VoiceInputService {
try {
final isStillAvailable = await _speech.isSupported();
if (!isStillAvailable && _isListening) {
// speech recognition no longer available, fallback to recording
// Speech recognition no longer available; stop listening
_localSttAvailable = false;
// Restart with fallback method
_startRecordingProxyIntensity();
_autoStopTimer?.cancel();
_autoStopTimer = Timer(const Duration(seconds: 30), () {
if (_isListening) {
_stopListening();
}
});
_stopListening();
return;
}
} catch (e) {
@@ -218,24 +210,17 @@ class VoiceInputService {
}
// Start recognition (no await blocking the sync flow)
_speech.start(SttRecognitionOptions(punctuation: true)).catchError((_) {
// fallback to recording
// On-device STT failed; stop listening entirely as server transcription is removed
_localSttAvailable = false;
_startRecordingProxyIntensity();
_stopListening();
});
} catch (e) {
_localSttAvailable = false;
_startRecordingProxyIntensity();
_stopListening();
}
} else {
// Fallback: record audio and signal file path for server transcription
// Local STT not available, falling back to recording
_startRecordingProxyIntensity();
_autoStopTimer?.cancel();
_autoStopTimer = Timer(const Duration(seconds: 30), () {
if (_isListening) {
_stopListening();
}
});
// No local STT available; stop immediately since server transcription is removed
_stopListening();
}
return _textStreamController!.stream;
@@ -262,9 +247,6 @@ class VoiceInputService {
_sttStateSub?.cancel();
} catch (_) {}
_sttStateSub = null;
} else {
// Also stop recorder if active
await _stopRecording();
}
_autoStopTimer?.cancel();
@@ -284,84 +266,12 @@ class VoiceInputService {
void dispose() {
stopListening();
_stopRecording(force: true);
try {
_speech.dispose().catchError((_) {});
} catch (_) {}
}
// --- Recording and intensity proxy for server transcription path ---
Future<void> _startRecordingProxyIntensity() async {
try {
final hasMic = await _recorder.hasPermission();
if (!hasMic) {
_textStreamController?.addError('Microphone permission not granted');
_stopListening();
return;
}
// Start recording in a portable format (WAV/PCM) for best compatibility with server
final tmpDir = await getTemporaryDirectory();
final filePath = p.join(
tmpDir.path,
'conduit_voice_${DateTime.now().millisecondsSinceEpoch}.wav',
);
await _recorder.start(
const RecordConfig(
encoder: AudioEncoder.wav,
numChannels: 1,
sampleRate: 16000,
bitRate: 128000,
),
path: filePath,
);
// recording started at filePath
// Drive intensity from amplitude stream and detect silence
// Consider amplitude less than threshold as silence; stop after ~3s of continuous silence
const silenceThresholdDb = -45.0; // dBFS threshold
const silenceWindow = Duration(seconds: 3);
DateTime lastNonSilent = DateTime.now();
_ampSub = _recorder
.onAmplitudeChanged(const Duration(milliseconds: 125))
.listen((amp) {
if (!_isListening) return;
// Normalize peak power (dBFS) into 0-10 bar scale
final db = amp.current;
// Map dB [-60..0] -> [0..10]
final clamped = db.clamp(-60.0, 0.0);
final norm = ((clamped + 60.0) / 60.0) * 10.0;
_intensityController?.add(norm.round().clamp(0, 10));
if (db > silenceThresholdDb) {
lastNonSilent = DateTime.now();
} else {
if (DateTime.now().difference(lastNonSilent) >= silenceWindow) {
_stopListening();
}
}
});
} catch (e) {
_textStreamController?.addError('Audio recording failed: $e');
_stopListening();
}
}
Future<void> _stopRecording({bool force = false}) async {
try {
if (!await _recorder.isRecording() && !force) return;
final path = await _recorder.stop();
if (path == null) {
_textStreamController?.addError('Recording failed: no file path');
return;
}
// Hand off recorded file path to listeners as a special token; UI layer will upload for transcription
_textStreamController?.add('[[AUDIO_FILE_PATH]]:$path');
} catch (e) {
_textStreamController?.addError('Stop recording error: $e');
}
}
// Recording fallback removed; only on-device STT is supported now
// Native locales not used in server transcription mode
}