Skip to content

Commit

Permalink
A few playback bugfixes:
Browse files Browse the repository at this point in the history
 - Cue delays were the source of a few race conditions which have now been fixed, including a crash bug
 - Added a few try catches defensively to the audio playback manager to prevent crashes
  • Loading branch information
space928 committed Feb 18, 2024
1 parent 7096fb1 commit 2e2b704
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 8 deletions.
56 changes: 48 additions & 8 deletions QPlayer/ViewModels/AudioPlaybackManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,15 @@ public void OpenOutputDevice(AudioOutputDriver driver, object key)
this.driver = driver;
device.PlaybackStopped += DevicePlaybackStopped;
deviceClosedEvent.Reset();
device.Init(mixer);
device.Play();
try
{
device.Init(mixer);
device.Play();
} catch(Exception ex)
{
MainViewModel.Log($"Failed to start device '{key}' with driver '{driver}'.\n" + ex,
MainViewModel.LogLevel.Error);
}
/*var sig = new SignalGenerator();
PlaySound(sig);*/
MainViewModel.Log($"Opened sound device '{key}' with driver '{driver}'!", MainViewModel.LogLevel.Info);
Expand Down Expand Up @@ -184,9 +191,18 @@ private ISampleProvider ConvertToMixerFormat(ISampleProvider input)
/// <param name="onCompleted">a callback raised when the stream is removed from the mixer</param>
public void PlaySound(ISampleProvider provider, Action<ISampleProvider>? onCompleted = null)
{
var converted = ConvertToMixerFormat(provider);
activeChannels.Add(provider, (converted, onCompleted));
mixer.AddMixerInput(converted);
try
{
if (activeChannels.ContainsKey(provider))
return;

var converted = ConvertToMixerFormat(provider);
activeChannels.Add(provider, (converted, onCompleted));
mixer.AddMixerInput(converted);
} catch (Exception ex)
{
MainViewModel.Log("Error while trying to play sound! \n" + ex, MainViewModel.LogLevel.Error);
}
}

/// <summary>
Expand All @@ -195,12 +211,36 @@ public void PlaySound(ISampleProvider provider, Action<ISampleProvider>? onCompl
/// <param name="provider">the sample stream to stop</param>
public void StopSound(ISampleProvider provider)
{
if (activeChannels.TryGetValue(provider, out var channel))
try
{
mixer.RemoveMixerInput(channel.convertedStream);
activeChannels.Remove(provider);
if (activeChannels.TryGetValue(provider, out var channel))
{
mixer.RemoveMixerInput(channel.convertedStream);
activeChannels.Remove(provider);
}
} catch (Exception ex)
{
MainViewModel.Log("Error while trying to stop sound! \n" + ex, MainViewModel.LogLevel.Error);
}
}

/// <summary>
/// Gets whether a sound stream is currently playing.
/// </summary>
/// <param name="provider"></param>
/// <returns></returns>
public bool IsPlaying(ISampleProvider provider) => activeChannels.ContainsKey(provider);

/// <summary>
/// Stops all sound sources.
/// </summary>
public void StopAllSounds()
{
mixer.RemoveAllMixerInputs();
foreach (var channel in activeChannels)
channel.Value.completedCallback?.Invoke(channel.Key);
activeChannels.Clear();
}
}

public class LoopingSampleProvider<T> : WaveStream, ISampleProvider where T : WaveStream, ISampleProvider
Expand Down
2 changes: 2 additions & 0 deletions QPlayer/ViewModels/CueViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,13 @@ public virtual void Go()

public virtual void Pause()
{
goTimer.Stop();
State = CueState.Paused;
}

public virtual void Stop()
{
goTimer.Stop();
State = CueState.Ready;
mainViewModel?.ActiveCues.Remove(this);
}
Expand Down
5 changes: 5 additions & 0 deletions QPlayer/ViewModels/SoundCueViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,13 @@ public override void Go()
base.Go();
if (oldState == CueState.Playing || oldState == CueState.PlayingLooped)
StopAudio();

if (audioFile == null || fadeInOutProvider == null || mainViewModel == null)
return;
// There are a few edge cases where this can happen (notably when starting a cue while it's in the delay state).
if (mainViewModel.AudioPlaybackManager.IsPlaying(fadeInOutProvider))
StopAudio();

audioProgressUpdater.Start();
if (FadeOut > 0)
{
Expand Down
2 changes: 2 additions & 0 deletions QPlayer/ViewModels/ViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@ public void StopExecute()
{
for(int i = ActiveCues.Count-1; i >= 0; i--)
ActiveCues[i].Stop();

AudioPlaybackManager.StopAllSounds();
}

public void MoveCueUpExecute()
Expand Down

0 comments on commit 2e2b704

Please sign in to comment.