scummvm/mixer/mixer.cpp

431 lines
11 KiB
C++
Raw Normal View History

// Residual - Virtual machine to run LucasArts' 3D adventure games
2006-02-15 19:43:48 +00:00
// Copyright (C) 2003-2006 The ScummVM-Residual Team (www.scummvm.org)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2005-01-01 12:27:57 +00:00
#include "stdafx.h"
#include "bits.h"
#include "debug.h"
2006-02-05 17:48:19 +00:00
#include "driver.h"
2005-01-01 12:27:57 +00:00
#include "mixer/mixer.h"
#include "mixer/rate.h"
#include "mixer/audiostream.h"
SoundMixer *g_mixer = NULL;
/**
* Channels used by the sound mixer.
*/
class Channel {
2004-02-01 10:48:44 +00:00
private:
SoundMixer *_mixer;
PlayingSoundHandle *_handle;
2004-02-01 10:48:44 +00:00
bool _autofreeStream;
bool _permanent;
byte _volume;
2004-02-01 10:48:44 +00:00
int8 _balance;
bool _paused;
2004-02-01 10:48:44 +00:00
int _id;
uint32 _samplesConsumed;
uint32 _samplesDecoded;
2004-02-01 10:48:44 +00:00
protected:
RateConverter *_converter;
AudioStream *_input;
public:
2004-02-01 10:48:44 +00:00
Channel(SoundMixer *mixer, PlayingSoundHandle *handle, bool isMusic, int id = -1);
Channel(SoundMixer *mixer, PlayingSoundHandle *handle, AudioStream *input, bool autofreeStream, bool isMusic, bool reverseStereo = false, int id = -1, bool permanent = false);
virtual ~Channel();
2004-02-01 10:48:44 +00:00
void mix(int16 *data, uint len);
bool isPermanent() const {
return _permanent;
}
2004-02-01 10:48:44 +00:00
bool isFinished() const {
return _input->endOfStream();
}
void pause(bool paused) {
_paused = paused;
}
2004-02-01 10:48:44 +00:00
bool isPaused() {
return _paused;
}
2004-02-01 10:48:44 +00:00
void setVolume(const byte volume) {
_volume = volume;
}
2004-02-01 10:48:44 +00:00
void setBalance(const int8 balance) {
_balance = balance;
}
2004-02-01 10:48:44 +00:00
int getId() const {
return _id;
}
uint32 getElapsedTime();
};
SoundMixer::SoundMixer() {
2006-02-13 21:15:32 +00:00
_mutex = g_driver->createMutex();
_premixChannel = NULL;
_globalVolume = 0;
_paused = false;
2004-02-01 10:48:44 +00:00
for (int i = 0; i != NUM_CHANNELS; i++)
_channels[i] = NULL;
2004-02-01 10:48:44 +00:00
2006-02-05 17:48:19 +00:00
_mixerReady = g_driver->setSoundCallback(mixCallback, this);
_outputRate = (uint)g_driver->getOutputSampleRate();
if (_outputRate == 0)
error("OSystem returned invalid sample rate");
}
SoundMixer::~SoundMixer() {
2006-02-05 17:48:19 +00:00
g_driver->clearSoundCallback();
stopAll(true);
delete _premixChannel;
_premixChannel = NULL;
2006-02-13 21:15:32 +00:00
g_driver->deleteMutex(_mutex);
}
bool SoundMixer::isPaused() {
return _paused;
}
void SoundMixer::setupPremix(AudioStream *stream) {
StackLock lock(_mutex);
delete _premixChannel;
_premixChannel = NULL;
if (stream == NULL)
return;
// Create the channel
_premixChannel = new Channel(this, NULL, stream, false, true);
}
2004-02-01 10:48:44 +00:00
void SoundMixer::insertChannel(PlayingSoundHandle *handle, Channel *chan) {
int index = -1;
for (int i = 0; i != NUM_CHANNELS; i++) {
if (_channels[i] == NULL) {
index = i;
break;
}
}
if (index == -1) {
warning("SoundMixer::out of mixer slots");
delete chan;
2004-02-01 10:48:44 +00:00
return;
}
_channels[index] = chan;
if (handle)
2004-02-01 10:48:44 +00:00
handle->setIndex(index);
}
void SoundMixer::playRaw(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags,
int id, byte volume, int8 balance, uint32 loopStart, uint32 loopEnd) {
StackLock lock(_mutex);
// Prevent duplicate sounds
if (id != -1) {
for (int i = 0; i != NUM_CHANNELS; i++)
if (_channels[i] != NULL && _channels[i]->getId() == id) {
2004-02-01 10:48:44 +00:00
if ((flags & SoundMixer::FLAG_AUTOFREE) != 0)
free(sound);
return;
}
}
2004-02-01 10:48:44 +00:00
// Create the input stream
AudioStream *input;
if (flags & SoundMixer::FLAG_LOOP) {
if (loopEnd == 0) {
input = makeLinearInputStream(rate, flags, (byte *)sound, size, 0, size);
} else {
assert(loopStart < loopEnd && loopEnd <= size);
input = makeLinearInputStream(rate, flags, (byte *)sound, size, loopStart, loopEnd - loopStart);
}
} else {
input = makeLinearInputStream(rate, flags, (byte *)sound, size, 0, 0);
}
// Create the channel
Channel *chan = new Channel(this, handle, input, true, false, (flags & SoundMixer::FLAG_REVERSE_STEREO) != 0, id);
chan->setVolume(volume);
chan->setBalance(balance);
insertChannel(handle, chan);
}
void SoundMixer::playInputStream(PlayingSoundHandle *handle, AudioStream *input, bool isMusic,
int id, byte volume, int8 balance, bool autofreeStream, bool permanent) {
StackLock lock(_mutex);
if (input == NULL) {
warning("input stream is NULL");
2004-02-01 10:48:44 +00:00
return;
}
2004-02-01 10:48:44 +00:00
// Prevent duplicate sounds
if (id != -1) {
for (int i = 0; i != NUM_CHANNELS; i++)
if (_channels[i] != NULL && _channels[i]->getId() == id) {
2004-02-01 10:48:44 +00:00
if (autofreeStream)
delete input;
return;
}
}
// Create the channel
Channel *chan = new Channel(this, handle, input, autofreeStream, isMusic, false, id, permanent);
2004-02-01 10:48:44 +00:00
chan->setVolume(volume);
chan->setBalance(balance);
insertChannel(handle, chan);
}
void SoundMixer::mix(int16 *buf, uint len) {
StackLock lock(_mutex);
// zero the buf
memset(buf, 0, 2 * len * sizeof(int16));
if (!_paused) {
if (_premixChannel)
_premixChannel->mix(buf, len);
2004-02-01 10:48:44 +00:00
// now mix all channels
for (int i = 0; i != NUM_CHANNELS; i++) {
2004-02-01 10:48:44 +00:00
if (_channels[i]) {
if (_channels[i]->isFinished()) {
delete _channels[i];
_channels[i] = NULL;
2004-02-01 10:48:44 +00:00
} else if (!_channels[i]->isPaused())
_channels[i]->mix(buf, len);
}
}
}
}
void SoundMixer::mixCallback(void *s, byte *samples, int len) {
assert(s);
assert(samples);
// Len is the number of bytes in the buffer; we divide it by
// four to get the number of samples (stereo 16 bit).
((SoundMixer *)s)->mix((int16 *)samples, len >> 2);
}
void SoundMixer::stopAll(bool force) {
StackLock lock(_mutex);
for (int i = 0; i != NUM_CHANNELS; i++) {
if (_channels[i] != NULL) {
if (force || !_channels[i]->isPermanent()) {
delete _channels[i];
_channels[i] = NULL;
}
2004-02-01 10:48:44 +00:00
}
}
}
void SoundMixer::stopID(int id) {
StackLock lock(_mutex);
for (int i = 0; i != NUM_CHANNELS; i++) {
if (_channels[i] != NULL && _channels[i]->getId() == id) {
2004-02-01 10:48:44 +00:00
delete _channels[i];
_channels[i] = NULL;
}
}
}
void SoundMixer::stopHandle(PlayingSoundHandle handle) {
StackLock lock(_mutex);
// Simply ignore stop requests for handles of sounds that already terminated
2004-02-01 10:48:44 +00:00
if (!handle.isActive())
return;
2004-02-01 10:48:44 +00:00
int index = handle.getIndex();
if ((index < 0) || (index >= NUM_CHANNELS)) {
warning("soundMixer::stopHandle has invalid index %d", index);
return;
}
2004-02-01 10:48:44 +00:00
if (_channels[index]) {
delete _channels[index];
_channels[index] = NULL;
2004-02-01 10:48:44 +00:00
}
}
void SoundMixer::setChannelVolume(PlayingSoundHandle handle, byte volume) {
StackLock lock(_mutex);
2004-02-01 10:48:44 +00:00
if (!handle.isActive())
return;
2004-02-01 10:48:44 +00:00
int index = handle.getIndex();
if ((index < 0) || (index >= NUM_CHANNELS)) {
warning("soundMixer::setChannelVolume has invalid index %d", index);
return;
}
if (_channels[index])
2004-02-01 10:48:44 +00:00
_channels[index]->setVolume(volume);
}
2004-02-01 10:48:44 +00:00
void SoundMixer::setChannelBalance(PlayingSoundHandle handle, int8 balance) {
StackLock lock(_mutex);
2004-02-01 10:48:44 +00:00
if (!handle.isActive())
return;
2004-02-01 10:48:44 +00:00
int index = handle.getIndex();
if ((index < 0) || (index >= NUM_CHANNELS)) {
2004-02-01 10:48:44 +00:00
warning("soundMixer::setChannelBalance has invalid index %d", index);
return;
}
if (_channels[index])
2004-02-01 10:48:44 +00:00
_channels[index]->setBalance(balance);
}
void SoundMixer::pauseAll(bool paused) {
_paused = paused;
}
void SoundMixer::pauseID(int id, bool paused) {
StackLock lock(_mutex);
for (int i = 0; i != NUM_CHANNELS; i++) {
if (_channels[i] != NULL && _channels[i]->getId() == id) {
_channels[i]->pause(paused);
return;
}
}
}
void SoundMixer::pauseHandle(PlayingSoundHandle handle, bool paused) {
StackLock lock(_mutex);
// Simply ignore pause/unpause requests for handles of sound that alreayd terminated
2004-02-01 10:48:44 +00:00
if (!handle.isActive())
return;
2004-02-01 10:48:44 +00:00
int index = handle.getIndex();
if ((index < 0) || (index >= NUM_CHANNELS)) {
warning("soundMixer::pauseHandle has invalid index %d", index);
return;
}
if (_channels[index])
_channels[index]->pause(paused);
}
bool SoundMixer::isSoundIDActive(int id) {
StackLock lock(_mutex);
for (int i = 0; i != NUM_CHANNELS; i++)
if (_channels[i] && _channels[i]->getId() == id)
return true;
return false;
}
void SoundMixer::setVolume(int volume) {
// Check range
if (volume > 256)
volume = 256;
else if (volume < 0)
volume = 0;
_globalVolume = volume;
}
2005-01-12 22:37:24 +00:00
Channel::Channel(SoundMixer *mixer, PlayingSoundHandle *handle, bool /*isMusic*/, int id)
: _mixer(mixer), _handle(handle), _autofreeStream(true),
_volume(255), _balance(0), _paused(false), _id(id), _samplesConsumed(0),
_samplesDecoded(0), _converter(0), _input(NULL) {
2004-02-01 10:48:44 +00:00
assert(mixer);
}
Channel::Channel(SoundMixer *mixer, PlayingSoundHandle *handle, AudioStream *input,
2005-01-12 22:37:24 +00:00
bool autofreeStream, bool /*isMusic*/, bool reverseStereo, int id, bool permanent)
: _mixer(mixer), _handle(handle), _autofreeStream(autofreeStream),
2005-01-12 22:37:24 +00:00
_permanent(permanent), _volume(255), _balance(0), _paused(false), _id(id),
_samplesConsumed(0), _samplesDecoded(0), _converter(0), _input(input) {
2004-02-01 10:48:44 +00:00
assert(mixer);
assert(input);
// Get a rate converter instance
_converter = makeRateConverter(_input->getRate(), mixer->getOutputRate(), _input->isStereo(), reverseStereo);
}
Channel::~Channel() {
delete _converter;
2004-02-01 10:48:44 +00:00
if (_autofreeStream)
delete _input;
if (_handle)
2004-02-01 10:48:44 +00:00
_handle->resetIndex();
}
/* len indicates the number of sample *pairs*. So a value of
10 means that the buffer contains twice 10 sample, each
16 bits, for a total of 40 bytes.
*/
void Channel::mix(int16 *data, uint len) {
assert(_input);
2004-02-01 10:48:44 +00:00
if (_input->endOfData()) {
// TODO: call drain method
} else {
assert(_converter);
2004-02-01 10:48:44 +00:00
// From the channel balance/volume and the global volume, we compute
// the effective volume for the left and right channel. Note the
// slightly odd divisor: the 255 reflects the fact that the maximal
// value for _volume is 255, while the 127 is there because the
// balance value ranges from -127 to 127. The mixer (music/sound)
// volume is in the range 0 - 256.
// Hence, the vol_l/vol_r values will be in that range, too
2004-02-01 10:48:44 +00:00
int vol = _mixer->getVolume() * _volume;
st_volume_t vol_l, vol_r;
if (_balance == 0) {
vol_l = vol / 255;
vol_r = vol / 255;
} else if (_balance < 0) {
vol_l = vol / 255;
vol_r = ((127 + _balance) * vol) / (255 * 127);
} else {
2004-02-01 10:48:44 +00:00
vol_l = ((127 - _balance) * vol) / (255 * 127);
vol_r = vol / 255;
}
_samplesConsumed = _samplesDecoded;
_converter->flow(*_input, data, len, vol_l, vol_r);
_samplesDecoded += len;
}
}