-
Notifications
You must be signed in to change notification settings - Fork 1
/
soundsynthesis.js
79 lines (63 loc) · 2.63 KB
/
soundsynthesis.js
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
let context = new (window.AudioContext || window.webkitAudioContext)();
let marimbaBuffers = {};
let reverb, wetGain, dryGain;
fetch('notes/ConcertHall.mp3').then(response => {
return response.arrayBuffer();
}).then(arrayBuffer => {
return context.decodeAudioData(arrayBuffer);
}).then(audioBuffer => {
reverb = context.createConvolver();
reverb.buffer = audioBuffer;
// Create gain nodes for wet and dry signals
wetGain = context.createGain();
dryGain = context.createGain();
wetGain.gain.value = 0.2; // Wet level is the "timbre" value
dryGain.gain.value = 0.8; // Dry level is the inverse of the "timbre" value
// Connect the reverb to the wet gain
reverb.connect(wetGain);
// Connect both gains to the destination
wetGain.connect(context.destination);
dryGain.connect(context.destination);
});
for (let i = 36; i <= 96; i += 6) {
fetch(`notes/marimba${i}.mp3`).then(response => {
return response.arrayBuffer();
}).then(arrayBuffer => {
return context.decodeAudioData(arrayBuffer);
}).then(audioBuffer => {
marimbaBuffers[i] = audioBuffer;
});
}
function playNote(pitch, volume, when, duration) {
// find the closest available MIDI note
let closest = Object.keys(marimbaBuffers).reduce((prev, curr) => {
return (Math.abs(curr - pitch) < Math.abs(prev - pitch) ? curr : prev);
});
let source = context.createBufferSource();
source.buffer = marimbaBuffers[closest];
// Calculate the playback rate for the correct pitch
let midiPitch = parseFloat(pitch);
let midiClosest = parseFloat(closest);
let frequencyRatio = Math.pow(2, (midiPitch - midiClosest) / 12);
source.playbackRate.value = frequencyRatio;
// Create a gain node to control the volume
let gainNode = context.createGain();
gainNode.gain.value = Math.pow(volume, 1.7);
// Connect the source to the gain node
source.connect(gainNode);
// Connect the gain node to both the reverb (wet signal) and the dry gain
gainNode.connect(reverb);
gainNode.connect(dryGain);
// Start the note at the specified time
source.start(when);
// Stop the note at the specified end time
source.stop(when + duration);
// Schedule the gain to reduce to 0 right before the note stops.
gainNode.gain.setValueAtTime(volume, when);
gainNode.gain.linearRampToValueAtTime(0, when + duration);
// Disconnect the gainNode shortly after the note stops.
setTimeout(() => {
gainNode.disconnect();
}, (when + duration - context.currentTime) * 1000 + 50); // Add 50ms buffer to ensure the note has stopped
return source
}