1 /*
2    Copyright (c) 2014 Marco Cosentino
3    Licence GPLv3
4 */
5 
6 module jack;
7 
8 import jack_c;
9 import std.conv;
10 import std..string;
11 
12 alias jack_c.jack_native_thread_t ThreadId;
13 alias jack_c.JACK_POSITION_MASK JACK_POSITION_MASK;
14 
15 alias jack_c.JackOpenOptions OpenOptions;
16 alias jack_c.JackLoadOptions LoadOptions;
17 
18 alias jack_latency_range_t LatencyRange;
19 alias jack_port_id_t PortID;
20 alias jack_port_type_id_t PortTypeID;
21 
22 alias jack_default_audio_sample_t DefaultAudioSample;
23 alias jack_c.JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE;
24 
25 alias jack_c.jack_unique_t Unique;
26 alias jack_c.jack_shmsize_t Shmsize;
27 alias jack_nframes_t NFrames;
28 alias jack_time_t Time;
29 
30 alias jack_c.JACK_MAX_FRAMES MAX_FRAMES;
31 alias jack_c.JACK_LOAD_INIT_LIMIT LOAD_INIT_LIMIT;
32 
33 enum Options : jack_options_t {
34   NullOption =    jack_options_t.JackNullOption,
35   NoStartServer = jack_options_t.JackNoStartServer,
36   UseExactName =  jack_options_t.JackUseExactName,
37   ServerName =    jack_options_t.JackServerName,
38   LoadName =      jack_options_t.JackLoadName,
39   LoadInit =      jack_options_t.JackLoadInit,
40   SessionID =     jack_options_t.JackSessionID
41 };
42 
43 enum Status : jack_status_t {
44   Failure =       jack_status_t.JackFailure,
45   InvalidOption = jack_status_t.JackInvalidOption,
46   NameNotUnique = jack_status_t.JackNameNotUnique,
47   ServerStarted = jack_status_t.JackServerStarted,
48   ServerFailed =  jack_status_t.JackServerFailed,
49   ServerError =   jack_status_t.JackServerError,
50   NoSuchClient =  jack_status_t.JackNoSuchClient,
51   LoadFailure =   jack_status_t.JackLoadFailure,
52   InitFailure =   jack_status_t.JackInitFailure,
53   ShmFailure =    jack_status_t.JackShmFailure,
54   VersionError =  jack_status_t.JackVersionError,
55   BackendError =  jack_status_t.JackBackendError,
56   ClientZombie =  jack_status_t.JackClientZombie
57 };
58 
59 enum LatencyCallbackMode : jack_latency_callback_mode_t {
60   CaptureLatency =  jack_latency_callback_mode_t.JackCaptureLatency,
61   PlaybackLatency = jack_latency_callback_mode_t.JackPlaybackLatency
62 }
63 
64 enum PortFlags : JackPortFlags {
65   IsInput =     JackPortFlags.JackPortIsInput,
66   IsOutput =    JackPortFlags.JackPortIsOutput,
67   IsPhysical =  JackPortFlags.JackPortIsPhysical,
68   CanMonitor =  JackPortFlags.JackPortCanMonitor,
69   IsTerminal =  JackPortFlags.JackPortIsTerminal
70 };
71 
72 enum TransportState : jack_transport_state_t {
73   Stopped = jack_transport_state_t.JackTransportStopped,
74   Rolling = jack_transport_state_t.JackTransportRolling,
75   Looping = jack_transport_state_t.JackTransportLooping,
76   Starting = jack_transport_state_t.JackTransportStarting,
77   NetStarting = jack_transport_state_t.JackTransportNetStarting,
78 };
79 
80 enum PositionBits : jack_position_bits_t {
81   PositionBBT =       jack_position_bits_t.JackPositionBBT,
82   PositionTimecode =  jack_position_bits_t.JackPositionTimecode,
83   BBTFrameOffset =    jack_position_bits_t.JackBBTFrameOffset,
84   AudioVideoRatio =   jack_position_bits_t.JackAudioVideoRatio,
85   VideoFrameOffset =  jack_position_bits_t.JackVideoFrameOffset
86 };
87 
88 
89 struct Position {
90   Unique unique_1;
91   Time usecs;
92   NFrames frame_rate;
93   NFrames frame;
94   PositionBits valid;
95   int bar;
96   int beat;
97   int tick;
98   double bar_start_tick;
99   float beats_per_bar;
100   float beat_type;
101   double ticks_per_beat;
102   double beats_per_minute;
103   double frame_time;
104   double next_time; 
105   NFrames bbt_offset;  
106   float audio_frames_per_video_frame; 
107   NFrames video_offset;
108   int padding[7];
109   Unique unique_2;
110 }
111 
112 interface NamesArray
113 {
114     string stringAt(size_t index);
115     void dispose();
116     string opIndex(size_t index);
117 
118     @property
119     {
120         int length();
121         bool isDisposed();
122     }
123 }
124 
125 interface Port
126 {
127     void* getBuffer(NFrames nframes);
128     bool isConnectedTo(string otherPortName);
129     NamesArray getConnections();
130     void aliasSet(string al);
131     void aliasUnset(string al);
132     
133     void requestMonitor(bool onoff);
134     void ensureMonitor(bool onoff);
135 
136     LatencyRange getLatencyRange(LatencyCallbackMode callbackMode);
137     void setLatencyRange(LatencyCallbackMode callbackMode, LatencyRange lr);
138 
139     @property
140     {
141         string name();
142         void name(string newname);
143 
144         string shortname();
145         PortFlags flags();
146         string type();
147         PortID typeID();
148         bool connected();
149         // TODO: It's not clear how can I interface to get the aliases
150         //string[] aliases();
151         bool isMonitoringInput();
152     }
153 }
154 
155 
156 alias extern(C) int function(NFrames nframes, void* data) ProcessCallback;
157 alias extern(C) void* function(void* data) ThreadCallback;
158 alias extern(C) void function(void* data) ThreadInitCallback;
159 alias extern(C) int function(void* data) GraphOrderCallback;
160 alias extern(C) int function(void* data) XRunCallback;
161 alias extern(C) int function(NFrames nframes, void* data) BufferSizeCallback;
162 alias extern(C) int function(NFrames nframes, void* data) SampleRateCallback;
163 alias extern(C) void function(PortID port, int register, void* data) PortRegistrationCallback;
164 alias extern(C) void function(string name, int register, void* data) ClientRegistrationCallback;
165 alias extern(C) void function(Port a, Port b, int connect, void* data) PortConnectCallback;
166 alias extern(C) int function(Port port, string old_name, string new_name, void* data) PortRenameCallback;
167 alias extern(C) void function(int starting, void* data) FreewheelCallback;
168 alias extern(C) void function(void* data) ShutdownCallback;
169 alias extern(C) void function(Status code, string reason, void* data) InfoShutdownCallback;
170 alias extern(C) void function(LatencyCallbackMode mode, void* data) LatencyCallback;
171 alias extern(C) int function(TransportState state, Position *pos, void *arg) SyncCallback;
172 alias extern(C) void function(TransportState state, NFrames nframes, Position *pos, int new_pos, void *arg) TimebaseCallback;
173 
174 interface Client
175 {
176     void close();
177     void activate();
178     void deactivate();
179 
180     // Port management
181     Port portRegister(string port_name, string port_type, PortFlags flags, uint buffer_size);
182     void portUnregister(Port port);
183     bool portIsMine(Port port);
184     NamesArray portGetAllConnections(Port port);
185     void portRequestMonitorByName(string name, bool onoff);
186     void portDisconnect(Port port);
187     size_t portTypeGetBufferSize(string type);
188     NamesArray getPorts(string pattern, string pattern_type, PortFlags flags);
189     Port getByName(string port_name);
190     Port getByID(PortID id);
191     void connect(string source_port, string dest_port);
192     void disconnect(string source_port, string dest_port);
193     void recomputeTotalLatencies();
194 
195 
196     // Transport
197     void releaseTimebase();
198     void transportStart();
199     void transportStop();
200     void setSyncTimeout(Time timeout);
201     void transportLocate(NFrames frame);
202     TransportState transportQuery(Position *pos);
203     NFrames getCurrentTransportFrame();
204     void trasnportReposition(Position *pos);
205 
206 
207     
208     // Callbacks
209     void setProcessCallback(ProcessCallback callback, void* data);
210     void setShutdownCallback(ShutdownCallback callback, void* data);
211     void setFreewheelCallback(FreewheelCallback callback, void* data);
212     void setBufferSizeCallback(BufferSizeCallback callback, void* data);
213     void setSampleRateCallback(SampleRateCallback callback, void* data);
214     void setClientRegistrationCallback(ClientRegistrationCallback callback, void* data);
215     void setPortRegistrationCallback(PortRegistrationCallback callback, void* data);
216     void setPortConnectCallback(PortConnectCallback callback, void* data);
217     void setPortRenameCallback(PortRenameCallback callback, void* data);
218     void setGraphOrderCallback(GraphOrderCallback callback, void* data);
219     void setXRunCallback(XRunCallback callback, void* data);
220     void setLatencyCallback(LatencyCallback callback, void* data);
221     void setSyncCallback(SyncCallback callback, void* data);
222     void setTimebaseCallback(int conditional, TimebaseCallback callback, void* data);
223 
224     Time framesToTime(NFrames frames);
225     NFrames timeToFrames(Time time);
226 
227     @property
228     {
229         string name();
230         ThreadId threadId();
231         bool isRealtime();
232         float cpuLoad();
233         NFrames samplerate();
234         NFrames buffersize();
235         NFrames framesSinceCycleStart();
236         NFrames frameTime();
237         NFrames lastFrameTime();
238     }
239 }
240 
241 struct Version
242 {
243     int major;
244     int minor;
245     int micro;
246     int proto;
247 }
248 
249 
250 // ######### Implementation ###########
251 
252 class JackException : Exception {
253   Status status = Status.Failure;
254 
255   this(string message) {
256     super(message);
257   }
258 
259   this(string message, Status status) {
260     super(message);
261     this.status = status;
262   }
263 }
264 
265 class NamesArrayImplementation : NamesArray {
266   immutable(char) ** rawPorts;
267   bool disposed;
268   int count;
269 
270   this(immutable(char) ** rawPorts) {
271     this.rawPorts = rawPorts;
272     disposed = false;
273     count = 0;
274     while( rawPorts[count] != null ) count ++;
275   }
276 
277   ~this() {
278     dispose();
279   }
280 
281   void dispose() {
282     jack_free(rawPorts);
283     disposed = true;
284   }
285 
286   bool isDisposed() {
287     return disposed;
288   }
289 
290   string stringAt(size_t index) {
291     if(index >= count) {
292       throw new JackException("Requested index out of bound");
293     }
294     return to!string( rawPorts[index] );
295   }
296 
297   int length() {
298     return count;
299   }
300 
301   string opIndex(size_t index) {
302     return stringAt(index);
303   }
304 }
305 
306 class PortImplementation : Port {
307   jack_port_t* port;
308 
309   this(jack_port_t* port) {
310     this.port = port;
311   }
312 
313   string name() {
314     return to!string( jack_port_name(port) );
315   }
316 
317   void name(string newName) {
318     if( jack_port_set_name(port, toStringz(newName)) ) {
319       throw new JackException("Cannot set port name to " ~ newName);
320     }
321   }
322 
323   string shortname() {
324     return to!string( jack_port_short_name(port) );
325   }
326 
327   PortFlags flags() {
328     return cast(PortFlags) jack_port_flags(port);
329   }
330 
331   string type() {
332     return to!string (jack_port_type(port));
333   }
334 
335   PortID typeID() {
336     return jack_port_type_id(port);
337   }
338 
339   bool connected() {
340     return (jack_port_connected(port) != 0);
341   }
342 
343   bool isMonitoringInput() {
344     return (jack_port_monitoring_input(port) != 0);
345   }
346 
347 
348   LatencyRange getLatencyRange(LatencyCallbackMode callbackMode) {
349     LatencyRange result;
350     jack_port_get_latency_range(port, callbackMode, &result);
351     return result;
352   }
353 
354   void setLatencyRange(LatencyCallbackMode callbackMode, LatencyRange lr) {
355     jack_port_set_latency_range(port, callbackMode, &lr);
356   }
357 
358   void* getBuffer(NFrames nframes) {
359     return jack_port_get_buffer(port, nframes);
360   }
361 
362   bool isConnectedTo(string otherPortName) { 
363     return (jack_port_connected_to(port, toStringz(otherPortName)) != 0);
364   }
365 
366   NamesArray getConnections() {
367     immutable(char)** rawConnections = jack_port_get_connections(port);
368     if(rawConnections == null) {
369       throw new JackException("Cannot get port connections");
370     }
371     return new NamesArrayImplementation(rawConnections);
372   }
373  
374   void aliasSet(string al) {
375     if( jack_port_set_alias(port, toStringz(al)) ) {
376       throw new JackException("Cannot set port alias " ~ al);
377     }
378   }
379 
380   void aliasUnset(string al) {
381     if( jack_port_unset_alias(port, toStringz(al)) ) {
382       throw new JackException("Cannot unset port alias " ~ al);
383     }
384   }
385 
386   void requestMonitor(bool onoff) {
387     if( jack_port_request_monitor(port, to!int (onoff)) ){
388       throw new JackException("Cannot request port monitor for port " ~ this.name);
389     }
390   }
391 
392   void ensureMonitor(bool onoff) {
393     if( jack_port_ensure_monitor(port, to!int (onoff)) ){
394       throw new JackException("Cannot ensure port monitor for port " ~ this.name);
395     }
396   }
397 
398   @property jack_port_t* rawPointer() {
399     return port;
400   }
401 }
402 
403 
404 
405 class ClientImplementation : Client {
406   jack_client_t* client;
407 
408   this(jack_client_t* client) {
409     this.client = client;
410   }
411 
412   void close() {
413     if( jack_client_close(client) ) {
414       throw new JackException("Cannot close client");
415     }
416   }
417 
418   void activate() {
419     if(jack_activate(client)) {
420       throw new JackException("Cannot activate client");
421     }
422   }
423 
424   void deactivate() {
425     if(jack_deactivate(client)) {
426       throw new JackException("Cannot deactivate client");
427     }
428   }
429 
430   Port portRegister(string portName, string portType, PortFlags flags, uint bufferSize) {
431     jack_port_t* port = jack_port_register (client, toStringz(portName), 
432         toStringz(portType), flags, bufferSize);
433 
434     if( port == null ) {
435       throw new JackException("Cannot register the port");
436     }
437 
438     return new PortImplementation(port);
439   }
440 
441   void portUnregister(Port port) {
442     if( jack_port_unregister(client, (cast(PortImplementation) port).rawPointer) ) {
443       throw new JackException("Cannot unregister port " ~ port.name);
444     }
445   }
446 
447   bool portIsMine(Port port) {
448     return (jack_port_is_mine(client, (cast(PortImplementation) port).rawPointer) != 0);
449   }
450 
451   NamesArray portGetAllConnections(Port port) {
452     immutable(char) ** rawPorts = jack_port_get_all_connections(client, 
453         (cast(PortImplementation) port).rawPointer);
454 
455     if(rawPorts == null) {
456       throw new JackException("Cannot get all ports connection");
457     }
458     return new NamesArrayImplementation(rawPorts);
459   }
460 
461   void portRequestMonitorByName(string name, bool onoff) {
462     if( jack_port_request_monitor_by_name(client, toStringz(name), to!int(onoff)) ) {
463       throw new JackException("Cannot request monitor by nme for " ~ name);
464     }
465   }
466 
467   void portDisconnect(Port port) {
468     if( jack_port_disconnect(client, (cast(PortImplementation) port).rawPointer) ){
469       throw new JackException("Cannot disconnect port " ~ port.name);
470     }
471   }
472 
473   size_t portTypeGetBufferSize(string type) {
474     return jack_port_type_get_buffer_size(client, toStringz(type));
475   }
476 
477   NamesArray getPorts(string pattern, string patternType, PortFlags flags) {
478     immutable(char) ** rawPorts = jack_get_ports (client, toStringz(pattern), 
479         toStringz(patternType), flags);
480     if(rawPorts == null) {
481       throw new JackException("Cannot get the ports");
482     }
483     return new NamesArrayImplementation(rawPorts);
484   }
485 
486   Port getByName(string portName) {
487     jack_port_t * portPtr = jack_port_by_name(client, toStringz(portName));
488     if(portPtr == null) {
489       throw new JackException("Cannot get port " ~ portName ~ " by name");
490     }
491     return new PortImplementation(portPtr);
492   }
493 
494   Port getByID(PortID id) {
495     jack_port_t * portPtr = jack_port_by_id(client, id);
496     if(portPtr == null) {
497       throw new JackException("Cannot get port " ~ to!string(id) ~ " by ID");
498     }
499     return new PortImplementation(portPtr);
500   }
501 
502   void connect(string sourcePort, string destPort) {
503     if( jack_connect (client, toStringz(sourcePort), toStringz(destPort)) ) {
504       throw new JackException("Cannot connect the ports <" ~ sourcePort ~ "," ~ destPort ~ ">");
505     }
506   }
507 
508   void disconnect(string sourcePort, string destPort) {
509     if( jack_disconnect (client, toStringz(sourcePort), toStringz(destPort)) ) {
510       throw new JackException("Cannot disconnect the ports <" ~ sourcePort ~ "," ~ destPort ~ ">");
511     }
512   }
513 
514   void recomputeTotalLatencies() {
515     if( jack_recompute_total_latencies(client) ) {
516       throw new JackException("Cannot recompute total latencies");
517     }
518   }
519 
520   void releaseTimebase() {
521     if( jack_release_timebase(client) ) {
522       throw new JackException("Cannot release timebase");
523     }
524   }
525 
526   void transportStart() {
527     jack_transport_start(client);
528   }
529 
530   void transportStop() {
531     jack_transport_stop(client);
532   }
533 
534   void setSyncTimeout(Time timeout) { 
535     if( jack_set_sync_timeout(client, timeout) ) {
536       throw new JackException("Cannot set sync timeout");
537     }
538   }
539 
540   void transportLocate(NFrames frame){ 
541     if(jack_transport_locate(client, frame)) {
542       throw new JackException("Cannot locate frame " ~ to!string (frame));
543     }
544   }
545 
546   TransportState transportQuery(Position *pos) { 
547     return cast(TransportState) jack_transport_query(client, cast(jack_position_t *) pos);
548   }
549 
550   NFrames getCurrentTransportFrame() { 
551     return jack_get_current_transport_frame(client);
552   }
553 
554   void trasnportReposition(Position *pos) {
555     if(jack_transport_reposition(client, cast(jack_position_t *) pos)) {
556       throw new JackException("Cannot reposition");
557     }
558   }
559 
560   void setProcessCallback(ProcessCallback callback, void* data) {
561     if(jack_set_process_callback (client, callback, data)) {
562       throw new JackException("Cannot set process callback");
563     }
564   }
565 
566   void setShutdownCallback(ShutdownCallback callback, void* data) {
567     jack_on_shutdown (client, callback, data);
568   }
569 
570   void setFreewheelCallback(FreewheelCallback callback, void* data) { 
571     if(jack_set_freewheel_callback (client, callback, data)) {
572       throw new JackException("Cannot set freewheel callback");
573     }
574   }
575 
576   void setBufferSizeCallback(BufferSizeCallback callback, void* data) { 
577     if(jack_set_buffer_size_callback (client, callback, data)) {
578       throw new JackException("Cannot set buffer size callback");
579     }
580   }
581 
582   void setSampleRateCallback(SampleRateCallback callback, void* data) { 
583     if(jack_set_sample_rate_callback (client, callback, data)) {
584       throw new JackException("Cannot set sample rate callback");
585     }
586   }
587 
588   void setClientRegistrationCallback(ClientRegistrationCallback callback, void* data) { 
589     if(jack_set_client_registration_callback (client, cast(_JackClientRegistrationCallback ) callback, data)) {
590       throw new JackException("Cannot set client registration callback");
591     }
592   }
593 
594   void setPortRegistrationCallback(PortRegistrationCallback callback, void* data) { 
595     if(jack_set_port_registration_callback (client, callback, data)) {
596       throw new JackException("Cannot set port registration callback");
597     }
598   }
599 
600   void setPortConnectCallback(PortConnectCallback callback, void* data) { 
601     if(jack_set_port_connect_callback (client, cast(_JackPortConnectCallback) callback, data)) {
602       throw new JackException("Cannot set port connect callback");
603     }
604   }
605 
606   void setPortRenameCallback(PortRenameCallback callback, void* data) { 
607     if(jack_set_port_rename_callback (client, cast(_JackPortRenameCallback) callback, data)) {
608       throw new JackException("Cannot set port rename callback");
609     }
610   }
611 
612   void setGraphOrderCallback(GraphOrderCallback callback, void* data) { 
613     if(jack_set_graph_order_callback (client, callback, data)) {
614       throw new JackException("Cannot set graph order callback");
615     }
616   }
617 
618   void setXRunCallback(XRunCallback callback, void* data) { 
619     if(jack_set_xrun_callback (client, callback, data)) {
620       throw new JackException("Cannot set xrun callback");
621     }
622   }
623 
624   void setLatencyCallback(LatencyCallback callback, void* data) { 
625     if(jack_set_latency_callback (client, cast(_JackLatencyCallback) callback, data)) {
626       throw new JackException("Cannot set latency callback");
627     }
628   }
629 
630   void setSyncCallback(SyncCallback callback, void* data) {
631     if(jack_set_sync_callback(client, cast(_JackSyncCallback) callback, data)) {
632       throw new JackException("Cannot set sync callback");
633     }
634   }
635 
636   void setTimebaseCallback(int conditional, TimebaseCallback callback, void* data) {
637     if (jack_set_timebase_callback(client, conditional, cast(_JackTimebaseCallback) callback, data)) {
638       throw new JackException("Cannot set timebase callback");
639     }
640   }
641 
642   Time framesToTime(NFrames frames) {
643     return jack_frames_to_time(client, frames);
644   }
645 
646   NFrames timeToFrames(Time time) {
647     return jack_time_to_frames(client, time);
648   }
649 
650 
651   string name() {
652     return to!string( jack_get_client_name(client) );
653   }
654 
655   ThreadId threadId() {
656     return jack_client_thread_id(client);
657   }
658 
659   bool isRealtime() {
660     return (jack_is_realtime(client) != 0);
661   }
662 
663   float cpuLoad() {
664     return jack_cpu_load(client);
665   }
666 
667   NFrames samplerate() {
668     return jack_get_sample_rate(client);
669   }
670 
671   NFrames buffersize() {
672     return jack_get_buffer_size(client);
673   }
674 
675   NFrames framesSinceCycleStart() {
676     return jack_frames_since_cycle_start(client);
677   }
678 
679   NFrames frameTime() {
680     return jack_frame_time(client);
681   }
682 
683   NFrames lastFrameTime() {
684     return jack_last_frame_time(client);
685   }
686 }
687 
688 
689 // ######### Global functions
690 
691 static Client clientOpen(string clientName, Options options, out Status status, string serverName) {
692   jack_client_t* client;
693 
694   if( status & Options.ServerName) {
695     client = jack_client_open (toStringz(clientName), options, cast(jack_status_t *) &status, toStringz(serverName));
696   } else {
697     client = jack_client_open (toStringz(clientName), options, cast(jack_status_t *) &status);
698   }
699 
700   if(client == null) {
701     throw new JackException("Cannot open client", status);
702   }
703 
704   return new ClientImplementation(client);
705 }
706 
707 static Version getVersion() {
708   Version result;
709   jack_get_version(&result.major, &result.minor, &result.micro, &result.proto);
710   return result;
711 }
712 
713 static string getVersionString() {
714   return to!string (jack_get_version_string());
715 }
716 
717 static int getClientPID(string name) {
718   return jack_get_client_pid(toStringz(name));
719 }
720 
721 static int portNameSize() {
722   return jack_port_name_size();
723 }
724 
725 static int portTypeSize() {
726   return jack_port_type_size();
727 }
728 
729 static Time getTime() {
730   return jack_get_time();
731 }
732