MIDI.js中setTimeout触发过晚及音符、程序变更事件滞后的修复咨询
修复MIDI.js abcjs分支首次播放延迟问题
针对你遇到的首次播放延迟(尤其是MIDI程序变更事件延迟更严重)、重新播放正常的问题,可通过以下方案修复:
1. 用Web Audio时间轴替代setTimeout处理程序变更事件
setTimeout依赖浏览器事件循环,精度低且无法和音频调度同步,这是程序变更事件延迟严重的核心原因。需改用Web Audio API的高精度时间轴来调度此类事件:
- 找到
adaptors.js中用setTimeout处理程序变更的代码段,替换为基于AudioContext.currentTime的调度逻辑:
该逻辑通过Web Audio的时间基准校准,确保程序变更和音符播放严格同步。// 替换原setTimeout代码 const targetTime = MIDI.audioContext.currentTime + delay / 1000; // delay为原毫秒值,转成秒级时间偏移 const triggerProgramChange = () => { if (MIDI.audioContext.currentTime >= targetTime) { MIDI.programChange(channel, program); // 原程序变更逻辑 } else { requestAnimationFrame(triggerProgramChange); } }; requestAnimationFrame(triggerProgramChange);
2. 预加载所有用到的乐器音色
首次播放延迟大概率和音色资源未完全加载有关,重新播放时资源已缓存所以正常。需在播放前预加载MIDI文件中涉及的所有乐器:
- 初始化MIDI.js时指定所有需要的乐器,等待加载完成后再触发播放:
MIDI.loadPlugin({ soundfontUrl: "./soundfont/", // 你的音色文件路径 instruments: ["acoustic_grand_piano", "electric_guitar_clean"], // 列出MIDI中用到的所有乐器 onsuccess: () => { // 加载完成后启用播放功能 document.getElementById("play-btn").disabled = false; } });
3. 校准AudioContext的激活时机
浏览器对AudioContext有自动暂停限制(需用户交互激活),若页面加载时提前创建Context,会导致初始时间基准异常:
- 仅在用户交互(如点击播放按钮)时创建或恢复AudioContext:
let audioContext; document.getElementById("play-btn").addEventListener("click", async () => { if (!audioContext) { audioContext = new (window.AudioContext || window.webkitAudioContext)(); MIDI.setContext(audioContext); } else if (audioContext.state === "suspended") { await audioContext.resume(); } // 执行MIDI播放逻辑 playMidiFile(); });
4. 统一事件时间基准计算
检查adaptors-AudioAPI.js中start()方法的时间计算逻辑,确保所有事件(音符、程序变更)的延迟值转换一致(毫秒转秒时除以1000),避免时间基准错位。
内容的提问来源于stack exchange,提问作者root
相关产品推荐
相关产品推荐

