MIDI Active Sensing issue

Hello all,

I’m new here and thought I’d finally come out with a post about a Active Sensing (AS) issue I am having with a STM32F4 DSP project I’ve been working on.

My project has MIDI via UART.
It works great with just about anything you throw at it… notes On and Off and CC msgs all get captured and interpreted correctly using a non AS keyboard.
BUT, when I try to use older M Audio or Yamaha Keyboards DIRECTLY, I get garbage up MIDI with hanging notes or a lowest note 0x00 or combination of.
YET, when I send the AS MIDI from those same keyboards into a USB midi interface and THRU that stream in a app like midi ox so it gets sent back out the usb midi out, my STM32 handles it just fine!

I can share some code later when I get home.
Just wondered if this issue rings any bells out there.
I have my code reject AS… think it is 0xFE… it’s supposed to ignore it and go back to fetch the next incoming byte…

Thanks in advance,
/Blaine

Maybe it’s your MIDI receiver (optocoupler) circuit?

You may be on to something with that.

I do recall checking signals with a scope and older M Audio KB with the AS did have a kinda dirty signal going in my 6N138 opto vs. the newer Axiom and Akai MPK kb’s signals.

The output of the 6N138 looked fairly clean though.
But I am going from 5v down to a 3.3v output before it hits uart…
Hmmm…
Maybe I need to try using a 6N137 instead.

I’ll have to share the schematic I used when I get a chance

I have 6N137s on order from aliexpress… be awhile before they get here :frowning:

I found a schematic like the one I used.

It looks a lot like this…
MIDI-6N138

…EXCEPT, I only have 3.3v powering the 6N138 and there is not a cap between pins 8 and 5.
Oh and theres a R220 and diode at the input like this…
6N138-2

Did I goof it up?
I was hoping to only use 3.3v, but I just found info that the 6N138 requires 5v to operate properly… so that may cause problems with my older keyboards.
The newer ones may be giving a cleaner signal and the USB interface may be cleaning up the signal before it resends it back out again.
Just a guess. Sure would be nice to figure this out and get the little project to work with ALL keyboards.

Don’t use a 6N138 at 3.3V

Last night I managed to change it to 5v for Pin 8 and kept 3.3v for the pullup on Pin 6.

No change.

Guess I will have to dig deeper and get a logic analyzer hooked up somehow to see what I am really receiving at the UART when using these troublesome KBs.

It sounds like a running status issue! USB MIDI doesn’t use RS that’s the reason why it works in USB.
Like all others realtime events, AS must be filtered and NOT STORED as Running status or the last correct Event type will be lost.
I think this issue is not an optocoupler problem, it’s in the code. But Emilie is right, don’t use 6N138 with 3V3.
Try first to deactivate the RS if you can and retry. If it works when deactivated, check the MIDI parser.
Best
Bruno

Thanks for your reply Bruno.

I will try to copy the current code I use in here after I get home…

I thought I was filtering 0xFE out by checking the oncoming byte on uart…
If the byte= 0xFE it would ignore the value and go fetch a new byte.

My midi parser is very basic. I am only after notes On Off and CCs for now. My code should be ignoring all other values.

I will post code later and maybe you can snicker at my amatuar attempts at coding :wink:

Do you use the UART directly or there’s a MIDI library before your code?

In my main.c
I have in:

while (1){
HAL_UART_Receive_IT(&huart1, rx_buff, 1);

}

Then in the Rx callback:

void HAL_UART_RxCallback(UART_HandleTypeDef *huart)
{
If (rx_buff[0]==0xFE){
rx_buff[0]=0;
}
HAL_UART_Receive_IT(&huart1, rx_buff, 1);
return;
}

There’s parsing code after that…
I am copying my code from a tablet to my phone to reply… it’s slow. Lol.

Wait to be home and share the whole code, parser included.
hummm… if HAL_UART_Receive_IT is an interrupt don’t put it in the main loop.
Also set the rx_buff to 0 is a strange thing, usually we just return and don’t call the parser for the event we don’t use then no need to reset rx_buff.

HAL_UART_Receive_IT() is not an interrupt but to initiate receiving a byte via interrupt, so can be in main loop, it’s fine.

Ok. I will get some full code pasted in soon.
Meanwhile… here is a crappy video of the STM32F4 synth I’ve developed so far

https://youtu.be/m_CoyIQd5e0

You can see it has no issue tracking notes and CCs…

It just hates those older keyboards with AS.
There may have been some data glitching from those KBs… I have a decent scope and usb logic analyzer if that’s what it takes to nail the issue down.

my main keyboard is a Roland A-70, it continuously send AS, and I’ve got no problem with all the synth or others MIDI receiving gear I made. MIDI(UART) is asynchronous, there’s no glitch, an event can not override or disturb an other… Akai follow the MIDI protocol and are part of MIDI Association since a long long time, I will be surprised if your keyboard is the guilty one :wink:

