[Scummvm-cvs-logs] SF.net SVN: scummvm:[49755] scummvm/trunk/engines/agi

sev at users.sourceforge.net sev at users.sourceforge.net
Tue Jun 15 12:36:10 CEST 2010


Revision: 49755
          http://scummvm.svn.sourceforge.net/scummvm/?rev=49755&view=rev
Author:   sev
Date:     2010-06-15 10:36:10 +0000 (Tue, 15 Jun 2010)

Log Message:
-----------
AGI: Implement FR #2813133.

FR #2813133: "AGI: Proper Tandy 3-Voice/IBM PCjr Sound Support".
Add proper Tandy music. Heavily based on NAGI source, thus attached
its X11 license.

To run it now use -e pcjr. Old one is still default for adlib but
most likely will be changed in the future.

Also lied ground for further separation of different sound generators.

Modified Paths:
--------------
    scummvm/trunk/engines/agi/agi.cpp
    scummvm/trunk/engines/agi/module.mk
    scummvm/trunk/engines/agi/sound.cpp
    scummvm/trunk/engines/agi/sound.h

Added Paths:
-----------
    scummvm/trunk/engines/agi/sound_pcjr.cpp
    scummvm/trunk/engines/agi/sound_pcjr.h

Modified: scummvm/trunk/engines/agi/agi.cpp
===================================================================
--- scummvm/trunk/engines/agi/agi.cpp	2010-06-15 10:35:42 UTC (rev 49754)
+++ scummvm/trunk/engines/agi/agi.cpp	2010-06-15 10:36:10 UTC (rev 49755)
@@ -583,15 +583,18 @@
 	} else if (getPlatform() == Common::kPlatformCoCo3) {
 		_soundemu = SOUND_EMU_COCO3;
 	} else {
-		switch (MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB)) {
+		switch (MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB | MDT_MIDI)) {
 		case MD_PCSPK:
 			_soundemu = SOUND_EMU_PC;
 			break;
+		case MD_PCJR:
+			_soundemu = SOUND_EMU_PCJR;
+			break;
 		case MD_ADLIB:
-			_soundemu = SOUND_EMU_MIDI;
+			_soundemu = SOUND_EMU_NONE;
 			break;
 		default:
-			_soundemu = SOUND_EMU_NONE;
+			_soundemu = SOUND_EMU_MIDI;
 			break;
 		}
 	}

Modified: scummvm/trunk/engines/agi/module.mk
===================================================================
--- scummvm/trunk/engines/agi/module.mk	2010-06-15 10:35:42 UTC (rev 49754)
+++ scummvm/trunk/engines/agi/module.mk	2010-06-15 10:36:10 UTC (rev 49755)
@@ -32,6 +32,7 @@
 	sound.o \
 	sound_2gs.o \
 	sound_midi.o \
+	sound_pcjr.o \
 	sprite.o \
 	text.o \
 	view.o \

Modified: scummvm/trunk/engines/agi/sound.cpp
===================================================================
--- scummvm/trunk/engines/agi/sound.cpp	2010-06-15 10:35:42 UTC (rev 49754)
+++ scummvm/trunk/engines/agi/sound.cpp	2010-06-15 10:36:10 UTC (rev 49755)
@@ -35,6 +35,7 @@
 
 #include "agi/sound_2gs.h"
 #include "agi/sound_midi.h"
+#include "agi/sound_pcjr.h"
 
 namespace Agi {
 
@@ -127,6 +128,15 @@
 }
 #endif
 
