简体   繁体   English

如何使用 js-mp3 解码 MP3 并在 AudioContext 中播放?

[英]How do I decode MP3 with js-mp3 and play in AudioContext?

Because of a bug in Safari 15 that sometimes causes AudioContext.decodeAudioData to fail (see Safari 15 fails to decode audio data that previous versions decoded without problems ) for normal MP3 files I'm trying to do a workaround.由于 Safari 15 中的错误有时会导致AudioContext.decodeAudioData对于普通 MP3 文件失败(请参阅Safari 15 无法解码以前版本解码的音频数据没有问题),我正在尝试解决方法。 The workaround is decoding the files with the library https://github.com/soundbus-technologies/js-mp3 , then creating an AudioBuffer from that data and playing that.解决方法是使用库https://github.com/soundbus-technologies/js-mp3解码文件,然后从该数据创建一个AudioBuffer并播放它。

The problem is that js-mp3 returns one ArrayBuffer with PCM data, and creating an AudioBuffer requires two seperate arrays, one for each channel, and the sampleRate and sample frame length.问题是 js-mp3 返回一个带有 PCM 数据的ArrayBuffer ,而创建一个AudioBuffer需要两个单独的数组,每个通道一个,以及 sampleRate 和样本帧长度。 What I've got so far is:到目前为止我所得到的是:


function concatTypedArrays(a, b) { // a, b TypedArray of same type
    var c = new (a.constructor)(a.length + b.length);
    c.set(a, 0);
    c.set(b, a.length);
    return c;
};

// responseData is an ArrayBuffer with the MP3 file...
let decoder = Mp3.newDecoder(responseData);
let pcmArrayBuffer = decoder.decode();

//Trying to read the frames to get the two channels. Maybe get it correctly from
//the pcmArrayBuffer instead?
    
decoder.source.pos = 0;
let left = new Float32Array(), right = new Float32Array();
console.log('Frame count: ' + decoder.frameStarts.length);
let result;
let i = 0;
let samplesDecoded = 0;
                    
while (true) {

    let result = decoder.readFrame();
    if (result.err) {
        break;
    } else {
        console.log('READ FRAME ' + (++i));
        samplesDecoded += 1152; //Think this is the right sample count per frame for MPEG1 files
        left = concatTypedArrays(left, decoder.frame.v_vec[0]);
        right = concatTypedArrays(left, decoder.frame.v_vec[1]);
    }
}

let audioContext = new AudioContext();
let buffer = audioContext.createBuffer(2, samplesDecoded, decoder.sampleRate);
let source = audioContext.createBufferSource();
source.buffer = buffer;
source.connect(audioContext.destination);
source.start(0);

Now, this sort of works, in that I do hear sounds and I can hear they are the right sounds but they are weirdly distorted.现在,这种作品,因为我确实听到了声音,我可以听到它们是正确的声音,但它们被奇怪地扭曲了。 An example sound file I'm trying to play is https://cardgames.io/mahjongs/sounds/selecttile.mp3我正在尝试播放的示例声音文件是https://cardgames.io/mahjongs/sounds/selecttile.mp3

Any ideas what is wrong here?任何想法这里有什么问题? Or how to convert the single PCM array buffer that is returned from the .decode() function correctly to the format needed to play it properly?或者如何将从.decode()函数返回的单个 PCM 数组缓冲区正确转换为正确播放所需的格式?

The example that fdcpp linked above shows that the ArrayBuffer returned by decoder.decode() can be used to write it to a WAV file without any further modification.上面fdcpp链接的示例表明, decoder.decode()返回的ArrayBuffer可用于将其写入 WAV 文件,而无需进一步修改。 This means the data must be interleaved PCM data.这意味着数据必须是交错的 PCM 数据。

It should therefore work when converting the data back to floating point values.因此,它应该在将数据转换回浮点值时起作用。 Additionally it must be put into planar arrays as expected by the Web Audio API.此外,它必须按照 Web Audio API 的预期放入平面阵列中。

const interleavedPcmData = new DataView(pcmArrayBuffer);
const numberOfChannels = decoder.frame.header.numberOfChannels();
const audioBuffer = new AudioBuffer({
    length: pcmArrayBuffer.byteLength / 2 / numberOfChannels,
    numberOfChannels,
    sampleRate: decoder.sampleRate
});
const planarChannelDatas = [];

for (let i = 0; i < numberOfChannels; i += 1) {
    planarChannelDatas.push(audioBuffer.getChannelData(i));
}

for (let i = 0; i < interleavedPcmData.byteLength; i += 2) {
    const channelNumber = i / 2 % numberOfChannels;
    const value = interleavedPcmData.getInt16(i, true);

    planarChannelDatas[channelNumber][Math.floor(i / 2 / numberOfChannels)]
        = value < 0
            ? value / 32768
            : value / 32767;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM