Skip to content

Commit 13cf2f3

Browse files
authored
feat!: add Engine::load_from_bytes() (#59)
BREAKING CHANGE: move `jbonsai::model::load_htsvoice_file` to `jbonsai::model::load_htsvoice_from_bytes`
1 parent f729e9a commit 13cf2f3

File tree

6 files changed

+54
-23
lines changed

6 files changed

+54
-23
lines changed

benches/bonsais.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fn bonsai(bencher: &mut Bencher) {
2222
"a^i-sil+xx=xx/A:xx+xx+xx/B:xx-xx_xx/C:xx_xx+xx/D:xx+xx_xx/E:4_4!0_xx-xx/F:xx_xx#xx_xx@xx_xx|xx_xx/G:xx_xx%xx_xx_xx/H:1_4/I:xx-xx@xx+xx&xx-xx|xx+xx/J:xx_xx/K:1+1-4",
2323
];
2424

25-
let engine = Engine::load(&[MODEL_NITECH_ATR503]).unwrap();
25+
let engine = Engine::load([MODEL_NITECH_ATR503]).unwrap();
2626

2727
bencher.iter(|| {
2828
engine.synthesize(&lines).unwrap();
@@ -60,7 +60,7 @@ fn is_bonsai(bencher: &mut Bencher) {
6060
"k^a-sil+xx=xx/A:xx+xx+xx/B:xx-xx_xx/C:xx_xx+xx/D:xx+xx_xx/E:7_5!1_xx-xx/F:xx_xx#xx_xx@xx_xx|xx_xx/G:xx_xx%xx_xx_xx/H:2_10/I:xx-xx@xx+xx&xx-xx|xx+xx/J:xx_xx/K:1+2-10",
6161
];
6262

63-
let engine = Engine::load(&[MODEL_NITECH_ATR503]).unwrap();
63+
let engine = Engine::load([MODEL_NITECH_ATR503]).unwrap();
6464

6565
bencher.iter(|| {
6666
engine.synthesize(&lines).unwrap();
@@ -132,7 +132,7 @@ fn bonsai_letter(bencher: &mut Bencher) {
132132
"t^a-sil+xx=xx/A:xx+xx+xx/B:xx-xx_xx/C:xx_xx+xx/D:xx+xx_xx/E:3_1!0_xx-xx/F:xx_xx#xx_xx@xx_xx|xx_xx/G:xx_xx%xx_xx_xx/H:6_24/I:xx-xx@xx+xx&xx-xx|xx+xx/J:xx_xx/K:1+6-24",
133133
];
134134

135-
let engine = Engine::load(&[MODEL_NITECH_ATR503]).unwrap();
135+
let engine = Engine::load([MODEL_NITECH_ATR503]).unwrap();
136136

137137
bencher.iter(|| {
138138
engine.synthesize(&lines).unwrap();

examples/genji/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
44
let label_str = std::fs::read_to_string("examples/genji/genji.lab")?;
55

66
let lines: Vec<_> = label_str.lines().collect();
7-
let mut engine = Engine::load(&[
7+
let mut engine = Engine::load([
88
"models/tohoku-f01/tohoku-f01-sad.htsvoice",
99
"models/tohoku-f01/tohoku-f01-happy.htsvoice",
1010
])?;

examples/is-bonsai/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
2424
"k^a-sil+xx=xx/A:xx+xx+xx/B:xx-xx_xx/C:xx_xx+xx/D:xx+xx_xx/E:7_5!1_xx-xx/F:xx_xx#xx_xx@xx_xx|xx_xx/G:xx_xx%xx_xx_xx/H:2_10/I:xx-xx@xx+xx&xx-xx|xx+xx/J:xx_xx/K:1+2-10",
2525
];
2626

27-
let engine = Engine::load(&[
27+
let engine = Engine::load([
2828
"models/hts_voice_nitech_jp_atr503_m001-1.05/nitech_jp_atr503_m001.htsvoice",
2929
])?;
3030
let speech = engine.synthesize(&lines)?;

src/engine.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,20 +254,37 @@ pub struct Engine {
254254
impl Engine {
255255
/// Load `.htsvoice` files and create a new [`Engine`].
256256
#[cfg(feature = "htsvoice")]
257-
pub fn load<P: AsRef<Path>>(voices: &[P]) -> Result<Self, EngineError> {
258-
use crate::model::load_htsvoice_file;
257+
pub fn load<P: AsRef<Path>>(voices: impl IntoIterator<Item = P>) -> Result<Self, EngineError> {
258+
Self::load_from_result_bytes(voices.into_iter().map(std::fs::read))
259+
}
260+
261+
/// Load htsvoice file content and create a new [`Engine`].
262+
#[cfg(feature = "htsvoice")]
263+
pub fn load_from_bytes<B: AsRef<[u8]>>(
264+
voices: impl IntoIterator<Item = B>,
265+
) -> Result<Self, EngineError> {
266+
Self::load_from_result_bytes(voices.into_iter().map(Ok))
267+
}
268+
269+
#[cfg(feature = "htsvoice")]
270+
fn load_from_result_bytes<B: AsRef<[u8]>>(
271+
voices: impl IntoIterator<Item = std::io::Result<B>>,
272+
) -> Result<Self, EngineError> {
273+
use crate::model::load_htsvoice_from_bytes;
259274

260275
let voices = voices
261-
.iter()
262-
.map(|path| Ok(Arc::new(load_htsvoice_file(path)?)))
276+
.into_iter()
277+
.map(|bytes| Ok(Arc::new(load_htsvoice_from_bytes(bytes?.as_ref())?)))
263278
.collect::<Result<Vec<_>, ModelError>>()?;
279+
264280
let voiceset = VoiceSet::new(voices)?;
265281

266282
let mut condition = Condition::default();
267283
condition.load_model(&voiceset)?;
268284

269285
Ok(Self::new(voiceset, condition))
270286
}
287+
271288
/// Create a new [`Engine`] with provided voices and condition.
272289
pub fn new(voices: VoiceSet, condition: Condition) -> Self {
273290
Engine { voices, condition }

src/lib.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,19 @@ mod tests {
3838

3939
#[test]
4040
fn bonsai() {
41-
let engine = Engine::load(&[MODEL_NITECH_ATR503]).unwrap();
41+
let engine = Engine::load([MODEL_NITECH_ATR503]).unwrap();
42+
43+
let speech = engine.synthesize(&SAMPLE_SENTENCE_1).unwrap();
44+
45+
assert_eq!(speech.len(), 66480);
46+
approx::assert_abs_diff_eq!(speech[2000], 19.35141137623778, epsilon = 1.0e-10);
47+
approx::assert_abs_diff_eq!(speech[30000], -980.6757547598129, epsilon = 1.0e-10);
48+
}
49+
50+
#[test]
51+
fn bonsai_load_from_bytes() {
52+
let model_bytes = std::fs::read(MODEL_NITECH_ATR503).unwrap();
53+
let engine = Engine::load_from_bytes(&[model_bytes]).unwrap();
4254

4355
let speech = engine.synthesize(&SAMPLE_SENTENCE_1).unwrap();
4456

@@ -54,7 +66,7 @@ mod tests {
5466
.map(|l| l.parse().unwrap())
5567
.collect();
5668

57-
let engine = Engine::load(&[MODEL_NITECH_ATR503]).unwrap();
69+
let engine = Engine::load([MODEL_NITECH_ATR503]).unwrap();
5870

5971
let speech = engine.synthesize(labels).unwrap();
6072

@@ -65,7 +77,7 @@ mod tests {
6577

6678
#[test]
6779
fn bonsai_multi() {
68-
let mut engine = Engine::load(&[MODEL_TOHOKU_F01_NORMAL, MODEL_TOHOKU_F01_HAPPY]).unwrap();
80+
let mut engine = Engine::load([MODEL_TOHOKU_F01_NORMAL, MODEL_TOHOKU_F01_HAPPY]).unwrap();
6981
let iw = engine.condition.get_interporation_weight_mut();
7082
iw.set_duration(&[0.7, 0.3]).unwrap();
7183
iw.set_parameter(0, &[0.7, 0.3]).unwrap();
@@ -110,7 +122,7 @@ mod tests {
110122

111123
#[test]
112124
fn is_this_bonsai() {
113-
let engine = Engine::load(&[MODEL_NITECH_ATR503]).unwrap();
125+
let engine = Engine::load([MODEL_NITECH_ATR503]).unwrap();
114126

115127
let speech = engine.synthesize(&SAMPLE_SENTENCE_2).unwrap();
116128

@@ -123,7 +135,7 @@ mod tests {
123135

124136
#[test]
125137
fn is_this_bonsai_fast() {
126-
let mut engine = Engine::load(&[MODEL_NITECH_ATR503]).unwrap();
138+
let mut engine = Engine::load([MODEL_NITECH_ATR503]).unwrap();
127139
engine.condition.set_speed(1.4);
128140

129141
let speech = engine.synthesize(&SAMPLE_SENTENCE_2).unwrap();
@@ -137,7 +149,7 @@ mod tests {
137149

138150
#[test]
139151
fn empty() {
140-
let mut engine = Engine::load(&[MODEL_NITECH_ATR503]).unwrap();
152+
let mut engine = Engine::load([MODEL_NITECH_ATR503]).unwrap();
141153
let labels: [&str; 0] = [];
142154

143155
let speech = engine.synthesize(&labels[..]).unwrap();

src/model/mod.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,10 @@ impl<'a> Models<'a> {
156156
}
157157
}
158158

159-
/// Load `.htsvoice` file as [`Voice`].
159+
/// Load `.htsvoice` file content as [`Voice`].
160160
#[cfg(feature = "htsvoice")]
161-
pub fn load_htsvoice_file<P: AsRef<std::path::Path>>(path: &P) -> Result<Voice, ModelError> {
162-
let f = std::fs::read(path)?;
163-
Ok(parser::parse_htsvoice(&f)?)
161+
pub fn load_htsvoice_from_bytes(bytes: &[u8]) -> Result<Voice, ModelError> {
162+
Ok(parser::parse_htsvoice(bytes)?)
164163
}
165164

166165
#[cfg(all(test, feature = "htsvoice"))]
@@ -174,10 +173,11 @@ pub(crate) mod tests {
174173
},
175174
};
176175

177-
use super::{Models, Voice, VoiceSet, load_htsvoice_file};
176+
use super::{Models, Voice, VoiceSet, load_htsvoice_from_bytes};
178177

179178
fn load_voice() -> Voice {
180-
load_htsvoice_file(&MODEL_NITECH_ATR503).unwrap()
179+
let htsvoice = std::fs::read(MODEL_NITECH_ATR503).unwrap();
180+
load_htsvoice_from_bytes(&htsvoice).unwrap()
181181
}
182182

183183
#[test]
@@ -394,8 +394,10 @@ pub(crate) mod tests {
394394

395395
#[test]
396396
fn multiple_models() {
397-
let normal = load_htsvoice_file(&MODEL_TOHOKU_F01_NORMAL).unwrap();
398-
let happy = load_htsvoice_file(&MODEL_TOHOKU_F01_HAPPY).unwrap();
397+
let normal_htsvoice = std::fs::read(MODEL_TOHOKU_F01_NORMAL).unwrap();
398+
let normal = load_htsvoice_from_bytes(&normal_htsvoice).unwrap();
399+
let happy_htsvoice = std::fs::read(MODEL_TOHOKU_F01_HAPPY).unwrap();
400+
let happy = load_htsvoice_from_bytes(&happy_htsvoice).unwrap();
399401
let voiceset = VoiceSet::new(vec![Arc::new(normal), Arc::new(happy)]).unwrap();
400402
let labels = vec![SAMPLE_SENTENCE_1[2].parse().unwrap()];
401403

0 commit comments

Comments
 (0)