Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Audio Playback Stability #1243

Merged
merged 2 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/Beutl.Engine/Audio/Sound.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public abstract class Sound : Renderable
public static readonly CoreProperty<float> GainProperty;
public static readonly CoreProperty<ISoundEffect?> EffectProperty;
private float _gain = 100;
// 現在の再生位置が前の再生位置よりも戻った場合、エフェクトプロセッサを無効にするために使用
private TimeRange _prevRange;
private TimeRange _range;
private TimeSpan _offset;
private ISoundEffect? _effect;
Expand Down Expand Up @@ -82,6 +84,11 @@ public Pcm<Stereo32BitFloat> ToPcm(int sampleRate)

public void Render(IAudio audio)
{
if (_prevRange.Start > _range.Start)
{
InvalidateEffectProcessor();
}

if (_effect is { IsEnabled: true } effect)
{
_effectProcessor ??= effect.CreateProcessor();
Expand Down Expand Up @@ -137,11 +144,7 @@ private void UpdateTime(IClock clock)
}
}

if (_range.Start > start)
{
InvalidateEffectProcessor();
}

_prevRange = _range;
_range = new TimeRange(start, length);
}

Expand Down
39 changes: 32 additions & 7 deletions src/Beutl/ViewModels/PlayerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public sealed class PlayerViewModel : IDisposable
private IDisposable? _currentFrameSubscription;
private CancellationTokenSource? _cts;
private Size _maxFrameSize;
private SemaphoreSlim _audioSemaphoreSlim = new(1, 1);

public PlayerViewModel(EditViewModel editViewModel)
{
Expand Down Expand Up @@ -226,6 +227,14 @@ public void Play()
_editViewModel.SceneId, rate, startFrame, durationFrame);
playerImpl.Start();

if (!await _audioSemaphoreSlim.WaitAsync(1000))
{
NotificationService.ShowError(Message.AnUnexpectedErrorHasOccurred,
Message.An_exception_occurred_during_audio_playback);
_logger.LogWarning("Failed to acquire the semaphore for audio playback.");
return;
}

PlayAudio(Scene);

await await Task.Factory.StartNew(async () =>
Expand Down Expand Up @@ -295,15 +304,27 @@ public int GetFrameRate()

private async void PlayAudio(Scene scene)
{
if (OperatingSystem.IsWindows())
try
{
using var audioContext = new XAudioContext();
await PlayWithXA2(audioContext, scene).ConfigureAwait(false);
if (OperatingSystem.IsWindows())
{
using var audioContext = new XAudioContext();
await PlayWithXA2(audioContext, scene).ConfigureAwait(false);
}
else
{
await Task.Run(async () =>
{
using var audioContext = new AudioContext();
await PlayWithOpenAL(audioContext, scene);
});
}
}
else
finally
{
using var audioContext = new AudioContext();
await PlayWithOpenAL(audioContext, scene);
// 呼び出し元でWaitAsync()しているので、ここでRelease()する
// PlayAudio内でWaitAsyncしないのは、セマフォを取得するまで、動画の再生を開始しないため
_audioSemaphoreSlim.Release();
}
}

Expand Down Expand Up @@ -439,6 +460,7 @@ static void CheckError()

while (IsPlaying.Value)
{
audioContext.MakeCurrent();
AL.GetSource(source, ALGetSourcei.BuffersProcessed, out int processed);
CheckError();
while (processed > 0)
Expand All @@ -462,6 +484,7 @@ static void CheckError()

if (AL.GetSourceState(source) != ALSourceState.Playing)
{
CheckError();
AL.SourcePlay(source);
CheckError();
}
Expand All @@ -471,11 +494,13 @@ static void CheckError()
break;
}

while (AL.GetSourceState(source) == ALSourceState.Playing)
while (AL.GetSourceState(source) == ALSourceState.Playing && IsPlaying.Value)
{
await Task.Delay(100).ConfigureAwait(false);
}

CheckError();
AL.SourceStop(source);
CheckError();
// https://hamken100.blogspot.com/2014/04/aldeletebuffersalinvalidoperation.html
AL.Source(source, ALSourcei.Buffer, 0);
Expand Down
Loading