[Scummvm-cvs-logs] SF.net SVN: scummvm:[46143] scummvm/trunk

Kirben at users.sourceforge.net Kirben at users.sourceforge.net
Thu Nov 26 01:31:20 CET 2009


Revision: 46143
          http://scummvm.svn.sourceforge.net/scummvm/?rev=46143&view=rev
Author:   Kirben
Date:     2009-11-26 00:31:19 +0000 (Thu, 26 Nov 2009)

Log Message:
-----------
Add patch #2839048 - MM C64 sound/music player, with minor changes.

Modified Paths:
--------------
    scummvm/trunk/engines/scumm/imuse/imuse.cpp
    scummvm/trunk/engines/scumm/imuse/imuse_internal.h
    scummvm/trunk/engines/scumm/module.mk
    scummvm/trunk/engines/scumm/music.h
    scummvm/trunk/engines/scumm/player_pce.cpp
    scummvm/trunk/engines/scumm/player_pce.h
    scummvm/trunk/engines/scumm/player_v1.cpp
    scummvm/trunk/engines/scumm/player_v1.h
    scummvm/trunk/engines/scumm/player_v2.cpp
    scummvm/trunk/engines/scumm/player_v2.h
    scummvm/trunk/engines/scumm/player_v2a.cpp
    scummvm/trunk/engines/scumm/player_v2a.h
    scummvm/trunk/engines/scumm/player_v2cms.cpp
    scummvm/trunk/engines/scumm/player_v3a.cpp
    scummvm/trunk/engines/scumm/player_v3a.h
    scummvm/trunk/engines/scumm/player_v4a.cpp
    scummvm/trunk/engines/scumm/player_v4a.h
    scummvm/trunk/engines/scumm/scumm.cpp
    scummvm/trunk/sound/module.mk

Added Paths:
-----------
    scummvm/trunk/engines/scumm/player_sid.cpp
    scummvm/trunk/engines/scumm/player_sid.h
    scummvm/trunk/sound/softsynth/sid.cpp
    scummvm/trunk/sound/softsynth/sid.h
    scummvm/trunk/sound/softsynth/wave6581.cpp

Modified: scummvm/trunk/engines/scumm/imuse/imuse.cpp
===================================================================
--- scummvm/trunk/engines/scumm/imuse/imuse.cpp	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/imuse/imuse.cpp	2009-11-26 00:31:19 UTC (rev 46143)
@@ -537,7 +537,7 @@
 	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;

Modified: scummvm/trunk/engines/scumm/imuse/imuse_internal.h
===================================================================
--- scummvm/trunk/engines/scumm/imuse/imuse_internal.h	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/imuse/imuse_internal.h	2009-11-26 00:31:19 UTC (rev 46143)
@@ -535,7 +535,7 @@
 	void stopSound(int sound);
 	void stopAllSounds();
 	int getSoundStatus(int sound) const;
-	int getMusicTimer() const;
+	int getMusicTimer();
 
 public:
 	// Factory function

Modified: scummvm/trunk/engines/scumm/module.mk
===================================================================
--- scummvm/trunk/engines/scumm/module.mk	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/module.mk	2009-11-26 00:31:19 UTC (rev 46143)
@@ -36,6 +36,7 @@
 	player_mod.o \
 	player_nes.o \
 	player_pce.o \
+	player_sid.o \
 	player_v1.o \
 	player_v2.o \
 	player_v2a.o \

Modified: scummvm/trunk/engines/scumm/music.h
===================================================================
--- scummvm/trunk/engines/scumm/music.h	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/music.h	2009-11-26 00:31:19 UTC (rev 46143)
@@ -80,7 +80,7 @@
 	 * the music/sound.
 	 * @return the music timer
 	 */
-	virtual int  getMusicTimer() const { return 0; }
+	virtual int  getMusicTimer() { return 0; }
 };
 
 } // End of namespace Scumm

Modified: scummvm/trunk/engines/scumm/player_pce.cpp
===================================================================
--- scummvm/trunk/engines/scumm/player_pce.cpp	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_pce.cpp	2009-11-26 00:31:19 UTC (rev 46143)
@@ -751,7 +751,7 @@
 	return 0;
 }
 
-int Player_PCE::getMusicTimer() const {
+int Player_PCE::getMusicTimer() {
 	return 0;
 }
 

Modified: scummvm/trunk/engines/scumm/player_pce.h
===================================================================
--- scummvm/trunk/engines/scumm/player_pce.h	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_pce.h	2009-11-26 00:31:19 UTC (rev 46143)
@@ -83,7 +83,7 @@
 	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);

Added: scummvm/trunk/engines/scumm/player_sid.cpp
===================================================================
--- scummvm/trunk/engines/scumm/player_sid.cpp	                        (rev 0)
+++ scummvm/trunk/engines/scumm/player_sid.cpp	2009-11-26 00:31:19 UTC (rev 46143)
@@ -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


Property changes on: scummvm/trunk/engines/scumm/player_sid.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/scumm/player_sid.h
===================================================================
--- scummvm/trunk/engines/scumm/player_sid.h	                        (rev 0)
+++ scummvm/trunk/engines/scumm/player_sid.h	2009-11-26 00:31:19 UTC (rev 46143)
@@ -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


Property changes on: scummvm/trunk/engines/scumm/player_sid.h
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Modified: scummvm/trunk/engines/scumm/player_v1.cpp
===================================================================
--- scummvm/trunk/engines/scumm/player_v1.cpp	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_v1.cpp	2009-11-26 00:31:19 UTC (rev 46143)
@@ -125,7 +125,7 @@
 	_channels[i].volume = 15;
 }
 
-int Player_V1::getMusicTimer() const {
+int Player_V1::getMusicTimer() {
 	/* Do V1 games have a music timer? */
 	return 0;
 }

Modified: scummvm/trunk/engines/scumm/player_v1.h
===================================================================
--- scummvm/trunk/engines/scumm/player_v1.h	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_v1.h	2009-11-26 00:31:19 UTC (rev 46143)
@@ -41,7 +41,7 @@
 	virtual void startSound(int sound);
 	virtual void stopSound(int sound);
 	virtual void stopAllSounds();
-	virtual int  getMusicTimer() const;
+	virtual int  getMusicTimer();
 
 protected:
 	virtual void nextTick();

Modified: scummvm/trunk/engines/scumm/player_v2.cpp
===================================================================
--- scummvm/trunk/engines/scumm/player_v2.cpp	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_v2.cpp	2009-11-26 00:31:19 UTC (rev 46143)
@@ -530,7 +530,7 @@
 	memset(channel, 0, sizeof(ChannelInfo));
 }
 
-int Player_V2::getMusicTimer() const {
+int Player_V2::getMusicTimer() {
 	if (_isV3Game)
 		return _music_timer;
 	else

Modified: scummvm/trunk/engines/scumm/player_v2.h
===================================================================
--- scummvm/trunk/engines/scumm/player_v2.h	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_v2.h	2009-11-26 00:31:19 UTC (rev 46143)
@@ -80,7 +80,7 @@
 	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 @@
 	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

Modified: scummvm/trunk/engines/scumm/player_v2a.cpp
===================================================================
--- scummvm/trunk/engines/scumm/player_v2a.cpp	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_v2a.cpp	2009-11-26 00:31:19 UTC (rev 46143)
@@ -1936,7 +1936,7 @@
 	}
 }
 
-int Player_V2A::getMusicTimer() const {
+int Player_V2A::getMusicTimer() {
 	return 0;	// FIXME - need to keep track of playing music resources
 }
 

Modified: scummvm/trunk/engines/scumm/player_v2a.h
===================================================================
--- scummvm/trunk/engines/scumm/player_v2a.h	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_v2a.h	2009-11-26 00:31:19 UTC (rev 46143)
@@ -49,7 +49,7 @@
 	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:

Modified: scummvm/trunk/engines/scumm/player_v2cms.cpp
===================================================================
--- scummvm/trunk/engines/scumm/player_v2cms.cpp	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_v2cms.cpp	2009-11-26 00:31:19 UTC (rev 46143)
@@ -1095,7 +1095,7 @@
 	memset(channel, 0, sizeof(ChannelInfo));
 }
 
-int Player_V2CMS::getMusicTimer() const {
+int Player_V2CMS::getMusicTimer() {
 	if (_isV3Game)
 		return _music_timer;
 	else

Modified: scummvm/trunk/engines/scumm/player_v3a.cpp
===================================================================
--- scummvm/trunk/engines/scumm/player_v3a.cpp	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_v3a.cpp	2009-11-26 00:31:19 UTC (rev 46143)
@@ -345,7 +345,7 @@
 	}
 }
 
-int Player_V3A::getMusicTimer() const {
+int Player_V3A::getMusicTimer() {
 	return _music_timer / 30;
 }
 

Modified: scummvm/trunk/engines/scumm/player_v3a.h
===================================================================
--- scummvm/trunk/engines/scumm/player_v3a.h	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_v3a.h	2009-11-26 00:31:19 UTC (rev 46143)
@@ -48,7 +48,7 @@
 	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:

Modified: scummvm/trunk/engines/scumm/player_v4a.cpp
===================================================================
--- scummvm/trunk/engines/scumm/player_v4a.cpp	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_v4a.cpp	2009-11-26 00:31:19 UTC (rev 46143)
@@ -171,7 +171,7 @@
 	}
 }
 
-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;

Modified: scummvm/trunk/engines/scumm/player_v4a.h
===================================================================
--- scummvm/trunk/engines/scumm/player_v4a.h	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/player_v4a.h	2009-11-26 00:31:19 UTC (rev 46143)
@@ -49,7 +49,7 @@
 	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:

Modified: scummvm/trunk/engines/scumm/scumm.cpp
===================================================================
--- scummvm/trunk/engines/scumm/scumm.cpp	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/engines/scumm/scumm.cpp	2009-11-26 00:31:19 UTC (rev 46143)
@@ -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 @@
 	} 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) {

Modified: scummvm/trunk/sound/module.mk
===================================================================
--- scummvm/trunk/sound/module.mk	2009-11-26 00:09:17 UTC (rev 46142)
+++ scummvm/trunk/sound/module.mk	2009-11-26 00:31:19 UTC (rev 46143)
@@ -37,7 +37,9 @@
 	softsynth/ym2612.o \
 	softsynth/fluidsynth.o \
 	softsynth/mt32.o \
-	softsynth/pcspk.o
+	softsynth/pcspk.o \
+	softsynth/sid.o \
+	softsynth/wave6581.o
 
 ifndef USE_ARM_SOUND_ASM
 MODULE_OBJS += \

Added: scummvm/trunk/sound/softsynth/sid.cpp
===================================================================
--- scummvm/trunk/sound/softsynth/sid.cpp	                        (rev 0)
+++ scummvm/trunk/sound/softsynth/sid.cpp	2009-11-26 00:31:19 UTC (rev 46143)
@@ -0,0 +1,1426 @@
+/* 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$
+ *
+ */
+
+/*
+ *  This file is based on reSID, a MOS6581 SID emulator engine.
+ *  Copyright (C) 2004  Dag Lem <resid at nimrod.no>
+ */
+
+#include "sid.h"
+#include <math.h>
+
+namespace Resid {
+
+// Fixpoint constants (16.16 bits).
+const int SID::FIXP_SHIFT = 16;
+const int SID::FIXP_MASK = 0xffff;
+
+/*
+ * WaveformGenerator
+ */
+
+WaveformGenerator::WaveformGenerator() {
+	sync_source = this;
+
+	wave__ST = wave6581__ST;
+	wave_P_T = wave6581_P_T;
+	wave_PS_ = wave6581_PS_;
+	wave_PST = wave6581_PST;
+
+	reset();
+}
+
+void WaveformGenerator::set_sync_source(WaveformGenerator* source) {
+	sync_source = source;
+	source->sync_dest = this;
+}
+
+void WaveformGenerator::writeFREQ_LO(reg8 freq_lo) {
+	freq = freq & 0xff00 | freq_lo & 0x00ff;
+}
+
+void WaveformGenerator::writeFREQ_HI(reg8 freq_hi) {
+	freq = (freq_hi << 8) & 0xff00 | freq & 0x00ff;
+}
+
+void WaveformGenerator::writePW_LO(reg8 pw_lo) {
+	pw = pw & 0xf00 | pw_lo & 0x0ff;
+}
+
+void WaveformGenerator::writePW_HI(reg8 pw_hi) {
+	pw = (pw_hi << 8) & 0xf00 | pw & 0x0ff;
+}
+
+void WaveformGenerator::writeCONTROL_REG(reg8 control) {
+	waveform = (control >> 4) & 0x0f;
+	ring_mod = control & 0x04;
+	sync = control & 0x02;
+
+	reg8 test_next = control & 0x08;
+
+	// Test bit set.
+	if (test_next) {
+		accumulator = 0;
+		shift_register = 0;
+	}
+	// Test bit cleared.
+	else if (test) {
+		shift_register = 0x7ffff8;
+	}
+
+	test = test_next;
+
+	// The gate bit is handled by the EnvelopeGenerator.
+}
+
+reg8 WaveformGenerator::readOSC() {
+	return output() >> 4;
+}
+
+void WaveformGenerator::reset() {
+	accumulator = 0;
+	shift_register = 0x7ffff8;
+	freq = 0;
+	pw = 0;
+
+	test = 0;
+	ring_mod = 0;
+	sync = 0;
+
+	msb_rising = false;
+}
+
+RESID_INLINE void WaveformGenerator::clock(cycle_count delta_t) {
+	// No operation if test bit is set.
+	if (test) {
+		return;
+	}
+
+	reg24 accumulator_prev = accumulator;
+
+	// Calculate new accumulator value;
+	reg24 delta_accumulator = delta_t*freq;
+	accumulator += delta_accumulator;
+	accumulator &= 0xffffff;
+
+	// Check whether the MSB is set high. This is used for synchronization.
+	msb_rising = !(accumulator_prev & 0x800000) && (accumulator & 0x800000);
+
+	// Shift noise register once for each time accumulator bit 19 is set high.
+	// Bit 19 is set high each time 2^20 (0x100000) is added to the accumulator.
+	reg24 shift_period = 0x100000;
+
+	while (delta_accumulator) {
+		if (delta_accumulator < shift_period) {
+			shift_period = delta_accumulator;
+			// Determine whether bit 19 is set on the last period.
+			// NB! Requires two's complement integer.
+			if (shift_period <= 0x080000) {
+				// Check for flip from 0 to 1.
+				if (((accumulator - shift_period) & 0x080000) || !(accumulator & 0x080000))
+				{
+					break;
+				}
+			}
+			else {
+				// Check for flip from 0 (to 1 or via 1 to 0) or from 1 via 0 to 1.
+				if (((accumulator - shift_period) & 0x080000) && !(accumulator & 0x080000))
+				{
+					break;
+				}
+			}
+		}
+
+		// Shift the noise/random register.
+		// NB! The shift is actually delayed 2 cycles, this is not modeled.
+		reg24 bit0 = ((shift_register >> 22) ^ (shift_register >> 17)) & 0x1;
+		shift_register <<= 1;
+		shift_register &= 0x7fffff;
+		shift_register |= bit0;
+
+		delta_accumulator -= shift_period;
+	}
+}
+
+
+/**
+ * Synchronize oscillators.
+ * This must be done after all the oscillators have been clock()'ed since the
+ * oscillators operate in parallel.
+ * Note that the oscillators must be clocked exactly on the cycle when the
+ * MSB is set high for hard sync to operate correctly. See SID::clock().
+ */
+RESID_INLINE void WaveformGenerator::synchronize() {
+	// A special case occurs when a sync source is synced itself on the same
+	// cycle as when its MSB is set high. In this case the destination will
+	// not be synced. This has been verified by sampling OSC3.
+	if (msb_rising && sync_dest->sync && !(sync && sync_source->msb_rising)) {
+		sync_dest->accumulator = 0;
+	}
+}
+
+
+/*
+ * Output functions
+ */
+
+// No waveform: Zero output.
+RESID_INLINE reg12 WaveformGenerator::output____() {
+	return 0x000;
+}
+
+// Triangle:
+RESID_INLINE reg12 WaveformGenerator::output___T() {
+	reg24 msb = (ring_mod ? accumulator ^ sync_source->accumulator : accumulator)
+		& 0x800000;
+	return ((msb ? ~accumulator : accumulator) >> 11) & 0xfff;
+}
+
+// Sawtooth:
+RESID_INLINE reg12 WaveformGenerator::output__S_() {
+	return accumulator >> 12;
+}
+
+// Pulse:
+RESID_INLINE reg12 WaveformGenerator::output_P__() {
+	return (test || (accumulator >> 12) >= pw) ? 0xfff : 0x000;
+}
+
+// Noise:
+RESID_INLINE reg12 WaveformGenerator::outputN___() {
+	return
+		((shift_register & 0x400000) >> 11) |
+		((shift_register & 0x100000) >> 10) |
+		((shift_register & 0x010000) >> 7) |
+		((shift_register & 0x002000) >> 5) |
+		((shift_register & 0x000800) >> 4) |
+		((shift_register & 0x000080) >> 1) |
+		((shift_register & 0x000010) << 1) |
+		((shift_register & 0x000004) << 2);
+}
+
+// Combined waveforms:
+
+RESID_INLINE reg12 WaveformGenerator::output__ST() {
+	return wave__ST[output__S_()] << 4;
+}
+
+RESID_INLINE reg12 WaveformGenerator::output_P_T() {
+	return (wave_P_T[output___T() >> 1] << 4) & output_P__();
+}
+
+RESID_INLINE reg12 WaveformGenerator::output_PS_() {
+	return (wave_PS_[output__S_()] << 4) & output_P__();
+}
+
+RESID_INLINE reg12 WaveformGenerator::output_PST() {
+	return (wave_PST[output__S_()] << 4) & output_P__();
+}
+
+// Combined waveforms including noise:
+
+RESID_INLINE reg12 WaveformGenerator::outputN__T() {
+	return 0;
+}
+
+RESID_INLINE reg12 WaveformGenerator::outputN_S_() {
+	return 0;
+}
+
+RESID_INLINE reg12 WaveformGenerator::outputN_ST() {
+	return 0;
+}
+
+RESID_INLINE reg12 WaveformGenerator::outputNP__() {
+	return 0;
+}
+
+RESID_INLINE reg12 WaveformGenerator::outputNP_T() {
+	return 0;
+}
+
+RESID_INLINE reg12 WaveformGenerator::outputNPS_() {
+	return 0;
+}
+
+RESID_INLINE reg12 WaveformGenerator::outputNPST() {
+	return 0;
+}
+
+/**
+ * Select one of 16 possible combinations of waveforms.
+ */
+RESID_INLINE reg12 WaveformGenerator::output() {
+	// It may seem cleaner to use an array of member functions to return
+	// waveform output; however a switch with inline functions is faster.
+
+	switch (waveform) {
+	default:
+	case 0x0:
+		return output____();
+	case 0x1:
+		return output___T();
+	case 0x2:
+		return output__S_();
+	case 0x3:
+		return output__ST();
+	case 0x4:
+		return output_P__();
+	case 0x5:
+		return output_P_T();
+	case 0x6:
+		return output_PS_();
+	case 0x7:
+		return output_PST();
+	case 0x8:
+		return outputN___();
+	case 0x9:
+		return outputN__T();
+	case 0xa:
+		return outputN_S_();
+	case 0xb:
+		return outputN_ST();
+	case 0xc:
+		return outputNP__();
+	case 0xd:
+		return outputNP_T();
+	case 0xe:
+		return outputNPS_();
+	case 0xf:
+		return outputNPST();
+	}
+}
+
+/*
+ * Our objective is to construct a smooth interpolating single-valued function
+ * y = f(x).
+ * Our approach is to approximate the properties of Catmull-Rom splines for
+ * piecewice cubic polynomials.
+ */
+
+/**
+ * Calculation of coefficients.
+ */
+inline void cubic_coefficients(double x1, double y1, double x2, double y2,
+						double k1, double k2,
+						double& a, double& b, double& c, double& d)
+{
+	double dx = x2 - x1, dy = y2 - y1;
+
+	a = ((k1 + k2) - 2*dy/dx)/(dx*dx);
+	b = ((k2 - k1)/dx - 3*(x1 + x2)*a)/2;
+	c = k1 - (3*x1*a + 2*b)*x1;
+	d = y1 - ((x1*a + b)*x1 + c)*x1;
+}
+
+/**
+ * Evaluation of cubic polynomial by forward differencing.
+ */
+template<class PointPlotter>
+inline void interpolate_segment(double x1, double y1, double x2, double y2,
+								double k1, double k2,
+								PointPlotter plot, double res)
+{
+	double a, b, c, d;
+	cubic_coefficients(x1, y1, x2, y2, k1, k2, a, b, c, d);
+
+	double y = ((a*x1 + b)*x1 + c)*x1 + d;
+	double dy = (3*a*(x1 + res) + 2*b)*x1*res + ((a*res + b)*res + c)*res;
+	double d2y = (6*a*(x1 + res) + 2*b)*res*res;
+	double d3y = 6*a*res*res*res;
+
+	// Calculate each point.
+	for (double x = x1; x <= x2; x += res) {
+		plot(x, y);
+		y += dy; dy += d2y; d2y += d3y;
+	}
+}
+
+template<class PointIter>
+inline double x(PointIter p) {
+	return (*p)[0];
+}
+
+template<class PointIter>
+inline double y(PointIter p) {
+	return (*p)[1];
+}
+
+/**
+ * Evaluation of complete interpolating function.
+ * Note that since each curve segment is controlled by four points, the
+ * end points will not be interpolated. If extra control points are not
+ * desirable, the end points can simply be repeated to ensure interpolation.
+ * Note also that points of non-differentiability and discontinuity can be
+ * introduced by repeating points.
+ */
+template<class PointIter, class PointPlotter>
+inline void interpolate(PointIter p0, PointIter pn, PointPlotter plot, double res) {
+	double k1, k2;
+
+	// Set up points for first curve segment.
+	PointIter p1 = p0; ++p1;
+	PointIter p2 = p1; ++p2;
+	PointIter p3 = p2; ++p3;
+
+	// Draw each curve segment.
+	for (; p2 != pn; ++p0, ++p1, ++p2, ++p3) {
+		// p1 and p2 equal; single point.
+		if (x(p1) == x(p2)) {
+			continue;
+		}
+		// Both end points repeated; straight line.
+		if (x(p0) == x(p1) && x(p2) == x(p3)) {
+			k1 = k2 = (y(p2) - y(p1))/(x(p2) - x(p1));
+		}
+		// p0 and p1 equal; use f''(x1) = 0.
+		else if (x(p0) == x(p1)) {
+			k2 = (y(p3) - y(p1))/(x(p3) - x(p1));
+			k1 = (3*(y(p2) - y(p1))/(x(p2) - x(p1)) - k2)/2;
+		}
+		// p2 and p3 equal; use f''(x2) = 0.
+		else if (x(p2) == x(p3)) {
+			k1 = (y(p2) - y(p0))/(x(p2) - x(p0));
+			k2 = (3*(y(p2) - y(p1))/(x(p2) - x(p1)) - k1)/2;
+		}
+		// Normal curve.
+		else {
+			k1 = (y(p2) - y(p0))/(x(p2) - x(p0));
+			k2 = (y(p3) - y(p1))/(x(p3) - x(p1));
+		}
+
+		interpolate_segment(x(p1), y(p1), x(p2), y(p2), k1, k2, plot, res);
+	}
+}
+
+/**
+ * Class for plotting integers into an array.
+ */
+template<class F>
+class PointPlotter {
+protected:
+	F* f;
+
+public:
+	PointPlotter(F* arr) : f(arr) {
+	}
+
+	void operator ()(double x, double y) {
+		// Clamp negative values to zero.
+		if (y < 0) {
+			y = 0;
+		}
+
+		f[F(x)] = F(y);
+	}
+};
+
+fc_point Filter::f0_points_6581[] = {
+	//  FC      f         FCHI FCLO
+	// ----------------------------
+	{    0,   220 },   // 0x00      - repeated end point
+	{    0,   220 },   // 0x00
+	{  128,   230 },   // 0x10
+	{  256,   250 },   // 0x20
+	{  384,   300 },   // 0x30
+	{  512,   420 },   // 0x40
+	{  640,   780 },   // 0x50
+	{  768,  1600 },   // 0x60
+	{  832,  2300 },   // 0x68
+	{  896,  3200 },   // 0x70
+	{  960,  4300 },   // 0x78
+	{  992,  5000 },   // 0x7c
+	{ 1008,  5400 },   // 0x7e
+	{ 1016,  5700 },   // 0x7f
+	{ 1023,  6000 },   // 0x7f 0x07
+	{ 1023,  6000 },   // 0x7f 0x07 - discontinuity
+	{ 1024,  4600 },   // 0x80      -
+	{ 1024,  4600 },   // 0x80
+	{ 1032,  4800 },   // 0x81
+	{ 1056,  5300 },   // 0x84
+	{ 1088,  6000 },   // 0x88
+	{ 1120,  6600 },   // 0x8c
+	{ 1152,  7200 },   // 0x90
+	{ 1280,  9500 },   // 0xa0
+	{ 1408, 12000 },   // 0xb0
+	{ 1536, 14500 },   // 0xc0
+	{ 1664, 16000 },   // 0xd0
+	{ 1792, 17100 },   // 0xe0
+	{ 1920, 17700 },   // 0xf0
+	{ 2047, 18000 },   // 0xff 0x07
+	{ 2047, 18000 }    // 0xff 0x07 - repeated end point
+};
+
+
+/*
+ * Filter
+ */
+
+Filter::Filter() {
+	fc = 0;
+
+	res = 0;
+
+	filt = 0;
+
+	voice3off = 0;
+
+	hp_bp_lp = 0;
+
+	vol = 0;
+
+	// State of filter.
+	Vhp = 0;
+	Vbp = 0;
+	Vlp = 0;
+	Vnf = 0;
+
+	enable_filter(true);
+
+	// Create mappings from FC to cutoff frequency.
+	interpolate(f0_points_6581, f0_points_6581
+		+ sizeof(f0_points_6581)/sizeof(*f0_points_6581) - 1,
+		PointPlotter<sound_sample>(f0_6581), 1.0);
+
+	mixer_DC = -0xfff*0xff/18 >> 7;
+
+	f0 = f0_6581;
+	f0_points = f0_points_6581;
+	f0_count = sizeof(f0_points_6581)/sizeof(*f0_points_6581);
+
+	set_w0();
+	set_Q();
+}
+
+void Filter::enable_filter(bool enable) {
+	enabled = enable;
+}
+
+void Filter::reset(){
+	fc = 0;
+
+	res = 0;
+
+	filt = 0;
+
+	voice3off = 0;
+
+	hp_bp_lp = 0;
+
+	vol = 0;
+
+	// State of filter.
+	Vhp = 0;
+	Vbp = 0;
+	Vlp = 0;
+	Vnf = 0;
+
+	set_w0();
+	set_Q();
+}
+
+void Filter::writeFC_LO(reg8 fc_lo) {
+	fc = fc & 0x7f8 | fc_lo & 0x007;
+	set_w0();
+}
+
+void Filter::writeFC_HI(reg8 fc_hi) {
+	fc = (fc_hi << 3) & 0x7f8 | fc & 0x007;
+	set_w0();
+}
+
+void Filter::writeRES_FILT(reg8 res_filt) {
+	res = (res_filt >> 4) & 0x0f;
+	set_Q();
+
+	filt = res_filt & 0x0f;
+}
+
+void Filter::writeMODE_VOL(reg8 mode_vol) {
+	voice3off = mode_vol & 0x80;
+
+	hp_bp_lp = (mode_vol >> 4) & 0x07;
+
+	vol = mode_vol & 0x0f;
+}
+
+// Set filter cutoff frequency.
+void Filter::set_w0() {
+	const double pi = 3.1415926535897932385;
+
+	// Multiply with 1.048576 to facilitate division by 1 000 000 by right-
+	// shifting 20 times (2 ^ 20 = 1048576).
+	w0 = static_cast<sound_sample>(2*pi*f0[fc]*1.048576);
+
+	// Limit f0 to 16kHz to keep 1 cycle filter stable.
+	const sound_sample w0_max_1 = static_cast<sound_sample>(2*pi*16000*1.048576);
+	w0_ceil_1 = w0 <= w0_max_1 ? w0 : w0_max_1;
+
+	// Limit f0 to 4kHz to keep delta_t cycle filter stable.
+	const sound_sample w0_max_dt = static_cast<sound_sample>(2*pi*4000*1.048576);
+	w0_ceil_dt = w0 <= w0_max_dt ? w0 : w0_max_dt;
+}
+
+// Set filter resonance.
+void Filter::set_Q() {
+	// Q is controlled linearly by res. Q has approximate range [0.707, 1.7].
+	// As resonance is increased, the filter must be clocked more often to keep
+	// stable.
+
+	// The coefficient 1024 is dispensed of later by right-shifting 10 times
+	// (2 ^ 10 = 1024).
+	_1024_div_Q = static_cast<sound_sample>(1024.0/(0.707 + 1.0*res/0x0f));
+}
+
+RESID_INLINE void Filter::clock(cycle_count delta_t,
+				   sound_sample voice1,
+				   sound_sample voice2,
+				   sound_sample voice3)
+{
+	// Scale each voice down from 20 to 13 bits.
+	voice1 >>= 7;
+	voice2 >>= 7;
+
+	// NB! Voice 3 is not silenced by voice3off if it is routed through
+	// the filter.
+	if (voice3off && !(filt & 0x04)) {
+		voice3 = 0;
+	}
+	else {
+		voice3 >>= 7;
+	}
+
+	// Enable filter on/off.
+	// This is not really part of SID, but is useful for testing.
+	// On slow CPUs it may be necessary to bypass the filter to lower the CPU
+	// load.
+	if (!enabled) {
+		Vnf = voice1 + voice2 + voice3;
+		Vhp = Vbp = Vlp = 0;
+		return;
+	}
+
+	// Route voices into or around filter.
+	// The code below is expanded to a switch for faster execution.
+	// (filt1 ? Vi : Vnf) += voice1;
+	// (filt2 ? Vi : Vnf) += voice2;
+	// (filt3 ? Vi : Vnf) += voice3;
+
+	sound_sample Vi;
+
+	switch (filt) {
+	default:
+	case 0x0:
+		Vi = 0;
+		Vnf = voice1 + voice2 + voice3;
+		break;
+	case 0x1:
+		Vi = voice1;
+		Vnf = voice2 + voice3;
+		break;
+	case 0x2:
+		Vi = voice2;
+		Vnf = voice1 + voice3;
+		break;
+	case 0x3:
+		Vi = voice1 + voice2;
+		Vnf = voice3;
+		break;
+	case 0x4:
+		Vi = voice3;
+		Vnf = voice1 + voice2;
+		break;
+	case 0x5:
+		Vi = voice1 + voice3;
+		Vnf = voice2;
+		break;
+	case 0x6:
+		Vi = voice2 + voice3;
+		Vnf = voice1;
+		break;
+	case 0x7:
+		Vi = voice1 + voice2 + voice3;
+		Vnf = 0;
+		break;
+	case 0x8:
+		Vi = 0;
+		Vnf = voice1 + voice2 + voice3;
+		break;
+	case 0x9:
+		Vi = voice1;
+		Vnf = voice2 + voice3;
+		break;
+	case 0xa:
+		Vi = voice2;
+		Vnf = voice1 + voice3;
+		break;
+	case 0xb:
+		Vi = voice1 + voice2;
+		Vnf = voice3;
+		break;
+	case 0xc:
+		Vi = voice3;
+		Vnf = voice1 + voice2;
+		break;
+	case 0xd:
+		Vi = voice1 + voice3;
+		Vnf = voice2;
+		break;
+	case 0xe:
+		Vi = voice2 + voice3;
+		Vnf = voice1;
+		break;
+	case 0xf:
+		Vi = voice1 + voice2 + voice3;
+		Vnf = 0;
+		break;
+	}
+
+	// Maximum delta cycles for the filter to work satisfactorily under current
+	// cutoff frequency and resonance constraints is approximately 8.
+	cycle_count delta_t_flt = 8;
+
+	while (delta_t) {
+		if (delta_t < delta_t_flt) {
+			delta_t_flt = delta_t;
+		}
+
+		// delta_t is converted to seconds given a 1MHz clock by dividing
+		// with 1 000 000. This is done in two operations to avoid integer
+		// multiplication overflow.
+
+		// Calculate filter outputs.
+		// Vhp = Vbp/Q - Vlp - Vi;
+		// dVbp = -w0*Vhp*dt;
+		// dVlp = -w0*Vbp*dt;
+		sound_sample w0_delta_t = w0_ceil_dt*delta_t_flt >> 6;
+
+		sound_sample dVbp = (w0_delta_t*Vhp >> 14);
+		sound_sample dVlp = (w0_delta_t*Vbp >> 14);
+		Vbp -= dVbp;
+		Vlp -= dVlp;
+		Vhp = (Vbp*_1024_div_Q >> 10) - Vlp - Vi;
+
+		delta_t -= delta_t_flt;
+	}
+}
+
+RESID_INLINE sound_sample Filter::output() {
+	// This is handy for testing.
+	if (!enabled) {
+		return (Vnf + mixer_DC)*static_cast<sound_sample>(vol);
+	}
+
+	// Mix highpass, bandpass, and lowpass outputs. The sum is not
+	// weighted, this can be confirmed by sampling sound output for
+	// e.g. bandpass, lowpass, and bandpass+lowpass from a SID chip.
+
+	// The code below is expanded to a switch for faster execution.
+	// if (hp) Vf += Vhp;
+	// if (bp) Vf += Vbp;
+	// if (lp) Vf += Vlp;
+
+	sound_sample Vf;
+
+	switch (hp_bp_lp) {
+	default:
+	case 0x0:
+		Vf = 0;
+		break;
+	case 0x1:
+		Vf = Vlp;
+		break;
+	case 0x2:
+		Vf = Vbp;
+		break;
+	case 0x3:
+		Vf = Vlp + Vbp;
+		break;
+	case 0x4:
+		Vf = Vhp;
+		break;
+	case 0x5:
+		Vf = Vlp + Vhp;
+		break;
+	case 0x6:
+		Vf = Vbp + Vhp;
+		break;
+	case 0x7:
+		Vf = Vlp + Vbp + Vhp;
+		break;
+	}
+
+	// Sum non-filtered and filtered output.
+	// Multiply the sum with volume.
+	return (Vnf + Vf + mixer_DC)*static_cast<sound_sample>(vol);
+}
+
+
+/*
+ * EnvelopeGenerator
+ */
+
+EnvelopeGenerator::EnvelopeGenerator() {
+	reset();
+}
+
+void EnvelopeGenerator::reset() {
+	envelope_counter = 0;
+
+	attack = 0;
+	decay = 0;
+	sustain = 0;
+	release = 0;
+
+	gate = 0;
+
+	rate_counter = 0;
+	exponential_counter = 0;
+	exponential_counter_period = 1;
+
+	state = RELEASE;
+	rate_period = rate_counter_period[release];
+	hold_zero = true;
+}
+
+reg16 EnvelopeGenerator::rate_counter_period[] = {
+	9,  //   2ms*1.0MHz/256 =     7.81
+	32,  //   8ms*1.0MHz/256 =    31.25
+	63,  //  16ms*1.0MHz/256 =    62.50
+	95,  //  24ms*1.0MHz/256 =    93.75
+	149,  //  38ms*1.0MHz/256 =   148.44
+	220,  //  56ms*1.0MHz/256 =   218.75
+	267,  //  68ms*1.0MHz/256 =   265.63
+	313,  //  80ms*1.0MHz/256 =   312.50
+	392,  // 100ms*1.0MHz/256 =   390.63
+	977,  // 250ms*1.0MHz/256 =   976.56
+	1954,  // 500ms*1.0MHz/256 =  1953.13
+	3126,  // 800ms*1.0MHz/256 =  3125.00
+	3907,  //   1 s*1.0MHz/256 =  3906.25
+	11720,  //   3 s*1.0MHz/256 = 11718.75
+	19532,  //   5 s*1.0MHz/256 = 19531.25
+	31251   //   8 s*1.0MHz/256 = 31250.00
+};
+
+
+reg8 EnvelopeGenerator::sustain_level[] = {
+	0x00,
+	0x11,
+	0x22,
+	0x33,
+	0x44,
+	0x55,
+	0x66,
+	0x77,
+	0x88,
+	0x99,
+	0xaa,
+	0xbb,
+	0xcc,
+	0xdd,
+	0xee,
+	0xff,
+};
+
+void EnvelopeGenerator::writeCONTROL_REG(reg8 control) {
+	reg8 gate_next = control & 0x01;
+
+	// The rate counter is never reset, thus there will be a delay before the
+	// envelope counter starts counting up (attack) or down (release).
+
+	// Gate bit on: Start attack, decay, sustain.
+	if (!gate && gate_next) {
+		state = ATTACK;
+		rate_period = rate_counter_period[attack];
+
+		// Switching to attack state unlocks the zero freeze.
+		hold_zero = false;
+	}
+	// Gate bit off: Start release.
+	else if (gate && !gate_next) {
+		state = RELEASE;
+		rate_period = rate_counter_period[release];
+	}
+
+	gate = gate_next;
+}
+
+void EnvelopeGenerator::writeATTACK_DECAY(reg8 attack_decay) {
+	attack = (attack_decay >> 4) & 0x0f;
+	decay = attack_decay & 0x0f;
+	if (state == ATTACK) {
+		rate_period = rate_counter_period[attack];
+	}
+	else if (state == DECAY_SUSTAIN) {
+		rate_period = rate_counter_period[decay];
+	}
+}
+
+void EnvelopeGenerator::writeSUSTAIN_RELEASE(reg8 sustain_release) {
+	sustain = (sustain_release >> 4) & 0x0f;
+	release = sustain_release & 0x0f;
+	if (state == RELEASE) {
+		rate_period = rate_counter_period[release];
+	}
+}
+
+reg8 EnvelopeGenerator::readENV() {
+	return output();
+}
+
+RESID_INLINE void EnvelopeGenerator::clock(cycle_count delta_t) {
+	// Check for ADSR delay bug.
+	// If the rate counter comparison value is set below the current value of the
+	// rate counter, the counter will continue counting up until it wraps around
+	// to zero at 2^15 = 0x8000, and then count rate_period - 1 before the
+	// envelope can finally be stepped.
+	// This has been verified by sampling ENV3.
+	//
+
+	// NB! This requires two's complement integer.
+	int rate_step = rate_period - rate_counter;
+	if (rate_step <= 0) {
+		rate_step += 0x7fff;
+	}
+
+	while (delta_t) {
+		if (delta_t < rate_step) {
+			rate_counter += delta_t;
+			if (rate_counter & 0x8000) {
+				++rate_counter &= 0x7fff;
+			}
+			return;
+		}
+
+		rate_counter = 0;
+		delta_t -= rate_step;
+
+		// The first envelope step in the attack state also resets the exponential
+		// counter. This has been verified by sampling ENV3.
+		//
+		if (state == ATTACK	|| ++exponential_counter == exponential_counter_period)
+		{
+			exponential_counter = 0;
+
+			// Check whether the envelope counter is frozen at zero.
+			if (hold_zero) {
+				rate_step = rate_period;
+				continue;
+			}
+
+			switch (state) {
+			case ATTACK:
+				// The envelope counter can flip from 0xff to 0x00 by changing state to
+				// release, then to attack. The envelope counter is then frozen at
+				// zero; to unlock this situation the state must be changed to release,
+				// then to attack. This has been verified by sampling ENV3.
+				//
+				++envelope_counter &= 0xff;
+				if (envelope_counter == 0xff) {
+					state = DECAY_SUSTAIN;
+					rate_period = rate_counter_period[decay];
+				}
+				break;
+			case DECAY_SUSTAIN:
+				if (envelope_counter != sustain_level[sustain]) {
+					--envelope_counter;
+				}
+				break;
+			case RELEASE:
+				// The envelope counter can flip from 0x00 to 0xff by changing state to
+				// attack, then to release. The envelope counter will then continue
+				// counting down in the release state.
+				// This has been verified by sampling ENV3.
+				// NB! The operation below requires two's complement integer.
+				//
+				--envelope_counter &= 0xff;
+				break;
+			}
+
+			// Check for change of exponential counter period.
+			switch (envelope_counter) {
+			case 0xff:
+				exponential_counter_period = 1;
+				break;
+			case 0x5d:
+				exponential_counter_period = 2;
+				break;
+			case 0x36:
+				exponential_counter_period = 4;
+				break;
+			case 0x1a:
+				exponential_counter_period = 8;
+				break;
+			case 0x0e:
+				exponential_counter_period = 16;
+				break;
+			case 0x06:
+				exponential_counter_period = 30;
+				break;
+			case 0x00:
+				exponential_counter_period = 1;
+
+				// When the envelope counter is changed to zero, it is frozen at zero.
+				// This has been verified by sampling ENV3.
+				hold_zero = true;
+				break;
+			}
+		}
+
+		rate_step = rate_period;
+	}
+}
+
+RESID_INLINE reg8 EnvelopeGenerator::output() {
+	return envelope_counter;
+}
+
+
+/*
+ * ExternalFilter
+ */
+
+ExternalFilter::ExternalFilter() {
+	reset();
+	enable_filter(true);
+	set_sampling_parameter(15915.6);
+	mixer_DC = ((((0x800 - 0x380) + 0x800)*0xff*3 - 0xfff*0xff/18) >> 7)*0x0f;
+}
+
+void ExternalFilter::enable_filter(bool enable) {
+	enabled = enable;
+}
+
+void ExternalFilter::set_sampling_parameter(double pass_freq) {
+	static const double pi = 3.1415926535897932385;
+
+	w0hp = 105;
+	w0lp = (sound_sample) (pass_freq * (2.0 * pi * 1.048576));
+	if (w0lp > 104858)
+		w0lp = 104858;
+}
+
+void ExternalFilter::reset() {
+	// State of filter.
+	Vlp = 0;
+	Vhp = 0;
+	Vo = 0;
+}
+
+RESID_INLINE void ExternalFilter::clock(cycle_count delta_t, sound_sample Vi) {
+	// This is handy for testing.
+	if (!enabled) {
+		// Remove maximum DC level since there is no filter to do it.
+		Vlp = Vhp = 0;
+		Vo = Vi - mixer_DC;
+		return;
+	}
+
+	// Maximum delta cycles for the external filter to work satisfactorily
+	// is approximately 8.
+	cycle_count delta_t_flt = 8;
+
+	while (delta_t) {
+		if (delta_t < delta_t_flt) {
+			delta_t_flt = delta_t;
+		}
+
+		// delta_t is converted to seconds given a 1MHz clock by dividing
+		// with 1 000 000.
+
+		// Calculate filter outputs.
+		// Vo  = Vlp - Vhp;
+		// Vlp = Vlp + w0lp*(Vi - Vlp)*delta_t;
+		// Vhp = Vhp + w0hp*(Vlp - Vhp)*delta_t;
+
+		sound_sample dVlp = (w0lp*delta_t_flt >> 8)*(Vi - Vlp) >> 12;
+		sound_sample dVhp = w0hp*delta_t_flt*(Vlp - Vhp) >> 20;
+		Vo = Vlp - Vhp;
+		Vlp += dVlp;
+		Vhp += dVhp;
+
+		delta_t -= delta_t_flt;
+	}
+}
+
+RESID_INLINE sound_sample ExternalFilter::output() {
+	return Vo;
+}
+
+
+/*
+ * Voice
+ */
+
+Voice::Voice() {
+	wave_zero = 0x380;
+	voice_DC = 0x800*0xff;
+}
+
+void Voice::set_sync_source(Voice* source) {
+	wave.set_sync_source(&source->wave);
+}
+
+void Voice::writeCONTROL_REG(reg8 control) {
+	wave.writeCONTROL_REG(control);
+	envelope.writeCONTROL_REG(control);
+}
+
+void Voice::reset() {
+	wave.reset();
+	envelope.reset();
+}
+
+
+/*
+ * SID
+ */
+
+SID::SID() {
+	voice[0].set_sync_source(&voice[2]);
+	voice[1].set_sync_source(&voice[0]);
+	voice[2].set_sync_source(&voice[1]);
+
+	set_sampling_parameters(985248, 44100);
+
+	bus_value = 0;
+	bus_value_ttl = 0;
+}
+
+SID::~SID() {}
+
+void SID::reset() {
+	for (int i = 0; i < 3; i++) {
+		voice[i].reset();
+	}
+	filter.reset();
+	extfilt.reset();
+
+	bus_value = 0;
+	bus_value_ttl = 0;
+}
+
+int SID::output() {
+	const int range = 1 << 16;
+	const int half = range >> 1;
+	int sample = extfilt.output()/((4095*255 >> 7)*3*15*2/range);
+	if (sample >= half) {
+		return half - 1;
+	}
+	if (sample < -half) {
+		return -half;
+	}
+	return sample;
+}
+
+
+/**
+ * Read registers.
+ *
+ * Reading a write only register returns the last byte written to any SID
+ * register. The individual bits in this value start to fade down towards
+ * zero after a few cycles. All bits reach zero within approximately
+ * $2000 - $4000 cycles.
+ * It has been claimed that this fading happens in an orderly fashion, however
+ * sampling of write only registers reveals that this is not the case.
+ * NB! This is not correctly modeled.
+ * The actual use of write only registers has largely been made in the belief
+ * that all SID registers are readable. To support this belief the read
+ * would have to be done immediately after a write to the same register
+ * (remember that an intermediate write to another register would yield that
+ * value instead). With this in mind we return the last value written to
+ * any SID register for $2000 cycles without modeling the bit fading.
+ */
+reg8 SID::read(reg8 offset) {
+	switch (offset) {
+		case 0x19:
+		case 0x1a:
+			return 0; //readPOT();
+		case 0x1b:
+			return voice[2].wave.readOSC();
+		case 0x1c:
+			return voice[2].envelope.readENV();
+		default:
+			return bus_value;
+	}
+}
+
+void SID::write(reg8 offset, reg8 value) {
+	bus_value = value;
+	bus_value_ttl = 0x2000;
+
+	switch (offset) {
+	  case 0x00:
+		  voice[0].wave.writeFREQ_LO(value);
+		  break;
+	  case 0x01:
+		  voice[0].wave.writeFREQ_HI(value);
+		  break;
+	  case 0x02:
+		  voice[0].wave.writePW_LO(value);
+		  break;
+	  case 0x03:
+		  voice[0].wave.writePW_HI(value);
+		  break;
+	  case 0x04:
+		  voice[0].writeCONTROL_REG(value);
+		  break;
+	  case 0x05:
+		  voice[0].envelope.writeATTACK_DECAY(value);
+		  break;
+	  case 0x06:
+		  voice[0].envelope.writeSUSTAIN_RELEASE(value);
+		  break;
+	  case 0x07:
+		  voice[1].wave.writeFREQ_LO(value);
+		  break;
+	  case 0x08:
+		  voice[1].wave.writeFREQ_HI(value);
+		  break;
+	  case 0x09:
+		  voice[1].wave.writePW_LO(value);
+		  break;
+	  case 0x0a:
+		  voice[1].wave.writePW_HI(value);
+		  break;
+	  case 0x0b:
+		  voice[1].writeCONTROL_REG(value);
+		  break;
+	  case 0x0c:
+		  voice[1].envelope.writeATTACK_DECAY(value);
+		  break;
+	  case 0x0d:
+		  voice[1].envelope.writeSUSTAIN_RELEASE(value);
+		  break;
+	  case 0x0e:
+		  voice[2].wave.writeFREQ_LO(value);
+		  break;
+	  case 0x0f:
+		  voice[2].wave.writeFREQ_HI(value);
+		  break;
+	  case 0x10:
+		  voice[2].wave.writePW_LO(value);
+		  break;
+	  case 0x11:
+		  voice[2].wave.writePW_HI(value);
+		  break;
+	  case 0x12:
+		  voice[2].writeCONTROL_REG(value);
+		  break;
+	  case 0x13:
+		  voice[2].envelope.writeATTACK_DECAY(value);
+		  break;
+	  case 0x14:
+		  voice[2].envelope.writeSUSTAIN_RELEASE(value);
+		  break;
+	  case 0x15:
+		  filter.writeFC_LO(value);
+		  break;
+	  case 0x16:
+		  filter.writeFC_HI(value);
+		  break;
+	  case 0x17:
+		  filter.writeRES_FILT(value);
+		  break;
+	  case 0x18:
+		  filter.writeMODE_VOL(value);
+		  break;
+	  default:
+		  break;
+	}
+}
+
+void SID::enable_filter(bool enable) {
+	filter.enable_filter(enable);
+}
+
+void SID::enable_external_filter(bool enable) {
+	extfilt.enable_filter(enable);
+}
+
+
+/**
+ * Setting of SID sampling parameters.
+ *
+ * Use a clock freqency of 985248Hz for PAL C64, 1022730Hz for NTSC C64.
+ * The default end of passband frequency is pass_freq = 0.9*sample_freq/2
+ * for sample frequencies up to ~ 44.1kHz, and 20kHz for higher sample
+ * frequencies.
+ *
+ * For resampling, the ratio between the clock frequency and the sample
+ * frequency is limited as follows:
+ *   125*clock_freq/sample_freq < 16384
+ * E.g. provided a clock frequency of ~ 1MHz, the sample frequency can not
+ * be set lower than ~ 8kHz. A lower sample frequency would make the
+ * resampling code overfill its 16k sample ring buffer.
+ * 
+ * The end of passband frequency is also limited:
+ *   pass_freq <= 0.9*sample_freq/2
+ *
+ * E.g. for a 44.1kHz sampling rate the end of passband frequency is limited
+ * to slightly below 20kHz. This constraint ensures that the FIR table is
+ * not overfilled.
+ */
+bool SID::set_sampling_parameters(double clock_freq,
+								  double sample_freq, double pass_freq,
+								  double filter_scale)
+{
+	// The default passband limit is 0.9*sample_freq/2 for sample
+	// frequencies below ~ 44.1kHz, and 20kHz for higher sample frequencies.
+	if (pass_freq < 0) {
+		pass_freq = 20000;
+		if (2*pass_freq/sample_freq >= 0.9) {
+			pass_freq = 0.9*sample_freq/2;
+		}
+	}
+	// Check whether the FIR table would overfill.
+	else if (pass_freq > 0.9*sample_freq/2) {
+		return false;
+	}
+
+	// The filter scaling is only included to avoid clipping, so keep
+	// it sane.
+	if (filter_scale < 0.9 || filter_scale > 1.0) {
+		return false;
+	}
+
+	// Set the external filter to the pass freq
+	extfilt.set_sampling_parameter (pass_freq);
+	clock_frequency = clock_freq;
+
+	cycles_per_sample =
+		cycle_count(clock_freq/sample_freq*(1 << FIXP_SHIFT) + 0.5);
+
+	sample_offset = 0;
+	sample_prev = 0;
+
+	return true;
+}
+
+void SID::clock(cycle_count delta_t) {
+	int i;
+
+	if (delta_t <= 0) {
+		return;
+	}
+
+	// Age bus value.
+	bus_value_ttl -= delta_t;
+	if (bus_value_ttl <= 0) {
+		bus_value = 0;
+		bus_value_ttl = 0;
+	}
+
+	// Clock amplitude modulators.
+	for (i = 0; i < 3; i++) {
+		voice[i].envelope.clock(delta_t);
+	}
+
+	// Clock and synchronize oscillators.
+	// Loop until we reach the current cycle.
+	cycle_count delta_t_osc = delta_t;
+	while (delta_t_osc) {
+		cycle_count delta_t_min = delta_t_osc;
+
+		// Find minimum number of cycles to an oscillator accumulator MSB toggle.
+		// We have to clock on each MSB on / MSB off for hard sync to operate
+		// correctly.
+		for (i = 0; i < 3; i++) {
+			WaveformGenerator& wave = voice[i].wave;
+
+			// It is only necessary to clock on the MSB of an oscillator that is
+			// a sync source and has freq != 0.
+			if (!(wave.sync_dest->sync && wave.freq)) {
+				continue;
+			}
+
+			reg16 freq = wave.freq;
+			reg24 accumulator = wave.accumulator;
+
+			// Clock on MSB off if MSB is on, clock on MSB on if MSB is off.
+			reg24 delta_accumulator =
+				(accumulator & 0x800000 ? 0x1000000 : 0x800000) - accumulator;
+
+			cycle_count delta_t_next = delta_accumulator/freq;
+			if (delta_accumulator%freq) {
+				++delta_t_next;
+			}
+
+			if (delta_t_next < delta_t_min) {
+				delta_t_min = delta_t_next;
+			}
+		}
+
+		// Clock oscillators.
+		for (i = 0; i < 3; i++) {
+			voice[i].wave.clock(delta_t_min);
+		}
+
+		// Synchronize oscillators.
+		for (i = 0; i < 3; i++) {
+			voice[i].wave.synchronize();
+		}
+
+		delta_t_osc -= delta_t_min;
+	}
+
+	// Clock filter.
+	filter.clock(delta_t,
+		voice[0].output(), voice[1].output(), voice[2].output());
+
+	// Clock external filter.
+	extfilt.clock(delta_t, filter.output());
+}
+
+
+/**
+ * SID clocking with audio sampling.
+ * Fixpoint arithmetics is used.
+ */
+int SID::clock(cycle_count& delta_t, short* buf, int n, int interleave) {
+	int s = 0;
+
+	for (;;) {
+		cycle_count next_sample_offset = sample_offset + cycles_per_sample + (1 << (FIXP_SHIFT - 1));
+		cycle_count delta_t_sample = next_sample_offset >> FIXP_SHIFT;
+		if (delta_t_sample > delta_t) {
+			break;
+		}
+		if (s >= n) {
+			return s;
+		}
+		clock(delta_t_sample);
+		delta_t -= delta_t_sample;
+		sample_offset = (next_sample_offset & FIXP_MASK) - (1 << (FIXP_SHIFT - 1));
+		buf[s++*interleave] = output();
+	}
+
+	clock(delta_t);
+	sample_offset -= delta_t << FIXP_SHIFT;
+	delta_t = 0;
+	return s;
+}
+
+}


Property changes on: scummvm/trunk/sound/softsynth/sid.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/sound/softsynth/sid.h
===================================================================
--- scummvm/trunk/sound/softsynth/sid.h	                        (rev 0)
+++ scummvm/trunk/sound/softsynth/sid.h	2009-11-26 00:31:19 UTC (rev 46143)
@@ -0,0 +1,353 @@
+/* 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$
+ *
+ */
+
+/*
+ *  This file is based on reSID, a MOS6581 SID emulator engine.
+ *  Copyright (C) 2004  Dag Lem <resid at nimrod.no>
+ */
+
+#ifndef __SID_H__
+#define __SID_H__
+
+// Inlining on/off.
+#define RESID_INLINE inline
+
+namespace Resid {
+
+// We could have used the smallest possible data type for each SID register,
+// however this would give a slower engine because of data type conversions.
+// An int is assumed to be at least 32 bits (necessary in the types reg24,
+// cycle_count, and sound_sample). GNU does not support 16-bit machines
+// (GNU Coding Standards: Portability between CPUs), so this should be
+// a valid assumption.
+
+typedef unsigned int reg4;
+typedef unsigned int reg8;
+typedef unsigned int reg12;
+typedef unsigned int reg16;
+typedef unsigned int reg24;
+
+typedef int cycle_count;
+typedef int sound_sample;
+typedef sound_sample fc_point[2];
+
+
+class WaveformGenerator {
+public:
+	WaveformGenerator();
+
+	void set_sync_source(WaveformGenerator*);
+
+	void clock(cycle_count delta_t);
+	void synchronize();
+	void reset();
+
+	void writeFREQ_LO(reg8);
+	void writeFREQ_HI(reg8);
+	void writePW_LO(reg8);
+	void writePW_HI(reg8);
+	void writeCONTROL_REG(reg8);
+	reg8 readOSC();
+
+	// 12-bit waveform output.
+	reg12 output();
+
+protected:
+	const WaveformGenerator* sync_source;
+	WaveformGenerator* sync_dest;
+
+	// Tell whether the accumulator MSB was set high on this cycle.
+	bool msb_rising;
+
+	reg24 accumulator;
+	reg24 shift_register;
+
+	// Fout  = (Fn*Fclk/16777216)Hz
+	reg16 freq;
+	// PWout = (PWn/40.95)%
+	reg12 pw;
+
+	// The control register right-shifted 4 bits; used for output function
+	// table lookup.
+	reg8 waveform;
+
+	// The remaining control register bits.
+	reg8 test;
+	reg8 ring_mod;
+	reg8 sync;
+	// The gate bit is handled by the EnvelopeGenerator.
+
+	// 16 possible combinations of waveforms.
+	reg12 output____();
+	reg12 output___T();
+	reg12 output__S_();
+	reg12 output__ST();
+	reg12 output_P__();
+	reg12 output_P_T();
+	reg12 output_PS_();
+	reg12 output_PST();
+	reg12 outputN___();
+	reg12 outputN__T();
+	reg12 outputN_S_();
+	reg12 outputN_ST();
+	reg12 outputNP__();
+	reg12 outputNP_T();
+	reg12 outputNPS_();
+	reg12 outputNPST();
+
+	// Sample data for combinations of waveforms.
+	static reg8 wave6581__ST[];
+	static reg8 wave6581_P_T[];
+	static reg8 wave6581_PS_[];
+	static reg8 wave6581_PST[];
+
+	reg8* wave__ST;
+	reg8* wave_P_T;
+	reg8* wave_PS_;
+	reg8* wave_PST;
+
+	friend class Voice;
+	friend class SID;
+};
+
+class Filter {
+public:
+	Filter();
+
+	void enable_filter(bool enable);
+
+	void clock(cycle_count delta_t,
+		sound_sample voice1, sound_sample voice2, sound_sample voice3);
+	void reset();
+
+	// Write registers.
+	void writeFC_LO(reg8);
+	void writeFC_HI(reg8);
+	void writeRES_FILT(reg8);
+	void writeMODE_VOL(reg8);
+
+	// SID audio output (16 bits).
+	sound_sample output();
+
+protected:
+	void set_w0();
+	void set_Q();
+
+	// Filter enabled.
+	bool enabled;
+
+	// Filter cutoff frequency.
+	reg12 fc;
+
+	// Filter resonance.
+	reg8 res;
+
+	// Selects which inputs to route through filter.
+	reg8 filt;
+
+	// Switch voice 3 off.
+	reg8 voice3off;
+
+	// Highpass, bandpass, and lowpass filter modes.
+	reg8 hp_bp_lp;
+
+	// Output master volume.
+	reg4 vol;
+
+	// Mixer DC offset.
+	sound_sample mixer_DC;
+
+	// State of filter.
+	sound_sample Vhp; // highpass
+	sound_sample Vbp; // bandpass
+	sound_sample Vlp; // lowpass
+	sound_sample Vnf; // not filtered
+
+	// Cutoff frequency, resonance.
+	sound_sample w0, w0_ceil_1, w0_ceil_dt;
+	sound_sample _1024_div_Q;
+
+	// Cutoff frequency tables.
+	// FC is an 11 bit register.
+	sound_sample f0_6581[2048];
+	sound_sample* f0;
+	static fc_point f0_points_6581[];
+	fc_point* f0_points;
+	int f0_count;
+
+	friend class SID;
+};
+
+class EnvelopeGenerator {
+public:
+	EnvelopeGenerator();
+
+	enum State { ATTACK, DECAY_SUSTAIN, RELEASE };
+
+	void clock(cycle_count delta_t);
+	void reset();
+
+	void writeCONTROL_REG(reg8);
+	void writeATTACK_DECAY(reg8);
+	void writeSUSTAIN_RELEASE(reg8);
+	reg8 readENV();
+
+	// 8-bit envelope output.
+	reg8 output();
+
+protected:
+	reg16 rate_counter;
+	reg16 rate_period;
+	reg8 exponential_counter;
+	reg8 exponential_counter_period;
+	reg8 envelope_counter;
+	bool hold_zero;
+
+	reg4 attack;
+	reg4 decay;
+	reg4 sustain;
+	reg4 release;
+
+	reg8 gate;
+
+	State state;
+
+	// Lookup table to convert from attack, decay, or release value to rate
+	// counter period.
+	static reg16 rate_counter_period[];
+
+	// The 16 selectable sustain levels.
+	static reg8 sustain_level[];
+
+	friend class SID;
+};
+
+class ExternalFilter {
+public:
+	ExternalFilter();
+
+	void enable_filter(bool enable);
+	void set_sampling_parameter(double pass_freq);
+
+	void clock(cycle_count delta_t, sound_sample Vi);
+	void reset();
+
+	// Audio output (20 bits).
+	sound_sample output();
+
+protected:
+	// Filter enabled.
+	bool enabled;
+
+	// Maximum mixer DC offset.
+	sound_sample mixer_DC;
+
+	// State of filters.
+	sound_sample Vlp; // lowpass
+	sound_sample Vhp; // highpass
+	sound_sample Vo;
+
+	// Cutoff frequencies.
+	sound_sample w0lp;
+	sound_sample w0hp;
+
+	friend class SID;
+};
+
+class Voice {
+public:
+	Voice();
+
+	void set_sync_source(Voice*);
+	void reset();
+
+	void writeCONTROL_REG(reg8);
+
+	// Amplitude modulated waveform output.
+	// Range [-2048*255, 2047*255].
+	sound_sample output() {
+		// Multiply oscillator output with envelope output.
+		return (wave.output() - wave_zero)*envelope.output() + voice_DC;
+	}
+
+protected:
+	WaveformGenerator wave;
+	EnvelopeGenerator envelope;
+
+	// Waveform D/A zero level.
+	sound_sample wave_zero;
+
+	// Multiplying D/A DC offset.
+	sound_sample voice_DC;
+
+	friend class SID;
+};
+
+
+class SID {
+public:
+	SID();
+	~SID();
+
+	void enable_filter(bool enable);
+	void enable_external_filter(bool enable);
+	bool set_sampling_parameters(double clock_freq,
+		double sample_freq, double pass_freq = -1,
+		double filter_scale = 0.97);
+
+	void clock(cycle_count delta_t);
+	int clock(cycle_count& delta_t, short* buf, int n, int interleave = 1);
+	void reset();
+
+	// Read/write registers.
+	reg8 read(reg8 offset);
+	void write(reg8 offset, reg8 value);
+
+	// 16-bit output (AUDIO OUT).
+	int output();
+
+protected:
+	Voice voice[3];
+	Filter filter;
+	ExternalFilter extfilt;
+
+	reg8 bus_value;
+	cycle_count bus_value_ttl;
+
+	double clock_frequency;
+
+	// Fixpoint constants.
+	static const int FIXP_SHIFT;
+	static const int FIXP_MASK;
+
+	// Sampling variables.
+	cycle_count cycles_per_sample;
+	cycle_count sample_offset;
+	short sample_prev;
+};
+
+}
+
+#endif // not __SID_H__


Property changes on: scummvm/trunk/sound/softsynth/sid.h
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/sound/softsynth/wave6581.cpp
===================================================================
--- scummvm/trunk/sound/softsynth/wave6581.cpp	                        (rev 0)
+++ scummvm/trunk/sound/softsynth/wave6581.cpp	2009-11-26 00:31:19 UTC (rev 46143)
@@ -0,0 +1,2100 @@
+/* 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$
+ *
+ */
+
+/*
+ *  This file is based on reSID, a MOS6581 SID emulator engine.
+ *  Copyright (C) 2004  Dag Lem <resid at nimrod.no>
+ */
+
+#include "sid.h"
+
+namespace Resid
+{
+
+reg8 WaveformGenerator::wave6581__ST[] =
+{
+/* 0x000: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x008: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x010: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x018: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x020: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x028: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x030: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x038: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x040: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x048: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x050: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x058: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x060: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x068: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x070: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x078: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03,
+/* 0x080: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x088: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x090: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x098: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x0a0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x0a8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x0b0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x0b8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+/* 0x0c0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x0c8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x0d0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x0d8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x0e0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x0e8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x0f0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x0f8: */  0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07,
+/* 0x100: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x108: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x110: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x118: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x120: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x128: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x130: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x138: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+/* 0x140: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x148: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x150: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x158: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x160: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x168: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x170: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x178: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03,
+/* 0x180: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x188: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x190: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x198: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x1a0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x1a8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x1b0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x1b8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+/* 0x1c0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x1c8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x1d0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x1d8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x1e0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x1e8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x1f0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x1f8: */  0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,
+/* 0x200: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x208: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x210: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x218: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x220: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x228: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x230: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x238: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x240: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x248: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x250: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x258: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x260: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x268: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x270: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x278: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03,
+/* 0x280: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x288: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x290: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x298: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x2a0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x2a8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x2b0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x2b8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+/* 0x2c0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x2c8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x2d0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x2d8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x2e0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x2e8: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x2f0: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x2f8: */  0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07,
+/* 0x300: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x308: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x310: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x318: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x320: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x328: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x330: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x338: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+/* 0x340: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x348: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x350: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x358: */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

@@ Diff output truncated at 100000 characters. @@

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list