From ee14f0fa8c27e531631e817668a31be48f4de688 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Tue, 12 Apr 2022 21:07:21 +1000 Subject: [PATCH 01/10] SBI fixes: - Support 2OP\x1A header (used in some third party implementations) - Include 2op pair for Freq Monster 801 6op (4+2op) patches. - Fall back to filename if no internal patch name found. --- src/engine/fileOpsIns.cpp | 71 +++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index af0c06d8..b8978f2a 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -447,6 +447,8 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St logE("S3I PCM samples currently not supported."); } ins->name = reader.readString(28); + ins->name = (ins->name[0] == '\0') ? stripPath : ins->name; + int s3i_signature = reader.readI(); if (s3i_signature != 0x49524353) { @@ -470,12 +472,13 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St int sbi_header = reader.readI(); // SBI header determines format - bool is_2op = (sbi_header == 0x1A494253); // SBI\x1A + bool is_2op = (sbi_header == 0x1A494253 || sbi_header == 0x1A504F32); // SBI\x1A or 2OP\x1A bool is_4op = (sbi_header == 0x1A504F34); // 4OP\x1A bool is_6op = (sbi_header == 0x1A504F36); // 6OP\x1A - Freq Monster 801-specific // 32-byte null terminated instrument name - ins->name = reader.readString(32); + String patchName = reader.readString(32); + patchName = (patchName.length() == 0) ? stripPath : patchName; // 2op SBI uint8_t sbi_Mcharacteristics = reader.readC(); @@ -502,11 +505,13 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St uint8_t sbi_M4wave; uint8_t sbi_C4wave; uint8_t sbi_4opConnect; - + if (is_2op) { DivInstrumentFM::Operator& opM = ins->fm.op[0]; DivInstrumentFM::Operator& opC = ins->fm.op[1]; ins->fm.ops = 2; + ins->name = patchName; + opM.mult = sbi_Mcharacteristics & 0xF; opM.ksr = ((sbi_Mcharacteristics >> 4) & 0x1); opM.sus = ((sbi_Mcharacteristics >> 5) & 0x1); @@ -538,6 +543,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St // Ignore rest of file - rest is 'reserved padding'. reader.seek(0, SEEK_END); + ret.push_back(ins); } if (is_4op || is_6op) { @@ -549,6 +555,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St DivInstrumentFM::Operator& opM4 = ins->fm.op[1]; DivInstrumentFM::Operator& opC4 = ins->fm.op[3]; ins->fm.ops = 4; + ins->name = patchName; sbi_M4characteristics = reader.readC(); sbi_C4characteristics = reader.readC(); @@ -617,19 +624,71 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St opC4.sl = ((sbi_C4eg_SR >> 4) & 0xF); opC4.ws = sbi_C4wave; + // Freq Monster 801 SBIs use a 4op+2op layout + if (is_6op) { + ins->name = ins->name + " (4op)"; + ret.push_back(ins); + + ins = new DivInstrument; + DivInstrumentFM::Operator& opM6 = ins->fm.op[0]; + DivInstrumentFM::Operator& opC6 = ins->fm.op[1]; + ins->type = DIV_INS_OPL; + ins->fm.ops = 2; + ins->name = patchName + " (2op)"; + + sbi_Mcharacteristics = reader.readC(); + sbi_Ccharacteristics = reader.readC(); + sbi_Mscaling_output = reader.readC(); + sbi_Cscaling_output = reader.readC(); + sbi_Meg_AD = reader.readC(); + sbi_Ceg_AD = reader.readC(); + sbi_Meg_SR = reader.readC(); + sbi_Ceg_SR = reader.readC(); + sbi_Mwave = reader.readC(); + sbi_Cwave = reader.readC(); + sbi_FeedConnect = reader.readC(); + + opM6.mult = sbi_Mcharacteristics & 0xF; + opM6.ksr = ((sbi_Mcharacteristics >> 4) & 0x1); + opM6.sus = ((sbi_Mcharacteristics >> 5) & 0x1); + opM6.vib = ((sbi_Mcharacteristics >> 6) & 0x1); + opM6.am = ((sbi_Mcharacteristics >> 7) & 0x1); + opM6.tl = sbi_Mscaling_output & 0x3F; + opM6.ksl = ((sbi_Mscaling_output >> 6) & 0x3); + opM6.ar = ((sbi_Meg_AD >> 4) & 0xF); + opM6.dr = (sbi_Meg_AD & 0xF); + opM6.rr = (sbi_Meg_SR & 0xF); + opM6.sl = ((sbi_Meg_SR >> 4) & 0xF); + opM6.ws = sbi_Mwave; + + ins->fm.alg = (sbi_FeedConnect & 0x1); + ins->fm.fb = ((sbi_FeedConnect >> 1) & 0x7); + + opC6.mult = sbi_Ccharacteristics & 0xF; + opC6.ksr = ((sbi_Ccharacteristics >> 4) & 0x1); + opC6.sus = ((sbi_Ccharacteristics >> 5) & 0x1); + opC6.vib = ((sbi_Ccharacteristics >> 6) & 0x1); + opC6.am = ((sbi_Ccharacteristics >> 7) & 0x1); + opC6.tl = sbi_Cscaling_output & 0x3F; + opC6.ksl = ((sbi_Cscaling_output >> 6) & 0x3); + opC6.ar = ((sbi_Ceg_AD >> 4) & 0xF); + opC6.dr = (sbi_Ceg_AD & 0xF); + opC6.rr = (sbi_Ceg_SR & 0xF); + opC6.sl = ((sbi_Ceg_SR >> 4) & 0xF); + opC6.ws = sbi_Cwave; + } + // 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); + ret.push_back(ins); } } catch (EndOfFileException& e) { lastError = "premature end of file"; logE("premature end of file!"); delete ins; - return; } - - ret.push_back(ins); } void DivEngine::loadFF(SafeReader& reader, std::vector& ret, String& stripPath) { From 29ed73a1577de8f136623e07f206519935216840 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Thu, 14 Apr 2022 21:33:20 +1000 Subject: [PATCH 02/10] Start BNK --- src/engine/engine.h | 1 + src/engine/fileOpsIns.cpp | 30 ++++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 6bcaa38c..d7860f37 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -287,6 +287,7 @@ class DivEngine { void loadVGI(SafeReader& reader, std::vector& ret, String& stripPath); void loadS3I(SafeReader& reader, std::vector& ret, String& stripPath); void loadSBI(SafeReader& reader, std::vector& ret, String& stripPath); + void loadBNK(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPM(SafeReader& reader, std::vector& ret, String& stripPath); void loadFF(SafeReader& reader, std::vector& ret, String& stripPath); diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index b8978f2a..9be3f02c 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -30,6 +30,7 @@ enum DivInsFormats { DIV_INSFORMAT_BTI, DIV_INSFORMAT_S3I, DIV_INSFORMAT_SBI, + DIV_INSFORMAT_BNK, DIV_INSFORMAT_OPM, DIV_INSFORMAT_FF, }; @@ -447,7 +448,7 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St logE("S3I PCM samples currently not supported."); } ins->name = reader.readString(28); - ins->name = (ins->name[0] == '\0') ? stripPath : ins->name; + ins->name = (ins->name.length() == 0) ? stripPath : ins->name; int s3i_signature = reader.readI(); @@ -544,9 +545,8 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St // Ignore rest of file - rest is 'reserved padding'. reader.seek(0, SEEK_END); ret.push_back(ins); - } - if (is_4op || is_6op) { + } else 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. @@ -624,8 +624,9 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St opC4.sl = ((sbi_C4eg_SR >> 4) & 0xF); opC4.ws = sbi_C4wave; - // Freq Monster 801 SBIs use a 4op+2op layout if (is_6op) { + // Freq Monster 801 6op SBIs use a 4+2op layout + // Save the 4op portion before reading the 2op part ins->name = ins->name + " (4op)"; ret.push_back(ins); @@ -680,6 +681,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St // 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. + // If instrument transpose is ever supported, we can read it in maybe? reader.seek(0, SEEK_END); ret.push_back(ins); } @@ -690,6 +692,22 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St delete ins; } } +void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, String& stripPath) { + DivInstrument* insList[256]; + memset(insList, 0, 256 * sizeof(void*)); + + // First distinguish between GEMS BNK and Adlib BNK + bool is_gems = false; // TODO + bool is_adlib = true; // TODO + + if (is_gems) { + logE("GEMS BNK currently not supported."); + + } else if (is_adlib) { + // TODO + } +} + void DivEngine::loadFF(SafeReader& reader, std::vector& ret, String& stripPath) { DivInstrument* insList[256]; @@ -902,6 +920,8 @@ std::vector DivEngine::instrumentFromFile(const char* path) { format=DIV_INSFORMAT_S3I; } else if (extS==String(".sbi")) { format=DIV_INSFORMAT_SBI; + } else if (ext5==String(".bnk")) { + format=DIV_INSFORMAT_BNK; } else if (extS==String(".opm")) { format=DIV_INSFORMAT_OPM; } else if (extS==String(".ff")) { @@ -932,6 +952,8 @@ std::vector DivEngine::instrumentFromFile(const char* path) { case DIV_INSFORMAT_SBI: loadSBI(reader,ret,stripPath); break; + case DIV_INSFORMAT_BNK: + loadBNK(reader, ret, stripPath); case DIV_INSFORMAT_FF: loadFF(reader,ret,stripPath); break; From 7e692eb0735d13072ff83c3d76e9a6504903972c Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Fri, 15 Apr 2022 00:32:55 +1000 Subject: [PATCH 03/10] Checkpoint: BNK presets now loading and also refactoring duplicated operations. --- src/engine/fileOpsIns.cpp | 378 +++++++++++++++++++++----------------- 1 file changed, 207 insertions(+), 171 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 9be3f02c..7d2dda37 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -391,6 +391,9 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St if (s3i_type >= 2) { ins->type = DIV_INS_OPL; + if (s3i_type > 2 && s3i_type <= 7) { + ins->fm.opllPreset = (uint8_t)(1<<4); // Flag as Drum preset. + } // skip internal filename - we'll use the long name description reader.seek(12, SEEK_CUR); @@ -481,72 +484,89 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St String patchName = reader.readString(32); patchName = (patchName.length() == 0) ? stripPath : patchName; - // 2op SBI - uint8_t sbi_Mcharacteristics = reader.readC(); - uint8_t sbi_Ccharacteristics = reader.readC(); - uint8_t sbi_Mscaling_output = reader.readC(); - uint8_t sbi_Cscaling_output = reader.readC(); - uint8_t sbi_Meg_AD = reader.readC(); - uint8_t sbi_Ceg_AD = reader.readC(); - uint8_t sbi_Meg_SR = reader.readC(); - uint8_t sbi_Ceg_SR = reader.readC(); - uint8_t sbi_Mwave = reader.readC(); - uint8_t sbi_Cwave = reader.readC(); - uint8_t sbi_FeedConnect = reader.readC(); + typedef struct { + uint8_t Mcharacteristics, + Ccharacteristics, + Mscaling_output, + Cscaling_output, + Meg_AD, + Ceg_AD, + Meg_SR, + Ceg_SR, + Mwave, + Cwave, + FeedConnect; + } sbi_t; - // 4op SBI - uint8_t sbi_M4characteristics; - uint8_t sbi_C4characteristics; - uint8_t sbi_M4scaling_output; - uint8_t sbi_C4scaling_output; - uint8_t sbi_M4eg_AD; - uint8_t sbi_C4eg_AD; - uint8_t sbi_M4eg_SR; - uint8_t sbi_C4eg_SR; - uint8_t sbi_M4wave; - uint8_t sbi_C4wave; - uint8_t sbi_4opConnect; + auto readSbiOpData = [](sbi_t& sbi, SafeReader& reader) { + sbi.Mcharacteristics = reader.readC(); + sbi.Ccharacteristics = reader.readC(); + sbi.Mscaling_output = reader.readC(); + sbi.Cscaling_output = reader.readC(); + sbi.Meg_AD = reader.readC(); + sbi.Ceg_AD = reader.readC(); + sbi.Meg_SR = reader.readC(); + sbi.Ceg_SR = reader.readC(); + sbi.Mwave = reader.readC(); + sbi.Cwave = reader.readC(); + sbi.FeedConnect = reader.readC(); + }; + + auto writeOp = [](sbi_t& sbi, DivInstrumentFM::Operator& opM, DivInstrumentFM::Operator& opC) { + 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; + + 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; + }; + + sbi_t sbi_op12; // 2op (+6op portion) + sbi_t sbi_op34; // 4op + + readSbiOpData(sbi_op12, reader); if (is_2op) { DivInstrumentFM::Operator& opM = ins->fm.op[0]; DivInstrumentFM::Operator& opC = ins->fm.op[1]; ins->fm.ops = 2; ins->name = patchName; + writeOp(sbi_op12, opM, opC); + ins->fm.alg = (sbi_op12.FeedConnect & 0x1); + ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7); - 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; + // SBTimbre extensions + uint8_t perc_voc = reader.readC(); + if (perc_voc >= 6) { + ins->fm.opllPreset = (uint8_t)(1 << 4); + } // Ignore rest of file - rest is 'reserved padding'. - reader.seek(0, SEEK_END); + reader.seek(4, SEEK_CUR); ret.push_back(ins); } else if (is_4op || is_6op) { + readSbiOpData(sbi_op34, reader); + // 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. @@ -556,127 +576,28 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St DivInstrumentFM::Operator& opC4 = ins->fm.op[3]; ins->fm.ops = 4; ins->name = patchName; - - 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(); - - ins->fm.alg = (sbi_FeedConnect & 0x1) | ((sbi_4opConnect & 0x1) << 1); - ins->fm.fb = ((sbi_FeedConnect >> 1) & 0x7); - - 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; - - 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.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.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.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.ws = sbi_C4wave; + ins->fm.alg = (sbi_op12.FeedConnect & 0x1) | ((sbi_op34.FeedConnect & 0x1) << 1); + ins->fm.fb = ((sbi_op34.FeedConnect >> 1) & 0x7); + writeOp(sbi_op12, opM, opC); + writeOp(sbi_op34, opM4, opC4); if (is_6op) { // Freq Monster 801 6op SBIs use a 4+2op layout // Save the 4op portion before reading the 2op part - ins->name = ins->name + " (4op)"; + ins->name = fmt::format("{0} (4op)", ins->name); ret.push_back(ins); + readSbiOpData(sbi_op12, reader); + ins = new DivInstrument; DivInstrumentFM::Operator& opM6 = ins->fm.op[0]; DivInstrumentFM::Operator& opC6 = ins->fm.op[1]; ins->type = DIV_INS_OPL; ins->fm.ops = 2; - ins->name = patchName + " (2op)"; - - sbi_Mcharacteristics = reader.readC(); - sbi_Ccharacteristics = reader.readC(); - sbi_Mscaling_output = reader.readC(); - sbi_Cscaling_output = reader.readC(); - sbi_Meg_AD = reader.readC(); - sbi_Ceg_AD = reader.readC(); - sbi_Meg_SR = reader.readC(); - sbi_Ceg_SR = reader.readC(); - sbi_Mwave = reader.readC(); - sbi_Cwave = reader.readC(); - sbi_FeedConnect = reader.readC(); - - opM6.mult = sbi_Mcharacteristics & 0xF; - opM6.ksr = ((sbi_Mcharacteristics >> 4) & 0x1); - opM6.sus = ((sbi_Mcharacteristics >> 5) & 0x1); - opM6.vib = ((sbi_Mcharacteristics >> 6) & 0x1); - opM6.am = ((sbi_Mcharacteristics >> 7) & 0x1); - opM6.tl = sbi_Mscaling_output & 0x3F; - opM6.ksl = ((sbi_Mscaling_output >> 6) & 0x3); - opM6.ar = ((sbi_Meg_AD >> 4) & 0xF); - opM6.dr = (sbi_Meg_AD & 0xF); - opM6.rr = (sbi_Meg_SR & 0xF); - opM6.sl = ((sbi_Meg_SR >> 4) & 0xF); - opM6.ws = sbi_Mwave; - - ins->fm.alg = (sbi_FeedConnect & 0x1); - ins->fm.fb = ((sbi_FeedConnect >> 1) & 0x7); - - opC6.mult = sbi_Ccharacteristics & 0xF; - opC6.ksr = ((sbi_Ccharacteristics >> 4) & 0x1); - opC6.sus = ((sbi_Ccharacteristics >> 5) & 0x1); - opC6.vib = ((sbi_Ccharacteristics >> 6) & 0x1); - opC6.am = ((sbi_Ccharacteristics >> 7) & 0x1); - opC6.tl = sbi_Cscaling_output & 0x3F; - opC6.ksl = ((sbi_Cscaling_output >> 6) & 0x3); - opC6.ar = ((sbi_Ceg_AD >> 4) & 0xF); - opC6.dr = (sbi_Ceg_AD & 0xF); - opC6.rr = (sbi_Ceg_SR & 0xF); - opC6.sl = ((sbi_Ceg_SR >> 4) & 0xF); - opC6.ws = sbi_Cwave; + ins->name = fmt::format("{0} (2op)", patchName); + writeOp(sbi_op12, opM6, opC6); + ins->fm.alg = (sbi_op12.FeedConnect & 0x1); + ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7); } // Ignore rest of file once we've read in all we need. @@ -694,18 +615,133 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St } void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, String& stripPath) { DivInstrument* insList[256]; + String instNames[256]; memset(insList, 0, 256 * sizeof(void*)); + reader.seek(0, SEEK_SET); // First distinguish between GEMS BNK and Adlib BNK - bool is_gems = false; // TODO - bool is_adlib = true; // TODO + uint64_t header = reader.readL(); + bool is_adlib = ((header>>8) == 0x2d42494c444100L); + + if (is_adlib) { + // Caveat: Technically Adlib BNK can hold up to 0xFFFF instruments, + // but Furnace only can handle up to 0xFF. + + typedef struct { + uint8_t ksl, + multiple, + feedback, // op1 only + attack, + sustain, + eg, + decay, + releaseRate, + totalLevel, + am, + vib, + ksr, + con; // op1 only + } bnkop_t; - if (is_gems) { - logE("GEMS BNK currently not supported."); + typedef struct { + uint8_t mode, // version + percVoice; // perc + bnkop_t op[2]; + uint8_t wave0, // wave op1 + wave1; // wave op2 + } bnktimbre_t; - } else if (is_adlib) { - // TODO - } + int readCount = 0; + + try { + // Seek to BNK patch names + reader.seek(0x0c, SEEK_SET); + int name_offset = reader.readI(); + reader.seek(0x10, SEEK_SET); + int data_offset = reader.readI(); + + reader.seek(name_offset, SEEK_SET); + + while (readCount < 256 && reader.tell() < data_offset) { + reader.seek(3, SEEK_CUR); + instNames[readCount] = reader.readString(9); + ++readCount; + } + + if (readCount >= 256) { + logW("BNK exceeds 256 presets. Only first 256 will be imported.\n"); + } + + // Seek to BNK data + reader.seek(data_offset, SEEK_SET); + + // Read until EOF + for (int i = 0; i < readCount && i < 256; ++i) { + try { + bnktimbre_t timbre; + insList[i] = new DivInstrument; + auto& ins = insList[i]; + + ins->type = DIV_INS_OPL; + + timbre.mode = reader.readC(); + timbre.percVoice = reader.readC(); + if (timbre.mode == 1) { + ins->fm.opllPreset = (uint8_t)(1<<4); + } + ins->fm.op[0].ksl = reader.readC(); + ins->fm.op[0].mult = reader.readC(); + ins->fm.fb = reader.readC(); + ins->fm.op[0].ar = reader.readC(); + ins->fm.op[0].sl = reader.readC(); + ins->fm.op[0].ksr = reader.readC(); + ins->fm.op[0].dr = reader.readC(); + ins->fm.op[0].rr = reader.readC(); + ins->fm.op[0].tl = reader.readC(); + ins->fm.op[0].am = reader.readC(); + ins->fm.op[0].vib = reader.readC(); + ins->fm.op[0].ksr = reader.readC(); + ins->fm.alg = (reader.readC() == 0) ? 1 : 0; + + ins->fm.op[1].ksl = reader.readC(); + ins->fm.op[1].mult = reader.readC(); + reader.readC(); // skip + ins->fm.op[1].ar = reader.readC(); + ins->fm.op[1].sl = reader.readC(); + ins->fm.op[1].ksr = reader.readC(); + ins->fm.op[1].dr = reader.readC(); + ins->fm.op[1].rr = reader.readC(); + ins->fm.op[1].tl = reader.readC(); + ins->fm.op[1].am = reader.readC(); + ins->fm.op[1].vib = reader.readC(); + ins->fm.op[1].ksr = reader.readC(); + reader.readC(); // skip + + ins->fm.op[0].ws = reader.readC(); + ins->fm.op[1].ws = reader.readC(); + ins->name = instNames[i].length() > 0 ? instNames[i] : fmt::format("{0}[{1}]", stripPath, i); + + ret.push_back(insList[i]); + } catch (EndOfFileException& e) { + // Reached end of BNK data + delete insList[i]; + break; + } + } + } catch (EndOfFileException& e) { + lastError = "premature end of file"; + logE("premature end of file!\n"); + for (int i = readCount; i >= 0; --i) { + delete insList[i]; + } + return; + } + + } else { + // assume GEMS BNK for now. + lastError = "GEMS BNK currently not supported.\n"; + logE("GEMS BNK currently not supported.\n"); + } } @@ -920,7 +956,7 @@ std::vector DivEngine::instrumentFromFile(const char* path) { format=DIV_INSFORMAT_S3I; } else if (extS==String(".sbi")) { format=DIV_INSFORMAT_SBI; - } else if (ext5==String(".bnk")) { + } else if (extS==String(".bnk")) { format=DIV_INSFORMAT_BNK; } else if (extS==String(".opm")) { format=DIV_INSFORMAT_OPM; From c431add35b4b42a4b14fc2e999b8178c973397ff Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Fri, 15 Apr 2022 00:42:05 +1000 Subject: [PATCH 04/10] BNK - Fix sustain flag --- src/engine/fileOpsIns.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 7d2dda37..1338b796 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -694,7 +694,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St ins->fm.fb = reader.readC(); ins->fm.op[0].ar = reader.readC(); ins->fm.op[0].sl = reader.readC(); - ins->fm.op[0].ksr = reader.readC(); + ins->fm.op[0].sus = (reader.readC() != 0) ? 1 : 0; ins->fm.op[0].dr = reader.readC(); ins->fm.op[0].rr = reader.readC(); ins->fm.op[0].tl = reader.readC(); @@ -708,7 +708,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St reader.readC(); // skip ins->fm.op[1].ar = reader.readC(); ins->fm.op[1].sl = reader.readC(); - ins->fm.op[1].ksr = reader.readC(); + ins->fm.op[1].sus = (reader.readC() != 0) ? 1 : 0; ins->fm.op[1].dr = reader.readC(); ins->fm.op[1].rr = reader.readC(); ins->fm.op[1].tl = reader.readC(); From 1985546d0b23059aaca88b5e3018c1d98e033c9b Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Fri, 15 Apr 2022 00:46:26 +1000 Subject: [PATCH 05/10] Add file extension to picker --- src/gui/gui.cpp | 4 ++-- src/gui/settings.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ba83b865..bdee5656 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1303,9 +1303,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Instrument", - {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.ff", + {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.bnk *.ff", "all files", ".*"}, - "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.ff},.*", + "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.bnk,.ff},.*", workingDirIns, dpiScale ); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index f4d62a81..d9495f97 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2437,6 +2437,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bnk",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ff",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); From c3b393004d4915187b23cd87e258bc7bd80db05a Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Fri, 15 Apr 2022 00:57:24 +1000 Subject: [PATCH 06/10] Fix for -Werror o/ o/ o/ --- buildme.bat | 6 ++++++ src/engine/fileOpsIns.cpp | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 buildme.bat diff --git a/buildme.bat b/buildme.bat new file mode 100644 index 00000000..239d4fe0 --- /dev/null +++ b/buildme.bat @@ -0,0 +1,6 @@ +@echo off +git pull +git submodule update --init --recursive +cmake -Bbuild +cmake --build build --target ALL_BUILD --config Release --parallel 12 +copy build\Release\furnace.exe \Apps\Audio\Furnace\ diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 1338b796..4556e5a8 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -656,9 +656,9 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St try { // Seek to BNK patch names reader.seek(0x0c, SEEK_SET); - int name_offset = reader.readI(); + uint32_t name_offset = reader.readI(); reader.seek(0x10, SEEK_SET); - int data_offset = reader.readI(); + uint32_t data_offset = reader.readI(); reader.seek(name_offset, SEEK_SET); @@ -990,6 +990,7 @@ std::vector DivEngine::instrumentFromFile(const char* path) { break; case DIV_INSFORMAT_BNK: loadBNK(reader, ret, stripPath); + break; case DIV_INSFORMAT_FF: loadFF(reader,ret,stripPath); break; From 2dc80e8d8ed859f1fa046b21b1faf498eed3940a Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Fri, 15 Apr 2022 01:07:11 +1000 Subject: [PATCH 07/10] Batch file wasn't supposed to be there --- buildme.bat | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 buildme.bat diff --git a/buildme.bat b/buildme.bat deleted file mode 100644 index 239d4fe0..00000000 --- a/buildme.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -git pull -git submodule update --init --recursive -cmake -Bbuild -cmake --build build --target ALL_BUILD --config Release --parallel 12 -copy build\Release\furnace.exe \Apps\Audio\Furnace\ From c3ca175e4635b683975416746fa6e06e6af044f9 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Fri, 15 Apr 2022 11:57:57 +1000 Subject: [PATCH 08/10] Nitpicky cruft cleanup. --- src/engine/fileOpsIns.cpp | 125 +++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 63 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 4556e5a8..2391f2a1 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -448,7 +448,8 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St // Skip more stuff we don't need reader.seek(21, SEEK_CUR); } else { - logE("S3I PCM samples currently not supported."); + lastError = "S3I PCM samples currently not supported."; + logE("S3I PCM samples currently not supported.\n"); } ins->name = reader.readString(28); ins->name = (ins->name.length() == 0) ? stripPath : ins->name; @@ -456,7 +457,8 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St int s3i_signature = reader.readI(); if (s3i_signature != 0x49524353) { - logW("S3I signature invalid."); + lastError = "S3I signature invalid."; + logW("S3I signature invalid.\n"); }; } catch (EndOfFileException& e) { lastError = "premature end of file"; @@ -622,7 +624,8 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St // First distinguish between GEMS BNK and Adlib BNK uint64_t header = reader.readL(); bool is_adlib = ((header>>8) == 0x2d42494c444100L); - + int readCount = 0; + if (is_adlib) { // Caveat: Technically Adlib BNK can hold up to 0xFFFF instruments, // but Furnace only can handle up to 0xFF. @@ -644,90 +647,82 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St } bnkop_t; typedef struct { - uint8_t mode, // version - percVoice; // perc + uint8_t mode, + percVoice; bnkop_t op[2]; - uint8_t wave0, // wave op1 - wave1; // wave op2 + uint8_t wave0, + wave1; } bnktimbre_t; - int readCount = 0; - try { - // Seek to BNK patch names reader.seek(0x0c, SEEK_SET); uint32_t name_offset = reader.readI(); reader.seek(0x10, SEEK_SET); uint32_t data_offset = reader.readI(); + // Seek to BNK patch names reader.seek(name_offset, SEEK_SET); - - while (readCount < 256 && reader.tell() < data_offset) { + while (reader.tell() < data_offset) { + if (readCount >= 256) { + lastError = "BNK exceeds 256 presets. Only first 256 will be imported."; + logW("BNK exceeds 256 presets. Only first 256 will be imported.\n"); + break; + } reader.seek(3, SEEK_CUR); instNames[readCount] = reader.readString(9); ++readCount; } - if (readCount >= 256) { - logW("BNK exceeds 256 presets. Only first 256 will be imported.\n"); - } - // Seek to BNK data reader.seek(data_offset, SEEK_SET); // Read until EOF for (int i = 0; i < readCount && i < 256; ++i) { - try { - bnktimbre_t timbre; - insList[i] = new DivInstrument; - auto& ins = insList[i]; + bnktimbre_t timbre; + insList[i] = new DivInstrument; + auto& ins = insList[i]; - ins->type = DIV_INS_OPL; + ins->type = DIV_INS_OPL; - timbre.mode = reader.readC(); - timbre.percVoice = reader.readC(); - if (timbre.mode == 1) { - ins->fm.opllPreset = (uint8_t)(1<<4); - } - ins->fm.op[0].ksl = reader.readC(); - ins->fm.op[0].mult = reader.readC(); - ins->fm.fb = reader.readC(); - ins->fm.op[0].ar = reader.readC(); - ins->fm.op[0].sl = reader.readC(); - ins->fm.op[0].sus = (reader.readC() != 0) ? 1 : 0; - ins->fm.op[0].dr = reader.readC(); - ins->fm.op[0].rr = reader.readC(); - ins->fm.op[0].tl = reader.readC(); - ins->fm.op[0].am = reader.readC(); - ins->fm.op[0].vib = reader.readC(); - ins->fm.op[0].ksr = reader.readC(); - ins->fm.alg = (reader.readC() == 0) ? 1 : 0; - - ins->fm.op[1].ksl = reader.readC(); - ins->fm.op[1].mult = reader.readC(); - reader.readC(); // skip - ins->fm.op[1].ar = reader.readC(); - ins->fm.op[1].sl = reader.readC(); - ins->fm.op[1].sus = (reader.readC() != 0) ? 1 : 0; - ins->fm.op[1].dr = reader.readC(); - ins->fm.op[1].rr = reader.readC(); - ins->fm.op[1].tl = reader.readC(); - ins->fm.op[1].am = reader.readC(); - ins->fm.op[1].vib = reader.readC(); - ins->fm.op[1].ksr = reader.readC(); - reader.readC(); // skip - - ins->fm.op[0].ws = reader.readC(); - ins->fm.op[1].ws = reader.readC(); - ins->name = instNames[i].length() > 0 ? instNames[i] : fmt::format("{0}[{1}]", stripPath, i); - - ret.push_back(insList[i]); - } catch (EndOfFileException& e) { - // Reached end of BNK data - delete insList[i]; - break; + timbre.mode = reader.readC(); + timbre.percVoice = reader.readC(); + if (timbre.mode == 1) { + ins->fm.opllPreset = (uint8_t)(1<<4); } + ins->fm.op[0].ksl = reader.readC(); + ins->fm.op[0].mult = reader.readC(); + ins->fm.fb = reader.readC(); + ins->fm.op[0].ar = reader.readC(); + ins->fm.op[0].sl = reader.readC(); + ins->fm.op[0].sus = (reader.readC() != 0) ? 1 : 0; + ins->fm.op[0].dr = reader.readC(); + ins->fm.op[0].rr = reader.readC(); + ins->fm.op[0].tl = reader.readC(); + ins->fm.op[0].am = reader.readC(); + ins->fm.op[0].vib = reader.readC(); + ins->fm.op[0].ksr = reader.readC(); + ins->fm.alg = (reader.readC() == 0) ? 1 : 0; + + ins->fm.op[1].ksl = reader.readC(); + ins->fm.op[1].mult = reader.readC(); + reader.readC(); // skip + ins->fm.op[1].ar = reader.readC(); + ins->fm.op[1].sl = reader.readC(); + ins->fm.op[1].sus = (reader.readC() != 0) ? 1 : 0; + ins->fm.op[1].dr = reader.readC(); + ins->fm.op[1].rr = reader.readC(); + ins->fm.op[1].tl = reader.readC(); + ins->fm.op[1].am = reader.readC(); + ins->fm.op[1].vib = reader.readC(); + ins->fm.op[1].ksr = reader.readC(); + reader.readC(); // skip + + ins->fm.op[0].ws = reader.readC(); + ins->fm.op[1].ws = reader.readC(); + ins->name = instNames[i].length() > 0 ? instNames[i] : fmt::format("{0}[{1}]", stripPath, i); } + reader.seek(0, SEEK_END); + } catch (EndOfFileException& e) { lastError = "premature end of file"; logE("premature end of file!\n"); @@ -742,6 +737,10 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St lastError = "GEMS BNK currently not supported.\n"; logE("GEMS BNK currently not supported.\n"); } + + for (int i = 0; i < readCount; ++i) { + ret.push_back(insList[i]); + } } From a8201fa535174df56e80d805101fc715e349ef1d Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sat, 16 Apr 2022 09:34:12 +1000 Subject: [PATCH 09/10] Address review comments --- src/engine/fileOpsIns.cpp | 242 ++++++++++++++++++-------------------- 1 file changed, 117 insertions(+), 125 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 2391f2a1..b1e5b397 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -35,6 +35,61 @@ enum DivInsFormats { DIV_INSFORMAT_FF, }; +// Patch data structures + +// SBI and some other OPL containers +struct sbi_t { + uint8_t Mcharacteristics, + Ccharacteristics, + Mscaling_output, + Cscaling_output, + Meg_AD, + Ceg_AD, + Meg_SR, + Ceg_SR, + Mwave, + Cwave, + FeedConnect; +}; + +// Adlib Visual Composer BNK +struct bnkop_t { + uint8_t ksl, + multiple, + feedback, // op1 only + attack, + sustain, + eg, + decay, + releaseRate, + totalLevel, + am, + vib, + ksr, + con; // op1 only +}; +struct bnktimbre_t { + uint8_t mode, + percVoice; + bnkop_t op[2]; + uint8_t wave0, + wave1; +}; + +auto readSbiOpData = [](sbi_t& sbi, SafeReader& reader) { + sbi.Mcharacteristics = reader.readC(); + sbi.Ccharacteristics = reader.readC(); + sbi.Mscaling_output = reader.readC(); + sbi.Cscaling_output = reader.readC(); + sbi.Meg_AD = reader.readC(); + sbi.Ceg_AD = reader.readC(); + sbi.Meg_SR = reader.readC(); + sbi.Ceg_SR = reader.readC(); + sbi.Mwave = reader.readC(); + sbi.Cwave = reader.readC(); + sbi.FeedConnect = reader.readC(); +}; + void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, String& stripPath) { DivInstrument* ins=new DivInstrument; // this is a ridiculous mess @@ -46,7 +101,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St logD(".dmp version %d",version); } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; return; } @@ -105,7 +160,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; return; } @@ -297,7 +352,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; return; } @@ -332,7 +387,7 @@ void DivEngine::loadTFI(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; return; } @@ -374,7 +429,7 @@ void DivEngine::loadVGI(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; return; } @@ -400,56 +455,48 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St // skip reserved bytes reader.seek(3, SEEK_CUR); - // 12-byte opl value - uint8_t s3i_Mcharacteristics = reader.readC(); - uint8_t s3i_Ccharacteristics = reader.readC(); - uint8_t s3i_Mscaling_output = reader.readC(); - uint8_t s3i_Cscaling_output = reader.readC(); - uint8_t s3i_Meg_AD = reader.readC(); - uint8_t s3i_Ceg_AD = reader.readC(); - uint8_t s3i_Meg_SR = reader.readC(); - uint8_t s3i_Ceg_SR = reader.readC(); - uint8_t s3i_Mwave = reader.readC(); - uint8_t s3i_Cwave = reader.readC(); - uint8_t s3i_FeedConnect = reader.readC(); + // 12-byte opl value - identical to SBI format + sbi_t s3i; + + readSbiOpData(s3i, reader); DivInstrumentFM::Operator& opM = ins->fm.op[0]; DivInstrumentFM::Operator& opC = ins->fm.op[1]; ins->fm.ops = 2; - opM.mult = s3i_Mcharacteristics & 0xF; - opM.ksr = ((s3i_Mcharacteristics >> 4) & 0x1); - opM.sus = ((s3i_Mcharacteristics >> 5) & 0x1); - opM.vib = ((s3i_Mcharacteristics >> 6) & 0x1); - opM.am = ((s3i_Mcharacteristics >> 7) & 0x1); - opM.tl = s3i_Mscaling_output & 0x3F; - opM.ksl = ((s3i_Mscaling_output >> 6) & 0x3); - opM.ar = ((s3i_Meg_AD >> 4) & 0xF); - opM.dr = (s3i_Meg_AD & 0xF); - opM.rr = (s3i_Meg_SR & 0xF); - opM.sl = ((s3i_Meg_SR >> 4) & 0xF); - opM.ws = s3i_Mwave; + opM.mult = s3i.Mcharacteristics & 0xF; + opM.ksr = ((s3i.Mcharacteristics >> 4) & 0x1); + opM.sus = ((s3i.Mcharacteristics >> 5) & 0x1); + opM.vib = ((s3i.Mcharacteristics >> 6) & 0x1); + opM.am = ((s3i.Mcharacteristics >> 7) & 0x1); + opM.tl = s3i.Mscaling_output & 0x3F; + opM.ksl = ((s3i.Mscaling_output >> 6) & 0x3); + opM.ar = ((s3i.Meg_AD >> 4) & 0xF); + opM.dr = (s3i.Meg_AD & 0xF); + opM.rr = (s3i.Meg_SR & 0xF); + opM.sl = ((s3i.Meg_SR >> 4) & 0xF); + opM.ws = s3i.Mwave; - ins->fm.alg = (s3i_FeedConnect & 0x1); - ins->fm.fb = ((s3i_FeedConnect >> 1) & 0x7); + ins->fm.alg = (s3i.FeedConnect & 0x1); + ins->fm.fb = ((s3i.FeedConnect >> 1) & 0x7); - opC.mult = s3i_Ccharacteristics & 0xF; - opC.ksr = ((s3i_Ccharacteristics >> 4) & 0x1); - opC.sus = ((s3i_Ccharacteristics >> 5) & 0x1); - opC.vib = ((s3i_Ccharacteristics >> 6) & 0x1); - opC.am = ((s3i_Ccharacteristics >> 7) & 0x1); - opC.tl = s3i_Cscaling_output & 0x3F; - opC.ksl = ((s3i_Cscaling_output >> 6) & 0x3); - opC.ar = ((s3i_Ceg_AD >> 4) & 0xF); - opC.dr = (s3i_Ceg_AD & 0xF); - opC.rr = (s3i_Ceg_SR & 0xF); - opC.sl = ((s3i_Ceg_SR >> 4) & 0xF); - opC.ws = s3i_Cwave; + opC.mult = s3i.Ccharacteristics & 0xF; + opC.ksr = ((s3i.Ccharacteristics >> 4) & 0x1); + opC.sus = ((s3i.Ccharacteristics >> 5) & 0x1); + opC.vib = ((s3i.Ccharacteristics >> 6) & 0x1); + opC.am = ((s3i.Ccharacteristics >> 7) & 0x1); + opC.tl = s3i.Cscaling_output & 0x3F; + opC.ksl = ((s3i.Cscaling_output >> 6) & 0x3); + opC.ar = ((s3i.Ceg_AD >> 4) & 0xF); + opC.dr = (s3i.Ceg_AD & 0xF); + opC.rr = (s3i.Ceg_SR & 0xF); + opC.sl = ((s3i.Ceg_SR >> 4) & 0xF); + opC.ws = s3i.Cwave; // Skip more stuff we don't need reader.seek(21, SEEK_CUR); } else { lastError = "S3I PCM samples currently not supported."; - logE("S3I PCM samples currently not supported.\n"); + logE(lastError.c_str()); } ins->name = reader.readString(28); ins->name = (ins->name.length() == 0) ? stripPath : ins->name; @@ -457,12 +504,12 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St int s3i_signature = reader.readI(); if (s3i_signature != 0x49524353) { - lastError = "S3I signature invalid."; - logW("S3I signature invalid.\n"); + warnings = "S3I signature invalid."; + logW(lastError.c_str()); }; } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; return; } @@ -486,34 +533,6 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St String patchName = reader.readString(32); patchName = (patchName.length() == 0) ? stripPath : patchName; - typedef struct { - uint8_t Mcharacteristics, - Ccharacteristics, - Mscaling_output, - Cscaling_output, - Meg_AD, - Ceg_AD, - Meg_SR, - Ceg_SR, - Mwave, - Cwave, - FeedConnect; - } sbi_t; - - auto readSbiOpData = [](sbi_t& sbi, SafeReader& reader) { - sbi.Mcharacteristics = reader.readC(); - sbi.Ccharacteristics = reader.readC(); - sbi.Mscaling_output = reader.readC(); - sbi.Cscaling_output = reader.readC(); - sbi.Meg_AD = reader.readC(); - sbi.Ceg_AD = reader.readC(); - sbi.Meg_SR = reader.readC(); - sbi.Ceg_SR = reader.readC(); - sbi.Mwave = reader.readC(); - sbi.Cwave = reader.readC(); - sbi.FeedConnect = reader.readC(); - }; - auto writeOp = [](sbi_t& sbi, DivInstrumentFM::Operator& opM, DivInstrumentFM::Operator& opC) { opM.mult = sbi.Mcharacteristics & 0xF; opM.ksr = ((sbi.Mcharacteristics >> 4) & 0x1); @@ -611,49 +630,22 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; } } void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, String& stripPath) { - DivInstrument* insList[256]; - String instNames[256]; - memset(insList, 0, 256 * sizeof(void*)); + std::vector insList; + std::vector instNames; reader.seek(0, SEEK_SET); // First distinguish between GEMS BNK and Adlib BNK uint64_t header = reader.readL(); bool is_adlib = ((header>>8) == 0x2d42494c444100L); + bool is_failed = false; int readCount = 0; if (is_adlib) { - // Caveat: Technically Adlib BNK can hold up to 0xFFFF instruments, - // but Furnace only can handle up to 0xFF. - - typedef struct { - uint8_t ksl, - multiple, - feedback, // op1 only - attack, - sustain, - eg, - decay, - releaseRate, - totalLevel, - am, - vib, - ksr, - con; // op1 only - } bnkop_t; - - typedef struct { - uint8_t mode, - percVoice; - bnkop_t op[2]; - uint8_t wave0, - wave1; - } bnktimbre_t; - try { reader.seek(0x0c, SEEK_SET); uint32_t name_offset = reader.readI(); @@ -663,13 +655,8 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St // Seek to BNK patch names reader.seek(name_offset, SEEK_SET); while (reader.tell() < data_offset) { - if (readCount >= 256) { - lastError = "BNK exceeds 256 presets. Only first 256 will be imported."; - logW("BNK exceeds 256 presets. Only first 256 will be imported.\n"); - break; - } reader.seek(3, SEEK_CUR); - instNames[readCount] = reader.readString(9); + instNames.push_back(new String(reader.readString(9))); ++readCount; } @@ -677,9 +664,9 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St reader.seek(data_offset, SEEK_SET); // Read until EOF - for (int i = 0; i < readCount && i < 256; ++i) { + for (int i = 0; i < readCount; ++i) { bnktimbre_t timbre; - insList[i] = new DivInstrument; + insList.push_back(new DivInstrument); auto& ins = insList[i]; ins->type = DIV_INS_OPL; @@ -719,31 +706,36 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St ins->fm.op[0].ws = reader.readC(); ins->fm.op[1].ws = reader.readC(); - ins->name = instNames[i].length() > 0 ? instNames[i] : fmt::format("{0}[{1}]", stripPath, i); + ins->name = instNames[i]->length() > 0 ? (*instNames[i]) : fmt::format("{0}[{1}]", stripPath, i); } reader.seek(0, SEEK_END); } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE("premature end of file!\n"); + logE(lastError.c_str()); for (int i = readCount; i >= 0; --i) { delete insList[i]; } - return; + is_failed = true; } } else { // assume GEMS BNK for now. - lastError = "GEMS BNK currently not supported.\n"; - logE("GEMS BNK currently not supported.\n"); + lastError = "GEMS BNK currently not supported."; + logE(lastError.c_str()); } - for (int i = 0; i < readCount; ++i) { - ret.push_back(insList[i]); + if (!is_failed) { + for (int i = 0; i < readCount; ++i) { + ret.push_back(insList[i]); + } + } + + for (auto& name : instNames) { + delete name; } } - void DivEngine::loadFF(SafeReader& reader, std::vector& ret, String& stripPath) { DivInstrument* insList[256]; memset(insList,0,256*sizeof(void*)); @@ -805,7 +797,7 @@ void DivEngine::loadFF(SafeReader& reader, std::vector& ret, Str } } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE("premature end of file!\n"); + logE(lastError.c_str()); for (int i = readCount; i >= 0; --i) { delete insList[i]; } @@ -826,7 +818,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); return; } } @@ -924,7 +916,7 @@ std::vector DivEngine::instrumentFromFile(const char* path) { } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; delete[] buf; return ret; From d1fadf1076e61b57139c750f2df7972a098691c5 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 17 Apr 2022 15:54:00 +1000 Subject: [PATCH 10/10] Address review comments take 2 --- src/engine/fileOpsIns.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index b1e5b397..f9eaab38 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -101,7 +101,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St logD(".dmp version %d",version); } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; return; } @@ -160,7 +160,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; return; } @@ -352,7 +352,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; return; } @@ -387,7 +387,7 @@ void DivEngine::loadTFI(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; return; } @@ -429,7 +429,7 @@ void DivEngine::loadVGI(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; return; } @@ -496,7 +496,7 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St reader.seek(21, SEEK_CUR); } else { lastError = "S3I PCM samples currently not supported."; - logE(lastError.c_str()); + logE("S3I PCM samples currently not supported."); } ins->name = reader.readString(28); ins->name = (ins->name.length() == 0) ? stripPath : ins->name; @@ -504,12 +504,12 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St int s3i_signature = reader.readI(); if (s3i_signature != 0x49524353) { - warnings = "S3I signature invalid."; - logW(lastError.c_str()); + addWarning("S3I signature invalid."); + logW("S3I signature invalid."); }; } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; return; } @@ -630,7 +630,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; } } @@ -712,7 +712,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); for (int i = readCount; i >= 0; --i) { delete insList[i]; } @@ -722,7 +722,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St } else { // assume GEMS BNK for now. lastError = "GEMS BNK currently not supported."; - logE(lastError.c_str()); + logE("GEMS BNK currently not supported."); } if (!is_failed) { @@ -797,7 +797,7 @@ void DivEngine::loadFF(SafeReader& reader, std::vector& ret, Str } } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); for (int i = readCount; i >= 0; --i) { delete insList[i]; } @@ -818,7 +818,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); return; } } @@ -916,7 +916,7 @@ std::vector DivEngine::instrumentFromFile(const char* path) { } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; delete[] buf; return ret;