1 /*
2  * Copyright (C) 2016-2017 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2016-2019 Robin Gareus <robin@gareus.org>
4  * Copyright (C) 2016 Julien "_FrnchFrgg_" RIVAUD <frnchfrgg@free.fr>
5  * Copyright (C) 2016 Tim Mayberry <mojofunk@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include <glib.h>
23 #include <glibmm/miscutils.h>
24 #include <glibmm/fileutils.h>
25 
26 #include "pbd/gstdio_compat.h"
27 #include "pbd/pthread_utils.h"
28 
29 #include "ardour/audio_buffer.h"
30 #include "ardour/buffer_set.h"
31 #include "ardour/filesystem_paths.h"
32 #include "ardour/luabindings.h"
33 #include "ardour/luaproc.h"
34 #include "ardour/luascripting.h"
35 #include "ardour/midi_buffer.h"
36 #include "ardour/plugin.h"
37 #include "ardour/session.h"
38 
39 #include "LuaBridge/LuaBridge.h"
40 
41 #include "pbd/i18n.h"
42 
43 using namespace ARDOUR;
44 using namespace PBD;
45 
LuaProc(AudioEngine & engine,Session & session,const std::string & script)46 LuaProc::LuaProc (AudioEngine& engine,
47                   Session& session,
48                   const std::string &script)
49 	: Plugin (engine, session)
50 	, _mempool ("LuaProc", 3145728)
51 #ifdef USE_TLSF
52 	, lua (lua_newstate (&PBD::TLSF::lalloc, &_mempool))
53 #elif defined USE_MALLOC
54 	, lua ()
55 #else
56 	, lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
57 #endif
58 	, _lua_dsp (0)
59 	, _lua_latency (0)
60 	, _script (script)
61 	, _lua_does_channelmapping (false)
62 	, _lua_has_inline_display (false)
63 	, _connect_all_audio_outputs (false)
64 	, _designated_bypass_port (UINT32_MAX)
65 	, _signal_latency (0)
66 	, _control_data (0)
67 	, _shadow_data (0)
68 	, _configured (false)
69 	, _has_midi_input (false)
70 	, _has_midi_output (false)
71 {
72 	init ();
73 
74 	/* when loading a session, or pasing a processor,
75 	 * the script is set during set_state();
76 	 */
77 	if (!_script.empty () && load_script ()) {
78 		throw failed_constructor ();
79 	}
80 }
81 
LuaProc(const LuaProc & other)82 LuaProc::LuaProc (const LuaProc &other)
83 	: Plugin (other)
84 	, _mempool ("LuaProc", 3145728)
85 #ifdef USE_TLSF
86 	, lua (lua_newstate (&PBD::TLSF::lalloc, &_mempool))
87 #elif defined USE_MALLOC
88 	, lua ()
89 #else
90 	, lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
91 #endif
92 	, _lua_dsp (0)
93 	, _lua_latency (0)
94 	, _script (other.script ())
95 	, _origin (other._origin)
96 	, _lua_does_channelmapping (false)
97 	, _lua_has_inline_display (false)
98 	, _designated_bypass_port (UINT32_MAX)
99 	, _signal_latency (0)
100 	, _control_data (0)
101 	, _shadow_data (0)
102 	, _configured (false)
103 	, _has_midi_input (false)
104 	, _has_midi_output (false)
105 {
106 	init ();
107 
108 	if (load_script ()) {
109 		throw failed_constructor ();
110 	}
111 
112 	for (uint32_t i = 0; i < parameter_count (); ++i) {
113 		_control_data[i] = other._shadow_data[i];
114 		_shadow_data[i]  = other._shadow_data[i];
115 	}
116 }
117 
~LuaProc()118 LuaProc::~LuaProc () {
119 #ifdef WITH_LUAPROC_STATS
120 	if (_info && _stats_cnt > 0) {
121 		printf ("LuaProc: '%s' run()  avg: %.3f  max: %.3f [ms] p: %.1f\n",
122 				_info->name.c_str (),
123 				0.0001f * _stats_avg[0] / (float) _stats_cnt,
124 				0.0001f * _stats_max[0],
125 				_stats_max[0] * (float)_stats_cnt / _stats_avg[0]);
126 		printf ("LuaProc: '%s' gc()   avg: %.3f  max: %.3f [ms] p: %.1f\n",
127 				_info->name.c_str (),
128 				0.0001f * _stats_avg[1] / (float) _stats_cnt,
129 				0.0001f * _stats_max[1],
130 				_stats_max[1] * (float)_stats_cnt / _stats_avg[1]);
131 	}
132 #endif
133 	lua.collect_garbage ();
134 	delete (_lua_dsp);
135 	delete (_lua_latency);
136 	delete [] _control_data;
137 	delete [] _shadow_data;
138 }
139 
140 void
init()141 LuaProc::init ()
142 {
143 #ifdef WITH_LUAPROC_STATS
144 	_stats_avg[0] = _stats_avg[1] = _stats_max[0] = _stats_max[1] = 0;
145 	_stats_cnt = -25;
146 #endif
147 
148 	lua.Print.connect (sigc::mem_fun (*this, &LuaProc::lua_print));
149 	// register session object
150 	lua_State* L = lua.getState ();
151 	lua_mlock (L, 1);
152 	LuaBindings::stddef (L);
153 	LuaBindings::common (L);
154 	LuaBindings::dsp (L);
155 
156 	luabridge::getGlobalNamespace (L)
157 		.beginNamespace ("Ardour")
158 		.deriveClass <LuaProc, PBD::StatefulDestructible> ("LuaProc")
159 		.addFunction ("queue_draw", &LuaProc::queue_draw)
160 		.addFunction ("shmem", &LuaProc::instance_shm)
161 		.addFunction ("table", &LuaProc::instance_ref)
162 		.addFunction ("route", &LuaProc::route)
163 		.addFunction ("unique_id", &LuaProc::unique_id)
164 		.addFunction ("name", &LuaProc::name)
165 		.endClass ()
166 		.endNamespace ();
167 	lua_mlock (L, 0);
168 
169 	// add session to global lua namespace
170 	luabridge::push <Session *> (L, &_session);
171 	lua_setglobal (L, "Session");
172 
173 	// instance
174 	luabridge::push <LuaProc *> (L, this);
175 	lua_setglobal (L, "self");
176 
177 	// sandbox
178 	lua.sandbox (true);
179 #if 0
180 	lua.do_command ("for n in pairs(_G) do print(n) end print ('----')"); // print global env
181 #endif
182 	lua.do_command ("function ardour () end");
183 }
184 
185 void
drop_references()186 LuaProc::drop_references ()
187 {
188 	lua.collect_garbage ();
189 	Plugin::drop_references ();
190 }
191 
192 boost::weak_ptr<Route>
route() const193 LuaProc::route () const
194 {
195 	if (!_owner) {
196 		return boost::weak_ptr<Route>();
197 	}
198 	return static_cast<Route*>(_owner)->weakroute ();
199 }
200 
201 void
lua_print(std::string s)202 LuaProc::lua_print (std::string s) {
203 #ifndef NDEBUG
204 	std::cout << "LuaProc: " << s << "\n";
205 #endif
206 	PBD::info << "LuaProc: " << s << "\n";
207 }
208 
209 bool
load_script()210 LuaProc::load_script ()
211 {
212 	if (_script.empty ()) {
213 		return true;
214 	}
215 	assert (!_lua_dsp); // don't allow to re-initialize
216 	LuaPluginInfoPtr lpi;
217 
218 	// TODO: refine APIs; function arguments..
219 	// - perform channel-map in ardour (silent/scratch buffers) ?
220 	// - control-port API (explicit get/set functions ??)
221 	// - latency reporting (global var? ctrl-port? set-function ?)
222 	// - MIDI -> sparse table of events
223 	//     { [sample] => { Event }, .. }
224 	//   or  { { sample, Event }, .. }
225 
226 	try {
227 		LuaScriptInfoPtr lsi = LuaScripting::script_info (_script);
228 		lpi = LuaPluginInfoPtr (new LuaPluginInfo (lsi));
229 		assert (lpi);
230 		set_info (lpi);
231 		_mempool.set_name ("LuaProc: " + lsi->name);
232 		_docs = lsi->description;
233 	} catch (failed_constructor& err) {
234 		return true;
235 	}
236 
237 	lua_State* L = lua.getState ();
238 	lua.do_command (_script);
239 
240 	// check if script has a DSP callback
241 	luabridge::LuaRef lua_dsp_run = luabridge::getGlobal (L, "dsp_run");
242 	luabridge::LuaRef lua_dsp_map = luabridge::getGlobal (L, "dsp_runmap");
243 
244 	if ((lua_dsp_run.type () != LUA_TFUNCTION) == (lua_dsp_map.type () != LUA_TFUNCTION)) {
245 		return true;
246 	}
247 
248 	if (lua_dsp_run.type () == LUA_TFUNCTION) {
249 		_lua_dsp = new luabridge::LuaRef (lua_dsp_run);
250 	}
251 	else if (lua_dsp_map.type () == LUA_TFUNCTION) {
252 		_lua_dsp = new luabridge::LuaRef (lua_dsp_map);
253 		_lua_does_channelmapping = true;
254 	}
255 	else {
256 		assert (0);
257 		return true;
258 	}
259 
260 	luabridge::LuaRef lua_dsp_latency = luabridge::getGlobal (L, "dsp_latency");
261 	if (lua_dsp_latency.type () == LUA_TFUNCTION) {
262 		_lua_latency = new luabridge::LuaRef (lua_dsp_latency);
263 	}
264 
265 	/* parse I/O options */
266 	luabridge::LuaRef ioconfig = luabridge::getGlobal (L, "dsp_ioconfig");
267 	if (ioconfig.isFunction ()) {
268 		try {
269 			luabridge::LuaRef iotable = ioconfig ();
270 			if (iotable.isTable ()) {
271 				for (luabridge::Iterator i (iotable); !i.isNil (); ++i) {
272 					if (!i.key().isString()) {
273 						continue;
274 					}
275 					if (i.key().cast<std::string> () == "connect_all_audio_outputs" && i.value().isBoolean ()) {
276 						_connect_all_audio_outputs = i.value().cast<bool> ();
277 					}
278 				}
279 			}
280 		} catch (...) {
281 			return true;
282 		}
283 	}
284 
285 	// initialize the DSP if needed
286 	luabridge::LuaRef lua_dsp_init = luabridge::getGlobal (L, "dsp_init");
287 	if (lua_dsp_init.type () == LUA_TFUNCTION) {
288 		try {
289 			lua_dsp_init (_session.nominal_sample_rate ());
290 		} catch (luabridge::LuaException const& e) {
291 #ifndef NDEBUG
292 			std::cerr << "LuaException:" << e.what () << std::endl;
293 #endif
294 			PBD::warning << "LuaException: " << e.what () << endmsg;
295 			return true; // error
296 		} catch (...) {
297 			return true;
298 		}
299 	}
300 
301 	_ctrl_params.clear ();
302 
303 	luabridge::LuaRef lua_render = luabridge::getGlobal (L, "render_inline");
304 	if (lua_render.isFunction ()) {
305 		_lua_has_inline_display = true;
306 	}
307 
308 	luabridge::LuaRef lua_params = luabridge::getGlobal (L, "dsp_params");
309 	if (lua_params.isFunction ()) {
310 
311 		// call function // add try {} catch (luabridge::LuaException const& e)
312 		luabridge::LuaRef params = lua_params ();
313 
314 		if (params.isTable ()) {
315 
316 			for (luabridge::Iterator i (params); !i.isNil (); ++i) {
317 				// required fields
318 				if (!i.key ().isNumber ())           { return true; }
319 				if (!i.value ().isTable ())          { return true; }
320 				if (!i.value ()["type"].isString ()) { return true; }
321 				if (!i.value ()["name"].isString ()) { return true; }
322 				if (!i.value ()["min"].isNumber ())  { return true; }
323 				if (!i.value ()["max"].isNumber ())  { return true; }
324 
325 				int pn = i.key ().cast<int> ();
326 				std::string type = i.value ()["type"].cast<std::string> ();
327 				if (type == "input") {
328 					if (!i.value ()["default"].isNumber ()) {
329 						return true; // error
330 					}
331 					_ctrl_params.push_back (std::make_pair (false, pn));
332 				}
333 				else if (type == "output") {
334 					_ctrl_params.push_back (std::make_pair (true, pn));
335 				} else {
336 					return true; // error
337 				}
338 				assert (pn == (int) _ctrl_params.size ());
339 
340 				//_param_desc[pn] = boost::shared_ptr<ParameterDescriptor> (new ParameterDescriptor());
341 				luabridge::LuaRef lr = i.value ();
342 
343 				if (type == "input") {
344 					_param_desc[pn].normal     = lr["default"].cast<float> ();
345 				} else {
346 					_param_desc[pn].normal     = lr["min"].cast<float> (); // output-port, no default
347 				}
348 				_param_desc[pn].lower        = lr["min"].cast<float> ();
349 				_param_desc[pn].upper        = lr["max"].cast<float> ();
350 				_param_desc[pn].toggled      = lr["toggled"].isBoolean () && (lr["toggled"]).cast<bool> ();
351 				_param_desc[pn].logarithmic  = lr["logarithmic"].isBoolean () && (lr["logarithmic"]).cast<bool> ();
352 				_param_desc[pn].integer_step = lr["integer"].isBoolean () && (lr["integer"]).cast<bool> ();
353 				_param_desc[pn].sr_dependent = lr["ratemult"].isBoolean () && (lr["ratemult"]).cast<bool> ();
354 				_param_desc[pn].enumeration  = lr["enum"].isBoolean () && (lr["enum"]).cast<bool> ();
355 
356 				if (lr["bypass"].isBoolean () && (lr["bypass"]).cast<bool> ()) {
357 					_designated_bypass_port = pn - 1; // lua table starts at 1.
358 				}
359 
360 				if (lr["unit"].isString ()) {
361 					std::string unit = lr["unit"].cast<std::string> ();
362 					if (unit == "dB")             { _param_desc[pn].unit = ParameterDescriptor::DB; }
363 					else if (unit == "Hz")        { _param_desc[pn].unit = ParameterDescriptor::HZ; }
364 					else if (unit == "Midi Note") { _param_desc[pn].unit = ParameterDescriptor::MIDI_NOTE; }
365 				}
366 				_param_desc[pn].label        = (lr["name"]).cast<std::string> ();
367 				_param_desc[pn].scale_points = parse_scale_points (&lr);
368 
369 				luabridge::LuaRef doc = lr["doc"];
370 				if (doc.isString ()) {
371 					_param_doc[pn] = doc.cast<std::string> ();
372 				} else {
373 					_param_doc[pn] = "";
374 				}
375 				assert (!(_param_desc[pn].toggled && _param_desc[pn].logarithmic));
376 			}
377 		}
378 	}
379 
380 	_control_data = new float[parameter_count ()];
381 	_shadow_data  = new float[parameter_count ()];
382 
383 	for (uint32_t i = 0; i < parameter_count (); ++i) {
384 		if (parameter_is_input (i)) {
385 			_control_data[i] = _shadow_data[i] = default_value (i);
386 		}
387 	}
388 
389 	// expose ctrl-ports to global lua namespace
390 	luabridge::push <float *> (L, _control_data);
391 	lua_setglobal (L, "CtrlPorts");
392 
393 	return false; // no error
394 }
395 
396 bool
match_variable_io(ChanCount & in,ChanCount & aux_in,ChanCount & out)397 LuaProc::match_variable_io (ChanCount& in, ChanCount& aux_in, ChanCount& out)
398 {
399 	/* Lua does not have dedicated sidechain busses */
400 	in += aux_in;
401 
402 	/* caller must hold process lock (no concurrent calls to interpreter */
403 	_output_configs.clear ();
404 
405 	lua_State* L = lua.getState ();
406 	luabridge::LuaRef ioconfig = luabridge::getGlobal (L, "dsp_ioconfig");
407 
408 	luabridge::LuaRef *_iotable = NULL; // can't use reference :(
409 
410 	if (ioconfig.isFunction ()) {
411 		try {
412 			luabridge::LuaRef iotable = ioconfig ();
413 			if (iotable.isTable ()) {
414 				_iotable = new luabridge::LuaRef (iotable);
415 			}
416 		} catch (luabridge::LuaException const& e) {
417 			_iotable = NULL;
418 		} catch (...) {
419 			_iotable = NULL;
420 		}
421 	}
422 
423 	if (!_iotable) {
424 		/* empty table as default */
425 		luabridge::LuaRef iotable = luabridge::newTable(L);
426 		_iotable = new luabridge::LuaRef (iotable);
427 	}
428 
429 	// now we can reference it.
430 	luabridge::LuaRef iotable (*_iotable);
431 	delete _iotable;
432 
433 	if ((iotable).length () < 1) {
434 		/* empty table as only config, to get default values */
435 		luabridge::LuaRef ioconf = luabridge::newTable(L);
436 		iotable[1] = ioconf;
437 	}
438 
439 	const int audio_in = in.n_audio ();
440 	const int midi_in = in.n_midi ();
441 
442 	// preferred setting (provided by plugin_insert)
443 	const int preferred_out = out.n_audio ();
444 	const int preferred_midiout = out.n_midi ();
445 
446 	int midi_out = -1;
447 	int audio_out = -1;
448 	float penalty = 9999;
449 	bool found = false;
450 
451 #define FOUNDCFG_PENALTY(n_in, n_out, p) {     \
452   _output_configs.insert (n_out);              \
453   if (p < penalty) {                           \
454     audio_out = (n_out);                       \
455     midi_out = possible_midiout;               \
456     in.set (DataType::AUDIO, (n_in));          \
457     in.set (DataType::MIDI, possible_midiin);  \
458     _has_midi_input = (possible_midiin > 0);   \
459     _has_midi_output = (possible_midiout > 0); \
460     penalty = p;                               \
461     found = true;                              \
462   }                                            \
463 }
464 
465 #define FOUNDCFG_IMPRECISE(n_in, n_out) {                                  \
466   const float p = fabsf ((float)(n_out) - preferred_out) *                 \
467                       (((n_out) > preferred_out) ? 1.1 : 1)                \
468                 + fabsf ((float)possible_midiout - preferred_midiout) *    \
469                       ((possible_midiout - preferred_midiout) ? 0.6 : 0.5) \
470                 + fabsf ((float)(n_in) - audio_in) *                       \
471                       (((n_in) > audio_in) ? 275 : 250)                    \
472                 + fabsf ((float)possible_midiin - midi_in) *               \
473                       ((possible_midiin - midi_in) ? 100 : 110);           \
474   FOUNDCFG_PENALTY(n_in, n_out, p);                                        \
475 }
476 
477 #define FOUNDCFG(n_out)              \
478   FOUNDCFG_IMPRECISE(audio_in, n_out)
479 
480 #define ANYTHINGGOES          \
481   _output_configs.insert (0);
482 
483 #define UPTO(nch) {               \
484   for (int n = 1; n < nch; ++n) { \
485     _output_configs.insert (n);   \
486   }                               \
487 }
488 
489 	for (luabridge::Iterator i (iotable); !i.isNil (); ++i) {
490 		luabridge::LuaRef io (i.value ());
491 		if (!io.isTable()) {
492 			continue;
493 		}
494 
495 		int possible_in = io["audio_in"].isNumber() ? io["audio_in"] : -1;
496 		int possible_out = io["audio_out"].isNumber() ? io["audio_out"] : -1;
497 		int possible_midiin = io["midi_in"].isNumber() ? io["midi_in"] : 0;
498 		int possible_midiout = io["midi_out"].isNumber() ? io["midi_out"] : 0;
499 
500 		// exact match
501 		if ((possible_in == audio_in) && (possible_out == preferred_out)) {
502 			/* Set penalty so low that this output configuration
503 			 * will trump any other one */
504 			FOUNDCFG_PENALTY(audio_in, preferred_out, -1);
505 		}
506 
507 		if (possible_out == 0 && possible_midiout == 0) {
508 			/* skip configurations with no output at all */
509 			continue;
510 		}
511 
512 		if (possible_in == -1 || possible_in == -2) {
513 			/* wildcard for input */
514 			if (possible_out == possible_in) {
515 				/* either both -1 or both -2 (invalid and
516 				 * interpreted as both -1): out must match in */
517 				FOUNDCFG (audio_in);
518 			} else if (possible_out == -3 - possible_in) {
519 				/* one is -1, the other is -2: any output configuration
520 				 * possible, pick what the insert prefers */
521 				FOUNDCFG (preferred_out);
522 				ANYTHINGGOES;
523 			} else if (possible_out < -2) {
524 				/* variable number of outputs up to -N,
525 				 * invalid if in == -2 but we accept it anyway */
526 				FOUNDCFG (std::min (-possible_out, preferred_out));
527 				UPTO (-possible_out)
528 			} else {
529 				/* exact number of outputs */
530 				FOUNDCFG (possible_out);
531 			}
532 		}
533 
534 		if (possible_in < -2 || possible_in >= 0) {
535 			/* specified number, exact or up to */
536 			int desired_in;
537 			if (possible_in >= 0) {
538 				/* configuration can only match possible_in */
539 				desired_in = possible_in;
540 			} else {
541 				/* configuration can match up to -possible_in */
542 				desired_in = std::min (-possible_in, audio_in);
543 			}
544 			if (possible_out == -1 || possible_out == -2) {
545 				/* any output configuration possible
546 				 * out == -2 is invalid, interpreted as out == -1.
547 				 * Really imprecise only if desired_in != audio_in */
548 				FOUNDCFG_IMPRECISE (desired_in, preferred_out);
549 				ANYTHINGGOES;
550 			} else if (possible_out < -2) {
551 				/* variable number of outputs up to -N
552 				 * not specified if in > 0, but we accept it anyway.
553 				 * Really imprecise only if desired_in != audio_in */
554 				FOUNDCFG_IMPRECISE (desired_in, std::min (-possible_out, preferred_out));
555 				UPTO (-possible_out)
556 			} else {
557 				/* exact number of outputs
558 				 * Really imprecise only if desired_in != audio_in */
559 				FOUNDCFG_IMPRECISE (desired_in, possible_out);
560 			}
561 		}
562 
563 	}
564 
565 	if (!found) {
566 		return false;
567 	}
568 
569 	out.set (DataType::MIDI, midi_out);
570 	out.set (DataType::AUDIO, audio_out);
571 
572 	_selected_in = in; // TODO remove after testing
573 	_selected_out = out;
574 
575 	/* restore side-chain input count */
576 	in -= aux_in;
577 
578 	return true;
579 }
580 
581 bool
reconfigure_io(ChanCount in,ChanCount aux_in,ChanCount out)582 LuaProc::reconfigure_io (ChanCount in, ChanCount aux_in, ChanCount out)
583 {
584 	in += aux_in;
585 	assert (in == _selected_in && out ==_selected_out);
586 
587 	in.set (DataType::MIDI, _has_midi_input ? 1 : 0);
588 	out.set (DataType::MIDI, _has_midi_output ? 1 : 0);
589 
590 	_info->n_inputs = in;
591 	_info->n_outputs = out;
592 
593 	// configure the DSP if needed
594 	if (in != _configured_in || out != _configured_out || !_configured) {
595 		lua_State* L = lua.getState ();
596 		luabridge::LuaRef lua_dsp_configure = luabridge::getGlobal (L, "dsp_configure");
597 		if (lua_dsp_configure.type () == LUA_TFUNCTION) {
598 			try {
599 				luabridge::LuaRef io = lua_dsp_configure (in, out);
600 				if (io.isTable ()) {
601 					ChanCount lin (in);
602 					ChanCount lout (out);
603 
604 					if (io["audio_in"].type() == LUA_TNUMBER) {
605 						const int c = io["audio_in"].cast<int> ();
606 						if (c >= 0) {
607 							lin.set (DataType::AUDIO, c);
608 						}
609 					}
610 					if (io["audio_out"].type() == LUA_TNUMBER) {
611 						const int c = io["audio_out"].cast<int> ();
612 						if (c >= 0) {
613 							lout.set (DataType::AUDIO, c);
614 						}
615 					}
616 					if (io["midi_in"].type() == LUA_TNUMBER) {
617 						const int c = io["midi_in"].cast<int> ();
618 						if (c >= 0) {
619 							lin.set (DataType::MIDI, c);
620 						}
621 					}
622 					if (io["midi_out"].type() == LUA_TNUMBER) {
623 						const int c = io["midi_out"].cast<int> ();
624 						if (c >= 0) {
625 							lout.set (DataType::MIDI, c);
626 						}
627 					}
628 					_info->n_inputs = lin;
629 					_info->n_outputs = lout;
630 				}
631 				_configured = true;
632 			} catch (luabridge::LuaException const& e) {
633 #ifndef NDEBUG
634 				std::cerr << "LuaException: " << e.what () << "\n";
635 #endif
636 				PBD::warning << "LuaException: " << e.what () << "\n";
637 				return false;
638 			} catch (...) {
639 				return false;
640 			}
641 		}
642 	}
643 
644 	_configured_in = in;
645 	_configured_out = out;
646 
647 	return true;
648 }
649 
650 int
connect_and_run(BufferSet & bufs,samplepos_t start,samplepos_t end,double speed,ChanMapping const & in,ChanMapping const & out,pframes_t nframes,samplecnt_t offset)651 LuaProc::connect_and_run (BufferSet& bufs,
652 		samplepos_t start, samplepos_t end, double speed,
653 		ChanMapping const& in, ChanMapping const& out,
654 		pframes_t nframes, samplecnt_t offset)
655 {
656 	if (!_lua_dsp) {
657 		return 0;
658 	}
659 
660 	Plugin::connect_and_run (bufs, start, end, speed, in, out, nframes, offset);
661 
662 	// This is needed for ARDOUR::Session requests :(
663 	assert (SessionEvent::has_per_thread_pool ());
664 
665 	uint32_t const n = parameter_count ();
666 	for (uint32_t i = 0; i < n; ++i) {
667 		if (parameter_is_control (i) && parameter_is_input (i)) {
668 			_control_data[i] = _shadow_data[i];
669 		}
670 	}
671 
672 #ifdef WITH_LUAPROC_STATS
673 	int64_t t0 = g_get_monotonic_time ();
674 #endif
675 
676 	try {
677 		if (_lua_does_channelmapping) {
678 			// run the DSP function
679 			(*_lua_dsp)(&bufs, &in, &out, nframes, offset);
680 		} else {
681 			// map buffers
682 			BufferSet& silent_bufs  = _session.get_silent_buffers (ChanCount (DataType::AUDIO, 1));
683 			BufferSet& scratch_bufs = _session.get_scratch_buffers (ChanCount (DataType::AUDIO, 1));
684 
685 			lua_State* L = lua.getState ();
686 			luabridge::LuaRef in_map (luabridge::newTable (L));
687 			luabridge::LuaRef out_map (luabridge::newTable (L));
688 
689 			const uint32_t audio_in = _configured_in.n_audio ();
690 			const uint32_t audio_out = _configured_out.n_audio ();
691 			const uint32_t midi_in = _configured_in.n_midi ();
692 
693 			for (uint32_t ap = 0; ap < audio_in; ++ap) {
694 				bool valid;
695 				const uint32_t buf_index = in.get(DataType::AUDIO, ap, &valid);
696 				if (valid) {
697 					in_map[ap + 1] = bufs.get_audio (buf_index).data (offset);
698 				} else {
699 					in_map[ap + 1] = silent_bufs.get_audio (0).data (offset);
700 				}
701 			}
702 			for (uint32_t ap = 0; ap < audio_out; ++ap) {
703 				bool valid;
704 				const uint32_t buf_index = out.get(DataType::AUDIO, ap, &valid);
705 				if (valid) {
706 					out_map[ap + 1] = bufs.get_audio (buf_index).data (offset);
707 				} else {
708 					out_map[ap + 1] = scratch_bufs.get_audio (0).data (offset);
709 				}
710 			}
711 
712 			luabridge::LuaRef lua_midi_src_tbl (luabridge::newTable (L));
713 			int e = 1; // > 1 port, we merge events (unsorted)
714 			for (uint32_t mp = 0; mp < midi_in; ++mp) {
715 				bool valid;
716 				const uint32_t idx = in.get(DataType::MIDI, mp, &valid);
717 				if (valid) {
718 					for (MidiBuffer::iterator m = bufs.get_midi(idx).begin();
719 							m != bufs.get_midi(idx).end(); ++m, ++e) {
720 						const Evoral::Event<samplepos_t> ev(*m, false);
721 						luabridge::LuaRef lua_midi_data (luabridge::newTable (L));
722 						const uint8_t* data = ev.buffer();
723 						for (uint32_t i = 0; i < ev.size(); ++i) {
724 							lua_midi_data [i + 1] = data[i];
725 						}
726 						luabridge::LuaRef lua_midi_event (luabridge::newTable (L));
727 						lua_midi_event["time"] = 1 + (*m).time();
728 						lua_midi_event["data"] = lua_midi_data;
729 						lua_midi_event["bytes"] = data;
730 						lua_midi_event["size"] = ev.size();
731 						lua_midi_src_tbl[e] = lua_midi_event;
732 					}
733 				}
734 			}
735 
736 			if (_has_midi_input) {
737 				// XXX TODO This needs a better solution than global namespace
738 				luabridge::push (L, lua_midi_src_tbl);
739 				lua_setglobal (L, "midiin");
740 			}
741 
742 			luabridge::LuaRef lua_midi_sink_tbl (luabridge::newTable (L));
743 			if (_has_midi_output) {
744 				luabridge::push (L, lua_midi_sink_tbl);
745 				lua_setglobal (L, "midiout");
746 			}
747 
748 			// run the DSP function
749 			(*_lua_dsp)(in_map, out_map, nframes);
750 
751 			// copy back midi events
752 			if (_has_midi_output && lua_midi_sink_tbl.isTable ()) {
753 				bool valid;
754 				const uint32_t idx = out.get(DataType::MIDI, 0, &valid);
755 				if (valid && bufs.count().n_midi() > idx) {
756 					MidiBuffer& mbuf = bufs.get_midi(idx);
757 					mbuf.silence(0, 0);
758 					for (luabridge::Iterator i (lua_midi_sink_tbl); !i.isNil (); ++i) {
759 						if (!i.key ().isNumber ()) { continue; }
760 						if (!i.value ()["time"].isNumber ()) { continue; }
761 						if (!i.value ()["data"].isTable ()) { continue; }
762 						luabridge::LuaRef data_tbl (i.value ()["data"]);
763 						samplepos_t tme = i.value ()["time"];
764 						if (tme < 1 || tme > nframes) { continue; }
765 						uint8_t data[64];
766 						size_t size = 0;
767 						for (luabridge::Iterator di (data_tbl); !di.isNil () && size < sizeof(data); ++di, ++size) {
768 							data[size] = di.value ();
769 						}
770 						if (size > 0 && size < 64) {
771 							mbuf.push_back(tme - 1, Evoral::MIDI_EVENT, size, data);
772 						}
773 					}
774 
775 				}
776 			}
777 		}
778 
779 		if (_lua_latency) {
780 			_signal_latency = (*_lua_latency)();
781 		}
782 
783 	} catch (luabridge::LuaException const& e) {
784 #ifndef NDEBUG
785 		std::cerr << "LuaException: " << e.what () << "\n";
786 #endif
787 		PBD::warning << "LuaException: " << e.what () << "\n";
788 		return -1;
789 	} catch (...) {
790 		return -1;
791 	}
792 #ifdef WITH_LUAPROC_STATS
793 	int64_t t1 = g_get_monotonic_time ();
794 #endif
795 
796 	lua.collect_garbage_step ();
797 #ifdef WITH_LUAPROC_STATS
798 	if (++_stats_cnt > 0) {
799 		int64_t t2 = g_get_monotonic_time ();
800 		int64_t ela0 = t1 - t0;
801 		int64_t ela1 = t2 - t1;
802 		if (ela0 > _stats_max[0]) _stats_max[0] = ela0;
803 		if (ela1 > _stats_max[1]) _stats_max[1] = ela1;
804 		_stats_avg[0] += ela0;
805 		_stats_avg[1] += ela1;
806 	}
807 #endif
808 	return 0;
809 }
810 
811 
812 void
add_state(XMLNode * root) const813 LuaProc::add_state (XMLNode* root) const
814 {
815 	XMLNode*    child;
816 
817 	gchar* b64 = g_base64_encode ((const guchar*)_script.c_str (), _script.size ());
818 	std::string b64s (b64);
819 	g_free (b64);
820 	XMLNode* script_node = new XMLNode (X_("script"));
821 	script_node->set_property (X_("lua"), LUA_VERSION);
822 	script_node->set_property (X_("origin"), _origin);
823 	script_node->add_content (b64s);
824 	root->add_child_nocopy (*script_node);
825 
826 	for (uint32_t i = 0; i < parameter_count(); ++i) {
827 		if (parameter_is_input(i) && parameter_is_control(i)) {
828 			child = new XMLNode("Port");
829 			child->set_property("id", i);
830 			child->set_property("value", _shadow_data[i]);
831 			root->add_child_nocopy(*child);
832 		}
833 	}
834 }
835 
836 int
set_script_from_state(const XMLNode & node)837 LuaProc::set_script_from_state (const XMLNode& node)
838 {
839 	XMLNode* child;
840 	if (node.name () != state_node_name ()) {
841 		return -1;
842 	}
843 
844 	if ((child = node.child (X_("script"))) != 0) {
845 		XMLProperty const* prop;
846 		if ((prop = node.property ("origin")) != 0) {
847 			_origin = prop->value();
848 		}
849 		for (XMLNodeList::const_iterator n = child->children ().begin (); n != child->children ().end (); ++n) {
850 			if (!(*n)->is_content ()) { continue; }
851 			gsize size;
852 			guchar* buf = g_base64_decode ((*n)->content ().c_str (), &size);
853 			_script = std::string ((const char*)buf, size);
854 			g_free (buf);
855 			if (load_script ()) {
856 				PBD::error << _("Failed to load Lua script from session state.") << endmsg;
857 #ifndef NDEBUG
858 				std::cerr << "Failed Lua Script: " << _script << std::endl;
859 #endif
860 				_script = "";
861 			}
862 			break;
863 		}
864 	}
865 	if (_script.empty ()) {
866 		PBD::error << _("Session State for LuaProcessor did not include a Lua script.") << endmsg;
867 		return -1;
868 	}
869 	if (!_lua_dsp) {
870 		PBD::error << _("Invalid/incompatible Lua script found for LuaProcessor.") << endmsg;
871 		return -1;
872 	}
873 	return 0;
874 }
875 
876 int
set_state(const XMLNode & node,int version)877 LuaProc::set_state (const XMLNode& node, int version)
878 {
879 	XMLNodeList nodes;
880 	XMLNodeConstIterator iter;
881 	XMLNode *child;
882 
883 	if (_script.empty ()) {
884 		if (set_script_from_state (node)) {
885 			return -1;
886 		}
887 	}
888 
889 	if (node.name() != state_node_name()) {
890 		error << _("Bad node sent to LuaProc::set_state") << endmsg;
891 		return -1;
892 	}
893 
894 	nodes = node.children ("Port");
895 	for (iter = nodes.begin(); iter != nodes.end(); ++iter) {
896 		child = *iter;
897 
898 		uint32_t port_id;
899 		float value;
900 
901 		if (!child->get_property("id", port_id)) {
902 			warning << _("LuaProc: port has no symbol, ignored") << endmsg;
903 			continue;
904 		}
905 
906 		if (!child->get_property("value", value)) {
907 			warning << _("LuaProc: port has no value, ignored") << endmsg;
908 			continue;
909 		}
910 
911 		set_parameter (port_id, value, 0);
912 	}
913 
914 	return Plugin::set_state (node, version);
915 }
916 
917 uint32_t
parameter_count() const918 LuaProc::parameter_count () const
919 {
920 	return _ctrl_params.size ();
921 }
922 
923 float
default_value(uint32_t port)924 LuaProc::default_value (uint32_t port)
925 {
926 	if (_ctrl_params[port].first) {
927 		assert (0);
928 		return 0;
929 	}
930 	int lp = _ctrl_params[port].second;
931 	return _param_desc[lp].normal;
932 }
933 
934 void
set_parameter(uint32_t port,float val,sampleoffset_t when)935 LuaProc::set_parameter (uint32_t port, float val, sampleoffset_t when)
936 {
937 	assert (port < parameter_count ());
938 	if (get_parameter (port) == val) {
939 		return;
940 	}
941 	_shadow_data[port] = val;
942 	Plugin::set_parameter (port, val, when);
943 }
944 
945 float
get_parameter(uint32_t port) const946 LuaProc::get_parameter (uint32_t port) const
947 {
948 	if (parameter_is_input (port)) {
949 		return _shadow_data[port];
950 	} else {
951 		return _control_data[port];
952 	}
953 }
954 
955 int
get_parameter_descriptor(uint32_t port,ParameterDescriptor & desc) const956 LuaProc::get_parameter_descriptor (uint32_t port, ParameterDescriptor& desc) const
957 {
958 	assert (port <= parameter_count ());
959 	int lp = _ctrl_params[port].second;
960 	const ParameterDescriptor& d (_param_desc.find(lp)->second);
961 
962 	desc.lower        = d.lower;
963 	desc.upper        = d.upper;
964 	desc.normal       = d.normal;
965 	desc.toggled      = d.toggled;
966 	desc.logarithmic  = d.logarithmic;
967 	desc.integer_step = d.integer_step;
968 	desc.sr_dependent = d.sr_dependent;
969 	desc.enumeration  = d.enumeration;
970 	desc.unit         = d.unit;
971 	desc.label        = d.label;
972 	desc.scale_points = d.scale_points;
973 
974 	desc.update_steps ();
975 	return 0;
976 }
977 
978 std::string
get_parameter_docs(uint32_t port) const979 LuaProc::get_parameter_docs (uint32_t port) const {
980 	assert (port <= parameter_count ());
981 	int lp = _ctrl_params[port].second;
982 	return _param_doc.find(lp)->second;
983 }
984 
985 uint32_t
nth_parameter(uint32_t port,bool & ok) const986 LuaProc::nth_parameter (uint32_t port, bool& ok) const
987 {
988 	if (port < _ctrl_params.size ()) {
989 		ok = true;
990 		return port;
991 	}
992 	ok = false;
993 	return 0;
994 }
995 
996 bool
parameter_is_input(uint32_t port) const997 LuaProc::parameter_is_input (uint32_t port) const
998 {
999 	assert (port < _ctrl_params.size ());
1000 	return (!_ctrl_params[port].first);
1001 }
1002 
1003 bool
parameter_is_output(uint32_t port) const1004 LuaProc::parameter_is_output (uint32_t port) const
1005 {
1006 	assert (port < _ctrl_params.size ());
1007 	return (_ctrl_params[port].first);
1008 }
1009 
1010 std::set<Evoral::Parameter>
automatable() const1011 LuaProc::automatable () const
1012 {
1013 	std::set<Evoral::Parameter> automatables;
1014 	for (uint32_t i = 0; i < _ctrl_params.size (); ++i) {
1015 		if (parameter_is_input (i)) {
1016 			automatables.insert (automatables.end (), Evoral::Parameter (PluginAutomation, 0, i));
1017 		}
1018 	}
1019 	return automatables;
1020 }
1021 
1022 std::string
describe_parameter(Evoral::Parameter param)1023 LuaProc::describe_parameter (Evoral::Parameter param)
1024 {
1025 	if (param.type () == PluginAutomation && param.id () < parameter_count ()) {
1026 		int lp = _ctrl_params[param.id ()].second;
1027 		return _param_desc[lp].label;
1028 	}
1029 	return "??";
1030 }
1031 
1032 boost::shared_ptr<ScalePoints>
parse_scale_points(luabridge::LuaRef * lr)1033 LuaProc::parse_scale_points (luabridge::LuaRef* lr)
1034 {
1035 	if (!(*lr)["scalepoints"].isTable()) {
1036 		return boost::shared_ptr<ScalePoints> ();
1037 	}
1038 
1039 	int cnt = 0;
1040 	boost::shared_ptr<ScalePoints> rv = boost::shared_ptr<ScalePoints>(new ScalePoints());
1041 	luabridge::LuaRef scalepoints ((*lr)["scalepoints"]);
1042 
1043 	for (luabridge::Iterator i (scalepoints); !i.isNil (); ++i) {
1044 		if (!i.key ().isString ())    { continue; }
1045 		if (!i.value ().isNumber ())  { continue; }
1046 		rv->insert(make_pair(i.key ().cast<std::string> (),
1047 					i.value ().cast<float> ()));
1048 		++cnt;
1049 	}
1050 
1051 	if (rv->size() > 0) {
1052 		return rv;
1053 	}
1054 	return boost::shared_ptr<ScalePoints> ();
1055 }
1056 
1057 boost::shared_ptr<ScalePoints>
get_scale_points(uint32_t port) const1058 LuaProc::get_scale_points (uint32_t port) const
1059 {
1060 	int lp = _ctrl_params[port].second;
1061 	return _param_desc.find(lp)->second.scale_points;
1062 }
1063 
1064 void
setup_lua_inline_gui(LuaState * lua_gui)1065 LuaProc::setup_lua_inline_gui (LuaState *lua_gui)
1066 {
1067 	lua_State* LG = lua_gui->getState ();
1068 	LuaBindings::stddef (LG);
1069 	LuaBindings::common (LG);
1070 	LuaBindings::dsp (LG);
1071 	LuaBindings::osc (LG);
1072 
1073 	lua_gui->Print.connect (sigc::mem_fun (*this, &LuaProc::lua_print));
1074 	lua_gui->do_command ("function ardour () end");
1075 	lua_gui->do_command (_script);
1076 
1077 	// TODO think: use a weak-pointer here ?
1078 	// (the GUI itself uses a shared ptr to this plugin, so we should be good)
1079 	luabridge::getGlobalNamespace (LG)
1080 		.beginNamespace ("Ardour")
1081 		.beginClass <LuaProc> ("LuaProc")
1082 		.addFunction ("shmem", &LuaProc::instance_shm)
1083 		.addFunction ("table", &LuaProc::instance_ref)
1084 		.endClass ()
1085 		.endNamespace ();
1086 
1087 	luabridge::push <LuaProc *> (LG, this);
1088 	lua_setglobal (LG, "self");
1089 
1090 	luabridge::push <float *> (LG, _control_data);
1091 	lua_setglobal (LG, "CtrlPorts");
1092 }
1093 ////////////////////////////////////////////////////////////////////////////////
1094 
1095 #include "ardour/search_paths.h"
1096 #include "sha1.c"
1097 
1098 std::string
preset_name_to_uri(const std::string & name) const1099 LuaProc::preset_name_to_uri (const std::string& name) const
1100 {
1101 	std::string uri ("urn:lua:");
1102 	char hash[41];
1103 	Sha1Digest s;
1104 	sha1_init (&s);
1105 	sha1_write (&s, (const uint8_t *) name.c_str(), name.size ());
1106 	sha1_write (&s, (const uint8_t *) _script.c_str(), _script.size ());
1107 	sha1_result_hash (&s, hash);
1108 	return uri + hash;
1109 }
1110 
1111 std::string
presets_file() const1112 LuaProc::presets_file () const
1113 {
1114 	return string_compose ("lua-%1", _info->unique_id);
1115 }
1116 
1117 XMLTree*
presets_tree() const1118 LuaProc::presets_tree () const
1119 {
1120 	XMLTree* t = new XMLTree;
1121 	std::string p = Glib::build_filename (ARDOUR::user_config_directory (), "presets");
1122 
1123 	if (!Glib::file_test (p, Glib::FILE_TEST_IS_DIR)) {
1124 		if (g_mkdir_with_parents (p.c_str(), 0755) != 0) {
1125 			error << _("Unable to create LuaProc presets directory") << endmsg;
1126 		};
1127 	}
1128 
1129 	p = Glib::build_filename (p, presets_file ());
1130 
1131 	if (!Glib::file_test (p, Glib::FILE_TEST_EXISTS)) {
1132 		t->set_root (new XMLNode (X_("LuaPresets")));
1133 		return t;
1134 	}
1135 
1136 	t->set_filename (p);
1137 	if (!t->read ()) {
1138 		delete t;
1139 		return 0;
1140 	}
1141 	return t;
1142 }
1143 
1144 bool
load_preset(PresetRecord r)1145 LuaProc::load_preset (PresetRecord r)
1146 {
1147 	boost::shared_ptr<XMLTree> t (presets_tree ());
1148 	if (t == 0) {
1149 		return false;
1150 	}
1151 
1152 	XMLNode* root = t->root ();
1153 	for (XMLNodeList::const_iterator i = root->children().begin(); i != root->children().end(); ++i) {
1154 		std::string str;
1155 		if (!(*i)->get_property (X_("label"), str)) {
1156 			assert (false);
1157 		}
1158 		if (str != r.label) {
1159 			continue;
1160 		}
1161 
1162 		for (XMLNodeList::const_iterator j = (*i)->children().begin(); j != (*i)->children().end(); ++j) {
1163 			if ((*j)->name() == X_("Parameter")) {
1164 				uint32_t index;
1165 				float value;
1166 				if (!(*j)->get_property (X_("index"), index) ||
1167 				    !(*j)->get_property (X_("value"), value)) {
1168 					assert (false);
1169 					continue;
1170 				}
1171 				set_parameter (index, value, 0);
1172 				PresetPortSetValue (index, value); /* EMIT SIGNAL */
1173 			}
1174 		}
1175 		return Plugin::load_preset(r);
1176 	}
1177 	return false;
1178 }
1179 
1180 std::string
do_save_preset(std::string name)1181 LuaProc::do_save_preset (std::string name) {
1182 
1183 	boost::shared_ptr<XMLTree> t (presets_tree ());
1184 	if (t == 0) {
1185 		return "";
1186 	}
1187 
1188 	// prevent dups -- just in case
1189 	t->root()->remove_nodes_and_delete (X_("label"), name);
1190 
1191 	std::string uri (preset_name_to_uri (name));
1192 
1193 	XMLNode* p = new XMLNode (X_("Preset"));
1194 	p->set_property (X_("uri"), uri);
1195 	p->set_property (X_("label"), name);
1196 
1197 	for (uint32_t i = 0; i < parameter_count(); ++i) {
1198 		if (parameter_is_input (i)) {
1199 			XMLNode* c = new XMLNode (X_("Parameter"));
1200 			c->set_property (X_("index"), i);
1201 			c->set_property (X_("value"), get_parameter (i));
1202 			p->add_child_nocopy (*c);
1203 		}
1204 	}
1205 	t->root()->add_child_nocopy (*p);
1206 
1207 	std::string f = Glib::build_filename (ARDOUR::user_config_directory (), "presets");
1208 	f = Glib::build_filename (f, presets_file ());
1209 
1210 	t->write (f);
1211 	return uri;
1212 }
1213 
1214 void
do_remove_preset(std::string name)1215 LuaProc::do_remove_preset (std::string name)
1216 {
1217 	boost::shared_ptr<XMLTree> t (presets_tree ());
1218 	if (t == 0) {
1219 		return;
1220 	}
1221 	t->root()->remove_nodes_and_delete (X_("label"), name);
1222 	std::string f = Glib::build_filename (ARDOUR::user_config_directory (), "presets");
1223 	f = Glib::build_filename (f, presets_file ());
1224 	t->write (f);
1225 }
1226 
1227 void
find_presets()1228 LuaProc::find_presets ()
1229 {
1230 	boost::shared_ptr<XMLTree> t (presets_tree ());
1231 	if (t) {
1232 		XMLNode* root = t->root ();
1233 		for (XMLNodeList::const_iterator i = root->children().begin(); i != root->children().end(); ++i) {
1234 			std::string uri;
1235 			std::string label;
1236 
1237 			if (!(*i)->get_property (X_("uri"), uri) || !(*i)->get_property (X_("label"), label)) {
1238 				assert (false);
1239 			}
1240 
1241 			PresetRecord r (uri, label, true);
1242 			_presets.insert (make_pair (r.uri, r));
1243 		}
1244 	}
1245 }
1246 
1247 ////////////////////////////////////////////////////////////////////////////////
1248 
LuaPluginInfo(LuaScriptInfoPtr lsi)1249 LuaPluginInfo::LuaPluginInfo (LuaScriptInfoPtr lsi) {
1250 	if (lsi->type != LuaScriptInfo::DSP) {
1251 		throw failed_constructor ();
1252 	}
1253 
1254 	path = lsi->path;
1255 	name = lsi->name;
1256 	creator = lsi->author;
1257 	category = lsi->category;
1258 	unique_id = lsi->unique_id;
1259 
1260 	n_inputs.set (DataType::AUDIO, 1);
1261 	n_outputs.set (DataType::AUDIO, 1);
1262 	type = Lua;
1263 
1264 	// TODO, parse script, get 'dsp_ioconfig', see match_variable_io()
1265 	_max_outputs = 0;
1266 }
1267 
1268 PluginPtr
load(Session & session)1269 LuaPluginInfo::load (Session& session)
1270 {
1271 	std::string script = "";
1272 	if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
1273 		return PluginPtr ();
1274 	}
1275 
1276 	try {
1277 		script = Glib::file_get_contents (path);
1278 	} catch (Glib::FileError const& err) {
1279 		return PluginPtr ();
1280 	}
1281 
1282 	if (script.empty ()) {
1283 		return PluginPtr ();
1284 	}
1285 
1286 	try {
1287 		LuaProc* lp = new LuaProc (session.engine (), session, script);
1288 		lp->set_origin (path);
1289 		PluginPtr plugin (lp);
1290 		return plugin;
1291 	} catch (failed_constructor& err) {
1292 		;
1293 	}
1294 	return PluginPtr ();
1295 }
1296 
1297 std::vector<Plugin::PresetRecord>
get_presets(bool) const1298 LuaPluginInfo::get_presets (bool /*user_only*/) const
1299 {
1300 	std::vector<Plugin::PresetRecord> p;
1301 	XMLTree* t = new XMLTree;
1302 	std::string pf = Glib::build_filename (ARDOUR::user_config_directory (), "presets", string_compose ("lua-%1", unique_id));
1303 	if (Glib::file_test (pf, Glib::FILE_TEST_EXISTS)) {
1304 		t->set_filename (pf);
1305 		if (t->read ()) {
1306 			XMLNode* root = t->root ();
1307 			for (XMLNodeList::const_iterator i = root->children().begin(); i != root->children().end(); ++i) {
1308 				XMLProperty const * uri = (*i)->property (X_("uri"));
1309 				XMLProperty const * label = (*i)->property (X_("label"));
1310 				p.push_back (Plugin::PresetRecord (uri->value(), label->value(), true));
1311 			}
1312 		}
1313 	}
1314 	delete t;
1315 	return p;
1316 }
1317