aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/sound/music.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/sound/music.cpp')
-rw-r--r--engines/sci/sound/music.cpp100
1 files changed, 73 insertions, 27 deletions
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
+ }
}