diff options
author | Travis Howell | 2009-11-26 00:31:19 +0000 |
---|---|---|
committer | Travis Howell | 2009-11-26 00:31:19 +0000 |
commit | 8783b0f2f7dbfb44c977812244a4ab4cde47a796 (patch) | |
tree | e1d210c4759d432a43d7805f20423257fb76a764 /engines/scumm | |
parent | 4e830d821cadbf1e29d2fe212a33e23770a890f8 (diff) | |
download | scummvm-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.cpp | 2 | ||||
-rw-r--r-- | engines/scumm/imuse/imuse_internal.h | 2 | ||||
-rw-r--r-- | engines/scumm/module.mk | 1 | ||||
-rw-r--r-- | engines/scumm/music.h | 2 | ||||
-rw-r--r-- | engines/scumm/player_pce.cpp | 2 | ||||
-rw-r--r-- | engines/scumm/player_pce.h | 2 | ||||
-rw-r--r-- | engines/scumm/player_sid.cpp | 1394 | ||||
-rw-r--r-- | engines/scumm/player_sid.h | 280 | ||||
-rw-r--r-- | engines/scumm/player_v1.cpp | 2 | ||||
-rw-r--r-- | engines/scumm/player_v1.h | 2 | ||||
-rw-r--r-- | engines/scumm/player_v2.cpp | 2 | ||||
-rw-r--r-- | engines/scumm/player_v2.h | 4 | ||||
-rw-r--r-- | engines/scumm/player_v2a.cpp | 2 | ||||
-rw-r--r-- | engines/scumm/player_v2a.h | 2 | ||||
-rw-r--r-- | engines/scumm/player_v2cms.cpp | 2 | ||||
-rw-r--r-- | engines/scumm/player_v3a.cpp | 2 | ||||
-rw-r--r-- | engines/scumm/player_v3a.h | 2 | ||||
-rw-r--r-- | engines/scumm/player_v4a.cpp | 2 | ||||
-rw-r--r-- | engines/scumm/player_v4a.h | 2 | ||||
-rw-r--r-- | engines/scumm/scumm.cpp | 3 |
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) { |