#[allow(deprecated)]
use skia_safe::gradient_shader::GradientShaderColors;
#[allow(deprecated)]
use skia_safe::{Canvas, Color, Color4f, Paint, Point, RRect, Rect, TileMode, gradient_shader};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use crate::drawing::{Drawable, Plan};
use crate::layouts::{HasPlanner, Layout};
pub struct AudioSpectrumChart {
pub v: RefCell<HashMap<u64, [f32; 128]>>,
pub current_time: RefCell<i64>,
colors: [Color; 3],
cap_paint: Paint,
planner: RefCell<Option<Rc<dyn Layout>>>,
}
impl HasPlanner for AudioSpectrumChart {
fn planner(&self) -> &RefCell<Option<Rc<dyn Layout>>> {
&self.planner
}
fn set_planner(&self, p: Option<Rc<dyn Layout>>) {
*self.planner.borrow_mut() = p;
}
}
impl Drawable for AudioSpectrumChart {
fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
let area = self.get_planned_drawing_area(canvas);
let available_w = area.width();
let available_h = area.height();
if available_w <= 0.0 || available_h <= 0.0 {
return Ok(());
}
let empty_frame = [0.0f32; 128];
let frame_buffer;
let mut current_frame: Option<&[f32; 128]> = None;
let current_time = *self.current_time.borrow();
let v = self.v.borrow();
if current_time >= 0 {
let time_u64 = current_time as u64;
for offset in 0..=42 {
if let Some(frame) = v.get(&time_u64.saturating_sub(offset)) {
frame_buffer = *frame;
current_frame = Some(&frame_buffer);
break;
}
if let Some(frame) = v.get(&(time_u64 + offset)) {
frame_buffer = *frame;
current_frame = Some(&frame_buffer);
break;
}
}
}
let frame_data = current_frame.unwrap_or(&empty_frame);
let max_bar_h = available_h / 2.0;
let center_y = area.top + available_h / 2.0;
let total_bars = 128;
let step_x = available_w / total_bars as f32;
let bar_width = (step_x * 0.7).max(1.0);
let visual_scale = 3.5;
#[allow(deprecated)]
let shader = gradient_shader::linear(
(
Point::new(area.left, area.top),
Point::new(area.right, area.top),
),
GradientShaderColors::Colors(&self.colors),
None,
TileMode::Clamp,
None,
None,
);
let mut bar_paint = Paint::default();
bar_paint.set_anti_alias(true);
if let Some(s) = &shader {
bar_paint.set_shader(s.clone());
} else {
bar_paint.set_color4f(Color4f::new(0.26, 0.52, 0.96, 1.0), None);
}
let mut reflection_paint = bar_paint.clone();
reflection_paint.set_alpha_f(0.25);
for (i, &value) in frame_data.iter().enumerate() {
let boost = 1.0 + (i as f32 / total_bars as f32) * 1.5;
let amplitude = (value * visual_scale * boost).clamp(1.0, max_bar_h);
let x = area.left + (i as f32) * step_x;
let top_rect = Rect::from_xywh(x, center_y - amplitude, bar_width, amplitude);
let top_rrect = RRect::new_rect_xy(top_rect, bar_width / 2.0, bar_width / 2.0);
canvas.draw_rrect(top_rrect, &bar_paint);
let bottom_rect = Rect::from_xywh(x, center_y, bar_width, amplitude * 0.6);
let bottom_rrect = RRect::new_rect_xy(bottom_rect, bar_width / 2.0, bar_width / 2.0);
canvas.draw_rrect(bottom_rrect, &reflection_paint);
if amplitude > 2.0 {
canvas.draw_circle(
Point::new(x + bar_width / 2.0, center_y - amplitude - 2.0),
bar_width / 2.0,
&self.cap_paint,
);
}
}
Ok(())
}
fn get_horizontal_plan(&self) -> Plan {
Plan::Fill
}
fn get_vertical_plan(&self) -> Plan {
Plan::Fill
}
}
impl AudioSpectrumChart {
pub fn new(v: HashMap<u64, [f32; 128]>) -> Rc<Self> {
let mut cap_paint = Paint::default();
cap_paint
.set_anti_alias(true)
.set_color4f(Color4f::new(1.0, 1.0, 1.0, 0.9), None);
let colors = [
Color::from_argb(255, 255, 42, 133),
Color::from_argb(255, 138, 43, 226),
Color::from_argb(255, 0, 212, 255),
];
Rc::new(Self {
v: RefCell::new(v),
current_time: RefCell::new(0),
colors,
cap_paint,
planner: RefCell::new(None),
})
}
}