expose OSC bindings to luasession util
[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", 512);
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 * _load_session (string dir, string state)
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         float sr;
177         SampleFormat sf;
178
179         std::string s = Glib::build_filename (dir, state + statefile_suffix);
180         if (!Glib::file_test (dir, Glib::FILE_TEST_EXISTS)) {
181                 std::cerr << "Cannot find session: " << s << "\n";
182                 return 0;
183         }
184
185         if (Session::get_info_from_path (s, sr, sf) == 0) {
186                 if (engine->set_sample_rate (sr)) {
187                         std::cerr << "Cannot set session's samplerate.\n";
188                         return 0;
189                 }
190         } else {
191                 std::cerr << "Cannot get samplerate from session.\n";
192                 return 0;
193         }
194
195         init_post_engine ();
196
197         if (engine->start () != 0) {
198                 std::cerr << "Cannot start Audio/MIDI engine\n";
199                 return 0;
200         }
201
202         Session* session = new Session (*engine, dir, state);
203         return session;
204 }
205
206 static Session* load_session (string dir, string state)
207 {
208         Session* s = 0;
209         if (_session) {
210                 cerr << "Session already open" << "\n";
211                 return 0;
212         }
213         try {
214                 s = _load_session (dir, state);
215         } catch (failed_constructor& e) {
216                 cerr << "failed_constructor: " << e.what () << "\n";
217                 return 0;
218         } catch (AudioEngine::PortRegistrationFailure& e) {
219                 cerr << "PortRegistrationFailure: " << e.what () << "\n";
220                 return 0;
221         } catch (exception& e) {
222                 cerr << "exception: " << e.what () << "\n";
223                 return 0;
224         } catch (...) {
225                 cerr << "unknown exception.\n";
226                 return 0;
227         }
228         Glib::usleep (1000000); // allo signal propagation, callback/thread-pool setup
229         assert (s);
230         set_session (s);
231         s->DropReferences.connect_same_thread (session_connections, &unset_session);
232         return s;
233 }
234
235 static void close_session ()
236 {
237         delete _session;
238         assert (!_session);
239 }
240
241 static int close_session_lua (lua_State *L)
242 {
243         if (!_session) {
244                 cerr << "No open session" << "\n";
245                 return 0;
246         }
247         close_session ();
248         return 0;
249 }
250
251 /* extern VST functions */
252 int vstfx_init (void*) { return 0; }
253 void vstfx_exit () {}
254 void vstfx_destroy_editor (VSTState*) {}
255
256 static void my_lua_print (std::string s) {
257         std::cout << s << "\n";
258 }
259
260 static void delay (float d) {
261         if (d > 0) {
262                 Glib::usleep (d * 1000000);
263         }
264 }
265
266 static void setup_lua ()
267 {
268         assert (!lua);
269
270         lua = new LuaState ();
271         lua->Print.connect (&my_lua_print);
272         lua_State* L = lua->getState ();
273
274         LuaBindings::stddef (L);
275         LuaBindings::common (L);
276         LuaBindings::session (L);
277         LuaBindings::osc (L);
278
279         luabridge::getGlobalNamespace (L)
280                 .beginNamespace ("_G")
281                 .addFunction ("load_session", &load_session)
282                 .addFunction ("close_session", &close_session)
283                 .addFunction ("sleep", &delay)
284                 .endNamespace ();
285
286         luabridge::getGlobalNamespace (L)
287                 .beginNamespace ("ARDOUR")
288                 .beginClass <Session> ("Session")
289                 .addExtCFunction ("close", &close_session_lua)
290                 .endClass ()
291                 .endNamespace ();
292
293         // push instance to global namespace (C++ lifetime)
294         luabridge::push <AudioEngine *> (L, AudioEngine::create ());
295         lua_setglobal (L, "AudioEngine");
296 }
297
298 int main (int argc, char **argv)
299 {
300         init ();
301         setup_lua ();
302
303         using_history ();
304         std::string histfile = Glib::build_filename (user_config_directory(), "/luahist");
305
306         read_history (histfile.c_str());
307
308         char *line;
309         while ((line = readline ("> "))) {
310                 event_loop->run();
311                 if (!strcmp (line, "quit")) {
312                         break;
313                 }
314                 if (strlen (line) == 0) {
315                         continue;
316                 }
317                 if (!lua->do_command (line)) {
318                         add_history (line); // OK
319                 } else {
320                         add_history (line); // :)
321                 }
322                 event_loop->run();
323                 free (line);
324         }
325         printf ("\n");
326
327         if (_session) {
328                 close_session ();
329         }
330
331         engine_connections.drop_connections ();
332
333         delete lua;
334         lua = NULL;
335
336         write_history (histfile.c_str());
337
338         AudioEngine::instance ()->stop ();
339         AudioEngine::destroy ();
340
341         // cleanup
342         ARDOUR::cleanup ();
343         delete event_loop;
344         pthread_cancel_all ();
345         return 0;
346 }