You've already forked fm-dx-webserver
mirror of
https://github.com/KubaPro010/fm-dx-webserver.git
synced 2026-02-26 22:13:53 +01:00
193 lines
8.0 KiB
JavaScript
193 lines
8.0 KiB
JavaScript
/*
|
|
Audio format reader is part of 3LAS (Low Latency Live Audio Streaming)
|
|
https://github.com/JoJoBond/3LAS
|
|
*/
|
|
var AudioFormatReader = /** @class */ (function () {
|
|
function AudioFormatReader(audio, logger, errorCallback, beforeDecodeCheck, dataReadyCallback) {
|
|
if (!audio)
|
|
throw new Error('AudioFormatReader: audio must be specified');
|
|
// Check callback argument
|
|
if (typeof errorCallback !== 'function')
|
|
throw new Error('AudioFormatReader: errorCallback must be specified');
|
|
if (typeof beforeDecodeCheck !== 'function')
|
|
throw new Error('AudioFormatReader: beforeDecodeCheck must be specified');
|
|
if (typeof dataReadyCallback !== 'function')
|
|
throw new Error('AudioFormatReader: dataReadyCallback must be specified');
|
|
this.Audio = audio;
|
|
this.Logger = logger;
|
|
this.ErrorCallback = errorCallback;
|
|
this.BeforeDecodeCheck = beforeDecodeCheck;
|
|
this.DataReadyCallback = dataReadyCallback;
|
|
this.Id = 0;
|
|
this.LastPushedId = -1;
|
|
this.Samples = new Array();
|
|
this.BufferStore = {};
|
|
this.DataBuffer = new Uint8Array(0);
|
|
}
|
|
// Pushes frame data into the buffer
|
|
AudioFormatReader.prototype.PushData = function (data) {
|
|
// Append data to framedata buffer
|
|
this.DataBuffer = this.ConcatUint8Array(this.DataBuffer, data);
|
|
// Try to extract frames
|
|
this.ExtractAll();
|
|
};
|
|
// Check if samples are available
|
|
AudioFormatReader.prototype.SamplesAvailable = function () {
|
|
return (this.Samples.length > 0);
|
|
};
|
|
// Get a single bunch of sampels from the reader
|
|
AudioFormatReader.prototype.PopSamples = function () {
|
|
if (this.Samples.length > 0) {
|
|
// Get first bunch of samples, remove said bunch from the array and hand it back to callee
|
|
return this.Samples.shift();
|
|
}
|
|
else
|
|
return null;
|
|
};
|
|
// Deletes all encoded and decoded data from the reader (does not effect headers, etc.)
|
|
AudioFormatReader.prototype.PurgeData = function () {
|
|
this.Id = 0;
|
|
this.LastPushedId = -1;
|
|
this.Samples = new Array();
|
|
this.BufferStore = {};
|
|
this.DataBuffer = new Uint8Array(0);
|
|
};
|
|
// Used to force frame extraction externaly
|
|
AudioFormatReader.prototype.Poke = function () {
|
|
this.ExtractAll();
|
|
};
|
|
// Deletes all data from the reader (does effect headers, etc.)
|
|
AudioFormatReader.prototype.Reset = function () {
|
|
this.PurgeData();
|
|
};
|
|
// Extracts and converts the raw data
|
|
AudioFormatReader.prototype.ExtractAll = function () {
|
|
};
|
|
// Checks if a decode makes sense
|
|
AudioFormatReader.prototype.OnBeforeDecode = function (id, duration) {
|
|
return true;
|
|
//TODO Fix this
|
|
/*
|
|
if(this.BeforeDecodeCheck(duration)) {
|
|
return true;
|
|
}
|
|
else {
|
|
this.OnDataReady(id, this.Audio.createBuffer(1, Math.ceil(duration * this.Audio.sampleRate), this.Audio.sampleRate));
|
|
return false;
|
|
}
|
|
*/
|
|
};
|
|
// Stores the converted bnuches of samples in right order
|
|
AudioFormatReader.prototype.OnDataReady = function (id, audioBuffer) {
|
|
if (this.LastPushedId + 1 == id) {
|
|
// Push samples into array
|
|
this.Samples.push(audioBuffer);
|
|
this.LastPushedId++;
|
|
while (this.BufferStore[this.LastPushedId + 1]) {
|
|
// Push samples we decoded earlier in correct order
|
|
this.Samples.push(this.BufferStore[this.LastPushedId + 1]);
|
|
delete this.BufferStore[this.LastPushedId + 1];
|
|
this.LastPushedId++;
|
|
}
|
|
// Callback to tell that data is ready
|
|
this.DataReadyCallback();
|
|
}
|
|
else {
|
|
// Is out of order, will be pushed later
|
|
this.BufferStore[id] = audioBuffer;
|
|
}
|
|
};
|
|
// Used to concatenate two Uint8Array (b comes BEHIND a)
|
|
AudioFormatReader.prototype.ConcatUint8Array = function (a, b) {
|
|
var tmp = new Uint8Array(a.length + b.length);
|
|
tmp.set(a, 0);
|
|
tmp.set(b, a.length);
|
|
return tmp;
|
|
};
|
|
AudioFormatReader.CanDecodeTypes = function (mimeTypes) {
|
|
var audioTag = new Audio();
|
|
var result = false;
|
|
for (var i = 0; i < mimeTypes.length; i++) {
|
|
var mimeType = mimeTypes[i];
|
|
var answer = audioTag.canPlayType(mimeType);
|
|
if (answer != "probably" && answer != "maybe")
|
|
continue;
|
|
result = true;
|
|
break;
|
|
}
|
|
audioTag = null;
|
|
return result;
|
|
};
|
|
AudioFormatReader.DefaultSettings = function () {
|
|
var settings = {};
|
|
// WAV
|
|
settings["wav"] = {};
|
|
// Duration of wave samples to decode together
|
|
settings["wav"]["BatchDuration"] = 1 / 10; // 0.1 seconds
|
|
/*
|
|
if (isAndroid && isNativeChrome)
|
|
settings["wav"]["BatchDuration"] = 96 / 375;
|
|
else if (isAndroid && isFirefox)
|
|
settings["wav"]["BatchDuration"] = 96 / 375;
|
|
else
|
|
settings["wav"]["BatchDuration"] = 16 / 375;
|
|
*/
|
|
// Duration of addtional samples to decode to account for edge effects
|
|
settings["wav"]["ExtraEdgeDuration"] = 1 / 300; // 0.00333... seconds
|
|
/*
|
|
if (isAndroid && isNativeChrome)
|
|
settings["wav"]["ExtraEdgeDuration"] = 1 / 1000;
|
|
else if (isAndroid && isFirefox)
|
|
settings["wav"]["ExtraEdgeDuration"] = 1 / 1000;
|
|
else
|
|
settings["wav"]["ExtraEdgeDuration"] = 1 / 1000;
|
|
*/
|
|
// MPEG
|
|
settings["mpeg"] = {};
|
|
// Adds a minimal ID3v2 tag before decoding frames.
|
|
settings["mpeg"]["AddID3Tag"] = false;
|
|
// Minimum number of frames to decode together
|
|
// Theoretical minimum is 2.
|
|
// Recommended value is 3 or higher.
|
|
if (isAndroid)
|
|
settings["mpeg"]["MinDecodeFrames"] = 3;
|
|
else
|
|
settings["mpeg"]["MinDecodeFrames"] = 3;
|
|
return settings;
|
|
};
|
|
AudioFormatReader.Create = function (mime, audio, logger, errorCallback, beforeDecodeCheck, dataReadyCallback, settings) {
|
|
if (settings === void 0) { settings = null; }
|
|
if (typeof mime !== "string")
|
|
throw new Error('CreateAudioFormatReader: Invalid MIME-Type, must be string');
|
|
if (!settings)
|
|
settings = this.DefaultSettings();
|
|
var fullMime = mime;
|
|
if (mime.indexOf("audio/pcm") == 0)
|
|
mime = "audio/pcm";
|
|
// Load format handler according to MIME-Type
|
|
switch (mime.replace(/\s/g, "")) {
|
|
// MPEG Audio (mp3)
|
|
case "audio/mpeg":
|
|
case "audio/MPA":
|
|
case "audio/mpa-robust":
|
|
if (!AudioFormatReader.CanDecodeTypes(new Array("audio/mpeg", "audio/MPA", "audio/mpa-robust")))
|
|
throw new Error('CreateAudioFormatReader: Browser can not decode specified MIME-Type (' + mime + ')');
|
|
return new AudioFormatReader_MPEG(audio, logger, errorCallback, beforeDecodeCheck, dataReadyCallback, settings["mpeg"]["AddID3Tag"], settings["mpeg"]["MinDecodeFrames"]);
|
|
break;
|
|
// Waveform Audio File Format
|
|
case "audio/vnd.wave":
|
|
case "audio/wav":
|
|
case "audio/wave":
|
|
case "audio/x-wav":
|
|
if (!AudioFormatReader.CanDecodeTypes(new Array("audio/wav", "audio/wave")))
|
|
throw new Error('CreateAudioFormatReader: Browser can not decode specified MIME-Type (' + mime + ')');
|
|
return new AudioFormatReader_WAV(audio, logger, errorCallback, beforeDecodeCheck, dataReadyCallback, settings["wav"]["BatchDuration"], settings["wav"]["ExtraEdgeDuration"]);
|
|
break;
|
|
// Unknown codec
|
|
default:
|
|
throw new Error('CreateAudioFormatReader: Specified MIME-Type (' + mime + ') not supported');
|
|
break;
|
|
}
|
|
};
|
|
return AudioFormatReader;
|
|
}()); |