Update Lua Script to use new MusicSample API
[ardour.git] / scripts / mixer_settings_recall.lua
1 ardour {
2         ["type"]    = "EditorAction",
3         name        = "Recall Mixer Settings",
4         author      = "Mixbus Team",
5         description = [[
6
7         Recalls mixer settings outined by files
8         created by Store Mixer Settings.
9
10         Allows for some room to change Source
11         and Destination.
12
13         ]]
14 }
15
16 function factory () 
17         
18         local acoraida_monicas_last_used_recall_file
19
20         return function ()
21
22         local user_cfg = ARDOUR.user_config_directory(-1)
23         local local_path = ARDOUR.LuaAPI.build_filename(Session:path(), 'mixer_settings')
24         local global_path = ARDOUR.LuaAPI.build_filename(user_cfg, 'mixer_settings')
25
26         local invalidate = {}
27
28         function exists(file)
29                 local ok, err, code = os.rename(file, file)
30                 if not ok then
31                         if code == 13 then -- Permission denied, but it exists
32                                 return true
33                         end
34                 end return ok, err
35         end
36
37         function whoami()
38                 if not pcall(function() local first_check = Session:get_mixbus(0) end) then
39                         return "Ardour"
40                 else
41                         local second_check = Session:get_mixbus(11)
42                         if second_check:isnil() then
43                                 return "Mixbus"
44                         else
45                                 return "32C"
46                         end
47                 end
48         end
49
50         function isdir(path)
51                 return exists(path.."/")
52         end
53
54         function get_processor_by_name(track, name)
55                 local i = 0
56                 local proc = track:nth_processor(i)
57                         repeat
58                                 if(proc:display_name() == name) then
59                                         return proc
60                                 else
61                                         i = i + 1
62                                 end
63                                 proc = track:nth_processor(i)
64                         until proc:isnil()
65                 end
66
67         function new_plugin(name, type)
68                 local plugin = ARDOUR.LuaAPI.new_plugin(Session, name, type, "")
69                 if not(plugin:isnil()) then return plugin end
70         end
71
72         function group_by_id(id)
73                 local id  = tonumber(id)
74                 for g in Session:route_groups():iter() do
75                         local group_id = tonumber(g:to_stateful():id():to_s())
76                         if group_id == id then return g end
77                 end
78         end
79
80         function group_by_name(name)
81                 for g in Session:route_groups():iter() do
82                         if g:name() == name then return g end
83                 end
84         end
85
86         function route_groupid_interrogate(t)
87                 local group = false
88                 for g in Session:route_groups():iter() do
89                         for r in g:route_list():iter() do
90                                 if r:name() == t:name() then group = g:to_stateful():id():to_s() end
91                         end
92                 end return group
93         end
94
95         function route_group_interrogate(t)
96                 for g in Session:route_groups():iter() do
97                         for r in g:route_list():iter() do
98                                 if r:name() == t:name() then return g end
99                         end
100                 end
101         end
102
103         function recall(debug, path, dry_run)
104                 local file = io.open(path, "r")
105                 assert(file, "File not found!")
106                 local bypass_routes = {}
107
108                 local i = 0
109                 for l in file:lines() do
110                         --print(i, l)
111
112                         local create_groups = dry_run["create_groups"]
113                         local skip_line = false
114
115                         local plugin, route, group = false, false, false
116                         local f = load(l)
117
118                         if debug then
119                                 --print('create_groups ' .. tostring(create_groups))
120                                 print(i, string.sub(l, 0, 29), f)
121                         end
122
123                         if f then f() end
124
125                         if instance["route_id"]  then route = true end
126                         if instance["plugin_id"] then plugin = true end
127                         if instance["group_id"]  then group = true end
128
129                         if group then
130                                 local g_id   = instance["group_id"]
131                                 local routes = instance["routes"]
132                                 local name   = instance["name"]
133                                 local group  = group_by_id(g_id)
134                                 if not(group) then
135                                         if create_groups then
136                                                 local group = Session:new_route_group(name)
137                                                 for _, v in pairs(routes) do
138                                                         local rt = Session:route_by_id(PBD.ID(v))
139                                                         if rt:isnil() then rt = Session:route_by_name(name) end
140                                                         if not(rt:isnil()) then group:add(rt) end
141                                                 end
142                                         end
143                                 end
144                         end
145
146                         if route then
147                                 local substitution = tonumber(dry_run["destination-"..i])
148                                 if substitution == 0 then
149                                         bypass_routes[#bypass_routes + 1] = instance["route_id"]
150                                         goto nextline
151                                 end
152
153                                 local old_order = ARDOUR.ProcessorList()
154                                 local route_id = instance["route_id"]
155                                 local r_id = PBD.ID(instance["route_id"])
156                                 local muted, soloed = instance["muted"], instance["soloed"]
157                                 local order = instance["order"]
158                                 local cache = instance["cache"]
159                                 local group = instance["group"]
160                                 local group_name = instance["group_name"]
161                                 local name  = instance["route_name"]
162                                 local gc, tc, pc = instance["gain_control"], instance["trim_control"], instance["pan_control"]
163
164                                 if not(substitution == instance["route_id"]) then
165                                         print('SUBSTITUTION FOR: ', name, substitution, Session:route_by_id(PBD.ID(substitution)):name())
166                                         --bypass_routes[#bypass_routes + 1] = route_id
167                                         was_subbed = true
168                                         r_id = PBD.ID(substitution)
169                                 end
170
171                                 local rt = Session:route_by_id(r_id)
172                                 if rt:isnil() then rt = Session:route_by_name(name) end
173                                 if rt:isnil() then goto nextline end
174
175                                 local cur_group_id = route_groupid_interrogate(rt)
176                                 if not(group) and (cur_group_id) then
177                                         local g = group_by_id(cur_group_id)
178                                         if g then g:remove(rt) end
179                                 end
180
181                                 local rt_group = group_by_name(group_name)
182                                 if rt_group then rt_group:add(rt) end
183
184                                 well_known = {'PRE', 'Trim', 'EQ', 'Comp', 'Fader', 'POST'}
185                                 protected_instrument = false
186                                 for k, v in pairs(order) do
187                                         local proc = Session:processor_by_id(PBD.ID(1))
188                                         if not(was_subbed) then
189                                                 proc = Session:processor_by_id(PBD.ID(v))
190                                         end
191                                         if proc:isnil() then
192                                                 for id, sub_tbl in pairs(cache) do
193                                                         local name = sub_tbl[1]
194                                                         local type = sub_tbl[2]
195                                                         if v == id then
196                                                                 proc = new_plugin(name, type)
197                                                                 for _, control in pairs(well_known) do
198                                                                         if name == control then
199                                                                                 proc = get_processor_by_name(rt, control)
200                                                                                 invalidate[v] = proc:to_stateful():id():to_s()
201                                                                                 goto nextproc
202                                                                         end
203                                                                 end
204                                                                 if not(proc) then goto nextproc end
205                                                                 if not(proc:isnil()) then
206                                                                         rt:add_processor_by_index(proc, 0, nil, true)
207                                                                         invalidate[v] = proc:to_stateful():id():to_s()
208                                                                 end
209                                                         end
210                                                 end
211                                         end
212                                         ::nextproc::
213                                         if proc and not(proc:isnil()) then old_order:push_back(proc) end
214                                         if not(old_order:empty()) and not(protected_instrument) then
215                                                 if not(rt:to_track():to_midi_track():isnil()) then
216                                                         if not(rt:the_instrument():isnil()) then
217                                                                 protected_instrument = true
218                                                                 old_order:push_back(rt:the_instrument())
219                                                         end
220                                                 end
221                                         end
222                                 end
223                                 rt:reorder_processors(old_order, nil)
224                                 if muted  then rt:mute_control():set_value(1, 1) else rt:mute_control():set_value(0, 1) end
225                                 if soloed then rt:solo_control():set_value(1, 1) else rt:solo_control():set_value(0, 1) end
226                                 rt:gain_control():set_value(gc, 1)
227                                 rt:trim_control():set_value(tc, 1)
228                                 if pc ~= false and not(rt:is_master()) then rt:pan_azimuth_control():set_value(pc, 1) end
229                         end
230
231                         if plugin then
232                                 --if the plugin is owned by a route
233                                 --we decided not to use, skip it
234                                 for _, v in pairs(bypass_routes) do
235                                         if instance["owned_by_route_id"] == v then
236                                                 goto nextline
237                                         end
238                                 end
239
240                                 local enable = {}
241                                 local params = instance["parameters"]
242                                 local p_id   = instance["plugin_id"]
243                                 local act    = instance["active"]
244
245                                 for k, v in pairs(invalidate) do --invalidate any deleted plugin's id
246                                         if p_id == k then
247                                                 p_id = v
248                                         end
249                                 end
250
251                                 local proc = Session:processor_by_id(PBD.ID(p_id))
252                                 if proc:isnil() then goto nextline end
253                                 local plug = proc:to_insert():plugin(0)
254
255                                 for k, v in pairs(params) do
256                                         local label = plug:parameter_label(k)
257                                         if string.find(label, "Assign") or string.find(label, "Enable") then --@ToDo: Check Plugin type == LADSPA or VST?
258                                                 enable[k] = v --queue any assignments/enables for after the initial parameter recalling to duck the 'in-on-change' feature
259                                         end
260                                         print(string.format("%s (Port: %s) -> %s", label, k, v))
261                                         ARDOUR.LuaAPI.set_processor_param(proc, k, v)
262                                 end
263
264                                 for k, v in pairs(enable) do
265                                         ARDOUR.LuaAPI.set_processor_param(proc, k, v)
266                                 end
267                                 if act then proc:activate() else proc:deactivate() end
268                         end
269
270                         ::nextline::
271                         i = i + 1
272
273                 end
274         end
275
276         function dry_run(debug, path)
277                 --returns a dialog-able table of
278                 --everything we do (logically)
279                 --in the recall function
280                 local route_values = {['----'] = "0"}
281                 for r in Session:get_routes():iter() do
282                         route_values[r:name()] =  r:to_stateful():id():to_s()
283                 end
284
285                 local i = 0
286                 local dry_table = {
287                         {type = "label", align="right", key="col-1-title", col=0, colspan=1, title = 'Source:'},
288                         {type = "label", align="left", key="col-2-title", col=1, colspan=1, title = 'Destination:'},
289                 }
290                 local file = io.open(path, "r")
291                 assert(file, "File not found!")
292                 pad = 0
293                 for l in file:lines() do
294                         local do_plugin, do_route, do_group = false, false, false
295                         local f = load(l)
296
297                         if debug then
298                                 print(i, string.sub(l, 0, 29), f)
299                         end
300
301                         if f then f() end
302
303                         if instance["route_id"]  then do_route = true end
304                         if instance["plugin_id"] then do_plugin = true end
305                         if instance["group_id"]  then do_group = true end
306
307                         if do_group then
308                                 local group_id   = instance["group_id"]
309                                 local group_name = instance["name"]
310                                 local dlg_title, action_title  = "", ""
311
312                                 local group_ptr  = group_by_id(group_id)
313
314                                 if not(group_ptr) then
315                                         dlg_title = string.format("(Group) %s.", group_name)
316                                         --action_title = "will create and use settings"
317                                 else
318                                         dlg_title = string.format("(Group) %s.", group_ptr:name())
319                                         --action_title = "will use group settings"
320                                 end
321                                 table.insert(dry_table, {
322                                         order=pad, type = "label", align="right", key =  "group-"..i , col = 0, colspan = 1, title = dlg_title
323                                 })
324                                 pad = pad + 1
325                         end
326
327                         if do_route then
328                                 local route_id   = instance["route_id"]
329                                 local route_name = instance["route_name"]
330                                 local dlg_title = ""
331
332                                 local route_ptr = Session:route_by_id(PBD.ID(route_id))
333
334                                 if route_ptr:isnil() then
335                                         route_ptr = Session:route_by_name(route_name)
336                                         if not(route_ptr:isnil()) then
337                                                 dlg_title = string.format("%s", route_ptr:name())
338                                                 --action_title = "will use route settings"
339                                         else
340                                                 dlg_title = string.format("%s", route_name)
341                                                 --action_title = "will be ignored"
342                                         end
343                                 else
344                                         dlg_title = string.format("%s", route_ptr:name())
345                                         --action_title = "will use route settings"
346                                 end
347                                 if route_ptr:isnil() then name = route_name else name = route_ptr:name() end
348
349                                 table.insert(dry_table, {
350                                         order=instance['pi_order']+pad, type = "label",    align="right", key = "route-"..i , col = 0, colspan = 1, title = dlg_title
351                                 })
352                                 table.insert(dry_table, {
353                                         type = "dropdown", align="left", key = "destination-"..i, col = 1, colspan = 1, title = "", values = route_values, default = name or "----"
354                                 })
355                         end
356                         i = i + 1
357                 end
358                 table.insert(dry_table, {
359                         type = "checkbox", col=0, colspan=2, align="left",  key = "create_groups", default = true, title = "Create Groups if necessary?"
360                 })
361                 return dry_table
362         end
363
364         local global_vs_local_dlg = {
365                 { type = "label", col=0, colspan=20, align="left", title = "" },
366                 {
367                         type = "radio", col=0, colspan=20, align="left", key = "recall-dir", title = "", values =
368                         {
369                                 ['Pick from Global Settings'] = 1, ['Pick from Local Settings'] = 2, ['Last Used Recall File'] = 3,
370                         },
371                         default = 'Last Used Recall File'
372                 },
373                 { type = "label", col=0, colspan=20, align="left", title = ""},
374         }
375
376         local recall_options = {
377                 { type = "label", col=0, colspan=10, align="left", title = "" },
378                 { type = "file",  col=0, colspan=15, align="left", key = "file", title = "Select a Settings File",  path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", "params.lua") },
379                 { type = "label", col=0, colspan=10, align="left", title = "" },
380         }
381
382         local gvld = LuaDialog.Dialog("Recall Mixer Settings:", global_vs_local_dlg):run()
383
384         if not(gvld) then
385                 return
386         else
387                 if gvld['recall-dir'] == 1 then
388                         local global_ok = isdir(global_path)
389                         local global_default_path = ARDOUR.LuaAPI.build_filename(global_path, string.format("FactoryDefault-%s.lua", whoami()))
390                         print(global_default_path)
391                         if global_ok then
392                                 recall_options[2]['path'] = global_default_path
393                                 local rv = LuaDialog.Dialog("Recall Mixer Settings:", recall_options):run()
394                                 if not(rv) then return end
395                                 local dry_return = LuaDialog.Dialog("Recall Mixer Settings:", dry_run(false, rv['file'])):run()
396                                 if dry_return then
397                                         acoraida_monicas_last_used_recall_file = rv['file']
398                                         recall(false, rv['file'], dry_return)
399                                 else
400                                         return
401                                 end
402                         else
403                                 LuaDialog.Message ("Recall Mixer Settings:",
404                                         global_path .. ' does not exist!\nPlease run Store Mixer Settings first.',
405                                         LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run()
406                         end
407                 end
408
409                 if gvld['recall-dir'] == 2 then
410                         local local_ok = isdir(local_path)
411                         local local_default_path = ARDOUR.LuaAPI.build_filename(local_path, 'asdf')
412                         print(local_default_path)
413                         if local_ok then
414                                 recall_options[2]['path'] = local_default_path
415                                 local rv = LuaDialog.Dialog("Recall Mixer Settings:", recall_options):run()
416                                 if not(rv) then return end
417                                 local dry_return = LuaDialog.Dialog("Recall Mixer Settings:", dry_run(false, rv['file'])):run()
418                                 if dry_return then
419                                         acoraida_monicas_last_used_recall_file = rv['file']
420                                         recall(true, rv['file'], dry_return)
421                                 else
422                                         return
423                                 end
424                         else
425                                 LuaDialog.Message ("Recall Mixer Settings:",
426                                         local_path .. 'does not exist!\nPlease run Store Mixer Settings first.',
427                                         LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run()
428                         end
429                 end
430
431                 if gvld['recall-dir'] == 3 then
432                         if acoraida_monicas_last_used_recall_file then
433                                 local dry_return = LuaDialog.Dialog("Recall Mixer Settings:", dry_run(false, acoraida_monicas_last_used_recall_file)):run()
434                                 if dry_return then
435                                         recall(true, acoraida_monicas_last_used_recall_file, dry_return)
436                                 else
437                                         return
438                                 end
439                         else
440                                 LuaDialog.Message ("Script has no record of last used file:",
441                                         'Please pick a recall file and then this option will be available',
442                                         LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run()
443                         end
444                 end
445         end
446
447 end end