remove unnecessary call to init_post_engine() (now called from libardour)
[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         return 0;
202 }
203
204 static Session * _create_session (string dir, string state, uint32_t rate) // throws
205 {
206         if (prepare_engine ()) {
207                 return 0;
208         }
209
210         std::string s = Glib::build_filename (dir, state + statefile_suffix);
211         if (Glib::file_test (dir, Glib::FILE_TEST_EXISTS)) {
212                 std::cerr << "Session already exists: " << s << "\n";
213                 return 0;
214         }
215
216         if (start_engine (rate)) {
217                 return 0;
218         }
219
220         // TODO add option/bindings for this
221         BusProfile bus_profile;
222         bus_profile.master_out_channels = 2;
223
224         AudioEngine* engine = AudioEngine::instance ();
225         Session* session = new Session (*engine, dir, state, &bus_profile);
226         return session;
227 }
228
229 static Session * _load_session (string dir, string state) // throws
230 {
231         if (prepare_engine ()) {
232                 return 0;
233         }
234
235         float sr;
236         SampleFormat sf;
237         std::string v;
238         std::string s = Glib::build_filename (dir, state + statefile_suffix);
239         if (!Glib::file_test (dir, Glib::FILE_TEST_EXISTS)) {
240                 std::cerr << "Cannot find session: " << s << "\n";
241                 return 0;
242         }
243
244         if (Session::get_info_from_path (s, sr, sf, v) != 0) {
245                 std::cerr << "Cannot get samplerate from session.\n";
246                 return 0;
247         }
248
249         if (start_engine (sr)) {
250                 return 0;
251         }
252
253         AudioEngine* engine = AudioEngine::instance ();
254         Session* session = new Session (*engine, dir, state);
255         return session;
256 }
257
258 /* ****************************************************************************/
259 /* lua bound functions */
260
261 static Session* create_session (string dir, string state, uint32_t rate)
262 {
263         Session* s = 0;
264         if (session) {
265                 cerr << "Session already open" << "\n";
266                 return 0;
267         }
268         try {
269                 s = _create_session (dir, state, rate);
270         } catch (failed_constructor& e) {
271                 cerr << "failed_constructor: " << e.what () << "\n";
272                 return 0;
273         } catch (AudioEngine::PortRegistrationFailure& e) {
274                 cerr << "PortRegistrationFailure: " << e.what () << "\n";
275                 return 0;
276         } catch (exception& e) {
277                 cerr << "exception: " << e.what () << "\n";
278                 return 0;
279         } catch (...) {
280                 cerr << "unknown exception.\n";
281                 return 0;
282         }
283         Glib::usleep (1000000); // allow signal propagation, callback/thread-pool setup
284         if (!s) {
285                 return 0;
286         }
287         set_session (s);
288         s->DropReferences.connect_same_thread (session_connections, &unset_session);
289         return s;
290 }
291
292 static Session* load_session (string dir, string state)
293 {
294         Session* s = 0;
295         if (session) {
296                 cerr << "Session already open" << "\n";
297                 return 0;
298         }
299         try {
300                 s = _load_session (dir, state);
301         } catch (failed_constructor& e) {
302                 cerr << "failed_constructor: " << e.what () << "\n";
303                 return 0;
304         } catch (AudioEngine::PortRegistrationFailure& e) {
305                 cerr << "PortRegistrationFailure: " << e.what () << "\n";
306                 return 0;
307         } catch (exception& e) {
308                 cerr << "exception: " << e.what () << "\n";
309                 return 0;
310         } catch (...) {
311                 cerr << "unknown exception.\n";
312                 return 0;
313         }
314         Glib::usleep (1000000); // allow signal propagation, callback/thread-pool setup
315         if (!s) {
316                 return 0;
317         }
318         set_session (s);
319         s->DropReferences.connect_same_thread (session_connections, &unset_session);
320         return s;
321 }
322
323 static int set_debug_options (const char *opts)
324 {
325         return PBD::parse_debug_options (opts);
326 }
327
328 static void close_session ()
329 {
330         delete session;
331         assert (!session);
332 }
333
334 static int close_session_lua (lua_State *L)
335 {
336         if (!session) {
337                 cerr << "No open session" << "\n";
338                 return 0;
339         }
340         close_session ();
341         return 0;
342 }
343
344 static void delay (float d) {
345         if (d > 0) {
346                 Glib::usleep (d * 1000000);
347         }
348 }
349
350 static int do_quit (lua_State *L)
351 {
352         keep_running = false;
353         return 0;
354 }
355
356 /* ****************************************************************************/
357
358 static void my_lua_print (std::string s) {
359         std::cout << s << "\n";
360 }
361
362 static void setup_lua ()
363 {
364         assert (!lua);
365
366         lua = new LuaState ();
367         lua->Print.connect (&my_lua_print);
368         lua_State* L = lua->getState ();
369
370         LuaBindings::stddef (L);
371         LuaBindings::common (L);
372         LuaBindings::session (L);
373         LuaBindings::osc (L);
374
375         luabridge::getGlobalNamespace (L)
376                 .beginNamespace ("_G")
377                 .addFunction ("create_session", &create_session)
378                 .addFunction ("load_session", &load_session)
379                 .addFunction ("close_session", &close_session)
380                 .addFunction ("sleep", &delay)
381                 .addFunction ("quit", &do_quit)
382                 .addFunction ("set_debug_options", &set_debug_options)
383                 .endNamespace ();
384
385         // add a Session::close() method
386         luabridge::getGlobalNamespace (L)
387                 .beginNamespace ("ARDOUR")
388                 .beginClass <Session> ("Session")
389                 .addExtCFunction ("close", &close_session_lua)
390                 .endClass ()
391                 .endNamespace ();
392
393         // push instance to global namespace (C++ lifetime)
394         luabridge::push <AudioEngine *> (L, AudioEngine::create ());
395         lua_setglobal (L, "AudioEngine");
396
397         AudioEngine::instance ()->stop ();
398 }
399
400 static int
401 incomplete (lua_State* L, int status) {
402         if (status == LUA_ERRSYNTAX) {
403                 size_t lmsg;
404                 const char *msg = lua_tolstring (L, -1, &lmsg);
405                 if (lmsg >= 5 && strcmp(msg + lmsg - 5, "<eof>") == 0) {
406                         lua_pop(L, 1);
407                         return 1;
408                 }
409         }
410         return 0;
411 }
412
413 int main (int argc, char **argv)
414 {
415         init ();
416         setup_lua ();
417
418         using_history ();
419         std::string histfile = Glib::build_filename (user_config_directory(), "/luahist");
420
421         rl_bind_key ('\t', rl_insert); // disable completion
422         read_history (histfile.c_str());
423
424         char *line = NULL;
425         while (keep_running && (line = readline ("> "))) {
426                 event_loop->run();
427                 if (!strcmp (line, "quit")) {
428                         free (line); line = NULL;
429                         break;
430                 }
431
432                 if (strlen (line) == 0) {
433                         free (line); line = NULL;
434                         continue;
435                 }
436
437                 do {
438                         LuaState lt;
439                         lua_State* L = lt.getState ();
440                         int status = luaL_loadbuffer (L, line, strlen(line), "=stdin");
441                         if (!incomplete (L, status)) {
442                                 break;
443                         }
444                         char *l2 = readline (">> ");
445                         if (!l2) {
446                                 break;
447                         }
448                         if (strlen (l2) == 0) {
449                                 continue;
450                         }
451                         line = (char*) realloc ((void*)line, (strlen(line) + strlen (l2) + 2) * sizeof(char));
452                         strcat (line, "\n");
453                         strcat (line, l2);
454                         free (l2);
455                 } while (1);
456
457                 if (lua->do_command (line)) {
458                         /* error */
459                         free (line); line = NULL;
460                         continue;
461                 }
462
463                 add_history (line);
464                 event_loop->run();
465                 free (line); line = NULL;
466         }
467         free (line);
468         printf ("\n");
469
470         if (session) {
471                 close_session ();
472         }
473
474         engine_connections.drop_connections ();
475
476         delete lua;
477         lua = NULL;
478
479         write_history (histfile.c_str());
480
481         AudioEngine::instance ()->stop ();
482         AudioEngine::destroy ();
483
484         // cleanup
485         ARDOUR::cleanup ();
486         delete event_loop;
487         pthread_cancel_all ();
488         return 0;
489 }