557 lines
20 KiB
C++
557 lines
20 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <algorithm>
|
|
#include <map>
|
|
|
|
#include "../../cpu.h"
|
|
#include "../../memory.h"
|
|
#include "../../expression.h"
|
|
#include "../../midi.h"
|
|
#include "../../gtmidi.h"
|
|
|
|
|
|
#define GTMIDI_MAJOR_VERSION "0.6"
|
|
#define GTMIDI_MINOR_VERSION "0"
|
|
#define GTMIDI_VERSION_STR "gtmidi v" GTMIDI_MAJOR_VERSION "." GTMIDI_MINOR_VERSION
|
|
|
|
#define MAX_ELEMENTS 16
|
|
|
|
|
|
enum Format {gtMID=0, vCPU, GBAS, GCL, CPP, PY, NumFormats};
|
|
|
|
int _elementCount = 0;
|
|
std::string _segmentName;
|
|
|
|
std::map<std::string, int> _formatName =
|
|
{
|
|
{"GTMID", gtMID},
|
|
{"VCPU", vCPU },
|
|
{"GBAS", GBAS },
|
|
{"GCL" , GCL },
|
|
{"CPP" , CPP },
|
|
{"PY" , PY },
|
|
};
|
|
|
|
uint8_t _midiBuffer[MIDI_MAX_BUFFER_SIZE];
|
|
std::vector<uint8_t> _outBuffer;
|
|
|
|
|
|
void padString(std::string &str, size_t num, char pad=' ')
|
|
{
|
|
if(num > str.size()) str.insert(0, num - str.size(), pad);
|
|
}
|
|
void addString(std::string &str, size_t num, char add=' ')
|
|
{
|
|
str.append(num, add);
|
|
}
|
|
|
|
|
|
// gtMID output
|
|
void outputGTMIDheader(std::ofstream& outfile, const std::string& name, bool hasVolume)
|
|
{
|
|
GtMidiHdr gtMidiHdr;
|
|
|
|
// Write GtMiDiHdr, upto HiSize
|
|
strncpy((char *)gtMidiHdr._name, name.c_str(), int(name.length()) % GTMIDI_NAME_SIZE);
|
|
gtMidiHdr._hasVolume = hasVolume;
|
|
outfile.write((char *)>MidiHdr, GTMIDI_HI_SIZE_OFFSET);
|
|
}
|
|
void outputGTMIDcommand(uint8_t command)
|
|
{
|
|
_outBuffer.push_back(command);
|
|
}
|
|
void outputGTMIDbuffer(std::ofstream& outfile)
|
|
{
|
|
// Write HiSize and LoSize, (which are part of GtMiDiHdr), can't be written until size is calculated
|
|
uint8_t loSize = uint8_t(_outBuffer.size() & 0xFF);
|
|
uint8_t hiSize = uint8_t((_outBuffer.size() >>8) & 0xFF);
|
|
outfile.write((char *)&hiSize, 1);
|
|
outfile.write((char *)&loSize, 1);
|
|
|
|
// Write MIDI stream
|
|
outfile.write((char *)&_outBuffer[0], _outBuffer.size());
|
|
}
|
|
|
|
// vCPU output
|
|
void outputVCPUheader(std::ofstream& outfile, const std::string& name, uint16_t address, uint16_t segmentSize, uint16_t segmentIndex)
|
|
{
|
|
_segmentName = name;
|
|
if(segmentSize)
|
|
{
|
|
std::stringstream ss;
|
|
ss << _segmentName << std::setfill('0') << std::setw(2) << std::to_string(segmentIndex);
|
|
_segmentName = ss.str();
|
|
}
|
|
addString(_segmentName, 16 - _segmentName.size());
|
|
outfile << _segmentName.c_str() << "EQU 0x" << std::hex << std::setw(4) << std::setfill('0') << address << std::endl;
|
|
outfile << _segmentName.c_str() << "DB ";
|
|
}
|
|
void outputVCPUnewLine(std::ofstream& outfile, bool newLine)
|
|
{
|
|
if(!newLine)
|
|
{
|
|
_elementCount = 0;
|
|
return;
|
|
}
|
|
|
|
if(++_elementCount < MAX_ELEMENTS) return;
|
|
_elementCount = 0;
|
|
|
|
std::string str;
|
|
addString(str, _segmentName.size());
|
|
outfile << std::endl << str << "DB ";
|
|
}
|
|
void outputVCPUcommand(std::ofstream& outfile, uint8_t command, bool newLine=true)
|
|
{
|
|
outfile << " 0x" << std::hex << std::setw(2) << std::setfill('0') << uint16_t(command);
|
|
outputVCPUnewLine(outfile, newLine);
|
|
}
|
|
|
|
// GBAS output
|
|
void outputGBASheader(std::ofstream& outfile, uint16_t address)
|
|
{
|
|
outfile << "def byte" << "(&h" << std::hex << std::setw(4) << std::setfill('0') << address << ") = ";
|
|
}
|
|
void outputGBASnewLine(std::ofstream& outfile, bool newLine)
|
|
{
|
|
if(!newLine)
|
|
{
|
|
_elementCount = 0;
|
|
return;
|
|
}
|
|
|
|
if(++_elementCount < MAX_ELEMENTS) return;
|
|
_elementCount = 0;
|
|
|
|
std::string str;
|
|
outfile << std::endl << str << "def byte = ";
|
|
}
|
|
void outputGBAScommand(std::ofstream& outfile, uint8_t command, bool newLine=true)
|
|
{
|
|
outfile << " &h" << std::hex << std::setw(2) << std::setfill('0') << uint16_t(command) << ",";
|
|
outputGBASnewLine(outfile, newLine);
|
|
}
|
|
|
|
// GCL output
|
|
void outputGCLheader(std::ofstream& outfile, uint16_t address)
|
|
{
|
|
outfile << "$" << std::hex << std::setw(4) << std::setfill('0') << address << ":" << std::endl;
|
|
outfile << "[def" << std::endl << " ";
|
|
}
|
|
void outputGCLnewLine(std::ofstream& outfile, bool newLine)
|
|
{
|
|
if(!newLine)
|
|
{
|
|
_elementCount = 0;
|
|
return;
|
|
}
|
|
|
|
if(++_elementCount < MAX_ELEMENTS) return;
|
|
_elementCount = 0;
|
|
|
|
outfile << std::endl << " ";
|
|
}
|
|
void outputGCLcommand(std::ofstream& outfile, uint8_t command, bool newLine=true)
|
|
{
|
|
outfile << " $" << std::hex << std::setw(2) << std::setfill('0') << uint16_t(command) << "#";
|
|
outputGCLnewLine(outfile, newLine);
|
|
}
|
|
void outputGCLfooter(std::ofstream& outfile, const std::string& name)
|
|
{
|
|
UNREFERENCED_PARAM(name);
|
|
outfile << std::endl << "]" << std::endl;
|
|
}
|
|
|
|
// CPP output
|
|
void outputCPPheader(std::ofstream& outfile, const std::string& name, uint16_t segmentSize, uint16_t segmentIndex)
|
|
{
|
|
_segmentName = name;
|
|
if(segmentSize)
|
|
{
|
|
std::stringstream ss;
|
|
ss << _segmentName << std::setfill('0') << std::setw(2) << std::to_string(segmentIndex);
|
|
_segmentName = ss.str() + "[] = ";
|
|
}
|
|
outfile << "uint8_t " << _segmentName.c_str() << std::endl;
|
|
outfile << "{" << std::endl;
|
|
outfile << " ";
|
|
}
|
|
void outputCPPnewLine(std::ofstream& outfile, bool newLine)
|
|
{
|
|
if(!newLine)
|
|
{
|
|
_elementCount = 0;
|
|
return;
|
|
}
|
|
|
|
if(++_elementCount < MAX_ELEMENTS) return;
|
|
_elementCount = 0;
|
|
|
|
outfile << std::endl << " ";
|
|
}
|
|
void outputCPPcommand(std::ofstream& outfile, uint8_t command, bool newLine=true)
|
|
{
|
|
outfile << "0x" << std::hex << std::setw(2) << std::setfill('0') << uint16_t(command) << ",";
|
|
outputCPPnewLine(outfile, newLine);
|
|
}
|
|
void outputCPPfooter(std::ofstream& outfile)
|
|
{
|
|
outfile << std::endl << "};" << std::endl;
|
|
}
|
|
|
|
// PY output
|
|
void outputPYheader(std::ofstream& outfile, const std::string& name, uint16_t segmentSize, uint16_t segmentIndex)
|
|
{
|
|
_segmentName = name;
|
|
if(segmentSize)
|
|
{
|
|
std::stringstream ss;
|
|
ss << _segmentName << std::setfill('0') << std::setw(2) << std::to_string(segmentIndex);
|
|
_segmentName = ss.str() + " = bytearray([";
|
|
}
|
|
outfile << _segmentName.c_str() << std::endl;
|
|
outfile << " ";
|
|
}
|
|
void outputPYnewLine(std::ofstream& outfile, bool newLine)
|
|
{
|
|
if(!newLine)
|
|
{
|
|
_elementCount = 0;
|
|
return;
|
|
}
|
|
|
|
if(++_elementCount < MAX_ELEMENTS) return;
|
|
_elementCount = 0;
|
|
|
|
outfile << std::endl << " ";
|
|
}
|
|
void outputPYcommand(std::ofstream& outfile, uint8_t command, bool newLine=true)
|
|
{
|
|
outfile << "0x" << std::hex << std::setw(2) << std::setfill('0') << uint16_t(command) << ",";
|
|
outputPYnewLine(outfile, newLine);
|
|
}
|
|
void outputPYfooter(std::ofstream& outfile)
|
|
{
|
|
outfile << std::endl << "])" << std::endl;
|
|
}
|
|
|
|
|
|
void outputDelay(std::ofstream& outfile, Format format, uint8_t delay8, double timingAdjust, double totalTime16, double& totalTime8)
|
|
{
|
|
// Adjust delay8 to try and keep overall timing as accurate as possible
|
|
if(timingAdjust)
|
|
{
|
|
if(totalTime16 > totalTime8 + double(delay8)*16.6666666667 + 16.6666666667*timingAdjust && delay8 < 0x7F) delay8++;
|
|
if(totalTime16 < totalTime8 + double(delay8)*16.6666666667 - 16.6666666667*timingAdjust && delay8 > 0x01) delay8--;
|
|
}
|
|
|
|
totalTime8 += double(delay8) * 16.6666666667;
|
|
|
|
switch(format)
|
|
{
|
|
case Format::gtMID: outputGTMIDcommand(delay8); break;
|
|
case Format::vCPU: outputVCPUcommand(outfile, delay8); break;
|
|
case Format::GBAS: outputGBAScommand(outfile, delay8); break;
|
|
case Format::GCL: outputGCLcommand(outfile, delay8); break;
|
|
case Format::CPP: outputCPPcommand(outfile, delay8); break;
|
|
case Format::PY: outputPYcommand(outfile, delay8); break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
if(argc < 9 || argc > 10)
|
|
{
|
|
fprintf(stderr, "Version: %s\n", GTMIDI_VERSION_STR);
|
|
fprintf(stderr, "Usage: gtmidi <input filename> <output filename> <midi name> <format name> <uint16_t start_address in hex>\n <uint16_t segment_offset in hex> <int segment_size> <float timing_adjust> <optional -v>\n\n");
|
|
fprintf(stderr, "Example1: gtmidi game_over.bin game_over.i gameOver vCPU 0x08A0 0x0100 96 0.5\n");
|
|
fprintf(stderr, "Example2: gtmidi game_over.bin game_over.i gameOver vCPU 0x8000 0 0 0.5 -v\n\n");
|
|
fprintf(stderr, "Input: miditones binary file produced with miditones, e.g. miditones -t4 -b -s1 -pi -v <filename>.bin\n");
|
|
fprintf(stderr, "Format: 'gtMID', 'vCPU', 'GBAS', 'GCL', 'CPP', 'Py'\n");
|
|
fprintf(stderr, "Optional: -v use volume/velocity values, (use -v with miditones)\n");
|
|
return 1;
|
|
}
|
|
|
|
std::string inFilename = std::string(argv[1]);
|
|
std::string outFilename = std::string(argv[2]);
|
|
std::string midiName = std::string(argv[3]);
|
|
|
|
std::string formatName = argv[4];
|
|
formatName = Expression::strToUpper(formatName);
|
|
if(_formatName.find(formatName) == _formatName.end())
|
|
{
|
|
fprintf(stderr, "Format must be one of : 'gtMID', 'vCPU', 'GBAS', 'GCL', 'CPP', 'Py'\n");
|
|
return 1;
|
|
}
|
|
|
|
Format format = (Format)_formatName[formatName];
|
|
|
|
// Handles hex numbers
|
|
uint16_t startAddress, segmentOffset;
|
|
std::stringstream ss0, ss1;
|
|
ss0 << std::hex << argv[5];
|
|
ss0 >> startAddress;
|
|
ss1 << std::hex << argv[6];
|
|
ss1 >> segmentOffset;
|
|
|
|
uint16_t segment = startAddress;
|
|
uint16_t segmentSize = uint16_t(strtol(argv[7], nullptr, 10));
|
|
double timingAdjust = strtod(argv[8], nullptr);
|
|
|
|
bool hasVolume = false;
|
|
if(argc == 10)
|
|
{
|
|
std::string volume = argv[9];
|
|
Expression::strToUpper(volume);
|
|
if(volume != "-V")
|
|
{
|
|
fprintf(stderr, "Optional volume specifier must be '-v'\n");
|
|
return 1;
|
|
}
|
|
|
|
hasVolume = true;
|
|
}
|
|
|
|
std::ifstream infile(inFilename, std::ios::binary | std::ios::in);
|
|
if(!infile.is_open())
|
|
{
|
|
fprintf(stderr, "Failed to open input file '%s'\n", inFilename.c_str());
|
|
return 1;
|
|
}
|
|
|
|
std::ofstream outfile(outFilename, std::ios::binary | std::ios::out);
|
|
if(!outfile.is_open())
|
|
{
|
|
fprintf(stderr, "Failed to open output file '%s'\n", outFilename.c_str());
|
|
return 1;
|
|
}
|
|
|
|
infile.read((char *)&_midiBuffer, MIDI_MAX_BUFFER_SIZE);
|
|
if(infile.bad())
|
|
{
|
|
fprintf(stderr, "Failed to read input file '%s'\n", inFilename.c_str());
|
|
return 1;
|
|
}
|
|
|
|
std::streamsize midiSize = infile.gcount();
|
|
uint8_t* midiPtr = _midiBuffer;
|
|
uint16_t gigaSize = 0;
|
|
|
|
double totalTime16 = 0;
|
|
double totalTime8 = 0;
|
|
|
|
uint16_t segmentIndex = 0;
|
|
|
|
// Header
|
|
switch(format)
|
|
{
|
|
case Format::gtMID: outputGTMIDheader(outfile, midiName, hasVolume); break;
|
|
case Format::vCPU: outputVCPUheader(outfile, midiName, startAddress, segmentSize, segmentIndex); break;
|
|
case Format::GBAS: outputGBASheader(outfile, startAddress); break;
|
|
case Format::GCL: outputGCLheader(outfile, startAddress); break;
|
|
case Format::CPP: outputCPPheader(outfile, midiName, segmentSize, segmentIndex); break;
|
|
case Format::PY: outputPYheader(outfile, midiName, segmentSize, segmentIndex); break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
// Commands
|
|
int noteOnCount = 0;
|
|
uint16_t segmentCount = 0;
|
|
uint16_t consecutiveDelays = 0;
|
|
while(midiSize)
|
|
{
|
|
uint8_t command = *midiPtr++; midiSize--;
|
|
if(command & 0x80)
|
|
{
|
|
// Start note
|
|
if((command & 0xF0) == MIDI_CMD_START_NOTE)
|
|
{
|
|
noteOnCount++;
|
|
|
|
uint8_t note = *midiPtr++; midiSize--;
|
|
if(note >= MIDI_PERCUSSION_NOTES) note -= MIDI_PERCUSSION_NOTES;
|
|
if(note < MIDI_MIN_GIGA_NOTE) note = MIDI_MIN_GIGA_NOTE;
|
|
if(note > MIDI_MAX_GIGA_NOTE) note = MIDI_MAX_GIGA_NOTE;
|
|
gigaSize += 2;
|
|
segmentCount += 2;
|
|
|
|
// Volume, (0 -> 127), needs to be scaled, inverted and offset into (127 -> 64)
|
|
uint8_t volume = 0;
|
|
if(hasVolume)
|
|
{
|
|
volume = *midiPtr++; midiSize--;
|
|
volume = (63 - ((volume & 127) >>1)) + 64;
|
|
gigaSize += 1;
|
|
segmentCount += 1;
|
|
}
|
|
|
|
switch(format)
|
|
{
|
|
case Format::gtMID: outputGTMIDcommand(command); outputGTMIDcommand(note); if(hasVolume) outputGTMIDcommand(volume); break;
|
|
case Format::vCPU: outputVCPUcommand(outfile, command); outputVCPUcommand(outfile, note); if(hasVolume) outputVCPUcommand(outfile, volume); break;
|
|
case Format::GBAS: outputGBAScommand(outfile, command); outputGBAScommand(outfile, note); if(hasVolume) outputGBAScommand(outfile, volume); break;
|
|
case Format::GCL: outputGCLcommand(outfile, command); outputGCLcommand(outfile, note); if(hasVolume) outputGCLcommand(outfile, volume); break;
|
|
case Format::CPP: outputCPPcommand(outfile, command); outputCPPcommand(outfile, note); if(hasVolume) outputCPPcommand(outfile, volume); break;
|
|
case Format::PY: outputPYcommand(outfile, command); outputPYcommand(outfile, note); if(hasVolume) outputPYcommand(outfile, volume); break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
// Stop note
|
|
else if((command & 0xF0) == MIDI_CMD_STOP_NOTE)
|
|
{
|
|
gigaSize += 1;
|
|
segmentCount += 1;
|
|
switch(format)
|
|
{
|
|
case Format::gtMID: outputGTMIDcommand(command); break;
|
|
case Format::vCPU: outputVCPUcommand(outfile, command); break;
|
|
case Format::GBAS: outputGBAScommand(outfile, command); break;
|
|
case Format::GCL: outputGCLcommand(outfile, command); break;
|
|
case Format::CPP: outputCPPcommand(outfile, command); break;
|
|
case Format::PY: outputPYcommand(outfile, command); break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
// Stop midi events are ignored
|
|
else if((command & 0xF0) == 0xF0)
|
|
{
|
|
}
|
|
// Restart midi events are ignored
|
|
else if((command & 0xF0) == 0xE0)
|
|
{
|
|
}
|
|
}
|
|
// Delay n milliseconds where n = 16bit value, converted to multiple, (if necessary), 7bit values, (0x00 <-> 0x7F)
|
|
else
|
|
{
|
|
// Coalesce sequence of delays together
|
|
int index = 0;
|
|
uint16_t coalescedDelay = ((command<<8) | *midiPtr++); midiSize--;
|
|
while(midiSize)
|
|
{
|
|
if(midiPtr[index] & 0x80) break;
|
|
coalescedDelay += (midiPtr[index]<<8) + midiPtr[index + 1];
|
|
index += 2;
|
|
midiSize -= 2;
|
|
consecutiveDelays++;
|
|
}
|
|
midiPtr += index;
|
|
|
|
// Break up coalesced delay into bytes
|
|
if(coalescedDelay)
|
|
{
|
|
totalTime16 += double(coalescedDelay);
|
|
coalescedDelay = uint16_t(double(coalescedDelay)/16.6666666667 + 0.5);
|
|
|
|
// Ignore zero delays
|
|
if(coalescedDelay)
|
|
{
|
|
uint8_t div = uint8_t(coalescedDelay / 0x7F);
|
|
uint8_t rem = uint8_t(coalescedDelay % 0x7F);
|
|
gigaSize += div + 1;
|
|
segmentCount += div + 1;
|
|
|
|
for(uint8_t i=0; i<div; i++)
|
|
{
|
|
outputDelay(outfile, format, 0x7f, timingAdjust, totalTime16, totalTime8);
|
|
}
|
|
outputDelay(outfile, format, rem, timingAdjust, totalTime16, totalTime8);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Segmented stream
|
|
if(midiSize && segmentSize && segmentOffset)
|
|
{
|
|
int size = 1;
|
|
uint8_t nextCommand = *midiPtr;
|
|
if((nextCommand & 0xF0) == 0x90) size = 3;
|
|
else if((nextCommand & 0xF0) == 0x80) size = 2;
|
|
|
|
// If segment size is approaching count, leave room for segment command + next command
|
|
if(segmentCount >= (segmentSize-(size+2)))
|
|
{
|
|
gigaSize += 3;
|
|
segmentCount = 0;
|
|
segmentIndex++;
|
|
segment += segmentOffset;
|
|
|
|
// Commands
|
|
switch(format)
|
|
{
|
|
case Format::vCPU: outputVCPUcommand(outfile, MIDI_CMD_JMP_SEG); outputVCPUcommand(outfile, LO_BYTE(segment)); outputVCPUcommand(outfile, HI_BYTE(segment), false); break;
|
|
case Format::GBAS: outputGBAScommand(outfile, MIDI_CMD_JMP_SEG); outputGBAScommand(outfile, LO_BYTE(segment)); outputGBAScommand(outfile, HI_BYTE(segment), false); break;
|
|
case Format::GCL: outputGCLcommand(outfile, MIDI_CMD_JMP_SEG); outputGCLcommand(outfile, LO_BYTE(segment)); outputGCLcommand(outfile, HI_BYTE(segment), false); break;
|
|
case Format::CPP: outputCPPcommand(outfile, MIDI_CMD_JMP_SEG); outputCPPcommand(outfile, LO_BYTE(segment)); outputCPPcommand(outfile, HI_BYTE(segment), false); break;
|
|
case Format::PY: outputPYcommand(outfile, MIDI_CMD_JMP_SEG); outputPYcommand(outfile, LO_BYTE(segment)); outputPYcommand(outfile, HI_BYTE(segment), false); break;
|
|
|
|
default: break;
|
|
}
|
|
// Old segment footer
|
|
switch(format)
|
|
{
|
|
case Format::GCL: outputGCLfooter(outfile, midiName); break;
|
|
case Format::CPP: outputCPPfooter(outfile); break;
|
|
case Format::PY: outputPYfooter(outfile); break;
|
|
|
|
default: break;
|
|
}
|
|
outfile << std::endl << std::endl;
|
|
// New segment header
|
|
switch(format)
|
|
{
|
|
case Format::vCPU: outputVCPUheader(outfile, midiName, segment, segmentSize, segmentIndex); break;
|
|
case Format::GBAS: outputGBASheader(outfile, segment); break;
|
|
case Format::GCL: outputGCLheader(outfile, segment); break;
|
|
case Format::CPP: outputCPPheader(outfile, midiName, segmentSize, segmentIndex); break;
|
|
case Format::PY: outputPYheader(outfile, midiName, segmentSize, segmentIndex); break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Last segment points back to address, (can be user edited in output source file to point to a different MIDI stream)
|
|
if(midiSize == 0)
|
|
{
|
|
switch(format)
|
|
{
|
|
case Format::vCPU: outputVCPUcommand(outfile, MIDI_CMD_JMP_SEG); outputVCPUcommand(outfile, LO_BYTE(startAddress)); outputVCPUcommand(outfile, HI_BYTE(startAddress)); break;
|
|
case Format::GBAS: outputGBAScommand(outfile, MIDI_CMD_JMP_SEG); outputGBAScommand(outfile, LO_BYTE(startAddress)); outputGBAScommand(outfile, HI_BYTE(startAddress)); break;
|
|
case Format::GCL: outputGCLcommand(outfile, MIDI_CMD_JMP_SEG); outputGCLcommand(outfile, LO_BYTE(startAddress)); outputGCLcommand(outfile, HI_BYTE(startAddress)); break;
|
|
case Format::CPP: outputCPPcommand(outfile, MIDI_CMD_JMP_SEG); outputCPPcommand(outfile, LO_BYTE(startAddress)); outputCPPcommand(outfile, HI_BYTE(startAddress)); break;
|
|
case Format::PY: outputPYcommand(outfile, MIDI_CMD_JMP_SEG); outputPYcommand(outfile, LO_BYTE(startAddress)); outputPYcommand(outfile, HI_BYTE(startAddress)); break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Footer
|
|
switch(format)
|
|
{
|
|
case Format::gtMID: outputGTMIDbuffer(outfile); break;
|
|
case Format::GCL: outputGCLfooter(outfile, midiName); break;
|
|
case Format::CPP: outputCPPfooter(outfile); break;
|
|
case Format::PY: outputPYfooter(outfile); break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
fprintf(stderr, "Note On count:%d Consecutive Delays Eliminated:%d Original size:%d New size:%d Original time:%.1lfms New time:%.1lfms Error:%.1lfms Start Address:0x%04x End Address:0x%04x\n",
|
|
noteOnCount, consecutiveDelays, int(infile.gcount()), gigaSize, totalTime16, totalTime8, totalTime8 - totalTime16, startAddress, segment+segmentSize);
|
|
return 0;
|
|
}
|