BWIKI 全站将于 9 月 3 日(全天)进行维护,期间无法编辑任何页面或发布新的评论。
模组讨论:音频
曲目列表导出
初始曲目列表是用下列这段代码导出的,可以在 Stardew Valley 1.5.5+ 中作为 C# mod 运行:
源码
/// <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.GetCategoryName()).OrderBy(p => p.Key))
{
output.AppendLine($"==={categoryGroup.Key}===");
output.AppendLine("{| class=\"wikitable sortable\"");
output.AppendLine("|-\n!rowspan=\"2\"| name\n!rowspan=\"2\"| wavebank\n!colspan=\"2\"| soundbank index\n!rowspan=\"2\"| description");
output.AppendLine("|-\n! decimal\n! hexadecimal");
foreach (TrackInfo track in categoryGroup.OrderBy(p => p.Name).ThenBy(p => p.Index))
output.AppendLine($"|-\n| <samp>{track.Name}</samp>\n| <samp>{track.GetWavebankName()}</samp>\n| <samp>{track.Index}</samp>\n| data-sort-value=\"{track.Index}\"| <samp>{track.GetSoundbankId()}</samp>\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<TrackInfo> GetTracks()
{
SoundBank soundBank = this.Helper.Reflection.GetField<SoundBank>(Game1.soundBank, "soundBank").GetValue();
IEnumerable<CueDefinition> cues = this.Helper.Reflection.GetField<Dictionary<string, CueDefinition>>(soundBank, "_cues").GetValue().Values;
foreach (CueDefinition cue in cues)
{
foreach (XactSoundBankSound sound in cue.sounds)
{
// simple sound
if (!sound.complexSound)
{
yield return new TrackInfo(
WavebankIndex: sound.waveBankIndex,
CategoryId: sound.categoryID,
Name: cue.name,
Index: sound.trackIndex
);
continue;
}
// complex sound
bool hasVariants = false;
if (sound.soundClips != null)
{
foreach (XactClip clip in sound.soundClips)
{
foreach (ClipEvent rawClipEvent in clip.clipEvents)
{
if (rawClipEvent is not PlayWaveEvent clipEvent)
{
this.Monitor.Log($"Unexpected clip event type '{rawClipEvent.GetType().FullName}'.", LogLevel.Error);
continue;
}
foreach (PlayWaveVariant variant in clipEvent.GetVariants())
{
hasVariants = true;
yield return new TrackInfo(
WavebankIndex: variant.waveBank,
CategoryId: sound.categoryID,
Name: cue.name,
Index: variant.track
);
}
}
}
}
// invalid sound, should never happen
if (!hasVariants)
this.Monitor.Log($"Complex sound '{cue.name}' unexpectedly has no variants.", LogLevel.Error);
}
}
}
}
/// <summary>A sound or music track in a wavebank.</summary>
/// <param name="WavebankIndex">The wavebank which contains the track.</param>
/// <param name="CategoryId">The sound or music category.</param>
/// <param name="Name">The sound name used in the game code.</param>
/// <param name="Index">The offset index in the raw soundbank.</param>
public record TrackInfo(int WavebankIndex, uint CategoryId, string Name, int Index)
{
/// <summary>Get a human-readable name for the category ID.</summary>
public string GetCategoryName()
{
return this.CategoryId switch
{
2 => "Music",
3 => "Sound",
4 => "Music (ambient)",
5 => "Footsteps",
_ => this.CategoryId.ToString()
};
}
/// <summary>Get a human-readable for the wavebank index.</summary>
public string GetWavebankName()
{
return this.WavebankIndex switch
{
0 => "Wavebank",
1 => "Wavebank(1.4)",
_ => this.WavebankIndex.ToString()
};
}
/// <summary>Get the hexadecimal soundbank ID which matches the filenames exported by unxwb.</summary>
public string GetSoundbankId()
{
return this.Index
.ToString("X")
.ToLower()
.PadLeft(8, '0');
}
}
游戏数据只有数字类别 ID,但英维用户 Pathoschild 与其中一位游戏开发人员确认了内部类别名称:
类别 | 名称 | Pathoschild的记录 |
---|---|---|
1 | Default | 似乎未被使用过 |
2 | Music | |
3 | Sound | |
4 | Ambient | 在 wiki 上列为 “Music (ambient)”,只是为了对音乐曲目进行分组。 |
5 | Footsteps |
—来源英维用户Pathoschild 用户讨论页:Pathoschild|talk 2021 年 10 月 17 日 05:21 (UTC) (更新于 2021 年 10 月 20 日 00:12 (UTC))