You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于ESP8266Audio库实现ESP8266多WAV文件带时间戳播放需求

基于ESP8266的多音频流时间戳播放实现

我正在开发基于ESP8266的脚本,使用ESP8266Audio库播放多个十六进制编码的WAV文件,需要实现不同时间戳的多音频流播放。硬件采用ESP8266与TIP 120晶体管,现有代码如下:

#include <Arduino.h>    
#include "AudioFileSourcePROGMEM.h"
#include "AudioGeneratorWAV.h"
#include "AudioOutputI2SNoDAC.h"
#include <ESP8266WiFi.h>
#include <SPI.h>
#include <SD.h>

// VIOLA 采样来自声音示例库
#include "viola.h"
#include "viola02.h"

AudioGeneratorWAV *wav;
AudioFileSourcePROGMEM *file;

AudioOutputI2SNoDAC *out;

void setup()
{
  Serial.begin(115200);
  delay(1000);
  Serial.printf("WAV start\n");

  audioLogger = &Serial;
  out = new AudioOutputI2SNoDAC();  
  // out02 = new AudioOutputI2SNoDAC();
  // out -> SetGain(0.08);
}

void Play(const void *data, uint32_t len) {  
  file = new AudioFileSourcePROGMEM( data, len );
  wav = new AudioGeneratorWAV();
  wav->begin(file, out);
  delay(3000);
  while (true) {
    if (wav->isRunning()) {
      if (!wav->loop()) wav->stop();
    } else {
      Serial.printf("WAV done\n");
      delay(1000);
      break;
    }
  }
}

void loop()
{
  Play(viola,sizeof(viola));
  Play(viola02,sizeof(viola02));
}

现有代码问题

当前代码是串行阻塞式播放:Play函数里的while(true)和delay会彻底卡住主循环,两个音频只能按顺序播放,完全无法实现多流同时按时间戳触发的需求。

解决方案:非阻塞多音频流实现

要实现不同时间戳的多音频播放,需要给每个音频流创建独立的播放实例,在主循环中异步处理每个流的播放逻辑,避免阻塞。

修改后的代码示例

#include <Arduino.h>    
#include "AudioFileSourcePROGMEM.h"
#include "AudioGeneratorWAV.h"
#include "AudioOutputI2SNoDAC.h"
#include <ESP8266WiFi.h>
#include <SPI.h>
#include <SD.h>

#include "viola.h"
#include "viola02.h"

// 定义音频流结构体,存储实例、启动时间戳等信息
struct AudioStream {
  AudioGeneratorWAV* wav;
  AudioFileSourcePROGMEM* file;
  unsigned long startTimestamp; // 启动时间(系统运行毫秒数)
  bool isInitialized; // 是否已创建播放实例
  const void* data;
  uint32_t len;
};

AudioOutputI2SNoDAC *out;
AudioStream streams[2]; // 管理两个音频流

void setup()
{
  Serial.begin(115200);
  delay(1000);
  Serial.printf("Multi-Audio Stream Start\n");

  audioLogger = &Serial;
  out = new AudioOutputI2SNoDAC();  
  out->SetGain(0.08); // 根据实际音量需求调整增益

  // 初始化音频流参数
  streams[0] = {nullptr, nullptr, 2000, false, viola, sizeof(viola)};    // 系统启动后2秒播放viola
  streams[1] = {nullptr, nullptr, 5000, false, viola02, sizeof(viola02)}; // 系统启动后5秒播放viola02
}

void loop()
{
  unsigned long currentMillis = millis();

  for (int i = 0; i < 2; i++) {
    AudioStream& stream = streams[i];

    // 到达启动时间且未初始化时,创建播放实例
    if (currentMillis >= stream.startTimestamp && !stream.isInitialized) {
      stream.file = new AudioFileSourcePROGMEM(stream.data, stream.len);
      stream.wav = new AudioGeneratorWAV();
      stream.wav->begin(stream.file, out);
      stream.isInitialized = true;
      Serial.printf("Started audio stream %d\n", i+1);
    }

    // 已初始化且正在播放时,执行播放循环
    if (stream.isInitialized && stream.wav->isRunning()) {
      if (!stream.wav->loop()) {
        stream.wav->stop();
        // 播放完成后释放内存,避免ESP8266资源耗尽
        delete stream.wav;
        delete stream.file;
        stream.wav = nullptr;
        stream.file = nullptr;
        stream.isInitialized = false;
        Serial.printf("Completed audio stream %d\n", i+1);
      }
    }
  }
}

关键说明

  1. 非阻塞逻辑:用millis()获取系统运行时间替代delay,通过时间戳判断是否启动音频,主循环始终保持响应。
  2. 独立实例:每个音频流拥有专属的AudioGeneratorWAV和AudioFileSourcePROGMEM实例,避免资源冲突。
  3. 资源回收:播放完成后主动释放实例内存,适配ESP8266有限的RAM资源。
  4. 时间戳控制:通过startTimestamp字段自由设置每个音频的启动时间,实现多流按计划播放。

硬件注意事项

TIP 120晶体管用于音频放大时,需确保电路连接正确:

  • ESP8266的I2S输出引脚串联限流电阻后连接TIP 120基极
  • TIP 120集电极接电源,发射极接扬声器,同时并联续流二极管保护器件

内容的提问来源于stack exchange,提问作者Fahim Uz Zaman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 21:40:21