Jump to content

Controlling the MIDI Transmission Rate From Pot Movements


jackchaos

Recommended Posts

I'm building a knobby programmer for my Oberheim Matrix 1000 analog synth module.

All the pots are sending SYSEX data to the synth and most of the parameters do not like receiving the flood of SYSEX MIDI at the rate it's being transmitted.

I had the same problem last year with a commercial MIDI controller but was able to resolve it by adjusting the update interval config option for the rotary encoders on a Behringer BCR 2000 which worked perfectly on the Matrix.

Is there a way I can do the same in MIOS?

I'm running 1.9 MIOS and an edited version of this C application: ain64_din128_dout128_v2_0.zip

Thanks.

Link to comment
Share on other sites

Hi,

MIOS itself doesn't allow to throttle the MIDI bandwidth, but maybe there are tricks to achieve the same without writing an own handler which delays SysEx messages: you could send some 0xfe events after a SysEx message - each 0xfe will cause a delay of 320 uS, and they should be ignored from the Synth

A proper solution would require some more effort at the application side

Best Regards, Thorsten.

Link to comment
Share on other sites

Stryd,

Not sure what you're asking.

Can you elaborate?

TK

I resolved my issue above by doing the following:

void AIN_NotifyChange(unsigned char pin, unsigned int pin_value) __wparam
{
  
  if(PotUpdateCount == POT_UPDATE_INTERVAL) {

     // send sysex 

     PotUpdateCount = 0;
  }
  
 PotUpdateCount++;
}

With POT_UPDATE_INTERVAL of 6.

I only send SYSEX, for every 6 AIN notifications. So far, this works great and I can't really tell things have slowed down.

The Matrix stuttering is marginal now, at least.

Thanks

Link to comment
Share on other sites

Allright.

Although skipping every 6 AIN notifications keeps the Matrix from choking, this isn't a good solution.

Changing a value from to the next value is difficult because I've in essence reduced my resolution.

I think the right way would be to only send the SYSEX value when the pot has stopped moving. The only way I think this is possible is to either count a timer or count cycles some other way and keep track of that last pot that was in motion and what its last value was, ignoring all the other values.

I'll work on this but if anyone can save me time and point me to an obvious feature that might be in MIOS, let me know.

Again, thanks!

Link to comment
Share on other sites

Well you don't really want it to send when the pot stops moving, more like you only want to send one sysex message every N milliseconds, where N is the amount of time the OB takes to recognise a sysex message, plus some time to be safe...

I would have a timer running which triggers the sysex messages to be sent using variables, and set those variables whenever the pot is moved. You can use the timer functions directly, or if your application is already using the timers, you can put a counter in the timed function.

Link to comment
Share on other sites

Ooops I got all confused in this thread... I forgot to post this:

Well sysex tends to be more susceptible to failures in cabling, so I'm just asking if you've got a good clean signal from your midibox to your oberheim? Is it a direct connection with a good quality cable?

There is a problem with that code - it will work - but you'll find that there will be jumps in values that are sent, because it doesn't just send the sysex every so often, it will ignore 5 of every 6 values you set the knob to....

You have already noticed the last part, but something we both missed is TK's solution. Give that a try!

Link to comment
Share on other sites

but something we both missed is TK's solution. Give that a try!

The problem is, I don't understand TK's solution. I sent that byte over MIDI right after each SYSEX message and midiox showed it as "Active Sensing". I don't understand how this is supposed to work. Can you explain what he meant?

The MIDI cabling is fine. Sending standard note on/off data and controller CC data works fine. But, sending a burst of SYSEX data to alter some parameters for a voice like DCO1 Freq to LFO2 modulation while tapping notes on the keyboard produces a noticable stutter and sometime a hung note for a couple of seconds.... especially if you move the pot quickly from min to max.

The flood of SYSEX for some parameters is too much for the MATRIX to adjust and alter the sound... The Matrix 1000 was not designed for real-time sound editing... it was designed to have a PC and a sound editor upload the patch parameters slowly.

Link to comment
Share on other sites

If I understand correctly TK's solution delays the stream

by sending sysex messages inbetween that aren't supported by most synths

so the actual time between two sysex messages is increased...

