soundboard/src/vbplay.rs
2023-07-17 22:43:55 +02:00

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),
}
}
}