From ababc774599748ab0dffcd7a75c39bf176c00544 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 7 Jun 2014 13:03:38 +0100 Subject: [PATCH] MIDI input working in juce drivers --- source/backend/engine/CarlaEngineJuce.cpp | 322 ++++++++++++++++++- source/backend/engine/CarlaEngineRtAudio.cpp | 91 +++--- 2 files changed, 355 insertions(+), 58 deletions(-) diff --git a/source/backend/engine/CarlaEngineJuce.cpp b/source/backend/engine/CarlaEngineJuce.cpp index 94b5782e0..5f45023f9 100644 --- a/source/backend/engine/CarlaEngineJuce.cpp +++ b/source/backend/engine/CarlaEngineJuce.cpp @@ -40,7 +40,7 @@ CARLA_BACKEND_START_NAMESPACE static const char** gRetNames = nullptr; static OwnedArray gJuceDeviceTypes; -struct JuceCleanup { +struct JuceCleanup : public DeletedAtShutdown { JuceCleanup() noexcept {} ~JuceCleanup() { @@ -62,7 +62,6 @@ struct JuceCleanup { static void initJuceDevicesIfNeeded() { - static const JuceCleanup sJuceCleanup; static AudioDeviceManager sDeviceManager; static bool needsInit = true; @@ -70,6 +69,7 @@ static void initJuceDevicesIfNeeded() return; needsInit = false; + new JuceCleanup(); sDeviceManager.createAudioDeviceTypes(gJuceDeviceTypes); @@ -87,7 +87,8 @@ static void initJuceDevicesIfNeeded() // Juce Engine class CarlaEngineJuce : public CarlaEngine, - public AudioIODeviceCallback + public AudioIODeviceCallback, + public MidiInputCallback { public: CarlaEngineJuce(AudioIODeviceType* const devType) @@ -211,6 +212,28 @@ public: fDevice = nullptr; } + for (LinkedList::Itenerator it = fMidiIns.begin(); it.valid(); it.next()) + { + MidiInPort& inPort(it.getValue()); + CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr); + + inPort.port->stop(); + delete inPort.port; + } + + for (LinkedList::Itenerator it = fMidiOuts.begin(); it.valid(); it.next()) + { + MidiOutPort& outPort(it.getValue()); + CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr); + + outPort.port->stopBackgroundThread(); + delete outPort.port; + } + + fMidiIns.clear(); + fMidiOuts.clear(); + fMidiInEvents.clear(); + return !hasError; } @@ -307,7 +330,47 @@ public: callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, RACK_GRAPH_GROUP_AUDIO_OUT, static_cast(i), PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, outputNames[i].toRawUTF8()); } - // TODO - MIDI + // MIDI In + { + StringArray midiIns(MidiInput::getDevices()); + + callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_GRAPH_GROUP_MIDI_IN, PATCHBAY_ICON_HARDWARE, -1, 0.0f, "Readable MIDI ports"); + + for (int i=0, count=midiIns.size(); i(i), portName.toRawUTF8(), strBuf); + + callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, portNameToId.group, static_cast(portNameToId.port), PATCHBAY_PORT_TYPE_MIDI, 0.0f, portNameToId.name); + + rack->midi.ins.append(portNameToId); + } + } + + // MIDI Out + { + StringArray midiOuts(MidiOutput::getDevices()); + + callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, RACK_GRAPH_GROUP_MIDI_OUT, PATCHBAY_ICON_HARDWARE, -1, 0.0f, "Writable MIDI ports"); + + for (int i=0, count=midiOuts.size(); i(i), portName.toRawUTF8(), strBuf); + + callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, portNameToId.group, static_cast(portNameToId.port), PATCHBAY_PORT_TYPE_MIDI|PATCHBAY_PORT_IS_INPUT, 0.0f, portNameToId.name); + + rack->midi.outs.append(portNameToId); + } + } // Connections rack->audio.mutex.lock(); @@ -374,7 +437,39 @@ public: rack->audio.mutex.unlock(); - // TODO - MIDI + for (LinkedList::Itenerator it=fMidiIns.begin(); it.valid(); it.next()) + { + const MidiInPort& inPort(it.getValue()); + + const uint portId(rack->midi.getPortId(true, inPort.name)); + CARLA_SAFE_ASSERT_CONTINUE(portId < rack->midi.ins.count()); + + ConnectionToId connectionToId; + connectionToId.setData(++(rack->connections.lastId), RACK_GRAPH_GROUP_MIDI_IN, portId, RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_MIDI_IN); + + std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB); + + callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf); + + rack->connections.list.append(connectionToId); + } + + for (LinkedList::Itenerator it=fMidiOuts.begin(); it.valid(); it.next()) + { + const MidiOutPort& outPort(it.getValue()); + + const uint portId(rack->midi.getPortId(false, outPort.name)); + CARLA_SAFE_ASSERT_CONTINUE(portId < rack->midi.outs.count()); + + ConnectionToId connectionToId; + connectionToId.setData(++(rack->connections.lastId), RACK_GRAPH_GROUP_CARLA, RACK_GRAPH_CARLA_PORT_MIDI_OUT, RACK_GRAPH_GROUP_MIDI_OUT, portId); + + std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB); + + callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf); + + rack->connections.list.append(connectionToId); + } } void patchbayRefreshPatchbay() noexcept @@ -395,6 +490,8 @@ protected: if (! pData->audio.isReady) return runPendingRtEvents(); + const uint32_t nframes(static_cast(numSamples)); + // initialize juce output for (int i=0; i < numOutputChannels; ++i) FloatVectorOperations::clear(outputChannelData[i], numSamples); @@ -402,7 +499,37 @@ protected: // initialize input events carla_zeroStruct(pData->events.in, kMaxEngineEventInternalCount); - // TODO - get events from juce + if (fMidiInEvents.mutex.tryLock()) + { + uint32_t engineEventIndex = 0; + fMidiInEvents.splice(); + + for (LinkedList::Itenerator it = fMidiInEvents.data.begin(); it.valid(); it.next()) + { + const RtMidiEvent& midiEvent(it.getValue()); + EngineEvent& engineEvent(pData->events.in[engineEventIndex++]); + + if (midiEvent.time < pData->timeInfo.frame) + { + engineEvent.time = 0; + } + else if (midiEvent.time >= pData->timeInfo.frame + nframes) + { + carla_stderr("MIDI Event in the future!, %i vs %i", engineEvent.time, pData->timeInfo.frame); + engineEvent.time = static_cast(pData->timeInfo.frame) + nframes - 1; + } + else + engineEvent.time = static_cast(midiEvent.time - pData->timeInfo.frame); + + engineEvent.fillFromMidiData(midiEvent.size, midiEvent.data); + + if (engineEventIndex >= kMaxEngineEventInternalCount) + break; + } + + fMidiInEvents.data.clear(); + fMidiInEvents.mutex.unlock(); + } if (pData->graph.isRack) { @@ -439,23 +566,135 @@ protected: // ------------------------------------------------------------------- - bool connectRackMidiInPort(const char* const /*portName*/) override + void handleIncomingMidiMessage(MidiInput* /*source*/, const MidiMessage& message) override { - return false; + if (! pData->audio.isReady) + return; + + const int messageSize(message.getRawDataSize()); + + if (messageSize <= 0 || messageSize > EngineMidiEvent::kDataSize) + return; + + const uint8_t* const messageData(message.getRawData()); + + RtMidiEvent midiEvent; + midiEvent.time = 0; // TODO + + midiEvent.size = static_cast(messageSize); + + int i=0; + for (; i < messageSize; ++i) + midiEvent.data[i] = messageData[i]; + for (; i < EngineMidiEvent::kDataSize; ++i) + midiEvent.data[i] = 0; + + fMidiInEvents.appendNonRT(midiEvent); } - bool connectRackMidiOutPort(const char* const /*portName*/) override + // ------------------------------------------------------------------- + + bool connectRackMidiInPort(const char* const portName) override { - return false; + CARLA_SAFE_ASSERT_RETURN(portName != nullptr && portName[0] != '\0', false); + carla_debug("CarlaEngineJuce::connectRackMidiInPort(\"%s\")", portName); + + RackGraph* const rack(pData->graph.rack); + CARLA_SAFE_ASSERT_RETURN(rack->midi.ins.count() > 0, false); + + StringArray midiIns(MidiInput::getDevices()); + + if (! midiIns.contains(portName)) + return false; + + MidiInput* const juceMidiIn(MidiInput::openDevice(midiIns.indexOf(portName), this)); + juceMidiIn->start(); + + MidiInPort midiPort; + midiPort.port = juceMidiIn; + + std::strncpy(midiPort.name, portName, STR_MAX); + midiPort.name[STR_MAX] = '\0'; + + fMidiIns.append(midiPort); + return true; } - bool disconnectRackMidiInPort(const char* const /*portName*/) override + bool connectRackMidiOutPort(const char* const portName) override { + CARLA_SAFE_ASSERT_RETURN(portName != nullptr && portName[0] != '\0', false); + carla_debug("CarlaEngineJuce::connectRackMidiOutPort(\"%s\")", portName); + + RackGraph* const rack(pData->graph.rack); + CARLA_SAFE_ASSERT_RETURN(rack->midi.ins.count() > 0, false); + + StringArray midiOuts(MidiOutput::getDevices()); + + if (! midiOuts.contains(portName)) + return false; + + MidiOutput* const juceMidiOut(MidiOutput::openDevice(midiOuts.indexOf(portName))); + juceMidiOut->startBackgroundThread(); + + MidiOutPort midiPort; + midiPort.port = juceMidiOut; + + std::strncpy(midiPort.name, portName, STR_MAX); + midiPort.name[STR_MAX] = '\0'; + + fMidiOuts.append(midiPort); + return true; + } + + bool disconnectRackMidiInPort(const char* const portName) override + { + CARLA_SAFE_ASSERT_RETURN(portName != nullptr && portName[0] != '\0', false); + carla_debug("CarlaEngineRtAudio::disconnectRackMidiInPort(\"%s\")", portName); + + RackGraph* const rack(pData->graph.rack); + CARLA_SAFE_ASSERT_RETURN(rack->midi.ins.count() > 0, false); + + for (LinkedList::Itenerator it=fMidiIns.begin(); it.valid(); it.next()) + { + MidiInPort& inPort(it.getValue()); + CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr); + + if (std::strcmp(inPort.name, portName) != 0) + continue; + + inPort.port->stop(); + delete inPort.port; + + fMidiIns.remove(it); + return true; + } + return false; } - bool disconnectRackMidiOutPort(const char* const /*portName*/) override + bool disconnectRackMidiOutPort(const char* const portName) override { + CARLA_SAFE_ASSERT_RETURN(portName != nullptr && portName[0] != '\0', false); + carla_debug("CarlaEngineRtAudio::disconnectRackMidiOutPort(\"%s\")", portName); + + RackGraph* const rack(pData->graph.rack); + CARLA_SAFE_ASSERT_RETURN(rack->midi.outs.count() > 0, false); + + for (LinkedList::Itenerator it=fMidiOuts.begin(); it.valid(); it.next()) + { + MidiOutPort& outPort(it.getValue()); + CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr); + + if (std::strcmp(outPort.name, portName) != 0) + continue; + + outPort.port->stopBackgroundThread(); + delete outPort.port; + + fMidiOuts.remove(it); + return true; + } + return false; } @@ -465,6 +704,65 @@ private: ScopedPointer fDevice; AudioIODeviceType* const fDeviceType; + struct MidiInPort { + MidiInput* port; + char name[STR_MAX+1]; + }; + + struct MidiOutPort { + MidiOutput* port; + char name[STR_MAX+1]; + }; + + LinkedList fMidiIns; + LinkedList fMidiOuts; + + struct RtMidiEvent { + uint64_t time; // needs to compare to internal time + uint8_t size; + uint8_t data[EngineMidiEvent::kDataSize]; + }; + + struct RtMidiEvents { + CarlaMutex mutex; + RtLinkedList::Pool dataPool; + RtLinkedList data; + RtLinkedList dataPending; + + // FIXME - 32, 512 + append_sleepy? check plugin code + RtMidiEvents() + : dataPool(512, 512), + data(dataPool), + dataPending(dataPool) {} + + ~RtMidiEvents() + { + clear(); + } + + void appendNonRT(const RtMidiEvent& event) + { + mutex.lock(); + dataPending.append(event); + mutex.unlock(); + } + + void clear() + { + mutex.lock(); + data.clear(); + dataPending.clear(); + mutex.unlock(); + } + + void splice() + { + dataPending.spliceAppendTo(data); + } + }; + + RtMidiEvents fMidiInEvents; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineJuce) }; diff --git a/source/backend/engine/CarlaEngineRtAudio.cpp b/source/backend/engine/CarlaEngineRtAudio.cpp index c3d2ce30e..5402caa92 100644 --- a/source/backend/engine/CarlaEngineRtAudio.cpp +++ b/source/backend/engine/CarlaEngineRtAudio.cpp @@ -192,8 +192,6 @@ public: CARLA_SAFE_ASSERT(fAudioOutCount == 0); CARLA_SAFE_ASSERT(fLastEventTime == 0); carla_debug("CarlaEngineRtAudio::~CarlaEngineRtAudio()"); - - fUsedMidiPorts.clear(); } // ------------------------------------- @@ -343,21 +341,23 @@ public: fAudio.closeStream(); } - for (LinkedList::Itenerator it = fMidiIns.begin(); it.valid(); it.next()) + for (LinkedList::Itenerator it = fMidiIns.begin(); it.valid(); it.next()) { - MidiPort& port(it.getValue()); - RtMidiIn* const midiInPort((RtMidiIn*)port.rtmidi); + MidiInPort& inPort(it.getValue()); + CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr); - midiInPort->cancelCallback(); - delete midiInPort; + inPort.port->cancelCallback(); + inPort.port->closePort(); + delete inPort.port; } - for (LinkedList::Itenerator it = fMidiOuts.begin(); it.valid(); it.next()) + for (LinkedList::Itenerator it = fMidiOuts.begin(); it.valid(); it.next()) { - MidiPort& port(it.getValue()); - RtMidiOut* const midiOutPort((RtMidiOut*)port.rtmidi); + MidiOutPort& outPort(it.getValue()); + CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr); - delete midiOutPort; + outPort.port->closePort(); + delete outPort.port; } fAudioInCount = 0; @@ -365,12 +365,10 @@ public: fLastEventTime = 0; fDeviceName.clear(); - fUsedMidiPorts.clear(); - fMidiInEvents.clear(); - //fMidiOutEvents.clear(); - fMidiIns.clear(); fMidiOuts.clear(); + fMidiInEvents.clear(); + //fMidiOutEvents.clear(); return !hasError; } @@ -402,8 +400,6 @@ public: { CARLA_SAFE_ASSERT_RETURN(pData->audio.isReady, false); - fUsedMidiPorts.clear(); - if (pData->graph.isRack) patchbayRefreshRack(); else @@ -572,11 +568,11 @@ public: rack->audio.mutex.unlock(); - for (LinkedList::Itenerator it=fMidiIns.begin(); it.valid(); it.next()) + for (LinkedList::Itenerator it=fMidiIns.begin(); it.valid(); it.next()) { - const MidiPort& midiPort(it.getValue()); + const MidiInPort& inPort(it.getValue()); - const uint portId(rack->midi.getPortId(true, midiPort.name)); + const uint portId(rack->midi.getPortId(true, inPort.name)); CARLA_SAFE_ASSERT_CONTINUE(portId < rack->midi.ins.count()); ConnectionToId connectionToId; @@ -589,11 +585,11 @@ public: rack->connections.list.append(connectionToId); } - for (LinkedList::Itenerator it=fMidiOuts.begin(); it.valid(); it.next()) + for (LinkedList::Itenerator it=fMidiOuts.begin(); it.valid(); it.next()) { - const MidiPort& midiPort(it.getValue()); + const MidiOutPort& outPort(it.getValue()); - const uint portId(rack->midi.getPortId(false, midiPort.name)); + const uint portId(rack->midi.getPortId(false, outPort.name)); CARLA_SAFE_ASSERT_CONTINUE(portId < rack->midi.outs.count()); ConnectionToId connectionToId; @@ -780,8 +776,8 @@ protected: return false; }; - MidiPort midiPort; - midiPort.rtmidi = rtMidiIn; + MidiInPort midiPort; + midiPort.port = rtMidiIn; std::strncpy(midiPort.name, portName, STR_MAX); midiPort.name[STR_MAX] = '\0'; @@ -826,8 +822,8 @@ protected: rtMidiOut->openPort(rtMidiPortIndex, portName); - MidiPort midiPort; - midiPort.rtmidi = rtMidiOut; + MidiOutPort midiPort; + midiPort.port = rtMidiOut; std::strncpy(midiPort.name, portName, STR_MAX); midiPort.name[STR_MAX] = '\0'; @@ -844,17 +840,17 @@ protected: RackGraph* const rack(pData->graph.rack); CARLA_SAFE_ASSERT_RETURN(rack->midi.ins.count() > 0, false); - for (LinkedList::Itenerator it=fMidiIns.begin(); it.valid(); it.next()) + for (LinkedList::Itenerator it=fMidiIns.begin(); it.valid(); it.next()) { - MidiPort& midiPort(it.getValue()); + MidiInPort& inPort(it.getValue()); + CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr); - if (std::strcmp(midiPort.name, portName) != 0) + if (std::strcmp(inPort.name, portName) != 0) continue; - RtMidiIn* const midiInPort((RtMidiIn*)midiPort.rtmidi); - - midiInPort->cancelCallback(); - delete midiInPort; + inPort.port->cancelCallback(); + inPort.port->closePort(); + delete inPort.port; fMidiIns.remove(it); return true; @@ -869,18 +865,18 @@ protected: carla_debug("CarlaEngineRtAudio::disconnectRackMidiOutPort(\"%s\")", portName); RackGraph* const rack(pData->graph.rack); - CARLA_SAFE_ASSERT_RETURN(rack->midi.ins.count() > 0, false); + CARLA_SAFE_ASSERT_RETURN(rack->midi.outs.count() > 0, false); - for (LinkedList::Itenerator it=fMidiOuts.begin(); it.valid(); it.next()) + for (LinkedList::Itenerator it=fMidiOuts.begin(); it.valid(); it.next()) { - MidiPort& midiPort(it.getValue()); + MidiOutPort& outPort(it.getValue()); + CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr); - if (std::strcmp(midiPort.name, portName) != 0) + if (std::strcmp(outPort.name, portName) != 0) continue; - RtMidiOut* const midiOutPort((RtMidiOut*)midiPort.rtmidi); - - delete midiOutPort; + outPort.port->closePort(); + delete outPort.port; fMidiOuts.remove(it); return true; @@ -902,15 +898,18 @@ private: // current device name CarlaString fDeviceName; - PatchbayPortList fUsedMidiPorts; + struct MidiInPort { + RtMidiIn* port; + char name[STR_MAX+1]; + }; - struct MidiPort { - RtMidi* rtmidi; + struct MidiOutPort { + RtMidiOut* port; char name[STR_MAX+1]; }; - LinkedList fMidiIns; - LinkedList fMidiOuts; + LinkedList fMidiIns; + LinkedList fMidiOuts; struct RtMidiEvent { uint64_t time; // needs to compare to internal time