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