forked from KilledByAPixel/1Keys
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
127 lines (109 loc) · 5.11 KB
/
index.html
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
119
120
121
122
123
124
125
126
127
<title>1Keys 🎹 Tiny JS Piano</title>
<meta charset=utf-8>
<body id=B style=background:#112;color:#fff;user-select:none;text-align:center>
<span id=K style=font-size:20></span>
Octave <input type=number id=O style=width:40 value=0 min=-3 max=3 onkeydown='return false'>
Key <input type=number id=Y style=width:40 value=0 min=-12 max=12 onkeydown='return false'>
Shape <select id=S>
<option>sine</option>
<option>square</option>
<option>sawtooth</option>
<option>triangle</option>
</select>
<br><script>
///////////////////////////////////////////////////////////////////////////////
/* 1Keys 🎹 A 1 Kilobyte JavaScript Piano - Enhanced Version
By Frank Force 2020
https://github.com/KilledByAPixel/1Keys
Globals...
K# = piano keys
K = instruments div
O = octave input
Y = key input
S = wave shape
*/
C = new AudioContext; // audio context
A = []; // active sounds
I = 0; // instrument type
// instrument select
[...`∿🎻🎷🎹`].map((i,j)=> // instrument icons
K.innerHTML += `${i // icon
}<input type=radio name=I checked onmousedown=I=${ // radio input
3 - j}> `); // instrument select
// piano keys
for( i = 0; i < 36; i++) // 3 x 12 keys
B.innerHTML +=
`${i%12 ? `` : `<br>` // new row
}<div id=K${ // create key
k = 24 + i%12 - (i/12|0)*12 // reorder keys
} style=display:inline-block;margin:2;background:${ // key style
(w = `02579`.indexOf(i%12 - 1) < 0) ? // b or w?
`#fff;color:#000;width:60;height:180` : // white
`#000;position:absolute;margin-left:-17;width:33;height:99` // black
} onmouseover=event.buttons&&P(${ k // mouse over
}) onmousedown=P(${ k // mouse down
}) onmouseup=X(${ k // mouse up
}) onmouseout=X(${ k // mouse out
})>` + (w ? `<br>` : ``) + // lower white keys
`ZSXDCVGBHNJMQ2W3ER5T6Y7UI9O0P[=] `[k]; // show key
///////////////////////////////////////////////////////////////////////////////
// sound
// play note
P = i=> i < 0 || A[i] || // is valid and note not playing?
(
k = eval(`K`+i), // get key
k.g = k.style.background, // save original color
k.style.transition = ``, // unset transition
k.style.background = `red`, // set key color red
k.innerHTML, // force reset transition
A[i] = [ // instruments
[...`1248`], // 🎹 organ
[...`3579`], // 🎷 brass
[...`123`], // 🎻 strings
[...`4`], // ∿ sine
][ I ].map(j=>(
o = C.createOscillator(), // create oscillator
o.connect( // oscillator to gain
o.g = C.createGain( // create gain node
o.frequency.value = // set frequency
j * 55 * // A 55 root note
2**((i+3)/12 // music scale formula
+ O.value*1 // octave control
+ Y.value/12))) // key control
.connect(C.destination), // gain to destination
o.g.gain.value = .2/(1+Math.log2(j)), // set gain
o.type = // oscilator type
S.options[S.selectedIndex].innerHTML, // get type setting
o.start(), // play sound
o) // return sound
)
);
// cancel note
X = i=> A[i] && // is note playing?
(
k = eval(`K`+i), // get key
k.style.transition = `.5s`, // set transition
k.style.background = k.g, // reset original color
A[i].map(o=>
setTimeout(e=>o.stop(), 350, // stop sound after delay
o.g.gain.linearRampToValueAtTime( // set gain start ramp
o.g.gain.value, C.currentTime), // set gain
o.g.gain.linearRampToValueAtTime( // ramp off gain
A[i] = 0, C.currentTime + .3) // clear note
)
)
);
// stop all sounds if focus lost
onblur = e=> A.map((e,i)=> X(i));
///////////////////////////////////////////////////////////////////////////////
// keyboard controls
// keyboard key to piano key
T = i=>
(k = `ZSXDCVGBHNJM,L.;/Q2W3ER5T6Y7UI9O0P[=]` // map key to note
.indexOf(i.key.toUpperCase()), // find key in string
k - 5 * (k > 16)); // remap second row of keys
// play note on key down
onkeydown = i=> P(T(i));
// release note on key up
onkeyup = i=> X(T(i));
</script>