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",
|
"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]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
@ -2837,7 +2828,6 @@ dependencies = [
|
|||||||
"cpal",
|
"cpal",
|
||||||
"eframe",
|
"eframe",
|
||||||
"egui",
|
"egui",
|
||||||
"egui_plot",
|
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
"realfft",
|
"realfft",
|
||||||
|
|||||||
@ -10,7 +10,6 @@ edition = "2021"
|
|||||||
anyhow = "1.0.83"
|
anyhow = "1.0.83"
|
||||||
cpal = "0.15.3"
|
cpal = "0.15.3"
|
||||||
egui = "0.27.0"
|
egui = "0.27.0"
|
||||||
egui_plot = "0.27.2"
|
|
||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
realfft = "3.3.0"
|
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 egui::mutex::Mutex;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub mod debug_plot;
|
|
||||||
use debug_plot::DebugPlots;
|
|
||||||
mod waterfall;
|
mod waterfall;
|
||||||
use waterfall::Waterfall;
|
use waterfall::Waterfall;
|
||||||
mod audio_fft;
|
mod audio_fft;
|
||||||
@ -12,8 +10,8 @@ pub mod turbo_colormap;
|
|||||||
|
|
||||||
const FFT_SIZE: usize = 1024;
|
const FFT_SIZE: usize = 1024;
|
||||||
|
|
||||||
|
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
||||||
pub struct TemplateApp {
|
pub struct TemplateApp {
|
||||||
plots: DebugPlots,
|
|
||||||
// Example stuff:
|
// Example stuff:
|
||||||
label: String,
|
label: String,
|
||||||
value: f32,
|
value: f32,
|
||||||
@ -31,8 +29,7 @@ impl TemplateApp {
|
|||||||
// Load previous app state (if any).
|
// Load previous app state (if any).
|
||||||
// Note that you must enable the `persistence` feature for this to work.
|
// Note that you must enable the `persistence` feature for this to work.
|
||||||
|
|
||||||
let plots = DebugPlots::new();
|
let (stream, rx) = AudioFFT::new(FFT_SIZE).unwrap();
|
||||||
let (stream, rx) = AudioFFT::new(FFT_SIZE, plots.get_sender()).unwrap();
|
|
||||||
let wf_size = stream.output_len;
|
let wf_size = stream.output_len;
|
||||||
let gl = cc
|
let gl = cc
|
||||||
.gl
|
.gl
|
||||||
@ -40,7 +37,6 @@ impl TemplateApp {
|
|||||||
.expect("Could not get gl context from glow backend");
|
.expect("Could not get gl context from glow backend");
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
plots,
|
|
||||||
// Example stuff:
|
// Example stuff:
|
||||||
label: "Hello World!".to_owned(),
|
label: "Hello World!".to_owned(),
|
||||||
value: 2.7,
|
value: 2.7,
|
||||||
@ -68,7 +64,6 @@ impl eframe::App for TemplateApp {
|
|||||||
// For inspiration and more examples, go to https://emilk.github.io/egui
|
// For inspiration and more examples, go to https://emilk.github.io/egui
|
||||||
|
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
self.plots.update_plots();
|
|
||||||
|
|
||||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||||
// The top panel is often a good place for a menu bar:
|
// 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);
|
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);
|
egui::widgets::global_dark_light_mode_buttons(ui);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
self.plots.render_plot_windows(ctx);
|
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
// The central panel the region left after adding TopPanel's and SidePanel's
|
// The central panel the region left after adding TopPanel's and SidePanel's
|
||||||
ui.heading("eframe template");
|
ui.heading("eframe template");
|
||||||
|
|||||||
@ -5,9 +5,7 @@ use cpal::{
|
|||||||
BufferSize, StreamConfig,
|
BufferSize, StreamConfig,
|
||||||
};
|
};
|
||||||
use realfft::RealFftPlanner;
|
use realfft::RealFftPlanner;
|
||||||
use std::sync::mpsc::{self, Sender};
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use super::debug_plot::PlotData;
|
|
||||||
|
|
||||||
pub struct AudioFFT {
|
pub struct AudioFFT {
|
||||||
pub stream: cpal::Stream,
|
pub stream: cpal::Stream,
|
||||||
@ -15,10 +13,7 @@ pub struct AudioFFT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AudioFFT {
|
impl AudioFFT {
|
||||||
pub fn new(
|
pub fn new(size: usize) -> Result<(Self, mpsc::Receiver<Vec<u8>>)> {
|
||||||
size: usize,
|
|
||||||
plot_tx: Sender<(&'static str, PlotData)>,
|
|
||||||
) -> Result<(Self, mpsc::Receiver<Vec<u8>>)> {
|
|
||||||
let output_len = size / 2 + 1;
|
let output_len = size / 2 + 1;
|
||||||
|
|
||||||
// Create mpsc queue
|
// Create mpsc queue
|
||||||
@ -50,20 +45,9 @@ impl AudioFFT {
|
|||||||
assert_eq!(size, fft_in.len());
|
assert_eq!(size, fft_in.len());
|
||||||
fft.process_with_scratch(&mut fft_in, &mut fft_out, &mut fft_scratch)
|
fft.process_with_scratch(&mut fft_in, &mut fft_out, &mut fft_scratch)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
plot_tx
|
|
||||||
.send(("FFT Output", PlotData::Bode32(fft_out.clone())))
|
|
||||||
.unwrap();
|
|
||||||
fft_in.clear();
|
fft_in.clear();
|
||||||
let output: Vec<u8> = fft_out
|
let output: Vec<u8> = fft_out.iter().map(|c| (c.arg() * 255.0) as u8).collect();
|
||||||
.iter()
|
|
||||||
.map(|c| {
|
|
||||||
(((c.re * c.re) + (c.im * c.im)).sqrt() / size as f32 * 255.0) as u8
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
assert_eq!(output_len, output.len());
|
assert_eq!(output_len, output.len());
|
||||||
plot_tx
|
|
||||||
.send(("FFT Processed Output", PlotData::U8(output.clone())))
|
|
||||||
.unwrap();
|
|
||||||
tx.send(output).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