Author Topic: BASS_ASYNCFILE causing weird stream behavior on Windows  (Read 1442 times)

Wieku

  • Posts: 3
Related topics:
https://github.com/ppy/osu/issues/18652
https://github.com/Wieku/danser-go/commit/b5a6ce356ce4b9efe151f47a85b1e045cb32da2c

Hi,

It seems that since like 2 weeks, when audio stream is initialized with BASS_ASYNCFILE flag and its position is being modified by ChannelSetPosition, it causes ChannelGetPosition to report delayed values and music seems to start playing earlier than target position.

From initial investigation it seems:
  • It's happening only on Windows (at least builds 19043 and 19044)
  • It's not deterministic, sometimes it works correctly, sometimes not
  • Setting position to 0 works correctly
  • Reproducible on BASS 2.4.16.1 and 2.4.16.7
  • Not enough data, but seems happening only on AMD machines as I'm not able to reproduce it on a machine with Intel Coffee Lake CPU (comparable setup uses the same audio device (Scarlett Solo) and has the same Windows 10 build (19043))
  • It's also happening when BASS is initialized with device_id 0 and data is being pulled from main mixer with ChannelGetData, in this case though it hints that seek is not working correctly because ChannelGetPosition is not used

Comparison recorded from affected system:

It's hard to tell if it's an undefined behavior in Windows' file handling in BASS or it's a Windows/AMD bug so assistance with that issue would be very helpful.

Thanks!

Ian @ un4seen

  • Administrator
  • Posts: 25718
That sounds strange, as BASS_ASYNCFILE shouldn't affect the accuracy of setting or getting the position. But it might cause a BASS_ChannelSetPosition call to take a bit longer, eg. if that has to wait for an asynchronous read to finish before seeking in the file. Perhaps that's making a difference? You could try logging and comparing the time taken by the BASS_ChannelSetPosition calls to see if there is indeed a difference.

Looking at the code in your GitHub link, it appears that all playback is through a mixer? Is the issue that 2 sources that should start at the same time in the mix are not? If so, please try locking the mixer to add the sources, which will ensure that they start together in the mix. For example:

Code: [Select]
BASS_ChannelLock(mixer, true); // lock mixer
BASS_Mixer_StreamAddChannel(mixer, source1, flags); // add 1st source
BASS_Mixer_StreamAddChannel(mixer, source2, flags); // add 2nd source
BASS_ChannelLock(mixer, false); // unlock mixer

Wieku

  • Posts: 3
Quote
it appears that all playback is through a mixer?

Yes, all audio goes through a mixer. Same in osu!.

Quote
Is the issue that 2 sources that should start at the same time in the mix are not?

The issue is with sudden breakage of audio seeking. The breakage seems to happen only when ChannelSetPosition is called with non-zero position.

It started appearing like 2 weeks ago. It was working correctly before. And yes, when it happens, people reported a sligtly bigger delay before music starts playing after seek. It doesn't happen every time though (like 20% chance to get that issue). After that music starts playing at earlier point than anticipated, but position is getting reported as if it resumed playing at correct position.

Squashed flow of playing an audio file:

Code: [Select]
BASS_SetConfig(BASS_CONFIG_DEV_NONSTOP, 1)
BASS_SetConfig(BASS_CONFIG_VISTA_TRUEPOS, 0)
BASS_SetConfig(BASS_CONFIG_BUFFER, 100)
BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 5)
BASS_SetConfig(BASS_CONFIG_DEV_BUFFER, 10)
BASS_SetConfig(BASS_CONFIG_DEV_PERIOD, 10)
BASS_SetConfig(68, 1) // BASS_CONFIG_MP3_OLDGAPS

BASS_Init(-1, 44100, 0, NULL, NULL)

masterMixer := BASS_Mixer_StreamCreate(44100, 2, BASS_MIXER_NONSTOP)
BASS_ChannelSetAttribute(masterMixer,BASS_ATTRIB_BUFFER, 0)
BASS_ChannelSetDevice(masterMixer, BASS_GetDevice())

BASS_ChannelPlay(masterMixer, 0)

channel := BASS_StreamCreateFile(0, filePath, 0, 0, BASS_STREAM_DECODE | BASS_STREAM_PRESCAN | BASS_ASYNCFILE)

fxChannel := BASS_FX_TempoCreate(channel, BASS_FX_FREESOURCE | BASS_STREAM_DECODE)

BASS_Mixer_StreamAddChannel(masterMixer, fxChannel, BASS_MIXER_CHAN_NORAMPIN|BASS_MIXER_CHAN_BUFFER)

BASS_Mixer_ChannelSetPosition(fxChannel, BASS_ChannelSeconds2Bytes(fxChannel, pos), BASS_POS_BYTE)

currentPos := BASS_ChannelBytes2Seconds(fxChannel, BASS_Mixer_ChannelGetPosition(fxChannel, BASS_POS_BYTE)) //if we call previous function with pos > 0 and BASS_ASYNCFILE is used in StreamCreateFile, subsequent calls to this function will start returning wrong values compared to music being heard


