better group support + mute and solo recall
[ardour.git] / scripts / _store_recall_mixer.lua
1 ardour {
2         ["type"] = "EditorAction",
3         name = "Mixer Store",
4         author = "Ardour Lua Taskforce",
5         description = [[Stores the current Mixer state as a file that can be read and recalled arbitrarily.
6         Supports: processor settings, grouping, mute, solo, gain, trim, pan and processor ordering, plus re-adding certain deleted plugins.]]
7 }
8
9 function factory() return function()
10
11         function new_plugin(name)
12                 local plugin = nil
13                 for x = 0, 6 do
14                         plugin = ARDOUR.LuaAPI.new_plugin(Session, name, x, "")
15                         if not(plugin:isnil()) then break end
16                 end return plugin
17         end
18
19         function group_by_id(id)
20                 local group = nil
21                 local id  = tonumber(id)
22                 for g in Session:route_groups():iter() do
23                         local group_id = tonumber(g:to_stateful():id():to_s())
24                         if group_id == id then group = g end
25                 end return group
26         end
27
28         function route_group_interrogate(t)
29                 local group = false
30                 for g in Session:route_groups():iter() do
31                         for r in g:route_list():iter() do
32                                 if r:name() == t:name() then group = g:to_stateful():id():to_s() end
33                         end
34                 end return group
35         end
36
37         local path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", "params.lua")
38         function mark()
39
40                 local file = io.open(path, "w")
41                 file:write("") --empty current file from last run
42                 file:close()
43
44                 local g_route_str, group_str = "", ""
45                 local i = 0
46                 for g in Session:route_groups():iter() do --@ToDo: Color, and other bools
47                         group_str = "instance = {group_id = " .. g:to_stateful():id():to_s() .. ", name = " .. "\"" .. g:name() .. "\"" .. ", routes = {"
48                         for t in g:route_list():iter() do
49                                 g_route_str = g_route_str .."[".. i .."] = " .. t:to_stateful():id():to_s() .. ","
50                                 i = i + 1
51                         end
52                         group_str = group_str .. g_route_str .. "}}"
53                 end
54
55                 if not(group_str == "") then --sometimes there are no groups in the session
56                         file = io.open(path, "a")
57                         file:write(group_str, "\r\n")
58                         file:close()
59                 end
60
61                 for r in Session:get_routes():iter() do
62                         if r:is_monitor () or r:is_auditioner () then goto nextroute end -- skip special routes
63
64                         local order = ARDOUR.ProcessorList()
65                         local x = 0
66                         repeat
67                                 local proc = r:nth_processor(x)
68                                 if not proc:isnil() then
69                                         order:push_back(proc)
70                                 end
71                                 x = x + 1
72                         until proc:isnil()
73
74                         local route_str, proc_order_str, cache_str = "", "", ""
75                         local rid = r:to_stateful():id():to_s()
76                         local pan = r:pan_azimuth_control()
77                         if pan:isnil() then pan = false else pan = pan:get_value() end --sometimes a route doesn't have pan, like the master.
78
79                         local on = 0
80                         for p in order:iter() do
81                                 local pid = p:to_stateful():id():to_s()
82                                 if not(string.find(p:display_name(), "latcomp")) then
83                                         proc_order_str = proc_order_str .. "[" .. on .. "] = " .. pid ..","
84                                         cache_str = cache_str .. "[" .. pid .. "] = " .. "\"" .. p:display_name() .. "\"" ..","
85                                 end
86                                 on = on + 1
87                         end
88
89                         route_str = "instance = {route_id = " .. rid .. ", gain_control = " .. r:gain_control():get_value() .. ", trim_control = " .. r:trim_control():get_value() .. ", pan_control = " .. tostring(pan) .. ", muted = " .. tostring(r:muted()) .. ", soloed = " .. tostring(r:soloed()) .. ", order = {" .. proc_order_str .."}, cache = {" .. cache_str .. "}, group = " .. tostring(route_group_interrogate(r))  .. "}"
90                         file = io.open(path, "a")
91                         file:write(route_str, "\r\n")
92                         file:close()
93
94                         local i = 0
95                         while true do
96                                 local params = {}
97                                 local proc_str, params_str = "", ""
98                                 local proc = r:nth_plugin (i)
99                                 if proc:isnil () then break end
100                                 local active = proc:active()
101                                 local id = proc:to_stateful():id():to_s()
102                                 local plug = proc:to_insert ():plugin (0)
103                                 local n = 0 -- count control-ports
104                                 for j = 0, plug:parameter_count () - 1 do -- iterate over all plugin parameters
105                                         if plug:parameter_is_control (j) then
106                                                 local label = plug:parameter_label (j)
107                                                 if plug:parameter_is_input (j) and label ~= "hidden" and label:sub (1,1) ~= "#" then
108                                                         local _, _, pd = ARDOUR.LuaAPI.plugin_automation(proc, n)
109                                                         local val = ARDOUR.LuaAPI.get_processor_param(proc, j, true)
110                                                         print(proc:display_name(), label, val)
111                                                         params[n] = val
112                                                 end
113                                                 n = n + 1
114                                         end
115                                 end
116                                 i = i + 1
117                                 for k, v in pairs(params) do
118                                         params_str = params_str .. "[".. k .."] = " .. v .. ","
119                                 end
120                                 proc_str = "instance = {plugin_id = " .. id .. ", parameters = {" .. params_str .. "}, active = " .. tostring(active) .. "}"
121                                 file = io.open(path, "a")
122                                 file:write(proc_str, "\r\n")
123                                 file:close()
124                         end
125                         ::nextroute::
126                 end
127         end
128         local invalidate = {}
129         function recall()
130                 local file = io.open(path, "r")
131                 assert(file, "File not found!")
132                 for l in file:lines() do
133                         --print(l)
134
135                         local plugin, route, group = false, false, false
136                         local f = load(l)
137                         f ()
138
139                         if instance["route_id"]  ~= nil then route = true end
140                         if instance["plugin_id"] ~= nil then plugin = true end
141                         if instance["group_id"]  ~= nil then group = true end
142
143                         if group then
144                                 local g_id = instance["group_id"]
145                                 local routes = instance["routes"]
146                                 local name = instance["name"]
147                                 local group = group_by_id(g_id)
148                                 if group == nil then group = Session:new_route_group(name) end
149                                 for k, v in pairs(routes) do
150                                         local rt = Session:route_by_id(PBD.ID(v))
151                                         if not(rt:isnil()) then group:add(rt) end
152                                 end
153                         end
154
155                         if route then
156
157                                 local old_order = ARDOUR.ProcessorList()
158                                 local r_id = PBD.ID(instance["route_id"])
159                                 local muted, soloed = instance["muted"], instance["soloed"]
160                                 local order = instance["order"]
161                                 local cache = instance["cache"]
162                                 local group = instance["group"]
163                                 local gc, tc, pc = instance["gain_control"], instance["trim_control"], instance["pan_control"]
164
165                                 local rt = Session:route_by_id(r_id)
166                                 if rt:isnil() then goto nextline end
167                                 local cur_group_id = route_group_interrogate(rt)
168                                 if not(group) and (cur_group_id ~= false) then
169                                         local g = group_by_id(cur_group_id)
170                                         if g ~= nil then g:remove(rt) end
171                                 end
172
173                                 for k, v in pairs(order) do
174                                         local proc = Session:processor_by_id(PBD.ID(v))
175                                         if proc:isnil() then
176                                                 for id, name in pairs(cache) do
177                                                         if v == id then
178                                                                 proc = new_plugin(name)
179                                                                 if not(proc:isnil()) then
180                                                                         rt:add_processor_by_index(proc, 0, nil, true)
181                                                                         invalidate[v] = proc:to_stateful():id():to_s()
182                                                                         old_order:push_back(proc)
183                                                                 end
184                                                         end
185                                                 end
186                                         end
187                                 end
188
189                                 if muted  then rt:mute_control():set_value(1, 1) else rt:mute_control():set_value(0, 1) end
190                                 if soloed then rt:solo_control():set_value(1, 1) else rt:solo_control():set_value(0, 1) end
191                                 rt:gain_control():set_value(gc, 1)
192                                 rt:trim_control():set_value(tc, 1)
193                                 if pc ~= false then rt:pan_azimuth_control():set_value(pc, 1) end
194                                 rt:reorder_processors(old_order, nil)
195                         end
196
197                         if plugin then
198                                 local enable = {}
199                                 local params = instance["parameters"]
200                                 local p_id   = instance["plugin_id"]
201                                 local act = instance["active"]
202
203                                 for k, v in pairs(invalidate) do --invalidate any deleted plugin's id
204                                         if p_id == k then
205                                                 p_id = v
206                                         end
207                                 end
208
209                                 local proc = Session:processor_by_id(PBD.ID(p_id))
210                                 if proc:isnil() then goto nextline end
211                                 local plug = proc:to_insert():plugin(0)
212
213                                 for k, v in pairs(params) do
214                                         local label = plug:parameter_label(k)
215                                         if string.find(label, "Assign") or string.find(label, "Enable") then --@ToDo: Check Plugin type == LADSPA or VST?
216                                                 enable[k] = v --queue any assignments/enables for after the initial parameter recalling to duck the 'in-on-change' feature
217                                         end
218                                         ARDOUR.LuaAPI.set_processor_param(proc, k, v)
219                                 end
220
221                                 for k, v in pairs(enable) do
222                                         ARDOUR.LuaAPI.set_processor_param(proc, k, v)
223                                 end
224                                 if act then proc:activate() else proc:deactivate() end
225                         end
226                         ::nextline::
227                 end
228         end
229
230         local dialog_options = {
231                 { type = "label", colspan= 10, title = "" },
232                 { type = "radio",  colspan= 10, key = "select", title = "", values ={ ["1. Mark"] = "mark", ["2. Recall"] = "recall" }, default = "1. Mark"},
233                 { type = "label", colspan= 10, title = "" },
234         }
235
236         local rv = LuaDialog.Dialog("Mixer Store:", dialog_options):run()
237         assert(rv, 'Dialog box was canceled or is ' .. type(rv))
238         local c = rv["select"]
239         if c == "mark" then mark() end
240         if c == "recall" then recall() end
241
242 end end