<commit_before>extern crate actix;
extern crate actix_web;
extern crate bytes;
extern crate futures;
extern crate http;
extern crate rand;

use bytes::Bytes;
use futures::Stream;
use rand::Rng;

#[cfg(feature = "alpn")]
extern crate openssl;

use actix::prelude::*;
use actix_web::*;

struct Ws;

impl Actor for Ws {
    type Context = ws::WebsocketContext<Self>;
}

impl StreamHandler<ws::Message, ws::ProtocolError> for Ws {
    fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
        match msg {
            ws::Message::Ping(msg) => ctx.pong(&msg),
            ws::Message::Text(text) => ctx.text(text),
            ws::Message::Binary(bin) => ctx.binary(bin),
            ws::Message::Close(reason) => ctx.close(reason, ""),
            _ => (),
        }
    }
}

#[test]
fn test_simple() {
    let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws)));
    let (reader, mut writer) = srv.ws().unwrap();

    writer.text("text");
    let (item, reader) = srv.execute(reader.into_future()).unwrap();
    assert_eq!(item, Some(ws::Message::Text("text".to_owned())));

    writer.binary(b"text".as_ref());
    let (item, reader) = srv.execute(reader.into_future()).unwrap();
    assert_eq!(
        item,
        Some(ws::Message::Binary(
            Bytes::from_static(b"text").into()
        ))
    );

    writer.ping("ping");
    let (item, reader) = srv.execute(reader.into_future()).unwrap();
    assert_eq!(item, Some(ws::Message::Pong("ping".to_owned())));

    writer.close(ws::CloseCode::Normal, "");
    let (item, _) = srv.execute(reader.into_future()).unwrap();
    assert_eq!(item, Some(ws::Message::Close(ws::CloseCode::Normal)));
}

#[test]
fn test_large_text() {
    let data = rand::thread_rng()
        .gen_ascii_chars()
        .take(65_536)
        .collect::<String>();

    let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws)));
    let (mut reader, mut writer) = srv.ws().unwrap();

    for _ in 0..100 {
        writer.text(data.clone());
        let (item, r) = srv.execute(reader.into_future()).unwrap();
        reader = r;
        assert_eq!(item, Some(ws::Message::Text(data.clone())));
    }
}

#[test]
fn test_large_bin() {
    let data = rand::thread_rng()
        .gen_ascii_chars()
        .take(65_536)
        .collect::<String>();

    let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws)));
    let (mut reader, mut writer) = srv.ws().unwrap();

    for _ in 0..100 {
        writer.binary(data.clone());
        let (item, r) = srv.execute(reader.into_future()).unwrap();
        reader = r;
        assert_eq!(
            item,
            Some(ws::Message::Binary(Binary::from(data.clone())))
        );
    }
}

struct Ws2 {
    count: usize,
    bin: bool,
}

impl Actor for Ws2 {
    type Context = ws::WebsocketContext<Self>;

    fn started(&mut self, ctx: &mut Self::Context) {
        self.send(ctx);
    }
}

impl Ws2 {
    fn send(&mut self, ctx: &mut ws::WebsocketContext<Self>) {
        if self.bin {
            ctx.binary(Vec::from("0".repeat(65_536)));
        } else {
            ctx.text("0".repeat(65_536));
        }
        ctx.drain()
            .and_then(|_, act, ctx| {
                act.count += 1;
                if act.count != 10_000 {
                    act.send(ctx);
                }
                actix::fut::ok(())
            })
            .wait(ctx);
    }
}

impl StreamHandler<ws::Message, ws::ProtocolError> for Ws2 {
    fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
        match msg {
            ws::Message::Ping(msg) => ctx.pong(&msg),
            ws::Message::Text(text) => ctx.text(text),
            ws::Message::Binary(bin) => ctx.binary(bin),
            ws::Message::Close(reason) => ctx.close(reason, ""),
            _ => (),
        }
    }
}

#[test]
fn test_server_send_text() {
    let data = Some(ws::Message::Text("0".repeat(65_536)));

    let mut srv = test::TestServer::new(|app| {
        app.handler(|req| {
            ws::start(
                req,
                Ws2 {
                    count: 0,
                    bin: false,
                },
            )
        })
    });
    let (mut reader, _writer) = srv.ws().unwrap();

    for _ in 0..10_000 {
        let (item, r) = srv.execute(reader.into_future()).unwrap();
        reader = r;
        assert_eq!(item, data);
    }
}

#[test]
fn test_server_send_bin() {
    let data = Some(ws::Message::Binary(Binary::from("0".repeat(65_536))));

    let mut srv = test::TestServer::new(|app| {
        app.handler(|req| {
            ws::start(
                req,
                Ws2 {
                    count: 0,
                    bin: true,
                },
            )
        })
    });
    let (mut reader, _writer) = srv.ws().unwrap();

    for _ in 0..10_000 {
        let (item, r) = srv.execute(reader.into_future()).unwrap();
        reader = r;
        assert_eq!(item, data);
    }
}

#[test]
#[cfg(feature = "alpn")]
fn test_ws_server_ssl() {
    extern crate openssl;
    use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};

    // load ssl keys
    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
    builder
        .set_private_key_file("tests/key.pem", SslFiletype::PEM)
        .unwrap();
    builder
        .set_certificate_chain_file("tests/cert.pem")
        .unwrap();

    let mut srv = test::TestServer::build()
        .ssl(builder.build())
        .start(|app| {
            app.handler(|req| {
                ws::start(
                    req,
                    Ws2 {
                        count: 0,
                        bin: false,
                    },
                )
            })
        });
    let (mut reader, _writer) = srv.ws().unwrap();

    let data = Some(ws::Message::Text("0".repeat(65_536)));
    for _ in 0..10_000 {
        let (item, r) = srv.execute(reader.into_future()).unwrap();
        reader = r;
        assert_eq!(item, data);
    }
}
<commit_msg>add websocket empty close status test<commit_after>extern crate actix;
extern crate actix_web;
extern crate bytes;
extern crate futures;
extern crate http;
extern crate rand;

use bytes::Bytes;
use futures::Stream;
use rand::Rng;

#[cfg(feature = "alpn")]
extern crate openssl;

use actix::prelude::*;
use actix_web::*;

struct Ws;

impl Actor for Ws {
    type Context = ws::WebsocketContext<Self>;
}

impl StreamHandler<ws::Message, ws::ProtocolError> for Ws {
    fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
        match msg {
            ws::Message::Ping(msg) => ctx.pong(&msg),
            ws::Message::Text(text) => ctx.text(text),
            ws::Message::Binary(bin) => ctx.binary(bin),
            ws::Message::Close(reason) => ctx.close(reason, ""),
            _ => (),
        }
    }
}

#[test]
fn test_simple() {
    let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws)));
    let (reader, mut writer) = srv.ws().unwrap();

    writer.text("text");
    let (item, reader) = srv.execute(reader.into_future()).unwrap();
    assert_eq!(item, Some(ws::Message::Text("text".to_owned())));

    writer.binary(b"text".as_ref());
    let (item, reader) = srv.execute(reader.into_future()).unwrap();
    assert_eq!(
        item,
        Some(ws::Message::Binary(
            Bytes::from_static(b"text").into()
        ))
    );

    writer.ping("ping");
    let (item, reader) = srv.execute(reader.into_future()).unwrap();
    assert_eq!(item, Some(ws::Message::Pong("ping".to_owned())));

    writer.close(ws::CloseCode::Normal, "");
    let (item, _) = srv.execute(reader.into_future()).unwrap();
    assert_eq!(item, Some(ws::Message::Close(ws::CloseCode::Normal)));
}

#[test]
fn test_empty_close_code() {
    let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws)));
    let (reader, mut writer) = srv.ws().unwrap();

    writer.close(ws::CloseCode::Empty, "");
    let (item, _) = srv.execute(reader.into_future()).unwrap();
    assert_eq!(item, Some(ws::Message::Close(ws::CloseCode::Status)));
}

#[test]
fn test_large_text() {
    let data = rand::thread_rng()
        .gen_ascii_chars()
        .take(65_536)
        .collect::<String>();

    let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws)));
    let (mut reader, mut writer) = srv.ws().unwrap();

    for _ in 0..100 {
        writer.text(data.clone());
        let (item, r) = srv.execute(reader.into_future()).unwrap();
        reader = r;
        assert_eq!(item, Some(ws::Message::Text(data.clone())));
    }
}

#[test]
fn test_large_bin() {
    let data = rand::thread_rng()
        .gen_ascii_chars()
        .take(65_536)
        .collect::<String>();

    let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws)));
    let (mut reader, mut writer) = srv.ws().unwrap();

    for _ in 0..100 {
        writer.binary(data.clone());
        let (item, r) = srv.execute(reader.into_future()).unwrap();
        reader = r;
        assert_eq!(
            item,
            Some(ws::Message::Binary(Binary::from(data.clone())))
        );
    }
}

struct Ws2 {
    count: usize,
    bin: bool,
}

impl Actor for Ws2 {
    type Context = ws::WebsocketContext<Self>;

    fn started(&mut self, ctx: &mut Self::Context) {
        self.send(ctx);
    }
}

impl Ws2 {
    fn send(&mut self, ctx: &mut ws::WebsocketContext<Self>) {
        if self.bin {
            ctx.binary(Vec::from("0".repeat(65_536)));
        } else {
            ctx.text("0".repeat(65_536));
        }
        ctx.drain()
            .and_then(|_, act, ctx| {
                act.count += 1;
                if act.count != 10_000 {
                    act.send(ctx);
                }
                actix::fut::ok(())
            })
            .wait(ctx);
    }
}

impl StreamHandler<ws::Message, ws::ProtocolError> for Ws2 {
    fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
        match msg {
            ws::Message::Ping(msg) => ctx.pong(&msg),
            ws::Message::Text(text) => ctx.text(text),
            ws::Message::Binary(bin) => ctx.binary(bin),
            ws::Message::Close(reason) => ctx.close(reason, ""),
            _ => (),
        }
    }
}

