Better syllable synthesis

- Polysyllable fulfillment algorithm
  - w/ debug padding
  - Still needs "ends" support

- Lots of cleanup and improvement to syllable code
  - Added Polysyllable and Polysyllabic trait
  - Updated syllable heuristics for 3-syllable stretches
  - 4-syllable parsing
  - syllable debug logging + config
This commit is contained in:
Brandon Thomas committed 2016-11-02 03:26:09 -04:00
1 parent b011ce4b87
commit eeb213bb76
6 files changed
+575 -169

No files matched your search

+7 -7
View File
@@ -16,6 +16,13 @@ Audio Samples
- Real versions of ['AO0', 'AW0', 'AW2', 'AY0', 'EY0', 'OY0', 'UH0'], which
were backfilled with fakes.
Scaling
1. Load test
2. Read wave files into memory
3. Multi-server, with nginx as load balancer
4. Prevent others from calling it
5. Caching strategy
Tokenizing
- Plurals
- Times (9 p.m. ET)
@@ -42,13 +49,6 @@ Cleanup
- Remove old query parameters
- Set default volume
Scaling
1. Load test
2. Read wave files into memory
3. Multi-server, with nginx as load balancer
4. Prevent others from calling it
5. Caching strategy
Server
1. Favicon, .ico and apple icon
2. robots.txt
+3
View File
@@ -12,6 +12,7 @@
# Logging verbosity
log_handler = false
log_parsing = true
log_syllable_decomposition = false
log_synthesis = false
log_timing = true
@@ -22,5 +23,7 @@
# Logging verbosity
log_handler = false
log_parsing = false
log_syllable_decomposition = true
log_synthesis = true
log_timing = false
+4
View File
@@ -28,6 +28,7 @@ pub struct Config {
/// What events to log.
pub log_handler: Option<bool>,
pub log_parsing: Option<bool>,
pub log_syllable_decomposition: Option<bool>,
pub log_synthesis: Option<bool>,
pub log_timing: Option<bool>,
}
@@ -115,6 +116,9 @@ impl Config {
log_parsing: self.log_parsing
.clone()
.or(other.log_parsing.clone()),
log_syllable_decomposition: self.log_syllable_decomposition
.clone()
.or(other.log_syllable_decomposition.clone()),
log_synthesis: self.log_synthesis
.clone()
.or(other.log_synthesis.clone()),
+266 -156
View File
@@ -18,13 +18,23 @@ Arpabet has 54 different units:
- 3 Semivowels (Y, W, Q)
*/
pub fn arpabet_to_syllables(input: &Vec<String>) -> Option<Vec<Vec<String>>> {
pub type Polysyllable = Vec<Vec<String>>;
/// Traits on syllables, in case I decide to change the underlying
/// representation.
pub trait Polysyllabic {
/// Number of monophones in a polysyllable (each syllable has n-many
/// monophones).
fn monophone_count(&self) -> usize;
}
/// Break an arpabet string into syllables comprised of arpabet monophones.
pub fn arpabet_to_syllables(input: &Vec<String>) -> Option<Polysyllable> {
let mut phones = Vec::new();
for atom in input {
match Phoneme::from_str(atom) {
None => {
println!("NONE: {}", atom); // TODO REMOVE
return None;
},
Some(phone) => { phones.push(phone); },
@@ -39,9 +49,6 @@ pub fn arpabet_to_syllables(input: &Vec<String>) -> Option<Vec<Vec<String>>> {
let mut consonants_after_vowel = 0; // Count consonants after vowel
for phone in phones {
println!("syllable_has_vowel = {}", syllable_has_vowel);
println!("Phone: {:?}, {}", phone, phone.is_vowel());
if !building_syllable {
// Starting the very first syllable of the word.
building_syllable = true;
@@ -59,150 +66,39 @@ pub fn arpabet_to_syllables(input: &Vec<String>) -> Option<Vec<Vec<String>>> {
// Find out where to allocate any run of consonants from the
// previous (current) syllable.
let mut leading_consonant = None;
if consonants_after_vowel == 2 {
// There are two consonants between vowels.
// Split consonants - one per vowel.
leading_consonant = syllable.pop();
if consonants_after_vowel > 0 {
let movement = consonant_allocation(&syllable,
consonants_after_vowel);
// But let's check some heuristics...
// Advance means put the consonants on the NEXT syllable
let mut advance = false;
// Reattach means put the consonants on the PREVIOUS syllable
let mut reattach = false;
{
// >>> 2-CONSONANT SEPARATION HEURISTICS <<<
let first_consonant = syllable.last();
if first_consonant.is_some() && leading_consonant.is_some() {
// Sequence is:
// Vowel, Consonant, Consonant, (Vowel - the current "phone")
let first_consonant = first_consonant.unwrap();
let second_consonant = leading_consonant.as_ref().unwrap();
advance = match *first_consonant {
Phoneme::K { .. } => {
match *second_consonant {
Phoneme::Y => true, // K Y ..
_ => false,
}
},
Phoneme::T { .. } => {
match *second_consonant {
Phoneme::R => true, // T R ..
_ => false,
}
},
_ => false,
};
if !advance {
reattach = match *first_consonant {
Phoneme::S { .. } => {
match *second_consonant {
Phoneme::T => true, // .. S T
_ => false,
}
},
_ => false,
};
}
}
}
if advance && leading_consonant.is_some() {
// Let's actually move both consonants forward to the next syllable.
let first_consonant = syllable.pop();
if first_consonant.is_some() {
match movement {
ConsonantAlloc::MoveNone => {
syllables.push(syllable);
syllable = Vec::new();
syllable.push(first_consonant.unwrap()); // 1st
syllable.push(leading_consonant.unwrap()); // 2nd
}
syllable.push(phone);
syllable_has_vowel = true;
consonants_after_vowel = 0;
continue;
},
ConsonantAlloc::MoveOne => {
let one = syllable.pop();
syllables.push(syllable);
syllable = Vec::new();
syllable.push(one.unwrap());
},
ConsonantAlloc::MoveTwo => {
let one = syllable.pop();
let two = syllable.pop();
syllables.push(syllable);
syllable = Vec::new();
syllable.push(two.unwrap());
syllable.push(one.unwrap());
},
}
if reattach && leading_consonant.is_some() {
// Keep both consonants on the last syllable.
syllable.push(leading_consonant.unwrap());
syllables.push(syllable);
syllable = Vec::new();
syllable.push(phone);
syllable_has_vowel = true;
consonants_after_vowel = 0;
continue;
}
} else if consonants_after_vowel == 1 {
// There's just one consonant between vowels.
// Usually we want to assign the consonant to the next vowel.
leading_consonant = syllable.pop();
// But let's check some heuristics...
let mut reattach = false;
{
// >>> 1-CONSONANT SEPARATION HEURISTICS <<<
let previous_vowel = syllable.last();
if previous_vowel.is_some() && leading_consonant.is_some() {
// Sequence is: Vowel, Consonant, (Vowel - the current "phone")
let previous_vowel = previous_vowel.unwrap();
let previous_consonant = leading_consonant.as_ref().unwrap();
reattach = match *previous_vowel {
Phoneme::Aa { .. } => {
match *previous_consonant {
Phoneme::L => true, // AA L .. ("All")
_ => false,
}
}
Phoneme::Ae { .. } => {
match *previous_consonant {
Phoneme::S => true, // AE S .. ("Ass")
_ => false,
}
},
Phoneme::Ah { stress : Stress::Secondary } => {
match *previous_consonant {
Phoneme::L => true, // AH2 L .. ("A(u)l", eg (n)ull-ify)
_ => false,
}
},
Phoneme::Ao { .. } => {
match *previous_consonant {
Phoneme::R => true, // AO R .. ("Orr")
_ => false,
}
},
Phoneme::Ih { .. } => {
match *previous_consonant {
//Phoneme::K => true, // IH K .. ("Ick")
Phoneme::N => true, // IH N .. ("Inn")
Phoneme::S => true, // IH S .. ("Iss")
_ => false,
}
},
_ => false,
};
}
}
if reattach && leading_consonant.is_some() {
// Nope, let's put the consonant back with the previous vowel.
syllable.push(leading_consonant.unwrap());
leading_consonant = None;
}
syllable.push(phone);
syllable_has_vowel = true;
consonants_after_vowel = 0;
continue;
}
syllables.push(syllable);
syllable = Vec::new();
if leading_consonant.is_some() {
syllable.push(leading_consonant.unwrap());
}
syllable.push(phone);
syllable_has_vowel = true;
consonants_after_vowel = 0;
@@ -226,10 +122,8 @@ pub fn arpabet_to_syllables(input: &Vec<String>) -> Option<Vec<Vec<String>>> {
syllables.push(syllable);
}
println!("Syllables: {} - {:?}\n\n", syllables.len(), syllables);
// Convert back to string.
// FIXME: So inefficient!
// FIXME: So inefficient! Linked lists would probably be best here.
let mut output_syllables = Vec::new();
for syllable in syllables {
let mut output_syllable = Vec::new();
@@ -239,17 +133,192 @@ pub fn arpabet_to_syllables(input: &Vec<String>) -> Option<Vec<Vec<String>>> {
output_syllables.push(output_syllable);
}
info!(target: "syllable", "Syllables: {:?}", output_syllables);
Some(output_syllables)
}
// How many consonants to keep for a leading vowel.
enum ConsonantAlloc {
MoveNone,
MoveOne,
MoveTwo,
}
// Heuristics for how to handle the consonants between vowels.
// Determine which vowel they get allocated to.
#[inline]
fn consonant_allocation(syllable: &Vec<Phoneme>,
consonants_after_vowel: usize)
-> ConsonantAlloc {
match consonants_after_vowel {
4 => {
// There are four consonants between vowels.
// Of the four consonants, how many does the last vowel retain?
/*let mut movement = ConsonantAlloc::KeepTwo;
let first_consonant = syllable.get(syllable.len() - 4);
let second_consonant = syllable.get(syllable.len() - 3);
let third_consonant = syllable.get(syllable.len() - 2);
let fourth_consonant = syllable.get(syllable.len() - 1);
if first_consonant.is_some()
&& second_consonant.is_some()
&& third_consonant.is_some()
&& fourth_consonant.is_some() {
let first_consonant = first_consonant.unwrap();
let second_consonant = second_consonant.unwrap();
let third_consonant = third_consonant.unwrap();
let fourth_consonant = fourth_consonant.unwrap();
movement = match *first_consonant {
_ => ConsonantAlloc::KeepTwo,
};
}
movement*/
ConsonantAlloc::MoveTwo
},
3 => {
let first_consonant = syllable.get(syllable.len() - 3);
let second_consonant = syllable.get(syllable.len() - 2);
let third_consonant = syllable.get(syllable.len() - 1);
if first_consonant.is_some()
&& second_consonant.is_some()
&& third_consonant.is_some() {
let first_consonant = first_consonant.unwrap();
let second_consonant = second_consonant.unwrap();
let third_consonant = third_consonant.unwrap();
return match *first_consonant {
Phoneme::M => {
match *second_consonant {
Phoneme::P => {
match *third_consonant {
Phoneme::L => ConsonantAlloc::MoveTwo, // M | P L ...
Phoneme::R => ConsonantAlloc::MoveTwo, // M | P R ...
Phoneme::T => ConsonantAlloc::MoveOne, // M P | T ...
_ => ConsonantAlloc::MoveNone,
}
},
_ => ConsonantAlloc::MoveNone,
}
},
Phoneme::Z => ConsonantAlloc::MoveTwo, // Z _ _ ...
_ => ConsonantAlloc::MoveNone,
};
}
ConsonantAlloc::MoveNone
},
2 => {
// Usually we allocate one consonant per vowel, which seems to work most
// of the time.
let first_consonant = syllable.get(syllable.len() - 2);
let second_consonant = syllable.get(syllable.len() - 1);
if first_consonant.is_some()
&& second_consonant.is_some() {
let first_consonant = first_consonant.unwrap();
let second_consonant = second_consonant.unwrap();
return match *first_consonant {
Phoneme::K => {
match *second_consonant {
Phoneme::Y => ConsonantAlloc::MoveTwo, // K Y ..
_ => ConsonantAlloc::MoveOne,
}
},
Phoneme::S => {
match *second_consonant {
Phoneme::T => ConsonantAlloc::MoveNone, // .. S T
_ => ConsonantAlloc::MoveOne,
}
}
Phoneme::T => {
match *second_consonant {
Phoneme::R => ConsonantAlloc::MoveTwo, // T R ..
_ => ConsonantAlloc::MoveOne,
}
},
_ => ConsonantAlloc::MoveOne,
};
}
ConsonantAlloc::MoveOne
},
1 => {
// Usually we want to assign the single consonant to the next vowel.
let previous_vowel = syllable.get(syllable.len() - 2);
let previous_consonant = syllable.get(syllable.len() - 1);
if previous_vowel.is_some() && previous_consonant.is_some() {
let previous_vowel = previous_vowel.unwrap();
let previous_consonant = previous_consonant.unwrap();
return match *previous_vowel {
Phoneme::Aa { .. } => {
match *previous_consonant {
Phoneme::L => ConsonantAlloc::MoveNone, // AA L .. ("All")
_ => ConsonantAlloc::MoveOne,
}
}
Phoneme::Ae { .. } => {
match *previous_consonant {
Phoneme::S => ConsonantAlloc::MoveNone, // AE S .. ("Ass")
_ => ConsonantAlloc::MoveOne,
}
},
Phoneme::Ah { stress: Stress::Secondary } => {
match *previous_consonant {
// AH2 L .. ("A(u)l", eg (n)ull-ify)
Phoneme::L => ConsonantAlloc::MoveNone,
_ => ConsonantAlloc::MoveOne,
}
},
Phoneme::Ao { .. } => {
match *previous_consonant {
Phoneme::R => ConsonantAlloc::MoveNone, // AO R .. ("Orr")
_ => ConsonantAlloc::MoveOne,
}
},
Phoneme::Ih { .. } => {
match *previous_consonant {
//Phoneme::K => ConsonantAlloc::MoveNone, // IH K .. ("Ick")
Phoneme::N => ConsonantAlloc::MoveNone, // IH N .. ("Inn")
Phoneme::S => ConsonantAlloc::MoveNone, // IH S .. ("Iss")
_ => ConsonantAlloc::MoveOne,
}
},
_ => ConsonantAlloc::MoveOne,
};
}
ConsonantAlloc::MoveOne
},
_ => ConsonantAlloc::MoveNone,
}
}
impl Polysyllabic for Polysyllable {
fn monophone_count(&self) -> usize {
let mut count = 0;
for syllable in self {
count += syllable.len();
}
count
}
}
#[cfg(test)]
mod tests {
use super::arpabet_to_syllables;
use super::*;
#[test]
fn test_easy_cases() {
// Test a bunch of random words from ARPABET.
// Test a bunch of random easy words from ARPABET.
// Reduction
assert_eq!(syl("R IH0|D AH1 K|SH AH0 N"), conv("R IH0 D AH1 K SH AH0 N"));
@@ -334,31 +403,47 @@ mod tests {
/*
assert_eq!(syl(""), conv(""));
assert_eq!(syl(""), conv(""));
assert_eq!(syl(""), conv(""));
assert_eq!(syl(""), conv(""));
*/
}
// TODO ENABLE
//#[test]
fn test_lots_of_consonants() {
// Wisecracks
assert_eq!(syl("W AY1 Z|K R AE2 K S"), conv("W AY1 Z K R AE2 K S"));
#[test]
fn test_three_consonants() {
// Test words with three consonants between vowels
// Employable (vs. empty)
assert_eq!(syl("EH0 M|P L OY1|AH0|B AH0 L"),
conv("EH0 M P L OY1 AH0 B AH0 L"));
// Empty (vs. employable)
assert_eq!(syl("EH1 M P|T IY0"), conv("EH1 M P T IY0"));
// Temperamental
assert_eq!(syl("T EH2 M|P R AH0|M EH1 N|T AH0 L"),
conv("T EH2 M P R AH0 M EH1 N T AH0 L"));
// Wisecracks
assert_eq!(syl("W AY1 Z|K R AE2 K S"), conv("W AY1 Z K R AE2 K S"));
/*
assert_eq!(syl(""), conv(""));
*/
}
#[test]
fn test_four_consonants() {
// TODO ENABLE - These don't work yet.
// Explain
assert_eq!(syl("IH0 K S|P L EY1 N"), conv("IH0 K S P L EY1 N"));
// Landscape
assert_eq!(syl("L AE1 N D|S K EY2 P"), conv("L AE1 N D S K EY2 P"));
}
// TODO ENABLE
//#[test]
fn test_difficult() {
// Explain
assert_eq!(syl("IH0 K|S P L EY1 N"), conv("IH0 K S P L EY1 N"));
// TODO ENABLE - These don't work yet.
// Talking
assert_eq!(syl("T AO1 K|IH0 NG"), conv("T AO1 K IH0 NG"));
@@ -367,6 +452,31 @@ mod tests {
conv("AA2 R K AH0 P EH1 L AH0 G OW2"));
}
#[test]
fn polysyllablic_monophone_count() {
fn syllable(input: Vec<Vec<&str>>) -> Polysyllable {
let mut out = Vec::new();
for syllable in input {
let new_syllable = syllable.iter().map(|s| s.to_string()).collect();
out.push(new_syllable);
}
out
}
let polyphone = syllable(vec![vec![]]);
assert_eq!(0, polyphone.monophone_count());
let polyphone = syllable(vec![vec!["A"]]);
assert_eq!(1, polyphone.monophone_count());
let polyphone = syllable(vec![vec!["A", "B", "C"], vec!["D"]]);
assert_eq!(4, polyphone.monophone_count());
let polyphone = syllable(vec![vec!["A", "B", "C"], vec!["D", "E"],
vec!["F", "G"]]);
assert_eq!(7, polyphone.monophone_count());
}
// Shorter for tests
fn conv(input: &str) -> Option<Vec<Vec<String>>> {
arpabet_to_syllables(&pho(input))
+1
View File
@@ -35,6 +35,7 @@ impl log::Log for SimpleLogger {
match metadata.target() {
"handler" => self.config.log_handler.unwrap_or(false),
"parsing" => self.config.log_parsing.unwrap_or(false),
"syllable" => self.config.log_syllable_decomposition.unwrap_or(false),
"synthesis" => self.config.log_synthesis.unwrap_or(false),
"timing" => self.config.log_timing.unwrap_or(false),
_ => metadata.level() <= LogLevel::Info,
+294 -6
View File
@@ -1,6 +1,8 @@
// Copyright (c) 2016 Brandon Thomas <bt@brand.io, echelon@gmail.com>
use lang::arpabet::Arpabet;
use lang::syllables::Polysyllabic;
use lang::syllables::Polysyllable;
use lang::syllables::arpabet_to_syllables;
use speaker::Speaker;
use synthesis::audio::SampleBytes;
@@ -73,10 +75,19 @@ impl WordSynthesizer {
buffer.extend(pause);
}
for syllable_phones in syllables_of_phones {
let samples = try!(self.get_syllabic_n_phone_samples(speaker,
syllable_phones, false, false));
let samples = try!(self.get_polysyllable_sample(speaker,
syllables_of_phones, false));
if debug_padding_between_phones.is_some() {
let padding = generate_pause(debug_padding_between_phones.unwrap());
let last = samples.len() - 1;
for (i, sample) in samples.into_iter().enumerate() {
buffer.extend(sample);
if i < last {
buffer.extend(padding.clone());
}
}
} else {
for sample in samples {
buffer.extend(sample);
}
@@ -164,6 +175,283 @@ impl WordSynthesizer {
Ok(())
}
/// TODO DOC
/// TODO DOC
fn get_polysyllable_sample(&self,
speaker: &Speaker,
syllables: Polysyllable,
use_ends: bool)
-> Result<Vec<SampleBytes>, SynthesisError> {
let mut capacity = syllables.monophone_count();
let mut monophones_fulfilled: Vec<bool> = Vec::with_capacity(capacity);
let mut chunks: Vec<Option<SampleBytes>> = Vec::with_capacity(capacity);
// Use this to debug the synthesis.
let mut debug: Vec<Option<String>> = Vec::with_capacity(capacity);
for _ in 0..capacity {
monophones_fulfilled.push(false);
chunks.push(None);
debug.push(None);
}
let mut syllables_fulfilled: Vec<bool> = Vec::with_capacity(syllables.len());
for _ in 0..syllables.len() {
syllables_fulfilled.push(false);
}
// Part 1: Fulfill entire syllables at once (mono- or poly-syllables).
// TODO/FIXME: This is a horrendous implementation that *must* change.
// NB: Outer two loops look O(n^2), but it cannot be. Inner iteration
// eagerly breaks when there is no match.
// FIXME: Inner code, however, is terribly inefficient.
let mut start = 0; // Index into output vectors.
for i in 0..syllables.len() {
for j in i..syllables.len() {
let mut polyphone: Vec<String> = Vec::new();
for k in i..j {
let ref syllable = syllables[k];
for phone in syllable {
polyphone.push(phone.clone());
}
}
// println!("n-phone: {:?}", candidate_n_phone);
let phone = self.audiobank.get_n_phone(
speaker.as_str(),
&polyphone,
SamplePreference::Middle, // TODO
false // TODO
);
if phone.is_none() {
break;
}
let phone = phone.unwrap();
let end = start + polyphone.len();
chunks[start] = Some(phone);
debug[start] = Some(polyphone.join("_"));
for k in start..end {
monophones_fulfilled[k] = true;
}
}
start += syllables[i].len(); // Monophone count of single syllable
}
// Part 2: Fix up any holes left unfulfilled.
let mut monophones = Vec::new();
for syllable in syllables {
for phone in syllable {
monophones.push(phone.clone());
}
}
// 5-phone
if monophones.len() >= 5 {
let range = monophones.len() - 4;
for i in 0..range {
if monophones_fulfilled[i]
|| monophones_fulfilled[i+1]
|| monophones_fulfilled[i+2]
|| monophones_fulfilled[i+3]
|| monophones_fulfilled[i+4] {
continue;
}
let candidate_n_phone = &monophones[i..i+5];
let sample_preference = match i {
0 => { SamplePreference::Begin },
_ if i == range - 1 => { SamplePreference::End },
_ => { SamplePreference::Middle },
};
//println!("n-phone: {:?}", candidate_n_phone);
let phone = self.audiobank.get_n_phone(speaker.as_str(),
candidate_n_phone,
sample_preference,
use_ends);
if phone.is_some() {
chunks[i] = phone;
debug[i] = Some(monophones[i..i+5].join("_"));
for j in i..i+5 {
monophones_fulfilled[j] = true;
}
}
}
}
info!(target: "synthesis", "5-fulfilled: {:?}", monophones_fulfilled);
// 4-phone
if monophones.len() >= 4 {
let range = monophones.len() - 3;
for i in 0..range {
if monophones_fulfilled[i]
|| monophones_fulfilled[i+1]
|| monophones_fulfilled[i+2]
|| monophones_fulfilled[i+3] {
continue;
}
let candidate_n_phone = &monophones[i..i+4];
let sample_preference = match i {
0 => { SamplePreference::Begin },
_ if i == range - 1 => { SamplePreference::End },
_ => { SamplePreference::Middle },
};
//println!("n-phone: {:?}", candidate_n_phone);
let phone = self.audiobank.get_n_phone(speaker.as_str(),
candidate_n_phone,
sample_preference,
use_ends);
if phone.is_some() {
chunks[i] = phone;
debug[i] = Some(monophones[i..i+4].join("_"));
for j in i..i+4 {
monophones_fulfilled[j] = true;
}
}
}
}
info!(target: "synthesis", "4-fulfilled: {:?}", monophones_fulfilled);
// 3-phone
if monophones.len() >= 3 {
let range = monophones.len() - 2;
for i in 0..range {
if monophones_fulfilled[i]
|| monophones_fulfilled[i+1]
|| monophones_fulfilled[i+2] {
continue;
}
let candidate_n_phone = &monophones[i..i+3];
let sample_preference = match i {
0 => { SamplePreference::Begin },
_ if i == range - 1 => { SamplePreference::End },
_ => { SamplePreference::Middle },
};
let phone = self.audiobank.get_n_phone(speaker.as_str(),
candidate_n_phone,
sample_preference,
use_ends);
if phone.is_some() {
chunks[i] = phone;
debug[i] = Some(monophones[i..i+3].join("_"));
for j in i..i+3 {
monophones_fulfilled[j] = true;
}
}
}
}
info!(target: "synthesis", "3-fulfilled: {:?}", monophones_fulfilled);
// 2-phone
if monophones.len() >= 2 {
let range = monophones.len() - 1;
for i in 0..range {
if monophones_fulfilled[i] || monophones_fulfilled[i+1] {
continue;
}
let candidate_n_phone = &monophones[i..i+2];
let sample_preference = match i {
0 => { SamplePreference::Begin },
_ if i == range - 1 => { SamplePreference::End },
_ => { SamplePreference::Middle },
};
let phone = self.audiobank.get_n_phone(speaker.as_str(),
candidate_n_phone,
sample_preference,
use_ends);
if phone.is_some() {
chunks[i] = phone;
debug[i] = Some(monophones[i..i+2].join("_"));
for j in i..i+2 {
monophones_fulfilled[j] = true;
}
}
}
}
info!(target: "synthesis", "2-fulfilled: {:?}", monophones_fulfilled);
// 1-phone
for i in 0..monophones.len() {
if monophones_fulfilled[i] {
continue;
}
let sample_preference = match i {
0 => { SamplePreference::Begin },
_ if i == monophones.len() - 1 => { SamplePreference::End },
_ => { SamplePreference::Middle },
};
let phone = self.audiobank.get_n_phone(speaker.as_str(),
&monophones[i..i+1],
sample_preference,
use_ends);
if phone.is_some() {
chunks[i] = phone;
debug[i] = Some(monophones[i].to_string());
monophones_fulfilled[i] = true;
}
}
info!(target: "synthesis", "1-fulfilled: {:?}", monophones_fulfilled);
for x in monophones_fulfilled {
if !x { return Err(SynthesisError::MonophoneDne); }
}
let debug_str : Vec<String> = debug.into_iter()
.map(|x| if x.is_some() { x.unwrap() } else { "None".to_string() })
.collect();
info!(target: "synthesis", "Comprised of: {:?}", debug_str);
/*info!(target: "synthesis", "Results Length: {} of {} phones",
results.len(),
syllable.len());*/
let results: Vec<SampleBytes> = chunks.into_iter()
.filter_map(|x| x) // Woo, already Option<T>!
.collect();
Ok(results) // TODO
}
/// TODO DOC
/// TODO DOC
fn get_syllabic_n_phone_samples(&self,
@@ -363,15 +651,15 @@ impl WordSynthesizer {
.map(|x| if x.is_some() { x.unwrap() } else { "None".to_string() })
.collect();
//info!(target: "synthesis", "Comprised of: {:?}", debug_str);
info!(target: "synthesis", "Comprised of: {:?}", debug_str);
let results : Vec<SampleBytes> = chunks.into_iter()
.filter_map(|x| x) // Woo, already Option<T>!
.collect();
info!(target: "synthesis", "Results Length: {} of {} phones",
/*info!(target: "synthesis", "Results Length: {} of {} phones",
results.len(),
syllable.len());
syllable.len());*/
Ok(results)
}