aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm
diff options
context:
space:
mode:
authorTravis Howell2009-11-26 00:31:19 +0000
committerTravis Howell2009-11-26 00:31:19 +0000
commit8783b0f2f7dbfb44c977812244a4ab4cde47a796 (patch)
treee1d210c4759d432a43d7805f20423257fb76a764 /engines/scumm
parent4e830d821cadbf1e29d2fe212a33e23770a890f8 (diff)
downloadscummvm-rg350-8783b0f2f7dbfb44c977812244a4ab4cde47a796.tar.gz
scummvm-rg350-8783b0f2f7dbfb44c977812244a4ab4cde47a796.tar.bz2
scummvm-rg350-8783b0f2f7dbfb44c977812244a4ab4cde47a796.zip
Add patch #2839048 - MM C64 sound/music player, with minor changes.
svn-id: r46143
Diffstat (limited to 'engines/scumm')
-rw-r--r--engines/scumm/imuse/imuse.cpp2
-rw-r--r--engines/scumm/imuse/imuse_internal.h2
-rw-r--r--engines/scumm/module.mk1
-rw-r--r--engines/scumm/music.h2
-rw-r--r--engines/scumm/player_pce.cpp2
-rw-r--r--engines/scumm/player_pce.h2
-rw-r--r--engines/scumm/player_sid.cpp1394
-rw-r--r--engines/scumm/player_sid.h280
-rw-r--r--engines/scumm/player_v1.cpp2
-rw-r--r--engines/scumm/player_v1.h2
-rw-r--r--engines/scumm/player_v2.cpp2
-rw-r--r--engines/scumm/player_v2.h4
-rw-r--r--engines/scumm/player_v2a.cpp2
-rw-r--r--engines/scumm/player_v2a.h2
-rw-r--r--engines/scumm/player_v2cms.cpp2
-rw-r--r--engines/scumm/player_v3a.cpp2
-rw-r--r--engines/scumm/player_v3a.h2
-rw-r--r--engines/scumm/player_v4a.cpp2
-rw-r--r--engines/scumm/player_v4a.h2
-rw-r--r--engines/scumm/scumm.cpp3
20 files changed, 1694 insertions, 18 deletions
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index 5b00a973f9..80b08f7ad4 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -537,7 +537,7 @@ int IMuseInternal::getSoundStatus(int sound) const {
return getSoundStatus_internal (sound, true);
}
-int IMuseInternal::getMusicTimer() const {
+int IMuseInternal::getMusicTimer() {
Common::StackLock lock(_mutex, "IMuseInternal::getMusicTimer()");
int best_time = 0;
const Player *player = _players;
diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h
index e3d2b9b2a4..93244b8c1d 100644
--- a/engines/scumm/imuse/imuse_internal.h
+++ b/engines/scumm/imuse/imuse_internal.h
@@ -535,7 +535,7 @@ public:
void stopSound(int sound);
void stopAllSounds();
int getSoundStatus(int sound) const;
- int getMusicTimer() const;
+ int getMusicTimer();
public:
// Factory function
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 1f2a68a4d6..e5f0745dd6 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -36,6 +36,7 @@ MODULE_OBJS := \
player_mod.o \
player_nes.o \
player_pce.o \
+ player_sid.o \
player_v1.o \
player_v2.o \
player_v2a.o \
diff --git a/engines/scumm/music.h b/engines/scumm/music.h
index 2fd7c50bce..6285412ffd 100644
--- a/engines/scumm/music.h
+++ b/engines/scumm/music.h
@@ -80,7 +80,7 @@ public:
* the music/sound.
* @return the music timer
*/
- virtual int getMusicTimer() const { return 0; }
+ virtual int getMusicTimer() { return 0; }
};
} // End of namespace Scumm
diff --git a/engines/scumm/player_pce.cpp b/engines/scumm/player_pce.cpp
index eab695e0c7..f4d44fea22 100644
--- a/engines/scumm/player_pce.cpp
+++ b/engines/scumm/player_pce.cpp
@@ -751,7 +751,7 @@ int Player_PCE::getSoundStatus(int nr) const {
return 0;
}
-int Player_PCE::getMusicTimer() const {
+int Player_PCE::getMusicTimer() {
return 0;
}
diff --git a/engines/scumm/player_pce.h b/engines/scumm/player_pce.h
index 0848d96db4..37a70b123b 100644
--- a/engines/scumm/player_pce.h
+++ b/engines/scumm/player_pce.h
@@ -83,7 +83,7 @@ public:
virtual void stopSound(int sound);
virtual void stopAllSounds();
virtual int getSoundStatus(int sound) const;
- virtual int getMusicTimer() const;
+ virtual int getMusicTimer();
// AudioStream API
int readBuffer(int16 *buffer, const int numSamples);
diff --git a/engines/scumm/player_sid.cpp b/engines/scumm/player_sid.cpp
new file mode 100644
index 0000000000..2a0a399198
--- /dev/null
+++ b/engines/scumm/player_sid.cpp
@@ -0,0 +1,1394 @@
+/* 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 <algorithm>
+#include "engines/engine.h"
+#include "scumm/player_sid.h"
+#include "scumm/scumm.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+/*
+ * The player's update() routine is called once per (NTSC/PAL) frame as it is
+ * called by the VIC Rasterline interrupt handler which is in turn called
+ * approx. 50 (PAL) or 60 (NTSC) times per second.
+ * The SCUMM V0/V1 music playback routines or sound data have not been adjusted
+ * to PAL systems. As a consequence, music is played audibly (-16%) slower
+ * on PAL systems.
+ * In addition, the SID oscillator frequency depends on the video clock too.
+ * As SCUMM games use an NTSC frequency table for both NTSC and PAL versions
+ * all tone frequencies on PAL systems are slightly (-4%) lower than on NTSC ones.
+ *
+ * For more info on the SID chip see:
+ * - http://www.dopeconnection.net/C64_SID.htm (German)
+ * For more info on the VIC chip see:
+ * - http://www.htu.tugraz.at/~herwig/c64/man-vic.php (German)
+ * - http://www.c64-wiki.de/index.php/VIC (German)
+ */
+
+struct TimingProps {
+ double clockFreq;
+ int cyclesPerFrame;
+};
+
+static const TimingProps timingProps[2] = {
+ { 17734472.0 / 18, 312 * 63 }, // PAL: 312*63 cycles/frame @ 985248 Hz (~50Hz)
+ { 14318180.0 / 14, 263 * 65 } // NTSC: 263*65 cycles/frame @ 1022727 Hz (~60Hz)
+};
+
+static const uint8 BITMASK[7] = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40
+};
+static const uint8 BITMASK_INV[7] = {
+ 0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF
+};
+
+static const int SID_REG_OFFSET[7] = {
+ 0, 7, 14, 21, 2, 9, 16
+};
+
+// NTSC frequency table (also used for PAL versions).
+// FREQ_TBL[i] = tone_freq[i] * 2^24 / clockFreq
+static const uint16 FREQ_TBL[97] = {
+ 0x0000, 0x010C, 0x011C, 0x012D, 0x013E, 0x0151, 0x0166, 0x017B,
+ 0x0191, 0x01A9, 0x01C3, 0x01DD, 0x01FA, 0x0218, 0x0238, 0x025A,
+ 0x027D, 0x02A3, 0x02CC, 0x02F6, 0x0323, 0x0353, 0x0386, 0x03BB,
+ 0x03F4, 0x0430, 0x0470, 0x04B4, 0x04FB, 0x0547, 0x0598, 0x05ED,
+ 0x0647, 0x06A7, 0x070C, 0x0777, 0x07E9, 0x0861, 0x08E1, 0x0968,
+ 0x09F7, 0x0A8F, 0x0B30, 0x0BDA, 0x0C8F, 0x0D4E, 0x0E18, 0x0EEF,
+ 0x0FD2, 0x10C3, 0x11C3, 0x12D1, 0x13EF, 0x151F, 0x1660, 0x17B5,
+ 0x191E, 0x1A9C, 0x1C31, 0x1DDF, 0x1FA5, 0x2187, 0x2386, 0x25A2,
+ 0x27DF, 0x2A3E, 0x2CC1, 0x2F6B, 0x323C, 0x3539, 0x3863, 0x3BBE,
+ 0x3F4B, 0x430F, 0x470C, 0x4B45, 0x4FBF, 0x547D, 0x5983, 0x5ED6,
+ 0x6479, 0x6A73, 0x70C7, 0x777C, 0x7E97, 0x861E, 0x8E18, 0x968B,
+ 0x9F7E, 0xA8FA, 0xB306, 0xBDAC, 0xC8F3, 0xD4E6, 0xE18F, 0xEEF8,
+ 0xFD2E
+};
+
+static const int SONG_CHANNEL_OFFSET[3] = { 6, 8, 10 };
+static const int RES_ID_CHANNEL[3] = { 3, 4, 5 };
+
+#define LOBYTE(a) ((a) & 0xFF)
+#define HIBYTE(a) (((a) >> 8) & 0xFF)
+
+#define GETBIT(var, pos) ((var) & (1<<(pos)))
+
+void Player_SID::handleMusicBuffer() { // $33cd
+ int channel = 2;
+ while (channel >= 0) {
+ if ((statusBits1A & BITMASK[channel]) == 0 ||
+ (busyChannelBits & BITMASK[channel]) != 0)
+ {
+ --channel;
+ continue;
+ }
+
+ if (setupSongFileData() == 1)
+ return;
+
+ uint8* l_chanFileDataPtr = chanFileData[channel];
+
+ uint16 l_freq = 0;
+ bool l_keepFreq = false;
+
+ int y = 0;
+ uint8 curByte = l_chanFileDataPtr[y++];
+
+ // freq or 0/0xFF
+ if (curByte == 0) {
+ func_3674(channel);
+ if (!isMusicPlaying)
+ return;
+ continue;
+ } else if (curByte == 0xFF) {
+ l_keepFreq = true;
+ } else {
+ l_freq = FREQ_TBL[curByte];
+ }
+
+ uint8 local1 = 0;
+ curByte = l_chanFileDataPtr[y++];
+ bool isLastCmdByte = (curByte & 0x80) != 0;
+ uint16 curStepSum = stepTbl[curByte & 0x7f];
+
+ for (int i = 0; !isLastCmdByte && (i < 2); ++i) {
+ curByte = l_chanFileDataPtr[y++];
+ isLastCmdByte = (curByte & 0x80) != 0;
+ if (curByte & 0x40) {
+ // note: bit used in zak theme (95) only (not used/handled in MM)
+ _music_timer = curByte & 0x3f;
+ } else {
+ local1 = curByte & 0x3f;
+ }
+ }
+
+ chanFileData[channel] += y;
+ chanDataOffset[channel] += y;
+
+ uint8 *l_chanBuf = getResource(RES_ID_CHANNEL[channel]);
+
+ if (local1 != 0) {
+ // TODO: signed or unsigned?
+ uint16 offset = READ_LE_UINT16(&actSongFileData[local1*2 + 12]);
+ l_chanFileDataPtr = actSongFileData + offset;
+
+ // next five bytes: freqDelta, attack, sustain and phase bit
+ for (int i = 0; i < 5; ++i) {
+ l_chanBuf[15 + i] = l_chanFileDataPtr[i];
+ }
+ phaseBit[channel] = l_chanFileDataPtr[4];
+
+ for (int i = 0; i < 17; ++i) {
+ l_chanBuf[25 + i] = l_chanFileDataPtr[5 + i];
+ }
+ }
+
+ if (l_keepFreq) {
+ if (!releasePhase[channel]) {
+ l_chanBuf[10] &= 0xfe; // release phase
+ }
+ releasePhase[channel] = true;
+ } else {
+ if (releasePhase[channel]) {
+ l_chanBuf[19] = phaseBit[channel];
+ l_chanBuf[10] |= 0x01; // attack phase
+ }
+ l_chanBuf[11] = LOBYTE(l_freq);
+ l_chanBuf[12] = HIBYTE(l_freq);
+ releasePhase[channel] = false;
+ }
+
+ // set counter value for frequency update (freqDeltaCounter)
+ l_chanBuf[13] = LOBYTE(curStepSum);
+ l_chanBuf[14] = HIBYTE(curStepSum);
+
+ _soundQueue[channel] = RES_ID_CHANNEL[channel];
+ processSongData(channel);
+ _soundQueue[channel+4] = RES_ID_CHANNEL[channel];
+ processSongData(channel+4);
+ --channel;
+ }
+}
+
+int Player_SID::setupSongFileData() { // $36cb
+ // no song playing
+ // TODO: remove (never NULL)
+ if (_music == NULL) {
+ for (int i = 2; i >= 0; --i) {
+ if (songChannelBits & BITMASK[i]) {
+ func_3674(i);
+ }
+ }
+ return 1;
+ }
+
+ // no new song
+ songFileOrChanBufData = _music;
+ if (_music == actSongFileData) {
+ return 0;
+ }
+
+ // new song selected
+ actSongFileData = _music;
+ for (int i = 0; i < 3; ++i) {
+ chanFileData[i] = _music + chanDataOffset[i];
+ }
+
+ return -1;
+}
+
+//x:0..2
+void Player_SID::func_3674(int channel) { // $3674
+ statusBits1B &= BITMASK_INV[channel];
+ if (statusBits1B == 0) {
+ isMusicPlaying = false;
+ unlockCodeLocation();
+ safeUnlockResource(resID_song);
+ for (int i = 0; i < 3; ++i) {
+ safeUnlockResource(RES_ID_CHANNEL[i]);
+ }
+ }
+
+ chanPrio[channel] = 2;
+
+ statusBits1A &= BITMASK_INV[channel];
+ phaseBit[channel] = 0;
+
+ func_4F45(channel);
+}
+
+void Player_SID::resetPlayerState() { // $48f7
+ for (int i = 6; i >= 0; --i)
+ releaseChannel(i);
+
+ isMusicPlaying = false;
+ unlockCodeLocation(); // does nothing
+ statusBits1B = 0;
+ statusBits1A = 0;
+ freeChannelCount = 3;
+ swapPrepared = false;
+ filterSwapped = false;
+ pulseWidthSwapped = false;
+ //var5163 = 0;
+}
+
+void Player_SID::resetSID() { // $48D8
+ SIDReg24 = 0x0f;
+
+ SID_Write( 4, 0);
+ SID_Write(11, 0);
+ SID_Write(18, 0);
+ SID_Write(23, 0);
+ SID_Write(21, 0);
+ SID_Write(22, 0);
+ SID_Write(24, SIDReg24);
+
+ resetPlayerState();
+}
+
+void Player_SID::update() { // $481B
+ if (initializing)
+ return;
+
+ if (_soundInQueue) {
+ for (int i = 6; i >= 0; --i) {
+ if (_soundQueue[i] != -1)
+ processSongData(i);
+ }
+ _soundInQueue = false;
+ }
+
+ // no sound
+ if (busyChannelBits == 0)
+ return;
+
+ for (int i = 6; i >= 0; --i) {
+ if (busyChannelBits & BITMASK[i]) {
+ updateFreq(i);
+ }
+ }
+
+ // seems to be used for background (prio=1?) sounds.
+ // If a bg sound cannot be played because all SID
+ // voices are used by higher priority sounds, the
+ // bg sound's state is updated here so it will be at
+ // the correct state when a voice is available again.
+ if (swapPrepared) {
+ swapVars(0, 0);
+ swapVarLoaded = true;
+ updateFreq(0);
+ swapVars(0, 0);
+ if (pulseWidthSwapped) {
+ swapVars(4, 1);
+ updateFreq(4);
+ swapVars(4, 1);
+ }
+ swapVarLoaded = false;
+ }
+
+ for (int i = 6; i >= 0; --i) {
+ if (busyChannelBits & BITMASK[i])
+ setSIDWaveCtrlReg(i);
+ };
+
+ if (isMusicPlaying) {
+ handleMusicBuffer();
+ }
+
+ return;
+}
+
+// channel: 0..6
+void Player_SID::processSongData(int channel) { // $4939
+ // always: _soundQueue[channel] != -1
+ // -> channelMap[channel] != -1
+ channelMap[channel] = _soundQueue[channel];
+ _soundQueue[channel] = -1;
+ songPosUpdateCounter[channel] = 0;
+
+ isVoiceChannel = (channel < 3);
+
+ songFileOrChanBufOffset[channel] = vec6[channel];
+
+ setupSongPtr(channel);
+
+ //vec5[channel] = songFileOrChanBufData; // not used
+
+ if (songFileOrChanBufData == NULL) { // chanBuf (4C1C)
+ /*
+ // TODO: do we need this?
+ LOBYTE(vec20[channel]) = 0;
+ LOBYTE(songPosPtr[channel]) = LOBYTE(songFileOrChanBufOffset[channel]);
+ */
+ releaseResourceUnk(channel);
+ return;
+ }
+
+ vec20[channel] = songFileOrChanBufData; // chanBuf (4C1C)
+ songPosPtr[channel] = songFileOrChanBufData + songFileOrChanBufOffset[channel]; // chanBuf (4C1C)
+ uint8* ptr1 = songPosPtr[channel];
+
+ int y = -1;
+ if (channel < 4) {
+ ++y;
+ if (channel == 3) {
+ readSetSIDFilterAndProps(&y, ptr1);
+ } else if (statusBits1A & BITMASK[channel]) {
+ ++y;
+ } else { // channel = 0/1/2
+ waveCtrlReg[channel] = ptr1[y];
+
+ ++y;
+ if (ptr1[y] & 0x0f) {
+ // filter on for voice channel
+ SIDReg23 |= BITMASK[channel];
+ } else {
+ // filter off for voice channel
+ SIDReg23 &= BITMASK_INV[channel];
+ }
+ SID_Write(23, SIDReg23);
+ }
+ }
+
+ saveSongPos(y, channel);
+ busyChannelBits |= BITMASK[channel];
+ readSongChunk(channel);
+}
+
+void Player_SID::readSetSIDFilterAndProps(int *offset, uint8* dataPtr) { // $49e7
+ SIDReg23 |= dataPtr[*offset];
+ SID_Write(23, SIDReg23);
+ ++*offset;
+ SIDReg24 = dataPtr[*offset];
+ SID_Write(24, SIDReg24);
+}
+
+void Player_SID::saveSongPos(int y, int channel) {
+ ++y;
+ songPosPtr[channel] += y;
+ songFileOrChanBufOffset[channel] += y;
+}
+
+// channel: 0..6
+void Player_SID::updateFreq(int channel) {
+ isVoiceChannel = (channel < 3);
+
+ --freqDeltaCounter[channel];
+ if (freqDeltaCounter[channel] < 0) {
+ readSongChunk(channel);
+ } else {
+ freqReg[channel] += freqDelta[channel];
+ }
+ setSIDFreqAS(channel);
+}
+
+void Player_SID::resetFreqDelta(int channel) {
+ freqDeltaCounter[channel] = 0;
+ freqDelta[channel] = 0;
+}
+
+void Player_SID::readSongChunk(int channel) { // $4a6b
+ while (true) {
+ if (setupSongPtr(channel) == 1) {
+ // do something with code resource
+ releaseResourceUnk(1);
+ return;
+ }
+
+ uint8* ptr1 = songPosPtr[channel];
+
+ //curChannelActive = true;
+
+ uint8 l_cmdByte = ptr1[0];
+ if (l_cmdByte == 0) {
+ //curChannelActive = false;
+ songPosUpdateCounter[channel] = 0;
+
+ var481A = -1;
+ releaseChannel(channel);
+ return;
+ }
+
+ //vec19[channel] = l_cmdByte;
+
+ // attack (1) / release (0) phase
+ if (isVoiceChannel) {
+ if (GETBIT(l_cmdByte, 0))
+ waveCtrlReg[channel] |= 0x01; // start attack phase
+ else
+ waveCtrlReg[channel] &= 0xfe; // start release phase
+ }
+
+ // channel finished bit
+ if (GETBIT(l_cmdByte, 1)) {
+ var481A = -1;
+ releaseChannel(channel);
+ return;
+ }
+
+ int y = 0;
+
+ // frequency
+ if (GETBIT(l_cmdByte, 2)) {
+ y += 2;
+ freqReg[channel] = READ_LE_UINT16(&ptr1[y-1]);
+ if (!GETBIT(l_cmdByte, 6)) {
+ y += 2;
+ freqDeltaCounter[channel] = READ_LE_UINT16(&ptr1[y-1]);
+ y += 2;
+ freqDelta[channel] = READ_LE_UINT16(&ptr1[y-1]);
+ } else {
+ resetFreqDelta(channel);
+ }
+ } else {
+ resetFreqDelta(channel);
+ }
+
+ // attack / release
+ if (isVoiceChannel && GETBIT(l_cmdByte, 3)) {
+ // start release phase
+ waveCtrlReg[channel] &= 0xfe;
+ setSIDWaveCtrlReg(channel);
+
+ ++y;
+ attackReg[channel] = ptr1[y];
+ ++y;
+ sustainReg[channel] = ptr1[y];
+
+ // set attack (1) or release (0) phase
+ waveCtrlReg[channel] |= (l_cmdByte & 0x01);
+ }
+
+ if (GETBIT(l_cmdByte, 4)) {
+ ++y;
+ uint8 curByte = ptr1[y];
+
+ // pulse width
+ if (isVoiceChannel && GETBIT(curByte, 0)) {
+ int reg = SID_REG_OFFSET[channel+4];
+
+ y += 2;
+ SID_Write(reg, ptr1[y-1]);
+ SID_Write(reg+1, ptr1[y]);
+ }
+
+ if (GETBIT(curByte, 1)) {
+ ++y;
+ readSetSIDFilterAndProps(&y, ptr1);
+
+ y += 2;
+ SID_Write(21, ptr1[y-1]);
+ SID_Write(22, ptr1[y]);
+ }
+
+ if (GETBIT(curByte, 2)) {
+ resetFreqDelta(channel);
+
+ y += 2;
+ freqDeltaCounter[channel] = READ_LE_UINT16(&ptr1[y-1]);
+ }
+ }
+
+ // set waveform (?)
+ if (GETBIT(l_cmdByte, 5)) {
+ ++y;
+ waveCtrlReg[channel] = (waveCtrlReg[channel] & 0x0f) | ptr1[y];
+ }
+
+ // song position
+ if (GETBIT(l_cmdByte, 7)) {
+ if (songPosUpdateCounter[channel] == 1) {
+ y += 2;
+ --songPosUpdateCounter[channel];
+ saveSongPos(y, channel);
+ } else {
+ // looping / skipping / ...
+ ++y;
+ songPosPtr[channel] -= ptr1[y];
+ songFileOrChanBufOffset[channel] -= ptr1[y];
+
+ ++y;
+ if (songPosUpdateCounter[channel] == 0) {
+ songPosUpdateCounter[channel] = ptr1[y];
+ } else {
+ --songPosUpdateCounter[channel];
+ }
+ }
+ } else {
+ saveSongPos(y, channel);
+ return;
+ }
+ }
+}
+
+/**
+ * Sets frequency, attack and sustain register
+ */
+void Player_SID::setSIDFreqAS(int channel) { // $4be6
+ if (swapVarLoaded)
+ return;
+ int reg = SID_REG_OFFSET[channel];
+ SID_Write(reg, LOBYTE(freqReg[channel])); // freq/pulseWidth voice 1/2/3
+ SID_Write(reg+1, HIBYTE(freqReg[channel]));
+ if (channel < 3) {
+ SID_Write(reg+5, attackReg[channel]); // attack
+ SID_Write(reg+6, sustainReg[channel]); // sustain
+ }
+}
+
+void Player_SID::setSIDWaveCtrlReg(int channel) { // $4C0D
+ if (channel < 3) {
+ int reg = SID_REG_OFFSET[channel];
+ SID_Write(reg+4, waveCtrlReg[channel]);
+ }
+}
+
+// channel: 0..6
+int Player_SID::setupSongPtr(int channel) { // $4C1C
+ //resID:5,4,3,songid
+ int resID = channelMap[channel];
+
+ // TODO: when does this happen, only if resID == 0?
+ if (getResource(resID) == NULL) {
+ releaseResourceUnk(resID);
+ if (resID == bgSoundResID) {
+ bgSoundResID = 0;
+ bgSoundActive = false;
+ swapPrepared = false;
+ pulseWidthSwapped = false;
+ }
+ return 1;
+ }
+
+ songFileOrChanBufData = getResource(resID); // chanBuf (4C1C)
+ if (songFileOrChanBufData == vec20[channel]) {
+ return 0;
+ } else {
+ vec20[channel] = songFileOrChanBufData;
+ songPosPtr[channel] = songFileOrChanBufData + songFileOrChanBufOffset[channel];
+ return -1;
+ }
+}
+
+// ignore: no effect
+// chanResIndex: 3,4,5 or 58
+void Player_SID::unlockResource(int chanResIndex) { // $4CDA
+ if ((resStatus[chanResIndex] & 0x7F) != 0)
+ --resStatus[chanResIndex];
+}
+
+void Player_SID::countFreeChannels() { // $4f26
+ freeChannelCount = 0;
+ for (int i = 0; i < 3; ++i) {
+ if (GETBIT(usedChannelBits, i) == 0)
+ ++freeChannelCount;
+ }
+}
+
+void Player_SID::func_4F45(int channel) { // $4F45
+ if (swapVarLoaded) {
+ if (channel == 0) {
+ swapPrepared = false;
+ resetSwapVars();
+ }
+ pulseWidthSwapped = false;
+ } else {
+ if (channel == 3) {
+ filterUsed = false;
+ }
+
+ if (chanPrio[channel] == 1) {
+ if (var481A == 1)
+ prepareSwapVars(channel);
+ else if (channel < 3)
+ clearSIDWaveform(channel);
+ } else if (channel < 3 && bgSoundActive && swapPrepared &&
+ !(filterSwapped && filterUsed))
+ {
+ busyChannelBits |= BITMASK[channel];
+ useSwapVars(channel);
+ waveCtrlReg[channel] |= 0x01;
+ setSIDWaveCtrlReg(channel);
+
+ safeUnlockResource(channelMap[channel]);
+ return;
+ }
+
+ chanPrio[channel] = 0;
+ usedChannelBits &= BITMASK_INV[channel];
+ countFreeChannels();
+ }
+
+ int resIndex = channelMap[channel];
+ channelMap[channel] = 0;
+ safeUnlockResource(resIndex);
+}
+
+// chanResIndex: 3,4,5 or 58
+void Player_SID::safeUnlockResource(int resIndex) { // $4FEA
+ if (!isMusicPlaying) {
+ unlockResource(resIndex);
+ }
+}
+
+void Player_SID::releaseResource(int resIndex) { // $5031
+ releaseResChannels(resIndex);
+ if (resIndex == bgSoundResID && var481A == -1) {
+ safeUnlockResource(resIndex);
+
+ bgSoundResID = 0;
+ bgSoundActive = false;
+ swapPrepared = false;
+ pulseWidthSwapped = false;
+
+ resetSwapVars();
+ }
+}
+
+void Player_SID::releaseResChannels(int resIndex) { // $5070
+ for (int i = 3; i >= 0; --i) {
+ if (resIndex == channelMap[i]) {
+ releaseChannel(i);
+ }
+ }
+}
+
+void Player_SID::stopSound_intern(int soundResID) { // $5093
+ for (int i = 0; i < 7; ++i) {
+ if (soundResID == _soundQueue[i]) {
+ _soundQueue[i] = -1;
+ }
+ }
+ var481A = -1;
+ releaseResource(soundResID);
+}
+
+void Player_SID::stopAllSounds_intern() { // $4CAA
+ statusBits1B = 0;
+ isMusicPlaying = false;
+
+ if (resID_song != 0) {
+ unlockResource(resID_song);
+ }
+
+ chanPrio[0] = 2;
+ chanPrio[1] = 2;
+ chanPrio[2] = 2;
+
+ statusBits1A = 0;
+ phaseBit[0] = 0;
+ phaseBit[1] = 0;
+ phaseBit[2] = 0;
+}
+
+void Player_SID::releaseResourceUnk(int resIndex) { // $50A4
+ var481A = -1;
+ releaseResource(resIndex);
+}
+
+// a: 0..6
+void Player_SID::releaseChannel(int channel) {
+ stopChannel(channel);
+ if (channel >= 4) {
+ return;
+ }
+ if (channel < 3) {
+ SIDReg23Stuff = SIDReg23;
+ clearSIDWaveform(channel);
+ }
+ func_4F45(channel);
+ if (channel >= 3) {
+ return;
+ }
+ if ((SIDReg23 != SIDReg23Stuff) &&
+ (SIDReg23 & 0x07) == 0)
+ {
+ if (filterUsed) {
+ func_4F45(3);
+ stopChannel(3);
+ }
+ }
+
+ stopChannel(channel + 4);
+}
+
+void Player_SID::clearSIDWaveform(int channel) {
+ if (!isMusicPlaying && var481A == -1) {
+ waveCtrlReg[channel] &= 0x0e;
+ setSIDWaveCtrlReg(channel);
+ }
+}
+
+void Player_SID::stopChannel(int channel) {
+ songPosUpdateCounter[channel] = 0;
+ // clear "channel" bit
+ busyChannelBits &= BITMASK_INV[channel];
+ if (channel >= 4) {
+ // pulsewidth = 0
+ channelMap[channel] = 0;
+ }
+}
+
+// channel: 0..6, swapIndex: 0..2
+void Player_SID::swapVars(int channel, int swapIndex) { // $51a5
+ if (channel < 3) {
+ std::swap(attackReg[channel], swapAttack[swapIndex]);
+ std::swap(sustainReg[channel], swapSustain[swapIndex]);
+ }
+ //std::swap(vec5[channel], swapVec5[swapIndex]); // not used
+ //std::swap(vec19[channel], swapVec19[swapIndex]); // not used
+
+ std::swap(chanPrio[channel], swapSongPrio[swapIndex]);
+ std::swap(channelMap[channel], swapVec479C[swapIndex]);
+ std::swap(songPosUpdateCounter[channel], swapSongPosUpdateCounter[swapIndex]);
+ std::swap(waveCtrlReg[channel], swapWaveCtrlReg[swapIndex]);
+ std::swap(songPosPtr[channel], swapSongPosPtr[swapIndex]);
+ std::swap(freqReg[channel], swapFreqReg[swapIndex]);
+ std::swap(freqDeltaCounter[channel], swapVec11[swapIndex]);
+ std::swap(freqDelta[channel], swapVec10[swapIndex]);
+ std::swap(vec20[channel], swapVec20[swapIndex]);
+ std::swap(songFileOrChanBufOffset[channel], swapVec8[swapIndex]);
+}
+
+void Player_SID::resetSwapVars() { // $52d0
+ for (int i = 0; i < 2; ++i) {
+ swapAttack[i] = 0;
+ swapSustain[i] = 0;
+ }
+ for (int i = 0; i < 3; ++i) {
+ swapVec5[i] = 0;
+ swapSongPrio[i] = 0;
+ swapVec479C[i] = 0;
+ swapVec19[i] = 0;
+ swapSongPosUpdateCounter[i] = 0;
+ swapWaveCtrlReg[i] = 0;
+ swapSongPosPtr[i] = 0;
+ swapFreqReg[i] = 0;
+ swapVec11[i] = 0;
+ swapVec10[i] = 0;
+ swapVec20[i] = 0;
+ swapVec8[i] = 0;
+ }
+}
+
+void Player_SID::prepareSwapVars(int channel) { // $52E5
+ if (channel >= 4)
+ return;
+
+ if (channel < 3) {
+ if (!keepSwapVars) {
+ resetSwapVars();
+ }
+ swapVars(channel, 0);
+ if (busyChannelBits & BITMASK[channel+4]) {
+ swapVars(channel+4, 1);
+ pulseWidthSwapped = true;
+ }
+ } else if (channel == 3) {
+ SIDReg24_HiNibble = SIDReg24 & 0x70;
+ resetSwapVars();
+ keepSwapVars = true;
+ swapVars(3, 2);
+ filterSwapped = true;
+ }
+ swapPrepared = true;
+}
+
+void Player_SID::useSwapVars(int channel) { // $5342
+ if (channel >= 3)
+ return;
+
+ swapVars(channel, 0);
+ setSIDFreqAS(channel);
+ if (pulseWidthSwapped) {
+ swapVars(channel+4, 1);
+ setSIDFreqAS(channel+4);
+ }
+ if (filterSwapped) {
+ swapVars(3, 2);
+
+ // resonating filter freq. or voice-to-filter mapping?
+ SIDReg23 = (SIDReg23Stuff & 0xf0) | BITMASK[channel];
+ SID_Write(23, SIDReg23);
+
+ // filter props
+ SIDReg24 = (SIDReg24 & 0x0f) | SIDReg24_HiNibble;
+ SID_Write(24, SIDReg24);
+
+ // filter freq.
+ SID_Write(21, LOBYTE(freqReg[3]));
+ SID_Write(22, HIBYTE(freqReg[3]));
+ } else {
+ SIDReg23 = SIDReg23Stuff & BITMASK_INV[channel];
+ SID_Write(23, SIDReg23);
+ }
+
+ swapPrepared = false;
+ pulseWidthSwapped = false;
+ keepSwapVars = false;
+ SIDReg24_HiNibble = 0;
+ filterSwapped = false;
+}
+
+// ignore: no effect
+// resIndex: 3,4,5 or 58
+void Player_SID::lockResource(int resIndex) { // $4ff4
+ if (!isMusicPlaying)
+ ++resStatus[resIndex];
+}
+
+void Player_SID::reserveChannel(int channel, uint8 prioValue, int chanResIndex) { // $4ffe
+ if (channel == 3) {
+ filterUsed = true;
+ } else if (channel < 3) {
+ usedChannelBits |= BITMASK[channel];
+ countFreeChannels();
+ }
+
+ chanPrio[channel] = prioValue;
+ lockResource(chanResIndex);
+}
+
+// ignore: no effect
+void Player_SID::unlockCodeLocation() { // $513e
+ resStatus[1] &= 0x80;
+ resStatus[2] &= 0x80;
+}
+
+// ignore: no effect
+void Player_SID::lockCodeLocation() { // $514f
+ resStatus[1] |= 0x01;
+ resStatus[2] |= 0x01;
+}
+
+void Player_SID::initMusic(int songResIndex) { // $7de6
+ unlockResource(resID_song);
+
+ resID_song = songResIndex;
+ _music = getResource(resID_song);
+ if (_music == NULL) {
+ return;
+ }
+
+ // song base address
+ uint8* songFileDataPtr = _music;
+ actSongFileData = _music;
+
+ initializing = true;
+ _soundInQueue = false;
+ isMusicPlaying = false;
+
+ unlockCodeLocation();
+ resetPlayerState();
+
+ lockResource(resID_song);
+ buildStepTbl(songFileDataPtr[5]);
+
+ // fetch sound
+ songChannelBits = songFileDataPtr[4];
+ for (int i = 2; i >= 0; --i) {
+ if ((songChannelBits & BITMASK[i]) != 0) {
+ func_7eae(i, songFileDataPtr);
+ }
+ }
+
+ isMusicPlaying = true;
+ lockCodeLocation();
+
+ SIDReg23 &= 0xf0;
+ SID_Write(23, SIDReg23);
+
+ handleMusicBuffer();
+
+ initializing = false;
+ _soundInQueue = true;
+}
+
+// params:
+// channel: channel 0..2
+void Player_SID::func_7eae(int channel, uint8* songFileDataPtr) {
+ int pos = SONG_CHANNEL_OFFSET[channel];
+ chanDataOffset[channel] = READ_LE_UINT16(&songFileDataPtr[pos]);
+ chanFileData[channel] = songFileDataPtr + chanDataOffset[channel];
+
+ //vec5[channel+4] = vec5[channel] = CHANNEL_BUFFER_ADDR[RES_ID_CHANNEL[channel]]; // not used
+ vec6[channel+4] = 0x0019;
+ vec6[channel] = 0x0008;
+
+ func_819b(channel);
+
+ waveCtrlReg[channel] = 0;
+}
+
+void Player_SID::func_819b(int channel) {
+ reserveChannel(channel, 127, RES_ID_CHANNEL[channel]);
+
+ statusBits1B |= BITMASK[channel];
+ statusBits1A |= BITMASK[channel];
+}
+
+void Player_SID::buildStepTbl(int step) { // $82B4
+ stepTbl[0] = 0;
+ stepTbl[1] = step - 2;
+ for (int i = 2; i < 33; ++i) {
+ stepTbl[i] = stepTbl[i-1] + step;
+ }
+}
+
+int Player_SID::reserveSoundFilter(uint8 value, uint8 chanResIndex) { // $4ED0
+ int channel = 3;
+ reserveChannel(channel, value, chanResIndex);
+ return channel;
+}
+
+int Player_SID::reserveSoundVoice(uint8 value, uint8 chanResIndex) { // $4EB8
+ for (int i = 2; i >= 0; --i) {
+ if ((usedChannelBits & BITMASK[i]) == 0) {
+ reserveChannel(i, value, chanResIndex);
+ return i;
+ }
+ }
+ return 0;
+}
+
+void Player_SID::findLessPrioChannels(uint8 soundPrio) { // $4ED8
+ minChanPrio = 127;
+
+ chansWithLowerPrioCount = 0;
+ for (int i = 2; i >= 0; --i) {
+ if (usedChannelBits & BITMASK[i]) {
+ if (chanPrio[i] < soundPrio)
+ ++chansWithLowerPrioCount;
+ if (chanPrio[i] < minChanPrio) {
+ minChanPrio = chanPrio[i];
+ minChanPrioIndex = i;
+ }
+ }
+ }
+
+ if (chansWithLowerPrioCount == 0)
+ return;
+
+ if (soundPrio >= chanPrio[3]) {
+ actFilterHasLowerPrio = true;
+ } else {
+ /* TODO: is this really a no-op?
+ if (minChanPrioIndex < chanPrio[3])
+ minChanPrioIndex = minChanPrioIndex;
+ */
+
+ actFilterHasLowerPrio = false;
+ }
+}
+
+void Player_SID::releaseResourceBySound(int resID) { // $5088
+ var481A = 1;
+ releaseResource(resID);
+}
+
+void Player_SID::readVec6Data(int x, int *offset, uint8 *songFilePtr, int chanResID) { // $4E99
+ //vec5[x] = songFilePtr;
+ vec6[x] = songFilePtr[*offset];
+ *offset += 2;
+ _soundQueue[x] = chanResID;
+}
+
+int Player_SID::initSound(int soundResID) { // $4D0A
+ initializing = true;
+
+ if (isMusicPlaying && (statusBits1A & 0x07) == 0x07) {
+ initializing = false;
+ return -2;
+ }
+
+ uint8 *songFilePtr = getResource(soundResID);
+ if (songFilePtr == NULL) {
+ initializing = false;
+ return 1;
+ }
+
+ uint8 soundPrio = songFilePtr[4];
+ // for (mostly but not always looped) background sounds
+ if (soundPrio == 1) {
+ bgSoundResID = soundResID;
+ bgSoundActive = true;
+ }
+
+ uint8 requestedChannels = 0;
+ if ((songFilePtr[5] & 0x40) == 0) {
+ ++requestedChannels;
+ if (songFilePtr[5] & 0x02)
+ ++requestedChannels;
+ if (songFilePtr[5] & 0x08)
+ ++requestedChannels;
+ }
+
+ bool filterNeeded = (songFilePtr[5] & 0x20) != 0;
+ bool filterBlocked = (filterUsed && filterNeeded);
+ if (filterBlocked || (freeChannelCount < requestedChannels))
+ {
+ findLessPrioChannels(soundPrio);
+
+ if ((freeChannelCount + chansWithLowerPrioCount < requestedChannels) ||
+ (filterBlocked && !actFilterHasLowerPrio))
+ {
+ initializing = false;
+ return -1;
+ }
+
+ if (filterBlocked) {
+ if (soundPrio < chanPrio[3]) {
+ initializing = false;
+ return -1;
+ }
+
+ uint8 l_resID = channelMap[3];
+ releaseResourceBySound(l_resID);
+ }
+
+ while ((freeChannelCount < requestedChannels) || (filterNeeded && filterUsed))
+ {
+ findLessPrioChannels(soundPrio);
+ if (minChanPrio >= soundPrio) {
+ initializing = false;
+ return -1;
+ }
+
+ uint8 l_resID = channelMap[minChanPrioIndex];
+ releaseResourceBySound(l_resID);
+ }
+ }
+
+ int x;
+ uint8 soundByte5 = songFilePtr[5];
+ if (soundByte5 & 0x40)
+ x = reserveSoundFilter(soundPrio, soundResID);
+ else
+ x = reserveSoundVoice(soundPrio, soundResID);
+
+ uint8 var4CF3 = x;
+ int y = 6;
+ if (soundByte5 & 0x01) {
+ x += 4;
+ readVec6Data(x, &y, songFilePtr, soundResID);
+ }
+ if (soundByte5 & 0x02) {
+ x = reserveSoundVoice(soundPrio, soundResID);
+ readVec6Data(x, &y, songFilePtr, soundResID);
+ }
+ if (soundByte5 & 0x04) {
+ x += 4;
+ readVec6Data(x, &y, songFilePtr, soundResID);
+ }
+ if (soundByte5 & 0x08) {
+ x = reserveSoundVoice(soundPrio, soundResID);
+ readVec6Data(x, &y, songFilePtr, soundResID);
+ }
+ if (soundByte5 & 0x10) {
+ x += 4;
+ readVec6Data(x, &y, songFilePtr, soundResID);
+ }
+ if (soundByte5 & 0x20) {
+ x = reserveSoundFilter(soundPrio, soundResID);
+ readVec6Data(x, &y, songFilePtr, soundResID);
+ }
+
+ //vec5[var4CF3] = songFilePtr;
+ vec6[var4CF3] = y;
+ _soundQueue[var4CF3] = soundResID;
+
+ initializing = false;
+ _soundInQueue = true;
+
+ return soundResID;
+}
+
+void Player_SID::unused1() { // $50AF
+ var481A = -1;
+ if (bgSoundResID != 0) {
+ releaseResourceUnk(bgSoundResID);
+ }
+}
+
+///////////////////////////
+///////////////////////////
+
+#define ZEROMEM(a) memset(a, 0, sizeof(a))
+
+Player_SID::Player_SID(ScummEngine *scumm, Audio::Mixer *mixer) {
+ /*
+ * clear memory
+ */
+
+ resID_song = 0;
+ statusBits1A = 0;
+ statusBits1B = 0;
+ busyChannelBits = 0;
+ SIDReg23 = 0;
+ SIDReg23Stuff = 0;
+ SIDReg24 = 0;
+ bgSoundResID = 0;
+ freeChannelCount = 0;
+ usedChannelBits = 0;
+ var481A = 0;
+ songChannelBits = 0;
+ //var5163 = 0;
+ SIDReg24_HiNibble = 0;
+ chansWithLowerPrioCount = 0;
+ minChanPrio = 0;
+ minChanPrioIndex = 0;
+
+ _music = NULL;
+ songFileOrChanBufData = NULL;
+ actSongFileData = NULL;
+
+ initializing = false;
+ _soundInQueue = false;
+ isVoiceChannel = false;
+ isMusicPlaying = false;
+ swapVarLoaded = false;
+ bgSoundActive = false;
+ filterUsed = false;
+ pulseWidthSwapped = false;
+ swapPrepared = false;
+ filterSwapped = false;
+ keepSwapVars = false;
+ actFilterHasLowerPrio = false;
+
+ ZEROMEM(chanFileData);
+ ZEROMEM(chanDataOffset);
+ ZEROMEM(songPosPtr);
+ ZEROMEM(freqReg);
+ ZEROMEM(vec6);
+ ZEROMEM(songFileOrChanBufOffset);
+ ZEROMEM(freqDelta);
+ ZEROMEM(freqDeltaCounter);
+ ZEROMEM(swapSongPosPtr);
+ ZEROMEM(swapVec5);
+ ZEROMEM(swapVec8);
+ ZEROMEM(swapVec10);
+ ZEROMEM(swapFreqReg);
+ ZEROMEM(swapVec11);
+ ZEROMEM(vec20);
+ ZEROMEM(swapVec20);
+ ZEROMEM(resStatus);
+ ZEROMEM(attackReg);
+ ZEROMEM(sustainReg);
+ ZEROMEM(phaseBit);
+ ZEROMEM(releasePhase);
+ ZEROMEM(_soundQueue);
+ ZEROMEM(channelMap);
+ ZEROMEM(songPosUpdateCounter);
+ ZEROMEM(chanPrio);
+ ZEROMEM(waveCtrlReg);
+ ZEROMEM(swapAttack);
+ ZEROMEM(swapSustain);
+ ZEROMEM(swapSongPrio);
+ ZEROMEM(swapVec479C);
+ ZEROMEM(swapVec19);
+ ZEROMEM(swapSongPosUpdateCounter);
+ ZEROMEM(swapWaveCtrlReg);
+ ZEROMEM(stepTbl);
+
+ /*
+ * initialize data
+ */
+
+ const uint8 chanBuffer_const[3][45] = {{
+ 0x00,0x00,0x00,0x00,0x7f,0x01,0x19,0x00,
+ 0x00,0x00,0x2d,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0xf0,0x40,0x10,0x04,0x00,0x00,
+ 0x00,0x04,0x27,0x03,0xff,0xff,0x01,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00
+ },{
+ 0x00,0x00,0x00,0x00,0x7f,0x01,0x19,0x00,
+ 0x00,0x00,0x2d,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0xf0,0x20,0x10,0x04,0x00,0x00,
+ 0x00,0x04,0x27,0x03,0xff,0xff,0x02,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00
+ },{
+ 0x00,0x00,0x00,0x00,0x7f,0x01,0x19,0x00,
+ 0x00,0x00,0x2d,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0xf0,0x20,0x10,0x04,0x00,0x00,
+ 0x00,0x04,0x27,0x03,0xff,0xff,0x02,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00
+ }};
+ memcpy(chanBuffer, chanBuffer_const, sizeof(chanBuffer_const));
+
+ for (int i = 0; i < 7; ++i) {
+ _soundQueue[i] = -1;
+ };
+
+ _music_timer = 0;
+
+ _mixer = mixer;
+ _sample_rate = _mixer->getOutputRate();
+ _vm = scumm;
+
+ // sound speed is slightly different on NTSC and PAL machines
+ // as the SID clock depends on the frame rate.
+ // ScummVM does not distinguish between NTSC and PAL targets
+ // so we use the NTSC timing here as the music was composed for
+ // NTSC systems (music on PAL systems is slower).
+ _videoSystem = NTSC;
+ _cpuCyclesLeft = 0;
+
+ initSID();
+ resetSID();
+
+ _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true);
+}
+
+Player_SID::~Player_SID() {
+ _mixer->stopHandle(_soundHandle);
+ delete _sid;
+}
+
+uint8 *Player_SID::getResource(int resID) {
+ switch (resID) {
+ case 0:
+ return NULL;
+ case 3:
+ case 4:
+ case 5:
+ return chanBuffer[resID-3];
+ default:
+ return _vm->getResourceAddress(rtSound, resID);
+ }
+}
+
+int Player_SID::readBuffer(int16 *buffer, const int numSamples) {
+ int samplesLeft = numSamples;
+
+ _mutex.lock();
+
+ while (samplesLeft > 0) {
+ // update SID status after each frame
+ if (_cpuCyclesLeft <= 0) {
+ update();
+ _cpuCyclesLeft = timingProps[_videoSystem].cyclesPerFrame;
+ }
+ // fetch samples
+ int sampleCount = _sid->clock(_cpuCyclesLeft, (short*)buffer, samplesLeft);
+ samplesLeft -= sampleCount;
+ buffer += sampleCount;
+ }
+
+ _mutex.unlock();
+
+ return numSamples;
+}
+
+void Player_SID::SID_Write(int reg, uint8 data) {
+ _sid->write(reg, data);
+}
+
+void Player_SID::initSID() {
+ _sid = new Resid::SID();
+ _sid->set_sampling_parameters(
+ timingProps[_videoSystem].clockFreq,
+ _sample_rate);
+ _sid->enable_filter(true);
+
+ _sid->reset();
+ // Synchronize the waveform generators (must occur after reset)
+ _sid->write( 4, 0x08);
+ _sid->write(11, 0x08);
+ _sid->write(18, 0x08);
+ _sid->write( 4, 0x00);
+ _sid->write(11, 0x00);
+ _sid->write(18, 0x00);
+}
+
+void Player_SID::startSound(int nr) {
+ byte *data = _vm->getResourceAddress(rtSound, nr);
+ assert(data);
+
+ // WORKAROUND:
+ // sound[4] contains either a song prio or a music channel usage byte.
+ // As music channel usage is always 0x07 for all music files and
+ // prio 7 is never used in any sound file use this byte for auto-detection.
+ bool isMusic = (data[4] == 0x07);
+
+ _mutex.lock();
+
+ if (isMusic) {
+ initMusic(nr);
+ } else {
+ stopSound_intern(nr);
+ initSound(nr);
+ }
+
+ _mutex.unlock();
+}
+
+void Player_SID::stopSound(int nr) {
+ if (nr == -1)
+ return;
+
+ _mutex.lock();
+ stopSound_intern(nr);
+ _mutex.unlock();
+}
+
+void Player_SID::stopAllSounds() {
+ _mutex.lock();
+ stopAllSounds_intern();
+ _mutex.unlock();
+}
+
+int Player_SID::getSoundStatus(int nr) const {
+ int result = 0;
+
+ //_mutex.lock();
+
+ if (resID_song == nr && isMusicPlaying) {
+ result = 1;
+ }
+
+ for (int i = 0; (i < 4) && (result == 0); ++i) {
+ if (nr == _soundQueue[i] ||
+ nr == channelMap[i])
+ {
+ result = 1;
+ }
+ }
+
+ //_mutex.unlock();
+
+ return result;
+}
+
+int Player_SID::getMusicTimer() {
+ int result = _music_timer;
+ _music_timer = 0;
+ return result;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/player_sid.h b/engines/scumm/player_sid.h
new file mode 100644
index 0000000000..2343bc8cb7
--- /dev/null
+++ b/engines/scumm/player_sid.h
@@ -0,0 +1,280 @@
+/* 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 SCUMM_PLAYER_SID_H
+#define SCUMM_PLAYER_SID_H
+
+#include "common/mutex.h"
+#include "common/scummsys.h"
+#include "scumm/music.h"
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+#include "sound/softsynth/sid.h"
+
+namespace Scumm {
+
+// the "channel" parameters seem to be in fact SID register
+// offsets. Should be replaced.
+enum sid_reg_t {
+ FREQ_VOICE1,
+ FREQ_VOICE2,
+ FREQ_VOICE3,
+ FREQ_FILTER,
+ PULSE_VOICE1,
+ PULSE_VOICE2,
+ PULSE_VOICE3
+};
+
+enum VideoStandard {
+ PAL,
+ NTSC
+};
+
+class ScummEngine;
+
+class Player_SID : public Audio::AudioStream, public MusicEngine {
+public:
+ Player_SID(ScummEngine *scumm, Audio::Mixer *mixer);
+ virtual ~Player_SID();
+
+ virtual void setMusicVolume(int vol) { _maxvol = vol; };
+ 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 _sample_rate; }
+
+private:
+ Resid::SID *_sid;
+ void SID_Write(int reg, uint8 data);
+ void initSID();
+ uint8 *getResource(int resID);
+
+ // number of cpu cycles until next frame update
+ Resid::cycle_count _cpuCyclesLeft;
+
+ ScummEngine *_vm;
+ Audio::Mixer *_mixer;
+ Audio::SoundHandle _soundHandle;
+ int _sample_rate;
+ int _maxvol;
+ Common::Mutex _mutex;
+
+ VideoStandard _videoSystem;
+
+ int _music_timer;
+ uint8* _music;
+
+private:
+ void initMusic(int songResIndex); // $7de6
+ int initSound(int soundResID); // $4D0A
+ void stopSound_intern(int soundResID); // $5093
+ void stopAllSounds_intern(); // $4CAA
+
+ void resetSID(); // $48D8
+ void update(); // $481B
+ void handleMusicBuffer();
+ int setupSongFileData(); // $36cb
+ void func_3674(int channel); // $3674
+ void resetPlayerState(); // $48f7
+ void processSongData(int channel); // $4939
+ void readSetSIDFilterAndProps(int *offset, uint8* dataPtr); // $49e7
+ void saveSongPos(int y, int channel);
+ void updateFreq(int channel);
+ void resetFreqDelta(int channel);
+ void readSongChunk(int channel); // $4a6b
+ void setSIDFreqAS(int channel); // $4be6
+ void setSIDWaveCtrlReg(int channel); // $4C0D
+ int setupSongPtr(int channel); // $4C1C
+ void unlockResource(int chanResIndex); // $4CDA
+ void countFreeChannels(); // $4f26
+ void func_4F45(int channel); // $4F45
+ void safeUnlockResource(int resIndex); // $4FEA
+ void releaseResource(int resIndex); // $5031
+ void releaseResChannels(int resIndex); // $5070
+ void releaseResourceUnk(int resIndex); // $50A4
+ void releaseChannel(int channel);
+ void clearSIDWaveform(int channel);
+ void stopChannel(int channel);
+ void swapVars(int channel, int swapIndex); // $51a5
+ void resetSwapVars(); // $52d0
+ void prepareSwapVars(int channel); // $52E5
+ void useSwapVars(int channel); // $5342
+ void lockResource(int resIndex); // $4ff4
+ void reserveChannel(int channel, uint8 prioValue, int chanResIndex); // $4ffe
+ void unlockCodeLocation(); // $513e
+ void lockCodeLocation(); // $514f
+ void func_7eae(int channel, uint8* songFileDataPtr); // $7eae
+ void func_819b(int channel); // $819b
+ void buildStepTbl(int step); // $82B4
+ int reserveSoundFilter(uint8 value, uint8 chanResIndex); // $4ED0
+ int reserveSoundVoice(uint8 value, uint8 chanResIndex); // $4EB8
+ void findLessPrioChannels(uint8 soundPrio); // $4ED8
+ void releaseResourceBySound(int resID); // $5088
+ void readVec6Data(int x, int *offset, uint8 *songFilePtr, int chanResID); // $4E99
+
+ void unused1(); // $50AF
+
+ uint8 chanBuffer[3][45];
+
+ int resID_song;
+
+ // statusBits1A/1B are always equal
+ uint8 statusBits1A;
+ uint8 statusBits1B;
+
+ uint8 busyChannelBits;
+
+ uint8 SIDReg23;
+ uint8 SIDReg23Stuff;
+ uint8 SIDReg24;
+
+ uint8* chanFileData[3];
+ uint16 chanDataOffset[3];
+ uint8* songPosPtr[7];
+
+ // 0..2: freq value voice1/2/3
+ // 3: filter freq
+ // 4..6: pulse width
+ uint16 freqReg[7];
+
+ // start offset[i] for songFileOrChanBufData to obtain songPosPtr[i]
+ // vec6[0..2] = 0x0008;
+ // vec6[4..6] = 0x0019;
+ uint16 vec6[7];
+
+ // current offset[i] for songFileOrChanBufData to obtain songPosPtr[i] (starts with vec6[i], increased later)
+ uint16 songFileOrChanBufOffset[7];
+
+ uint16 freqDelta[7];
+ int freqDeltaCounter[7];
+ uint8* swapSongPosPtr[3];
+ uint8* swapVec5[3];
+ uint16 swapVec8[3];
+ uint16 swapVec10[3];
+ uint16 swapFreqReg[3];
+ int swapVec11[3];
+
+ // never read
+ //uint8* vec5[7];
+ // never read
+ //uint8 vec19[7];
+ // never read (needed by scumm engine?)
+ //bool curChannelActive;
+
+ uint8* vec20[7];
+
+ uint8* swapVec20[3];
+
+ // resource status (never read)
+ // bit7: some flag
+ // bit6..0: counter (use-count?), maybe just bit0 as flag (used/unused?)
+ uint8 resStatus[70];
+
+ uint8* songFileOrChanBufData;
+ uint8* actSongFileData;
+
+ uint16 stepTbl[33];
+
+ bool initializing;
+ bool _soundInQueue;
+ bool isVoiceChannel;
+
+ bool isMusicPlaying;
+ bool swapVarLoaded;
+ bool bgSoundActive;
+ bool filterUsed;
+
+ uint8 bgSoundResID;
+ uint8 freeChannelCount;
+
+ // seems to be used for managing the three voices
+ // bit[0..2]: 0 -> unused, 1 -> already in use
+ uint8 usedChannelBits;
+ uint8 attackReg[3];
+ uint8 sustainReg[3];
+
+ // -1/0/1
+ int var481A;
+
+ // bit-array: 00000cba
+ // a/b/c: channel1/2/3
+ uint8 songChannelBits;
+
+ bool pulseWidthSwapped;
+ bool swapPrepared;
+
+ // never read
+ //uint8 var5163;
+
+ bool filterSwapped;
+ uint8 SIDReg24_HiNibble;
+ bool keepSwapVars;
+
+ uint8 phaseBit[3];
+ bool releasePhase[3];
+
+ // values: a resID or -1
+ // resIDs: 3, 4, 5 or song-number
+ int _soundQueue[7];
+
+ // values: a resID or 0
+ // resIDs: 3, 4, 5 or song-number
+ int channelMap[7];
+
+ uint8 songPosUpdateCounter[7];
+
+ // priortity of channel contents
+ // MM: 1: lowest .. 120: highest (1,2,A,64,6E,73,78)
+ // Zak: -???: lowest .. 120: highest (5,32,64,65,66,6E,78, A5,A6,AF,D7)
+ uint8 chanPrio[7];
+
+ // only [0..2] used?
+ uint8 waveCtrlReg[7];
+
+ uint8 swapAttack[2];
+ uint8 swapSustain[2];
+ uint8 swapSongPrio[3];
+ int swapVec479C[3];
+ uint8 swapVec19[3];
+ uint8 swapSongPosUpdateCounter[3];
+ uint8 swapWaveCtrlReg[3];
+
+ bool actFilterHasLowerPrio;
+ uint8 chansWithLowerPrioCount;
+ uint8 minChanPrio;
+ uint8 minChanPrioIndex;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/player_v1.cpp b/engines/scumm/player_v1.cpp
index c2b82581b3..2d43eb378b 100644
--- a/engines/scumm/player_v1.cpp
+++ b/engines/scumm/player_v1.cpp
@@ -125,7 +125,7 @@ void Player_V1::clear_channel(int i) {
_channels[i].volume = 15;
}
-int Player_V1::getMusicTimer() const {
+int Player_V1::getMusicTimer() {
/* Do V1 games have a music timer? */
return 0;
}
diff --git a/engines/scumm/player_v1.h b/engines/scumm/player_v1.h
index ab5d6c1619..acba13e475 100644
--- a/engines/scumm/player_v1.h
+++ b/engines/scumm/player_v1.h
@@ -41,7 +41,7 @@ public:
virtual void startSound(int sound);
virtual void stopSound(int sound);
virtual void stopAllSounds();
- virtual int getMusicTimer() const;
+ virtual int getMusicTimer();
protected:
virtual void nextTick();
diff --git a/engines/scumm/player_v2.cpp b/engines/scumm/player_v2.cpp
index 4e8ee81486..200ad4ad6d 100644
--- a/engines/scumm/player_v2.cpp
+++ b/engines/scumm/player_v2.cpp
@@ -530,7 +530,7 @@ void Player_V2::clear_channel(int i) {
memset(channel, 0, sizeof(ChannelInfo));
}
-int Player_V2::getMusicTimer() const {
+int Player_V2::getMusicTimer() {
if (_isV3Game)
return _music_timer;
else
diff --git a/engines/scumm/player_v2.h b/engines/scumm/player_v2.h
index 5b375fb1e5..e2dd252b5b 100644
--- a/engines/scumm/player_v2.h
+++ b/engines/scumm/player_v2.h
@@ -80,7 +80,7 @@ public:
virtual void startSound(int sound);
virtual void stopSound(int sound);
virtual void stopAllSounds();
- virtual int getMusicTimer() const;
+ virtual int getMusicTimer();
virtual int getSoundStatus(int sound) const;
// AudioStream API
@@ -170,7 +170,7 @@ public:
virtual void startSound(int sound);
virtual void stopSound(int sound);
virtual void stopAllSounds();
- virtual int getMusicTimer() const;
+ virtual int getMusicTimer();
virtual int getSoundStatus(int sound) const;
// AudioStream API
diff --git a/engines/scumm/player_v2a.cpp b/engines/scumm/player_v2a.cpp
index 1453f01711..aadb789f1f 100644
--- a/engines/scumm/player_v2a.cpp
+++ b/engines/scumm/player_v2a.cpp
@@ -1936,7 +1936,7 @@ void Player_V2A::updateSound() {
}
}
-int Player_V2A::getMusicTimer() const {
+int Player_V2A::getMusicTimer() {
return 0; // FIXME - need to keep track of playing music resources
}
diff --git a/engines/scumm/player_v2a.h b/engines/scumm/player_v2a.h
index 3157e9d8be..d60040bd76 100644
--- a/engines/scumm/player_v2a.h
+++ b/engines/scumm/player_v2a.h
@@ -49,7 +49,7 @@ public:
virtual void startSound(int sound);
virtual void stopSound(int sound);
virtual void stopAllSounds();
- virtual int getMusicTimer() const;
+ virtual int getMusicTimer();
virtual int getSoundStatus(int sound) const;
private:
diff --git a/engines/scumm/player_v2cms.cpp b/engines/scumm/player_v2cms.cpp
index 28040a84dc..9f14a3587c 100644
--- a/engines/scumm/player_v2cms.cpp
+++ b/engines/scumm/player_v2cms.cpp
@@ -1095,7 +1095,7 @@ void Player_V2CMS::clear_channel(int i) {
memset(channel, 0, sizeof(ChannelInfo));
}
-int Player_V2CMS::getMusicTimer() const {
+int Player_V2CMS::getMusicTimer() {
if (_isV3Game)
return _music_timer;
else
diff --git a/engines/scumm/player_v3a.cpp b/engines/scumm/player_v3a.cpp
index 5082bf102d..cffbd729ea 100644
--- a/engines/scumm/player_v3a.cpp
+++ b/engines/scumm/player_v3a.cpp
@@ -345,7 +345,7 @@ void Player_V3A::playMusic() {
}
}
-int Player_V3A::getMusicTimer() const {
+int Player_V3A::getMusicTimer() {
return _music_timer / 30;
}
diff --git a/engines/scumm/player_v3a.h b/engines/scumm/player_v3a.h
index ce8d9477c9..d3d1e5eeb5 100644
--- a/engines/scumm/player_v3a.h
+++ b/engines/scumm/player_v3a.h
@@ -48,7 +48,7 @@ public:
virtual void startSound(int sound);
virtual void stopSound(int sound);
virtual void stopAllSounds();
- virtual int getMusicTimer() const;
+ virtual int getMusicTimer();
virtual int getSoundStatus(int sound) const;
private:
diff --git a/engines/scumm/player_v4a.cpp b/engines/scumm/player_v4a.cpp
index f441b3b364..7ee4ed980a 100644
--- a/engines/scumm/player_v4a.cpp
+++ b/engines/scumm/player_v4a.cpp
@@ -171,7 +171,7 @@ void Player_V4A::startSound(int nr) {
}
}
-int Player_V4A::getMusicTimer() const {
+int Player_V4A::getMusicTimer() {
// A workaround if the modplayer couldnt load the datafiles - just return a number big enough to pass all tests
if (_initState < 0)
return 2000;
diff --git a/engines/scumm/player_v4a.h b/engines/scumm/player_v4a.h
index 5f5fae6b56..15e225854c 100644
--- a/engines/scumm/player_v4a.h
+++ b/engines/scumm/player_v4a.h
@@ -49,7 +49,7 @@ public:
virtual void startSound(int sound);
virtual void stopSound(int sound);
virtual void stopAllSounds();
- virtual int getMusicTimer() const;
+ virtual int getMusicTimer();
virtual int getSoundStatus(int sound) const;
private:
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 309e79b7a0..6e9582b3a7 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -52,6 +52,7 @@
#include "scumm/he/sound_he.h"
#include "scumm/object.h"
#include "scumm/player_nes.h"
+#include "scumm/player_sid.h"
#include "scumm/player_pce.h"
#include "scumm/player_v1.h"
#include "scumm/player_v2.h"
@@ -1719,7 +1720,7 @@ void ScummEngine::setupMusic(int midi) {
} else if (_game.platform == Common::kPlatformApple2GS && _game.version == 0){
// TODO: Add support for music format
} else if (_game.platform == Common::kPlatformC64 && _game.version <= 1) {
- // TODO: Add support for music format
+ _musicEngine = new Player_SID(this, _mixer);
} else if (_game.platform == Common::kPlatformNES && _game.version == 1) {
_musicEngine = new Player_NES(this, _mixer);
} else if (_game.platform == Common::kPlatformAmiga && _game.version == 2) {