generated from lks/eframe_template_android
Compare commits
No commits in common. "24d9fc797297c902033d27343efa29268decf317" and "fc9e04ffd2adcc54bfab09d3eb1559aba693056c" have entirely different histories.
24d9fc7972
...
fc9e04ffd2
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -1013,15 +1013,6 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui_plot"
|
||||
version = "0.27.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7854b86dc1c2d352c5270db3d600011daa913d6b554141a03939761323288a1"
|
||||
dependencies = [
|
||||
"egui",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.11.0"
|
||||
@ -2837,7 +2828,6 @@ dependencies = [
|
||||
"cpal",
|
||||
"eframe",
|
||||
"egui",
|
||||
"egui_plot",
|
||||
"env_logger",
|
||||
"log",
|
||||
"realfft",
|
||||
|
||||
@ -10,7 +10,6 @@ edition = "2021"
|
||||
anyhow = "1.0.83"
|
||||
cpal = "0.15.3"
|
||||
egui = "0.27.0"
|
||||
egui_plot = "0.27.2"
|
||||
log = "0.4.21"
|
||||
realfft = "3.3.0"
|
||||
|
||||
|
||||
14
src/app.rs
14
src/app.rs
@ -2,8 +2,6 @@ use eframe::{egui_glow, glow};
|
||||
use egui::mutex::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub mod debug_plot;
|
||||
use debug_plot::DebugPlots;
|
||||
mod waterfall;
|
||||
use waterfall::Waterfall;
|
||||
mod audio_fft;
|
||||
@ -12,8 +10,8 @@ pub mod turbo_colormap;
|
||||
|
||||
const FFT_SIZE: usize = 1024;
|
||||
|
||||
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
||||
pub struct TemplateApp {
|
||||
plots: DebugPlots,
|
||||
// Example stuff:
|
||||
label: String,
|
||||
value: f32,
|
||||
@ -31,8 +29,7 @@ impl TemplateApp {
|
||||
// Load previous app state (if any).
|
||||
// Note that you must enable the `persistence` feature for this to work.
|
||||
|
||||
let plots = DebugPlots::new();
|
||||
let (stream, rx) = AudioFFT::new(FFT_SIZE, plots.get_sender()).unwrap();
|
||||
let (stream, rx) = AudioFFT::new(FFT_SIZE).unwrap();
|
||||
let wf_size = stream.output_len;
|
||||
let gl = cc
|
||||
.gl
|
||||
@ -40,7 +37,6 @@ impl TemplateApp {
|
||||
.expect("Could not get gl context from glow backend");
|
||||
|
||||
Self {
|
||||
plots,
|
||||
// Example stuff:
|
||||
label: "Hello World!".to_owned(),
|
||||
value: 2.7,
|
||||
@ -68,7 +64,6 @@ impl eframe::App for TemplateApp {
|
||||
// For inspiration and more examples, go to https://emilk.github.io/egui
|
||||
|
||||
ctx.request_repaint();
|
||||
self.plots.update_plots();
|
||||
|
||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||
// The top panel is often a good place for a menu bar:
|
||||
@ -82,16 +77,13 @@ impl eframe::App for TemplateApp {
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||
}
|
||||
});
|
||||
ui.add_space(16.0);
|
||||
}
|
||||
self.plots.render_menu_buttons(ui);
|
||||
ui.add_space(16.0);
|
||||
|
||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
||||
});
|
||||
});
|
||||
|
||||
self.plots.render_plot_windows(ctx);
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
// The central panel the region left after adding TopPanel's and SidePanel's
|
||||
ui.heading("eframe template");
|
||||
|
||||
@ -5,9 +5,7 @@ use cpal::{
|
||||
BufferSize, StreamConfig,
|
||||
};
|
||||
use realfft::RealFftPlanner;
|
||||
use std::sync::mpsc::{self, Sender};
|
||||
|
||||
use super::debug_plot::PlotData;
|
||||
use std::sync::mpsc;
|
||||
|
||||
pub struct AudioFFT {
|
||||
pub stream: cpal::Stream,
|
||||
@ -15,10 +13,7 @@ pub struct AudioFFT {
|
||||
}
|
||||
|
||||
impl AudioFFT {
|
||||
pub fn new(
|
||||
size: usize,
|
||||
plot_tx: Sender<(&'static str, PlotData)>,
|
||||
) -> Result<(Self, mpsc::Receiver<Vec<u8>>)> {
|
||||
pub fn new(size: usize) -> Result<(Self, mpsc::Receiver<Vec<u8>>)> {
|
||||
let output_len = size / 2 + 1;
|
||||
|
||||
// Create mpsc queue
|
||||
@ -50,20 +45,9 @@ impl AudioFFT {
|
||||
assert_eq!(size, fft_in.len());
|
||||
fft.process_with_scratch(&mut fft_in, &mut fft_out, &mut fft_scratch)
|
||||
.unwrap();
|
||||
plot_tx
|
||||
.send(("FFT Output", PlotData::Bode32(fft_out.clone())))
|
||||
.unwrap();
|
||||
fft_in.clear();
|
||||
let output: Vec<u8> = fft_out
|
||||
.iter()
|
||||
.map(|c| {
|
||||
(((c.re * c.re) + (c.im * c.im)).sqrt() / size as f32 * 255.0) as u8
|
||||
})
|
||||
.collect();
|
||||
let output: Vec<u8> = fft_out.iter().map(|c| (c.arg() * 255.0) as u8).collect();
|
||||
assert_eq!(output_len, output.len());
|
||||
plot_tx
|
||||
.send(("FFT Processed Output", PlotData::U8(output.clone())))
|
||||
.unwrap();
|
||||
tx.send(output).unwrap();
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,99 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc::{self, Sender};
|
||||
|
||||
use egui::{Context, Ui};
|
||||
use egui_plot::{Line, Plot, PlotBounds, PlotPoints};
|
||||
use realfft::num_complex::Complex32;
|
||||
|
||||
pub enum PlotData {
|
||||
U8(Vec<u8>),
|
||||
//F32(Vec<f32>),
|
||||
Bode32(Vec<Complex32>),
|
||||
}
|
||||
|
||||
pub struct DebugPlots {
|
||||
plots: HashMap<&'static str, PlotData>,
|
||||
plot_en: HashMap<&'static str, bool>,
|
||||
rx: mpsc::Receiver<(&'static str, PlotData)>,
|
||||
tx: mpsc::Sender<(&'static str, PlotData)>,
|
||||
}
|
||||
|
||||
impl DebugPlots {
|
||||
pub fn new() -> Self {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
DebugPlots {
|
||||
plots: HashMap::new(),
|
||||
plot_en: HashMap::new(),
|
||||
rx,
|
||||
tx,
|
||||
}
|
||||
}
|
||||
pub fn get_sender(&self) -> Sender<(&'static str, PlotData)> {
|
||||
self.tx.clone()
|
||||
}
|
||||
pub fn update_plots(&mut self) {
|
||||
while let Ok((key, plot)) = self.rx.try_recv() {
|
||||
if self.plots.insert(key, plot).is_none() {
|
||||
self.plot_en.insert(key, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn render_menu_buttons(&mut self, ui: &mut Ui) {
|
||||
ui.menu_button("Debug Plots", |ui| {
|
||||
for &k in self.plots.keys() {
|
||||
if !self.plot_en.contains_key(k) {
|
||||
self.plot_en.insert(k, false);
|
||||
}
|
||||
let enabled = self.plot_en.get_mut(k).unwrap();
|
||||
ui.checkbox(enabled, k);
|
||||
}
|
||||
});
|
||||
}
|
||||
pub fn render_plot_windows(&mut self, ctx: &Context) {
|
||||
for (key, plot) in self.plots.iter() {
|
||||
let enabled = self.plot_en.get_mut(key).unwrap();
|
||||
if *enabled {
|
||||
DebugPlots::render_window(ctx, key, plot, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn render_window(ctx: &Context, title: &'static str, plot: &PlotData, open: &mut bool) {
|
||||
egui::Window::new(title).open(open).show(ctx, |ui| {
|
||||
ui.heading(title);
|
||||
match plot {
|
||||
PlotData::U8(v) => {
|
||||
ui.heading("u8 Plot");
|
||||
let line = Line::new(PlotPoints::from_iter(
|
||||
v.iter().enumerate().map(|(i, y)| [i as f64, *y as f64]),
|
||||
));
|
||||
let plot = Plot::new(title);
|
||||
plot.show(ui, |plot_ui| {
|
||||
plot_ui.line(line);
|
||||
plot_ui.set_plot_bounds(PlotBounds::from_min_max(
|
||||
[-1.0, -1.0],
|
||||
[(v.len() + 1) as f64, 256.0],
|
||||
));
|
||||
});
|
||||
}
|
||||
PlotData::Bode32(v) => {
|
||||
ui.heading("Bode Plot");
|
||||
let mag_line =
|
||||
Line::new(PlotPoints::from_iter(v.iter().enumerate().map(|(i, c)| {
|
||||
[i as f64, ((c.re * c.re) + (c.im * c.im)).sqrt() as f64]
|
||||
})));
|
||||
let phase_line = Line::new(PlotPoints::from_iter(
|
||||
v.iter()
|
||||
.enumerate()
|
||||
.map(|(i, c)| [i as f64, c.arg() as f64]),
|
||||
));
|
||||
let plot = Plot::new(title);
|
||||
plot.show(ui, |plot_ui| {
|
||||
plot_ui.line(mag_line);
|
||||
plot_ui.line(phase_line);
|
||||
});
|
||||
ui.heading("TODO");
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user