75 lines
2.1 KiB
Python
75 lines
2.1 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
"""
|
|||
|
|
AudioRecorder – minimaler Recorder mit sounddevice (16kHz mono WAV).
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import os
|
|||
|
|
import tempfile
|
|||
|
|
import wave
|
|||
|
|
|
|||
|
|
|
|||
|
|
class AudioRecorder:
|
|||
|
|
"""
|
|||
|
|
Minimaler Recorder mit sounddevice.
|
|||
|
|
Speichert als 16kHz mono WAV (16-bit PCM).
|
|||
|
|
"""
|
|||
|
|
def __init__(self, samplerate=16000, channels=1):
|
|||
|
|
self.samplerate = samplerate
|
|||
|
|
self.channels = channels
|
|||
|
|
self._stream = None
|
|||
|
|
self._frames = []
|
|||
|
|
self._recording = False
|
|||
|
|
|
|||
|
|
def start(self):
|
|||
|
|
try:
|
|||
|
|
import sounddevice as sd
|
|||
|
|
except Exception as e:
|
|||
|
|
raise RuntimeError(
|
|||
|
|
"Python-Paket 'sounddevice' fehlt.\n\n"
|
|||
|
|
"Installiere es mit:\n"
|
|||
|
|
" py -3.11 -m pip install sounddevice\n\n"
|
|||
|
|
f"Details: {e}"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
self._frames = []
|
|||
|
|
self._recording = True
|
|||
|
|
|
|||
|
|
def callback(indata, frames, time_info, status):
|
|||
|
|
if self._recording:
|
|||
|
|
self._frames.append(indata.copy())
|
|||
|
|
|
|||
|
|
self._stream = sd.InputStream(
|
|||
|
|
samplerate=self.samplerate,
|
|||
|
|
channels=self.channels,
|
|||
|
|
callback=callback,
|
|||
|
|
dtype="float32",
|
|||
|
|
blocksize=0,
|
|||
|
|
)
|
|||
|
|
self._stream.start()
|
|||
|
|
|
|||
|
|
def stop_and_save_wav(self) -> str:
|
|||
|
|
if not self._stream:
|
|||
|
|
raise RuntimeError("Recorder wurde nicht gestartet.")
|
|||
|
|
self._recording = False
|
|||
|
|
self._stream.stop()
|
|||
|
|
self._stream.close()
|
|||
|
|
self._stream = None
|
|||
|
|
|
|||
|
|
if not self._frames:
|
|||
|
|
raise RuntimeError("Keine Audio-Daten aufgenommen (leer).")
|
|||
|
|
|
|||
|
|
import numpy as np
|
|||
|
|
audio = np.concatenate(self._frames, axis=0)
|
|||
|
|
audio = np.clip(audio, -1.0, 1.0)
|
|||
|
|
pcm16 = (audio * 32767.0).astype(np.int16)
|
|||
|
|
|
|||
|
|
fd, path = tempfile.mkstemp(suffix=".wav", prefix="kg_rec_")
|
|||
|
|
os.close(fd)
|
|||
|
|
with wave.open(path, "wb") as wf:
|
|||
|
|
wf.setnchannels(self.channels)
|
|||
|
|
wf.setsampwidth(2)
|
|||
|
|
wf.setframerate(self.samplerate)
|
|||
|
|
wf.writeframes(pcm16.tobytes())
|
|||
|
|
|
|||
|
|
return path
|