npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

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 a recorded 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 automatically
  • timeOrPromise either a time derived from AudioContext.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 seconds
  • precision 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 Channel
  • precision 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 to Modulator.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 ear
  • right optional boolean, if true will only play in the right ear
  • effects 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 later
  • end 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 into from 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 object
  • object.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 1
  • object.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 pitches
  • fn 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 propertyes v for value, d for duration in seconds, and optionally linear 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 returns next 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 interval
  • hz starting hz for the scale all other notes are derived from. Defaults to 16
  • toHz 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 to Scale.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 0
  • name optional name to reverse engineer the number
  • hz 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 last
  • decay how quickly for the sound to dissipate
  • reverse if true will do fade in effect instead of fade out