Browse Source

Ildaeil: Add MIDI input (converted from polyphonic CV)

tags/22.02
falkTX 3 years ago
parent
commit
0535c7eabc
1 changed files with 194 additions and 5 deletions
  1. +194
    -5
      plugins/Cardinal/src/Ildaeil.cpp

+ 194
- 5
plugins/Cardinal/src/Ildaeil.cpp View File

@@ -15,6 +15,16 @@
* For a full copy of the GNU General Public License see the LICENSE file.
*/

/**
* This file uses code adapted from VCVRack's CV_MIDI.cpp and midi.hpp
* Copyright (C) 2016-2021 VCV.
*
* 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 3 of
* the License, or (at your option) any later version.
*/

#include "plugincontext.hpp"

#ifndef HEADLESS
@@ -61,6 +71,124 @@ namespace ildaeil {

// --------------------------------------------------------------------------------------------------------------------

/** Converts gates and CV to MIDI messages. */
struct IldaeilMidiGenerator {
static const constexpr uint CHANNELS = 16;
static const constexpr uint MAX_MIDI_EVENTS = 128;
int8_t vels[CHANNELS];
int8_t notes[CHANNELS];
bool gates[CHANNELS];
int8_t keyPressures[CHANNELS];
int8_t mw;
int16_t pw;
uint32_t frame;
// generated output
uint midiEventCount;
NativeMidiEvent midiEvents[MAX_MIDI_EVENTS];

IldaeilMidiGenerator() {
reset();
}

void reset() {
for (uint c = 0; c < CHANNELS; c++) {
vels[c] = 100;
notes[c] = 60;
gates[c] = false;
keyPressures[c] = -1;
}
mw = -1;
pw = 0x2000;
frame = 0;
midiEventCount = 0;
}

void setFrame(uint32_t frame) {
this->frame = frame;
if (frame == 0)
midiEventCount = 0;
}

/** Must be called before setNoteGate(). */
void setVelocity(int8_t vel, int c) {
vels[c] = vel;
}

void setNoteGate(int8_t note, bool gate, int c) {
if (midiEventCount == MAX_MIDI_EVENTS)
return;
bool changedNote = gate && gates[c] && (note != notes[c]);
bool enabledGate = gate && !gates[c];
bool disabledGate = !gate && gates[c];
if (changedNote || disabledGate) {
// Note off
NativeMidiEvent& m(midiEvents[midiEventCount++]);
m.time = frame;
m.port = 0;
m.size = 3;
m.data[0] = 0x80;
m.data[1] = notes[c];
m.data[2] = vels[c];
}
if (changedNote || enabledGate) {
// Note on
NativeMidiEvent& m(midiEvents[midiEventCount++]);
m.time = frame;
m.port = 0;
m.size = 3;
m.data[0] = 0x90;
m.data[1] = note;
m.data[2] = vels[c];
}
notes[c] = note;
gates[c] = gate;
}

void setKeyPressure(int8_t val, int c) {
if (keyPressures[c] == val || midiEventCount == MAX_MIDI_EVENTS)
return;
keyPressures[c] = val;
// Polyphonic key pressure
NativeMidiEvent& m(midiEvents[midiEventCount++]);
m.time = frame;
m.port = 0;
m.size = 3;
m.data[0] = 0xa0;
m.data[1] = notes[c];
m.data[2] = val;
}

void setModWheel(int8_t mw) {
if (this->mw == mw || midiEventCount == MAX_MIDI_EVENTS)
return;
this->mw = mw;
// Modulation Wheel (CC1)
NativeMidiEvent& m(midiEvents[midiEventCount++]);
m.time = frame;
m.port = 0;
m.size = 3;
m.data[0] = 0xb0;
m.data[1] = 1;
m.data[2] = mw;
}

void setPitchWheel(int16_t pw) {
if (this->pw == pw || midiEventCount == MAX_MIDI_EVENTS)
return;
this->pw = pw;
// Pitch Wheel
NativeMidiEvent& m(midiEvents[midiEventCount++]);
m.time = frame;
m.port = 0;
m.size = 3;
m.data[0] = 0xe0;
m.data[1] = pw & 0x7f;
m.data[2] = (pw >> 7) & 0x7f;
}
};

// --------------------------------------------------------------------------------------------------------------------

using namespace CarlaBackend;

static uint32_t host_get_buffer_size(NativeHostHandle);
@@ -85,6 +213,12 @@ struct IldaeilModule : Module {
enum InputIds {
INPUT1,
INPUT2,
PITCH_INPUT,
GATE_INPUT,
VEL_INPUT,
AFT_INPUT,
PW_INPUT,
MW_INPUT,
NUM_INPUTS
};
enum OutputIds {
@@ -105,7 +239,6 @@ struct IldaeilModule : Module {
CarlaHostHandle fCarlaHostHandle = nullptr;

mutable NativeTimeInfo fCarlaTimeInfo;
// mutable water::MemoryOutputStream fLastProjectState;

void* fUI = nullptr;

@@ -115,10 +248,24 @@ struct IldaeilModule : Module {
float audioDataOut2[BUFFER_SIZE];
unsigned audioDataFill = 0;

IldaeilMidiGenerator midiGenerator;

IldaeilModule()
: pcontext(reinterpret_cast<CardinalPluginContext*>(APP))
{
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
for (uint i=0; i<2; ++i)
{
const char name[] = { 'A','u','d','i','o',' ','#',static_cast<char>('0'+i+1),'\0' };
configInput(i, name);
configOutput(i, name);
}
configInput(PITCH_INPUT, "Pitch (1V/oct)");
configInput(GATE_INPUT, "Gate");
configInput(VEL_INPUT, "Velocity");
configInput(AFT_INPUT, "Aftertouch");
configInput(PW_INPUT, "Pitch wheel");
configInput(MW_INPUT, "Mod wheel");

std::memset(audioDataOut1, 0, sizeof(audioDataOut1));
std::memset(audioDataOut2, 0, sizeof(audioDataOut2));
@@ -270,7 +417,7 @@ struct IldaeilModule : Module {
engine->loadProjectInternal(xml, true);
}

void process(const ProcessArgs&) override
void process(const ProcessArgs& args) override
{
if (fCarlaPluginHandle == nullptr)
return;
@@ -282,15 +429,46 @@ struct IldaeilModule : Module {
outputs[OUTPUT1].setVoltage(audioDataOut1[i] * 10.0f);
outputs[OUTPUT2].setVoltage(audioDataOut2[i] * 10.0f);

midiGenerator.setFrame(i);

for (int c = 0; c < inputs[PITCH_INPUT].getChannels(); c++) {
int vel = (int) std::round(inputs[VEL_INPUT].getNormalPolyVoltage(10.f * 100 / 127, c) / 10.f * 127);
vel = clamp(vel, 0, 127);
midiGenerator.setVelocity(vel, c);

int note = (int) std::round(inputs[PITCH_INPUT].getVoltage(c) * 12.f + 60.f);
note = clamp(note, 0, 127);
bool gate = inputs[GATE_INPUT].getPolyVoltage(c) >= 1.f;
midiGenerator.setNoteGate(note, gate, c);

int aft = (int) std::round(inputs[AFT_INPUT].getPolyVoltage(c) / 10.f * 127);
aft = clamp(aft, 0, 127);
midiGenerator.setKeyPressure(aft, c);
}

int pw = (int) std::round((inputs[PW_INPUT].getVoltage() + 5.f) / 10.f * 0x4000);
pw = clamp(pw, 0, 0x3fff);
midiGenerator.setPitchWheel(pw);

int mw = (int) std::round(inputs[MW_INPUT].getVoltage() / 10.f * 127);
mw = clamp(mw, 0, 127);
midiGenerator.setModWheel(mw);

if (audioDataFill == BUFFER_SIZE)
{
audioDataFill = 0;
float* ins[2] = { audioDataIn1, audioDataIn2 };
float* outs[2] = { audioDataOut1, audioDataOut2 };
fCarlaPluginDescriptor->process(fCarlaPluginHandle, ins, outs, BUFFER_SIZE, nullptr, 0);
fCarlaPluginDescriptor->process(fCarlaPluginHandle, ins, outs, BUFFER_SIZE,
midiGenerator.midiEvents, midiGenerator.midiEventCount);
}
}

void onReset() override
{
midiGenerator.reset();
}

void onSampleRateChange(const SampleRateChangeEvent& e) override
{
if (fCarlaPluginHandle == nullptr)
@@ -493,10 +671,14 @@ struct IldaeilWidget : ImGuiWidget, IdleCallback, Thread {
if (module->fCarlaHostHandle != nullptr)
{
module->fUI = nullptr;

if (fPluginRunning)
carla_show_custom_ui(module->fCarlaHostHandle, 0, false);

carla_set_engine_option(module->fCarlaHostHandle, ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0");
carla_show_custom_ui(module->fCarlaHostHandle, 0, false);

module->pcontext->removeIdleCallback(this);
if (idleCallbackActive)
module->pcontext->removeIdleCallback(this);
}

if (isThreadRunning())
@@ -1235,6 +1417,13 @@ struct IldaeilModuleWidget : ModuleWidget {
addInput(createInput<PJ301MPort>(Vec(3, 54 + 30), module, IldaeilModule::INPUT2));
addOutput(createOutput<PJ301MPort>(Vec(3, 54 + 60), module, IldaeilModule::OUTPUT1));
addOutput(createOutput<PJ301MPort>(Vec(3, 54 + 90), module, IldaeilModule::OUTPUT2));

addOutput(createInput<PJ301MPort>(Vec(3, 54 + 135), module, IldaeilModule::PITCH_INPUT));
addOutput(createInput<PJ301MPort>(Vec(3, 54 + 165), module, IldaeilModule::GATE_INPUT));
addOutput(createInput<PJ301MPort>(Vec(3, 54 + 195), module, IldaeilModule::VEL_INPUT));
addOutput(createInput<PJ301MPort>(Vec(3, 54 + 225), module, IldaeilModule::AFT_INPUT));
addOutput(createInput<PJ301MPort>(Vec(3, 54 + 255), module, IldaeilModule::PW_INPUT));
addOutput(createInput<PJ301MPort>(Vec(3, 54 + 285), module, IldaeilModule::MW_INPUT));
}
};
#else


Loading…
Cancel
Save