/* * XY Controller UI, taken from Cadence * Copyright (C) 2011-2022 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. */ #include "CarlaDefines.h" #include "CarlaMIDI.h" #include "CarlaNativeExtUI.hpp" #include "midi-queue.hpp" #include "water/text/StringArray.h" // ----------------------------------------------------------------------- class XYControllerPlugin : public NativePluginAndUiClass { public: enum Parameters { kParamInX, kParamInY, kParamOutX, kParamOutY, kParamCount, }; XYControllerPlugin(const NativeHostDescriptor* const host) : NativePluginAndUiClass(host, "xycontroller-ui"), params(), channels(), mqueue(), mqueueRT() { carla_zeroStruct(params); carla_zeroStruct(channels); channels[0] = true; } protected: // ------------------------------------------------------------------- // Plugin parameter calls uint32_t getParameterCount() const override { return kParamCount; } const NativeParameter* getParameterInfo(const uint32_t index) const override { CARLA_SAFE_ASSERT_RETURN(index < kParamCount, nullptr); static NativeParameter param; int hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMATABLE; param.name = nullptr; param.unit = "%"; param.ranges.def = 0.0f; param.ranges.min = -100.0f; param.ranges.max = 100.0f; param.ranges.step = 1.0f; param.ranges.stepSmall = 0.01f; param.ranges.stepLarge = 10.0f; param.scalePointCount = 0; param.scalePoints = nullptr; switch (index) { case kParamInX: param.name = "X"; break; case kParamInY: param.name = "Y"; break; case kParamOutX: hints |= NATIVE_PARAMETER_IS_OUTPUT; param.name = "Out X"; break; case kParamOutY: hints |= NATIVE_PARAMETER_IS_OUTPUT; param.name = "Out Y"; break; } param.hints = static_cast(hints); return ¶m; } float getParameterValue(const uint32_t index) const override { CARLA_SAFE_ASSERT_RETURN(index < kParamCount, 0.0f); return params[index]; } // ------------------------------------------------------------------- // Plugin state calls void setParameterValue(const uint32_t index, const float value) override { switch (index) { case kParamInX: case kParamInY: params[index] = value; break; } } void setCustomData(const char* const key, const char* const value) override { CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',); CARLA_SAFE_ASSERT_RETURN(value != nullptr,); if (std::strcmp(key, "channels") == 0) { const water::StringArray chans(water::StringArray::fromTokens(value, ",", "")); carla_zeroStruct(channels); for (const water::String *it=chans.begin(), *end=chans.end(); it != end; ++it) { const int ichan = std::atoi((*it).toRawUTF8()); CARLA_SAFE_ASSERT_INT_CONTINUE(ichan >= 1 && ichan <= 16, ichan); channels[ichan-1] = true; } } } // ------------------------------------------------------------------- // Plugin process calls void process(const float* const*, float**, const uint32_t, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override { params[kParamOutX] = params[kParamInX]; params[kParamOutY] = params[kParamInY]; if (mqueue.isNotEmpty() && mqueueRT.tryToCopyDataFrom(mqueue)) { uint8_t d1, d2, d3; NativeMidiEvent ev = { 0, 0, 3, { 0, 0, 0, 0 } }; while (mqueueRT.get(d1, d2, d3)) { ev.data[0] = d1; ev.data[1] = d2; ev.data[2] = d3; writeMidiEvent(&ev); } } for (uint32_t i=0; i < midiEventCount; ++i) writeMidiEvent(&midiEvents[i]); } // ------------------------------------------------------------------- // Pipe Server calls bool msgReceived(const char* const msg) noexcept override { if (NativePluginAndUiClass::msgReceived(msg)) return true; if (std::strcmp(msg, "cc") == 0) { uint8_t cc, value; CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(cc), true); CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(value), true); const CarlaMutexLocker cml(mqueue.getMutex()); for (int i=0; i<16; ++i) { if (channels[i]) if (! mqueue.put(uint8_t(MIDI_STATUS_CONTROL_CHANGE | (i & MIDI_CHANNEL_BIT)), cc, value)) break; } return true; } if (std::strcmp(msg, "cc2") == 0) { uint8_t cc1, value1, cc2, value2; CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(cc1), true); CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(value1), true); CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(cc2), true); CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(value2), true); const CarlaMutexLocker cml(mqueue.getMutex()); for (int i=0; i<16; ++i) { if (channels[i]) { if (! mqueue.put(uint8_t(MIDI_STATUS_CONTROL_CHANGE | (i & MIDI_CHANNEL_BIT)), cc1, value1)) break; if (! mqueue.put(uint8_t(MIDI_STATUS_CONTROL_CHANGE | (i & MIDI_CHANNEL_BIT)), cc2, value2)) break; } } return true; } if (std::strcmp(msg, "note") == 0) { bool onOff; uint8_t note; CARLA_SAFE_ASSERT_RETURN(readNextLineAsBool(onOff), true); CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(note), true); const uint8_t status = onOff ? MIDI_STATUS_NOTE_ON : MIDI_STATUS_NOTE_OFF; const uint8_t velocity = onOff ? 100 : 0; const CarlaMutexLocker cml(mqueue.getMutex()); for (int i=0; i<16; ++i) { if (channels[i]) if (! mqueue.put(uint8_t(status | (i & MIDI_CHANNEL_BIT)), note, velocity)) break; } return true; } return false; } private: float params[kParamCount]; bool channels[16]; MIDIEventQueue<128> mqueue, mqueueRT; PluginClassEND(XYControllerPlugin) CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(XYControllerPlugin) }; // ----------------------------------------------------------------------- static const NativePluginDescriptor notesDesc = { /* category */ NATIVE_PLUGIN_CATEGORY_UTILITY, /* hints */ static_cast(NATIVE_PLUGIN_IS_RTSAFE |NATIVE_PLUGIN_HAS_UI), /* supports */ NATIVE_PLUGIN_SUPPORTS_NOTHING, /* audioIns */ 0, /* audioOuts */ 0, /* midiIns */ 1, /* midiOuts */ 1, /* paramIns */ 2, /* paramOuts */ 2, /* name */ "XY Controller", /* label */ "xycontroller", /* maker */ "falkTX", /* copyright */ "GNU GPL v2+", PluginDescriptorFILL(XYControllerPlugin) }; // ----------------------------------------------------------------------- CARLA_API_EXPORT void carla_register_native_plugin_xycontroller(); CARLA_API_EXPORT void carla_register_native_plugin_xycontroller() { carla_register_native_plugin(¬esDesc); } // -----------------------------------------------------------------------