From 9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22 Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 13 Apr 2025 18:48:02 +0100 Subject: initial --- site/udo/twigs/twigs.udo | 444 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 444 insertions(+) create mode 100755 site/udo/twigs/twigs.udo (limited to 'site/udo/twigs/twigs.udo') diff --git a/site/udo/twigs/twigs.udo b/site/udo/twigs/twigs.udo new file mode 100755 index 0000000..a4bda3f --- /dev/null +++ b/site/udo/twigs/twigs.udo @@ -0,0 +1,444 @@ +#ifndef UDO_TWIGS +#define UDO_TWIGS ## +/* + Twigs + Spectral transformer + + This file is part of the SONICS UDO collection by Richard Knight 2024, 2025 + License: GPL-2.0-or-later + http://1bpm.net +*/ + +#include "/pvs_fulltabproc.udo" +#include "/host_platform.udo" +#include "/lagdetect.udo" +#include "/interop.udo" + +gitwgs_bufferL = -1 +gitwgs_bufferR = -1 +gitwgs_tpvfHandleL = -1 +gitwgs_tpvfHandleR = -1 +gitwgs_channels = -1 +gitwgs_userstopped = 0 +gitwgs_fnbinselection = -1 +gitwgs_fnbintimeselection = -1 + +#include "/twigs/checkpointing.udo" + +opcode twgs_getselectionindexes, ii, i + indexbin xin + ibins = ftlen(gitwgs_fnbinselection) + itablen = ftlen(tab_i(2, gitwgs_tpvfHandleL)) + iframes = itablen / ibins + istartindex = (round(tab_i(indexbin, gitwgs_fnbintimeselection) * iframes) * ibins) + indexbin + iendindex = (round(tab_i(indexbin + ibins, gitwgs_fnbintimeselection) * iframes) * ibins) + indexbin + if (iendindex > itablen) then + iendindex = itablen + endif + +;print tab_i(indexbin, gitwgs_fnbintimeselection) +;print istartindex +;print tab_i(indexbin + ibins, gitwgs_fnbintimeselection) +;print iendindex + xout istartindex, iendindex +endop + +opcode twgs_failresponse, S, ij + icbid, istatus xin + xout sprintf("{\"cbid\":%d,\"status\":%d}", icbid, istatus) +endop + + +opcode twgs_refreshviewresponse, S, i + icbid xin + ifnampL tab_i 2, gitwgs_tpvfHandleL + ifnfreqL tab_i 3, gitwgs_tpvfHandleL + iduration tab_i 4, gitwgs_tpvfHandleL + if (gitwgs_channels == 2) then + ifnampR tab_i 2, gitwgs_tpvfHandleR + ifnfreqR tab_i 3, gitwgs_tpvfHandleR + else + ifnampR = -1 + ifnfreqR = -1 + endif + xout sprintf("{\"cbid\":%d,\"status\":1,\"tables\":[%d,%d,%d,%d],\"duration\":%f,\"undolevel\":%d}", icbid, ifnampL, ifnfreqL, ifnampR, ifnfreqR, iduration, gitwgs_checkpointstate) +endop + + +opcode twgs_clearbuffers, 0, pp + ihandles, ibuffers xin + if (ibuffers == 1) then + if (gitwgs_bufferL > 0 && ftexists(gitwgs_bufferL) == 1) then + ftfree gitwgs_bufferL, 0 + gitwgs_bufferL = -1 + endif + if (gitwgs_bufferR > 0 && ftexists(gitwgs_bufferR) == 1) then + ftfree gitwgs_bufferR, 0 + gitwgs_bufferR = -1 + endif + endif + + if (ihandles == 1) then + if (gitwgs_tpvfHandleL > 0 && ftexists(gitwgs_tpvfHandleL) == 1) then + ftfree gitwgs_tpvfHandleL, 0 + gitwgs_tpvfHandleL = -1 + endif + if (gitwgs_tpvfHandleR > 0 && ftexists(gitwgs_tpvfHandleR) == 1) then + ftfree gitwgs_tpvfHandleR, 0 + gitwgs_tpvfHandleR = -1 + endif + gitwgs_channels = -1 + endif +endop + +opcode twgs_loadfile, ik, Sjj + Spath, ifftsize, ifftdecimation xin + kdone init 0 + if (filevalid(Spath) != 1) then + iresponse = -1 + goto complete + endif + + ifilesr = filesr(Spath) + ifilechannels = filenchnls(Spath) + ilens = filelen(Spath) + ilen = round(ilens * ifilesr) + + if (ilen >= gihost_max32bitftlen || ilens * sr >= gihost_max32bitftlen) then ; limitation with WASM Csound build at the moment + iresponse = -2 + goto complete + endif + + gitwgs_channels = ifilechannels + twgs_clearbuffers() + twgs_checkpoint_clear() + + gitwgs_bufferL = ftgen(0, 0, -ilen, 1, Spath, 0, 0, 1) + if (gitwgs_channels == 2) then + gitwgs_bufferR = ftgen(0, 0, -ilen, 1, Spath, 0, 0, 2) + imono = 0 + else + imono = 1 + endif + + if (sr != ifilesr) then ; different sr causes issues in table reading opcodes, convert.. + inewlen = ilens * sr + ifnnewL ftgen 0, 0, -inewlen, -2, 0 + if (imono == 0) then + ifnnewR ftgen 0, 0, -inewlen, -2, 0 + endif + ktimek timeinstk + ikcycles = ilens * kr + if (ktimek == 1) then + kcount = 0 + while (kcount < ikcycles) do + aposw linseg 0, ilens, inewlen - 1 + aposr linseg 0, ilens, ilen - 1 + asig table3 aposr, gitwgs_bufferL + tablew asig, aposw, ifnnewL + if (imono == 0) then + asig table3 aposr, gitwgs_bufferR + tablew asig, aposw, ifnnewR + endif + kcount += 1 + od + else + kdone = 1 + endif + + ftfree gitwgs_bufferL, 1 + gitwgs_bufferL = ifnnewL + if (imono == 0) then + ftfree gitwgs_bufferR, 1 + gitwgs_bufferR = ifnnewR + endif + else + kdone = 1 + endif + + iresponse = 1 +complete: + xout iresponse, kdone +endop + + +#include "/twigs/transforms.udo" + + +instr twgs_undo + icbid = p4 + + istatus twgs_undo + if (istatus < 0) then + Sresponse = twgs_failresponse(icbid) + else + Sresponse = twgs_refreshviewresponse(icbid) + endif + + io_sendstring("callback", Sresponse) + turnoff +endin + + +instr twgs_stop + gitwgs_userstopped = 1 + turnoff2 "twgs_play", 0, 1 + turnoff +endin + +instr twgs_playcomplete_response + icbid = p4 + istatus = 0 + if (gitwgs_userstopped == 1) then + istatus = 3 + endif + io_sendstring("callback", sprintf("{\"cbid\":%d,\"status\":%d}", icbid, istatus)) + turnoff +endin + +instr twgs_playlag_response + icbid = p4 + turnoff2 "twgs_play", 0, 0 + io_sendstring("callback", sprintf("{\"cbid\":%d,\"status\":-1}", icbid)) + turnoff +endin + +instr twgs_play + icbid = p4 + iplaytype = p5 ; 0 = from analysis, 1 = from buffer + iselectedonly = p6 + istart = p7 + iend = p8 + iresynthtype = p9 ; 0 = pvsynth, 1 = pvsadsyn + gitwgs_userstopped = 0 + io_sendstring("callback", sprintf("{\"cbid\":%d,\"status\":1}", icbid)) + + ilen = tab_i(4, gitwgs_tpvfHandleL) + p3 = ilen * (iend - istart) + + if (iplaytype == 0) then + if (iselectedonly == 1) then + ibinselection = gitwgs_fnbinselection + else + ibinselection = -1 + endif + + istartframe = tpvf_framecount(gitwgs_tpvfHandleL) * istart + aL, k_ tpvf_resynth gitwgs_tpvfHandleL, ibinselection, iresynthtype, 0, istartframe + aL dcblock aL + if (gitwgs_channels == 2) then + aR, k_ tpvf_resynth gitwgs_tpvfHandleR, ibinselection, iresynthtype, istartframe + aR dcblock aR + else + aR = aL + endif + else + apos lphasor 1 + apos += (istart * ftlen(gitwgs_bufferL)) + aL table3 apos, gitwgs_bufferL + if (gitwgs_channels == 2) then + aR table3 apos, gitwgs_bufferR + else + aR = aL + endif + endif + + kpos linseg istart, p3, iend + chnset kpos, "twgs_playposratio" + + klagging lagdetect 1.1 + if (klagging == 1) then + schedulek("twgs_playlag_response", 0, 1, icbid) + endif + + kreleasing init 0 + ktimek timeinstk + iduration = (p3 * kr) ;+ (iextracycles / sr) + krelease release + if (kreleasing == 0 && (krelease == 1 || ktimek >= iduration)) then + kreleasing = 1 + schedulek("twgs_playcomplete_response", 0, 1, icbid) + turnoff + endif + outs aL, aR +endin + + +instr twgs_resynth_response + icbid = p4 + io_sendstring("callback", sprintf("{\"cbid\":%d,\"status\":1}", icbid)) + turnoff +endin + +instr twgs_resynth + icbid = p4 + Snext = strget(p5) + iresynthtype = p6 ; 0 = pvsynth, 1 = pvsadsyn + p3 = 3600 + io_sendstring("callback", sprintf("{\"cbid\":%d,\"status\":5}", icbid)) + twgs_clearbuffers(0, 1) + kdone init 0 + gitwgs_bufferL, kdoneL tpvf_resynth_offline gitwgs_tpvfHandleL, iresynthtype + if (gitwgs_channels == 2) then + gitwgs_bufferR, kdoneR tpvf_resynth_offline gitwgs_tpvfHandleR, iresynthtype + else + kdoneR init 1 + endif + if (kdoneL == 1 && kdoneR == 1) then + schedulek(Snext, 0, 1, icbid, 1) + turnoff + endif +endin + +instr twgs_getbuffers + icbid = p4 + if (gitwgs_channels == 2) then + Stables = sprintf("[%d,%d]", gitwgs_bufferL, gitwgs_bufferR) + else + Stables = sprintf("[%d]", gitwgs_bufferL) + endif + io_sendstring("callback", sprintf("{\"cbid\":%d,\"status\":1,\"tables\":%s}", icbid, Stables)) + turnoff +endin + + +instr twgs_savefile_response + icbid = p4 + Spath = p5 + io_sendstring("callback", sprintf("{\"cbid\":%d,\"status\":1,\"path\":\"%s\"}", icbid, Spath)) + turnoff +endin + + +instr twgs_savefile + icbid = p4 + Spath = strget(p5) + + ktimek timeinstk + idurations = ftlen(gitwgs_bufferL) / sr + ikcycles = idurations * kr + if (ktimek == 1) then + kcount init 0 + while (kcount < ikcycles) do + apos lphasor 1 + aL table apos, gitwgs_bufferR + if (gitwgs_channels == 1) then + fout Spath, 14, aL + else + aR table3 apos, gitwgs_bufferL + fout Spath, 14, aL, aR + endif + kcount += 1 + od + else + schedulek("twgs_savefile_response", 0, 1, icbid, Spath) + turnoff + endif +endin + + + +instr twgs_loadfilecomplete_response + icbid = p4 + ifftsize tab_i 0, gitwgs_tpvfHandleL + ifftdecimation tab_i 1, gitwgs_tpvfHandleL + ifnampL tab_i 2, gitwgs_tpvfHandleL + ifnfreqL tab_i 3, gitwgs_tpvfHandleL + iduration tab_i 4, gitwgs_tpvfHandleL + if (gitwgs_channels == 2) then + ifnampR tab_i 2, gitwgs_tpvfHandleR + ifnfreqR tab_i 3, gitwgs_tpvfHandleR + else + ifnampR = -1 + ifnfreqR = -1 + endif + + Sresponse = sprintf("{\"cbid\":%d,\"status\":1,\"channels\":%d,\"tables\":[%d,%d,%d,%d],\"bins\":%d,\"binseltab\":%d,\"bintimeseltab\":%d,\"duration\":%f,\"undolevel\":%d,\"fftdecim\":%d,\"sr\":%d}", icbid, gitwgs_channels, ifnampL, ifnfreqL, ifnampR, ifnfreqR, ifftsize / 2, gitwgs_fnbinselection, gitwgs_fnbintimeselection, iduration, gitwgs_checkpointstate, ifftdecimation, sr) + io_sendstring("callback", Sresponse) + turnoff +endin + +instr twgs_loadfile_analysis + icbid = p4 + ifftsize = p5 + ifftdecimation = p6 + + itablen = tpvf_tablen(ftlen(gitwgs_bufferL), ifftsize, ifftdecimation) + if (itablen >= gihost_max32bitftlen) then + io_sendstring("callback", twgs_failresponse(icbid, -2)) + turnoff + endif + + kdoneL, gitwgs_tpvfHandleL tpvf_analyse gitwgs_bufferL, ifftsize, ifftdecimation + ftfree gitwgs_bufferL, 1 + if (gitwgs_channels == 2) then + kdoneR, gitwgs_tpvfHandleR tpvf_analyse gitwgs_bufferR, ifftsize, ifftdecimation + ftfree gitwgs_bufferR, 1 + else + istatusR = 1 + kdoneR init 1 + endif + + if (gitwgs_fnbinselection > 0) then + ftfree gitwgs_fnbinselection, 0 + endif + gitwgs_fnbinselection ftgen 0, 0, -(ifftsize / 2), -2, 0 + + if (gitwgs_fnbintimeselection > 0) then + ftfree gitwgs_fnbintimeselection, 0 + endif + gitwgs_fnbintimeselection ftgen 0, 0, -ifftsize, -2, 0 + + if (kdoneL == 1 && kdoneR == 1) then + schedule("twgs_loadfilecomplete_response", 0, 1, icbid) + turnoff + endif +endin + + +instr twgs_loadftable + icbid = p4 + ifftsize = p5 + ifftdecimation = p6 + ifnL = p7 + ifnR = p8 + iclearaudiobuffers = p9 + + if (ifnL <= 0 || ftexists(ifnL) == 0 || (ifnR > 0 && ftexists(ifnR) == 0)) then + io_sendstring("callback", twgs_failresponse(icbid, -1)) + turnoff + endif + + twgs_clearbuffers(1, iclearaudiobuffers) + twgs_checkpoint_clear() + + gitwgs_bufferL = ifnL + if (ifnR > 0) then + gitwgs_bufferR = ifnR + gitwgs_channels = 2 + else + gitwgs_channels = 1 + endif + + schedule("twgs_loadfile_analysis", 0, 1, icbid, ifftsize, ifftdecimation) + turnoff +endin + +instr twgs_loadfile + icbid = p4 + Spath = strget(p5) + ifftsize = p6 + ifftdecimation = p7 + istatus, kdone twgs_loadfile Spath + if (istatus < 0) then + io_sendstring("callback", twgs_failresponse(icbid, istatus)) + turnoff + else + if (kdone == 1) then + schedulek("twgs_loadfile_analysis", 0, 1, icbid, ifftsize, ifftdecimation) + turnoff + endif + endif +endin + +#end -- cgit v1.2.3