+int SoundMgr::readBuffer(int16 *buffer, const int numSamples) {
+	if (_vm->_soundemu == SOUND_EMU_PCJR)
+		_soundGen->premixerCall(buffer, numSamples);
+	else
+		premixerCall(buffer, numSamples / 2);
+	
+	return numSamples;
+}
+
 void SoundMgr::unloadSound(int resnum) {
 	if (_vm->_game.dirSound[resnum].flags & RES_LOADED) {
 		if (_vm->_game.sounds[resnum]->isPlaying()) {
@@ -174,6 +184,8 @@
 	case AGI_SOUND_4CHN:
 		if (_vm->_soundemu == SOUND_EMU_MIDI) {
 			_musicPlayer->playMIDI((MIDISound *)_vm->_game.sounds[resnum]);
+		} else if (_vm->_soundemu == SOUND_EMU_PCJR) {
+			_soundGen->play(resnum, flag);
 		} else {
 
 			PCjrSound *pcjrSound = (PCjrSound *) _vm->_game.sounds[resnum];
@@ -212,7 +224,8 @@
 	debugC(3, kDebugLevelSound, "stopSound() --> %d", _playingSound);
 
 	_endflag = -1;
-	if (_vm->_soundemu != SOUND_EMU_APPLE2GS) {
+
+	if (_vm->_soundemu != SOUND_EMU_APPLE2GS && _vm->_soundemu != SOUND_EMU_PCJR) {
 		for (i = 0; i < NUM_CHANNELS; i++)
 			stopNote(i);
 	}
@@ -229,6 +242,10 @@
 			_musicPlayer->stop();
 		}
 
+		if (_vm->_soundemu == SOUND_EMU_PCJR) {
+			_soundGen->stop();
+		}
+
 		_playingSound = -1;
 	}
 
@@ -261,6 +278,9 @@
 		break;
 	case SOUND_EMU_MIDI:
 		break;
+	case SOUND_EMU_PCJR:
+		_soundGen = new SoundGenPCJr(_vm);
+		break;
 	}
 
 	report("Initializing sound:\n");
@@ -281,6 +301,7 @@
 void SoundMgr::deinitSound() {
 	debugC(3, kDebugLevelSound, "()");
 
+	stopSound();
 	_mixer->stopHandle(_soundHandle);
 }
 
@@ -617,7 +638,7 @@
 	data_available -= len;
 }
 
-SoundMgr::SoundMgr(AgiBase *agi, Audio::Mixer *pMixer) : _chn() {
+SoundMgr::SoundMgr(AgiEngine *agi, Audio::Mixer *pMixer) : _chn() {
 	_vm = agi;
 	_mixer = pMixer;
 	_sampleRate = pMixer->getOutputRate();
@@ -630,6 +651,8 @@
 	_disabledMidi = false;
 	_useChorus = true;	// FIXME: Currently always true?
 	_midiDriver = 0;
+	_musicPlayer = 0;
+	_soundGen = 0;
 
 	_gsSound = new IIgsSoundMgr;
 
@@ -654,6 +677,7 @@
 	free(_sndBuffer);
 	delete _gsSound;
 
+	delete _soundGen;
 	delete _musicPlayer;
 	delete _midiDriver;
 }

Modified: scummvm/trunk/engines/agi/sound.h
===================================================================
--- scummvm/trunk/engines/agi/sound.h	2010-06-15 10:35:42 UTC (rev 49754)
+++ scummvm/trunk/engines/agi/sound.h	2010-06-15 10:36:10 UTC (rev 49755)
@@ -37,7 +37,7 @@
 
 #define SOUND_EMU_NONE	0
 #define SOUND_EMU_PC	1
-#define SOUND_EMU_TANDY	2
+#define SOUND_EMU_PCJR	2
 #define SOUND_EMU_MAC	3
 #define SOUND_EMU_AMIGA	4
 #define SOUND_EMU_APPLE2GS 5
@@ -126,6 +126,17 @@
 
 class SoundMgr;
 
+class SoundGen {
+public:
+	SoundGen() {}
+	virtual ~SoundGen() {}
+
+	virtual void play(int resnum, int flag) = 0;
+	virtual void stop(void) = 0;
+
+	virtual void premixerCall(int16 *stream, int len) = 0;
+};
+
 /**
  * AGI sound resource structure.
  */
@@ -167,7 +178,6 @@
 };
 
 class AgiEngine;
-class AgiBase;
 class IIgsSoundMgr;
 class MusicPlayer;
 
@@ -176,15 +186,12 @@
 class SoundMgr : public Audio::AudioStream {
 
 public:
-	SoundMgr(AgiBase *agi, Audio::Mixer *pMixer);
+	SoundMgr(AgiEngine *agi, Audio::Mixer *pMixer);
 	~SoundMgr();
 	virtual void setVolume(uint8 volume);
 
 	// AudioStream API
-	int readBuffer(int16 *buffer, const int numSamples) {
-		premixerCall(buffer, numSamples / 2);
-		return numSamples;
-	}
+	int readBuffer(int16 *buffer, const int numSamples);
 
 	bool isStereo() const {
 		return false;
@@ -200,7 +207,7 @@
 	}
 
 	int _endflag;
-	AgiBase *_vm;
+	AgiEngine *_vm;
 
 private:
 	Audio::Mixer *_mixer;
@@ -226,6 +233,8 @@
 	void premixerCall(int16 *buf, uint len);
 	void fillAudio(void *udata, int16 *stream, uint len);
 
+	SoundGen *_soundGen;
+
 public:
 	void unloadSound(int);
 	void playSound();

Added: scummvm/trunk/engines/agi/sound_pcjr.cpp
===================================================================
--- scummvm/trunk/engines/agi/sound_pcjr.cpp	                        (rev 0)
+++ scummvm/trunk/engines/agi/sound_pcjr.cpp	2010-06-15 10:36:10 UTC (rev 49755)
@@ -0,0 +1,497 @@
+/* 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$
+ *
+ */
+
+/* Heavily based on code from NAGI
+ *
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright (c) 2001, 2001, 2002 Nick Sonneveld
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, and/or sell copies of the Software, and to permit persons
+ * to whom the Software is furnished to do so, provided that the above
+ * copyright notice(s) and this permission notice appear in all copies of
+ * the Software and that both the above copyright notice(s) and this
+ * permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+ * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
+ * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder
+ * shall not be used in advertising or otherwise to promote the sale, use
+ * or other dealings in this Software without prior written authorization
+ *
+ */
+
+#include "agi/agi.h"
+#include "agi/sound.h"
+#include "agi/sound_pcjr.h"
+
+namespace Agi {
+
+// "fade out" or possibly "dissolve"
+// v2.9xx
+const int8 dissolveDataV2[] = {
+	-2, -3, -2, -1,
+	0x00, 0x00,
+	0x01, 0x01, 0x01, 0x01,
+	0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x04, 0x04, 0x04, 0x04,
+	0x05, 0x05, 0x05, 0x05,
+	0x06, 0x06, 0x06, 0x06, 0x06,
+	0x07, 0x07, 0x07, 0x07,
+	0x08, 0x08, 0x08, 0x08,
+	0x09, 0x09, 0x09, 0x09,
+	0x0A, 0x0A, 0x0A, 0x0A,
+	0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+	0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+	0x0D,
+	-100
+};
+
+// v3
+const int8 dissolveDataV3[] = {
+	-2, -3, -2, -1,
+	0x00, 0x00, 0x00, 0x00, 0x00,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x04, 0x04, 0x04, 0x04, 0x04,
+	0x05, 0x05, 0x05, 0x05, 0x05,
+	0x06, 0x06, 0x06, 0x06, 0x06,
+	0x07, 0x07, 0x07, 0x07,
+	0x08, 0x08, 0x08, 0x08,
+	0x09, 0x09, 0x09, 0x09,
+	0x0A, 0x0A, 0x0A, 0x0A,
+	0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+	0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+	0x0D,
+	-100
+};
+
+
+SoundGenPCJr::SoundGenPCJr(AgiEngine *vm) : _vm(vm) {
+	_chanAllocated = 10240; // preallocate something which will most likely fit
+	_chanData = (int16 *)malloc(_chanAllocated << 1);
+
+	// Pick dissolve method
+	//
+	// 0 = no dissolve.. just play for as long as it's meant to be played
+	// this was used in older v2.4 and under games i THINK
+	// 1 = not used
+	// 2 = v2.9+ games used a shorter dissolve
+	// 3 (default) = v3 games used this dissolve pattern.. slightly longer
+	if (_vm->getVersion() >= 0x3000)
+		_dissolveMethod = 3;
+	else if (_vm->getVersion() >= 0x2900)
+		_dissolveMethod = 2;
+	else
+		_dissolveMethod = 0;
+}
+
+SoundGenPCJr::~SoundGenPCJr() {
+	free(_chanData);
+}
+
+void SoundGenPCJr::play(int resnum, int flag) {
+	PCjrSound *pcjrSound = (PCjrSound *)_vm->_game.sounds[resnum];
+
+	for (int i = 0; i < CHAN_MAX; i++) {
+		_channel[i].data = pcjrSound->getVoicePointer(i % 4);
+		_channel[i].duration = 0;
+		_channel[i].avail = 0xffff;
+		_channel[i].dissolveCount = 0xFFFF;
+		_channel[i].attenuation = 0;
+		_channel[i].attenuationCopy = 0;
+
+		_tchannel[i].avail = 1;
+		_tchannel[i].noteCount = 0;
+		_tchannel[i].freqCount = 250;
+		_tchannel[i].freqCountPrev = -1;
+		_tchannel[i].atten = 0xF;	// silence
+		_tchannel[i].genType = kGenTone;
+		_tchannel[i].genTypePrev = -1;
+	}
+}
+
+void SoundGenPCJr::stop(void) {
+	int i;
+
+	for (i = 0; i < CHAN_MAX ; i++) {
+		_channel[i].avail = 0;
+		_tchannel[i].avail = 0;
+	}
+}
+
+int SoundGenPCJr::volumeCalc(SndGenChan *chan) {
+	int8 attenuation, dissolveValue;
+
+	const int8 *dissolveData;
+
+	switch (_dissolveMethod) {
+	case 2:
+		dissolveData = dissolveDataV2;
+		break;
+	case 3:
+	default:
+		dissolveData = dissolveDataV3;
+		break;
+	}
+
+	assert(chan);
+
+	attenuation = chan->attenuation;
+	if (attenuation != 0x0F) {	// != silence
+		if (chan->dissolveCount != 0xFFFF) {
+			dissolveValue = dissolveData[chan->dissolveCount];
+			if (dissolveValue == -100) {	// if at end of list
+				chan->dissolveCount = 0xFFFF;
+				chan->attenuation = chan->attenuationCopy;
+				attenuation = chan->attenuation;
+			} else {
+				chan->dissolveCount++;
+
+				attenuation += dissolveValue;
+				if (attenuation < 0)
+					attenuation = 0;
+				if (attenuation > 0x0F)
+					attenuation = 0x0F;
+
+				chan->attenuationCopy = attenuation;
+
+				attenuation &= 0x0F;
+				attenuation += _vm->getvar(vVolume);
+				if (attenuation > 0x0F)
+					attenuation = 0x0F;
+			}
+		}
+		//if (computer_type == 2) && (attenuation < 8)
+		if (attenuation < 8)
+			attenuation += 2;
+	}
+
+	return attenuation;
+}
+
+// read the next channel data.. fill it in *tone
+// if tone isn't touched.. it should be inited so it just plays silence
+// return 0 if it's passing more data
+// return -1 if it's passing nothing (end of data)
+int SoundGenPCJr::getNextNote(int ch, Tone *tone) {
+	SndGenChan *chan;
+	const byte *data;
+
+	assert(tone);
+	assert(ch < CHAN_MAX);
+
+	if (!_vm->getflag(fSoundOn))
+		return -1;
+
+	chan = &_channel[ch];
+	if (!chan->avail)
+		return -1;
+
+	while ((chan->duration == 0) && (chan->duration != 0xFFFF)) {
+		data = chan->data;
+
+		// read the duration of the note
+		chan->duration = READ_LE_UINT16(data);	// duration
+
+		// if it's 0 then it's not going to be played
+		// if it's 0xFFFF then the channel data has finished.
+		if ((chan->duration != 0) && (chan->duration != 0xFFFF)) {
+			// only tone channels dissolve
+			if ((ch != 3) && (_dissolveMethod != 0))	// != noise??
+				chan->dissolveCount = 0;
+
+			// attenuation (volume)
+			chan->attenuation = data[4] & 0xF;
+
+			// frequency
+			if (ch < (CHAN_MAX - 1)) {
+				chan->freqCount = (uint16)data[2] & 0x3F;
+				chan->freqCount <<= 4;
+				chan->freqCount |= data[3] & 0x0F;
+
+				chan->genType = kGenTone;
+			} else {
+				int noiseFreq;
+
+				// check for white noise (1) or periodic (0)
+				chan->genType = (data[3] & 0x04) ? kGenWhite : kGenPeriod;
+
+				noiseFreq = data[3] & 0x03;
+
+				switch (noiseFreq) {
+				case 0:
+					chan->freqCount = 32;
+					break;
+				case 1:
+					chan->freqCount = 64;
+					break;
+				case 2:
+					chan->freqCount = 128;
+					break;
+				case 3:
+					chan->freqCount = _channel[2].freqCount * 2;
+					break;
+				}
+			}
+		}
+		// data now points to the next data seg-a-ment
+		chan->data += 5;
+	}
+
+	if (chan->duration != 0xFFFF) {
+		tone->freqCount = chan->freqCount;
+		tone->atten = volumeCalc(chan);	// calc volume, sent vol is different from saved vol
+		tone->type = chan->genType;
+		chan->duration--;
+	} else {
+		// kill channel
+		chan->avail = 0;
+		chan->attenuation = 0x0F;	// silent
+		chan->attenuationCopy = 0x0F;	// dunno really
+
+		return -1;
+	}
+
+	return 0;
+}
+
+// Formulas for noise generator
+// bit0 = output
+
+// noise feedback for white noise mode
+#define FB_WNOISE 0x12000	// bit15.d(16bits) = bit0(out) ^ bit2
+//#define FB_WNOISE 0x14000	// bit15.d(16bits) = bit0(out) ^ bit1
+//#define FB_WNOISE 0x28000	// bit16.d(17bits) = bit0(out) ^ bit2 (same to AY-3-8910)
+//#define FB_WNOISE 0x50000	// bit17.d(18bits) = bit0(out) ^ bit2
+
+// noise feedback for periodic noise mode
+// it is correct maybe (it was in the Megadrive sound manual)
+//#define FB_PNOISE 0x10000	// 16bit rorate
+#define FB_PNOISE 0x08000
+
+// noise generator start preset (for periodic noise)
+#define NG_PRESET 0x0f35
+
+//#define WAVE_HEIGHT (0x7FFF)
+
+// Volume table.
+//
+// 2dB = 20*log(a/b)
+// 10^(2/20)*b = a;
+// value = 0x7fff;
+// value /= 1.258925411794;
+const int16 volTable[16] = {
+	32767, 26027, 20674, 16422, 13044, 10361, 8230, 6537, 5193, 4125, 3276, 2602, 2067, 1642, 1304, 0
+};
+
+#define FREQ_DIV 111844
+#define MULT FREQ_DIV
+
+// fill buff
+int SoundGenPCJr::chanGen(int chan, int16 *stream, int len) {
+	ToneChan *tpcm;
+	Tone toneNew;
+	int fillSize;
+	int retVal;
+
+	tpcm = &_tchannel[chan];
+
+	retVal = -1;
+
+	while (len > 0) {
+		if (tpcm->noteCount <= 0) {
+			// get new tone data
+			toneNew.freqCount = 0;
+			toneNew.atten = 0xF;
+			toneNew.type = kGenTone;
+			if ((tpcm->avail) && (getNextNote(chan, &toneNew) == 0)) {
+				tpcm->atten = toneNew.atten;
+				tpcm->freqCount = toneNew.freqCount;
+				tpcm->genType = toneNew.type;
+
+				// setup counters 'n stuff
+				// SAMPLE_RATE samples per sec.. tone changes 60 times per sec
+				tpcm->noteCount = SAMPLE_RATE / 60;
+				retVal = 0;
+			} else {
+				// if it doesn't return an
+				tpcm->genType = kGenSilence;
+				tpcm->noteCount = len;
+				tpcm->avail = 0;
+			}
+		}
+
+		// write nothing
+		if ((tpcm->freqCount == 0) || (tpcm->atten == 0xf)) {
+			tpcm->genType = kGenSilence;
+		}
+
+		// find which is smaller.. the buffer or the
+		fillSize = (tpcm->noteCount <= len) ? tpcm->noteCount : len;
+
+		switch (tpcm->genType) {
+			case kGenTone:
+				fillSize = fillSquare(tpcm, stream, fillSize);
+				break;
+			case kGenPeriod:
+			case kGenWhite:
+				fillSize = fillNoise(tpcm, stream, fillSize);
+				break;
+			case kGenSilence:
+			default:
+				// fill with whitespace
+				memset(stream, 0, fillSize * sizeof(int16));
+				break;
+		}
+
+		tpcm->noteCount -= fillSize;
+		stream += fillSize;
+		len -= fillSize;
+	}
+
+	return retVal;
+}
+
+int SoundGenPCJr::fillSquare(ToneChan *t, int16 *buf, int len) {
+	int count;
+
+	if (t->genType != t->genTypePrev) {
+		// make sure the freqCount is checked
+		t->freqCountPrev = -1;
+		t->sign = 1;
+		t->genTypePrev = t->genType;
+	}
+
+	if (t->freqCount != t->freqCountPrev) {
+		//t->scale = (int)( (double)t->samp->freq*t->freqCount/FREQ_DIV * MULT + 0.5);
+		t->scale = (SAMPLE_RATE / 2) * t->freqCount;
+		t->count = t->scale;
+		t->freqCountPrev = t->freqCount;
+	}
+
+	count = len;
+
+	while (count > 0) {
+		*(buf++) = t->sign ? volTable[t->atten] : -volTable[t->atten];
+		count--;
+
+		// get next sample
+		t->count -= MULT;
+		while (t->count <= 0) {
+			t->sign ^= 1;
+			t->count += t->scale;
+		}
+	}
+
+	return len;
+}
+
+int SoundGenPCJr::fillNoise(ToneChan *t, int16 *buf, int len) {
+	int count;
+
+	if (t->genType != t->genTypePrev) {
+		// make sure the freqCount is checked
+		t->freqCountPrev = -1;
+		t->genTypePrev = t->genType;
+	}
+
+	if (t->freqCount != t->freqCountPrev) {
+		//t->scale = (int)( (double)t->samp->freq*t->freqCount/FREQ_DIV * MULT + 0.5);
+		t->scale = (SAMPLE_RATE / 2) * t->freqCount;
+		t->count = t->scale;
+		t->freqCountPrev = t->freqCount;
+
+		t->feedback = (t->genType == kGenWhite) ? FB_WNOISE : FB_PNOISE;
+		// reset noise shifter
+		t->noiseState = NG_PRESET;
+		t->sign = t->noiseState & 1;
+	}
+
+	count = len;
+
+	while (count > 0) {
+		*(buf++) = t->sign ? volTable[t->atten] : -volTable[t->atten];
+		count--;
+
+		// get next sample
+		t->count -= MULT;
+		while (t->count <= 0) {
+			if (t->noiseState & 1)
+				t->noiseState ^= t->feedback;
+
+			t->noiseState >>= 1;
+			t->sign = t->noiseState & 1;
+			t->count += t->scale;
+		}
+	}
+
+	return len;
+}
+
+void SoundGenPCJr::premixerCall(int16 *stream, int len) {
+	int streamCount;
+	int16 *sPtr, *cPtr;
+
+	if (_chanAllocated < len) {
+		free(_chanData);
+		_chanData = (int16 *)malloc(len << 1);
+		_chanAllocated = len;
+	}
+	memset(stream, 0, len << 1);
+
+	assert(stream);
+
+	for (int i = 0; i < CHAN_MAX; i++) {
+		// get channel data(chan.userdata)
+		if (chanGen(i, _chanData, len) == 0) {
+			// divide by number of channels then add to stream
+			streamCount = len;
+			sPtr = stream;
+			cPtr = _chanData;
+					
+			while (streamCount--)
+				*(sPtr++) += *(cPtr++) / CHAN_MAX;
+		}
+	}
+}
+
+} // End of namespace Agi


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

Added: scummvm/trunk/engines/agi/sound_pcjr.h
===================================================================
--- scummvm/trunk/engines/agi/sound_pcjr.h	                        (rev 0)
+++ scummvm/trunk/engines/agi/sound_pcjr.h	2010-06-15 10:36:10 UTC (rev 49755)
@@ -0,0 +1,112 @@
+/* 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 AGI_SOUND_PCJR_H
+#define AGI_SOUND_PCJR_H
+
+namespace Agi {
+
+#define CHAN_MAX 4
+
+#define SAMPLE_RATE 22050
+
+enum GenType {
+	kGenSilence,
+	kGenTone,
+	kGenPeriod,
+	kGenWhite
+};
+
+struct SndGenChan {
+	const byte *data;
+	uint16 duration;
+	uint16 avail;	// turned on (1)  but when the channel's data runs out, it's set to (0)
+	uint16 dissolveCount;
+	byte attenuation;
+	byte attenuationCopy;
+	
+	GenType genType;
+	
+	// for the sample mixer
+	int freqCount;
+};
+
+struct ToneChan {
+	int avail;
+
+	int noteCount; // length of tone.. duration
+
+	int freqCount;
+	int freqCountPrev;
+	int atten;  // volume
+
+	GenType genType;
+	int genTypePrev;
+
+	int count;
+	int scale;
+	int sign;
+	unsigned int noiseState;		/* noise generator      */
+	int feedback;						/* noise feedback mask */
+};
+
+struct Tone {
+	int freqCount;
+	int atten;
+	GenType type;
+};
+
+class SoundGenPCJr : public SoundGen {
+public:
+	SoundGenPCJr(AgiEngine *vm);
+	~SoundGenPCJr();
+
+	void play(int resnum, int flag);
+	void stop(void);
+
+	void premixerCall(int16 *stream, int len);
+
+private:
+	int getNextNote(int ch, Tone *tone);
+	int volumeCalc(SndGenChan *chan);
+
+	int chanGen(int chan, int16 *stream, int len);
+
+	int fillNoise(ToneChan *t, int16 *buf, int len);
+	int fillSquare(ToneChan *t, int16 *buf, int len);
+
+private:
+	AgiEngine *_vm;
+	SndGenChan _channel[CHAN_MAX];
+	ToneChan _tchannel[CHAN_MAX];
+	int16 *_chanData;
+	int _chanAllocated;
+
+	int _dissolveMethod;
+};
+
+} // End of namespace Agi
+
+#endif /* AGI_SOUND_PCJR_H */


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


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