Spaces:
Running
Running
| 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 | |
| ) | |
| def input_schema(self) -> Dict[str, Any]: | |
| return { | |
| "type": "object", | |
| "properties": { | |
| "audio_path": {"type": "string"}, | |
| "synthetic_freq_hz": {"type": "number"} | |
| } | |
| } | |
| 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) | |
| } | |