diff options
Diffstat (limited to 'engines/scumm/player/sid.cpp')
-rw-r--r-- | engines/scumm/player/sid.cpp | 1384 |
1 files changed, 0 insertions, 1384 deletions
diff --git a/engines/scumm/player/sid.cpp b/engines/scumm/player/sid.cpp deleted file mode 100644 index ae5346f18f..0000000000 --- a/engines/scumm/player/sid.cpp +++ /dev/null @@ -1,1384 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef DISABLE_SID - -#include "engines/engine.h" -#include "scumm/player/sid.h" -#include "scumm/scumm.h" -#include "audio/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::stopMusic_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) { - SWAP(attackReg[channel], swapAttack[swapIndex]); - SWAP(sustainReg[channel], swapSustain[swapIndex]); - } - //SWAP(vec5[channel], swapVec5[swapIndex]); // not used - //SWAP(vec19[channel], swapVec19[swapIndex]); // not used - - SWAP(chanPrio[channel], swapSongPrio[swapIndex]); - SWAP(channelMap[channel], swapVec479C[swapIndex]); - SWAP(songPosUpdateCounter[channel], swapSongPosUpdateCounter[swapIndex]); - SWAP(waveCtrlReg[channel], swapWaveCtrlReg[swapIndex]); - SWAP(songPosPtr[channel], swapSongPosPtr[swapIndex]); - SWAP(freqReg[channel], swapFreqReg[swapIndex]); - SWAP(freqDeltaCounter[channel], swapVec11[swapIndex]); - SWAP(freqDelta[channel], swapVec10[swapIndex]); - SWAP(vec20[channel], swapVec20[swapIndex]); - 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; - _sampleRate = _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->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, 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; - - Common::StackLock lock(_mutex); - - while (samplesLeft > 0) { - // update SID status after each frame - if (_cpuCyclesLeft <= 0) { - update(); - _cpuCyclesLeft = timingProps[_videoSystem].cyclesPerFrame; - } - // fetch samples - int sampleCount = _sid->updateClock(_cpuCyclesLeft, (short *)buffer, samplesLeft); - samplesLeft -= sampleCount; - buffer += sampleCount; - } - - 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, - _sampleRate); - _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); - - Common::StackLock lock(_mutex); - - if (isMusic) { - initMusic(nr); - } else { - stopSound_intern(nr); - initSound(nr); - } -} - -void Player_SID::stopSound(int nr) { - if (nr == -1) - return; - - Common::StackLock lock(_mutex); - stopSound_intern(nr); -} - -void Player_SID::stopAllSounds() { - Common::StackLock lock(_mutex); - resetPlayerState(); -} - -int Player_SID::getSoundStatus(int nr) const { - int result = 0; - - //Common::StackLock lock(_mutex); - - 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; - } - } - - return result; -} - -int Player_SID::getMusicTimer() { - int result = _music_timer; - _music_timer = 0; - return result; -} - -} // End of namespace Scumm - -#endif |