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