summaryrefslogtreecommitdiff
path: root/sensfreq
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2020-10-06 19:52:20 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2020-10-06 19:52:20 +0800
commitb546bfef82875d91dac4ad5d9e4fbb9c9c87d27b (patch)
tree23ade3d14c82a7266f0da1beec4ccf2d586da3c6 /sensfreq
parent4a25a7a4b95e0380ffb9e35d4bc3842778396f9e (diff)
downloadweb-b546bfef82875d91dac4ad5d9e4fbb9c9c87d27b.tar.xz
Random stuff (sensfreq).
Diffstat (limited to 'sensfreq')
-rw-r--r--sensfreq/index.html95
-rw-r--r--sensfreq/main.js114
2 files changed, 209 insertions, 0 deletions
diff --git a/sensfreq/index.html b/sensfreq/index.html
new file mode 100644
index 0000000..7422b4a
--- /dev/null
+++ b/sensfreq/index.html
@@ -0,0 +1,95 @@
+<!doctype html>
+<html>
+<head>
+<title>SensFreq</title>
+<script src="themer.js"></script>
+<script src="main.js"></script>
+<link rel="stylesheet" type="text/css" href="common.css">
+<link rel="stylesheet" type="text/css" href="theme0a.css" id="theme0a">
+<link rel="stylesheet" type="text/css" href="theme0b.css" id="theme0b">
+<link rel="stylesheet" type="text/css" href="theme1a.css" id="theme1a">
+<link rel="stylesheet" type="text/css" href="theme1b.css" id="theme1b">
+<link rel="stylesheet" type="text/css" href="theme2a.css" id="theme2a">
+<link rel="stylesheet" type="text/css" href="theme2b.css" id="theme2b">
+<link rel="stylesheet" type="text/css" href="theme3a.css" id="theme3a">
+<link rel="stylesheet" type="text/css" href="theme3b.css" id="theme3b">
+</head>
+<body style="text-align:center;" onload="init()" class="TText">
+<h2>SensFreq</h2>
+<table style="margin:auto;" class="TText">
+<tr>
+<td>Note to look for</td>
+<td>
+<select id="note" class="TText">
+<option value="3">C</option>
+<option value="4">C#</option>
+<option value="5">D</option>
+<option value="6">D#</option>
+<option value="7">E</option>
+<option value="8">F</option>
+<option value="9">F#</option>
+<option value="10">G</option>
+<option value="11">G#</option>
+<option value="0">A</option>
+<option value="1">A#</option>
+<option value="2">B</option>
+</select>
+</td>
+</tr>
+<tr>
+<td>Number of notes</td>
+<td>
+<input type="number" value="1" min="1" max="10" id="minnnotes" class="TText"></input>
+-
+<input type="number" value="3" min="1" max="10" id="maxnnotes" class="TText"></input>
+</td>
+</tr>
+<tr>
+<td>Octave range</td>
+<td>
+<input type="number" value="-1" min="-5" max="5" id="minorange" class="TText"></input>
+-
+<input type="number" value="1" min="-5" max="5" id="maxorange" class="TText"></input>
+</td>
+</tr>
+</table>
+<input type="checkbox" id="arp" class="TText">Arpeggio</input><br>
+<button id="gench" onclick="generate_challenge()" class="TText">Play something!</button><br>
+<span id="prompt" class="TText"> </span>
+<div style="width:100%;height:3em;">
+<span id="response">
+<style>
+.yes {
+ background-color:#0c0;
+ border: 2px solid #0a0;
+ color: #fff;
+ padding: 8px;
+ margin: 4px;
+}
+.yes:hover {
+ background-color:#0e0;
+}
+.no {
+ background-color:#c00;
+ border: 2px solid #a00;
+ color: #fff;
+ padding: 8px;
+ margin: 4px;
+}
+.no:hover {
+ background-color:#e00;
+}
+</style>
+<button onclick="check_yes()" class="yes TText">Yes</button>
+<button onclick="check_no()" class="no TText">No</button>
+</span>
+</div>
+<div>
+<span id="stats" class="TText">Correct: * of *</span><br>
+<button onclick="reset_stats()" class="TText">Reset statistics</button>
+</div>
+<div style="padding-top:1em;" class="TText">
+Disclaimer: this application cannot serve as a test for perfect pitch. Please do not go around and claim you have perfect pitch if you get 99.999% challenges right!
+</div>
+</body>
+</html
diff --git a/sensfreq/main.js b/sensfreq/main.js
new file mode 100644
index 0000000..6dc431e
--- /dev/null
+++ b/sensfreq/main.js
@@ -0,0 +1,114 @@
+const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+
+e = s => document.getElementById(s);
+
+let ans = false;
+let nq = 0;
+let nc = 0;
+
+function init()
+{
+ e("gench").disabled = false;
+ e("response").style.display="none";
+ 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="";
+}
+
+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();
+}