From b1a0aa86ef9d4c4088a4c615607114cd7409af9a Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 20 Mar 2022 00:52:43 +1100 Subject: [PATCH] Instrument File Import support for 2op SBI, 4op SBI, and Freq Monster 801 SBI format (4op portion only). --- src/engine/engine.cpp | 144 +++++++++++++++++++++++++++++------------- 1 file changed, 100 insertions(+), 44 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 169dcb3c..2345f0d6 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1347,6 +1347,8 @@ bool DivEngine::addInstrumentFromFile(const char* path) { format = DIV_INSFORMAT_SBI; } } + + // TDOO these really should be refactored to separate functions/cpp files per instrument file type. switch (format) { case DIV_INSFORMAT_DMP: { // this is a ridiculous mess @@ -1679,12 +1681,11 @@ bool DivEngine::addInstrumentFromFile(const char* path) { ins->type = DIV_INS_OPL; int sbi_header = reader.readI(); - // TODO check header == "SBI\x1A" (0x1A494253) + // SBI header determines format + bool is_2op = (sbi_header == 0x1A494253); // SBI\x1A + bool is_4op = (sbi_header == 0x1A504F34); // 4OP\x1A + bool is_6op = (sbi_header == 0x1A504F36); // 6OP\x1A - Freq Monster 801-specific - // 60 bytes = 4op SBI - // 52 bytes = 2op SBI (assumed) - bool is4op = (reader.size() >= 60); - // 32-byte null terminated instrument name ins->name = reader.readString(32); @@ -1713,73 +1714,128 @@ bool DivEngine::addInstrumentFromFile(const char* path) { uint8_t sbi_M4wave; uint8_t sbi_C4wave; uint8_t sbi_4opConnect; - for (int j = 0; j < 6 && reader.tell() < reader.size(); ++j) { - reader.readC(); + + if (is_2op) { + DivInstrumentFM::Operator& opM = ins->fm.op[0]; + DivInstrumentFM::Operator& opC = ins->fm.op[1]; + ins->fm.ops = 2; + opM.mult = sbi_Mcharacteristics & 0xF; + opM.ksr = ((sbi_Mcharacteristics >> 4) & 0x1); + opM.sus = ((sbi_Mcharacteristics >> 5) & 0x1); + opM.vib = ((sbi_Mcharacteristics >> 6) & 0x1); + opM.am = ((sbi_Mcharacteristics >> 7) & 0x1); + opM.tl = sbi_Mscaling_output & 0x3F; + opM.ksl = ((sbi_Mscaling_output >> 6) & 0x3); + opM.ar = ((sbi_Meg_AD >> 4) & 0xF); + opM.dr = (sbi_Meg_AD & 0xF); + opM.rr = (sbi_Meg_SR & 0xF); + opM.sl = ((sbi_Meg_SR >> 4) & 0xF); + opM.ws = sbi_Mwave; + + ins->fm.alg = (sbi_FeedConnect & 0x1); + ins->fm.fb = ((sbi_FeedConnect >> 1) & 0x7); + + opC.mult = sbi_Ccharacteristics & 0xF; + opC.ksr = ((sbi_Ccharacteristics >> 4) & 0x1); + opC.sus = ((sbi_Ccharacteristics >> 5) & 0x1); + opC.vib = ((sbi_Ccharacteristics >> 6) & 0x1); + opC.am = ((sbi_Ccharacteristics >> 7) & 0x1); + opC.tl = sbi_Cscaling_output & 0x3F; + opC.ksl = ((sbi_Cscaling_output >> 6) & 0x3); + opC.ar = ((sbi_Ceg_AD >> 4) & 0xF); + opC.dr = (sbi_Ceg_AD & 0xF); + opC.rr = (sbi_Ceg_SR & 0xF); + opC.sl = ((sbi_Ceg_SR >> 4) & 0xF); + opC.ws = sbi_Cwave; + + // Ignore rest of file - rest is 'reserved padding'. + if (is_2op) { + reader.seek(0, SEEK_END); + } } - - DivInstrumentFM::Operator& opM = ins->fm.op[0]; - DivInstrumentFM::Operator& opC = ins->fm.op[1]; - opM.mult = sbi_Mcharacteristics & 0xF; - opM.tl = sbi_Mscaling_output & 0x3F; - //opM.rs = reader.readC(); - opM.ar = ((sbi_Meg_AD >> 4) & 0xF); - opM.dr = (sbi_Meg_AD & 0xF); - opM.rr = (sbi_Meg_SR & 0xF); - opM.sl = ((sbi_Meg_SR >> 4) & 0xF); - //opM.ssgEnv = reader.readC(); + if (is_4op || is_6op) { + // Operator placement is different so need to place in correct registers. + // Note: 6op is an unofficial extension of 4op SBIs by Darron Broad (Freq Monster 801). + // We'll only use the 4op portion here for pure OPL3. + DivInstrumentFM::Operator& opM = ins->fm.op[0]; + DivInstrumentFM::Operator& opC = ins->fm.op[2]; + DivInstrumentFM::Operator& opM4 = ins->fm.op[1]; + DivInstrumentFM::Operator& opC4 = ins->fm.op[3]; + ins->fm.ops = 4; - ins->fm.alg = (sbi_FeedConnect & 0x1); - ins->fm.fb = (sbi_FeedConnect >> 1) & 0x7; - - opC.mult = sbi_Ccharacteristics & 0xF; - opC.tl = sbi_Cscaling_output & 0x3F; - //opC.rs = ???; - opC.ar = ((sbi_Ceg_AD >> 4) & 0xF); - opC.dr = (sbi_Ceg_AD & 0xF); - opC.rr = (sbi_Ceg_SR & 0xF); - opC.sl = ((sbi_Ceg_SR >> 4) & 0xF); - //opC.ssgEnv = ???; - - if (is4op) { sbi_M4characteristics = reader.readC(); sbi_C4characteristics = reader.readC(); sbi_M4scaling_output = reader.readC(); sbi_C4scaling_output = reader.readC(); sbi_M4eg_AD = reader.readC(); + sbi_C4eg_AD = reader.readC(); sbi_M4eg_SR = reader.readC(); + sbi_C4eg_SR = reader.readC(); sbi_M4wave = reader.readC(); sbi_C4wave = reader.readC(); sbi_4opConnect = reader.readC(); - for (int j = 0; j < 6 && reader.tell() < reader.size(); ++j) { - reader.readC(); - } + + ins->fm.alg = (sbi_FeedConnect & 0x1) | ((sbi_4opConnect & 0x1) << 1); + opM.mult = sbi_Mcharacteristics & 0xF; + opM.ksr = ((sbi_Mcharacteristics >> 4) & 0x1); + opM.sus = ((sbi_Mcharacteristics >> 5) & 0x1); + opM.vib = ((sbi_Mcharacteristics >> 6) & 0x1); + opM.am = ((sbi_Mcharacteristics >> 7) & 0x1); + opM.tl = sbi_Mscaling_output & 0x3F; + opM.ksl = ((sbi_Mscaling_output >> 6) & 0x3); + opM.ar = ((sbi_Meg_AD >> 4) & 0xF); + opM.dr = (sbi_Meg_AD & 0xF); + opM.rr = (sbi_Meg_SR & 0xF); + opM.sl = ((sbi_Meg_SR >> 4) & 0xF); + opM.ws = sbi_Mwave; - ins->fm.alg |= (sbi_4opConnect & 0x1); - DivInstrumentFM::Operator& opM4 = ins->fm.op[2]; - DivInstrumentFM::Operator& opC4 = ins->fm.op[3]; - + opC.mult = sbi_Ccharacteristics & 0xF; + opC.ksr = ((sbi_Ccharacteristics >> 4) & 0x1); + opC.sus = ((sbi_Ccharacteristics >> 5) & 0x1); + opC.vib = ((sbi_Ccharacteristics >> 6) & 0x1); + opC.am = ((sbi_Ccharacteristics >> 7) & 0x1); + opC.tl = sbi_Cscaling_output & 0x3F; + opC.ksl = ((sbi_Cscaling_output >> 6) & 0x3); + opC.ar = ((sbi_Ceg_AD >> 4) & 0xF); + opC.dr = (sbi_Ceg_AD & 0xF); + opC.rr = (sbi_Ceg_SR & 0xF); + opC.sl = ((sbi_Ceg_SR >> 4) & 0xF); + opC.ws = sbi_Cwave; + opM4.mult = sbi_M4characteristics & 0xF; + opM4.ksr = ((sbi_M4characteristics >> 4) & 0x1); + opM4.sus = ((sbi_M4characteristics >> 5) & 0x1); + opM4.vib = ((sbi_M4characteristics >> 6) & 0x1); + opM4.am = ((sbi_M4characteristics >> 7) & 0x1); opM4.tl = sbi_M4scaling_output & 0x3F; - //opM4.rs = reader.readC(); + opM4.ksl = ((sbi_M4scaling_output >> 6) & 0x3); opM4.ar = ((sbi_M4eg_AD >> 4) & 0xF); opM4.dr = (sbi_M4eg_AD & 0xF); opM4.rr = (sbi_M4eg_SR & 0xF); opM4.sl = ((sbi_M4eg_SR >> 4) & 0xF); - //opM4.ssgEnv = reader.readC(); + opM4.ws = sbi_M4wave; opC4.mult = sbi_C4characteristics & 0xF; + opC4.ksr = ((sbi_C4characteristics >> 4) & 0x1); + opC4.sus = ((sbi_C4characteristics >> 5) & 0x1); + opC4.vib = ((sbi_C4characteristics >> 6) & 0x1); + opC4.am = ((sbi_C4characteristics >> 7) & 0x1); opC4.tl = sbi_C4scaling_output & 0x3F; - //opC4.rs = ???; + opC4.ksl = ((sbi_C4scaling_output >> 6) & 0x3); opC4.ar = ((sbi_C4eg_AD >> 4) & 0xF); opC4.dr = (sbi_C4eg_AD & 0xF); opC4.rr = (sbi_C4eg_SR & 0xF); opC4.sl = ((sbi_C4eg_SR >> 4) & 0xF); - //opC4.ssgEnv = ???; + opC4.ws = sbi_C4wave; + + // Ignore rest of file once we've read in all we need. + // Note: Freq Monster 801 adds a ton of other additional fields irrelevant to chip registers. + reader.seek(0, SEEK_END); } - } - catch (EndOfFileException& e) { + + } catch (EndOfFileException& e) { lastError = "premature end of file"; logE("premature end of file!\n"); delete ins;