Skip to content

v1.8 ~ audio offset?! 1000Hz pollrate?!?! no more "magic bpm"! ~

Choose a tag to compare

@CrazyRedMachine CrazyRedMachine released this 30 Jul 21:11
· 127 commits to main since this release

Back to "real" changes this time, with two BIG features and a couple extras.
Only one big hurdle left to complete my long yearned for pop'n dream (maybe for v2.0 ? ๐Ÿ‘€ )

Special thanks

Huge thanks to Roganis, pulse, though, xinrin, Milo and babyzionov for discussing and/or beta-testing the new big features.
I think that without them these new features would have been released in a much less refined state, so once again thank you very much :D

Thanks to r2nk226 whose practice mode patch taught me how to display info on screen, which proved VERY useful for this version.

Thanks to leonid, fishluv, though and pulse who shared a great deal of information about magic bpm on various discords along the years :D

New additions

<disable_keysounds>

You can now disable keysounds. They will be played automatically at the right time.

This actually makes the game rewrite the whole chart on-the-fly when it's loaded, to convert it into a non-keysounded one, how cool is that!

Note: extra keysounds tied to buttons after the end of the song are still there (like Kenshiro screaming "atatatatatatata" at the end of Hokuto no Ken opening theme, or "Je t'aime Oscar!" at the end of Versailles no Bara), I wouldn't want to take away the fun from these charts :D

<audio_offset>

