Reading MIDI on interrupt

Jon_

Member
The following code works fine for MIDI but causes a problem when i want another program to be running and then listen when MIDI starts talking:

Code:
#picaxe 40X1

HserSetup B31250_4, %001

Do
  If ptr <> hSerPtr Then
    b0 = @ptrInc
    b1 = b0 / $10 + "0" : If b1 > "9" Then : b1 = b1 + 7 : End If
    b2 = b0 & $0F + "0" : If b2 > "9" Then : b2 = b2 + 7 : End If
    SerTxd( "Got $", b1, b2, " ", #b0, CR, LF )
  End If
Loop

so with very little knowledge i have tried adjusting the code with the setintflags:

Code:
#picaxe 40X1

HserSetup B31250_4, %001
setintflags %00100000,%00100000

main:
	high portc 5
	pause 300
	low portc 5
	pause 300
	
goto main

interrupt:

'Do
  If ptr <> hSerPtr Then
    b0 = @ptrInc
    b1 = b0 / $10 + "0" : If b1 > "9" Then : b1 = b1 + 7 : End If
    b2 = b0 & $0F + "0" : If b2 > "9" Then : b2 = b2 + 7 : End If
    SerTxd( "Got $", b1, b2, " ", #b0, CR, LF )
  End If
'Loop

HserSetup B31250_4, %001 
setintflags %00100000,%00100000
return
I was hoping that on every message sent the interrupt would get fired, however, those more knowledgeable than myself can probably already see the issues. Rather than receiving the whole MIDI message you receive parts of it and eventually it becomes completely corrupt. I am guessing this has something to do with the pointers changing between running the main program and running the interrupt. The Do...loop obviously keeps them synced on the first piece of code but the second fails as it is removed. An answer would be to leave the do..loop in but i need to find a way of knowing when to escape the loop otherwise the interrupt is pointless.

So the short question is, how can i get an interrupt that captures the full midi message?
 

hippy

Ex-Staff (retired)
You can't generate an interrupt to indicate a full MIDI message received; the only way is to read the MIDI status, work out how long the message is and wait for that number of bytes to have been received.

The difficult part is in ensuring the PICAXE is always ready to handle the received data; in your first code the SERTXD takes a long time to execute and received data can be lost during that time.

If you know that data turns up in bursts you can delay after the first byte so all data will have been received, then dump what you have, but with MIDI further data could still turn up while reporting what was received. The only option is to reduce the SERTXD command to execute more quickly; output smaller chunks of data and increase the PICAXE operating speed with SETFREQ.

Basically you need to have each SERTXD execute faster than the rate MIDI bytes will be received, and 4800 baud at 4MHz is far slower than 31250 baud. Increase the speed to SETFREQ EM32 and SERTXD baud rate becomes 38400 baud which should be fast enough and send only one byte at a time within each SERTXD.

If that's still not fast enough, you'll have to go to a 20X2 or 28X2 running at 64MHz.
 

Jon_

Member
Ok, so this could be the chipset not outputing it quick enough, but the data is being received by the picaxe correctly. Ultimately, all i will be sending is program changes and note changes, so i know how big the message should be so i could work backwards from that, use the interrupt to start receving and wait for the full message which should be a the program or note change commands, then return to the program.
 

hippy

Ex-Staff (retired)
You can either use the HSERINFLAG to indicate when the first MIDI byte has been received or simply use HSERPTR <> PTR, either use interrupts or polling - Personally I'd just poll and check HSERPTR <> PTR as in your first code example.

Don't forget that you'll have to (should) handle MIDI Running Status where the command byte might not be sent but the data payload is. That will be more likely to occur with Note On/Off messages but can occur with any.

There should be example code on the forum for a finite state machine which handles various MIDI messages, determines when full messages have been received and ignores others but that will require searching for as I don't have a link to hand.

Your original code example was really just debugging code and won't necessarily work for all cases - MIDI received during SERTXD. Without SERTXD ( or anything which blocks execution and prevents high-speed serial receive ) everything should be fine.
 

Jon_

Member
thanks for the help so far hippy.

lets just go back to basic for a minute (well basics as in for me to get my head around how to achieve this).

I know there will be a command for program change which will be something like $c0 44, that would change channel one to program 44. Which is effectively two bytes of information?

if this was a note it would have something like 94 3D 79 turns note 3D on channel 4 on with a velocity of 79, which would be three bytes?

so i am looking to receving a maximum of three bytes of comms from the interrupt firing. The interrupt should fire when a byte has been received? so should i be thinking that it should fire three times, effectively receiving three bytes or should i be thinking that once it has fired i should listen until all three bytes have been received?

so going back to programming here,
Code:
  Do : Loop Until hSerPtr <> ptr
  b0 = @ptrInc

  If b1 = PROG_CHANGE_COMMAND And b0 <= $7F Then
    SerTxd( "Got Prog Change = ", #b0, CR, LF )
  Else
    b1 = b0
  End If
So this is a piece of code which i believe you wrote a while ago, could you explain how this is actually working? I think this is saying that when the serial data is received then its changing the pointer to the scratchpad area than contains the received data. Is this anywhere near the mark?
 

hippy

Ex-Staff (retired)
I would say forget counting interrupt flag being set as there's a chance to miss further interrupts before clearing a previous; just count the bytes, or use a state machine.

That code is a very iterative simple state machine for matching previous byte and current byte to a Program Change message; that will notionally be two bytes but only one byte if using Running Status. It works for that but would need enhancing for other commands.
 

Jon_

Member
ok so my original problem was being able to leave main part of the program when MIDI was talking. So now i have turned that on its head to make it a state machine, so therefore, the user will interrupt the program and MIDI will constantly be read.

The software i am using does not use running status so it should send the command every time data is sent.

So now i have a problem of insight again, the program posted works fine for program changes but i am not sure how to capture three bytes of note on command. If i just write another conditional looking for $90 then i get four iteration none of which seem to be showing anything that i can figure out. from this code:

Code:
Symbol MIDI_CHANNEL = 1 

Symbol PROG_CHANGE_COMMAND = $BF + MIDI_CHANNEL ' $C0

symbol NOTE_ON_COMMAND = $90

do
	do :	Loop Until hSerPtr <> ptr
	
	b0 = @ptrInc
	
	If b1 = PROG_CHANGE_COMMAND And b0 <= $7F Then
		SerTxd( "Got Prog Change = ", #b0, CR, LF )
	
	Elseif b1 = NOTE_ON_COMMAND then
		SerTxd( "Got Note On = ", b1, #b0, CR, LF )
	else
		b1 = b0
	End If
loop
you get:

Got Note On: 48
Got Note On: 100
Got Note On: 128
Got Note On: 48

when middle C is played, which should be 48, so its capturing the key. I am not sure what 100 is representing but guessing 128 is the velocity, then its capturing the note off again, but the note off should be $80 so would not fall into that conditional....its all a bit confusing....
 

hippy

Ex-Staff (retired)
Note On is a three byte command including command byte so the state machine has to be more complicated than you have it - and more complicated still to handle messages of various sizes.

The 128 ($80) is Note Off which is leaking through as part of Note On due to the way the state machine is.

I'll have a look to see if I can find better MIDI receiver parsing code. If not it's quicker to rewrite it than explain it but it's also a case of finding the time to do that.
 

hippy

Ex-Staff (retired)
Code updated, bugs removed from previously posted code. Not tested with real MIDI ...
 

Attachments

Last edited:

Jon_

Member
well that code mostly works, thank you.

The only slight problem is that when you do a program change it repeats the last value of data so it needs to be sent twice before getting the real value.

So if you send program change 1 and then send it again you get:
program change 0
program change 1

then if you send program change 2 you get
program change 1 (then send it again)
program change 2

if you hit a note you then get the note off data when sending a program change.
program change 64 (resend program change)
program change 2

so for some reason its not reseting....
 

hippy

Ex-Staff (retired)
Oops, but easy to fix - change #arg1 to #arg2 in the SERTXD for both PROGRAM_CHANGE and AFTERTOUCH.

Handling Channel Mode messages ( All Notes Off etc ) would also be worthwhile adding to the handler as they are often also sent out by instruments at turn on and in idle periods to fix anything which has jammed-on. I'll do that later.

One day I'll actually get round to building a MIDI interface then I can actually test MIDI code - Another thing to add to the list !
 
Top