Time Based Thinning of Controller Data

CreepyPants

2016-07-19 19:20:57

Still a n00b at coding MTPro, so please bear with me (although I have pgmg background, but that's from decades ago when we still used mainframes).

Situation:
Crossfader from Controller sends MIDI CC data, which I want to convert to outgoing CC data on a different channel and CC#. That's the easy bit.

Problem:
When using the crossfader quickly, MIDI seems to choke - I believe too much data. Can use the crossfader with moderate speeds, and slow speeds quite effectively, although I'm still in testing mode and have yet to develop the layers of modulation I'm hoping I can achieve.

Recgnizing:
When the crossfader is used quickly (lots of CC data in short amount of time), fine data changes are not necessary.

Assumed algorithm:
A time based test against Timer values to only pull every so many ticks. (Suggestion on resolution, perhaps?)

Data:
Note: Current data may be invalid due to miscalculation, but I'm assuming calculations are correct.
When crossfade is used quickly, it generates a CC value every 1.42ms. Slow use of the Crossfade is around 60-90ms. Moderate use in practice is about 10-14ms.

I'm thinking with 8 channels of MIDI data, some general CC data flowing *through* MTPro, and processing (converting Ch9 CC 48 -> Ch 1 CC1 & Ch 2 CC 4 for example) through MTPro I could get a 10ms resolution with no audible dramas.

Question: So, I figure I'd set a timer to sample the CC data to process every 10ms or so. Is there a thread that highlights this technique that I'm missing on searches or (sorry for the n00b question) a section of the manul someone might direct me to?

CreepyPants

2016-07-21 15:48:27

Greetings Florian!
Thank you for the response!
I'll bet you're all busy busy busy getting the BomeBox out - THANK YOU for such awesome, *resilient* software. I've never had to post questions using MTPro in the past, although I've been using it for a year in my previous synth/controller rig configurations. I've never done anything this complex before.

Right now, I can translate the CC values into a basic scenario: no problem. Even have translators to send to multiple Channels.
My next task is to create Global Variables to store OT Scene selection and current CC value.
Then I need to be able to select Preset via CC.

None of these will seem to pose a problem, as I've done each task individually in test scenarios. Building it into a bigger script is a new thing for me too - and terribly excited about getting this code working & tested.

The hurdle I'm having right now is that I *seem* get a MIDI data clog if I send the data too quickly.
That is:
- Using the crossfader at a moderate pace yields expected results
- Using the crossfader quickly makes data (Note On information is most noticeable) clog and/or slow.

Current workarounds would include making sure I don't use the crossfader quickly, but we all know that goes out the window when we're performing. :)

Note: Using the crossfade quickly sends CC data every 1.42ms (if my calculations are correct). I don't need that kind of resolution.

So the big task on my plate is creating an MTPro Preset that will thin the controller data. I pondered using "modulo" to just remove even CC values, but since the problem is the speed at which I use the crossfade I am under the impression a Timer based thinning of CC values would be better.

Supid n00b questions:
Is there a way to create a timer that will poll a specific CC# on a specific channel every so many milliseconds and then send that value to the translator for conversation (Channel/CC#)?
Or would using modulo (as in: if (CC value % 2 = 0, then do nothing, else send the CC value to the translator)) be sufficient?
Which one might be faster/less processsor overhead on the processor on the BomeBox?

Thanks for your help!
-Jim

florian

2016-07-21 21:29:12

Hi CreepyPants,
don't worry about processing power, there's plenty for all that kind of stuff. The usual bottleneck is the MIDI connection, especially standard MIDI DIN ports. They can transmit approx. one 3-byte message per millisecond. Some MIDI interfaces don't even support the full rate. So if you're merging or adding Note messages in addition to a dense stream of CC messages, then yes, it'll clog up.

Thinning is possible, but usually incurs a slight delay which you probably want to avoid...
So, a standard algorithm would be:
  1. use global variable g0 for the current crossfader value
  2. whenever a crossfader message comes in, set g0 to the current value (Rules) and start a one-shot timer (Outgoing) with e.g. 5ms
    Note: when the timer is already started, it's restarted
  3. when the timer expires (timer incoming action), send the desired CC message with value g0
This has 2 problems:
  • continuous moving of the fader with at least 1 message per 5ms will never send out any fader message, because the timer keeps on getting restarted
  • every fader action gets delayed by 5ms
