rs_audio/legacyplayer.rs
1use crate::assets::loader::load_asset;
2use crate::note::Note;
3use crate::waveform::WaveForm;
4use crate::BPMChoice;
5use rodio::{source::SineWave, OutputStream, Sink, Source};
6use std::{io::Error, time::Duration};
7
8/**
9# Deprecated
10This feature has been deprecated in the latest update. Please use the new player, not the legacy one.<br><br>
11
12Basic songs are collections of Notes. Each song can export to a .wav file.<br>
13
14Example:
15```
16use rs_audio::*;
17
18let mut song = BasicSong::default();
19song.play().unwrap();
20
21let mut second_song = BasicSong::new(vec![
22Note { freq: 880.0, dur: 1.0, vol: 0.20, wave: WaveForm::Sine },
23Note { freq: 220.0, dur: 1.0, vol: 0.20, wave: WaveForm::Square },
24Note { freq: 880.0, dur: 1.0, vol: 0.20, wave: WaveForm::Sine },
25Note { freq: 220.0, dur: 1.0, vol: 0.20, wave: WaveForm::Triangle },
26], BPMChoice::Default);
27
28second_song.play().unwrap(); // Uses the main thread.
29second_song.export_to_wav("test.wav".to_string());
30```
31*/
32pub struct BasicSong {
33 pub notes: Vec<Note>,
34
35 pub bpm: BPMChoice, // beats per minute
36}
37
38impl Default for BasicSong {
39 /**
40 # Deprecated
41 This feature has been deprecated in the latest update. Please use the new player, not the legacy one.<br><br>
42
43
44 Creates a default song that is useful for debugging purposes.<br><br>
45 It contains a single default sine wave.<br>
46 Usage:
47 ```
48 use rs_audio::*;
49 let song = BasicSong::default();
50 ```
51 */
52 fn default() -> Self {
53 BasicSong {
54 notes: vec![Note::default()],
55 bpm: BPMChoice::Default,
56 }
57 }
58}
59
60impl BasicSong {
61 pub fn new(notes: Vec<Note>, bpm: BPMChoice) -> Self {
62 BasicSong { notes, bpm }
63 }
64
65 pub fn play(&mut self) -> Result<(), Error> {
66 let mut volume_warning_given: bool = false;
67 // if the volume warning has been given (this is for volume warnings with sine waves)
68
69 // creates stream and sink (audio mixer)
70 let (_stream, handle) = match OutputStream::try_default() {
71 Ok(e) => e,
72 Err(e) => return Err(Error::other(e.to_string())),
73 };
74
75 let sink = match Sink::try_new(&handle) {
76 Ok(e) => e,
77 Err(e) => return Err(Error::other(e.to_string())),
78 };
79
80 // iterates over the notes
81 for note in &mut self.notes {
82 if !volume_warning_given && note.vol > 0.20 && note.wave == WaveForm::Sine {
83 // issue a warning
84
85 /* loads the warning ascii art */
86 println!("{}", load_asset("warning_ascii.txt"));
87
88 /* loads the volume warning text */
89 println!("{}", load_asset("warning_volume.txt"));
90
91 let mut input = String::new();
92
93 // wait for input
94 std::io::stdin()
95 .read_line(&mut input)
96 .expect("RS-AUDIO: Could not read input!");
97
98 match input.trim() {
99 // handle options
100 n if n.starts_with("c") => {
101 volume_warning_given = true;
102 } // do nothing
103
104 n if n.starts_with("a") => {
105 // abort
106 println!("RS-AUDIO: Exiting...");
107 std::process::exit(0);
108 // no need to change volume_warning_given because we exited
109 }
110
111 n if n.starts_with("d") || n.is_empty() => {
112 /*
113 use the default value
114
115 this is either "d" or just an empty string (achieved by pressing enter and trimming)
116 */
117 note.vol = 0.20;
118 volume_warning_given = true;
119 }
120
121 _ => {
122 eprintln!("RS-AUDIO: Input is invalid\nRS-AUDIO: Exiting...");
123 std::process::exit(1);
124 // no need to change volume_warning_given because we exitted
125 }
126 }
127 }
128 let converted = match note.wave {
129 WaveForm::Sine => SineWave::new(note.freq as f32),
130 _ => note.to_approx_sine(),
131 }
132 .take_duration(Duration::from_secs_f64(note.dur))
133 .amplify(note.vol);
134
135 sink.append(converted);
136 }
137
138 sink.sleep_until_end();
139 Ok(())
140 }
141}