UNPKG

web-audio-player

Version:
165 lines (148 loc) 4.32 kB
var canPlaySrc = require('./can-play-src') var createAudioContext = require('./audio-context') var xhrAudio = require('./xhr-audio') var EventEmitter = require('events').EventEmitter var rightNow = require('right-now') var resume = require('./resume-context') module.exports = createBufferSource function createBufferSource (src, opt) { opt = opt || {} var emitter = new EventEmitter() var audioContext = opt.context || createAudioContext() // a pass-through node so user just needs to // connect() once var bufferNode, buffer, duration var node = audioContext.createGain() var audioStartTime = null var audioPauseTime = null var audioCurrentTime = 0 var playing = false var loop = opt.loop emitter.play = function () { if (playing) return playing = true if (opt.autoResume !== false) resume(emitter.context) disposeBuffer() bufferNode = audioContext.createBufferSource() bufferNode.connect(emitter.node) bufferNode.onended = ended if (buffer) { // Might be null undefined if we are still loading bufferNode.buffer = buffer } if (loop) { bufferNode.loop = true if (typeof opt.loopStart === 'number') bufferNode.loopStart = opt.loopStart if (typeof opt.loopEnd === 'number') bufferNode.loopEnd = opt.loopEnd } if (duration && audioCurrentTime > duration) { // for when it loops... audioCurrentTime = audioCurrentTime % duration } var nextTime = audioCurrentTime bufferNode.start(0, nextTime) audioStartTime = rightNow() } emitter.pause = function () { if (!playing) return playing = false // Don't let the "end" event // get triggered on manual pause. bufferNode.onended = null bufferNode.stop(0) audioPauseTime = rightNow() audioCurrentTime += (audioPauseTime - audioStartTime) / 1000 } emitter.stop = function () { emitter.pause() ended() } emitter.dispose = function () { disposeBuffer() buffer = null } emitter.node = node emitter.context = audioContext Object.defineProperties(emitter, { duration: { enumerable: true, configurable: true, get: function () { return duration } }, playing: { enumerable: true, configurable: true, get: function () { return playing } }, buffer: { enumerable: true, configurable: true, get: function () { return buffer } }, volume: { enumerable: true, configurable: true, get: function () { return node.gain.value }, set: function (n) { node.gain.value = n } } }) // set initial volume if (typeof opt.volume === 'number') { emitter.volume = opt.volume } // filter down to a list of playable sources var sources = Array.isArray(src) ? src : [ src ] sources = sources.filter(Boolean) var playable = sources.some(canPlaySrc) if (playable) { var source = sources.filter(canPlaySrc)[0] // Support the same source types as in // MediaElement mode... if (typeof source.getAttribute === 'function') { source = source.getAttribute('src') } else if (typeof source.src === 'string') { source = source.src } // We have at least one playable source. // For now just play the first, // ideally this module could attempt each one. startLoad(source) } else { // no sources can be played... process.nextTick(function () { emitter.emit('error', canPlaySrc.createError(sources)) }) } return emitter function startLoad (src) { xhrAudio(audioContext, src, function audioDecoded (err, decoded) { if (err) return emitter.emit('error', err) buffer = decoded // store for later use if (bufferNode) { // if play() was called early bufferNode.buffer = buffer } duration = buffer.duration node.buffer = buffer emitter.emit('load') }, function audioProgress (amount, total) { emitter.emit('progress', amount, total) }, function audioDecoding () { emitter.emit('decoding') }) } function ended () { emitter.emit('end') playing = false audioCurrentTime = 0 } function disposeBuffer () { if (bufferNode) bufferNode.disconnect() } }