From 854928f09a2d839c667848c2b18eb7e6b3dd6c22 Mon Sep 17 00:00:00 2001 From: Tobias Gunkel Date: Sat, 24 Dec 2011 14:29:06 +0100 Subject: SCUMM: Rename player_appleII.* -> player_apple2.* to have the filenames lower case --- engines/scumm/player_apple2.cpp | 500 +++++++++++++++++++++++++++++++++++++++ engines/scumm/player_apple2.h | 298 +++++++++++++++++++++++ engines/scumm/player_appleII.cpp | 500 --------------------------------------- engines/scumm/player_appleII.h | 298 ----------------------- engines/scumm/scumm.cpp | 2 +- 5 files changed, 799 insertions(+), 799 deletions(-) create mode 100644 engines/scumm/player_apple2.cpp create mode 100644 engines/scumm/player_apple2.h delete mode 100644 engines/scumm/player_appleII.cpp delete mode 100644 engines/scumm/player_appleII.h diff --git a/engines/scumm/player_apple2.cpp b/engines/scumm/player_apple2.cpp new file mode 100644 index 0000000000..a8e150caa9 --- /dev/null +++ b/engines/scumm/player_apple2.cpp @@ -0,0 +1,500 @@ +/* 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. + * + */ + +#include "engines/engine.h" +#include "scumm/player_apple2.h" +#include "scumm/scumm.h" + +namespace Scumm { + +/************************************ + * Apple-II sound-resource parsers + ************************************/ + +/* + * SoundFunction1: frequency up/down + */ +class AppleII_SoundFunction1_FreqUpDown : public AppleII_SoundFunction { +public: + virtual void init(Player_AppleII *player, const byte *params) { + _player = player; + _delta = params[0]; + _count = params[1]; + _interval = params[2]; + _limit = params[3]; + _decInterval = (params[4] >= 0x40); + } + + virtual bool update() { // D085 + if (_decInterval) { + do { + _update(_interval, _count); + _interval -= _delta; + } while (_interval >= _limit); + } else { + do { + _update(_interval, _count); + _interval += _delta; + } while (_interval < _limit); + } + return true; + } + +private: + void _update(int interval /*a*/, int count /*y*/) { // D076 + assert(interval > 0); // 0 == 256? + assert(count > 0); // 0 == 256? + + for (; count >= 0; --count) { + _player->speakerToggle(); + _player->generateSamples(17 + 5 * interval); + } + } + +protected: + int _delta; + int _count; + byte _interval; // must be unsigned byte ("interval < delta" possible) + int _limit; + bool _decInterval; +}; + +/* + * SoundFunction2: symmetric wave (~) + */ +class AppleII_SoundFunction2_SymmetricWave : public AppleII_SoundFunction { +public: + virtual void init(Player_AppleII *player, const byte *params) { + _player = player; + _params = params; + _pos = 1; + } + + virtual bool update() { // D0D6 + // while (pos = 1; pos < 256; ++pos) + if (_pos < 256) { + byte interval = _params[_pos]; + if (interval == 0xFF) + return true; + _update(interval, _params[0] /*, LD12F=interval*/); + + ++_pos; + return false; + } + return true; + } + +private: + void _update(int interval /*a*/, int count) { // D0EF + if (interval == 0xFE) { + _player->wait(interval, 10); + } else { + assert(count > 0); // 0 == 256? + assert(interval > 0); // 0 == 256? + + int a = (interval >> 3) + count; + for (int y = a; y > 0; --y) { + _player->generateSamples(1292 - 5*interval); + _player->speakerToggle(); + + _player->generateSamples(1287 - 5*interval); + _player->speakerToggle(); + } + } + } + +protected: + const byte *_params; + int _pos; +}; + +/* + * SoundFunction3: asymmetric wave (__-) + */ +class AppleII_SoundFunction3_AsymmetricWave : public AppleII_SoundFunction { +public: + virtual void init(Player_AppleII *player, const byte *params) { + _player = player; + _params = params; + _pos = 1; + } + + virtual bool update() { // D132 + // while (pos = 1; pos < 256; ++pos) + if (_pos < 256) { + byte interval = _params[_pos]; + if (interval == 0xFF) + return true; + _update(interval, _params[0]); + + ++_pos; + return false; + } + return true; + } + +private: + void _update(int interval /*a*/, int count /*LD12D*/) { // D14B + if (interval == 0xFE) { + _player->wait(interval, 70); + } else { + assert(interval > 0); // 0 == 256? + assert(count > 0); // 0 == 256? + + for (int y = count; y > 0; --y) { + _player->generateSamples(1289 - 5*interval); + _player->speakerToggle(); + } + } + } + +protected: + const byte *_params; + int _pos; +}; + +/* + * SoundFunction4: polyphone (2 voices) + */ +class AppleII_SoundFunction4_Polyphone : public AppleII_SoundFunction { +public: + virtual void init(Player_AppleII *player, const byte *params) { + _player = player; + _params = params; + _updateRemain1 = 80; + _updateRemain2 = 10; + _count = 0; + } + + virtual bool update() { // D170 + // while (_params[0] != 0x01) + if (_params[0] != 0x01) { + if (_count == 0) // prepare next loop + nextLoop(_params[0], _params[1], _params[2]); + if (loopIteration()) // loop finished -> fetch next parameter set + _params += 3; + return false; + } + return true; + } + +private: + /* + * prepare for next parameter set loop + */ + void nextLoop(byte param0, byte param1, byte param2) { // LD182 + _count = (-param2 << 8) | 0x3; + + _bitmask1 = 0x3; + _bitmask2 = 0x3; + + _updateInterval2 = param0; + if (_updateInterval2 == 0) + _bitmask2 = 0x0; + + _updateInterval1 = param1; + if (_updateInterval1 == 0) { + _bitmask1 = 0x0; + if (_bitmask2 != 0) { + _bitmask1 = _bitmask2; + _bitmask2 = 0; + _updateInterval1 = _updateInterval2; + } + } + + _speakerShiftReg = 0; + } + + /* + * perform one loop iteration + * Returns true if loop finished + */ + bool loopIteration() { // D1A2 + --_updateRemain1; + --_updateRemain2; + + if (_updateRemain2 == 0) { + _updateRemain2 = _updateInterval2; + // use only first voice's data (bitmask1) if both voices are triggered + if (_updateRemain1 != 0) { + _speakerShiftReg ^= _bitmask2; + } + } + + if (_updateRemain1 == 0) { + _updateRemain1 = _updateInterval1; + _speakerShiftReg ^= _bitmask1; + } + + if (_speakerShiftReg & 0x1) + _player->speakerToggle(); + _speakerShiftReg >>= 1; + _player->generateSamples(42); /* actually 42.5 */ + + ++_count; + return (_count == 0); + } + +protected: + const byte *_params; + + byte _updateRemain1; + byte _updateRemain2; + + uint16 _count; + byte _bitmask1; + byte _bitmask2; + byte _updateInterval1; + byte _updateInterval2; + byte _speakerShiftReg; +}; + +/* + * SoundFunction5: periodic noise + */ +class AppleII_SoundFunction5_Noise : public AppleII_SoundFunction { +public: + virtual void init(Player_AppleII *player, const byte *params) { + _player = player; + _index = 0; + _param0 = params[0]; + assert(_param0 > 0); + } + + virtual bool update() { // D222 + const byte noiseMask[] = { + 0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F + }; + + // while (i = 0; i < 10; ++i) + if (_index < 10) { + int count = _param0; + do { + _update(noise() & noiseMask[_index], 1); + --count; + } while (count > 0); + + ++_index; + return false; + } + + return true; + } + +private: + void _update(int interval /*a*/, int count) { // D270 + assert(count > 0); // 0 == 256? + if (interval == 0) + interval = 256; + + for (int i = count; i > 0; --i) { + _player->generateSamples(10 + 5*interval); + _player->speakerToggle(); + + _player->generateSamples(5 + 5*interval); + _player->speakerToggle(); + } + } + + byte /*a*/ noise() { // D261 + static int pos = 0; // initial value? + byte result = _noiseTable[pos]; + pos = (pos + 1) % 256; + return result; + } + +protected: + int _index; + int _param0; + +private: + static const byte _noiseTable[256]; +}; + +// LD000[loc] ^ LD00A[loc] +const byte AppleII_SoundFunction5_Noise::_noiseTable[256] = { + 0x65, 0x1b, 0xda, 0x11, 0x61, 0xe5, 0x77, 0x57, 0x92, 0xc8, 0x51, 0x1c, 0xd4, 0x91, 0x62, 0x63, + 0x00, 0x38, 0x57, 0xd5, 0x18, 0xd8, 0xdc, 0x40, 0x03, 0x86, 0xd3, 0x2f, 0x10, 0x11, 0xd8, 0x3c, + 0xbe, 0x00, 0x19, 0xc5, 0xd2, 0xc3, 0xca, 0x34, 0x00, 0x28, 0xbf, 0xb9, 0x18, 0x20, 0x01, 0xcc, + 0xda, 0x08, 0xbc, 0x75, 0x7c, 0xb0, 0x8d, 0xe0, 0x09, 0x18, 0xbf, 0x5d, 0xe9, 0x8c, 0x75, 0x64, + 0xe5, 0xb5, 0x5d, 0xe0, 0xb7, 0x7d, 0xe9, 0x8c, 0x55, 0x65, 0xc5, 0xb5, 0x5d, 0xd8, 0x09, 0x0d, + 0x64, 0xf0, 0xf0, 0x08, 0x63, 0x03, 0x00, 0x55, 0x35, 0xc0, 0x00, 0x20, 0x74, 0xa5, 0x1e, 0xe3, + 0x00, 0x06, 0x3c, 0x52, 0xd1, 0x70, 0xd0, 0x57, 0x02, 0xf0, 0x00, 0xb6, 0xfc, 0x02, 0x11, 0x9a, + 0x3b, 0xc8, 0x38, 0xdf, 0x1a, 0xb0, 0xd1, 0xb8, 0xd0, 0x18, 0x8a, 0x4a, 0xea, 0x1b, 0x12, 0x5d, + 0x29, 0x58, 0xd8, 0x43, 0xb8, 0x2d, 0xd2, 0x61, 0x10, 0x3c, 0x0c, 0x5d, 0x1b, 0x61, 0x10, 0x3c, + 0x0a, 0x5d, 0x1d, 0x61, 0x10, 0x3c, 0x0b, 0x19, 0x88, 0x21, 0xc0, 0x21, 0x07, 0x00, 0x65, 0x62, + 0x08, 0xe9, 0x36, 0x40, 0x20, 0x41, 0x06, 0x00, 0x20, 0x00, 0x00, 0xed, 0xa3, 0x00, 0x88, 0x06, + 0x98, 0x01, 0x5d, 0x7f, 0x02, 0x1d, 0x78, 0x03, 0x60, 0xcb, 0x3a, 0x01, 0xbd, 0x78, 0x02, 0x5d, + 0x7e, 0x03, 0x1d, 0xf5, 0xa6, 0x40, 0x81, 0xb4, 0xd0, 0x8d, 0xd3, 0xd0, 0x6d, 0xd5, 0x61, 0x48, + 0x61, 0x4d, 0xd1, 0xc8, 0xb1, 0xd8, 0x69, 0xff, 0x61, 0xd9, 0xed, 0xa0, 0xfe, 0x19, 0x91, 0x37, + 0x19, 0x37, 0x00, 0xf1, 0x00, 0x01, 0x1f, 0x00, 0xad, 0xc1, 0x01, 0x01, 0x2e, 0x00, 0x40, 0xc6, + 0x7a, 0x9b, 0x95, 0x43, 0xfc, 0x18, 0xd2, 0x9e, 0x2a, 0x5a, 0x4b, 0x2a, 0xb6, 0x87, 0x30, 0x6c +}; + +/************************************ + * Apple-II player + ************************************/ + +Player_AppleII::Player_AppleII(ScummEngine *scumm, Audio::Mixer *mixer) + : _mixer(mixer), _vm(scumm), _soundFunc(0) { + resetState(); + setSampleRate(_mixer->getOutputRate()); + _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); +} + +Player_AppleII::~Player_AppleII() { + _mixer->stopHandle(_soundHandle); + delete _soundFunc; +} + +void Player_AppleII::resetState() { + _soundNr = 0; + _type = 0; + _loop = 0; + _params = NULL; + _speakerState = 0; + delete _soundFunc; + _soundFunc = 0; + _sampleConverter.reset(); +} + +void Player_AppleII::startSound(int nr) { + Common::StackLock lock(_mutex); + + byte *data = _vm->getResourceAddress(rtSound, nr); + assert(data); + byte *ptr1 = data + 4; + + resetState(); + _soundNr = nr; + _type = ptr1[0]; + _loop = ptr1[1]; + _params = &ptr1[2]; + + switch (_type) { + case 0: // empty (nothing to play) + resetState(); + return; + case 1: + _soundFunc = new AppleII_SoundFunction1_FreqUpDown(); + break; + case 2: + _soundFunc = new AppleII_SoundFunction2_SymmetricWave(); + break; + case 3: + _soundFunc = new AppleII_SoundFunction3_AsymmetricWave(); + break; + case 4: + _soundFunc = new AppleII_SoundFunction4_Polyphone(); + break; + case 5: + _soundFunc = new AppleII_SoundFunction5_Noise(); + break; + } + _soundFunc->init(this, _params); + + assert(_loop > 0); + + debug(4, "startSound %d: type %d, loop %d", + nr, _type, _loop); +} + +bool Player_AppleII::updateSound() { + if (!_soundFunc) + return false; + + if (_soundFunc->update()) { + --_loop; + if (_loop <= 0) { + delete _soundFunc; + _soundFunc = 0; + } else { + // reset function state on each loop + _soundFunc->init(this, _params); + } + } + + return true; +} + +void Player_AppleII::stopAllSounds() { + Common::StackLock lock(_mutex); + resetState(); +} + +void Player_AppleII::stopSound(int nr) { + Common::StackLock lock(_mutex); + if (_soundNr == nr) { + resetState(); + } +} + +int Player_AppleII::getSoundStatus(int nr) const { + Common::StackLock lock(_mutex); + return (_soundNr == nr); +} + +int Player_AppleII::getMusicTimer() { + /* Apple-II sounds are synchronous -> no music timer */ + return 0; +} + +int Player_AppleII::readBuffer(int16 *buffer, const int numSamples) { + Common::StackLock lock(_mutex); + + if (!_soundNr) + return 0; + + int samplesLeft = numSamples; + do { + int nSamplesRead = _sampleConverter.readSamples(buffer, samplesLeft); + samplesLeft -= nSamplesRead; + buffer += nSamplesRead; + } while ((samplesLeft > 0) && updateSound()); + + // reset state if sound is played completely + if (!_soundFunc && (_sampleConverter.availableSize() == 0)) + resetState(); + + return numSamples - samplesLeft; +} + +/************************************ + * Apple-II sound-resource helpers + ************************************/ + +// toggle speaker on/off +void Player_AppleII::speakerToggle() { + _speakerState ^= 0x1; +} + +void Player_AppleII::generateSamples(int cycles) { + _sampleConverter.addCycles(_speakerState, cycles); +} + +void Player_AppleII::wait(int interval, int count /*y*/) { + assert(count > 0); // 0 == 256? + assert(interval > 0); // 0 == 256? + generateSamples(11 + count*(8 + 5 * interval)); +} + +} // End of namespace Scumm diff --git a/engines/scumm/player_apple2.h b/engines/scumm/player_apple2.h new file mode 100644 index 0000000000..9e97ab0c89 --- /dev/null +++ b/engines/scumm/player_apple2.h @@ -0,0 +1,298 @@ +/* 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. + * + */ + +#ifndef SCUMM_PLAYER_APPLEII_H +#define SCUMM_PLAYER_APPLEII_H + +#include "common/mutex.h" +#include "common/scummsys.h" +#include "common/memstream.h" +#include "scumm/music.h" +#include "audio/audiostream.h" +#include "audio/mixer.h" +#include "audio/softsynth/sid.h" + +namespace Scumm { + +class ScummEngine; + +/* + * Optimized for use with periodical read/write phases when the buffer + * is filled in a write phase and completely read in a read phase. + * The growing strategy is optimized for repeated small (e.g. 2 bytes) + * single writes resulting in large buffers + * (avg.: 4KB, max: 18KB @ 16bit/22.050kHz (MM sound21)). + */ +class SampleBuffer { +public: + SampleBuffer() : _data(0) { + clear(); + } + + ~SampleBuffer() { + free(_data); + } + + void clear() { + free(_data); + _data = 0; + _capacity = 0; + _writePos = 0; + _readPos = 0; + } + + void ensureFree(uint32 needed) { + // if data was read completely, reset read/write pos to front + if ((_writePos != 0) && (_writePos == _readPos)) { + _writePos = 0; + _readPos = 0; + } + + // check for enough space at end of buffer + uint32 freeEndCnt = _capacity - _writePos; + if (needed <= freeEndCnt) + return; + + uint32 avail = availableSize(); + + // check for enough space at beginning and end of buffer + if (needed <= _readPos + freeEndCnt) { + // move unread data to front of buffer + memmove(_data, _data + _readPos, avail); + _writePos = avail; + _readPos = 0; + } else { // needs a grow + byte *old_data = _data; + uint32 new_len = avail + needed; + + _capacity = new_len + 2048; + _data = (byte *)malloc(_capacity); + + if (old_data) { + // copy old unread data to front of new buffer + memcpy(_data, old_data + _readPos, avail); + free(old_data); + _writePos = avail; + _readPos = 0; + } + } + } + + uint32 availableSize() const { + if (_readPos >= _writePos) + return 0; + return _writePos - _readPos; + } + + virtual uint32 write(const void *dataPtr, uint32 dataSize) { + ensureFree(dataSize); + memcpy(_data + _writePos, dataPtr, dataSize); + _writePos += dataSize; + return dataSize; + } + + uint32 read(byte *dataPtr, uint32 dataSize) { + uint32 avail = availableSize(); + if (avail == 0) + return 0; + if (dataSize > avail) + dataSize = avail; + memcpy(dataPtr, _data + _readPos, dataSize); + _readPos += dataSize; + return dataSize; + } + +private: + uint32 _writePos; + uint32 _readPos; + uint32 _capacity; + byte *_data; +}; + +// CPU_CLOCK according to AppleWin +static const double APPLEII_CPU_CLOCK = 1020484.5; // ~ 1.02 MHz + +/* + * Converts the 1-bit speaker state values into audio samples. + * This is done by aggregation of the speaker states at each + * CPU cycle in a sampling period into an audio sample. + */ +class SampleConverter { +private: + void addSampleToBuffer(int sample) { + int16 value = sample * _volume / _maxVolume; + _buffer.write(&value, sizeof(value)); + } + +public: + SampleConverter() : + _cyclesPerSampleFP(0), + _missingCyclesFP(0), + _sampleCyclesSumFP(0), + _volume(_maxVolume) + {} + + ~SampleConverter() {} + + void reset() { + _missingCyclesFP = 0; + _sampleCyclesSumFP = 0; + _buffer.clear(); + } + + uint32 availableSize() const { + return _buffer.availableSize(); + } + + void setMusicVolume(int vol) { + assert(vol >= 0 && vol <= _maxVolume); + _volume = vol; + } + + void setSampleRate(int rate) { + /* ~46 CPU cycles per sample @ 22.05kHz */ + _cyclesPerSampleFP = APPLEII_CPU_CLOCK * (1 << PREC_SHIFT) / rate; + reset(); + } + + void addCycles(byte level, const int cycles) { + /* convert to fixed precision floats */ + int cyclesFP = cycles << PREC_SHIFT; + + // step 1: if cycles are left from the last call, process them first + if (_missingCyclesFP > 0) { + int n = (_missingCyclesFP < cyclesFP) ? _missingCyclesFP : cyclesFP; + if (level) + _sampleCyclesSumFP += n; + cyclesFP -= n; + _missingCyclesFP -= n; + if (_missingCyclesFP == 0) { + addSampleToBuffer(2*32767 * _sampleCyclesSumFP / _cyclesPerSampleFP - 32767); + } else { + return; + } + } + + _sampleCyclesSumFP = 0; + + // step 2: process blocks of cycles fitting into a whole sample + while (cyclesFP >= _cyclesPerSampleFP) { + addSampleToBuffer(level ? 32767 : -32767); + cyclesFP -= _cyclesPerSampleFP; + } + + // step 3: remember cycles left for next call + if (cyclesFP > 0) { + _missingCyclesFP = _cyclesPerSampleFP - cyclesFP; + if (level) + _sampleCyclesSumFP = cyclesFP; + } + } + + uint32 readSamples(void *buffer, int numSamples) { + return _buffer.read((byte*)buffer, numSamples * 2) / 2; + } + +private: + static const int PREC_SHIFT = 7; + +private: + int _cyclesPerSampleFP; /* (fixed precision) */ + int _missingCyclesFP; /* (fixed precision) */ + int _sampleCyclesSumFP; /* (fixed precision) */ + int _volume; /* 0 - 256 */ + static const int _maxVolume = 256; + SampleBuffer _buffer; +}; + +class Player_AppleII; + +class AppleII_SoundFunction { +public: + AppleII_SoundFunction() {} + virtual ~AppleII_SoundFunction() {} + virtual void init(Player_AppleII *player, const byte *params) = 0; + /* returns true if finished */ + virtual bool update() = 0; +protected: + Player_AppleII *_player; +}; + +class Player_AppleII : public Audio::AudioStream, public MusicEngine { +public: + Player_AppleII(ScummEngine *scumm, Audio::Mixer *mixer); + virtual ~Player_AppleII(); + + virtual void setMusicVolume(int vol) { _sampleConverter.setMusicVolume(vol); } + void setSampleRate(int rate) { + _sampleRate = rate; + _sampleConverter.setSampleRate(rate); + } + void startMusic(int songResIndex); + virtual void startSound(int sound); + virtual void stopSound(int sound); + virtual void stopAllSounds(); + virtual int getSoundStatus(int sound) const; + virtual int getMusicTimer(); + + // AudioStream API + int readBuffer(int16 *buffer, const int numSamples); + bool isStereo() const { return false; } + bool endOfData() const { return false; } + int getRate() const { return _sampleRate; } + +public: + void speakerToggle(); + void generateSamples(int cycles); + void wait(int interval, int count); + +private: + // sound number + int _soundNr; + // type of sound + int _type; + // number of loops left + int _loop; + // global sound param list + const byte *_params; + // speaker toggle state (0 / 1) + byte _speakerState; + // sound function + AppleII_SoundFunction *_soundFunc; + // cycle to sample converter + SampleConverter _sampleConverter; + +private: + ScummEngine *_vm; + Audio::Mixer *_mixer; + Audio::SoundHandle _soundHandle; + int _sampleRate; + Common::Mutex _mutex; + +private: + void resetState(); + bool updateSound(); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/player_appleII.cpp b/engines/scumm/player_appleII.cpp deleted file mode 100644 index f310e3e546..0000000000 --- a/engines/scumm/player_appleII.cpp +++ /dev/null @@ -1,500 +0,0 @@ -/* 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. - * - */ - -#include "engines/engine.h" -#include "scumm/player_appleII.h" -#include "scumm/scumm.h" - -namespace Scumm { - -/************************************ - * Apple-II sound-resource parsers - ************************************/ - -/* - * SoundFunction1: frequency up/down - */ -class AppleII_SoundFunction1_FreqUpDown : public AppleII_SoundFunction { -public: - virtual void init(Player_AppleII *player, const byte *params) { - _player = player; - _delta = params[0]; - _count = params[1]; - _interval = params[2]; - _limit = params[3]; - _decInterval = (params[4] >= 0x40); - } - - virtual bool update() { // D085 - if (_decInterval) { - do { - _update(_interval, _count); - _interval -= _delta; - } while (_interval >= _limit); - } else { - do { - _update(_interval, _count); - _interval += _delta; - } while (_interval < _limit); - } - return true; - } - -private: - void _update(int interval /*a*/, int count /*y*/) { // D076 - assert(interval > 0); // 0 == 256? - assert(count > 0); // 0 == 256? - - for (; count >= 0; --count) { - _player->speakerToggle(); - _player->generateSamples(17 + 5 * interval); - } - } - -protected: - int _delta; - int _count; - byte _interval; // must be unsigned byte ("interval < delta" possible) - int _limit; - bool _decInterval; -}; - -/* - * SoundFunction2: symmetric wave (~) - */ -class AppleII_SoundFunction2_SymmetricWave : public AppleII_SoundFunction { -public: - virtual void init(Player_AppleII *player, const byte *params) { - _player = player; - _params = params; - _pos = 1; - } - - virtual bool update() { // D0D6 - // while (pos = 1; pos < 256; ++pos) - if (_pos < 256) { - byte interval = _params[_pos]; - if (interval == 0xFF) - return true; - _update(interval, _params[0] /*, LD12F=interval*/); - - ++_pos; - return false; - } - return true; - } - -private: - void _update(int interval /*a*/, int count) { // D0EF - if (interval == 0xFE) { - _player->wait(interval, 10); - } else { - assert(count > 0); // 0 == 256? - assert(interval > 0); // 0 == 256? - - int a = (interval >> 3) + count; - for (int y = a; y > 0; --y) { - _player->generateSamples(1292 - 5*interval); - _player->speakerToggle(); - - _player->generateSamples(1287 - 5*interval); - _player->speakerToggle(); - } - } - } - -protected: - const byte *_params; - int _pos; -}; - -/* - * SoundFunction3: asymmetric wave (__-) - */ -class AppleII_SoundFunction3_AsymmetricWave : public AppleII_SoundFunction { -public: - virtual void init(Player_AppleII *player, const byte *params) { - _player = player; - _params = params; - _pos = 1; - } - - virtual bool update() { // D132 - // while (pos = 1; pos < 256; ++pos) - if (_pos < 256) { - byte interval = _params[_pos]; - if (interval == 0xFF) - return true; - _update(interval, _params[0]); - - ++_pos; - return false; - } - return true; - } - -private: - void _update(int interval /*a*/, int count /*LD12D*/) { // D14B - if (interval == 0xFE) { - _player->wait(interval, 70); - } else { - assert(interval > 0); // 0 == 256? - assert(count > 0); // 0 == 256? - - for (int y = count; y > 0; --y) { - _player->generateSamples(1289 - 5*interval); - _player->speakerToggle(); - } - } - } - -protected: - const byte *_params; - int _pos; -}; - -/* - * SoundFunction4: polyphone (2 voices) - */ -class AppleII_SoundFunction4_Polyphone : public AppleII_SoundFunction { -public: - virtual void init(Player_AppleII *player, const byte *params) { - _player = player; - _params = params; - _updateRemain1 = 80; - _updateRemain2 = 10; - _count = 0; - } - - virtual bool update() { // D170 - // while (_params[0] != 0x01) - if (_params[0] != 0x01) { - if (_count == 0) // prepare next loop - nextLoop(_params[0], _params[1], _params[2]); - if (loopIteration()) // loop finished -> fetch next parameter set - _params += 3; - return false; - } - return true; - } - -private: - /* - * prepare for next parameter set loop - */ - void nextLoop(byte param0, byte param1, byte param2) { // LD182 - _count = (-param2 << 8) | 0x3; - - _bitmask1 = 0x3; - _bitmask2 = 0x3; - - _updateInterval2 = param0; - if (_updateInterval2 == 0) - _bitmask2 = 0x0; - - _updateInterval1 = param1; - if (_updateInterval1 == 0) { - _bitmask1 = 0x0; - if (_bitmask2 != 0) { - _bitmask1 = _bitmask2; - _bitmask2 = 0; - _updateInterval1 = _updateInterval2; - } - } - - _speakerShiftReg = 0; - } - - /* - * perform one loop iteration - * Returns true if loop finished - */ - bool loopIteration() { // D1A2 - --_updateRemain1; - --_updateRemain2; - - if (_updateRemain2 == 0) { - _updateRemain2 = _updateInterval2; - // use only first voice's data (bitmask1) if both voices are triggered - if (_updateRemain1 != 0) { - _speakerShiftReg ^= _bitmask2; - } - } - - if (_updateRemain1 == 0) { - _updateRemain1 = _updateInterval1; - _speakerShiftReg ^= _bitmask1; - } - - if (_speakerShiftReg & 0x1) - _player->speakerToggle(); - _speakerShiftReg >>= 1; - _player->generateSamples(42); /* actually 42.5 */ - - ++_count; - return (_count == 0); - } - -protected: - const byte *_params; - - byte _updateRemain1; - byte _updateRemain2; - - uint16 _count; - byte _bitmask1; - byte _bitmask2; - byte _updateInterval1; - byte _updateInterval2; - byte _speakerShiftReg; -}; - -/* - * SoundFunction5: periodic noise - */ -class AppleII_SoundFunction5_Noise : public AppleII_SoundFunction { -public: - virtual void init(Player_AppleII *player, const byte *params) { - _player = player; - _index = 0; - _param0 = params[0]; - assert(_param0 > 0); - } - - virtual bool update() { // D222 - const byte noiseMask[] = { - 0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F - }; - - // while (i = 0; i < 10; ++i) - if (_index < 10) { - int count = _param0; - do { - _update(noise() & noiseMask[_index], 1); - --count; - } while (count > 0); - - ++_index; - return false; - } - - return true; - } - -private: - void _update(int interval /*a*/, int count) { // D270 - assert(count > 0); // 0 == 256? - if (interval == 0) - interval = 256; - - for (int i = count; i > 0; --i) { - _player->generateSamples(10 + 5*interval); - _player->speakerToggle(); - - _player->generateSamples(5 + 5*interval); - _player->speakerToggle(); - } - } - - byte /*a*/ noise() { // D261 - static int pos = 0; // initial value? - byte result = _noiseTable[pos]; - pos = (pos + 1) % 256; - return result; - } - -protected: - int _index; - int _param0; - -private: - static const byte _noiseTable[256]; -}; - -// LD000[loc] ^ LD00A[loc] -const byte AppleII_SoundFunction5_Noise::_noiseTable[256] = { - 0x65, 0x1b, 0xda, 0x11, 0x61, 0xe5, 0x77, 0x57, 0x92, 0xc8, 0x51, 0x1c, 0xd4, 0x91, 0x62, 0x63, - 0x00, 0x38, 0x57, 0xd5, 0x18, 0xd8, 0xdc, 0x40, 0x03, 0x86, 0xd3, 0x2f, 0x10, 0x11, 0xd8, 0x3c, - 0xbe, 0x00, 0x19, 0xc5, 0xd2, 0xc3, 0xca, 0x34, 0x00, 0x28, 0xbf, 0xb9, 0x18, 0x20, 0x01, 0xcc, - 0xda, 0x08, 0xbc, 0x75, 0x7c, 0xb0, 0x8d, 0xe0, 0x09, 0x18, 0xbf, 0x5d, 0xe9, 0x8c, 0x75, 0x64, - 0xe5, 0xb5, 0x5d, 0xe0, 0xb7, 0x7d, 0xe9, 0x8c, 0x55, 0x65, 0xc5, 0xb5, 0x5d, 0xd8, 0x09, 0x0d, - 0x64, 0xf0, 0xf0, 0x08, 0x63, 0x03, 0x00, 0x55, 0x35, 0xc0, 0x00, 0x20, 0x74, 0xa5, 0x1e, 0xe3, - 0x00, 0x06, 0x3c, 0x52, 0xd1, 0x70, 0xd0, 0x57, 0x02, 0xf0, 0x00, 0xb6, 0xfc, 0x02, 0x11, 0x9a, - 0x3b, 0xc8, 0x38, 0xdf, 0x1a, 0xb0, 0xd1, 0xb8, 0xd0, 0x18, 0x8a, 0x4a, 0xea, 0x1b, 0x12, 0x5d, - 0x29, 0x58, 0xd8, 0x43, 0xb8, 0x2d, 0xd2, 0x61, 0x10, 0x3c, 0x0c, 0x5d, 0x1b, 0x61, 0x10, 0x3c, - 0x0a, 0x5d, 0x1d, 0x61, 0x10, 0x3c, 0x0b, 0x19, 0x88, 0x21, 0xc0, 0x21, 0x07, 0x00, 0x65, 0x62, - 0x08, 0xe9, 0x36, 0x40, 0x20, 0x41, 0x06, 0x00, 0x20, 0x00, 0x00, 0xed, 0xa3, 0x00, 0x88, 0x06, - 0x98, 0x01, 0x5d, 0x7f, 0x02, 0x1d, 0x78, 0x03, 0x60, 0xcb, 0x3a, 0x01, 0xbd, 0x78, 0x02, 0x5d, - 0x7e, 0x03, 0x1d, 0xf5, 0xa6, 0x40, 0x81, 0xb4, 0xd0, 0x8d, 0xd3, 0xd0, 0x6d, 0xd5, 0x61, 0x48, - 0x61, 0x4d, 0xd1, 0xc8, 0xb1, 0xd8, 0x69, 0xff, 0x61, 0xd9, 0xed, 0xa0, 0xfe, 0x19, 0x91, 0x37, - 0x19, 0x37, 0x00, 0xf1, 0x00, 0x01, 0x1f, 0x00, 0xad, 0xc1, 0x01, 0x01, 0x2e, 0x00, 0x40, 0xc6, - 0x7a, 0x9b, 0x95, 0x43, 0xfc, 0x18, 0xd2, 0x9e, 0x2a, 0x5a, 0x4b, 0x2a, 0xb6, 0x87, 0x30, 0x6c -}; - -/************************************ - * Apple-II player - ************************************/ - -Player_AppleII::Player_AppleII(ScummEngine *scumm, Audio::Mixer *mixer) - : _mixer(mixer), _vm(scumm), _soundFunc(0) { - resetState(); - setSampleRate(_mixer->getOutputRate()); - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); -} - -Player_AppleII::~Player_AppleII() { - _mixer->stopHandle(_soundHandle); - delete _soundFunc; -} - -void Player_AppleII::resetState() { - _soundNr = 0; - _type = 0; - _loop = 0; - _params = NULL; - _speakerState = 0; - delete _soundFunc; - _soundFunc = 0; - _sampleConverter.reset(); -} - -void Player_AppleII::startSound(int nr) { - Common::StackLock lock(_mutex); - - byte *data = _vm->getResourceAddress(rtSound, nr); - assert(data); - byte *ptr1 = data + 4; - - resetState(); - _soundNr = nr; - _type = ptr1[0]; - _loop = ptr1[1]; - _params = &ptr1[2]; - - switch (_type) { - case 0: // empty (nothing to play) - resetState(); - return; - case 1: - _soundFunc = new AppleII_SoundFunction1_FreqUpDown(); - break; - case 2: - _soundFunc = new AppleII_SoundFunction2_SymmetricWave(); - break; - case 3: - _soundFunc = new AppleII_SoundFunction3_AsymmetricWave(); - break; - case 4: - _soundFunc = new AppleII_SoundFunction4_Polyphone(); - break; - case 5: - _soundFunc = new AppleII_SoundFunction5_Noise(); - break; - } - _soundFunc->init(this, _params); - - assert(_loop > 0); - - debug(4, "startSound %d: type %d, loop %d", - nr, _type, _loop); -} - -bool Player_AppleII::updateSound() { - if (!_soundFunc) - return false; - - if (_soundFunc->update()) { - --_loop; - if (_loop <= 0) { - delete _soundFunc; - _soundFunc = 0; - } else { - // reset function state on each loop - _soundFunc->init(this, _params); - } - } - - return true; -} - -void Player_AppleII::stopAllSounds() { - Common::StackLock lock(_mutex); - resetState(); -} - -void Player_AppleII::stopSound(int nr) { - Common::StackLock lock(_mutex); - if (_soundNr == nr) { - resetState(); - } -} - -int Player_AppleII::getSoundStatus(int nr) const { - Common::StackLock lock(_mutex); - return (_soundNr == nr); -} - -int Player_AppleII::getMusicTimer() { - /* Apple-II sounds are synchronous -> no music timer */ - return 0; -} - -int Player_AppleII::readBuffer(int16 *buffer, const int numSamples) { - Common::StackLock lock(_mutex); - - if (!_soundNr) - return 0; - - int samplesLeft = numSamples; - do { - int nSamplesRead = _sampleConverter.readSamples(buffer, samplesLeft); - samplesLeft -= nSamplesRead; - buffer += nSamplesRead; - } while ((samplesLeft > 0) && updateSound()); - - // reset state if sound is played completely - if (!_soundFunc && (_sampleConverter.availableSize() == 0)) - resetState(); - - return numSamples - samplesLeft; -} - -/************************************ - * Apple-II sound-resource helpers - ************************************/ - -// toggle speaker on/off -void Player_AppleII::speakerToggle() { - _speakerState ^= 0x1; -} - -void Player_AppleII::generateSamples(int cycles) { - _sampleConverter.addCycles(_speakerState, cycles); -} - -void Player_AppleII::wait(int interval, int count /*y*/) { - assert(count > 0); // 0 == 256? - assert(interval > 0); // 0 == 256? - generateSamples(11 + count*(8 + 5 * interval)); -} - -} // End of namespace Scumm diff --git a/engines/scumm/player_appleII.h b/engines/scumm/player_appleII.h deleted file mode 100644 index 9e97ab0c89..0000000000 --- a/engines/scumm/player_appleII.h +++ /dev/null @@ -1,298 +0,0 @@ -/* 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. - * - */ - -#ifndef SCUMM_PLAYER_APPLEII_H -#define SCUMM_PLAYER_APPLEII_H - -#include "common/mutex.h" -#include "common/scummsys.h" -#include "common/memstream.h" -#include "scumm/music.h" -#include "audio/audiostream.h" -#include "audio/mixer.h" -#include "audio/softsynth/sid.h" - -namespace Scumm { - -class ScummEngine; - -/* - * Optimized for use with periodical read/write phases when the buffer - * is filled in a write phase and completely read in a read phase. - * The growing strategy is optimized for repeated small (e.g. 2 bytes) - * single writes resulting in large buffers - * (avg.: 4KB, max: 18KB @ 16bit/22.050kHz (MM sound21)). - */ -class SampleBuffer { -public: - SampleBuffer() : _data(0) { - clear(); - } - - ~SampleBuffer() { - free(_data); - } - - void clear() { - free(_data); - _data = 0; - _capacity = 0; - _writePos = 0; - _readPos = 0; - } - - void ensureFree(uint32 needed) { - // if data was read completely, reset read/write pos to front - if ((_writePos != 0) && (_writePos == _readPos)) { - _writePos = 0; - _readPos = 0; - } - - // check for enough space at end of buffer - uint32 freeEndCnt = _capacity - _writePos; - if (needed <= freeEndCnt) - return; - - uint32 avail = availableSize(); - - // check for enough space at beginning and end of buffer - if (needed <= _readPos + freeEndCnt) { - // move unread data to front of buffer - memmove(_data, _data + _readPos, avail); - _writePos = avail; - _readPos = 0; - } else { // needs a grow - byte *old_data = _data; - uint32 new_len = avail + needed; - - _capacity = new_len + 2048; - _data = (byte *)malloc(_capacity); - - if (old_data) { - // copy old unread data to front of new buffer - memcpy(_data, old_data + _readPos, avail); - free(old_data); - _writePos = avail; - _readPos = 0; - } - } - } - - uint32 availableSize() const { - if (_readPos >= _writePos) - return 0; - return _writePos - _readPos; - } - - virtual uint32 write(const void *dataPtr, uint32 dataSize) { - ensureFree(dataSize); - memcpy(_data + _writePos, dataPtr, dataSize); - _writePos += dataSize; - return dataSize; - } - - uint32 read(byte *dataPtr, uint32 dataSize) { - uint32 avail = availableSize(); - if (avail == 0) - return 0; - if (dataSize > avail) - dataSize = avail; - memcpy(dataPtr, _data + _readPos, dataSize); - _readPos += dataSize; - return dataSize; - } - -private: - uint32 _writePos; - uint32 _readPos; - uint32 _capacity; - byte *_data; -}; - -// CPU_CLOCK according to AppleWin -static const double APPLEII_CPU_CLOCK = 1020484.5; // ~ 1.02 MHz - -/* - * Converts the 1-bit speaker state values into audio samples. - * This is done by aggregation of the speaker states at each - * CPU cycle in a sampling period into an audio sample. - */ -class SampleConverter { -private: - void addSampleToBuffer(int sample) { - int16 value = sample * _volume / _maxVolume; - _buffer.write(&value, sizeof(value)); - } - -public: - SampleConverter() : - _cyclesPerSampleFP(0), - _missingCyclesFP(0), - _sampleCyclesSumFP(0), - _volume(_maxVolume) - {} - - ~SampleConverter() {} - - void reset() { - _missingCyclesFP = 0; - _sampleCyclesSumFP = 0; - _buffer.clear(); - } - - uint32 availableSize() const { - return _buffer.availableSize(); - } - - void setMusicVolume(int vol) { - assert(vol >= 0 && vol <= _maxVolume); - _volume = vol; - } - - void setSampleRate(int rate) { - /* ~46 CPU cycles per sample @ 22.05kHz */ - _cyclesPerSampleFP = APPLEII_CPU_CLOCK * (1 << PREC_SHIFT) / rate; - reset(); - } - - void addCycles(byte level, const int cycles) { - /* convert to fixed precision floats */ - int cyclesFP = cycles << PREC_SHIFT; - - // step 1: if cycles are left from the last call, process them first - if (_missingCyclesFP > 0) { - int n = (_missingCyclesFP < cyclesFP) ? _missingCyclesFP : cyclesFP; - if (level) - _sampleCyclesSumFP += n; - cyclesFP -= n; - _missingCyclesFP -= n; - if (_missingCyclesFP == 0) { - addSampleToBuffer(2*32767 * _sampleCyclesSumFP / _cyclesPerSampleFP - 32767); - } else { - return; - } - } - - _sampleCyclesSumFP = 0; - - // step 2: process blocks of cycles fitting into a whole sample - while (cyclesFP >= _cyclesPerSampleFP) { - addSampleToBuffer(level ? 32767 : -32767); - cyclesFP -= _cyclesPerSampleFP; - } - - // step 3: remember cycles left for next call - if (cyclesFP > 0) { - _missingCyclesFP = _cyclesPerSampleFP - cyclesFP; - if (level) - _sampleCyclesSumFP = cyclesFP; - } - } - - uint32 readSamples(void *buffer, int numSamples) { - return _buffer.read((byte*)buffer, numSamples * 2) / 2; - } - -private: - static const int PREC_SHIFT = 7; - -private: - int _cyclesPerSampleFP; /* (fixed precision) */ - int _missingCyclesFP; /* (fixed precision) */ - int _sampleCyclesSumFP; /* (fixed precision) */ - int _volume; /* 0 - 256 */ - static const int _maxVolume = 256; - SampleBuffer _buffer; -}; - -class Player_AppleII; - -class AppleII_SoundFunction { -public: - AppleII_SoundFunction() {} - virtual ~AppleII_SoundFunction() {} - virtual void init(Player_AppleII *player, const byte *params) = 0; - /* returns true if finished */ - virtual bool update() = 0; -protected: - Player_AppleII *_player; -}; - -class Player_AppleII : public Audio::AudioStream, public MusicEngine { -public: - Player_AppleII(ScummEngine *scumm, Audio::Mixer *mixer); - virtual ~Player_AppleII(); - - virtual void setMusicVolume(int vol) { _sampleConverter.setMusicVolume(vol); } - void setSampleRate(int rate) { - _sampleRate = rate; - _sampleConverter.setSampleRate(rate); - } - void startMusic(int songResIndex); - virtual void startSound(int sound); - virtual void stopSound(int sound); - virtual void stopAllSounds(); - virtual int getSoundStatus(int sound) const; - virtual int getMusicTimer(); - - // AudioStream API - int readBuffer(int16 *buffer, const int numSamples); - bool isStereo() const { return false; } - bool endOfData() const { return false; } - int getRate() const { return _sampleRate; } - -public: - void speakerToggle(); - void generateSamples(int cycles); - void wait(int interval, int count); - -private: - // sound number - int _soundNr; - // type of sound - int _type; - // number of loops left - int _loop; - // global sound param list - const byte *_params; - // speaker toggle state (0 / 1) - byte _speakerState; - // sound function - AppleII_SoundFunction *_soundFunc; - // cycle to sample converter - SampleConverter _sampleConverter; - -private: - ScummEngine *_vm; - Audio::Mixer *_mixer; - Audio::SoundHandle _soundHandle; - int _sampleRate; - Common::Mutex _mutex; - -private: - void resetState(); - bool updateSound(); -}; - -} // End of namespace Scumm - -#endif diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 3eea68fbbe..9a093891d2 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -55,7 +55,7 @@ #include "scumm/player_nes.h" #include "scumm/player_sid.h" #include "scumm/player_pce.h" -#include "scumm/player_appleII.h" +#include "scumm/player_apple2.h" #include "scumm/player_v1.h" #include "scumm/player_v2.h" #include "scumm/player_v2cms.h" -- cgit v1.2.3