FlyBrain-Lab / src /tools /audio.py
timfromhcs's picture
FlyBrain V5: src/tools/audio.py (optional-dep guards, v1, backup)
5a10ec4 verified
Raw
History Blame Contribute Delete
2.78 kB
import os
import numpy as np
try:
import soundfile as sf
except ImportError: # minimal hosts (e.g. HF Space): audio input disabled
sf = None
from typing import Dict, Any, Optional
from src.tools.base import ToolConnector
class ListenAudioConnector(ToolConnector):
def __init__(self):
super().__init__(
name="listen_audio",
description="Listens to audio input from WAV file or microphone, computing VAD and 64-dimensional acoustic features.",
timeout_sec=10.0
)
@property
def input_schema(self) -> Dict[str, Any]:
return {
"type": "object",
"properties": {
"audio_path": {"type": "string"},
"synthetic_freq_hz": {"type": "number"}
}
}
@property
def output_schema(self) -> Dict[str, Any]:
return {
"type": "object",
"properties": {
"features_vector": {"type": "array", "items": {"type": "number"}},
"rms_energy": {"type": "number"},
"voice_active": {"type": "boolean"},
"duration_sec": {"type": "number"}
},
"required": ["features_vector", "rms_energy", "voice_active", "duration_sec"]
}
def _execute(self, params: Dict[str, Any], execution_id: str) -> Dict[str, Any]:
audio_path = params.get("audio_path")
if audio_path and os.path.exists(audio_path):
if sf is None:
raise RuntimeError("soundfile unavailable on this host: "
"audio file input disabled")
data, sr = sf.read(audio_path)
if data.ndim > 1:
data = data.mean(axis=1)
else:
# Generate synthetic tone / pulse
freq = float(params.get("synthetic_freq_hz", 440.0))
sr = 16000
t = np.linspace(0, 1.0, sr, endpoint=False)
data = 0.5 * np.sin(2 * np.pi * freq * t)
duration = float(len(data) / sr)
rms = float(np.sqrt(np.mean(data**2)))
voice_active = bool(rms > 0.02)
# Compute FFT spectrum
fft_vals = np.abs(np.fft.rfft(data[:min(len(data), 4096)]))
# Bin spectrum into 64 frequency bands
band_size = max(1, len(fft_vals) // 64)
bands = [float(np.mean(fft_vals[i*band_size:(i+1)*band_size])) for i in range(64)]
norm_bands = np.array(bands, dtype=np.float32)
if norm_bands.max() > 0:
norm_bands = norm_bands / norm_bands.max()
return {
"features_vector": norm_bands.tolist(),
"rms_energy": round(rms, 4),
"voice_active": voice_active,
"duration_sec": round(duration, 3)
}