UNPKG

tb-web-audio

Version:

Simplified Web Audio API with cross-browser support

241 lines (214 loc) 6.08 kB
'use strict'; function WebAudio() { var _this = this; var context = createAudioContext(); var gain = context.createGain(); var analyser = context.createAnalyser(); var request = new XMLHttpRequest(); var array = new Uint8Array(1024); var source = null; var audioStartTime = 0; var contextStartTime = 0; var volume = 1; var isEnded = false; gain.connect(analyser); analyser.connect(context.destination); this.load = function (src) { var onload = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; var onended = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2]; var autoplay = arguments.length <= 3 || arguments[3] === undefined ? true : arguments[3]; var loop = arguments.length <= 4 || arguments[4] === undefined ? true : arguments[4]; if (!/\.(mp3|ogg|wav)$/i.test(src)) { var arr = src.split('.').reverse(); console.error(new Error('File format \'.' + arr[0] + '\' is not supported. Unable to decode audio data.')); return; } _this.pause(); request.abort(); request.open('GET', src, true); request.responseType = 'arraybuffer'; request.onload = function () { if (request.response.byteLength == 124) { console.error(new Error('\'' + src + '\' is not found. Unable to decode audio data.')); return; } else { context.decodeAudioData(request.response, function (buffer) { if (onended != null && !isFunction(onended)) { console.error(new TypeError('Onended must be a function.')); onended = null; } destroySource(); createSource(buffer, loop, onended, 0); if (autoplay) { _this.play(); } else { _this.pause(); } if (isFunction(onload)) { onload(); } else if (onload != null) { console.error(new Error('Onload must be a function.')); } }); } }; request.send(); }; this.play = function () { if (context.state == 'suspended') { context.resume(); } }; this.pause = function () { if (context.state == 'running') { context.suspend(); } }; this.isPlaying = function () { return context.state == 'running'; }; this.getDuration = function () { if (source == null) { console.error(new Error('Source is not loaded yet.')); return; } return source.buffer.duration; }; this.getCurrentTime = function () { if (source == null) { console.error(new Error('Source is not loaded yet.')); return; } if (isEnded) { return source.buffer.duration; } return (audioStartTime + context.currentTime - contextStartTime) % source.buffer.duration; }; this.setCurrentTime = function (time) { if (source == null) { console.error(new Error('Source is not loaded yet.')); return; } else if (typeof time != 'number') { console.error(new TypeError('Time must be a number.')); return; } if (time < 0) { time = 0; } else if (time > source.buffer.duration) { time = source.buffer.duration; } var tmpBuffer = source.buffer; var tmpLoop = source.loop; var tmpOnended = source.onended; destroySource(); createSource(tmpBuffer, tmpLoop, tmpOnended, time); }; this.mute = function () { gain.gain.value = 0; }; this.unmute = function () { gain.gain.value = volume; }; this.getVolume = function () { return gain.gain.value; }; this.setVolume = function (value) { if (typeof value != 'number') { console.error(new TypeError('Volume must be a number.')); return; } else if (value < 0 || value > 100) { console.error(new Error('Volume must be between 0 and 100.')); return; } volume = value; gain.gain.value = roundToDec(volume, 2); }; this.fadeIn = function (time) { if (typeof time != 'number') { console.error(new TypeError('Volume must be a number.')); return; } _this.play(); gain.gain.value = 0; var stepVolume = volume / time * 100; var fadeInInterval = setInterval(function () { gain.gain.value = roundToDec(gain.gain.value + stepVolume, 2); if (gain.gain.value >= volume) { gain.gain.value = volume; clearInterval(fadeInInterval); } }, 100); }; this.fadeOut = function (time) { var pause = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; if (typeof time != 'number') { console.error(new TypeError('Volume must be a number.')); return; } var stepVolume = gain.gain.value / time * 100; var fadeOutInterval = setInterval(function () { gain.gain.value = roundToDec(gain.gain.value - stepVolume, 2); if (gain.gain.value <= 0) { gain.gain.value = 0; clearInterval(fadeOutInterval); if (pause === true) { _this.pause(); } } }, 100); }; this.getFreqData = function () { analyser.getByteFrequencyData(array); return array; }; this.setFreqDataLength = function (length) { if (typeof length != 'number') { console.error(new TypeError('Length must be a number.')); return; } else if (length < 1 || length > 1024) { console.error(new Error('Length must be between 1 and 1024.')); return; } array = new Uint8Array(length); }; function createAudioContext() { var ctx = new (window.AudioContext || window.webkitAudioContext)(); if (ctx.sampleRate != 44100) { if (ctx.close) { ctx.close(); } ctx = new (window.AudioContext || window.webkitAudioContext)(); } return ctx; } function createSource(buffer, loop, onended, time) { source = context.createBufferSource(); source.connect(gain); source.buffer = buffer; source.loop = loop; source.onended = function () { isEnded = true; if (onended) { onended(); } }; source.start(0, time); isEnded = false; audioStartTime = time; contextStartTime = context.currentTime; } function destroySource() { if (source) { source.disconnect(); source.onended = null; } } function isFunction(fn) { var getType = {}; return fn && getType.toString.call(fn) == '[object Function]'; } function roundToDec(number, dec) { return Math.round(number * Math.pow(10, dec)) / Math.pow(10, dec); } } module.exports = WebAudio;