mirror of
https://github.com/csd4ni3l/soundboard.git
synced 2026-03-10 17:19:24 +01:00
Code formatted by Zed automatically, added black fill color for
directory button that is currently selected, removed the current directory text
This commit is contained in:
105
src/main.rs
105
src/main.rs
@@ -1,15 +1,12 @@
|
|||||||
use bevy::{
|
use bevy::{log::Level, prelude::*};
|
||||||
log::Level,
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::{collections::HashMap, fs::File, io::BufReader, path::Path};
|
use std::{collections::HashMap, fs::File, io::BufReader, path::Path};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use bevy_egui::{
|
use bevy_egui::{EguiContextSettings, EguiContexts, EguiPrimaryContextPass, EguiStartupSet, egui};
|
||||||
EguiContextSettings, EguiContexts, EguiPrimaryContextPass, EguiStartupSet, egui,
|
|
||||||
};
|
use egui::ecolor::Color32;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod linux_lib;
|
mod linux_lib;
|
||||||
@@ -17,7 +14,10 @@ mod linux_lib;
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
mod windows_lib;
|
mod windows_lib;
|
||||||
|
|
||||||
use rodio::{Decoder, OutputStream, OutputStreamBuilder, Sink, Source, cpal::{self, traits::HostTrait}};
|
use rodio::{
|
||||||
|
Decoder, OutputStream, OutputStreamBuilder, Sink, Source,
|
||||||
|
cpal::{self, traits::HostTrait},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct JSONData {
|
struct JSONData {
|
||||||
@@ -35,7 +35,7 @@ struct PlayingSound {
|
|||||||
struct SoundSystem {
|
struct SoundSystem {
|
||||||
virtual_mic_stream: OutputStream,
|
virtual_mic_stream: OutputStream,
|
||||||
// normal_output_stream: OutputStream,
|
// normal_output_stream: OutputStream,
|
||||||
paused: bool
|
paused: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
@@ -47,7 +47,7 @@ struct AppState {
|
|||||||
sound_system: SoundSystem,
|
sound_system: SoundSystem,
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALLOWED_FILE_EXTENSIONS: [&str; 4] = ["mp3", "wav", "flac", "ogg"];
|
const ALLOWED_FILE_EXTENSIONS: [&str; 4] = ["mp3", "wav", "flac", "ogg"];
|
||||||
@@ -59,15 +59,22 @@ fn create_virtual_mic() -> OutputStream {
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
return linux_lib::create_virtual_mic_linux();
|
return linux_lib::create_virtual_mic_linux();
|
||||||
|
|
||||||
#[allow(unreachable_code)] {
|
#[allow(unreachable_code)]
|
||||||
println!("Unknown/unsupported OS. Audio support may not work or may route to default output (headset, headphones, etc).");
|
{
|
||||||
|
println!(
|
||||||
|
"Unknown/unsupported OS. Audio support may not work or may route to default output (headset, headphones, etc)."
|
||||||
|
);
|
||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
let virtual_mic = host.default_output_device().expect("Could not get default output device");
|
let virtual_mic = host
|
||||||
return OutputStreamBuilder::from_device(virtual_mic).expect("Unable to open default audio device").open_stream().expect("Failed to open stream")
|
.default_output_device()
|
||||||
|
.expect("Could not get default output device");
|
||||||
|
return OutputStreamBuilder::from_device(virtual_mic)
|
||||||
|
.expect("Unable to open default audio device")
|
||||||
|
.open_stream()
|
||||||
|
.expect("Failed to open stream");
|
||||||
// normal_output = host.default_output_device().expect("Could not get default output device");
|
// 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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reload_sound() -> OutputStream {
|
fn reload_sound() -> OutputStream {
|
||||||
@@ -77,7 +84,7 @@ fn reload_sound() -> OutputStream {
|
|||||||
return create_virtual_mic();
|
return create_virtual_mic();
|
||||||
}
|
}
|
||||||
|
|
||||||
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 in apps".to_string(), String::from("9999999"))]);
|
||||||
|
|
||||||
@@ -119,11 +126,11 @@ fn main() {
|
|||||||
sound_system: SoundSystem {
|
sound_system: SoundSystem {
|
||||||
virtual_mic_stream,
|
virtual_mic_stream,
|
||||||
// normal_output_stream,
|
// normal_output_stream,
|
||||||
paused: false
|
paused: false,
|
||||||
},
|
},
|
||||||
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"),
|
||||||
})
|
})
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PreStartup,
|
PreStartup,
|
||||||
@@ -179,7 +186,15 @@ fn load_data(app_state: &mut AppState) {
|
|||||||
.filter_map(|entry| {
|
.filter_map(|entry| {
|
||||||
entry.ok().and_then(|e| {
|
entry.ok().and_then(|e| {
|
||||||
let path = e.path();
|
let path = e.path();
|
||||||
if path.is_file() && ALLOWED_FILE_EXTENSIONS.contains(&path.extension().unwrap_or_default().to_str().expect("Could not convert extension to string")) {
|
if path.is_file()
|
||||||
|
&& ALLOWED_FILE_EXTENSIONS.contains(
|
||||||
|
&path
|
||||||
|
.extension()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_str()
|
||||||
|
.expect("Could not convert extension to string"),
|
||||||
|
)
|
||||||
|
{
|
||||||
path.to_str().map(|s| s.to_string())
|
path.to_str().map(|s| s.to_string())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -197,9 +212,7 @@ fn setup_camera_system(mut commands: Commands) {
|
|||||||
commands.spawn(Camera2d);
|
commands.spawn(Camera2d);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_ui_scale_factor_system(
|
fn update_ui_scale_factor_system(egui_context: Single<(&mut EguiContextSettings, &Camera)>) {
|
||||||
egui_context: Single<(&mut EguiContextSettings, &Camera)>,
|
|
||||||
) {
|
|
||||||
let (mut egui_settings, camera) = egui_context.into_inner();
|
let (mut egui_settings, camera) = egui_context.into_inner();
|
||||||
egui_settings.scale_factor = 1.5 / camera.target_scaling_factor().unwrap_or(1.5);
|
egui_settings.scale_factor = 1.5 / camera.target_scaling_factor().unwrap_or(1.5);
|
||||||
}
|
}
|
||||||
@@ -208,7 +221,10 @@ fn play_sound(file_path: String, app_state: &mut AppState) {
|
|||||||
let virtual_file = File::open(&file_path).unwrap();
|
let virtual_file = File::open(&file_path).unwrap();
|
||||||
let virtual_src = Decoder::new(BufReader::new(virtual_file)).unwrap();
|
let virtual_src = Decoder::new(BufReader::new(virtual_file)).unwrap();
|
||||||
let virtual_sink = Sink::connect_new(&app_state.sound_system.virtual_mic_stream.mixer());
|
let virtual_sink = Sink::connect_new(&app_state.sound_system.virtual_mic_stream.mixer());
|
||||||
let length = virtual_src.total_duration().expect("Could not get source duration").as_secs_f32();
|
let length = virtual_src
|
||||||
|
.total_duration()
|
||||||
|
.expect("Could not get source duration")
|
||||||
|
.as_secs_f32();
|
||||||
virtual_sink.append(virtual_src);
|
virtual_sink.append(virtual_src);
|
||||||
virtual_sink.play();
|
virtual_sink.play();
|
||||||
|
|
||||||
@@ -244,7 +260,8 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
|
|
||||||
let mut mic_name = "Select inside apps".to_string();
|
let mut mic_name = "Select inside apps".to_string();
|
||||||
|
|
||||||
#[cfg(target_os = "linux")] {
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
let output_index = app_state.virt_output_index.clone();
|
let output_index = app_state.virt_output_index.clone();
|
||||||
let output_device = linux_lib::get_device_by_index("source-outputs", output_index);
|
let output_device = linux_lib::get_device_by_index("source-outputs", output_index);
|
||||||
if let Some(app_name) = output_device["properties"]["application.name"].as_str() {
|
if let Some(app_name) = output_device["properties"]["application.name"].as_str() {
|
||||||
@@ -260,7 +277,11 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
.height(available_height / 15.0)
|
.height(available_height / 15.0)
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
for output in &outputs {
|
for output in &outputs {
|
||||||
ui.selectable_value(&mut app_state.virt_output_index_switch, output.1.clone(), output.0.clone());
|
ui.selectable_value(
|
||||||
|
&mut app_state.virt_output_index_switch,
|
||||||
|
output.1.clone(),
|
||||||
|
output.0.clone(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -327,14 +348,18 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if app_state.sound_system.paused {
|
if app_state.sound_system.paused {
|
||||||
ui.heading("Paused");
|
ui.heading("Paused");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
ui.heading("Playing");
|
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!("{} - {:.2} / {:.2}", playing_sound.file_path, playing_sound.virtual_sink.get_pos().as_secs_f32(), playing_sound.length));
|
ui.label(format!(
|
||||||
|
"{} - {:.2} / {:.2}",
|
||||||
|
playing_sound.file_path,
|
||||||
|
playing_sound.virtual_sink.get_pos().as_secs_f32(),
|
||||||
|
playing_sound.length
|
||||||
|
));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -342,15 +367,22 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
let available_height = ui.available_height();
|
let available_height = ui.available_height();
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let available_width = ui.available_width();
|
let available_width = ui.available_width();
|
||||||
let current_directories = app_state.loaded_files.keys().cloned().collect::<Vec<_>>();
|
let current_directories = app_state.loaded_files.keys().cloned().collect::<Vec<_>>();
|
||||||
for directory in current_directories.clone() {
|
for directory in current_directories.clone() {
|
||||||
|
let mut button = egui::Button::new(&directory);
|
||||||
|
if directory == app_state.current_directory {
|
||||||
|
button = button.fill(Color32::BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
if ui
|
if ui
|
||||||
.add_sized(
|
.add_sized(
|
||||||
[available_width / current_directories.len() as f32, available_height / 15.0],
|
[
|
||||||
egui::Button::new(&directory),
|
available_width / current_directories.len() as f32,
|
||||||
|
available_height / 15.0,
|
||||||
|
],
|
||||||
|
button,
|
||||||
)
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
@@ -359,10 +391,6 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.add_space(available_height / 50.0);
|
ui.add_space(available_height / 50.0);
|
||||||
ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| {
|
|
||||||
ui.label(egui::RichText::new(format!("The current directory is {}", app_state.current_directory)).font(egui::FontId::proportional(20.0)));
|
|
||||||
});
|
|
||||||
ui.add_space(available_height / 50.0);
|
|
||||||
if app_state.current_directory.chars().count() > 0 {
|
if app_state.current_directory.chars().count() > 0 {
|
||||||
let files = app_state
|
let files = app_state
|
||||||
.loaded_files
|
.loaded_files
|
||||||
@@ -373,10 +401,13 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
for element in files {
|
for element in files {
|
||||||
if let Some(filename) = element.split("/").collect::<Vec<_>>().last() {
|
if let Some(filename) = element.split("/").collect::<Vec<_>>().last() {
|
||||||
if ui.add_sized(
|
if ui
|
||||||
|
.add_sized(
|
||||||
[ui.available_width(), available_height / 15.0],
|
[ui.available_width(), available_height / 15.0],
|
||||||
egui::Button::new(*filename),
|
egui::Button::new(*filename),
|
||||||
).clicked() {
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
let path = Path::new(&app_state.current_directory)
|
let path = Path::new(&app_state.current_directory)
|
||||||
.join(filename)
|
.join(filename)
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
|
|||||||
Reference in New Issue
Block a user