diff --git a/source/backend/CarlaEngine.hpp b/source/backend/CarlaEngine.hpp index 73216856b..2006f82a4 100644 --- a/source/backend/CarlaEngine.hpp +++ b/source/backend/CarlaEngine.hpp @@ -975,6 +975,7 @@ protected: EngineOptions fOptions; EngineTimeInfo fTimeInfo; + friend struct CarlaEngineProtectedData; CarlaEngineProtectedData* const kData; /*! diff --git a/source/backend/CarlaNative.h b/source/backend/CarlaNative.h index 54ca0cd19..d42bdf873 100644 --- a/source/backend/CarlaNative.h +++ b/source/backend/CarlaNative.h @@ -195,6 +195,7 @@ void carla_register_native_plugin(const PluginDescriptor* desc); // Simple plugins void carla_register_native_plugin_bypass(); +void carla_register_native_plugin_midiSequencer(); void carla_register_native_plugin_midiSplit(); void carla_register_native_plugin_midiThrough(); void carla_register_native_plugin_midiTranspose(); diff --git a/source/backend/engine/CarlaEngine.cpp b/source/backend/engine/CarlaEngine.cpp index b942536d1..e5339b3de 100644 --- a/source/backend/engine/CarlaEngine.cpp +++ b/source/backend/engine/CarlaEngine.cpp @@ -569,6 +569,7 @@ bool CarlaEngine::init(const char* const clientName) break; } + //kData->pluginsPool.resize(maxPluginNumber, 999); kData->plugins = new EnginePluginData[kData->maxPluginNumber]; kData->osc.init(clientName); @@ -609,6 +610,7 @@ bool CarlaEngine::close() kData->curPluginCount = 0; kData->maxPluginNumber = 0; + //kData->plugins.clear(); if (kData->plugins != nullptr) { delete[] kData->plugins; @@ -637,6 +639,17 @@ void CarlaEngine::idle() CARLA_ASSERT(kData->plugins != nullptr); CARLA_ASSERT(isRunning()); +#if 0 + for (auto it = kData->plugins.begin(); it.valid(); it.next()) + { + CarlaPlugin* const plugin = (*it).plugin; + CARLA_ASSERT(plugin != nullptr); + + if (plugin && plugin->enabled()) + plugin->idleGui(); + } +#endif + for (unsigned int i=0; i < kData->curPluginCount; i++) { CarlaPlugin* const plugin = kData->plugins[i].plugin; @@ -1101,7 +1114,7 @@ float CarlaEngine::getInputPeak(const unsigned int pluginId, const unsigned shor CARLA_ASSERT(pluginId < kData->curPluginCount); CARLA_ASSERT(id-1 < MAX_PEAKS); - if (id > MAX_PEAKS) + if (id == 0 || id > MAX_PEAKS) return 0.0f; return kData->plugins[pluginId].insPeak[id-1]; @@ -1112,7 +1125,7 @@ float CarlaEngine::getOutputPeak(const unsigned int pluginId, const unsigned sho CARLA_ASSERT(pluginId < kData->curPluginCount); CARLA_ASSERT(id-1 < MAX_PEAKS); - if (id > MAX_PEAKS) + if (id == 0 || id > MAX_PEAKS) return 0.0f; return kData->plugins[pluginId].outsPeak[id-1]; diff --git a/source/backend/engine/CarlaEngineInternal.hpp b/source/backend/engine/CarlaEngineInternal.hpp index 1beb04ffd..d453ec6a0 100644 --- a/source/backend/engine/CarlaEngineInternal.hpp +++ b/source/backend/engine/CarlaEngineInternal.hpp @@ -22,6 +22,7 @@ #include "CarlaEngineThread.hpp" #include "CarlaEngine.hpp" #include "CarlaPlugin.hpp" +#include "RtList.hpp" #ifndef BUILD_BRIDGE # include @@ -92,10 +93,6 @@ const char* EngineControlEventType2Str(const EngineControlEventType type) // ------------------------------------------------------------------------------------------------------------------- -/*! - * Maximum number of peaks per plugin.\n - * \note There are both input and output peaks. - */ const uint32_t PATCHBAY_BUFFER_SIZE = 128; const unsigned short PATCHBAY_EVENT_COUNT = 512; const unsigned short RACK_EVENT_COUNT = 512; @@ -116,6 +113,7 @@ struct EnginePostEvent { enum EnginePostAction { EnginePostActionNull, EnginePostActionIdle, + EnginePostActionInsertPlugin, EnginePostActionRemovePlugin }; @@ -185,6 +183,9 @@ struct CarlaEngineProtectedData { frame(0) {} } time; + //RtList::Pool pluginsPool; + //RtList plugins; + EnginePluginData* plugins; CarlaEngineProtectedData(CarlaEngine* const engine) @@ -195,8 +196,24 @@ struct CarlaEngineProtectedData { callbackPtr(nullptr), aboutToClose(false), curPluginCount(0), + //pluginsPool(1, 999), + //plugins(&pluginsPool) maxPluginNumber(0), plugins(nullptr) {} + + ~CarlaEngineProtectedData() + { + //plugins.clear(); + } + + CarlaEngineProtectedData() = delete; + CarlaEngineProtectedData(CarlaEngineProtectedData&) = delete; + CarlaEngineProtectedData(const CarlaEngineProtectedData&) = delete; + + //static RtList::Itenerator pluginsBegin(CarlaEngine* const engine) + //{ + // return engine->kData->plugins.begin(); + //} }; CARLA_BACKEND_END_NAMESPACE diff --git a/source/backend/native/Makefile b/source/backend/native/Makefile index 1e241d860..d9772d734 100644 --- a/source/backend/native/Makefile +++ b/source/backend/native/Makefile @@ -29,6 +29,7 @@ endif # Simple plugins OBJS = \ bypass.c.o \ + midi-sequencer.cpp.o \ midi-split.c.o \ midi-through.c.o \ midi-transpose.c.o \ diff --git a/source/backend/native/midi-sequencer.cpp b/source/backend/native/midi-sequencer.cpp new file mode 100644 index 000000000..edb80b8ee --- /dev/null +++ b/source/backend/native/midi-sequencer.cpp @@ -0,0 +1,320 @@ +/* + * Carla Native Plugins + * Copyright (C) 2013 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 GPL.txt file + */ + +#include "CarlaNative.hpp" +#include "CarlaMIDI.h" +#include "CarlaMutex.hpp" +#include "RtList.hpp" + +#define MAX_EVENT_DATA_SIZE 3 +#define MIN_PREALLOCATED_EVENT_COUNT 100 +#define MAX_PREALLOCATED_EVENT_COUNT 1000 + +struct RawMidiEvent { + unsigned char data[MAX_EVENT_DATA_SIZE]; + //size_t dataSize; + uint32_t time; + //double value; // used for special events +}; + +class MidiSequencerPlugin : public PluginDescriptorClass +{ +public: + MidiSequencerPlugin(const HostDescriptor* const host) + : PluginDescriptorClass(host), + fWantInEvents(false) + { + // TEST SONG + + uint32_t m = 44; + + fOutEvents.addControl(0*m, 0, 7, 99); + fOutEvents.addControl(0*m, 0, 10, 63); + fOutEvents.addProgram(0*m, 0, 0, 0); + + // 0 On ch=1 n=64 v=90 + // 325 Off ch=1 n=64 v=90 + // 384 On ch=1 n=62 v=90 + // 709 Off ch=1 n=62 v=90 + // 768 On ch=1 n=60 v=90 + //1093 Off ch=1 n=60 v=90 + fOutEvents.addNote( 0*m, 0, 64, 90, 325*m); + fOutEvents.addNote(384*m, 0, 62, 90, 325*m); + fOutEvents.addNote(768*m, 0, 60, 90, 325*m); + + // 1152 On ch=1 n=62 v=90 + // 1477 Off ch=1 n=62 v=90 + // 1536 On ch=1 n=64 v=90 + // 1861 Off ch=1 n=64 v=90 + // 1920 On ch=1 n=64 v=90 + // 2245 Off ch=1 n=64 v=90 + fOutEvents.addNote(1152*m, 0, 62, 90, 325*m); + fOutEvents.addNote(1536*m, 0, 64, 90, 325*m); + fOutEvents.addNote(1920*m, 0, 64, 90, 325*m); + + // 2304 On ch=1 n=64 v=90 + // 2955 Off ch=1 n=64 v=90 + // 3072 On ch=1 n=62 v=90 + // 3397 Off ch=1 n=62 v=90 + // 3456 On ch=1 n=62 v=90 + // 3781 Off ch=1 n=62 v=90 + fOutEvents.addNote(2304*m, 0, 64, 90, 650*m); + fOutEvents.addNote(3072*m, 0, 62, 90, 325*m); + fOutEvents.addNote(3456*m, 0, 62, 90, 325*m); + + // 3840 On ch=1 n=62 v=90 + // 4491 Off ch=1 n=62 v=90 + // 4608 On ch=1 n=64 v=90 + // 4933 Off ch=1 n=64 v=90 + // 4992 On ch=1 n=67 v=90 + // 5317 Off ch=1 n=67 v=90 + fOutEvents.addNote(3840*m, 0, 62, 90, 650*m); + fOutEvents.addNote(4608*m, 0, 64, 90, 325*m); + fOutEvents.addNote(4992*m, 0, 67, 90, 325*m); + + // 5376 On ch=1 n=67 v=90 + // 6027 Off ch=1 n=67 v=90 + // 6144 On ch=1 n=64 v=90 + // 6469 Off ch=1 n=64 v=90 + // 6528 On ch=1 n=62 v=90 + // 6853 Off ch=1 n=62 v=90 + fOutEvents.addNote(5376*m, 0, 67, 90, 650*m); + fOutEvents.addNote(6144*m, 0, 64, 90, 325*m); + fOutEvents.addNote(6528*m, 0, 62, 90, 325*m); + + // 6912 On ch=1 n=60 v=90 + // 7237 Off ch=1 n=60 v=90 + // 7296 On ch=1 n=62 v=90 + // 7621 Off ch=1 n=62 v=90 + // 7680 On ch=1 n=64 v=90 + // 8005 Off ch=1 n=64 v=90 + fOutEvents.addNote(6912*m, 0, 60, 90, 325*m); + fOutEvents.addNote(7296*m, 0, 62, 90, 325*m); + fOutEvents.addNote(7680*m, 0, 64, 90, 325*m); + + // 8064 On ch=1 n=64 v=90 + // 8389 Off ch=1 n=64 v=90 + // 8448 On ch=1 n=64 v=90 + // 9099 Off ch=1 n=64 v=90 + // 9216 On ch=1 n=62 v=90 + // 9541 Off ch=1 n=62 v=90 + fOutEvents.addNote(8064*m, 0, 64, 90, 325*m); + fOutEvents.addNote(8448*m, 0, 64, 90, 650*m); + fOutEvents.addNote(9216*m, 0, 62, 90, 325*m); + + // 9600 On ch=1 n=62 v=90 + // 9925 Off ch=1 n=62 v=90 + // 9984 On ch=1 n=64 v=90 + // 10309 Off ch=1 n=64 v=90 + // 10368 On ch=1 n=62 v=90 + // 10693 Off ch=1 n=62 v=90 + fOutEvents.addNote(9600*m, 0, 62, 90, 325*m); + fOutEvents.addNote(9984*m, 0, 64, 90, 325*m); + fOutEvents.addNote(10368*m, 0, 62, 90, 325*m); + + // 10752 On ch=1 n=60 v=90 + // 12056 Off ch=1 n=60 v=90 + fOutEvents.addNote(10752*m, 0, 60, 90, 650*m); + } + + ~MidiSequencerPlugin() + { + fOutEvents.data.clear(); + } + +protected: + // ------------------------------------------------------------------- + // Plugin process calls + + void activate() + { + } + + void deactivate() + { + } + + void process(float**, float**, const uint32_t frames, const uint32_t midiEventCount, const MidiEvent* const midiEvents) + { + const TimeInfo* const timePos = getTimeInfo(); + + if (fWantInEvents) + { + RawMidiEvent rawMidiEvent; + + for (uint32_t i=0; i < midiEventCount; i++) + { + const MidiEvent* const midiEvent = &midiEvents[i]; + + rawMidiEvent.data[0] = midiEvent->data[0]; + rawMidiEvent.data[1] = midiEvent->data[1]; + rawMidiEvent.data[2] = midiEvent->data[2]; + rawMidiEvent.time = timePos->frame + midiEvent->time; + + fInEvents.appendRT(rawMidiEvent); + } + } + + if (! fMutex.tryLock()) + return; + + if (fWantInEvents) + fInEvents.splice(); + + if (timePos->playing) + { + MidiEvent midiEvent; + + for (auto it = fOutEvents.data.begin(); it.valid(); it.next()) + { + RawMidiEvent* const rawMidiEvent(*it); + + if (timePos->frame > rawMidiEvent->time || timePos->frame + frames <= rawMidiEvent->time) + continue; + + midiEvent.port = 0; + midiEvent.time = rawMidiEvent->time-timePos->frame; + midiEvent.data[0] = rawMidiEvent->data[0]; + midiEvent.data[1] = rawMidiEvent->data[1]; + midiEvent.data[2] = rawMidiEvent->data[2]; + + writeMidiEvent(&midiEvent); + } + } + + fMutex.unlock(); + } + +private: + CarlaMutex fMutex; + bool fWantInEvents; + + struct InRtEvents { + RtList::Pool dataPool; + RtList data; + RtList dataPendingRT; + + InRtEvents() + : dataPool(MIN_PREALLOCATED_EVENT_COUNT, MAX_PREALLOCATED_EVENT_COUNT), + data(&dataPool), + dataPendingRT(&dataPool) {} + + ~InRtEvents() + { + clear(); + } + + void appendRT(const RawMidiEvent& event) + { + dataPendingRT.append(event); + } + + void clear() + { + data.clear(); + dataPendingRT.clear(); + } + + void splice() + { + dataPendingRT.spliceAppend(data, true); + } + + } fInEvents; + + struct OutRtEvents { + NonRtList data; + + void addControl(uint32_t time, uint8_t channel, uint8_t control, uint8_t value) + { + RawMidiEvent* ctrlEvent(new RawMidiEvent); + ctrlEvent->data[0] = MIDI_STATUS_CONTROL_CHANGE | (channel & 0x0F); + ctrlEvent->data[1] = control; + ctrlEvent->data[2] = value; + ctrlEvent->time = time; + + data.append(ctrlEvent); + } + + void addProgram(uint32_t time, uint8_t channel, uint8_t bank, uint8_t program) + { + RawMidiEvent* bankEvent(new RawMidiEvent); + bankEvent->data[0] = MIDI_STATUS_CONTROL_CHANGE | (channel & 0x0F); + bankEvent->data[1] = MIDI_CONTROL_BANK_SELECT; + bankEvent->data[2] = bank; + bankEvent->time = time; + + RawMidiEvent* programEvent(new RawMidiEvent); + programEvent->data[0] = MIDI_STATUS_PROGRAM_CHANGE | (channel & 0x0F); + programEvent->data[1] = program; + programEvent->data[2] = 0; + programEvent->time = time; + + data.append(bankEvent); + data.append(programEvent); + } + + void addNote(uint32_t time, uint8_t channel, uint8_t pitch, uint8_t velocity, uint32_t duration) + { + RawMidiEvent* noteOnEvent(new RawMidiEvent); + noteOnEvent->data[0] = MIDI_STATUS_NOTE_ON | (channel & 0x0F); + noteOnEvent->data[1] = pitch; + noteOnEvent->data[2] = velocity; + noteOnEvent->time = time; + + RawMidiEvent* noteOffEvent(new RawMidiEvent); + noteOffEvent->data[0] = MIDI_STATUS_NOTE_OFF | (channel & 0x0F); + noteOffEvent->data[1] = pitch; + noteOffEvent->data[2] = velocity; + noteOffEvent->time = time+duration; + + data.append(noteOnEvent); + data.append(noteOffEvent); + } + + } fOutEvents; + + PluginDescriptorClassEND(MidiSequencerPlugin) + CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MidiSequencerPlugin) +}; + +// ----------------------------------------------------------------------- + +static const PluginDescriptor midiSequencerDesc = { + /* category */ PLUGIN_CATEGORY_UTILITY, + /* hints */ PLUGIN_IS_RTSAFE/*|PLUGIN_HAS_GUI*/, + /* audioIns */ 0, + /* audioOuts */ 0, + /* midiIns */ 1, + /* midiOuts */ 1, + /* paramIns */ 0, + /* paramOuts */ 0, + /* name */ "MIDI Sequencer", + /* label */ "midiSequencer", + /* maker */ "falkTX", + /* copyright */ "GNU GPL v2+", + PluginDescriptorFILL(MidiSequencerPlugin) +}; + +// ----------------------------------------------------------------------- + +void carla_register_native_plugin_midiSequencer() +{ + carla_register_native_plugin(&midiSequencerDesc); +} + +// ----------------------------------------------------------------------- diff --git a/source/backend/plugin/NativePlugin.cpp b/source/backend/plugin/NativePlugin.cpp index 00c0094d4..4657806f7 100644 --- a/source/backend/plugin/NativePlugin.cpp +++ b/source/backend/plugin/NativePlugin.cpp @@ -1924,6 +1924,7 @@ private: { #ifndef BUILD_BRIDGE carla_register_native_plugin_bypass(); + carla_register_native_plugin_midiSequencer(); carla_register_native_plugin_midiSplit(); carla_register_native_plugin_midiThrough(); carla_register_native_plugin_midiTranspose(); diff --git a/source/bridges/CarlaBridgePlugin.cpp b/source/bridges/CarlaBridgePlugin.cpp index 18dd2fc38..700bb6183 100644 --- a/source/bridges/CarlaBridgePlugin.cpp +++ b/source/bridges/CarlaBridgePlugin.cpp @@ -332,6 +332,11 @@ protected: if (value1 != 1 && ! isOscControlRegistered()) gCloseNow = true; break; + default: // TODO + (void)value2; + (void)value3; + (void)valueStr; + break; } } diff --git a/source/bridges/qtcreator/carla-bridge-plugin.pro b/source/bridges/qtcreator/carla-bridge-plugin.pro index 8ae629a80..736837bcb 100644 --- a/source/bridges/qtcreator/carla-bridge-plugin.pro +++ b/source/bridges/qtcreator/carla-bridge-plugin.pro @@ -120,4 +120,4 @@ LIBS = -ldl \ ../../libs/lilv.a \ ../../libs/rtmempool.a -QMAKE_CXXFLAGS *= -std=c++0x +QMAKE_CXXFLAGS *= -std=gnu++0x diff --git a/source/utils/CarlaJuceUtils.hpp b/source/utils/CarlaJuceUtils.hpp index ac069c725..b3486a055 100644 --- a/source/utils/CarlaJuceUtils.hpp +++ b/source/utils/CarlaJuceUtils.hpp @@ -31,14 +31,15 @@ CARLA_LEAK_DETECTOR macro for a class. */ #define CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className) \ - CARLA_DECLARE_NON_COPYABLE(className) \ +private: \ + CARLA_DECLARE_NON_COPYABLE(className) \ CARLA_LEAK_DETECTOR(className) /** This macro can be added to class definitions to disable the use of new/delete to allocate the object on the heap, forcing it to only be used as a stack or member variable. */ -#define CARLA_PREVENT_HEAP_ALLOCATION \ -private: \ +#define CARLA_PREVENT_HEAP_ALLOCATION \ +private: \ static void* operator new (size_t); \ static void operator delete (void*);