1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use cursive_core::{
    Printer, Vec2, View,
    theme::{Color, ColorStyle}
};
use rand::{
    Rng, thread_rng,
    prelude::SliceRandom as _,
    rngs::ThreadRng
};
use audiovis::RawVisualizer;
use mlounge_core::library::Song;

/// An audio visualizer that visualizes the audio as a waveform
pub struct WaveVisualizer {
    rand: ThreadRng,
    wave: RawVisualizer,
    latest_data: Vec<f32>,
    art_path: String,
    colors: Vec<Color>,
    width: usize
}

impl WaveVisualizer {
    /// Create a new `WaveVisualizer`
    pub fn new() -> WaveVisualizer {
        WaveVisualizer {
            rand: thread_rng(),
            wave: RawVisualizer::new(),
            latest_data: vec![],
            art_path: String::new(),
            colors: vec![],
            width: 0
        }
    }

    /// Update the audio visualizer with the specified song
    pub fn update(&mut self, song: &Song) {
        if self.width == 0 { return; }
        self.latest_data = self.wave.get_wave_data(self.width / 2);
        if self.latest_data.len() < 4 { return; }
        if let Some(path) = song.get_art_path() {
            if self.art_path != path || self.latest_data[3] >= 0.25 {
                self.colors.clear();
                self.art_path = path;
                let colors = song.cursive_album_colors();
                let num_colors = self.rand.gen_range(5..20);
                self.colors = colors.choose_multiple(&mut self.rand, num_colors).copied().collect();
            }
        }
    }

    fn draw_multicolored_line(&self, printer: &Printer, x: usize, line_height: usize, max_height: usize) {
        let height = printer.size.y;
        let color_height = max_height / self.colors.len();
        let mut color_index = 0;
        let real_height =
            if line_height % 2 == 0 {
                line_height / 2
            }
            else {
                (line_height + 1) / 2 
            }
                .clamp(2, max_height);
        
        let mut height_idx = 0;
        
        for y in 0..real_height {
            let color1 = if height_idx <= 3 {
                self.colors[0]
            }
            else { self.colors[color_index] };

            if color_height > 0 {
                if height_idx % color_height == 0 && height_idx > 0 && height_idx < line_height { color_index += 1; }
                if color_index >= self.colors.len() { color_index = 0; }
            }

            height_idx += 1;
            let color2 = self.colors[color_index];

            if color_height > 0 {
                if height_idx % color_height == 0 && height_idx > 0 && height_idx < line_height { color_index += 1; }
                if color_index >= self.colors.len() { color_index = 0; }
            }

            height_idx += 1;
            let style = if height_idx >= line_height {
                ColorStyle::new(color1, Color::TerminalDefault)
            }
            else { ColorStyle::new(color1, color2) };

            printer.with_color(style, |printer| printer.print((x, height - y), "▄"));
        }
    }
}

impl View for WaveVisualizer {
    fn draw(&self, printer: &Printer) {
        if self.colors.is_empty() { return; }
        let height = printer.size.y;
        let max_height = height / 3;
        for (x, freq) in self.latest_data.iter().rev().chain(&self.latest_data).enumerate() {
            self.draw_multicolored_line(printer, x, (*freq * 10. * max_height as f32) as usize, max_height);
        }
    }

    fn required_size(&mut self, constraint: Vec2) -> Vec2 {
        self.width = constraint.x;
        constraint
    }
}

impl Default for WaveVisualizer {
    fn default() -> Self { Self::new() }
}