4 Commits
v1.4 ... v1.7

Author SHA1 Message Date
Jack Andersen
fe78a675d7 Change default volume to 80% to fill newfound headroom 2016-07-03 17:35:37 -10:00
Jack Andersen
3427515960 Add Starfox Adventures midi.wad support 2016-07-03 12:41:31 -10:00
Jack Andersen
5ad8c06b99 add SongState::DetectVersion for much less hacky version-detection 2016-07-02 11:50:38 -10:00
Jack Andersen
e99dbc7e0a Initialization fix in SongConverter as indicated by valgrind 2016-06-30 09:30:13 -10:00
9 changed files with 479 additions and 225 deletions

View File

@@ -72,8 +72,9 @@ static bool ExtractAudioGroup(const amuse::SystemString& inPath, const amuse::Sy
if (fp) if (fp)
{ {
Log.report(logvisor::Info, _S("Extracting %s"), pair.first.c_str()); Log.report(logvisor::Info, _S("Extracting %s"), pair.first.c_str());
amuse::SongConverter::Target extractedTarget; int extractedVersion;
std::vector<uint8_t> mid = amuse::SongConverter::SongToMIDI(pair.second.m_data.get(), extractedTarget); bool isBig;
std::vector<uint8_t> mid = amuse::SongConverter::SongToMIDI(pair.second.m_data.get(), extractedVersion, isBig);
fwrite(mid.data(), 1, mid.size(), fp); fwrite(mid.data(), 1, mid.size(), fp);
fclose(fp); fclose(fp);
} }
@@ -82,7 +83,7 @@ static bool ExtractAudioGroup(const amuse::SystemString& inPath, const amuse::Sy
return true; return true;
} }
static bool BuildSNG(const amuse::SystemString& inPath, const amuse::SystemString& targetPath, amuse::SongConverter::Target target) static bool BuildSNG(const amuse::SystemString& inPath, const amuse::SystemString& targetPath, int version, bool big)
{ {
FILE* fp = amuse::FOpen(inPath.c_str(), _S("rb")); FILE* fp = amuse::FOpen(inPath.c_str(), _S("rb"));
if (!fp) if (!fp)
@@ -95,7 +96,7 @@ static bool BuildSNG(const amuse::SystemString& inPath, const amuse::SystemStrin
fread(&data[0], 1, sz, fp); fread(&data[0], 1, sz, fp);
fclose(fp); fclose(fp);
std::vector<uint8_t> out = amuse::SongConverter::MIDIToSong(data, target); std::vector<uint8_t> out = amuse::SongConverter::MIDIToSong(data, version, big);
if (out.empty()) if (out.empty())
return false; return false;
@@ -119,13 +120,12 @@ static bool ExtractSNG(const amuse::SystemString& inPath, const amuse::SystemStr
fread(&data[0], 1, sz, fp); fread(&data[0], 1, sz, fp);
fclose(fp); fclose(fp);
amuse::SongConverter::Target target; int extractedVersion;
std::vector<uint8_t> out = amuse::SongConverter::SongToMIDI(data.data(), target); bool isBig;
std::vector<uint8_t> out = amuse::SongConverter::SongToMIDI(data.data(), extractedVersion, isBig);
if (out.empty()) if (out.empty())
return false; return false;
ReportConvType(ConvType(target));
fp = amuse::FOpen(targetPath.c_str(), _S("wb")); fp = amuse::FOpen(targetPath.c_str(), _S("wb"));
fwrite(out.data(), 1, out.size(), fp); fwrite(out.data(), 1, out.size(), fp);
fclose(fp); fclose(fp);
@@ -177,7 +177,7 @@ int main(int argc, const amuse::SystemChar** argv)
!amuse::CompareCaseInsensitive(dot, _S(".midi"))) !amuse::CompareCaseInsensitive(dot, _S(".midi")))
{ {
ReportConvType(type); ReportConvType(type);
good = BuildSNG(barePath, argv[2], amuse::SongConverter::Target(type)); good = BuildSNG(barePath, argv[2], 1, true);
} }
else if (!amuse::CompareCaseInsensitive(dot, _S(".son")) || else if (!amuse::CompareCaseInsensitive(dot, _S(".son")) ||
!amuse::CompareCaseInsensitive(dot, _S(".sng"))) !amuse::CompareCaseInsensitive(dot, _S(".sng")))

View File

@@ -132,7 +132,7 @@ struct AppCallback : boo::IApplicationCallback
int8_t m_lastChanProg = -1; int8_t m_lastChanProg = -1;
/* Control state */ /* Control state */
float m_volume = 0.5f; float m_volume = 0.8f;
float m_modulation = 0.f; float m_modulation = 0.f;
float m_pitchBend = 0.f; float m_pitchBend = 0.f;
bool m_updateDisp = false; bool m_updateDisp = false;
@@ -724,8 +724,27 @@ struct AppCallback : boo::IApplicationCallback
int idx = 0; int idx = 0;
for (const auto& pair : songs) for (const auto& pair : songs)
{ {
const amuse::ContainerRegistry::SongData& sngData = pair.second;
int16_t grpId = sngData.m_groupId;
int16_t setupId = sngData.m_setupId;
if (sngData.m_groupId == -1 && sngData.m_setupId != -1)
{
for (const auto& pair : allSongGroups)
{
for (const auto& setup : pair.second.second->m_midiSetups)
{
if (setup.first == sngData.m_setupId)
{
grpId = pair.first;
break;
}
}
if (grpId != -1)
break;
}
}
amuse::Printf(_S(" %d %s (Group %d, Setup %d)\n"), idx++, amuse::Printf(_S(" %d %s (Group %d, Setup %d)\n"), idx++,
pair.first.c_str(), pair.second.m_groupId, pair.second.m_setupId); pair.first.c_str(), grpId, setupId);
} }
int userSel = 0; int userSel = 0;
@@ -757,7 +776,25 @@ struct AppCallback : boo::IApplicationCallback
} }
} }
/* Get group selection from user */ /* Get group selection via setup search */
if (m_groupId == -1 && m_setupId != -1)
{
for (const auto& pair : allSongGroups)
{
for (const auto& setup : pair.second.second->m_midiSetups)
{
if (setup.first == m_setupId)
{
m_groupId = pair.first;
break;
}
}
if (m_groupId != -1)
break;
}
}
/* Get group selection via user */
if (m_groupId != -1) if (m_groupId != -1)
{ {
if (allSongGroups.find(m_groupId) != allSongGroups.end()) if (allSongGroups.find(m_groupId) != allSongGroups.end())

View File

@@ -10,14 +10,8 @@ namespace amuse
class SongConverter class SongConverter
{ {
public: public:
enum class Target static std::vector<uint8_t> SongToMIDI(const unsigned char* data, int& versionOut, bool& isBig);
{ static std::vector<uint8_t> MIDIToSong(const std::vector<uint8_t>& data, int version, bool big);
N64,
GCN,
PC
};
static std::vector<uint8_t> SongToMIDI(const unsigned char* data, Target& targetOut);
static std::vector<uint8_t> MIDIToSong(const std::vector<uint8_t>& data, Target target);
}; };
} }

