mirror of
https://github.com/AxioDL/amuse.git
synced 2025-12-08 21:17:49 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe78a675d7 | ||
|
|
3427515960 | ||
|
|
5ad8c06b99 |
@@ -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")))
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -618,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));
|
||||||
@@ -629,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)
|
||||||
@@ -768,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 */
|
||||||
@@ -812,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 */
|
||||||
@@ -920,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
|
||||||
{
|
{
|
||||||
@@ -971,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
|
||||||
{
|
{
|
||||||
@@ -1022,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);
|
||||||
@@ -1091,14 +1084,16 @@ 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
|
||||||
|
{
|
||||||
|
if (big)
|
||||||
{
|
{
|
||||||
uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
|
uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
@@ -1106,7 +1101,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
|||||||
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::PC)
|
else
|
||||||
{
|
{
|
||||||
uint32_t tick = uint32_t(eventTick - startTick);
|
uint32_t tick = uint32_t(eventTick - startTick);
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
@@ -1116,16 +1111,19 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (event.second.isProgChange)
|
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
|
||||||
|
{
|
||||||
|
if (big)
|
||||||
{
|
{
|
||||||
uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
|
uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
@@ -1133,7 +1131,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
|||||||
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::PC)
|
else
|
||||||
{
|
{
|
||||||
uint32_t tick = uint32_t(eventTick - startTick);
|
uint32_t tick = uint32_t(eventTick - startTick);
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
@@ -1142,6 +1140,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
|||||||
region.eventBuf.push_back(0);
|
region.eventBuf.push_back(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (event.second.isPitchBend)
|
else if (event.second.isPitchBend)
|
||||||
{
|
{
|
||||||
EncodeRLE(region.pitchBuf, uint32_t(eventTick - lastPitchTick));
|
EncodeRLE(region.pitchBuf, uint32_t(eventTick - lastPitchTick));
|
||||||
@@ -1152,7 +1151,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
|||||||
}
|
}
|
||||||
else if (event.second.isNote)
|
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;
|
||||||
@@ -1162,7 +1161,9 @@ 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
|
||||||
|
{
|
||||||
|
if (big)
|
||||||
{
|
{
|
||||||
uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
|
uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
@@ -1173,7 +1174,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
|||||||
region.eventBuf.push_back(event.second.noteOrCtrl);
|
region.eventBuf.push_back(event.second.noteOrCtrl);
|
||||||
region.eventBuf.push_back(event.second.velOrVal);
|
region.eventBuf.push_back(event.second.velOrVal);
|
||||||
}
|
}
|
||||||
else if (target == Target::PC)
|
else
|
||||||
{
|
{
|
||||||
uint32_t tick = uint32_t(eventTick - startTick);
|
uint32_t tick = uint32_t(eventTick - startTick);
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
@@ -1187,6 +1188,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (didInit)
|
if (didInit)
|
||||||
{
|
{
|
||||||
@@ -1200,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;
|
||||||
@@ -1213,7 +1215,9 @@ 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
|
||||||
|
{
|
||||||
|
if (big)
|
||||||
{
|
{
|
||||||
uint32_t selTick = std::max(std::max(lastEventTick - startTick,
|
uint32_t selTick = std::max(std::max(lastEventTick - startTick,
|
||||||
lastPitchTick - startTick),
|
lastPitchTick - startTick),
|
||||||
@@ -1226,7 +1230,7 @@ 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::PC)
|
else
|
||||||
{
|
{
|
||||||
uint32_t selTick = std::max(std::max(lastEventTick - startTick,
|
uint32_t selTick = std::max(std::max(lastEventTick - startTick,
|
||||||
lastPitchTick - startTick),
|
lastPitchTick - startTick),
|
||||||
@@ -1239,6 +1243,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (region.pitchBuf.size())
|
if (region.pitchBuf.size())
|
||||||
{
|
{
|
||||||
@@ -1264,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;
|
||||||
@@ -1296,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;
|
||||||
@@ -1318,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;
|
||||||
@@ -1329,6 +1336,7 @@ 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;
|
||||||
|
if (big)
|
||||||
head.swapBig();
|
head.swapBig();
|
||||||
*reinterpret_cast<SongState::Header*>(&*ret.insert(ret.cend(), 0x18, 0)) = head;
|
*reinterpret_cast<SongState::Header*>(&*ret.insert(ret.cend(), 0x18, 0)) = head;
|
||||||
|
|
||||||
@@ -1341,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)
|
||||||
@@ -1349,156 +1358,24 @@ 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())
|
|
||||||
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) =
|
|
||||||
SBig(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)) =
|
|
||||||
SBig(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
memmove(&*ret.insert(ret.cend(), 64, 0), chanMap.data(), 64);
|
|
||||||
|
|
||||||
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::N64)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
uint32_t chanMapOff = head.m_chanMapOff;
|
|
||||||
head.swapBig();
|
|
||||||
*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)) = SBig(uint32_t(0x18 + 4 * 64 + idx * 12));
|
|
||||||
}
|
|
||||||
|
|
||||||
memmove(&*ret.insert(ret.cend(), 64, 0), chanMap.data(), 64);
|
|
||||||
|
|
||||||
uint32_t regBase = chanMapOff + 64;
|
|
||||||
uint32_t curOffset = regBase;
|
|
||||||
for (Region& reg : regions)
|
|
||||||
{
|
|
||||||
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = SBig(uint32_t(8));
|
|
||||||
|
|
||||||
if (reg.pitchBuf.size())
|
|
||||||
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) =
|
|
||||||
SBig(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)) =
|
|
||||||
SBig(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
if (reg.pitchBuf.size())
|
||||||
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) =
|
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) =
|
||||||
|
big ? SBig(uint32_t(curOffset + 12 + reg.eventBuf.size())) :
|
||||||
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)) =
|
||||||
|
big ? SBig(uint32_t(curOffset + 12 + reg.eventBuf.size() + reg.pitchBuf.size())) :
|
||||||
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);
|
||||||
@@ -1512,11 +1389,88 @@ 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());
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
uint32_t chanMapOff = head.m_chanMapOff;
|
||||||
|
if (big)
|
||||||
|
head.swapBig();
|
||||||
|
*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)) = 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);
|
||||||
|
|
||||||
|
uint32_t regBase = chanMapOff + 64;
|
||||||
|
uint32_t curOffset = regBase;
|
||||||
|
for (Region& reg : regions)
|
||||||
|
{
|
||||||
|
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) = big ? SBig(uint32_t(8)) : 8;
|
||||||
|
|
||||||
|
if (reg.pitchBuf.size())
|
||||||
|
*reinterpret_cast<uint32_t*>(&*ret.insert(ret.cend(), 4, 0)) =
|
||||||
|
big ? SBig(uint32_t(curOffset + 12 + reg.eventBuf.size())) :
|
||||||
|
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)) =
|
||||||
|
big ? SBig(uint32_t(curOffset + 12 + reg.eventBuf.size() + reg.pitchBuf.size())) :
|
||||||
|
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());
|
||||||
|
|
||||||
|
ret.insert(ret.cend(), reg.padding, 0);
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@@ -18,8 +18,11 @@ 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 = ®ion[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 = ®ion[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 */
|
||||||
|
|||||||
Reference in New Issue
Block a user