| @@ -0,0 +1,482 @@ | |||
| /* | |||
| * Carla Jack Plugin | |||
| * Copyright (C) 2014 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| * published by the Free Software Foundation; either version 2 of | |||
| * the License, or any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||
| */ | |||
| #include "CarlaEngine.hpp" | |||
| #include "CarlaHost.h" | |||
| #include "CarlaUtils.hpp" | |||
| #include "jackbridge/JackBridge.hpp" | |||
| //#include "CarlaEngine.hpp" | |||
| //#include "CarlaPlugin.hpp" | |||
| //#include "CarlaBackendUtils.hpp" | |||
| //#include "CarlaBridgeUtils.hpp" | |||
| //#include "CarlaMIDI.h" | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| struct _jack_client { | |||
| JackShutdownCallback shutdown_cb; | |||
| void* shutdown_ptr; | |||
| JackProcessCallback process_cb; | |||
| void* process_ptr; | |||
| _jack_client() | |||
| { | |||
| clear(); | |||
| } | |||
| void clear() | |||
| { | |||
| shutdown_cb = nullptr; | |||
| shutdown_ptr = nullptr; | |||
| process_cb = nullptr; | |||
| process_ptr = nullptr; | |||
| } | |||
| }; | |||
| static jack_client_t gJackClient; | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| struct _jack_port { | |||
| bool used; | |||
| char name[128+1]; | |||
| _jack_port() | |||
| : used(false) {} | |||
| }; | |||
| // system ports | |||
| static jack_port_t gPortSystemIn1; // 0 | |||
| static jack_port_t gPortSystemIn2; // 1 | |||
| static jack_port_t gPortSystemOut1; // 2 | |||
| static jack_port_t gPortSystemOut2; // 3 | |||
| // client ports | |||
| static jack_port_t gPortAudioIn1; // 4 | |||
| static jack_port_t gPortAudioIn2; // 5 | |||
| static jack_port_t gPortAudioOut1; // 6 | |||
| static jack_port_t gPortAudioOut2; // 7 | |||
| static jack_port_t gPortMidiIn; // 8 | |||
| static jack_port_t gPortMidiOut; // 9 | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT jack_client_t* jack_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...); | |||
| CARLA_EXPORT int jack_client_close(jack_client_t* client); | |||
| CARLA_EXPORT int jack_client_name_size(); | |||
| CARLA_EXPORT char* jack_get_client_name(jack_client_t* client); | |||
| CARLA_EXPORT int jack_activate(jack_client_t* client); | |||
| CARLA_EXPORT int jack_deactivate(jack_client_t* client); | |||
| CARLA_EXPORT int jack_is_realtime(jack_client_t* client); | |||
| CARLA_EXPORT jack_nframes_t jack_get_sample_rate(jack_client_t* client); | |||
| CARLA_EXPORT jack_nframes_t jack_get_buffer_size(jack_client_t* client); | |||
| CARLA_EXPORT jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); | |||
| CARLA_EXPORT const char* jack_port_name(const jack_port_t* port); | |||
| CARLA_EXPORT const char** jack_get_ports(jack_client_t*, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); | |||
| CARLA_EXPORT jack_port_t* jack_port_by_name(jack_client_t* client, const char* port_name); | |||
| CARLA_EXPORT jack_port_t* jack_port_by_id(jack_client_t* client, jack_port_id_t port_id); | |||
| CARLA_EXPORT int jack_connect(jack_client_t* client, const char* source_port, const char* destination_port); | |||
| CARLA_EXPORT int jack_disconnect(jack_client_t* client, const char* source_port, const char* destination_port); | |||
| CARLA_EXPORT void jack_on_shutdown(jack_client_t* client, JackShutdownCallback function, void* arg); | |||
| CARLA_EXPORT int jack_set_process_callback(jack_client_t* client, JackProcessCallback process_callback, void* arg); | |||
| CARLA_EXPORT void jack_set_info_function(void (*func)(const char*)); | |||
| CARLA_EXPORT void jack_set_error_function(void (*func)(const char*)); | |||
| CARLA_EXPORT void jack_free(void* ptr); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| typedef void (*jack_error_callback)(const char* msg); | |||
| typedef void (*jack_info_callback)(const char* msg); | |||
| jack_error_callback sErrorCallback = nullptr; | |||
| jack_info_callback sInfoCallback = nullptr; | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| jack_client_t* jack_client_open(const char* client_name, jack_options_t /*options*/, jack_status_t* status, ...) | |||
| { | |||
| if (carla_is_engine_running()) | |||
| { | |||
| if (status != nullptr) | |||
| *status = JackServerStarted; | |||
| return nullptr; | |||
| } | |||
| if (! carla_engine_init("JACK", client_name)) | |||
| { | |||
| if (status != nullptr) | |||
| *status = JackServerFailed; | |||
| return nullptr; | |||
| } | |||
| if (! gPortSystemIn1.used) | |||
| { | |||
| gPortSystemIn1.used = true; | |||
| gPortSystemIn2.used = true; | |||
| gPortSystemOut1.used = true; | |||
| gPortSystemOut2.used = true; | |||
| std::strcpy(gPortSystemIn1.name, "system:capture_1"); | |||
| std::strcpy(gPortSystemIn2.name, "system:capture_2"); | |||
| std::strcpy(gPortSystemOut1.name, "system:playback_1"); | |||
| std::strcpy(gPortSystemOut2.name, "system:playback_2"); | |||
| } | |||
| return &gJackClient; | |||
| } | |||
| int jack_client_close(jack_client_t* client) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, -1); | |||
| if (! carla_is_engine_running()) | |||
| return -1; | |||
| carla_engine_close(); | |||
| gJackClient.clear(); | |||
| gPortAudioIn1.used = 0; | |||
| gPortAudioIn2.used = 0; | |||
| gPortAudioOut1.used = 0; | |||
| gPortAudioOut2.used = 0; | |||
| gPortMidiIn.used = 0; | |||
| gPortMidiOut.used = 0; | |||
| return 0; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| int jack_client_name_size() | |||
| { | |||
| return 32+1; // same as JACK1 | |||
| } | |||
| char* jack_get_client_name(jack_client_t* client) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, nullptr); | |||
| if (const CarlaEngine* const engine = carla_get_engine()) | |||
| return const_cast<char*>(engine->getName()); | |||
| return nullptr; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| int jack_activate(jack_client_t* client) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, -1); | |||
| return 0; | |||
| } | |||
| int jack_deactivate(jack_client_t* client) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, -1); | |||
| return 0; | |||
| } | |||
| int jack_is_realtime(jack_client_t* client) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, 0); | |||
| return 1; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| jack_nframes_t jack_get_sample_rate(jack_client_t* client) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, 0); | |||
| return static_cast<uint32_t>(carla_get_sample_rate()); | |||
| } | |||
| jack_nframes_t jack_get_buffer_size(jack_client_t* client) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, 0); | |||
| return carla_get_buffer_size(); | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, nullptr); | |||
| if (std::strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) == 0) | |||
| { | |||
| if (flags & JackPortIsInput) | |||
| { | |||
| if (gPortAudioIn1.used && gPortAudioIn2.used) | |||
| return nullptr; | |||
| if (! gPortAudioIn1.used) | |||
| { | |||
| std::strncpy(gPortAudioIn1.name, port_name, 128); | |||
| return &gPortAudioIn1; | |||
| } | |||
| else | |||
| { | |||
| std::strncpy(gPortAudioIn2.name, port_name, 128); | |||
| return &gPortAudioIn2; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| if (gPortAudioOut1.used && gPortAudioOut2.used) | |||
| return nullptr; | |||
| if (! gPortAudioOut1.used) | |||
| { | |||
| std::strncpy(gPortAudioOut1.name, port_name, 128); | |||
| return &gPortAudioOut1; | |||
| } | |||
| else | |||
| { | |||
| std::strncpy(gPortAudioOut2.name, port_name, 128); | |||
| return &gPortAudioOut2; | |||
| } | |||
| } | |||
| } | |||
| if (std::strcmp(port_type, JACK_DEFAULT_MIDI_TYPE) == 0) | |||
| { | |||
| if (flags & JackPortIsInput) | |||
| { | |||
| if (gPortMidiIn.used) | |||
| return nullptr; | |||
| std::strncpy(gPortMidiIn.name, port_name, 128); | |||
| return &gPortMidiIn; | |||
| } | |||
| else | |||
| { | |||
| if (gPortMidiOut.used) | |||
| return nullptr; | |||
| std::strncpy(gPortMidiOut.name, port_name, 128); | |||
| return &gPortMidiOut; | |||
| } | |||
| } | |||
| return nullptr; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| const char* jack_port_name(const jack_port_t* port) | |||
| { | |||
| return port->name; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| const char** jack_get_ports(jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, nullptr); | |||
| if (port_name_pattern != nullptr) | |||
| { | |||
| if (std::strstr("system:playback_", port_name_pattern) == nullptr) | |||
| return nullptr; | |||
| if (std::strstr("system:capture_", port_name_pattern) == nullptr) | |||
| return nullptr; | |||
| } | |||
| if (type_name_pattern != nullptr) | |||
| { | |||
| if (std::strstr(JACK_DEFAULT_AUDIO_TYPE, type_name_pattern) == nullptr) | |||
| return nullptr; | |||
| } | |||
| uint numPorts = 0; | |||
| if (flags == 0) | |||
| { | |||
| numPorts = 4; | |||
| } | |||
| else | |||
| { | |||
| if (flags & JackPortIsInput) | |||
| numPorts += 2; | |||
| if (flags & JackPortIsOutput) | |||
| numPorts += 2; | |||
| } | |||
| if (numPorts == 0) | |||
| return nullptr; | |||
| const char** const ports = new const char*[numPorts+1]; | |||
| uint i = 0; | |||
| if (flags == 0 || (flags & JackPortIsInput) != 0) | |||
| { | |||
| ports[i++] = gPortSystemIn1.name; | |||
| ports[i++] = gPortSystemIn1.name; | |||
| } | |||
| if (flags == 0 || (flags & JackPortIsOutput) != 0) | |||
| { | |||
| ports[i++] = gPortSystemOut1.name; | |||
| ports[i++] = gPortSystemOut1.name; | |||
| } | |||
| ports[i++] = nullptr; | |||
| return ports; | |||
| } | |||
| jack_port_t* jack_port_by_name(jack_client_t* client, const char* port_name) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, nullptr); | |||
| CARLA_SAFE_ASSERT_RETURN(gPortSystemIn1.used, nullptr); | |||
| if (std::strcmp(port_name, gPortSystemIn1.name) == 0) | |||
| return &gPortSystemIn1; | |||
| if (std::strcmp(port_name, gPortSystemIn2.name) == 0) | |||
| return &gPortSystemIn2; | |||
| if (std::strcmp(port_name, gPortSystemOut1.name) == 0) | |||
| return &gPortSystemOut1; | |||
| if (std::strcmp(port_name, gPortSystemOut2.name) == 0) | |||
| return &gPortSystemOut2; | |||
| if (gPortAudioIn1.used && std::strcmp(port_name, gPortAudioIn1.name) == 0) | |||
| return &gPortAudioIn1; | |||
| if (gPortAudioIn2.used && std::strcmp(port_name, gPortAudioIn2.name) == 0) | |||
| return &gPortAudioIn2; | |||
| if (gPortAudioOut1.used && std::strcmp(port_name, gPortAudioOut1.name) == 0) | |||
| return &gPortAudioOut1; | |||
| if (gPortAudioOut2.used && std::strcmp(port_name, gPortAudioOut2.name) == 0) | |||
| return &gPortAudioOut2; | |||
| if (gPortMidiIn.used && std::strcmp(port_name, gPortMidiIn.name) == 0) | |||
| return &gPortMidiIn; | |||
| if (gPortMidiOut.used && std::strcmp(port_name, gPortMidiOut.name) == 0) | |||
| return &gPortMidiOut; | |||
| return nullptr; | |||
| } | |||
| jack_port_t* jack_port_by_id(jack_client_t* client, jack_port_id_t port_id) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, nullptr); | |||
| CARLA_SAFE_ASSERT_RETURN(gPortSystemIn1.used, nullptr); | |||
| switch (port_id) | |||
| { | |||
| case 0: | |||
| return &gPortSystemIn1; | |||
| case 1: | |||
| return &gPortSystemIn2; | |||
| case 2: | |||
| return &gPortSystemOut1; | |||
| case 3: | |||
| return &gPortSystemOut2; | |||
| case 4: | |||
| if (gPortAudioIn1.used) | |||
| return &gPortAudioIn1; | |||
| break; | |||
| case 5: | |||
| if (gPortAudioIn2.used) | |||
| return &gPortAudioIn2; | |||
| break; | |||
| case 6: | |||
| if (gPortAudioOut1.used) | |||
| return &gPortAudioOut1; | |||
| break; | |||
| case 7: | |||
| if (gPortAudioOut2.used) | |||
| return &gPortAudioOut2; | |||
| break; | |||
| case 8: | |||
| if (gPortMidiIn.used) | |||
| return &gPortMidiIn; | |||
| break; | |||
| case 9: | |||
| if (gPortMidiOut.used) | |||
| return &gPortMidiOut; | |||
| break; | |||
| } | |||
| return nullptr; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| int jack_connect(jack_client_t* client, const char*, const char*) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, -1); | |||
| return 0; | |||
| } | |||
| int jack_disconnect(jack_client_t* client, const char*, const char*) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, -1); | |||
| return 0; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| void jack_on_shutdown(jack_client_t* client, JackShutdownCallback function, void* arg) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient,); | |||
| gJackClient.shutdown_cb = function; | |||
| gJackClient.shutdown_ptr = arg; | |||
| } | |||
| int jack_set_process_callback(jack_client_t* client, JackProcessCallback process_callback, void* arg) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(client == &gJackClient, -1); | |||
| gJackClient.process_cb = process_callback; | |||
| gJackClient.process_ptr = arg; | |||
| return 0; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| void jack_set_error_function(void (*func)(const char*)) | |||
| { | |||
| sErrorCallback = func; | |||
| } | |||
| void jack_set_info_function(void (*func)(const char*)) | |||
| { | |||
| sInfoCallback = func; | |||
| } | |||
| void jack_free(void* ptr) | |||
| { | |||
| delete[] (char**)ptr; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT void carla_register_all_plugins(); | |||
| void carla_register_all_plugins() {} | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||