Skip to content

Instantly share code, notes, and snippets.

@plugnburn
Last active August 19, 2023 14:01
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save plugnburn/75ce136bea973d8767e0 to your computer and use it in GitHub Desktop.
Save plugnburn/75ce136bea973d8767e0 to your computer and use it in GitHub Desktop.
Samples.js: a tiny and simple Web Audio API based sample playback library

Samples.js: a tiny and simple Web Audio API based sample playback library

Web Audio API spec is a great way of professional audio playing and mixing in the modern browser. However, it's too complex and versatile for some simple one-off tasks such as "load an audio sample into memory and just play it when necessary". That's why Samples.js has come true. It's a really simple and tiny (less than 1K minified) library that exposes just 5 methods to manipulate your samples from JS code in a really easy and fun way.

API reference

The library exposes the following calls:

  • samples.load(key, urlOrFile[, callback]) - load an audio sample from a URL (absolute or relative) via AJAX or from a File object (obtained from <input type=file>, for example), save it in the memory under key key and call the optional callback function if present. For the convenience, the loaded sample key is passed as a single parameter to the callback.
  • samples.unload(key) - completely delete the sample stored under key from the memory.
  • samples.start(key) - start playing the sample stored under key.
  • samples.stop(key) - stop playing the sample stored under key.
  • samples.setLooping(key, value) - turn the sample looping on/off (value can be true or false). If null is passed as the key, the looping is set/unset for all loaded samples at once.

Example

The following example will load some ding.wav file via AJAX and play it as soon as it's ready.

samples.load('bell', 'samples/ding.wav', function(key){
    samples.start('bell') //alternatively, we could just pass key parameter here
})

For the best showcase of what this library can do, see the numLaunchpad online drum/beat machine app that uses Samples.js as the only sound library.

Compatibility and performance

The library is compatible with all browsers that follow current standard Web Audio API and XHR2 specifications and also implement FileReader and ArrayBuffer interfaces. So recent Chrome, Firefox and Opera versions should be just fine. As for performance, some lags have been noticed on Firefox for Android but that's the drawback of native Web Audio API performance there.

!function(W,libName){
var ctx = new (W.AudioContext||W.webkitAudioContext), buffers = {},
loadSampleIntoBuffer = function(key, arrayBuffer, cb){
ctx.decodeAudioData(arrayBuffer, function(b){
buffers[key] = {};
buffers[key].data = b;
buffers[key].loop = false;
if(cb) cb(key)
}, function(e){console.error('Audio decoding error: ' + e.err)}
)},
loadSample = function(key, urlOrFile, cb, r){
if(urlOrFile.big) {
r = new XMLHttpRequest();
r.open('GET',urlOrFile,true);
r.responseType = 'arraybuffer';
r.onload = function(){loadSampleIntoBuffer(key, r.response, cb)};
r.send()
}
else {
r = new FileReader();
r.onload = function(){loadSampleIntoBuffer(key, r.result, cb)};
r.readAsArrayBuffer(urlOrFile);
}
},
unloadSample = function(key){delete buffers[key]},
startSample = function(key, bufSrc){
if(key in buffers) {
if('node' in buffers[key]) buffers[key].node.stop(0);
bufSrc = ctx.createBufferSource();
bufSrc.buffer = buffers[key].data;
bufSrc.connect(ctx.destination);
bufSrc.loop = buffers[key].loop;
bufSrc.start(0)
buffers[key].node = bufSrc;
}
},
stopSample = function(key){if(key in buffers && buffers[key].node) buffers[key].node.stop(0)};
W[libName] = {
setLooping:function(key, value){
value = (value === undefined) ? true : !!value;
if(key === null) for(key in buffers) buffers[key].loop = value;
else if(key in buffers) buffers[key].loop = value
},
load:loadSample,
unload:unloadSample,
start:startSample,
stop:stopSample
}
}(self,'samples')
!function(e,k){var f=new (e.AudioContext||e.webkitAudioContext),b={},h=function(a,c,g){f.decodeAudioData(c,function(c){b[a]={};b[a].data=c;b[a].loop=!1;g&&g(a)},function(a){console.error("Audio decoding error: "+a.err)})};e[k]={setLooping:function(a,c){c=void 0===c?!0:!!c;if(null===a)for(a in b)b[a].loop=c;else a in b&&(b[a].loop=c)},load:function(a,c,b,d){c.big?(d=new XMLHttpRequest,d.open("GET",c,!0),d.responseType="arraybuffer",d.onload=function(){h(a,d.response,b)},d.send()):(d=new FileReader, d.onload=function(){h(a,d.result,b)},d.readAsArrayBuffer(c))},unload:function(a){delete b[a]},start:function(a,c){a in b&&("node"in b[a]&&b[a].node.stop(0),c=f.createBufferSource(),c.buffer=b[a].data,c.connect(f.destination),c.loop=b[a].loop,c.start(0),b[a].node=c)},stop:function(a){a in b&&b[a].node&&b[a].node.stop(0)}}}(self,"samples")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment