javascriptwavaudio-processingaudioformat

Is there anything special I have to do to create a 24-bit WAV file?


I can successfully create a 16-bit wav file, but when creating a 24-bit file, all I hear is white noise. I'm setting 24-bit signed integer data chunks. Do I have to set some special audio format at byte 20 in the wav file header? I'm currently using format 1.

Edit #1

The wBitsPerSample field is set to 24. The wAvgBytesPerSec (byte rate) field is set to

// 44100 * (2 * 3)
sampleRate * blockAlign

and wBlockAlign is set to

// 2 * 3
numChannels * bytesPerSampe

Assuming you already did, the data itself needs to be 24-bit butt-to-butt (requires bit-shifting and masking unless the data already is from a 24-bit byte source). Or put differently: each sample will take 3 bytes. And as with 16-bit the byte-order matters.

I don't know if I got this part right, though.

My data comes in the form of Float32 that goes from -1 to 1 (AudioBuffer.getChannelData()). I then convert it to Int24:

function floatTo24BitPCM(output, offset, input) {
  for (var i = 0; i < input.length; i++, offset += 3) {
    var s = Math.floor(input[i] * 8388608 + 0.5)
    output.setInt24(offset, s, true)
  }
}

and

DataView.prototype.setInt24 = function(pos, val, littleEndian) {
    this.setInt16(pos, val >> 8, littleEndian);
    this.setInt8(pos+2, val & ~4294967040, littleEndian); // this "magic number" masks off the first 16 bits
}

Does this process respect the specification (it seems like I'm doit bit shifting and masking in setInt24)?

Edit #2

Modifying

DataView.prototype.setInt24 = function(pos, val, littleEndian) {
    this.setInt16(pos, val >> 8, littleEndian);
    this.setInt8(pos+2, val & ~4294967040, littleEndian); // this "magic number" masks off the first 16 bits
}

to

DataView.prototype.setUint24 = function(pos, val, littleEndian) {
    this.setUint8(pos, val & ~4294967040, littleEndian); // this "magic number" masks off the first 16 bits
    this.setUint16(pos + 1, val >> 8, littleEndian);
}

did the trick. I assume that the byte-order was incorrect? I'm confused as to why taking the last byte and putting it before the first two bytes is the correct order.

I also suggest that this question is re-opened as it is narrow enough to not be considered as "broad" anymore.


Solution

  • From the limited context in the post -

    The field wBitsPerSample (inside fmt->fmt-ck format specific chunk, using 1 (PCM) for wFormatTag is fine) must reflect number of bits per sample, ie. 24 in this case.

    Assuming you already did, the data itself needs to be 24-bit butt-to-butt (requires bit-shifting and masking unless the data already is from a 24-bit byte source). Or put differently: each sample will take 3 bytes. And as with 16-bit the byte-order matters.

    wAvgBytesPerSec and wBlockAlign are calculated as before just with the new bit size.