From 784db6dcaed6e597fbe74e826e73cf94b874c9af Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Thu, 6 Feb 2025 21:33:39 -0600 Subject: [PATCH] WIP Sequence importer --- src/audio/audio_load.c | 8 + .../importers/audio/SequenceFactory.cpp | 311 ++++++++++++++++++ .../importers/audio/SequenceFactory.h | 4 + src/port/resource/loaders/AudioLoader.cpp | 5 +- 4 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 src/port/resource/importers/audio/SequenceFactory.cpp create mode 100644 src/port/resource/importers/audio/SequenceFactory.h diff --git a/src/audio/audio_load.c b/src/audio/audio_load.c index c5804b62..3b4fbff2 100644 --- a/src/audio/audio_load.c +++ b/src/audio/audio_load.c @@ -402,6 +402,7 @@ void AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) { AudioLoad_SyncLoadFont(fontId); } + printf("seqId: %d\n", seqId); seqData = AudioLoad_SyncLoadSeq(seqId); AudioSeq_ResetSequencePlayer(playerIdx); @@ -416,8 +417,15 @@ void AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) { gSeqPlayers[playerIdx].finished = false; } +char* WriteDummySequence(); + void* AudioLoad_SyncLoadSeq(s32 seqId) { + // if(seqId == 9 || seqId == 21){ + // return WriteDummySequence(); + // } AudioTable* table = AudioLoad_GetLoadTable(SEQUENCE_TABLE); + char* seqPath = ResourceGetNameByCrc((uint64_t) table->entries[seqId].romAddr); + printf("seqPath: %s\n", seqPath); return ResourceGetDataByCrc((uint64_t) table->entries[seqId].romAddr); } diff --git a/src/port/resource/importers/audio/SequenceFactory.cpp b/src/port/resource/importers/audio/SequenceFactory.cpp new file mode 100644 index 00000000..bc7728e3 --- /dev/null +++ b/src/port/resource/importers/audio/SequenceFactory.cpp @@ -0,0 +1,311 @@ +#include "SequenceFactory.h" +#include "BinaryWriter.h" +#include + +template static void WriteInsnOneArg(Ship::BinaryWriter* writer, uint8_t opcode, T arg) { + static_assert(std::is_fundamental::value); + writer->Write(opcode); + writer->Write(arg); +} + +template +static void WriteInsnTwoArg(Ship::BinaryWriter* writer, uint8_t opcode, T1 arg1, T2 arg2) { + static_assert(std::is_fundamental::value && std::is_fundamental::value); + writer->Write(opcode); + writer->Write(arg1); + writer->Write(arg2); +} + +template +static void WriteInsnThreeArg(Ship::BinaryWriter* writer, uint8_t opcode, T1 arg1, T2 arg2, T3 arg3) { + static_assert(std::is_fundamental::value && std::is_fundamental::value); + writer->Write(opcode); + writer->Write(arg1); + writer->Write(arg2); + writer->Write(arg3); +} + +static void WriteInsnNoArg(Ship::BinaryWriter* writer, uint8_t opcode) { + writer->Write(opcode); +} + +static void WriteLegato(Ship::BinaryWriter* writer) { + WriteInsnNoArg(writer, 0xC4); +} + +static void WriteNoLegato(Ship::BinaryWriter* writer) { + WriteInsnNoArg(writer, 0xC5); +} + +static void WriteMuteBhv(Ship::BinaryWriter* writer, uint8_t arg) { + WriteInsnOneArg(writer, 0xD3, arg); +} + +static void WriteMuteScale(Ship::BinaryWriter* writer, uint8_t arg) { + WriteInsnOneArg(writer, 0xD5, arg); +} + +static void WriteInitchan(Ship::BinaryWriter* writer, uint16_t channels) { + WriteInsnOneArg(writer, 0xD7, channels); +} + +static void WriteLdchan(Ship::BinaryWriter* writer, uint8_t channel, uint16_t offset) { + WriteInsnOneArg(writer, 0x90 | channel, offset); +} + +static void WriteVolSHeader(Ship::BinaryWriter* writer, uint8_t vol) { + WriteInsnOneArg(writer, 0xDB, vol); +} + +static void WriteVolCHeader(Ship::BinaryWriter* writer, uint8_t vol) { + WriteInsnOneArg(writer, 0xDF, vol); +} + +static void WriteTempo(Ship::BinaryWriter* writer, uint8_t tempo) { + WriteInsnOneArg(writer, 0xDD, tempo); +} + +static void WriteJump(Ship::BinaryWriter* writer, uint16_t offset) { + WriteInsnOneArg(writer, 0xFB, offset); +} + +static void WriteDisablecan(Ship::BinaryWriter* writer, uint16_t channels) { + WriteInsnOneArg(writer, 0xD6, channels); +} + +static void WriteNoshort(Ship::BinaryWriter* writer) { + WriteInsnNoArg(writer, 0xC4); +} + +// TODO: This is different on sf64 +static void WriteLdlayer(Ship::BinaryWriter* writer, uint8_t layer, uint16_t offset) { + WriteInsnOneArg(writer, 0x90 | layer, offset); +} + +static void WritePan(Ship::BinaryWriter* writer, uint8_t pan) { + WriteInsnOneArg(writer, 0xDD, pan); +} + +static void WriteBend(Ship::BinaryWriter* writer, uint8_t bend) { + WriteInsnOneArg(writer, 0xD3, bend); +} + +static void WriteInstrument(Ship::BinaryWriter* writer, uint8_t instrument) { + WriteInsnOneArg(writer, 0xC1, instrument); +} + +static void WriteTranspose(Ship::BinaryWriter* writer, int8_t transpose) { + WriteInsnOneArg(writer, 0xC2, transpose); +} + +static void WriteDelay(Ship::BinaryWriter* writer, uint16_t delay) { + if (delay > 0x7F) { + WriteInsnOneArg(writer, 0xFD, static_cast(delay | 0x8000)); + } else { + WriteInsnOneArg(writer, 0xFD, static_cast(delay)); + } +} + +template static void WriteLDelay(Ship::BinaryWriter* writer, T delay) { + WriteInsnOneArg(writer, 0xC0, delay); +} + +template static void WriteNotedv(Ship::BinaryWriter* writer, uint8_t note, T delay, uint8_t velocity) { + WriteInsnTwoArg(writer, note, delay, velocity); +} + +static void WriteNotedvg(Ship::BinaryWriter* writer, uint8_t note, uint16_t delay, uint8_t velocity, uint8_t gateTime) { + if (delay > 0x7F) { + WriteInsnThreeArg(writer, note, static_cast(delay | 0x8000), velocity, gateTime); + } else { + WriteInsnThreeArg(writer, note, static_cast(delay), velocity, gateTime); + } +} + +static void WriteMonoSingleSeq(Ship::BinaryWriter* writer, uint16_t delay, uint8_t tempo, bool looped) { + uint16_t channelStart; + uint16_t channelPlaceholderOff; + uint16_t loopPoint; + uint16_t layerPlaceholderOff; + uint16_t layerStart; + if (looped) { + delay = 0x7FFF; + } + // Write seq header + + // These two values are always the same in OOT and MM + WriteMuteBhv(writer, 0x20); + WriteMuteScale(writer, 0x32); + + // We only have one channel + WriteInitchan(writer, 0b11); + // Store the current position so we can write the address of the channel when we are ready. + channelPlaceholderOff = writer->GetBaseAddress(); + // Store the current position so we can loop here after the song ends. + loopPoint = writer->GetBaseAddress(); + WriteLdchan(writer, 0, 0); // Fill in the actual address later + + WriteVolSHeader(writer, 127); // Max volume + WriteTempo(writer, tempo); + + WriteDelay(writer, delay); + if (looped) { + WriteJump(writer, loopPoint); + } + WriteDisablecan(writer, 0b11); + writer->Write(static_cast(0xFF)); + + // Fill in the ldchan from before + channelStart = writer->GetBaseAddress(); + writer->Seek(channelPlaceholderOff, Ship::SeekOffsetType::Start); + WriteLdchan(writer, 0, channelStart); + writer->Seek(channelStart, Ship::SeekOffsetType::Start); + + // Channel header + layerPlaceholderOff = writer->GetBaseAddress(); + WriteNoshort(writer); + WriteLdlayer(writer, 0, 0); + WritePan(writer, 70); + WriteVolCHeader(writer, 127); // Max volume + WriteBend(writer, 0); + WriteInstrument(writer, 0); + WriteDelay(writer, delay); + writer->Write(static_cast(0xFF)); + + layerStart = writer->GetBaseAddress(); + writer->Seek(layerPlaceholderOff, Ship::SeekOffsetType::Start); + WriteLdlayer(writer, 0, layerStart); + writer->Seek(layerStart, Ship::SeekOffsetType::Start); + + // Note layer + WriteLegato(writer); + WriteNotedvg(writer, 39, 0x7FFF - 1, static_cast(0x7F), static_cast(1)); + writer->Write(static_cast(0xFF)); +} + +static void WriteStereoSingleSeq(Ship::BinaryWriter* writer, uint16_t delay, uint8_t tempo, bool looped) { + uint16_t lChannelStart; + uint16_t rChannelStart; + uint16_t channelPlaceholderOff; + uint16_t loopPoint; + uint16_t lLayerPlaceholderOff; + uint16_t rLayerPlaceholderOff; + uint16_t lLayerOffset; + uint16_t rLayerOffset; + + uint16_t layerStart; + // Write seq header + if (looped) { + delay = 0x7FFF; + } + // These two values are always the same in OOT and MM + WriteMuteBhv(writer, 0x20); + WriteMuteScale(writer, 0x32); + + // We only have one channel + WriteInitchan(writer, 0b11); + // Store the current position so we can write the address of the channel when we are ready. + channelPlaceholderOff = writer->GetBaseAddress(); + // Store the current position so we can loop here after the song ends. + loopPoint = writer->GetBaseAddress(); + // Left note channel + WriteLdchan(writer, 0, 0); // Fill in the actual address later + // Right note channel + WriteLdchan(writer, 1, 0); // Fill in the actual address later + + WriteVolSHeader(writer, 127); // Max volume + WriteTempo(writer, tempo); + + WriteDelay(writer, delay); + if (looped) { + WriteJump(writer, loopPoint); + } + WriteDisablecan(writer, 0b11); + writer->Write(static_cast(0xFF)); + + lChannelStart = writer->GetBaseAddress(); + // Left Channel header + WriteNoshort(writer); + lLayerPlaceholderOff = writer->GetBaseAddress(); + WriteLdlayer(writer, 0, 0); + WritePan(writer, 0); + WriteVolCHeader(writer, 127); // Max volume + WriteBend(writer, 0); + WriteInstrument(writer, 0); + WriteDelay(writer, delay); + writer->Write(static_cast(0xFF)); + + rChannelStart = writer->GetBaseAddress(); + // Right Channel header + WriteNoshort(writer); + rLayerPlaceholderOff = writer->GetBaseAddress(); + WriteLdlayer(writer, 1, 0); + WritePan(writer, 127); + WriteVolCHeader(writer, 127); // Max volume + WriteBend(writer, 0); + WriteInstrument(writer, 1); + WriteDelay(writer, delay); + writer->Write(static_cast(0xFF)); + uint16_t placeHolder = writer->GetBaseAddress(); + writer->Seek(channelPlaceholderOff, Ship::SeekOffsetType::Start); + WriteLdchan(writer, 0, lChannelStart); + WriteLdchan(writer, 1, rChannelStart); + writer->Seek(placeHolder, Ship::SeekOffsetType::Start); + + // Left Note layer + lLayerOffset = writer->GetBaseAddress(); + WriteLegato(writer); + WriteNotedvg(writer, 39, 0x7FFF - 1, static_cast(0x7F), static_cast(1)); + writer->Write(static_cast(0xFF)); + + // Right Note layer + rLayerOffset = writer->GetBaseAddress(); + WriteLegato(writer); + WriteNotedvg(writer, 39, 0x7FFF - 1, static_cast(0x7F), static_cast(1)); + writer->Write(static_cast(0xFF)); + + writer->Seek(lLayerPlaceholderOff, Ship::SeekOffsetType::Start); + WriteLdlayer(writer, 0, lLayerOffset); + writer->Seek(rLayerPlaceholderOff, Ship::SeekOffsetType::Start); + WriteLdlayer(writer, 1, rLayerOffset); +} + +extern "C" char* WriteDummySequence() { + Ship::BinaryWriter writer = Ship::BinaryWriter(); + writer.SetEndianness(Ship::Endianness::Big); + + bool stereo = true; + bool looped = true; + uint32_t length = 60; + // Placeholder off is the offset of the instruction to be replaced. The second variable is the target adress + // of what we need to load of jump to + uint16_t loopPoint; + uint16_t channelPlaceholderOff, channelStart; + uint16_t layerPlaceholderOff, layerStart; + + // 1 second worth of ticks can be found by using `ticks = 60 / (bpm * 48)` + // Get the number of ticks per second and then divide the length by this number to get the number of ticks + // for the song. + constexpr uint8_t TEMPO = 1; + constexpr float TEMPO_F = TEMPO; + // Use floats for this first calculation so we can round up + float delayF = length / (60.0f / (TEMPO_F * 48.0f)); + // Convert to u16. This way this value is encoded changes depending on the value. + // It can be at most 0xFFFF so store it in a u16 for now. + uint16_t delay; + if (delayF >= 65535.0f) { + delay = 0x7FFF; + } else { + delay = delayF; + } + if (stereo) { + WriteStereoSingleSeq(&writer, delay, TEMPO, looped); + } else { + WriteMonoSingleSeq(&writer, delay, TEMPO, looped); + } + + auto result = writer.ToVector(); + char* output = new char[result.size()]; + memcpy(output, result.data(), result.size()); + return output; +} \ No newline at end of file diff --git a/src/port/resource/importers/audio/SequenceFactory.h b/src/port/resource/importers/audio/SequenceFactory.h new file mode 100644 index 00000000..d537fdb4 --- /dev/null +++ b/src/port/resource/importers/audio/SequenceFactory.h @@ -0,0 +1,4 @@ +#pragma once + + +class ResourceFactoryXMLSequenceV0 {}; \ No newline at end of file diff --git a/src/port/resource/loaders/AudioLoader.cpp b/src/port/resource/loaders/AudioLoader.cpp index 156d067b..515d5f59 100644 --- a/src/port/resource/loaders/AudioLoader.cpp +++ b/src/port/resource/loaders/AudioLoader.cpp @@ -9,7 +9,10 @@ #ifdef OTR_AUDIO extern "C" SoundFont* Audio_LoadFont(AudioTableEntry entry, uint32_t fontId) { - return (SoundFont*) ResourceGetDataByCrc((uint64_t) gSoundFontTable->entries[fontId].romAddr); + auto crc = (uint64_t) gSoundFontTable->entries[fontId].romAddr; + auto path = ResourceGetNameByCrc(crc); + printf("Font: %s\n", path); + return (SoundFont*) ResourceGetDataByCrc(crc); } #else namespace fs = std::filesystem;