All files / src/utils/audio pcmFloat32ToWAV.ts

0% Statements 0/43
0% Branches 0/2
0% Functions 0/2
0% Lines 0/41

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78                                                                                                                                                           
export function pcmFloat32ToWAV(audioBuffer: AudioBuffer): ArrayBuffer {
    const numChannels = audioBuffer.numberOfChannels;
    const numSamples = audioBuffer.length;
    const numBitsPerSample = 16;
    const dataChunkSize = (numSamples * numChannels * numBitsPerSample) / 8;
    const sampleRate = audioBuffer.sampleRate;
 
    const arrayBuffer = new ArrayBuffer(44 + dataChunkSize);
    const dataView = new DataView(arrayBuffer);
 
    let byteOffset = 0;
 
    function writeString(s: string) {
        for (let i = 0; i < s.length; i++, byteOffset++) {
            dataView.setUint8(byteOffset, s.charCodeAt(i));
        }
    }
 
    /** RIFF header */
    writeString("RIFF"); /* RIFF */
 
    dataView.setUint32(byteOffset, 36 + dataChunkSize, true); /* Chunk size */
    byteOffset += 4;
 
    writeString("WAVE"); /* Format */
 
    /** FMT */
    writeString("fmt "); /* Sub-chunk 1 ID */
 
    dataView.setUint32(byteOffset, 16, true); /* Sub-chunk 1 size */
    byteOffset += 4;
 
    /* Audio format */
    dataView.setUint16(byteOffset, 1, true);
    byteOffset += 2;
 
    /* Number of channels */
    dataView.setUint16(byteOffset, numChannels, true);
    byteOffset += 2;
 
    /* Sample rate */
    dataView.setUint32(byteOffset, sampleRate, true);
    byteOffset += 4;
 
    /* Byte rate */
    dataView.setUint32(byteOffset, (sampleRate * numChannels * numBitsPerSample) / 8, true);
    byteOffset += 4;
 
    /* Block align */
    dataView.setUint16(byteOffset, (numChannels * numBitsPerSample) / 8, true);
    byteOffset += 2;
 
    /* Number of bits per sample */
    dataView.setUint16(byteOffset, numBitsPerSample, true);
    byteOffset += 2;
 
    /** DATA */
    writeString("data"); /* Sub-chunk 2 ID */
 
    dataView.setUint32(byteOffset, dataChunkSize); /* Sub-chunk 2 size */
    byteOffset += 4;
 
    let channelOffset = 0;
 
    while (byteOffset < arrayBuffer.byteLength) {
        for (let i = 0; i < numChannels; i++) {
            const sampleValue = Math.max(-1, Math.min(1, audioBuffer.getChannelData(i)[channelOffset]));
            const int16SampleValue = Math.trunc(sampleValue < 0 ? sampleValue * 0x8000 : sampleValue * 0x7fff);
            dataView.setInt16(byteOffset, int16SampleValue, true);
            byteOffset += 2;
        }
 
        channelOffset++;
    }
 
    return arrayBuffer;
}