From 80cc9f7c94e06c44bc88be9ee1bca869bc90b293 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 28 May 2016 18:31:58 -1000 Subject: [PATCH] MusyX 1 compatibility fixes, Rogue Squadron N64 decoding confirmed --- include/amuse/AudioGroupData.hpp | 3 +- include/amuse/AudioGroupSampleDirectory.hpp | 26 ++-- include/amuse/Voice.hpp | 9 +- lib/AudioGroup.cpp | 2 +- lib/AudioGroupProject.cpp | 16 +-- lib/AudioGroupSampleDirectory.cpp | 28 +++-- lib/ContainerRegistry.cpp | 11 +- lib/N64MusyXCodec.c | 127 +++++--------------- lib/Voice.cpp | 55 ++++++--- 9 files changed, 121 insertions(+), 156 deletions(-) diff --git a/include/amuse/AudioGroupData.hpp b/include/amuse/AudioGroupData.hpp index e7941a8..4b9f441 100644 --- a/include/amuse/AudioGroupData.hpp +++ b/include/amuse/AudioGroupData.hpp @@ -10,7 +10,6 @@ namespace amuse class AudioGroupData { friend class Engine; - friend class AudioGroupProject; protected: unsigned char* m_proj; unsigned char* m_pool; @@ -41,6 +40,8 @@ public: { return m_proj != nullptr && m_pool != nullptr && m_sdir != nullptr && m_samp != nullptr; } + + DataFormat getDataFormat() const {return m_fmt;} }; /** A buffer-owning version of AudioGroupData */ diff --git a/include/amuse/AudioGroupSampleDirectory.hpp b/include/amuse/AudioGroupSampleDirectory.hpp index 77ee43c..1087bb8 100644 --- a/include/amuse/AudioGroupSampleDirectory.hpp +++ b/include/amuse/AudioGroupSampleDirectory.hpp @@ -26,21 +26,29 @@ public: uint32_t m_adpcmParmOffset; void swapBig(); }; - struct ADPCMParms + union ADPCMParms { - uint16_t m_bytesPerFrame; - uint8_t m_ps; - uint8_t m_lps; - int16_t m_hist2; - int16_t m_hist1; - int16_t m_coefs[8][2]; - void swapBig(); + struct DSPParms + { + uint16_t m_bytesPerFrame; + uint8_t m_ps; + uint8_t m_lps; + int16_t m_hist2; + int16_t m_hist1; + int16_t m_coefs[8][2]; + } dsp; + struct VADPCMParms + { + int16_t m_coefs[8][2][8]; + } vadpcm; + void swapBigDSP(); + void swapBigVADPCM(); }; private: std::unordered_map> m_entries; public: AudioGroupSampleDirectory(const unsigned char* data, GCNDataTag); - AudioGroupSampleDirectory(const unsigned char* data, N64DataTag); + AudioGroupSampleDirectory(const unsigned char* data, const unsigned char* sampData, N64DataTag); AudioGroupSampleDirectory(const unsigned char* data, PCDataTag); }; diff --git a/include/amuse/Voice.hpp b/include/amuse/Voice.hpp index db8c9c3..4472820 100644 --- a/include/amuse/Voice.hpp +++ b/include/amuse/Voice.hpp @@ -48,10 +48,11 @@ class Voice : public Entity enum class SampleFormat : uint8_t { - DSP, - IMA, - PCM, - N64 + DSP, /**< GCN DSP-ucode ADPCM (very common for GameCube games) */ + IMA, /**< IMA-ADPCM (rarely used within MusyX itself) */ + PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */ + N64, /**< 2-stage VADPCM coding with SAMP-embedded codebooks */ + PCM_PC /**< Little-endian PCM found in PC Rogue Squadron (actually enum 0 which conflicts with DSP-ADPCM) */ }; const Sample* m_curSample = nullptr; /**< Current sample entry playing */ const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */ diff --git a/lib/AudioGroup.cpp b/lib/AudioGroup.cpp index e0c154d..7381b74 100644 --- a/lib/AudioGroup.cpp +++ b/lib/AudioGroup.cpp @@ -15,7 +15,7 @@ AudioGroup::AudioGroup(const AudioGroupData& data, GCNDataTag) AudioGroup::AudioGroup(const AudioGroupData& data, N64DataTag) : m_proj(data.getProj(), N64DataTag{}), m_pool(data.getPool()), - m_sdir(data.getSdir(), N64DataTag{}), + m_sdir(data.getSdir(), data.getSamp(), N64DataTag{}), m_samp(data.getSamp()), m_fmt(DataFormat::N64) {} diff --git a/lib/AudioGroupProject.cpp b/lib/AudioGroupProject.cpp index 984d682..733f024 100644 --- a/lib/AudioGroupProject.cpp +++ b/lib/AudioGroupProject.cpp @@ -187,8 +187,7 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, N64DataT /* MIDI setups */ const uint8_t* setupData = subData + header.midiSetupsOff; - const uint8_t* setupEnd = subData + header.groupEndOff; - while (setupData < setupEnd) + while (*reinterpret_cast(setupData) != 0xffffffff) { ++midiSetupCount; setupData += 8 * 16 + 4; @@ -252,8 +251,7 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, N64DataTag) /* MIDI setups */ const uint8_t* setupData = subData + header.midiSetupsOff; - const uint8_t* setupEnd = subData + header.groupEndOff; - while (setupData < setupEnd) + while (*reinterpret_cast(setupData) != 0xffffffff) { uint16_t songId = SBig(*reinterpret_cast(setupData)); const std::array* midiSetups = @@ -329,8 +327,7 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, PCDataTa /* MIDI setups */ const uint8_t* setupData = subData + group->midiSetupsOff; - const uint8_t* setupEnd = subData + group->groupEndOff; - while (setupData < setupEnd) + while (*reinterpret_cast(setupData) != 0xffffffff) { ++midiSetupCount; setupData += 8 * 16 + 4; @@ -392,10 +389,9 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, PCDataTag) /* MIDI setups */ const uint8_t* setupData = subData + group->midiSetupsOff; - const uint8_t* setupEnd = subData + group->groupEndOff; - while (setupData < setupEnd) + while (*reinterpret_cast(setupData) != 0xffffffff) { - uint16_t songId = SBig(*reinterpret_cast(setupData)); + uint16_t songId = *reinterpret_cast(setupData); const std::array* midiSetups = reinterpret_cast*>(setupData + 4); @@ -438,7 +434,7 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, PCDataTag) AudioGroupProject AudioGroupProject::CreateAudioGroupProject(const AudioGroupData& data) { - switch (data.m_fmt) + switch (data.getDataFormat()) { case DataFormat::GCN: return AudioGroupProject(data.getProj(), GCNDataTag{}); diff --git a/lib/AudioGroupSampleDirectory.cpp b/lib/AudioGroupSampleDirectory.cpp index 0f43903..468561c 100644 --- a/lib/AudioGroupSampleDirectory.cpp +++ b/lib/AudioGroupSampleDirectory.cpp @@ -1,5 +1,6 @@ #include "amuse/AudioGroupSampleDirectory.hpp" #include "amuse/Common.hpp" +#include namespace amuse { @@ -16,18 +17,25 @@ void AudioGroupSampleDirectory::Entry::swapBig() m_adpcmParmOffset = SBig(m_adpcmParmOffset); } -void AudioGroupSampleDirectory::ADPCMParms::swapBig() +void AudioGroupSampleDirectory::ADPCMParms::swapBigDSP() { - m_bytesPerFrame = SBig(m_bytesPerFrame); - m_hist2 = SBig(m_hist2); - m_hist1 = SBig(m_hist1); + dsp.m_bytesPerFrame = SBig(dsp.m_bytesPerFrame); + dsp.m_hist2 = SBig(dsp.m_hist2); + dsp.m_hist1 = SBig(dsp.m_hist1); for (int i=0 ; i<8 ; ++i) { - m_coefs[i][0] = SBig(m_coefs[i][0]); - m_coefs[i][1] = SBig(m_coefs[i][1]); + dsp.m_coefs[i][0] = SBig(dsp.m_coefs[i][0]); + dsp.m_coefs[i][1] = SBig(dsp.m_coefs[i][1]); } } +void AudioGroupSampleDirectory::ADPCMParms::swapBigVADPCM() +{ + int16_t* allCoefs = reinterpret_cast(vadpcm.m_coefs[0][0]); + for (int i=0 ; i<128 ; ++i) + allCoefs[i] = SBig(allCoefs[i]); +} + AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, GCNDataTag) { const unsigned char* cur = data; @@ -46,7 +54,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, reinterpret_cast(data + store.first.m_adpcmParmOffset); store.second = *adpcm; - store.second.swapBig(); + store.second.swapBigDSP(); } cur += 32; @@ -86,7 +94,8 @@ struct MusyX1SdirEntry } }; -AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, N64DataTag) +AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, + const unsigned char* sampData, N64DataTag) { const unsigned char* cur = data; while (*reinterpret_cast(cur) != 0xffffffff) @@ -97,6 +106,9 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, std::pair& store = m_entries[ent.m_sfxId]; ent.setIntoMusyX2(store.first); + memcpy(&store.second.vadpcm.m_coefs, sampData + ent.m_sampleOff, 256); + store.second.swapBigVADPCM(); + cur += 24; } } diff --git a/lib/ContainerRegistry.cpp b/lib/ContainerRegistry.cpp index af635bd..6a9956a 100644 --- a/lib/ContainerRegistry.cpp +++ b/lib/ContainerRegistry.cpp @@ -518,19 +518,16 @@ static bool ValidateRS1N64(FILE* fp) if (endPos > 32 * 1024 * 1024) return false; /* N64 ROM definitely won't exceed 32MB */ - FSeek(fp, 59, SEEK_SET); - uint32_t gameId; - fread(&gameId, 1, 4, fp); - if (gameId != 0x4e525345 && gameId != 0x4553524e) - return false; /* GameId not 'NRSE' */ - FSeek(fp, 0, SEEK_SET); - std::unique_ptr data(new uint8_t[endPos]); fread(data.get(), 1, endPos, fp); if ((data[0] & 0x80) != 0x80 && (data[3] & 0x80) == 0x80) SwapN64Rom(data.get(), endPos); + const uint32_t* gameId = reinterpret_cast(&data[59]); + if (*gameId != 0x4e525345 && *gameId != 0x4553524e) + return false; /* GameId not 'NRSE' */ + const uint8_t* dataSeg = reinterpret_cast(memmem(data.get(), endPos, "dbg_data\0\0\0\0\0\0\0\0", 16)); if (dataSeg) diff --git a/lib/N64MusyXCodec.c b/lib/N64MusyXCodec.c index 52491cd..6df84f4 100644 --- a/lib/N64MusyXCodec.c +++ b/lib/N64MusyXCodec.c @@ -1,6 +1,7 @@ #include "amuse/N64MusyXCodec.h" #include #include +#include /* Acknowledgements: * SubDrag for N64 Sound Tool (http://www.goldeneyevault.com/viewfile.php?id=212) @@ -72,68 +73,44 @@ static unsigned adpcm_decode_upto_8_samples(int16_t* dst, const int16_t* src, return size; } -static unsigned adpcm_decode_upto_8_samples_ranged(int16_t* dst, const int16_t* src, - const int16_t* cb_entry, - const int16_t* last_samples, - unsigned firstSample, - unsigned size) -{ - if (firstSample >= size) - return 0; - - const int16_t* book1 = cb_entry; - const int16_t* book2 = cb_entry + 8; - - int16_t l1 = last_samples[0]; - int16_t l2 = last_samples[1]; - - int accu; - - unsigned ret = 0; - for (unsigned i=firstSample ; i> 11); - ++ret; - } - - return ret; -} - unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in, const int16_t coefs[8][2][8], unsigned lastSample) { int16_t frame[32]; + unsigned remSamples = lastSample; unsigned samples = 0; - unsigned procSamples; + unsigned procSamples, procRes; unsigned char c2 = in[0x8]; c2 = c2 % 0x80; - const int16_t* bookPredictors = coefs[(c2 & 0xf0) >> 8][0]; + const int16_t* bookPredictors = coefs[(c2 & 0xf0) >> 4][0]; unsigned int rshift = (c2 & 0x0f); adpcm_get_predicted_frame(frame, &in[0x0], &in[0x8], rshift); - procSamples = (lastSample < 2) ? lastSample : 2; + procSamples = (remSamples < 2) ? remSamples : 2; memcpy(out, frame, 2 * procSamples); samples += procSamples; + remSamples -= procSamples; if (samples == lastSample) return samples; -#define adpcm_decode_upto_end_samples(inOffset, bookPredictors, outOffset, size) \ - procSamples = (lastSample < size) ? lastSample : size; \ - samples += adpcm_decode_upto_8_samples(out + inOffset, frame + inOffset, bookPredictors, out + outOffset, procSamples); \ +#define adpcm_decode_upto_end_samples(inOffset, outOffset, size) \ + procSamples = (remSamples < size) ? remSamples : size; \ + procRes = adpcm_decode_upto_8_samples(out + inOffset, frame + inOffset, bookPredictors, \ + out + outOffset, procSamples); \ + samples += procRes; \ + remSamples -= procRes; \ if (samples == lastSample) \ return samples; - adpcm_decode_upto_end_samples(2, bookPredictors, 0, 6); - adpcm_decode_upto_end_samples(8, bookPredictors, 6, 8); - adpcm_decode_upto_end_samples(16, bookPredictors, 14, 8); - adpcm_decode_upto_end_samples(24, bookPredictors, 22, 8); + adpcm_decode_upto_end_samples(2, 0, 6); + adpcm_decode_upto_end_samples(8, 6, 8); + adpcm_decode_upto_end_samples(16, 14, 8); + adpcm_decode_upto_end_samples(24, 22, 8); out += 32; @@ -141,21 +118,22 @@ unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in, c2 = in[0x18]; c2 = c2 % 0x80; - bookPredictors = coefs[(c2 & 0xf0) >> 8][0]; + bookPredictors = coefs[(c2 & 0xf0) >> 4][0]; rshift = (c2 & 0x0f); adpcm_get_predicted_frame(frame, &in[0x4], &in[0x18], rshift); - procSamples = (lastSample < 2) ? lastSample : 2; + procSamples = (remSamples < 2) ? remSamples : 2; memcpy(out, frame, 2 * procSamples); samples += procSamples; + remSamples -= procSamples; if (samples == lastSample) return samples; - adpcm_decode_upto_end_samples(2, bookPredictors, 0, 6); - adpcm_decode_upto_end_samples(8, bookPredictors, 6, 8); - adpcm_decode_upto_end_samples(16, bookPredictors, 14, 8); - adpcm_decode_upto_end_samples(24, bookPredictors, 22, 8); + adpcm_decode_upto_end_samples(2, 0, 6); + adpcm_decode_upto_end_samples(8, 6, 8); + adpcm_decode_upto_end_samples(16, 14, 8); + adpcm_decode_upto_end_samples(24, 22, 8); return samples; } @@ -164,61 +142,10 @@ unsigned N64MusyXDecompressFrameRanged(int16_t* out, const uint8_t* in, const int16_t coefs[8][2][8], unsigned firstSample, unsigned lastSample) { - int16_t frame[32]; - - unsigned samples = 0; - unsigned procSamples; - - unsigned char c2 = in[0x8]; - c2 = c2 % 0x80; - - const int16_t* bookPredictors = coefs[(c2 & 0xf0) >> 8][0]; - unsigned int rshift = (c2 & 0x0f); - - adpcm_get_predicted_frame(frame, &in[0x0], &in[0x8], rshift); - - procSamples = (lastSample < 2) ? lastSample : 2; - memcpy(out, frame, 2 * procSamples); - samples += procSamples; - firstSample = (2 > firstSample) ? 0 : (firstSample - 2); - if (samples == lastSample) - return samples; - -#define adpcm_decode_upto_end_samples_ranged(inOffset, bookPredictors, outOffset, size) \ - procSamples = (lastSample < size) ? lastSample : size; \ - samples += adpcm_decode_upto_8_samples_ranged(out + inOffset, frame + inOffset, bookPredictors, out + outOffset, firstSample, procSamples); \ - firstSample = (size > firstSample) ? 0 : (firstSample - size); \ - if (samples == lastSample) \ - return samples;\ - - adpcm_decode_upto_end_samples_ranged(2, bookPredictors, 0, 6); - adpcm_decode_upto_end_samples_ranged(8, bookPredictors, 6, 8); - adpcm_decode_upto_end_samples_ranged(16, bookPredictors, 14, 8); - adpcm_decode_upto_end_samples_ranged(24, bookPredictors, 22, 8); - - out += 32; - - - c2 = in[0x18]; - c2 = c2 % 0x80; - - bookPredictors = coefs[(c2 & 0xf0) >> 8][0]; - rshift = (c2 & 0x0f); - - adpcm_get_predicted_frame(frame, &in[0x4], &in[0x18], rshift); - - procSamples = (lastSample < 2) ? lastSample : 2; - memcpy(out, frame, 2 * procSamples); - samples += procSamples; - firstSample = (2 > firstSample) ? 0 : (firstSample - 2); - if (samples == lastSample) - return samples; - - adpcm_decode_upto_end_samples_ranged(2, bookPredictors, 0, 6); - adpcm_decode_upto_end_samples_ranged(8, bookPredictors, 6, 8); - adpcm_decode_upto_end_samples_ranged(16, bookPredictors, 14, 8); - adpcm_decode_upto_end_samples_ranged(24, bookPredictors, 22, 8); - + int16_t final[64]; + unsigned procSamples = N64MusyXDecompressFrame(final, in, coefs, firstSample + lastSample); + unsigned samples = procSamples - firstSample; + memcpy(out, final + firstSample, samples * 2); return samples; } diff --git a/lib/Voice.cpp b/lib/Voice.cpp index 13e2023..c79272f 100644 --- a/lib/Voice.cpp +++ b/lib/Voice.cpp @@ -94,8 +94,11 @@ bool Voice::_checkSamplePos(bool& looped) { /* Turn over looped sample */ m_curSamplePos = m_curSample->first.m_loopStartSample; - m_prev1 = m_curSample->second.m_hist1; - m_prev2 = m_curSample->second.m_hist2; + if (m_curFormat == SampleFormat::DSP) + { + m_prev1 = m_curSample->second.dsp.m_hist1; + m_prev2 = m_curSample->second.dsp.m_hist2; + } looped = true; } else @@ -408,10 +411,17 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data) case SampleFormat::DSP: { decSamples = DSPDecompressFrameRanged(data, m_curSampleData + 8 * block, - m_curSample->second.m_coefs, + m_curSample->second.dsp.m_coefs, &m_prev1, &m_prev2, rem, remCount); break; } + case SampleFormat::N64: + { + decSamples = N64MusyXDecompressFrameRanged(data, m_curSampleData + 256 + 40 * block, + m_curSample->second.vadpcm.m_coefs, + rem, remCount); + break; + } case SampleFormat::PCM: { const int16_t* pcm = reinterpret_cast(m_curSampleData); @@ -421,11 +431,12 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data) decSamples = remCount; break; } - case SampleFormat::N64: + case SampleFormat::PCM_PC: { - decSamples = N64MusyXDecompressFrameRanged(data, m_curSampleData + 256 + 40 * block, - reinterpret_cast(m_curSampleData), - rem, remCount); + const int16_t* pcm = reinterpret_cast(m_curSampleData); + remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos); + memcpy(data, pcm + m_curSamplePos, remCount * sizeof(int16_t)); + decSamples = remCount; break; } default: @@ -465,10 +476,17 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data) case SampleFormat::DSP: { decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block, - m_curSample->second.m_coefs, + m_curSample->second.dsp.m_coefs, &m_prev1, &m_prev2, remCount); break; } + case SampleFormat::N64: + { + decSamples = N64MusyXDecompressFrame(data, m_curSampleData + 256 + 40 * block, + m_curSample->second.vadpcm.m_coefs, + remCount); + break; + } case SampleFormat::PCM: { const int16_t* pcm = reinterpret_cast(m_curSampleData); @@ -478,11 +496,12 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data) decSamples = remCount; break; } - case SampleFormat::N64: + case SampleFormat::PCM_PC: { - decSamples = N64MusyXDecompressFrame(data, m_curSampleData + 256 + 40 * block, - reinterpret_cast(m_curSampleData), - remCount); + const int16_t* pcm = reinterpret_cast(m_curSampleData); + remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos); + memcpy(data, pcm + m_curSamplePos, remCount * sizeof(int16_t)); + decSamples = remCount; break; } default: @@ -725,13 +744,17 @@ void Voice::startSample(int16_t sampId, int32_t offset) m_curSampleData = m_audioGroup.getSampleData(m_curSample->first.m_sampleOff); m_prev1 = 0; m_prev2 = 0; - m_curFormat = SampleFormat(m_curSample->first.m_numSamples >> 24); + if (m_audioGroup.getDataFormat() == DataFormat::PC) + m_curFormat = SampleFormat::PCM_PC; + else + m_curFormat = SampleFormat(m_curSample->first.m_numSamples >> 24); m_lastSamplePos = m_curSample->first.m_loopLengthSamples ? (m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) : numSamples; if (m_curFormat != SampleFormat::DSP && m_curFormat != SampleFormat::PCM && - m_curFormat != SampleFormat::N64) + m_curFormat != SampleFormat::N64 && + m_curFormat != SampleFormat::PCM_PC) { m_curSample = nullptr; return; @@ -746,11 +769,11 @@ void Voice::startSample(int16_t sampId, int32_t offset) uint32_t block = m_curSamplePos / 14; uint32_t rem = m_curSamplePos % 14; for (uint32_t b = 0 ; b < block ; ++b) - DSPDecompressFrameStateOnly(m_curSampleData + 8 * b, m_curSample->second.m_coefs, + DSPDecompressFrameStateOnly(m_curSampleData + 8 * b, m_curSample->second.dsp.m_coefs, &m_prev1, &m_prev2, 14); if (rem) - DSPDecompressFrameStateOnly(m_curSampleData + 8 * block, m_curSample->second.m_coefs, + DSPDecompressFrameStateOnly(m_curSampleData + 8 * block, m_curSample->second.dsp.m_coefs, &m_prev1, &m_prev2, rem); } }