using System;
using System.Collections.Generic;
using UnityEngine;
namespace ADOFAIRenderer.Util.Audio.Clip;
///
/// Copies a Unity into memory for clips without a file on disk
/// (built-in hit sounds from Resources, official/DLC level audio).
/// runs on the main Unity thread only, so the whole clip is copied once on the main Unity thread
/// and the decoder thread reads the copy through a .
/// Streaming clips () are rejected because GetData does not read them.
/// Reference: https://docs.unity3d.com/ScriptReference/AudioClip.GetData.html
///
internal static class UnityClipLoader
{
///
/// Copies in blocks of floats or less
/// (FramesPerBlock = ChunkFloats / channels frames per GetData call). Main thread only.
/// Block memory is counted in and released block by block as the decoder thread reads it.
///
public static MemoryPCMReader Load(AudioClip clip, AudioClipStore store)
{
if (!clip) throw new ArgumentNullException(nameof(clip));
if (clip.loadType == AudioClipLoadType.Streaming)
throw new NotSupportedException($"AudioClip '{clip.name}' is a streaming clip; GetData cannot read it.");
// Clips with preloadAudioData off start unloaded; LoadAudioData loads the data synchronously, or asynchronously for
// clips with loadInBackground (GetData below then fails while the data is still loading).
// Reference: https://docs.unity3d.com/ScriptReference/AudioClip.LoadAudioData.html
if (clip.loadState == AudioDataLoadState.Unloaded)
clip.LoadAudioData();
var channels = clip.channels;
var totalFrames = (long)clip.samples;
var framesPerBlock = AudioClipSource.ChunkFloats / channels;
var blocks = new List();
try
{
for (long start = 0; start < totalFrames; start += framesPerBlock)
{
var frames = (int)Math.Min(framesPerBlock, totalFrames - start);
var block = store.AllocateExactArray(frames * channels);
blocks.Add(block);
// GetData fills the whole array; the offset is in frames (samples per channel).
if (!clip.GetData(block, (int)start))
throw new InvalidOperationException($"AudioClip.GetData failed for '{clip.name}' at frame {start} (load state {clip.loadState}).");
}
}
catch
{
foreach (var block in blocks) store.FreeExactArray(block);
throw;
}
return new MemoryPCMReader(blocks, framesPerBlock, channels, clip.frequency, totalFrames, store.FreeExactArray);
}
}