[Scummvm-cvs-logs] CVS: scummvm/scumm player_v2.cpp,2.4,2.5 player_v2.h,2.1,2.2

Jochen Hoenicke hoenicke at users.sourceforge.net
Sat May 31 09:55:02 CEST 2003


Update of /cvsroot/scummvm/scummvm/scumm
In directory sc8-pr-cvs1:/tmp/cvs-serv16304

Modified Files:
	player_v2.cpp player_v2.h 
Log Message:
Made thread-safe.
PCjr support.
Rewrote the generator, so I can reuse more code between PCjr and speaker output.
Added function to set master volume and to switch between PCjr and speaker.


Index: player_v2.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/scumm/player_v2.cpp,v
retrieving revision 2.4
retrieving revision 2.5
diff -u -d -r2.4 -r2.5
--- player_v2.cpp	31 May 2003 11:23:25 -0000	2.4
+++ player_v2.cpp	31 May 2003 16:54:46 -0000	2.5
@@ -24,6 +24,17 @@
 
 #define FREQ_HZ 236 // Don't change!
 
+#define SPK_DECAY   0xfa00              /* Depends on sample rate */
+#define PCJR_DECAY  0xf600              /* Depends on sample rate */
+
+#define FIXP_SHIFT  16
+#define MAX_OUTPUT 0x7fff
+
+#define NG_PRESET 0x0f35        /* noise generator preset */
+#define FB_WNOISE 0x12000       /* feedback for white noise */
+#define FB_PNOISE 0x08000       /* feedback for periodic noise */
+
+
 const uint8 note_lengths[] = {
 	0,  
 	0,  0,  2,
@@ -316,65 +327,133 @@
 
 Player_V2::Player_V2() {
 	int i;
-
+	
 	// This simulates the pc speaker sound, which is driven
 	// by the 8253 (square wave generator) and a low-band filter.
+	
+	_system = g_system;
+	_sample_rate = _system->property(OSystem::PROP_GET_SAMPLE_RATE, 0);
+	_mutex = _system->create_mutex();
 
-	sample_rate = g_system->property(OSystem::PROP_GET_SAMPLE_RATE, 0);
-	ticks_per_sample = 1193000*1000 / sample_rate;
-	last_freq = freq = 0;
-	samples_left = 0;
-	level = 0;
-	decay = 0xF400; // 63455 // found by try and error
-//	for (i = 0; (sample_rate << i) < 30000; i++)
-//		decay = decay * decay / 65536;
-
-	_mixer = g_mixer;
-	_mixer->setupPremix(this, premix_proc);
-	for (i = 0; i < 4; ++i) {
-		clear_channel(i);
-	}
-
-	pcjr = 0;
-	freqs_table = spk_freq_table;
+	// Initialize sound queue 
 	current_nr = next_nr = 0;
 	current_data = next_data = 0;
+	
+	// Initialize channel code
+	for (i = 0; i < 4; ++i)
+		clear_channel(i);
+
 	_next_tick = 0;
+	_tick_len = (_sample_rate << FIXP_SHIFT) / FREQ_HZ;
+
+	// Initialize square generator
+	_level = 0;
+
+	_RNG = NG_PRESET;
+
+	set_pcjr(true);
+	set_master_volume(255);
+
+	_mixer = g_mixer;
+	_mixer->setupPremix(this, premix_proc);
 }
 
 Player_V2::~Player_V2() {
+	mutex_up();
 	// Detach the premix callback handler
 	_mixer->setupPremix (0, 0);
+	mutex_down();
+	_system->delete_mutex (_mutex);
 }
 
-void Player_V2::chainNextSound() {
+void Player_V2::set_pcjr (bool pcjr) {
+	mutex_up();
+	_pcjr = pcjr;
+
+	if (_pcjr) {
+		_decay = PCJR_DECAY;
+		_update_step = (_sample_rate << FIXP_SHIFT) / (111860 * 2);
+		_freqs_table = pcjr_freq_table;
+	} else {
+		_decay = SPK_DECAY;
+		_update_step = (_sample_rate << FIXP_SHIFT) / (1193000 * 2);
+		_freqs_table = spk_freq_table;
+	}
+
+	/* adapt _decay to sample rate.  It must be squared when
+	 * sample rate doubles.
+	 */
 	int i;
+	for (i = 0; (_sample_rate << i) < 30000; i++)
+		_decay = _decay * _decay / 65536;
 
-	if (next_nr) {
-		for (i = 0; i < 4; i++)
-			clear_channel(i);
-		current_nr = next_nr;
-		current_data = next_data;
-		for (i = 0; i < 4; i++) {
-			channels[i].d.next_cmd = READ_LE_UINT16(next_data+6+2*i);
+
+	_timer_output = 0;
+	for (i = 0; i < 4; i++)
+		_timer_count[i] = 0;
+
+	if (current_data)
+		restartSound();
+	mutex_down();
+}
+
+void Player_V2::set_master_volume (int vol) {
+	if (vol > 255)
+		vol = 255;
+
+	/* scale to int16, FIXME: find best value */
+	double out = vol * 128 / 3;
+	
+    /* build volume table (2dB per step) */
+	for (int i = 0; i < 15; i++) {
+		/* limit volume to avoid clipping */
+		if (out > 0x7fff)
+			_volumetable[i] = 0x7fff;
+		else
+			_volumetable[i] = (int) out;
+
+		out /= 1.258925412;         /* = 10 ^ (2/20) = 2dB */
+	}
+	_volumetable[15] = 0;
+}
+
+void Player_V2::chainSound(int nr, byte *data) {
+	int offset = _pcjr ? 14 : 6;
+	current_nr = nr;
+	current_data = data;
+
+	for (int i = 0; i < 4; i++) {
+		clear_channel(i);
+
+		channels[i].d.music_script_nr = nr;
+		if (data) {
+			channels[i].d.next_cmd = READ_LE_UINT16(data+offset+2*i);
 			if (channels[i].d.next_cmd)
 				channels[i].d.time_left = 1;
-			channels[i].d.music_script_nr = current_nr;
 		}
+	}
+}
+
+void Player_V2::chainNextSound() {
+	if (next_nr) {
+		chainSound(next_nr, next_data);
 		next_nr = 0;
 		next_data = 0;
 	}
 }
 
 void Player_V2::stopAllSounds() {
+	mutex_up();
 	for (int i = 0; i < 4; i++) {
 		clear_channel(i);
 	}
 	next_nr = current_nr = 0;
 	next_data = current_data = 0;
+	mutex_down();
 }
 
 void Player_V2::stopSound(int nr) {
+	mutex_up();
 	if (next_nr == nr) {
 		next_nr = 0;
 		next_data = 0;
@@ -387,32 +466,28 @@
 		current_data = 0;
 		chainNextSound();
 	}
+	mutex_down();
 }
 
 void Player_V2::startSound(int nr, byte *data) {
-	int cprio = current_data ? READ_LE_UINT16(current_data+4) : 0;
-	int prio  = READ_LE_UINT16(data+4);
-	int nprio = next_data ? READ_LE_UINT16(next_data+4) : 0;
+	mutex_up();
 
-	if (!current_nr || (cprio & 0xff) <= (prio & 0xff)) {
+	int cprio = current_data ? *(current_data+4) : 0;
+	int prio  = *(data+4);
+	int nprio = next_data ? *(next_data+4) : 0;
+
+	int restartable = *(data+5);
+
+	if (!current_nr || cprio <= prio) {
 		int tnr = current_nr;
 		int tprio = cprio;
 		byte *tdata  = current_data;
-		int i;
-		for (i = 0; i < 4; i++)
-			clear_channel(i);
 
-		current_nr = nr;
-		current_data = data;
-		for (i = 0; i < 4; i++) {
-			channels[i].d.next_cmd = READ_LE_UINT16(data+6+2*i);
-			if (channels[i].d.next_cmd)
-				channels[i].d.time_left = 1;
-			channels[i].d.music_script_nr = current_nr;
-		}
+		chainSound(nr, data);
 		nr   = tnr;
 		prio = tprio;
 		data = tdata;
+		restartable = data ? *(data+5) : 0;
 	}
 	
 	if (!current_nr) {
@@ -422,13 +497,24 @@
 	}
 	
 	if (nr != current_nr
-	    && (prio & 0xff00)
+	    && restartable
 	    && (!next_nr
-		|| (nprio & 0xff) <= (prio & 0xff))) {
+		|| nprio <= prio)) {
 
 		next_nr = nr;
 		next_data = data;
 	}
+
+	mutex_down();
+}
+
+void Player_V2::restartSound() {
+	if (*(current_data + 5)) {
+		/* current sound is restartable */
+		chainSound(current_nr, current_data);
+	} else {
+		chainNextSound();
+	}
 }
 
 int Player_V2::getSoundStatus(int nr) {
@@ -604,11 +690,11 @@
 				note = note % 12;
 				dest_channel->d.hull_offset = 0;
 				dest_channel->d.hull_counter = 1;
-				if (pcjr && dest_channel == &channels[3]) {
+				if (_pcjr && dest_channel == &channels[3]) {
 					dest_channel->d.hull_curve = 180 + note * 12;
 					myfreq = 384 - 64 * octave;
 				} else {
-					myfreq = freqs_table[note] >> octave;
+					myfreq = _freqs_table[note] >> octave;
 				}
 				dest_channel->d.freq = dest_channel->d.base_freq = myfreq;
 				if (is_last_note)
@@ -694,58 +780,137 @@
 }
 
 void Player_V2::do_mix (int16 *data, int len) {
+	mutex_up();
 	int step;
 
 	do {
 		step = len;
-		if (step > _next_tick)
-			step = _next_tick;
+		if (step > (_next_tick >> FIXP_SHIFT))
+			step = (_next_tick >> FIXP_SHIFT);
 
-		if (freq == 0 && level == 0)
-			memset (data, 0, sizeof(int16) * step);
+		if (_pcjr)
+			generatePCjrSamples(data, step);
 		else
-			generate_samples(data, step);
+			generateSpkSamples(data, step);
 		data += step;
+		_next_tick -= step << FIXP_SHIFT;
 
-		if (!(_next_tick -= step)) {
-//			if (_timer_proc)
-//				(*_timer_proc) (_timer_param);
-			int winning_channel = -1;
+		if (!(_next_tick >> FIXP_SHIFT)) {
 			for (int i = 0; i < 4; i++) {
 				if (!channels[i].d.time_left)
 					continue;
 				next_freqs(&channels[i]);
+			}
+			_next_tick += _tick_len;
+		}
+	} while (len -= step);
+	mutex_down();
+}
 
-				if (winning_channel == -1
-				    && channels[i].d.volume
-				    && channels[i].d.time_left) {
-					winning_channel = i;
+void Player_V2::lowPassFilter(int16 *sample, int len) {
+	for (int i = 0; i < len; i++) {
+		_level = ((int)_level * _decay
+			 + (int)sample[i] * (0x10000-_decay)) >> 16;
+		sample[i] = _level;
+	}
+}
+
+void Player_V2::squareGenerator(int channel, int freq, int vol,
+                                int noiseFeedback, int16 *sample, int len) {
+	int period = _update_step * freq;
+	if (period == 0)
+		period = _update_step;
+
+	for (int i = 0; i < len; i++) {
+		unsigned int duration = 0;
+		
+		if (_timer_output & (1 << channel))
+			duration += _timer_count[channel];
+		
+		_timer_count[channel] -= (1 << FIXP_SHIFT);
+		while (_timer_count[channel] <= 0) {
+
+			if (noiseFeedback) {
+				if (_RNG & 1) {
+					_RNG ^= noiseFeedback;
+					_timer_output ^= (1 << channel);
 				}
-			}
-			if (winning_channel != -1) {
-				freq = channels[winning_channel].d.freq;
+				_RNG >>= 1;
 			} else {
-				freq = 0;
+				_timer_output ^= (1 << channel);
 			}
-			_next_tick = 93;
+
+			if (_timer_output & (1 << channel))
+				duration += period;
+
+			_timer_count[channel] += period;
 		}
-	} while (len -= step);
+		
+		if (_timer_output & (1 << channel))
+			duration -= _timer_count[channel];
+		
+		sample[i] += (duration * _volumetable[vol]) >> FIXP_SHIFT;
+		if (sample[i] < 0) {
+			/* overflow: clip value */
+			sample[i] = 0x7fff;
+		}
+	}
 }
 
-void Player_V2::generate_samples(int16 *data, int step) {
-	int j;
-	for (j = 0; j < step; j++) {
-		level = (level * decay) >> 16;
-		if (ticks_counted < freq*500)
-			level += 0xffff - decay;
+void Player_V2::generateSpkSamples(int16 *data, int len) {
+	int winning_channel = -1;
+	int freq;
+	for (int i = 0; i < 4; i++) {
+		if (winning_channel == -1
+		    && channels[i].d.volume
+		    && channels[i].d.time_left) {
+			winning_channel = i;
+		}
+	}
 
-		data[j] = (level >> 1);
-		
-		ticks_counted += ticks_per_sample;
-		if (ticks_counted >= last_freq*1000) {
-			ticks_counted -= last_freq*1000;
-			last_freq = freq;
+	memset (data, 0, sizeof(int16) * len);
+	if (winning_channel != -1) {
+		squareGenerator(0, channels[winning_channel].d.freq, 0, 
+				0, data, len);
+	} else if (_level == 0)
+		/* shortcut: no sound is being played. */
+		return;
+	
+	lowPassFilter(data, len);
+}
+
+void Player_V2::generatePCjrSamples(int16 *data, int len) {
+	int i;
+	int freq, vol;
+
+	memset(data, 0, sizeof(int16) * len);
+	bool hasdata = false;
+
+	for (i = 0; i < 4; i++) {
+		freq = channels[i].d.freq >> 6;
+		vol  = (65535 - channels[i].d.volume) >> 12;
+		if (!channels[i].d.volume || !channels[i].d.time_left) {
+			_timer_count[i] -= len << FIXP_SHIFT;
+			if (_timer_count[i] < 0)
+				_timer_count[i] = 0;
+		} else if (i < 3) {
+			hasdata = true;
+			squareGenerator(i, freq, vol, 0, data, len);
+		} else {
+			int noiseFB = (freq & 4) ? FB_WNOISE : FB_PNOISE;
+			int n = (freq & 3);
+			
+			freq = (n == 3) ? 2 * (channels[2].d.freq>>6) : 1 << (5 + n);
+			hasdata = true;
+			squareGenerator(i, freq, vol, noiseFB, data, len);
 		}
+#if 0
+		debug(9, "channel[%d]: freq %d %.1f ; volume %d", 
+		      i, freq, 111860.0/freq,  vol);
+#endif
 	}
+
+	if (_level || hasdata)
+		lowPassFilter(data, len);
 }
 

Index: player_v2.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/scumm/player_v2.h,v
retrieving revision 2.1
retrieving revision 2.2
diff -u -d -r2.1 -r2.2
--- player_v2.h	26 May 2003 22:03:04 -0000	2.1
+++ player_v2.h	31 May 2003 16:54:46 -0000	2.2
@@ -62,6 +62,9 @@
 	Player_V2();
 	~Player_V2();
 
+	void set_pcjr(bool pcjr);
+	void set_master_volume(int vol);
+
 	void startSound(int nr, byte *data);
 	void stopSound(int nr);
 	void stopAllSounds();
@@ -69,18 +72,23 @@
 
 private:
 	SoundMixer *_mixer;
+
+
+	bool _pcjr;
+
+	int _sample_rate;
 	int _next_tick;
+	int _tick_len;
+	unsigned int _update_step;
+	unsigned int _decay;
+	unsigned int _level;
+	unsigned int _RNG;
+	unsigned int _volumetable[16];
 
-	int sample_rate;
-	int ticks_per_sample;
-	int ticks_counted;
-	int samples_left;
-	int freq;
-	int last_freq;
-	int level;
-	int pcjr;
-	const uint16 *freqs_table;
-	unsigned int decay;
+	int _timer_count[4];
+	int _timer_output;
+
+	const uint16 *_freqs_table;
 
 	ChannelInfo channels[4];
 
@@ -90,15 +98,27 @@
 	byte *next_data;
 	byte *retaddr;
 
+	OSystem *_system;
+	void *_mutex;
+	void mutex_up() { _system->lock_mutex (_mutex); }
+	void mutex_down() { _system->unlock_mutex (_mutex); }
+
+	void restartSound();
 	void execute_cmd(ChannelInfo *channel);
 	void next_freqs(ChannelInfo *channel);
-	void generate_samples(int16 *buf, int len);
-	void on_timer();
 	void clear_channel(int i);
+	void chainSound(int nr, byte *data);
 	void chainNextSound();
 
 	static void premix_proc(void *param, int16 *buf, uint len);
 	void do_mix (int16 *buf, int len);
+
+	void lowPassFilter(int16 *data, int len);
+	void squareGenerator(int channel, int freq, int vol,
+	                     int noiseFeedback, int16 *sample, int len);
+	void generateSpkSamples(int16 *data, int len);
+	void generatePCjrSamples(int16 *data, int len);
+    
 };
 
 #endif





More information about the Scummvm-git-logs mailing list