Modules / Sound

audiodawplugin

A song as one project of instruments, effects, note and automation tracks, clips and buses, played through Tone.js.

Putting it in a scene

A song is a project, one JSON object, handed to load on the page. The plugin has no row and no server half: a song is heard on the page alone, and nothing of it goes down the wire. A scene that lists audiodaw must give world.audiodaw.

"plugins": ["loader", "scenemanager", "player", "render3d", "audiodaw"],
"world": {
  "audiodaw": { "lookAhead": 0.5, "latencyHint": 0.2 }
}

A project, here with a bass, a pad sent to a reverb bus, a drum kit, and an automation track that fades in the pad's chorus:

{
  "bpm": 120, "timeSignature": 4, "swing": 0, "swingSubdivision": "8n", "ppq": 192,
  "loop": { "start": 0, "end": 3072 },
  "rack": [
    { "name": "bass", "instrument": { "is": "MonoSynth", "voices": 1, "options": { "oscillator": { "type": "sawtooth" } } },
      "effects": [], "channel": { "volume": -6, "pan": 0, "mute": false, "solo": false }, "sends": {} },
    { "name": "pad", "instrument": { "is": "Synth", "voices": 4, "options": { "envelope": { "attack": 0.4, "release": 1.5 } } },
      "effects": [{ "is": "Chorus", "on": true, "options": { "frequency": 1.5, "depth": 0.5 } }],
      "channel": { "volume": -10, "pan": 0, "mute": false, "solo": false }, "sends": { "room": -12 } },
    { "name": "drums", "instrument": { "is": "DrumKit", "options": { "volume": 0, "pads": {
        "C1": { "name": "kick", "is": "MembraneSynth", "pitch": "C1", "options": {} },
        "F#1": { "name": "closed hat", "is": "NoiseSynth", "choke": "hat", "options": { "envelope": { "decay": 0.05 } } },
        "A#1": { "name": "open hat", "is": "NoiseSynth", "choke": "hat", "options": { "envelope": { "decay": 0.4 } } } } } },
      "effects": [], "channel": { "volume": -4, "pan": 0, "mute": false, "solo": false }, "sends": {} }
  ],
  "tracks": [
    { "kind": "notes", "name": "bass", "color": "#e0a040", "target": 0, "mute": false, "solo": false,
      "patterns": { "a": { "length": 768, "notes": [{ "time": 0, "note": "C2", "duration": 192, "velocity": 0.9 }] } } },
    { "kind": "notes", "name": "beat", "color": "#d05050", "target": 2, "mute": false, "solo": false,
      "patterns": { "b": { "length": 768, "notes": [{ "time": 0, "note": "C1", "duration": 48, "velocity": 1 }] } } },
    { "kind": "automation", "name": "chorus mix", "color": "#60a0e0", "mute": false,
      "target": { "part": "effect", "chain": { "device": 1 }, "effect": 0, "path": ["wet"] },
      "patterns": { "rise": { "length": 3072, "notes": [{ "time": 0, "duration": 3072,
        "points": [{ "at": 0, "value": 0, "shape": "linear" }, { "at": 3072, "value": 1, "shape": "linear" }] }] } } }
  ],
  "buses": { "room": { "volume": 0, "pan": 0, "effects": [{ "is": "Reverb", "on": true, "options": { "decay": 3, "wet": 1 } }] } },
  "master": { "volume": -3, "effects": [{ "is": "Limiter", "on": true, "options": { "threshold": -1 } }] },
  "clips": [
    { "track": 0, "pattern": "a", "start": 0 }, { "track": 0, "pattern": "a", "start": 768 },
    { "track": 1, "pattern": "b", "start": 0 }, { "track": 2, "pattern": "rise", "start": 0 }
  ]
}

Properties

Read from world.audiodaw, once, on the plugin's first tick. The plugin makes a Tone context of its own on them before any song is made, so list audiodaw in plugins ahead of whatever loads a song.

.lookAhead : number

Seconds. Tone works out each note on the page's main thread and hands it to the audio this far ahead, so the page may be busy this long and no note is late. A song that only plays back, such as a game's music, can take a large value; a page where a key plays a note wants a small one, since a key is heard this late.

.latencyHint : number

Seconds of the browser's own audio buffer, the audio mixed ahead of the speaker. It is handed to the browser's AudioContext, which rounds it to what it can give. Too small, and a heavy song runs it dry and crackles. retime changes both while the page runs.

The project

Every time in a project is a tick at the project's ppq. Every options is the Tone.js constructor's own options object, untranslated, so Tone's documentation is the documentation of every setting.

