| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- """
- 音频采集模块
- 提供音频输入流管理,支持AEC回音消除
- """
- import asyncio
- import numpy as np
- import sounddevice as sd
- from typing import Callable, Optional, List
- from pathlib import Path
- class AudioListener:
- """音频监听器协议"""
- def on_audio_data(self, audio_data: np.ndarray) -> None:
- """接收音频数据的回调"""
- ...
- class AudioCapture:
- """音频采集器 - 支持AEC回音消除"""
- def __init__(self, sample_rate: int = 16000, channels: int = 1, device_id: Optional[int] = None, enable_aec: bool = False):
- """
- 初始化音频采集器
- Args:
- sample_rate: 采样率
- channels: 声道数
- device_id: 设备ID,None表示自动选择
- enable_aec: 是否启用AEC回音消除
- """
- self.sample_rate = sample_rate
- self.channels = channels
- self.device_id = device_id
- self.enable_aec = enable_aec
- self.input_stream = None
- self.frame_size = int(sample_rate * 0.02) # 20ms帧
- self._audio_listeners: List[AudioListener] = []
- self._is_closing = False
- # AEC处理器
- self.aec_processor = None
- def add_audio_listener(self, listener: AudioListener):
- """添加音频监听器"""
- if listener not in self._audio_listeners:
- self._audio_listeners.append(listener)
- def remove_audio_listener(self, listener: AudioListener):
- """移除音频监听器"""
- if listener in self._audio_listeners:
- self._audio_listeners.remove(listener)
- async def start(self):
- """启动音频采集"""
- try:
- # 初始化AEC处理器(如果启用)
- if self.enable_aec:
- try:
- from .aec_processor import AECProcessor
- self.aec_processor = AECProcessor(
- sample_rate=self.sample_rate,
- channels=self.channels
- )
- await self.aec_processor.initialize()
- print(f"AEC回音消除已启用")
- except Exception as e:
- print(f"AEC初始化失败,将使用原始音频: {e}")
- self.aec_processor = None
- # 自动选择设备
- if self.device_id is None:
- devices = sd.query_devices()
- input_devices = [
- (i, d) for i, d in enumerate(devices)
- if d['max_input_channels'] > 0
- ]
- if not input_devices:
- raise RuntimeError("找不到可用的输入设备")
- self.device_id = input_devices[0][0]
- print(f"自动选择输入设备: {input_devices[0][1]['name']}")
- # 创建输入流
- self.input_stream = sd.InputStream(
- device=self.device_id,
- samplerate=self.sample_rate,
- channels=self.channels,
- dtype=np.float32,
- blocksize=self.frame_size,
- callback=self._input_callback,
- latency="low",
- )
- self.input_stream.start()
- print(f"音频采集已启动: {self.sample_rate}Hz {self.channels}ch")
- except Exception as e:
- raise RuntimeError(f"启动音频采集失败: {e}")
- def _input_callback(self, indata, frames, time_info, status):
- """输入回调"""
- if status and "overflow" not in str(status).lower():
- print(f"输入流状态: {status}")
- if self._is_closing:
- return
- try:
- # 转换为 int16 格式
- audio_data_int16 = (indata.flatten() * 32768.0).astype(np.int16)
- # 应用AEC处理(如果启用)
- if self.aec_processor and self.aec_processor.is_initialized:
- audio_data_int16 = self.aec_processor.process_audio(audio_data_int16)
- # 通知所有监听器
- for listener in self._audio_listeners:
- try:
- listener.on_audio_data(audio_data_int16.copy())
- except Exception as e:
- print(f"音频监听器处理失败: {e}")
- except Exception as e:
- print(f"输入回调错误: {e}")
- async def stop(self):
- """停止音频采集"""
- self._is_closing = True
- # 停止AEC处理器(如果启用)
- if self.aec_processor:
- try:
- await self.aec_processor.close()
- except Exception as e:
- print(f"关闭AEC处理器失败: {e}")
- finally:
- self.aec_processor = None
- if self.input_stream:
- try:
- if self.input_stream.active:
- self.input_stream.stop()
- self.input_stream.close()
- except Exception as e:
- print(f"关闭音频流失败: {e}")
- finally:
- self.input_stream = None
- self._audio_listeners.clear()
- print("音频采集已停止")
- @staticmethod
- def list_devices():
- """列出所有可用设备"""
- devices = sd.query_devices()
- print("\n可用音频设备:")
- print("-" * 60)
- for i, device in enumerate(devices):
- if device['max_input_channels'] > 0:
- print(f"[{i}] {device['name']}")
- print(f" 输入声道: {device['max_input_channels']}, 采样率: {device['default_samplerate']}Hz")
- print("-" * 60)
|