#ifndef UDO_MELSEQUENCINGPERSIST #define UDO_MELSEQUENCINGPERSIST ## /* Melodic sequencer persistence: saving/loading from files and database Requires JSON opcodes This file is part of the SONICS UDO collection by Richard Knight 2021, 2022 License: GPL-2.0-or-later http://1bpm.net */ #include "pgdb.udo" #include "sequencing_melodic.udo" #include "array_tools.udo" #include "interop.udo" /* Get the current state as a JSON object iJson mel_getstate_json iJson the JSON object containing current sequencing and progression data */ opcode mel_getstate_json, i, 0 iJson jsoninit index = 0 while (index < lenarray(gimel_fns)) do iarray[] tab2array gimel_fns[index] jsoninsertval iJson, gSmel_names[index], iarray index += 1 od iarray[] tab2array gimel_state jsoninsertval iJson, "state", iarray jsoninsertval iJson, "details", gSmel_details jsoninsertval iJson, "mel_number", gimel_number jsoninsertval iJson, "seq_tempo", i(gkseq_tempo) jsoninsertval iJson, "seq_swing", i(gkseq_swing) xout iJson endop /* Set the current sequencing and progression state mel_setstate_json iJson iJson JSON object containing state data */ opcode mel_setstate_json, 0, i iJson xin gSmel_details jsongetval iJson, "details" gimel_number jsongetval iJson, "mel_number" itempo jsongetval iJson, "seq_tempo" iswing jsongetval iJson, "seq_swing" #ifdef MEL_INITTIME gkseq_tempo init itempo gkseq_swing init iswing #end ; if data to be loaded has more progression items than ftables, free and generate again if (gimel_number > ftlen(gimel_fns[0])) then index = 0 while (index < lenarray(gimel_fns)) do ftfree gimel_fns[index], 0 gimel_fns[index] = ftgen(0, 0, -gimel_number, -7, 0) od endif index = 0 while (index < lenarray(gimel_fns)) do iJsonSub jsonget iJson, gSmel_names[index] iarray[] jsonarrval iJsonSub copya2ftab iarray, gimel_fns[index] jsondestroy iJsonSub index += 1 od iJsonSub jsonget iJson, "state" iarray[] jsonarrval iJsonSub copya2ftab iarray, gimel_state jsondestroy(iJsonSub) endop /* Save the sequencing and progression state to a file p4 path to save file to p5 optional callback ID for host interop; sent on completion */ instr mel_savestate_fs Spath = p4 icbid = p5 iJson = mel_getstate_json() jsondump(iJson, Spath) jsondestroy(iJson) if (icbid != 0) then schedule("io_callback", 0, 1, icbid) endif turnoff endin opcode mel_loadstate_fs, 0, S Spath xin iJson jsonload Spath mel_setstate_json(iJson) jsondestroy(iJson) endop /* Load the sequencing and progression state from a file p4 path to load data from p5 optional callback ID for host interop; sent on completion */ instr mel_loadstate_fs Spath = p4 icbid = p5 mel_loadstate_fs Spath if (icbid != 0) then schedule("io_callback", 0, 1, icbid) endif schedule("mel_futures_refresh", 0, 1) turnoff endin opcode mel_savestate_db, 0, S Sname xin iJson = mel_getstate_json() pgdb_json_save Sname, "melsys", iJson jsondestroy(iJson) endop /* Save the sequencing and progression state to database p4 reference name in database p5 optional callback ID for host interop; sent on completion */ instr mel_savestate_db Sname = p4 icbid = p5 mel_savestate_db Sname if (icbid != 0) then schedule("io_callback", 0, 1, icbid) endif turnoff endin opcode mel_loadstate_db, 0, S Sname xin iJson pgdb_json_load Sname, "melsys" mel_setstate_json(iJson) jsondestroy(iJson) endop /* Load the sequencing and progression state from database p4 reference name in database p5 optional callback ID for host interop; sent on completion */ instr mel_loadstate_db Sname = p4 icbid = p5 mel_loadstate_db Sname if (icbid != 0) then schedule("io_callback", 0, 1, icbid) endif schedule("mel_futures_refresh", 0, 1) turnoff endin /* Load the sequencing and progression state from a string channel p4 channel name containing string representation of JSON p5 optional callback ID for host interop; sent on completion */ instr mel_loadstate_channel Schannel = p4 icbid = p5 Sdata chnget Schannel iJson jsonloads Sdata mel_setstate_json(iJson) jsondestroy(iJson) if (icbid != 0) then schedule("io_callback", 0, 1, icbid) endif schedule("mel_futures_refresh", 0, 1) turnoff endin /* Just get state: interop host handles persistence p4 callback ID to send data with/to */ instr mel_getstate_string icbid = p4 iJson = mel_getstate_json() jsoninsertval iJson, "cbid", icbid Sjson = jsondumps(iJson, 0) io_sendstring("callback", Sjson) turnoff endin /* Get an array of the known mel states from database Sdata[] mel_liststates_db Sdata[] the state names */ opcode mel_liststates_db, S[], 0 Sresult[][] dbarray gidb, "SELECT name FROM savejson WHERE unit = 'melsys'" ilen = lenarray(Sresult) Sdata[] init ilen index = 0 while (index < ilen) do Sdata[index] = Sresult[index][0] index += 1 od xout Sdata endop /* Get a list of mel states from database and return to host with the specified callback ID p4 callback ID */ instr mel_liststates_db icbid = p4 iJson = jsoninit() jsoninsertval(iJson, "cbid", icbid) jsoninsertval(iJson, "states", mel_liststates_db()) io_sendstring("callback", jsondumps(iJson, 0)) turnoff endin ; if MEL_INITPATH or MEL_INITDB is set, load the specified progression data accordingly #ifdef MEL_HASINIT instr _mel_persistence_init #ifdef MEL_INITPATH subinstrinit "mel_loadstate_fs", "$MEL_INITPATH" #end #ifdef MEL_INITDB ;mel_loadstate_db "$MEL_INITDB" subinstrinit "mel_loadstate_db", "$MEL_INITDB" #end alwayson "_mel_manager" turnoff endin schedule "_mel_persistence_init", 0, 60 ; end MEL_HASINIT #end #end