Skip to content

Instantly share code, notes, and snippets.

@johnnyshankman
Created March 6, 2022 23:47
Show Gist options
  • Save johnnyshankman/389f1b68c504190ffb8ac99f44e04034 to your computer and use it in GitHub Desktop.
Save johnnyshankman/389f1b68c504190ffb8ac99f44e04034 to your computer and use it in GitHub Desktop.
Web Audio API (Sinewave)
<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);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment