Midi Receive Clock

QuIcK

Senior Member
Heya,
Ive been poking around some midi implementation for a while. I want to make a midi clock that displays Bars:Beats from the midi clock.
It needs to respond to Song Position Pointer, Beat Clock, and the Time Signature packet (get get beats per bar).

Ive got the hardware side of it working. I can receive midi & process it, then display it on 7-seg displays.

The problem Im having is the Beat:Bar readout im generating is falling behind the Beat:Bar readout of my DAW (ie, clock source). Its like the pic isnt catching all the beatclock packets and drifting (about 1 bar in 28 bars at 200bpm).

Im using a 20x2, the rest is in the attached code. Its an adaptation of Hippy's Midi Parser (actually, pretty much his code!)

Running at 64MHz, a picaxe should be able to keep up with the beatclock, right?
 

Attachments

hippy

Ex-Staff (retired)
Running at 64MHz, a picaxe should be able to keep up with the beatclock, right?
Good question, but one I cannot answer, cannot even remember the details of the clocking these days !

There seems to be four possible reasons for losing sync to the reference you have -

Over-running the background receive buffer with too much data arriving and quicker than being processed - I'd have expected more noticeable errors if that's the case

Missing MIDI bytes while busy doing something - possibly; I'd split the HSPIOUT in the interrupt into four separate commands sending one byte each.

The maths for converting Song Position Pointer to bars or elsewhere not being quite right.

Not using all / the right MIDI messages to keep track of timing - As I say I cannot remember how it works.

The difficulty with any time critical code is in debugging it without having it stop working.

The best approach to find out what's going on is perhaps to take another PICAXE and generate the MIDI messages at a more sedate rate which the receiving PICAXE can keep up with, see if it then matches the reference display, if not it's likely a program error, if it does match it's more likely a timing issue.

You could remove the interrupt routine, make that display when a button is pushed, set the MIDI output running, stop that, press the button, see if the display matches when interrupt removed, if so that narrows the problem down to the interrupt or timer.

Added: Try reducing the rate at which the interrupt is called, see if that causes fewer loses over time.
 
Last edited:

QuIcK

Senior Member
Hey Hippy.
Thanks a lot. I feel like a bit of an idiot.
The way I was counting the beat clocks, i was reseting after 24, to 0... meaning i was counting 25 per quarter not.
WOOPS
fixed it, and it seems to be tracking now.
Just need to fix the goto song position pointer calculations (I know they are wrong).

Anyway, your code is amazing as always, and your support next to none! so thank you!

ps.
anyone wanting to use that code (if u get the same serial 4-seg display as me, there needs to be a return after the interrupt routine :S)
 

hippy

Ex-Staff (retired)
The way I was counting the beat clocks, i was reseting after 24, to 0... meaning i was counting 25 per quarter not.
Part of the famous category of "off-by-one errors", always blindingly obvious with hindsight when you do find them. Glad you got it sorted.
 

QuIcK

