Amend 1caef183 (Windows Lua bindings)
[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 void init ()
124 {
125         if (!ARDOUR::init (false, true, localedir)) {
126                 cerr << "Ardour failed to initialize\n" << endl;
127                 ::exit (EXIT_FAILURE);
128         }
129
130         assert (!event_loop);
131         event_loop = new MyEventLoop ("lua");
132         EventLoop::set_event_loop_for_thread (event_loop);
133         SessionEvent::create_per_thread_pool ("lua", 4096);
134
135         static LuaReceiver lua_receiver;
136
137         lua_receiver.listen_to (error);
138         lua_receiver.listen_to (info);
139         lua_receiver.listen_to (fatal);
140         lua_receiver.listen_to (warning);
141 }
142
143 static void set_session (ARDOUR::Session *s)
144 {
145         session = s;
146         assert (lua);
147         lua_State* L = lua->getState ();
148         LuaBindings::set_session (L, session);
149         lua->collect_garbage (); // drop references
150 }
151
152 static void unset_session ()
153 {
154         session_connections.drop_connections ();
155         set_session (NULL);
156 }
157
158 static int prepare_engine ()
159 {
160         AudioEngine* engine = AudioEngine::instance ();
161
162         if (!engine->current_backend ()) {
163                 if (!engine->set_backend ("None (Dummy)", "Unit-Test", "")) {
164                         std::cerr << "Cannot create Audio/MIDI engine\n";
165                         return -1;
166                 }
167         }
168
169         if (!engine->current_backend ()) {
170                 std::cerr << "Cannot create Audio/MIDI engine\n";
171                 return -1;
172         }
173
174         if (engine->running ()) {
175                 engine->stop ();
176         }
177         return 0;
178 }
179
180 static int start_engine (uint32_t rate)
181 {
182         AudioEngine* engine = AudioEngine::instance ();
183
184         if (engine->set_sample_rate (rate)) {
185                 std::cerr << "Cannot set session's samplerate.\n";
186                 return -1;
187         }
188
189         if (engine->start () != 0) {
190                 std::cerr << "Cannot start Audio/MIDI engine\n";
191                 return -1;
192         }
193
194         return 0;
195 }
196
197 static Session * _create_session (string dir, string state, uint32_t rate) // throws
198 {
199         if (prepare_engine ()) {
200                 return 0;
201         }
202
203         std::string s = Glib::build_filename (dir, state + statefile_suffix);
204         if (Glib::file_test (dir, Glib::FILE_TEST_EXISTS)) {
205                 std::cerr << "Session already exists: " << s << "\n";
206                 return 0;
207         }
208
209         if (start_engine (rate)) {
210                 return 0;
211         }
212
213         // TODO add option/bindings for this
214         BusProfile bus_profile;
215         bus_profile.master_out_channels = 2;
216
217         AudioEngine* engine = AudioEngine::instance ();
218         Session* session = new Session (*engine, dir, state, &bus_profile);
219         return session;
220 }
221
222 static Session * _load_session (string dir, string state) // throws
223 {
224         if (prepare_engine ()) {
225                 return 0;
226         }
227
228         float sr;
229         SampleFormat sf;
230         std::string v;
231         std::string s = Glib::build_filename (dir, state + statefile_suffix);
232         if (!Glib::file_test (dir, Glib::FILE_TEST_EXISTS)) {
233                 std::cerr << "Cannot find session: " << s << "\n";
234                 return 0;
235         }
236
237         if (Session::get_info_from_path (s, sr, sf, v) != 0) {
238                 std::cerr << "Cannot get samplerate from session.\n";
239                 return 0;
240         }
241
242         if (start_engine (sr)) {
243                 return 0;
244         }
245
246         AudioEngine* engine = AudioEngine::instance ();
247         Session* session = new Session (*engine, dir, state);
248         return session;
249 }
250
251 /* ****************************************************************************/
252 /* lua bound functions */
253
254 static Session* create_session (string dir, string state, uint32_t rate)
255 {
256         Session* s = 0;
257         if (session) {
258                 cerr << "Session already open" << "\n";
259                 return 0;
260         }
261         try {
262                 s = _create_session (dir, state, rate);
263         } catch (failed_constructor& e) {
264                 cerr << "failed_constructor: " << e.what () << "\n";
265                 return 0;
266         } catch (AudioEngine::PortRegistrationFailure& e) {
267                 cerr << "PortRegistrationFailure: " << e.what () << "\n";
268                 return 0;
269         } catch (exception& e) {
270                 cerr << "exception: " << e.what () << "\n";
271                 return 0;
272         } catch (...) {
273                 cerr << "unknown exception.\n";
274                 return 0;
275         }
276         Glib::usleep (1000000); // allow signal propagation, callback/thread-pool setup
277         if (!s) {
278                 return 0;
279         }
280         set_session (s);
281         s->DropReferences.connect_same_thread (session_connections, &unset_session);
282         return s;
283 }
284
285 static Session* load_session (string dir, string state)
286 {
287         Session* s = 0;
288         if (session) {
289                 cerr << "Session already open" << "\n";
290                 return 0;
291         }
292         try {
293                 s = _load_session (dir, state);
294         } catch (failed_constructor& e) {
295                 cerr << "failed_constructor: " << e.what () << "\n";
296                 return 0;
297         } catch (AudioEngine::PortRegistrationFailure& e) {
298                 cerr << "PortRegistrationFailure: " << e.what () << "\n";
299                 return 0;
300         } catch (exception& e) {
301                 cerr << "exception: " << e.what () << "\n";
302                 return 0;
303         } catch (...) {
304                 cerr << "unknown exception.\n";
305                 return 0;
306         }
307         Glib::usleep (1000000); // allow signal propagation, callback/thread-pool setup
308         if (!s) {
309                 return 0;
310         }
311         set_session (s);
312         s->DropReferences.connect_same_thread (session_connections, &unset_session);
313         return s;
314 }
315
316 static int set_debug_options (const char *opts)
317 {
318         return PBD::parse_debug_options (opts);
319 }
320
321 static void close_session ()
322 {
323         delete session;
324         assert (!session);
325 }
326
327 static int close_session_lua (lua_State *L)
328 {
329         if (!session) {
330                 cerr << "No open session" << "\n";
331                 return 0;
332         }
333         close_session ();
334         return 0;
335 }
336
337 static void delay (float d) {
338         if (d > 0) {
339                 Glib::usleep (d * 1000000);
340         }
341 }
342
343 static int do_quit (lua_State *L)
344 {
345         keep_running = false;
346         return 0;
347 }
348
349 /* ****************************************************************************/
350
351 static void my_lua_print (std::string s) {
352         std::cout << s << "\n";
353 }
354
355 static void setup_lua ()
356 {
357         assert (!lua);
358
359         lua = new LuaState ();
360         lua->Print.connect (&my_lua_print);
361         lua_State* L = lua->getState ();
362
363         LuaBindings::stddef (L);
364         LuaBindings::common (L);
365         LuaBindings::session (L);
366         LuaBindings::osc (L);
367
368         luabridge::getGlobalNamespace (L)
369                 .beginNamespace ("_G")
370                 .addFunction ("create_session", &create_session)
371                 .addFunction ("load_session", &load_session)
372                 .addFunction ("close_session", &close_session)
373                 .addFunction ("sleep", &delay)
374                 .addFunction ("quit", &do_quit)
375                 .addFunction ("set_debug_options", &set_debug_options)
376                 .endNamespace ();
377
378         // add a Session::close() method
379         luabridge::getGlobalNamespace (L)
380                 .beginNamespace ("ARDOUR")
381                 .beginClass <Session> ("Session")
382                 .addExtCFunction ("close", &close_session_lua)
383                 .endClass ()
384                 .endNamespace ();
385
386         // push instance to global namespace (C++ lifetime)
387         luabridge::push <AudioEngine *> (L, AudioEngine::create ());
388         lua_setglobal (L, "AudioEngine");
389
390         AudioEngine::instance ()->stop ();
391 }
392
393 static int
394 incomplete (lua_State* L, int status) {
395         if (status == LUA_ERRSYNTAX) {
396                 size_t lmsg;
397                 const char *msg = lua_tolstring (L, -1, &lmsg);
398                 if (lmsg >= 5 && strcmp(msg + lmsg - 5, "<eof>") == 0) {
399                         lua_pop(L, 1);
400                         return 1;
401                 }
402         }
403         return 0;
404 }
405
406 int main (int argc, char **argv)
407 {
408         init ();
409         setup_lua ();
410
411         using_history ();
412         std::string histfile = Glib::build_filename (user_config_directory(), "/luahist");
413
414         rl_bind_key ('\t', rl_insert); // disable completion
415         read_history (histfile.c_str());
416
417         char *line = NULL;
418         while (keep_running && (line = readline ("> "))) {
419                 event_loop->run();
420                 if (!strcmp (line, "quit")) {
421                         free (line); line = NULL;
422                         break;
423                 }
424
425                 if (strlen (line) == 0) {
426                         free (line); line = NULL;
427                         continue;
428                 }
429
430                 do {
431                         LuaState lt;
432                         lua_State* L = lt.getState ();
433                         int status = luaL_loadbuffer (L, line, strlen(line), "=stdin");
434                         if (!incomplete (L, status)) {
435                                 break;
436                         }
437                         char *l2 = readline (">> ");
438                         if (!l2) {
439                                 break;
440                         }
441                         if (strlen (l2) == 0) {
442                                 continue;
443                         }
444                         line = (char*) realloc ((void*)line, (strlen(line) + strlen (l2) + 2) * sizeof(char));
445                         strcat (line, "\n");
446                         strcat (line, l2);
447                         free (l2);
448                 } while (1);
449
450                 if (lua->do_command (line)) {
451                         /* error */
452                         free (line); line = NULL;
453                         continue;
454                 }
455
456                 add_history (line);
457                 event_loop->run();
458                 free (line); line = NULL;
459         }
460         free (line);
461         printf ("\n");
462
463         if (session) {
464                 close_session ();
465         }
466
467         engine_connections.drop_connections ();
468
469         delete lua;
470         lua = NULL;
471
472         write_history (histfile.c_str());
473
474         AudioEngine::instance ()->stop ();
475         AudioEngine::destroy ();
476
477         // cleanup
478         ARDOUR::cleanup ();
479         delete event_loop;
480         pthread_cancel_all ();
481         return 0;
482 }