blob: 1a6f907e284284a135cf985d87764eacdf298f0b (
plain)
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
|
type AudioContextCtor = typeof AudioContext;
export class AudioEngine {
private context?: AudioContext;
private master?: GainNode;
private drone?: OscillatorNode;
private shimmer?: OscillatorNode;
private active = false;
activate(): void {
if (this.active) {
void this.context?.resume();
return;
}
const ctor = window.AudioContext ?? (window as Window & { webkitAudioContext?: AudioContextCtor }).webkitAudioContext;
if (!ctor) {
return;
}
this.context = new ctor();
this.master = this.context.createGain();
this.master.gain.value = 0.07;
this.master.connect(this.context.destination);
this.drone = this.context.createOscillator();
this.drone.type = 'sawtooth';
this.drone.frequency.value = 82;
const droneGain = this.context.createGain();
droneGain.gain.value = 0.18;
this.drone.connect(droneGain);
droneGain.connect(this.master);
this.drone.start();
this.shimmer = this.context.createOscillator();
this.shimmer.type = 'triangle';
this.shimmer.frequency.value = 164;
const shimmerGain = this.context.createGain();
shimmerGain.gain.value = 0.08;
this.shimmer.connect(shimmerGain);
shimmerGain.connect(this.master);
this.shimmer.start();
this.active = true;
}
setMood(fidelity: number, cycle: number, layerIndex: number): void {
if (!this.context || !this.master || !this.drone || !this.shimmer) {
return;
}
const now = this.context.currentTime;
const base = 82 + cycle * 8 + layerIndex * 3;
this.drone.frequency.linearRampToValueAtTime(base + fidelity * 18, now + 0.18);
this.shimmer.frequency.linearRampToValueAtTime(base * 2 + (1 - fidelity) * 70, now + 0.22);
this.master.gain.linearRampToValueAtTime(0.045 + fidelity * 0.04, now + 0.25);
}
pulseJump(boost = false): void {
if (!this.context || !this.master) {
return;
}
const osc = this.context.createOscillator();
const gain = this.context.createGain();
osc.type = boost ? 'square' : 'sine';
osc.frequency.value = boost ? 420 : 260;
gain.gain.value = 0.0001;
osc.connect(gain);
gain.connect(this.master);
const now = this.context.currentTime;
gain.gain.exponentialRampToValueAtTime(0.06, now + 0.01);
gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.24);
osc.frequency.exponentialRampToValueAtTime(boost ? 170 : 120, now + 0.24);
osc.start(now);
osc.stop(now + 0.26);
}
pulseSnap(): void {
if (!this.context || !this.master) {
return;
}
const osc = this.context.createOscillator();
const gain = this.context.createGain();
osc.type = 'triangle';
osc.frequency.value = 680;
gain.gain.value = 0.0001;
osc.connect(gain);
gain.connect(this.master);
const now = this.context.currentTime;
gain.gain.exponentialRampToValueAtTime(0.08, now + 0.01);
gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.45);
osc.frequency.exponentialRampToValueAtTime(240, now + 0.45);
osc.start(now);
osc.stop(now + 0.5);
}
}
|