Skip to content

bugfix(audio): Reapply Bink movie volume on first frame - #3083

Open
CryoTheRenegade wants to merge 10 commits into
TheSuperHackers:mainfrom
CryoTheRenegade:bugfix/2850-video-audio-volume
Open

bugfix(audio): Reapply Bink movie volume on first frame#3083
CryoTheRenegade wants to merge 10 commits into
TheSuperHackers:mainfrom
CryoTheRenegade:bugfix/2850-video-audio-volume

Conversation

@CryoTheRenegade

@CryoTheRenegade CryoTheRenegade commented Aug 9, 2026

Copy link
Copy Markdown

Fixes #2850.

Movie audio (Bink) bypasses the Miles mixer and plays straight through DirectSound via BinkSoundUseDirectSound, so the only volume control is BinkSetVolume(). It was called exactly once in BinkVideoPlayer::createStream(), before Bink's audio output had actually started, so it never took effect. Movies played at full volume regardless of the Options sliders.

The fix extracts the volume calc into calculateMovieAudioVolume() and reapplies it on every decoded frame in BinkVideoStream::frameDecompress(), so the setting takes effect once audio is running and live slider changes during playback are picked up.

The volume formula itself is unchanged from retail: floor of 327 out of 32768, never literal 0, because Bink interprets 0 as "play at full volume". That quirk is why the original defensive comment exists; the floor isn't the bug.

Fixes TheSuperHackers#2850. Movie audio ignored the Options volume sliders because
BinkSetVolume was called once in createStream(), before Bink's audio
output had started, so it never took effect. Extract the volume calc
into calculateMovieAudioVolume() and reapply it on every decoded frame
in frameDecompress() so it takes effect and tracks live slider changes.
Comment thread Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp Outdated
Comment thread Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp Outdated
Fix the floor comment (formula is unchanged from retail, floor is 327
not 1) and drop the live-slider-change claim from the per-frame comment;
the Options menu isn't reachable during movie playback. The reason to
reapply every frame is that the one-shot createStream() call ran before
Bink's audio output started, so it never took effect.
@xezon xezon added Audio Is audio related Bug Something is not working right, typically is user facing Minor Severity: Minor < Major < Critical < Blocker labels Aug 9, 2026
Per review: don't reapply the volume every frame. The original defect
was that BinkSetVolume ran in createStream(), before Bink's audio output
existed, so it was discarded. Apply it once on the first BinkDoFrame(),
the earliest point it can actually take effect. Drops the createStream()
call and the per-frame poll in favor of a one-shot latch.
Comment thread Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp Outdated
Comment thread Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp Outdated
Comment thread Core/GameEngineDevice/Include/VideoDevice/Bink/BinkVideoPlayer.h Outdated
@Skyaero42

Copy link
Copy Markdown

I don't think the title is correct, as the volume is not applied every frame, but only once.

@CryoTheRenegade CryoTheRenegade changed the title bugfix(audio): Reapply Bink movie volume every frame bugfix(audio): Reapply Bink movie volume on first frame Aug 10, 2026
- Return the 327 floor instead of 0 on the null-audio path so it does not
  trip Bink's 'play at full volume' quirk.
- Trim the redundant formula comment.
- Restore retail tab alignment on m_handle/m_memFile and align m_volumeSet.
@CryoTheRenegade

Copy link
Copy Markdown
Author

I don't think the title is correct, as the volume is not applied every frame, but only once.

fixed

Comment thread Core/GameEngineDevice/Include/VideoDevice/Bink/BinkVideoPlayer.h Outdated
Comment thread Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp Outdated
Comment thread Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp Outdated
Per xezon's review. Replace the per-frame latch with a push model:

- VideoPlayerInterface gains a setVolume(Real) virtual (no-op default in
  VideoPlayer); BinkVideoPlayer overrides it to push the volume to every
  open stream's Bink audio output.
