|
var beatbox = (function() { //MAIN |
|
|
|
// shim the requestAnimationFrame API with a setTimeout fallback |
|
//Chris |
|
window.requestAnimFrame = (function(){ |
|
return window.requestAnimationFrame || |
|
window.webkitRequestAnimationFrame || |
|
window.mozRequestAnimationFrame || |
|
window.oRequestAnimationFrame || |
|
window.msRequestAnimationFrame || |
|
function( callback ){ |
|
window.setTimeout(callback, 1000 / 60); |
|
}; |
|
})(); |
|
|
|
//timer worker by Chris Wilson; as a BLOB here b/c I couldn't figure out how to interact with a worker module in another pen |
|
var metronomeWorkerBlob = new Blob(["var timerID=null;var interval=100;self.onmessage=function(e){ if (e.data==\"start\"){timerID=setInterval(function(){postMessage(\"tick\");},interval) } else if (e.data.interval) { console.log(\"setting interval\"); interval=e.data.interval; console.log(\"interval=\"+interval); if (timerID) { clearInterval(timerID); timerID=setInterval(function(){postMessage(\"tick\");},interval) } } else if (e.data==\"stop\") { console.log(\"stopping\"); clearInterval(timerID); timerID=null; }};postMessage('messageWorker last line');"]); |
|
|
|
//original vars defined by Chris Wilson |
|
var audioContext = null; |
|
var isPlaying = false; // Are we currently playing? |
|
var startTime; // The start time of the entire sequence. |
|
var currentStep; // What step is currently the last scheduled? |
|
var tempo; // tempo (in beats per minute) |
|
var lookahead = 20.0; // How frequently to call scheduling function (in milliseconds) |
|
var scheduleAheadTime = 0.13; // How far ahead to schedule audio (sec) |
|
// calculated from lookahead and overlaps |
|
// next interval (if timer is late) |
|
var nextStepTime = 0.0; // when the next step is due. |
|
//var hiHatResolution; // 0 == 16th, 1 == 8th, 2 == quarter note |
|
var lastStepDrawn = -1; // the last "box" we drew on the screen |
|
var stepsInQueue = []; // the steps that have been put into the web audio, |
|
// and may or may not have played yet. {step, time} |
|
var timerWorker = null; // The Web Worker used to fire timer messages |
|
|
|
var unlocked = false; |
|
|
|
//new vars since I forked it |
|
var melodyPads = [], kickPads = [], snarePads = [], hhPads = []; |
|
var midiNoteFreqs = [16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.50, 25.96, 27.50, 29.14, 30.87, 32.70, 34.65, 36.71, 38.89, 41.20, 43.65, 46.25, 49.00, 51.91, 55.00, 58.27, 61.74, 65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.83, 110.00, 116.54, 123.47, 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 220.00, 233.08, 246.94, 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88, 523.25, 554.37, 587.33, 622.25, 659.25, 698.46, 739.99, 783.99, 830.61, 880.00, 932.33, 987.77, 1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760.00, 1864.66, 1975.53, 2093.00, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520.00, 3729.31, 3951.07, 4186.01, 4434.92, 4698.63, 4978.03, 5274.04, 5587.65, 5919.91, 6271.93, 6644.88, 7040.00, 7458.62, 7902.13]; |
|
var numSteps = 1; |
|
var forceRestart = false; |
|
//var toggledPad = -1; // for mouseover to toggle on/off multiple pads |
|
//stores state of clicked-on pad |
|
//under construction |
|
|
|
var patternLoader = document.getElementById("patternLoader"); |
|
var tempoSlider = document.getElementById("tempo"); |
|
var swingControl = document.getElementById("swing"); |
|
|
|
var hiHatBuffer, snareBuffer; |
|
var hhMixLevel = document.getElementById("hi-hat-mix-level"); |
|
var hhAccent = document.getElementById("hh-accent"); |
|
|
|
var melodyMixLevel = document.getElementById("melody-mix-level"); |
|
var melodyAccent = document.getElementById("melody-accent"); |
|
var snareMixLevel = document.getElementById("snare-mix-level"); |
|
var snareAccent = document.getElementById("snare-accent"); |
|
var kickMixLevel = document.getElementById("kick-mix-level"); |
|
var kickAccent = document.getElementById("kick-accent"); |
|
|
|
document.onclick = function(evt, yadda) { //general click event handler |
|
if (evt.target.className.indexOf("seq-pad")!==-1) { |
|
togglePad(evt.target, "seq-pad"); //toggle pad on and off |
|
} else if (evt.target.className.indexOf("perc-pad")!==-1) { |
|
togglePad(evt.target, "perc-pad"); //toggle pad on and off |
|
} else if (evt.target.className.indexOf("pitch-adjust")!==-1) { |
|
adjustPitch(evt.target.parentElement, (evt.target.className.indexOf("up")!==-1)); //go up or down a semitone |
|
} else if (evt.target.className.indexOf("triangle")!==-1) { |
|
adjustPitch(evt.target.parentElement.parentElement, (evt.target.parentElement.className.indexOf("up")!==-1)); //passthru for triangle shapes |
|
} else { |
|
switch (evt.target.id) { |
|
case "play": //clear output "field" |
|
//igniteIOS(); |
|
transport(); |
|
break; |
|
case "save-pattern": |
|
logPattern(); |
|
break; |
|
default: //clicked elsewhere |
|
} |
|
} |
|
}; |
|
|
|
document.onkeypress = function (e) { //general keypress event handler |
|
e = e || window.event; |
|
if (e.keyCode===32) { //toggle play/pause when press spacebar |
|
transport(); |
|
} |
|
}; |
|
|
|
patternLoader.onchange = function() { |
|
loadPattern(event.target.selectedIndex); |
|
} |
|
|
|
tempoSlider.oninput = function() { |
|
setTempo(event.target.value); |
|
} |
|
|
|
/* |
|
document.addEventListener("mouseover", function(evt) { //mouseover event handler |
|
if ((evt.target.className.indexOf("seq-pad")!==-1)&&(evt.buttons === 1)) { |
|
togglePad(evt.target); //toggle pad on and off |
|
} |
|
}); |
|
|
|
document.addEventListener("touchmove", function(evt) { //touchmove event handler; under construction |
|
if (evt.target.className.indexOf("seq-pad")!==-1) { |
|
togglePad(evt.target); //toggle pad on and off |
|
} |
|
}); |
|
*/ |
|
|
|
var patterns = [ |
|
{'title':'pattern snapshot','numSteps':32,'bpm':128,'globalSwing':'1.04','melodyNotes':['42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','42','44','42','42','45','42'],'playNotes':[false,false,false,true,false,false,true,false,false,false,false,true,false,false,true,false,false,false,false,true,false,false,true,false,false,false,false,true,false,false,true,false],'melodyAccents':[false,false,false,true,false,false,true,false,false,false,false,true,false,false,true,false,false,false,false,true,false,false,true,false,false,false,false,true,false,false,true,false],'melodyLevel':'0.25','melodyAccentLevel':'0.4','playSnare':[false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,true,false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,true],'snareAccents':[false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false],'snareLevel':'0.4','snareAccentLevel':'1','playHh':[false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false],'hhAccents':[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],'hhLevel':'0.4','hhAccentLevel':'1','playKick':[true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false],'kickAccents':[true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false],'kickLevel':'0.47','kickAccentLevel':'0.4'}, |
|
{"title":"Pass the Dutchie","numSteps":16,"bpm":80,"globalSwing":"1.07","melodyNotes":["48","64","62","45","48","45","48","63","55","62","55","62","50","62","62","65"],"playNotes":[true,false,false,true,true,true,true,false,true,false,true,false,true,false,false,false],"melodyAccents":[true,false,false,false,true,false,false,false,true,false,true,false,true,false,false,false],"melodyLevel":"0.3","melodyAccentLevel":"0.4","playSnare":[false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false],"snareAccents":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"snareLevel":"0.4","snareAccentLevel":"1","playHh":[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],"hhAccents":[true,false,true,false,true,false,true,false,true,false,true,false,true,false,true,false],"hhLevel":"0.2","hhAccentLevel":"2","playKick":[true,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false],"kickAccents":[true,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false],"kickLevel":"0.4","kickAccentLevel":"0.4"}, |
|
{"title":"Fried Green Tomatoes","numSteps":16,"bpm":120,"globalSwing":"1","melodyNotes":["57","57","69","55","52","69","52","50","52","69","52","50","52","69","55","69"],"playNotes":[true,true,false,true,true,false,true,true,true,false,true,true,true,false,true,false],"melodyAccents":[true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false],"melodyLevel":"0.4","melodyAccentLevel":"0.4","playSnare":[false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false],"snareAccents":[false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false],"snareLevel":"0.4","snareAccentLevel":"1","playHh":[false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,true],"hhAccents":[false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false],"hhLevel":"0.26","hhAccentLevel":"1.3","playKick":[true,false,false,false,true,false,false,false,true,false,false,true,true,false,true,false],"kickAccents":[true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false],"kickLevel":"0.4","kickAccentLevel":"0.4"}, |
|
|
|
{ |
|
"title" : "kick me", |
|
"numSteps" : 16, |
|
"bpm" : 120, |
|
"globalSwing" : 1, |
|
"melodyNotes" : [69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69], |
|
"playNotes" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"melodyAccents" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"melodyLevel" : 0.4, |
|
"melodyAccentLevel" : 0.4, |
|
"playSnare" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"snareAccents" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"snareLevel" : 0.4, |
|
"snareAccentLevel" : 1, |
|
"playHh" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"hhAccents" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"hhLevel" : 0.4, |
|
"hhAccentLevel" : 1, |
|
"playKick" : [true, false, false, false, true, false, false, false, true, false, false, true, true, false, true, false], |
|
"kickAccents" : [true, false, false, false, true, false, false, false, true, false, false, false, true, false, false, false], |
|
"kickLevel" : 0.4, |
|
"kickAccentLevel" : 0.4 |
|
}, |
|
{"title":"solo snare","numSteps":16,"bpm":120,"globalSwing":"1","melodyNotes":["69","69","69","69","69","69","69","69","69","69","69","69","69","69","69","69"],"playNotes":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"melodyAccents":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"melodyLevel":"0.4","melodyAccentLevel":"0.4","playSnare":[false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false],"snareAccents":[false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false],"snareLevel":"0.4","snareAccentLevel":"1","playHh":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"hhAccents":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"hhLevel":"0.26","hhAccentLevel":"1.3","playKick":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"kickAccents":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"kickLevel":"0.4","kickAccentLevel":"0.4"}, |
|
{ |
|
"title":"4 on floor", |
|
"numSteps":16, |
|
"bpm":120, |
|
"globalSwing":"1", |
|
"melodyNotes" : [69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69], |
|
"playNotes" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"melodyAccents" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"melodyLevel":"0.4", |
|
"melodyAccentLevel":"0.4", |
|
"playSnare":[false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false], |
|
"snareAccents":[false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false], |
|
"snareLevel":"0.4", |
|
"snareAccentLevel":"1", |
|
"playHh":[false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,true], |
|
"hhAccents":[false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false], |
|
"hhLevel":"0.26", |
|
"hhAccentLevel":"1.3", |
|
"playKick":[true,false,false,false,true,false,false,false,true,false,false,true,true,false,true,false], |
|
"kickAccents":[true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false], |
|
"kickLevel":"0.4", |
|
"kickAccentLevel":"0.4" |
|
}, |
|
{"title":"dreaming","numSteps":32,"bpm":128,"globalSwing":"1.04","melodyNotes":["62","55","67","67","67","55","65","55","67","55","67","65","67","65","67","69","70","55","70","69","70","55","70","69","70","55","70","72","70","69","67","65"],"playNotes":[true,false,true,true,true,false,true,false,true,false,true,true,true,true,true,true,true,false,true,true,true,false,true,true,true,false,true,true,true,true,true,true],"melodyAccents":[true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false],"melodyLevel":"0.25","melodyAccentLevel":"0.4","playSnare":[false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,true,false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,true],"snareAccents":[false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,true,false,false,false],"snareLevel":"0.4","snareAccentLevel":"1","playHh":[false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false],"hhAccents":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"hhLevel":"0.4","hhAccentLevel":"1","playKick":[true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false],"kickAccents":[true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false],"kickLevel":"0.47","kickAccentLevel":"0.4"}, |
|
{ |
|
"title" : "empty24", |
|
"numSteps" : 24, |
|
"bpm" : 120, |
|
"globalSwing" : 1, |
|
"melodyNotes" : [69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69], |
|
"playNotes" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"melodyAccents" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"melodyLevel" : 0.4, |
|
"melodyAccentLevel" : 0.4, |
|
"playSnare" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"snareAccents" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"snareLevel" : 0.4, |
|
"snareAccentLevel" : 1, |
|
"playHh" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"hhAccents" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"hhLevel" : 0.4, |
|
"hhAccentLevel" : 1, |
|
"playKick" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"kickAccents" : [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], |
|
"kickLevel" : 0.4, |
|
"kickAccentLevel" : 0.4 |
|
}, |
|
{"title":"Owl's Moving Asshole","numSteps":24,"bpm":"41","globalSwing":"1","melodyNotes":["62","55","55","60","58","57","58","55","62","55","58","62","67","55","66","67","69","65","65","55","55","50","55","58"],"playNotes":[true,false,false,true,true,true,true,false,false,true,true,true,true,false,false,true,true,true,true,false,false,true,true,true],"melodyAccents":[true,false,false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,true,false,false,true,false,false],"melodyLevel":"0.49","melodyAccentLevel":"0.53","playSnare":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"snareAccents":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"snareLevel":"0.4","snareAccentLevel":"1","playHh":[false,true,true,false,true,true,false,true,true,false,true,true,false,true,true,false,true,true,false,true,true,false,true,true],"hhAccents":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"hhLevel":"0.4","hhAccentLevel":"1","playKick":[true,false,false,false,false,true,true,false,false,false,false,false,true,false,false,false,false,true,true,false,false,false,false,false],"kickAccents":[true,false,false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false,true,false,false,false,false,false],"kickLevel":"0.04","kickAccentLevel":"0.4"}, |
|
{"title":"whatevs","numSteps":16,"bpm":120,"globalSwing":"1.12","melodyNotes":["69","67","69","69","69","64","64","64","69","69","67","67","67","69","69","69"],"playNotes":[true,true,false,false,false,false,true,false,false,false,false,true,true,false,false,false],"melodyAccents":[true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"melodyLevel":"0.15","melodyAccentLevel":"1.3","playSnare":[false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false],"snareAccents":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"snareLevel":"0.4","snareAccentLevel":"1","playHh":[false,false,false,false,false,false,true,true,false,false,false,false,false,false,true,false],"hhAccents":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false],"hhLevel":"0.4","hhAccentLevel":"1","playKick":[true,false,false,true,true,false,false,false,true,false,false,true,true,false,false,false],"kickAccents":[true,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false],"kickLevel":"0.46","kickAccentLevel":"0.4"} |
|
]; |
|
|
|
window.addEventListener("load", initBeatbox()); |
|
|
|
function adjustPitch(melodyPadGroup, up) { |
|
var midiNote = melodyPadGroup.firstElementChild.nextElementSibling.dataset.midiNote; |
|
|
|
if (up&&(midiNoteFreqs.length-midiNote>1)) { |
|
//increment pitch |
|
melodyPadGroup.firstElementChild.nextElementSibling.dataset.midiNote++; |
|
} else if (!up&&(midiNote>0)) { //must be down |
|
melodyPadGroup.firstElementChild.nextElementSibling.dataset.midiNote--; |
|
} |
|
melodyPadGroup.firstElementChild.nextElementSibling.innerText = (noteName(melodyPadGroup.firstElementChild.nextElementSibling.dataset.midiNote)); |
|
} |
|
|
|
function draw() { |
|
//created by Chris |
|
var thisStep = lastStepDrawn; |
|
while (stepsInQueue.length && stepsInQueue[0].time < audioContext.currentTime) { |
|
thisStep = stepsInQueue[0].step; |
|
stepsInQueue.splice(0,1); // remove step from queue |
|
} |
|
|
|
if (lastStepDrawn != thisStep) { // We only need to draw if the step has advanced |
|
//should maybe use html5 attrs not class names for the following? |
|
melodyPads[thisStep].className += " current"; |
|
melodyPads[thisStep].previousElementSibling.className += " current"; |
|
melodyPads[thisStep].nextElementSibling.className += " current"; |
|
kickPads[thisStep].className += " current"; |
|
snarePads[thisStep].className += " current"; |
|
hhPads[thisStep].className += " current"; |
|
if (lastStepDrawn!==-1) { //don't change prev pad if this is the first step |
|
melodyPads[lastStepDrawn].className = melodyPads[lastStepDrawn].className.replace(" current", ""); |
|
melodyPads[lastStepDrawn].previousElementSibling.className = melodyPads[lastStepDrawn].previousElementSibling.className.replace(" current", ""); |
|
melodyPads[lastStepDrawn].nextElementSibling.className = melodyPads[lastStepDrawn].nextElementSibling.className.replace(" current", ""); |
|
kickPads[lastStepDrawn].className = kickPads[lastStepDrawn].className.replace(" current", ""); |
|
snarePads[lastStepDrawn].className = snarePads[lastStepDrawn].className.replace(" current", ""); |
|
hhPads[lastStepDrawn].className = hhPads[lastStepDrawn].className.replace(" current", ""); |
|
} |
|
lastStepDrawn = thisStep; |
|
} |
|
// set up to draw again |
|
requestAnimFrame(draw); |
|
} //end draw function |
|
|
|
function getPatternList() { |
|
var opt; |
|
for (i=0;i<patterns.length;i++) { |
|
opt = document.createElement("option"); |
|
opt.innerHTML = patterns[i].title; // whatever property it has |
|
|
|
// then append it to the select element |
|
patternLoader.appendChild(opt); |
|
|
|
} |
|
} |
|
|
|
function getPatternSnapshot() { |
|
return (JSON.stringify({ |
|
"title" : "pattern snapshot", |
|
"numSteps" : numSteps, |
|
"bpm" : tempo, |
|
"globalSwing" : swingControl.value, |
|
"melodyNotes" : readMelodyPads("melody", "melodyNotes"), |
|
"playNotes" : readMelodyPads("melody", "playNotes"), |
|
"melodyAccents" : readMelodyPads("melody", "melodyAccents"), |
|
"melodyLevel" : melodyMixLevel.value, |
|
"melodyAccentLevel" : melodyAccent.value, |
|
"playSnare" : readPercPads("snare"), |
|
"snareAccents" : readPercPads("snare", true), |
|
"snareLevel" : snareMixLevel.value, |
|
"snareAccentLevel" : snareAccent.value, |
|
"playHh" : readPercPads("hihat"), |
|
"hhAccents" : readPercPads("hihat", true), |
|
"hhLevel" : hhMixLevel.value, |
|
"hhAccentLevel" : hhAccent.value, |
|
"playKick" : readPercPads("kick"), |
|
"kickAccents" : readPercPads("kick", true), |
|
"kickLevel" : kickMixLevel.value, |
|
"kickAccentLevel" : kickAccent.value |
|
})); |
|
} //getPatternSnapshot() |
|
|
|
/* |
|
function igniteIOS() { //get audio working on iOS |
|
var osc = audioContext.createOscillator(); |
|
//osc.gain.value = 1; |
|
osc.start(); |
|
osc.stop(audioContext.currentTime + 2); |
|
osc.connect( audioContext.destination ); |
|
} |
|
*/ |
|
function initBeatbox(){ |
|
|
|
//populate pattern select box |
|
getPatternList(); |
|
|
|
//create AudioContext |
|
var AudioContextCreator = window.AudioContext || window.webkitAudioContext; |
|
audioContext = new AudioContextCreator(); |
|
|
|
// load audio files, etc., here |
|
initHiHat(); |
|
initSnareNoise(); |
|
loadPattern(); |
|
|
|
requestAnimFrame(draw); // start the drawing loop. |
|
timerWorker = new Worker(window.URL.createObjectURL(metronomeWorkerBlob)); |
|
timerWorker.onmessage = function(e) { |
|
if (e.data == "tick") { |
|
// console.log("tick!"); |
|
scheduler(); |
|
} |
|
else |
|
console.log("message: " + e.data); |
|
}; |
|
timerWorker.postMessage({"interval":lookahead}); |
|
} |
|
|
|
function initHiHat() { |
|
var channels = 2; |
|
// Create an empty 1/80 second stereo buffer at the sample rate of the AudioContext |
|
var frameCount = audioContext.sampleRate / 80; |
|
|
|
hiHatBuffer = audioContext.createBuffer(channels, frameCount, audioContext.sampleRate); |
|
|
|
// Fill the buffer with white noise; just random values between -1.0 and 1.0 |
|
for (var channel = 0; channel < channels; channel++) { |
|
// This gives us the actual array that contains the data |
|
var nowBuffering = hiHatBuffer.getChannelData(channel); |
|
for (var i = 0; i < frameCount; i++) { |
|
// Math.random() is in [0; 1.0]; audio needs to be in [-1.0; 1.0] |
|
nowBuffering[i] = Math.random() * 2 - 1; |
|
} |
|
} |
|
} |
|
|
|
function initSnareNoise() { |
|
var channels = 2; |
|
var frameCount = audioContext.sampleRate / 3; |
|
snareBuffer = audioContext.createBuffer(channels, frameCount, audioContext.sampleRate); |
|
// Fill the buffer with white noise; just random values between -1.0 and 1.0 |
|
for (var channel = 0; channel < channels; channel++) { |
|
// This gives us the actual array that contains the data |
|
var nowBuffering = snareBuffer.getChannelData(channel); |
|
for (var i = 0; i < frameCount; i++) { |
|
nowBuffering[i] = Math.random() * 2 - 1; |
|
} |
|
} |
|
} |
|
|
|
function loadPattern(patternIndex) { |
|
if (patternIndex===undefined) { //set default pattern to load |
|
patternIndex = 0; |
|
} |
|
setPattern(patterns[patternIndex]); |
|
patternLoader.selectedIndex = patternIndex; |
|
} |
|
|
|
function logPattern() { |
|
var logButton = document.getElementById("save-pattern"), oldLogButtText = logButton.innerText; |
|
console.log(getPatternSnapshot()); |
|
logButton.innerText = "check console"; |
|
setTimeout(function(){ |
|
logButton.innerText = oldLogButtText; |
|
}, 2000); |
|
} |
|
|
|
|
|
function nextStep() { |
|
// Advance current step and time: Chris |
|
var swingAmount = swing.value; |
|
var secondsPerBeat = 60.0 / tempo; // picks up CURRENT tempo value to calculate beat length |
|
//nextStepTime += 0.25 * secondsPerBeat; // Add beat length to last beat time (0.25 because convert from 1/4 to 1/16) |
|
nextStepTime += 0.25 * secondsPerBeat * (currentStep%2?2-swingAmount:swingAmount); // Add beat length to last beat time (0.25 because convert from 1/4 to 1/16) |
|
currentStep++; // Advance the beat number, wrap to zero |
|
if (currentStep == numSteps) { |
|
currentStep = 0; |
|
} |
|
} |
|
|
|
function noteName(midiNoteNumber) { |
|
var noteNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; |
|
return noteNames[(midiNoteNumber % 12)].concat(parseInt(midiNoteNumber / 12)); |
|
} |
|
|
|
function playBass(beatNumber, time) { |
|
var noteLength = 60/(tempo*4), trimEnds = 0.001; //(in seconds) |
|
var envelope = audioContext.createGain(); |
|
var bassLevel = audioContext.createGain(); |
|
var saws = [], panners = []; |
|
|
|
for (var i = 0;i<4;i++) { |
|
saws.push(); //looks odd but pushing a new OscillatorNode() here confused Android |
|
saws[i] = audioContext.createOscillator(); |
|
saws[i].type = "sawtooth"; |
|
saws[i].detune = 10-(i*3); |
|
saws[i].frequency.value = (midiNoteFreqs[melodyPads[beatNumber].dataset.midiNote])/4; |
|
saws[i].start( time ); |
|
saws[i].stop( time + noteLength ); |
|
|
|
/* |
|
panners.push(); |
|
panners[i] = audioContext.createStereoPanner(); //try 3D panner to avoid crashing iOS |
|
panners[i].pan.value = (1.5 - i)/2; |
|
|
|
saws[i].connect(panners[i]); |
|
panners[i].connect(envelope); |
|
*/ |
|
saws[i].connect(envelope); |
|
|
|
} |
|
|
|
envelope.gain.linearRampToValueAtTime(1, time+trimEnds); |
|
envelope.gain.linearRampToValueAtTime(0, time+noteLength-trimEnds); |
|
envelope.connect( bassLevel ); |
|
|
|
bassLevel.gain.value = (1+((melodyPads[beatNumber].className.indexOf("accent")===-1)?0:Number(melodyAccent.value))) * Number(melodyMixLevel.value); |
|
bassLevel.connect( audioContext.destination ); |
|
} //playBass() |
|
|
|
function playHiHat(beatNumber, time) { |
|
var hihat = audioContext.createBufferSource(), hhGain = audioContext.createGain(); |
|
hihat.buffer = hiHatBuffer; |
|
hihat.connect(hhGain); |
|
hhGain.gain.value = ((beatNumber%4)?1:(1+Number(hhAccent.value)))*Number(hhMixLevel.value); |
|
hhGain.connect(audioContext.destination); |
|
hihat.start( time ); |
|
hhGain.gain.value = (1+((hhPads[beatNumber].className.indexOf("accent")===-1)?0:Number(hhAccent.value))) * Number(hhMixLevel.value); |
|
} //playHiHat() |
|
|
|
function playKick(beatNumber, time) { |
|
var noteLength = 0.05, trimEnds = 0.001; //(in seconds) |
|
var osc = audioContext.createOscillator(), envelope = audioContext.createGain(), kickLevel = audioContext.createGain(), squealFilter = audioContext.createBiquadFilter(); |
|
var startFreq = 820, endFreq = 55; //Hz |
|
|
|
osc.type = 'sine'; |
|
osc.frequency.value = startFreq; |
|
osc.frequency.exponentialRampToValueAtTime(endFreq, time + (noteLength/2)); |
|
osc.start(time); |
|
osc.stop(time + noteLength); |
|
osc.connect(envelope); |
|
|
|
envelope.gain.value = 0; |
|
envelope.gain.linearRampToValueAtTime(1, time+trimEnds); |
|
envelope.gain.linearRampToValueAtTime(0, time+noteLength-trimEnds); |
|
envelope.connect(squealFilter); |
|
|
|
squealFilter.type = "lowpass"; |
|
squealFilter.frequency.value = 1000; |
|
squealFilter.Q.value = 15; |
|
squealFilter.connect(kickLevel); |
|
|
|
kickLevel.gain.value = (1+((kickPads[beatNumber].className.indexOf("accent")===-1)?0:Number(kickAccent.value))) * Number(kickMixLevel.value); |
|
kickLevel.connect(audioContext.destination); |
|
|
|
} //playKick() |
|
|
|
function playOrgan(beatNumber, time) { |
|
var noteLength = 60/(tempo*4), trimEnds = 0.001; //(in seconds) |
|
var envelope = audioContext.createGain(); |
|
var bassLevel = audioContext.createGain(); |
|
var overtone = function (interval) { |
|
var a = Math.pow(2, 1/12); // as per http://www.phy.mtu.edu/~suits/NoteFreqCalcs.html |
|
return Math.pow(a, interval); |
|
} |
|
var oscs = [], playFreqs = []; |
|
//var tonewheels = [-12, 0, 7, 12, 19, 24, 28, 31, 36]; //the original hammond tonewheels |
|
var tonewheels = [-12, 0, 7, 12, 19, 24, 31, 36]; //nuked the major third so can play less discordant chords |
|
var notes = [0, 7]; //chord |
|
|
|
for (var j = 0; j < notes.length; j++) { |
|
for (var k = 0; k < tonewheels.length; k++) { |
|
playFreqs.push([overtone((tonewheels[k]+notes[j])), 1]); |
|
} |
|
} |
|
|
|
for (var i = 0;i<playFreqs.length;i++) { |
|
oscs.push(); //looks odd but pushing a new OscillatorNode() here confused Android |
|
oscs[i] = audioContext.createOscillator(); |
|
oscs[i].type = "sine"; |
|
//oscs[i].detune = 10-(i*3); |
|
oscs[i].frequency.value = (midiNoteFreqs[melodyPads[beatNumber].dataset.midiNote])*playFreqs[i][0]; |
|
oscs[i].start( time ); |
|
oscs[i].stop( time + noteLength ); |
|
oscs[i].connect(envelope); |
|
|
|
} |
|
|
|
envelope.gain.linearRampToValueAtTime(0.4, time+trimEnds); |
|
envelope.gain.linearRampToValueAtTime(0, time+noteLength-trimEnds); |
|
envelope.connect( bassLevel ); |
|
|
|
bassLevel.gain.value = (1+((melodyPads[beatNumber].className.indexOf("accent")===-1)?0:Number(melodyAccent.value))) * Number(melodyMixLevel.value); |
|
bassLevel.connect( audioContext.destination ); |
|
} //playOrgan() |
|
|
|
|
|
function oldPlayOrgan(beatNumber, time) { //uncalled at the moment |
|
var noteLength = 0.05; // length of "beep" (in seconds) |
|
var osc, gainNode; |
|
//if pad is on, play it |
|
// create an oscillator |
|
osc = audioContext.createOscillator(); |
|
gainNode = audioContext.createGain(); |
|
//osc.oscillatorType = 'sawtooth'; |
|
osc.connect( gainNode ); |
|
gainNode.connect( audioContext.destination ); |
|
osc.frequency.value = midiNoteFreqs[melodyPads[beatNumber].dataset.midiNote]; |
|
osc.start( time ); |
|
osc.stop( time + noteLength ); |
|
gainNode.gain.value = (1+((melodyPads[beatNumber].className.indexOf("accent")===-1)?0:Number(melodyAccent.value))) * Number(melodyMixLevel.value); |
|
|
|
//the 2nd oscillator makes an organ-like sound |
|
var octaver = audioContext.createOscillator(), octaveGainNode = audioContext.createGain(); |
|
octaver.connect( octaveGainNode ); |
|
octaveGainNode.connect( audioContext.destination ); |
|
octaver.frequency.value = midiNoteFreqs[melodyPads[beatNumber].dataset.midiNote-12]; //why does this crash if I add sth to midiNote rather than subtract? |
|
octaver.start( time ); |
|
octaver.stop( time + noteLength ); |
|
octaveGainNode.gain.value = gainNode.gain.value * 0.5; |
|
} //playOrgan() |
|
|
|
function playSnare(beatNumber, time) { |
|
|
|
//for main waveform |
|
var snareHead = audioContext.createOscillator(), headEnv = audioContext.createGain(); |
|
var headNoteLength = 0.1, headGainAttack = 0.005, headGainSustain = 0.03, headGainDecay = 0.08; //in seconds |
|
var startFreq = 3000, quickDropFreq = 400, finalFreq = 140; //Hz |
|
var quickDropTime = 0.005, finalFreqTime = 0.02; |
|
|
|
//for noise |
|
var snareNoise = audioContext.createBufferSource(), noiseEnv = audioContext.createGain(), noiseHpf = audioContext.createBiquadFilter(); |
|
var noiseGainAttack = 0.01, noiseGainPeakAttack = 0.02, noiseGainQuickDecay = 0.06, noiseGainFinalDecay = 0.1; |
|
var noiseHpfFreq = 3600; |
|
|
|
//for setting mix output level |
|
var snareLevel = audioContext.createGain(); |
|
|
|
//make and put gain envelope on triangle oscillator for main sound |
|
snareHead.type = 'triangle'; |
|
snareLevel.connect(audioContext.destination); |
|
snareHead.frequency.value = startFreq; |
|
snareHead.frequency.exponentialRampToValueAtTime(quickDropFreq, time + quickDropTime); |
|
snareHead.frequency.exponentialRampToValueAtTime(finalFreq, time + quickDropTime + finalFreqTime); |
|
snareHead.start(time); |
|
snareHead.stop(time + headNoteLength); |
|
snareHead.connect(headEnv); |
|
headEnv.gain.linearRampToValueAtTime(1.0, time + headGainAttack); |
|
headEnv.gain.linearRampToValueAtTime(1.0, time + headGainAttack + headGainSustain); |
|
headEnv.gain.linearRampToValueAtTime(0, time + headGainAttack + headGainSustain + headGainDecay); |
|
headEnv.connect(snareLevel); |
|
|
|
//add and apply envelope and hpf to noise |
|
snareNoise.buffer = snareBuffer; |
|
snareNoise.connect(noiseEnv); |
|
noiseEnv.gain.value = 0; |
|
noiseEnv.gain.linearRampToValueAtTime(0.5, time + noiseGainAttack); |
|
noiseEnv.gain.linearRampToValueAtTime(0.7, time + noiseGainAttack + noiseGainPeakAttack); |
|
noiseEnv.gain.exponentialRampToValueAtTime(0.2, time + noiseGainAttack + noiseGainPeakAttack + noiseGainQuickDecay); |
|
noiseEnv.gain.linearRampToValueAtTime(0, time + noiseGainAttack + noiseGainPeakAttack + noiseGainQuickDecay + noiseGainFinalDecay); |
|
snareNoise.start( time ); |
|
noiseEnv.connect(noiseHpf); |
|
noiseHpf.type = "highpass"; |
|
noiseHpf.frequency.value = noiseHpfFreq; |
|
noiseHpf.connect(snareLevel); |
|
|
|
//apply mixer and accent levels |
|
snareLevel.gain.value = (1+((snarePads[beatNumber].className.indexOf("accent")===-1)?0:Number(snareAccent.value))) * Number(snareMixLevel.value); |
|
|
|
} //playSnare() |
|
|
|
function readMelodyPads(instrument, readParam) { //for outputting user-changed pattern to console |
|
var ret = []; |
|
var padArray = document.getElementById(instrument).children; |
|
for (var i=1; i< padArray.length; i++) { //ignore padArray[0] which is h2 element |
|
switch (readParam) { |
|
case "melodyNotes": |
|
ret.push(padArray[i].firstElementChild.nextElementSibling.dataset.midiNote); |
|
break; |
|
case "playNotes": |
|
ret.push(padArray[i].firstElementChild.nextElementSibling.className.indexOf("off")===-1); |
|
break; |
|
case "melodyAccents": |
|
ret.push(padArray[i].firstElementChild.nextElementSibling.className.indexOf("accent")!==-1); |
|
break; |
|
} |
|
} |
|
return (ret); |
|
} |
|
|
|
|
|
function readPercPads(instrument, accent) { //for outputting user-changed pattern to console |
|
var ret = []; |
|
var padArray = document.getElementById(instrument).children; |
|
for (var i=1; i< padArray.length; i++) { //ignore padArray[0] which is h2 element |
|
if (accent) { |
|
ret.push(padArray[i].className.indexOf("accent")!==-1); |
|
} else { |
|
ret.push(padArray[i].className.indexOf("off")===-1); |
|
} |
|
} |
|
return (ret); |
|
} |
|
|
|
function scheduleHiHat(beatNumber, time) { |
|
//if pad is on, play it |
|
if (hhPads[beatNumber].className.indexOf("off")===-1) { |
|
playHiHat(beatNumber, time); |
|
} |
|
} |
|
|
|
function scheduleKick(beatNumber, time) { |
|
//if pad is on, play it |
|
if (kickPads[beatNumber].className.indexOf("off")===-1) { |
|
playKick(beatNumber, time); |
|
} |
|
} |
|
|
|
function scheduleMelody(beatNumber, time) { |
|
//if pad is on, play it |
|
if (melodyPads[beatNumber].className.indexOf("off")===-1) { |
|
playOrgan(beatNumber, time); |
|
} |
|
} |
|
|
|
function scheduleSnare(beatNumber, time) { |
|
//if pad is on, play it |
|
if (snarePads[beatNumber].className.indexOf("off")===-1) { |
|
playSnare(beatNumber, time); |
|
} |
|
} |
|
|
|
function scheduler() { |
|
// while there are steps that will need to play before the next interval, |
|
// schedule them and advance the pointer. |
|
// Chris W |
|
while (nextStepTime < audioContext.currentTime + scheduleAheadTime ) { |
|
scheduleStep( currentStep, nextStepTime ); |
|
nextStep(); |
|
} |
|
} |
|
|
|
function scheduleStep( beatNumber, time ) { |
|
// push the step on the queue, even if we're not playing. |
|
// Chris |
|
stepsInQueue.push( { step: beatNumber, time: time } ); |
|
scheduleMelody(beatNumber, time); |
|
scheduleKick(beatNumber, time); |
|
scheduleSnare(beatNumber, time); |
|
scheduleHiHat(beatNumber, time); |
|
} |
|
|
|
function setPattern(pattern) { |
|
setStepCount(pattern.numSteps); |
|
setTempo(pattern.bpm); |
|
swing.value = pattern.globalSwing; |
|
setMelody(pattern.melodyNotes, pattern.playNotes, pattern.melodyAccents); |
|
melodyMixLevel.value = pattern.melodyLevel; |
|
melodyAccent.value = pattern.melodyAccentLevel; |
|
setPercPattern(kickPads, pattern.playKick, pattern.kickAccents); |
|
kickMixLevel.value = pattern.kickLevel; |
|
kickAccent.value = pattern.kickAccentLevel; |
|
setPercPattern(snarePads, pattern.playSnare, pattern.snareAccents); |
|
snareMixLevel.value = pattern.snareLevel; |
|
snareAccent.value = pattern.snareAccentLevel; |
|
setPercPattern(hhPads, pattern.playHh, pattern.hhAccents); |
|
//setHiHat(pattern.hiHatBackbeat, pattern.hiHatResolution); |
|
hhMixLevel.value = pattern.hhLevel; |
|
hhAccent.value = pattern.hhAccentLevel; |
|
} |
|
|
|
function setPercPattern(percPads, playPad, accents) { |
|
for (var i = 0; i < percPads.length; i++) { |
|
percPads[i].className = (playPad[i]===true?(accents[i]===true?"perc-pad accent":"perc-pad on"):"perc-pad off"); |
|
} |
|
} |
|
|
|
|
|
function setMelody(notes, playNote, accents) { |
|
for (var i = 0; i < melodyPads.length; i++) { |
|
melodyPads[i].innerHTML = noteName(notes[i]); |
|
melodyPads[i].setAttribute('data-midi-note', notes[i]); |
|
//melodyPads[i].className = (playNote[i]===true?"seq-pad on":"seq-pad off"); |
|
melodyPads[i].className = (playNote[i]===true?(accents[i]===true?"seq-pad accent":"seq-pad on"):"seq-pad off"); |
|
melodyPads[i].previousElementSibling.className = (playNote[i]===true?(accents[i]===true?"pitch-adjust up accent":"pitch-adjust up on"):"pitch-adjust up off"); |
|
melodyPads[i].nextElementSibling.className = (playNote[i]===true?(accents[i]===true?"pitch-adjust down accent":"pitch-adjust down on"):"pitch-adjust down off"); |
|
|
|
} |
|
} |
|
|
|
function setStepCount(newStepCount) { |
|
if (newStepCount === numSteps) { //exit if no need to change number of steps |
|
return; |
|
} else { |
|
var j, melody = document.getElementById("melody"), kick = document.getElementById("kick"), snare = document.getElementById("snare"), hihat = document.getElementById("hihat"); |
|
if (newStepCount > numSteps) { //add steps |
|
var padTemplate, kickTemplate, snareTemplate, hhTemplate; |
|
for (j = 0; j < newStepCount-numSteps; j++) { |
|
padTemplate = melody.firstElementChild.nextElementSibling.cloneNode(true); |
|
melody.appendChild(padTemplate); |
|
kickTemplate = kick.firstElementChild.nextElementSibling.cloneNode(true); |
|
kick.appendChild(kickTemplate); |
|
snareTemplate = snare.firstElementChild.nextElementSibling.cloneNode(true); |
|
snare.appendChild(snareTemplate); |
|
hhTemplate = hihat.firstElementChild.nextElementSibling.cloneNode(true); |
|
hihat.appendChild(hhTemplate); |
|
} |
|
} else if (newStepCount < numSteps) { //delete steps |
|
if (currentStep > newStepCount) {//prevent attempt to play step that doesn't exist anymore when shorter pattern chosen |
|
//attempting bug fix here |
|
//beatNumber = 0; |
|
currentStep = 0; |
|
lastStepDrawn = -1; |
|
stepsInQueue = []; |
|
//forceRestart = true; |
|
//draw(0); |
|
} |
|
for (j = 0; j < numSteps-newStepCount; j++) { |
|
melody.lastElementChild.remove(); |
|
kick.lastElementChild.remove(); |
|
snare.lastElementChild.remove(); |
|
hihat.lastElementChild.remove(); |
|
} |
|
} |
|
melodyPads = melody.getElementsByClassName("seq-pad"); //bind for later use in turning pads on/off |
|
kickPads = kick.getElementsByClassName("perc-pad"); //bind for later use in turning pads on/off |
|
snarePads = snare.getElementsByClassName("perc-pad"); //bind for later use in turning pads on/off |
|
hhPads = hihat.getElementsByClassName("perc-pad"); //bind for later use in turning pads on/off |
|
numSteps = newStepCount; |
|
} |
|
} |
|
|
|
function setTempo(newTempo) { |
|
tempo = newTempo; |
|
tempoSlider.value = newTempo; |
|
document.getElementById('showTempo').innerText=tempo; |
|
} |
|
|
|
/* |
|
function toggle_more_pads(pad){ |
|
pad.className = ((pad.className==="seq-pad off")?"seq-pad on":"seq-pad off"); |
|
//console.log (pad.dataset.midiNote); |
|
} |
|
*/ |
|
|
|
function togglePad(pad, padType){ |
|
//cycles through 3 (off, on, and accent) states |
|
switch (pad.className) { |
|
case padType + " accent current": //deliberate fall-through |
|
case padType + " accent": //advance to next pad status |
|
pad.className = padType + " on"; |
|
if (padType === "seq-pad") { |
|
pad.previousElementSibling.className = "pitch-adjust up on"; |
|
pad.nextElementSibling.className = "pitch-adjust down on"; |
|
} |
|
break; |
|
case padType + " off current": //deliberate fall-through |
|
case padType + " off": //advance to next pad status |
|
pad.className = padType + " accent"; |
|
if (padType === "seq-pad") { |
|
pad.previousElementSibling.className = "pitch-adjust up accent"; |
|
pad.nextElementSibling.className = "pitch-adjust down accent"; |
|
} |
|
break; |
|
case padType + " on current": //deliberate fall-through |
|
case padType + " on": //advance to next pad status |
|
pad.className = padType + " off"; |
|
if (padType === "seq-pad") { |
|
pad.previousElementSibling.className = "pitch-adjust up off"; |
|
pad.nextElementSibling.className = "pitch-adjust down off"; |
|
} |
|
break; |
|
default: |
|
console.log("unexpected pad class name: "+pad.className); |
|
} |
|
} //end togglePad() |
|
|
|
function oldTogglePad(pad){ |
|
//cycles through 3 (off, on, and accent) states |
|
switch (pad.className) { |
|
case "seq-pad accent current": //deliberate fall-through |
|
case "seq-pad accent": //advance to next pad status |
|
pad.className = "seq-pad on"; |
|
//pad.previousElementSibling.className = "pitch-adjust on"; |
|
//pad.nextElementSibling.className = "pitch-adjust on"; |
|
break; |
|
case "seq-pad off current": //deliberate fall-through |
|
case "seq-pad off": //advance to next pad status |
|
pad.className = "seq-pad accent"; |
|
//pad.previousElementSibling.className = "pitch-adjust accent"; |
|
//pad.nextElementSibling.className = "pitch-adjust accent"; |
|
break; |
|
case "seq-pad on current": //deliberate fall-through |
|
case "seq-pad on": //advance to next pad status |
|
pad.className = "seq-pad off"; |
|
//pad.previousElementSibling.className = "pitch-adjust off"; |
|
//pad.nextElementSibling.className = "pitch-adjust off"; |
|
break; |
|
default: |
|
console.log("unexpected seq-pad class name: "+pad.className); |
|
} |
|
} //end togglePad() |
|
|
|
|
|
function transport() { //toggle play/stop |
|
if (!unlocked) { |
|
// play silent buffer to unlock the audio |
|
var buffer = audioContext.createBuffer(1, 1, 22050); |
|
var node = audioContext.createBufferSource(); |
|
node.buffer = buffer; |
|
node.start(0); |
|
unlocked = true; |
|
} |
|
var transportText = document.getElementById("play"); |
|
if (!isPlaying) { // start playing |
|
currentStep = 0; |
|
nextStepTime = audioContext.currentTime; |
|
timerWorker.postMessage("start"); |
|
transportText.innerText = "stop"; |
|
} else { |
|
timerWorker.postMessage("stop"); |
|
transportText.innerText = "restart"; |
|
} |
|
isPlaying = !isPlaying; |
|
|
|
} |
|
|
|
|
|
}()); |