UNPKG

wav-encoder

Version:
201 lines (171 loc) 5.53 kB
"use strict"; function encodeSync(audioData, opts) { opts = opts || {}; audioData = toAudioData(audioData); if (audioData === null) { throw new TypeError("Invalid AudioData"); } var floatingPoint = !!(opts.floatingPoint || opts.float); var bitDepth = floatingPoint ? 32 : ((opts.bitDepth|0) || 16); var bytes = bitDepth >> 3; var length = audioData.length * audioData.numberOfChannels * bytes; var dataView = new DataView(new Uint8Array(44 + length).buffer); var writer = createWriter(dataView); var format = { formatId: floatingPoint ? 0x0003 : 0x0001, floatingPoint: floatingPoint, numberOfChannels: audioData.numberOfChannels, sampleRate: audioData.sampleRate, bitDepth: bitDepth }; writeHeader(writer, format, dataView.buffer.byteLength - 8); var err = writeData(writer, format, length, audioData, opts); if (err instanceof Error) { throw err; } return dataView.buffer; } function encode(audioData, opts) { return new Promise(function(resolve) { resolve(encodeSync(audioData, opts)); }); } function toAudioData(data) { var audioData = {}; if (typeof data.sampleRate !== "number") { return null; } if (!Array.isArray(data.channelData)) { return null; } if (!(data.channelData[0] instanceof Float32Array)) { return null; } audioData.numberOfChannels = data.channelData.length; audioData.length = data.channelData[0].length|0; audioData.sampleRate = data.sampleRate|0; audioData.channelData = data.channelData; return audioData; } function writeHeader(writer, format, length) { var bytes = format.bitDepth >> 3; writer.string("RIFF"); writer.uint32(length); writer.string("WAVE"); writer.string("fmt "); writer.uint32(16); writer.uint16(format.floatingPoint ? 0x0003 : 0x0001); writer.uint16(format.numberOfChannels); writer.uint32(format.sampleRate); writer.uint32(format.sampleRate * format.numberOfChannels * bytes); writer.uint16(format.numberOfChannels * bytes); writer.uint16(format.bitDepth); } function writeData(writer, format, length, audioData, opts) { var bitDepth = format.bitDepth; var encoderOption = format.floatingPoint ? "f" : opts.symmetric ? "s" : ""; var methodName = "pcm" + bitDepth + encoderOption; if (!writer[methodName]) { return new TypeError("Not supported bit depth: " + bitDepth); } var write = writer[methodName].bind(writer); var numberOfChannels = format.numberOfChannels; var channelData = audioData.channelData; writer.string("data"); writer.uint32(length); for (var i = 0, imax = audioData.length; i < imax; i++) { for (var ch = 0; ch < numberOfChannels; ch++) { write(channelData[ch][i]); } } } function createWriter(dataView) { var pos = 0; return { int16: function(value) { dataView.setInt16(pos, value, true); pos += 2; }, uint16: function(value) { dataView.setUint16(pos, value, true); pos += 2; }, uint32: function(value) { dataView.setUint32(pos, value, true); pos += 4; }, string: function(value) { for (var i = 0, imax = value.length; i < imax; i++) { dataView.setUint8(pos++, value.charCodeAt(i)); } }, pcm8: function(value) { value = Math.max(-1, Math.min(value, +1)); value = (value * 0.5 + 0.5) * 255; value = Math.round(value)|0; dataView.setUint8(pos, value, true); pos += 1; }, pcm8s: function(value) { value = Math.round(value * 128) + 128; value = Math.max(0, Math.min(value, 255)); dataView.setUint8(pos, value, true); pos += 1; }, pcm16: function(value) { value = Math.max(-1, Math.min(value, +1)); value = value < 0 ? value * 32768 : value * 32767; value = Math.round(value)|0; dataView.setInt16(pos, value, true); pos += 2; }, pcm16s: function(value) { value = Math.round(value * 32768); value = Math.max(-32768, Math.min(value, 32767)); dataView.setInt16(pos, value, true); pos += 2; }, pcm24: function(value) { value = Math.max(-1, Math.min(value, +1)); value = value < 0 ? 0x1000000 + value * 8388608 : value * 8388607; value = Math.round(value)|0; var x0 = (value >> 0) & 0xFF; var x1 = (value >> 8) & 0xFF; var x2 = (value >> 16) & 0xFF; dataView.setUint8(pos + 0, x0); dataView.setUint8(pos + 1, x1); dataView.setUint8(pos + 2, x2); pos += 3; }, pcm24s: function(value) { value = Math.round(value * 8388608); value = Math.max(-8388608, Math.min(value, 8388607)); var x0 = (value >> 0) & 0xFF; var x1 = (value >> 8) & 0xFF; var x2 = (value >> 16) & 0xFF; dataView.setUint8(pos + 0, x0); dataView.setUint8(pos + 1, x1); dataView.setUint8(pos + 2, x2); pos += 3; }, pcm32: function(value) { value = Math.max(-1, Math.min(value, +1)); value = value < 0 ? value * 2147483648 : value * 2147483647; value = Math.round(value)|0; dataView.setInt32(pos, value, true); pos += 4; }, pcm32s: function(value) { value = Math.round(value * 2147483648); value = Math.max(-2147483648, Math.min(value, +2147483647)); dataView.setInt32(pos, value, true); pos += 4; }, pcm32f: function(value) { dataView.setFloat32(pos, value, true); pos += 4; } }; } module.exports.encode = encode; module.exports.encode.sync = encodeSync;