Debido a un error en Safari 15 que a veces hace que AudioContext.decodeAudioData falle (consulte Safari 15 no puede decodificar datos de audio que las versiones anteriores decodificaron sin problemas ) para archivos MP3 normales, estoy tratando de solucionarlo. La solución es decodificar los archivos con la biblioteca https://github.com/soundbus-technologies/js-mp3 , luego crear un AudioBuffer a partir de esos datos y reproducirlos.
El problema es que js-mp3 devuelve un ArrayBuffer con datos PCM, y la creación de un AudioBuffer requiere dos arreglos separados, uno para cada canal, y el sampleRate y la longitud del cuadro de muestra. Lo que tengo hasta ahora es:
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);Ahora, este tipo de trabajos, en el sentido de que escucho sonidos y puedo escuchar que son los sonidos correctos pero están extrañamente distorsionados. Un archivo de sonido de ejemplo que intento reproducir es https://cardgames.io/mahjong/sounds/selecttile.mp3
¿Alguna idea de lo que está mal aquí? ¿O cómo convertir correctamente el búfer de matriz PCM único que se devuelve desde la función .decode() al formato necesario para reproducirlo correctamente?
El ejemplo que fdcpp vinculó anteriormente muestra que el ArrayBuffer devuelto por decoder.decode() se puede usar para escribirlo en un archivo WAV sin ninguna modificación adicional. Esto significa que los datos deben ser datos PCM intercalados.
Por lo tanto, debería funcionar al convertir los datos nuevamente a valores de coma flotante. Además, debe colocarse en matrices planas como lo espera la API de audio web.
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; }