ptcrab 0.1.0

A pure-Rust alternative to the official pxtone library.
Documentation
//! Example of creating a ptvoice from scratch.
#![allow(unused)]

use ptcrab::data::WriteTo;
use ptcrab::ptvoice::{PtvEnvelope, PtvUnit, PtvWave, Ptvoice};
use ptcrab::{Key, Tuning, Volume};

use anyhow::Result as AnyResult;
use std::env::args;
use std::f32::consts::TAU;
use std::fs::File;

//--------------------------------------------------------------------------------------------------

fn main() -> AnyResult<()> {
    // Create a `PtvWave` that defines a ptvoice unit's waveform.
    //----------------------------------------------------------------------------------------------
    // Coordinate points can be manually specified...
    let wave_saw = PtvWave::coordinate_from_points(Box::new([(0, 0), (0, 64), (255, -64)]));
    // ... or generated by a function over 0 ≤ x < 1 (in this case sine cubed).
    let wave_sine3 = PtvWave::coordinate_from_function(31, |x| (x * TAU).sin().powi(3));
    // Oscillator `(harmonic_num, amplitude)` pairs must be manually specified.
    let wave_osc = PtvWave::oscillator_from_pairs(Box::new([
        (1, 128), // 1st harmonic with 128 amplitude.
        (2, -64), // 2nd harmonic with 64 amplitude, inverted.
        (4, 32),  // 4th harmonic with 32 amplitude.
    ]));

    // Create a `PtvEnvelope` that controls a unit's volume over time.
    //----------------------------------------------------------------------------------------------
    // An envelope with 100ms attack and 50ms release.
    let envelope_attack = PtvEnvelope::new(Box::new([(100, 128)]), 50);
    // An envelope that creates a 160ms pulse after 80ms.
    let envelope_pulse = PtvEnvelope::new(Box::new([(79, 0), (80, 128), (240, 128), (241, 0)]), 1);

    // Create `PtvUnit`s with the desired parameters, waveforms, and envelopes.
    //----------------------------------------------------------------------------------------------
    let unit_1 = PtvUnit {
        // Slightly detune this unit.
        tuning: 0.9995.into(),
        wave: Some(wave_sine3),
        envelope: Some(envelope_attack),
        ..Default::default()
    };
    let unit_2 = PtvUnit {
        // Make this unit quieter than default (64).
        volume: 32.into(),
        wave: Some(wave_osc),
        envelope: Some(envelope_pulse),
        ..Default::default()
    };

    //----------------------------------------------------------------------------------------------

    // Finally, construct the ptvoice from the two units.
    let ptv = Ptvoice::new(Box::new([unit_1, unit_2]));

    // If a filename was given, write the ptvoice to the output file. Otherwise, just print it.
    if let Some(filename) = args().nth(1) {
        let mut file = File::create(filename)?;
        ptv.write_to(&mut file)?;
    } else {
        println!("{:?}", ptv);
    }

    Ok(())
}