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