var ResponsiveVoice = function(){ var self = this; // Ourn own collection of voices var responsivevoices = [ {name: 'UK English Female', voiceIDs: [3,5,1,6,7,8] }, {name: 'UK English Male', voiceIDs: [0,4,2,6,7,8] }, {name: 'US English Female', voiceIDs: [39,40,41,42,43,44] }, {name: 'Spanish Female', voiceIDs: [19,16,17,18,20,15] }, {name: 'French Female', voiceIDs: [21,22,23,26] }, {name: 'Deutsch Female', voiceIDs: [27,28,29,30,31,32] }, {name: 'Italian Female', voiceIDs: [33,34,35,36,37,38] }, {name: 'Hungarian Female', voiceIDs: [9,10,11] }, {name: 'Serbian Male', voiceIDs: [12] }, {name: 'Croatian Male', voiceIDs: [13] }, {name: 'Bosnian Male', voiceIDs: [14] }, {name: 'Fallback UK Female', voiceIDs: [8] } ]; //All voices available on every system and device var voicecollection = [ {name: 'Google UK English Male'}, //0 male uk android/chrome {name: 'Agnes'}, //1 female us safari mac {name: 'Daniel Compact'}, //2 male us safari mac {name: 'Google UK English Female'}, //3 female uk android/chrome {name: 'en-GB', rate: 0.5, pitch: 1}, //4 male uk IOS {name: 'en-AU', rate: 0.5, pitch: 1}, //5 female english IOS {name: 'inglés Reino Unido'}, //6 spanish english android {name: 'English United Kingdom'}, //7 english english android {name: 'Fallback en-GB Female', lang: 'en-GB', fallbackvoice: true}, //8 fallback english female {name: 'Eszter Compact'}, //9 Hungarian mac {name: 'hu-HU', rate: 0.4}, //10 Hungarian iOS {name: 'Fallback Hungarian', lang: 'hu', fallbackvoice:true}, //11 Hungarian fallback {name: 'Fallback Serbian', lang: 'sr', fallbackvoice:true}, //12 Serbian fallback {name: 'Fallback Croatian', lang: 'hr', fallbackvoice:true}, //13 Croatian fallback {name: 'Fallback Bosnian', lang: 'bs', fallbackvoice:true}, //14 Bosnian fallback {name: 'Fallback Spanish', lang: 'es', fallbackvoice:true}, //15 Spanish fallback {name: 'Spanish Spain'}, //16 female es android/chrome {name: 'español España'}, //17 female es android/chrome {name: 'Diego Compact', rate: 0.3}, //18 male es mac {name: 'Google Español'}, //19 male es chrome {name: 'es-ES', rate: 0.3}, //20 male es iOS {name: 'Google Français'}, //21 FR chrome {name: 'French France'}, //22 android/chrome {name: 'francés Francia'}, //23 android/chrome {name: 'Virginie Compact', rate: 0.5}, //24 mac {name: 'fr-FR', rate: 0.5}, //25 iOS {name: 'Fallback French', lang: 'fr', fallbackvoice:true}, //26 fallback {name: 'Google Deutsch'}, //27 DE chrome {name: 'German Germany'}, //28 android/chrome {name: 'alemán Alemania'}, //29 android/chrome {name: 'Yannick Compact', rate: 0.5}, //30 mac {name: 'de-DE', rate: 0.5}, //31 iOS {name: 'Fallback Deutsch', lang: 'de', fallbackvoice:true}, //32 fallback {name: 'Google Italiano'}, //33 DE chrome {name: 'Italian Italy'}, //34 android/chrome {name: 'italiano Italia'}, //35 android/chrome {name: 'Paolo Compact', rate: 0.5}, //36 mac {name: 'it-IT', rate: 0.5}, //37 iOS {name: 'Fallback Italian', lang: 'it', fallbackvoice:true}, //38 fallback {name: 'Google US English'}, //39 DE chrome {name: 'English United States'}, //40 android/chrome {name: 'inglés Estados Unidos'}, //41 android/chrome {name: 'Vicki'}, //42 mac {name: 'en-US', rate: 0.5}, //43 iOS {name: 'Fallback English', lang: 'en-US', fallbackvoice:true}, //44 fallback ]; var systemvoices; var CHARACTER_LIMIT = 100; var VOICESUPPORT_ATTEMPTLIMIT = 5; var voicesupport_attempts = 0; var fallbackMode = false; this.fallback_playing = false; this.fallback_parts = null; this.fallback_part_index = 0; this.fallback_audio = null; //Wait until system voices are ready and trigger the event OnVoiceReady if (typeof speechSynthesis != 'undefined') { speechSynthesis.onvoiceschanged = function() { systemvoices = window.speechSynthesis.getVoices(); if (self.OnVoiceReady!=null) { self.OnVoiceReady.call(); } }; } this.default_rv = responsivevoices[0]; this.OnVoiceReady = null; //We should use jQuery if it's available if (typeof $ === 'undefined') { document.addEventListener('DOMContentLoaded',function(){ init(); }); }else{ $(document).ready(function() { init(); }); } function init() { if (typeof speechSynthesis === 'undefined') { enableFallbackMode(); } else { //Waiting a few ms before calling getVoices() fixes some issues with safari on IOS as well as Chrome setTimeout(function(){ var gsvinterval = setInterval(function() { var v = window.speechSynthesis.getVoices(); if (v.length==0 && (systemvoices==null || systemvoices.length==0)) { console.log('Voice support NOT ready'); voicesupport_attempts++; if (voicesupport_attempts > VOICESUPPORT_ATTEMPTLIMIT) { //We don't support voices. Using fallback clearInterval(gsvinterval); enableFallbackMode(); } }else{ console.log('Voice support ready'); clearInterval(gsvinterval); systemvoices = v; mapRVs(); if (self.OnVoiceReady!=null) self.OnVoiceReady.call(); } },100); },100); } } function enableFallbackMode() { fallbackMode = true; console.log('Voice not supported. Using fallback mode'); mapRVs(); if (self.OnVoiceReady!=null) self.OnVoiceReady.call(); } this.getVoices = function() { //Create voices array var v = []; for (var i=0; iCHARACTER_LIMIT) { var tmptxt = text; while(tmptxt.length>CHARACTER_LIMIT) { //Split by common phrase delimiters var p = tmptxt.search(/[:!?.;]+/); var part = ''; //Coludn't split by priority characters, try commas if (p==-1 || p>=CHARACTER_LIMIT ) { p = tmptxt.search(/[,]+/); } //Couldn't split by normal characters, then we use spaces if (p==-1 || p>=CHARACTER_LIMIT) { var words = tmptxt.split(' '); for (var i=0; iCHARACTER_LIMIT) break; part += (i!=0?' ':'') + words[i]; } } else { part = tmptxt.substr(0, p+1); } tmptxt = tmptxt.substr(part.length, tmptxt.length-part.length); multipartText.push(part); //console.log(part.length + " - " + part); } //Add the remaining text if (tmptxt.length>0) { multipartText.push(tmptxt); } }else{ //Small text multipartText.push(text); } //Find system voice that matches voice name var rv; if (voicename==null) { rv = self.default_rv; }else{ rv = getResponsiveVoice(voicename); } var profile = {}; //Map was done so no need to look for the mapped voice if (rv.mappedProfile!=null) { profile = rv.mappedProfile; }else{ profile.systemvoice = getMatchedVoice(rv); profile.collectionvoice = {}; if (profile.systemvoice==null) { console.log('ERROR: No voice found for: ' + voicename); return; } } if (profile.collectionvoice.fallbackvoice==true) { fallbackMode = true; self.fallback_parts = []; }else{ fallbackMode = false; } //Play multipart text for (var i=0; i