void filter_midi_onechannelfilter(MidiFilter* self, uint32_t tme, const uint8_t* const buffer, uint32_t size) { if (size > 3) { forge_midimessage(self, tme, buffer, size); return; } int chn = buffer[0]&0x0f; switch (buffer[0] & 0xf0) { case MIDI_NOTEOFF: case MIDI_NOTEON: case MIDI_POLYKEYPRESSURE: case MIDI_CONTROLCHANGE: case MIDI_PROGRAMCHANGE: case MIDI_CHANNELPRESSURE: case MIDI_PITCHBEND: if (rintf(*(self->cfg[0])) == chn + 1) { forge_midimessage(self, tme, buffer, size); } break; default: forge_midimessage(self, tme, buffer, size); break; } }
static void filter_preproc_miditranspose(MidiFilter* self) { if (floor(self->lcfg[1]) == floor(*self->cfg[1])) return; const int transp = rint(*(self->cfg[1])); int c,k; uint8_t buf[3]; buf[2] = 0; for (c=0; c < 16; ++c) { #if 0 /* send "all notes off" */ buf[0] = MIDI_CONTROLCHANGE | c; buf[1] = 123; forge_midimessage(self, tme, buf, 3); // send "all sound off" buf[0] = 0xb0 | i; buf[1] = 120; forge_midimessage(self, tme, buf, 3); #else /* re-transpose playing notes */ for (k=0; k < 127; ++k) { if (!self->memCM[c][k]) continue; buf[0] = MIDI_NOTEOFF | c; buf[1] = midi_limit_val(k + self->memCI[c][k]); buf[2] = 0; forge_midimessage(self, 0, buf, 3); buf[0] = MIDI_NOTEON | c; buf[1] = midi_limit_val(k + transp); buf[2] = self->memCM[c][k]; self->memCI[c][k] = transp; forge_midimessage(self, 0, buf, 3); } #endif } }
static void filter_preproc_mapkeyscale(MidiFilter* self) { int i; int identical_cfg = 1; int keymap[12]; for (i=0; i < 12; ++i) { keymap[i] = RAIL(floorf(*self->cfg[i+1]), -13, 12); if (floorf(self->lcfg[i+1]) != floorf(*self->cfg[i+1])) { identical_cfg = 0; } } if (identical_cfg) return; int c,k; uint8_t buf[3]; buf[2] = 0; for (c=0; c < 16; ++c) { for (k=0; k < 127; ++k) { int note; const int n = 1 + k%12; if (!self->memCM[c][k]) continue; if (floorf(self->lcfg[n]) == floorf(*self->cfg[n])) continue; note = k + self->memCI[c][k]; if (midi_valid(note)) { note = midi_limit_val(note); if (self->memCS[c][note] > 0) { self->memCS[c][note]--; if (self->memCS[c][note] == 0) { buf[0] = MIDI_NOTEOFF | c; buf[1] = note; buf[2] = 0; forge_midimessage(self, 0, buf, 3); } } } note = k + keymap[k%12]; if (midi_valid(note)) { note = midi_limit_val(note); buf[0] = MIDI_NOTEON | c; buf[1] = note; buf[2] = self->memCM[c][k]; self->memCI[c][k] = note - k; self->memCS[c][note]++; if (self->memCS[c][note] == 1) { forge_midimessage(self, 0, buf, 3); } } else { self->memCM[c][k] = 0; self->memCI[c][k] = -1000; } } } }
/* ***************************************************************************** * the actual "process" function, called for every midi event */ static void filter_midimessage (MidiMap* self, uint32_t tme, const uint8_t* const mmsg, const uint32_t size) { if (!self->rules || size > MAX_MSG) { /* just foward */ forge_midimessage (self, tme, mmsg, size); return; } bool matched = false; // TODO if "match-all" is set, use tree with masked status-bit as // 1st level hash-table const unsigned int rc = self->rules->count; for (unsigned int i = 0; i < rc; ++i) { Rule *r = &self->rules->rule[i]; uint8_t msg[MAX_MSG]; uint32_t b; if ((r->len != size)) { continue; } bool match = true; for (b = 0; b < size && match; ++b) { // TODO also allow >= or <= comparators if ((mmsg[b] & r->mask[b]) != r->match[b]) { match = false; } } if (!match) { continue; } for (b = 0; b < r->len; ++b) { // TODO also allow += and *= operators // and map bytes ? msg[b] = (mmsg[b] & r->tx_mask[b]) | r->tx_set[b]; } for (;b < r->tx_len; ++b) { msg[b] = r->tx_set[b]; } forge_midimessage (self, tme, msg, r->tx_len); matched = true; if (!self->rules->match_all) { break; } } if (!matched && self->rules->forward_unmatched) { forge_midimessage (self, tme, mmsg, size); } }
static void filter_postproc_midistrum(MidiFilter* self) { int i; const int max_delay = self->memI[0]; const int roff = self->memI[1]; const int woff = self->memI[2]; const uint32_t n_samples = self->n_samples; int skipped = 0; filter_midistrum_process(self, n_samples); for (i=0; i < max_delay; ++i) { const int off = (i + roff) % max_delay; if (self->memQ[off].size > 0) { if (self->memQ[off].reltime < n_samples) { if (self->memQ[off].size == 3 && (self->memQ[off].buf[0] & 0xf0) == MIDI_NOTEON) { const uint8_t chn = self->memQ[off].buf[0] & 0x0f; const uint8_t key = self->memQ[off].buf[1] & 0x7f; self->memCS[chn][key]++; if (self->memCS[chn][key] > 1) { // send a note-off first uint8_t buf[3]; buf[0] = MIDI_NOTEOFF | chn; buf[1] = key; buf[2] = 0; forge_midimessage(self, self->memQ[off].reltime, buf, 3); } forge_midimessage(self, self->memQ[off].reltime, self->memQ[off].buf, self->memQ[off].size); } else if (self->memQ[off].size == 3 && (self->memQ[off].buf[0] & 0xf0) == MIDI_NOTEOFF) { const uint8_t chn = self->memQ[off].buf[0] & 0x0f; const uint8_t key = self->memQ[off].buf[1] & 0x7f; if (self->memCS[chn][key] > 0) { self->memCS[chn][key]--; if (self->memCS[chn][key] == 0) { forge_midimessage(self, self->memQ[off].reltime, self->memQ[off].buf, self->memQ[off].size); } } } else { forge_midimessage(self, self->memQ[off].reltime, self->memQ[off].buf, self->memQ[off].size); } self->memQ[off].size = 0; if (!skipped) self->memI[1] = (self->memI[1] + 1) % max_delay; } else { self->memQ[off].reltime -= n_samples; skipped = 1; } } else if (!skipped) self->memI[1] = off; if (off == woff) break; } self->memI[3] = (self->memI[3] + n_samples)%MSC_MAX; }
static void filter_preproc_keyrange(MidiFilter* self) { if ( floorf(self->lcfg[1]) == floorf(*self->cfg[1]) && floorf(self->lcfg[2]) == floorf(*self->cfg[2]) && floorf(self->lcfg[3]) == floorf(*self->cfg[3]) ) return; int c,k; uint8_t buf[3]; buf[2] = 0; const int mode = RAIL(floorf(*self->cfg[3]),0, 3); const uint8_t low = midi_limit_val(floorf(*self->cfg[1])); const uint8_t upp = midi_limit_val(floorf(*self->cfg[2])); for (c=0; c < 16; ++c) { for (k=0; k < 127; ++k) { const uint8_t vel = self->memCM[c][k]; if (vel == 0) continue; if (mode != 0 && (k >= low && k <= upp) ^ (mode == 2)) continue; buf[0] = MIDI_NOTEOFF | c; buf[1] = midi_limit_val(k + self->memCI[c][k]); forge_midimessage(self, 0, buf, 3); self->memCM[c][k] = 0; } } }
static void filter_postproc_sostenuto(MidiFilter* self) { int i; const int max_delay = self->memI[0]; const int roff = self->memI[1]; const int woff = self->memI[2]; uint32_t n_samples = self->n_samples; int skipped = 0; /* only process until given time */ if (self->memI[3] > 0) n_samples = MIN(self->memI[3], n_samples); for (i=0; i < max_delay; ++i) { const int off = (i + roff) % max_delay; if (self->memQ[off].size > 0) { if (self->memQ[off].reltime < n_samples) { forge_midimessage(self, self->memQ[off].reltime, self->memQ[off].buf, self->memQ[off].size); self->memQ[off].size = 0; if (!skipped) self->memI[1] = (self->memI[1] + 1) % max_delay; } else { /* don't decrement time if called from filter_midi_sostenuto() */ if (self->memI[3] < 0) {self->memQ[off].reltime -= n_samples;} skipped = 1; } } else if (!skipped) self->memI[1] = off; if (off == woff) break; } }
static void filter_preproc_enforcescale(MidiFilter* self) { if (floorf(self->lcfg[1]) == floorf(*self->cfg[1])) return; const int scale = RAIL(floorf(*self->cfg[1]), 0, 11); int c,k; uint8_t buf[3]; buf[2] = 0; for (c=0; c < 16; ++c) { for (k=0; k < 127; ++k) { if (self->memCS[c][k] ==0) continue; if (filter_enforcescale_check(scale, k)) { self->memCI[c][k] = 0; continue; } buf[0] = MIDI_NOTEOFF | c; buf[1] = midi_limit_val(k); buf[2] = 0; forge_midimessage(self, 0, buf, 3); self->memCS[c][k] = 0; self->memCI[c][k] = 0; } } }
static inline void filter_midistrum_panic(MidiFilter* self, const uint8_t c, const uint32_t tme) { int i,k; const int max_delay = self->memI[0]; for (i=0; i < max_delay; ++i) { if (self->memQ[i].size == 3 && (self->memQ[i].buf[0] & 0xf0) != 0xf0 && (self->memQ[i].buf[0] & 0x0f) != c) continue; self->memQ[i].size = 0; } self->memI[4] = 0; // collection stattime self->memI[5] = 0; // collected notes self->memI[6] = 0; // stroke direction for (k=0; k < 127; ++k) { if (self->memCS[c][k]) { uint8_t buf[3]; buf[0] = MIDI_NOTEOFF | c; buf[1] = k; buf[2] = 0; forge_midimessage(self, tme, buf, 3); } self->memCS[c][k] = 0; // count note-on per key } }
static void filter_midi_keyrange(MidiFilter* self, uint32_t tme, const uint8_t* const buffer, uint32_t size) { const int mode = RAIL(floorf(*self->cfg[3]),0, 3); const uint8_t chs = midi_limit_chn(floorf(*self->cfg[0]) -1); const uint8_t chn = buffer[0] & 0x0f; uint8_t mst = buffer[0] & 0xf0; if (size != 3 || !(mst == MIDI_NOTEON || mst == MIDI_NOTEOFF) || !(floorf(*self->cfg[0]) == 0 || chs == chn) || mode == 0 ) { forge_midimessage(self, tme, buffer, size); return; } const uint8_t low = midi_limit_val(floorf(*self->cfg[1])); const uint8_t upp = midi_limit_val(floorf(*self->cfg[2])); const uint8_t key = buffer[1] & 0x7f; const uint8_t vel = buffer[2] & 0x7f; if (mst == MIDI_NOTEON && vel ==0 ) { mst = MIDI_NOTEOFF; } switch(mst) { case MIDI_NOTEON: if ((key >= low && key <= upp) ^ (mode == 2)) { forge_midimessage(self, tme, buffer, size); self->memCM[chn][key] = vel; } break; case MIDI_NOTEOFF: if (self->memCM[chn][key] > 0) { forge_midimessage(self, tme, buffer, size); } self->memCM[chn][key] = 0; break; } }
static inline void filter_midichord_noteon(MidiFilter* self, uint32_t tme, uint8_t chn, int note, uint8_t vel) { uint8_t buf[3]; if (!midi_valid(note)) return; buf[0] = MIDI_NOTEON | chn; buf[1] = note; buf[2] = vel; self->memCS[chn][note]++; if (self->memCS[chn][note] == 1) { forge_midimessage(self, tme, buf, 3); } }
static inline void filter_enforcescale_panic(MidiFilter* self, const uint8_t c, const uint32_t tme) { int k; for (k=0; k < 127; ++k) { if (self->memCS[c][k] > 0) { uint8_t buf[3]; buf[0] = MIDI_NOTEOFF | c; buf[1] = k; buf[2] = 0; forge_midimessage(self, tme, buf, 3); } self->memCI[c][k] = 0; // current key transpose self->memCS[c][k] = 0; // count note-on per key } }
static inline void filter_midichord_panic(MidiFilter* self, const uint8_t c, const uint32_t tme) { int k; for (k=0; k < 127; ++k) { if (self->memCS[c][k] > 0) { uint8_t buf[3]; buf[0] = MIDI_NOTEOFF | c; buf[1] = k; buf[2] = 0; forge_midimessage(self, tme, buf, 3); } self->memCI[c][k] = -1000; // current chord for this key self->memCS[c][k] = 0; // count note-on per key self->memCM[c][k] = 0; // last known velocity for this key } }
static void mctl_cb(int fnid, const char *fn, unsigned char val, midiCCmap *mm, void *arg) { B3S* b3s = (B3S*)arg; #ifdef DEBUGPRINT fprintf(stderr, "xfn: %d (\"%s\", %d) mm:%s\n", fnid, fn, val, mm?"yes":"no"); #endif if (b3s->midiout && mm) { while (mm) { #ifdef DEBUGPRINT fprintf(stderr, "MIDI FEEDBACK %d %d %d\n", mm->channel, mm->param, val); #endif uint8_t msg[3]; msg[0] = 0xb0 | (mm->channel&0x0f); // Control Change msg[1] = mm->param; msg[2] = val; forge_midimessage(&b3s->forge, &b3s->uris, msg, 3); mm = mm->next; } } if (b3s->midiout && fn && !b3s->suspend_ui_msg) { forge_kvcontrolmessage(&b3s->forge, &b3s->uris, fn, (int32_t) val); } }
static inline void filter_ntapdelay_panic(MidiFilter* self, const uint8_t c, const uint32_t tme) { int i,k; const int max_delay = self->memI[0]; for (i=0; i < max_delay; ++i) { if (self->memQ[i].size == 3 && (self->memQ[i].buf[0] & 0xf0) != 0xf0 && (self->memQ[i].buf[0] & 0x0f) != c) continue; self->memQ[i].size = 0; } for (k=0; k < 127; ++k) { if (self->memCS[c][k]) { uint8_t buf[3]; buf[0] = MIDI_NOTEOFF | c; buf[1] = k; buf[2] = 0; forge_midimessage(self, tme, buf, 3); } self->memCS[c][k] = 0; // count note-on per key post-delay self->memCM[c][k] = 0; // remember velocity of note-on self->memCI[c][k] = -1; // remember time of note-on } }
static void filter_midi_enforcescale(MidiFilter* self, uint32_t tme, const uint8_t* const buffer, uint32_t size) { const int chs = midi_limit_chn(floorf(*self->cfg[0]) -1); const int scale = RAIL(floorf(*self->cfg[1]), 0, 11); const int mode = RAIL(floorf(*self->cfg[2]), 0, 2); const uint8_t chn = buffer[0] & 0x0f; const uint8_t key = buffer[1] & 0x7f; uint8_t mst = buffer[0] & 0xf0; if (midi_is_panic(buffer, size)) { filter_enforcescale_panic(self, chn, tme); } if (size != 3 || !(mst == MIDI_NOTEON || mst == MIDI_NOTEOFF || mst == MIDI_POLYKEYPRESSURE) || !(floorf(*self->cfg[0]) == 0 || chs == chn) ) { forge_midimessage(self, tme, buffer, size); return; } int transp = 0; if (!filter_enforcescale_check(scale, key)) { switch (mode) { case 1: transp = -1; break; case 2: transp = +1; break; case 0: /* discard */ default: return; } } if (!midi_valid(key + transp)) { return; } if (!filter_enforcescale_check(scale, key + transp)) { return; } int note; uint8_t buf[3]; memcpy(buf, buffer, 3); switch (mst) { case MIDI_NOTEON: note = key + transp; if (midi_valid(note)) { buf[1] = note; self->memCS[chn][note]++; if (self->memCS[chn][note] == 1) forge_midimessage(self, tme, buf, size); } self->memCI[chn][key] = transp; break; case MIDI_NOTEOFF: note = key + self->memCI[chn][key]; if (midi_valid(note)) { buf[1] = note; if (self->memCS[chn][note] > 0) { self->memCS[chn][note]--; if (self->memCS[chn][note] == 0) forge_midimessage(self, tme, buf, size); self->memCI[chn][key] = 0; } } break; case MIDI_POLYKEYPRESSURE: note = key + transp; if (midi_valid(note)) { buf[1] = note; forge_midimessage(self, tme, buf, size); } break; } }
void filter_midi_ntabdelay(MidiFilter* self, const uint32_t tme, const uint8_t* const buffer, const uint32_t size) { int i; float bpm = MAX(*self->cfg[2], 1.0); if (*self->cfg[1] && (self->available_info & NFO_BPM)) { bpm = self->bpm; } if (bpm <= 0) bpm = 60; if (midi_is_panic(buffer, size)) { filter_ntapdelay_panic(self, buffer[0]&0x0f, tme); } forge_midimessage(self, tme, buffer, size); const uint8_t chs = midi_limit_chn(floor(*self->cfg[0]) -1); const uint8_t chn = buffer[0] & 0x0f; uint8_t mst = buffer[0] & 0xf0; if (size != 3 || !(mst == MIDI_NOTEON || mst == MIDI_NOTEOFF || mst == MIDI_POLYKEYPRESSURE) || !(floor(*self->cfg[0]) == 0 || chs == chn) ) { return; } if ((self->memI[2] + 1) % self->memI[0] == self->memI[1]) { return; } const float grid = RAIL((*self->cfg[3]), 1/256.0, 4.0); const double samples_per_beat = 60.0 / bpm * self->samplerate; const uint8_t key = buffer[1] & 0x7f; const uint8_t vel = buffer[2] & 0x7f; uint8_t buf[3]; memcpy(buf, buffer, 3); /* treat note-on w/velocity==0 as note-off */ if (mst == MIDI_NOTEON && vel == 0) { mst = MIDI_NOTEOFF; buf[0] = MIDI_NOTEOFF | chn; } if (mst == MIDI_NOTEON) { self->memCI[chn][key] = tme + rint(grid * samples_per_beat); self->memCM[chn][key] = vel; } else if (mst == MIDI_NOTEOFF) { self->memCI[chn][key] = -1; self->memCM[chn][key] = 0; } for (i=0; i < RAIL(*self->cfg[4], 0, 128); ++i) { int delay = rint( grid * samples_per_beat * (i+1.0)); buf[2] = RAIL(rint(vel + (i+1.0) * (*self->cfg[5])), 1, 127); MidiEventQueue *qm = &(self->memQ[self->memI[2]]); memcpy(qm->buf, buf, 3); qm->size = size; qm->reltime = tme + delay; self->memI[2] = (self->memI[2] + 1) % self->memI[0]; if ((self->memI[2] + 1) % self->memI[0] == self->memI[1]) { return; } } }
void filter_midi_midistrum(MidiFilter* self, const uint32_t tme, const uint8_t* const buffer, const uint32_t size) { uint8_t mst = buffer[0] & 0xf0; if (size > 3) { forge_midimessage(self, tme, buffer, size); return; } if (midi_is_panic(buffer, size)) { filter_midistrum_panic(self, buffer[0]&0x0f, tme); } if (size != 3 || !(mst == MIDI_NOTEON || mst == MIDI_NOTEOFF)) { if ((self->memI[2] + 1) % self->memI[0] == self->memI[1]) { return; // queue full } MidiEventQueue *qm = &(self->memQ[self->memI[2]]); memcpy(qm->buf, buffer, size); qm->size = size; qm->reltime = tme; self->memI[2] = (self->memI[2] + 1) % self->memI[0]; return; } float bpm = (*self->cfg[1]); if (*self->cfg[0] && (self->available_info & NFO_BPM)) { bpm = self->bpm; } if (bpm <= 0) bpm = 60; const int strum_time = floor(self->samplerate * (*self->cfg[4]) * 60.0 / bpm); const int max_collect = 1 + rintf(self->samplerate * (*self->cfg[3]) / 1000.0); const uint8_t key = buffer[1] & 0x7f; const uint8_t vel = buffer[2] & 0x7f; if (mst == MIDI_NOTEON && vel ==0 ) { mst = MIDI_NOTEOFF; } // check if we're overdue -> process collected notes, reset collection mode filter_midistrum_process(self, tme); /* add note-ons to collection */ if (mst == MIDI_NOTEON) { int i; if (self->memI[5] == 0) { self->memI[4] = (tme + max_collect + self->memI[3]) % MSC_MAX; } #if 0 if ((self->available_info & NFO_BEAT)) { const double samples_per_beat = 60.0 / self->bpm * self->samplerate; printf("NOTE ON: %ld, %f || %f\n", self->pos_frame + tme, self->bar_beats + (float)tme / samples_per_beat, self->beat_beats + (float)tme / samples_per_beat); } #endif // check if note-on for this key is already queued -> skip for (i=0; i < self->memI[5]; ++i) { if (self->memS[i].size == 3 && self->memS[i].buf[2] == key) { return; } } MidiEventQueue *qm = &(self->memS[self->memI[5]]); memcpy(qm->buf, buffer, size); qm->size = size; self->memI[5]++; } /* delay note-off by max-latency (= collection-time + strum-time) */ else if (mst == MIDI_NOTEOFF) { #if 0 // TODO -- shorten delay time IF possible int delay = strum_time + 1; if (self->memI[5] > 0) { delay += 1 + MSC_DIFF(self->memI[4], (self->memI[3] + tme)%MSC_MAX); printf("add.. %d\n" , MSC_DIFF(self->memI[4], (self->memI[3] + tme)%MSC_MAX)); } #else const int delay = strum_time + max_collect; #endif // TODO filter out ignored dups from (ignored note-on) above MidiEventQueue *qm = &(self->memQ[self->memI[2]]); memcpy(qm->buf, buffer, size); qm->size = size; qm->reltime = tme + delay; self->memI[2] = (self->memI[2] + 1) % self->memI[0]; } }
static void filter_midi_miditranspose(MidiFilter* self, uint32_t tme, const uint8_t* const buffer, uint32_t size) { const int chs = midi_limit_chn(floor(*self->cfg[0]) -1); const int transp = rint(*(self->cfg[1])); const uint8_t chn = buffer[0] & 0x0f; const uint8_t key = buffer[1] & 0x7f; const uint8_t vel = buffer[2] & 0x7f; uint8_t mst = buffer[0] & 0xf0; if (size != 3 || !(mst == MIDI_NOTEON || mst == MIDI_NOTEOFF || mst == MIDI_POLYKEYPRESSURE) || !(floor(*self->cfg[0]) == 0 || chs == chn) ) { forge_midimessage(self, tme, buffer, size); return; } if (mst == MIDI_NOTEON && vel ==0 ) { mst = MIDI_NOTEOFF; } int note; uint8_t buf[3]; buf[0] = buffer[0]; buf[1] = buffer[1]; buf[2] = buffer[2]; switch (mst) { case MIDI_NOTEON: note = key + transp; if (midi_valid(note)) { buf[1] = note; forge_midimessage(self, tme, buf, size); } self->memCM[chn][key] = vel; self->memCI[chn][key] = transp; break; case MIDI_NOTEOFF: note = key + self->memCI[chn][key]; if (midi_valid(note)) { buf[1] = note; forge_midimessage(self, tme, buf, size); } self->memCM[chn][key] = 0; self->memCI[chn][key] = -1000; break; case MIDI_POLYKEYPRESSURE: note = key + transp; if (midi_valid(note)) { buf[1] = note; forge_midimessage(self, tme, buf, size); } break; } }
static void filter_midi_mapkeyscale(MidiFilter* self, uint32_t tme, const uint8_t* const buffer, uint32_t size) { int i; const int chs = midi_limit_chn(floorf(*self->cfg[0]) -1); int keymap[12]; for (i=0; i < 12; ++i) { keymap[i] = RAIL(floorf(*self->cfg[i+1]), -13, 12); } const uint8_t chn = buffer[0] & 0x0f; uint8_t mst = buffer[0] & 0xf0; if (midi_is_panic(buffer, size)) { filter_mapkeyscale_panic(self, chn, tme); } if (size != 3 || !(mst == MIDI_NOTEON || mst == MIDI_NOTEOFF || mst == MIDI_POLYKEYPRESSURE) || !(floorf(*self->cfg[0]) == 0 || chs == chn) ) { forge_midimessage(self, tme, buffer, size); return; } const uint8_t key = buffer[1] & 0x7f; const uint8_t vel = buffer[2] & 0x7f; if (mst == MIDI_NOTEON && vel ==0 ) { mst = MIDI_NOTEOFF; } int note; uint8_t buf[3]; memcpy(buf, buffer, 3); switch (mst) { case MIDI_NOTEON: if (keymap[key%12] < -12) return; note = key + keymap[key%12]; // TODO keep track of dup result note-on -- see enforcescale.c if (midi_valid(note)) { buf[1] = note; self->memCS[chn][note]++; if (self->memCS[chn][note] == 1) { forge_midimessage(self, tme, buf, size); } self->memCM[chn][key] = vel; self->memCI[chn][key] = note - key; } break; case MIDI_NOTEOFF: note = key + self->memCI[chn][key]; if (midi_valid(note)) { buf[1] = note; if (self->memCS[chn][note] > 0) { self->memCS[chn][note]--; if (self->memCS[chn][note] == 0) forge_midimessage(self, tme, buf, size); } } self->memCM[chn][key] = 0; self->memCI[chn][key] = -1000; break; case MIDI_POLYKEYPRESSURE: if (keymap[key%12] < -12) return; note = key + keymap[key%12]; if (midi_valid(note)) { buf[1] = note; forge_midimessage(self, tme, buf, size); } break; } }
static void filter_midi_midichord(MidiFilter* self, uint32_t tme, const uint8_t* const buffer, uint32_t size) { int i; const int chs = midi_limit_chn(floor(*self->cfg[0]) -1); const int scale = RAIL(floor(*self->cfg[1]), 0, 11); int chord = 0; for (i=0; i < 10 ; ++i) { if ((*self->cfg[i+2]) != 0) chord |= 1<<i; } const uint8_t chn = buffer[0] & 0x0f; uint8_t mst = buffer[0] & 0xf0; if (midi_is_panic(buffer, size)) { filter_midichord_panic(self, chn, tme); } if (size != 3 || !(mst == MIDI_NOTEON || mst == MIDI_NOTEOFF || mst == MIDI_POLYKEYPRESSURE) || !(floor(*self->cfg[0]) == 0 || chs == chn) ) { forge_midimessage(self, tme, buffer, size); return; } const uint8_t key = buffer[1] & 0x7f; const uint8_t vel = buffer[2] & 0x7f; const int tonika = (key + 12 - scale) % 12; if (! filter_midichord_isonscale(tonika)) { chord = 1; } switch (mst) { case MIDI_NOTEON: self->memCI[chn][key] = chord; self->memCM[chn][key] = vel; for (i=0; i < 10 ; ++i) { if (!(chord & (1<<i))) continue; filter_midichord_noteon(self, tme, chn, key + filter_midichord_halftoneoffset(tonika, i), vel); } break; case MIDI_NOTEOFF: chord = self->memCI[chn][key]; for (i=0; i < 10 ; ++i) { if (!(chord & (1<<i))) continue; filter_midichord_noteoff(self, tme, chn, key + filter_midichord_halftoneoffset(tonika, i), vel); } self->memCI[chn][key] = -1000; self->memCM[chn][key] = 0; break; case MIDI_POLYKEYPRESSURE: for (i=0; i < 10 ; ++i) { uint8_t buf[3]; if (!(chord & (1<<i))) continue; int note = key + filter_midichord_halftoneoffset(tonika, i); if (midi_valid(note)) { buf[0] = buffer[0]; buf[1] = note; buf[2] = buffer[2]; forge_midimessage(self, tme, buf, size); } } break; } }
void filter_midi_sostenuto(MidiFilter* self, const uint32_t tme, const uint8_t* const buffer, const uint32_t size) { const uint8_t chs = midi_limit_chn(floorf(*self->cfg[0]) -1); const uint32_t delay = floor(self->samplerate * RAIL((*self->cfg[1]), 0, 120)); const int pedal = RAIL(*self->cfg[2], 0, 2); int state; const uint8_t chn = buffer[0] & 0x0f; const uint8_t vel = buffer[2] & 0x7f; uint8_t mst = buffer[0] & 0xf0; /* pedal CC 64 -- TODO make CC (and theshold) configurable !? */ if (size == 3 && mst == MIDI_CONTROLCHANGE && (buffer[1]) == 64) { self->memI[16 + chn] = vel > 63 ? 1 : 0; } if (size != 3 || !(mst == MIDI_NOTEON || mst == MIDI_NOTEOFF) || !(floorf(*self->cfg[0]) == 0 || chs == chn) ) { forge_midimessage(self, tme, buffer, size); return; } switch(pedal) { default: case 0: state = 0; break; case 1: state = 1; break; case 2: state = self->memI[16 + chn]; break; } if (mst == MIDI_NOTEON && vel ==0 ) { mst = MIDI_NOTEOFF; } if (size == 3 && mst == MIDI_NOTEON && state == 1) { const uint8_t chn = buffer[0] & 0x0f; const uint8_t key = buffer[1] & 0x7f; if (sostenuto_check_dup(self, chn, key, -1)) { /* note was already on, * send note off + immediate note on, again */ uint8_t buf[3]; buf[0] = MIDI_NOTEOFF | chn; buf[1] = key; buf[2] = 0; forge_midimessage(self, tme, buf, size); } forge_midimessage(self, tme, buffer, size); } else if (size == 3 && mst == MIDI_NOTEOFF && state == 1) { const uint8_t chn = buffer[0] & 0x0f; const uint8_t key = buffer[1] & 0x7f; if (!sostenuto_check_dup(self, chn, key, tme + delay)) { // queue note-off if not already queued MidiEventQueue *qm = &(self->memQ[self->memI[2]]); memcpy(qm->buf, buffer, size); qm->size = size; qm->reltime = tme + delay; self->memI[2] = (self->memI[2] + 1) % self->memI[0]; } } else { forge_midimessage(self, tme, buffer, size); } /* only note-off are queued in the buffer. * Interleave delay-buffer with messages sent in-process above * to retain sequential order. */ self->memI[3] = tme + 1; filter_postproc_sostenuto(self); self->memI[3] = -1; }
static void filter_postproc_ntabdelay(MidiFilter* self) { int i,c,k; const int max_delay = self->memI[0]; const int roff = self->memI[1]; const int woff = self->memI[2]; const uint32_t n_samples = self->n_samples; int skipped = 0; float bpm = MAX(*self->cfg[2], 1.0); if (*self->cfg[1] && (self->available_info & NFO_BPM)) { bpm = self->bpm; } if (bpm <= 0) bpm = 60; const float grid = RAIL((*self->cfg[3]), 1/256.0, 4.0); const double samples_per_beat = 60.0 / bpm * self->samplerate; /* add note-on/off for held notes.. * TODO: use a preallocated stack-like structure */ if (*self->cfg[4] == 0) for (c=0; c < 16; ++c) for (k=0; k < 127; ++k) { if (self->memCM[c][k] == 0) continue; if (self->memCI[c][k] >= n_samples) { self->memCI[c][k] -= n_samples; continue; } while (1) { self->memCM[c][k] = RAIL(rint(self->memCM[c][k] + (*self->cfg[5])), 1, 127); /* enqueue note-off + note-on */ MidiEventQueue *qm = &(self->memQ[self->memI[2]]); qm->buf[0] = MIDI_NOTEOFF | c; qm->buf[1] = k; qm->buf[2] = 0; qm->size = 3; qm->reltime = self->memCI[c][k]; self->memI[2] = (self->memI[2] + 1) % self->memI[0]; if ((self->memI[2] + 1) % self->memI[0] == self->memI[1]) { self->memCI[c][k] += ceil(grid * samples_per_beat); break; } qm = &(self->memQ[self->memI[2]]); qm->buf[0] = MIDI_NOTEON | c; qm->buf[1] = k; qm->buf[2] = self->memCM[c][k]; qm->size = 3; qm->reltime = self->memCI[c][k]; self->memI[2] = (self->memI[2] + 1) % self->memI[0]; if ((self->memI[2] + 1) % self->memI[0] == self->memI[1]) { self->memCI[c][k] += ceil(grid * samples_per_beat); break; } self->memCI[c][k] += ceil(grid * samples_per_beat); if (self->memCI[c][k] >= n_samples) { break; } } self->memCI[c][k] -= n_samples; } /* dequeue delayline */ for (i=0; i < max_delay; ++i) { const int off = (i + roff) % max_delay; if (self->memQ[off].size > 0) { if (self->memQ[off].reltime < n_samples) { if (self->memQ[off].size == 3 && (self->memQ[off].buf[0] & 0xf0) == MIDI_NOTEON) { const uint8_t chn = self->memQ[off].buf[0] & 0x0f; const uint8_t key = self->memQ[off].buf[1] & 0x7f; self->memCS[chn][key]++; if (self->memCS[chn][key] > 1) { // send a note-off first uint8_t buf[3]; buf[0] = MIDI_NOTEOFF | chn; buf[1] = key; buf[2] = 0; forge_midimessage(self, self->memQ[off].reltime, buf, 3); } forge_midimessage(self, self->memQ[off].reltime, self->memQ[off].buf, self->memQ[off].size); } else if (self->memQ[off].size == 3 && (self->memQ[off].buf[0] & 0xf0) == MIDI_NOTEOFF) { const uint8_t chn = self->memQ[off].buf[0] & 0x0f; const uint8_t key = self->memQ[off].buf[1] & 0x7f; if (self->memCS[chn][key] > 0) { self->memCS[chn][key]--; if (self->memCS[chn][key] == 0) { forge_midimessage(self, self->memQ[off].reltime, self->memQ[off].buf, self->memQ[off].size); } } } else { forge_midimessage(self, self->memQ[off].reltime, self->memQ[off].buf, self->memQ[off].size); } self->memQ[off].size = 0; if (!skipped) self->memI[1] = (self->memI[1] + 1) % max_delay; } else { self->memQ[off].reltime -= n_samples; skipped = 1; } } else if (!skipped) self->memI[1] = off; if (off == woff) break; } }