Merge pull request 'Release v26.04.15.1 — Dynamic voice silence threshold' (#24) from dev into main
This commit was merged in pull request #24.
This commit is contained in:
@@ -99,10 +99,23 @@ class VoiceNotifier extends Notifier<VoiceState> {
|
||||
StreamSubscription<Amplitude>? _amplitudeSubscription;
|
||||
|
||||
// Recording / silence detection
|
||||
//
|
||||
// Silence threshold is dynamic: we track the session peak dBFS and treat
|
||||
// "silent" as "current level is at least _dropFromPeakDb below peak."
|
||||
// This auto-calibrates to whatever mic + room the user is on rather than
|
||||
// assuming a fixed ambient level. Until the peak climbs above
|
||||
// _dynamicArmDb we fall back to a conservative static threshold so a
|
||||
// quiet room doesn't spin forever. A grace period at the start of the
|
||||
// recording gives the user time to begin speaking before silence checks
|
||||
// arm.
|
||||
int _recordingStartMs = 0;
|
||||
int _silenceMs = 0;
|
||||
static const _silenceThresholdDb = -40.0;
|
||||
static const _silenceDurationMs = 1500;
|
||||
double _peakDb = -100.0;
|
||||
static const _fallbackThresholdDb = -35.0;
|
||||
static const _dropFromPeakDb = 15.0;
|
||||
static const _dynamicArmDb = -20.0;
|
||||
static const _graceMs = 1500;
|
||||
static const _silenceDurationMs = 2000;
|
||||
static const _minRecordingMs = 300;
|
||||
|
||||
// Voice mode callbacks
|
||||
@@ -236,6 +249,7 @@ class VoiceNotifier extends Notifier<VoiceState> {
|
||||
if (!state.voiceModeActive) return;
|
||||
|
||||
_silenceMs = 0;
|
||||
_peakDb = -100.0;
|
||||
_recordingStartMs = DateTime.now().millisecondsSinceEpoch;
|
||||
state = state.copyWith(mode: VoiceMode.recording);
|
||||
|
||||
@@ -285,13 +299,22 @@ class VoiceNotifier extends Notifier<VoiceState> {
|
||||
if ((norm - state.amplitude).abs() > 0.02) {
|
||||
state = state.copyWith(amplitude: norm);
|
||||
}
|
||||
if (db > _peakDb) _peakDb = db;
|
||||
}
|
||||
|
||||
final elapsed =
|
||||
DateTime.now().millisecondsSinceEpoch - _recordingStartMs;
|
||||
// Grace period at the start — let the user begin speaking before we
|
||||
// start silence-counting. Also suppresses any false triggers while
|
||||
// the native recorder is still warming up.
|
||||
if (elapsed < _graceMs) return;
|
||||
if (elapsed < _minRecordingMs) return;
|
||||
|
||||
final isSilent = !validDb || db < _silenceThresholdDb;
|
||||
// Dynamic threshold once we've seen real speech; static fallback
|
||||
// before that so a dead-silent session doesn't spin forever.
|
||||
final threshold =
|
||||
_peakDb > _dynamicArmDb ? _peakDb - _dropFromPeakDb : _fallbackThresholdDb;
|
||||
final isSilent = !validDb || db < threshold;
|
||||
|
||||
if (isSilent) {
|
||||
_silenceMs += 200;
|
||||
|
||||
Reference in New Issue
Block a user