1use anyhow::Result;
2use std::path::Path;
3
4#[allow(dead_code)]
6pub struct AudioData {
7 pub data: String, pub filename: String,
9 pub mime_type: String,
10}
11
12pub fn process_audio_file(file_path: &Path) -> Result<String> {
14 let audio_bytes = std::fs::read(file_path)?;
15
16 use base64::{engine::general_purpose, Engine as _};
18 let base64_data = general_purpose::STANDARD.encode(&audio_bytes);
19
20 let extension = file_path
22 .extension()
23 .and_then(|ext| ext.to_str())
24 .unwrap_or("")
25 .to_lowercase();
26
27 let mime_type = match extension.as_str() {
28 "mp3" => "audio/mpeg",
29 "wav" => "audio/wav",
30 "flac" => "audio/flac",
31 "ogg" => "audio/ogg",
32 "m4a" | "mp4" => "audio/mp4",
33 "webm" => "audio/webm",
34 _ => "audio/wav", };
36
37 Ok(format!("data:{};base64,{}", mime_type, base64_data))
38}
39
40pub fn process_audio_url(url: &str) -> Result<String> {
42 Ok(url.to_string())
45}
46
47pub fn generate_wav_header(
52 data_size: u32,
53 sample_rate: u32,
54 channels: u16,
55 bits_per_sample: u16,
56) -> Vec<u8> {
57 let mut header = Vec::with_capacity(44);
58
59 header.extend_from_slice(b"RIFF");
61
62 let file_size = 36 + data_size;
64 header.extend_from_slice(&file_size.to_le_bytes());
65
66 header.extend_from_slice(b"WAVE");
68
69 header.extend_from_slice(b"fmt ");
71
72 header.extend_from_slice(&16u32.to_le_bytes());
74
75 header.extend_from_slice(&1u16.to_le_bytes());
77
78 header.extend_from_slice(&channels.to_le_bytes());
80
81 header.extend_from_slice(&sample_rate.to_le_bytes());
83
84 let byte_rate = sample_rate * channels as u32 * bits_per_sample as u32 / 8;
86 header.extend_from_slice(&byte_rate.to_le_bytes());
87
88 let block_align = channels * bits_per_sample / 8;
90 header.extend_from_slice(&block_align.to_le_bytes());
91
92 header.extend_from_slice(&bits_per_sample.to_le_bytes());
94
95 header.extend_from_slice(b"data");
97
98 header.extend_from_slice(&data_size.to_le_bytes());
100
101 header
102}
103
104pub fn pcm_to_wav(
114 pcm_data: &[u8],
115 sample_rate: Option<u32>,
116 channels: Option<u16>,
117 bits_per_sample: Option<u16>,
118) -> Vec<u8> {
119 let sample_rate = sample_rate.unwrap_or(24000); let channels = channels.unwrap_or(1); let bits_per_sample = bits_per_sample.unwrap_or(16); let data_size = pcm_data.len() as u32;
124 let header = generate_wav_header(data_size, sample_rate, channels, bits_per_sample);
125
126 let mut wav_data = Vec::with_capacity(header.len() + pcm_data.len());
127 wav_data.extend_from_slice(&header);
128 wav_data.extend_from_slice(pcm_data);
129
130 wav_data
131}
132
133pub fn is_likely_pcm(data: &[u8]) -> bool {
138 if data.len() < 4 {
140 return false;
141 }
142
143 let header = &data[0..4];
145
146 if header == b"RIFF" {
148 return false;
149 }
150
151 if header[0..3] == [0x49, 0x44, 0x33] || (header[0] == 0xFF && (header[1] & 0xE0) == 0xE0)
154 {
155 return false;
157 }
158
159 if header == b"fLaC" {
161 return false;
162 }
163
164 if header == b"OggS" {
166 return false;
167 }
168
169 true
171}
172
173pub fn get_audio_file_extension(data: &[u8], requested_format: Option<&str>) -> &'static str {
175 if let Some(format) = requested_format {
177 return match format.to_lowercase().as_str() {
178 "mp3" => "mp3",
179 "wav" => "wav",
180 "flac" => "flac",
181 "ogg" => "ogg",
182 "aac" => "aac",
183 "opus" => "opus",
184 "pcm" => "wav", _ => "wav", };
187 }
188
189 if is_likely_pcm(data) {
191 "wav" } else {
193 if data.len() >= 4 {
195 let header = &data[0..4];
196 if header == b"RIFF" {
197 "wav"
198 } else if header[0..3] == [0x49, 0x44, 0x33]
199 || (header[0] == 0xFF && (header[1] & 0xE0) == 0xE0)
200 {
201 "mp3"
202 } else if header == b"fLaC" {
203 "flac"
204 } else if header == b"OggS" {
205 "ogg"
206 } else {
207 "wav" }
209 } else {
210 "wav" }
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 #[test]
220 fn test_wav_header_generation() {
221 let header = generate_wav_header(1000, 44100, 2, 16);
222 assert_eq!(header.len(), 44);
223 assert_eq!(&header[0..4], b"RIFF");
224 assert_eq!(&header[8..12], b"WAVE");
225 assert_eq!(&header[12..16], b"fmt ");
226 }
227
228 #[test]
229 fn test_pcm_to_wav_conversion() {
230 let pcm_data = vec![0u8; 1000]; let wav_data = pcm_to_wav(&pcm_data, Some(44100), Some(2), Some(16));
232
233 assert_eq!(wav_data.len(), 44 + 1000);
235 assert_eq!(&wav_data[0..4], b"RIFF");
236 assert_eq!(&wav_data[8..12], b"WAVE");
237 }
238
239 #[test]
240 fn test_pcm_detection() {
241 let wav_header = b"RIFF\x24\x08\x00\x00WAVE";
243 assert!(!is_likely_pcm(wav_header));
244
245 let pcm_data = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC];
247 assert!(is_likely_pcm(&pcm_data));
248 }
249
250 #[test]
251 fn test_file_extension_detection() {
252 let pcm_data = vec![0x12, 0x34, 0x56, 0x78];
254 assert_eq!(get_audio_file_extension(&pcm_data, None), "wav");
255
256 let wav_data = b"RIFF\x24\x08\x00\x00WAVE";
258 assert_eq!(get_audio_file_extension(wav_data, None), "wav");
259
260 assert_eq!(get_audio_file_extension(&pcm_data, Some("mp3")), "mp3");
262 assert_eq!(get_audio_file_extension(&pcm_data, Some("pcm")), "wav");
263 }
264}