do I have amnesia or what
This commit is contained in:
		
						commit
						27698892d2
					
				|  | @ -287,6 +287,7 @@ class DivEngine { | |||
|   void loadVGI(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 loadBNK(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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,10 +30,66 @@ enum DivInsFormats { | |||
|   DIV_INSFORMAT_BTI, | ||||
|   DIV_INSFORMAT_S3I, | ||||
|   DIV_INSFORMAT_SBI, | ||||
|   DIV_INSFORMAT_BNK, | ||||
|   DIV_INSFORMAT_OPM, | ||||
|   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<DivInstrument*>& ret, String& stripPath) { | ||||
|   DivInstrument* ins=new DivInstrument; | ||||
|   // this is a ridiculous mess
 | ||||
|  | @ -45,7 +101,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St | |||
|     logD(".dmp version %d",version); | ||||
|   } catch (EndOfFileException& e) { | ||||
|     lastError="premature end of file"; | ||||
|     logE("premature end of file!"); | ||||
|     logE("premature end of file"); | ||||
|     delete ins; | ||||
|     return; | ||||
|   } | ||||
|  | @ -104,7 +160,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St | |||
|       } | ||||
|     } catch (EndOfFileException& e) { | ||||
|       lastError="premature end of file"; | ||||
|       logE("premature end of file!"); | ||||
|       logE("premature end of file"); | ||||
|       delete ins; | ||||
|       return; | ||||
|     } | ||||
|  | @ -296,7 +352,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St | |||
|     } | ||||
|   } catch (EndOfFileException& e) { | ||||
|     lastError="premature end of file"; | ||||
|     logE("premature end of file!"); | ||||
|     logE("premature end of file"); | ||||
|     delete ins; | ||||
|     return; | ||||
|   } | ||||
|  | @ -331,7 +387,7 @@ void DivEngine::loadTFI(SafeReader& reader, std::vector<DivInstrument*>& ret, St | |||
|     } | ||||
|   } catch (EndOfFileException& e) { | ||||
|     lastError="premature end of file"; | ||||
|     logE("premature end of file!"); | ||||
|     logE("premature end of file"); | ||||
|     delete ins; | ||||
|     return; | ||||
|   } | ||||
|  | @ -373,7 +429,7 @@ void DivEngine::loadVGI(SafeReader& reader, std::vector<DivInstrument*>& ret, St | |||
|     } | ||||
|   } catch (EndOfFileException& e) { | ||||
|     lastError="premature end of file"; | ||||
|     logE("premature end of file!"); | ||||
|     logE("premature end of file"); | ||||
|     delete ins; | ||||
|     return; | ||||
|   } | ||||
|  | @ -390,71 +446,70 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector<DivInstrument*>& 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); | ||||
| 
 | ||||
|       // 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."); | ||||
|     } | ||||
|     ins->name = reader.readString(28); | ||||
|     ins->name = (ins->name.length() == 0) ? stripPath : ins->name; | ||||
| 
 | ||||
|     int s3i_signature = reader.readI(); | ||||
| 
 | ||||
|     if (s3i_signature != 0x49524353) { | ||||
|       addWarning("S3I signature invalid."); | ||||
|       logW("S3I signature invalid."); | ||||
|     }; | ||||
|   } catch (EndOfFileException& e) { | ||||
|     lastError = "premature end of file"; | ||||
|     logE("premature end of file!"); | ||||
|     logE("premature end of file"); | ||||
|     delete ins; | ||||
|     return; | ||||
|   } | ||||
|  | @ -470,77 +525,69 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector<DivInstrument*>& 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(); | ||||
|     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(); | ||||
|     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; | ||||
| 
 | ||||
|     // 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; | ||||
|       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; | ||||
|       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->name = patchName; | ||||
|       writeOp(sbi_op12, opM, opC); | ||||
|       ins->fm.alg = (sbi_op12.FeedConnect & 0x1); | ||||
|       ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7); | ||||
| 
 | ||||
|       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); | ||||
| 
 | ||||
|     if (is_4op || is_6op) { | ||||
|     } 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.
 | ||||
|  | @ -549,87 +596,144 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector<DivInstrument*>& ret, St | |||
|       DivInstrumentFM::Operator& opM4 = ins->fm.op[1]; | ||||
|       DivInstrumentFM::Operator& opC4 = ins->fm.op[3]; | ||||
|       ins->fm.ops = 4; | ||||
|       ins->name = patchName; | ||||
|       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); | ||||
| 
 | ||||
