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
128
129
130
|
import Tone from 'tone'
import Sampler from './lib/sampler'
import draw from './draw'
import keys from './lib/keys'
import color from './lib/color'
import mouse from './lib/mouse'
import { Hall } from './lib/hall'
import {
browser, requestAudioContext,
randint, randrange, choice, clamp, lerp, dist, shuffle,
isMobile,
} from './lib/util'
const root = 440
const s = 50
const w = window.innerWidth
const h = window.innerHeight
const ws = w/s, hs = h/s
const HALLWAY_LENGTH = 147
const SPEAKER_COUNT = 16
let notes = [299, 336, 374, 399, 449, 498, 561, 598].map(i => i/2)
notes = notes.concat(notes.map(i => i/2))
notes = notes.concat(notes.map(i => i*2))
notes = shuffle(notes)
let samplers = {}
let sampler
requestAudioContext( () => {
samplers.smash = new Sampler('samples/smash/g{}.mp3', 12)
samplers.glass = new Sampler('samples/glass/0{}Particle.mp3', 20)
samplers.kalimba = new Sampler('samples/kalimba/380731__cabled-mess__sansula-08-c-raw.wav', 10)
samplers.choice = (m,n) => {
const r = Math.random()
if (r < m) return samplers.smash
if (r < m+n) return samplers.kalimba
return samplers.glass
}
Tone.Buffer.on('load', function(){
console.log('all buffers are loaded.')
redraw()
})
})
const hall = new Hall ({
length: HALLWAY_LENGTH,
speakers: SPEAKER_COUNT,
})
let last_index = 0, last_dist = 0
keys.listen(index => {
trigger(Math.random(), ((index+7) % SPEAKER_COUNT) / SPEAKER_COUNT, 0, samplers.smash)
})
function redraw(){
draw.clear()
draw.line(50/147 * window.innerHeight)
draw.line(100/147 * window.innerHeight)
hall.speakers.forEach(speaker => {
draw.ctx.save()
draw.ctx.globalAlpha = 1 - speaker.z / HALLWAY_LENGTH
draw.dot(
window.innerWidth / 2 - speaker.pan * window.innerHeight,
speaker.z / HALLWAY_LENGTH * window.innerHeight,
10
)
draw.dot(
window.innerWidth / 2 + speaker.pan * window.innerHeight,
speaker.z / HALLWAY_LENGTH * window.innerHeight,
10
)
draw.ctx.restore()
})
}
mouse.register({
down: (x, y) => {
px = x
py = y
x /= window.innerWidth
y /= window.innerHeight
redraw()
trigger(x, y)
},
move: (x, y, dx, dy) => {
last_dist = dist(x,y,px,py)
if (last_dist < 5) {
return
}
px = x
py = y
x /= window.innerWidth
y /= window.innerHeight
play(x, y)
},
up: (x, y) => {
},
})
let timeout, px = 0, py = 0
function play(x, y){
const count = 3
const dt = 0.25
trigger(x, y, 0)
}
function trigger(x, y, t, sampler){
t = t || 0
t += Tone.now()
sampler = sampler || last_dist > 40
? samplers.choice(0.2, 0.2)
: samplers.choice((1-y) * 0.2, y*0.02)
const freq = notes[Math.floor(x * notes.length)]
const speaker = hall.play(sampler, y, freq, x, t)
draw.triangle(
lerp(x, 0, 1) * window.innerWidth,
lerp(y, 0, 1) * window.innerHeight - 20,
40
)
}
|