Skip to content

Commit

Permalink
add LUFS
Browse files Browse the repository at this point in the history
  • Loading branch information
aizcutei committed May 20, 2024
1 parent facf410 commit 5baf52e
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 114 deletions.
19 changes: 17 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,23 @@ impl NanometersApp {
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 {}

// 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>);
// }
// _ => {}
// }
return app;
}
Default::default()
Expand Down
10 changes: 4 additions & 6 deletions src/audio/callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crossbeam_channel::Sender;
// use std::sync::{Arc, Mutex};

pub fn get_callback(
tx_lrms: Sender<SendData>,
tx: Sender<SendData>,
buffer: Arc<Mutex<AudioSourceBuffer>>,
) -> Box<dyn FnMut(Vec<Vec<f32>>) + Send + Sync> {
Box::new(move |data: Vec<Vec<f32>>| {
Expand Down Expand Up @@ -73,14 +73,12 @@ pub fn get_callback(
//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.sum += iir_l * iir_l + 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);
send_data.iir_data.push(peak_buffer.sum / 4800.0);
}
}

Expand All @@ -90,7 +88,7 @@ pub fn get_callback(
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();
tx.send(send_data).unwrap();
})
}

Expand Down
5 changes: 3 additions & 2 deletions src/frame/main_frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,12 @@ impl NanometersApp {
}

