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
|