Support different controller types, pitch wheel and NRPN
Clamp output data to rangemaster
parent
d3c5ac6bbf
commit
0b0add19bd
67
cvin.cpp
67
cvin.cpp
|
@ -22,25 +22,66 @@ int CVIn::process(jack_nframes_t nframes)
|
||||||
MappingList::iterator it;
|
MappingList::iterator it;
|
||||||
for (it = m_it_begin; it != m_it_end; ++it, port++) {
|
for (it = m_it_begin; it != m_it_end; ++it, port++) {
|
||||||
Mapping *m = &(*it);
|
Mapping *m = &(*it);
|
||||||
|
jack_midi_data_t buf[3];
|
||||||
|
|
||||||
m->cur_mv = m->to_mv(*m_buffers[port]++);
|
m->cur_mv = m->to_mv(*m_buffers[port]++);
|
||||||
|
|
||||||
if (m->tick_cvin()) {
|
if (m->tick_cvin()) {
|
||||||
if (m->cclsb == -1) {
|
switch (m->type) {
|
||||||
jack_midi_data_t buf[3];
|
case TYPE_PB:
|
||||||
buf[0] = 0xB0 | m->channel;
|
buf[0] = 0xE0 | m->channel;
|
||||||
buf[1] = m->ccmsb;
|
buf[1] = m->last_mv >> 7;
|
||||||
buf[2] = m->last_mv;
|
|
||||||
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
|
||||||
} else {
|
|
||||||
jack_midi_data_t buf[3];
|
|
||||||
buf[0] = 0xB0 | m->channel;
|
|
||||||
buf[1] = m->ccmsb;
|
|
||||||
buf[2] = m->last_mv >> 7;
|
|
||||||
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
|
||||||
buf[1] = m->cclsb;
|
|
||||||
buf[2] = m->last_mv & 0x7F;
|
buf[2] = m->last_mv & 0x7F;
|
||||||
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_CC:
|
||||||
|
buf[0] = 0xB0 | m->channel;
|
||||||
|
buf[1] = m->ccmsb;
|
||||||
|
if (m->has_lsb) {
|
||||||
|
buf[2] = m->last_mv >> 7;
|
||||||
|
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
||||||
|
buf[1] = m->cclsb;
|
||||||
|
}
|
||||||
|
buf[2] = m->last_mv & 0x7F;
|
||||||
|
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_NRPN:
|
||||||
|
buf[0] = 0xB0 | m->channel;
|
||||||
|
buf[1] = 99;
|
||||||
|
buf[2] = m->ccmsb;
|
||||||
|
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
||||||
|
buf[1] = 98;
|
||||||
|
buf[2] = m->cclsb;
|
||||||
|
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
||||||
|
buf[1] = 6;
|
||||||
|
if (m->has_lsb) {
|
||||||
|
buf[2] = m->last_mv >> 7;
|
||||||
|
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
||||||
|
buf[1] = 38;
|
||||||
|
}
|
||||||
|
buf[2] = m->last_mv & 0x7F;
|
||||||
|
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_RPN:
|
||||||
|
buf[0] = 0xB0 | m->channel;
|
||||||
|
buf[1] = 101;
|
||||||
|
buf[2] = m->ccmsb;
|
||||||
|
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
||||||
|
buf[1] = 100;
|
||||||
|
buf[2] = m->cclsb;
|
||||||
|
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
||||||
|
buf[1] = 6;
|
||||||
|
if (m->has_lsb) {
|
||||||
|
buf[2] = m->last_mv >> 7;
|
||||||
|
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
||||||
|
buf[1] = 38;
|
||||||
|
}
|
||||||
|
buf[2] = m->last_mv & 0x7F;
|
||||||
|
jack_midi_event_write(midi_out, f, buf, sizeof buf);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
73
cvout.cpp
73
cvout.cpp
|
@ -24,15 +24,78 @@ int CVOut::process(jack_nframes_t nframes)
|
||||||
|
|
||||||
for (jack_nframes_t f = 0; f < nframes; f++) {
|
for (jack_nframes_t f = 0; f < nframes; f++) {
|
||||||
while (ev.time == f && event_index < event_count) {
|
while (ev.time == f && event_index < event_count) {
|
||||||
if ((ev.buffer[0] & 0xF0) == 0xB0) {
|
int channel = ev.buffer[0] & 0x0F;
|
||||||
// Do what
|
MappingList::iterator it;
|
||||||
int channel = ev.buffer[0] & 0x0F;
|
|
||||||
|
|
||||||
MappingList::iterator it;
|
switch (ev.buffer[0] & 0xF0) {
|
||||||
|
case 0xB0:
|
||||||
|
if (ev.buffer[1] == 99) {
|
||||||
|
m_nrpn_msb[channel] = ev.buffer[2];
|
||||||
|
m_nrpn[channel] = true;
|
||||||
|
} else if (ev.buffer[1] == 98) {
|
||||||
|
m_nrpn_lsb[channel] = ev.buffer[2];
|
||||||
|
m_nrpn[channel] = true;
|
||||||
|
} else if (ev.buffer[1] == 101) {
|
||||||
|
m_nrpn_msb[channel] = ev.buffer[2];
|
||||||
|
m_nrpn[channel] = false;
|
||||||
|
} else if (ev.buffer[1] == 100) {
|
||||||
|
m_nrpn_lsb[channel] = ev.buffer[2];
|
||||||
|
m_nrpn[channel] = false;
|
||||||
|
} else {
|
||||||
|
if (ev.buffer[1] == 6 || ev.buffer[1] == 38) {
|
||||||
|
ControllerType type = m_nrpn[channel] ? TYPE_NRPN : TYPE_RPN;
|
||||||
|
bool msb = ev.buffer[1] == 6;
|
||||||
|
|
||||||
|
for (it = m_it_begin; it != m_it_end; ++it) {
|
||||||
|
Mapping *m = &(*it);
|
||||||
|
if (!m->match(type, channel)) continue;
|
||||||
|
if (m_nrpn_msb[channel] != m->ccmsb) continue;
|
||||||
|
if (m_nrpn_lsb[channel] != m->cclsb) continue;
|
||||||
|
|
||||||
|
if (msb) {
|
||||||
|
if (m->has_lsb) {
|
||||||
|
m->cur_mv = ev.buffer[2] << 7;
|
||||||
|
} else {
|
||||||
|
m->cur_mv = ev.buffer[2];
|
||||||
|
m->interp_cvout(m_tick);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m->cur_mv &= ~0x7F;
|
||||||
|
m->cur_mv |= ev.buffer[2];
|
||||||
|
m->interp_cvout(m_tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (it = m_it_begin; it != m_it_end; ++it) {
|
||||||
|
Mapping *m = &(*it);
|
||||||
|
if (!m->match(TYPE_CC, channel)) continue;
|
||||||
|
|
||||||
|
if (ev.buffer[1] == m->ccmsb) {
|
||||||
|
if (m->has_lsb) {
|
||||||
|
m->cur_mv = ev.buffer[2] << 7;
|
||||||
|
} else {
|
||||||
|
m->cur_mv = ev.buffer[2];
|
||||||
|
m->interp_cvout(m_tick);
|
||||||
|
}
|
||||||
|
} else if (ev.buffer[1] == m->cclsb) {
|
||||||
|
m->cur_mv &= ~0x7F;
|
||||||
|
m->cur_mv |= ev.buffer[2];
|
||||||
|
m->interp_cvout(m_tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xE0:
|
||||||
for (it = m_it_begin; it != m_it_end; ++it) {
|
for (it = m_it_begin; it != m_it_end; ++it) {
|
||||||
Mapping *m = &(*it);
|
Mapping *m = &(*it);
|
||||||
m->handle_cvout(channel, ev.buffer[1], ev.buffer[2], m_tick);
|
if (!m->match(TYPE_PB, channel)) continue;
|
||||||
|
|
||||||
|
m->cur_mv = ev.buffer[1] | (ev.buffer[2] << 7);
|
||||||
|
m->interp_cvout(m_tick);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
event_index++;
|
event_index++;
|
||||||
|
|
6
cvout.h
6
cvout.h
|
@ -1,6 +1,8 @@
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "mapping.h"
|
#include "mapping.h"
|
||||||
|
|
||||||
|
#define MAX_MIDI_CHANNELS 16
|
||||||
|
|
||||||
class CVOut : Client
|
class CVOut : Client
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -14,6 +16,10 @@ private:
|
||||||
MappingList::iterator m_it_begin;
|
MappingList::iterator m_it_begin;
|
||||||
MappingList::iterator m_it_end;
|
MappingList::iterator m_it_end;
|
||||||
|
|
||||||
|
char m_nrpn_msb[MAX_MIDI_CHANNELS];
|
||||||
|
char m_nrpn_lsb[MAX_MIDI_CHANNELS];
|
||||||
|
bool m_nrpn[MAX_MIDI_CHANNELS];
|
||||||
|
|
||||||
void shutdown();
|
void shutdown();
|
||||||
int process(jack_nframes_t nframes);
|
int process(jack_nframes_t nframes);
|
||||||
|
|
||||||
|
|
34
example.cfg
34
example.cfg
|
@ -1,16 +1,22 @@
|
||||||
#type name chan, cc MSB/LSB, midi range, cv range, latency
|
#type name chan, cc MSB/LSB, midi range, cv range, latency
|
||||||
cvout pan -1 10 -1 0 127 -1.0 1.0 10
|
cvout pan -1 cc 10 -1 0 127 -1.0 1.0 10
|
||||||
cvout reverb -1 91 -1 0 127 -1.0 1.0 10
|
cvout reverb -1 cc 91 -1 0 127 -1.0 1.0 10
|
||||||
cvout exp -1 11 -1 0 127 -1.0 1.0 10
|
cvout exp -1 cc 11 -1 0 127 -1.0 1.0 10
|
||||||
cvout vol1 0 7 -1 0 127 -1.0 1.0 10
|
cvout vol1 0 cc 7 -1 0 127 -1.0 1.0 10
|
||||||
cvout vol2 1 7 -1 0 127 -1.0 1.0 10
|
cvout vol2 1 cc 7 -1 0 127 -1.0 1.0 10
|
||||||
cvout vol3 2 7 -1 0 127 -1.0 1.0 10
|
cvout vol3 2 cc 7 -1 0 127 -1.0 1.0 10
|
||||||
cvout vol4 3 7 -1 0 127 -1.0 1.0 10
|
cvout vol4 3 cc 7 -1 0 127 -1.0 1.0 10
|
||||||
cvout vol5 4 7 -1 0 127 -1.0 1.0 10
|
cvout vol5 4 cc 7 -1 0 127 -1.0 1.0 10
|
||||||
cvout vol6 5 7 -1 0 127 -1.0 1.0 10
|
cvout vol6 5 cc 7 -1 0 127 -1.0 1.0 10
|
||||||
cvout vol7 6 7 -1 0 127 -1.0 1.0 10
|
cvout vol7 6 cc 7 -1 0 127 -1.0 1.0 10
|
||||||
cvout vol8 7 7 -1 0 127 -1.0 1.0 10
|
cvout vol8 7 cc 7 -1 0 127 -1.0 1.0 10
|
||||||
cvout mod -1 1 33 0 16383 -1.0 1.0 10
|
cvout mod -1 cc 1 33 0 16383 -1.0 1.0 10
|
||||||
cvin mod 0 1 33 0 16383 -1.0 1.0 10
|
cvin mod 0 cc 1 33 0 16383 -1.0 1.0 10
|
||||||
cvin vol1 0 7 -1 0 127 -1.0 1.0 10
|
cvin vol1 0 cc 7 -1 0 127 -1.0 1.0 10
|
||||||
|
cvout exp1 -1 nrpn 0 5 0 16383 -1.0 1.0 10
|
||||||
|
cvout exp2 -1 nrpn7 0 6 0 127 -1.0 1.0 10
|
||||||
|
cvout pitch -1 pb -1 -1 0 16383 -1.0 1.0 10
|
||||||
|
cvin exp1 0 nrpn 0 5 0 16383 -1.0 1.0 10
|
||||||
|
cvin exp2 0 nrpn7 0 6 0 127 -1.0 1.0 10
|
||||||
|
cvin pitch 0 pb -1 -1 0 16383 -1.0 1.0 10
|
||||||
|
|
||||||
|
|
41
jm2cv.cpp
41
jm2cv.cpp
|
@ -23,28 +23,49 @@ bool read_config(const char *filename)
|
||||||
/* Ignore comments */
|
/* Ignore comments */
|
||||||
if (buf[0] == '#') continue;
|
if (buf[0] == '#') continue;
|
||||||
|
|
||||||
char type[80], name[80];
|
char dir[80], name[80], type[80];
|
||||||
int channel, ccmsb, cclsb, mrl, mru;
|
int channel, ccmsb, cclsb, mrl, mru;
|
||||||
float crl, cru;
|
float crl, cru;
|
||||||
float latency;
|
float latency;
|
||||||
if (sscanf(buf, "%s %s %d %d %d %d %d %f %f %f", type, name, &channel, &ccmsb, &cclsb, &mrl, &mru, &crl, &cru, &latency) == 10) {
|
if (sscanf(buf, "%s %s %d %s %d %d %d %d %f %f %f", dir, name, &channel, type, &ccmsb, &cclsb, &mrl, &mru, &crl, &cru, &latency) == 11) {
|
||||||
|
|
||||||
if (ccmsb < 0 || ccmsb > 127) continue;
|
ControllerType itype;
|
||||||
if (cclsb < -1 || cclsb > 127) continue;
|
bool has_lsb = true;
|
||||||
if (mrl < 0 || mrl > (cclsb == -1 ? 127 : 16383)) continue;
|
|
||||||
if (mru < 0 || mru > (cclsb == -1 ? 127 : 16383)) continue;
|
if (!strcmp(type, "pb")) itype = TYPE_PB;
|
||||||
|
else if (!strcmp(type, "cc")) itype = TYPE_CC;
|
||||||
|
else if (!strcmp(type, "nrpn")) itype = TYPE_NRPN;
|
||||||
|
else if (!strcmp(type, "nrpn7")) { itype = TYPE_NRPN; has_lsb = false; }
|
||||||
|
else if (!strcmp(type, "rpn")) itype = TYPE_RPN;
|
||||||
|
else if (!strcmp(type, "rpn7")) { itype = TYPE_RPN; has_lsb = false; }
|
||||||
|
else continue;
|
||||||
|
|
||||||
|
if (itype == TYPE_CC && cclsb == -1) has_lsb = false;
|
||||||
|
|
||||||
|
if (itype == TYPE_PB) {
|
||||||
|
if (ccmsb != -1) continue;
|
||||||
|
if (cclsb != -1) continue;
|
||||||
|
if (mrl < 0 || mrl > 16383) continue;
|
||||||
|
if (mru < 0 || mru > 16383) continue;
|
||||||
|
has_lsb = false;
|
||||||
|
} else {
|
||||||
|
if (ccmsb < 0 || ccmsb > 127) continue;
|
||||||
|
if (cclsb < -1 || cclsb > 127) continue;
|
||||||
|
if (mrl < 0 || mrl > (has_lsb ? 16383 : 127)) continue;
|
||||||
|
if (mru < 0 || mru > (has_lsb ? 16383 : 127)) continue;
|
||||||
|
}
|
||||||
if (mrl > mru) continue;
|
if (mrl > mru) continue;
|
||||||
// if (crl < -1.0f || crl > 1.0f) continue;
|
// if (crl < -1.0f || crl > 1.0f) continue;
|
||||||
// if (cru < -1.0f || cru > 1.0f) continue;
|
// if (cru < -1.0f || cru > 1.0f) continue;
|
||||||
if (crl > cru) continue;
|
if (crl > cru) continue;
|
||||||
if (latency < 0.0f) continue;
|
if (latency < 0.0f) continue;
|
||||||
|
|
||||||
if (!strcmp(type, "cvout")) {
|
if (!strcmp(dir, "cvout")) {
|
||||||
if (channel < -1 || channel > 15) continue;
|
if (channel < -1 || channel > 15) continue;
|
||||||
cvout.add_mapping(Mapping(name, channel, ccmsb, cclsb, mrl, mru, crl, cru, latency));
|
cvout.add_mapping(Mapping(name, channel, itype, ccmsb, cclsb, mrl, mru, crl, cru, latency, has_lsb));
|
||||||
} else if (!strcmp(type, "cvin")) {
|
} else if (!strcmp(dir, "cvin")) {
|
||||||
if (channel < 0 || channel > 15) continue;
|
if (channel < 0 || channel > 15) continue;
|
||||||
cvin.add_mapping(Mapping(name, channel, ccmsb, cclsb, mrl, mru, crl, cru, latency));
|
cvin.add_mapping(Mapping(name, channel, itype, ccmsb, cclsb, mrl, mru, crl, cru, latency, has_lsb));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
47
mapping.h
47
mapping.h
|
@ -4,16 +4,33 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
enum ControllerType {
|
||||||
|
TYPE_PB,
|
||||||
|
TYPE_CC,
|
||||||
|
TYPE_NRPN,
|
||||||
|
TYPE_RPN
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T clamp(T x, T min, T max)
|
||||||
|
{
|
||||||
|
if (x < min) return min;
|
||||||
|
if (x > max) return max;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
class Mapping
|
class Mapping
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::string name;
|
std::string name;
|
||||||
int channel;
|
int channel;
|
||||||
|
ControllerType type;
|
||||||
int ccmsb, cclsb;
|
int ccmsb, cclsb;
|
||||||
int mrl, mru;
|
int mrl, mru;
|
||||||
float crl, cru;
|
float crl, cru;
|
||||||
float latency;
|
float latency;
|
||||||
|
|
||||||
|
bool has_lsb;
|
||||||
float adj1;
|
float adj1;
|
||||||
float adj2;
|
float adj2;
|
||||||
tick_t latency_ticks;
|
tick_t latency_ticks;
|
||||||
|
@ -27,10 +44,11 @@ public:
|
||||||
tick_t since_last_tick;
|
tick_t since_last_tick;
|
||||||
|
|
||||||
Mapping(const char *name,
|
Mapping(const char *name,
|
||||||
int channel, int ccmsb, int cclsb,
|
int channel, ControllerType(type), int ccmsb, int cclsb,
|
||||||
int mrl, int mru, float crl, float cru, float latency) :
|
int mrl, int mru, float crl, float cru, float latency, bool has_lsb) :
|
||||||
name(name), channel(channel), ccmsb(ccmsb), cclsb(cclsb),
|
name(name), channel(channel), type(type), ccmsb(ccmsb), cclsb(cclsb),
|
||||||
mrl(mrl), mru(mru), crl(crl), cru(cru), latency(latency),
|
mrl(mrl), mru(mru), crl(crl), cru(cru), latency(latency),
|
||||||
|
has_lsb(has_lsb),
|
||||||
cur_mv(0), last_mv(0),
|
cur_mv(0), last_mv(0),
|
||||||
last_cv(0.0),
|
last_cv(0.0),
|
||||||
last_tick(0), since_last_tick(0)
|
last_tick(0), since_last_tick(0)
|
||||||
|
@ -67,30 +85,19 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_cvout(int c, int cc, int val, tick_t tick)
|
inline float to_cv(int mv) const
|
||||||
{
|
{
|
||||||
if (c != channel && channel != -1) return;
|
return clamp((mv - adj2) * adj1, crl, cru);
|
||||||
if (cc == ccmsb) {
|
|
||||||
if (cclsb == -1) {
|
|
||||||
cur_mv = val;
|
|
||||||
interp_cvout(tick);
|
|
||||||
} else {
|
|
||||||
cur_mv = val << 7;
|
|
||||||
}
|
|
||||||
} else if (cc == cclsb) {
|
|
||||||
cur_mv |= val;
|
|
||||||
interp_cvout(tick);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline float to_cv(int mv)
|
inline int to_mv(float cv) const
|
||||||
{
|
{
|
||||||
return (mv - adj2) * adj1;
|
return clamp((int)(cv / adj1 + adj2), mrl, mru);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int to_mv(float cv)
|
inline bool match(ControllerType _type, int _channel) const
|
||||||
{
|
{
|
||||||
return cv / adj1 + adj2;
|
return type == _type && (channel == _channel || channel == -1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue