/* * Carla JACK API for external applications * Copyright (C) 2016-2017 Filipe Coelho * * 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. */ // need to include this first #include "CarlaDefines.h" // then define this for bridge utils #define BUILD_BRIDGE 1 // now include a bunch of stuff #include "CarlaBackendUtils.hpp" #include "CarlaBridgeUtils.hpp" #include "CarlaMIDI.h" #include "CarlaMutex.hpp" #include "LinkedList.hpp" #include "AppConfig.h" #include "juce_core/juce_core.h" #ifdef __SSE2_MATH__ # include #endif #if 0 #include #include #include #include #include #endif // must be last #include "jackbridge/JackBridge.hpp" // small check to not hurt myself #ifdef JACKBRIDGE_DIRECT # error "Cannot create custom jack server while linking to libjack directly" #endif using juce::File; using juce::MemoryBlock; using juce::String; using juce::Time; using juce::Thread; CARLA_BACKEND_START_NAMESPACE // -------------------------------------------------------------------------------------------------------------------- struct JackPortState { char* name; char* fullname; void* buffer; uint index; uint flags; bool isSystem; JackPortState() : name(nullptr), fullname(nullptr), buffer(nullptr), index(0), flags(0), isSystem(false) {} JackPortState(const char* const cn, const char* const pn, const uint i, const uint f, const bool sys) : name(strdup(pn)), fullname(nullptr), buffer(nullptr), index(i), flags(f), isSystem(sys) { char strBuf[STR_MAX+1]; snprintf(strBuf, STR_MAX, "%s:%s", cn, pn); strBuf[STR_MAX] = '\0'; fullname = strdup(strBuf); } ~JackPortState() { free(name); free(fullname); } }; struct JackClientState { bool activated; bool prematurelyActivated; char* name; uint32_t bufferSize; double sampleRate; bool playing; jack_position_t position; LinkedList audioIns; LinkedList audioOuts; uint32_t fakeIns, fakeOuts; LinkedList midiIns; LinkedList midiOuts; JackProcessCallback process; void* processPtr; JackShutdownCallback shutdown; void* shutdownPtr; JackClientState() : activated(false), prematurelyActivated(false), name(nullptr), bufferSize(0), sampleRate(0.0), playing(false), audioIns(), audioOuts(), fakeIns(0), fakeOuts(0), midiIns(), midiOuts(), process(nullptr), processPtr(nullptr), shutdown(nullptr), shutdownPtr(nullptr) { carla_zeroStruct(position); } ~JackClientState() { free(name); } }; class CarlaJackClient : public Thread { public: JackClientState fState; CarlaJackClient(const char* const audioPoolBaseName, const char* const rtClientBaseName, const char* const nonRtClientBaseName, const char* const nonRtServerBaseName) : Thread("CarlaJackClient"), fState(), fShmAudioPool(), fShmRtClientControl(), fShmNonRtClientControl(), fShmNonRtServerControl(), fBaseNameAudioPool(audioPoolBaseName), fBaseNameRtClientControl(rtClientBaseName), fBaseNameNonRtClientControl(nonRtClientBaseName), fBaseNameNonRtServerControl(nonRtServerBaseName), fIsOffline(false), fFirstIdle(true), fLastPingTime(-1), fAudioIns(0), fAudioOuts(0) { carla_debug("CarlaJackClient::CarlaJackClient(\"%s\", \"%s\", \"%s\", \"%s\")", audioPoolBaseName, rtClientBaseName, nonRtClientBaseName, nonRtServerBaseName); } ~CarlaJackClient() noexcept override { carla_debug("CarlaJackClient::~CarlaJackClient()"); clear(); } bool init(const char* const clientName) { carla_debug("CarlaJackClient::init(\"%s\")", clientName); if (! fShmAudioPool.attachClient(fBaseNameAudioPool)) { carla_stderr("Failed to attach to audio pool shared memory"); return false; } if (! fShmRtClientControl.attachClient(fBaseNameRtClientControl)) { clear(); carla_stderr("Failed to attach to rt client control shared memory"); return false; } if (! fShmRtClientControl.mapData()) { clear(); carla_stderr("Failed to map rt client control shared memory"); return false; } if (! fShmNonRtClientControl.attachClient(fBaseNameNonRtClientControl)) { clear(); carla_stderr("Failed to attach to non-rt client control shared memory"); return false; } if (! fShmNonRtClientControl.mapData()) { clear(); carla_stderr("Failed to map non-rt control client shared memory"); return false; } if (! fShmNonRtServerControl.attachClient(fBaseNameNonRtServerControl)) { clear(); carla_stderr("Failed to attach to non-rt server control shared memory"); return false; } if (! fShmNonRtServerControl.mapData()) { clear(); carla_stderr("Failed to map non-rt control server shared memory"); return false; } PluginBridgeNonRtClientOpcode opcode; opcode = fShmNonRtClientControl.readOpcode(); CARLA_SAFE_ASSERT_INT(opcode == kPluginBridgeNonRtClientNull, opcode); const uint32_t shmRtClientDataSize = fShmNonRtClientControl.readUInt(); CARLA_SAFE_ASSERT_INT2(shmRtClientDataSize == sizeof(BridgeRtClientData), shmRtClientDataSize, sizeof(BridgeRtClientData)); const uint32_t shmNonRtClientDataSize = fShmNonRtClientControl.readUInt(); CARLA_SAFE_ASSERT_INT2(shmNonRtClientDataSize == sizeof(BridgeNonRtClientData), shmNonRtClientDataSize, sizeof(BridgeNonRtClientData)); const uint32_t shmNonRtServerDataSize = fShmNonRtClientControl.readUInt(); CARLA_SAFE_ASSERT_INT2(shmNonRtServerDataSize == sizeof(BridgeNonRtServerData), shmNonRtServerDataSize, sizeof(BridgeNonRtServerData)); if (shmRtClientDataSize != sizeof(BridgeRtClientData) || shmNonRtClientDataSize != sizeof(BridgeNonRtClientData) || shmNonRtServerDataSize != sizeof(BridgeNonRtServerData)) { carla_stderr2("CarlaJackClient: data size mismatch"); return false; } opcode = fShmNonRtClientControl.readOpcode(); CARLA_SAFE_ASSERT_INT(opcode == kPluginBridgeNonRtClientSetBufferSize, opcode); fState.bufferSize = fShmNonRtClientControl.readUInt(); opcode = fShmNonRtClientControl.readOpcode(); CARLA_SAFE_ASSERT_INT(opcode == kPluginBridgeNonRtClientSetSampleRate, opcode); fState.sampleRate = fShmNonRtClientControl.readDouble(); if (fState.bufferSize == 0 || carla_isZero(fState.sampleRate)) { carla_stderr2("CarlaJackClient: invalid empty state"); return false; } fState.name = strdup(clientName); // tell backend we're live { const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex); fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerPong); fShmNonRtServerControl.commitWrite(); } startThread(10); return true; } bool close() { carla_debug("CarlaEnginePlugin::close()"); fLastPingTime = -1; stopThread(5000); clear(); for (LinkedList::Itenerator it = fState.audioIns.begin2(); it.valid(); it.next()) { if (JackPortState* const jport = it.getValue(nullptr)) delete jport; } for (LinkedList::Itenerator it = fState.audioOuts.begin2(); it.valid(); it.next()) { if (JackPortState* const jport = it.getValue(nullptr)) delete jport; } fState.audioIns.clear(); fState.audioOuts.clear(); return true; } void clear() noexcept { fShmAudioPool.clear(); fShmRtClientControl.clear(); fShmNonRtClientControl.clear(); fShmNonRtServerControl.clear(); } void activate() { const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex); const bool wasFirstIdle(fFirstIdle); if (wasFirstIdle) { fFirstIdle = false; fLastPingTime = Time::currentTimeMillis(); CARLA_SAFE_ASSERT(fLastPingTime > 0); if (fState.audioIns.count() == 0 && fState.audioOuts.count() == 0) { carla_stderr("Create 2 ins, 2 outs prematurely for the client"); fState.audioIns.append(new JackPortState(fState.name, "in_1", 0, JackPortIsOutput, false)); fState.audioIns.append(new JackPortState(fState.name, "in_2", 1, JackPortIsOutput, false)); fState.fakeIns = 2; fState.audioOuts.append(new JackPortState(fState.name, "out_1", 0, JackPortIsInput, false)); fState.audioOuts.append(new JackPortState(fState.name, "out_2", 1, JackPortIsInput, false)); fState.fakeOuts = 2; fState.prematurelyActivated = true; } char bufStr[STR_MAX+1]; uint32_t bufStrSize; // kPluginBridgeNonRtServerPluginInfo1 { // uint/category, uint/hints, uint/optionsAvailable, uint/optionsEnabled, long/uniqueId fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerPluginInfo1); fShmNonRtServerControl.writeUInt(PLUGIN_CATEGORY_NONE); fShmNonRtServerControl.writeUInt(0x0); fShmNonRtServerControl.writeUInt(0x0); fShmNonRtServerControl.writeUInt(0x0); fShmNonRtServerControl.writeLong(0); fShmNonRtServerControl.commitWrite(); } // kPluginBridgeNonRtServerPluginInfo2 { // uint/size, str[] (realName), uint/size, str[] (label), uint/size, str[] (maker), uint/size, str[] (copyright) fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerPluginInfo2); carla_zeroChars(bufStr, STR_MAX); std::strncpy(bufStr, fState.name, 64); bufStrSize = carla_fixedValue(1U, 64U, static_cast(std::strlen(bufStr))); fShmNonRtServerControl.writeUInt(bufStrSize); fShmNonRtServerControl.writeCustomData(bufStr, bufStrSize); carla_zeroChars(bufStr, STR_MAX); bufStrSize = 1; fShmNonRtServerControl.writeUInt(bufStrSize); fShmNonRtServerControl.writeCustomData(bufStr, bufStrSize); fShmNonRtServerControl.writeUInt(bufStrSize); fShmNonRtServerControl.writeCustomData(bufStr, bufStrSize); fShmNonRtServerControl.writeUInt(bufStrSize); fShmNonRtServerControl.writeCustomData(bufStr, bufStrSize); fShmNonRtServerControl.commitWrite(); } // kPluginBridgeNonRtServerAudioCount { const uint32_t aIns = fState.audioIns.count(); const uint32_t aOuts = fState.audioOuts.count(); // uint/ins, uint/outs fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerAudioCount); fShmNonRtServerControl.writeUInt(aIns); fShmNonRtServerControl.writeUInt(aOuts); fShmNonRtServerControl.commitWrite(); // kPluginBridgeNonRtServerPortName for (uint32_t i=0; iname); CARLA_SAFE_ASSERT_CONTINUE(portName != nullptr && portName[0] != '\0'); // byte/type, uint/index, uint/size, str[] (name) fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerPortName); fShmNonRtServerControl.writeByte(kPluginBridgePortAudioInput); fShmNonRtServerControl.writeUInt(i); bufStrSize = static_cast(std::strlen(portName)); fShmNonRtServerControl.writeUInt(bufStrSize); fShmNonRtServerControl.writeCustomData(portName, bufStrSize); } // kPluginBridgeNonRtServerPortName for (uint32_t i=0; iname); CARLA_SAFE_ASSERT_CONTINUE(portName != nullptr && portName[0] != '\0'); // byte/type, uint/index, uint/size, str[] (name) fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerPortName); fShmNonRtServerControl.writeByte(kPluginBridgePortAudioOutput); fShmNonRtServerControl.writeUInt(i); bufStrSize = static_cast(std::strlen(portName)); fShmNonRtServerControl.writeUInt(bufStrSize); fShmNonRtServerControl.writeCustomData(portName, bufStrSize); } } fShmNonRtServerControl.waitIfDataIsReachingLimit(); // kPluginBridgeNonRtServerMidiCount { const uint32_t mIns = fState.midiIns.count(); const uint32_t mOuts = fState.midiOuts.count(); // uint/ins, uint/outs fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerMidiCount); fShmNonRtServerControl.writeUInt(mIns); fShmNonRtServerControl.writeUInt(mOuts); fShmNonRtServerControl.commitWrite(); } fShmNonRtServerControl.waitIfDataIsReachingLimit(); // ready! fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerReady); fShmNonRtServerControl.commitWrite(); fShmNonRtServerControl.waitIfDataIsReachingLimit(); // must be static fAudioIns = fState.audioIns.count(); fAudioOuts = fState.audioOuts.count(); carla_stdout("Carla Jack Client Ready!"); fLastPingTime = Time::currentTimeMillis(); } fState.activated = true; } void deactivate() { const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex); fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerUiClosed); fShmNonRtServerControl.commitWrite(); fState.activated = false; fState.prematurelyActivated = false; for (LinkedList::Itenerator it = fState.audioIns.begin2(); it.valid(); it.next()) { if (JackPortState* const jport = it.getValue(nullptr)) delete jport; } for (LinkedList::Itenerator it = fState.audioOuts.begin2(); it.valid(); it.next()) { if (JackPortState* const jport = it.getValue(nullptr)) delete jport; } fState.audioIns.clear(); fState.audioOuts.clear(); } void handleNonRtData() { for (; fShmNonRtClientControl.isDataAvailableForReading();) { const PluginBridgeNonRtClientOpcode opcode(fShmNonRtClientControl.readOpcode()); // #ifdef DEBUG if (opcode != kPluginBridgeNonRtClientPing) { static int shownNull = 0; if (opcode == kPluginBridgeNonRtClientNull) { if (shownNull > 5) continue; ++shownNull; } carla_stdout("CarlaJackClient::handleNonRtData() - got opcode: %s", PluginBridgeNonRtClientOpcode2str(opcode)); } // #endif if (opcode != kPluginBridgeNonRtClientNull && opcode != kPluginBridgeNonRtClientPingOnOff && fLastPingTime > 0) fLastPingTime = Time::currentTimeMillis(); switch (opcode) { case kPluginBridgeNonRtClientNull: break; case kPluginBridgeNonRtClientPing: { const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex); fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerPong); fShmNonRtServerControl.commitWrite(); } break; case kPluginBridgeNonRtClientPingOnOff: { const uint32_t onOff(fShmNonRtClientControl.readBool()); fLastPingTime = onOff ? Time::currentTimeMillis() : -1; } break; case kPluginBridgeNonRtClientActivate: case kPluginBridgeNonRtClientDeactivate: break; case kPluginBridgeNonRtClientSetBufferSize: fShmNonRtClientControl.readUInt(); //bufferSizeChanged(); break; case kPluginBridgeNonRtClientSetSampleRate: fShmNonRtClientControl.readDouble(); //sampleRateChanged(); break; case kPluginBridgeNonRtClientSetOffline: fIsOffline = true; //offlineModeChanged(true); break; case kPluginBridgeNonRtClientSetOnline: fIsOffline = false; //offlineModeChanged(false); break; case kPluginBridgeNonRtClientSetParameterValue: case kPluginBridgeNonRtClientSetParameterMidiChannel: case kPluginBridgeNonRtClientSetParameterMidiCC: case kPluginBridgeNonRtClientSetProgram: case kPluginBridgeNonRtClientSetMidiProgram: case kPluginBridgeNonRtClientSetCustomData: case kPluginBridgeNonRtClientSetChunkDataFile: break; case kPluginBridgeNonRtClientSetOption: fShmNonRtClientControl.readUInt(); fShmNonRtClientControl.readBool(); break; case kPluginBridgeNonRtClientSetCtrlChannel: fShmNonRtClientControl.readShort(); break; case kPluginBridgeNonRtClientPrepareForSave: { const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex); fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerSaved); fShmNonRtServerControl.commitWrite(); } break; case kPluginBridgeNonRtClientShowUI: case kPluginBridgeNonRtClientHideUI: case kPluginBridgeNonRtClientUiParameterChange: case kPluginBridgeNonRtClientUiProgramChange: case kPluginBridgeNonRtClientUiMidiProgramChange: case kPluginBridgeNonRtClientUiNoteOn: case kPluginBridgeNonRtClientUiNoteOff: break; case kPluginBridgeNonRtClientQuit: signalThreadShouldExit(); break; } } } // ------------------------------------------------------------------- protected: void run() override { carla_stderr("CarlaJackClient run START"); #ifdef __SSE2_MATH__ // Set FTZ and DAZ flags _mm_setcsr(_mm_getcsr() | 0x8040); #endif bool quitReceived = false; for (; ! quitReceived && ! threadShouldExit();) { handleNonRtData(); const BridgeRtClientControl::WaitHelper helper(fShmRtClientControl); if (! helper.ok) continue; for (; fShmRtClientControl.isDataAvailableForReading();) { const PluginBridgeRtClientOpcode opcode(fShmRtClientControl.readOpcode()); //#ifdef DEBUG if (opcode != kPluginBridgeRtClientProcess && opcode != kPluginBridgeRtClientMidiEvent) { carla_stdout("CarlaJackClientRtThread::run() - got opcode: %s", PluginBridgeRtClientOpcode2str(opcode)); } //#endif switch (opcode) { case kPluginBridgeRtClientNull: break; case kPluginBridgeRtClientSetAudioPool: { if (fShmAudioPool.data != nullptr) { jackbridge_shm_unmap(fShmAudioPool.shm, fShmAudioPool.data); fShmAudioPool.data = nullptr; } const uint64_t poolSize(fShmRtClientControl.readULong()); CARLA_SAFE_ASSERT_BREAK(poolSize > 0); fShmAudioPool.data = (float*)jackbridge_shm_map(fShmAudioPool.shm, static_cast(poolSize)); break; } case kPluginBridgeRtClientControlEventParameter: case kPluginBridgeRtClientControlEventMidiBank: case kPluginBridgeRtClientControlEventMidiProgram: case kPluginBridgeRtClientControlEventAllSoundOff: case kPluginBridgeRtClientControlEventAllNotesOff: case kPluginBridgeRtClientMidiEvent: break; case kPluginBridgeRtClientProcess: { CARLA_SAFE_ASSERT_BREAK(fShmAudioPool.data != nullptr); const CarlaMutexTryLocker cmtl(fRealtimeThreadMutex); if (cmtl.wasLocked()) { float* fdata = fShmAudioPool.data; if (fState.process == nullptr || ! fState.activated) { for (uint32_t i=0; i 0) carla_zeroFloats(fdata, fState.bufferSize*fAudioOuts); if (! fState.activated) { fShmRtClientControl.data->procFlags = 1; } } else { const BridgeTimeInfo& bridgeTimeInfo(fShmRtClientControl.data->timeInfo); uint32_t i = 0; for (LinkedList::Itenerator it = fState.audioIns.begin2(); it.valid(); it.next()) { CARLA_SAFE_ASSERT_BREAK(i++ < fAudioIns); if (JackPortState* const jport = it.getValue(nullptr)) jport->buffer = fdata; fdata += fState.bufferSize; } i=0; for (LinkedList::Itenerator it = fState.audioOuts.begin2(); it.valid(); it.next()) { CARLA_SAFE_ASSERT_BREAK(i++ < fAudioOuts); if (JackPortState* const jport = it.getValue(nullptr)) jport->buffer = fdata; fdata += fState.bufferSize; } fState.playing = bridgeTimeInfo.playing; fState.position.frame = bridgeTimeInfo.frame; fState.position.usecs = bridgeTimeInfo.usecs; if (bridgeTimeInfo.valid & 0x1 /* kValidBBT */) { fState.position.valid = JackPositionBBT; fState.position.bar = bridgeTimeInfo.bar; fState.position.beat = bridgeTimeInfo.beat; fState.position.tick = bridgeTimeInfo.tick; fState.position.beats_per_bar = bridgeTimeInfo.beatsPerBar; fState.position.beat_type = bridgeTimeInfo.beatType; fState.position.ticks_per_beat = bridgeTimeInfo.ticksPerBeat; fState.position.beats_per_minute = bridgeTimeInfo.beatsPerMinute; fState.position.bar_start_tick = bridgeTimeInfo.barStartTick; } else { fState.position.valid = static_cast(0); } fState.process(fState.bufferSize, fState.processPtr); } } else { carla_stderr2("CarlaJackClient: fRealtimeThreadMutex tryLock failed"); } carla_zeroBytes(fShmRtClientControl.data->midiOut, kBridgeRtClientDataMidiOutSize); break; } case kPluginBridgeRtClientQuit: quitReceived = true; signalThreadShouldExit(); break; } } } //callback(ENGINE_CALLBACK_ENGINE_STOPPED, 0, 0, 0, 0.0f, nullptr); if (quitReceived) { carla_stderr("CarlaJackClient run END - quit by carla"); ::kill(::getpid(), SIGTERM); } else { const char* const message("Plugin bridge error, process thread has stopped"); const std::size_t messageSize(std::strlen(message)); bool activated; { const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex); activated = fState.activated; if (activated) { carla_stderr("CarlaJackClient run END - quit error"); fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerError); fShmNonRtServerControl.writeUInt(messageSize); fShmNonRtServerControl.writeCustomData(message, messageSize); fShmNonRtServerControl.commitWrite(); } else { carla_stderr("CarlaJackClient run END - quit itself"); } } if (activated && fState.shutdown != nullptr) fState.shutdown(fState.shutdownPtr); } } private: BridgeAudioPool fShmAudioPool; BridgeRtClientControl fShmRtClientControl; BridgeNonRtClientControl fShmNonRtClientControl; BridgeNonRtServerControl fShmNonRtServerControl; CarlaString fBaseNameAudioPool; CarlaString fBaseNameRtClientControl; CarlaString fBaseNameNonRtClientControl; CarlaString fBaseNameNonRtServerControl; bool fIsOffline; bool fFirstIdle; int64_t fLastPingTime; uint32_t fAudioIns; uint32_t fAudioOuts; CarlaMutex fRealtimeThreadMutex; CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaJackClient) }; CARLA_BACKEND_END_NAMESPACE // -------------------------------------------------------------------------------------------------------------------- CARLA_BACKEND_USE_NAMESPACE static CarlaJackClient* gClient = nullptr; static int gClientRefCount = 0; CARLA_EXPORT jack_client_t* jack_client_open(const char* client_name, jack_options_t /*options*/, jack_status_t* status, ...) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); if (gClient != nullptr) { ++gClientRefCount; return (jack_client_t*)gClient; } const char* const shmIds(std::getenv("CARLA_SHM_IDS")); if (shmIds == nullptr || std::strlen(shmIds) != 6*4) { if (status != nullptr) *status = JackFailure; return nullptr; } char audioPoolBaseName[6+1]; char rtClientBaseName[6+1]; char nonRtClientBaseName[6+1]; char nonRtServerBaseName[6+1]; std::memcpy(audioPoolBaseName, shmIds+6*0, 6); std::memcpy(rtClientBaseName, shmIds+6*1, 6); std::memcpy(nonRtClientBaseName, shmIds+6*2, 6); std::memcpy(nonRtServerBaseName, shmIds+6*3, 6); audioPoolBaseName[6] = '\0'; rtClientBaseName[6] = '\0'; nonRtClientBaseName[6] = '\0'; nonRtServerBaseName[6] = '\0'; CarlaJackClient* const client = new CarlaJackClient(audioPoolBaseName, rtClientBaseName, nonRtClientBaseName, nonRtServerBaseName); if (! client->init(client_name)) { if (status != nullptr) *status = JackServerError; return nullptr; } gClient = client; ++gClientRefCount; return (jack_client_t*)client; } CARLA_EXPORT jack_client_t* jack_client_new(const char* client_name) { return jack_client_open(client_name, JackNullOption, nullptr); } CARLA_EXPORT int jack_client_close(jack_client_t* client) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); JackClientState& jstate(jclient->fState); if (jstate.activated) jclient->deactivate(); if (--gClientRefCount == 0) { #if 0 static bool ignoreFirstClientClose = true; if (ignoreFirstClientClose) { ignoreFirstClientClose = false; return 0; } #endif jclient->close(); delete jclient; gClient = nullptr; } return 0; } 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_stdout("CarlaJackClient :: %s | %s %s %lu", __FUNCTION__, port_name, port_type, flags); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, nullptr); JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(port_name != nullptr && port_name[0] != '\0', nullptr); CARLA_SAFE_ASSERT_RETURN(port_type != nullptr && port_type[0] != '\0', nullptr); if (std::strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) == 0) { uint32_t index; /**/ if (flags & JackPortIsInput) { if (jstate.prematurelyActivated) { CARLA_SAFE_ASSERT_RETURN(jstate.fakeIns > 0, nullptr); jstate.fakeIns -= 1; index = jstate.audioIns.count() - jstate.fakeIns - 1; } else { index = jstate.audioIns.count(); jstate.audioIns.append(new JackPortState(jstate.name, port_name, index, flags, false)); } return (jack_port_t*)jstate.audioIns.getAt(index, nullptr); } else if (flags & JackPortIsOutput) { if (jstate.prematurelyActivated) { CARLA_SAFE_ASSERT_RETURN(jstate.fakeOuts > 0, nullptr); jstate.fakeOuts -= 1; index = jstate.audioOuts.count() - jstate.fakeOuts - 1; } else { index = jstate.audioOuts.count(); jstate.audioOuts.append(new JackPortState(jstate.name, port_name, index, flags, false)); } return (jack_port_t*)jstate.audioOuts.getAt(index, nullptr); } carla_stderr2("Invalid port flags '%x'", flags); return nullptr; } carla_stderr2("Invalid port type '%s'", port_type); return nullptr; } CARLA_EXPORT int jack_port_unregister(jack_client_t* client, jack_port_t* port) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); JackPortState* const jport = (JackPortState*)port; CARLA_SAFE_ASSERT_RETURN(jport != nullptr, 1); CARLA_SAFE_ASSERT_RETURN(! jport->isSystem, 1); JackClientState& jstate(jclient->fState); //CARLA_SAFE_ASSERT_RETURN(! jstate.activated, 1); if (jport->flags & JackPortIsOutput) { if (jstate.prematurelyActivated) { CARLA_SAFE_ASSERT_RETURN(jstate.fakeIns < 2, 1); jstate.fakeIns += 1; } else { CARLA_SAFE_ASSERT_RETURN(jstate.audioIns.removeOne(jport), 1); } } else { if (jstate.prematurelyActivated) { CARLA_SAFE_ASSERT_RETURN(jstate.fakeOuts < 2, 1); jstate.fakeOuts += 1; } else { CARLA_SAFE_ASSERT_RETURN(jstate.audioOuts.removeOne(jport), 1); } } return 0; } CARLA_EXPORT void* jack_port_get_buffer(jack_port_t* port, jack_nframes_t) { JackPortState* const jport = (JackPortState*)port; CARLA_SAFE_ASSERT_RETURN(jport != nullptr, nullptr); return jport->buffer; } CARLA_EXPORT int jack_set_process_callback(jack_client_t* client, JackProcessCallback callback, void* arg) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(! jstate.activated, 1); jstate.process = callback; jstate.processPtr = arg; return 0; } CARLA_EXPORT void jack_on_shutdown(jack_client_t* client, JackShutdownCallback callback, void* arg) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr,); JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(! jstate.activated,); jstate.shutdown = callback; jstate.shutdownPtr = arg; } CARLA_EXPORT int jack_activate(jack_client_t* client) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); const JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(! jstate.activated, 1); #if 0 // needed for pulseaudio static bool skipFirstActivate = true; if (skipFirstActivate) { skipFirstActivate = false; return 0; } #endif jclient->activate(); return 0; } CARLA_EXPORT int jack_deactivate(jack_client_t* /*client*/) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); //CarlaJackClient* const jclient = (CarlaJackClient*)client; //CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); //JackClientState& jstate(jclient->fState); //CARLA_SAFE_ASSERT_RETURN(jstate.activated, 1); //jclient->deactivate(); return 0; } CARLA_EXPORT char* jack_get_client_name(jack_client_t* client) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, nullptr); const JackClientState& jstate(jclient->fState); return jstate.name; } CARLA_EXPORT jack_nframes_t jack_get_buffer_size(jack_client_t* client) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); const JackClientState& jstate(jclient->fState); return jstate.bufferSize; } CARLA_EXPORT jack_nframes_t jack_get_sample_rate(jack_client_t* client) { carla_debug("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); const JackClientState& jstate(jclient->fState); return jstate.sampleRate; } // -------------------------------------------------------------------------------------------------------------------- CARLA_EXPORT void jack_set_error_function(void (*func)(const char *)) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); (void)func; } CARLA_EXPORT int jack_set_sync_callback(jack_client_t* client, JackSyncCallback /*callback*/, void* /*arg*/) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); const JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(! jstate.activated, 1); // TODO return 0; } CARLA_EXPORT int jack_set_timebase_callback(jack_client_t* client, int, JackTimebaseCallback, void*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); const JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(! jstate.activated, 1); // TODO return EBUSY; } CARLA_EXPORT int jack_set_buffer_size_callback(jack_client_t* client, JackBufferSizeCallback /*callback*/, void* /*arg*/) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); const JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(! jstate.activated, 1); // TODO return 0; } CARLA_EXPORT int jack_set_sample_rate_callback(jack_client_t* client, JackSampleRateCallback /*callback*/, void* /*arg*/) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); const JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(! jstate.activated, 1); // TODO return 0; } CARLA_EXPORT int jack_set_client_registration_callback(jack_client_t* client, JackClientRegistrationCallback, void*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); const JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(! jstate.activated, 1); carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return 0; } CARLA_EXPORT int jack_set_port_registration_callback(jack_client_t* client, JackPortRegistrationCallback, void*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); const JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(! jstate.activated, 1); return 0; } CARLA_EXPORT int jack_set_port_connect_callback(jack_client_t* client, JackPortConnectCallback, void*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); const JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(! jstate.activated, 1); return 0; } CARLA_EXPORT int jack_set_graph_order_callback(jack_client_t* client, JackGraphOrderCallback, void*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); const JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(! jstate.activated, 1); return 0; } CARLA_EXPORT int jack_is_realtime(jack_client_t*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return 0; } CARLA_EXPORT int jack_transport_locate(jack_client_t*, jack_nframes_t) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return 1; } CARLA_EXPORT jack_transport_state_t jack_transport_query(const jack_client_t* client, jack_position_t* pos) { carla_debug("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, JackTransportStopped); const JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(jstate.activated, JackTransportStopped); if (pos != nullptr) std::memcpy(pos, &jstate.position, sizeof(jack_position_t)); return jstate.playing ? JackTransportRolling : JackTransportStopped; } CARLA_EXPORT int jack_transport_reposition(jack_client_t*, const jack_position_t*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return EINVAL; } CARLA_EXPORT void jack_transport_start(jack_client_t*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); } CARLA_EXPORT void jack_transport_stop (jack_client_t*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); } CARLA_EXPORT pthread_t jack_client_thread_id(jack_client_t* client) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); const JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(jstate.activated, 0); return (pthread_t)jclient->getThreadId(); } CARLA_EXPORT jack_nframes_t jack_frame_time(const jack_client_t* client) { carla_debug("CarlaJackClient :: %s", __FUNCTION__); CarlaJackClient* const jclient = (CarlaJackClient*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); const JackClientState& jstate(jclient->fState); CARLA_SAFE_ASSERT_RETURN(jstate.activated, 0); return jstate.position.usecs; } CARLA_EXPORT void jack_free(void* ptr) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); free(ptr); } CARLA_EXPORT jack_nframes_t jack_midi_get_event_count(void*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return 0; } CARLA_EXPORT int jack_midi_event_get(jack_midi_event_t*, void*, uint32_t) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return ENODATA; } CARLA_EXPORT const char* jack_port_short_name(const jack_port_t* port) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); JackPortState* const jport = (JackPortState*)port; CARLA_SAFE_ASSERT_RETURN(jport != nullptr, nullptr); return jport->name; } CARLA_EXPORT const char* jack_port_name(const jack_port_t* port) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); JackPortState* const jport = (JackPortState*)port; CARLA_SAFE_ASSERT_RETURN(jport != nullptr, nullptr); return jport->fullname; } CARLA_EXPORT int jack_port_flags(const jack_port_t* port) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); JackPortState* const jport = (JackPortState*)port; CARLA_SAFE_ASSERT_RETURN(jport != nullptr, 0); return jport->flags; } CARLA_EXPORT const char* jack_port_type(const jack_port_t* port) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); JackPortState* const jport = (JackPortState*)port; CARLA_SAFE_ASSERT_RETURN(jport != nullptr, nullptr); // TODO return JACK_DEFAULT_AUDIO_TYPE; } CARLA_EXPORT jack_uuid_t jack_port_uuid(const jack_port_t*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return 0; } CARLA_EXPORT int jack_client_real_time_priority(jack_client_t*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return -1; } CARLA_EXPORT int jack_connect(jack_client_t*, const char*, const char*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return 0; } CARLA_EXPORT int jack_disconnect(jack_client_t*, const char*, const char*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return 0; } CARLA_EXPORT int jack_port_disconnect(jack_client_t*, jack_port_t*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return 0; } CARLA_EXPORT const char** jack_get_ports(jack_client_t*, const char* a, const char* b, unsigned long flags) { carla_stdout("CarlaJackClient :: %s | %s %s %li", __FUNCTION__, a, b, flags); static const char* capture_1 = "system:capture_1"; static const char* capture_2 = "system:capture_2"; static const char* playback_1 = "system:playback_1"; static const char* playback_2 = "system:playback_2"; if (flags == 0 || (flags & (JackPortIsInput|JackPortIsOutput)) == (JackPortIsInput|JackPortIsOutput)) { if (const char** const ret = (const char**)calloc(5, sizeof(const char*))) { ret[0] = capture_1; ret[1] = capture_2; ret[2] = playback_1; ret[3] = playback_2; ret[4] = nullptr; return ret; } } if (flags & JackPortIsInput) { if (const char** const ret = (const char**)calloc(3, sizeof(const char*))) { ret[0] = playback_1; ret[1] = playback_2; ret[2] = nullptr; return ret; } } if (flags & JackPortIsOutput) { if (const char** const ret = (const char**)calloc(3, sizeof(const char*))) { ret[0] = capture_1; ret[1] = capture_2; ret[2] = nullptr; return ret; } } return nullptr; } CARLA_EXPORT jack_port_t* jack_port_by_name(jack_client_t* /*client*/, const char* name) { carla_stdout("CarlaJackClient :: %s | %s", __FUNCTION__, name); // CarlaJackClient* const jclient = (CarlaJackClient*)client; // CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); // const JackClientState& jstate(jclient->fState); //CARLA_SAFE_ASSERT_RETURN(jstate.activated, 0); static const JackPortState capturePorts[] = { JackPortState("system", "capture_1", 0, JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal, true), JackPortState("system", "capture_2", 1, JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal, true), }; static const JackPortState playbackPorts[] = { JackPortState("system", "playback_1", 3, JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal, true), JackPortState("system", "playback_2", 4, JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal, true), }; if (std::strncmp(name, "system:", 7) == 0) { name += 7; /**/ if (std::strncmp(name, "capture_", 8) == 0) { name += 8; const int index = std::atoi(name); CARLA_SAFE_ASSERT_RETURN(index >= 0 && index < 2, nullptr); return (jack_port_t*)&capturePorts[index]; } else if (std::strncmp(name, "playback_", 9) == 0) { name += 9; const int index = std::atoi(name); CARLA_SAFE_ASSERT_RETURN(index >= 0, nullptr); return (jack_port_t*)&playbackPorts[index]; } else { carla_stderr2("Invalid port short name: '%s'", name); return nullptr; } } carla_stderr2("Invalid port name: '%s'", name); return nullptr; } CARLA_EXPORT jack_port_t* jack_port_by_id(jack_client_t*, jack_port_id_t) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return nullptr; } CARLA_EXPORT void jack_set_info_function(void (*func)(const char *)) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); (void)func; } CARLA_EXPORT int jack_set_freewheel(jack_client_t*, int) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return 1; } CARLA_EXPORT int jack_set_buffer_size(jack_client_t*, jack_nframes_t) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return 1; } CARLA_EXPORT int jack_engine_takeover_timebase(jack_client_t*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return ENOSYS; } CARLA_EXPORT float jack_cpu_load(jack_client_t*) { return 0.0f; } CARLA_EXPORT int jack_client_name_size(void) { return STR_MAX; } CARLA_EXPORT void jack_port_get_latency_range(jack_port_t*, jack_latency_callback_mode_t, jack_latency_range_t* range) { range->min = range->max = 0; } CARLA_EXPORT jack_nframes_t jack_port_get_latency(jack_port_t*) { return 0; } CARLA_EXPORT int jack_set_xrun_callback(jack_client_t*, JackXRunCallback, void*) { return 0; } CARLA_EXPORT int jack_set_thread_init_callback(jack_client_t*, JackThreadInitCallback, void*) { return 0; } CARLA_EXPORT int jack_port_name_size(void) { return STR_MAX; } CARLA_EXPORT int jack_port_connected(const jack_port_t*) { carla_stdout("CarlaJackClient :: %s", __FUNCTION__); return 1; } CARLA_EXPORT const char* JACK_METADATA_PRETTY_NAME; const char* JACK_METADATA_PRETTY_NAME = "http://jackaudio.org/metadata/pretty-name"; // jack_ringbuffer_create // jack_port_is_mine // jack_port_set_name // jack_port_get_all_connections // jack_port_uuid // -------------------------------------------------------------------------------------------------------------------- #include "jackbridge/JackBridge2.cpp" #include "CarlaBridgeUtils.cpp" // --------------------------------------------------------------------------------------------------------------------