generated from lks/eframe_template_android
Add simple device selector window
This commit is contained in:
parent
243fcdb31e
commit
f3216e2d8d
68
src/app.rs
68
src/app.rs
@ -1,13 +1,15 @@
|
||||
use eframe::{egui_glow, glow};
|
||||
use egui::mutex::Mutex;
|
||||
use egui::{mutex::Mutex, ScrollArea};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::backend::{self, Backends};
|
||||
|
||||
pub mod debug_plot;
|
||||
use debug_plot::DebugPlots;
|
||||
mod waterfall;
|
||||
use waterfall::Waterfall;
|
||||
mod audio;
|
||||
use audio::Audio;
|
||||
//mod aud1o;
|
||||
//use aud1o::Audio;
|
||||
mod fft;
|
||||
use fft::Fft;
|
||||
pub mod turbo_colormap;
|
||||
@ -21,8 +23,11 @@ pub struct TemplateApp {
|
||||
value: f32,
|
||||
/// Behind an `Arc<Mutex<…>>` so we can pass it to [`egui::PaintCallback`] and paint later.
|
||||
waterfall: Arc<Mutex<Waterfall>>,
|
||||
_stream: Audio,
|
||||
//_stream: Audio,
|
||||
_fft: Fft,
|
||||
_backends: backend::Backends,
|
||||
_selected_backend: usize,
|
||||
_open_device: Option<Box<dyn backend::Device>>,
|
||||
}
|
||||
|
||||
impl TemplateApp {
|
||||
@ -38,7 +43,7 @@ impl TemplateApp {
|
||||
|
||||
//let (stream, rx) = AudioFFT::new(FFT_SIZE, plots.get_sender()).unwrap();
|
||||
let (fft, rx) = Fft::new(FFT_SIZE, plots.get_sender()).unwrap();
|
||||
let stream = Audio::new(fft.tx.clone(), plots.get_sender()).unwrap();
|
||||
//let stream = Audio::new(fft.tx.clone(), plots.get_sender()).unwrap();
|
||||
|
||||
let wf_size = fft.output_len;
|
||||
let gl = cc
|
||||
@ -52,8 +57,11 @@ impl TemplateApp {
|
||||
label: "Hello World!".to_owned(),
|
||||
value: 2.7,
|
||||
waterfall: Arc::new(Mutex::new(Waterfall::new(gl, wf_size, wf_size, rx))),
|
||||
_stream: stream,
|
||||
//_stream: stream,
|
||||
_fft: fft,
|
||||
_backends: Backends::default(),
|
||||
_selected_backend: 0,
|
||||
_open_device: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -100,6 +108,54 @@ impl eframe::App for TemplateApp {
|
||||
|
||||
self.plots.render_plot_windows(ctx);
|
||||
|
||||
egui::Window::new("Select Device")
|
||||
.default_width(600.0)
|
||||
.default_height(400.0)
|
||||
.vscroll(false)
|
||||
.resizable(true)
|
||||
.show(ctx, |ui| {
|
||||
egui::SidePanel::left("Select Driver")
|
||||
.resizable(true)
|
||||
.default_width(150.0)
|
||||
.width_range(80.0..=200.0)
|
||||
.show_inside(ui, |ui| {
|
||||
ScrollArea::vertical().show(ui, |ui| {
|
||||
ui.with_layout(
|
||||
egui::Layout::top_down_justified(egui::Align::LEFT),
|
||||
|ui| {
|
||||
for (i, b) in self._backends.0.iter().enumerate() {
|
||||
ui.selectable_value(
|
||||
&mut self._selected_backend,
|
||||
i,
|
||||
b.display_text(),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
//egui::CentralPanel::default().show_inside(ui, |ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
//if self._selected_backend < self._backends.0.len() {
|
||||
if let Some(b) = self._backends.0.get_mut(self._selected_backend) {
|
||||
//let mut b = &self._backends.0[self._selected_backend];
|
||||
b.show_device_selection(ui);
|
||||
if ui.add(egui::Button::new("Apply")).clicked() {
|
||||
drop(self._open_device.take());
|
||||
if let Ok(device) =
|
||||
b.build_device(self._fft.tx.clone(), self.plots.get_sender())
|
||||
{
|
||||
self._open_device = Some(device);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ui.add(egui::Label::new("Select a Device Driver"));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
// The central panel the region left after adding TopPanel's and SidePanel's
|
||||
ui.heading("eframe template");
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use cpal::{
|
||||
self,
|
||||
traits::{DeviceTrait, HostTrait},
|
||||
BufferSize, StreamConfig,
|
||||
};
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use super::debug_plot::PlotData;
|
||||
|
||||
pub struct Audio {
|
||||
pub stream: cpal::Stream,
|
||||
}
|
||||
|
||||
impl Audio {
|
||||
pub fn new(
|
||||
fft_input: Sender<Vec<f32>>,
|
||||
_plot_tx: Sender<(&'static str, PlotData)>,
|
||||
) -> Result<Self> {
|
||||
// Setup audio input
|
||||
let host = cpal::default_host();
|
||||
let device = host
|
||||
.default_input_device()
|
||||
.ok_or(anyhow!("No input audio device found"))?;
|
||||
// Basic config that 'should' be suppoted by most devices
|
||||
let config = StreamConfig {
|
||||
channels: 1,
|
||||
sample_rate: cpal::SampleRate(44100),
|
||||
buffer_size: BufferSize::Default,
|
||||
};
|
||||
|
||||
let stream = device.build_input_stream(
|
||||
&config,
|
||||
move |data: &[f32], _: &cpal::InputCallbackInfo| {
|
||||
fft_input.send(data.to_vec()).unwrap();
|
||||
},
|
||||
move |err| log::error!("Audio Thread Error: {err}"),
|
||||
None,
|
||||
)?;
|
||||
|
||||
Ok(Self { stream })
|
||||
}
|
||||
}
|
||||
106
src/backend/audio.rs
Normal file
106
src/backend/audio.rs
Normal file
@ -0,0 +1,106 @@
|
||||
use anyhow::Result;
|
||||
use cpal::{
|
||||
self,
|
||||
traits::{DeviceTrait, HostTrait},
|
||||
BufferSize,
|
||||
};
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use crate::app::debug_plot::PlotData;
|
||||
|
||||
pub struct Audio {
|
||||
pub stream: cpal::Stream,
|
||||
}
|
||||
impl Audio {
|
||||
pub fn new(
|
||||
device: &cpal::Device,
|
||||
config: cpal::StreamConfig,
|
||||
fft_input: Sender<Vec<f32>>,
|
||||
_plot_tx: Sender<(&'static str, PlotData)>,
|
||||
) -> Result<Self> {
|
||||
let stream = device.build_input_stream(
|
||||
&config,
|
||||
move |data: &[f32], _: &cpal::InputCallbackInfo| {
|
||||
fft_input.send(data.to_vec()).unwrap();
|
||||
},
|
||||
move |err| log::error!("Audio Thread Error: {err}"),
|
||||
None,
|
||||
)?;
|
||||
|
||||
Ok(Self { stream })
|
||||
}
|
||||
}
|
||||
impl crate::backend::Device for Audio {
|
||||
fn show_settings(&mut self, _ui: &mut egui::Ui) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn can_tune(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn tune(&mut self, _freq: usize) -> anyhow::Result<()> {
|
||||
anyhow::bail!("Can't tune this device")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AudioBackend {
|
||||
host: cpal::Host,
|
||||
devices: Vec<cpal::Device>,
|
||||
current_device: usize,
|
||||
}
|
||||
impl AudioBackend {
|
||||
pub fn new() -> Self {
|
||||
let host = cpal::default_host();
|
||||
let devices = host.devices().unwrap().collect();
|
||||
let current_device = 0;
|
||||
Self {
|
||||
host,
|
||||
devices,
|
||||
current_device,
|
||||
}
|
||||
}
|
||||
fn update_devices(&mut self) {
|
||||
self.devices.clear();
|
||||
self.devices = self.host.devices().unwrap().collect();
|
||||
self.current_device = 0;
|
||||
}
|
||||
}
|
||||
impl super::Backend for AudioBackend {
|
||||
fn display_text(&self) -> &'static str {
|
||||
"Audio"
|
||||
}
|
||||
|
||||
fn show_device_selection(&mut self, ui: &mut egui::Ui) {
|
||||
egui::ComboBox::from_label("Device")
|
||||
.selected_text(
|
||||
self.devices[self.current_device]
|
||||
.name()
|
||||
.unwrap_or("UNKNOWN DEVICE".into()),
|
||||
)
|
||||
.show_index(ui, &mut self.current_device, self.devices.len(), |i| {
|
||||
self.devices[i].name().unwrap_or("UNKNOWN DEVICE".into())
|
||||
});
|
||||
if ui.add(egui::Button::new("Refresh")).clicked() {
|
||||
self.update_devices();
|
||||
}
|
||||
}
|
||||
|
||||
fn build_device(
|
||||
&mut self,
|
||||
fft_input: Sender<Vec<f32>>,
|
||||
_plot_tx: Sender<(&'static str, PlotData)>,
|
||||
) -> anyhow::Result<Box<dyn super::Device>> {
|
||||
let config = cpal::StreamConfig {
|
||||
channels: 1,
|
||||
sample_rate: cpal::SampleRate(44100),
|
||||
buffer_size: BufferSize::Default,
|
||||
};
|
||||
Ok(Box::new(Audio::new(
|
||||
&self.devices[self.current_device],
|
||||
config,
|
||||
fft_input,
|
||||
_plot_tx,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
42
src/backend/mod.rs
Normal file
42
src/backend/mod.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use egui::Ui;
|
||||
|
||||
use crate::app::debug_plot::PlotData;
|
||||
mod audio;
|
||||
pub trait Device {
|
||||
fn show_settings(&mut self, ui: &mut Ui);
|
||||
fn can_tune(&self) -> bool;
|
||||
fn tune(&mut self, freq: usize) -> anyhow::Result<()>;
|
||||
}
|
||||
pub trait Backend {
|
||||
fn display_text(&self) -> &'static str;
|
||||
fn show_device_selection(&mut self, ui: &mut Ui);
|
||||
fn build_device(
|
||||
&mut self,
|
||||
fft_input: Sender<Vec<f32>>,
|
||||
_plot_tx: Sender<(&'static str, PlotData)>,
|
||||
) -> anyhow::Result<Box<dyn Device>>;
|
||||
}
|
||||
pub struct Backends(pub Vec<Box<dyn Backend>>);
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
|
||||
impl Default for Backends {
|
||||
fn default() -> Self {
|
||||
Backends(vec![Box::new(audio::AudioBackend::new())])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
impl Default for Backends {
|
||||
fn default() -> Self {
|
||||
Backends(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
impl Default for Backends {
|
||||
fn default() -> Self {
|
||||
Backends(vec![])
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
#![warn(clippy::all, rust_2018_idioms)]
|
||||
|
||||
pub mod app;
|
||||
mod backend;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[no_mangle]
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
mod app;
|
||||
use app::TemplateApp;
|
||||
mod backend;
|
||||
|
||||
//#[cfg(target_os = "android")]
|
||||
//fn main() {}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user