rs_audio/wav/
exporter.rs

1use std::{
2    fs::File,
3    io::{BufReader, Error},
4};
5
6use hound::{WavSpec, WavWriter};
7use rodio::{Decoder, OutputStream, Sink};
8
9use crate::{waveform::generate_sample, Song};
10
11impl Song {
12    /**
13    Exports a Song struct to a .wav file.<br>
14    Usage:
15    ```
16    use rs_audio::*;
17    use rs_audio::{player::Song};
18
19    let song = Song::default();
20    song.export_to_wav("test.wav".to_string());
21    ```
22
23    # Panics
24    This function will panic if the file could not be created due to some error.
25    */
26    pub fn export_to_wav(&self, filename: String) -> Result<(), Box<dyn std::error::Error>> {
27        // set up wave file specs
28        let spec = WavSpec {
29            channels: 1,
30            sample_rate: 44100,  // 44.1k Hz
31            bits_per_sample: 16, // 16 bit depth
32            sample_format: hound::SampleFormat::Int,
33        };
34
35        // create writer for writing to files
36        let mut writer = match WavWriter::create(filename, spec) {
37            Ok(e) => e,
38            Err(e) => {
39                panic!("RS-AUDIO: Error while creating file: {e}");
40            }
41        };
42
43        for note in &self.notes {
44            let total_samples = (note.dur * 44100.0) as usize;
45            for i in 0..total_samples {
46                let phase = (i as f64 * note.freq / 44100.0) % 1.0; // generate phase
47                let sample = generate_sample(&note.wave, phase) * note.vol as f64; // generate sample from waveform
48                writer.write_sample((sample * i16::MAX as f64) as i16)?; // add the sample
49            }
50        }
51
52        writer.finalize()?;
53
54        Ok(())
55    }
56
57    /**
58    Plays a .wav file directly.<br>
59    Note that `.wav`'s are not converted to Songs in this function due to complexity.
60    <br>
61
62
63    Usage:
64    ```
65    use rs_audio::*;
66    use rs_audio::{player::Song};
67
68    Song::play_wav("test.wav").unwrap();
69    ```
70
71    **This function will return an Error if it encounters an error.<br>**
72    The recommended way to use it is the following:
73    ```
74    use rs_audio::*;
75    use rs_audio::{player::Song};
76
77    match Song::play_wav("test.wav") {
78        Ok(_) => (),
79        Err(e) => {
80            eprintln!("Error: {e}");
81            std::process::exit(1);
82        }
83    }
84    ```
85    */
86    pub fn play_wav(file_path: &str) -> Result<(), Error> {
87        let (_stream, handle) = match OutputStream::try_default() {
88            Ok(e) => e,
89            Err(e) => return Err(Error::other(e.to_string())),
90        };
91
92        let sink = match Sink::try_new(&handle) {
93            Ok(e) => e,
94
95            /* convert PlayError to std::io::Error */
96            Err(e) => return Err(Error::other(e.to_string())),
97        };
98
99        let file = File::open(file_path)?;
100
101        let source = match Decoder::new(BufReader::new(file)) {
102            Ok(e) => e,
103            Err(e) => return Err(Error::other(e.to_string())),
104        };
105
106        sink.append(source);
107        sink.sleep_until_end(); // blocks thread until finished
108
109        Ok(())
110    }
111}