fry/
lib.rs

1use std::mem::size_of;
2use include_data::include_data;
3
4/// The BYTE_SIZE of the files as reported by `ls -l`
5/// All consts defined here will be exactly this value divided by two `u16`s long.
6const BYTE_SIZE: usize = 20810;
7
8type PcmSample = i16;
9
10/// The sample size in bytes of the .wav files.
11const SAMPLE_SIZE: usize = size_of::<PcmSample>();
12
13/// The maximum length of the output buffer in chunks of BYTE_SIZE
14const MAX_LETTERS: usize = 32;
15
16/// Single letter constant length sample (in samples, not bytes)
17const LETTER_SAMPLES: usize = BYTE_SIZE/SAMPLE_SIZE;
18
19/// The maximum length of the output buffer in bytes.
20const MAX_BUFFER_SIZE: usize = BYTE_SIZE * MAX_LETTERS;
21
22/// Include the bytes of a raw file, and place them into a constant sized buffer of u16s, size: BYTE_SIZE/2
23/// Since we want half the number of bytes, but all of them to be stored in u16s.
24/// This could fail dramatically if endianess is swapped for some reason.
25/// By default, `espeak` will use little-endian on x86_64.
26macro_rules! import_raw {
27  ($var_name:ident, $file_name:literal) => {
28    const $var_name: [PcmSample; BYTE_SIZE/2] = include_data!($file_name);
29  }
30}
31
32import_raw!(A, "../data/a.raw");
33import_raw!(B, "../data/b.raw");
34import_raw!(C, "../data/c.raw");
35import_raw!(D, "../data/d.raw");
36import_raw!(E, "../data/e.raw");
37import_raw!(F, "../data/f.raw");
38import_raw!(G, "../data/g.raw");
39import_raw!(H, "../data/h.raw");
40import_raw!(I, "../data/i.raw");
41import_raw!(J, "../data/j.raw");
42import_raw!(K, "../data/k.raw");
43import_raw!(L, "../data/l.raw");
44import_raw!(M, "../data/m.raw");
45import_raw!(N, "../data/n.raw");
46import_raw!(O, "../data/o.raw");
47import_raw!(P, "../data/p.raw");
48import_raw!(Q, "../data/q.raw");
49import_raw!(R, "../data/r.raw");
50import_raw!(S, "../data/s.raw");
51import_raw!(T, "../data/t.raw");
52import_raw!(U, "../data/u.raw");
53import_raw!(V, "../data/v.raw");
54import_raw!(W, "../data/w.raw");
55import_raw!(X, "../data/x.raw");
56import_raw!(Y, "../data/y.raw");
57import_raw!(Z, "../data/z.raw");
58import_raw!(SPACE, "../data/space.raw");
59
60const fn letter_to_pcm(c: char) -> Option<[PcmSample; BYTE_SIZE/2]> {
61  match c {
62    'a' => Some(A),
63    'b' => Some(B),
64    'c' => Some(C),
65    'd' => Some(D),
66    'e' => Some(E),
67    'f' => Some(F),
68    'g' => Some(G),
69    'h' => Some(H),
70    'i' => Some(I),
71    'j' => Some(J),
72    'k' => Some(K),
73    'l' => Some(L),
74    'm' => Some(M),
75    'n' => Some(N),
76    'o' => Some(O),
77    'p' => Some(P),
78    'q' => Some(Q),
79    'r' => Some(R),
80    's' => Some(S),
81    't' => Some(T),
82    'u' => Some(U),
83    'v' => Some(V),
84    'w' => Some(W),
85    'x' => Some(X),
86    'y' => Some(Y),
87    'z' => Some(Z),
88    ' ' => Some(SPACE),
89    _ => None
90  }
91}
92
93/// Fill a buffer with TTS data.
94/// This is done by character.
95/// It can be done for `s` where s is less less than or equal to `MAX_LETTERS`.
96/// It returns Option<usize>:
97///
98/// * None if `s` is too large.
99/// * Some(usize) if successful, contained value is number of *letters*, not bytes that have been copied to the buffer.
100/// 
101/// If you want the number of bytes, multiply the v in Some(v) by `BYTE_SIZE`.
102pub fn tts<S: AsRef<str>>(s: S, buf: &mut [PcmSample; MAX_BUFFER_SIZE]) -> Option<usize> {
103  if s.as_ref().len() > MAX_LETTERS {
104    return None;
105  }
106  Some(
107    s
108    .as_ref()
109    .chars()
110    .fold(0, |offset: usize, ch| {
111      letter_to_pcm(ch).unwrap()
112        .iter()
113        .enumerate()
114        .for_each(|(i, pcm)| buf[(offset*BYTE_SIZE)+i] = *pcm);
115      offset+1
116    })
117  )
118}
119
120#[cfg(test)]
121mod tests {
122  use super::MAX_BUFFER_SIZE;
123  use super::tts;
124  use super::BYTE_SIZE;
125  use super::A;
126  use super::PcmSample;
127  use super::LETTER_SAMPLES;
128
129  #[test]
130  fn check_one_letter_str() {
131    let mut buf: [PcmSample; MAX_BUFFER_SIZE] = [0; MAX_BUFFER_SIZE];
132    let conv = String::from("a");
133    let bytes = tts(conv, &mut buf);
134    assert_eq!(bytes.unwrap(), 1);
135    let created_slice = &buf[0..LETTER_SAMPLES];
136    assert_eq!(created_slice.len(), A.len());
137    assert_eq!(created_slice, A);
138  }
139  #[test]
140  fn check_one_word_str() {
141    let mut buf: [PcmSample; MAX_BUFFER_SIZE] = [0; MAX_BUFFER_SIZE];
142    let conv = String::from("hello");
143    let bytes = tts(conv, &mut buf);
144    assert_eq!(bytes.unwrap(), 5);
145  }
146  #[test]
147  fn check_multi_word() {
148    let mut buf: [PcmSample; MAX_BUFFER_SIZE] = [0; MAX_BUFFER_SIZE];
149    let conv = String::from("hello world");
150    let bytes = tts(conv, &mut buf);
151    assert_eq!(bytes.unwrap(), 11);
152  }
153}