#[test]
fn test_server_send_text() {
    let data = Some(ws::Message::Text("0".repeat(65_536)));

    let mut srv = test::TestServer::new(|app| {
        app.handler(|req| {
            ws::start(
                req,
                Ws2 {
                    count: 0,
                    bin: false,
                },
            )
        })
    });
    let (mut reader, _writer) = srv.ws().unwrap();

    for _ in 0..10_000 {
        let (item, r) = srv.execute(reader.into_future()).unwrap();
        reader = r;
        assert_eq!(item, data);
    }
}

#[test]
fn test_server_send_bin() {
    let data = Some(ws::Message::Binary(Binary::from("0".repeat(65_536))));

    let mut srv = test::TestServer::new(|app| {
        app.handler(|req| {
            ws::start(
                req,
                Ws2 {
                    count: 0,
                    bin: true,
                },
            )
        })
    });
    let (mut reader, _writer) = srv.ws().unwrap();

    for _ in 0..10_000 {
        let (item, r) = srv.execute(reader.into_future()).unwrap();
        reader = r;
        assert_eq!(item, data);
    }
}

#[test]
#[cfg(feature = "alpn")]
fn test_ws_server_ssl() {
    extern crate openssl;
    use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};

    // load ssl keys
    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
    builder
        .set_private_key_file("tests/key.pem", SslFiletype::PEM)
        .unwrap();
    builder
        .set_certificate_chain_file("tests/cert.pem")
        .unwrap();

    let mut srv = test::TestServer::build()
        .ssl(builder.build())
        .start(|app| {
            app.handler(|req| {
                ws::start(
                    req,
                    Ws2 {
                        count: 0,
                        bin: false,
                    },
                )
            })
        });
    let (mut reader, _writer) = srv.ws().unwrap();

    let data = Some(ws::Message::Text("0".repeat(65_536)));
    for _ in 0..10_000 {
        let (item, r) = srv.execute(reader.into_future()).unwrap();
        reader = r;
        assert_eq!(item, data);
    }
}
<|endoftext|>


<commit_before>//! Spectral feature extraction module.
//!
//! Contains functions to extract & summarize zero-crossing rate,
//! spectral centroid, spectral flatness and spectral roll-off of
//! a given Song.
#[cfg(feature = "aubio-lib")]
extern crate aubio_lib;

use aubio_rs::{bin_to_freq, PVoc, SpecDesc, SpecShape};
use aubio_rs::vec::{CVec};

use super::utils::{geometric_mean, mean, number_crossings};
use super::Descriptor;

pub struct SpectralDesc {
    aubio_obj: SpecDesc,
    phase_vocoder: PVoc,
    // Values before being summarized through f.ex. a mean
    values: Vec<f32>,
    sample_rate: u32,
}

impl SpectralDesc {
    pub const WINDOW_SIZE: usize = 512;
    pub const HOP_SIZE: usize = SpectralDesc::WINDOW_SIZE / 4;

    /**
     * Compute score related to the spectral descriptor.
     *
     * Currently returns the mean of all the chunks' values.
     */
    pub fn get_value(&mut self) -> f32 {
        mean(&self.values)
    }

    pub fn new(shape: SpecShape, sample_rate: u32) -> Self {
        SpectralDesc {
            aubio_obj: SpecDesc::new(shape, SpectralDesc::WINDOW_SIZE).unwrap(),
            phase_vocoder: PVoc::new(SpectralDesc::WINDOW_SIZE, SpectralDesc::HOP_SIZE).unwrap(),
            // TODO vec with capacity?
            values: Vec::new(),
            sample_rate,
        }
    }
}

/**
 * [Zero-crossing rate](https://en.wikipedia.org/wiki/Zero-crossing_rate)
 * detection object.
 *
 * Zero-crossing rate is mostly used to detect percussive sounds in an audio
 * signal, as well as whether an audio signal contains speech or not.
 *
 * It is a good metric to differentiate between songs with people speaking clearly,
 * (e.g. slam) and instrumental songs.
 *
 * The value range is between 0 and 1.
 */
#[derive(Default)]
pub struct ZeroCrossingRateDesc {
    values: Vec<u32>,
    number_samples: usize,
}

/**
 * [Spectral centroid](https://en.wikipedia.org/wiki/Spectral_centroid)
 * detection object.
 *
 * Spectral centroid is used to determine the "brightness" of a sound, i.e.
 * how much high frequency there is in an audio signal.
 *
 * It of course depends of the instrument used: a piano-only track that makes
 * use of high frequencies will still score less than a song using a lot of
 * percussive sound, because the piano frequency range is lower.
 *
 * The value range is between 0 and `sample_rate / 2`.
 */
pub struct SpectralCentroidDesc {
    spectral_desc: SpectralDesc,
}

/**
 * Spectral roll-off detection object.
 *
 * Spectral roll-off is the bin frequency number below which a certain
 * percentage of the spectral energy is found, here, 95%.
 *
 * It can be used to distinguish voiced speech (low roll-off) and unvoiced
 * speech (high roll-off). It is also a good indication of the energy
 * repartition of a song.
 *
 * The value range is between 0 and `sample_rate / 2`
 */
// TODO is it really relevant to use?
pub struct SpectralRollOffDesc {
    spectral_desc: SpectralDesc,
}

pub struct SpectralFlatnessDesc {
    spectral_desc: SpectralDesc,
}

impl Descriptor for ZeroCrossingRateDesc {
    fn new(_sample_rate: u32) -> Self {
        ZeroCrossingRateDesc::default()
    }

    /// Count the number of zero-crossings for the current `chunk`.
    fn do_(&mut self, chunk: &[f32]) {
        self.values.push(number_crossings(chunk));
        self.number_samples += chunk.len();
    }

    /// Sum the number of zero-crossings witnessed and divide by
    /// the total number of samples.
    fn get_value(&mut self) -> f32 {
        (self.values.iter().sum::<u32>()) as f32 / self.number_samples as f32
    }
}

impl Descriptor for SpectralCentroidDesc {
    fn new(sample_rate: u32) -> Self {
        SpectralCentroidDesc {
            spectral_desc: SpectralDesc::new(SpecShape::Centroid, sample_rate),
        }
    }

    // TODO make FFT computation common for all spectral descs
    /// Compute FFT and associated spectral centroid for the current chunk.
    fn do_(&mut self, chunk: &[f32]) {
        let mut fftgrain: Vec<f32> = vec![0.0; SpectralDesc::WINDOW_SIZE + 2];
        self.spectral_desc
            .phase_vocoder
            .do_(chunk, fftgrain.as_mut_slice())
            .unwrap();
        let bin = self
            .spectral_desc
            .aubio_obj
            .do_result(fftgrain.as_slice())
            .unwrap();
        let freq = bin_to_freq(
            bin,
            self.spectral_desc.sample_rate as f32,
            SpectralDesc::WINDOW_SIZE as f32,
        );
        self.spectral_desc.values.push(freq);
    }

    fn get_value(&mut self) -> f32 {
        self.spectral_desc.get_value()
    }
}

impl Descriptor for SpectralRollOffDesc {
    fn new(sample_rate: u32) -> Self {
        SpectralRollOffDesc {
            spectral_desc: SpectralDesc::new(SpecShape::Rolloff, sample_rate),
        }
    }

    /// Compute FFT and associated spectral roll-off for the current chunk.
    fn do_(&mut self, chunk: &[f32]) {
        let mut fftgrain: Vec<f32> = vec![0.0; SpectralDesc::WINDOW_SIZE + 2];

        self.spectral_desc
            .phase_vocoder
            .do_(chunk, fftgrain.as_mut_slice())
            .unwrap();
        let bin = self
            .spectral_desc
            .aubio_obj
            .do_result(fftgrain.as_slice())
            .unwrap();
        let freq = bin_to_freq(
            bin,
            self.spectral_desc.sample_rate as f32,
            SpectralDesc::WINDOW_SIZE as f32,
        );
        self.spectral_desc.values.push(freq);
    }

    fn get_value(&mut self) -> f32 {
        self.spectral_desc.get_value()
    }
}

impl Descriptor for SpectralFlatnessDesc {
    fn new(sample_rate: u32) -> Self {
        SpectralFlatnessDesc {
            spectral_desc: SpectralDesc::new(SpecShape::Kurtosis, sample_rate),
        }
    }

    fn do_(&mut self, chunk: &[f32]) {
        let mut fftgrain: Vec<f32> = vec![0.0; SpectralDesc::WINDOW_SIZE + 2];

        self.spectral_desc
            .phase_vocoder
            .do_(chunk, fftgrain.as_mut_slice())
            .unwrap();
        let cvec: CVec = fftgrain.as_slice().into();
        let flatness =  geometric_mean(&cvec.norm()) / mean(&cvec.norm());
        self.spectral_desc.values.push(flatness);
    }

