oh how much did i MISS 3las, i surely LOVE firefox and its compatiblity (might as well switch to fucking ie)

This commit is contained in:
2026-04-05 08:20:59 +02:00
parent c2cbbdd3b9
commit c7cf01a317
11 changed files with 1430 additions and 36 deletions
@@ -0,0 +1,319 @@
/*
MPEG audio format reader is part of 3LAS (Low Latency Live Audio Streaming)
https://github.com/JoJoBond/3LAS
*/
window.startTimeConnectionWatchdog = performance.now();
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var MPEGFrameInfo = /** @class */ (function () {
function MPEGFrameInfo(data, sampleCount, sampleRate) {
this.Data = data;
this.SampleCount = sampleCount;
this.SampleRate = sampleRate;
}
return MPEGFrameInfo;
}());
var AudioFormatReader_MPEG = /** @class */ (function (_super) {
__extends(AudioFormatReader_MPEG, _super);
function AudioFormatReader_MPEG(audio, logger, errorCallback, beforeDecodeCheck, dataReadyCallback, addId3Tag, minDecodeFrames) {
var _this = _super.call(this, audio, logger, errorCallback, beforeDecodeCheck, dataReadyCallback) || this;
_this._OnDecodeSuccess = _this.OnDecodeSuccess.bind(_this);
_this._OnDecodeError = _this.OnDecodeError.bind(_this);
_this.AddId3Tag = addId3Tag;
_this.MinDecodeFrames = minDecodeFrames;
_this.Frames = new Array();
_this.FrameStartIdx = -1;
_this.FrameEndIdx = -1;
_this.FrameSamples = 0;
_this.FrameSampleRate = 0;
_this.TimeBudget = 0;
return _this;
}
// Deletes all frames from the databuffer and framearray and all samples from the samplearray
AudioFormatReader_MPEG.prototype.PurgeData = function () {
_super.prototype.PurgeData.call(this);
this.Frames = new Array();
this.FrameStartIdx = -1;
this.FrameEndIdx = -1;
this.FrameSamples = 0;
this.FrameSampleRate = 0;
this.TimeBudget = 0;
};
// Extracts all currently possible frames
AudioFormatReader_MPEG.prototype.ExtractAll = function () {
// Look for frames
this.FindFrame();
// Repeat as long as we can extract frames
while (this.CanExtractFrame()) {
// Extract frame and push into array
this.Frames.push(this.ExtractFrame());
// Look for frames
this.FindFrame();
}
// Check if we have enough frames to decode
if (this.Frames.length >= this.MinDecodeFrames) {
// Note:
// =====
// mp3 frames have an overlap of [granule size] so we can't use the first or last [granule size] samples.
// [granule size] is equal to half of a [frame size] in samples (using the mp3's sample rate).
// Sum up the playback time of each decoded frame and data buffer lengths
// Note: Since mp3-Frames overlap by half of their sample-length we expect the
// first and last frame to be only half as long. Some decoders will still output
// the full frame length by adding zeros.
var bufferLength = 0;
var expectedTotalPlayTime_1 = 0;
var firstGranulePlayTime_1 = 0;
var lastGranulePlayTime_1 = 0;
firstGranulePlayTime_1 = this.Frames[0].SampleCount / this.Frames[0].SampleRate / 2.0; // Only half of data is usable due to overlap
expectedTotalPlayTime_1 += firstGranulePlayTime_1;
bufferLength += this.Frames[0].Data.length;
for (var i = 1; i < this.Frames.length - 1; i++) {
expectedTotalPlayTime_1 += this.Frames[i].SampleCount / this.Frames[i].SampleRate;
bufferLength += this.Frames[i].Data.length;
}
lastGranulePlayTime_1 = this.Frames[this.Frames.length - 1].SampleCount / this.Frames[this.Frames.length - 1].SampleRate / 2.0; // Only half of data is usable due to overlap
expectedTotalPlayTime_1 += lastGranulePlayTime_1;
bufferLength += this.Frames[this.Frames.length - 1].Data.length;
// If needed, add some space for the ID3v2 tag
if (this.AddId3Tag) {
bufferLength += AudioFormatReader_MPEG.Id3v2Tag.length;
}
// Create a buffer long enough to hold everything
var decodeBuffer = new Uint8Array(bufferLength);
var offset = 0;
// If needed, add ID3v2 tag to beginning of buffer
if (this.AddId3Tag) {
decodeBuffer.set(AudioFormatReader_MPEG.Id3v2Tag, offset);
offset += AudioFormatReader_MPEG.Id3v2Tag.length;
}
// Add the frames to the window
for (var i = 0; i < this.Frames.length; i++) {
decodeBuffer.set(this.Frames[i].Data, offset);
offset += this.Frames[i].Data.length;
}
// Remove the used frames from the array
this.Frames.splice(0, this.Frames.length - 1);
// Increment Id
var id_1 = this.Id++;
// Check if decoded frames might be too far back in the past
if (!this.OnBeforeDecode(id_1, expectedTotalPlayTime_1))
return;
// Push window to the decoder
this.Audio.decodeAudioData(decodeBuffer.buffer, (function (decodedData) {
var _id = id_1;
var _expectedTotalPlayTime = expectedTotalPlayTime_1;
var _firstGranulePlayTime = firstGranulePlayTime_1;
var _lastGranulePlayTime = lastGranulePlayTime_1;
this._OnDecodeSuccess(decodedData, _id, _expectedTotalPlayTime, _firstGranulePlayTime, _lastGranulePlayTime);
}).bind(this), this._OnDecodeError.bind(this));
}
};
// Finds frame boundries within the data buffer
AudioFormatReader_MPEG.prototype.FindFrame = function () {
// Find frame start
if (this.FrameStartIdx < 0) {
var i = 0;
// Make sure we don't exceed array bounds
while ((i + 1) < this.DataBuffer.length) {
// Look for MPEG sync word
if (this.DataBuffer[i] == 0xFF && (this.DataBuffer[i + 1] & 0xE0) == 0xE0) {
// Sync found, set frame start
this.FrameStartIdx = i;
break;
}
i++;
}
}
// Find frame end
if (this.FrameStartIdx >= 0 && this.FrameEndIdx < 0) {
// Check if we have enough data to process the header
if ((this.FrameStartIdx + 2) < this.DataBuffer.length) {
// Get header data
// Version index
var ver = (this.DataBuffer[this.FrameStartIdx + 1] & 0x18) >>> 3;
// Layer index
var lyr = (this.DataBuffer[this.FrameStartIdx + 1] & 0x06) >>> 1;
// Padding? 0/1
var pad = (this.DataBuffer[this.FrameStartIdx + 2] & 0x02) >>> 1;
// Bitrate index
var brx = (this.DataBuffer[this.FrameStartIdx + 2] & 0xF0) >>> 4;
// SampRate index
var srx = (this.DataBuffer[this.FrameStartIdx + 2] & 0x0C) >>> 2;
// Resolve flags to real values
var bitrate = AudioFormatReader_MPEG.MPEG_bitrates[ver][lyr][brx] * 1000;
var samprate = AudioFormatReader_MPEG.MPEG_srates[ver][srx];
var samples = AudioFormatReader_MPEG.MPEG_frame_samples[ver][lyr];
var slot_size = AudioFormatReader_MPEG.MPEG_slot_size[lyr];
// In-between calculations
var bps = samples / 8.0;
var fsize = ((bps * bitrate) / samprate) + ((pad == 1) ? slot_size : 0);
// Truncate to integer
var frameSize = Math.floor(fsize);
// Store number of samples and samplerate for frame
this.FrameSamples = samples;
this.FrameSampleRate = samprate;
// Set end frame boundry
this.FrameEndIdx = this.FrameStartIdx + frameSize;
}
}
};
// Checks if there is a frame ready to be extracted
AudioFormatReader_MPEG.prototype.CanExtractFrame = function () {
if (this.FrameStartIdx < 0 || this.FrameEndIdx < 0)
return false;
else if (this.FrameEndIdx <= this.DataBuffer.length)
return true;
else
return false;
};
// Extract a single frame from the buffer
AudioFormatReader_MPEG.prototype.ExtractFrame = function () {
// Extract frame data from buffer
var frameArray = this.DataBuffer.buffer.slice(this.FrameStartIdx, this.FrameEndIdx);
// Remove frame from buffer
if ((this.FrameEndIdx + 1) < this.DataBuffer.length)
this.DataBuffer = new Uint8Array(this.DataBuffer.buffer.slice(this.FrameEndIdx));
else
this.DataBuffer = new Uint8Array(0);
// Reset Start/End indices
this.FrameStartIdx = 0;
this.FrameEndIdx = -1;
return new MPEGFrameInfo(new Uint8Array(frameArray), this.FrameSamples, this.FrameSampleRate);
};
// Is called if the decoding of the window succeeded
AudioFormatReader_MPEG.prototype.OnDecodeSuccess = function (decodedData, id, expectedTotalPlayTime, firstGranulePlayTime, lastGranulePlayTime) {
window.startTimeConnectionWatchdog = performance.now();
var extractSampleCount;
var extractSampleOffset;
var delta = 0.001;
// Check if we got the expected number of samples
if (expectedTotalPlayTime > decodedData.duration) {
// We got less samples than expect, we suspect that they were truncated equally at start and end.
// This can happen in case of sample rate conversions.
extractSampleCount = decodedData.length;
extractSampleOffset = 0;
this.TimeBudget += (expectedTotalPlayTime - decodedData.duration);
}
else if (expectedTotalPlayTime < decodedData.duration) {
// We got more samples than expect, we suspect that zeros were added equally at start and end.
// This can happen in case of sample rate conversions or edge frame handling.
extractSampleCount = Math.ceil(expectedTotalPlayTime * decodedData.sampleRate);
var budgetSamples = this.TimeBudget * decodedData.sampleRate;
if (budgetSamples > 1.0) {
if (budgetSamples > decodedData.length - extractSampleCount) {
budgetSamples = decodedData.length - extractSampleCount;
}
extractSampleCount += budgetSamples;
this.TimeBudget -= (budgetSamples / decodedData.sampleRate);
}
var diff = decodedData.duration - expectedTotalPlayTime;
if ((diff - firstGranulePlayTime - lastGranulePlayTime) >= -delta) {
// Both first and last granule are present. Cut out middle section.
extractSampleOffset = Math.floor((decodedData.length - extractSampleCount) / 2);
}
if (Math.abs(firstGranulePlayTime - lastGranulePlayTime) <= delta) {
// First and last granule are equal. We need to make an educated guess which one is present.
if (isIOS || isIPadOS || isMacOSX) {
// I don't know why, but Apple does things differently.
extractSampleOffset = Math.floor((decodedData.length - extractSampleCount) / 2);
}
else {
// Assume last granule is present.
extractSampleOffset = decodedData.length - extractSampleCount;
}
}
else if (Math.abs(diff - firstGranulePlayTime) <= delta) {
// Last granule is present.
extractSampleOffset = decodedData.length - extractSampleCount;
}
else if (Math.abs(diff - lastGranulePlayTime) <= delta) {
// First granule is present.
extractSampleOffset = 0;
}
else {
// The difference is not equal to neither the first, the last nor the sum of both granule. So we just use the middle.
extractSampleOffset = Math.floor((decodedData.length - extractSampleCount) / 2);
}
}
else {
// We got the expected number of samples, no adaption needed
extractSampleCount = decodedData.length;
extractSampleOffset = 0;
}
// Create a buffer that can hold the frame to extract
var audioBuffer = this.Audio.createBuffer(decodedData.numberOfChannels, extractSampleCount, decodedData.sampleRate);
// Fill buffer with the last part of the decoded frame leave out last granule
for (var i = 0; i < decodedData.numberOfChannels; i++)
audioBuffer.getChannelData(i).set(decodedData.getChannelData(i).subarray(extractSampleOffset, extractSampleOffset + extractSampleCount));
this.OnDataReady(id, audioBuffer);
};
// Is called in case the decoding of the window fails
AudioFormatReader_MPEG.prototype.OnDecodeError = function (_error) {
this.ErrorCallback();
};
// MPEG versions - use [version]
AudioFormatReader_MPEG.MPEG_versions = new Array(25, 0, 2, 1);
// Layers - use [layer]
AudioFormatReader_MPEG.MPEG_layers = new Array(0, 3, 2, 1);
// Bitrates - use [version][layer][bitrate]
AudioFormatReader_MPEG.MPEG_bitrates = new Array(new Array(// Version 2.5
new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), // Reserved
new Array(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0), // Layer 3
new Array(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0), // Layer 2
new Array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0) // Layer 1
), new Array(// Reserved
new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), // Invalid
new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), // Invalid
new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), // Invalid
new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) // Invalid
), new Array(// Version 2
new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), // Reserved
new Array(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0), // Layer 3
new Array(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0), // Layer 2
new Array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0) // Layer 1
), new Array(// Version 1
new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), // Reserved
new Array(0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0), // Layer 3
new Array(0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0), // Layer 2
new Array(0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0) // Layer 1
));
// Sample rates - use [version][srate]
AudioFormatReader_MPEG.MPEG_srates = new Array(new Array(11025, 12000, 8000, 0), // MPEG 2.5
new Array(0, 0, 0, 0), // Reserved
new Array(22050, 24000, 16000, 0), // MPEG 2
new Array(44100, 48000, 32000, 0) // MPEG 1
);
// Samples per frame - use [version][layer]
AudioFormatReader_MPEG.MPEG_frame_samples = new Array(
// Rsvd 3 2 1 < Layer v Version
new Array(0, 576, 1152, 384), // 2.5
new Array(0, 0, 0, 0), // Reserved
new Array(0, 576, 1152, 384), // 2
new Array(0, 1152, 1152, 384) // 1
);
AudioFormatReader_MPEG.Id3v2Tag = new Uint8Array(new Array(0x49, 0x44, 0x33, // File identifier: "ID3"
0x03, 0x00, // Version 2.3
0x00, // Flags: no unsynchronisation, no extended header, no experimental indicator
0x00, 0x00, 0x00, 0x0D, // Size of the (tag-)frames, extended header and padding
0x54, 0x49, 0x54, 0x32, // Title frame: "TIT2"
0x00, 0x00, 0x00, 0x02, // Size of the frame data
0x00, 0x00, // Frame Flags
0x00, 0x20, 0x00 // Frame data (space character) and padding
));
// Slot size (MPEG unit of measurement) - use [layer]
AudioFormatReader_MPEG.MPEG_slot_size = new Array(0, 1, 1, 4); // Rsvd, 3, 2, 1
return AudioFormatReader_MPEG;
}(AudioFormatReader));
@@ -0,0 +1,222 @@
/*
WAV audio format reader is part of 3LAS (Low Latency Live Audio Streaming)
https://github.com/JoJoBond/3LAS
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var AudioFormatReader_WAV = /** @class */ (function (_super) {
__extends(AudioFormatReader_WAV, _super);
function AudioFormatReader_WAV(audio, logger, errorCallback, beforeDecodeCheck, dataReadyCallback, batchDuration, extraEdgeDuration) {
var _this = _super.call(this, audio, logger, errorCallback, beforeDecodeCheck, dataReadyCallback) || this;
_this._OnDecodeSuccess = _this.OnDecodeSuccess.bind(_this);
_this._OnDecodeError = _this.OnDecodeError.bind(_this);
_this.BatchDuration = batchDuration;
_this.ExtraEdgeDuration = extraEdgeDuration;
_this.GotHeader = false;
_this.RiffHeader = null;
_this.WaveSampleRate = 0;
_this.WaveBitsPerSample = 0;
_this.WaveBytesPerSample = 0;
_this.WaveBlockAlign = 0;
_this.WaveChannels = 0;
_this.BatchSamples = 0;
_this.BatchBytes = 0;
_this.ExtraEdgeSamples = 0;
_this.TotalBatchSampleSize = 0;
_this.TotalBatchByteSize = 0;
_this.SampleBudget = 0;
return _this;
}
// Deletes all samples from the databuffer and the samplearray
AudioFormatReader_WAV.prototype.PurgeData = function () {
_super.prototype.PurgeData.call(this);
this.SampleBudget = 0;
};
// Deletes all data from the reader (deos effect headers, etc.)
AudioFormatReader_WAV.prototype.Reset = function () {
_super.prototype.Reset.call(this);
this.GotHeader = false;
this.RiffHeader = null;
this.WaveSampleRate = 0;
this.WaveBitsPerSample = 0;
this.WaveBytesPerSample = 0;
this.WaveBlockAlign = 0;
this.WaveChannels = 0;
this.BatchSamples = 0;
this.BatchBytes = 0;
this.ExtraEdgeSamples = 0;
this.TotalBatchSampleSize = 0;
this.TotalBatchByteSize = 0;
this.SampleBudget = 0;
};
AudioFormatReader_WAV.prototype.ExtractAll = function () {
if (!this.GotHeader)
this.FindAndExtractHeader();
else {
var _loop_1 = function () {
// Extract samples
var tmpSamples = this_1.ExtractIntSamples();
// Increment Id
var id = this_1.Id++;
if (!this_1.OnBeforeDecode(id, this_1.BatchDuration))
return "continue";
// Note:
// =====
// When audio data is resampled we get edge-effects at beginnging and end.
// We should be able to compensate for that by keeping the last sample of the
// previous batch and adding it to the beginning of the current one, but then
// cutting it out AFTER the resampling (since the same effects apply to it)
// The effects at the end can be compensated by cutting the resampled samples shorter
// This is not trivial for non-natural ratios (e.g. 16kHz -> 44.1kHz). Because we would have
// to cut out a non-natural number of samples at beginning and end.
// TODO: All of the above...
// Create a buffer long enough to hold everything
var samplesBuffer = new Uint8Array(this_1.RiffHeader.length + tmpSamples.length);
var offset = 0;
// Add header
samplesBuffer.set(this_1.RiffHeader, offset);
offset += this_1.RiffHeader.length;
// Add samples
samplesBuffer.set(tmpSamples, offset);
// Push pages to the decoder
this_1.Audio.decodeAudioData(samplesBuffer.buffer, (function (decodedData) {
var _id = id;
this._OnDecodeSuccess(decodedData, _id);
}).bind(this_1), this_1._OnDecodeError);
};
var this_1 = this;
while (this.CanExtractSamples()) {
_loop_1();
}
}
};
// Finds riff header within the data buffer and extracts it
AudioFormatReader_WAV.prototype.FindAndExtractHeader = function () {
var curpos = 0;
// Make sure a whole header can fit
if (!((curpos + 4) < this.DataBuffer.length))
return;
// Check chunkID, should be "RIFF"
if (!(this.DataBuffer[curpos] == 0x52 && this.DataBuffer[curpos + 1] == 0x49 && this.DataBuffer[curpos + 2] == 0x46 && this.DataBuffer[curpos + 3] == 0x46))
return;
curpos += 8;
if (!((curpos + 4) < this.DataBuffer.length))
return;
// Check riffType, should be "WAVE"
if (!(this.DataBuffer[curpos] == 0x57 && this.DataBuffer[curpos + 1] == 0x41 && this.DataBuffer[curpos + 2] == 0x56 && this.DataBuffer[curpos + 3] == 0x45))
return;
curpos += 4;
if (!((curpos + 4) < this.DataBuffer.length))
return;
// Check for format subchunk, should be "fmt "
if (!(this.DataBuffer[curpos] == 0x66 && this.DataBuffer[curpos + 1] == 0x6d && this.DataBuffer[curpos + 2] == 0x74 && this.DataBuffer[curpos + 3] == 0x20))
return;
curpos += 4;
if (!((curpos + 4) < this.DataBuffer.length))
return;
var subChunkSize = this.DataBuffer[curpos] | this.DataBuffer[curpos + 1] << 8 | this.DataBuffer[curpos + 2] << 16 | this.DataBuffer[curpos + 3] << 24;
if (!((curpos + 4 + subChunkSize) < this.DataBuffer.length))
return;
curpos += 6;
this.WaveChannels = this.DataBuffer[curpos] | this.DataBuffer[curpos + 1] << 8;
curpos += 2;
this.WaveSampleRate = this.DataBuffer[curpos] | this.DataBuffer[curpos + 1] << 8 | this.DataBuffer[curpos + 2] << 16 | this.DataBuffer[curpos + 3] << 24;
curpos += 8;
this.WaveBlockAlign = this.DataBuffer[curpos] | this.DataBuffer[curpos + 1] << 8;
curpos += 2;
this.WaveBitsPerSample = this.DataBuffer[curpos] | this.DataBuffer[curpos + 1] << 8;
this.WaveBytesPerSample = this.WaveBitsPerSample / 8;
curpos += subChunkSize - 14;
while (true) {
if ((curpos + 8) < this.DataBuffer.length) {
subChunkSize = this.DataBuffer[curpos + 4] | this.DataBuffer[curpos + 5] << 8 | this.DataBuffer[curpos + 6] << 16 | this.DataBuffer[curpos + 7] << 24;
// Check for data subchunk, should be "data"
if (this.DataBuffer[curpos] == 0x64 && this.DataBuffer[curpos + 1] == 0x61 && this.DataBuffer[curpos + 2] == 0x74 && this.DataBuffer[curpos + 3] == 0x61) // Data chunk found
break;
else
curpos += 8 + subChunkSize;
}
else
return;
}
curpos += 8;
this.RiffHeader = new Uint8Array(this.DataBuffer.buffer.slice(0, curpos));
this.BatchSamples = Math.ceil(this.BatchDuration * this.WaveSampleRate);
this.ExtraEdgeSamples = Math.ceil(this.ExtraEdgeDuration * this.WaveSampleRate);
this.BatchBytes = this.BatchSamples * this.WaveBlockAlign;
this.TotalBatchSampleSize = (this.BatchSamples + this.ExtraEdgeSamples);
this.TotalBatchByteSize = this.TotalBatchSampleSize * this.WaveBlockAlign;
var chunkSize = this.RiffHeader.length + this.TotalBatchByteSize - 8;
// Fix header chunksizes
this.RiffHeader[4] = chunkSize & 0xFF;
this.RiffHeader[5] = (chunkSize & 0xFF00) >>> 8;
this.RiffHeader[6] = (chunkSize & 0xFF0000) >>> 16;
this.RiffHeader[7] = (chunkSize & 0xFF000000) >>> 24;
this.RiffHeader[this.RiffHeader.length - 4] = (this.TotalBatchByteSize & 0xFF);
this.RiffHeader[this.RiffHeader.length - 3] = (this.TotalBatchByteSize & 0xFF00) >>> 8;
this.RiffHeader[this.RiffHeader.length - 2] = (this.TotalBatchByteSize & 0xFF0000) >>> 16;
this.RiffHeader[this.RiffHeader.length - 1] = (this.TotalBatchByteSize & 0xFF000000) >>> 24;
this.GotHeader = true;
};
// Checks if there is a samples ready to be extracted
AudioFormatReader_WAV.prototype.CanExtractSamples = function () {
if (this.DataBuffer.length >= this.TotalBatchByteSize)
return true;
else
return false;
};
// Extract a single batch of samples from the buffer
AudioFormatReader_WAV.prototype.ExtractIntSamples = function () {
// Extract sample data from buffer
var intSampleArray = new Uint8Array(this.DataBuffer.buffer.slice(0, this.TotalBatchByteSize));
// Remove samples from buffer
this.DataBuffer = new Uint8Array(this.DataBuffer.buffer.slice(this.BatchBytes));
return intSampleArray;
};
// Is called if the decoding of the samples succeeded
AudioFormatReader_WAV.prototype.OnDecodeSuccess = function (decodedData, id) {
// Calculate the length of the parts
var pickSize = this.BatchDuration * decodedData.sampleRate;
this.SampleBudget += (pickSize - Math.ceil(pickSize));
pickSize = Math.ceil(pickSize);
var pickOffset = (decodedData.length - pickSize) / 2.0;
if (pickOffset < 0)
pickOffset = 0; // This should never happen!
else
pickOffset = Math.floor(pickOffset);
if (this.SampleBudget < -1.0) {
var correction = -1.0 * Math.floor(Math.abs(this.SampleBudget));
this.SampleBudget -= correction;
pickSize += correction;
}
else if (this.SampleBudget > 1.0) {
var correction = Math.floor(this.SampleBudget);
this.SampleBudget -= correction;
pickSize += correction;
}
// Create a buffer that can hold a single part
var audioBuffer = this.Audio.createBuffer(decodedData.numberOfChannels, pickSize, decodedData.sampleRate);
// Fill buffer with the last part of the decoded frame
for (var i = 0; i < decodedData.numberOfChannels; i++)
audioBuffer.getChannelData(i).set(decodedData.getChannelData(i).slice(pickOffset, -pickOffset));
this.OnDataReady(id, audioBuffer);
};
// Is called in case the decoding of the window fails
AudioFormatReader_WAV.prototype.OnDecodeError = function (_error) {
this.ErrorCallback();
};
return AudioFormatReader_WAV;
}(AudioFormatReader));