Music: Kavinsky ft. The Weekend - Odd Look Learning canvas? Why not learn the Audio API at the same time? So much fun, you guys!
Forked from Sean Dempsey's Pen Web Audio API (Sinewave).
A Pen by Johnny Shankman on CodePen.
<div class="container"> | |
<header> | |
<h1>Web Audio API (Sinewave)</h1> | |
<h2>Music: Kavinsky ft. The Weekend - Odd Look</h2> | |
<p id="not-supported"></p> | |
</header> | |
<canvas id="visualizer" width="956" height="100"></canvas> | |
<section class="status"> | |
<span>Audio: </span><span id="audio-status"></span> | |
</section> | |
<section class="controls" id="controls"> | |
<a id="play" class="button"><i class="fa fa-play"></i></a> | |
<a id="pause" class="button"><i class="fa fa-pause"></i></a> | |
<a id="stop" class="button"><i class="fa fa-stop"></i></a> | |
</section> | |
</div> |
(function($) { | |
var context, source, soundSource, soundUrl, | |
play, stop, pause, sound, | |
win, doc, audioStatus, | |
notSupported, canvas, | |
analyser, stream, controls; | |
function makeDistortionCurve( amount = 200 ) { | |
var k = typeof amount === 'number' ? amount : 50, | |
n_samples = 44100, | |
curve = new Float32Array(n_samples), | |
deg = Math.PI / 180, | |
i = 0, | |
x; | |
for ( ; i < n_samples; ++i ) { | |
x = i * 2 / n_samples - 1; | |
curve[i] = ( 3 + k ) * x * 20 * deg / ( Math.PI + k * Math.abs(x) ); | |
} | |
return curve; | |
}; | |
function onDocumentReady() { | |
win = $(window); | |
soundUrl = '//katiebaca.com/tutorial/odd-look.mp3'; | |
notSupported = document.getElementById('not-supported'); | |
audioStatus = document.getElementById('audio-status'); | |
controls = $(document.getElementById('controls')); | |
canvas = document.getElementById('visualizer'); | |
canvasContext = canvas.getContext('2d'); | |
play = $(document.getElementById('play')); | |
pause = $(document.getElementById('pause')); | |
stop = $(document.getElementById('stop')); | |
init(); | |
win.on('load', function() { | |
play.on('click', function() { | |
playSound(sound); | |
}); | |
stop.on("click", stopSound); | |
pause.on("click", pauseSound); | |
}); | |
} | |
function init() { | |
try { | |
window.AudioContext = window.AudioContext || window.webkitAudioContext; | |
context = new AudioContext(); | |
analyser = context.createAnalyser(); | |
analyser.minDecibels = -90; | |
analyser.maxDecibels = -10; | |
analyser.smoothingTimeConstant = 0.85; | |
loadSound(soundUrl); | |
audioStatus.innerHTML = " Loading Audio "; | |
audioStatus.className = "loading"; | |
controls.addClass("loading"); | |
} catch(e) { | |
// API not supported | |
notSupported.className = "not-supported"; | |
notSupported.innerHTML = "Web Audio API is not supported in this browser"; | |
} | |
} | |
function loadSound(url) { | |
var request = new XMLHttpRequest(); | |
request.open('GET', url, true); | |
request.responseType = 'arraybuffer'; | |
request.onload = function() { | |
// request.response is encoded... so decode it now | |
context.decodeAudioData(request.response, function(buffer) { | |
sound = buffer; | |
audioStatus.innerHTML = "Ready!"; | |
audioStatus.className = "ready"; | |
controls.removeClass("loading"); | |
setTimeout(function() { | |
audioStatus.className = ""; | |
}, 1500); | |
}, function(err) { | |
notSupported.className = "not-supported"; | |
notSupported.innerHTML = "Failed to Load Sound - Check the console"; | |
} | |
); | |
}; | |
request.send(); | |
} | |
function onWindowLoad() { | |
play.on('click', function() { | |
playSound(sound); | |
}); | |
} | |
function playSound(buffer) { | |
//is the player paused? | |
if (audioStatus.innerHTML === "Paused") { | |
//reconnect the analyser and play! | |
audioStatus.innerHTML = "Playing"; | |
source.connect(analyser); | |
return false; | |
} | |
if (audioStatus.innerHTML === "Playing") { | |
return false; | |
} | |
source = context.createBufferSource(); | |
source.buffer = buffer; | |
analyser.connect(context.destination); | |
source.connect(analyser); | |
var waveshaper = context.createWaveShaper(); | |
source.connect(waveshaper); | |
waveshaper.connect(context.destination); | |
waveShapingCurve = makeDistortionCurve(); | |
waveshaper.curve = waveShapingCurve; | |
source.start(0); | |
visualize(sound); | |
audioStatus.innerHTML = "Playing"; | |
} | |
function pauseSound() { | |
if (audioStatus.innerHTML != "Paused") { | |
audioStatus.innerHTML = "Paused"; | |
//not the best solution but it works | |
source.disconnect(analyser); | |
} | |
} | |
function stopSound() { | |
source.stop(0); | |
audioStatus.innerHTML = "Stopped"; | |
} | |
function visualize() { | |
WIDTH = canvas.width; | |
HEIGHT = canvas.height; | |
analyser.fftSize = 2048; | |
var bufferLength = analyser.frequencyBinCount; // half the FFT value | |
var dataArray = new Uint8Array(bufferLength); // create an array to store the data | |
canvasContext.clearRect(0, 0, WIDTH, HEIGHT); | |
function draw() { | |
drawVisual = requestAnimationFrame(draw); | |
analyser.getByteTimeDomainData(dataArray); // get waveform data and put it into the array created above | |
canvasContext.fillStyle = '#181818'; // draw wave with canvas | |
canvasContext.fillRect(0, 0, WIDTH, HEIGHT); | |
canvasContext.lineWidth = 2; | |
canvasContext.strokeStyle = '#3cfd2a'; | |
canvasContext.beginPath(); | |
var sliceWidth = WIDTH * 1.0 / bufferLength; | |
var x = 0; | |
for(var i = 0; i < bufferLength; i++) { | |
var v = dataArray[i] / 128.0; | |
var y = v * HEIGHT/2; | |
if(i === 0) { | |
canvasContext.moveTo(x, y); | |
} else { | |
canvasContext.lineTo(x, y); | |
} | |
x += sliceWidth; | |
} | |
canvasContext.lineTo(canvas.width, canvas.height/2); | |
canvasContext.stroke(); | |
} | |
draw(); | |
} | |
$(onDocumentReady); | |
})(jQuery); |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> |
//mixins and functions | |
$colors: ( | |
body-bg: #181818, | |
text-color: #3cfd2a, | |
container-border: lighten(black, 25%), | |
element-bg: skyblue | |
); | |
@function color($key) { | |
@if map-has-key($colors, $key) { | |
@return map-get($colors, $key); | |
} | |
@warn "Unkown '#{$key}' in $colors."; | |
@return null; | |
} | |
::selection { | |
color: color(body-bg); | |
background: color(text-color); | |
} | |
*, *:before, *:after { | |
box-sizing: border-box; | |
} | |
body { | |
background: color(body-bg); | |
color: color(text-color); | |
font-family: "Source Code Pro", sans-serif; | |
font-weight:300; | |
-webkit-font-smoothing: antialiased; | |
-moz-font-smoothing: grayscale; | |
} | |
h1, h2 { | |
font-weight:300; | |
} | |
.container { | |
max-width:64em; | |
padding: 1.5em; | |
margin: 0 auto; | |
height: 100%; | |
} | |
canvas { | |
&.playing:hover { | |
cursor: none; | |
} | |
} | |
.controls { | |
margin-top: 2.5em; | |
text-align:center; | |
&.loading { | |
opacity: 0.7; | |
cursor: pointer; | |
pointer-events: none; | |
} | |
} | |
header { | |
.not-supported { | |
background: color(text-color); | |
color: color(body-bg); | |
padding: 10px 0; | |
text-indent: 10px; | |
} | |
} | |
.button { | |
cursor: pointer; | |
position: relative; | |
top:0; | |
color: color(text-color); | |
background: color(body-bg); | |
font-size: 2em; | |
padding: 0.5em; | |
box-shadow: 0px 6px 0px darken(color(body-bg), 10%), 0px 6px 25px rgba(black, 0.7); | |
transition: all .1s ease-in-out; | |
&:active { | |
box-shadow: 0px 3px 0px darken(color(body-bg), 10%), 0px 3px 6px rgba(black, 0.7); | |
top:6px; | |
} | |
& + & { | |
margin-left:0.33em; | |
} | |
} | |
.status { | |
margin-top: 1.5em; | |
text-align:center; | |
} | |
#audio-status { | |
&:before, &:after { | |
content: ''; | |
display:inline-block; | |
width:5px; | |
} | |
} | |
span.loading, span.ready { | |
animation: loading 0.5s infinite alternate; | |
} | |
@keyframes loading { | |
from { | |
background: color(text-color); | |
color: color(body-bg); | |
} | |
to { | |
background: color(body-bg); | |
color: color(text-color); | |
} | |
} |
Music: Kavinsky ft. The Weekend - Odd Look Learning canvas? Why not learn the Audio API at the same time? So much fun, you guys!
Forked from Sean Dempsey's Pen Web Audio API (Sinewave).
A Pen by Johnny Shankman on CodePen.