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