A better way is to use a timer to continuously increment a global clock variable gc. Then we can remember the last time we've sent out a message, and when there has been enough time since the last time, send the current one. The following preset is a bit wasteful, it uses an ever-running timer to increment the clock.

Code: Select all

[x] Preset 0: New Preset
 Default MIDI IN ports:   Fader Input
 Default MIDI OUT ports:  To Application

[x] Translator 0.0: Variables Initialization
Incoming: on activation of this preset
Rules:
  // clock
  gc=0
  // timer frequency in milliseconds
  gr=5
  // thinning duration in milliseconds (greater or equal gr)
  gf=5
  // current fader value
  g0=0
  // last value sent out
  g1=0
  // last time of current fader value.
  // use negative value to enforce sending right away.
  g2=-1000
Outgoing: (none)

[x] Translator 0.1: Start Clock Timer
Incoming: on activation of this preset
Outgoing: Periodic timer "clock timer": gr ms (initial delay: 100 ms)

[x] Translator 0.2: Stop Timer
Incoming: on deactivation this preset
Outgoing: Kill timer "clock timer"

[x] Translator 0.3: Fader Movement
Options: swallow
Incoming: Control Change on channel 1 with CC#:1 (0x01) set 'pp' to value
Rules:
  // remember current value
  g0=pp
  // calculate time since last sending of a message
  qq=gc-g2
  // if less than the frequency, bail out
  if qq<gf then exit rules, skip Outgoing Action
  // remember last value sent out
  g1=pp
  // remember time of sending
  g2=gc
Outgoing: Control Change on channel 1 with CC#:1 (0x01) and value:pp

[x] Translator 0.4: Check for pending fader message
Incoming: On timer "clock timer"
Rules:
  // increase clock
  gc=gc+gr
  // if CC hasn't changed, bail out
  if g0==g1 then exit rules, skip Outgoing Action
  // calculate time since last sending of a message
  qq=gc-g2
  // if less than the frequency, bail out
  if qq<gf then exit rules, skip Outgoing Action
  // remember last value sent out
  g1=g0
  // remember time of sending
  g2=gc
Outgoing: Control Change on channel 1 with CC#:1 (0x01) and value:g0
I assumed that the fader sends on CC#1 and a thinning ratio of 1 message per 5 milliseconds (gf=5). I've tested the preset by increasing gf to 1000 (while keeping gr to 5). Then it would send out one message per second, and when stopping to send messages, send the last received CC value at the very end. The timer will keep on running.

Attached is the project file.
Florian
Attachments
DataThinning.bmtp
(2.62 KiB) Downloaded 117 times

CreepyPants

2016-07-21 22:02:07

Nevermind. Think I have an algorithm to approach this.
Still working this out, but in case anyone else scans the boards also looking at this kind of quandary:

(Note: I suck at type in MT Speak, so apologies for the pseudocode)

Preset 0: TIMER
- On Activate: Deactivate "Reset"
- On Activate: Activate Timer "Enable" 200ms delay
- Set incoming MIDI CC value to global var (in my case, Ch09, CC#48, all values -> gc)
Test for CCval=0 and 127, if so, Skip Rules & Execute Outgoing Action
(If not, Skip Outgoing Action)
Outgoing: Activate Preset "DO"
- On TIMER Activate "DO"

Preset 1: DO
- On Activate: Send my MIDI CCval on my chosen channel (in my case, Ch01, CC#01, value: gc)
(Note: once I have this 'down', MIDI Channel & CC# will also become global variables defined by wholly separate incoming MIDI CCs)
- On Activate: Deactivate TIMER
- On Activate: Activate RESET

Preset 3: RESET
- On Activate: Active TIMER
- On Activate: Deactivate DO

There's probably much more elegant ways of achieving solutions, but this is what I'm working with right now. :)

Note: This does require me to start with the TIMER Preset as [x] Active and others as [ ]Not Active

CreepyPants

2016-07-21 22:06:23

Oh my! Excellent!
Thank you for this - I will take a bit to chew on it and try to wrap my head around it.

Been fiddling today and came up with a different yet somewhat similar algorithm (I think). Posted it on my thread in the MTPro board. Kind of hit a brick wall wrt thinking capabilities.

florian

2016-07-21 23:20:34

I've merged the two topics, sorry for the weird order of posts :)