Your code replaces 0xFE by 0x00. This is not the same thing as filtering out those messages! If the status byte is 0x80, a pair of AS for example will cause the parser to wrongly receive a note off for note 0 at a velocity of 0.

Hi Emilie,
No need of two consecutive AS. Only one AS byte replaced by 0x00 will put the mess. If he handle correctly the running status, it will receive 3 bytes instead of 2 and shift the bytes, the parser will not be able to put the datas in the right order until the next RS refresh(byte>0x7f).
…I found this strange too, let’s see the whole code…

sounds like I probably messed it all up then…

Ok. here’s the code-

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

	//this works OK for non Active Sense keyboards
	//it can work with Active Sense from a USB Midi interface though..

	if ( (rx_buff[0] >= 0xF8) && (rx_buff[0] <= 0xFF) ){
		HAL_UART_Receive_IT(&huart1, rx_buff, 1);
		return;
		}


		if (rx_buff[0]&0x80){
			MIDI_rx[0]=rx_buff[0];
			//MIDI_rx[1]=0;
			noteByteFlag=0;
			HAL_UART_Receive_IT(&huart1, rx_buff, 1);
			return;
		}


		if (noteByteFlag) {
			noteByteFlag=0;
			MIDI_rx[2]=rx_buff[0];
			HAL_UART_Receive_IT(&huart1, rx_buff, 1);
			procMIDI();
			for (int i=0; i<3; i++){
				MIDI_rx[i]=0;
			}
			//HAL_UART_Receive_IT(&huart1, rx_buff, 1);
			return;
		}
		MIDI_rx[1]=rx_buff[0];
		noteByteFlag=1;
		HAL_UART_Receive_IT(&huart1, rx_buff, 1);



	}

//the rest of the wreckage…

	void procMIDI(void){


MIDIcmd= MIDI_rx[0];
MIDInote= MIDI_rx[1];
MIDIvel= MIDI_rx[2];



if((!MIDIvel)&&(MIDIcmd==0x90)){
	MIDInoteOff(MIDInote);
for (int i=0; i<3; i++){
	    	MIDI_rx[i]=0;}
HAL_UART_Receive_IT(&huart1, rx_buff, 1);
return;
}

if(MIDIcmd==0x80){
	MIDInoteOff(MIDInote);
    for (int i=0; i<3; i++){
    	MIDI_rx[i]=0;}
    HAL_UART_Receive_IT(&huart1, rx_buff, 1);
    return;
}

if(MIDIcmd==0x90){
	MIDInoteOn(MIDInote);
    for (int i=0; i<3; i++){
    	    	MIDI_rx[i]=0;}
    HAL_UART_Receive_IT(&huart1, rx_buff, 1);
    return;
}


if(MIDIcmd==0xB0){
	ProcessReceivedMidiDatas();
for (int i=0; i<3; i++){
	    	    	MIDI_rx[i]=0;}
HAL_UART_Receive_IT(&huart1, rx_buff, 1);
return;
}


if(MIDIcmd==0xE0){
	doPitchBnd();
	for (int i=0; i<3; i++){
	MIDI_rx[i]=0;}
	HAL_UART_Receive_IT(&huart1, rx_buff, 1);
	return;
}}

It seems you just need voice events. No Realtime, Sysex etc…
Then remove your AS filter in the HAL_UART_RxCallback()
and in HAL_UART_RxCpltCallback() replace:

	if ( (rx_buff[0] >= 0xF8) && (rx_buff[0] <= 0xFF) ){

by

	if (rx_buff[0] >= 0xF0) {

It will reject all events except the voice events (Note Off[0c80]…Pitch Bend[0xEF)

Should work

Thanks for taking a look.

I have tried many variants of that first if( ); to filter out 0xFE.

I will try it like you said… It may have been tried before, but I will do again to see what happens.

I still don’t get why it isn’t troubled by USB.
Is it because the interface rearranges AS in a way that it won’t slip into the middle of a note or CC command?

switch case might be more elegant for procMIDI()

void procMIDI(void){

  MIDIcmd= MIDI_rx[0];
  MIDInote= MIDI_rx[1];
  MIDIvel= MIDI_rx[2];

  switch(MIDIcmd >> 4){
    // Note off
    case 0x8:
      MIDInoteOff(MIDInote);
      break;
    // Note On
    case 0x9:
      if(MIDIvel) MIDInoteOn(MIDInote);
      else MIDInoteOff(MIDInote);
      break;
    // CC
    case 0xB:
      ProcessReceivedMidiDatas();
      break;
    // Pitch Bend
    case 0xE:
      doPitchBnd();
      break;
    // all others
    default:
      // Nothing to do 
      break;
  }

  // this is facultative cause all bytes are filled before calling procMIDI()
  for (int i=0; i<3; i++)MIDI_rx[i]=0;   

  // initiate receiving interrupt
  HAL_UART_Receive_IT(&huart1, rx_buff, 1);

}