OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview | 6 * @fileoverview |
7 * This is a component extension that implements a text-to-speech (TTS) | 7 * This is a component extension that implements a text-to-speech (TTS) |
8 * engine powered by Google's speech synthesis API. | 8 * engine powered by Google's speech synthesis API. |
9 * | 9 * |
10 * This is an "event page", so it's not loaded when the API isn't being used, | 10 * This is an "event page", so it's not loaded when the API isn't being used, |
(...skipping 12 matching lines...) Expand all Loading... |
23 function TtsExtension() {} | 23 function TtsExtension() {} |
24 | 24 |
25 TtsExtension.prototype = { | 25 TtsExtension.prototype = { |
26 /** | 26 /** |
27 * The url prefix of the speech server, including static query | 27 * The url prefix of the speech server, including static query |
28 * parameters that don't change. | 28 * parameters that don't change. |
29 * @type {string} | 29 * @type {string} |
30 * @const | 30 * @const |
31 * @private | 31 * @private |
32 */ | 32 */ |
33 SPEECH_SERVER_URL_: | 33 SPEECH_SERVER_URL_: 'https://www.google.com/speech-api/v2/synthesize?' + |
34 'https://www.google.com/speech-api/v2/synthesize?' + | |
35 'enc=mpeg&client=chromium', | 34 'enc=mpeg&client=chromium', |
36 | 35 |
37 /** | 36 /** |
38 * A mapping from language and gender to voice name, hardcoded for now | 37 * A mapping from language and gender to voice name, hardcoded for now |
39 * until the speech synthesis server capabilities response provides this. | 38 * until the speech synthesis server capabilities response provides this. |
40 * The key of this map is of the form '<lang>-<gender>'. | 39 * The key of this map is of the form '<lang>-<gender>'. |
41 * @type {Object<string>} | 40 * @type {Object<string>} |
42 * @private | 41 * @private |
43 */ | 42 */ |
44 LANG_AND_GENDER_TO_VOICE_NAME_: { | 43 LANG_AND_GENDER_TO_VOICE_NAME_: { |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
140 lang = navigator.language; | 139 lang = navigator.language; |
141 | 140 |
142 // Look up the specific voice name for this language and gender. | 141 // Look up the specific voice name for this language and gender. |
143 // If it's not in the map, it doesn't matter - the language will | 142 // If it's not in the map, it doesn't matter - the language will |
144 // be used directly. This is only used for languages where more | 143 // be used directly. This is only used for languages where more |
145 // than one gender is actually available. | 144 // than one gender is actually available. |
146 var key = lang.toLowerCase() + '-' + gender; | 145 var key = lang.toLowerCase() + '-' + gender; |
147 var voiceName = this.LANG_AND_GENDER_TO_VOICE_NAME_[key]; | 146 var voiceName = this.LANG_AND_GENDER_TO_VOICE_NAME_[key]; |
148 | 147 |
149 var url = this.SPEECH_SERVER_URL_; | 148 var url = this.SPEECH_SERVER_URL_; |
150 chrome.systemPrivate.getApiKey((function(key) { | 149 chrome.systemPrivate.getApiKey( |
151 url += '&key=' + key; | 150 (function(key) { |
152 url += '&text=' + encodeURIComponent(utterance); | 151 url += '&key=' + key; |
153 url += '&lang=' + lang.toLowerCase(); | 152 url += '&text=' + encodeURIComponent(utterance); |
| 153 url += '&lang=' + lang.toLowerCase(); |
154 | 154 |
155 if (voiceName) | 155 if (voiceName) |
156 url += '&name=' + voiceName; | 156 url += '&name=' + voiceName; |
157 | 157 |
158 if (options.rate) { | 158 if (options.rate) { |
159 // Input rate is between 0.1 and 10.0 with a default of 1.0. | 159 // Input rate is between 0.1 and 10.0 with a default of 1.0. |
160 // Output speed is between 0.0 and 1.0 with a default of 0.5. | 160 // Output speed is between 0.0 and 1.0 with a default of 0.5. |
161 url += '&speed=' + (options.rate / 2.0); | 161 url += '&speed=' + (options.rate / 2.0); |
162 } | 162 } |
163 | 163 |
164 if (options.pitch) { | 164 if (options.pitch) { |
165 // Input pitch is between 0.0 and 2.0 with a default of 1.0. | 165 // Input pitch is between 0.0 and 2.0 with a default of 1.0. |
166 // Output pitch is between 0.0 and 1.0 with a default of 0.5. | 166 // Output pitch is between 0.0 and 1.0 with a default of 0.5. |
167 url += '&pitch=' + (options.pitch / 2.0); | 167 url += '&pitch=' + (options.pitch / 2.0); |
168 } | 168 } |
169 | 169 |
170 // This begins loading the audio but does not play it. | 170 // This begins loading the audio but does not play it. |
171 // When enough of the audio has loaded to begin playback, | 171 // When enough of the audio has loaded to begin playback, |
172 // the 'canplaythrough' handler will call this.onStart_, | 172 // the 'canplaythrough' handler will call this.onStart_, |
173 // which sends a start event to the ttsEngine callback and | 173 // which sends a start event to the ttsEngine callback and |
174 // then begins playing audio. | 174 // then begins playing audio. |
175 this.audioElement_.src = url; | 175 this.audioElement_.src = url; |
176 }).bind(this)); | 176 }).bind(this)); |
177 } catch (err) { | 177 } catch (err) { |
178 console.error(String(err)); | 178 console.error(String(err)); |
179 callback({ | 179 callback({'type': 'error', 'errorMessage': String(err)}); |
180 'type': 'error', | |
181 'errorMessage': String(err) | |
182 }); | |
183 this.currentUtterance_ = null; | 180 this.currentUtterance_ = null; |
184 } | 181 } |
185 }, | 182 }, |
186 | 183 |
187 /** | 184 /** |
188 * Handler for the chrome.ttsEngine.onStop interface. | 185 * Handler for the chrome.ttsEngine.onStop interface. |
189 * Called either when the ttsEngine API requests us to stop, or when | 186 * Called either when the ttsEngine API requests us to stop, or when |
190 * we reach the end of the audio stream. Pause the audio element to | 187 * we reach the end of the audio stream. Pause the audio element to |
191 * silence it, and send a callback to the ttsEngine API to let it know | 188 * silence it, and send a callback to the ttsEngine API to let it know |
192 * that we've completed. Note that the ttsEngine API manages callback | 189 * that we've completed. Note that the ttsEngine API manages callback |
(...skipping 20 matching lines...) Expand all Loading... |
213 * then begin playing the audio element. | 210 * then begin playing the audio element. |
214 * @private | 211 * @private |
215 */ | 212 */ |
216 onStart_: function() { | 213 onStart_: function() { |
217 if (this.currentUtterance_) { | 214 if (this.currentUtterance_) { |
218 if (this.currentUtterance_.options.volume !== undefined) { | 215 if (this.currentUtterance_.options.volume !== undefined) { |
219 // Both APIs use the same range for volume, between 0.0 and 1.0. | 216 // Both APIs use the same range for volume, between 0.0 and 1.0. |
220 this.audioElement_.volume = this.currentUtterance_.options.volume; | 217 this.audioElement_.volume = this.currentUtterance_.options.volume; |
221 } | 218 } |
222 this.audioElement_.play(); | 219 this.audioElement_.play(); |
223 this.currentUtterance_.callback({ | 220 this.currentUtterance_.callback({'type': 'start', 'charIndex': 0}); |
224 'type': 'start', | |
225 'charIndex': 0 | |
226 }); | |
227 } | 221 } |
228 }, | 222 }, |
229 | 223 |
230 /** | 224 /** |
231 * Handler for the chrome.ttsEngine.onPause interface. | 225 * Handler for the chrome.ttsEngine.onPause interface. |
232 * Pauses audio if we're in the middle of an utterance. | 226 * Pauses audio if we're in the middle of an utterance. |
233 * @private | 227 * @private |
234 */ | 228 */ |
235 onPause_: function() { | 229 onPause_: function() { |
236 if (this.currentUtterance_) { | 230 if (this.currentUtterance_) { |
237 this.audioElement_.pause(); | 231 this.audioElement_.pause(); |
238 } | 232 } |
239 }, | 233 }, |
240 | 234 |
241 /** | 235 /** |
242 * Handler for the chrome.ttsEngine.onPause interface. | 236 * Handler for the chrome.ttsEngine.onPause interface. |
243 * Resumes audio if we're in the middle of an utterance. | 237 * Resumes audio if we're in the middle of an utterance. |
244 * @private | 238 * @private |
245 */ | 239 */ |
246 onResume_: function() { | 240 onResume_: function() { |
247 if (this.currentUtterance_) { | 241 if (this.currentUtterance_) { |
248 this.audioElement_.play(); | 242 this.audioElement_.play(); |
249 } | 243 } |
250 } | 244 } |
251 | 245 |
252 }; | 246 }; |
253 | 247 |
254 (new TtsExtension()).init(); | 248 (new TtsExtension()).init(); |
OLD | NEW |