[Scummvm-cvs-logs] CVS: residual/mixer audiostream.cpp,NONE,1.1 audiostream.h,NONE,1.1 mixer.cpp,NONE,1.1 mixer.h,NONE,1.1 rate.cpp,NONE,1.1 rate.h,NONE,1.1
Pawel Kolodziejski
aquadran at users.sourceforge.net
Fri Dec 12 13:18:01 CET 2003
- Previous message: [Scummvm-cvs-logs] CVS: residual/mixer - New directory
- Next message: [Scummvm-cvs-logs] CVS: residual actor.cpp,1.16,1.17 bits.h,1.7,1.8 costume.cpp,1.10,1.11 lua.cpp,1.34,1.35 main.cpp,1.15,1.16 residual.vcproj,1.10,1.11 smush.cpp,1.1,1.2 smush.h,1.1,1.2 sound.cpp,1.4,1.5 sound.h,1.3,1.4 mixer.cpp,1.5,NONE mixer.h,1.5,NONE
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
Update of /cvsroot/scummvm/residual/mixer
In directory sc8-pr-cvs1:/tmp/cvs-serv13367/mixer
Added Files:
audiostream.cpp audiostream.h mixer.cpp mixer.h rate.cpp
rate.h
Log Message:
added mixer code, handle smush sound, handle smush filenames in lua
--- NEW FILE: audiostream.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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
#include "../stdafx.h"
#include "../debug.h"
#include "mixer.h"
#include "audiostream.h"
// This used to be an inline template function, but
// buggy template function handling in MSVC6 forced
// us to go with the macro approach. So far this is
// the only template function that MSVC6 seemed to
// compile incorrectly. Knock on wood.
#define READSAMPLE(is16Bit, isUnsigned, ptr) \
((is16Bit ? READ_BE_UINT16(ptr) : (*ptr << 8)) ^ (isUnsigned ? 0x8000 : 0))
template<bool stereo, bool is16Bit, bool isUnsigned>
class LinearMemoryStream : public AudioInputStream {
protected:
const byte *_ptr;
const byte *_end;
const byte *_loopPtr;
const byte *_loopEnd;
inline int16 readIntern() {
//assert(_ptr < _end);
int16 val = READSAMPLE(is16Bit, isUnsigned, _ptr);
_ptr += (is16Bit ? 2 : 1);
if (_loopPtr && eosIntern()) {
_ptr = _loopPtr;
_end = _loopEnd;
}
return val;
}
inline bool eosIntern() const { return _ptr >= _end; };
public:
LinearMemoryStream(const byte *ptr, uint len, uint loopOffset, uint loopLen)
: _ptr(ptr), _end(ptr+len), _loopPtr(0), _loopEnd(0) {
// Verify the buffer sizes are sane
if (is16Bit && stereo)
assert((len & 3) == 0 && (loopLen & 3) == 0);
else if (is16Bit || stereo)
assert((len & 1) == 0 && (loopLen & 1) == 0);
if (loopLen) {
_loopPtr = _ptr + loopOffset;
_loopEnd = _loopPtr + loopLen;
}
if (stereo) // Stereo requires even sized data
assert(len % 2 == 0);
}
int readBuffer(int16 *buffer, const int numSamples);
int16 read() { return readIntern(); }
bool eos() const { return eosIntern(); }
bool isStereo() const { return stereo; }
};
template<bool stereo, bool is16Bit, bool isUnsigned>
int LinearMemoryStream<stereo, is16Bit, isUnsigned>::readBuffer(int16 *buffer, const int numSamples) {
int samples = 0;
while (samples < numSamples && !eosIntern()) {
const int len = MIN(numSamples, samples + (int)(_end - _ptr) / (is16Bit ? 2 : 1));
while (samples < len) {
*buffer++ = READSAMPLE(is16Bit, isUnsigned, _ptr);
_ptr += (is16Bit ? 2 : 1);
samples++;
}
// Loop, if looping was specified
if (_loopPtr && eosIntern()) {
_ptr = _loopPtr;
_end = _loopEnd;
}
}
return samples;
}
// Wrapped memory stream, to be used by the ChannelStream class (and possibly others?)
template<bool stereo, bool is16Bit, bool isUnsigned>
class WrappedMemoryStream : public WrappedAudioInputStream {
protected:
byte *_bufferStart;
byte *_bufferEnd;
byte *_pos;
byte *_end;
inline int16 readIntern();
inline bool eosIntern() const { return _end == _pos; };
public:
WrappedMemoryStream(uint bufferSize);
~WrappedMemoryStream() { free(_bufferStart); }
int readBuffer(int16 *buffer, const int numSamples);
int16 read() { return readIntern(); }
bool eos() const { return eosIntern(); }
bool isStereo() const { return stereo; }
void append(const byte *data, uint32 len);
};
template<bool stereo, bool is16Bit, bool isUnsigned>
WrappedMemoryStream<stereo, is16Bit, isUnsigned>::WrappedMemoryStream(uint bufferSize) {
// Verify the buffer size is sane
if (is16Bit && stereo)
assert((bufferSize & 3) == 0);
else if (is16Bit || stereo)
assert((bufferSize & 1) == 0);
_bufferStart = (byte *)malloc(bufferSize);
_pos = _end = _bufferStart;
_bufferEnd = _bufferStart + bufferSize;
}
template<bool stereo, bool is16Bit, bool isUnsigned>
inline int16 WrappedMemoryStream<stereo, is16Bit, isUnsigned>::readIntern() {
//assert(_pos != _end);
int16 val = READSAMPLE(is16Bit, isUnsigned, _pos);
_pos += (is16Bit ? 2 : 1);
// Wrap around?
if (_pos >= _bufferEnd)
_pos = _pos - (_bufferEnd - _bufferStart);
return val;
}
template<bool stereo, bool is16Bit, bool isUnsigned>
int WrappedMemoryStream<stereo, is16Bit, isUnsigned>::readBuffer(int16 *buffer, const int numSamples) {
int samples = 0;
while (samples < numSamples && !eosIntern()) {
const byte *endMarker = (_pos > _end) ? _bufferEnd : _end;
const int len = MIN(numSamples, samples + (int)(endMarker - _pos) / (is16Bit ? 2 : 1));
while (samples < len) {
*buffer++ = READSAMPLE(is16Bit, isUnsigned, _pos);
_pos += (is16Bit ? 2 : 1);
samples++;
}
// Wrap around?
if (_pos >= _bufferEnd)
_pos = _pos - (_bufferEnd - _bufferStart);
}
return samples;
}
template<bool stereo, bool is16Bit, bool isUnsigned>
void WrappedMemoryStream<stereo, is16Bit, isUnsigned>::append(const byte *data, uint32 len) {
// Verify the buffer size is sane
if (is16Bit && stereo)
assert((len & 3) == 0);
else if (is16Bit || stereo)
assert((len & 1) == 0);
if (_end + len > _bufferEnd) {
// Wrap-around case
uint32 size_to_end_of_buffer = _bufferEnd - _end;
len -= size_to_end_of_buffer;
if ((_end < _pos) || (_bufferStart + len >= _pos)) {
warning("WrappedMemoryStream: buffer overflow (A)");
return;
}
memcpy(_end, data, size_to_end_of_buffer);
memcpy(_bufferStart, data + size_to_end_of_buffer, len);
_end = _bufferStart + len;
} else {
if ((_end < _pos) && (_end + len >= _pos)) {
warning("WrappedMemoryStream: buffer overflow (B)");
return;
}
memcpy(_end, data, len);
_end += len;
}
}
template<bool stereo>
static AudioInputStream *makeLinearInputStream(const byte *ptr, uint32 len, bool is16Bit, bool isUnsigned, uint loopOffset, uint loopLen) {
if (isUnsigned) {
if (is16Bit)
return new LinearMemoryStream<stereo, true, true>(ptr, len, loopOffset, loopLen);
else
return new LinearMemoryStream<stereo, false, true>(ptr, len, loopOffset, loopLen);
} else {
if (is16Bit)
return new LinearMemoryStream<stereo, true, false>(ptr, len, loopOffset, loopLen);
else
return new LinearMemoryStream<stereo, false, false>(ptr, len, loopOffset, loopLen);
}
}
template<bool stereo>
static WrappedAudioInputStream *makeWrappedInputStream(uint32 len, bool is16Bit, bool isUnsigned) {
if (isUnsigned) {
if (is16Bit)
return new WrappedMemoryStream<stereo, true, true>(len);
else
return new WrappedMemoryStream<stereo, false, true>(len);
} else {
if (is16Bit)
return new WrappedMemoryStream<stereo, true, false>(len);
else
return new WrappedMemoryStream<stereo, false, false>(len);
}
}
AudioInputStream *makeLinearInputStream(byte _flags, const byte *ptr, uint32 len, uint loopOffset, uint loopLen) {
const bool is16Bit = (_flags & SoundMixer::FLAG_16BITS) != 0;
const bool isUnsigned = (_flags & SoundMixer::FLAG_UNSIGNED) != 0;
if (_flags & SoundMixer::FLAG_STEREO) {
return makeLinearInputStream<true>(ptr, len, is16Bit, isUnsigned, loopOffset, loopLen);
} else {
return makeLinearInputStream<false>(ptr, len, is16Bit, isUnsigned, loopOffset, loopLen);
}
}
WrappedAudioInputStream *makeWrappedInputStream(byte _flags, uint32 len) {
const bool is16Bit = (_flags & SoundMixer::FLAG_16BITS) != 0;
const bool isUnsigned = (_flags & SoundMixer::FLAG_UNSIGNED) != 0;
if (_flags & SoundMixer::FLAG_STEREO) {
return makeWrappedInputStream<true>(len, is16Bit, isUnsigned);
} else {
return makeWrappedInputStream<false>(len, is16Bit, isUnsigned);
}
}
--- NEW FILE: audiostream.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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
#ifndef AUDIOSTREAM_H
#define AUDIOSTREAM_H
#include "../stdafx.h"
#include "../bits.h"
/**
* Generic input stream for the resampling code.
*/
class AudioInputStream {
public:
virtual ~AudioInputStream() {}
/**
* Fill the given buffer with up to numSamples samples.
* Returns the actual number of samples read, or -1 if
* a critical error occured (note: you *must* check if
* this value is less than what you requested, this can
* happen when the stream is fully used up).
* For stereo stream, buffer will be filled with interleaved
* left and right channel samples.
*
* For maximum efficency, subclasses should always override
* the default implementation!
*/
virtual int readBuffer(int16 *buffer, const int numSamples) {
int samples;
for (samples = 0; samples < numSamples && !eos(); samples++) {
*buffer++ = read();
}
return samples;
}
/** Read a singel (16 bit signed) sample from the stream. */
virtual int16 read() = 0;
/** Is this a stereo stream? */
virtual bool isStereo() const = 0;
/* End of stream reached? */
virtual bool eos() const = 0;
virtual int getRate() const { return -1; }
};
class WrappedAudioInputStream : public AudioInputStream {
public:
virtual void append(const byte *data, uint32 len) = 0;
};
class ZeroInputStream : public AudioInputStream {
protected:
int _len;
public:
ZeroInputStream(uint len) : _len(len) { }
int readBuffer(int16 *buffer, const int numSamples) {
int samples = MIN(_len, numSamples);
memset(buffer, 0, samples * 2);
_len -= samples;
return samples;
}
int16 read() { assert(_len > 0); _len--; return 0; }
int size() const { return _len; }
bool isStereo() const { return false; }
bool eos() const { return _len <= 0; }
};
class MusicStream : public AudioInputStream {
public:
virtual int getRate() const = 0;
};
AudioInputStream *makeLinearInputStream(byte _flags, const byte *ptr, uint32 len, uint loopOffset, uint loopLen);
WrappedAudioInputStream *makeWrappedInputStream(byte _flags, uint32 len);
#endif
--- NEW FILE: mixer.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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
#include "../stdafx.h"
#include "../bits.h"
#include "../debug.h"
#include "mixer.h"
#include "rate.h"
#include "audiostream.h"
SoundMixer *g_mixer = NULL;
StackLock::StackLock(MutexRef mutex)
: _mutex(mutex) {
lock_mutex(_mutex);
}
StackLock::~StackLock() {
unlock_mutex(_mutex);
}
MutexRef create_mutex() {
return (MutexRef) SDL_CreateMutex();
}
void lock_mutex(MutexRef mutex) {
SDL_mutexP((SDL_mutex *) mutex);
}
void unlock_mutex(MutexRef mutex) {
SDL_mutexV((SDL_mutex *) mutex);
}
void delete_mutex(MutexRef mutex) {
SDL_DestroyMutex((SDL_mutex *) mutex);
}
/**
* Channels used by the sound mixer.
*/
class Channel {
protected:
SoundMixer *_mixer;
PlayingSoundHandle *_handle;
RateConverter *_converter;
AudioInputStream *_input;
byte _volume;
int8 _pan;
bool _paused;
public:
int _id;
Channel(SoundMixer *mixer, PlayingSoundHandle *handle)
: _mixer(mixer), _handle(handle), _converter(0), _input(0), _volume(0), _pan(0), _paused(false), _id(-1) {
assert(mixer);
}
virtual ~Channel();
void destroy();
virtual void mix(int16 *data, uint len);
virtual void pause(bool paused) {
_paused = paused;
}
virtual bool isPaused() {
return _paused;
}
virtual void setChannelVolume(const byte volume) {
_volume = volume;
}
virtual void setChannelPan(const int8 pan) {
_pan = pan;
}
virtual int getVolume() const {
return _mixer->getVolume();
}
};
class ChannelRaw : public Channel {
byte *_ptr;
public:
ChannelRaw(SoundMixer *mixer, PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, byte volume, int8 pan, int id, uint32 loopStart, uint32 loopEnd);
~ChannelRaw();
};
class ChannelStream : public Channel {
bool _finished;
public:
ChannelStream(SoundMixer *mixer, PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, uint32 buffer_size, byte volume, int8 pan);
void mix(int16 *data, uint len);
void append(void *sound, uint32 size);
void finish() { _finished = true; }
};
SoundMixer::SoundMixer() {
_mutex = NULL;
_premixParam = NULL;
_premixProc = NULL;
int i = 0;
_outputRate = 0;
_globalVolume = 0;
_paused = false;
for (i = 0; i != NUM_CHANNELS; i++)
_channels[i] = NULL;
}
SoundMixer::~SoundMixer() {
SDL_CloseAudio();
for (int i = 0; i != NUM_CHANNELS; i++) {
delete _channels[i];
}
delete_mutex(_mutex);
}
void set_sound_proc(SoundProc proc, void *param) {
SDL_AudioSpec desired;
memset(&desired, 0, sizeof(desired));
/* only one format supported at the moment */
desired.freq = 22050;
desired.format = AUDIO_S16SYS;
desired.channels = 2;
desired.samples = 2048;
desired.callback = proc;
desired.userdata = param;
if (SDL_OpenAudio(&desired, NULL) != 0) {
return;
}
SDL_PauseAudio(0);
}
void SoundMixer::bindToSystem() {
_mutex = create_mutex();
_outputRate = 22050;
if (_outputRate == 0)
error("OSystem returned invalid sample rate");
set_sound_proc(mixCallback, this);
}
void SoundMixer::setupPremix(PremixProc *proc, void *param) {
StackLock lock(_mutex);
_premixParam = param;
_premixProc = proc;
}
int SoundMixer::newStream(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, uint32 buffer_size, byte volume, int8 pan) {
StackLock lock(_mutex);
return insertChannel(handle, new ChannelStream(this, handle, sound, size, rate, flags, buffer_size, volume, pan));
}
void SoundMixer::appendStream(PlayingSoundHandle handle, void *sound, uint32 size) {
StackLock lock(_mutex);
if (handle == 0)
return;
int index = handle - 1;
if ((index < 0) || (index >= NUM_CHANNELS)) {
warning("soundMixer::appendStream has invalid index %d", index);
return;
}
ChannelStream *chan;
chan = (ChannelStream *)_channels[index];
if (!chan) {
error("Trying to append to nonexistant streamer : %d", index);
} else {
chan->append(sound, size);
}
}
void SoundMixer::endStream(PlayingSoundHandle handle) {
StackLock lock(_mutex);
// Simply ignore stop requests for handles of sounds that already terminated
if (handle == 0)
return;
int index = handle - 1;
if ((index < 0) || (index >= NUM_CHANNELS)) {
warning("soundMixer::endStream has invalid index %d", index);
return;
}
ChannelStream *chan;
chan = (ChannelStream *)_channels[index];
if (!chan) {
error("Trying to end a nonexistant streamer : %d", index);
} else {
chan->finish();
}
}
int 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;
return -1;
}
_channels[index] = chan;
if (handle)
*handle = index + 1;
return index;
}
int SoundMixer::playRaw(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, int id, byte volume, int8 pan, 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]->_id == id)
return -1;
}
return insertChannel(handle, new ChannelRaw(this, handle, sound, size, rate, flags, volume, pan, id, loopStart, loopEnd));
}
void SoundMixer::mix(int16 *buf, uint len) {
StackLock lock(_mutex);
if (_premixProc && !_paused) {
_premixProc(_premixParam, buf, len);
} else {
// zero the buf out
memset(buf, 0, 2 * len * sizeof(int16));
}
if (!_paused) {
// now mix all channels
for (int i = 0; i != NUM_CHANNELS; i++)
if (_channels[i] && !_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() {
StackLock lock(_mutex);
for (int i = 0; i != NUM_CHANNELS; i++)
if (_channels[i])
_channels[i]->destroy();
}
void SoundMixer::stopChannel(int index) {
if ((index < 0) || (index >= NUM_CHANNELS)) {
warning("soundMixer::stop has invalid index %d", index);
return;
}
StackLock lock(_mutex);
if (_channels[index])
_channels[index]->destroy();
}
void SoundMixer::stopID(int id) {
StackLock lock(_mutex);
for (int i = 0; i != NUM_CHANNELS; i++) {
if (_channels[i] != NULL && _channels[i]->_id == id) {
_channels[i]->destroy();
return;
}
}
}
void SoundMixer::stopHandle(PlayingSoundHandle handle) {
StackLock lock(_mutex);
// Simply ignore stop requests for handles of sounds that already terminated
if (handle == 0)
return;
int index = handle - 1;
if ((index < 0) || (index >= NUM_CHANNELS)) {
warning("soundMixer::stopHandle has invalid index %d", index);
return;
}
if (_channels[index])
_channels[index]->destroy();
}
void SoundMixer::setChannelVolume(PlayingSoundHandle handle, byte volume) {
StackLock lock(_mutex);
if (handle == 0)
return;
int index = handle - 1;
if ((index < 0) || (index >= NUM_CHANNELS)) {
warning("soundMixer::setChannelVolume has invalid index %d", index);
return;
}
if (_channels[index])
_channels[index]->setChannelVolume(volume);
}
void SoundMixer::setChannelPan(PlayingSoundHandle handle, int8 pan) {
StackLock lock(_mutex);
if (handle == 0)
return;
int index = handle - 1;
if ((index < 0) || (index >= NUM_CHANNELS)) {
warning("soundMixer::setChannelVolume has invalid index %d", index);
return;
}
if (_channels[index])
_channels[index]->setChannelPan(pan);
}
void SoundMixer::pauseAll(bool paused) {
_paused = paused;
}
void SoundMixer::pauseChannel(int index, bool paused) {
if ((index < 0) || (index >= NUM_CHANNELS)) {
warning("soundMixer::pauseChannel has invalid index %d", index);
return;
}
StackLock lock(_mutex);
if (_channels[index])
_channels[index]->pause(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]->_id == 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
if (handle == 0)
return;
int index = handle - 1;
if ((index < 0) || (index >= NUM_CHANNELS)) {
warning("soundMixer::pauseHandle has invalid index %d", index);
return;
}
if (_channels[index])
_channels[index]->pause(paused);
}
void SoundMixer::setVolume(int volume) {
// Check range
if (volume > 256)
volume = 256;
else if (volume < 0)
volume = 0;
_globalVolume = volume;
}
Channel::~Channel() {
delete _converter;
delete _input;
if (_handle)
*_handle = 0;
}
void Channel::destroy() {
for (int i = 0; i != SoundMixer::NUM_CHANNELS; i++)
if (_mixer->_channels[i] == this)
_mixer->_channels[i] = 0;
delete this;
}
/* 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);
if (_input->eos()) {
// TODO: call drain method
destroy();
} else {
assert(_converter);
// The pan value ranges from -127 to +127. That's 255 different values.
// From the channel pan/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 254 is there
// because the maximal left/right pan value is 2*127 = 254.
// The value getVolume() returns is in the range 0 - 256.
// Hence, the vol_l/vol_r values will be in that range, too
int vol = getVolume() * _volume;
st_volume_t vol_l = (127 - _pan) * vol / (255 * 254);
st_volume_t vol_r = (127 + _pan) * vol / (255 * 254);
_converter->flow(*_input, data, len, vol_l, vol_r);
}
}
/* RAW mixer */
ChannelRaw::ChannelRaw(SoundMixer *mixer, PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, byte volume, int8 pan, int id, uint32 loopStart, uint32 loopEnd)
: Channel(mixer, handle) {
_id = id;
_ptr = (byte *)sound;
_volume = volume;
_pan = pan;
// Create the input stream
if (flags & SoundMixer::FLAG_LOOP) {
if (loopEnd == 0) {
_input = makeLinearInputStream(flags, _ptr, size, 0, size);
} else {
assert(loopStart < loopEnd && loopEnd <= size);
_input = makeLinearInputStream(flags, _ptr, size, loopStart, loopEnd - loopStart);
}
} else {
_input = makeLinearInputStream(flags, _ptr, size, 0, 0);
}
// Get a rate converter instance
_converter = makeRateConverter(rate, mixer->getOutputRate(), _input->isStereo(), (flags & SoundMixer::FLAG_REVERSE_STEREO) != 0);
if (!(flags & SoundMixer::FLAG_AUTOFREE))
_ptr = 0;
}
ChannelRaw::~ChannelRaw() {
free(_ptr);
}
ChannelStream::ChannelStream(SoundMixer *mixer, PlayingSoundHandle *handle,
void *sound, uint32 size, uint rate,
byte flags, uint32 buffer_size, byte volume, int8 pan)
: Channel(mixer, handle) {
_volume = volume;
_pan = pan;
assert(size <= buffer_size);
// Create the input stream
_input = makeWrappedInputStream(flags, buffer_size);
// Append the initial data
((WrappedAudioInputStream *)_input)->append((const byte *)sound, size);
// Get a rate converter instance
_converter = makeRateConverter(rate, mixer->getOutputRate(), _input->isStereo(), (flags & SoundMixer::FLAG_REVERSE_STEREO) != 0);
_finished = false;
}
void ChannelStream::append(void *data, uint32 len) {
((WrappedAudioInputStream *)_input)->append((const byte *)data, len);
}
void ChannelStream::mix(int16 *data, uint len) {
assert(_input);
if (_input->eos()) {
// TODO: call drain method
// Normally, the stream stays around even if all its data is used up.
// This is in case more data is streamed into it. To make the stream
// go away, one can either stop() it (which takes effect immediately,
// ignoring any remaining sound data), or finish() it, which means
// it will finish playing before it terminates itself.
if (_finished) {
destroy();
}
} else {
// Invoke the parent implementation.
Channel::mix(data, len);
}
}
--- NEW FILE: mixer.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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
#ifndef SOUND_MIXER_H
#define SOUND_MIXER_H
#include "../stdafx.h"
#include "../bits.h"
#include <SDL.h>
typedef uint32 PlayingSoundHandle;
typedef struct Mutex *MutexRef;
typedef void (*SoundProc)(void *param, byte *buf, int len);
typedef int (*TimerProc)(int interval);
class StackLock {
MutexRef _mutex;
public:
StackLock(MutexRef mutex);
~StackLock();
};
MutexRef create_mutex();
void lock_mutex(MutexRef mutex);
void unlock_mutex(MutexRef mutex);
void delete_mutex(MutexRef mutex);
class Channel;
class SoundMixer {
friend class Channel;
public:
typedef void PremixProc (void *param, int16 *data, uint len);
enum {
NUM_CHANNELS = 16
};
enum {
FLAG_UNSIGNED = 1 << 0, // unsigned samples (default: signed)
FLAG_STEREO = 1 << 1, // sound is in stereo (default: mono)
FLAG_16BITS = 1 << 2, // sound is 16 bits wide (default: 8bit)
FLAG_AUTOFREE = 1 << 3, // sound buffer is freed automagically at the end of playing
FLAG_REVERSE_STEREO = 1 << 4, // reverse the left and right stereo channel
FLAG_LOOP = 1 << 5 // loop the audio
};
private:
MutexRef _mutex;
void *_premixParam;
PremixProc *_premixProc;
uint _outputRate;
int _globalVolume;
bool _paused;
Channel *_channels[NUM_CHANNELS];
public:
SoundMixer();
~SoundMixer();
void bindToSystem();
void setupPremix(PremixProc *proc, void *param);
// start playing a raw sound
int playRaw(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags,
int id = -1, byte volume = 255, int8 pan = 0, uint32 loopStart = 0, uint32 loopEnd = 0);
/** Start a new stream. */
int newStream(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, uint32 buffer_size, byte volume = 255, int8 pan = 0);
/** Append to an existing stream. */
void appendStream(PlayingSoundHandle handle, void *sound, uint32 size);
/** Mark a stream as finished - it will play all its remaining data, then stop. */
void endStream(PlayingSoundHandle handle);
/** stop all currently playing sounds */
void stopAll();
/** stop playing the given channel */
void stopChannel(int channel);
/** stop playing the sound with given ID */
void stopID(int id);
/** stop playing the channel for the given handle */
void stopHandle(PlayingSoundHandle handle);
/** pause/unpause all channels */
void pauseAll(bool paused);
/** pause/unpause the given channel */
void pauseChannel(int index, bool paused);
/** pause/unpause the sound with the given ID */
void pauseID(int id, bool paused);
/** pause/unpause the channel for the given handle */
void pauseHandle(PlayingSoundHandle handle, bool paused);
/** set the channel volume for the given handle (0 - 255) */
void setChannelVolume(PlayingSoundHandle handle, byte volume);
/** set the channel pan for the given handle (-127 ... 0 ... 127) (left ... center ... right)*/
void setChannelPan(PlayingSoundHandle handle, int8 pan);
/** set the global volume, 0-256 */
void setVolume(int volume);
/** query the global volume, 0-256 */
int getVolume() const { return _globalVolume; }
/** query the output rate in kHz */
uint getOutputRate() const { return _outputRate; }
private:
int insertChannel(PlayingSoundHandle *handle, Channel *chan);
/** main mixer method */
void mix(int16 *buf, uint len);
static void mixCallback(void *s, byte *samples, int len);
};
#endif
--- NEW FILE: rate.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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
/*
* The code in this file is based on code with Copyright 1998 Fabrice Bellard
* Fabrice original code is part of SoX (http://sox.sourceforge.net).
* Max Horn adapted that code to the needs of ScummVM and rewrote it partial,
* in the process removing any use of floating point arithmetic. Various other
* improvments over the original code were made.
*/
#include "../stdafx.h"
#include "rate.h"
#include "audiostream.h"
#include "../debug.h"
/**
* The precision of the fractional computations used by the rate converter.
* Normally you should never have to modify this value.
*/
#define FRAC_BITS 16
/**
* The size of the intermediate input cache. Bigger values may increase
* performance, but only until some point (depends largely on cache size,
* target processor and various other factors), at which it will decrease
* again.
*/
#define INTERMEDIATE_BUFFER_SIZE 512
/**
* Audio rate converter based on simple linear Interpolation.
*
* The use of fractional increment allows us to use no buffer. It
* avoid the problems at the end of the buffer we had with the old
* method which stored a possibly big buffer of size
* lcm(in_rate,out_rate).
*
* Limited to sampling frequency <= 65535 Hz.
*/
template<bool stereo, bool reverseStereo>
class LinearRateConverter : public RateConverter {
protected:
st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
const st_sample_t *inPtr;
int inLen;
/** fractional position of the output stream in input stream unit */
unsigned long opos, opos_frac;
/** fractional position increment in the output stream */
unsigned long opos_inc, opos_inc_frac;
/** position in the input stream (integer) */
unsigned long ipos;
/** last sample(s) in the input stream (left/right channel) */
st_sample_t ilast[2];
/** current sample(s) in the input stream (left/right channel) */
st_sample_t icur[2];
public:
LinearRateConverter(st_rate_t inrate, st_rate_t outrate);
int flow(AudioInputStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r);
int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
return (ST_SUCCESS);
}
};
/*
* Prepare processing.
*/
template<bool stereo, bool reverseStereo>
LinearRateConverter<stereo, reverseStereo>::LinearRateConverter(st_rate_t inrate, st_rate_t outrate) {
unsigned long incr;
if (inrate == outrate) {
error("Input and Output rates must be different to use rate effect");
}
if (inrate >= 65536 || outrate >= 65536) {
error("rate effect can only handle rates < 65536");
}
opos_frac = 0;
opos = 1;
/* increment */
incr = (inrate << FRAC_BITS) / outrate;
opos_inc_frac = incr & ((1UL << FRAC_BITS) - 1);
opos_inc = incr >> FRAC_BITS;
ipos = 0;
ilast[0] = ilast[1] = 0;
icur[0] = icur[1] = 0;
inLen = 0;
}
/*
* Processed signed long samples from ibuf to obuf.
* Return number of samples processed.
*/
template<bool stereo, bool reverseStereo>
int LinearRateConverter<stereo, reverseStereo>::flow(AudioInputStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
st_sample_t *ostart, *oend;
st_sample_t out[2];
const int numChannels = stereo ? 2 : 1;
int i;
ostart = obuf;
oend = obuf + osamp * 2;
while (obuf < oend) {
// read enough input samples so that ipos > opos
while (ipos <= opos) {
// Check if we have to refill the buffer
if (inLen == 0) {
inPtr = inBuf;
inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf));
if (inLen <= 0)
goto the_end;
}
for (i = 0; i < numChannels; i++) {
ilast[i] = icur[i];
icur[i] = *inPtr++;
inLen--;
}
ipos++;
}
// Loop as long as the outpos trails behind, and as long as there is
// still space in the output buffer.
while (ipos > opos) {
// interpolate
out[0] = out[1] = (st_sample_t)(ilast[0] + (((icur[0] - ilast[0]) * opos_frac + (1UL << (FRAC_BITS-1))) >> FRAC_BITS));
if (stereo) {
// interpolate
out[reverseStereo ? 0 : 1] = (st_sample_t)(ilast[1] + (((icur[1] - ilast[1]) * opos_frac + (1UL << (FRAC_BITS-1))) >> FRAC_BITS));
}
// output left channel
clampedAdd(*obuf++, (out[0] * (int)vol_l) >> 8);
// output right channel
clampedAdd(*obuf++, (out[1] * (int)vol_r) >> 8);
// Increment output position
unsigned long tmp = opos_frac + opos_inc_frac;
opos += opos_inc + (tmp >> FRAC_BITS);
opos_frac = tmp & ((1UL << FRAC_BITS) - 1);
// Abort if we reached the end of the output buffer
if (obuf >= oend)
goto the_end;
}
}
the_end:
return (ST_SUCCESS);
}
/**
* Simple audio rate converter for the case that the inrate equals the outrate.
*/
template<bool stereo, bool reverseStereo>
class CopyRateConverter : public RateConverter {
public:
virtual int flow(AudioInputStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
int16 tmp[2];
st_size_t len = osamp;
assert(input.isStereo() == stereo);
while (!input.eos() && len--) {
tmp[0] = tmp[1] = input.read();
if (stereo)
tmp[reverseStereo ? 0 : 1] = input.read();
// output left channel
clampedAdd(*obuf++, (tmp[0] * (int)vol_l) >> 8);
// output right channel
clampedAdd(*obuf++, (tmp[1] * (int)vol_r) >> 8);
}
return (ST_SUCCESS);
}
virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
return (ST_SUCCESS);
}
};
/**
* Create and return a RateConverter object for the specified input and output rates.
*/
RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo) {
if (inrate != outrate) {
if (stereo) {
if (reverseStereo)
return new LinearRateConverter<true, true>(inrate, outrate);
else
return new LinearRateConverter<true, false>(inrate, outrate);
} else
return new LinearRateConverter<false, false>(inrate, outrate);
} else {
if (stereo) {
if (reverseStereo)
return new CopyRateConverter<true, true>();
else
return new CopyRateConverter<true, false>();
} else
return new CopyRateConverter<false, false>();
}
}
--- NEW FILE: rate.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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
#ifndef SOUND_RATE_H
#define SOUND_RATE_H
#include "../bits.h"
class AudioInputStream;
typedef int16 st_sample_t;
typedef uint16 st_volume_t;
typedef uint32 st_size_t;
typedef uint32 st_rate_t;
/* Minimum and maximum values a sample can hold. */
#define ST_SAMPLE_MAX 0x7fffL
#define ST_SAMPLE_MIN (-ST_SAMPLE_MAX - 1L)
#define ST_EOF (-1)
#define ST_SUCCESS (0)
static inline void clampedAdd(int16& a, int b) {
register int val;
#ifdef OUTPUT_UNSIGNED_AUDIO
val = (a ^ 0x8000) + b;
#else
val = a + b;
#endif
if (val > ST_SAMPLE_MAX)
val = ST_SAMPLE_MAX;
else if (val < ST_SAMPLE_MIN)
val = ST_SAMPLE_MIN;
#ifdef OUTPUT_UNSIGNED_AUDIO
a = ((int16)val) ^ 0x8000;
#else
a = val;
#endif
}
// Q&D hack to get this SOX stuff to work
#define st_report warning
#define st_warn warning
#define st_fail error
class RateConverter {
public:
RateConverter() {}
virtual ~RateConverter() {}
virtual int flow(AudioInputStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) = 0;
virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) = 0;
};
RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo = false);
#endif
- Previous message: [Scummvm-cvs-logs] CVS: residual/mixer - New directory
- Next message: [Scummvm-cvs-logs] CVS: residual actor.cpp,1.16,1.17 bits.h,1.7,1.8 costume.cpp,1.10,1.11 lua.cpp,1.34,1.35 main.cpp,1.15,1.16 residual.vcproj,1.10,1.11 smush.cpp,1.1,1.2 smush.h,1.1,1.2 sound.cpp,1.4,1.5 sound.h,1.3,1.4 mixer.cpp,1.5,NONE mixer.h,1.5,NONE
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the Scummvm-git-logs
mailing list