MIDI help

BlitzMax Forums/BlitzMax Programming/MIDI help

Ash_UK(Posted 2009) [#1]
Hi guys,

I am looking for a way to interpret a MIDI file. Not to play it, but to find out the MIDI instruments, note and durations and then print them to the screen. Can anybody please point me in the right direction? Thanks for your help.


GaryV(Posted 2009) [#2]
http://www.wotsit.org/list.asp?page=3&fc=12&search=&al=

Hopefully this will help?


Ash_UK(Posted 2009) [#3]
Hi Gary,

Many thanks for your quick response :o) This will certainly come in handy, but I was just wondering if maybe you have an example of how to read a MIDI file in BMax and then output data to the screen such as instrument, note, duration etc. Thanks again


GaryV(Posted 2009) [#4]
Nope, no BMax code. What MIDI code I do have is not useful anymore since MCI was deprecated and everything has to be done via DirectShow now. You might want to look at Planet Source Code and see if there is some VB or VC++ code you could port to BMax.


Damien Sturdy(Posted 2009) [#5]
VERY messy BlitzBasic code, uncleaned before posting. I didn't know what I was doing when I coded this, the fact it worked for my purpose was a miracle

It may be good enough for you to make a start with :-)

the includes are my sound system,
Graphics 640,480,16,2
Global chans=50
Global maxlength=10240*2
Global Notes=512
Dim Frst(chans)

Dim started(chans,Notes)
Dim status(chans,Notes)
Dim con(Notes)
Dim counter(Notes)
Global add

Global Mididelta[4]
;      The header chunk appears at the beginning of the file, And describes the
;file in three ways. The header chunk always looks like:
;
;4D 54 68 64 00 00 00 06 ff ff nn nn dd dd
;
;The ascii equivalent of the First 4 bytes is MThd. After MThd comes the 4-byte
;size of the header. This will always be 00 00 00 06, because the actual header
;information will always be 6 bytes. 
;
;ff ff is the file format. There are 3 formats:
;
;0 - single-track 
;1 - multiple tracks, synchronous
;2 - multiple tracks, asynchronous
;
;Single track is fairly self-explanatory - one track only. Synchronous multiple
;tracks means that the tracks will all be vertically synchronous, Or in other
;words, they all start at the same time, And so can represent different parts
;in one song. Asynchronous multiple tracks do Not necessarily start at the same
;time, And can be completely asynchronous. 
;
;nn nn is the number of tracks in the midi file.
;
;dd dd is the number of delta-time ticks per quarter note. (More about this
;later)


fs=OpenFile("potc2.mid")

head$=Readhead(fs)
Print head$
head2=ReadmInt(fs)
Format=ReadmShort(fs)
tracks=Readmshort(fs)
timeticks=Readmshort(fs)
Print "File is of format "+Format
Print tracks+" tracks in file..."

;3. Track Chunks
;
;The remainder of the file After the header chunk consists of track chunks.
;Each track has one header And may contain as many midi commands as you like.
;The header For a track is very similar To the one For the file:
;
;4D 54 72 6B xx xx xx xx
;
;As with the header, the First 4 bytes has an ascii equivalent. This one is
;MTrk. The 4 bytes After MTrk give the length of the track (Not including the
;track header) in bytes. 
      ;Following the header are midi events. These events are identical To the
;actual Data sent And received by MIDI ports on a synth with one addition. A
;midi event is preceded by a delta-time. A delta time is the number of ticks
;After which the midi event is To be executed. The number of ticks per quarter
;note was defined previously in the file header chunk. This delta-time is a
;variable-length encoded value. This format, While confusing, allows large
;numbers To use as many bytes as they need, without requiring small numbers To
;waste bytes by filling with zeros. The number is converted into 7-bit bytes,
;And the most-significant bit of Each byte is 1 except For the Last byte of the
;number, which has a msb of 0. This allows the number To be Read one byte at a
;time, And when you see a msb of 0, you know that it was the Last (least
;significant) byte of the number. According To the MIDI spec, the entire delta-
;time should be at most 4 bytes long. 
;      Following the delta-time is a midi event. Each midi event (except a
;running midi event) has a command byte which will always have a msb of 1 (the
;value will be >= 128). A list of most of these commands is in appendix A. Each
;command has different parameters And lengths, but the Data that follows the
;command will have a msb of 0 (less than 128). The exception To this is a meta-
;event, which may contain Data with a msb of 1. However, meta-events require a
;length parameter which alleviates confusion. 
      ;One subtlety which can cause confusion is running mode. This is where
;the actual midi command is omitted, And the Last midi command issued is
;assumed. This means that the midi event will consist of a delta-time And the
;parameters that would go To the command If it were included. 


Global fs2=WriteFile("mariomusic.bb")
chantime=0
std=0
Cls

For n=0 To chans
frst(n)=1
Next

For Midchan=1 To tracks
fst=1
counter(Midchan)=0
t2=counter(Midchan)
addc=0
head$=Readhead$(fs)
Print head$
Length=ReadmInt(fs)
add=0
ldt=0
Timer=0

fintrack=0
Repeat
;byte=readbyte2(fs)
;Repeat
;byte=readbyte2(fs)
;Until byte<128 Or KeyDown(1)
ldt=dt

dt=getdeltatime(fs)
;If dt>timeticks*64 Then dt=0
adt=dt
;If dt>1500 Then dt=dt/4.0
;If dt<>0 Then Print dt
ocmd=cmd
cmd=readbyte2(fs)

If cmd<128 Then   ;running mode
	cmd=ocmd
	add=add-1
	SeekFile(fs,FilePos(fs)-1)
	;counter(channel)=counter(channel)+adt
EndIf

command=getcommand(cmd)
If cmd=255 Then ;meta command
xx=ReadByte2(fs)
;Print "Meta command! type:  "+xx
nn=ReadByte2(fs)
If xx=47 And nn=00 Then fintrack=1

For nvt=1 To nn:n2=ReadByte2(fs):Next
EndIf


channel=(getchannel(cmd))+Midchan*3
channel=getchannel(cmd)
;'channel=Midchan
;channel=Midchan
cn=channel;Midchan
t2=counter(cn)
fst=frst(channel)
cmdn=0
ac=0
If command>127 Then ac=1
;och=channel
;channel=channel+addc
If (command=128 Or command=128+16) And (dt>-1 Or fst=1) Then

note=ReadByte2(fs)
vl=ReadByte2(fs)
;Print Note+"  "+vl+"   "+chanel
Note2=16
DN=0
If Note>0 And Note<Notes  Then;dt=0:t2=0
If command=128+16 And vl>0 And status(channel,Note2)=0 Then
cmdn=1
DN=1
	addnote channel,Note,((t2+adt)-started(channel,Note2))*timeticks,status(channel,Note2):addc=addc+1
	;If status(channel,note)=0 Then
	started(channel,Note2)=t2+adt
	status(channel,Note2)=1
EndIf
If ((((command=128+16 And vl=0)  Or command=128) And dn=0) And status(channel,Note2)=1) Or fintrack=1 Then			;note off routine
DN=2
cmdn=1
;If channel=3 Then
 Line STARTED(CHANNEL,NotE2)*.05,200-NotE,(T2+(t2+adt)-started(channel,Note2))*.05,200-NotE
	addnote channel,Note,((t2+adt)-started(channel,Note2))*timeticks,status(channel,Note2):addc=addc-1
	started(channel,Note2)=(t2+adt)
	status(channel,Note2)=0

EndIf
;och=channel
;channel=och

If adt>0 And fst=1 Then fst=0
;If dn>0 And fst=1 Then fst=0
frst(channel)=fst

;If ac=1 Then
;updatesnd

;If command=128+16 Then grid(t2+dt,channel,1)=note
;If command=128+16 And vl=0  Then grid(t2+dt,channel,1)=-Note
;If command=128 Then grid(t2+dt,channel,1)=-Note
;Repeat
;sk=SeekFile(fs,FilePos(fs)-1)
;bt=ReadByte(fs)
;sk=SeekFile(fs,FilePos(fs)-1)
;If KeyDown(1) Then End
;Until bt>127

EndIf


;WriteLine fs2,"Data "+channel+","+Str$(Float(vl)/Float(64.0))+","+Note*4+","+ln1
;	 Print command+"  "+channel+"    "+cmd
EndIf
	If fst=0 Then counter(cn)=counter(cn)+adt

;If command=
;Print add+"!"
If KeyDown(1) Then End
;timer=timer+timeticks
Until add>=Length Or Eof(fs) Or Ed=1
If add>length Then sk=SeekFile(fs,FilePos(fs)-(add-Length))
Next
Print "done!"
Repeat
Until KeyDown(1)
End
Cls








Function getchannel(byte)
Return byte And 15
End Function
Function getcommand(byte)
Return byte And (16+32+64+128)
End Function
Function Readmshort(fs)
Return (readbyte2(fs)*256)+readbyte2(fs)
End Function
Function Readmint(fs)
Return (readbyte2(fs)*256*256*256)+(readbyte2(fs)*256*256)+(readbyte2(fs)*256)+(readbyte2(fs))
End Function
Function getdeltatime(fs)
Local ok=0,bt,n
;SeekFile(fs,FilePos(fs)-1):add=add-1
;Repeat:bt=ReadByte2(fs):Until bt>127 Or KeyDown(1) Or Eof(fs)
;SeekFile(fs,FilePos(fs)-1):add=add-1
;bt=ReadByte2(fs)
;Print "---"
For n=1 To 4
mididelta[n]=0
Next
For n=1 To 4
n2%=readbyte2(fs)

n3=n2 And 128
Mididelta[n]=n2:If n2>128 Then n2=n2-128
If n3<128 Then ct=n:n=50
;Print n2+"   "+n3+"   !!!"
;Print Hex$(n2)
Next
;If n3>127 Then n2=n2-128
;time=(Mididelta[4]*256*256*256)+(Mididelta[3]*256*256)+(Mididelta[2]*256)+(Mididelta[1])
time=(Mididelta[4]*128*128*128)+(Mididelta[3]*128*128)+(Mididelta[2]*128)+(Mididelta[1])
;If time<>0 Then Print time:WaitKey
Return time
End
n=1
Repeat
mididelta[n]=readbyte2(fs)

ok=Abs(Sgn(Mididelta[n] And 128))
If ok=0 Then Mididelta[n]=Mididelta[n]-128;If mididelta[n]>127 Then ok=1:Mididelta[n]=Mididelta[n] And 127
;Print n
n=n+1
Until KeyDown(1) Or Eof(fs) Or ok=1 Or n>4
n=n-1
time=(Mididelta[4]*256*256*256)+(Mididelta[3]*256*256)+(Mididelta[2]*256)+(Mididelta[1])
;time=(Mididelta[1]*256*256*256)+(Mididelta[2]*256*256)+(Mididelta[3]*256)+(Mididelta[4])
Return time
End Function
Function Readhead$(fs)
Local m$="",n,o
For n=1 To 4
o=readbyte2(fs)
m$=m$+Chr$(o)
Next
Return m$
End Function
Function ReadByte2(fs)
add=add+1
Return ReadByte(fs)
End Function
Function HexToDec (h$)
	If Left (h$, 1) = "%" Then h$ = Right (h$, Len (h$) - 1)
	h$ = Upper (h$)
	For r=1 To Len (h$)
		d = d Shl 4: a$ = Mid (h$, r, 1)
		If Asc (a$) > 60
			d = d + Asc (a$) - 55
		Else
			d = d + Asc (a$) - 48
		EndIf
	Next
	Return d
End Function

Function addnote(channel,Note,Length#,vol#)
;Length=Int(Length/5.0)*5.0
Note=Note-12*2
;If Length>30000 Then Length=0
;Length=Length*.25
If Length>0  Then
If vol>0 Then WriteLine fs2,"Data                            "+channel+","+vol+","+Note*4+","+Length;+","+time
If vol=0 Then WriteLine fs2,"Data "+channel+","+vol+","+Note*4+","+Length;+","+time
EndIf
End Function