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
|
#ifndef UDO_TABPROC
#define UDO_TABPROC ##
/*
JIT style table processor
This file is part of the SONICS UDO collection by Richard Knight 2022
License: GPL-2.0-or-later
http://1bpm.net
*/
#include "lagdetect.udo"
#define TP_DFLT_RATE #10#
gitabproc_instancetrack = 0 ; internal instance tracking for channel names
opcode tabproc_profiler, kkk, ij
instrnum, istabletime xin
istabletime = (istabletime == -1) ? 5 : istabletime
ichannelbase = gitabproc_instancetrack
gitabproc_instancetrack += 1
kblocksize init 16384
kprocrate init $TP_DFLT_RATE
kdone init 0
; actual processing at iprocrate
if (kdone == 0) then
klagging, ktimesincelag lagdetect
if (klagging == 1) then
; reduce block size first, then reduce processing rate
if (kblocksize > 128) then
kblocksize -= 128
elseif (kprocrate > 1) then
kprocrate -= 1
endif
endif
if (ktimesincelag >= istabletime) then
kdone = 1
kgoto complete
endif
ktrig metro kprocrate
if (ktrig == 1) then
kcount = 0
while (kcount < kblocksize) do
atest oscil 1, 1000
; set channels and call subinstrument for processing
SchannelL = sprintf("tabproc%dL", ichannelbase)
SchannelR = sprintf("tabproc%dR", ichannelbase)
chnset atest, SchannelL
chnset atest, SchannelR
aL, aR subinstr instrnum, SchannelL, SchannelR
kcount += 1
od
endif
endif
complete:
xout kdone, kblocksize, kprocrate
endop
opcode tabproc_profiler, kkk, S
Sinstrname xin
kdone, kblocksize, kprocrate tabproc_profiler nstrnum(Sinstrname)
xout kdone, kblocksize, kprocrate
endop
/*
Process a table with a subinstrument at deferred k-rate.
The table ifn is read and passed to the instrument Sinstrname/instrnum which should perform processing and use the outs() or out() opcode to return audio to be written to ifn.
Sinstrument is passed the input audio on channels with the names passed as p4 (left) and p5 (right, if the sound is stereo).
Hence the instrument should implement something such as
aL = chnget:a(strget(p4))
aR = chnget:a(strget(p5))
in order to deal with the incoming audio.
The table can be read in reverse by specifying ireverseread=1. Similarly it can be written back to in reverse with ireversewrite=1.
tabproc performs processing in chunks at a given rate in order to minimise CPU usage and allow other instruments to continue uninterrupted (without this, it
would consume all available CPU and block the performance until complete). The blocksize iblocksize is how many k-cycles are processed in each chunk, and iprocrate
determines the frequency at which chunks are processed. Defaults are provided but these will be best altered depending on the load of Sinstrument and the
available CPU.
iautoadjust can be set to 1 to attempt to automatically reduce iblocksize and iprocrate if it is detected that the CPU cannot keep up. This is done by comparing
the realtime clock against Csound's instrument time. If the difference is over iautotimethreshold, then a reduction of block size/frequency is made.
kdone, kpercent tabproc ifn, instrnum [, ireverseread=0, ireversewrite=0, iautoadjust=0, iblocksize=2048, iprocrate=TP_DFLT_RATE, iautotimethreshold=TP_DFLT_TTHRESH]
kdone, kpercent tabproc ifn, Sinstrname [, ireverseread=0, ireversewrite=0, iautoadjust=0, iblocksize=2048, iprocrate=TP_DFLT_RATE, iautotimethreshold=TP_DFLT_TTHRESH]
kdone output trigger when the processing has completed
kpercent percent of the table that has been processed
ifn table containing audio (typically GEN1 but could be any). Mono and stereo are supported
instrnum the instrument number to be invoked to process the table
Sinstrname the instrument name to be invoked to process the table
ireverseread set to 1 in order to read the table in reverse
ireversewrite set to 1 in order to write back to the table in reverse
iautoadjust set to 1 to enable automatic reduction of iblocksize and iprocrate if processing lag is detected
iblocksize number of k-cycles of audio to be processed in each run
iprocrate frequency at which to perform block processing
iautotimethreshold time lag at which to automatically adjust, if iautoadjust is specified
*/
opcode tabproc, kk, iiooojjj
ifn, instrnum, ireverseread, ireversewrite, iautoadjust, iblocksize, iprocrate, iautotimethreshold xin
kblocksize init (iblocksize == -1) ? 2048 : iblocksize
kprocrate init ((iprocrate == -1) ? $TP_DFLT_RATE : iprocrate)
iautotimethreshold = (iautotimethreshold == -1) ? $LAG_DFLT_TTHRESH : iautotimethreshold
ichannelbase = gitabproc_instancetrack
gitabproc_instancetrack += 1
ichannels = ftchnls(ifn)
isr = ftsr(ifn)
ilenraw = ftlen(ifn)
ilen = ilenraw / ichannels
kpos init 0
kpercent = 0
kdone init 0
if (kdone == 0) then
; auto adjust block size to account when cpu can't keep up
if (iautoadjust == 1 && lagdetect:k(iautotimethreshold) == 1) then
if (kblocksize > 128) then
kblocksize -= 128
elseif (kprocrate > 1) then
kprocrate -= 1
endif
endif
; each block processing at iprocrate
ktrig metro kprocrate
if (ktrig == 1) then
kcount = 0
while (kcount < kblocksize) do
apos = round:a(lphasor(isr / sr)) ; rounded to deal with stereo offset if required
; if complete, set kdone
if (downsamp(apos) >= ilen) then ;? >= ilenraw) then
kpercent = 100
kdone = 1
kgoto complete
endif
; read and write position calcs for stereo / reverse
aposbase = apos * ichannels
if (ireverseread == 1) then
aposrR = ilen - aposbase
aposrL = (ichannels == 2) ? aposrR + 1 : aposrR
else
aposrL = aposbase
aposrR = aposbase + 1
endif
if (ireversewrite == 1) then
aposwR = ilen - aposbase
aposwL = (ichannels == 2) ? aposwR + 1 : aposwR
else
aposwL = aposbase
aposwR = aposbase + 1
endif
; read source table
aL table aposrL, ifn
if (ichannels == 2) then
aR table aposrR, ifn
else
aR = aL
endif
; set channels and call subinstrument for processing
SchannelL = sprintf("tabproc%dL", ichannelbase)
SchannelR = sprintf("tabproc%dR", ichannelbase)
chnset aL, SchannelL
chnset aR, SchannelR
aL, aR subinstr instrnum, SchannelL, SchannelR
; write back to table
tablew aL, aposwL, ifn
if (ichannels == 2) then
tablew aR, aposwR, ifn
endif
kcount += 1
od
endif
; calculate percent complete
kpercent = (100 / ilenraw) * downsamp(apos)
endif
complete:
xout kdone, kpercent
endop
; derived opcode for named instruments
opcode tabproc, kk, iSooojjj
ifn, Sinstrname, ireverseread, ireversewrite, iautoadjust, iblocksize, iprocrate, iautotimethreshold xin
kdone, kpercent tabproc ifn, nstrnum(Sinstrname), ireverseread, ireversewrite, iautoadjust, iblocksize, iprocrate, iautotimethreshold
xout kdone, kpercent
endop
#end
|