#include #include "miditable.h" using namespace rtosc; #define RTOSC_INVALID_MIDI 255 class rtosc::MidiTable_Impl { public: MidiTable_Impl(unsigned len, unsigned elms) :len(len), elms(elms) { table = new MidiAddr[elms]; for(unsigned i=0; iports) {//missing or directory node error_cb("Bad path", path); return; } if(MidiAddr *e = this->get(ch,ctl)) { strncpy(e->path,path,impl->len); if(!mash_port(*e, *port)) { e->ch = RTOSC_INVALID_MIDI; e->ctl = RTOSC_INVALID_MIDI; error_cb("Failed to read metadata", path); } modify_cb("REPLACE", path, e->conversion, (int) ch, (int) ctl); return; } for(MidiAddr &e:*impl) { if(e.ch == RTOSC_INVALID_MIDI) {//free spot e.ch = ch; e.ctl = ctl; strncpy(e.path,path,impl->len); if(!mash_port(e, *port)) { e.ch = RTOSC_INVALID_MIDI; e.ctl = RTOSC_INVALID_MIDI; error_cb("Failed to read metadata", path); } modify_cb("ADD", path, e.conversion, (int) ch, (int) ctl); return; } } } void MidiTable::check_learn(void) { if(unhandled_ctl == RTOSC_INVALID_MIDI || unhandled_path[0] == '\0') return; addElm(unhandled_ch, unhandled_ctl, unhandled_path); unhandled_ch = unhandled_ctl = RTOSC_INVALID_MIDI; memset(unhandled_path, 0, MAX_UNHANDLED_PATH); } void MidiTable::learn(const char *s) { if(strlen(s) > impl->len) { error_cb("String too long", s); return; } clear_entry(s); strncpy(unhandled_path, s, MAX_UNHANDLED_PATH); unhandled_path[MAX_UNHANDLED_PATH-1] = '\0'; check_learn(); } void MidiTable::clear_entry(const char *s) { for(unsigned i=0; ielms; ++i) { if(!strcmp(impl->table[i].path, s)) { //Invalidate impl->table[i].ch = RTOSC_INVALID_MIDI; impl->table[i].ctl = RTOSC_INVALID_MIDI; modify_cb("DEL", s, "", -1, -1); break; } } } void MidiTable::process(uint8_t ch, uint8_t ctl, uint8_t val) { const MidiAddr *addr = get(ch,ctl); if(!addr) { unhandled_ctl = ctl; unhandled_ch = ch; check_learn(); return; } char buffer[1024]; switch(addr->type) { case 'f': rtosc_message(buffer, 1024, addr->path, "f", translate(val,addr->conversion)); break; case 'i': rtosc_message(buffer, 1024, addr->path, "i", val); break; case 'T': rtosc_message(buffer, 1024, addr->path, (val<64 ? "F" : "T")); break; case 'c': rtosc_message(buffer, 1024, addr->path, "c", val); } event_cb(buffer); } Port MidiTable::learnPort(void) { return Port{"learn:s", "", 0, [this](msg_t m, RtData&){ this->learn(rtosc_argument(m,0).s); }}; } Port MidiTable::unlearnPort(void) { return Port{"unlearn:s", "", 0, [this](msg_t m, RtData&){ this->clear_entry(rtosc_argument(m,0).s); }}; } Port MidiTable::registerPort(void) { return Port{"register:iis","", 0, [this](msg_t m,RtData&){ const char *pos = rtosc_argument(m,2).s; while(*pos) putchar(*pos++); this->addElm(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).s);}}; } //TODO generalize to an addScalingFunction() system float MidiTable::translate(uint8_t val, const char *meta_) { //Allow for middle value to be set //TODO consider the centered trait for this op float x = val!=64.0 ? val/127.0 : 0.5; Port::MetaContainer meta(meta_); if(!meta["min"] || !meta["max"] || !meta["scale"]) { fprintf(stderr, "failed to get properties\n"); return 0.0f; } const float min = atof(meta["min"]); const float max = atof(meta["max"]); const char *scale = meta["scale"]; if(!strcmp(scale,"linear")) return x*(max-min)+min; else if(!strcmp(scale,"logarithmic")) { const float b = log(min); const float a = log(max)-b; return expf(a*x+b); } return 0.0f; }