aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Sandulenko2011-12-26 00:52:52 -0800
committerEugene Sandulenko2011-12-26 00:52:52 -0800
commit70b988634096afe0698132a824febd88a6a6b911 (patch)
tree196f2464ab8e06369c0234232336330c93d9c72f
parent9751d6ded4067f6de5b70aa61d4391b64be9e04c (diff)
parent5d6710ebfd7b5bac79120492bbb3ff937f99efdb (diff)
downloadscummvm-rg350-70b988634096afe0698132a824febd88a6a6b911.tar.gz
scummvm-rg350-70b988634096afe0698132a824febd88a6a6b911.tar.bz2
scummvm-rg350-70b988634096afe0698132a824febd88a6a6b911.zip
Merge pull request #150 from tobigun/player_appleII
SCUMM: Add Apple-II sound player support
-rw-r--r--engines/scumm/module.mk1
-rw-r--r--engines/scumm/player_apple2.cpp500
-rw-r--r--engines/scumm/player_apple2.h298
-rw-r--r--engines/scumm/scumm.cpp3
4 files changed, 801 insertions, 1 deletions
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 781ca30459..1f219f5187 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -34,6 +34,7 @@ MODULE_OBJS := \
midiparser_ro.o \
object.o \
palette.o \
+ player_apple2.o \
player_mod.o \
player_nes.o \
player_pce.o \
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/scumm.cpp b/engines/scumm/scumm.cpp
index f94496b14b..9a093891d2 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -55,6 +55,7 @@
#include "scumm/player_nes.h"
#include "scumm/player_sid.h"
#include "scumm/player_pce.h"
+#include "scumm/player_apple2.h"
#include "scumm/player_v1.h"
#include "scumm/player_v2.h"
#include "scumm/player_v2cms.h"
@@ -1797,7 +1798,7 @@ void ScummEngine::setupMusic(int midi) {
if (_game.version >= 7) {
// Setup for digital iMuse is performed in another place
} else if (_game.platform == Common::kPlatformApple2GS && _game.version == 0){
- // TODO: Add support for music format
+ _musicEngine = new Player_AppleII(this, _mixer);
} else if (_game.platform == Common::kPlatformC64 && _game.version <= 1) {
#ifndef DISABLE_SID
_musicEngine = new Player_SID(this, _mixer);