aboutsummaryrefslogtreecommitdiff
path: root/site/udo/sequencing_melodic_portamento.udo
blob: fe3bde2219d6d8f91889c391db9b6e7fedc38922 (plain)
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