shared-utils
Version:
contains useful utilities to read/write WAV file to/from a buffer, various float to int conversions, 32 bit float to 16 bit int conversion, random number generator for ints or floats in which optionally uses same random sequence across runs
1,342 lines (823 loc) • 46.3 kB
JavaScript
(function(exports) {
// console.log(require('../package.json').name, require('../package.json').version);
var shared_utils = require('./shared_utils');
var fs = require('fs');
// ---
exports.get_random_in_range_inclusive_float = shared_utils.get_random_in_range_inclusive_float;
exports.get_random_in_range_inclusive_int = shared_utils.get_random_in_range_inclusive_int;
exports.set_random_seed = shared_utils.set_random_seed;
exports.diff_buffers = shared_utils.diff_buffers;
exports.show_object = shared_utils.show_object;
exports.release_all_prop_from_object = shared_utils.release_all_prop_from_object;
exports.convert_32_bit_float_into_unsigned_16_bit_int_lossy = shared_utils.convert_32_bit_float_into_unsigned_16_bit_int_lossy;
exports.convert_16_bit_unsigned_int_to_32_bit_float = shared_utils.convert_16_bit_unsigned_int_to_32_bit_float;
exports.convert_16_bit_signed_int_to_32_bit_float = shared_utils.convert_16_bit_signed_int_to_32_bit_float;
exports.convert_32_bit_float_into_signed_16_bit_int_lossy = shared_utils.convert_32_bit_float_into_signed_16_bit_int_lossy;
// ---
/* SEE shared_utils.convert_16_bit_signed_int_to_32_bit_float
var convert_16_bit_signed_ints_into_32_bit_floats = function(audio_buffer) {
var size_buffer = audio_buffer.length;
var buffer_32_bit_floats = new Float32Array(~~(size_buffer / 2)); // integer division by 2
var index_float = 0;
for (var index_16_bit = 0; index_16_bit < size_buffer;) {
buffer_32_bit_floats[index_float] = -1 + (audio_buffer[index_16_bit] | (audio_buffer[index_16_bit + 1] << 8)) / 32768;
index_16_bit += 2;
index_float++;
}
return buffer_32_bit_floats;
};
exports.convert_16_bit_signed_ints_into_32_bit_floats = convert_16_bit_signed_ints_into_32_bit_floats;
*/
// ---
var toFixed = function(value, precision) {
var sign_or_padding = " ";
if (value < 0) sign_or_padding = "";
var power = Math.pow(10, precision || 0);
return sign_or_padding + String(Math.round(value * power) / power);
};
exports.toFixed = toFixed;
var copy_properties_across_objects = function(input_obj, output_obj) {
for (var property in input_obj) {
output_obj[property] = input_obj[property];
}
};
exports.copy_properties_across_objects = copy_properties_across_objects;
var list_files_in_dir_sync = function(dir, files_) {
var files_ = files_ || [];
if (! fs.existsSync(dir)) {
// directory does not exist - silently return
return files_;
}
if (typeof files_ === 'undefined') { files_=[]; };
var files = fs.readdirSync(dir);
for(var i in files) {
if (! files.hasOwnProperty(i)) continue;
var name = dir +'/' + files[i];
if (fs.statSync(name).isDirectory()) {
list_files_in_dir_sync(name,files_);
} else {
files_.push(name);
}
}
return files_;
}
exports.list_files_in_dir_sync = list_files_in_dir_sync;
var inner_parse_header = function(wav_input_file_obj, property_input_buffer, cb_when_done) {
// console.log("TOP inner_parse_header");
// http://stackoverflow.com/questions/19991405/how-can-i-detect-whether-a-wav-file-has-a-44-or-46-byte-header?lq=1
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Samples.html
// var raw_buffer = wav_input_file_obj.raw_buffer; // entire contents of input file which is parsed
var local_input_buffer = wav_input_file_obj[property_input_buffer]; // entire contents of input file which is parsed
// console.log("top of parse_wav +++++++++++++++++++++ local_input_buffer.length ", local_input_buffer.length,
// " typeof local_input_buffer ", typeof local_input_buffer,
// " instanceof local_input_buffer ", (local_input_buffer instanceof Array) ? "Array" : "other"
// );
var size_header = 44;
var offset = 0;
var RIFF = new Buffer.alloc(4); // these MUST remain size 4 ... Resource Interchange File Format
var WAVE = new Buffer.alloc(4);
var fmt = new Buffer.alloc(4);
var data = new Buffer.alloc(4);
local_input_buffer.copy(RIFF, 0, offset, RIFF.length); // chunckID 0 offset 4 bytes
offset += 4;
// console.log("is this RIFF or what ",RIFF.toString('ascii',0,RIFF.length)," RIFF.length ",RIFF.length);
if (RIFF != "RIFF") {
if (RIFF == " RIFX") {
cb_when_done("ERROR - this WAV file is stored Big Endian - currently we only handle Little Endian",
wav_input_file_obj);
return;
} else {
cb_when_done("ERROR - failed to see RIFF at top of input WAV file parse, or RIFX for that matter" +
" instead saw : " + RIFF, wav_input_file_obj);
return;
// https://stackoverflow.com/questions/7310521/node-js-best-practice-exception-handling
}
}
var chunckSize;
chunckSize = local_input_buffer.readUInt32LE(offset); // chunckSize 4 offset 4 bytes
offset += 4;
// console.log("on read ... chunckSize ", chunckSize);
local_input_buffer.copy(WAVE, 0, offset, offset + WAVE.length); // format 8 offset 4 bytes
offset += 4;
// console.log("on read ... WAVE is what ", WAVE.toString('ascii', 0, WAVE.length), " WAVE.length ", WAVE.length);
local_input_buffer.copy(fmt, 0, offset, offset + fmt.length);// subchunk1ID 12 offset 4 bytes
offset += 4;
// console.log("on read ... fmt is what ", fmt.toString('ascii', 0, fmt.length), " fmt.length ", fmt.length);
wav_input_file_obj.pcm_format = local_input_buffer.readUInt32LE(offset); // subchunk1Size 16 offset 4 bytes
offset += 4;
// console.log("on read ... pcm_format ", wav_input_file_obj.pcm_format);
// valid values of Chunk size : 16 or 18 or 40
wav_input_file_obj.audio_format = local_input_buffer.readUInt16LE(offset); // audioFormat 20 offset 2 bytes
offset += 2;
// console.log('on read ... audio_format ', wav_input_file_obj.audio_format);
wav_input_file_obj.num_channels = local_input_buffer.readUInt16LE(offset); // numChannels 22 offset 2 bytes
offset += 2;
// console.log('on read ... num_channels ', wav_input_file_obj.num_channels);
// Number of interleaved channels
wav_input_file_obj.sample_rate = local_input_buffer.readUInt32LE(offset); // sampleRate 24 offset 4 bytes
offset += 4;
// console.log('on read ... sample_rate ', wav_input_file_obj.sample_rate);
// blocks per second
wav_input_file_obj.byte_rate = local_input_buffer.readUInt32LE(offset); // byteRate 28 offset 4 bytes
offset += 4;
// console.log("on read ... byte_rate ", wav_input_file_obj.byte_rate);
wav_input_file_obj.bit_depth = (wav_input_file_obj.byte_rate * 8.0) /
(wav_input_file_obj.sample_rate * wav_input_file_obj.num_channels);
// console.log("on read ... bit_depth ", wav_input_file_obj.bit_depth);
// average bytes per second - data rate
wav_input_file_obj.block_align = local_input_buffer.readUInt16LE(offset); // blockAlign 32 offset 2 bytes
offset += 2;
// console.log("on read ... block_align ", wav_input_file_obj.block_align);
// data block size in bytes
wav_input_file_obj.bits_per_sample = local_input_buffer.readUInt16LE(offset); // bitsPerSample 34 offset 2 bytes
offset += 2;
// console.log("on read ... bits_per_sample ", wav_input_file_obj.bits_per_sample);
// bits per sample
local_input_buffer.copy(data, 0, offset, offset + data.length); // subchunk2ID 36 offset 4 bytes
offset += 4;
// console.log("data is what ", data.toString('ascii', 0, data.length), " data.length ", data.length);
var subchunk2Size;
subchunk2Size = local_input_buffer.readUInt32LE(offset); // subchunk2Size 36 offset 4 bytes
offset += 4;
// console.log("subchunk2Size ", subchunk2Size);
if (! (size_header == offset)) {
cb_when_done("ERROR - input file header must contain " + size_header +
" bytes it incorrectly contains : " + offset, wav_input_file_obj);
};
delete wav_input_file_obj[property_input_buffer]; // zaps off property header_buffer
cb_when_done(null, wav_input_file_obj);
// end of read header
}; // inner_parse_header
// ---
var parse_wav_header = function(wav_input_file_obj, requested_input_filename, db_done) {
var header_chunk_size = 44;
var property_input_buffer = "header_buffer";
fs.open(requested_input_filename, 'r', function(error, fd) {
if (error) {
db_done("ERROR - failed to open file " + requested_input_filename +
" message : " + error.message, wav_input_file_obj);
} else {
wav_input_file_obj[property_input_buffer] = new Buffer.alloc(header_chunk_size);
fs.read(fd, wav_input_file_obj[property_input_buffer], 0, header_chunk_size, 0, function(err, end_index) {
// console.log("wav_input_file_obj[property_input_buffer] ",
// wav_input_file_obj[property_input_buffer].toString('utf-8', 0, end_index));
// console.log("wav_input_file_obj[property_input_buffer] ", wav_input_file_obj[property_input_buffer]);
// console.log("end_index ", end_index);
inner_parse_header(wav_input_file_obj, property_input_buffer, db_done);
});
}
});
};
exports.parse_wav_header = parse_wav_header;
// ---
var parse_wav = function(wav_input_file_obj, property_input_buffer, property_output_buffer) {
// http://stackoverflow.com/questions/19991405/how-can-i-detect-whether-a-wav-file-has-a-44-or-46-byte-header?lq=1
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Samples.html
// var raw_buffer = wav_input_file_obj.raw_buffer; // entire contents of input file which is parsed
var local_input_buffer = wav_input_file_obj[property_input_buffer]; // entire contents of input file which is parsed
// console.log("top of parse_wav +++++++++++++++++++++ local_input_buffer.length ", local_input_buffer.length,
// " typeof local_input_buffer ", typeof local_input_buffer,
// " instanceof local_input_buffer ", (local_input_buffer instanceof Array) ? "Array" : "other"
// );
var size_header = 44;
var offset = 0;
var RIFF = new Buffer.alloc(4); // these MUST remain size 4 ... Resource Interchange File Format
var WAVE = new Buffer.alloc(4);
var fmt = new Buffer.alloc(4);
var data = new Buffer.alloc(4);
local_input_buffer.copy(RIFF, 0, offset, RIFF.length); // chunckID 0 offset 4 bytes
offset += 4;
// console.log("is this RIFF or what ",RIFF.toString('ascii',0,RIFF.length)," RIFF.length ",RIFF.length);
if (RIFF != "RIFF") {
if (RIFF == " RIFX") {
console.error("ERROR - this WAV file is stored Big Endian - currently we only handle Little Endian");
process.exit(8);
} else {
var err_msg = "ERROR - failed to see RIFF at top of input WAV file parse, or RIFX for that matter";
console.error(err_msg);
console.error("instead saw : ", RIFF);
process.exit(8);
// return new Error(err_msg); // stens TODO - have caller to handle this error
// https://stackoverflow.com/questions/7310521/node-js-best-practice-exception-handling
}
}
var chunckSize;
chunckSize = local_input_buffer.readUInt32LE(offset); // chunckSize 4 offset 4 bytes
offset += 4;
// console.log("on read ... chunckSize ", chunckSize);
local_input_buffer.copy(WAVE, 0, offset, offset + WAVE.length); // format 8 offset 4 bytes
offset += 4;
// console.log("on read ... WAVE is what ", WAVE.toString('ascii', 0, WAVE.length), " WAVE.length ", WAVE.length);
local_input_buffer.copy(fmt, 0, offset, offset + fmt.length);// subchunk1ID 12 offset 4 bytes
offset += 4;
// console.log("on read ... fmt is what ", fmt.toString('ascii', 0, fmt.length), " fmt.length ", fmt.length);
wav_input_file_obj.pcm_format = local_input_buffer.readUInt32LE(offset); // subchunk1Size 16 offset 4 bytes
offset += 4;
// console.log("on read ... pcm_format ", wav_input_file_obj.pcm_format);
// valid values of Chunk size : 16 or 18 or 40
wav_input_file_obj.audio_format = local_input_buffer.readUInt16LE(offset); // audioFormat 20 offset 2 bytes
offset += 2;
// console.log('on read ... audio_format ', wav_input_file_obj.audio_format);
wav_input_file_obj.num_channels = local_input_buffer.readUInt16LE(offset); // numChannels 22 offset 2 bytes
offset += 2;
// console.log('on read ... num_channels ', wav_input_file_obj.num_channels);
// Number of interleaved channels
wav_input_file_obj.sample_rate = local_input_buffer.readUInt32LE(offset); // sampleRate 24 offset 4 bytes
offset += 4;
// console.log('on read ... sample_rate ', wav_input_file_obj.sample_rate);
// blocks per second
wav_input_file_obj.byte_rate = local_input_buffer.readUInt32LE(offset); // byteRate 28 offset 4 bytes
offset += 4;
// console.log("on read ... byte_rate ", wav_input_file_obj.byte_rate);
wav_input_file_obj.bit_depth = (wav_input_file_obj.byte_rate * 8.0) /
(wav_input_file_obj.sample_rate * wav_input_file_obj.num_channels);
// console.log("on read ... bit_depth ", wav_input_file_obj.bit_depth);
// average bytes per second - data rate
wav_input_file_obj.block_align = local_input_buffer.readUInt16LE(offset); // blockAlign 32 offset 2 bytes
offset += 2;
// console.log("on read ... block_align ", wav_input_file_obj.block_align);
// data block size in bytes
wav_input_file_obj.bits_per_sample = local_input_buffer.readUInt16LE(offset); // bitsPerSample 34 offset 2 bytes
offset += 2;
// console.log("on read ... bits_per_sample ", wav_input_file_obj.bits_per_sample);
// bits per sample
local_input_buffer.copy(data, 0, offset, offset + data.length); // subchunk2ID 36 offset 4 bytes
offset += 4;
// console.log("data is what ", data.toString('ascii', 0, data.length), " data.length ", data.length);
var subchunk2Size;
subchunk2Size = local_input_buffer.readUInt32LE(offset); // subchunk2Size 36 offset 4 bytes
offset += 4;
// console.log("subchunk2Size ", subchunk2Size);
if (! (size_header == offset)) {
var err_msg = "ERROR - input file header must contain " + size_header +
" bytes it incorrectly contains : " + offset;
console.log(err_msg);
}
// console.log("end of read header ......... offset ", offset);
var size_buffer = wav_input_file_obj[property_input_buffer].length - size_header;
// console.log(" ......... size_buffer ", size_buffer);
// wav_input_file_obj.buffer = new Buffer(size_buffer);
// local_input_buffer.copy(wav_input_file_obj.buffer, 0, offset, offset + size_buffer);
// console.log("end of read payload buffer size ", wav_input_file_obj.buffer.length);
wav_input_file_obj[property_output_buffer] = new Buffer.alloc(size_buffer);
local_input_buffer.copy(wav_input_file_obj[property_output_buffer], 0, offset, offset + size_buffer);
// console.log("end of read payload buffer size ", wav_input_file_obj[property_output_buffer].length);
// -------- now show count number samples
// NumSamples = NumBytes / (NumChannels * BitsPerSample / 8)
// console.log("ssssssssssss size_buffer ", size_buffer);
// console.log("ssssssssssss num_channels ", wav_input_file_obj.num_channels);
// console.log("ssssssssssss bits_per_sample ", wav_input_file_obj.bits_per_sample);
// var num_samples = size_buffer / (wav_input_file_obj.num_channels * wav_input_file_obj.bits_per_sample / 8);
// console.log("ssssssssssss num_samples ", num_samples);
}; // parse_wav
// exports.parse_wav = parse_wav;
// ---
var read_file_into_buffer = function(input_file_obj, property_buffer_raw_input_file,
property_buffer_input_file, cb_post_process, cb_when_done) {
// console.log("thuthuthu IIIIIIIII inside read_file_into_buffer filename ", input_file_obj.filename);
// console.log("thuthuthu IIIIIIIII cb_when_done ", cb_when_done.name);
// console.log("thuthuthu IIIIIIIII cb_post_process ", cb_post_process.name);
var input_read_stream = fs.createReadStream(input_file_obj.filename);
// var max_print_count = 5;
// var curr_print_count = 0;
var current_byte_count = 0;
// var size_limit_buffer;
input_read_stream.on('readable', function() {
var newData;
// console.log('inside READABLEEEEEEEEEE of read_file_into_buffer ');
while (null !== (newData = input_read_stream.read())) {
// if (curr_print_count < max_print_count) {
// console.log('CCCCCC binary newData length this callback cycle is ', newData.length);
// }
// size_limit_buffer = (newData.length < limit_size_input_file_buffer) ?
// newData.length : limit_size_input_file_buffer;
// console.log("size_limit_buffer ", size_limit_buffer);
input_file_obj[property_buffer_raw_input_file] =
Buffer.concat([input_file_obj[property_buffer_raw_input_file], newData],
input_file_obj[property_buffer_raw_input_file].length+newData.length);
// var index = 44;
// for (; index < 50; index++) {
// console.log(index, ' SSSSSSSSS ', input_file_obj[property_buffer_raw_input_file][index]);
// };
// input_file_obj[property_buffer_raw_input_file] =
// Buffer.concat([input_file_obj[property_buffer_raw_input_file], newData],
// input_file_obj[property_buffer_raw_input_file].length+size_limit_buffer);
// if (curr_print_count < max_print_count) {
// console.log('binary input_file_obj.raw_buffer length post concat ',
// input_file_obj[property_buffer_raw_input_file].length);
// }
// curr_print_count++;
}
});
input_read_stream.on("error", function (error) {
console.error("ERROR - failure when attempting to read input file : ", input_file_obj.filename, error);
// process.exit(8);
new Error("ERROR - failure when attempting to read input file : ", input_file_obj.filename, error);
});
// Done, process the data
input_read_stream.on("end", function () {
// do something with data read from file - parse_wav
// console.log("INNN read_file_into_buffer with cb_post_process.name ",cb_post_process.name);
cb_post_process(input_file_obj, property_buffer_raw_input_file, property_buffer_input_file, cb_when_done);
// ---
// console.log("post callback to parse_wav with property iteration of input_file_obj");
});
}; // read_file_into_buffer
// this is NOT for export for INTERNAL usage ONLY
// ---
// exports.write_wav = function(wav_file_obj) {
var write_wav = function(wav_file_obj) {
// console.log("~~~~~~~~~ TOP write_wav ~~~~~~~~~");
// --- iterate across all properties of given audio file object to see things like sample_rate
// var sampleRate = 44100; // defaults to be overridden in below switch statement
// var bitDepth = 16;
// var numChannels = 1;
// var pcm_format = 16;
// var audio_format = 1; // raw PCM
var sample_rate = 44100; // defaults to be overridden in below switch statement
var bit_depth = 16;
var num_channels = 1;
var pcm_format = 16; // valid values of Chunk size : 16 or 18 or 40
var audio_format = 1; // raw PCM
var path = "unknown_filename_must_populate_filename";
// var data_length = wav_file_obj.buffer.length;
var data_length = "unknown_filename_must_populate_buffer";
// show_object_with_buffer(wav_file_obj, "in write_wav");
// shared_utils.show_object(wav_file_obj, "total",
// "in write_wav", 10);
for (var property in wav_file_obj) {
// console.log("1111111 write_wav ", property, wav_file_obj[property]);
if (wav_file_obj.hasOwnProperty(property)) {
switch (property) {
case "sample_rate" : {
sample_rate = wav_file_obj[property];
break;
}
case "bit_depth" : {
bit_depth = wav_file_obj[property];
break;
}
case "num_channels" : {
num_channels = wav_file_obj[property];
break;
}
case "filename" : {
path = wav_file_obj[property];
break;
}
case "buffer" : {
data_length = wav_file_obj[property].length;
break;
}
// --- default - catch all if not identifed above
default :
console.log("NOTICE - write_wav ignore this ... seeing property NOT on authorized list : ",
property, " value ", wav_file_obj[property]);
process.exit(8);
break;
};
};
};
// console.log("FFFFFFFF sample_rate ", sample_rate);
// console.log("FFFFFFFF bit_depth ", bit_depth);
// console.log("FFFFFFFF num_channels ", num_channels);
// console.log("FFFFFFFF data_length ", data_length);
// console.log("FFFFFFFF filename ", path);
// ---
var size_header = 44; // constant number of bytes in WAV header as per spec
var RIFF = new Buffer.from('RIFF'); // each of these constant MUST remain 4 bytes in size
var WAVE = new Buffer.from('WAVE');
var fmt = new Buffer.from('fmt ');
var data = new Buffer.from('data');
// ---
// var path = wav_file_obj.filename;
// console.log("/////////// about to write wav output file path ", path);
// console.log("size buffer to write ", wav_file_obj.buffer.length); // deal with 1 channel for now
// console.log(shared_utils.checkEndian()); // stens TODO - confirm we are on little endian else fiddle
// ---
// var data_length = wav_file_obj.buffer.length;
var entire_size_file = data_length + size_header;
var write_stream = fs.createWriteStream(path);
// This is here incase any errors occur
write_stream.on('error', function (err) {
console.log("ERROR - seeing error in write_stream");
console.log(err);
// process.exit(8);
return;
});
// ---
write_stream.on('finish', function() {
// console.error('all writes are now complete.');
});
// ---
var header = new Buffer.alloc(size_header);
var offset = 0;
// write the "RIFF" identifier
RIFF.copy(header, offset); // chunckID 0 offset 4 bytes
offset += 4;
var chunckSize = entire_size_file - 8;
// write the file size minus the identifier and this 32-bit int
// header['writeUInt32' + this.endianness](entire_size_file - 8, offset);
header.writeUInt32LE(chunckSize, offset); // chunckSize 4 offset 4 bytes
offset += 4;
// write the "WAVE" identifier
WAVE.copy(header, offset); // format 8 offset 4 bytes
offset += 4;
// write the "fmt " sub-chunk identifier
fmt.copy(header, offset); // subchunk1ID 12 offset 4 bytes
offset += 4;
// write the size of the "fmt " chunk
// XXX: value of 16 is hard-coded for raw PCM format. other formats have
// different size.
// header['writeUInt32' + this.endianness](16, offset);
// var pcm_format = 16;
header.writeUInt32LE(pcm_format, offset); // subchunk1Size 16 offset 4 bytes
offset += 4;
// console.log('write pcm_format ', pcm_format, " post incr offset ", offset);
// valid values of Chunk size : 16 or 18 or 40
// write the audio format code
// header['writeUInt16' + this.endianness](this.format, offset);
// var audio_format = 1; // raw PCM
header.writeUInt16LE(audio_format, offset); // audioFormat 20 offset 2 bytes
offset += 2;
// console.log('write audio_format ', audio_format, " post incr offset ", offset);
// write the number of channels
// var num_channels = 1;
// header['writeUInt16' + this.endianness](this.channels, offset);
header.writeUInt16LE(num_channels, offset); // num_channels 22 offset 2 bytes
offset += 2;
// console.log('write num_channels ', num_channels, " post incr offset ", offset);
// write the sample rate
// var sampleRate = 44100;
// header['writeUInt32' + this.endianness](this.sampleRate, offset);
header.writeUInt32LE(sample_rate, offset); // sampleRate 24 offset 4 bytes
offset += 4;
// console.log('write sample_rate ', sample_rate, " post incr offset ", offset);
// var bitDepth = 16;
// write the byte rate
var byteRate = this.byteRate;
if (null == byteRate) {
byteRate = sample_rate * num_channels * bit_depth / 8;
// console.log("on write byteRate was null so post calculation its ", byteRate);
}
// header['writeUInt32' + this.endianness](byteRate, offset);
header.writeUInt32LE(byteRate, offset); // byteRate 28 offset 4 bytes
offset += 4;
// console.log("on write ... byteRate ", byteRate);
// console.log("on write ... sample_rate ", sample_rate);
// console.log("on write ... num_channels ", num_channels);
// console.log("on write ... bit_depth ", bit_depth);
// console.log('write byteRate ', byteRate, " post incr offset ", offset);
// write the block align
var blockAlign = this.blockAlign;
if (null == blockAlign) {
blockAlign = num_channels * bit_depth / 8;
}
// header['writeUInt16' + this.endianness](blockAlign, offset);
header.writeUInt16LE(blockAlign, offset); // blockAlign 32 offset 2 bytes
offset += 2;
// console.log("on write ... blockAlign ", blockAlign);
// write the bits per sample
var bitsPerSample = bit_depth;
// header['writeUInt16' + this.endianness](this.bitDepth, offset);
header.writeUInt16LE(bitsPerSample, offset); // bitsPerSample 34 offset 2 bytes
offset += 2;
// offset += 2; // filler_01 36 offset 2 bytes - just ignore
// write the "data" sub-chunk ID
data.copy(header, offset); // subchunk2ID 36 offset 4 bytes
offset += 4;
// write the remaining length of the rest of the data
// header['writeUInt32' + this.endianness](dataLength, offset);
var subchunk2Size = data_length;
header.writeUInt32LE(data_length, offset); // subchunk2Size 40 offset 4 bytes
offset += 4;
if (! (44 == offset)) {
var err_msg = "ERROR - input file header must contain " + size_header +
" bytes it incorrectly contains : " + offset;
console.log(err_msg);
// process.exit(4);
return;
}
// console.log("end of write ........... offset ", offset);
// ---
write_stream.write(header);
// ---
var buffer_to_file = new Buffer.from( new Int16Array(wav_file_obj.buffer) );
write_stream.write(buffer_to_file);
// console.log("wav_file_obj.buffer ", wav_file_obj.buffer.length);
// console.log("~~~~~~~~~ BOT write_wav ~~~~~~~~~");
}; // write_wav
// exports.write_wav = write_wav;
// ---
exports.interleave = function(leftChannel, rightChannel) {
var length = leftChannel.length + rightChannel.length;
var result = new Float32Array(length);
var inputIndex = 0;
for (var index = 0; index < length; ){
result[index++] = leftChannel[inputIndex];
result[index++] = rightChannel[inputIndex];
inputIndex++;
}
return result;
};
// ---
var write_json_to_file = function(output_filename, given_json, given_options) {
var options = given_options || 'utf8';
var json_as_string = JSON.stringify(given_json); // convert json into string
try {
fs.writeFileSync(output_filename, json_as_string, options); // or async : writeFile
} catch (err) {
console.log(err);
process.exit(8);
};
// console.log("just wrote to output file ", output_filename);
};
exports.write_json_to_file = write_json_to_file;
// ---
var read_file_retrieve_json = function(input_filename, given_options) {
var options = given_options || 'utf8';
var json_back_to_caller;
try {
json_back_to_caller = JSON.parse(fs.readFileSync(input_filename, given_options));
} catch (err) {
console.log(err);
process.exit(8);
};
// console.log("just read from input file ", input_filename);
return json_back_to_caller;
};
exports.read_file_retrieve_json = read_file_retrieve_json;
// ---
/*
convert_8_bit_buffer_from_32_bit_float_to_16_bit_int = function(input_data, output_data) {
// http://www.html5rocks.com/en/tutorials/webgl/typed_arrays/
var size_one_16_bit_in_bytes = Int16Array.BYTES_PER_ELEMENT;
// Int16Array
console.log("size_one_16_bit_in_bytes ", size_one_16_bit_in_bytes);
console.log("size input_data ", input_data.buffer.length);
console.log("size output_data ", output_data.buffer.length);
var offset = 0;
var index_float = 0;
var buffer_single_byte_sized = new Buffer(4); // holds enough to populate a single float
var number_float;
// var buffer_single_16bit_sized = new Buffer(2); // holds enough to populate a single 16 bit signed int
var size_buff = input_data.buffer.length;
var index_int = 0;
console.log("size_buff ", size_buff);
for (var index = 0; index < size_buff; index += 4) {
// for (var index = 0; index < size_buff; index++) {
// for (var inneri = 0; inneri < 4; inneri++) { // take float sized gulps endian
for (var inneri = 3; inneri >= 0; inneri--) { // take float sized gulps other endian
buffer_single_byte_sized[inneri] = input_data.buffer[index_int];
if (index < 100) {
console.log('rawrawraw ', index, inneri, buffer_single_byte_sized[inneri],
input_data.buffer[index_int]);
}
index_int++;
}
// var view = new jDataView(buffer_single_byte_sized);
var view = new jdataview(buffer_single_byte_sized);
number_float = view.getFloat32(0);
// output_data.buffer[index_float++] = number_float;
// output_data.buffer[index_float++] = number_float;
// var same_value_in_16_bit_signed_int = number_float * (32768 - 1)
var same_value_in_16_bit_signed_int = ~~(number_float * (32768 - 1));
if (index < 20) {
console.log(index, ' binary number_float ', number_float, ' int ',
same_value_in_16_bit_signed_int);
}
output_data.buffer.writeInt16LE(same_value_in_16_bit_signed_int, offset);
offset += 2;
}
// write_json_serialized(big_binary_float);
// write_buffer_to_file(big_binary_float);
// write_buffer_to_file(output_data.buffer);
}; // convert_8_bit_buffer_from_32_bit_float_to_16_bit_int
exports.convert_8_bit_buffer_from_32_bit_float_to_16_bit_int = convert_8_bit_buffer_from_32_bit_float_to_16_bit_int;
*/
// ---
// var convert_32_bit_floats_into_16_bit_ints = function(input_32_bit_float_audio_obj, output_16_bit_audio_obj) {
var convert_32_bit_floats_into_16_bit_signed_ints = function(input_32_bit_float_audio_obj, output_16_bit_audio_obj) {
// MUST assure input float is BOUNDED between +1.0 to -1.0 ONLY
// this is a lossless process - no information is thrown away
// this logic MAINTAINS full information of input ... 32 bit float --> two 16 bit integers
// so NO loss of information - output buffer size is twice input buffer size
// output 16 bit format can be later converted back into identical input 32 bit floats
// DO NOT use this method to convert floats into 16 bit ints as preliminary step to save into WAV format
// because that is a LOSSY process
var size_32_bit_float_buff = input_32_bit_float_audio_obj.buffer.length;
// console.log("size 32 bit float ", size_32_bit_float_buff);
var num_16_bit_chunks_per_32_bit_float = Float32Array.BYTES_PER_ELEMENT /
Uint16Array.BYTES_PER_ELEMENT;
// console.log("num_16_bit_chunks_per_32_bit_float ", num_16_bit_chunks_per_32_bit_float);
var size_16_bit_int_buff = size_32_bit_float_buff * num_16_bit_chunks_per_32_bit_float;
// console.log("size 16 bit ints ", size_16_bit_int_buff); // is OK
output_16_bit_audio_obj.buffer = new Buffer.alloc(size_16_bit_int_buff);// input buffer is 32 bit we want 16 bit so half it
// var one_float = new Float32Array(1);
var one_float_in_2_16_bit_ints;
// var curr_float;
// var index_16_bit_ints = 0;
var offset = 0;
var same_value_in_16_bit_signed_int;
// var local_divisor;
// stens TODO - put below for loop into a try catch so when :
/*
14 ' aboutttttt to show raw float ' 1.2392250299453735
buffer.js:564
throw new TypeError('value is out of bounds');
^
TypeError: value is out of bounds
at checkInt (buffer.js:564:11)
at Buffer.writeInt16LE (buffer.js:642:5)
at convert_32_bit_floats_into_16_bit_ints (node-genome/src/node_utils.js:689:40)
at Object.exports.write_buffer_to_file
when above happens - due to input raw float OUTSIDE boundary +1 <=> -1
*/
for (var index_float = 0; index_float < size_32_bit_float_buff; index_float++) {
// one_float[0] = input_32_bit_float_audio_obj.buffer[index_float];
// if (index_float < 20) {
// console.log(index_float, " one_float ", one_float[0],
// " convert_32_bit_floats_into_16_bit_ints"); // is OK
// }
// var same_value_in_16_bit_signed_int = ~~(one_float[0] * (32768 - 1));
// https://stackoverflow.com/questions/11682804/confusion-in-16-bit-data-type-range
/*
if (one_float[0] < 0) {
same_value_in_16_bit_signed_int = ~~(one_float[0] * (0x8000)); // 32768
} else {
same_value_in_16_bit_signed_int = ~~(one_float[0] * (0x7FFF)); // 32767
}
*/
// local_divisor = (one_float[0] < 0) ? 0x8000 : 0x7FFF; // 0x8000 = 32768 ... 0x7FFF = 32767
// same_value_in_16_bit_signed_int = ~~(one_float[0] * (local_divisor));
// same_value_in_16_bit_signed_int = ~~(one_float[0] * ((one_float[0] < 0) ? 0x8000 : 0x7FFF));// WORKS
// console.log(index_float, " aboutttttt to show raw float ", input_32_bit_float_audio_obj.buffer[index_float]);
same_value_in_16_bit_signed_int = ~~(input_32_bit_float_audio_obj.buffer[index_float] *
((input_32_bit_float_audio_obj.buffer[index_float] < 0) ? 0x8000 : 0x7FFF));
// if (index_float < 20) {
// console.log("same_value_in_16_bit_signed_int ", same_value_in_16_bit_signed_int);
// }
// output_16_bit_audio_obj.buffer[index_16_bit_ints++] = one_float_in_two_16_bit_ints[index_16_bit];
output_16_bit_audio_obj.buffer.writeInt16LE(same_value_in_16_bit_signed_int, offset);
offset += num_16_bit_chunks_per_32_bit_float;
}
// process.exit(3);
}; // convert_32_bit_floats_into_16_bit_ints
// exports.convert_32_bit_floats_into_16_bit_ints = convert_32_bit_floats_into_16_bit_ints;
exports.convert_32_bit_floats_into_16_bit_signed_ints = convert_32_bit_floats_into_16_bit_signed_ints;
// normalize_buffer = function(audio_obj, property_buffer, desired_max, desired_min) {
var normalize_buffer = function(audio_obj, spec) {
var property_buffer = "buffer"; // defaults
var allowed_minimum = -1.0; // defaults
var allowed_maximum = +1.0; // defaults
var really_big_number = 999999.9;
var spec = spec || {};
if (typeof spec.property_buffer !== "undefined") {
property_buffer = spec.property_buffer;
// console.log("seeing input spec with spec.property_buffer ", spec.property_buffer);
};
if (typeof spec.allowed_minimum !== "undefined") {
allowed_minimum = spec.allowed_minimum;
// console.log("seeing input spec with spec.allowed_minimum ", spec.allowed_minimum);
};
if (typeof spec.allowed_maximum !== "undefined") {
allowed_maximum = spec.allowed_maximum;
// console.log("seeing input spec with spec.allowed_maximum ", spec.allowed_maximum);
};
// console.log("here is spec ", spec);
// console.log("here is spec property_buffer ", property_buffer);
// console.log("here is spec allowed_minimum ", allowed_minimum);
// console.log("here is spec allowed_maximum ", allowed_maximum);
// var given_buffer = audio_obj[property_buffer];
var size_buffer = audio_obj[property_buffer].length;
// console.log("size_buffer ", size_buffer);
var observed_min = really_big_number; // pull from some max float constant if possible
var observed_max = -1.0 * really_big_number; // pull from some max float constant if possible
// --- probe input buffer to determine whether any values are outside boundary
for (var index = 0; index < size_buffer; index++) {
var curr_value = audio_obj[property_buffer][index];
// console.log(index, " curr_value ", curr_value);
if (observed_min > curr_value) {
observed_min = curr_value;
} else if (observed_max < curr_value) {
observed_max = curr_value;
}
};
// ---
// console.log("observed_min ", observed_min);
// console.log("observed_max ", observed_max);
// console.log("allowed_minimum ", allowed_minimum);
// console.log("allowed_maximum ", allowed_maximum);
// if (observed_min > allowed_minimum && observed_max < allowed_maximum) {
if (observed_min >= allowed_minimum && observed_max <= allowed_maximum) {
// console.log("OK no work to do ... values already inside boundary");
return; // no work to do ... values already inside boundary
}
// --- do normalize
var allowed_difference_btw_max_N_min = allowed_maximum - allowed_minimum;
var observed_difference_btw_max_N_min = observed_max - observed_min;
// var observed_midpoint = observed_difference_btw_max_N_min / 2.0;
var observed_midpoint = (observed_max + observed_min) / 2.0;
// console.log("allowed_difference_btw_max_N_min ", allowed_difference_btw_max_N_min);
// console.log("observed_difference_btw_max_N_min ", observed_difference_btw_max_N_min);
// console.log("observed_midpoint ", observed_midpoint);
if (observed_difference_btw_max_N_min == 0) {
// seeing flatline input so just shift to zero
// console.log("OK seeing unruly values YET all are same so just shift to zero");
for (var index = 0; index < size_buffer; index++) {
audio_obj[property_buffer][index] = 0.0;
};
} else { // now implement normalize of unruly input values into allowed values inside boundary
// console.log("OK about to normalize values from observed min,max : ", observed_min, observed_max,
// " to allowed min,max : ", allowed_minimum, allowed_maximum);
var correction_factor = allowed_difference_btw_max_N_min / observed_difference_btw_max_N_min;
// console.log("observed_midpoint ", observed_midpoint);
// console.log("correction_factor ", correction_factor);
var post_processing_min = really_big_number;
var post_processing_max = -1.0 * really_big_number;
for (var index = 0; index < size_buffer; index++) {
var prior_value = parseFloat(audio_obj[property_buffer][index]);
// audio_obj[property_buffer][index] = (correction_factor * audio_obj[property_buffer][index]) - observed_midpoint;
// audio_obj[property_buffer][index] = correction_factor * audio_obj[property_buffer][index];
// audio_obj[property_buffer][index] = correction_factor * (1.0 * audio_obj[property_buffer][index] - observed_midpoint);
audio_obj[property_buffer][index] = correction_factor * (audio_obj[property_buffer][index] - observed_midpoint);
// console.log(index, " CCCCCCC input value ", prior_value, " output value ", audio_obj[property_buffer][index]);
if (post_processing_min > audio_obj[property_buffer][index]) {
post_processing_min = audio_obj[property_buffer][index];
} else if (post_processing_max < audio_obj[property_buffer][index]) {
post_processing_max = audio_obj[property_buffer][index];
}
};
// console.log(" CCCCCCC post_processing_min ", post_processing_min, " post_processing_max ", post_processing_max);
};
};
exports.normalize_buffer = normalize_buffer;
// ---
function cb_parse_buffer_as_wav_format(audio_obj, property_buffer_raw_input_file, property_buffer_input_file, cb_when_done) {
// sync NOT async ... output into buffer_input_file
parse_wav(audio_obj, property_buffer_raw_input_file, property_buffer_input_file);
delete audio_obj[property_buffer_raw_input_file]; // no longer need raw pre parse buffer
// console.log("buffer size ", audio_obj[property_buffer_input_file].length);
audio_obj.buffer = shared_utils.convert_16_bit_signed_int_to_32_bit_float(audio_obj[property_buffer_input_file]);
delete audio_obj[property_buffer_input_file]; // no longer need raw pre parse buffer
cb_when_done(audio_obj); // this is defined by original caller to hand back audio object with populated buffer
}; // cb_parse_buffer_as_wav_format
// ---
var read_16_bit_wav_file_into_32_bit_float_buffer = function(read_wav_file_obj, wav_input_filename, spec, cb_when_done) {
// console.log("TTT ___ read_16_bit_wav_file_into_32_bit_float_buffer ___ ");
// ---
var property_buffer_raw_input_file = "buffer_raw_input_file";
var property_buffer_input_file = "buffer_input_file";
read_wav_file_obj.filename = wav_input_filename;
read_wav_file_obj[property_buffer_raw_input_file] = new Buffer.alloc(0);
// console.log("name cb cb_parse_buffer_as_wav_format ", cb_parse_buffer_as_wav_format.name);
// console.log("name cb cb_when_done ", cb_when_done.name);
read_file_into_buffer(read_wav_file_obj, property_buffer_raw_input_file,
property_buffer_input_file,
cb_parse_buffer_as_wav_format,
cb_when_done);
// console.log("BBB ___ read_16_bit_wav_file_into_32_bit_float_buffer ___ ");
}; // read_16_bit_wav_file_into_32_bit_float_buffer
// NOT for export --- internal consumption ONLY its outer wrapper is called read_wav_file
// ---
var read_wav_file = function(input_filename, cb_read_file_done) {
// ------------ read wav file -------------------- //
// console.log("read_wav_file with input_filename ", input_filename);
// bbb
var wav_file_input_obj = {}; // create stub object to which we attach .buffer
var property_buffer_raw_input_file = "buffer_raw_input_file";
wav_file_input_obj.filename = input_filename;
wav_file_input_obj[property_buffer_raw_input_file] = new Buffer.alloc(0);
// console.log("abouttttt to read wav_file_input_obj.filename ", wav_file_input_obj.filename);
var spec = {};
read_16_bit_wav_file_into_32_bit_float_buffer(
wav_file_input_obj,
wav_file_input_obj.filename,
spec,
cb_read_file_done);
};
exports.read_wav_file = read_wav_file;
// ---
exports.write_32_bit_float_buffer_to_16_bit_wav_file = function(audio_obj, wav_output_filename, spec, db_done) {
var property_buffer = "buffer"; // defaults
var really_big_number = 999999.9;
var spec = spec || { flag_normalize : false }; // flag to normalize input into float range -1 <--> +1
// var spec = spec || { flag_normalize : true };
if (typeof spec.property_buffer !== "undefined") {
property_buffer = spec.property_buffer;
};
if (true == spec.flag_normalize) {
normalize_buffer(audio_obj, spec);
}
var output_16_bit_audio_obj = {};
copy_properties_across_objects(audio_obj, output_16_bit_audio_obj);
output_16_bit_audio_obj.filename = wav_output_filename;
// shared_utils.show_object(audio_obj, "total",
// "hhhhhhhhhhhhhhh corindddeeee audio_obj corindddeeee", 10);
output_16_bit_audio_obj.buffer = shared_utils.convert_32_bit_float_into_signed_16_bit_int_lossy(audio_obj[property_buffer]);
write_wav(output_16_bit_audio_obj);
}; // write_32_bit_float_buffer_to_16_bit_wav_file
// ---
})(typeof exports === "undefined" ? this["node_utils"]={}: exports);