soundview 0.3.0

Live analyzer/voiceprint visualization of system audio
Documentation
fn hue_to_rgbval_with_p0(q: f32, mut t: f32) -> f32 {
    if t < 0.0 {
        t += 1.;
    }
    if t < 1. / 6. {
        q * 6. * t
    } else if t < 1. / 2. {
        q
    } else if t < 2. / 3. {
        q * (2. / 3. - t) * 6.
    } else {
        0.
    }
}

fn hue_to_rgbval_with_q1(p: f32, mut t: f32) -> f32 {
    if t < 0. {
        t += 1.;
    }
    if t < 1. / 6. {
        p + ((1. - p) * 6. * t)
    } else if t < 1. / 2. {
        1.
    } else if t < 2. / 3. {
        p + ((1. - p) * (2. / 3. - t) * 6.)
    } else {
        p
    }
}

/// Computes an RGBA color for the provided input value.
fn value_to_rgba(max_lum: f32, lum_exponent: f32, value: f32) -> u32 {
    let lum_calc = f32::powf(value, lum_exponent);
    let mut lum = if max_lum > lum_calc {
        lum_calc
    } else {
        max_lum
    };

    let hue = 1. / 3. * (1. - value);
    lum *= 2.;
    // Assumption: texture color buffers are always little endian?
    // Worst case can do a conversion here without affecting perf.
    if lum < 1. {
        ((hue_to_rgbval_with_p0(lum, hue + 1. / 3.) * 255.) as u32)
            | ((hue_to_rgbval_with_p0(lum, hue) * 255.) as u32) << 8
            | ((hue_to_rgbval_with_p0(lum, hue - 1. / 3.) * 255.) as u32) << 16
            | 255 << 24
    } else {
        lum -= 1.;
        ((hue_to_rgbval_with_q1(lum, hue + 1. / 3.) * 255.) as u32)
            | ((hue_to_rgbval_with_q1(lum, hue) * 255.) as u32) << 8
            | ((hue_to_rgbval_with_q1(lum, hue - 1. / 3.) * 255.) as u32) << 16
            | 255 << 24
    }
}

/// Lookup object that converts [0.0, 1.0] to RGBA or BGRA colors.
pub struct HSL {
    precached_vals: Vec<u32>,
}

impl HSL {
    /// Precalculates an RGBA color table indexed by value.
    pub fn new(
        format: wgpu::TextureFormat,
        color_max_lum: usize,
        color_lum_exaggeration: usize,
    ) -> HSL {
        let cache_size = 1024;
        let max_lum = color_max_lum as f32 / 100.;
        let lum_exponent = 1 as f32 - (color_lum_exaggeration as f32 / 100.);
        let mut precached_vals = Vec::new();
        for i in 0..cache_size {
            precached_vals.push(value_to_rgba(
                max_lum,
                lum_exponent,
                i as f32 / cache_size as f32,
            ));
        }
        if format == wgpu::TextureFormat::Bgra8Unorm
            || format == wgpu::TextureFormat::Bgra8UnormSrgb
        {
            // Convert RGBA (actually ARGB) -> BGRA (actually ABGR)
            precached_vals = precached_vals
                .iter()
                .map(|val| {
                    let r = (val & 0xff0000) >> 16;
                    let b = val & 0xff;
                    // ABGR: A_G_ + B__ + R
                    (val & 0xff00ff00) | (b << 16) | r
                })
                .collect();
        }
        HSL { precached_vals }
    }

    /// Looks up and returns a RGBA or BGRA color for the provided [0.0, 1.0] value.
    pub fn value_to_color(self: &HSL, value: f32) -> u32 {
        let max_index: usize = self.precached_vals.len() - 1;
        let mut index: usize = (value * self.precached_vals.len() as f32) as usize;
        if index > max_index {
            // Just in case...
            index = max_index;
        }
        return self.precached_vals[index];
    }
}