tui_equalizer/lib.rs
1//! An equalizer widget for [Ratatui] with multiple frequency bands.
2//!
3//! The equalizer is a vertical bar chart where each band represents a frequency range. Each band
4//! can display a value from 0.0 to 1.0, where 1.0 is the maximum value.
5//!
6//! 
7//!
8//! This demo can be found in the examples folder in the git repo.
9//!
10//! ```shell
11//! cargo run --example demo
12//! ```
13//!
14//! Inspired by [a comment in the ratatui
15//! repo](https://github.com/ratatui/ratatui/issues/1325#issuecomment-2335095486).
16//!
17//! # Example
18//!
19//! ```rust
20//! # use ratatui::{widgets::Widget, layout::Rect, buffer::Buffer};
21//! # let area = Rect::default();
22//! # let mut buf = Buffer::empty(area);
23//! use tui_equalizer::{Band, Equalizer};
24//!
25//! let equalizer = Equalizer {
26//! bands: vec![
27//! Band::from(0.5),
28//! Band::from(0.8),
29//! Band::from(0.3),
30//! ],
31//! brightness: 1.0,
32//! };
33//! equalizer.render(area, &mut buf);
34//! ```
35//!
36//! # License
37//!
38//! Copyright (c) Josh McKinney
39//!
40//! This project is licensed under either of:
41//!
42//! - Apache License, Version 2.0 ([LICENSE-APACHE] or <http://www.apache.org/licenses/LICENSE-2.0>)
43//! - MIT license ([LICENSE-MIT] or <http://opensource.org/licenses/MIT>)
44//!
45//! at your option.
46//!
47//! [LICENSE-APACHE]: ./LICENSE-APACHE
48//! [LICENSE-MIT]: ./LICENSE-MIT
49//!
50//!
51//! [Ratatui]: https://crates.io/crates/ratatui
52
53use std::iter::zip;
54
55use ratatui::{
56 buffer::Buffer,
57 layout::{Constraint, Layout, Rect},
58 style::Color,
59 widgets::Widget,
60};
61
62/// An equalizer widget with multiple frequency bands.
63///
64/// The equalizer is a vertical bar chart where each band represents a frequency range.
65///
66/// # Example
67///
68/// ```
69/// # use ratatui::widgets::Widget;
70/// # let area = ratatui::layout::Rect::default();
71/// # let mut buf = ratatui::buffer::Buffer::empty(area);
72/// use tui_equalizer::{Band, Equalizer};
73///
74/// let equalizer = Equalizer {
75/// bands: vec![
76/// Band::from(0.5),
77/// Band::from(0.8),
78/// Band::from(0.3),
79/// ],
80/// brightness: 1.0,
81/// };
82/// equalizer.render(area, &mut buf);
83/// ```
84#[derive(Debug, Clone)]
85pub struct Equalizer {
86 /// A vector of `Band` structs representing each frequency band.
87 pub bands: Vec<Band>,
88 pub brightness: f64,
89}
90
91/// A struct representing a single frequency band in the equalizer.
92#[derive(Debug, Clone)]
93pub struct Band {
94 /// The normalized value of the band, where the maximum is 1.0.
95 pub value: f64,
96}
97
98impl From<f64> for Band {
99 fn from(value: f64) -> Self {
100 Self { value }
101 }
102}
103
104impl Widget for Equalizer {
105 fn render(self, area: Rect, buf: &mut Buffer) {
106 let areas = Layout::horizontal(vec![Constraint::Length(2); self.bands.len()]).split(area);
107 for (band, area) in zip(self.bands, areas.iter()) {
108 band.render(*area, buf, self.brightness);
109 }
110 }
111}
112
113impl Band {
114 fn render(self, area: Rect, buf: &mut Buffer, brightness: f64) {
115 let value = self.value.clamp(0.0, 1.0);
116 let height = (value * area.height as f64) as u16;
117
118 // Calculate the color gradient step
119 let color_step = 1.0 / area.height as f64;
120
121 // Iterate over each segment and render it with the corresponding color
122 for i in 0..height {
123 // Green to Yellow to Red gradient
124 let v = i as f64 * color_step;
125 let vv = 1.0 - v;
126 let br = brightness.clamp(0.0, 1.0) * 255.0;
127 let r = if v < 0.5 { v * 2.0 * br } else { br } as u8;
128 let g = if v < 0.5 { br } else { vv * 2.0 * br } as u8;
129 let b = 0;
130 let color = Color::Rgb(r, g, b);
131 buf[(area.left(), area.bottom().saturating_sub(i + 1))]
132 .set_fg(color)
133 .set_symbol(ratatui::symbols::bar::HALF);
134 }
135 }
136}