Fix possible crashes and glitches when playing song previews (#898)

* Add AutoplayPending

* Add locks

* Add Base Update

* Add Base Update

* Add locks
This commit is contained in:
McSchneidar authored and GitHub committed 2026-10-06 05:20:14 +02:00
1 parent 52bdf3950e
commit e7fc959def
4 files changed
+184 -102

No files matched your search

+34 -12
View File
@@ -177,6 +177,7 @@ namespace VocaluxeLib.Menu.SongMenu
protected int _AutoplayDelayinMs = CBase.Config.GetAutoplayPreviewDelay(); protected int _AutoplayDelayinMs = CBase.Config.GetAutoplayPreviewDelay();
protected System.Timers.Timer _AutoplayTimer = new System.Timers.Timer(); protected System.Timers.Timer _AutoplayTimer = new System.Timers.Timer();
protected volatile bool _AutoplayPending = false;
private SColorF _ColorInternal; private SColorF _ColorInternal;
protected SColorF _Color protected SColorF _Color
@@ -228,12 +229,22 @@ namespace VocaluxeLib.Menu.SongMenu
_Initialized = true; _Initialized = true;
} }
public abstract void Update(SScreenSongOptions songOptions); public virtual void Update(SScreenSongOptions songOptions)
{
if (_AutoplayPending)
{
_AutoplayPending = false;
_PreviewSelectedSong();
}
}
public abstract void OnShow(); public abstract void OnShow();
public virtual void OnHide() public virtual void OnHide()
{ {
_AutoplayTimer.Stop();
_AutoplayPending = false;
var check = CBase.Graphics.GetNextScreenType(); var check = CBase.Graphics.GetNextScreenType();
if (CBase.Graphics.GetNextScreenType() == EScreen.Sing) if (CBase.Graphics.GetNextScreenType() == EScreen.Sing)
{ {
@@ -384,6 +395,9 @@ namespace VocaluxeLib.Menu.SongMenu
protected virtual void _EnterCategory(int categoryNr) protected virtual void _EnterCategory(int categoryNr)
{ {
_AutoplayTimer.Stop();
_AutoplayPending = false;
if (!_Initialized) if (!_Initialized)
{ {
return; return;
@@ -400,6 +414,9 @@ namespace VocaluxeLib.Menu.SongMenu
protected virtual void _LeaveCategory() protected virtual void _LeaveCategory()
{ {
_AutoplayTimer.Stop();
_AutoplayPending = false;
if (!_Initialized) if (!_Initialized)
{ {
return; return;
@@ -429,6 +446,9 @@ namespace VocaluxeLib.Menu.SongMenu
protected void _ResetPreview(bool playBGagain = true) protected void _ResetPreview(bool playBGagain = true)
{ {
_AutoplayTimer.Stop();
_AutoplayPending = false;
if (_PreviewNrInternal == -1) if (_PreviewNrInternal == -1)
{ {
return; return;
@@ -454,11 +474,19 @@ namespace VocaluxeLib.Menu.SongMenu
protected void _InitializeAutoplayTimer() protected void _InitializeAutoplayTimer()
{ {
_AutoplayTimer.Stop();
_AutoplayPending = false;
_AutoplayTimer.Interval = _AutoplayDelayinMs; _AutoplayTimer.Interval = _AutoplayDelayinMs;
_AutoplayTimer.AutoReset = false; _AutoplayTimer.AutoReset = false;
_AutoplayTimer.Elapsed += (object sender, System.Timers.ElapsedEventArgs e) => { _PreviewSelectedSong(); }; _AutoplayTimer.Elapsed -= _OnAutoplayTimerElapsed;
_AutoplayTimer.Elapsed += _OnAutoplayTimerElapsed;
} }
private void _OnAutoplayTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
_AutoplayPending = true;
}
protected void _AutoplayPreviewIfEnabled() protected void _AutoplayPreviewIfEnabled()
{ {
if (CBase.Config.GetAutoplayPreviews() == EOffOn.TR_CONFIG_ON) if (CBase.Config.GetAutoplayPreviews() == EOffOn.TR_CONFIG_ON)
@@ -469,15 +497,9 @@ namespace VocaluxeLib.Menu.SongMenu
protected void _PlayPreviewAfterDelay() protected void _PlayPreviewAfterDelay()
{ {
if (!_AutoplayTimer.Enabled) _AutoplayPending = false;
{ _AutoplayTimer.Stop();
_AutoplayTimer.Start(); _AutoplayTimer.Start();
}
else
{
_AutoplayTimer.Stop();
_AutoplayTimer.Start();
}
} }
#region ThemeEdit #region ThemeEdit
@@ -506,4 +528,4 @@ namespace VocaluxeLib.Menu.SongMenu
} }
#endregion ThemeEdit #endregion ThemeEdit
} }
} }
+3 -1
View File
@@ -246,6 +246,8 @@ namespace VocaluxeLib.Menu.SongMenu
public override void Update(SScreenSongOptions songOptions) public override void Update(SScreenSongOptions songOptions)
{ {
base.Update(songOptions);
if (songOptions.Selection.RandomOnly) if (songOptions.Selection.RandomOnly)
{ {
_PreviewSelectedSong(); _PreviewSelectedSong();
@@ -888,4 +890,4 @@ namespace VocaluxeLib.Menu.SongMenu
base.LoadSkin(); base.LoadSkin();
} }
} }
} }
@@ -220,6 +220,8 @@ namespace VocaluxeLib.Menu.SongMenu
public override void Update(SScreenSongOptions songOptions) public override void Update(SScreenSongOptions songOptions)
{ {
base.Update(songOptions);
if (songOptions.Selection.RandomOnly) if (songOptions.Selection.RandomOnly)
{ {
_PreviewSelectedSong(); _PreviewSelectedSong();
@@ -783,4 +785,4 @@ namespace VocaluxeLib.Menu.SongMenu
Init(); Init();
} }
} }
} }
+144 -88
View File
@@ -1,4 +1,4 @@
#region license #region license
// This file is part of Vocaluxe. // This file is part of Vocaluxe.
// //
// Vocaluxe is free software: you can redistribute it and/or modify // Vocaluxe is free software: you can redistribute it and/or modify
@@ -24,6 +24,7 @@ namespace VocaluxeLib.Utils.Player
{ {
public class CSongPlayer : CSoundPlayer public class CSongPlayer : CSoundPlayer
{ {
private readonly object _lock = new object();
private CSong _Song; private CSong _Song;
private bool _VideoEnabled; private bool _VideoEnabled;
private CVideoStream _Video; private CVideoStream _Video;
@@ -31,37 +32,51 @@ namespace VocaluxeLib.Utils.Player
public bool VideoEnabled public bool VideoEnabled
{ {
get { return _VideoEnabled; } get
{
lock (_lock)
{
return _VideoEnabled;
}
}
set set
{ {
if (_VideoEnabled == value) lock (_lock)
{ {
return; if (_VideoEnabled == value)
} {
return;
}
_VideoEnabled = value; _VideoEnabled = value;
if (_VideoEnabled) if (_VideoEnabled)
{ {
_LoadVideo(); _LoadVideo();
} }
else else
{ {
_CloseVideo(); _CloseVideo();
}
} }
} }
} }
public int SongId public int SongId
{ {
get { return _Song == null ? -1 : _Song.Id; } get
{
var song = _Song;
return song == null ? -1 : song.Id;
}
} }
public bool SongHasVideo public bool SongHasVideo
{ {
get get
{ {
return _Song != null && !string.IsNullOrEmpty(_Song.Folder) && !string.IsNullOrEmpty(_Song.Video) && var song = _Song;
File.Exists(Path.Combine(_Song.Folder, _Song.Video)); return song != null && !string.IsNullOrEmpty(song.Folder) && !string.IsNullOrEmpty(song.Video) &&
File.Exists(Path.Combine(song.Folder, song.Video));
} }
} }
@@ -69,9 +84,10 @@ namespace VocaluxeLib.Utils.Player
{ {
get get
{ {
if (_Song != null && !string.IsNullOrEmpty(_Song.Artist) && !string.IsNullOrEmpty(_Song.Title)) var song = _Song;
if (song != null && !string.IsNullOrEmpty(song.Artist) && !string.IsNullOrEmpty(song.Title))
{ {
return _Song.Artist + " - " + _Song.Title; return song.Artist + " - " + song.Title;
} }
return base.ArtistAndTitle; return base.ArtistAndTitle;
@@ -80,34 +96,42 @@ namespace VocaluxeLib.Utils.Player
public CTextureRef Cover public CTextureRef Cover
{ {
get { return _Song == null ? CBase.Cover.GetNoCover() : _Song.CoverTextureBig; } get
{
var song = _Song;
return song == null ? CBase.Cover.GetNoCover() : song.CoverTextureBig;
}
} }
public CSongPlayer(bool loop = false) : base(loop) { } public CSongPlayer(bool loop = false) : base(loop) { }
public CTextureRef GetVideoTexture() public CTextureRef GetVideoTexture()
{ {
if (_Video == null || _Song == null) lock (_lock)
{ {
return null; var video = _Video;
} var song = _Song;
if (video == null || video.IsClosed() || song == null || CBase.Video == null || CBase.Sound == null)
return null;
if (CBase.Video.GetFrame(_Video, CBase.Sound.GetPosition(_StreamId))) if (CBase.Video.GetFrame(video, CBase.Sound.GetPosition(_StreamId)))
{
if (_VideoFading != null)
{ {
bool finished; var texture = video.Texture;
_Video.Texture.Color.A = _VideoFading.GetValue(out finished); if (texture != null)
if (finished)
{ {
_VideoFading = null; if (_VideoFading != null)
{
bool finished;
texture.Color.A = _VideoFading.GetValue(out finished);
if (finished)
_VideoFading = null;
}
return texture;
} }
} }
return _Video.Texture; return null;
} }
return null;
} }
public void Load(CSong song, float position = 0f, bool autoplay = false) public void Load(CSong song, float position = 0f, bool autoplay = false)
@@ -117,104 +141,136 @@ namespace VocaluxeLib.Utils.Player
throw new ArgumentNullException("song"); throw new ArgumentNullException("song");
} }
Load(song.GetMP3(), position, autoplay); lock (_lock)
_Song = song; {
_LoadVideo(); Load(song.GetMP3(), position, autoplay);
_Song = song;
_LoadVideo();
}
} }
private void _LoadVideo() private void _LoadVideo()
{ {
if (_Song == null) lock (_lock)
{ {
return; var song = _Song;
} if (song == null)
{
return;
}
if (_Video != null || !SongHasVideo) if (_Video != null || !SongHasVideo)
{ {
return; return;
} }
var videoFilePath = Path.Combine(_Song.Folder, _Song.Video); var videoFilePath = Path.Combine(song.Folder, song.Video);
_Video = CBase.Video.Load(videoFilePath); var video = CBase.Video?.Load(videoFilePath);
if (_Video == null) if (video == null)
{ {
return; return;
} }
_VideoFading = new CFading(0f, 1f, 3f); _Video = video;
_VideoFading = new CFading(0f, 1f, 3f);
if (IsPlaying) if (IsPlaying)
{ {
CBase.Video.Skip(_Video, Position, _Song.VideoGap); CBase.Video?.Skip(video, Position, song.VideoGap);
CBase.Video.Resume(_Video); CBase.Video?.Resume(video);
} }
else else
{ {
CBase.Video.Skip(_Video, 0f, _Song.VideoGap); CBase.Video?.Skip(video, 0f, song.VideoGap);
}
} }
} }
public override bool Play() public override bool Play()
{ {
if (!base.Play()) lock (_lock)
{ {
return false; if (!base.Play())
} {
return false;
}
if (_Video != null) var video = _Video;
{ var song = _Song;
CBase.Video.Skip(_Video, Position, _Song.VideoGap); if (video != null && !video.IsClosed() && song != null)
CBase.Video.Resume(_Video); {
} CBase.Video?.Skip(video, Position, song.VideoGap);
CBase.Video?.Resume(video);
}
return true; return true;
}
} }
public override bool Pause() public override bool Pause()
{ {
if (!base.Pause()) lock (_lock)
{ {
return false; if (!base.Pause())
} {
return false;
}
if (_Video != null) var video = _Video;
{ if (video != null && !video.IsClosed())
CBase.Video.Pause(_Video); {
} CBase.Video?.Pause(video);
}
return true; return true;
}
} }
public override bool Stop() public override bool Stop()
{ {
if (!base.Stop()) lock (_lock)
{ {
return false; if (!base.Stop())
} {
return false;
}
if (_Video != null) var video = _Video;
{ var song = _Song;
CBase.Video.Pause(_Video); if (video != null && !video.IsClosed())
CBase.Video.Skip(_Video, 0f, _Song.VideoGap); {
} CBase.Video?.Pause(video);
if (song != null)
{
CBase.Video?.Skip(video, 0f, song.VideoGap);
}
}
return true; return true;
}
} }
private void _CloseVideo() private void _CloseVideo()
{ {
if (_Video != null) lock (_lock)
{ {
CBase.Video.Close(ref _Video); if (_Video != null)
{
CBase.Video?.Close(ref _Video);
_Video = null;
}
} }
} }
public override void Close() public override void Close()
{ {
base.Close(); lock (_lock)
{
base.Close();
_Song = null; _Song = null;
_CloseVideo(); _CloseVideo();
}
} }
} }
} }