mirror of
https://github.com/R0m1k3/EveFlow.git
synced 2026-10-17 20:28:43 +02:00
feat: local offline voice models (sherpa-onnx) with model manager and wake word (v2.1.0)
- electron/voice: model catalog (Whisper base/small/turbo, SenseVoice, Kokoro v1.0, Piper fr), streaming tar.bz2 downloader with progress, sherpa-onnx worker running in an Electron utilityProcess (transcribe / synthesize / status / unload), IPC + bridge. - Renderer: 'local' providers for STT and TTS, Settings → Modèles locaux (download, progress, delete, activate), speaker selection, hands-free wake word with a tolerant matcher (accents, punctuation, edit distance) and attention window after a bare wake word. - Packaging: native addon and worker unpacked from the asar; release 2.1.0. Validated on Linux: Kokoro (fr) → Whisper base round trip, Piper (fr) → Whisper round trip. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Wn5VX9HNbJ7N54hR24u9Y
This commit is contained in:
22 files changed
+1361
-16
No files matched your search
@@ -11,6 +11,15 @@ import type {
|
||||
WebhookStatus,
|
||||
WindowMode
|
||||
} from './ipc';
|
||||
import type {
|
||||
SynthesizeRequest,
|
||||
SynthesizeResult,
|
||||
TranscribeRequest,
|
||||
TranscribeResult,
|
||||
VoiceDownloadProgress,
|
||||
VoiceEngineStatus,
|
||||
VoiceModelStatus
|
||||
} from './voice';
|
||||
|
||||
export type Unsubscribe = () => void;
|
||||
|
||||
@@ -51,4 +60,15 @@ export interface EveFlowBridge {
|
||||
hotkeys: {
|
||||
on: (cb: (event: HotkeyEvent) => void) => Unsubscribe;
|
||||
};
|
||||
voice: {
|
||||
status: () => Promise<VoiceEngineStatus>;
|
||||
listModels: () => Promise<VoiceModelStatus[]>;
|
||||
downloadModel: (id: string) => Promise<VoiceModelStatus>;
|
||||
cancelDownload: (id: string) => Promise<boolean>;
|
||||
removeModel: (id: string) => Promise<VoiceModelStatus[]>;
|
||||
onProgress: (cb: (progress: VoiceDownloadProgress) => void) => Unsubscribe;
|
||||
transcribe: (req: TranscribeRequest) => Promise<TranscribeResult>;
|
||||
synthesize: (req: SynthesizeRequest) => Promise<SynthesizeResult>;
|
||||
unload: (id?: string) => Promise<unknown>;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/** Local voice engine contract (sherpa-onnx in a utility process). Shared by main and renderer. */
|
||||
|
||||
export type VoiceModelKind = 'stt' | 'tts';
|
||||
export type VoiceEngineKind = 'whisper' | 'sense-voice' | 'nemo-transducer' | 'kokoro' | 'piper';
|
||||
|
||||
export interface VoiceSpeaker {
|
||||
id: number;
|
||||
name: string;
|
||||
lang: string;
|
||||
}
|
||||
|
||||
export interface VoiceModelSpec {
|
||||
id: string;
|
||||
kind: VoiceModelKind;
|
||||
engine: VoiceEngineKind;
|
||||
name: string;
|
||||
description: string;
|
||||
languages: string[];
|
||||
sizeMb: number;
|
||||
url: string;
|
||||
/** Folder created by the archive (files are referenced relative to it). */
|
||||
dir: string;
|
||||
/** Files that must exist once installed. */
|
||||
files: string[];
|
||||
speakers?: VoiceSpeaker[];
|
||||
sampleRate?: number;
|
||||
recommended?: boolean;
|
||||
}
|
||||
|
||||
export interface VoiceModelStatus extends VoiceModelSpec {
|
||||
installed: boolean;
|
||||
downloading: boolean;
|
||||
installedBytes: number;
|
||||
}
|
||||
|
||||
export interface VoiceDownloadProgress {
|
||||
id: string;
|
||||
phase: 'download' | 'extract' | 'done' | 'error' | 'cancelled';
|
||||
received: number;
|
||||
total: number;
|
||||
percent: number;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface TranscribeRequest {
|
||||
modelId: string;
|
||||
wav: Uint8Array;
|
||||
language: string; // 'fr', 'en', 'auto'
|
||||
}
|
||||
|
||||
export interface TranscribeResult {
|
||||
text: string;
|
||||
language?: string;
|
||||
durationMs: number;
|
||||
audioSec: number;
|
||||
}
|
||||
|
||||
export interface SynthesizeRequest {
|
||||
modelId: string;
|
||||
text: string;
|
||||
speaker: number;
|
||||
speed: number;
|
||||
}
|
||||
|
||||
export interface SynthesizeResult {
|
||||
wav: Uint8Array;
|
||||
sampleRate: number;
|
||||
durationMs: number;
|
||||
audioSec: number;
|
||||
}
|
||||
|
||||
export interface VoiceEngineStatus {
|
||||
available: boolean;
|
||||
error?: string;
|
||||
version?: string;
|
||||
loaded: string[];
|
||||
modelsDir: string;
|
||||
}
|
||||
|
||||
export const VOICE_IPC = {
|
||||
status: 'voice:status',
|
||||
modelsList: 'voice:models:list',
|
||||
modelsDownload: 'voice:models:download',
|
||||
modelsCancel: 'voice:models:cancel',
|
||||
modelsRemove: 'voice:models:remove',
|
||||
modelsProgress: 'voice:models:progress',
|
||||
transcribe: 'voice:transcribe',
|
||||
synthesize: 'voice:synthesize',
|
||||
unload: 'voice:unload'
|
||||
} as const;
|
||||
Reference in new issue
Block a user