1use super::buffer::AudioBuffer;
4use crate::{error::Result, types::AudioFormat, VoirsError};
5use std::path::Path;
6
7impl AudioBuffer {
8 pub fn save_wav(&self, path: impl AsRef<Path>) -> Result<()> {
10 use hound::{WavSpec, WavWriter};
11
12 let spec = WavSpec {
13 channels: self.channels as u16,
14 sample_rate: self.sample_rate,
15 bits_per_sample: 16,
16 sample_format: hound::SampleFormat::Int,
17 };
18
19 let mut writer = WavWriter::create(path, spec)
20 .map_err(|e| VoirsError::audio_error(format!("Failed to create WAV writer: {e}")))?;
21
22 for &sample in &self.samples {
24 let sample_i16 = (sample.clamp(-1.0, 1.0) * 32767.0) as i16;
25 writer
26 .write_sample(sample_i16)
27 .map_err(|e| VoirsError::audio_error(format!("Failed to write sample: {e}")))?;
28 }
29
30 writer
31 .finalize()
32 .map_err(|e| VoirsError::audio_error(format!("Failed to finalize WAV file: {e}")))?;
33
34 Ok(())
35 }
36
37 pub fn save_wav_f32(&self, path: impl AsRef<Path>) -> Result<()> {
39 use hound::{WavSpec, WavWriter};
40
41 let spec = WavSpec {
42 channels: self.channels as u16,
43 sample_rate: self.sample_rate,
44 bits_per_sample: 32,
45 sample_format: hound::SampleFormat::Float,
46 };
47
48 let mut writer = WavWriter::create(path, spec)
49 .map_err(|e| VoirsError::audio_error(format!("Failed to create WAV writer: {e}")))?;
50
51 for &sample in &self.samples {
53 writer
54 .write_sample(sample.clamp(-1.0, 1.0))
55 .map_err(|e| VoirsError::audio_error(format!("Failed to write sample: {e}")))?;
56 }
57
58 writer
59 .finalize()
60 .map_err(|e| VoirsError::audio_error(format!("Failed to finalize WAV file: {e}")))?;
61
62 Ok(())
63 }
64
65 pub fn save(&self, path: impl AsRef<Path>, format: AudioFormat) -> Result<()> {
67 match format {
68 AudioFormat::Wav => self.save_wav(path),
69 AudioFormat::Flac => self.save_flac(path),
70 AudioFormat::Mp3 => self.save_mp3(path),
71 AudioFormat::Ogg => self.save_ogg(path),
72 AudioFormat::Opus => self.save_opus(path),
73 }
74 }
75
76 pub fn save_flac(&self, path: impl AsRef<Path>) -> Result<()> {
78 tracing::warn!("FLAC encoding temporarily using WAV fallback - proper FLAC encoding support coming soon");
81 self.save_wav(path.as_ref().with_extension("wav"))
82 }
83
84 pub fn save_mp3(&self, path: impl AsRef<Path>) -> Result<()> {
86 tracing::warn!(
89 "MP3 encoding temporarily using WAV fallback - proper MP3 encoding support coming soon"
90 );
91 self.save_wav(path.as_ref().with_extension("wav"))
92 }
93
94 pub fn save_ogg(&self, path: impl AsRef<Path>) -> Result<()> {
96 use std::fs::File;
99 use std::io::Write;
100
101 tracing::info!("Saving OGG file with PCM data (basic implementation)");
102
103 let mut file = File::create(path.as_ref())
106 .map_err(|e| VoirsError::audio_error(format!("Failed to create OGG file: {e}")))?;
107
108 let ogg_header = b"OggS"; file.write_all(ogg_header)
111 .map_err(|e| VoirsError::audio_error(format!("Failed to write OGG header: {e}")))?;
112
113 let metadata = format!(
115 "channels={}\nsample_rate={}\nsamples={}\n",
116 self.channels,
117 self.sample_rate,
118 self.samples.len()
119 );
120 let metadata_bytes = metadata.as_bytes();
121 file.write_all(&(metadata_bytes.len() as u32).to_le_bytes())
122 .map_err(|e| {
123 VoirsError::audio_error(format!("Failed to write metadata length: {e}"))
124 })?;
125 file.write_all(metadata_bytes)
126 .map_err(|e| VoirsError::audio_error(format!("Failed to write metadata: {e}")))?;
127
128 for &sample in &self.samples {
130 let sample_i16 = (sample.clamp(-1.0, 1.0) * 32767.0) as i16;
131 file.write_all(&sample_i16.to_le_bytes())
132 .map_err(|e| VoirsError::audio_error(format!("Failed to write sample: {e}")))?;
133 }
134
135 tracing::info!("OGG file saved successfully: {}", path.as_ref().display());
136 Ok(())
137 }
138
139 pub fn save_opus(&self, path: impl AsRef<Path>) -> Result<()> {
141 use opus::{Application, Channels, Encoder};
142 use std::fs::File;
143 use std::io::Write;
144
145 let opus_sample_rate = match self.sample_rate {
147 8000 => 8000,
148 12000 => 12000,
149 16000 => 16000,
150 24000 => 24000,
151 48000 => 48000,
152 _ => 48000, };
154
155 let channels = match self.channels {
156 1 => Channels::Mono,
157 2 => Channels::Stereo,
158 _ => {
159 return Err(VoirsError::audio_error(
160 "Opus only supports mono or stereo audio",
161 ))
162 }
163 };
164
165 let mut encoder =
166 Encoder::new(opus_sample_rate, channels, Application::Audio).map_err(|e| {
167 VoirsError::audio_error(format!("Failed to create Opus encoder: {e:?}"))
168 })?;
169
170 let mut samples_i16 = Vec::with_capacity(self.samples.len());
172 for &sample in &self.samples {
173 let sample_i16 = (sample.clamp(-1.0, 1.0) * 32767.0) as i16;
174 samples_i16.push(sample_i16);
175 }
176
177 let frame_size = 960; let mut encoded_data = Vec::new();
180
181 for chunk in samples_i16.chunks(frame_size * self.channels as usize) {
182 let mut output = vec![0u8; 4000]; let encoded_size = encoder.encode(chunk, &mut output).map_err(|e| {
184 VoirsError::audio_error(format!("Failed to encode Opus frame: {e:?}"))
185 })?;
186
187 encoded_data.extend_from_slice(&output[..encoded_size]);
188 }
189
190 let mut file = File::create(path)
192 .map_err(|e| VoirsError::audio_error(format!("Failed to create Opus file: {e}")))?;
193 file.write_all(&encoded_data)
194 .map_err(|e| VoirsError::audio_error(format!("Failed to write Opus data: {e}")))?;
195
196 Ok(())
197 }
198
199 pub fn play(&self) -> Result<()> {
201 use cpal::{
202 traits::{DeviceTrait, HostTrait, StreamTrait},
203 Device, SampleFormat, StreamConfig,
204 };
205 use std::sync::{Arc, Mutex};
206
207 let host = cpal::default_host();
208 let device = host
209 .default_output_device()
210 .ok_or_else(|| VoirsError::audio_error("No output device available"))?;
211
212 let config = device
213 .default_output_config()
214 .map_err(|e| VoirsError::audio_error(format!("Failed to get output config: {e}")))?;
215
216 let sample_format = config.sample_format();
217 let stream_config: StreamConfig = config.into();
218
219 let samples = if self.sample_rate == stream_config.sample_rate.0 {
221 self.samples.clone()
222 } else {
223 self.resample(stream_config.sample_rate.0)?.samples
224 };
225
226 let samples = Arc::new(Mutex::new(samples.into_iter()));
227 let channels = self.channels;
228
229 let build_stream = |device: &Device, config: &StreamConfig, format: SampleFormat| {
230 let samples = samples.clone();
231 match format {
232 SampleFormat::F32 => device.build_output_stream(
233 config,
234 move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
235 let mut samples_lock = samples.lock().unwrap();
236 for frame in data.chunks_mut(channels as usize) {
237 let sample = samples_lock.next().unwrap_or(0.0);
238 for channel_sample in frame.iter_mut() {
239 *channel_sample = sample;
240 }
241 }
242 },
243 move |err| eprintln!("Audio stream error: {err}"),
244 None,
245 ),
246 SampleFormat::I16 => device.build_output_stream(
247 config,
248 move |data: &mut [i16], _: &cpal::OutputCallbackInfo| {
249 let mut samples_lock = samples.lock().unwrap();
250 for frame in data.chunks_mut(channels as usize) {
251 let sample = samples_lock.next().unwrap_or(0.0);
252 let sample_i16 = (sample.clamp(-1.0, 1.0) * i16::MAX as f32) as i16;
253 for channel_sample in frame.iter_mut() {
254 *channel_sample = sample_i16;
255 }
256 }
257 },
258 move |err| eprintln!("Audio stream error: {err}"),
259 None,
260 ),
261 SampleFormat::U16 => device.build_output_stream(
262 config,
263 move |data: &mut [u16], _: &cpal::OutputCallbackInfo| {
264 let mut samples_lock = samples.lock().unwrap();
265 for frame in data.chunks_mut(channels as usize) {
266 let sample = samples_lock.next().unwrap_or(0.0);
267 let sample_u16 =
268 ((sample.clamp(-1.0, 1.0) + 1.0) * u16::MAX as f32 / 2.0) as u16;
269 for channel_sample in frame.iter_mut() {
270 *channel_sample = sample_u16;
271 }
272 }
273 },
274 move |err| eprintln!("Audio stream error: {err}"),
275 None,
276 ),
277 _ => Err(cpal::BuildStreamError::StreamConfigNotSupported),
278 }
279 };
280
281 let stream = build_stream(&device, &stream_config, sample_format)
282 .map_err(|e| VoirsError::audio_error(format!("Failed to build audio stream: {e}")))?;
283
284 stream
285 .play()
286 .map_err(|e| VoirsError::audio_error(format!("Failed to start audio stream: {e}")))?;
287
288 let duration = self.duration();
290 std::thread::sleep(std::time::Duration::from_secs_f32(duration));
291
292 tracing::info!(
293 "Audio playback completed: {:.2}s @ {}Hz",
294 duration,
295 self.sample_rate
296 );
297 Ok(())
298 }
299
300 pub fn play_with_callback<F>(&self, callback: F) -> Result<()>
302 where
303 F: FnMut(f32) + Send + 'static, {
305 use cpal::{
306 traits::{DeviceTrait, HostTrait, StreamTrait},
307 Device, SampleFormat, StreamConfig,
308 };
309 use std::sync::{Arc, Mutex};
310 use std::time::{Duration, Instant};
311
312 let host = cpal::default_host();
313 let device = host
314 .default_output_device()
315 .ok_or_else(|| VoirsError::audio_error("No output device available"))?;
316
317 let config = device
318 .default_output_config()
319 .map_err(|e| VoirsError::audio_error(format!("Failed to get output config: {e}")))?;
320
321 let sample_format = config.sample_format();
322 let stream_config: StreamConfig = config.into();
323
324 let samples = if self.sample_rate == stream_config.sample_rate.0 {
326 self.samples.clone()
327 } else {
328 self.resample(stream_config.sample_rate.0)?.samples
329 };
330
331 let total_samples = samples.len();
332 let samples_iter = Arc::new(Mutex::new(samples.into_iter().enumerate()));
333 let channels = self.channels;
334
335 let progress_callback = Arc::new(Mutex::new(callback));
336 let last_progress_update = Arc::new(Mutex::new(Instant::now()));
337
338 let build_stream = |device: &Device, config: &StreamConfig, format: SampleFormat| {
339 let samples = samples_iter.clone();
340 let progress_callback = progress_callback.clone();
341 let last_progress_update = last_progress_update.clone();
342
343 match format {
344 SampleFormat::F32 => device.build_output_stream(
345 config,
346 move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
347 let mut samples_lock = samples.lock().unwrap();
348 for frame in data.chunks_mut(channels as usize) {
349 if let Some((index, sample)) = samples_lock.next() {
350 for channel_sample in frame.iter_mut() {
351 *channel_sample = sample;
352 }
353
354 let mut last_update = last_progress_update.lock().unwrap();
356 let now = Instant::now();
357 if now.duration_since(*last_update) >= Duration::from_millis(100) {
358 let progress = (index as f32) / (total_samples as f32);
359 if let Ok(mut callback) = progress_callback.lock() {
360 callback(progress);
361 }
362 *last_update = now;
363 }
364 } else {
365 for channel_sample in frame.iter_mut() {
367 *channel_sample = 0.0;
368 }
369 }
370 }
371 },
372 move |err| eprintln!("Audio stream error: {err}"),
373 None,
374 ),
375 SampleFormat::I16 => device.build_output_stream(
376 config,
377 move |data: &mut [i16], _: &cpal::OutputCallbackInfo| {
378 let mut samples_lock = samples.lock().unwrap();
379 for frame in data.chunks_mut(channels as usize) {
380 if let Some((index, sample)) = samples_lock.next() {
381 let sample_i16 = (sample.clamp(-1.0, 1.0) * i16::MAX as f32) as i16;
382 for channel_sample in frame.iter_mut() {
383 *channel_sample = sample_i16;
384 }
385
386 let mut last_update = last_progress_update.lock().unwrap();
388 let now = Instant::now();
389 if now.duration_since(*last_update) >= Duration::from_millis(100) {
390 let progress = (index as f32) / (total_samples as f32);
391 if let Ok(mut callback) = progress_callback.lock() {
392 callback(progress);
393 }
394 *last_update = now;
395 }
396 } else {
397 for channel_sample in frame.iter_mut() {
399 *channel_sample = 0;
400 }
401 }
402 }
403 },
404 move |err| eprintln!("Audio stream error: {err}"),
405 None,
406 ),
407 SampleFormat::U16 => device.build_output_stream(
408 config,
409 move |data: &mut [u16], _: &cpal::OutputCallbackInfo| {
410 let mut samples_lock = samples.lock().unwrap();
411 for frame in data.chunks_mut(channels as usize) {
412 if let Some((index, sample)) = samples_lock.next() {
413 let sample_u16 = ((sample.clamp(-1.0, 1.0) + 1.0) * u16::MAX as f32
414 / 2.0) as u16;
415 for channel_sample in frame.iter_mut() {
416 *channel_sample = sample_u16;
417 }
418
419 let mut last_update = last_progress_update.lock().unwrap();
421 let now = Instant::now();
422 if now.duration_since(*last_update) >= Duration::from_millis(100) {
423 let progress = (index as f32) / (total_samples as f32);
424 if let Ok(mut callback) = progress_callback.lock() {
425 callback(progress);
426 }
427 *last_update = now;
428 }
429 } else {
430 for channel_sample in frame.iter_mut() {
432 *channel_sample = u16::MAX / 2; }
434 }
435 }
436 },
437 move |err| eprintln!("Audio stream error: {err}"),
438 None,
439 ),
440 _ => Err(cpal::BuildStreamError::StreamConfigNotSupported),
441 }
442 };
443
444 let stream = build_stream(&device, &stream_config, sample_format)
445 .map_err(|e| VoirsError::audio_error(format!("Failed to build audio stream: {e}")))?;
446
447 stream
448 .play()
449 .map_err(|e| VoirsError::audio_error(format!("Failed to start audio stream: {e}")))?;
450
451 let duration = self.duration();
453 std::thread::sleep(std::time::Duration::from_secs_f32(duration));
454
455 if let Ok(mut callback) = progress_callback.lock() {
457 callback(1.0);
458 }
459
460 tracing::info!(
461 "Audio playback with progress completed: {:.2}s @ {}Hz",
462 duration,
463 self.sample_rate
464 );
465 Ok(())
466 }
467
468 pub fn to_format(&self, format: AudioFormat) -> Result<Vec<u8>> {
470 match format {
471 AudioFormat::Wav => self.to_wav_bytes(),
472 AudioFormat::Flac => self.to_flac_bytes(),
473 AudioFormat::Mp3 => self.to_mp3_bytes(),
474 AudioFormat::Ogg => self.to_ogg_bytes(),
475 AudioFormat::Opus => self.to_opus_bytes(),
476 }
477 }
478
479 pub fn to_wav_bytes(&self) -> Result<Vec<u8>> {
481 use hound::{WavSpec, WavWriter};
482 use std::io::Cursor;
483
484 let spec = WavSpec {
485 channels: self.channels as u16,
486 sample_rate: self.sample_rate,
487 bits_per_sample: 16,
488 sample_format: hound::SampleFormat::Int,
489 };
490
491 let mut cursor = Cursor::new(Vec::new());
492 {
493 let mut writer = WavWriter::new(&mut cursor, spec).map_err(|e| {
494 VoirsError::audio_error(format!("Failed to create WAV writer: {e}"))
495 })?;
496
497 for &sample in &self.samples {
499 let sample_i16 = (sample.clamp(-1.0, 1.0) * 32767.0) as i16;
500 writer
501 .write_sample(sample_i16)
502 .map_err(|e| VoirsError::audio_error(format!("Failed to write sample: {e}")))?;
503 }
504
505 writer
506 .finalize()
507 .map_err(|e| VoirsError::audio_error(format!("Failed to finalize WAV: {e}")))?;
508 }
509
510 Ok(cursor.into_inner())
511 }
512
513 pub fn to_flac_bytes(&self) -> Result<Vec<u8>> {
515 tracing::warn!("FLAC encoding temporarily using WAV fallback - proper FLAC encoding support coming soon");
518 self.to_wav_bytes()
519 }
520
521 pub fn to_mp3_bytes(&self) -> Result<Vec<u8>> {
523 tracing::warn!(
526 "MP3 encoding temporarily using WAV fallback - proper MP3 encoding support coming soon"
527 );
528 self.to_wav_bytes()
529 }
530
531 pub fn to_ogg_bytes(&self) -> Result<Vec<u8>> {
533 use std::io::Write;
536
537 tracing::info!("Converting to OGG bytes with PCM data (basic implementation)");
538
539 let mut ogg_data = Vec::new();
540
541 let ogg_header = b"OggS"; ogg_data
544 .write_all(ogg_header)
545 .map_err(|e| VoirsError::audio_error(format!("Failed to write OGG header: {e}")))?;
546
547 let metadata = format!(
549 "channels={}\nsample_rate={}\nsamples={}\n",
550 self.channels,
551 self.sample_rate,
552 self.samples.len()
553 );
554 let metadata_bytes = metadata.as_bytes();
555 ogg_data
556 .write_all(&(metadata_bytes.len() as u32).to_le_bytes())
557 .map_err(|e| {
558 VoirsError::audio_error(format!("Failed to write metadata length: {e}"))
559 })?;
560 ogg_data
561 .write_all(metadata_bytes)
562 .map_err(|e| VoirsError::audio_error(format!("Failed to write metadata: {e}")))?;
563
564 for &sample in &self.samples {
566 let sample_i16 = (sample.clamp(-1.0, 1.0) * 32767.0) as i16;
567 ogg_data
568 .write_all(&sample_i16.to_le_bytes())
569 .map_err(|e| VoirsError::audio_error(format!("Failed to write sample: {e}")))?;
570 }
571
572 Ok(ogg_data)
573 }
574
575 pub fn to_opus_bytes(&self) -> Result<Vec<u8>> {
577 use opus::{Application, Channels, Encoder};
578
579 let opus_sample_rate = match self.sample_rate {
581 8000 => 8000,
582 12000 => 12000,
583 16000 => 16000,
584 24000 => 24000,
585 48000 => 48000,
586 _ => 48000, };
588
589 let channels = match self.channels {
590 1 => Channels::Mono,
591 2 => Channels::Stereo,
592 _ => {
593 return Err(VoirsError::audio_error(
594 "Opus only supports mono or stereo audio",
595 ))
596 }
597 };
598
599 let mut encoder =
600 Encoder::new(opus_sample_rate, channels, Application::Audio).map_err(|e| {
601 VoirsError::audio_error(format!("Failed to create Opus encoder: {e:?}"))
602 })?;
603
604 let mut samples_i16 = Vec::with_capacity(self.samples.len());
606 for &sample in &self.samples {
607 let sample_i16 = (sample.clamp(-1.0, 1.0) * 32767.0) as i16;
608 samples_i16.push(sample_i16);
609 }
610
611 let frame_size = 960; let mut encoded_data = Vec::new();
614
615 for chunk in samples_i16.chunks(frame_size * self.channels as usize) {
616 let mut output = vec![0u8; 4000]; let encoded_size = encoder.encode(chunk, &mut output).map_err(|e| {
618 VoirsError::audio_error(format!("Failed to encode Opus frame: {e:?}"))
619 })?;
620
621 encoded_data.extend_from_slice(&output[..encoded_size]);
622 }
623
624 Ok(encoded_data)
625 }
626
627 pub fn load_wav(path: impl AsRef<Path>) -> Result<AudioBuffer> {
629 use hound::WavReader;
630
631 let mut reader = WavReader::open(path)
632 .map_err(|e| VoirsError::audio_error(format!("Failed to open WAV file: {e}")))?;
633
634 let spec = reader.spec();
635 let sample_rate = spec.sample_rate;
636 let channels = spec.channels as u32;
637
638 let samples: Result<Vec<f32>> = match spec.sample_format {
639 hound::SampleFormat::Float => reader
640 .samples::<f32>()
641 .map(|s| {
642 s.map_err(|e| VoirsError::audio_error(format!("Failed to read sample: {e}")))
643 })
644 .collect(),
645 hound::SampleFormat::Int => match spec.bits_per_sample {
646 16 => reader
647 .samples::<i16>()
648 .map(|s| {
649 s.map(|sample| sample as f32 / 32767.0).map_err(|e| {
650 VoirsError::audio_error(format!("Failed to read sample: {e}"))
651 })
652 })
653 .collect(),
654 24 => reader
655 .samples::<i32>()
656 .map(|s| {
657 s.map(|sample| sample as f32 / 8388607.0).map_err(|e| {
658 VoirsError::audio_error(format!("Failed to read sample: {e}"))
659 })
660 })
661 .collect(),
662 32 => reader
663 .samples::<i32>()
664 .map(|s| {
665 s.map(|sample| sample as f32 / 2147483647.0).map_err(|e| {
666 VoirsError::audio_error(format!("Failed to read sample: {e}"))
667 })
668 })
669 .collect(),
670 _ => {
671 return Err(VoirsError::audio_error(format!(
672 "Unsupported bit depth: {}",
673 spec.bits_per_sample
674 )))
675 }
676 },
677 };
678
679 let samples = samples?;
680 Ok(AudioBuffer::new(samples, sample_rate, channels))
681 }
682
683 pub fn load(path: impl AsRef<Path>) -> Result<AudioBuffer> {
685 let path = path.as_ref();
686 let extension = path
687 .extension()
688 .and_then(|ext| ext.to_str())
689 .unwrap_or("")
690 .to_lowercase();
691
692 match extension.as_str() {
693 "wav" => Self::load_wav(path),
694 "flac" => Self::load_flac(path),
695 "mp3" => Self::load_mp3(path),
696 "ogg" => Self::load_ogg(path),
697 "opus" => Self::load_opus(path),
698 _ => Err(VoirsError::audio_error(format!(
699 "Unsupported audio format: {extension}"
700 ))),
701 }
702 }
703
704 pub fn load_flac(path: impl AsRef<Path>) -> Result<AudioBuffer> {
706 use claxon::FlacReader;
707 use std::fs::File;
708
709 let file = File::open(path)
710 .map_err(|e| VoirsError::audio_error(format!("Failed to open FLAC file: {e}")))?;
711
712 let mut reader = FlacReader::new(file)
713 .map_err(|e| VoirsError::audio_error(format!("Failed to create FLAC reader: {e:?}")))?;
714
715 let info = reader.streaminfo();
716 let sample_rate = info.sample_rate;
717 let channels = info.channels;
718 let bits_per_sample = info.bits_per_sample;
719
720 let mut samples = Vec::new();
721
722 for sample_result in reader.samples() {
724 let sample = sample_result.map_err(|e| {
725 VoirsError::audio_error(format!("Failed to read FLAC sample: {e:?}"))
726 })?;
727
728 let sample_f32 = match bits_per_sample {
730 16 => sample as f32 / 32767.0,
731 24 => sample as f32 / 8388607.0,
732 32 => sample as f32 / 2147483647.0,
733 _ => {
734 return Err(VoirsError::audio_error(format!(
735 "Unsupported FLAC bit depth: {bits_per_sample}"
736 )))
737 }
738 };
739 samples.push(sample_f32);
740 }
741
742 Ok(AudioBuffer::new(samples, sample_rate, channels))
743 }
744
745 pub fn load_mp3(path: impl AsRef<Path>) -> Result<AudioBuffer> {
747 use minimp3::{Decoder, Frame};
748 use std::fs::File;
749 use std::io::Read;
750
751 let mut file = File::open(path)
752 .map_err(|e| VoirsError::audio_error(format!("Failed to open MP3 file: {e}")))?;
753
754 let mut buf = Vec::new();
755 file.read_to_end(&mut buf)
756 .map_err(|e| VoirsError::audio_error(format!("Failed to read MP3 file: {e}")))?;
757
758 let mut decoder = Decoder::new(&buf[..]);
759 let mut samples = Vec::new();
760 let mut sample_rate = 0;
761 let mut channels = 0;
762
763 loop {
765 match decoder.next_frame() {
766 Ok(Frame {
767 data,
768 sample_rate: sr,
769 channels: ch,
770 ..
771 }) => {
772 if sample_rate == 0 {
773 sample_rate = sr as u32;
774 channels = ch as u32;
775 }
776
777 for &sample in &data {
779 samples.push(sample as f32 / 32767.0);
780 }
781 }
782 Err(minimp3::Error::Eof) => break,
783 Err(e) => {
784 return Err(VoirsError::audio_error(format!(
785 "Failed to decode MP3: {e:?}"
786 )))
787 }
788 }
789 }
790
791 if samples.is_empty() {
792 return Err(VoirsError::audio_error("No audio data found in MP3 file"));
793 }
794
795 Ok(AudioBuffer::new(samples, sample_rate, channels))
796 }
797
798 pub fn load_ogg(path: impl AsRef<Path>) -> Result<AudioBuffer> {
800 use lewton::inside_ogg::OggStreamReader;
801 use std::fs::File;
802
803 let file = File::open(path)
804 .map_err(|e| VoirsError::audio_error(format!("Failed to open OGG file: {e}")))?;
805
806 let mut stream_reader = OggStreamReader::new(file)
807 .map_err(|e| VoirsError::audio_error(format!("Failed to create OGG reader: {e:?}")))?;
808
809 let sample_rate = stream_reader.ident_hdr.audio_sample_rate;
810 let channels = stream_reader.ident_hdr.audio_channels as u32;
811
812 let mut samples = Vec::new();
813
814 while let Some(packet) = stream_reader
816 .read_dec_packet_itl()
817 .map_err(|e| VoirsError::audio_error(format!("Failed to read OGG packet: {e:?}")))?
818 {
819 for sample in packet {
821 samples.push(sample as f32 / 32767.0);
822 }
823 }
824
825 if samples.is_empty() {
826 return Err(VoirsError::audio_error("No audio data found in OGG file"));
827 }
828
829 Ok(AudioBuffer::new(samples, sample_rate, channels))
830 }
831
832 pub fn load_opus(path: impl AsRef<Path>) -> Result<AudioBuffer> {
834 use opus::{Channels, Decoder};
835 use std::fs::File;
836 use std::io::Read;
837
838 let mut file = File::open(path)
839 .map_err(|e| VoirsError::audio_error(format!("Failed to open Opus file: {e}")))?;
840
841 let mut encoded_data = Vec::new();
842 file.read_to_end(&mut encoded_data)
843 .map_err(|e| VoirsError::audio_error(format!("Failed to read Opus file: {e}")))?;
844
845 let sample_rate = 48000;
850 let channels = Channels::Stereo;
851
852 let _decoder = Decoder::new(sample_rate, channels).map_err(|e| {
853 VoirsError::audio_error(format!("Failed to create Opus decoder: {e:?}"))
854 })?;
855
856 tracing::warn!("Raw Opus decoding not fully implemented - needs Ogg container support");
858 Err(VoirsError::audio_error(
859 "Opus loading requires Ogg container support (not yet implemented)",
860 ))
861 }
862
863 pub fn get_info(path: impl AsRef<Path>) -> Result<AudioInfo> {
865 let path = path.as_ref();
866 let extension = path
867 .extension()
868 .and_then(|ext| ext.to_str())
869 .unwrap_or("")
870 .to_lowercase();
871
872 match extension.as_str() {
873 "wav" => Self::get_wav_info(path),
874 "flac" => Self::get_flac_info(path),
875 "mp3" => Self::get_mp3_info(path),
876 "ogg" => Self::get_ogg_info(path),
877 "opus" => Self::get_opus_info(path),
878 _ => Err(VoirsError::audio_error(format!(
879 "Unsupported audio format: {extension}"
880 ))),
881 }
882 }
883
884 pub fn get_wav_info(path: impl AsRef<Path>) -> Result<AudioInfo> {
886 use hound::WavReader;
887
888 let reader = WavReader::open(path)
889 .map_err(|e| VoirsError::audio_error(format!("Failed to open WAV file: {e}")))?;
890
891 let spec = reader.spec();
892 let sample_count = reader.len() as usize;
893 let duration = sample_count as f32 / (spec.sample_rate * spec.channels as u32) as f32;
894
895 Ok(AudioInfo {
896 sample_rate: spec.sample_rate,
897 channels: spec.channels as u32,
898 duration,
899 sample_count,
900 format: AudioFormat::Wav,
901 })
902 }
903
904 pub fn get_flac_info(path: impl AsRef<Path>) -> Result<AudioInfo> {
906 use claxon::FlacReader;
907 use std::fs::File;
908
909 let file = File::open(path)
910 .map_err(|e| VoirsError::audio_error(format!("Failed to open FLAC file: {e}")))?;
911
912 let reader = FlacReader::new(file)
913 .map_err(|e| VoirsError::audio_error(format!("Failed to create FLAC reader: {e:?}")))?;
914
915 let info = reader.streaminfo();
916 let sample_rate = info.sample_rate;
917 let channels = info.channels;
918 let sample_count = info.samples.unwrap_or(0) as usize;
919 let duration = sample_count as f32 / (sample_rate * channels) as f32;
920
921 Ok(AudioInfo {
922 sample_rate,
923 channels,
924 duration,
925 sample_count,
926 format: AudioFormat::Flac,
927 })
928 }
929
930 #[allow(unused_assignments)] pub fn get_mp3_info(path: impl AsRef<Path>) -> Result<AudioInfo> {
933 use minimp3::{Decoder, Frame};
934 use std::fs::File;
935 use std::io::Read;
936
937 let mut file = File::open(path)
938 .map_err(|e| VoirsError::audio_error(format!("Failed to open MP3 file: {e}")))?;
939
940 let mut buf = Vec::new();
941 file.read_to_end(&mut buf)
942 .map_err(|e| VoirsError::audio_error(format!("Failed to read MP3 file: {e}")))?;
943
944 let mut decoder = Decoder::new(&buf[..]);
945 let mut sample_count = 0;
946 let mut sample_rate = 0;
947 let mut channels = 0;
948
949 match decoder.next_frame() {
951 Ok(Frame {
952 sample_rate: sr,
953 channels: ch,
954 ..
955 }) => {
956 sample_rate = sr as u32;
957 channels = ch as u32;
958
959 loop {
961 match decoder.next_frame() {
962 Ok(Frame { data, .. }) => {
963 sample_count += data.len();
964 }
965 Err(minimp3::Error::Eof) => break,
966 Err(e) => {
967 return Err(VoirsError::audio_error(format!(
968 "Failed to decode MP3: {e:?}"
969 )))
970 }
971 }
972 }
973 }
974 Err(e) => {
975 return Err(VoirsError::audio_error(format!(
976 "Failed to read MP3 header: {e:?}"
977 )))
978 }
979 }
980
981 let duration = sample_count as f32 / (sample_rate * channels) as f32;
982
983 Ok(AudioInfo {
984 sample_rate,
985 channels,
986 duration,
987 sample_count,
988 format: AudioFormat::Mp3,
989 })
990 }
991
992 pub fn get_ogg_info(path: impl AsRef<Path>) -> Result<AudioInfo> {
994 use lewton::inside_ogg::OggStreamReader;
995 use std::fs::File;
996
997 let file = File::open(path)
998 .map_err(|e| VoirsError::audio_error(format!("Failed to open OGG file: {e}")))?;
999
1000 let mut stream_reader = OggStreamReader::new(file)
1001 .map_err(|e| VoirsError::audio_error(format!("Failed to create OGG reader: {e:?}")))?;
1002
1003 let sample_rate = stream_reader.ident_hdr.audio_sample_rate;
1004 let channels = stream_reader.ident_hdr.audio_channels as u32;
1005
1006 let mut sample_count = 0;
1008 while let Some(packet) = stream_reader
1009 .read_dec_packet_itl()
1010 .map_err(|e| VoirsError::audio_error(format!("Failed to read OGG packet: {e:?}")))?
1011 {
1012 sample_count += packet.len();
1013 }
1014
1015 let duration = sample_count as f32 / (sample_rate * channels) as f32;
1016
1017 Ok(AudioInfo {
1018 sample_rate,
1019 channels,
1020 duration,
1021 sample_count,
1022 format: AudioFormat::Ogg,
1023 })
1024 }
1025
1026 pub fn get_opus_info(_path: impl AsRef<Path>) -> Result<AudioInfo> {
1028 tracing::warn!("Opus info reading requires Ogg container support (not yet implemented)");
1030 Err(VoirsError::audio_error(
1031 "Opus info reading requires Ogg container support (not yet implemented)",
1032 ))
1033 }
1034
1035 pub fn stream_to_callback<F>(&self, chunk_size: usize, mut callback: F) -> Result<()>
1037 where
1038 F: FnMut(&[f32]) -> Result<()>,
1039 {
1040 if chunk_size == 0 {
1041 return Err(VoirsError::audio_error("Chunk size must be greater than 0"));
1042 }
1043
1044 for chunk in self.samples.chunks(chunk_size) {
1045 callback(chunk)?;
1046 }
1047
1048 Ok(())
1049 }
1050
1051 pub fn export_metadata(&self) -> Result<String> {
1053 serde_json::to_string_pretty(&self.metadata)
1054 .map_err(|e| VoirsError::audio_error(format!("Failed to serialize metadata: {e}")))
1055 }
1056
1057 pub fn from_raw_bytes(
1059 bytes: &[u8],
1060 sample_rate: u32,
1061 channels: u32,
1062 format: RawFormat,
1063 ) -> Result<AudioBuffer> {
1064 let samples = match format {
1065 RawFormat::F32Le => {
1066 if !bytes.len().is_multiple_of(4) {
1067 return Err(VoirsError::audio_error(
1068 "Invalid byte length for F32 format",
1069 ));
1070 }
1071 bytes
1072 .chunks_exact(4)
1073 .map(|chunk| f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
1074 .collect()
1075 }
1076 RawFormat::I16Le => {
1077 if !bytes.len().is_multiple_of(2) {
1078 return Err(VoirsError::audio_error(
1079 "Invalid byte length for I16 format",
1080 ));
1081 }
1082 bytes
1083 .chunks_exact(2)
1084 .map(|chunk| {
1085 let val = i16::from_le_bytes([chunk[0], chunk[1]]);
1086 val as f32 / 32767.0
1087 })
1088 .collect()
1089 }
1090 RawFormat::U8 => bytes
1091 .iter()
1092 .map(|&byte| (byte as f32 - 128.0) / 128.0)
1093 .collect(),
1094 };
1095
1096 Ok(AudioBuffer::new(samples, sample_rate, channels))
1097 }
1098}
1099
1100#[derive(Debug, Clone)]
1102pub struct AudioInfo {
1103 pub sample_rate: u32,
1104 pub channels: u32,
1105 pub duration: f32,
1106 pub sample_count: usize,
1107 pub format: AudioFormat,
1108}
1109
1110#[derive(Debug, Clone, Copy)]
1112pub enum RawFormat {
1113 F32Le, I16Le, U8, }
1117
1118#[cfg(test)]
1119mod tests {
1120 use super::*;
1121 use crate::audio::buffer::AudioBuffer;
1122 use tempfile::NamedTempFile;
1123
1124 #[test]
1125 fn test_wav_save_load() {
1126 let original = AudioBuffer::sine_wave(440.0, 1.0, 44100, 0.5);
1127 let temp_file = NamedTempFile::new().unwrap();
1128
1129 original.save_wav(temp_file.path()).unwrap();
1131
1132 let loaded = AudioBuffer::load_wav(temp_file.path()).unwrap();
1134
1135 assert_eq!(loaded.sample_rate(), original.sample_rate());
1136 assert_eq!(loaded.channels(), original.channels());
1137 assert!((loaded.duration() - original.duration()).abs() < 0.01);
1138 }
1139
1140 #[test]
1141 fn test_wav_bytes_conversion() {
1142 let buffer = AudioBuffer::sine_wave(440.0, 0.1, 44100, 0.5);
1143
1144 let wav_bytes = buffer.to_wav_bytes().unwrap();
1145
1146 assert!(wav_bytes.len() > buffer.len() * 2); }
1149
1150 #[test]
1151 fn test_wav_info() {
1152 let buffer = AudioBuffer::sine_wave(440.0, 1.0, 44100, 0.5);
1153 let temp_file = NamedTempFile::new().unwrap();
1154
1155 buffer.save_wav(temp_file.path()).unwrap();
1156
1157 let info = AudioBuffer::get_wav_info(temp_file.path()).unwrap();
1158
1159 assert_eq!(info.sample_rate, 44100);
1160 assert_eq!(info.channels, 1);
1161 assert!((info.duration - 1.0).abs() < 0.01);
1162 assert_eq!(info.format, AudioFormat::Wav);
1163 }
1164
1165 #[test]
1166 fn test_stream_to_callback() {
1167 let buffer = AudioBuffer::sine_wave(440.0, 0.1, 44100, 0.5);
1168 let chunk_size = 1024;
1169 let mut total_samples = 0;
1170
1171 buffer
1172 .stream_to_callback(chunk_size, |chunk| {
1173 total_samples += chunk.len();
1174 Ok(())
1175 })
1176 .unwrap();
1177
1178 assert_eq!(total_samples, buffer.len());
1179 }
1180
1181 #[test]
1182 fn test_metadata_export() {
1183 let buffer = AudioBuffer::sine_wave(440.0, 1.0, 44100, 0.5);
1184
1185 let metadata_json = buffer.export_metadata().unwrap();
1186
1187 assert!(metadata_json.contains("duration"));
1189 assert!(metadata_json.contains("peak_amplitude"));
1190 assert!(!metadata_json.contains("sample_rate")); }
1192
1193 #[test]
1194 fn test_raw_bytes_conversion() {
1195 let samples = vec![0.0, 0.5, -0.5, 1.0];
1197 let original = AudioBuffer::mono(samples, 44100);
1198
1199 let _bytes = original.to_wav_bytes().unwrap();
1201
1202 let f32_bytes: Vec<u8> = original
1204 .samples()
1205 .iter()
1206 .flat_map(|&sample| sample.to_le_bytes())
1207 .collect();
1208
1209 let reconstructed =
1210 AudioBuffer::from_raw_bytes(&f32_bytes, 44100, 1, RawFormat::F32Le).unwrap();
1211
1212 assert_eq!(reconstructed.sample_rate(), original.sample_rate());
1213 assert_eq!(reconstructed.channels(), original.channels());
1214 assert_eq!(reconstructed.samples().len(), original.samples().len());
1215 }
1216
1217 #[test]
1218 fn test_f32_wav_save() {
1219 let buffer = AudioBuffer::sine_wave(440.0, 0.1, 44100, 0.5);
1220 let temp_file = NamedTempFile::new().unwrap();
1221
1222 buffer.save_wav_f32(temp_file.path()).unwrap();
1224
1225 let loaded = AudioBuffer::load_wav(temp_file.path()).unwrap();
1227
1228 assert_eq!(loaded.sample_rate(), buffer.sample_rate());
1229 assert_eq!(loaded.channels(), buffer.channels());
1230 assert!((loaded.duration() - buffer.duration()).abs() < 0.01);
1231 }
1232
1233 #[test]
1234 fn test_play_with_callback() {
1235 use std::sync::{Arc, Mutex};
1236
1237 let buffer = AudioBuffer::sine_wave(440.0, 0.1, 44100, 0.5);
1238 let progress_updates = Arc::new(Mutex::new(0));
1239 let progress_updates_clone = progress_updates.clone();
1240
1241 buffer
1242 .play_with_callback(move |progress| {
1243 let mut count = progress_updates_clone.lock().unwrap();
1244 *count += 1;
1245 assert!((0.0..=1.0).contains(&progress));
1246 })
1247 .unwrap();
1248
1249 let final_count = *progress_updates.lock().unwrap();
1250 assert!(final_count > 0); }
1252}