Skip to content

Commit

Permalink
add stereometer
Browse files Browse the repository at this point in the history
  • Loading branch information
aizcutei committed May 23, 2024
1 parent 5baf52e commit ba84a79
Show file tree
Hide file tree
Showing 20 changed files with 710 additions and 176 deletions.
Binary file modified .DS_Store
Binary file not shown.
Binary file added src/.DS_Store
Binary file not shown.
60 changes: 37 additions & 23 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use eframe::wgpu::core::storage;
use eframe::wgpu::rwh::HasWindowHandle;
use egui::*;
use rayon::prelude::*;
use std::sync::atomic::AtomicU32;
use std::sync::{Arc, Mutex};
use std::{thread, vec};

Expand All @@ -20,22 +21,25 @@ use std::{thread, vec};
pub struct NanometersApp {
#[serde(skip)]
pub(crate) audio_source: Option<Box<dyn AudioSource>>,

#[serde(skip)]
pub(crate) frame_history: FrameHistory,

#[serde(skip)]
pub(crate) tx: Option<Sender<SendData>>,

#[serde(skip)]
pub(crate) rx: Option<Receiver<SendData>>,

// #[serde(skip)]
// pub(crate) tx_setting: Option<Sender<AudioSourceSetting>>,
// #[serde(skip)]
// pub(crate) rx_setting: Option<Receiver<AudioSourceSetting>>,
#[serde(skip)]
pub(crate) audio_source_buffer: Arc<Mutex<AudioSourceBuffer>>,

pub(crate) color_lut_129: Vec<Color32>,
#[serde(skip)]
pub(crate) audio_source_setting: Arc<Mutex<Setting>>,

pub(crate) setting: Setting,
pub(crate) sample_rate: AtomicU32,

pub(crate) setting_switch: bool,
pub(crate) allways_on_top: bool,
Expand All @@ -44,12 +48,16 @@ pub struct NanometersApp {

pub(crate) waveform: Waveform,
pub(crate) peak: Peak,
pub(crate) stereo: Stereo,
}

impl Default for NanometersApp {
fn default() -> Self {
let (tx, rx) = unbounded();
// let (tx_setting, rx_setting) = unbounded();
let audio_source_buffer = Arc::new(Mutex::new(AudioSourceBuffer::new()));
let setting = Setting::default();
let audio_source_setting = Arc::new(Mutex::new(setting.clone()));
let mut system_capture =
SystemCapture::new(get_callback(tx.clone(), audio_source_buffer.clone()));
system_capture.start();
Expand All @@ -60,15 +68,19 @@ impl Default for NanometersApp {
frame_history: Default::default(),
tx: Some(tx),
rx: Some(rx),
// tx_setting: Some(tx_setting),
// rx_setting: Some(rx_setting),
audio_source_buffer,
color_lut_129: color_lut_129(),
setting: Default::default(),
audio_source_setting,
setting,
sample_rate: AtomicU32::new(48000),
setting_switch: false,
allways_on_top: false,
meter_size: Rect::from_two_pos([0.0, 0.0].into(), [600.0, 200.0].into()),
meters_rects: vec![],
waveform: Default::default(),
peak: Default::default(),
stereo: Default::default(),
}
}
}
Expand All @@ -78,28 +90,30 @@ impl NanometersApp {
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
// This is also where you can customize the look and feel of egui using
// `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`.
let version = env!("CARGO_PKG_VERSION").to_string();

if let Some(storage) = cc.storage {
let mut app: NanometersApp =
eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default();
cc.egui_ctx.set_visuals(set_theme(&mut app));
// match app.setting.audio_device.device {
// SystemCapture => {
// let tx = app.tx.clone().unwrap();
// let callback = get_callback(tx, app.audio_source_buffer.clone());
// let mut system_capture = SystemCapture::new(callback);
// system_capture.start();
// app.audio_source = Some(Box::new(system_capture) as Box<dyn AudioSource>);
// }
// PluginClient => {
// let tx = app.tx.clone().unwrap();
// let callback = get_callback(tx, app.audio_source_buffer.clone());
// let mut plugin_client = PluginClient::new(callback);
// plugin_client.start();
// app.audio_source = Some(Box::new(plugin_client) as Box<dyn AudioSource>);
// }
// _ => {}
// }
match app.setting.audio_device.device {
AudioDevice::OutputCapture => {
let tx = app.tx.clone().unwrap();
let callback = get_callback(tx, app.audio_source_buffer.clone());
let mut system_capture = SystemCapture::new(callback);
system_capture.start();
app.audio_source = Some(Box::new(system_capture) as Box<dyn AudioSource>);
}
AudioDevice::PluginCapture => {
let tx = app.tx.clone().unwrap();
let callback = get_callback(tx, app.audio_source_buffer.clone());
let mut plugin_client = PluginClient::new(callback);
plugin_client.start();
app.audio_source = Some(Box::new(plugin_client) as Box<dyn AudioSource>);
}
_ => {}
}
app.audio_source_setting = Arc::new(Mutex::new(app.setting.clone()));
return app;
}
Default::default()
Expand Down
94 changes: 61 additions & 33 deletions src/audio/callback.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use realfft::RealFftPlanner;
use crate::utils::*;

use crossbeam_channel::Sender;
use egui::Pos2;
use realfft::RealFftPlanner;
use rustfft::num_traits::{real::Real, Pow};
use std::sync::{Arc, Mutex};

use crate::utils::*;
use crossbeam_channel::Sender;
// use std::collections::VecDeque;
// use std::sync::{Arc, Mutex};
const SQRT_2: f32 = 1.4142135;

pub fn get_callback(
tx: Sender<SendData>,
Expand All @@ -16,20 +17,52 @@ pub fn get_callback(
puffin::profile_scope!("callback");

let mut buffer = buffer.lock().unwrap();
let block_length = 256;
let waveform_block_length = 256;
let stereo_block_length = 1;

let mut send_data = SendData::new();
let len = data[0].len();
let mut amp_l = 0.0;
let mut amp_r = 0.0;
for i in 0..len {
let l = data[0][i];
let r = data[1][i];
let m = (l + r) / 2.0;
let s = (l - r) / 2.0;
// Stereo
buffer.stereo.update(l, r);
if buffer.stereo.index >= stereo_block_length {
buffer.stereo.index = 0;
send_data.stereo_data.lissa.push(Pos2::new(l, r));
send_data
.stereo_data
.linear
.push(Pos2::new(-SQRT_2 * s, -SQRT_2 * m));

let length = (l * l + r * r).sqrt();
let log_x = if length.log10() >= -3.0 {
(length.log10() / 3.0 + 1.0) * l / length
} else {
0.0
};
let log_y = if length.log10() >= -3.0 {
(length.log10() / 3.0 + 1.0) * r / length
} else {
0.0
};
send_data.stereo_data.log.push(Pos2::new(
0.7071067812 * (log_y - log_x),
-0.7071067812 * (log_x + log_y),
));
}

// Waveform
buffer.waveform.update_l(data[0][i]);
buffer.waveform.update_r(data[1][i]);
buffer.waveform.update_m((data[0][i] + data[1][i]) / 2.0);
buffer.waveform.update_s((data[0][i] - data[1][i]) / 2.0);
buffer.waveform.update_l(l);
buffer.waveform.update_r(r);
buffer.waveform.update_m(m);
buffer.waveform.update_s(s);
buffer.waveform.index += 1;
if buffer.waveform.index >= block_length {
if buffer.waveform.index >= waveform_block_length {
let mut waveform_buffer = buffer.waveform.clone();
buffer.waveform.reset();
send_data.waveform_data.l.push(waveform_buffer.l);
Expand All @@ -38,41 +71,41 @@ pub fn get_callback(
send_data.waveform_data.s.push(waveform_buffer.s);

let mut real_planner = RealFftPlanner::<f32>::new();
let r2c = real_planner.plan_fft_forward(block_length);
let r2c = real_planner.plan_fft_forward(waveform_block_length);
let mut spectrum = r2c.make_output_vec();
r2c.process(&mut waveform_buffer.raw.l, &mut spectrum)
.unwrap();
send_data
.waveform_data
.l_freq
.push(max_index(spectrum.iter().map(|x| x.norm()).collect()));
.l_color
.push(multiband_color(spectrum.iter().map(|x| x.norm()).collect()));
r2c.process(&mut waveform_buffer.raw.r, &mut spectrum)
.unwrap();
send_data
.waveform_data
.r_freq
.push(max_index(spectrum.iter().map(|x| x.norm()).collect()));
.r_color
.push(multiband_color(spectrum.iter().map(|x| x.norm()).collect()));
r2c.process(&mut waveform_buffer.raw.m, &mut spectrum)
.unwrap();
send_data
.waveform_data
.m_freq
.push(max_index(spectrum.iter().map(|x| x.norm()).collect()));
.m_color
.push(multiband_color(spectrum.iter().map(|x| x.norm()).collect()));
r2c.process(&mut waveform_buffer.raw.s, &mut spectrum)
.unwrap();
send_data
.waveform_data
.s_freq
.push(max_index(spectrum.iter().map(|x| x.norm()).collect()));
.s_color
.push(multiband_color(spectrum.iter().map(|x| x.norm()).collect()));
}

// Peak
// DB
amp_l += data[0][i].abs();
amp_r += data[1][i].abs();
amp_l += l.abs();
amp_r += r.abs();
//IIR
let iir_l = combined_filter(data[0][i], &mut buffer.peak.iir_l);
let iir_r = combined_filter(data[1][i], &mut buffer.peak.iir_r);
let iir_l = combined_filter(l, &mut buffer.peak.iir_l);
let iir_r = combined_filter(r, &mut buffer.peak.iir_r);
buffer.peak.sum += iir_l * iir_l + iir_r * iir_r;
buffer.peak.index += 1;
if buffer.peak.index >= 4800 {
Expand All @@ -81,8 +114,11 @@ pub fn get_callback(
send_data.iir_data.push(peak_buffer.sum / 4800.0);
}
}
// Stereo
send_data.stereo_data.max = buffer.stereo.max;
buffer.stereo.max = f32::NEG_INFINITY;

//DB
// DB
amp_l /= len as f32;
amp_r /= len as f32;
send_data.db_data.l = gain_to_db(amp_l);
Expand All @@ -91,11 +127,3 @@ pub fn get_callback(
tx.send(send_data).unwrap();
})
}

fn max_index(data: Vec<f32>) -> usize {
data.iter()
.enumerate()
.max_by(|&(_, a), &(_, b)| a.partial_cmp(b).unwrap())
.map(|(index, _)| index)
.unwrap_or(0)
}
4 changes: 4 additions & 0 deletions src/frame.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
#![allow(unused)]
pub(crate) mod main_frame;
pub(crate) mod meters;
pub(crate) mod oscilloscope_frame;
pub(crate) mod peak_frame;
pub(crate) mod resize;
pub(crate) mod setting_frame;
pub(crate) mod stereogram_frame;
pub(crate) mod waveform_frame;

pub use main_frame::*;
pub use meters::*;
pub use oscilloscope_frame::*;
pub use peak_frame::*;
pub use resize::*;
pub use setting_frame::*;
pub use stereogram_frame::*;
pub use waveform_frame::*;
15 changes: 12 additions & 3 deletions src/frame/main_frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,24 @@ impl NanometersApp {
}

let mut update_waveform_data = WaveformSendData::new();

let mut update_stereogram_data = StereoSendData::new();
let mut update_iir_data = Vec::new();
let mut update_db_data = DBData::new();

self.rx.as_mut().unwrap().try_iter().for_each(|data| {
update_iir_data.extend_from_slice(&data.iir_data);
update_db_data.l = data.db_data.l;
update_db_data.r = data.db_data.r;
update_stereogram_data.max = data.stereo_data.max;
update_stereogram_data
.lissa
.extend_from_slice(&data.stereo_data.lissa);
update_stereogram_data
.linear
.extend_from_slice(&data.stereo_data.linear);
update_stereogram_data
.log
.extend_from_slice(&data.stereo_data.log);
update_waveform_data.concat(&data.waveform_data);
});

Expand All @@ -100,7 +110,7 @@ impl NanometersApp {
self.spectrum_frame(meter_rect, ui);
}
ModuleList::Stereogram => {
self.stereogram_frame(meter_rect, ui);
self.stereogram_frame(&update_stereogram_data, meter_rect, ui);
}
}
}
Expand Down Expand Up @@ -195,7 +205,6 @@ impl NanometersApp {

self.device_setting_block(ui);
self.theme_setting_block(ui);
self.peak_setting_block(ui);
self.cpu_setting_block(ui);
ui.end_row();
});
Expand Down
10 changes: 0 additions & 10 deletions src/frame/meters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,8 @@ impl NanometersApp {
painter.rect_filled(rect, 0.0, Color32::RED);
}

pub fn oscilloscope_frame(&mut self, rect: eframe::epaint::Rect, ui: &mut Ui) {
let painter = ui.painter();
painter.rect_filled(rect, 0.0, Color32::YELLOW);
}

pub fn spectrum_frame(&mut self, rect: eframe::epaint::Rect, ui: &mut Ui) {
let painter = ui.painter();
painter.rect_filled(rect, 0.0, Color32::KHAKI);
}

pub fn stereogram_frame(&mut self, rect: eframe::epaint::Rect, ui: &mut Ui) {
let painter = ui.painter();
painter.rect_filled(rect, 0.0, Color32::BROWN);
}
}
8 changes: 8 additions & 0 deletions src/frame/oscilloscope_frame.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use crate::NanometersApp;
use egui::*;

impl NanometersApp {
pub fn oscilloscope_frame(&mut self, rect: eframe::epaint::Rect, ui: &mut Ui) {
ui.painter().rect_filled(rect, 0.0, self.setting.theme.bg);
}
}
Loading

0 comments on commit ba84a79

Please sign in to comment.