mirror of https://github.com/AxioDL/amuse.git
Plenty of SongConverter bug fixes
This commit is contained in:
parent
bd10015024
commit
a0241574ba
|
@ -7,6 +7,30 @@
|
||||||
|
|
||||||
static logvisor::Module Log(_S("amuseconv"));
|
static logvisor::Module Log(_S("amuseconv"));
|
||||||
|
|
||||||
|
enum ConvType
|
||||||
|
{
|
||||||
|
ConvN64,
|
||||||
|
ConvGCN,
|
||||||
|
ConvPC
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ReportConvType(ConvType tp)
|
||||||
|
{
|
||||||
|
switch (tp)
|
||||||
|
{
|
||||||
|
case ConvN64:
|
||||||
|
Log.report(logvisor::Info, _S("using N64 format"));
|
||||||
|
break;
|
||||||
|
case ConvPC:
|
||||||
|
Log.report(logvisor::Info, _S("using PC format"));
|
||||||
|
break;
|
||||||
|
case ConvGCN:
|
||||||
|
default:
|
||||||
|
Log.report(logvisor::Info, _S("using GameCube format"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool BuildAudioGroup(const amuse::SystemString& groupBase, const amuse::SystemString& targetPath)
|
static bool BuildAudioGroup(const amuse::SystemString& groupBase, const amuse::SystemString& targetPath)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -38,6 +62,7 @@ static bool ExtractAudioGroup(const amuse::SystemString& inPath, const amuse::Sy
|
||||||
{
|
{
|
||||||
if (!madeDir)
|
if (!madeDir)
|
||||||
{
|
{
|
||||||
|
amuse::Mkdir(targetPath.c_str(), 0755);
|
||||||
amuse::Mkdir(songsDir.c_str(), 0755);
|
amuse::Mkdir(songsDir.c_str(), 0755);
|
||||||
madeDir = true;
|
madeDir = true;
|
||||||
}
|
}
|
||||||
|
@ -57,45 +82,57 @@ static bool ExtractAudioGroup(const amuse::SystemString& inPath, const amuse::Sy
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool BuildN64SNG(const amuse::SystemString& inPath, const amuse::SystemString& targetPath, bool bigEndian)
|
static bool BuildSNG(const amuse::SystemString& inPath, const amuse::SystemString& targetPath, amuse::SongConverter::Target target)
|
||||||
{
|
{
|
||||||
return true;
|
FILE* fp = amuse::FOpen(inPath.c_str(), _S("rb"));
|
||||||
}
|
if (!fp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
long sz = ftell(fp);
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
std::vector<uint8_t> data(sz, 0);
|
||||||
|
fread(&data[0], 1, sz, fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
std::vector<uint8_t> out = amuse::SongConverter::MIDIToSong(data, target);
|
||||||
|
if (out.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
fp = amuse::FOpen(targetPath.c_str(), _S("wb"));
|
||||||
|
fwrite(out.data(), 1, out.size(), fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
static bool BuildGCNSNG(const amuse::SystemString& inPath, const amuse::SystemString& targetPath)
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ExtractSNG(const amuse::SystemString& inPath, const amuse::SystemString& targetPath)
|
static bool ExtractSNG(const amuse::SystemString& inPath, const amuse::SystemString& targetPath)
|
||||||
{
|
{
|
||||||
|
FILE* fp = amuse::FOpen(inPath.c_str(), _S("rb"));
|
||||||
|
if (!fp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
long sz = ftell(fp);
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
std::vector<uint8_t> data(sz, 0);
|
||||||
|
fread(&data[0], 1, sz, fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
amuse::SongConverter::Target target;
|
||||||
|
std::vector<uint8_t> out = amuse::SongConverter::SongToMIDI(data.data(), target);
|
||||||
|
if (out.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ReportConvType(ConvType(target));
|
||||||
|
|
||||||
|
fp = amuse::FOpen(targetPath.c_str(), _S("wb"));
|
||||||
|
fwrite(out.data(), 1, out.size(), fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConvType
|
|
||||||
{
|
|
||||||
ConvN64,
|
|
||||||
ConvGCN,
|
|
||||||
ConvPC
|
|
||||||
};
|
|
||||||
|
|
||||||
static void ReportConvType(ConvType tp)
|
|
||||||
{
|
|
||||||
switch (tp)
|
|
||||||
{
|
|
||||||
case ConvN64:
|
|
||||||
Log.report(logvisor::Info, _S("using N64 format"));
|
|
||||||
break;
|
|
||||||
case ConvPC:
|
|
||||||
Log.report(logvisor::Info, _S("using PC format"));
|
|
||||||
break;
|
|
||||||
case ConvGCN:
|
|
||||||
default:
|
|
||||||
Log.report(logvisor::Info, _S("using GameCube format"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
int wmain(int argc, const amuse::SystemChar** argv)
|
int wmain(int argc, const amuse::SystemChar** argv)
|
||||||
#else
|
#else
|
||||||
|
@ -140,19 +177,7 @@ int main(int argc, const amuse::SystemChar** argv)
|
||||||
!amuse::CompareCaseInsensitive(dot, _S(".midi")))
|
!amuse::CompareCaseInsensitive(dot, _S(".midi")))
|
||||||
{
|
{
|
||||||
ReportConvType(type);
|
ReportConvType(type);
|
||||||
switch (type)
|
good = BuildSNG(barePath, argv[2], amuse::SongConverter::Target(type));
|
||||||
{
|
|
||||||
case ConvN64:
|
|
||||||
good = BuildN64SNG(amuse::SystemString(barePath.begin(), barePath.begin() + dotPos), argv[2], true);
|
|
||||||
break;
|
|
||||||
case ConvPC:
|
|
||||||
good = BuildN64SNG(amuse::SystemString(barePath.begin(), barePath.begin() + dotPos), argv[2], false);
|
|
||||||
break;
|
|
||||||
case ConvGCN:
|
|
||||||
default:
|
|
||||||
good = BuildGCNSNG(amuse::SystemString(barePath.begin(), barePath.begin() + dotPos), argv[2]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (!amuse::CompareCaseInsensitive(dot, _S(".son")) ||
|
else if (!amuse::CompareCaseInsensitive(dot, _S(".son")) ||
|
||||||
!amuse::CompareCaseInsensitive(dot, _S(".sng")))
|
!amuse::CompareCaseInsensitive(dot, _S(".sng")))
|
||||||
|
|
|
@ -40,7 +40,8 @@ class SongState
|
||||||
struct TrackRegion
|
struct TrackRegion
|
||||||
{
|
{
|
||||||
uint32_t m_startTick;
|
uint32_t m_startTick;
|
||||||
uint16_t m_unk1;
|
uint8_t m_progNum;
|
||||||
|
uint8_t m_unk1;
|
||||||
uint16_t m_unk2;
|
uint16_t m_unk2;
|
||||||
int16_t m_regionIndex;
|
int16_t m_regionIndex;
|
||||||
int16_t m_unk3;
|
int16_t m_unk3;
|
||||||
|
|
|
@ -32,6 +32,12 @@ enum class Status
|
||||||
Reset = 0xFF,
|
Reset = 0xFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Event tags */
|
||||||
|
struct NoteEvent {};
|
||||||
|
struct CtrlEvent {};
|
||||||
|
struct ProgEvent {};
|
||||||
|
struct PitchEvent {};
|
||||||
|
|
||||||
/* Intermediate event */
|
/* Intermediate event */
|
||||||
struct Event
|
struct Event
|
||||||
{
|
{
|
||||||
|
@ -47,13 +53,16 @@ struct Event
|
||||||
bool isPitchBend = false;
|
bool isPitchBend = false;
|
||||||
int pitchBend;
|
int pitchBend;
|
||||||
|
|
||||||
Event(int pBend) : isPitchBend(true), pitchBend(pBend) {}
|
Event(NoteEvent, uint8_t chan, uint8_t note, uint8_t vel, uint16_t len)
|
||||||
|
: controlChange(false), channel(chan), noteOrCtrl(note), velOrVal(vel), length(len) {}
|
||||||
|
|
||||||
Event(bool ctrlCh, 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(ctrlCh), channel(chan), noteOrCtrl(note), velOrVal(vel), length(len) {}
|
: controlChange(true), channel(chan), noteOrCtrl(note), velOrVal(vel), length(len) {}
|
||||||
|
|
||||||
Event(uint8_t chan, uint8_t prog)
|
Event(ProgEvent, uint8_t chan, uint8_t prog)
|
||||||
: progChange(true), channel(chan), program(prog) {}
|
: progChange(true), channel(chan), program(prog) {}
|
||||||
|
|
||||||
|
Event(PitchEvent, uint8_t chan, int pBend) : isPitchBend(true), channel(chan), pitchBend(pBend) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class MIDIDecoder
|
class MIDIDecoder
|
||||||
|
@ -125,6 +134,30 @@ public:
|
||||||
_addProgramChange(0);
|
_addProgramChange(0);
|
||||||
std::multimap<int, Event>& res = m_results.back().second;
|
std::multimap<int, Event>& res = m_results.back().second;
|
||||||
|
|
||||||
|
if (m_status == 0xff)
|
||||||
|
{
|
||||||
|
/* Meta events */
|
||||||
|
if (it == end)
|
||||||
|
return begin;
|
||||||
|
a = *it++;
|
||||||
|
|
||||||
|
uint32_t length;
|
||||||
|
_readContinuedValue(it, end, length);
|
||||||
|
|
||||||
|
switch (a)
|
||||||
|
{
|
||||||
|
case 0x51:
|
||||||
|
{
|
||||||
|
uint32_t tempo = 0;
|
||||||
|
memcpy(&reinterpret_cast<uint8_t*>(&tempo)[1], &*it, 3);
|
||||||
|
m_tempos.emplace(m_tick, 60000000 / SBig(tempo));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
it += length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
uint8_t chan = m_status & 0xf;
|
uint8_t chan = m_status & 0xf;
|
||||||
switch (Status(m_status & 0xf0))
|
switch (Status(m_status & 0xf0))
|
||||||
{
|
{
|
||||||
|
@ -161,7 +194,7 @@ public:
|
||||||
if (note != res.end())
|
if (note != res.end())
|
||||||
note->second.length = uint16_t(m_tick - note->first);
|
note->second.length = uint16_t(m_tick - note->first);
|
||||||
|
|
||||||
m_notes[notenum] = res.emplace(m_tick, Event{false, chan, notenum, vel, 0});
|
m_notes[notenum] = res.emplace(m_tick, Event{NoteEvent{}, chan, notenum, vel, 0});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::NotePressure:
|
case Status::NotePressure:
|
||||||
|
@ -182,7 +215,7 @@ public:
|
||||||
if (it == end)
|
if (it == end)
|
||||||
return begin;
|
return begin;
|
||||||
b = *it++;
|
b = *it++;
|
||||||
res.emplace(m_tick, Event{true, chan, clamp7(a), clamp7(b), 0});
|
res.emplace(m_tick, Event{CtrlEvent{}, chan, clamp7(a), clamp7(b), 0});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::ProgramChange:
|
case Status::ProgramChange:
|
||||||
|
@ -190,7 +223,7 @@ public:
|
||||||
if (it == end)
|
if (it == end)
|
||||||
return begin;
|
return begin;
|
||||||
a = *it++;
|
a = *it++;
|
||||||
res.emplace(m_tick, Event{chan, a});
|
res.emplace(m_tick, Event{ProgEvent{}, chan, a});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::ChannelPressure:
|
case Status::ChannelPressure:
|
||||||
|
@ -208,7 +241,7 @@ public:
|
||||||
if (it == end)
|
if (it == end)
|
||||||
return begin;
|
return begin;
|
||||||
b = *it++;
|
b = *it++;
|
||||||
res.emplace(m_tick, Event{clamp7(b) * 128 + clamp7(a)});
|
res.emplace(m_tick, Event{PitchEvent{}, chan, clamp7(b) * 128 + clamp7(a)});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::SysEx:
|
case Status::SysEx:
|
||||||
|
@ -258,30 +291,9 @@ public:
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::Reset:
|
|
||||||
{
|
|
||||||
/* Meta events */
|
|
||||||
if (it == end)
|
|
||||||
return begin;
|
|
||||||
a = *it++;
|
|
||||||
|
|
||||||
uint32_t length;
|
|
||||||
_readContinuedValue(it, end, length);
|
|
||||||
|
|
||||||
switch (a)
|
|
||||||
{
|
|
||||||
case 0x51:
|
|
||||||
{
|
|
||||||
uint32_t tempo = 0;
|
|
||||||
memcpy(&reinterpret_cast<uint8_t*>(&tempo)[1], &*it, 3);
|
|
||||||
m_tempos.emplace(m_tick, 60000000 / SBig(tempo));
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
it += length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
@ -696,15 +708,19 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
|
||||||
if (trk)
|
if (trk)
|
||||||
{
|
{
|
||||||
MIDIEncoder encoder;
|
MIDIEncoder encoder;
|
||||||
|
std::multimap<int, Event> allEvents;
|
||||||
|
|
||||||
/* Iterate all regions */
|
/* Iterate all regions */
|
||||||
int lastTime = 0;
|
|
||||||
while (trk->m_nextRegion->indexValid())
|
while (trk->m_nextRegion->indexValid())
|
||||||
{
|
{
|
||||||
std::multimap<int, Event> events;
|
std::multimap<int, Event> events;
|
||||||
trk->advanceRegion(nullptr);
|
trk->advanceRegion(nullptr);
|
||||||
uint32_t regStart = song.m_bigEndian ? SBig(trk->m_curRegion->m_startTick) : trk->m_curRegion->m_startTick;
|
uint32_t regStart = song.m_bigEndian ? SBig(trk->m_curRegion->m_startTick) : trk->m_curRegion->m_startTick;
|
||||||
|
|
||||||
|
/* Initial program change */
|
||||||
|
if (trk->m_curRegion->m_progNum != 0xff)
|
||||||
|
events.emplace(regStart, Event{ProgEvent{}, trk->m_midiChan, trk->m_curRegion->m_progNum});
|
||||||
|
|
||||||
/* Update continuous pitch data */
|
/* Update continuous pitch data */
|
||||||
if (trk->m_pitchWheelData)
|
if (trk->m_pitchWheelData)
|
||||||
{
|
{
|
||||||
|
@ -720,7 +736,7 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
|
||||||
trk->m_lastPitchVal += pitchDelta;
|
trk->m_lastPitchVal += pitchDelta;
|
||||||
trk->m_pitchWheelData = ptr;
|
trk->m_pitchWheelData = ptr;
|
||||||
trk->m_lastPitchTick = nextTick;
|
trk->m_lastPitchTick = nextTick;
|
||||||
events.emplace(regStart + nextTick, Event{clamp(0, trk->m_lastPitchVal / 2 + 0x2000, 0x4000)});
|
events.emplace(regStart + nextTick, Event{PitchEvent{}, trk->m_midiChan, clamp(0, trk->m_lastPitchVal / 2 + 0x2000, 0x4000)});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
@ -742,7 +758,7 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
|
||||||
trk->m_lastModVal += modDelta;
|
trk->m_lastModVal += modDelta;
|
||||||
trk->m_modWheelData = ptr;
|
trk->m_modWheelData = ptr;
|
||||||
trk->m_lastModTick = nextTick;
|
trk->m_lastModTick = nextTick;
|
||||||
events.emplace(regStart + nextTick, Event{true, trk->m_midiChan, 1, uint8_t(clamp(0, trk->m_lastModVal * 128 / 16384, 127)), 0});
|
events.emplace(regStart + nextTick, Event{CtrlEvent{}, trk->m_midiChan, 1, uint8_t(clamp(0, trk->m_lastModVal * 128 / 16384, 127)), 0});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
@ -767,14 +783,14 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
|
||||||
/* Control change */
|
/* Control change */
|
||||||
uint8_t val = trk->m_data[0] & 0x7f;
|
uint8_t val = trk->m_data[0] & 0x7f;
|
||||||
uint8_t ctrl = trk->m_data[1] & 0x7f;
|
uint8_t ctrl = trk->m_data[1] & 0x7f;
|
||||||
events.emplace(regStart + trk->m_eventWaitCountdown, Event{true, trk->m_midiChan, ctrl, val, 0});
|
events.emplace(regStart + trk->m_eventWaitCountdown, Event{CtrlEvent{}, trk->m_midiChan, ctrl, val, 0});
|
||||||
trk->m_data += 2;
|
trk->m_data += 2;
|
||||||
}
|
}
|
||||||
else if (trk->m_data[0] & 0x80)
|
else if (trk->m_data[0] & 0x80)
|
||||||
{
|
{
|
||||||
/* Program change */
|
/* Program change */
|
||||||
uint8_t prog = trk->m_data[0] & 0x7f;
|
uint8_t prog = trk->m_data[0] & 0x7f;
|
||||||
events.emplace(regStart + trk->m_eventWaitCountdown, Event{trk->m_midiChan, prog});
|
events.emplace(regStart + trk->m_eventWaitCountdown, Event{ProgEvent{}, trk->m_midiChan, prog});
|
||||||
trk->m_data += 2;
|
trk->m_data += 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -784,8 +800,7 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
|
||||||
uint8_t vel = trk->m_data[1] & 0x7f;
|
uint8_t vel = trk->m_data[1] & 0x7f;
|
||||||
uint16_t length = (song.m_bigEndian ? SBig(*reinterpret_cast<const uint16_t*>(trk->m_data + 2)) :
|
uint16_t length = (song.m_bigEndian ? SBig(*reinterpret_cast<const uint16_t*>(trk->m_data + 2)) :
|
||||||
*reinterpret_cast<const uint16_t*>(trk->m_data + 2));
|
*reinterpret_cast<const uint16_t*>(trk->m_data + 2));
|
||||||
if (length)
|
events.emplace(regStart + trk->m_eventWaitCountdown, Event{NoteEvent{}, trk->m_midiChan, note, vel, length});
|
||||||
events.emplace(regStart + trk->m_eventWaitCountdown, Event{false, trk->m_midiChan, note, vel, length});
|
|
||||||
trk->m_data += 4;
|
trk->m_data += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -810,14 +825,14 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
|
||||||
/* Control change */
|
/* Control change */
|
||||||
uint8_t val = trk->m_data[0] & 0x7f;
|
uint8_t val = trk->m_data[0] & 0x7f;
|
||||||
uint8_t ctrl = trk->m_data[1] & 0x7f;
|
uint8_t ctrl = trk->m_data[1] & 0x7f;
|
||||||
events.emplace(regStart + trk->m_eventWaitCountdown, Event{true, trk->m_midiChan, ctrl, val, 0});
|
events.emplace(regStart + trk->m_eventWaitCountdown, Event{CtrlEvent{}, trk->m_midiChan, ctrl, val, 0});
|
||||||
trk->m_data += 2;
|
trk->m_data += 2;
|
||||||
}
|
}
|
||||||
else if (trk->m_data[0] & 0x80)
|
else if (trk->m_data[0] & 0x80)
|
||||||
{
|
{
|
||||||
/* Program change */
|
/* Program change */
|
||||||
uint8_t prog = trk->m_data[0] & 0x7f;
|
uint8_t prog = trk->m_data[0] & 0x7f;
|
||||||
events.emplace(regStart + trk->m_eventWaitCountdown, Event{trk->m_midiChan, prog});
|
events.emplace(regStart + trk->m_eventWaitCountdown, Event{ProgEvent{}, trk->m_midiChan, prog});
|
||||||
trk->m_data += 2;
|
trk->m_data += 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -829,8 +844,7 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
|
||||||
*reinterpret_cast<const uint16_t*>(trk->m_data));
|
*reinterpret_cast<const uint16_t*>(trk->m_data));
|
||||||
uint8_t note = trk->m_data[2] & 0x7f;
|
uint8_t note = trk->m_data[2] & 0x7f;
|
||||||
uint8_t vel = trk->m_data[3] & 0x7f;
|
uint8_t vel = trk->m_data[3] & 0x7f;
|
||||||
if (length)
|
events.emplace(regStart + trk->m_eventWaitCountdown, Event{NoteEvent{}, trk->m_midiChan, note, vel, length});
|
||||||
events.emplace(regStart + trk->m_eventWaitCountdown, Event{false, trk->m_midiChan, note, vel, length});
|
|
||||||
}
|
}
|
||||||
trk->m_data += 4;
|
trk->m_data += 4;
|
||||||
}
|
}
|
||||||
|
@ -844,25 +858,23 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Merge events */
|
||||||
|
allEvents.insert(events.begin(), events.end());
|
||||||
|
|
||||||
/* Resolve key-off events */
|
/* Resolve key-off events */
|
||||||
std::multimap<int, Event> offEvents;
|
|
||||||
for (auto& pair : events)
|
for (auto& pair : events)
|
||||||
{
|
{
|
||||||
if (!pair.second.controlChange)
|
if (!pair.second.controlChange)
|
||||||
{
|
{
|
||||||
auto it = offEvents.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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* Merge key-off events */
|
|
||||||
events.insert(offEvents.begin(), offEvents.end());
|
|
||||||
|
|
||||||
/* Emit MIDI events */
|
/* Emit MIDI events */
|
||||||
encoder._sendContinuedValue(0);
|
int lastTime = 0;
|
||||||
encoder.programChange(trk->m_midiChan, trk->m_curRegion->m_unk3);
|
for (auto& pair : allEvents)
|
||||||
|
|
||||||
for (auto& pair : events)
|
|
||||||
{
|
{
|
||||||
encoder._sendContinuedValue(pair.first - lastTime);
|
encoder._sendContinuedValue(pair.first - lastTime);
|
||||||
lastTime = pair.first;
|
lastTime = pair.first;
|
||||||
|
@ -887,7 +899,6 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, Target
|
||||||
encoder.noteOn(pair.second.channel, pair.second.noteOrCtrl, pair.second.velOrVal);
|
encoder.noteOn(pair.second.channel, pair.second.noteOrCtrl, pair.second.velOrVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
encoder.getResult().push_back(0);
|
encoder.getResult().push_back(0);
|
||||||
encoder.getResult().push_back(0xff);
|
encoder.getResult().push_back(0xff);
|
||||||
|
@ -1028,6 +1039,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
/* Extract channel events */
|
/* Extract channel events */
|
||||||
std::vector<uint8_t>::const_iterator begin = it;
|
std::vector<uint8_t>::const_iterator begin = it;
|
||||||
std::vector<uint8_t>::const_iterator end = it + length;
|
std::vector<uint8_t>::const_iterator end = it + length;
|
||||||
|
it = end;
|
||||||
|
|
||||||
MIDIDecoder dec;
|
MIDIDecoder dec;
|
||||||
while (begin != end)
|
while (begin != end)
|
||||||
|
@ -1051,12 +1063,14 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
|
|
||||||
for (auto& event : prog.second)
|
for (auto& event : prog.second)
|
||||||
{
|
{
|
||||||
|
uint32_t eventTick = event.first * 384 / header.div;
|
||||||
|
|
||||||
if (event.second.channel == c)
|
if (event.second.channel == c)
|
||||||
{
|
{
|
||||||
if (!didInit)
|
if (!didInit)
|
||||||
{
|
{
|
||||||
didInit = true;
|
didInit = true;
|
||||||
startTick = event.first;
|
startTick = eventTick;
|
||||||
lastTrackStartTick = startTick;
|
lastTrackStartTick = startTick;
|
||||||
lastEventTick = startTick;
|
lastEventTick = startTick;
|
||||||
lastPitchTick = startTick;
|
lastPitchTick = startTick;
|
||||||
|
@ -1069,8 +1083,8 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
{
|
{
|
||||||
if (event.second.noteOrCtrl == 1)
|
if (event.second.noteOrCtrl == 1)
|
||||||
{
|
{
|
||||||
EncodeRLE(region.modBuf, uint32_t(event.first - lastModTick));
|
EncodeRLE(region.modBuf, uint32_t(eventTick - lastModTick));
|
||||||
lastModTick = event.first;
|
lastModTick = eventTick;
|
||||||
int newMod = event.second.velOrVal * 16384 / 128;
|
int newMod = event.second.velOrVal * 16384 / 128;
|
||||||
EncodeContinuousRLE(region.modBuf, newMod - lastModVal);
|
EncodeContinuousRLE(region.modBuf, newMod - lastModVal);
|
||||||
lastModVal = newMod;
|
lastModVal = newMod;
|
||||||
|
@ -1079,13 +1093,14 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
{
|
{
|
||||||
if (target == Target::GCN)
|
if (target == Target::GCN)
|
||||||
{
|
{
|
||||||
EncodeTimeRLE(region.eventBuf, uint32_t(event.first - lastEventTick));
|
EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick));
|
||||||
|
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 (target == Target::N64)
|
||||||
{
|
{
|
||||||
uint32_t tickBig = SBig(uint32_t(event.first - startTick));
|
uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
|
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
|
||||||
region.eventBuf.push_back(0x80 | event.second.velOrVal);
|
region.eventBuf.push_back(0x80 | event.second.velOrVal);
|
||||||
|
@ -1093,7 +1108,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
}
|
}
|
||||||
else if (target == Target::PC)
|
else if (target == Target::PC)
|
||||||
{
|
{
|
||||||
uint32_t tick = uint32_t(event.first - startTick);
|
uint32_t tick = uint32_t(eventTick - startTick);
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]);
|
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.velOrVal);
|
||||||
|
@ -1105,13 +1120,14 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
{
|
{
|
||||||
if (target == Target::GCN)
|
if (target == Target::GCN)
|
||||||
{
|
{
|
||||||
EncodeTimeRLE(region.eventBuf, uint32_t(event.first - lastEventTick));
|
EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick));
|
||||||
|
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 (target == Target::N64)
|
||||||
{
|
{
|
||||||
uint32_t tickBig = SBig(uint32_t(event.first - startTick));
|
uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
|
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
|
||||||
region.eventBuf.push_back(0x80 | event.second.program);
|
region.eventBuf.push_back(0x80 | event.second.program);
|
||||||
|
@ -1119,17 +1135,17 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
}
|
}
|
||||||
else if (target == Target::PC)
|
else if (target == Target::PC)
|
||||||
{
|
{
|
||||||
uint32_t tick = uint32_t(event.first - startTick);
|
uint32_t tick = uint32_t(eventTick - startTick);
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]);
|
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]);
|
||||||
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 (event.second.pitchBend)
|
else if (event.second.isPitchBend)
|
||||||
{
|
{
|
||||||
EncodeRLE(region.pitchBuf, uint32_t(event.first - lastModTick));
|
EncodeRLE(region.pitchBuf, uint32_t(eventTick - lastPitchTick));
|
||||||
lastModTick = event.first;
|
lastPitchTick = eventTick;
|
||||||
int newPitch = (event.second.pitchBend - 0x2000) * 2;
|
int newPitch = (event.second.pitchBend - 0x2000) * 2;
|
||||||
EncodeContinuousRLE(region.pitchBuf, newPitch - lastPitchVal);
|
EncodeContinuousRLE(region.pitchBuf, newPitch - lastPitchVal);
|
||||||
lastPitchVal = newPitch;
|
lastPitchVal = newPitch;
|
||||||
|
@ -1138,7 +1154,8 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
{
|
{
|
||||||
if (target == Target::GCN)
|
if (target == Target::GCN)
|
||||||
{
|
{
|
||||||
EncodeTimeRLE(region.eventBuf, uint32_t(event.first - lastEventTick));
|
EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick));
|
||||||
|
lastEventTick = eventTick;
|
||||||
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);
|
||||||
uint16_t lenBig = SBig(uint16_t(event.second.length));
|
uint16_t lenBig = SBig(uint16_t(event.second.length));
|
||||||
|
@ -1147,7 +1164,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
}
|
}
|
||||||
else if (target == Target::N64)
|
else if (target == Target::N64)
|
||||||
{
|
{
|
||||||
uint32_t tickBig = SBig(uint32_t(event.first - startTick));
|
uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
|
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
|
||||||
uint16_t lenBig = SBig(uint16_t(event.second.length));
|
uint16_t lenBig = SBig(uint16_t(event.second.length));
|
||||||
|
@ -1158,7 +1175,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
}
|
}
|
||||||
else if (target == Target::PC)
|
else if (target == Target::PC)
|
||||||
{
|
{
|
||||||
uint32_t tick = uint32_t(event.first - startTick);
|
uint32_t tick = uint32_t(eventTick - startTick);
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]);
|
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]);
|
||||||
uint16_t len = uint16_t(event.second.length);
|
uint16_t len = uint16_t(event.second.length);
|
||||||
|
@ -1176,19 +1193,32 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
if (!didChanInit)
|
if (!didChanInit)
|
||||||
{
|
{
|
||||||
didChanInit = true;
|
didChanInit = true;
|
||||||
|
if (trackRegionIdxArr.size() == 64)
|
||||||
|
return {};
|
||||||
|
chanMap[trackRegionIdxArr.size()] = c;
|
||||||
trackRegionIdxArr.push_back(regionBuf.size());
|
trackRegionIdxArr.push_back(regionBuf.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Terminate region */
|
/* Terminate region */
|
||||||
if (target == Target::GCN)
|
if (target == Target::GCN)
|
||||||
{
|
{
|
||||||
EncodeTimeRLE(region.eventBuf, 0);
|
size_t pitchDelta = 0;
|
||||||
|
size_t modDelta = 0;
|
||||||
|
if (lastPitchTick > lastEventTick)
|
||||||
|
pitchDelta = lastPitchTick - lastEventTick;
|
||||||
|
if (lastModTick > lastEventTick)
|
||||||
|
modDelta = lastModTick - lastEventTick;
|
||||||
|
|
||||||
|
EncodeTimeRLE(region.eventBuf, std::max(pitchDelta, modDelta));
|
||||||
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 (target == Target::N64)
|
||||||
{
|
{
|
||||||
uint32_t tickBig = SBig(uint32_t(lastEventTick - startTick));
|
uint32_t selTick = std::max(std::max(lastEventTick - startTick,
|
||||||
|
lastPitchTick - startTick),
|
||||||
|
lastModTick - startTick);
|
||||||
|
uint32_t tickBig = SBig(uint32_t(selTick));
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
|
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
|
||||||
region.eventBuf.push_back(0);
|
region.eventBuf.push_back(0);
|
||||||
|
@ -1198,7 +1228,10 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
}
|
}
|
||||||
else if (target == Target::PC)
|
else if (target == Target::PC)
|
||||||
{
|
{
|
||||||
uint32_t tick = uint32_t(lastEventTick - startTick);
|
uint32_t selTick = std::max(std::max(lastEventTick - startTick,
|
||||||
|
lastPitchTick - startTick),
|
||||||
|
lastModTick - startTick);
|
||||||
|
uint32_t tick = uint32_t(selTick);
|
||||||
for (int i=0 ; i<4 ; ++i)
|
for (int i=0 ; i<4 ; ++i)
|
||||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]);
|
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]);
|
||||||
region.eventBuf.push_back(0);
|
region.eventBuf.push_back(0);
|
||||||
|
@ -1208,10 +1241,16 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (region.pitchBuf.size())
|
if (region.pitchBuf.size())
|
||||||
EncodeRLE(region.pitchBuf, 0xffffffff);
|
{
|
||||||
|
region.pitchBuf.push_back(0x80);
|
||||||
|
region.pitchBuf.push_back(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (region.modBuf.size())
|
if (region.modBuf.size())
|
||||||
EncodeRLE(region.modBuf, 0xffffffff);
|
{
|
||||||
|
region.modBuf.push_back(0x80);
|
||||||
|
region.modBuf.push_back(0);
|
||||||
|
}
|
||||||
|
|
||||||
/* See if there's a matching region buffer already present */
|
/* See if there's a matching region buffer already present */
|
||||||
int regIdx = 0;
|
int regIdx = 0;
|
||||||
|
@ -1234,7 +1273,8 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
if (bigEndian)
|
if (bigEndian)
|
||||||
{
|
{
|
||||||
reg.m_startTick = SBig(uint32_t(startTick));
|
reg.m_startTick = SBig(uint32_t(startTick));
|
||||||
reg.m_unk1 = 0xffff;
|
reg.m_progNum = 0xff;
|
||||||
|
reg.m_unk1 = 0xff;
|
||||||
reg.m_unk2 = 0;
|
reg.m_unk2 = 0;
|
||||||
reg.m_regionIndex = SBig(uint16_t(regIdx));
|
reg.m_regionIndex = SBig(uint16_t(regIdx));
|
||||||
reg.m_unk3 = 0;
|
reg.m_unk3 = 0;
|
||||||
|
@ -1242,7 +1282,8 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reg.m_startTick = uint32_t(startTick);
|
reg.m_startTick = uint32_t(startTick);
|
||||||
reg.m_unk1 = 0xffff;
|
reg.m_progNum = 0xff;
|
||||||
|
reg.m_unk1 = 0xff;
|
||||||
reg.m_unk2 = 0;
|
reg.m_unk2 = 0;
|
||||||
reg.m_regionIndex = uint16_t(regIdx);
|
reg.m_regionIndex = uint16_t(regIdx);
|
||||||
reg.m_unk3 = 0;
|
reg.m_unk3 = 0;
|
||||||
|
@ -1258,7 +1299,8 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
if (bigEndian)
|
if (bigEndian)
|
||||||
{
|
{
|
||||||
reg.m_startTick = SBig(uint32_t(lastTrackStartTick));
|
reg.m_startTick = SBig(uint32_t(lastTrackStartTick));
|
||||||
reg.m_unk1 = 0xffff;
|
reg.m_progNum = 0xff;
|
||||||
|
reg.m_unk1 = 0xff;
|
||||||
reg.m_unk2 = 0;
|
reg.m_unk2 = 0;
|
||||||
reg.m_regionIndex = 0xffff;
|
reg.m_regionIndex = 0xffff;
|
||||||
reg.m_unk3 = 0;
|
reg.m_unk3 = 0;
|
||||||
|
@ -1266,7 +1308,8 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reg.m_startTick = uint32_t(lastTrackStartTick);
|
reg.m_startTick = uint32_t(lastTrackStartTick);
|
||||||
reg.m_unk1 = 0xffff;
|
reg.m_progNum = 0xff;
|
||||||
|
reg.m_unk1 = 0xff;
|
||||||
reg.m_unk2 = 0;
|
reg.m_unk2 = 0;
|
||||||
reg.m_regionIndex = 0xffff;
|
reg.m_regionIndex = 0xffff;
|
||||||
reg.m_unk3 = 0;
|
reg.m_unk3 = 0;
|
||||||
|
@ -1285,6 +1328,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
head.m_initialTempo = initTempo;
|
head.m_initialTempo = initTempo;
|
||||||
head.m_unkOff = 0;
|
head.m_unkOff = 0;
|
||||||
|
|
||||||
|
uint32_t regIdxOff = head.m_regionIdxOff;
|
||||||
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;
|
||||||
|
|
||||||
|
@ -1303,7 +1347,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
for (SongState::TrackRegion& reg : regionBuf)
|
for (SongState::TrackRegion& reg : regionBuf)
|
||||||
*reinterpret_cast<SongState::TrackRegion*>(&*ret.insert(ret.cend(), 12, 0)) = reg;
|
*reinterpret_cast<SongState::TrackRegion*>(&*ret.insert(ret.cend(), 12, 0)) = reg;
|
||||||
|
|
||||||
uint32_t regBase = head.m_regionIdxOff + 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)) = SBig(uint32_t(regBase + regOff));
|
||||||
|
|
||||||
|
@ -1353,6 +1397,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
head.m_initialTempo = initTempo;
|
head.m_initialTempo = initTempo;
|
||||||
head.m_unkOff = 0;
|
head.m_unkOff = 0;
|
||||||
|
|
||||||
|
uint32_t chanMapOff = head.m_chanMapOff;
|
||||||
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;
|
||||||
|
|
||||||
|
@ -1373,7 +1418,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||||
|
|
||||||
memmove(&*ret.insert(ret.cend(), 64, 0), chanMap.data(), 64);
|
memmove(&*ret.insert(ret.cend(), 64, 0), chanMap.data(), 64);
|
||||||
|
|
||||||
uint32_t regBase = head.m_chanMapOff + 64;
|
uint32_t regBase = chanMapOff + 64;
|
||||||
uint32_t curOffset = regBase;
|
uint32_t curOffset = regBase;
|
||||||
for (Region& reg : regions)
|
for (Region& reg : regions)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue