From bb953069475b834a4b07f979e21b4ce1f746430d Mon Sep 17 00:00:00 2001 From: Hanspeter Portner Date: Tue, 15 Dec 2015 11:43:10 +0100 Subject: [PATCH] implement MIDI MPE mode in oscmidi engine. --- cmc/cmc.c | 33 +++++++++++++--- config/config.c | 1 + engines/oscmidi.h | 2 +- include/cmc.h | 4 +- include/config.h | 1 + include/midi.h | 35 +++++++++++++++-- midi/midi.c | 99 +++++++++++++++++++++++++++++++++++++++++++---- oscmidi/oscmidi.c | 98 +++++++++++++++++++++++++++++++++++++++++----- sensors/sensors.c | 16 +++++--- 9 files changed, 255 insertions(+), 34 deletions(-) diff --git a/cmc/cmc.c b/cmc/cmc.c index f943767..291df65 100644 --- a/cmc/cmc.c +++ b/cmc/cmc.c @@ -40,6 +40,7 @@ CMC_Engine *engines [ENGINE_MAX+1]; uint_fast8_t cmc_engines_active = 0; CMC_Group *cmc_groups = config.groups; +uint16_t cmc_groups_n = GROUP_MAX; #ifdef BENCHMARK Stop_Watch sw_engine_process = {.id = "engine_process", .thresh=3000}; @@ -75,8 +76,6 @@ static uint8_t va[SENSOR_N+2]; static CMC_Blob blobs[2][BLOB_MAX]; static uint8_t pacemaker = 0x0b; // pacemaker rate 2^11=2048 -static void cmc_engines_init(void); - void cmc_velocity_stiffness_update(uint8_t stiffness) { @@ -111,8 +110,8 @@ cmc_init(void) cmc_old = blobs[old]; cmc_neu = blobs[neu]; - // initialize output engines - cmc_engines_init(); + // update group and initialize output engines + cmc_group_update(); // update engines stack cmc_engines_update(); @@ -775,7 +774,7 @@ cmc_process(OSC_Timetag now, OSC_Timetag offset, int16_t *rela, osc_data_t *buf, } void -cmc_group_clear(void) +cmc_group_reset(void) { uint16_t gid; for(gid=0; gidgid = gid; - grp->pid = CMC_BOTH; + grp->pid = 0; grp->x0 = 0.0; grp->x1 = 1.0; grp->m = CMC_NOSCALE; } + + // define two groups by default + cmc_groups[0].pid = CMC_SOUTH; + cmc_groups[1].pid = CMC_NORTH; + + cmc_group_update(); } static void @@ -812,6 +817,22 @@ cmc_engines_init(void) custom_engine.init_cb(); } +void +cmc_group_update(void) +{ + uint16_t gid; + for(gid=0; gid inline void -midi_add_key(MIDI_Hash *hash, uint32_t sid, uint8_t key) +midi_add_key(MIDI_Hash *hash, uint32_t sid, uint8_t key, uint8_t cha) { uint_fast8_t k; for(k=0; k ZONE_MAX + int8_t rem = CHAN_MAX % n_zones; + const uint8_t span = (CHAN_MAX - rem) / n_zones - 1; + uint8_t ptr = 0; + + mpe->n_zones = n_zones; + zone_t *zones = mpe->zones; + int8_t *channels = mpe->channels; + + for(uint8_t i=0; + i 0) + zones[i].span += 1; + } + + for(uint8_t i=0; in_zones; // wrap around if zone_idx > n_zones + zone_t *zone = &mpe->zones[zone_idx]; + int8_t *channels = mpe->channels; + + int8_t min = INT8_MAX; + uint8_t pos = zone->ref; // start search at current channel + const uint8_t base_1 = zone->base + 1; + for(uint8_t i = zone->ref; i < zone->ref + zone->span; i++) + { + const uint8_t ch = base_1 + (i % zone->span); // wrap to [0..span] + if(channels[ch] < min) // check for less occupation + { + min = channels[ch]; // lower minimum + pos = i; // set new minimally occupied channel } - return 0; // not found + } + + const uint8_t ch = base_1 + (pos % zone->span); // wrap to [0..span] + if(channels[ch] <= 0) // off since long + channels[ch] = 1; + else + channels[ch] += 1; // increase occupation + zone->ref = (pos + 1) % zone->span; // start next search from next channel + + return ch; +} + +inline void +mpe_release(mpe_t *mpe, uint8_t zone_idx, uint8_t ch) +{ + zone_idx %= mpe->n_zones; // wrap around if zone_idx > n_zones + ch %= CHAN_MAX; // wrap around if ch > CHAN_MAX + zone_t *zone = &mpe->zones[zone_idx]; + int8_t *channels = mpe->channels; + + const uint8_t base_1 = zone->base + 1; + for(uint8_t i = base_1; i < base_1 + zone->span; i++) + { + if( (i == ch) || (channels[i] <= 0) ) + channels[i] -= 1; + // do not decrease occupied channels + } } diff --git a/oscmidi/oscmidi.c b/oscmidi/oscmidi.c index 824f47c..51e6852 100644 --- a/oscmidi/oscmidi.c +++ b/oscmidi/oscmidi.c @@ -45,17 +45,31 @@ static const char *oscmidi_fmt_1 [] = { }; static MIDI_Hash oscmidi_hash [BLOB_MAX]; +static mpe_t mpe; static osc_data_t *pack; static osc_data_t *bndl; +static uint_fast8_t update_zones = 0; + static void oscmidi_init(void) { uint_fast8_t i; OSC_MIDI_Group *group = config.oscmidi_groups; for(i=0; irange; + { + if(config.oscmidi.mpe) + mul[i] = (float)0x1fff / ceil(group->range); //MPE only supports whole seminote ranges + else + mul[i] = (float)0x1fff / group->range; + } + + // populate mpe struct + mpe_populate(&mpe, cmc_groups_n); + + // only update zones when mpe is activated + update_zones = config.oscmidi.mpe; } static osc_data_t * @@ -109,6 +123,41 @@ oscmidi_engine_frame_cb(osc_data_t *buf, osc_data_t *end, CMC_Frame_Event *fev) buf_ptr = osc_start_bundle_item(buf_ptr, end, &pack); buf_ptr = osc_start_bundle(buf_ptr, end, fev->offset, &bndl); + if(update_zones) + { + OSC_MIDI_Format format = config.oscmidi.format; + uint_fast8_t multi = config.oscmidi.multi; + osc_data_t *itm = NULL; + + for(uint8_t z=0; zbase, MIDI_STATUS_CONTROL_CHANGE, MIDI_CONTROLLER_RPN_LSB, 0x6); + buf_ptr = oscmidi_serialize(buf_ptr, end, format, zone->base, MIDI_STATUS_CONTROL_CHANGE, MIDI_CONTROLLER_RPN_MSB, 0x0); + buf_ptr = oscmidi_serialize(buf_ptr, end, format, zone->base, MIDI_STATUS_CONTROL_CHANGE, MIDI_CONTROLLER_DATA_ENTRY, zone->span); + + // define zone bend range + buf_ptr = oscmidi_serialize(buf_ptr, end, format, zone->base+1, MIDI_STATUS_CONTROL_CHANGE, MIDI_CONTROLLER_RPN_LSB, 0x0); + buf_ptr = oscmidi_serialize(buf_ptr, end, format, zone->base+1, MIDI_STATUS_CONTROL_CHANGE, MIDI_CONTROLLER_RPN_MSB, 0x0); + const uint8_t semitone_range = ceil(oscmidi_groups[z].range); + buf_ptr = oscmidi_serialize(buf_ptr, end, format, zone->base+1, MIDI_STATUS_CONTROL_CHANGE, MIDI_CONTROLLER_DATA_ENTRY, semitone_range); + + if(multi) + buf_ptr = osc_end_bundle_item(buf_ptr, end, itm); + } + + update_zones = 0; + } + return buf_ptr; } @@ -132,6 +181,7 @@ oscmidi_engine_on_cb(osc_data_t *buf, osc_data_t *end, CMC_Blob_Event *bev) osc_data_t *itm = NULL; OSC_MIDI_Format format = config.oscmidi.format; uint_fast8_t multi = config.oscmidi.multi; + uint_fast8_t use_mpe = config.oscmidi.mpe; OSC_MIDI_Group *group = &oscmidi_groups[bev->gid]; OSC_MIDI_Mapping mapping = group->mapping; @@ -145,10 +195,14 @@ oscmidi_engine_on_cb(osc_data_t *buf, osc_data_t *end, CMC_Blob_Event *bev) buf_ptr = osc_set_fmt(buf_ptr, end, oscmidi_fmt_3[format]); } - uint8_t ch = bev->gid % 0xf; + uint8_t ch; + if(use_mpe) + ch = mpe_acquire(&mpe, bev->gid); + else + ch = bev->gid; float X = group->offset + bev->x*group->range; uint8_t key = floor(X); - midi_add_key(oscmidi_hash, bev->sid, key); + midi_add_key(oscmidi_hash, bev->sid, key, ch); uint16_t bend =(X - key)*mul[bev->gid] + 0x1fff; uint16_t eff = bev->y * 0x3fff; @@ -184,6 +238,7 @@ oscmidi_engine_off_cb(osc_data_t *buf, osc_data_t *end, CMC_Blob_Event *bev) osc_data_t *itm = NULL; OSC_MIDI_Format format = config.oscmidi.format; uint_fast8_t multi = config.oscmidi.multi; + uint_fast8_t use_mpe = config.oscmidi.mpe; if(multi) { @@ -192,8 +247,11 @@ oscmidi_engine_off_cb(osc_data_t *buf, osc_data_t *end, CMC_Blob_Event *bev) buf_ptr = osc_set_fmt(buf_ptr, end, oscmidi_fmt_1[format]); } - uint8_t ch = bev->gid % 0xf; - uint8_t key = midi_rem_key(oscmidi_hash, bev->sid); + uint8_t key; + uint8_t ch; + midi_rem_key(oscmidi_hash, bev->sid, &key, &ch); + if(use_mpe) + mpe_release(&mpe, bev->gid, ch); // serialize buf_ptr = oscmidi_serialize(buf_ptr, end, format, ch, MIDI_STATUS_NOTE_OFF, key, 0x7f); @@ -224,9 +282,10 @@ oscmidi_engine_set_cb(osc_data_t *buf, osc_data_t *end, CMC_Blob_Event *bev) buf_ptr = osc_set_fmt(buf_ptr, end, oscmidi_fmt_2[format]); } - uint8_t ch = bev->gid % 0xf; float X = group->offset + bev->x*group->range; - uint8_t key = midi_get_key(oscmidi_hash, bev->sid); + uint8_t key; + uint8_t ch; + midi_get_key(oscmidi_hash, bev->sid, &key, &ch); uint16_t bend =(X - key)*mul[bev->gid] + 0x1fff; uint16_t eff = bev->y * 0x3fff; @@ -280,6 +339,15 @@ _oscmidi_multi(const char *path, const char *fmt, uint_fast8_t argc, osc_data_t return config_check_bool(path, fmt, argc, buf, &config.oscmidi.multi); } +static uint_fast8_t +_oscmidi_mpe(const char *path, const char *fmt, uint_fast8_t argc, osc_data_t *buf) +{ + uint8_t res = config_check_bool(path, fmt, argc, buf, &config.oscmidi.mpe); + if( (argc > 1) && config.oscmidi.mpe) + oscmidi_init(); // send zones + return res; +} + static uint_fast8_t _oscmidi_path(const char *path, const char *fmt, uint_fast8_t argc, osc_data_t *buf) { @@ -370,12 +438,18 @@ _oscmidi_reset(const char *path, const char *fmt, uint_fast8_t argc, osc_data_t group->control = 0x07; group->offset = MIDI_BOT; group->range = MIDI_RANGE; - mul[i] = (float)0x1fff / group->range; + if(config.oscmidi.mpe) + mul[i] = (float)0x1fff / ceil(group->range); //MPE only supports whole semitone ranges + else + mul[i] = (float)0x1fff / group->range; } size = CONFIG_SUCCESS("is", uuid, path); CONFIG_SEND(size); + if(config.oscmidi.mpe) + oscmidi_init(); // send zones + return 1; } @@ -443,7 +517,12 @@ _oscmidi_range(const char *path, const char *fmt, uint_fast8_t argc, osc_data_t { OSC_MIDI_Group *grp = OSCMIDI_GID(path); - return config_check_float(path, fmt, argc, buf, &grp->range); + uint_fast8_t res = config_check_float(path, fmt, argc, buf, &grp->range); + + if( (argc > 1) && config.oscmidi.mpe) + oscmidi_init(); // send zones + + return res; } static uint_fast8_t @@ -497,6 +576,7 @@ const OSC_Query_Item oscmidi_tree [] = { OSC_QUERY_ITEM_METHOD("enabled", "Enable/disable", _oscmidi_enabled, config_boolean_args), OSC_QUERY_ITEM_METHOD("multi", "OSC Multi argument?", _oscmidi_multi, config_boolean_args), OSC_QUERY_ITEM_METHOD("format", "OSC Format", _oscmidi_format, oscmidi_format_args), + OSC_QUERY_ITEM_METHOD("mpe", "Multidimensional polyphonic expression?", _oscmidi_mpe, config_boolean_args), OSC_QUERY_ITEM_METHOD("path", "OSC Path", _oscmidi_path, oscmidi_path_args), OSC_QUERY_ITEM_METHOD("reset", "Reset attributes", _oscmidi_reset, NULL), OSC_QUERY_ITEM_ARRAY("attributes/", "Attributes", group_array, GROUP_MAX) diff --git a/sensors/sensors.c b/sensors/sensors.c index a0f2e95..7071d9a 100644 --- a/sensors/sensors.c +++ b/sensors/sensors.c @@ -232,7 +232,7 @@ _sensors_velocity_stiffness(const char *path, const char *fmt, uint_fast8_t argc } static uint_fast8_t -_group_clear(const char *path, const char *fmt, uint_fast8_t argc, osc_data_t *buf) +_group_reset(const char *path, const char *fmt, uint_fast8_t argc, osc_data_t *buf) { (void)fmt; (void)argc; @@ -242,7 +242,7 @@ _group_clear(const char *path, const char *fmt, uint_fast8_t argc, osc_data_t *b buf_ptr = osc_get_int32(buf_ptr, &uuid); - cmc_group_clear(); + cmc_group_reset(); size = CONFIG_SUCCESS("is", uuid, path); CONFIG_SEND(size); @@ -293,6 +293,9 @@ _group_north(const char *path, const char *fmt, uint_fast8_t argc, osc_data_t *b else grp->pid &= ~CMC_NORTH; + if(argc > 1) + cmc_group_update(); // there may be new groups, we need to update + return ret; } @@ -308,6 +311,9 @@ _group_south(const char *path, const char *fmt, uint_fast8_t argc, osc_data_t *b else grp->pid &= ~CMC_SOUTH; + if(argc > 1) + cmc_group_update(); // there may be new groups, we need to update + return ret; } @@ -337,7 +343,7 @@ _group_number(const char *path, const char *fmt, uint_fast8_t argc, osc_data_t * buf_ptr = osc_get_int32(buf_ptr, &uuid); - size = CONFIG_SUCCESS("isi", uuid, path, GROUP_MAX); + size = CONFIG_SUCCESS("isi", uuid, path, cmc_groups_n); CONFIG_SEND(size); return 1; @@ -348,7 +354,7 @@ _group_number(const char *path, const char *fmt, uint_fast8_t argc, osc_data_t * */ static const OSC_Query_Argument group_number_args [] = { - OSC_QUERY_ARGUMENT_INT32("Number", OSC_QUERY_MODE_R, GROUP_MAX, GROUP_MAX, 1) + OSC_QUERY_ARGUMENT_INT32("Number", OSC_QUERY_MODE_R, 0, GROUP_MAX, 1) }; static const OSC_Query_Argument min_args [] = { @@ -384,7 +390,7 @@ static const OSC_Query_Item group_attribute_array [] = { }; static const OSC_Query_Item group_tree [] = { - OSC_QUERY_ITEM_METHOD("reset", "Reset all groups", _group_clear, NULL), + OSC_QUERY_ITEM_METHOD("reset", "Reset all groups", _group_reset, NULL), OSC_QUERY_ITEM_METHOD("number", "Number", _group_number, group_number_args), OSC_QUERY_ITEM_ARRAY("attributes/", "Attributes", group_attribute_array, GROUP_MAX) }; -- 2.38.5