mirror of
https://github.com/csd4ni3l/soundboard.git
synced 2026-03-10 17:19:24 +01:00
Format the code, improve vb cable warning for windows, Update available apps to output to automatically, merge the update functions, Use a label when no apps are available or unsupported. Add a stop all and pause all button, exclude apps that are not useful.
This commit is contained in:
110
src/linux_lib.rs
110
src/linux_lib.rs
@@ -1,7 +1,11 @@
|
|||||||
|
use rodio::{
|
||||||
use std::process::Command;
|
OutputStream, OutputStreamBuilder,
|
||||||
|
cpal::{self, traits::HostTrait},
|
||||||
|
};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use rodio::{OutputStream, OutputStreamBuilder, cpal::{self, traits::HostTrait}};
|
use std::process::Command;
|
||||||
|
|
||||||
|
const APPS_TO_EXCLUDE: [&str; 1] = ["plasmashell"];
|
||||||
|
|
||||||
fn pactl_list(sink_type: &str) -> Value {
|
fn pactl_list(sink_type: &str) -> Value {
|
||||||
let command_output = Command::new("pactl")
|
let command_output = Command::new("pactl")
|
||||||
@@ -10,10 +14,12 @@ fn pactl_list(sink_type: &str) -> Value {
|
|||||||
.expect("Failed to execute process");
|
.expect("Failed to execute process");
|
||||||
|
|
||||||
if command_output.status.success() {
|
if command_output.status.success() {
|
||||||
serde_json::from_str(str::from_utf8(&command_output.stdout).expect("Failed to convert to string")).expect("Failed to parse sink JSON output")
|
serde_json::from_str(
|
||||||
}
|
str::from_utf8(&command_output.stdout).expect("Failed to convert to string"),
|
||||||
else {
|
)
|
||||||
Value::Null{}
|
.expect("Failed to parse sink JSON output")
|
||||||
|
} else {
|
||||||
|
Value::Null {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,20 +27,26 @@ pub fn get_sink_by_index(sink_type: &str, index: String) -> Value {
|
|||||||
let sinks = pactl_list(sink_type);
|
let sinks = pactl_list(sink_type);
|
||||||
|
|
||||||
for sink in sinks.as_array().unwrap_or(&vec![]) {
|
for sink in sinks.as_array().unwrap_or(&vec![]) {
|
||||||
if sink["index"].as_u64().expect("sink index is not a number").to_string() == index {
|
if sink["index"]
|
||||||
|
.as_u64()
|
||||||
|
.expect("sink index is not a number")
|
||||||
|
.to_string()
|
||||||
|
== index
|
||||||
|
{
|
||||||
return sink.clone();
|
return sink.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Value::Null{};
|
return Value::Null {};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_soundboard_sinks() -> Vec<Value> {
|
fn find_soundboard_sinks() -> Vec<Value> {
|
||||||
let sink_inputs = pactl_list("sink-inputs");
|
let sink_inputs = pactl_list("sink-inputs");
|
||||||
sink_inputs.as_array()
|
sink_inputs
|
||||||
|
.as_array()
|
||||||
.unwrap_or(&vec![])
|
.unwrap_or(&vec![])
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|sink| {sink["properties"]["node.name"] == "alsa_playback.soundboard"})
|
.filter(|sink| sink["properties"]["node.name"] == "alsa_playback.soundboard")
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -42,7 +54,10 @@ fn find_soundboard_sinks() -> Vec<Value> {
|
|||||||
pub fn move_playback_to_sink() {
|
pub fn move_playback_to_sink() {
|
||||||
let soundboard_sinks = find_soundboard_sinks();
|
let soundboard_sinks = find_soundboard_sinks();
|
||||||
for sink in soundboard_sinks {
|
for sink in soundboard_sinks {
|
||||||
let index = sink["index"].as_u64().expect("sink index is not a number").to_string();
|
let index = sink["index"]
|
||||||
|
.as_u64()
|
||||||
|
.expect("sink index is not a number")
|
||||||
|
.to_string();
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["move-sink-input", index.as_str(), "SoundboardSink"]) // as_str is needed here as you cannot instantly dereference a growing String (Rust...)
|
.args(&["move-sink-input", index.as_str(), "SoundboardSink"]) // as_str is needed here as you cannot instantly dereference a growing String (Rust...)
|
||||||
.output()
|
.output()
|
||||||
@@ -52,12 +67,25 @@ pub fn move_playback_to_sink() {
|
|||||||
|
|
||||||
pub fn list_outputs() -> Vec<(String, String)> {
|
pub fn list_outputs() -> Vec<(String, String)> {
|
||||||
let source_outputs = pactl_list("source-outputs");
|
let source_outputs = pactl_list("source-outputs");
|
||||||
return source_outputs.as_array().unwrap_or(&vec![]).iter().filter_map(|sink| {
|
return source_outputs
|
||||||
|
.as_array()
|
||||||
|
.unwrap_or(&vec![])
|
||||||
|
.iter()
|
||||||
|
.filter_map(|sink| {
|
||||||
let app_name = sink["properties"]["application.name"].as_str()?;
|
let app_name = sink["properties"]["application.name"].as_str()?;
|
||||||
let binary = sink["properties"]["application.process.binary"].as_str().unwrap_or("Unknown");
|
let binary = sink["properties"]["application.process.binary"]
|
||||||
let index = sink["index"].as_u64().expect("sink index is not a number").to_string();
|
.as_str()
|
||||||
|
.unwrap_or("Unknown");
|
||||||
|
if APPS_TO_EXCLUDE.contains(&binary) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let index = sink["index"]
|
||||||
|
.as_u64()
|
||||||
|
.expect("sink index is not a number")
|
||||||
|
.to_string();
|
||||||
Some((format!("{} ({})", app_name, binary), index))
|
Some((format!("{} ({})", app_name, binary), index))
|
||||||
}).collect();
|
})
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_index_to_virtualmic(index: String) {
|
pub fn move_index_to_virtualmic(index: String) {
|
||||||
@@ -69,35 +97,69 @@ pub fn move_index_to_virtualmic(index: String) {
|
|||||||
|
|
||||||
pub fn create_virtual_mic_linux() -> OutputStream {
|
pub fn create_virtual_mic_linux() -> OutputStream {
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["load-module", "module-null-sink", "sink_name=SoundboardSink", "sink_properties=device.description=\"Soundboard_Audio\""])
|
.args(&[
|
||||||
|
"load-module",
|
||||||
|
"module-null-sink",
|
||||||
|
"sink_name=SoundboardSink",
|
||||||
|
"sink_properties=device.description=\"Soundboard_Audio\"",
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create SoundboardSink");
|
.expect("Failed to create SoundboardSink");
|
||||||
|
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["load-module", "module-null-sink", "sink_name=VirtualMic", "sink_properties=device.description=\"Virtual_Microphone\""])
|
.args(&[
|
||||||
|
"load-module",
|
||||||
|
"module-null-sink",
|
||||||
|
"sink_name=VirtualMic",
|
||||||
|
"sink_properties=device.description=\"Virtual_Microphone\"",
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create VirtualMic");
|
.expect("Failed to create VirtualMic");
|
||||||
|
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["load-module", "module-remap-source", "master=VirtualMic.monitor", "source_name=VirtualMicSource", "source_properties=device.description=\"Virtual_Mic_Source\""])
|
.args(&[
|
||||||
|
"load-module",
|
||||||
|
"module-remap-source",
|
||||||
|
"master=VirtualMic.monitor",
|
||||||
|
"source_name=VirtualMicSource",
|
||||||
|
"source_properties=device.description=\"Virtual_Mic_Source\"",
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create VirtualMicSource");
|
.expect("Failed to create VirtualMicSource");
|
||||||
|
|
||||||
// Soundboard audio -> speakers
|
// Soundboard audio -> speakers
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["load-module", "module-loopback", "source=SoundboardSink.monitor", "sink=@DEFAULT_SINK@", "latency_msec=1"])
|
.args(&[
|
||||||
|
"load-module",
|
||||||
|
"module-loopback",
|
||||||
|
"source=SoundboardSink.monitor",
|
||||||
|
"sink=@DEFAULT_SINK@",
|
||||||
|
"latency_msec=1",
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create soundboard to speakers loopback");
|
.expect("Failed to create soundboard to speakers loopback");
|
||||||
|
|
||||||
// Soundboard audio -> VirtualMic
|
// Soundboard audio -> VirtualMic
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["load-module", "module-loopback", "source=SoundboardSink.monitor", "sink=VirtualMic", "latency_msec=1"])
|
.args(&[
|
||||||
|
"load-module",
|
||||||
|
"module-loopback",
|
||||||
|
"source=SoundboardSink.monitor",
|
||||||
|
"sink=VirtualMic",
|
||||||
|
"latency_msec=1",
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create soundboard to VirtualMic loopback");
|
.expect("Failed to create soundboard to VirtualMic loopback");
|
||||||
|
|
||||||
// Microphone -> VirtualMic ONLY
|
// Microphone -> VirtualMic ONLY
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["load-module", "module-loopback", "source=@DEFAULT_SOURCE@", "sink=VirtualMic", "latency_msec=1"])
|
.args(&[
|
||||||
|
"load-module",
|
||||||
|
"module-loopback",
|
||||||
|
"source=@DEFAULT_SOURCE@",
|
||||||
|
"sink=VirtualMic",
|
||||||
|
"latency_msec=1",
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create microphone loopback");
|
.expect("Failed to create microphone loopback");
|
||||||
|
|
||||||
@@ -112,7 +174,9 @@ pub fn create_virtual_mic_linux() -> OutputStream {
|
|||||||
.expect("Failed to set soundboard volume");
|
.expect("Failed to set soundboard volume");
|
||||||
|
|
||||||
let host = cpal::host_from_id(cpal::HostId::Alsa).expect("Could not initialize ALSA");
|
let host = cpal::host_from_id(cpal::HostId::Alsa).expect("Could not initialize ALSA");
|
||||||
let device = host.default_output_device().expect("Could not get default output device");
|
let device = host
|
||||||
|
.default_output_device()
|
||||||
|
.expect("Could not get default output device");
|
||||||
|
|
||||||
let stream = OutputStreamBuilder::from_device(device)
|
let stream = OutputStreamBuilder::from_device(device)
|
||||||
.expect("Unable to open VirtualMic")
|
.expect("Unable to open VirtualMic")
|
||||||
|
|||||||
100
src/main.rs
100
src/main.rs
@@ -1,6 +1,6 @@
|
|||||||
use bevy::{log::Level, prelude::*};
|
use bevy::{log::Level, prelude::*};
|
||||||
|
|
||||||
use std::{collections::HashMap, fs::File, io::BufReader, path::Path};
|
use std::{collections::HashMap, fs::File, io::BufReader, path::Path, time::Instant};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -15,7 +15,8 @@ mod linux_lib;
|
|||||||
mod windows_lib;
|
mod windows_lib;
|
||||||
|
|
||||||
use rodio::{
|
use rodio::{
|
||||||
Decoder, OutputStream, Sink, Source, cpal::{self, traits::HostTrait}, OutputStreamBuilder
|
Decoder, OutputStream, OutputStreamBuilder, Sink, Source,
|
||||||
|
cpal::{self, traits::HostTrait},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@@ -29,7 +30,7 @@ struct PlayingSound {
|
|||||||
length: f32,
|
length: f32,
|
||||||
sink: Sink,
|
sink: Sink,
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
normal_sink: Sink
|
normal_sink: Sink,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SoundSystem {
|
struct SoundSystem {
|
||||||
@@ -49,6 +50,7 @@ struct AppState {
|
|||||||
virt_outputs: Vec<(String, String)>,
|
virt_outputs: Vec<(String, String)>,
|
||||||
virt_output_index_switch: String,
|
virt_output_index_switch: String,
|
||||||
virt_output_index: String,
|
virt_output_index: String,
|
||||||
|
last_virt_output_update: Instant
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALLOWED_FILE_EXTENSIONS: [&str; 4] = ["mp3", "wav", "flac", "ogg"];
|
const ALLOWED_FILE_EXTENSIONS: [&str; 4] = ["mp3", "wav", "flac", "ogg"];
|
||||||
@@ -75,7 +77,9 @@ fn create_virtual_mic() -> SoundSystem {
|
|||||||
#[allow(unreachable_code)]
|
#[allow(unreachable_code)]
|
||||||
{
|
{
|
||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
let device = host.default_output_device().expect("Could not get default output device");
|
let device = host
|
||||||
|
.default_output_device()
|
||||||
|
.expect("Could not get default output device");
|
||||||
SoundSystem {
|
SoundSystem {
|
||||||
output_stream: OutputStreamBuilder::from_device(device)
|
output_stream: OutputStreamBuilder::from_device(device)
|
||||||
.expect("Unable to open device")
|
.expect("Unable to open device")
|
||||||
@@ -101,7 +105,7 @@ fn reload_sound() -> SoundSystem {
|
|||||||
|
|
||||||
fn list_outputs() -> Vec<(String, String)> {
|
fn list_outputs() -> Vec<(String, String)> {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
return Vec::from([("Select in apps".to_string(), String::from("9999999"))]);
|
return Vec::from([("Select inside apps".to_string(), String::from("9999999"))]);
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
return linux_lib::list_outputs();
|
return linux_lib::list_outputs();
|
||||||
@@ -139,6 +143,7 @@ fn main() {
|
|||||||
virt_outputs: Vec::new(),
|
virt_outputs: Vec::new(),
|
||||||
virt_output_index_switch: String::from("0"),
|
virt_output_index_switch: String::from("0"),
|
||||||
virt_output_index: String::from("999"),
|
virt_output_index: String::from("999"),
|
||||||
|
last_virt_output_update: Instant::now()
|
||||||
})
|
})
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PreStartup,
|
PreStartup,
|
||||||
@@ -147,16 +152,25 @@ fn main() {
|
|||||||
.add_systems(Startup, load_system)
|
.add_systems(Startup, load_system)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
EguiPrimaryContextPass,
|
EguiPrimaryContextPass,
|
||||||
(ui_system, update_ui_scale_factor_system, update_virtualmic),
|
(draw, update_ui_scale_factor_system, update),
|
||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_virtualmic(mut app_state: ResMut<AppState>) {
|
fn update(mut app_state: ResMut<AppState>) {
|
||||||
|
if app_state.last_virt_output_update.elapsed().as_secs_f32() >= 3.0 {
|
||||||
|
app_state.last_virt_output_update = Instant::now();
|
||||||
|
app_state.virt_outputs = list_outputs();
|
||||||
|
}
|
||||||
|
|
||||||
if app_state.virt_outputs.is_empty() {
|
if app_state.virt_outputs.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !(app_state.virt_output_index == "999".to_string()) {
|
||||||
|
app_state.virt_output_index_switch = app_state.virt_outputs[0].1.clone();
|
||||||
|
}
|
||||||
|
|
||||||
if app_state.virt_output_index != app_state.virt_output_index_switch {
|
if app_state.virt_output_index != app_state.virt_output_index_switch {
|
||||||
app_state.virt_output_index = app_state.virt_output_index_switch.clone();
|
app_state.virt_output_index = app_state.virt_output_index_switch.clone();
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
@@ -165,7 +179,6 @@ fn update_virtualmic(mut app_state: ResMut<AppState>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn load_system(mut app_state: ResMut<AppState>) {
|
fn load_system(mut app_state: ResMut<AppState>) {
|
||||||
app_state.virt_outputs = list_outputs();
|
|
||||||
if !app_state.virt_outputs.is_empty() {
|
if !app_state.virt_outputs.is_empty() {
|
||||||
app_state.virt_output_index_switch = app_state.virt_outputs[0].1.clone();
|
app_state.virt_output_index_switch = app_state.virt_outputs[0].1.clone();
|
||||||
}
|
}
|
||||||
@@ -245,17 +258,18 @@ fn play_sound(file_path: String, app_state: &mut AppState) {
|
|||||||
normal_sink: {
|
normal_sink: {
|
||||||
let file2 = File::open(&file_path).unwrap();
|
let file2 = File::open(&file_path).unwrap();
|
||||||
let src2 = Decoder::new(BufReader::new(file2)).unwrap();
|
let src2 = Decoder::new(BufReader::new(file2)).unwrap();
|
||||||
let normal_sink = Sink::connect_new(&app_state.sound_system.normal_output_stream.mixer());
|
let normal_sink =
|
||||||
|
Sink::connect_new(&app_state.sound_system.normal_output_stream.mixer());
|
||||||
normal_sink.append(src2);
|
normal_sink.append(src2);
|
||||||
normal_sink.play();
|
normal_sink.play();
|
||||||
normal_sink
|
normal_sink
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
app_state.currently_playing.push(playing_sound);
|
app_state.currently_playing.push(playing_sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Result {
|
fn draw(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Result {
|
||||||
let ctx = contexts.ctx_mut()?;
|
let ctx = contexts.ctx_mut()?;
|
||||||
|
|
||||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||||
@@ -270,23 +284,13 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
let available_width = ui.available_width();
|
let available_width = ui.available_width();
|
||||||
let available_height = ui.available_height();
|
let available_height = ui.available_height();
|
||||||
let outputs = app_state.virt_outputs.clone();
|
let outputs = app_state.virt_outputs.clone();
|
||||||
|
ui.label("Virtual Mic Output");
|
||||||
#[allow(unused_mut)]
|
if cfg!(target_os = "linux") {
|
||||||
let mut mic_name = "Select inside apps".to_string();
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
|
||||||
let output_index = app_state.virt_output_index.clone();
|
let output_index = app_state.virt_output_index.clone();
|
||||||
let output_sink = linux_lib::get_sink_by_index("source-outputs", output_index);
|
let output_sink = linux_lib::get_sink_by_index("source-outputs", output_index);
|
||||||
if let Some(app_name) = output_sink["properties"]["application.name"].as_str() {
|
if let Some(app_name) = output_sink["properties"]["application.name"].as_str() {
|
||||||
mic_name = app_name.to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.label("Virtual Mic Output");
|
|
||||||
|
|
||||||
egui::ComboBox::from_id_salt("Virtual Mic Output")
|
egui::ComboBox::from_id_salt("Virtual Mic Output")
|
||||||
.selected_text(mic_name)
|
.selected_text(app_name.to_string())
|
||||||
.width(available_width)
|
.width(available_width)
|
||||||
.height(available_height / 15.0)
|
.height(available_height / 15.0)
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
@@ -298,6 +302,14 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ui.add(egui::Button::new("No apps found to use.".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ui.add(egui::Button::new("Unsupported. Select inside apps.".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
if ui
|
if ui
|
||||||
.add_sized(
|
.add_sized(
|
||||||
@@ -358,13 +370,6 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
});
|
});
|
||||||
|
|
||||||
egui::TopBottomPanel::bottom("currently_playing").show(ctx, |ui| {
|
egui::TopBottomPanel::bottom("currently_playing").show(ctx, |ui| {
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if app_state.sound_system.paused {
|
|
||||||
ui.heading("Paused");
|
|
||||||
} else {
|
|
||||||
ui.heading("Playing");
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
for playing_sound in &app_state.currently_playing {
|
for playing_sound in &app_state.currently_playing {
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
@@ -374,8 +379,39 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
playing_sound.length
|
playing_sound.length
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
let available_width = ui.available_width();
|
||||||
|
let available_height = ui.available_height();
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[available_width, available_height / 15.0],
|
||||||
|
egui::Button::new("Stop all"),
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
app_state.currently_playing.clear();
|
||||||
|
}
|
||||||
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[available_width, available_height / 15.0],
|
||||||
|
egui::Button::new(if app_state.sound_system.paused {"Resume"} else {"Pause"}),
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
app_state.sound_system.paused = !app_state.sound_system.paused;
|
||||||
|
|
||||||
|
if app_state.sound_system.paused {
|
||||||
|
for sound in &app_state.currently_playing {
|
||||||
|
sound.sink.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for sound in &app_state.currently_playing {
|
||||||
|
sound.sink.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
|||||||
@@ -1,14 +1,35 @@
|
|||||||
use rodio::{OutputStream, OutputStreamBuilder, cpal::{self, traits::HostTrait, traits::DeviceTrait}};
|
use rodio::{
|
||||||
|
OutputStream, OutputStreamBuilder,
|
||||||
|
cpal::{self, traits::DeviceTrait, traits::HostTrait},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn create_virtual_mic_windows() -> (OutputStream, OutputStream) {
|
pub fn create_virtual_mic_windows() -> (OutputStream, OutputStream) {
|
||||||
let host = cpal::host_from_id(cpal::HostId::Wasapi).expect("Could not initialize audio routing using WasAPI");
|
let host = cpal::host_from_id(cpal::HostId::Wasapi)
|
||||||
let virtual_mic = host.output_devices().expect("Could not list Output devices").find(|device| {
|
.expect("Could not initialize audio routing using WasAPI");
|
||||||
device.name().ok().map(|name|{
|
let virtual_mic = host
|
||||||
name.contains("CABLE Input") || name.contains("VB-Audio")
|
.output_devices()
|
||||||
}).unwrap_or(false)
|
.expect("Could not list Output devices")
|
||||||
}).expect("Could not get default output device");
|
.find(|device| {
|
||||||
|
device
|
||||||
|
.name()
|
||||||
|
.ok()
|
||||||
|
.map(|name| name.contains("CABLE Input") || name.contains("VB-Audio"))
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
.expect("Could not get VB Cable output device. Is VB Cable Driver installed?");
|
||||||
|
|
||||||
let normal_output = host.default_output_device().expect("Could not get default output device");
|
let normal_output = host
|
||||||
|
.default_output_device()
|
||||||
|
.expect("Could not get default output device");
|
||||||
|
|
||||||
return (OutputStreamBuilder::from_device(normal_output).expect("Unable to open default audio device").open_stream().expect("Failed to open stream"), OutputStreamBuilder::from_device(virtual_mic).expect("Unable to open default audio device").open_stream().expect("Failed to open stream"));
|
return (
|
||||||
|
OutputStreamBuilder::from_device(normal_output)
|
||||||
|
.expect("Unable to open default audio device")
|
||||||
|
.open_stream()
|
||||||
|
.expect("Failed to open stream"),
|
||||||
|
OutputStreamBuilder::from_device(virtual_mic)
|
||||||
|
.expect("Unable to open default audio device")
|
||||||
|
.open_stream()
|
||||||
|
.expect("Failed to open stream"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user