Skip to content
This repository has been archived by the owner on May 16, 2023. It is now read-only.

Commit

Permalink
Split library in parts
Browse files Browse the repository at this point in the history
  • Loading branch information
danigb committed Dec 2, 2015
1 parent f0b7c84 commit 8fb6106
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 11 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[0.6.0]
- Split functionallity in files

[0.5.0]
- Duration no longer required (thanks @benwiley4000).

Expand Down
2 changes: 2 additions & 0 deletions INSTRUMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

If you need to copy and paste... ;-)

```
accordion
acoustic_bass
acoustic_grand_piano
Expand Down Expand Up @@ -130,3 +131,4 @@ voice_oohs
whistle
woodblock
xylophone
```
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ To run pure javascript examples `npm install -g beefy` then `beefy examples/pian
## Available instruments
You can grab a [json file](https://github.com/danigb/soundfont-player/blob/master/instruments.json) with all the instruments, or require it:
You can grab a [json file](https://github.com/danigb/soundfont-player/blob/master/instruments.json) with all the instrument names, or require it:
```js
var instrumentNames = require('soundfont-player/instruments.json');
Expand Down
11 changes: 11 additions & 0 deletions examples/oscillator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

var ctx = new window.AudioContext()
var soundfont = require('..')(ctx)

var osc = soundfont.instrument()
osc.onready(function () {
var time = ctx.currentTime
'G3 B3 D4'.split(' ').forEach(function (note, index) {
osc.play(note, time + index)
})
})
9 changes: 6 additions & 3 deletions examples/piano.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ var ctx = new window.AudioContext()
var soundfont = require('..')(ctx)

