#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