Turning off ASYNCFILE fixes the problem completely.

I will try to make some diagnostics about seek delay with affected people.

Ian @ un4seen

  • Administrator
  • Posts: 25718
The issue is with sudden breakage of audio seeking. The breakage seems to happen only when ChannelSetPosition is called with non-zero position.

It started appearing like 2 weeks ago. It was working correctly before. And yes, when it happens, people reported a sligtly bigger delay before music starts playing after seek. It doesn't happen every time though (like 20% chance to get that issue). After that music starts playing at earlier point than anticipated, but position is getting reported as if it resumed playing at correct position.

Strange that it should suddenly start happening. Did you make any changes (eg. code or DLLs) just before that? Is it happening with a particular file format or all? If you haven't already done so, please try with WAV files. If you can, please also try without the BASS_FX_TempoCreate call (ie. plug the decoder directly into the mixer), to check if the problem is in any way related to that stuff.

Also confirm what BASSmix version you're using, ie. what BASS_Mixer_GetVersion returns.

Wieku

  • Posts: 3
Code: [Select]
BASS Version:     2.4.16.1
BASS FX Version:  2.4.12.1
BASS Mix Version: 2.4.10.0

Although it also happens with osu!lazer which uses:
Code: [Select]
BASS Version:     2.4.16.7
BASS FX Version:  2.4.12.6
BASS MIX Version: 2.4.11.1

Quote
Is it happening with a particular file format or all?
IIRC all files were mp3.

Quote
Did you make any changes (eg. code or DLLs) just before that?
No, BASS related code/dlls haven't been touched in at least 8 months. I just found that I got a first notice about this on 18th of May this year. AMD CPU as well.

ppy

  • Posts: 4
To add to the weirdness, even without the async flag, at least one user is reporting discrepancies to a lesser degree (https://github.com/ppy/osu/issues/18909). Hard to say if this is the same issue (maybe Wieku can confirm or deny whether they can reproduce the lesser differences?)

Ian @ un4seen

  • Administrator
  • Posts: 25718
After reading the GitHub issue links, it unfortunately still isn't entirely clear to me what the problem actually is, as I'm not familiar with the game/terms being discussed. Can you say the problem is definitely happening in a BASS_Mixer_ChannelSetPosition call, or might it be in BASS_Mixer_ChannelGetPosition or even elsewhere? And what is the reference that's being compared with, ie. how do you know it's off target? Once it goes off, does it stay that way and by the same amount?

Wieku mentioned all files being MP3. Would it be possible to try with WAV files instead to see if that makes any difference? If it only happens with MP3 files then perhaps it's a pre-scanning (BASS_STREAM_PRESCAN) issue.

msitton

  • Posts: 9
I just want to chime in here on this thread, I'm a developer of a widely played rhythm game(Clone Hero) which also makes use of the bass library. And we also see this behavior using BASS_ASYNCFILE along with Mixers, I can also confirm that this issue only happens with MP3 files on my end.


Ian @ un4seen

  • Administrator
  • Posts: 25718
Please first check that you're using the latest BASS (2.4.17) and BASSmix (2.4.12) versions with BASS_GetVersion and BASS_Mixer_GetVersion, and try upgrading if either isn't the latest. Also check that you're using the BASS_STREAM_PRESCAN flag (with BASS_StreamCreateFile) to enable more accurate seeking in MP3 files. If the problem persists, is it always happening with particular MP3 files and not with others? If so, please upload an affected file to have a look at here:

   ftp.un4seen.com/incoming/

It looks like your game is multi-platform, so please also confirm which platform(s) are affected by the problem.

msitton

  • Posts: 9
Please first check that you're using the latest BASS (2.4.17) and BASSmix (2.4.12) versions with BASS_GetVersion and BASS_Mixer_GetVersion, and try upgrading if either isn't the latest. Also check that you're using the BASS_STREAM_PRESCAN flag (with BASS_StreamCreateFile) to enable more accurate seeking in MP3 files. If the problem persists, is it always happening with particular MP3 files and not with others? If so, please upload an affected file to have a look at here:

   ftp.un4seen.com/incoming/

It looks like your game is multi-platform, so please also confirm which platform(s) are affected by the problem.

I totally forgot about this thread until now, and just saw your response. I had thought that we were on the latest version, but i just checked and updated to the latest bass/bassmix version i'll see if i can still reproduce the issue and report back.

I do have a file i usually use to reproduce it on my end but let me do a bit more testing to see what others i can come across with the issue.

As for platforms 95% of our players are on windows so I've only seen the issue reported on that platform, I'll have to test if i can reproduce it on other platforms.

msitton

  • Posts: 9
Ok so i can still reproduce the issue on windows with the latest version of bass/bassmix/bassfx

So where this issue occurs in our project is by seeking multiple times within a short period of time which means that this isn't a consistent issue but it's easy to reproduce.

Here's a video of it occuring: (edit i just realized this doesn't have audio, working on re-doing this video)
https://youtu.be/OLbFPv3uNWs

