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