|       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); | ||||
|       if (is_6op) { | ||||
|         // Freq Monster 801 6op SBIs use a 4+2op layout
 | ||||
|         // Save the 4op portion before reading the 2op part
 | ||||
|         ins->name = fmt::format("{0} (4op)", ins->name); | ||||
|         ret.push_back(ins); | ||||
| 
 | ||||
|       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; | ||||
|         readSbiOpData(sbi_op12, reader); | ||||
| 
 | ||||
|       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 = 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 = 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.
 | ||||
|       // 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); | ||||
|     } | ||||
| 
 | ||||
|   } catch (EndOfFileException& e) { | ||||
|     lastError = "premature end of file"; | ||||
|     logE("premature end of file!"); | ||||
|     logE("premature end of file"); | ||||
|     delete ins; | ||||
|     return; | ||||
|   } | ||||
| } | ||||
| void DivEngine::loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) { | ||||
|   std::vector<DivInstrument*> insList; | ||||
|   std::vector<String*> 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) { | ||||
|     try { | ||||
|       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 (reader.tell() < data_offset) { | ||||
|         reader.seek(3, SEEK_CUR); | ||||
|         instNames.push_back(new String(reader.readString(9))); | ||||
|         ++readCount; | ||||
|       } | ||||
| 
 | ||||
|       // Seek to BNK data
 | ||||
|       reader.seek(data_offset, SEEK_SET); | ||||
| 
 | ||||
|       // Read until EOF
 | ||||
|       for (int i = 0; i < readCount; ++i) { | ||||
|         bnktimbre_t timbre; | ||||
|         insList.push_back(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].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"); | ||||
|       for (int i = readCount; i >= 0; --i) { | ||||
|         delete insList[i]; | ||||
|       } | ||||
|       is_failed = true; | ||||
|     } | ||||
| 
 | ||||
|   } else { | ||||
|     // assume GEMS BNK for now.
 | ||||
|     lastError = "GEMS BNK currently not supported."; | ||||
|     logE("GEMS BNK currently not supported."); | ||||
|   } | ||||
| 
 | ||||
|   ret.push_back(ins); | ||||
|   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<DivInstrument*>& ret, String& stripPath) { | ||||
|  | @ -693,7 +797,7 @@ void DivEngine::loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, Str | |||
|     } | ||||
|   } catch (EndOfFileException& e) { | ||||
|     lastError = "premature end of file"; | ||||
|     logE("premature end of file!\n"); | ||||
|     logE("premature end of file"); | ||||
|     for (int i = readCount; i >= 0; --i) { | ||||
|       delete insList[i]; | ||||
|     } | ||||
|  | @ -714,7 +818,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, St | |||
|          | ||||
|   } catch (EndOfFileException& e) { | ||||
|     lastError="premature end of file"; | ||||
|     logE("premature end of file!"); | ||||
|     logE("premature end of file"); | ||||
|     return; | ||||
|   } | ||||
| } | ||||
|  | @ -812,7 +916,7 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) { | |||
|       } | ||||
|     } catch (EndOfFileException& e) { | ||||
|       lastError="premature end of file"; | ||||
|       logE("premature end of file!"); | ||||
|       logE("premature end of file"); | ||||
|       delete ins; | ||||
|       delete[] buf; | ||||
|       return ret; | ||||
|  | @ -843,6 +947,8 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) { | |||
|         format=DIV_INSFORMAT_S3I; | ||||
|       } else if (extS==String(".sbi")) { | ||||
|         format=DIV_INSFORMAT_SBI; | ||||
|       } else if (extS==String(".bnk")) { | ||||
|         format=DIV_INSFORMAT_BNK; | ||||
|       } else if (extS==String(".opm")) { | ||||
|         format=DIV_INSFORMAT_OPM; | ||||
|       } else if (extS==String(".ff")) { | ||||
|  | @ -873,6 +979,9 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) { | |||
|       case DIV_INSFORMAT_SBI: | ||||
|         loadSBI(reader,ret,stripPath); | ||||
|         break; | ||||
|       case DIV_INSFORMAT_BNK: | ||||
|         loadBNK(reader, ret, stripPath); | ||||
|         break; | ||||
|       case DIV_INSFORMAT_FF: | ||||
|         loadFF(reader,ret,stripPath); | ||||
|         break; | ||||
|  |  | |||
|  | @ -713,10 +713,10 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { | |||
|   bool writeDACSamples=false; | ||||
|   bool writeNESSamples=false; | ||||
|   bool writePCESamples=false; | ||||
|   bool writeADPCM=false; | ||||
|   bool writeSegaPCM=false; | ||||
|   bool writeX1010=false; | ||||
|   bool writeQSound=false; | ||||
|   int writeADPCM=0; | ||||
|   int writeSegaPCM=0; | ||||
|   int writeX1010=0; | ||||
|   int writeQSound=0; | ||||
| 
 | ||||
|   for (int i=0; i<song.systemLen; i++) { | ||||
|     willExport[i]=false; | ||||
|  | @ -792,10 +792,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { | |||
|         if (!hasSegaPCM) { | ||||
|           hasSegaPCM=4000000; | ||||
|           willExport[i]=true; | ||||
|           writeSegaPCM=true; | ||||
|           writeSegaPCM=1; | ||||
|         } else if (!(hasSegaPCM&0x40000000)) { | ||||
|           isSecond[i]=true; | ||||
|           willExport[i]=true; | ||||
|           writeSegaPCM=2; | ||||
|           hasSegaPCM|=0x40000000; | ||||
|           howManyChips++; | ||||
|         } | ||||
|  | @ -804,10 +805,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { | |||
|         if (!hasX1) { | ||||
|           hasX1=disCont[i].dispatch->chipClock; | ||||
|           willExport[i]=true; | ||||
|           writeX1010=true; | ||||
|           writeX1010=1; | ||||
|         } else if (!(hasX1&0x40000000)) { | ||||
|           isSecond[i]=true; | ||||
|           willExport[i]=true; | ||||
|           writeX1010=2; | ||||
|           hasX1|=0x40000000; | ||||
|           howManyChips++; | ||||
|         } | ||||
|  | @ -821,10 +823,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { | |||
|         if (!hasOPNB) { | ||||
|           hasOPNB=disCont[i].dispatch->chipClock; | ||||
|           willExport[i]=true; | ||||
|           writeADPCM=true; | ||||
|           writeADPCM=1; | ||||
|         } else if (!(hasOPNB&0x40000000)) { | ||||
|           isSecond[i]=true; | ||||
|           willExport[i]=true; | ||||
|           writeADPCM=2; | ||||
|           hasOPNB|=0x40000000; | ||||
|           howManyChips++; | ||||
|         } | ||||
|  | @ -926,10 +929,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { | |||
|           // not be able to handle the 64kb sample bank trick
 | ||||
|           hasQSound=disCont[i].dispatch->chipClock; | ||||
|           willExport[i]=true; | ||||
|           writeQSound=true; | ||||
|           writeQSound=1; | ||||
|         } else if (!(hasQSound&0x40000000)) { | ||||
|           isSecond[i]=true; | ||||
|           willExport[i]=false; | ||||
|           writeQSound=2; | ||||
|           addWarning("dual QSound is not supported by the VGM format"); | ||||
|         } | ||||
|         break; | ||||
|  | @ -1200,7 +1204,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (writeSegaPCM) { | ||||
|   if (writeSegaPCM>0) { | ||||
|     unsigned char* pcmMem=new unsigned char[16777216]; | ||||
| 
 | ||||
|     size_t memPos=0; | ||||
|  | @ -1232,60 +1236,70 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { | |||
|       if (memPos>=16777216) break; | ||||
|     } | ||||
| 
 | ||||
|     w->writeC(0x67); | ||||
|     w->writeC(0x66); | ||||
|     w->writeC(0x80); | ||||
|     w->writeI(memPos+8); | ||||
|     w->writeI(memPos); | ||||
|     w->writeI(0); | ||||
|     w->write(pcmMem,memPos); | ||||
|     for (int i=0; i<writeSegaPCM; i++) { | ||||
|       w->writeC(0x67); | ||||
|       w->writeC(0x66); | ||||
|       w->writeC(0x80); | ||||
|       w->writeI((memPos+8)|(i*0x80000000)); | ||||
|       w->writeI(memPos); | ||||
|       w->writeI(0); | ||||
|       w->write(pcmMem,memPos); | ||||
|     } | ||||
| 
 | ||||
|     delete[] pcmMem; | ||||
|   } | ||||
| 
 | ||||
|   if (writeADPCM && adpcmAMemLen>0) { | ||||
|     w->writeC(0x67); | ||||
|     w->writeC(0x66); | ||||
|     w->writeC(0x82); | ||||
|     w->writeI(adpcmAMemLen+8); | ||||
|     w->writeI(adpcmAMemLen); | ||||
|     w->writeI(0); | ||||
|     w->write(adpcmAMem,adpcmAMemLen); | ||||
|   if (adpcmAMemLen>0) { | ||||
|     for (int i=0; i<writeADPCM; i++) { | ||||
|       w->writeC(0x67); | ||||
|       w->writeC(0x66); | ||||
|       w->writeC(0x82); | ||||
|       w->writeI((adpcmAMemLen+8)|(i*0x80000000)); | ||||
|       w->writeI(adpcmAMemLen); | ||||
|       w->writeI(0); | ||||
|       w->write(adpcmAMem,adpcmAMemLen); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (writeADPCM && adpcmBMemLen>0) { | ||||
|     w->writeC(0x67); | ||||
|     w->writeC(0x66); | ||||
|     w->writeC(0x83); | ||||
|     w->writeI(adpcmBMemLen+8); | ||||
|     w->writeI(adpcmBMemLen); | ||||
|     w->writeI(0); | ||||
|     w->write(adpcmBMem,adpcmBMemLen); | ||||
|   if (adpcmBMemLen>0) { | ||||
|     for (int i=0; i<writeADPCM; i++) { | ||||
|       w->writeC(0x67); | ||||
|       w->writeC(0x66); | ||||
|       w->writeC(0x83); | ||||
|       w->writeI((adpcmBMemLen+8)|(i*0x80000000)); | ||||
|       w->writeI(adpcmBMemLen); | ||||
|       w->writeI(0); | ||||
|       w->write(adpcmBMem,adpcmBMemLen); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (writeQSound && qsoundMemLen>0) { | ||||
|   if (qsoundMemLen>0) { | ||||
|     // always write a whole bank
 | ||||
|     unsigned int blockSize=(qsoundMemLen+0xffff)&(~0xffff); | ||||
|     if (blockSize > 0x1000000) { | ||||
|       blockSize = 0x1000000; | ||||
|     } | ||||
|     w->writeC(0x67); | ||||
|     w->writeC(0x66); | ||||
|     w->writeC(0x8F); | ||||
|     w->writeI(blockSize+8); | ||||
|     w->writeI(0x1000000); | ||||
|     w->writeI(0); | ||||
|     w->write(qsoundMem,blockSize); | ||||
|     for (int i=0; i<writeQSound; i++) { | ||||
|       w->writeC(0x67); | ||||
|       w->writeC(0x66); | ||||
|       w->writeC(0x8F); | ||||
|       w->writeI((blockSize+8)|(i*0x80000000)); | ||||
|       w->writeI(0x1000000); | ||||
|       w->writeI(0); | ||||
|       w->write(qsoundMem,blockSize); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (writeX1010 && x1_010MemLen>0) { | ||||
|     w->writeC(0x67); | ||||
|     w->writeC(0x66); | ||||
|     w->writeC(0x91); | ||||
|     w->writeI(x1_010MemLen+8); | ||||
|     w->writeI(x1_010MemLen); | ||||
|     w->writeI(0); | ||||
|     w->write(x1_010Mem,x1_010MemLen); | ||||
|   if (x1_010MemLen>0) { | ||||
|     for (int i=0; i<writeX1010; i++) { | ||||
|       w->writeC(0x67); | ||||
|       w->writeC(0x66); | ||||
|       w->writeC(0x91); | ||||
|       w->writeI((x1_010MemLen+8)|(i*0x80000000)); | ||||
|       w->writeI(x1_010MemLen); | ||||
|       w->writeI(0); | ||||
|       w->write(x1_010Mem,x1_010MemLen); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // initialize streams
 | ||||
|  |  | |||
|  | @ -1235,9 +1235,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 | ||||
|       ); | ||||
|  |  | |||
|  | @ -2476,6 +2476,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); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 tildearrow
						tildearrow