    /**
     * Compute score related to spectral centroid.
     * Returns the mean of computed spectral centroids over the song.
     *
     * - `song` Song to compute score from
     */
    // TODO do we really want the mean there?
    fn get_value(&mut self) -> f32 {
        self.spectral_desc.get_value()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::decode::decode_song;

    #[test]
    fn test_zcr() {
        let song = decode_song("data/s16_mono_22_5kHz.flac").unwrap();
        let mut zcr_desc = ZeroCrossingRateDesc::default();
        for chunk in song.sample_array.chunks_exact(SpectralDesc::HOP_SIZE) {
            zcr_desc.do_(&chunk);
        }
        assert!(0.001 > (0.075 - zcr_desc.get_value()).abs());
    }

    #[test]
    fn test_spectral_flatness() {
        let song = decode_song("data/s16_mono_22_5kHz.flac").unwrap();
        let mut flatness_desc = SpectralFlatnessDesc::new(song.sample_rate);
        for chunk in song.sample_array.chunks_exact(SpectralDesc::HOP_SIZE) {
            flatness_desc.do_(&chunk);
        }
        // Spectral flatness value computed here with phase vocoder: 0.111949615
        // Essentia value with spectrum / hann window: 0.11197535695207445
        assert!(0.01 > (0.11 - flatness_desc.get_value()).abs());
    }

    #[test]
    fn test_spectral_roll_off() {
        let song = decode_song("data/s16_mono_22_5kHz.flac").unwrap();
        let mut roll_off_desc = SpectralRollOffDesc::new(song.sample_rate);
        for chunk in song.sample_array.chunks_exact(SpectralDesc::HOP_SIZE) {
            roll_off_desc.do_(&chunk);
        }
        // Roll-off value computed here with phase vocoder: 2026.7644
        // Essentia value with spectrum / hann window: 1979.632683520047
        assert!(0.01 > (2026.76 - roll_off_desc.get_value()).abs());
    }

    #[test]
    fn test_spectral_centroid() {
        let song = decode_song("data/s16_mono_22_5kHz.flac").unwrap();
        let mut centroid_desc = SpectralCentroidDesc::new(song.sample_rate);
        for chunk in song.sample_array.chunks_exact(SpectralDesc::HOP_SIZE) {
            centroid_desc.do_(&chunk);
        }
        // Spectral centroid value computed here with phase vocoder: 1354.2273
        // Essentia value with spectrum / hann window: 1351
        assert!(0.01 > (1354.2273 - centroid_desc.get_value()).abs());
    }
}
<commit_msg>Add doc for spectral flatness<commit_after>//! Spectral feature extraction module.
//!
//! Contains functions to extract & summarize zero-crossing rate,
//! spectral centroid, spectral flatness and spectral roll-off of
//! a given Song.
#[cfg(feature = "aubio-lib")]
extern crate aubio_lib;

use aubio_rs::{bin_to_freq, PVoc, SpecDesc, SpecShape};
use aubio_rs::vec::{CVec};

use super::utils::{geometric_mean, mean, number_crossings};
use super::Descriptor;

pub struct SpectralDesc {
    aubio_obj: SpecDesc,
    phase_vocoder: PVoc,
    // Values before being summarized through f.ex. a mean
    values: Vec<f32>,
    sample_rate: u32,
}

impl SpectralDesc {
    pub const WINDOW_SIZE: usize = 512;
    pub const HOP_SIZE: usize = SpectralDesc::WINDOW_SIZE / 4;

    /**
     * Compute score related to the spectral descriptor.
     *
     * Currently returns the mean of all the chunks' values.
     */
    pub fn get_value(&mut self) -> f32 {
        mean(&self.values)
    }

    pub fn new(shape: SpecShape, sample_rate: u32) -> Self {
        SpectralDesc {
            aubio_obj: SpecDesc::new(shape, SpectralDesc::WINDOW_SIZE).unwrap(),
            phase_vocoder: PVoc::new(SpectralDesc::WINDOW_SIZE, SpectralDesc::HOP_SIZE).unwrap(),
            // TODO vec with capacity?
            values: Vec::new(),
            sample_rate,
        }
    }
}

/**
 * [Zero-crossing rate](https://en.wikipedia.org/wiki/Zero-crossing_rate)
 * detection object.
 *
 * Zero-crossing rate is mostly used to detect percussive sounds in an audio
 * signal, as well as whether an audio signal contains speech or not.
 *
 * It is a good metric to differentiate between songs with people speaking clearly,
 * (e.g. slam) and instrumental songs.
 *
 * The value range is between 0 and 1.
 */
#[derive(Default)]
pub struct ZeroCrossingRateDesc {
    values: Vec<u32>,
    number_samples: usize,
}

/**
 * [Spectral centroid](https://en.wikipedia.org/wiki/Spectral_centroid)
 * detection object.
 *
 * Spectral centroid is used to determine the "brightness" of a sound, i.e.
 * how much high frequency there is in an audio signal.
 *
 * It of course depends of the instrument used: a piano-only track that makes
 * use of high frequencies will still score less than a song using a lot of
 * percussive sound, because the piano frequency range is lower.
 *
 * The value range is between 0 and `sample_rate / 2`.
 */
pub struct SpectralCentroidDesc {
    spectral_desc: SpectralDesc,
}

/**
 * Spectral roll-off detection object.
 *
 * Spectral roll-off is the bin frequency number below which a certain
 * percentage of the spectral energy is found, here, 95%.
 *
 * It can be used to distinguish voiced speech (low roll-off) and unvoiced
 * speech (high roll-off). It is also a good indication of the energy
 * repartition of a song.
 *
 * The value range is between 0 and `sample_rate / 2`
 */
// TODO is it really relevant to use?
pub struct SpectralRollOffDesc {
    spectral_desc: SpectralDesc,
}

/**
 * Spectral flatness detection object.
 *
 * Spectral flatness is the ratio between the geometric mean of the spectrum
 * and its arithmetic mean.
 *
 * It is used to distinguish between tone-like and noise-like signals.
 * Tone-like audio is f.ex. a piano key, something that has one or more
 * specific frequencies, while (white) noise has an equal distribution
 * of intensity among all frequencies.
 *
 * The value range is between 0 and 1, since the geometric mean is always less
 * than the arithmetic mean.
 */
pub struct SpectralFlatnessDesc {
    spectral_desc: SpectralDesc,
}

impl Descriptor for ZeroCrossingRateDesc {
    fn new(_sample_rate: u32) -> Self {
        ZeroCrossingRateDesc::default()
    }

    /// Count the number of zero-crossings for the current `chunk`.
    fn do_(&mut self, chunk: &[f32]) {
        self.values.push(number_crossings(chunk));
        self.number_samples += chunk.len();
    }

    /// Sum the number of zero-crossings witnessed and divide by
    /// the total number of samples.
    fn get_value(&mut self) -> f32 {
        (self.values.iter().sum::<u32>()) as f32 / self.number_samples as f32
    }
}

impl Descriptor for SpectralCentroidDesc {
    fn new(sample_rate: u32) -> Self {
        SpectralCentroidDesc {
            spectral_desc: SpectralDesc::new(SpecShape::Centroid, sample_rate),
        }
    }

    // TODO make FFT computation common for all spectral descs
    // maybe make a big spectral desc with the 3 spectral desc
    /// Compute FFT and associated spectral centroid for the current chunk.
    fn do_(&mut self, chunk: &[f32]) {
        let mut fftgrain: Vec<f32> = vec![0.0; SpectralDesc::WINDOW_SIZE + 2];
        self.spectral_desc
            .phase_vocoder
            .do_(chunk, fftgrain.as_mut_slice())
            .unwrap();
        let bin = self
            .spectral_desc
            .aubio_obj
            .do_result(fftgrain.as_slice())
            .unwrap();
        let freq = bin_to_freq(
            bin,
            self.spectral_desc.sample_rate as f32,
            SpectralDesc::WINDOW_SIZE as f32,
        );
        self.spectral_desc.values.push(freq);
    }

    fn get_value(&mut self) -> f32 {
        self.spectral_desc.get_value()
    }
}

impl Descriptor for SpectralRollOffDesc {
    fn new(sample_rate: u32) -> Self {
        SpectralRollOffDesc {
            spectral_desc: SpectralDesc::new(SpecShape::Rolloff, sample_rate),
        }
    }

    /// Compute FFT and associated spectral roll-off for the current chunk.
    fn do_(&mut self, chunk: &[f32]) {
        let mut fftgrain: Vec<f32> = vec![0.0; SpectralDesc::WINDOW_SIZE + 2];

        self.spectral_desc
            .phase_vocoder
            .do_(chunk, fftgrain.as_mut_slice())
            .unwrap();
        let bin = self
            .spectral_desc
            .aubio_obj
            .do_result(fftgrain.as_slice())
            .unwrap();
        let freq = bin_to_freq(
            bin,
            self.spectral_desc.sample_rate as f32,
            SpectralDesc::WINDOW_SIZE as f32,
        );
        self.spectral_desc.values.push(freq);
    }

    fn get_value(&mut self) -> f32 {
        self.spectral_desc.get_value()
    }
}

impl Descriptor for SpectralFlatnessDesc {
    // TODO not using the spectral desc here
    // remove this when done with the merging of the 3 spectral descriptors
    fn new(sample_rate: u32) -> Self {
        SpectralFlatnessDesc {
            spectral_desc: SpectralDesc::new(SpecShape::Kurtosis, sample_rate),
        }
    }

    fn do_(&mut self, chunk: &[f32]) {
        let mut fftgrain: Vec<f32> = vec![0.0; SpectralDesc::WINDOW_SIZE + 2];

        self.spectral_desc
            .phase_vocoder
            .do_(chunk, fftgrain.as_mut_slice())
            .unwrap();
        let cvec: CVec = fftgrain.as_slice().into();
        let geo_mean = geometric_mean(&cvec.norm());
        if geo_mean == 0.0 {
            self.spectral_desc.values.push(0.0);
            return;
        }
        let flatness = geo_mean / mean(&cvec.norm());
        self.spectral_desc.values.push(flatness);
    }

    fn get_value(&mut self) -> f32 {
        self.spectral_desc.get_value()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::decode::decode_song;

    #[test]
    fn test_zcr() {
        let song = decode_song("data/s16_mono_22_5kHz.flac").unwrap();
        let mut zcr_desc = ZeroCrossingRateDesc::default();
        for chunk in song.sample_array.chunks_exact(SpectralDesc::HOP_SIZE) {
            zcr_desc.do_(&chunk);
        }
        assert!(0.001 > (0.075 - zcr_desc.get_value()).abs());
    }

    #[test]
    fn test_spectral_flatness() {
        let song = decode_song("data/s16_mono_22_5kHz.flac").unwrap();
        let mut flatness_desc = SpectralFlatnessDesc::new(song.sample_rate);
        for chunk in song.sample_array.chunks_exact(SpectralDesc::HOP_SIZE) {
            flatness_desc.do_(&chunk);
        }
        // Spectral flatness value computed here with phase vocoder: 0.111949615
        // Essentia value with spectrum / hann window: 0.11197535695207445
        assert!(0.01 > (0.11 - flatness_desc.get_value()).abs());
    }

    #[test]
    fn test_spectral_roll_off() {
        let song = decode_song("data/s16_mono_22_5kHz.flac").unwrap();
        let mut roll_off_desc = SpectralRollOffDesc::new(song.sample_rate);
        for chunk in song.sample_array.chunks_exact(SpectralDesc::HOP_SIZE) {
            roll_off_desc.do_(&chunk);
        }
        // Roll-off value computed here with phase vocoder: 2026.7644
        // Essentia value with spectrum / hann window: 1979.632683520047
        assert!(0.01 > (2026.76 - roll_off_desc.get_value()).abs());
    }

    #[test]
    fn test_spectral_centroid() {
        let song = decode_song("data/s16_mono_22_5kHz.flac").unwrap();
        let mut centroid_desc = SpectralCentroidDesc::new(song.sample_rate);
        for chunk in song.sample_array.chunks_exact(SpectralDesc::HOP_SIZE) {
            centroid_desc.do_(&chunk);
        }
        // Spectral centroid value computed here with phase vocoder: 1354.2273
        // Essentia value with spectrum / hann window: 1351
        assert!(0.01 > (1354.2273 - centroid_desc.get_value()).abs());
    }
}
<|endoftext|>


<commit_before>use Color;
use dimensions::Dimensions;
use opengl_graphics::glyph_cache::{
    GlyphCache,
    Character,
};
use opengl_graphics::Gl;
use label::FontSize;
use mouse::{
    ButtonState,
    Mouse,
};
use input;
use event::{
    GenericEvent,
    MouseCursorEvent,
    PressEvent,
    ReleaseEvent,
    RenderEvent,
    TextEvent,
};
use point::Point;
use theme::Theme;
use widget;
use widget::Widget;

/// User Interface Identifier. Each unique `widget::draw` call
/// should pass it's own unique UIID so that UiContext can keep
/// track of it's state.
pub type UIID = u64;

/// UiContext retains the state of all widgets and
/// data relevant to the draw_widget functions.
pub struct UiContext {
    data: Vec<(Widget, widget::Placing)>,
    pub theme: Theme,
    pub mouse: Mouse,
    pub keys_just_pressed: Vec<input::keyboard::Key>,
    pub keys_just_released: Vec<input::keyboard::Key>,
    pub text_just_entered: Vec<String>,
    glyph_cache: GlyphCache,
    prev_event_was_render: bool,
    /// Window width.
    pub win_w: f64,
    /// Window height.
    pub win_h: f64,
    /// The UIID of the widget drawn previously.
    prev_uiid: u64,
}

impl UiContext {

    /// Constructor for a UiContext.
    pub fn new(glyph_cache: GlyphCache, theme: Theme) -> UiContext {
        UiContext {
            data: Vec::from_elem(512, (widget::Widget::NoWidget, widget::Placing::NoPlace)),
            theme: theme,
            mouse: Mouse::new([0f64, 0f64], ButtonState::Up, ButtonState::Up, ButtonState::Up),
            keys_just_pressed: Vec::with_capacity(10u),
            keys_just_released: Vec::with_capacity(10u),
            text_just_entered: Vec::with_capacity(10u),
            glyph_cache: glyph_cache,
            prev_event_was_render: false,
            win_w: 0f64,
            win_h: 0f64,
            prev_uiid: 0u64,
        }
    }

    /// Handle game events and update the state.
    pub fn handle_event<E: GenericEvent>(&mut self, event: &E) {
        if self.prev_event_was_render {
            self.flush_input();
            self.prev_event_was_render = false;
        }
        event.render(|args| {
            self.win_w = args.width as f64;
            self.win_h = args.height as f64;
            self.prev_event_was_render = true;
        });
        event.mouse_cursor(|x, y| {
            self.mouse.pos = [x, y];
        });
        event.press(|button_type| {
            use input::Button;
            use input::MouseButton::Left;

            match button_type {
                Button::Mouse(button) => {
                    *match button {
                        Left => &mut self.mouse.left,
                        _/*input::mouse::Right*/ => &mut self.mouse.right,
                        //Middle => &mut self.mouse.middle,
                    } = ButtonState::Down;
                },
                Button::Keyboard(key) => self.keys_just_pressed.push(key),
            }
        });
        event.release(|button_type| {
            use input::Button;
            use input::MouseButton::Left;

            match button_type {
                Button::Mouse(button) => {
                    *match button {
                        Left => &mut self.mouse.left,
                        _/*input::mouse::Right*/ => &mut self.mouse.right,
                        //Middle => &mut self.mouse.middle,
                    } = ButtonState::Up;
                },
                Button::Keyboard(key) => self.keys_just_released.push(key),
            }
        });
        event.text(|text| {
            self.text_just_entered.push(text.to_string())
        });
    }

    /// Return the current mouse state.
    pub fn get_mouse_state(&self) -> Mouse {
        self.mouse
    }

    /// Return the vector of recently pressed keys.
    pub fn get_pressed_keys(&self) -> Vec<input::keyboard::Key> {
        self.keys_just_pressed.clone()
    }

    /// Return the vector of recently entered text.
    pub fn get_entered_text(&self) -> Vec<String> {
        self.text_just_entered.clone()
    }

    /// Return a mutable reference to the widget that matches the given ui_id
    pub fn get_widget(&mut self, ui_id: UIID, default: Widget) -> &mut Widget {
        let ui_id_idx = ui_id as uint;
        if self.data.len() > ui_id_idx {
            match &mut self.data[ui_id_idx] {
                &(widget::Widget::NoWidget, _) => {
                    match &mut self.data[ui_id_idx] {
                        &(ref mut widget, _) => {
                            *widget = default; widget
                        }
                    }
                },
                _ => {
                    match &mut self.data[ui_id_idx] {
                        &(ref mut widget, _) => widget
                    }
                },
            }
        } else {
            if ui_id_idx >= self.data.len() {
                let num_to_push = ui_id_idx - self.data.len();
                let mut vec = Vec::from_elem(num_to_push, (widget::Widget::NoWidget, widget::Placing::NoPlace));
                vec.push((default, widget::Placing::NoPlace));
                self.data.extend(vec.into_iter());
            } else {
                self.data[ui_id_idx] = (default, widget::Placing::NoPlace);
            }
            match &mut self.data[ui_id_idx] {
                &(ref mut widget, _) => widget,
            }
        }
    }

    /// Set the Placing for a particular widget.
    pub fn set_place(&mut self, ui_id: UIID, pos: Point, dim: Dimensions) {
        match &mut self.data[ui_id as uint] {
            &(_, ref mut placing) => {
                *placing = widget::Placing::Place(pos[0], pos[1], dim[0], dim[1])
            }
        }
        self.prev_uiid = ui_id;
    }

    /// Get the UIID of the previous widget.
    pub fn get_prev_uiid(&self) -> UIID { self.prev_uiid }

    /// Get the Placing for a particular widget.
    pub fn get_placing(&self, ui_id: UIID) -> widget::Placing {
        if ui_id as uint >= self.data.len() { widget::Placing::NoPlace }
        else {
            match self.data[ui_id as uint] { (_, ref placing) => *placing }
        }
    }

    /// Return a reference to a `Character` from the GlyphCache.
    pub fn get_character(&mut self, size: FontSize, ch: char) -> &Character {
        use graphics::character::CharacterCache;

        self.glyph_cache.character(size, ch)
    }

    /// Return the width of a 'Character'.
    pub fn get_character_w(&mut self, size: FontSize, ch: char) -> f64 {
        self.get_character(size, ch).width()
    }

    /// Flush all stored keys.
    pub fn flush_input(&mut self) {
        self.keys_just_pressed.clear();
        self.keys_just_released.clear();
        self.text_just_entered.clear();
    }

    /// Draws text
    pub fn draw_text(
        &mut self,
        graphics: &mut Gl,
        pos: Point,
        size: FontSize,
        color: Color,
        text: &str
    ) {
        use graphics::Context;
        use graphics::text::Text;
        use graphics::RelativeTransform;

        let Color(col) = color;
        let context = Context::abs(self.win_w, self.win_h)
                        .trans(pos[0], pos[1] + size as f64);
        Text::colored(col, size).draw(
            text,
            &mut self.glyph_cache,
            &context,
            graphics
        );
    }

}
<commit_msg>Removed artifacts when draw text.<commit_after>use Color;
use dimensions::Dimensions;
use opengl_graphics::glyph_cache::{
    GlyphCache,
    Character,
};
use opengl_graphics::Gl;
use label::FontSize;
use mouse::{
    ButtonState,
    Mouse,
};
use input;
use event::{
    GenericEvent,
    MouseCursorEvent,
    PressEvent,
    ReleaseEvent,
    RenderEvent,
    TextEvent,
};
use point::Point;
use theme::Theme;
use widget;
use widget::Widget;

/// User Interface Identifier. Each unique `widget::draw` call
/// should pass it's own unique UIID so that UiContext can keep
/// track of it's state.
pub type UIID = u64;

/// UiContext retains the state of all widgets and
/// data relevant to the draw_widget functions.
pub struct UiContext {
    data: Vec<(Widget, widget::Placing)>,
    pub theme: Theme,
    pub mouse: Mouse,
    pub keys_just_pressed: Vec<input::keyboard::Key>,
    pub keys_just_released: Vec<input::keyboard::Key>,
    pub text_just_entered: Vec<String>,
    glyph_cache: GlyphCache,
    prev_event_was_render: bool,
    /// Window width.
    pub win_w: f64,
    /// Window height.
    pub win_h: f64,
    /// The UIID of the widget drawn previously.
    prev_uiid: u64,
}

impl UiContext {

    /// Constructor for a UiContext.
    pub fn new(glyph_cache: GlyphCache, theme: Theme) -> UiContext {
        UiContext {
            data: Vec::from_elem(512, (widget::Widget::NoWidget, widget::Placing::NoPlace)),
            theme: theme,
            mouse: Mouse::new([0f64, 0f64], ButtonState::Up, ButtonState::Up, ButtonState::Up),
            keys_just_pressed: Vec::with_capacity(10u),
            keys_just_released: Vec::with_capacity(10u),
            text_just_entered: Vec::with_capacity(10u),
            glyph_cache: glyph_cache,
            prev_event_was_render: false,
            win_w: 0f64,
            win_h: 0f64,
            prev_uiid: 0u64,
        }
    }

    /// Handle game events and update the state.
    pub fn handle_event<E: GenericEvent>(&mut self, event: &E) {
        if self.prev_event_was_render {
            self.flush_input();
            self.prev_event_was_render = false;
        }
        event.render(|args| {
            self.win_w = args.width as f64;
            self.win_h = args.height as f64;
            self.prev_event_was_render = true;
        });
        event.mouse_cursor(|x, y| {
            self.mouse.pos = [x, y];
        });
        event.press(|button_type| {
            use input::Button;
            use input::MouseButton::Left;

            match button_type {
                Button::Mouse(button) => {
                    *match button {
                        Left => &mut self.mouse.left,
                        _/*input::mouse::Right*/ => &mut self.mouse.right,
                        //Middle => &mut self.mouse.middle,
                    } = ButtonState::Down;
                },
                Button::Keyboard(key) => self.keys_just_pressed.push(key),
            }
        });
        event.release(|button_type| {
            use input::Button;
            use input::MouseButton::Left;

            match button_type {
                Button::Mouse(button) => {
                    *match button {
                        Left => &mut self.mouse.left,
                        _/*input::mouse::Right*/ => &mut self.mouse.right,
                        //Middle => &mut self.mouse.middle,
                    } = ButtonState::Up;
                },
                Button::Keyboard(key) => self.keys_just_released.push(key),
            }
        });
        event.text(|text| {
            self.text_just_entered.push(text.to_string())
        });
    }

    /// Return the current mouse state.
    pub fn get_mouse_state(&self) -> Mouse {
        self.mouse
    }

    /// Return the vector of recently pressed keys.
    pub fn get_pressed_keys(&self) -> Vec<input::keyboard::Key> {
        self.keys_just_pressed.clone()
    }

    /// Return the vector of recently entered text.
    pub fn get_entered_text(&self) -> Vec<String> {
        self.text_just_entered.clone()
    }

    /// Return a mutable reference to the widget that matches the given ui_id
    pub fn get_widget(&mut self, ui_id: UIID, default: Widget) -> &mut Widget {
        let ui_id_idx = ui_id as uint;
        if self.data.len() > ui_id_idx {
            match &mut self.data[ui_id_idx] {
                &(widget::Widget::NoWidget, _) => {
                    match &mut self.data[ui_id_idx] {
                        &(ref mut widget, _) => {
                            *widget = default; widget
                        }
                    }
                },
                _ => {
                    match &mut self.data[ui_id_idx] {
                        &(ref mut widget, _) => widget
                    }
                },
            }
        } else {
            if ui_id_idx >= self.data.len() {
                let num_to_push = ui_id_idx - self.data.len();
                let mut vec = Vec::from_elem(num_to_push, (widget::Widget::NoWidget, widget::Placing::NoPlace));
                vec.push((default, widget::Placing::NoPlace));
                self.data.extend(vec.into_iter());
            } else {
                self.data[ui_id_idx] = (default, widget::Placing::NoPlace);
            }
            match &mut self.data[ui_id_idx] {
                &(ref mut widget, _) => widget,
            }
        }
    }

    /// Set the Placing for a particular widget.
    pub fn set_place(&mut self, ui_id: UIID, pos: Point, dim: Dimensions) {
        match &mut self.data[ui_id as uint] {
            &(_, ref mut placing) => {
                *placing = widget::Placing::Place(pos[0], pos[1], dim[0], dim[1])
            }
        }
        self.prev_uiid = ui_id;
    }

    /// Get the UIID of the previous widget.
    pub fn get_prev_uiid(&self) -> UIID { self.prev_uiid }

    /// Get the Placing for a particular widget.
    pub fn get_placing(&self, ui_id: UIID) -> widget::Placing {
        if ui_id as uint >= self.data.len() { widget::Placing::NoPlace }
        else {
            match self.data[ui_id as uint] { (_, ref placing) => *placing }
        }
    }

    /// Return a reference to a `Character` from the GlyphCache.
    pub fn get_character(&mut self, size: FontSize, ch: char) -> &Character {
        use graphics::character::CharacterCache;

        self.glyph_cache.character(size, ch)
    }

    /// Return the width of a 'Character'.
    pub fn get_character_w(&mut self, size: FontSize, ch: char) -> f64 {
        self.get_character(size, ch).width()
    }

    /// Flush all stored keys.
    pub fn flush_input(&mut self) {
        self.keys_just_pressed.clear();
        self.keys_just_released.clear();
        self.text_just_entered.clear();
    }

    /// Draws text
    pub fn draw_text(
        &mut self,
        graphics: &mut Gl,
        pos: Point,
        size: FontSize,
        color: Color,
        text: &str
    ) {
        use graphics::Context;
        use graphics::text::Text;
        use graphics::RelativeTransform;
        use std::num::Float;

        let Color(col) = color;
        let context = Context::abs(self.win_w, self.win_h)
                        .trans(pos[0].ceil(), pos[1].ceil() + size as f64);
        Text::colored(col, size).draw(
            text,
            &mut self.glyph_cache,
            &context,
            graphics
        );
    }

}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Mark Lee
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Low-level bindings to OSX/macOS/iOS's `CommonCrypto` library.

#![warn(missing_docs)]

extern crate libc;

use libc::{c_int, c_uint};

/// Total number of operations.
const MD5_CBLOCK: usize = 64;
/// Number of operations per round.
const MD5_LBLOCK: usize = MD5_CBLOCK / 4;
/// Number of bytes for an MD5 hash.
pub const MD5_DIGEST_LENGTH: usize = 16;

const SHA_LBLOCK: usize = 16;
/// Number of bytes for an SHA1 hash.
pub const SHA1_DIGEST_LENGTH: usize = 20;
/// Number of bytes for an SHA256 hash.
pub const SHA256_DIGEST_LENGTH: usize = 32;
/// Number of bytes for an SHA384 hash.
pub const SHA384_DIGEST_LENGTH: usize = 48;
/// Number of bytes for an SHA512 hash.
pub const SHA512_DIGEST_LENGTH: usize = 64;

/// Struct used to generate MD5 hashes.
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Clone, Debug, Default, PartialEq)]
#[repr(C)]
pub struct CC_MD5_CTX {
    A: c_uint,
    B: c_uint,
    C: c_uint,
    D: c_uint,
    Nl: c_uint,
    Nh: c_uint,
    data: [c_uint; MD5_LBLOCK],
    num: c_uint,
}

/// Struct used to generate SHA1 hashes.
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Clone, Debug, Default, PartialEq)]
#[repr(C)]
pub struct CC_SHA_CTX {
    h0: c_uint,
    h1: c_uint,
    h2: c_uint,
    h3: c_uint,
    h4: c_uint,
    Nl: c_uint,
    Nh: c_uint,
    data: [c_uint; SHA_LBLOCK],
    num: c_uint,
}

macro_rules! cc_sha2_struct {
    ($ctx_name: ident, $ty: ty) => {
        /// Struct used to generate SHA2 hashes with the given bits.
        #[allow(non_camel_case_types, non_snake_case)]
        #[derive(Clone, Debug, Default, PartialEq)]
        #[repr(C)]
        pub struct $ctx_name {
            count: [$ty; 2],
            hash: [$ty; 8],
            wbuf: [$ty; 16],
        }
    }
}

cc_sha2_struct!(CC_SHA256_CTX, u32);
cc_sha2_struct!(CC_SHA512_CTX, u64);

/// Digest algorithm used in `CCDigest*()` functions.
#[repr(C)]
pub enum CCDigestAlgorithm {
    /// No digest algorithm
    kCCDigestNone = 0,
    /// MD2
    kCCDigestMD2 = 1,
    /// MD4
    kCCDigestMD4 = 2,
    /// MD5
    kCCDigestMD5 = 3,
    /// RIPEMD-128
    kCCDigestRMD128 = 4,
    /// RIPEMD-160
    kCCDigestRMD160 = 5,
    /// RIPEMD-256
    kCCDigestRMD256 = 6,
    /// RIPEMD-320
    kCCDigestRMD320 = 7,
    /// SHA1
    kCCDigestSHA1 = 8,
    /// SHA224
    kCCDigestSHA224 = 9,
    /// SHA256
    kCCDigestSHA256 = 10,
    /// SHA384
    kCCDigestSHA384 = 11,
    /// SHA512
    kCCDigestSHA512 = 12,
    /// Skein, 128 bit (Deprecated in iPhoneOS 6.0 and MacOSX10.9)
    kCCDigestSkein128 = 13,
    /// Skein, 160 bit (Deprecated in iPhoneOS 6.0 and MacOSX10.9)
    kCCDigestSkein160 = 14,
    /// Skein, 224 bit (Deprecated in iPhoneOS 6.0 and MacOSX10.9)
    kCCDigestSkein224 = 16,
    /// Skein, 256 bit (Deprecated in iPhoneOS 6.0 and MacOSX10.9)
    kCCDigestSkein256 = 17,
    /// Skein, 384 bit (Deprecated in iPhoneOS 6.0 and MacOSX10.9)
    kCCDigestSkein384 = 18,
    /// Skein, 512 bit (Deprecated in iPhoneOS 6.0 and MacOSX10.9)
    kCCDigestSkein512 = 19,
}

const CC_DIGEST_SIZE: usize = 1032;

/// Context used in `CCDigest*()` functions.
#[allow(non_camel_case_types, non_snake_case)]
#[repr(C)]
pub struct CCDigestCtx {
    context: [u8; CC_DIGEST_SIZE],
}

/// Algorithm for use with `CCKeyDerivationPBKDF()`
#[repr(C)]
pub enum CCPBKDFAlgorithm {
    /// PBKDF2
    kCCPBKDF2 = 2,
}

/// Pseudo-random algorithm to use with `CCKeyDerivationPBKDF()`
#[repr(C)]
pub enum CCPseudoRandomAlgorithm {
    /// SHA-1
    kCCPRFHmacAlgSHA1 = 1,
    /// SHA-224
    kCCPRFHmacAlgSHA224 = 2,
    /// SHA-256
    kCCPRFHmacAlgSHA256 = 3,
    /// SHA-384
    kCCPRFHmacAlgSHA384 = 4,
    /// SHA-512
    kCCPRFHmacAlgSHA512 = 5,
}

extern "C" {
    /// Initializes MD5 hasher. See `man 3cc CC_MD5` for details.
    pub fn CC_MD5_Init(ctx: *mut CC_MD5_CTX) -> c_int;
    /// Appends data to be hashed. See `man 3cc CC_MD5` for details.
    pub fn CC_MD5_Update(ctx: *mut CC_MD5_CTX, data: *const u8, n: usize) -> c_int;
    /// Generates MD5 hash. See `man 3cc CC_MD5` for details.
    pub fn CC_MD5_Final(md: *mut u8, ctx: *mut CC_MD5_CTX) -> c_int;
    /// Initializes SHA1 hasher. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA1_Init(ctx: *mut CC_SHA_CTX) -> c_int;
    /// Appends data to be hashed. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA1_Update(ctx: *mut CC_SHA_CTX, data: *const u8, n: usize) -> c_int;
    /// Generates SHA1 hash. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA1_Final(md: *mut u8, ctx: *mut CC_SHA_CTX) -> c_int;
    /// Initializes SHA256 hasher. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA256_Init(ctx: *mut CC_SHA256_CTX) -> c_int;
    /// Appends data to be hashed. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA256_Update(ctx: *mut CC_SHA256_CTX, data: *const u8, n: usize) -> c_int;
    /// Generates SHA256 hash. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA256_Final(md: *mut u8, ctx: *mut CC_SHA256_CTX) -> c_int;
    /// Initializes SHA384 hasher. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA384_Init(ctx: *mut CC_SHA512_CTX) -> c_int;
    /// Appends data to be hashed. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA384_Update(ctx: *mut CC_SHA512_CTX, data: *const u8, n: usize) -> c_int;
    /// Generates SHA384 hash. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA384_Final(md: *mut u8, ctx: *mut CC_SHA512_CTX) -> c_int;
    /// Initializes SHA512 hasher. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA512_Init(ctx: *mut CC_SHA512_CTX) -> c_int;
    /// Appends data to be hashed. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA512_Update(ctx: *mut CC_SHA512_CTX, data: *const u8, n: usize) -> c_int;
    /// Generates SHA512 hash. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA512_Final(md: *mut u8, ctx: *mut CC_SHA512_CTX) -> c_int;
    /// Generic digest hasher.
    pub fn CCDigest(algorithm: CCDigestAlgorithm,
                    data: *const u8,
                    length: usize,
                    output: *mut u8)
                    -> c_int;
    /// Allocate and initialize a `CCDigestCtx` for a digest.
    pub fn CCDigestCreate(algorithm: CCDigestAlgorithm) -> *mut CCDigestCtx;
    /// Continue to digest data. Returns `0` on success.
    pub fn CCDigestUpdate(ctx: *mut CCDigestCtx, data: *const u8, length: usize) -> c_int;
    /// Conclude digest operations and produce the digest output. Returns `0` on success.
    pub fn CCDigestFinal(ctx: *mut CCDigestCtx, output: *mut u8) -> c_int;
    /// Clear and free a `CCDigestCtx`.
    pub fn CCDigestDestroy(ctx: *mut CCDigestCtx);
    /// Clear and re-initialize a `CCDigestCtx` for the same algorithm.
    pub fn CCDigestReset(ctx: *mut CCDigestCtx);
    /// Produce the digest output result for the bytes currently processed. Returns `0` on success.
    pub fn CCDigestGetDigest(ctx: *mut CCDigestCtx, output: *mut u8) -> c_int;
    /// Provides the block size of the digest algorithm. Returns `0` on failure.
    pub fn CCDigestGetBlockSize(algorithm: CCDigestAlgorithm) -> usize;
    /// Provides the digest output size of the digest algorithm. Returns `0` on failure.
    pub fn CCDigestGetOutputSize(algorithm: CCDigestAlgorithm) -> usize;
    /// Provides the block size of the digest algorithm. Returns `0` on failure.
    pub fn CCDigestGetBlockSizeFromRef(ctx: *mut CCDigestCtx) -> usize;
    /// Provides the digest output size of the digest algorithm. Returns `0` on failure.
    pub fn CCDigestGetOutputSizeFromRef(ctx: *mut CCDigestCtx) -> usize;

    /// Derive a key from a user-supplied password via PBKDF2
    pub fn CCKeyDerivationPBKDF(algorithm: CCPBKDFAlgorithm,
                                password: *const u8,
                                passwordLen: usize,
                                salt: *const u8,
                                saltLen: usize,
                                prf: CCPseudoRandomAlgorithm,
                                rounds: u32,
                                derivedKey: *mut u8,
                                derivedKeyLen: usize)
                                -> c_int;
}
<commit_msg>More doc fixes<commit_after>// Copyright (c) 2016 Mark Lee
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Low-level bindings to OSX/macOS/iOS's `CommonCrypto` library.

#![warn(missing_docs)]

extern crate libc;

use libc::{c_int, c_uint};

/// Total number of operations.
const MD5_CBLOCK: usize = 64;
/// Number of operations per round.
const MD5_LBLOCK: usize = MD5_CBLOCK / 4;
/// Number of bytes for an MD5 hash.
pub const MD5_DIGEST_LENGTH: usize = 16;

const SHA_LBLOCK: usize = 16;
/// Number of bytes for an SHA1 hash.
pub const SHA1_DIGEST_LENGTH: usize = 20;
/// Number of bytes for an SHA256 hash.
pub const SHA256_DIGEST_LENGTH: usize = 32;
/// Number of bytes for an SHA384 hash.
pub const SHA384_DIGEST_LENGTH: usize = 48;
/// Number of bytes for an SHA512 hash.
pub const SHA512_DIGEST_LENGTH: usize = 64;

/// Struct used to generate MD5 hashes.
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Clone, Debug, Default, PartialEq)]
#[repr(C)]
pub struct CC_MD5_CTX {
    A: c_uint,
    B: c_uint,
    C: c_uint,
    D: c_uint,
    Nl: c_uint,
    Nh: c_uint,
    data: [c_uint; MD5_LBLOCK],
    num: c_uint,
}

/// Struct used to generate SHA1 hashes.
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Clone, Debug, Default, PartialEq)]
#[repr(C)]
pub struct CC_SHA_CTX {
    h0: c_uint,
    h1: c_uint,
    h2: c_uint,
    h3: c_uint,
    h4: c_uint,
    Nl: c_uint,
    Nh: c_uint,
    data: [c_uint; SHA_LBLOCK],
    num: c_uint,
}

macro_rules! cc_sha2_struct {
    ($ctx_name: ident, $ty: ty) => {
        /// Struct used to generate SHA2 hashes with the given bits.
        #[allow(non_camel_case_types, non_snake_case)]
        #[derive(Clone, Debug, Default, PartialEq)]
        #[repr(C)]
        pub struct $ctx_name {
            count: [$ty; 2],
            hash: [$ty; 8],
            wbuf: [$ty; 16],
        }
    }
}

cc_sha2_struct!(CC_SHA256_CTX, u32);
cc_sha2_struct!(CC_SHA512_CTX, u64);

/// Digest algorithm used in `CCDigest*()` functions.
#[repr(C)]
pub enum CCDigestAlgorithm {
    /// No digest algorithm
    kCCDigestNone = 0,
    /// MD2
    kCCDigestMD2 = 1,
    /// MD4
    kCCDigestMD4 = 2,
    /// MD5
    kCCDigestMD5 = 3,
    /// RIPEMD-128
    kCCDigestRMD128 = 4,
    /// RIPEMD-160
    kCCDigestRMD160 = 5,
    /// RIPEMD-256
    kCCDigestRMD256 = 6,
    /// RIPEMD-320
    kCCDigestRMD320 = 7,
    /// SHA1
    kCCDigestSHA1 = 8,
    /// SHA224
    kCCDigestSHA224 = 9,
    /// SHA256
    kCCDigestSHA256 = 10,
    /// SHA384
    kCCDigestSHA384 = 11,
    /// SHA512
    kCCDigestSHA512 = 12,
    /// Skein, 128 bit (Deprecated in iPhoneOS 6.0 and MacOSX10.9)
    kCCDigestSkein128 = 13,
    /// Skein, 160 bit (Deprecated in iPhoneOS 6.0 and MacOSX10.9)
    kCCDigestSkein160 = 14,
    /// Skein, 224 bit (Deprecated in iPhoneOS 6.0 and MacOSX10.9)
    kCCDigestSkein224 = 16,
    /// Skein, 256 bit (Deprecated in iPhoneOS 6.0 and MacOSX10.9)
    kCCDigestSkein256 = 17,
    /// Skein, 384 bit (Deprecated in iPhoneOS 6.0 and MacOSX10.9)
    kCCDigestSkein384 = 18,
    /// Skein, 512 bit (Deprecated in iPhoneOS 6.0 and MacOSX10.9)
    kCCDigestSkein512 = 19,
}

const CC_DIGEST_SIZE: usize = 1032;

/// Context used in `CCDigest*()` functions.
#[allow(non_camel_case_types, non_snake_case)]
#[repr(C)]
pub struct CCDigestCtx {
    context: [u8; CC_DIGEST_SIZE],
}

/// Algorithm for use with `CCKeyDerivationPBKDF()`.
#[repr(C)]
pub enum CCPBKDFAlgorithm {
    /// PBKDF2
    kCCPBKDF2 = 2,
}

/// Pseudo-random algorithm to use with `CCKeyDerivationPBKDF()`.
#[repr(C)]
pub enum CCPseudoRandomAlgorithm {
    /// SHA-1
    kCCPRFHmacAlgSHA1 = 1,
    /// SHA-224
    kCCPRFHmacAlgSHA224 = 2,
    /// SHA-256
    kCCPRFHmacAlgSHA256 = 3,
    /// SHA-384
    kCCPRFHmacAlgSHA384 = 4,
    /// SHA-512
    kCCPRFHmacAlgSHA512 = 5,
}

extern "C" {
    /// Initializes MD5 hasher. See `man 3cc CC_MD5` for details.
    pub fn CC_MD5_Init(ctx: *mut CC_MD5_CTX) -> c_int;
    /// Appends data to be hashed. See `man 3cc CC_MD5` for details.
    pub fn CC_MD5_Update(ctx: *mut CC_MD5_CTX, data: *const u8, n: usize) -> c_int;
    /// Generates MD5 hash. See `man 3cc CC_MD5` for details.
    pub fn CC_MD5_Final(md: *mut u8, ctx: *mut CC_MD5_CTX) -> c_int;
    /// Initializes SHA1 hasher. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA1_Init(ctx: *mut CC_SHA_CTX) -> c_int;
    /// Appends data to be hashed. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA1_Update(ctx: *mut CC_SHA_CTX, data: *const u8, n: usize) -> c_int;
    /// Generates SHA1 hash. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA1_Final(md: *mut u8, ctx: *mut CC_SHA_CTX) -> c_int;
    /// Initializes SHA256 hasher. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA256_Init(ctx: *mut CC_SHA256_CTX) -> c_int;
    /// Appends data to be hashed. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA256_Update(ctx: *mut CC_SHA256_CTX, data: *const u8, n: usize) -> c_int;
    /// Generates SHA256 hash. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA256_Final(md: *mut u8, ctx: *mut CC_SHA256_CTX) -> c_int;
    /// Initializes SHA384 hasher. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA384_Init(ctx: *mut CC_SHA512_CTX) -> c_int;
    /// Appends data to be hashed. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA384_Update(ctx: *mut CC_SHA512_CTX, data: *const u8, n: usize) -> c_int;
    /// Generates SHA384 hash. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA384_Final(md: *mut u8, ctx: *mut CC_SHA512_CTX) -> c_int;
    /// Initializes SHA512 hasher. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA512_Init(ctx: *mut CC_SHA512_CTX) -> c_int;
    /// Appends data to be hashed. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA512_Update(ctx: *mut CC_SHA512_CTX, data: *const u8, n: usize) -> c_int;
    /// Generates SHA512 hash. See `man 3cc CC_SHA` for details.
    pub fn CC_SHA512_Final(md: *mut u8, ctx: *mut CC_SHA512_CTX) -> c_int;
    /// Generic digest hasher.
    pub fn CCDigest(algorithm: CCDigestAlgorithm,
                    data: *const u8,
                    length: usize,
                    output: *mut u8)
                    -> c_int;
    /// Allocate and initialize a `CCDigestCtx` for a digest.
    pub fn CCDigestCreate(algorithm: CCDigestAlgorithm) -> *mut CCDigestCtx;
    /// Continue to digest data. Returns `0` on success.
    pub fn CCDigestUpdate(ctx: *mut CCDigestCtx, data: *const u8, length: usize) -> c_int;
    /// Conclude digest operations and produce the digest output. Returns `0` on success.
    pub fn CCDigestFinal(ctx: *mut CCDigestCtx, output: *mut u8) -> c_int;
    /// Clear and free a `CCDigestCtx`.
    pub fn CCDigestDestroy(ctx: *mut CCDigestCtx);
    /// Clear and re-initialize a `CCDigestCtx` for the same algorithm.
    pub fn CCDigestReset(ctx: *mut CCDigestCtx);
    /// Produce the digest output result for the bytes currently processed. Returns `0` on success.
    pub fn CCDigestGetDigest(ctx: *mut CCDigestCtx, output: *mut u8) -> c_int;
    /// Provides the block size of the digest algorithm. Returns `0` on failure.
    pub fn CCDigestGetBlockSize(algorithm: CCDigestAlgorithm) -> usize;
    /// Provides the digest output size of the digest algorithm. Returns `0` on failure.
    pub fn CCDigestGetOutputSize(algorithm: CCDigestAlgorithm) -> usize;
    /// Provides the block size of the digest algorithm. Returns `0` on failure.
    pub fn CCDigestGetBlockSizeFromRef(ctx: *mut CCDigestCtx) -> usize;
    /// Provides the digest output size of the digest algorithm. Returns `0` on failure.
    pub fn CCDigestGetOutputSizeFromRef(ctx: *mut CCDigestCtx) -> usize;

    /// Derive a key from a user-supplied password via PBKDF2.
    pub fn CCKeyDerivationPBKDF(algorithm: CCPBKDFAlgorithm,
                                password: *const u8,
                                passwordLen: usize,
                                salt: *const u8,
                                saltLen: usize,
                                prf: CCPseudoRandomAlgorithm,
                                rounds: u32,
                                derivedKey: *mut u8,
                                derivedKeyLen: usize)
                                -> c_int;
}
<|endoftext|>


<commit_before>use bullet::{BulletDrawable, bullets::Bullets};
use cgmath;
use cgmath::{Deg, Point2};
use character::{CharacterDrawable, controls::CharacterInputState};
use critter::CritterData;
use data;
use game::constants::{ASPECT_RATIO, NORMAL_DEATH_SPRITE_OFFSET, SPRITE_OFFSET, ZOMBIE_SHEET_TOTAL_WIDTH, ZOMBIE_STILL_SPRITE_OFFSET};
use game::get_random_bool;
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use graphics::{camera::CameraInputState, Dimensions, direction, direction_movement, get_orientation, load_texture, orientation::{Orientation, Stance}, overlaps};
use shaders::{CharacterSheet, critter_pipeline, Position, Projection, VertexData};
use specs;
use specs::{Fetch, ReadStorage, WriteStorage};
use zombie::zombies::Zombies;

pub mod zombies;

const SHADER_VERT: &[u8] = include_bytes!("../shaders/character.v.glsl");
const SHADER_FRAG: &[u8] = include_bytes!("../shaders/character.f.glsl");

#[derive(Debug, Clone)]
pub struct ZombieDrawable {
  projection: Projection,
  pub position: Position,
  previous_position: Position,
  orientation: Orientation,
  pub stance: Stance,
  direction: Orientation,
  pub movement_direction: Point2<f32>,
  zombie_idx: usize,
  zombie_death_idx: usize,
}

impl ZombieDrawable {
  pub fn new(position: Position) -> ZombieDrawable {
    let view = Dimensions::get_view_matrix();
    ZombieDrawable {
      projection: Projection {
        model: view.into(),
        view: view.into(),
        proj: cgmath::perspective(Deg(60.0f32), ASPECT_RATIO, 0.1, 4000.0).into(),
      },
      position,
      previous_position: Position::new([0.0, 0.0]),
      orientation: Orientation::Left,
      stance: Stance::Still,
      direction: Orientation::Left,
      movement_direction: Point2::new(0.0, 0.0),
      zombie_idx: 0,
      zombie_death_idx: 0,
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, character: &CharacterDrawable, bullets: &[BulletDrawable]) {
    self.projection = *world_to_clip;

    let offset_delta =
      Position::new([
        ci.x_movement - self.previous_position.position[0],
        ci.y_movement - self.previous_position.position[1]
      ]);

    self.previous_position = Position::new([
      ci.x_movement,
      ci.y_movement
    ]);
    let movement_speed = 0.2;

    if self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath {
      let dir = direction(
        Point2::new(self.position.position[0], self.position.position[1]),
        Point2::new(character.position.position[0], character.position.position[1])
      );

      self.direction = get_orientation(dir);

      self.movement_direction = direction_movement(dir);
      self.stance = Stance::Walking;
    } else {
      self.movement_direction = Point2::new(0.0, 0.0);
    }

    self.position = Position::new([
      self.position.position[0] + offset_delta.position[0] + (self.movement_direction.x * movement_speed),
      self.position.position[1] + offset_delta.position[1] + (self.movement_direction.y * movement_speed)
    ]);
    bullets.iter().for_each(|bullet| {
      if overlaps(self.position, bullet.position, 80.0, 80.0) && self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath {
        self.stance =
          if get_random_bool() {
            Stance::NormalDeath
          } else {
            Stance::CriticalDeath
          };
      }
    });
  }

  pub fn update_alive_idx(&mut self, max_idx: usize) {
    if self.zombie_idx < max_idx {
      self.zombie_idx += 1;
    } else {
      self.zombie_idx = 0;
    }
  }

  pub fn update_death_idx(&mut self, max_idx: usize) {
    if self.zombie_death_idx < max_idx {
      self.zombie_death_idx += 1;
    }
  }
}

pub struct ZombieDrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, critter_pipeline::Data<R>>,
  data: Vec<CritterData>,
}

impl<R: gfx::Resources> ZombieDrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> ZombieDrawSystem<R>
                where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let zombie_bytes = include_bytes!("../../assets/zombie.png");

    let vertex_data: Vec<VertexData> =
      vec![
        VertexData::new([-25.0, -35.0], [0.0, 1.0]),
        VertexData::new([25.0, -35.0], [1.0, 1.0]),
        VertexData::new([25.0, 35.0], [1.0, 0.0]),
        VertexData::new([-25.0, 35.0], [0.0, 0.0]),
      ];

    let index_data: [u16; 6] = [0, 1, 2, 2, 3, 0];

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data, &index_data[..]);

    let char_texture = load_texture(factory, zombie_bytes).unwrap();
    let pso = factory
      .create_pipeline_simple(SHADER_VERT,
                              SHADER_FRAG,
                              critter_pipeline::new())
      .unwrap();

    let pipeline_data = critter_pipeline::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (char_texture, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_zombie();

    ZombieDrawSystem {
      bundle: gfx::Bundle::new(slice, pso, pipeline_data),
      data,
    }
  }

  fn get_next_sprite(&self, drawable: &mut ZombieDrawable) -> CharacterSheet {
    let zombie_sprite = match drawable.stance {
      Stance::Still => {
        let sprite_idx = (drawable.direction as usize * 4 + drawable.zombie_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::Walking if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::NormalDeath if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 6 + drawable.zombie_death_idx + NORMAL_DEATH_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::CriticalDeath if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 8 + drawable.zombie_death_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      _ => {
        drawable.direction = drawable.orientation;
        let sprite_idx = (drawable.orientation as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
    };

    let (y_div, row_idx) =
      if drawable.stance == Stance::NormalDeath || drawable.stance == Stance::CriticalDeath {
        (0.0, 2)
      } else {
        (1.0, 2)
      };

    let elements_x = ZOMBIE_SHEET_TOTAL_WIDTH / (zombie_sprite.0.data[2] + SPRITE_OFFSET);
    CharacterSheet {
      x_div: elements_x,
      y_div,
      row_idx,
      index: zombie_sprite.1 as f32,
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut ZombieDrawable,
                 encoder: &mut gfx::Encoder<R, C>)
                 where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb,
                                   &self.get_next_sprite(&mut drawable));
    self.bundle.encode(encoder);
  }
}

#[derive(Debug)]
pub struct PreDrawSystem;

impl PreDrawSystem {
  pub fn new() -> PreDrawSystem {
    PreDrawSystem {}
  }
}

impl<'a> specs::System<'a> for PreDrawSystem {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, Zombies>,
                     ReadStorage<'a, CameraInputState>,
                     ReadStorage<'a, CharacterInputState>,
                     ReadStorage<'a, CharacterDrawable>,
                     ReadStorage<'a, Bullets>,
                     Fetch<'a, Dimensions>);

  fn run(&mut self, (mut zombies, camera_input, character_input, character, bullets, dim): Self::SystemData) {
    use specs::Join;

    for (zs, camera, ci, c, bs) in (&mut zombies, &camera_input, &character_input, &character, &bullets).join() {
      let world_to_clip = dim.world_to_projection(camera);

      for z in &mut zs.zombies {
        z.update(&world_to_clip, ci, c, &bs.bullets);
      }
    }
  }
}
<commit_msg>Extract bullet hit check to new function<commit_after>use bullet::{BulletDrawable, bullets::Bullets};
use cgmath;
use cgmath::{Deg, Point2};
use character::{CharacterDrawable, controls::CharacterInputState};
use critter::CritterData;
use data;
use game::constants::{ASPECT_RATIO, NORMAL_DEATH_SPRITE_OFFSET, SPRITE_OFFSET, ZOMBIE_SHEET_TOTAL_WIDTH, ZOMBIE_STILL_SPRITE_OFFSET};
use game::get_random_bool;
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use graphics::{camera::CameraInputState, Dimensions, direction, direction_movement, get_orientation, load_texture, orientation::{Orientation, Stance}, overlaps};
use shaders::{CharacterSheet, critter_pipeline, Position, Projection, VertexData};
use specs;
use specs::{Fetch, ReadStorage, WriteStorage};
use zombie::zombies::Zombies;

pub mod zombies;

const SHADER_VERT: &[u8] = include_bytes!("../shaders/character.v.glsl");
const SHADER_FRAG: &[u8] = include_bytes!("../shaders/character.f.glsl");

#[derive(Debug, Clone)]
pub struct ZombieDrawable {
  projection: Projection,
  pub position: Position,
  previous_position: Position,
  orientation: Orientation,
  pub stance: Stance,
  direction: Orientation,
  pub movement_direction: Point2<f32>,
  zombie_idx: usize,
  zombie_death_idx: usize,
}

impl ZombieDrawable {
  pub fn new(position: Position) -> ZombieDrawable {
    let view = Dimensions::get_view_matrix();
    ZombieDrawable {
      projection: Projection {
        model: view.into(),
        view: view.into(),
        proj: cgmath::perspective(Deg(60.0f32), ASPECT_RATIO, 0.1, 4000.0).into(),
      },
      position,
      previous_position: Position::new([0.0, 0.0]),
      orientation: Orientation::Left,
      stance: Stance::Still,
      direction: Orientation::Left,
      movement_direction: Point2::new(0.0, 0.0),
      zombie_idx: 0,
      zombie_death_idx: 0,
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, character: &CharacterDrawable/*, bullets: &[BulletDrawable]*/) {
    self.projection = *world_to_clip;

    let offset_delta =
      Position::new([
        ci.x_movement - self.previous_position.position[0],
        ci.y_movement - self.previous_position.position[1]
      ]);

    self.previous_position = Position::new([
      ci.x_movement,
      ci.y_movement
    ]);
    let movement_speed = 0.2;

    if self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath {
      let dir = direction(
        Point2::new(self.position.position[0], self.position.position[1]),
        Point2::new(character.position.position[0], character.position.position[1])
      );

      self.direction = get_orientation(dir);

      self.movement_direction = direction_movement(dir);
      self.stance = Stance::Walking;
    } else {
      self.movement_direction = Point2::new(0.0, 0.0);
    }

    self.position = Position::new([
      self.position.position[0] + offset_delta.position[0] + (self.movement_direction.x * movement_speed),
      self.position.position[1] + offset_delta.position[1] + (self.movement_direction.y * movement_speed)
    ]);
  }

  pub fn check_bullet_hits(&mut self, bullets: &[BulletDrawable]) {
    bullets.iter().for_each(|bullet| {
      if overlaps(self.position, bullet.position, 80.0, 80.0) && self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath {
        self.stance =
          if get_random_bool() {
            Stance::NormalDeath
          } else {
            Stance::CriticalDeath
          };
      }
    });
  }

  pub fn update_alive_idx(&mut self, max_idx: usize) {
    if self.zombie_idx < max_idx {
      self.zombie_idx += 1;
    } else {
      self.zombie_idx = 0;
    }
  }

  pub fn update_death_idx(&mut self, max_idx: usize) {
    if self.zombie_death_idx < max_idx {
      self.zombie_death_idx += 1;
    }
  }
}

pub struct ZombieDrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, critter_pipeline::Data<R>>,
  data: Vec<CritterData>,
}

impl<R: gfx::Resources> ZombieDrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> ZombieDrawSystem<R>
                where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let zombie_bytes = include_bytes!("../../assets/zombie.png");

    let vertex_data: Vec<VertexData> =
      vec![
        VertexData::new([-25.0, -35.0], [0.0, 1.0]),
        VertexData::new([25.0, -35.0], [1.0, 1.0]),
        VertexData::new([25.0, 35.0], [1.0, 0.0]),
        VertexData::new([-25.0, 35.0], [0.0, 0.0]),
      ];

    let index_data: [u16; 6] = [0, 1, 2, 2, 3, 0];

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data, &index_data[..]);

    let char_texture = load_texture(factory, zombie_bytes).unwrap();
    let pso = factory
      .create_pipeline_simple(SHADER_VERT,
                              SHADER_FRAG,
                              critter_pipeline::new())
      .unwrap();

    let pipeline_data = critter_pipeline::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (char_texture, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_zombie();

    ZombieDrawSystem {
      bundle: gfx::Bundle::new(slice, pso, pipeline_data),
      data,
    }
  }

  fn get_next_sprite(&self, drawable: &mut ZombieDrawable) -> CharacterSheet {
    let zombie_sprite = match drawable.stance {
      Stance::Still => {
        let sprite_idx = (drawable.direction as usize * 4 + drawable.zombie_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::Walking if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::NormalDeath if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 6 + drawable.zombie_death_idx + NORMAL_DEATH_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::CriticalDeath if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 8 + drawable.zombie_death_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      _ => {
        drawable.direction = drawable.orientation;
        let sprite_idx = (drawable.orientation as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
    };

    let (y_div, row_idx) =
      if drawable.stance == Stance::NormalDeath || drawable.stance == Stance::CriticalDeath {
        (0.0, 2)
      } else {
        (1.0, 2)
      };

    let elements_x = ZOMBIE_SHEET_TOTAL_WIDTH / (zombie_sprite.0.data[2] + SPRITE_OFFSET);
    CharacterSheet {
      x_div: elements_x,
      y_div,
      row_idx,
      index: zombie_sprite.1 as f32,
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut ZombieDrawable,
                 encoder: &mut gfx::Encoder<R, C>)
                 where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb,
                                   &self.get_next_sprite(&mut drawable));
    self.bundle.encode(encoder);
  }
}

#[derive(Debug)]
pub struct PreDrawSystem;

impl PreDrawSystem {
  pub fn new() -> PreDrawSystem {
    PreDrawSystem {}
  }
}

impl<'a> specs::System<'a> for PreDrawSystem {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, Zombies>,
                     ReadStorage<'a, CameraInputState>,
                     ReadStorage<'a, CharacterInputState>,
                     ReadStorage<'a, CharacterDrawable>,
                     ReadStorage<'a, Bullets>,
                     Fetch<'a, Dimensions>);

  fn run(&mut self, (mut zombies, camera_input, character_input, character, bullets, dim): Self::SystemData) {
    use specs::Join;

    for (zs, camera, ci, c, bs) in (&mut zombies, &camera_input, &character_input, &character, &bullets).join() {
      let world_to_clip = dim.world_to_projection(camera);

      for z in &mut zs.zombies {
        z.update(&world_to_clip, ci, c);
        z.check_bullet_hits(&bs.bullets);
      }
    }
  }
}
<|endoftext|>