diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/mid2agb/Makefile | 7 | ||||
-rw-r--r-- | tools/mid2agb/agb.cpp | 97 | ||||
-rw-r--r-- | tools/mid2agb/main.cpp | 117 | ||||
-rw-r--r-- | tools/mid2agb/midi.cpp | 26 |
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++; |