use cpal::traits::{DeviceTrait, HostTrait}; use cpal::{Device, Host}; use minimp3::{Decoder, Error}; use regex::Regex; use std::fs::File; use std::io::Cursor; use std::io::Read; use std::sync::mpsc::{self, Sender}; use std::thread; pub enum Command { Play(String), Stop, } fn list_devices(host: &Host) -> Vec { let devices = host.output_devices().unwrap(); devices.map(|device| device.name().unwrap()).collect() } fn find_device_index_regex(devices: Vec, pattern: &str) -> Option { let re = Regex::new(pattern).unwrap(); devices.iter().position(|device| re.is_match(device)) } fn select_device(host: &Host, i: usize) -> Option { host.output_devices().unwrap().nth(i) } fn find_virtual_audio_cable() -> Option { let host = cpal::default_host(); let devices = list_devices(&host); println!("Devices: {:?}", devices); let pattern = ".*VB-Audio Virtual Cable.*"; if let Some(index) = find_device_index_regex(devices, pattern) { println!("Selecting Device: \"{}\" at index {:?}", pattern, index); return select_device(&host, index); } return None; } pub fn audio_thread() -> Sender { let (tx, rx) = mpsc::channel(); thread::spawn(move || { let device = find_virtual_audio_cable().unwrap(); // Create a rodio Sink connected to our device let (_stream, stream_handle) = rodio::OutputStream::try_from_device(&device).unwrap(); let sink = rodio::Sink::try_new(&stream_handle).unwrap(); for command in rx { match command { Command::Play(file_name) => { play_file(&sink, file_name); } Command::Stop => { sink.stop(); } } } }); tx } fn play_file(sink: &rodio::Sink, file_name: String) { let mut mp3_file = File::open(file_name).unwrap(); let mut mp3_data = Vec::new(); mp3_file.read_to_end(&mut mp3_data).unwrap(); // Iterate over the MP3 frames and play them let mut decoder = Decoder::new(Cursor::new(mp3_data)); loop { match decoder.next_frame() { Ok(frame) => { let source = rodio::buffer::SamplesBuffer::new( 2, frame.sample_rate.try_into().unwrap(), &*frame.data, ); sink.append(source); } Err(Error::Eof) => { println!("EOF"); break; } Err(e) => panic!("{:?}", e), } } }