diff options
Diffstat (limited to 'audio')
80 files changed, 1626 insertions, 780 deletions
diff --git a/audio/audiostream.cpp b/audio/audiostream.cpp index 0c41a38254..776f904e77 100644 --- a/audio/audiostream.cpp +++ b/audio/audiostream.cpp @@ -24,14 +24,14 @@ */ #include "common/debug.h" -#include "common/endian.h" #include "common/file.h" +#include "common/mutex.h" +#include "common/textconsole.h" #include "common/queue.h" #include "common/util.h" #include "audio/audiostream.h" #include "audio/decoders/flac.h" -#include "audio/mixer.h" #include "audio/decoders/mp3.h" #include "audio/decoders/raw.h" #include "audio/decoders/vorbis.h" diff --git a/audio/audiostream.h b/audio/audiostream.h index cd6456cc70..22de21cb34 100644 --- a/audio/audiostream.h +++ b/audio/audiostream.h @@ -26,16 +26,14 @@ #ifndef SOUND_AUDIOSTREAM_H #define SOUND_AUDIOSTREAM_H -#include "common/util.h" #include "common/scummsys.h" +#include "common/str.h" #include "common/types.h" #include "audio/timestamp.h" namespace Audio { -class SeekableAudioStream; - /** * Generic audio input stream. Subclasses of this are used to feed arbitrary * sampled audio data into ScummVM's audio mixer. diff --git a/audio/decoders/adpcm.cpp b/audio/decoders/adpcm.cpp index 7def89b688..a9284973d5 100644 --- a/audio/decoders/adpcm.cpp +++ b/audio/decoders/adpcm.cpp @@ -23,47 +23,16 @@ * */ -#include "common/endian.h" +#include "common/stream.h" +#include "common/textconsole.h" +#include "common/util.h" #include "audio/decoders/adpcm.h" -#include "audio/audiostream.h" +#include "audio/decoders/adpcm_intern.h" namespace Audio { -class ADPCMStream : public RewindableAudioStream { -protected: - Common::SeekableReadStream *_stream; - const DisposeAfterUse::Flag _disposeAfterUse; - const int32 _startpos; - const int32 _endpos; - const int _channels; - const uint32 _blockAlign; - uint32 _blockPos[2]; - const int _rate; - - struct { - // OKI/IMA - struct { - int32 last; - int32 stepIndex; - } ima_ch[2]; - } _status; - - virtual void reset(); - int16 stepAdjust(byte); - -public: - ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign); - ~ADPCMStream(); - - virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos); } - virtual bool isStereo() const { return _channels == 2; } - virtual int getRate() const { return _rate; } - - virtual bool rewind(); -}; - // Routines to convert 12 bit linear samples to the // Dialogic or Oki ADPCM coding format aka VOX. // See also <http://www.comptek.ru/telephony/tnotes/tt1-13.html> @@ -107,17 +76,6 @@ bool ADPCMStream::rewind() { #pragma mark - -class Oki_ADPCMStream : public ADPCMStream { -public: - Oki_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} - - virtual int readBuffer(int16 *buffer, const int numSamples); - -protected: - int16 decodeOKI(byte); -}; - int Oki_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { int samples; byte data; @@ -153,7 +111,7 @@ int16 Oki_ADPCMStream::decodeOKI(byte code) { samp = CLIP<int16>(samp, -2048, 2047); _status.ima_ch[0].last = samp; - _status.ima_ch[0].stepIndex += stepAdjust(code); + _status.ima_ch[0].stepIndex += _stepAdjustTable[code]; _status.ima_ch[0].stepIndex = CLIP<int32>(_status.ima_ch[0].stepIndex, 0, ARRAYSIZE(okiStepSize) - 1); // * 16 effectively converts 12-bit input to 16-bit output @@ -164,20 +122,7 @@ int16 Oki_ADPCMStream::decodeOKI(byte code) { #pragma mark - -class Ima_ADPCMStream : public ADPCMStream { -protected: - int16 decodeIMA(byte code, int channel = 0); // Default to using the left channel/using one channel - -public: - Ima_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { - memset(&_status, 0, sizeof(_status)); - } - - virtual int readBuffer(int16 *buffer, const int numSamples); -}; - -int Ima_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { +int DVI_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { int samples; byte data; @@ -194,34 +139,6 @@ int Ima_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { #pragma mark - -class Apple_ADPCMStream : public Ima_ADPCMStream { -protected: - // Apple QuickTime IMA ADPCM - int32 _streamPos[2]; - int16 _buffer[2][2]; - uint8 _chunkPos[2]; - - void reset() { - Ima_ADPCMStream::reset(); - _chunkPos[0] = 0; - _chunkPos[1] = 0; - _streamPos[0] = 0; - _streamPos[1] = _blockAlign; - } - -public: - Apple_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { - _chunkPos[0] = 0; - _chunkPos[1] = 0; - _streamPos[0] = 0; - _streamPos[1] = _blockAlign; - } - - virtual int readBuffer(int16 *buffer, const int numSamples); - -}; - int Apple_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { // Need to write at least one samples per channel assert((numSamples % _channels) == 0); @@ -285,82 +202,49 @@ int Apple_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { return samples[0] + samples[1]; } -#pragma mark - +#pragma mark - -class MSIma_ADPCMStream : public Ima_ADPCMStream { -public: - MSIma_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign, bool invertSamples = false) - : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign), _invertSamples(invertSamples) { - if (blockAlign == 0) - error("ADPCMStream(): blockAlign isn't specified for MS IMA ADPCM"); - } - - virtual int readBuffer(int16 *buffer, const int numSamples) { - if (_channels == 1) - return readBufferMSIMA1(buffer, numSamples); - else - return readBufferMSIMA2(buffer, numSamples); - } - - int readBufferMSIMA1(int16 *buffer, const int numSamples); - int readBufferMSIMA2(int16 *buffer, const int numSamples); -private: - bool _invertSamples; // Some implementations invert the way samples are decoded -}; +int MSIma_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { + // Need to write at least one sample per channel + assert((numSamples % _channels) == 0); -int MSIma_ADPCMStream::readBufferMSIMA1(int16 *buffer, const int numSamples) { int samples = 0; - byte data; - - assert(numSamples % 2 == 0); while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { if (_blockPos[0] == _blockAlign) { - // read block header - _status.ima_ch[0].last = _stream->readSint16LE(); - _status.ima_ch[0].stepIndex = _stream->readSint16LE(); - _blockPos[0] = 4; - } + for (int i = 0; i < _channels; i++) { + // read block header + _status.ima_ch[i].last = _stream->readSint16LE(); + _status.ima_ch[i].stepIndex = _stream->readSint16LE(); + } - for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) { - data = _stream->readByte(); - _blockPos[0]++; - buffer[samples] = decodeIMA(_invertSamples ? (data >> 4) & 0x0f : data & 0x0f); - buffer[samples + 1] = decodeIMA(_invertSamples ? data & 0x0f : (data >> 4) & 0x0f); + _blockPos[0] = _channels * 4; } - } - return samples; -} + // Decode a set of samples + for (int i = 0; i < _channels; i++) { + // The stream encodes four bytes per channel at a time + for (int j = 0; j < 4; j++) { + byte data = _stream->readByte(); + _blockPos[0]++; + _buffer[i][j * 2] = decodeIMA(data & 0x0f, i); + _buffer[i][j * 2 + 1] = decodeIMA((data >> 4) & 0x0f, i); + _samplesLeft[i] += 2; + } + } -// Microsoft as usual tries to implement it differently. This method -// is used for stereo data. -int MSIma_ADPCMStream::readBufferMSIMA2(int16 *buffer, const int numSamples) { - int samples; - uint32 data; - int nibble; - byte k; - - // TODO: Currently this implementation only supports - // reading a multiple of 16 samples at once. We might - // consider changing that so it could read an arbitrary - // sample pair count. - assert(numSamples % 16 == 0); - - for (samples = 0; samples < numSamples && !_stream->eos() && _stream->pos() < _endpos;) { - for (int channel = 0; channel < 2; channel++) { - data = _stream->readUint32LE(); - - for (nibble = 0; nibble < 8; nibble++) { - k = ((data & 0xf0000000) >> 28); - buffer[samples + channel + nibble * 2] = decodeIMA(k); - data <<= 4; + while (samples < numSamples && _samplesLeft[0] != 0) { + for (int i = 0; i < _channels; i++) { + buffer[samples] = _buffer[i][8 - _samplesLeft[i]]; + _samplesLeft[i]--; } + + samples += _channels; } - samples += 16; } + return samples; } @@ -382,41 +266,6 @@ static const int MSADPCMAdaptationTable[] = { }; -class MS_ADPCMStream : public ADPCMStream { -protected: - struct ADPCMChannelStatus { - byte predictor; - int16 delta; - int16 coeff1; - int16 coeff2; - int16 sample1; - int16 sample2; - }; - - struct { - // MS ADPCM - ADPCMChannelStatus ch[2]; - } _status; - - void reset() { - ADPCMStream::reset(); - memset(&_status, 0, sizeof(_status)); - } - -public: - MS_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { - if (blockAlign == 0) - error("MS_ADPCMStream(): blockAlign isn't specified for MS ADPCM"); - memset(&_status, 0, sizeof(_status)); - } - - virtual int readBuffer(int16 *buffer, const int numSamples); - -protected: - int16 decodeMS(ADPCMChannelStatus *c, byte); -}; - int16 MS_ADPCMStream::decodeMS(ADPCMChannelStatus *c, byte code) { int32 predictor; @@ -478,246 +327,9 @@ int MS_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { } - #pragma mark - -class Tinsel_ADPCMStream : public ADPCMStream { -protected: - struct { - // Tinsel - double predictor; - double K0, K1; - double d0, d1; - } _status; - - void reset() { - ADPCMStream::reset(); - memset(&_status, 0, sizeof(_status)); - } - - int16 decodeTinsel(int16, double); - void readBufferTinselHeader(); - -public: - Tinsel_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { - - if (blockAlign == 0) - error("Tinsel_ADPCMStream(): blockAlign isn't specified"); - - if (channels != 1) - error("Tinsel_ADPCMStream(): Tinsel ADPCM only supports mono"); - - memset(&_status, 0, sizeof(_status)); - } - -}; - -static const double TinselFilterTable[4][2] = { - {0, 0 }, - {0.9375, 0}, - {1.796875, -0.8125}, - {1.53125, -0.859375} -}; - -void Tinsel_ADPCMStream::readBufferTinselHeader() { - uint8 start = _stream->readByte(); - uint8 filterVal = (start & 0xC0) >> 6; - - if ((start & 0x20) != 0) { - //Lower 6 bit are negative - - // Negate - start = ~(start | 0xC0) + 1; - - _status.predictor = 1 << start; - } else { - // Lower 6 bit are positive - - // Truncate - start &= 0x1F; - - _status.predictor = ((double) 1.0) / (1 << start); - } - - _status.K0 = TinselFilterTable[filterVal][0]; - _status.K1 = TinselFilterTable[filterVal][1]; -} - -int16 Tinsel_ADPCMStream::decodeTinsel(int16 code, double eVal) { - double sample; - - sample = (double) code; - sample *= eVal * _status.predictor; - sample += (_status.d0 * _status.K0) + (_status.d1 * _status.K1); - - _status.d1 = _status.d0; - _status.d0 = sample; - - return (int16) CLIP<double>(sample, -32768.0, 32767.0); -} - -class Tinsel4_ADPCMStream : public Tinsel_ADPCMStream { -public: - Tinsel4_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} - - virtual int readBuffer(int16 *buffer, const int numSamples); -}; - -int Tinsel4_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { - int samples; - uint16 data; - const double eVal = 1.142822265; - - samples = 0; - - assert(numSamples % 2 == 0); - - while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { - if (_blockPos[0] == _blockAlign) { - readBufferTinselHeader(); - _blockPos[0] = 0; - } - - for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2, _blockPos[0]++) { - // Read 1 byte = 8 bits = two 4 bit blocks - data = _stream->readByte(); - buffer[samples] = decodeTinsel((data << 8) & 0xF000, eVal); - buffer[samples+1] = decodeTinsel((data << 12) & 0xF000, eVal); - } - } - - return samples; -} - -class Tinsel6_ADPCMStream : public Tinsel_ADPCMStream { -protected: - uint8 _chunkPos; - uint16 _chunkData; - - void reset() { - ADPCMStream::reset(); - _chunkPos = 0; - _chunkData = 0; - } - -public: - Tinsel6_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { - _chunkPos = 0; - _chunkData = 0; - } - - virtual int readBuffer(int16 *buffer, const int numSamples); -}; - -int Tinsel6_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { - int samples; - const double eVal = 1.032226562; - - samples = 0; - - while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { - if (_blockPos[0] == _blockAlign) { - readBufferTinselHeader(); - _blockPos[0] = 0; - _chunkPos = 0; - } - - for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _chunkPos = (_chunkPos + 1) % 4) { - - switch (_chunkPos) { - case 0: - _chunkData = _stream->readByte(); - buffer[samples] = decodeTinsel((_chunkData << 8) & 0xFC00, eVal); - break; - case 1: - _chunkData = (_chunkData << 8) | (_stream->readByte()); - buffer[samples] = decodeTinsel((_chunkData << 6) & 0xFC00, eVal); - _blockPos[0]++; - break; - case 2: - _chunkData = (_chunkData << 8) | (_stream->readByte()); - buffer[samples] = decodeTinsel((_chunkData << 4) & 0xFC00, eVal); - _blockPos[0]++; - break; - case 3: - _chunkData = (_chunkData << 8); - buffer[samples] = decodeTinsel((_chunkData << 2) & 0xFC00, eVal); - _blockPos[0]++; - break; - } - - } - - } - - return samples; -} - -class Tinsel8_ADPCMStream : public Tinsel_ADPCMStream { -public: - Tinsel8_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} - - virtual int readBuffer(int16 *buffer, const int numSamples); -}; - -int Tinsel8_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { - int samples; - byte data; - const double eVal = 1.007843258; - - samples = 0; - - while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { - if (_blockPos[0] == _blockAlign) { - readBufferTinselHeader(); - _blockPos[0] = 0; - } - - for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _blockPos[0]++) { - // Read 1 byte = 8 bits = one 8 bit block - data = _stream->readByte(); - buffer[samples] = decodeTinsel(data << 8, eVal); - } - } - - return samples; -} - - -#pragma mark - - -// Duck DK3 IMA ADPCM Decoder -// Based on FFmpeg's decoder and http://wiki.multimedia.cx/index.php?title=Duck_DK3_IMA_ADPCM - -class DK3_ADPCMStream : public Ima_ADPCMStream { -protected: - - void reset() { - Ima_ADPCMStream::reset(); - _topNibble = false; - } - -public: - DK3_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { - - // DK3 only works as a stereo stream - assert(channels == 2); - _topNibble = false; - } - - virtual int readBuffer(int16 *buffer, const int numSamples); - -private: - byte _nibble, _lastByte; - bool _topNibble; -}; - #define DK3_READ_NIBBLE() \ do { \ if (_topNibble) { \ @@ -782,14 +394,15 @@ int DK3_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { #pragma mark - -// adjust the step for use on the next sample. -int16 ADPCMStream::stepAdjust(byte code) { - static const int16 adjusts[] = {-1, -1, -1, -1, 2, 4, 6, 8}; - - return adjusts[code & 0x07]; -} +// This table is used to adjust the step for use on the next sample. +// We could half the table, but since the lookup index used is always +// a 4-bit nibble, it's more efficient to just keep it as it is. +const int16 ADPCMStream::_stepAdjustTable[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; -static const uint16 imaStepTable[89] = { +const int16 Ima_ADPCMStream::_imaTable[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, @@ -805,13 +418,13 @@ static const uint16 imaStepTable[89] = { }; int16 Ima_ADPCMStream::decodeIMA(byte code, int channel) { - int32 E = (2 * (code & 0x7) + 1) * imaStepTable[_status.ima_ch[channel].stepIndex] / 8; + int32 E = (2 * (code & 0x7) + 1) * _imaTable[_status.ima_ch[channel].stepIndex] / 8; int32 diff = (code & 0x08) ? -E : E; int32 samp = CLIP<int32>(_status.ima_ch[channel].last + diff, -32768, 32767); _status.ima_ch[channel].last = samp; - _status.ima_ch[channel].stepIndex += stepAdjust(code); - _status.ima_ch[channel].stepIndex = CLIP<int32>(_status.ima_ch[channel].stepIndex, 0, ARRAYSIZE(imaStepTable) - 1); + _status.ima_ch[channel].stepIndex += _stepAdjustTable[code]; + _status.ima_ch[channel].stepIndex = CLIP<int32>(_status.ima_ch[channel].stepIndex, 0, ARRAYSIZE(_imaTable) - 1); return samp; } @@ -826,18 +439,10 @@ RewindableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, Dispo return new Oki_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); case kADPCMMSIma: return new MSIma_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); - case kADPCMMSImaLastExpress: - return new MSIma_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign, true); case kADPCMMS: return new MS_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); - case kADPCMTinsel4: - return new Tinsel4_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); - case kADPCMTinsel6: - return new Tinsel6_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); - case kADPCMTinsel8: - return new Tinsel8_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); - case kADPCMIma: - return new Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); + case kADPCMDVI: + return new DVI_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); case kADPCMApple: return new Apple_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); case kADPCMDK3: diff --git a/audio/decoders/adpcm.h b/audio/decoders/adpcm.h index 38ec870a27..10344101e2 100644 --- a/audio/decoders/adpcm.h +++ b/audio/decoders/adpcm.h @@ -38,12 +38,15 @@ #define SOUND_ADPCM_H #include "common/scummsys.h" -#include "common/stream.h" +#include "common/types.h" + +namespace Common { +class SeekableReadStream; +} namespace Audio { -class AudioStream; class RewindableAudioStream; // There are several types of ADPCM encoding, only some are supported here @@ -54,12 +57,8 @@ class RewindableAudioStream; enum typesADPCM { kADPCMOki, // Dialogic/Oki ADPCM (aka VOX) kADPCMMSIma, // Microsoft IMA ADPCM - kADPCMMSImaLastExpress, // Microsoft IMA ADPCM (with inverted samples) kADPCMMS, // Microsoft ADPCM - kADPCMTinsel4, // 4-bit ADPCM used by the Tinsel engine - kADPCMTinsel6, // 6-bit ADPCM used by the Tinsel engine - kADPCMTinsel8, // 8-bit ADPCM used by the Tinsel engine - kADPCMIma, // Standard IMA ADPCM + kADPCMDVI, // Intel DVI IMA ADPCM kADPCMApple, // Apple QuickTime IMA ADPCM kADPCMDK3 // Duck DK3 IMA ADPCM }; diff --git a/audio/decoders/adpcm_intern.h b/audio/decoders/adpcm_intern.h new file mode 100644 index 0000000000..f875bd88c7 --- /dev/null +++ b/audio/decoders/adpcm_intern.h @@ -0,0 +1,242 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +/** + * Internal interfaces to the ADPCM decoders. + * + * These can be used to make custom ADPCM decoder subclasses, + * or to at least share some common data tables between various + * ADPCM decoder implementations. + */ + +#ifndef SOUND_ADPCM_INTERN_H +#define SOUND_ADPCM_INTERN_H + +#include "audio/audiostream.h" +#include "common/endian.h" +#include "common/stream.h" +#include "common/textconsole.h" + + +namespace Audio { + +class ADPCMStream : public RewindableAudioStream { +protected: + Common::SeekableReadStream *_stream; + const DisposeAfterUse::Flag _disposeAfterUse; + const int32 _startpos; + const int32 _endpos; + const int _channels; + const uint32 _blockAlign; + uint32 _blockPos[2]; + const int _rate; + + struct ADPCMStatus { + // OKI/IMA + struct { + int32 last; + int32 stepIndex; + } ima_ch[2]; + } _status; + + virtual void reset(); + +public: + ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign); + ~ADPCMStream(); + + virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos); } + virtual bool isStereo() const { return _channels == 2; } + virtual int getRate() const { return _rate; } + + virtual bool rewind(); + + + /** + * This table is used by some ADPCM variants (IMA and OKI) to adjust the + * step for use on the next sample. + * The first 8 entries are identical to the second 8 entries. Hence, we + * could half the table in size. But since the lookup index is always a + * 4-bit nibble, it is more efficient to just keep it as it is. + */ + static const int16 _stepAdjustTable[16]; +}; + +class Oki_ADPCMStream : public ADPCMStream { +public: + Oki_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) + : ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} + + virtual int readBuffer(int16 *buffer, const int numSamples); + +protected: + int16 decodeOKI(byte); +}; + +class Ima_ADPCMStream : public ADPCMStream { +protected: + int16 decodeIMA(byte code, int channel = 0); // Default to using the left channel/using one channel + +public: + Ima_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) + : ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { + memset(&_status, 0, sizeof(_status)); + } + + /** + * This table is used by decodeIMA. + */ + static const int16 _imaTable[89]; +}; + +class DVI_ADPCMStream : public Ima_ADPCMStream { +public: + DVI_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) + : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} + + virtual int readBuffer(int16 *buffer, const int numSamples); +}; + +class Apple_ADPCMStream : public Ima_ADPCMStream { +protected: + // Apple QuickTime IMA ADPCM + int32 _streamPos[2]; + int16 _buffer[2][2]; + uint8 _chunkPos[2]; + + void reset() { + Ima_ADPCMStream::reset(); + _chunkPos[0] = 0; + _chunkPos[1] = 0; + _streamPos[0] = 0; + _streamPos[1] = _blockAlign; + } + +public: + Apple_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) + : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { + _chunkPos[0] = 0; + _chunkPos[1] = 0; + _streamPos[0] = 0; + _streamPos[1] = _blockAlign; + } + + virtual int readBuffer(int16 *buffer, const int numSamples); + +}; + +class MSIma_ADPCMStream : public Ima_ADPCMStream { +public: + MSIma_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) + : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { + + if (blockAlign == 0) + error("MSIma_ADPCMStream(): blockAlign isn't specified"); + + if (blockAlign % (_channels * 4)) + error("MSIma_ADPCMStream(): invalid blockAlign"); + + _samplesLeft[0] = 0; + _samplesLeft[1] = 0; + } + + virtual int readBuffer(int16 *buffer, const int numSamples); + + void reset() { + Ima_ADPCMStream::reset(); + _samplesLeft[0] = 0; + _samplesLeft[1] = 0; + } + +private: + int16 _buffer[2][8]; + int _samplesLeft[2]; +}; + +class MS_ADPCMStream : public ADPCMStream { +protected: + struct ADPCMChannelStatus { + byte predictor; + int16 delta; + int16 coeff1; + int16 coeff2; + int16 sample1; + int16 sample2; + }; + + struct { + // MS ADPCM + ADPCMChannelStatus ch[2]; + } _status; + + void reset() { + ADPCMStream::reset(); + memset(&_status, 0, sizeof(_status)); + } + +public: + MS_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) + : ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { + if (blockAlign == 0) + error("MS_ADPCMStream(): blockAlign isn't specified for MS ADPCM"); + memset(&_status, 0, sizeof(_status)); + } + + virtual int readBuffer(int16 *buffer, const int numSamples); + +protected: + int16 decodeMS(ADPCMChannelStatus *c, byte); +}; + +// Duck DK3 IMA ADPCM Decoder +// Based on FFmpeg's decoder and http://wiki.multimedia.cx/index.php?title=Duck_DK3_IMA_ADPCM + +class DK3_ADPCMStream : public Ima_ADPCMStream { +protected: + + void reset() { + Ima_ADPCMStream::reset(); + _topNibble = false; + } + +public: + DK3_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) + : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { + + // DK3 only works as a stereo stream + assert(channels == 2); + _topNibble = false; + } + + virtual int readBuffer(int16 *buffer, const int numSamples); + +private: + byte _nibble, _lastByte; + bool _topNibble; +}; + +} // End of namespace Audio + +#endif diff --git a/audio/decoders/aiff.cpp b/audio/decoders/aiff.cpp index 0f947752f6..957fb13638 100644 --- a/audio/decoders/aiff.cpp +++ b/audio/decoders/aiff.cpp @@ -32,12 +32,10 @@ */ #include "common/endian.h" -#include "common/util.h" #include "common/stream.h" +#include "common/textconsole.h" #include "audio/decoders/aiff.h" -#include "audio/audiostream.h" -#include "audio/mixer.h" #include "audio/decoders/raw.h" namespace Audio { diff --git a/audio/decoders/aiff.h b/audio/decoders/aiff.h index 06c56ecd38..dddbffb520 100644 --- a/audio/decoders/aiff.h +++ b/audio/decoders/aiff.h @@ -37,7 +37,9 @@ #include "common/scummsys.h" #include "common/types.h" -namespace Common { class SeekableReadStream; } +namespace Common { +class SeekableReadStream; +} namespace Audio { diff --git a/audio/decoders/flac.cpp b/audio/decoders/flac.cpp index 76b6d35419..fe15877ac6 100644 --- a/audio/decoders/flac.cpp +++ b/audio/decoders/flac.cpp @@ -32,6 +32,7 @@ #include "common/debug.h" #include "common/stream.h" +#include "common/textconsole.h" #include "common/util.h" #include "audio/audiostream.h" diff --git a/audio/decoders/flac.h b/audio/decoders/flac.h index 17f95ec1fb..69222f22a1 100644 --- a/audio/decoders/flac.h +++ b/audio/decoders/flac.h @@ -49,12 +49,11 @@ #ifdef USE_FLAC namespace Common { - class SeekableReadStream; +class SeekableReadStream; } namespace Audio { -class AudioStream; class SeekableAudioStream; /** diff --git a/audio/decoders/iff_sound.cpp b/audio/decoders/iff_sound.cpp index 2ec189c586..8efe017e75 100644 --- a/audio/decoders/iff_sound.cpp +++ b/audio/decoders/iff_sound.cpp @@ -25,7 +25,6 @@ #include "audio/decoders/iff_sound.h" #include "audio/audiostream.h" -#include "audio/mixer.h" #include "audio/decoders/raw.h" #include "common/iff_container.h" #include "common/func.h" diff --git a/audio/decoders/iff_sound.h b/audio/decoders/iff_sound.h index 4e53059380..4d26b32e71 100644 --- a/audio/decoders/iff_sound.h +++ b/audio/decoders/iff_sound.h @@ -33,7 +33,7 @@ #define SOUND_IFF_H namespace Common { - class ReadStream; +class ReadStream; } namespace Audio { diff --git a/audio/decoders/mac_snd.cpp b/audio/decoders/mac_snd.cpp index 7c1a2f75f0..fc69988860 100644 --- a/audio/decoders/mac_snd.cpp +++ b/audio/decoders/mac_snd.cpp @@ -30,11 +30,10 @@ * We implement both type 1 and type 2 snd resources, but only those that are sampled */ -#include "common/util.h" +#include "common/textconsole.h" #include "common/stream.h" #include "audio/decoders/mac_snd.h" -#include "audio/audiostream.h" #include "audio/decoders/raw.h" namespace Audio { diff --git a/audio/decoders/mac_snd.h b/audio/decoders/mac_snd.h index 198a61333e..bf6331a265 100644 --- a/audio/decoders/mac_snd.h +++ b/audio/decoders/mac_snd.h @@ -35,7 +35,9 @@ #include "common/scummsys.h" #include "common/types.h" -namespace Common { class SeekableReadStream; } +namespace Common { +class SeekableReadStream; +} namespace Audio { diff --git a/audio/decoders/mp3.cpp b/audio/decoders/mp3.cpp index 53d68fa9db..91bd49873a 100644 --- a/audio/decoders/mp3.cpp +++ b/audio/decoders/mp3.cpp @@ -29,6 +29,7 @@ #include "common/debug.h" #include "common/stream.h" +#include "common/textconsole.h" #include "common/util.h" #include "audio/audiostream.h" diff --git a/audio/decoders/mp3.h b/audio/decoders/mp3.h index 72bc6e1b3e..d3a5b70d45 100644 --- a/audio/decoders/mp3.h +++ b/audio/decoders/mp3.h @@ -50,12 +50,11 @@ #ifdef USE_MAD namespace Common { - class SeekableReadStream; +class SeekableReadStream; } namespace Audio { -class AudioStream; class SeekableAudioStream; /** diff --git a/audio/decoders/raw.cpp b/audio/decoders/raw.cpp index 8b833c7838..cf787f9b12 100644 --- a/audio/decoders/raw.cpp +++ b/audio/decoders/raw.cpp @@ -25,9 +25,10 @@ #include "common/endian.h" #include "common/memstream.h" +#include "common/textconsole.h" +#include "common/util.h" #include "audio/audiostream.h" -#include "audio/mixer.h" #include "audio/decoders/raw.h" namespace Audio { diff --git a/audio/decoders/raw.h b/audio/decoders/raw.h index 3e9426012c..23ed02182d 100644 --- a/audio/decoders/raw.h +++ b/audio/decoders/raw.h @@ -32,12 +32,12 @@ #include "common/list.h" -namespace Common { class SeekableReadStream; } - +namespace Common { +class SeekableReadStream; +} namespace Audio { -class AudioStream; class SeekableAudioStream; /** diff --git a/audio/decoders/vag.h b/audio/decoders/vag.h index cdf91a8ea1..4adc1d3dde 100644 --- a/audio/decoders/vag.h +++ b/audio/decoders/vag.h @@ -35,12 +35,11 @@ #define SOUND_VAG_H namespace Common { - class SeekableReadStream; +class SeekableReadStream; } namespace Audio { -class AudioStream; class RewindableAudioStream; /** diff --git a/audio/decoders/voc.cpp b/audio/decoders/voc.cpp index b811a640ec..9c2dc4f337 100644 --- a/audio/decoders/voc.cpp +++ b/audio/decoders/voc.cpp @@ -27,9 +27,9 @@ #include "common/endian.h" #include "common/util.h" #include "common/stream.h" +#include "common/textconsole.h" #include "audio/audiostream.h" -#include "audio/mixer.h" #include "audio/decoders/raw.h" #include "audio/decoders/voc.h" diff --git a/audio/decoders/voc.h b/audio/decoders/voc.h index 82cc261f2c..38250dcf7a 100644 --- a/audio/decoders/voc.h +++ b/audio/decoders/voc.h @@ -41,8 +41,10 @@ #include "common/scummsys.h" #include "common/types.h" -namespace Common { class ReadStream; } -namespace Common { class SeekableReadStream; } +namespace Common { +class ReadStream; +class SeekableReadStream; +} namespace Audio { diff --git a/audio/decoders/vorbis.cpp b/audio/decoders/vorbis.cpp index dc37e852d3..63f7ba8207 100644 --- a/audio/decoders/vorbis.cpp +++ b/audio/decoders/vorbis.cpp @@ -32,15 +32,15 @@ #ifdef USE_VORBIS -#include "common/debug.h" #include "common/stream.h" +#include "common/textconsole.h" #include "common/util.h" #include "audio/audiostream.h" #ifdef USE_TREMOR -#if defined(__GP32__) // custom libtremor locations -#include <ivorbisfile.h> +#ifdef USE_TREMOLO +#include <tremolo/ivorbisfile.h> #else #include <tremor/ivorbisfile.h> #endif diff --git a/audio/decoders/vorbis.h b/audio/decoders/vorbis.h index 7cc395cccb..51d0b82d5f 100644 --- a/audio/decoders/vorbis.h +++ b/audio/decoders/vorbis.h @@ -49,12 +49,11 @@ #ifdef USE_VORBIS namespace Common { - class SeekableReadStream; +class SeekableReadStream; } namespace Audio { -class AudioStream; class SeekableAudioStream; /** diff --git a/audio/decoders/wave.cpp b/audio/decoders/wave.cpp index 1f0ddd8ceb..a64874887a 100644 --- a/audio/decoders/wave.cpp +++ b/audio/decoders/wave.cpp @@ -24,11 +24,10 @@ */ #include "common/debug.h" -#include "common/util.h" +#include "common/textconsole.h" #include "common/stream.h" #include "audio/audiostream.h" -#include "audio/mixer.h" #include "audio/decoders/wave.h" #include "audio/decoders/adpcm.h" #include "audio/decoders/raw.h" diff --git a/audio/decoders/wave.h b/audio/decoders/wave.h index 2bdbe8f0b6..33c3e798a0 100644 --- a/audio/decoders/wave.h +++ b/audio/decoders/wave.h @@ -43,7 +43,9 @@ #include "common/scummsys.h" #include "common/types.h" -namespace Common { class SeekableReadStream; } +namespace Common { +class SeekableReadStream; +} namespace Audio { diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp index 1f61e16101..5952a987a7 100644 --- a/audio/fmopl.cpp +++ b/audio/fmopl.cpp @@ -28,6 +28,7 @@ #include "audio/softsynth/opl/mame.h" #include "common/config-manager.h" +#include "common/textconsole.h" #include "common/translation.h" namespace OPL { diff --git a/audio/mididrv.cpp b/audio/mididrv.cpp index a1487ff69d..6cc3366847 100644 --- a/audio/mididrv.cpp +++ b/audio/mididrv.cpp @@ -23,14 +23,14 @@ * */ -#include "engines/engine.h" #include "common/config-manager.h" +#include "common/error.h" #include "common/str.h" #include "common/system.h" +#include "common/textconsole.h" #include "common/util.h" #include "audio/mididrv.h" #include "audio/musicplugin.h" -#include "common/translation.h" const byte MidiDriver::_mt32ToGm[128] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F diff --git a/audio/mididrv.h b/audio/mididrv.h index 9e649cba3d..c6c5179051 100644 --- a/audio/mididrv.h +++ b/audio/mididrv.h @@ -27,24 +27,10 @@ #define SOUND_MIDIDRV_H #include "common/scummsys.h" +#include "common/str.h" #include "common/timer.h" class MidiChannel; -class MusicDevice; - -namespace Audio { - class Mixer; -} -namespace Common { class String; } - -/** - * Music Driver Types, used to uniquely identify each music driver. - * - * The pseudo drivers are listed first, then all native drivers, - * then all other MIDI drivers, and finally the non-MIDI drivers. - * - * @todo Rename MidiDriverType to MusicDriverType - */ /** * Music types that music drivers can implement and engines can rely on. @@ -71,16 +57,21 @@ enum MusicType { * A set of flags to be passed to detectDevice() which can be used to * specify what kind of music driver is preferred / accepted. * - * The flags (except for MDT_PREFER_MT32 and MDT_PREFER_GM) indicate whether a given driver - * type is acceptable. E.g. the TOWNS music driver could be returned by - * detectDevice if and only if MDT_TOWNS is specified. + * The flags (except for MDT_PREFER_MT32 and MDT_PREFER_GM) indicate whether a + * given driver type is acceptable. E.g. the TOWNS music driver could be + * returned by detectDevice if and only if MDT_TOWNS is specified. + * + * MDT_PREFER_MT32 and MDT_PREFER_GM indicate the MIDI device type to use when + * no device is selected in the music options, or when the MIDI device selected + * does not match the requirements of a game engine. With these flags, more + * priority is given to an MT-32 device, or a GM device respectively. * * @todo Rename MidiDriverFlags to MusicDriverFlags */ enum MidiDriverFlags { MDT_NONE = 0, - MDT_PCSPK = 1 << 0, // PC Speaker: Maps to MD_PCSPK and MD_PCJR - MDT_CMS = 1 << 1, // Creative Music System / Gameblaster: Maps to MD_CMS + MDT_PCSPK = 1 << 0, // PC Speaker: Maps to MT_PCSPK and MT_PCJR + MDT_CMS = 1 << 1, // Creative Music System / Gameblaster: Maps to MT_CMS MDT_PCJR = 1 << 2, // Tandy/PC Junior driver MDT_ADLIB = 1 << 3, // AdLib: Maps to MT_ADLIB MDT_C64 = 1 << 4, @@ -94,19 +85,53 @@ enum MidiDriverFlags { }; /** - * Abstract description of a MIDI driver. Used by the config file and command - * line parsing code, and also to be able to give the user a list of available - * drivers. - * - * @todo Rename MidiDriverType to MusicDriverType + * TODO: Document this, give it a better name. */ +class MidiDriver_BASE { +public: + virtual ~MidiDriver_BASE() { } + + /** + * Output a packed midi command to the midi stream. + * The 'lowest' byte (i.e. b & 0xFF) is the status + * code, then come (if used) the first and second + * opcode. + */ + virtual void send(uint32 b) = 0; + + /** + * Output a midi command to the midi stream. Convenience wrapper + * around the usual 'packed' send method. + * + * Do NOT use this for sysEx transmission; instead, use the sysEx() + * method below. + */ + void send(byte status, byte firstOp, byte secondOp) { + send(status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16)); + } + + /** + * Transmit a sysEx to the midi device. + * + * The given msg MUST NOT contain the usual SysEx frame, i.e. + * do NOT include the leading 0xF0 and the trailing 0xF7. + * + * Furthermore, the maximal supported length of a SysEx + * is 264 bytes. Passing longer buffers can lead to + * undefined behavior (most likely, a crash). + */ + virtual void sysEx(const byte *msg, uint16 length) { } + + // TODO: Document this. + virtual void metaEvent(byte type, byte *data, uint16 length) { } +}; /** * Abstract MIDI Driver Class * * @todo Rename MidiDriver to MusicDriver */ -class MidiDriver { +class MidiDriver : public MidiDriver_BASE { public: /** * The device handle. @@ -177,27 +202,13 @@ public: */ virtual int open() = 0; - /** Close the midi driver. */ - virtual void close() = 0; - /** - * Output a packed midi command to the midi stream. - * The 'lowest' byte (i.e. b & 0xFF) is the status - * code, then come (if used) the first and second - * opcode. + * Check whether the midi driver has already been opened. */ - virtual void send(uint32 b) = 0; + virtual bool isOpen() const = 0; - /** - * Output a midi command to the midi stream. Convenience wrapper - * around the usual 'packed' send method. - * - * Do NOT use this for sysEx transmission; instead, use the sysEx() - * method below. - */ - void send(byte status, byte firstOp, byte secondOp) { - send(status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16)); - } + /** Close the midi driver. */ + virtual void close() = 0; /** Get or set a property. */ virtual uint32 property(int prop, uint32 param) { return 0; } @@ -225,22 +236,8 @@ public: */ void sendGMReset(); - /** - * Transmit a sysEx to the midi device. - * - * The given msg MUST NOT contain the usual SysEx frame, i.e. - * do NOT include the leading 0xF0 and the trailing 0xF7. - * - * Furthermore, the maximal supported length of a SysEx - * is 264 bytes. Passing longer buffers can lead to - * undefined behavior (most likely, a crash). - */ - virtual void sysEx(const byte *msg, uint16 length) { } - virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) { } - virtual void metaEvent(byte type, byte *data, uint16 length) { } - // Timing functions - MidiDriver now operates timers virtual void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) = 0; diff --git a/audio/midiparser.cpp b/audio/midiparser.cpp index e01b8a7fc6..a1399d0c30 100644 --- a/audio/midiparser.cpp +++ b/audio/midiparser.cpp @@ -25,6 +25,7 @@ #include "audio/midiparser.h" #include "audio/mididrv.h" +#include "common/textconsole.h" #include "common/util.h" ////////////////////////////////////////////////// @@ -350,7 +351,7 @@ void MidiParser::hangAllActiveNotes() { if (_next_event.command() == 0x8) { if (temp_active[_next_event.basic.param1] & (1 << _next_event.channel())) { hangingNote(_next_event.channel(), _next_event.basic.param1, (advance_tick - _position._last_event_tick) * _psec_per_tick, false); - temp_active[_next_event.basic.param1] &= ~ (1 << _next_event.channel()); + temp_active[_next_event.basic.param1] &= ~(1 << _next_event.channel()); } } else if (_next_event.event == 0xFF && _next_event.ext.type == 0x2F) { // warning("MidiParser::hangAllActiveNotes(): Hit End of Track with active notes left"); diff --git a/audio/midiparser.h b/audio/midiparser.h index 0b18a19a5b..24f2ba7963 100644 --- a/audio/midiparser.h +++ b/audio/midiparser.h @@ -31,8 +31,7 @@ #include "common/scummsys.h" #include "common/endian.h" -class MidiParser; -class MidiDriver; +class MidiDriver_BASE; @@ -273,7 +272,7 @@ protected: ///< Used for "Smart Jump" and MIDI formats that do not include explicit Note Off events. byte _hanging_notes_count; ///< Count of hanging notes, used to optimize expiration. - MidiDriver *_driver; ///< The device to which all events will be transmitted. + MidiDriver_BASE *_driver; ///< The device to which all events will be transmitted. uint32 _timer_rate; ///< The time in microseconds between onTimer() calls. Obtained from the MidiDriver. uint32 _ppqn; ///< Pulses Per Quarter Note. (We refer to "pulses" as "ticks".) uint32 _tempo; ///< Microseconds per quarter note. @@ -380,7 +379,7 @@ public: virtual void unloadMusic(); virtual void property(int prop, int value); - void setMidiDriver(MidiDriver *driver) { _driver = driver; } + void setMidiDriver(MidiDriver_BASE *driver) { _driver = driver; } void setTimerRate(uint32 rate) { _timer_rate = rate; } void setTempo(uint32 tempo); void onTimer(); diff --git a/audio/midiparser_smf.cpp b/audio/midiparser_smf.cpp index 9e4e8ed293..8ead95de4c 100644 --- a/audio/midiparser_smf.cpp +++ b/audio/midiparser_smf.cpp @@ -24,7 +24,7 @@ */ #include "audio/midiparser.h" -#include "audio/mididrv.h" +#include "common/textconsole.h" #include "common/util.h" /** diff --git a/audio/midiparser_xmidi.cpp b/audio/midiparser_xmidi.cpp index edc7c7a943..1146084cde 100644 --- a/audio/midiparser_xmidi.cpp +++ b/audio/midiparser_xmidi.cpp @@ -24,7 +24,7 @@ */ #include "audio/midiparser.h" -#include "audio/mididrv.h" +#include "common/textconsole.h" #include "common/util.h" /** diff --git a/audio/midiplayer.cpp b/audio/midiplayer.cpp new file mode 100644 index 0000000000..f4a13a0438 --- /dev/null +++ b/audio/midiplayer.cpp @@ -0,0 +1,198 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "audio/midiplayer.h" +#include "audio/midiparser.h" + +#include "common/config-manager.h" + +namespace Audio { + +MidiPlayer::MidiPlayer() : + _driver(0), + _parser(0), + _midiData(0), + _isLooping(false), + _isPlaying(false), + _masterVolume(0), + _nativeMT32(false) { + + memset(_channelsTable, 0, sizeof(_channelsTable)); + memset(_channelsVolume, 127, sizeof(_channelsVolume)); + +// TODO +} + +MidiPlayer::~MidiPlayer() { + // FIXME/TODO: In some engines, stop() was called first; + // in others, _driver->setTimerCallback(NULL, NULL) came first. + // Hopefully, this make no real difference, but we should + // watch out for regressions. + stop(); + + // Unhook & unload the driver + if (_driver) { + _driver->setTimerCallback(0, 0); + _driver->close(); + delete _driver; + _driver = 0; + } +} + +void MidiPlayer::createDriver(int flags) { + MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(flags); + _nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32")); + + _driver = MidiDriver::createMidi(dev); + assert(_driver); + if (_nativeMT32) + _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); +} + + +void MidiPlayer::setVolume(int volume) { + volume = CLIP(volume, 0, 255); + if (_masterVolume == volume) + return; + + Common::StackLock lock(_mutex); + + _masterVolume = volume; + for (int i = 0; i < kNumChannels; ++i) { + if (_channelsTable[i]) { + _channelsTable[i]->volume(_channelsVolume[i] * _masterVolume / 255); + } + } +} + +void MidiPlayer::syncVolume() { + int volume = ConfMan.getInt("music_volume"); + if (ConfMan.getBool("mute")) { + volume = -1; + } + setVolume(volume); +} + + +void MidiPlayer::send(uint32 b) { + byte ch = (byte)(b & 0x0F); + if ((b & 0xFFF0) == 0x07B0) { + // Adjust volume changes by master volume + byte volume = (byte)((b >> 16) & 0x7F); + _channelsVolume[ch] = volume; + volume = volume * _masterVolume / 255; + b = (b & 0xFF00FFFF) | (volume << 16); + } else if ((b & 0xFFF0) == 0x007BB0) { + // Only respond to All Notes Off if this channel + // has currently been allocated + if (!_channelsTable[ch]) + return; + } + + sendToChannel(ch, b); +} + +void MidiPlayer::sendToChannel(byte ch, uint32 b) { + if (!_channelsTable[ch]) { + _channelsTable[ch] = (ch == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + // TODO: Some engines overload this method to insert code at this + // point which calls the channel's volume() method. + // Does this make sense, and should we maybe do it in general? + } + if (_channelsTable[ch]) { + _channelsTable[ch]->send(b); + } +} + +void MidiPlayer::metaEvent(byte type, byte *data, uint16 length) { + switch (type) { + case 0x2F: // End of Track + endOfTrack(); + break; + default: + //warning("Unhandled meta event: %02x", type); + break; + } +} + +void MidiPlayer::endOfTrack() { + if (_isLooping) { + assert(_parser); + _parser->jumpToTick(0); + } else + stop(); +} + +void MidiPlayer::timerCallback(void *data) { + assert(data); + ((MidiPlayer *)data)->onTimer(); +} + +void MidiPlayer::onTimer() { + Common::StackLock lock(_mutex); + + // TODO: Maybe we can replace _isPlaying + // by a simple check for "_parser != 0" ? + + if (_isPlaying && _parser) { + _parser->onTimer(); + } +} + + +void MidiPlayer::stop() { + Common::StackLock lock(_mutex); + + _isPlaying = false; + if (_parser) { + _parser->unloadMusic(); + + // FIXME/TODO: The MidiParser destructor calls allNotesOff() + // but unloadMusic also does. To suppress double notes-off, + // we reset the midi driver of _parser before deleting it. + // This smells very fishy, in any case. + _parser->setMidiDriver(0); + + delete _parser; + _parser = NULL; + } + + free(_midiData); + _midiData = 0; +} + +void MidiPlayer::pause() { +// debugC(2, kDraciSoundDebugLevel, "Pausing track %d", _track); + _isPlaying = false; + setVolume(-1); // FIXME: This should be 0, shouldn't it? +} + +void MidiPlayer::resume() { +// debugC(2, kDraciSoundDebugLevel, "Resuming track %d", _track); + syncVolume(); + _isPlaying = true; +} + +} // End of namespace Audio diff --git a/audio/midiplayer.h b/audio/midiplayer.h new file mode 100644 index 0000000000..0cf373d646 --- /dev/null +++ b/audio/midiplayer.h @@ -0,0 +1,191 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef SOUND_MIDIPLAYER_H +#define SOUND_MIDIPLAYER_H + +#include "common/scummsys.h" +#include "common/mutex.h" +#include "audio/mididrv.h" + +class MidiParser; + +namespace Audio { + +/** + * Simple MIDI playback class. + * + * @note Currently incomplete, as it lacks play() methods. This is just a + * start of the real thing, which tries to include code replicated between + * several of our engines. + * + * Eventually, this should offer ways to start playback of SMF and XMIDI + * data (and possibly make it easy to add further playback methods), + * should be able to automatically instantiate _driver as needed, + * should perform memory management on the MidiParser object(s) being + * used, and possibly more. + * + * Also, pause/resume handling should be unified (we provide an implementation, + * but several subclasses use different ones). + * + * Also, care should be taken to ensure that mutex locking is done right. + * + * @todo Document origin of this class. It is based on code shared by + * several engines (e.g. DRACI says it copied it from MADE, which took + * it from SAGE). + */ +class MidiPlayer : public MidiDriver_BASE { +public: + MidiPlayer(); + ~MidiPlayer(); + + // TODO: Implement ways to actually play stuff + //virtual void play(TODO); + + // TODO: Document these + virtual void stop(); + virtual void pause(); + virtual void resume(); + + /** + * Return whether there is currently any MIDI music playing. + * + * @todo There is a subtle difference between the semantics of this in + * various subclasses, related to paused music: Namely, should this + * function return true or false if a MIDI song is currently loaded, + * but paused? In the base implementation of pause/resume, "false" + * will be returned (that is, it is not possible to distinguish between + * nothing being played, and an active but paused MIDI tune). + * But in several subclasses (e.g. in HUGO), there is a separate _paused + * variable, which is used to pause playback, and for these, "true" + * will be returned. + * And in SAGA, isPlaying is overwritten to return the value + * of _parser->isPlaying() (which should amount to "true" in the + * described situation). + * We really should unify this and clearly define the desired + * semantics of this method. + */ + bool isPlaying() const { return _isPlaying; } + + /** + * Return the currently active master volume, in the range 0-255. + */ + int getVolume() const { return _masterVolume; } + + /** + * Set the master volume to the specified value in the range 0-255. + * (values outside this range are automatically clipped). + * This may cause suitable MIDI events to be sent to active channels. + * + * @todo This method currently does not do anything if the new volume + * matches the old volume. But some engines always update the + * volume (Parallaction, Tinsel, Touche, ...). This is why + * this method is currently virtual. We really should figure + * which way to do it, and then make this the default, and make + * this method non-virtual again. + */ + virtual void setVolume(int volume); + + /** + * Update the volume according to the ConfMan's 'music_volume' + * setting. also respects the 'mute' setting. + */ + void syncVolume(); + + // TODO: Document this + bool hasNativeMT32() const { return _nativeMT32; } + + // MidiDriver_BASE implementation + virtual void send(uint32 b); + virtual void metaEvent(byte type, byte *data, uint16 length); + +protected: + /** + * This method is invoked by the default send() implementation, + * after suitably filtering the message b. + */ + virtual void sendToChannel(byte ch, uint32 b); + + /** + * This method is invoked by metaEvent when an end-of-track + * event arrives. By default, this tells the parser + * to jump to the start (if looping is enabled) resp. + * invokes stope(): + * Overload this to customize behavior. + */ + virtual void endOfTrack(); + + // TODO: Document this + virtual void onTimer(); + + static void timerCallback(void *data); + + void createDriver(int flags = MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM); + +protected: + enum { + /** + * The number of MIDI channels supported. + */ + kNumChannels = 16 + }; + + Common::Mutex _mutex; + MidiDriver *_driver; + + /** + * A MidiParser instances, to be created by methods of a MidiPlayer + * subclass. + * @note The stop() method (and hence the destructor) invoke the + * unloadMusic() method of _parser and then delete it. + */ + MidiParser *_parser; + + /** + * This is an (optional) pointer to a malloc'ed buffer containing + * MIDI data used by _parser. The stop() method (and hence the + * destructor) will free this if set. + * Subclasses of this class may use _midiData, but don't have to + */ + byte *_midiData; + + MidiChannel *_channelsTable[kNumChannels]; + uint8 _channelsVolume[kNumChannels]; + + bool _isLooping; + bool _isPlaying; + + /** + * The master volume, in the range 0-255. + */ + int _masterVolume; // FIXME: byte or int ? + + bool _nativeMT32; +}; + + +} // End of namespace Audio + +#endif diff --git a/audio/mixer.cpp b/audio/mixer.cpp index c2271b1059..4d23487e71 100644 --- a/audio/mixer.cpp +++ b/audio/mixer.cpp @@ -25,6 +25,7 @@ #include "common/util.h" #include "common/system.h" +#include "common/textconsole.h" #include "audio/mixer_intern.h" #include "audio/rate.h" @@ -54,8 +55,9 @@ public: * @param len 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. + * @return number of sample pairs processed (which can still be silence!) */ - void mix(int16 *data, uint len); + int mix(int16 *data, uint len); /** * Queries whether the channel is still playing or not. @@ -161,16 +163,11 @@ private: MixerImpl::MixerImpl(OSystem *system, uint sampleRate) - : _syst(system), _sampleRate(sampleRate), _mixerReady(false), _handleSeed(0) { + : _syst(system), _mutex(), _sampleRate(sampleRate), _mixerReady(false), _handleSeed(0), _soundTypeSettings() { assert(sampleRate > 0); - int i; - - for (i = 0; i < ARRAYSIZE(_volumeForSoundType); i++) - _volumeForSoundType[i] = kMaxMixerVolume; - - for (i = 0; i != NUM_CHANNELS; i++) + for (int i = 0; i != NUM_CHANNELS; i++) _channels[i] = 0; } @@ -257,7 +254,7 @@ void MixerImpl::playStream( insertChannel(handle, chan); } -void MixerImpl::mixCallback(byte *samples, uint len) { +int MixerImpl::mixCallback(byte *samples, uint len) { assert(samples); Common::StackLock lock(_mutex); @@ -272,14 +269,21 @@ void MixerImpl::mixCallback(byte *samples, uint len) { memset(buf, 0, 2 * len * sizeof(int16)); // mix all channels + int res = 0, tmp; for (int i = 0; i != NUM_CHANNELS; i++) if (_channels[i]) { if (_channels[i]->isFinished()) { delete _channels[i]; _channels[i] = 0; - } else if (!_channels[i]->isPaused()) - _channels[i]->mix(buf, len); + } else if (!_channels[i]->isPaused()) { + tmp = _channels[i]->mix(buf, len); + + if (tmp > res) + res = tmp; + } } + + return res; } void MixerImpl::stopAll() { @@ -314,6 +318,21 @@ void MixerImpl::stopHandle(SoundHandle handle) { _channels[index] = 0; } +void MixerImpl::muteSoundType(SoundType type, bool mute) { + assert(0 <= type && type < ARRAYSIZE(_soundTypeSettings)); + _soundTypeSettings[type].mute = mute; + + for (int i = 0; i != NUM_CHANNELS; ++i) { + if (_channels[i] && _channels[i]->getType() == type) + _channels[i]->notifyGlobalVolChange(); + } +} + +bool MixerImpl::isSoundTypeMuted(SoundType type) const { + assert(0 <= type && type < ARRAYSIZE(_soundTypeSettings)); + return _soundTypeSettings[type].mute; +} + void MixerImpl::setChannelVolume(SoundHandle handle, byte volume) { Common::StackLock lock(_mutex); @@ -409,7 +428,7 @@ bool MixerImpl::hasActiveChannelOfType(SoundType type) { } void MixerImpl::setVolumeForSoundType(SoundType type, int volume) { - assert(0 <= type && type < ARRAYSIZE(_volumeForSoundType)); + assert(0 <= type && type < ARRAYSIZE(_soundTypeSettings)); // Check range if (volume > kMaxMixerVolume) @@ -421,7 +440,7 @@ void MixerImpl::setVolumeForSoundType(SoundType type, int volume) { // scaling? See also Player_V2::setMasterVolume Common::StackLock lock(_mutex); - _volumeForSoundType[type] = volume; + _soundTypeSettings[type].volume = volume; for (int i = 0; i != NUM_CHANNELS; ++i) { if (_channels[i] && _channels[i]->getType() == type) @@ -430,9 +449,9 @@ void MixerImpl::setVolumeForSoundType(SoundType type, int volume) { } int MixerImpl::getVolumeForSoundType(SoundType type) const { - assert(0 <= type && type < ARRAYSIZE(_volumeForSoundType)); + assert(0 <= type && type < ARRAYSIZE(_soundTypeSettings)); - return _volumeForSoundType[type]; + return _soundTypeSettings[type].volume; } @@ -478,17 +497,21 @@ void Channel::updateChannelVolumes() { // volume is in the range 0 - kMaxMixerVolume. // Hence, the vol_l/vol_r values will be in that range, too - int vol = _mixer->getVolumeForSoundType(_type) * _volume; - - if (_balance == 0) { - _volL = vol / Mixer::kMaxChannelVolume; - _volR = vol / Mixer::kMaxChannelVolume; - } else if (_balance < 0) { - _volL = vol / Mixer::kMaxChannelVolume; - _volR = ((127 + _balance) * vol) / (Mixer::kMaxChannelVolume * 127); + if (!_mixer->isSoundTypeMuted(_type)) { + int vol = _mixer->getVolumeForSoundType(_type) * _volume; + + if (_balance == 0) { + _volL = vol / Mixer::kMaxChannelVolume; + _volR = vol / Mixer::kMaxChannelVolume; + } else if (_balance < 0) { + _volL = vol / Mixer::kMaxChannelVolume; + _volR = ((127 + _balance) * vol) / (Mixer::kMaxChannelVolume * 127); + } else { + _volL = ((127 - _balance) * vol) / (Mixer::kMaxChannelVolume * 127); + _volR = vol / Mixer::kMaxChannelVolume; + } } else { - _volL = ((127 - _balance) * vol) / (Mixer::kMaxChannelVolume * 127); - _volR = vol / Mixer::kMaxChannelVolume; + _volL = _volR = 0; } } @@ -538,19 +561,23 @@ Timestamp Channel::getElapsedTime() { return ts; } -void Channel::mix(int16 *data, uint len) { +int Channel::mix(int16 *data, uint len) { assert(_stream); + int res = 0; + if (_stream->endOfData()) { // TODO: call drain method } else { assert(_converter); - _samplesConsumed = _samplesDecoded; _mixerTimeStamp = g_system->getMillis(); _pauseTime = 0; - _samplesDecoded += _converter->flow(*_stream, data, len, _volL, _volR); + res = _converter->flow(*_stream, data, len, _volL, _volR); + _samplesDecoded += res; } + + return res; } } // End of namespace Audio diff --git a/audio/mixer.h b/audio/mixer.h index a048124ca3..5802abdd6b 100644 --- a/audio/mixer.h +++ b/audio/mixer.h @@ -27,20 +27,13 @@ #define SOUND_MIXER_H #include "common/types.h" -#include "common/mutex.h" #include "common/noncopyable.h" -#include "audio/timestamp.h" - -class OSystem; - - namespace Audio { class AudioStream; class Channel; -class Mixer; -class MixerImpl; +class Timestamp; /** * A SoundHandle instances corresponds to a specific sound @@ -197,6 +190,20 @@ public: virtual bool isSoundHandleActive(SoundHandle handle) = 0; + /** + * Set the mute state for a given sound type. + * + * @param type the sound type + * @param mute Whether to mute (= true) or not (= false). + */ + virtual void muteSoundType(SoundType type, bool mute) = 0; + + /** + * Query the mute state for a given sound type. + * + * @param type the sound type + */ + virtual bool isSoundTypeMuted(SoundType type) const = 0; /** * Set the channel volume for the given handle. diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h index c8df9a594d..d7764e50d9 100644 --- a/audio/mixer_intern.h +++ b/audio/mixer_intern.h @@ -64,7 +64,14 @@ private: bool _mixerReady; uint32 _handleSeed; - int _volumeForSoundType[4]; + struct SoundTypeSettings { + SoundTypeSettings() : mute(false), volume(kMaxMixerVolume) {} + + bool mute; + int volume; + }; + + SoundTypeSettings _soundTypeSettings[4]; Channel *_channels[NUM_CHANNELS]; @@ -97,6 +104,9 @@ public: virtual bool isSoundHandleActive(SoundHandle handle); + virtual void muteSoundType(SoundType type, bool mute); + virtual bool isSoundTypeMuted(SoundType type) const; + virtual void setChannelVolume(SoundHandle handle, byte volume); virtual void setChannelBalance(SoundHandle handle, int8 balance); @@ -118,8 +128,10 @@ public: * The mixer callback function, to be called at regular intervals by * the backend (e.g. from an audio mixing thread). All the actual mixing * work is done from here. + * + * @return number of sample pairs processed (which can still be silence!) */ - void mixCallback(byte *samples, uint len); + int mixCallback(byte *samples, uint len); /** * Set the internal 'is ready' flag of the mixer. diff --git a/audio/mods/infogrames.cpp b/audio/mods/infogrames.cpp index 27e42c637b..8bfffeacb6 100644 --- a/audio/mods/infogrames.cpp +++ b/audio/mods/infogrames.cpp @@ -27,6 +27,7 @@ #include "common/endian.h" #include "common/file.h" #include "common/memstream.h" +#include "common/textconsole.h" namespace Audio { diff --git a/audio/mods/infogrames.h b/audio/mods/infogrames.h index c7abebf24e..9787210e82 100644 --- a/audio/mods/infogrames.h +++ b/audio/mods/infogrames.h @@ -33,7 +33,10 @@ #define SOUND_MODS_INFOGRAMES_H #include "audio/mods/paula.h" -#include "common/stream.h" + +namespace Common { +class SeekableReadStream; +} namespace Audio { diff --git a/audio/mods/maxtrax.cpp b/audio/mods/maxtrax.cpp index a577c72eed..0738966dab 100644 --- a/audio/mods/maxtrax.cpp +++ b/audio/mods/maxtrax.cpp @@ -24,10 +24,10 @@ */ #include "common/scummsys.h" -#include "common/endian.h" #include "common/stream.h" #include "common/util.h" #include "common/debug.h" +#include "common/textconsole.h" #include "audio/mods/maxtrax.h" diff --git a/audio/mods/module.cpp b/audio/mods/module.cpp index 0da6923b5d..987b17bc8d 100644 --- a/audio/mods/module.cpp +++ b/audio/mods/module.cpp @@ -27,6 +27,8 @@ #include "common/util.h" #include "common/endian.h" +#include "common/stream.h" +#include "common/textconsole.h" namespace Modules { @@ -113,7 +115,7 @@ const int16 Module::periods[16][60] = { 108 , 101 , 96 , 90 , 85 , 80 , 76 , 72 , 68 , 64 , 60 , 57 }}; const uint32 Module::signatures[] = { - MKID_BE('M.K.'), MKID_BE('M!K!'), MKID_BE('FLT4') + MKTAG('M','.','K','.'), MKTAG('M','!','K','!'), MKTAG('F','L','T','4') }; bool Module::load(Common::SeekableReadStream &st, int offs) { diff --git a/audio/mods/module.h b/audio/mods/module.h index 550b63617e..c9b72bd2d6 100644 --- a/audio/mods/module.h +++ b/audio/mods/module.h @@ -26,7 +26,11 @@ #ifndef SOUND_MODS_MODULE_H #define SOUND_MODS_MODULE_H -#include "common/stream.h" +#include "common/scummsys.h" + +namespace Common { +class SeekableReadStream; +} namespace Modules { diff --git a/audio/mods/protracker.cpp b/audio/mods/protracker.cpp index 6051338900..f781920505 100644 --- a/audio/mods/protracker.cpp +++ b/audio/mods/protracker.cpp @@ -29,6 +29,8 @@ #include "audio/audiostream.h" +#include "common/textconsole.h" + namespace Modules { class ProtrackerStream : public ::Audio::Paula { diff --git a/audio/mods/protracker.h b/audio/mods/protracker.h index af722637c7..fa9895b81f 100644 --- a/audio/mods/protracker.h +++ b/audio/mods/protracker.h @@ -33,7 +33,9 @@ #ifndef SOUND_MODS_PROTRACKER_H #define SOUND_MODS_PROTRACKER_H -#include "common/stream.h" +namespace Common { +class SeekableReadStream; +} namespace Audio { diff --git a/audio/mods/rjp1.cpp b/audio/mods/rjp1.cpp index 7423abb668..eaa99e6928 100644 --- a/audio/mods/rjp1.cpp +++ b/audio/mods/rjp1.cpp @@ -25,10 +25,12 @@ #include "common/debug.h" #include "common/endian.h" +#include "common/stream.h" +#include "common/textconsole.h" +#include "common/util.h" #include "audio/mods/paula.h" #include "audio/mods/rjp1.h" -#include "audio/audiostream.h" namespace Audio { @@ -138,7 +140,7 @@ Rjp1::~Rjp1() { } bool Rjp1::load(Common::SeekableReadStream *songData, Common::SeekableReadStream *instrumentsData) { - if (songData->readUint32BE() == MKID_BE('RJP1') && songData->readUint32BE() == MKID_BE('SMOD')) { + if (songData->readUint32BE() == MKTAG('R','J','P','1') && songData->readUint32BE() == MKTAG('S','M','O','D')) { for (int i = 0; i < 7; ++i) { uint32 size = songData->readUint32BE(); _vars.songData[i] = (uint8 *)malloc(size); @@ -167,7 +169,7 @@ bool Rjp1::load(Common::SeekableReadStream *songData, Common::SeekableReadStream } } - if (instrumentsData->readUint32BE() == MKID_BE('RJP1')) { + if (instrumentsData->readUint32BE() == MKTAG('R','J','P','1')) { uint32 size = instrumentsData->size() - 4; _vars.instData = (int8 *)malloc(size); if (!_vars.instData) diff --git a/audio/mods/rjp1.h b/audio/mods/rjp1.h index e1960921b2..e3cb0b853d 100644 --- a/audio/mods/rjp1.h +++ b/audio/mods/rjp1.h @@ -32,7 +32,9 @@ #ifndef SOUND_MODS_RJP1_H #define SOUND_MODS_RJP1_H -#include "common/stream.h" +namespace Common { +class SeekableReadStream; +} namespace Audio { diff --git a/audio/mods/soundfx.cpp b/audio/mods/soundfx.cpp index 06a1e29514..44fdd65986 100644 --- a/audio/mods/soundfx.cpp +++ b/audio/mods/soundfx.cpp @@ -24,10 +24,11 @@ */ #include "common/endian.h" +#include "common/stream.h" +#include "common/textconsole.h" #include "audio/mods/paula.h" #include "audio/mods/soundfx.h" -#include "audio/audiostream.h" namespace Audio { diff --git a/audio/mods/soundfx.h b/audio/mods/soundfx.h index 089c19d292..9dd5240160 100644 --- a/audio/mods/soundfx.h +++ b/audio/mods/soundfx.h @@ -32,7 +32,9 @@ #ifndef SOUND_MODS_SOUNDFX_H #define SOUND_MODS_SOUNDFX_H -#include "common/stream.h" +namespace Common { +class SeekableReadStream; +} namespace Audio { diff --git a/audio/mods/tfmx.cpp b/audio/mods/tfmx.cpp index 8c69a75ebd..f7cfc50c10 100644 --- a/audio/mods/tfmx.cpp +++ b/audio/mods/tfmx.cpp @@ -26,8 +26,8 @@ #include "common/scummsys.h" #include "common/endian.h" #include "common/stream.h" -#include "common/util.h" #include "common/debug.h" +#include "common/textconsole.h" #include "audio/mods/tfmx.h" diff --git a/audio/module.mk b/audio/module.mk index 5b93c80d57..a9d9bfc869 100644 --- a/audio/module.mk +++ b/audio/module.mk @@ -7,6 +7,7 @@ MODULE_OBJS := \ midiparser_smf.o \ midiparser_xmidi.o \ midiparser.o \ + midiplayer.o \ mixer.o \ mpu401.o \ musicplugin.o \ @@ -44,6 +45,7 @@ MODULE_OBJS := \ softsynth/ym2612.o \ softsynth/fluidsynth.o \ softsynth/mt32.o \ + softsynth/eas.o \ softsynth/pcspk.o \ softsynth/sid.o \ softsynth/wave6581.o diff --git a/audio/mpu401.cpp b/audio/mpu401.cpp index 4f62de930c..4834772c07 100644 --- a/audio/mpu401.cpp +++ b/audio/mpu401.cpp @@ -101,12 +101,18 @@ MidiDriver_MPU401::MidiDriver_MPU401() : } } +MidiDriver_MPU401::~MidiDriver_MPU401() { +} + void MidiDriver_MPU401::close() { - if (_timer_proc) + if (_timer_proc) { g_system->getTimerManager()->removeTimerProc(_timer_proc); - _timer_proc = 0; - for (int i = 0; i < 16; ++i) - send(0x7B << 8 | 0xB0 | i); + _timer_proc = 0; + } + if (isOpen()) { + for (int i = 0; i < 16; ++i) + send(0x7B << 8 | 0xB0 | i); + } } uint32 MidiDriver_MPU401::property(int prop, uint32 param) { diff --git a/audio/mpu401.h b/audio/mpu401.h index 070eaf636a..5f1a5108ac 100644 --- a/audio/mpu401.h +++ b/audio/mpu401.h @@ -34,8 +34,6 @@ // //////////////////////////////////////// -class MidiDriver_MPU401; - class MidiChannel_MPU401 : public MidiChannel { private: @@ -46,22 +44,22 @@ private: public: MidiDriver *device(); byte getNumber() { return _channel; } - void release() { _allocated = false; } + virtual void release() { _allocated = false; } - void send(uint32 b); + virtual void send(uint32 b); // Regular messages - void noteOff(byte note); - void noteOn(byte note, byte velocity); - void programChange(byte program); - void pitchBend(int16 bend); + virtual void noteOff(byte note); + virtual void noteOn(byte note, byte velocity); + virtual void programChange(byte program); + virtual void pitchBend(int16 bend); // Control Change messages - void controlChange(byte control, byte value); - void pitchBendFactor(byte value); + virtual void controlChange(byte control, byte value); + virtual void pitchBendFactor(byte value); // SysEx messages - void sysEx_customInstrument(uint32 type, const byte *instr); + virtual void sysEx_customInstrument(uint32 type, const byte *instr); // Only to be called by the owner void init(MidiDriver *owner, byte channel); @@ -78,14 +76,15 @@ private: public: MidiDriver_MPU401(); + virtual ~MidiDriver_MPU401(); virtual void close(); - void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc); - uint32 getBaseTempo(void) { return 10000; } - uint32 property(int prop, uint32 param); + virtual void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc); + virtual uint32 getBaseTempo(void) { return 10000; } + virtual uint32 property(int prop, uint32 param); - MidiChannel *allocateChannel(); - MidiChannel *getPercussionChannel() { return &_midi_channels[9]; } + virtual MidiChannel *allocateChannel(); + virtual MidiChannel *getPercussionChannel() { return &_midi_channels[9]; } }; diff --git a/audio/musicplugin.h b/audio/musicplugin.h index da0e0ff816..53a2ae426d 100644 --- a/audio/musicplugin.h +++ b/audio/musicplugin.h @@ -29,6 +29,10 @@ #include "audio/mididrv.h" #include "common/list.h" +namespace Common { +class Error; +} + class MusicPluginObject; /** diff --git a/audio/null.cpp b/audio/null.cpp index 152f5da03e..2066a6d048 100644 --- a/audio/null.cpp +++ b/audio/null.cpp @@ -22,6 +22,7 @@ * $Id$ */ +#include "common/error.h" #include "audio/null.h" Common::Error NullMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const { diff --git a/audio/null.h b/audio/null.h index 5df7493a06..8570d4d293 100644 --- a/audio/null.h +++ b/audio/null.h @@ -33,6 +33,7 @@ class MidiDriver_NULL : public MidiDriver_MPU401 { public: int open() { return 0; } + bool isOpen() const { return true; } void send(uint32 b) { } }; diff --git a/audio/rate.cpp b/audio/rate.cpp index 1fcf0ade2e..fd52503b8c 100644 --- a/audio/rate.cpp +++ b/audio/rate.cpp @@ -35,6 +35,7 @@ #include "audio/rate.h" #include "audio/mixer.h" #include "common/frac.h" +#include "common/textconsole.h" #include "common/util.h" namespace Audio { diff --git a/audio/rate.h b/audio/rate.h index fb231e4c4a..af37994d02 100644 --- a/audio/rate.h +++ b/audio/rate.h @@ -27,13 +27,11 @@ #define SOUND_RATE_H #include "common/scummsys.h" -#include "engines/engine.h" - -class AudioStream; - namespace Audio { +class AudioStream; + typedef int16 st_sample_t; typedef uint16 st_volume_t; typedef uint32 st_size_t; diff --git a/audio/rate_arm.cpp b/audio/rate_arm.cpp index 41944ef698..1eb55d0dc3 100644 --- a/audio/rate_arm.cpp +++ b/audio/rate_arm.cpp @@ -48,6 +48,7 @@ #include "audio/rate.h" #include "audio/mixer.h" #include "common/util.h" +#include "common/textconsole.h" //#define DEBUG_RATECONV @@ -60,7 +61,7 @@ namespace Audio { * ARM routine we call doesn't respect those definitions. */ #define FRAC_BITS 16 -#define FRAC_ONE (1<<FRAC_BITS) +#define FRAC_ONE (1 << FRAC_BITS) /** * The size of the intermediate input cache. Bigger values may increase @@ -129,43 +130,40 @@ SimpleRateConverter<stereo, reverseStereo>::SimpleRateConverter(st_rate_t inrate sr.inLen = 0; } -extern "C" { #ifndef IPHONE #define ARM_SimpleRate_M _ARM_SimpleRate_M #define ARM_SimpleRate_S _ARM_SimpleRate_S #define ARM_SimpleRate_R _ARM_SimpleRate_R #endif -} extern "C" st_sample_t *ARM_SimpleRate_M( - AudioStream &input, - int (*fn)(Audio::AudioStream&,int16*,int), - SimpleRateDetails *sr, - st_sample_t *obuf, - st_size_t osamp, - st_volume_t vol_l, - st_volume_t vol_r); + AudioStream &input, + int (*fn)(Audio::AudioStream&,int16*,int), + SimpleRateDetails *sr, + st_sample_t *obuf, + st_size_t osamp, + st_volume_t vol_l, + st_volume_t vol_r); extern "C" st_sample_t *ARM_SimpleRate_S( - AudioStream &input, - int (*fn)(Audio::AudioStream&,int16*,int), - SimpleRateDetails *sr, - st_sample_t *obuf, - st_size_t osamp, - st_volume_t vol_l, - st_volume_t vol_r); + AudioStream &input, + int (*fn)(Audio::AudioStream&,int16*,int), + SimpleRateDetails *sr, + st_sample_t *obuf, + st_size_t osamp, + st_volume_t vol_l, + st_volume_t vol_r); extern "C" st_sample_t *ARM_SimpleRate_R( - AudioStream &input, - int (*fn)(Audio::AudioStream&,int16*,int), - SimpleRateDetails *sr, - st_sample_t *obuf, - st_size_t osamp, - st_volume_t vol_l, - st_volume_t vol_r); - -extern "C" int SimpleRate_readFudge(Audio::AudioStream &input, - int16 *a, int b) + AudioStream &input, + int (*fn)(Audio::AudioStream&,int16*,int), + SimpleRateDetails *sr, + st_sample_t *obuf, + st_size_t osamp, + st_volume_t vol_l, + st_volume_t vol_r); + +extern "C" int SimpleRate_readFudge(Audio::AudioStream &input, int16 *a, int b) { #ifdef DEBUG_RATECONV debug("Reading ptr=%x n%d", a, b); @@ -183,21 +181,22 @@ int SimpleRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_samp if (!stereo) { obuf = ARM_SimpleRate_M(input, - &SimpleRate_readFudge, - &sr, - obuf, osamp, vol_l, vol_r); + &SimpleRate_readFudge, + &sr, + obuf, osamp, vol_l, vol_r); } else if (reverseStereo) { obuf = ARM_SimpleRate_R(input, - &SimpleRate_readFudge, - &sr, - obuf, osamp, vol_l, vol_r); + &SimpleRate_readFudge, + &sr, + obuf, osamp, vol_l, vol_r); } else { obuf = ARM_SimpleRate_S(input, - &SimpleRate_readFudge, - &sr, - obuf, osamp, vol_l, vol_r); + &SimpleRate_readFudge, + &sr, + obuf, osamp, vol_l, vol_r); } - return (obuf-ostart)/2; + + return (obuf - ostart) / 2; } /** @@ -240,31 +239,31 @@ extern "C" { } extern "C" st_sample_t *ARM_LinearRate_M( - AudioStream &input, - int (*fn)(Audio::AudioStream&,int16*,int), - LinearRateDetails *lr, - st_sample_t *obuf, - st_size_t osamp, - st_volume_t vol_l, - st_volume_t vol_r); + AudioStream &input, + int (*fn)(Audio::AudioStream&,int16*,int), + LinearRateDetails *lr, + st_sample_t *obuf, + st_size_t osamp, + st_volume_t vol_l, + st_volume_t vol_r); extern "C" st_sample_t *ARM_LinearRate_S( - AudioStream &input, - int (*fn)(Audio::AudioStream&,int16*,int), - LinearRateDetails *lr, - st_sample_t *obuf, - st_size_t osamp, - st_volume_t vol_l, - st_volume_t vol_r); + AudioStream &input, + int (*fn)(Audio::AudioStream&,int16*,int), + LinearRateDetails *lr, + st_sample_t *obuf, + st_size_t osamp, + st_volume_t vol_l, + st_volume_t vol_r); extern "C" st_sample_t *ARM_LinearRate_R( - AudioStream &input, - int (*fn)(Audio::AudioStream&,int16*,int), - LinearRateDetails *lr, - st_sample_t *obuf, - st_size_t osamp, - st_volume_t vol_l, - st_volume_t vol_r); + AudioStream &input, + int (*fn)(Audio::AudioStream&,int16*,int), + LinearRateDetails *lr, + st_sample_t *obuf, + st_size_t osamp, + st_volume_t vol_l, + st_volume_t vol_r); template<bool stereo, bool reverseStereo> class LinearRateConverter : public RateConverter { @@ -320,23 +319,29 @@ int LinearRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_samp #endif st_sample_t *ostart = obuf; + if (vol_l > 0xff) + vol_l = 0xff; + + if (vol_r > 0xff) + vol_r = 0xff; + if (!stereo) { obuf = ARM_LinearRate_M(input, - &SimpleRate_readFudge, - &lr, - obuf, osamp, vol_l, vol_r); + &SimpleRate_readFudge, + &lr, + obuf, osamp, vol_l, vol_r); } else if (reverseStereo) { obuf = ARM_LinearRate_R(input, - &SimpleRate_readFudge, - &lr, - obuf, osamp, vol_l, vol_r); + &SimpleRate_readFudge, + &lr, + obuf, osamp, vol_l, vol_r); } else { obuf = ARM_LinearRate_S(input, - &SimpleRate_readFudge, - &lr, - obuf, osamp, vol_l, vol_r); + &SimpleRate_readFudge, + &lr, + obuf, osamp, vol_l, vol_r); } - return (obuf-ostart)/2; + return (obuf - ostart) / 2; } @@ -355,31 +360,32 @@ extern "C" { } extern "C" st_sample_t *ARM_CopyRate_M( - st_size_t len, - st_sample_t *obuf, - st_volume_t vol_l, - st_volume_t vol_r, - st_sample_t *_buffer); + st_size_t len, + st_sample_t *obuf, + st_volume_t vol_l, + st_volume_t vol_r, + st_sample_t *_buffer); extern "C" st_sample_t *ARM_CopyRate_S( - st_size_t len, - st_sample_t *obuf, - st_volume_t vol_l, - st_volume_t vol_r, - st_sample_t *_buffer); + st_size_t len, + st_sample_t *obuf, + st_volume_t vol_l, + st_volume_t vol_r, + st_sample_t *_buffer); extern "C" st_sample_t *ARM_CopyRate_R( - st_size_t len, - st_sample_t *obuf, - st_volume_t vol_l, - st_volume_t vol_r, - st_sample_t *_buffer); + st_size_t len, + st_sample_t *obuf, + st_volume_t vol_l, + st_volume_t vol_r, + st_sample_t *_buffer); template<bool stereo, bool reverseStereo> class CopyRateConverter : public RateConverter { st_sample_t *_buffer; st_size_t _bufferSize; + public: CopyRateConverter() : _buffer(0), _bufferSize(0) {} ~CopyRateConverter() { @@ -393,7 +399,7 @@ public: debug("Copy st=%d rev=%d", stereo, reverseStereo); #endif st_size_t len; - st_sample_t *ostart = obuf; + st_sample_t *ostart = obuf; if (stereo) osamp *= 2; @@ -418,8 +424,9 @@ public: else obuf = ARM_CopyRate_M(len, obuf, vol_l, vol_r, _buffer); - return (obuf-ostart)/2; + return (obuf - ostart) / 2; } + virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) { return (ST_SUCCESS); } @@ -463,3 +470,4 @@ RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stere } } // End of namespace Audio + diff --git a/audio/softsynth/adlib.cpp b/audio/softsynth/adlib.cpp index 4a9ce54c75..0b5215e5d6 100644 --- a/audio/softsynth/adlib.cpp +++ b/audio/softsynth/adlib.cpp @@ -24,6 +24,11 @@ #include "audio/softsynth/emumidi.h" #include "common/debug.h" +#include "common/error.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "common/textconsole.h" +#include "common/types.h" #include "common/util.h" #include "audio/fmopl.h" #include "audio/musicplugin.h" @@ -118,7 +123,7 @@ public: byte getNumber() { return _channel; } void release() { _allocated = false; } - void send (uint32 b); + void send(uint32 b); // Regular messages void noteOff(byte note); diff --git a/audio/softsynth/eas.cpp b/audio/softsynth/eas.cpp new file mode 100644 index 0000000000..7d17655d20 --- /dev/null +++ b/audio/softsynth/eas.cpp @@ -0,0 +1,486 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/scummsys.h" + +#if defined(__ANDROID__) + +#include <dlfcn.h> + +#include "common/debug.h" +#include "common/endian.h" +#include "common/textconsole.h" +#include "common/error.h" +#include "common/file.h" +#include "common/config-manager.h" +#include "common/system.h" +#include "audio/audiostream.h" +#include "audio/mpu401.h" +#include "audio/musicplugin.h" +#include "audio/mixer.h" + +//#define EAS_DUMPSTREAM + +// NOTE: +// EAS's render function *only* accepts one mix buffer size. it's defined at +// compile time of the library and can be retrieved via EASLibConfig.bufSize +// (seen: 128 bytes). +// to avoid local intermediate buffers, this implementation insists on a fixed +// buffer size of the calling rate converter, which in return must be a +// multiple of EAS's. that may change if there're hickups because slower +// devices can't render fast enough + +// from rate_arm.cpp +#define INTERMEDIATE_BUFFER_SIZE 512 + +// so far all android versions have the very same library version +#define EAS_LIBRARY "libsonivox.so" +#define EAS_KNOWNVERSION 0x03060a0e + +#define EAS_REVERB 2 +#define EAS_REVERB_BYPASS 0 +#define EAS_REVERB_PRESET 1 +#define EAS_REVERB_CHAMBER 2 + +class MidiDriver_EAS : public MidiDriver_MPU401, Audio::AudioStream { +public: + MidiDriver_EAS(); + virtual ~MidiDriver_EAS(); + + // MidiDriver + virtual int open(); + virtual bool isOpen() const; + virtual void close(); + virtual void send(uint32 b); + virtual void sysEx(const byte *msg, uint16 length); + virtual void setTimerCallback(void *timerParam, + Common::TimerManager::TimerProc timerProc); + virtual uint32 getBaseTempo(); + + // AudioStream + virtual int readBuffer(int16 *buffer, const int numSamples); + virtual bool isStereo() const; + virtual int getRate() const; + virtual bool endOfData() const; + +private: + struct EASLibConfig { + uint32 version; + uint32 debug; + int32 voices; + int32 channels; + int32 rate; + int32 bufSize; + uint32 filter; + uint32 timeStamp; + char *GUID; + }; + + struct EASFile { + const char *path; + int fd; + long long offset; + long long length; + }; + + typedef void * EASDataHandle; + typedef void * EASHandle; + + typedef EASLibConfig *(*ConfigFunc)(); + typedef int32 (*InitFunc)(EASDataHandle *); + typedef int32 (*ShutdownFunc)(EASDataHandle); + typedef int32 (*LoadDLSFunc)(EASDataHandle, EASHandle, EASFile *); + typedef int32 (*SetParameterFunc)(EASDataHandle, int32, int32, int32); + typedef int32 (*SetVolumeFunc)(EASDataHandle, EASHandle, int32); + typedef int32 (*OpenStreamFunc)(EASDataHandle, EASHandle *, EASHandle); + typedef int32 (*WriteStreamFunc)(EASDataHandle, EASHandle, byte *, int32); + typedef int32 (*CloseStreamFunc)(EASDataHandle, EASHandle); + typedef int32 (*RenderFunc)(EASDataHandle, int16 *, int32, int32 *); + + template<typename T> + void sym(T &t, const char *symbol) { + union { + void *v; + T t; + } u; + + assert(sizeof(u.v) == sizeof(u.t)); + + u.v = dlsym(_dlHandle, symbol); + + if (!u.v) + warning("couldn't resolve %s from " EAS_LIBRARY, symbol); + + t = u.t; + } + + void *_dlHandle; + + ConfigFunc _configFunc; + InitFunc _initFunc; + ShutdownFunc _shutdownFunc; + LoadDLSFunc _loadDLSFunc; + SetParameterFunc _setParameterFunc; + SetVolumeFunc _setVolumeFunc; + OpenStreamFunc _openStreamFunc; + WriteStreamFunc _writeStreamFunc; + CloseStreamFunc _closeStreamFunc; + RenderFunc _renderFunc; + + const EASLibConfig *_config; + EASDataHandle _EASHandle; + EASHandle _midiStream; + + Common::TimerManager::TimerProc _timerProc; + void *_timerParam; + uint32 _baseTempo; + uint _rounds; + Audio::SoundHandle _soundHandle; + + Common::DumpFile _dump; +}; + +MidiDriver_EAS::MidiDriver_EAS() : + MidiDriver_MPU401(), + _dlHandle(0), + _configFunc(0), + _initFunc(0), + _shutdownFunc(0), + _loadDLSFunc(0), + _setParameterFunc(0), + _setVolumeFunc(0), + _openStreamFunc(0), + _writeStreamFunc(0), + _closeStreamFunc(0), + _renderFunc(0), + _config(0), + _EASHandle(0), + _midiStream(0), + _timerProc(0), + _timerParam(0), + _baseTempo(0), + _rounds(0), + _soundHandle(), + _dump() { +} + +MidiDriver_EAS::~MidiDriver_EAS() { +} + +int MidiDriver_EAS::open() { + if (isOpen()) + return MERR_ALREADY_OPEN; + + _dlHandle = dlopen(EAS_LIBRARY, RTLD_LAZY); + if (!_dlHandle) { + warning("error opening " EAS_LIBRARY ": %s", dlerror()); + return MERR_DEVICE_NOT_AVAILABLE; + } + + sym(_configFunc, "EAS_Config"); + if (!_configFunc) { + close(); + return -1; + } + + _config = _configFunc(); + if (!_config) { + close(); + warning("error retrieving EAS library configuration"); + return -1; + } + + if (_config->version != EAS_KNOWNVERSION) { + close(); + warning("unknown EAS library version: 0x%08x", _config->version); + return -1; + } + + if (_config->channels > 2) { + close(); + warning("unsupported number of EAS channels: %d", _config->channels); + return -1; + } + + // see note at top of this file + if (INTERMEDIATE_BUFFER_SIZE % (_config->bufSize * _config->channels)) { + close(); + warning("unsupported EAS buffer size: %d", _config->bufSize); + return -1; + } + + sym(_initFunc, "EAS_Init"); + sym(_shutdownFunc, "EAS_Shutdown"); + sym(_loadDLSFunc, "EAS_LoadDLSCollection"); + sym(_setParameterFunc, "EAS_SetParameter"); + sym(_setVolumeFunc, "EAS_SetVolume"); + sym(_openStreamFunc, "EAS_OpenMIDIStream"); + sym(_writeStreamFunc, "EAS_WriteMIDIStream"); + sym(_closeStreamFunc, "EAS_CloseMIDIStream"); + sym(_renderFunc, "EAS_Render"); + + if (!_initFunc || !_shutdownFunc || !_loadDLSFunc || !_setParameterFunc || + !_openStreamFunc || !_writeStreamFunc || !_closeStreamFunc || + !_renderFunc) { + close(); + return -1; + } + + int32 res = _initFunc(&_EASHandle); + if (res) { + close(); + warning("error initializing the EAS library: %d", res); + return -1; + } + + res = _setParameterFunc(_EASHandle, EAS_REVERB, EAS_REVERB_PRESET, + EAS_REVERB_CHAMBER); + if (res) + warning("error setting reverb preset: %d", res); + + res = _setParameterFunc(_EASHandle, EAS_REVERB, EAS_REVERB_BYPASS, 0); + if (res) + warning("error disabling reverb bypass: %d", res); + + // 90 is EAS's default, max is 100 + // so the option slider will only work from 0.1 to 1.1 + res = _setVolumeFunc(_EASHandle, 0, ConfMan.getInt("midi_gain") - 10); + if (res) + warning("error setting EAS master volume: %d", res); + + res = _openStreamFunc(_EASHandle, &_midiStream, 0); + if (res) { + close(); + warning("error opening EAS MIDI stream: %d", res); + return -1; + } + + // set the timer frequency to match a single buffer size + _baseTempo = (1000000 * _config->bufSize) / _config->rate; + + // number of buffer fills per readBuffer() + _rounds = INTERMEDIATE_BUFFER_SIZE / (_config->bufSize * _config->channels); + + debug("EAS initialized (voices:%d channels:%d rate:%d buffer:%d) " + "tempo:%u rounds:%u", _config->voices, _config->channels, + _config->rate, _config->bufSize, _baseTempo, _rounds); + + // TODO doesn't seem to work with midi streams? + if (ConfMan.hasKey("soundfont")) { + const Common::String dls = ConfMan.get("soundfont"); + + debug("loading DLS file '%s'", dls.c_str()); + + EASFile f; + memset(&f, 0, sizeof(EASFile)); + f.path = dls.c_str(); + + res = _loadDLSFunc(_EASHandle, 0, &f); + if (res) + warning("error loading DLS file '%s': %d", dls.c_str(), res); + else + debug("DLS file loaded"); + } + +#ifdef EAS_DUMPSTREAM + if (!_dump.open("/sdcard/eas.dump")) + warning("error opening EAS dump file"); +#endif + + g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, + &_soundHandle, this, -1, + Audio::Mixer::kMaxChannelVolume, 0, + DisposeAfterUse::NO, true); + + return 0; +} + +bool MidiDriver_EAS::isOpen() const { + return _dlHandle != 0; +} + +void MidiDriver_EAS::close() { + MidiDriver_MPU401::close(); + + if (!isOpen()) + return; + + g_system->getMixer()->stopHandle(_soundHandle); + +#ifdef EAS_DUMPSTREAM + if (_dump.isOpen()) + _dump.close(); +#endif + + // not pretty, but better than a mutex + g_system->delayMillis((_baseTempo * _rounds) / 1000); + + if (_midiStream) { + int32 res = _closeStreamFunc(_EASHandle, _midiStream); + if (res) + warning("error closing EAS MIDI stream: %d", res); + + _midiStream = 0; + } + + if (_EASHandle) { + int32 res = _shutdownFunc(_EASHandle); + if (res) + warning("error shutting down the EAS library: %d", res); + + _EASHandle = 0; + } + + if (dlclose(_dlHandle)) + warning("error closing " EAS_LIBRARY ": %s", dlerror()); + + _dlHandle = 0; +} + +void MidiDriver_EAS::send(uint32 b) { + byte buf[4]; + + WRITE_LE_UINT32(buf, b); + + int32 len = 3; + if ((buf[0] >> 4) == 0xC || (buf[0] >> 4) == 0xD) + len = 2; + + int32 res = _writeStreamFunc(_EASHandle, _midiStream, buf, len); + if (res) + warning("error writing to EAS MIDI stream: %d", res); +} + +void MidiDriver_EAS::sysEx(const byte *msg, uint16 length) { + byte buf[266]; + + assert(length + 2 <= ARRAYSIZE(buf)); + + buf[0] = 0xF0; + memcpy(buf + 1, msg, length); + buf[length + 1] = 0xF7; + + int32 res = _writeStreamFunc(_EASHandle, _midiStream, buf, length + 2); + if (res) + warning("error writing to EAS MIDI stream: %d", res); +} + +void MidiDriver_EAS::setTimerCallback(void *timerParam, + Common::TimerManager::TimerProc timerProc) { + _timerParam = timerParam; + _timerProc = timerProc; +} + +uint32 MidiDriver_EAS::getBaseTempo() { + return _baseTempo; +} + +int MidiDriver_EAS::readBuffer(int16 *buffer, const int numSamples) { + // see note at top of this file + assert(numSamples == INTERMEDIATE_BUFFER_SIZE); + + int32 res, c; + + for (uint i = 0; i < _rounds; ++i) { + // pull in MIDI events for exactly one buffer size + if (_timerProc) + (*_timerProc)(_timerParam); + + // if there are no MIDI events, this just renders silence + res = _renderFunc(_EASHandle, buffer, _config->bufSize, &c); + if (res) { + warning("error rendering EAS samples: %d", res); + return -1; + } + +#ifdef EAS_DUMPSTREAM + if (_dump.isOpen()) + _dump.write(buffer, c * _config->channels * 2); +#endif + + buffer += c * _config->channels; + } + + return numSamples; +} + +bool MidiDriver_EAS::isStereo() const { + return _config->channels == 2; +} + +int MidiDriver_EAS::getRate() const { + return _config->rate; +} + +bool MidiDriver_EAS::endOfData() const { + return false; +} + +class EASMusicPlugin : public MusicPluginObject { +public: + EASMusicPlugin(); + virtual ~EASMusicPlugin(); + + const char *getName() const; + const char *getId() const; + MusicDevices getDevices() const; + Common::Error createInstance(MidiDriver **mididriver, + MidiDriver::DeviceHandle = 0) const; +}; + +EASMusicPlugin::EASMusicPlugin() { +} + +EASMusicPlugin::~EASMusicPlugin() { +} + +const char *EASMusicPlugin::getName() const { + return "Embedded Audio Synthesis"; +} + +const char *EASMusicPlugin::getId() const { + return "eas"; +} + +MusicDevices EASMusicPlugin::getDevices() const { + MusicDevices devices; + devices.push_back(MusicDevice(this, "", MT_GM)); + + return devices; +} + +Common::Error EASMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const { + *mididriver = new MidiDriver_EAS(); + + return Common::kNoError; +} + +//#if PLUGIN_ENABLED_DYNAMIC(EAS) + //REGISTER_PLUGIN_DYNAMIC(EAS, PLUGIN_TYPE_MUSIC, EASMusicPlugin); +//#else + REGISTER_PLUGIN_STATIC(EAS, PLUGIN_TYPE_MUSIC, EASMusicPlugin); +//#endif + +#endif + diff --git a/audio/softsynth/emumidi.h b/audio/softsynth/emumidi.h index 35c81490e4..190b70c392 100644 --- a/audio/softsynth/emumidi.h +++ b/audio/softsynth/emumidi.h @@ -45,25 +45,24 @@ private: int _samplesPerTick; protected: + int _baseFreq; + virtual void generateSamples(int16 *buf, int len) = 0; virtual void onTimer() {} - int _baseFreq; - public: - MidiDriver_Emulated(Audio::Mixer *mixer) : _mixer(mixer) { - _isOpen = false; - - _timerProc = 0; - _timerParam = 0; - - _nextTick = 0; - _samplesPerTick = 0; - - _baseFreq = 250; + MidiDriver_Emulated(Audio::Mixer *mixer) : + _mixer(mixer), + _isOpen(false), + _timerProc(0), + _timerParam(0), + _nextTick(0), + _samplesPerTick(0), + _baseFreq(250) { } - int open() { + // MidiDriver API + virtual int open() { _isOpen = true; int d = getRate() / _baseFreq; @@ -73,19 +72,23 @@ public: // but less prone to arithmetic overflow. _samplesPerTick = (d << FIXP_SHIFT) + (r << FIXP_SHIFT) / _baseFreq; + return 0; } - void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { + bool isOpen() const { return _isOpen; } + + virtual void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { _timerProc = timer_proc; _timerParam = timer_param; } - uint32 getBaseTempo() { return 1000000 / _baseFreq; } - + virtual uint32 getBaseTempo() { + return 1000000 / _baseFreq; + } // AudioStream API - int readBuffer(int16 *data, const int numSamples) { + virtual int readBuffer(int16 *data, const int numSamples) { const int stereoFactor = isStereo() ? 2 : 1; int len = numSamples / stereoFactor; int step; @@ -101,16 +104,22 @@ public: if (!(_nextTick >> FIXP_SHIFT)) { if (_timerProc) (*_timerProc)(_timerParam); + onTimer(); + _nextTick += _samplesPerTick; } + data += step * stereoFactor; len -= step; } while (len); return numSamples; } - bool endOfData() const { return false; } + + virtual bool endOfData() const { + return false; + } }; #endif diff --git a/audio/softsynth/fluidsynth.cpp b/audio/softsynth/fluidsynth.cpp index bd016548ec..a4877a4ec1 100644 --- a/audio/softsynth/fluidsynth.cpp +++ b/audio/softsynth/fluidsynth.cpp @@ -27,6 +27,9 @@ #ifdef USE_FLUIDSYNTH #include "common/config-manager.h" +#include "common/error.h" +#include "common/system.h" +#include "common/textconsole.h" #include "audio/musicplugin.h" #include "audio/mpu401.h" #include "audio/softsynth/emumidi.h" @@ -40,7 +43,6 @@ private: fluid_synth_t *_synth; int _soundFont; int _outputRate; - Audio::SoundHandle _handle; protected: // Because GCC complains about casting from const to non-const... @@ -144,7 +146,7 @@ int MidiDriver_FluidSynth::open() { MidiDriver_Emulated::open(); // The MT-32 emulator uses kSFXSoundType here. I don't know why. - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); return 0; } @@ -153,7 +155,7 @@ void MidiDriver_FluidSynth::close() { return; _isOpen = false; - _mixer->stopHandle(_handle); + _mixer->stopHandle(_mixerSoundHandle); if (_soundFont != -1) fluid_synth_sfunload(_synth, _soundFont, 1); diff --git a/audio/softsynth/fmtowns_pc98/towns_audio.cpp b/audio/softsynth/fmtowns_pc98/towns_audio.cpp index e019aa2481..7b6658ab3a 100644 --- a/audio/softsynth/fmtowns_pc98/towns_audio.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_audio.cpp @@ -25,6 +25,7 @@ #include "audio/softsynth/fmtowns_pc98/towns_audio.h" #include "common/endian.h" +#include "common/util.h" #include "backends/audiocd/audiocd.h" diff --git a/audio/softsynth/fmtowns_pc98/towns_euphony.cpp b/audio/softsynth/fmtowns_pc98/towns_euphony.cpp index 7c071c43fb..cd3a348b85 100644 --- a/audio/softsynth/fmtowns_pc98/towns_euphony.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_euphony.cpp @@ -25,6 +25,8 @@ #include "audio/softsynth/fmtowns_pc98/towns_euphony.h" #include "common/endian.h" +#include "common/util.h" +#include "common/textconsole.h" TownsEuphonyDriver::TownsEuphonyDriver(Audio::Mixer *mixer) : _activeChannels(0), _sustainChannels(0), _assignedChannels(0), _paraCount(0), _command(0), _tEnable(0), _tMode(0), _tOrdr(0), _tLevel(0), @@ -40,6 +42,7 @@ TownsEuphonyDriver::~TownsEuphonyDriver() { delete[] _activeChannels; delete[] _sustainChannels; delete[] _assignedChannels; + delete[] _eventBuffer; delete[] _tEnable; delete[] _tMode; delete[] _tOrdr; @@ -51,6 +54,16 @@ bool TownsEuphonyDriver::init() { if (!_intf->init()) return false; + delete[] _activeChannels; + delete[] _sustainChannels; + delete[] _assignedChannels; + delete[] _eventBuffer; + delete[] _tEnable; + delete[] _tMode; + delete[] _tOrdr; + delete[] _tLevel; + delete[] _tTranspose; + _activeChannels = new int8[16]; _sustainChannels = new int8[16]; _assignedChannels = new ActiveChannel[128]; diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp index 79fd95ea0d..a551276ab1 100644 --- a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp @@ -25,6 +25,7 @@ #include "audio/softsynth/fmtowns_pc98/towns_pc98_driver.h" #include "common/endian.h" +#include "common/textconsole.h" class TownsPC98_MusicChannel { public: diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp index f84fd841a6..9d3751a0cc 100644 --- a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp @@ -25,6 +25,8 @@ #include "audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h" #include "common/endian.h" +#include "common/textconsole.h" +#include "common/util.h" class TownsPC98_FmSynthOperator { public: @@ -1253,7 +1255,7 @@ void TownsPC98_FmSynth::generateTables() { delete[] _oprSinTbl; _oprSinTbl = new uint32[1024]; for (int i = 0; i < 1024; i++) { - double val = sin((double)(((i << 1) + 1) * PI / 1024.0)); + double val = sin((double)(((i << 1) + 1) * M_PI / 1024.0)); double d_dcb = log(1.0 / (double)ABS(val)) / log(2.0) * 256.0; int32 i_dcb = (int32)(2.0 * d_dcb); i_dcb = (i_dcb & 1) ? (i_dcb >> 1) + 1 : (i_dcb >> 1); diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h index 18cca56e29..2b916b2cdc 100644 --- a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h +++ b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h @@ -28,7 +28,7 @@ #include "audio/audiostream.h" #include "audio/mixer.h" -#include "common/list.h" +#include "common/mutex.h" #ifdef __DS__ /* This disables the rhythm channel when emulating the PC-98 type 86 sound card. diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp index a980b564fc..5371be60b3 100644 --- a/audio/softsynth/mt32.cpp +++ b/audio/softsynth/mt32.cpp @@ -34,15 +34,20 @@ #include "common/config-manager.h" #include "common/debug.h" +#include "common/error.h" #include "common/events.h" #include "common/file.h" #include "common/system.h" #include "common/util.h" #include "common/archive.h" +#include "common/textconsole.h" #include "common/translation.h" #include "graphics/fontman.h" #include "graphics/surface.h" +#include "graphics/pixelformat.h" +#include "graphics/palette.h" +#include "graphics/font.h" class MidiChannel_MT32 : public MidiChannel_MPU401 { void effectLevel(byte value) { } @@ -51,7 +56,6 @@ class MidiChannel_MT32 : public MidiChannel_MPU401 { class MidiDriver_MT32 : public MidiDriver_Emulated { private: - Audio::SoundHandle _handle; MidiChannel_MT32 _midiChannels[16]; uint16 _channelMask; MT32Emu::Synth *_synth; @@ -336,7 +340,7 @@ int MidiDriver_MT32::open() { g_system->updateScreen(); - _mixer->playStream(Audio::Mixer::kSFXSoundType, &_handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); return 0; } @@ -347,7 +351,7 @@ void MidiDriver_MT32::send(uint32 b) { void MidiDriver_MT32::setPitchBendRange(byte channel, uint range) { if (range > 24) { - printf("setPitchBendRange() called with range > 24: %d", range); + warning("setPitchBendRange() called with range > 24: %d", range); } byte benderRangeSysex[9]; benderRangeSysex[0] = 0x41; // Roland @@ -378,7 +382,7 @@ void MidiDriver_MT32::close() { // Detach the player callback handler setTimerCallback(NULL, NULL); // Detach the mixer callback handler - _mixer->stopHandle(_handle); + _mixer->stopHandle(_mixerSoundHandle); _synth->close(); delete _synth; diff --git a/audio/softsynth/mt32/part.cpp b/audio/softsynth/mt32/part.cpp index eb087f7ea0..9f9269cba5 100644 --- a/audio/softsynth/mt32/part.cpp +++ b/audio/softsynth/mt32/part.cpp @@ -100,7 +100,7 @@ void RhythmPart::setBend(unsigned int midiBend) { } void Part::setBend(unsigned int midiBend) { - // FIXME:KG: Slightly unbalanced increments, but I wanted min -1.0, centre 0.0 and max 1.0 + // FIXME:KG: Slightly unbalanced increments, but I wanted min -1.0, center 0.0 and max 1.0 if (midiBend <= 0x2000) { bend = ((signed int)midiBend - 0x2000) / (float)0x2000; } else { @@ -413,7 +413,7 @@ void RhythmPart::setPan(unsigned int midiPan) } void Part::setPan(unsigned int midiPan) { - // FIXME:KG: Tweaked this a bit so that we have a left 100%, centre and right 100% + // FIXME:KG: Tweaked this a bit so that we have a left 100%, center and right 100% // (But this makes the range somewhat skewed) // Check against the real thing // NOTE: Panning is inverted compared to GM. diff --git a/audio/softsynth/mt32/part.h b/audio/softsynth/mt32/part.h index 54c4999653..967298258c 100644 --- a/audio/softsynth/mt32/part.h +++ b/audio/softsynth/mt32/part.h @@ -24,7 +24,6 @@ namespace MT32Emu { -class PartialManager; class Synth; class Part { diff --git a/audio/softsynth/mt32/synth.cpp b/audio/softsynth/mt32/synth.cpp index 16460795a5..112527cc71 100644 --- a/audio/softsynth/mt32/synth.cpp +++ b/audio/softsynth/mt32/synth.cpp @@ -19,14 +19,18 @@ * IN THE SOFTWARE. */ +// FIXME: Avoid using printf +#define FORBIDDEN_SYMBOL_EXCEPTION_printf + +// FIXME: Avoid using vprintf +#define FORBIDDEN_SYMBOL_EXCEPTION_vprintf + #include <math.h> #include <string.h> #include <stdlib.h> #include "mt32emu.h" -#include "common/str.h" - #if defined(MACOSX) || defined(SOLARIS) || defined(__MINGW32__) // Older versions of Mac OS X didn't supply a powf function, so using it // will cause a binary incompatibility when trying to run a binary built diff --git a/audio/softsynth/mt32/synth.h b/audio/softsynth/mt32/synth.h index 3fc303d322..edda446287 100644 --- a/audio/softsynth/mt32/synth.h +++ b/audio/softsynth/mt32/synth.h @@ -29,7 +29,6 @@ class revmodel; namespace MT32Emu { class File; -class TableInitialiser; class Partial; class PartialManager; class Part; diff --git a/audio/softsynth/opl/dbopl.cpp b/audio/softsynth/opl/dbopl.cpp index 47e263b6b9..2c46cfee75 100644 --- a/audio/softsynth/opl/dbopl.cpp +++ b/audio/softsynth/opl/dbopl.cpp @@ -41,10 +41,6 @@ namespace OPL { namespace DOSBox { -#ifndef PI -#define PI 3.14159265358979323846 -#endif - namespace DBOPL { #define OPLRATE ((double)(14318180.0 / 288.0)) @@ -1392,7 +1388,7 @@ void InitTables( void ) { //Add 0.5 for the trunc rounding of the integer cast //Do a PI sinetable instead of the original 0.5 PI for ( int i = 0; i < 512; i++ ) { - SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); + SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (M_PI / 512.0) ) ) / log10(2.0)*256 ); } #endif #if ( DBOPL_WAVE == WAVE_TABLEMUL ) @@ -1406,7 +1402,7 @@ void InitTables( void ) { //Sine Wave Base for ( int i = 0; i < 512; i++ ) { - WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (PI / 512.0) ) * 4084); + WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (M_PI / 512.0) ) * 4084); WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ]; } //Exponential wave @@ -1418,7 +1414,7 @@ void InitTables( void ) { #if ( DBOPL_WAVE == WAVE_TABLELOG ) //Sine Wave Base for ( int i = 0; i < 512; i++ ) { - WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); + WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (M_PI / 512.0) ) ) / log10(2.0)*256 ); WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i]; } //Exponential wave diff --git a/audio/softsynth/opl/dosbox.cpp b/audio/softsynth/opl/dosbox.cpp index 29993ce3d8..7a494d70ec 100644 --- a/audio/softsynth/opl/dosbox.cpp +++ b/audio/softsynth/opl/dosbox.cpp @@ -36,6 +36,7 @@ #include "common/system.h" #include "common/scummsys.h" +#include "common/util.h" #include <math.h> #include <string.h> diff --git a/audio/softsynth/opl/mame.cpp b/audio/softsynth/opl/mame.cpp index c875080e8f..5d790f924f 100644 --- a/audio/softsynth/opl/mame.cpp +++ b/audio/softsynth/opl/mame.cpp @@ -33,6 +33,9 @@ #include "mame.h" +#include "common/textconsole.h" +#include "common/util.h" + #if defined (_WIN32_WCE) || defined (__SYMBIAN32__) || defined(__GP32__) || defined(GP2X) || defined (__MAEMO__) || defined(__DS__) || defined (__MINT__) || defined(__N64__) #include "common/config-manager.h" #endif @@ -708,7 +711,7 @@ static int OPLOpenTable(void) { /* degree 0 = degree 180 = off */ SIN_TABLE[0] = SIN_TABLE[SIN_ENT /2 ] = &TL_TABLE[EG_ENT - 1]; for (s = 1;s <= SIN_ENT / 4; s++) { - pom = sin(2 * PI * s / SIN_ENT); /* sin */ + pom = sin(2 * M_PI * s / SIN_ENT); /* sin */ pom = 20 * log10(1 / pom); /* decibel */ j = int(pom / EG_STEP); /* TL_TABLE steps */ @@ -739,14 +742,14 @@ static int OPLOpenTable(void) { ENV_CURVE[EG_OFF >> ENV_BITS]= EG_ENT - 1; /* make LFO ams table */ for (i=0; i < AMS_ENT; i++) { - pom = (1.0 + sin(2 * PI * i / AMS_ENT)) / 2; /* sin */ + pom = (1.0 + sin(2 * M_PI * i / AMS_ENT)) / 2; /* sin */ AMS_TABLE[i] = (int)((1.0 / EG_STEP) * pom); /* 1dB */ AMS_TABLE[AMS_ENT + i] = (int)((4.8 / EG_STEP) * pom); /* 4.8dB */ } /* make LFO vibrate table */ for (i=0; i < VIB_ENT; i++) { /* 100cent = 1seminote = 6% ?? */ - pom = (double)VIB_RATE * 0.06 * sin(2 * PI * i / VIB_ENT); /* +-100sect step */ + pom = (double)VIB_RATE * 0.06 * sin(2 * M_PI * i / VIB_ENT); /* +-100sect step */ VIB_TABLE[i] = (int)(VIB_RATE + (pom * 0.07)); /* +- 7cent */ VIB_TABLE[VIB_ENT + i] = (int)(VIB_RATE + (pom * 0.14)); /* +-14cent */ } diff --git a/audio/softsynth/opl/mame.h b/audio/softsynth/opl/mame.h index 58ef5f18dc..4314aa6dba 100644 --- a/audio/softsynth/opl/mame.h +++ b/audio/softsynth/opl/mame.h @@ -30,7 +30,6 @@ #define SOUND_SOFTSYNTH_OPL_MAME_H #include "common/scummsys.h" -#include "common/util.h" #include "common/random.h" #include "audio/fmopl.h" diff --git a/audio/softsynth/pcspk.cpp b/audio/softsynth/pcspk.cpp index 69ba113c8b..947c142b73 100644 --- a/audio/softsynth/pcspk.cpp +++ b/audio/softsynth/pcspk.cpp @@ -109,7 +109,7 @@ int8 PCSpeaker::generateSine(uint32 x, uint32 oscLength) { return 0; // TODO: Maybe using a look-up-table would be better? - return CLIP<int16>((int16) (128 * sin(2.0 * PI * x / oscLength)), -128, 127); + return CLIP<int16>((int16) (128 * sin(2.0 * M_PI * x / oscLength)), -128, 127); } int8 PCSpeaker::generateSaw(uint32 x, uint32 oscLength) { diff --git a/audio/softsynth/pcspk.h b/audio/softsynth/pcspk.h index c0d85bbceb..8f01fa852b 100644 --- a/audio/softsynth/pcspk.h +++ b/audio/softsynth/pcspk.h @@ -26,7 +26,6 @@ #define SOUND_SOFTSYNTH_PCSPK_H #include "audio/audiostream.h" -#include "audio/mixer.h" #include "common/mutex.h" namespace Audio { diff --git a/audio/softsynth/sid.cpp b/audio/softsynth/sid.cpp index 241766fa0a..40dc50e972 100644 --- a/audio/softsynth/sid.cpp +++ b/audio/softsynth/sid.cpp @@ -32,7 +32,6 @@ #include "sid.h" #include "audio/null.h" -#include <math.h> namespace Resid { @@ -110,7 +109,7 @@ void WaveformGenerator::reset() { msb_rising = false; } -RESID_INLINE void WaveformGenerator::clock(cycle_count delta_t) { +RESID_INLINE void WaveformGenerator::updateClock(cycle_count delta_t) { // No operation if test bit is set. if (test) { return; @@ -165,10 +164,10 @@ RESID_INLINE void WaveformGenerator::clock(cycle_count delta_t) { /** * Synchronize oscillators. - * This must be done after all the oscillators have been clock()'ed since the + * This must be done after all the oscillators have been updateClock()'ed since the * oscillators operate in parallel. * Note that the oscillators must be clocked exactly on the cycle when the - * MSB is set high for hard sync to operate correctly. See SID::clock(). + * MSB is set high for hard sync to operate correctly. See SID::updateClock(). */ RESID_INLINE void WaveformGenerator::synchronize() { // A special case occurs when a sync source is synced itself on the same @@ -592,7 +591,7 @@ void Filter::set_Q() { _1024_div_Q = static_cast<sound_sample>(1024.0/(0.707 + 1.0*res/0x0f)); } -RESID_INLINE void Filter::clock(cycle_count delta_t, +RESID_INLINE void Filter::updateClock(cycle_count delta_t, sound_sample voice1, sound_sample voice2, sound_sample voice3) @@ -888,7 +887,7 @@ reg8 EnvelopeGenerator::readENV() { return output(); } -RESID_INLINE void EnvelopeGenerator::clock(cycle_count delta_t) { +RESID_INLINE void EnvelopeGenerator::updateClock(cycle_count delta_t) { // Check for ADSR delay bug. // If the rate counter comparison value is set below the current value of the // rate counter, the counter will continue counting up until it wraps around @@ -1027,7 +1026,7 @@ void ExternalFilter::reset() { Vo = 0; } -RESID_INLINE void ExternalFilter::clock(cycle_count delta_t, sound_sample Vi) { +RESID_INLINE void ExternalFilter::updateClock(cycle_count delta_t, sound_sample Vi) { // This is handy for testing. if (!enabled) { // Remove maximum DC level since there is no filter to do it. @@ -1317,7 +1316,7 @@ bool SID::set_sampling_parameters(double clock_freq, return true; } -void SID::clock(cycle_count delta_t) { +void SID::updateClock(cycle_count delta_t) { int i; if (delta_t <= 0) { @@ -1333,7 +1332,7 @@ void SID::clock(cycle_count delta_t) { // Clock amplitude modulators. for (i = 0; i < 3; i++) { - voice[i].envelope.clock(delta_t); + voice[i].envelope.updateClock(delta_t); } // Clock and synchronize oscillators. @@ -1373,7 +1372,7 @@ void SID::clock(cycle_count delta_t) { // Clock oscillators. for (i = 0; i < 3; i++) { - voice[i].wave.clock(delta_t_min); + voice[i].wave.updateClock(delta_t_min); } // Synchronize oscillators. @@ -1385,11 +1384,11 @@ void SID::clock(cycle_count delta_t) { } // Clock filter. - filter.clock(delta_t, + filter.updateClock(delta_t, voice[0].output(), voice[1].output(), voice[2].output()); // Clock external filter. - extfilt.clock(delta_t, filter.output()); + extfilt.updateClock(delta_t, filter.output()); } @@ -1397,7 +1396,7 @@ void SID::clock(cycle_count delta_t) { * SID clocking with audio sampling. * Fixpoint arithmetics is used. */ -int SID::clock(cycle_count& delta_t, short* buf, int n, int interleave) { +int SID::updateClock(cycle_count& delta_t, short* buf, int n, int interleave) { int s = 0; for (;;) { @@ -1409,13 +1408,13 @@ int SID::clock(cycle_count& delta_t, short* buf, int n, int interleave) { if (s >= n) { return s; } - clock(delta_t_sample); + updateClock(delta_t_sample); delta_t -= delta_t_sample; sample_offset = (next_sample_offset & FIXP_MASK) - (1 << (FIXP_SHIFT - 1)); buf[s++*interleave] = output(); } - clock(delta_t); + updateClock(delta_t); sample_offset -= delta_t << FIXP_SHIFT; delta_t = 0; return s; diff --git a/audio/softsynth/sid.h b/audio/softsynth/sid.h index c78f538441..d6e402dc3b 100644 --- a/audio/softsynth/sid.h +++ b/audio/softsynth/sid.h @@ -60,7 +60,7 @@ public: void set_sync_source(WaveformGenerator*); - void clock(cycle_count delta_t); + void updateClock(cycle_count delta_t); void synchronize(); void reset(); @@ -133,7 +133,7 @@ public: void enable_filter(bool enable); - void clock(cycle_count delta_t, + void updateClock(cycle_count delta_t, sound_sample voice1, sound_sample voice2, sound_sample voice3); void reset(); @@ -201,7 +201,7 @@ public: enum State { ATTACK, DECAY_SUSTAIN, RELEASE }; - void clock(cycle_count delta_t); + void updateClock(cycle_count delta_t); void reset(); void writeCONTROL_REG(reg8); @@ -246,7 +246,7 @@ public: void enable_filter(bool enable); void set_sampling_parameter(double pass_freq); - void clock(cycle_count delta_t, sound_sample Vi); + void updateClock(cycle_count delta_t, sound_sample Vi); void reset(); // Audio output (20 bits). @@ -312,8 +312,8 @@ public: double sample_freq, double pass_freq = -1, double filter_scale = 0.97); - void clock(cycle_count delta_t); - int clock(cycle_count& delta_t, short* buf, int n, int interleave = 1); + void updateClock(cycle_count delta_t); + int updateClock(cycle_count& delta_t, short* buf, int n, int interleave = 1); void reset(); // Read/write registers. diff --git a/audio/softsynth/ym2612.cpp b/audio/softsynth/ym2612.cpp index 94fcfb97db..162c92f05a 100644 --- a/audio/softsynth/ym2612.cpp +++ b/audio/softsynth/ym2612.cpp @@ -27,7 +27,11 @@ #include "audio/softsynth/ym2612.h" #include "common/util.h" #include "audio/musicplugin.h" +#include "common/error.h" +#include "common/system.h" +#include "common/textconsole.h" #include "common/translation.h" +#include "common/types.h" //////////////////////////////////////// // @@ -677,14 +681,14 @@ void MidiDriver_YM2612::createLookupTables() { int i; sintbl = new int [2048]; for (i = 0; i < 2048; i++) - sintbl[i] = (int)(0xffff * sin(i/2048.0*2.0*PI)); + sintbl[i] = (int)(0xffff * sin(i/2048.0 * 2.0 * M_PI)); } { int i; powtbl = new int [1025]; for (i = 0; i <= 1024; i++) - powtbl[i] = (int)(0x10000 * pow(2.0, (i-512)/512.0)); + powtbl[i] = (int)(0x10000 * pow(2.0, (i - 512) / 512.0)); } { |