Skip to content

Instantly share code, notes, and snippets.

@alexmacy
Last active November 15, 2016 16:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexmacy/5e4b763bf399027de8c110086c74eba0 to your computer and use it in GitHub Desktop.
Save alexmacy/5e4b763bf399027de8c110086c74eba0 to your computer and use it in GitHub Desktop.
Sampler using Web Audio
license: mit
.DS_Store
.DS_Store

This is something I made using the Web Audio API along with d3.js. Lots of credit goes to my friend Cooper Baker because this is like a VERY slimmed-down version of an application he made back when we were in college - I believe it was cuisinart. He's got some pretty cool stuff that's definitely worth checking out!

Anyway, you trigger the different cuts using the number keys that correspond with the numbered sections of the loop. Or if you are on a mobile device, touch the section you want to hear. When loading this directly instead of in its own window, you might have to click inside the iframe so that it can detect keystrokes.

Here's an updated version with the ability load more samples and customize the cues: Web Audio Sampler v2.

<!DOCTYPE html>
<html>
<head>
<style>
button {position: absolute; left: 900px}
path {fill: none; stroke: black; stroke-width: .5;}
line {stroke: red; stroke-width: 2; visibility: hidden;}
rect {fill: steelblue; fill-opacity: 0; stroke: black; stroke-width: .5;}
.active {fill: blue;}
</style>
<script src="//d3js.org/d3.v4.min.js"></script>
</head>
<body>
<div>
<text>Press a number key to start from the corresponding section of the loop.</text>
<button onclick="stopSound()">Stop</button>
</div>
<svg></svg>
</body>
<script>
testExp = new RegExp('Android|webOS|iPhone|iPad|BlackBerry|Windows Phone|' +
'Opera Mini|IEMobile|Mobile' , 'i');
if (testExp.test(navigator.userAgent)) {
d3.select("div").select("text").html("Touch the section of the waveform that you want to hear.")
}
var width = 960,
height = 230,
waveHeight = 200,
sampleCount = 8;
var svg = d3.select("svg")
.attr("width", width)
.attr("height", height)
var waveShape = svg.append("g")
.attr("id", "waveShape")
.append("path")
.attr("transform", "translate(0,30)")
var cuts = svg.append("g").selectAll("g")
.data(d3.range(sampleCount))
.enter().append("g")
.attr("transform", function(d, i) {return "translate("+ width/sampleCount*i + ",30)"})
cuts.append("rect")
.attr("id", function(d, i) {return "rect"+i})
.attr("height",waveHeight)
.attr("width",width/sampleCount)
.attr("stroke-dasharray", [3,3])
.on("touchstart", function(d, i) {playSound(i);})
cuts.append("text")
.attr("transform", "translate(0," + -10 + ")")
.text(function(d, i) {return i+1})
var position = svg.append("line")
.attr("y2", waveHeight)
var audioCtx = new (window.AudioContext || window.webkitAudioContext)(),
source = null,
beatData = importAudio("back_on_the_streets_again.wav");
function importAudio(url) {
var req = new XMLHttpRequest();
req.open('GET', url, true);
req.responseType = 'arraybuffer';
req.onload = function() {audioCtx.decodeAudioData(req.response, loadAudio)}
req.send();
function loadAudio(buffer) {
beatData = buffer;
var waveData = d3.values(beatData.getChannelData(0))
.filter(function(d,i) {return i % 10 == 0})
var xScale = d3.scaleLinear().range([0, width]).domain([0, waveData.length]);
var line = d3.line()
.x(function(d, i) {return xScale(i)})
.y(function(d) {return waveHeight/2*d+waveHeight/2});
waveShape.datum(waveData).attr("d",line)
}
}
function playSound(i) {
if (source) {source.stop();}
source = audioCtx.createBufferSource();
source.buffer = beatData;
source.connect(audioCtx.destination);
source.start(0, beatData.duration/sampleCount*i);
positionSlide(i);
function positionSlide(n) {
cuts.selectAll("rect")
.transition().duration(100)
.style("fill-opacity", function() {return (this.id == "rect"+n) ? .2 : 0})
position.style("visibility", "visible")
.transition().duration(0)
.attr("transform", "translate(" + width/sampleCount*n + ",30)")
.transition().duration(beatData.duration/sampleCount*1000).ease(d3.easeLinear)
.attr("transform", "translate(" + width/sampleCount*++n + ",30)")
.on("end", function() {n < sampleCount ? positionSlide(n) : playSound(0)})
}
}
function stopSound() {
if (source) {source.stop();}
position.interrupt()
.style("visibility", "hidden")
.attr("transform", "translate(0,30)");
cuts.selectAll("rect")
.transition().duration(100)
.style("fill-opacity", 0)
}
document.body.onkeydown = function(e){
var thisKey = e.keyCode-48;
if (thisKey > 0 && thisKey < 9) {playSound(thisKey-1)}
};
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment