-
Notifications
You must be signed in to change notification settings - Fork 0
/
sid.glsl
115 lines (94 loc) · 2.57 KB
/
sid.glsl
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
#version 430
layout(local_size_x = 16) in;
uniform uint sampleOffset;
uniform float sampleFrequency;
uniform int noteCount;
struct Note
{
float timestamp;
float frequency;
float pulse;
int waveform;
float attack;
float decay;
float sustain;
float release;
float duration;
int ctrlOffset;
int ctrlCount;
};
layout(std430, binding = 0) restrict readonly buffer musicBuffer
{
Note notes[];
};
struct Ctrl
{
float timestamp;
float frequency;
};
layout(std430, binding = 1) restrict readonly buffer ctrlBuffer
{
Ctrl ctrl[];
};
layout(binding = 1) uniform samplerBuffer noiseBuffer;
layout(r16i, binding = 0) uniform writeonly iimageBuffer sampleBuffer;
float envelope(float attack, float decay, float sustain, float release, float duration, float t)
{
float v = sustain;
if(t < attack)
v = clamp(t / attack, 0.0f, 1.0f);
else if(t < attack + decay)
v = max(exp2((t - attack) / decay * -7.2f), sustain);
if(t < duration)
return v;
t -= duration;
if(t < release)
return v * exp2(t / release * -7.2f);
return 0.0f;
}
float waveform(int form, float t, float freq, float pulse, int ctrlOffset, int ctrlCount)
{
for(int i = 0; i < ctrlCount; i++)
{
if(ctrl[ctrlOffset + i].timestamp > t)
break;
freq = ctrl[ctrlOffset + i].frequency;
}
t *= freq;
float v = 1.0f;
if((form & 0x10) != 0) // tri
{
float p = fract(t);
v = min(p * 4.0f - 1.0f, 3.0f - p * 4.0f);
}
if((form & 0x20) != 0) // saw
v = fract(t + 0.5f) * 2.0f - 1.0f;
if((form & 0x40) != 0) // noise
v = texelFetch(noiseBuffer, int(t * 16.0f) & (1024 * 1024 - 1)).x * 2.0f - 1.0f;
if((form & 0x80) != 0) // pulse
v *= float(fract(t) > pulse);
return v;
}
float playNote(Note n, float t)
{
t -= n.timestamp;
float v = waveform(n.waveform, t, n.frequency, n.pulse, n.ctrlOffset, n.ctrlCount);
v *= envelope(n.attack, n.decay, n.sustain, n.release, n.duration, t);
return v;
}
void main()
{
float t = float(sampleOffset + gl_GlobalInvocationID.x) / sampleFrequency;
int found[3] = {0, 0, 0};
for(int i = 1; i < noteCount; i++)
{
if(notes[i].timestamp > t)
break;
found[notes[i].waveform & 0xf] = i;
}
float v = playNote(notes[found[0]], t);
v += playNote(notes[found[1]], t);
v += playNote(notes[found[2]], t);
int s = int(clamp(v * 0.3f, -1.0f, 1.0f) * 32767.0f);
imageStore(sampleBuffer, int(gl_GlobalInvocationID.x), ivec4(s, 0, 0, 0));
}