feat(audio): lazy stream close for bluetooth mic latency (#747)
* feat(audio): opt-in lazy stream close for bluetooth mic latency keep the microphone stream open for 30s after recording stops to reduce latency on back-to-back transcriptions. gated behind an experimental setting (off by default) since it keeps the mic actively capturing while idle — degrading bluetooth audio quality on macOS. adds lazy_stream_close setting, tauri command, toggle in experimental settings section, and i18n strings. * fix: gate lazy close on setting in cancel_recording path cancel_recording was unconditionally calling schedule_lazy_close, keeping the mic open for 30s even when the setting was disabled. also bump close_generation on AlwaysOn→OnDemand switch to cancel any stale lazy close timers. * feat(audio): opt-in lazy stream close for bluetooth mic latency keep the microphone stream open for 30s after recording stops to reduce latency on back-to-back transcriptions. gated behind an experimental setting (off by default) since it keeps the mic actively capturing while idle — degrading bluetooth audio quality on macOS. adds lazy_stream_close setting, tauri command, toggle in experimental settings section, and i18n strings. * fix: gate lazy close on setting in cancel_recording path cancel_recording was unconditionally calling schedule_lazy_close, keeping the mic open for 30s even when the setting was disabled. also bump close_generation on AlwaysOn→OnDemand switch to cancel any stale lazy close timers. * chore: fix cargo fmt on model.rs line from main merge * fix race --------- Co-authored-by: CJ Pais <cj@cjpais.com>
This commit is contained in:
parent
095f4ac455
commit
cb32d35b9e
26 changed files with 176 additions and 10 deletions
|
|
@ -359,6 +359,7 @@ pub fn run(cli_args: CliArgs) {
|
|||
shortcut::resume_binding,
|
||||
shortcut::change_mute_while_recording_setting,
|
||||
shortcut::change_append_trailing_space_setting,
|
||||
shortcut::change_lazy_stream_close_setting,
|
||||
shortcut::change_app_language_setting,
|
||||
shortcut::change_update_checks_setting,
|
||||
shortcut::change_keyboard_implementation_setting,
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@ use crate::helpers::clamshell;
|
|||
use crate::settings::{get_settings, AppSettings};
|
||||
use crate::utils;
|
||||
use log::{debug, error, info};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
use tauri::Manager;
|
||||
|
||||
const STREAM_IDLE_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
fn set_mute(mute: bool) {
|
||||
// Expected behavior:
|
||||
// - Windows: works on most systems using standard audio drivers.
|
||||
|
|
@ -149,6 +152,7 @@ pub struct AudioRecordingManager {
|
|||
is_open: Arc<Mutex<bool>>,
|
||||
is_recording: Arc<Mutex<bool>>,
|
||||
did_mute: Arc<Mutex<bool>>,
|
||||
close_generation: Arc<AtomicU64>,
|
||||
}
|
||||
|
||||
impl AudioRecordingManager {
|
||||
|
|
@ -171,6 +175,7 @@ impl AudioRecordingManager {
|
|||
is_open: Arc::new(Mutex::new(false)),
|
||||
is_recording: Arc::new(Mutex::new(false)),
|
||||
did_mute: Arc::new(Mutex::new(false)),
|
||||
close_generation: Arc::new(AtomicU64::new(0)),
|
||||
};
|
||||
|
||||
// Always-on? Open immediately.
|
||||
|
|
@ -210,6 +215,30 @@ impl AudioRecordingManager {
|
|||
}
|
||||
}
|
||||
|
||||
fn schedule_lazy_close(&self) {
|
||||
let gen = self.close_generation.fetch_add(1, Ordering::SeqCst) + 1;
|
||||
let app = self.app_handle.clone();
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(STREAM_IDLE_TIMEOUT);
|
||||
let rm = app.state::<Arc<AudioRecordingManager>>();
|
||||
// Hold state lock across the check AND close to serialize against
|
||||
// try_start_recording, preventing a race where the stream is closed
|
||||
// under an active recording.
|
||||
let state = rm.state.lock().unwrap();
|
||||
if rm.close_generation.load(Ordering::SeqCst) == gen
|
||||
&& matches!(*state, RecordingState::Idle)
|
||||
{
|
||||
// stop_microphone_stream does not acquire the state lock,
|
||||
// so holding it here is safe (no deadlock).
|
||||
info!(
|
||||
"Closing idle microphone stream after {:?}",
|
||||
STREAM_IDLE_TIMEOUT
|
||||
);
|
||||
rm.stop_microphone_stream();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* ---------- microphone life-cycle -------------------------------------- */
|
||||
|
||||
/// Applies mute if mute_while_recording is enabled and stream is open
|
||||
|
|
@ -309,18 +338,17 @@ impl AudioRecordingManager {
|
|||
/* ---------- mode switching --------------------------------------------- */
|
||||
|
||||
pub fn update_mode(&self, new_mode: MicrophoneMode) -> Result<(), anyhow::Error> {
|
||||
let mode_guard = self.mode.lock().unwrap();
|
||||
let cur_mode = mode_guard.clone();
|
||||
let cur_mode = self.mode.lock().unwrap().clone();
|
||||
|
||||
match (cur_mode, &new_mode) {
|
||||
(MicrophoneMode::AlwaysOn, MicrophoneMode::OnDemand) => {
|
||||
if matches!(*self.state.lock().unwrap(), RecordingState::Idle) {
|
||||
drop(mode_guard);
|
||||
self.close_generation.fetch_add(1, Ordering::SeqCst);
|
||||
self.stop_microphone_stream();
|
||||
}
|
||||
}
|
||||
(MicrophoneMode::OnDemand, MicrophoneMode::AlwaysOn) => {
|
||||
drop(mode_guard);
|
||||
self.close_generation.fetch_add(1, Ordering::SeqCst);
|
||||
self.start_microphone_stream()?;
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -338,6 +366,8 @@ impl AudioRecordingManager {
|
|||
if let RecordingState::Idle = *state {
|
||||
// Ensure microphone is open in on-demand mode
|
||||
if matches!(*self.mode.lock().unwrap(), MicrophoneMode::OnDemand) {
|
||||
// Cancel any pending lazy close
|
||||
self.close_generation.fetch_add(1, Ordering::SeqCst);
|
||||
if let Err(e) = self.start_microphone_stream() {
|
||||
let msg = format!("{e}");
|
||||
error!("Failed to open microphone stream: {msg}");
|
||||
|
|
@ -364,6 +394,7 @@ impl AudioRecordingManager {
|
|||
pub fn update_selected_device(&self) -> Result<(), anyhow::Error> {
|
||||
// If currently open, restart the microphone stream to use the new device
|
||||
if *self.is_open.lock().unwrap() {
|
||||
self.close_generation.fetch_add(1, Ordering::SeqCst);
|
||||
self.stop_microphone_stream();
|
||||
self.start_microphone_stream()?;
|
||||
}
|
||||
|
|
@ -405,9 +436,13 @@ impl AudioRecordingManager {
|
|||
|
||||
*self.is_recording.lock().unwrap() = false;
|
||||
|
||||
// In on-demand mode turn the mic off again
|
||||
// In on-demand mode, close the mic (lazily if the setting is enabled)
|
||||
if matches!(*self.mode.lock().unwrap(), MicrophoneMode::OnDemand) {
|
||||
self.stop_microphone_stream();
|
||||
if get_settings(&self.app_handle).lazy_stream_close {
|
||||
self.schedule_lazy_close();
|
||||
} else {
|
||||
self.stop_microphone_stream();
|
||||
}
|
||||
}
|
||||
|
||||
// Pad if very short
|
||||
|
|
@ -445,9 +480,13 @@ impl AudioRecordingManager {
|
|||
|
||||
*self.is_recording.lock().unwrap() = false;
|
||||
|
||||
// In on-demand mode turn the mic off again
|
||||
// In on-demand mode, close the mic (lazily if the setting is enabled)
|
||||
if matches!(*self.mode.lock().unwrap(), MicrophoneMode::OnDemand) {
|
||||
self.stop_microphone_stream();
|
||||
if get_settings(&self.app_handle).lazy_stream_close {
|
||||
self.schedule_lazy_close();
|
||||
} else {
|
||||
self.stop_microphone_stream();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -589,7 +589,11 @@ impl ModelManager {
|
|||
)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to resolve GigaAM vocab path: {}", e))?;
|
||||
|
||||
info!("Resolved vocab path: {:?} (exists: {})", vocab_path, vocab_path.exists());
|
||||
info!(
|
||||
"Resolved vocab path: {:?} (exists: {})",
|
||||
vocab_path,
|
||||
vocab_path.exists()
|
||||
);
|
||||
info!("Old file: {:?} (exists: {})", old_file, old_file.exists());
|
||||
info!("New dir: {:?} (exists: {})", new_dir, new_dir.exists());
|
||||
|
||||
|
|
|
|||
|
|
@ -381,6 +381,8 @@ pub struct AppSettings {
|
|||
#[serde(default)]
|
||||
pub experimental_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub lazy_stream_close: bool,
|
||||
#[serde(default)]
|
||||
pub keyboard_implementation: KeyboardImplementation,
|
||||
#[serde(default = "default_show_tray_icon")]
|
||||
pub show_tray_icon: bool,
|
||||
|
|
@ -756,6 +758,7 @@ pub fn get_default_settings() -> AppSettings {
|
|||
append_trailing_space: false,
|
||||
app_language: default_app_language(),
|
||||
experimental_enabled: false,
|
||||
lazy_stream_close: false,
|
||||
keyboard_implementation: KeyboardImplementation::default(),
|
||||
show_tray_icon: default_show_tray_icon(),
|
||||
paste_delay_ms: default_paste_delay_ms(),
|
||||
|
|
|
|||
|
|
@ -1051,6 +1051,15 @@ pub fn change_append_trailing_space_setting(app: AppHandle, enabled: bool) -> Re
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn change_lazy_stream_close_setting(app: AppHandle, enabled: bool) -> Result<(), String> {
|
||||
let mut settings = settings::get_settings(&app);
|
||||
settings.lazy_stream_close = enabled;
|
||||
settings::write_settings(&app, settings);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn change_app_language_setting(app: AppHandle, language: String) -> Result<(), String> {
|
||||
|
|
|
|||
|
|
@ -303,6 +303,14 @@ async changeAppendTrailingSpaceSetting(enabled: boolean) : Promise<Result<null,
|
|||
else return { status: "error", error: e as any };
|
||||
}
|
||||
},
|
||||
async changeLazyStreamCloseSetting(enabled: boolean) : Promise<Result<null, string>> {
|
||||
try {
|
||||
return { status: "ok", data: await TAURI_INVOKE("change_lazy_stream_close_setting", { enabled }) };
|
||||
} catch (e) {
|
||||
if(e instanceof Error) throw e;
|
||||
else return { status: "error", error: e as any };
|
||||
}
|
||||
},
|
||||
async changeAppLanguageSetting(language: string) : Promise<Result<null, string>> {
|
||||
try {
|
||||
return { status: "ok", data: await TAURI_INVOKE("change_app_language_setting", { language }) };
|
||||
|
|
@ -787,7 +795,7 @@ async isLaptop() : Promise<Result<boolean, string>> {
|
|||
|
||||
/** user-defined types **/
|
||||
|
||||
export type AppSettings = { bindings: Partial<{ [key in string]: ShortcutBinding }>; push_to_talk: boolean; audio_feedback: boolean; audio_feedback_volume?: number; sound_theme?: SoundTheme; start_hidden?: boolean; autostart_enabled?: boolean; update_checks_enabled?: boolean; selected_model?: string; always_on_microphone?: boolean; selected_microphone?: string | null; clamshell_microphone?: string | null; selected_output_device?: string | null; translate_to_english?: boolean; selected_language?: string; overlay_position?: OverlayPosition; debug_mode?: boolean; log_level?: LogLevel; custom_words?: string[]; model_unload_timeout?: ModelUnloadTimeout; word_correction_threshold?: number; history_limit?: number; recording_retention_period?: RecordingRetentionPeriod; paste_method?: PasteMethod; clipboard_handling?: ClipboardHandling; auto_submit?: boolean; auto_submit_key?: AutoSubmitKey; post_process_enabled?: boolean; post_process_provider_id?: string; post_process_providers?: PostProcessProvider[]; post_process_api_keys?: Partial<{ [key in string]: string }>; post_process_models?: Partial<{ [key in string]: string }>; post_process_prompts?: LLMPrompt[]; post_process_selected_prompt_id?: string | null; mute_while_recording?: boolean; append_trailing_space?: boolean; app_language?: string; experimental_enabled?: boolean; keyboard_implementation?: KeyboardImplementation; show_tray_icon?: boolean; paste_delay_ms?: number; typing_tool?: TypingTool; external_script_path: string | null; custom_filler_words?: string[] | null; whisper_accelerator?: WhisperAcceleratorSetting; ort_accelerator?: OrtAcceleratorSetting; extra_recording_buffer_ms?: number }
|
||||
export type AppSettings = { bindings: Partial<{ [key in string]: ShortcutBinding }>; push_to_talk: boolean; audio_feedback: boolean; audio_feedback_volume?: number; sound_theme?: SoundTheme; start_hidden?: boolean; autostart_enabled?: boolean; update_checks_enabled?: boolean; selected_model?: string; always_on_microphone?: boolean; selected_microphone?: string | null; clamshell_microphone?: string | null; selected_output_device?: string | null; translate_to_english?: boolean; selected_language?: string; overlay_position?: OverlayPosition; debug_mode?: boolean; log_level?: LogLevel; custom_words?: string[]; model_unload_timeout?: ModelUnloadTimeout; word_correction_threshold?: number; history_limit?: number; recording_retention_period?: RecordingRetentionPeriod; paste_method?: PasteMethod; clipboard_handling?: ClipboardHandling; auto_submit?: boolean; auto_submit_key?: AutoSubmitKey; post_process_enabled?: boolean; post_process_provider_id?: string; post_process_providers?: PostProcessProvider[]; post_process_api_keys?: Partial<{ [key in string]: string }>; post_process_models?: Partial<{ [key in string]: string }>; post_process_prompts?: LLMPrompt[]; post_process_selected_prompt_id?: string | null; mute_while_recording?: boolean; append_trailing_space?: boolean; app_language?: string; experimental_enabled?: boolean; lazy_stream_close?: boolean; keyboard_implementation?: KeyboardImplementation; show_tray_icon?: boolean; paste_delay_ms?: number; typing_tool?: TypingTool; external_script_path: string | null; custom_filler_words?: string[] | null; whisper_accelerator?: WhisperAcceleratorSetting; ort_accelerator?: OrtAcceleratorSetting; extra_recording_buffer_ms?: number }
|
||||
export type AudioDevice = { index: string; name: string; is_default: boolean }
|
||||
export type AutoSubmitKey = "enter" | "ctrl_enter" | "cmd_enter"
|
||||
export type AvailableAccelerators = { whisper: string[]; ort: string[] }
|
||||
|
|
|
|||
30
src/components/settings/LazyStreamClose.tsx
Normal file
30
src/components/settings/LazyStreamClose.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ToggleSwitch } from "../ui/ToggleSwitch";
|
||||
import { useSettings } from "../../hooks/useSettings";
|
||||
|
||||
interface LazyStreamCloseProps {
|
||||
descriptionMode?: "inline" | "tooltip";
|
||||
grouped?: boolean;
|
||||
}
|
||||
|
||||
export const LazyStreamClose: React.FC<LazyStreamCloseProps> = React.memo(
|
||||
({ descriptionMode = "tooltip", grouped = false }) => {
|
||||
const { t } = useTranslation();
|
||||
const { getSetting, updateSetting, isUpdating } = useSettings();
|
||||
|
||||
const enabled = getSetting("lazy_stream_close") ?? false;
|
||||
|
||||
return (
|
||||
<ToggleSwitch
|
||||
checked={enabled}
|
||||
onChange={(enabled) => updateSetting("lazy_stream_close", enabled)}
|
||||
isUpdating={isUpdating("lazy_stream_close")}
|
||||
label={t("settings.advanced.lazyStreamClose.label")}
|
||||
description={t("settings.advanced.lazyStreamClose.description")}
|
||||
descriptionMode={descriptionMode}
|
||||
grouped={grouped}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
@ -19,6 +19,7 @@ import { ExperimentalToggle } from "../ExperimentalToggle";
|
|||
import { useSettings } from "../../../hooks/useSettings";
|
||||
import { KeyboardImplementationSelector } from "../debug/KeyboardImplementationSelector";
|
||||
import { AccelerationSelector } from "../AccelerationSelector";
|
||||
import { LazyStreamClose } from "../LazyStreamClose";
|
||||
|
||||
export const AdvancedSettings: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -64,6 +65,7 @@ export const AdvancedSettings: React.FC = () => {
|
|||
grouped={true}
|
||||
/>
|
||||
<AccelerationSelector descriptionMode="tooltip" grouped={true} />
|
||||
<LazyStreamClose descriptionMode="tooltip" grouped={true} />
|
||||
</SettingsGroup>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -223,6 +223,10 @@
|
|||
"label": "الميزات التجريبية",
|
||||
"description": ".تمكين الميزات التجريبية التي لا تزال قيد التطوير"
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "إبقاء الميكروفون مفتوحًا بين عمليات النسخ",
|
||||
"description": "يبقي بث الميكروفون مفتوحًا لمدة 30 ثانية بعد توقف التسجيل، مما يقلل التأخير عند النسخ المتتالي. قد يؤثر على جودة صوت البلوتوث أثناء التفعيل."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "تسريع Whisper",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "Experimentální funkce",
|
||||
"description": "Povolit experimentální funkce, které jsou stále ve vývoji."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "Ponechat mikrofon zapnutý mezi přepisy",
|
||||
"description": "Ponechá stream mikrofonu otevřený po dobu 30 sekund po zastavení nahrávání, čímž se sníží latence při opakovaném přepisu. Může zhoršit kvalitu zvuku Bluetooth."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Akcelerace Whisper",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "Experimentelle Funktionen",
|
||||
"description": "Experimentelle Funktionen aktivieren, die sich noch in Entwicklung befinden."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "Mikrofon zwischen Transkriptionen offen halten",
|
||||
"description": "Hält den Mikrofon-Stream nach dem Aufnahmestopp 30 Sekunden lang offen, um die Latenz bei aufeinanderfolgenden Transkriptionen zu verringern. Kann die Bluetooth-Audioqualität beeinträchtigen."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Whisper-Beschleunigung",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "Experimental Features",
|
||||
"description": "Enable experimental features that are still in development."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "Keep Mic Open Between Transcriptions",
|
||||
"description": "Keeps the microphone stream open for 30 seconds after recording stops, reducing latency for back-to-back transcriptions. May degrade Bluetooth audio quality while active."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Whisper Acceleration",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "Funciones Experimentales",
|
||||
"description": "Habilitar funciones experimentales que aún están en desarrollo."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "Mantener micrófono abierto entre transcripciones",
|
||||
"description": "Mantiene el flujo del micrófono abierto durante 30 segundos después de detener la grabación, reduciendo la latencia en transcripciones consecutivas. Puede degradar la calidad del audio Bluetooth mientras está activo."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Aceleración de Whisper",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "Fonctionnalités Expérimentales",
|
||||
"description": "Activer les fonctionnalités expérimentales encore en développement."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "Garder le micro ouvert entre les transcriptions",
|
||||
"description": "Maintient le flux du microphone ouvert pendant 30 secondes après l'arrêt de l'enregistrement, réduisant la latence pour les transcriptions consécutives. Peut dégrader la qualité audio Bluetooth."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Accélération Whisper",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "Funzionalità Sperimentali",
|
||||
"description": "Abilita le funzionalità sperimentali ancora in fase di sviluppo."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "Mantieni il microfono aperto tra le trascrizioni",
|
||||
"description": "Mantiene lo stream del microfono aperto per 30 secondi dopo l'arresto della registrazione, riducendo la latenza per trascrizioni consecutive. Potrebbe degradare la qualità audio Bluetooth."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Accelerazione Whisper",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "実験的機能",
|
||||
"description": "まだ開発中の実験的機能を有効にします。"
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "文字起こし間でマイクを開いたままにする",
|
||||
"description": "録音停止後30秒間マイクストリームを開いたままにし、連続した文字起こしの遅延を軽減します。Bluetooth音声品質に影響する場合があります。"
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Whisper アクセラレーション",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "실험적 기능",
|
||||
"description": "개발 중인 실험적 기능을 활성화합니다."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "전사 사이에 마이크 열어 두기",
|
||||
"description": "녹음 중지 후 30초 동안 마이크 스트림을 열어 두어 연속 전사 시 지연을 줄입니다. 활성화 중 블루투스 오디오 품질이 저하될 수 있습니다."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Whisper 가속",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "Funkcje Eksperymentalne",
|
||||
"description": "Włącz funkcje eksperymentalne, które są jeszcze w fazie rozwoju."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "Pozostaw mikrofon otwarty między transkrypcjami",
|
||||
"description": "Utrzymuje strumień mikrofonu otwarty przez 30 sekund po zatrzymaniu nagrywania, zmniejszając opóźnienie przy kolejnych transkrypcjach. Może pogorszyć jakość dźwięku Bluetooth."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Akceleracja Whisper",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "Recursos Experimentais",
|
||||
"description": "Ativar recursos experimentais que ainda estão em desenvolvimento."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "Manter microfone aberto entre transcrições",
|
||||
"description": "Mantém o fluxo do microfone aberto por 30 segundos após parar a gravação, reduzindo a latência em transcrições consecutivas. Pode degradar a qualidade do áudio Bluetooth enquanto ativo."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Aceleração Whisper",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "Экспериментальные функции",
|
||||
"description": "Включить экспериментальные функции, которые находятся в разработке."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "Оставлять микрофон включённым между транскрипциями",
|
||||
"description": "Оставляет поток микрофона открытым в течение 30 секунд после остановки записи, уменьшая задержку при последовательных транскрипциях. Может ухудшить качество звука Bluetooth."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Ускорение Whisper",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "Deneysel Özellikler",
|
||||
"description": "Hala geliştirme aşamasında olan deneysel özellikleri etkinleştir."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "Transkripsiyonlar arasında mikrofonu açık tut",
|
||||
"description": "Kayıt durdurulduktan sonra mikrofon akışını 30 saniye açık tutar, ardışık transkripsiyonlarda gecikmeyi azaltır. Etkinken Bluetooth ses kalitesini düşürebilir."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Whisper Hızlandırma",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "Експериментальні функції",
|
||||
"description": "Увімкнути експериментальні функції, які ще в розробці."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "Залишати мікрофон увімкненим між транскрипціями",
|
||||
"description": "Залишає потік мікрофона відкритим протягом 30 секунд після зупинки запису, зменшуючи затримку при послідовних транскрипціях. Може погіршити якість звуку Bluetooth."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Прискорення Whisper",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "Tính năng thử nghiệm",
|
||||
"description": "Bật các tính năng thử nghiệm đang trong quá trình phát triển."
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "Giữ micro mở giữa các lần phiên âm",
|
||||
"description": "Giữ luồng micro mở trong 30 giây sau khi dừng ghi âm, giảm độ trễ khi phiên âm liên tiếp. Có thể làm giảm chất lượng âm thanh Bluetooth khi đang hoạt động."
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Tăng tốc Whisper",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "實驗性功能",
|
||||
"description": "啟用仍在開發中的實驗性功能"
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "在轉錄之間保持麥克風開啟",
|
||||
"description": "錄音停止後保持麥克風串流開啟30秒,減少連續轉錄時的延遲。啟用時可能會降低藍牙音訊品質。"
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Whisper 加速",
|
||||
|
|
|
|||
|
|
@ -245,6 +245,10 @@
|
|||
"label": "实验性功能",
|
||||
"description": "启用仍在开发中的实验性功能。"
|
||||
},
|
||||
"lazyStreamClose": {
|
||||
"label": "在转录之间保持麦克风开启",
|
||||
"description": "录音停止后保持麦克风流开启30秒,减少连续转录时的延迟。激活时可能会降低蓝牙音频质量。"
|
||||
},
|
||||
"acceleration": {
|
||||
"whisper": {
|
||||
"title": "Whisper 加速",
|
||||
|
|
|
|||
|
|
@ -139,6 +139,8 @@ const settingUpdaters: {
|
|||
app_language: (value) => commands.changeAppLanguageSetting(value as string),
|
||||
experimental_enabled: (value) =>
|
||||
commands.changeExperimentalEnabledSetting(value as boolean),
|
||||
lazy_stream_close: (value) =>
|
||||
commands.changeLazyStreamCloseSetting(value as boolean),
|
||||
show_tray_icon: (value) =>
|
||||
commands.changeShowTrayIconSetting(value as boolean),
|
||||
whisper_accelerator: (value) =>
|
||||
|
|
|
|||
Loading…
Reference in a new issue