mic level bar
This commit is contained in:
parent
07233e0e12
commit
022f414acb
|
@ -3,13 +3,17 @@
|
||||||
left: 0px;
|
left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
position-type: absolute;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.frames-container {
|
.frames-container {
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.frame {
|
.frame {
|
||||||
|
@ -33,3 +37,36 @@
|
||||||
height: auto;
|
height: auto;
|
||||||
max-height: 75%;
|
max-height: 75%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.levels-container {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.levels-container div {
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.levels-container range {
|
||||||
|
padding: 0px;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-back {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mic-level {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.levels-container .active .range-low {
|
||||||
|
background-color: green
|
||||||
|
}
|
||||||
|
|
||||||
|
.levels-container .range-low {
|
||||||
|
background-color: blue
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
42
src/audio.rs
42
src/audio.rs
|
@ -10,39 +10,57 @@ use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
pub struct MicMonitor(Stream);
|
pub struct MicMonitor(Stream);
|
||||||
|
|
||||||
pub type Level = Arc<Mutex<f32>>;
|
pub type Level = Mutex<f32>;
|
||||||
#[derive(Resource, Deref)]
|
|
||||||
pub struct MicLevel(Level);
|
pub static MIC_LEVEL_RAW: Level = Mutex::new(0.);
|
||||||
|
pub static MIC_SENS_RAW: Level = Mutex::new(1.);
|
||||||
|
|
||||||
|
#[derive(Resource, Deref, Default)]
|
||||||
|
pub struct MicLevel(f32);
|
||||||
|
|
||||||
|
impl MicLevel {
|
||||||
|
pub fn value(&self) -> f32 {
|
||||||
|
**self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Resource, Deref)]
|
#[derive(Resource, Deref)]
|
||||||
pub struct MicSens(Level);
|
pub struct MicSens(f32);
|
||||||
|
|
||||||
pub struct AudioPlugin;
|
pub struct AudioPlugin;
|
||||||
|
|
||||||
impl Plugin for AudioPlugin {
|
impl Plugin for AudioPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_startup_system(monitor);
|
app.add_startup_system(monitor)
|
||||||
|
.add_systems((update_mic_level, update_mic_sens).in_base_set(CoreSet::PreUpdate))
|
||||||
|
.insert_resource(MicSens(1.))
|
||||||
|
.init_resource::<MicLevel>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_mic_level(mut level: ResMut<MicLevel>) {
|
||||||
|
level.0 = *MIC_LEVEL_RAW.lock().expect("Poisoned mutex");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_mic_sens(mut sens: ResMut<MicSens>) {
|
||||||
|
sens.0 = *MIC_SENS_RAW.lock().expect("Poisoned mutex");
|
||||||
|
}
|
||||||
|
|
||||||
pub fn monitor(world: &mut World) {
|
pub fn monitor(world: &mut World) {
|
||||||
let device = initialize().expect("Unable to init audio");
|
let device = initialize().expect("Unable to init audio");
|
||||||
info!("Using device {}", device.name().unwrap());
|
info!("Using device {}", device.name().unwrap());
|
||||||
let config = device.default_input_config().unwrap();
|
let config = device.default_input_config().unwrap();
|
||||||
|
|
||||||
let level = Arc::new(Mutex::new(0.));
|
*MIC_SENS_RAW.lock().expect("Poisoned sens mutex") = 100.0;
|
||||||
world.insert_resource(MicLevel(level.clone()));
|
|
||||||
let sens = Arc::new(Mutex::new(1.));
|
|
||||||
world.insert_resource(MicSens(sens.clone()));
|
|
||||||
|
|
||||||
let stream = device
|
let stream = device
|
||||||
.build_input_stream(
|
.build_input_stream(
|
||||||
&config.config(),
|
&config.config(),
|
||||||
move |data: &[f32], _: &InputCallbackInfo| {
|
move |data: &[f32], _: &InputCallbackInfo| {
|
||||||
let cur_level = { *level.lock().unwrap() };
|
let cur_level = { *MIC_LEVEL_RAW.lock().unwrap() };
|
||||||
*level.lock().unwrap() = (data
|
*MIC_LEVEL_RAW.lock().unwrap() = (data
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| e.abs() * *sens.lock().unwrap())
|
.map(|e| e.abs() * *MIC_SENS_RAW.lock().unwrap())
|
||||||
.max_by(|a, b| a.total_cmp(b))
|
.max_by(|a, b| a.total_cmp(b))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
* 0.5)
|
* 0.5)
|
||||||
|
|
|
@ -64,14 +64,12 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn logging(level: Res<MicLevel>, threshold: Res<TriggerThreshold>) {
|
fn logging(level: Res<MicLevel>, threshold: Res<TriggerThreshold>) {
|
||||||
info!("Audio in: {}", level.lock().unwrap());
|
info!("Audio in: {}", **level);
|
||||||
info!("Mic thresh: {}", **threshold);
|
info!("Mic thresh: {}", **threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(mut commands: Commands, sens: ResMut<MicSens>) {
|
fn setup(mut commands: Commands) {
|
||||||
commands.spawn(Camera2dBundle::default());
|
commands.spawn(Camera2dBundle::default());
|
||||||
|
|
||||||
*sens.lock().expect("Poisoned sens mutex") = 100.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// window systems
|
// window systems
|
||||||
|
@ -87,7 +85,7 @@ fn react_to_focus(
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.focused {
|
if event.focused {
|
||||||
commands.insert_resource(ClearColor(Color::ALICE_BLUE));
|
commands.insert_resource(ClearColor(Color::PURPLE));
|
||||||
ui_events.send(UiMessage::Show);
|
ui_events.send(UiMessage::Show);
|
||||||
} else {
|
} else {
|
||||||
commands.insert_resource(ClearColor(Color::NONE));
|
commands.insert_resource(ClearColor(Color::NONE));
|
||||||
|
|
13
src/tube.rs
13
src/tube.rs
|
@ -3,7 +3,7 @@ use bevy::prelude::*;
|
||||||
use bevy_tweening::{lens::TransformPositionLens, Animator, EaseMethod, Tween};
|
use bevy_tweening::{lens::TransformPositionLens, Animator, EaseMethod, Tween};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::audio::MicLevel;
|
use crate::audio::{update_mic_level, MicLevel};
|
||||||
|
|
||||||
#[derive(Resource, Clone, Copy)]
|
#[derive(Resource, Clone, Copy)]
|
||||||
pub enum MouthState {
|
pub enum MouthState {
|
||||||
|
@ -42,7 +42,11 @@ pub struct TubePlugin;
|
||||||
impl Plugin for TubePlugin {
|
impl Plugin for TubePlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_startup_system(setup)
|
app.add_startup_system(setup)
|
||||||
.add_system(audio_trigger.in_base_set(CoreSet::PreUpdate))
|
.add_system(
|
||||||
|
audio_trigger
|
||||||
|
.in_base_set(CoreSet::PreUpdate)
|
||||||
|
.after(update_mic_level),
|
||||||
|
)
|
||||||
.add_system(run_animation.run_if(resource_changed::<MouthState>()))
|
.add_system(run_animation.run_if(resource_changed::<MouthState>()))
|
||||||
.add_system(sync_images)
|
.add_system(sync_images)
|
||||||
.insert_resource(MouthState::Closed)
|
.insert_resource(MouthState::Closed)
|
||||||
|
@ -87,15 +91,14 @@ fn setup(mut commands: Commands, ass: Res<AssetServer>) {
|
||||||
const MAX_THRESHOLD: f32 = 10.0;
|
const MAX_THRESHOLD: f32 = 10.0;
|
||||||
fn audio_trigger(
|
fn audio_trigger(
|
||||||
mut mouth_state: ResMut<MouthState>,
|
mut mouth_state: ResMut<MouthState>,
|
||||||
mic_level: Res<MicLevel>,
|
level: Res<MicLevel>,
|
||||||
threshold: Res<TriggerThreshold>,
|
threshold: Res<TriggerThreshold>,
|
||||||
mut buffer: ResMut<ActivationBuffer>,
|
mut buffer: ResMut<ActivationBuffer>,
|
||||||
mut was_zero: Local<bool>,
|
mut was_zero: Local<bool>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
) {
|
) {
|
||||||
let level = mic_level.lock().expect("Poisoned mutex");
|
|
||||||
// info!("{}", ((**threshold / 100.) * MAX_THRESHOLD));
|
// info!("{}", ((**threshold / 100.) * MAX_THRESHOLD));
|
||||||
if *level > ((**threshold / 100.) * MAX_THRESHOLD) {
|
if level.value() > ((**threshold / 100.) * MAX_THRESHOLD) {
|
||||||
if buffer.finished() {
|
if buffer.finished() {
|
||||||
info!("Open mouth");
|
info!("Open mouth");
|
||||||
*mouth_state = MouthState::Opened;
|
*mouth_state = MouthState::Opened;
|
||||||
|
|
460
src/ui/levels.rs
460
src/ui/levels.rs
|
@ -1,9 +1,9 @@
|
||||||
use std::{collections::HashMap, marker::PhantomData, time::Duration};
|
use std::{collections::HashMap, marker::PhantomData, time::Duration};
|
||||||
|
|
||||||
|
use belly::prelude::*;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_eventlistener::callbacks::ListenerInput;
|
use bevy_eventlistener::callbacks::ListenerInput;
|
||||||
use bevy_mod_picking::prelude::*;
|
use bevy_mod_picking::prelude::*;
|
||||||
use bevy_ninepatch::*;
|
|
||||||
use bevy_tweening::{lens::UiPositionLens, *};
|
use bevy_tweening::{lens::UiPositionLens, *};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -12,19 +12,21 @@ use crate::{
|
||||||
utils::UiSizeLens,
|
utils::UiSizeLens,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Component)]
|
use super::setup_ui;
|
||||||
|
|
||||||
|
#[derive(Component, Default)]
|
||||||
pub struct LevelBars;
|
pub struct LevelBars;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Default)]
|
||||||
pub struct MicLevelBar;
|
pub struct MicLevelBar;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Default)]
|
||||||
pub struct MicLevelContainer;
|
pub struct MicLevelContainer;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Default)]
|
||||||
pub struct ActivationLevelBar;
|
pub struct ActivationLevelBar;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Default)]
|
||||||
pub struct MicLevelArrow;
|
pub struct MicLevelArrow;
|
||||||
|
|
||||||
pub struct ArrowDrag<C: Component> {
|
pub struct ArrowDrag<C: Component> {
|
||||||
|
@ -70,250 +72,240 @@ pub(super) struct LevelsPlugin;
|
||||||
|
|
||||||
impl Plugin for LevelsPlugin {
|
impl Plugin for LevelsPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_startup_system(spawn_levels)
|
app.add_event::<ArrowDrag<MicLevelArrow>>()
|
||||||
.add_systems((set_max_size::<MicLevelContainer, MicLevelArrow>
|
|
||||||
.after(spawn_levels)
|
|
||||||
.in_base_set(CoreSet::Last)
|
|
||||||
.run_if(run_once()),))
|
|
||||||
.add_event::<ArrowDrag<MicLevelArrow>>()
|
|
||||||
.add_system(
|
.add_system(
|
||||||
handle_mic_thresh_set.run_if(resource_exists::<MaxPercent<MicLevelContainer>>()),
|
handle_mic_thresh_set.run_if(resource_exists::<MaxPercent<MicLevelContainer>>()),
|
||||||
)
|
)
|
||||||
.add_system(react_mic_level_color.run_if(resource_changed::<MouthState>()))
|
.add_system(react_mic_level_color.run_if(resource_changed::<MouthState>()));
|
||||||
.add_system(animate_mic_level)
|
|
||||||
.add_system(animate_active_level);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const RESTING_POS: f32 = 20.;
|
const RESTING_POS: f32 = 20.;
|
||||||
|
|
||||||
pub(super) fn spawn_levels(
|
pub(super) fn spawn_levels(
|
||||||
mut commands: Commands,
|
// mut commands: Commands,
|
||||||
ass: Res<AssetServer>,
|
// mic_threshold: Res<TriggerThreshold>,
|
||||||
mut patches: ResMut<Assets<NinePatchBuilder<()>>>,
|
mut elements: Elements,
|
||||||
mic_threshold: Res<TriggerThreshold>,
|
|
||||||
) {
|
) {
|
||||||
let bar_img: Handle<Image> = ass.load("square-small.png");
|
|
||||||
let bar_patch = patches.add(NinePatchBuilder::by_margins(2, 2, 2, 2));
|
|
||||||
let mic_bar = commands
|
|
||||||
.spawn(NodeBundle {
|
|
||||||
background_color: Color::GREEN.into(),
|
|
||||||
style: Style {
|
|
||||||
position_type: PositionType::Absolute,
|
|
||||||
size: Size::new(Val::Percent(100.), Val::Percent(0.)),
|
|
||||||
position: UiRect::bottom(Val::Px(0.)),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
..default()
|
|
||||||
})
|
|
||||||
.insert(MicLevelBar)
|
|
||||||
.id();
|
|
||||||
|
|
||||||
let active_bar = commands
|
// let bar_img: Handle<Image> = ass.load("square-small.png");
|
||||||
.spawn(NodeBundle {
|
// let bar_patch = patches.add(NinePatchBuilder::by_margins(2, 2, 2, 2));
|
||||||
background_color: Color::PURPLE.into(),
|
// let mic_bar = commands
|
||||||
style: Style {
|
// .spawn(NodeBundle {
|
||||||
position_type: PositionType::Absolute,
|
// background_color: Color::GREEN.into(),
|
||||||
size: Size::new(Val::Percent(100.), Val::Percent(0.)),
|
// style: Style {
|
||||||
position: UiRect::bottom(Val::Px(0.)),
|
// position_type: PositionType::Absolute,
|
||||||
..default()
|
// size: Size::new(Val::Percent(100.), Val::Percent(0.)),
|
||||||
},
|
// position: UiRect::bottom(Val::Px(0.)),
|
||||||
..default()
|
// ..default()
|
||||||
})
|
// },
|
||||||
.insert(ActivationLevelBar)
|
// ..default()
|
||||||
.id();
|
// })
|
||||||
|
// .insert(MicLevelBar)
|
||||||
|
// .id();
|
||||||
|
|
||||||
commands
|
// let active_bar = commands
|
||||||
.spawn(NodeBundle {
|
// .spawn(NodeBundle {
|
||||||
style: Style {
|
// background_color: Color::PURPLE.into(),
|
||||||
size: Size::new(Val::Px(200.), Val::Percent(100.)),
|
// style: Style {
|
||||||
position_type: PositionType::Absolute,
|
// position_type: PositionType::Absolute,
|
||||||
gap: Size::all(Val::Px(20.)),
|
// size: Size::new(Val::Percent(100.), Val::Percent(0.)),
|
||||||
position: UiRect::right(Val::Px(RESTING_POS)),
|
// position: UiRect::bottom(Val::Px(0.)),
|
||||||
justify_content: JustifyContent::FlexEnd,
|
// ..default()
|
||||||
padding: UiRect::vertical(Val::Px(20.)),
|
// },
|
||||||
..default()
|
// ..default()
|
||||||
},
|
// })
|
||||||
..default()
|
// .insert(ActivationLevelBar)
|
||||||
})
|
// .id();
|
||||||
.insert(LevelBars)
|
|
||||||
.with_children(|parent| {
|
|
||||||
parent
|
|
||||||
.spawn(NodeBundle {
|
|
||||||
style: Style {
|
|
||||||
flex_direction: FlexDirection::Column,
|
|
||||||
gap: Size::height(Val::Px(10.)),
|
|
||||||
// size: Size::all(Val::Percent(100.)),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
..default()
|
|
||||||
})
|
|
||||||
.with_children(|parent| {
|
|
||||||
parent.spawn(ImageBundle {
|
|
||||||
image: UiImage::new(ass.load("microphone.png")),
|
|
||||||
background_color: Color::BLACK.into(),
|
|
||||||
..default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut content = HashMap::new();
|
// commands
|
||||||
content.insert((), mic_bar);
|
// .spawn(NodeBundle {
|
||||||
parent
|
// style: Style {
|
||||||
.spawn(NodeBundle {
|
// size: Size::new(Val::Px(200.), Val::Percent(100.)),
|
||||||
style: Style {
|
// position_type: PositionType::Absolute,
|
||||||
size: Size::height(Val::Percent(100.)),
|
// gap: Size::all(Val::Px(20.)),
|
||||||
..default()
|
// position: UiRect::right(Val::Px(RESTING_POS)),
|
||||||
},
|
// justify_content: JustifyContent::FlexEnd,
|
||||||
..default()
|
// padding: UiRect::vertical(Val::Px(20.)),
|
||||||
})
|
// ..default()
|
||||||
.with_children(|parent| {
|
// },
|
||||||
parent
|
// ..default()
|
||||||
.spawn(ImageBundle {
|
// })
|
||||||
image: UiImage::new(ass.load("plain-arrow.png")),
|
// .insert(LevelBars)
|
||||||
background_color: Color::BLACK.into(),
|
// .with_children(|parent| {
|
||||||
style: Style {
|
// parent
|
||||||
size: Size::all(Val::Px(16.)),
|
// .spawn(NodeBundle {
|
||||||
position_type: PositionType::Absolute,
|
// style: Style {
|
||||||
position: UiRect::new(
|
// flex_direction: FlexDirection::Column,
|
||||||
Val::Px(-16.),
|
// gap: Size::height(Val::Px(10.)),
|
||||||
Val::Auto,
|
// // size: Size::all(Val::Percent(100.)),
|
||||||
Val::Auto,
|
// ..default()
|
||||||
Val::Percent(**mic_threshold),
|
// },
|
||||||
),
|
// ..default()
|
||||||
..default()
|
// })
|
||||||
},
|
// .with_children(|parent| {
|
||||||
z_index: ZIndex::Global(100),
|
// parent.spawn(ImageBundle {
|
||||||
..default()
|
// image: UiImage::new(ass.load("microphone.png")),
|
||||||
})
|
// background_color: Color::BLACK.into(),
|
||||||
.insert(MicLevelArrow)
|
// ..default()
|
||||||
.insert(
|
// });
|
||||||
On::<Pointer<Drag>>::send_event::<ArrowDrag<MicLevelArrow>>(),
|
|
||||||
);
|
// let mut content = HashMap::new();
|
||||||
parent
|
// content.insert((), mic_bar);
|
||||||
.spawn(NinePatchBundle {
|
// parent
|
||||||
style: Style {
|
// .spawn(NodeBundle {
|
||||||
size: Size::new(Val::Px(32.), Val::Percent(100.)),
|
// style: Style {
|
||||||
..default()
|
// size: Size::height(Val::Percent(100.)),
|
||||||
},
|
// ..default()
|
||||||
nine_patch_data: NinePatchData {
|
// },
|
||||||
texture: bar_img.clone(),
|
// ..default()
|
||||||
nine_patch: bar_patch.clone(),
|
// })
|
||||||
content: Some(content),
|
// .with_children(|parent| {
|
||||||
..default()
|
// parent
|
||||||
},
|
// .spawn(ImageBundle {
|
||||||
..default()
|
// image: UiImage::new(ass.load("plain-arrow.png")),
|
||||||
})
|
// background_color: Color::BLACK.into(),
|
||||||
.insert(MicLevelContainer);
|
// style: Style {
|
||||||
});
|
// size: Size::all(Val::Px(16.)),
|
||||||
});
|
// position_type: PositionType::Absolute,
|
||||||
parent
|
// position: UiRect::new(
|
||||||
.spawn(NodeBundle {
|
// Val::Px(-16.),
|
||||||
style: Style {
|
// Val::Auto,
|
||||||
flex_direction: FlexDirection::Column,
|
// Val::Auto,
|
||||||
gap: Size::height(Val::Px(10.)),
|
// Val::Percent(**mic_threshold),
|
||||||
..default()
|
// ),
|
||||||
},
|
// ..default()
|
||||||
..default()
|
// },
|
||||||
})
|
// z_index: ZIndex::Global(100),
|
||||||
.with_children(|parent| {
|
// ..default()
|
||||||
parent.spawn(ImageBundle {
|
// })
|
||||||
image: UiImage::new(ass.load("lips.png")),
|
// .insert(MicLevelArrow)
|
||||||
background_color: Color::BLACK.into(),
|
// .insert(
|
||||||
..default()
|
// On::<Pointer<Drag>>::send_event::<ArrowDrag<MicLevelArrow>>(),
|
||||||
});
|
// );
|
||||||
let mut content = HashMap::new();
|
// parent
|
||||||
content.insert((), active_bar);
|
// .spawn(NinePatchBundle {
|
||||||
parent.spawn(NinePatchBundle {
|
// style: Style {
|
||||||
style: Style {
|
// size: Size::new(Val::Px(32.), Val::Percent(100.)),
|
||||||
size: Size::new(Val::Px(32.), Val::Percent(100.)),
|
// ..default()
|
||||||
..default()
|
// },
|
||||||
},
|
// nine_patch_data: NinePatchData {
|
||||||
nine_patch_data: NinePatchData {
|
// texture: bar_img.clone(),
|
||||||
texture: bar_img.clone(),
|
// nine_patch: bar_patch.clone(),
|
||||||
nine_patch: bar_patch.clone(),
|
// content: Some(content),
|
||||||
content: Some(content),
|
// ..default()
|
||||||
..default()
|
// },
|
||||||
},
|
// ..default()
|
||||||
..default()
|
// })
|
||||||
});
|
// .insert(MicLevelContainer);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
// parent
|
||||||
|
// .spawn(NodeBundle {
|
||||||
|
// style: Style {
|
||||||
|
// flex_direction: FlexDirection::Column,
|
||||||
|
// gap: Size::height(Val::Px(10.)),
|
||||||
|
// ..default()
|
||||||
|
// },
|
||||||
|
// ..default()
|
||||||
|
// })
|
||||||
|
// .with_children(|parent| {
|
||||||
|
// parent.spawn(ImageBundle {
|
||||||
|
// image: UiImage::new(ass.load("lips.png")),
|
||||||
|
// background_color: Color::BLACK.into(),
|
||||||
|
// ..default()
|
||||||
|
// });
|
||||||
|
// let mut content = HashMap::new();
|
||||||
|
// content.insert((), active_bar);
|
||||||
|
// parent.spawn(NinePatchBundle {
|
||||||
|
// style: Style {
|
||||||
|
// size: Size::new(Val::Px(32.), Val::Percent(100.)),
|
||||||
|
// ..default()
|
||||||
|
// },
|
||||||
|
// nine_patch_data: NinePatchData {
|
||||||
|
// texture: bar_img.clone(),
|
||||||
|
// nine_patch: bar_patch.clone(),
|
||||||
|
// content: Some(content),
|
||||||
|
// ..default()
|
||||||
|
// },
|
||||||
|
// ..default()
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_max_size<B: Component, C: Component>(
|
// fn set_max_size<B: Component, C: Component>(
|
||||||
mut commands: Commands,
|
// mut commands: Commands,
|
||||||
bar_q: Query<&Node, With<B>>,
|
// bar_q: Query<&Node, With<B>>,
|
||||||
other: Query<&Node, With<C>>,
|
// other: Query<&Node, With<C>>,
|
||||||
) {
|
// ) {
|
||||||
let Ok(bar) = bar_q.get_single() else { return };
|
// let Ok(bar) = bar_q.get_single() else { return };
|
||||||
let Ok(other) = other.get_single() else { return };
|
// let Ok(other) = other.get_single() else {
|
||||||
let size = ((bar.size().y - (other.size().y / 2.)) / bar.size().y) * 100.;
|
// return;
|
||||||
info!("{size}");
|
// };
|
||||||
commands.insert_resource(MaxPercent::<B>::new(size));
|
// let size = ((bar.size().y - (other.size().y / 2.)) / bar.size().y) * 100.;
|
||||||
}
|
// info!("{size}");
|
||||||
|
// commands.insert_resource(MaxPercent::<B>::new(size));
|
||||||
|
// }
|
||||||
|
|
||||||
pub(super) fn animate_mic_level(
|
// pub(super) fn animate_mic_level(
|
||||||
mut commands: Commands,
|
// mut commands: Commands,
|
||||||
bar_q: Query<(Entity, &Style), With<MicLevelBar>>,
|
// bar_q: Query<(Entity, &Style), With<MicLevelBar>>,
|
||||||
level: Res<MicLevel>,
|
// level: Res<MicLevel>,
|
||||||
) {
|
// ) {
|
||||||
const SCALE: f32 = 10.;
|
// const SCALE: f32 = 10.;
|
||||||
for (ent, style) in bar_q.iter() {
|
// for (ent, style) in bar_q.iter() {
|
||||||
let current = style.size;
|
// let current = style.size;
|
||||||
let new = Size::new(
|
// let new = Size::new(
|
||||||
style.size.width,
|
// style.size.width,
|
||||||
Val::Percent((*level.lock().expect("Poisoned mic level") * SCALE).clamp(0., 100.)),
|
// Val::Percent((level.value() * SCALE).clamp(0., 100.)),
|
||||||
);
|
// );
|
||||||
let tween = Tween::new(
|
// let tween = Tween::new(
|
||||||
EaseMethod::Linear,
|
// EaseMethod::Linear,
|
||||||
Duration::from_millis(1),
|
// Duration::from_millis(1),
|
||||||
UiSizeLens {
|
// UiSizeLens {
|
||||||
start: current,
|
// start: current,
|
||||||
end: new,
|
// end: new,
|
||||||
},
|
// },
|
||||||
);
|
// );
|
||||||
commands.entity(ent).insert(Animator::new(tween));
|
// commands.entity(ent).insert(Animator::new(tween));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub(super) fn animate_active_level(
|
// pub(super) fn animate_active_level(
|
||||||
mut commands: Commands,
|
// mut commands: Commands,
|
||||||
bar_q: Query<(Entity, &Style), With<ActivationLevelBar>>,
|
// bar_q: Query<(Entity, &Style), With<ActivationLevelBar>>,
|
||||||
level: Res<ActivationBuffer>,
|
// level: Res<ActivationBuffer>,
|
||||||
) {
|
// ) {
|
||||||
const SCALE: f32 = 100.;
|
// const SCALE: f32 = 100.;
|
||||||
for (ent, style) in bar_q.iter() {
|
// for (ent, style) in bar_q.iter() {
|
||||||
let current = style.size;
|
// let current = style.size;
|
||||||
let new = Size::new(
|
// let new = Size::new(
|
||||||
style.size.width,
|
// style.size.width,
|
||||||
Val::Percent((level.percent_left() * SCALE).clamp(0., 100.)),
|
// Val::Percent((level.percent_left() * SCALE).clamp(0., 100.)),
|
||||||
);
|
// );
|
||||||
|
|
||||||
// info!("UI mic level: {:?}", new.height);
|
// // info!("UI mic level: {:?}", new.height);
|
||||||
let tween = Tween::new(
|
// let tween = Tween::new(
|
||||||
EaseMethod::Linear,
|
// EaseMethod::Linear,
|
||||||
Duration::from_millis(1),
|
// Duration::from_millis(1),
|
||||||
UiSizeLens {
|
// UiSizeLens {
|
||||||
start: current,
|
// start: current,
|
||||||
end: new,
|
// end: new,
|
||||||
},
|
// },
|
||||||
);
|
// );
|
||||||
commands.entity(ent).insert(Animator::new(tween));
|
// commands.entity(ent).insert(Animator::new(tween));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub(super) fn react_mic_level_color(
|
pub(super) fn react_mic_level_color(mut elements: Elements, mouth_state: Res<MouthState>) {
|
||||||
mut bar_q: Query<&mut BackgroundColor, With<MicLevelBar>>,
|
match *mouth_state {
|
||||||
mouth_state: Res<MouthState>,
|
MouthState::Opened => {
|
||||||
) {
|
elements.select(".mic-level").add_class("active");
|
||||||
for mut color in bar_q.iter_mut() {
|
}
|
||||||
match *mouth_state {
|
|
||||||
MouthState::Opened => {
|
|
||||||
*color = Color::GREEN.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
MouthState::Closed => {
|
MouthState::Closed => {
|
||||||
*color = Color::BLUE.into();
|
elements.select(".mic-level").remove_class("active");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,8 +318,12 @@ fn handle_mic_thresh_set(
|
||||||
max_size: Res<MaxPercent<MicLevelContainer>>,
|
max_size: Res<MaxPercent<MicLevelContainer>>,
|
||||||
) {
|
) {
|
||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
let Ok((mut style, arrow)) = styles.get_mut(event.target) else { continue };
|
let Ok((mut style, arrow)) = styles.get_mut(event.target) else {
|
||||||
let Ok(bar) = bar_q.get_single() else { continue };
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(bar) = bar_q.get_single() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
if let Val::Percent(pos) = style.position.bottom {
|
if let Val::Percent(pos) = style.position.bottom {
|
||||||
let delta = (event.delta.y / bar.size().y) * 100.;
|
let delta = (event.delta.y / bar.size().y) * 100.;
|
||||||
let new_percent = (delta + pos).clamp(0., **max_size);
|
let new_percent = (delta + pos).clamp(0., **max_size);
|
||||||
|
|
|
@ -2,13 +2,15 @@ mod frames;
|
||||||
mod levels;
|
mod levels;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
|
|
||||||
use belly::prelude::*;
|
use belly::{prelude::*, widgets::range::RangeWidgetExtension};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_ninepatch::NinePatchPlugin;
|
use bevy_ninepatch::NinePatchPlugin;
|
||||||
|
|
||||||
|
use crate::audio::MicLevel;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
frames::{CardImages, FrameCards},
|
frames::{CardImages, FrameCards},
|
||||||
levels::LevelBars,
|
levels::{ActivationLevelBar, LevelBars, MicLevelArrow, MicLevelContainer},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -23,15 +25,10 @@ impl Plugin for UiPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_plugin(BellyPlugin)
|
app.add_plugin(BellyPlugin)
|
||||||
.add_event::<UiMessage>()
|
.add_event::<UiMessage>()
|
||||||
|
.add_plugin(levels::LevelsPlugin)
|
||||||
.add_startup_system(setup_ui)
|
.add_startup_system(setup_ui)
|
||||||
.add_system(handle_msg)
|
.add_system(handle_msg)
|
||||||
.add_system(frames::update_card_images);
|
.add_system(frames::update_card_images);
|
||||||
// app.add_plugin(NinePatchPlugin::<()>::default())
|
|
||||||
// .add_plugin(levels::LevelsPlugin)
|
|
||||||
// .init_resource::<CardImages>()
|
|
||||||
// .add_startup_system(frames::update_card_images)
|
|
||||||
// .add_system(frames::update_card_images.before(frames::render_cards))
|
|
||||||
// .add_startup_system(frames::render_cards);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +42,16 @@ fn setup_ui(mut commands: Commands) {
|
||||||
</div>
|
</div>
|
||||||
</for>
|
</for>
|
||||||
</div>
|
</div>
|
||||||
|
<div c:right c:levels-container with=LevelBars s:right="0px">
|
||||||
|
<div s:flex-direction="column">
|
||||||
|
<range c:mic-level s:height="75%" s:width="50px" mode="vertical" maximum=5.0 bind:value=from!(MicLevel:value())/>
|
||||||
|
<img src="microphone.png" modulate=Color::BLACK/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div c:level-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -56,7 +63,9 @@ fn handle_msg(
|
||||||
levels_q: Query<(Entity, &Style), With<LevelBars>>,
|
levels_q: Query<(Entity, &Style), With<LevelBars>>,
|
||||||
) {
|
) {
|
||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
let Ok((frames, frame_style)) = frame_q.get_single() else { return };
|
let Ok((frames, frame_style)) = frame_q.get_single() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
// let Ok((bars, bar_style)) = levels_q.get_single() else { return };
|
// let Ok((bars, bar_style)) = levels_q.get_single() else { return };
|
||||||
match event {
|
match event {
|
||||||
UiMessage::Hide => {
|
UiMessage::Hide => {
|
||||||
|
|
Loading…
Reference in New Issue