mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-19 00:15:30 +00:00
synced with scummvm mixer
This commit is contained in:
parent
289c285fce
commit
654a931e6f
@ -20,7 +20,6 @@
|
||||
#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
|
||||
@ -29,28 +28,23 @@
|
||||
#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 {
|
||||
#define READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, ptr, isLE) \
|
||||
((is16Bit ? (isLE ? READ_LE_UINT16(ptr) : READ_BE_UINT16(ptr)) : (*ptr << 8)) ^ (isUnsigned ? 0x8000 : 0))
|
||||
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
|
||||
class LinearMemoryStream : public AudioStream {
|
||||
protected:
|
||||
const byte *_ptr;
|
||||
const byte *_end;
|
||||
const byte *_loopPtr;
|
||||
const byte *_loopEnd;
|
||||
const int _rate;
|
||||
const byte *_origPtr;
|
||||
|
||||
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) {
|
||||
LinearMemoryStream(int rate, const byte *ptr, uint len, uint loopOffset, uint loopLen, bool autoFreeMemory)
|
||||
: _ptr(ptr), _end(ptr+len), _loopPtr(0), _loopEnd(0), _rate(rate) {
|
||||
|
||||
// Verify the buffer sizes are sane
|
||||
if (is16Bit && stereo)
|
||||
@ -64,21 +58,37 @@ public:
|
||||
}
|
||||
if (stereo) // Stereo requires even sized data
|
||||
assert(len % 2 == 0);
|
||||
|
||||
_origPtr = autoFreeMemory ? ptr : 0;
|
||||
}
|
||||
~LinearMemoryStream() {
|
||||
free(const_cast<byte *>(_origPtr));
|
||||
}
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
int16 read() { return readIntern(); }
|
||||
bool eos() const { return eosIntern(); }
|
||||
int16 read() {
|
||||
//assert(_ptr < _end);
|
||||
int16 val = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE);
|
||||
_ptr += (is16Bit ? 2 : 1);
|
||||
if (_loopPtr && eosIntern()) {
|
||||
_ptr = _loopPtr;
|
||||
_end = _loopEnd;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
bool isStereo() const { return stereo; }
|
||||
bool endOfData() const { return eosIntern(); }
|
||||
|
||||
int getRate() const { return _rate; }
|
||||
};
|
||||
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned>
|
||||
int LinearMemoryStream<stereo, is16Bit, isUnsigned>::readBuffer(int16 *buffer, const int numSamples) {
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
|
||||
int LinearMemoryStream<stereo, is16Bit, isUnsigned, isLE>::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);
|
||||
*buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE);
|
||||
_ptr += (is16Bit ? 2 : 1);
|
||||
samples++;
|
||||
}
|
||||
@ -91,32 +101,40 @@ int LinearMemoryStream<stereo, is16Bit, isUnsigned>::readBuffer(int16 *buffer, c
|
||||
return samples;
|
||||
}
|
||||
|
||||
// Wrapped memory stream, to be used by the ChannelStream class (and possibly others?)
|
||||
/**
|
||||
* Wrapped memory stream.
|
||||
*/
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned>
|
||||
class WrappedMemoryStream : public WrappedAudioInputStream {
|
||||
class AppendableMemoryStream : public AppendableAudioStream {
|
||||
protected:
|
||||
byte *_bufferStart;
|
||||
byte *_bufferEnd;
|
||||
byte *_pos;
|
||||
byte *_end;
|
||||
bool _finalized;
|
||||
const int _rate;
|
||||
|
||||
inline int16 readIntern();
|
||||
inline bool eosIntern() const { return _end == _pos; };
|
||||
public:
|
||||
WrappedMemoryStream(uint bufferSize);
|
||||
~WrappedMemoryStream() { free(_bufferStart); }
|
||||
AppendableMemoryStream(int rate, uint bufferSize);
|
||||
~AppendableMemoryStream() { free(_bufferStart); }
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
int16 read() { return readIntern(); }
|
||||
bool eos() const { return eosIntern(); }
|
||||
int16 read();
|
||||
bool isStereo() const { return stereo; }
|
||||
bool endOfStream() const { return _finalized && eosIntern(); }
|
||||
bool endOfData() const { return eosIntern(); }
|
||||
|
||||
int getRate() const { return _rate; }
|
||||
|
||||
void append(const byte *data, uint32 len);
|
||||
void finish() { _finalized = true; }
|
||||
};
|
||||
|
||||
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned>
|
||||
WrappedMemoryStream<stereo, is16Bit, isUnsigned>::WrappedMemoryStream(uint bufferSize) {
|
||||
AppendableMemoryStream<stereo, is16Bit, isUnsigned>::AppendableMemoryStream(int rate, uint bufferSize)
|
||||
: _finalized(false), _rate(rate) {
|
||||
|
||||
// Verify the buffer size is sane
|
||||
if (is16Bit && stereo)
|
||||
@ -130,22 +148,27 @@ WrappedMemoryStream<stereo, is16Bit, isUnsigned>::WrappedMemoryStream(uint buffe
|
||||
}
|
||||
|
||||
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);
|
||||
inline int16 AppendableMemoryStream<stereo, is16Bit, isUnsigned>::read() {
|
||||
assert(!eosIntern());
|
||||
|
||||
// Wrap around?
|
||||
if (_pos >= _bufferEnd)
|
||||
_pos = _pos - (_bufferEnd - _bufferStart);
|
||||
|
||||
int16 val = READSAMPLE(is16Bit, isUnsigned, _pos);
|
||||
_pos += (is16Bit ? 2 : 1);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned>
|
||||
int WrappedMemoryStream<stereo, is16Bit, isUnsigned>::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int AppendableMemoryStream<stereo, is16Bit, isUnsigned>::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples = 0;
|
||||
while (samples < numSamples && !eosIntern()) {
|
||||
// Wrap around?
|
||||
if (_pos >= _bufferEnd)
|
||||
_pos = _pos - (_bufferEnd - _bufferStart);
|
||||
|
||||
const byte *endMarker = (_pos > _end) ? _bufferEnd : _end;
|
||||
const int len = MIN(numSamples, samples + (int)(endMarker - _pos) / (is16Bit ? 2 : 1));
|
||||
while (samples < len) {
|
||||
@ -153,28 +176,28 @@ int WrappedMemoryStream<stereo, is16Bit, isUnsigned>::readBuffer(int16 *buffer,
|
||||
_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) {
|
||||
void AppendableMemoryStream<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);
|
||||
|
||||
// Verify that the stream has not yet been finalized (by a call to finish())
|
||||
assert(!_finalized);
|
||||
|
||||
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)");
|
||||
warning("AppendableMemoryStream: buffer overflow (A)");
|
||||
return;
|
||||
}
|
||||
memcpy(_end, data, size_to_end_of_buffer);
|
||||
@ -182,7 +205,7 @@ void WrappedMemoryStream<stereo, is16Bit, isUnsigned>::append(const byte *data,
|
||||
_end = _bufferStart + len;
|
||||
} else {
|
||||
if ((_end < _pos) && (_end + len >= _pos)) {
|
||||
warning("WrappedMemoryStream: buffer overflow (B)");
|
||||
warning("AppendableMemoryStream: buffer overflow (B)");
|
||||
return;
|
||||
}
|
||||
memcpy(_end, data, len);
|
||||
@ -190,52 +213,92 @@ void WrappedMemoryStream<stereo, is16Bit, isUnsigned>::append(const byte *data,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
#if 0
|
||||
// Work in progress!!! Not yet usable/finished/working/anything :-)
|
||||
|
||||
class ProcInputStream : public AudioStream {
|
||||
public:
|
||||
typedef void InputProc (void *refCon, int16 *data, uint len);
|
||||
|
||||
private:
|
||||
const int _rate;
|
||||
const bool _isStereo;
|
||||
InputProc *_proc;
|
||||
void *_refCon;
|
||||
|
||||
public:
|
||||
ProcInputStream(int rate, bool stereo, InputProc *proc, void *refCon)
|
||||
: _rate(rate), _isStereo(stereo), _proc(proc), _refCon(refCon) { }
|
||||
int readBuffer(int16 *buffer, const int numSamples) {
|
||||
(_proc)(_refCon, buffer, numSamples);
|
||||
return numSamples;
|
||||
}
|
||||
int16 read() {
|
||||
int16 sample;
|
||||
(_proc)(_refCon, &sample, 1);
|
||||
return sample;
|
||||
}
|
||||
bool isStereo() const { return _isStereo; }
|
||||
bool endOfData() const { return false; }
|
||||
|
||||
int getRate() const { return _rate; }
|
||||
};
|
||||
#endif
|
||||
|
||||
#define MAKE_LINEAR(STEREO, UNSIGNED) \
|
||||
if (is16Bit) { \
|
||||
if (isLE) \
|
||||
return new LinearMemoryStream<STEREO, true, UNSIGNED, true>(rate, ptr, len, loopOffset, loopLen, autoFree); \
|
||||
else \
|
||||
return new LinearMemoryStream<STEREO, true, UNSIGNED, false>(rate, ptr, len, loopOffset, loopLen, autoFree); \
|
||||
} else \
|
||||
return new LinearMemoryStream<STEREO, false, UNSIGNED, false>(rate, ptr, len, loopOffset, loopLen, autoFree)
|
||||
|
||||
AudioStream *makeLinearInputStream(int rate, byte _flags, const byte *ptr, uint32 len, uint loopOffset, uint loopLen) {
|
||||
const bool isStereo = (_flags & SoundMixer::FLAG_STEREO) != 0;
|
||||
const bool is16Bit = (_flags & SoundMixer::FLAG_16BITS) != 0;
|
||||
const bool isUnsigned = (_flags & SoundMixer::FLAG_UNSIGNED) != 0;
|
||||
const bool isLE = (_flags & SoundMixer::FLAG_LITTLE_ENDIAN) != 0;
|
||||
const bool autoFree = (_flags & SoundMixer::FLAG_AUTOFREE) != 0;
|
||||
|
||||
if (isStereo) {
|
||||
if (isUnsigned) {
|
||||
MAKE_LINEAR(true, true);
|
||||
} else {
|
||||
MAKE_LINEAR(true, false);
|
||||
}
|
||||
} else {
|
||||
if (is16Bit)
|
||||
return new LinearMemoryStream<stereo, true, false>(ptr, len, loopOffset, loopLen);
|
||||
else
|
||||
return new LinearMemoryStream<stereo, false, false>(ptr, len, loopOffset, loopLen);
|
||||
if (isUnsigned) {
|
||||
MAKE_LINEAR(false, true);
|
||||
} else {
|
||||
MAKE_LINEAR(false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
#define MAKE_WRAPPED(STEREO, UNSIGNED) \
|
||||
if (is16Bit) \
|
||||
return new AppendableMemoryStream<STEREO, true, UNSIGNED>(rate, len); \
|
||||
else \
|
||||
return new AppendableMemoryStream<STEREO, false, UNSIGNED>(rate, len)
|
||||
|
||||
AudioInputStream *makeLinearInputStream(byte _flags, const byte *ptr, uint32 len, uint loopOffset, uint loopLen) {
|
||||
AppendableAudioStream *makeAppendableAudioStream(int rate, byte _flags, uint32 len) {
|
||||
const bool isStereo = (_flags & SoundMixer::FLAG_STEREO) != 0;
|
||||
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);
|
||||
|
||||
if (isStereo) {
|
||||
if (isUnsigned) {
|
||||
MAKE_WRAPPED(true, true);
|
||||
} else {
|
||||
MAKE_WRAPPED(true, false);
|
||||
}
|
||||
} 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);
|
||||
if (isUnsigned) {
|
||||
MAKE_WRAPPED(false, true);
|
||||
} else {
|
||||
MAKE_WRAPPED(false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,9 @@
|
||||
/**
|
||||
* Generic input stream for the resampling code.
|
||||
*/
|
||||
class AudioInputStream {
|
||||
class AudioStream {
|
||||
public:
|
||||
virtual ~AudioInputStream() {}
|
||||
virtual ~AudioStream() {}
|
||||
|
||||
/**
|
||||
* Fill the given buffer with up to numSamples samples.
|
||||
@ -36,37 +36,61 @@ public:
|
||||
* 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;
|
||||
}
|
||||
virtual int readBuffer(int16 *buffer, const int numSamples) = 0;
|
||||
|
||||
/** Read a singel (16 bit signed) sample from the stream. */
|
||||
virtual int16 read() = 0;
|
||||
/**
|
||||
* Read a single (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;
|
||||
/**
|
||||
* End of data reached? If this returns true, it means that at this
|
||||
* time there is no data available in the stream. However there may be
|
||||
* more data in the future.
|
||||
* This is used by e.g. a rate converter to decide whether to keep on
|
||||
* converting data or stop.
|
||||
*/
|
||||
virtual bool endOfData() const = 0;
|
||||
|
||||
/**
|
||||
* End of stream reached? If this returns true, it means that all data
|
||||
* in this stream is used up and no additional data will appear in it
|
||||
* in the future.
|
||||
* This is used by the mixer to decide whether a given stream shall be
|
||||
* removed from the list of active streams (and thus be destroyed).
|
||||
* By default this maps to endOfData()
|
||||
*/
|
||||
virtual bool endOfStream() const { return endOfData(); }
|
||||
|
||||
virtual int getRate() const { return -1; }
|
||||
/** Sample rate of the stream. */
|
||||
virtual int getRate() const = 0;
|
||||
|
||||
/**
|
||||
* This function returns the number of samples that were delivered to
|
||||
* the mixer which is a rough estimate of how moch time of the stream
|
||||
* has been played.
|
||||
* The exact value is not available as it needs information from the
|
||||
* audio device on how many samples have been already played
|
||||
* As our buffer is relatively short the estimate is exact enough
|
||||
* The return -1 is kind of a hack as this function is only required
|
||||
* for the video audio sync in the bs2 cutscenes I am to lazy to
|
||||
* implement it for all subclasses
|
||||
*/
|
||||
virtual int getSamplesPlayed() const { return -1; }
|
||||
};
|
||||
|
||||
class WrappedAudioInputStream : public AudioInputStream {
|
||||
class AppendableAudioStream : public AudioStream {
|
||||
public:
|
||||
virtual void append(const byte *data, uint32 len) = 0;
|
||||
virtual void finish() = 0;
|
||||
};
|
||||
|
||||
class ZeroInputStream : public AudioInputStream {
|
||||
protected:
|
||||
class ZeroInputStream : public AudioStream {
|
||||
private:
|
||||
int _len;
|
||||
public:
|
||||
ZeroInputStream(uint len) : _len(len) { }
|
||||
@ -77,18 +101,13 @@ public:
|
||||
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; }
|
||||
|
||||
int getRate() const { return -1; }
|
||||
};
|
||||
|
||||
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);
|
||||
AudioStream *makeLinearInputStream(int rate, byte _flags, const byte *ptr, uint32 len, uint loopOffset, uint loopLen);
|
||||
AppendableAudioStream *makeAppendableAudioStream(int rate, byte _flags, uint32 len);
|
||||
|
||||
#endif
|
||||
|
404
mixer/mixer.cpp
404
mixer/mixer.cpp
@ -29,84 +29,80 @@ SoundMixer *g_mixer = NULL;
|
||||
* Channels used by the sound mixer.
|
||||
*/
|
||||
class Channel {
|
||||
protected:
|
||||
private:
|
||||
SoundMixer *_mixer;
|
||||
PlayingSoundHandle *_handle;
|
||||
RateConverter *_converter;
|
||||
AudioInputStream *_input;
|
||||
bool _autofreeStream;
|
||||
const bool _isMusic;
|
||||
byte _volume;
|
||||
int8 _pan;
|
||||
int8 _balance;
|
||||
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);
|
||||
}
|
||||
protected:
|
||||
RateConverter *_converter;
|
||||
AudioStream *_input;
|
||||
|
||||
public:
|
||||
|
||||
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);
|
||||
virtual ~Channel();
|
||||
void destroy();
|
||||
virtual void mix(int16 *data, uint len);
|
||||
virtual void pause(bool paused) {
|
||||
|
||||
void mix(int16 *data, uint len);
|
||||
|
||||
bool isFinished() const {
|
||||
return _input->endOfStream();
|
||||
}
|
||||
bool isMusicChannel() const {
|
||||
return _isMusic;
|
||||
}
|
||||
void pause(bool paused) {
|
||||
_paused = paused;
|
||||
}
|
||||
virtual bool isPaused() {
|
||||
bool isPaused() {
|
||||
return _paused;
|
||||
}
|
||||
virtual void setChannelVolume(const byte volume) {
|
||||
void setVolume(const byte volume) {
|
||||
_volume = volume;
|
||||
}
|
||||
virtual void setChannelPan(const int8 pan) {
|
||||
_pan = pan;
|
||||
void setBalance(const int8 balance) {
|
||||
_balance = balance;
|
||||
}
|
||||
virtual int getVolume() const {
|
||||
return _mixer->getVolume();
|
||||
int getId() const {
|
||||
return _id;
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
ChannelStream(SoundMixer *mixer, PlayingSoundHandle *handle, uint rate, byte flags, uint32 buffer_size);
|
||||
void append(void *sound, uint32 size);
|
||||
void finish() { _finished = true; }
|
||||
|
||||
void finish();
|
||||
};
|
||||
|
||||
SoundMixer::SoundMixer() {
|
||||
_mutex = NULL;
|
||||
|
||||
_mutex = create_mutex();
|
||||
_premixParam = NULL;
|
||||
_premixProc = NULL;
|
||||
int i = 0;
|
||||
|
||||
_outputRate = 0;
|
||||
|
||||
_outputRate = 22050;
|
||||
_globalVolume = 0;
|
||||
|
||||
_paused = false;
|
||||
|
||||
for (i = 0; i != NUM_CHANNELS; i++)
|
||||
for (int i = 0; i != NUM_CHANNELS; i++)
|
||||
_channels[i] = NULL;
|
||||
|
||||
_mixerReady = setSoundProc(mixCallback, this);
|
||||
}
|
||||
|
||||
SoundMixer::~SoundMixer() {
|
||||
SDL_CloseAudio();
|
||||
for (int i = 0; i != NUM_CHANNELS; i++) {
|
||||
delete _channels[i];
|
||||
}
|
||||
stopAll();
|
||||
delete_mutex(_mutex);
|
||||
}
|
||||
|
||||
void set_sound_proc(SoundProc proc, void *param) {
|
||||
bool SoundMixer::setSoundProc(SoundProc proc, void *param) {
|
||||
SDL_AudioSpec desired;
|
||||
|
||||
memset(&desired, 0, sizeof(desired));
|
||||
@ -118,20 +114,13 @@ void set_sound_proc(SoundProc proc, void *param) {
|
||||
desired.samples = 2048;
|
||||
desired.callback = proc;
|
||||
desired.userdata = param;
|
||||
|
||||
if (SDL_OpenAudio(&desired, NULL) != 0) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundMixer::setupPremix(PremixProc *proc, void *param) {
|
||||
@ -140,18 +129,22 @@ void SoundMixer::setupPremix(PremixProc *proc, void *param) {
|
||||
_premixProc = proc;
|
||||
}
|
||||
|
||||
int SoundMixer::newStream(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, uint32 buffer_size, byte volume, int8 pan) {
|
||||
void SoundMixer::newStream(PlayingSoundHandle *handle, uint rate, byte flags, uint32 buffer_size, byte volume, int8 balance) {
|
||||
StackLock lock(_mutex);
|
||||
return insertChannel(handle, new ChannelStream(this, handle, sound, size, rate, flags, buffer_size, volume, pan));
|
||||
|
||||
Channel *chan = new ChannelStream(this, handle, rate, flags, buffer_size);
|
||||
chan->setVolume(volume);
|
||||
chan->setBalance(balance);
|
||||
insertChannel(handle, chan);
|
||||
}
|
||||
|
||||
void SoundMixer::appendStream(PlayingSoundHandle handle, void *sound, uint32 size) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
if (handle == 0)
|
||||
|
||||
if (!handle.isActive())
|
||||
return;
|
||||
|
||||
int index = handle - 1;
|
||||
int index = handle.getIndex();
|
||||
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::appendStream has invalid index %d", index);
|
||||
@ -159,9 +152,9 @@ void SoundMixer::appendStream(PlayingSoundHandle handle, void *sound, uint32 siz
|
||||
}
|
||||
|
||||
ChannelStream *chan;
|
||||
chan = (ChannelStream *)_channels[index];
|
||||
chan = dynamic_cast<ChannelStream *>(_channels[index]);
|
||||
if (!chan) {
|
||||
error("Trying to append to nonexistant streamer : %d", index);
|
||||
error("Trying to append to nonexistant stream : %d", index);
|
||||
} else {
|
||||
chan->append(sound, size);
|
||||
}
|
||||
@ -171,10 +164,10 @@ void SoundMixer::endStream(PlayingSoundHandle handle) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
// Simply ignore stop requests for handles of sounds that already terminated
|
||||
if (handle == 0)
|
||||
if (!handle.isActive())
|
||||
return;
|
||||
|
||||
int index = handle - 1;
|
||||
int index = handle.getIndex();
|
||||
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::endStream has invalid index %d", index);
|
||||
@ -182,7 +175,7 @@ void SoundMixer::endStream(PlayingSoundHandle handle) {
|
||||
}
|
||||
|
||||
ChannelStream *chan;
|
||||
chan = (ChannelStream *)_channels[index];
|
||||
chan = dynamic_cast<ChannelStream *>(_channels[index]);
|
||||
if (!chan) {
|
||||
error("Trying to end a nonexistant streamer : %d", index);
|
||||
} else {
|
||||
@ -190,10 +183,11 @@ void SoundMixer::endStream(PlayingSoundHandle handle) {
|
||||
}
|
||||
}
|
||||
|
||||
int SoundMixer::insertChannel(PlayingSoundHandle *handle, Channel *chan) {
|
||||
void SoundMixer::insertChannel(PlayingSoundHandle *handle, Channel *chan) {
|
||||
|
||||
int index = -1;
|
||||
for (int i = 0; i != NUM_CHANNELS; i++) {
|
||||
if (_channels[i] == NULL) {
|
||||
if (_channels[i] == 0) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
@ -201,43 +195,91 @@ int SoundMixer::insertChannel(PlayingSoundHandle *handle, Channel *chan) {
|
||||
if(index == -1) {
|
||||
warning("SoundMixer::out of mixer slots");
|
||||
delete chan;
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
_channels[index] = chan;
|
||||
if (handle)
|
||||
*handle = index + 1;
|
||||
return index;
|
||||
handle->setIndex(index);
|
||||
}
|
||||
|
||||
int SoundMixer::playRaw(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, int id, byte volume, int8 pan, uint32 loopStart, uint32 loopEnd) {
|
||||
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]->_id == id)
|
||||
return -1;
|
||||
if (_channels[i] != 0 && _channels[i]->getId() == id) {
|
||||
if ((flags & SoundMixer::FLAG_AUTOFREE) != 0)
|
||||
free(sound);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return insertChannel(handle, new ChannelRaw(this, handle, sound, size, rate, flags, volume, pan, id, loopStart, loopEnd));
|
||||
// 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, byte volume, int8 balance, int id, bool autofreeStream) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
if (input == 0) {
|
||||
warning("input stream is 0");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent duplicate sounds
|
||||
if (id != -1) {
|
||||
for (int i = 0; i != NUM_CHANNELS; i++)
|
||||
if (_channels[i] != 0 && _channels[i]->getId() == id) {
|
||||
if (autofreeStream)
|
||||
delete input;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the channel
|
||||
Channel *chan = new Channel(this, handle, input, autofreeStream, isMusic, false, id);
|
||||
chan->setVolume(volume);
|
||||
chan->setBalance(balance);
|
||||
insertChannel(handle, chan);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
// zero the buf
|
||||
memset(buf, 0, 2 * len * sizeof(int16));
|
||||
|
||||
if (!_paused) {
|
||||
if (_premixProc)
|
||||
_premixProc(_premixParam, buf, len);
|
||||
|
||||
// now mix all channels
|
||||
for (int i = 0; i != NUM_CHANNELS; i++)
|
||||
if (_channels[i] && !_channels[i]->isPaused())
|
||||
_channels[i]->mix(buf, len);
|
||||
if (_channels[i]) {
|
||||
if (_channels[i]->isFinished()) {
|
||||
delete _channels[i];
|
||||
_channels[i] = 0;
|
||||
} else if (!_channels[i]->isPaused())
|
||||
_channels[i]->mix(buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,27 +294,18 @@ void SoundMixer::mixCallback(void *s, byte *samples, int len) {
|
||||
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();
|
||||
if (_channels[i] != 0) {
|
||||
delete _channels[i];
|
||||
_channels[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (_channels[i] != 0 && _channels[i]->getId() == id) {
|
||||
delete _channels[i];
|
||||
_channels[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -281,27 +314,29 @@ void SoundMixer::stopHandle(PlayingSoundHandle handle) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
// Simply ignore stop requests for handles of sounds that already terminated
|
||||
if (handle == 0)
|
||||
if (!handle.isActive())
|
||||
return;
|
||||
|
||||
int index = handle - 1;
|
||||
int index = handle.getIndex();
|
||||
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::stopHandle has invalid index %d", index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_channels[index])
|
||||
_channels[index]->destroy();
|
||||
if (_channels[index]) {
|
||||
delete _channels[index];
|
||||
_channels[index] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMixer::setChannelVolume(PlayingSoundHandle handle, byte volume) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
if (handle == 0)
|
||||
if (!handle.isActive())
|
||||
return;
|
||||
|
||||
int index = handle - 1;
|
||||
int index = handle.getIndex();
|
||||
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::setChannelVolume has invalid index %d", index);
|
||||
@ -309,45 +344,34 @@ void SoundMixer::setChannelVolume(PlayingSoundHandle handle, byte volume) {
|
||||
}
|
||||
|
||||
if (_channels[index])
|
||||
_channels[index]->setChannelVolume(volume);
|
||||
_channels[index]->setVolume(volume);
|
||||
}
|
||||
|
||||
void SoundMixer::setChannelPan(PlayingSoundHandle handle, int8 pan) {
|
||||
void SoundMixer::setChannelBalance(PlayingSoundHandle handle, int8 balance) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
if (handle == 0)
|
||||
if (!handle.isActive())
|
||||
return;
|
||||
|
||||
int index = handle - 1;
|
||||
int index = handle.getIndex();
|
||||
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::setChannelVolume has invalid index %d", index);
|
||||
warning("soundMixer::setChannelBalance has invalid index %d", index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_channels[index])
|
||||
_channels[index]->setChannelPan(pan);
|
||||
_channels[index]->setBalance(balance);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (_channels[i] != 0 && _channels[i]->getId() == id) {
|
||||
_channels[i]->pause(paused);
|
||||
return;
|
||||
}
|
||||
@ -358,10 +382,10 @@ 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)
|
||||
if (!handle.isActive())
|
||||
return;
|
||||
|
||||
int index = handle - 1;
|
||||
int index = handle.getIndex();
|
||||
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::pauseHandle has invalid index %d", index);
|
||||
@ -382,18 +406,29 @@ void SoundMixer::setVolume(int volume) {
|
||||
_globalVolume = volume;
|
||||
}
|
||||
|
||||
Channel::~Channel() {
|
||||
delete _converter;
|
||||
delete _input;
|
||||
if (_handle)
|
||||
*_handle = 0;
|
||||
Channel::Channel(SoundMixer *mixer, PlayingSoundHandle *handle, bool isMusic, int id)
|
||||
: _mixer(mixer), _handle(handle), _autofreeStream(true), _isMusic(isMusic),
|
||||
_volume(255), _balance(0), _paused(false), _id(id), _converter(0), _input(0) {
|
||||
assert(mixer);
|
||||
}
|
||||
|
||||
void Channel::destroy() {
|
||||
for (int i = 0; i != SoundMixer::NUM_CHANNELS; i++)
|
||||
if (_mixer->_channels[i] == this)
|
||||
_mixer->_channels[i] = 0;
|
||||
delete this;
|
||||
Channel::Channel(SoundMixer *mixer, PlayingSoundHandle *handle, AudioStream *input,
|
||||
bool autofreeStream, bool isMusic, bool reverseStereo, int id)
|
||||
: _mixer(mixer), _handle(handle), _autofreeStream(autofreeStream), _isMusic(isMusic),
|
||||
_volume(255), _balance(0), _paused(false), _id(id), _converter(0), _input(input) {
|
||||
assert(mixer);
|
||||
assert(input);
|
||||
|
||||
// Get a rate converter instance
|
||||
_converter = makeRateConverter(_input->getRate(), mixer->getOutputRate(), _input->isStereo(), reverseStereo);
|
||||
}
|
||||
|
||||
Channel::~Channel() {
|
||||
delete _converter;
|
||||
if (_autofreeStream)
|
||||
delete _input;
|
||||
if (_handle)
|
||||
_handle->resetIndex();
|
||||
}
|
||||
|
||||
/* len indicates the number of sample *pairs*. So a value of
|
||||
@ -402,99 +437,52 @@ void Channel::destroy() {
|
||||
*/
|
||||
void Channel::mix(int16 *data, uint len) {
|
||||
assert(_input);
|
||||
if (_input->eos()) {
|
||||
|
||||
if (_input->endOfData()) {
|
||||
// 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.
|
||||
// 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
|
||||
|
||||
int vol = getVolume() * _volume;
|
||||
st_volume_t vol_l = (127 - _pan) * vol / (255 * 254);
|
||||
st_volume_t vol_r = (127 + _pan) * vol / (255 * 254);
|
||||
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 {
|
||||
vol_l = ((127 - _balance) * vol) / (255 * 127);
|
||||
vol_r = vol / 255;
|
||||
}
|
||||
|
||||
_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);
|
||||
|
||||
uint rate, byte flags, uint32 buffer_size)
|
||||
: Channel(mixer, handle, true) {
|
||||
// Create the input stream
|
||||
_input = makeWrappedInputStream(flags, buffer_size);
|
||||
|
||||
// Append the initial data
|
||||
((WrappedAudioInputStream *)_input)->append((const byte *)sound, size);
|
||||
_input = makeAppendableAudioStream(rate, flags, buffer_size);
|
||||
|
||||
// Get a rate converter instance
|
||||
_converter = makeRateConverter(rate, mixer->getOutputRate(), _input->isStereo(), (flags & SoundMixer::FLAG_REVERSE_STEREO) != 0);
|
||||
_converter = makeRateConverter(_input->getRate(), mixer->getOutputRate(), _input->isStereo(), (flags & SoundMixer::FLAG_REVERSE_STEREO) != 0);
|
||||
}
|
||||
|
||||
_finished = false;
|
||||
void ChannelStream::finish() {
|
||||
((AppendableAudioStream *)_input)->finish();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
((AppendableAudioStream *)_input)->append((const byte *)data, len);
|
||||
}
|
||||
|
@ -22,13 +22,24 @@
|
||||
#include "../bits.h"
|
||||
#include <SDL.h>
|
||||
|
||||
typedef uint32 PlayingSoundHandle;
|
||||
typedef void (*SoundProc)(void *param, byte *buf, int len);
|
||||
|
||||
class AudioStream;
|
||||
class Channel;
|
||||
|
||||
class SoundMixer {
|
||||
class PlayingSoundHandle {
|
||||
friend class Channel;
|
||||
friend class SoundMixer;
|
||||
int val;
|
||||
int getIndex() const { return val - 1; }
|
||||
void setIndex(int i) { val = i + 1; }
|
||||
void resetIndex() { val = 0; }
|
||||
public:
|
||||
PlayingSoundHandle() { resetIndex(); }
|
||||
bool isActive() const { return val > 0; }
|
||||
};
|
||||
|
||||
typedef void (*SoundProc)(void *param, byte *buf, int len);
|
||||
|
||||
class SoundMixer {
|
||||
public:
|
||||
typedef void PremixProc (void *param, int16 *data, uint len);
|
||||
|
||||
@ -37,12 +48,13 @@ public:
|
||||
};
|
||||
|
||||
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
|
||||
FLAG_UNSIGNED = 1 << 0, /** unsigned samples (default: signed) */
|
||||
FLAG_16BITS = 1 << 1, /** sound is 16 bits wide (default: 8bit) */
|
||||
FLAG_LITTLE_ENDIAN = 1 << 2, /** sample is little endian (default: big endian) */
|
||||
FLAG_STEREO = 1 << 3, /** sound is in stereo (default: mono) */
|
||||
FLAG_REVERSE_STEREO = 1 << 4, /** reverse the left and right stereo channel */
|
||||
FLAG_AUTOFREE = 1 << 5, /** sound buffer is freed automagically at the end of playing */
|
||||
FLAG_LOOP = 1 << 6 /** loop the audio */
|
||||
};
|
||||
|
||||
private:
|
||||
@ -52,10 +64,9 @@ private:
|
||||
PremixProc *_premixProc;
|
||||
|
||||
uint _outputRate;
|
||||
|
||||
int _globalVolume;
|
||||
|
||||
bool _paused;
|
||||
bool _mixerReady;
|
||||
|
||||
Channel *_channels[NUM_CHANNELS];
|
||||
|
||||
@ -63,16 +74,18 @@ public:
|
||||
SoundMixer();
|
||||
~SoundMixer();
|
||||
|
||||
void bindToSystem();
|
||||
bool isReady() const { return _mixerReady; };
|
||||
|
||||
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);
|
||||
void playRaw(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags,
|
||||
int id = -1, byte volume = 255, int8 balance = 0, uint32 loopStart = 0, uint32 loopEnd = 0);
|
||||
|
||||
void playInputStream(PlayingSoundHandle *handle, AudioStream *input, bool isMusic, byte volume = 255, int8 balance = 0, int id = -1, bool autofreeStream = true);
|
||||
|
||||
/** 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);
|
||||
void newStream(PlayingSoundHandle *handle, uint rate, byte flags, uint32 buffer_size, byte volume = 255, int8 balance = 0);
|
||||
|
||||
/** Append to an existing stream. */
|
||||
void appendStream(PlayingSoundHandle handle, void *sound, uint32 size);
|
||||
@ -83,9 +96,6 @@ public:
|
||||
/** 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);
|
||||
|
||||
@ -95,9 +105,6 @@ public:
|
||||
/** 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);
|
||||
|
||||
@ -107,8 +114,8 @@ public:
|
||||
/** 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 channel balance for the given handle (-127 ... 0 ... 127) (left ... center ... right)*/
|
||||
void setChannelBalance(PlayingSoundHandle handle, int8 balance);
|
||||
|
||||
/** set the global volume, 0-256 */
|
||||
void setVolume(int volume);
|
||||
@ -120,10 +127,12 @@ public:
|
||||
uint getOutputRate() const { return _outputRate; }
|
||||
|
||||
private:
|
||||
int insertChannel(PlayingSoundHandle *handle, Channel *chan);
|
||||
bool setSoundProc(SoundProc proc, void *param);
|
||||
|
||||
void insertChannel(PlayingSoundHandle *handle, Channel *chan);
|
||||
|
||||
/** main mixer method */
|
||||
void mix(int16 *buf, uint len);
|
||||
void mix(int16 * buf, uint len);
|
||||
|
||||
static void mixCallback(void *s, byte *samples, int len);
|
||||
};
|
||||
|
@ -42,7 +42,6 @@
|
||||
*/
|
||||
#define INTERMEDIATE_BUFFER_SIZE 512
|
||||
|
||||
|
||||
/**
|
||||
* Audio rate converter based on simple linear Interpolation.
|
||||
*
|
||||
@ -77,13 +76,12 @@ protected:
|
||||
|
||||
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 flow(AudioStream &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.
|
||||
*/
|
||||
@ -121,7 +119,7 @@ LinearRateConverter<stereo, reverseStereo>::LinearRateConverter(st_rate_t inrate
|
||||
* 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) {
|
||||
int LinearRateConverter<stereo, reverseStereo>::flow(AudioStream &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];
|
||||
|
||||
@ -188,21 +186,51 @@ the_end:
|
||||
*/
|
||||
template<bool stereo, bool reverseStereo>
|
||||
class CopyRateConverter : public RateConverter {
|
||||
st_sample_t *_buffer;
|
||||
st_size_t _bufferSize;
|
||||
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;
|
||||
CopyRateConverter() : _buffer(0), _bufferSize(0) {}
|
||||
~CopyRateConverter() {
|
||||
free(_buffer);
|
||||
}
|
||||
|
||||
virtual int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
|
||||
assert(input.isStereo() == stereo);
|
||||
while (!input.eos() && len--) {
|
||||
tmp[0] = tmp[1] = input.read();
|
||||
if (stereo)
|
||||
tmp[reverseStereo ? 0 : 1] = input.read();
|
||||
|
||||
st_sample_t *ptr;
|
||||
st_size_t len;
|
||||
|
||||
if (stereo)
|
||||
osamp *= 2;
|
||||
|
||||
// Reallocate temp buffer, if necessary
|
||||
if (osamp > _bufferSize) {
|
||||
free(_buffer);
|
||||
_buffer = (st_sample_t *)malloc(osamp * 2);
|
||||
_bufferSize = osamp;
|
||||
}
|
||||
|
||||
// Read up to 'osamp' samples into our temporary buffer
|
||||
len = input.readBuffer(_buffer, osamp);
|
||||
|
||||
// Mix the data into the output buffer
|
||||
ptr = _buffer;
|
||||
while (len--) {
|
||||
st_sample_t tmp0, tmp1;
|
||||
tmp0 = tmp1 = *ptr++;
|
||||
if (stereo) {
|
||||
if (reverseStereo)
|
||||
tmp0 = *ptr++;
|
||||
else
|
||||
tmp1 = *ptr++;
|
||||
len--;
|
||||
}
|
||||
|
||||
// output left channel
|
||||
clampedAdd(*obuf++, (tmp[0] * (int)vol_l) >> 8);
|
||||
clampedAdd(*obuf++, (tmp0 * (int)vol_l) >> 8);
|
||||
|
||||
// output right channel
|
||||
clampedAdd(*obuf++, (tmp[1] * (int)vol_r) >> 8);
|
||||
clampedAdd(*obuf++, (tmp1 * (int)vol_r) >> 8);
|
||||
}
|
||||
return (ST_SUCCESS);
|
||||
}
|
||||
@ -223,6 +251,7 @@ RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stere
|
||||
return new LinearRateConverter<true, false>(inrate, outrate);
|
||||
} else
|
||||
return new LinearRateConverter<false, false>(inrate, outrate);
|
||||
//return new ResampleRateConverter(inrate, outrate, 1);
|
||||
} else {
|
||||
if (stereo) {
|
||||
if (reverseStereo)
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
#include "../bits.h"
|
||||
|
||||
class AudioInputStream;
|
||||
class AudioStream;
|
||||
|
||||
typedef int16 st_sample_t;
|
||||
typedef uint16 st_volume_t;
|
||||
@ -64,7 +64,7 @@ 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 flow(AudioStream &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;
|
||||
};
|
||||
|
||||
|
44
smush.cpp
44
smush.cpp
@ -93,7 +93,6 @@ Smush::Smush() {
|
||||
_speed = 0;
|
||||
_channels = -1;
|
||||
_freq = 0;
|
||||
_soundHandle = 0;
|
||||
_freq = 22050;
|
||||
}
|
||||
|
||||
@ -136,11 +135,9 @@ void Smush::handleWave(const byte *src, uint32 size) {
|
||||
int flags = SoundMixer::FLAG_16BITS | SoundMixer::FLAG_AUTOFREE;
|
||||
if (_channels == 2)
|
||||
flags |= SoundMixer::FLAG_STEREO;
|
||||
if (_soundHandle == 0)
|
||||
g_mixer->newStream(&_soundHandle, (byte *)dst, size * _channels * 2, _freq,
|
||||
flags, 500000);
|
||||
else
|
||||
g_mixer->appendStream(_soundHandle, (byte *)dst, size * _channels * 2);
|
||||
if (!_soundHandle.isActive())
|
||||
g_mixer->newStream(&_soundHandle, _freq, flags, 500000);
|
||||
g_mixer->appendStream(_soundHandle, (byte *)dst, size * _channels * 2);
|
||||
}
|
||||
|
||||
void Smush::handleFrame() {
|
||||
@ -236,24 +233,23 @@ void Smush::play(const char *filename, const char *directory) {
|
||||
char inBuf[1024], outBuf[1024], flags;
|
||||
int status = 0;
|
||||
|
||||
|
||||
warning("Decompressing SMUSH cutscene %s - This may take a minute", filename);
|
||||
fread(inBuf, 4, sizeof(char), tmp); // Header, Method, Flags
|
||||
fread(inBuf, 4, sizeof(byte), tmp); // Header, Method, Flags
|
||||
flags = inBuf[4];
|
||||
fread(inBuf, 6, sizeof(char), tmp); // XFlags
|
||||
fread(inBuf, 6, sizeof(byte), tmp); // XFlags
|
||||
|
||||
if (((flags & 0x04) != 0) || ((flags & 0x10) != 0)) // Misc
|
||||
error("Unsupported header flag");
|
||||
|
||||
if ((flags & 0x08) != 0) { // Name
|
||||
while(inBuf[0] != 0) {
|
||||
fread(inBuf, 1, sizeof(char), tmp);
|
||||
printf("%c", inBuf[0]);
|
||||
}
|
||||
while(inBuf[0] != 0) {
|
||||
fread(inBuf, 1, sizeof(byte), tmp);
|
||||
printf("%c", inBuf[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & 0x02 != 0)) // CRC
|
||||
fread(inBuf, 2, sizeof(char), tmp);
|
||||
if ((flags & 0x02) != 0) // CRC
|
||||
fread(inBuf, 2, sizeof(byte), tmp);
|
||||
|
||||
z.zalloc = NULL;
|
||||
z.zfree = NULL;
|
||||
@ -262,15 +258,15 @@ void Smush::play(const char *filename, const char *directory) {
|
||||
if (inflateInit2(&z, -15) != Z_OK)
|
||||
error("Smush::play() - inflateInit error");
|
||||
|
||||
z.next_in = (Bytef*)inBuf;
|
||||
z.avail_in = fread(inBuf, 1, sizeof(inBuf), tmp);
|
||||
z.next_in = (Bytef *)inBuf;
|
||||
z.avail_in = (uInt)fread(inBuf, 1, sizeof(inBuf), tmp);
|
||||
z.next_out = (Bytef *)outBuf;
|
||||
z.avail_out = sizeof(outBuf);
|
||||
|
||||
while(1) {
|
||||
for (;;) {
|
||||
if (z.avail_in == 0) {
|
||||
z.next_in = (Bytef*)inBuf;
|
||||
z.avail_in = fread(inBuf, 1, sizeof(inBuf), tmp);
|
||||
z.avail_in = (uInt)fread(inBuf, 1, sizeof(inBuf), tmp);
|
||||
}
|
||||
|
||||
status = inflate(&z, Z_NO_FLUSH);
|
||||
@ -293,7 +289,7 @@ void Smush::play(const char *filename, const char *directory) {
|
||||
outFile = fopen(tmpOut, "wb");
|
||||
|
||||
fwrite(outBuf, 1, sizeof(outBuf), outFile);
|
||||
z.next_out = (Bytef*)outBuf;
|
||||
z.next_out = (Bytef *)outBuf;
|
||||
z.avail_out = sizeof(outBuf);
|
||||
}
|
||||
}
|
||||
@ -360,7 +356,7 @@ FILE *File::fopenNoCase(const char *filename, const char *directory, const char
|
||||
|
||||
// Record the length of the dir name (so we can cut of anything trailing it
|
||||
// later, when we try with different file names).
|
||||
const int dirLen = strlen(buf);
|
||||
const int dirLen = (int)strlen(buf);
|
||||
|
||||
if (dirLen > 0) {
|
||||
strcat(buf, "/"); // prevent double /
|
||||
@ -380,7 +376,7 @@ FILE *File::fopenNoCase(const char *filename, const char *directory, const char
|
||||
for (int dirIdx = 0; dirIdx < ARRAYSIZE(dirs); dirIdx++) {
|
||||
buf[dirLen] = 0;
|
||||
strcat(buf, dirs[dirIdx]);
|
||||
int8 len = strlen(buf);
|
||||
int len = (int)strlen(buf);
|
||||
strcat(buf, filename);
|
||||
|
||||
ptr = buf + len;
|
||||
@ -443,7 +439,7 @@ bool File::open(const char *filename, const char *directory, int mode, byte encb
|
||||
|
||||
_encbyte = encbyte;
|
||||
|
||||
int len = strlen(filename);
|
||||
int len = (int)strlen(filename);
|
||||
if (_name != 0)
|
||||
delete [] _name;
|
||||
_name = new char[len+1];
|
||||
@ -524,7 +520,7 @@ uint32 File::read(void *ptr, uint32 len) {
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
real_len = fread(ptr2, 1, len, _handle);
|
||||
real_len = (uint32)fread(ptr2, 1, len, _handle);
|
||||
if (real_len < len) {
|
||||
clearerr(_handle);
|
||||
_ioFailed = true;
|
||||
|
Loading…
x
Reference in New Issue
Block a user