aboutsummaryrefslogtreecommitdiff
path: root/src/adlmidi_load.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/adlmidi_load.cpp')
-rw-r--r--src/adlmidi_load.cpp275
1 files changed, 272 insertions, 3 deletions
diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp
index c21249c..5258dc9 100644
--- a/src/adlmidi_load.cpp
+++ b/src/adlmidi_load.cpp
@@ -76,6 +76,275 @@ uint64_t MIDIplay::ReadVarLenEx(size_t tk, bool &ok)
return result;
}
+bool MIDIplay::LoadBank(const std::string &filename)
+{
+ fileReader file;
+ file.openFile(filename.c_str());
+ return LoadBank(file);
+}
+
+bool MIDIplay::LoadBank(void *data, unsigned long size)
+{
+ fileReader file;
+ file.openData(data, size);
+ return LoadBank(file);
+}
+
+
+
+/* WOPL-needed misc functions */
+static uint16_t toUint16LE(const uint8_t *arr)
+{
+ uint16_t num = arr[0];
+ num |= ((arr[1] << 8) & 0xFF00);
+ return num;
+}
+
+static uint16_t toUint16BE(const uint8_t *arr)
+{
+ uint16_t num = arr[1];
+ num |= ((arr[0] << 8) & 0xFF00);
+ return num;
+}
+
+static 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;
+}
+
+static const char *wopl3_magic = "WOPL3-BANK\0";
+static const uint16_t wopl_latest_version = 2;
+
+enum WOPL_InstrumentFlags
+{
+ WOPL_Flags_NONE = 0,
+ WOPL_Flag_Enable4OP = 0x01,
+ WOPL_Flag_Pseudo4OP = 0x02,
+};
+
+struct WOPL_Inst
+{
+ bool fourOps;
+ char padding[7];
+ struct adlinsdata adlins;
+ struct adldata op[2];
+};
+
+static bool readInstrument(MIDIplay::fileReader &file, WOPL_Inst &ins, bool isPercussion = false)
+{
+ uint8_t idata[62];
+ if(file.read(idata, 1, 62) != 62)
+ return false;
+
+ //strncpy(ins.name, char_p(idata), 32);
+ ins.op[0].finetune = (int8_t)toSint16BE(idata + 32);
+ ins.op[1].finetune = (int8_t)toSint16BE(idata + 34);
+ //ins.velocity_offset = int8_t(idata[36]);
+
+ ins.adlins.voice2_fine_tune = 0.0;
+ int8_t voice2_fine_tune = int8_t(idata[37]);
+ if(voice2_fine_tune != 0)
+ {
+ if(voice2_fine_tune == 1)
+ ins.adlins.voice2_fine_tune = 0.000025;
+ else if(voice2_fine_tune == -1)
+ ins.adlins.voice2_fine_tune = -0.000025;
+ else
+ ins.adlins.voice2_fine_tune = ((voice2_fine_tune * 15.625) / 1000.0);
+ }
+
+ ins.adlins.tone = isPercussion ? idata[38] : 0;
+
+ /* TODO: add those fields into next version of WOPL format
+ * and re-generate those values on file save! */
+ ins.adlins.ms_sound_kon = 1000;
+ ins.adlins.ms_sound_koff = 500;
+ /* ----------------------------------------------------- */
+
+ uint8_t flags = idata[39];
+ ins.adlins.flags = (flags & WOPL_Flag_Enable4OP) && (flags & WOPL_Flag_Pseudo4OP) ? adlinsdata::Flag_Pseudo4op : 0;
+ ins.fourOps = (flags & WOPL_Flag_Enable4OP) || (flags & WOPL_Flag_Pseudo4OP);
+
+ ins.op[0].feedconn = (idata[40]);
+ ins.op[1].feedconn = (idata[41]);
+
+ for(size_t op = 0, slt = 0; op < 4; op++, slt++)
+ {
+ size_t off = 42 + size_t(op) * 5;
+ // ins.setAVEKM(op, idata[off + 0]);//AVEKM
+ // ins.setAtDec(op, idata[off + 2]);//AtDec
+ // ins.setSusRel(op, idata[off + 3]);//SusRel
+ // ins.setWaveForm(op, idata[off + 4]);//WaveForm
+ // ins.setKSLL(op, idata[off + 1]);//KSLL
+ ins.op[slt].carrier_E862 =
+ ((static_cast<uint32_t>(idata[off + 4]) << 24) & 0xFF000000) //WaveForm
+ | ((static_cast<uint32_t>(idata[off + 3]) << 16) & 0x00FF0000) //SusRel
+ | ((static_cast<uint32_t>(idata[off + 2]) << 8) & 0x0000FF00) //AtDec
+ | ((static_cast<uint32_t>(idata[off + 0]) << 0) & 0x000000FF); //AVEKM
+ ins.op[slt].carrier_40 = idata[off + 1];//KSLL
+
+ op++;
+ off = 42 + size_t(op) * 5;
+ ins.op[slt].modulator_E862 =
+ ((static_cast<uint32_t>(idata[off + 4]) << 24) & 0xFF000000) //WaveForm
+ | ((static_cast<uint32_t>(idata[off + 3]) << 16) & 0x00FF0000) //SusRel
+ | ((static_cast<uint32_t>(idata[off + 2]) << 8) & 0x0000FF00) //AtDec
+ | ((static_cast<uint32_t>(idata[off + 0]) << 0) & 0x000000FF); //AVEKM
+ ins.op[slt].modulator_40 = idata[off + 1];//KSLL
+ }
+ return true;
+}
+
+bool MIDIplay::LoadBank(MIDIplay::fileReader &fr)
+{
+#define qqq(x) (void)x
+ size_t fsize;
+ qqq(fsize);
+ if(!fr.isValid())
+ {
+ ADLMIDI_ErrorString = "Custom bank: Invalid data stream!";
+ return false;
+ }
+
+ char magic[32];
+ memset(magic, 0, 32);
+
+ uint16_t version = 0;
+
+ uint16_t count_melodic_banks = 1;
+ uint16_t count_percusive_banks = 1;
+
+ if(fr.read(magic, 1, 11) != 11)
+ {
+ ADLMIDI_ErrorString = "Custom bank: Can't read magic number!";
+ return false;
+ }
+
+ if(strncmp(magic, wopl3_magic, 11) != 0)
+ {
+ ADLMIDI_ErrorString = "Custom bank: Invalid magic number!";
+ return false;
+ }
+
+ uint8_t version_raw[2];
+ if(fr.read(version_raw, 1, 2) != 2)
+ {
+ ADLMIDI_ErrorString = "Custom bank: Can't read version!";
+ return false;
+ }
+
+ version = toUint16LE(version_raw);
+ if(version > wopl_latest_version)
+ {
+ ADLMIDI_ErrorString = "Custom bank: Unsupported WOPL version!";
+ return false;
+ }
+
+ uint8_t head[6];
+ memset(head, 0, 6);
+ if(fr.read(head, 1, 6) != 6)
+ {
+ ADLMIDI_ErrorString = "Custom bank: Can't read header!";
+ return false;
+ }
+
+ count_melodic_banks = toUint16BE(head);
+ count_percusive_banks = toUint16BE(head + 2);
+
+ if((count_melodic_banks < 1) || (count_percusive_banks < 1))
+ {
+ ADLMIDI_ErrorString = "Custom bank: Too few banks in this file!";
+ return false;
+ }
+
+ /*UNUSED YET*/
+ //bool default_deep_vibrato = ((head[4]>>0) & 0x01);
+ //bool default_deep_tremolo = ((head[4]>>1) & 0x01);
+
+ //5'th byte reserved for Deep-Tremolo and Deep-Vibrato flags
+ //6'th byte reserved for ADLMIDI's default volume model
+
+ if(version >= 2)//Read bank meta-entries
+ {
+ for(uint16_t i = 0; i < count_melodic_banks; i++)
+ {
+ uint8_t bank_meta[34];
+ if(fr.read(bank_meta, 1, 34) != 34)
+ {
+ ADLMIDI_ErrorString = "Custom bank: Fail to read melodic bank meta-data!";
+ return false;
+ }
+ //strncpy(bankMeta.name, char_p(bank_meta), 32);
+ //bankMeta.lsb = bank_meta[32];
+ //bankMeta.msb = bank_meta[33];
+ }
+
+ for(uint16_t i = 0; i < count_percusive_banks; i++)
+ {
+ uint8_t bank_meta[34];
+ if(fr.read(bank_meta, 1, 34) != 34)
+ {
+ ADLMIDI_ErrorString = "Custom bank: Fail to read melodic bank meta-data!";
+ return false;
+ }
+ //strncpy(bankMeta.name, char_p(bank_meta), 32);
+ //bankMeta.lsb = bank_meta[32];
+ //bankMeta.msb = bank_meta[33];
+ }
+ }
+
+ opl.dynamic_metainstruments.clear();
+ opl.dynamic_instruments.clear();
+
+ uint16_t total = 128 * count_melodic_banks;
+ bool readPercussion = false;
+
+tryAgain:
+ for(uint16_t i = 0; i < total; i++)
+ {
+ WOPL_Inst ins;
+ memset(&ins, 0, sizeof(WOPL_Inst));
+ if(!readInstrument(fr, ins, readPercussion))
+ {
+ opl.dynamic_metainstruments.clear();
+ opl.dynamic_instruments.clear();
+ ADLMIDI_ErrorString = "Custom bank: Fail to read instrument!";
+ return false;
+ }
+
+ if(i < 128) //Only first bank!
+ {
+ /*
+ * 0..127 - melodic, 128...255 - percussion.
+ * TODO: Make separated melodic and drum arrays and make MIDI bank ID support
+ */
+ ins.adlins.adlno1 = static_cast<uint16_t>(opl.dynamic_instruments.size() | opl.DynamicInstrumentTag);
+ opl.dynamic_instruments.push_back(ins.op[0]);
+ ins.adlins.adlno2 = ins.adlins.adlno1;
+ if(ins.fourOps)
+ {
+ ins.adlins.adlno2 = static_cast<uint16_t>(opl.dynamic_instruments.size() | opl.DynamicInstrumentTag);
+ opl.dynamic_instruments.push_back(ins.op[1]);
+ }
+ opl.dynamic_metainstruments.push_back(ins.adlins);
+ }
+ }
+
+ if(!readPercussion)
+ {
+ total = 128 * count_percusive_banks;
+ readPercussion = true;
+ goto tryAgain;
+ }
+
+ opl.AdlBank = ~0u; // Use dynamic banks!
+
+ return true;
+}
+
bool MIDIplay::LoadMIDI(const std::string &filename)
{
fileReader file;
@@ -99,7 +368,7 @@ bool MIDIplay::LoadMIDI(void *data, unsigned long size)
bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr)
{
- #define qqq(x) (void)x
+#define qqq(x) (void)x
size_t fsize;
qqq(fsize);
//! Temp buffer for conversion
@@ -197,7 +466,7 @@ riffskip:
fr.seek(0, SEEK_END);
size_t mus_len = fr.tell();
fr.seek(0, SEEK_SET);
- uint8_t *mus = (uint8_t*)malloc(mus_len);
+ uint8_t *mus = (uint8_t *)malloc(mus_len);
if(!mus)
{
ADLMIDI_ErrorString = "Out of memory!";
@@ -235,7 +504,7 @@ riffskip:
fr.seek(0, SEEK_END);
size_t mus_len = fr.tell();
fr.seek(0, SEEK_SET);
- uint8_t *mus = (uint8_t*)malloc(mus_len);
+ uint8_t *mus = (uint8_t *)malloc(mus_len);
if(!mus)
{
ADLMIDI_ErrorString = "Out of memory!";