feat: wakeword — OpenWakeWord subprocess with hey_jarvis model
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
71f7341974
commit
0a2154ddbd
|
|
@ -0,0 +1,44 @@
|
|||
const { spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
let wakewordProcess = null;
|
||||
let running = false;
|
||||
|
||||
function startWakeword({ onWake }) {
|
||||
if (running) return;
|
||||
running = true;
|
||||
|
||||
const scriptPath = path.join(__dirname, '../python/wakeword.py');
|
||||
|
||||
wakewordProcess = spawn('python3', [scriptPath], {
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
wakewordProcess.stdout.on('data', (data) => {
|
||||
if (data.toString().trim() === 'WAKE') {
|
||||
onWake();
|
||||
}
|
||||
});
|
||||
|
||||
wakewordProcess.stderr.on('data', (data) => {
|
||||
console.error('[Wakeword]', data.toString().trim());
|
||||
});
|
||||
|
||||
wakewordProcess.on('close', (code) => {
|
||||
running = false;
|
||||
wakewordProcess = null;
|
||||
if (code !== 0) {
|
||||
setTimeout(() => startWakeword({ onWake }), 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function stopWakeword() {
|
||||
if (wakewordProcess) {
|
||||
wakewordProcess.kill();
|
||||
wakewordProcess = null;
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { startWakeword, stopWakeword };
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import numpy as np
|
||||
import pyaudio
|
||||
from openwakeword.model import Model
|
||||
|
||||
CHUNK = 1280 # 80ms bei 16kHz — von openwakeword erwartet
|
||||
RATE = 16000
|
||||
THRESHOLD = 0.5
|
||||
|
||||
def main():
|
||||
model = Model(wakeword_models=["hey_jarvis"])
|
||||
|
||||
pa = pyaudio.PyAudio()
|
||||
stream = pa.open(
|
||||
rate=RATE,
|
||||
channels=1,
|
||||
format=pyaudio.paInt16,
|
||||
input=True,
|
||||
frames_per_buffer=CHUNK,
|
||||
)
|
||||
|
||||
print("Wakeword aktiv", flush=True)
|
||||
|
||||
try:
|
||||
while True:
|
||||
data = stream.read(CHUNK, exception_on_overflow=False)
|
||||
audio = np.frombuffer(data, dtype=np.int16)
|
||||
prediction = model.predict(audio)
|
||||
for mdl, score in prediction.items():
|
||||
if score > THRESHOLD:
|
||||
print("WAKE", flush=True)
|
||||
model.reset()
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
stream.stop_stream()
|
||||
stream.close()
|
||||
pa.terminate()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue