tunes 1.1.0

A music composition, synthesis, and audio generation library
Documentation
// GPU Synthesis Compute Shader (WGSL)
//
// This shader synthesizes audio on the GPU at 500-1000x realtime speed.
// Each workgroup processes a chunk of samples in parallel.

// Note parameters (uploaded from CPU)
struct NoteParams {
    frequency: f32,           // Hz
    duration: f32,            // seconds
    sample_rate: f32,         // samples per second (usually 44100)
    waveform: u32,            // 0=Sine, 1=Saw, 2=Square, 3=Triangle

    // Envelope (ADSR)
    attack: f32,              // seconds
    decay: f32,               // seconds
    sustain: f32,             // 0.0 to 1.0
    release: f32,             // seconds

    // FM synthesis
    fm_enabled: u32,          // 0=off, 1=on
    fm_mod_ratio: f32,        // modulator frequency ratio
    fm_mod_index: f32,        // modulation depth

    velocity: f32,            // 0.0 to 1.0
    _padding: u32,            // Alignment padding
}

// Input: Note parameters
@group(0) @binding(0)
var<storage, read> params: NoteParams;

// Output: Generated samples
@group(0) @binding(1)
var<storage, read_write> output: array<f32>;

// Constants
const PI: f32 = 3.14159265359;
const TWO_PI: f32 = 6.28318530718;

// Waveform generators
fn sine_wave(phase: f32) -> f32 {
    return sin(phase * TWO_PI);
}

fn saw_wave(phase: f32) -> f32 {
    return 2.0 * (phase - floor(phase + 0.5));
}

fn square_wave(phase: f32) -> f32 {
    let frac = phase - floor(phase);
    return select(-1.0, 1.0, frac < 0.5);
}

fn triangle_wave(phase: f32) -> f32 {
    let frac = phase - floor(phase);
    return 4.0 * abs(frac - 0.5) - 1.0;
}

// Generate waveform sample at given phase
fn generate_waveform(waveform: u32, phase: f32) -> f32 {
    switch waveform {
        case 0u: { return sine_wave(phase); }
        case 1u: { return saw_wave(phase); }
        case 2u: { return square_wave(phase); }
        case 3u: { return triangle_wave(phase); }
        default: { return sine_wave(phase); }
    }
}

// ADSR envelope
fn envelope_amplitude(time: f32, note_duration: f32, attack: f32, decay: f32, sustain: f32, release: f32) -> f32 {
    let total_duration = attack + decay + note_duration + release;

    if time < attack {
        // Attack phase
        return time / attack;
    } else if time < attack + decay {
        // Decay phase
        let decay_time = time - attack;
        return 1.0 - (1.0 - sustain) * (decay_time / decay);
    } else if time < attack + decay + note_duration {
        // Sustain phase
        return sustain;
    } else if time < total_duration {
        // Release phase
        let release_time = time - (attack + decay + note_duration);
        return sustain * (1.0 - release_time / release);
    } else {
        // After release
        return 0.0;
    }
}

// FM synthesis
fn fm_synthesis(time: f32, carrier_freq: f32, mod_ratio: f32, mod_index: f32) -> f32 {
    let modulator_freq = carrier_freq * mod_ratio;
    let modulator = sine_wave(time * modulator_freq);
    let phase_modulation = modulator * mod_index;
    return sine_wave(time * carrier_freq + phase_modulation);
}

// Main compute shader
@compute @workgroup_size(256)
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
    let sample_idx = global_id.x;
    let total_duration = params.attack + params.decay + params.duration + params.release;
    let total_samples = u32(total_duration * params.sample_rate);

    // Bounds check
    if sample_idx >= total_samples {
        return;
    }

    // Calculate time in seconds
    let time = f32(sample_idx) / params.sample_rate;

    // Generate oscillator output
    var oscillator_output: f32;

    if params.fm_enabled != 0u {
        // FM synthesis
        oscillator_output = fm_synthesis(
            time,
            params.frequency,
            params.fm_mod_ratio,
            params.fm_mod_index
        );
    } else {
        // Basic waveform
        let phase = time * params.frequency;
        oscillator_output = generate_waveform(params.waveform, phase);
    }

    // Apply envelope
    let envelope = envelope_amplitude(
        time,
        params.duration,
        params.attack,
        params.decay,
        params.sustain,
        params.release
    );

    // Apply velocity and write output
    output[sample_idx] = oscillator_output * envelope * params.velocity;
}