vgmrips

The forum about vgm files
It is currently 2017-06-27, 5:46:58

All times are UTC + 1 hour [ DST ]




Post new topic Reply to topic  [ 6 posts ] 
Author Message
PostPosted: 2011-12-25, 21:55:30 

Programmers Programmers
Artists Artists
Offline
User avatar

Joined: 2011-12-12, 12:43:15
Posts: 75
For those making tools that take in MIDI files and convert them into a format for a sound engine (or even VGM directly if you dare), you need to know how to convert MIDI timestamps into whatever the sound engine uses. There are two types of timings: the standard one that almost all MIDIs use and the SMPTE one. I figured out the calculations for both and I'm posting them here.

This takes a delta time and turns in into Echo ticks or NTSC frames (60 Hz ticks):
  • Normal timing: delta × 3600 ÷ tempo ÷ ticks per beat
  • SMPTE timing: delta × 60 ÷ framerate ÷ ticks per frame

To convert to other speeds, change those numbers accordingly, e.g. for PAL frames (50 Hz) you'd use 3000 and 50, respectively (in other words, the output rate × 60 for the first, and just the output rate for the second).

Also, take into account that's the raw calculations. In practice it's likely you'll find events happening at less than a frame of distance, so if you use those calculations directly with integers (and thereby truncation) expect lots of issues. Either use fixed point (like I do in midi2esf) or use floating point.


Top
 Profile  
 
 Post subject:
PostPosted: 2011-12-25, 22:42:43 
Offline

Joined: 2011-11-30, 17:26:44
Posts: 454
Location: Italy
Great stuff :)

However, keep in mind that not everyone is familiar with MIDI files timings in general, so I'll post a link to a page with the complete documentation: http://segaretro.org/MIDI ;)

For reference, this is the WriteVgmWait subroutine from my YM2612 midi2vgm converter, which obviously only works with non-SMPTE timing:
Code:
Private Sub WriteVgmWait()
Dim tmp As Long
Dim tmp2 As Byte
VgmWait& = ((midiWait& * (Tempo& / 1000000)) * 44100) / TimeDivision
TotalVgmWait = TotalVgmWait + VgmWait&
tmp = VgmWait& \ 65535
For tmp2 = 1 To tmp
vgmData$ = vgmData$ + Chr$(&H61) + String$(2, 255)
Next tmp2
tmp = VgmWait& Mod 65535
Select Case tmp
Case Is = 0
Case Is < 17
vgmData$ = vgmData$ + Chr$(&H6F + tmp)
Case Is = 735
vgmData$ = vgmData$ + Chr$(&H62)
Case Is = 882
vgmData$ = vgmData$ + Chr$(&H63)
Case Else
vgmData$ = vgmData$ + Chr$(&H61) + Chr$(tmp And 255) + Chr$(tmp \ 256)
End Select
midiWait& = 0
End Sub
In this code, the TimeDivision variable is that value taken from the header when you're not using SMPTE, and it tells how many ticks there are in a quarter note (a typical value is 480, for example), and the Tempo& variable is read from the MIDI data (meta event $51), it defaults to 500000, and it's measured in microseconds per quarter note. The midiWait variable is incremented by reading the delta times between notes and events, and it's obviously reseted to 0 once the Vgm Wait has been written into the file. It is technically possible for a MIDI to have two events which happen faster than the VGM resolution (1/44100th of a second); in that case, you're fucked. Fortunately it's not a common case.

Note that the code above is probably obsolete by now, as I gave the complete source code of my YM2612 midi2vgm converter to ValleyBell, who's been improving it a lot. However, this works, and it can be a starting point for other people :)

_________________
My webhost decided to shut down most of my webspace without a warning. If you find any broken Digilander link in any of my posts (basically all of them should be), please inform me.


Top
 Profile  
 
 Post subject:
PostPosted: 2011-12-25, 23:24:07 

Staff Staff
Programmers Programmers
Musicians Musicians
Contributors Contributors
Offline
User avatar

Joined: 2011-12-01, 20:20:07
Posts: 2721
Location: Germany
Looks like it's time for me posting some code - give it the absolute tick (not delay) and get the absolute sample:
Code:
Private Function Tick2Sample(ByVal TickVal As Long) As Long

    Dim TempQud As Long
    Dim TickCount As Long
   
    TickCount = TickVal - MidTempo.BaseTick
    TempQud = CCur(TickCount) * VGM_SMPL_RATE / 1000@ * MidTempo.Tempo / 1000@ / MidTickRate
   
    Tick2Sample = MidTempo.SmplTime + TempQud

End Function

This code comes from mid2vgm OPLL (and mid2vgm PSG) and is slightly more accurate then Tom's code. (I think it's up to 100 samples every minute, but depends on the tempo and delays.)
VGM_SMPL_RATE is constant 44100, default Tempo is 500 000. (120 BPM)
CCur(...) is just a type-conversion to a 64-bit (sort of) integer type.
I'm dividing /1000 two times to prevent it from overflowing when multiplying with Tempo, btw.

The MidTempo.BaseTick and .SmplTime are refreshed when a tempo change occours, so they're usually 0.


I have to note that I don't check for SMPTE timing, so my tools may show run-time errors, hang or have some "undefined" behaviour, because of a negative MidTick value.


Top
 Profile  
 
 Post subject:
PostPosted: 2011-12-25, 23:48:35 

Programmers Programmers
Artists Artists
Offline
User avatar

Joined: 2011-12-12, 12:43:15
Posts: 75
I don't think that using absolute values is a good idea when you consider the tempo can change mid-tune (meaning you suddenly have to deal with ticks that can have different durations).

EDIT: I just realized I made topic ID #111.


Last edited by Sik on 2011-12-26, 0:03:17, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: 2011-12-26, 0:06:01 

Staff Staff
Programmers Programmers
Musicians Musicians
Contributors Contributors
Offline
User avatar

Joined: 2011-12-01, 20:20:07
Posts: 2721
Location: Germany
The trick is, that I update the MidTempo structure when the tempo changes.
I.e. I calculate all ticks and samples relative to the last tempo change. This includes the tick/sample of the next tempo change.

It works very well and is quite accurate even with odd tempos.


Top
 Profile  
 
 Post subject:
PostPosted: 2011-12-26, 1:24:28 
Offline

Joined: 2011-11-30, 17:26:44
Posts: 454
Location: Italy
ValleyBell wrote:
This code comes from mid2vgm OPLL (and mid2vgm PSG) and is slightly more accurate then Tom's code. (I think it's up to 100 samples every minute, but depends on the tempo and delays.)
I agree, it's probably more accurate than mine, since I introduce potential rounding errors with every delta time. I went for a progressive approach because I thought it was the best way to handle tempo variations.
In practice, though, I merged several songs converted with my converter and your ones, and I never noticed any audible timing difference.

Sik wrote:
I just realized I made topic ID #111.
If you want I can renumber it to 112 :2:

_________________
My webhost decided to shut down most of my webspace without a warning. If you find any broken Digilander link in any of my posts (basically all of them should be), please inform me.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC + 1 hour [ DST ]


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group