aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorJohannes Schickel2014-06-04 01:49:31 +0200
committerJohannes Schickel2014-06-04 01:49:31 +0200
commitf78e12cd5c5abd99a0e505e77bbadeb3398f9c06 (patch)
tree812cd9dd1246eb618f19b20ee1e2c7c7818d128f /engines
parent02f47dd497710330f89c28d640e21623da180c06 (diff)
downloadscummvm-rg350-f78e12cd5c5abd99a0e505e77bbadeb3398f9c06.tar.gz
scummvm-rg350-f78e12cd5c5abd99a0e505e77bbadeb3398f9c06.tar.bz2
scummvm-rg350-f78e12cd5c5abd99a0e505e77bbadeb3398f9c06.zip
SCUMM: Dynamically allocate hw channels for SFX in AD player.
Diffstat (limited to 'engines')
-rw-r--r--engines/scumm/players/player_ad.cpp77
-rw-r--r--engines/scumm/players/player_ad.h13
2 files changed, 77 insertions, 13 deletions
diff --git a/engines/scumm/players/player_ad.cpp b/engines/scumm/players/player_ad.cpp
index 33dbd0b55e..6c3c7f9458 100644
--- a/engines/scumm/players/player_ad.cpp
+++ b/engines/scumm/players/player_ad.cpp
@@ -66,9 +66,11 @@ Player_AD::Player_AD(ScummEngine *scumm, Audio::Mixer *mixer)
for (int i = 0; i < ARRAYSIZE(_sfx); ++i) {
_sfx[i].resource = -1;
for (int j = 0; j < ARRAYSIZE(_sfx[i].channels); ++j) {
- _sfx[i].channels[j].hardwareChannel = i * 3 + j;
+ _sfx[i].channels[j].hardwareChannel = -1;
}
}
+
+ memset(_hwChannels, 0, sizeof(_hwChannels));
}
Player_AD::~Player_AD() {
@@ -121,13 +123,13 @@ void Player_AD::startSound(int sound) {
return;
}
- // Lock the new resource
+ // Try to start sfx playback
sfx->resource = sound;
sfx->priority = priority;
- _vm->_res->lock(rtSound, sound);
-
- // Start the actual sfx resource
- startSfx(sfx, res);
+ if (startSfx(sfx, res)) {
+ // Lock the new resource
+ _vm->_res->lock(rtSound, sound);
+ }
}
}
@@ -246,6 +248,43 @@ void Player_AD::setupVolume() {
}
}
+int Player_AD::allocateHWChannel(int priority, SfxSlot *owner) {
+ // First pass: Check whether there's any unallocated channel
+ for (int i = 0; i < ARRAYSIZE(_hwChannels); ++i) {
+ if (!_hwChannels[i].allocated) {
+ _hwChannels[i].allocated = true;
+ _hwChannels[i].priority = priority;
+ _hwChannels[i].sfxOwner = owner;
+ return i;
+ }
+ }
+
+ // Second pass: Reassign channels based on priority
+ for (int i = 0; i < ARRAYSIZE(_hwChannels); ++i) {
+ if (_hwChannels[i].priority <= priority) {
+ // In case the HW channel belongs to a SFX we will completely
+ // stop playback of that SFX.
+ // TODO: Maybe be more fine grained in the future and allow
+ // detachment of individual channels of a SFX?
+ if (_hwChannels[i].sfxOwner) {
+ stopSfx(_hwChannels[i].sfxOwner);
+ }
+ _hwChannels[i].allocated = true;
+ _hwChannels[i].priority = priority;
+ _hwChannels[i].sfxOwner = owner;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void Player_AD::freeHWChannel(int channel) {
+ assert(_hwChannels[channel].allocated);
+ _hwChannels[channel].allocated = false;
+ _hwChannels[channel].sfxOwner = nullptr;
+}
+
void Player_AD::writeReg(int r, int v) {
if (r >= 0 && r < ARRAYSIZE(_registerBackUpTable)) {
_registerBackUpTable[r] = v;
@@ -549,19 +588,21 @@ Player_AD::SfxSlot *Player_AD::allocateSfxSlot(int priority) {
return nullptr;
}
-void Player_AD::startSfx(SfxSlot *sfx, const byte *resource) {
+bool Player_AD::startSfx(SfxSlot *sfx, const byte *resource) {
writeReg(0xBD, 0x00);
- // Clear the channel.
+ // Clear the channels.
sfx->channels[0].state = kChannelStateOff;
sfx->channels[1].state = kChannelStateOff;
sfx->channels[2].state = kChannelStateOff;
- clearChannel(sfx->channels[0]);
- clearChannel(sfx->channels[1]);
- clearChannel(sfx->channels[2]);
-
// Set up the first channel to pick up playback.
+ // Try to allocate a hardware channel.
+ sfx->channels[0].hardwareChannel = allocateHWChannel(sfx->priority, sfx);
+ if (sfx->channels[0].hardwareChannel == -1) {
+ ::debugC(3, DEBUG_SOUND, "AD No hardware channel available");
+ return false;
+ }
sfx->channels[0].currentOffset = sfx->channels[0].startOffset = resource + 2;
sfx->channels[0].state = kChannelStateParse;
@@ -592,6 +633,11 @@ void Player_AD::startSfx(SfxSlot *sfx, const byte *resource) {
if (curChannel >= 3) {
error("AD SFX resource %d uses more than 3 channels", sfx->resource);
}
+ sfx->channels[curChannel].hardwareChannel = allocateHWChannel(sfx->priority, sfx);
+ if (sfx->channels[curChannel].hardwareChannel == -1) {
+ ::debugC(3, DEBUG_SOUND, "AD No hardware channel available");
+ return false;
+ }
sfx->channels[curChannel].currentOffset = bufferPosition;
sfx->channels[curChannel].startOffset = bufferPosition;
sfx->channels[curChannel].state = kChannelStateParse;
@@ -599,6 +645,8 @@ void Player_AD::startSfx(SfxSlot *sfx, const byte *resource) {
break;
}
}
+
+ return true;
}
void Player_AD::stopSfx(SfxSlot *sfx) {
@@ -612,6 +660,11 @@ void Player_AD::stopSfx(SfxSlot *sfx) {
clearChannel(sfx->channels[i]);
sfx->channels[i].state = kChannelStateOff;
}
+
+ if (sfx->channels[i].hardwareChannel != -1) {
+ freeHWChannel(sfx->channels[i].hardwareChannel);
+ sfx->channels[i].hardwareChannel = -1;
+ }
}
// 2. step: Unlock the resource.
diff --git a/engines/scumm/players/player_ad.h b/engines/scumm/players/player_ad.h
index 73f3f86cea..8994be7ec3 100644
--- a/engines/scumm/players/player_ad.h
+++ b/engines/scumm/players/player_ad.h
@@ -80,6 +80,17 @@ private:
int _soundPlaying;
int _engineMusicTimer;
+ struct SfxSlot;
+
+ struct HardwareChannel {
+ bool allocated;
+ int priority;
+ SfxSlot *sfxOwner;
+ } _hwChannels[9];
+
+ int allocateHWChannel(int priority, SfxSlot *owner = nullptr);
+ void freeHWChannel(int channel);
+
// AdLib register utilities
uint8 _registerBackUpTable[256];
void writeReg(int r, int v);
@@ -173,7 +184,7 @@ private:
} _sfx[3];
SfxSlot *allocateSfxSlot(int priority);
- void startSfx(SfxSlot *sfx, const byte *resource);
+ bool startSfx(SfxSlot *sfx, const byte *resource);
void stopSfx(SfxSlot *sfx);
void updateSfx();