-
Notifications
You must be signed in to change notification settings - Fork 0
/
envelopes.ts
202 lines (176 loc) · 6.85 KB
/
envelopes.ts
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
namespace envelopes {
export class Envelope {
constructor(public attack = 0, public decay = 0, public sustain = 255, public release = 0) {
}
}
export interface Stage {
offset: number;
value: number;
}
const BUFFER_SIZE = 12;
const stages = control.createBuffer(BUFFER_SIZE * 8)
export function makeTrigger(gateLength: number, pitch: number, waveForm: number, ampMod: number, pitchMod: number, ampEnv: Envelope, pitchEnv: Envelope) {
const ampStages = getStages(ampEnv, 0, ampMod, gateLength);
const pitchStages = getStages(pitchEnv, pitch, pitchMod, gateLength);
let ptr = 0;
let offset = 0;
let prevPitch = pitch;
let prevAmp = 0;
let currentAmp = ampStages[0];
let currentPitch = pitchStages[0];
let ampIndex = 0;
let pitchIndex = 0;
let temp: number;
do {
if (currentAmp && currentPitch) {
if (currentAmp.offset === currentPitch.offset) {
ptr = addNote(
stages,
ptr,
currentAmp.offset - offset,
prevAmp,
currentAmp.value,
waveForm,
prevPitch,
currentPitch.value
);
prevAmp = currentAmp.value;
prevPitch = currentPitch.value;
offset = currentAmp.offset;
ampIndex++;
pitchIndex++;
}
else if (currentAmp.offset < currentPitch.offset) {
temp = prevPitch + (currentPitch.value - prevPitch) * ((currentAmp.offset - offset) / (currentPitch.offset - offset));
ptr = addNote(
stages,
ptr,
currentAmp.offset - offset,
prevAmp,
currentAmp.value,
waveForm,
prevPitch,
temp
);
prevAmp = currentAmp.value
prevPitch = temp;
offset = currentAmp.offset;
ampIndex++;
}
else {
temp = prevAmp + (currentAmp.value - prevAmp) * ((currentPitch.offset - offset) / (currentAmp.offset - offset));
ptr = addNote(
stages,
ptr,
currentPitch.offset - offset,
prevAmp,
temp,
waveForm,
prevPitch,
currentPitch.value
);
prevAmp = temp;
prevPitch = currentPitch.value
offset = currentPitch.offset;
pitchIndex++;
}
}
else if (currentAmp) {
ptr = addNote(
stages,
ptr,
currentAmp.offset - offset,
prevAmp,
currentAmp.value,
waveForm,
prevPitch,
prevPitch
);
prevAmp = currentAmp.value
offset = currentAmp.offset;
ampIndex++;
}
else {
ptr = addNote(
stages,
ptr,
currentPitch.offset - offset,
prevAmp,
prevAmp,
waveForm,
prevPitch,
currentPitch.value
);
prevPitch = currentPitch.value
offset = currentPitch.offset;
pitchIndex++;
}
currentAmp = ampStages[ampIndex]
currentPitch = pitchStages[pitchIndex]
} while (currentAmp || currentPitch);
const buf = control.createBuffer(ptr);
buf.write(0, stages);
return buf;
}
export function getStages(env: Envelope, base: number, mod: number, gateLength: number) {
let stages: Stage[] = [];
let offset = 0;
let stageTime = 0;
if (mod > 0) {
// Attack
offset = Math.min(gateLength, env.attack)
stages.push({
offset: offset,
value: base + mod * (offset / env.attack)
});
// Decay
if (offset < gateLength) {
stageTime = Math.min(gateLength - env.attack, env.decay);
offset += stageTime;
stages.push({
offset: offset,
value: base + ((mod - (mod * (env.sustain / 255))) / env.decay) * stageTime
});
}
// Sustain
if (offset < gateLength) {
offset = gateLength;
stages.push({
offset: offset,
value: base + mod * (env.sustain / 255)
});
}
// Release
stages.push({
offset: offset + env.release,
value: base
});
}
return stages;
}
const buff = pins.createBuffer(24);
export function addNote(sndInstr: Buffer, sndInstrPtr: number, ms: number, beg: number, end: number, soundWave: number, hz: number, endHz: number) {
if (ms > 0) {
sndInstr.setNumber(NumberFormat.UInt8LE, sndInstrPtr, soundWave)
sndInstr.setNumber(NumberFormat.UInt8LE, sndInstrPtr + 1, 0)
sndInstr.setNumber(NumberFormat.UInt16LE, sndInstrPtr + 2, hz)
sndInstr.setNumber(NumberFormat.UInt16LE, sndInstrPtr + 4, ms)
sndInstr.setNumber(NumberFormat.UInt16LE, sndInstrPtr + 6, (beg * 255) >> 6)
sndInstr.setNumber(NumberFormat.UInt16LE, sndInstrPtr + 8, (end * 255) >> 6)
sndInstr.setNumber(NumberFormat.UInt16LE, sndInstrPtr + 10, endHz);
sndInstrPtr += BUFFER_SIZE;
}
sndInstr.setNumber(NumberFormat.UInt8LE, sndInstrPtr, 0) // terminate
return sndInstrPtr
}
}
namespace music {
//% shim=music::queuePlayInstructions
export function queuePlayInstructions2(timeDelta: number, buf: Buffer) { }
const freqs = hex`
1f00210023002500270029002c002e003100340037003a003e004100450049004e00520057005c00620068006e00
75007b0083008b0093009c00a500af00b900c400d000dc00e900f70006011501260137014a015d01720188019f01
b801d201ee010b022a024b026e029302ba02e40210033f037003a403dc03170455049704dd0427057505c8052006
7d06e0064907b8072d08a9082d09b9094d0aea0a900b400cfa0cc00d910e6f0f5a1053115b1272139a14d4152017
8018f519801b231dde1e`
}