soundfont-player
Version:
Lightweight soundfont (music instrument) loader and player for WebAudio API
141 lines (125 loc) • 4.86 kB
JavaScript
var parser = require('note-parser')
/**
* Create a Soundfont object
*
* @param {AudioContext} context - the [audio context](https://developer.mozilla.org/en/docs/Web/API/AudioContext)
* @param {Function} nameToUrl - (Optional) a function that maps the sound font name to the url
* @return {Soundfont} a soundfont object
*/
function Soundfont (ctx, nameToUrl) {
console.warn('new Soundfont() is deprected')
console.log('Please use Soundfont.instrument() instead of new Soundfont().instrument()')
if (!(this instanceof Soundfont)) return new Soundfont(ctx)
this.nameToUrl = nameToUrl || Soundfont.nameToUrl
this.ctx = ctx
this.instruments = {}
this.promises = []
}
Soundfont.prototype.onready = function (callback) {
console.warn('deprecated API')
console.log('Please use Promise.all(Soundfont.instrument(), Soundfont.instrument()).then() instead of new Soundfont().onready()')
Promise.all(this.promises).then(callback)
}
Soundfont.prototype.instrument = function (name, options) {
console.warn('new Soundfont().instrument() is deprecated.')
console.log('Please use Soundfont.instrument() instead.')
var ctx = this.ctx
name = name || 'default'
if (name in this.instruments) return this.instruments[name]
var inst = {name: name, play: oscillatorPlayer(ctx, options)}
this.instruments[name] = inst
if (name !== 'default') {
var promise = Soundfont.instrument(ctx, name, options).then(function (instrument) {
inst.play = instrument.play
return inst
})
this.promises.push(promise)
inst.onready = function (cb) {
console.warn('onready is deprecated. Use Soundfont.instrument().then()')
promise.then(cb)
}
} else {
inst.onready = function (cb) {
console.warn('onready is deprecated. Use Soundfont.instrument().then()')
cb()
}
}
return inst
}
/*
* Load the buffers of a given instrument name. It returns a promise that resolves
* to a hash with midi note numbers as keys, and audio buffers as values.
*
* @param {AudioContext} ac - the audio context
* @param {String} name - the instrument name (it accepts an url if starts with "http")
* @param {Object} options - (Optional) options object
* @return {Promise} a promise that resolves to a Hash of { midiNoteNum: <AudioBuffer> }
*
* The options object accepts the following keys:
*
* - nameToUrl {Function}: a function to convert from instrument names to urls.
* By default it uses Benjamin Gleitzman's package of
* [pre-rendered sound fonts](https://github.com/gleitz/midi-js-soundfonts)
* - notes {Array}: the list of note names to be decoded (all by default)
*
* @example
* var Soundfont = require('soundfont-player')
* Soundfont.loadBuffers(ctx, 'acoustic_grand_piano').then(function(buffers) {
* buffers[60] // => An <AudioBuffer> corresponding to note C4
* })
*/
function loadBuffers (ac, name, options) {
console.warn('Soundfont.loadBuffers is deprecate.')
console.log('Use Soundfont.instrument(..) and get buffers properties from the result.')
return Soundfont.instrument(ac, name, options).then(function (inst) {
return inst.buffers
})
}
Soundfont.loadBuffers = loadBuffers
/**
* Returns a function that plays an oscillator
*
* @param {AudioContext} ac - the audio context
* @param {Hash} defaultOptions - (Optional) a hash of options:
* - vcoType: the oscillator type (default: 'sine')
* - gain: the output gain value (default: 0.4)
* - destination: the player destination (default: ac.destination)
*/
function oscillatorPlayer (ctx, defaultOptions) {
defaultOptions = defaultOptions || {}
return function (note, time, duration, options) {
console.warn('The oscillator player is deprecated.')
console.log('Starting with version 0.9.0 you will have to wait until the soundfont is loaded to play sounds.')
var midi = note > 0 && note < 129 ? +note : parser.midi(note)
var freq = midi ? parser.midiToFreq(midi, 440) : null
if (!freq) return
duration = duration || 0.2
options = options || {}
var destination = options.destination || defaultOptions.destination || ctx.destination
var vcoType = options.vcoType || defaultOptions.vcoType || 'sine'
var gain = options.gain || defaultOptions.gain || 0.4
var vco = ctx.createOscillator()
vco.type = vcoType
vco.frequency.value = freq
/* VCA */
var vca = ctx.createGain()
vca.gain.value = gain
/* Connections */
vco.connect(vca)
vca.connect(destination)
vco.start(time)
if (duration > 0) vco.stop(time + duration)
return vco
}
}
/**
* Given a note name, return the note midi number
*
* @name noteToMidi
* @function
* @param {String} noteName
* @return {Integer} the note midi number or null if not a valid note name
*/
Soundfont.noteToMidi = parser.midi
module.exports = Soundfont