Permalink
Browse files

BLADERUNNER: Cleanup of audio code

Separated audio cache.
Fixed bug in the audio cache where still used sound might get freed.
Fixes crashes when engine is unloading which were caused
by a race condition between the timer code and engine teardown code.
  • Loading branch information...
peterkohaut committed Feb 10, 2019
1 parent 22e5913 commit b14fbaa72b3218862a533dd7f7c0e97e1bed4df7
@@ -22,7 +22,7 @@

#include "bladerunner/aud_stream.h"

#include "bladerunner/audio_player.h"
#include "bladerunner/audio_cache.h"

#include "common/util.h"

@@ -36,12 +36,12 @@ AudStream::AudStream(byte *data, int overrideFrequency) {
init(data);
}

AudStream::AudStream(AudioCache *cache, int32 hash) {
AudStream::AudStream(AudioCache *cache, int32 hash, int overrideFrequency) {
assert(cache != nullptr);

_cache = cache;
_hash = hash;
_overrideFrequency = -1;
_overrideFrequency = overrideFrequency;

_cache->incRef(_hash);

@@ -128,7 +128,7 @@ bool AudStream::rewind() {
}

int AudStream::getLength() const {
int bytesPerSecond = _frequency;
int bytesPerSecond = _overrideFrequency > 0 ? _overrideFrequency : _frequency;
if (_flags & 1) { // 16 bit
bytesPerSecond *= 2;
}
@@ -53,7 +53,7 @@ class AudStream : public Audio::RewindableAudioStream {

public:
AudStream(byte *data, int overrideFrequency = -1);
AudStream(AudioCache *cache, int32 hash);
AudStream(AudioCache *cache, int32 hash, int overrideFrequency = -1);
~AudStream();

int readBuffer(int16 *buffer, const int numSamples);
@@ -0,0 +1,130 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/

#include "bladerunner/audio_cache.h"

#include "bladerunner/bladerunner.h"

namespace BladeRunner {

AudioCache::AudioCache(BladeRunnerEngine *vm) :
_vm(vm),
_totalSize(0),
_maxSize(2457600),
_accessCounter(0) {}

AudioCache::~AudioCache() {
for (uint i = 0; i != _cacheItems.size(); ++i) {
free(_cacheItems[i].data);
}
}

bool AudioCache::canAllocate(uint32 size) const {
Common::StackLock lock(_mutex);

return _maxSize - _totalSize >= size;
}

bool AudioCache::dropOldest() {
Common::StackLock lock(_mutex);

if (_cacheItems.size() == 0)
return false;

int oldest = -1;
for (uint i = 1; i != _cacheItems.size(); ++i) {
if (_cacheItems[i].refs == 0) {
if (oldest == -1 || _cacheItems[i].lastAccess < _cacheItems[oldest].lastAccess) {
oldest = i;
}
}
}

if (oldest == -1) {
return false;
}

memset(_cacheItems[oldest].data, 0x00, _cacheItems[oldest].size);
free(_cacheItems[oldest].data);
_totalSize -= _cacheItems[oldest].size;
_cacheItems.remove_at(oldest);
return true;
}

byte *AudioCache::findByHash(int32 hash) {
Common::StackLock lock(_mutex);

for (uint i = 0; i != _cacheItems.size(); ++i) {
if (_cacheItems[i].hash == hash) {
_cacheItems[i].lastAccess = _accessCounter++;
return _cacheItems[i].data;
}
}

return nullptr;
}

void AudioCache::storeByHash(int32 hash, Common::SeekableReadStream *stream) {
Common::StackLock lock(_mutex);

uint32 size = stream->size();
byte *data = (byte *)malloc(size);
stream->read(data, size);

cacheItem item = {
hash,
0,
_accessCounter++,
data,
size
};

_cacheItems.push_back(item);
_totalSize += size;
}

void AudioCache::incRef(int32 hash) {
Common::StackLock lock(_mutex);

for (uint i = 0; i != _cacheItems.size(); ++i) {
if (_cacheItems[i].hash == hash) {
_cacheItems[i].refs++;
return;
}
}
assert(false && "AudioCache::incRef: hash not found");
}

void AudioCache::decRef(int32 hash) {
Common::StackLock lock(_mutex);

for (uint i = 0; i != _cacheItems.size(); ++i) {
if (_cacheItems[i].hash == hash) {
assert(_cacheItems[i].refs > 0);
_cacheItems[i].refs--;
return;
}
}
assert(false && "AudioCache::decRef: hash not found");
}

} // End of namespace BladeRunner
@@ -0,0 +1,70 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/

#ifndef BLADERUNNER_AUDIO_CACHE_H
#define BLADERUNNER_AUDIO_CACHE_H

#include "common/array.h"
#include "common/mutex.h"

namespace BladeRunner {

class BladeRunnerEngine;
class AudioCache;

/*
* This is a poor imitation of Bladerunner's resource cache
*/
class AudioCache {
struct cacheItem {
int32 hash;
int refs;
uint lastAccess;
byte *data;
uint32 size;
};

BladeRunnerEngine *_vm;

Common::Mutex _mutex;
Common::Array<cacheItem> _cacheItems;

uint32 _totalSize;
uint32 _maxSize;
uint32 _accessCounter;

public:
AudioCache(BladeRunnerEngine *vm);
~AudioCache();

bool canAllocate(uint32 size) const;
bool dropOldest();
byte *findByHash(int32 hash);
void storeByHash(int32 hash, Common::SeekableReadStream *stream);

void incRef(int32 hash);
void decRef(int32 hash);
};

} // End of namespace BladeRunner

#endif
Oops, something went wrong.

0 comments on commit b14fbaa

Please sign in to comment.