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); } }