const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); e = s => document.getElementById(s); n2n = n => ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"][n]; let ans = false; let nq = 0; let nc = 0; function init() { e("gench").disabled = false; e("response").style.display = "none"; e("prompt").innerHTML = " "; reset_stats(); loadTheme(); } function play_notes(freq, t, arpt) { const stop_notes = function(g, o) { o.stop(); g.disconnect(audioCtx.destination); o.disconnect(g); } const play_note = function(f) { const gn = audioCtx.createGain(); gn.gain.setValueAtTime(.5 / freq.length, audioCtx.currentTime); gn.gain.linearRampToValueAtTime(0, audioCtx.currentTime + t / 1000.); gn.connect(audioCtx.destination); const osc = audioCtx.createOscillator(); osc.type = "sine"; osc.frequency.setValueAtTime(f, audioCtx.currentTime); osc.connect(gn); osc.start(); setTimeout(stop_notes.bind(null, gn, osc), t); } let i=0; freq.forEach(function(f){setTimeout(play_note.bind(null, f), arpt * (i++));}); } get_freq = n => 440 * Math.pow(Math.pow(2, 1. / 12), n - 69) function generate_challenge() { const nmin=Number(e("minnnotes").value); const nmax=Number(e("maxnnotes").value); const omin=Number(e("minorange").value); const omax=Number(e("maxorange").value); if (omax < omin || nmax < nmin) { alert("?"); return; } const nnotes = Math.floor(Math.random() * (nmax - nmin + 1)) + nmin; const minrange = 60 + omin * 12; const maxrange = 71 + omax * 12; let f = []; let l = []; ans = false; if (Math.random() < 0.3) { const o = Math.floor(Math.random() * (omax - omin + 1)) + omin; let n = 60 + Number(e("note").value) + o * 12; f.push(get_freq(n)); l.push(n); ans = true; } while (f.length < nnotes) { let n = Math.floor(Math.random() * (maxrange - minrange + 1)) + minrange; if (l.indexOf(n) != -1) continue; ans |= ((n % 12) == Number(e("note").value)); f.push(get_freq(n)); } f.sort((a,b)=>a - b); play_notes(f, 1000, e("arp").checked ? 50 : 0); e("gench").disabled = true; e("response").style.display = ""; e("prompt").innerHTML = nnotes == 1 ? `Was the note you just heard ${n2n(Number(e("note").value))}?` : `Did the chord you just heard contain the note ${n2n(Number(e("note").value))}?`; } function update_stats() { e("stats").innerHTML = `You got ${nc} of the ${nq} challenges correct.`; } function reset_stats() { nq = nc = 0; update_stats(); } function check_yes() { if (ans) ++nc; ++nq; e("gench").disabled = false; e("response").style.display="none"; update_stats(); } function check_no() { if (!ans) ++nc; ++nq; e("gench").disabled = false; e("response").style.display="none"; update_stats(); }