改用Tone.js播放MIDI音乐
广告
{{v.name}}
📋 原始需求(task.md):

浏览器播放MIDI音乐时还是没有声音,这是因为浏览器不支持MIDI播放。
需要使用Tone.js来在前端播放MIDI音乐。

遇到的问题:
1. Uncaught TypeError: this.draw is not a function
2. 播放失败: Tone.startAudioContext is not a function
3. 播放失败: Tone.Transport.add is not a function
4. TypeError: Tone.Transport.removeAllEvents is not a function
5. The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.
6. 必须把this.startPlayback()的函数体直接写在togglePlay()里面,否则浏览器就会拒绝播放音乐
7. The AudioContext is "suspended". Invoke Tone.start() from a user action to start the audio.
8. 可以把togglePlay()改成 async togglePlay()

使用Tone.js库在前端播放MIDI音乐,解决浏览器Autoplay政策限制的问题。


问题分析
  • 问题现象:使用浏览器原生Audio API播放MIDI没有声音,改用Tone.js后遇到API兼容性问题
  • 根本原因
    1. Tone.js 14.x不支持startAudioContext()Transport.add()Transport.removeAllEvents()等方法
    2. Chrome Autoplay政策要求AudioContext.resume()在用户点击后立即调用,不能在async操作后调用
  • 正确逻辑:使用Tone.js 14.x支持的API,确保AudioContext在点击事件的同步调用链中恢复

解决方案
  • 加载Tone.js 14.7.77 CDN:Tone.Transport、Tone.PolySynth、Tone.Part、Tone.Transport.start/stop/cancel
  • 将播放逻辑直接写在togglePlay()中,或使用async togglePlay()配合await Tone.context._nativeAudioContext.resume()
  • 使用Tone.Transport.start(0)part.start(0)Tone.Transport.cancel()Tone.Transport.stop()等API
  • 通过playableNotes直接传递前端的notes数组给Tone.js,无需后端API生成MIDI
  • stopPlayback()中正确释放资源:part.dispose()synth.dispose()masterVol.dispose()
友链