UNPKG

js-tts-wrapper

Version:

A JavaScript/TypeScript library that provides a unified API for working with multiple cloud-based Text-to-Speech (TTS) services

1 lines 725 kB
!function(e,a){"object"==typeof exports&&"undefined"!=typeof module?a(exports):"function"==typeof define&&define.amd?define(["exports"],a):a((e="undefined"!=typeof globalThis?globalThis:e||self).JSTTSWrapper={})}(this,(function(e){"use strict";function a(e,a){return a.forEach((function(a){a&&"string"!=typeof a&&!Array.isArray(a)&&Object.keys(a).forEach((function(i){if("default"!==i&&!(i in e)){var n=Object.getOwnPropertyDescriptor(a,i);Object.defineProperty(e,i,n.get?n:{enumerable:!0,get:function(){return a[i]}})}}))})),Object.freeze(e)}const i="undefined"!=typeof window,n=!i&&"undefined"!=typeof process&&void 0!==process.versions&&void 0!==process.versions.node,r=e=>{if(n){return new Function("n","return require(n)")("node:fs").readFileSync(e,"utf-8")}throw new Error("Synchronous file reading is not supported in browsers")},t=e=>{if(n){return new Function("n","return require(n)")("node:fs").existsSync(e)}throw new Error("Synchronous file existence check is not supported in browsers")},l=(...e)=>{if(n){return new Function("n","return require(n)")("node:path").join(...e)}return e.join("/").replace(/\/+/g,"/")},s={},o=/^(1|true|yes|on)$/i,d=/^(0|false|no|off)$/i;function u(e){if(null==e)return;const a=String(e).trim();return a?!!o.test(a)||!d.test(a)&&void 0:void 0}function c(){if("boolean"==typeof s.enabled)return s.enabled;const e=function(){var e;if(n)try{const a=null!==(e=null===process||void 0===process?void 0:process.env)&&void 0!==e?e:{},i=u(a.SPEECHMARKDOWN_DISABLE);if(!0===i)return!1;if(!1===i)return!0;const n=u(a.SPEECHMARKDOWN_ENABLE);if(void 0!==n)return n}catch(e){}}();return"boolean"!=typeof e||e}let h=null,p=!1;function m(e){let a=e;return a=a.replace(/\[break:"([^"]+)"\]/g,'<break time="$1"/>'),a=a.replace(/\[(\d+)m?s\]/g,'<break time="$1ms"/>'),a=a.replace(/\+\+([\s\S]+?)\+\+/g,'<emphasis level="strong">$1</emphasis>'),a=a.replace(/\(([\s\S]+?)\)\[rate:['"]([^'"]+)['"]\]/g,'<prosody rate="$2">$1</prosody>'),a=a.replace(/\(([\s\S]+?)\)\[pitch:['"]([^'"]+)['"]\]/g,'<prosody pitch="$2">$1</prosody>'),a=a.replace(/\(([\s\S]+?)\)\[volume:['"]([^'"]+)['"]\]/g,'<prosody volume="$2">$1</prosody>'),a}const g=new class{constructor(){this.speechMarkdownInstance=null}async ensureInitialized(){if(!c())return this.speechMarkdownInstance=null,null;if(!this.speechMarkdownInstance){const e=await async function(){var e,a,i;if(p)return h;try{if(!c())return console.warn("speechmarkdown-js disabled (set SPEECHMARKDOWN_DISABLE=false or configureSpeechMarkdown({ enabled: true }) to re-enable). Using built-in fallback."),null;let r=null;if(n)try{const e="undefined"!=typeof require?require:void 0;e&&(r=e("speechmarkdown-js"))}catch(e){}if(!r)try{r=await Promise.resolve().then((function(){return ii}))}catch(e){}if(h=null!==(i=null!==(e=null==r?void 0:r.SpeechMarkdown)&&void 0!==e?e:null===(a=null==r?void 0:r.default)||void 0===a?void 0:a.SpeechMarkdown)&&void 0!==i?i:null==r?void 0:r.default,!h)throw new Error("speechmarkdown-js module did not expose SpeechMarkdown class");return p=!0,h}catch(e){return console.warn("speechmarkdown-js not available. Using built-in fallback. To enable full Speech Markdown in browsers, add 'speechmarkdown-js' to your app and it will be loaded at runtime."),null}}();e&&(this.speechMarkdownInstance=new e)}return this.speechMarkdownInstance}async toSSML(e,a="amazon-alexa"){if(!c()){this.speechMarkdownInstance=null;return`<speak>${m(e)}</speak>`}if(await this.ensureInitialized(),this.speechMarkdownInstance)return this.speechMarkdownInstance.toSSML(e,{platform:a});return`<speak>${m(e)}</speak>`}isSpeechMarkdown(e){return N(e)}getAvailablePlatforms(){return["amazon-alexa","google-assistant","microsoft-azure"]}};async function v(e,a="amazon-alexa"){return await g.toSSML(e,a)}function N(e){return[/\[\d+m?s\]/,/\[break:"[^"\]]+"\]/,/\+\+.*?\+\+/,/\+.*?\+/,/~.*?~/,/-.*?-/,/\(.*?\)\[emphasis(:"(strong|moderate|reduced|none)")?\]/,/\(.*?\)\[rate:"(x-slow|slow|medium|fast|x-fast)"\]/,/\(.*?\)\[pitch:"(x-low|low|medium|high|x-high)"\]/,/\(.*?\)\[volume:"(silent|x-soft|soft|medium|loud|x-loud)"\]/,/\(.*?\)\[voice:".*?"\]/,/\(.*?\)\[lang:".*?"\]/,/\(.*?\)\[\w+:"?.*?"?\]/].some((a=>a.test(e)))}class y{constructor(){this.ssml=""}add(e){return e.trim().startsWith("<speak")?this.ssml=e:this.ssml=`<speak>${e}</speak>`,this.ssml}addBreak(e="500ms"){return this.ssml=this.ssml.replace("</speak>",`<break time="${e}"/></speak>`),this}addProsody(e,a,i,n){let r="";a&&(r+=` rate="${a}"`),i&&(r+=` pitch="${i}"`),n&&(r+=` volume="${n}"`);const t=`<prosody${r}>${e}</prosody>`;return this.ssml.includes("<speak>")?this.ssml=this.ssml.replace("<speak>",`<speak>${t}`):this.ssml=`<speak>${t}</speak>`,this}wrapWithSpeak(e){return e.trim().startsWith("<speak")?e:`<speak>${e}</speak>`}clearSSML(){this.ssml=""}toString(){return this.ssml}}async function f(e){const a=[];let i=0;if("getReader"in e&&"function"==typeof e.getReader){const r=e.getReader();try{for(;;){const{done:e,value:n}=await r.read();if(e)break;n&&(a.push(n),i+=n.length)}}finally{r.releaseLock()}if(n){const e=a.map((e=>Buffer.from(e)));return Buffer.concat(e,i)}const t=new Uint8Array(i);let l=0;for(const e of a)t.set(e,l),l+=e.length;return t}if("function"==typeof e.on)return new Promise(((n,r)=>{const t=e;t.on("data",(e=>{const n=Buffer.isBuffer(e)?e:Buffer.from(e);a.push(n),i+=n.length})),t.on("end",(()=>{n(Buffer.concat(a,i))})),t.on("error",(e=>{r(e)}))}));throw new Error("Unsupported stream type provided to streamToBuffer")}function C(e){const a=[e.text,e.filename,e.audioBytes,e.audioStream].filter(Boolean).length;if(0===a)throw new Error("No input provided. Please provide text, filename, audioBytes, or audioStream.");if(a>1)throw new Error("Multiple input sources provided. Please provide only one of: text, filename, audioBytes, or audioStream.")}function S(e){switch(e.toLowerCase().split(".").pop()){case"mp3":return"audio/mpeg";case"wav":default:return"audio/wav";case"ogg":return"audio/ogg";case"opus":return"audio/opus";case"aac":return"audio/aac";case"flac":return"audio/flac"}}function b(e){if(e.length<4)return"audio/wav";const a=Array.from(e.slice(0,12));return 73===a[0]&&68===a[1]&&51===a[2]||255===a[0]&&!(224&~a[1])?"audio/mpeg":82===a[0]&&73===a[1]&&70===a[2]&&70===a[3]&&87===a[8]&&65===a[9]&&86===a[10]&&69===a[11]?"audio/wav":79===a[0]&&103===a[1]&&103===a[2]&&83===a[3]?"audio/ogg":102===a[0]&&76===a[1]&&97===a[2]&&67===a[3]?"audio/flac":"audio/wav"}async function D(e){if(!n)throw new Error("File reading is only supported in Node.js environment");try{const a=await new Function("m","return import(m)")("node:fs/promises"),i=await a.readFile(e);return new Uint8Array(i)}catch(a){throw new Error(`Failed to read audio file "${e}": ${a instanceof Error?a.message:String(a)}`)}}async function H(e){const a=await f(e);return a instanceof Buffer?new Uint8Array(a):a}var A=Object.freeze({__proto__:null,detectAudioFormat:b,getAudioFormatFromFilename:S,processAudioInput:async function(e){if(C(e),e.audioBytes)return{audioBytes:e.audioBytes,mimeType:b(e.audioBytes)};if(e.audioStream){const a=await H(e.audioStream);return{audioBytes:a,mimeType:b(a)}}if(e.filename){return{audioBytes:await D(e.filename),mimeType:S(e.filename)}}throw new Error("No valid audio input provided")},readAudioFile:D,streamToBytes:H,validateSpeakInput:C});function I(e,a){return e.filter((e=>e.gender===a))}var T=Object.freeze({__proto__:null,filterByGender:I,filterByLanguage:function(e,a){return e.filter((e=>e.languageCodes.some((e=>e.bcp47.toLowerCase()===a.toLowerCase()))))},filterByProvider:function(e,a){return e.filter((e=>e.provider===a))},findById:function(e,a){return e.find((e=>e.id===a))},getAvailableLanguages:function(e){const a=new Set;for(const i of e)for(const e of i.languageCodes)a.add(e.bcp47);return Array.from(a)}});class w{static normalize(e,a){try{let i,n;if(e.startsWith("mms_")&&(e=e.substring(4)),e.includes("-")){const a=e.split("-");i=a[0].toLowerCase(),n=a[1].toUpperCase()}else i=e.toLowerCase(),n=null==a?void 0:a.toUpperCase();const r=w.iso1To3[i]||i,t=n?`${i}-${n}`:i;let l=w.languageNames[i]||i;return n&&w.regionNames[n]?l+=` (${w.regionNames[n]})`:n&&(l+=` (${n})`),{iso639_3:r,bcp47:t,display:l,countryCode:n}}catch(e){return{iso639_3:"und",bcp47:"und",display:"Unknown"}}}static getDisplayName(e){return w.normalize(e).display}static getISO639_3(e){return w.normalize(e).iso639_3}static getBCP47(e,a){return w.normalize(e,a).bcp47}}w.languageNames={en:"English",fr:"French",es:"Spanish",de:"German",it:"Italian",ja:"Japanese",ko:"Korean",zh:"Chinese",ru:"Russian",pt:"Portuguese",ar:"Arabic",hi:"Hindi",nl:"Dutch",sv:"Swedish",fi:"Finnish",no:"Norwegian",da:"Danish",pl:"Polish",tr:"Turkish",cs:"Czech",hu:"Hungarian",el:"Greek",he:"Hebrew",th:"Thai",vi:"Vietnamese",id:"Indonesian",ms:"Malay",ro:"Romanian",sk:"Slovak",uk:"Ukrainian",bg:"Bulgarian",hr:"Croatian",lt:"Lithuanian",lv:"Latvian",et:"Estonian",sl:"Slovenian",sr:"Serbian"},w.regionNames={US:"United States",GB:"United Kingdom",AU:"Australia",CA:"Canada",IN:"India",IE:"Ireland",ZA:"South Africa",NZ:"New Zealand",FR:"France",DE:"Germany",IT:"Italy",ES:"Spain",MX:"Mexico",JP:"Japan",KR:"Korea",CN:"China",TW:"Taiwan",HK:"Hong Kong",BR:"Brazil",PT:"Portugal",RU:"Russia"},w.iso1To3={ar:"ara",bg:"bul",ca:"cat",cs:"ces",da:"dan",de:"deu",el:"ell",en:"eng",es:"spa",et:"est",fi:"fin",fr:"fra",he:"heb",hi:"hin",hr:"hrv",hu:"hun",id:"ind",it:"ita",ja:"jpn",ko:"kor",lt:"lit",lv:"lav",ms:"msa",nl:"nld",no:"nor",pl:"pol",pt:"por",ro:"ron",ru:"rus",sk:"slk",sl:"slv",sr:"srp",sv:"swe",th:"tha",tr:"tur",uk:"ukr",vi:"vie",zh:"zho"};const k={sapi:{supportsSSML:!0,supportLevel:"full",supportedTags:["speak","prosody","break","emphasis","voice","phoneme","say-as","sub","p","s"],unsupportedTags:[],requiresNamespace:!1,requiresVersion:!0},witai:{supportsSSML:!0,supportLevel:"full",supportedTags:["speak","prosody","break","emphasis","voice","phoneme","say-as","sub","p","s"],unsupportedTags:[],requiresNamespace:!1,requiresVersion:!1},watson:{supportsSSML:!0,supportLevel:"full",supportedTags:["speak","prosody","break","emphasis","voice","phoneme","say-as","sub","p","s"],unsupportedTags:[],requiresNamespace:!1,requiresVersion:!1},cerevoice:{supportsSSML:!0,supportLevel:"full",supportedTags:["speak","audio","break","emphasis","lexicon","mark","meta","metadata","p","phoneme","prosody","say-as","sub","s","voice"],unsupportedTags:["lang"],requiresNamespace:!1,requiresVersion:!1},azure:{supportsSSML:!0,supportLevel:"full",supportedTags:["speak","prosody","break","emphasis","voice","phoneme","say-as","sub","p","s","mstts:express-as"],unsupportedTags:[],requiresNamespace:!0,requiresVersion:!0},polly:{supportsSSML:!0,supportLevel:"limited",supportedTags:["speak","prosody","break","voice","phoneme","say-as","sub","p","s","mark","lang"],unsupportedTags:[],requiresNamespace:!0,requiresVersion:!1},google:{supportsSSML:!0,supportLevel:"limited",supportedTags:["speak","prosody","break","emphasis","voice","phoneme","say-as","sub","p","s","mark","lang","audio"],unsupportedTags:[],requiresNamespace:!1,requiresVersion:!1},elevenlabs:{supportsSSML:!1,supportLevel:"none",supportedTags:[],unsupportedTags:["*"],requiresNamespace:!1,requiresVersion:!1},openai:{supportsSSML:!1,supportLevel:"none",supportedTags:[],unsupportedTags:["*"],requiresNamespace:!1,requiresVersion:!1},playht:{supportsSSML:!1,supportLevel:"none",supportedTags:[],unsupportedTags:["*"],requiresNamespace:!1,requiresVersion:!1},upliftai:{supportsSSML:!1,supportLevel:"none",supportedTags:[],unsupportedTags:["*"],requiresNamespace:!1,requiresVersion:!1},sherpaonnx:{supportsSSML:!1,supportLevel:"none",supportedTags:[],unsupportedTags:["*"],requiresNamespace:!1,requiresVersion:!1},"sherpaonnx-wasm":{supportsSSML:!1,supportLevel:"none",supportedTags:[],unsupportedTags:["*"],requiresNamespace:!1,requiresVersion:!1},espeak:{supportsSSML:!0,supportLevel:"limited",supportedTags:["speak","prosody","break","emphasis","p","s"],unsupportedTags:["voice","phoneme","say-as","sub"],requiresNamespace:!1,requiresVersion:!1},"espeak-wasm":{supportsSSML:!0,supportLevel:"limited",supportedTags:["speak","prosody","break","emphasis","p","s"],unsupportedTags:["voice","phoneme","say-as","sub"],requiresNamespace:!1,requiresVersion:!1}},E={polly:{standard:{supportLevel:"full",unsupportedTags:[]},"long-form":{supportLevel:"full",unsupportedTags:[]},neural:{supportLevel:"limited",unsupportedTags:["emphasis","amazon:auto-breaths","amazon:effect"]},generative:{supportLevel:"limited",unsupportedTags:["emphasis","amazon:auto-breaths","amazon:effect","mark"]}},google:{standard:{supportLevel:"full",unsupportedTags:[]},wavenet:{supportLevel:"full",unsupportedTags:[]},neural2:{supportLevel:"limited",unsupportedTags:["mark"]},journey:{supportLevel:"none",unsupportedTags:["*"]},studio:{supportLevel:"none",unsupportedTags:["*"]}}};class U{static getCapabilities(e,a){const i=k[e];if(!i)return{supportsSSML:!1,supportLevel:"none",supportedTags:[],unsupportedTags:["*"],requiresNamespace:!1,requiresVersion:!1};if(a&&E[e]){const n=U.getVoiceSpecificCapabilities(e,a);if(n)return{...i,supportLevel:n.supportLevel,unsupportedTags:n.unsupportedTags}}return i}static getVoiceSpecificCapabilities(e,a){const i=E[e];if(!i)return null;return i[U.detectVoiceType(e,a)]||null}static detectVoiceType(e,a){const i=a.toLowerCase();switch(e){case"polly":return i.includes("neural")?"neural":i.includes("generative")?"generative":i.includes("long-form")?"long-form":"standard";case"google":return i.includes("neural2")?"neural2":i.includes("journey")?"journey":i.includes("studio")?"studio":i.includes("wavenet")?"wavenet":(i.includes("standard"),"standard");default:return"default"}}static validateSSML(e,a,i){const n=U.getCapabilities(a,i),r=[],t=[];if(e.trim().startsWith("<speak")&&e.trim().endsWith("</speak>")||r.push("SSML must be wrapped in <speak> tags"),!n.supportsSSML)return t.push(`Engine '${a}' does not support SSML. Tags will be stripped.`),{isValid:!0,errors:r,warnings:t};if(n.unsupportedTags.includes("*"))t.push(`Engine '${a}' does not support any SSML tags. All tags will be stripped.`);else for(const i of n.unsupportedTags){new RegExp(`<${i}[^>]*>`,"gi").test(e)&&t.push(`Tag '<${i}>' is not supported by engine '${a}' and will be removed.`)}return n.requiresNamespace&&!e.includes("xmlns=")&&t.push(`Engine '${a}' requires xmlns attribute in <speak> tag.`),n.requiresVersion&&!e.includes("version=")&&t.push(`Engine '${a}' requires version attribute in <speak> tag.`),{isValid:0===r.length,errors:r,warnings:t}}static processSSMLForEngine(e,a,i){const n=U.getCapabilities(a,i);if(!n.supportsSSML)return U.stripAllSSMLTags(e);let r=e;if(n.unsupportedTags.includes("*"))return U.stripAllSSMLTags(e);for(const e of n.unsupportedTags)r=U.removeSSMLTag(r,e);return r=U.addRequiredAttributes(r,n),r}static stripAllSSMLTags(e){let a=e;a=a.replace(/<speak[^>]*>/gi,""),a=a.replace(/<\/speak>/gi,""),a=a.replace(/<break[^>]*\/?>/gi," ");let i="";for(;a!==i;)i=a,a=a.replace(/<emphasis[^>]*>(.*?)<\/emphasis>/gis,"$1"),a=a.replace(/<prosody[^>]*>(.*?)<\/prosody>/gis,"$1"),a=a.replace(/<voice[^>]*>(.*?)<\/voice>/gis,"$1"),a=a.replace(/<say-as[^>]*>(.*?)<\/say-as>/gis,"$1"),a=a.replace(/<phoneme[^>]*>(.*?)<\/phoneme>/gis,"$1"),a=a.replace(/<sub[^>]*>(.*?)<\/sub>/gis,"$1"),a=a.replace(/<p[^>]*>(.*?)<\/p>/gis,"$1 "),a=a.replace(/<s[^>]*>(.*?)<\/s>/gis,"$1 "),a=a.replace(/<lang[^>]*>(.*?)<\/lang>/gis,"$1"),a=a.replace(/<audio[^>]*>(.*?)<\/audio>/gis,"$1"),a=a.replace(/<mark[^>]*\/?>/gi,""),a=a.replace(/<[^>]+>/g,"");return a=a.replace(/\s+/g," ").trim(),a}static removeSSMLTag(e,a){let i=e;const n=new RegExp(`<${a}[^>]*\\/>`,"gi");i=i.replace(n,"");const r=new RegExp(`<${a}[^>]*>(.*?)<\\/${a}>`,"gi");return i=i.replace(r,"$1"),i}static addRequiredAttributes(e,a){let i=e;return a.requiresNamespace&&!e.includes("xmlns=")&&(i=i.replace(/<speak([^>]*)>/i,'<speak$1 xmlns="http://www.w3.org/2001/10/synthesis">')),a.requiresVersion&&!e.includes("version=")&&(i=i.replace(/<speak([^>]*)>/i,'<speak version="1.0"$1>')),i}}function R(e){return e.trim().startsWith("<speak")&&e.trim().endsWith("</speak>")}function M(e,a,i){return U.validateSSML(e,a,i)}function B(e,a,i){return U.processSSMLForEngine(e,a,i)}function L(e){return e.replace(/<speak.*?>/g,"").replace(/<\/speak>/g,"").replace(/<break.*?\/>/g," ").replace(/<emphasis.*?>(.*?)<\/emphasis>/g,"$1").replace(/<prosody.*?>(.*?)<\/prosody>/g,"$1").replace(/<voice.*?>(.*?)<\/voice>/g,"$1").replace(/<say-as.*?>(.*?)<\/say-as>/g,"$1").replace(/<phoneme.*?>(.*?)<\/phoneme>/g,"$1").replace(/<sub.*?>(.*?)<\/sub>/g,"$1").replace(/<p>(.*?)<\/p>/g,"$1 ").replace(/<s>(.*?)<\/s>/g,"$1 ").replace(/\s+/g," ").trim()}function x(e){return R(e)?e:`<speak>${e}</speak>`}class P{constructor(e){this.credentials=e,this.voiceId=null,this.lang="en-US",this.callbacks={},this.properties={volume:100,rate:"medium",pitch:"medium"},this.timings=[],this._models=[],this.capabilities={browserSupported:!0,nodeSupported:!0,needsWasm:!1},this.sampleRate=24e3,this.ssml=new y,this.audio={isPlaying:!1,isPaused:!1,audioElement:null,position:0,duration:0}}async synthToBytesWithFormat(e,a){return this.synthToBytesWithConversion(e,a)}async synthToBytesWithConversion(e,a){const i=this.normalizeSpeechMarkdownOptions(e,a),r=await this.synthToBytes(e,i);if(!(null==i?void 0:i.format))return r;const t=i.format,l=this.detectNativeFormat(r);if(l===t)return r;if(!n)return console.warn(`Audio format conversion not available in browser. Returning native format (${l}) instead of requested format (${t})`),r;try{const{isAudioConversionAvailable:e,convertAudioFormat:a}=await new Function("m","return import(m)")("../utils/audio-converter");if(e())try{return(await a(r,t)).audioBytes}catch(e){console.warn(`Audio format conversion failed: ${e instanceof Error?e.message:String(e)}`),console.warn(`Returning native format (${l}) instead of requested format (${t})`)}else console.warn(`Audio format conversion not available. Returning native format (${l}) instead of requested format (${t})`)}catch(e){console.warn(`Audio converter not available at runtime; returning native format (${l})`)}return r}detectNativeFormat(e){switch(b(e)){case"audio/mpeg":return"mp3";case"audio/ogg":return"ogg";default:return"wav"}}async getVoices(){const e=await this._getVoices();return(await this._mapVoicesToUnified(e)).map((e=>{const a=e.languageCodes.map((e=>{const a=w.normalize(e.bcp47);return{bcp47:a.bcp47,iso639_3:a.iso639_3,display:a.display}}));return{...e,languageCodes:a}}))}async _mapVoicesToUnified(e){return e}async speak(e,a){this.emit("start");try{let r,t;if("string"==typeof e)r=await this.synthToBytesWithConversion(e,a),t=b(r);else{const{processAudioInput:a}=await Promise.resolve().then((function(){return A})),i=await a(e);r=i.audioBytes,t=i.mimeType}if(i){const a=new ArrayBuffer(r.byteLength);new Uint8Array(a).set(r);const i=new Blob([a],{type:t}),n=URL.createObjectURL(i),l=new Audio;l.oncanplay=async()=>{try{this.audio.audioElement=l,this.audio.isPlaying=!0,this.audio.isPaused=!1,"string"==typeof e&&this._createEstimatedWordTimings(e),await l.play()}catch(e){console.error("Error playing audio:",e),this.emit("end")}},l.onerror=e=>{console.error("Audio playback error:",e),this.emit("end"),URL.revokeObjectURL(n)},l.onended=()=>{this.emit("end"),this.audio.isPlaying=!1,URL.revokeObjectURL(n)},l.src=n}else if(n)try{const a=await Promise.resolve().then((function(){return Si})),{isNodeAudioAvailable:i,playAudioInNode:n}=a,t=await i();"string"==typeof e&&this._createEstimatedWordTimings(e),t?(this.emit("start"),this._scheduleWordBoundaryCallbacks(),await n(r,this.sampleRate,this.constructor.name.replace("TTSClient","").toLowerCase()),this.emit("end")):(console.log("Audio playback in Node.js requires the sound-play package."),console.log("Install it with: npm install js-tts-wrapper[node-audio]"),console.log("Or use synthToFile() to save audio to a file and play it with an external player."),this._fireWordBoundaryCallbacks(),this.emit("end"))}catch(e){console.error("Error playing audio in Node.js:",e),this._fireWordBoundaryCallbacks(),this.emit("end")}else console.log("Audio playback is not supported in this environment."),console.log("Use synthToFile() to save audio to a file and play it with an external player."),this.emit("end")}catch(e){throw console.error("Error in speak method:",e),this.emit("end"),e}}async speakStreamed(e,a){this.emit("start");try{let r,t,l=[],s="";if("string"==typeof e){s=e;const i=this.normalizeSpeechMarkdownOptions(s,a),o=await this.synthToBytestream(s,i),d=o.audioStream;l=o.wordBoundaries;const u=d.getReader(),c=[];let h=await u.read();for(;!h.done;)c.push(h.value),h=await u.read();const p=c.reduce(((e,a)=>e+a.length),0);r=new Uint8Array(p);let m=0;for(const e of c)r.set(e,m),m+=e.length;if(null==i?void 0:i.format)if(n)try{const{isAudioConversionAvailable:e,convertAudioFormat:a}=await new Function("m","return import(m)")("../utils/audio-converter");if(e()){const e=await a(r,i.format);r=e.audioBytes,t=e.mimeType}else t=b(r)}catch(e){console.warn(`Streaming format conversion failed: ${e instanceof Error?e.message:String(e)}`),t=b(r)}else t=b(r);else t=b(r)}else{const{processAudioInput:a}=await Promise.resolve().then((function(){return A})),i=await a(e);r=i.audioBytes,t=i.mimeType,s=""}if(l.length>0?this.timings=l.map((e=>[e.offset/1e4,(e.offset+e.duration)/1e4,e.text])):s?this._createEstimatedWordTimings(s):this.timings=[],i){const e=new ArrayBuffer(r.byteLength);new Uint8Array(e).set(r);const a=new Blob([e],{type:t}),i=URL.createObjectURL(a),n=new Audio;n.oncanplay=async()=>{try{this.audio.audioElement=n,this.audio.isPlaying=!0,this.audio.isPaused=!1,await n.play()}catch(e){console.error("Error playing audio:",e),this.emit("end")}},n.onerror=e=>{console.error("Audio playback error:",e),this.emit("end"),URL.revokeObjectURL(i)},n.onended=()=>{this.emit("end"),this.audio.isPlaying=!1,URL.revokeObjectURL(i)},n.src=i}else if(n){console.log("🔍 Taking Node.js audio path");try{const e=await Promise.resolve().then((function(){return Si})),{isNodeAudioAvailable:a,playAudioInNode:i}=e,n=await a();console.log(`🔍 Audio available: ${n}`),s&&(console.log(`🔍 Creating estimated word timings for: "${s}"`),this._createEstimatedWordTimings(s),console.log(`🔍 Created ${this.timings.length} timings`)),n?(console.log("🔍 Audio available - scheduling word boundary callbacks"),this._scheduleWordBoundaryCallbacks(),await i(r,this.sampleRate,this.constructor.name.replace("TTSClient","").toLowerCase()),this.emit("end")):(console.log("Audio playback in Node.js requires the sound-play package."),console.log("Install it with: npm install js-tts-wrapper[node-audio]"),console.log("Or use synthToFile() to save audio to a file and play it with an external player."),this._fireWordBoundaryCallbacks(),this.emit("end"))}catch(e){console.error("Error playing audio in Node.js:",e),this._fireWordBoundaryCallbacks(),this.emit("end")}}else console.log("Audio playback is not supported in this environment."),console.log("Use synthToFile() to save audio to a file and play it with an external player."),s&&this._createEstimatedWordTimings(s),setTimeout((()=>{this._fireWordBoundaryCallbacks(),this.emit("end")}),100)}catch(e){throw console.error("Error in streaming synthesis:",e),this.emit("end"),e}}async synthToFile(e,a,r="wav",t){const l=await this.synthToBytesWithConversion(e,{...t,format:r});if(i){const e="mp3"===r?"audio/mpeg":"audio/wav",i=new ArrayBuffer(l.byteLength);new Uint8Array(i).set(l);const n=new Blob([i],{type:e}),t=URL.createObjectURL(n),s=document.createElement("a");s.href=t,s.download=a.endsWith(`.${r}`)?a:`${a}.${r}`,document.body.appendChild(s),s.click(),requestAnimationFrame((()=>{var e;(null===(e=null===document||void 0===document?void 0:document.body)||void 0===e?void 0:e.contains(s))&&document.body.removeChild(s),URL.revokeObjectURL(t)}))}else if(n){const e=a.endsWith(`.${r}`)?a:`${a}.${r}`;(await new Function("m","return import(m)")("node:fs")).writeFileSync(e,Buffer.from(l))}else console.warn("File saving not implemented for this environment.")}setVoice(e,a){this.voiceId=e,a&&(this.lang=a)}pause(){if(i)this.audio.audioElement&&this.audio.isPlaying&&!this.audio.isPaused&&(this.audio.audioElement.pause(),this.audio.isPaused=!0);else if(n)try{Promise.resolve().then((function(){return bi})).then((e=>{e.pauseAudioPlayback()&&(this.audio.isPaused=!0)})).catch((e=>{console.error("Error importing node-audio-control:",e)}))}catch(e){console.error("Error pausing audio in Node.js:",e)}}resume(){if(i)this.audio.audioElement&&this.audio.isPlaying&&this.audio.isPaused&&(this.audio.audioElement.play(),this.audio.isPaused=!1);else if(n)try{Promise.resolve().then((function(){return bi})).then((e=>{e.resumeAudioPlayback()&&(this.audio.isPaused=!1)})).catch((e=>{console.error("Error importing node-audio-control:",e)}))}catch(e){console.error("Error resuming audio in Node.js:",e)}}stop(){if(i)this.audio.audioElement&&(this.audio.audioElement.pause(),this.audio.audioElement.currentTime=0,this.audio.isPlaying=!1,this.audio.isPaused=!1);else if(n)try{Promise.resolve().then((function(){return bi})).then((e=>{e.stopAudioPlayback()&&(this.audio.isPlaying=!1,this.audio.isPaused=!1)})).catch((e=>{console.error("Error importing node-audio-control:",e)}))}catch(e){console.error("Error stopping audio in Node.js:",e)}}_createEstimatedWordTimings(e){const a=(this._isSSML(e)?this._stripSSML(e):e).split(/\s+/).filter((e=>e.length>0));if(!a.length)return;const i=.3*a.length/a.length;this.timings=[];for(let e=0;e<a.length;e++){const n=e*i,r=(e+1)*i;this.timings.push([n,r,a[e]])}}_fireWordBoundaryCallbacks(){if(this.timings.length)for(const[e,a,i]of this.timings)this.emit("boundary",{text:i,offset:Math.round(1e4*e),duration:Math.round(1e4*(a-e))})}_scheduleWordBoundaryCallbacks(){if(this.timings.length)for(const[e,a,i]of this.timings){const n={text:i,offset:Math.round(1e4*e),duration:Math.round(1e4*(a-e))};setTimeout((()=>{this.emit("boundary",n)}),1e3*e)}}_isSSML(e){return R(e)}_stripSSML(e){return L(e)}normalizeSpeechMarkdownOptions(e,a){return void 0!==(null==a?void 0:a.useSpeechMarkdown)||(null==a?void 0:a.rawSSML)||R(e)?a:N(e)?{...a,useSpeechMarkdown:!0}:a}on(e,a){this.callbacks[e]=this.callbacks[e]||[],this.callbacks[e].push(a)}emit(e,...a){for(const i of this.callbacks[e]||[])i(...a)}async startPlaybackWithCallbacks(e,a,i){await this.speak(e,i);for(const[e,i,n]of this.timings)setTimeout((()=>{a(n,e,i)}),1e3*e)}connect(e,a){"onStart"===e?this.on("start",a):"onEnd"===e&&this.on("end",a)}getProperty(e){return this.properties[e]}setProperty(e,a){this.properties[e]=a}constructProsodyTag(e){const a=[];return this.properties.rate&&a.push(`rate="${this.properties.rate}"`),this.properties.pitch&&a.push(`pitch="${this.properties.pitch}"`),this.properties.volume&&a.push(`volume="${this.properties.volume}"`),0===a.length?e:`<prosody ${a.join(" ")}>${e}</prosody>`}async checkCredentials(){try{return(await this._getVoices()).length>0}catch(e){return console.error("Error checking credentials:",e),!1}}async checkCredentialsDetailed(){try{const e=await this._getVoices();return{success:e.length>0,voiceCount:e.length}}catch(e){return console.error("Error checking credentials:",e),{success:!1,error:e instanceof Error?e.message:String(e)}}}async getCredentialStatus(){const e="undefined"!=typeof window,a=this.constructor.name.replace("TTSClient","").toLowerCase();try{const i=await this.checkCredentials(),n=this.getRequiredCredentials().length>0;let r=[];if(i&&n)try{r=await this._getVoices()}catch(e){console.warn("getCredentialStatus: _getVoices() failed; continuing without voices",e),r=[]}return{valid:i,engine:a,environment:e?"browser":"node",requiresCredentials:n,credentialTypes:this.getRequiredCredentials(),message:i?`${a} credentials are valid and ${r.length} voices are available`:`${a} credentials are invalid or service is unavailable`,details:{voiceCount:r.length,hasCredentials:Object.keys(this.credentials||{}).length>0}}}catch(i){return{valid:!1,engine:a,environment:e?"browser":"node",requiresCredentials:this.getRequiredCredentials().length>0,credentialTypes:this.getRequiredCredentials(),message:`Error validating ${a} credentials`,error:i instanceof Error?i.message:String(i)}}}getModels(){return this._models}hasFeature(e,a){const i=a||this._getCurrentModelId();if(!i)return!1;const n=this._models.find((e=>e.id===i));return!!n&&n.features.includes(e)}_getCurrentModelId(){return this.model||""}getRequiredCredentials(){return[]}async getVoicesByLanguage(e){const a=w.normalize(e);return(await this.getVoices()).filter((e=>e.languageCodes.some((e=>e.bcp47===a.bcp47||e.iso639_3===a.iso639_3))))}async getVoicesByGender(e){return I(await this.getVoices(),e)}}class O extends P{constructor(e){super(e),this.sdk=null,this.sdkLoadingPromise=null,this._models=[{id:"azure",features:["streaming","ssml","word-boundary-events"]}],this.subscriptionKey=e.subscriptionKey,this.region=e.region,this.voiceId="en-US-AriaNeural"}async checkCredentials(){if(!this.subscriptionKey||!this.region)return console.error("Azure subscription key and region are required"),!1;try{return(await this._getVoices()).length>0}catch(e){return console.error("Error checking Azure credentials:",e),!1}}getRequiredCredentials(){return["subscriptionKey","region"]}async _getVoices(){try{const e=await fetch(`https://${this.region}.tts.speech.microsoft.com/cognitiveservices/voices/list`,{method:"GET",headers:{"Ocp-Apim-Subscription-Key":this.subscriptionKey}});if(!e.ok)throw new Error(`Failed to fetch voices: ${e.statusText}`);return await e.json()}catch(e){return console.error("Error fetching Azure voices:",e),[]}}async _mapVoicesToUnified(e){return e.map((e=>({id:e.ShortName,name:e.DisplayName,gender:"Female"===e.Gender?"Female":"Male"===e.Gender?"Male":"Unknown",provider:"azure",languageCodes:[{bcp47:e.Locale,iso639_3:e.Locale.split("-")[0],display:e.LocaleName}]})))}async synthToBytes(e,a){const i=await this.prepareSSML(e,a);console.debug(`${this.constructor.name}.synthToBytes - TTS text ${i}, Options: ${JSON.stringify(a)}`);try{const e=await fetch(`https://${this.region}.tts.speech.microsoft.com/cognitiveservices/v1`,{method:"POST",headers:{"Ocp-Apim-Subscription-Key":this.subscriptionKey,"Content-Type":"application/ssml+xml","X-Microsoft-OutputFormat":"mp3"===(null==a?void 0:a.format)?"audio-24khz-96kbitrate-mono-mp3":"riff-24khz-16bit-mono-pcm","User-Agent":"js-tts-wrapper"},body:i});if(!e.ok)throw new Error(`Failed to synthesize speech: ${e.statusText}`);const n=await e.arrayBuffer();return new Uint8Array(n)}catch(e){throw console.error("Error synthesizing speech:",e),e}}async synthToBytestream(e,a){const i=await this.prepareSSML(e,a),n=!1!==(null==a?void 0:a.useWordBoundary);let r=null;return n&&(r=await this.loadSDK()),r&&n?this.synthToBytestreamWithSDK(i,a,r):this.synthToBytestreamWithREST(i,a)}async loadSDK(){if(this.sdk)return this.sdk;if(this.sdkLoadingPromise)return this.sdkLoadingPromise;if("undefined"!=typeof window){const e=window;return e.SpeechSDK?(this.sdk=e.SpeechSDK,this.sdk):(this.sdkLoadingPromise=new Promise((e=>{const a=document.createElement("script");a.src="https://aka.ms/csspeech/jsbrowserpackageraw",a.async=!0,a.onload=()=>{this.sdk=window.SpeechSDK||null,this.sdkLoadingPromise=null,this.sdk?(console.log("Microsoft Speech SDK (browser) loaded successfully."),e(this.sdk)):(console.warn("Speech SDK script loaded but window.SpeechSDK not found. Falling back to REST."),e(null))},a.onerror=()=>{console.warn("Failed to load Microsoft Speech SDK (browser). Falling back to REST."),this.sdkLoadingPromise=null,e(null)},document.head.appendChild(a)})),this.sdkLoadingPromise)}const e=new Function("m","return import(m)");return this.sdkLoadingPromise=e("microsoft-cognitiveservices-speech-sdk").then((e=>(this.sdk=e,this.sdkLoadingPromise=null,console.log("Microsoft Speech SDK loaded successfully."),this.sdk))).catch((e=>(console.warn("microsoft-cognitiveservices-speech-sdk not found or failed to load, using REST API fallback for word boundaries."),this.sdkLoadingPromise=null,this.sdk=null,null))),this.sdkLoadingPromise}async synthToBytestreamWithSDK(e,a,i){try{if(!i)throw new Error("Attempted to use SDK method, but SDK instance is missing.");const n=i.SpeechConfig.fromSubscription(this.subscriptionKey,this.region);n.speechSynthesisOutputFormat="mp3"===(null==a?void 0:a.format)?i.SpeechSynthesisOutputFormat.Audio24Khz96KBitRateMonoMp3:i.SpeechSynthesisOutputFormat.Riff24Khz16BitMonoPcm,this.voiceId&&(n.speechSynthesisVoiceName=this.voiceId);const r=new i.SpeechSynthesizer(n);return new Promise(((a,n)=>{const t=[],l=[];r.wordBoundary=(e,a)=>{t.push({text:a.text,offset:a.audioOffset/1e4,duration:0})},r.synthesizing=(e,a)=>{a.result.reason===i.ResultReason.SynthesizingAudio&&l.push(new Uint8Array(a.result.audioData))},r.speakSsmlAsync(e,(e=>{if(r.close(),e.reason===i.ResultReason.SynthesizingAudioCompleted){l.push(new Uint8Array(e.audioData));const i=new ReadableStream({start(e){for(const a of l)e.enqueue(a);e.close()}});if(t.length>1){for(let e=0;e<t.length-1;e++)t[e].duration=t[e+1].offset-t[e].offset;if(t.length>0){t[t.length-1].duration=500}}a({audioStream:i,wordBoundaries:t})}else n(new Error(`Synthesis failed: ${e.errorDetails}`))}),(e=>{r.close(),n(e)}))}))}catch(e){throw console.error("Error synthesizing speech with SDK:",e),e}}async synthToBytestreamWithREST(e,a){try{const i=`https://${this.region}.tts.speech.microsoft.com/cognitiveservices/v1`,n=await fetch(i,{method:"POST",headers:{"Ocp-Apim-Subscription-Key":this.subscriptionKey,"Content-Type":"application/ssml+xml","X-Microsoft-OutputFormat":"mp3"===(null==a?void 0:a.format)?"audio-24khz-96kbitrate-mono-mp3":"riff-24khz-16bit-mono-pcm","User-Agent":"js-tts-wrapper"},body:e});if(!n.ok)throw new Error(`Failed to synthesize speech: ${n.statusText}`);const r=[];return{audioStream:n.body,wordBoundaries:r}}catch(e){throw console.error("Error synthesizing speech with REST API:",e),e}}async startPlaybackWithCallbacks(e,a,i){if(this.sdk)await this.startPlaybackWithCallbacksSDK(e,a,i);else{this.on("boundary",a);const n={...i,useWordBoundary:!0};await this.speakStreamed(e,n)}}async startPlaybackWithCallbacksSDK(e,a,i){const n=await this.prepareSSML(e,i);try{const e=this.sdk.SpeechConfig.fromSubscription(this.subscriptionKey,this.region);e.speechSynthesisOutputFormat="mp3"===(null==i?void 0:i.format)?this.sdk.SpeechSynthesisOutputFormat.Audio24Khz96KBitRateMonoMp3:this.sdk.SpeechSynthesisOutputFormat.Riff24Khz16BitMonoPcm,this.voiceId&&(e.speechSynthesisVoiceName=this.voiceId);const r=this.sdk.AudioConfig.fromDefaultSpeakerOutput(),t=new this.sdk.SpeechSynthesizer(e,r);this.emit("start"),t.wordBoundary=(e,i)=>{const n=i.audioOffset/1e4;this.timings.push([n,n+500,i.text]),a(i.text,n,n+500)},t.synthesisCompleted=(e,a)=>{this.emit("end"),t.close()},await new Promise(((e,a)=>{t.speakSsmlAsync(n,(()=>{e()}),(e=>{a(e)}))}))}catch(e){throw console.error("Error starting playback with callbacks:",e),e}}async prepareSSML(e,a){if(null==a?void 0:a.rawSSML){console.debug("Using raw SSML as provided, skipping conversion and validation.");let i=R(e)?e:x(e);return i=this.ensureAzureSSMLStructure(i,(null==a?void 0:a.voice)||this.voiceId,a),i}if((null==a?void 0:a.useSpeechMarkdown)&&N(e)){console.debug("Converting Speech Markdown to SSML for Azure.");e=await v(e,"microsoft-azure")}let i=R(e)?e:x(e);const n=(null==a?void 0:a.voice)||this.voiceId;i=B(i,"azure",n||void 0),i=this.ensureAzureSSMLStructure(i,n,a);const r=M(i,"azure",n||void 0);if(r.warnings.length>0&&console.warn("Azure SSML warnings:",r.warnings),!r.isValid)throw console.error("Azure SSML validation errors:",r.errors),new Error(`Invalid SSML for Azure: ${r.errors.join(", ")}`);return i}ensureAzureSSMLStructure(e,a,i){var n,r;const t=/mstts:/.test(e);if(e.includes("version=")||(e=e.replace("<speak",'<speak version="1.0"')),e.includes("xmlns=")||(e=e.replace("<speak",'<speak xmlns="http://www.w3.org/2001/10/synthesis"')),t&&!e.includes("xmlns:mstts=")&&(e=e.replace("<speak",'<speak xmlns:mstts="https://www.w3.org/2001/mstts"')),e.includes("xml:lang=")||(e=e.replace("<speak",`<speak xml:lang="${this.lang}"`)),a&&!e.includes("<voice")){const i=e.match(/<speak[^>]*>(.*?)<\/speak>/s);if(i){const n=i[1].trim(),r=e.substring(0,e.indexOf(">")+1);e=`${r}<voice name="${a}">${n}</voice></speak>`}}{const a="medium",t="medium",l=100,s=null!==(n=null==i?void 0:i.rate)&&void 0!==n?n:this.properties.rate,o=null!==(r=null==i?void 0:i.pitch)&&void 0!==r?r:this.properties.pitch;let d=void 0!==(null==i?void 0:i.volume)?i.volume:this.properties.volume;void 0!==d&&d>0&&d<=1&&(d=Math.round(100*d));const u=void 0!==d?d:l;if(void 0!==s&&s!==a||void 0!==o&&o!==t||u!==l){const i=[];if(s&&s!==a&&i.push(`rate="${s}"`),o&&o!==t&&i.push(`pitch="${o}"`),u!==l&&i.push(`volume="${u}"`),e.includes("<voice")){const a=e.match(/<voice[^>]*>(.*?)<\/voice>/s);if(a){const n=a[1],r=`<prosody ${i.join(" ")}>${n}</prosody>`;e=e.replace(n,r)}}else{const a=e.match(/<speak[^>]*>(.*?)<\/speak>/s);if(a){const n=a[1],r=`<prosody ${i.join(" ")}>${n}</prosody>`;e=e.replace(n,r)}}}}return e}}function K(){if("function"==typeof globalThis.fetch)return globalThis.fetch;try{return require("node-fetch")}catch(e){return async()=>{throw new Error("Fetch API is not available. Please install node-fetch package or use a newer version of Node.js.")}}}const F={aa:"aar",ab:"abk",af:"afr",ak:"aka",am:"amh",an:"arg",ar:"ara",as:"asm",av:"ava",ay:"aym",az:"aze",ba:"bak",be:"bel",bg:"bul",bh:"bih",bi:"bis",bm:"bam",bn:"ben",bo:"bod",br:"bre",bs:"bos",ca:"cat",ce:"che",ch:"cha",co:"cos",cr:"cre",cs:"ces",cu:"chu",cv:"chv",cy:"cym",da:"dan",de:"deu",dv:"div",dz:"dzo",ee:"ewe",el:"ell",en:"eng",eo:"epo",es:"spa",et:"est",eu:"eus",fa:"fas",ff:"ful",fi:"fin",fj:"fij",fo:"fao",fr:"fra",fy:"fry",ga:"gle",gd:"gla",gl:"glg",gn:"grn",gu:"guj",gv:"glv",ha:"hau",he:"heb",hi:"hin",ho:"hmo",hr:"hrv",ht:"hat",hu:"hun",hy:"hye",hz:"her",ia:"ina",id:"ind",ie:"ile",ig:"ibo",ii:"iii",ik:"ipk",io:"ido",is:"isl",it:"ita",iu:"iku",ja:"jpn",jv:"jav",ka:"kat",kg:"kon",ki:"kik",kj:"kua",kk:"kaz",kl:"kal",km:"khm",kn:"kan",ko:"kor",kr:"kau",ks:"kas",ku:"kur",kv:"kom",kw:"cor",ky:"kir",la:"lat",lb:"ltz",lg:"lug",li:"lim",ln:"lin",lo:"lao",lt:"lit",lu:"lub",lv:"lav",mg:"mlg",mh:"mah",mi:"mri",mk:"mkd",ml:"mal",mn:"mon",mr:"mar",ms:"msa",mt:"mlt",my:"mya",na:"nau",nb:"nob",nd:"nde",ne:"nep",ng:"ndo",nl:"nld",nn:"nno",no:"nor",nr:"nbl",nv:"nav",ny:"nya",oc:"oci",oj:"oji",om:"orm",or:"ori",os:"oss",pa:"pan",pi:"pli",pl:"pol",ps:"pus",pt:"por",qu:"que",rm:"roh",rn:"run",ro:"ron",ru:"rus",rw:"kin",sa:"san",sc:"srd",sd:"snd",se:"sme",sg:"sag",si:"sin",sk:"slk",sl:"slv",sm:"smo",sn:"sna",so:"som",sq:"sqi",sr:"srp",ss:"ssw",st:"sot",su:"sun",sv:"swe",sw:"swa",ta:"tam",te:"tel",tg:"tgk",th:"tha",ti:"tir",tk:"tuk",tl:"tgl",tn:"tsn",to:"ton",tr:"tur",ts:"tso",tt:"tat",tw:"twi",ty:"tah",ug:"uig",uk:"ukr",ur:"urd",uz:"uzb",ve:"ven",vi:"vie",vo:"vol",wa:"wln",wo:"wol",xh:"xho",yi:"yid",yo:"yor",za:"zha",zh:"zho",zu:"zul"},_={"af-ZA":"Afrikaans (South Africa)","am-ET":"Amharic (Ethiopia)","ar-AE":"Arabic (UAE)","ar-BH":"Arabic (Bahrain)","ar-DZ":"Arabic (Algeria)","ar-EG":"Arabic (Egypt)","ar-IQ":"Arabic (Iraq)","ar-JO":"Arabic (Jordan)","ar-KW":"Arabic (Kuwait)","ar-LB":"Arabic (Lebanon)","ar-LY":"Arabic (Libya)","ar-MA":"Arabic (Morocco)","ar-OM":"Arabic (Oman)","ar-QA":"Arabic (Qatar)","ar-SA":"Arabic (Saudi Arabia)","ar-SY":"Arabic (Syria)","ar-TN":"Arabic (Tunisia)","ar-YE":"Arabic (Yemen)","az-AZ":"Azerbaijani (Azerbaijan)","bg-BG":"Bulgarian (Bulgaria)","bn-BD":"Bengali (Bangladesh)","bn-IN":"Bengali (India)","ca-ES":"Catalan (Spain)","cs-CZ":"Czech (Czech Republic)","cy-GB":"Welsh (United Kingdom)","da-DK":"Danish (Denmark)","de-AT":"German (Austria)","de-CH":"German (Switzerland)","de-DE":"German (Germany)","el-GR":"Greek (Greece)","en-AU":"English (Australia)","en-CA":"English (Canada)","en-GB":"English (United Kingdom)","en-IE":"English (Ireland)","en-IN":"English (India)","en-NZ":"English (New Zealand)","en-PH":"English (Philippines)","en-SG":"English (Singapore)","en-US":"English (United States)","en-ZA":"English (South Africa)","es-AR":"Spanish (Argentina)","es-BO":"Spanish (Bolivia)","es-CL":"Spanish (Chile)","es-CO":"Spanish (Colombia)","es-CR":"Spanish (Costa Rica)","es-DO":"Spanish (Dominican Republic)","es-EC":"Spanish (Ecuador)","es-ES":"Spanish (Spain)","es-GT":"Spanish (Guatemala)","es-HN":"Spanish (Honduras)","es-MX":"Spanish (Mexico)","es-NI":"Spanish (Nicaragua)","es-PA":"Spanish (Panama)","es-PE":"Spanish (Peru)","es-PR":"Spanish (Puerto Rico)","es-PY":"Spanish (Paraguay)","es-SV":"Spanish (El Salvador)","es-US":"Spanish (United States)","es-UY":"Spanish (Uruguay)","es-VE":"Spanish (Venezuela)","et-EE":"Estonian (Estonia)","eu-ES":"Basque (Spain)","fa-IR":"Persian (Iran)","fi-FI":"Finnish (Finland)","fil-PH":"Filipino (Philippines)","fr-BE":"French (Belgium)","fr-CA":"French (Canada)","fr-CH":"French (Switzerland)","fr-FR":"French (France)","ga-IE":"Irish (Ireland)","gl-ES":"Galician (Spain)","gu-IN":"Gujarati (India)","he-IL":"Hebrew (Israel)","hi-IN":"Hindi (India)","hr-HR":"Croatian (Croatia)","hu-HU":"Hungarian (Hungary)","hy-AM":"Armenian (Armenia)","id-ID":"Indonesian (Indonesia)","is-IS":"Icelandic (Iceland)","it-IT":"Italian (Italy)","ja-JP":"Japanese (Japan)","jv-ID":"Javanese (Indonesia)","ka-GE":"Georgian (Georgia)","kk-KZ":"Kazakh (Kazakhstan)","km-KH":"Khmer (Cambodia)","kn-IN":"Kannada (India)","ko-KR":"Korean (South Korea)","lo-LA":"Lao (Laos)","lt-LT":"Lithuanian (Lithuania)","lv-LV":"Latvian (Latvia)","mk-MK":"Macedonian (Macedonia)","ml-IN":"Malayalam (India)","mn-MN":"Mongolian (Mongolia)","mr-IN":"Marathi (India)","ms-MY":"Malay (Malaysia)","mt-MT":"Maltese (Malta)","my-MM":"Burmese (Myanmar)","nb-NO":"Norwegian Bokmål (Norway)","ne-NP":"Nepali (Nepal)","nl-BE":"Dutch (Belgium)","nl-NL":"Dutch (Netherlands)","pa-IN":"Punjabi (India)","pl-PL":"Polish (Poland)","ps-AF":"Pashto (Afghanistan)","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ro-RO":"Romanian (Romania)","ru-RU":"Russian (Russia)","si-LK":"Sinhala (Sri Lanka)","sk-SK":"Slovak (Slovakia)","sl-SI":"Slovenian (Slovenia)","so-SO":"Somali (Somalia)","sq-AL":"Albanian (Albania)","sr-RS":"Serbian (Serbia)","su-ID":"Sundanese (Indonesia)","sv-SE":"Swedish (Sweden)","sw-KE":"Swahili (Kenya)","sw-TZ":"Swahili (Tanzania)","ta-IN":"Tamil (India)","ta-LK":"Tamil (Sri Lanka)","ta-SG":"Tamil (Singapore)","te-IN":"Telugu (India)","th-TH":"Thai (Thailand)","tr-TR":"Turkish (Turkey)","uk-UA":"Ukrainian (Ukraine)","ur-PK":"Urdu (Pakistan)","uz-UZ":"Uzbek (Uzbekistan)","vi-VN":"Vietnamese (Vietnam)","yue-CN":"Cantonese (China)","yue-HK":"Cantonese (Hong Kong)","zh-CN":"Chinese (China)","zh-HK":"Chinese (Hong Kong)","zh-TW":"Chinese (Taiwan)"},G={af:"Afrikaans",am:"Amharic",ar:"Arabic",az:"Azerbaijani",bg:"Bulgarian",bn:"Bengali",ca:"Catalan",cs:"Czech",cy:"Welsh",da:"Danish",de:"German",el:"Greek",en:"English",es:"Spanish",et:"Estonian",eu:"Basque",fa:"Persian",fi:"Finnish",fil:"Filipino",fr:"French",ga:"Irish",gl:"Galician",gu:"Gujarati",he:"Hebrew",hi:"Hindi",hr:"Croatian",hu:"Hungarian",hy:"Armenian",id:"Indonesian",is:"Icelandic",it:"Italian",ja:"Japanese",jv:"Javanese",ka:"Georgian",kk:"Kazakh",km:"Khmer",kn:"Kannada",ko:"Korean",lo:"Lao",lt:"Lithuanian",lv:"Latvian",mk:"Macedonian",ml:"Malayalam",mn:"Mongolian",mr:"Marathi",ms:"Malay",mt:"Maltese",my:"Burmese",nb:"Norwegian Bokmål",ne:"Nepali",nl:"Dutch",pa:"Punjabi",pl:"Polish",ps:"Pashto",pt:"Portuguese",ro:"Romanian",ru:"Russian",si:"Sinhala",sk:"Slovak",sl:"Slovenian",so:"Somali",sq:"Albanian",sr:"Serbian",su:"Sundanese",sv:"Swedish",sw:"Swahili",ta:"Tamil",te:"Telugu",th:"Thai",tr:"Turkish",uk:"Ukrainian",ur:"Urdu",uz:"Uzbek",vi:"Vietnamese",yue:"Cantonese",zh:"Chinese"};function j(e){const a=e.split("-")[0].toLowerCase();return F[a]||a}function z(e){if(_[e])return _[e];const a=e.split("-")[0].toLowerCase();return G[a]||e}const V=K(),W=/\[[^\]]+\]/g,$=["laughter"],J=["neutral","angry","excited","content","sad","scared","happy","euphoric","anxious","panicked","calm","confident","curious","frustrated","sarcastic","melancholic","surprised","disgusted","contemplative","determined","proud","distant","skeptical","mysterious","anticipation","grateful","affectionate","sympathetic","nostalgic","wistful","apologetic","hesitant","insecure","confused","resigned","alarmed","bored","tired","rejected","hurt","disappointed","dejected","guilty","envious","contempt","threatened","agitated","outraged","mad","triumphant","amazed","flirtatious","joking/comedic","serene","peaceful","enthusiastic","elated","trust"];class Z extends P{constructor(e={}){super(e),this.apiKey=e.apiKey||process.env.CARTESIA_API_KEY||"",this.baseUrl=e.baseURL||"https://api.cartesia.ai",this.model=e.model||"sonic-3",this.voiceId="694f938dd2a74762ba554ff8e2a9d786",this.outputFormat={container:"wav",encoding:"pcm_f32le",sample_rate:44100},this._models=[{id:"sonic-3",features:["streaming","audio-tags","inline-voice-cloning"]},{id:"sonic-2",features:["streaming"]}],this.sampleRate=44100,this.applyCredentialProperties(e)}applyCredentialProperties(e){var a,i;const n=null!==(i=null!==(a=e.properties)&&void 0!==a?a:e.propertiesJson)&&void 0!==i?i:e.propertiesJSON;if(n){let e=null;if("string"==typeof n)try{e=JSON.parse(n)}catch(e){}else"object"==typeof n&&(e=n);if(e)for(const[a,i]of Object.entries(e))this.setProperty(a,i)}}processAudioTags(e){var a;if("sonic-3"!==this.model)return e.replace(W,"").replace(/\s+/g," ").trim();const i=null!==(a=e.match(W))&&void 0!==a?a:[];if(0===i.length)return e;let n=e;for(const e of i){const a=e.slice(1,-1).toLowerCase();$.includes(a)||(n=J.includes(a)?n.replace(e,`<emotion value="${a}"/>`):n.replace(e,""))}return n.replace(/\s+/g," ").trim()}async prepareText(e,a){let i=e;if((null==a?void 0:a.useSpeechMarkdown)&&N(i)){i=L(await v(i,"w3c"))}return R(i)&&(i=L(i)),i=this.processAudioTags(i),i}setModel(e){this.model=e}setVoice(e){this.voiceId=e}getProperty(e){switch(e){case"model":return this.model;case"voice":return this.voiceId;case"outputFormat":return this.outputFormat;default:return super.getProperty(e)}}setProperty(e,a){switch(e){case"model":this.setModel(a);break;case"voice":this.setVoice(a);break;case"outputFormat":"object"==typeof a&&(this.outputFormat=a);break;default:super.setProperty(e,a)}}async checkCredentials(){if(!this.apiKey)return!1;try{return(await V(`${this.baseUrl}/voices`,{method:"GET",headers:{"X-API-Key":this.apiKey,"Cartesia-Version":"2025-04-16"}})).ok}catch(e){return!1}}getRequiredCredentials(){return["apiKey"]}async _getVoices(){try{const e=await V(`${this.baseUrl}/voices`,{method:"GET",headers:{"X-API-Key":this.apiKey,"Cartesia-Version":"2025-04-16"}});return e.ok?await e.json():[]}catch(e){return[]}}async _mapVoicesToUnified(e){return e.map((e=>{var a,i;return{id:e.id,name:e.name,gender:(null===(a=e.description)||void 0===a?void 0:a.toLowerCase().includes("female"))?"Female":(null===(i=e.description)||void 0===i?void 0:i.toLowerCase().includes("male"))?"Male":"Unknown",languageCodes:e.language?[{bcp47:e.language,iso639_3:j(e.language),display:z(e.language)}]:[{bcp47:"en-US",iso639_3:"eng",display:"English (US)"}],provider:"cartesia"}}))}async synthToBytes(e,a={}){const i=await this.prepareText(e,a),n=a.voice||this.voiceId||"694f938dd2a74762ba554ff8e2a9d786",r={output_format:this.outputFormat,...a.providerOptions,model_id:a.model||this.model,transcript:i,voice:{mode:"id",id:n}},t=await V(`${this.baseUrl}/tts/bytes`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey,"Cartesia-Version":"2025-04-16"},body:JSON.stringify(r)});if(!t.ok){const e=await t.text();throw new Error(`Cartesia API error: ${t.status} ${t.statusText} - ${e}`)}const l=await t.arrayBuffer();return this._createEstimatedWordTimings(i),new Uint8Array(l)}async synthToBytestream(e,a={}){const i=await this.prepareText(e,a),n=a.voice||this.voiceId||"694f938dd2a74762ba554ff8e2a9d786",r={output_format:this.outputFormat,...a.providerOptions,model_id:a.model||this.model,transcript:i,voice:{mode:"id",id:n}},t=await V(`${this.baseUrl}/tts/bytes`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey,"Cartesia-Version":"2025-04-16"},body:JSON.stringify(r)});if(!t.ok){const e=await t.text();throw new Error(`Cartesia API error: ${t.status} ${t.statusText} - ${e}`)}if(!t.body){const e=await t.arrayBuffer(),a=new Uint8Array(e);return{audioStream:new ReadableStream({start(e){e.enqueue(a),e.close()}}),wordBoundaries:[]}}return{audioStream:t.body,wordBoundaries:[]}}}const X=108e5,q=new Set(["wav","mp3","ogg"]);class Y extends P{constructor(e={}){super(e),this.metadata=!1,this.tokenExpiresAt=0,this.email=e.email||"undefined"!=typeof process&&process.env.CEREVOICE_EMAIL||"",this.password=e.password||"undefined"!=typeof process&&process.env.CEREVOICE_PASSWORD||"",this.accessToken=e.accessToken||"undefined"!=typeof process&&process.env.CEREVOICE_ACCESS_TOKEN||"",this.refreshToken=e.refreshToken||"undefined"!=typeof process&&process.env.CEREVOICE_REFRESH_TOKEN||"",this.baseUrl=(e.baseURL||"https://api.cerevoice.com/v2").replace(/\/+$/,""),this.voiceId=e.voice||"Heather",this.audioFormat=e.audioFormat||"wav",this.outputSampleRate=e.sampleRate,this.outputSampleRate&&(this.sampleRate=this.outputSampleRate),this.capabilities={browserSupported:!0,nodeSupported:!0,needsWasm:!1},this._models=[{id:"cerevoice-cloud-v2",features:["streaming","ssml","word-boundary-events"]}],this.accessToken&&(this.tokenExpiresAt=Number.POSITIVE_INFINITY),this.applyCredentialProperties(e)}applyCredentialProperties(e){var a,i;const n=null!==(i=null!==(a=e.properties)&&void 0!==a?a:e.propertiesJson)&&void 0!==i?i:e.propertiesJSON;if(!n)return;let r=null;if("string"==typeof n)try{r=JSON.parse(n)}catch(e){r=null}else"object"==typeof n&&(r=n);if(r)for(const[e,a]of Object.entries(r))this.setProperty(e,a)}setVoice(e,a){this.voiceId=e,a&&(t