Senior Member
If your still about...
I cant seem to get the goto song position to work :(

as a refresher, there are 24 midi clocks per quarter note.
The Goto position sends out the status byte, then 2 byte packets. the 2 byte packets get put together to for a 14-bit number. this represents the position.
there are 6 midi clocks per 'goto beat'... thus 4 'goto beats' per quarter note.

The maths i've come up with should be solid, however i think theres a problem with the receiving & processing the actual packets of midi.

using your code, ive got the following results:
bar 1:1 -> outputs 0,0 -> receives 0,0 - fine!
bar 2:1 -> outputs 16,0 -> receives 16,0 - fine!
bar 3:1 -> outputs 32,0 -> receives 0,0 - ??
bar 4:1 -> outputs 48,0 -> receives 32,0 - ??

i dunno if its skipping packets, or if theres a 'timesaver' in the midi protocol that allows you not to send all the position pointer packets. i havent seen anything to that effect.

any thoughts?
 

hippy

Ex-Staff (retired)
Reading this ...

http://home.roadrunner.com/~jgglatt/tech/midispec/ssp.htm

I am remembering just how complicated it was to get ones head around MIDI beats, clocks and quarter frames, and how much I've forgotten :)

The error might be in how I'm handling the SPP payload, may have messed up my MSB and LSB. Try changing -

Case SONG_POSITION_PTR
statusByte = arg2
arg2 = arg1 * 2
arg1 = statusByte
argW = argW / 2
statusByte = 0

To

Case SONG_POSITION_PTR
arg2 = arg2 * 2
argW = argW / 2
statusByte = 0

That might fix it. If not a SERTXD(#arg1,9,#arg2,CR,LF) after the Case will let us see what numbers we are receiving.
 

hippy

Ex-Staff (retired)
Or is it your beats / bar_count maths which isn't right ?

Edited:

You seem to have a mis-match in what you are calling 'a beat'. You are counting 'beat_count' in individual MIDI Clock counts, but you define 'beats_per_bar' in terms of quarter notes per bar, not as MIDI Clocks per bar.

Add ...

Symbol beat_counts_per_bar = beats_per_bar * 24

Then the maths simply becomes ...

argW = argW * 6
bar = argW / beat_counts_per_bar
beat_count = argW // beat_counts_per_bar
 
Last edited:

QuIcK

Senior Member
I dont think its a case of MSB & LSB handling.
Ive been using debug after each 'goto position'... they happen at a non-critcal time, so its not bogging anything down.

Anyway, the 'new way'
bar 1:1 is returning 0, 0
bar 2:1 is returning 0, 0
bar 3:1 is returning 16, 0
bar 4:1 -> 16,0
bar 5:1 ->0,0
bar 6 -> 0,0

the 'old way'
1:1 -> 0, 0
2:1 -> 0,0
3:1 -> 0,0
4:1 -> 48, 0
5:1 -> 0,0
6:1 -> 0,0.

Im not sure why it would pick up some packets (wrongly) and the rest pick up 0s

for reference:
Code:
Do : Loop until ptr <> hSerPtr
		If @ptr >= $80 Then
			statusByte = @ptrinc
			If statusByte = $F8 Then
				inc beat_count
				if beat_count > 23 then
					'reset beatcounter
					beat_count = 0
					inc beats
					if beats > beats_per_bar then
						beats = 1
						inc bars
					end if
				end if
			end if
		Else '448us
			arg1 = arg2
			arg2 = @ptrInc
			statusCount = statusCount + 1
			Select Case status
				Case TIME_CODE_FRAME
					statusByte = 0
				Case SONG_POSITION_PTR
					statusByte = arg2
					arg2 = arg1 * 2
					arg1 = statusByte
					argW = argW / 2
					statusByte = 0					
					debug
					
				Case SONG_SELECT
					statusByte = 0
				Else
					Goto MainLoop
			End Select
		End If
	statusCount = 1
goto MainLoop
old implementaion
 

hippy

Ex-Staff (retired)
I think the thing now is to determine if it's zero value bytes being sent or maths processing. You'll do better to put DEBUG immediately after the CASE so you can see byte values as received.

You can debug by simulation if you strip out the HSERSETUP and add code to push data into the background receive buffer at the start of code ...

ptr = 0
@ptrInc = SONG_POSITION_PTR
@ptrInc = 0
@ptrInc = 48
@ptrInc = SONG_POSITION_PTR
@ptrInc = 0
@ptrInc = 49
hSerPtr = ptr
ptr = 0

And so on.
 

QuIcK

Senior Member
Right. I know whats going on, but I have no idea how to fix it!

With the code:
Code:
          arg2 = arg2 * 2
          argW = argW / 2
          SerTxd( "Song Position Pointer", 9, #argW, CR, LF )
I get Arg1 & arg2 the wrong way round.
Thats an easy fix.
The difficult one is that its not picking up the most significant packet (arg2).
Ie, im getting the fine-adjust, but not the course adjust.

not sure how to 'get it back'
Ive tried changing the number of arguments it waits for (and having a look at all of them), but there doesnt seem to be any extra in the wrong place
 
Last edited:

hippy

Ex-Staff (retired)
The difficult one is that its not picking up the most significant packet (arg2) ... not sure how to 'get it back'
Not sure how you mean when you say it's not picking it up. Are arg1 and arg2 present with correct values immediately after the CASE ( regardless of whether reversed ), or are you saying arg2 is always zero ?
 
Last edited:

QuIcK

Senior Member
yes, arg2 is always 0.
im at home now (i was doing all the work today on a macbook w/ mac-axepad.)
Ill get on the programming editor, and blast through all the code with an actual keyboard and mouse. And simulate it.
ill let you know what i come up with!
 

QuIcK

Senior Member
in regards to the beats_per_bar etc, that for different time signatures. in a 4/4, there are 4 quarter notes per bar, thus 96 midi clocks per bar. in something like 3/4, theres 3 quarter notes per bar.

so i need midi_beats (ie position pointer), midi_clocks (ie timing clock), beats, bars, beats_per_bar. i just didnt name things very well. Once I have the SPP worked out, im probably going to re-write the code so it actually makes sense (ive learned quite a lot about midi since i started naming things)

As for the SPP being received & what not, its a bit unreliable.
sometimes it works beautifully, sometimes not so much.

with a debug after the position pointer case statement (so no processing on the arguments) the fine adjust (Arg1) always seems to track properly.
The coarse adjust (Arg2) will either track or be 0. I cant seem to see any correlation between going to a bar and getting this missed value
 

hippy

Ex-Staff (retired)
In the conversion to your code you seem to have lost the "statusCount = 1" at the end of the main loop so I'm not really sure what you'll be seeing; I simply incremented statusCount on each byte so it will eventually match every 255 bytes or so but not a problem when it gets reset to 1 after a valid message.

You did however manage to remove a bug in my code where I wasn't catering properly for Real Time System messages ($F8-$FF) which can appear anywhere, including within the payload of of other messages. That shouldn't normally happen but I'll rewrite my code anyway.

As to how you've edited the code you may have also lost some of the Running Status handling though that shouldn't be a problem if only dealing with song position information.
 

hippy

Ex-Staff (retired)
Latest version of the MIDI parser. If altering pay particularly close attention to the status assignments and where @ptrInc is used, because lose those and it may simply lock in an infinite loop.

Untested.
 

Attachments

QuIcK

Senior Member
yeh, i realise i lost the statusCount, and i've fixed that.

Heres a terminal dump (ive cleaned it up) of the input stream. Straight from the scratch-pad, output 1 byte at a time to terminal (then i formatted it so it was sensible).
252 is stop
242 is SPP
this is incrementing 1 bar at a time. the fine (arg1) increments by 16 each time with no problem. once arg1 gets to 127, it should overflow to 0, with arg2 being incremented.
Code:
252
242 - 16 - 0
252
242 - 32 - 0
252
242 - 48 - 0
252
242 - 64 - 0
252
242 - 80 - 0
252
242 - 96 - 0
252
242 - 112 - 0
252
242 - 0 - 0
252
242 - 16 - 0
252
242 - 32 - 1
252
242 - 48 - 1
252
242 - 64 - 0
252
242 - 80 - 0
252
242 - 96 - 0
252
242 - 112 - 0
252
242 - 0 - 0
252
242 - 16 - 0
252
242 - 32 - 0
252
242 - 48 - 0
252
242 - 64 - 2
252
242 - 80 - 0
252
242 - 96 - 0
252
242 - 112 - 0
252
242 - 0 - 2
252
242 - 16 - 2
252
242 - 32 - 2
252
242 - 48 - 2
252
242 - 64 - 3
252
242 - 80 - 2
252
242 - 96 - 2
252
242 - 112 - 2
252
242 - 0 - 0
252
242 - 16 - 0
252
242 - 32 - 0
252
242 - 48 - 0
252
242 - 64 - 0
252
242 - 80 - 0
252
242 - 96 - 0
I dont get why it doesnt work correctly. When i first found the code, all the other commands seemed to function (thot it was just a quick check).

attached is the code i used to get these results
 

Attachments

hippy

Ex-Staff (retired)
It does seem odd. You could try putting a PAUSE before the SERTXD to make sure that isn't causing bytes received to be dropped, but I wouldn't expect there to be zero values byte where there are if that was the case.

What does my own code show for SPP values, is that any better ?
 

QuIcK

Senior Member
that latest code still seems to be having issues with arg2 for SPP

the simulations (feeding it into the scratchpad manually) seems to work fine. but actually decoding the midi seems to be an issue.
maybe it is my hardware.

Ive also tried this with MTC (quarter frame timing)
and get the following terminal output:
Code:
System Exclusive ...
End of System Exclusive
Time Code Quarter Frame	0/0
Time Code Quarter Frame	3/0
Time Code Quarter Frame	4/0
Time Code Quarter Frame	5/0
Time Code Quarter Frame	6/0
Time Code Quarter Frame	7/0
Time Code Quarter Frame	0/2
Time Code Quarter Frame	1/0
Time Code Quarter Frame	2/0
Time Code Quarter Frame	3/0
Time Code Quarter Frame	4/0
Time Code Quarter Frame	0/4
Time Code Quarter Frame	1/0
Time Code Quarter Frame	5/0
Time Code Quarter Frame	6/0
Time Code Quarter Frame	7/0
Time Code Quarter Frame	3/0
Time Code Quarter Frame	4/0
Time Code Quarter Frame	0/8
Time Code Quarter Frame	1/0
Time Code Quarter Frame	2/0
pretty sure the left hand numbers should be 1,2,3...

i tried bumping the speed up to 64mhz (and the hsersetup to _64), and i got worse tracking of the numbers. changing the speed for midi clock didnt change anything. it just seems to not be reliably receiving arg2 for SPP
 

QuIcK

Senior Member
And on the hardware note, i feel a little embarrassed.

I hadnt tied the base of the opto transistor to ground. I guess it was getting phantom signals/floating.
what a day of small mistakes.

Thanks so much for your time Mr Hippy, and sorry for being such a toothache
 

hippy

Ex-Staff (retired)
My code will be dropping some MIDI data because it's arriving during the SERTXD. It could be a hardware issue, maybe post your circuit.
 
Top