Browse Source

Merge branch 'bontric-2017-10-bontric-quadmidi'

tags/v0.5.0
Andrew Belt 7 years ago
parent
commit
8d5dfc40b9
3 changed files with 408 additions and 1 deletions
  1. +5
    -0
      src/core/MidiIO.hpp
  2. +401
    -0
      src/core/QuadMidiToCV.cpp
  3. +2
    -1
      src/core/core.cpp

+ 5
- 0
src/core/MidiIO.hpp View File

@@ -152,3 +152,8 @@ struct MIDITriggerToCVWidget : ModuleWidget {
void step();
};

struct QuadMidiToCVWidget : ModuleWidget {
QuadMidiToCVWidget();

void step();
};

+ 401
- 0
src/core/QuadMidiToCV.cpp View File

@@ -0,0 +1,401 @@
#include <list>
#include <algorithm>
#include "rtmidi/RtMidi.h"
#include "core.hpp"
#include "MidiIO.hpp"
#include "dsp/digital.hpp"

struct MidiKey {
int pitch = 60;
int at = 0; // aftertouch
int vel = 0; // velocity
int retriggerC = 0;
bool gate = false;
};

struct QuadMIDIToCVInterface : MidiIO, Module {
enum ParamIds {
RESET_PARAM,
NUM_PARAMS
};
enum InputIds {
NUM_INPUTS
};
enum OutputIds {
PITCH_OUTPUT = 0,
GATE_OUTPUT = 4,
VELOCITY_OUTPUT = 8,
AT_OUTPUT = 12,
NUM_OUTPUTS = 16
};

enum Modes {
ROTATE,
RESET,
REASSIGN
};

bool pedal = false;

int mode = REASSIGN;

int getMode() const;

void setMode(int mode);

MidiKey activeKeys[4];
std::list<int> open;

SchmittTrigger resetTrigger;
float resetLight = 0.0;

QuadMIDIToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {

}

~QuadMIDIToCVInterface() {
};

void step();

void processMidi(std::vector<unsigned char> msg);

json_t *toJson() {
json_t *rootJ = json_object();
addBaseJson(rootJ);
return rootJ;
}

void fromJson(json_t *rootJ) {
baseFromJson(rootJ);
}

void reset() {
resetMidi();
}

void resetMidi();

};

void QuadMIDIToCVInterface::resetMidi() {

for (int i = 0; i < 4; i++) {
outputs[GATE_OUTPUT + i].value = 0.0;
activeKeys[i].gate = false;
activeKeys[i].vel = 0;
activeKeys[i].at = 0;
}

open.clear();

pedal = false;
resetLight = 1.0;
}

void QuadMIDIToCVInterface::step() {
static float sampleRate = engineGetSampleRate();
static int msgsProcessed = 0;

if (isPortOpen()) {
std::vector<unsigned char> message;

// midiIn->getMessage returns empty vector if there are no messages in the queue
// NOTE: For the quadmidi we will process max 4 midi messages per step to avoid
// problems with parallel input.
getMessage(&message);
while (msgsProcessed < 4 && message.size() > 0) {
processMidi(message);
getMessage(&message);
msgsProcessed++;
}
msgsProcessed = 0;
}


for (int i = 0; i < 4; i++) {
outputs[GATE_OUTPUT + i].value = activeKeys[i].gate ? 10.0 : 0;
outputs[PITCH_OUTPUT + i].value = (activeKeys[i].pitch - 60) / 12.0;
outputs[VELOCITY_OUTPUT + i].value = activeKeys[i].vel / 127.0 * 10.0;
outputs[AT_OUTPUT + i].value = activeKeys[i].at / 127.0 * 10.0;
}

if (resetTrigger.process(params[RESET_PARAM].value)) {
resetMidi();
return;
}

if (resetLight > 0) {
resetLight -= resetLight / 0.55 / sampleRate; // fade out light
}

}


