Browse Source

Create dsp/midi.hpp, based on CV_MIDI's implementation of PolyphonicMidiOutput.

tags/v1.0.0
Andrew Belt 5 years ago
parent
commit
5a40e0a140
5 changed files with 230 additions and 212 deletions
  1. +1
    -2
      include/dsp/common.hpp
  2. +212
    -0
      include/dsp/midi.hpp
  3. +8
    -4
      include/midi.hpp
  4. +1
    -0
      include/rack.hpp
  5. +8
    -206
      src/Core/CV_MIDI.cpp

+ 1
- 2
include/dsp/common.hpp View File

@@ -82,8 +82,7 @@ Slow. Not recommended for parameter scaling.
*/ */
template <typename T> template <typename T>
T exponentialBipolar(T b, T x) { T exponentialBipolar(T b, T x) {
T a = b - 1.f / b;
return (std::pow(b, x) - std::pow(b, -x)) / a;
return (simd::pow(b, x) - simd::pow(b, -x)) / (b - 1.f / b);
} }






+ 212
- 0
include/dsp/midi.hpp View File

@@ -0,0 +1,212 @@
#pragma once
#include <dsp/common.hpp>
#include <midi.hpp>


namespace rack {
namespace dsp {


/** Converts gates and CV to MIDI messages.
CHANNELS is the number of polyphony channels. Use 1 for monophonic.
*/
template <int CHANNELS>
struct MidiGenerator {
int8_t vels[CHANNELS];
int8_t notes[CHANNELS];
bool gates[CHANNELS];
int8_t keyPressures[CHANNELS];
int8_t channelPressures[CHANNELS];
int8_t ccs[128];
int16_t pw;
bool clk;
bool start;
bool stop;
bool cont;

MidiGenerator() {
reset();
}

void reset() {
for (int c = 0; c < CHANNELS; c++) {
vels[c] = 100;
notes[c] = 60;
gates[c] = false;
keyPressures[c] = -1;
channelPressures[c] = -1;
}
for (int i = 0; i < 128; i++) {
ccs[i] = -1;
}
pw = 0x2000;
clk = false;
start = false;
stop = false;
cont = false;
}

void panic() {
reset();
// Send all note off commands
for (int note = 0; note <= 127; note++) {
// Note off
midi::Message m;
m.setStatus(0x8);
m.setNote(note);
m.setValue(0);
onMessage(m);
}
}

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

void setNoteGate(int8_t note, bool gate, int c) {
bool changedNote = gate && gates[c] && (note != notes[c]);
bool enabledGate = gate && !gates[c];
bool disabledGate = !gate && gates[c];
if (changedNote || disabledGate) {
// Note off
midi::Message m;
m.setStatus(0x8);
m.setNote(notes[c]);
m.setValue(vels[c]);
onMessage(m);
}
if (changedNote || enabledGate) {
// Note on
midi::Message m;
m.setStatus(0x9);
m.setNote(note);
m.setValue(vels[c]);
onMessage(m);
}
notes[c] = note;
gates[c] = gate;
}

void setKeyPressure(int8_t val, int c) {
if (keyPressures[c] == val)
return;
keyPressures[c] = val;
// Polyphonic key pressure
midi::Message m;
m.setStatus(0xa);
m.setNote(notes[c]);
m.setValue(val);
onMessage(m);
}

void setChannelPressure(int8_t val, int c) {
if (channelPressures[c] == val)
return;
channelPressures[c] = val;
// Channel pressure
midi::Message m;
m.setSize(2);
m.setStatus(0xd);
m.setNote(val);
onMessage(m);
}

void setCc(int8_t cc, int id) {
if (ccs[id] == cc)
return;
ccs[id] = cc;
// Control change
midi::Message m;
m.setStatus(0xb);
m.setNote(id);
m.setValue(cc);
onMessage(m);
}

void setModWheel(int8_t mw) {
setCc(mw, 0x01);
}

void setVolume(int8_t mw) {
setCc(mw, 0x07);
}

void setPan(int8_t mw) {
setCc(mw, 0x0a);
}

void setPitchWheel(int16_t pw) {
if (this->pw == pw)
return;
this->pw = pw;
// Pitch wheel
midi::Message m;
m.setStatus(0xe);
m.setNote(pw & 0x7f);
m.setValue((pw >> 7) & 0x7f);
onMessage(m);
}

void setClock(bool clk) {
if (this->clk == clk)
return;
this->clk = clk;
if (clk) {
// Timing clock
midi::Message m;
m.setSize(1);
m.setStatus(0xf);
m.setChannel(0x8);
onMessage(m);
}
}

void setStart(bool start) {
if (this->start == start)
return;
this->start = start;
if (start) {
// Start
midi::Message m;
m.setSize(1);
m.setStatus(0xf);
m.setChannel(0xa);
onMessage(m);
}
}

void setContinue(bool cont) {
if (this->cont == cont)
return;
this->cont = cont;
if (cont) {
// Continue
midi::Message m;
m.setSize(1);
m.setStatus(0xf);
m.setChannel(0xb);
onMessage(m);
}
}

void setStop(bool stop) {
if (this->stop == stop)
return;
this->stop = stop;
if (stop) {
// Stop
midi::Message m;
m.setSize(1);
m.setStatus(0xf);
m.setChannel(0xc);
onMessage(m);
}
}

virtual void onMessage(midi::Message message) {}
};


} // namespace dsp
} // namespace rack

