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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
|
#ifndef UDO_MELSEQUENCINGPORT
#define UDO_MELSEQUENCINGPORT ##
/*
Extension to sequencing_melodic.udo which permits usage of k-rate frequency arrays
This file is part of the SONICS UDO collection by Richard Knight 2021
License: GPL-2.0-or-later
http://1bpm.net
*/
#include "__config__.udo" ; using fftsize for tuning
#include "sequencing_melodic.udo"
#include "wavetables.udo"
#include "frequency_tools.udo"
gimel_freqs ftgen 0, 0, -12, -7, 0 ; current notes: index 0 is the length
gimel_amps ftgen 0, 0, -12, -7, 0 ; current notes: index 0 is the length
gimel_portamento_beatratio init 0.5 ; portamento time as ratio of current beat time
gimel_linetype init 0 ; 0=pre-section, 1=post-section
/*
Automate a frequency/amp line
*/
instr _mel_linedraw
index = p4
ifreq = p5
iamp = p6
icurrentfreq table index, gimel_freqs
if (icurrentfreq == 0 && ifreq != 0) then
tablew ifreq, index, gimel_freqs
elseif (ifreq != 0 && icurrentfreq != ifreq) then
tablew line:k(icurrentfreq, p3, ifreq), index, gimel_freqs
endif
icurrentamp table index, gimel_amps
if (icurrentamp != iamp) then
tablew line:k(icurrentamp, p3, iamp), index, gimel_amps
endif
endin
instr _mel_linestep_inner
if (timeinstk() == 1) then
turnoff2 "_mel_linedraw", 0, 0
endif
if (table:i(1, gimel_next_notes) != 0) then
index = 0
while (index < table:i(0, gimel_next_notes)) do
event_i "i", "_mel_linedraw", 1/kr, p3, index, cpsmidinn(table:i(index + 1, gimel_next_notes)), 1
index += 1
od
while (index < ftlen(gimel_freqs)) do
event_i "i", "_mel_linedraw", 1/kr, p3, index, 0, 0
index += 1
od
endif
endin
instr _mel_linestep
icurrentduration mel_length
ilinetime = (i(gkseq_beattime) * gimel_portamento_beatratio)
if (gimel_linetype == 0) then
inextline = icurrentduration - ilinetime
else
inextline = icurrentduration
endif
event_i "i", "_mel_linestep_inner", inextline, ilinetime
turnoff
endin
/*
Portamento manager: respond to gkmel_section_change trigger by calling _mel_linestep instrument
*/
instr _mel_linemanager
; set initial freqs
index = 0
while (index < table:i(0, gimel_current_notes)) do
tablew cpsmidinn(table:i(index + 1, gimel_current_notes)), index, gimel_freqs
tablew 1, index, gimel_amps
index += 1
od
while (index < ftlen(gimel_freqs)) do
tablew 0, index, gimel_amps
index += 1
od
schedkwhen gkmel_section_change, 0, 1, "_mel_linestep", 0, 1
endin
schedule "_mel_linemanager", 0.1, 36000 ; notes not ready on 0
;alwayson "_mel_linemanager"
/*
Recursively create a chord to be used by mel_tune_portamento; internal use only
aout _mel_tune_chord_portamento kfreqmult, ifn, imaxmult, imult, index
aout chord output
kfreqmult frequency multiplier to apply to tuning
ifn wavetable to use
imaxmult multiples of harmonics to generate in tuning
imult internal multiplier for recursion
index internal index for recursion
*/
opcode _mel_tune_chord_portamento, a, kiipo
kfreqmult, ifn, imaxmult, imult, index xin
if (index + 1 > ftlen(gimel_amps)) then
index = 0
imult += 1
endif
aout = oscil(table:k(index, gimel_amps), kfreqmult * table:k(index, gimel_freqs) * pow:k(2, imult), ifn) * 0.1
; recursion for all chord parts
if (imult <= imaxmult) then
aout += _mel_tune_chord_portamento(kfreqmult, ifn, imaxmult, imult, index + 1)
endif
xout aout
endop
/*
PVS morph tuning to current melodic sequencer notes
aoutL, aoutR mel_tune_portamento ainL, ainR, [ifn=gifnSine, imult=4, ifftrate=giFFTsize, ifftdiv=giFFTwinFactor, kfreqmult=1]
aoutL, aoutR output audio
ainL, ainR input audio
ifn wavetable to use
imaxmult multiples of harmonics to generate in tuning (defaults to 4)
ifftrate fft size, defaults to config default
ifftdiv fft window division factor (eg 4, 8, 16), defaults to config default
kfreqmult frequency multiplier to apply to tuning
*/
opcode mel_tune_portamento, aa, aaooooP
aL, aR, ifn, imaxmult, ifftrate, ifftdiv, kfreqmult xin
ifn = (ifn == 0) ? gifnSine : ifn
imaxmult = (imaxmult == 0) ? 4 : imaxmult
ifftrate = (ifftrate == 0) ? giFFTsize : ifftrate
ifftdiv = (ifftdiv == 0) ? giFFTwinFactor : ifftdiv
fmods pvsanal _mel_tune_chord_portamento(kfreqmult, ifn, imaxmult), ifftrate, ifftrate/ifftdiv, ifftrate, 1
fL1 pvsanal aL, ifftrate, ifftrate/ifftdiv, ifftrate, 1
fR1 pvsanal aR, ifftrate, ifftrate/ifftdiv, ifftrate, 1
fL2 pvsmorph fL1, fmods, 0, 1
fR2 pvsmorph fR1, fmods, 0, 1
aL1 pvsynth fL2
aR1 pvsynth fR2
idel = (ifftrate)/sr
aL1 balance aL1, delay(aL, idel)
aR1 balance aR1, delay(aR, idel)
xout aL1, aR1
endop
/*
Bandpass tuning for internal use only, applied to each note frequency for full spectrum bandpass
aoutL, aoutR _mel_bandpass_portamento_freqgroup ainL, ainR, kfreq, kbw, iprecise, imult
aoutL, aoutR output audio
ainL, ainR input audio
kfreq frequency to tune to
kbw bandwidth of bandpass filters
iprecise if 1, use two serial bandpass filters for more precision
imult current multiplier for recursion
*/
opcode _mel_bandpass_portamento_freqgroup, aa, aakkip
ainL, ainR, kfreq, kbw, iprecise, imult xin
imaxmult = 24
aoutL butterbp ainL, kfreq*imult, kbw
aoutR butterbp ainR, kfreq*imult, kbw
if (iprecise == 1) then
aoutL butterbp aoutL, kfreq*imult, kbw
aoutR butterbp aoutR, kfreq*imult, kbw
endif
if (imult <= imaxmult) then
aoutLrec, aoutRrec _mel_bandpass_portamento_freqgroup ainL, ainR, kfreq, kbw, iprecise, imult * 2
aoutL += aoutLrec
aoutR += aoutRrec
endif
xout aoutL, aoutR
endop
/*
Bandpass tuning to current melodic sequencer notes
aoutL, aoutR mel_bandpass_portamento ainL, ainR [, kbw=1, iprecise=0, index=0]
aoutL, aoutR output audio
ainL, ainR input audio
kbw bandwidth of bandpass filters
iprecise if 1, use two serial bandpass filters for more precision
index recursion index for internal use
*/
opcode mel_bandpass_portamento, aa, aaPoo
ainL, ainR, kbw, iprecise, index xin
kamp = table:k(index, gimel_amps)
if (kamp > 0) then
kfreq = table:k(index, gimel_freqs)
aoutL, aoutR _mel_bandpass_portamento_freqgroup ainL, ainR, kfreq, kbw, iprecise
aoutL *= kamp
aoutR *= kamp
else
aoutL = 0
aoutR = 0
endif
if (index < ftlen(gimel_amps)) then
aoutLr, aoutRr mel_bandpass_portamento ainL, ainR, kbw, iprecise, index + 1
aoutL += aoutLr
aoutR += aoutRr
endif
xout aoutL, aoutR
endop
/*
Ringmod tuning to current melodic sequencer notes
aoutL, aoutR mel_ringmod_portamento ainL, ainR kfreqmult, index
aoutL, aoutR output audio
ainL, ainR input audio
kfreqmult frequency multiplier to apply to current frequencies
index recursion index for internal use
*/
opcode mel_ringmod_portamento, aa, aaPo
ainL, ainR, kfreqmult, index xin
kamp = table:k(index, gimel_amps)
if (kamp > 0) then
aoutL, aoutR ringmod1 ainL, ainR, table:k(index, gimel_freqs) * kfreqmult
aoutL *= kamp
aoutR *= kamp
else
aoutL = 0
aoutR = 0
endif
if (index < ftlen(gimel_amps)) then
aoutLr, aoutRr mel_ringmod_portamento ainL, ainR, kfreqmult, index + 1
aoutL += aoutLr
aoutR += aoutRr
endif
xout aoutL, aoutR
endop
/*
Reson tuning to current melodic sequencer notes
aoutL, aoutR mel_reson_portamento ainL, ainR kfreqmult, index
aoutL, aoutR output audio
ainL, ainR input audio
kfreqmult frequency multiplier to apply to current frequencies
index recursion index for internal use
*/
opcode mel_reson_portamento, aa, aaPo
ainL, ainR, kfreqmult, index xin
kamp = table:k(index, gimel_amps)
kfreq = table:k(index, gimel_freqs)
if (kamp > 0) then
aoutL resony ainL, kfreq * kfreqmult, 2, 8, 10
aoutR resony ainR, kfreq * kfreqmult, 2, 8, 10
aoutL balance aoutL, ainL
aoutR balance aoutR, ainR
aoutL *= kamp
aoutR *= kamp
else
aoutL = 0
aoutR = 0
endif
if (index < ftlen(gimel_amps)) then
aoutLr, aoutRr mel_reson_portamento ainL, ainR, kfreqmult, index + 1
aoutL += aoutLr
aoutR += aoutRr
endif
xout aoutL, aoutR
endop
#end
|