SHA256
61 lines
2.7 KiB
C#
61 lines
2.7 KiB
C#
using System;
|
|
using System.IO;
|
|
using MelonLoader;
|
|
using UnityEngine;
|
|
|
|
namespace ADOFAIRenderer.Util.Audio.Clip;
|
|
|
|
/// <summary>
|
|
/// Opens the <see cref="AudioClipSource"/> for a clip the game plays, keyed by the game's audioLib key (also AudioClip.name):
|
|
/// - Key recorded in <see cref="AudioClipPaths"/> and the file exists: decoded from the file with <see cref="FFmpegPCMReader"/>
|
|
/// (mono MP3 through <see cref="MP3MonoLayoutReader"/>)
|
|
/// (song files and PlaySound files of custom levels).
|
|
/// - Otherwise, or when FFmpeg cannot open the file: the Unity clip is copied with <see cref="UnityClipLoader"/>
|
|
/// (built-in hit sounds from Resources, official/DLC level songs from Addressables).
|
|
/// Main thread only (the Unity copy calls AudioClip.GetData).
|
|
/// </summary>
|
|
internal static class AudioClipResolver
|
|
{
|
|
/// <param name="store">Store of the current render.</param>
|
|
/// <param name="key">audioLib key of the clip (for example "song.ogg*external" or "sndKick").</param>
|
|
/// <param name="clip">Loaded Unity clip for the key, used when the clip has no file; may be null when the key has a file.</param>
|
|
/// <returns>The source, or null when neither the file nor the clip can be read.</returns>
|
|
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<RenderMod>.Logger.Warning($"[AudioClipResolver] Mono MP3 '{path}' is rendered like the game: half speed, one octave lower.");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Melon<RenderMod>.Logger.Warning($"[AudioClipResolver] FFmpeg could not open '{path}' for '{key}', using the Unity clip: {e.Message}");
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
/// <summary>Opens <paramref name="path"/> with FFmpeg; mono MP3 files are wrapped in <see cref="MP3MonoLayoutReader"/> (game layout).</summary>
|
|
private static IPCMReader OpenFile(string path)
|
|
{
|
|
var reader = new FFmpegPCMReader(path);
|
|
if (!reader.IsMP3 || reader.Channels != 1) return reader;
|
|
return new MP3MonoLayoutReader(reader);
|
|
}
|
|
}
|