diff options
Diffstat (limited to 'utils/gen_adldata')
-rw-r--r-- | utils/gen_adldata/CMakeLists.txt | 25 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/common.h | 28 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/load_ail.h | 179 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/load_bisqwit.h | 54 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/load_bnk.h | 49 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/load_bnk2.h | 61 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/load_ea.h | 57 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/load_ibk.h | 52 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/load_jv.h | 75 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/load_op2.h | 117 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/load_tmb.h | 44 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/load_wopl.h | 130 | ||||
-rw-r--r-- | utils/gen_adldata/gen_adldata.cc | 381 | ||||
-rw-r--r-- | utils/gen_adldata/measurer.cpp | 568 | ||||
-rw-r--r-- | utils/gen_adldata/measurer.h | 20 | ||||
-rw-r--r-- | utils/gen_adldata/progs_cache.cpp | 452 | ||||
-rw-r--r-- | utils/gen_adldata/progs_cache.h | 357 |
17 files changed, 1505 insertions, 1144 deletions
diff --git a/utils/gen_adldata/CMakeLists.txt b/utils/gen_adldata/CMakeLists.txt index a3be516..7939d5b 100644 --- a/utils/gen_adldata/CMakeLists.txt +++ b/utils/gen_adldata/CMakeLists.txt @@ -19,6 +19,22 @@ list(APPEND GEN_ADLDATA_SRC ini/ini_processing.cpp ) +option(WITH_GENADLDATA_DEEPDEBUG "Enable deep debug mode of gen_adldata with dumping every voice into WAV file" OFF) +if(WITH_GENADLDATA_DEEPDEBUG) + add_definitions(-DGEN_ADLDATA_DEEP_DEBUG) + list(APPEND GEN_ADLDATA_SRC + ../midiplay/wave_writer.c + ) + file(GLOB WAV_FILES ${libADLMIDI_SOURCE_DIR}/fm_banks/_deep_debug/*.wav) + if(WAV_FILES) + file(REMOVE ${WAV_FILES}) + endif() + add_custom_target(gen_adldata_deepdebug_prepare ALL + COMMAND ${CMAKE_COMMAND} -E make_directory ${libADLMIDI_SOURCE_DIR}/fm_banks/_deep_debug + COMMAND ${CMAKE_COMMAND} -E remove ${libADLMIDI_SOURCE_DIR}/fm_banks/adldata-cache-x.dat + ) +endif() + if(USE_DOSBOX_EMULATOR) set(HAS_EMULATOR TRUE) list(APPEND GEN_ADLDATA_SRC @@ -53,6 +69,15 @@ endif() set_nopie(gen_adldata) +if(WITH_GENADLDATA_DEEPDEBUG) + add_dependencies(gen_adldata gen_adldata_deepdebug_prepare) +endif() + +option(WITH_GENADLDATA_PROGRESS "Enable progress printing in gen_adldata" OFF) +if(WITH_GENADLDATA_PROGRESS OR WITH_GENADLDATA_DEEPDEBUG) + target_compile_options(gen_adldata PUBLIC "-DADL_GENDATA_PRINT_PROGRESS") +endif() + if(WITH_GENADLDATA_COMMENTS) target_compile_options(gen_adldata PUBLIC "-DADLDATA_WITH_COMMENTS") endif() diff --git a/utils/gen_adldata/file_formats/common.h b/utils/gen_adldata/file_formats/common.h index d06059e..644e7a6 100644 --- a/utils/gen_adldata/file_formats/common.h +++ b/utils/gen_adldata/file_formats/common.h @@ -1,3 +1,4 @@ +#pragma once #ifndef COMMON_H #define COMMON_H @@ -18,6 +19,14 @@ inline int16_t toSint16BE(const uint8_t *arr) return num; } +inline int16_t toSint16LE(const uint8_t *arr) +{ + int16_t num = *reinterpret_cast<const int8_t *>(&arr[1]); + num *= 1 << 8; + num |= static_cast<int16_t>(arr[0]) & 0x00FF; + return num; +} + inline uint16_t toUint16LE(const uint8_t *arr) { uint16_t num = arr[0]; @@ -25,4 +34,23 @@ inline uint16_t toUint16LE(const uint8_t *arr) return num; } +inline uint32_t toUint32LE(const uint8_t *arr) +{ + uint32_t num = arr[0]; + num |= (static_cast<uint32_t>(arr[1] << 8) & 0x0000FF00); + num |= (static_cast<uint32_t>(arr[2] << 16) & 0x00FF0000); + num |= (static_cast<uint32_t>(arr[3] << 24) & 0xFF000000); + return num; +} + +inline int32_t toSint32LE(const uint8_t *arr) +{ + int32_t num = *reinterpret_cast<const int8_t *>(&arr[3]); + num *= 1 << 24; + num |= (static_cast<int32_t>(arr[2]) << 16) & 0x00FF0000; + num |= (static_cast<int32_t>(arr[1]) << 8) & 0x0000FF00; + num |= static_cast<int32_t>(arr[0]) & 0x000000FF; + return num; +} + #endif // COMMON_H diff --git a/utils/gen_adldata/file_formats/load_ail.h b/utils/gen_adldata/file_formats/load_ail.h index 80ca8ad..d6fa014 100644 --- a/utils/gen_adldata/file_formats/load_ail.h +++ b/utils/gen_adldata/file_formats/load_ail.h @@ -3,17 +3,26 @@ #include "../progs_cache.h" #include "../midi_inst_list.h" +#include "common.h" -static bool LoadMiles(const char *fn, unsigned bank, const char *prefix) +struct GTL_Head // GTL file header entry structure { - #ifdef HARD_BANKS + uint8_t patch = 0; + uint8_t bank = 0; + uint32_t offset = 0; +}; + +bool BankFormats::LoadMiles(BanksDump &db, const char *fn, unsigned bank, + const std::string &bankTitle, const char *prefix) +{ +#ifdef HARD_BANKS writeIni("AIL", fn, prefix, bank, INI_Both); - #endif +#endif FILE *fp = std::fopen(fn, "rb"); if(!fp) return false; std::fseek(fp, 0, SEEK_END); - std::vector<unsigned char> data(size_t(std::ftell(fp))); + std::vector<uint8_t> data(size_t(std::ftell(fp))); std::rewind(fp); if(std::fread(&data[0], 1, data.size(), fp) != data.size()) { @@ -22,90 +31,152 @@ static bool LoadMiles(const char *fn, unsigned bank, const char *prefix) } std::fclose(fp); - for(unsigned a = 0; a < 2000; ++a) + GTL_Head head; + std::vector<GTL_Head> heads; + uint_fast8_t max_bank_number = 0; + heads.reserve(256); + uint8_t *data_pos = data.data(); + uint8_t *data_end = data.data() + data.size(); + do { - unsigned gm_patch = data[a * 6 + 0]; - unsigned gm_bank = data[a * 6 + 1]; - unsigned offset = *(unsigned *)&data[a * 6 + 2]; + if((data_end - data_pos) < 6) + return false; + + head.patch = data_pos[0]; + head.bank = data_pos[1]; + head.offset = toUint32LE(data_pos + 2); - if(gm_patch == 0xFF) + if((head.patch == 0xFF) || (head.bank == 0xFF)) break; - int gmno = gm_bank == 0x7F ? int(gm_patch + 0x80) : int(gm_patch); - int midi_index = gmno < 128 ? gmno - : gmno < 128 + 35 ? -1 - : gmno < 128 + 88 ? gmno - 35 - : -1; - unsigned length = data[offset] + data[offset + 1] * 256; - signed char notenum = ((signed char)data[offset + 2]); + if(head.patch > 127)//Patch ID is more than 127 + return false; + + if((head.bank != 0x7F) && (head.bank > max_bank_number) ) + max_bank_number = head.bank; - /*printf("%02X %02X %08X ", gmnumber,gmnumber2, offset); - for(unsigned b=0; b<length; ++b) + heads.push_back(head); + data_pos += 6; + } + while(data_pos < data_end); + + size_t bankDb = db.initBank(bank, bankTitle, BanksDump::BankEntry::SETUP_AIL); + + std::vector<BanksDump::MidiBank> bnkMelodic; + bnkMelodic.resize(max_bank_number + 1, BanksDump::MidiBank()); + BanksDump::MidiBank bnkPercussion; + + { + uint8_t bank_lsb_counter = 0; + uint8_t bank_msb_counter = 0; + for(BanksDump::MidiBank &b : bnkMelodic) { - if(b > 3 && (b-3)%11 == 0) printf("\n "); - printf("%02X ", data[offset+b]); + b.lsb = bank_lsb_counter++; + b.msb = bank_msb_counter; + if(bank_lsb_counter == 0) + bank_msb_counter++; } - printf("\n");*/ + } - if(gm_bank != 0 && gm_bank != 0x7F) - continue; + uint32_t totalInsts = static_cast<uint32_t>(heads.size()); + for(uint32_t i = 0; i < totalInsts; i++) + { + GTL_Head &h = heads[i]; + bool isPerc = (h.bank == 0x7F); + // int gmPatchId = isPerc ? h.patch : (h.patch + (h.bank * 128)); + unsigned offset = h.offset; + + BanksDump::MidiBank &bnk = isPerc ? bnkPercussion : bnkMelodic[h.bank]; + + int gmno = isPerc ? int(h.patch + 0x80) : int(h.patch); +// int midi_index = gmno < 128 ? gmno +// : gmno < 128 + 35 ? -1 +// : gmno < 128 + 88 ? gmno - 35 +// : -1; + + unsigned length = data[offset] + data[offset + 1] * 256; + signed char notenum = ((signed char)data[offset + 2]); + + BanksDump::InstrumentEntry inst; + BanksDump::Operator ops[5]; char name2[512]; sprintf(name2, "%s%c%u", prefix, (gmno < 128 ? 'M' : 'P'), gmno & 127); - insdata tmp[200]; + InstBuffer tmp[200]; const unsigned inscount = (length - 3) / 11; - bool twoOp = (inscount == 1); +// bool twoOp = (inscount == 1); for(unsigned i = 0; i < inscount; ++i) { + if(i >= 2) + break; unsigned o = offset + 3 + i * 11; - tmp[i].finetune = (gmno < 128 && i == 0) ? notenum : 0; - tmp[i].diff = (i == 1); - tmp[i].data[0] = data[o + 0]; // 20 - tmp[i].data[8] = data[o + 1]; // 40 (vol) - tmp[i].data[2] = data[o + 2]; // 60 - tmp[i].data[4] = data[o + 3]; // 80 - tmp[i].data[6] = data[o + 4]; // E0 - tmp[i].data[1] = data[o + 6]; // 23 - tmp[i].data[9] = data[o + 7]; // 43 (vol) - tmp[i].data[3] = data[o + 8]; // 63 - tmp[i].data[5] = data[o + 9]; // 83 - tmp[i].data[7] = data[o + 10]; // E3 +// tmp[i].finetune = (gmno < 128 && i == 0) ? notenum : 0; +// tmp[i].diff = (i == 1); + + uint8_t temp[11] = {0}; + if(o < data.size()) + { + size_t count = data.size() - o; + count = (count > sizeof(temp)) ? sizeof(temp) : count; + std::memcpy(temp, &data[o], count); + } + + tmp[i].data[0] = temp[0]; // 20 + tmp[i].data[8] = temp[1]; // 40 (vol) + tmp[i].data[2] = temp[2]; // 60 + tmp[i].data[4] = temp[3]; // 80 + tmp[i].data[6] = temp[4]; // E0 + tmp[i].data[1] = temp[6]; // 23 + tmp[i].data[9] = temp[7]; // 43 (vol) + tmp[i].data[3] = temp[8]; // 63 + tmp[i].data[5] = temp[9]; // 83 + tmp[i].data[7] = temp[10]; // E3 unsigned fb_c = data[offset + 3 + 5]; tmp[i].data[10] = uint8_t(fb_c); if(i == 1) { + inst.instFlags |= BanksDump::InstrumentEntry::WOPL_Ins_4op; tmp[0].data[10] = fb_c & 0x0F; tmp[1].data[10] = uint8_t((fb_c & 0x0E) | (fb_c >> 7)); } + db.toOps(tmp[i].d, ops, i * 2); } if(inscount <= 2) { - struct ins tmp2; - tmp2.notenum = gmno < 128 ? 0 : (unsigned char)notenum; - tmp2.pseudo4op = false; - tmp2.real4op = (inscount > 1); - tmp2.voice2_fine_tune = 0.0; - tmp2.midi_velocity_offset = 0; - tmp2.rhythmModeDrum = 0; - std::string name; - if(midi_index >= 0) name = std::string(1, '\377') + MidiInsName[midi_index]; - size_t resno = InsertIns(tmp[0], tmp[1], tmp2, name, name2, twoOp); - SetBank(bank, (unsigned int)gmno, resno); +// struct ins tmp2; +// tmp2.notenum = gmno < 128 ? 0 : (unsigned char)notenum; +// tmp2.pseudo4op = false; +// tmp2.real4op = (inscount > 1); +// tmp2.voice2_fine_tune = 0.0; +// tmp2.midi_velocity_offset = 0; +// tmp2.rhythmModeDrum = 0; + +// std::string name; +// if(midi_index >= 0) name = std::string(1, '\377') + MidiInsName[midi_index]; +// if(h.bank == 0 || h.bank == 0x7F) +// { +// size_t resno = InsertIns(tmp[0], tmp[1], tmp2, name, name2, twoOp); +// SetBank(bank, (unsigned int)gmno, resno); +// } + //--------------------------------------------------------------- + inst.percussionKeyNumber = isPerc ? static_cast<uint_fast8_t>(notenum) : 0; + inst.noteOffset1 = isPerc ? 0 : notenum; + unsigned fb_c = data[offset + 3 + 5]; + inst.fbConn = (static_cast<uint_fast16_t>(fb_c & 0x0F)) | + (static_cast<uint_fast16_t>((fb_c & 0x0E) | (fb_c >> 7)) << 8); + db.addInstrument(bnk, h.patch, inst, ops, fn); } } - AdlBankSetup setup; - setup.volumeModel = VOLUME_Generic; - setup.deepTremolo = true; - setup.deepVibrato = true; - setup.scaleModulators = false; - SetBankSetup(bank, setup); + for(auto &b : bnkMelodic) + db.addMidiBank(bankDb, false, b); + db.addMidiBank(bankDb, true, bnkPercussion); return true; } diff --git a/utils/gen_adldata/file_formats/load_bisqwit.h b/utils/gen_adldata/file_formats/load_bisqwit.h index 9749a76..63d133f 100644 --- a/utils/gen_adldata/file_formats/load_bisqwit.h +++ b/utils/gen_adldata/file_formats/load_bisqwit.h @@ -4,36 +4,41 @@ #include "../progs_cache.h" #include "../midi_inst_list.h" -static bool LoadBisqwit(const char *fn, unsigned bank, const char *prefix) +bool BankFormats::LoadBisqwit(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix) { - #ifdef HARD_BANKS +#ifdef HARD_BANKS writeIni("Bisqwit", fn, prefix, bank, INI_Both); - #endif +#endif FILE *fp = std::fopen(fn, "rb"); if(!fp) return false; - for(uint32_t a = 0; a < 256; ++a) + size_t bankDb = db.initBank(bank, bankTitle, BanksDump::BankEntry::SETUP_Generic); + BanksDump::MidiBank bnkMelodique; + BanksDump::MidiBank bnkPercussion; + + for(uint32_t a = 0, patchId = 0; a < 256; ++a, patchId++) { //unsigned offset = a * 25; uint32_t gmno = a; + bool isPercussion = gmno >= 128; int32_t midi_index = gmno < 128 ? int32_t(gmno) : gmno < 128 + 35 ? -1 : gmno < 128 + 88 ? int32_t(gmno - 35) : -1; + if(patchId == 128) + patchId = 0; + + BanksDump::MidiBank &bnk = isPercussion ? bnkPercussion : bnkMelodique; + BanksDump::InstrumentEntry inst; + BanksDump::Operator ops[5]; - struct ins tmp2; - tmp2.notenum = (uint8_t)std::fgetc(fp); - tmp2.pseudo4op = false; - tmp2.voice2_fine_tune = 0.0; - tmp2.midi_velocity_offset = 0; - tmp2.rhythmModeDrum = 0; + uint8_t notenum = static_cast<uint8_t>(std::fgetc(fp)); - insdata tmp[2]; + InstBuffer tmp[2]; for(int side = 0; side < 2; ++side) { - tmp[side].finetune = (int8_t)std::fgetc(fp); - tmp[side].diff = false; + std::fseek(fp, +1, SEEK_CUR); // skip first byte, unused "fine tune" if(std::fread(tmp[side].data, 1, 11, fp) != 11) return false; } @@ -45,19 +50,22 @@ static bool LoadBisqwit(const char *fn, unsigned bank, const char *prefix) sprintf(name2, "%s%c%u", prefix, (gmno < 128 ? 'M' : 'P'), gmno & 127); - tmp[1].diff = (tmp[0] != tmp[1]); - tmp2.real4op = tmp[1].diff; - size_t resno = InsertIns(tmp[0], tmp[1], tmp2, name, name2, (tmp[0] == tmp[1])); - SetBank(bank, gmno, resno); + db.toOps(tmp[0].d, ops, 0); + if(tmp[0].d != tmp[1].d) + { + inst.instFlags |= BanksDump::InstrumentEntry::WOPL_Ins_4op; + db.toOps(tmp[1].d, ops, 2); + } + + inst.fbConn = uint_fast16_t(tmp[0].data[10]) | (uint_fast16_t(tmp[1].data[10]) << 8); + inst.percussionKeyNumber = a >= 128 ? notenum : 0; + inst.noteOffset1 = a < 128 ? notenum : 0; + db.addInstrument(bnk, patchId, inst, ops, fn); } std::fclose(fp); - AdlBankSetup setup; - setup.volumeModel = VOLUME_Generic; - setup.deepTremolo = true; - setup.deepVibrato = true; - setup.scaleModulators = false; - SetBankSetup(bank, setup); + db.addMidiBank(bankDb, false, bnkMelodique); + db.addMidiBank(bankDb, true, bnkPercussion); return true; } diff --git a/utils/gen_adldata/file_formats/load_bnk.h b/utils/gen_adldata/file_formats/load_bnk.h index f1412ba..29f5494 100644 --- a/utils/gen_adldata/file_formats/load_bnk.h +++ b/utils/gen_adldata/file_formats/load_bnk.h @@ -7,11 +7,13 @@ #include <cstdint> #include <string> -static bool LoadBNK(const char *fn, unsigned bank, const char *prefix, bool is_fat, bool percussive) +bool BankFormats::LoadBNK(BanksDump &db, const char *fn, unsigned bank, + const std::string &bankTitle, const char *prefix, + bool is_fat, bool percussive) { - #ifdef HARD_BANKS +#ifdef HARD_BANKS writeIni("HMI", fn, prefix, bank, percussive ? INI_Drums : INI_Melodic); - #endif +#endif FILE *fp = std::fopen(fn, "rb"); if(!fp) return false; @@ -25,6 +27,9 @@ static bool LoadBNK(const char *fn, unsigned bank, const char *prefix, bool is_f } std::fclose(fp); + size_t bankDb = db.initBank(bank, bankTitle, BanksDump::BankEntry::SETUP_Generic); + BanksDump::MidiBank bnk; + /*printf("%s:\n", fn);*/ //unsigned short version = *(short*)&data[0]; // major,minor (2 bytes) // "ADLIB-" (6 bytes) @@ -80,7 +85,10 @@ static bool LoadBNK(const char *fn, unsigned bank, const char *prefix, bool is_f else sprintf(name2, "%s%u", prefix, n); - insdata tmp; + BanksDump::InstrumentEntry inst; + BanksDump::Operator ops[5]; + + InstBuffer tmp; tmp.data[0] = uint8_t( (op1[ 9] << 7) // TREMOLO FLAG + (op1[10] << 6) // VIBRATO FLAG @@ -102,30 +110,18 @@ static bool LoadBNK(const char *fn, unsigned bank, const char *prefix, bool is_f tmp.data[8] = op1[0] * 0x40 + op1[8]; // KSL , LEVEL tmp.data[9] = op2[0] * 0x40 + op2[8]; // KSL , LEVEL tmp.data[10] = op1[2] * 2 + op1[12]; // FEEDBACK, ADDITIVEFLAG - tmp.finetune = 0; - tmp.diff = false; + // Note: op2[2] and op2[12] are unused and contain garbage. - ins tmp2; - tmp2.notenum = is_fat ? voice_num : (percussive ? usage_flag : 0); - tmp2.pseudo4op = false; - tmp2.real4op = false; - tmp2.voice2_fine_tune = 0.0; - tmp2.midi_velocity_offset = 0; - tmp2.rhythmModeDrum = 0; - if(is_fat) tmp.data[10] ^= 1; + if(is_fat) + tmp.data[10] ^= 1; - size_t resno = InsertIns(tmp, tmp2, std::string(1, '\377') + name, name2); + db.toOps(tmp.d, ops, 0); + inst.percussionKeyNumber = is_fat ? voice_num : (percussive ? usage_flag : 0); + inst.setFbConn(op1[2] * 2 + op1[12]); if(!is_fat) - { - SetBank(bank, (unsigned int)gmno, resno); - } - else - { - if(name[2] == 'O' || name[1] == 'M') SetBank(bank + 0, (unsigned int)gmno, resno); - if(name[2] == 'S' || name[1] == 'M') SetBank(bank + 1, (unsigned int)gmno, resno); - } + db.addInstrument(bnk, n & 127, inst, ops, fn); /* for(unsigned p=0; p<30; ++p) @@ -151,12 +147,7 @@ static bool LoadBNK(const char *fn, unsigned bank, const char *prefix, bool is_f */ } - AdlBankSetup setup; - setup.volumeModel = VOLUME_Generic; - setup.deepTremolo = false; - setup.deepVibrato = false; - setup.scaleModulators = false; - SetBankSetup(bank, setup); + db.addMidiBank(bankDb, percussive, bnk); return true; } diff --git a/utils/gen_adldata/file_formats/load_bnk2.h b/utils/gen_adldata/file_formats/load_bnk2.h index 072712c..46ba604 100644 --- a/utils/gen_adldata/file_formats/load_bnk2.h +++ b/utils/gen_adldata/file_formats/load_bnk2.h @@ -8,9 +8,10 @@ inline int stdstoi(const std::string& str) return std::atoi(str.c_str()); } -static bool LoadBNK2(const char *fn, unsigned bank, const char *prefix, - const std::string &melo_filter, - const std::string &perc_filter) +bool BankFormats::LoadBNK2(BanksDump &db, const char *fn, unsigned bank, + const std::string &bankTitle, const char *prefix, + const std::string &melo_filter, + const std::string &perc_filter) { #ifdef HARD_BANKS writeIni("AdLibGold", fn, prefix, bank, INI_Both, melo_filter.c_str(), perc_filter.c_str()); @@ -28,6 +29,10 @@ static bool LoadBNK2(const char *fn, unsigned bank, const char *prefix, } std::fclose(fp); + size_t bankDb = db.initBank(bank, bankTitle, BanksDump::BankEntry::SETUP_Generic); + BanksDump::MidiBank bnkMelodic; + BanksDump::MidiBank bnkPercussion; + unsigned short ins_entries = *(unsigned short *)&data[28 + 2 + 10]; unsigned char *records = &data[48]; @@ -47,13 +52,24 @@ static bool LoadBNK2(const char *fn, unsigned bank, const char *prefix, } int gmno = 0; + int patchId = 0; + bool isPercussion = false; if(name.substr(0, melo_filter.size()) == melo_filter) - gmno = stdstoi(name.substr(melo_filter.size())); + { + gmno = patchId = stdstoi(name.substr(melo_filter.size())); + isPercussion = false; + } else if(name.substr(0, perc_filter.size()) == perc_filter) - gmno = stdstoi(name.substr(perc_filter.size())) + 128; + { + patchId = stdstoi(name.substr(perc_filter.size())); + gmno = patchId + 128; + isPercussion = true; + } else continue; + BanksDump::MidiBank &bnk = isPercussion ? bnkPercussion : bnkMelodic; + const unsigned char *insdata = &data[size_t(offset2)]; const unsigned char *ops[4] = { insdata + 0, insdata + 6, insdata + 12, insdata + 18 }; unsigned char C4xxxFFFC = insdata[24]; @@ -64,7 +80,10 @@ static bool LoadBNK2(const char *fn, unsigned bank, const char *prefix, char name2[512]; sprintf(name2, "%s%c%u", prefix, (gmno & 128) ? 'P' : 'M', gmno & 127); - struct insdata tmp[2]; + BanksDump::InstrumentEntry inst; + BanksDump::Operator opsD[5]; + + InstBuffer tmp[2]; for(unsigned a = 0; a < 2; ++a) { tmp[a].data[0] = ops[a * 2 + 0][0]; @@ -77,42 +96,30 @@ static bool LoadBNK2(const char *fn, unsigned bank, const char *prefix, tmp[a].data[7] = ops[a * 2 + 1][4] & 0x07; tmp[a].data[8] = ops[a * 2 + 0][1]; tmp[a].data[9] = ops[a * 2 + 1][1]; - tmp[a].finetune = (int8_t)TTTTTTTT; - tmp[a].diff = false; + db.toOps(tmp[a].d, opsD, a * 2); } tmp[0].data[10] = C4xxxFFFC & 0x0F; tmp[1].data[10] = (tmp[0].data[10] & 0x0E) | (C4xxxFFFC >> 7); - ins tmp2; - tmp2.notenum = (gmno & 128) ? 35 : 0; - tmp2.pseudo4op = false; - tmp2.real4op = false; - tmp2.voice2_fine_tune = 0.0; - tmp2.midi_velocity_offset = 0; - tmp2.rhythmModeDrum = 0; + inst.setFbConn(C4xxxFFFC & 0x0F, (tmp[0].data[10] & 0x0E) | (C4xxxFFFC >> 7)); + inst.noteOffset1 = (int8_t)TTTTTTTT; + inst.percussionKeyNumber = (gmno & 128) ? 35 : 0; if(xxP24NNN & 8) { // dual-op - tmp2.real4op = true; - tmp[1].diff = true; - size_t resno = InsertIns(tmp[0], tmp[1], tmp2, std::string(1, '\377') + name, name2); - SetBank(bank, (unsigned int)gmno, resno); + inst.instFlags |= BanksDump::InstrumentEntry::WOPL_Ins_4op; + db.addInstrument(bnk, patchId, inst, opsD, fn); } else { // single-op - size_t resno = InsertIns(tmp[0], tmp2, std::string(1, '\377') + name, name2); - SetBank(bank, (unsigned int)gmno, resno); + db.addInstrument(bnk, patchId, inst, opsD, fn); } } - AdlBankSetup setup; - setup.volumeModel = VOLUME_Generic; - setup.deepTremolo = false; - setup.deepVibrato = false; - setup.scaleModulators = false; - SetBankSetup(bank, setup); + db.addMidiBank(bankDb, false, bnkMelodic); + db.addMidiBank(bankDb, true, bnkPercussion); return true; } diff --git a/utils/gen_adldata/file_formats/load_ea.h b/utils/gen_adldata/file_formats/load_ea.h index c217c09..5e5711e 100644 --- a/utils/gen_adldata/file_formats/load_ea.h +++ b/utils/gen_adldata/file_formats/load_ea.h @@ -4,17 +4,22 @@ #include "../progs_cache.h" #include "../midi_inst_list.h" -static bool LoadEA(const char *fn, unsigned bank, const char *prefix) +bool BankFormats::LoadEA(BanksDump &db, const char *fn, unsigned bank, + const std::string &bankTitle, const char *prefix) { FILE *fp = std::fopen(fn, "rb"); if(!fp) return false; + size_t bankDb = db.initBank(bank, bankTitle, BanksDump::BankEntry::SETUP_CMF); + BanksDump::MidiBank bnkMelodic = db.midiBanks[db.banks[0].melodic[0]]; + BanksDump::MidiBank bnkPercussion = db.midiBanks[db.banks[0].percussion[0]]; + // Copy all instruments from bank 0 - for(unsigned gmno = 0; gmno < 128; ++gmno) - progs[bank][gmno] = progs[0][gmno]; - for(unsigned gmno = 35; gmno < 80; ++gmno) - progs[bank][0x80 + gmno] = progs[0][0x80 + gmno]; +// for(unsigned gmno = 0; gmno < 128; ++gmno) +// progs[bank][gmno] = progs[0][gmno]; +// for(unsigned gmno = 35; gmno < 80; ++gmno) +// progs[bank][0x80 + gmno] = progs[0][0x80 + gmno]; uint16_t sources[20 + 8]; // Copy also the unused instruments @@ -61,7 +66,10 @@ static bool LoadEA(const char *fn, unsigned bank, const char *prefix) return false; } - insdata tmp; + BanksDump::InstrumentEntry inst; + BanksDump::Operator ops[5]; + + InstBuffer tmp; tmp.data[0] = bytes[0]; // reg 0x20: modulator AM/VIG/EG/KSR tmp.data[8] = bytes[1]; // reg 0x40: modulator ksl/attenuation tmp.data[2] = bytes[2]; // reg 0x60: modulator attack/decay @@ -78,15 +86,12 @@ static bool LoadEA(const char *fn, unsigned bank, const char *prefix) tmp.data[10] = bytes[8]; // reg 0xC0 (feedback and connection) - tmp.finetune = int8_t(bytes[9] + 12); // finetune tmp.data[6] = 0; // reg 0xE0: modulator, never seems to be set tmp.data[7] = 0; // reg 0xE0: carrier, never seems to be set - ins tmp2{}; - tmp2.notenum = 0; - tmp2.pseudo4op = false; - tmp2.real4op = false; - tmp2.rhythmModeDrum = 0; + db.toOps(tmp.d, ops, 0); + inst.setFbConn(bytes[8]); + inst.noteOffset1 = int8_t(bytes[9] + 12); std::string name; char name2[512]; @@ -98,42 +103,38 @@ static bool LoadEA(const char *fn, unsigned bank, const char *prefix) { snprintf(name2, 512, "%sunk%04X", prefix, offset); } - size_t resno = InsertIns(tmp, tmp2, std::string(1, '\377') + name, name2); - SetBank(bank, gmno, resno); + + db.addInstrument(bnkMelodic, gmno, inst, ops, fn); if(gmno == 10) { - /*tmp.finetune=0;*/ tmp2.notenum = 0x49; - SetBank(bank, 0x80 + 0x36, InsertIns(tmp, tmp2, std::string(1, '\377') + MidiInsName[0x80 + 0x36 - 35], std::string(1, '\377') + prefix + "P54")); + inst.percussionKeyNumber = 0x49; + db.addInstrument(bnkPercussion, 0x36, inst, ops, fn); } if(gmno == 18) { - /*tmp.finetune=0;*/ tmp2.notenum = 0x17; - SetBank(bank, 0x80 + 0x2A, InsertIns(tmp, tmp2, std::string(1, '\377') + MidiInsName[0x80 + 0x2A - 35], std::string(1, '\377') + prefix + "P42")); + inst.percussionKeyNumber = 0x17; + db.addInstrument(bnkPercussion, 0x2A, inst, ops, fn); } if(gmno == 16) { - /*tmp.finetune=0;*/ tmp2.notenum = 0x0C; - SetBank(bank, 0x80 + 0x24, InsertIns(tmp, tmp2, std::string(1, '\377') + MidiInsName[0x80 + 0x24 - 35], std::string(1, '\377') + prefix + "P36")); + inst.percussionKeyNumber = 0x0C; + db.addInstrument(bnkPercussion, 0x24, inst, ops, fn); } if(gmno == 17) { - /*tmp.finetune=0;*/ tmp2.notenum = 0x01; - SetBank(bank, 0x80 + 0x26, InsertIns(tmp, tmp2, std::string(1, '\377') + MidiInsName[0x80 + 0x26 - 35], std::string(1, '\377') + prefix + "P38")); + inst.percussionKeyNumber = 0x01; + db.addInstrument(bnkPercussion, 0x26, inst, ops, fn); } } std::fclose(fp); - AdlBankSetup setup; - setup.volumeModel = VOLUME_CMF; - setup.deepTremolo = false; - setup.deepVibrato = false; - setup.scaleModulators = false; - SetBankSetup(bank, setup); + db.addMidiBank(bankDb, false, bnkMelodic); + db.addMidiBank(bankDb, true, bnkPercussion); return true; } diff --git a/utils/gen_adldata/file_formats/load_ibk.h b/utils/gen_adldata/file_formats/load_ibk.h index 28177c7..f95a244 100644 --- a/utils/gen_adldata/file_formats/load_ibk.h +++ b/utils/gen_adldata/file_formats/load_ibk.h @@ -3,11 +3,13 @@ #include "../progs_cache.h" -static bool LoadIBK(const char *fn, unsigned bank, const char *prefix, bool percussive, bool noRhythmMode = false) +bool BankFormats::LoadIBK(BanksDump &db, const char *fn, unsigned bank, + const std::string &bankTitle, const char *prefix, + bool percussive, bool noRhythmMode) { - #ifdef HARD_BANKS +#ifdef HARD_BANKS writeIni("IBK", fn, prefix, bank, percussive ? INI_Drums : INI_Melodic); - #endif +#endif FILE *fp = std::fopen(fn, "rb"); if(!fp) return false; @@ -21,6 +23,9 @@ static bool LoadIBK(const char *fn, unsigned bank, const char *prefix, bool perc } std::fclose(fp); + size_t bankDb = db.initBank(bank, bankTitle, BanksDump::BankEntry::SETUP_Generic); + BanksDump::MidiBank bnk; + unsigned offs1_base = 0x804, offs1_len = 9; unsigned offs2_base = 0x004, offs2_len = 16; @@ -43,7 +48,10 @@ static bool LoadIBK(const char *fn, unsigned bank, const char *prefix, bool perc sprintf(name2, "%s%c%u", prefix, (gmno < 128 ? 'M' : 'P'), gmno & 127); - insdata tmp; + BanksDump::InstrumentEntry inst; + BanksDump::Operator ops[5]; + + InstBuffer tmp; tmp.data[0] = data[offset2 + 0]; tmp.data[1] = data[offset2 + 1]; tmp.data[8] = data[offset2 + 2]; @@ -57,53 +65,43 @@ static bool LoadIBK(const char *fn, unsigned bank, const char *prefix, bool perc tmp.data[10] = data[offset2 + 10]; // bisqwit: [+11] seems to be used also, what is it for? // Wohlstand: You wanna know? It's the rhythm-mode drum number! If 0 - melodic, >0 - rhythm-mode drum - tmp.finetune = percussive ? 0 : data[offset2 + 12]; - tmp.diff = false; - struct ins tmp2; - tmp2.notenum = percussive ? data[offset2 + 13] : 0; - tmp2.pseudo4op = false; - tmp2.real4op = false; - tmp2.voice2_fine_tune = 0.0; - tmp2.midi_velocity_offset = 0; - tmp2.rhythmModeDrum = 0; + db.toOps(tmp.d, ops, 0); + inst.noteOffset1 = percussive ? 0 : data[offset2 + 12]; + inst.percussionKeyNumber = percussive ? data[offset2 + 13] : 0; + inst.setFbConn(data[offset2 + 10]); + if(percussive && !noRhythmMode) { int rm = data[offset2 + 11]; switch(rm) { case 6: - tmp2.rhythmModeDrum = ins::Flag_RM_BassDrum; + inst.instFlags |= BanksDump::InstrumentEntry::WOPL_RM_BassDrum; break; case 7: - tmp2.rhythmModeDrum = ins::Flag_RM_Snare; + inst.instFlags |= BanksDump::InstrumentEntry::WOPL_RM_Snare; break; case 8: - tmp2.rhythmModeDrum = ins::Flag_RM_TomTom; + inst.instFlags |= BanksDump::InstrumentEntry::WOPL_RM_TomTom; break; case 9: - tmp2.rhythmModeDrum = ins::Flag_RM_Cymbal; + inst.instFlags |= BanksDump::InstrumentEntry::WOPL_RM_Cymbal; break; case 10: - tmp2.rhythmModeDrum = ins::Flag_RM_HiHat; + inst.instFlags |= BanksDump::InstrumentEntry::WOPL_RM_HiHat; break; default: // IBK logic: make non-percussion instrument be silent - tmp = MakeNoSoundIns(); +// tmp = MakeNoSoundIns(); break; } } - size_t resno = InsertIns(tmp, tmp2, std::string(1, '\377') + name, name2); - SetBank(bank, (unsigned int)gmno, resno); + db.addInstrument(bnk, a, inst, ops, fn); } - AdlBankSetup setup; - setup.volumeModel = VOLUME_Generic; - setup.deepTremolo = false; - setup.deepVibrato = false; - setup.scaleModulators = false; - SetBankSetup(bank, setup); + db.addMidiBank(bankDb, percussive, bnk); return true; } diff --git a/utils/gen_adldata/file_formats/load_jv.h b/utils/gen_adldata/file_formats/load_jv.h index a498bb6..9281114 100644 --- a/utils/gen_adldata/file_formats/load_jv.h +++ b/utils/gen_adldata/file_formats/load_jv.h @@ -4,11 +4,11 @@ #include "../progs_cache.h" #include "../midi_inst_list.h" -static bool LoadJunglevision(const char *fn, unsigned bank, const char *prefix) +bool BankFormats::LoadJunglevision(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix) { - #ifdef HARD_BANKS +#ifdef HARD_BANKS writeIni("Junglevision", fn, prefix, bank, INI_Both); - #endif +#endif FILE *fp = std::fopen(fn, "rb"); if(!fp) return false; @@ -22,6 +22,10 @@ static bool LoadJunglevision(const char *fn, unsigned bank, const char *prefix) } std::fclose(fp); + size_t bankDb = db.initBank(bank, bankTitle, BanksDump::BankEntry::SETUP_Win9X); + BanksDump::MidiBank bnkMelodique; + BanksDump::MidiBank bnkPercussion; + uint16_t ins_count = uint16_t(data[0x20] + (data[0x21] << 8)); uint16_t drum_count = uint16_t(data[0x22] + (data[0x23] << 8)); uint16_t first_ins = uint16_t(data[0x24] + (data[0x25] << 8)); @@ -38,7 +42,14 @@ static bool LoadJunglevision(const char *fn, unsigned bank, const char *prefix) : gmno < 128 + 88 ? int(gmno - 35) : -1; - insdata tmp[2]; + bool isPercussion = a >= ins_count; + size_t patchId = (a < ins_count) ? (a + first_ins) : ((a - ins_count) + first_drum); + BanksDump::MidiBank &bnk = isPercussion ? bnkPercussion : bnkMelodique; + BanksDump::InstrumentEntry inst; + BanksDump::Operator ops[5]; + uint8_t notenum; + + InstBuffer tmp[2]; tmp[0].data[0] = data[offset + 2]; tmp[0].data[1] = data[offset + 8]; @@ -51,8 +62,7 @@ static bool LoadJunglevision(const char *fn, unsigned bank, const char *prefix) tmp[0].data[8] = data[offset + 3]; tmp[0].data[9] = data[offset + 9]; tmp[0].data[10] = data[offset + 7] & 0x0F;//~0x30; - tmp[0].finetune = 0; - tmp[0].diff = false; + inst.noteOffset1 = 0; tmp[1].data[0] = data[offset + 2 + 11]; tmp[1].data[1] = data[offset + 8 + 11]; @@ -65,52 +75,39 @@ static bool LoadJunglevision(const char *fn, unsigned bank, const char *prefix) tmp[1].data[8] = data[offset + 3 + 11]; tmp[1].data[9] = data[offset + 9 + 11]; tmp[1].data[10] = data[offset + 7 + 11] & 0x0F;//~0x30; - tmp[1].finetune = 0; - tmp[1].diff = (data[offset] != 0); - - struct ins tmp2; - tmp2.notenum = data[offset + 1]; - tmp2.pseudo4op = false; - tmp2.real4op = (data[offset] != 0); - tmp2.voice2_fine_tune = 0.0; - tmp2.midi_velocity_offset = 0; - tmp2.rhythmModeDrum = 0; - - while(tmp2.notenum && tmp2.notenum < 20) + inst.noteOffset2 = 0; + + notenum = data[offset + 1]; + + while(notenum && notenum < 20) { - tmp2.notenum += 12; - tmp[0].finetune -= 12; - tmp[1].finetune -= 12; + notenum += 12; + inst.noteOffset1 -= 12; + inst.noteOffset2 -= 12; } + if(data[offset] != 0) + inst.instFlags |= BanksDump::InstrumentEntry::WOPL_Ins_4op; + inst.percussionKeyNumber = notenum; + inst.setFbConn(data[offset + 7], data[offset + 7 + 11]); + db.toOps(tmp[0].d, ops, 0); + db.toOps(tmp[1].d, ops, 2); + std::string name; - if(midi_index >= 0) name = std::string(1, '\377') + MidiInsName[midi_index]; + if(midi_index >= 0) + name = std::string(1, '\377') + MidiInsName[midi_index]; char name2[512]; sprintf(name2, "%s%c%u", prefix, (gmno < 128 ? 'M' : 'P'), gmno & 127); - if(!data[offset]) - { - size_t resno = InsertIns(tmp[0], tmp2, name, name2); - SetBank(bank, gmno, resno); - } - else // Double instrument - { - size_t resno = InsertIns(tmp[0], tmp[1], tmp2, name, name2); - SetBank(bank, gmno, resno); - } + db.addInstrument(bnk, patchId, inst, ops, fn); } - AdlBankSetup setup; - setup.volumeModel = VOLUME_9X; - setup.deepTremolo = true; - setup.deepVibrato = true; - setup.scaleModulators = false; - SetBankSetup(bank, setup); + db.addMidiBank(bankDb, false, bnkMelodique); + db.addMidiBank(bankDb, true, bnkPercussion); return true; } #endif // LOAD_JV_H - diff --git a/utils/gen_adldata/file_formats/load_op2.h b/utils/gen_adldata/file_formats/load_op2.h index 86f1377..e43f17f 100644 --- a/utils/gen_adldata/file_formats/load_op2.h +++ b/utils/gen_adldata/file_formats/load_op2.h @@ -2,6 +2,9 @@ #define LOAD_OP2_H #include "../progs_cache.h" +#ifndef COMMON_H +#include "common.h" +#endif #ifndef _MSC_VER #define PACKED_STRUCT __attribute__((packed)) @@ -15,32 +18,32 @@ struct Doom_OPL2instrument { - unsigned char trem_vibr_1; /* OP 1: tremolo/vibrato/sustain/KSR/multi */ - unsigned char att_dec_1; /* OP 1: attack rate/decay rate */ - unsigned char sust_rel_1; /* OP 1: sustain level/release rate */ - unsigned char wave_1; /* OP 1: waveform select */ - unsigned char scale_1; /* OP 1: key scale level */ - unsigned char level_1; /* OP 1: output level */ - unsigned char feedback; /* feedback/AM-FM (both operators) */ - unsigned char trem_vibr_2; /* OP 2: tremolo/vibrato/sustain/KSR/multi */ - unsigned char att_dec_2; /* OP 2: attack rate/decay rate */ - unsigned char sust_rel_2; /* OP 2: sustain level/release rate */ - unsigned char wave_2; /* OP 2: waveform select */ - unsigned char scale_2; /* OP 2: key scale level */ - unsigned char level_2; /* OP 2: output level */ - unsigned char unused; - short basenote; /* base note offset */ + uint8_t trem_vibr_1; /* OP 1: tremolo/vibrato/sustain/KSR/multi */ + uint8_t att_dec_1; /* OP 1: attack rate/decay rate */ + uint8_t sust_rel_1; /* OP 1: sustain level/release rate */ + uint8_t wave_1; /* OP 1: waveform select */ + uint8_t scale_1; /* OP 1: key scale level */ + uint8_t level_1; /* OP 1: output level */ + uint8_t feedback; /* feedback/AM-FM (both operators) */ + uint8_t trem_vibr_2; /* OP 2: tremolo/vibrato/sustain/KSR/multi */ + uint8_t att_dec_2; /* OP 2: attack rate/decay rate */ + uint8_t sust_rel_2; /* OP 2: sustain level/release rate */ + uint8_t wave_2; /* OP 2: waveform select */ + uint8_t scale_2; /* OP 2: key scale level */ + uint8_t level_2; /* OP 2: output level */ + uint8_t unused; + uint8_t basenote[2]; /* base note offset */ } PACKED_STRUCT; struct Doom_opl_instr { - unsigned short flags; + uint8_t flags[2]; #define FL_FIXED_PITCH 0x0001 // note has fixed pitch (drum note) -#define FL_UNKNOWN 0x0002 // ??? (used in instrument #65 only) +#define FL_VIB_DELAY 0x0002 // vib_delay (used in instrument #65 only) #define FL_DOUBLE_VOICE 0x0004 // use two voices instead of one - unsigned char finetune; - unsigned char note; + uint8_t finetune; + uint8_t note; struct Doom_OPL2instrument patchdata[2]; } PACKED_STRUCT; @@ -49,11 +52,11 @@ struct Doom_opl_instr #endif -static bool LoadDoom(const char *fn, unsigned bank, const char *prefix) +bool BankFormats::LoadDoom(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix) { - #ifdef HARD_BANKS +#ifdef HARD_BANKS writeIni("OP2", fn, prefix, bank, INI_Both); - #endif +#endif FILE *fp = std::fopen(fn, "rb"); if(!fp) return false; @@ -68,11 +71,17 @@ static bool LoadDoom(const char *fn, unsigned bank, const char *prefix) } std::fclose(fp); + size_t bankDb = db.initBank(bank, bankTitle, BanksDump::BankEntry::SETUP_DMX); + BanksDump::MidiBank bnkMelodique; + BanksDump::MidiBank bnkPercussion; + for(unsigned a = 0; a < 175; ++a) { const size_t offset1 = 0x18A4 + a * 32; const size_t offset2 = 8 + a * 36; - + BanksDump::MidiBank &bnk = a < 128 ? bnkMelodique : bnkPercussion; + BanksDump::InstrumentEntry inst; + BanksDump::Operator ops[5]; std::string name; for(unsigned p = 0; p < 32; ++p) if(data[offset1] != '\0') @@ -80,15 +89,17 @@ static bool LoadDoom(const char *fn, unsigned bank, const char *prefix) //printf("%3d %3d %3d %8s: ", a,b,c, name.c_str()); int gmno = int(a < 128 ? a : ((a | 128) + 35)); + size_t patchId = a < 128 ? a : ((a - 128) + 35); char name2[512]; snprintf(name2, 512, "%s%c%u", prefix, (gmno < 128 ? 'M' : 'P'), gmno & 127); Doom_opl_instr &ins = *(Doom_opl_instr *) &data[offset2]; + uint16_t flags = toSint16LE(ins.flags); + + InstBuffer tmp[2] = {MakeNoSoundIns1(), MakeNoSoundIns1()}; + int16_t noteOffset[2]; - insdata tmp[2] = {MakeNoSoundIns(), MakeNoSoundIns()}; - tmp[0].diff = false; - tmp[1].diff = false; for(size_t index = 0; index < 2; ++index) { const Doom_OPL2instrument &src = ins.patchdata[index]; @@ -103,38 +114,30 @@ static bool LoadDoom(const char *fn, unsigned bank, const char *prefix) tmp[index].data[8] = src.scale_1 | src.level_1; tmp[index].data[9] = src.scale_2 | src.level_2; tmp[index].data[10] = src.feedback; - tmp[index].finetune = int8_t(src.basenote + 12); - } - struct ins tmp2; - tmp2.notenum = ins.note; - tmp2.pseudo4op = false; - tmp2.real4op = false; - tmp2.voice2_fine_tune = 0.0; - tmp2.midi_velocity_offset = 0; - tmp2.rhythmModeDrum = 0; - - while(tmp2.notenum && tmp2.notenum < 20) - { - tmp2.notenum += 12; - tmp[0].finetune -= 12; - tmp[1].finetune -= 12; + noteOffset[index] = toSint16LE(src.basenote) + 12; + inst.fbConn |= (uint_fast16_t(src.feedback) << (index == 1 ? 8 : 0)); + db.toOps(tmp[index].d, ops, index * 2); } - if(!(ins.flags & FL_DOUBLE_VOICE)) - { - size_t resno = InsertIns(tmp[0], tmp2, std::string(1, '\377') + name, name2); - SetBank(bank, (unsigned int)gmno, resno); - } - else // Double instrument + uint8_t notenum = ins.note; + + while(notenum && notenum < 20) { - tmp2.pseudo4op = true; - // Simulate behavior of DMX second voice detune - tmp2.voice2_fine_tune = (double)((ins.finetune >> 1) - 64) / 32.0; - //printf("/*DOOM FINE TUNE (flags %000X instrument is %d) IS %d -> %lf*/\n", ins.flags, a, ins.finetune, tmp2.fine_tune); - size_t resno = InsertIns(tmp[0], tmp[1], tmp2, std::string(1, '\377') + name, name2); - SetBank(bank, (unsigned int)gmno, resno); + notenum += 12; + noteOffset[0] -= 12; + noteOffset[1] -= 12; } + inst.noteOffset1 = noteOffset[0]; + inst.noteOffset2 = noteOffset[1]; + + if((flags & FL_DOUBLE_VOICE) != 0) + inst.instFlags |= BanksDump::InstrumentEntry::WOPL_Ins_4op | BanksDump::InstrumentEntry::WOPL_Ins_Pseudo4op; + inst.percussionKeyNumber = notenum; + inst.secondVoiceDetune = static_cast<char>(static_cast<int>(ins.finetune) - 128); + + db.addInstrument(bnk, patchId, inst, ops, fn); + /*const Doom_OPL2instrument& A = ins.patchdata[0]; const Doom_OPL2instrument& B = ins.patchdata[1]; printf( @@ -156,12 +159,8 @@ static bool LoadDoom(const char *fn, unsigned bank, const char *prefix) printf("------------------------------------------------------------\n");*/ } - AdlBankSetup setup; - setup.volumeModel = VOLUME_DMX; - setup.deepTremolo = false; - setup.deepVibrato = false; - setup.scaleModulators = false; - SetBankSetup(bank, setup); + db.addMidiBank(bankDb, false, bnkMelodique); + db.addMidiBank(bankDb, true, bnkPercussion); return true; } diff --git a/utils/gen_adldata/file_formats/load_tmb.h b/utils/gen_adldata/file_formats/load_tmb.h index 4417b02..d6fa588 100644 --- a/utils/gen_adldata/file_formats/load_tmb.h +++ b/utils/gen_adldata/file_formats/load_tmb.h @@ -4,11 +4,11 @@ #include "../progs_cache.h" #include "../midi_inst_list.h" -static bool LoadTMB(const char *fn, unsigned bank, const char *prefix) +bool BankFormats::LoadTMB(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix) { - #ifdef HARD_BANKS +#ifdef HARD_BANKS writeIni("TMB", fn, prefix, bank, INI_Both); - #endif +#endif FILE *fp = std::fopen(fn, "rb"); if(!fp) return false; @@ -22,7 +22,11 @@ static bool LoadTMB(const char *fn, unsigned bank, const char *prefix) } std::fclose(fp); - for(unsigned a = 0; a < 256; ++a) + size_t bankDb = db.initBank(bank, bankTitle, BanksDump::BankEntry::SETUP_Apogee); + BanksDump::MidiBank bnkMelodique; + BanksDump::MidiBank bnkPercussion; + + for(unsigned a = 0, patchId = 0; a < 256; ++a, patchId++) { unsigned offset = a * 0x0D; unsigned gmno = a; @@ -30,8 +34,14 @@ static bool LoadTMB(const char *fn, unsigned bank, const char *prefix) : gmno < 128 + 35 ? -1 : gmno < 128 + 88 ? int(gmno - 35) : -1; + if(patchId == 128) + patchId = 0; + bool isPercussion = a >= 128; + BanksDump::MidiBank &bnk = isPercussion ? bnkPercussion : bnkMelodique; + BanksDump::InstrumentEntry inst; + BanksDump::Operator ops[5]; - insdata tmp; + InstBuffer tmp; tmp.data[0] = data[offset + 0]; tmp.data[1] = data[offset + 1]; @@ -44,16 +54,11 @@ static bool LoadTMB(const char *fn, unsigned bank, const char *prefix) tmp.data[8] = data[offset + 2]; tmp.data[9] = data[offset + 3]; tmp.data[10] = data[offset + 10]; - tmp.finetune = 0; - tmp.diff = false; - struct ins tmp2; - tmp2.notenum = data[offset + 11]; - tmp2.pseudo4op = false; - tmp2.real4op = false; - tmp2.voice2_fine_tune = 0.0; - tmp2.midi_velocity_offset = (int8_t)data[offset + 12]; - tmp2.rhythmModeDrum = 0; + inst.percussionKeyNumber = data[offset + 11]; + inst.midiVelocityOffset = (int8_t)data[offset + 12]; + inst.fbConn = data[offset + 10]; + db.toOps(tmp.d, ops, 0); std::string name; if(midi_index >= 0) name = std::string(1, '\377') + MidiInsName[midi_index]; @@ -62,16 +67,11 @@ static bool LoadTMB(const char *fn, unsigned bank, const char *prefix) sprintf(name2, "%s%c%u", prefix, (gmno < 128 ? 'M' : 'P'), gmno & 127); - size_t resno = InsertIns(tmp, tmp2, name, name2); - SetBank(bank, gmno, resno); + db.addInstrument(bnk, patchId, inst, ops, fn); } - AdlBankSetup setup; - setup.volumeModel = VOLUME_APOGEE; - setup.deepTremolo = false; - setup.deepVibrato = false; - setup.scaleModulators = false; - SetBankSetup(bank, setup); + db.addMidiBank(bankDb, false, bnkMelodique); + db.addMidiBank(bankDb, true, bnkPercussion); return true; } diff --git a/utils/gen_adldata/file_formats/load_wopl.h b/utils/gen_adldata/file_formats/load_wopl.h index d0c01d3..6705bd9 100644 --- a/utils/gen_adldata/file_formats/load_wopl.h +++ b/utils/gen_adldata/file_formats/load_wopl.h @@ -18,7 +18,7 @@ enum class WOPL_Flags WOPL_RhythmModeMask = 0x38, }; -static bool LoadWopl(const char *fn, unsigned bank, const char *prefix) +bool BankFormats::LoadWopl(BanksDump &db, const char *fn, unsigned bank, const std::string bankTitle, const char *prefix) { FILE *fp = std::fopen(fn, "rb"); if(!fp) @@ -27,6 +27,7 @@ static bool LoadWopl(const char *fn, unsigned bank, const char *prefix) std::fflush(stderr); return false; } + std::fseek(fp, 0, SEEK_END); std::vector<unsigned char> data(size_t(std::ftell(fp))); std::rewind(fp); @@ -57,11 +58,7 @@ static bool LoadWopl(const char *fn, unsigned bank, const char *prefix) uint16_t mbanks_count = toUint16BE((const uint8_t *)data.data() + 0x0d); uint16_t pbanks_count = toUint16BE((const uint8_t *)data.data() + 0x0f); - AdlBankSetup setup; - setup.deepTremolo = (data[0x11] & 0x01) != 0; - setup.deepVibrato = (data[0x11] & 0x02) != 0; - setup.volumeModel = (int)data[0x12]; - setup.scaleModulators = false; + size_t bankDb = db.initBank(bank, bankTitle, static_cast<uint_fast16_t>((static_cast<unsigned>(data[0x11]) << 8) | static_cast<unsigned>(data[0x12]))); // Validate file format by size calculation if(version == 1) @@ -91,27 +88,50 @@ static bool LoadWopl(const char *fn, unsigned bank, const char *prefix) uint32_t melodic_offset = 0; uint32_t percussion_offset = 0; + uint32_t melodic_meta_offset = 0; + uint32_t percussion_meta_offset = 0; + if(version < 2) + { melodic_offset = 0x13; + melodic_meta_offset = 0; + } else + { melodic_offset = 0x13 + 34 * mbanks_count + 34 * pbanks_count; + melodic_meta_offset = 0x13; + percussion_meta_offset = 0x13 + 34 * mbanks_count; + } percussion_offset = melodic_offset + (insSize * 128 * mbanks_count); - uint32_t root_offsets[2] = {melodic_offset, percussion_offset}; + uint32_t root_sizes[2] = {mbanks_count, pbanks_count}; + uint32_t root_offsets[2] = {melodic_offset, percussion_offset}; + uint32_t root_meta_offsets[2] = {melodic_meta_offset, percussion_meta_offset}; for(size_t bset = 0; bset < 2; bset++) { bool is_percussion = (bset == 1); - for(uint32_t bankno = 0; bankno < 1; bankno++) // only first melodic bank (Until multi-banks support will be implemented) + for(uint32_t bankno = 0; bankno < root_sizes[bset]; bankno++) // only first melodic bank (Until multi-banks support will be implemented) { uint32_t bank_offset = root_offsets[bset] + (bankno * insSize * 128); + BanksDump::MidiBank bnk; + if(version >= 2) + { + uint32_t meta_offset = root_meta_offsets[bset] + (bankno * 34); + bnk.lsb = data[meta_offset + 32 + 0]; + bnk.msb = data[meta_offset + 32 + 1]; + } + for(uint32_t i = 0; i < 128; i++) { uint32_t offset = bank_offset + uint32_t(i * insSize); std::string name; - insdata tmp[2]; + InstBuffer tmp[2]; + + BanksDump::InstrumentEntry inst; + BanksDump::Operator ops[5]; name.resize(32); std::memcpy(&name[0], data.data() + offset, 32); @@ -131,57 +151,55 @@ static bool LoadWopl(const char *fn, unsigned bank, const char *prefix) 8 Systain and Release register data 9 Wave form */ - tmp[0].data[0] = data[offset + 42 + 5];//AMVIB op1 - tmp[0].data[1] = data[offset + 42 + 0];//AMVIB op2 - tmp[0].data[2] = data[offset + 42 + 7];//AtDec op1 - tmp[0].data[3] = data[offset + 42 + 2];//AtDec op2 - tmp[0].data[4] = data[offset + 42 + 8];//SusRel op1 - tmp[0].data[5] = data[offset + 42 + 3];//SusRel op2 - tmp[0].data[6] = data[offset + 42 + 9];//Wave op1 - tmp[0].data[7] = data[offset + 42 + 4];//Wave op2 - tmp[0].data[8] = data[offset + 42 + 6];//KSL op1 - tmp[0].data[9] = data[offset + 42 + 1];//KSL op2 - tmp[0].data[10] = data[offset + 40]; //FeedBack/Connection - - tmp[1].data[0] = data[offset + 52 + 5]; - tmp[1].data[1] = data[offset + 52 + 0]; - tmp[1].data[2] = data[offset + 52 + 7]; - tmp[1].data[3] = data[offset + 52 + 2]; - tmp[1].data[4] = data[offset + 52 + 8]; - tmp[1].data[5] = data[offset + 52 + 3]; - tmp[1].data[6] = data[offset + 52 + 9]; - tmp[1].data[7] = data[offset + 52 + 4]; - tmp[1].data[8] = data[offset + 52 + 6]; - tmp[1].data[9] = data[offset + 52 + 1]; - tmp[1].data[10] = data[offset + 41]; + tmp[0].d.op1_amvib = data[offset + 42 + 5];//AMVIB op1 + tmp[0].d.op2_amvib = data[offset + 42 + 0];//AMVIB op2 + tmp[0].d.op1_atdec = data[offset + 42 + 7];//AtDec op1 + tmp[0].d.op2_atdec = data[offset + 42 + 2];//AtDec op2 + tmp[0].d.op1_susrel = data[offset + 42 + 8];//SusRel op1 + tmp[0].d.op2_susrel = data[offset + 42 + 3];//SusRel op2 + tmp[0].d.op1_wave = data[offset + 42 + 9];//Wave op1 + tmp[0].d.op2_wave = data[offset + 42 + 4];//Wave op2 + tmp[0].d.op1_ksltl = data[offset + 42 + 6];//KSL op1 + tmp[0].d.op2_ksltl = data[offset + 42 + 1];//KSL op2 + tmp[0].d.fbconn = data[offset + 40]; //FeedBack/Connection + + tmp[1].d.op1_amvib = data[offset + 52 + 5]; + tmp[1].d.op2_amvib = data[offset + 52 + 0]; + tmp[1].d.op1_atdec = data[offset + 52 + 7]; + tmp[1].d.op2_atdec = data[offset + 52 + 2]; + tmp[1].d.op1_susrel = data[offset + 52 + 8]; + tmp[1].d.op2_susrel = data[offset + 52 + 3]; + tmp[1].d.op1_wave = data[offset + 52 + 9]; + tmp[1].d.op2_wave = data[offset + 52 + 4]; + tmp[1].d.op1_ksltl = data[offset + 52 + 6]; + tmp[1].d.op2_ksltl = data[offset + 52 + 1]; + tmp[1].d.fbconn = data[offset + 41]; /* * We will don't read two millisecond delays on tail of instrument * as there are will be re-calculated by measurer here. * Those fields are made for hot-loading while runtime, but not * for generation of embedded banks database. */ + db.toOps(tmp[0].d, ops, 0); + db.toOps(tmp[1].d, ops, 2); - tmp[0].finetune = int8_t(toSint16BE((const uint8_t *)data.data() + offset + 32)); - tmp[1].finetune = int8_t(toSint16BE((const uint8_t *)data.data() + offset + 34)); uint8_t flags = data[offset + 39]; - struct ins tmp2; - tmp2.notenum = is_percussion ? data[offset + 38] : 0; - bool real4op = (flags & (uint8_t)WOPL_Flags::Mode_4op) != 0; - tmp2.pseudo4op = (flags & (uint8_t)WOPL_Flags::Mode_DoubleVoice) != 0; - tmp2.real4op = real4op && !tmp2.pseudo4op; - tmp2.voice2_fine_tune = 0; - tmp2.midi_velocity_offset = (int8_t)data[offset + 36]; - tmp2.rhythmModeDrum = (flags & (uint8_t)WOPL_Flags::WOPL_RhythmModeMask); - tmp[0].diff = false; - tmp[1].diff = real4op && !tmp2.pseudo4op; - - int8_t fine_tune = (int8_t)data[offset + 37]; - if(fine_tune != 0) + //---------------- + inst.instFlags = flags; + inst.percussionKeyNumber = is_percussion ? data[offset + 38] : 0; + inst.noteOffset1 = int8_t(toSint16BE((const uint8_t *)data.data() + offset + 32)); + inst.noteOffset2 = int8_t(toSint16BE((const uint8_t *)data.data() + offset + 34)); + inst.secondVoiceDetune = static_cast<int_fast8_t>(data[offset + 37]); + inst.midiVelocityOffset = static_cast<int_fast8_t>(data[offset + 36]); + inst.fbConn = (static_cast<uint_fast16_t>(data[offset + 40])) | + (static_cast<uint_fast16_t>(data[offset + 41]) << 8); + if(version >= 2) { - // Simulate behavior of DMX second voice detune - tmp2.voice2_fine_tune = (double)((((int)fine_tune + 128) >> 1) - 64) / 32.0; + inst.delay_on_ms = toUint16BE((const uint8_t *)data.data() + offset + 62); + inst.delay_off_ms = toUint16BE((const uint8_t *)data.data() + offset + 64); } + //---------------- uint32_t gmno = is_percussion ? i + 128 : i; @@ -210,22 +228,12 @@ static bool LoadWopl(const char *fn, unsigned bank, const char *prefix) else snprintf(name2, 512, "%sM%u", prefix, i); - if(!real4op && !tmp2.pseudo4op) - { - size_t resno = InsertIns(tmp[0], tmp2, name, name2); - SetBank(bank, gmno, resno); - } - else - { - size_t resno = InsertIns(tmp[0], tmp[1], tmp2, name, name2); - SetBank(bank, gmno, resno); - } + db.addInstrument(bnk, i, inst, ops, fn); } + db.addMidiBank(bankDb, is_percussion, bnk); } } - SetBankSetup(bank, setup); - return true; } diff --git a/utils/gen_adldata/gen_adldata.cc b/utils/gen_adldata/gen_adldata.cc index 7cb4528..45f0c7c 100644 --- a/utils/gen_adldata/gen_adldata.cc +++ b/utils/gen_adldata/gen_adldata.cc @@ -15,17 +15,6 @@ #include "midi_inst_list.h" -#include "file_formats/load_ail.h" -#include "file_formats/load_bisqwit.h" -#include "file_formats/load_bnk2.h" -#include "file_formats/load_bnk.h" -#include "file_formats/load_ibk.h" -#include "file_formats/load_jv.h" -#include "file_formats/load_op2.h" -#include "file_formats/load_tmb.h" -#include "file_formats/load_wopl.h" -#include "file_formats/load_ea.h" - int main(int argc, char**argv) { if(argc == 1) @@ -39,21 +28,8 @@ int main(int argc, char**argv) const char *outFile_s = argv[1]; - FILE *outFile = std::fopen(outFile_s, "w"); - if(!outFile) - { - std::fprintf(stderr, "Can't open %s file for write!\n", outFile_s); - return 1; - } + BanksDump db; - std::fprintf(outFile, "\ -#include \"adldata.hh\"\n\ -\n\ -/* THIS OPL-3 FM INSTRUMENT DATA IS AUTOMATICALLY GENERATED\n\ - * FROM A NUMBER OF SOURCES, MOSTLY PC GAMES.\n\ - * PREPROCESSED, CONVERTED, AND POSTPROCESSED OFF-SCREEN.\n\ - */\n\ -"); { IniProcessing ini; if(!ini.open("banks.ini")) @@ -106,13 +82,13 @@ int main(int argc, char**argv) return 1; } - banknames.push_back(bank_name); +// banknames.push_back(bank_name); //printf("Loading %s...\n", filepath.c_str()); if(format == "AIL") { - if(!LoadMiles(filepath.c_str(), bank, prefix.c_str())) + if(!BankFormats::LoadMiles(db, filepath.c_str(), bank, bank_name, prefix.c_str())) { std::fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str()); return 1; @@ -121,7 +97,7 @@ int main(int argc, char**argv) else if(format == "Bisqwit") { - if(!LoadBisqwit(filepath.c_str(), bank, prefix.c_str())) + if(!BankFormats::LoadBisqwit(db, filepath.c_str(), bank, bank_name, prefix.c_str())) { std::fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str()); return 1; @@ -130,7 +106,7 @@ int main(int argc, char**argv) else if(format == "WOPL") { - if(!LoadWopl(filepath.c_str(), bank, prefix.c_str())) + if(!BankFormats::LoadWopl(db, filepath.c_str(), bank, bank_name, prefix.c_str())) { std::fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str()); return 1; @@ -139,7 +115,7 @@ int main(int argc, char**argv) else if(format == "OP2") { - if(!LoadDoom(filepath.c_str(), bank, prefix.c_str())) + if(!BankFormats::LoadDoom(db, filepath.c_str(), bank, bank_name, prefix.c_str())) { std::fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str()); return 1; @@ -148,7 +124,7 @@ int main(int argc, char**argv) else if(format == "EA") { - if(!LoadEA(filepath.c_str(), bank, prefix.c_str())) + if(!BankFormats::LoadEA(db, filepath.c_str(), bank, bank_name, prefix.c_str())) { std::fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str()); return 1; @@ -157,7 +133,7 @@ int main(int argc, char**argv) else if(format == "TMB") { - if(!LoadTMB(filepath.c_str(), bank, prefix.c_str())) + if(!BankFormats::LoadTMB(db, filepath.c_str(), bank, bank_name, prefix.c_str())) { std::fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str()); return 1; @@ -166,7 +142,7 @@ int main(int argc, char**argv) else if(format == "Junglevision") { - if(!LoadJunglevision(filepath.c_str(), bank, prefix.c_str())) + if(!BankFormats::LoadJunglevision(db, filepath.c_str(), bank, bank_name, prefix.c_str())) { std::fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str()); return 1; @@ -175,7 +151,7 @@ int main(int argc, char**argv) else if(format == "AdLibGold") { - if(!LoadBNK2(filepath.c_str(), bank, prefix.c_str(), filter_m, filter_p)) + if(!BankFormats::LoadBNK2(db, filepath.c_str(), bank, bank_name, prefix.c_str(), filter_m, filter_p)) { std::fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str()); return 1; @@ -184,14 +160,14 @@ int main(int argc, char**argv) else if(format == "HMI") { - if(!LoadBNK(filepath.c_str(), bank, prefix.c_str(), false, false)) + if(!BankFormats::LoadBNK(db, filepath.c_str(), bank, bank_name, prefix.c_str(), false, false)) { std::fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str()); return 1; } if(!filepath_d.empty()) { - if(!LoadBNK(filepath_d.c_str(),bank, prefix_d.c_str(), false, true)) + if(!BankFormats::LoadBNK(db, filepath_d.c_str(), bank, bank_name, prefix_d.c_str(), false, true)) { std::fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str()); return 1; @@ -201,7 +177,7 @@ int main(int argc, char**argv) else if(format == "IBK") { - if(!LoadIBK(filepath.c_str(), bank, prefix.c_str(), false)) + if(!BankFormats::LoadIBK(db, filepath.c_str(), bank, bank_name, prefix.c_str(), false)) { std::fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str()); return 1; @@ -209,7 +185,7 @@ int main(int argc, char**argv) if(!filepath_d.empty()) { //printf("Loading %s... \n", filepath_d.c_str()); - if(!LoadIBK(filepath_d.c_str(),bank, prefix_d.c_str(), true, noRhythmMode)) + if(!BankFormats::LoadIBK(db, filepath_d.c_str(),bank, bank_name, prefix_d.c_str(), true, noRhythmMode)) { std::fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str()); return 1; @@ -233,337 +209,26 @@ int main(int argc, char**argv) std::fflush(stdout); } - #if 0 - for(unsigned a = 0; a < 36 * 8; ++a) - { - if((1 << (a % 8)) > maxvalues[a / 8]) continue; - - const std::map<unsigned, unsigned> &data = Correlate[a]; - if(data.empty()) continue; - std::vector< std::pair<unsigned, unsigned> > correlations; - for(std::map<unsigned, unsigned>::const_iterator - i = data.begin(); - i != data.end(); - ++i) - { - correlations.push_back(std::make_pair(i->second, i->first)); - } - std::sort(correlations.begin(), correlations.end()); - std::fprintf(outFile, "Byte %2u bit %u=mask %02X:\n", a / 8, a % 8, 1 << (a % 8)); - for(size_t c = 0; c < correlations.size() && c < 10; ++c) - { - unsigned count = correlations[correlations.size() - 1 - c ].first; - unsigned index = correlations[correlations.size() - 1 - c ].second; - std::fprintf(outFile, "\tAdldata index %u, bit %u=mask %02X (%u matches)\n", - index / 8, index % 8, 1 << (index % 8), count); - } - } - #endif - - std::printf("Writing raw instrument data...\n"); - std::fflush(stdout); - { - std::fprintf(outFile, - /* - "static const struct\n" - "{\n" - " unsigned modulator_E862, carrier_E862; // See below\n" - " unsigned char modulator_40, carrier_40; // KSL/attenuation settings\n" - " unsigned char feedconn; // Feedback/connection bits for the channel\n" - " signed char finetune; // Finetune\n" - "} adl[] =\n"*/ - "const adldata adl[%u] =\n" - "{ // ,---------+-------- Wave select settings\n" - " // | ,-------ч-+------ Sustain/release rates\n" - " // | | ,-----ч-ч-+---- Attack/decay rates\n" - " // | | | ,---ч-ч-ч-+-- AM/VIB/EG/KSR/Multiple bits\n" - " // | | | | | | | |\n" - " // | | | | | | | | ,----+-- KSL/attenuation settings\n" - " // | | | | | | | | | | ,----- Feedback/connection bits\n" - " // | | | | | | | | | | | ,----- Fine tune\n\n" - " // | | | | | | | | | | | |\n" - " // | | | | | | | | | | | |\n", (unsigned)insdatatab.size()); - - for(size_t b = insdatatab.size(), c = 0; c < b; ++c) - { - for(std::map<insdata, std::pair<size_t, std::set<std::string> > > - ::const_iterator - i = insdatatab.begin(); - i != insdatatab.end(); - ++i) - { - if(i->second.first != c) continue; - std::fprintf(outFile, " { "); - - uint32_t carrier_E862 = - uint32_t(i->first.data[6] << 24) - + uint32_t(i->first.data[4] << 16) - + uint32_t(i->first.data[2] << 8) - + uint32_t(i->first.data[0] << 0); - uint32_t modulator_E862 = - uint32_t(i->first.data[7] << 24) - + uint32_t(i->first.data[5] << 16) - + uint32_t(i->first.data[3] << 8) - + uint32_t(i->first.data[1] << 0); - - std::fprintf(outFile, "0x%07X,0x%07X, 0x%02X,0x%02X, 0x%X, %+d", - carrier_E862, - modulator_E862, - i->first.data[8], - i->first.data[9], - i->first.data[10], - i->first.finetune); - -#ifdef ADLDATA_WITH_COMMENTS - std::string names; - for(std::set<std::string>::const_iterator - j = i->second.second.begin(); - j != i->second.second.end(); - ++j) - { - if(!names.empty()) names += "; "; - if((*j)[0] == '\377') - names += j->substr(1); - else - names += *j; - } - std::fprintf(outFile, " }, // %u: %s\n", (unsigned)c, names.c_str()); -#else - std::fprintf(outFile, " },\n"); -#endif - } - } - std::fprintf(outFile, "};\n"); - } - - /*std::fprintf(outFile, "static const struct\n" - "{\n" - " unsigned short adlno1, adlno2;\n" - " unsigned char tone;\n" - " unsigned char flags;\n" - " long ms_sound_kon; // Number of milliseconds it produces sound;\n" - " long ms_sound_koff;\n" - " double voice2_fine_tune;\n" - "} adlins[] =\n");*/ - - std::fprintf(outFile, "const struct adlinsdata adlins[%u] =\n", (unsigned)instab.size()); - std::fprintf(outFile, "{\n"); - MeasureThreaded measureCounter; + { + measureCounter.LoadCache("fm_banks/adldata-cache.dat"); + measureCounter.m_cache_matches = 0; + measureCounter.m_done = 0; + measureCounter.m_total = db.instruments.size(); std::printf("Beginning to generate measures data... (hardware concurrency of %d)\n", std::thread::hardware_concurrency()); std::fflush(stdout); - measureCounter.LoadCache("fm_banks/adldata-cache.dat"); - measureCounter.m_total = instab.size(); - for(size_t b = instab.size(), c = 0; c < b; ++c) + for(size_t b = 0; b < db.instruments.size(); ++b) { - for(std::map<ins, std::pair<size_t, std::set<std::string> > >::const_iterator i = instab.begin(); i != instab.end(); ++i) - { - if(i->second.first != c) continue; - measureCounter.run(i); - } + assert(db.instruments[b].instId == b); + measureCounter.run(db, db.instruments[b]); } std::fflush(stdout); measureCounter.waitAll(); measureCounter.SaveCache("fm_banks/adldata-cache.dat"); } - std::printf("Writing generated measure data...\n"); - std::fflush(stdout); - - std::vector<unsigned> adlins_flags; - - for(size_t b = instab.size(), c = 0; c < b; ++c) - for(std::map<ins, std::pair<size_t, std::set<std::string> > > - ::const_iterator - i = instab.begin(); - i != instab.end(); - ++i) - { - if(i->second.first != c) continue; - //DurationInfo info = MeasureDurations(i->first); - MeasureThreaded::DurationInfoCache::iterator indo_i = measureCounter.m_durationInfo.find(i->first); - DurationInfo info = indo_i->second; -#ifdef ADLDATA_WITH_COMMENTS - { - if(info.peak_amplitude_time == 0) - { - std::fprintf(outFile, - " // Amplitude begins at %6.1f,\n" - " // fades to 20%% at %.1fs, keyoff fades to 20%% in %.1fs.\n", - info.begin_amplitude, - info.quarter_amplitude_time / double(info.interval), - info.keyoff_out_time / double(info.interval)); - } - else - { - std::fprintf(outFile, - " // Amplitude begins at %6.1f, peaks %6.1f at %.1fs,\n" - " // fades to 20%% at %.1fs, keyoff fades to 20%% in %.1fs.\n", - info.begin_amplitude, - info.peak_amplitude_value, - info.peak_amplitude_time / double(info.interval), - info.quarter_amplitude_time / double(info.interval), - info.keyoff_out_time / double(info.interval)); - } - } -#endif - - unsigned flags = (i->first.pseudo4op ? ins::Flag_Pseudo4op : 0)| - (i->first.real4op ? ins::Flag_Real4op : 0) | - (info.nosound ? ins::Flag_NoSound : 0) | i->first.rhythmModeDrum; - - std::fprintf(outFile, " {"); - std::fprintf(outFile, "%4d,%4d,%3d, %d, %6" PRId64 ",%6" PRId64 ", %6d, %g", - (unsigned) i->first.insno1, - (unsigned) i->first.insno2, - (int)(i->first.notenum), - flags, - info.ms_sound_kon, - info.ms_sound_koff, - i->first.midi_velocity_offset, - i->first.voice2_fine_tune); - std::string names; - for(std::set<std::string>::const_iterator - j = i->second.second.begin(); - j != i->second.second.end(); - ++j) - { - if(!names.empty()) names += "; "; - if((*j)[0] == '\377') - names += j->substr(1); - else - names += *j; - } -#ifdef ADLDATA_WITH_COMMENTS - std::fprintf(outFile, " }, // %u: %s\n\n", (unsigned)c, names.c_str()); -#else - std::fprintf(outFile, " },\n"); -#endif - std::fflush(outFile); - adlins_flags.push_back(flags); - } - std::fprintf(outFile, "};\n\n"); - - - std::printf("Writing banks data...\n"); - std::fflush(stdout); - - //fprintf(outFile, "static const unsigned short banks[][256] =\n"); -#ifdef HARD_BANKS - const unsigned bankcount = sizeof(banknames) / sizeof(*banknames); -#else - const size_t bankcount = banknames.size(); -#endif - - size_t nosound = InsertNoSoundIns(); - - std::map<size_t, std::vector<size_t> > bank_data; - for(size_t bank = 0; bank < bankcount; ++bank) - { - //bool redundant = true; - std::vector<size_t> data(256); - for(size_t p = 0; p < 256; ++p) - { - size_t v = progs[bank][p]; - if(v == 0 || (adlins_flags[v - 1] & 2)) - v = nosound; // Blank.in - else - v -= 1; - data[p] = v; - } - bank_data[bank] = data; - } - std::set<size_t> listed; - - std::fprintf(outFile, - "\n\n//Returns total number of generated banks\n" - "int maxAdlBanks()\n" - "{\n" - " return %u;\n" - "}\n\n" - "const char* const banknames[%u] =\n", - (unsigned int)bankcount, - (unsigned int)(bankcount + 1)); - std::fprintf(outFile, "{\n"); - for(size_t bank = 0; bank < bankcount; ++bank) - std::fprintf(outFile, " \"%s\",\n", banknames[bank].c_str()); - std::fprintf(outFile, " NULL\n};\n"); - - std::fprintf(outFile, "const unsigned short banks[%u][256] =\n", (unsigned int)bankcount); - std::fprintf(outFile, "{\n"); - for(size_t bank = 0; bank < bankcount; ++bank) - { -#ifdef ADLDATA_WITH_COMMENTS - std::fprintf(outFile, " { // bank %u, %s\n", bank, banknames[bank].c_str()); -#else - std::fprintf(outFile, " {\n"); - #endif -#ifdef ADLDATA_WITH_COMMENTS - bool redundant = true; -#endif - for(size_t p = 0; p < 256; ++p) - { - size_t v = bank_data[bank][p]; - if(listed.find(v) == listed.end()) - { - listed.insert(v); -#ifdef ADLDATA_WITH_COMMENTS - redundant = false; -#endif - } - std::fprintf(outFile, "%4d,", (unsigned int)v); - if(p % 16 == 15) fprintf(outFile, "\n"); - } - std::fprintf(outFile, " },\n"); -#ifdef ADLDATA_WITH_COMMENTS - if(redundant) - { - std::fprintf(outFile, " // Bank %u defines nothing new.\n", bank); - for(unsigned refbank = 0; refbank < bank; ++refbank) - { - bool match = true; - for(unsigned p = 0; p < 256; ++p) - if(bank_data[bank][p] != nosound - && bank_data[bank][p] != bank_data[refbank][p]) - { - match = false; - break; - } - if(match) - std::fprintf(outFile, " // Bank %u is just a subset of bank %u!\n", - bank, refbank); - } - } -#endif - } - - std::fprintf(outFile, "};\n\n"); - std::fflush(outFile); - - std::fprintf(outFile, "const AdlBankSetup adlbanksetup[%u] =\n", (unsigned)banksetup.size()); - std::fprintf(outFile, "{\n"); - { - BankSetupData::iterator last = banksetup.end(); - last--; - for(BankSetupData::iterator it = banksetup.begin(); it != banksetup.end(); it++) - { - AdlBankSetup &setup = it->second; - std::fprintf(outFile, " {%d, %d, %d, %d}", - setup.volumeModel, - setup.deepTremolo, - setup.deepVibrato, - setup.scaleModulators); - if(it != last) - std::fprintf(outFile, ", //Bank %u, %s\n", (unsigned)it->first, banknames[it->first].c_str()); - else - std::fprintf(outFile, " //Bank %u, %s\n", (unsigned)it->first, banknames[it->first].c_str()); - } - } - std::fprintf(outFile, "};\n"); - std::fflush(outFile); - - std::fclose(outFile); + db.exportBanks(std::string(outFile_s)); std::printf("Generation of ADLMIDI data has been completed!\n"); std::fflush(stdout); diff --git a/utils/gen_adldata/measurer.cpp b/utils/gen_adldata/measurer.cpp index 4f28671..af9e0da 100644 --- a/utils/gen_adldata/measurer.cpp +++ b/utils/gen_adldata/measurer.cpp @@ -1,6 +1,11 @@ #include "measurer.h" +#include "file_formats/common.h" #include <cmath> +#ifdef GEN_ADLDATA_DEEP_DEBUG +#include "../midiplay/wave_writer.h" +#endif + #ifndef M_PI #define M_PI 3.14159265358979323846 #endif @@ -16,6 +21,56 @@ # include "../../src/chips/dosbox_opl3.h" #endif +#define NUM_OF_CHANNELS 23 +#define NUM_OF_RM_CHANNELS 5 + +//! Per-channel and per-operator registers map +static const uint16_t g_operatorsMap[(NUM_OF_CHANNELS + NUM_OF_RM_CHANNELS) * 2] = +{ + // Channels 0-2 + 0x000, 0x003, 0x001, 0x004, 0x002, 0x005, // operators 0, 3, 1, 4, 2, 5 + // Channels 3-5 + 0x008, 0x00B, 0x009, 0x00C, 0x00A, 0x00D, // operators 6, 9, 7,10, 8,11 + // Channels 6-8 + 0x010, 0x013, 0x011, 0x014, 0x012, 0x015, // operators 12,15, 13,16, 14,17 + // Same for second card + 0x100, 0x103, 0x101, 0x104, 0x102, 0x105, // operators 18,21, 19,22, 20,23 + 0x108, 0x10B, 0x109, 0x10C, 0x10A, 0x10D, // operators 24,27, 25,28, 26,29 + 0x110, 0x113, 0x111, 0x114, 0x112, 0x115, // operators 30,33, 31,34, 32,35 + + //==For Rhythm-mode percussions + // Channel 18 + 0x010, 0x013, // operators 12,15 + // Channel 19 + 0xFFF, 0x014, // operator 16 + // Channel 19 + 0x012, 0xFFF, // operator 14 + // Channel 19 + 0xFFF, 0x015, // operator 17 + // Channel 19 + 0x011, 0xFFF, // operator 13 + + //==For Rhythm-mode percussions in CMF, snare and cymbal operators has inverted! + 0x010, 0x013, // operators 12,15 + // Channel 19 + 0x014, 0xFFF, // operator 16 + // Channel 19 + 0x012, 0xFFF, // operator 14 + // Channel 19 + 0x015, 0xFFF, // operator 17 + // Channel 19 + 0x011, 0xFFF // operator 13 +}; + +//! Channel map to regoster offsets +static const uint16_t g_channelsMap[NUM_OF_CHANNELS] = +{ + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, // 0..8 + 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, // 9..17 (secondary set) + 0x006, 0x007, 0x008, 0x008, 0x008 // <- hw percussions, hihats and cymbals using tom-tom's channel as pitch source +}; + + template <class T> class AudioHistory { @@ -87,94 +142,116 @@ struct TinySynth { OPLChipBase *m_chip; unsigned m_notesNum; - int m_notenum; - int8_t m_fineTune; + unsigned m_actualNotesNum; + bool m_isReal4op; + bool m_isPseudo4op; + bool m_isRhythmMode; + int m_playNoteNum; + int8_t m_voice1Detune; int16_t m_noteOffsets[2]; unsigned m_x[2]; + bool m_isSilentGuess; + + void writeReg(uint16_t addr, uint8_t data) + { + m_chip->writeReg(addr, data); + } + void resetChip() { - static const short initdata[(2 + 3 + 2 + 2) * 2] = + static const short initdata[] = { - 0x004, 96, 0x004, 128, // Pulse timer - 0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable, leave disabled - 0x001, 32, 0x0BD, 0 // Enable wave & melodic + 0x004, 96, 0x004, 128, // Pulse timer + 0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable + 0x001, 32, 0x105, 1 // Enable wave, OPL3 extensions }; m_chip->setRate(g_outputRate); - for(unsigned a = 0; a < 18; a += 2) - m_chip->writeReg((uint16_t)initdata[a], (uint8_t)initdata[a + 1]); + for(size_t a = 0; a < 18; ++a) + writeReg(0xB0 + g_channelsMap[a], 0x00); + for(unsigned a = 0; a < 14; a += 2) + writeReg((uint16_t)initdata[a], (uint8_t)initdata[a + 1]); } - void setInstrument(const ins &in) + void setInstrument(const BanksDump &db, const BanksDump::InstrumentEntry &ins) { - insdata rawData[2]; - bool found[2] = {false, false}; - for(InstrumentDataTab::const_iterator j = insdatatab.begin(); - j != insdatatab.end(); - ++j) + bool isPseudo4ops = ((ins.instFlags & BanksDump::InstrumentEntry::WOPL_Ins_Pseudo4op) != 0); + bool is4ops = ((ins.instFlags & BanksDump::InstrumentEntry::WOPL_Ins_4op) != 0) && !isPseudo4ops; + m_isRhythmMode = ((ins.instFlags & BanksDump::InstrumentEntry::WOPL_RhythmModeMask) != 0); + size_t opsNum = (is4ops || isPseudo4ops) ? 4 : 2; + BanksDump::Operator ops[4]; + assert(ins.ops[0] >= 0); + assert(ins.ops[1] >= 0); + ops[0] = db.operators[ins.ops[0]]; + ops[1] = db.operators[ins.ops[1]]; + if(opsNum > 2) { - if(j->second.first == in.insno1) - { - rawData[0] = j->first; - found[0] = true; - if(found[1]) break; - } - if(j->second.first == in.insno2) - { - rawData[1] = j->first; - found[1] = true; - if(found[0]) break; - } + assert(ins.ops[2] >= 0); + assert(ins.ops[3] >= 0); + ops[2] = db.operators[ins.ops[2]]; + ops[3] = db.operators[ins.ops[3]]; } std::memset(m_x, 0, sizeof(m_x)); - m_notenum = in.notenum >= 128 ? (in.notenum - 128) : in.notenum; - if(m_notenum == 0) - m_notenum = 25; - m_notesNum = in.insno1 == in.insno2 ? 1 : 2; - m_fineTune = 0; - m_noteOffsets[0] = rawData[0].finetune; - m_noteOffsets[1] = rawData[1].finetune; - if(in.pseudo4op) - m_fineTune = in.voice2_fine_tune; - if(in.real4op) - { - m_chip->writeReg(0x105, 1); - m_chip->writeReg(0x104, 0xFF); - } - - //For clearer measurement, disable tremolo and vibrato - rawData[0].data[0] &= 0x3F; - rawData[0].data[1] &= 0x3F; - rawData[1].data[0] &= 0x3F; - rawData[1].data[1] &= 0x3F; + m_playNoteNum = ins.percussionKeyNumber >= 128 ? (ins.percussionKeyNumber - 128) : ins.percussionKeyNumber; + m_isReal4op = is4ops; + m_isPseudo4op = isPseudo4ops; + if(m_playNoteNum == 0) + m_playNoteNum = 25;//60; + m_notesNum = opsNum / 2; + m_actualNotesNum = (m_isReal4op ? 1 : m_notesNum); + m_voice1Detune = 0; + m_noteOffsets[0] = ins.noteOffset1; + m_noteOffsets[1] = ins.noteOffset2; + if(isPseudo4ops) + m_voice1Detune = ins.secondVoiceDetune; + writeReg(0x104, m_isReal4op ? 0x3F : 0x00); + + //For cleaner measurement, disable tremolo and vibrato + ops[0].d_E862 &= 0xFFFFFF3F; + ops[1].d_E862 &= 0xFFFFFF3F; + ops[2].d_E862 &= 0xFFFFFF3F; + ops[3].d_E862 &= 0xFFFFFF3F; for(unsigned n = 0; n < m_notesNum; ++n) { - static const unsigned char patchdata[11] = - {0x20, 0x23, 0x60, 0x63, 0x80, 0x83, 0xE0, 0xE3, 0x40, 0x43, 0xC0}; - for(unsigned a = 0; a < 10; ++a) - m_chip->writeReg(patchdata[a] + n * 8, rawData[n].data[a]); - m_chip->writeReg(patchdata[10] + n * 8, rawData[n].data[10] | 0x30); + static const uint8_t data[4] = {0x20, 0x60, 0x80, 0xE0}; + size_t opOffset = (n * 2); + size_t opMapOffset = m_isReal4op ? (n * 6) : opOffset; + uint16_t op1off = g_operatorsMap[opMapOffset + 0]; + uint16_t op2off = g_operatorsMap[opMapOffset + 1]; + uint_fast32_t x1 = ops[opOffset + 0].d_E862, y1 = ops[opOffset + 1].d_E862; + uint_fast8_t x2 = ops[opOffset + 0].d_40, y2 = ops[opOffset + 1].d_40; + uint_fast8_t fbConn = (ins.fbConn >> (n * 8)) & 0xFF; + + for(size_t a = 0; a < 4; ++a, x1 >>= 8, y1 >>= 8) + { + writeReg(data[a] + op1off, x1 & 0xFF); + writeReg(data[a] + op2off, y1 & 0xFF); + } + writeReg(0xC0 + g_channelsMap[m_isReal4op ? (n * 3) : n], fbConn | 0x30); + + writeReg(0x40 + op1off, x2 & 0xFF); + writeReg(0x40 + op2off, y2 & 0xFF); } } void noteOn() { std::memset(m_x, 0, sizeof(m_x)); - for(unsigned n = 0; n < m_notesNum; ++n) + for(unsigned n = 0; n < m_actualNotesNum; ++n) { - double hertz = 172.00093 * std::exp(0.057762265 * (m_notenum + m_noteOffsets[n])); + double hertz = 172.00093 * std::exp(0.057762265 * (m_playNoteNum + m_noteOffsets[n])); if(hertz > 131071) { std::fprintf(stdout, "%s:%d:0: warning: Why does note %d + note-offset %d produce hertz %g?\n", __FILE__, __LINE__, - m_notenum, m_noteOffsets[n], hertz); + m_playNoteNum, m_noteOffsets[n], hertz); std::fflush(stdout); hertz = 131071; } - m_x[n] = 0x2000; + m_x[n] = 0x2000u; while(hertz >= 1023.5) { hertz /= 2.0; // Calculate octave @@ -183,16 +260,16 @@ struct TinySynth m_x[n] += (unsigned int)(hertz + 0.5); // Keyon the note - m_chip->writeReg(0xA0 + n * 3, m_x[n] & 0xFF); - m_chip->writeReg(0xB0 + n * 3, m_x[n] >> 8); + writeReg(0xA0 + g_channelsMap[n], m_x[n] & 0xFF); + writeReg(0xB0 + g_channelsMap[n], (m_x[n] >> 8) & 0xFF); } } void noteOff() { // Keyoff the note - for(unsigned n = 0; n < m_notesNum; ++n) - m_chip->writeReg(0xB0 + n * 3, (m_x[n] >> 8) & 0xDF); + for(unsigned n = 0; n < m_actualNotesNum; ++n) + writeReg(0xB0 + g_channelsMap[n], (m_x[n] >> 8) & 0xDF); } void generate(int16_t *output, size_t frames) @@ -201,8 +278,7 @@ struct TinySynth } }; - -DurationInfo MeasureDurations(const ins &in, OPLChipBase *chip) +DurationInfo MeasureDurations(BanksDump &db, const BanksDump::InstrumentEntry &ins, OPLChipBase *chip) { AudioHistory<double> audioHistory; @@ -219,9 +295,34 @@ DurationInfo MeasureDurations(const ins &in, OPLChipBase *chip) TinySynth synth; synth.m_chip = chip; synth.resetChip(); - synth.setInstrument(in); + synth.setInstrument(db, ins); synth.noteOn(); + if(synth.m_isRhythmMode) // Skip rhythm-mode check + { + DurationInfo result; + std::memset(&result, 0, sizeof(DurationInfo)); + result.ms_sound_kon = 1000; + result.ms_sound_koff = 10; + result.nosound = false; + db.instruments[ins.instId].delay_on_ms = result.ms_sound_kon; + db.instruments[ins.instId].delay_off_ms = result.ms_sound_koff; + return result; + } + +#ifdef GEN_ADLDATA_DEEP_DEBUG + /*****************DEBUG******************/ + char waveFileOut[80] = ""; + std::snprintf(waveFileOut, 80, "fm_banks/_deep_debug/%04lu_%s_%u_an_%u_no.wav", + ins.instId, synth.m_isPseudo4op ? "pseudo4op" : + synth.m_isReal4op ? "4op" : "2op", + synth.m_actualNotesNum, + synth.m_notesNum); + void *waveCtx = ctx_wave_open(g_outputRate, waveFileOut); + ctx_wave_enable_stereo(waveCtx); + /*****************DEBUG******************/ +#endif + /* For capturing */ const unsigned max_silent = 6; const unsigned max_on = 40; @@ -260,6 +361,11 @@ DurationInfo MeasureDurations(const ins &in, OPLChipBase *chip) size_t blocksize = samples_per_interval - i; blocksize = (blocksize < audioBufferLength) ? blocksize : audioBufferLength; synth.generate(audioBuffer, blocksize); +#ifdef GEN_ADLDATA_DEEP_DEBUG + /***************DEBUG******************/ + ctx_wave_write(waveCtx, audioBuffer, blocksize * 2); + /***************DEBUG******************/ +#endif for (unsigned j = 0; j < blocksize; ++j) { int16_t s = audioBuffer[2 * j]; @@ -318,7 +424,7 @@ DurationInfo MeasureDurations(const ins &in, OPLChipBase *chip) { // Reset the emulator and re-run the "ON" simulation until reaching the peak time synth.resetChip(); - synth.setInstrument(in); + synth.setInstrument(db, ins); synth.noteOn(); audioHistory.reset(std::ceil(historyLength * g_outputRate)); @@ -387,17 +493,66 @@ DurationInfo MeasureDurations(const ins &in, OPLChipBase *chip) result.ms_sound_kon = (int64_t)(quarter_amplitude_time * 1000.0 / interval); result.ms_sound_koff = (int64_t)(keyoff_out_time * 1000.0 / interval); - result.nosound = (peak_amplitude_value < 0.5) || ((sound_min >= -1) && (sound_max <= 1)); + result.nosound = (peak_amplitude_value < 0.5) || ((sound_min >= -19) && (sound_max <= 18)); + + db.instruments[ins.instId].delay_on_ms = result.ms_sound_kon; + db.instruments[ins.instId].delay_off_ms = result.ms_sound_koff; + if(result.nosound) + db.instruments[ins.instId].instFlags |= BanksDump::InstrumentEntry::WOPL_Ins_IsBlank; + +#ifdef GEN_ADLDATA_DEEP_DEBUG + /***************DEBUG******************/ + ctx_wave_close(waveCtx); + /***************DEBUG******************/ +#endif + { + bool silent1 = result.nosound; + bool silent2 = BanksDump::isSilent(db, ins); + if(silent1 != silent2) + { + std::fprintf(stdout, + "\n\n%04lu - %s AN=%u NN=%u -- con1=%lu, con2=%lu\n%s computed - %s actual (%g peak, %d<%d)\n\n", + ins.instId, synth.m_isPseudo4op ? "pseudo4op" : + synth.m_isReal4op ? "4op" : "2op", + synth.m_actualNotesNum, + synth.m_notesNum, + (ins.fbConn) & 0x01, + (ins.fbConn >> 8) & 0x01, + silent2 ? "silent" : "sound", + silent1 ? "silent" : "sound", + peak_amplitude_value, + sound_min, + sound_max); + for(auto &sss : ins.instMetas) + std::fprintf(stdout, "%s\n", sss.c_str()); + BanksDump::isSilent(db, ins, true); + std::fprintf(stdout, "\n\n"); + std::fflush(stdout); +// assert(silent1 == silent2); +// exit(1); + } + } return result; } + +MeasureThreaded::MeasureThreaded() : + m_semaphore(int(std::thread::hardware_concurrency()) * 2), + m_done(0), + m_cache_matches(0) +{ + DosBoxOPL3::globalPreInit(); +} + void MeasureThreaded::LoadCache(const char *fileName) { + m_durationInfo.clear(); + FILE *in = std::fopen(fileName, "rb"); if(!in) { - std::printf("Failed to load cache: file is not exists.\n" + std::printf("Failed to load CacheX: file is not exists.\n" "Complete data will be generated from scratch.\n"); std::fflush(stdout); return; @@ -407,157 +562,75 @@ void MeasureThreaded::LoadCache(const char *fileName) if(std::fread(magic, 1, 32, in) != 32) { std::fclose(in); - std::printf("Failed to load cache: can't read magic.\n" + std::printf("Failed to load CacheX: can't read magic.\n" "Complete data will be generated from scratch.\n"); std::fflush(stdout); return; } - if(memcmp(magic, "ADLMIDI-DURATION-CACHE-FILE-V1.0", 32) != 0) + if(std::memcmp(magic, "ADLMIDI-DURATION-CACHE-FILE-V2.0", 32) != 0) { std::fclose(in); - std::printf("Failed to load cache: magic missmatch.\n" + std::printf("Failed to load CacheX: magic missmatch.\n" "Complete data will be generated from scratch.\n"); std::fflush(stdout); return; } - while(!std::feof(in)) + uint_fast32_t itemsCount; + uint8_t itemsCountA[4]; + if(std::fread(itemsCountA, 1, 4, in) != 4) { - DurationInfo info; - ins inst; - //got by instrument - insdata id[2]; - size_t insNo[2] = {0, 0}; - bool found[2] = {false, false}; - //got from file - insdata id_f[2]; - bool found_f[2] = {false, false}; - bool isMatches = false; - - memset(id, 0, sizeof(insdata) * 2); - memset(id_f, 0, sizeof(insdata) * 2); - memset(&info, 0, sizeof(DurationInfo)); - memset(&inst, 0, sizeof(ins)); - - //Instrument - uint64_t inval; - if(std::fread(&inval, 1, sizeof(uint64_t), in) != sizeof(uint64_t)) - break; - inst.insno1 = inval; - if(std::fread(&inval, 1, sizeof(uint64_t), in) != sizeof(uint64_t)) - break; - inst.insno2 = inval; - if(std::fread(&inst.notenum, 1, 1, in) != 1) - break; - if(std::fread(&inst.real4op, 1, 1, in) != 1) - break; - if(std::fread(&inst.pseudo4op, 1, 1, in) != 1) - break; - if(std::fread(&inst.voice2_fine_tune, sizeof(double), 1, in) != 1) - break; + std::fclose(in); + std::printf("Failed to load CacheX: can't read cache size value.\n" + "Complete data will be generated from scratch.\n"); + std::fflush(stdout); + return; + } - //Instrument data - if(fread(found_f, 1, 2 * sizeof(bool), in) != sizeof(bool) * 2) - break; - for(size_t i = 0; i < 2; i++) - { - if(fread(id_f[i].data, 1, 11, in) != 11) - break; - if(fread(&id_f[i].finetune, 1, 1, in) != 1) - break; - if(fread(&id_f[i].diff, 1, sizeof(bool), in) != sizeof(bool)) - break; - } + itemsCount = static_cast<uint_fast32_t>(toUint32LE(itemsCountA)); - if(found_f[0] || found_f[1]) - { - for(InstrumentDataTab::const_iterator j = insdatatab.begin(); j != insdatatab.end(); ++j) - { - if(j->second.first == inst.insno1) - { - id[0] = j->first; - found[0] = (id[0] == id_f[0]); - insNo[0] = inst.insno1; - if(found[1]) break; - } - if(j->second.first == inst.insno2) - { - id[1] = j->first; - found[1] = (id[1] == id_f[1]); - insNo[1] = inst.insno2; - if(found[0]) break; - } - } + while(!std::feof(in) && itemsCount > 0) + { + OperatorsKey k; + DurationInfo v; - //Find instrument entries are matching - if((found[0] != found_f[0]) || (found[1] != found_f[1])) - { - for(InstrumentDataTab::const_iterator j = insdatatab.begin(); j != insdatatab.end(); ++j) - { - if(found_f[0] && (j->first == id_f[0])) - { - found[0] = true; - insNo[0] = j->second.first; - } - if(found_f[1] && (j->first == id_f[1])) - { - found[1] = true; - insNo[1] = j->second.first; - } - if(found[0] && !found_f[1]) - { - isMatches = true; - break; - } - if(found[0] && found[1]) - { - isMatches = true; - break; - } - } - } - else - { - isMatches = true; - } + uint8_t data_k[5]; - //Then find instrument entry that uses found instruments - if(isMatches) + for(auto &kv : k) + { + uint8_t data[4]; + auto ret = std::fread(data, 1, 4, in); + if(ret != 4) { - inst.insno1 = insNo[0]; - inst.insno2 = insNo[1]; - InstrumentsData::iterator d = instab.find(inst); - if(d == instab.end()) - isMatches = false; + std::fclose(in); + std::printf("Failed to load CacheX: unexpected end of file.\n" + "Complete data will be generated from scratch.\n"); + std::fflush(stdout); + return; } + kv = static_cast<int_fast32_t>(toSint32LE(data)); } - //Duration data - if(std::fread(&info.peak_amplitude_time, 1, sizeof(uint64_t), in) != sizeof(uint64_t)) - break; - if(std::fread(&info.peak_amplitude_value, 1, sizeof(double), in) != sizeof(double)) - break; - if(std::fread(&info.quarter_amplitude_time, 1, sizeof(double), in) != sizeof(double)) - break; - if(std::fread(&info.begin_amplitude, 1, sizeof(double), in) != sizeof(double)) - break; - if(std::fread(&info.interval, 1, sizeof(double), in) != sizeof(double)) - break; - if(std::fread(&info.keyoff_out_time, 1, sizeof(double), in) != sizeof(double)) - break; - if(std::fread(&info.ms_sound_kon, 1, sizeof(int64_t), in) != sizeof(int64_t)) - break; - if(std::fread(&info.ms_sound_koff, 1, sizeof(int64_t), in) != sizeof(int64_t)) - break; - if(std::fread(&info.nosound, 1, sizeof(bool), in) != sizeof(bool)) - break; + auto ret = std::fread(data_k, 1, 5, in); + if(ret != 5) + { + std::fclose(in); + std::printf("Failed to load CacheX: unexpected end of file.\n" + "Complete data will be generated from scratch.\n"); + std::fflush(stdout); + return; + } - if(isMatches)//Store only if cached entry matches actual raw instrument data - m_durationInfo.insert({inst, info}); + v.ms_sound_kon = static_cast<int_fast64_t>(toUint16LE(data_k + 0)); + v.ms_sound_koff = static_cast<int_fast64_t>(toUint16LE(data_k + 2)); + v.nosound = (data_k[4] == 0x01); + + m_durationInfo.insert({k, v}); + itemsCount--; } - std::printf("Cache loaded!\n"); + std::printf("CacheX loaded!\n"); std::fflush(stdout); std::fclose(in); @@ -566,57 +639,44 @@ void MeasureThreaded::LoadCache(const char *fileName) void MeasureThreaded::SaveCache(const char *fileName) { FILE *out = std::fopen(fileName, "wb"); - fprintf(out, "ADLMIDI-DURATION-CACHE-FILE-V1.0"); - for(DurationInfoCache::iterator it = m_durationInfo.begin(); it != m_durationInfo.end(); it++) + std::fprintf(out, "ADLMIDI-DURATION-CACHE-FILE-V2.0"); + + uint_fast32_t itemsCount = static_cast<uint_fast32_t>(m_durationInfo.size()); + uint8_t itemsCountA[4] = + { + static_cast<uint8_t>((itemsCount >> 0) & 0xFF), + static_cast<uint8_t>((itemsCount >> 8) & 0xFF), + static_cast<uint8_t>((itemsCount >> 16) & 0xFF), + static_cast<uint8_t>((itemsCount >> 24) & 0xFF) + }; + std::fwrite(itemsCountA, 1, 4, out); + + for(DurationInfoCacheX::iterator it = m_durationInfo.begin(); it != m_durationInfo.end(); it++) { - const ins &in = it->first; - insdata id[2]; - bool found[2] = {false, false}; - memset(id, 0, sizeof(insdata) * 2); - - uint64_t outval; - outval = in.insno1; - fwrite(&outval, 1, sizeof(uint64_t), out); - outval = in.insno2; - fwrite(&outval, 1, sizeof(uint64_t), out); - fwrite(&in.notenum, 1, 1, out); - fwrite(&in.real4op, 1, 1, out); - fwrite(&in.pseudo4op, 1, 1, out); - fwrite(&in.voice2_fine_tune, sizeof(double), 1, out); - - for(InstrumentDataTab::const_iterator j = insdatatab.begin(); j != insdatatab.end(); ++j) + const OperatorsKey &k = it->first; + const DurationInfo &v = it->second; + + uint8_t data_k[5] = { - if(j->second.first == in.insno1) - { - id[0] = j->first; - found[0] = true; - if(found[1]) break; - } - if(j->second.first == in.insno2) - { - id[1] = j->first; - found[1] = true; - if(found[0]) break; - } - } + static_cast<uint8_t>((v.ms_sound_kon >> 0) & 0xFF), + static_cast<uint8_t>((v.ms_sound_kon >> 8) & 0xFF), + static_cast<uint8_t>((v.ms_sound_koff >> 0) & 0xFF), + static_cast<uint8_t>((v.ms_sound_koff >> 8) & 0xFF), + static_cast<uint8_t>(v.nosound ? 0x01 : 0x00) + }; - fwrite(found, 1, 2 * sizeof(bool), out); - for(size_t i = 0; i < 2; i++) + for(auto &kv : k) { - fwrite(id[i].data, 1, 11, out); - fwrite(&id[i].finetune, 1, 1, out); - fwrite(&id[i].diff, 1, sizeof(bool), out); + uint8_t data[4] = + { + static_cast<uint8_t>((kv >> 0) & 0xFF), + static_cast<uint8_t>((kv >> 8) & 0xFF), + static_cast<uint8_t>((kv >> 16) & 0xFF), + static_cast<uint8_t>((kv >> 24) & 0xFF) + }; + std::fwrite(data, 1, 4, out); } - - fwrite(&it->second.peak_amplitude_time, 1, sizeof(uint64_t), out); - fwrite(&it->second.peak_amplitude_value, 1, sizeof(double), out); - fwrite(&it->second.quarter_amplitude_time, 1, sizeof(double), out); - fwrite(&it->second.begin_amplitude, 1, sizeof(double), out); - fwrite(&it->second.interval, 1, sizeof(double), out); - fwrite(&it->second.keyoff_out_time, 1, sizeof(double), out); - fwrite(&it->second.ms_sound_kon, 1, sizeof(int64_t), out); - fwrite(&it->second.ms_sound_koff, 1, sizeof(int64_t), out); - fwrite(&it->second.nosound, 1, sizeof(bool), out); + std::fwrite(data_k, 1, 5, out); } std::fclose(out); } @@ -652,7 +712,7 @@ void MeasureThreaded::printFinal() std::fflush(stdout); } -void MeasureThreaded::run(InstrumentsData::const_iterator i) +void MeasureThreaded::run(BanksDump &bd, BanksDump::InstrumentEntry &e) { m_semaphore.wait(); if(m_threads.size() > 0) @@ -670,7 +730,8 @@ void MeasureThreaded::run(InstrumentsData::const_iterator i) } destData *dd = new destData; - dd->i = i; + dd->bd = &bd; + dd->bd_ins = &e; dd->myself = this; dd->start(); m_threads.push_back(dd); @@ -701,18 +762,33 @@ void MeasureThreaded::destData::callback(void *myself) { destData *s = reinterpret_cast<destData *>(myself); DurationInfo info; - DosBoxOPL3 dosbox; - DurationInfoCache::iterator cachedEntry = s->myself->m_durationInfo.find(s->i->first); + DosBoxOPL3 chip; + // NukedOPL3 chip; + + OperatorsKey ok = {s->bd_ins->ops[0], s->bd_ins->ops[1], s->bd_ins->ops[2], s->bd_ins->ops[3], + static_cast<int_fast32_t>(s->bd_ins->fbConn), + s->bd_ins->noteOffset1, s->bd_ins->noteOffset2, + static_cast<int_fast32_t>(s->bd_ins->percussionKeyNumber), + static_cast<int_fast32_t>(s->bd_ins->instFlags), + static_cast<int_fast32_t>(s->bd_ins->secondVoiceDetune)}; + s->myself->m_durationInfo_mx.lock(); + DurationInfoCacheX::iterator cachedEntry = s->myself->m_durationInfo.find(ok); + bool atEnd = cachedEntry == s->myself->m_durationInfo.end(); + s->myself->m_durationInfo_mx.unlock(); - if(cachedEntry != s->myself->m_durationInfo.end()) + if(!atEnd) { + const DurationInfo &di = cachedEntry->second; + s->bd_ins->delay_on_ms = di.ms_sound_kon; + s->bd_ins->delay_off_ms = di.ms_sound_koff; + if(di.nosound) + s->bd_ins->instFlags |= BanksDump::InstrumentEntry::WOPL_Ins_IsBlank; s->myself->m_cache_matches++; goto endWork; } - - info = MeasureDurations(s->i->first, &dosbox); + info = MeasureDurations(*s->bd, *s->bd_ins, &chip); s->myself->m_durationInfo_mx.lock(); - s->myself->m_durationInfo.insert({s->i->first, info}); + s->myself->m_durationInfo.insert({ok, info}); s->myself->m_durationInfo_mx.unlock(); endWork: diff --git a/utils/gen_adldata/measurer.h b/utils/gen_adldata/measurer.h index 63475ca..e521451 100644 --- a/utils/gen_adldata/measurer.h +++ b/utils/gen_adldata/measurer.h @@ -6,6 +6,7 @@ #include <mutex> #include <condition_variable> #include <thread> +#include <array> #include "progs_cache.h" @@ -54,17 +55,14 @@ private: struct MeasureThreaded { - typedef std::map<ins, DurationInfo> DurationInfoCache; + typedef std::array<int_fast32_t, 10> OperatorsKey; + typedef std::map<OperatorsKey, DurationInfo> DurationInfoCacheX; - MeasureThreaded() : - m_semaphore(int(std::thread::hardware_concurrency()) * 2), - m_done(0), - m_cache_matches(0) - {} + MeasureThreaded(); Semaphore m_semaphore; std::mutex m_durationInfo_mx; - DurationInfoCache m_durationInfo; + DurationInfoCacheX m_durationInfo; std::atomic_bool m_delete_tail; size_t m_total = 0; std::atomic<size_t> m_done; @@ -84,7 +82,8 @@ struct MeasureThreaded m_work.join(); } MeasureThreaded *myself; - std::map<ins, std::pair<size_t, std::set<std::string> > >::const_iterator i; + BanksDump *bd; + BanksDump::InstrumentEntry *bd_ins; std::thread m_work; std::atomic_bool m_works; @@ -96,11 +95,12 @@ struct MeasureThreaded void printProgress(); void printFinal(); - void run(InstrumentsData::const_iterator i); + + void run(BanksDump &bd, BanksDump::InstrumentEntry &e); void waitAll(); }; class OPLChipBase; -extern DurationInfo MeasureDurations(const ins &in, OPLChipBase *chip); +extern DurationInfo MeasureDurations(const BanksDump &db, const BanksDump::InstrumentEntry &ins, OPLChipBase *chip); #endif // MEASURER_H diff --git a/utils/gen_adldata/progs_cache.cpp b/utils/gen_adldata/progs_cache.cpp index 10124a8..60518ba 100644 --- a/utils/gen_adldata/progs_cache.cpp +++ b/utils/gen_adldata/progs_cache.cpp @@ -1,123 +1,427 @@ #include "progs_cache.h" -InstrumentDataTab insdatatab; +#include "file_formats/load_ail.h" +#include "file_formats/load_bisqwit.h" +#include "file_formats/load_bnk2.h" +#include "file_formats/load_bnk.h" +#include "file_formats/load_ibk.h" +#include "file_formats/load_jv.h" +#include "file_formats/load_op2.h" +#include "file_formats/load_tmb.h" +#include "file_formats/load_wopl.h" +#include "file_formats/load_ea.h" -InstrumentsData instab; -InstProgsData progs; -BankSetupData banksetup; -std::vector<std::string> banknames; - -//unsigned maxvalues[30] = { 0 }; +InstBuffer MakeNoSoundIns1() +{ + InstBuffer nosnd; + uint8_t d[] = {0x00, 0x10, 0x07, 0x07, 0xF7, 0xF7, 0x00, 0x00, 0xFF, 0xFF, 0x00}; + std::memcpy(nosnd.data, d, 11); + return nosnd; +} -void SetBank(size_t bank, unsigned patch, size_t insno) +void BanksDump::toOps(const InstBuffer_t &inData, BanksDump::Operator *outData, size_t begin) { - progs[bank][patch] = insno + 1; + outData[begin + 0].d_E862 = + uint_fast32_t(inData.op1_wave << 24) + + uint_fast32_t(inData.op1_susrel << 16) + + uint_fast32_t(inData.op1_atdec << 8) + + uint_fast32_t(inData.op1_amvib << 0); + outData[begin + 1].d_E862 = + uint_fast32_t(inData.op2_wave << 24) + + uint_fast32_t(inData.op2_susrel << 16) + + uint_fast32_t(inData.op2_atdec << 8) + + uint_fast32_t(inData.op2_amvib << 0); + outData[begin + 0].d_40 = inData.op1_ksltl; + outData[begin + 1].d_40 = inData.op2_ksltl; } -void SetBankSetup(size_t bank, const AdlBankSetup &setup) +size_t BanksDump::initBank(size_t bankId, const std::string &title, uint_fast16_t bankSetup) { - banksetup[bank] = setup; + for(size_t bID = 0; bID < banks.size(); bID++) + { + BankEntry &be = banks[bID]; + if(bankId == be.bankId) + { + be.bankTitle = title; + be.bankSetup = bankSetup; + return bID; + } + } + + size_t bankIndex = banks.size(); + banks.emplace_back(); + BankEntry &b = banks.back(); + + b.bankId = bankId; + b.bankTitle = title; + b.bankSetup = bankSetup; + return bankIndex; } -size_t InsertIns(const insdata &id, ins &in, const std::string &name, const std::string &name2) +void BanksDump::addMidiBank(size_t bankId, bool percussion, BanksDump::MidiBank b) { - return InsertIns(id, id, in, name, name2, true); + assert(bankId < banks.size()); + BankEntry &be = banks[bankId]; + + auto it = std::find(midiBanks.begin(), midiBanks.end(), b); + if(it == midiBanks.end()) + { + b.midiBankId = midiBanks.size(); + midiBanks.push_back(b); + } + else + { + b.midiBankId = it->midiBankId; + } + + if(percussion) + be.percussion.push_back(b.midiBankId); + else + be.melodic.push_back(b.midiBankId); } -size_t InsertIns( - const insdata &id, - const insdata &id2, - ins &in, - const std::string &name, - const std::string &name2, - bool oneVoice) +void BanksDump::addInstrument(BanksDump::MidiBank &bank, size_t patchId, + BanksDump::InstrumentEntry e, + BanksDump::Operator *ops, + const std::string &meta) { + assert(patchId < 128); + size_t opsCount = ((e.instFlags & InstrumentEntry::WOPL_Ins_4op) != 0 || + (e.instFlags & InstrumentEntry::WOPL_Ins_Pseudo4op) != 0) ? + 4 : 2; + + if((e.instFlags & InstrumentEntry::WOPL_Ins_IsBlank) != 0) { - InstrumentDataTab::iterator i = insdatatab.lower_bound(id); + bank.instruments[patchId] = -1; + return; + } - size_t insno = ~size_t(0); - if(i == insdatatab.end() || i->first != id) + for(size_t op = 0; op < opsCount; op++) + { + Operator o = ops[op]; + auto it = std::find(operators.begin(), operators.end(), o); + if(it == operators.end()) { - std::pair<insdata, std::pair<size_t, std::set<std::string> > > res; - res.first = id; - res.second.first = insdatatab.size(); - if(!name.empty()) res.second.second.insert(name); - if(!name2.empty()) res.second.second.insert(name2); - insdatatab.insert(i, res); - insno = res.second.first; + o.opId = operators.size(); + e.ops[op] = static_cast<int_fast32_t>(o.opId); + operators.push_back(o); } else { - if(!name.empty()) i->second.second.insert(name); - if(!name2.empty()) i->second.second.insert(name2); - insno = i->second.first; + e.ops[op] = static_cast<int_fast32_t>(it->opId); } - - in.insno1 = insno; } - if(oneVoice || (id == id2)) - in.insno2 = in.insno1; + auto it = std::find(instruments.begin(), instruments.end(), e); + if(it == instruments.end()) + { + e.instId = instruments.size(); + e.instMetas.push_back(meta + "_" + std::to_string(patchId)); + instruments.push_back(e); + } else { - InstrumentDataTab::iterator i = insdatatab.lower_bound(id2); + e.instId = it->instId; + e.instMetas.push_back(meta + "_" + std::to_string(patchId)); + it->instMetas.push_back(meta + "_" + std::to_string(patchId)); + } + bank.instruments[patchId] = static_cast<int_fast32_t>(e.instId); +} + +void BanksDump::exportBanks(const std::string &outPath, const std::string &headerName) +{ + FILE *out = std::fopen(outPath.c_str(), "w"); + + std::fprintf(out, "/**********************************************************\n" + " This file is generated by `gen_adldata` automatically\n" + " Don't edit it directly!\n" + " To modify content of this file, modify banks\n" + " and re-run the `gen_adldata` build step.\n" + "***********************************************************/\n\n" + "#include \"%s\"\n\n\n", headerName.c_str()); + + std::fprintf(out, "const size_t g_embeddedBanksCount = %zu;\n\n", banks.size()); + std::fprintf(out, "const BanksDump::BankEntry g_embeddedBanks[] =\n" + "{\n"); + + std::vector<size_t> bankNumberLists; + + for(const BankEntry &be : banks) + { + std::fprintf(out, "\t{0x%04lX, %zu, %zu, \"%s\", ", + be.bankSetup, + be.melodic.size(), + be.percussion.size(), + be.bankTitle.c_str()); + + fprintf(out, "%zu, ", bankNumberLists.size()); // Use offset to point the common array of bank IDs + for(const size_t &me : be.melodic) + bankNumberLists.push_back(me); + + fprintf(out, "%zu", bankNumberLists.size()); + for(const size_t &me : be.percussion) + bankNumberLists.push_back(me); + + std::fprintf(out, "},\n"); + } - size_t insno2 = ~size_t(0); - if(i == insdatatab.end() || i->first != id2) + std::fprintf(out, "};\n\n"); + + std::fprintf(out, "const char* const g_embeddedBankNames[] =\n" + "{\n\t"); + { + bool commaNeeded = false; + size_t operatorEntryCounter = 0; + for(const BankEntry &be : banks) { - std::pair<insdata, std::pair<size_t, std::set<std::string> > > res; - res.first = id2; - res.second.first = insdatatab.size(); - if(!name.empty()) res.second.second.insert(name); - if(!name2.empty()) res.second.second.insert(name2); - insdatatab.insert(i, res); - insno2 = res.second.first; + if(commaNeeded) + std::fprintf(out, ", "); + else + commaNeeded = true; + operatorEntryCounter++; + if(operatorEntryCounter >= 25) + { + std::fprintf(out, "\n"); + operatorEntryCounter = 0; + } + if(operatorEntryCounter == 0) + std::fprintf(out, "\t"); + std::fprintf(out, "g_embeddedBanks[%zu].title", be.bankId); } - else + std::fprintf(out, ",\n\tNULL"); // Make a null entry as finalizer + } + std::fprintf(out, "\n};\n\n"); + + std::fprintf(out, "const size_t g_embeddedBanksMidiIndex[] =\n" + "{"); + { + bool commaNeeded = false; + for(const size_t &me : bankNumberLists) { - if(!name.empty()) i->second.second.insert(name); - if(!name2.empty()) i->second.second.insert(name2); - insno2 = i->second.first; + if(commaNeeded) + std::fprintf(out, ","); + else + commaNeeded = true; + std::fprintf(out, "%zu", me); } - in.insno2 = insno2; } + std::fprintf(out, "};\n\n"); + std::fprintf(out, "const BanksDump::MidiBank g_embeddedBanksMidi[] =\n" + "{\n"); + for(const MidiBank &be : midiBanks) { - InstrumentsData::iterator i = instab.lower_bound(in); + bool commaNeeded = true; + std::fprintf(out, "\t{%u,%u,", be.msb, be.lsb); - size_t resno = ~size_t(0); - if(i == instab.end() || i->first != in) + std::fprintf(out, "{"); + commaNeeded = false; + for(size_t i = 0; i < 128; i++) { - std::pair<ins, std::pair<size_t, std::set<std::string> > > res; - res.first = in; - res.second.first = instab.size(); - if(!name.empty()) res.second.second.insert(name); - if(!name2.empty()) res.second.second.insert(name2); - instab.insert(i, res); - resno = res.second.first; + if(commaNeeded) + std::fprintf(out, ","); + else + commaNeeded = true; + std::fprintf(out, "%ld", be.instruments[i]); } + std::fprintf(out, "}"); + + std::fprintf(out, "},\n"); + } + std::fprintf(out, "};\n\n"); + + + std::fprintf(out, "const BanksDump::InstrumentEntry g_embeddedBanksInstruments[] =\n" + "{\n"); + for(const InstrumentEntry &be : instruments) + { + size_t opsCount = ((be.instFlags & InstrumentEntry::WOPL_Ins_4op) != 0 || + (be.instFlags & InstrumentEntry::WOPL_Ins_Pseudo4op) != 0) ? 4 : 2; + std::fprintf(out, "\t{%d,%d,%d,%u,%s%lX,%d,%s%lX,%s%lX,%s%lX,", + be.noteOffset1, + be.noteOffset2, + be.midiVelocityOffset, + be.percussionKeyNumber, + (be.instFlags == 0 ? "" : "0x"), be.instFlags, // for compactness, don't print "0x" when is zero + be.secondVoiceDetune, + (be.fbConn == 0 ? "" : "0x"), be.fbConn, + (be.delay_on_ms == 0 ? "" : "0x"), be.delay_on_ms, + (be.delay_off_ms == 0 ? "" : "0x"), be.delay_off_ms); + + if(opsCount == 4) + std::fprintf(out, "{%ld,%ld,%ld,%ld} ", + be.ops[0], be.ops[1], be.ops[2], be.ops[3]); else + std::fprintf(out, "{%ld,%ld}", + be.ops[0], be.ops[1]); + + std::fprintf(out, "},\n"); + } + std::fprintf(out, "};\n\n"); + + std::fprintf(out, "const BanksDump::Operator g_embeddedBanksOperators[] =\n" + "{\n"); + size_t operatorEntryCounter = 0; + for(const Operator &be : operators) + { + if(operatorEntryCounter == 0) + std::fprintf(out, "\t"); + std::fprintf(out, "{0x%07lX,%s%02lX},", + be.d_E862, + (be.d_40 == 0 ? "" : "0x"), be.d_40); + operatorEntryCounter++; + if(operatorEntryCounter >= 25) { - if(!name.empty()) i->second.second.insert(name); - if(!name2.empty()) i->second.second.insert(name2); - resno = i->second.first; + std::fprintf(out, "\n"); + operatorEntryCounter = 0; } - return resno; } + std::fprintf(out, "\n};\n\n"); + + std::fclose(out); } -// Create silent 'nosound' instrument -size_t InsertNoSoundIns() +struct OpCheckData +{ + uint_fast8_t egEn; + uint_fast8_t attack; + uint_fast8_t decay; + uint_fast8_t sustain; + uint_fast8_t release; + uint_fast8_t level; + + void setData(uint_fast32_t d_E862, uint_fast32_t d_40) + { + egEn = static_cast<uint_fast8_t>(((d_E862 & 0xFF) >> 5) & 0x01); + decay = static_cast<uint_fast8_t>((d_E862 >> 8) & 0x0F); + attack = static_cast<uint_fast8_t>((d_E862 >> 12) & 0x0F); + release = static_cast<uint_fast8_t>((d_E862 >> 16) & 0x0F); + sustain = static_cast<uint_fast8_t>((d_E862 >> 20) & 0x0F); + level = static_cast<uint_fast8_t>(d_40 & 0x3F); + } + + bool isOpSilent(bool moreInfo) + { + // level=0x3f - silence + // attack=0x00 - silence + // attack=0x0F & sustain=0x0F & decay=0x0F - half-silence + // attack=0x0F & decay=0x0F & release=0x00 & egOff - half-silence + if(level == 0x3F) + { + if(moreInfo) + std::fprintf(stdout, "== volume=0x3F ==\n"); + return true; + } + if(attack == 0x00) + { + if(moreInfo) + std::fprintf(stdout, "== attack=0x00 ==\n"); + return true; + } + if(attack == 0x0F && sustain == 0x0F && decay == 0x0F) + { + if(moreInfo) + std::fprintf(stdout, "== attack=0x0F, sustain=0x0F, decay=0x0F ==\n"); + return true; + } + if(attack == 0x0F && decay == 0x0F && release == 0x00 && !egEn) + { + if(moreInfo) + std::fprintf(stdout, "== attack=0x0F, decay=0x0F, release=0x00, !egEn ==\n"); + return true; + } + return false; + } +}; + +bool BanksDump::isSilent(const BanksDump &db, const BanksDump::InstrumentEntry &ins, bool moreInfo) { - // { 0x0F70700,0x0F70710, 0xFF,0xFF, 0x0,+0 }, - insdata tmp1 = MakeNoSoundIns(); - struct ins tmp2 = { 0, 0, 0, false, false, 0u, 0.0, 0}; - return InsertIns(tmp1, tmp1, tmp2, "nosound", ""); + bool isPseudo4ops = ((ins.instFlags & BanksDump::InstrumentEntry::WOPL_Ins_Pseudo4op) != 0); + bool is4ops = ((ins.instFlags & BanksDump::InstrumentEntry::WOPL_Ins_4op) != 0) && !isPseudo4ops; + size_t opsNum = (is4ops || isPseudo4ops) ? 4 : 2; + BanksDump::Operator ops[4]; + assert(ins.ops[0] >= 0); + assert(ins.ops[1] >= 0); + ops[0] = db.operators[ins.ops[0]]; + ops[1] = db.operators[ins.ops[1]]; + if(opsNum > 2) + { + assert(ins.ops[2] >= 0); + assert(ins.ops[3] >= 0); + ops[2] = db.operators[ins.ops[2]]; + ops[3] = db.operators[ins.ops[3]]; + } + return isSilent(ops, ins.fbConn, opsNum, isPseudo4ops, moreInfo); } -insdata MakeNoSoundIns() +bool BanksDump::isSilent(const BanksDump::Operator *ops, uint_fast16_t fbConn, size_t countOps, bool pseudo4op, bool moreInfo) { - return { {0x00, 0x10, 0x07, 0x07, 0xF7, 0xF7, 0x00, 0x00, 0xFF, 0xFF, 0x00}, 0, false}; + // TODO: Implement this completely!!! + const uint_fast8_t conn1 = (fbConn) & 0x01; + const uint_fast8_t conn2 = (fbConn >> 8) & 0x01; + OpCheckData opd[4]; + for(size_t i = 0; i < 4; i++) + opd[i].setData(ops[i].d_E862, ops[i].d_40); + + if(countOps == 2) + { + if(conn1 == 0) + { + if(opd[1].isOpSilent(moreInfo)) + return true; + } + if(conn1 == 1) + { + if(opd[0].isOpSilent(moreInfo) && opd[1].isOpSilent(moreInfo)) + return true; + } + } + else if(countOps == 4 && pseudo4op) + { + bool silent1 = false; + bool silent2 = false; + if(conn1 == 0 && opd[1].isOpSilent(moreInfo)) + silent1 = true; + if(conn1 == 1 && opd[0].isOpSilent(moreInfo) && opd[1].isOpSilent(moreInfo)) + silent1 = true; + if(conn2 == 0 && opd[3].isOpSilent(moreInfo)) + silent2 = true; + if(conn2 == 1 && opd[2].isOpSilent(moreInfo) && opd[3].isOpSilent(moreInfo)) + silent2 = true; + if(silent1 && silent2) + return true; + } + else if(countOps == 4 && !pseudo4op) + { + if(conn1 == 0 && conn2 == 0) // FM-FM [0, 0, 0, 1] + { + if(opd[3].isOpSilent(moreInfo)) + return true; + } + + if(conn1 == 1 && conn2 == 0) // AM-FM [1, 0, 0, 1] + { + if(opd[0].isOpSilent(moreInfo) && opd[3].isOpSilent(moreInfo)) + return true; + } + if(conn1 == 0 && conn2 == 1) // FM-AM [0, 1, 0, 1] + { + if(opd[1].isOpSilent(moreInfo) && opd[3].isOpSilent(moreInfo)) + return true; + } + if(conn1 == 1 && conn2 == 1) // FM-AM [1, 0, 1, 1] + { + if(opd[0].isOpSilent(moreInfo) && opd[2].isOpSilent(moreInfo) && opd[3].isOpSilent(moreInfo)) + return true; + } + } + + return false; } +void BanksDump::InstrumentEntry::setFbConn(uint_fast16_t fbConn1, uint_fast16_t fbConn2) +{ + fbConn = (static_cast<uint_fast16_t>(fbConn1 & 0x0F)) | + (static_cast<uint_fast16_t>(fbConn2 & 0x0F) << 8); +} diff --git a/utils/gen_adldata/progs_cache.h b/utils/gen_adldata/progs_cache.h index 28aaa19..6573307 100644 --- a/utils/gen_adldata/progs_cache.h +++ b/utils/gen_adldata/progs_cache.h @@ -1,3 +1,4 @@ +#pragma once #ifndef PROGS_H #define PROGS_H @@ -12,84 +13,60 @@ #include <vector> #include <limits> #include <cmath> +#include <cstdint> +#include <cstdio> + +#include <cassert> -struct insdata +struct InstBuffer_t { - uint8_t data[11]; - int8_t finetune; - bool diff; - bool operator==(const insdata &b) const + uint8_t op1_amvib; // 0 + uint8_t op2_amvib; // 1 + uint8_t op1_atdec; // 2 + uint8_t op2_atdec; // 3 + uint8_t op1_susrel; // 4 + uint8_t op2_susrel; // 5 + uint8_t op1_wave; // 6 + uint8_t op2_wave; // 7 + uint8_t op1_ksltl; // 8 + uint8_t op2_ksltl; // 9 + uint8_t fbconn; // 10 + + bool operator==(const InstBuffer_t &b) const { - return (std::memcmp(data, b.data, 11) == 0) && (finetune == b.finetune) && (diff == b.diff); + return std::memcmp(this, &b, sizeof(InstBuffer_t)) == 0; } - bool operator!=(const insdata &b) const + + bool operator!=(const InstBuffer_t &b) const { return !operator==(b); } - bool operator<(const insdata &b) const + + bool operator<(const InstBuffer_t &b) const { - int c = std::memcmp(data, b.data, 11); + int c = std::memcmp(this, &b, 11); if(c != 0) return c < 0; - if(finetune != b.finetune) return finetune < b.finetune; - if(diff != b.diff) return (diff) == (!b.diff); return 0; } - bool operator>(const insdata &b) const + + bool operator>(const InstBuffer_t &b) const { return !operator<(b) && operator!=(b); } }; -inline bool equal_approx(double const a, double const b) +union InstBuffer { - double const epsilon(std::numeric_limits<double>::epsilon() * 100); - double const scale(1.0); - return std::fabs(a - b) < epsilon * (scale + (std::max)(std::fabs(a), std::fabs(b))); -} + InstBuffer_t d; + uint8_t data[11]; +}; -struct ins +inline bool equal_approx(double const a, double const b) { - enum { Flag_Pseudo4op = 0x01, Flag_NoSound = 0x02, Flag_Real4op = 0x04 }; - - enum { Flag_RM_BassDrum = 0x08, Flag_RM_Snare = 0x10, Flag_RM_TomTom = 0x18, - Flag_RM_Cymbal = 0x20, Flag_RM_HiHat = 0x28, Mask_RhythmMode = 0x38 }; - - size_t insno1, insno2; - unsigned char notenum; - bool pseudo4op; - bool real4op; - uint32_t rhythmModeDrum; - double voice2_fine_tune; - int8_t midi_velocity_offset; - - bool operator==(const ins &b) const - { - return notenum == b.notenum - && insno1 == b.insno1 - && insno2 == b.insno2 - && pseudo4op == b.pseudo4op - && real4op == b.real4op - && rhythmModeDrum == b.rhythmModeDrum - && equal_approx(voice2_fine_tune, b.voice2_fine_tune) - && midi_velocity_offset == b.midi_velocity_offset; - } - bool operator< (const ins &b) const - { - if(insno1 != b.insno1) return insno1 < b.insno1; - if(insno2 != b.insno2) return insno2 < b.insno2; - if(notenum != b.notenum) return notenum < b.notenum; - if(pseudo4op != b.pseudo4op) return pseudo4op < b.pseudo4op; - if(real4op != b.real4op) return real4op < b.real4op; - if(rhythmModeDrum != b.rhythmModeDrum) return rhythmModeDrum < b.rhythmModeDrum; - if(!equal_approx(voice2_fine_tune, b.voice2_fine_tune)) return voice2_fine_tune < b.voice2_fine_tune; - if(midi_velocity_offset != b.midi_velocity_offset) return midi_velocity_offset < b.midi_velocity_offset; - return 0; - } - bool operator!=(const ins &b) const - { - return !operator==(b); - } -}; + int_fast64_t ai = static_cast<int_fast64_t>(a * 1000000.0); + int_fast64_t bi = static_cast<int_fast64_t>(b * 1000000.0); + return ai == bi; +} enum VolumesModels { @@ -100,44 +77,250 @@ enum VolumesModels VOLUME_9X }; -struct AdlBankSetup +InstBuffer MakeNoSoundIns1(); + + + + + + + + + +struct BanksDump { - int volumeModel; - bool deepTremolo; - bool deepVibrato; - bool scaleModulators; -}; + struct BankEntry + { + uint_fast32_t bankId = 0; + std::string bankTitle = "Untitled"; + + /* Global OPL flags */ + typedef enum WOPLFileFlags + { + /* Enable Deep-Tremolo flag */ + WOPL_FLAG_DEEP_TREMOLO = 0x01, + /* Enable Deep-Vibrato flag */ + WOPL_FLAG_DEEP_VIBRATO = 0x02 + } WOPLFileFlags; + + /* Volume scaling model implemented in the libADLMIDI */ + typedef enum WOPL_VolumeModel + { + WOPL_VM_Generic = 0, + WOPL_VM_Native, + WOPL_VM_DMX, + WOPL_VM_Apogee, + WOPL_VM_Win9x + } WOPL_VolumeModel; + + /** + * @brief Suggested bank setup in dependence from a driver that does use of this + */ + enum BankSetup + { + SETUP_Generic = 0x0300, + SETUP_Win9X = 0x0304, + SETUP_DMX = 0x0002, + SETUP_Apogee = 0x0003, + SETUP_AIL = 0x0300, + SETUP_IBK = 0x0301, + SETUP_IMF = 0x0200, + SETUP_CMF = 0x0201 + }; + + uint_fast16_t bankSetup = SETUP_Generic; // 0xAABB, AA - OPL flags, BB - Volume model + std::vector<size_t> melodic; + std::vector<size_t> percussion; + + explicit BankEntry() = default; + + BankEntry(const BankEntry &o) + { + bankId = o.bankId; + bankTitle = o.bankTitle; + bankSetup = o.bankSetup; + melodic = o.melodic; + percussion = o.percussion; + } + + BankEntry(const BankEntry &&o) + { + bankId = std::move(o.bankId); + bankTitle = std::move(o.bankTitle); + bankSetup = std::move(o.bankSetup); + melodic = std::move(o.melodic); + percussion = std::move(o.percussion); + } + }; + + struct MidiBank + { + uint_fast32_t midiBankId = 0; + uint_fast8_t msb = 0, lsb = 0; + int_fast32_t instruments[128]; -typedef std::map<insdata, std::pair<size_t, std::set<std::string> > > InstrumentDataTab; -extern InstrumentDataTab insdatatab; + MidiBank() + { + for(size_t i = 0; i < 128; i++) + instruments[i] = -1; + } -typedef std::map<ins, std::pair<size_t, std::set<std::string> > > InstrumentsData; -extern InstrumentsData instab; + MidiBank(const MidiBank &o) + { + midiBankId = o.midiBankId; + msb = o.msb; + lsb = o.lsb; + std::memcpy(instruments, o.instruments, sizeof(int_fast32_t) * 128); + } -typedef std::map<size_t, std::map<size_t, size_t> > InstProgsData; -extern InstProgsData progs; + bool operator==(const MidiBank &o) + { + if(msb != o.msb) + return false; + if(lsb != o.lsb) + return false; + if(std::memcmp(instruments, o.instruments, sizeof(int_fast32_t) * 128) != 0) + return false; + return true; + } + bool operator!=(const MidiBank &o) + { + return !operator==(o); + } + }; -typedef std::map<size_t, AdlBankSetup> BankSetupData; -extern BankSetupData banksetup; + struct InstrumentEntry + { + uint_fast32_t instId = 0; + std::vector<std::string> instMetas; + + typedef enum WOPL_InstrumentFlags + { + /* Is two-operator single-voice instrument (no flags) */ + WOPL_Ins_2op = 0x00, + /* Is true four-operator instrument */ + WOPL_Ins_4op = 0x01, + /* Is pseudo four-operator (two 2-operator voices) instrument */ + WOPL_Ins_Pseudo4op = 0x02, + /* Is a blank instrument entry */ + WOPL_Ins_IsBlank = 0x04, + + /* RythmMode flags mask */ + WOPL_RhythmModeMask = 0x38, + + /* Mask of the flags range */ + WOPL_Ins_ALL_MASK = 0x07 + } WOPL_InstrumentFlags; + + typedef enum WOPL_RhythmMode + { + /* RythmMode: BassDrum */ + WOPL_RM_BassDrum = 0x08, + /* RythmMode: Snare */ + WOPL_RM_Snare = 0x10, + /* RythmMode: TomTom */ + WOPL_RM_TomTom = 0x18, + /* RythmMode: Cymbell */ + WOPL_RM_Cymbal = 0x20, + /* RythmMode: HiHat */ + WOPL_RM_HiHat = 0x28 + } WOPL_RhythmMode; + + int_fast8_t noteOffset1 = 0; + int_fast8_t noteOffset2 = 0; + int_fast8_t midiVelocityOffset = 0; + uint_fast8_t percussionKeyNumber = 0; + uint_fast32_t instFlags = 0; + int_fast8_t secondVoiceDetune = 0; + /* + 2op: modulator1, carrier1, feedback1 + 2vo: modulator1, carrier1, modulator2, carrier2, feedback(1+2) + 4op: modulator1, carrier1, modulator2, carrier2, feedback1 + */ + //! Contains FeedBack-Connection for both operators 0xBBAA - AA - first, BB - second + uint_fast16_t fbConn = 0; + int_fast64_t delay_on_ms = 0; + int_fast64_t delay_off_ms = 0; + int_fast32_t ops[5] = {-1, -1, -1, -1, -1}; + + void setFbConn(uint_fast16_t fbConn1, uint_fast16_t fbConn2 = 0x00); -extern std::vector<std::string> banknames; + bool operator==(const InstrumentEntry &o) + { + return ( + (noteOffset1 == o.noteOffset1) && + (noteOffset2 == o.noteOffset2) && + (midiVelocityOffset == o.midiVelocityOffset) && + (percussionKeyNumber == o.percussionKeyNumber) && + (instFlags == o.instFlags) && + (secondVoiceDetune == o.secondVoiceDetune) && + (fbConn == o.fbConn) && + (delay_on_ms == o.delay_on_ms) && + (delay_off_ms == o.delay_off_ms) && + (std::memcmp(ops, o.ops, sizeof(int_fast32_t) * 5) == 0) + ); + } + bool operator!=(const InstrumentEntry &o) + { + return !operator==(o); + } + }; -//static std::map<unsigned, std::map<unsigned, unsigned> > Correlate; -//extern unsigned maxvalues[30]; + struct Operator + { + uint_fast32_t opId = 0; + uint_fast32_t d_E862 = 0; + uint_fast32_t d_40 = 0; + explicit Operator() {} + Operator(const Operator &o) + { + opId = o.opId; + d_E862 = o.d_E862; + d_40 = o.d_40; + } + bool operator==(const Operator &o) + { + return ((d_E862 == o.d_E862) && (d_40 == o.d_40)); + } + bool operator!=(const Operator &o) + { + return !operator==(o); + } + }; + + std::vector<BankEntry> banks; + std::vector<MidiBank> midiBanks; + std::vector<InstrumentEntry> instruments; + std::vector<Operator> operators; -void SetBank(size_t bank, unsigned patch, size_t insno); -void SetBankSetup(size_t bank, const AdlBankSetup &setup); + static void toOps(const InstBuffer_t &inData, Operator *outData, size_t begin = 0); + //! WIP + static bool isSilent(const BanksDump &db, const BanksDump::InstrumentEntry &ins, bool moreInfo = false); + static bool isSilent(const Operator *ops, uint_fast16_t fbConn, size_t countOps = 2, bool pseudo4op = false, bool moreInfo = false); -/* 2op voice instrument */ -size_t InsertIns(const insdata &id, ins &in, - const std::string &name, const std::string &name2); + size_t initBank(size_t bankId, const std::string &title, uint_fast16_t bankSetup); + void addMidiBank(size_t bankId, bool percussion, MidiBank b); + void addInstrument(MidiBank &bank, size_t patchId, InstrumentEntry e, Operator *ops, const std::string &meta = std::string()); + void exportBanks(const std::string &outPath, const std::string &headerName = "adlmidi_db.h"); +}; -/* 4op voice instrument or double-voice 2-op instrument */ -size_t InsertIns(const insdata &id, const insdata &id2, ins &in, - const std::string &name, const std::string &name2, - bool oneVoice = false); -size_t InsertNoSoundIns(); -insdata MakeNoSoundIns(); +namespace BankFormats +{ + +bool LoadMiles(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix); +bool LoadBisqwit(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix); +bool LoadBNK(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix, bool is_fat, bool percussive); +bool LoadBNK2(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix, + const std::string &melo_filter, + const std::string &perc_filter); +bool LoadEA(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix); +bool LoadIBK(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix, bool percussive, bool noRhythmMode = false); +bool LoadJunglevision(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix); +bool LoadDoom(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix); +bool LoadTMB(BanksDump &db, const char *fn, unsigned bank, const std::string &bankTitle, const char *prefix); +bool LoadWopl(BanksDump &db, const char *fn, unsigned bank, const std::string bankTitle, const char *prefix); + +} #endif // PROGS_H |