diff options
-rw-r--r-- | engines/sci/console.cpp | 3 | ||||
-rw-r--r-- | engines/sci/resource.h | 4 | ||||
-rw-r--r-- | engines/sci/resource_audio.cpp | 3 | ||||
-rw-r--r-- | engines/sci/sound/midiparser_sci.cpp | 76 | ||||
-rw-r--r-- | engines/sci/sound/midiparser_sci.h | 25 | ||||
-rw-r--r-- | engines/sci/sound/music.cpp | 64 | ||||
-rw-r--r-- | engines/sci/sound/music.h | 13 | ||||
-rw-r--r-- | sound/midiparser.cpp | 22 | ||||
-rw-r--r-- | sound/midiparser.h | 5 |
9 files changed, 109 insertions, 106 deletions
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 8fc49fa396..ebe7edecbe 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -859,7 +859,8 @@ bool Console::cmdShowInstruments(int argc, const char **argv) { SciVersion doSoundVersion = _engine->_features->detectDoSoundType(); MidiPlayer *player = MidiPlayer_Midi_create(doSoundVersion); - MidiParser_SCI *parser = new MidiParser_SCI(doSoundVersion); + MidiParser_SCI *parser; // = new MidiParser_SCI(doSoundVersion); + // FIXME: add SciMusic object parser->setMidiDriver(player); Common::List<ResourceId> *resources = _engine->getResMan()->listResources(kResourceTypeSound); diff --git a/engines/sci/resource.h b/engines/sci/resource.h index 13eb6567ab..b37f71832d 100644 --- a/engines/sci/resource.h +++ b/engines/sci/resource.h @@ -513,7 +513,6 @@ public: Track *getDigitalTrack(); int getChannelFilterMask(int hardwareMask, bool wantsRhythm); byte getInitialVoiceCount(byte channel); - bool isChannelUsed(byte channel) const { return _channelsUsed & (1 << channel); } private: SciVersion _soundVersion; @@ -521,9 +520,6 @@ private: Track *_tracks; Resource *_innerResource; ResourceManager *_resMan; - uint16 _channelsUsed; - - void setChannelUsed(byte channel) { _channelsUsed |= (1 << channel); } }; } // End of namespace Sci diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp index c98b5387be..72fd8f4594 100644 --- a/engines/sci/resource_audio.cpp +++ b/engines/sci/resource_audio.cpp @@ -461,8 +461,6 @@ SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersi byte *dataEnd; Channel *channel, *sampleChannel; - _channelsUsed = 0; - switch (_soundVersion) { case SCI_VERSION_0_EARLY: case SCI_VERSION_0_LATE: @@ -552,7 +550,6 @@ SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersi channel->data = resource->data + READ_LE_UINT16(data + 2) + 2; channel->size = READ_LE_UINT16(data + 4) - 2; // Not counting channel header channel->number = *(channel->data - 2); - setChannelUsed(channel->number); channel->poly = *(channel->data - 1); channel->time = channel->prev = 0; if (channel->number == 0xFE) { // Digital channel diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index c74a7eae11..8bc9b97926 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -43,9 +43,10 @@ enum SciMidiCommands { // MidiParser_SCI // -MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion) : +MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion, SciMusic *music) : MidiParser() { _soundVersion = soundVersion; + _music = music; _mixedData = NULL; // mididata contains delta in 1/60th second // values of ppqn and tempo are found experimentally and may be wrong @@ -59,10 +60,6 @@ MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion) : _dataincAdd = false; _dataincToAdd = 0; _resetOnPause = false; - _channelsUsed = 0; - - for (int i = 0; i < 16; i++) - _channelRemap[i] = i; } MidiParser_SCI::~MidiParser_SCI() { @@ -78,6 +75,13 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in if (_pSnd) setVolume(psnd->volume); + for (int i = 0; i < 15; i++) { + _channelUsed[i] = false; + _channelRemap[i] = -1; + } + _channelRemap[9] = 9; // never map channel 9, because that's used for percussion + _channelRemap[15] = 15; // never map channel 15, because thats used by sierra internally + if (channelFilterMask) { // SCI0 only has 1 data stream, but we need to filter out channels depending on music hardware selection midiFilterChannels(channelFilterMask); @@ -128,21 +132,24 @@ void MidiParser_SCI::unloadMusic() { // Center the pitch wheels and hold pedal in preparation for the next piece of music if (_driver && _pSnd) { for (int i = 0; i < 16; ++i) { - if (isChannelUsed(i)) { - _driver->send(0xE0 | i, 0, 0x40); // Reset pitch wheel - _driver->send(0xB0 | i, 0x40, 0); // Reset hold pedal + int16 realChannel = _channelRemap[i]; + if (realChannel != -1) { + _driver->send(0xE0 | realChannel, 0, 0x40); // Reset pitch wheel + _driver->send(0xB0 | realChannel, 0x40, 0); // Reset hold pedal } } } +} - for (int i = 0; i < 16; i++) - _channelRemap[i] = i; +void MidiParser_SCI::sendToDriver(uint32 b) { + // Channel remapping + int16 realChannel = _channelRemap[b & 0xf]; + assert(realChannel != -1); + b = (b & 0xFFFFFFF0) | realChannel; + _driver->send(b); } void MidiParser_SCI::parseNextEvent(EventInfo &info) { - // Monitor which channels are used by this song - setChannelUsed(info.channel()); - // Set signal AFTER waiting for delta, otherwise we would set signal too soon resulting in all sorts of bugs if (_dataincAdd) { _dataincAdd = false; @@ -334,8 +341,9 @@ void MidiParser_SCI::allNotesOff() { // Turn off all active notes for (i = 0; i < 128; ++i) { for (j = 0; j < 16; ++j) { - if ((_active_notes[i] & (1 << j)) && isChannelUsed(j)){ - _driver->send(0x80 | j, i, 0); + int16 realChannel = _channelRemap[j]; + if ((_active_notes[i] & (1 << j)) && realChannel != -1){ + _driver->send(0x80 | realChannel, i, 0); } } } @@ -353,8 +361,9 @@ void MidiParser_SCI::allNotesOff() { // support this...). for (i = 0; i < 16; ++i) { - if (isChannelUsed(i)) - _driver->send(0xB0 | i, 0x7b, 0); // All notes off + int16 realChannel = _channelRemap[i]; + if (realChannel != -1) + _driver->send(0xB0 | realChannel, 0x7b, 0); // All notes off } memset(_active_notes, 0, sizeof(_active_notes)); @@ -436,19 +445,23 @@ byte *MidiParser_SCI::midiMixChannels() { default: // MIDI command if (command & 0x80) { par1 = *channel->data++; - - // TODO: Fix remapping - -#if 0 - // Remap channel. Keep the upper 4 bits (command code) and change - // the lower 4 bits (channel) - byte remappedChannel = _channelRemap[par1 & 0xF]; - par1 = (par1 & 0xF0) | (remappedChannel & 0xF); -#endif } else {// running status par1 = command; command = channel->prev; } + + // remember which channel got used for channel remapping + byte midiChannel = command & 0xF; + _channelUsed[midiChannel] = true; +// int16 realChannel = _channelRemap[midiChannel]; +// if (realChannel == -1) { +// // We don't own this channel yet, so ask SciMusic to get it (or a remapped one) +// realChannel = _music->tryToOwnChannel(_pSnd, midiChannel); +// _channelRemap[midiChannel] = realChannel; +// } +// // Map new channel +// command = realChannel | (command & 0xF0); + if (command != global_prev) *outData++ = command; // out command *outData++ = par1;// pout par1 @@ -577,6 +590,17 @@ byte *MidiParser_SCI::midiFilterChannels(int channelMask) { return _mixedData; } +// This will get called right before actual playing and will try to own the used channels +void MidiParser_SCI::tryToOwnChannels() { + for (int curChannel = 0; curChannel < 15; curChannel++) { + if (_channelUsed[curChannel]) { + if (_channelRemap[curChannel] == -1) { + _channelRemap[curChannel] = _music->tryToOwnChannel(_pSnd, curChannel); + } + } + } +} + void MidiParser_SCI::setVolume(byte volume) { // FIXME: This receives values > 127... throw a warning for now and clip the variable if (volume > MUSIC_VOLUME_MAX) { diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h index 80401460d2..4888fa06ca 100644 --- a/engines/sci/sound/midiparser_sci.h +++ b/engines/sci/sound/midiparser_sci.h @@ -53,7 +53,7 @@ namespace Sci { */ class MidiParser_SCI : public MidiParser { public: - MidiParser_SCI(SciVersion soundVersion); + MidiParser_SCI(SciVersion soundVersion, SciMusic *music); ~MidiParser_SCI(); bool loadMusic(SoundResource::Track *track, MusicEntry *psnd, int channelFilterMask, SciVersion soundVersion); bool loadMusic(byte *, uint32) { @@ -73,25 +73,19 @@ public: void allNotesOff(); - void remapChannel(byte channel, byte newChannel) { - assert(channel < 0xF); // don't touch special SCI channel 15 - assert(newChannel < 0xF); // don't touch special SCI channel 15 - _channelRemap[channel] = newChannel; - } - - void clearUsedChannels() { _channelsUsed = 0; } - const byte *getMixedData() const { return _mixedData; } -protected: - bool isChannelUsed(byte channel) const { return _channelsUsed & (1 << channel); } - void setChannelUsed(byte channel) { _channelsUsed |= (1 << channel); } + void tryToOwnChannels(); +protected: + void sendToDriver(uint32 b); void parseNextEvent(EventInfo &info); byte *midiMixChannels(); byte *midiFilterChannels(int channelMask); byte midiGetNextChannel(long ticker); + SciMusic *_music; + SciVersion _soundVersion; byte *_mixedData; SoundResource::Track *_track; @@ -105,11 +99,8 @@ protected: int16 _dataincToAdd; bool _resetOnPause; - // A 16-bit mask, containing the channels used - // by the currently parsed song - uint16 _channelsUsed; - - byte _channelRemap[16]; + bool _channelUsed[16]; + int16 _channelRemap[16]; }; } // End of namespace Sci diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index 254c70a24f..69e1c180e8 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -43,6 +43,9 @@ SciMusic::SciMusic(SciVersion soundVersion) // Reserve some space in the playlist, to avoid expensive insertion // operations _playList.reserve(10); + + for (int i = 0; i < 16; i++) + _usedChannel[i] = 0; } SciMusic::~SciMusic() { @@ -174,20 +177,6 @@ void SciMusic::sortPlayList() { Common::sort(_playList.begin(), _playList.end(), musicEntryCompare); } -void SciMusic::findUsedChannels() { - // Reset list - for (int k = 0; k < 16; k++) - _usedChannels[k] = false; - - const MusicList::const_iterator end = _playList.end(); - for (MusicList::const_iterator i = _playList.begin(); i != end; ++i) { - for (int channel = 0; channel < 16; channel++) { - if ((*i)->soundRes && (*i)->soundRes->isChannelUsed(channel)) - _usedChannels[channel] = true; - } - } -} - void SciMusic::soundInitSnd(MusicEntry *pSnd) { int channelFilterMask = 0; SoundResource::Track *track = pSnd->soundRes->getTrackByType(_pMidiDrv->getPlayId()); @@ -224,34 +213,13 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) { _mutex.lock(); pSnd->soundType = Audio::Mixer::kMusicSoundType; if (pSnd->pMidiParser == NULL) { - pSnd->pMidiParser = new MidiParser_SCI(_soundVersion); + pSnd->pMidiParser = new MidiParser_SCI(_soundVersion, this); pSnd->pMidiParser->setMidiDriver(_pMidiDrv); pSnd->pMidiParser->setTimerRate(_dwTempo); } pSnd->pauseCounter = 0; - // TODO: Fix channel remapping. This doesn't quite work... (e.g. no difference in LSL1VGA) -#if 0 - // Remap channels - findUsedChannels(); - - pSnd->pMidiParser->clearUsedChannels(); - - for (int i = 0; i < 16; i++) { - if (_usedChannels[i] && pSnd->soundRes->isChannelUsed(i)) { - int16 newChannel = getNextUnusedChannel(); - if (newChannel >= 0) { - _usedChannels[newChannel] = true; - debug("Remapping channel %d to %d\n", i, newChannel); - pSnd->pMidiParser->remapChannel(i, newChannel); - } else { - warning("Attempt to remap channel %d, but no unused channels exist", i); - } - } - } -#endif - // Find out what channels to filter for SCI0 channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayId(), _pMidiDrv->hasRhythmChannel()); pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion); @@ -263,6 +231,24 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) { } } +// This one checks, if requested channel is available -> in that case give caller that channel +// Otherwise look for an unused one +int16 SciMusic::tryToOwnChannel(MusicEntry *caller, int16 bestChannel) { + if (!_usedChannel[bestChannel]) { + // currently unused, so give it to caller directly + _usedChannel[bestChannel] = caller; + return bestChannel; + } + // otherwise look for unused channel + for (int channelNr = 0; channelNr < 15; channelNr++) { + if (!_usedChannel[channelNr]) { + _usedChannel[channelNr] = caller; + return channelNr; + } + } + error("no free channels"); +} + void SciMusic::onTimer() { const MusicList::iterator end = _playList.end(); for (MusicList::iterator i = _playList.begin(); i != end; ++i) @@ -324,6 +310,7 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { DisposeAfterUse::NO); } } else { + pSnd->pMidiParser->tryToOwnChannels(); _mutex.lock(); if (pSnd->pMidiParser) { pSnd->pMidiParser->setVolume(pSnd->volume); @@ -388,6 +375,11 @@ void SciMusic::soundKill(MusicEntry *pSnd) { _mutex.lock(); uint sz = _playList.size(), i; + // Remove used channels + for (i = 0; i < 15; i++) { + if (_usedChannel[i] == pSnd) + _usedChannel[i] = 0; + } // Remove sound from playlist for (i = 0; i < sz; i++) { if (_playList[i] == pSnd) { diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h index 83cd59e89b..bbafb808cb 100644 --- a/engines/sci/sound/music.h +++ b/engines/sci/sound/music.h @@ -196,6 +196,8 @@ public: // where a deadlock can occur Common::Mutex _mutex; + int16 tryToOwnChannel(MusicEntry *caller, int16 bestChannel); + protected: void sortPlayList(); @@ -210,20 +212,11 @@ protected: bool _bMultiMidi; private: static void miditimerCallback(void *p); - void findUsedChannels(); - int16 getNextUnusedChannel() const { - for (int i = 0; i < 16; i++) { - if (!_usedChannels[i]) - return i; - } - - return -1; - } MusicList _playList; bool _soundOn; byte _masterVolume; - bool _usedChannels[16]; + MusicEntry *_usedChannel[16]; }; } // End of namespace Sci diff --git a/sound/midiparser.cpp b/sound/midiparser.cpp index 8ae2bad71a..d58471765e 100644 --- a/sound/midiparser.cpp +++ b/sound/midiparser.cpp @@ -67,6 +67,10 @@ void MidiParser::property(int prop, int value) { } } +void MidiParser::sendToDriver(uint32 b) { + _driver->send(b); +} + void MidiParser::setTempo(uint32 tempo) { _tempo = tempo; if (_ppqn) @@ -127,7 +131,7 @@ void MidiParser::hangingNote(byte channel, byte note, uint32 time_left, bool rec best = ptr; if (ptr->time_left) { if (recycle) - _driver->send(0x80 | channel, note, 0); + sendToDriver(0x80 | channel, note, 0); --_hanging_notes_count; } break; @@ -172,7 +176,7 @@ void MidiParser::onTimer() { for (i = ARRAYSIZE(_hanging_notes); i; --i, ++ptr) { if (ptr->time_left) { if (ptr->time_left <= _timer_rate) { - _driver->send(0x80 | ptr->channel, ptr->note, 0); + sendToDriver(0x80 | ptr->channel, ptr->note, 0); ptr->time_left = 0; --_hanging_notes_count; } else { @@ -232,7 +236,7 @@ void MidiParser::onTimer() { else activeNote(info.channel(), info.basic.param1, true); } - _driver->send(info.event, info.basic.param1, info.basic.param2); + sendToDriver(info.event, info.basic.param1, info.basic.param2); } @@ -258,7 +262,7 @@ void MidiParser::allNotesOff() { for (i = 0; i < 128; ++i) { for (j = 0; j < 16; ++j) { if (_active_notes[i] & (1 << j)) { - _driver->send(0x80 | j, i, 0); + sendToDriver(0x80 | j, i, 0); } } } @@ -266,7 +270,7 @@ void MidiParser::allNotesOff() { // Turn off all hanging notes for (i = 0; i < ARRAYSIZE(_hanging_notes); i++) { if (_hanging_notes[i].time_left) { - _driver->send(0x80 | _hanging_notes[i].channel, _hanging_notes[i].note, 0); + sendToDriver(0x80 | _hanging_notes[i].channel, _hanging_notes[i].note, 0); _hanging_notes[i].time_left = 0; } } @@ -276,7 +280,7 @@ void MidiParser::allNotesOff() { // support this...). for (i = 0; i < 16; ++i) { - _driver->send(0xB0 | i, 0x7b, 0); // All notes off + sendToDriver(0xB0 | i, 0x7b, 0); // All notes off } memset(_active_notes, 0, sizeof(_active_notes)); @@ -348,7 +352,7 @@ void MidiParser::hangAllActiveNotes() { for (j = 0; j < 16; ++j) { if (temp_active[i] & (1 << j)) { activeNote(j, i, false); - _driver->send(0x80 | j, i, 0); + sendToDriver(0x80 | j, i, 0); } } } @@ -399,7 +403,7 @@ bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes) { else _driver->sysEx(info.ext.data, (uint16)info.length); } else - _driver->send(info.event, info.basic.param1, info.basic.param2); + sendToDriver(info.event, info.basic.param1, info.basic.param2); } parseNextEvent(_next_event); @@ -441,7 +445,7 @@ void MidiParser::unloadMusic() { if (_driver) { for (int i = 0; i < 16; ++i) { - _driver->send(0xE0 | i, 0, 0x40); + sendToDriver(0xE0 | i, 0, 0x40); } } } diff --git a/sound/midiparser.h b/sound/midiparser.h index 37c00db5b6..35a14954a7 100644 --- a/sound/midiparser.h +++ b/sound/midiparser.h @@ -374,6 +374,11 @@ public: void setTempo(uint32 tempo); void onTimer(); + virtual void sendToDriver(uint32 b); + void sendToDriver(byte status, byte firstOp, byte secondOp) { + sendToDriver(status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16)); + } + bool isPlaying() const { return (_position._play_pos != 0); } void stopPlaying(); |