Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
<!DOCTYPE html>
<html>
<body>
</body>
<script>
// Load sound
var xhr = new XMLHttpRequest();
xhr.open('GET', '//localhost/file.wav', true);
xhr.onprogress = function (evt) {
if (evt.lengthComputable) {
console.log("Progress: " + evt.loaded + "/" + evt.total + " (" + (100*evt.loaded/evt.total) + "%)");
} else {
console.log("Progress: unavailable");
}
};
var audCtx = new AudioContext();
window.data_bufSrc = undefined;
xhr.onload = function () {
console.log("File loaded.");
var d = new DataView(xhr.response);
if (d.getUint32(0) != 0x52494646 || d.getUint32(8) != 0x57415645) {
console.error('Not a .wav file!');
return;
}
// Read metadata
var data = {};
var meta = false;
var file_length = d.getUint32(4, true) + 8;
console.log('Length: ' + file_length);
var current_offset = 12;
var audioBuffer;
while (current_offset < xhr.response.byteLength) {
// Chunk header
var chunk_type = d.getUint32(current_offset);
var chunk_size = d.getUint32(current_offset+4, true);
current_offset += 8;
console.log("Chunk: " + chunk_type + "; size=" + chunk_size);
if (chunk_type == 0x666d7420) { // "fmt "
if (chunk_size < 14) {
console.error('Malformed wav file: invalid wav header size');
return;
}
console.log('Found meta chunk!');
data.format = d.getUint16(current_offset, true);
data.num_chan = d.getUint16(current_offset+2, true);
data.sample_rate = d.getUint32(current_offset+4, true);
data.byte_rate = d.getUint32(current_offset+8, true);
data.block_align = d.getUint16(current_offset+12, true);
if (chunk_size >= 16)
data.bps = d.getUint16(current_offset+14, true);
else
data.bps = 16; // FIXME not sure on this...
if (chunk_size >= 18)
data.extraSize = d.getUint16(current_offset+16, true);
if (data.format == 0xFFFE) { // Wave Format Extensible
if (!data.extraSize || data.extraSize < 22) {
console.error('WAVEEXT Format Error!');
return;
}
data.sample = d.getUint16(current_offset+18, true);
data.channel_mask = d.getUint32(current_offset+20, true);
data.guid = readByteArray(d, current_offset+24, 16);
// GUID
var intLPCM = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71];
var floatLPCM = [0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71];
if (arraysEqual(data.guid, intLPCM)) {
console.log('Integer LPCM found.');
} else if (arraysEqual(data.guid, floatLPCM)) {
console.log('Float LPCM found.');
} else {
console.error("Invalid data format!");
return;
}
} else if (data.format == 1) { // Integer LPCM
console.log('Integer LPCM found.');
} else if (data.format == 3) {
console.log('Float LPCM found.');
} else {
console.error("Invalid data format!");
return;
}
meta = true;
current_offset += chunk_size;
} else if (chunk_type == 0x64617461) { // "data"
if (!meta) {
console.error('Malformed wav file: data chunk before fmt chunk');
return;
}
console.log('Found data chunk!');
// Calculate audio length
var bytePerSample = data.num_chan * data.bps / 8;
var bytePerSecond = bytePerSample * data.sample_rate;
var second = chunk_size / bytePerSecond;
var sampleCount = chunk_size / bytePerSample;
console.log("File length: %d (%d:%d)", second, second/60, second%60);
// Create AudioBuffer
audioBuffer = audCtx.createBuffer(data.num_chan, sampleCount, data.sample_rate);
var chanBuff = [];
for (var i = 0; i < data.num_chan; i++) {
chanBuff[i] = audioBuffer.getChannelData(i);
}
// Read data to AudioBuffer
var ofst = current_offset;
for (var sample = 0; sample < sampleCount; sample++) {
for (var i = 0; i < data.num_chan; i++) {
switch (data.bps) { // Integer only for now
case 8:
var dat = d.getUint8(ofst);
chanBuff[i][sample] = (dat - 128) / 128;
break;
case 16:
var dat = d.getInt16(ofst, true);
chanBuff[i][sample] = dat / (1 << 15);
break;
case 32:
var dat = d.getInt32(ofst, true);
chanBuff[i][sample] = dat / (1 << 31);
break;
}
ofst += data.bps / 8;
}
}
console.log("Demuxing complete.")
current_offset += chunk_size;
break; // stop processing
} else { // we are skipping 'fact' chunk if present
// Bypass chunk
console.log("Skipping: " + chunk_type);
current_offset += chunk_size;
}
}
window.data_bufSrc = audCtx.createBufferSource();
window.data_bufSrc.buffer = audioBuffer;
window.data_bufSrc.connect(audCtx.destination);
window.data_bufSrc.start(0);
console.log("Done!");
}
function arraysEqual(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length != b.length) return false;
for (var i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
function readByteArray(d, o, s) {
var r = [];
for (var i = 0; i < s; i++) {
r.push(d.getUint8(o+i));
}
return r;
}
xhr.responseType = "arraybuffer";
xhr.send();
console.log("Loading file...");
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.