OSC: fix issue #7444 send fader position/gain even when 0.
[ardour.git] / tools / luadevel / luasession.cc
1 #include <stdint.h>
2 #include <assert.h>
3
4 #include <cstdio>
5 #include <iostream>
6 #include <string>
7 #include <list>
8 #include <vector>
9
10 #include <glibmm.h>
11
12 #include "pbd/debug.h"
13 #include "pbd/event_loop.h"
14 #include "pbd/error.h"
15 #include "pbd/failed_constructor.h"
16 #include "pbd/pthread_utils.h"
17 #include "pbd/reallocpool.h"
18 #include "pbd/receiver.h"
19 #include "pbd/transmitter.h"
20
21 #include "ardour/ardour.h"
22 #include "ardour/audioengine.h"
23 #include "ardour/filename_extensions.h"
24 #include "ardour/filesystem_paths.h"
25 #include "ardour/luabindings.h"
26 #include "ardour/session.h"
27 #include "ardour/types.h"
28 #include "ardour/vst_types.h"
29
30 #include <readline/readline.h>
31 #include <readline/history.h>
32
33 #include "lua/luastate.h"
34 #include "LuaBridge/LuaBridge.h"
35
36 using namespace std;
37 using namespace ARDOUR;
38 using namespace PBD;
39
40 static const char* localedir = LOCALEDIR;
41 static PBD::ScopedConnectionList engine_connections;
42 static PBD::ScopedConnectionList session_connections;
43 static Session* session = NULL;
44 static LuaState* lua;
45 static bool keep_running = true;
46
47 /* extern VST functions */
48 int vstfx_init (void*) { return 0; }
49 void vstfx_exit () {}
50 void vstfx_destroy_editor (VSTState*) {}
51
52 class LuaReceiver : public Receiver
53 {
54   protected:
55     void receive (Transmitter::Channel chn, const char * str)
56                 {
57                         const char *prefix = "";
58
59                         switch (chn) {
60                                 case Transmitter::Error:
61                                         prefix = "[ERROR]: ";
62                                         break;
63                                 case Transmitter::Info:
64                                         /* ignore */
65                                         return;
66                                 case Transmitter::Warning:
67                                         prefix = "[WARNING]: ";
68                                         break;
69                                 case Transmitter::Fatal:
70                                         prefix = "[FATAL]: ";
71                                         break;
72                                 case Transmitter::Throw:
73                                         /* this isn't supposed to happen */
74                                         abort ();
75                         }
76
77                         /* note: iostreams are already thread-safe: no external
78                                  lock required.
79                                  */
80
81                         std::cout << prefix << str << std::endl;
82
83                         if (chn == Transmitter::Fatal) {
84                                 ::exit (9);
85                         }
86                 }
87 };
88
89 class MyEventLoop : public sigc::trackable, public EventLoop
90 {
91         public:
92                 MyEventLoop (std::string const& name) : EventLoop (name) {
93                         run_loop_thread = Glib::Threads::Thread::self ();
94                 }
95
96                 void call_slot (InvalidationRecord* ir, const boost::function<void()>& f) {
97                         if (Glib::Threads::Thread::self () == run_loop_thread) {
98                                 cout << string_compose ("%1/%2 direct dispatch of call slot via functor @ %3, invalidation %4\n", event_loop_name(), pthread_name(), &f, ir);
99                                 f ();
100                         } else {
101                                 cout << string_compose ("%1/%2 queue call-slot using functor @ %3, invalidation %4\n", event_loop_name(), pthread_name(), &f, ir);
102                                 assert (!ir);
103                                 f (); // XXX TODO, queue and process during run ()
104                         }
105                 }
106
107                 void run () {
108                         ; // TODO process Events, if any
109                 }
110
111                 Glib::Threads::Mutex& slot_invalidation_mutex () { return request_buffer_map_lock; }
112
113         private:
114                 Glib::Threads::Thread* run_loop_thread;
115                 Glib::Threads::Mutex   request_buffer_map_lock;
116 };
117
118 static MyEventLoop *event_loop = NULL;
119
120 /* ****************************************************************************/
121 /* internal helper fn and callbacks */
122
123 static int do_audio_midi_setup (uint32_t desired_sample_rate)
124 {
125         return AudioEngine::instance ()->start ();
126 }
127
128 static void init ()
129 {
130         if (!ARDOUR::init (false, true, localedir)) {
131                 cerr << "Ardour failed to initialize\n" << endl;
132                 ::exit (EXIT_FAILURE);
133         }
134
135         assert (!event_loop);
136         event_loop = new MyEventLoop ("lua");
137         EventLoop::set_event_loop_for_thread (event_loop);
138         SessionEvent::create_per_thread_pool ("lua", 4096);
139
140         static LuaReceiver lua_receiver;
141
142         lua_receiver.listen_to (error);
143         lua_receiver.listen_to (info);
144         lua_receiver.listen_to (fatal);
145         lua_receiver.listen_to (warning);
146
147         ARDOUR::Session::AudioEngineSetupRequired.connect_same_thread (engine_connections, &do_audio_midi_setup);
148 }
149
150 static void set_session (ARDOUR::Session *s)
151 {
152         session = s;
153         assert (lua);
154         lua_State* L = lua->getState ();
155         LuaBindings::set_session (L, session);
156         lua->collect_garbage (); // drop references
157 }
158
159 static void unset_session ()
160 {
161         session_connections.drop_connections ();
162         set_session (NULL);
163 }
164
165 static int prepare_engine ()
166 {
167         AudioEngine* engine = AudioEngine::instance ();
168
169         if (!engine->current_backend ()) {
170                 if (!engine->set_backend ("None (Dummy)", "Unit-Test", "")) {
171                         std::cerr << "Cannot create Audio/MIDI engine\n";
172                         return -1;
173                 }
174         }
175
176         if (!engine->current_backend ()) {
177                 std::cerr << "Cannot create Audio/MIDI engine\n";
178                 return -1;
179         }
180
181         if (engine->running ()) {
182                 engine->stop ();
183         }
184         return 0;
185 }
186
187 static int start_engine (uint32_t rate)
188 {
189         AudioEngine* engine = AudioEngine::instance ();
190
191         if (engine->set_sample_rate (rate)) {
192                 std::cerr << "Cannot set session's samplerate.\n";
193                 return -1;
194         }
195
196         if (engine->start () != 0) {
197                 std::cerr << "Cannot start Audio/MIDI engine\n";
198                 return -1;
199         }
200
201         init_post_engine ();
202         return 0;
203 }
204
205 static Session * _create_session (string dir, string state, uint32_t rate) // throws
206 {
207         if (prepare_engine ()) {
208                 return 0;
209         }
210
211         std::string s = Glib::build_filename (dir, state + statefile_suffix);
212         if (Glib::file_test (dir, Glib::FILE_TEST_EXISTS)) {
213                 std::cerr << "Session already exists: " << s << "\n";
214                 return 0;
215         }
216
217         if (start_engine (rate)) {
218                 return 0;
219         }
220
221         // TODO add option/bindings for this
222         BusProfile bus_profile;
223         bus_profile.master_out_channels = 2;
224         bus_profile.input_ac = AutoConnectPhysical;
225         bus_profile.output_ac = AutoConnectMaster;
226         bus_profile.requested_physical_in = 0; // use all available
227         bus_profile.requested_physical_out = 0; // use all available
228
229         AudioEngine* engine = AudioEngine::instance ();
230         Session* session = new Session (*engine, dir, state, &bus_profile);
231         return session;
232 }
233
234 static Session * _load_session (string dir, string state) // throws
235 {
236         if (prepare_engine ()) {
237                 return 0;
238         }
239
240         float sr;
241         SampleFormat sf;
242         std::string v;
243         std::string s = Glib::build_filename (dir, state + statefile_suffix);
244         if (!Glib::file_test (dir, Glib::FILE_TEST_EXISTS)) {
245                 std::cerr << "Cannot find session: " << s << "\n";
246                 return 0;
247         }
248
249         if (Session::get_info_from_path (s, sr, sf, v) != 0) {
250                 std::cerr << "Cannot get samplerate from session.\n";
251                 return 0;
252         }
253
254         if (start_engine (sr)) {
255                 return 0;
256         }
257
258         AudioEngine* engine = AudioEngine::instance ();
259         Session* session = new Session (*engine, dir, state);
260         return session;
261 }
262
263 /* ****************************************************************************/
264 /* lua bound functions */
265
266 static Session* create_session (string dir, string state, uint32_t rate)
267 {
268         Session* s = 0;
269         if (session) {
270                 cerr << "Session already open" << "\n";
271                 return 0;
272         }
273         try {
274                 s = _create_session (dir, state, rate);
275         } catch (failed_constructor& e) {
276                 cerr << "failed_constructor: " << e.what () << "\n";
277                 return 0;
278         } catch (AudioEngine::PortRegistrationFailure& e) {
279                 cerr << "PortRegistrationFailure: " << e.what () << "\n";
280                 return 0;
281         } catch (exception& e) {
282                 cerr << "exception: " << e.what () << "\n";
283                 return 0;
284         } catch (...) {
285                 cerr << "unknown exception.\n";
286                 return 0;
287         }
288         Glib::usleep (1000000); // allow signal propagation, callback/thread-pool setup
289         if (!s) {
290                 return 0;
291         }
292         set_session (s);
293         s->DropReferences.connect_same_thread (session_connections, &unset_session);
294         return s;
295 }
296
297 static Session* load_session (string dir, string state)
298 {
299         Session* s = 0;
300         if (session) {
301                 cerr << "Session already open" << "\n";
302                 return 0;
303         }
304         try {
305                 s = _load_session (dir, state);
306         } catch (failed_constructor& e) {
307                 cerr << "failed_constructor: " << e.what () << "\n";
308                 return 0;
309         } catch (AudioEngine::PortRegistrationFailure& e) {
310                 cerr << "PortRegistrationFailure: " << e.what () << "\n";
311                 return 0;
312         } catch (exception& e) {
313                 cerr << "exception: " << e.what () << "\n";
314                 return 0;
315         } catch (...) {
316                 cerr << "unknown exception.\n";
317                 return 0;
318         }
319         Glib::usleep (1000000); // allow signal propagation, callback/thread-pool setup
320         if (!s) {
321                 return 0;
322         }
323         set_session (s);
324         s->DropReferences.connect_same_thread (session_connections, &unset_session);
325         return s;
326 }
327
328 static int set_debug_options (const char *opts)
329 {
330         return PBD::parse_debug_options (opts);
331 }
332
333 static void close_session ()
334 {
335         delete session;
336         assert (!session);
337 }
338
339 static int close_session_lua (lua_State *L)
340 {
341         if (!session) {
342                 cerr << "No open session" << "\n";
343                 return 0;
344         }
345         close_session ();
346         return 0;
347 }
348
349 static void delay (float d) {
350         if (d > 0) {
351                 Glib::usleep (d * 1000000);
352         }
353 }
354
355 static int do_quit (lua_State *L)
356 {
357         keep_running = false;
358         return 0;
359 }
360
361 /* ****************************************************************************/
362
363 static void my_lua_print (std::string s) {
364         std::cout << s << "\n";
365 }
366
367 static void setup_lua ()
368 {
369         assert (!lua);
370
371         lua = new LuaState ();
372         lua->Print.connect (&my_lua_print);
373         lua_State* L = lua->getState ();
374
375         LuaBindings::stddef (L);
376         LuaBindings::common (L);
377         LuaBindings::session (L);
378         LuaBindings::osc (L);
379
380         luabridge::getGlobalNamespace (L)
381                 .beginNamespace ("_G")
382                 .addFunction ("create_session", &create_session)
383                 .addFunction ("load_session", &load_session)
384                 .addFunction ("close_session", &close_session)
385                 .addFunction ("sleep", &delay)
386                 .addFunction ("quit", &do_quit)
387                 .addFunction ("set_debug_options", &set_debug_options)
388                 .endNamespace ();
389
390         // add a Session::close() method
391         luabridge::getGlobalNamespace (L)
392                 .beginNamespace ("ARDOUR")
393                 .beginClass <Session> ("Session")
394                 .addExtCFunction ("close", &close_session_lua)
395                 .endClass ()
396                 .endNamespace ();
397
398         // push instance to global namespace (C++ lifetime)
399         luabridge::push <AudioEngine *> (L, AudioEngine::create ());
400         lua_setglobal (L, "AudioEngine");
401
402         AudioEngine::instance ()->stop ();
403 }
404
405 int main (int argc, char **argv)
406 {
407         init ();
408         setup_lua ();
409
410         using_history ();
411         std::string histfile = Glib::build_filename (user_config_directory(), "/luahist");
412
413         read_history (histfile.c_str());
414
415         char *line = NULL;
416         while (keep_running && (line = readline ("> "))) {
417                 event_loop->run();
418                 if (!strcmp (line, "quit")) {
419                         free (line); line = NULL;
420                         break;
421                 }
422
423                 if (strlen (line) == 0) {
424                         free (line); line = NULL;
425                         continue;
426                 }
427
428                 if (lua->do_command (line)) {
429                         // error
430                 }
431
432                 add_history (line);
433                 event_loop->run();
434                 free (line); line = NULL;
435         }
436         free (line);
437         printf ("\n");
438
439         if (session) {
440                 close_session ();
441         }
442
443         engine_connections.drop_connections ();
444
445         delete lua;
446         lua = NULL;
447
448         write_history (histfile.c_str());
449
450         AudioEngine::instance ()->stop ();
451         AudioEngine::destroy ();
452
453         // cleanup
454         ARDOUR::cleanup ();
455         delete event_loop;
456         pthread_cancel_all ();
457         return 0;
458 }