prx-wavefile
Version:
Create, read and write wav files according to the specs.
755 lines (717 loc) • 18.9 kB
JavaScript
/*
* Copyright (c) 2017-2019 Rafael da Silva Rocha.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/**
* @fileoverview Externs for wavefile 11.0
* @see https://github.com/rochars/wavefile
* @externs
*/
// wavefile module
var wavefile = {};
// WaveFile class
var WaveFile = {};
/**
* The container identifier.
* RIFF, RIFX and RF64 are supported.
* @type {string}
*/
WaveFile.prototype.container = "";
/**
* @type {number}
*/
WaveFile.prototype.chunkSize = 0;
/**
* The format.
* Always WAVE.
* @type {string}
*/
WaveFile.prototype.format = "";
/**
* The data of the fmt chunk.
* @type {!Object<string, *>}
*/
WaveFile.prototype.fmt = {
/** @type {string} */
chunkId: "",
/** @type {number} */
chunkSize: 0,
/** @type {number} */
audioFormat: 0,
/** @type {number} */
numChannels: 0,
/** @type {number} */
sampleRate: 0,
/** @type {number} */
byteRate: 0,
/** @type {number} */
blockAlign: 0,
/** @type {number} */
bitsPerSample: 0,
/** @type {number} */
cbSize: 0,
/** @type {number} */
validBitsPerSample: 0,
/** @type {number} */
dwChannelMask: 0,
/**
* 4 32-bit values representing a 128-bit ID
* @type {!Array<number>}
*/
subformat: [],
/**
* MPEG additional format information
* when audioFormat == 80
* https://tech.ebu.ch/docs/tech/tech3285s1.pdf
*/
/** @type {number} */
headLayer: 0,
/** @type {number} */
headBitRate: 0,
/** @type {number} */
headMode: 0,
/** @type {number} */
headModeExt: 0,
/** @type {number} */
headEmphasis: 0,
/** @type {number} */
headFlags: 0,
/** @type {number} */
ptsLow: 0,
/** @type {number} */
ptsHigh: 0
};
/**
* The data of the fact chunk.
* @type {!Object<string, *>}
*/
WaveFile.prototype.fact = {
/** @type {string} */
chunkId: "",
/** @type {number} */
chunkSize: 0,
/** @type {number} */
dwSampleLength: 0
};
/**
* The data of the cue chunk.
* @type {!Object<string, *>}
*/
WaveFile.prototype.cue = {
/** @type {string} */
chunkId: "",
/** @type {number} */
chunkSize: 0,
/** @type {number} */
dwCuePoints: 0,
/** @type {!Array<!Object>} */
points: [
{
dwName: 0, // a cue point ID
dwPosition: 0,
fccChunk: 0,
dwChunkStart: 0,
dwBlockStart: 0,
dwSampleOffset: 0,
position: 0
}
]
};
/**
* The data of the smpl chunk.
* @type {!Object<string, *>}
*/
WaveFile.prototype.smpl = {
/** @type {string} */
chunkId: "",
/** @type {number} */
chunkSize: 0,
/** @type {number} */
dwManufacturer: 0,
/** @type {number} */
dwProduct: 0,
/** @type {number} */
dwSamplePeriod: 0,
/** @type {number} */
dwMIDIUnityNote: 0,
/** @type {number} */
dwMIDIPitchFraction: 0,
/** @type {number} */
dwSMPTEFormat: 0,
/** @type {number} */
dwSMPTEOffset: 0,
/** @type {number} */
dwNumSampleLoops: 0,
/** @type {number} */
dwSamplerData: 0,
/** @type {!Array<!Object>} */
loops: [
{
dwName: "", // a cue point ID
dwType: 0,
dwStart: 0,
dwEnd: 0,
dwFraction: 0,
dwPlayCount: 0
}
]
};
/**
* The data of the bext chunk.
* @type {!Object<string, *>}
*/
WaveFile.prototype.bext = {
/** @type {string} */
chunkId: "",
/** @type {number} */
chunkSize: 0,
/** @type {string} */
description: "",
/** @type {string} */
originator: "",
/** @type {string} */
originatorReference: "",
/** @type {string} */
originationDate: "",
/** @type {string} */
originationTime: "",
/**
* 2 32-bit values, timeReference high and low
* @type {!Array<number>}
*/
timeReference: [0, 0],
/** @type {number} */
version: 0,
/** @type {string} */
UMID: "",
/** @type {number} */
loudnessValue: 0,
/** @type {number} */
loudnessRange: 0,
/** @type {number} */
maxTruePeakLevel: 0,
/** @type {number} */
maxMomentaryLoudness: 0,
/** @type {number} */
maxShortTermLoudness: 0,
/** @type {string} */
reserved: "",
/** @type {string} */
codingHistory: ""
};
/**
* The data of the 'mext' chunk.
* @type {!Object<string, *>}
*/
WaveFile.prototype.mext = {
/** @type {string} */
chunkId: "",
/** @type {number} */
chunkSize: 0,
/** @type {number} */
soundInformation: 0,
/** @type {number} */
frameSize: 0,
/** @type {number} */
ancillaryDataLength: 0,
/** @type {number} */
ancillaryDataDef: 0, //4
/** @type {string} */
reserved: ""
};
/**
* mpegInfo for making a wav from mpeg audio
* @type {!Object<string, *>}
*/
WaveFile.prototype.mpegInfo = {
/** @type {number} */
version: 0,
/** @type {number} */
layer: 0,
/** @type {number} */
sampleRate: 0,
/** @type {number} */
bitRate: 0,
/** @type {string} */
channelMode: "",
/** @type {number} */
padding: 0,
/** @type {number} */
modeExtension: 0,
/** @type {number} */
emphasis: 0,
/** @type {number} */
privateBit: 0,
/** @type {boolean} */
copyright: false,
/** @type {boolean} */
original: false,
/** @type {boolean} */
errorProtection: false,
/** @type {number} */
numChannels: 0,
/** @type {number} */
frameSize: 0,
/** @type {number} */
sampleLength: 0,
/** @type {boolean} */
freeForm: false
};
/**
* The data of the cart chunk.
* @type {!Object<string, *>}
*/
WaveFile.prototype.cart = {
/** @type {string} */
chunkId: "",
/** @type {number} */
chunkSize: 0,
/** @type {string} */
version: "",
/** @type {string} */
title: "",
/** @type {string} */
artist: "",
/** @type {string} */
cutId: "",
/** @type {string} */
clientId: "",
/** @type {string} */
category: "",
/** @type {string} */
classification: "",
/** @type {string} */
outCue: "",
/** @type {string} */
startDate: "",
/** @type {string} */
startTime: "",
/** @type {string} */
endDate: "",
/** @type {string} */
endTime: "",
/** @type {string} */
producerAppId: "",
/** @type {string} */
producerAppVersion: "",
/** @type {string} */
userDef: "",
/** @type {number} */
levelReference: 0,
/** @type {string} */
postTimer: "",
/** @type {string} */
reserved: "",
/** @type {string} */
url: "",
/** @type {string} */
tagText: ""
};
/**
* The data of the iXML chunk.
* @type {!Object<string, *>}
*/
WaveFile.prototype.iXML = {
/** @type {string} */
chunkId: "",
/** @type {number} */
chunkSize: 0,
/** @type {number} */
value: 0
};
/**
* The data of the ds64 chunk.
* Used only with RF64 files.
* @type {!Object<string, *>}
*/
WaveFile.prototype.ds64 = {
/** @type {string} */
chunkId: "",
/** @type {number} */
chunkSize: 0,
/** @type {number} */
riffSizeHigh: 0,
/** @type {number} */
riffSizeLow: 0,
/** @type {number} */
dataSizeHigh: 0,
/** @type {number} */
dataSizeLow: 0,
/** @type {number} */
originationTime: 0,
/** @type {number} */
sampleCountHigh: 0,
/** @type {number} */
sampleCountLow: 0
};
/**
* The data of the data chunk.
* @type {!Object<string, *>}
*/
WaveFile.prototype.data = {
/** @type {string} */
chunkId: "",
/** @type {number} */
chunkSize: 0,
/** @type {!Uint8Array} */
samples: null //
};
/**
* The data of the LIST chunks.
* @type {!Array<!Object>}
*/
WaveFile.prototype.LIST = [
{
chunkId: "",
chunkSize: 0,
format: "",
subChunks: [
// For format 'INFO'
{
chunkId: "",
chunkSize: 0,
value: ""
},
// For format 'adtl' types 'labl' or 'note'
{
chunkId: "",
chunkSize: 0,
dwName: 0,
value: ""
},
// For format 'adtl' type 'ltxt'
{
chunkId: "",
value: 0,
dwName: 0,
dwSampleLength: 0,
dwPurposeID: 0,
dwCountry: 0,
dwLanguage: 0,
dwDialect: 0,
dwCodePage: 0
}
]
}
];
/**
* The data of the junk chunk.
* @type {!Object<string, *>}
*/
WaveFile.prototype.junk = {
/** @type {string} */
chunkId: "",
/** @type {number} */
chunkSize: 0,
/** @type {!Array<number>} */
chunkData: []
};
/**
* The data of the _PMX chunk.
* @type {!Object<string, *>}
*/
WaveFile.prototype._PMX = {
/** @type {string} */
chunkId: "",
/** @type {number} */
chunkSize: 0,
/** @type {number} */
value: 0
};
/**
* The bit depth code according to the samples.
* @type {string}
*/
WaveFile.prototype.bitDepth = "";
/**
* Whether to apply a pad byte or not
* Defaults to 'true'
* @type {boolean}
*/
WaveFile.prototype.padBytes = true;
/**
* Return the samples packed in a Float64Array.
* @param {boolean=} [interleaved=false] True to return interleaved samples,
* false to return the samples de-interleaved.
* @param {Function=} [OutputObject=Float64Array] The sample container.
* @return {!(Array|TypedArray)} the samples.
*/
WaveFile.prototype.getSamples = function(
interleaved = false,
OutputObject = Float64Array
) {};
/**
* Return the sample at a given index.
* @param {number} index The sample index.
* @return {number} The sample.
* @throws {Error} If the sample index is off range.
*/
WaveFile.prototype.getSample = function(index) {};
/**
* Set the sample at a given index.
* @param {number} index The sample index.
* @param {number} sample The sample.
* @throws {Error} If the sample index is off range.
*/
WaveFile.prototype.setSample = function(index, sample) {};
/**
* Set up the WaveFileCreator object based on the arguments passed.
* Existing chunks are reset.
* @param {number} numChannels The number of channels.
* @param {number} sampleRate The sample rate.
* Integers like 8000, 44100, 48000, 96000, 192000.
* @param {string} bitDepthCode The audio bit depth code.
* One of '4', '8', '8a', '8m', '16', '24', '32', '32f', '64'
* or any value between '8' and '32' (like '12').
* @param {!(Array|TypedArray)} samples The samples.
* @param {Object=} options Optional. Used to force the container
* as RIFX with {'container': 'RIFX'}
* @throws {Error} If any argument does not meet the criteria.
*/
WaveFile.prototype.fromScratch = function(
numChannels,
sampleRate,
bitDepthCode,
samples,
options = {
container: "RIFF"
}
) {};
/**
* Set up the WaveFileCreator object from an mpeg buffer and metadata info.
* @param {!Uint8Array} mpegBuffer The buffer.
* @param {Object=} info Mpeg info such as version, layer, bitRate, etc.
* @throws {Error} If any argument does not meet the criteria.
*/
WaveFile.prototype.fromMpeg = function(mpegBuffer, info) {};
/**
* Set up the WaveFileParser object from a byte buffer.
* @param {!Uint8Array} wavBuffer The buffer.
* @param {boolean=} [samples=true] True if the samples should be loaded.
* @throws {Error} If container is not RIFF, RIFX or RF64.
* @throws {Error} If format is not WAVE.
* @throws {Error} If no 'fmt ' chunk is found.
* @throws {Error} If no 'data' chunk is found.
*/
WaveFile.prototype.fromBuffer = function(wavBuffer, samples = true) {};
/**
* Return a byte buffer representig the WaveFileParser object as a .wav file.
* The return value of this method can be written straight to disk.
* @return {!Uint8Array} A wav file.
* @throws {Error} If bit depth is invalid.
* @throws {Error} If the number of channels is invalid.
* @throws {Error} If the sample rate is invalid.
*/
WaveFile.prototype.toBuffer = function() {};
/**
* Use a .wav file encoded as a base64 string to load the WaveFile object.
* @param {string} base64String A .wav file as a base64 string.
* @throws {Error} If any property of the object appears invalid.
*/
WaveFile.prototype.fromBase64 = function(base64String) {};
/**
* Return a base64 string representig the WaveFile object as a .wav file.
* @return {string} A .wav file as a base64 string.
* @throws {Error} If any property of the object appears invalid.
*/
WaveFile.prototype.toBase64 = function() {};
/**
* Return a DataURI string representig the WaveFile object as a .wav file.
* The return of this method can be used to load the audio in browsers.
* @return {string} A .wav file as a DataURI.
* @throws {Error} If any property of the object appears invalid.
*/
WaveFile.prototype.toDataURI = function() {};
/**
* Use a .wav file encoded as a DataURI to load the WaveFile object.
* @param {string} dataURI A .wav file as DataURI.
* @throws {Error} If any property of the object appears invalid.
*/
WaveFile.prototype.fromDataURI = function(dataURI) {};
/**
* Force a file as RIFF.
*/
WaveFile.prototype.toRIFF = function() {};
/**
* Force a file as RIFX.
*/
WaveFile.prototype.toRIFX = function() {};
/**
* Change the bit depth of the samples.
* @param {string} newBitDepth The new bit depth of the samples.
* One of '8' ... '32' (integers), '32f' or '64' (floats)
* @param {boolean=} [changeResolution=true] A boolean indicating if the
* resolution of samples should be actually changed or not.
* @throws {Error} If the bit depth is not valid.
*/
WaveFile.prototype.toBitDepth = function(
newBitDepth,
changeResolution = true
) {};
/**
* Convert the sample rate of the file.
* @param {number} sampleRate The target sample rate.
* @param {Object=} options The extra configuration, if needed.
*/
WaveFile.prototype.toSampleRate = function(
sampleRate,
options = {
method: "cubic",
clip: "mirror",
tension: 0,
sincFilterSize: 32,
lanczosFilterSize: 24,
sincWindow: function(x) {},
LPF: true,
LPFType: "IIR",
LPForder: 1
}
) {};
/**
* Encode a 16-bit wave file as 4-bit IMA ADPCM.
* @throws {Error} If sample rate is not 8000.
* @throws {Error} If number of channels is not 1.
*/
WaveFile.prototype.toIMAADPCM = function() {};
/**
* Decode a 4-bit IMA ADPCM wave file as a 16-bit wave file.
* @param {string=} [bitDepthCode='16'] The new bit depth of the samples.
* One of '8' ... '32' (integers), '32f' or '64' (floats).
*/
WaveFile.prototype.fromIMAADPCM = function(bitDepthCode = "16") {};
/**
* Encode 16-bit wave file as 8-bit A-Law.
*/
WaveFile.prototype.toALaw = function() {};
/**
* Decode a 8-bit A-Law wave file into a 16-bit wave file.
* @param {string=} [bitDepthCode='16'] The new bit depth of the samples.
* One of '8' ... '32' (integers), '32f' or '64' (floats).
*/
WaveFile.prototype.fromALaw = function(bitDepthCode = "16") {};
/**
* Encode 16-bit wave file as 8-bit mu-Law.
*/
WaveFile.prototype.toMuLaw = function() {};
/**
* Decode a 8-bit mu-Law wave file into a 16-bit wave file.
* @param {string=} [bitDepthCode='16'] The new bit depth of the samples.
* One of '8' ... '32' (integers), '32f' or '64' (floats).
*/
WaveFile.prototype.fromMuLaw = function(bitDepthCode = "16") {};
/**
* Write a RIFF tag in the INFO chunk. If the tag do not exist,
* then it is created. It if exists, it is overwritten.
* @param {string} tag The tag name.
* @param {string} value The tag value.
* @throws {Error} If the tag name is not valid.
*/
WaveFile.prototype.setTag = function(tag, value) {};
/**
* Return the value of a RIFF tag in the INFO chunk.
* @param {string} tag The tag name.
* @return {?string} The value if the tag is found, null otherwise.
*/
WaveFile.prototype.getTag = function(tag) {};
/**
* Remove a RIFF tag in the INFO chunk.
* @param {string} tag The tag name.
* @return {boolean} True if a tag was deleted.
*/
WaveFile.prototype.deleteTag = function(tag) {};
/**
* Create a cue point in the wave file.
* @param {!{
* position: number,
* label: ?string,
* end: ?number,
* dwPurposeID: ?number,
* dwCountry: ?number,
* dwLanguage: ?number,
* dwDialect: ?number,
* dwCodePage: ?number
* }} pointData A object with the data of the cue point.
*/
WaveFile.prototype.setCuePoint = function(pointData) {};
/**
* Remove a cue point from a wave file.
* @param {number} index the index of the point. First is 1,
* second is 2, and so on.
*/
WaveFile.prototype.deleteCuePoint = function(index) {};
/**
* Update the label of a cue point.
* @param {number} pointIndex The ID of the cue point.
* @param {string} label The new text for the label.
*/
WaveFile.prototype.updateLabel = function(pointIndex, label) {};
/**
* Return a Object<tag, value> with the RIFF tags in the file.
* @return {!Object<string, string>} The file tags.
*/
WaveFile.prototype.listTags = function() {};
/**
* Return an array with all cue points in the file, in the order they appear
* in the file.
* Objects representing cue points/regions look like this:
* {
* position: 500, // the position in milliseconds
* label: 'cue marker 1',
* end: 1500, // the end position in milliseconds
* dwName: 1,
* dwPosition: 0,
* fccChunk: 'data',
* dwChunkStart: 0,
* dwBlockStart: 0,
* dwSampleOffset: 22050, // the position as a sample offset
* dwSampleLength: 3646827, // the region length as a sample count
* dwPurposeID: 544106354,
* dwCountry: 0,
* dwLanguage: 0,
* dwDialect: 0,
* dwCodePage: 0,
* }
* @return {!Array<Object>}
*/
WaveFile.prototype.listCuePoints = function() {};
/**
* Return the value of the iXML chunk.
* @return {string} The contents of the iXML chunk.
*/
WaveFile.prototype.getiXML = function() {};
/**
* Set the value of the iXML chunk.
* @param {string} iXMLValue The value for the iXML chunk.
* @throws {TypeError} If the value is not a string.
*/
WaveFile.prototype.setiXML = function(iXMLValue) {};
/**
* Get the value of the _PMX chunk.
* @return {string} The contents of the _PMX chunk.
*/
WaveFile.prototype.get_PMX = function() {};
/**
* Set the value of the _PMX chunk.
* @param {string} _PMXValue The value for the _PMX chunk.
* @throws {TypeError} If the value is not a string.
*/
WaveFile.prototype.set_PMX = function(_PMXValue) {};