From f78e12cd5c5abd99a0e505e77bbadeb3398f9c06 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Wed, 4 Jun 2014 01:49:31 +0200 Subject: SCUMM: Dynamically allocate hw channels for SFX in AD player. --- engines/scumm/players/player_ad.cpp | 77 +++++++++++++++++++++++++++++++------ engines/scumm/players/player_ad.h | 13 ++++++- 2 files changed, 77 insertions(+), 13 deletions(-) (limited to 'engines') 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(); -- cgit v1.2.3