using System;
using System.IO;
using MelonLoader;
using UnityEngine;
namespace ADOFAIRenderer.Util.Audio.Clip;
///
/// Opens the for a clip the game plays, keyed by the game's audioLib key (also AudioClip.name):
/// - Key recorded in and the file exists: decoded from the file with
/// (mono MP3 through )
/// (song files and PlaySound files of custom levels).
/// - Otherwise, or when FFmpeg cannot open the file: the Unity clip is copied with
/// (built-in hit sounds from Resources, official/DLC level songs from Addressables).
/// Main thread only (the Unity copy calls AudioClip.GetData).
///
internal static class AudioClipResolver
{
///
/// Returns the source registered under in , opening it on first use
/// (file first, then the Unity clip; see the class summary). A key that could not be opened is remembered as null.
///
/// Store of the current render.
/// audioLib key of the clip (for example "song.ogg*external" or "sndKick").
/// Loaded Unity clip for the key, used when the clip has no file; may be null when the key has a file.
/// The source, or null when neither the file nor the clip can be read.
public static AudioClipSource? GetOrAdd(AudioClipStore store, string key, AudioClip? clip)
{
if (store.TryGet(key, out var existing)) return existing;
if (AudioClipPaths.TryGetPath(key, out var path) && File.Exists(path))
{
IPCMReader? fileReader = null;
try
{
fileReader = OpenFile(path);
if (fileReader is MP3MonoLayoutReader)
Melon.Logger.Warning($"[AudioClipResolver] Mono MP3 '{path}' is rendered like the game: half speed, one octave lower.");
}
catch (Exception e)
{
Melon.Logger.Warning($"[AudioClipResolver] FFmpeg could not open '{path}' for '{key}', using the Unity clip: {e.Message}");
}
// The reader opened here becomes the first decode stream; additional streams reopen the file with OpenFile.
// When no source results (store disposed, or the source could not be created), the reader is disposed here
// (readers allow Dispose more than once).
if (fileReader != null)
{
var source = store.GetOrAdd(key, () => fileReader, () => OpenFile(path));
if (source == null) fileReader.Dispose();
return source;
}
}
if (!clip) return store.GetOrAdd(key, () => null);
return store.GetOrAdd(key, () => UnityClipLoader.Load(clip!, store));
}
/// Opens with FFmpeg; mono MP3 files are wrapped in (game layout).
private static IPCMReader OpenFile(string path)
{
var reader = new FFmpegPCMReader(path);
if (!reader.IsMP3 || reader.Channels != 1) return reader;
return new MP3MonoLayoutReader(reader);
}
}