void QuadMIDIToCVInterface::processMidi(std::vector<unsigned char> msg) {
int channel = msg[0] & 0xf;
int status = (msg[0] >> 4) & 0xf;
int data1 = msg[1];
int data2 = msg[2];

static int gate;

// Filter channels
if (this->channel >= 0 && this->channel != channel)
return;

switch (status) {
// note off
case 0x8: {
gate = false;
}
break;
case 0x9: // note on
if (data2 > 0) {
gate = true;
} else {
// For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released.
gate = false;
}
break;
case 0xa: // channel aftertouch
for (int i = 0; i < 4; i++) {
if (activeKeys[i].pitch == data1) {
activeKeys[i].at = data2;
}
}
return;
case 0xb: // cc
if (data1 == 0x40) { // pedal
pedal = (data2 >= 64);
if (!pedal) {
for (int i = 0; i < 4; i++) {
activeKeys[i].gate = false;
open.push_back(i);
}
}
}
return;
default:
return;
}

if (!pedal && !gate) {
for (int i = 0; i < 4; i++) {
if (activeKeys[i].pitch == data1) {
activeKeys[i].gate = false;
activeKeys[i].vel = data2;
if (std::find(open.begin(), open.end(), i) != open.end()) {
open.remove(i);
}
open.push_front(i);
}
}
return;
}

if (open.empty()) {
open.clear();
for (int i = 0; i < 4; i++) {
open.push_back(i);
}
}


if (!activeKeys[0].gate && !activeKeys[1].gate &&
!activeKeys[2].gate && !activeKeys[3].gate) {
open.sort();
}

switch (mode) {
case RESET:
if (open.size() == 4 ) {
for (int i = 0; i < 4; i++) {
activeKeys[i].gate = false;
open.push_back(i);
}
}
break;
case REASSIGN:
open.push_back(open.front());
break;
case ROTATE:
break;
default:
fprintf(stderr, "No mode selected?!\n");
}

activeKeys[open.front()].gate = true;
activeKeys[open.front()].pitch = data1;
activeKeys[open.front()].vel = data2;
open.pop_front();
return;


}

int QuadMIDIToCVInterface::getMode() const {
return mode;
}

void QuadMIDIToCVInterface::setMode(int mode) {
resetMidi();
QuadMIDIToCVInterface::mode = mode;
}

struct ModeItem : MenuItem {
int mode;
QuadMIDIToCVInterface *module;

void onAction() {
module->setMode(mode);
}
};

struct ModeChoice : ChoiceButton {
QuadMIDIToCVInterface *module;
const std::vector<std::string> modeNames = {"ROTATE", "RESET", "REASSIGN"};


void onAction() {
Menu *menu = gScene->createMenu();
menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
menu->box.size.x = box.size.x;

for (unsigned long i = 0; i < modeNames.size(); i++) {
ModeItem *modeItem = new ModeItem();
modeItem->mode = i;
modeItem->module = module;
modeItem->text = modeNames[i];
menu->pushChild(modeItem);
}
}

void step() {
text = modeNames[module->getMode()];
}
};


QuadMidiToCVWidget::QuadMidiToCVWidget() {
QuadMIDIToCVInterface *module = new QuadMIDIToCVInterface();
setModule(module);
box.size = Vec(15 * 16, 380);

{
Panel *panel = new LightPanel();
panel->box.size = box.size;
addChild(panel);
}

float margin = 5;
float labelHeight = 15;
float yPos = margin;

addChild(createScrew<ScrewSilver>(Vec(15, 0)));
addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 0)));
addChild(createScrew<ScrewSilver>(Vec(15, 365)));
addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 365)));

{
Label *label = new Label();
label->box.pos = Vec(box.size.x - margin - 12 * 15, margin);
label->text = "Quad MIDI to CV";
addChild(label);
yPos = labelHeight * 2;
}

addParam(createParam<LEDButton>(Vec(12 * 15, labelHeight), module, QuadMIDIToCVInterface::RESET_PARAM, 0.0, 1.0,
0.0));
addChild(createValueLight<SmallLight<RedValueLight>>(Vec(12 * 15 + 5, labelHeight + 5), &module->resetLight));
{
Label *label = new Label();
label->box.pos = Vec(margin, yPos);
label->text = "MIDI Interface";
addChild(label);
yPos += labelHeight + margin;

MidiChoice *midiChoice = new MidiChoice();
midiChoice->midiModule = dynamic_cast<MidiIO *>(module);
midiChoice->box.pos = Vec(margin, yPos);
midiChoice->box.size.x = box.size.x - 10;
addChild(midiChoice);
yPos += midiChoice->box.size.y + margin;
}

