aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/dumpbank/dumpbank.cpp179
-rw-r--r--utils/dumpmiles/dumpmiles.cpp52
-rw-r--r--utils/gen_adldata/file_formats/common.h28
-rw-r--r--utils/gen_adldata/file_formats/load_ail.h102
-rw-r--r--utils/gen_adldata/file_formats/load_bisqwit.h53
-rw-r--r--utils/gen_adldata/file_formats/load_bnk.h152
-rw-r--r--utils/gen_adldata/file_formats/load_bnk2.h102
-rw-r--r--utils/gen_adldata/file_formats/load_ibk.h72
-rw-r--r--utils/gen_adldata/file_formats/load_jv.h105
-rw-r--r--utils/gen_adldata/file_formats/load_op2.h148
-rw-r--r--utils/gen_adldata/file_formats/load_tmb.h68
-rw-r--r--utils/gen_adldata/file_formats/load_wopl.h265
-rw-r--r--utils/gen_adldata/gen_adldata.cc506
-rw-r--r--utils/gen_adldata/gen_adldata.pro38
-rw-r--r--utils/gen_adldata/ini/IniProcessor.pri10
-rw-r--r--utils/gen_adldata/ini/ini_processing.cpp1385
-rw-r--r--utils/gen_adldata/ini/ini_processing.h677
-rw-r--r--utils/gen_adldata/ini/ini_processing_variant.h243
-rw-r--r--utils/gen_adldata/measurer.cpp543
-rw-r--r--utils/gen_adldata/measurer.h105
-rw-r--r--utils/gen_adldata/midi_inst_list.h185
-rw-r--r--utils/gen_adldata/progs_cache.cpp106
-rw-r--r--utils/gen_adldata/progs_cache.h85
-rw-r--r--utils/gen_adldata/scrapped.txt263
-rw-r--r--utils/midiplay/Makefile12
-rw-r--r--utils/midiplay/Makefile.win3212
-rw-r--r--utils/midiplay/adlmidiplay.cpp338
-rwxr-xr-xutils/midiplay/wave_writer.c170
-rwxr-xr-xutils/midiplay/wave_writer.h21
-rw-r--r--utils/test/adldatatest.cc2
-rw-r--r--utils/test/test.cc202
31 files changed, 6229 insertions, 0 deletions
diff --git a/utils/dumpbank/dumpbank.cpp b/utils/dumpbank/dumpbank.cpp
new file mode 100644
index 0000000..6996681
--- /dev/null
+++ b/utils/dumpbank/dumpbank.cpp
@@ -0,0 +1,179 @@
+//#ifdef __MINGW32__
+//typedef struct vswprintf {} swprintf;
+//#endif
+#include <cstdio>
+#include <vector>
+#include <cstring>
+
+struct BNK1_header
+{
+ char maj_vers, min_vers;
+ char signature[6]; // "ADLIB-"
+ unsigned short ins_used, ins_entries;
+ unsigned name_list, inst_data;
+} __attribute__((packed));
+struct BNK1_record
+{
+ unsigned short index;
+ unsigned char used;
+ char name[9];
+} __attribute__((packed));
+struct OPL2_op
+{
+ unsigned char key_scale_lvl;
+ unsigned char freq_mult;
+ unsigned char feedback;
+ unsigned char attack;
+ unsigned char sustain_lvl;
+ unsigned char sustain_sound;
+ unsigned char decay;
+ unsigned char release;
+ unsigned char out_lvl;
+ unsigned char amp_vib;
+ unsigned char pitch_vib;
+ unsigned char env_scaling;
+ unsigned char connection;
+} __attribute__((packed));
+struct BNK1_instrument
+{
+ unsigned char sound_mode;
+ unsigned char voice_num;
+ OPL2_op ops[2];
+ unsigned char waveforms[2];
+} __attribute__((packed)); // conventional Ad Lib instrument maker bankfile patch
+
+struct BNK2_header
+{
+ char signature[28]; // "Accomp. Bank, (C) AdLib Inc"
+ unsigned short file_ver;
+ char filler[10];
+ unsigned short ins_entries, ins_used;
+ int lostSpace;
+} __attribute__((packed));
+struct BNK2_record
+{
+ char O3_sig[3];
+ char key[12];
+ char used;
+ int attrib, dataOffset;
+ unsigned short blockSize, allocBSize;
+} __attribute__((packed));
+struct OPL3_op
+{
+ unsigned char AVEKMMMM, KKLLLLLL;
+ unsigned char AAAADDDD, SSSSRRRR;
+ unsigned char DxxxxWWW, xxxxxxxx;
+} __attribute__((packed));
+struct BNK2_instrument
+{
+ OPL3_op ops[4];
+ unsigned char C4xxxFFFC;
+ unsigned char xxP24NNN;
+ unsigned char TTTTTTTT;
+ unsigned char xxxxxxxx;
+} __attribute__((packed)); // Ad Lib Gold instrument maker bankfile patch
+
+static void LoadBNK1(const std::vector<unsigned char>& data)
+{
+ const BNK1_header& bnk1 = *(const BNK1_header*) &data[0];
+ const BNK1_record* names = (const BNK1_record*) &data[ bnk1.name_list ];
+ const BNK1_instrument* ins = (const BNK1_instrument*) &data[ bnk1.inst_data ];
+ std::printf("BNK1 version: %d.%d\n", bnk1.maj_vers, bnk1.min_vers);
+ for(unsigned a=0; a<bnk1.ins_entries; ++a)
+ {
+ std::printf("%04X %02X %-9.9s: ",
+ names[a].index, names[a].used, names[a].name);
+
+ const BNK1_instrument& i = ins[ names[a].index ];
+ std::printf("%02X %02X %X %X",
+ i.sound_mode, i.voice_num, i.waveforms[0], i.waveforms[1]);
+ for(unsigned b=0; b<2; ++b)
+ {
+ std::printf(" | ");
+ std::printf("%X %X %X %X %X %X %X %X %02X %X %X %X %X",
+ i.ops[b].key_scale_lvl,
+ i.ops[b].freq_mult,
+ b==1 ? 0 : i.ops[b].feedback,
+ i.ops[b].attack,
+ i.ops[b].sustain_lvl,
+ i.ops[b].sustain_sound,
+ i.ops[b].decay,
+ i.ops[b].release,
+ i.ops[b].out_lvl,
+ i.ops[b].amp_vib,
+ i.ops[b].pitch_vib,
+ i.ops[b].env_scaling,
+ b==1 ? 0 : i.ops[b].connection);
+ }
+ std::printf("\n");
+ }
+}
+static void LoadBNK2(const std::vector<unsigned char>& data)
+{
+ const BNK2_header& bnk2 = *(const BNK2_header*) &data[0];
+ const BNK2_record* names = (const BNK2_record*) &data[ sizeof(bnk2) ];
+ std::printf("BNK2 version: %d, lost space %d\n", bnk2.file_ver, bnk2.lostSpace);
+ for(unsigned a=0; a<bnk2.ins_entries; ++a)
+ {
+ std::printf("%3.3s %-12.12s %02X %08X %04X %04X: ",
+ names[a].O3_sig,
+ names[a].key,
+ names[a].used,
+ names[a].attrib,
+ names[a].blockSize,
+ names[a].allocBSize);
+
+ const BNK2_instrument& i = *(const BNK2_instrument*) &data[ names[a].dataOffset ];
+ std::printf("%02X %02X %02X %02X",
+ i.C4xxxFFFC, i.xxP24NNN, i.TTTTTTTT, i.xxxxxxxx);
+ for(unsigned b=0; b<4; ++b)
+ {
+ std::printf(" | ");
+ std::printf("%02X %02X %02X %02X %02X %02X",
+ i.ops[b].AVEKMMMM,
+ i.ops[b].KKLLLLLL,
+ i.ops[b].AAAADDDD,
+ i.ops[b].SSSSRRRR,
+ i.ops[b].DxxxxWWW,
+ i.ops[b].xxxxxxxx);
+ }
+ std::printf("\n");
+ }
+}
+
+static void LoadBNK(const char* fn)
+{
+ FILE* fp = fopen(fn, "rb");
+ if(!fp)
+ {
+ std::fprintf(stderr, "ERROR: Can't open %s file!", fn);
+ return;
+ }
+
+ std::fseek(fp, 0, SEEK_END);
+ std::vector<unsigned char> data(ftell(fp));
+ std::rewind(fp);
+ size_t got = std::fread(&data[0], 1, data.size(), fp);
+ std::fclose(fp);
+
+ if(got == 0)
+ {
+ std::fprintf(stderr, "ERROR: Can't read %s file!", fn);
+ return;
+ }
+
+ const BNK1_header& bnk1 = *(const BNK1_header*) &data[0];
+ const BNK2_header& bnk2 = *(const BNK2_header*) &data[0];
+
+ if(std::memcmp(bnk1.signature, "ADLIB-", 6) == 0)
+ LoadBNK1(data);
+ else if(std::memcmp(bnk2.signature, "Accomp. Bank, (C) AdLib Inc", 28) == 0)
+ LoadBNK2(data);
+ else
+ std::fprintf(stderr, "%s: Unknown format\n", fn);
+}
+
+int main(int argc, const char* const* argv)
+{
+ LoadBNK(argv[1]);
+}
diff --git a/utils/dumpmiles/dumpmiles.cpp b/utils/dumpmiles/dumpmiles.cpp
new file mode 100644
index 0000000..832da93
--- /dev/null
+++ b/utils/dumpmiles/dumpmiles.cpp
@@ -0,0 +1,52 @@
+//#ifdef __MINGW32__
+//typedef struct vswprintf {} swprintf;
+//#endif
+#include <cstdio>
+#include <vector>
+
+static void LoadMiles(const char* fn)
+{
+ FILE* fp = fopen(fn, "rb");
+ if(!fp)
+ {
+ std::fprintf(stderr, "ERROR: Can't open %s file!", fn);
+ return;
+ }
+
+ std::fseek(fp, 0, SEEK_END);
+ std::vector<unsigned char> data(ftell(fp));
+ std::rewind(fp);
+ size_t got = std::fread(&data[0], 1, data.size(), fp);
+ std::fclose(fp);
+
+ if(got == 0)
+ {
+ std::fprintf(stderr, "ERROR: Can't read %s file!", fn);
+ return;
+ }
+
+ for(unsigned a=0; a<500; ++a)
+ {
+ unsigned gmnumber = data[a*6+0];
+ unsigned gmnumber2 = data[a*6+1];
+ unsigned offset = *(unsigned*)&data[a*6+2];
+
+ if(gmnumber == 0xFF) break;
+ int gmno = gmnumber2==0x7F ? gmnumber+0x80 : gmnumber;
+ unsigned length = data[offset] + data[offset+1]*256;
+ signed char notenum = data[offset+2];
+
+ std::printf("%02X %02X ", gmnumber,gmnumber2); //, offset);
+ for(unsigned b=0; b<length; ++b)
+ {
+ if(b > 3 && (b-3)%11 == 0) printf("\n ");
+ std::printf("%02X ", data[offset+b]);
+ }
+ std::printf("\n");
+ }
+}
+
+int main(int argc, const char* const* argv)
+{
+ LoadMiles(argv[1]);
+}
diff --git a/utils/gen_adldata/file_formats/common.h b/utils/gen_adldata/file_formats/common.h
new file mode 100644
index 0000000..d06059e
--- /dev/null
+++ b/utils/gen_adldata/file_formats/common.h
@@ -0,0 +1,28 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <stdint.h>
+
+inline uint16_t toUint16BE(const uint8_t *arr)
+{
+ uint16_t num = arr[1];
+ num |= ((arr[0] << 8) & 0xFF00);
+ return num;
+}
+
+inline int16_t toSint16BE(const uint8_t *arr)
+{
+ int16_t num = *reinterpret_cast<const int8_t *>(&arr[0]);
+ num *= 1 << 8;
+ num |= arr[1];
+ return num;
+}
+
+inline uint16_t toUint16LE(const uint8_t *arr)
+{
+ uint16_t num = arr[0];
+ num |= ((arr[1] << 8) & 0xFF00);
+ 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
new file mode 100644
index 0000000..4c6c482
--- /dev/null
+++ b/utils/gen_adldata/file_formats/load_ail.h
@@ -0,0 +1,102 @@
+#ifndef LOAD_AIL_H
+#define LOAD_AIL_H
+
+#include "../progs_cache.h"
+#include "../midi_inst_list.h"
+
+static bool LoadMiles(const char *fn, unsigned bank, const char *prefix)
+{
+ #ifdef HARD_BANKS
+ writeIni("AIL", fn, prefix, bank, INI_Both);
+ #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::rewind(fp);
+ if(std::fread(&data[0], 1, data.size(), fp) != data.size())
+ {
+ std::fclose(fp);
+ return false;
+ }
+ std::fclose(fp);
+
+ for(unsigned a = 0; a < 2000; ++a)
+ {
+ unsigned gm_patch = data[a * 6 + 0];
+ unsigned gm_bank = data[a * 6 + 1];
+ unsigned offset = *(unsigned *)&data[a * 6 + 2];
+
+ if(gm_patch == 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]);
+
+ /*printf("%02X %02X %08X ", gmnumber,gmnumber2, offset);
+ for(unsigned b=0; b<length; ++b)
+ {
+ if(b > 3 && (b-3)%11 == 0) printf("\n ");
+ printf("%02X ", data[offset+b]);
+ }
+ printf("\n");*/
+
+ if(gm_bank != 0 && gm_bank != 0x7F)
+ continue;
+
+ char name2[512];
+ sprintf(name2, "%s%c%u", prefix,
+ (gmno < 128 ? 'M' : 'P'), gmno & 127);
+
+ insdata tmp[200];
+
+ const unsigned inscount = (length - 3) / 11;
+ for(unsigned i = 0; i < inscount; ++i)
+ {
+ unsigned o = offset + 3 + i * 11;
+ tmp[i].finetune = (gmno < 128 && i == 0) ? notenum : 0;
+ tmp[i].diff = false;
+ 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
+
+ unsigned fb_c = data[offset + 3 + 5];
+ tmp[i].data[10] = uint8_t(fb_c);
+ if(i == 1)
+ {
+ tmp[0].data[10] = fb_c & 0x0F;
+ tmp[1].data[10] = uint8_t((fb_c & 0x0E) | (fb_c >> 7));
+ }
+ }
+ if(inscount == 1)
+ tmp[1] = tmp[0];
+
+ if(inscount <= 2)
+ {
+ struct ins tmp2;
+ tmp2.notenum = gmno < 128 ? 0 : (unsigned char)notenum;
+ tmp2.pseudo4op = false;
+ tmp2.voice2_fine_tune = 0.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);
+ SetBank(bank, (unsigned int)gmno, resno);
+ }
+ }
+ return true;
+}
+
+#endif // LOAD_AIL_H
diff --git a/utils/gen_adldata/file_formats/load_bisqwit.h b/utils/gen_adldata/file_formats/load_bisqwit.h
new file mode 100644
index 0000000..4928efa
--- /dev/null
+++ b/utils/gen_adldata/file_formats/load_bisqwit.h
@@ -0,0 +1,53 @@
+#ifndef LOAD_BISQWIT_H
+#define LOAD_BISQWIT_H
+
+#include "../progs_cache.h"
+#include "../midi_inst_list.h"
+
+static bool LoadBisqwit(const char *fn, unsigned bank, const char *prefix)
+{
+ #ifdef HARD_BANKS
+ writeIni("Bisqwit", fn, prefix, bank, INI_Both);
+ #endif
+ FILE *fp = std::fopen(fn, "rb");
+ if(!fp)
+ return false;
+
+ for(uint32_t a = 0; a < 256; ++a)
+ {
+ //unsigned offset = a * 25;
+ uint32_t gmno = a;
+ int32_t midi_index = gmno < 128 ? int32_t(gmno)
+ : gmno < 128 + 35 ? -1
+ : gmno < 128 + 88 ? int32_t(gmno - 35)
+ : -1;
+
+ struct ins tmp2;
+ tmp2.notenum = (uint8_t)std::fgetc(fp);
+ tmp2.pseudo4op = false;
+ tmp2.voice2_fine_tune = 0.0;
+
+ insdata tmp[2];
+ for(int side = 0; side < 2; ++side)
+ {
+ tmp[side].finetune = (int8_t)std::fgetc(fp);
+ tmp[side].diff = false;
+ if(std::fread(tmp[side].data, 1, 11, fp) != 11)
+ return false;
+ }
+
+ std::string name;
+ 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);
+
+ size_t resno = InsertIns(tmp[0], tmp[1], tmp2, name, name2);
+ SetBank(bank, gmno, resno);
+ }
+ std::fclose(fp);
+ return true;
+}
+
+#endif // LOAD_BISQWIT_H
diff --git a/utils/gen_adldata/file_formats/load_bnk.h b/utils/gen_adldata/file_formats/load_bnk.h
new file mode 100644
index 0000000..79ce5f5
--- /dev/null
+++ b/utils/gen_adldata/file_formats/load_bnk.h
@@ -0,0 +1,152 @@
+#ifndef LOAD_BNK_H
+#define LOAD_BNK_H
+
+#include "../progs_cache.h"
+
+#include <cstdio>
+#include <cstdint>
+#include <string>
+
+static bool LoadBNK(const char *fn, unsigned bank, const char *prefix, bool is_fat, bool percussive)
+{
+ #ifdef HARD_BANKS
+ writeIni("HMI", fn, prefix, bank, percussive ? INI_Drums : INI_Melodic);
+ #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::rewind(fp);
+ if(std::fread(&data[0], 1, data.size(), fp) != data.size())
+ {
+ std::fclose(fp);
+ return false;
+ }
+ std::fclose(fp);
+
+ /*printf("%s:\n", fn);*/
+ //unsigned short version = *(short*)&data[0]; // major,minor (2 bytes)
+ // "ADLIB-" (6 bytes)
+ uint16_t entries_used = *(uint16_t *)&data[8]; // entries used
+ //unsigned short total_entries = *(short*)&data[10]; // total entries
+ unsigned name_offset = *(unsigned *)&data[12]; // name_offset
+ unsigned data_offset = *(unsigned *)&data[16]; // data_offset
+ // 16..23: 8 byte sof filler
+ /*printf("version=%u %u %u %u %u\n",
+ version, entries_used,total_entries,name_offset,data_offset);*/
+
+ for(unsigned n = 0; n < entries_used; ++n)
+ {
+ const size_t offset1 = name_offset + n * 12;
+
+ unsigned short data_index = data[offset1 + 0] + data[offset1 + 1] * 256;
+ unsigned char usage_flag = data[offset1 + 2];
+ std::string name;
+ for(unsigned p = 0; p < 9; ++p)
+ {
+ if(data[offset1 + 3 + p] == '\0') break;
+ name += char(data[offset1 + 3 + p]);
+ }
+
+ const size_t offset2 = data_offset + data_index * 30;
+ //const unsigned char mode = data[offset2+0];
+ const unsigned char voice_num = data[offset2 + 1];
+ const unsigned char *op1 = &data[offset2 + 2]; // 13 bytes
+ const unsigned char *op2 = &data[offset2 + 15];
+ const unsigned char waveform_mod = data[offset2 + 28];
+ const unsigned char waveform_car = data[offset2 + 29];
+
+ /*printf("%5d %3d %8s mode=%02X voice=%02X: ", data_index,usage_flag, name.c_str(),
+ mode,voice_num);*/
+
+ int gmno = int(is_fat
+ ? ((n & 127) + percussive * 128)
+ : (n + percussive * 128));
+
+ if(is_fat && percussive)
+ {
+ if(name[2] == 'O'
+ || name[2] == 'S')
+ {
+ gmno = 128 + std::stoi(name.substr(3));
+ }
+ }
+
+ char name2[512];
+ if(is_fat)
+ sprintf(name2, "%s%c%u", prefix, percussive ? 'P' : 'M', gmno & 127);
+ else
+ sprintf(name2, "%s%u", prefix, n);
+
+ insdata tmp;
+ tmp.data[0] = uint8_t(
+ (op1[ 9] << 7) // TREMOLO FLAG
+ + (op1[10] << 6) // VIBRATO FLAG
+ + (op1[ 5] << 5) // SUSTAIN FLAG
+ + (op1[11] << 4) // SCALING FLAG
+ + op1[ 1]); // FREQMUL
+
+ tmp.data[1] = uint8_t((op2[ 9] << 7)
+ + (op2[10] << 6)
+ + (op2[ 5] << 5)
+ + (op2[11] << 4)
+ + op2[ 1]);
+ tmp.data[2] = op1[3] * 0x10 + op1[6]; // ATTACK, DECAY
+ tmp.data[3] = op2[3] * 0x10 + op2[6];
+ tmp.data[4] = op1[4] * 0x10 + op1[7]; // SUSTAIN, RELEASE
+ tmp.data[5] = op2[4] * 0x10 + op2[7];
+ tmp.data[6] = waveform_mod;
+ tmp.data[7] = waveform_car;
+ 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.voice2_fine_tune = 0.0;
+
+ if(is_fat) tmp.data[10] ^= 1;
+
+ size_t resno = InsertIns(tmp, tmp, tmp2, std::string(1, '\377') + name, name2);
+
+ 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);
+ }
+
+ /*
+ for(unsigned p=0; p<30; ++p)
+ {
+ unsigned char value = data[offset2+p];
+ if(value > maxvalues[p]) maxvalues[p] = value;
+
+ #define dot(maxval) if(value<=maxval) printf("."); else printf("?[%u]%X",p,value);
+
+ {
+ //if(p==6 || p==7 || p==19||p==20) value=15-value;
+
+ if(p==4 || p==10 || p==17 || p==23)// || p==25)
+ printf(" %2X", value);
+ else
+ printf(" %X", value);
+ }
+ nl:;
+ //if(p == 12) printf("\n%*s", 22, "");
+ //if(p == 25) printf("\n%*s", 22, "");
+ }
+ printf("\n");
+ */
+ }
+ return true;
+}
+
+#endif // LOAD_BNK_H
diff --git a/utils/gen_adldata/file_formats/load_bnk2.h b/utils/gen_adldata/file_formats/load_bnk2.h
new file mode 100644
index 0000000..202402c
--- /dev/null
+++ b/utils/gen_adldata/file_formats/load_bnk2.h
@@ -0,0 +1,102 @@
+#ifndef LOAD_BNK2_H
+#define LOAD_BNK2_H
+
+#include "../progs_cache.h"
+
+static bool LoadBNK2(const char *fn, unsigned bank, 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());
+ #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::rewind(fp);
+ if(std::fread(&data[0], 1, data.size(), fp) != data.size())
+ {
+ std::fclose(fp);
+ return false;
+ }
+ std::fclose(fp);
+
+ unsigned short ins_entries = *(unsigned short *)&data[28 + 2 + 10];
+ unsigned char *records = &data[48];
+
+ for(unsigned n = 0; n < ins_entries; ++n)
+ {
+ const size_t offset1 = n * 28;
+ int used = records[offset1 + 15];
+ //int attrib = *(int*)&records[offset1 + 16];
+ int offset2 = *(int *)&records[offset1 + 20];
+ if(used == 0) continue;
+
+ std::string name;
+ for(unsigned p = 0; p < 12; ++p)
+ {
+ if(records[offset1 + 3 + p] == '\0') break;
+ name += char(records[offset1 + 3 + p]);
+ }
+
+ int gmno = 0;
+ if(name.substr(0, melo_filter.size()) == melo_filter)
+ gmno = std::stoi(name.substr(melo_filter.size()));
+ else if(name.substr(0, perc_filter.size()) == perc_filter)
+ gmno = std::stoi(name.substr(perc_filter.size())) + 128;
+ else
+ continue;
+
+ 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];
+ unsigned char xxP24NNN = insdata[25];
+ unsigned char TTTTTTTT = insdata[26];
+ //unsigned char xxxxxxxx = insdata[27];
+
+ char name2[512];
+ sprintf(name2, "%s%c%u", prefix, (gmno & 128) ? 'P' : 'M', gmno & 127);
+
+ struct insdata tmp[2];
+ for(unsigned a = 0; a < 2; ++a)
+ {
+ tmp[a].data[0] = ops[a * 2 + 0][0];
+ tmp[a].data[1] = ops[a * 2 + 1][0];
+ tmp[a].data[2] = ops[a * 2 + 0][2];
+ tmp[a].data[3] = ops[a * 2 + 1][2];
+ tmp[a].data[4] = ops[a * 2 + 0][3];
+ tmp[a].data[5] = ops[a * 2 + 1][3];
+ tmp[a].data[6] = ops[a * 2 + 0][4] & 0x07;
+ 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;
+ }
+ 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.voice2_fine_tune = 0.0;
+
+ if(xxP24NNN & 8)
+ {
+ // dual-op
+ size_t resno = InsertIns(tmp[0], tmp[1], tmp2, std::string(1, '\377') + name, name2);
+ SetBank(bank, (unsigned int)gmno, resno);
+ }
+ else
+ {
+ // single-op
+ size_t resno = InsertIns(tmp[0], tmp[0], tmp2, std::string(1, '\377') + name, name2);
+ SetBank(bank, (unsigned int)gmno, resno);
+ }
+ }
+ return true;
+}
+
+#endif // LOAD_BNK2_H
diff --git a/utils/gen_adldata/file_formats/load_ibk.h b/utils/gen_adldata/file_formats/load_ibk.h
new file mode 100644
index 0000000..6370ad0
--- /dev/null
+++ b/utils/gen_adldata/file_formats/load_ibk.h
@@ -0,0 +1,72 @@
+#ifndef LOAD_IBK_H
+#define LOAD_IBK_H
+
+#include "../progs_cache.h"
+
+static bool LoadIBK(const char *fn, unsigned bank, const char *prefix, bool percussive)
+{
+ #ifdef HARD_BANKS
+ writeIni("IBK", fn, prefix, bank, percussive ? INI_Drums : INI_Melodic);
+ #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::rewind(fp);
+ if(std::fread(&data[0], 1, data.size(), fp) != data.size())
+ {
+ std::fclose(fp);
+ return false;
+ }
+ std::fclose(fp);
+
+ unsigned offs1_base = 0x804, offs1_len = 9;
+ unsigned offs2_base = 0x004, offs2_len = 16;
+
+ for(unsigned a = 0; a < 128; ++a)
+ {
+ unsigned offset1 = offs1_base + a * offs1_len;
+ unsigned offset2 = offs2_base + a * offs2_len;
+
+ std::string name;
+ for(unsigned p = 0; p < 9; ++p)
+ if(data[offset1] != '\0')
+ name += char(data[offset1 + p]);
+
+ int gmno = int(a + 128 * percussive);
+ /*int midi_index = gmno < 128 ? gmno
+ : gmno < 128+35 ? -1
+ : gmno < 128+88 ? gmno-35
+ : -1;*/
+ char name2[512];
+ sprintf(name2, "%s%c%u", prefix,
+ (gmno < 128 ? 'M' : 'P'), gmno & 127);
+
+ insdata tmp;
+ tmp.data[0] = data[offset2 + 0];
+ tmp.data[1] = data[offset2 + 1];
+ tmp.data[8] = data[offset2 + 2];
+ tmp.data[9] = data[offset2 + 3];
+ tmp.data[2] = data[offset2 + 4];
+ tmp.data[3] = data[offset2 + 5];
+ tmp.data[4] = data[offset2 + 6];
+ tmp.data[5] = data[offset2 + 7];
+ tmp.data[6] = data[offset2 + 8];
+ tmp.data[7] = data[offset2 + 9];
+ tmp.data[10] = data[offset2 + 10];
+ // [+11] seems to be used also, what is it for?
+ tmp.finetune = 0;
+ tmp.diff = false;
+ struct ins tmp2;
+ tmp2.notenum = gmno < 128 ? 0 : 35;
+ tmp2.pseudo4op = false;
+ tmp2.voice2_fine_tune = 0.0;
+
+ size_t resno = InsertIns(tmp, tmp, tmp2, std::string(1, '\377') + name, name2);
+ SetBank(bank, (unsigned int)gmno, resno);
+ }
+ return true;
+}
+
+#endif // LOAD_IBK_H
diff --git a/utils/gen_adldata/file_formats/load_jv.h b/utils/gen_adldata/file_formats/load_jv.h
new file mode 100644
index 0000000..f35de03
--- /dev/null
+++ b/utils/gen_adldata/file_formats/load_jv.h
@@ -0,0 +1,105 @@
+#ifndef LOAD_JV_H
+#define LOAD_JV_H
+
+#include "../progs_cache.h"
+#include "../midi_inst_list.h"
+
+static bool LoadJunglevision(const char *fn, unsigned bank, const char *prefix)
+{
+ #ifdef HARD_BANKS
+ writeIni("Junglevision", fn, prefix, bank, INI_Both);
+ #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::rewind(fp);
+ if(std::fread(&data[0], 1, data.size(), fp) != data.size())
+ {
+ std::fclose(fp);
+ return false;
+ }
+ std::fclose(fp);
+
+ 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));
+ uint16_t first_drum = uint16_t(data[0x26] + (data[0x27] << 8));
+
+ unsigned total_ins = ins_count + drum_count;
+
+ for(unsigned a = 0; a < total_ins; ++a)
+ {
+ unsigned offset = 0x28 + a * 0x18;
+ unsigned gmno = (a < ins_count) ? (a + first_ins) : (a + first_drum);
+ int midi_index = gmno < 128 ? int(gmno)
+ : gmno < 128 + 35 ? -1
+ : gmno < 128 + 88 ? int(gmno - 35)
+ : -1;
+
+ insdata tmp[2];
+
+ tmp[0].data[0] = data[offset + 2];
+ tmp[0].data[1] = data[offset + 8];
+ tmp[0].data[2] = data[offset + 4];
+ tmp[0].data[3] = data[offset + 10];
+ tmp[0].data[4] = data[offset + 5];
+ tmp[0].data[5] = data[offset + 11];
+ tmp[0].data[6] = data[offset + 6];
+ tmp[0].data[7] = data[offset + 12];
+ 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;
+
+ tmp[1].data[0] = data[offset + 2 + 11];
+ tmp[1].data[1] = data[offset + 8 + 11];
+ tmp[1].data[2] = data[offset + 4 + 11];
+ tmp[1].data[3] = data[offset + 10 + 11];
+ tmp[1].data[4] = data[offset + 5 + 11];
+ tmp[1].data[5] = data[offset + 11 + 11];
+ tmp[1].data[6] = data[offset + 6 + 11];
+ tmp[1].data[7] = data[offset + 12 + 11];
+ 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 = false;
+
+ struct ins tmp2;
+ tmp2.notenum = data[offset + 1];
+ tmp2.pseudo4op = false;
+ tmp2.voice2_fine_tune = 0.0;
+
+ while(tmp2.notenum && tmp2.notenum < 20)
+ {
+ tmp2.notenum += 12;
+ tmp[0].finetune -= 12;
+ tmp[1].finetune -= 12;
+ }
+
+ std::string name;
+ 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], 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);
+ }
+ }
+ 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
new file mode 100644
index 0000000..30a118f
--- /dev/null
+++ b/utils/gen_adldata/file_formats/load_op2.h
@@ -0,0 +1,148 @@
+#ifndef LOAD_OP2_H
+#define LOAD_OP2_H
+
+#include "../progs_cache.h"
+
+#pragma pack(push, 1)
+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 */
+} __attribute__((packed));
+#pragma pack(pop)
+
+struct Doom_opl_instr
+{
+ unsigned short flags;
+#define FL_FIXED_PITCH 0x0001 // note has fixed pitch (drum note)
+#define FL_UNKNOWN 0x0002 // ??? (used in instrument #65 only)
+#define FL_DOUBLE_VOICE 0x0004 // use two voices instead of one
+
+ unsigned char finetune;
+ unsigned char note;
+ struct Doom_OPL2instrument patchdata[2];
+} __attribute__((packed));
+
+
+static bool LoadDoom(const char *fn, unsigned bank, const char *prefix)
+{
+ #ifdef HARD_BANKS
+ writeIni("OP2", fn, prefix, bank, INI_Both);
+ #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::rewind(fp);
+
+ if(std::fread(&data[0], 1, data.size(), fp) != data.size())
+ {
+ std::fclose(fp);
+ return false;
+ }
+ std::fclose(fp);
+
+ for(unsigned a = 0; a < 175; ++a)
+ {
+ const size_t offset1 = 0x18A4 + a * 32;
+ const size_t offset2 = 8 + a * 36;
+
+ std::string name;
+ for(unsigned p = 0; p < 32; ++p)
+ if(data[offset1] != '\0')
+ name += char(data[offset1 + p]);
+
+ //printf("%3d %3d %3d %8s: ", a,b,c, name.c_str());
+ int gmno = int(a < 128 ? a : ((a | 128) + 35));
+
+ char name2[512];
+ sprintf(name2, "%s%c%u", prefix, (gmno < 128 ? 'M' : 'P'), gmno & 127);
+
+ Doom_opl_instr &ins = *(Doom_opl_instr *) &data[offset2];
+
+ insdata tmp[2];
+ tmp[0].diff = false;
+ tmp[1].diff = true;
+ for(unsigned index = 0; index < 2; ++index)
+ {
+ const Doom_OPL2instrument &src = ins.patchdata[index];
+ tmp[index].data[0] = src.trem_vibr_1;
+ tmp[index].data[1] = src.trem_vibr_2;
+ tmp[index].data[2] = src.att_dec_1;
+ tmp[index].data[3] = src.att_dec_2;
+ tmp[index].data[4] = src.sust_rel_1;
+ tmp[index].data[5] = src.sust_rel_2;
+ tmp[index].data[6] = src.wave_1;
+ tmp[index].data[7] = src.wave_2;
+ 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.voice2_fine_tune = 0.0;
+ while(tmp2.notenum && tmp2.notenum < 20)
+ {
+ tmp2.notenum += 12;
+ tmp[0].finetune -= 12;
+ tmp[1].finetune -= 12;
+ }
+
+ if(!(ins.flags & 4))
+ {
+ size_t resno = InsertIns(tmp[0], tmp[0], tmp2, std::string(1, '\377') + name, name2);
+ SetBank(bank, (unsigned int)gmno, resno);
+ }
+ else // Double instrument
+ {
+ tmp2.pseudo4op = true;
+ tmp2.voice2_fine_tune = (((double)ins.finetune - 128.0) * 15.625) / 1000.0;
+ if(ins.finetune == 129)
+ tmp2.voice2_fine_tune = 0.000025;
+ else if(ins.finetune == 127)
+ tmp2.voice2_fine_tune = -0.000025;
+ //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);
+ }
+
+ /*const Doom_OPL2instrument& A = ins.patchdata[0];
+ const Doom_OPL2instrument& B = ins.patchdata[1];
+ printf(
+ "flags:%04X fine:%02X note:%02X\n"
+ "{t:%02X a:%02X s:%02X w:%02X s:%02X l:%02X f:%02X\n"
+ " t:%02X a:%02X s:%02X w:%02X s:%02X l:%02X ?:%02X base:%d}\n"
+ "{t:%02X a:%02X s:%02X w:%02X s:%02X l:%02X f:%02X\n"
+ " t:%02X a:%02X s:%02X w:%02X s:%02X l:%02X ?:%02X base:%d} ",
+ ins.flags, ins.finetune, ins.note,
+ A.trem_vibr_1, A.att_dec_1, A.sust_rel_1,
+ A.wave_1, A.scale_1, A.level_1, A.feedback,
+ A.trem_vibr_2, A.att_dec_2, A.sust_rel_2,
+ A.wave_2, A.scale_2, A.level_2, A.unused, A.basenote,
+ B.trem_vibr_1, B.att_dec_1, B.sust_rel_1,
+ B.wave_1, B.scale_1, B.level_1, B.feedback,
+ B.trem_vibr_2, B.att_dec_2, B.sust_rel_2,
+ B.wave_2, B.scale_2, B.level_2, B.unused, B.basenote);
+ printf(" %s VS %s\n", name.c_str(), MidiInsName[a]);
+ printf("------------------------------------------------------------\n");*/
+ }
+ return true;
+}
+
+#endif // LOAD_OP2_H
diff --git a/utils/gen_adldata/file_formats/load_tmb.h b/utils/gen_adldata/file_formats/load_tmb.h
new file mode 100644
index 0000000..c5a3f60
--- /dev/null
+++ b/utils/gen_adldata/file_formats/load_tmb.h
@@ -0,0 +1,68 @@
+#ifndef LOAD_TMB_H
+#define LOAD_TMB_H
+
+#include "../progs_cache.h"
+#include "../midi_inst_list.h"
+
+static bool LoadTMB(const char *fn, unsigned bank, const char *prefix)
+{
+ #ifdef HARD_BANKS
+ writeIni("TMB", fn, prefix, bank, INI_Both);
+ #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::rewind(fp);
+ if(std::fread(&data[0], 1, data.size(), fp) != data.size())
+ {
+ std::fclose(fp);
+ return false;
+ }
+ std::fclose(fp);
+
+ for(unsigned a = 0; a < 256; ++a)
+ {
+ unsigned offset = a * 0x0D;
+ unsigned gmno = a;
+ int midi_index = gmno < 128 ? int(gmno)
+ : gmno < 128 + 35 ? -1
+ : gmno < 128 + 88 ? int(gmno - 35)
+ : -1;
+
+ insdata tmp;
+
+ tmp.data[0] = data[offset + 0];
+ tmp.data[1] = data[offset + 1];
+ tmp.data[2] = data[offset + 4];
+ tmp.data[3] = data[offset + 5];
+ tmp.data[4] = data[offset + 6];
+ tmp.data[5] = data[offset + 7];
+ tmp.data[6] = data[offset + 8];
+ tmp.data[7] = data[offset + 9];
+ tmp.data[8] = data[offset + 2];
+ tmp.data[9] = data[offset + 3];
+ tmp.data[10] = data[offset + 10];
+ tmp.finetune = 0; //data[offset + 12];
+ tmp.diff = false;
+
+ struct ins tmp2;
+ tmp2.notenum = data[offset + 11];
+ tmp2.pseudo4op = false;
+ tmp2.voice2_fine_tune = 0.0;
+
+ std::string name;
+ 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);
+
+ size_t resno = InsertIns(tmp, tmp, tmp2, name, name2);
+ SetBank(bank, gmno, resno);
+ }
+ return true;
+}
+
+#endif // LOAD_TMB_H
diff --git a/utils/gen_adldata/file_formats/load_wopl.h b/utils/gen_adldata/file_formats/load_wopl.h
new file mode 100644
index 0000000..300a3a0
--- /dev/null
+++ b/utils/gen_adldata/file_formats/load_wopl.h
@@ -0,0 +1,265 @@
+#ifndef LOAD_WOPL_H
+#define LOAD_WOPL_H
+
+#include "../progs_cache.h"
+#include "../midi_inst_list.h"
+#include "common.h"
+
+static uint8_t wopl_latest_version = 2;
+
+static bool LoadWopl(const char *fn, unsigned bank, const char *prefix)
+{
+ FILE *fp = std::fopen(fn, "rb");
+ if(!fp)
+ {
+ std::fprintf(stderr, "WOPL: CAN'T OPEN FILE %s\n", fn);
+ std::fflush(stderr);
+ return false;
+ }
+ std::fseek(fp, 0, SEEK_END);
+ std::vector<unsigned char> data(size_t(std::ftell(fp)));
+ std::rewind(fp);
+ if(std::fread(&data[0], 1, data.size(), fp) != data.size())
+ {
+ std::fclose(fp);
+ std::fprintf(stderr, "WOPL: CAN'T READ FILE %s\n", fn);
+ std::fflush(stderr);
+ return false;
+ }
+ std::fclose(fp);
+
+ if(data.size() < 19) // Smaller than header size
+ {
+ std::fprintf(stderr, "WOPL: Too small header %s\n", fn);
+ std::fflush(stderr);
+ return false;
+ }
+
+ uint16_t version = toUint16LE((const uint8_t *)data.data() + 11);
+ if(version > wopl_latest_version)
+ {
+ std::fprintf(stderr, "WOPL: Version %d is not supported (latest %d) %s\n", version, wopl_latest_version, fn);
+ std::fflush(stderr);
+ return false;
+ }
+
+ uint16_t mbanks_count = toUint16BE((const uint8_t *)data.data() + 0x0d);
+ uint16_t pbanks_count = toUint16BE((const uint8_t *)data.data() + 0x0f);
+
+ // Validate file format by size calculation
+ if(version == 1)
+ {
+ //Header size + melodic banks + percussion banks
+ if(data.size() < size_t(19 + (62 * 128 * mbanks_count) + (62 * 128 * pbanks_count)))
+ {
+ std::fprintf(stderr, "WOPL: Version 1 size calculation failed %s\n", fn);
+ std::fflush(stderr);
+ return false;
+ }
+ }
+ else if(version >= 2)
+ {
+ //Header size + melodic bank meta data + percussion bank meta data + melodic banks + percussion banks
+ if(data.size() < size_t(19 + (34 * mbanks_count) + (34 * pbanks_count) + (62 * 128 * mbanks_count) + (62 * 128 * pbanks_count)))
+ {
+ std::fprintf(stderr, "WOPL: Version %d size calculation failed %s\n", version, fn);
+ std::fflush(stderr);
+ return false;
+ }
+ }
+
+ uint32_t melodic_offset = 0;
+ uint32_t percussion_offset = 0;
+ if(version < 2)
+ melodic_offset = 0x13;
+ else
+ melodic_offset = 0x13 + 34 * mbanks_count + 34 * pbanks_count;
+
+ percussion_offset = melodic_offset + (62 * 128 * mbanks_count);
+
+ for(uint32_t mbank = 0; mbank < 1; mbank++) // only first melodic bank (Until multi-banks support will be implemented)
+ {
+ uint32_t bank_offset = melodic_offset + (mbank * 62 * 128);
+
+ for(unsigned i = 0; i < 128; i++)
+ {
+ uint32_t offset = bank_offset + uint32_t(i * 62);
+ std::string name;
+ insdata tmp[2];
+
+ name.resize(32);
+ std::memcpy(&name[0], data.data() + offset, 32);
+ name.resize(std::strlen(&name[0]));
+
+ tmp[0].data[0] = data[offset + 42 + 5];
+ tmp[0].data[1] = data[offset + 42 + 0];
+ tmp[0].data[2] = data[offset + 42 + 7];
+ tmp[0].data[3] = data[offset + 42 + 2];
+ tmp[0].data[4] = data[offset + 42 + 8];
+ tmp[0].data[5] = data[offset + 42 + 3];
+ tmp[0].data[6] = data[offset + 42 + 9];
+ tmp[0].data[7] = data[offset + 42 + 4];
+ tmp[0].data[8] = data[offset + 42 + 6];
+ tmp[0].data[9] = data[offset + 42 + 1];
+ tmp[0].data[10] = data[offset + 40];
+
+ 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].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 = 0;
+ tmp2.pseudo4op = (flags & 0x02) != 0;
+ tmp2.voice2_fine_tune = 0;
+ tmp[0].diff = false;
+ tmp[1].diff = tmp2.pseudo4op;
+
+ int8_t fine_tune = (int8_t)data[offset + 37];
+ if(fine_tune != 0)
+ {
+ if(fine_tune == 1)
+ tmp2.voice2_fine_tune = 0.000025;
+ else if(fine_tune == -1)
+ tmp2.voice2_fine_tune = -0.000025;
+ else
+ tmp2.voice2_fine_tune = ((fine_tune * 15.625) / 1000.0);
+ }
+
+ if(name.empty())
+ name = std::string(1, '\377') + MidiInsName[i];
+ else
+ name.insert(0, 1, '\377');
+
+ char name2[512];
+ sprintf(name2, "%sM%u", prefix, i);
+
+ if((flags & 0x03) == 0)
+ {
+ size_t resno = InsertIns(tmp[0], tmp[0], tmp2, name, name2);
+ SetBank(bank, i, resno);
+ }
+ else
+ {
+ size_t resno = InsertIns(tmp[0], tmp[1], tmp2, name, name2);
+ SetBank(bank, i, resno);
+ }
+ }
+ }
+
+ for(uint32_t pbank = 0; pbank < 1; pbank++) // only first percussion bank (Until multi-banks support will be implemented)
+ {
+ uint32_t bank_offset = percussion_offset + (pbank * 62 * 128);
+
+ for(uint32_t i = 0; i < 128; i++)
+ {
+ uint32_t offset = bank_offset + (i * 62);
+ std::string name;
+ insdata tmp[2];
+
+ name.resize(32);
+ std::memcpy(&name[0], data.data() + offset, 32);
+ name.resize(std::strlen(&name[0]));
+/*
+ WOPL's
+
+ 0 AM/Vib/Env/Ksr/FMult characteristics
+ 1 Key Scale Level / Total level register data
+ 2 Attack / Decay
+ 3 Systain and Release register data
+ 4 Wave form
+
+ 5 AM/Vib/Env/Ksr/FMult characteristics
+ 6 Key Scale Level / Total level register data
+ 7 Attack / Decay
+ 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];
+
+ 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].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 = data[offset + 38];
+ tmp2.pseudo4op = (flags & 0x02) != 0;
+ tmp2.voice2_fine_tune = 0;
+ tmp[0].diff = false;
+ tmp[1].diff = tmp2.pseudo4op;
+
+ int8_t fine_tune = (int8_t)data[offset + 37];
+ if(fine_tune != 0)
+ {
+ if(fine_tune == 1)
+ tmp2.voice2_fine_tune = 0.000025;
+ else if(fine_tune == -1)
+ tmp2.voice2_fine_tune = -0.000025;
+ else
+ tmp2.voice2_fine_tune = ((fine_tune * 15.625) / 1000.0);
+ }
+
+ uint32_t gmno = i + 128;
+ int midi_index = (gmno < (128 + 35)) ? -1
+ : (gmno < (128 + 88)) ? int(gmno) - 35
+ : -1;
+
+ if(name.empty() && (midi_index >= 0))
+ name = std::string(1, '\377') + MidiInsName[midi_index];
+ if(!name.empty())
+ name.insert(0, 1, '\377');
+
+ char name2[512];
+ sprintf(name2, "%sP%u", prefix, gmno & 127);
+
+ if((flags & 0x03) == 0)
+ {
+ size_t resno = InsertIns(tmp[0], 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);
+ }
+ }
+ }
+ return true;
+}
+
+
+#endif // LOAD_WOPL_H
diff --git a/utils/gen_adldata/gen_adldata.cc b/utils/gen_adldata/gen_adldata.cc
new file mode 100644
index 0000000..6c77a12
--- /dev/null
+++ b/utils/gen_adldata/gen_adldata.cc
@@ -0,0 +1,506 @@
+//#ifdef __MINGW32__
+//typedef struct vswprintf {} swprintf;
+//#endif
+#include <cstdio>
+#include <string>
+#include <cstring>
+
+#include "ini/ini_processing.h"
+
+#include "progs_cache.h"
+#include "measurer.h"
+
+#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"
+
+int main(int argc, char**argv)
+{
+ if(argc == 1)
+ {
+ printf("Usage:\n"
+ "\n"
+ "bin/gen_adldata src/adldata.cpp\n"
+ "\n");
+ return 1;
+ }
+
+ const char *outFile_s = argv[1];
+
+ FILE *outFile = fopen(outFile_s, "w");
+ if(!outFile)
+ {
+ fprintf(stderr, "Can't open %s file for write!\n", outFile_s);
+ return 1;
+ }
+
+ 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"))
+ {
+ fprintf(stderr, "Can't open banks.ini!\n");
+ return 1;
+ }
+
+ uint32_t banks_count;
+ ini.beginGroup("General");
+ ini.read("banks", banks_count, 0);
+ ini.endGroup();
+
+ if(!banks_count)
+ {
+ fprintf(stderr, "Zero count of banks found in banks.ini!\n");
+ return 1;
+ }
+
+ for(uint32_t bank = 0; bank < banks_count; bank++)
+ {
+ if(!ini.beginGroup(std::string("bank-") + std::to_string(bank)))
+ {
+ fprintf(stderr, "Failed to find bank %u!\n", bank);
+ return 1;
+ }
+ std::string bank_name;
+ std::string filepath;
+ std::string filepath_d;
+ std::string prefix;
+ std::string prefix_d;
+ std::string filter_m;
+ std::string filter_p;
+ std::string format;
+
+ ini.read("name", bank_name, "Untitled");
+ ini.read("format", format, "Unknown");
+ ini.read("file", filepath, "");
+ ini.read("file-p", filepath_d, "");
+ ini.read("prefix", prefix, "");
+ ini.read("prefix-p", prefix_d, "");
+ ini.read("filter-m", filter_m, "");
+ ini.read("filter-p", filter_p, "");
+
+ if(filepath.empty())
+ {
+ fprintf(stderr, "Failed to load bank %u, file is empty!\n", bank);
+ return 1;
+ }
+
+ banknames.push_back(bank_name);
+
+ //printf("Loading %s...\n", filepath.c_str());
+
+ if(format == "AIL")
+ {
+ if(!LoadMiles(filepath.c_str(), bank, prefix.c_str()))
+ {
+ fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str());
+ return 1;
+ }
+ }
+ else
+ if(format == "Bisqwit")
+ {
+ if(!LoadBisqwit(filepath.c_str(), bank, prefix.c_str()))
+ {
+ fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str());
+ return 1;
+ }
+ }
+ else
+ if(format == "WOPL")
+ {
+ if(!LoadWopl(filepath.c_str(), bank, prefix.c_str()))
+ {
+ fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str());
+ return 1;
+ }
+ }
+ else
+ if(format == "OP2")
+ {
+ if(!LoadDoom(filepath.c_str(), bank, prefix.c_str()))
+ {
+ fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str());
+ return 1;
+ }
+ }
+ else
+ if(format == "TMB")
+ {
+ if(!LoadTMB(filepath.c_str(), bank, prefix.c_str()))
+ {
+ fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str());
+ return 1;
+ }
+ }
+ else
+ if(format == "Junglevision")
+ {
+ if(!LoadJunglevision(filepath.c_str(), bank, prefix.c_str()))
+ {
+ fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str());
+ return 1;
+ }
+ }
+ else
+ if(format == "AdLibGold")
+ {
+ if(!LoadBNK2(filepath.c_str(), bank, prefix.c_str(), filter_m, filter_p))
+ {
+ fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str());
+ return 1;
+ }
+ }
+ else
+ if(format == "HMI")
+ {
+ if(!LoadBNK(filepath.c_str(), bank, prefix.c_str(), false, false))
+ {
+ 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))
+ {
+ fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str());
+ return 1;
+ }
+ }
+ }
+ else
+ if(format == "IBK")
+ {
+ if(!LoadIBK(filepath.c_str(), bank, prefix.c_str(), false))
+ {
+ fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str());
+ return 1;
+ }
+ if(!filepath_d.empty())
+ {
+ //printf("Loading %s... \n", filepath_d.c_str());
+ if(!LoadIBK(filepath_d.c_str(),bank, prefix_d.c_str(), true))
+ {
+ fprintf(stderr, "Failed to load bank %u, file %s!\n", bank, filepath.c_str());
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ fprintf(stderr, "Failed to load bank %u, file %s!\nUnknown format type %s\n",
+ bank,
+ filepath.c_str(),
+ format.c_str());
+ return 1;
+ }
+
+
+ ini.endGroup();
+ }
+
+ printf("Loaded %u banks!\n", banks_count);
+ 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());
+ 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;
+ fprintf(outFile, "\tAdldata index %u, bit %u=mask %02X (%u matches)\n",
+ index / 8, index % 8, 1 << (index % 8), count);
+ }
+ }
+ #endif
+
+ printf("Writing raw instrument data...\n");
+ fflush(stdout);
+ {
+ 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;
+ 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);
+
+ 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);
+
+ 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;
+ }
+ fprintf(outFile, " }, // %u: %s\n", (unsigned)c, names.c_str());
+ }
+ }
+ fprintf(outFile, "};\n");
+ }
+
+ /*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"
+ "} adlins[] =\n");*/
+
+ fprintf(outFile, "const struct adlinsdata adlins[%u] =\n", (unsigned)instab.size());
+ fprintf(outFile, "{\n");
+
+ MeasureThreaded measureCounter;
+ {
+ std::printf("Beginning to generate measures data... (Hardware concurrency: %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(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);
+ }
+ }
+ 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;
+ {
+ if(info.peak_amplitude_time == 0)
+ {
+ 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
+ {
+ 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));
+ }
+ }
+
+ unsigned flags = (i->first.pseudo4op ? 1 : 0) | (info.nosound ? 2 : 0);
+
+ fprintf(outFile, " {");
+ fprintf(outFile, "%4d,%4d,%3d, %d, %6ld,%6ld,%lf",
+ (unsigned) i->first.insno1,
+ (unsigned) i->first.insno2,
+ (int)(i->first.notenum),
+ flags,
+ info.ms_sound_kon,
+ info.ms_sound_koff,
+ 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;
+ }
+ fprintf(outFile, " }, // %u: %s\n\n", (unsigned)c, names.c_str());
+ fflush(outFile);
+ adlins_flags.push_back(flags);
+ }
+ fprintf(outFile, "};\n\n");
+
+
+ printf("Writing banks data...\n");
+ 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;
+
+ fprintf(outFile,
+ "\n\n//Returns total number of generated banks\n"
+ "int maxAdlBanks()\n"
+ "{"
+ " return %u;\n"
+ "}\n\n"
+ "const char* const banknames[%u] =\n", (unsigned int)bankcount, (unsigned int)bankcount);
+ fprintf(outFile, "{\n");
+ for(unsigned bank = 0; bank < bankcount; ++bank)
+ fprintf(outFile, " \"%s\",\n", banknames[bank].c_str());
+ fprintf(outFile, "};\n");
+
+ fprintf(outFile, "const unsigned short banks[%u][256] =\n", (unsigned int)bankcount);
+ fprintf(outFile, "{\n");
+ for(unsigned bank = 0; bank < bankcount; ++bank)
+ {
+ fprintf(outFile, " { // bank %u, %s\n", bank, banknames[bank].c_str());
+ bool redundant = true;
+ for(unsigned p = 0; p < 256; ++p)
+ {
+ size_t v = bank_data[bank][p];
+ if(listed.find(v) == listed.end())
+ {
+ listed.insert(v);
+ redundant = false;
+ }
+ fprintf(outFile, "%4d,", (unsigned int)v);
+ if(p % 16 == 15) fprintf(outFile, "\n");
+ }
+ fprintf(outFile, " },\n");
+ if(redundant)
+ {
+ 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)
+ fprintf(outFile, " // Bank %u is just a subset of bank %u!\n",
+ bank, refbank);
+ }
+ }
+ }
+
+ fprintf(outFile, "};\n");
+ fflush(outFile);
+ fclose(outFile);
+
+ printf("Generation of ADLMIDI data has been completed!\n");
+ fflush(stdout);
+}
diff --git a/utils/gen_adldata/gen_adldata.pro b/utils/gen_adldata/gen_adldata.pro
new file mode 100644
index 0000000..74c504e
--- /dev/null
+++ b/utils/gen_adldata/gen_adldata.pro
@@ -0,0 +1,38 @@
+TEMPLATE=app
+CONFIG-=qt
+CONFIG+=console
+TARGET=gen_adldata
+DESTDIR=$$PWD/../../bin
+CONFIG += c++11
+
+include($$PWD/ini/IniProcessor.pri)
+
+#DEFINES += ADLMIDI_USE_DOSBOX_OPL
+
+QMAKE_CXXFLAGS_RELEASE += -O3 -finline-functions
+LIBS += -lpthread
+
+HEADERS += \
+ midi_inst_list.h \
+ ../nukedopl3.h \
+ ../dbopl.h \
+ progs_cache.h \
+ file_formats/load_bnk.h \
+ file_formats/load_bnk2.h \
+ file_formats/load_op2.h \
+ file_formats/load_ail.h \
+ file_formats/load_ibk.h \
+ file_formats/load_jv.h \
+ file_formats/load_tmb.h \
+ file_formats/load_bisqwit.h \
+ file_formats/load_wopl.h \
+ measurer.h \
+ file_formats/common.h
+
+SOURCES += \
+ gen_adldata.cc \
+ ../nukedopl3.c \
+ ../dbopl.cpp \
+ progs_cache.cpp \
+ measurer.cpp
+
diff --git a/utils/gen_adldata/ini/IniProcessor.pri b/utils/gen_adldata/ini/IniProcessor.pri
new file mode 100644
index 0000000..cdadb59
--- /dev/null
+++ b/utils/gen_adldata/ini/IniProcessor.pri
@@ -0,0 +1,10 @@
+
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/ini_processing.h \
+ $$PWD/ini_processing_variant.h \
+
+SOURCES += \
+ $$PWD/ini_processing.cpp
+
diff --git a/utils/gen_adldata/ini/ini_processing.cpp b/utils/gen_adldata/ini/ini_processing.cpp
new file mode 100644
index 0000000..2f15821
--- /dev/null
+++ b/utils/gen_adldata/ini/ini_processing.cpp
@@ -0,0 +1,1385 @@
+/*
+INI Processor - a small library which allows you parsing INI-files
+
+Copyright (c) 2017 Vitaliy Novichkov <admin@wohlnet.ru>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+//#define USE_FILE_MAPPER
+
+/* Stop parsing on first error (default is to keep parsing). */
+//#define INI_STOP_ON_FIRST_ERROR
+
+#include "ini_processing.h"
+#include <cstdio>
+#include <cstring>
+#include <cstdlib>
+#include <clocale>
+#include <sstream>
+#include <algorithm>
+#include <assert.h>
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#ifdef USE_FILE_MAPPER
+/*****Replace this with right path to file mapper class*****/
+#include "../fileMapper/file_mapper.h"
+#endif
+
+static const unsigned char utfbom[3] = {0xEF, 0xBB, 0xBF};
+
+enum { Space = 0x01, Special = 0x02, INIParamEq = 0x04 };
+
+static const unsigned char charTraits[256] =
+{
+ // Space: '\t', '\n', '\r', ' '
+ // Special: '\n', '\r', '"', ';', '=', '\\'
+ // INIParamEq: ':', '='
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, Space, Space | Special, 0, 0, Space | Special,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Space, 0, Special,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, INIParamEq,
+ Special, 0, Special | INIParamEq, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+#if 0//for speed comparison who faster - macro or inline function. Seems speeds are same
+#define IS_SPACE(c) (charTraits[static_cast<unsigned char>(c)] & Space)
+#define IS_SPECIAL(c) (charTraits[static_cast<unsigned char>(c)] & Special)
+#define IS_INIEQUAL(c) (charTraits[static_cast<unsigned char>(c)] & INIParamEq)
+#else
+inline unsigned char IS_SPACE(char &c)
+{
+ return (charTraits[static_cast<unsigned char>(c)] & Space);
+}
+inline unsigned char IS_SPECIAL(char &c)
+{
+ return (charTraits[static_cast<unsigned char>(c)] & Special);
+}
+inline unsigned char IS_INIEQUAL(char &c)
+{
+ return (charTraits[static_cast<unsigned char>(c)] & INIParamEq);
+}
+#endif
+
+/* Strip whitespace chars off end of given string, in place. Return s. */
+inline char *rstrip(char *s)
+{
+ char *p = s + strlen(s);
+
+ while(p > s && IS_SPACE(*--p))
+ *p = '\0';
+
+ return s;
+}
+
+/* Return pointer to first non-whitespace char in given string. */
+inline char *lskip(char *s)
+{
+ while(*s && IS_SPACE(*s))
+ s++;
+
+ return reinterpret_cast<char *>(s);
+}
+
+inline char *lrtrim(char *s)
+{
+ while(*s && IS_SPACE(*s))
+ s++;
+
+ char *p = s + strlen(s);
+
+ while(p > s && IS_SPACE(*--p))
+ *p = '\0';
+
+ return s;
+}
+
+/* Return pointer to first char c or ';' comment in given string, or pointer to
+ null at end of string if neither found. ';' must be prefixed by a whitespace
+ character to register as a comment. */
+inline char *find_char_or_comment(char *s, char c)
+{
+ unsigned char was_whitespace = 0;
+
+ while(*s && *s != c && !(was_whitespace && *s == ';'))
+ {
+ was_whitespace = IS_SPACE(*s);
+ s++;
+ }
+
+ return s;
+}
+
+inline char *find_inieq_or_comment(char *s)
+{
+ unsigned char was_whitespace = 0;
+
+ while(*s && (!IS_INIEQUAL(*s)) && !(was_whitespace && *s == ';'))
+ {
+ was_whitespace = IS_SPACE(*s);
+ s++;
+ }
+
+ return s;
+}
+
+inline char *removeQuotes(char *begin, char *end)
+{
+ if((*begin == '\0') || (begin == end))
+ return begin;
+
+ if((*begin == '"') && (begin + 1 != end))
+ begin++;
+ else
+ return begin;
+
+ if(*(end - 1) == '"')
+ *(end - 1) = '\0';
+
+ return begin;
+}
+
+inline char *unescapeString(char* str)
+{
+ char *src, *dst;
+ src = str;
+ dst = str;
+ while(*src)
+ {
+ if(*src == '\\')
+ {
+ src++;
+ switch(*src)
+ {
+ case 'n': *dst = '\n'; break;
+ case 'r': *dst = '\r'; break;
+ case 't': *dst = '\t'; break;
+ default: *dst = *src; break;
+ }
+ }
+ else
+ if(src != dst)
+ {
+ *dst = *src;
+ }
+ src++; dst++;
+ }
+ *dst = '\0';
+ return str;
+}
+
+//Remove comment line from a tail of value
+inline void skipcomment(char *value)
+{
+ unsigned char quoteDepth = 0;
+
+ while(*value)
+ {
+ if(quoteDepth > 0)
+ {
+ if(*value == '\\')
+ {
+ value++;
+ continue;
+ }
+
+ if(*value == '"')
+ --quoteDepth;
+ }
+ else if(*value == '"')
+ ++quoteDepth;
+
+ if((quoteDepth == 0) && (*value == ';'))
+ {
+ *value = '\0';
+ break;
+ }
+
+ value++;
+ }
+}
+
+inline bool memfgets(char *&line, char *data, char *&pos, char *end)
+{
+ line = pos;
+
+ while(pos != end)
+ {
+ if(*pos == '\n')
+ {
+ if((pos > data) && (*(pos - 1) == '\r'))
+ *((pos++) - 1) = '\0';//Support CRLF too
+ else
+ *(pos++) = '\0';
+
+ break;
+ }
+
+ ++pos;
+ }
+
+ return (pos != line);
+ //EOF is a moment when position wasn't changed.
+ //If do check "pos != end", will be an inability to read last line.
+ //this logic allows detect true EOF when line is really eof
+}
+
+/* See documentation in header file. */
+bool IniProcessing::parseHelper(char *data, size_t size)
+{
+ char *section = nullptr;
+ #if defined(INI_ALLOW_MULTILINE)
+ char *prev_name = nullptr;
+ #endif
+ char *start;
+ char *end;
+ char *name;
+ char *value;
+ int lineno = 0;
+ int error = 0;
+ char *line;
+ char *pos_end = data + size;
+ char *pos_cur = data;
+ params::IniKeys *recentKeys = nullptr;
+
+ /* Scan through file line by line */
+ //while (fgets(line, INI_MAX_LINE, file) != NULL)
+ while(memfgets(line, data, pos_cur, pos_end))
+ {
+ lineno++;
+ start = line;
+
+ if((lineno == 1) && (size >= 3) && (memcmp(start, utfbom, 3) == 0))
+ start += 3;
+
+ start = lrtrim(start);
+
+ if(!*start)//if empty line - skip it away!
+ continue;
+
+ switch(*start)
+ {
+ case ';':
+ case '#':
+ //if (*start == ';' || *start == '#') {
+ // /* Per Python ConfigParser, allow '#' comments at start of line */
+ //}
+ continue;
+
+ case '[':
+ {
+ /* A "[section]" line */
+ end = find_char_or_comment(start + 1, ']');
+
+ if(*end == ']')
+ {
+ *end = '\0';
+ section = start + 1;
+ //#if defined(INI_ALLOW_MULTILINE)
+ // prev_name = nullptr;
+ //#endif
+ recentKeys = &m_params.iniData[section];
+ }
+ else if(!error)
+ {
+ /* No ']' found on section line */
+ m_params.errorCode = ERR_SECTION_SYNTAX;
+ error = lineno;
+ }
+ }
+ break;
+
+ default:
+ {
+ /* Not a comment, must be a name[=:]value pair */
+ end = find_inieq_or_comment(start);
+
+ if(IS_INIEQUAL(*end))
+ {
+ *end = '\0';
+ name = rstrip(start);
+ value = lskip(end + 1);
+ end = find_char_or_comment(value, '\0');
+
+ #ifndef CASE_SENSITIVE_KEYS
+ for(char *iter = name; *iter != '\0'; ++iter)
+ *iter = (char)tolower(*iter);
+ #endif
+
+ if(*end == ';')
+ *end = '\0';
+
+ rstrip(value);
+ {
+ char *v = value;
+ skipcomment(v);
+ v = rstrip(v);
+
+ if(!recentKeys)
+ recentKeys = &m_params.iniData["General"];
+
+ #ifdef INIDEBUG
+ printf("-> [%s]; %s = %s\n", section, name, v);
+ #endif
+ (*recentKeys)[name] = unescapeString( removeQuotes(v, v + strlen(v)) );
+ }
+ }
+ else if(!error)
+ {
+ /* No '=' or ':' found on name[=:]value line */
+ m_params.errorCode = ERR_KEY_SYNTAX;
+ error = lineno;
+ }
+
+ break;
+ }
+ }//switch(*start)
+
+ #if defined(INI_STOP_ON_FIRST_ERROR)
+
+ if(error)
+ break;
+
+ #endif
+ }
+
+ m_params.lineWithError = error;
+ return (error == 0);
+}
+
+/* See documentation in header file. */
+bool IniProcessing::parseFile(const char *filename)
+{
+ bool valid = true;
+ char *tmp = nullptr;
+ #ifdef USE_FILE_MAPPER
+ //By mystical reasons, reading whole file form fread() is faster than mapper :-P
+ PGE_FileMapper file(filename);
+
+ if(!file.data)
+ {
+ m_params.errorCode = ERR_NOFILE;
+ return -1;
+ }
+
+ tmp = reinterpret_cast<char *>(malloc(static_cast<size_t>(file.size + 1)));
+
+ if(!tmp)
+ {
+ m_params.errorCode = ERR_NO_MEMORY;
+ return false;
+ }
+
+ memcpy(tmp, file.data, static_cast<size_t>(file.size));
+ *(tmp + file.size) = '\0';//null terminate last line
+ valid = ini_parse_file(tmp, static_cast<size_t>(file.size));
+ #else
+ #ifdef _WIN32
+ //Convert UTF8 file path into UTF16 to support non-ASCII paths on Windows
+ std::wstring dest;
+ dest.resize(std::strlen(filename));
+ int newSize = MultiByteToWideChar(CP_UTF8,
+ 0,
+ filename,
+ dest.size(),
+ (wchar_t *)dest.c_str(),
+ dest.size());
+ dest.resize(newSize);
+ FILE *cFile = _wfopen(dest.c_str(), L"rb");
+ #else
+ FILE *cFile = fopen(filename, "rb");
+ #endif
+
+ if(!cFile)
+ {
+ m_params.errorCode = ERR_NOFILE;
+ return false;
+ }
+
+ fseek(cFile, 0, SEEK_END);
+ ssize_t size = static_cast<ssize_t>(ftell(cFile));
+ if(size < 0)
+ {
+ m_params.errorCode = ERR_KEY_SYNTAX;
+ fclose(cFile);
+ return false;
+ }
+ fseek(cFile, 0, SEEK_SET);
+ tmp = reinterpret_cast<char *>(malloc(static_cast<size_t>(size + 1)));
+ if(!tmp)
+ {
+ fclose(cFile);
+ m_params.errorCode = ERR_NO_MEMORY;
+ return false;
+ }
+
+ if(fread(tmp, 1, static_cast<size_t>(size), cFile) != static_cast<size_t>(size))
+ valid = false;
+
+ fclose(cFile);
+ if(valid)
+ {
+ *(tmp + size) = '\0';//null terminate last line
+ try
+ {
+ valid = parseHelper(tmp, static_cast<size_t>(size));
+ }
+ catch(...)
+ {
+ valid = false;
+ m_params.errorCode = ERR_SECTION_SYNTAX;
+ }
+ }
+ #endif
+
+ free(tmp);
+ return valid;
+}
+
+bool IniProcessing::parseMemory(char *mem, size_t size)
+{
+ bool valid = true;
+ char *tmp = nullptr;
+ tmp = reinterpret_cast<char *>(malloc(size + 1));
+
+ if(!tmp)
+ {
+ m_params.errorCode = ERR_NO_MEMORY;
+ return false;
+ }
+
+ memcpy(tmp, mem, static_cast<size_t>(size));
+ *(tmp + size) = '\0';//null terminate last line
+ valid = parseHelper(tmp, size);
+ free(tmp);
+ return valid;
+}
+
+
+IniProcessing::IniProcessing() :
+ m_params{"", false, -1, ERR_OK, false, params::IniSections(), nullptr, ""}
+{}
+
+IniProcessing::IniProcessing(const char *iniFileName, int) :
+ m_params{iniFileName, false, -1, ERR_OK, false, params::IniSections(), nullptr, ""}
+{
+ open(iniFileName);
+}
+
+IniProcessing::IniProcessing(const std::string &iniFileName, int) :
+ m_params{iniFileName, false, -1, ERR_OK, false, params::IniSections(), nullptr, ""}
+{
+ open(iniFileName);
+}
+
+#ifdef INI_PROCESSING_ALLOW_QT_TYPES
+IniProcessing::IniProcessing(const QString &iniFileName, int) :
+ m_params{iniFileName.toStdString(), false, -1, ERR_OK, false, params::IniSections(), nullptr, ""}
+{
+ open(m_params.filePath);
+}
+#endif
+
+IniProcessing::IniProcessing(char *memory, size_t size):
+ m_params{"", false, -1, ERR_OK, false, params::IniSections(), nullptr, ""}
+{
+ openMem(memory, size);
+}
+
+IniProcessing::IniProcessing(const IniProcessing &ip) :
+ m_params(ip.m_params)
+{}
+
+bool IniProcessing::open(const std::string &iniFileName)
+{
+ std::setlocale(LC_NUMERIC, "C");
+
+ if(!iniFileName.empty())
+ {
+ close();
+ m_params.errorCode = ERR_OK;
+ m_params.filePath = iniFileName;
+ bool res = parseFile(m_params.filePath.c_str());
+ #ifdef INIDEBUG
+
+ if(res)
+ printf("\n==========WOOHOO!!!==============\n\n");
+ else
+ printf("\n==========OOOUCH!!!==============\n\n");
+
+ #endif
+ m_params.opened = res;
+ return res;
+ }
+
+ m_params.errorCode = ERR_NOFILE;
+ return false;
+}
+
+bool IniProcessing::openMem(char *memory, size_t size)
+{
+ std::setlocale(LC_NUMERIC, "C");
+
+ if((memory != nullptr) && (size > 0))
+ {
+ close();
+ m_params.errorCode = ERR_OK;
+ m_params.filePath.clear();
+ bool res = parseMemory(memory, size);
+ m_params.opened = res;
+ return res;
+ }
+
+ m_params.errorCode = ERR_NOFILE;
+ return false;
+}
+
+void IniProcessing::close()
+{
+ m_params.errorCode = ERR_OK;
+ m_params.iniData.clear();
+ m_params.opened = false;
+ m_params.lineWithError = -1;
+}
+
+IniProcessing::ErrCode IniProcessing::lastError()
+{
+ return m_params.errorCode;
+}
+
+int IniProcessing::lineWithError()
+{
+ return m_params.lineWithError;
+}
+
+bool IniProcessing::isOpened()
+{
+ return m_params.opened;
+}
+
+bool IniProcessing::beginGroup(const std::string &groupName)
+{
+ //Keep the group name. If not exist, will be created on value write
+ m_params.currentGroupName = groupName;
+
+ params::IniSections::iterator e = m_params.iniData.find(groupName);
+
+ if(e == m_params.iniData.end())
+ return false;
+
+ params::IniKeys &k = e->second;
+ m_params.currentGroup = &k;
+ return true;
+}
+
+bool IniProcessing::contains(const std::string &groupName)
+{
+ if(!m_params.opened)
+ return false;
+
+ params::IniSections::iterator e = m_params.iniData.find(groupName);
+ return (e != m_params.iniData.end());
+}
+
+std::string IniProcessing::fileName()
+{
+ return m_params.filePath;
+}
+
+std::string IniProcessing::group()
+{
+ return m_params.currentGroupName;
+}
+
+std::vector<std::string> IniProcessing::childGroups()
+{
+ std::vector<std::string> groups;
+ groups.reserve(m_params.iniData.size());
+ for(params::IniSections::iterator e = m_params.iniData.begin();
+ e != m_params.iniData.end();
+ e++)
+ {
+ groups.push_back(e->first);
+ }
+ return groups;
+}
+
+bool IniProcessing::hasKey(const std::string &keyName)
+{
+ if(!m_params.opened)
+ return false;
+
+ if(!m_params.currentGroup)
+ return false;
+
+ params::IniKeys::iterator e = m_params.currentGroup->find(keyName);
+ return (e != m_params.currentGroup->end());
+}
+
+std::vector<std::string> IniProcessing::allKeys()
+{
+ std::vector<std::string> keys;
+ if(!m_params.opened)
+ return keys;
+ if(!m_params.currentGroup)
+ return keys;
+
+ keys.reserve(m_params.currentGroup->size());
+
+ for(params::IniKeys::iterator it = m_params.currentGroup->begin();
+ it != m_params.currentGroup->end();
+ it++)
+ {
+ keys.push_back( it->first );
+ }
+
+ return keys;
+}
+
+void IniProcessing::endGroup()
+{
+ m_params.currentGroup = nullptr;
+ m_params.currentGroupName.clear();
+}
+
+void IniProcessing::read(const char *key, bool &dest, bool defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ std::string &k = e->second;
+ size_t i = 0;
+ size_t ss = std::min(static_cast<size_t>(4ul), k.size());
+ char buff[4] = {0, 0, 0, 0};
+ const char *pbufi = k.c_str();
+ char *pbuff = buff;
+
+ for(; i < ss; i++)
+ (*pbuff++) = static_cast<char>(std::tolower(*pbufi++));
+
+ if(ss < 4)
+ {
+ if(ss == 0)
+ {
+ dest = false;
+ return;
+ }
+
+ if(ss == 1)
+ {
+ dest = (buff[0] == '1');
+ return;
+ }
+
+ bool isNum = true;
+ isNum &= std::isdigit(buff[i]) || (buff[i] == '-') || (buff[i] == '+');
+
+ for(size_t i = 1; i < ss; i++)
+ isNum &= std::isdigit(buff[i]);
+
+ if(isNum)
+ {
+ long num = std::strtol(buff, 0, 0);
+ dest = num != 0l;
+ return;
+ }
+ else
+ {
+ dest = (std::memcmp(buff, "yes", 3) == 0) ||
+ (std::memcmp(buff, "on", 2) == 0);
+ return;
+ }
+ }
+
+ if(std::memcmp(buff, "true", 4) == 0)
+ {
+ dest = true;
+ return;
+ }
+
+ try
+ {
+ long num = std::strtol(buff, 0, 0);
+ dest = num != 0l;
+ return;
+ }
+ catch(...)
+ {
+ dest = false;
+ return;
+ }
+}
+
+void IniProcessing::read(const char *key, unsigned char &dest, unsigned char defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ std::string &k = e->second;
+
+ if(k.size() >= 1)
+ dest = static_cast<unsigned char>(k[0]);
+ else
+ dest = defVal;
+}
+
+void IniProcessing::read(const char *key, char &dest, char defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ std::string &k = e->second;
+
+ if(k.size() >= 1)
+ dest = k[0];
+ else
+ dest = defVal;
+}
+
+void IniProcessing::read(const char *key, unsigned short &dest, unsigned short defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ dest = static_cast<unsigned short>(std::strtoul(e->second.c_str(), nullptr, 0));
+}
+
+void IniProcessing::read(const char *key, short &dest, short defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ dest = static_cast<short>(std::strtol(e->second.c_str(), nullptr, 0));
+}
+
+void IniProcessing::read(const char *key, unsigned int &dest, unsigned int defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ dest = static_cast<unsigned int>(std::strtoul(e->second.c_str(), nullptr, 0));
+}
+
+void IniProcessing::read(const char *key, int &dest, int defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ dest = static_cast<int>(std::strtol(e->second.c_str(), nullptr, 0));
+}
+
+void IniProcessing::read(const char *key, unsigned long &dest, unsigned long defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ dest = std::strtoul(e->second.c_str(), nullptr, 0);
+}
+
+void IniProcessing::read(const char *key, long &dest, long defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ dest = std::strtol(e->second.c_str(), nullptr, 0);
+}
+
+void IniProcessing::read(const char *key, unsigned long long &dest, unsigned long long defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ dest = std::strtoull(e->second.c_str(), nullptr, 0);
+}
+
+void IniProcessing::read(const char *key, long long &dest, long long defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ dest = std::strtoll(e->second.c_str(), nullptr, 0);
+}
+
+void IniProcessing::read(const char *key, float &dest, float defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+ dest = std::strtof(e->second.c_str(), nullptr);
+}
+
+void IniProcessing::read(const char *key, double &dest, double defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+ dest = std::strtod(e->second.c_str(), nullptr);
+}
+
+void IniProcessing::read(const char *key, long double &dest, long double defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+ dest = std::strtold(e->second.c_str(), nullptr);
+}
+
+void IniProcessing::read(const char *key, std::string &dest, const std::string &defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ dest = e->second;
+}
+
+#ifdef INI_PROCESSING_ALLOW_QT_TYPES
+void IniProcessing::read(const char *key, QString &dest, const QString &defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ dest = QString::fromStdString(e->second);
+}
+#endif
+
+template<class TList>
+inline void StrToNumVectorHelper(const std::string &source, TList &dest, const typename TList::value_type &def)
+{
+ typedef typename TList::value_type T;
+ dest.clear();
+
+ if(!source.empty())
+ {
+ std::stringstream ss(source);
+ std::string item;
+ while(std::getline(ss, item, ','))
+ {
+ std::remove(item.begin(), item.end(), ' ');
+ try
+ {
+ if(std::is_same<T, int>::value ||
+ std::is_same<T, long>::value ||
+ std::is_same<T, short>::value)
+ dest.push_back(static_cast<T>(std::strtol(item.c_str(), NULL, 0)));
+ else if(std::is_same<T, unsigned int>::value ||
+ std::is_same<T, unsigned long>::value ||
+ std::is_same<T, unsigned short>::value)
+ dest.push_back(static_cast<T>(std::strtoul(item.c_str(), NULL, 0)));
+ else if(std::is_same<T, float>::value)
+ dest.push_back(std::strtof(item.c_str(), NULL));
+ else
+ dest.push_back(std::strtod(item.c_str(), NULL));
+ }
+ catch(...)
+ {
+ dest.pop_back();
+ }
+ }
+
+ if(dest.empty())
+ dest.push_back(def);
+ }
+ else
+ dest.push_back(def);
+}
+
+template<class TList, typename T>
+void readNumArrHelper(IniProcessing *self, const char *key, TList &dest, const TList &defVal)
+{
+ bool ok = false;
+ IniProcessing::params::IniKeys::iterator e = self->readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ StrToNumVectorHelper(e->second, dest, static_cast<T>(0));
+}
+
+void IniProcessing::read(const char *key, std::vector<unsigned short> &dest, const std::vector<unsigned short> &defVal)
+{
+ readNumArrHelper<std::vector<unsigned short>, unsigned short>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, std::vector<short> &dest, const std::vector<short> &defVal)
+{
+ readNumArrHelper<std::vector<short>, short>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, std::vector<unsigned int> &dest, const std::vector<unsigned int> &defVal)
+{
+ readNumArrHelper<std::vector<unsigned int>, unsigned int>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, std::vector<int> &dest, const std::vector<int> &defVal)
+{
+ readNumArrHelper<std::vector<int>, int>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, std::vector<unsigned long> &dest, const std::vector<unsigned long> &defVal)
+{
+ readNumArrHelper<std::vector<unsigned long>, unsigned long>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, std::vector<long> &dest, const std::vector<long> &defVal)
+{
+ readNumArrHelper<std::vector<long>, long>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, std::vector<unsigned long long> &dest, const std::vector<unsigned long long> &defVal)
+{
+ readNumArrHelper<std::vector<unsigned long long>, unsigned long long>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, std::vector<long long> &dest, const std::vector<long long> &defVal)
+{
+ readNumArrHelper<std::vector<long long>, long long>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, std::vector<float> &dest, const std::vector<float> &defVal)
+{
+ readNumArrHelper<std::vector<float>, float>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, std::vector<double> &dest, const std::vector<double> &defVal)
+{
+ readNumArrHelper<std::vector<double>, double>(this, key, dest, defVal);
+}
+
+void IniProcessing::read(const char *key, std::vector<long double> &dest, const std::vector<long double> &defVal)
+{
+ readNumArrHelper<std::vector<long double>, long double>(this, key, dest, defVal);
+}
+
+#ifdef INI_PROCESSING_ALLOW_QT_TYPES
+void IniProcessing::read(const char *key, QList<short> &dest, const QList<short> &defVal)
+{
+ readNumArrHelper<QList<short>, short>(this, key, dest, defVal);
+}
+
+void IniProcessing::read(const char *key, QList<unsigned short> &dest, const QList<unsigned short> &defVal)
+{
+ readNumArrHelper<QList<unsigned short>, unsigned short>(this, key, dest, defVal);
+}
+
+void IniProcessing::read(const char *key, QList<int> &dest, const QList<int> &defVal)
+{
+ readNumArrHelper<QList<int>, int>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QList<unsigned int> &dest, const QList<unsigned int> &defVal)
+{
+ readNumArrHelper<QList<unsigned int>, unsigned int>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QList<long> &dest, const QList<long> &defVal)
+{
+ readNumArrHelper<QList<long>, long>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QList<unsigned long> &dest, const QList<unsigned long> &defVal)
+{
+ readNumArrHelper<QList<unsigned long>, unsigned long>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QList<long long> &dest, const QList<long long> &defVal)
+{
+ readNumArrHelper<QList<long long>, long long>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QList<unsigned long long> &dest, const QList<unsigned long long> &defVal)
+{
+ readNumArrHelper<QList<unsigned long long>, unsigned long long>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QList<float> &dest, const QList<float> &defVal)
+{
+ readNumArrHelper<QList<float>, float>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QList<double> &dest, const QList<double> &defVal)
+{
+ readNumArrHelper<QList<double>, double>(this, key, dest, defVal);
+}
+
+void IniProcessing::read(const char *key, QList<long double> &dest, const QList<long double> &defVal)
+{
+ readNumArrHelper<QList<long double>, long double>(this, key, dest, defVal);
+}
+
+void IniProcessing::read(const char *key, QVector<short> &dest, const QVector<short> &defVal)
+{
+ readNumArrHelper<QVector<short>, short>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QVector<unsigned short> &dest, const QVector<unsigned short> &defVal)
+{
+ readNumArrHelper<QVector<unsigned short>, unsigned short>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QVector<int> &dest, const QVector<int> &defVal)
+{
+ readNumArrHelper<QVector<int>, int>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QVector<unsigned int> &dest, const QVector<unsigned int> &defVal)
+{
+ readNumArrHelper<QVector<unsigned int>, unsigned int>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QVector<long> &dest, const QVector<long> &defVal)
+{
+ readNumArrHelper<QVector<long>, long>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QVector<unsigned long> &dest, const QVector<unsigned long> &defVal)
+{
+ readNumArrHelper<QVector<unsigned long>, unsigned long>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QVector<long long> &dest, const QVector<long long> &defVal)
+{
+ readNumArrHelper<QVector<long long>, long long>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QVector<unsigned long long> &dest, const QVector<unsigned long long> &defVal)
+{
+ readNumArrHelper<QVector<unsigned long long>, unsigned long long>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QVector<float> &dest, const QVector<float> &defVal)
+{
+ readNumArrHelper<QVector<float>, float>(this, key, dest, defVal);
+}
+void IniProcessing::read(const char *key, QVector<double> &dest, const QVector<double> &defVal)
+{
+ readNumArrHelper<QVector<double>, double>(this, key, dest, defVal);
+}
+
+void IniProcessing::read(const char *key, QVector<long double> &dest, const QVector<long double> &defVal)
+{
+ readNumArrHelper<QVector<long double>, long double>(this, key, dest, defVal);
+}
+#endif
+
+IniProcessingVariant IniProcessing::value(const char *key, const IniProcessingVariant &defVal)
+{
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ return defVal;
+
+ std::string &k = e->second;
+ return IniProcessingVariant(&k);
+}
+
+void IniProcessing::writeIniParam(const char *key, const std::string &value)
+{
+ if(m_params.currentGroupName.empty())
+ return;
+
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+ if(ok)
+ {
+ e->second = value;
+ }
+ else
+ {
+ if(!m_params.currentGroup)
+ {
+ m_params.iniData.insert({m_params.currentGroupName, params::IniKeys()});
+ m_params.currentGroup = &m_params.iniData[m_params.currentGroupName];
+ }
+ m_params.currentGroup->insert({std::string(key), value});
+ //Mark as opened
+ m_params.opened = true;
+ }
+}
+
+void IniProcessing::setValue(const char *key, unsigned short value)
+{
+ writeIniParam(key, std::to_string(value));
+}
+
+void IniProcessing::setValue(const char *key, short value)
+{
+ writeIniParam(key, std::to_string(value));
+}
+
+void IniProcessing::setValue(const char *key, unsigned int value)
+{
+ writeIniParam(key, std::to_string(value));
+}
+
+void IniProcessing::setValue(const char *key, int value)
+{
+ writeIniParam(key, std::to_string(value));
+}
+
+void IniProcessing::setValue(const char *key, unsigned long value)
+{
+ writeIniParam(key, std::to_string(value));
+}
+
+void IniProcessing::setValue(const char *key, long value)
+{
+ writeIniParam(key, std::to_string(value));
+}
+
+void IniProcessing::setValue(const char *key, unsigned long long value)
+{
+ writeIniParam(key, std::to_string(value));
+}
+
+void IniProcessing::setValue(const char *key, long long value)
+{
+ writeIniParam(key, std::to_string(value));
+}
+
+void IniProcessing::setValue(const char *key, float value)
+{
+ writeIniParam(key, IniProcessing::to_string_with_precision(value));
+}
+
+void IniProcessing::setValue(const char *key, double value)
+{
+ writeIniParam(key, IniProcessing::to_string_with_precision(value));
+}
+
+void IniProcessing::setValue(const char *key, long double value)
+{
+ writeIniParam(key, IniProcessing::to_string_with_precision(value));
+}
+
+void IniProcessing::setValue(const char *key, const char *value)
+{
+ writeIniParam(key, value);
+}
+
+void IniProcessing::setValue(const char *key, const std::string &value)
+{
+ writeIniParam(key, value);
+}
+
+#ifdef INI_PROCESSING_ALLOW_QT_TYPES
+void IniProcessing::setValue(const char *key, const QString &value)
+{
+ writeIniParam(key, value.toStdString());
+}
+#endif
+
+static inline bool isFloatValue(const std::string &str)
+{
+ enum State
+ {
+ ST_SIGN = 0,
+ ST_DOT,
+ ST_EXPONENT,
+ ST_EXPONENT_SIGN,
+ ST_TAIL
+ } st = ST_SIGN;
+
+ for(const char &c : str)
+ {
+ if(!isdigit(c))
+ {
+ switch(st)
+ {
+ case ST_SIGN:
+ if(c != '-')
+ return false;
+ st = ST_DOT;
+ continue;
+ case ST_DOT:
+ if(c != '.')
+ return false;
+ else
+ if((c == 'E') || (c == 'e'))
+ st = ST_EXPONENT_SIGN;
+ else
+ st = ST_EXPONENT;
+ continue;
+ case ST_EXPONENT:
+ if((c != 'E') && (c != 'e'))
+ return false;
+ st = ST_EXPONENT_SIGN;
+ continue;
+ case ST_EXPONENT_SIGN:
+ if(c != '-')
+ return false;
+ st = ST_TAIL;
+ continue;
+ case ST_TAIL:
+ return false;
+ }
+ return false;
+ }
+ else
+ {
+ if(st == ST_SIGN)
+ st = ST_DOT;
+ }
+ }
+ return true;
+}
+
+bool IniProcessing::writeIniFile()
+{
+ #ifdef _WIN32
+ //Convert UTF8 file path into UTF16 to support non-ASCII paths on Windows
+ std::wstring dest;
+ dest.resize(m_params.filePath.size());
+ int newSize = MultiByteToWideChar(CP_UTF8,
+ 0,
+ m_params.filePath.c_str(),
+ dest.size(),
+ (wchar_t *)dest.c_str(),
+ dest.size());
+ dest.resize(newSize);
+ FILE *cFile = _wfopen(dest.c_str(), L"wb");
+ #else
+ FILE *cFile = fopen(m_params.filePath.c_str(), "wb");
+ #endif
+ if(!cFile)
+ return false;
+
+ for(params::IniSections::iterator group = m_params.iniData.begin();
+ group != m_params.iniData.end();
+ group++)
+ {
+ fprintf(cFile, "[%s]\n", group->first.c_str());
+ for(params::IniKeys::iterator key = group->second.begin();
+ key != group->second.end();
+ key++)
+ {
+ if(isFloatValue(key->second))
+ {
+ //Store as-is without quoting
+ fprintf(cFile, "%s = %s\n", key->first.c_str(), key->second.c_str());
+ }
+ else
+ {
+ //Set escape quotes and put the string with a quotes
+ std::string &s = key->second;
+ std::string escaped;
+ escaped.reserve(s.length() * 2);
+ for(char &c : s)
+ {
+ switch(c)
+ {
+ case '\n': escaped += "\\n"; break;
+ case '\r': escaped += "\\r"; break;
+ case '\t': escaped += "\\t"; break;
+ default:
+ if((c == '\\') || (c == '"'))
+ escaped.push_back('\\');
+ escaped.push_back(c);
+ }
+ }
+ fprintf(cFile, "%s = \"%s\"\n", key->first.c_str(), escaped.c_str());
+ }
+ }
+ fprintf(cFile, "\n");
+ fflush(cFile);
+ }
+ fclose(cFile);
+ return true;
+}
+
diff --git a/utils/gen_adldata/ini/ini_processing.h b/utils/gen_adldata/ini/ini_processing.h
new file mode 100644
index 0000000..52ab2e2
--- /dev/null
+++ b/utils/gen_adldata/ini/ini_processing.h
@@ -0,0 +1,677 @@
+/*
+INI Processor - a small library which allows you parsing INI-files
+
+Copyright (c) 2017 Vitaliy Novichkov <admin@wohlnet.ru>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef INIPROCESSING_H
+#define INIPROCESSING_H
+
+#include <string>
+#include <cstring>
+#include <cstdlib>
+#include <vector>
+#include <unordered_map>
+#ifdef INI_PROCESSING_ALLOW_QT_TYPES
+#include <QString>
+#include <QList>
+#include <QVector>
+#endif
+
+#include "ini_processing_variant.h"
+
+/**
+ * @brief INI Processor - an utility which providing fast and flexible INI file parsing API
+ */
+class IniProcessing
+{
+public:
+ /**
+ * @brief Available error codes
+ */
+ enum ErrCode
+ {
+ //! Everything is fine
+ ERR_OK = 0,
+ //! File not found or memory pointer is null
+ ERR_NOFILE,
+ //! Invalid section declaration syntax
+ ERR_SECTION_SYNTAX,
+ //! Invalid key declaration syntax
+ ERR_KEY_SYNTAX,
+ //! No available memory
+ ERR_NO_MEMORY
+ };
+
+private:
+ struct params
+ {
+ std::string filePath;
+ bool opened;
+ int lineWithError;
+ ErrCode errorCode;
+ bool modified;
+ typedef std::unordered_map<std::string, std::string> IniKeys;
+ typedef std::unordered_map<std::string, IniKeys> IniSections;
+ IniSections iniData;
+ IniKeys *currentGroup;
+ std::string currentGroupName;
+ } m_params;
+
+ template<class TList, typename T>
+ friend void readNumArrHelper(IniProcessing* self, const char *key, TList &dest, const TList &defVal);
+
+ bool parseHelper(char *data, size_t size);
+
+ bool parseFile(const char *filename);
+ bool parseMemory(char *mem, size_t size);
+
+ inline params::IniKeys::iterator readHelper(const char *key, bool &ok)
+ {
+ if(!m_params.opened)
+ return params::IniKeys::iterator();
+
+ if(!m_params.currentGroup)
+ return params::IniKeys::iterator();
+
+ #ifndef CASE_SENSITIVE_KEYS
+ std::string key1(key);
+ for(char *iter = &key1[0]; *iter != '\0'; ++iter)
+ *iter = (char)tolower(*iter);
+ #endif
+
+ params::IniKeys::iterator e = m_params.currentGroup->find(key1);
+
+ if(e != m_params.currentGroup->end())
+ ok = true;
+
+ return e;
+ }
+
+ void writeIniParam(const char *key, const std::string &value);
+
+public:
+ IniProcessing();
+ IniProcessing(const char *iniFileName, int dummy = 0);
+ IniProcessing(const std::string &iniFileName, int dummy = 0);
+#ifdef INI_PROCESSING_ALLOW_QT_TYPES
+ IniProcessing(const QString &iniFileName, int dummy = 0);
+#endif
+ IniProcessing(char *memory, size_t size);
+ IniProcessing(const IniProcessing &ip);
+
+ /**
+ * @brief Open INI-file from disk and parse it
+ * @param iniFileName full path to INI-file to parse
+ * @return true if INI file has been passed, false if any error happen
+ */
+ bool open(const std::string &iniFileName);
+
+ /**
+ * @brief Open raw INI-data from memory and parse it
+ * @param memory pointer to memory block
+ * @param size size of memory block to process
+ * @return
+ */
+ bool openMem(char *memory, size_t size);
+
+ /**
+ * @brief Clear all internal buffers and close the file
+ */
+ void close();
+
+ /**
+ * @brief Returns last happen error code
+ * @return Error code
+ */
+ ErrCode lastError();
+ /**
+ * @brief Line number which contains error
+ * @return line number wit herror
+ */
+ int lineWithError();
+
+ /**
+ * @brief State of INI Processor
+ * @return true if any file is opened
+ */
+ bool isOpened();
+
+ /**
+ * @brief Select a section to process
+ * @param groupName name of section to process
+ * @return true if group exists and opened
+ */
+ bool beginGroup(const std::string &groupName);
+
+ /**
+ * @brief Is this INI file contains section of specific name
+ * @param groupName name of section
+ * @return true if section is exists
+ */
+ bool contains(const std::string &groupName);
+
+ /**
+ * @brief Currently opened file name
+ * @return path to currently opened file
+ */
+ std::string fileName();
+
+ /**
+ * @brief Currently processing section
+ * @return name of current section
+ */
+ std::string group();
+
+ /**
+ * @brief Get list of available groups
+ * @return Array of strings
+ */
+ std::vector<std::string> childGroups();
+
+ /**
+ * @brief Is current section contains specific key name
+ * @param keyName name of key
+ * @return true if key is presented in this section
+ */
+ bool hasKey(const std::string &keyName);
+
+ /**
+ * @brief Get list of available keys in current groul
+ * @return Array of strings
+ */
+ std::vector<std::string> allKeys();
+
+ /**
+ * @brief Release current section to choice another for process
+ */
+ void endGroup();
+
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, bool &dest, bool defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, unsigned char &dest, unsigned char defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, char &dest, char defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, unsigned short &dest, unsigned short defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, short &dest, short defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, unsigned int &dest, unsigned int defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, int &dest, int defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, unsigned long &dest, unsigned long defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, long &dest, long defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, unsigned long long &dest, unsigned long long defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, long long &dest, long long defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, float &dest, float defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, double &dest, double defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, long double &dest, long double defVal);
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, std::string &dest, const std::string &defVal);
+
+ #ifdef INI_PROCESSING_ALLOW_QT_TYPES
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QString &dest, const QString &defVal);
+ #endif
+
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, std::vector<unsigned short> &dest, const std::vector<unsigned short> &defVal = std::vector<unsigned short>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, std::vector<short> &dest, const std::vector<short> &defVal = std::vector<short>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, std::vector<unsigned int> &dest, const std::vector<unsigned int> &defVal = std::vector<unsigned int>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, std::vector<int> &dest, const std::vector<int> &defVal = std::vector<int>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, std::vector<unsigned long> &dest, const std::vector<unsigned long> &defVal = std::vector<unsigned long>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, std::vector<long> &dest, const std::vector<long> &defVal = std::vector<long>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, std::vector<unsigned long long> &dest, const std::vector<unsigned long long> &defVal = std::vector<unsigned long long>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, std::vector<long long> &dest, const std::vector<long long> &defVal = std::vector<long long>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, std::vector<float> &dest, const std::vector<float> &defVal = std::vector<float>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, std::vector<double> &dest, const std::vector<double> &defVal = std::vector<double>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, std::vector<long double> &dest, const std::vector<long double> &defVal = std::vector<long double>());
+
+ #ifdef INI_PROCESSING_ALLOW_QT_TYPES
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QList<short> &dest, const QList<short> &defVal = QList<short>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QList<unsigned short> &dest, const QList<unsigned short> &defVal = QList<unsigned short>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QList<int> &dest, const QList<int> &defVal = QList<int>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QList<unsigned int> &dest, const QList<unsigned int> &defVal = QList<unsigned int>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QList<long> &dest, const QList<long> &defVal = QList<long>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QList<unsigned long> &dest, const QList<unsigned long> &defVal = QList<unsigned long>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QList<long long> &dest, const QList<long long> &defVal = QList<long long>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QList<unsigned long long> &dest, const QList<unsigned long long> &defVal = QList<unsigned long long>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QList<float> &dest, const QList<float> &defVal = QList<float>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QList<double> &dest, const QList<double> &defVal = QList<double>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QList<long double> &dest, const QList<long double> &defVal = QList<long double>());
+
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QVector<short> &dest, const QVector<short> &defVal = QVector<short>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QVector<unsigned short> &dest, const QVector<unsigned short> &defVal = QVector<unsigned short>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QVector<int> &dest, const QVector<int> &defVal = QVector<int>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QVector<unsigned int> &dest, const QVector<unsigned int> &defVal = QVector<unsigned int>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QVector<long> &dest, const QVector<long> &defVal = QVector<long>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QVector<unsigned long> &dest, const QVector<unsigned long> &defVal = QVector<unsigned long>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QVector<long long> &dest, const QVector<long long> &defVal = QVector<long long>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QVector<unsigned long long> &dest, const QVector<unsigned long long> &defVal = QVector<unsigned long long>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QVector<float> &dest, const QVector<float> &defVal = QVector<float>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QVector<double> &dest, const QVector<double> &defVal = QVector<double>());
+ /**
+ * @brief Retreive value by specific key and pass it via reference
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ */
+ void read(const char *key, QVector<long double> &dest, const QVector<long double> &defVal = QVector<long double>());
+ #endif
+
+ //! Hash-table for the fast string to enum conversion
+ typedef std::unordered_map<std::string, int> StrEnumMap;
+
+
+ template<typename T>
+ /**
+ * @brief Retreive value by string-based enum key
+ * @param [_IN] key name of key with value to retrieved
+ * @param [_OUT] dest Reference to destination variable to store retrieved value
+ * @param [_IN] defVal Default value for case of non-existing key
+ * @param [_IN] enumMap
+ */
+ void readEnum(const char *key, T &dest, T defVal, IniProcessing::StrEnumMap enumMap)
+ {
+ bool ok = false;
+ params::IniKeys::iterator e = readHelper(key, ok);
+
+ if(!ok)
+ {
+ dest = defVal;
+ return;
+ }
+
+ StrEnumMap::iterator em = enumMap.find(e->second);
+
+ if(em == enumMap.end())
+ {
+ dest = defVal;
+ return;
+ }
+
+ dest = static_cast<T>(em->second);
+ }
+
+ /**
+ * @brief QSettings-compatible way to retreive value
+ * @param key key with value to retreive
+ * @param defVal default value key
+ * @return variant which contains a value
+ */
+ IniProcessingVariant value(const char *key, const IniProcessingVariant &defVal = IniProcessingVariant());
+
+ void setValue(const char *key, unsigned short value);
+ void setValue(const char *key, short value);
+ void setValue(const char *key, unsigned int value);
+ void setValue(const char *key, int value);
+ void setValue(const char *key, unsigned long value);
+ void setValue(const char *key, long value);
+ void setValue(const char *key, unsigned long long value);
+ void setValue(const char *key, long long value);
+ void setValue(const char *key, float value);
+ void setValue(const char *key, double value);
+ void setValue(const char *key, long double value);
+
+ template <typename T>
+ static inline std::string to_string_with_precision(const T a_value)
+ {
+ char buf[35];
+ memset(buf, 0, 35);
+ snprintf(buf, 34, "%.15g", static_cast<double>(a_value));
+ return buf;
+ }
+
+ template<class TList>
+ static inline std::string fromVector(const TList &value)
+ {
+ typedef typename TList::value_type T;
+ std::string out;
+ for(const T &f: value)
+ {
+ if(!out.empty())
+ out.push_back(',');
+ if(std::is_same<T, float>::value ||
+ std::is_same<T, double>::value ||
+ std::is_same<T, long double>::value)
+ out.append(to_string_with_precision(f));
+ else
+ out.append(std::to_string(f));
+ }
+ return out;
+ }
+
+ template<typename T>
+ void setValue(const char *key, const std::vector<T> &value)
+ {
+ static_assert(std::is_arithmetic<T>::value, "Not arithmetic (integral or floating point required!)");
+ writeIniParam(key, fromVector(value));
+ }
+
+ void setValue(const char *key, const char *value);
+ void setValue(const char *key, const std::string &value);
+
+ #ifdef INI_PROCESSING_ALLOW_QT_TYPES
+ void setValue(const char *key, const QString &value);
+
+ template<typename T>
+ void setValue(const char *key, const QList<T> &value)
+ {
+ static_assert(std::is_arithmetic<T>::value, "Not arithmetic (integral or floating point required!)");
+ writeIniParam(key, fromVector(value));
+ }
+
+ template<typename T>
+ void setValue(const char *key, const QVector<T> &value)
+ {
+ static_assert(std::is_arithmetic<T>::value, "Not arithmetic (integral or floating point required!)");
+ writeIniParam(key, fromVector(value));
+ }
+ #endif
+
+ /**
+ * @brief Write INI file by the recently given file path
+ * @return true if INI file was successfully written
+ */
+ bool writeIniFile();
+};
+
+#endif // INIPROCESSING_H
diff --git a/utils/gen_adldata/ini/ini_processing_variant.h b/utils/gen_adldata/ini/ini_processing_variant.h
new file mode 100644
index 0000000..34f999a
--- /dev/null
+++ b/utils/gen_adldata/ini/ini_processing_variant.h
@@ -0,0 +1,243 @@
+/*
+INI Processor - a small library which allows you parsing INI-files
+
+Copyright (c) 2017 Vitaliy Novichkov <admin@wohlnet.ru>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/*
+A QVariant-like thing created just like a proxy between
+INI Processor and target value (to be compatible with QSettings)
+*/
+
+#ifndef INI_PROCESSING_VARIANT_H
+#define INI_PROCESSING_VARIANT_H
+
+#include <string>
+#include <cstring>
+#include <cstdlib>
+#ifdef INI_PROCESSING_ALLOW_QT_TYPES
+#include <QString>
+#endif
+
+class IniProcessingVariant
+{
+ std::string m_data;
+ std::string *m_dataP;
+ inline std::string &data()
+ {
+ if(m_dataP)
+ return *m_dataP;
+ else
+ return m_data;
+ }
+public:
+ IniProcessingVariant():
+ m_data(""),
+ m_dataP(nullptr) {}
+
+ IniProcessingVariant(const std::string &data):
+ m_data(data),
+ m_dataP(nullptr) {}
+ IniProcessingVariant(const char *data):
+ m_data(data),
+ m_dataP(nullptr) {}
+ #ifdef INI_PROCESSING_ALLOW_QT_TYPES
+ IniProcessingVariant(const QString &data):
+ m_data(data.toStdString()),
+ m_dataP(nullptr) {}
+ #endif
+ IniProcessingVariant(std::string *dataPointer):
+ m_data(""),
+ m_dataP(dataPointer) {}
+
+ IniProcessingVariant(const IniProcessingVariant &v):
+ m_data(v.m_data),
+ m_dataP(v.m_dataP) {}
+
+ IniProcessingVariant(char data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+
+ IniProcessingVariant(unsigned char data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+
+ IniProcessingVariant(bool data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+
+ IniProcessingVariant(short data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+
+ IniProcessingVariant(unsigned short data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+
+ IniProcessingVariant(int data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+ IniProcessingVariant(unsigned int data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+
+ IniProcessingVariant(long data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+ IniProcessingVariant(unsigned long data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+
+ IniProcessingVariant(long long data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+ IniProcessingVariant(unsigned long long data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+
+ IniProcessingVariant(float data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+
+ IniProcessingVariant(double data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+
+ IniProcessingVariant(long double data):
+ m_data(std::to_string(data)),
+ m_dataP(nullptr) {}
+
+ bool isNull()
+ {
+ return (m_data.empty() && !m_dataP);
+ }
+
+ bool isValid()
+ {
+ return ((!m_data.empty()) || (static_cast<std::string *>(m_dataP)));
+ }
+
+ std::string toString()
+ {
+ std::string out = data();
+
+ if((out.size() > 2) && (out[0] == '"'))
+ out.erase(0, 1);
+
+ if((out.size() > 1) && (out[out.size() - 1] == '"'))
+ out.erase((out.size() - 1), 1);
+
+ return out;
+ }
+
+ #ifdef INI_PROCESSING_ALLOW_QT_TYPES
+ QString toQString()
+ {
+ return QString::fromStdString(toString());
+ }
+ #endif
+
+ bool toBool()
+ {
+ size_t i = 0;
+ size_t ss = std::min(static_cast<size_t>(4ul), data().size());
+ char buff[4] = {0, 0, 0, 0};
+ const char *pbufi = data().c_str();
+ char *pbuff = buff;
+
+ for(; i < ss; i++)
+ (*pbuff++) = static_cast<char>(std::tolower(*pbufi++));
+
+ if(ss < 4)
+ {
+ if(ss == 0)
+ return false;
+
+ if(ss == 1)
+ return (buff[0] == '1');
+
+ try
+ {
+ long num = std::strtol(buff, 0, 0);
+ return num != 0l;
+ }
+ catch(...)
+ {
+ bool res = (std::memcmp(buff, "yes", 3) == 0) ||
+ (std::memcmp(buff, "on", 2) == 0);
+ return res;
+ }
+ }
+
+ if(std::memcmp(buff, "true", 4) == 0)
+ return true;
+
+ try
+ {
+ long num = std::strtol(buff, 0, 0);
+ return num != 0l;
+ }
+ catch(...)
+ {
+ return false;
+ }
+ }
+
+ int toInt()
+ {
+ return std::atoi(data().c_str());
+ }
+ unsigned int toUInt()
+ {
+ return static_cast<unsigned int>(std::strtoul(data().c_str(), nullptr, 0));
+ }
+
+ long toLong()
+ {
+ return std::atol(data().c_str());
+ }
+ unsigned long toULong()
+ {
+ return std::strtoul(data().c_str(), nullptr, 0);
+ }
+
+ long long toLongLong()
+ {
+ return std::atoll(data().c_str());
+ }
+ unsigned long long toULongLong()
+ {
+ return std::strtoull(data().c_str(), nullptr, 0);
+ }
+
+ float toFloat()
+ {
+ return float(std::atof(data().c_str()));
+ }
+
+ double toDouble()
+ {
+ return std::atof(data().c_str());
+ }
+};
+
+
+#endif // INI_PROCESSING_VARIANT_H
diff --git a/utils/gen_adldata/measurer.cpp b/utils/gen_adldata/measurer.cpp
new file mode 100644
index 0000000..8bb0772
--- /dev/null
+++ b/utils/gen_adldata/measurer.cpp
@@ -0,0 +1,543 @@
+#include "measurer.h"
+#include <cmath>
+
+#ifndef ADLMIDI_USE_DOSBOX_OPL
+#include "nukedopl3.h"
+#else
+#include "dbopl.h"
+#endif
+
+DurationInfo MeasureDurations(const ins &in)
+{
+ std::vector<int16_t> stereoSampleBuf;
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+ std::vector<int32_t> stereoSampleBuf_32;
+#endif
+ insdata id[2];
+ bool found[2] = {false, false};
+ for(InstrumentDataTab::const_iterator j = insdatatab.begin();
+ j != insdatatab.end();
+ ++j)
+ {
+ 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;
+ }
+ }
+ const unsigned rate = 22010;
+ const unsigned interval = 150;
+ const unsigned samples_per_interval = rate / interval;
+ const int notenum =
+ in.notenum < 20 ? (44 + in.notenum)
+ : in.notenum >= 128 ? (44 + 128 - in.notenum)
+ : in.notenum;
+
+#ifndef ADLMIDI_USE_DOSBOX_OPL
+#define WRITE_REG(key, value) OPL3_WriteReg(&opl, (Bit8u)(key), (Bit8u)(value))
+ _opl3_chip opl;
+#else
+#define WRITE_REG(key, value) opl.WriteReg((Bit8u)(key), (Bit8u)(value));
+ DBOPL::Handler opl;
+#endif
+
+ static const short initdata[(2 + 3 + 2 + 2) * 2] =
+ {
+ 0x004, 96, 0x004, 128, // Pulse timer
+ 0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable, leave disabled
+ 0x001, 32, 0x0BD, 0 // Enable wave & melodic
+};
+#ifndef ADLMIDI_USE_DOSBOX_OPL
+ OPL3_Reset(&opl, rate);
+#else
+ opl.Init(rate);
+#endif
+ for(unsigned a = 0; a < 18; a += 2) WRITE_REG(initdata[a], initdata[a + 1]);
+
+ const unsigned n_notes = in.insno1 == in.insno2 ? 1 : 2;
+ unsigned x[2];
+
+ if(n_notes == 2 && !in.pseudo4op)
+ {
+ WRITE_REG(0x105, 1);
+ WRITE_REG(0x104, 1);
+ }
+
+ for(unsigned n = 0; n < n_notes; ++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) WRITE_REG(patchdata[a] + n * 8, id[n].data[a]);
+ WRITE_REG(patchdata[10] + n * 8, id[n].data[10] | 0x30);
+ }
+
+ for(unsigned n = 0; n < n_notes; ++n)
+ {
+ double hertz = 172.00093 * std::exp(0.057762265 * (notenum + id[n].finetune));
+ if(hertz > 131071)
+ {
+ fprintf(stderr, "Why does note %d + finetune %d produce hertz %g?\n",
+ notenum, id[n].finetune, hertz);
+ hertz = 131071;
+ }
+ x[n] = 0x2000;
+ while(hertz >= 1023.5)
+ {
+ hertz /= 2.0; // Calculate octave
+ x[n] += 0x400;
+ }
+ x[n] += (unsigned int)(hertz + 0.5);
+
+ // Keyon the note
+ WRITE_REG(0xA0 + n * 3, x[n] & 0xFF);
+ WRITE_REG(0xB0 + n * 3, x[n] >> 8);
+ }
+
+ const unsigned max_on = 40;
+ const unsigned max_off = 60;
+
+ // For up to 40 seconds, measure mean amplitude.
+ std::vector<double> amplitudecurve_on;
+ double highest_sofar = 0;
+ for(unsigned period = 0; period < max_on * interval; ++period)
+ {
+ stereoSampleBuf.clear();
+ stereoSampleBuf.resize(samples_per_interval * 2);
+#ifndef ADLMIDI_USE_DOSBOX_OPL
+ OPL3_GenerateStream(&opl, stereoSampleBuf.data(), samples_per_interval);
+#else
+ {
+ stereoSampleBuf_32.resize(samples_per_interval * 2);
+ Bitu samples = samples_per_interval;
+ opl.GenerateArr(stereoSampleBuf_32.data(), &samples);
+ size_t ssat = 0;
+ for(const int32_t &i : stereoSampleBuf_32)
+ stereoSampleBuf[ssat++] = (int16_t)i;
+ }
+#endif
+
+ double mean = 0.0;
+ for(unsigned long c = 0; c < samples_per_interval; ++c)
+ mean += stereoSampleBuf[c * 2];
+ mean /= samples_per_interval;
+ double std_deviation = 0;
+ for(unsigned long c = 0; c < samples_per_interval; ++c)
+ {
+ double diff = (stereoSampleBuf[c * 2] - mean);
+ std_deviation += diff * diff;
+ }
+ std_deviation = std::sqrt(std_deviation / samples_per_interval);
+ amplitudecurve_on.push_back(std_deviation);
+ if(std_deviation > highest_sofar)
+ highest_sofar = std_deviation;
+
+ if(period > 6 * interval && std_deviation < highest_sofar * 0.2)
+ break;
+ }
+
+ // Keyoff the note
+ for(unsigned n = 0; n < n_notes; ++n)
+ WRITE_REG(0xB0 + n, (x[n] >> 8) & 0xDF);
+
+ // Now, for up to 60 seconds, measure mean amplitude.
+ std::vector<double> amplitudecurve_off;
+ for(unsigned period = 0; period < max_off * interval; ++period)
+ {
+ stereoSampleBuf.clear();
+ stereoSampleBuf.resize(samples_per_interval * 2);
+#ifndef ADLMIDI_USE_DOSBOX_OPL
+ OPL3_GenerateStream(&opl, stereoSampleBuf.data(), samples_per_interval);
+#else
+ {
+ stereoSampleBuf_32.resize(samples_per_interval * 2);
+ Bitu samples = samples_per_interval;
+ opl.GenerateArr(stereoSampleBuf_32.data(), &samples);
+ size_t ssat = 0;
+ for(const int32_t &i : stereoSampleBuf_32)
+ stereoSampleBuf[ssat++] = (int16_t)i;
+ }
+#endif
+
+ double mean = 0.0;
+ for(unsigned long c = 0; c < samples_per_interval; ++c)
+ mean += stereoSampleBuf[c * 2];
+ mean /= samples_per_interval;
+ double std_deviation = 0;
+ for(unsigned long c = 0; c < samples_per_interval; ++c)
+ {
+ double diff = (stereoSampleBuf[c * 2] - mean);
+ std_deviation += diff * diff;
+ }
+ std_deviation = std::sqrt(std_deviation / samples_per_interval);
+ amplitudecurve_off.push_back(std_deviation);
+
+ if(std_deviation < highest_sofar * 0.2) break;
+ }
+
+ /* Analyze the results */
+ double begin_amplitude = amplitudecurve_on[0];
+ double peak_amplitude_value = begin_amplitude;
+ size_t peak_amplitude_time = 0;
+ size_t quarter_amplitude_time = amplitudecurve_on.size();
+ size_t keyoff_out_time = 0;
+
+ for(size_t a = 1; a < amplitudecurve_on.size(); ++a)
+ {
+ if(amplitudecurve_on[a] > peak_amplitude_value)
+ {
+ peak_amplitude_value = amplitudecurve_on[a];
+ peak_amplitude_time = a;
+ }
+ }
+ for(size_t a = peak_amplitude_time; a < amplitudecurve_on.size(); ++a)
+ {
+ if(amplitudecurve_on[a] <= peak_amplitude_value * 0.2)
+ {
+ quarter_amplitude_time = a;
+ break;
+ }
+ }
+ for(size_t a = 0; a < amplitudecurve_off.size(); ++a)
+ {
+ if(amplitudecurve_off[a] <= peak_amplitude_value * 0.2)
+ {
+ keyoff_out_time = a;
+ break;
+ }
+ }
+
+ if(keyoff_out_time == 0 && amplitudecurve_on.back() < peak_amplitude_value * 0.2)
+ keyoff_out_time = quarter_amplitude_time;
+
+ DurationInfo result;
+ result.peak_amplitude_time = peak_amplitude_time;
+ result.peak_amplitude_value = peak_amplitude_value;
+ result.begin_amplitude = begin_amplitude;
+ result.quarter_amplitude_time = quarter_amplitude_time;
+ result.keyoff_out_time = keyoff_out_time;
+
+ result.ms_sound_kon = (long)(quarter_amplitude_time * 1000.0 / interval);
+ result.ms_sound_koff = (long)(keyoff_out_time * 1000.0 / interval);
+ result.nosound = (peak_amplitude_value < 0.5);
+ return result;
+}
+
+static const char* spinner = "-\\|/";
+
+void MeasureThreaded::LoadCache(const char *fileName)
+{
+ FILE *in = std::fopen(fileName, "rb");
+ if(!in)
+ {
+ std::printf("Failed to load cache: file is not exists.\n"
+ "Complete data will be generated from scratch.\n");
+ std::fflush(stdout);
+ return;
+ }
+
+ char magic[32];
+ if(std::fread(magic, 1, 32, in) != 32)
+ {
+ std::fclose(in);
+ std::printf("Failed to load cache: 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)
+ {
+ std::fclose(in);
+ std::printf("Failed to load cache: magic missmatch.\n"
+ "Complete data will be generated from scratch.\n");
+ std::fflush(stdout);
+ return;
+ }
+
+ while(!std::feof(in))
+ {
+ 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.pseudo4op, 1, 1, in) != 1)
+ break;
+ if(std::fread(&inst.voice2_fine_tune, sizeof(double), 1, in) != 1)
+ break;
+
+ //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;
+ }
+
+ 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;
+ }
+ }
+
+ //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;
+ }
+
+ //Then find instrument entry that uses found instruments
+ if(isMatches)
+ {
+ inst.insno1 = insNo[0];
+ inst.insno2 = insNo[1];
+ InstrumentsData::iterator d = instab.find(inst);
+ if(d == instab.end())
+ isMatches = false;
+ }
+ }
+
+ //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;
+
+ if(isMatches)//Store only if cached entry matches actual raw instrument data
+ m_durationInfo.insert({inst, info});
+ }
+
+ std::printf("Cache loaded!\n");
+ std::fflush(stdout);
+
+ std::fclose(in);
+}
+
+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++)
+ {
+ 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.pseudo4op, 1, 1, out);
+ fwrite(&in.voice2_fine_tune, sizeof(double), 1, out);
+
+ for(InstrumentDataTab::const_iterator j = insdatatab.begin(); j != insdatatab.end(); ++j)
+ {
+ 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;
+ }
+ }
+
+ fwrite(found, 1, 2 * sizeof(bool), out);
+ for(size_t i = 0; i < 2; i++)
+ {
+ fwrite(id[i].data, 1, 11, out);
+ fwrite(&id[i].finetune, 1, 1, out);
+ fwrite(&id[i].diff, 1, sizeof(bool), 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::fclose(out);
+}
+
+void MeasureThreaded::printProgress()
+{
+ std::printf("Calculating measures... [%c %3u%% (%4u/%4u) Threads %3u, Matches %u] \r",
+ spinner[m_done.load() % 4],
+ (unsigned int)(((double)m_done.load() / (double)(m_total)) * 100),
+ (unsigned int)m_done.load(),
+ (unsigned int)m_total,
+ (unsigned int)m_threads.size(),
+ (unsigned int)m_cache_matches
+ );
+ std::fflush(stdout);
+}
+
+void MeasureThreaded::printFinal()
+{
+ std::printf("Calculating measures completed! [Total entries %4u with %u cache matches]\n",
+ (unsigned int)m_total,
+ (unsigned int)m_cache_matches);
+ std::fflush(stdout);
+}
+
+void MeasureThreaded::run(InstrumentsData::const_iterator i)
+{
+ m_semaphore.wait();
+ if(m_threads.size() > 0)
+ {
+ for(std::vector<destData *>::iterator it = m_threads.begin(); it != m_threads.end();)
+ {
+ if(!(*it)->m_works)
+ {
+ delete(*it);
+ it = m_threads.erase(it);
+ }
+ else
+ it++;
+ }
+ }
+
+ destData *dd = new destData;
+ dd->i = i;
+ dd->myself = this;
+ dd->start();
+ m_threads.push_back(dd);
+ printProgress();
+}
+
+void MeasureThreaded::waitAll()
+{
+ for(auto &th : m_threads)
+ {
+ printProgress();
+ delete th;
+ }
+ m_threads.clear();
+ printFinal();
+}
+
+void MeasureThreaded::destData::start()
+{
+ m_work = std::thread(&destData::callback, this);
+}
+
+void MeasureThreaded::destData::callback(void *myself)
+{
+ destData *s = reinterpret_cast<destData *>(myself);
+ DurationInfo info;
+ DurationInfoCache::iterator cachedEntry = s->myself->m_durationInfo.find(s->i->first);
+
+ if(cachedEntry != s->myself->m_durationInfo.end())
+ {
+ s->myself->m_cache_matches++;
+ goto endWork;
+ }
+
+ info = MeasureDurations(s->i->first);
+ s->myself->m_durationInfo_mx.lock();
+ s->myself->m_durationInfo.insert({s->i->first, info});
+ s->myself->m_durationInfo_mx.unlock();
+
+endWork:
+ s->myself->m_semaphore.notify();
+ s->myself->m_done++;
+ s->m_works = false;
+}
diff --git a/utils/gen_adldata/measurer.h b/utils/gen_adldata/measurer.h
new file mode 100644
index 0000000..b9ae3c6
--- /dev/null
+++ b/utils/gen_adldata/measurer.h
@@ -0,0 +1,105 @@
+#ifndef MEASURER_H
+#define MEASURER_H
+
+#include <atomic>
+#include <map>
+#include <mutex>
+#include <condition_variable>
+#include <thread>
+
+#include "progs_cache.h"
+
+struct DurationInfo
+{
+ uint64_t peak_amplitude_time;
+ double peak_amplitude_value;
+ double quarter_amplitude_time;
+ double begin_amplitude;
+ double interval;
+ double keyoff_out_time;
+ int64_t ms_sound_kon;
+ int64_t ms_sound_koff;
+ bool nosound;
+ uint8_t padding[7];
+};
+
+class Semaphore
+{
+public:
+ Semaphore(int count_ = 0)
+ : m_count(count_) {}
+
+ inline void notify()
+ {
+ std::unique_lock<std::mutex> lock(mtx);
+ m_count++;
+ cv.notify_one();
+ }
+
+ inline void wait()
+ {
+ std::unique_lock<std::mutex> lock(mtx);
+ while(m_count == 0)
+ {
+ cv.wait(lock);
+ }
+ m_count--;
+ }
+
+private:
+ std::mutex mtx;
+ std::condition_variable cv;
+ std::atomic_int m_count;
+};
+
+struct MeasureThreaded
+{
+ typedef std::map<ins, DurationInfo> DurationInfoCache;
+
+ MeasureThreaded() :
+ m_semaphore(int(std::thread::hardware_concurrency()) * 2),
+ m_done(0),
+ m_cache_matches(0)
+ {}
+
+ Semaphore m_semaphore;
+ std::mutex m_durationInfo_mx;
+ DurationInfoCache m_durationInfo;
+ std::atomic_bool m_delete_tail;
+ size_t m_total = 0;
+ std::atomic<size_t> m_done;
+ std::atomic<size_t> m_cache_matches;
+
+ void LoadCache(const char *fileName);
+ void SaveCache(const char *fileName);
+
+ struct destData
+ {
+ destData()
+ {
+ m_works = true;
+ }
+ ~destData()
+ {
+ m_work.join();
+ }
+ MeasureThreaded *myself;
+ std::map<ins, std::pair<size_t, std::set<std::string> > >::const_iterator i;
+ std::thread m_work;
+ std::atomic_bool m_works;
+
+ void start();
+ static void callback(void *myself);
+ };
+
+ std::vector<destData *> m_threads;
+
+ void printProgress();
+ void printFinal();
+ void run(InstrumentsData::const_iterator i);
+ void waitAll();
+};
+
+extern DurationInfo MeasureDurations(const ins &in);
+
+#endif // MEASURER_H
diff --git a/utils/gen_adldata/midi_inst_list.h b/utils/gen_adldata/midi_inst_list.h
new file mode 100644
index 0000000..61c9119
--- /dev/null
+++ b/utils/gen_adldata/midi_inst_list.h
@@ -0,0 +1,185 @@
+#pragma once
+#ifndef MIDI_INS_LIST
+#define MIDI_INS_LIST
+
+static const char *const MidiInsName[] = {
+"AcouGrandPiano",
+"BrightAcouGrand",
+"ElecGrandPiano",
+"Honky-tonkPiano",
+"Rhodes Piano",
+"Chorused Piano",
+"Harpsichord",
+"Clavinet",
+"Celesta",
+"Glockenspiel",
+"Music box",
+"Vibraphone",
+"Marimba",
+"Xylophone",
+"Tubular Bells",
+"Dulcimer",
+"Hammond Organ",
+"Percussive Organ",
+"Rock Organ",
+"Church Organ",
+"Reed Organ",
+"Accordion",
+"Harmonica",
+"Tango Accordion",
+"Acoustic Guitar1",
+"Acoustic Guitar2",
+"Electric Guitar1",
+"Electric Guitar2",
+"Electric Guitar3",
+"Overdrive Guitar",
+"Distorton Guitar",
+"Guitar Harmonics",
+"Acoustic Bass",
+"Electric Bass 1",
+"Electric Bass 2",
+"Fretless Bass",
+"Slap Bass 1",
+"Slap Bass 2",
+"Synth Bass 1",
+"Synth Bass 2",
+"Violin",
+"Viola",
+"Cello",
+"Contrabass",
+"Tremulo Strings",
+"Pizzicato String",
+"Orchestral Harp",
+"Timpany",
+"String Ensemble1",
+"String Ensemble2",
+"Synth Strings 1",
+"SynthStrings 2",
+"Choir Aahs",
+"Voice Oohs",
+"Synth Voice",
+"Orchestra Hit",
+"Trumpet",
+"Trombone",
+"Tuba",
+"Muted Trumpet",
+"French Horn",
+"Brass Section",
+"Synth Brass 1",
+"Synth Brass 2",
+"Soprano Sax",
+"Alto Sax",
+"Tenor Sax",
+"Baritone Sax",
+"Oboe",
+"English Horn",
+"Bassoon",
+"Clarinet",
+"Piccolo",
+"Flute",
+"Recorder",
+"Pan Flute",
+"Bottle Blow",
+"Shakuhachi",
+"Whistle",
+"Ocarina",
+"Lead 1 squareea",
+"Lead 2 sawtooth",
+"Lead 3 calliope",
+"Lead 4 chiff",
+"Lead 5 charang",
+"Lead 6 voice",
+"Lead 7 fifths",
+"Lead 8 brass",
+"Pad 1 new age",
+"Pad 2 warm",
+"Pad 3 polysynth",
+"Pad 4 choir",
+"Pad 5 bowedpad",
+"Pad 6 metallic",
+"Pad 7 halo",
+"Pad 8 sweep",
+"FX 1 rain",
+"FX 2 soundtrack",
+"FX 3 crystal",
+"FX 4 atmosphere",
+"FX 5 brightness",
+"FX 6 goblins",
+"FX 7 echoes",
+"FX 8 sci-fi",
+"Sitar",
+"Banjo",
+"Shamisen",
+"Koto",
+"Kalimba",
+"Bagpipe",
+"Fiddle",
+"Shanai",
+"Tinkle Bell",
+"Agogo Bells",
+"Steel Drums",
+"Woodblock",
+"Taiko Drum",
+"Melodic Tom",
+"Synth Drum",
+"Reverse Cymbal",
+"Guitar FretNoise",
+"Breath Noise",
+"Seashore",
+"Bird Tweet",
+"Telephone",
+"Helicopter",
+"Applause/Noise",
+"Gunshot",
+// 27..34: High Q; Slap; Scratch Push; Scratch Pull; Sticks;
+// Square Click; Metronome Click; Metronome Bell
+"Ac Bass Drum",
+"Bass Drum 1",
+"Side Stick",
+"Acoustic Snare",
+"Hand Clap",
+"Electric Snare",
+"Low Floor Tom",
+"Closed High Hat",
+"High Floor Tom",
+"Pedal High Hat",
+"Low Tom",
+"Open High Hat",
+"Low-Mid Tom",
+"High-Mid Tom",
+"Crash Cymbal 1",
+"High Tom",
+"Ride Cymbal 1",
+"Chinese Cymbal",
+"Ride Bell",
+"Tambourine",
+"Splash Cymbal",
+"Cow Bell",
+"Crash Cymbal 2",
+"Vibraslap",
+"Ride Cymbal 2",
+"High Bongo",
+"Low Bongo",
+"Mute High Conga",
+"Open High Conga",
+"Low Conga",
+"High Timbale",
+"Low Timbale",
+"High Agogo",
+"Low Agogo",
+"Cabasa",
+"Maracas",
+"Short Whistle",
+"Long Whistle",
+"Short Guiro",
+"Long Guiro",
+"Claves",
+"High Wood Block",
+"Low Wood Block",
+"Mute Cuica",
+"Open Cuica",
+"Mute Triangle",
+"Open Triangle",
+"Shaker","Jingle Bell","Bell Tree","Castanets","Mute Surdu","Open Surdu",""};
+
+#endif //MIDI_INS_LIST
diff --git a/utils/gen_adldata/progs_cache.cpp b/utils/gen_adldata/progs_cache.cpp
new file mode 100644
index 0000000..4aec9be
--- /dev/null
+++ b/utils/gen_adldata/progs_cache.cpp
@@ -0,0 +1,106 @@
+#include "progs_cache.h"
+
+InstrumentDataTab insdatatab;
+
+InstrumentsData instab;
+InstProgsData progs;
+
+std::vector<std::string> banknames;
+
+//unsigned maxvalues[30] = { 0 };
+
+void SetBank(unsigned bank, unsigned patch, size_t insno)
+{
+ progs[bank][patch] = insno + 1;
+}
+
+size_t InsertIns(
+ const insdata &id,
+ const insdata &id2,
+ ins &in,
+ const std::string &name,
+ const std::string &name2)
+{
+ if(true)
+ {
+ InstrumentDataTab::iterator i = insdatatab.lower_bound(id);
+
+ size_t insno = size_t(~0);
+ if(i == insdatatab.end() || i->first != id)
+ {
+ 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;
+ }
+ else
+ {
+ if(!name.empty()) i->second.second.insert(name);
+ if(!name2.empty()) i->second.second.insert(name2);
+ insno = i->second.first;
+ }
+
+ in.insno1 = insno;
+ }
+ if(id != id2)
+ {
+ InstrumentDataTab::iterator i = insdatatab.lower_bound(id2);
+
+ size_t insno2 = size_t(~0);
+ if(i == insdatatab.end() || i->first != id2)
+ {
+ 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;
+ }
+ else
+ {
+ if(!name.empty()) i->second.second.insert(name);
+ if(!name2.empty()) i->second.second.insert(name2);
+ insno2 = i->second.first;
+ }
+ in.insno2 = insno2;
+ }
+ else
+ in.insno2 = in.insno1;
+
+
+ {
+ InstrumentsData::iterator i = instab.lower_bound(in);
+
+ size_t resno = size_t(~0);
+ if(i == instab.end() || i->first != in)
+ {
+ 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;
+ }
+ else
+ {
+ if(!name.empty()) i->second.second.insert(name);
+ if(!name2.empty()) i->second.second.insert(name2);
+ resno = i->second.first;
+ }
+ return resno;
+ }
+}
+
+// Create silent 'nosound' instrument
+size_t InsertNoSoundIns()
+{
+ // { 0x0F70700,0x0F70710, 0xFF,0xFF, 0x0,+0 },
+ insdata tmp1 = { {0x00, 0x10, 0x07, 0x07, 0xF7, 0xF7, 0x00, 0x00, 0xFF, 0xFF, 0x00}, 0, 0 };
+ struct ins tmp2 = { 0, 0, 0, false, 0.0 };
+ return InsertIns(tmp1, tmp1, tmp2, "nosound");
+}
diff --git a/utils/gen_adldata/progs_cache.h b/utils/gen_adldata/progs_cache.h
new file mode 100644
index 0000000..007c3b4
--- /dev/null
+++ b/utils/gen_adldata/progs_cache.h
@@ -0,0 +1,85 @@
+#ifndef PROGS_H
+#define PROGS_H
+
+#include <map>
+#include <set>
+#include <utility>
+#include <memory>
+#include <cstring>
+#include <cstdint>
+#include <vector>
+
+struct insdata
+{
+ uint8_t data[11];
+ int8_t finetune;
+ bool diff;
+ bool operator==(const insdata &b) const
+ {
+ return std::memcmp(data, b.data, 11) == 0 && finetune == b.finetune && diff == b.diff;
+ }
+ bool operator< (const insdata &b) const
+ {
+ int c = std::memcmp(data, b.data, 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
+ {
+ return !operator==(b);
+ }
+};
+
+struct ins
+{
+ size_t insno1, insno2;
+ unsigned char notenum;
+ bool pseudo4op;
+ double voice2_fine_tune;
+
+ bool operator==(const ins &b) const
+ {
+ return notenum == b.notenum
+ && insno1 == b.insno1
+ && insno2 == b.insno2
+ && pseudo4op == b.pseudo4op
+ && voice2_fine_tune == b.voice2_fine_tune;
+ }
+ 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(voice2_fine_tune != b.voice2_fine_tune) return voice2_fine_tune < b.voice2_fine_tune;
+ return 0;
+ }
+ bool operator!=(const ins &b) const
+ {
+ return !operator==(b);
+ }
+};
+
+typedef std::map<insdata, std::pair<size_t, std::set<std::string> > > InstrumentDataTab;
+extern InstrumentDataTab insdatatab;
+
+typedef std::map<ins, std::pair<size_t, std::set<std::string> > > InstrumentsData;
+extern InstrumentsData instab;
+
+typedef std::map<size_t, std::map<size_t, size_t> > InstProgsData;
+extern InstProgsData progs;
+
+extern std::vector<std::string> banknames;
+
+//static std::map<unsigned, std::map<unsigned, unsigned> > Correlate;
+//extern unsigned maxvalues[30];
+
+void SetBank(unsigned bank, unsigned patch, size_t insno);
+
+size_t InsertIns(const insdata &id, const insdata &id2, ins &in,
+ const std::string &name, const std::string &name2 = "");
+size_t InsertNoSoundIns();
+
+#endif // PROGS_H
diff --git a/utils/gen_adldata/scrapped.txt b/utils/gen_adldata/scrapped.txt
new file mode 100644
index 0000000..10c15cd
--- /dev/null
+++ b/utils/gen_adldata/scrapped.txt
@@ -0,0 +1,263 @@
+
+#ifdef HARD_BANKS
+static const char *const banknames[] =
+{
+ // 0
+ "AIL (Star Control 3, Albion, Empire 2, Sensible Soccer, Settlers 2, many others)",
+ "Bisqwit (selection of 4op and 2op)",
+ "HMI (Descent, Asterix)", //melodic,drum
+ "HMI (Descent:: Int)", //intmelo,intdrum
+ "HMI (Descent:: Ham)", //hammelo,hamdrum
+ "HMI (Descent:: Rick)", //rickmelo,rickdrum
+ "HMI (Descent 2)", //d2melo,d2drum
+ "HMI (Normality)", //normmelo,normdrum
+ "HMI (Shattered Steel)", //ssmelo,ssdrum
+ "HMI (Theme Park)", // file131, file132
+ // 10
+ "HMI (3d Table Sports, Battle Arena Toshinden)", //file133, file134
+ "HMI (Aces of the Deep)", //file142, file143
+ "HMI (Earthsiege)", //file144, file145
+ "HMI (Anvil of Dawn)", //file167,file168
+ "DMX (Doom :: partially pseudo 4op)",
+ "DMX (Hexen, Heretic :: partially pseudo 4op)",
+ "DMX (MUS Play :: partially pseudo 4op)",
+ "AIL (Discworld, Grandest Fleet, Pocahontas, Slob Zone 3d, Ultima 4, Zorro)", // file17
+ "AIL (Warcraft 2)",
+ "AIL (Syndicate)", // file19
+ // 20
+ "AIL (Guilty, Orion Conspiracy, Terra Nova Strike Force Centauri :: 4op)", // file20
+ "AIL (Magic Carpet 2)", // file21
+ "AIL (Nemesis)",
+ "AIL (Jagged Alliance)", //file23
+ "AIL (When Two Worlds War :: 4op, MISSING INSTRUMENTS)", //file24
+ "AIL (Bards Tale Construction :: MISSING INSTRUMENTS)", //file25
+ "AIL (Return to Zork)", //file26
+ "AIL (Theme Hospital)", //file27
+ "AIL (National Hockey League PA)",
+ "AIL (Inherit The Earth)", //file29
+ // 30
+ "AIL (Inherit The Earth, file two)", //file30
+ "AIL (Little Big Adventure :: 4op)", //file31
+ "AIL (Wreckin Crew)", //file32
+ "AIL (Death Gate)", // file13
+ "AIL (FIFA International Soccer)", //file34
+ "AIL (Starship Invasion)", //file35
+ "AIL (Super Street Fighter 2 :: 4op)", //file36
+ "AIL (Lords of the Realm :: MISSING INSTRUMENTS)", //file37
+ "AIL (SimFarm, SimHealth :: 4op)",
+ "AIL (SimFarm, Settlers, Serf City)",
+ // 40
+ "AIL (Caesar 2 :: partially 4op, MISSING INSTRUMENTS)", // file12
+ "AIL (Syndicate Wars)", //file41
+ "AIL (Bubble Bobble Feat. Rainbow Islands, Z)", //file42
+ "AIL (Warcraft)", //file47
+ "AIL (Terra Nova Strike Force Centuri :: partially 4op)", //file48
+ "AIL (System Shock :: partially 4op)", //file49
+ "AIL (Advanced Civilization)", //file50
+ "AIL (Battle Chess 4000 :: partially 4op, melodic only)", //file53
+ "AIL (Ultimate Soccer Manager :: partially 4op)", //file54
+ "AIL (Air Bucks, Blue And The Gray, America Invades, Terminator 2029)", // sample.ad
+ // 50
+ "AIL (Ultima Underworld 2)", // sample.opl
+ "AIL (Kasparov's Gambit)", // file15
+ "AIL (High Seas Trader :: MISSING INSTRUMENTS)", // file16
+ "AIL (Master of Magic, Master of Orion 2 :: 4op, std percussion)", //file159
+ "AIL (Master of Magic, Master of Orion 2 :: 4op, orchestral percussion)", //file159
+ "SB (Action Soccer)",
+ "SB (3d Cyberpuck :: melodic only)",
+ "SB (Simon the Sorcerer :: melodic only)",
+ "OP3 (The Fat Man 2op set)",
+ "OP3 (The Fat Man 4op set)",
+ // 60
+ "OP3 (JungleVision 2op set :: melodic only)",
+ "OP3 (Wallace 2op set, Nitemare 3D :: melodic only)",
+ "TMB (Duke Nukem 3D)",
+ "TMB (Shadow Warrior)",
+ "DMX (Raptor)",
+ "OP3 (Modded GMOPL by Wohlstand)",
+ "SB (Jammey O'Connel's bank)",
+ "TMB (Default bank of Build Engine)",
+ "OP3 (4op bank by James Alan Nguyen)",
+ "TMB (Blood)",
+ // 70
+ "TMB (Lee)",
+ "TMB (Nam)",
+ "DMX (Bank by Sneakernets)"
+};
+
+const char *prev = "";
+const char *prev_prefix = "";
+size_t prev_index = 0;
+
+enum IniType
+{
+ INI_Both,
+ INI_Melodic,
+ INI_Drums
+};
+
+void writeIni(const char * format, const char * filepath, const char * prefix, size_t index, int type, const char*filter_mel = 0, const char *filter_perc = 0)
+{
+ if(type == INI_Both)
+ {
+ FILE *ini = fopen("banks.ini", "a");
+ fprintf(ini, "[bank-%lu]\n", index);
+ fprintf(ini, "name = \"%s\"\n", banknames[index]);
+ fprintf(ini, "format = %s\n", format);
+ fprintf(ini, "file = \"%s\"\n", filepath);
+ fprintf(ini, "prefix = \"%s\"\n", prefix);
+ if(filter_mel) fprintf(ini, "filter-m = \"%s\"\n", filter_mel);
+ if(filter_perc) fprintf(ini, "filter-p = \"%s\"\n", filter_perc);
+ fprintf(ini, "\n");
+ fclose(ini);
+ }
+
+ if(type == INI_Drums)
+ {
+ FILE *ini = fopen("banks.ini", "a");
+ fprintf(ini, "[bank-%lu]\n", index);
+ fprintf(ini, "name = \"%s\"\n", banknames[index]);
+ fprintf(ini, "format = %s\n", format);
+ fprintf(ini, "file = \"%s\"\n", prev);
+ fprintf(ini, "file-p = \"%s\"\n", filepath);
+ fprintf(ini, "prefix = \"%s\"\n", prev_prefix);
+ fprintf(ini, "prefix-p = \"%s\"\n", prefix);
+ fprintf(ini, "\n");
+ fclose(ini);
+ }
+
+ prev = filepath;
+ prev_prefix = prefix;
+ prev_index = index;
+}
+#endif
+
+
+
+#ifdef HARD_BANKS
+LoadMiles("fm_banks/opl_files/sc3.opl", 0, "G"); // Our "standard" bank! Same as file22.opl
+
+LoadBisqwit("fm_banks/op3_files/bisqwit.adlraw", 1, "Bisq");
+
+LoadBNK("fm_banks/bnk_files/melodic.bnk", 2, "HMIGM", false, false); // same as file156.bnk
+LoadBNK("fm_banks/bnk_files/drum.bnk", 2, "HMIGP", false, true);
+
+LoadBNK("fm_banks/bnk_files/intmelo.bnk", 3, "intM", false, false);
+LoadBNK("fm_banks/bnk_files/intdrum.bnk", 3, "intP", false, true);
+
+LoadBNK("fm_banks/bnk_files/hammelo.bnk", 4, "hamM", false, false);
+LoadBNK("fm_banks/bnk_files/hamdrum.bnk", 4, "hamP", false, true);
+
+LoadBNK("fm_banks/bnk_files/rickmelo.bnk", 5, "rickM", false, false);
+LoadBNK("fm_banks/bnk_files/rickdrum.bnk", 5, "rickP", false, true);
+
+LoadBNK("fm_banks/bnk_files/d2melo.bnk", 6, "b6M", false, false);
+LoadBNK("fm_banks/bnk_files/d2drum.bnk", 6, "b6P", false, true);
+
+LoadBNK("fm_banks/bnk_files/normmelo.bnk", 7, "b7M", false, false);
+LoadBNK("fm_banks/bnk_files/normdrum.bnk", 7, "b7P", false, true); // same as file122.bnk
+
+LoadBNK("fm_banks/bnk_files/ssmelo.bnk", 8, "b8M", false, false);
+LoadBNK("fm_banks/bnk_files/ssdrum.bnk", 8, "b8P", false, true);
+
+LoadTMB("fm_banks/bnk_files/themepark.tmb", 9, "b9MP");
+//LoadBNK("fm_banks/bnk_files/file131.bnk", 9, "b9M", false, false);
+//LoadBNK("fm_banks/bnk_files/file132.bnk", 9, "b9P", false, true);
+
+LoadBNK("fm_banks/bnk_files/file133.bnk", 10, "b10P", false, true);
+LoadBNK("fm_banks/bnk_files/file134.bnk", 10, "b10M", false, false);
+
+LoadBNK("fm_banks/bnk_files/file142.bnk", 11, "b11P", false, true);
+LoadBNK("fm_banks/bnk_files/file143.bnk", 11, "b11M", false, false);
+
+LoadBNK("fm_banks/bnk_files/file145.bnk", 12, "b12M", false, false);//file145 is MELODIC
+LoadBNK("fm_banks/bnk_files/file144.bnk", 12, "b12P", false, true);//file144 is DRUMS
+
+LoadBNK("fm_banks/bnk_files/file167.bnk", 13, "b13P", false, true);
+LoadBNK("fm_banks/bnk_files/file168.bnk", 13, "b13M", false, false);
+
+LoadDoom("fm_banks/doom2/genmidi.op2", 14, "dM");
+LoadDoom("fm_banks/doom2/genmidi.htc", 15, "hxM"); // same as genmidi.hxn
+LoadDoom("fm_banks/doom2/default.op2", 16, "mus");
+
+LoadMiles("fm_banks/opl_files/file17.opl", 17, "f17G");
+LoadMiles("fm_banks/opl_files/warcraft.ad", 18, "sG"); // same as file44, warcraft.opl
+LoadMiles("fm_banks/opl_files/file19.opl", 19, "f19G");
+LoadMiles("fm_banks/opl_files/file20.opl", 20, "f20G");
+LoadMiles("fm_banks/opl_files/file21.opl", 21, "f21G");
+LoadMiles("fm_banks/opl_files/nemesis.opl", 22, "nem");
+LoadMiles("fm_banks/opl_files/file23.opl", 23, "f23G");
+LoadMiles("fm_banks/opl_files/file24.opl", 24, "f24G");
+LoadMiles("fm_banks/opl_files/file25.opl", 25, "f25G");
+LoadMiles("fm_banks/opl_files/file26.opl", 26, "f26G");
+LoadMiles("fm_banks/opl_files/file27.opl", 27, "f27G");
+LoadMiles("fm_banks/opl_files/nhlpa.opl", 28, "nhl");
+LoadMiles("fm_banks/opl_files/file29.opl", 29, "f29G");
+LoadMiles("fm_banks/opl_files/file30.opl", 30, "f30G");
+LoadMiles("fm_banks/opl_files/file31.opl", 31, "f31G");
+LoadMiles("fm_banks/opl_files/file32.opl", 32, "f32G");
+LoadMiles("fm_banks/opl_files/file13.opl", 33, "f13G");
+LoadMiles("fm_banks/opl_files/file34.opl", 34, "f34G");
+LoadMiles("fm_banks/opl_files/file35.opl", 35, "f35G");
+LoadMiles("fm_banks/opl_files/file36.opl", 36, "f36G");
+LoadMiles("fm_banks/opl_files/file37.opl", 37, "f37G");
+LoadMiles("fm_banks/opl_files/simfarm.opl", 38, "qG");
+LoadMiles("fm_banks/opl_files/simfarm.ad", 39, "mG"); // same as file18.opl
+LoadMiles("fm_banks/opl_files/file12.opl", 40, "f12G");
+LoadMiles("fm_banks/opl_files/file41.opl", 41, "f41G");
+LoadMiles("fm_banks/opl_files/file42.opl", 42, "f42G");
+LoadMiles("fm_banks/opl_files/file47.opl", 43, "f47G");
+LoadMiles("fm_banks/opl_files/file48.opl", 44, "f48G");
+LoadMiles("fm_banks/opl_files/file49.opl", 45, "f49G");
+LoadMiles("fm_banks/opl_files/file50.opl", 46, "f50G");
+LoadMiles("fm_banks/opl_files/file53.opl", 47, "f53G");
+LoadBNK("fm_banks/bnk_files/file144.bnk", 47, "f53GD", false, true);//Attempt to append missing drums
+LoadMiles("fm_banks/opl_files/file54.opl", 48, "f54G");
+
+LoadMiles("fm_banks/opl_files/sample.ad", 49, "MG"); // same as file51.opl
+LoadMiles("fm_banks/opl_files/sample.opl", 50, "oG"); // same as file40.opl
+LoadMiles("fm_banks/opl_files/file15.opl", 51, "f15G");
+LoadMiles("fm_banks/opl_files/file16.opl", 52, "f16G");
+
+LoadBNK2("fm_banks/bnk_files/file159.bnk", 53, "b50", "gm", "gps"); // fat-opl3
+LoadBNK2("fm_banks/bnk_files/file159.bnk", 54, "b51", "gm", "gpo");
+
+LoadIBK("fm_banks/ibk_files/soccer-genmidi.ibk", 55, "b55M", false);
+LoadIBK("fm_banks/ibk_files/soccer-percs.ibk", 55, "b55P", true);
+LoadIBK("fm_banks/ibk_files/game.ibk", 56, "b56", false);
+LoadIBK("fm_banks/ibk_files/mt_fm.ibk", 57, "b57", false);
+
+LoadJunglevision("fm_banks/op3_files/fat2.op3", 58, "fat2");
+LoadJunglevision("fm_banks/op3_files/fat4.op3", 59, "fat4");
+LoadJunglevision("fm_banks/op3_files/jv_2op.op3", 60, "b60");
+LoadJunglevision("fm_banks/op3_files/wallace.op3", 61, "b61");
+
+LoadTMB("fm_banks/tmb_files/d3dtimbr.tmb", 62, "duke");
+LoadTMB("fm_banks/tmb_files/swtimbr.tmb", 63, "sw");
+
+LoadDoom("fm_banks/raptor/genmidi.op2", 64, "rapt");
+
+//LoadJunglevision("fm_banks/op3_files/fat2_modded.op3", 65, "b65M");
+LoadTMB("fm_banks/op3_files/gmopl_wohl_mod.tmb", 65, "b65");
+
+//LoadIBK("fm_banks/ibk_files/JOconnel.IBK", 66, "b66M", false);
+//LoadIBK("fm_banks/ibk_files/my-gmopldrums.IBK", 66, "b66P", true);
+LoadTMB("fm_banks/op3_files/gmoconel.tmb", 66, "b66");
+
+LoadTMB("fm_banks/tmb_files/default.tmb", 67, "3drm67");
+//LoadDoom("fm_banks/doom2/wolfinstein.op2", 67, "wolf"); //Small experiment!
+
+//LoadJunglevision("fm_banks/op3_files/2x2.op3", 68, "2x2byJAN");
+LoadMiles("fm_banks/op3_files/2x2.opl", 68, "2x2byJAN");
+
+LoadTMB("fm_banks/tmb_files/bloodtmb.tmb", 69, "apgblood");
+LoadTMB("fm_banks/tmb_files/lee.tmb", 70, "apglee");
+LoadTMB("fm_banks/tmb_files/nam.tmb", 71, "apgnam");
+
+LoadDoom("fm_banks/doom2/DMXOPL-by-sneakernets.op2", 72, "skeakernets");
+
+//LoadBNK("bnk_files/grassman1.bnk", 63, "b63", false);
+//LoadBNK("bnk_files/grassman2.bnk", 64, "b64", false);
+
+//LoadIBK("ibk_files/nitemare_3d.ibk", 65, "b65G", false); // Seems to be identical to wallace.op3 despite different format!
+#endif
diff --git a/utils/midiplay/Makefile b/utils/midiplay/Makefile
new file mode 100644
index 0000000..f2512b4
--- /dev/null
+++ b/utils/midiplay/Makefile
@@ -0,0 +1,12 @@
+all: midiplay
+
+midiplay: adlmidiplay.o wave_writer.o
+ g++ $^ -Wl,-rpath='$$ORIGIN' -L../../bin -ladlmidi -lSDL2 -o ../../bin/adlmidiplay
+ rm *.o
+
+adlmidiplay.o: adlmidiplay.cpp
+ g++ -c $^ -I.. -o adlmidiplay.o
+
+wave_writer.o: wave_writer.c
+ gcc -c $^ -I.. -o wave_writer.o
+
diff --git a/utils/midiplay/Makefile.win32 b/utils/midiplay/Makefile.win32
new file mode 100644
index 0000000..05e192f
--- /dev/null
+++ b/utils/midiplay/Makefile.win32
@@ -0,0 +1,12 @@
+all: midiplay
+
+midiplay: adlmidiplay.o wave_writer.o
+ g++ $^ -L../../bin -ladlmidi -lSDL2 -o ../../bin/adlmidiplay
+ rm *.o
+
+adlmidiplay.o: adlmidiplay.cpp
+ g++ -c $^ -I.. -o adlmidiplay.o
+
+wave_writer.o: wave_writer.c
+ gcc -c $^ -I.. -o wave_writer.o
+
diff --git a/utils/midiplay/adlmidiplay.cpp b/utils/midiplay/adlmidiplay.cpp
new file mode 100644
index 0000000..7c8766d
--- /dev/null
+++ b/utils/midiplay/adlmidiplay.cpp
@@ -0,0 +1,338 @@
+
+#include <vector>
+#include <string>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <deque>
+#include <signal.h>
+#define SDL_MAIN_HANDLED
+#include <SDL2/SDL.h>
+
+#include <adlmidi.h>
+
+#include "wave_writer.h"
+
+class MutexType
+{
+ SDL_mutex *mut;
+public:
+ MutexType() : mut(SDL_CreateMutex()) { }
+ ~MutexType()
+ {
+ SDL_DestroyMutex(mut);
+ }
+ void Lock()
+ {
+ SDL_mutexP(mut);
+ }
+ void Unlock()
+ {
+ SDL_mutexV(mut);
+ }
+};
+
+
+static std::deque<short> AudioBuffer;
+static MutexType AudioBuffer_lock;
+
+static void SDL_AudioCallbackX(void *, Uint8 *stream, int len)
+{
+ SDL_LockAudio();
+ short *target = (short *) stream;
+ AudioBuffer_lock.Lock();
+ /*if(len != AudioBuffer.size())
+ fprintf(stderr, "len=%d stereo samples, AudioBuffer has %u stereo samples",
+ len/4, (unsigned) AudioBuffer.size()/2);*/
+ unsigned ate = (unsigned)len / 2; // number of shorts
+ if(ate > AudioBuffer.size())
+ ate = (unsigned)AudioBuffer.size();
+ for(unsigned a = 0; a < ate; ++a)
+ {
+ target[a] = AudioBuffer[a];
+ }
+ AudioBuffer.erase(AudioBuffer.begin(), AudioBuffer.begin() + ate);
+ AudioBuffer_lock.Unlock();
+ SDL_UnlockAudio();
+}
+
+static bool is_number(const std::string &s)
+{
+ std::string::const_iterator it = s.begin();
+ while(it != s.end() && std::isdigit(*it)) ++it;
+ return !s.empty() && it == s.end();
+}
+
+static void printError(const char *err)
+{
+ std::fprintf(stderr, "\nERROR: %s\n\n", err);
+ std::fflush(stderr);
+}
+
+static int stop = 0;
+static void sighandler(int dum)
+{
+ if((dum == SIGINT)
+ || (dum == SIGTERM)
+ #ifndef _WIN32
+ || (dum == SIGHUP)
+ #endif
+ )
+ stop = 1;
+}
+
+int main(int argc, char **argv)
+{
+ if(argc < 2 || std::string(argv[1]) == "--help" || std::string(argv[1]) == "-h")
+ {
+ std::printf(
+ "Usage: adlmidi <midifilename> [ <options> ] [ <bank> [ <numcards> [ <numfourops>] ] ]\n"
+ " -p Enables adlib percussion instrument mode\n"
+ " -t Enables tremolo amplification mode\n"
+ " -v Enables vibrato amplification mode\n"
+ " -s Enables scaling of modulator volumes\n"
+ " -nl Quit without looping\n"
+ " -w Write WAV file rather than playing\n"
+ "\n"
+ "Where <bank> - number of embeeded bank or filepath to custom WOPL bank file\n"
+ "\n"
+ "Note: To create WOPL bank files use OPL Bank Editor you can get here: \n"
+ "https://github.com/Wohlstand/OPL3BankEditor\n"
+ "\n"
+ );
+
+ int banksCount = adl_getBanksCount();
+ const char *const *banknames = adl_getBankNames();
+
+ if(banksCount > 0)
+ {
+ std::printf(" Available embedded banks by number:\n\n");
+
+ for(int a = 0; a < banksCount; ++a)
+ std::printf("%10s%2u = %s\n", a ? "" : "Banks:", a, banknames[a]);
+
+ std::printf(
+ "\n"
+ " Use banks 2-5 to play Descent \"q\" soundtracks.\n"
+ " Look up the relevant bank number from descent.sng.\n"
+ "\n"
+ " The fourth parameter can be used to specify the number\n"
+ " of four-op channels to use. Each four-op channel eats\n"
+ " the room of two regular channels. Use as many as required.\n"
+ " The Doom & Hexen sets require one or two, while\n"
+ " Miles four-op set requires the maximum of numcards*6.\n"
+ "\n"
+ );
+ }
+ else
+ {
+ std::printf(" This build of libADLMIDI has no embedded banks!\n\n");
+ }
+
+ return 0;
+ }
+
+ //const unsigned MaxSamplesAtTime = 512; // 512=dbopl limitation
+ // How long is SDL buffer, in seconds?
+ // The smaller the value, the more often SDL_AudioCallBack()
+ // is called.
+ const double AudioBufferLength = 0.08;
+ // How much do WE buffer, in seconds? The smaller the value,
+ // the more prone to sound chopping we are.
+ const double OurHeadRoomLength = 0.1;
+ // The lag between visual content and audio content equals
+ // the sum of these two buffers.
+ SDL_AudioSpec spec;
+ SDL_AudioSpec obtained;
+
+ spec.freq = 44100;
+ spec.format = AUDIO_S16SYS;
+ spec.channels = 2;
+ spec.samples = Uint16((double)spec.freq * AudioBufferLength);
+ spec.callback = SDL_AudioCallbackX;
+
+ ADL_MIDIPlayer *myDevice;
+ myDevice = adl_init(44100);
+ if(myDevice == NULL)
+ {
+ printError("Failed to init MIDI device!\n");
+ return 1;
+ }
+
+ bool recordWave = false;
+
+ adl_setLoopEnabled(myDevice, 1);
+
+ while(argc > 2)
+ {
+ bool had_option = false;
+
+ if(!std::strcmp("-p", argv[2]))
+ adl_setPercMode(myDevice, 1);
+ else if(!std::strcmp("-v", argv[2]))
+ adl_setHVibrato(myDevice, 1);
+ else if(!std::strcmp("-w", argv[2]))
+ {
+ recordWave = true;
+ adl_setLoopEnabled(myDevice, 0);//Disable loop while record WAV
+ }
+ else if(!std::strcmp("-t", argv[2]))
+ adl_setHTremolo(myDevice, 1);
+ else if(!std::strcmp("-nl", argv[2]))
+ adl_setLoopEnabled(myDevice, 0);
+ else if(!std::strcmp("-s", argv[2]))
+ adl_setScaleModulators(myDevice, 1);
+ else break;
+
+ std::copy(argv + (had_option ? 4 : 3), argv + argc,
+ argv + 2);
+ argc -= (had_option ? 2 : 1);
+ }
+
+ if(!recordWave)
+ {
+ // Set up SDL
+ if(SDL_OpenAudio(&spec, &obtained) < 0)
+ {
+ std::fprintf(stderr, "\nERROR: Couldn't open audio: %s\n\n", SDL_GetError());
+ std::fflush(stderr);
+ //return 1;
+ }
+ if(spec.samples != obtained.samples)
+ {
+ std::fprintf(stderr, "Wanted (samples=%u,rate=%u,channels=%u); obtained (samples=%u,rate=%u,channels=%u)\n",
+ spec.samples, spec.freq, spec.channels,
+ obtained.samples, obtained.freq, obtained.channels);
+ std::fflush(stderr);
+ }
+ }
+
+ if(argc >= 3)
+ {
+ if(is_number(argv[2]))
+ {
+ int bankno = std::atoi(argv[2]);
+ if(adl_setBank(myDevice, bankno) != 0)
+ {
+ printError(adl_errorString());
+ return 1;
+ }
+ }
+ else
+ {
+ std::fprintf(stdout, "Loading custom bank file %s...", argv[2]);
+ std::fflush(stdout);
+ if(adl_openBankFile(myDevice, argv[2]) != 0)
+ {
+ std::fprintf(stdout, "FAILED!\n");
+ std::fflush(stdout);
+ printError(adl_errorString());
+ return 1;
+ }
+ std::fprintf(stdout, "OK!\n");
+ std::fflush(stdout);
+ }
+ }
+
+ if(argc >= 4)
+ {
+ if(adl_setNumCards(myDevice, std::atoi(argv[3])) != 0)
+ {
+ printError(adl_errorString());
+ return 1;
+ }
+ std::fprintf(stdout, "Number of cards %s\n", argv[3]);
+ }
+ else
+ {
+ // 4 chips by default
+ if(adl_setNumCards(myDevice, 4) != 0)
+ {
+ printError(adl_errorString());
+ return 1;
+ }
+ }
+
+ if(argc >= 5)
+ {
+ if(adl_setNumFourOpsChn(myDevice, std::atoi(argv[4])) != 0)
+ {
+ printError(adl_errorString());
+ return 1;
+ }
+ std::fprintf(stdout, "Number of four-ops %s\n", argv[4]);
+ }
+
+ if(adl_openFile(myDevice, argv[1]) != 0)
+ {
+ printError(adl_errorString());
+ return 2;
+ }
+
+ signal(SIGINT, sighandler);
+ signal(SIGTERM, sighandler);
+ #ifndef _WIN32
+ signal(SIGHUP, sighandler);
+ #endif
+
+ if(!recordWave)
+ {
+ SDL_PauseAudio(0);
+
+ while(!stop)
+ {
+ short buff[4096];
+ size_t got = (size_t)adl_play(myDevice, 4096, buff);
+ if(got <= 0)
+ break;
+
+ AudioBuffer_lock.Lock();
+ size_t pos = AudioBuffer.size();
+ AudioBuffer.resize(pos + got);
+ for(size_t p = 0; p < got; ++p)
+ AudioBuffer[pos + p] = buff[p];
+ AudioBuffer_lock.Unlock();
+
+ const SDL_AudioSpec &spec = obtained;
+ while(AudioBuffer.size() > spec.samples + (spec.freq * 2) * OurHeadRoomLength)
+ {
+ SDL_Delay(1);
+ }
+ }
+
+ SDL_CloseAudio();
+ }
+ else
+ {
+ std::string wave_out = std::string(argv[1]) + ".wav";
+ std::fprintf(stdout, "Recording WAV file %s...\n", wave_out.c_str());
+ std::fflush(stdout);
+
+ if(wave_open(spec.freq, wave_out.c_str()) == 0)
+ {
+ wave_enable_stereo();
+ while(!stop)
+ {
+ short buff[4096];
+ size_t got = (size_t)adl_play(myDevice, 4096, buff);
+ if(got <= 0)
+ break;
+ wave_write(buff, (long)got);
+ }
+ wave_close();
+
+ std::fprintf(stdout, "Completed!\n");
+ std::fflush(stdout);
+ }
+ else
+ {
+ adl_close(myDevice);
+ return 1;
+ }
+ }
+
+ adl_close(myDevice);
+
+ return 0;
+}
+
diff --git a/utils/midiplay/wave_writer.c b/utils/midiplay/wave_writer.c
new file mode 100755
index 0000000..0bfaf68
--- /dev/null
+++ b/utils/midiplay/wave_writer.c
@@ -0,0 +1,170 @@
+/* snes_spc 0.9.0. http://www.slack.net/~ant/ */
+
+#include "wave_writer.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+enum { buf_size = 32768 * 2 };
+enum { header_size = 0x2C };
+
+typedef short sample_t;
+
+static unsigned char* buf;
+static FILE* file;
+static long sample_count_;
+static long sample_rate_;
+static long buf_pos;
+static int chan_count;
+
+static void exit_with_error( const char* str )
+{
+ fprintf(stderr, "WAVE Writer Error: %s\n", str );
+ fflush(stderr);
+}
+
+int wave_open( long sample_rate, const char* filename )
+{
+ sample_count_ = 0;
+ sample_rate_ = sample_rate;
+ buf_pos = header_size;
+ chan_count = 1;
+
+ buf = (unsigned char*) malloc( buf_size * sizeof *buf );
+ if ( !buf )
+ {
+ exit_with_error( "Out of memory" );
+ return -1;
+ }
+
+#ifndef _WIN32
+ file = fopen( filename, "wb" );
+#else
+ wchar_t widePath[MAX_PATH];
+ int size = MultiByteToWideChar(CP_UTF8, 0, filename, strlen(filename), widePath, MAX_PATH);
+ widePath[size] = '\0';
+ file = _wfopen( widePath, L"wb" );
+#endif
+ if (!file)
+ {
+ exit_with_error( "Couldn't open WAVE file for writing" );
+ return -1;
+ }
+
+ setvbuf( file, 0, _IOFBF, 32 * 1024L );
+ return 0;
+}
+
+void wave_enable_stereo( void )
+{
+ chan_count = 2;
+}
+
+static void flush_()
+{
+ if ( buf_pos && !fwrite( buf, (size_t)buf_pos, 1, file ) )
+ exit_with_error( "Couldn't write WAVE data" );
+ buf_pos = 0;
+}
+
+void wave_write( short const* in, long remain )
+{
+ sample_count_ += remain;
+ while ( remain )
+ {
+ if ( buf_pos >= buf_size )
+ flush_();
+
+ {
+ unsigned char* p = &buf [buf_pos];
+ long n = (buf_size - (unsigned long)buf_pos) / sizeof (sample_t);
+ if ( n > remain )
+ n = remain;
+ remain -= n;
+
+ /* convert to LSB first format */
+ while ( n-- )
+ {
+ int s = *in++;
+ *p++ = (unsigned char) s & (0x00FF);
+ *p++ = (unsigned char) (s >> 8) & (0x00FF);
+ }
+
+ buf_pos = p - buf;
+ assert( buf_pos <= buf_size );
+ }
+ }
+}
+
+long wave_sample_count( void )
+{
+ return sample_count_;
+}
+
+static void set_le32( void* p, unsigned long n )
+{
+ ((unsigned char*) p) [0] = (unsigned char) n & (0xFF);
+ ((unsigned char*) p) [1] = (unsigned char) (n >> 8) & (0xFF);
+ ((unsigned char*) p) [2] = (unsigned char) (n >> 16) & (0xFF);
+ ((unsigned char*) p) [3] = (unsigned char) (n >> 24) & (0xFF);
+}
+
+void wave_close( void )
+{
+ if ( file )
+ {
+ /* generate header */
+ unsigned char h [header_size] =
+ {
+ 'R','I','F','F',
+ 0,0,0,0, /* length of rest of file */
+ 'W','A','V','E',
+ 'f','m','t',' ',
+ 0x10,0,0,0, /* size of fmt chunk */
+ 1,0, /* uncompressed format */
+ 0,0, /* channel count */
+ 0,0,0,0, /* sample rate */
+ 0,0,0,0, /* bytes per second */
+ 0,0, /* bytes per sample frame */
+ 16,0, /* bits per sample */
+ 'd','a','t','a',
+ 0,0,0,0, /* size of sample data */
+ /* ... */ /* sample data */
+ };
+ long ds = sample_count_ * (long)sizeof (sample_t);
+ int frame_size = chan_count * (long)sizeof (sample_t);
+
+ set_le32( h + 0x04, header_size - 8 + ds );
+ h [0x16] = (unsigned char)chan_count;
+ set_le32( h + 0x18, (unsigned long)sample_rate_ );
+ set_le32( h + 0x1C, (unsigned long)sample_rate_ * (unsigned long)frame_size );
+ h [0x20] = (unsigned char)frame_size;
+ set_le32( h + 0x28, (unsigned long)ds );
+
+ flush_();
+
+ /* write header */
+ fseek( file, 0, SEEK_SET );
+ fwrite( h, header_size, 1, file );
+ fclose( file );
+ file = 0;
+ free( buf );
+ buf = 0;
+ }
+}
diff --git a/utils/midiplay/wave_writer.h b/utils/midiplay/wave_writer.h
new file mode 100755
index 0000000..6d49718
--- /dev/null
+++ b/utils/midiplay/wave_writer.h
@@ -0,0 +1,21 @@
+/* WAVE sound file writer for recording 16-bit output during program development */
+
+#pragma once
+#ifndef WAVE_WRITER_H
+#define WAVE_WRITER_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+int wave_open( long sample_rate, const char* filename );
+void wave_enable_stereo( void );
+void wave_write( short const* in, long count );
+long wave_sample_count( void );
+void wave_close( void );
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/utils/test/adldatatest.cc b/utils/test/adldatatest.cc
new file mode 100644
index 0000000..9f992c0
--- /dev/null
+++ b/utils/test/adldatatest.cc
@@ -0,0 +1,2 @@
+static const int adldata[2184] = {
+1,1,1,129,1,1,1,1,12,7,23,152,24,21,69,3,113,114,112,35,97,36,97,33,2,3,35,3,3,35,35,9,33,49,49,49,1,33,49,49,49,49,113,33,241,2,2,16,33,161,161,33,49,161,113,144,33,33,33,49,33,97,161,33,49,49,49,49,33,49,49,50,225,225,161,49,225,98,98,98,34,33,34,33,33,162,32,33,119,97,97,113,33,161,33,161,58,33,6,34,65,97,97,164,2,17,17,147,4,33,49,32,5,7,5,24,16,17,1,14,6,14,14,213,53,14,38,0,16,16,2,0,0,0,0,12,0,12,0,12,0,0,14,0,14,14,14,2,78,17,14,128,14,6,1,1,1,1,1,0,3,3,14,14,215,215,128,128,6,6,6,1,65,10,10,14,14,5,2,1,0,0,1,1,1,65,1,1,22,1,129,17,1,129,1,1,129,129,49,48,177,177,177,177,33,161,65,17,33,33,33,33,33,132,162,49,49,50,33,33,49,49,33,33,49,35,225,1,1,17,162,33,97,97,114,97,114,65,33,33,97,33,33,34,33,33,97,97,97,97,33,33,50,33,225,225,33,33,161,161,161,161,33,33,33,161,34,97,96,33,161,177,97,114,162,33,97,33,81,33,1,97,66,163,161,97,7,19,17,145,1,34,33,33,3,2,1,18,0,16,16,192,3,208,192,218,20,208,228,0,17,17,17,0,1,0,0,18,0,18,0,18,0,0,208,0,7,208,7,5,158,16,208,16,7,2,2,1,2,2,0,0,12,12,0,3,199,199,17,17,21,18,18,2,66,30,30,0,7,4,21,2,0,0,143,75,73,18,87,147,128,146,92,151,33,98,35,145,89,73,146,20,68,147,19,72,19,19,156,84,95,135,71,74,74,161,30,18,141,91,139,139,139,18,21,22,73,77,64,26,29,65,155,152,147,24,91,144,87,0,146,148,148,67,155,138,134,77,143,142,145,142,75,144,129,144,31,70,156,139,76,203,153,147,89,14,70,69,139,158,26,143,165,31,23,93,151,28,137,21,206,21,91,146,77,148,140,76,133,12,6,145,79,73,133,4,106,21,157,150,134,65,142,0,128,0,0,149,92,0,0,0,68,68,7,0,2,0,0,0,0,0,0,0,0,0,0,0,8,0,10,3,0,69,0,0,8,11,0,81,84,89,0,128,128,133,64,64,220,220,0,0,63,63,63,88,69,64,124,64,10,5,63,79,0,0,6,0,0,0,0,0,14,0,0,128,0,0,0,0,128,128,0,0,0,0,128,0,0,137,128,0,0,128,0,5,0,128,0,0,0,0,64,8,0,0,0,0,0,128,0,128,128,0,1,0,0,0,131,0,0,0,1,5,0,0,0,6,131,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,64,0,128,0,128,0,0,0,0,3,0,0,0,0,131,0,128,128,0,3,128,0,0,0,0,0,129,128,0,0,0,3,3,0,0,136,5,0,64,8,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,74,10,93,10,0,8,0,13,74,0,0,0,0,0,0,0,8,0,8,0,0,0,0,9,0,0,0,0,8,78,82,8,64,64,0,0,0,0,242,242,242,242,241,241,161,194,246,243,84,243,246,246,211,117,246,199,170,151,151,152,145,113,243,243,241,246,249,145,149,32,148,241,241,81,161,162,244,241,221,221,209,113,241,245,245,245,177,127,193,193,244,116,84,84,133,117,118,158,97,117,114,84,147,147,147,147,170,126,117,155,133,136,117,132,102,118,87,119,255,255,134,102,146,223,239,241,83,168,145,84,33,161,17,17,248,33,116,177,241,17,17,243,210,163,246,212,250,124,221,218,241,236,103,250,168,248,241,31,248,248,246,55,178,246,255,243,248,248,249,252,255,252,246,246,246,246,246,246,246,246,246,246,248,245,228,180,246,248,246,255,248,245,250,250,250,250,249,249,248,248,118,200,173,168,246,245,0,0,0,103,248,224,224,122,228,249,0,250,246,0,242,242,242,242,242,242,242,194,243,242,244,242,231,246,163,181,241,199,138,85,85,70,97,97,243,241,242,243,246,132,148,209,195,241,241,113,242,161,241,241,86,102,97,114,111,133,243,242,114,63,79,79,138,113,122,165,143,143,130,98,127,116,113,166,114,114,130,114,143,139,97,114,101,101,117,101,101,85,86,118,255,255,100,150,145,111,143,244,160,37,85,106,66,49,66,207,134,65,165,242,242,17,29,129,242,162,242,235,194,111,86,143,195,248,223,248,250,243,243,31,86,52,31,86,244,79,18,246,243,243,248,250,255,250,246,251,246,123,246,203,246,246,159,246,244,159,245,151,159,243,159,255,244,245,200,250,248,248,250,246,246,246,119,155,141,136,103,70,247,247,247,117,117,255,255,123,85,214,247,248,246,0,244,244,244,247,247,247,242,248,244,242,244,246,246,246,243,245,20,88,24,35,4,42,6,6,148,154,58,34,84,65,25,79,6,40,232,40,154,22,232,40,19,19,28,18,33,117,117,5,37,3,18,34,21,57,5,99,23,23,21,23,106,31,85,60,2,3,3,15,22,23,25,33,95,95,31,88,86,70,7,7,3,15,85,18,42,5,1,41,148,17,52,1,67,119,51,71,246,35,149,129,81,81,49,115,83,17,65,50,86,32,51,5,229,38,53,40,7,71,6,0,36,0,0,163,97,0,1,240,119,119,255,5,7,5,12,8,12,8,12,2,12,12,0,12,66,48,228,4,0,55,0,3,66,12,191,135,141,136,10,137,136,136,79,73,5,4,6,5,244,244,244,231,72,240,240,74,228,50,243,141,12,0,247,247,246,247,247,247,245,248,245,241,244,246,247,246,243,245,7,8,8,20,4,26,7,7,200,231,248,248,58,25,25,248,166,24,120,72,223,223,120,24,38,6,12,6,22,53,244,195,8,7,5,5,5,103,5,69,9,9,55,44,10,15,24,28,11,9,9,15,10,12,25,23,26,26,10,26,38,54,7,7,15,15,24,10,42,7,6,9,5,3,22,3,53,71,37,7,2,19,114,38,245,19,3,35,246,229,230,17,5,12,22,11,229,22,5,229,3,3,2,255,132,4,2,55,21,245,22,201,6,6,255,23,8,23,6,71,6,71,6,67,6,6,2,6,228,2,229,247,2,5,2,20,228,8,151,183,184,182,6,108,182,182,24,105,5,4,23,22,245,245,245,7,5,5,2,27,57,165,245,181,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,0,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,2,1,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,1,1,0,1,0,1,1,0,0,0,1,2,0,0,0,0,0,2,2,0,0,0,0,0,2,0,0,0,2,2,0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,2,0,3,0,0,0,0,0,0,3,3,3,3,0,0,3,3,3,2,0,3,0,0,0,3,3,0,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,2,1,0,1,0,0,0,0,0,0,0,0,0,3,2,3,3,0,0,3,1,2,0,0,0,0,0,0,0,2,0,2,0,2,0,0,3,0,3,0,1,0,3,0,3,0,3,0,0,0,0,0,0,0,0,0,2,2,0,0,3,3,0,0,0,0,0,0,0,2,1,0,0,0,0,0,8,8,8,6,0,0,8,10,0,2,2,0,0,4,12,4,2,2,4,4,0,12,10,6,12,12,0,6,0,8,8,8,2,10,10,12,8,8,10,10,8,8,8,2,2,0,0,2,14,0,10,12,0,0,12,8,12,12,12,2,2,8,0,8,8,8,10,10,8,6,0,4,0,0,2,0,0,0,11,11,0,0,0,0,0,2,0,10,2,10,12,0,8,0,10,0,2,0,0,12,0,6,6,4,0,0,4,8,12,6,10,6,6,10,8,10,6,4,14,14,14,14,14,0,10,14,14,14,8,8,8,14,0,14,4,10,4,10,4,10,4,4,14,4,14,14,6,14,14,8,14,12,14,6,7,6,6,6,14,14,15,15,14,14,14,14,14,14,1,0,1,0,0,8,8,14,6,14,8,7,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,35,52,48,58,60,47,43,49,43,51,43,54,57,72,60,76,84,36,65,84,83,84,24,77,60,65,59,51,45,71,60,58,53,64,71,61,61,44,40,69,68,63,74,60,80,64,72,73,70,68,48,53,0};
diff --git a/utils/test/test.cc b/utils/test/test.cc
new file mode 100644
index 0000000..ff8515b
--- /dev/null
+++ b/utils/test/test.cc
@@ -0,0 +1,202 @@
+#include <stdio.h>
+static const unsigned char adl[182][12] =
+{
+// The data bytes are:
+// [0,1] AM/VIB/EG/KSR/Multiple bits for carrier and modulator respectively
+// [2,3] KSL/Attenuation settings for carrier and modulator respectively
+// [4,5] Attack and decay rates for carrier and modulator respectively
+// [6,7] Sustain and release rates for carrier and modulator respectively
+// [8,9] Wave select settings for carrier and modulator respectively
+// [10] Feedback/connection bits for the channel (also stereo/pan bits)
+// [11] For percussive instruments (GP35..GP87), the tone to play
+ { 1, 1,143, 6,242,242,244,247,0,0, 56, 0}, // GM1:AcouGrandPiano
+ { 1, 1, 75, 0,242,242,244,247,0,0, 56, 0}, // GM2:BrightAcouGrand
+ { 1, 1, 73, 0,242,242,244,246,0,0, 56, 0}, // GM3:ElecGrandPiano
+ { 129, 65, 18, 0,242,242,247,247,0,0, 54, 0}, // GM4:Honky-tonkPiano
+ { 1, 1, 87, 0,241,242,247,247,0,0, 48, 0}, // GM5:Rhodes Piano
+ { 1, 1,147, 0,241,242,247,247,0,0, 48, 0}, // GM6:Chorused Piano
+ { 1, 22,128, 14,161,242,242,245,0,0, 56, 0}, // GM7:Harpsichord
+ { 1, 1,146, 0,194,194,248,248,0,0, 58, 0}, // GM8:Clavinet
+ { 12,129, 92, 0,246,243,244,245,0,0, 48, 0}, // GM9:Celesta
+ { 7, 17,151,128,243,242,242,241,0,0, 50, 0}, // GM10:Glockenspiel
+ { 23, 1, 33, 0, 84,244,244,244,0,0, 50, 0}, // GM11:Music box
+ { 152,129, 98, 0,243,242,246,246,0,0, 48, 0}, // GM12:Vibraphone
+ { 24, 1, 35, 0,246,231,246,247,0,0, 48, 0}, // GM13:Marimba
+ { 21, 1,145, 0,246,246,246,246,0,0, 52, 0}, // GM14:Xylophone
+ { 69,129, 89,128,211,163,243,243,0,0, 60, 0}, // GM15:Tubular Bells
+ { 3,129, 73,128,117,181,245,245,1,0, 52, 0}, // GM16:Dulcimer
+ { 113, 49,146, 0,246,241, 20, 7,0,0, 50, 0}, // GM17:Hammond Organ
+ { 114, 48, 20, 0,199,199, 88, 8,0,0, 50, 0}, // GM18:Percussive Organ
+ { 112,177, 68, 0,170,138, 24, 8,0,0, 52, 0}, // GM19:Rock Organ
+ { 35,177,147, 0,151, 85, 35, 20,1,0, 52, 0}, // GM20:Church Organ
+ { 97,177, 19,128,151, 85, 4, 4,1,0, 48, 0}, // GM21:Reed Organ
+ { 36,177, 72, 0,152, 70, 42, 26,1,0, 60, 0}, // GM22:Accordion
+ { 97, 33, 19, 0,145, 97, 6, 7,1,0, 58, 0}, // GM23:Harmonica
+ { 33,161, 19,137,113, 97, 6, 7,0,0, 54, 0}, // GM24:Tango Accordion
+ { 2, 65,156,128,243,243,148,200,1,0, 60, 0}, // GM25:Acoustic Guitar1
+ { 3, 17, 84, 0,243,241,154,231,1,0, 60, 0}, // GM26:Acoustic Guitar2
+ { 35, 33, 95, 0,241,242, 58,248,0,0, 48, 0}, // GM27:Electric Guitar1
+ { 3, 33,135,128,246,243, 34,248,1,0, 54, 0}, // GM28:Electric Guitar2
+ { 3, 33, 71, 0,249,246, 84, 58,0,0, 48, 0}, // GM29:Electric Guitar3
+ { 35, 33, 74, 5,145,132, 65, 25,1,0, 56, 0}, // GM30:Overdrive Guitar
+ { 35, 33, 74, 0,149,148, 25, 25,1,0, 56, 0}, // GM31:Distorton Guitar
+ { 9,132,161,128, 32,209, 79,248,0,0, 56, 0}, // GM32:Guitar Harmonics
+ { 33,162, 30, 0,148,195, 6,166,0,0, 50, 0}, // GM33:Acoustic Bass
+ { 49, 49, 18, 0,241,241, 40, 24,0,0, 58, 0}, // GM34:Electric Bass 1
+ { 49, 49,141, 0,241,241,232,120,0,0, 58, 0}, // GM35:Electric Bass 2
+ { 49, 50, 91, 0, 81,113, 40, 72,0,0, 60, 0}, // GM36:Fretless Bass
+ { 1, 33,139, 64,161,242,154,223,0,0, 56, 0}, // GM37:Slap Bass 1
+ { 33, 33,139, 8,162,161, 22,223,0,0, 56, 0}, // GM38:Slap Bass 2
+ { 49, 49,139, 0,244,241,232,120,0,0, 58, 0}, // GM39:Synth Bass 1
+ { 49, 49, 18, 0,241,241, 40, 24,0,0, 58, 0}, // GM40:Synth Bass 2
+ { 49, 33, 21, 0,221, 86, 19, 38,1,0, 56, 0}, // GM41:Violin
+ { 49, 33, 22, 0,221,102, 19, 6,1,0, 56, 0}, // GM42:Viola
+ { 113, 49, 73, 0,209, 97, 28, 12,1,0, 56, 0}, // GM43:Cello
+ { 33, 35, 77,128,113,114, 18, 6,1,0, 50, 0}, // GM44:Contrabass
+ { 241,225, 64, 0,241,111, 33, 22,1,0, 50, 0}, // GM45:Tremulo Strings
+ { 2, 1, 26,128,245,133,117, 53,1,0, 48, 0}, // GM46:Pizzicato String
+ { 2, 1, 29,128,245,243,117,244,1,0, 48, 0}, // GM47:Orchestral Harp
+ { 16, 17, 65, 0,245,242, 5,195,1,0, 50, 0}, // GM48:Timpany
+ { 33,162,155, 1,177,114, 37, 8,1,0, 62, 0}, // GM49:String Ensemble1
+ { 161, 33,152, 0,127, 63, 3, 7,1,1, 48, 0}, // GM50:String Ensemble2
+ { 161, 97,147, 0,193, 79, 18, 5,0,0, 58, 0}, // GM51:Synth Strings 1
+ { 33, 97, 24, 0,193, 79, 34, 5,0,0, 60, 0}, // GM52:SynthStrings 2
+ { 49,114, 91,131,244,138, 21, 5,0,0, 48, 0}, // GM53:Choir Aahs
+ { 161, 97,144, 0,116,113, 57,103,0,0, 48, 0}, // GM54:Voice Oohs
+ { 113,114, 87, 0, 84,122, 5, 5,0,0, 60, 0}, // GM55:Synth Voice
+ { 144, 65, 0, 0, 84,165, 99, 69,0,0, 56, 0}, // GM56:Orchestra Hit
+ { 33, 33,146, 1,133,143, 23, 9,0,0, 60, 0}, // GM57:Trumpet
+ { 33, 33,148, 5,117,143, 23, 9,0,0, 60, 0}, // GM58:Trombone
+ { 33, 97,148, 0,118,130, 21, 55,0,0, 60, 0}, // GM59:Tuba
+ { 49, 33, 67, 0,158, 98, 23, 44,1,1, 50, 0}, // GM60:Muted Trumpet
+ { 33, 33,155, 0, 97,127,106, 10,0,0, 50, 0}, // GM61:French Horn
+ { 97, 34,138, 6,117,116, 31, 15,0,0, 56, 0}, // GM62:Brass Section
+ { 161, 33,134,131,114,113, 85, 24,1,0, 48, 0}, // GM63:Synth Brass 1
+ { 33, 33, 77, 0, 84,166, 60, 28,0,0, 56, 0}, // GM64:Synth Brass 2
+ { 49, 97,143, 0,147,114, 2, 11,1,0, 56, 0}, // GM65:Soprano Sax
+ { 49, 97,142, 0,147,114, 3, 9,1,0, 56, 0}, // GM66:Alto Sax
+ { 49, 97,145, 0,147,130, 3, 9,1,0, 58, 0}, // GM67:Tenor Sax
+ { 49, 97,142, 0,147,114, 15, 15,1,0, 58, 0}, // GM68:Baritone Sax
+ { 33, 33, 75, 0,170,143, 22, 10,1,0, 56, 0}, // GM69:Oboe
+ { 49, 33,144, 0,126,139, 23, 12,1,1, 54, 0}, // GM70:English Horn
+ { 49, 50,129, 0,117, 97, 25, 25,1,0, 48, 0}, // GM71:Bassoon
+ { 50, 33,144, 0,155,114, 33, 23,0,0, 52, 0}, // GM72:Clarinet
+ { 225,225, 31, 0,133,101, 95, 26,0,0, 48, 0}, // GM73:Piccolo
+ { 225,225, 70, 0,136,101, 95, 26,0,0, 48, 0}, // GM74:Flute
+ { 161, 33,156, 0,117,117, 31, 10,0,0, 50, 0}, // GM75:Recorder
+ { 49, 33,139, 0,132,101, 88, 26,0,0, 48, 0}, // GM76:Pan Flute
+ { 225,161, 76, 0,102,101, 86, 38,0,0, 48, 0}, // GM77:Bottle Blow
+ { 98,161,203, 0,118, 85, 70, 54,0,0, 48, 0}, // GM78:Shakuhachi
+ { 98,161,153, 0, 87, 86, 7, 7,0,0, 59, 0}, // GM79:Whistle
+ { 98,161,147, 0,119,118, 7, 7,0,0, 59, 0}, // GM80:Ocarina
+ { 34, 33, 89, 0,255,255, 3, 15,2,0, 48, 0}, // GM81:Lead 1 squareea
+ { 33, 33, 14, 0,255,255, 15, 15,1,1, 48, 0}, // GM82:Lead 2 sawtooth
+ { 34, 33, 70,128,134,100, 85, 24,0,0, 48, 0}, // GM83:Lead 3 calliope
+ { 33,161, 69, 0,102,150, 18, 10,0,0, 48, 0}, // GM84:Lead 4 chiff
+ { 33, 34,139, 0,146,145, 42, 42,1,0, 48, 0}, // GM85:Lead 5 charang
+ { 162, 97,158, 64,223,111, 5, 7,0,0, 50, 0}, // GM86:Lead 6 voice
+ { 32, 96, 26, 0,239,143, 1, 6,0,2, 48, 0}, // GM87:Lead 7 fifths
+ { 33, 33,143,128,241,244, 41, 9,0,0, 58, 0}, // GM88:Lead 8 brass
+ { 119,161,165, 0, 83,160,148, 5,0,0, 50, 0}, // GM89:Pad 1 new age
+ { 97,177, 31,128,168, 37, 17, 3,0,0, 58, 0}, // GM90:Pad 2 warm
+ { 97, 97, 23, 0,145, 85, 52, 22,0,0, 60, 0}, // GM91:Pad 3 polysynth
+ { 113,114, 93, 0, 84,106, 1, 3,0,0, 48, 0}, // GM92:Pad 4 choir
+ { 33,162,151, 0, 33, 66, 67, 53,0,0, 56, 0}, // GM93:Pad 5 bowedpad
+ { 161, 33, 28, 0,161, 49,119, 71,1,1, 48, 0}, // GM94:Pad 6 metallic
+ { 33, 97,137, 3, 17, 66, 51, 37,0,0, 58, 0}, // GM95:Pad 7 halo
+ { 161, 33, 21, 0, 17,207, 71, 7,1,0, 48, 0}, // GM96:Pad 8 sweep
+ { 58, 81,206, 0,248,134,246, 2,0,0, 50, 0}, // GM97:FX 1 rain
+ { 33, 33, 21, 0, 33, 65, 35, 19,1,0, 48, 0}, // GM98:FX 2 soundtrack
+ { 6, 1, 91, 0,116,165,149,114,0,0, 48, 0}, // GM99:FX 3 crystal
+ { 34, 97,146,131,177,242,129, 38,0,0, 60, 0}, // GM100:FX 4 atmosphere
+ { 65, 66, 77, 0,241,242, 81,245,1,0, 48, 0}, // GM101:FX 5 brightness
+ { 97,163,148,128, 17, 17, 81, 19,1,0, 54, 0}, // GM102:FX 6 goblins
+ { 97,161,140,128, 17, 29, 49, 3,0,0, 54, 0}, // GM103:FX 7 echoes
+ { 164, 97, 76, 0,243,129,115, 35,1,0, 52, 0}, // GM104:FX 8 sci-fi
+ { 2, 7,133, 3,210,242, 83,246,0,1, 48, 0}, // GM105:Sitar
+ { 17, 19, 12,128,163,162, 17,229,1,0, 48, 0}, // GM106:Banjo
+ { 17, 17, 6, 0,246,242, 65,230,1,2, 52, 0}, // GM107:Shamisen
+ { 147,145,145, 0,212,235, 50, 17,0,1, 56, 0}, // GM108:Koto
+ { 4, 1, 79, 0,250,194, 86, 5,0,0, 60, 0}, // GM109:Kalimba
+ { 33, 34, 73, 0,124,111, 32, 12,0,1, 54, 0}, // GM110:Bagpipe
+ { 49, 33,133, 0,221, 86, 51, 22,1,0, 58, 0}, // GM111:Fiddle
+ { 32, 33, 4,129,218,143, 5, 11,2,0, 54, 0}, // GM112:Shanai
+ { 5, 3,106,128,241,195,229,229,0,0, 54, 0}, // GM113:Tinkle Bell
+ { 7, 2, 21, 0,236,248, 38, 22,0,0, 58, 0}, // GM114:Agogo Bells
+ { 5, 1,157, 0,103,223, 53, 5,0,0, 56, 0}, // GM115:Steel Drums
+ { 24, 18,150, 0,250,248, 40,229,0,0, 58, 0}, // GM116:Woodblock
+ { 16, 0,134, 3,168,250, 7, 3,0,0, 54, 0}, // GM117:Taiko Drum
+ { 17, 16, 65, 3,248,243, 71, 3,2,0, 52, 0}, // GM118:Melodic Tom
+ { 1, 16,142, 0,241,243, 6, 2,2,0, 62, 0}, // GM119:Synth Drum
+ { 14,192, 0, 0, 31, 31, 0,255,0,3, 62, 0}, // GM120:Reverse Cymbal
+ { 6, 3,128,136,248, 86, 36,132,0,2, 62, 0}, // GM121:Guitar FretNoise
+ { 14,208, 0, 5,248, 52, 0, 4,0,3, 62, 0}, // GM122:Breath Noise
+ { 14,192, 0, 0,246, 31, 0, 2,0,3, 62, 0}, // GM123:Seashore
+ { 213,218,149, 64, 55, 86,163, 55,0,0, 48, 0}, // GM124:Bird Tweet
+ { 53, 20, 92, 8,178,244, 97, 21,2,0, 58, 0}, // GM125:Telephone
+ { 14,208, 0, 0,246, 79, 0,245,0,3, 62, 0}, // GM126:Helicopter
+ { 38,228, 0, 0,255, 18, 1, 22,0,1, 62, 0}, // GM127:Applause/Noise
+ { 0, 0, 0, 0,243,246,240,201,0,2, 62, 0}, // GM128:Gunshot
+ { 16, 17, 68, 0,248,243,119, 6,2,0, 56, 35}, // GP35:Ac Bass Drum
+ { 16, 17, 68, 0,248,243,119, 6,2,0, 56, 35}, // GP36:Bass Drum 1
+ { 2, 17, 7, 0,249,248,255,255,0,0, 56, 52}, // GP37:Side Stick
+ { 0, 0, 0, 0,252,250, 5, 23,2,0, 62, 48}, // GP38:Acoustic Snare
+ { 0, 1, 2, 0,255,255, 7, 8,0,0, 48, 58}, // GP39:Hand Clap
+ { 0, 0, 0, 0,252,250, 5, 23,2,0, 62, 60}, // GP40:Electric Snare
+ { 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 47}, // GP41:Low Floor Tom
+ { 12, 18, 0, 0,246,251, 8, 71,0,2, 58, 43}, // GP42:Closed High Hat
+ { 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 49}, // GP43:High Floor Tom
+ { 12, 18, 0, 5,246,123, 8, 71,0,2, 58, 43}, // GP44:Pedal High Hat
+ { 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 51}, // GP45:Low Tom
+ { 12, 18, 0, 0,246,203, 2, 67,0,2, 58, 43}, // GP46:Open High Hat
+ { 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 54}, // GP47:Low-Mid Tom
+ { 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 57}, // GP48:High-Mid Tom
+ { 14,208, 0, 0,246,159, 0, 2,0,3, 62, 72}, // GP49:Crash Cymbal 1
+ { 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 60}, // GP50:High Tom
+ { 14, 7, 8, 74,248,244, 66,228,0,3, 62, 76}, // GP51:Ride Cymbal 1
+ { 14,208, 0, 10,245,159, 48, 2,0,0, 62, 84}, // GP52:Chinese Cymbal
+ { 14, 7, 10, 93,228,245,228,229,3,1, 54, 36}, // GP53:Ride Bell
+ { 2, 5, 3, 10,180,151, 4,247,0,0, 62, 65}, // GP54:Tambourine
+ { 78,158, 0, 0,246,159, 0, 2,0,3, 62, 84}, // GP55:Splash Cymbal
+ { 17, 16, 69, 8,248,243, 55, 5,2,0, 56, 83}, // GP56:Cow Bell
+ { 14,208, 0, 0,246,159, 0, 2,0,3, 62, 84}, // GP57:Crash Cymbal 2
+ { 128, 16, 0, 13,255,255, 3, 20,3,0, 60, 24}, // GP58:Vibraslap
+ { 14, 7, 8, 74,248,244, 66,228,0,3, 62, 77}, // GP59:Ride Cymbal 2
+ { 6, 2, 11, 0,245,245, 12, 8,0,0, 54, 60}, // GP60:High Bongo
+ { 1, 2, 0, 0,250,200,191,151,0,0, 55, 65}, // GP61:Low Bongo
+ { 1, 1, 81, 0,250,250,135,183,0,0, 54, 59}, // GP62:Mute High Conga
+ { 1, 2, 84, 0,250,248,141,184,0,0, 54, 51}, // GP63:Open High Conga
+ { 1, 2, 89, 0,250,248,136,182,0,0, 54, 45}, // GP64:Low Conga
+ { 1, 0, 0, 0,249,250, 10, 6,3,0, 62, 71}, // GP65:High Timbale
+ { 0, 0,128, 0,249,246,137,108,3,0, 62, 60}, // GP66:Low Timbale
+ { 3, 12,128, 8,248,246,136,182,3,0, 63, 58}, // GP67:High Agogo
+ { 3, 12,133, 0,248,246,136,182,3,0, 63, 53}, // GP68:Low Agogo
+ { 14, 0, 64, 8,118,119, 79, 24,0,2, 62, 64}, // GP69:Cabasa
+ { 14, 3, 64, 0,200,155, 73,105,0,2, 62, 71}, // GP70:Maracas
+ { 215,199,220, 0,173,141, 5, 5,3,0, 62, 61}, // GP71:Short Whistle
+ { 215,199,220, 0,168,136, 4, 4,3,0, 62, 61}, // GP72:Long Whistle
+ { 128, 17, 0, 0,246,103, 6, 23,3,3, 62, 44}, // GP73:Short Guiro
+ { 128, 17, 0, 9,245, 70, 5, 22,2,3, 62, 40}, // GP74:Long Guiro
+ { 6, 21, 63, 0, 0,247,244,245,0,0, 49, 69}, // GP75:Claves
+ { 6, 18, 63, 0, 0,247,244,245,3,0, 48, 68}, // GP76:High Wood Block
+ { 6, 18, 63, 0, 0,247,244,245,0,0, 49, 63}, // GP77:Low Wood Block
+ { 1, 2, 88, 0,103,117,231, 7,0,0, 48, 74}, // GP78:Mute Cuica
+ { 65, 66, 69, 8,248,117, 72, 5,0,0, 48, 60}, // GP79:Open Cuica
+ { 10, 30, 64, 78,224,255,240, 5,3,0, 56, 80}, // GP80:Mute Triangle
+ { 10, 30,124, 82,224,255,240, 2,3,0, 56, 64}, // GP81:Open Triangle
+ { 14, 0, 64, 8,122,123, 74, 27,0,2, 62, 72}, // GP82
+ { 14, 7, 10, 64,228, 85,228, 57,3,1, 54, 73}, // GP83
+ { 5, 4, 5, 64,249,214, 50,165,3,0, 62, 70}, // GP84
+ { 2, 21, 63, 0, 0,247,243,245,3,0, 56, 68}, // GP85
+ { 1, 2, 79, 0,250,248,141,181,0,0, 55, 48}, // GP86
+ { 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 53} // GP87 (low-low-mid tom?)
+};
+int main()
+{
+ printf("static const int adldata[2184] = {\n");
+ for(unsigned x=0; x<12; ++x)
+ for(unsigned i=0; i<182; ++i)
+ printf(",%d",
+ (x==10 ? adl[i][x]&~0x30 : adl[i][x]));
+ printf("};\n");
+}