播放 base64 音频数据数组
playback of array of base64 audio data
我正在使用 javascript 解析 SWF 文件并在 HTML5 canvas 中显示内容。
我在播放音频流 swf 标签中的音频数据时遇到问题。音频按帧拆分,我能够按照与帧相同的顺序将音频数据放入 base64 数据数组中。 Creating/destorying 每帧上的音频元素似乎不是最好的方法,但这是我能想到的唯一方法。有没有更好的方法来解决这个问题?
注意:swf文件中也有rewind/fastforward/pause个按钮,所以音频传回时需要和帧对齐,所以我不相信我可以只创建一个长的来自较小数据位的音频文件。
您需要将这些音频文件加载为 AudioBuffers and play them through the Web Audio API。
您当前拥有的是数据 URL,它代表完整的音频文件(带有元数据)。
在音频元素中加载所有这些可能确实不是一个好主意,一开始 because some browsers may not let you do so,然后因为 HTMLMediaElement 并不适合完美的时机。
因此您需要首先获取所有这些数据 URL 以取回它们在 ArrayBuffers 中的实际二进制内容,然后您将能够从这些音频文件中提取原始 PCM 音频数据。
// would be the same with data-URLs
const urls = [
"kbgd2jm7ezk3u3x/hihat.mp3",
"h2j6vm17r07jf03/snare.mp3",
"1cdwpm3gca9mlo0/kick.mp3",
"h8pvqqol3ovyle8/tom.mp3"
].map( (path) => 'https://dl.dropboxusercontent.com/s/' + path );
const audio_ctx = new AudioContext();
Promise.all( urls.map( toAudioBuffer ) )
.then( activateBtn )
.catch( console.error );
async function toAudioBuffer( url ) {
const resp = await fetch( url );
const arr_buffer = await resp.arrayBuffer();
return audio_ctx.decodeAudioData( arr_buffer );
}
function activateBtn( audio_buffers ) {
const btn = document.getElementById( 'btn' );
btn.onclick = playInSequence;
btn.disabled = false;
// simply play one after the other
// you could add your own logic of course
async function playInSequence() {
await audio_ctx.resume(); // to make noise we need to be allowed by a user gesture
let current = 0;
while( current < audio_buffers.length ) {
// create a bufferSourceNode, no worry, it weights nothing
const source = audio_ctx.createBufferSource();
source.buffer = audio_buffers[ current ];
// so it makes noise
source.connect( audio_ctx.destination );
// [optional] promisify
const will_stop = new Promise( (res) => source.onended = res );
source.start(0); // start playing now
await will_stop;
current ++;
}
}
}
<button id="btn" disabled>play all in sequence</button>
我最终在声音 ID 索引的 javascript 中创建了一个数组,它与帧 ID 相对应,并且在对象内部它包含一个在我解析标签时创建的音频元素。元素没有添加到 DOM 中,它们是预先创建的,所以它们在帧处理程序的生命周期内一直存在(因为它们存储在对象内部的声音数组中),所以没有create/destory成本。
这样,当我播放帧(视觉效果)时,我可以在与活动帧对应的音频元素上调用播放。由于帧控制播放哪个音频,因此 rewind/fastforward 功能得以保留。
我正在使用 javascript 解析 SWF 文件并在 HTML5 canvas 中显示内容。
我在播放音频流 swf 标签中的音频数据时遇到问题。音频按帧拆分,我能够按照与帧相同的顺序将音频数据放入 base64 数据数组中。 Creating/destorying 每帧上的音频元素似乎不是最好的方法,但这是我能想到的唯一方法。有没有更好的方法来解决这个问题?
注意:swf文件中也有rewind/fastforward/pause个按钮,所以音频传回时需要和帧对齐,所以我不相信我可以只创建一个长的来自较小数据位的音频文件。
您需要将这些音频文件加载为 AudioBuffers and play them through the Web Audio API。
您当前拥有的是数据 URL,它代表完整的音频文件(带有元数据)。
在音频元素中加载所有这些可能确实不是一个好主意,一开始 because some browsers may not let you do so,然后因为 HTMLMediaElement 并不适合完美的时机。
因此您需要首先获取所有这些数据 URL 以取回它们在 ArrayBuffers 中的实际二进制内容,然后您将能够从这些音频文件中提取原始 PCM 音频数据。
// would be the same with data-URLs
const urls = [
"kbgd2jm7ezk3u3x/hihat.mp3",
"h2j6vm17r07jf03/snare.mp3",
"1cdwpm3gca9mlo0/kick.mp3",
"h8pvqqol3ovyle8/tom.mp3"
].map( (path) => 'https://dl.dropboxusercontent.com/s/' + path );
const audio_ctx = new AudioContext();
Promise.all( urls.map( toAudioBuffer ) )
.then( activateBtn )
.catch( console.error );
async function toAudioBuffer( url ) {
const resp = await fetch( url );
const arr_buffer = await resp.arrayBuffer();
return audio_ctx.decodeAudioData( arr_buffer );
}
function activateBtn( audio_buffers ) {
const btn = document.getElementById( 'btn' );
btn.onclick = playInSequence;
btn.disabled = false;
// simply play one after the other
// you could add your own logic of course
async function playInSequence() {
await audio_ctx.resume(); // to make noise we need to be allowed by a user gesture
let current = 0;
while( current < audio_buffers.length ) {
// create a bufferSourceNode, no worry, it weights nothing
const source = audio_ctx.createBufferSource();
source.buffer = audio_buffers[ current ];
// so it makes noise
source.connect( audio_ctx.destination );
// [optional] promisify
const will_stop = new Promise( (res) => source.onended = res );
source.start(0); // start playing now
await will_stop;
current ++;
}
}
}
<button id="btn" disabled>play all in sequence</button>
我最终在声音 ID 索引的 javascript 中创建了一个数组,它与帧 ID 相对应,并且在对象内部它包含一个在我解析标签时创建的音频元素。元素没有添加到 DOM 中,它们是预先创建的,所以它们在帧处理程序的生命周期内一直存在(因为它们存储在对象内部的声音数组中),所以没有create/destory成本。
这样,当我播放帧(视觉效果)时,我可以在与活动帧对应的音频元素上调用播放。由于帧控制播放哪个音频,因此 rewind/fastforward 功能得以保留。