From 419d96edc3008ccc0fadbddb1b6b426f713229d3 Mon Sep 17 00:00:00 2001 From: Wohlstand Date: Sat, 22 Oct 2016 21:51:54 +0300 Subject: Apply latest update of ADLMIDI - Added support of playing CMF files --- src/adlmidi.cpp | 394 ++++++++++++++++++++++++++++++++--------- src/adlmidi.h | 7 + src/gen_adldata/gen_adldata.cc | 2 +- 3 files changed, 318 insertions(+), 85 deletions(-) (limited to 'src') diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp index 96c07f4..050924e 100644 --- a/src/adlmidi.cpp +++ b/src/adlmidi.cpp @@ -69,22 +69,31 @@ static const unsigned MaxCards = 100; static std::string ADLMIDI_ErrorString; static const unsigned short Operators[23*2] = - {0x000,0x003,0x001,0x004,0x002,0x005, // operators 0, 3, 1, 4, 2, 5 + {// Channels 0-2 + 0x000,0x003,0x001,0x004,0x002,0x005, // operators 0, 3, 1, 4, 2, 5 + // Channels 3-5 0x008,0x00B,0x009,0x00C,0x00A,0x00D, // operators 6, 9, 7,10, 8,11 + // Channels 6-8 0x010,0x013,0x011,0x014,0x012,0x015, // operators 12,15, 13,16, 14,17 + // Same for second card 0x100,0x103,0x101,0x104,0x102,0x105, // operators 18,21, 19,22, 20,23 0x108,0x10B,0x109,0x10C,0x10A,0x10D, // operators 24,27, 25,28, 26,29 0x110,0x113,0x111,0x114,0x112,0x115, // operators 30,33, 31,34, 32,35 + // Channel 18 0x010,0x013, // operators 12,15 + // Channel 19 0x014,0xFFF, // operator 16 + // Channel 19 0x012,0xFFF, // operator 14 + // Channel 19 0x015,0xFFF, // operator 17 + // Channel 19 0x011,0xFFF }; // operator 13 static const unsigned short Channels[23] = {0x000,0x001,0x002, 0x003,0x004,0x005, 0x006,0x007,0x008, // 0..8 0x100,0x101,0x102, 0x103,0x104,0x105, 0x106,0x107,0x108, // 9..17 (secondary set) - 0x006,0x007,0x008,0xFFF,0xFFF }; // <- hw percussions, 0xFFF = no support for pitch/pan + 0x006,0x007,0x008, 0xFFF,0xFFF }; // <- hw percussions, 0xFFF = no support for pitch/pan /* In OPL3 mode: @@ -119,6 +128,7 @@ static const unsigned short Channels[23] = struct OPL3 { + friend class MIDIplay; unsigned NumChannels; ADL_MIDIPlayer* _parent; @@ -127,6 +137,29 @@ private: std::vector ins; // index to adl[], cached, needed by Touch() std::vector pit; // value poked to B0, cached, needed by NoteOff)( std::vector regBD; + + std::vector dynamic_metainstruments; // Replaces adlins[] when CMF file + std::vector dynamic_instruments; // Replaces adl[] when CMF file + const unsigned DynamicInstrumentTag = 0x8000u, DynamicMetaInstrumentTag = 0x4000000u; + const adlinsdata& GetAdlMetaIns(unsigned n) + { + return (n & DynamicMetaInstrumentTag) ? + dynamic_metainstruments[n & ~DynamicMetaInstrumentTag] + : adlins[n]; + } + unsigned GetAdlMetaNumber(unsigned midiins) + { + return (AdlBank == ~0u) ? + (midiins | DynamicMetaInstrumentTag) + : banks[AdlBank][midiins]; + } + const adldata& GetAdlIns(unsigned short insno) + { + return (insno & DynamicInstrumentTag) + ? dynamic_instruments[insno & ~DynamicInstrumentTag] + : adl[insno]; + } + public: unsigned int NumCards; unsigned int AdlBank; @@ -135,6 +168,16 @@ public: bool HighVibratoMode; bool AdlPercussionMode; bool ScaleModulators; + bool LogarithmicVolumes; + OPL3() : + NumCards(1), + AdlBank(0), + NumFourOps(0), + HighTremoloMode(false), + HighVibratoMode(false), + AdlPercussionMode(false), + LogarithmicVolumes(false) + {} std::vector four_op_category; // 1 = quad-master, 2 = quad-slave, 0 = regular // 3 = percussion BassDrum // 4 = percussion Snare @@ -184,15 +227,17 @@ public: { if(volume > 63) volume = 63; unsigned card = c/23, cc = c%23; - unsigned i = ins[c], o1 = Operators[cc*2], o2 = Operators[cc*2+1]; - unsigned x = adl[i].modulator_40, y = adl[i].carrier_40; - bool do_modulator; - bool do_carrier; + unsigned i = ins[c]; + unsigned o1 = Operators[cc*2+0]; + unsigned o2 = Operators[cc*2+1]; + + const adldata& adli = GetAdlIns(i); + unsigned x = adli.modulator_40, y = adli.carrier_40; unsigned mode = 1; // 2-op AM if(four_op_category[c] == 0 || four_op_category[c] == 3) { - mode = adl[i].feedconn & 1; // 2-op FM or 2-op AM + mode = adli.feedconn & 1; // 2-op FM or 2-op AM } else if(four_op_category[c] == 1 || four_op_category[c] == 2) { @@ -209,7 +254,7 @@ public: i1 = i; mode = 6; // 4-op xx-xx ops 3&4 } - mode += (adl[i0].feedconn & 1) + (adl[i1].feedconn & 1) * 2; + mode += (GetAdlIns(i0).feedconn & 1) + (GetAdlIns(i1).feedconn & 1) * 2; } static const bool do_ops[10][2] = { { false, true }, /* 2 op FM */ @@ -224,8 +269,8 @@ public: { true, true } /* 4 op AM-AM ops 3&4 */ }; - do_modulator = (ScaleModulators==1) ? true : do_ops[ mode ][ 0 ]; - do_carrier = (ScaleModulators==1) ? true : do_ops[ mode ][ 1 ]; + bool do_modulator = do_ops[ mode ][ 0 ] || ScaleModulators; + bool do_carrier = do_ops[ mode ][ 1 ] || ScaleModulators; Poke(card, 0x40+o1, do_modulator ? (x|63) - volume + volume*(x&63)/63 : x); if(o2 != 0xFFF) @@ -239,39 +284,86 @@ public: } void Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127 { - // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) - Touch_Real(c, volume>8725 ? std::log(volume)*11.541561 + (0.5 - 104.22845) : 0); - // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) - //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); + if(LogarithmicVolumes) + { + Touch_Real(c, volume*127/(127*127*127)); + } + else + { + // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) + Touch_Real(c, volume>8725 ? std::log(volume)*11.541561 + (0.5 - 104.22845) : 0); + + // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) + //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); + } } void Patch(unsigned c, unsigned i) { unsigned card = c/23, cc = c%23; static const unsigned char data[4] = {0x20,0x60,0x80,0xE0}; ins[c] = i; - unsigned o1 = Operators[cc*2+0], o2 = Operators[cc*2+1]; - unsigned x = adl[i].modulator_E862, y = adl[i].carrier_E862; - for(unsigned a=0; a<4; ++a) + unsigned o1 = Operators[cc*2+0]; + unsigned o2 = Operators[cc*2+1]; + + const adldata& adli = GetAdlIns(i); + unsigned x = adli.modulator_E862, y = adli.carrier_E862; + for(unsigned a=0; a<4; ++a, x>>=8, y>>=8) { - Poke(card, data[a]+o1, x&0xFF); x>>=8; + Poke(card, data[a]+o1, x&0xFF); if(o2 != 0xFFF) - Poke(card, data[a]+o2, y&0xFF); y>>=8; + Poke(card, data[a]+o2, y&0xFF); } } void Pan(unsigned c, unsigned value) { unsigned card = c/23, cc = c%23; if(Channels[cc] != 0xFFF) - Poke(card, 0xC0 + Channels[cc], adl[ins[c]].feedconn | value); + Poke(card, 0xC0 + Channels[cc], GetAdlIns(ins[c]).feedconn | value); } void Silence() // Silence all OPL channels. { for(unsigned c=0; cPCM_RATE); - for(unsigned a=0; a< 18; ++a) Poke(card, 0xB0+Channels[a], 0x00); for(unsigned a=0; a< sizeof(data)/sizeof(*data); a+=2) Poke(card, data[a], data[a+1]); @@ -306,7 +397,7 @@ public: } // Mark all channels that are reserved for four-operator function - if(AdlPercussionMode==1) + if(AdlPercussionMode == 1) for(unsigned a=0; a track; Position(): began(false), wait(0.0), track() { } - }; - Position CurrentPosition, LoopBeginPosition, trackBeginPosition; - + } CurrentPosition, LoopBeginPosition, trackBeginPosition; std::map devices; std::map current_device; @@ -465,6 +554,7 @@ class MIDIplay activenotes() { } }; std::vector Ch; + bool cmf_percussion_mode = false; // Additional information about AdLib channels struct AdlChannel @@ -515,6 +605,22 @@ class MIDIplay std::vector< std::vector > TrackData; public: + MIDIplay(): + cmf_percussion_mode(false), + config(nullptr), + trackStart(false), + loopStart(false), + loopEnd(false), + loopStart_passed(false), + invalidLoop(false), + loopStart_hit(false) + { + devices.clear(); + } + ~MIDIplay() + {} + + ADL_MIDIPlayer* config; std::string musTitle; fraction InvDeltaTicks, Tempo; bool trackStart, @@ -525,7 +631,7 @@ public: loopStart_hit /*loopStart entry was hited in previous tick*/; OPL3 opl; public: - static unsigned long ReadBEInt(const void* buffer, unsigned nbytes) + static unsigned long ReadBEint(const void* buffer, unsigned nbytes) { unsigned long result=0; const unsigned char* data = (const unsigned char*) buffer; @@ -533,6 +639,14 @@ public: result = (result << 8) + data[n]; return result; } + static unsigned long ReadLEint(const void* buffer, unsigned nbytes) + { + unsigned long result=0; + const unsigned char* data = (const unsigned char*) buffer; + for(unsigned n=0; n mp_size) mp_tell = mp_size; @@ -695,30 +809,117 @@ public: int fsize; qqq(fsize); //std::FILE* fr = std::fopen(filename.c_str(), "rb"); - if(!fr.isValid()) { return false; } - char HeaderBuf[4+4+2+2+2]=""; + if(!fr.isValid()) { ADLMIDI_ErrorString = "Invalid data stream!"; return false; } + const unsigned HeaderSize = 4+4+2+2+2; // 14 + char HeaderBuf[HeaderSize]=""; riffskip:; - fsize=fr.read(HeaderBuf, 1, 4+4+2+2+2); + fsize=fr.read(HeaderBuf, 1, HeaderSize); if(std::memcmp(HeaderBuf, "RIFF", 4) == 0) { fr.seek(6, SEEK_CUR); goto riffskip; } size_t DeltaTicks=192, TrackCount=1; - bool is_GMF = false, is_MUS = false, is_IMF = false; + config->stored_samples = 0; + config->backup_samples_size = 0; + opl.AdlPercussionMode = config->AdlPercussionMode; + opl.HighTremoloMode = config->HighTremoloMode; + opl.HighVibratoMode = config->HighVibratoMode; + opl.ScaleModulators = config->ScaleModulators; + opl.LogarithmicVolumes = config->LogarithmicVolumes; + opl.NumCards = config->NumCards; + opl.NumFourOps = config->NumFourOps; + cmf_percussion_mode = false; + + opl.Reset(); + + trackStart = true; + loopStart = true; + loopStart_passed = false; + invalidLoop = false; + loopStart_hit = false; + + bool is_GMF = false; // GMD/MUS files (ScummVM) + bool is_MUS = false; // MUS/DMX files (Doom) + bool is_IMF = false; // IMF + bool is_CMF = false; // Creative Music format (CMF/CTMF) //std::vector MUS_instrumentList; - if(std::memcmp(HeaderBuf, "GMF\1", 4) == 0) + if(std::memcmp(HeaderBuf, "GMF\x1", 4) == 0) { // GMD/MUS files (ScummVM) - fr.seek(7-(4+4+2+2+2), SEEK_CUR); + fr.seek(7-(HeaderSize), SEEK_CUR); is_GMF = true; } else if(std::memcmp(HeaderBuf, "MUS\x1A", 4) == 0) { // MUS/DMX files (Doom) - fr.seek(8-(4+4+2+2+2), SEEK_CUR); + unsigned start = ReadLEint(HeaderBuf+6, 2); is_MUS = true; - unsigned start = fr.getc(); start += (fr.getc() << 8); - fr.seek(-8+start, SEEK_CUR); + fr.seek(start, SEEK_SET); + } + else if(std::memcmp(HeaderBuf, "CTMF", 4) == 0) + { + opl.dynamic_instruments.clear(); + opl.dynamic_metainstruments.clear(); + // Creative Music Format (CMF). + // When playing CTMF files, use the following commandline: + // adlmidi song8.ctmf -p -v 1 1 0 + // i.e. enable percussion mode, deeper vibrato, and use only 1 card. + + is_CMF = true; + //unsigned version = ReadLEint(HeaderBuf+4, 2); + unsigned ins_start = ReadLEint(HeaderBuf+6, 2); + unsigned mus_start = ReadLEint(HeaderBuf+8, 2); + //unsigned deltas = ReadLEint(HeaderBuf+10, 2); + unsigned ticks = ReadLEint(HeaderBuf+12, 2); + // Read title, author, remarks start offsets in file + fr.read(HeaderBuf, 1, 6); + //unsigned long notes_starts[3] = {ReadLEint(HeaderBuf+0,2),ReadLEint(HeaderBuf+0,4),ReadLEint(HeaderBuf+0,6)}; + fr.seek(16, SEEK_CUR); // Skip the channels-in-use table + fr.read(HeaderBuf, 1, 4); + unsigned ins_count = ReadLEint(HeaderBuf+0, 2);//, basictempo = ReadLEint(HeaderBuf+2, 2); + fr.seek(ins_start, SEEK_SET); + //std::printf("%u instruments\n", ins_count); + for(unsigned i=0; i= Ch[MidCh].vibdelay) @@ -1115,7 +1313,7 @@ private: std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length ); CurrentPosition.track[tk].ptr += length; if(evtype == 0x2F) { CurrentPosition.track[tk].status = -1; return; } - if(evtype == 0x51) { Tempo = InvDeltaTicks * fraction( (long) ReadBEInt(data.data(), data.size())); return; } + if(evtype == 0x51) { Tempo = InvDeltaTicks * fraction( (long) ReadBEint(data.data(), data.size())); return; } if(evtype == 6) { for(size_t i=0;i missing_warnings; - if(!missing_warnings.count(midiins) && (adlins[meta].flags & adlinsdata::Flag_NoSound)) + if(!missing_warnings.count(midiins) && (ains.flags & adlinsdata::Flag_NoSound)) { //UI.PrintLn("[%i]Playing missing instrument %i", MidCh, midiins); missing_warnings.insert(midiins); @@ -1268,7 +1469,12 @@ private: // Only use regular channels int expected_mode = 0; if(opl.AdlPercussionMode==1) - expected_mode = PercussionMap[midiins & 0xFF]; + { + if(cmf_percussion_mode) + expected_mode = MidCh < 11 ? 0 : (3+MidCh-11); // CMF + else + expected_mode = PercussionMap[midiins & 0xFF]; + } if(opl.four_op_category[a] != expected_mode) continue; } @@ -1416,12 +1622,13 @@ private: case 113: break; // Related to pitch-bender, used by missimp.mid in Duke3D case 6: SetRPN(MidCh, value, true); break; case 38: SetRPN(MidCh, value, false); break; + case 103: cmf_percussion_mode = value; break; // CMF (ctrl 0x67) rhythm mode case 111://LoopStart unofficial controller if(!invalidLoop) { loopStart = true; loopStart_passed=true; - } break; + } break; default: break; //UI.PrintLn("Ctrl %d <- %d (ch %u)", ctrlno, value, MidCh); } @@ -1889,6 +2096,7 @@ ADLMIDI_EXPORT struct ADL_MIDIPlayer* adl_init(long sample_rate) _device->HighTremoloMode = 0; _device->HighVibratoMode = 0; _device->AdlPercussionMode = 0; + _device->LogarithmicVolumes = 0; _device->QuitFlag = 0; _device->SkipForward = 0; _device->QuitWithoutLooping = 0; @@ -1900,17 +2108,19 @@ ADLMIDI_EXPORT struct ADL_MIDIPlayer* adl_init(long sample_rate) _device->stored_samples=0; _device->backup_samples_size=0; - - _device->adl_midiPlayer = (void*)new MIDIplay; - ((MIDIplay*)_device->adl_midiPlayer)->opl._parent=_device; - ((MIDIplay*)_device->adl_midiPlayer)->opl.NumCards=_device->NumCards; - ((MIDIplay*)_device->adl_midiPlayer)->opl.AdlBank=_device->AdlBank; - ((MIDIplay*)_device->adl_midiPlayer)->opl.NumFourOps=_device->NumFourOps; - ((MIDIplay*)_device->adl_midiPlayer)->opl.HighTremoloMode=(bool)_device->HighTremoloMode; - ((MIDIplay*)_device->adl_midiPlayer)->opl.HighVibratoMode=(bool)_device->HighVibratoMode; - ((MIDIplay*)_device->adl_midiPlayer)->opl.AdlPercussionMode=(bool)_device->AdlPercussionMode; - ((MIDIplay*)_device->adl_midiPlayer)->opl.ScaleModulators=(bool)_device->ScaleModulators; - ((MIDIplay*)(_device->adl_midiPlayer))->ChooseDevice(""); + MIDIplay* player = new MIDIplay; + _device->adl_midiPlayer = player; + player->config = _device; + player->opl._parent = _device; + player->opl.NumCards = _device->NumCards; + player->opl.AdlBank = _device->AdlBank; + player->opl.NumFourOps = _device->NumFourOps; + player->opl.LogarithmicVolumes = (bool)_device->LogarithmicVolumes; + player->opl.HighTremoloMode = (bool)_device->HighTremoloMode; + player->opl.HighVibratoMode = (bool)_device->HighVibratoMode; + player->opl.AdlPercussionMode = (bool)_device->AdlPercussionMode; + player->opl.ScaleModulators = (bool)_device->ScaleModulators; + player->ChooseDevice("none"); adlRefreshNumCards(_device); return _device; } @@ -1953,6 +2163,11 @@ ADLMIDI_EXPORT int adl_getBanksCount() return maxAdlBanks(); } +ADLMIDI_EXPORT const char * const* adl_getBankNames() +{ + return banknames; +} + ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4) { device->NumFourOps = ops4; @@ -2002,15 +2217,24 @@ ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn) device->QuitWithoutLooping=(int)(!(bool)loopEn); } +ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer* device, int logvol) +{ + if(!device) return; + device->LogarithmicVolumes = logvol; + ((MIDIplay*)device->adl_midiPlayer)->opl.LogarithmicVolumes = (bool)device->LogarithmicVolumes; +} + ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, char *filePath) { + ADLMIDI_ErrorString.clear(); if(device && device->adl_midiPlayer) { device->stored_samples=0; device->backup_samples_size=0; if(!((MIDIplay *)device->adl_midiPlayer)->LoadMIDI(filePath)) { - ADLMIDI_ErrorString="ADL MIDI: Can't load file"; + if(ADLMIDI_ErrorString.empty()) + ADLMIDI_ErrorString="ADL MIDI: Can't load file"; return -1; } else return 0; } @@ -2020,13 +2244,15 @@ ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, char *filePath) ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer* device, void *mem, long size) { + ADLMIDI_ErrorString.clear(); if(device && device->adl_midiPlayer) { device->stored_samples=0; device->backup_samples_size=0; if(!((MIDIplay *)device->adl_midiPlayer)->LoadMIDI(mem, size)) { - ADLMIDI_ErrorString="ADL MIDI: Can't load data from memory"; + if(ADLMIDI_ErrorString.empty()) + ADLMIDI_ErrorString="ADL MIDI: Can't load data from memory"; return -1; } else return 0; } diff --git a/src/adlmidi.h b/src/adlmidi.h index 23dd65c..c9aaa3e 100644 --- a/src/adlmidi.h +++ b/src/adlmidi.h @@ -35,6 +35,7 @@ struct ADL_MIDIPlayer { unsigned int HighTremoloMode; unsigned int HighVibratoMode; unsigned int AdlPercussionMode; + unsigned int LogarithmicVolumes; unsigned int QuitFlag; unsigned int SkipForward; unsigned int QuitWithoutLooping; @@ -66,6 +67,9 @@ extern int adl_setBank(struct ADL_MIDIPlayer* device, int bank); /* Returns total number of available banks */ extern int adl_getBanksCount(); +/* Returns pointer to array of names of every bank */ +extern const char * const* adl_getBankNames(); + /*Sets number of 4-chan operators*/ extern int adl_setNumFourOpsChn(struct ADL_MIDIPlayer*device, int ops4); @@ -84,6 +88,9 @@ extern void adl_setScaleModulators(struct ADL_MIDIPlayer* device, int smod); /*Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part)*/ extern void adl_setLoopEnabled(struct ADL_MIDIPlayer* device, int loopEn); +/*Enable or disable Logariphmic volume changer */ +extern void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer* device, int logvol); + /*Returns string which contains last error message*/ extern const char* adl_errorString(); diff --git a/src/gen_adldata/gen_adldata.cc b/src/gen_adldata/gen_adldata.cc index 8e74309..c5e966f 100644 --- a/src/gen_adldata/gen_adldata.cc +++ b/src/gen_adldata/gen_adldata.cc @@ -1140,7 +1140,7 @@ int main() LoadJunglevision("fm_banks/op3_files/2x2.op3", 68, "2x2byJAN"); - LoadTMB("fm_banks/tmb_files/blood.tmb", 69, "apgblood"); + 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"); -- cgit v1.2.3