1mod record;
2mod playback;
3mod dsp;
4mod opus_encoder;
5mod opus_playback;
6
7use std::sync::atomic::{AtomicBool, Ordering};
8use std::sync::Arc;
9use std::thread;
10use std::sync::Mutex;
11use crate::record::record_audio;
12use crate::playback::playback_audio;
13use crate::opus_playback::playback_opus;
14
15pub use crate::dsp::AudioProcessor;
17pub use crate::opus_encoder::OpusEncoder;
18
19#[derive(Clone)]
20pub struct AudioFileInfo {
21 pub file_size: u64,
22 pub duration: f64,
23 pub original_wav_size: u64,
24 pub unprocessed_opus_size: u64,
25 pub processed_opus_size: u64,
26 pub last_message: String,
27}
28
29pub struct RusticAudio {
34 is_recording: Arc<AtomicBool>,
35 is_playing: Arc<AtomicBool>,
36 is_playing_original: Arc<AtomicBool>,
37 is_playing_unprocessed_opus: Arc<AtomicBool>,
38 recording_thread: Option<thread::JoinHandle<()>>,
39 playback_thread: Option<thread::JoinHandle<()>>,
40 playback_original_thread: Option<thread::JoinHandle<()>>,
41 playback_unprocessed_opus_thread: Option<thread::JoinHandle<()>>,
42 audio_info: Arc<Mutex<AudioFileInfo>>,
43 pub processor: AudioProcessor,
44 pub opus_encoder: OpusEncoder,
45}
46
47impl Default for RusticAudio {
48 fn default() -> Self {
49 Self {
50 is_recording: Arc::new(AtomicBool::new(false)),
51 is_playing: Arc::new(AtomicBool::new(false)),
52 is_playing_original: Arc::new(AtomicBool::new(false)),
53 is_playing_unprocessed_opus: Arc::new(AtomicBool::new(false)),
54 recording_thread: None,
55 playback_thread: None,
56 playback_original_thread: None,
57 playback_unprocessed_opus_thread: None,
58 audio_info: Arc::new(Mutex::new(AudioFileInfo {
59 file_size: 0,
60 duration: 0.0,
61 original_wav_size: 0,
62 unprocessed_opus_size: 0,
63 processed_opus_size: 0,
64 last_message: String::new(),
65 })),
66 processor: AudioProcessor::new(44100.0),
67 opus_encoder: OpusEncoder::new(),
68 }
69 }
70}
71
72impl RusticAudio {
73 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn start_recording(&mut self, output_path: &str) -> Result<(), String> {
87 if self.is_recording.load(Ordering::Relaxed) ||
88 self.is_playing.load(Ordering::Relaxed) ||
89 self.is_playing_original.load(Ordering::Relaxed) ||
90 self.is_playing_unprocessed_opus.load(Ordering::Relaxed) {
91 return Err("Another operation is already in progress".to_string());
92 }
93
94 let is_recording = Arc::clone(&self.is_recording);
95 let audio_info = Arc::clone(&self.audio_info);
96 let processor = self.processor.clone();
97 let opus_encoder = self.opus_encoder.clone();
98 let output_path = output_path.to_string();
99
100 self.is_recording.store(true, Ordering::Relaxed);
101 self.recording_thread = Some(thread::spawn(move || {
102 if let Ok(_) = record_audio(&output_path, is_recording, processor.clone()) {
103 let mut info = audio_info.lock().unwrap();
104 info.last_message = "Recording completed successfully".to_string();
105
106 let original_path = format!("{}_original.wav", output_path.trim_end_matches(".wav"));
108 if let Err(e) = std::fs::copy(&output_path, &original_path) {
109 info.last_message = format!("Error copying to original file: {:?}", e);
110 return;
111 }
112
113 if let Ok(metadata) = std::fs::metadata(&original_path) {
115 info.original_wav_size = metadata.len();
116 }
117
118 let mut processor_instance = processor;
120 let processed_path = format!("{}_processed.wav", output_path.trim_end_matches(".wav"));
121 if let Err(e) = processor_instance.process_file(&output_path, &processed_path) {
122 info.last_message = format!("Error processing audio: {:?}", e);
123 return;
124 }
125
126 let processed_opus_path = format!("{}_processed.opus", output_path.trim_end_matches(".wav"));
128 if let Err(e) = opus_encoder.encode_wav_to_opus(&processed_path, &processed_opus_path) {
129 info.last_message = format!("Error encoding to Opus: {:?}", e);
130 } else {
131 match opus_playback::get_opus_info(&processed_opus_path) {
133 Ok((size, duration)) => {
134 info.file_size = size;
135 info.processed_opus_size = size;
136 info.duration = duration;
137 info.last_message = "Processing and Opus encoding completed successfully".to_string();
138 }
139 Err(e) => {
140 info.last_message = format!("Error getting Opus file info: {:?}", e);
141 }
142 }
143 }
144
145 let unprocessed_opus_path = format!("{}_unprocessed.opus", output_path.trim_end_matches(".wav"));
147 if let Err(e) = opus_encoder.encode_wav_to_opus(&original_path, &unprocessed_opus_path) {
148 info.last_message = format!("Error encoding unprocessed audio: {:?}", e);
149 } else {
150 if let Ok(metadata) = std::fs::metadata(&unprocessed_opus_path) {
152 info.unprocessed_opus_size = metadata.len();
153 }
154 }
155 }
156 }));
157
158 Ok(())
159 }
160
161 pub fn stop_recording(&mut self) -> Result<(), String> {
162 if !self.is_recording.load(Ordering::Relaxed) {
163 return Err("Not currently recording".to_string());
164 }
165
166 self.is_recording.store(false, Ordering::Relaxed);
167
168 if let Some(thread) = self.recording_thread.take() {
170 if thread.join().is_err() {
171 return Err("Failed to join recording thread".to_string());
172 }
173 }
174
175 Ok(())
176 }
177
178 pub fn play_original_wav(&mut self, file_path: &str) -> Result<(), String> {
179 if self.is_recording.load(Ordering::Relaxed) ||
180 self.is_playing.load(Ordering::Relaxed) ||
181 self.is_playing_original.load(Ordering::Relaxed) ||
182 self.is_playing_unprocessed_opus.load(Ordering::Relaxed) {
183 return Err("Another operation is already in progress".to_string());
184 }
185
186 let is_playing = Arc::clone(&self.is_playing_original);
187 let audio_info = Arc::clone(&self.audio_info);
188 let file_path = file_path.to_string();
189
190 self.is_playing_original.store(true, Ordering::Relaxed);
191 self.playback_original_thread = Some(thread::spawn(move || {
192 match playback_audio(&file_path, is_playing) {
193 Ok(_) => {
194 let mut info = audio_info.lock().unwrap();
195 info.last_message = "Original playback completed successfully".to_string();
196 },
197 Err(e) => {
198 let mut info = audio_info.lock().unwrap();
199 info.last_message = format!("Error during original playback: {:?}", e);
200 },
201 }
202 }));
203
204 Ok(())
205 }
206
207 pub fn play_processed_wav(&mut self, file_path: &str) -> Result<(), String> {
208 if self.is_recording.load(Ordering::Relaxed) ||
209 self.is_playing.load(Ordering::Relaxed) ||
210 self.is_playing_original.load(Ordering::Relaxed) ||
211 self.is_playing_unprocessed_opus.load(Ordering::Relaxed) {
212 return Err("Another operation is already in progress".to_string());
213 }
214
215 let is_playing = Arc::clone(&self.is_playing);
216 let audio_info = Arc::clone(&self.audio_info);
217 let file_path = file_path.to_string();
218
219 self.is_playing.store(true, Ordering::Relaxed);
220 self.playback_thread = Some(thread::spawn(move || {
221 match playback_audio(&file_path, is_playing) {
222 Ok(_) => {
223 let mut info = audio_info.lock().unwrap();
224 info.last_message = "Processed WAV playback completed successfully".to_string();
225 },
226 Err(e) => {
227 let mut info = audio_info.lock().unwrap();
228 info.last_message = format!("Error during processed WAV playback: {:?}", e);
229 },
230 }
231 }));
232
233 Ok(())
234 }
235
236 pub fn play_unprocessed_opus(&mut self, file_path: &str) -> Result<(), String> {
237 if self.is_recording.load(Ordering::Relaxed) ||
238 self.is_playing.load(Ordering::Relaxed) ||
239 self.is_playing_original.load(Ordering::Relaxed) ||
240 self.is_playing_unprocessed_opus.load(Ordering::Relaxed) {
241 return Err("Another operation is already in progress".to_string());
242 }
243
244 let is_playing = Arc::clone(&self.is_playing_unprocessed_opus);
245 let audio_info = Arc::clone(&self.audio_info);
246 let file_path = file_path.to_string();
247
248 self.is_playing_unprocessed_opus.store(true, Ordering::Relaxed);
249 self.playback_unprocessed_opus_thread = Some(thread::spawn(move || {
250 match playback_opus(&file_path, is_playing) {
251 Ok(_) => {
252 let mut info = audio_info.lock().unwrap();
253 info.last_message = "Unprocessed opus playback completed successfully".to_string();
254 },
255 Err(e) => {
256 let mut info = audio_info.lock().unwrap();
257 info.last_message = format!("Error during unprocessed opus playback: {:?}", e);
258 },
259 }
260 }));
261
262 Ok(())
263 }
264
265 pub fn play_processed_opus(&mut self, file_path: &str) -> Result<(), String> {
266 if self.is_recording.load(Ordering::Relaxed) ||
267 self.is_playing.load(Ordering::Relaxed) ||
268 self.is_playing_original.load(Ordering::Relaxed) ||
269 self.is_playing_unprocessed_opus.load(Ordering::Relaxed) {
270 return Err("Another operation is already in progress".to_string());
271 }
272
273 let is_playing = Arc::clone(&self.is_playing);
274 let audio_info = Arc::clone(&self.audio_info);
275 let file_path = file_path.to_string();
276
277 self.is_playing.store(true, Ordering::Relaxed);
278 self.playback_thread = Some(thread::spawn(move || {
279 match playback_opus(&file_path, is_playing) {
280 Ok(_) => {
281 let mut info = audio_info.lock().unwrap();
282 info.last_message = "Processed opus playback completed successfully".to_string();
283 },
284 Err(e) => {
285 let mut info = audio_info.lock().unwrap();
286 info.last_message = format!("Error during processed opus playback: {:?}", e);
287 },
288 }
289 }));
290
291 Ok(())
292 }
293
294 pub fn stop_playback(&mut self) -> Result<(), String> {
295 if self.is_playing.load(Ordering::Relaxed) {
296 self.is_playing.store(false, Ordering::Relaxed);
297 if let Some(thread) = self.playback_thread.take() {
298 if thread.join().is_err() {
299 return Err("Failed to join playback thread".to_string());
300 }
301 }
302 }
303
304 if self.is_playing_original.load(Ordering::Relaxed) {
305 self.is_playing_original.store(false, Ordering::Relaxed);
306 if let Some(thread) = self.playback_original_thread.take() {
307 if thread.join().is_err() {
308 return Err("Failed to join original playback thread".to_string());
309 }
310 }
311 }
312
313 if self.is_playing_unprocessed_opus.load(Ordering::Relaxed) {
314 self.is_playing_unprocessed_opus.store(false, Ordering::Relaxed);
315 if let Some(thread) = self.playback_unprocessed_opus_thread.take() {
316 if thread.join().is_err() {
317 return Err("Failed to join unprocessed opus playback thread".to_string());
318 }
319 }
320 }
321
322 Ok(())
323 }
324
325 pub fn get_audio_info(&self) -> AudioFileInfo {
326 self.audio_info.lock().unwrap().clone()
327 }
328
329 pub fn set_opus_bitrate(&mut self, bitrate: i32) {
330 self.opus_encoder.set_bitrate(bitrate);
331 }
332
333 pub fn get_opus_bitrate(&self) -> i32 {
334 self.opus_encoder.get_bitrate()
335 }
336
337 pub fn process_file(&mut self, input_path: &str, output_path: &str) -> Result<(), Box<dyn std::error::Error>> {
338 self.processor.process_file(input_path, output_path)
339 }
340
341 pub fn encode_to_opus(&self, input_path: &str, output_path: &str) -> Result<(), Box<dyn std::error::Error>> {
342 self.opus_encoder.encode_wav_to_opus(input_path, output_path)
343 }
344
345 pub fn is_recording(&self) -> bool {
346 self.is_recording.load(Ordering::Relaxed)
347 }
348
349 pub fn is_playing(&self) -> bool {
350 self.is_playing.load(Ordering::Relaxed) ||
351 self.is_playing_original.load(Ordering::Relaxed) ||
352 self.is_playing_unprocessed_opus.load(Ordering::Relaxed)
353 }
354}
355