summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/mid2agb/Makefile7
-rw-r--r--tools/mid2agb/agb.cpp97
-rw-r--r--tools/mid2agb/main.cpp117
-rw-r--r--tools/mid2agb/midi.cpp26
4 files changed, 180 insertions, 67 deletions
diff --git a/tools/mid2agb/Makefile b/tools/mid2agb/Makefile
index 7fd5d5fc8..4dc2f123f 100644
--- a/tools/mid2agb/Makefile
+++ b/tools/mid2agb/Makefile
@@ -1,12 +1,15 @@
CXX := g++
-CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
+CXXFLAGS := -std=c++11 -O2 -s -Wall -Wno-switch -Werror
SRCS := agb.cpp error.cpp main.cpp midi.cpp tables.cpp
HEADERS := agb.h error.h main.h midi.h tables.h
-.PHONY: clean
+.PHONY: all clean
+
+all: mid2agb
+ @:
mid2agb: $(SRCS) $(HEADERS)
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
diff --git a/tools/mid2agb/agb.cpp b/tools/mid2agb/agb.cpp
index 9ff1efa86..d4d79f133 100644
--- a/tools/mid2agb/agb.cpp
+++ b/tools/mid2agb/agb.cpp
@@ -38,6 +38,9 @@ static bool s_noteChanged;
static bool s_velocityChanged;
static bool s_inPattern;
static int s_extendedCommand;
+static int s_memaccOp;
+static int s_memaccParam1;
+static int s_memaccParam2;
void PrintAgbHeader()
{
@@ -247,6 +250,84 @@ void PrintSeqLoopLabel(const Event& event)
ResetTrackVars();
}
+void PrintMemAcc(const Event& event)
+{
+ switch (s_memaccOp)
+ {
+ case 0x00:
+ PrintByte("MEMACC, mem_set, 0x%02X, %u", s_memaccParam1, event.param2);
+ break;
+ case 0x01:
+ PrintByte("MEMACC, mem_add, 0x%02X, %u", s_memaccParam1, event.param2);
+ break;
+ case 0x02:
+ PrintByte("MEMACC, mem_sub, 0x%02X, %u", s_memaccParam1, event.param2);
+ break;
+ case 0x03:
+ PrintByte("MEMACC, mem_mem_set, 0x%02X, 0x%02X", s_memaccParam1, event.param2);
+ break;
+ case 0x04:
+ PrintByte("MEMACC, mem_mem_add, 0x%02X, 0x%02X", s_memaccParam1, event.param2);
+ break;
+ case 0x05:
+ PrintByte("MEMACC, mem_mem_sub, 0x%02X, 0x%02X", s_memaccParam1, event.param2);
+ break;
+ // TODO: everything else
+ case 0x06:
+ break;
+ case 0x07:
+ break;
+ case 0x08:
+ break;
+ case 0x09:
+ break;
+ case 0x0A:
+ break;
+ case 0x0B:
+ break;
+ case 0x0C:
+ break;
+ case 0x0D:
+ break;
+ case 0x0E:
+ break;
+ case 0x0F:
+ break;
+ case 0x10:
+ break;
+ case 0x11:
+ break;
+ case 0x46:
+ break;
+ case 0x47:
+ break;
+ case 0x48:
+ break;
+ case 0x49:
+ break;
+ case 0x4A:
+ break;
+ case 0x4B:
+ break;
+ case 0x4C:
+ break;
+ case 0x4D:
+ break;
+ case 0x4E:
+ break;
+ case 0x4F:
+ break;
+ case 0x50:
+ break;
+ case 0x51:
+ break;
+ default:
+ break;
+ }
+
+ PrintWait(event.time);
+}
+
void PrintExtendedOp(const Event& event)
{
// TODO: support for other extended commands
@@ -280,16 +361,19 @@ void PrintControllerOp(const Event& event)
break;
case 0x0C:
case 0x10:
- // TODO: memacc
+ PrintMemAcc(event);
break;
case 0x0D:
- // TODO: memacc var
+ s_memaccOp = event.param2;
+ PrintWait(event.time);
break;
case 0x0E:
- // TODO: memacc var
+ s_memaccParam1 = event.param2;
+ PrintWait(event.time);
break;
case 0x0F:
- // TODO: memacc var
+ s_memaccParam2 = event.param2;
+ PrintWait(event.time);
break;
case 0x11:
std::fprintf(g_outputFile, "%s_%u_L%u:\n", g_asmLabel.c_str(), g_agbTrack, event.param2);
@@ -334,8 +418,6 @@ void PrintAgbTrack(std::vector<Event>& events)
{
std::fprintf(g_outputFile, "\n@**************** Track %u (Midi-Chn.%u) ****************@\n\n", g_agbTrack, g_midiChan + 1);
std::fprintf(g_outputFile, "%s_%u:\n", g_asmLabel.c_str(), g_agbTrack);
- PrintWait(g_initialWait);
- PrintByte("KEYSH , %s_key%+d", g_asmLabel.c_str(), 0);
int wholeNoteCount = 0;
int loopEndBlockNum = 0;
@@ -359,6 +441,9 @@ void PrintAgbTrack(std::vector<Event>& events)
if (!foundVolBeforeNote)
PrintByte("\tVOL , 127*%s_mvl/mxv", g_asmLabel.c_str());
+ PrintWait(g_initialWait);
+ PrintByte("KEYSH , %s_key%+d", g_asmLabel.c_str(), 0);
+
for (unsigned i = 0; events[i].type != EventType::EndOfTrack; i++)
{
const Event& event = events[i];
diff --git a/tools/mid2agb/main.cpp b/tools/mid2agb/main.cpp
index 9b883fba5..ea2b294ac 100644
--- a/tools/mid2agb/main.cpp
+++ b/tools/mid2agb/main.cpp
@@ -50,7 +50,8 @@ bool g_compressionEnabled = true;
" input_file filename(.mid) of MIDI file\n"
" output_file filename(.s) for AGB file (default:input_file)\n"
"\n"
- "options -V??? master volume (default:127)\n"
+ "options -L??? label for assembler (default:output_file)\n"
+ " -V??? master volume (default:127)\n"
" -G??? voice group number (default:0)\n"
" -P??? priority (default:0)\n"
" -R??? reverb (default:off)\n"
@@ -85,57 +86,45 @@ static std::string GetExtension(std::string s)
return "";
}
-struct Option
+static std::string BaseName(std::string s)
{
- char letter = 0;
- const char *arg = NULL;
-};
+ std::size_t posAfterSlash = s.find_last_of("/\\");
-static Option ParseOption(int& index, const int argc, char** argv)
-{
- static std::set<char> optionsWithArg = { 'L', 'V', 'G', 'P', 'R' };
- static std::set<char> optionsWithoutArg = { 'X', 'E', 'N' };
-
- assert(index >= 0 && index < argc);
-
- const char *opt = argv[index];
+ if (posAfterSlash == std::string::npos)
+ posAfterSlash = 0;
+ else
+ posAfterSlash++;
- assert(opt[0] == '-');
- assert(std::strlen(opt) == 2);
+ std::size_t dotPos = s.find_first_of('.', posAfterSlash);
+ if (dotPos > posAfterSlash && dotPos != std::string::npos)
+ s = s.substr(posAfterSlash, dotPos - posAfterSlash);
- char letter = std::toupper(opt[1]);
-
- bool isOption = false;
- bool hasArg = false;
+ return s;
+}
- if (optionsWithArg.count(letter) != 0)
- {
- isOption = true;
- hasArg = true;
- }
- else if (optionsWithoutArg.count(letter) != 0)
- {
- isOption = true;
- }
+static const char *GetArgument(int argc, char **argv, int& index)
+{
+ assert(index >= 0 && index < argc);
- if (!isOption)
- PrintUsage();
+ const char *option = argv[index];
- Option retVal;
+ assert(option != nullptr);
+ assert(option[0] == '-');
- retVal.letter = letter;
+ // If there is text following the letter, return that.
+ if (std::strlen(option) >= 3)
+ return option + 2;
- if (hasArg)
+ // Otherwise, try to get the next arg.
+ if (index + 1 < argc)
{
index++;
-
- if (index >= argc)
- RaiseError("missing argument for \"-%c\"", letter);
-
- retVal.arg = argv[index];
+ return argv[index];
+ }
+ else
+ {
+ return nullptr;
}
-
- return retVal;
}
int main(int argc, char** argv)
@@ -145,51 +134,65 @@ int main(int argc, char** argv)
for (int i = 1; i < argc; i++)
{
- if (argv[i][0] == '-' && std::strlen(argv[i]) == 2)
+ const char *option = argv[i];
+
+ if (option[0] == '-' && option[1] != '\0')
{
- Option option = ParseOption(i, argc, argv);
+ const char *arg;
- switch (option.letter)
+ switch (std::toupper(option[1]))
{
case 'E':
g_exactGateTime = true;
break;
case 'G':
- g_voiceGroup = std::stoi(option.arg);
+ arg = GetArgument(argc, argv, i);
+ if (arg == nullptr)
+ PrintUsage();
+ g_voiceGroup = std::stoi(arg);
break;
case 'L':
- g_asmLabel = option.arg;
+ arg = GetArgument(argc, argv, i);
+ if (arg == nullptr)
+ PrintUsage();
+ g_asmLabel = arg;
break;
case 'N':
g_compressionEnabled = false;
break;
case 'P':
- g_priority = std::stoi(option.arg);
+ arg = GetArgument(argc, argv, i);
+ if (arg == nullptr)
+ PrintUsage();
+ g_priority = std::stoi(arg);
break;
case 'R':
- g_reverb = std::stoi(option.arg);
+ arg = GetArgument(argc, argv, i);
+ if (arg == nullptr)
+ PrintUsage();
+ g_reverb = std::stoi(arg);
break;
case 'V':
- g_masterVolume = std::stoi(option.arg);
+ arg = GetArgument(argc, argv, i);
+ if (arg == nullptr)
+ PrintUsage();
+ g_masterVolume = std::stoi(arg);
break;
case 'X':
g_clocksPerBeat = 2;
break;
+ default:
+ PrintUsage();
}
}
else
{
- switch (i)
- {
- case 1:
+ if (inputFilename.empty())
inputFilename = argv[i];
- break;
- case 2:
+ else if (outputFilename.empty())
outputFilename = argv[i];
- break;
- default:
+ else
PrintUsage();
- }
}
}
@@ -206,7 +209,7 @@ int main(int argc, char** argv)
RaiseError("output filename extension is not \"s\"");
if (g_asmLabel.empty())
- g_asmLabel = StripExtension(outputFilename);
+ g_asmLabel = BaseName(outputFilename);
g_inputFile = std::fopen(inputFilename.c_str(), "rb");
diff --git a/tools/mid2agb/midi.cpp b/tools/mid2agb/midi.cpp
index ba5dd654a..fa7d9ce28 100644
--- a/tools/mid2agb/midi.cpp
+++ b/tools/mid2agb/midi.cpp
@@ -52,6 +52,7 @@ static std::int32_t s_absoluteTime;
static int s_blockCount = 0;
static int s_minNote;
static int s_maxNote;
+static int s_runningStatus;
void Seek(long offset)
{
@@ -170,6 +171,7 @@ void StartTrack()
{
Seek(s_trackDataStart);
s_absoluteTime = 0;
+ s_runningStatus = 0;
}
void SkipEventData()
@@ -181,15 +183,24 @@ void DetermineEventCategory(MidiEventCategory& category, int& typeChan, int& siz
{
typeChan = ReadInt8();
+ if (typeChan < 0x80)
+ {
+ // If data byte was found, use the running status.
+ ungetc(typeChan, g_inputFile);
+ typeChan = s_runningStatus;
+ }
+
if (typeChan == 0xFF)
{
category = MidiEventCategory::Meta;
size = 0;
+ s_runningStatus = 0;
}
else if (typeChan >= 0xF0)
{
category = MidiEventCategory::SysEx;
size = 0;
+ s_runningStatus = 0;
}
else if (typeChan >= 0x80)
{
@@ -205,6 +216,7 @@ void DetermineEventCategory(MidiEventCategory& category, int& typeChan, int& siz
size = 2;
break;
}
+ s_runningStatus = typeChan;
}
else
{
@@ -421,7 +433,10 @@ bool CheckNoteEnd(Event& event)
void FindNoteEnd(Event& event)
{
+ // Save the current file position and running status
+ // which get modified by CheckNoteEnd.
long startPos = ftell(g_inputFile);
+ int savedRunningStatus = s_runningStatus;
event.param2 = 0;
@@ -429,6 +444,7 @@ void FindNoteEnd(Event& event)
;
Seek(startPos);
+ s_runningStatus = savedRunningStatus;
}
bool ReadTrackEvent(Event& event)
@@ -764,7 +780,7 @@ int CalculateCompressionScore(std::vector<Event>& events, int index)
std::uint8_t lastVelocity = 0x80u;
EventType lastType = events[index].type;
std::int32_t lastDuration = 0x80000000;
- std::uint8_t lastNote = 0x80u;
+ std::uint8_t lastNote = 0x40u;
if (events[index].time > 0)
score++;
@@ -828,7 +844,7 @@ int CalculateCompressionScore(std::vector<Event>& events, int index)
lastType = events[i].type;
if (events[i].time)
- ++score;
+ score++;
}
return score;
@@ -836,6 +852,12 @@ int CalculateCompressionScore(std::vector<Event>& events, int index)
bool IsCompressionMatch(std::vector<Event>& events, int index1, int index2)
{
+ if (events[index1].type != events[index2].type ||
+ events[index1].note != events[index2].note ||
+ events[index1].param1 != events[index2].param1 ||
+ events[index1].time != events[index2].time)
+ return false;
+
index1++;
index2++;