-
Notifications
You must be signed in to change notification settings - Fork 2
/
sound-maker.html
148 lines (145 loc) · 5.25 KB
/
sound-maker.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
#knobs p label {
display: inline-block;
width: 120px;
}
#knobs p span {
display: inline-block;
margin-left: 10px;
width: 50px;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
</head>
<body>
<div id="content">
<h1>Sound Maker</h1>
<div id="knobs"></div>
</div>
<script src="lib/riffwave.js"></script> <!-- imports RIFFWAVE -->
<script src="lib/sfxr.js"></script> <!-- imports SoundEffect -->
<script>
const knobGroups = [
{
label: 'Envelope',
knobs: [
{ key: 'p_env_attack', label: 'Attack time' },
{ key: 'p_env_sustain', label: 'Sustain time' },
{ key: 'p_env_punch', label: 'Sustain punch' },
{ key: 'p_env_decay', label: 'Decay time' },
],
},
{
label: 'Tone',
knobs: [
{ key: 'p_base_freq', label: 'Start frequency' },
{ key: 'p_freq_limit', label: 'Min frequency cutoff' },
{ key: 'p_freq_ramp', label: 'Slide', signed: true },
{ key: 'p_freq_dramp', label: 'Delta slide', signed: true },
],
},
{
label: 'Vibrato',
knobs: [
{ key: 'p_vib_strength', label: 'Vibrato depth' },
{ key: 'p_vib_speed', label: 'Vibrato speed' },
],
},
{
label: 'Tonal change',
knobs: [
{ key: 'p_arp_mod', label: 'Change amount', signed: true },
{ key: 'p_arp_speed', label: 'Change speed' },
],
},
{
label: 'Square wave duty', // proportion of time signal is high vs. low
knobs: [
{ key: 'p_duty', label: 'Square duty' },
{ key: 'p_duty_ramp', label: 'Duty sweep', signed: true },
],
},
{
label: 'Repeat',
knobs: [
{ key: 'p_repeat_speed', label: 'Repeat speed' },
],
},
{
label: 'Flanger',
knobs: [
{ key: 'p_pha_offset', label: 'Flanger offset', signed: true },
{ key: 'p_pha_ramp', label: 'Flanger sweep', signed: true },
],
},
{
label: 'Low-pass filter',
knobs: [
{ key: 'p_lpf_freq', label: 'Low-pass filter cutoff' },
{ key: 'p_lpf_ramp', label: 'Low-pass filter cutoff sweep', signed: true },
{ key: 'p_lpf_resonance', label: 'Low-pass filter resonance' },
],
},
{
label: 'High-pass filter',
knobs: [
{ key: 'p_hpf_freq', label: 'High-pass filter cutoff' },
{ key: 'p_hpf_ramp', label: 'High-pass filter cutoff sweep', signed: true },
],
},
];
// note: some of the knobs listed above may not be used by noise generator (e.g. duty?)
const DRY_SNARE = '7BMHBGHtQBNHMkrpKTFy7cNBjZbgFpyZUvbv8X8fogVzVC9t8rHtuLK1vyDgC9JRZkPicMw6bWZs5a4xDexdVuD8DgT9gZr7kSaw2D2XfLiUG824x65Fj7XXm'; // dry snare
const soundParams = (new Params()).fromB58(DRY_SNARE); // contains knob values
const playSound = () => new SoundEffect(soundParams).generate().getAudio().play();
const knobsNode = document.getElementById('knobs');
const PRECISION = 100;
const appendKnob = ({ key, value, label, signed }) => {
if (!key.match(/^p_/)) return; // skip non-knob entries
const labelText = label || key.replace(/^p_/, '').replace(/_/g, ' ');
const labelNode = document.createElement('label');
labelNode.innerHTML = labelText + ': ';
const valueNode = document.createElement('span');
valueNode.innerHTML = value;
const inputNode = document.createElement('input');
inputNode.type = 'range';
inputNode.value = signed
? (value + 1) * PRECISION / 2
: value * PRECISION;
inputNode.onclick = playSound;
inputNode.onchange = () => {
console.log({ key, value: inputNode.value });
const value = signed
? (parseInt(2 * inputNode.value) - 1) / PRECISION
: parseInt(inputNode.value) / PRECISION;
valueNode.innerHTML = value;
soundParams[key] = value;
playSound();
};
const pNode = document.createElement('p');
pNode.appendChild(labelNode);
pNode.appendChild(inputNode);
pNode.appendChild(valueNode);
knobsNode.appendChild(pNode);
};
//Object.entries(soundParams).forEach(([key, value]) => appendKnob({ key, value }));
Object.values(knobGroups).forEach(({ label, knobs }) => {
const headingNode = document.createElement('h2');
headingNode.innerHTML = label;
knobsNode.appendChild(headingNode);
knobs.forEach(({ key, label, signed }) => appendKnob({ key, label, signed, value: soundParams[key] }));
});
const pNode = document.createElement('p');
const buttonNode = document.createElement('button');
buttonNode.innerHTML = 'play';
buttonNode.onclick = playSound;
pNode.appendChild(buttonNode);
document.getElementById('content').appendChild(pNode);
</script>
</body>
</html>