|
- #include <math.h>
- #include <rtosc/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; i<elms; ++i) {
- table[i].ch = RTOSC_INVALID_MIDI;
- table[i].ctl = RTOSC_INVALID_MIDI;
- table[i].path = new char[len];
- table[i].conversion = NULL;
- }
- //TODO initialize all elms
- }
- ~MidiTable_Impl()
- {
- for(unsigned i=0; i<elms; ++i) {
- delete [] table[i].path;
- }
- delete [] table;
- }
-
- MidiAddr *begin(void) {return table;}
- MidiAddr *end(void) {return table + elms;}
-
- unsigned len;
- unsigned elms;
- MidiAddr *table;
- };
-
- //MidiAddr::MidiAddr(void)
- // :ch(RTOSC_INVALID_MIDI),ctl(RTOSC_INVALID_MIDI)
- //{}
-
- static void black_hole3 (const char *, const char *, const char *, int, int)
- {}
- static void black_hole2(const char *a, const char *b)
- {printf("'%s' and '%s'\n", a,b);}
- static void black_hole1(const char *a)
- {printf("'%s'\n", a);}
-
- #define MAX_UNHANDLED_PATH 128
-
- MidiTable::MidiTable(const Ports &_dispatch_root)
- :dispatch_root(_dispatch_root), unhandled_ch(RTOSC_INVALID_MIDI), unhandled_ctl(RTOSC_INVALID_MIDI),
- error_cb(black_hole2), event_cb(black_hole1), modify_cb(black_hole3)
- {
- impl = new MidiTable_Impl(128,128);
- unhandled_path = new char[MAX_UNHANDLED_PATH];
- memset(unhandled_path, 0, MAX_UNHANDLED_PATH);
- }
-
- MidiTable::~MidiTable()
- {
- delete impl;
- delete [] unhandled_path;
- }
-
- bool MidiTable::has(uint8_t ch, uint8_t ctl) const
- {
- for(auto e: *impl) {
- if(e.ch == ch && e.ctl == ctl)
- return true;
- }
- return false;
- }
-
- MidiAddr *MidiTable::get(uint8_t ch, uint8_t ctl)
- {
- for(auto &e: *impl)
- if(e.ch==ch && e.ctl == ctl)
- return &e;
- return NULL;
- }
-
- const MidiAddr *MidiTable::get(uint8_t ch, uint8_t ctl) const
- {
- for(auto &e:*impl)
- if(e.ch==ch && e.ctl == ctl)
- return &e;
- return NULL;
- }
-
- bool MidiTable::mash_port(MidiAddr &e, const Port &port)
- {
- const char *args = strchr(port.name, ':');
- if(!args)
- return false;
-
- //Consider a path to be typed based upon the argument restrictors
- if(strchr(args, 'f')) {
- e.type = 'f';
- e.conversion = port.metadata;
- } else if(strchr(args, 'i'))
- e.type = 'i';
- else if(strchr(args, 'T'))
- e.type = 'T';
- else if(strchr(args, 'c'))
- e.type = 'c';
- else
- return false;
- return true;
- }
-
- void MidiTable::addElm(uint8_t ch, uint8_t ctl, const char *path)
- {
- const Port *port = dispatch_root.apropos(path);
-
- if(!port || port->ports) {//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; i<impl->elms; ++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;
- }
|