{
Label *label = new Label();
label->box.pos = Vec(margin, yPos);
label->text = "Channel";
addChild(label);
yPos += labelHeight + margin;

ChannelChoice *channelChoice = new ChannelChoice();
channelChoice->midiModule = dynamic_cast<MidiIO *>(module);
channelChoice->box.pos = Vec(margin, yPos);
channelChoice->box.size.x = box.size.x - 10;
addChild(channelChoice);
yPos += channelChoice->box.size.y + margin;
}

{
Label *label = new Label();
label->box.pos = Vec(margin, yPos);
label->text = "Mode";
addChild(label);
yPos += labelHeight + margin;

ModeChoice *modeChoice = new ModeChoice();
modeChoice->module = module;
modeChoice->box.pos = Vec(margin, yPos);
modeChoice->box.size.x = box.size.x - 10;
addChild(modeChoice);
yPos += modeChoice->box.size.y + margin + 15;
}

{
Label *label = new Label();
label->box.pos = Vec(84, yPos);
label->text = "1";
addChild(label);
}
{
Label *label = new Label();
label->box.pos = Vec(125, yPos);
label->text = "2";
addChild(label);
}
{
Label *label = new Label();
label->box.pos = Vec(164, yPos);
label->text = "3";
addChild(label);
}
{
Label *label = new Label();
label->box.pos = Vec(203, yPos);
label->text = "4";
addChild(label);
}
std::string labels[4] = {"1V/oct", "Gate", "Velocity", "Aftertouch"};

yPos += labelHeight + margin * 2;
for (int i = 0; i < 4; i++) {
Label *label = new Label();
label->box.pos = Vec(margin, yPos);
label->text = labels[i];
addChild(label);
addOutput(createOutput<PJ3410Port>(Vec(2 * (40), yPos - 5), module, i * 4));
addOutput(createOutput<PJ3410Port>(Vec(3 * (40), yPos - 5), module, i * 4 + 1));
addOutput(createOutput<PJ3410Port>(Vec(4 * (40), yPos - 5), module, i * 4 + 2));
addOutput(createOutput<PJ3410Port>(Vec(5 * (40), yPos - 5), module, i * 4 + 3));
yPos += 40;
}


}

void QuadMidiToCVWidget::step() {

ModuleWidget::step();
}

+ 2
- 1
src/core/core.cpp View File

@@ -1,7 +1,6 @@
#include "core.hpp"
#include "MidiIO.hpp"


void init(rack::Plugin *p) {
p->slug = "Core";
p->addModel(createModel<AudioInterfaceWidget>("Core", "Core", "AudioInterface", "Audio Interface"));
@@ -9,6 +8,8 @@ void init(rack::Plugin *p) {
p->addModel(createModel<MIDICCToCVWidget>("Core", "Core", "MIDICCToCVInterface", "MIDI CC-to-CV Interface"));
p->addModel(createModel<MIDIClockToCVWidget>("Core", "Core", "MIDIClockToCVInterface", "MIDI Clock-to-CV Interface"));
p->addModel(createModel<MIDITriggerToCVWidget>("Core", "Core", "MIDITriggerToCVInterface", "MIDI Trigger-to-CV Interface"));
p->addModel(createModel<QuadMidiToCVWidget>("Core", "Core", "QuadMIDIToCVInterface", "Quad MIDI-to-CV Interface"));

// p->addModel(createModel<BridgeWidget>("Core", "Core", "Bridge", "Bridge"));
p->addModel(createModel<BlankWidget>("Core", "Core", "Blank", "Blank"));
p->addModel(createModel<NotesWidget>("Core", "Core", "Notes", "Notes"));


Loading…
Cancel
Save