This allows to compensate the game audio latency (especially needed for stock cabs which can't benefit from win10 low-latency audio driver, or some DAC setups, etc..).

This patch is the only reason why I worked on disabling keysounds :D

If there are no keysounds, then judgement timing offset (aka "keysound_offset" option) is equivalent to an actual audio offset.

This option is added for convenience, so that you don't have to set the other two options separately.

Note: you cannot use audio_offset if keysound_offset is already set.

<enhanced_polling> (decoupled 1000Hz polling)

Pop'n engine judges your timing based on the gap in milliseconds between your button press and the "expected time", however it checks your inputs only once every 16ms.

This means it cannot accurately track your timing, and what should be a 40ms timing window ( (-22ms <= gap <= +18ms) <=> cool ) becomes a "sometimes 2, sometimes 3 frames" (40/16.6666 = 2.4 frames at 60Hz).

In other words, "40ms timing window" is "sometimes 33.33333ms, sometimes 50ms" instead.

This leads to a number of issues which have been noticed and discussed over the years, like the infamous "magic BPM".

You can skip the next subsection if you're already familiar with these issues : long story short this patch solves them :D

Magic BPM?

This was first observed as "higher score on specific songs when playing on home setup vs playing on cab".

This is a game engine quirk where if you run the game at 60Hz, your score becomes higher than it should be on songs with base BPM 100, 150, 180, or 200.

Indeed, running the game at 60Hz (instead of 59.9Hz like a CRT cab would), for these songs the "sometimes 2, sometimes 3 frames" becomes a "mostly 3 frames"
(it's almost like having a 50ms cool window instead of a badly implemented 40ms one :D)

Further studies have shown that there are in fact issues with any chart whose BPM divides 3600 (and this number has a lot of divisors). See the appendix for more info.

The converse is also theoretically true, some songs at 59.9Hz probably have more 3-frame windows than they should...
(a 3-frame window is always too long while a 2-frame window is always too short anyways)

How it is solved

This patch setups a separate thread which polls your inputs every ms and keeps track of when each button was first pressed, which allows to readjust the judgement to what it should be.

Without the patch:

  13ms too late, you've missed the cool window by 5ms, 
  you have a "great"

With the patch:

  "..., oh but you've been pressing the button for 10ms already 
  => you're really only 3ms too late, you're within the cool window, 
  you have a cool"

TL;DR: This makes the game scoring more accurate (entirely millisecond-based timing), and most importantly, independent of the framerate you're running the game at.

Note: You might find some songs become harder to score on, especially if you were running at 60Hz before, but I assure you this new scoring is "correct" ๐Ÿ˜…

<enhanced_polling_priority>

You can set a custom thread priority for the polling. This is an underlying call to SetThreadPriority so you can check Win32 API documentation for more info about the different possible values.

I personally have to use "1" on my SD BemaniPC hardware else it wouldn't be able to keep up with 1000Hz polling rate.

Setting it to a higher value had a weird effect on official hardware, where it seemed like the timing judgement function was running a bit late (I would get frequent offset corrections above 16ms... while it is not really a problem since we're correcting it appropriately, I think it should not happen :p)

<enhanced_polling_stats>

You can enable polling stats display to ensure the system is working properly, and maybe find which thread priority is best suited for your system.

Colors have been chosen so that it's readable over the game lanes during song, but if you have trouble, the stats are also positionned in a way that "Sudden+" option will give you a nice black background for them :D

Note: It is normal not to see offset correction values updating while in menus. This only happens during songs.

<debounceย >

Since we are running our own input management with enhanced_polling (and since I had to disable calls to usbPadReadLast() on real IO for it to work on cabs), I added a "debounce on button release" which you can set to your needs (I suggest keeping the default value of 5ms, you can bump it a little if you encounter unwanted double-taps).

I'm only debouncing on release because the game doesn't have strict release timing, and this allows to keep a quick reaction to new inputs.

Note: this requires enhanced_polling.

Note for cab owners: I've thoroughly checked and usbPadReadLast() serves no other purpose than debouncing inputs, so we're not losing anything by disabling these calls and rolling our own.

<back_to_song_select>

This option allows you to press numpad 9 on option select (before or after pressing the yellow buttons for settings), in order to go back to song selection. This is useful if you picked that song by mistake (or have second thoughts after seeing the hellish solfan surprise :D)

This causes minor issues with song select menu sounds, which I won't bother fixing, so I didn't integrate it in quick_retire and instead put this separately in the "experimental patches" category. Thanks for your understanding.

Note: when you're back to song select, the cursor is on the last played song rather than the one you just picked. This might get fixed in a later version as ideally I'd like to fix other cursor issues with omni songs.

enhancements

Aside from a bit of code cleanup, here are the notable enhancements.

quick retry

It is now possible to trigger a retry from the result screen.

This will send your current score and bring you back to option select for the same song (which means you can hold 8 to skip option select as well if you want to retry with same options).

Note: Because numpad 7 is used on result screen for "add to favorites", quick retry has been moved to numpad 8 instead. This also applies to quick retry during song and option select skip.

force unlock

force_unlock_deco is gone. The new engine causes less trouble when a string is not found, so there's no need to separate the options anymore, force_unlock will always attempt to unlock deco as well if it can.

xml rewrite

With the number of options growing, popnhax.xml was becoming hard to read. Groupings and rearrangements have been made, I hope it will give users an easier time browsing this file.

APPENDIX: Magic BPM data summary (courtesy of fishluv)

BPMs of 100, 150, 180 and 200 are "magic bpm" because any 16th note in them would get a consistent 3 frame cool timing window.
But in reality, there are also other bpms where it's not the 16ths but rather other note speeds which benefit from this lenient cool timing :

Note speeds equivalent to 16ths at 100, 150, 180, and 200.

64ths at  25 ---  45  50

48ths at ---  50  60 ---

32nds at  50  75  90 100

24ths at --- 100 120 ---

16ths at 100 150 180 200

12ths at --- 200 240 ---

8ths at  200 300 360 400

6ths at  --- 400 480 ---

4ths at  400 600 720 800

effective list of bugged bpms = 25, 45, 50, 60, 75, 90, 100, 120, 150, 180, 200, 240, 300, 360, 400, 480, 600, 720, 800

In conclusion, any BPM which is a divisor of 3600 is "magic".