flesh out sequencer
This commit is contained in:
@@ -3,6 +3,10 @@ use ratatui::layout::Rect;
|
||||
use ratatui::style::Color;
|
||||
use ratatui::widgets::Widget;
|
||||
|
||||
const DB_MIN: f32 = -48.0;
|
||||
const DB_MAX: f32 = 3.0;
|
||||
const DB_RANGE: f32 = DB_MAX - DB_MIN;
|
||||
|
||||
pub struct VuMeter {
|
||||
left: f32,
|
||||
right: f32,
|
||||
@@ -13,10 +17,22 @@ impl VuMeter {
|
||||
Self { left, right }
|
||||
}
|
||||
|
||||
fn level_to_color(level: f32) -> Color {
|
||||
if level > 0.9 {
|
||||
fn amplitude_to_db(amp: f32) -> f32 {
|
||||
if amp <= 0.0 {
|
||||
DB_MIN
|
||||
} else {
|
||||
(20.0 * amp.log10()).clamp(DB_MIN, DB_MAX)
|
||||
}
|
||||
}
|
||||
|
||||
fn db_to_normalized(db: f32) -> f32 {
|
||||
(db - DB_MIN) / DB_RANGE
|
||||
}
|
||||
|
||||
fn row_to_color(row_position: f32) -> Color {
|
||||
if row_position > 0.9 {
|
||||
Color::Red
|
||||
} else if level > 0.7 {
|
||||
} else if row_position > 0.75 {
|
||||
Color::Yellow
|
||||
} else {
|
||||
Color::Green
|
||||
@@ -26,40 +42,38 @@ impl VuMeter {
|
||||
|
||||
impl Widget for VuMeter {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
if area.width < 2 || area.height == 0 {
|
||||
if area.width < 3 || area.height == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let height = area.height as usize;
|
||||
let left_col = area.x;
|
||||
let right_col = area.x + area.width - 1;
|
||||
let half_width = area.width / 2;
|
||||
let gap = 1u16;
|
||||
|
||||
let left_level = (self.left.clamp(0.0, 1.0) * height as f32) as usize;
|
||||
let right_level = (self.right.clamp(0.0, 1.0) * height as f32) as usize;
|
||||
let left_db = Self::amplitude_to_db(self.left);
|
||||
let right_db = Self::amplitude_to_db(self.right);
|
||||
let left_norm = Self::db_to_normalized(left_db);
|
||||
let right_norm = Self::db_to_normalized(right_db);
|
||||
|
||||
let left_rows = (left_norm * height as f32).round() as usize;
|
||||
let right_rows = (right_norm * height as f32).round() as usize;
|
||||
|
||||
for row in 0..height {
|
||||
let y = area.y + area.height - 1 - row as u16;
|
||||
let level_at_row = (row as f32 + 0.5) / height as f32;
|
||||
let color = Self::level_to_color(level_at_row);
|
||||
let row_position = (row as f32 + 0.5) / height as f32;
|
||||
let color = Self::row_to_color(row_position);
|
||||
|
||||
if row < left_level {
|
||||
buf[(left_col, y)].set_char('█').set_fg(color);
|
||||
} else {
|
||||
buf[(left_col, y)].set_char('░').set_fg(Color::DarkGray);
|
||||
for col in 0..half_width.saturating_sub(gap) {
|
||||
let x = area.x + col;
|
||||
if row < left_rows {
|
||||
buf[(x, y)].set_char(' ').set_bg(color);
|
||||
}
|
||||
}
|
||||
|
||||
if row < right_level {
|
||||
buf[(right_col, y)].set_char('█').set_fg(color);
|
||||
} else {
|
||||
buf[(right_col, y)].set_char('░').set_fg(Color::DarkGray);
|
||||
}
|
||||
}
|
||||
|
||||
if area.width > 2 {
|
||||
for row in 0..height {
|
||||
let y = area.y + row as u16;
|
||||
for x in (area.x + 1)..(area.x + area.width - 1) {
|
||||
buf[(x, y)].set_char(' ');
|
||||
for col in 0..half_width.saturating_sub(gap) {
|
||||
let x = area.x + half_width + gap + col;
|
||||
if x < area.x + area.width && row < right_rows {
|
||||
buf[(x, y)].set_char(' ').set_bg(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user