@@ -331,14 +331,10 @@ function sepiaFW_build_speech_synthesis(Speech, sepiaSessionId){
331331 //native voices
332332 selectedVoice = newVoice ;
333333 if ( selectedVoice ) {
334- var selectedVoiceObjectArray = speechSynthesis . getVoices ( ) . filter ( function ( voice ) {
334+ selectedVoiceObject = window . speechSynthesis . getVoices ( ) . find ( function ( voice ) {
335335 return ( voice && voice . name && ( voice . name === selectedVoice ) ) ;
336336 } ) ;
337- if ( selectedVoiceObjectArray . length == 0 ) {
338- selectedVoiceObject = { } ;
339- } else {
340- selectedVoiceObject = selectedVoiceObjectArray [ 0 ] ;
341- }
337+ if ( ! selectedVoiceObject ) selectedVoiceObject = { } ;
342338 } else {
343339 selectedVoiceObject = { } ;
344340 }
@@ -360,18 +356,31 @@ function sepiaFW_build_speech_synthesis(Speech, sepiaSessionId){
360356 setVoiceOnce ( ) ;
361357 }
362358 }
359+ Speech . findVoiceObjForLanguage = function ( lang ) {
360+ if ( voices && voices . length > 0 ) {
361+ lang = lang . replace ( / ( - | _ ) .* / , "" ) . trim ( ) . toLowerCase ( ) ;
362+ var knownSelection = SepiaFW . data . getPermanent ( lang + "-voice" ) ;
363+ var bestFitV ;
364+ if ( knownSelection ) bestFitV = voices . find ( function ( v ) { return ( v . name && v . name == knownSelection ) ; } ) ;
365+ if ( ! bestFitV ) bestFitV = voices . find ( function ( v ) { return ( v . lang && v . lang . indexOf ( lang ) == 0 ) ; } ) ;
366+ return bestFitV ;
367+ } else {
368+ return ; //we only search in pre-loaded voices for now
369+ }
370+ }
363371
364372 //speak an utterance
365- Speech . speak = function ( text , finishedCallback , errorCallback , startedCallback ) {
373+ Speech . speak = function ( text , finishedCallback , errorCallback , startedCallback , options ) {
366374 //NOTE: this is the high level function with all events etc...
367- // If you the direct interface with default settings use 'SepiaFW.audio.tts.speak'
375+ // If you need the direct interface with default settings use 'SepiaFW.audio.tts.speak'
376+ if ( ! options ) options = { } ;
368377
369378 //stop running stuff
370379 if ( isSpeaking ) {
371380 Speech . stopSpeech ( ) ;
372381 clearTimeout ( stopSpeechTimeout ) ;
373382 stopSpeechTimeout = setTimeout ( function ( ) {
374- Speech . speak ( text , finishedCallback , errorCallback , startedCallback ) ;
383+ Speech . speak ( text , finishedCallback , errorCallback , startedCallback , options ) ;
375384 } , 500 ) ;
376385 return ;
377386 }
@@ -399,7 +408,8 @@ function sepiaFW_build_speech_synthesis(Speech, sepiaSessionId){
399408 }
400409
401410 } else {
402- //chunk text if there is a limit - TODO: 'isChromiumDesktop' is not the entire truth, it depends on the selected voice!
411+ //chunk text if there is a limit
412+ //TODO: 'isChromiumDesktop' is not the entire truth, it depends on the selected voice!
403413 if ( SepiaFW . ui . isChromiumDesktop && text && ! Speech . skipTTS && Speech . isTtsSupported ) {
404414 text = chunkUtterance ( text ) ;
405415 }
@@ -414,6 +424,14 @@ function sepiaFW_build_speech_synthesis(Speech, sepiaSessionId){
414424 return ;
415425 }
416426
427+ //one-time voice change? - TODO: not all engines support this yet
428+ var oneTimeVoice ;
429+ if ( options . oneTimeLanguage && Speech . getLanguage ( ) != options . oneTimeLanguage ) {
430+ oneTimeVoice = Speech . findVoiceObjForLanguage ( options . oneTimeLanguage ) ;
431+ }
432+ if ( oneTimeVoice ) options . oneTimeVoice = oneTimeVoice ;
433+ else options . oneTimeVoice = undefined ; //make sure this is only set after 'findVoiceForLanguage'
434+
417435 //Streaming audio server (e.g. SEPIA, MARY-TTS API)
418436 if ( Speech . voiceEngine == 'sepia' || Speech . voiceEngine == 'custom-mary-api' ) {
419437 broadcastTtsRequested ( ) ;
@@ -434,7 +452,7 @@ function sepiaFW_build_speech_synthesis(Speech, sepiaSessionId){
434452 var event = { } ;
435453 event . msg = reason ;
436454 onTtsError ( event , errorCallback ) ;
437- } ) ;
455+ } , options ) ;
438456
439457 //NATIVE-TTS
440458 } else if ( SepiaFW . ui . isCordova ) {
@@ -449,7 +467,7 @@ function sepiaFW_build_speech_synthesis(Speech, sepiaSessionId){
449467 onTtsStart ( undefined , startedCallback , errorCallback ) ;
450468 TTS . speak ( { //Cordova plugin!
451469 text : text ,
452- locale : Speech . getLongLanguageCode ( Speech . getLanguage ( ) ) ,
470+ locale : Speech . getLongLanguageCode ( Speech . getLanguage ( ) ) , //TODO: support one-time lang.? Check support!
453471 rate : 1.00
454472
455473 } , function ( ) {
@@ -468,9 +486,15 @@ function sepiaFW_build_speech_synthesis(Speech, sepiaSessionId){
468486 window . sepia_tts_utterances = [ ] ; //This is a bug-fix to prevent utterance from getting garbage collected
469487 var utterance = new SpeechSynthesisUtterance ( ) ;
470488 utterance . text = text ;
471- utterance . lang = Speech . getLongLanguageCode ( Speech . getLanguage ( ) ) ;
472- //set voice if valid one was selected
473- if ( selectedVoiceObject && selectedVoiceObject . name ) utterance . voice = selectedVoiceObject ;
489+ if ( options . oneTimeVoice ) {
490+ //support for one-time language switch
491+ utterance . lang = Speech . getLongLanguageCode ( options . oneTimeLanguage ) ;
492+ utterance . voice = options . oneTimeVoice ;
493+ } else {
494+ utterance . lang = Speech . getLongLanguageCode ( Speech . getLanguage ( ) ) ;
495+ //set voice if valid one was selected
496+ if ( selectedVoiceObject && selectedVoiceObject . name ) utterance . voice = selectedVoiceObject ;
497+ }
474498 utterance . pitch = 1.0 ; //accepted values: 0-2 inclusive, default value: 1
475499 utterance . rate = 1.0 ; //accepted values: 0.1-10 inclusive, default value: 1
476500 utterance . volume = 1.0 ; //accepted values: 0-1, default value: 1
@@ -702,13 +726,13 @@ function sepiaFW_build_speech_synthesis(Speech, sepiaSessionId){
702726
703727 //--- Common Stream TTS Interface ---
704728
705- Speech . getTtsStreamURL = function ( message , successCallback , errorCallback ) {
729+ Speech . getTtsStreamURL = function ( message , successCallback , errorCallback , options ) {
706730 //SEPIA server
707731 if ( Speech . voiceEngine == 'sepia' ) {
708- Speech . sepiaTTS . getURL ( message , successCallback , errorCallback ) ;
732+ Speech . sepiaTTS . getURL ( message , successCallback , errorCallback , options ) ;
709733 //CUSTOM MARY-TTS API
710734 } else if ( Speech . voiceEngine == 'custom-mary-api' ) {
711- Speech . maryTTS . getURL ( message , successCallback , errorCallback ) ;
735+ Speech . maryTTS . getURL ( message , successCallback , errorCallback , options ) ;
712736 //NONE
713737 } else {
714738 if ( errorCallback ) errorCallback ( { name : "NotSupported" , message : "The selected voice engine is missing a required function." } ) ;
@@ -749,7 +773,8 @@ function sepiaFW_build_speech_synthesis(Speech, sepiaSessionId){
749773 }
750774
751775 //get audio URL
752- Speech . sepiaTTS . getURL = function ( message , successCallback , errorCallback ) {
776+ Speech . sepiaTTS . getURL = function ( message , successCallback , errorCallback , options ) {
777+ //TODO: implement 'options.oneTimeVoice' and 'options.oneTimeLanguage'
753778 var apiUrl = SepiaFW . config . assistAPI + "tts" ;
754779 var submitData = {
755780 text : message ,
@@ -857,7 +882,8 @@ function sepiaFW_build_speech_synthesis(Speech, sepiaSessionId){
857882 Speech . maryTTS . settings . maxChunkLength = ( Settings . maxChunkLength ) ? Settings . maxChunkLength : 600 ;
858883 }
859884
860- Speech . maryTTS . getURL = function ( message , successCallback , errorCallback ) {
885+ Speech . maryTTS . getURL = function ( message , successCallback , errorCallback , options ) {
886+ //TODO: implement 'options.oneTimeVoice' and 'options.oneTimeLanguage'
861887 if ( ! Speech . voiceCustomServer ) {
862888 var err = { name : "MissingServerInfo" , message : "Custom Mary-TTS API is missing server URL." } ;
863889 SepiaFW . debug . error ( "Speech.maryTTS - getURL ERROR: " + JSON . stringify ( err ) ) ;
@@ -876,9 +902,9 @@ function sepiaFW_build_speech_synthesis(Speech, sepiaSessionId){
876902 } else if ( ! Speech . maryTTS . settings . voice || Speech . maryTTS . settings . voice == "default" ) {
877903 //console.log("voices", voices); //DEBUG
878904 var lang = Speech . getLanguage ( ) ;
879- var bestFitV = voices . find ( function ( v ) { return ( v . lang && v . lang . indexOf ( lang ) == 0 ) ; } )
905+ var bestFitV = Speech . findVoiceObjForLanguage ( lang )
880906 || voices . find ( function ( v ) { return ( v . name && ( v . name . indexOf ( lang + "-" ) == 0 || v . name . indexOf ( lang + "_" ) == 0 ) ) ; } )
881- || voices [ 0 ] . name
907+ || voices [ 0 ] ;
882908 if ( bestFitV ) {
883909 Speech . maryTTS . settings . voice = bestFitV . name ;
884910 } else {
0 commit comments