View File

@@ -57,6 +57,7 @@ class SongState
}; };
const unsigned char* m_songData = nullptr; /**< Base pointer to active song */ const unsigned char* m_songData = nullptr; /**< Base pointer to active song */
int m_sngVersion; /**< Detected song revision, 1 has RLE-compressed delta-times */
bool m_bigEndian; /**< True if loaded song is big-endian data */ bool m_bigEndian; /**< True if loaded song is big-endian data */
/** State of a single track within arrangement */ /** State of a single track within arrangement */
@@ -104,8 +105,13 @@ class SongState
double m_curDt = 0.f; /**< Cumulative dt value for time-remainder tracking */ double m_curDt = 0.f; /**< Cumulative dt value for time-remainder tracking */
public: public:
/** Determine SNG version
* @param isBig returns true if big-endian SNG
* @return 0 for initial version, 1 for delta-time revision, -1 for non-SNG */
static int DetectVersion(const unsigned char* ptr, bool& isBig);
/** initialize state for Song data at `ptr` */ /** initialize state for Song data at `ptr` */
void initialize(const unsigned char* ptr); bool initialize(const unsigned char* ptr);
/** advances `dt` seconds worth of commands in the Song /** advances `dt` seconds worth of commands in the Song
* @return `true` if END reached * @return `true` if END reached

View File

@@ -7,10 +7,10 @@ IntrusiveAudioGroupData::~IntrusiveAudioGroupData()
{ {
if (m_owns) if (m_owns)
{ {
delete m_pool; delete[] m_pool;
delete m_proj; delete[] m_proj;
delete m_sdir; delete[] m_sdir;
delete m_samp; delete[] m_samp;
} }
} }
@@ -27,10 +27,10 @@ IntrusiveAudioGroupData& IntrusiveAudioGroupData::operator=(IntrusiveAudioGroupD
{ {
if (m_owns) if (m_owns)
{ {
delete m_pool; delete[] m_pool;
delete m_proj; delete[] m_proj;
delete m_sdir; delete[] m_sdir;
delete m_samp; delete[] m_samp;
} }
m_owns = other.m_owns; m_owns = other.m_owns;

View File

@@ -1679,6 +1679,72 @@ static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadRS3(FIL
return ret; return ret;
} }
static bool ValidateStarFoxAdvSongs(FILE* fp)
{
size_t endPos = FileLength(fp);
if (endPos > 2 * 1024 * 1024)
return false;
std::unique_ptr<uint8_t[]> data(new uint8_t[endPos]);
fread(data.get(), 1, endPos, fp);
const uint32_t* lengths = reinterpret_cast<const uint32_t*>(data.get());
size_t totalLen = 0;
int i=0;
for (; i<128 ; ++i)
{
uint32_t len = SBig(lengths[i]);
if (len == 0)
break;
totalLen += len;
totalLen = ((totalLen + 31) & ~31);
}
totalLen += (((i*4) + 31) & ~31);
return totalLen == endPos;
}
static std::vector<std::pair<SystemString, ContainerRegistry::SongData>> LoadStarFoxAdvSongs(FILE* midifp)
{
std::vector<std::pair<SystemString, ContainerRegistry::SongData>> ret;
size_t endPos = FileLength(midifp);
if (endPos > 2 * 1024 * 1024)
return {};
std::unique_ptr<uint8_t[]> data(new uint8_t[endPos]);
fread(data.get(), 1, endPos, midifp);
const uint32_t* lengths = reinterpret_cast<const uint32_t*>(data.get());
int i=0;
for (; i<128 ; ++i)
{
uint32_t len = SBig(lengths[i]);
if (len == 0)
break;
}
size_t sngCount = i;
size_t cur = (((sngCount*4) + 31) & ~31);
for (i=0; i<sngCount ; ++i)
{
uint32_t len = SBig(lengths[i]);
if (len == 0)
break;
SystemChar name[128];
SNPrintf(name, 128, _S("Song%u"), i);
std::unique_ptr<uint8_t[]> song(new uint8_t[len]);
memmove(song.get(), data.get() + cur, len);
ret.emplace_back(name, ContainerRegistry::SongData(std::move(song), len, -1, i));
cur += len;
cur = ((cur + 31) & ~31);
}
return ret;
}
ContainerRegistry::Type ContainerRegistry::DetectContainerType(const SystemChar* path) ContainerRegistry::Type ContainerRegistry::DetectContainerType(const SystemChar* path)
{ {
FILE* fp; FILE* fp;
@@ -2062,6 +2128,13 @@ ContainerRegistry::LoadSongs(const SystemChar* path)
return ret; return ret;
} }
if (ValidateStarFoxAdvSongs(fp))
{
auto ret = LoadStarFoxAdvSongs(fp);
fclose(fp);
return ret;
}
fclose(fp); fclose(fp);
} }

View File

@@ -356,6 +356,15 @@ void Sequencer::setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val)
if (chan > 15) if (chan > 15)
return; return;
if (ctrl == 0x66)
{
printf("Loop Start\n");
}
else if (ctrl == 0x67)
{
printf("Loop End\n");
}
if (!m_chanStates[chan]) if (!m_chanStates[chan])
m_chanStates[chan].emplace(*this, chan); m_chanStates[chan].emplace(*this, chan);

View File

@@ -42,8 +42,9 @@ struct PitchEvent {};
struct Event struct Event
{ {
bool endEvent = false; bool endEvent = false;
bool controlChange = false; bool isNote = false;
bool progChange = false; bool isControlChange = false;
bool isProgChange = false;
uint8_t channel; uint8_t channel;
uint8_t noteOrCtrl; uint8_t noteOrCtrl;
uint8_t velOrVal; uint8_t velOrVal;
@@ -54,15 +55,16 @@ struct Event
int pitchBend; int pitchBend;
Event(NoteEvent, uint8_t chan, uint8_t note, uint8_t vel, uint16_t len) Event(NoteEvent, uint8_t chan, uint8_t note, uint8_t vel, uint16_t len)
: controlChange(false), channel(chan), noteOrCtrl(note), velOrVal(vel), length(len) {} : isNote(true), channel(chan), noteOrCtrl(note), velOrVal(vel), length(len) {}
Event(CtrlEvent, uint8_t chan, uint8_t note, uint8_t vel, uint16_t len) Event(CtrlEvent, uint8_t chan, uint8_t note, uint8_t vel, uint16_t len)
: controlChange(true), channel(chan), noteOrCtrl(note), velOrVal(vel), length(len) {} : isControlChange(true), channel(chan), noteOrCtrl(note), velOrVal(vel), length(len) {}
Event(ProgEvent, uint8_t chan, uint8_t prog) Event(ProgEvent, uint8_t chan, uint8_t prog)
: progChange(true), channel(chan), program(prog) {} : isProgChange(true), channel(chan), program(prog) {}
Event(PitchEvent, uint8_t chan, int pBend) : isPitchBend(true), channel(chan), pitchBend(pBend) {} Event(PitchEvent, uint8_t chan, int pBend)
: isPitchBend(true), channel(chan), pitchBend(pBend) {}
}; };
class MIDIDecoder class MIDIDecoder
@@ -616,7 +618,7 @@ static void EncodeTimeRLE(std::vector<uint8_t>& vecOut, uint32_t val)
vecOut.push_back(reinterpret_cast<const uint8_t*>(&lastPart)[1]); vecOut.push_back(reinterpret_cast<const uint8_t*>(&lastPart)[1]);
} }
std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target& targetOut) std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, int& versionOut, bool& isBig)
{ {
std::vector<uint8_t> ret = {'M', 'T', 'h', 'd'}; std::vector<uint8_t> ret = {'M', 'T', 'h', 'd'};
uint32_t six32 = SBig(uint32_t(6)); uint32_t six32 = SBig(uint32_t(6));
@@ -627,17 +629,10 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
ret.push_back(1); ret.push_back(1);
SongState song; SongState song;
song.initialize(data); if (!song.initialize(data))
return {};
if (song.m_bigEndian) versionOut = song.m_sngVersion;
{ isBig = song.m_bigEndian;
if (song.m_header.m_trackIdxOff == 0x18 || song.m_header.m_trackIdxOff == 0x58)
targetOut = Target::GCN;
else
targetOut = Target::N64;
}
else
targetOut = Target::PC;
size_t trkCount = 1; size_t trkCount = 1;
for (std::experimental::optional<SongState::Track>& trk : song.m_tracks) for (std::experimental::optional<SongState::Track>& trk : song.m_tracks)
@@ -766,9 +761,9 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
} }
/* Loop through as many commands as we can for this time period */ /* Loop through as many commands as we can for this time period */
if (song.m_header.m_trackIdxOff == 0x18 || song.m_header.m_trackIdxOff == 0x58) if (song.m_sngVersion == 1)
{ {
/* GameCube */ /* Revision */
while (true) while (true)
{ {
/* Load next command */ /* Load next command */
@@ -810,7 +805,7 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
} }
else else
{ {
/* N64 */ /* Legacy */
while (true) while (true)
{ {
/* Load next command */ /* Load next command */
@@ -862,7 +857,7 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
/* Resolve key-off events */ /* Resolve key-off events */
for (auto& pair : events) for (auto& pair : events)
{ {
if (!pair.second.controlChange) if (pair.second.isNote)
{ {
auto it = allEvents.emplace(pair.first + pair.second.length, pair.second); auto it = allEvents.emplace(pair.first + pair.second.length, pair.second);
it->second.endEvent = true; it->second.endEvent = true;
@@ -877,11 +872,11 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
encoder._sendContinuedValue(pair.first - lastTime); encoder._sendContinuedValue(pair.first - lastTime);
lastTime = pair.first; lastTime = pair.first;
if (pair.second.controlChange) if (pair.second.isControlChange)
{ {
encoder.controlChange(pair.second.channel, pair.second.noteOrCtrl, pair.second.velOrVal); encoder.controlChange(pair.second.channel, pair.second.noteOrCtrl, pair.second.velOrVal);
} }
else if (pair.second.progChange) else if (pair.second.isProgChange)
{ {
encoder.programChange(trk->m_midiChan, pair.second.program); encoder.programChange(trk->m_midiChan, pair.second.program);
} }
@@ -889,7 +884,7 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
{ {
encoder.pitchBend(trk->m_midiChan, pair.second.pitchBend); encoder.pitchBend(trk->m_midiChan, pair.second.pitchBend);
} }
else else if (pair.second.isNote)
{ {
if (pair.second.endEvent) if (pair.second.endEvent)
encoder.noteOff(pair.second.channel, pair.second.noteOrCtrl, pair.second.velOrVal); encoder.noteOff(pair.second.channel, pair.second.noteOrCtrl, pair.second.velOrVal);
@@ -918,11 +913,10 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
return ret; return ret;
} }
std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data, Target target) std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data, int version, bool big)
{ {
std::vector<uint8_t> ret; std::vector<uint8_t> ret;
std::vector<uint8_t>::const_iterator it = data.cbegin(); std::vector<uint8_t>::const_iterator it = data.cbegin();
bool bigEndian = (target == Target::GCN || target == Target::N64);
struct MIDIHeader struct MIDIHeader
{ {
@@ -969,6 +963,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
std::vector<uint8_t> eventBuf; std::vector<uint8_t> eventBuf;
std::vector<uint8_t> pitchBuf; std::vector<uint8_t> pitchBuf;
std::vector<uint8_t> modBuf; std::vector<uint8_t> modBuf;
int padding = 0;
bool operator==(const Region& other) const bool operator==(const Region& other) const
{ {
@@ -1020,7 +1015,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
++it; ++it;
for (auto& pair : tempos) for (auto& pair : tempos)
{ {
if (bigEndian) if (big)
tempoBuf.emplace_back(SBig(uint32_t(pair.first * 384 / header.div)), SBig(uint32_t(pair.second))); tempoBuf.emplace_back(SBig(uint32_t(pair.first * 384 / header.div)), SBig(uint32_t(pair.second)));
else else
tempoBuf.emplace_back(pair.first * 384 / header.div, pair.second); tempoBuf.emplace_back(pair.first * 384 / header.div, pair.second);
@@ -1077,7 +1072,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
lastModVal = 0; lastModVal = 0;
} }
if (event.second.controlChange) if (event.second.isControlChange)
{ {
if (event.second.noteOrCtrl == 1) if (event.second.noteOrCtrl == 1)
{ {
@@ -1089,55 +1084,61 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
} }
else else
{ {
if (target == Target::GCN) if (version == 1)
{ {
EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick)); EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick));
lastEventTick = eventTick; lastEventTick = eventTick;
region.eventBuf.push_back(0x80 | event.second.velOrVal); region.eventBuf.push_back(0x80 | event.second.velOrVal);
region.eventBuf.push_back(0x80 | event.second.noteOrCtrl); region.eventBuf.push_back(0x80 | event.second.noteOrCtrl);
} }
else if (target == Target::N64) else
{ {
uint32_t tickBig = SBig(uint32_t(eventTick - startTick)); if (big)
for (int i=0 ; i<4 ; ++i) {
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]); uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
region.eventBuf.push_back(0x80 | event.second.velOrVal); for (int i=0 ; i<4 ; ++i)
region.eventBuf.push_back(0x80 | event.second.noteOrCtrl); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
} region.eventBuf.push_back(0x80 | event.second.velOrVal);
else if (target == Target::PC) region.eventBuf.push_back(0x80 | event.second.noteOrCtrl);
{ }
uint32_t tick = uint32_t(eventTick - startTick); else
for (int i=0 ; i<4 ; ++i) {
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]); uint32_t tick = uint32_t(eventTick - startTick);
region.eventBuf.push_back(0x80 | event.second.velOrVal); for (int i=0 ; i<4 ; ++i)
region.eventBuf.push_back(0x80 | event.second.noteOrCtrl); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]);
region.eventBuf.push_back(0x80 | event.second.velOrVal);
region.eventBuf.push_back(0x80 | event.second.noteOrCtrl);
}
} }
} }
} }
else if (event.second.progChange) else if (event.second.isProgChange)
{ {
if (target == Target::GCN) if (version == 1)
{ {
EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick)); EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick));
lastEventTick = eventTick; lastEventTick = eventTick;
region.eventBuf.push_back(0x80 | event.second.program); region.eventBuf.push_back(0x80 | event.second.program);
region.eventBuf.push_back(0); region.eventBuf.push_back(0);
} }
else if (target == Target::N64) else
{ {
uint32_t tickBig = SBig(uint32_t(eventTick - startTick)); if (big)
for (int i=0 ; i<4 ; ++i) {
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]); uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
region.eventBuf.push_back(0x80 | event.second.program); for (int i=0 ; i<4 ; ++i)
region.eventBuf.push_back(0); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
} region.eventBuf.push_back(0x80 | event.second.program);
else if (target == Target::PC) region.eventBuf.push_back(0);
{ }
uint32_t tick = uint32_t(eventTick - startTick); else
for (int i=0 ; i<4 ; ++i) {
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]); uint32_t tick = uint32_t(eventTick - startTick);
region.eventBuf.push_back(0x80 | event.second.program); for (int i=0 ; i<4 ; ++i)
region.eventBuf.push_back(0); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]);
region.eventBuf.push_back(0x80 | event.second.program);
region.eventBuf.push_back(0);
}
} }
} }
else if (event.second.isPitchBend) else if (event.second.isPitchBend)
@@ -1148,9 +1149,9 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
EncodeContinuousRLE(region.pitchBuf, newPitch - lastPitchVal); EncodeContinuousRLE(region.pitchBuf, newPitch - lastPitchVal);
lastPitchVal = newPitch; lastPitchVal = newPitch;
} }
else else if (event.second.isNote)
{ {
if (target == Target::GCN) if (version == 1)
{ {
EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick)); EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick));
lastEventTick = eventTick; lastEventTick = eventTick;
@@ -1160,27 +1161,30 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&lenBig)[0]); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&lenBig)[0]);
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&lenBig)[1]); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&lenBig)[1]);
} }
else if (target == Target::N64) else
{ {
uint32_t tickBig = SBig(uint32_t(eventTick - startTick)); if (big)
for (int i=0 ; i<4 ; ++i) {
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]); uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
uint16_t lenBig = SBig(uint16_t(event.second.length)); for (int i=0 ; i<4 ; ++i)
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&lenBig)[0]); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&lenBig)[1]); uint16_t lenBig = SBig(uint16_t(event.second.length));
region.eventBuf.push_back(event.second.noteOrCtrl); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&lenBig)[0]);
region.eventBuf.push_back(event.second.velOrVal); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&lenBig)[1]);
} region.eventBuf.push_back(event.second.noteOrCtrl);
else if (target == Target::PC) region.eventBuf.push_back(event.second.velOrVal);
{ }
uint32_t tick = uint32_t(eventTick - startTick); else
for (int i=0 ; i<4 ; ++i) {
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]); uint32_t tick = uint32_t(eventTick - startTick);
uint16_t len = uint16_t(event.second.length); for (int i=0 ; i<4 ; ++i)
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&len)[0]); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]);
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&len)[1]); uint16_t len = uint16_t(event.second.length);
region.eventBuf.push_back(event.second.noteOrCtrl); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&len)[0]);
region.eventBuf.push_back(event.second.velOrVal); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&len)[1]);
region.eventBuf.push_back(event.second.noteOrCtrl);
region.eventBuf.push_back(event.second.velOrVal);
}
} }
} }
} }
@@ -1198,7 +1202,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
} }
/* Terminate region */ /* Terminate region */
if (target == Target::GCN) if (version == 1)
{ {
size_t pitchDelta = 0; size_t pitchDelta = 0;
size_t modDelta = 0; size_t modDelta = 0;
@@ -1211,31 +1215,34 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
region.eventBuf.push_back(0xff); region.eventBuf.push_back(0xff);
region.eventBuf.push_back(0xff); region.eventBuf.push_back(0xff);
} }
else if (target == Target::N64) else
{ {
uint32_t selTick = std::max(std::max(lastEventTick - startTick, if (big)
lastPitchTick - startTick), {
lastModTick - startTick); uint32_t selTick = std::max(std::max(lastEventTick - startTick,
uint32_t tickBig = SBig(uint32_t(selTick)); lastPitchTick - startTick),
for (int i=0 ; i<4 ; ++i) lastModTick - startTick);
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]); uint32_t tickBig = SBig(uint32_t(selTick));
region.eventBuf.push_back(0); for (int i=0 ; i<4 ; ++i)
region.eventBuf.push_back(0); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
region.eventBuf.push_back(0xff); region.eventBuf.push_back(0);
region.eventBuf.push_back(0xff); region.eventBuf.push_back(0);
} region.eventBuf.push_back(0xff);
else if (target == Target::PC) region.eventBuf.push_back(0xff);
{ }
uint32_t selTick = std::max(std::max(lastEventTick - startTick, else
lastPitchTick - startTick), {
lastModTick - startTick); uint32_t selTick = std::max(std::max(lastEventTick - startTick,
uint32_t tick = uint32_t(selTick); lastPitchTick - startTick),
for (int i=0 ; i<4 ; ++i) lastModTick - startTick);
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]); uint32_t tick = uint32_t(selTick);
region.eventBuf.push_back(0); for (int i=0 ; i<4 ; ++i)
region.eventBuf.push_back(0); region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]);
region.eventBuf.push_back(0xff); region.eventBuf.push_back(0);
region.eventBuf.push_back(0xff); region.eventBuf.push_back(0);
region.eventBuf.push_back(0xff);
region.eventBuf.push_back(0xff);
}
} }
if (region.pitchBuf.size()) if (region.pitchBuf.size())
@@ -1262,13 +1269,15 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
{ {
regionDataIdxArr.push_back(curRegionOff); regionDataIdxArr.push_back(curRegionOff);
curRegionOff += 12 + region.eventBuf.size() + region.pitchBuf.size() + region.modBuf.size(); curRegionOff += 12 + region.eventBuf.size() + region.pitchBuf.size() + region.modBuf.size();
int paddedRegOff = ((curRegionOff + 3) & ~3);
region.padding = paddedRegOff - curRegionOff;
regions.push_back(std::move(region)); regions.push_back(std::move(region));
} }
/* Region header */ /* Region header */
regionBuf.emplace_back(); regionBuf.emplace_back();
SongState::TrackRegion& reg = regionBuf.back(); SongState::TrackRegion& reg = regionBuf.back();
if (bigEndian) if (big)
{ {
reg.m_startTick = SBig(uint32_t(startTick)); reg.m_startTick = SBig(uint32_t(startTick));
reg.m_progNum = 0xff; reg.m_progNum = 0xff;
@@ -1294,7 +1303,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
/* Terminating region header */ /* Terminating region header */
regionBuf.emplace_back(); regionBuf.emplace_back();
SongState::TrackRegion& reg = regionBuf.back(); SongState::TrackRegion& reg = regionBuf.back();
if (bigEndian) if (big)
{ {
reg.m_startTick = SBig(uint32_t(lastTrackStartTick)); reg.m_startTick = SBig(uint32_t(lastTrackStartTick));
reg.m_progNum = 0xff; reg.m_progNum = 0xff;
@@ -1316,7 +1325,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
} }
} }
if (target == Target::GCN) if (version == 1)
{ {
SongState::Header head; SongState::Header head;
head.m_trackIdxOff = 0x18; head.m_trackIdxOff = 0x18;
@@ -1327,7 +1336,8 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
head.m_unkOff = 0; head.m_unkOff = 0;
uint32_t regIdxOff = head.m_regionIdxOff; uint32_t regIdxOff = head.m_regionIdxOff;
head.swapBig(); if (big)
head.swapBig();
*reinterpret_cast<SongState::Header*>(&*ret.insert(ret.cend(), 0x18, 0)) = head; *reinterpret_cast<SongState::Header*>(&*ret.insert(ret.cend(), 0x18, 0)) = head;
for (int i=0 ; i<64 ; ++i) for (int i=0 ; i<64 ; ++i)
@@ -1339,7 +1349,8 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
} }
uint32_t idx = trackRegionIdxArr[i]; uint32_t idx = trackRegionIdxArr[i];
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = SBig(uint32_t(0x18 + 4 * 64 + idx * 12)); *reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = big ? SBig(uint32_t(0x18 + 4 * 64 + idx * 12)) :
uint32_t(0x18 + 4 * 64 + idx * 12);
} }
for (SongState::TrackRegion& reg : regionBuf) for (SongState::TrackRegion& reg : regionBuf)
@@ -1347,22 +1358,25 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
uint32_t regBase = regIdxOff + 4 * regionDataIdxArr.size(); uint32_t regBase = regIdxOff + 4 * regionDataIdxArr.size();
for (uint32_t regOff : regionDataIdxArr) for (uint32_t regOff : regionDataIdxArr)
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = SBig(uint32_t(regBase + regOff)); *reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = big ? SBig(uint32_t(regBase + regOff)) :
uint32_t(regBase + regOff);
uint32_t curOffset = regBase; uint32_t curOffset = regBase;
for (Region& reg : regions) for (Region& reg : regions)
{ {
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = SBig(uint32_t(8)); *reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = big ? SBig(uint32_t(8)) : 8;
if (reg.pitchBuf.size()) if (reg.pitchBuf.size())
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = *reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) =
SBig(uint32_t(curOffset + 12 + reg.eventBuf.size())); big ? SBig(uint32_t(curOffset + 12 + reg.eventBuf.size())) :
uint32_t(curOffset + 12 + reg.eventBuf.size());
else else
ret.insert(ret.cend(), 4, 0); ret.insert(ret.cend(), 4, 0);
if (reg.modBuf.size()) if (reg.modBuf.size())
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = *reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) =
SBig(uint32_t(curOffset + 12 + reg.eventBuf.size() + reg.pitchBuf.size())); big ? SBig(uint32_t(curOffset + 12 + reg.eventBuf.size() + reg.pitchBuf.size())) :
uint32_t(curOffset + 12 + reg.eventBuf.size() + reg.pitchBuf.size());
else else
ret.insert(ret.cend(), 4, 0); ret.insert(ret.cend(), 4, 0);
@@ -1375,7 +1389,9 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
if (reg.modBuf.size()) if (reg.modBuf.size())
memmove(&*ret.insert(ret.cend(), reg.modBuf.size(), 0), reg.modBuf.data(), reg.modBuf.size()); memmove(&*ret.insert(ret.cend(), reg.modBuf.size(), 0), reg.modBuf.data(), reg.modBuf.size());
curOffset += 12 + reg.eventBuf.size() + reg.pitchBuf.size() + reg.modBuf.size(); ret.insert(ret.cend(), reg.padding, 0);
curOffset += 12 + reg.eventBuf.size() + reg.pitchBuf.size() + reg.modBuf.size() + reg.padding;
} }
memmove(&*ret.insert(ret.cend(), 64, 0), chanMap.data(), 64); memmove(&*ret.insert(ret.cend(), 64, 0), chanMap.data(), 64);
@@ -1385,7 +1401,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = uint32_t(0xffffffff); *reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = uint32_t(0xffffffff);
} }
else if (target == Target::N64) else
{ {
SongState::Header head; SongState::Header head;
head.m_trackIdxOff = 0x18 + regionBuf.size() * 12; head.m_trackIdxOff = 0x18 + regionBuf.size() * 12;
@@ -1396,7 +1412,8 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
head.m_unkOff = 0; head.m_unkOff = 0;
uint32_t chanMapOff = head.m_chanMapOff; uint32_t chanMapOff = head.m_chanMapOff;
head.swapBig(); if (big)
head.swapBig();
*reinterpret_cast<SongState::Header*>(&*ret.insert(ret.cend(), 0x18, 0)) = head; *reinterpret_cast<SongState::Header*>(&*ret.insert(ret.cend(), 0x18, 0)) = head;
for (SongState::TrackRegion& reg : regionBuf) for (SongState::TrackRegion& reg : regionBuf)
@@ -1411,7 +1428,8 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
} }
uint32_t idx = trackRegionIdxArr[i]; uint32_t idx = trackRegionIdxArr[i];
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = SBig(uint32_t(0x18 + 4 * 64 + idx * 12)); *reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = big ? SBig(uint32_t(0x18 + 4 * 64 + idx * 12)) :
uint32_t(0x18 + 4 * 64 + idx * 12);
} }
memmove(&*ret.insert(ret.cend(), 64, 0), chanMap.data(), 64); memmove(&*ret.insert(ret.cend(), 64, 0), chanMap.data(), 64);
@@ -1420,17 +1438,19 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
uint32_t curOffset = regBase; uint32_t curOffset = regBase;
for (Region& reg : regions) for (Region& reg : regions)
{ {
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = SBig(uint32_t(8)); *reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = big ? SBig(uint32_t(8)) : 8;
if (reg.pitchBuf.size()) if (reg.pitchBuf.size())
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = *reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) =
SBig(uint32_t(curOffset + 12 + reg.eventBuf.size())); big ? SBig(uint32_t(curOffset + 12 + reg.eventBuf.size())) :
uint32_t(curOffset + 12 + reg.eventBuf.size());
else else
ret.insert(ret.cend(), 4, 0); ret.insert(ret.cend(), 4, 0);
if (reg.modBuf.size()) if (reg.modBuf.size())
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = *reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) =
SBig(uint32_t(curOffset + 12 + reg.eventBuf.size() + reg.pitchBuf.size())); big ? SBig(uint32_t(curOffset + 12 + reg.eventBuf.size() + reg.pitchBuf.size())) :
uint32_t(curOffset + 12 + reg.eventBuf.size() + reg.pitchBuf.size());
else else
ret.insert(ret.cend(), 4, 0); ret.insert(ret.cend(), 4, 0);
@@ -1443,78 +1463,14 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
if (reg.modBuf.size()) if (reg.modBuf.size())
memmove(&*ret.insert(ret.cend(), reg.modBuf.size(), 0), reg.modBuf.data(), reg.modBuf.size()); memmove(&*ret.insert(ret.cend(), reg.modBuf.size(), 0), reg.modBuf.data(), reg.modBuf.size());
curOffset += 12 + reg.eventBuf.size() + reg.pitchBuf.size() + reg.modBuf.size(); ret.insert(ret.cend(), reg.padding, 0);
}
for (uint32_t regOff : regionDataIdxArr)
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = SBig(uint32_t(regBase + regOff));
if (tempoBuf.size())
memmove(&*ret.insert(ret.cend(), tempoBuf.size() * 8, 0), tempoBuf.data(), tempoBuf.size() * 8);
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = uint32_t(0xffffffff);
}
else if (target == Target::PC)
{
SongState::Header head;
head.m_trackIdxOff = 0x18 + regionBuf.size() * 12;
head.m_regionIdxOff = head.m_trackIdxOff + 4 * 64 + 64 + curRegionOff;
head.m_chanMapOff = head.m_trackIdxOff + 4 * 64;
head.m_tempoTableOff = tempoBuf.size() ? head.m_regionIdxOff + 4 * regionDataIdxArr.size() : 0;
head.m_initialTempo = initTempo;
head.m_unkOff = 0;
*reinterpret_cast<SongState::Header*>(&*ret.insert(ret.cend(), 0x18, 0)) = head;
for (SongState::TrackRegion& reg : regionBuf)
*reinterpret_cast<SongState::TrackRegion*>(&*ret.insert(ret.cend(), 12, 0)) = reg;
for (int i=0 ; i<64 ; ++i)
{
if (i >= trackRegionIdxArr.size())
{
ret.insert(ret.cend(), 4, 0);
continue;
}
uint32_t idx = trackRegionIdxArr[i];
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = uint32_t(0x18 + 4 * 64 + idx * 12);
}
memmove(&*ret.insert(ret.cend(), 64, 0), chanMap.data(), 64);
uint32_t regBase = head.m_chanMapOff + 64;
uint32_t curOffset = regBase;
for (Region& reg : regions)
{
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = uint32_t(8);
if (reg.pitchBuf.size())
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) =
uint32_t(curOffset + 12 + reg.eventBuf.size());
else
ret.insert(ret.cend(), 4, 0);
if (reg.modBuf.size())
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) =
uint32_t(curOffset + 12 + reg.eventBuf.size() + reg.pitchBuf.size());
else
ret.insert(ret.cend(), 4, 0);
if (reg.eventBuf.size())
memmove(&*ret.insert(ret.cend(), reg.eventBuf.size(), 0), reg.eventBuf.data(), reg.eventBuf.size());
if (reg.pitchBuf.size())
memmove(&*ret.insert(ret.cend(), reg.pitchBuf.size(), 0), reg.pitchBuf.data(), reg.pitchBuf.size());
if (reg.modBuf.size())
memmove(&*ret.insert(ret.cend(), reg.modBuf.size(), 0), reg.modBuf.data(), reg.modBuf.size());
curOffset += 12 + reg.eventBuf.size() + reg.pitchBuf.size() + reg.modBuf.size(); curOffset += 12 + reg.eventBuf.size() + reg.pitchBuf.size() + reg.modBuf.size();
} }
for (uint32_t regOff : regionDataIdxArr) for (uint32_t regOff : regionDataIdxArr)
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = uint32_t(regBase + regOff); *reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = big ? SBig(uint32_t(regBase + regOff)) :
uint32_t(regBase + regOff);
if (tempoBuf.size()) if (tempoBuf.size())
memmove(&*ret.insert(ret.cend(), tempoBuf.size() * 8, 0), tempoBuf.data(), tempoBuf.size() * 8); memmove(&*ret.insert(ret.cend(), tempoBuf.size() * 8, 0), tempoBuf.data(), tempoBuf.size() * 8);

