#ifndef UDO_SCSS_PERSISTENCE #define UDO_SCSS_PERSISTENCE ## /* SONICS Category Sequencer System Persistence: saving and loading state to database and FS Designed for use with an API host to/from which callbacks and JSON states can be exchanged Requires JSON opcodes https://git.1bpm.net/csound-json This file is part of the SONICS UDO collection by Richard Knight 2022 License: GPL-2.0-or-later http://1bpm.net */ #include "sequencing_melodic_persistence.udo" #include "table_tools.udo" #include "pgdb.udo" opcode scss_json2tab, 0, ii iJson, ifn xin if (jsontype(iJson) == 4) then iarray[] jsonarrval iJson copya2ftab iarray, ifn endif endop opcode scss_json2tab, 0, iiS iJsonTop, ifn, Skey xin iJson = jsonget(iJsonTop, Skey) scss_json2tab(iJson, ifn) jsondestroy(iJson) endop opcode scss_tabarr2json, 0, iSi[][] iJson, Srelname, ifns[][] xin iJarrTop = jsonloads("[]") index = 0 while (index < lenarray(ifns, 1)) do iJarrSub = jsonloads("[]") index2 = 0 while (index2 < lenarray(ifns, 2)) do iJtable = jsonloads(tab_serialise(ifns[index][index2])) jsonptradd iJarrSub, sprintf("/%d", jsonsize(iJarrSub)), iJtable index2 += 1 od index += 1 jsonptradd iJarrTop, sprintf("/%d", jsonsize(iJarrTop)), iJarrSub jsondestroy(iJarrSub) od jsoninsert iJson, Srelname, iJarrTop jsondestroy(iJarrTop) endop opcode scss_json2tabarr, i[][], iSi[][] iJsonTop, Srelname, ifns[][] xin iJson = jsonget(iJsonTop, Srelname) isize = jsonsize(iJson) index = 0 while (index < isize) do iJsonSub = jsonget(iJson, index) isize2 = jsonsize(iJsonSub) index2 = 0 while (index2 < isize2) do iJtable = jsonget(iJsonSub, index2) iarray[] jsonarrval iJsonSub copya2ftab iarray, ifns[index][index2] ;tab_unserialise(jsondumps(iJtable, 0), ifns[index][index2]) jsondestroy(iJtable) index2 += 1 od jsondestroy(iJsonSub) index += 1 od jsondestroy(iJson) xout ifns endop /* Set parameters based on a JSON object with key as channel name and value as channel value to be set scss_setparamstate iJsonInput iJsonInput JSON object of channel values */ opcode scss_setparamstate, 0, i iJsonInput xin Skeys[] jsonkeys iJsonInput index = 0 while (index < lenarray(Skeys)) do Sparam = Skeys[index] chnset jsongetval:i(iJsonInput, Sparam), Sparam index += 1 od endop /* Get parameters from a SCSS definition JSON object and append current values to iJsonOutput using channel prefix Sprefix _scss_getparams iJsonInput, iJsonOutput, Sprefix iJsonInput object to evaluate; should have an array with key "parameters" featuring objects which have a key "name" specifying parameter name iJsonOutput output object to add values to (key is channel name, value is current value) Sprefix parameter prefix to use for channel name */ opcode _scss_getparams, 0, iiS iJsonInput, iJsonOutput, Sprefix xin SparameterPointer = "/parameters" if (jsonptrhas(iJsonInput, SparameterPointer) == 1) then iJsonParameters = jsonptr(iJsonInput, SparameterPointer) iparamsize = jsonsize(iJsonParameters) indexparam = 0 while (indexparam < iparamsize) do SparamName jsonptrval iJsonParameters, sprintf("/%d/name", indexparam) Sparam = sprintf("%s_%s", Sprefix, SparamName) jsoninsertval iJsonOutput, Sparam, chnget:i(Sparam) indexparam += 1 od jsondestroy(iJsonParameters) endif endop /* Get all registered parameters in giscss_stateJson, and get current channel values, returning an object with key as channel name and value as channel value iJsonOutput scss_getparamstate iJsonOutput JSON object of channel values */ opcode scss_getparamstate, i, 0 iJsonOutput = jsoninit() ; global parameters _scss_getparams(giscss_stateJson, iJsonOutput, "scss") ; categories iJsonCategories = jsonptr(giscss_stateJson, "/categories") icatnum = jsonsize(iJsonCategories) jsondestroy(iJsonCategories) index = 0 while (index < icatnum) do iJsonCategory = jsonptr(giscss_stateJson, sprintf("/categories/%d", index)) Scategory jsonptrval iJsonCategory, "/name" ; category parameters _scss_getparams(iJsonCategory, iJsonOutput, Scategory) ; items/instruments iJsonItems = jsonptr(iJsonCategory, "/items") iinstrnum = jsonsize(iJsonItems) indexItem = 0 while (indexItem < iinstrnum) do iJsonItem = jsonptr(iJsonItems, sprintf("/%d", indexItem)) Sinstrument = jsongetval(iJsonItem, "name") _scss_getparams(iJsonItem, iJsonOutput, Sinstrument) jsondestroy(iJsonItem) indexItem += 1 od jsondestroy(iJsonItems) jsondestroy(iJsonCategory) index += 1 od xout iJsonOutput endop /* Get sequencing state as JSON object iJsonSeq scss_getseqstate iJsonSeq object of sequencing data */ opcode scss_getseqstate, i, 0 iJsonSeq = jsoninit() scss_tabarr2json(iJsonSeq, "giscss_stfn_trig", giscss_stfn_trig) scss_tabarr2json(iJsonSeq, "giscss_stfn_dur", giscss_stfn_dur) scss_tabarr2json(iJsonSeq, "giscss_stfn_params", giscss_stfn_params) Skeys[] fillarray "giscss_st_size", "giscss_st_slots", "giscss_st_paramnumber" ; giscss_stfn_temp", "giscss_stfn_param_temp" ivalues[] fillarray giscss_st_size, giscss_st_slots, giscss_st_paramnumber ; giscss_stfn_temp giscss_stfn_param_temp jsoninsertval(iJsonSeq, Skeys, ivalues) xout iJsonSeq endop /* Set sequencing state from JSON object scss_setseqstate iJsonSeq iJsonSeq object of sequencing data */ opcode scss_setseqstate, 0, i iJsonSeq xin giscss_stfn_trig scss_json2tabarr iJsonSeq, "giscss_stfn_trig", giscss_stfn_trig giscss_stfn_dur scss_json2tabarr iJsonSeq, "giscss_stfn_dur", giscss_stfn_dur giscss_stfn_params scss_json2tabarr iJsonSeq, "giscss_stfn_params", giscss_stfn_params giscss_st_size = jsongetval:i(iJsonSeq, "giscss_st_size") giscss_st_slots = jsongetval:i(iJsonSeq, "giscss_st_slots") giscss_st_paramnumber = jsongetval:i(iJsonSeq, "giscss_st_paramnumber") endop opcode scss_getstate, i, ppppp igetsequencing, igetparameters, igetmelstate, igetinstrstate, igetcatenabled xin iJson = jsoninit() if (igetsequencing == 1) then iJsonSequencing = scss_getseqstate() jsoninsert(iJson, "sequencing", iJsonSequencing) jsondestroy(iJsonSequencing) endif if (igetparameters == 1) then iJsonParameters = scss_getparamstate() jsoninsert(iJson, "parameters", iJsonParameters) jsondestroy(iJsonParameters) endif if (igetmelstate == 1) then iJsonMelstate = mel_getstate_json() jsoninsert(iJson, "melstate", iJsonMelstate) jsondestroy(iJsonMelstate) endif if (igetinstrstate == 1) then iJinstrState = jsonloads(tab_serialise(giscss_instrState)) jsoninsert(iJson, "giscss_instrState", iJinstrState) jsondestroy(iJinstrState) endif if (igetcatenabled == 1) then iJcatEnabled = jsonloads(tab_serialise(giscss_catEnabled)) jsoninsert(iJson, "giscss_catEnabled", iJcatEnabled) jsondestroy(iJcatEnabled) endif xout iJson endop opcode scss_setstate, 0, ippppp iJson, isetsequencing, isetparameters, isetmelstate, isetinstrstate, isetcatenabled xin if (isetsequencing == 1 && jsonptrhas(iJson, "/sequencing") == 1) then iJsonSequencing = jsonget(iJson, "sequencing") scss_setseqstate(iJsonSequencing) jsondestroy(iJsonSequencing) endif if (isetparameters == 1 && jsonptrhas(iJson, "/parameters") == 1) then iJsonParameters = jsonget(iJson, "parameters") scss_setparamstate(iJsonParameters) jsondestroy(iJsonParameters) endif if (isetmelstate == 1 && jsonptrhas(iJson, "/melstate") == 1) then iJsonMelstate = jsonget(iJson, "melstate") mel_setstate_json(iJsonMelstate) jsondestroy(iJsonMelstate) endif if (isetinstrstate == 1 && jsonptrhas(iJson, "/giscss_instrState") == 1) then iJinstrState = jsonget(iJson, "giscss_instrState") endif if (isetcatenabled == 1 && jsonptrhas(iJson, "/giscss_catEnabled") == 1) then iJcatEnabled = jsonget(iJson, "giscss_catEnabled") endif endop /* Get the current values of all registered parameters, returning callback ID to host and an object with key "parameters" containing keys as the channel names and values as the channel values p4 callback ID */ instr scss_getparamstate icbid = p4 iJson = jsoninit() iJsonState = scss_getparamstate() jsoninsert iJson, "parameters", iJsonState jsoninsertval iJson, "cbid", icbid jsoninsertval iJson, "status", "complete" io_sendstring("callback", jsondumps(iJson, 0)) jsondestroy(iJsonState) jsondestroy(iJson) turnoff endin /* Save state to database p4 callback ID p5 reference name of the state to save; blank is accepted and saves as SCSS_NAME */ instr scss_savestate_db icbid = p4 Sreference = strcat("$SCSS_NAME||", strget(p5)) ; save state values iJsonState = scss_getstate() Squery = sprintf("DELETE FROM savejson WHERE name = '%s' AND unit = 'scss_state'; INSERT INTO savejson (name, data, unit, created) VALUES ('%s', '%s', 'scss_state', current_timestamp)",\ Sreference, Sreference, jsondumps(iJsonState, 0)\ ) dbexec gidb, Squery jsondestroy(iJsonState) ; return callback ID to host iJson = jsoninit() jsoninsertval iJson, "cbid", icbid jsoninsertval iJson, "status", "complete" io_sendstring("callback", jsondumps(iJson, 0)) turnoff endin /* Load parameter state from database, returning JSON to host as in the instrument scss_getparamstate p4 callback ID p5 reference name of the state to load; blank is accepted and loads as SCSS_NAME */ instr scss_loadstate_db icbid = p4 Sreference = strcat("$SCSS_NAME||", strget(p5)) ; load parameter values Squery = sprintf("SELECT data::text FROM savejson WHERE unit = 'scss_state' AND name = '%s'", Sreference) Sresult dbscalar gidb, Squery iJson = jsonloads(Sresult) scss_setstate(iJson) jsondestroy(iJson) ; returns state data and callback ID to host schedule("scss_getparamstate", 0, 1, icbid) turnoff endin #end