KeyMeaning
bpm, timeSignatureThe tempo in beats a minute, and the beats to a bar.
swing, swingSubdivisionTone's swing, 0 to 1, and the note length it delays, a Tone subdivision such as "8n".
ppqTicks to a quarter note.
loopOptional { start, end } in ticks, where the song loops while cycle(true) is on.
spanOptional { start, end } in ticks. The song stops itself each time it reaches end. Playing does not read start: a song plays from the tick handed to play. With no span the end is the end of the last clip, and a song with no clips never stops itself.
rackThe devices that sound, below. A track names a device by its index here.
tracksNote tracks and automation tracks, in one list. A clip names a track by its index here.
busesReturn channels by name, { volume, pan, effects }, fed by the devices' sends.
master{ volume, effects }: the volume in dB and the effects every sound goes out through. The master has no pan.
clips{ track, pattern, start }: one of the track's patterns played once, never repeated, its tick from at the song's tick start, until its length.

Devices

A device is { name, instrument, effects, channel, sends }. Its instrument goes through its effects in order, then its channel, then out to the master.

KeyMeaning
instrument{ is, voices?, options }: voices of new Tone[is](options), 1 when left out. is is a Tone class name, such as "Synth", "MonoSynth", "FMSynth", or "DrumKit".
effects{ is, on, options } each: new Tone[is](options). Off is the effect's wet at 0, and on is options.wet, 1 when not given. An effect Tone makes without a wet, such as a Compressor, cannot be off, and a song with one off fails to load.
channel{ volume, pan, mute, solo }: the volume in dB, the pan from −1 left to 1 right, and Tone's channel mute and solo.
sendsBus name to the level sent to it, in dB.

All the voices of a device are made when the song loads and kept until it is thrown away. Each note goes to the next voice in turn, the first again after the last, whatever that voice was playing; a note let go lets go of the voice last struck with it. A device with portamento slides only with voices: 1, since the next note takes another voice. Every voice and effect runs from load to unload, sounding or not, so what a song costs is its voices times what one voice is.

A drum kit is { is: "DrumKit", options: { pads, volume? } } and has no voices. pads is keyed by the note that strikes the pad, in Tone's names ("C1"), and a pad is { name, is, pitch?, choke?, options }: the Tone drum new Tone[is](options), the pitch it plays ("C1" when left out; a NoiseSynth has none), and a choke group, so a pad struck lets go of every other pad of its group, as a closed hat cuts the open one. A note no pad has sounds nothing. volume is the kit's level in dB, 0 when left out.

Note tracks

A note track is { kind: "notes", name, color, target?, patterns, mute, solo }. It sounds nothing of its own: its notes go to the device at index target, and a track with none plays nothing. Any number of tracks may target one device, and they share its voices. color is a CSS colour. mute holds back the track's notes; when any note track is soloed, only soloed note tracks are heard.

PartMeaning
a pattern{ length, from?, notes } by name. A clip of it plays its ticks from from (0 when left out) to length, so trimming a pattern trims every clip of it. Notes outside those ticks are kept and not heard.
a note{ time, note, duration, velocity }: the tick inside the pattern, the note in Tone's names ("C4", "F#3"), its length in ticks, and its velocity, 0 to 1.

A track is read when each note falls, so a track muted, soloed or retargeted while the song plays is heard so from its next note.

Automation tracks

An automation track is { kind: "automation", name, color, target?, patterns, mute }. It sends one knob its values. It has no solo, and is heard whatever is soloed; muted, or with no target, it leaves its knob at its own value. Its patterns are { length, from?, notes } as a note track's are, of automation notes.

TargetThe knob
{ part: "instrument", device, path }An option of a device's instrument at its path in Tone's options, as ["filter", "Q"]. On a kit, ["pads", note, "options", ...] is a pad's drum, and ["volume"] the kit's level.
{ part: "effect", chain, effect, path }An option of effect number effect of a chain: { device }, { bus } or "master". The wet of an effect turned off cannot be automated.
{ part: "volume", strip }The volume of a device's channel, a bus or the master, strip being a chain.
{ part: "pan", strip }The pan of a device's channel or a bus.
{ part: "send", device, bus }A device's send to a bus.

