diff options
-rw-r--r-- | saga/sound.cpp | 119 | ||||
-rw-r--r-- | saga/sound.h | 1 | ||||
-rw-r--r-- | sound/adpcm.cpp | 215 | ||||
-rw-r--r-- | sound/adpcm.h | 37 | ||||
-rw-r--r-- | sound/module.mk | 1 |
5 files changed, 263 insertions, 110 deletions
diff --git a/saga/sound.cpp b/saga/sound.cpp index 0a6a2bd061..9c2400fb65 100644 --- a/saga/sound.cpp +++ b/saga/sound.cpp @@ -26,119 +26,12 @@ #include "sound/audiostream.h" #include "sound/mixer.h" +#include "sound/adpcm.h" namespace Saga { -#define BUFFER_SIZE 4096 - -// Routines to convert 12 bit linear samples to the -// Dialogic or Oki ADPCM coding format. -class VOXInputStream : public AudioStream { -private: - const byte *_buf; - uint32 _pos; - uint32 _inputLen; - bool _evenPos; - - struct adpcmStatus { - int16 last; - int16 stepIndex; - } _status; - - int16 stepAdjust(byte); - int16 adpcmDecode(byte); - -public: - VOXInputStream(const byte *input, int inputLen); - ~VOXInputStream() {}; - - int readBuffer(int16 *buffer, const int numSamples); - - bool endOfData() const { return _pos >= _inputLen; } - bool isStereo() const { return false; } - int getRate() const { return 22050; } -}; - - -VOXInputStream::VOXInputStream(const byte *input, int inputLen) - : _buf(input), _pos(0), _inputLen(inputLen), _evenPos(true) { - - _status.last = 0; - _status.stepIndex = 0; -} - -int VOXInputStream::readBuffer(int16 *buffer, const int numSamples) { - int samples = 0; - - while (samples < numSamples && !endOfData()) { - const int len = MIN(numSamples - samples, (int) (_inputLen - _pos)); - - // * 16 effectively converts 12-bit input to 16-bit output - for (int i = 0; i < len; i++) { - if (_evenPos) - buffer[i] = adpcmDecode((_buf[_pos] >> 4) & 0x0f) * 16; - else { - buffer[i] = adpcmDecode(_buf[_pos] & 0x0f) * 16; - _pos++; - } - _evenPos = !_evenPos; - } - - samples += len; - } - return samples; -} - -// adjust the step for use on the next sample. -int16 VOXInputStream::stepAdjust(byte code) { - static int16 adjusts[] = {-1, -1, -1, -1, 2, 4, 6, 8}; - - return adjusts[code & 0x07]; -} - -static int16 stepSize[49] = { 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, - 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, - 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, - 724, 796, 876, 963, 1060, 1166, 1282, 1408, 1552 }; - -// Decode Linear to ADPCM -int16 VOXInputStream::adpcmDecode(byte code) { - int16 diff, E, SS, samp; - - SS = stepSize[_status.stepIndex]; - E = SS/8; - if (code & 0x01) - E += SS/4; - if (code & 0x02) - E += SS/2; - if (code & 0x04) - E += SS; - diff = (code & 0x08) ? -E : E; - samp = _status.last + diff; - - // Clip the values to +/- 2^11 (supposed to be 12 bits) - if(samp > 2048) - samp = 2048; - if(samp < -2048) - samp = -2048; - - _status.last = samp; - _status.stepIndex += stepAdjust(code); - if(_status.stepIndex < 0) - _status.stepIndex = 0; - if(_status.stepIndex > 48) - _status.stepIndex = 48; - - return samp; -} - -AudioStream *makeVOXStream(const byte *input, int size) { - AudioStream *audioStream = new VOXInputStream(input, size); - - return audioStream; -} Sound::Sound(SagaEngine *vm, SoundMixer *mixer, int enabled) : - _vm(vm), _mixer(mixer), _enabled(enabled) { + _vm(vm), _mixer(mixer), _enabled(enabled), _voxStream(0) { _soundInitialized = 1; return; @@ -149,6 +42,7 @@ Sound::~Sound() { return; } + delete _voxStream; _soundInitialized = 0; } @@ -220,7 +114,12 @@ int Sound::playVoice(SOUNDBUFFER *buf) { int Sound::playVoxVoice(SOUNDBUFFER *buf) { AudioStream *audioStream; - audioStream = makeVOXStream(buf->s_buf, buf->s_buf_len); + if (_voxStream) + delete _voxStream; + + _voxStream = new Common::MemoryReadStream(buf->s_buf, buf->s_buf_len); + + audioStream = makeADPCMStream(*_voxStream, kADPCMOki); _mixer->playInputStream(SoundMixer::kSFXSoundType, &_voiceHandle, audioStream); return SUCCESS; diff --git a/saga/sound.h b/saga/sound.h index a0b73d69e0..4418583acb 100644 --- a/saga/sound.h +++ b/saga/sound.h @@ -71,6 +71,7 @@ public: SagaEngine *_vm; SoundMixer *_mixer; + Common::MemoryReadStream *_voxStream; SoundHandle _effectHandle; SoundHandle _voiceHandle; diff --git a/sound/adpcm.cpp b/sound/adpcm.cpp new file mode 100644 index 0000000000..18f600ed23 --- /dev/null +++ b/sound/adpcm.cpp @@ -0,0 +1,215 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2005 The ScummVM project + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "stdafx.h" +#include "common/stream.h" + +#include "sound/audiostream.h" +#include "sound/adpcm.h" + + +// Routines to convert 12 bit linear samples to the +// Dialogic or Oki ADPCM coding format aka VOX +class ADPCMInputStream : public AudioStream { +private: + bool _evenPos; + Common::SeekableReadStream *_stream; + typesADPCM _type; + + struct adpcmStatus { + int16 last; + int16 stepIndex; + } _status; + + int16 stepAdjust(byte); + int16 okiADPCMDecode(byte); + int16 imaADPCMDecode(byte); + + int readOkiBuffer(int16 *buffer, const int numSamples); + int readIMABuffer(int16 *buffer, const int numSamples); + +public: + ADPCMInputStream(Common::SeekableReadStream *stream, typesADPCM type); + ~ADPCMInputStream() {}; + + int readBuffer(int16 *buffer, const int numSamples); + + bool endOfData() const { return _stream->eos(); } + bool isStereo() const { return false; } + int getRate() const { return 22050; } +}; + + +ADPCMInputStream::ADPCMInputStream(Common::SeekableReadStream *stream, typesADPCM type) + : _stream(stream), _evenPos(true), _type(type) { + + _status.last = 0; + _status.stepIndex = 0; +} + +int ADPCMInputStream::readBuffer(int16 *buffer, const int numSamples) { + switch (_type) { + case kADPCMOki: + return readOkiBuffer(buffer, numSamples); + break; + case kADPCMIma: + return readIMABuffer(buffer, numSamples); + break; + default: + error("Unsupported ADPCM encoding"); + break; + } + return 0; +} + +int ADPCMInputStream::readOkiBuffer(int16 *buffer, const int numSamples) { + int samples; + + for (samples = 0; samples < numSamples && !_stream->eos(); samples++) { + // * 16 effectively converts 12-bit input to 16-bit output + if (_evenPos) { + buffer[samples] = okiADPCMDecode((_stream->readByte() >> 4) & 0x0f) * 16; + // Rewind back so we will reget byte later + _stream->seek(-1, SEEK_CUR); + } else { + buffer[samples] = okiADPCMDecode(_stream->readByte() & 0x0f) * 16; + } + _evenPos = !_evenPos; + } + return samples; +} + +int ADPCMInputStream::readIMABuffer(int16 *buffer, const int numSamples) { + int samples; + + for (samples = 0; samples < numSamples && !_stream->eos(); samples++) { + if (_evenPos) { + buffer[samples] = imaADPCMDecode((_stream->readByte() >> 4) & 0x0f); + // Rewind back so we will reget byte later + _stream->seek(-1, SEEK_CUR); + } else { + buffer[samples] = imaADPCMDecode(_stream->readByte() & 0x0f); + } + _evenPos = !_evenPos; + } + return samples; +} + +// adjust the step for use on the next sample. +int16 ADPCMInputStream::stepAdjust(byte code) { + static const int16 adjusts[] = {-1, -1, -1, -1, 2, 4, 6, 8}; + + return adjusts[code & 0x07]; +} + +static const int16 okiStepSize[49] = { + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1408, + 1552 +}; + +// Decode Linear to ADPCM +int16 ADPCMInputStream::okiADPCMDecode(byte code) { + int16 diff, E, SS, samp; + + SS = okiStepSize[_status.stepIndex]; + E = SS/8; + if (code & 0x01) + E += SS/4; + if (code & 0x02) + E += SS/2; + if (code & 0x04) + E += SS; + diff = (code & 0x08) ? -E : E; + samp = _status.last + diff; + + // Clip the values to +/- 2^11 (supposed to be 12 bits) + if(samp > 2048) + samp = 2048; + if(samp < -2048) + samp = -2048; + + _status.last = samp; + _status.stepIndex += stepAdjust(code); + if(_status.stepIndex < 0) + _status.stepIndex = 0; + if(_status.stepIndex > 48) + _status.stepIndex = 48; + + return samp; +} + +AudioStream *makeADPCMStream(Common::SeekableReadStream &stream, typesADPCM type) { + AudioStream *audioStream = new ADPCMInputStream(&stream, type); + + return audioStream; +} + + +static const uint16 imaStepTable[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, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493,10442,11487,12635,13899, + 15289,16818,18500,20350,22385,24623,27086,29794, + 32767 +}; + + +int16 ADPCMInputStream::imaADPCMDecode(byte code) { + int diff, E, SS, samp; + + SS = imaStepTable[_status.stepIndex]; + + E = SS/8; + if (code & 0x01) + E += SS/4; + if (code & 0x02) + E += SS/2; + if (code & 0x04) + E += SS; + diff = (code & 0x08) ? -E : E; + samp = _status.last + diff; + + if(samp < -0x8000) + samp = -0x8000; + else if(samp > 0x7fff) + samp = 0x7fff; + + _status.last = samp; + _status.stepIndex += stepAdjust(code); + if(_status.stepIndex < 0) + _status.stepIndex = 0; + if(_status.stepIndex > 88) + _status.stepIndex = 88; + + return samp; +} diff --git a/sound/adpcm.h b/sound/adpcm.h new file mode 100644 index 0000000000..e5c51f36aa --- /dev/null +++ b/sound/adpcm.h @@ -0,0 +1,37 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2005 The ScummVM project + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef SOUND_ADPCM_H +#define SOUND_ADPCM_H + +#include "stdafx.h" +#include "common/scummsys.h" + +class AudioStream; + +enum typesADPCM { + kADPCMOki, + kADPCMIma +}; + +AudioStream *makeADPCMStream(Common::SeekableReadStream &stream, typesADPCM type); + +#endif diff --git a/sound/module.mk b/sound/module.mk index e57d145b55..5d0297bb11 100644 --- a/sound/module.mk +++ b/sound/module.mk @@ -1,6 +1,7 @@ MODULE := sound MODULE_OBJS := \ + sound/adpcm.o \ sound/audiocd.o \ sound/audiostream.o \ sound/flac.o \ |