ok part 3...
the midi messages and delta time.
all notes that are turned on must be turned off at some point in time. failing to do this will sound horrible and all your notes turned on will just stay on.
all midi messages are described after the associated delta time wait interval.
so if no time has passed by then you would use a delta time of 0 saying that this event occurs at the same time as the last event message.
advancing this number means that from this new event being posted.... this much time has elapsed. every new event after this point has also advanced this far.
-start of song-
so if i turn a note on at delta time 0 (start of song)
i turn that same note off at delta time 32
-end of song-
this means that a 32 delta time has passed before turning the note off
you can stack all the channels events in there using the same delta times or advancing your song as needed. changing the delta time will advance the whole song forward. After advancing the song forward you will use 0 delta times to describe the rest of the activity that needs to be performed at the same time.
changing the delta time means to change the time since the last events that occurred. This number describes how much time has passed since last event. Do not keep adding bigger and bigger values to the delta... thats not how it works.
if i had data every 4 delta time cycles .... my delta advance number would always be 4 and then 0's to describe all the events that happen at the same time.
ok some code:
set midi tempo:
function setmiditempo(fn,delta,tempo)
setvariablebytedata_msb(delta)
writebytes(fn)
mbyte=0
mbyte=setnibble(mbyte,5,15) //f hex
mbyte=setnibble(mbyte,1,15) //f hex
writebyte(fn,mbyte)
mbyte=0
mbyte=setnibble(mbyte,5,5) //5 hex
mbyte=setnibble(mbyte,1,1) //1 hex
writebyte(fn,mbyte)
writebyte(fn,3)
value=((60000/(tempo*8))*8000)
//message(str(value))
setbytedata_lsb3(value)
writebyte(fn,bytes1[3])
writebyte(fn,bytes1[2])
writebyte(fn,bytes1[1])
endfunction
lets Analise that:
after we set our variable byte delta time we must call the midi command. I see there is 2 new functions being called that have not been defined yet....so lets define them first.
function setbytedata_lsb3(value)
//lets mt out variable byte length
for x=0 to 4 step 1
bytes1[x]=0
next x
bit_01=0
bit_02=0
bit_03=0
bit_04=0
bit_05=0
bit_06=0
bit_07=0
bit_08=0
bit_09=0
bit_10=0
bit_11=0
bit_12=0
bit_13=0
bit_14=0
bit_15=0
bit_16=0
bit_17=0
bit_18=0
bit_19=0
bit_20=0
bit_21=0
bit_22=0
bit_23=0
bit_24=0
working=value
//find current binary
test=working/8388608
if test=>1 then bit_24=1
if bit_24=1 then working=working-8388608
test=working/4194304
if test=>1 then bit_23=1
if bit_23=1 then working=working-4194304
test=working/2097152
if test=>1 then bit_22=1
if bit_22=1 then working=working-2097152
test=working/1048576
if test=>1 then bit_21=1
if bit_21=1 then working=working-1048576
test=working/524288
if test=>1 then bit_20=1
if bit_20=1 then working=working-524288
test=working/262144
if test=>1 then bit_19=1
if bit_19=1 then working=working-262144
test=working/131072
if test=>1 then bit_18=1
if bit_18=1 then working=working-131072
test=working/65536
if test=>1 then bit_17=1
if bit_17=1 then working=working-65536
test=working/32768
if test=>1 then bit_16=1
if bit_16=1 then working=working-32768
test=working/16384
if test=>1 then bit_15=1
if bit_15=1 then working=working-16384
test=working/8192
if test=>1 then bit_14=1
if bit_14=1 then working=working-8192
test=working/4096
if test=>1 then bit_13=1
if bit_13=1 then working=working-4096
test=working/2048
if test=>1 then bit_12=1
if bit_12=1 then working=working-2048
test=working/1024
if test=>1 then bit_11=1
if bit_11=1 then working=working-1024
test=working/512
if test=>1 then bit_10=1
if bit_10=1 then working=working-512
test=working/256
if test=>1 then bit_09=1
if bit_09=1 then working=working-256
test=working/128
if test=>1 then bit_08=1
if bit_08=1 then working=working-128
test=working/64
if test=>1 then bit_07=1
if bit_07=1 then working=working-64
test=working/32
if test=>1 then bit_06=1
if bit_06=1 then working=working-32
test=working/16
if test=>1 then bit_05=1
if bit_05=1 then working=working-16
test=working/8
if test=>1 then bit_04=1
if bit_04=1 then working=working-8
test=working/4
if test=>1 then bit_03=1
if bit_03=1 then working=working-4
test=working/2
if test=>1 then bit_02=1
if bit_02=1 then working=working-2
test=working/1
if test=>1 then bit_01=1
if bit_01=1 then working=working-1
//build each byte
//start with byte 3 then 2 then 1
bytes1[1]=(bit_08*128)+(bit_07*64)+(bit_06*32)+(bit_05*16)+(bit_04*8)+(bit_03*4)+(bit_02*2)+(bit_01*1)
bytes1[2]=(bit_16*128)+(bit_15*64)+(bit_14*32)+(bit_13*16)+(bit_12*8)+(bit_11*4)+(bit_10*2)+(bit_09*1)
bytes1[3]=(bit_24*128)+(bit_23*64)+(bit_22*32)+(bit_21*16)+(bit_20*8)+(bit_19*4)+(bit_18*2)+(bit_17*1)
bytes1[0]=3
endfunction
another new command involves setting a nibble inside a byte.
function setnibble(byte,startaddress,value)
bit1=0
bit2=0
bit3=0
bit4=0
bit5=0
bit6=0
bit7=0
bit8=0
working=byte
//find current binary
test=working/128
if test=>1 then bit8=1
if bit8=1 then working=working-128
test=working/64
if test=>1 then bit7=1
if bit7=1 then working=working-64
test=working/32
if test=>1 then bit6=1
if bit6=1 then working=working-32
test=working/16
if test=>1 then bit5=1
if bit5=1 then working=working-16
test=working/8
if test=>1 then bit4=1
if bit4=1 then working=working-8
test=working/4
if test=>1 then bit3=1
if bit3=1 then working=working-4
test=working/2
if test=>1 then bit2=1
if bit2=1 then working=working-2
test=working/1
if test=>1 then bit1=1
if bit1=1 then working=working-1
nibb1=0
nibb2=0
nibb3=0
nibb4=0
working=value
test=working/8
if test=>1 then nibb4=1
if nibb4=1 then working=working-8
test=working/4
if test=>1 then nibb3=1
if nibb3=1 then working=working-4
test=working/2
if test=>1 then nibb2=1
if nibb2=1 then working=working-2
test=working/1
if test=>1 then nibb1=1
if nibb1=1 then working=working-1
if startaddress=1
bit1=nibb1
bit2=nibb2
bit3=nibb3
bit4=nibb4
endif
if startaddress=2
bit2=nibb1
bit3=nibb2
bit4=nibb3
bit5=nibb4
endif
if startaddress=3
bit3=nibb1
bit4=nibb2
bit5=nibb3
bit6=nibb4
endif
if startaddress=4
bit4=nibb1
bit5=nibb2
bit6=nibb3
bit7=nibb4
endif
if startaddress=5
bit5=nibb1
bit6=nibb2
bit7=nibb3
bit8=nibb4
endif
newbyte=(bit8*128)+(bit7*64)+(bit6*32)+(bit5*16)+(bit4*8)+(bit3*4)+(bit2*2)+(bit1*1)
endfunction newbyte
2 nibbles are used inside a byte to describe what midi event is happening. in this case it was FF "hex" 255 decimal.
the command FF 51 (using 2 bytes or... 4 nibbles in our case) this tells the midi player to set the tempo which is written in 3 bytes of data
so <deltatime> <FF> <51> <03> <byte3> <byte2> <byte1> written as msb. my command writes it as lsb but is then saved as msb manually
you must use 3 bytes of data to describe this tempo. i could be wrong.... but for this examples sake.... dont change it.
the tempo is in beats per minute. since the start of my header says im using a 32 delta time division... it will take 32 delta time frames to hit a quarter note using 4/4
to write a 32nd note delta time would equal 4 and the sizes go up from there. Since my previous midi work involves the smallest note a 32nd note, all my delta times advance by 4
once you get passed this the rest of the events are easy i promise!
all the commands i have written to write a midi song.
set midi channel volume
function writemidivolume(fn,delta,cn,value)
setvariablebytedata_msb(delta)
writebytes(fn)
mbyte=0
mbyte=setnibble(mbyte,5,11) //b hex control change
mbyte=setnibble(mbyte,1,cn) // channel
writebyte(fn,mbyte)
writebyte(fn,7) //volume
writebyte(fn,value)
endfunction
fn=file number, deltatimepassed, channel, value)
the value can be 0 to 127 max volume for the channel
channel 9 is the standard drum kit (fyi)
the channel range is 0-15 and 9 is the drum kit.
set midi channel balance
function writemidibalance(fn,delta,cn,value)
setvariablebytedata_msb(delta)
writebytes(fn)
mbyte=0
mbyte=setnibble(mbyte,5,11) //b hex control change
mbyte=setnibble(mbyte,1,cn) // channel
writebyte(fn,mbyte)
writebyte(fn,10) //balance
writebyte(fn,value)
endfunction
filenumber,deltatime,channel,value
the channel range is 0-15
balance is 0 to 127
0 = left
63 center
127 =right
midi channel modulation
function writemidimodulation(fn,delta,cn,value)
setvariablebytedata_msb(delta)
writebytes(fn)
mbyte=0
mbyte=setnibble(mbyte,5,11) //b hex control change
mbyte=setnibble(mbyte,1,cn) //channel
writebyte(fn,mbyte)
writebyte(fn,1) //mod wheel
writebyte(fn,value)
endfunction
filenumber,delta,channel,value
the modulation wheel is 0 for off..... 127 max
midi channel pitch change
function writemidipitch(fn,delta,cn,value)
setvariablebytedata_msb(delta)
writebytes(fn)
mbyte=0
mbyte=setnibble(mbyte,5,14) //e hex pitch change
mbyte=setnibble(mbyte,1,cn) //channel
writebyte(fn,mbyte)
writebyte(fn,0) //dud
writebyte(fn,value) //pitch 63 is normal
endfunction
0 would change the pitch of notes on the selected channel to be 1 tone lower
63 would sound normal
127 would change the pitch of notes on the selected channel to be 1 tone higher
change a channel instrument
function setmidiinstrument(fn,delta,channel,instrument)
setvariablebytedata_msb(delta)
writebytes(fn)
mbyte=0
mbyte=setnibble(mbyte,5,12)
mbyte=setnibble(mbyte,1,channel)
writebyte(fn,mbyte) //program change channel 0
writebyte(fn,instrument-1)
endfunction
its setup for an instrument value of 1 to 128 but actually subtracts a value
normally this would have been 0-127
1=piano and so on.... the instrument numbers can be looked up online.
turn a midi note on
function writemidinoteon(fn,delta,channel,key,velocity)
if channel<0 then channel=0
if channel>15 then channel=15
setvariablebytedata_msb(delta)
writebytes(fn)
mbyte=0
mbyte=setnibble(mbyte,5,9) //note on
mbyte=setnibble(mbyte,1,channel) //channel 0-15
writebyte(fn,mbyte)
writebyte(fn,key) //note 0-127
writebyte(fn,velocity) //volume
endfunction
turn a midi note off
function writemidinoteoff(fn,delta,channel,key,velocity)
setvariablebytedata_msb(delta)
writebytes(fn)
mbyte=0
mbyte=setnibble(mbyte,5,8) //note off
mbyte=setnibble(mbyte,1,channel) //channel 0-15
writebyte(fn,mbyte)
writebyte(fn,key) //note 0-127
writebyte(fn,velocity) //
endfunction
midi tones are in the range of 0-127... you will need to do some homework to see what numbers are what notes at what octaves.
thats all the messages needed to write a midi file. after this you would write the close track command that i have on the first post of this tutorial.
looks like this:
function writemidiendtrack(fn,delta)
setvariablebytedata_msb(delta)
writebytes(fn)
writebyte(fn,255) //control
writebyte(fn,47) //end ...its all over
writebyte(fn,0) //no idea but needs a zero byte at end
endfunction
now like i said before we will cheat and fill in that bytes to follow value. remember above i had written 4. we now need to count all the bytes used inside our track and can do it like this.
closefile(1)
//now need to fix byte total
if getmemblockexists(1)=1 then deletememblock(1)
creatememblockfromfile(1,filename$)
size=GetMemblockSize( 1 )-22
setbytedata_lsb4(size) //now we know the size
setmemblockbyte(1,18,bytes1[4])
setmemblockbyte(1,19,bytes1[3])
setmemblockbyte(1,20,bytes1[2])
setmemblockbyte(1,21,bytes1[1])
deletefile(filename$)
sync()
CreateFileFromMemblock( filename$,1 )
//done!!
if getmemblockexists(1)=1 then deletememblock(1)
so we saved the file... loaded it back in as a memblock
we subtracted the header bytes and part of the track header to get the total bytes to follow.
then we write it in the correct byte order.
delete the original made file
save the new one...
thats it.
ask plenty of questions if you gotem. see my example on first post if you want to see the code in action. Some of these new functions need to be added in however but are posted in this very post.
hope that helps.... it should make it easy to create a music editor now using this.