Skip to content

Commit

Permalink
update waveform
Browse files Browse the repository at this point in the history
  • Loading branch information
aizcutei committed May 19, 2024
1 parent a1e1173 commit 8198e09
Show file tree
Hide file tree
Showing 32 changed files with 1,001 additions and 415 deletions.
Binary file modified .DS_Store
Binary file not shown.
Empty file added .zed/settings.json
Empty file.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@ rust-version = "1.78"
[dependencies]
egui = {version = "0.27.2", features = ["rayon"]}
eframe = { version = "0.27.2", default-features = false, features = [
"default_fonts",
"default_fonts",
"glow",
"wgpu",
"persistence",
"puffin",
] }
serde = { version = "1", features = ["derive"] }
cpal = {version = "0.15.3", exclude = ["js-sys"]}
interprocess = "1.2.1"
env_logger = { version = "0.11", default-features = false, features = [
"auto-color",
"humantime",
] }
cpal = {version = "0.15.3", exclude = ["js-sys"]}
anyhow = "1.0"
rustfft = "6.2.0"
realfft = "3.3.0"
ruhear = "0.1.0"
log = "0.4"
rayon = "1.10"
Expand Down
Binary file modified assets/favicon.ico
100755 → 100644
Binary file not shown.
Binary file modified assets/icon-1024.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/icon-256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/icon_ios_touch_192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/maskable_icon_x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion rust-toolchain
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
# to the user in the error, instead of "error: invalid channel name '[toolchain]'".

[toolchain]
channel = "1.78.0"
channel = "nightly"
components = [ "rustfmt", "clippy" ]
46 changes: 22 additions & 24 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ use crate::audio::*;
use crate::frame::*;
use crate::setting::*;
use crate::utils::*;
use crate::AudioSource;
use crate::RingBuffer;

use crossbeam_channel::unbounded;
use crossbeam_channel::{Receiver, Sender};
use eframe::egui::{self, ViewportCommand};
use eframe::wgpu::core::storage;
use eframe::wgpu::rwh::HasWindowHandle;
use egui::*;
use rayon::prelude::*;
Expand All @@ -26,10 +25,15 @@ pub struct NanometersApp {
pub(crate) frame_history: FrameHistory,

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

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

#[serde(skip)]
pub(crate) audio_source_buffer: Arc<Mutex<AudioSourceBuffer>>,

pub(crate) color_lut_129: Vec<Color32>,

pub(crate) setting: Setting,

Expand All @@ -44,31 +48,20 @@ pub struct NanometersApp {

impl Default for NanometersApp {
fn default() -> Self {
let (tx_lrms, rx_lrms) = unbounded();
let tx_lrms_save = Some(tx_lrms.clone());
let rx_lrms_save = Some(rx_lrms.clone());
let callback = Box::new(move |data: Vec<Vec<f32>>| {
#[cfg(feature = "puffin")]
puffin::profile_scope!("callback");
let mut send_data = RawData::new();
data[0].iter().zip(&data[1]).for_each(|(l, r)| {
send_data.push_l(*l);
send_data.push_r(*r);
send_data.push_m((l + r) / 2.0);
send_data.push_s((l - r) / 2.0);
});
tx_lrms.send(send_data).unwrap();
});

let mut system_capture = SystemCapture::new(callback);
let (tx, rx) = unbounded();
let audio_source_buffer = Arc::new(Mutex::new(AudioSourceBuffer::new()));
let mut system_capture =
SystemCapture::new(get_callback(tx.clone(), audio_source_buffer.clone()));
system_capture.start();
let audio_source = Some(Box::new(system_capture) as Box<dyn AudioSource>);

Self {
audio_source,
frame_history: Default::default(),
tx_lrms: tx_lrms_save,
rx_lrms: rx_lrms_save,
tx: Some(tx),
rx: Some(rx),
audio_source_buffer,
color_lut_129: color_lut_129(),
setting: Default::default(),
setting_switch: false,
allways_on_top: false,
Expand All @@ -87,7 +80,12 @@ impl NanometersApp {
// `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`.

if let Some(storage) = cc.storage {
return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default();
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 {}

return app;
}
Default::default()
}
Expand Down
4 changes: 4 additions & 0 deletions src/audio.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
pub(crate) mod audio_source;
pub(crate) mod callback;
pub(crate) mod plugin_client;
pub(crate) mod system_capture;
pub(crate) mod system_input;

pub use audio_source::*;
pub use callback::*;
pub use plugin_client::*;
pub use system_capture::*;
pub use system_input::*;
5 changes: 5 additions & 0 deletions src/audio/audio_source.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub trait AudioSource {
fn get_name(&self) -> String;
fn start(&mut self);
fn stop(&mut self);
}
103 changes: 103 additions & 0 deletions src/audio/callback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use realfft::RealFftPlanner;

use std::sync::{Arc, Mutex};

use crate::utils::*;
use crossbeam_channel::Sender;
// use std::collections::VecDeque;
// use std::sync::{Arc, Mutex};

pub fn get_callback(
tx_lrms: Sender<SendData>,
buffer: Arc<Mutex<AudioSourceBuffer>>,
) -> Box<dyn FnMut(Vec<Vec<f32>>) + Send + Sync> {
Box::new(move |data: Vec<Vec<f32>>| {
#[cfg(feature = "puffin")]
puffin::profile_scope!("callback");

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

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 {
// 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.index += 1;
if buffer.waveform.index >= block_length {
let mut waveform_buffer = buffer.waveform.clone();
buffer.waveform.reset();
send_data.waveform_data.l.push(waveform_buffer.l);
send_data.waveform_data.r.push(waveform_buffer.r);
send_data.waveform_data.m.push(waveform_buffer.m);
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 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()));
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()));
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()));
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()));
}

// Peak
// DB
amp_l += data[0][i].abs();
amp_r += data[1][i].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);
buffer.peak.sum_l += iir_l * iir_l;
buffer.peak.sum_r += iir_r * iir_r;
buffer.peak.index += 1;
if buffer.peak.index >= 4800 {
let peak_buffer = buffer.peak.clone();
buffer.peak.reset_sum();
send_data.iir_data.l.push(peak_buffer.sum_l);
send_data.iir_data.r.push(peak_buffer.sum_r);
}
}

//DB
amp_l /= len as f32;
amp_r /= len as f32;
send_data.db_data.l = gain_to_db(amp_l);
send_data.db_data.r = gain_to_db(amp_r);

tx_lrms.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: 2 additions & 2 deletions src/audio/plugin_client.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::AudioSource;
use crate::audio::AudioSource;
use interprocess::local_socket::{LocalSocketStream, NameTypeSupport};
use std::io::Read;
use std::sync::mpsc;
Expand Down Expand Up @@ -69,7 +69,7 @@ impl AudioSource for PluginClient {
continue;
}
};
let mut update_buf: Vec<f32> = Vec::new();
let mut update_buf;
match conn.read(&mut buf) {
Ok(_) => {
let buffer = buf
Expand Down
9 changes: 2 additions & 7 deletions src/audio/system_capture.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
use crate::AudioSource;
use crate::audio::AudioSource;
use ruhear::RUHear;
use std::sync::{Arc, Mutex};

pub struct SystemCapture {
name: String,
ruhear: RUHear,
callback: Arc<Mutex<dyn FnMut(Vec<Vec<f32>>) + Send>>,
}

impl SystemCapture {
pub fn new(callback: Box<dyn FnMut(Vec<Vec<f32>>) + Send>) -> Self {
let name = "System Default Output".to_string();
let callback = Arc::new(Mutex::new(callback));
let ruhear = RUHear::new(callback.clone());
Self {
name,
ruhear,
callback,
}
Self { name, ruhear }
}
}

Expand Down
76 changes: 69 additions & 7 deletions src/audio/system_input.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,79 @@
#![allow(unused)]
// use crate::AudioSource;
use crate::audio::AudioSource;
// use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use std::sync::{Arc, Mutex};

pub struct SystemInput {
name: String,
callback: Arc<Mutex<dyn FnMut(Vec<Vec<f32>>) + Send>>,
stream: cpal::Stream,
host: cpal::Host,
device: cpal::Device,
format: cpal::SupportedStreamConfig,
stream: Option<cpal::Stream>,
}

// impl SystemInput {
// pub fn new(callback: Box<dyn FnMut(Vec<Vec<f32>>) + Send>) -> Self {
// let name = "System Microphone".to_string();
impl SystemInput {
pub fn new(callback: Box<dyn FnMut(Vec<Vec<f32>>) + Send>) -> Self {
let name = "System Default Microphone".to_string();
let host = cpal::default_host();
let device = host.default_input_device().unwrap();
let format = device.default_input_config().unwrap();
Self {
name,
callback: Arc::new(Mutex::new(callback)),
host,
device,
format,
stream: None,
}
}
}

impl AudioSource for SystemInput {
fn get_name(&self) -> String {
self.name.clone()
}

// }
// }
fn start(&mut self) {
if self.stream.is_none() {
let callback = self.callback.clone();
let channels = &self.format.channels().clone();
let channels = *channels as usize;
let stream = match self.format.sample_format() {
cpal::SampleFormat::F32 => self.device.build_input_stream(
&self.format.config(),
move |data: &[f32], &_| {
let mut bufs = vec![vec![]; channels];
for (i, sample) in data.chunks(channels).enumerate() {
for (j, &channel) in sample.iter().enumerate() {
bufs[j].push(channel as f32);
}
}
if let Ok(mut callback) = callback.lock() {
(*callback)(bufs);
}
},
|err| {
eprintln!("an error occurred on stream: {}", err);
},
None,
),
sample_format => {
panic!("unsupported format {:?}", sample_format);
}
}
.unwrap();
self.stream = Some(stream);
}
if let Some(stream) = &self.stream {
stream.play();
}
}

fn stop(&mut self) {
if let Some(stream) = &self.stream {
stream.pause().unwrap();
}
}
}
Loading

0 comments on commit 8198e09

Please sign in to comment.