lib/load.js
'use strict'
var base64 = require('./base64')
var isBuffer = require('is-buffer')
// Given a regex, return a function that test if against a string
function fromRegex (r) {
return function (o) { return typeof o === 'string' && r.test(o) }
}
// Try to apply a prefix to a name
function prefix (pre, name) {
return typeof pre === 'string' ? pre + name
: typeof pre === 'function' ? pre(name)
: name
}
/**
* Load one or more audio files
*
*
* Possible option keys:
*
* - __from__ {Function|String}: a function or string to convert from file names to urls.
* If is a string it will be prefixed to the name:
* `load('snare.mp3', { from: 'http://audio.net/samples/' })`
* If it's a function it receives the file name and should return the url as string.
* - __only__ {Array} - when loading objects, if provided, only the given keys
* will be included in the decoded object:
* `load('piano.json', { only: ['C2', 'D2'] })`
*
* @param {Object} source - the object to be loaded
* @param {Object} options - (Optional) the load options for that object
* @param {Object} defaultValue - (Optional) the default value to return as
* in a promise if not valid loader found
*/
function load (source, options, defVal) {
var loader =
// Basic audio loading
isArrayBuffer(source) || isBuffer(source) ? decodeBuffer
: isAudioFileName(source) ? loadAudioFile
: isPromise(source) ? loadPromise
// Compound objects
: isArray(source) ? loadArrayData
: isObject(source) ? loadObjectData
: isJsonFileName(source) ? loadJsonFile
// Base64 encoded audio
: isBase64Audio(source) ? loadBase64Audio
: isJsFileName(source) ? loadMidiJSFile
: null
var opts = options || {}
var promise = loader ? loader(source, opts)
: defVal ? Promise.resolve(defVal)
: Promise.reject('Source not valid (' + source + ')')
return promise.then(function (data) {
opts.ready(null, data)
return data
}, function (err) {
opts.ready(err)
throw err
})
}
// BASIC AUDIO LOADING
// ===================
// Load (decode) an array buffer
function isArrayBuffer (o) { return o instanceof ArrayBuffer }
function decodeBuffer (array, options) {
return options.decode(array)
}
// Load an audio filename
var isAudioFileName = fromRegex(/\.(mp3|wav|ogg|m4a)(\?.*)?$/i)
function loadAudioFile (name, options) {
var url = prefix(options.from, name)
return load(options.fetch(url, 'arraybuffer'), options)
}
// Load the result of a promise
function isPromise (o) { return o && typeof o.then === 'function' }
function loadPromise (promise, options) {
return promise.then(function (value) {
return load(value, options)
})
}
// COMPOUND OBJECTS
// ================
// Try to load all the items of an array
var isArray = Array.isArray
function loadArrayData (array, options) {
return Promise.all(array.map(function (data) {
return load(data, options, data)
}))
}
// Try to load all the values of a key/value object
function isObject (o) { return o && typeof o === 'object' }
function loadObjectData (obj, options) {
var dest = {}
var promises = Object.keys(obj).map(function (key) {
if (options.only && options.only.indexOf(key) === -1) return null
var value = obj[key]
return load(value, options, value).then(function (audio) {
dest[key] = audio
})
})
return Promise.all(promises).then(function () { return dest })
}
// Load the content of a JSON file
var isJsonFileName = fromRegex(/\.json(\?.*)?$/i)
function loadJsonFile (name, options) {
var url = prefix(options.from, name)
return load(options.fetch(url, 'text').then(JSON.parse), options)
}
// BASE64 ENCODED FORMATS
// ======================
// Load strings with Base64 encoded audio
var isBase64Audio = fromRegex(/^data:audio/)
function loadBase64Audio (source, options) {
var i = source.indexOf(',')
return load(base64.decode(source.slice(i + 1)).buffer, options)
}
// Load .js files with MidiJS soundfont prerendered audio
var isJsFileName = fromRegex(/\.js(\?.*)?$/i)
function loadMidiJSFile (name, options) {
var url = prefix(options.from, name)
return load(options.fetch(url, 'text').then(midiJsToJson), options)
}
// convert a MIDI.js javascript soundfont file to json
function midiJsToJson (data) {
var begin = data.indexOf('MIDI.Soundfont.')
if (begin < 0) throw Error('Invalid MIDI.js Soundfont format')
begin = data.indexOf('=', begin) + 2
var end = data.lastIndexOf(',')
return JSON.parse(data.slice(begin, end) + '}')
}
if (typeof module === 'object' && module.exports) module.exports = load
if (typeof window !== 'undefined') window.loadAudio = load