doot-deet
v0.0.25
Published
make music with browser web audio api
Downloads
6
Readme
Usage
Node: const {Player, Channel, Tone, Instrument, Modulator, Scale, Pitch, effects} = require('doot-deet')
)
Webpack: import {Player, Channel, Tone, Instrument, Modulator, Scale, Pitch, effects} from 'doot-deet'
CDN: https://unpkg.com/doot-deet (const {Player, Channel, Tone, Instrument, Modulator, Scale, Pitch, effects} = dootdeet
)
import {Player} from 'doot-deet'
const player = new Player()
const play = ()=>{
player.clear() //stop
player.play(
[{pitch: 60}]
).then(console.log)
}
import {Player, Channel, Modulator, Instrument} from 'doot-deet'
const player = new Player()
const play = ()=>{
player.clear() //stop
const volume = new Modulator(
[0.05, 1], //seconds, volume to reach
0.1, //seconds to hold
[0.1, 0]
)
let last = 60
const pitches = Array.from({length: 30}, ()=> last=(Math.random()*8-4)+last)
const channel = Channel.looper({
volume,
pitch: pitches,
instrument: Instrument.organ_richer,
rEnd: [-0.6, -0.2], //% relative end of notes, 1 being 100%
rStart: [0, 0.05, 0.2]
})
player.play([channel]).then(console.log)
}
import {Player, Channel, Modulator, Instrument, effects} from 'doot-deet'
const player = new Player()
const play = ()=>{
player.clear() //stop
const volume = new Modulator([0.05, 1], 0.1, [0.1, 0])
const volume2 = volume.toDuration(1)
const distort = new effects.Distortion(500)
const reverb = new effects.Reverb(2, 5000)
let last = 60
const pitches = Array.from({length: 12}, ()=> last=(Math.random()*8-4)+last)
const channel = Channel.looper({
volume,
pitch: pitches,
instrument: Instrument.organ_richer,
rEnd: [-0.4, -0.2], //% relative end of notes, 1 being 100%
rStart: [0, 0.05, 0.2]
})
const channel2 = channel
.mapEachTone((t, i)=> i%3===0? t.chord(-3, 3): t) //splits a tone's pitch into chords
.mapEachTone(t=> t.binaural(Math.random()>0.5? -5: 5, Math.random>0.5? -5: 5)) //hz off in right/left
.mapEachTone(t=>{
if(Math.random()>0.2) return t
if(Math.random()>0.5) return {...t, effects: [reverb], volume: volume2} //reverb
return {...t, effects: [distort], volume: volume2} //distortiob
})
player.play(
Channel.align([channel, channel2]) //loops channels until they align
).then(console.log)
}
Player
is event emitter, emits play
, pause
, empty
, and recorded
events
constructor()
- creates a suspended audio context, takes no arguments
- use for hearing or recording
volume
- can be gotten or set. Is typically too loud, by default is 0.5
playing
returns true if audio context is running (not suspended or closed)
empty
is true if queued music time is passed current time (nothing left to play)
resume()
- emits
play
event if resumed (will only resume if not playing) - is a promise that will return when done regardless if successfully resumed or not
record()
- starts recording the playing sound
- if is not playing, will make it play (and emit
play
) - when the recording is done (if no more things to play or someone calls
pause()
) will emit arecorded
event with a URL object to the recording - is a promise that will return when the recording is done, supplying the recorded url
pause()
- suspends / pauses anything playing (will only pause if playing)
- stops recording if one was going (and subsequently fires
recorded
event when it is ready) - emits
pause
event - is a promise that will return when done regardless if successfully paused or not
clear()
- removes everything playing or scheduled to play
schedule(channelOrArray, [timeOrPromise])
channelOrArray
Channel object or an array that will be converted into one for you automaticallytimeOrPromise
either a time derived fromAudioContext.currentTime
or a promise. Will play the Channels at this "time". Chooses earliest time to play by default.- returns a promise that will resolve when the Channel is done playing
play(channelOrArray, [timeOrPromise])
- calls
schedule
but resumes if paused - returns
schedule
's promise
compressor
- is a DynamicsCompressorNode
- used to level out the amplitude of in phase oscillators / multiple notes that play at the same time
gainer
- is a GainNode
- starts at 0.1 volume because it is typically too loud
output
- final audio node all the sound is routed to. Use this for whatever.
- this is the node that gets recorded for recordings
Channel
extends Array
constructor(...data)
data
objects to play, can be either Channel or Tone, or objects like them (arrays become Channels, and objects use Tone.from)- even nested arrays/depths play in parallel and odd play sequentially, depth starts at 0
duration
- get the total duration of this channel (ignores promises)
- will always assume this channel will be played in sequential (instead of being parallel)
forEachTone(fn)
- allows you to loop through only tone objects in this channel, no matter the depth
fn
that is given a Tone, index, and parent channel
mapEachTone(fn)
- allows you to loop and map each tone object in this channel, no matter the depth
fn
that is given a Tone, index, parent channel, and is expected to return a new Tone- returns new Channel
repeat(n)
- returns a new Channel that is this channel but repeated
n
times. Does not change the original channel, shallow copy.
static looper({[instrument], [volume], [pitch], [start], [end]}, [length])
- makes a channel filled with tones. Can send in arrays or values (see Tone, both constructor and
from
method for all the variables accepted) length
how many tones to produce. If omitted uses longest array length sent in.- returns a Channel with tone values generated from the specified inputs above
static lcm(durations. [precision])
- finds least common multipliers and returns them (synchronizes different channels)
durations
expects an array of durations in secondsprecision
optional number. The durations must fall within this difference, in seconds. Defaults to 1.- returns an array containing the number of times each of the durations need to be repeated to synchronize within precision
static align(channels, [precision])
- calls lcm and aligns the channels to be within precision length of each other
channels
an array of Channelprecision
optional number. The durations must fall within this difference, in seconds. Defaults to 1.- returns an array of the channels that have been repeated enough times for their ending to align within precision (does not change the original)
example:
const channel = new Channel({ //makes pitch from object-like representations
pitch: {note: 1, interval: 1}
}, [ //FAC one by one
{pitch: {note: 5, interval: 4}},
{pitch: {note: 9, interval: 4}},
{pitch: 60} //or shorthand for {note: 0, interval: 5}
])
Tone
constructor({[instrument], [volume], [pitch], [left], [right], [effects] [start], [end]})
- a tone that can be played
instrument
optional. Can be the strings sine, square, sawtooth, triangle, or an Instrument instance. If omitted defaults to sine (Web Audio API default).volume
optional Modulator instance or like object, defaults toModulator.default
. v should be valued between 0 and 1, and should end at 0 v to not get clicking noises.pitch
optional Pitch instance, creates default if omitted. Can also be a Modulator or like object, where v is Pitch or pitch-like objects.left
optional boolean, if true will only play in the left earright
optional boolean, if true will only play in the right eareffects
optional array of instantiated objects from effects list (reuse is more performant), if you want to add any effects.start
the time bleed start of the tone. If negative will start the tone earlier, if positive will start it laterend
the time bleed end of the tone. If negative will start the next tone earlier, if positive will start it later
duration
- how much time this tone will be played for before another tone can start playing
extraDuration
- extra time tone will be played for to let things like effects to play out
split(...deltas)
- makes new Tones from the base Tone but with specified data shifts
deltas
objects that will be accepted intofrom
function and override current tone values- returns array of Tones
chord(...pitchDeltas)
- makes new Tones from the base Tone but with specified pitch shifts
pitchDeltas
a number added or removed from the current Pitch of the tone, or a function that accepts Pitch, index, and modulator array returning the desired pitch number (index and array may be empty if not modulator)- returns array of Tones
binaural(left, right)
- makes left and right Tones from the base Tone but with left and right pitches off by the specified hz
left
hz for the left ear to be off by, or a function that accepts Pitch, index, and modulator array returning the desired pitch hz (index and array may be empty if not modulator)right
hz for the right ear to be off by, or a function that accepts Pitch, index, and modulator array returning the desired pitch hz (index and array may be empty if not modulator)- returns array of Tones, for the left ear and right ear. The pitch used is hz based so do this step last because you can't use note/number methods on it after.
static from(object)
- makes Tone from object-like presentation
object
everything that goes into a constructor but...object.pitch
Pitch or Modulator of pitches. Accepts shorthands/like objects.object.volume
Modulator or like objectobject.rPitch
optional, in place of and like above pitch or true. If set will make duration of pitch the same as volume's duration (ignores if pitch is not a Modulator, doesn't account for promises)object.rVolume
optional, in place of and like above volume or true. If set will make duration of volume the same as pitch's duration (ignores if pitch is not a Modulator, doesn't account for promises)object.rStart
optional, overwrites start variable with value relative to tone's duration (pitch or volume, whichever is higher). Expected to be between -1 to 1object.rEnd
optional, overwrites end variable with value relative to tone's duration (pitch or volume, whichever is higher). Expected to be between -1 to 1
static mapPitch(pitch, fn)
- changes pitch value on a Tone, whether it be Pitch or Modulator of pitches
pitch
Tone pitch value to map, could be Pitch or Modulator of pitchesfn
the function that will map each Pitch object, given Pitch, index, and modulator array (index and modulator array missing if not modulator pitch value)- returns replacement Pitch or Modulator of pitches (matching on what was originally sent in)
Instrument
constructor([real], [imag])
- see PeriodicWave
real
array of numbers (can be any, will be auto normalized), should start with 0 or some global offset. Represents cosine. If ommitted (or false) uses all 0s of length imag.imag
array of numbers (can be any, will be auto normalized). Represents sine. First number is ignored. If ommitted uses all 0s of length real.
static from(data)
data
array of y or [x, y] values representing a wave. Has to be ^2 of length, for example 2, 4, 8, 16, 32, etc. If x is omitted it will use array index for x.- returns an Instrument that fits that wave (relatively, you know how waves are with their waving)
Modulator
extends Array
constructor(...data)
data
array of objects with propertyesv
for value,d
for duration in seconds, and optionallylinear
if you want linear ramping instead of exponential (see example below for all the ways to input these). Can also be a promise that returns that object; if the promise also returnsnext
that is a promise will wait for that one after and etc.
duration
- can be gotten or set. Calculates total duration omitting any promises, and will set total duration omitting any promises.
value
- can be gotten or set. Calculates maximum v
value omitting any promises, and will also set the maximum v
value and all other values scaling appropriately if set.
toDuration(duration)
- makes a copy of this modulator but with the inputted duration set
- returns Modulator
toValue(value)
- makes a copy of this modulator but with the inputted value set
- returns Modulator
example:
const modulator = new Modulator( //all the different ways to add values
0.1, //d with last v (or 0 if none)
[0.2], //d with last v
[0.3, 1], //d, v
[0.4, 0, true], //d, v, linear
{d: 0.5, v: 1},
{d: 0.6, v: 0, linear: true},
)
const tap = new Modulator(
{v: 1, d: 0.02}, //rise to 1 in 0.02 seconds
{v: 1, d: 0.07}, //stay at 1 for 0.07 seconds
{v: 0, d: 0.01} //drop to 0 in 0.01 seconds
)
tap.duration = 1 //make the tap last 1 second instead of 0.1, now with durations 0.3, 0.4, and 0.3
Scale
constructor([hz], {[length], [toHz], [toName], [fromName]})
- helps to decompose a pitch into a one number representation in this scale. The number is derived by note + interval * length
length
the number of notes you want per intervalhz
starting hz for the scale all other notes are derived from. Defaults to 16toHz
function to get hz from the starting hz, the number, and the scale length. defaults to(hz, number, length)=> hz * Math.pow(2, number/length)
toName
function to attribute a name to a note and interval, defaults to(note, interval)=> '${note}::${interval}'
fromName
function to get the number from a name, has a default for the above representation
Scale.classic
- the classical scale of C, C#, D, D#, E, F, F#, G, G#, A, A#, B. Will also recognize flats like Db.
Scale.fib
- traverses notes by golden ratio
Pitch
constructor({[scale], [note], [interval], [name], [hz]})
- representation of a hz, with either a number or a hz
scale
optional scale to be derived from with a number, defaults toScale.classic
,note
optional numeric value for the note, effectively defaults to 0. If set over the scale length will automatically calculate interval, so effectively can be used to set the "number"interval
optional numeric value for the interval or octave in classic music, effectively defaults to 0name
optional name to reverse engineer the numberhz
optional hz to use, if this is used will not use number or any derivatives (this is effectively an "override")
hz
- hz that will be played, cannot be set
name
- can be gotten or set, will translate between this Pitch's number and the scale
note
- can be gotten or set, will translate between this Pitch's number and the scale
interval
- can be gotten or set, will translate between this Pitch's number and the scale
number
- internal representation of a pitch derived from the given scale
static from(object)
object
can be another Pitch object, a number (pitch note), or a pitch-like object- returns a Pitch instance
effects
These should be made once and re-used, for performance.
new effects.Distortion(amount)
- creates a distortion effect, higher the
amount
the more distorted
new effects.Reverb([duration], [decay], [reverse])
- creates spatial reverb effect
duration
for how many seconds to lastdecay
how quickly for the sound to dissipatereverse
if true will do fade in effect instead of fade out