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

C++ PortAudio多轨音频播放下不同音轨序列的时序同步实现方法

时序交错实现方案

你当前的串行执行逻辑之所以无法达到预期,是因为所有轨道的事件被按顺序排队执行,不会互相穿插。优先推荐单线程事件调度器方案,不要使用多线程:多线程受操作系统调度抖动影响,很难满足音频场景下毫秒级的时序精度要求,还会额外引入MIDI流访问冲突、轨道时钟同步的问题。

具体实现步骤如下:

  1. 预处理所有轨道的序列,把相对时间的sleep转换为全局绝对时间戳
    把每条轨道的seq按顺序累加sleep时长,生成「时间戳(单位:beat,从play触发时刻的0开始计算)+ 事件类型+参数」的结构,比如你的示例转换后得到的事件列表为:
    • 0beat:track1 noteOn 70
    • 0beat:track2 noteOn 30
    • 0.125beat:track2 noteOff 30
    • 0.125beat:track2 noteOn 30
    • 0.25beat:track1 noteOff 70
    • 0.25beat:track1 noteOn 77
    • 0.25beat:track2 noteOff 30
    • 0.25beat:track2 noteOn 31
    • 0.75beat:track1 noteOff 77
    • 0.75beat:track2 noteOff 31
  2. 把所有事件按时间戳从小到大排序,相同时间戳的事件执行顺序不影响效果
  3. 单线程循环调度事件:维护全局当前时间,每次取出时间最早的待执行事件,计算当前时间到该事件时间的差值,高精度等待对应时长后执行所有该时间点的事件,重复该流程直到所有事件执行完毕

简化实现代码示例:

#include <queue>
#include <chrono>
#include <vector>

// 事件结构
struct Event {
    double beat_time; // 绝对beat时间
    int track_id;
    bool is_note_on;
    int note;
    // 排序用,时间小的排前面
    bool operator<(const Event& other) const {
        return beat_time > other.beat_time; // 适配最小堆的反向定义
    }
};

// 按BPM把beat数转换为毫秒值
double bpm_to_ms(double beats, int bpm) {
    return beats * 60000.0 / bpm;
}

void play(int bpm, std::priority_queue<Event> event_queue, MidiStream& stream) {
    auto start_time = std::chrono::high_resolution_clock::now();
    while (!event_queue.empty()) {
        auto next_event = event_queue.top();
        // 计算事件需要触发的绝对墙上时间
        auto trigger_time = start_time + std::chrono::milliseconds((long long)bpm_to_ms(next_event.beat_time, bpm));
        // 高精度等待到触发时间
        std::this_thread::sleep_until(trigger_time);
        // 执行所有当前时间点的事件
        while (!event_queue.empty() && event_queue.top().beat_time == next_event.beat_time) {
            auto ev = event_queue.top();
            event_queue.pop();
            if (ev.is_note_on) {
                stream.noteOn(ev.note, ev.track_id);
            } else {
                stream.noteOff(ev.note, ev.track_id);
            }
        }
    }
}
需要学习的相关知识领域
  • 软音序器/合成器的事件驱动架构设计
  • 最小堆(优先级队列)的使用,用来高效获取最近待触发的事件
  • 高精度计时与定时接口的使用,C++场景下可参考std::chrono库的相关用法
  • 实时系统基础概念,了解音频场景下低抖动、高时序精度的要求
  • 若后续需要支持运行中动态修改序列,可学习无锁队列的用法,实现控制线程与音频调度线程的安全数据交互

内容的提问来源于stack exchange,提问作者panthyon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 15:45:04