diff --git a/src/handlers/audio_synth_handler.rs b/src/handlers/audio_synth_handler.rs index a9dfd5e336..d80fc5f98f 100644 --- a/src/handlers/audio_synth_handler.rs +++ b/src/handlers/audio_synth_handler.rs @@ -1,6 +1,7 @@ // Copyright (c) 2015 Brandon Thomas // TODO: This looks really bad now. Needs cleanup. +use std::collections::HashMap; use config::Config; use crypto::digest::Digest; use crypto::sha1::Sha1; @@ -43,6 +44,10 @@ const VOLUME_PARAM : &'static str = "vol"; const MONOPHONE_PADDING_START_PARAM : &'static str = "mps"; const MONOPHONE_PADDING_END_PARAM : &'static str = "mpe"; const POLYPHONE_PADDING_END_PARAM : &'static str = "ppe"; +const WORD_PADDING_START_PARAM : &'static str = "wps"; +const WORD_PADDING_END_PARAM : &'static str = "wpe"; + +type QueryParams = HashMap>; /// Represents a request to this endpoint. #[derive(Clone, Debug)] @@ -82,6 +87,12 @@ struct SpeakRequest { /** Padding after a polyphone. */ pub polyphone_padding_end: Option, + + /// Silent padding before a word (not one constructed from phones). + pub word_padding_start: Option, + + /// Silent padding after a word (not one constructed from phones). + pub word_padding_end: Option, } enum SpeakerRequestError { @@ -249,68 +260,11 @@ impl SpeakRequest { }, }; - let mps : Option = match map.get(MONOPHONE_PADDING_START_PARAM) { - None => { None }, - Some(list) => { - match list.get(0) { - None => { None }, - Some(s) => { - match s.trim().parse::() { - Err(_) => None, - Ok(i) => { - if i == 0 { - None - } else { - Some(i) - } - } - } - }, - } - }, - }; - - let mpe : Option = match map.get(MONOPHONE_PADDING_END_PARAM) { - None => { None }, - Some(list) => { - match list.get(0) { - None => { None }, - Some(s) => { - match s.trim().parse::() { - Err(_) => None, - Ok(i) => { - if i == 0 { - None - } else { - Some(i) - } - } - } - }, - } - }, - }; - - let ppe : Option = match map.get(POLYPHONE_PADDING_END_PARAM) { - None => { None }, - Some(list) => { - match list.get(0) { - None => { None }, - Some(s) => { - match s.trim().parse::() { - Err(_) => None, - Ok(i) => { - if i == 0 { - None - } else { - Some(i) - } - } - } - }, - } - }, - }; + let mps = get_u16(map, MONOPHONE_PADDING_START_PARAM); + let mpe = get_u16(map, MONOPHONE_PADDING_END_PARAM); + let ppe = get_u16(map, POLYPHONE_PADDING_END_PARAM); + let wps = get_u16(map, WORD_PADDING_START_PARAM); + let wpe = get_u16(map, WORD_PADDING_END_PARAM); Ok(SpeakRequest { sentence: sen, @@ -325,6 +279,8 @@ impl SpeakRequest { monophone_padding_start: mps, monophone_padding_end: mpe, polyphone_padding_end: ppe, + word_padding_start: wps, + word_padding_end: wpe, }) }, } @@ -413,7 +369,9 @@ impl AudioSynthHandler { request.speed, request.monophone_padding_start, request.monophone_padding_end, - request.polyphone_padding_end); + request.polyphone_padding_end, + request.word_padding_start, + request.word_padding_end); match generated { Err(e) => { println!("Error synthesizing: {:?}", e); @@ -475,3 +433,14 @@ impl AudioSynthHandler { } } + +/** Parse an optional u16 out of query parameters. */ +fn get_u16(params: &QueryParams, param_name: &str) -> Option { + params.get(param_name) + .and_then(|v| v.get(0)) + .map(|s| s.trim()) + .map(|s| s.parse::()) + .and_then(|res| res.ok()) + .and_then(|d| if d == 0 { None } else { Some(d) } ) +} + diff --git a/src/synthesizer.rs b/src/synthesizer.rs index 42c7e344b0..fb59b64d72 100644 --- a/src/synthesizer.rs +++ b/src/synthesizer.rs @@ -57,7 +57,9 @@ impl Synthesizer { speed: Option, monophone_padding_start: Option, monophone_padding_end: Option, - polyphone_padding_end: Option) + polyphone_padding_end: Option, + word_padding_start: Option, + word_padding_end: Option) -> Result { let mut words = split_sentence(sentence); @@ -83,7 +85,9 @@ impl Synthesizer { if use_words { word_added = self.concatenate_word(&mut concatenated_waveform, speaker, - word); + word, + word_padding_start, + word_padding_end); } if !word_added && use_n_phones { @@ -131,17 +135,33 @@ impl Synthesizer { /// Concatenate a word to the waveform we're building. Returns /// whether or not the word was successfully found and concatenated. - fn concatenate_word(&self, concatenated_waveform: &mut Vec, - speaker: &str, word: &str) -> bool { + fn concatenate_word(&self, + concatenated_waveform: &mut Vec, + speaker: &str, + word: &str, + word_padding_start: Option, + word_padding_end: Option) -> bool { match self.audiobank.get_word(speaker, word) { + None => { + // The word does not exist in the database. + false + }, Some(waveform_data) => { // The word exists in the database. + if word_padding_start.is_some() { + let pause = generate_pause(word_padding_start.unwrap()); + concatenated_waveform.extend(pause); + } + concatenated_waveform.extend(waveform_data); + + if word_padding_end.is_some() { + let pause = generate_pause(word_padding_end.unwrap()); + concatenated_waveform.extend(pause); + } + true }, - None => { - false - }, } } diff --git a/src/web/js/events.js b/src/web/js/events.js index 64ce318fc2..276776e212 100644 --- a/src/web/js/events.js +++ b/src/web/js/events.js @@ -63,6 +63,8 @@ mps = $('input#monophone_padding_start').val(), mpe = $('input#monophone_padding_end').val(), ppe = $('input#polyphone_padding_end').val(), + wps = $('input#word_padding_start').val(), + wpe = $('input#word_padding_end').val(), query = encodeURIComponent(sentence), url = '/speak?v=' + speaker + '&s=' + query @@ -71,6 +73,8 @@ + '&mps=' + mps + '&mpe=' + mpe + '&ppe=' + ppe + + '&wps=' + wps + + '&wpe=' + wpe + '&up=' + up + '&ud=' + ud + '&un=' + un @@ -90,7 +94,9 @@ ue, mps, mpe, - ppe); + ppe, + wps, + wpe); Sound.play(url); diff --git a/src/web/js/initialize.js b/src/web/js/initialize.js index 8635beb385..79e8c30cb4 100644 --- a/src/web/js/initialize.js +++ b/src/web/js/initialize.js @@ -25,6 +25,8 @@ mps = Url.getMonophonePaddingStart() || 0, mpe = Url.getMonophonePaddingEnd() || 0, ppe = Url.getPolyphonePaddingEnd() || 600, + wps = Url.getWordPaddingStart() || 600, + wpe = Url.getWordPaddingEnd() || 600, urlPreviousState = false; if (usePhonemes === null) usePhonemes = true; @@ -55,6 +57,8 @@ $('input#monophone_padding_start').val(mps); $('input#monophone_padding_end').val(mpe); $('input#polyphone_padding_end').val(ppe); + $('input#word_padding_start').val(wps); + $('input#word_padding_end').val(wpe); Library.load(function(library) { Ui.libraryLoadCallback(library); }); Sound.install(); diff --git a/src/web/js/url.js b/src/web/js/url.js index decab58f87..2b08c9cba0 100644 --- a/src/web/js/url.js +++ b/src/web/js/url.js @@ -34,6 +34,8 @@ _MONOPHONE_PADDING_START_KEY: 'mps', _MONOPHONE_PADDING_END_KEY: 'mpe', _POLYPHONE_PADDING_END_KEY: 'ppe', + _WORD_PADDING_START_KEY: 'wps', + _WORD_PADDING_END_KEY: 'wpe', /** Get the sentence from the `window.location`. */ getSentence: function() { @@ -157,6 +159,25 @@ } }, + getWordPaddingStart: function() { + var state = this.parseState(window.location); + if (!state || !(this._WORD_PADDING_START_KEY in state)) { + return null; + } else { + return state[this._WORD_PADDING_START_KEY]; + } + }, + + getWordPaddingEnd: function() { + var state = this.parseState(window.location); + if (!state || !(this._WORD_PADDING_END_KEY in state)) { + return null; + } else { + return state[this._WORD_PADDING_END_KEY]; + } + }, + + // TODO: Independent functions to set both speaker and sentence separately. /** Set the `window.history` speaker, sentence, phoneme use, and word use. */ setState: function(speaker, @@ -170,7 +191,9 @@ useEnds, monophonePaddingStart, monophonePaddingEnd, - polyphonePaddingEnd) { + polyphonePaddingEnd, + wordPaddingStart, + wordPaddingEnd) { var urlHash = this.fromParams(speaker, rawSentence, @@ -183,7 +206,9 @@ useEnds, monophonePaddingStart, monophonePaddingEnd, - polyphonePaddingEnd); + polyphonePaddingEnd, + wordPaddingStart, + wordPaddingEnd); console.log('set state, urlhash = ', urlHash); window.history.replaceState(null, null, urlHash); @@ -201,7 +226,9 @@ useEnds, monophonePaddingStart, monophonePaddingEnd, - polyphonePaddingEnd) { + polyphonePaddingEnd, + wordPaddingStart, + wordPaddingEnd) { var cleanedSentence = SentenceHelper.cleanSentence(rawSentence), cleanedSpeaker = speaker, // TODO: Check against speakers. @@ -222,6 +249,8 @@ state[this._MONOPHONE_PADDING_START_KEY] = monophonePaddingStart; state[this._MONOPHONE_PADDING_END_KEY] = monophonePaddingEnd; state[this._POLYPHONE_PADDING_END_KEY] = polyphonePaddingEnd; + state[this._WORD_PADDING_START_KEY] = wordPaddingStart; + state[this._WORD_PADDING_END_KEY] = wordPaddingEnd; json = JSON.stringify(state); diff --git a/web/index.html b/web/index.html index 60223c15bf..5ac6644554 100644 --- a/web/index.html +++ b/web/index.html @@ -51,7 +51,8 @@
- +
@@ -75,6 +76,16 @@ +
+ + +
+
+ + +