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