Fixes for progress bar and applause sounds (#906)

* Control score sounds from UpdateGame instead of the thread pool

_PlayProgressBarSound started a Task that waited for the progress bar
duration and then closed whatever stream was stored in
_ProgressBarSoundStream. When switching rounds on the score screen
before that time had passed, an old task closed the progress bar
sound of the new round too early and played the applause of the
previous round, while the applause of the new round never played.

Remember when the progress bar sound was started and check it in
UpdateGame on the main thread instead. Starting a new progress bar
sound and leaving the screen now also stop a running applause.

* Sync progress bar sound with the score bar animation

The sound duration was computed from the points (10,000 points =
5 seconds), while the bar animation duration depends on the bar size
in the theme and only starts when the bar is first drawn. Both only
matched approximately and drifted apart with other themes.

Add CProgressBar.AnimatesMovement and CProgressBar.IsAnimating. The
score screen now stops the progress bar sound and starts the applause
as soon as no visible score bar is animating anymore. If the theme
has no movement animation, the previous duration is used.

* Make stream id allocation in CPlaybackBase thread-safe

Load assigned stream ids with _NextId++ outside of the lock. Load is
also called from other threads, so two streams could get the same id
and a later Close or Play could hit the wrong stream.

Use Interlocked.Increment instead. Ids still start at 0.

* Do not play the progress bar sound for 0 points

With 0 points the score bars do not move, so the progress bar sound
started and stopped again right away, which could be heard as a short
click. Do not start it at all if the highest score is 0.
This commit is contained in:
McSchneidar authored and GitHub committed 2026-10-16 05:21:34 +02:00
1 parent d5e5f3fbeb
commit d48426b04d
3 files changed
+69 -32

No files matched your search

+1 -1
View File
@@ -160,7 +160,7 @@ namespace Vocaluxe.Lib.Sound.Playback
return -1;
}
var stream = _CreateStream(_NextId++, medium, loop, effect);
var stream = _CreateStream(Interlocked.Increment(ref _NextId) - 1, medium, loop, effect);
if (stream.Open(prescan))
{
+57 -31
View File
@@ -17,8 +17,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Vocaluxe.Base;
using Vocaluxe.Base.Server;
@@ -155,6 +155,8 @@ namespace Vocaluxe.Screens
public override bool UpdateGame()
{
_UpdateProgressBarSound();
var players = new SPlayer[CGame.NumPlayers];
if (_Round >= 0)
{
@@ -295,39 +297,73 @@ namespace Vocaluxe.Screens
}
private int _ProgressBarSoundStream = -1;
private readonly Stopwatch _ProgressBarSoundTimer = new Stopwatch();
private double _ProgressBarSoundDuration;
private int _ProgressBarSoundMaxPoints;
private void _PlayProgressBarSound(int maxPoints)
{
// Stop any previous ProgressBar sound
_StopScoreSounds();
// Do not play in Karaoke Mode
if (CScreenSong.GetAudioMode() == EAudioMode.TR_AUDIOMODE_KARAOKE)
if (CScreenSong.GetAudioMode() == EAudioMode.TR_AUDIOMODE_KARAOKE || maxPoints <= 0)
{
return;
}
// Stop any previous ProgressBar sound
if (_ProgressBarSoundStream != -1)
{
CSound.Close(_ProgressBarSoundStream);
_ProgressBarSoundStream = -1;
}
// Calculate duration: 10,000 points = 5 seconds
var duration = Math.Min(maxPoints / 10000.0, 1.0) * 5;
_ProgressBarSoundDuration = Math.Min(maxPoints / 10000.0, 1.0) * 5;
_ProgressBarSoundMaxPoints = maxPoints;
// Play the sound
_ProgressBarSoundStream = PlaySound(ESounds.ProgressBar, CConfig.SoundEffectVolume);
_ProgressBarSoundTimer.Restart();
}
// Schedule stop after duration and start Applause sound
Task.Run(async () =>
private void _UpdateProgressBarSound()
{
if (!_ProgressBarSoundTimer.IsRunning || !_IsProgressBarAnimationFinished())
{
await Task.Delay((int)(duration * 1000));
if (_ProgressBarSoundStream != -1)
{
CSound.Close(_ProgressBarSoundStream);
_ProgressBarSoundStream = -1;
_PlayApplauseSound(maxPoints);
}
});
return;
}
_ProgressBarSoundTimer.Reset();
_CloseStream(ref _ProgressBarSoundStream);
_PlayApplauseSound(_ProgressBarSoundMaxPoints);
}
private bool _IsProgressBarAnimationFinished()
{
var animatedProgressBars = Enumerable.Range(0, CGame.NumPlayers)
.Select(p => _ProgressBars[_ProgressBarPoints[p, CGame.NumPlayers - 1]])
.Where(progressBar => progressBar.Visible && progressBar.AnimatesMovement)
.ToList();
if (animatedProgressBars.Count == 0)
{
return _ProgressBarSoundTimer.Elapsed.TotalSeconds >= _ProgressBarSoundDuration;
}
return !animatedProgressBars.Any(progressBar => progressBar.IsAnimating);
}
private void _StopScoreSounds()
{
_ProgressBarSoundTimer.Reset();
_CloseStream(ref _ProgressBarSoundStream);
_CloseStream(ref _ApplauseStream);
}
private static void _CloseStream(ref int stream)
{
if (stream == -1)
{
return;
}
CSound.Close(stream);
stream = -1;
}
private int _ApplauseStream = -1;
@@ -531,17 +567,7 @@ namespace Vocaluxe.Screens
private void _LeaveScreen()
{
if (_ApplauseStream != -1)
{
CSound.Close(_ApplauseStream);
_ApplauseStream = -1;
}
if (_ProgressBarSoundStream != -1)
{
CSound.Close(_ProgressBarSoundStream);
_ProgressBarSoundStream = -1;
}
_StopScoreSounds();
if (CScreenSong.GetAudioMode() == EAudioMode.TR_AUDIOMODE_KARAOKE)
{
+11
View File
@@ -144,6 +144,17 @@ namespace VocaluxeLib.Menu
_ProgressTarget = value;
}
}
public bool AnimatesMovement
{
get { return _AnimateMovement == EOffOn.TR_CONFIG_ON; }
}
public bool IsAnimating
{
get { return AnimatesMovement && _ProgressCurrent != _ProgressTarget; }
}
private float _ProgressCurrent;
private float _ProgressLast;