aboutsummaryrefslogtreecommitdiff
path: root/site/udo/tabproc.udo
diff options
context:
space:
mode:
Diffstat (limited to 'site/udo/tabproc.udo')
-rwxr-xr-xsite/udo/tabproc.udo217
1 files changed, 217 insertions, 0 deletions
diff --git a/site/udo/tabproc.udo b/site/udo/tabproc.udo
new file mode 100755
index 0000000..1fd4e49
--- /dev/null
+++ b/site/udo/tabproc.udo
@@ -0,0 +1,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