aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorathrxx2018-12-14 23:04:40 +0100
committerathrxx2019-08-07 16:43:08 +0200
commit09f007fa5ed7c85eb69f40f768b06962e4e03ac8 (patch)
tree842c06512cb24f4fb5b5c041728f9bd576ea439b
parentf8c98d76e61ffd43d663baebbe009e5f182a1e7d (diff)
downloadscummvm-rg350-09f007fa5ed7c85eb69f40f768b06962e4e03ac8.tar.gz
scummvm-rg350-09f007fa5ed7c85eb69f40f768b06962e4e03ac8.tar.bz2
scummvm-rg350-09f007fa5ed7c85eb69f40f768b06962e4e03ac8.zip
SCI: (ADL driver) - implement/fix voice mapping
- Backport some code of waltervn's fork at his recommendation (taken from 97604200 and 4c3bfee5). That code has caught some bitrot and I were too lazy to manually resolve the merge conflicts. All changes have been re-checked with disasm. - Add initTrack() implementation for SCI0 EARLY/LATE - Minor cleanup - Please note that this commit does not fix any shortcomings of the SCI sound engine
-rw-r--r--engines/sci/sound/drivers/adlib.cpp255
1 files changed, 171 insertions, 84 deletions
diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index 472f3df074..265ad71858 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -41,9 +41,6 @@ namespace Sci {
#define STEREO true
#endif
-// FIXME: We don't seem to be sending the polyphony init data, so disable this for now
-#define ADLIB_DISABLE_VOICE_MAPPING
-
class MidiDriver_AdLib : public MidiDriver {
public:
enum {
@@ -51,14 +48,17 @@ public:
kRhythmKeys = 62
};
- MidiDriver_AdLib(Audio::Mixer *mixer) :_playSwitch(true), _masterVolume(15), _rhythmKeyMap(), _opl(0), _isOpen(false) { }
+ MidiDriver_AdLib(SciVersion version) : _version(version), _isSCI0(version < SCI_VERSION_1_EARLY), _playSwitch(true), _masterVolume(15),
+ _numVoiceMax(version == SCI_VERSION_0_EARLY ? 8 : kVoices), _rhythmKeyMap(), _opl(0), _adlibTimerParam(0), _adlibTimerProc(0), _stereo(false), _isOpen(false) { }
virtual ~MidiDriver_AdLib() { }
// MidiDriver
int open() { return -1; } // Dummy implementation (use openAdLib)
- int openAdLib(bool isSCI0);
+ int openAdLib();
void close();
void send(uint32 b);
+ void initTrack(SciSpan<const byte> &header);
+
MidiChannel *allocateChannel() { return NULL; }
MidiChannel *getPercussionChannel() { return NULL; }
bool isOpen() const { return _isOpen; }
@@ -116,32 +116,39 @@ private:
uint16 pitchWheel; // Pitch wheel setting (0-16383, 8192 is center)
uint8 lastVoice; // Last voice used for this MIDI channel
bool enableVelocity; // Enable velocity control (SCI0)
+ uint8 voices; // Number of voices currently used by this channel
+ uint8 mappedVoices; // Number of voices currently mapped to this channel
Channel() : patch(0), volume(63), pan(64), holdPedal(0), extraVoices(0),
- pitchWheel(8192), lastVoice(0), enableVelocity(false) { }
+ pitchWheel(8192), lastVoice(0), enableVelocity(false), voices(0),
+ mappedVoices(0) { }
};
struct AdLibVoice {
- int8 channel; // MIDI channel that this voice is assigned to or -1
+ int8 channel; // MIDI channel that is currently using this voice, or -1
+ int8 mappedChannel; // MIDI channel that this voice is mapped to, or -1
int8 note; // Currently playing MIDI note or -1
int patch; // Currently playing patch or -1
uint8 velocity; // Note velocity
bool isSustained; // Flag indicating a note that is being sustained by the hold pedal
uint16 age; // Age of the current note
- AdLibVoice() : channel(-1), note(-1), patch(-1), velocity(0), isSustained(false), age(0) { }
+ AdLibVoice() : channel(-1), mappedChannel(-1), note(-1), patch(-1), velocity(0), isSustained(false), age(0) { }
};
bool _stereo;
bool _isSCI0;
+ SciVersion _version;
OPL::OPL *_opl;
bool _isOpen;
bool _playSwitch;
int _masterVolume;
+ const uint8 _numVoiceMax;
Channel _channels[MIDI_CHANNELS];
AdLibVoice _voices[kVoices];
Common::SpanOwner<SciSpan<const byte> > _rhythmKeyMap;
Common::Array<AdLibPatch> _patches;
+ Common::List<int> _voiceQueue;
Common::TimerManager::TimerProc _adlibTimerProc;
void *_adlibTimerParam;
@@ -158,18 +165,19 @@ private:
void noteOn(int channel, int note, int velocity);
void noteOff(int channel, int note);
int findVoice(int channel);
+ int findVoiceLateSci11(int channel);
void voiceMapping(int channel, int voices);
void assignVoices(int channel, int voices);
void releaseVoices(int channel, int voices);
void donateVoices();
- int findVoiceBasic(int channel);
+ void queueMoveToBack(int voice);
void setVelocityReg(int regOffset, int velocity, int kbScaleLevel, int pan);
int calcVelocity(int voice, int op);
};
class MidiPlayer_AdLib : public MidiPlayer {
public:
- MidiPlayer_AdLib(SciVersion soundVersion) : MidiPlayer(soundVersion) { _driver = new MidiDriver_AdLib(g_system->getMixer()); }
+ MidiPlayer_AdLib(SciVersion soundVersion) : MidiPlayer(soundVersion) { _driver = new MidiDriver_AdLib(soundVersion); }
~MidiPlayer_AdLib() {
delete _driver;
_driver = 0;
@@ -183,7 +191,7 @@ public:
bool hasRhythmChannel() const { return false; }
void setVolume(byte volume) { static_cast<MidiDriver_AdLib *>(_driver)->setVolume(volume); }
void playSwitch(bool play) { static_cast<MidiDriver_AdLib *>(_driver)->playSwitch(play); }
-
+ void initTrack(SciSpan<const byte> &header) { static_cast<MidiDriver_AdLib *>(_driver)->initTrack(header); }
int getLastChannel() const { return (static_cast<const MidiDriver_AdLib *>(_driver)->useRhythmChannel() ? 8 : 15); }
};
@@ -219,15 +227,18 @@ static const int ym3812_note[13] = {
0x2ae
};
-int MidiDriver_AdLib::openAdLib(bool isSCI0) {
+int MidiDriver_AdLib::openAdLib() {
_stereo = STEREO;
- debug(3, "ADLIB: Starting driver in %s mode", (isSCI0 ? "SCI0" : "SCI1"));
- _isSCI0 = isSCI0;
+ debug(3, "ADLIB: Starting driver in %s mode", (_isSCI0 ? "SCI0" : "SCI1"));
+
+ // Fill in the voice queue
+ for (int i = 0; i < kVoices; ++i)
+ _voiceQueue.push_back(i);
_opl = OPL::Config::create(_stereo ? OPL::Config::kDualOpl2 : OPL::Config::kOpl2);
- // Try falling back to mono, thus plain OPL2 emualtor, when no Dual OPL2 is available.
+ // Try falling back to mono, thus plain OPL2 emulator, when no Dual OPL2 is available.
if (!_opl && _stereo) {
_stereo = false;
_opl = OPL::Config::create(OPL::Config::kOpl2);
@@ -330,6 +341,53 @@ void MidiDriver_AdLib::send(uint32 b) {
}
}
+void MidiDriver_AdLib::initTrack(SciSpan<const byte> &header) {
+ if (!_isOpen || !_isSCI0)
+ return;
+
+ uint8 readPos = 0;
+ uint8 caps = header.getInt8At(readPos++);
+ if (caps != 0 && (_version == SCI_VERSION_0_EARLY || caps != 2))
+ return;
+
+ for (int i = 0; i < kVoices; ++i) {
+ _voices[i].channel = _voices[i].mappedChannel = _voices[i].note = -1;
+ _voices[i].isSustained = false;
+ _voices[i].patch = 13;
+ _voices[i].velocity = 0;
+ _voices[i].age = 0;
+ }
+
+ int numVoices = 0;
+ for (int i = 0; i < 16; ++i) {
+ _channels[i].patch = 13;
+ _channels[i].extraVoices = 0;
+ _channels[i].mappedVoices = 0;
+
+ if (_version == SCI_VERSION_0_LATE) {
+ uint8 num = header.getInt8At(readPos++) & 0x7F;
+ uint8 flags = header.getInt8At(readPos++);
+ if ((flags & 0x04) && num)
+ assignVoices(i, num);
+ } else {
+ uint8 val = header.getInt8At(readPos++);
+ if (val & 0x01) {
+ uint8 num = val >> 4;
+ if (!(val & 0x08) && num && num != 0x0F) {
+ while (num--) {
+ if (numVoices >= _numVoiceMax)
+ continue;
+ _voices[numVoices++].mappedChannel = i;
+ _channels[i].mappedVoices++;
+ }
+ }
+ } else if (val & 0x08) {
+ debugC(9, kDebugLevelSound, "MidiDriver_AdLib::initTrack(): Control channel found: 0x%.02x", i);
+ }
+ }
+ }
+}
+
void MidiDriver_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
_adlibTimerProc = timerProc;
_adlibTimerParam = timerParam;
@@ -377,8 +435,8 @@ void MidiDriver_AdLib::loadInstrument(const SciSpan<const byte> &ins) {
void MidiDriver_AdLib::voiceMapping(int channel, int voices) {
int curVoices = 0;
- for (int i = 0; i < kVoices; i++)
- if (_voices[i].channel == channel)
+ for (int i = 0; i < _numVoiceMax; i++)
+ if (_voices[i].mappedChannel == channel)
curVoices++;
curVoices += _channels[channel].extraVoices;
@@ -396,14 +454,19 @@ void MidiDriver_AdLib::voiceMapping(int channel, int voices) {
void MidiDriver_AdLib::assignVoices(int channel, int voices) {
assert(voices > 0);
- for (int i = 0; i < kVoices; i++)
- if (_voices[i].channel == -1) {
- _voices[i].channel = channel;
+ for (int i = 0; i < _numVoiceMax; i++)
+ if (_voices[i].mappedChannel == -1) {
+ if (_voices[i].note != -1) // Late SCI1.1, stop note on unmapped channel
+ voiceOff(i);
+ _voices[i].mappedChannel = channel;
+ ++_channels[channel].mappedVoices;
if (--voices == 0)
return;
}
- _channels[channel].extraVoices += voices;
+ // This is already too advanced for SCI0...
+ if (!_isSCI0)
+ _channels[channel].extraVoices += voices;
}
void MidiDriver_AdLib::releaseVoices(int channel, int voices) {
@@ -415,18 +478,20 @@ void MidiDriver_AdLib::releaseVoices(int channel, int voices) {
voices -= _channels[channel].extraVoices;
_channels[channel].extraVoices = 0;
- for (int i = 0; i < kVoices; i++) {
- if ((_voices[i].channel == channel) && (_voices[i].note == -1)) {
- _voices[i].channel = -1;
+ for (int i = 0; i < _numVoiceMax; i++) {
+ if ((_voices[i].mappedChannel == channel) && (_voices[i].note == -1)) {
+ _voices[i].mappedChannel = -1;
+ --_channels[channel].mappedVoices;
if (--voices == 0)
return;
}
}
- for (int i = 0; i < kVoices; i++) {
- if (_voices[i].channel == channel) {
+ for (int i = 0; i < _numVoiceMax; i++) {
+ if (_voices[i].mappedChannel == channel) {
voiceOff(i);
- _voices[i].channel = -1;
+ _voices[i].mappedChannel = -1;
+ --_channels[channel].mappedVoices;
if (--voices == 0)
return;
}
@@ -434,10 +499,13 @@ void MidiDriver_AdLib::releaseVoices(int channel, int voices) {
}
void MidiDriver_AdLib::donateVoices() {
+ if (_isSCI0)
+ return;
+
int freeVoices = 0;
for (int i = 0; i < kVoices; i++)
- if (_voices[i].channel == -1)
+ if (_voices[i].mappedChannel == -1)
freeVoices++;
if (freeVoices == 0)
@@ -484,11 +552,7 @@ void MidiDriver_AdLib::noteOn(int channel, int note, int velocity) {
}
}
-#ifdef ADLIB_DISABLE_VOICE_MAPPING
- int voice = findVoiceBasic(channel);
-#else
- int voice = findVoice(channel);
-#endif
+ int voice = _rhythmKeyMap ? findVoiceLateSci11(channel) : findVoice(channel);
if (voice == -1) {
debug(3, "ADLIB: failed to find free voice assigned to channel %i", channel);
@@ -498,77 +562,97 @@ void MidiDriver_AdLib::noteOn(int channel, int note, int velocity) {
voiceOn(voice, note, velocity);
}
-// FIXME: Temporary, see comment at top of file regarding ADLIB_DISABLE_VOICE_MAPPING
-int MidiDriver_AdLib::findVoiceBasic(int channel) {
+int MidiDriver_AdLib::findVoice(int channel) {
int voice = -1;
int oldestVoice = -1;
- int oldestAge = -1;
+ uint32 oldestAge = 0;
// Try to find a voice assigned to this channel that is free (round-robin)
for (int i = 0; i < kVoices; i++) {
int v = (_channels[channel].lastVoice + i + 1) % kVoices;
- if (_voices[v].note == -1) {
- voice = v;
- break;
- }
+ if (_voices[v].mappedChannel == channel) {
+ if (_voices[v].note == -1) {
+ voice = v;
+ _voices[voice].channel = channel;
+ break;
+ }
- // We also keep track of the oldest note in case the search fails
- if (_voices[v].age > oldestAge) {
- oldestAge = _voices[v].age;
- oldestVoice = v;
+ // We also keep track of the oldest note in case the search fails
+ // Notes started in the current time slice will not be selected
+ if (_voices[v].age >= oldestAge) {
+ oldestAge = _voices[v].age;
+ oldestVoice = v;
+ }
}
}
if (voice == -1) {
- if (oldestVoice >= 0) {
- voiceOff(oldestVoice);
- voice = oldestVoice;
- } else {
+ if (!oldestAge)
return -1;
- }
+ voiceOff(oldestVoice);
+ voice = oldestVoice;
+ _voices[voice].channel = channel;
}
- _voices[voice].channel = channel;
_channels[channel].lastVoice = voice;
+
return voice;
}
-int MidiDriver_AdLib::findVoice(int channel) {
- int voice = -1;
- int oldestVoice = -1;
- uint32 oldestAge = 0;
+int MidiDriver_AdLib::findVoiceLateSci11(int channel) {
+ Common::List<int>::const_iterator it;
- // Try to find a voice assigned to this channel that is free (round-robin)
- for (int i = 0; i < kVoices; i++) {
- int v = (_channels[channel].lastVoice + i + 1) % kVoices;
+ // Search for unused voice
+ for (it = _voiceQueue.begin(); it != _voiceQueue.end(); ++it) {
+ int voice = *it;
+ if (_voices[voice].note == -1 && _voices[voice].patch == _channels[channel].patch) {
+ _voices[voice].channel = channel;
+ return voice;
+ }
+ }
- if (_voices[v].channel == channel) {
- if (_voices[v].note == -1) {
- voice = v;
- break;
- }
+ // Same as before, minus the program check
+ for (it = _voiceQueue.begin(); it != _voiceQueue.end(); ++it) {
+ int voice = *it;
+ if (_voices[voice].note == -1) {
+ _voices[voice].channel = channel;
+ return voice;
+ }
+ }
- // We also keep track of the oldest note in case the search fails
- // Notes started in the current time slice will not be selected
- if (_voices[v].age > oldestAge) {
- oldestAge = _voices[v].age;
- oldestVoice = v;
+ // Search for channel with highest excess of voices
+ int maxExceed = 0;
+ int maxExceedChan = 0;
+ for (uint i = 0; i < MIDI_CHANNELS; ++i) {
+ if (_channels[i].voices > _channels[i].mappedVoices) {
+ int exceed = _channels[i].voices - _channels[i].mappedVoices;
+ if (exceed > maxExceed) {
+ maxExceed = exceed;
+ maxExceedChan = i;
}
}
}
- if (voice == -1) {
- if (oldestVoice >= 0) {
- voiceOff(oldestVoice);
- voice = oldestVoice;
- } else {
- return -1;
+ // Stop voice on channel with highest excess if possible, otherwise stop
+ // note on this channel.
+ int stopChan = (maxExceed > 0) ? maxExceedChan : channel;
+
+ for (it = _voiceQueue.begin(); it != _voiceQueue.end(); ++it) {
+ int voice = *it;
+ if (_voices[voice].channel == stopChan) {
+ voiceOff(voice);
+ _voices[voice].channel = channel;
+ return voice;
}
}
- _channels[channel].lastVoice = voice;
- return voice;
+ return -1;
+}
+
+void MidiDriver_AdLib::queueMoveToBack(int voice) {
+ _voiceQueue.remove(voice);
+ _voiceQueue.push_back(voice);
}
void MidiDriver_AdLib::noteOff(int channel, int note) {
@@ -585,18 +669,18 @@ void MidiDriver_AdLib::noteOff(int channel, int note) {
void MidiDriver_AdLib::voiceOn(int voice, int note, int velocity) {
int channel = _voices[voice].channel;
- int patch;
+ int patch = _channels[channel].patch;
_voices[voice].age = 0;
+ ++_channels[channel].voices;
+ queueMoveToBack(voice);
- if (channel == 9 && _rhythmKeyMap) {
+ if ((channel == 9) && _rhythmKeyMap) {
patch = CLIP(note, 27, 88) + 101;
- } else {
- patch = _channels[channel].patch;
}
// Set patch if different from current patch
- if (patch != _voices[voice].patch)
+ if (patch != _voices[voice].patch && _playSwitch)
setPatch(voice, patch);
_voices[voice].velocity = velocity;
@@ -604,10 +688,14 @@ void MidiDriver_AdLib::voiceOn(int voice, int note, int velocity) {
}
void MidiDriver_AdLib::voiceOff(int voice) {
+ int channel = _voices[voice].channel;
+
_voices[voice].isSustained = false;
setNote(voice, _voices[voice].note, 0);
_voices[voice].note = -1;
_voices[voice].age = 0;
+ queueMoveToBack(voice);
+ --_channels[channel].voices;
}
void MidiDriver_AdLib::setNote(int voice, int note, bool key) {
@@ -616,9 +704,8 @@ void MidiDriver_AdLib::setNote(int voice, int note, bool key) {
float delta;
int bend = _channels[channel].pitchWheel;
- if (channel == 9 && _rhythmKeyMap) {
+ if ((channel == 9) && _rhythmKeyMap)
note = _rhythmKeyMap[CLIP(note, 27, 88) - 27];
- }
_voices[voice].note = note;
@@ -837,7 +924,7 @@ int MidiPlayer_AdLib::open(ResourceManager *resMan) {
return -1;
}
- return static_cast<MidiDriver_AdLib *>(_driver)->openAdLib(_version <= SCI_VERSION_0_LATE);
+ return static_cast<MidiDriver_AdLib *>(_driver)->openAdLib();
}
void MidiPlayer_AdLib::close() {
@@ -849,7 +936,7 @@ void MidiPlayer_AdLib::close() {
byte MidiPlayer_AdLib::getPlayId() const {
switch (_version) {
case SCI_VERSION_0_EARLY:
- return 0x01;
+ return 0x09;
case SCI_VERSION_0_LATE:
return 0x04;
default: