mic level bar

This commit is contained in:
Emerald 2023-10-10 14:27:24 -04:00
parent 07233e0e12
commit 022f414acb
Signed by: emerald
GPG Key ID: 420C9E1863CCB30F
6 changed files with 325 additions and 264 deletions

View File

@ -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
}

View File

@ -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)

View File

@ -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));

View File

@ -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;

View File

@ -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);

View File

@ -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 => {