View File

@@ -18,7 +18,10 @@ static uint32_t DecodeRLE(const unsigned char*& data)
++data; ++data;
thisPart = thisPart * 256 + *data; thisPart = thisPart * 256 + *data;
if (thisPart == 0) if (thisPart == 0)
{
++data;
return -1; return -1;
}
} }
if (thisPart == 32767) if (thisPart == 32767)
@@ -131,7 +134,7 @@ void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
seq->setPitchWheel(m_midiChan, clamp(-1.f, m_lastPitchVal / 32768.f, 1.f)); seq->setPitchWheel(m_midiChan, clamp(-1.f, m_lastPitchVal / 32768.f, 1.f));
seq->setCtrlValue(m_midiChan, 1, clamp(0, m_lastModVal * 128 / 16384, 127)); seq->setCtrlValue(m_midiChan, 1, clamp(0, m_lastModVal * 128 / 16384, 127));
} }
if (m_parent.m_header.m_trackIdxOff == 0x18 || m_parent.m_header.m_trackIdxOff == 0x58) if (m_parent.m_sngVersion == 1)
m_eventWaitCountdown = int32_t(DecodeTimeRLE(m_data)); m_eventWaitCountdown = int32_t(DecodeTimeRLE(m_data));
else else
{ {
@@ -148,9 +151,183 @@ void SongState::Track::advanceRegion(Sequencer* seq)
setRegion(seq, m_nextRegion); setRegion(seq, m_nextRegion);
} }
void SongState::initialize(const unsigned char* ptr) int SongState::DetectVersion(const unsigned char* ptr, bool& isBig)
{ {
m_bigEndian = ptr[0] == 0; isBig = ptr[0] == 0;
Header header = *reinterpret_cast<const Header*>(ptr);
if (isBig)
header.swapBig();
const uint32_t* trackIdx = reinterpret_cast<const uint32_t*>(ptr + header.m_trackIdxOff);
const uint32_t* regionIdxTable = reinterpret_cast<const uint32_t*>(ptr + header.m_regionIdxOff);
/* First determine maximum index of MIDI regions across all tracks */
uint32_t maxRegionIdx = 0;
for (int i=0 ; i<64 ; ++i)
{
if (trackIdx[i])
{
const TrackRegion* region = nullptr;
const TrackRegion* nextRegion = reinterpret_cast<const TrackRegion*>(ptr + (isBig ? SBig(trackIdx[i]) : trackIdx[i]));
/* Iterate all regions */
while (nextRegion->indexValid(isBig))
{
region = nextRegion;
uint32_t regionIdx = (isBig ? SBig(region->m_regionIndex) :
region->m_regionIndex);
maxRegionIdx = std::max(maxRegionIdx, regionIdx);
nextRegion = &region[1];
}
}
}
/* Perform 2 trials, first assuming revised format (more likely) */
int v=1;
for (; v>=0 ; --v)
{
bool bad = false;
/* Validate all tracks */
for (int i=0 ; i<64 ; ++i)
{
if (trackIdx[i])
{
const TrackRegion* region = nullptr;
const TrackRegion* nextRegion = reinterpret_cast<const TrackRegion*>(ptr + (isBig ? SBig(trackIdx[i]) : trackIdx[i]));
/* Iterate all regions */
while (nextRegion->indexValid(isBig))
{
region = nextRegion;
uint32_t regionIdx = (isBig ? SBig(region->m_regionIndex) :
region->m_regionIndex);
nextRegion = &region[1];
const unsigned char* data = ptr + (isBig ? SBig(regionIdxTable[regionIdx]) :
regionIdxTable[regionIdx]);
/* Can't reliably validate final region */
if (regionIdx == maxRegionIdx)
continue;
/* Expected end pointer (next region) */
const unsigned char* expectedEnd = ptr + (isBig ? SBig(regionIdxTable[regionIdx+1]) :
regionIdxTable[regionIdx+1]);
Track::Header header = *reinterpret_cast<const Track::Header*>(data);
if (isBig)
header.swapBig();
data += 12;
/* continuous pitch data */
if (header.m_pitchOff)
{
const unsigned char* dptr = ptr + header.m_pitchOff;
while (DecodeRLE(dptr) != 0xffffffff) {DecodeContinuousRLE(dptr);}
if (dptr >= (expectedEnd - 4) && (dptr <= expectedEnd))
continue;
}
/* continuous modulation data */
if (header.m_modOff)
{
const unsigned char* dptr = ptr + header.m_modOff;
while (DecodeRLE(dptr) != 0xffffffff) {DecodeContinuousRLE(dptr);}
if (dptr >= (expectedEnd - 4) && (dptr <= expectedEnd))
continue;
}
/* Loop through as many commands as we can for this time period */
if (v == 1)
{
/* Revised */
while (true)
{
/* Delta time */
DecodeTimeRLE(data);
/* Load next command */
if (*reinterpret_cast<const uint16_t*>(data) == 0xffff)
{
/* End of channel */
data += 2;
break;
}
else if (data[0] & 0x80 && data[1] & 0x80)
{
/* Control change */
data += 2;
}
else if (data[0] & 0x80)
{
/* Program change */
data += 2;
}
else
{
/* Note */
data += 4;
}
}
}
else
{
/* Legacy */
while (true)
{
/* Delta-time */
data += 4;
/* Load next command */
if (*reinterpret_cast<const uint16_t*>(&data[2]) == 0xffff)
{
/* End of channel */
data += 4;
break;
}
else
{
if ((data[2] & 0x80) != 0x80)
{
/* Note */
}
else if (data[2] & 0x80 && data[3] & 0x80)
{
/* Control change */
}
else if (data[2] & 0x80)
{
/* Program change */
}
data += 4;
}
}
}
if (data < (expectedEnd - 4) || (data > expectedEnd))
{
bad = true;
break;
}
}
if (bad)
break;
}
}
if (bad)
continue;
break;
}
return v;
}
bool SongState::initialize(const unsigned char* ptr)
{
m_sngVersion = DetectVersion(ptr, m_bigEndian);
if (m_sngVersion < 0)
return false;
m_songData = ptr; m_songData = ptr;
m_header = *reinterpret_cast<const Header*>(ptr); m_header = *reinterpret_cast<const Header*>(ptr);
if (m_bigEndian) if (m_bigEndian)
@@ -180,6 +357,8 @@ void SongState::initialize(const unsigned char* ptr)
m_tempo = m_header.m_initialTempo; m_tempo = m_header.m_initialTempo;
m_curTick = 0; m_curTick = 0;
m_songState = SongPlayState::Playing; m_songState = SongPlayState::Playing;
return true;
} }
bool SongState::Track::advance(Sequencer& seq, int32_t ticks) bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
@@ -281,9 +460,9 @@ bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
} }
/* Loop through as many commands as we can for this time period */ /* Loop through as many commands as we can for this time period */
if (m_parent.m_header.m_trackIdxOff == 0x18 || m_parent.m_header.m_trackIdxOff == 0x58) if (m_parent.m_sngVersion == 1)
{ {
/* GameCube */ /* Revision */
while (true) while (true)
{ {
/* Advance wait timer if active, returning if waiting */ /* Advance wait timer if active, returning if waiting */
@@ -335,7 +514,7 @@ bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
} }
else else
{ {
/* N64 */ /* Legacy */
while (true) while (true)
{ {
/* Advance wait timer if active, returning if waiting */ /* Advance wait timer if active, returning if waiting */