| 
				
				
					
				
				
				 | 
			
			 | 
			@@ -16,7 +16,7 @@ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			 */ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			/** | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			 * This file contains a substantial amount of code from VCVRack's core/....cpp and core/....cpp | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			 * This file contains a substantial amount of code from VCVRack's core/Gate_MIDI.cpp and core/MIDI_Gate.cpp | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			 * Copyright (C) 2016-2021 VCV. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			 * | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			 * This program is free software: you can redistribute it and/or | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -38,9 +38,11 @@ struct HostMIDIGate : Module { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        NUM_PARAMS | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    enum InputIds { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        ENUMS(GATE_INPUTS, 16), | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        NUM_INPUTS | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    enum OutputIds { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        ENUMS(GATE_OUTPUTS, 16), | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        NUM_OUTPUTS | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    enum LightIds { | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -49,21 +51,309 @@ struct HostMIDIGate : Module { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    CardinalPluginContext* const pcontext; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    struct MidiInput { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        // Cardinal specific | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        CardinalPluginContext* const pcontext; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        midi::Message converterMsg; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        const MidiEvent* midiEvents; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        uint32_t midiEventsLeft; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        uint32_t midiEventFrame; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        int64_t lastBlockFrame; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        uint8_t channel; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        // stuff from Rack | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        /** [cell][channel] */ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        bool gates[16][16]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        /** [cell][channel] */ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        float gateTimes[16][16]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        /** [cell][channel] */ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        uint8_t velocities[16][16]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        /** Cell ID in learn mode, or -1 if none. */ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        int learningId; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        bool mpeMode; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        MidiInput(CardinalPluginContext* const pc) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            : pcontext(pc) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            converterMsg.bytes.resize(0xff); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            reset(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        void reset() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            midiEvents = nullptr; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            midiEventsLeft = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            midiEventFrame = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            lastBlockFrame = -1; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            channel = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            learningId = -1; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            mpeMode = false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            panic(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        void panic() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            for (int i = 0; i < 16; ++i) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                for (int c = 0; c < 16; ++c) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    gates[i][c] = false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    gateTimes[i][c] = 0.f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        bool process(const ProcessArgs& args, std::vector<rack::engine::Output>& outputs, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                     const bool velocityMode, uint8_t learnedNotes[16]) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            // Cardinal specific | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            const int64_t blockFrame = pcontext->engine->getBlockFrame(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            const bool blockFrameChanged = lastBlockFrame != blockFrame; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if (blockFrameChanged) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                lastBlockFrame = blockFrame; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                midiEvents = pcontext->midiEvents; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                midiEventsLeft = pcontext->midiEventCount; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                midiEventFrame = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            while (midiEventsLeft != 0) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                const MidiEvent& midiEvent(*midiEvents); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if (midiEvent.frame > midiEventFrame) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                ++midiEvents; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                --midiEventsLeft; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                const uint8_t* data; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if (midiEvent.size > MidiEvent::kDataSize) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    data = midiEvent.dataExt; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    converterMsg.bytes.resize(midiEvent.size); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    data = midiEvent.data; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if (channel != 0 && data[0] < 0xF0) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    if ((data[0] & 0x0F) != (channel - 1)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        continue; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                // adapted from Rack | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                switch (data[0] & 0xF0) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                // note on | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                case 0x90: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    if (data[2] > 0) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        const int c = mpeMode ? (data[0] & 0x0F) : 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        // Learn | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        if (learningId >= 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                            learnedNotes[learningId] = data[1]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                            learningId = -1; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        // Find id | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        for (int i = 0; i < 16; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                            if (learnedNotes[i] == data[1]) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                gates[i][c] = true; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                gateTimes[i][c] = 1e-3f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                velocities[i][c] = data[2]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    // fall-through | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                // note off | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                case 0x80: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    const int c = mpeMode ? (data[0] & 0x0F) : 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    // Find id | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    for (int i = 0; i < 16; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        if (learnedNotes[i] == data[1]) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                            gates[i][c] = false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            ++midiEventFrame; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            // Rack stuff | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            const int channels = mpeMode ? 16 : 1; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            for (int i = 0; i < 16; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                outputs[GATE_OUTPUTS + i].setChannels(channels); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                for (int c = 0; c < channels; c++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    // Make sure all pulses last longer than 1ms | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    if (gates[i][c] || gateTimes[i][c] > 0.f) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        float velocity = velocityMode ? (velocities[i][c] / 127.f) : 1.f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        outputs[GATE_OUTPUTS + i].setVoltage(velocity * 10.f, c); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        gateTimes[i][c] -= args.sampleTime; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        outputs[GATE_OUTPUTS + i].setVoltage(0.f, c); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            return blockFrameChanged; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    } midiInput; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    struct MidiOutput { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        // cardinal specific | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        CardinalPluginContext* const pcontext; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        uint8_t channel = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        // base class vars | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        int vels[128]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        bool lastGates[128]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        int64_t frame = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        MidiOutput(CardinalPluginContext* const pc) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            : pcontext(pc) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            reset(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        void reset() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            // base class vars | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            for (int note = 0; note < 128; ++note) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                vels[note] = 100; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                lastGates[note] = false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            // cardinal specific | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            channel = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        void panic() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            // TODO send all notes off CC | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            // Send all note off commands | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            for (int note = 0; note < 128; note++) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                // Note off | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                midi::Message m; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                m.setStatus(0x8); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                m.setNote(note); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                m.setValue(0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                m.setFrame(frame); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                sendMessage(m); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                lastGates[note] = false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        void setVelocity(int vel, int note) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            vels[note] = vel; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        void setGate(bool gate, int note) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if (gate && !lastGates[note]) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                // Note on | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                midi::Message m; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                m.setStatus(0x9); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                m.setNote(note); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                m.setValue(vels[note]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                m.setFrame(frame); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                sendMessage(m); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            else if (!gate && lastGates[note]) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                // Note off | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                midi::Message m; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                m.setStatus(0x8); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                m.setNote(note); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                m.setValue(vels[note]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                m.setFrame(frame); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                sendMessage(m); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            lastGates[note] = gate; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        void sendMessage(const midi::Message& message) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            pcontext->writeMidiMessage(message, channel); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    } midiOutput; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    bool velocityMode = false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    uint8_t learnedNotes[16] = {}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    HostMIDIGate() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        : pcontext(static_cast<CardinalPluginContext*>(APP)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        : pcontext(static_cast<CardinalPluginContext*>(APP)), | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			          midiInput(pcontext), | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			          midiOutput(pcontext) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if (pcontext == nullptr) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            throw rack::Exception("Plugin context is null"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        for (int i = 0; i < 16; i++) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            configInput(GATE_INPUTS + i, string::f("Cell %d", i + 1)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        for (int i = 0; i < 16; i++) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            configOutput(GATE_OUTPUTS + i, string::f("Gate %d", i + 1)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        onReset(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    void onReset() override | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        for (int y = 0; y < 4; ++y) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            for (int x = 0; x < 4; ++x) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                learnedNotes[4 * y + x] = 36 + 4 * (3 - y) + x; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        velocityMode = false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        midiInput.reset(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        midiOutput.reset(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    void process(const ProcessArgs& args) override | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if (midiInput.process(args, outputs, velocityMode, learnedNotes)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            midiOutput.frame = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            ++midiOutput.frame; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        for (int i = 0; i < 16; i++) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            const int note = learnedNotes[i]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if (velocityMode) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                int vel = (int) std::round(inputs[GATE_INPUTS + i].getVoltage() / 10.f * 127); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                vel = clamp(vel, 0, 127); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                midiOutput.setVelocity(vel, note); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                midiOutput.setGate(vel > 0, note); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                const bool gate = inputs[GATE_INPUTS + i].getVoltage() >= 1.f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                midiOutput.setVelocity(100, note); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                midiOutput.setGate(gate, note); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    json_t* dataToJson() override | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -71,11 +361,52 @@ struct HostMIDIGate : Module { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        json_t* const rootJ = json_object(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr, nullptr); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        // input and output | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if (json_t* const notesJ = json_array()) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            for (int i = 0; i < 16; i++) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                json_array_append_new(notesJ, json_integer(learnedNotes[i])); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            json_object_set_new(rootJ, "notes", notesJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        json_object_set_new(rootJ, "velocity", json_boolean(velocityMode)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        // input only | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        json_object_set_new(rootJ, "mpeMode", json_boolean(midiInput.mpeMode)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        // separate | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        json_object_set_new(rootJ, "inputChannel", json_integer(midiInput.channel)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        json_object_set_new(rootJ, "outputChannel", json_integer(midiOutput.channel)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return rootJ; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    void dataFromJson(json_t* rootJ) override | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    void dataFromJson(json_t* const rootJ) override | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        // input and output | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if (json_t* const notesJ = json_object_get(rootJ, "notes")) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            for (int i = 0; i < 16; i++) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                if (json_t* const noteJ = json_array_get(notesJ, i)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    learnedNotes[i] = json_integer_value(noteJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    learnedNotes[i] = -1; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if (json_t* const velocityJ = json_object_get(rootJ, "velocity")) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            velocityMode = json_boolean_value(velocityJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        // input only | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if (json_t* const mpeModeJ = json_object_get(rootJ, "mpeMode")) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            midiInput.mpeMode = json_boolean_value(mpeModeJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        // separate | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if (json_t* const inputChannelJ = json_object_get(rootJ, "inputChannel")) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            midiInput.channel = json_integer_value(inputChannelJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if (json_t* const outputChannelJ = json_object_get(rootJ, "outputChannel")) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            midiOutput.channel = json_integer_value(outputChannelJ) & 0x0F; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
	
		
			
				| 
				
					
				
				
					
				
				
				 | 
			
			 | 
			@@ -115,6 +446,61 @@ struct HostMIDIGateWidget : ModuleWidget { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    void appendContextMenu(Menu* const menu) override | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        menu->addChild(new MenuSeparator); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        menu->addChild(createMenuLabel("MIDI Input")); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        menu->addChild(createBoolPtrMenuItem("MPE mode", "", &module->midiInput.mpeMode)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        struct InputChannelItem : MenuItem { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            HostMIDIGate* module; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            Menu* createChildMenu() override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                Menu* menu = new Menu; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                for (int c = 0; c <= 16; c++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    menu->addChild(createCheckMenuItem((c == 0) ? "All" : string::f("%d", c), "", | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        [=]() {return module->midiInput.channel == c;}, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        [=]() {module->midiInput.channel = c;} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    )); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                return menu; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        }; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        InputChannelItem* const inputChannelItem = new InputChannelItem; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        inputChannelItem->text = "MIDI channel"; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        inputChannelItem->rightText = (module->midiInput.channel ? string::f("%d", module->midiInput.channel) : "All") | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                    + "  " + RIGHT_ARROW; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        inputChannelItem->module = module; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        menu->addChild(inputChannelItem); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        menu->addChild(new MenuSeparator); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        menu->addChild(createMenuLabel("MIDI Output")); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        struct OutputChannelItem : MenuItem { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            HostMIDIGate* module; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            Menu* createChildMenu() override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                Menu* menu = new Menu; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                for (uint8_t c = 0; c < 16; c++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    menu->addChild(createCheckMenuItem(string::f("%d", c+1), "", | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        [=]() {return module->midiOutput.channel == c;}, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                        [=]() {module->midiOutput.channel = c;} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    )); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                return menu; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        }; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        OutputChannelItem* const outputChannelItem = new OutputChannelItem; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        outputChannelItem->text = "MIDI channel"; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        outputChannelItem->rightText = string::f("%d", module->midiOutput.channel+1) + "  " + RIGHT_ARROW; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        outputChannelItem->module = module; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        menu->addChild(outputChannelItem); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        menu->addChild(new MenuSeparator); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        menu->addChild(createMenuLabel("MIDI Input & Output")); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        menu->addChild(createBoolPtrMenuItem("Velocity mode", "", &module->velocityMode)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        menu->addChild(createMenuItem("Panic", "", | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            [=]() { module->midiInput.panic(); module->midiOutput.panic(); } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        )); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
	
		
			
				| 
				
					
				
				
				
				 | 
			
			 | 
			
  |