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.