diff --git a/scripts/caption.js b/scripts/caption.js index e2878d19..2b85923c 100644 --- a/scripts/caption.js +++ b/scripts/caption.js @@ -197,7 +197,13 @@ if (this.currentCaption !== thisCaption) { // it's time to load the new caption into the container div captionText = this.flattenCueForCaption(cues[thisCaption]).replace( /\n/g, "
" ); - + // If preference enabled to voice captions, send to synthesizer. + if ( this.speechEnabled && this.prefCaptionsSpeak ) { + let announceText = new DOMParser().parseFromString( captionText, 'text/html' ); + let announcement = announceText.body.textContent || ''; + // use browser's built-in speech synthesis + this.announceText( 'caption', announcement ); + } this.$captionsDiv.html(captionText); this.currentCaption = thisCaption; if (captionText.length === 0) { @@ -323,7 +329,29 @@ options[0] = "overlay"; options[1] = "below"; break; + + case "prefCaptionsSpeak": + options[0] = ["0", this.translate( 'off', 'Off' ) ]; + options[1] = ["1", this.translate( 'on', 'On' ) ]; + break; + + case "prefCaptionsVoice": + options[0] = null; // set later. + break; + + case "prefCaptionsPitch": + options[0] = null; // set later. + break; + + case "prefCaptionsRate": + options[0] = null; // set later. + break; + + case "prefCaptionsVolume": + options[0] = null; // set later. + break; } + return options; }; diff --git a/scripts/description.js b/scripts/description.js index 871b7abc..aafe589d 100644 --- a/scripts/description.js +++ b/scripts/description.js @@ -238,7 +238,7 @@ var preferences, voices, prefDescVoice, descVoice, descLang, prefVoiceFound; preferences = this.getPref(); - prefDescVoice = (typeof preferences.voices !== 'undefined') ? this.getPrefDescVoice() : null; + prefDescVoice = (typeof preferences.voices !== 'undefined') ? this.getPrefVoice() : null; this.getBrowserVoices(); this.rebuildDescPrefsForm(); @@ -488,7 +488,7 @@ } else if (this.speechEnabled) { if ( 'video' !== this.descMethod ) { // use browser's built-in speech synthesis - this.announceDescriptionText('description',descText); + this.announceText('description',descText); } if (this.prefDescVisible) { // write description to the screen for sighted users @@ -543,13 +543,14 @@ this.prefDescRate = speechRate; }; - AblePlayer.prototype.announceDescriptionText = function(context, text) { + AblePlayer.prototype.announceText = function(context, text) { - // this function announces description text using speech synthesis + // this function announces text using speech synthesis // it's only called if already determined that browser supports speech synthesis // context is either: // 'description' - actual description text extracted from WebVTT file // 'sample' - called when user changes a setting in Description Prefs dialog + // 'caption' - called when announcing a caption. var thisObj, voiceName, i, voice, pitch, rate, volume, utterance, timeElapsed, secondsElapsed; @@ -586,12 +587,24 @@ pitch = $('#' + this.mediaId + '_prefDescPitch').val(); rate = $('#' + this.mediaId + '_prefDescRate').val(); volume = $('#' + this.mediaId + '_prefDescVolume').val(); - } else { + } else if ( context === 'captionSample' ) { + // get settings from form + voiceName = $('#' + this.mediaId + '_prefCaptionsVoice').val(); + pitch = $('#' + this.mediaId + '_prefCaptionsPitch').val(); + rate = $('#' + this.mediaId + '_prefCaptionsRate').val(); + volume = $('#' + this.mediaId + '_prefCaptionsVolume').val(); + } else if ( context === 'description' ) { // get settings from global prefs voiceName = this.prefDescVoice; pitch = this.prefDescPitch; rate = this.prefDescRate; volume = this.prefDescVolume; + } else { + // get settings from global prefs + voiceName = this.prefCaptionsVoice; + pitch = this.prefCaptionsPitch; + rate = this.prefCaptionsRate; + volume = this.prefCaptionsVolume; } // get the voice associated with the user's chosen voice name diff --git a/scripts/preference.js b/scripts/preference.js index a03a16ff..f0bbc6ca 100644 --- a/scripts/preference.js +++ b/scripts/preference.js @@ -239,6 +239,36 @@ 'group': 'captions', 'default': '100%' }); + prefs.push({ + 'name': 'prefCaptionsSpeak', + 'label': this.translate( 'prefVoicedCaptions', 'Voiced Captions' ), + 'group': 'captions', + 'default': 0 + }); + prefs.push({ + 'name': 'prefCaptionsVoice', + 'label': this.translate( 'prefDescVoice', 'Voice' ), + 'group': 'captions', + 'default': null // will be set later, in injectPrefsForm() + }); + prefs.push({ + 'name': 'prefCaptionsPitch', + 'label': this.translate( 'prefDescPitch', 'Pitch' ), + 'group': 'captions', + 'default': 1 // 0 to 2 + }); + prefs.push({ + 'name': 'prefCaptionsRate', + 'label': this.translate( 'prefDescRate', 'Rate' ), + 'group': 'captions', + 'default': 1.2 // 0.1 to 10 (1 is normal speech; 2 is fast but decipherable; >2 is super fast) + }); + prefs.push({ + 'name': 'prefCaptionsVolume', + 'label': this.translate( 'volume', 'Volume' ), + 'group': 'captions', + 'default': 1 // 0 to 1 + }); } if (this.mediaType === 'video') { @@ -340,7 +370,7 @@ var thisObj, available, $prefsDiv, formTitle, introText, $prefsIntro,$prefsIntroP2,p3Text,$prefsIntroP3,i, j, $fieldset, fieldsetClass, fieldsetId, $legend, legendId, thisPref, $thisDiv, thisClass, - thisId, $thisLabel, $thisField, options,$thisOption,optionValue,optionLang,optionText, + thisId, $thisLabel, $thisField, captionsOptions,options,$thisOption,optionValue,optionLang,optionText, changedPref,changedSpan,changedText, currentDescState, prefDescVoice, $kbHeading,$kbList, kbLabels,keys,kbListText,$kbListItem, dialog,$saveButton,$cancelButton,$buttonContainer; @@ -434,7 +464,7 @@ thisPref = available[i]['name']; thisClass = 'able-' + thisPref; thisId = this.mediaId + '_' + thisPref; - $thisDiv = $('
').addClass(thisClass); + $thisDiv = $('
').addClass(thisClass + ' able-player-setting'); if (form === 'captions') { $thisLabel = $(''); @@ -442,44 +472,97 @@ name: thisPref, id: thisId, }); - if (thisPref !== 'prefCaptions' && thisPref !== 'prefCaptionsStyle') { - // add a change handler that updates the style of the sample caption text + // add a change handler that updates the style of the sample caption text + let viewingOptions = ['prefCaptionsPosition','prefCaptionsFont','prefCaptionsSize','prefCaptionsColor','prefCaptionsBGColor','prefCaptionsOpacity']; + if ( viewingOptions.indexOf(thisPref) !== -1 ) { $thisField.on( 'change', function() { changedPref = $(this).attr('name'); thisObj.stylizeCaptions(thisObj.$sampleCapsDiv,changedPref); }); } - options = this.getCaptionsOptions(thisPref); - for (j=0; j < options.length; j++) { + captionsOptions = this.getCaptionsOptions(thisPref); + $thisDiv.append($thisLabel,$thisField); + for (j=0; j < captionsOptions.length; j++) { if (thisPref === 'prefCaptionsPosition') { - optionValue = options[j]; + optionValue = captionsOptions[j]; if (optionValue === 'overlay') { optionText = this.translate( 'captionsPositionOverlay', 'Overlay' ); } else if (optionValue === 'below') { - optionValue = options[j]; + optionValue = captionsOptions[j]; optionText = this.translate( 'captionsPositionBelow', 'Below video' ); } - } else if (thisPref === 'prefCaptionsFont' || thisPref === 'prefCaptionsColor' || thisPref === 'prefCaptionsBGColor') { - optionValue = options[j][0]; - optionText = options[j][1]; + } else if (thisPref === 'prefCaptionsFont' || thisPref === 'prefCaptionsColor' || thisPref === 'prefCaptionsBGColor' || thisPref === 'prefCaptionsSpeak' ) { + optionValue = captionsOptions[j][0]; + optionText = captionsOptions[j][1]; } else if (thisPref === 'prefCaptionsOpacity') { - optionValue = options[j]; - optionText = options[j]; + optionValue = captionsOptions[j]; + optionText = captionsOptions[j]; optionText += (optionValue === '0%') ? ' (' + this.translate( 'transparent', 'transparent' ) + ')' : ' (' + this.translate( 'solid', 'solid' ) + ')'; - } else { - optionValue = options[j]; - optionText = options[j]; + } else if (thisPref === 'prefCaptionsSize') { + optionValue = captionsOptions[j]; + optionText = captionsOptions[j]; } - $thisOption = $('