|
Post by srylain on Jul 3, 2015 15:32:07 GMT
I've been making my own music game, and would like to be able to take the already available MIDIs and use them in my game. As is right now, my game just loads the notechart from a .txt file, and each note has 3 values, the time in milliseconds that note is played, the type of note it is, and how long of a sustain it is (if it is one).
The problem that I have, is that after getting a program (that I made) to read a MIDI and spit out a file my game could read, is that when the tempo changes the timing values are usually messed up to the point where one note could be played at 200,000 milliseconds, and the next note could be back down to 192,000. I do take tempo changes into account when reading the MIDI, I just must be doing it wrong.
If anyone could help, thanks.
|
|
|
Post by djlastnight on Jul 3, 2015 17:37:48 GMT
If you use C#, I could help you.
|
|
|
Post by Æmetta on Jul 3, 2015 18:38:56 GMT
I'm not much of a programmer, but there's probably just some variable you're not accounting for. That kind of thing seems like it happens quite often when coding something, and once you figure out the problem then hopefully you can avoid it in the future.
If you haven't done so already, check out the source code of EoF. That program uses a similar system when importing MIDI files that converts time signatures and measures into real time in milliseconds. You may even learn some new things from it. If you can't figure it out (and I haven't looked at it, so I wouldn't know how easy it is to read), you could talk to Raynebc.
My guess on the problem you're having is that your program reads notes as if the tempo it's under is the tempo used throughout the whole song. Works fine without tempo changes, but as soon as the tempo doubles in the middle of the song it takes half as long to reach those notes. Try making note of the time when the tempo changes in milliseconds, then read all the notes (and other markers) after the tempo change relative to it. Add that time to the measure of the tempo change, and you have the real time of those notes.
|
|
|
Post by David on Jul 3, 2015 19:04:15 GMT
Yes, you must take the tempo changes into consideration and keep track of them throughout the MIDI even if they change multiple times between note events. Most MIDI's that are synced to an audio track need constant tempo adjustments to keep them in time. I also assume you understand MIDI uses a delta time(relative to the last note) rather than an absolute time. There are loads of other complex things to handle like running status bytes which through me off when I was first figuring it all out myself. This was my bible when I started out but I think it doesn't explain the running status bytes. If you get stuck post here and I'm sure Raynebc or I will go into some detail. www.idea2ic.com/File_Formats/midi.pdfThanks David
|
|
|
Post by raynebc on Jul 3, 2015 20:01:01 GMT
|
|
|
Post by JarheadHME on Jul 4, 2015 3:14:32 GMT
I don't really get the whole "converting midi to text to read it" thing. I just don't understand why so many people take this approach. (When I say so many, I mean the other instance I've seen of someone making a rhythm game that is similar to PS or GH or RB or something where they did the same thing)
|
|
|
Post by raynebc on Jul 4, 2015 3:35:21 GMT
Storing notes internally in real time format is one thing, but it doesn't make sense for a chart format because text wastes space unless you add compression.
|
|
|
Post by srylain on Jul 4, 2015 12:35:20 GMT
I'm using a library called NAudio to load the MIDIs (and I'm using C# since I'm making the game in Unity), and NAudio is able to do all the processing for me and is able to just give me the event tracks and is able to tell me the absolute time of each note.
I am keeping track of all the tempo changes and changing to the next one as soon as the current note falls inside of that interval, but I think what might be happening is that I'm either not changing tempos and accounting for it correctly or I'm using the wrong values or the wrong algorithms to change the ticks to millisecond values. But for example, Theme of Laura (from Silent Hill 2) works just fine, because there's only a 3 tempo changes throughout the whole song and it stays at pretty much the same tempo the whole time. Love Bites (But So Do I) on the other hand, changes pretty drastically from 125BPM to 117BPM at times, and that's what throws everything off.
(int)(Tempo * ticksPerQuarterNote * absTime / 60000.0);
That's the algorithm I use for getting the millisecond time as an integer, where Tempo is the current tempo in BPM, absTime is the absolute time of the note, and ticksPerQuarterNote is the resolution of the file or something. That algorithm I got from the FoF source, but it works the same as multiple others I'd found.
As for the reason I'm storing everything in text files, is just for right now since I'd like to get it working in the first place before adding MIDI functionality to my game. It also makes it much easier for me to fix stuff when something goes wrong since I can just go in and edit something easily.
|
|
|
Post by David on Jul 4, 2015 16:24:27 GMT
How much are you drifting? Could this be a Floating point precision issue maybe?
|
|
|
Post by srylain on Jul 4, 2015 18:08:25 GMT
I doubt it's because of precision errors, because there's times where it'll get desynced, and then it'll be back to normal, and then it'll desync again. When it does desync, the farthest I've seen was by about 10 seconds.
|
|
|
Post by raynebc on Jul 4, 2015 20:57:03 GMT
You'll probably get more accurate results keeping a list of each tempo changes timestamp and then looking up note times relative to its most recent tempo change as I describe here: www.fretsonfire.net/forums/viewtopic.php?p=353102#p353102It's the method I've been using to convert from MIDI timing to real timing for several years now.
|
|
|
Post by djlastnight on Jul 4, 2015 21:24:36 GMT
Check this class MidiTimeConverter.cs (5.16 KB) Here is the place to say thanks to TrojanNemo and raynebc for their direct or indirect help. Create instance of the class and then call GetRealNotes public method, which you should change for your needs. I include parts of this method just to get the idea (it will not compile). After you get the real note timings you should use Stopwatch or another very precise clock.
|
|
|
Post by TrojanNemo on Jul 5, 2015 2:13:11 GMT
The code sample that djlastnight provided is based on code I shared with him when he approached me (or I approached him?) for help with his own program. That's why he credits me, but really, it's mostly raynebc's work. He's the person to go to in my opinion on MIDI stuff.
Some suggestions since I also found myself having to learn this stuff not that long ago:
First, recognize that you're not interfacing with the MIDI directly, you're relying on NAudio. Mark Heath is a great guy and very sharp, but MIDI is not his forte nor the main purpose of the NAudio library. During my time with it raynebc and I found some issues with the source code, and I now use a modified and stripped down version of NAudio's MIDI library for my own uses. I believe from speaking with djlastnight that there is some precision loss in the distributed library you get from the NAudio project site. The whole point being that if something isn't working quite right, it might be your code, but it might also be NAudio. So keep an eye out, don't be afraid to ask for help.
Second, and I know it's been mentioned before: learn and understand running status. If you intend to use "all" Rock Band / Phase Shift / FoF MIDIs, you'll come across quite a few that use that feature. Typically, Rock Band authored MIDIs coming from REAPER won't have it, but you'll see them. If you're not accounting for running status, you'll run into errors when/if you assume a note will have a length and it doesn't, among other issues. The last version of NAudio I got and what I continue to use did not address running status natively, so we have to.
Third, review the code sample provided to you for keeping track of tempo and time signature changes. The code I provided djlastnight, which in turn I got mostly from raynebc, should give you real time of a note accurate to 5 decimal places, which should be more than enough for most uses of the MIDI file.
And lastly, again, feel free to ask for help, here or in the C3 forums (if you want me in particular to see posts). I know I bugged the hell out of raynebc and Mark Heath when I was learning my way around NAudio. Use the resources available to ya.
|
|
|
Post by djlastnight on Jul 5, 2015 8:55:56 GMT
The current version of the code provided has no precision lost (it really works perfect), but it can not load any midi file (some of ps mid files in example will give you similar format exception when calling the constructor, which is calling the NAudio's MidiFile constructor : Read too far 9110+16981!=51163). I did'n fix this, because it's not important for my needs.
|
|
|
Post by srylain on Jul 6, 2015 22:24:33 GMT
Thanks for all the help. I was able to use the code from that MidiTimeConverter class, and while it seems to work perfect for most songs, there are some songs that do still desync. Paradise City and Money For Nothing it both happens to. I haven't really had time yet to look through how it fully works, but will get around to doing that soon and see if I can fix anything.
|
|