feat(voice): add voice silence duration configuration

This commit is contained in:
cogwheel0
2025-11-05 00:33:17 +05:30
parent a3b5c4f5b7
commit 1bb2cbae25
13 changed files with 145 additions and 14 deletions

View File

@@ -26,6 +26,7 @@ class VoiceInputService {
final MicStreamRecorder _recorder = MicStreamRecorder();
final Stt _speech = Stt();
final ApiService? _api;
final Ref? _ref;
bool _isInitialized = false;
bool _isListening = false;
bool _localSttAvailable = false;
@@ -59,7 +60,9 @@ class VoiceInputService {
bool get prefersServerOnly => _preference == SttPreference.serverOnly;
bool get prefersDeviceOnly => _preference == SttPreference.deviceOnly;
VoiceInputService({ApiService? api}) : _api = api;
VoiceInputService({ApiService? api, Ref? ref})
: _api = api,
_ref = ref;
void updatePreference(SttPreference preference) {
_preference = preference;
@@ -451,7 +454,8 @@ class VoiceInputService {
_silenceTimer?.cancel();
_silenceTimer = null;
} else if (_hasDetectedSpeech && _silenceTimer == null) {
_silenceTimer = Timer(const Duration(milliseconds: 800), () {
final silenceDuration = _ref?.read(appSettingsProvider).voiceSilenceDuration ?? 2000;
_silenceTimer = Timer(Duration(milliseconds: silenceDuration), () {
if (_isListening && _usingServerStt) {
unawaited(_stopListening());
}
@@ -652,7 +656,7 @@ class VoiceInputService {
final voiceInputServiceProvider = Provider<VoiceInputService>((ref) {
final api = ref.watch(apiServiceProvider);
final service = VoiceInputService(api: api);
final service = VoiceInputService(api: api, ref: ref);
final currentSettings = ref.read(appSettingsProvider);
service.updatePreference(currentSettings.sttPreference);
ref.listen<AppSettings>(appSettingsProvider, (previous, next) {

View File

@@ -684,6 +684,83 @@ class AppCustomizationPage extends ConsumerWidget {
),
),
],
if (settings.sttPreference == SttPreference.serverOnly ||
(settings.sttPreference == SttPreference.auto &&
serverAvailable)) ...[
const SizedBox(height: Spacing.md),
const Divider(),
const SizedBox(height: Spacing.md),
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l10n.sttSilenceDuration,
style: theme.bodyMedium?.copyWith(
color: theme.sidebarForeground,
fontWeight: FontWeight.w600,
) ??
TextStyle(
color: theme.sidebarForeground,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: Spacing.xs),
Text(
'${settings.voiceSilenceDuration}ms',
style: theme.bodySmall?.copyWith(
color: theme.sidebarForeground
.withValues(alpha: 0.7),
) ??
TextStyle(
color: theme.sidebarForeground
.withValues(alpha: 0.7),
fontSize: 12,
),
),
],
),
),
Text(
'${(settings.voiceSilenceDuration / 1000).toStringAsFixed(1)}s',
style: theme.bodyMedium?.copyWith(
color: theme.buttonPrimary,
fontWeight: FontWeight.w600,
) ??
TextStyle(
color: theme.buttonPrimary,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
],
),
const SizedBox(height: Spacing.sm),
Slider(
value: settings.voiceSilenceDuration.toDouble(),
min: 300,
max: 3000,
divisions: 27,
activeColor: theme.buttonPrimary,
inactiveColor: theme.cardBorder.withValues(alpha: 0.4),
onChanged: (value) {
notifier.setVoiceSilenceDuration(value.round());
},
),
Text(
l10n.sttSilenceDurationDescription,
style: theme.bodySmall?.copyWith(
color: theme.sidebarForeground.withValues(alpha: 0.7),
) ??
TextStyle(
color: theme.sidebarForeground.withValues(alpha: 0.7),
fontSize: 12,
),
),
],
],
),
),