From 9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22 Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 13 Apr 2025 18:48:02 +0100 Subject: initial --- site/udo/tabproc.udo | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100755 site/udo/tabproc.udo (limited to 'site/udo/tabproc.udo') 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 -- cgit v1.2.3