writing variablelength values (midi)

BlitzMax Forums/BlitzMax Programming/writing variablelength values (midi)

nitti(Posted 2012) [#1]
Hi,

I am trying to get my head aroundthe midi specifiction, so I can generate Midi eventually. I am running into a little problem when trying to convert this c code to blitzmax:




the bit shifting syntax in bmax is a bit different and I don't know how to handle the :

>>=
|=

signs


matibee(Posted 2012) [#2]
>>= is bit shifting and re-assignment.

buffer <<= 8

is the same as

buffer = buffer << 8

in Max we can

buffer :Shl 8

which is the same as

buffer = buffer Shl 8

The bitwise Or is the same "|" but Or and re-assignemnt in Max is ":|"


Floyd(Posted 2012) [#3]
buffer >>= 8;

This is an unsigned right shift because the variable itself is unsigned, which BlitzMax doesn't have. But it does have an unsigned shift. You would do

buffer :Shr 8

where buffer is an ordinary Int.

Last edited 2012


nitti(Posted 2012) [#4]
thanks!

another question about the c code (seems the same as some java code I found aswell)

the first while condition
while ( (value >>= 7) )
   {
     buffer <<= 8;
     buffer |= ((value & 0x7F) | 0x80);
   }


will lead to a endless loop in my code, it seems obvious because inside the loop the value variable is not changed, but why is this code correct in c/java then?


matibee(Posted 2012) [#5]
The "value" variable is being changed.. inside the while condition, hence the double brackets.

If you're unsure write it like this..

value :Shr 7
While value
  buffer :Shl 8
  buffer :| ((value & 0x7F) | 0x80)
  value :Shr 7
Wend



nitti(Posted 2012) [#6]
that did the trick, I managed to translate the java code that returns the (variable) length of the value

  /**
   * Return the length of a variable length encoded int without
   * writing it out.
   *
   * @return the length of the encoding
   */
  public int variableLengthIntLength (int value)
  {
    int length = 0;
    int buffer = value & 0x7F;

    while ((value >>= 7) != 0)
      {
	buffer <<= 8;
	buffer |= ((value & 0x7F) | 0x80);
      }
      
    while (true)
      {
	length++;
	if ((buffer & 0x80) != 0)
	  buffer >>>= 8;
	else
	  break;
      }

    return length;
  }



blitzmax equivalent

Function variableLengthIntLength:Int(value:Int)
	Local length:Int = 0
	Local buffer:Int = value & $7f
			
	value :Shr 7
	While value
	  buffer :Shl 8
	  buffer :| ((value & $7F) | $80)
	  value :Shr 7
	Wend

	While(True)
		length:+1
		If ((buffer & $80) <>0)
			buffer :Shr 8
		Else
			Exit
		EndIf	
	Wend
	
	Return length
EndFunction



haven't tested it fully but it appears to work, next up the actual writing


Midimaster(Posted 2012) [#7]
Hi...
My name is 'Midimaster'! That's not a joke, I'm really experienced in MIDI topics.

If you have any questions about MIDI, feel free to ask.

first of all: There is a perfect running MIDI library for BlitzMax made by Brucey (famous). It is a wrapper of RtMidi from Gary P. Scavone (also famous):

all you need package (wrapper + rtmidi):

http://code.google.com/p/maxmods/downloads/list?q=Midi


info about rtmidi:

http://music.mcgill.ca/~gary/rtmidi/

both, the wrapper and the rtmidi are cross-platform windows and mac and always supported/updated by the authors


Floyd(Posted 2012) [#8]
Whenever you see an operand with an = sign appended it is an immediate operand. For example

x += 5;
x = x + 5;

These ultimately have the same effect on x, adding 5. But the second one first calculates the value x+5 and then assigns that to x. The first one operates on x directly. The difference is more apparent here

y = (x += 5);
y = x + 5;

The both calculate x+5 and assign the result to y. But the first one also changes x because the expression (x += 5) operates directly on x.


nitti(Posted 2012) [#9]
@Midimaster : thats very good to know, I intend of making a procedural music generator (that generates midi files) to use in my game. So I suppose I will walk into issues (not being a musician and not being fluent in Midi) so we will talk again, I promise :)

I am not aiming for jazz solo's or complex tune more mixing and matching of a few drumpresets and instrumental riffs.

I am using fmodex (wrapped by brucey too) and the only thinkg i don't like about that is the dll that needs to be in the same folder, I've read something about rtmidi somewhere, i was under the impression it was more for sending signals out of your ports (as in to use it with sensors and stuff) but I'll have another look.

the nice thing about fmodex is that it understands midi and i get all sorts of high tech musical possibilities (at a financial price)

well I am off trying to generate some tones


Midimaster(Posted 2012) [#10]
ah! I understand.. RTMidi is indead for realtime communication with midi instruments. That's what I'm doing: sending midi or interacting with receiving midi events in real time. I never did something with manipulating of midifiles...

I'm not experienced with FMod because is much to expensive if you use it for commercial purposes. I think the licence fees are much to high...


nitti(Posted 2012) [#11]
Hi again, yeah the licenses seem steep, but i've read something about a shareware license (for individuals) that's 100$.

Anyway, about the midi:

some continous controllers don't seem to do anything, I am pretty sure i am sending the correct events since i do get the Pan to work, but these events don't do anything:

* polyphonic aftertouch (no continous controller, but no effect anyhow)
* breath control
* channel pressure
* sound timbre
* portamento

is there a small range of instruments these effects do anything with, do I have to setup some other stuff, or any other ideas ?

there might be more that don't work, I haven't written all these functions yet..


edit: Oh and what about that tempo ?
I've found this : link

but i don't follow, I set the tempo in the main midi header, I would like to know a formula so i could for example noteOff a note after 1 second precisely


when i set the midi tempo (in the main midi header) to 120
and I play 60 notes (by using noteOn and noteOff) and I give the noteOffs a deltatime of 240, the midi file lasts for 60 seconds , does this mean a deltatime of 240 always is a whole note ?

and I found a better explanation of some controller events here so I understand now why some of those controllers didn't seem todo anything, I first had to tell the system what variable they control...

Last edited 2012

Last edited 2012


AdamStrange(Posted 2012) [#12]
off the top of my head, there is no formula, you send the noteoff when you want it, so...

to stop a note after one second you send the nots off one second after you send the noteon.

Also things like breath control, portamento, etc are implemented by the device you are sending the information to.
This means if the device doesn't (for example) support portamento, then sending it that information wont do anything., the same goes for aftertouch etc.

Midi is a wide specification with lots of nice thins, but very few devices support all of them, and most only support the basics (note velocity, program change, pitch bend, etc)


Midimaster(Posted 2012) [#13]
Setting midi file time to 120 means that the player will play 120 quarter notes in one minute. if your song has 60 notes all on quarter positions the second will end after 30sec. If your notes are halfes, the song will last 60sec, because a half note equals 2 quarters.

The midi system is not based on a absolute but a relativ time. the minimum resolution is called 1 tick and it is long 1/96 of a quarter note.

a Eights is 48 ticks, a Sixteenth is 24 ticks, a triblet Eights is 32 ticks

so the notes on position 0, 96, 192 and 288 will sound like 4 quarter notes. in a midi tempo of 120 this "song" will last 2sec. With tempo 60 thy will need 4sec.

To each MIDI-ON you have to send a corresponing MIDI-OFF. In our example with the 4 notes, the corresponding NOTE-OFFs will have the position

Note-Off: 72, 148, 264 and 360 to simulate a normal "portato" style: Note last 75% of the delta.

Note-Off:for a legato I would use 90, 186, 282 and 382: Note last 93% of the delta.

Note-Off:for a staccato feeling I would use 24, 120, 216 and 302: Note last 25% of the delta.


If your sound is a drum sound you can send the MIDI-OFF imediately after the NOTE-ON: for drums I would use 4, 100, 196 and 292: Note last 25% of the delta.


AdamStrange:
....to stop a note after one second you send the nots off one second after you send the noteon....

This statement is correct, if you talk about sending real time midi outs via RTMidi module. But if you work with Midi files, you have to calculte in "notes" and never in "time". If your note should last for "1 half note" you give the NOTE-OFF a timestamp of 2*96 ticks behind the NOTE-ON. In a song played with tempo=120 this means, that the note will last 1sec.

Last edited 2012


nitti(Posted 2012) [#14]
Hi again thanks for the numbers,

but I just can't get the timing to work properly,
because of that i started using a real midi editor (anvil) to test and debug my generated files, I noted something weird,

many times the resulting midi would play fine in my program (using fmod) but when checked in Anvil it wouldn't do a thing, in that program there's also a event editor that shows a list of midi events and times.

most off the time there where only noteOffs in there !
i am 100% sure the functions I have written are not that faulty

	Method noteOn(deltaTime:Int, key:Byte, velocity:Byte)
		writeVariableLengthInt(deltaTime)
		stream.WriteByte($90 + channel)
		stream.WriteByte(key)
		stream.WriteByte(velocity)
	EndMethod
	
	Method noteOff(deltaTime:Int, key:Byte, velocity:Byte)
		writeVariableLengthInt(deltaTime)
		stream.WriteByte($80 + channel)
		stream.WriteByte(key)
		stream.WriteByte(velocity)
	EndMethod


so could that have something to do with that running status compression thing I've read about ? is that mandatory to use ?
anyway I feel i should fix this probelm first before trying to tackle the timing issues I also have..

any tips ?


nitti(Posted 2012) [#15]
I am sorry I figured the noteoff problem, I was using 128 for velocity at noteOn wich overflowed i guess..

ok back to the timing problems.


nitti(Posted 2012) [#16]
ok let me try again, a clearer case this time I hope

i'll use the drum channel so I don't need to think about durations of noteoffs

midi header tempo = 120

'write midi header
'write track header

For Local i:Int = 0 Until 120
Local note:Byte = 50
channel.noteOn(96,note,127)
channel.noteOff(0,note,0)
Next

'write track footer

so 120 times i play a quarter note on a quarter position, since the tempo is 120 this song should last 60 seconds right ?

instead it lasts 48 seconds, the event output from Anvil looks like this




Midimaster(Posted 2012) [#17]
I think you missunderstood midi tempo and quarter resolution in header chunk...

You have to descripe the resolution of a quarter note in the file header chunk

What did you write in dd dd?
4D 54 68 64 00 00 00 06 ff ff nn nn dd dd


I think you have also to write a 96 in there.

Then you will get a 120-quarter-notes-song of 60sec, when you play the song with a tempo of 120

Last edited 2012


nitti(Posted 2012) [#18]
Hi guys back from the front already, I think i sussed it (sort of)
I made the same midi file in Anvil as the one i thought i was generating and noted some differences:

song Midi ticks per beat in Anvil is 960
duration for each quarter played on quarter is also 960

in my generated file
song Midi ticks per beat = 120
duration for each quarter played on quarter is 96
wich explains why my midi file was roughly 4/5 the length is should be.

so when i set the midi tempo in header to 96 all is well....

next up i should look for the event to change the tempo and leave the midi ticks correct.


Midimaster(Posted 2012) [#19]
0xFF 0x51 0x03 0xtt 0xtt 0xtt
tttttt=microseconds/quarter note


so tempo=120 will become tt tt tt=500000

0xFF 0x51 0x03 0x07 0xA1 0x20


This message would normally appear in the first track chunk.


nitti(Posted 2012) [#20]
yeah, i found that too (the meta event part),

eventually I have rewritten part of my generator to output exactly the same as the midi editor i am working with (including track titles etc. ) it's a bit easier to compare the hex like that..
there are still some things happening I don't really get but i'll fiddle some more with it .

ahh I was using shorts/ints for that tt tt tt both aren't what I need i need a 3 byte value ...

Last edited 2012

Last edited 2012


nitti(Posted 2012) [#21]
okidoki atleast the writing of a three byte value is working,
but whats the relation between 120 & 500000
what do you use for microseconds ?

for example i found out i need to use 300000 for bpm 200, but how would i calculate that ?

and where does the 960 (midi ticks) go in this equation ?
------------
edit
sorry: should search first and then post questions : the relation is the number 60000000 aka microseconds per minute

60.000.000 / 120 = 500.000
60.000.000 / 200 = 300.000

Last edited 2012

Last edited 2012


nitti(Posted 2012) [#22]
awfully sounding but procedurally generated Midi file