| @@ -951,12 +951,12 @@ public: | |||
| /*! | |||
| * Connect patchbay ports \a portA and \a portB. | |||
| */ | |||
| virtual bool patchbayConnect(int portA, int portB); | |||
| virtual bool patchbayConnect(const int portA, const int portB); | |||
| /*! | |||
| * Disconnect patchbay connection \a connectionId. | |||
| */ | |||
| virtual bool patchbayDisconnect(int connectionId); | |||
| virtual bool patchbayDisconnect(const int connectionId); | |||
| /*! | |||
| * Force the engine to resend all patchbay clients, ports and connections again. | |||
| @@ -1104,27 +1104,19 @@ protected: | |||
| */ | |||
| void setPluginPeaks(const unsigned int pluginId, float const inPeaks[2], float const outPeaks[2]) noexcept; | |||
| #ifndef BUILD_BRIDGE | |||
| /*! | |||
| * Proccess audio buffer in rack mode. | |||
| * \note RT call | |||
| */ | |||
| void processRack(float* inBuf[2], float* outBuf[2], const uint32_t frames); | |||
| // ------------------------------------------------------------------- | |||
| /*! | |||
| * Proccess audio buffer in patchbay mode. | |||
| * In \a bufCount, [0]=inBufCount and [1]=outBufCount | |||
| * \note RT call | |||
| */ | |||
| void processPatchbay(float** inBuf, float** outBuf, const uint32_t bufCount[2], const uint32_t frames); | |||
| #endif | |||
| virtual bool connectRackMidiInPort(const int) { return false; } | |||
| virtual bool connectRackMidiOutPort(const int) { return false; } | |||
| virtual bool disconnectRackMidiInPort(const int) { return false; } | |||
| virtual bool disconnectRackMidiOutPort(const int) { return false; } | |||
| // ------------------------------------------------------------------- | |||
| // Engine initializers | |||
| private: | |||
| static CarlaEngine* newJack(); | |||
| /*! | |||
| * Native audio APIs. | |||
| */ | |||
| enum AudioApi { | |||
| AUDIO_API_NULL = 0, | |||
| // common | |||
| @@ -1140,12 +1132,20 @@ private: | |||
| AUDIO_API_DS = 7 | |||
| }; | |||
| // ------------------------------------------------------------------- | |||
| // Engine initializers | |||
| // jack | |||
| static CarlaEngine* newJack(); | |||
| // rtaudio | |||
| static CarlaEngine* newRtAudio(const AudioApi api); | |||
| static unsigned int getRtAudioApiCount(); | |||
| static const char* getRtAudioApiName(const unsigned int index); | |||
| static const char* const* getRtAudioApiDeviceNames(const unsigned int index); | |||
| static const EngineDriverDeviceInfo* getRtAudioDeviceInfo(const unsigned int index, const char* const deviceName); | |||
| // juce | |||
| static CarlaEngine* newJuce(const AudioApi api); | |||
| static unsigned int getJuceApiCount(); | |||
| static const char* getJuceApiName(const unsigned int index); | |||
| @@ -23,6 +23,7 @@ | |||
| * - proper find&load plugins | |||
| * - uncomment CarlaPlugin::newAU and newCSOUND | |||
| * - something about the peaks? | |||
| * - patchbayDisconnect should return false sometimes | |||
| */ | |||
| #include "CarlaEngineInternal.hpp" | |||
| @@ -190,6 +191,262 @@ void EngineEvent::fillFromMidiData(const uint8_t size, const uint8_t* const data | |||
| } | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| #ifndef BUILD_BRIDGE | |||
| void CarlaEngineProtectedData::processRack(float* inBufReal[2], float* outBuf[2], const uint32_t frames, const bool isOffline) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(bufEvents.in != nullptr,); | |||
| CARLA_SAFE_ASSERT_RETURN(bufEvents.out != nullptr,); | |||
| // safe copy | |||
| float inBuf0[frames]; | |||
| float inBuf1[frames]; | |||
| float* inBuf[2] = { inBuf0, inBuf1 }; | |||
| // initialize audio inputs | |||
| FLOAT_COPY(inBuf0, inBufReal[0], frames); | |||
| FLOAT_COPY(inBuf1, inBufReal[1], frames); | |||
| // initialize audio outputs (zero) | |||
| FLOAT_CLEAR(outBuf[0], frames); | |||
| FLOAT_CLEAR(outBuf[1], frames); | |||
| // initialize event outputs (zero) | |||
| carla_zeroStruct<EngineEvent>(bufEvents.out, kEngineMaxInternalEventCount); | |||
| bool processed = false; | |||
| uint32_t oldAudioInCount = 0; | |||
| uint32_t oldMidiOutCount = 0; | |||
| // process plugins | |||
| for (unsigned int i=0; i < curPluginCount; ++i) | |||
| { | |||
| CarlaPlugin* const plugin = plugins[i].plugin; | |||
| if (plugin == nullptr || ! plugin->isEnabled() || ! plugin->tryLock(isOffline)) | |||
| continue; | |||
| if (processed) | |||
| { | |||
| // initialize audio inputs (from previous outputs) | |||
| FLOAT_COPY(inBuf0, outBuf[0], frames); | |||
| FLOAT_COPY(inBuf1, outBuf[1], frames); | |||
| // initialize audio outputs (zero) | |||
| FLOAT_CLEAR(outBuf[0], frames); | |||
| FLOAT_CLEAR(outBuf[1], frames); | |||
| // if plugin has no midi out, add previous events | |||
| if (oldMidiOutCount == 0 && bufEvents.in[0].type != kEngineEventTypeNull) | |||
| { | |||
| if (bufEvents.out[0].type != kEngineEventTypeNull) | |||
| { | |||
| // TODO: carefully add to input, sorted events | |||
| } | |||
| // else nothing needed | |||
| } | |||
| else | |||
| { | |||
| // initialize event inputs from previous outputs | |||
| carla_copyStruct<EngineEvent>(bufEvents.in, bufEvents.out, kEngineMaxInternalEventCount); | |||
| // initialize event outputs (zero) | |||
| carla_zeroStruct<EngineEvent>(bufEvents.out, kEngineMaxInternalEventCount); | |||
| } | |||
| } | |||
| oldAudioInCount = plugin->getAudioInCount(); | |||
| oldMidiOutCount = plugin->getMidiOutCount(); | |||
| // process | |||
| plugin->initBuffers(); | |||
| plugin->process(inBuf, outBuf, frames); | |||
| plugin->unlock(); | |||
| // if plugin has no audio inputs, add input buffer | |||
| if (oldAudioInCount == 0) | |||
| { | |||
| FLOAT_ADD(outBuf[0], inBuf0, frames); | |||
| FLOAT_ADD(outBuf[1], inBuf1, frames); | |||
| } | |||
| // set peaks | |||
| { | |||
| EnginePluginData& pluginData(plugins[i]); | |||
| #ifdef HAVE_JUCE | |||
| float tmpMin, tmpMax; | |||
| if (oldAudioInCount > 0) | |||
| { | |||
| FloatVectorOperations::findMinAndMax(inBuf0, frames, tmpMin, tmpMax); | |||
| pluginData.insPeak[0] = carla_max<float>(std::abs(tmpMin), std::abs(tmpMax), 1.0f); | |||
| FloatVectorOperations::findMinAndMax(inBuf1, frames, tmpMin, tmpMax); | |||
| pluginData.insPeak[1] = carla_max<float>(std::abs(tmpMin), std::abs(tmpMax), 1.0f); | |||
| } | |||
| else | |||
| { | |||
| pluginData.insPeak[0] = 0.0f; | |||
| pluginData.insPeak[1] = 0.0f; | |||
| } | |||
| if (plugin->getAudioOutCount() > 0) | |||
| { | |||
| FloatVectorOperations::findMinAndMax(outBuf[0], frames, tmpMin, tmpMax); | |||
| pluginData.outsPeak[0] = carla_max<float>(std::abs(tmpMin), std::abs(tmpMax), 1.0f); | |||
| FloatVectorOperations::findMinAndMax(outBuf[1], frames, tmpMin, tmpMax); | |||
| pluginData.outsPeak[1] = carla_max<float>(std::abs(tmpMin), std::abs(tmpMax), 1.0f); | |||
| } | |||
| else | |||
| { | |||
| pluginData.outsPeak[0] = 0.0f; | |||
| pluginData.outsPeak[1] = 0.0f; | |||
| } | |||
| #else | |||
| float peak1, peak2; | |||
| if (oldAudioInCount > 0) | |||
| { | |||
| peak1 = peak2 = 0.0f; | |||
| for (uint32_t k=0; k < frames; ++k) | |||
| { | |||
| peak1 = carla_max<float>(peak1, std::fabs(inBuf0[k]), 1.0f); | |||
| peak2 = carla_max<float>(peak2, std::fabs(inBuf1[k]), 1.0f); | |||
| } | |||
| pluginData.insPeak[0] = peak1; | |||
| pluginData.insPeak[1] = peak2; | |||
| } | |||
| else | |||
| { | |||
| pluginData.insPeak[0] = 0.0f; | |||
| pluginData.insPeak[1] = 0.0f; | |||
| } | |||
| if (plugin->getAudioOutCount() > 0) | |||
| { | |||
| peak1 = peak2 = 0.0f; | |||
| for (uint32_t k=0; k < frames; ++k) | |||
| { | |||
| peak1 = carla_max<float>(peak1, std::fabs(outBuf[0][k]), 1.0f); | |||
| peak2 = carla_max<float>(peak2, std::fabs(outBuf[1][k]), 1.0f); | |||
| } | |||
| pluginData.outsPeak[0] = peak1; | |||
| pluginData.outsPeak[1] = peak2; | |||
| } | |||
| else | |||
| { | |||
| pluginData.outsPeak[0] = 0.0f; | |||
| pluginData.outsPeak[1] = 0.0f; | |||
| } | |||
| #endif | |||
| } | |||
| processed = true; | |||
| } | |||
| } | |||
| void CarlaEngineProtectedData::processRackFull(float** const inBuf, const uint32_t inCount, float** const outBuf, const uint32_t outCount, const uint32_t nframes, const bool isOffline) | |||
| { | |||
| EngineRackBuffers* const rack(bufAudio.rack); | |||
| const CarlaMutex::ScopedLocker sl(rack->connectLock); | |||
| // connect input buffers | |||
| if (rack->connectedIns[0].count() == 0) | |||
| { | |||
| FLOAT_CLEAR(rack->in[0], nframes); | |||
| } | |||
| else | |||
| { | |||
| bool first = true; | |||
| for (List<uint>::Itenerator it = rack->connectedIns[0].begin(); it.valid(); it.next()) | |||
| { | |||
| const uint& port(it.getConstValue()); | |||
| CARLA_SAFE_ASSERT_CONTINUE(port < inCount); | |||
| if (first) | |||
| { | |||
| FLOAT_COPY(rack->in[0], inBuf[port], nframes); | |||
| first = false; | |||
| } | |||
| else | |||
| { | |||
| FLOAT_ADD(rack->in[0], inBuf[port], nframes); | |||
| } | |||
| } | |||
| if (first) | |||
| FLOAT_CLEAR(rack->in[0], nframes); | |||
| } | |||
| if (rack->connectedIns[1].count() == 0) | |||
| { | |||
| FLOAT_CLEAR(rack->in[1], nframes); | |||
| } | |||
| else | |||
| { | |||
| bool first = true; | |||
| for (List<uint>::Itenerator it = rack->connectedIns[1].begin(); it.valid(); it.next()) | |||
| { | |||
| const uint& port(it.getConstValue()); | |||
| CARLA_SAFE_ASSERT_CONTINUE(port < inCount); | |||
| if (first) | |||
| { | |||
| FLOAT_COPY(rack->in[1], inBuf[port], nframes); | |||
| first = false; | |||
| } | |||
| else | |||
| { | |||
| FLOAT_ADD(rack->in[1], inBuf[port], nframes); | |||
| } | |||
| } | |||
| if (first) | |||
| FLOAT_CLEAR(rack->in[1], nframes); | |||
| } | |||
| FLOAT_CLEAR(rack->out[0], nframes); | |||
| FLOAT_CLEAR(rack->out[1], nframes); | |||
| // process | |||
| processRack(rack->in, rack->out, nframes, isOffline); | |||
| // connect output buffers | |||
| if (rack->connectedOuts[0].count() != 0) | |||
| { | |||
| for (List<uint>::Itenerator it = rack->connectedOuts[0].begin(); it.valid(); it.next()) | |||
| { | |||
| const uint& port(it.getConstValue()); | |||
| CARLA_SAFE_ASSERT_CONTINUE(port < outCount); | |||
| FLOAT_ADD(outBuf[port], rack->out[0], nframes); | |||
| } | |||
| } | |||
| if (rack->connectedOuts[1].count() != 0) | |||
| { | |||
| for (List<uint>::Itenerator it = rack->connectedOuts[1].begin(); it.valid(); it.next()) | |||
| { | |||
| const uint& port(it.getConstValue()); | |||
| CARLA_SAFE_ASSERT_CONTINUE(port < outCount); | |||
| FLOAT_ADD(outBuf[port], rack->out[1], nframes); | |||
| } | |||
| } | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // Carla Engine port (Abstract) | |||
| @@ -1606,16 +1863,187 @@ void CarlaEngine::setCallback(const EngineCallbackFunc func, void* const ptr) | |||
| // ----------------------------------------------------------------------- | |||
| // Patchbay | |||
| bool CarlaEngine::patchbayConnect(int, int) | |||
| bool CarlaEngine::patchbayConnect(const int portA, const int portB) | |||
| { | |||
| setLastError("Unsupported operation"); | |||
| return false; | |||
| CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY, false); | |||
| CARLA_SAFE_ASSERT_RETURN(pData->bufAudio.isReady, false); | |||
| carla_debug("CarlaEngineRtAudio::patchbayConnect(%i, %i)", portA, portB); | |||
| if (pData->bufAudio.usePatchbay) | |||
| { | |||
| // not implemented yet | |||
| return false; | |||
| } | |||
| EngineRackBuffers* const rack(pData->bufAudio.rack); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(portA > RACK_PATCHBAY_PORT_MAX, "Invalid output port"); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(portB > RACK_PATCHBAY_PORT_MAX, "Invalid input port"); | |||
| // only allow connections between Carla and other ports | |||
| if (portA < 0 && portB < 0) | |||
| { | |||
| setLastError("Invalid connection (1)"); | |||
| return false; | |||
| } | |||
| if (portA >= 0 && portB >= 0) | |||
| { | |||
| setLastError("Invalid connection (2)"); | |||
| return false; | |||
| } | |||
| const int carlaPort = (portA < 0) ? portA : portB; | |||
| const int targetPort = (carlaPort == portA) ? portB : portA; | |||
| bool makeConnection = false; | |||
| switch (carlaPort) | |||
| { | |||
| case RACK_PATCHBAY_PORT_AUDIO_IN1: | |||
| CARLA_SAFE_ASSERT_BREAK(targetPort >= RACK_PATCHBAY_GROUP_AUDIO_IN*1000); | |||
| CARLA_SAFE_ASSERT_BREAK(targetPort <= RACK_PATCHBAY_GROUP_AUDIO_IN*1000+999); | |||
| rack->connectLock.lock(); | |||
| rack->connectedIns[0].append(targetPort - RACK_PATCHBAY_GROUP_AUDIO_IN*1000); | |||
| rack->connectLock.unlock(); | |||
| makeConnection = true; | |||
| break; | |||
| case RACK_PATCHBAY_PORT_AUDIO_IN2: | |||
| CARLA_SAFE_ASSERT_BREAK(targetPort >= RACK_PATCHBAY_GROUP_AUDIO_IN*1000); | |||
| CARLA_SAFE_ASSERT_BREAK(targetPort <= RACK_PATCHBAY_GROUP_AUDIO_IN*1000+999); | |||
| rack->connectLock.lock(); | |||
| rack->connectedIns[1].append(targetPort - RACK_PATCHBAY_GROUP_AUDIO_IN*1000); | |||
| rack->connectLock.unlock(); | |||
| makeConnection = true; | |||
| break; | |||
| case RACK_PATCHBAY_PORT_AUDIO_OUT1: | |||
| CARLA_SAFE_ASSERT_BREAK(targetPort >= RACK_PATCHBAY_GROUP_AUDIO_OUT*1000); | |||
| CARLA_SAFE_ASSERT_BREAK(targetPort <= RACK_PATCHBAY_GROUP_AUDIO_OUT*1000+999); | |||
| rack->connectLock.lock(); | |||
| rack->connectedOuts[0].append(targetPort - RACK_PATCHBAY_GROUP_AUDIO_OUT*1000); | |||
| rack->connectLock.unlock(); | |||
| makeConnection = true; | |||
| break; | |||
| case RACK_PATCHBAY_PORT_AUDIO_OUT2: | |||
| CARLA_SAFE_ASSERT_BREAK(targetPort >= RACK_PATCHBAY_GROUP_AUDIO_OUT*1000); | |||
| CARLA_SAFE_ASSERT_BREAK(targetPort <= RACK_PATCHBAY_GROUP_AUDIO_OUT*1000+999); | |||
| rack->connectLock.lock(); | |||
| rack->connectedOuts[1].append(targetPort - RACK_PATCHBAY_GROUP_AUDIO_OUT*1000); | |||
| rack->connectLock.unlock(); | |||
| makeConnection = true; | |||
| break; | |||
| case RACK_PATCHBAY_PORT_MIDI_IN: | |||
| CARLA_SAFE_ASSERT_BREAK(targetPort >= RACK_PATCHBAY_GROUP_MIDI_IN*1000); | |||
| CARLA_SAFE_ASSERT_BREAK(targetPort <= RACK_PATCHBAY_GROUP_MIDI_IN*1000+999); | |||
| makeConnection = connectRackMidiInPort(targetPort - RACK_PATCHBAY_GROUP_MIDI_IN*1000); | |||
| break; | |||
| case RACK_PATCHBAY_PORT_MIDI_OUT: | |||
| CARLA_SAFE_ASSERT_BREAK(targetPort >= RACK_PATCHBAY_GROUP_MIDI_OUT*1000); | |||
| CARLA_SAFE_ASSERT_BREAK(targetPort <= RACK_PATCHBAY_GROUP_MIDI_OUT*1000+999); | |||
| makeConnection = connectRackMidiOutPort(targetPort - RACK_PATCHBAY_GROUP_MIDI_OUT*1000); | |||
| break; | |||
| } | |||
| if (! makeConnection) | |||
| { | |||
| setLastError("Invalid connection (3)"); | |||
| return false; | |||
| } | |||
| ConnectionToId connectionToId; | |||
| connectionToId.id = rack->lastConnectionId; | |||
| connectionToId.portOut = portA; | |||
| connectionToId.portIn = portB; | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, rack->lastConnectionId, portA, portB, 0.0f, nullptr); | |||
| rack->usedConnections.append(connectionToId); | |||
| rack->lastConnectionId++; | |||
| return true; | |||
| } | |||
| bool CarlaEngine::patchbayDisconnect(int) | |||
| bool CarlaEngine::patchbayDisconnect(const int connectionId) | |||
| { | |||
| setLastError("Unsupported operation"); | |||
| return false; | |||
| CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY, false); | |||
| CARLA_SAFE_ASSERT_RETURN(pData->bufAudio.isReady, false); | |||
| carla_debug("CarlaEngineRtAudio::patchbayDisconnect(%i)", connectionId); | |||
| if (pData->bufAudio.usePatchbay) | |||
| { | |||
| // not implemented yet | |||
| return false; | |||
| } | |||
| EngineRackBuffers* const rack(pData->bufAudio.rack); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(rack->usedConnections.count() > 0, "No connections available"); | |||
| for (List<ConnectionToId>::Itenerator it=rack->usedConnections.begin(); it.valid(); it.next()) | |||
| { | |||
| const ConnectionToId& connection(it.getConstValue()); | |||
| if (connection.id == connectionId) | |||
| { | |||
| const int targetPort((connection.portOut >= 0) ? connection.portOut : connection.portIn); | |||
| const int carlaPort((targetPort == connection.portOut) ? connection.portIn : connection.portOut); | |||
| if (targetPort >= RACK_PATCHBAY_GROUP_MIDI_OUT*1000) | |||
| { | |||
| const int portId(targetPort-RACK_PATCHBAY_GROUP_MIDI_OUT*1000); | |||
| disconnectRackMidiInPort(portId); | |||
| } | |||
| else if (targetPort >= RACK_PATCHBAY_GROUP_MIDI_IN*1000) | |||
| { | |||
| const int portId(targetPort-RACK_PATCHBAY_GROUP_MIDI_IN*1000); | |||
| disconnectRackMidiOutPort(portId); | |||
| } | |||
| else if (targetPort >= RACK_PATCHBAY_GROUP_AUDIO_OUT*1000) | |||
| { | |||
| CARLA_ASSERT(carlaPort == RACK_PATCHBAY_PORT_AUDIO_OUT1 || carlaPort == RACK_PATCHBAY_PORT_AUDIO_OUT2); | |||
| const int portId(targetPort-RACK_PATCHBAY_GROUP_AUDIO_OUT*1000); | |||
| rack->connectLock.lock(); | |||
| if (carlaPort == RACK_PATCHBAY_PORT_AUDIO_OUT1) | |||
| rack->connectedOuts[0].removeAll(portId); | |||
| else | |||
| rack->connectedOuts[1].removeAll(portId); | |||
| rack->connectLock.unlock(); | |||
| } | |||
| else if (targetPort >= RACK_PATCHBAY_GROUP_AUDIO_IN*1000) | |||
| { | |||
| CARLA_ASSERT(carlaPort == RACK_PATCHBAY_PORT_AUDIO_IN1 || carlaPort == RACK_PATCHBAY_PORT_AUDIO_IN2); | |||
| const int portId(targetPort-RACK_PATCHBAY_GROUP_AUDIO_IN*1000); | |||
| rack->connectLock.lock(); | |||
| if (carlaPort == RACK_PATCHBAY_PORT_AUDIO_IN1) | |||
| rack->connectedIns[0].removeAll(portId); | |||
| else | |||
| rack->connectedIns[1].removeAll(portId); | |||
| rack->connectLock.unlock(); | |||
| } | |||
| else | |||
| { | |||
| CARLA_ASSERT(false); | |||
| } | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_REMOVED, connection.id, connection.portOut, connection.portIn, 0.0f, nullptr); | |||
| rack->usedConnections.remove(it); | |||
| break; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| bool CarlaEngine::patchbayRefresh() | |||
| @@ -1884,181 +2312,6 @@ void CarlaEngine::setPluginPeaks(const unsigned int pluginId, float const inPeak | |||
| pluginData.outsPeak[1] = outPeaks[1]; | |||
| } | |||
| #ifndef BUILD_BRIDGE | |||
| void CarlaEngine::processRack(float* inBufReal[2], float* outBuf[2], const uint32_t frames) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(pData->bufEvents.in != nullptr,); | |||
| CARLA_SAFE_ASSERT_RETURN(pData->bufEvents.out != nullptr,); | |||
| // safe copy | |||
| float inBuf0[frames]; | |||
| float inBuf1[frames]; | |||
| float* inBuf[2] = { inBuf0, inBuf1 }; | |||
| // initialize audio inputs | |||
| FLOAT_COPY(inBuf0, inBufReal[0], frames); | |||
| FLOAT_COPY(inBuf1, inBufReal[1], frames); | |||
| // initialize audio outputs (zero) | |||
| FLOAT_CLEAR(outBuf[0], frames); | |||
| FLOAT_CLEAR(outBuf[1], frames); | |||
| // initialize event outputs (zero) | |||
| carla_zeroStruct<EngineEvent>(pData->bufEvents.out, kEngineMaxInternalEventCount); | |||
| bool processed = false; | |||
| uint32_t oldAudioInCount = 0; | |||
| uint32_t oldMidiOutCount = 0; | |||
| const bool forcedOffline(isOffline()); | |||
| // process plugins | |||
| for (unsigned int i=0; i < pData->curPluginCount; ++i) | |||
| { | |||
| CarlaPlugin* const plugin = pData->plugins[i].plugin; | |||
| if (plugin == nullptr || ! plugin->isEnabled() || ! plugin->tryLock(forcedOffline)) | |||
| continue; | |||
| if (processed) | |||
| { | |||
| // initialize audio inputs (from previous outputs) | |||
| FLOAT_COPY(inBuf0, outBuf[0], frames); | |||
| FLOAT_COPY(inBuf1, outBuf[1], frames); | |||
| // initialize audio outputs (zero) | |||
| FLOAT_CLEAR(outBuf[0], frames); | |||
| FLOAT_CLEAR(outBuf[1], frames); | |||
| // if plugin has no midi out, add previous events | |||
| if (oldMidiOutCount == 0 && pData->bufEvents.in[0].type != kEngineEventTypeNull) | |||
| { | |||
| if (pData->bufEvents.out[0].type != kEngineEventTypeNull) | |||
| { | |||
| // TODO: carefully add to input, sorted events | |||
| } | |||
| // else nothing needed | |||
| } | |||
| else | |||
| { | |||
| // initialize event inputs from previous outputs | |||
| std::memcpy(pData->bufEvents.in, pData->bufEvents.out, sizeof(EngineEvent)*kEngineMaxInternalEventCount); | |||
| // initialize event outputs (zero) | |||
| std::memset(pData->bufEvents.out, 0, sizeof(EngineEvent)*kEngineMaxInternalEventCount); | |||
| } | |||
| } | |||
| oldAudioInCount = plugin->getAudioInCount(); | |||
| oldMidiOutCount = plugin->getMidiOutCount(); | |||
| // process | |||
| plugin->initBuffers(); | |||
| plugin->process(inBuf, outBuf, frames); | |||
| plugin->unlock(); | |||
| // if plugin has no audio inputs, add input buffer | |||
| if (oldAudioInCount == 0) | |||
| { | |||
| FLOAT_ADD(outBuf[0], inBuf0, frames); | |||
| FLOAT_ADD(outBuf[1], inBuf1, frames); | |||
| } | |||
| // set peaks | |||
| { | |||
| EnginePluginData& pluginData(pData->plugins[i]); | |||
| #ifdef HAVE_JUCE | |||
| float tmpMin, tmpMax; | |||
| if (oldAudioInCount > 0) | |||
| { | |||
| FloatVectorOperations::findMinAndMax(inBuf0, frames, tmpMin, tmpMax); | |||
| pluginData.insPeak[0] = carla_max<float>(std::abs(tmpMin), std::abs(tmpMax), 1.0f); | |||
| FloatVectorOperations::findMinAndMax(inBuf1, frames, tmpMin, tmpMax); | |||
| pluginData.insPeak[1] = carla_max<float>(std::abs(tmpMin), std::abs(tmpMax), 1.0f); | |||
| } | |||
| else | |||
| { | |||
| pluginData.insPeak[0] = 0.0f; | |||
| pluginData.insPeak[1] = 0.0f; | |||
| } | |||
| if (plugin->getAudioOutCount() > 0) | |||
| { | |||
| FloatVectorOperations::findMinAndMax(outBuf[0], frames, tmpMin, tmpMax); | |||
| pluginData.outsPeak[0] = carla_max<float>(std::abs(tmpMin), std::abs(tmpMax), 1.0f); | |||
| FloatVectorOperations::findMinAndMax(outBuf[1], frames, tmpMin, tmpMax); | |||
| pluginData.outsPeak[1] = carla_max<float>(std::abs(tmpMin), std::abs(tmpMax), 1.0f); | |||
| } | |||
| else | |||
| { | |||
| pluginData.outsPeak[0] = 0.0f; | |||
| pluginData.outsPeak[1] = 0.0f; | |||
| } | |||
| #else | |||
| float peak1, peak2; | |||
| if (oldAudioInCount > 0) | |||
| { | |||
| peak1 = peak2 = 0.0f; | |||
| for (uint32_t k=0; k < frames; ++k) | |||
| { | |||
| peak1 = carla_max<float>(peak1, std::fabs(inBuf0[k]), 1.0f); | |||
| peak2 = carla_max<float>(peak2, std::fabs(inBuf1[k]), 1.0f); | |||
| } | |||
| pluginData.insPeak[0] = peak1; | |||
| pluginData.insPeak[1] = peak2; | |||
| } | |||
| else | |||
| { | |||
| pluginData.insPeak[0] = 0.0f; | |||
| pluginData.insPeak[1] = 0.0f; | |||
| } | |||
| if (plugin->getAudioOutCount() > 0) | |||
| { | |||
| peak1 = peak2 = 0.0f; | |||
| for (uint32_t k=0; k < frames; ++k) | |||
| { | |||
| peak1 = carla_max<float>(peak1, std::fabs(outBuf[0][k]), 1.0f); | |||
| peak2 = carla_max<float>(peak2, std::fabs(outBuf[1][k]), 1.0f); | |||
| } | |||
| pluginData.outsPeak[0] = peak1; | |||
| pluginData.outsPeak[1] = peak2; | |||
| } | |||
| else | |||
| { | |||
| pluginData.outsPeak[0] = 0.0f; | |||
| pluginData.outsPeak[1] = 0.0f; | |||
| } | |||
| #endif | |||
| } | |||
| processed = true; | |||
| } | |||
| } | |||
| void CarlaEngine::processPatchbay(float** inBuf, float** outBuf, const uint32_t bufCount[2], const uint32_t frames) | |||
| { | |||
| // TODO | |||
| return; | |||
| // unused, for now | |||
| (void)inBuf; | |||
| (void)outBuf; | |||
| (void)bufCount; | |||
| (void)frames; | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // Bridge/Controller OSC stuff | |||
| @@ -24,6 +24,7 @@ | |||
| #include "CarlaPlugin.hpp" | |||
| #include "CarlaMutex.hpp" | |||
| #include "List.hpp" | |||
| #ifdef HAVE_JUCE | |||
| # include "juce_audio_basics.h" | |||
| @@ -167,6 +168,137 @@ struct EnginePluginData { | |||
| // ----------------------------------------------------------------------- | |||
| enum RackPatchbayGroupIds { | |||
| RACK_PATCHBAY_GROUP_CARLA = -1, | |||
| RACK_PATCHBAY_GROUP_AUDIO_IN = 0, | |||
| RACK_PATCHBAY_GROUP_AUDIO_OUT = 1, | |||
| RACK_PATCHBAY_GROUP_MIDI_IN = 2, | |||
| RACK_PATCHBAY_GROUP_MIDI_OUT = 3, | |||
| RACK_PATCHBAY_GROUP_MAX = 4 | |||
| }; | |||
| enum RackPatchbayPortIds { | |||
| RACK_PATCHBAY_PORT_AUDIO_IN1 = -1, | |||
| RACK_PATCHBAY_PORT_AUDIO_IN2 = -2, | |||
| RACK_PATCHBAY_PORT_AUDIO_OUT1 = -3, | |||
| RACK_PATCHBAY_PORT_AUDIO_OUT2 = -4, | |||
| RACK_PATCHBAY_PORT_MIDI_IN = -5, | |||
| RACK_PATCHBAY_PORT_MIDI_OUT = -6, | |||
| RACK_PATCHBAY_PORT_MAX = -7 | |||
| }; | |||
| struct PortNameToId { | |||
| int portId; | |||
| char name[STR_MAX+1]; | |||
| }; | |||
| struct ConnectionToId { | |||
| int id; | |||
| int portOut; | |||
| int portIn; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| struct EngineRackBuffers { | |||
| float* in[2]; | |||
| float* out[2]; | |||
| // connections stuff | |||
| List<uint> connectedIns[2]; | |||
| List<uint> connectedOuts[2]; | |||
| CarlaMutex connectLock; | |||
| int lastConnectionId; | |||
| List<ConnectionToId> usedConnections; | |||
| EngineRackBuffers(const uint32_t bufferSize) | |||
| : lastConnectionId(0) | |||
| { | |||
| resize(bufferSize); | |||
| } | |||
| ~EngineRackBuffers() | |||
| { | |||
| clear(); | |||
| } | |||
| void clear() | |||
| { | |||
| lastConnectionId = 0; | |||
| if (in[0] != nullptr) | |||
| { | |||
| delete[] in[0]; | |||
| in[0] = nullptr; | |||
| } | |||
| if (in[1] != nullptr) | |||
| { | |||
| delete[] in[1]; | |||
| in[1] = nullptr; | |||
| } | |||
| if (out[0] != nullptr) | |||
| { | |||
| delete[] out[0]; | |||
| out[0] = nullptr; | |||
| } | |||
| if (out[1] != nullptr) | |||
| { | |||
| delete[] out[1]; | |||
| out[1] = nullptr; | |||
| } | |||
| connectedIns[0].clear(); | |||
| connectedIns[1].clear(); | |||
| connectedOuts[0].clear(); | |||
| connectedOuts[1].clear(); | |||
| usedConnections.clear(); | |||
| } | |||
| void resize(const uint32_t bufferSize) | |||
| { | |||
| if (bufferSize > 0) | |||
| { | |||
| in[0] = new float[bufferSize]; | |||
| in[1] = new float[bufferSize]; | |||
| out[0] = new float[bufferSize]; | |||
| out[1] = new float[bufferSize]; | |||
| } | |||
| else | |||
| { | |||
| in[0] = nullptr; | |||
| in[1] = nullptr; | |||
| out[0] = nullptr; | |||
| out[1] = nullptr; | |||
| } | |||
| } | |||
| }; | |||
| struct EnginePatchbayBuffers { | |||
| EnginePatchbayBuffers(const uint32_t bufferSize) | |||
| { | |||
| resize(bufferSize); | |||
| } | |||
| ~EnginePatchbayBuffers() | |||
| { | |||
| clear(); | |||
| } | |||
| void clear() | |||
| { | |||
| } | |||
| void resize(const uint32_t /*bufferSize*/) | |||
| { | |||
| } | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| struct CarlaEngineProtectedData { | |||
| CarlaEngineOsc osc; | |||
| CarlaEngineThread thread; | |||
| @@ -192,6 +324,94 @@ struct CarlaEngineProtectedData { | |||
| EnginePluginData* plugins; | |||
| #ifndef BUILD_BRIDGE | |||
| struct InternalAudio { | |||
| bool isReady; | |||
| bool usePatchbay; | |||
| union { | |||
| EngineRackBuffers* rack; | |||
| EnginePatchbayBuffers* patchbay; | |||
| }; | |||
| InternalAudio() noexcept | |||
| : isReady(false), | |||
| usePatchbay(false) | |||
| { | |||
| rack = nullptr; | |||
| } | |||
| ~InternalAudio() | |||
| { | |||
| CARLA_ASSERT(! isReady); | |||
| CARLA_ASSERT(rack == nullptr); | |||
| } | |||
| void initPatchbay() | |||
| { | |||
| if (usePatchbay) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(patchbay != nullptr,); | |||
| } | |||
| else | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(rack != nullptr,); | |||
| rack->lastConnectionId = 0; | |||
| rack->usedConnections.clear(); | |||
| } | |||
| } | |||
| void clear() | |||
| { | |||
| isReady = false; | |||
| if (usePatchbay) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(patchbay != nullptr,); | |||
| delete patchbay; | |||
| patchbay = nullptr; | |||
| } | |||
| else | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(rack != nullptr,); | |||
| delete rack; | |||
| rack = nullptr; | |||
| } | |||
| } | |||
| void create(const uint32_t bufferSize) | |||
| { | |||
| if (usePatchbay) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(patchbay == nullptr,); | |||
| patchbay = new EnginePatchbayBuffers(bufferSize); | |||
| } | |||
| else | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(rack == nullptr,); | |||
| rack = new EngineRackBuffers(bufferSize); | |||
| } | |||
| isReady = true; | |||
| } | |||
| void resize(const uint32_t bufferSize) | |||
| { | |||
| if (usePatchbay) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(patchbay != nullptr,); | |||
| patchbay->resize(bufferSize); | |||
| } | |||
| else | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(rack != nullptr,); | |||
| rack->resize(bufferSize); | |||
| } | |||
| } | |||
| } bufAudio; | |||
| #endif | |||
| struct InternalEvents { | |||
| EngineEvent* in; | |||
| EngineEvent* out; | |||
| @@ -346,6 +566,14 @@ struct CarlaEngineProtectedData { | |||
| nextAction.mutex.unlock(); | |||
| } | |||
| #ifndef BUILD_BRIDGE | |||
| // the base, where plugins run | |||
| void processRack(float* inBufReal[2], float* outBuf[2], const uint32_t nframes, const bool isOffline); | |||
| // extended, will call processRack() in the middle | |||
| void processRackFull(float** const inBuf, const uint32_t inCount, float** const outBuf, const uint32_t outCount, const uint32_t nframes, const bool isOffline); | |||
| #endif | |||
| class ScopedActionLock | |||
| { | |||
| public: | |||
| @@ -1133,7 +1133,7 @@ protected: | |||
| } | |||
| // process rack | |||
| processRack(inBuf, outBuf, nframes); | |||
| pData->processRack(inBuf, outBuf, nframes, fFreewheel); | |||
| // output control | |||
| { | |||
| @@ -21,7 +21,6 @@ | |||
| #include "CarlaEngineInternal.hpp" | |||
| #include "CarlaBackendUtils.hpp" | |||
| #include "CarlaMIDI.h" | |||
| // #include "RtList.hpp" | |||
| @@ -51,32 +50,102 @@ static void initJuceDevices() | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| // Juce Engine | |||
| class CarlaEngineJuce : public CarlaEngine/*, | |||
| public AudioIODeviceCallback*/ | |||
| class CarlaEngineJuce : public CarlaEngine, | |||
| public AudioIODeviceCallback | |||
| { | |||
| public: | |||
| CarlaEngineJuce() | |||
| : CarlaEngine() | |||
| CarlaEngineJuce(AudioIODeviceType* const devType) | |||
| : CarlaEngine(), | |||
| AudioIODeviceCallback(), | |||
| fDeviceType(devType) | |||
| { | |||
| carla_debug("CarlaEngineJuce::CarlaEngineJuce()"); | |||
| carla_debug("CarlaEngineJuce::CarlaEngineJuce(%p)", devType); | |||
| // just to make sure | |||
| pData->options.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL; | |||
| } | |||
| ~CarlaEngineJuce() override | |||
| { | |||
| carla_debug("CarlaEngineJuce::~CarlaEngineJuce()"); | |||
| if (gRetNames != nullptr) | |||
| { | |||
| delete[] gRetNames; | |||
| gRetNames = nullptr; | |||
| } | |||
| gJuceDeviceTypes.clear(true); | |||
| } | |||
| // ------------------------------------- | |||
| bool init(const char* const clientName) override | |||
| { | |||
| CARLA_ASSERT(clientName != nullptr && clientName[0] != '\0'); | |||
| carla_debug("CarlaEngineJuce::init(\"%s\")", clientName); | |||
| if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK && pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY) | |||
| { | |||
| setLastError("Invalid process mode"); | |||
| return false; | |||
| } | |||
| pData->bufAudio.usePatchbay = (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY); | |||
| String deviceName; | |||
| if (pData->options.audioDevice != nullptr && pData->options.audioDevice[0] != '\0') | |||
| { | |||
| deviceName = pData->options.audioDevice; | |||
| } | |||
| else | |||
| { | |||
| const int defaultIndex(fDeviceType->getDefaultDeviceIndex(false)); | |||
| StringArray deviceNames(fDeviceType->getDeviceNames()); | |||
| if (defaultIndex >= 0 && defaultIndex < deviceNames.size()) | |||
| deviceName = deviceNames[defaultIndex]; | |||
| } | |||
| if (deviceName.isEmpty()) | |||
| { | |||
| setLastError("something"); | |||
| return false; | |||
| } | |||
| fDevice = fDeviceType->createDevice(deviceName, deviceName); | |||
| if (fDevice == nullptr) | |||
| { | |||
| setLastError("something 2"); | |||
| return false; | |||
| } | |||
| BigInteger inputChannels; | |||
| BigInteger outputChannels; | |||
| String error = fDevice->open(inputChannels, outputChannels, pData->options.audioSampleRate, static_cast<int>(pData->options.audioBufferSize)); | |||
| if (error.isNotEmpty()) | |||
| { | |||
| fDevice = nullptr; | |||
| setLastError(error.toUTF8()); | |||
| return false; | |||
| } | |||
| fDevice->start(this); | |||
| //getActiveOutputChannels(); | |||
| //getActiveInputChannels(); | |||
| pData->bufferSize = fDevice->getCurrentBufferSizeSamples(); | |||
| pData->sampleRate = fDevice->getCurrentSampleRate(); | |||
| pData->bufAudio.create(pData->bufferSize); | |||
| CarlaEngine::init(clientName); | |||
| patchbayRefresh(); | |||
| return true; | |||
| } | |||
| @@ -84,12 +153,29 @@ public: | |||
| { | |||
| carla_debug("CarlaEngineJuce::close()"); | |||
| return CarlaEngine::close(); | |||
| pData->bufAudio.isReady = false; | |||
| bool hasError = !CarlaEngine::close(); | |||
| if (fDevice != nullptr) | |||
| { | |||
| if (fDevice->isPlaying()) | |||
| fDevice->stop(); | |||
| if (fDevice->isOpen()) | |||
| fDevice->close(); | |||
| fDevice = nullptr; | |||
| } | |||
| pData->bufAudio.clear(); | |||
| return !hasError; | |||
| } | |||
| bool isRunning() const noexcept override | |||
| { | |||
| return false; | |||
| return fDevice != nullptr && fDevice->isPlaying(); | |||
| } | |||
| bool isOffline() const noexcept override | |||
| @@ -104,45 +190,341 @@ public: | |||
| const char* getCurrentDriverName() const noexcept override | |||
| { | |||
| return nullptr; | |||
| return fDeviceType->getTypeName().toRawUTF8(); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Patchbay | |||
| bool patchbayRefresh() override | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(pData->bufAudio.isReady, false); | |||
| pData->bufAudio.initPatchbay(); | |||
| if (pData->bufAudio.usePatchbay) | |||
| { | |||
| // not implemented yet | |||
| return false; | |||
| } | |||
| char strBuf[STR_MAX+1]; | |||
| EngineRackBuffers* const rack(pData->bufAudio.rack); | |||
| // Main | |||
| { | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_PATCHBAY_GROUP_CARLA, 0, 0, 0.0f, getName()); | |||
| callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_PATCHBAY_GROUP_CARLA, RACK_PATCHBAY_PORT_AUDIO_IN1, PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, "audio-in1"); | |||
| callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_PATCHBAY_GROUP_CARLA, RACK_PATCHBAY_PORT_AUDIO_IN2, PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, "audio-in2"); | |||
| callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_PATCHBAY_GROUP_CARLA, RACK_PATCHBAY_PORT_AUDIO_OUT1, PATCHBAY_PORT_TYPE_AUDIO, 0.0f, "audio-out1"); | |||
| callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_PATCHBAY_GROUP_CARLA, RACK_PATCHBAY_PORT_AUDIO_OUT2, PATCHBAY_PORT_TYPE_AUDIO, 0.0f, "audio-out2"); | |||
| callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_PATCHBAY_GROUP_CARLA, RACK_PATCHBAY_PORT_MIDI_IN, PATCHBAY_PORT_TYPE_MIDI|PATCHBAY_PORT_IS_INPUT, 0.0f, "midi-in"); | |||
| callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_PATCHBAY_GROUP_CARLA, RACK_PATCHBAY_PORT_MIDI_OUT, PATCHBAY_PORT_TYPE_MIDI, 0.0f, "midi-out"); | |||
| } | |||
| // Audio In | |||
| { | |||
| /*if (fDeviceName.isNotEmpty()) | |||
| std::snprintf(strBuf, STR_MAX, "Capture (%s)", (const char*)fDeviceName); | |||
| else*/ | |||
| std::strncpy(strBuf, "Capture", STR_MAX); | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_PATCHBAY_GROUP_AUDIO_IN, 0, 0, 0.0f, strBuf); | |||
| /*for (unsigned int i=0; i < fAudioCountIn; ++i) | |||
| { | |||
| std::snprintf(strBuf, STR_MAX, "capture_%i", i+1); | |||
| callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_PATCHBAY_GROUP_AUDIO_IN, RACK_PATCHBAY_GROUP_AUDIO_IN*1000 + i, PATCHBAY_PORT_TYPE_AUDIO, 0.0f, strBuf); | |||
| }*/ | |||
| } | |||
| // Audio Out | |||
| { | |||
| /*if (fDeviceName.isNotEmpty()) | |||
| std::snprintf(strBuf, STR_MAX, "Playback (%s)", (const char*)fDeviceName); | |||
| else*/ | |||
| std::strncpy(strBuf, "Playback", STR_MAX); | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_PATCHBAY_GROUP_AUDIO_OUT, 0, 0, 0.0f, strBuf); | |||
| /*for (unsigned int i=0; i < fAudioCountOut; ++i) | |||
| { | |||
| std::snprintf(strBuf, STR_MAX, "playback_%i", i+1); | |||
| callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_PATCHBAY_GROUP_AUDIO_OUT, RACK_PATCHBAY_GROUP_AUDIO_OUT*1000 + i, PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, strBuf); | |||
| }*/ | |||
| } | |||
| #if 0 // midi-out not implemented yet | |||
| // MIDI In | |||
| { | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_PATCHBAY_GROUP_MIDI_IN, 0, 0, 0.0f, "Readable MIDI ports"); | |||
| for (unsigned int i=0, count=fDummyMidiIn.getPortCount(); i < count; ++i) | |||
| { | |||
| PortNameToId portNameToId; | |||
| portNameToId.portId = RACK_PATCHBAY_GROUP_MIDI_IN*1000 + i; | |||
| std::strncpy(portNameToId.name, fDummyMidiIn.getPortName(i).c_str(), STR_MAX); | |||
| fUsedMidiIns.append(portNameToId); | |||
| callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_PATCHBAY_GROUP_MIDI_IN, portNameToId.portId, PATCHBAY_PORT_TYPE_MIDI, 0.0f, portNameToId.name); | |||
| } | |||
| } | |||
| // MIDI Out | |||
| { | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, 0, RACK_PATCHBAY_GROUP_MIDI_OUT, 0, 0.0f, "Writable MIDI ports"); | |||
| for (unsigned int i=0, count=fDummyMidiOut.getPortCount(); i < count; ++i) | |||
| { | |||
| PortNameToId portNameToId; | |||
| portNameToId.portId = RACK_PATCHBAY_GROUP_MIDI_OUT*1000 + i; | |||
| std::strncpy(portNameToId.name, fDummyMidiOut.getPortName(i).c_str(), STR_MAX); | |||
| fUsedMidiOuts.append(portNameToId); | |||
| callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, 0, RACK_PATCHBAY_GROUP_MIDI_OUT, portNameToId.portId, PATCHBAY_PORT_TYPE_MIDI|PATCHBAY_PORT_IS_INPUT, portNameToId.name); | |||
| } | |||
| } | |||
| #endif | |||
| // Connections | |||
| rack->connectLock.lock(); | |||
| for (List<uint>::Itenerator it = rack->connectedIns[0].begin(); it.valid(); it.next()) | |||
| { | |||
| const uint& port(it.getConstValue()); | |||
| //CARLA_SAFE_ASSERT_CONTINUE(port < fAudioCountIn); // FIXME | |||
| ConnectionToId connectionToId; | |||
| connectionToId.id = rack->lastConnectionId; | |||
| connectionToId.portOut = RACK_PATCHBAY_GROUP_AUDIO_IN*1000 + port; | |||
| connectionToId.portIn = RACK_PATCHBAY_PORT_AUDIO_IN1; | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, rack->lastConnectionId, connectionToId.portOut, connectionToId.portIn, 0.0f, nullptr); | |||
| rack->usedConnections.append(connectionToId); | |||
| rack->lastConnectionId++; | |||
| } | |||
| for (List<uint>::Itenerator it = rack->connectedIns[1].begin(); it.valid(); it.next()) | |||
| { | |||
| const uint& port(it.getConstValue()); | |||
| //CARLA_SAFE_ASSERT_CONTINUE(port < fAudioCountIn); // FIXME | |||
| ConnectionToId connectionToId; | |||
| connectionToId.id = rack->lastConnectionId; | |||
| connectionToId.portOut = RACK_PATCHBAY_GROUP_AUDIO_IN*1000 + port; | |||
| connectionToId.portIn = RACK_PATCHBAY_PORT_AUDIO_IN2; | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, rack->lastConnectionId, connectionToId.portOut, connectionToId.portIn, 0.0f, nullptr); | |||
| rack->usedConnections.append(connectionToId); | |||
| rack->lastConnectionId++; | |||
| } | |||
| for (List<uint>::Itenerator it = rack->connectedOuts[0].begin(); it.valid(); it.next()) | |||
| { | |||
| const uint& port(it.getConstValue()); | |||
| //CARLA_SAFE_ASSERT_CONTINUE(port < fAudioCountOut); // FIXME | |||
| ConnectionToId connectionToId; | |||
| connectionToId.id = rack->lastConnectionId; | |||
| connectionToId.portOut = RACK_PATCHBAY_PORT_AUDIO_OUT1; | |||
| connectionToId.portIn = RACK_PATCHBAY_GROUP_AUDIO_OUT*1000 + port; | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, rack->lastConnectionId, connectionToId.portOut, connectionToId.portIn, 0.0f, nullptr); | |||
| rack->usedConnections.append(connectionToId); | |||
| rack->lastConnectionId++; | |||
| } | |||
| for (List<uint>::Itenerator it = rack->connectedOuts[1].begin(); it.valid(); it.next()) | |||
| { | |||
| const uint& port(it.getConstValue()); | |||
| //CARLA_SAFE_ASSERT_CONTINUE(port < fAudioCountOut); // FIXME | |||
| ConnectionToId connectionToId; | |||
| connectionToId.id = rack->lastConnectionId; | |||
| connectionToId.portOut = RACK_PATCHBAY_PORT_AUDIO_OUT2; | |||
| connectionToId.portIn = RACK_PATCHBAY_GROUP_AUDIO_OUT*1000 + port; | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, rack->lastConnectionId, connectionToId.portOut, connectionToId.portIn, 0.0f, nullptr); | |||
| rack->usedConnections.append(connectionToId); | |||
| rack->lastConnectionId++; | |||
| } | |||
| pData->bufAudio.rack->connectLock.unlock(); | |||
| #if 0 | |||
| for (List<MidiPort>::Itenerator it=fMidiIns.begin(); it.valid(); it.next()) | |||
| { | |||
| const MidiPort& midiPort(it.getConstValue()); | |||
| ConnectionToId connectionToId; | |||
| connectionToId.id = rack->lastConnectionId; | |||
| connectionToId.portOut = RACK_PATCHBAY_GROUP_MIDI_IN*1000 + midiPort.portId; | |||
| connectionToId.portIn = RACK_PATCHBAY_PORT_MIDI_IN; | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, rack->lastConnectionId, connectionToId.portOut, connectionToId.portIn, 0.0f, nullptr); | |||
| rack->usedConnections.append(connectionToId); | |||
| rack->lastConnectionId++; | |||
| } | |||
| for (List<MidiPort>::Itenerator it=fMidiOuts.begin(); it.valid(); it.next()) | |||
| { | |||
| const MidiPort& midiPort(it.getConstValue()); | |||
| ConnectionToId connectionToId; | |||
| connectionToId.id = rack->lastConnectionId; | |||
| connectionToId.portOut = RACK_PATCHBAY_PORT_MIDI_OUT; | |||
| connectionToId.portIn = RACK_PATCHBAY_GROUP_MIDI_OUT*1000 + midiPort.portId; | |||
| callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, rack->lastConnectionId, connectionToId.portOut, connectionToId.portIn, 0.0f, nullptr); | |||
| rack->usedConnections.append(connectionToId); | |||
| rack->lastConnectionId++; | |||
| } | |||
| #endif | |||
| return true; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| protected: | |||
| // void audioDeviceIOCallback (const float** inputChannelData, | |||
| // int numInputChannels, | |||
| // float** outputChannelData, | |||
| // int numOutputChannels, | |||
| // int numSamples) | |||
| // { | |||
| // } | |||
| // | |||
| // void audioDeviceAboutToStart (juce::AudioIODevice* device) | |||
| // { | |||
| // } | |||
| // | |||
| // void audioDeviceStopped() | |||
| // { | |||
| // } | |||
| // | |||
| // void audioDeviceError (const juce::String& errorMessage) | |||
| // { | |||
| // } | |||
| void audioDeviceIOCallback(const float** inputChannelData, int numInputChannels, float** outputChannelData, int numOutputChannels, int numSamples) override | |||
| { | |||
| // assert juce buffers | |||
| CARLA_SAFE_ASSERT_RETURN(outputChannelData != nullptr,); | |||
| CARLA_SAFE_ASSERT_RETURN(numSamples == static_cast<int>(pData->bufferSize),); | |||
| if (numOutputChannels == 0 || ! pData->bufAudio.isReady) | |||
| return runPendingRtEvents(); | |||
| // initialize input events | |||
| carla_zeroStruct<EngineEvent>(pData->bufEvents.in, kEngineMaxInternalEventCount); | |||
| // TODO - get events from juce | |||
| if (pData->bufAudio.usePatchbay) | |||
| { | |||
| } | |||
| else | |||
| { | |||
| pData->processRackFull(const_cast<float**>(inputChannelData), numInputChannels, outputChannelData, numOutputChannels, numSamples, false); | |||
| } | |||
| // output events | |||
| { | |||
| // TODO | |||
| //fMidiOutEvents... | |||
| } | |||
| runPendingRtEvents(); | |||
| return; | |||
| // unused | |||
| (void)inputChannelData; | |||
| (void)numInputChannels; | |||
| } | |||
| void audioDeviceAboutToStart(AudioIODevice* /*device*/) override | |||
| { | |||
| } | |||
| void audioDeviceStopped() override | |||
| { | |||
| } | |||
| void audioDeviceError(const String& errorMessage) override | |||
| { | |||
| callback(ENGINE_CALLBACK_ERROR, 0, 0, 0, 0.0f, errorMessage.toRawUTF8()); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| bool connectRackMidiInPort(const int) override | |||
| { | |||
| return false; | |||
| } | |||
| bool connectRackMidiOutPort(const int) override | |||
| { | |||
| return false; | |||
| } | |||
| bool disconnectRackMidiInPort(const int) override | |||
| { | |||
| return false; | |||
| } | |||
| bool disconnectRackMidiOutPort(const int) override | |||
| { | |||
| return false; | |||
| } | |||
| // ------------------------------------- | |||
| private: | |||
| //juce::AudioIODeviceType* fDeviceType; | |||
| ScopedPointer<AudioIODevice> fDevice; | |||
| AudioIODeviceType* const fDeviceType; | |||
| //JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineJuce) | |||
| CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineJuce) | |||
| }; | |||
| // ----------------------------------------- | |||
| CarlaEngine* CarlaEngine::newJuce(const AudioApi /*api*/) | |||
| CarlaEngine* CarlaEngine::newJuce(const AudioApi api) | |||
| { | |||
| return new CarlaEngineJuce(); | |||
| initJuceDevices(); | |||
| String juceApi; | |||
| switch (api) | |||
| { | |||
| case AUDIO_API_NULL: | |||
| case AUDIO_API_OSS: | |||
| case AUDIO_API_PULSE: | |||
| break; | |||
| case AUDIO_API_JACK: | |||
| juceApi = "JACK"; | |||
| break; | |||
| case AUDIO_API_ALSA: | |||
| juceApi = "ALSA"; | |||
| break; | |||
| case AUDIO_API_CORE: | |||
| juceApi = "CoreAudio"; | |||
| break; | |||
| case AUDIO_API_ASIO: | |||
| juceApi = "ASIO"; | |||
| break; | |||
| case AUDIO_API_DS: | |||
| juceApi = "DirectSound"; | |||
| break; | |||
| } | |||
| if (juceApi.isEmpty()) | |||
| return nullptr; | |||
| AudioIODeviceType* deviceType = nullptr; | |||
| for (int i=0, count=gJuceDeviceTypes.size(); i < count; ++i) | |||
| { | |||
| deviceType = gJuceDeviceTypes[i]; | |||
| if (deviceType == nullptr || deviceType->getTypeName() == juceApi) | |||
| break; | |||
| } | |||
| if (deviceType == nullptr) | |||
| return nullptr; | |||
| deviceType->scanForDevices(); | |||
| return new CarlaEngineJuce(deviceType); | |||
| } | |||
| unsigned int CarlaEngine::getJuceApiCount() | |||
| @@ -519,14 +519,14 @@ protected: | |||
| // ----------------------------------------------------------- | |||
| // create audio buffers | |||
| float* inBuf[2] = { inBuffer[0], inBuffer[1] }; | |||
| float* outBuf[2] = { outBuffer[0], outBuffer[1] }; | |||
| uint32_t bufCount[2] = { 2, 2 }; | |||
| //float* inBuf[2] = { inBuffer[0], inBuffer[1] }; | |||
| //float* outBuf[2] = { outBuffer[0], outBuffer[1] }; | |||
| //uint32_t bufCount[2] = { 2, 2 }; | |||
| // ----------------------------------------------------------- | |||
| // process | |||
| processPatchbay(inBuf, outBuf, bufCount, frames); | |||
| //pData->processPatchbay(inBuf, outBuf, bufCount, frames, isOffline()); | |||
| } | |||
| else | |||
| { | |||
| @@ -539,7 +539,7 @@ protected: | |||
| // ----------------------------------------------------------- | |||
| // process | |||
| processRack(inBuf, outBuf, frames); | |||
| pData->processRack(inBuf, outBuf, frames, isOffline()); | |||
| } | |||
| // --------------------------------------------------------------- | |||