diff --git a/source/backend/Makefile b/source/backend/Makefile index 9424a028c..d5527c180 100644 --- a/source/backend/Makefile +++ b/source/backend/Makefile @@ -54,6 +54,7 @@ UTILS_LIBS += $(MODULEDIR)/juce_audio_formats.a UTILS_LIBS += $(MODULEDIR)/juce_core.a UTILS_LIBS += $(MODULEDIR)/lilv.a UTILS_LIBS += $(MODULEDIR)/native-plugins.a +UTILS_LIBS += $(MODULEDIR)/rtmempool.a ifeq ($(HAVE_DGL),true) UTILS_LIBS += $(MODULEDIR)/dgl.a @@ -109,6 +110,7 @@ UTILS_LINK_FLAGS += $(JUCE_AUDIO_FORMATS_LIBS) UTILS_LINK_FLAGS += $(JUCE_CORE_LIBS) UTILS_LINK_FLAGS += $(LILV_LIBS) UTILS_LINK_FLAGS += $(NATIVE_PLUGINS_LIBS) +UTILS_LINK_FLAGS += $(RTMEMPOOL_LIBS) ifneq ($(HAIKU),true) UTILS_LINK_FLAGS += -lpthread diff --git a/source/native-plugins/Makefile b/source/native-plugins/Makefile index 3dc47faad..8882e0192 100644 --- a/source/native-plugins/Makefile +++ b/source/native-plugins/Makefile @@ -72,6 +72,7 @@ OBJS += \ $(OBJDIR)/audio-file.cpp.o \ $(OBJDIR)/bigmeter.cpp.o \ $(OBJDIR)/midi-file.cpp.o \ + $(OBJDIR)/midi-sequencer.cpp.o \ $(OBJDIR)/notes.cpp.o # ---------------------------------------------------------------------------------------------------------------------------- diff --git a/source/native-plugins/_all.c b/source/native-plugins/_all.c index 1e67bfd42..275995916 100644 --- a/source/native-plugins/_all.c +++ b/source/native-plugins/_all.c @@ -32,11 +32,12 @@ extern void carla_register_native_plugin_midithrough(void); extern void carla_register_native_plugin_miditranspose(void); extern void carla_register_native_plugin_nekofilter(void); -// Audio File +// Audio file extern void carla_register_native_plugin_audiofile(void); -// MIDI File +// MIDI file and sequencer extern void carla_register_native_plugin_midifile(void); +extern void carla_register_native_plugin_midisequencer(void); // Carla extern void carla_register_native_plugin_carla(void); @@ -70,8 +71,6 @@ extern void carla_register_native_plugin_zita_jaaa(void); // ----------------------------------------------------------------------- -void carla_register_all_plugins(void); - void carla_register_all_plugins(void) { // Simple plugins @@ -83,11 +82,12 @@ void carla_register_all_plugins(void) carla_register_native_plugin_miditranspose(); carla_register_native_plugin_nekofilter(); - // Audio File + // Audio file carla_register_native_plugin_audiofile(); - // MIDI File + // MIDI file and sequencer carla_register_native_plugin_midifile(); + carla_register_native_plugin_midisequencer(); // Carla carla_register_native_plugin_carla(); diff --git a/source/native-plugins/midi-base.hpp b/source/native-plugins/midi-base.hpp index 70039e5e8..2703aa60d 100644 --- a/source/native-plugins/midi-base.hpp +++ b/source/native-plugins/midi-base.hpp @@ -186,6 +186,34 @@ public: appendSorted(rawEvent); } + // ------------------------------------------------------------------- + // remove data + + void removeRaw(const uint64_t time, const uint8_t* const data, const uint8_t size) + { + const CarlaMutexLocker sl(fMutex); + + for (LinkedList::Itenerator it = fData.begin(); it.valid(); it.next()) + { + const RawMidiEvent* const rawMidiEvent(it.getValue(nullptr)); + CARLA_SAFE_ASSERT_CONTINUE(rawMidiEvent != nullptr); + + if (rawMidiEvent->time != time) + continue; + if (rawMidiEvent->size != size) + continue; + if (std::memcmp(rawMidiEvent->data, data, size) != 0) + continue; + + delete rawMidiEvent; + fData.remove(it); + + return; + } + + carla_stderr("MidiPattern::removeRaw(" P_INT64 ", %p, %i) - unable to find event to remove", time, data, size); + } + // ------------------------------------------------------------------- // clear @@ -239,6 +267,19 @@ public: } // ------------------------------------------------------------------- + // special + + const CarlaMutex& getLock() const noexcept + { + return fMutex; + } + + LinkedList::Itenerator iteneratorBegin() const noexcept + { + return fData.begin(); + } + + // ------------------------------------------------------------------- private: AbstractMidiPlayer* const kPlayer; diff --git a/source/native-plugins/midi-file.cpp b/source/native-plugins/midi-file.cpp index 7c7190ca8..84c69d931 100644 --- a/source/native-plugins/midi-file.cpp +++ b/source/native-plugins/midi-file.cpp @@ -199,7 +199,8 @@ static const NativePluginDescriptor midifileDesc = { /* category */ NATIVE_PLUGIN_CATEGORY_UTILITY, /* hints */ static_cast(NATIVE_PLUGIN_IS_RTSAFE |NATIVE_PLUGIN_HAS_UI - |NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE), + |NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE + |NATIVE_PLUGIN_USES_TIME), /* supports */ static_cast(0x0), /* audioIns */ 0, /* audioOuts */ 0, diff --git a/source/native-plugins/midi-sequencer.cpp b/source/native-plugins/midi-sequencer.cpp index 133c5856d..64795dd69 100644 --- a/source/native-plugins/midi-sequencer.cpp +++ b/source/native-plugins/midi-sequencer.cpp @@ -15,19 +15,24 @@ * For a full copy of the GNU General Public License see the doc/GPL.txt file. */ -#include "CarlaNative.hpp" +#include "CarlaNativeExtUI.hpp" +#include "RtLinkedList.hpp" + #include "midi-base.hpp" // ----------------------------------------------------------------------- -class MidiSequencerPlugin : public NativePluginClass, +class MidiSequencerPlugin : public NativePluginAndUiClass, public AbstractMidiPlayer { public: MidiSequencerPlugin(const NativeHostDescriptor* const host) - : NativePluginClass(host), + : NativePluginAndUiClass(host, CARLA_OS_SEP_STR "midiseq-ui"), fWantInEvents(false), - fMidiOut(this) + fWasPlayingBefore(false), + fInEvents(), + fMidiOut(this), + leakDetector_MidiSequencerPlugin() { // TEST SONG (unsorted to test RtList API) @@ -122,22 +127,10 @@ public: fMidiOut.addNote(3456*m, 0, 62, 90, 325*m); } - ~MidiSequencerPlugin() override - { - } - protected: // ------------------------------------------------------------------- // Plugin process calls - void activate() override - { - } - - void deactivate() override - { - } - void process(float**, float**, const uint32_t frames, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override { const NativeTimeInfo* const timePos = getTimeInfo(); @@ -155,26 +148,80 @@ protected: rawMidiEvent.data[2] = midiEvent->data[2]; rawMidiEvent.data[3] = midiEvent->data[3]; rawMidiEvent.size = midiEvent->size; - rawMidiEvent.time = timePos->frame + midiEvent->time; + rawMidiEvent.time = timePos->playing ? timePos->frame + midiEvent->time : 0; fInEvents.appendRT(rawMidiEvent); } - if (fInEvents.mutex.tryLock()) - { - fInEvents.splice(); - fInEvents.mutex.unlock(); - } + fInEvents.trySplice(); } if (timePos->playing) + { fMidiOut.play(timePos->frame, frames); + } + else if (fWasPlayingBefore) + { + NativeMidiEvent midiEvent; + + midiEvent.port = 0; + midiEvent.time = 0; + midiEvent.data[0] = MIDI_STATUS_CONTROL_CHANGE; + midiEvent.data[1] = MIDI_CONTROL_ALL_NOTES_OFF; + midiEvent.data[2] = 0; + midiEvent.data[3] = 0; + midiEvent.size = 3; + + for (int i=0; i < MAX_MIDI_CHANNELS; ++i) + { + midiEvent.data[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE+i); + NativePluginAndUiClass::writeMidiEvent(&midiEvent); + } + + carla_stdout("WAS PLAYING BEFORE, NOW STOPPED"); + } + + fWasPlayingBefore = timePos->playing; } // ------------------------------------------------------------------- - // Plugin process calls + // Plugin UI calls - void writeMidiEvent(const uint8_t port, const uint32_t timePosFrame, const RawMidiEvent* const event) override + void uiShow(const bool show) override + { + NativePluginAndUiClass::uiShow(show); + + if (show) + _sendEventsToUI(); + } + + // ------------------------------------------------------------------- + // Plugin state calls + + char* getState() const override + { + // TODO: malloc list of events + + return nullptr; + } + + void setState(const char* const data) override + { + CARLA_SAFE_ASSERT_RETURN(data != nullptr,); + + return; + + fMidiOut.clear(); + + // TODO: set events according to data + + _sendEventsToUI(); + } + + // ------------------------------------------------------------------- + // AbstractMidiPlayer calls + + void writeMidiEvent(const uint8_t port, const uint64_t timePosFrame, const RawMidiEvent* const event) override { NativeMidiEvent midiEvent; @@ -186,48 +233,151 @@ protected: midiEvent.data[3] = event->data[3]; midiEvent.size = event->size; - NativePluginClass::writeMidiEvent(&midiEvent); + NativePluginAndUiClass::writeMidiEvent(&midiEvent); + } + + // ------------------------------------------------------------------- + // Pipe Server calls + + bool msgReceived(const char* const msg) noexcept override + { + if (NativePluginAndUiClass::msgReceived(msg)) + return true; + + if (std::strcmp(msg, "midi-clear-all") == 0) + { + fMidiOut.clear(); + return true; + } + + if (std::strcmp(msg, "midievent-add") == 0) + { + uint64_t time; + uint8_t size; + + CARLA_SAFE_ASSERT_RETURN(readNextLineAsULong(time), true); + CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(size), true); + + uint8_t data[size], dvalue; + + for (uint8_t i=0; i::Pool dataPool; - RtList data; - RtList dataPendingRT; + RtLinkedList::Pool dataPool; + RtLinkedList data; + RtLinkedList dataPendingRT; - InRtEvents() - : dataPool(MIN_PREALLOCATED_EVENT_COUNT, MAX_PREALLOCATED_EVENT_COUNT), + InRtEvents() noexcept + : mutex(), + dataPool(MIN_PREALLOCATED_EVENT_COUNT, MAX_PREALLOCATED_EVENT_COUNT), data(dataPool), dataPendingRT(dataPool) {} - ~InRtEvents() + ~InRtEvents() noexcept { clear(); } - void appendRT(const RawMidiEvent& event) + void appendRT(const RawMidiEvent& event) noexcept { dataPendingRT.append(event); } - void clear() + void clear() noexcept { + mutex.lock(); data.clear(); dataPendingRT.clear(); + mutex.unlock(); } - void splice() + void trySplice() noexcept { - dataPendingRT.spliceAppend(data); + if (mutex.tryLock()) + { + if (dataPendingRT.count() > 0) + dataPendingRT.moveTo(data, true); + mutex.unlock(); + } } + CARLA_DECLARE_NON_COPY_STRUCT(InRtEvents); + } fInEvents; MidiPattern fMidiOut; + void _sendEventsToUI() const noexcept + { + char strBuf[0xff+1]; + strBuf[0xff] = '\0'; + + const CarlaMutexLocker cml1(getPipeLock()); + const CarlaMutexLocker cml2(fMidiOut.getLock()); + + writeMessage("midi-clear-all\n", 16); + + for (LinkedList::Itenerator it = fMidiOut.iteneratorBegin(); it.valid(); it.next()) + { + const RawMidiEvent* const rawMidiEvent(it.getValue(nullptr)); + CARLA_SAFE_ASSERT_CONTINUE(rawMidiEvent != nullptr); + + writeMessage("midievent-add\n", 14); + + std::snprintf(strBuf, 0xff, P_INT64 "\n", rawMidiEvent->time); + writeMessage(strBuf); + + std::snprintf(strBuf, 0xff, "%i\n", rawMidiEvent->size); + writeMessage(strBuf); + + for (uint8_t i=0, size=rawMidiEvent->size; idata[i]); + writeMessage(strBuf); + } + } + } + PluginClassEND(MidiSequencerPlugin) CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MidiSequencerPlugin) }; @@ -236,8 +386,11 @@ private: static const NativePluginDescriptor midisequencerDesc = { /* category */ NATIVE_PLUGIN_CATEGORY_UTILITY, - /* hints */ NATIVE_PLUGIN_IS_RTSAFE/*|NATIVE_PLUGIN_HAS_GUI*/, - /* supports */ static_cast(0x0), + /* hints */ static_cast(NATIVE_PLUGIN_IS_RTSAFE + |NATIVE_PLUGIN_HAS_UI + |NATIVE_PLUGIN_USES_STATE + |NATIVE_PLUGIN_USES_TIME), + /* supports */ NATIVE_PLUGIN_SUPPORTS_EVERYTHING, /* audioIns */ 0, /* audioOuts */ 0, /* midiIns */ 1, @@ -246,7 +399,7 @@ static const NativePluginDescriptor midisequencerDesc = { /* paramOuts */ 0, /* name */ "MIDI Sequencer", /* label */ "midisequencer", - /* maker */ "falkTX", + /* maker */ "falkTX, tatch", /* copyright */ "GNU GPL v2+", PluginDescriptorFILL(MidiSequencerPlugin) };