+ 8
- 4
include/midi.hpp View File

@@ -18,6 +18,10 @@ struct Message {
uint8_t size = 3; uint8_t size = 3;
uint8_t bytes[3] = {}; uint8_t bytes[3] = {};


void setSize(uint8_t size) {
assert(size <= 3);
this->size = size;
}
uint8_t getChannel() { uint8_t getChannel() {
return bytes[0] & 0xf; return bytes[0] & 0xf;
} }
@@ -25,19 +29,19 @@ struct Message {
bytes[0] = (bytes[0] & 0xf0) | (channel & 0xf); bytes[0] = (bytes[0] & 0xf0) | (channel & 0xf);
} }
uint8_t getStatus() { uint8_t getStatus() {
return (bytes[0] >> 4) & 0xf;
return bytes[0] >> 4;
} }
void setStatus(uint8_t status) { void setStatus(uint8_t status) {
bytes[0] = (bytes[0] & 0xf) | ((status << 4) & 0xf0);
bytes[0] = (bytes[0] & 0xf) | (status << 4);
} }
uint8_t getNote() { uint8_t getNote() {
return bytes[1] & 0x7f;
return bytes[1];
} }
void setNote(uint8_t note) { void setNote(uint8_t note) {
bytes[1] = note & 0x7f; bytes[1] = note & 0x7f;
} }
uint8_t getValue() { uint8_t getValue() {
return bytes[2] & 0x7f;
return bytes[2];
} }
void setValue(uint8_t value) { void setValue(uint8_t value) {
bytes[2] = value & 0x7f; bytes[2] = value & 0x7f;


+ 1
- 0
include/rack.hpp View File

@@ -84,6 +84,7 @@
#include <dsp/fft.hpp> #include <dsp/fft.hpp>
#include <dsp/filter.hpp> #include <dsp/filter.hpp>
#include <dsp/fir.hpp> #include <dsp/fir.hpp>
#include <dsp/midi.hpp>
#include <dsp/minblep.hpp> #include <dsp/minblep.hpp>
#include <dsp/ode.hpp> #include <dsp/ode.hpp>
#include <dsp/resampler.hpp> #include <dsp/resampler.hpp>


+ 8
- 206
src/Core/CV_MIDI.cpp View File

@@ -1,207 +1,14 @@
#include "plugin.hpp" #include "plugin.hpp"




template <int C>
struct PolyphonicMidiOutput : midi::Output {
int vels[C];
int lastNotes[C];
int notes[C];
bool lastGates[C];
bool gates[C];
int lastAfts[C];
int lastPw;
int lastMw;
bool lastClk;
int lastVol;
int lastPan;
bool lastStart;
bool lastStop;
bool lastCont;

PolyphonicMidiOutput() {
reset();
struct MidiOutput : dsp::MidiGenerator<PORT_MAX_CHANNELS>, midi::Output {
void onMessage(midi::Message message) override {
midi::Output::sendMessage(message);
} }


void reset() { void reset() {
for (int c = 0; c < C; c++) {
vels[c] = 100;
lastNotes[c] = notes[c] = 60;
lastGates[c] = gates[c] = false;
lastAfts[c] = -1;
}
lastPw = 0x2000;
lastMw = 0;
lastClk = false;
lastVol = 127;
lastPan = 64;
lastStart = false;
lastStop = false;
lastCont = false;
}

void panic() {
reset();
// Send all note off commands
for (int note = 0; note <= 127; note++) {
// Note off
midi::Message m;
m.setStatus(0x8);
m.setNote(note);
m.setValue(0);
sendMessage(m);
}
}

void setVelocity(int vel, int c) {
vels[c] = vel;
}

void setNote(int note, int c) {
notes[c] = note;
}

void setGate(bool gate, int c) {
gates[c] = gate;
}

void stepChannel(int c) {
bool changedNote = gates[c] && lastGates[c] && notes[c] != lastNotes[c];
bool enabledGate = gates[c] && !lastGates[c];
bool disabledGate = !gates[c] && lastGates[c];
if (changedNote || enabledGate) {
// Note on
midi::Message m;
m.setStatus(0x9);
m.setNote(notes[c]);
m.setValue(vels[c]);
sendMessage(m);
}
if (changedNote || disabledGate) {
// Note off
midi::Message m;
m.setStatus(0x8);
m.setNote(lastNotes[c]);
m.setValue(vels[c]);
sendMessage(m);
}
lastNotes[c] = notes[c];
lastGates[c] = gates[c];
}

void setAftertouch(int aft, int c) {
if (lastAfts[c] == aft)
return;
lastAfts[c] = aft;
// Polyphonic key pressure
midi::Message m;
m.setStatus(0xa);
m.setNote(notes[c]);
m.setValue(aft);
sendMessage(m);
}

void setPitchWheel(int pw) {
if (lastPw == pw)
return;
lastPw = pw;
// Pitch wheel
midi::Message m;
m.setStatus(0xe);
m.setNote(pw & 0x7f);
m.setValue((pw >> 7) & 0x7f);
sendMessage(m);
}

void setModWheel(int mw) {
if (lastMw == mw)
return;
lastMw = mw;
// CC Mod wheel
midi::Message m;
m.setStatus(0xb);
m.setNote(0x01);
m.setValue(mw);
sendMessage(m);
}

void setClock(bool clk) {
if (lastClk == clk)
return;
lastClk = clk;
if (clk) {
// Timing clock
midi::Message m;
m.size = 1;
m.setStatus(0xf);
m.setChannel(0x8);
sendMessage(m);
}
}

void setVolume(int vol) {
if (lastVol == vol)
return;
lastVol = vol;
// CC Volume
midi::Message m;
m.setStatus(0xb);
m.setNote(0x07);
m.setValue(vol);
sendMessage(m);
}

void setPan(int pan) {
if (lastPan == pan)
return;
lastPan = pan;
// CC Pan
midi::Message m;
m.setStatus(0xb);
m.setNote(0x0a);
m.setValue(pan);
sendMessage(m);
}

void setStart(bool start) {
if (lastStart == start)
return;
lastStart = start;
if (start) {
// Start
midi::Message m;
m.size = 1;
m.setStatus(0xf);
m.setChannel(0xa);
sendMessage(m);
}
}

void setContinue(bool cont) {
if (lastCont == cont)
return;
lastCont = cont;
if (cont) {
// Continue
midi::Message m;
m.size = 1;
m.setStatus(0xf);
m.setChannel(0xb);
sendMessage(m);
}
}

void setStop(bool stop) {
if (lastStop == stop)
return;
lastStop = stop;
if (stop) {
// Stop
midi::Message m;
m.size = 1;
m.setStatus(0xf);
m.setChannel(0xc);
sendMessage(m);
}
Output::reset();
MidiGenerator::reset();
} }
}; };


@@ -232,7 +39,7 @@ struct CV_MIDI : Module {
NUM_LIGHTS NUM_LIGHTS
}; };


PolyphonicMidiOutput<PORT_MAX_CHANNELS> midiOutput;
MidiOutput midiOutput;
float rateLimiterPhase = 0.f; float rateLimiterPhase = 0.f;


CV_MIDI() { CV_MIDI() {
@@ -242,7 +49,6 @@ struct CV_MIDI : Module {


void onReset() override { void onReset() override {
midiOutput.reset(); midiOutput.reset();
midiOutput.midi::Output::reset();
} }


void process(const ProcessArgs &args) override { void process(const ProcessArgs &args) override {
@@ -262,16 +68,12 @@ struct CV_MIDI : Module {


int note = (int) std::round(inputs[PITCH_INPUT].getVoltage(c) * 12.f + 60.f); int note = (int) std::round(inputs[PITCH_INPUT].getVoltage(c) * 12.f + 60.f);
note = clamp(note, 0, 127); note = clamp(note, 0, 127);
midiOutput.setNote(note, c);

bool gate = inputs[GATE_INPUT].getPolyVoltage(c) >= 1.f; bool gate = inputs[GATE_INPUT].getPolyVoltage(c) >= 1.f;
midiOutput.setGate(gate, c);

midiOutput.stepChannel(c);
midiOutput.setNoteGate(note, gate, c);


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


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


Loading…
Cancel
Save