summaryrefslogtreecommitdiff
path: root/sensfreq/main.js
blob: a50ae9516b6db9b25afaf1d98fbd1cc182af842e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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();
}