92 lines
2.7 KiB
Rust
92 lines
2.7 KiB
Rust
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<String> {
|
|
let devices = host.output_devices().unwrap();
|
|
devices.map(|device| device.name().unwrap()).collect()
|
|
}
|
|
|
|
fn find_device_index_regex(devices: Vec<String>, pattern: &str) -> Option<usize> {
|
|
let re = Regex::new(pattern).unwrap();
|
|
devices.iter().position(|device| re.is_match(device))
|
|
}
|
|
|
|
fn select_device(host: &Host, i: usize) -> Option<Device> {
|
|
host.output_devices().unwrap().nth(i)
|
|
}
|
|
|
|
fn find_virtual_audio_cable() -> Option<Device> {
|
|
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<Command> {
|
|
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),
|
|
}
|
|
}
|
|
}
|