An automation note is { time, duration, points }. Each point is { at, value, shape }: a tick from the note's start, the first at 0 and the last at duration, in order; a value from 0 to 1 of the knob's travel; and how the line reaches it from the point before, "linear", "exp" (slow and then fast, along (e3u − 1) / (e3 − 1)) or "step" (the value before held until the point's tick). The note holds the knob while it lasts. At its end the knob goes back to its own value, the song's option at that path, else Tone's default, unless another note of the track holds it there.

A knob Tone holds as a Param or a Signal glides: the whole line is scheduled on the audio clock at the note's first tick. On a knob in decibels, or one whose range has the log curve, the line is a ramp of the same ratio each second; on any other it is straight, and an exp line is drawn as 16 short ramps. Volumes, pans, sends, detune, wet, a Filter's frequency and Q, a delay's delayTime and feedback, most LFOs' frequency and depth, and compressor and EQ settings glide. Any other knob steps: it is set on the note's first tick and on every 16th note of the song the note covers, to the line's value there, with nothing between. Envelope times, a filter envelope's baseFrequency, a Phaser's or AutoFilter's baseFrequency, distortion and a Reverb's decay step. glides answers for any knob.

A play from the middle of a note, a locate and the loop's jump back take every knob up where the song lands. A stop or a pause puts every knob back, and so does muting, retargeting or removing its track.

Ranges

What 0 and 1 are in a knob's units is the page's. load takes ranges( knob, project ), which gives each knob's Range, { min, max, curve?, step? }, or undefined. A knob with no range is not automated. A value v is min + (max − min)·v, or min·(max / min)v with curve: "log", rounded to whole steps when step is given. A track retargeted to another knob plays the same shapes across that knob's travel.

Methods

On the page, through plugins.audiodaw.

load( project, ranges? )

Stops the transport, throws away the song before, makes every Tone object of this one and schedules its notes and automation. Every voice is made here, so playing makes and frees none.

edit( project )

Hands each value that changed to the Tone object that holds it, while the song plays. A device's instrument is made again when its is or voices changed, an effect when an option Tone takes only when it is made changed (a Phaser's stages, a delay's maxDelay), a chain when the number or kinds of its effects changed, and the whole rack when its length changed. Changes are found by comparing objects, so each part that changed is a new object, as the edits below make them. It needs a song loaded.

start( ) : Promise<void>

Lets Tone's context run. The browser allows it only after the page has had a key press or a click; before that nothing is heard.

play( tick? )

Starts the transport from tick, or from where it is.

stop( ) / pause( )

Stops or pauses the transport. Either, pressed or at the span's end, lets go of every note every instrument holds.

locate( tick )

Moves the transport to a tick.

cycle( on )

Turns looping over the project's loop on or off.

position( ) : number

The transport's tick.

stamp( ) : number

The tick at Tone's now, the audio clock plus the look-ahead: where a key played now lands in the song.

attack( device, note, velocity ) / release( device, note )

Plays a key live on a device's instrument now, and lets it go.

meter( on ) : Tone.Meter

A meter on a device's channel (its index), a bus (its name) or "master", made the first time it is asked for. A device's meter goes when the rack is made again.

optionsOf( device ) : object

Every option a device's instrument holds now, Tone's get(), with those an oscillator's type brings (a fat one's count and spread). A kit's comes with each pad's drum's options.

effectOptionsOf( chain, i ) : object

Every option effect i of a chain holds now. An option Tone takes only when it makes the effect, and the wet of an effect turned off, are read from the song.

glides( knob ) : boolean | undefined

True for a knob that glides, false for one that steps, undefined for a knob the loaded song does not have.

render( project, seconds, during?, ranges? ) : Promise<Tone.ToneAudioBuffer>

The song rendered offline for seconds, from tick 0. during is handed the song before it plays. A knob that steps is not heard changing in it, since Tone runs every scheduled event before it renders.

retime( { lookAhead, latencyHint } ) : Promise<void>

Other timing while the page runs. The transport stops, a new context is made on the new numbers, and the song last loaded, with its edits since, is made again on it, so the next play is heard through it. The promise is the old context's closing.

dispose( )

Throws the song away.

Properties of the plugin

Also on plugins.audiodaw.

playing : boolean

Whether the transport is running.

timing : { lookAhead, latencyHint, buffer }

What the context holds now. buffer is the seconds the browser gave for the hint, its base latency plus its output latency.

Editing a project

From #plugins/audiodaw/audiodaw.sim.ts. Nothing here touches Web Audio. Each edit returns a new project and leaves the one it was handed as it was, so undo is the project before. An edit that names a track, device, bus, pattern or index the project does not have throws.