Yet another suggestion:

have you tried adjusting the deadband setting?

MIOS_AIN_DeadbandSet
C_DECLARATION 	void MIOS_AIN_DeadbandSet(unsigned char deadband)
DESCRIPTION 	sets the difference between last and current pot value which has to be achieved to trigger the "NotifyChange" function
C_IN 	deadband value in <deadband>
C_OUT

larger values should generate less sysex messages...

Michael

Link to comment
Share on other sites

The problem is, I don't understand TK's solution. I sent that byte over MIDI right after each SYSEX message and midiox showed it as "Active Sensing". I don't understand how this is supposed to work. Can you explain what he meant?

maybe I could explain you what I meant? I would tell you the background behind this "trick", if I would know if it is worth the effort...

Best Regards, Thorsten.

Link to comment
Share on other sites

Reducing the resolution is propably not what he want...

He could also reduce the update rate by disabling the "dynamic priority" feature with MIOS_AIN_DynamicPrioSet(0). This will result into a static update rate of "number_of_pots * 200 uS" for each pot, accordingly when the number of pots is set to 64 (all unused analog inputs have to be clamped to ground), this will result into a delay of 12 mS, which is propably enough.

And if this doesn't help, he could modify the TMR0L reload value in mios_isr.inc, IRQ_Timer0, but this requires a manual change in MIOS. It's not controlable from the application, since such a flexibility was not required yet...

Best Regards, Thorsten.

Link to comment
Share on other sites

I sent that byte over MIDI right after each SYSEX message and midiox showed it as "Active Sensing". I don't understand how this is supposed to work. Can you explain what he meant?

Well the idea is to send several of those bytes appended to each sysex message. This means that your matrix gets the sysex, and then while it spends time loading that information, it also receives, and hopefully ignores, the active sense messages that follow it.

The next sysex message can't be sent until the first one has finished sending, so all of your active sense data acts as a "spacer" inbetween the messages

The MIDI cabling is fine. Sending standard note on/off data and controller CC data works fine.

...and? ;)

It's not really relevant any more, it's clear that the problem isn't your cabling but the matrix's slow response...

But FWIW, sending notes and CC's OK does not imply that sysex will be OK - as I said earlier, sysex messages tend to be more susceptible to corruption in poor cabling. Because of this, CC's and notes might work 100%, but sysex might fail. I have a midi cable which was a cheap one I bought in a rush one time, and has poor shielding, and does just that.... I've also seen the same problem happen where midi cables were run alongside power cables, and when midi cables were routed through cheap synths or midi interfaces.

Link to comment
Share on other sites

I did this:

MIOS_MIDI_BeginStream();

MIOS_MIDI_TxBufferPut(matrix sysex byte);
MIOS_MIDI_TxBufferPut(matrix sysex byte);
MIOS_MIDI_TxBufferPut(matrix sysex byte);
...

MIOS_MIDI_TxBufferPut(0xfe); // active sensing
...3 to 6 times

MIOS_MIDI_EndStream();

I tried sending 0xfe both before and after the MIOS_MIDI_EndStream() and was unable to hear the difference while moving a pot.

I do not think this was having any affect on how the Matrix was handling the flood of SYSEX being received when moving a pot rapidly.

BTW: Whats the difference between using MIOS_MIDI_BeginStream() and MIOS_MIDI_EndStream() and not using them at all when sending MIDI. I notice in the sample code and applications, Begin and End Stream isn't always used.

I started working on a solution this morning right before I had to leave for work. Its not elegant but I think it will work.

I'm anxious to get back home and finish it.

I have a lookup table that holds configuration info on each pot like param number, min/max value, if the value should be handled as SIGNED or UNSIGNED etc.

I want to add another config parameter that describes its transmission frequency (skipping values); some pots are fine, others need to be slowed down a little, some need to be slowed down a lot.

For the pots that need to be slowed a lot, once its moved, I'll skip x (say 4) values but keep track of the last value. When I stop moving the pot, MIOS_Tick is notified a special pot value (last value) needs transmitting and sends the value there.

So, in essence. I'm reducing the resolution but my last displayed value, after I stop moving the pot gets transmitted.