var piano = soundfont.instrument('acoustic_grand_piano')
piano.onready(function () {
play('A4 C#5 E5')
piano.onready(function () { play('E5 C#5 A4') })

function play (notes) {
var time = ctx.currentTime
'A4 C#5 E5'.split(' ').forEach(function (note, index) {
notes.split(' ').forEach(function (note, index) {
piano.play(note, time + index)
})
})
}
16 changes: 16 additions & 0 deletions lib/buffers-player.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict'

var midi = require('note.midi')

module.exports = function (ctx, buffers, options) {
return function (note, time, duration) {
var buffer = buffers[midi(note)]
if (!buffer) return
var source = ctx.createBufferSource()
source.buffer = buffer
source.connect(ctx.destination)
source.start(time)
if (duration > 0) source.stop(time + duration)
return source
}
}
53 changes: 53 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use strict'

var loadBank = require('./load-bank')
var oscillatorPlayer = require('./oscillator-player')
var buffersPlayer = require('./buffers-player')

module.exports = Soundfont

/**
* Create a Soundfont object
*
* @param {AudioContext} context - the [audio context](https://developer.mozilla.org/en/docs/Web/API/AudioContext)
* @return {Soundfont} a soundfont object
*/
function Soundfont (ctx, nameToUrl) {
if (!(this instanceof Soundfont)) return new Soundfont(ctx)

this.nameToUrl = nameToUrl || Soundfont.nameToUrl || gleitzUrl
this.ctx = ctx
this.instruments = {}
this.promises = []
}

Soundfont.prototype.instrument = function (name, options) {
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
var promise = loadBank(ctx, this.nameToUrl(name))
this.promises.push(promise)
promise.then(function (buffers) {
inst.play = buffersPlayer(ctx, buffers, options)
})
inst.onready = function (cb) { promise.then(cb) }
return inst
}

Soundfont.loadBuffers = function (ctx, name) {
var nameToUrl = Soundfont.nameToUrl || gleitzUrl
return loadBank(ctx, nameToUrl(name))
}

/*
* Given an instrument name returns a URL to to the Benjamin Gleitzman's
* package of [pre-rendered sound fonts](https://github.com/gleitz/midi-js-soundfonts)
*
* @param {String} name - instrument name
* @returns {String} the Soundfont file url
*/
function gleitzUrl (name) {
return 'https://cdn.rawgit.com/gleitz/midi-js-Soundfonts/master/FluidR3_GM/' + name + '-ogg.js'
}
76 changes: 76 additions & 0 deletions lib/load-bank.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use strict'

var midi = require('note.midi')
var decodeBuffer = require('./decode-buffer')

/**
* Load a soundfont bank
*
* @param {AudioContext} ctx - the audio context object
* @param {String} url - the url of the js file
* @param {Function} get - (Optional) given a url return a promise with the contents
* @param {Function} parse - (Optinal) given a js file return JSON object
*/
module.exports = function (ctx, url, get, parse) {
get = get || getContent
parse = parse || parseJavascript
return Promise.resolve(url).then(get).then(parse)
.then(function (data) {
return { ctx: ctx, data: data, buffers: {} }
})
.then(decodeBank)
.then(function (bank) { return bank.buffers })
}

function getContent (url) {
return new Promise(function (done, reject) {
var req = new window.XMLHttpRequest()
req.open('GET', url)

req.onload = function () {
if (req.status === 200) {
done(req.response)
} else {
reject(Error(req.statusText))
}
}
req.onerror = function () {
reject(Error('Network Error'))
}
req.send()
})
}

/**
* Parse the SoundFont data and return a JSON object
* (SoundFont data are .js files wrapping json data)
*
* @param {String} data - the SoundFont js file content
* @return {JSON} the parsed data as JSON object
* @api private
*/
function parseJavascript (data) {
var begin = data.indexOf('MIDI.Soundfont.')
begin = data.indexOf('=', begin) + 2
var end = data.lastIndexOf(',')
return JSON.parse(data.slice(begin, end) + '}')
}

/*
* Decode a bank
* @param {Object} bank - the bank object
* @return {Promise} a promise that resolves to the bank with the buffers decoded
* @api private
*/
function decodeBank (bank) {
var promises = Object.keys(bank.data).map(function (note) {
return decodeBuffer(bank.ctx, bank.data[note])
.then(function (buffer) {
bank.buffers[midi(note)] = buffer
})
})

return Promise.all(promises).then(function () {
return bank
})
}
36 changes: 36 additions & 0 deletions lib/oscillator-player.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict'

var freq = require('midi.freq')(440)
var midi = require('note.midi')

/**
* Returns a function that plays an oscillator
*
*/
module.exports = function (context, options) {
return function (note, time, duration) {
var f = freq(midi(note))
if (!f) return

options = options || {}
duration = duration || 0.2

var vcoType = options.vcoType || 'sine'

var vco = context.createOscillator()
vco.type = vcoType
vco.frequency.value = f

/* VCA */
var vca = context.createGain()
vca.gain.value = options.gain || 0.5

/* Connections */
vco.connect(vca)
vca.connect(context.destination)

vco.start(time)
if (duration > 0) vco.stop(time + duration)
return vco
}
}
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "soundfont-player",
"version": "0.5.0",
"version": "0.6.0",
"description": "Lightweight soundfont (music instrument) loader and player for WebAudio API",
"main": "index.js",
"main": "lib/index.js",
"scripts": {
"test": "vows test/*.js",
"dist": "browserify index.js > dist/soundfont-player.js && uglifyjs dist/soundfont-player.js > dist/soundfont-player.min.js"
Expand All @@ -25,7 +25,8 @@
"url": "https://github.com/danigb/soundfont-player/issues"
},
"dependencies": {
"note-parser": "^0.7.0"
"midi.freq": "^1.0.0",
"note.midi": "0.0.3"
},
"devDependencies": {
"vows": "^0.8.1"
Expand Down
8 changes: 4 additions & 4 deletions test/load-buffers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ var vows = require('vows')
var assert = require('assert')
var fs = require('fs')

var Soundfont = require('../')
var loadBank = require('../lib/load-bank')

// Mocks
var audioContext = {}
audioContext.decodeAudioData = function (data, callback) { callback(data) }

Soundfont.nameToUrl = function (name) { return './test/support/' + name + '.js' }
Soundfont.loadData = function (url) {
function nameToUrl (name) { return './test/support/' + name + '.js' }
function load (url) {
var src = fs.readFileSync(url, 'utf8')
return Promise.resolve(src)
}

vows.describe('Soundfont.loadBuffers').addBatch({
'loading a buffer': {
'topic': function () {
Soundfont.loadBuffers(audioContext, 'piano').then(this.callback)
loadBank(audioContext, nameToUrl('piano'), load).then(this.callback)
},
'load buffers': function (buffers, nothing) {
assert.deepEqual(buffers, { 21: {}, 22: {}, 23: {}, 24: {}, 25: {} })
Expand Down

0 comments on commit 8fb6106

Please sign in to comment.