Modding talk:Audio
Revision as of 02:19, 17 October 2021 by Pathoschild (talk | contribs) (+ code for track list export)
Track list export
The initial track list was exported with this quick code, which can be run as a C# mod:
source code |
---|
/// <summary>The main entry point for the mod.</summary>
public class ModEntry : Mod
{
/*********
** Public methods
*********/
/// <inheritdoc />
public override void Entry(IModHelper helper)
{
helper.Events.GameLoop.GameLaunched += (_, _) =>
{
StringBuilder output = new();
foreach (var categoryGroup in this.GetTracks().GroupBy(p => p.Category).OrderBy(p => p.Key))
{
output.AppendLine($"==={categoryGroup.Key}===");
output.AppendLine("{| class=\"wikitable\"");
output.AppendLine("|-\n! name\n! soundbank ID\n! description");
foreach (SoundInfo sound in categoryGroup.OrderBy(p => p.Name).ThenBy(p => p.Id))
{
string soundIdLabel = sound.Id.ToString("X").ToLower().PadLeft(8, '0');
output.AppendLine($"|-\n| <tt>{sound.Name}</tt>\n| <tt>{soundIdLabel}</tt>\n| ");
}
output.AppendLine("|}");
output.AppendLine();
}
string result = output.ToString();
this.Monitor.Log(result, LogLevel.Info);
};
}
/*********
** Private methods
*********/
/// <summary>Extract the music/sound tracks from the game's soundbank.</summary>
private IEnumerable<SoundInfo> GetTracks()
{
SoundBank soundBank = this.Helper.Reflection.GetField<SoundBank>(Game1.soundBank, "soundBank").GetValue();
Dictionary<string, CueDefinition> cues = this.Helper.Reflection.GetField<Dictionary<string, CueDefinition>>(soundBank, "_cues").GetValue();
foreach (var entry in cues)
{
foreach (XactSoundBankSound sound in entry.Value.sounds)
{
HashSet<int> ids = new();
// from main sound info
string name = entry.Key;
string category = this.GetCategoryName(sound.categoryID);
if (sound.trackIndex != 0)
ids.Add(sound.trackIndex);
// from variants
if (sound.soundClips != null)
{
foreach (XactClip clip in sound.soundClips)
{
foreach (var rawClipEvent in clip.clipEvents)
{
if (rawClipEvent is not PlayWaveEvent clipEvent)
{
this.Monitor.Log($"Unexpected clip event type '{rawClipEvent.GetType().FullName}'.", LogLevel.Error);
continue;
}
foreach (var variant in clipEvent.GetVariants())
{
if (variant.track != 0)
ids.Add(variant.track);
}
}
}
}
// export tracks
foreach (int id in ids)
{
yield return new SoundInfo
{
Category = category,
Name = name,
Id = id
};
}
}
}
}
/// <summary>Get a human-readable name for a raw audio category ID.</summary>
/// <param name="categoryId">The raw category ID.</param>
private string GetCategoryName(uint categoryId)
{
// the categories seem to be arbitrarily defined for Stardew Valley;
// these are approximate labels based on the tracks in each group.
return categoryId switch
{
2 => "Music",
3 => "Sound",
4 => "Music (ambient)",
5 => "Footsteps",
_ => categoryId.ToString()
};
}
}
public class SoundInfo
{
public string Category { get; set; }
public string Name { get; set; }
public int Id { get; set; }
}
|
—Pathoschild (talk) 02:18, 17 October 2021 (UTC)