diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/adlmidi_load.cpp | 97 | ||||
-rw-r--r-- | src/adlmidi_midiplay.cpp | 27 | ||||
-rw-r--r-- | src/adlmidi_opl3.cpp | 43 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 13 |
4 files changed, 119 insertions, 61 deletions
diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp index 3f2ad8c..c4ca816 100644 --- a/src/adlmidi_load.cpp +++ b/src/adlmidi_load.cpp @@ -369,6 +369,7 @@ bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr) opl.HighVibratoMode = m_setup.HighVibratoMode; opl.ScaleModulators = m_setup.ScaleModulators; opl.LogarithmicVolumes = m_setup.LogarithmicVolumes; + opl.CartoonersVolumes = false; opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(m_setup.VolumeModel)); if(m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO) @@ -416,6 +417,7 @@ bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr) //bool is_MUS = false; // MUS/DMX files (Doom) bool is_IMF = false; // IMF bool is_CMF = false; // Creative Music format (CMF/CTMF) + bool is_RSXX = false; // RSXX, such as Cartooners const size_t HeaderSize = 4 + 4 + 2 + 2 + 2; // 14 char HeaderBuf[HeaderSize] = ""; @@ -576,52 +578,70 @@ riffskip: } else { - // Try parsing as an IMF file + // Try to identify RSXX format + if(HeaderBuf[0] == 0x7D) { - uint8_t raw[4]; - size_t end = static_cast<uint8_t>(HeaderBuf[0]) + 256 * static_cast<uint8_t>(HeaderBuf[1]); - - if(!end || (end & 3)) - goto not_imf; - - size_t backup_pos = fr.tell(); - int64_t sum1 = 0, sum2 = 0; - fr.seek(2, SEEK_SET); - - for(unsigned n = 0; n < 42; ++n) + fr.seek(0x6D, SEEK_SET); + fr.read(HeaderBuf, 6, 1); + if(std::memcmp(HeaderBuf, "rsxx}u", 6) == 0) { - if(fr.read(raw, 1, 4) != 4) - break; - int64_t value1 = raw[0]; - value1 += raw[1] << 8; - sum1 += value1; - int64_t value2 = raw[2]; - value2 += raw[3] << 8; - sum2 += value2; + is_RSXX = true; + fr.seek(0x7D, SEEK_SET); + TrackCount = 1; + DeltaTicks = 60; + opl.LogarithmicVolumes = true; + opl.CartoonersVolumes = true; + opl.m_volumeScale = OPL3::VOLUME_CMF; } + } - fr.seek(static_cast<long>(backup_pos), SEEK_SET); - - if(sum1 > sum2) + // Try parsing as an IMF file + if(!is_RSXX) + { + do { - is_IMF = true; - DeltaTicks = 1; - } + uint8_t raw[4]; + size_t end = static_cast<uint8_t>(HeaderBuf[0]) + 256 * static_cast<uint8_t>(HeaderBuf[1]); + + if(!end || (end & 3)) + break; + + size_t backup_pos = fr.tell(); + int64_t sum1 = 0, sum2 = 0; + fr.seek(2, SEEK_SET); + + for(unsigned n = 0; n < 42; ++n) + { + if(fr.read(raw, 1, 4) != 4) + break; + int64_t value1 = raw[0]; + value1 += raw[1] << 8; + sum1 += value1; + int64_t value2 = raw[2]; + value2 += raw[3] << 8; + sum2 += value2; + } + + fr.seek(static_cast<long>(backup_pos), SEEK_SET); + + if(sum1 > sum2) + { + is_IMF = true; + DeltaTicks = 1; + } + } while(false); } - if(!is_IMF) + if(!is_IMF && !is_RSXX) { -not_imf: - if(std::memcmp(HeaderBuf, "MThd\0\0\0\6", 8) != 0) { -InvFmt: fr.close(); - ADLMIDI_ErrorString = fr._fileName + ": Invalid format\n"; + ADLMIDI_ErrorString = fr._fileName + ": Invalid format, Header signature is unknown!\n"; return false; } - /*size_t Fmt =*/ ReadBEint(HeaderBuf + 8, 2); + /*size_t Fmt = ReadBEint(HeaderBuf + 8, 2);*/ TrackCount = (size_t)ReadBEint(HeaderBuf + 10, 2); DeltaTicks = (size_t)ReadBEint(HeaderBuf + 12, 2); } @@ -691,7 +711,8 @@ InvFmt: } else { - if(is_GMF || is_CMF) // Take the rest of the file + // Take the rest of the file + if(is_GMF || is_CMF || is_RSXX) { size_t pos = fr.tell(); fr.seek(0, SEEK_END); @@ -710,7 +731,11 @@ InvFmt: { fsize = fr.read(HeaderBuf, 1, 8); if(std::memcmp(HeaderBuf, "MTrk", 4) != 0) - goto InvFmt; + { + fr.close(); + ADLMIDI_ErrorString = fr._fileName + ": Invalid format, MTrk signature is not found!\n"; + return false; + } TrackLength = (size_t)ReadBEint(HeaderBuf + 4, 4); } @@ -719,8 +744,10 @@ InvFmt: fsize = fr.read(&TrackData[tk][0], 1, TrackLength); totalGotten += fsize; - if(is_GMF /*|| is_MUS*/) // Note: CMF does include the track end tag. + if(is_GMF/*|| is_MUS*/) // Note: CMF does include the track end tag. TrackData[tk].insert(TrackData[tk].end(), EndTag + 0, EndTag + 4); + if(is_RSXX)//Finalize raw track data with a zero + TrackData[tk].push_back(0); //bool ok = false; //// Read next event time diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 458e1b5..32ac6c3 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -233,7 +233,9 @@ bool MIDIplay::buildTrackData() //Time delay that follows the first event in the track { MidiTrackRow evtPos; - evtPos.delay = ReadVarLenEx(&trackPtr, end, ok); + if(!opl.CartoonersVolumes) + evtPos.delay = ReadVarLenEx(&trackPtr, end, ok); + else ok = true; if(!ok) { int len = std::snprintf(error, 150, "buildTrackData: Can't read variable-length value at begin of track %d.\n", (int)tk); @@ -541,7 +543,8 @@ uint64_t MIDIplay::ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok) return 2; unsigned char byte = *((*ptr)++); result = (result << 7) + (byte & 0x7F); - if(!(byte & 0x80)) break; + if(!(byte & 0x80)) + break; } ok = true; @@ -679,7 +682,7 @@ void MIDIplay::realTime_ResetState() for(size_t ch = 0; ch < Ch.size(); ch++) { MIDIchannel &chan = Ch[ch]; - chan.volume = 100; + chan.volume = opl.CartoonersVolumes ? 127 : 100; chan.expression = 127; chan.panning = 0x30; chan.vibrato = 0; @@ -699,6 +702,18 @@ void MIDIplay::realTime_ResetState() bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) { + if((opl.CartoonersVolumes) && (velocity != 0)) + { + // Check if this is just a note after-touch + MIDIchannel::activenoteiterator i = Ch[channel].activenotes.find(note); + if(i != Ch[channel].activenotes.end()) + { + i->second.vol = velocity; + NoteUpdate(channel, i, Upd_Volume); + return false; + } + } + channel = channel % 16; NoteOff(channel, note); // On Note on, Keyoff the note first, just in case keyoff @@ -1417,8 +1432,10 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu if(ptr + 1 > end) { - errorString += "parseEvent: Can't read event type byte - Unexpected end of track data.\n"; - evt.isValid = 0; + //errorString += "parseEvent: Can't read event type byte - Unexpected end of track data.\n"; + //evt.isValid = 0; + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = MidiEvent::ST_ENDTRACK; return evt; } diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp index 4878233..4d38e3a 100644 --- a/src/adlmidi_opl3.cpp +++ b/src/adlmidi_opl3.cpp @@ -44,7 +44,7 @@ int maxAdlBanks() } const unsigned short banks[][256] = {{0}}; -const char* const banknames[] = {"<Embedded banks are disabled>"}; +const char *const banknames[] = {"<Embedded banks are disabled>"}; #endif static const unsigned short Operators[23 * 2] = @@ -143,6 +143,7 @@ OPL3::OPL3() : HighVibratoMode(false), AdlPercussionMode(false), LogarithmicVolumes(false), + CartoonersVolumes(false), m_volumeScale(VOLUME_Generic) {} @@ -260,12 +261,21 @@ void OPL3::Touch_Real(unsigned c, unsigned volume) { false, true }, /* 4 op FM-AM ops 3&4 */ { true, true } /* 4 op AM-AM ops 3&4 */ }; - 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) - Poke(card, 0x40 + o2, do_carrier ? (y | 63) - volume + volume * (y & 63) / 63 : y); + if(CartoonersVolumes) + { + Poke(card, 0x40 + o1, x); + if(o2 != 0xFFF) + Poke(card, 0x40 + o2, y - volume / 2); + } + else + { + 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) + Poke(card, 0x40 + o2, do_carrier ? (y | 63) - volume + volume * (y & 63) / 63 : y); + } // Correct formula (ST3, AdPlug): // 63-((63-(instrvol))/63)*chanvol @@ -412,7 +422,7 @@ void OPL3::Reset(unsigned long PCM_RATE) DBOPL::Handler emptyChip; //Constructors inside are will initialize necessary fields #else _opl3_chip emptyChip; - memset(&emptyChip, 0, sizeof(_opl3_chip)); + std::memset(&emptyChip, 0, sizeof(_opl3_chip)); #endif cards.clear(); ins.clear(); @@ -427,11 +437,8 @@ void OPL3::Reset(unsigned long PCM_RATE) for(unsigned p = 0, a = 0; a < NumCards; ++a) { - for(unsigned b = 0; b < 18; ++b) - four_op_category[p++] = 0; - - for(unsigned b = 0; b < 5; ++b) - four_op_category[p++] = 8; + for(unsigned b = 0; b < 18; ++b) four_op_category[p++] = 0; + for(unsigned b = 0; b < 5; ++b) four_op_category[p++] = 8; } static const uint16_t data[] = @@ -451,10 +458,8 @@ void OPL3::Reset(unsigned long PCM_RATE) #endif for(unsigned a = 0; a < 18; ++a) Poke(card, 0xB0 + Channels[a], 0x00); - for(unsigned a = 0; a < sizeof(data) / sizeof(*data); a += 2) PokeN(card, data[a], static_cast<uint8_t>(data[a + 1])); - Poke(card, 0x0BD, regBD[card] = (HighTremoloMode * 0x80 + HighVibratoMode * 0x40 + AdlPercussionMode * 0x20)); @@ -466,15 +471,13 @@ void OPL3::Reset(unsigned long PCM_RATE) // Mark all channels that are reserved for four-operator function if(AdlPercussionMode == 1) + { for(unsigned a = 0; a < NumCards; ++a) { - for(unsigned b = 0; b < 5; ++b) - four_op_category[a * 23 + 18 + b] = static_cast<char>(b + 3); - - for(unsigned b = 0; b < 3; ++b) - four_op_category[a * 23 + 6 + b] = 8; + for(unsigned b = 0; b < 5; ++b) four_op_category[a * 23 + 18 + b] = static_cast<char>(b + 3); + for(unsigned b = 0; b < 3; ++b) four_op_category[a * 23 + 6 + b] = 8; } - + } unsigned nextfour = 0; for(unsigned a = 0; a < NumFourOps; ++a) diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index 7defa1e..3229f62 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -140,16 +140,27 @@ private: const adldata &GetAdlIns(unsigned short insno); public: + //! Total number of running concurrent emulated chips unsigned int NumCards; + //! Currently running embedded bank number. "~0" means usign of the custom bank. unsigned int AdlBank; + //! Total number of needed four-operator channels in all running chips unsigned int NumFourOps; + //! Turn global Deep Tremolo mode on bool HighTremoloMode; + //! Turn global Deep Vibrato mode on bool HighVibratoMode; + //! Use AdLib percussion mode bool AdlPercussionMode; + //! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too. bool ScaleModulators; - + //! Required to play CMF files. Can be turned on by using of "CMF" volume model bool LogarithmicVolumes; + //! Required to play EA-MUS files + bool CartoonersVolumes; + //! Just a padding. Reserved. char ___padding2[3]; + //! Volume models enum enum VolumesScale { VOLUME_Generic, |