0.1.0 • Published 8 years ago

polytone v0.1.0

Weekly downloads
2
License
MIT
Repository
github
Last release
8 years ago

polytone npm

Build Status js-standard-style license

Create polyphonic instruments from audio sources

polytone provides a clean API to work with web audio source nodes. It handles common tasks like converting from note names to frequency, add gain, envelope and filter support, events, scheduling or even handling audio context and node lifecycles.

The gist:

var polytone = require('polytone')

// Create a function that returns an audio source node
function synth (options) {
  var osc = options.context.createOscillator()
  osc.frequency.value = options.frequency
  return osc
}

// specify the default options and the synth function
var inst = polytone(synth, { gain: 0.5, attack: 0.01, release: 0.2 })
// pass the note name and receive the frequency and midi note number
inst.start({ note: 'C4' })
// works when passing just the note
inst.start('C4')
// override any option
inst.start({ gain: 0.8, release: 0.4, duration: 1 })
// stop all sounds after 3 seconds
inst.stop(null, 3)

## Examples

Create a audio buffer player

var snare = polytone(<AudioBuffer>)
snare.start() // play the buffer
snare.start({ gain: 0.5 }, null, 1) // => play after 1 second

Create an oscillator voice

var sine = polytone('sine')
sine.start({ note: 'C4' })
sine.start({ note: 'G4' })
sine.stop(null, 1) // => stop all after 1 second

Create a sampled instrument

You can combine multiple AudioBuffers in one polytone instrument:

var drumMachine = polytone({ snare: <AudioBuffer>, kick: <AudioBuffer>, ... })
drumMachine.start('kick') // => play the kick
drumMachine.start('snare', null, 1) // => play the snare after 1 second
drumMachine.start({ name: 'conga', gain: 0.5 }) // => play the conga with options

If the sample names are notes, some conversions are performed:

var piano = polytone({ 'C4': <AudioBuffer>, 'C#4': <AudioBuffer>, 'D4': ... })
piano.start('Db4') // => handles enharmonic notes
piano.start(70) // => can use midi numbers
piano.start(70.5) // => if midi is float, the buffer is detuned
piano.start({ note: 'C4', duration: 1 })

Create a synthetized drum machine

var dm = polytone(null, {
  snare: function(options) { /* returns an AudioNode or similar object */ },
  kick: function(options) { /* returns an AudioNode or similar object */ },
  hihat: function(options) { /* returns an AudioNode or similar object */ },
  ...
})
dm.start('snare')
dm.start({ name: 'kick', gain: 1.2 })

See my Tiny808 gist for a complete example.

Usage

Note names midi note numbers, frequency and detune

Note names or midi numbers are converted to frequencies:

var instrument = polytone(synth)
instrument.start('A4') // => Options are { note: 'A4', midi: 69, frequency: 440 }
instrument.start(60.2) // => options are { midi: 60, detune: 20, frequency: ... }
instrument.start({ note: 'C4 '}) // the same as passing the note directly
instrument.start({ name: 'C4 '}) // the same as before

Gain, Envelope and Filter

You can set the gain:

instrument.start({ note: 'C4', gain: 0.2 })

Use a simple envelope with attack and release:

instrument.start({ note: 'C4', attack: 0.2, release: 0.5, duration: 1 })
// (the real duration of the note will be 1 + 0.5 secs of the release envelope)

Or a filter:

instrument.start({ note: 'Db3', filter: { type: 'lowpass', frequency: 500 }})

Events

You can listen all events:

var instrument = polytone(synth).on('*', function (event, time, node) {
  console.log('EVENT: ', event, time, node)
})
instrument.play('C4') // => all events to the console

Or to single events (start, stop, ended, disconnect are accepted):

var instrument = polytone(synth)
instrument.on('start', function (event, time, node) {
  console.log('EVENT: ', event, time, node)
})
instrument.start(440) // => only 'start' events are displayed

Audio Context

By default, polytone uses audio-context module to get the audio context, so no initialization is required. Anyway, you can pass your own context if you prefer:

var context = new AudioContext()
var instrument = polytone({ context: context }, synth)

You can provide any option either to the polytone constructor or the start function:

function synth (options) { console.log(options) }
var instrument = polytone({ one: 1, two: 2 }, synth)
instrument.start({ two: 22, three: 333 })
// => { context: <AudioContext>, one: 1, two: 22, three: 333 }

Connect and disconnect

By default, all sources are automatically connected to the audio context destination, but you can override this option:

var reverb = /* <AudioNode> */
var instrument = polytone(synth, { connect: reverb })
instrument.start('d4')

Or for each instance individually:

var instrument = polytone(synth)
instrument.start({ note: 'C4', gain: 0.2, connect: reverb })

Are nodes are disconnected automatically after they stop.

Connect to midi inputs via Web Midi API

var piano = polytone({ 'c2': <AudioBuffer>, 'c#2': <AudioBuffer>, ... })
window.navigator.requestMIDIAccess().then(function (midiAccess) {
  midiAccess.inputs.forEach(function (midiInput) {
    piano.listenToMidi(midiInput)
  })
})

Schedule events

A simple convenient function is provide to schedule a group of events:

var piano = polytone(...)
piano.schedule([
  { time: 0, note: 'C4' },
  { time: 0.5, note: 'G4' }
], null, 3) // => shedule it after 3 second

Or more compact:

piano.schedule('C4 G4 C5'.split(' ').map(function (note, i) {
  return { time: 0.5 * i, note: note }
}))

Documentation, tests and examples

To run the examples, install budo: npm i -g budo and then:

budo examples/index

License

MIT License