edit here's the new video:
https://youtu.be/nVBlvxFSAOE

i've uploaded the audio file from this video to the ftp as "msitton-asyncfile-seek-bug - jarvis9999 - prevail.mp3"
« Last Edit: 15 Feb '23 - 19:06 by msitton »

msitton

  • Posts: 9
Additional update:

I'm seeing this behavior now regardless of async file having been set or not. This previously wasn't the case (it seems unrelated to the bass update since the behavior is the same between the old and new version)

So that's rather odd.

One way that i find easiest to reproduce it is attempting to seek to the beginning of the song. And then this seems to occur, and only on mp3 doesn't happen on ogg vorbis. Testing through multiple mp3 files i do get the same behavior so i don't think it's specific to the one i've provided.
« Last Edit: 15 Feb '23 - 19:40 by msitton »

Ian @ un4seen

  • Administrator
  • Posts: 25718
Thanks for the additional info. It is sounding a lot like the issue that another user reported recently (off forum) regarding an "offset between rhythm games that use bass (osu, quaver, malody) and games that do not (etterna)". The root of it is that MP3 encoding introduces a delay/gap at the start, ie. the sound starts later than in the original file. The encoder will usually include info on the delay in the file, so that the decoder can remove the delay. BASS does just that, and it turns out the other game's decoder doesn't, which is why there's an offset difference between them. The user fixed the problem by adjusting for that.

In the case of your example MP3 file, it was created by the LAME encoder and does include delay info, and as a result BASS is skipping the first 1105 samples.

msitton

  • Posts: 9
Thanks for the additional info. It is sounding a lot like the issue that another user reported recently (off forum) regarding an "offset between rhythm games that use bass (osu, quaver, malody) and games that do not (etterna)". The root of it is that MP3 encoding introduces a delay/gap at the start, ie. the sound starts later than in the original file. The encoder will usually include info on the delay in the file, so that the decoder can remove the delay. BASS does just that, and it turns out the other game's decoder doesn't, which is why there's an offset difference between them. The user fixed the problem by adjusting for that.

In the case of your example MP3 file, it was created by the LAME encoder and does include delay info, and as a result BASS is skipping the first 1105 samples.

I'm not really sure how that explains the inconsistency of seeking though. If that were the issue it would be consistent. The issue we have seems to be intermittent.

Ian @ un4seen

  • Administrator
  • Posts: 25718
Check that the BASS_STREAM_PRESCAN flag is used in the BASS_StreamCreateFile calls, as that's required for accurate seeking in MP3 files (it scans the file for seek points).

msitton

  • Posts: 9
Check that the BASS_STREAM_PRESCAN flag is used in the BASS_StreamCreateFile calls, as that's required for accurate seeking in MP3 files (it scans the file for seek points).

Yes we do use the prescan flag on our streams

Ian @ un4seen

  • Administrator
  • Posts: 25718
That is puzzling then. Is the problem definitely only happening with MP3 files? For example, if you convert an affected MP3 file to WAV and then play that instead, then the problem doesn't happen? If so, perhaps it's related to how long seeking (BASS_ChannelSetPosition) takes, ie. a delay there could affect your timing? MP3 seeking (with BASS_STREAM_PRESCAN enabled) may take longer than other formats (certainly WAV) because it involves decoding some data from before the target position to warm-up the decoder (there would otherwise be some silence first).

msitton

  • Posts: 9
Another part of this, is that from my understanding it only occurs when utilizing bassmix, previously before we used it in the project we did not have this issue and it only came about after we switched to using it.

msitton

  • Posts: 9
Ok so i made some progress figuring this out on my end today:

I built a standalone console app to try and reproduce this desync issue but couldn't get it to reproduce. After that i tried a few things going through our codebase, and stumbled on a fix.

So to reference the docs about BASS_Mixer_Create
Quote
The playback buffer can be flushed by calling BASS_ChannelPlay (restart = TRUE) or BASS_ChannelSetPosition (pos = 0). That can also be done to restart a mixer that has ended.

basically we use the mixer position plus clearing the buffer when pausing/seeking and keeping an accumulated pre-seek/pause value to have the full audio position.

It appears that under certain cases, calling BASS_ChannelSetPosition (pos = 0) then after not doing anything else calling BASS_ChannelPlay (restart = TRUE)  will cause a desync specifically on the mp3 files i've been testing (not sure why this is the case as it doesn't make sense as to be format specific) But essentially calling BASS_ChannelPlay without restart seems to fix the issue in our game. Maybe there is an issue there to be looked at?

I don't really understand why the following process causes this issue:

pause mixer using Bass_ChannelPause
clearing the mixer buffer using BASS_ChannelSetPosition (pos = 0)
Seeking individual mixer streams using BassMix.ChannelSetPosition
Play mixer using BASS_ChannelPlay (restart = TRUE) <- desync happening here
« Last Edit: 15 Mar '23 - 14:21 by msitton »