EditWhat it does
addNote( p, t, name, note ), moveNote( p, t, name, i, to ), removeNote( p, t, name, i )A note of a note track's pattern added, changed in the fields to gives, or taken away.
addAutomationNote( p, t, name, note ), removeAutomationNote( p, t, name, i )An automation note added or taken away. A note is refused unless it is longer than 0, its points run from 0 to its duration in order, and every value is 0 to 1.
moveAutomationNote( p, t, name, i, { time?, duration? } )Moves or resizes an automation note; its points stretch with it, each at the same fraction of the note.
addPoint( p, t, name, i, point ), movePoint( p, t, name, i, j, to ), removePoint( p, t, name, i, j )A point put in between the first and the last, moved or reshaped, or taken away. The first stays at 0 and the last at the note's end, and neither can be removed; a point between stays between its neighbours.
addPattern( p, t, name, x ), addAutomationPattern( p, t, name, x )A pattern added to a track of its kind under a new name.
renamePattern( p, t, name, to ), removePattern( p, t, name )A pattern renamed or taken away, its clips with it.
setLength( p, t, name, length )Sets where a pattern ends, a whole tick at least one past its from, which lengthens or trims every clip of it. Its notes stay.
setFrom( p, t, name, from )Moves where a pattern starts, kept a tick before its end; every clip of it moves with it, so each note it still plays stays where it was in the song. No clip moves before tick 0.
addClip( p, clip ), moveClip( p, i, to ), removeClip( p, i )A clip added, changed in the fields to gives, or taken away.
uniqueClip( p, i )Gives a clip a copy of its pattern under a free name, which only it plays.
moveClipsTo( p, which, to )Moves clips onto other tracks, each carrying its pattern: a track that has the very pattern under its name plays that, else it is copied in once for all the clips moved together. A clip moved onto a track of the other kind stays where it is.
addTrack( p, track ), removeTrack( p, t ), moveTrack( p, from, to )A track added last, taken away with its clips, or moved; the clips follow their tracks.
setTarget( p, t, device? ), setKnob( p, t, knob? )The device a note track plays, or the knob an automation track sends to, or none.
setTrack( p, t, field, value )A track's name, color or mute, or a note track's solo.
addDevice( p, device ), removeDevice( p, d ), renameDevice( p, d, name ), moveDevice( p, from, to )A device added last, taken away, renamed or moved in the rack. Every track keeps playing the device, or the knob, it did; one whose device is gone targets none.
setChannel( p, d, field, value )One of a device's volume, pan, mute, solo.
setSend( p, d, bus, dB ), removeSend( p, d, bus )A device's send to a bus set or taken away.
addEffect( p, chain, effect, i? ), removeEffect( p, chain, i ), moveEffect( p, chain, from, to )An effect put in a chain at i (its end when left out), taken away, or moved. The knobs of the chain's effects go with their effects.
setEffect( p, chain, i, options ), turnEffect( p, chain, i, on )An effect's options merged with options, nested objects merged too, or the effect turned on or off.
putEffect( p, chain, i, effect )An effect put in place of one, as handed. One of another is leaves the tracks that targeted the old one's knobs with none.
addBus( p, name, bus ), removeBus( p, name )A bus added under a new name, or taken away with every send to it.
setInstrument( p, d, options )A device's instrument options merged with options, nested objects merged too.
putInstrument( p, d, instrument ), loadPreset( p, d, preset )A device's instrument put in place of the one it had, or a preset's; the channel, sends and effects stay. One of another is leaves the tracks that targeted the old one's knobs with none.

A preset is { name, instrument, effects? }. deviceFrom( name, preset ) makes a device of one: its instrument and effects, a channel at 0 dB in the middle, and no sends.

Reading a project

From the same file.

spanOf( p ) : { start, end }

The project's span, or from 0 to the end of its last clip.

sounds( p, t ) : boolean

Whether track t is heard: not muted, and, for a note track, soloed when any note track is.

clipLength( p, clip ) / clipEnd( p, clip ) : number

The ticks a clip plays, its pattern's length less its from, and the song's tick it ends on.

valueAt( note, at ) : number

An automation note's value, 0 to 1, at ticks from its start: its first point's before it, its last point's after it.

level( range, v ) : number

A value from 0 to 1 in a knob's units, through its range.

automationNote( time, duration, start, end ) : AutomationNote

A straight line from value start to end: two linear points, at 0 and at duration.

freeName( taken, base ) : string

base if no name in taken is it, else base and the least number from 2 that is free.

Running in Node

A script that builds a song and renders it in Node imports #plugins/audiodaw/nodeaudio.ts before Tone. It puts node-web-audio-api's classes where a page has Web Audio, so Tone renders offline in Node as it does in a page.

In the guide