40 lines
1.1 KiB
Python
40 lines
1.1 KiB
Python
"""PCM helpers for Bonvoice media (from test_bonvoice/audio_utils.py)."""
|
|
|
|
import audioop
|
|
import wave
|
|
from pathlib import Path
|
|
|
|
TARGET_RATE = 8000
|
|
CHUNK_SIZE = 320
|
|
|
|
|
|
def wav_to_pcm8000_mono(path: str | Path) -> bytes:
|
|
path = Path(path)
|
|
with wave.open(str(path), "rb") as wf:
|
|
channels = wf.getnchannels()
|
|
sample_width = wf.getsampwidth()
|
|
frame_rate = wf.getframerate()
|
|
pcm = wf.readframes(wf.getnframes())
|
|
|
|
if sample_width != 2:
|
|
raise ValueError(f"Expected 16-bit audio, got {sample_width * 8}-bit")
|
|
|
|
if channels == 2:
|
|
pcm = audioop.tomono(pcm, 2, 0.5, 0.5)
|
|
elif channels != 1:
|
|
raise ValueError(f"Unsupported channel count: {channels}")
|
|
|
|
if frame_rate != TARGET_RATE:
|
|
pcm, _ = audioop.ratecv(pcm, 2, 1, frame_rate, TARGET_RATE, None)
|
|
|
|
return pcm
|
|
|
|
|
|
def chunk_pcm(pcm: bytes, chunk_size: int = CHUNK_SIZE) -> list[bytes]:
|
|
chunks: list[bytes] = []
|
|
for i in range(0, len(pcm), chunk_size):
|
|
chunk = pcm[i : i + chunk_size]
|
|
if len(chunk) < chunk_size:
|
|
chunk = chunk + b"\x00" * (chunk_size - len(chunk))
|
|
chunks.append(chunk)
|
|
return chunks
|