-
Notifications
You must be signed in to change notification settings - Fork 2
/
synth.js
88 lines (78 loc) · 2.46 KB
/
synth.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
80
81
82
83
84
85
86
87
88
// Polyphonic Synth Tone generator based on WebAudio
// (very) inspired by https://devdocs.io/dom/web_audio_api/simple_synth
function initSynth({ audioContext, onPatchChange }){
const NOTES = [ 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B' ];
function makePulseWaveform ({ dutyCycle }) {
// from https://mitxela.com/projects/swotgb/about
const real = new Float32Array(512);
const imag = new Float32Array(512); // defaults to zeros
for (var n = 1; n < 512; n++) {
real[n] = 2 * Math.sin(Math.PI * n * dutyCycle) / (Math.PI * n)
}
return audioContext.createPeriodicWave(real, imag);
}
var patches = [
{
name: 'pulse (12.5%)',
type: 'custom',
apply: osc => osc.setPeriodicWave(makePulseWaveform({ dutyCycle: 0.125 }))
},
{
name: 'pulse (25%)',
type: 'custom',
apply: osc => osc.setPeriodicWave(makePulseWaveform({ dutyCycle: 0.25 }))
},
{
name: 'pulse (50%)',
type: 'square'
},
{
name: 'pulse (75%)',
type: 'custom',
apply: osc => osc.setPeriodicWave(makePulseWaveform({ dutyCycle: 0.75 }))
},
{
name: 'triangle',
type: 'triangle'
},
{ type: 'sine' },
{ type: 'sawtooth' },
];
var currentPatch = 0;
function switchPatch(incr = 1) {
currentPatch = (patches.length + currentPatch + incr) % patches.length;
console.log(currentPatch);
onPatchChange && onPatchChange(patches[currentPatch]);
}
onPatchChange && onPatchChange(patches[currentPatch]);
function playTone({ freq, velocity }, patch = patches[currentPatch]) {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
gainNode.connect(audioContext.destination);
// TODO: disconnect when we stop playing that note
// masterGainNode.gain.value = 1.0;
gainNode.gain.value = velocity / 127;
oscillator.connect(gainNode);
if (patch.apply) {
patch.apply(oscillator);
} else {
oscillator.type = patch.type;
}
oscillator.frequency.value = freq;
oscillator.start();
return {
oscillator,
gainNode,
};
}
function playNote({ note, octave, velocity }, patch) {
console.log('synth.playNote', { note, octave, velocity });
const freq = 440 * Math.pow(Math.pow(2, 1 / 12), ((octave - 4) * 12) + NOTES.indexOf(note));
return playTone({ freq, velocity }, patch);
}
return {
playTone,
playNote,
switchPatch,
};
}