Restructured MIDI channel allocation architecture.

Adlib no longer suffers from 16-channel MIDI restrictions.
Fixes a regression in the MI2 intro music under Adlib.

svn-id: r5721
This commit is contained in:
Jamieson Christian 2002-11-26 16:54:58 +00:00
parent 4544d24fe2
commit 973164a741
5 changed files with 494 additions and 248 deletions

View file

@ -61,10 +61,11 @@ struct Instrument {
byte duration;
};
struct AdlibPart {
byte _chan;
AdlibPart *_prev, *_next;
MidiDriver_ADLIB *_drv;
class AdlibPart : public MidiChannel {
friend MidiDriver_ADLIB;
private:
// AdlibPart *_prev, *_next;
MidiChannelAdl *_mc;
int16 _pitchbend;
byte _pitchbend_factor;
@ -76,6 +77,39 @@ struct AdlibPart {
byte _program;
byte _pri_eff;
Instrument _part_instr;
private:
MidiDriver_ADLIB *_owner;
bool _allocated;
byte _channel;
void init (MidiDriver_ADLIB *owner);
void allocate() { _allocated = true; }
public:
void release() { _allocated = false; }
// Regular messages
void noteOff (byte note);
void noteOn (byte note, byte velocity);
void programChange (byte program);
void pitchBend (int16 bend);
// Control Change messages
void controlChange (byte control, byte value);
void modulationWheel (byte value);
void volume (byte value);
void panPosition (byte value) { return; } // Not supported
void pitchBendFactor (byte value);
void detune (byte value);
void priority (byte value);
void sustain (bool value);
void effectLevel (byte value) { return; } // Not supported
void chorusLevel (byte value) { return; } // Not supported
void allNotesOff();
// SysEx messages
void sysEx_customInstrument (uint32 type, byte *instr);
};
struct Struct10 {
@ -459,6 +493,8 @@ typedef void TimerCallback (void *);
////////////////////////////////////////
class MidiDriver_ADLIB : public MidiDriver {
friend AdlibPart;
public:
MidiDriver_ADLIB();
@ -479,6 +515,9 @@ public:
#endif //_WIN32_WCE
}
MidiChannel *allocateChannel();
MidiChannel *getPercussionChannel() { return NULL; } // Percussion currently not supported
private:
int _mode;
@ -496,13 +535,15 @@ private:
int _next_tick;
uint16 curnote_table[9];
MidiChannelAdl _midi_channels[9];
AdlibPart _parts[16];
AdlibPart _parts[32];
void sysEx_customInstrument (AdlibPart *part, uint32 type, byte *instr);
void generate_samples(int16 *buf, int len);
void on_timer();
void part_set_instrument (AdlibPart *part, Instrument * instr);
void part_key_on (byte chan, byte note, byte velocity);
void part_key_off (byte chan, byte note);
void part_key_on (AdlibPart *part, byte note, byte velocity);
void part_key_off (AdlibPart *part, byte note);
void adlib_key_off(int chan);
void adlib_note_on(int chan, byte note, int mod);
@ -523,9 +564,9 @@ private:
void mc_off(MidiChannelAdl * mc);
static void link_mc (AdlibPart *part, MidiChannelAdl *mc);
static void mc_inc_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11);
static void mc_init_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11, byte flags,
InstrumentExtra * ie);
void mc_inc_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11);
void mc_init_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11, byte flags,
InstrumentExtra * ie);
static void struct10_init(Struct10 * s10, InstrumentExtra * ie);
static byte struct10_ontimer(Struct10 * s10, Struct11 * s11);
@ -538,16 +579,152 @@ private:
// MidiChannel method implementations
void AdlibPart::init (MidiDriver_ADLIB *owner) {
_owner = owner;
}
void AdlibPart::noteOff (byte note)
{
_owner->part_key_off (this, note);
}
void AdlibPart::noteOn (byte note, byte velocity)
{
_owner->part_key_on (this, note, velocity);
}
void AdlibPart::programChange (byte program)
{
if (program > 127) return;
uint i;
uint count = 0;
for (i = 0; i < ARRAYSIZE(map_gm_to_fm[0]); ++i)
count += map_gm_to_fm[program][i];
if (!count)
warning ("No Adlib instrument defined for GM program %d", (int) program);
_program = program;
_owner->part_set_instrument (this, (Instrument *) &map_gm_to_fm [program]);
}
void AdlibPart::pitchBend (int16 bend)
{
MidiChannelAdl *mc;
_pitchbend = bend;
for (mc = _mc; mc; mc = mc->_next) {
_owner->adlib_note_on(mc->_channel, mc->_note + _transpose_eff,
(_pitchbend * _pitchbend_factor >> 6) + _detune_eff);
}
}
void AdlibPart::controlChange (byte control, byte value)
{
switch (control) {
case 1: modulationWheel (value); break;
case 7: volume (value); break;
case 10: break; // Pan position. Not supported.
case 16: pitchBendFactor (value); break;
case 17: detune (value); break;
case 18: priority (value); break;
case 64: sustain (value > 0); break;
case 91: break; // Effects level. Not supported.
case 93: break; // Chorus level. Not supported.
case 123: allNotesOff(); break;
default:
warning ("Adlib: Unknown control change message %d", (int) control);
}
}
void AdlibPart::modulationWheel (byte value)
{
MidiChannelAdl *mc;
_modwheel = value;
for (mc = _mc; mc; mc = mc->_next) {
if (mc->_s10a.active && mc->_s11a.flag0x40)
mc->_s10a.modwheel = _modwheel >> 2;
if (mc->_s10b.active && mc->_s11b.flag0x40)
mc->_s10b.modwheel = _modwheel >> 2;
}
}
void AdlibPart::volume (byte value)
{
MidiChannelAdl *mc;
_vol_eff = value;
for (mc = _mc; mc; mc = mc->_next) {
_owner->adlib_set_param(mc->_channel, 0, volume_table[lookup_table[mc->_vol_2][_vol_eff >> 2]]);
if (mc->_twochan) {
_owner->adlib_set_param(mc->_channel, 13, volume_table[lookup_table[mc->_vol_1][_vol_eff >> 2]]);
}
}
}
void AdlibPart::pitchBendFactor (byte value)
{
MidiChannelAdl *mc;
_pitchbend_factor = value;
for (mc = _mc; mc; mc = mc->_next) {
_owner->adlib_note_on(mc->_channel, mc->_note + _transpose_eff,
(_pitchbend * _pitchbend_factor >> 6) + _detune_eff);
}
}
void AdlibPart::detune (byte value)
{
MidiChannelAdl *mc;
_detune_eff = value;
for (mc = _mc; mc; mc = mc->_next) {
_owner->adlib_note_on(mc->_channel, mc->_note + _transpose_eff,
(_pitchbend * _pitchbend_factor >> 6) + _detune_eff);
}
}
void AdlibPart::priority (byte value)
{
_pri_eff = value;
}
void AdlibPart::sustain (bool value)
{
MidiChannelAdl *mc;
_pedal = value;
if (!value) {
for (mc = _mc; mc; mc = mc->_next) {
if (mc->_waitforpedal)
_owner->mc_off(mc);
}
}
}
void AdlibPart::allNotesOff()
{
while (_mc)
_owner->mc_off (_mc);
}
void AdlibPart::sysEx_customInstrument (uint32 type, byte *instr)
{
_owner->sysEx_customInstrument (this, type, instr);
}
// MidiDriver method implementations
MidiDriver_ADLIB::MidiDriver_ADLIB()
{
uint i;
for (i = 0; i < ARRAYSIZE(_parts); ++i) {
_parts [i]._chan = i;
_parts [i]._prev = (i ? &_parts[i-1] : 0);
_parts [i]._next = ((i < ARRAYSIZE(_parts) - 1) ? &_parts[i+1] : 0);
_parts [i]._drv = this;
_parts[i].init (this);
}
}
@ -599,8 +776,6 @@ void MidiDriver_ADLIB::send (uint32 b)
if (_mode != MO_SIMPLE)
error("MidiDriver_ADLIB::send called but driver is not in simple mode");
MidiChannelAdl *mc;
//byte param3 = (byte) ((b >> 24) & 0xFF);
byte param2 = (byte) ((b >> 16) & 0xFF);
byte param1 = (byte) ((b >> 8) & 0xFF);
@ -610,112 +785,21 @@ void MidiDriver_ADLIB::send (uint32 b)
switch (cmd) {
case 0x80:// Note Off
part_key_off (chan, param1);
break;
part_key_off (part, param1); break;
case 0x90: // Note On
part_key_on (chan, param1, param2);
break;
part_key_on (part, param1, param2); break;
case 0xA0: // Aftertouch
// Not supported.
break;
break; // Not supported.
case 0xB0: // Control Change
switch (param1) {
case 01: // Modulation wheel
part->_modwheel = param2;
for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
if (mc->_s10a.active && mc->_s11a.flag0x40)
mc->_s10a.modwheel = part->_modwheel >> 2;
if (mc->_s10b.active && mc->_s11b.flag0x40)
mc->_s10b.modwheel = part->_modwheel >> 2;
}
break;
case 07: // Volume
part->_vol_eff = param2;
for (mc = part->_mc; mc; mc = mc->_next) {
adlib_set_param(mc->_channel, 0, volume_table[lookup_table[mc->_vol_2][part->_vol_eff >> 2]]);
if (mc->_twochan) {
adlib_set_param(mc->_channel, 13, volume_table[lookup_table[mc->_vol_1][part->_vol_eff >> 2]]);
}
}
break;
case 10: // Pan position
// Not supported in Adlib (OPL2)
break;
case 16: // Pitchbend factor
part->_pitchbend_factor = param2;
for (mc = part->_mc; mc; mc = mc->_next) {
adlib_note_on(mc->_channel, mc->_note + part->_transpose_eff,
(part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff);
}
break;
case 17: // GP slider 2 (detune)
part->_detune_eff = param2;
for (mc = part->_mc; mc; mc = mc->_next) {
adlib_note_on(mc->_channel, mc->_note + part->_transpose_eff,
(part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff);
}
break;
case 18: // GP slider 3 (priority)
part->_pri_eff = param2;
break;
case 64: // Sustain pedal
part->_pedal = (param2 > 0);
if (!part->_pedal) {
for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
if (mc->_waitforpedal)
mc_off(mc);
}
}
break;
case 91: // Effects level
// Not supported in Adlib (OPL2)
break;
case 93: // Chorus
// Not supported in Adlib (OPL2)
break;
case 123: // All Notes Off
while (part->_mc)
mc_off (part->_mc);
break;
default:
warning ("MidiDriver_ADLIB: Unknown control change message %d", param1);
}
break;
part->controlChange (param1, param2); break;
case 0xC0: // Program Change
if (chan != 9) {
if (!map_gm_to_fm [part->_program][0])
warning ("No Adlib instrument defined for GM program %d", (int) param1);
part->_program = param1;
part_set_instrument (&_parts[chan], (Instrument *) &map_gm_to_fm[part->_program]);
}
if (chan != 9)
part->programChange (param1);
break;
case 0xD0: // Channel Pressure
// Not supported.
break;
break; // Not supported.
case 0xE0: // Pitch Bend
part->_pitchbend = (param1 | (param2 << 7)) - 0x2000;
for (mc = part->_mc; mc; mc = mc->_next) {
adlib_note_on(mc->_channel, mc->_note + part->_transpose_eff,
(part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff);
}
break;
part->pitchBend ((param1 | (param2 << 7)) - 0x2000); break;
case 0xF0: // SysEx
// We should never get here! SysEx information has to be
// sent via high-level semantic methods.
@ -740,9 +824,14 @@ void MidiDriver_ADLIB::setPitchBendRange (byte channel, uint range)
}
void MidiDriver_ADLIB::sysEx_customInstrument (byte channel, uint32 type, byte *instr)
{
sysEx_customInstrument (&_parts [channel], type, instr);
}
void MidiDriver_ADLIB::sysEx_customInstrument (AdlibPart *part, uint32 type, byte *instr)
{
if (type == 'ADL ') {
Instrument *i = &_parts[channel]._part_instr;
Instrument *i = &part->_part_instr;
memcpy(i, instr, sizeof(Instrument));
}
}
@ -753,6 +842,21 @@ void MidiDriver_ADLIB::setTimerCallback (void *timer_param, void (*timer_proc) (
_timer_param = timer_param;
}
MidiChannel *MidiDriver_ADLIB::allocateChannel()
{
AdlibPart *part;
uint i;
for (i = 0; i < ARRAYSIZE(_parts); ++i) {
part = &_parts[i];
if (!part->_allocated) {
part->allocate();
return (part);
}
}
return NULL;
}
MidiDriver *MidiDriver_ADLIB_create()
{
return new MidiDriver_ADLIB();
@ -860,18 +964,18 @@ void MidiDriver_ADLIB::mc_inc_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11
switch (s11->param) {
case 0:
mc->_vol_2 = s10->start_value + s11->modify_val;
((MidiDriver_ADLIB *) part->_drv)->adlib_set_param(mc->_channel, 0,
volume_table[lookup_table[mc->_vol_2]
[part->_vol_eff >> 2]]);
adlib_set_param(mc->_channel, 0,
volume_table[lookup_table[mc->_vol_2]
[part->_vol_eff >> 2]]);
break;
case 13:
mc->_vol_1 = s10->start_value + s11->modify_val;
if (mc->_twochan) {
((MidiDriver_ADLIB *) part->_drv)->adlib_set_param(mc->_channel, 13,
volume_table[lookup_table[mc->_vol_1]
[part->_vol_eff >> 2]]);
adlib_set_param(mc->_channel, 13,
volume_table[lookup_table[mc->_vol_1]
[part->_vol_eff >> 2]]);
} else {
((MidiDriver_ADLIB *) part->_drv)->adlib_set_param(mc->_channel, 13, mc->_vol_1);
adlib_set_param(mc->_channel, 13, mc->_vol_1);
}
break;
case 30:
@ -881,14 +985,14 @@ void MidiDriver_ADLIB::mc_inc_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11
s11->s10->unk3 = (char)s11->modify_val;
break;
default:
((MidiDriver_ADLIB *) part->_drv)->adlib_set_param(mc->_channel, s11->param,
s10->start_value + s11->modify_val);
adlib_set_param(mc->_channel, s11->param,
s10->start_value + s11->modify_val);
break;
}
}
if (code & 2 && s11->flag0x10)
((MidiDriver_ADLIB *) part->_drv)->adlib_key_onoff(mc->_channel);
adlib_key_onoff(mc->_channel);
}
void MidiDriver_ADLIB::adlib_key_off(int chan)
@ -1081,23 +1185,11 @@ int MidiDriver_ADLIB::random_nr(int a)
return _rand_seed * a >> 8;
}
void MidiDriver_ADLIB::part_key_off (byte chan, byte note)
void MidiDriver_ADLIB::part_key_off (AdlibPart *part, byte note)
{
MidiChannelAdl *mc;
// Percussion is not implemented; filter that channel
if (chan == 9)
return;
AdlibPart *part;
for (part = _parts; part; part = part->_next) {
if (part->_chan == chan)
break;
}
if (!part)
return;
for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
for (mc = part->_mc; mc; mc = mc->_next) {
if (mc->_note == note) {
if (part->_pedal)
mc->_waitforpedal = true;
@ -1107,22 +1199,10 @@ void MidiDriver_ADLIB::part_key_off (byte chan, byte note)
}
}
void MidiDriver_ADLIB::part_key_on (byte chan, byte note, byte velocity)
void MidiDriver_ADLIB::part_key_on (AdlibPart *part, byte note, byte velocity)
{
MidiChannelAdl *mc;
// Percussion is not implemented; filter that channel
if (chan == 9)
return;
AdlibPart *part;
for (part = _parts; part; part = part->_next) {
if (part->_chan == chan)
break;
}
if (!part)
return;
mc = allocate_midichan(part->_pri_eff);
if (!mc)
return;
@ -1287,7 +1367,7 @@ void MidiDriver_ADLIB::mc_init_stuff (MidiChannelAdl *mc, Struct10 * s10,
s11->s10->unk3 = 0;
break;
default:
s10->start_value = ((MidiDriver_ADLIB *) part->_drv)->adlib_read_param(mc->_channel, s11->param);
s10->start_value = adlib_read_param(mc->_channel, s11->param);
}
struct10_init(s10, ie);