MusyX 1 compatibility fixes, Rogue Squadron N64 decoding confirmed

This commit is contained in:
Jack Andersen 2016-05-28 18:31:58 -10:00
parent 3ba25fd06a
commit 80cc9f7c94
9 changed files with 121 additions and 156 deletions

View File

@ -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 */

View File

@ -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<uint16_t, std::pair<Entry, ADPCMParms>> 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);
};

View File

@ -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 */

View File

@ -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)
{}

View File

@ -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<const uint32_t*>(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<const uint32_t*>(setupData) != 0xffffffff)
{
uint16_t songId = SBig(*reinterpret_cast<const uint16_t*>(setupData));
const std::array<MusyX1MIDISetup, 16>* 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<const uint32_t*>(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<const uint32_t*>(setupData) != 0xffffffff)
{
uint16_t songId = SBig(*reinterpret_cast<const uint16_t*>(setupData));
uint16_t songId = *reinterpret_cast<const uint16_t*>(setupData);
const std::array<MusyX1MIDISetup, 16>* midiSetups =
reinterpret_cast<const std::array<MusyX1MIDISetup, 16>*>(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{});

View File

@ -1,5 +1,6 @@
#include "amuse/AudioGroupSampleDirectory.hpp"
#include "amuse/Common.hpp"
#include <string.h>
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<int16_t*>(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<const AudioGroupSampleDirectory::ADPCMParms*>(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<const uint32_t*>(cur) != 0xffffffff)
@ -97,6 +106,9 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data,
std::pair<Entry, ADPCMParms>& 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;
}
}

View File

@ -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<uint8_t[]> 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<const uint32_t*>(&data[59]);
if (*gameId != 0x4e525345 && *gameId != 0x4553524e)
return false; /* GameId not 'NRSE' */
const uint8_t* dataSeg = reinterpret_cast<const uint8_t*>(memmem(data.get(), endPos,
"dbg_data\0\0\0\0\0\0\0\0", 16));
if (dataSeg)

View File

@ -1,6 +1,7 @@
#include "amuse/N64MusyXCodec.h"
#include <stdint.h>
#include <string.h>
#include <stdio.h>
/* 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<size ; ++i)
{
accu = (int)src[i] << 11;
accu += book1[i] * l1 + book2[i] * l2 + rdot(i, book2, src);
*dst++ = N64MusyXSampClamp(accu >> 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;
}

View File

@ -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<const int16_t*>(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<const int16_t(*)[2][8]>(m_curSampleData),
rem, remCount);
const int16_t* pcm = reinterpret_cast<const int16_t*>(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<const int16_t*>(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<const int16_t(*)[2][8]>(m_curSampleData),
remCount);
const int16_t* pcm = reinterpret_cast<const int16_t*>(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);
}
}