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