- MilesAudioManager::processPlayingList() pushes the speech volume to
  TheVideoPlayer whenever the volume changes, so movies that bypass the
  Miles mixer follow the sliders.
- BinkVideoPlayer::update() applies the volume once on the first frame a
  stream exists, fixing the original too-early set in createStream()
  (Bink's audio output is not running yet at creation, so it was lost).
- calculateMovieAudioVolume() now takes the speech volume as a parameter.
Comment thread Core/GameEngineDevice/Include/VideoDevice/Bink/BinkVideoPlayer.h Outdated
@Skyaero42

Skyaero42 commented Aug 10, 2026

Copy link
Copy Markdown

You are clearly using AI. While there is in principle nothing against it, I would strongly recommend you review the changes it makes - per our contribution guide.
The sloppy improvements currently take up unnecessary and valuable review time.

Comment thread Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp
Comment thread Core/GameEngineDevice/Include/VideoDevice/Bink/BinkVideoPlayer.h Outdated
Comment thread Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp
Comment thread Core/GameEngineDevice/Include/VideoDevice/Bink/BinkVideoPlayer.h Outdated
Comment thread Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp Outdated
Comment thread Core/GameEngineDevice/Source/VideoDevice/Bink/BinkVideoPlayer.cpp Outdated
Comment thread Core/GameEngineDevice/Include/VideoDevice/Bink/BinkVideoPlayer.h Outdated
TheAudio->getVolume(AudioAffect_Speech), mod, volume));
BinkSetVolume( stream->m_handle,0, volume);
DEBUG_LOG(("BinkVideoPlayer::createStream() - set volume"));
BinkSetVolume( stream->m_handle, 0, calculateMovieAudioVolume(TheAudio->getVolume(AudioAffect_Speech)) );

@bobtista bobtista Aug 19, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was the first-frame apply meant to come out in the 08-16 push? 9130511 moved the one-shot into BinkVideoPlayer::update() and dropped this createStream() call because it runs before Bink's audio output exists. On the current head update() is back to just VideoPlayer::update();, and this is the only per-stream BinkSetVolume left.

The MilesAudioManager push handles slider changes during playback, but opening a movie with the slider already low goes back to full volume, which is the #2850 symptom.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restored the delayed one-shot in BinkVideoPlayer::update() and dropped the createStream() BinkSetVolume call. Miles still pushes slider changes; this path covers opening a movie with the slider already low. The latch resets when no stream is left so the next movie gets the current volume too.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I see the push. One thing - the normal playMovie() replacement paths close the old stream and open the new one synchronously, so BinkVideoPlayer::update() never observes firstStream() == nullptr. m_volumeApplied remains TRUE, and the replacement movie can miss the delayed volume set. Could we reset when createStream() adds a stream instead?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. playMovie() close+open never lets update() see an empty stream list, so the latch stayed set. createStream() now clears it when a stream is added; update() still applies once the new stream is live.


virtual void notifyVideoPlayerOfNewProvider( Bool nowHasValid ) override { }

virtual void setVolume( Real volume ) override { }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FFmpegVideoPlayer derives from VideoPlayer, so it picks up this no-op and the volume plumbing is Bink-only. Override it there too?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FFmpegVideoPlayer now overrides setVolume and pushes the speech volume to the OpenAL movie stream. Initial volume is also applied when the stream starts, since that backend is already live by then.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait do we have OpenAL in upstream yet? This might not work until we get that landed :(

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, OpenAL is not in this repo yet. The FFmpeg override is behind RTS_USE_OPENAL so Miles/Bink builds keep a no-op, and the OpenAL gain path is just plumbing until that backend lands.

CryoTheRenegade and others added 2 commits August 21, 2026 08:42
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Audio Is audio related Bug Something is not working right, typically is user facing Minor Severity: Minor < Major < Critical < Blocker

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Video/movie audio ignores all volume settings (plays at full volume even when sliders are 0)

4 participants