aboutsummaryrefslogtreecommitdiff
path: root/src/adlmidi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/adlmidi.cpp')
-rw-r--r--src/adlmidi.cpp394
1 files changed, 310 insertions, 84 deletions
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<unsigned short> ins; // index to adl[], cached, needed by Touch()
std::vector<unsigned char> pit; // value poked to B0, cached, needed by NoteOff)(
std::vector<unsigned char> regBD;
+
+ std::vector<adlinsdata> dynamic_metainstruments; // Replaces adlins[] when CMF file
+ std::vector<adldata> 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<char> 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; c<NumChannels; ++c) { NoteOff(c); Touch_Real(c,0); }
}
+
+ void updateFlags()
+ {
+ unsigned fours = NumFourOps;
+ for(unsigned card=0; card<NumCards; ++card)
+ {
+ Poke(card, 0x0BD, regBD[card] = (HighTremoloMode*0x80
+ + HighVibratoMode*0x40
+ + AdlPercussionMode*0x20) );
+ unsigned fours_this_card = std::min(fours, 6u);
+ Poke(card, 0x104, (1 << fours_this_card) - 1);
+ fours -= fours_this_card;
+ }
+
+ // 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] = 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)
+ {
+ four_op_category[nextfour ] = 1;
+ four_op_category[nextfour+3] = 2;
+ switch(a % 6)
+ {
+ case 0: case 1: nextfour += 1; break;
+ case 2: nextfour += 9-2; break;
+ case 3: case 4: nextfour += 1; break;
+ case 5: nextfour += 23-9-2; break;
+ }
+ }
+ }
void Reset()
{
+ LogarithmicVolumes = false;
cards.resize(NumCards);
-
NumChannels = NumCards * 23;
ins.resize(NumChannels, 189);
pit.resize(NumChannels, 0);
@@ -292,7 +384,6 @@ public:
for(unsigned card=0; card<NumCards; ++card)
{
cards[card].Init(_parent->PCM_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<NumCards; ++a)
{
for(unsigned b=0; b<5; ++b) four_op_category[a*23 + 18 + b] = b+3;
@@ -415,9 +506,7 @@ class MIDIplay
std::vector<TrackInfo> track;
Position(): began(false), wait(0.0), track() { }
- };
- Position CurrentPosition, LoopBeginPosition, trackBeginPosition;
-
+ } CurrentPosition, LoopBeginPosition, trackBeginPosition;
std::map<std::string, unsigned> devices;
std::map<unsigned/*track*/, unsigned/*channel begin index*/> current_device;
@@ -465,6 +554,7 @@ class MIDIplay
activenotes() { }
};
std::vector<MIDIchannel> Ch;
+ bool cmf_percussion_mode = false;
// Additional information about AdLib channels
struct AdlChannel
@@ -515,6 +605,22 @@ class MIDIplay
std::vector< std::vector<unsigned char> > 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<long> 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<nbytes; ++n)
+ result = result + (data[n] << (n*8));
+ return result;
+ }
unsigned long ReadVarLen(unsigned tk)
{
unsigned long result = 0;
@@ -596,8 +710,8 @@ public:
switch(rel_to)
{
case SET: mp_tell = pos; break;
- case END: mp_tell = mp_size-pos; break;
- case CUR: mp_tell+= pos; break;
+ case END: mp_tell = mp_size - pos; break;
+ case CUR: mp_tell = mp_tell + pos; break;
}
if(mp_tell > 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<unsigned char> 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<ins_count; ++i)
+ {
+ unsigned char InsData[16];
+ fr.read(InsData, 1, 16);
+ /*std::printf("Ins %3u: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ i, InsData[0],InsData[1],InsData[2],InsData[3], InsData[4],InsData[5],InsData[6],InsData[7],
+ InsData[8],InsData[9],InsData[10],InsData[11], InsData[12],InsData[13],InsData[14],InsData[15]);*/
+ struct adldata adl;
+ struct adlinsdata adlins;
+ adl.modulator_E862 =
+ ((uint(InsData[8]&0x07) << 24)&0xFF000000)//WaveForm
+ | ((uint(InsData[6]) << 16)&0x00FF0000)//Sustain/Release
+ | ((uint(InsData[4]) << 8)&0x0000FF00)//Attack/Decay
+ | ((uint(InsData[0]) << 0)&0x000000FF);//MultKEVA
+ adl.carrier_E862 =
+ ((uint(InsData[9]&0x07) << 24)&0xFF000000)//WaveForm
+ | ((uint(InsData[7]) << 16)&0x00FF0000)//Sustain/Release
+ | ((uint(InsData[5]) << 8)&0x0000FF00)//Attack/Decay
+ | ((uint(InsData[1]) << 0)&0x000000FF);//MultKEVA
+ adl.modulator_40 = InsData[2];
+ adl.carrier_40 = InsData[3];
+ adl.feedconn = InsData[10]&0x0F;
+ adl.finetune = 0;
+
+ adlins.adlno1 = opl.dynamic_instruments.size() | opl.DynamicInstrumentTag;
+ adlins.adlno2 = adlins.adlno1;
+ adlins.ms_sound_kon = 1000;
+ adlins.ms_sound_koff = 500;
+ adlins.tone = 0;
+ adlins.flags = 0;
+ adlins.fine_tune = 0.0;
+ opl.dynamic_metainstruments.push_back(adlins);
+ opl.dynamic_instruments.push_back(adl);
+ }
+ fr.seek(mus_start, SEEK_SET);
+ TrackCount = 1;
+ DeltaTicks = ticks;
+ opl.AdlBank = ~0u; // Ignore AdlBank number, use dynamic banks instead
+ //std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo);
+ opl.LogarithmicVolumes = true;
+ opl.AdlPercussionMode = true;
}
else
{
@@ -752,9 +953,9 @@ public:
ADLMIDI_ErrorString=fr._fileName+": Invalid format\n";
return false;
}
- /*size_t Fmt =*/ ReadBEInt(HeaderBuf+8, 2);
- TrackCount = ReadBEInt(HeaderBuf+10, 2);
- DeltaTicks = ReadBEInt(HeaderBuf+12, 2);
+ /*size_t Fmt =*/ ReadBEint(HeaderBuf+8, 2);
+ TrackCount = ReadBEint(HeaderBuf+10, 2);
+ DeltaTicks = ReadBEint(HeaderBuf+12, 2);
}
}
TrackData.resize(TrackCount);
@@ -814,14 +1015,14 @@ public:
}
else
{
- if(is_GMF)
+ if(is_GMF || is_CMF) // Take the rest of the file
{
long pos = fr.tell();
fr.seek(0, SEEK_END);
TrackLength = fr.tell() - pos;
fr.seek(pos, SEEK_SET);
}
- else if(is_MUS)
+ else if(is_MUS) // Read TrackLength from file position 4
{
long pos = fr.tell();
fr.seek(4, SEEK_SET);
@@ -832,13 +1033,13 @@ public:
{
fsize=fr.read(HeaderBuf, 1, 8);
if(std::memcmp(HeaderBuf, "MTrk", 4) != 0) goto InvFmt;
- TrackLength = ReadBEInt(HeaderBuf+4, 4);
+ TrackLength = ReadBEint(HeaderBuf+4, 4);
}
// Read track data
TrackData[tk].resize(TrackLength);
fsize=fr.read(&TrackData[tk][0], 1, TrackLength);
- totalGotten+=fsize;
- if(is_GMF || is_MUS)
+ totalGotten += fsize;
+ if(is_GMF || is_MUS) // Note: CMF does include the track end tag.
{
TrackData[tk].insert(TrackData[tk].end(), EndTag+0, EndTag+4);
}
@@ -846,18 +1047,14 @@ public:
CurrentPosition.track[tk].delay = ReadVarLen(tk);
}
}
+ for(size_t tk = 0; tk < TrackCount; ++tk)
+ totalGotten+= TrackData[tk].size();
if(totalGotten==0)
{
- ADLMIDI_ErrorString=fr._fileName+": Empty track data\n";
+ ADLMIDI_ErrorString=fr._fileName+": Empty track data";
return false;
}
- trackStart = true;
- loopStart = true;
- loopStart_passed = false;
- invalidLoop = false;
- loopStart_hit = false;
-
opl.Reset(); // Reset AdLib
//opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously)
ch.clear();
@@ -913,6 +1110,7 @@ private:
const int vol = info.vol;
//const int midiins = info.midiins;
const int insmeta = info.insmeta;
+ const adlinsdata& ains = opl.GetAdlMetaIns(insmeta);
AdlChannel::Location my_loc;
my_loc.MidCh = MidCh;
@@ -934,7 +1132,7 @@ private:
AdlChannel::LocationData& d = ch[c].users[my_loc];
d.sustained = false; // inserts if necessary
d.vibdelay = 0;
- d.kon_time_until_neglible = adlins[insmeta].ms_sound_kon;
+ d.kon_time_until_neglible = ains.ms_sound_kon;
d.ins = ins;
}
}
@@ -961,7 +1159,7 @@ private:
{
opl.NoteOff(c);
ch[c].koff_time_until_neglible =
- adlins[insmeta].ms_sound_koff;
+ ains.ms_sound_koff;
}
}
else
@@ -998,12 +1196,12 @@ private:
// Don't bend a sustained note
if(!d.sustained)
{
- double bend = Ch[MidCh].bend + adl[ins].finetune;
+ double bend = Ch[MidCh].bend + opl.GetAdlIns(ins).finetune;
double phase = 0.0;
- if((adlins[insmeta].flags & adlinsdata::Flag_Pseudo4op) && ins == adlins[insmeta].adlno2)
+ if((ains.flags & adlinsdata::Flag_Pseudo4op) && ins == ains.adlno2)
{
- phase = adlins[insmeta].fine_tune;//0.125; // Detune the note slightly (this is what Doom does)
+ phase = ains.fine_tune;//0.125; // Detune the note slightly (this is what Doom does)
}
if(Ch[MidCh].vibrato && d.vibdelay >= 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>( (long) ReadBEInt(data.data(), data.size())); return; }
+ if(evtype == 0x51) { Tempo = InvDeltaTicks * fraction<long>( (long) ReadBEint(data.data(), data.size())); return; }
if(evtype == 6)
{
for(size_t i=0;i<data.size();i++)
@@ -1223,24 +1421,27 @@ private:
}
}
- int meta = banks[opl.AdlBank][midiins];
+ //int meta = banks[opl.AdlBank][midiins];
+ const unsigned meta = opl.GetAdlMetaNumber(midiins);
+ const adlinsdata& ains = opl.GetAdlMetaIns(meta);
+
int tone = note;
- if(adlins[meta].tone)
+ if(ains.tone)
{
- if(adlins[meta].tone < 20)
- tone += adlins[meta].tone;
- else if(adlins[meta].tone < 128)
- tone = adlins[meta].tone;
+ if(ains.tone < 20)
+ tone += ains.tone;
+ else if(ains.tone < 128)
+ tone = ains.tone;
else
- tone -= adlins[meta].tone-128;
+ tone -= ains.tone-128;
}
- int i[2] = { adlins[meta].adlno1, adlins[meta].adlno2 };
- bool pseudo_4op = adlins[meta].flags & adlinsdata::Flag_Pseudo4op;
+ int i[2] = { ains.adlno1, ains.adlno2 };
+ bool pseudo_4op = ains.flags & adlinsdata::Flag_Pseudo4op;
if((opl.AdlPercussionMode==1) && PercussionMap[midiins & 0xFF]) i[1] = i[0];
static std::set<unsigned char> 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;
}