UNPKG

sofya.transcription

Version:

a JavaScript library that provides a robust and flexible solution for real-time audio transcription. It is designed to transcribe audio streams and can be easily integrated into web applications.

200 lines 8.68 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var filters_1 = require("./filters"); var AudioResampler = /** @class */ (function () { /** * Initialize a AudioResampler instance so that configurations need not to be applied before every chunk. * * @param {SampleRate} oldSampleRate Input sample rate for the audio buffer * @param {SampleRate} newSampleRate Target sample rate for the audio buffer * @param {boolean} liveAudio Set whether the audio is live (multiple sequential chunks) or a single audio buffer. * @param {boolean} antiAlias Set whether to enable or disable Anti-aliasing filters. Recommended for downsampling, disabled by default for upsampling. * @returns {AudioResampler} */ function AudioResampler(oldSampleRate, newSampleRate, liveAudio, antiAlias) { var _this = this; this.antiAlias = true; this.isFirstFrame = true; this.filterBuffer = new Float32Array(); /** * Lanczos resampling * https://en.wikipedia.org/wiki/Lanczos_resampling * * @private * @param {number} a * @param {number} t * @returns {number} */ this.lanczosWindow = function (a, t) { if (t === 0) return 1; if (t >= a || t <= -a) return 0; var p = Math.PI * t; return (a * Math.sin(p) * Math.sin(p / a)) / (p * p); }; /** * Downsampling with Anti-Aliasing * * @private * @param {Float32Array} buffer * @returns {Float32Array} */ this.antiAliasDownsample = function (buffer) { var filter = []; if (_this.decimationFactor === 3) { filter = filters_1.LP_3_TO_1_FIR; } else if (_this.decimationFactor === 2) { filter = filters_1.LP_2_TO_1_FIR; } else if (_this.decimationFactor === 2.75625) { filter = filters_1.LP_275_TO_100_FIR; } else { throw new Error(_this.oldSampleRate + " kHz is not an expected input sampling frequency for conversion into " + _this.newSampleRate + " kHz"); } var nSamplesForNextFrame; //number of samples set aside for the next frame var nSamplesCurrentFrame; //number of samples taken for the current frame var workBuffer; //working buffer for the current chunk if (_this.isFirstFrame) { nSamplesForNextFrame = Math.floor(buffer.length % _this.decimationFactor); //number of samples saved for the next frame nSamplesCurrentFrame = buffer.length - nSamplesForNextFrame; //number of samples selected for the current frame if (nSamplesForNextFrame === 0) { workBuffer = buffer; } else { workBuffer = buffer.slice(0, nSamplesCurrentFrame); } } else { nSamplesForNextFrame = Math.floor((buffer.length + _this.filterBuffer.length - filter.length) % _this.decimationFactor); // Avoid temporal shift nSamplesCurrentFrame = buffer.length + _this.filterBuffer.length - filter.length - nSamplesForNextFrame; workBuffer = new Float32Array(_this.filterBuffer.length + nSamplesCurrentFrame); workBuffer.set(_this.filterBuffer); workBuffer.set(buffer.slice(0, nSamplesCurrentFrame), _this.filterBuffer.length); } // Apply filtering and decimation / interpolation var outBufferLength = Math.floor(nSamplesCurrentFrame / _this.decimationFactor); var outBuffer = new Float32Array(outBufferLength); // decimationFactor is an integer (3) if (_this.decimationFactor === 3 || _this.decimationFactor === 2) { for (var i = filter.length; i < workBuffer.length; i += _this.decimationFactor) { // Low Pass filtering var acc = 0; for (var k = 0; k < filter.length; k++) { acc += workBuffer[i - k] * filter[k]; } // Handle clipping and short formatting var formattedValue = Math.max(Math.min(acc, 1), -1); // Decimation outBuffer[(i - filter.length) / _this.decimationFactor] = formattedValue; } } else if (_this.decimationFactor === 2.75625) { // 44100 kHz : decimationFactor is not an integer (~2.75) // Low Pass filtering var LP_SIGNAL = []; for (var i = filter.length; i < workBuffer.length; i++) { var acc = 0; for (var k = 0; k < filter.length; k++) { acc += workBuffer[i - k] * filter[k]; } LP_SIGNAL[i - filter.length] = acc; } // Decimation / interpolation for (var n = 0; n < outBufferLength; n++) { var a = 3; // Lanczos window type 3 var x = n * _this.decimationFactor; var start = Math.floor(x) - a + 1; var end = Math.floor(x) + a; var acc = 0; for (var i = start; i <= end; i++) { var s = void 0; if (i < 0) s = LP_SIGNAL[0]; else if (i >= LP_SIGNAL.length) s = LP_SIGNAL[LP_SIGNAL.length - 1]; else s = LP_SIGNAL[i]; acc += s * _this.lanczosWindow(a, x - i); } // Handle clipping and short formatting outBuffer[n] = Math.max(Math.min(acc, 1), -1); } } // Update filterBuffer with the last samples of current frame if (_this.isFirstFrame) { _this.filterBuffer = buffer.slice(nSamplesCurrentFrame - filter.length - _this.filterBuffer.length); } else { _this.filterBuffer = buffer.slice(nSamplesCurrentFrame - filter.length - (_this.filterBuffer.length - filter.length)); } // First frame already handled _this.isFirstFrame = false; // will be re-initialized when listening is done (allowing next records) return outBuffer; }; /** * Upsampling * * @private * @param {Float32Array} buffer * @returns {Float32Array} */ this.upsample = function (buffer) { return buffer; }; /** * Downsampling without Anti-Aliasing * * @private * @param {Float32Array} buffer * @returns {Float32Array} */ this.downsample = function (buffer) { return buffer; }; /** * Resampling with applied config. * Determines whether to downsample or upsample using `oldSampleRate` and `newSampleRate` values. * @public * @param {Float32Array} buffer * @returns {Float32Array} */ this.resample = function (buffer) { if (_this.decimationFactor === 1) { return buffer; } else if (_this.decimationFactor > 1) { if (_this.antiAlias) { return _this.antiAliasDownsample(buffer); } else { return _this.downsample(buffer); } } else { return _this.upsample(buffer); } }; /** * Encodes audio buffers as WAVE Format. * * @public * @param {Float32Array} buffer * @returns {DataView} */ this.encodeAsWav = function (buffer) { return new DataView(new ArrayBuffer(buffer.length)); }; this.oldSampleRate = oldSampleRate; this.newSampleRate = newSampleRate; // @ts-ignore this.antiAlias = oldSampleRate > newSampleRate ? antiAlias : false; this.decimationFactor = oldSampleRate / newSampleRate; this.liveAudio = liveAudio; } return AudioResampler; }()); exports.default = AudioResampler; //# sourceMappingURL=SpeechAudioResampler.js.map