ipatix / midi2agb

This is a reimplementation of Nintendo's mid2agb tool. It's not related to the original and is intended to fix a bunch of mid2agb's flaws.
MIT License
30 stars 5 forks source link

volume overflows on subsequent loops #3

Closed pizzagrrl closed 2 years ago

pizzagrrl commented 2 years ago

I'm inserting music into Mother 3 and noticed that after a song's first sequence through the audio will start to clip when clipping was not present initially. This will build as well and clips will show up in additional new places as loops continue.

ipatix commented 2 years ago

Can you provide me a sample MIDI file or an assembled version where I can see the wrongly generated song data?

pizzagrrl commented 2 years ago

Of course! I have been manually inserting the loop points based on some Pokemon Mid2AGB guides - I don't know if that could affect it because Logic has no way to insert loop points as midi events. I will include the .mid & .s file though.

Thanks for getting back so quickly!!

pizzagrrl commented 2 years ago

Also here is a bad OBS recording of the issue in action - first minute or so is a normal play of the song. At around 2:25 is the first clip. Around 5:55 is an example of how new clips just start to appear over time.

pizzagrrl commented 2 years ago

I figured out a way to have the loops in the midi file itself sorry for being redundant but just to show it definitely isn't a problem from me manually putting it in - problem persists and is exactly the same. Correctly looped .mid & .s.

ipatix commented 2 years ago

I cannot say this for sure but from what you're describing it seems very likely to me that you didn't do the looping correctly.

When you're looping manually in the .s file (or in the mid file with legacy mid2agb) you have the problem that when looping back to the start or mid of the song, the music player retains the same state from the end of the loop. This is bad because at least in your case your song changes for example volume through the mid of the song and that can result in a case where the music player is in a different state depending on when it's start from the beginning or when it's looping back from the loop end.

Here is an example to illustrate:

start_0:
@ 000   ----------------------------------------
        .byte           VOL   , 127
        .byte   W96
@ 001   ----------------------------------------
loop_0:
        .byte           N24   , Cn1 , v127
        .byte   W24
        .byte           N24   , Dn1 , v127
        .byte   W24
        .byte           VOL   , 32
        .byte           N24   , En1 , v127
        .byte   W24
        .byte           N24   , Fn1 , v127
        .byte   W24
        .byte   GOTO
@ 002   ----------------------------------------
        .word   loop_0

So what's going on here?

There are four notes in this little piece of track, two volume change events. When played from the start, the first two notes will play with volume set to 127 and the last two with volume set to 32. Now, when the song loops back to loop_0, it does not pass the VOL, 127 which comes before loop_0 event and so all four notes will be played with volume 32. So it's playing in a way that was very likely not intended.

So when looping manually, you have to be aware of this and insert events at the loop start (or loop end) that set the state to what it's supposed to be at loop start.

Now, midi2agb already has a feature to automatically do this, but to utilize it you have to loop it using the supported mechanisms. See the readme for details.

If you can confirm that this is indeed what's going on, please close the issue since there is nothing midi2agb can do about it if it doesn't know where the loop is (this will also happen with legacy mid2agb).

EDIT: Just read your second reply, I'll investigate...

ipatix commented 2 years ago

Okay, so I've been staring at this now knowing what's going on and I think I have an idea of what's causing it. In the picture below you can see the section where it clips aligned in such a way that above you can see the same section where it previously does not clip:

Bildschirmfoto von 2022-03-24 14-53-15

The red box marks the section where the overflow occurs on the lower track and does not on the upper track. Initially I had absolutely no idea what would cause it since almost everything else looks really quite similar and all the levels are pretty much identical. Could it be a audio compression issue from your upload? Nah, unlikely (although I still recommend going lossless if something should happen next time). But then I discovered what's visible in the pink box and I was a bit confused.

So what's happening here? Everything is perfectly aligned in the red box and then suddenly there is this discontinuity in the pink box. And then I noticed that the time diffference in the purple box is exactly 16.7 ms, which is exactly one frame in terms of GBA timing. What's going on here is that since songs may play at arbitrary speeds, the music player code has to fit the time line of the midi ticks to GBA frame times (~60 Hz). Which means that if a song plays more than 60 midi ticks per second (> 150 bpm) the music player occasionally has to process more than one midi tick per GBA frame. If it plays slower than 60 midi ticks per second (< 150 bpm) it sometimes has to skip processing in a GBA frame. So all midi events get realigned to a time grid at a rate of 60 Hz.

This process of retiming of the events can cause discontinuities such as visible in the pink box. Since we cannot plot the music player code's counter variable that's responsible for this retiming, we can't say for sure when exactly this happens by just looking at an arbitrary piece of recording. However, I can tell you for sure that they will very likely be not in sync for those two recordings visible on the screenshot above.

There is no way to reliably make sure for these counters to stay in sync (except fixing your bpm to 150) and usually that's not a problem at all. In your case you're probably very close to the maximum possible level and due to unlucky circumstances the bass or something will be at a different phase than the snare drum or something and cause an ever so slight difference to push the levels beyond the maximum allowed.

"For science" you could try to make your songs speed to a bpm 150 and see what happens. It may not fix the problem but it should make the results more consistent.

pizzagrrl commented 2 years ago

Yes! Switching it to 150 bpm worked (and with this song in particular it really doesn't make a big difference in terms of vibe so works perfectly). I'm on the fifth or sixth loop now and not a pop! Unfortunately not every song will function perfectly at 150 and because of Mother 3's battle system I can't just set certain tracks to 150 for exporting, but this is a HUGE step forward so thank you so much for the help!! Especially since it turned out to be a GBA problem and not midi2agb at all. Thanks again!

ipatix commented 2 years ago

If you don't want to adjust the tempo, just lower the volume a bit and you should be fine. Lowering the volume or note velocity just at this point may also be a way to "fix" it.