基于Teensy的嵌入式音频延迟器设计与实现:解决礼堂声场同步问题 1. 项目概述为什么礼堂需要独立的音频延迟器在任何一个稍具规模的礼堂、教堂或者多功能厅里你经常会看到不止一对扬声器。靠近舞台的主扬声器负责覆盖前区而在大厅中部和后部通常会悬挂或壁装额外的辅助扬声器以确保每个座位的听众都能获得足够清晰的直达声。这听起来是个完美的方案但这里藏着一个物理上的小麻烦声音在空气中的传播速度大约是每毫秒0.34米或者说每英尺约1毫秒。这意味着对于坐在后排的听众来说从舞台主扬声器传来的声音会比从头顶上仅几米远的后场辅助扬声器传来的声音晚到几十毫秒。这种时间差会导致严重的声波干涉特别是对于语音这类瞬态信号后果就是声音变得模糊、浑浊专业上称之为“梳状滤波效应”会显著降低语音的可懂度。想象一下听一个带有轻微回音的电话你就能体会到那种不适。正确的工程解决方案是为距离听众更近的后场扬声器施加一个精确的电子延迟让所有扬声器发出的声音“同步”到达听众的耳朵。这个延迟量正好等于声音从主扬声器传播到该听众位置与从辅助扬声器传播到同一位置的时间差。所以如果后场扬声器比主扬声器离听众近了10米我们就需要为它增加约29.4毫秒的延迟。我接手这个项目的契机是本地一所教堂的原有商用音频延迟设备损坏了。他们有三组扬声器主扬声器在舞台两侧另两组分别位于后方26英尺和37英尺处。维修或更换商用设备成本不菲这给了我一个绝佳的机会不仅能为社区解决问题还能深入实践我一直想探索的嵌入式音频处理。我的专业背景是软件硬件和音频电子算是“副业”这次正好跨界练手。经过一番调研我选择了以高性能和丰富音频库著称的Teensy微控制器平台目标是打造一个成本可控、性能可靠且易于使用的双通道音频延迟器。2. 核心硬件选型与设计思路硬件是整个项目的基石选型决定了系统的性能上限和开发难度。我的核心需求很明确一个能进行低延迟、高质量音频处理的微控制器一个负责模拟音频输入输出的编解码器以及一套直观的人机交互界面。2.1 主控与音频接口为什么是Teensy Audio Adaptor Board在嵌入式音频领域PJRC公司的Teensy系列微控制器几乎是“开挂”般的存在。我选择了当时的旗舰型号Teensy 4.0它基于NXP的i.MX RT1062跨界处理器主频高达600MHz拥有丰富的内存和强大的数字信号处理能力。最关键的是PJRC为其开发了极其强大且易用的Audio Library。这个库提供了大量现成的音频处理模块如混音器、滤波器、延迟线、效果器等并且通过一个图形化的“音频系统设计工具”进行连接和配置能自动生成底层驱动代码极大降低了开发门槛。与之配套的Audio Adaptor Board音频适配板是完美搭档。这块子板集成了专业的音频编解码器芯片通常是SGTL5000或WM8731提供标准的线路电平输入输出接口。它通过I2S总线与Teensy进行高速数字音频数据传输并通过I2C总线进行编解码器的参数配置。这意味着我无需自己设计复杂的模拟电路来匹配电平、抗干扰只需像搭积木一样将适配板插在Teensy上就能获得一个专业的立体声音频输入输出通道。对于本项目我只需要一个单声道输入来自调音台和两个独立的单声道输出分别驱动两组延迟扬声器这块板子完全满足需求。注意Teensy Audio Adaptor Board的输入输出接口是3.5mm立体声耳机接口。为了接入专业音响系统中标准的XLR平衡接口需要进行电平转换和平衡-非平衡转换。我选择了简单的无源转换电路直接焊接在XLR接口后面。如果对音质和抗噪有极致要求可以考虑使用带运算放大器的有源转换电路。2.2 人机交互设计旋钮与屏幕的取舍用户需要能够直观地设置和查看两个通道的延迟时间。我放弃了复杂的菜单和按钮选择了两个集成了按压开关的旋转编码器。每个编码器对应一个输出通道旋转可以增减延迟值按下则可以保存当前设置到EEPROM电可擦可编程只读存储器即使设备断电设置也不会丢失。这种操作方式符合音响工程师的直觉调节起来快速直接。为了显示信息我选用了一块0.91英寸的128x32像素的OLED显示屏通过I2C总线驱动。这块屏幕虽小但足够清晰功耗低。它主要显示两个信息一是两个通道当前设置的延迟值以英尺为单位这对安装人员来说比毫秒更直观二是一个实时的输入音频峰值表。这个峰值表非常实用它能立刻告诉用户信号是否正常输入电平是否过大导致削波失真我在表上设置了一条参考线代表0 dBu专业音频的标准参考电平便于快速判断。这里有一个硬件设计上的小心得Teensy 4.0有多个I2C总线。我将OLED屏连接在了第二组I2C引脚上而Audio Adaptor板使用默认的第一组I2C。这样做可以避免音频数据流和屏幕刷新之间的总线竞争确保音频处理的实时性不受屏幕刷新影响。2.3 供电与机箱那些容易忽略的细节整个系统通过Teensy的Micro USB口供电5V直流。这在原型阶段很方便但在成品中从机箱后面伸出一根USB线很不专业。我的一个重大失误就在于此我选择了一个尺寸非常紧凑的金属机箱10x5x10 cm以至于没有空间在背板上安装一个标准的USB面板插座。最后只好在背板边缘开了一个小槽将USB线穿入后用热熔胶固定。这是一个妥协的方案虽然能用但既不美观也不牢固。下次设计时机箱宽度至少应增加2英寸为各种面板接口留出充足空间。机箱内部我使用了一块半尺寸的PCB万用板作为主电路板。Teensy被直接焊接在万用板上以增加稳固性Audio Adaptor板则通过排母插在Teensy上方。旋转编码器和OLED屏通过杜邦线连接到万用板。虽然飞线在原型阶段提供了灵活性但在成品中它可能成为潜在的故障点接触不良、断线。如果追求更高的可靠性应该设计一块定制PCB将所有连接器、电阻等元件集成在一起。3. 软件架构与音频流水线实现软件部分是项目的灵魂得益于Teensy Audio Library复杂的音频流处理变得像画流程图一样简单。3.1 音频处理流程搭建PJRC提供的在线“Audio System Design Tool”是神器。我在工具中拖放了以下几个核心组件AudioInputI2S从Audio Adaptor板的编解码器获取立体声音频输入数据流。AudioMixer4由于输入是立体声但我只需要单声道这里使用一个混音器将左右声道合并。实际上我直接将左声道输入复制给了两个处理通道因为音源是单声道话筒信号。AudioEffectDelay这是核心的延迟效果器。Teensy Audio Library提供的延迟对象最大能提供约500毫秒的延迟具体取决于内存分配远超本项目所需约110毫秒对应37英尺。我创建了两个独立的延迟对象实例分别对应两个输出通道。AudioOutputI2S将处理后的音频数据流发送回编解码器输出到硬件。在工具中我用“连线”将这些组件的输入输出连接起来形成一个清晰的音频流水线输入 - 复制 - 延迟通道A - 输出A输入 - 延迟通道B - 输出B。设计完成后点击“Export”导出工具会自动生成一个.h头文件里面包含了所有这些音频对象的声明和连接配置代码。我只需要将这个文件包含到我的Arduino项目中并在setup()函数里调用一句AudioMemory(12)来分配音频内存池整个音频引擎就初始化完成了。剩下的就是通过API去控制那两个延迟对象。3.2 延迟控制逻辑与中断处理延迟时间需要根据旋转编码器的操作来实时改变。每个编码器有A、B两相输出旋转时会产生一系列相位差90度的脉冲。通过检测A、B相的边沿变化顺序就能判断是顺时针还是逆时针旋转。最可靠的方式是使用外部中断。我将两个编码器的A、B相分别连接到Teensy支持外部中断的引脚上。在中断服务函数中我读取A、B相的当前状态并与之前保存的状态对比根据状态机判断出旋转方向。然后在一个全局变量中增加或减少对应的延迟值单位为英尺。中断函数要尽可能短只做标记复杂的计算如英尺到采样点数的转换和屏幕刷新放在主循环中执行。这里有一个关键的计算如何将用户设定的距离英尺转换为音频库需要的延迟采样点数已知声音速度 ≈ 1 英尺/毫秒。所以延迟时间毫秒 距离英尺。音频系统采样率AUDIO_SAMPLE_RATE_EXACT默认为44100 Hz即每秒44100个采样点。因此1毫秒 44.1个采样点。最终所需延迟的采样点数 距离英尺 * 44.1。在代码中我使用delayChannelA.delay(0, sampleCount)来设置延迟。第一个参数0表示延迟线中的第0个抽头可以设置多个不同延迟的抽头同时工作第二个参数就是计算出的采样点数。库函数内部会处理所有缓冲区和内存管理。3.3 OLED显示与峰值表实现OLED显示驱动使用了Adafruit的SSD1306和GFX库。显示内容分为两部分延迟值显示以大字号显示“A: 26 ft”和“B: 37 ft”这样的信息。峰值表实现这是一个更有趣的部分。我需要实时显示输入信号的瞬时电平。在音频回调中或者在一个定时器中断中我会获取输入音频缓冲区的数据。音频样本是16位有符号整数范围-32768到32767。峰值表的算法是遍历一个缓冲区内的所有样本找到绝对值最大的那个样本。将这个峰值样本的绝对值映射到OLED屏幕的像素宽度上。例如0对应最左边32767对应最右边。为了显示更平滑通常会采用“峰值保持并衰减”的策略当前计算出的峰值会以一条竖线显示并保持一段时间如300毫秒然后缓慢衰减下落这样人眼既能捕捉到瞬态峰值又能看到电平的变化趋势。我在屏幕上约2/3宽度处画了一条固定的竖线代表0 dBu对应一个特定的电压值在代码中需要根据编解码器的增益设置换算成具体的样本值。这样用户就能一目了然地看到信号是否接近或超过标准电平。4. 系统集成、测试与校准当硬件焊接完毕软件代码编译下载后最激动人心也最考验耐心的阶段——系统集成与测试就开始了。4.1 原型测试与延迟测量在装入机箱前我在面包板上搭建了完整的原型系统。使用信号发生器产生一个1kHz的正弦波接入音频适配板的输入。同时用示波器的两个探头一个接输入一个接输出。将延迟设置为0观察两个波形。理论上应该完全对齐但实际测量发现即使延迟为0输出信号也比输入信号晚了大约6毫秒。这6毫秒的系统固有延迟主要来自哪里它主要是Teensy音频库的缓冲区大小造成的。库默认使用128个样本的音频块进行处理。在44100Hz采样率下处理一个块的时间就是 128 / 44100 ≈ 2.9 ms。音频数据需要经过输入缓冲区、处理队列、输出缓冲区这个管道中可能同时存在2个这样的块再加上编解码器自身的微小延迟总计约6ms。对于本应用来说这个延迟完全在可接受范围内因为没有人会把辅助扬声器安装在距离主扬声器仅2米约6ms声程差的范围内。如果需要更低的延迟可以修改音频库的头文件减小缓冲区大小例如改为64样本但这会增加处理中断的频率对微控制器的实时性要求更高。4.2 装配与布线挑战将原型移植到金属机箱内是一次挑战。前面板需要为两个编码器和OLED屏开孔后面板需要为三个XLR接口一进二出开孔。我没有台钻为了保证孔位精准我借助了大学机器 shop的台钻和台钳这步非常关键歪斜的孔会严重影响成品美观度。OLED屏的安装我用了一个巧办法用3D打印了一个刚好卡住屏幕的支架然后用热熔胶将其固定在前面板内侧。这样既牢固又避免了在脆弱的OLED电路板上钻孔。内部布线是另一个痛点。由于机箱空间狭小飞线显得杂乱。我用扎带尽量将线束捆扎整齐并确保没有线材靠近编码器或Teensy的晶振等敏感部位防止引入噪声。特别是音频输入线我使用了屏蔽线并且将屏蔽层仅在Audio Adaptor板的接地点单点接地以避免形成地线环路引入嗡嗡声。4.3 最终功能测试与现场校准设备组装完成后进行了最终测试基本功能测试旋转编码器观察OLED显示的延迟值是否平滑变化按下编码器检查EEPROM存储功能断电重启后数值应保持不变。音频通路测试输入音乐信号监听两个输出确认声音正常无失真或噪声。快速开关延迟应能听到明显的“回声”效果变化。延迟精度验证再次使用信号发生器和示波器测量不同设置值下的实际延迟时间。实测值与理论值英尺数 * 1 ms 6 ms系统延迟基本吻合误差在可接受范围内 0.1ms。现场校准将设备接入教堂的音响系统。校准方法是暂时关闭所有其他扬声器只打开一组待校准的后场扬声器。在舞台上放置一个话筒发出一个尖锐的脉冲声如拍手。在后场扬声器下方用测量话筒连接到一个带波形显示的音频分析软件上。调整该通道的延迟值直到屏幕上看到的直接声从主扬声器来和延迟声从后场扬声器来的脉冲波形完全重合。重复此步骤校准另一组扬声器。5. 常见问题、优化方向与经验总结在实际制作和调试过程中遇到了一些典型问题也沉淀下一些思考。5.1 遇到的问题与解决方案问题现象可能原因排查与解决思路旋转编码器调节时数值乱跳1. 编码器引脚接触不良原型阶段面包板连接不牢。2. 中断服务函数处理时间过长错过了边沿检测。3. 未使用硬件消抖或软件消抖不佳。1. 检查并重新压紧连接线最终焊接解决。2. 简化中断函数只设置标志位在主循环中处理计数逻辑。3. 在代码中增加去抖延时约10ms或使用编码器库如Encoder.h其内置了稳健的状态机。OLED屏幕无显示或显示乱码1. I2C地址错误常见的有0x3C或0x3D。2. I2C总线未上拉电阻。3. 与音频适配板的I2C冲突。1. 用I2C扫描程序确认设备地址。2. Teensy 4.0内部有上拉电阻但线缆较长时建议外接4.7kΩ上拉电阻到3.3V。3. 将OLED连接到Teensy的第二组I2C引脚Wire1彻底避免总线冲突。音频输出有持续的高频噪声或杂音1. 电源噪声。2. 数字信号对模拟部分的干扰。3. 接地环路。1. 使用线性稳压电源或高质量的USB电源为Teensy供电避免开关电源的纹波。2. 确保音频模拟走线远离微控制器的数字高频区域如时钟线。3. 确保整个系统单点接地特别是XLR接口的屏蔽层。延迟设置后声音有“咔哒”声或爆音直接更改延迟线的长度会导致音频流不连续。使用音频库提供的delayChannelA.disableInterpolation(true/false)函数。禁用插值true时切换延迟会有爆音但CPU占用低启用插值false默认时库会平滑过渡无爆音但计算量稍大。对于本应用应保持启用。EEPROM存储次数有限担心损坏EEPROM有擦写寿命通常10万次。频繁保存会缩短寿命。修改逻辑仅在按下编码器并保持超过1秒后才执行保存操作。日常旋转调整只改变内存中的变量不触发保存。5.2 可改进的优化方向这个项目已经可以稳定工作但从更专业的角度还有不少可以提升的空间平衡输入实现目前的输入是非平衡的。专业音响环境使用平衡传输来抑制共模噪声。一个巧妙的改进思路是将XLR平衡接口的热线和冷线-分别接入音频适配板的左、右输入通道。然后在软件中添加一个音频混合器对象执行一个简单的减法左声道 - 右声道。这样任何同时叠加在两根线上的同相噪声就会被抵消掉而反相的音频信号则会增强。这几乎不增加硬件成本仅通过软件就能大幅提升抗干扰能力。网络化与控制增加一个Wi-Fi或以太网模块如ESP8266让设备可以接入网络。这样就可以通过网页界面或手机APP进行远程控制、预设存储甚至进行自动化的声学测量和延迟校准。定制PCB设计将Teensy、音频编解码器、XLR接口、电源管理全部集成到一块定制PCB上。这能彻底解决飞线问题提高可靠性缩小体积并且可以加入专业的平衡输入输出电路、更好的电源滤波。动态延迟与更高级处理目前的延迟是静态设置的。可以引入一个测量话筒输入通过算法如相关函数自动检测主扬声器和辅助扬声器到达的时间差实现自动校准。甚至可以加入简单的均衡或限幅功能成为一个功能更全面的扬声器处理器。5.3 核心经验与心得回顾整个项目最大的收获不是做出了一个设备而是打通了从需求分析、硬件选型、软件编程到系统调试的完整流程。对于软件开发者而言亲手焊接元件、测量信号、解决接地噪声问题是非常宝贵的实践经验。几点深刻的体会预留空间至关重要无论是机箱的物理空间还是代码的内存、CPU资源前期多预留20%后期会少很多麻烦。我的小机箱就是个反面教材。原型分阶段验证不要试图一次性把所有功能都做出来再测试。我是先让音频通路出声再调试编码器最后做OLED显示。分阶段排查问题目标清晰效率更高。善用社区与现有资源Teensy和Arduino生态的强大在于其背后活跃的社区和丰富的库。遇到问题首先去查阅官方文档和论坛绝大多数基础问题都有现成答案。不要重复造轮子比如编码器驱动就有非常成熟的Encoder库可以直接使用。测试工具的投资一个简单的USB声卡、一个免费的音频分析软件如REW甚至是一副好耳朵都是音频项目调试的利器。示波器和信号发生器对于定量测量延迟和失真不可或缺。这个基于Teensy的音频延迟器最终成功部署在教堂的音响系统中替代了原先损坏的商用设备。它运行稳定操作直观完美解决了后场声音不同步的问题。更重要的是这个DIY过程本身就是一次将理论知识转化为实际解决方案的生动实践其带来的成就感和学到的东西远超过设备本身的价值。