diff --git a/system/admin.html b/system/admin.html index 7a988dc..c4c03b0 100644 --- a/system/admin.html +++ b/system/admin.html @@ -304,6 +304,7 @@ + @@ -2500,6 +2501,7 @@ en English
fr French
de German
+ hi Hindi
hu Hungarian
it Italian
ja Japanese
diff --git a/system/at_admin/at_settings.html b/system/at_admin/at_settings.html index ddf67a8..90434ec 100644 --- a/system/at_admin/at_settings.html +++ b/system/at_admin/at_settings.html @@ -124,6 +124,7 @@ + diff --git a/system/config/languages.json b/system/config/languages.json index 3b780e8..d7d061f 100644 --- a/system/config/languages.json +++ b/system/config/languages.json @@ -6,6 +6,7 @@ "English": "en", "French": "fr", "German": "de", + "Hindi": "hi", "Hungarian": "hu", "Italian": "it", "Japanese": "ja", diff --git a/system/tts_generator/crunker.d.ts b/system/tts_generator/crunker.d.ts index 4422095..8ec0fe8 100644 --- a/system/tts_generator/crunker.d.ts +++ b/system/tts_generator/crunker.d.ts @@ -1,179 +1,179 @@ -export interface CrunkerConstructorOptions { - /** - * Sample rate for Crunker's internal audio context. - * - * @default 44100 - */ - sampleRate: number; -} -export type CrunkerInputTypes = string | File | Blob; -/** - * An exported Crunker audio object. - */ -export interface ExportedCrunkerAudio { - blob: Blob; - url: string; - element: HTMLAudioElement; -} -/** - * Crunker is the simple way to merge, concatenate, play, export and download audio files using the Web Audio API. - */ -export default class Crunker { - private readonly _sampleRate; - private readonly _context; - /** - * Creates a new instance of Crunker with the provided options. - * - * If `sampleRate` is not defined, it will auto-select an appropriate sample rate - * for the device being used. - */ - constructor({ sampleRate }?: Partial); - /** - * Creates Crunker's internal AudioContext. - * - * @internal - */ - private _createContext; - /** - * - * The internal AudioContext used by Crunker. - */ - get context(): AudioContext; - /** - * Asynchronously fetches multiple audio files and returns an array of AudioBuffers. - */ - fetchAudio(...filepaths: CrunkerInputTypes[]): Promise; - /** - * Merges (layers) multiple AudioBuffers into a single AudioBuffer. - * - * **Visual representation:** - * - * ![](https://user-images.githubusercontent.com/12958674/88806278-968f0680-d186-11ea-9cb5-8ef2606ffcc7.png) - */ - mergeAudio(buffers: AudioBuffer[]): AudioBuffer; - /** - * Concatenates multiple AudioBuffers into a single AudioBuffer. - * - * **Visual representation:** - * - * ![](https://user-images.githubusercontent.com/12958674/88806297-9d1d7e00-d186-11ea-8cd2-c64cb0324845.png) - */ - concatAudio(buffers: AudioBuffer[]): AudioBuffer; - /** - * Pads a specified AudioBuffer with silence from a specified start time, - * for a specified length of time. - * - * Accepts float values as well as whole integers. - * - * @param buffer AudioBuffer to pad - * @param padStart Time to start padding (in seconds) - * @param seconds Duration to pad for (in seconds) - */ - padAudio(buffer: AudioBuffer, padStart?: number, seconds?: number): AudioBuffer; - /** - * Slices an AudioBuffer from the specified start time to the end time, with optional fade in and out. - * - * @param buffer AudioBuffer to slice - * @param start Start time (in seconds) - * @param end End time (in seconds) - * @param fadeIn Fade in duration (in seconds, default is 0) - * @param fadeOut Fade out duration (in seconds, default is 0) - */ - sliceAudio(buffer: AudioBuffer, start: number, end: number, fadeIn?: number, fadeOut?: number): AudioBuffer; - /** - * Plays the provided AudioBuffer in an AudioBufferSourceNode. - */ - play(buffer: AudioBuffer): AudioBufferSourceNode; - /** - * Exports the specified AudioBuffer to a Blob, Object URI and HTMLAudioElement. - * - * Note that changing the MIME type does not change the actual file format. The - * file format will **always** be a WAVE file due to how audio is stored in the - * browser. - * - * @param buffer Buffer to export - * @param type MIME type (default: `audio/wav`) - */ - export(buffer: AudioBuffer, type?: string): ExportedCrunkerAudio; - /** - * Downloads the provided Blob. - * - * @param blob Blob to download - * @param filename An optional file name to use for the download (default: `crunker`) - */ - download(blob: Blob, filename?: string): HTMLAnchorElement; - /** - * Executes a callback if the browser does not support the Web Audio API. - * - * Returns the result of the callback, or `undefined` if the Web Audio API is supported. - * - * @param callback callback to run if the browser does not support the Web Audio API - */ - notSupported(callback: () => T): T | undefined; - /** - * Closes Crunker's internal AudioContext. - */ - close(): this; - /** - * Returns the largest duration of the longest AudioBuffer. - * - * @internal - */ - private _maxDuration; - /** - * Returns the largest number of channels in an array of AudioBuffers. - * - * @internal - */ - private _maxNumberOfChannels; - /** - * Returns the sum of the lengths of an array of AudioBuffers. - * - * @internal - */ - private _totalLength; - /** - * Returns whether the browser supports the Web Audio API. - * - * @internal - */ - private _isSupported; - /** - * Writes the WAV headers for the specified Float32Array. - * - * Returns a DataView containing the WAV headers and file content. - * - * @internal - */ - private _writeHeaders; - /** - * Converts a Float32Array to 16-bit PCM. - * - * @internal - */ - private _floatTo16BitPCM; - /** - * Writes a string to a DataView at the specified offset. - * - * @internal - */ - private _writeString; - /** - * Converts an AudioBuffer to a Float32Array. - * - * @internal - */ - private _interleave; - /** - * Creates an HTMLAudioElement whose source is the specified Blob. - * - * @internal - */ - private _renderAudioElement; - /** - * Creates an Object URL for the specified Blob. - * - * @internal - */ - private _renderURL; -} +export interface CrunkerConstructorOptions { + /** + * Sample rate for Crunker's internal audio context. + * + * @default 44100 + */ + sampleRate: number; +} +export type CrunkerInputTypes = string | File | Blob; +/** + * An exported Crunker audio object. + */ +export interface ExportedCrunkerAudio { + blob: Blob; + url: string; + element: HTMLAudioElement; +} +/** + * Crunker is the simple way to merge, concatenate, play, export and download audio files using the Web Audio API. + */ +export default class Crunker { + private readonly _sampleRate; + private readonly _context; + /** + * Creates a new instance of Crunker with the provided options. + * + * If `sampleRate` is not defined, it will auto-select an appropriate sample rate + * for the device being used. + */ + constructor({ sampleRate }?: Partial); + /** + * Creates Crunker's internal AudioContext. + * + * @internal + */ + private _createContext; + /** + * + * The internal AudioContext used by Crunker. + */ + get context(): AudioContext; + /** + * Asynchronously fetches multiple audio files and returns an array of AudioBuffers. + */ + fetchAudio(...filepaths: CrunkerInputTypes[]): Promise; + /** + * Merges (layers) multiple AudioBuffers into a single AudioBuffer. + * + * **Visual representation:** + * + * ![](https://user-images.githubusercontent.com/12958674/88806278-968f0680-d186-11ea-9cb5-8ef2606ffcc7.png) + */ + mergeAudio(buffers: AudioBuffer[]): AudioBuffer; + /** + * Concatenates multiple AudioBuffers into a single AudioBuffer. + * + * **Visual representation:** + * + * ![](https://user-images.githubusercontent.com/12958674/88806297-9d1d7e00-d186-11ea-8cd2-c64cb0324845.png) + */ + concatAudio(buffers: AudioBuffer[]): AudioBuffer; + /** + * Pads a specified AudioBuffer with silence from a specified start time, + * for a specified length of time. + * + * Accepts float values as well as whole integers. + * + * @param buffer AudioBuffer to pad + * @param padStart Time to start padding (in seconds) + * @param seconds Duration to pad for (in seconds) + */ + padAudio(buffer: AudioBuffer, padStart?: number, seconds?: number): AudioBuffer; + /** + * Slices an AudioBuffer from the specified start time to the end time, with optional fade in and out. + * + * @param buffer AudioBuffer to slice + * @param start Start time (in seconds) + * @param end End time (in seconds) + * @param fadeIn Fade in duration (in seconds, default is 0) + * @param fadeOut Fade out duration (in seconds, default is 0) + */ + sliceAudio(buffer: AudioBuffer, start: number, end: number, fadeIn?: number, fadeOut?: number): AudioBuffer; + /** + * Plays the provided AudioBuffer in an AudioBufferSourceNode. + */ + play(buffer: AudioBuffer): AudioBufferSourceNode; + /** + * Exports the specified AudioBuffer to a Blob, Object URI and HTMLAudioElement. + * + * Note that changing the MIME type does not change the actual file format. The + * file format will **always** be a WAVE file due to how audio is stored in the + * browser. + * + * @param buffer Buffer to export + * @param type MIME type (default: `audio/wav`) + */ + export(buffer: AudioBuffer, type?: string): ExportedCrunkerAudio; + /** + * Downloads the provided Blob. + * + * @param blob Blob to download + * @param filename An optional file name to use for the download (default: `crunker`) + */ + download(blob: Blob, filename?: string): HTMLAnchorElement; + /** + * Executes a callback if the browser does not support the Web Audio API. + * + * Returns the result of the callback, or `undefined` if the Web Audio API is supported. + * + * @param callback callback to run if the browser does not support the Web Audio API + */ + notSupported(callback: () => T): T | undefined; + /** + * Closes Crunker's internal AudioContext. + */ + close(): this; + /** + * Returns the largest duration of the longest AudioBuffer. + * + * @internal + */ + private _maxDuration; + /** + * Returns the largest number of channels in an array of AudioBuffers. + * + * @internal + */ + private _maxNumberOfChannels; + /** + * Returns the sum of the lengths of an array of AudioBuffers. + * + * @internal + */ + private _totalLength; + /** + * Returns whether the browser supports the Web Audio API. + * + * @internal + */ + private _isSupported; + /** + * Writes the WAV headers for the specified Float32Array. + * + * Returns a DataView containing the WAV headers and file content. + * + * @internal + */ + private _writeHeaders; + /** + * Converts a Float32Array to 16-bit PCM. + * + * @internal + */ + private _floatTo16BitPCM; + /** + * Writes a string to a DataView at the specified offset. + * + * @internal + */ + private _writeString; + /** + * Converts an AudioBuffer to a Float32Array. + * + * @internal + */ + private _interleave; + /** + * Creates an HTMLAudioElement whose source is the specified Blob. + * + * @internal + */ + private _renderAudioElement; + /** + * Creates an Object URL for the specified Blob. + * + * @internal + */ + private _renderURL; +} diff --git a/system/tts_generator/crunker.esm.js b/system/tts_generator/crunker.esm.js index a6f5633..e1086c0 100644 --- a/system/tts_generator/crunker.esm.js +++ b/system/tts_generator/crunker.esm.js @@ -1,2 +1,2 @@ -var e={d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}function r(e){return function(e){if(Array.isArray(e))return a(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return a(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return a(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function a(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);nu});var u=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.sampleRate;o(this,e),this._context=this._createContext(n),n||(n=this._context.sampleRate),this._sampleRate=n}var t,n,a;return t=e,n=[{key:"_createContext",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:44100;return window.AudioContext=window.AudioContext||window.webkitAudioContext||window.mozAudioContext,new AudioContext({sampleRate:e})}},{key:"context",get:function(){return this._context}},{key:"fetchAudio",value:async function(){for(var e=this,t=arguments.length,n=new Array(t),r=0;r=0;o--)r[o]+=a[o];t.getChannelData(n).set(r)}})),t}},{key:"concatAudio",value:function(e){var t=this._context.createBuffer(this._maxNumberOfChannels(e),this._totalLength(e),this._sampleRate),n=0;return e.forEach((function(e){for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(0===n)return e;if(t<0)throw new Error('Crunker: Parameter "padStart" in padAudio must be positive');if(n<0)throw new Error('Crunker: Parameter "seconds" in padAudio must be positive');for(var r=this._context.createBuffer(e.numberOfChannels,Math.ceil(e.length+n*e.sampleRate),e.sampleRate),a=0;a3&&void 0!==arguments[3]?arguments[3]:0,a=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0;if(t>=n)throw new Error('Crunker: "start" time should be less than "end" time in sliceAudio method');for(var o=Math.round((n-t)*this._sampleRate),i=Math.round(t*this._sampleRate),u=this._context.createBuffer(e.numberOfChannels,o,this._sampleRate),l=0;lo-a*this._sampleRate&&(c[f]*=(o-f)/(a*this._sampleRate));return u}},{key:"play",value:function(e){var t=this._context.createBufferSource();return t.buffer=e,t.connect(this._context.destination),t.start(),t}},{key:"export",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"audio/wav",n=this._interleave(e),r=this._writeHeaders(n,e.numberOfChannels,e.sampleRate),a=new Blob([r],{type:t});return{blob:a,url:this._renderURL(a),element:this._renderAudioElement(a)}}},{key:"download",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"crunker",n=document.createElement("a");return n.style.display="none",n.href=this._renderURL(e),n.download="".concat(t,".").concat(e.type.split("/")[1]),n.click(),n}},{key:"notSupported",value:function(e){return this._isSupported()?void 0:e()}},{key:"close",value:function(){return this._context.close(),this}},{key:"_maxDuration",value:function(e){return Math.max.apply(Math,r(e.map((function(e){return e.duration}))))}},{key:"_maxNumberOfChannels",value:function(e){return Math.max.apply(Math,r(e.map((function(e){return e.numberOfChannels}))))}},{key:"_totalLength",value:function(e){return e.map((function(e){return e.length})).reduce((function(e,t){return e+t}),0)}},{key:"_isSupported",value:function(){return"AudioContext"in window||"webkitAudioContext"in window||"mozAudioContext"in window}},{key:"_writeHeaders",value:function(e,t,n){var r=2*t,a=2*e.length,o=36+a,i=new ArrayBuffer(8+o),u=new DataView(i);return this._writeString(u,0,"RIFF"),u.setUint32(4,o,!0),this._writeString(u,8,"WAVE"),this._writeString(u,12,"fmt "),u.setUint32(16,16,!0),u.setUint16(20,1,!0),u.setUint16(22,t,!0),u.setUint32(24,n,!0),u.setUint32(28,n*r,!0),u.setUint16(32,r,!0),u.setUint16(34,16,!0),this._writeString(u,36,"data"),u.setUint32(40,a,!0),this._floatTo16BitPCM(u,e,44)}},{key:"_floatTo16BitPCM",value:function(e,t,n){for(var r=0;r{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}function r(e){return function(e){if(Array.isArray(e))return a(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return a(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return a(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function a(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);nu});var u=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.sampleRate;o(this,e),this._context=this._createContext(n),n||(n=this._context.sampleRate),this._sampleRate=n}var t,n,a;return t=e,n=[{key:"_createContext",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:44100;return window.AudioContext=window.AudioContext||window.webkitAudioContext||window.mozAudioContext,new AudioContext({sampleRate:e})}},{key:"context",get:function(){return this._context}},{key:"fetchAudio",value:async function(){for(var e=this,t=arguments.length,n=new Array(t),r=0;r=0;o--)r[o]+=a[o];t.getChannelData(n).set(r)}})),t}},{key:"concatAudio",value:function(e){var t=this._context.createBuffer(this._maxNumberOfChannels(e),this._totalLength(e),this._sampleRate),n=0;return e.forEach((function(e){for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(0===n)return e;if(t<0)throw new Error('Crunker: Parameter "padStart" in padAudio must be positive');if(n<0)throw new Error('Crunker: Parameter "seconds" in padAudio must be positive');for(var r=this._context.createBuffer(e.numberOfChannels,Math.ceil(e.length+n*e.sampleRate),e.sampleRate),a=0;a3&&void 0!==arguments[3]?arguments[3]:0,a=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0;if(t>=n)throw new Error('Crunker: "start" time should be less than "end" time in sliceAudio method');for(var o=Math.round((n-t)*this._sampleRate),i=Math.round(t*this._sampleRate),u=this._context.createBuffer(e.numberOfChannels,o,this._sampleRate),l=0;lo-a*this._sampleRate&&(c[f]*=(o-f)/(a*this._sampleRate));return u}},{key:"play",value:function(e){var t=this._context.createBufferSource();return t.buffer=e,t.connect(this._context.destination),t.start(),t}},{key:"export",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"audio/wav",n=this._interleave(e),r=this._writeHeaders(n,e.numberOfChannels,e.sampleRate),a=new Blob([r],{type:t});return{blob:a,url:this._renderURL(a),element:this._renderAudioElement(a)}}},{key:"download",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"crunker",n=document.createElement("a");return n.style.display="none",n.href=this._renderURL(e),n.download="".concat(t,".").concat(e.type.split("/")[1]),n.click(),n}},{key:"notSupported",value:function(e){return this._isSupported()?void 0:e()}},{key:"close",value:function(){return this._context.close(),this}},{key:"_maxDuration",value:function(e){return Math.max.apply(Math,r(e.map((function(e){return e.duration}))))}},{key:"_maxNumberOfChannels",value:function(e){return Math.max.apply(Math,r(e.map((function(e){return e.numberOfChannels}))))}},{key:"_totalLength",value:function(e){return e.map((function(e){return e.length})).reduce((function(e,t){return e+t}),0)}},{key:"_isSupported",value:function(){return"AudioContext"in window||"webkitAudioContext"in window||"mozAudioContext"in window}},{key:"_writeHeaders",value:function(e,t,n){var r=2*t,a=2*e.length,o=36+a,i=new ArrayBuffer(8+o),u=new DataView(i);return this._writeString(u,0,"RIFF"),u.setUint32(4,o,!0),this._writeString(u,8,"WAVE"),this._writeString(u,12,"fmt "),u.setUint32(16,16,!0),u.setUint16(20,1,!0),u.setUint16(22,t,!0),u.setUint32(24,n,!0),u.setUint32(28,n*r,!0),u.setUint16(32,r,!0),u.setUint16(34,16,!0),this._writeString(u,36,"data"),u.setUint32(40,a,!0),this._floatTo16BitPCM(u,e,44)}},{key:"_floatTo16BitPCM",value:function(e,t,n){for(var r=0;rEnglish +