1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
|
#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
|