diff options
Diffstat (limited to 'engines/sci/sound')
-rw-r--r-- | engines/sci/sound/midiparser_sci.cpp | 17 | ||||
-rw-r--r-- | engines/sci/sound/music.cpp | 100 | ||||
-rw-r--r-- | engines/sci/sound/music.h | 14 | ||||
-rw-r--r-- | engines/sci/sound/soundcmd.cpp | 44 | ||||
-rw-r--r-- | engines/sci/sound/soundcmd.h | 2 |
5 files changed, 128 insertions, 49 deletions
diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index c0b4f3122e..25facacced 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -360,6 +360,13 @@ void MidiParser_SCI::sendInitCommands() { sendToDriver(0xB0 | i, 0x4B, voiceCount); } } + } else { + for (int i = 0; i < _track->channelCount; ++i) { + byte voiceCount = _track->channels[i].poly; + byte num = _track->channels[i].number; + // TODO: Should we skip the control channel? + sendToDriver(0xB0 | num, 0x4B, voiceCount); + } } } @@ -480,6 +487,11 @@ void MidiParser_SCI::trackState(uint32 b) { s._sustain = (op2 != 0); break; case 0x4B: // voices + if (s._voices != op2) { + // CHECKME: Should we directly call remapChannels() if _mainThreadCalled? + debugC(2, kDebugLevelSound, "Dynamic voice change (%d to %d)", s._voices, op2); + _music->needsRemap(); + } s._voices = op2; _pSnd->_chan[channel]._voices = op2; // Also sync our MusicEntry break; @@ -491,8 +503,9 @@ void MidiParser_SCI::trackState(uint32 b) { bool m = op2; if (_pSnd->_chan[channel]._mute != m) { _pSnd->_chan[channel]._mute = m; - // TODO: If muting/unmuting a channel, remap channels. - warning("Mute change without immediate remapping (mainThread = %d)", _mainThreadCalled); + // CHECKME: Should we directly call remapChannels() if _mainThreadCalled? + _music->needsRemap(); + debugC(2, kDebugLevelSound, "Dynamic mute change (mainThread = %d)", _mainThreadCalled); } } break; diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index 7a6eaf62b4..7156e3c099 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -144,6 +144,8 @@ void SciMusic::init() { _globalReverb = _pMidiDrv->getReverb(); // Init global reverb for SCI0 _currentlyPlayingSample = NULL; + _timeCounter = 0; + _needsRemap = false; } void SciMusic::miditimerCallback(void *p) { @@ -158,6 +160,11 @@ void SciMusic::onTimer() { // sending out queued commands that were "sent" via main thread sendMidiCommandsFromQueue(); + // remap channels, if requested + if (_needsRemap) + remapChannels(false); + _needsRemap = false; + for (MusicList::iterator i = _playList.begin(); i != end; ++i) (*i)->onTimer(); } @@ -285,8 +292,10 @@ byte SciMusic::getCurrentReverb() { return _pMidiDrv->getReverb(); } +// A larger priority value has higher priority. For equal priority values, +// songs that have been added later have higher priority. static bool musicEntryCompare(const MusicEntry *l, const MusicEntry *r) { - return (l->priority > r->priority); + return (l->priority > r->priority) || (l->priority == r->priority && l->time > r->time); } void SciMusic::sortPlayList() { @@ -317,6 +326,8 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) { track = digital; } + pSnd->time = ++_timeCounter; + if (track) { // Play digital sample if (track->digitalChannelNr != -1) { @@ -334,6 +345,8 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) { pSnd->pLoopStream = 0; pSnd->soundType = Audio::Mixer::kSFXSoundType; pSnd->hCurrentAud = Audio::SoundHandle(); + pSnd->playBed = false; + pSnd->overridePriority = false; } else { // play MIDI track Common::StackLock lock(_mutex); @@ -380,6 +393,8 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) { int16 prevHold = pSnd->hold; pSnd->loop = 0; pSnd->hold = -1; + pSnd->playBed = false; + pSnd->overridePriority = false; pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion); pSnd->reverb = pSnd->pMidiParser->getSongReverb(); @@ -395,6 +410,23 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) { void SciMusic::soundPlay(MusicEntry *pSnd) { _mutex.lock(); + if (_soundVersion <= SCI_VERSION_1_EARLY && pSnd->playBed) { + // If pSnd->playBed, and version <= SCI1_EARLY, then kill + // existing sounds with playBed enabled. + + uint playListCount = _playList.size(); + for (uint i = 0; i < playListCount; i++) { + if (_playList[i] != pSnd && _playList[i]->playBed) { + debugC(2, kDebugLevelSound, "Automatically stopping old playBed song from soundPlay"); + MusicEntry *old = _playList[i]; + _mutex.unlock(); + soundStop(old); + _mutex.lock(); + break; + } + } + } + uint playListCount = _playList.size(); uint playListNo = playListCount; MusicEntry *alreadyPlaying = NULL; @@ -408,9 +440,11 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { } if (playListNo == playListCount) { // not found _playList.push_back(pSnd); - sortPlayList(); } + pSnd->time = ++_timeCounter; + sortPlayList(); + _mutex.unlock(); // unlock to perform mixer-related calls if (pSnd->pMidiParser) { @@ -554,6 +588,7 @@ void SciMusic::soundSetPriority(MusicEntry *pSnd, byte prio) { Common::StackLock lock(_mutex); pSnd->priority = prio; + pSnd->time = ++_timeCounter; sortPlayList(); } @@ -905,12 +940,12 @@ int ChannelRemapping::lowestPrio() const { } -void SciMusic::remapChannels() { +void SciMusic::remapChannels(bool mainThread) { if (_soundVersion <= SCI_VERSION_0_LATE) return; - // NB: This function should only be called from the main thread, - // with _mutex locked + // NB: This function should only be called with _mutex locked + // Make sure to set the mainThread argument correctly. ChannelRemapping *map = determineChannelMap(); @@ -963,9 +998,9 @@ void SciMusic::remapChannels() { for (int j = 0; j < 16; ++j) { if (!channelMapped[j]) { - song->pMidiParser->mainThreadBegin(); + if (mainThread) song->pMidiParser->mainThreadBegin(); song->pMidiParser->remapChannel(j, -1); - song->pMidiParser->mainThreadEnd(); + if (mainThread) song->pMidiParser->mainThreadEnd(); #ifdef DEBUG_REMAP if (channelUsed[j]) debug(" Unmapping song %d, channel %d", songIndex, j); @@ -997,9 +1032,9 @@ void SciMusic::remapChannels() { #ifdef DEBUG_REMAP debug(" Mapping (dontRemap) song %d, channel %d to device channel %d", songIndex, _channelMap[i]._channel, i); #endif - _channelMap[i]._song->pMidiParser->mainThreadBegin(); + if (mainThread) _channelMap[i]._song->pMidiParser->mainThreadBegin(); _channelMap[i]._song->pMidiParser->remapChannel(_channelMap[i]._channel, i); - _channelMap[i]._song->pMidiParser->mainThreadEnd(); + if (mainThread) _channelMap[i]._song->pMidiParser->mainThreadEnd(); } } @@ -1052,9 +1087,9 @@ void SciMusic::remapChannels() { #ifdef DEBUG_REMAP debug(" Mapping song %d, channel %d to device channel %d", songIndex, _channelMap[j]._channel, j); #endif - _channelMap[j]._song->pMidiParser->mainThreadBegin(); + if (mainThread) _channelMap[j]._song->pMidiParser->mainThreadBegin(); _channelMap[j]._song->pMidiParser->remapChannel(_channelMap[j]._channel, j); - _channelMap[j]._song->pMidiParser->mainThreadEnd(); + if (mainThread) _channelMap[j]._song->pMidiParser->mainThreadEnd(); break; } } @@ -1062,9 +1097,9 @@ void SciMusic::remapChannels() { } // And finally, stop any empty channels - for (int i = _driverFirstChannel; i <= _driverLastChannel; ++i) { - if (!_channelMap[i]._song) - resetDeviceChannel(i); + for (int i = _driverLastChannel; i >= _driverFirstChannel; --i) { + if (!_channelMap[i]._song && currentMap[i]._song) + resetDeviceChannel(i, mainThread); } delete map; @@ -1105,7 +1140,8 @@ ChannelRemapping *SciMusic::determineChannelMap() { #ifdef DEBUG_REMAP - debug(" Song %d (%p), prio %d", songIndex, (void*)song, song->priority); + const char* name = g_sci->getEngineState()->_segMan->getObjectName(song->soundObj); + debug(" Song %d (%p) [%s], prio %d%s", songIndex, (void*)song, name, song->priority, song->playBed ? ", bed" : ""); #endif // Store backup. If we fail to map this song, we will revert to this. @@ -1123,8 +1159,10 @@ ChannelRemapping *SciMusic::determineChannelMap() { if (channel._mute) continue; + bool dontRemap = channel._dontRemap || song->playBed; + #ifdef DEBUG_REMAP - debug(" Channel %d: prio %d, %d voice%s%s", c, channel._prio, channel._voices, channel._voices == 1 ? "" : "s", channel._dontRemap ? ", dontRemap" : "" ); + debug(" Channel %d: prio %d, %d voice%s%s", c, channel._prio, channel._voices, channel._voices == 1 ? "" : "s", dontRemap ? ", dontRemap" : "" ); #endif DeviceChannelUsage dc = { song, c }; @@ -1132,7 +1170,7 @@ ChannelRemapping *SciMusic::determineChannelMap() { // our target int devChannel = -1; - if (channel._dontRemap && map->_map[c]._song == 0) { + if (dontRemap && map->_map[c]._song == 0) { // unremappable channel, with channel still free devChannel = c; } @@ -1192,8 +1230,12 @@ ChannelRemapping *SciMusic::determineChannelMap() { int neededVoices = channel._voices; // do we have enough free voices? if (map->_freeVoices < neededVoices) { - // We only care for essential channels - if (prio > 0) { + // We only care for essential channels. + // Note: In early SCI1 interpreters, a song started by 'playBed' + // would not be skipped even if some channels couldn't be + // mapped due to voice limits. So, we treat all channels as + // non-essential here for playBed songs. + if (prio > 0 || (song->playBed && _soundVersion <= SCI_VERSION_1_EARLY)) { #ifdef DEBUG_REMAP debug(" not enough voices; need %d, have %d. Skipping this channel.", neededVoices, map->_freeVoices); #endif @@ -1229,10 +1271,10 @@ ChannelRemapping *SciMusic::determineChannelMap() { map->_map[devChannel] = dc; map->_voices[devChannel] = neededVoices; map->_prio[devChannel] = prio; - map->_dontRemap[devChannel] = channel._dontRemap; + map->_dontRemap[devChannel] = dontRemap; map->_freeVoices -= neededVoices; - if (!channel._dontRemap || devChannel == c) { + if (!dontRemap || devChannel == c) { // If this channel fits here, we're done. #ifdef DEBUG_REMAP debug(" OK"); @@ -1285,14 +1327,18 @@ ChannelRemapping *SciMusic::determineChannelMap() { return map; } -void SciMusic::resetDeviceChannel(int devChannel) { - // NB: This function should only be called from the main thread - +void SciMusic::resetDeviceChannel(int devChannel, bool mainThread) { assert(devChannel >= 0 && devChannel <= 0x0F); - putMidiCommandInQueue(0x0040B0 | devChannel); // sustain off - putMidiCommandInQueue(0x007BB0 | devChannel); // notes off - putMidiCommandInQueue(0x004BB0 | devChannel); // release voices + if (mainThread) { + putMidiCommandInQueue(0x0040B0 | devChannel); // sustain off + putMidiCommandInQueue(0x007BB0 | devChannel); // notes off + putMidiCommandInQueue(0x004BB0 | devChannel); // release voices + } else { + _pMidiDrv->send(0x0040B0 | devChannel); // sustain off + _pMidiDrv->send(0x007BB0 | devChannel); // notes off + _pMidiDrv->send(0x004BB0 | devChannel); // release voices + } } diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h index 4e44074630..a610f32d89 100644 --- a/engines/sci/sound/music.h +++ b/engines/sci/sound/music.h @@ -75,6 +75,8 @@ public: SoundResource *soundRes; uint16 resourceId; + int time; // "tim"estamp to indicate in which order songs have been added + bool isQueued; // for SCI0 only! uint16 dataInc; @@ -85,6 +87,8 @@ public: int16 volume; int16 hold; int8 reverb; + bool playBed; + bool overridePriority; // Use soundObj's priority instead of resource's int16 pauseCounter; uint sampleLoopCounter; @@ -224,6 +228,8 @@ public: byte getCurrentReverb(); + void needsRemap() { _needsRemap = true; } + virtual void saveLoadWithSerializer(Common::Serializer &ser); // Mutex for music code. Used to guard access to the song playlist, to the @@ -245,9 +251,9 @@ protected: bool _useDigitalSFX; // remapping: - void remapChannels(); + void remapChannels(bool mainThread = true); ChannelRemapping *determineChannelMap(); - void resetDeviceChannel(int devChannel); + void resetDeviceChannel(int devChannel, bool mainThread); private: MusicList _playList; @@ -256,6 +262,7 @@ private: MusicEntry *_usedChannel[16]; int8 _channelRemap[16]; int8 _globalReverb; + bool _needsRemap; DeviceChannelUsage _channelMap[16]; @@ -266,6 +273,9 @@ private: int _driverLastChannel; MusicEntry *_currentlyPlayingSample; + + int _timeCounter; // Used to keep track of the order in which MusicEntries + // are added, for priority purposes. }; } // End of namespace Sci diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index 73e0a23a6a..64991cbf5c 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -142,11 +142,14 @@ void SoundCommandParser::processInitSound(reg_t obj) { reg_t SoundCommandParser::kDoSoundPlay(int argc, reg_t *argv, reg_t acc) { debugC(kDebugLevelSound, "kDoSound(play): %04x:%04x", PRINT_REG(argv[0])); - processPlaySound(argv[0]); + bool playBed = false; + if (argc >= 2 && !argv[1].isNull()) + playBed = true; + processPlaySound(argv[0], playBed); return acc; } -void SoundCommandParser::processPlaySound(reg_t obj) { +void SoundCommandParser::processPlaySound(reg_t obj, bool playBed) { MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { warning("kDoSound(play): Slot not found (%04x:%04x), initializing it manually", PRINT_REG(obj)); @@ -181,15 +184,24 @@ void SoundCommandParser::processPlaySound(reg_t obj) { } musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); - musicSlot->priority = readSelectorValue(_segMan, obj, SELECTOR(priority)); + + // Get song priority from either obj or soundRes + byte resourcePriority = musicSlot->soundRes->getSoundPriority(); + if (!musicSlot->overridePriority && resourcePriority != 0xFF) { + musicSlot->priority = resourcePriority; + } else { + musicSlot->priority = readSelectorValue(_segMan, obj, SELECTOR(priority)); + } + // Reset hold when starting a new song. kDoSoundSetHold is always called after // kDoSoundPlay to set it properly, if needed. Fixes bug #3413589. musicSlot->hold = -1; + musicSlot->playBed = playBed; if (_soundVersion >= SCI_VERSION_1_EARLY) musicSlot->volume = readSelectorValue(_segMan, obj, SELECTOR(vol)); - debugC(kDebugLevelSound, "kDoSound(play): %04x:%04x number %d, loop %d, prio %d, vol %d", PRINT_REG(obj), - resourceId, musicSlot->loop, musicSlot->priority, musicSlot->volume); + debugC(kDebugLevelSound, "kDoSound(play): %04x:%04x number %d, loop %d, prio %d, vol %d, bed %d", PRINT_REG(obj), + resourceId, musicSlot->loop, musicSlot->priority, musicSlot->volume, playBed ? 1 : 0); _music->soundPlay(musicSlot); @@ -538,7 +550,7 @@ void SoundCommandParser::processUpdateCues(reg_t obj) { if (_soundVersion >= SCI_VERSION_1_EARLY) { writeSelectorValue(_segMan, obj, SELECTOR(min), musicSlot->ticker / 3600); writeSelectorValue(_segMan, obj, SELECTOR(sec), musicSlot->ticker % 3600 / 60); - writeSelectorValue(_segMan, obj, SELECTOR(frame), musicSlot->ticker); + writeSelectorValue(_segMan, obj, SELECTOR(frame), musicSlot->ticker % 60 / 2); } } @@ -673,23 +685,19 @@ reg_t SoundCommandParser::kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc) } if (value == -1) { - uint16 resourceId = musicSlot->resourceId; + musicSlot->overridePriority = false; + musicSlot->priority = 0; - // Set priority from the song data - Resource *song = _resMan->findResource(ResourceId(kResourceTypeSound, resourceId), 0); - if (song->data[0] == 0xf0) - _music->soundSetPriority(musicSlot, song->data[1]); - else - warning("kDoSound(setPriority): Attempt to unset song priority when there is no built-in value"); + // NB: It seems SSCI doesn't actually reset the priority here. - //pSnd->prio=0;field_15B=0 writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) & 0xFD); } else { // Scripted priority + musicSlot->overridePriority = true; - //pSnd->field_15B=1; writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) | 2); - //DoSOund(0xF,hobj,w) + + _music->soundSetPriority(musicSlot, value); } return acc; } @@ -777,6 +785,8 @@ void SoundCommandParser::stopAllSounds() { } void SoundCommandParser::startNewSound(int number) { + // NB: This is only used by the debugging console. + Common::StackLock lock(_music->_mutex); // Overwrite the first sound in the playlist @@ -785,7 +795,7 @@ void SoundCommandParser::startNewSound(int number) { processDisposeSound(soundObj); writeSelectorValue(_segMan, soundObj, SELECTOR(number), number); processInitSound(soundObj); - processPlaySound(soundObj); + processPlaySound(soundObj, false); } void SoundCommandParser::setMasterVolume(int vol) { diff --git a/engines/sci/sound/soundcmd.h b/engines/sci/sound/soundcmd.h index 4effda68e4..5bb7cf2cb1 100644 --- a/engines/sci/sound/soundcmd.h +++ b/engines/sci/sound/soundcmd.h @@ -63,7 +63,7 @@ public: void printPlayList(Console *con); void printSongInfo(reg_t obj, Console *con); - void processPlaySound(reg_t obj); + void processPlaySound(reg_t obj, bool playBed); void processStopSound(reg_t obj, bool sampleFinishedPlaying); void initSoundResource(MusicEntry *newSound); |