From 0b0add19bd4d723d248f1a645ed859c35b574c01 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 24 Jan 2010 23:05:34 +0000 Subject: [PATCH] Support different controller types, pitch wheel and NRPN Clamp output data to range --- cvin.cpp | 67 ++++++++++++++++++++++++++++++++++++++---------- cvout.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++++++++---- cvout.h | 6 +++++ example.cfg | 34 +++++++++++++++---------- jm2cv.cpp | 41 ++++++++++++++++++++++-------- mapping.h | 47 +++++++++++++++++++--------------- 6 files changed, 206 insertions(+), 62 deletions(-) diff --git a/cvin.cpp b/cvin.cpp index 85e10ba..d7fd4d8 100644 --- a/cvin.cpp +++ b/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]; - buf[0] = 0xB0 | m->channel; - buf[1] = m->ccmsb; - 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; + 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; + 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; } } } diff --git a/cvout.cpp b/cvout.cpp index 923951f..e2fec46 100644 --- a/cvout.cpp +++ b/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; - 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) { 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++; diff --git a/cvout.h b/cvout.h index 346f707..513229b 100644 --- a/cvout.h +++ b/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); diff --git a/example.cfg b/example.cfg index 11d92ed..a6a49bb 100644 --- a/example.cfg +++ b/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 diff --git a/jm2cv.cpp b/jm2cv.cpp index cd84ebc..ee7e304 100644 --- a/jm2cv.cpp +++ b/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 (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; - if (cclsb < -1 || cclsb > 127) continue; - if (mrl < 0 || mrl > (cclsb == -1 ? 127 : 16383)) continue; - if (mru < 0 || mru > (cclsb == -1 ? 127 : 16383)) continue; + 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)); } } } diff --git a/mapping.h b/mapping.h index a6b353e..45f9e64 100644 --- a/mapping.h +++ b/mapping.h @@ -4,16 +4,33 @@ #include #include +enum ControllerType { + TYPE_PB, + TYPE_CC, + TYPE_NRPN, + TYPE_RPN +}; + +template +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); } };