let mut update_waveform_data = WaveformSendData::new();
let mut update_iir_data = IIRData::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.concat(&data.iir_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_waveform_data.concat(&data.waveform_data);
Expand Down
116 changes: 70 additions & 46 deletions src/frame/peak_frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ use crate::NanometersApp;
use egui::*;

impl NanometersApp {
pub fn peak_frame(&mut self, iir_data: &IIRData, db_data: &DBData, rect: Rect, ui: &mut Ui) {
pub fn peak_frame(&mut self, iir_data: &Vec<f32>, db_data: &DBData, rect: Rect, ui: &mut Ui) {
ui.painter().rect_filled(rect, 0.0, self.setting.theme.bg);
if iir_data.l.is_empty() || iir_data.r.is_empty() {
// DB
if !db_data.l.is_finite() {
let l_rect = Rect::from_two_pos(
pos2(rect.center().x - 12.0, self.peak.plot_l),
pos2(rect.center().x - 7.0, rect.max.y),
pos2(rect.center().x - 40.0, self.peak.plot_l),
pos2(rect.center().x - 34.0, rect.max.y),
);
let r_rect = Rect::from_two_pos(
pos2(rect.center().x - 6.0, self.peak.plot_r),
pos2(rect.center().x - 1.0, rect.max.y),
pos2(rect.center().x - 33.0, self.peak.plot_r),
pos2(rect.center().x - 27.0, rect.max.y),
);
ui.painter()
.rect_filled(l_rect, 0.0, self.setting.theme.main);
Expand All @@ -34,18 +35,18 @@ impl NanometersApp {
+ (1.0 - self.setting.peak.decay) * db_data.r;
}

let l_height = -rect.height() * (db_data.l / 60.0);
let r_height = -rect.height() * (db_data.r / 60.0);
let l_height = -rect.height() * (db_data.l / 60.0) - 5.0;
let r_height = -rect.height() * (db_data.r / 60.0) - 5.0;
self.peak.plot_l = l_height;
self.peak.plot_r = r_height;

let l_rect = Rect::from_two_pos(
pos2(rect.center().x - 12.0, l_height),
pos2(rect.center().x - 7.0, rect.max.y),
pos2(rect.center().x - 40.0, l_height),
pos2(rect.center().x - 34.0, rect.max.y),
);
let r_rect = Rect::from_two_pos(
pos2(rect.center().x - 6.0, r_height),
pos2(rect.center().x - 1.0, rect.max.y),
pos2(rect.center().x - 33.0, r_height),
pos2(rect.center().x - 27.0, rect.max.y),
);
ui.painter()
.rect_filled(l_rect, 0.0, self.setting.theme.main);
Expand All @@ -54,43 +55,66 @@ impl NanometersApp {
}

// LUFS
if !iir_data.l.is_empty() && !iir_data.r.is_empty() {
let len = iir_data.l.len();
for i in 0..len {
self.peak.data_buffer_l.push_back(iir_data.l[i]);
self.peak.data_buffer_r.push_back(iir_data.r[i]);
if self.peak.data_buffer_l.len() >= 4 {
let sigma = (self.peak.data_buffer_l.iter().sum::<f32>()
+ self.peak.data_buffer_r.iter().sum::<f32>())
/ 19200.0;
if sigma.log10() * 10.0 - 0.691 > -70.0 {
self.peak.past_3s.push_back(sigma);
} else {
self.peak.past_3s.push_back(0.0);
if !iir_data.is_empty() {
iir_data.iter().for_each(|&sample| {
self.peak.data_buffer.push_back(sample);

if self.peak.data_buffer.len() >= 4 {
let sigma: f32 = self.peak.data_buffer.iter().take(4).sum::<f32>() / 4.0;
let lkfs = sigma.log10() * 10.0 - 10.691;

self.peak
.past_1500ms
.push_back(if lkfs > -70.0 { sigma } else { 0.0 });

if self.peak.past_1500ms.len() > 12 {
self.peak.past_1500ms.pop_front();
}
self.peak.past_3s.pop_front();
self.peak.data_buffer_l.pop_front();
self.peak.data_buffer_r.pop_front();

self.peak.data_buffer.pop_front();
}
}
});
}
self.peak.lufs = self
let (count, sum) = self
.peak
.past_3s
.clone()
.into_iter()
.filter(|x| *x != 0.0)
.sum::<f32>()
.log10()
* 10.0
- 10.691;
// println!("{}", self.peak.lufs);
// ui.painter().text(
// rect.center(),
// Align2::CENTER_CENTER,
// format!("{}", self.peak.lufs),
// FontId::proportional(20.0),
// Color32::WHITE,
// );
.past_1500ms
.iter()
.filter(|&&x| x != 0.0)
.fold((0, 0.0), |(count, sum), &x| (count + 1, sum + x));
if count != 0 {
self.peak.lufs = (sum / count as f32).log10() * 10.0 - 0.691;
} else {
self.peak.lufs = f32::NEG_INFINITY;
}
if self.peak.lufs > -40.0 {
let lufs_rect = Rect::from_two_pos(
pos2(
rect.center().x - 22.0,
-rect.height() * (self.peak.lufs / 40.0),
),
pos2(rect.center().x - 12.0, rect.max.y),
);
let text_rect = Rect::from_two_pos(
pos2(
rect.center().x - 10.0,
10.0 - rect.height() * (self.peak.lufs / 40.0),
),
pos2(
rect.center().x + 48.0,
-rect.height() * (self.peak.lufs / 40.0) - 10.0,
),
);
ui.painter()
.rect_filled(lufs_rect, 0.0, self.setting.theme.main);
ui.painter()
.rect_filled(text_rect, 0.0, self.setting.theme.main);
ui.painter().text(
text_rect.center(),
Align2::CENTER_CENTER,
format!("{:.1}LU", self.peak.lufs),
FontId::monospace(12.0),
self.setting.theme.bg,
);
}
}
}
8 changes: 4 additions & 4 deletions src/frame/setting_frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ impl NanometersApp {
.changed()
{
self.audio_source.as_mut().unwrap().stop();
let tx_lrms = self.tx.clone().unwrap();
let callback = get_callback(tx_lrms, self.audio_source_buffer.clone());
let tx = self.tx.clone().unwrap();
let callback = get_callback(tx, self.audio_source_buffer.clone());
let mut system_capture = SystemCapture::new(callback);
system_capture.start();
self.audio_source = Some(Box::new(system_capture) as Box<dyn AudioSource>);
Expand All @@ -220,8 +220,8 @@ impl NanometersApp {
.changed()
{
self.audio_source.as_mut().unwrap().stop();
let tx_lrms = self.tx.clone().unwrap();
let callback = get_callback(tx_lrms, self.audio_source_buffer.clone());
let tx = self.tx.clone().unwrap();
let callback = get_callback(tx, self.audio_source_buffer.clone());
let mut plugin_client = PluginClient::new(callback);
plugin_client.start();
self.audio_source = Some(Box::new(plugin_client) as Box<dyn AudioSource>);
Expand Down
4 changes: 2 additions & 2 deletions src/frame/waveform_frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl NanometersApp {
match self.setting.waveform.mode {
WaveformMode::Static => Stroke::new(1.0, self.setting.theme.main),
WaveformMode::MultiBand => Stroke::new(
1.0,
1.1,
self.waveform
.plot_point
.ucolor
Expand Down Expand Up @@ -140,7 +140,7 @@ impl NanometersApp {
match self.setting.waveform.mode {
WaveformMode::Static => Stroke::new(1.0, self.setting.theme.main),
WaveformMode::MultiBand => Stroke::new(
1.0,
1.1,
self.waveform
.plot_point
.dcolor
Expand Down
26 changes: 19 additions & 7 deletions src/setting/peak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ pub struct Peak {
pub(crate) plot_r: f32,
pub(crate) lufs: f32,
#[serde(skip)]
pub(crate) past_3s: VecDeque<f32>,
pub(crate) past_1500ms: VecDeque<f32>,
#[serde(skip)]
pub(crate) data_buffer_l: VecDeque<f32>,
#[serde(skip)]
pub(crate) data_buffer_r: VecDeque<f32>,
pub(crate) data_buffer: VecDeque<f32>,
}

impl Default for Peak {
Expand All @@ -37,9 +35,23 @@ impl Default for Peak {
plot_l: 0.0,
plot_r: 0.0,
lufs: f32::NEG_INFINITY,
past_3s: vec![f32::NEG_INFINITY; 27].into(), //3000ms, 400ms per block, overlap 75%
data_buffer_l: VecDeque::new(),
data_buffer_r: VecDeque::new(),
past_1500ms: VecDeque::new(), //1500ms, 400ms per block, overlap 75%
data_buffer: VecDeque::new(),
}
}
}

#[derive(Debug, Clone, Default)]
pub struct DBData {
pub l: f32,
pub r: f32,
}

impl DBData {
pub fn new() -> Self {
Self {
l: f32::NEG_INFINITY,
r: f32::NEG_INFINITY,
}
}
}
Loading

0 comments on commit 5baf52e

Please sign in to comment.