扩音器:麦克风到扬声器直通
app mic_pa 让手表充当放大器(扩音器):麦克风拾取到的任何声音都会立即从扬声器输出。
sh
app mic_pa start [sr_khz] [mic_gain] [dac_vol] [mono] # 默认值:16 200 -600 1
app mic_pa stop
app mic_pa info
app mic_pa vol -300 # 运行时扬声器音量,步进 0.01 dB,0 = 最大
app mic_pa gain 300 # 运行时麦克风增益,步进 0.1 dB(300 = 30 dB)
工作原理
音频从不经过 CPU。shell 音频驱动已经有一条 ADDA 路径(audio_record_attr / audio_play_attr 中的 adda_en):
text
ADC DMA 半缓冲区 --(audio_rec_read_data_cb)--> shell_audio_ringbuf (1 KiB)
|
(audio_play_write_data_cb) --> DAC DMA --> 扬声器
-
当
handle->attribute.adda_en被设置时,audio_rec_read_data_cb()将每个采集到的半缓冲区放入共享的 ADDA 环形缓冲区(ring_buf_put)。 -
audio_play_write_data_cb()从该环形缓冲区取出数据(ring_buf_get),并用audio_out_write()写入 DAC,因此扬声器仅以两个 DMA 的延迟播放麦克风信号。当环形缓冲区为空时,DAC 得到静音而不是杂音。 -
zephyr/drivers/audio/shell/audio_driver_shell.c中的audio_pa_start()以adda_en构建两个实例,并按正确顺序启动它们:-
创建录音器实例(它初始化 ADDA 环形缓冲区),
-
audio_record_config()------占用采集会话,ADC 仍停止, -
创建 + 配置 + 启动 DAC 播放器(它首先播放静音),
-
audio_record_start()------现在环形缓冲区携带麦克风数据。
audio_pa_stop()以相反顺序关闭它们。 -
-
应用侧(
application/bt_watch/src/main/mic_pa.c)添加策略:通话进行时拒绝启动,并在通话到来时立即自行停止(bt_manager_hfp_get_status()),见下文。 -
CONFIG_MIC_PA(application/bt_watch/src/Kconfig、prj.conf)启用它。
参数
| 参数 | 默认值 | 单位 | 含义 |
|---|---|---|---|
sr_khz |
16 | kHz | ADC 和 DAC 的采样率 |
mic_gain |
200 | 0.1 dB | ADC 通道 0 增益,200 = 20.0 dB |
dac_vol |
-600 | 0.01 dB | DAC 音量,0 = 最大,-600 = -6 dB |
mono |
1 | - | 忽略:此板的 DAC(phy_leopard)仅接受 MONO_MODE,因此单个麦克风通道始终送往两个 DAC 通道 |
mic |
0 | - | 转发哪个板载麦克风:0 = 模拟输入对 INPUT0,1 = INPUT1 |
两个麦克风(app mic_pa mic)
板上有两个模拟麦克风。它们挂在编解码器的两个模拟输入对上:
| 模拟输入 | 焊盘 | 板级映射条目 |
|---|---|---|
| 对 0 | INPUT0P / INPUT0N | AUDIO_ANALOG_MIC0 -> adc 通道 0(扩音器之前使用的) |
| 对 1 | INPUT1P / INPUT1N | 第二个麦克风 |
zephyr/boards/arm/ats3089p_dev_watch/board.c 中的板级表有一个 AUDIO_ANALOG_MIC1 条目,但它是 MIC0 的副本(相同的 INPUT0 对),因此选择它不会改变任何东西。AUDIO_ANALOG_MIC2 才是有用的:它将通道 0 映射到 INPUT0 对,通道 1 映射到 INPUT1 对 ,即同时采集两个麦克风。因此扩音器总是启动 AUDIO_ANALOG_MIC2(ch_gain[0] 和 ch_gain[1] 都获得 mic_gain),驱动的 ADC 回调在进入 ADDA 环形缓冲区之前仅保留选定的通道(audio_driver_shell.c 中的 pa_mic_stereo / pa_mic_sel)。因为两个通道始终被采集,切换只是翻转该选择:
text
app mic_pa start 16 100 -900 1 0 # 星号:两个麦克风开启,转发 INPUT0
app mic_pa mic 1 # 切换到 INPUT1 麦克风,声音继续运行
app mic_pa mic 0 # 再切回
app mic_pa info # 显示当前活动的是哪一个
此板上所有 VMIC 偏置都已启用(CONFIG_AUDIO_ADC_0_VMIC_CTL_ARRAY = {3, 3, 3}),因此第二个麦克风无需额外 GPIO 即可供电。现在 ADC 传输两个通道(半缓冲区每 8 ms 到达一次,而不是 16 ms),到达环形缓冲区的去交织单声道流仍为 32 kB/s,这正是 DAC 消耗的。
在这些默认值下语音很响;麦克风靠近扬声器的扩音器会产生反馈(啸叫),因此请从较小的 mic_gain 或较低的 dac_vol 开始,然后逐步提高。
仅一个采集会话
ADC 只有一个采集会话(当第二个用户请求时,驱动记录 channel type:8 session max 1 and opened 1),因此扩音器不能与任何其他东西共享麦克风:
-
普通的
audio record ... rec_file=1录音在扩音器运行时将无法打开 ADC(反之亦然), -
电话通话需要同一会话用于两个方向,因此
app mic_pa在通话期间拒绝启动,并在通话到来时停止扩音器(mic_pa: a call is up, megaphone off)。同时运行两者会使通话没有下行和扬声器声音。 -
自动通话桥接(
CONFIG_CALL_RECORDER)仅在录音文件打开时起作用,因此不会干扰扩音器。
备注
-
不涉及媒体播放器和 DSP 会话:路径是本地 ADC 和 DAC 及其 DMA。不会写入磁盘,因此扩音器可以一直运行到电池耗尽。
-
两个 DMA 缓冲区必须不同,这就是 shell 定义
SHELL_ADC_USE_DIFFERENT_BUFFER的原因:录音器(id 0)使用shell_audio_buffer1,播放器(id 0)使用shell_audio_buffer。 -
如果音调听起来不对(太低/半速),请尝试
mono 0:这会告诉 DAC 将环形缓冲区作为立体声读取,而不是将一个通道扩展到两个。注意 :在此板上不可能。zephyr/drivers/audio/phy_leopard/phy_audio_dac.c中的phy_dac_prepare_enable()仅接受MONO_MODE,对STEREO_MODE回答[dac0][E]: Invalid channel mode 2,这会使audio_out_open()失败,启动返回-14(-EFAULT)。因此audio_pa_start()强制mono_en = 1,并在请求 0 时打印mic_pa: this dac supports mono mode only, using mono。 -
直通可以根据需要随意停止和重新启动:ADC/DAC 实例每次都会重新创建,ADDA 环形缓冲区由
audio_record_create()重新初始化。 -
扩音器运行时两个音频 shell 实例的 id 为 0,因此后续的
audio record create/audio play create会重用它们(首先调用audio_record_stop()/audio_play_stop())。