73 lines
1.9 KiB
Python
73 lines
1.9 KiB
Python
import queue
|
|
from collections import deque
|
|
|
|
import numpy as np
|
|
import sounddevice as sd
|
|
import speech_recognition as sr
|
|
|
|
RATE = 16000
|
|
CHUNK = RATE // 20 # 50 ms
|
|
CHUNK_S = CHUNK / RATE
|
|
|
|
|
|
class NothingHeard(Exception):
|
|
pass
|
|
|
|
|
|
def _rms(block):
|
|
return float(np.sqrt(np.mean(block.astype(np.float32) ** 2)))
|
|
|
|
|
|
def record(wait_for_speech=5.0, max_len=10.0, trailing_silence=0.9):
|
|
"""Record from the default mic until the speaker pauses. Returns raw int16 PCM."""
|
|
q = queue.Queue()
|
|
noise = []
|
|
preroll = deque(maxlen=6)
|
|
frames = []
|
|
quiet = elapsed = 0.0
|
|
|
|
def on_audio(data, frames_, time_, status):
|
|
q.put(data[:, 0].copy())
|
|
|
|
with sd.InputStream(samplerate=RATE, channels=1, dtype="int16",
|
|
blocksize=CHUNK, callback=on_audio):
|
|
while elapsed < max_len:
|
|
block = q.get(timeout=2)
|
|
elapsed += CHUNK_S
|
|
level = _rms(block)
|
|
|
|
if len(noise) < 6:
|
|
noise.append(level)
|
|
preroll.append(block)
|
|
continue
|
|
threshold = max(float(np.median(noise)) * 2.5, 250.0)
|
|
|
|
if not frames:
|
|
if level < threshold:
|
|
preroll.append(block)
|
|
if elapsed > wait_for_speech:
|
|
raise NothingHeard()
|
|
continue
|
|
frames.extend(preroll)
|
|
|
|
frames.append(block)
|
|
quiet = quiet + CHUNK_S if level < threshold else 0.0
|
|
if quiet > trailing_silence:
|
|
break
|
|
|
|
if not frames:
|
|
raise NothingHeard()
|
|
return np.concatenate(frames).tobytes()
|
|
|
|
|
|
def transcribe(pcm, language):
|
|
audio = sr.AudioData(pcm, RATE, 2)
|
|
try:
|
|
return sr.Recognizer().recognize_google(audio, language=language)
|
|
except sr.UnknownValueError:
|
|
raise NothingHeard()
|
|
|
|
|
|
def listen(language):
|
|
return transcribe(record(), language)
|