SHA256
Resolve Audio Clips From Level Files
This commit is contained in:
5 files changed
+149
-7
No files matched your search
@@ -26,6 +26,9 @@ internal sealed class AudioClipStore : IDisposable
|
||||
/// <summary>Longest wait of a consumer between checks when no chunk is loaded, in milliseconds.</summary>
|
||||
private const int ConsumerWaitMs = 50;
|
||||
|
||||
/// <summary>Longest wait for the decoder thread to stop in <see cref="Dispose"/>, in milliseconds.</summary>
|
||||
private const int DecoderStopTimeoutMs = 3000;
|
||||
|
||||
private readonly Dictionary<string, AudioClipSource?> _sources = new();
|
||||
private readonly List<AudioClipSource> _sourceList = new();
|
||||
private readonly Stack<float[]> _chunkPool = new();
|
||||
@@ -128,10 +131,10 @@ internal sealed class AudioClipStore : IDisposable
|
||||
/// <summary>
|
||||
/// Returns the source registered under <paramref name="key"/>, creating it with <paramref name="createReader"/> on first use.
|
||||
/// A key whose reader could not be created is remembered as null and not retried.
|
||||
/// For readers that can seek, <paramref name="createReader"/> is kept and called again on the decoder thread
|
||||
/// to open additional decode streams (see <see cref="AudioClipSource.MaxDecodeStreams"/>).
|
||||
/// For readers that can seek, additional decode streams (see <see cref="AudioClipSource.MaxDecodeStreams"/>) are opened on the
|
||||
/// decoder thread with <paramref name="openAdditionalReader"/>, or with <paramref name="createReader"/> when it is null.
|
||||
/// </summary>
|
||||
public AudioClipSource? GetOrAdd(string key, Func<IPCMReader?> createReader)
|
||||
public AudioClipSource? GetOrAdd(string key, Func<IPCMReader?> createReader, Func<IPCMReader?>? openAdditionalReader = null)
|
||||
{
|
||||
lock (_sources)
|
||||
{
|
||||
@@ -147,7 +150,7 @@ internal sealed class AudioClipStore : IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
source = new AudioClipSource(this, key, reader, reader.IsForwardOnly ? null : createReader, OutputRate);
|
||||
source = new AudioClipSource(this, key, reader, reader.IsForwardOnly ? null : openAdditionalReader ?? createReader, OutputRate);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -315,7 +318,12 @@ internal sealed class AudioClipStore : IDisposable
|
||||
_stopRequested = true;
|
||||
_serviceRequested.Set();
|
||||
NotifyChunkLoaded();
|
||||
_decoderThread?.Join();
|
||||
|
||||
// A decoder thread blocked inside a read (for example a file on an unresponsive drive) is left running:
|
||||
// it is a background thread, and its sources keep their readers so the blocked call does not touch freed FFmpeg contexts.
|
||||
var decoderStopped = _decoderThread == null || _decoderThread.Join(DecoderStopTimeoutMs);
|
||||
if (!decoderStopped)
|
||||
Melon<RenderMod>.Logger.Warning($"[AudioClipStore] Decoder thread did not stop within {DecoderStopTimeoutMs} ms; its readers are left open.");
|
||||
|
||||
LogMemoryUsage();
|
||||
|
||||
@@ -325,8 +333,11 @@ internal sealed class AudioClipStore : IDisposable
|
||||
_sources.Clear();
|
||||
}
|
||||
|
||||
foreach (var source in sources)
|
||||
source.DisposeInternal();
|
||||
if (decoderStopped)
|
||||
{
|
||||
foreach (var source in sources)
|
||||
source.DisposeInternal();
|
||||
}
|
||||
|
||||
lock (_poolLock)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user