diff --git a/Cargo.lock b/Cargo.lock index 8562c16..38a180f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -369,6 +369,30 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ffe3a660c3a1b10e96f304a9413d673b2118d62e4520f7ddf4a4faccfe8b9b9" +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + [[package]] name = "generic-array" version = "0.14.7" @@ -386,8 +410,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -550,6 +576,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.22" @@ -597,6 +633,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + [[package]] name = "ndk" version = "0.8.0" @@ -924,6 +969,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.206" @@ -999,6 +1050,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "strsim" version = "0.8.0" @@ -1010,6 +1070,7 @@ name = "sweetmusic" version = "0.1.0" dependencies = [ "file-format", + "flume", "metadata", "rand", "regex", diff --git a/Cargo.toml b/Cargo.toml index 4028ddb..1251383 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] file-format = "0.25.0" +flume = "0.11.0" metadata = "0.1.9" rand = "0.8.5" regex = "1.10.6" diff --git a/src/audioplayer.rs b/src/audioplayer.rs index 49103f6..51393d4 100644 --- a/src/audioplayer.rs +++ b/src/audioplayer.rs @@ -43,6 +43,7 @@ pub mod audioplayer { ///If true, the player will loop back to the beginning of the playlist when it's done pub repeating: bool, + } //-----------------------------------Enums diff --git a/src/audiowrapper.rs b/src/audiowrapper.rs new file mode 100644 index 0000000..4a2262c --- /dev/null +++ b/src/audiowrapper.rs @@ -0,0 +1,125 @@ +pub mod audiowrapper { + + use std::thread::*; + use std::error::Error; + use std::time::Duration; + use std::io::BufReader; + use std::fs::File; + use std::any::Any; + + use flume::{Receiver, Sender}; + use rodio::{Decoder, OutputStream, Sink, Source}; + use std::path::{Path, PathBuf}; + + use crate::playlist::playlist::*; + use crate::songmeta::songmeta::*; + + + //A wrapper around a rodio sink that allows for better controls. + //Can't use sink queues so I implement myself... + //Reads songs in threads, one at a time so we can stop, play, pause, insert songs in playlist, etc. + #[derive(Debug)] + pub struct AudioWrapper { + tx: Sender, + rx: Receiver, + pub playlist: Playlist, + handle: Option>, + } + + //Used for handling communication with the thread + enum ThreadCom { + Stop, + Pause, + Unpause, + SendPos, + Pos(Duration), + SetVol(f32), + GetVol, + } + + impl AudioWrapper { + pub fn new() -> Self { + let (tx, rx) = flume::unbounded(); + Self { + tx: tx, + rx: rx, + playlist: Playlist::new(), + handle: None, + } + } + + //Inner function used in a thread to read music + fn sub_sink(path: String, tx: Sender, rx: Receiver) { + let path = Path::new(&path); + let (stream, stream_handle) = OutputStream::try_default().unwrap(); + let sink = Sink::try_new(&stream_handle).unwrap(); + let file = BufReader::new(File::open(&path).unwrap()); + let source = Decoder::new(file).unwrap(); + sink.append(source); + + loop { + + let recv = rx.try_recv(); + + match recv { + Ok(v) => { + match v { + ThreadCom::Stop => { + sink.stop(); + break; + }, + ThreadCom::Pause => { + sink.pause(); + }, + ThreadCom::Unpause => { + sink.play(); + }, + ThreadCom::SendPos => { + tx.try_send(ThreadCom::Pos(sink.get_pos())); + }, + ThreadCom::SetVol(w) => { + sink.set_volume(w); + }, + ThreadCom::GetVol => { + tx.try_send(ThreadCom::SetVol(sink.volume())); + }, + ThreadCom::Pos(w) => { + let _ = sink.try_seek(w); + } + }; + }, + Err(_) => (), + }; + + //Finished + if sink.len() < 1 { + break; + } + } + } + + ///Reads ONE song by starting a sink in a new thread. + ///Stop any previous song playing. + pub fn play_at(&mut self, index: usize){ + self.playlist.check_index(index.clone()).unwrap(); + self.stop(); + let path = self.playlist.songs[index].path.clone(); + let tx2 = self.tx.clone(); + let rx2 = self.rx.clone(); + let handle = spawn(|| { + AudioWrapper::sub_sink(path, tx2, rx2); + }); + self.handle = Some(handle); + } + + pub fn stop(&mut self) { + match &self.handle { + Some(h) => { + self.tx.try_send(ThreadCom::Stop).unwrap(); + self.handle.take().expect("Called stop on non-running thread").join().expect("Could not join"); + }, + None => (), + }; + } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index d47dd13..f04462a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,48 +7,22 @@ use crate::playlist::playlist::*; pub mod audioplayer; use crate::audioplayer::audioplayer::*; +pub mod audiowrapper; +use crate::audiowrapper::audiowrapper::*; + use std::thread::sleep; use std::time::Duration; fn main() { - let mysong = SongMeta::frompath(&String::from("/home/justine/Music/one.mp3")).unwrap(); - let mysong2 = SongMeta::frompath(&String::from("/home/justine/Music/two.mp3")).unwrap(); - let mysong3 = SongMeta::frompath(&String::from("/home/justine/Music/three.mp3")).unwrap(); - - - let mut playlist = Playlist::new(); - playlist.songs.push(mysong); - playlist.songs.push(mysong2); - playlist.songs.push(mysong3); - - let mut player = AudioPlayer::new().unwrap(); - - - player.playlist = playlist; - player.play_at(0); - dbg!(player.get_state()); - sleep(Duration::from_secs(3)); - - dbg!("SKIPPING FORW TWICE"); - player.skip_forward(); - player.skip_forward(); - sleep(Duration::from_secs(3)); - - dbg!("BACKW 5"); - player.skip_backwards(); - player.skip_backwards(); - player.skip_backwards(); - player.skip_backwards(); - player.skip_backwards(); - - player.unpause(); - player.set_vol(0.9); - - loop { - println!("{:?} - {:?}", player.get_state(), player.get_vol()); - sleep(Duration::from_secs(1)); - } - + let mut wrap = AudioWrapper::new(); + let mut pl = Playlist::new(); + pl.add_song_from_path(&String::from("/home/justine/Music/1.mp3")).unwrap(); + wrap.playlist = pl; + wrap.play_at(0); + sleep(Duration::from_secs(5)); + wrap.stop(); + sleep(Duration::from_secs(5)); + dbg!(wrap); }