#79: OPM import progress and start everything else

This commit is contained in:
James Alan Nguyen 2022-04-23 19:52:09 +10:00
parent 9c8d122389
commit f716ac262d
4 changed files with 198 additions and 10 deletions

View file

@ -31,6 +31,7 @@
#include <mutex> #include <mutex>
#include <map> #include <map>
#include <queue> #include <queue>
#include <sstream>
#define addWarning(x) \ #define addWarning(x) \
if (warnings.empty()) { \ if (warnings.empty()) { \
@ -288,9 +289,15 @@ class DivEngine {
void loadVGI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath); void loadVGI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadS3I(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath); void loadS3I(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadSBI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath); void loadSBI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadOPLI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadOPNI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadPAT(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadY12(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath); void loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath); void loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath); void loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadWOPL(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadWOPN(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
bool initAudioBackend(); bool initAudioBackend();
bool deinitAudioBackend(); bool deinitAudioBackend();

View file

@ -30,9 +30,15 @@ enum DivInsFormats {
DIV_INSFORMAT_BTI, DIV_INSFORMAT_BTI,
DIV_INSFORMAT_S3I, DIV_INSFORMAT_S3I,
DIV_INSFORMAT_SBI, DIV_INSFORMAT_SBI,
DIV_INSFORMAT_PAT,
DIV_INSFORMAT_Y12,
DIV_INSFORMAT_OPLI,
DIV_INSFORMAT_OPNI,
DIV_INSFORMAT_BNK, DIV_INSFORMAT_BNK,
DIV_INSFORMAT_OPM, DIV_INSFORMAT_OPM,
DIV_INSFORMAT_FF, DIV_INSFORMAT_FF,
DIV_INSFORMAT_WOPL,
DIV_INSFORMAT_WOPN,
}; };
// Patch data structures // Patch data structures
@ -670,6 +676,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, St
auto& ins = insList[i]; auto& ins = insList[i];
ins->type = DIV_INS_OPL; ins->type = DIV_INS_OPL;
ins->fm.ops = 2;
timbre.mode = reader.readC(); timbre.mode = reader.readC();
timbre.percVoice = reader.readC(); timbre.percVoice = reader.readC();
@ -810,16 +817,132 @@ void DivEngine::loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, Str
} }
void DivEngine::loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) { void DivEngine::loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) {
DivInstrument* ins[128]; std::vector<DivInstrument*> insList;
memset(ins,0,128*sizeof(void*)); std::stringstream ss;
int readCount = 0;
bool patchNameRead = false,
lfoRead = false,
characteristicRead = false,
m1Read = false,
c1Read = false,
m2Read = false,
c2Read = false;
auto completePatchRead = [&]() {
return patchNameRead && lfoRead && characteristicRead && m1Read && c1Read && m2Read && c2Read;
};
auto resetPatchRead = [&]() {
patchNameRead = lfoRead = characteristicRead = m1Read = c1Read = m2Read = c2Read = false;
};
auto readOpmOperator = [](SafeReader& reader, DivInstrumentFM::Operator& op) {
op.ar = atoi(reader.readString_Token().c_str());
op.dr = atoi(reader.readString_Token().c_str());
op.d2r = atoi(reader.readString_Token().c_str());
op.rr = atoi(reader.readString_Token().c_str());
op.sl = atoi(reader.readString_Token().c_str());
op.tl = atoi(reader.readString_Token().c_str());
op.ksl = atoi(reader.readString_Token().c_str());
op.mult = atoi(reader.readString_Token().c_str());
op.dt = atoi(reader.readString_Token().c_str());
op.dt2 = atoi(reader.readString_Token().c_str());
op.ssgEnv = atoi(reader.readString_Token().c_str());
};
DivInstrument* newPatch = nullptr;
try { try {
String line; reader.seek(0, SEEK_SET);
while (!reader.isEOF()) {
String token = reader.readString_Token();
if (token.length() == 0) {
continue;
}
if (token.compare(0,2,"//") == 0) {
if (!reader.isEOF()) {
reader.readString_Line();
}
continue;
}
// At this point we know any other line would be associated with patch params
if (newPatch == nullptr) {
newPatch = new DivInstrument;
newPatch->type = DIV_INS_FM;
newPatch->fm.ops = 4;
}
// Read each line for their respective params. They may not be written in the same order but they
// must absolutely be properly grouped per patch! Line prefixes must be separated by a space!
// Patch number + name
// "@:123 Name of patch"
if (token.length() >= 2) {
if (token[0] == '@') {
newPatch->name = reader.readString_Line();
patchNameRead = true;
} else if (token.compare(0,3,"CH:") == 0) {
// CH: PAN FL CON AMS PMS SLOT NE
reader.readString_Token(); // skip PAN
newPatch->fm.fb = atoi(reader.readString_Token().c_str());
newPatch->fm.alg = atoi(reader.readString_Token().c_str());
newPatch->fm.ams = atoi(reader.readString_Token().c_str());
newPatch->fm.ams2 = atoi(reader.readString_Token().c_str());
reader.readString_Token(); // skip SLOT
reader.readString_Token(); // skip NE
characteristicRead = true;
} else if (token.compare(0,3,"C1:") == 0) {
// C1: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN
readOpmOperator(reader, newPatch->fm.op[2]);
c1Read = true;
} else if (token.compare(0,3,"C2:") == 0) {
// C2: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN
readOpmOperator(reader, newPatch->fm.op[3]);
c2Read = true;
} else if (token.compare(0,3,"M1:") == 0) {
// M1: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN
readOpmOperator(reader, newPatch->fm.op[0]);
m1Read = true;
} else if (token.compare(0,3,"M2:") == 0) {
// M2: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN
readOpmOperator(reader, newPatch->fm.op[1]);
m2Read = true;
} else if (token.compare(0,4,"LFO:") == 0) {
// LFO: LFRQ AMD PMD WF NFRQ
// Furnace patches do not store this as these are chip-global.
reader.readString_Line();
lfoRead = true;
} else {
// other unsupported lines ignored.
reader.readString_Line();
}
}
if (completePatchRead()) {
insList.push_back(newPatch);
newPatch = nullptr;
++readCount;
}
}
if (newPatch != nullptr) {
addWarning("Last OPM patch read was incomplete and therefore not imported.");
logW("Last OPM patch read was incomplete and therefore not imported.");
delete newPatch;
}
for (int i = 0; i < readCount; ++i) {
ret.push_back(insList[i]);
}
} catch (EndOfFileException& e) { } catch (EndOfFileException& e) {
lastError="premature end of file"; lastError = "premature end of file";
logE("premature end of file"); logE("premature end of file");
return; for (int i = readCount; i >= 0; --i) {
delete insList[i];
}
delete newPatch;
} }
} }
@ -947,12 +1070,24 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) {
format=DIV_INSFORMAT_S3I; format=DIV_INSFORMAT_S3I;
} else if (extS==String(".sbi")) { } else if (extS==String(".sbi")) {
format=DIV_INSFORMAT_SBI; format=DIV_INSFORMAT_SBI;
} else if (extS==String(".opli")) {
format=DIV_INSFORMAT_OPLI;
} else if (extS==String(".opni")) {
format=DIV_INSFORMAT_OPNI;
} else if (extS==String(".pat")) {
format=DIV_INSFORMAT_PAT;
} else if (extS==String(".y12")) {
format=DIV_INSFORMAT_Y12;
} else if (extS==String(".bnk")) { } else if (extS==String(".bnk")) {
format=DIV_INSFORMAT_BNK; format=DIV_INSFORMAT_BNK;
} else if (extS==String(".opm")) { } else if (extS==String(".opm")) {
format=DIV_INSFORMAT_OPM; format=DIV_INSFORMAT_OPM;
} else if (extS==String(".ff")) { } else if (extS==String(".ff")) {
format=DIV_INSFORMAT_FF; format=DIV_INSFORMAT_FF;
} else if (extS==String(".wopl")) {
format=DIV_INSFORMAT_WOPL;
} else if (extS==String(".wopn")) {
format=DIV_INSFORMAT_WOPN;
} }
} }
@ -971,7 +1106,8 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) {
break; break;
case DIV_INSFORMAT_BTI: // TODO case DIV_INSFORMAT_BTI: // TODO
break; break;
case DIV_INSFORMAT_OPM: // TODO case DIV_INSFORMAT_OPM:
loadOPM(reader,ret,stripPath);
break; break;
case DIV_INSFORMAT_S3I: case DIV_INSFORMAT_S3I:
loadS3I(reader,ret,stripPath); loadS3I(reader,ret,stripPath);

View file

@ -139,12 +139,14 @@ double SafeReader::readD() {
} }
String SafeReader::readString(size_t stlen) { String SafeReader::readString(size_t stlen) {
String ret; String ret(stlen, ' ');
#ifdef READ_DEBUG #ifdef READ_DEBUG
logD("SR: reading string len %d at %x",stlen,curSeek); logD("SR: reading string len %d at %x",stlen,curSeek);
#endif #endif
size_t curPos=0; size_t curPos=0;
while (curPos<stlen) { if (isEOF()) throw EndOfFileException(this, len);
while (!isEOF() && curPos<stlen) {
unsigned char c=readC(); unsigned char c=readC();
if (c!=0) ret.push_back(c); if (c!=0) ret.push_back(c);
curPos++; curPos++;
@ -155,8 +157,48 @@ String SafeReader::readString(size_t stlen) {
String SafeReader::readString() { String SafeReader::readString() {
String ret; String ret;
unsigned char c; unsigned char c;
while ((c=readC())!=0) { if (isEOF()) throw EndOfFileException(this, len);
while (!isEOF() && (c=readC())!=0) {
ret.push_back(c); ret.push_back(c);
} }
return ret; return ret;
} }
String SafeReader::readString_Line() {
String ret;
unsigned char c;
if (isEOF()) throw EndOfFileException(this, len);
while (!isEOF() && (c = readC()) != 0) {
if (c=='\r'||c=='\n') {
break;
}
ret.push_back(c);
}
return ret;
}
String SafeReader::readString_Token(unsigned char delim) {
String ret;
unsigned char c;
if (isEOF()) throw EndOfFileException(this, len);
while (!isEOF() && (c=readC())!=0) {
if (c == '\r' || c == '\n') {
break;
}
if (c == delim) {
if (ret.length() == 0) {
continue;
}
break;
}
ret.push_back(c);
}
return ret;
}
bool SafeReader::isEOF() {
return curSeek >= len;
}

View file

@ -66,6 +66,9 @@ class SafeReader {
double readD_BE(); double readD_BE();
String readString(); String readString();
String readString(size_t len); String readString(size_t len);
String readString_Line();
String readString_Token(unsigned char delim=' ');
bool isEOF();
SafeReader(void* b, size_t l): SafeReader(void* b, size_t l):
buf((unsigned char*)b), buf((unsigned char*)b),