ibm_watson/text-to-speech/synthesis/
mod.rs1use std::borrow::Cow;
2pub mod errors;
4
5use reqwest::{Method, Request, StatusCode, Url, Version};
6use url::form_urlencoded::byte_serialize;
7
8use self::errors::SynthesisError;
9
10use super::TextToSpeech;
11
12#[derive(Clone, Copy)]
14pub enum AudioFormat {
15 AudioAlaw { sample_rate: u16 },
17 AudioBasic,
19 AudioFlac { sample_rate: Option<u16> },
21 AudioL16 {
23 sample_rate: u16,
24 endianess: Option<AudioEndianness>,
25 },
26 AudioOgg { sample_rate: Option<u16> },
28 AudioOggCodecsOpus { sample_rate: Option<u16> },
30 AudioOggCodecsVorbis { sample_rate: Option<u16> },
32 AudioMp3 { sample_rate: Option<u16> },
34 AudioMpeg { sample_rate: Option<u16> },
36 AudioMulaw { sample_rate: u16 },
38 AudioWav { sample_rate: Option<u16> },
40 AudioWebm,
42 AudioWebmCodecsOpus,
44 AudioWebmCodecsVorbis { sample_rate: Option<u16> },
46}
47
48impl Default for AudioFormat {
49 fn default() -> Self {
53 AudioFormat::AudioOggCodecsOpus {
54 sample_rate: Some(48000),
55 }
56 }
57}
58
59impl AudioFormat {
60 pub fn id(&self) -> Cow<'static, str> {
62 match &self {
63 AudioFormat::AudioAlaw { sample_rate } => {
64 let url = format!("audio/alaw;rate={sample_rate}");
65 serialise_bytes(&url)
66 }
67 AudioFormat::AudioBasic => Cow::from("audio/basic"),
68 AudioFormat::AudioFlac { sample_rate } => {
69 let url = format!("audio/flac;rate={}", sample_rate.unwrap_or(22050));
70 serialise_bytes(&url)
71 }
72 AudioFormat::AudioL16 {
73 sample_rate,
74 endianess: endianness,
75 } => {
76 let url = match endianness {
77 Some(endianness) => {
78 format!(
79 "audio/flac;rate={sample_rate};endianness={}",
80 endianness.id()
81 )
82 }
83 None => {
84 format!("audio/flac;rate={sample_rate}")
85 }
86 };
87 serialise_bytes(&url)
88 }
89 AudioFormat::AudioOgg { sample_rate } => {
90 let url = format!("audio/ogg;rate={}", sample_rate.unwrap_or(22050));
91 serialise_bytes(&url)
92 }
93 AudioFormat::AudioOggCodecsOpus { sample_rate } => {
94 let url = format!(
95 "audio/ogg;codecs=opus;rate={}",
96 match sample_rate {
97 Some(rate) => *rate,
98 None => 48000,
99 }
100 );
101 serialise_bytes(&url)
102 }
103 AudioFormat::AudioOggCodecsVorbis { sample_rate } => {
104 let url = format!(
105 "audio/ogg;codecs=vorbis;rate={}",
106 sample_rate.unwrap_or(22050)
107 );
108 serialise_bytes(&url)
109 }
110 AudioFormat::AudioMp3 { sample_rate } => {
111 let url = format!("audio/mp3;rate={}", sample_rate.unwrap_or(22050));
112 serialise_bytes(&url)
113 }
114 AudioFormat::AudioMpeg { sample_rate } => {
115 let url = format!("audio/mpeg;rate={}", sample_rate.unwrap_or(22050));
116 serialise_bytes(&url)
117 }
118 AudioFormat::AudioMulaw { sample_rate } => {
119 let url = format!("audio/mulaw;rate={}", sample_rate);
120 serialise_bytes(&url)
121 }
122 AudioFormat::AudioWav { sample_rate } => {
123 let url = format!("audio/wav;rate={}", sample_rate.unwrap_or(22050));
124 serialise_bytes(&url)
125 }
126 AudioFormat::AudioWebm => serialise_bytes("audio/webm"),
127 AudioFormat::AudioWebmCodecsOpus => serialise_bytes("audio/webm;codecs=opus"),
128 AudioFormat::AudioWebmCodecsVorbis { sample_rate } => {
129 let url = format!(
130 "audio/webm;codecs=vorbis/rate={}",
131 sample_rate.unwrap_or(22050)
132 );
133 serialise_bytes(&url)
134 }
135 }
136 }
137}
138
139fn serialise_bytes(url: &str) -> Cow<'static, str> {
140 let url: String = byte_serialize(url.as_bytes()).collect();
141 Cow::from(url)
142}
143
144#[derive(Default, Clone, Copy)]
145pub enum AudioEndianness {
147 BigEndian,
149 #[default]
150 LittleEndian,
152}
153
154impl AudioEndianness {
155 pub fn id(&self) -> &str {
159 match self {
160 AudioEndianness::BigEndian => "big-endian",
161 AudioEndianness::LittleEndian => "little-endian",
162 }
163 }
164}
165
166impl TextToSpeech<'_> {
167 pub async fn synthesise(
197 &self,
198 text: impl AsRef<str>,
199 format: Option<AudioFormat>,
200 customisation_id: Option<&str>,
201 ) -> Result<bytes::Bytes, SynthesisError> {
202 let mut url = Url::parse(self.service_url).unwrap();
203 url.set_path("v1/synthesize");
204 url.set_query(customisation_id);
205 url.query_pairs_mut().append_pair("text", text.as_ref());
206 url.query_pairs_mut().append_pair("voice", self.voice.id());
207 if let Some(format) = format {
208 url.query_pairs_mut().append_pair("accept", &format.id());
209 }
210 let mut req = Request::new(Method::GET, url);
211
212 if cfg!(feature = "http2") {
213 *req.version_mut() = Version::HTTP_2;
214 }
215
216 let client = self.get_client();
217 let response = client
218 .execute(req)
219 .await
220 .map_err(|e| SynthesisError::ConnectionError(e.to_string()))?;
221 assert_eq!(response.status(), 200);
222 match response.status() {
223 StatusCode::OK => {
224 let bytes = response.bytes().await.unwrap();
225 Ok(bytes)
226 }
227 StatusCode::NOT_ACCEPTABLE => Err(SynthesisError::NotAcceptable406),
228 StatusCode::UNSUPPORTED_MEDIA_TYPE => Err(SynthesisError::UnsupportedMediaType415),
229 StatusCode::INTERNAL_SERVER_ERROR => Err(SynthesisError::InternalServerError500),
230 StatusCode::SERVICE_UNAVAILABLE => Err(SynthesisError::ServiceUnavailable500),
231 StatusCode::BAD_REQUEST => Err(SynthesisError::BadRequest400),
232 StatusCode::NOT_FOUND => Err(SynthesisError::NotFound404),
233 _ => {
234 unreachable!()
235 }
236 }
237 }
238}