You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
5.6 KiB
Rust
179 lines
5.6 KiB
Rust
use bevy::prelude::*;
|
|
use monologue_trees::debug::*;
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins((
|
|
DefaultPlugins.set(WindowPlugin {
|
|
primary_window: Some(Window {
|
|
title: "Audio Inspect".into(),
|
|
resolution: (640., 480.).into(),
|
|
..default()
|
|
}),
|
|
..default()
|
|
}),
|
|
DebugInfoPlugin,
|
|
))
|
|
.add_systems(PreStartup, (load,))
|
|
.add_systems(Startup, (init,))
|
|
.add_systems(Update, (save, update, sort_buttons))
|
|
.run()
|
|
}
|
|
|
|
/// Stores audio handles so they don't get dropped
|
|
#[derive(Component, Debug)]
|
|
struct AudioItem(Handle<AudioSource>);
|
|
|
|
#[derive(Component)]
|
|
struct Container;
|
|
|
|
///
|
|
/// Load audio assets
|
|
/// TODO: Does this load new items when they're added to the folder?
|
|
fn load(server: Res<AssetServer>) {
|
|
// To prevent handles from being dropped,
|
|
// they are stored on entities created triggered on AssetEvent<AudioSource>::Created
|
|
let _yeet = server.load_folder("audio").expect("Load audios");
|
|
}
|
|
|
|
///
|
|
/// Initialize audio inspector UI
|
|
fn init(mut commands: Commands) {
|
|
commands.spawn(Camera3dBundle { ..default() });
|
|
|
|
commands.spawn((
|
|
NodeBundle {
|
|
style: Style {
|
|
width: Val::Percent(100.0),
|
|
height: Val::Percent(100.0),
|
|
align_items: AlignItems::FlexStart,
|
|
align_content: AlignContent::FlexStart,
|
|
flex_direction: FlexDirection::Row,
|
|
flex_wrap: FlexWrap::Wrap,
|
|
..default()
|
|
},
|
|
background_color: BackgroundColor(Color::MIDNIGHT_BLUE),
|
|
..default()
|
|
},
|
|
Container,
|
|
));
|
|
}
|
|
|
|
///
|
|
/// Save loaded audio sources to entities
|
|
fn save(
|
|
mut audio_load_events: EventReader<AssetEvent<AudioSource>>,
|
|
server: Res<AssetServer>,
|
|
mut commands: Commands,
|
|
container_q: Query<Entity, With<Container>>,
|
|
) {
|
|
for event in audio_load_events.iter() {
|
|
match event {
|
|
AssetEvent::Created { handle } => {
|
|
let style = TextStyle {
|
|
color: Color::BLACK,
|
|
font_size: 16.0,
|
|
..default()
|
|
};
|
|
let handle_path = server
|
|
.get_handle_path(handle.clone())
|
|
.expect("Get handle path");
|
|
let path = handle_path.path().to_str().expect("Convert path to str");
|
|
let title = path
|
|
.split("/")
|
|
.last()
|
|
.expect("Extracting filename")
|
|
.trim_end_matches(".ogg");
|
|
|
|
commands
|
|
.spawn((
|
|
Name::new(format!("{}", title)),
|
|
ButtonBundle {
|
|
style: Style {
|
|
border: UiRect::all(Val::Px(1.0)),
|
|
margin: UiRect::all(Val::Px(2.0)),
|
|
padding: UiRect::all(Val::Px(2.0)),
|
|
..default()
|
|
},
|
|
border_color: BorderColor(Color::BLACK),
|
|
..default()
|
|
},
|
|
AudioBundle {
|
|
source: handle.clone(),
|
|
settings: PlaybackSettings {
|
|
mode: bevy::audio::PlaybackMode::Loop,
|
|
paused: true,
|
|
..default()
|
|
},
|
|
},
|
|
))
|
|
.with_children(|parent| {
|
|
parent.spawn(TextBundle::from_section(title, style));
|
|
})
|
|
.set_parent(container_q.single());
|
|
}
|
|
AssetEvent::Modified { .. } => {
|
|
debug_assert!(false, "Audio file was modified, not handled!")
|
|
}
|
|
AssetEvent::Removed { .. } => {
|
|
debug_assert!(false, "Audio file was deleted, not handled!")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn sort_buttons(
|
|
mut container: Query<&mut Children, (With<Container>, Changed<Children>)>,
|
|
names: Query<&Name, With<Parent>>,
|
|
) {
|
|
container.iter_mut().for_each(|mut children| {
|
|
children.sort_by(|&a, &b| {
|
|
names
|
|
.get(a)
|
|
.expect("Button has name")
|
|
.cmp(names.get(b).expect("Button has name"))
|
|
});
|
|
});
|
|
}
|
|
|
|
/// Update loop; play/pause/volume
|
|
fn update(
|
|
mut interactions: Query<
|
|
(&Interaction, &AudioSink, &mut BackgroundColor),
|
|
(With<Button>, Changed<Interaction>),
|
|
>,
|
|
) {
|
|
interactions
|
|
.iter_mut()
|
|
.for_each(|(interaction, sink, mut bg_color)| {
|
|
// Toggle audio loop
|
|
|
|
// Toggle background color
|
|
bg_color.0 = match interaction {
|
|
Interaction::Hovered => {
|
|
if bg_color.0 == Color::ORANGE {
|
|
Color::ORANGE
|
|
} else {
|
|
Color::ORANGE_RED
|
|
}
|
|
}
|
|
Interaction::Pressed => {
|
|
sink.toggle();
|
|
|
|
if bg_color.0 == Color::ORANGE {
|
|
Color::WHITE
|
|
} else {
|
|
Color::ORANGE
|
|
}
|
|
}
|
|
Interaction::None => {
|
|
if bg_color.0 == Color::ORANGE_RED {
|
|
Color::WHITE
|
|
} else {
|
|
bg_color.0
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|