Browse Source

Support different controller types, pitch wheel and NRPN

Clamp output data to range
master
Peter Nelson 12 years ago
parent
commit
0b0add19bd
  1. 57
      cvin.cpp
  2. 73
      cvout.cpp
  3. 6
      cvout.h
  4. 34
      example.cfg
  5. 43
      jm2cv.cpp
  6. 47
      mapping.h

57
cvin.cpp

@ -22,25 +22,66 @@ int CVIn::process(jack_nframes_t nframes)
MappingList::iterator it;
for (it = m_it_begin; it != m_it_end; ++it, port++) {
Mapping *m = &(*it);
jack_midi_data_t buf[3];
m->cur_mv = m->to_mv(*m_buffers[port]++);
if (m->tick_cvin()) {
if (m->cclsb == -1) {
jack_midi_data_t buf[3];
switch (m->type) {
case TYPE_PB:
buf[0] = 0xE0 | m->channel;
buf[1] = m->last_mv >> 7;
buf[2] = m->last_mv & 0x7F;
jack_midi_event_write(midi_out, f, buf, sizeof buf);
break;
case TYPE_CC:
buf[0] = 0xB0 | m->channel;
buf[1] = m->ccmsb;
buf[2] = m->last_mv;
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);
} else {
jack_midi_data_t buf[3];
break;
case TYPE_NRPN:
buf[0] = 0xB0 | m->channel;
buf[1] = m->ccmsb;
buf[2] = m->last_mv >> 7;
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] = m->cclsb;
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

@ -24,15 +24,78 @@ int CVOut::process(jack_nframes_t nframes)
for (jack_nframes_t f = 0; f < nframes; f++) {
while (ev.time == f && event_index < event_count) {
if ((ev.buffer[0] & 0xF0) == 0xB0) {
// Do what
int channel = ev.buffer[0] & 0x0F;
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;
MappingList::iterator it;
case 0xE0:
for (it = m_it_begin; it != m_it_end; ++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++;

6
cvout.h

@ -1,6 +1,8 @@
#include "client.h"
#include "mapping.h"
#define MAX_MIDI_CHANNELS 16
class CVOut : Client
{
private:
@ -14,6 +16,10 @@ private:
MappingList::iterator m_it_begin;
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();
int process(jack_nframes_t nframes);

34
example.cfg

@ -1,16 +1,22 @@
#type name chan, cc MSB/LSB, midi range, cv range, latency
cvout pan -1 10 -1 0 127 -1.0 1.0 10
cvout reverb -1 91 -1 0 127 -1.0 1.0 10
cvout exp -1 11 -1 0 127 -1.0 1.0 10
cvout vol1 0 7 -1 0 127 -1.0 1.0 10
cvout vol2 1 7 -1 0 127 -1.0 1.0 10
cvout vol3 2 7 -1 0 127 -1.0 1.0 10
cvout vol4 3 7 -1 0 127 -1.0 1.0 10
cvout vol5 4 7 -1 0 127 -1.0 1.0 10
cvout vol6 5 7 -1 0 127 -1.0 1.0 10
cvout vol7 6 7 -1 0 127 -1.0 1.0 10
cvout vol8 7 7 -1 0 127 -1.0 1.0 10
cvout mod -1 1 33 0 16383 -1.0 1.0 10
cvin mod 0 1 33 0 16383 -1.0 1.0 10
cvin vol1 0 7 -1 0 127 -1.0 1.0 10
cvout pan -1 cc 10 -1 0 127 -1.0 1.0 10
cvout reverb -1 cc 91 -1 0 127 -1.0 1.0 10
cvout exp -1 cc 11 -1 0 127 -1.0 1.0 10
cvout vol1 0 cc 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 cc 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 cc 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 cc 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 cc 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 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

43
jm2cv.cpp

@ -23,28 +23,49 @@ bool read_config(const char *filename)
/* Ignore comments */
if (buf[0] == '#') continue;
char type[80], name[80];
char dir[80], name[80], type[80];
int channel, ccmsb, cclsb, mrl, mru;
float crl, cru;
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 (ccmsb < 0 || ccmsb > 127) continue;
if (cclsb < -1 || cclsb > 127) continue;
if (mrl < 0 || mrl > (cclsb == -1 ? 127 : 16383)) continue;
if (mru < 0 || mru > (cclsb == -1 ? 127 : 16383)) continue;
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) {
ControllerType itype;
bool has_lsb = true;
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 (crl < -1.0f || crl > 1.0f) continue;
// if (cru < -1.0f || cru > 1.0f) continue;
if (crl > cru) continue;
if (latency < 0.0f) continue;
if (!strcmp(type, "cvout")) {
if (!strcmp(dir, "cvout")) {
if (channel < -1 || channel > 15) continue;
cvout.add_mapping(Mapping(name, channel, ccmsb, cclsb, mrl, mru, crl, cru, latency));
} else if (!strcmp(type, "cvin")) {
cvout.add_mapping(Mapping(name, channel, itype, ccmsb, cclsb, mrl, mru, crl, cru, latency, has_lsb));
} else if (!strcmp(dir, "cvin")) {
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

@ -4,16 +4,33 @@
#include <string>
#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
{
public:
std::string name;
int channel;
ControllerType type;
int ccmsb, cclsb;
int mrl, mru;
float crl, cru;
float latency;
bool has_lsb;
float adj1;
float adj2;
tick_t latency_ticks;
@ -27,10 +44,11 @@ public:
tick_t since_last_tick;
Mapping(const char *name,
int channel, int ccmsb, int cclsb,
int mrl, int mru, float crl, float cru, float latency) :
name(name), channel(channel), ccmsb(ccmsb), cclsb(cclsb),
int channel, ControllerType(type), int ccmsb, int cclsb,
int mrl, int mru, float crl, float cru, float latency, bool has_lsb) :
name(name), channel(channel), type(type), ccmsb(ccmsb), cclsb(cclsb),
mrl(mrl), mru(mru), crl(crl), cru(cru), latency(latency),
has_lsb(has_lsb),
cur_mv(0), last_mv(0),
last_cv(0.0),
last_tick(0), since_last_tick(0)
@ -67,30 +85,19 @@ public:
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;
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);
}
return clamp((mv - adj2) * adj1, crl, cru);
}
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…
Cancel
Save