NES APU Replayer

做了一个重播 NES APU 指令的 UI:

在这里插入图片描述
点击图片或这里体验。

使用说明

  • 将录制好的文本文件拖放到 UI 中即可开始播放
  • 录制文件格式如下:
    82946, $4015, $0f
    82952, $4017, $c0
    83145, $4015, $0b
    83151, $4008, $00
    83157, $4015, $0f
    83169, $4000, $30
    83205, $4004, $30
    83241, $400c, $30
    83279, $4001, $7f
    83315, $4005, $7f
    
    其中,第一列表示 CPU 时钟周期;第二列表示寄存器地址;第三列表示写入寄存器的数据
  • 录制文件可由其它模拟器录制产生。使用 Mesen 录制时,可以使用如下录制条件:
    IsWrite && ((Address >= $4000 && Address <= $400f) || Address == $4015 || Address == $4017)
  • UI 从上到下共 5 行。分别代表:方波通道一、方波通道二、三角波通道、噪声通道、最终合成效果
  • UI 从左到右共 5 列。分别代表:波形、通道基本信息、滑音单元、包络生成器、长度计数器

实现细节
APU 部分可以参考 NES APU 这篇文章,不赘述

音频部分当然是采用 WebAudio API,基本思路就是每次产生一小段样本数据,然后送去播放。收到播放完毕通知后,再产生,再播放,这样一直循环。不过,这里有几个小细节。

一是播放完毕的通知存在延迟且时长不固定。如果收到通知再产生下个音频数据块,会让整体声音有停顿,听起来极度不适。解决此问题可维护一个音频数据块列表,这个列表不用太大,比如长度为 5 即可。设每个音频数据块的播放时长是 20 ms, 则这个列表中的音频数据全部播放完毕共需要 100 ms。每个块儿播放完毕后,会有一个播放完毕通知。因此,不用等 100 ms 就可以收到通知,此时列表内应还剩音频数据还没有播放完毕。收到通知后,先移除掉已经播放过的块,再产生音频数据,直到列表填满。由于此时列表还有剩余音频没有播放完毕,而新的音频数据已经就绪,就不会出现停顿。这个方法有个前提假设,就是通知的延迟不能太高。按列表长度为 5 来算,延迟不能大于 80 ms。就实际情况来看,延迟大约在 20 ms 左右的样子。

二是 AudioBufferSourceNode 没有队列功能。起初我以为跟 Windows API 是有列队功能的,结果导致列表内的音频数据实际上是同时播放了,听起来也是极度不适。这里应该采用 AudioBufferSourceNode.start(startTime) 指定每个块的开始时间,避免同时播放的问题。设每个块儿播放时长是 20 ms,则每个块的开始时间就是上个块的开始时间再加 20 ms。第一个块的播放时间应该是 AudioContext.currentTime,开始时间小于 AudioContext.currentTime 的音频数据也是会立即播放的!这里,第一个块是指第一个被播放的数据块,不是上述列表中的第一个元素。

三是如果用户没有在页面上进行过用户交互,则 AudioContext.state 的初始状态是 suspended,此时即使调用 AudioContext.resume() 也没有任何效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值