aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/adlmidi_load.cpp97
-rw-r--r--src/adlmidi_midiplay.cpp27
-rw-r--r--src/adlmidi_opl3.cpp43
-rw-r--r--src/adlmidi_private.hpp13
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,