notesy::sound

Playing sounds: files named in the manifest's [[sounds]], a sound file's bytes it got some other way, and sound it makes as it runs. All of it takes the sound permission. notesy plays them; the script only says what, so a sound goes on while the script does other things, and a slow script can't make one stutter.

rustuse notesy::{commands, events, log, sound, Event};

pub fn ready() {
    commands::add("ding", "Ding", |command| sound::play("ding"));
    commands::add("chirp", "Chirp", |command| {
        sound::synth(#{ wave: "triangle", hz: 880.0, to_hz: 1760.0, ms: 120, volume: 0.4 });
    });
    events::on(Event::SoundEnded, |event| log::info(`sound ${event.id} is over`));
}

Every function that plays a sound gives back its id, a number of the script's own, for sound::stop and for the events it sends.

Function What it does
sound::play(name) Plays one of its [[sounds]].
sound::play_with(name, options) The same, with #{ volume, looped }: volume from 0 to 1 (1 unless said), looped to play it over and over until stopped.
sound::play_bytes(bytes) Plays a sound file's bytes, a WAV, MP3, FLAC or Ogg Vorbis file's, at most 16 MB: from files::read_bytes, net::fetch, or sound::wav.
sound::tone(hz, ms) A plain beep: a sine wave at hz for ms milliseconds, at half volume.
sound::synth(options) A note made as it plays; see below.
sound::samples(samples, rate, channels) Plays samples it worked out; see below.
sound::stream(rate, channels) A sound it feeds as it plays; see Streams.
sound::push(id, samples) More for stream id. Gives back how many samples it took.
sound::stop(id) Stops one of its sounds. It fades out over a few milliseconds rather than clicking.
sound::stop_all() Stops every sound of its own.
sound::wav(samples, rate, channels) Samples as a 16-bit WAV file's bytes, to keep with files::write or play again.

A plugin plays at most 8 sounds at once: a ninth stops its oldest. Its sounds stop when it does. Each one it plays is in its log, so its dev tools show what it played and when.

#Making sound

sound::synth makes a note in notesy, which costs the script nothing however long it lasts:

rustuse notesy::sound;

pub fn ready() {
    sound::synth(#{
        wave: "square",   // sine (the default), square, saw, triangle or noise
        hz: 220.0,        // its pitch, 20 to 20,000 (440 unless said)
        to_hz: 110.0,     // sliding there as it plays (staying at hz unless said)
        ms: 400,          // how long, up to 60 seconds (200 unless said)
        attack: 5,        // milliseconds coming in (5 unless said)
        release: 120,     // milliseconds dying away (50 unless said)
        volume: 0.5,      // 0 to 1 (1 unless said)
    });
}

sound::samples plays samples the script made itself: a list of numbers from -1 to 1 (louder ones are held at 1), rate of them a second for each channel, from 8,000 to 192,000, and 1 channel or 2. With 2 they take turns, left then right. At most 60 seconds of them at once.

rustuse notesy::{files, math, sound};

pub fn ready() {
    // A second of a chord, worked out sample by sample.
    let rate = 44100;
    let turn = 2.0 * math::pi();
    let samples = [];
    for i in 0..rate {
        let t = math::float(i) / math::float(rate);
        let s = math::sin(t * 261.63 * turn) + math::sin(t * 329.63 * turn) + math::sin(t * 392.0 * turn);
        samples.push(s / 3.0 * (1.0 - t));
    }
    sound::samples(samples, rate, 1);
    // And kept, to play again later.
    files::write("chord.wav", sound::wav(samples, rate, 1));
}

#Streams

A stream is sound with no end the script knows of: a metronome, a drone, a synthesizer it plays from a panel. It plays what it's pushed, in order, silence while it has nothing, and tells the script sound.wants (with ms, how much it has left) as it runs low, once, until it's given enough again. It ends when stopped, or after 10 seconds with nothing; at most 10 seconds of samples wait in it at once.

rustuse notesy::{events, sound, Event};

pub fn ready() {
    let rate = 48000;
    let tick = [];
    for i in 0..2400 {
        tick.push(if i < 240 { 0.6 } else { 0.0 });
    }
    let quiet = [];
    for i in 0..21600 {
        quiet.push(0.0);
    }
    let stream = sound::stream(rate, 1);
    events::on(Event::SoundWants, |event| {
        if event.id == stream {
            sound::push(stream, tick);
            sound::push(stream, quiet);
        }
    });
}

Every page