Link to comment
Share on other sites

Why is that?? Are they the same length sysex string?

You have to imagine the Matrix being a digital modular synth where you have the option to route 1 of 20 modulation sources like, levers, pedals, velocity, pressure and LFOs, ENVs, portamento etc. To one of 32 desinations like, DCO1 Frequency, VCF Freq, DCO2 Pulse Width etc.

From what I understand, the Matrix cpu has to do some acrobatics to make this work, to completely change the signal path like this.

Oberheim didn't design the Matrix 1000 for real-time editing of all parameters... though some respond nicely for example: DCO1 to DCO2 Mix level requires no slow down in MIDI transmission at all. VCF Resonance works great too. But DCO1 to LFO1 chokes when I move the pot from one extreme to the other quickly.

The customizable matrix modulations all respond slowly to pot events.

Link to comment
Share on other sites

Ahh that explains it.... the reason I asked about the length of the string is that I was trying to figure out if the bottleneck was the MIDI interface or synthesis engine.... Guess it's synthesis, which is actually not so bad, because it shows that it's midi interface can handle full-speed sysex... So long as that sysex doesn't cause the the synthesis engine to overload the uC.

This indicates that the sysex messages don't need to be slowed down, as the midi interface will handle them, but they do need to be spaced out far enough to avoid overloading the uC.

I think you will find that the space you created with 6-7 active sense bytes was insufficient. As TK described,

each 0xfe will cause a delay of 320 uS

6-7 of these is not even 3ms, probably not long enough - if the pause is audible (which it obviously is) then it's at least 10ms, if the pause is only a 'click'. I think you're going to need more like 30 of those bytes! Give it a try... Although, there still might be problems with the sysex not being sent as fast as you turn the knob. ...

I like your new idea, but I was thinking...

I'll skip x (say 4) values

I would run a timer and just send the value when the pot has not been moved in X milliseconds or something... That way there's no loss in resolution :)

Link to comment
Share on other sites

Stryd

When I woke up this morning, I was able to test my solution for this problem after installing Code:Blocks and the nice library that comes with it.

The solution seems to work like a charm; each pot I confgured to behave differently in regards to when and how often it sends a SYSEX.

For the slow pots, they only send SYSEX on the first movement and on the last.

Below is the code I wrote to make this happen:

void AIN_NotifyChange(unsigned char pin, unsigned int pin_value) __wparam

{

.

.

.

//  when a pot is 1st moved, this flag isn't set, so the first move gets transmitted

// and all other subsequent pot movements do not.

if(!app_flags.TRANSMIT_SPECIAL_PARAM) {

Transmit_MIDI(pin, value);

app_flags.TRANSMIT_SPECIAL_PARAM = 1;

MIOS_LCD_CursorSet(0x46); // give a visual that a pot is being moved

MIOS_LCD_PrintCString("-");

}

.

.

.

}

and later in:

void Tick(void) __wparam

{

unsigned int interval;

// We set the special parameter when a slow pot was moved in AIN_Notify

// We didn't transmit then but we will after this counts to max

if(!app_flags.TRANSMIT_SPECIAL_PARAM)

return;

// At this point, app_flags.TRANSMIT_SPECIAL_PARAM is set to 1

// the pot config lookup table is made of unsigned chars, so we have to cast the UPDATE_INTERVAL value to an INT so we can multiply it to a larger number

interval = (unsigned int)POT_CONFIG_MAP[last_ain_pin][POT_CONFIG_UPDATE_INTERVAL];

// we transmit the last pin values here only when we've counted high enough

if(PotUpdateCount >= (interval * 500) {

MIOS_LCD_CursorSet(0x46); // display a visual of when the SYSEX transmits

MIOS_LCD_PrintCString("*");    //

app_flags.TRANSMIT_SPECIAL_PARAM  = 0;

Transmit_MIDI(last_ain_pin, last_ain_value);

PotUpdateCount = 0;

}

PotUpdateCount++;

}

I spent 5 minutes moving, listening and watching the MIDI monitor while moving the pots and I like what I see.

The high resolution pots sound good and the pots that need slow transmissions are working with no stuttering or note hanging.

I'm very pleased!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...