1 //
2 // /home/ms/source/sidplay/libsidplay/RCS/eeconfig.cpp,v
3 //
4 // SID Emulator Engine configuration interface class.
5 // Quick documentation in header file ``include/emucfg.h''.
6 
7 #include "eeconfig.h"
8 
9 // ------------------------------------------------------------- constructors
10 
emuEngine()11 emuEngine::emuEngine()
12 {
13 	// Set the defaults.
14 	config.frequency = 44100;
15 	config.bitsPerSample = SIDEMU_16BIT;
16 	config.sampleFormat = SIDEMU_SIGNED_PCM;
17 	config.channels = SIDEMU_MONO;
18 	config.sidChips = 1;
19 	config.volumeControl = SIDEMU_NONE;
20 	config.mos8580 = false;
21 	config.measuredVolume = true;
22 	config.digiPlayerScans = 10*50;
23 	config.emulateFilter = true;
24 	config.autoPanning = SIDEMU_NONE;
25 	config.memoryMode = MPU_BANK_SWITCHING;
26 	config.clockSpeed = SIDTUNE_CLOCK_PAL;
27 	config.forceSongSpeed = false;
28 
29 #if defined(SIDEMU_TIME_COUNT)
30 	// Reset data counter.
31 	bytesCountTotal = (bytesCountSong = 0);
32 	secondsTotal = (secondsThisSong = 0);
33 #endif
34 
35 	isThreeVoiceTune = false;
36 
37 	extern void sidEmuResetAutoPanning(int);
38 	sidEmuResetAutoPanning(config.autoPanning);
39 
40 	// Allocate memory for the interpreter.
41 	c64memFree();
42 	MPUstatus = c64memAlloc();
43 
44 	// Allocate memory for the SID emulator engine.
45 	freeMem();
46 	if ( MPUstatus && allocMem() )
47 	{
48 		setRandomSeed();
49 		MPUreset();
50 		configureSID();
51 		initMixerEngine();
52 		setDefaultVoiceVolumes();
53 		setDefaultFilterStrength();
54 		isReady = true;
55 		reset();
56 	}
57 	else
58 	{
59 		isReady = false;
60 	}
61 }
62 
63 // -------------------------------------------------------------- destructors
64 
~emuEngine()65 emuEngine::~emuEngine()
66 {
67 	c64memFree();
68 	freeMem();
69 }
70 
71 // -------------------------------------------------- public member functions
72 
73 const char* versionString = emu_version;
74 
getVersionString()75 const char* emuEngine::getVersionString()
76 {
77 	return versionString;
78 }
79 
setConfig(struct emuConfig & inCfg)80 bool emuEngine::setConfig( struct emuConfig& inCfg )
81 {
82 	bool gotInvalidConfig = false;
83 
84 	// Validate input value.
85 	if ((inCfg.memoryMode == MPU_BANK_SWITCHING)
86 		|| (inCfg.memoryMode == MPU_TRANSPARENT_ROM)
87 		|| (inCfg.memoryMode == MPU_PLAYSID_ENVIRONMENT))
88 	{
89 		config.memoryMode = inCfg.memoryMode;
90 	}
91 	else
92 	{
93 		gotInvalidConfig = true;  // invalid settings
94 	}
95 
96 	// Validate input value.
97 	// Check various settings before doing a single SID-config call.
98 	bool newSIDconfig = false;
99 	bool newFilterInit = false;
100 
101 	if ((inCfg.clockSpeed == SIDTUNE_CLOCK_PAL)
102 		|| (inCfg.clockSpeed == SIDTUNE_CLOCK_NTSC))
103 	{
104 		if (inCfg.clockSpeed != config.clockSpeed)
105 		{
106 			config.clockSpeed = inCfg.clockSpeed;
107 			newSIDconfig = true;
108 		}
109 	}
110 	else
111 	{
112 		gotInvalidConfig = true;  // invalid settings
113 	}
114 
115 	if (inCfg.forceSongSpeed != config.forceSongSpeed)
116 	{
117 		config.forceSongSpeed = (inCfg.forceSongSpeed == true);
118 	}
119 
120 	// Range-check the sample frequency.
121 	if (( inCfg.frequency >= 4000 ) && ( inCfg.frequency <= 48000 ))
122 	{
123 		// Has it changed ? Then do the necessary initialization.
124 		if ( inCfg.frequency != config.frequency )
125 		{
126 			config.frequency = inCfg.frequency;
127 			newSIDconfig = true;
128 			newFilterInit = true;
129 		}
130 	}
131 	else
132 	{
133 		gotInvalidConfig = true;  // invalid settings
134 	}
135 
136 	if (inCfg.measuredVolume != config.measuredVolume)
137 	{
138 		config.measuredVolume = (inCfg.measuredVolume == true);
139 		newSIDconfig = true;
140 	}
141 
142 	// The mixer mode, the sample format, the number of channels and bits per
143 	// sample all affect the mixing tables and settings.
144 	// Hence we define a handy flag here.
145 	bool newMixerSettings = false;
146 
147 	// Is the requested sample format valid?
148 	if (( inCfg.sampleFormat == SIDEMU_UNSIGNED_PCM )
149 		|| ( inCfg.sampleFormat == SIDEMU_SIGNED_PCM ))
150 	{
151 		// Has it changed ? Then do the necessary initialization.
152 		if ( inCfg.sampleFormat != config.sampleFormat )
153 		{
154 			config.sampleFormat = inCfg.sampleFormat;
155 			newMixerSettings = true;
156 		}
157 	}
158 	else
159 	{
160 		gotInvalidConfig = true;  // invalid settings
161 	}
162 
163 	// Is the requested number of channels valid?
164 	if (( inCfg.channels == SIDEMU_MONO )
165 		|| ( inCfg.channels == SIDEMU_STEREO ))
166 	{
167 		// Has it changed ? Then do the necessary initialization.
168 		if ( inCfg.channels != config.channels )
169 		{
170 			config.channels = inCfg.channels;
171 			setDefaultVoiceVolumes();
172 			newMixerSettings = true;
173 		}
174 	}
175 	else
176 	{
177 		gotInvalidConfig = true;  // invalid settings
178 	}
179 
180 	// Is the requested sample precision valid?
181 	if (( inCfg.bitsPerSample == SIDEMU_8BIT )
182 		|| ( inCfg.bitsPerSample == SIDEMU_16BIT ))
183 	{
184 		// Has it changed ? Then do the necessary initialization.
185 		if ( inCfg.bitsPerSample != config.bitsPerSample )
186 		{
187 			config.bitsPerSample = inCfg.bitsPerSample;
188 			newMixerSettings = true;
189 		}
190 	}
191 	else
192 	{
193 		gotInvalidConfig = true;  // invalid settings
194 	}
195 
196 	// Is the requested mixing mode valid?
197 	if (( inCfg.volumeControl == SIDEMU_NONE )
198 		|| ( inCfg.volumeControl == SIDEMU_VOLCONTROL )
199 		|| ( inCfg.volumeControl == SIDEMU_FULLPANNING )
200 		|| ( inCfg.volumeControl == SIDEMU_HWMIXING )
201 		|| ( inCfg.volumeControl == SIDEMU_STEREOSURROUND ))
202 	{
203 		// Has it changed ? Then do the necessary initialization.
204 		if ( inCfg.volumeControl != config.volumeControl )
205 		{
206 			config.volumeControl = inCfg.volumeControl;
207 			setDefaultVoiceVolumes();
208 			newMixerSettings = true;
209 		}
210 	}
211 	else
212 	{
213 		gotInvalidConfig = true;  // invalid settings
214 	}
215 
216 	if ((inCfg.autoPanning == SIDEMU_NONE)
217 		|| (inCfg.autoPanning == SIDEMU_CENTEREDAUTOPANNING))
218 	{
219 		if (inCfg.autoPanning != config.autoPanning)
220 		{
221 			config.autoPanning = inCfg.autoPanning;
222 			if (config.autoPanning != SIDEMU_NONE)
223 			{
224 				if (( config.volumeControl != SIDEMU_FULLPANNING )
225 					&& ( config.volumeControl != SIDEMU_STEREOSURROUND ))
226 				{
227 					config.autoPanning = false;
228 					gotInvalidConfig = true;  // wrong mixing mode
229 				}
230 			}
231 			extern void sidEmuResetAutoPanning(int);
232 			sidEmuResetAutoPanning(config.autoPanning);
233 		}
234 	}
235 	else
236 	{
237 		gotInvalidConfig = true;  // invalid panning mode
238 	}
239 
240 	if (inCfg.emulateFilter != config.emulateFilter)
241 	{
242 		config.emulateFilter = (inCfg.emulateFilter == true);
243 		newSIDconfig = true;      // filter
244 		newMixerSettings = true;  // amplification
245 	}
246 	// Range-check the filter settings.
247 	if (( inCfg.filterFs >= 1.0 ) && ( inCfg.filterFm != 0 ))
248 	{
249 		// Have they changed? Then do the necessary initialization.
250 		if ((inCfg.filterFs != config.filterFs)
251 			|| (inCfg.filterFm != config.filterFm)
252 			|| (inCfg.filterFt != config.filterFt))
253 		{
254 			config.filterFs = inCfg.filterFs;
255 			config.filterFm = inCfg.filterFm;
256 			config.filterFt = inCfg.filterFt;
257 			newFilterInit = true;
258 		}
259 	}
260 	else
261 	{
262 		gotInvalidConfig = true;  // invalid settings
263 	}
264 
265 	if (inCfg.digiPlayerScans != config.digiPlayerScans)
266 	{
267 		config.digiPlayerScans = inCfg.digiPlayerScans;
268 		newMixerSettings = true;  // extra amplification
269 	}
270 
271 	if ((config.channels==SIDEMU_MONO) &&
272 		((config.volumeControl==SIDEMU_STEREOSURROUND)
273 		 ||(config.autoPanning!=SIDEMU_NONE)))
274 	{
275 		gotInvalidConfig = true;  // invalid settings
276 	}
277 
278 	if (inCfg.mos8580 != config.mos8580)
279 	{
280 		config.mos8580 = (inCfg.mos8580 == true);
281 		newSIDconfig = true;
282 	}
283 
284 	// Here re-initialize the SID, if required.
285 	if (newSIDconfig)
286 	{
287 		configureSID();
288 	}
289 
290 	// Here re-initialize the mixer engine, if required.
291 	if (newMixerSettings)
292 	{
293 		initMixerEngine();
294 	}
295 
296 		// Here re-initialize the filter settings, if required.
297 	if (newFilterInit)
298 	{
299 		filterTableInit();
300 	}
301 
302 	// Return flag, whether input config was valid.
303 	return !gotInvalidConfig;
304 }
305 
getConfig(struct emuConfig & outCfg)306 void emuEngine::getConfig( struct emuConfig& outCfg )
307 {
308 	outCfg = config;
309 }
310 
setDefaultFilterStrength()311 void emuEngine::setDefaultFilterStrength()
312 {
313 	config.filterFs = SIDEMU_DEFAULTFILTERFS;
314 	config.filterFm = SIDEMU_DEFAULTFILTERFM;
315 	config.filterFt = SIDEMU_DEFAULTFILTERFT;
316 	filterTableInit();
317 }
318 
verifyEndianess()319 bool emuEngine::verifyEndianess()
320 {
321 	// Test endianess. Should swap bytes.
322 	ubyte endTest[2];
323 	writeLEword(endTest,0x55aa);
324 	if (0xaa55!=readBEword(endTest))
325 		return false;
326 	else
327 		return true;
328 }
329 
330 #if defined(SIDEMU_TIME_COUNT)
getSecondsThisSong()331 int emuEngine::getSecondsThisSong()
332 {
333 	return secondsThisSong;
334 }
335 
getSecondsTotal()336 int emuEngine::getSecondsTotal()
337 {
338 	return secondsTotal;
339 }
340 #endif
341 
342 // ------------------------------------------------- private member functions
343 
freeMem()344 bool emuEngine::freeMem()
345 {
346 	if ( ampMod1x8 != 0 )
347 		delete[] ampMod1x8;
348 	ampMod1x8 = 0;
349 	if ( signedPanMix8 != 0 )
350 		delete[] signedPanMix8;
351 	signedPanMix8 = 0;
352 	if ( signedPanMix16 != 0 )
353 		delete[] signedPanMix16;
354 	signedPanMix16 = 0;
355 	return true;
356 }
357 
allocMem()358 bool emuEngine::allocMem()
359 {
360 	// Keep track of memory allocation failures.
361 	bool wasSuccess = true;
362 	// Seems as if both tables are needed for panning-mixing with 16-bit samples.
363 	// 8-bit
364 #ifdef SID_HAVE_EXCEPTIONS
365 	if (( ampMod1x8 = new(std::nothrow) sbyte[256*256] ) == 0 )
366 #else
367 	if (( ampMod1x8 = new sbyte[256*256] ) == 0 )
368 #endif
369 		wasSuccess = false;
370 #ifdef SID_HAVE_EXCEPTIONS
371 	if (( signedPanMix8 = new(std::nothrow) sbyte[256*256] ) == 0 )
372 #else
373 	if (( signedPanMix8 = new sbyte[256*256] ) == 0 )
374 #endif
375 		wasSuccess = false;
376 	// 16-bit
377 #ifdef SID_HAVE_EXCEPTIONS
378 	if (( signedPanMix16 = new(std::nothrow) sword[256*256] ) == 0 )
379 #else
380 	if (( signedPanMix16 = new sword[256*256] ) == 0 )
381 #endif
382 		wasSuccess = false;
383 
384 	if (!wasSuccess)
385 	{
386 		freeMem();
387 	}
388 	return wasSuccess;
389 }
390 
MPUreset()391 void emuEngine::MPUreset()
392 {
393 	if (MPUstatus)
394 	{
395 		initInterpreter(config.memoryMode);
396 		c64memClear();
397 		c64memReset(config.clockSpeed,randomSeed);
398 	}
399 }
400 
MPUreturnRAMbase()401 ubyte * emuEngine::MPUreturnRAMbase()
402 {
403 	if (MPUstatus)
404 	{
405 		return c64mem1;
406 	}
407 	else
408 	{
409 		return 0;
410 	}
411 }
412 
setRandomSeed()413 void emuEngine::setRandomSeed()
414 {
415 	time_t now = time(NULL);
416 	randomSeed = (ubyte)now;
417 }
418 
reset()419 bool emuEngine::reset()
420 {
421 	if (isReady)
422 	{
423 		// Check internal mixer state whether tables have to be redone.
424 		if (config.digiPlayerScans != 0)
425 		{
426 			if (isThreeVoiceAmplified != isThreeVoiceTune)  // needs diff.ampl.
427 			{
428 				initMixerEngine();
429 			}
430 		}
431 		else if (isThreeVoiceAmplified)  // and do not want amplification
432 		{
433 			initMixerEngine();
434 		}
435 		extern bool sidEmuReset();
436 		sidEmuReset();
437 
438 		resetSampleEmu();
439 	}
440 	return isReady;
441 }
442 
resetSampleEmu()443 bool emuEngine::resetSampleEmu()
444 {
445 	sampleEmuReset();
446 	return true;
447 }
448 
amplifyThreeVoiceTunes(bool inIsThreeVoiceTune)449 void emuEngine::amplifyThreeVoiceTunes(bool inIsThreeVoiceTune)
450 {
451 	// ::initMixerEngine depends on this and config.digiPlayerScans.
452 	// Specify whether a sidtune uses only three voices (here: no digis).
453 	isThreeVoiceTune = inIsThreeVoiceTune;
454 }
455 
456 // Initialize the SID chip and everything that depends on the frequency.
configureSID()457 void emuEngine::configureSID()
458 {
459 	extern void sidEmuConfigure(udword PCMfrequency, bool measuredEnveValues,
460 								bool isNewSID, bool emulateFilter, int clockSpeed);
461 	sidEmuConfigure(config.frequency,config.measuredVolume,config.mos8580,
462 					config.emulateFilter,config.clockSpeed);
463 }
464 
initMixerEngine()465 void emuEngine::initMixerEngine()
466 {
467 	uword uk;
468 
469 	// If just three (instead of four) voices, do different amplification,
470 	// if that is desired (digiPlayerScans!=0).
471 	if ((config.digiPlayerScans!=0) && isThreeVoiceTune)
472 	{
473 		isThreeVoiceAmplified = true;
474 	}
475 	else
476 	{
477 		isThreeVoiceAmplified = false;
478 	}
479 
480 #if defined(SID_FPUMIXERINIT)
481 	sdword si, sj;
482 
483 	// 8-bit volume modulation tables.
484 	float filterAmpl = 1.0;
485 	if (config.emulateFilter)
486 	{
487 		filterAmpl = 0.7;
488 	}
489 	uk = 0;
490 	for ( si = 0; si < 256; si++ )
491 	{
492 		for ( sj = -128; sj < 128; sj++, uk++ )
493 		{
494 			ampMod1x8[uk] = (sbyte)(((si*sj)/255)*filterAmpl);
495 		}
496 	}
497 
498 	// Determine single-voice de-amplification.
499 	float ampDiv;  // logical voices per physical channel
500 	if ( config.volumeControl == SIDEMU_HWMIXING )
501 	{
502 		ampDiv = 1.0;
503 	}
504 	else if ((config.channels==SIDEMU_STEREO) &&
505 			 ((config.volumeControl==SIDEMU_NONE)
506 			  ||(config.volumeControl==SIDEMU_VOLCONTROL)))
507 	{
508 		ampDiv = 2.0;
509 	}
510 	else  // SIDEMU_MONO or SIDEMU_FULLPANNING or SIDEMU_STEREOSURROUND
511 	{
512 		if (isThreeVoiceAmplified)
513 		{
514 			ampDiv = 3.0;
515 		}
516 		else
517 		{
518 			ampDiv = 4.0;
519 		}
520 	}
521 
522 	uk = 0;
523 	for ( si = 0; si < 256; si++ )
524 	{
525 		for ( sj = -128; sj < 128; sj++, uk++ )
526 		{
527 			// 8-bit mixing modulation tables.
528 			signedPanMix8[uk] = (sbyte)(((si*sj)/255)/ampDiv);
529 			// 16-bit mixing modulation tables.
530 			signedPanMix16[uk] = (uword)((si*sj)/ampDiv);
531 		}
532 	}
533 #else
534 	sdword si, sj;
535 
536 	// 8-bit volume modulation tables.
537 	sword filterAmpl = 10;
538 	if (config.emulateFilter)
539 	{
540 		filterAmpl = 7;
541 	}
542 	uk = 0;
543 	for ( si = 0; si < 256; si++ )
544 	{
545 		for ( sj = -128; sj < 128; sj++, uk++ )
546 		{
547 			ampMod1x8[uk] = (sbyte)((si*sj*filterAmpl)/(255*10));
548 		}
549 	}
550 
551 	// Determine single-voice de-amplification.
552 	short ampDiv;  // logical voices per physical channel
553 	if ( config.volumeControl == SIDEMU_HWMIXING )
554 	{
555 		ampDiv = 1;
556 	}
557 	else if ((config.channels==SIDEMU_STEREO) &&
558 			 ((config.volumeControl==SIDEMU_NONE)
559 			  ||(config.volumeControl==SIDEMU_VOLCONTROL)))
560 	{
561 		ampDiv = 2;
562 	}
563 	else  // SIDEMU_MONO or SIDEMU_FULLPANNING or SIDEMU_STEREOSURROUND
564 	{
565 		if (isThreeVoiceAmplified)
566 		{
567 			ampDiv = 3;
568 		}
569 		else
570 		{
571 			ampDiv = 4;
572 		}
573 	}
574 
575 	uk = 0;
576 	for ( si = 0; si < 256; si++ )
577 	{
578 		for ( sj = -128; sj < 128; sj++, uk++ )
579 		{
580 			// 8-bit mixing modulation tables.
581 			signedPanMix8[uk] = (sbyte)((si*sj)/(255*ampDiv));
582 			// 16-bit mixing modulation tables.
583 			signedPanMix16[uk] = (uword)((si*sj)/ampDiv);
584 		}
585 	}
586 #endif
587 
588 	// ------------------------------------------------- Init mixer function.
589 
590 	extern void* fill8bitMono(void*, udword);
591 	extern void* fill8bitMonoControl(void*, udword);
592 	extern void* fill8bitSplit(void*, udword);
593 	extern void* fill8bitStereo(void*, udword);
594 	extern void* fill8bitStereoControl(void*, udword);
595 	extern void* fill8bitStereoSurround(void*, udword);
596 	extern void* fill16bitMono(void*, udword);
597 	extern void* fill16bitMonoControl(void*, udword);
598 	extern void* fill16bitStereo(void*, udword);
599 	extern void* fill16bitStereoControl(void*, udword);
600 	extern void* fill16bitStereoSurround(void*, udword);
601 	extern void* fill16bitSplit(void*, udword);
602 
603 	typedef void* (*ptr2fillfunc)(void*,udword);
604 	// Fill function lookup-table.
605 	// bits, mono/stereo, control
606 	const ptr2fillfunc fillfunctions[2][2][4] =
607 	{
608 		{
609 			{
610 				&fill8bitMono, &fill8bitSplit, &fill8bitMonoControl, &fill8bitMonoControl
611 			},
612 			{
613 				&fill8bitStereo, &fill8bitSplit, &fill8bitStereoControl, &fill8bitStereoSurround
614 			},
615 		},
616 		{
617 			{
618 				&fill16bitMono, &fill16bitSplit, &fill16bitMonoControl, &fill16bitMonoControl
619 			},
620 			{
621 				&fill16bitStereo, &fill16bitSplit, &fill16bitStereoControl, &fill16bitStereoSurround
622 			},
623 		}
624 	};
625 
626 	int bitsIndex, monoIndex, controlIndex;
627 
628 	// Define the ``zero'' sample for signed or unsigned samples.
629 	ubyte zero8bit = 0x80;
630 	uword zero16bit = 0;
631 
632 	if ( config.bitsPerSample == SIDEMU_16BIT )
633 	{
634 		bitsIndex = 1;
635 		switch( config.sampleFormat )
636 		{
637 			// waveform and amplification tables are signed samples,
638 			// so adjusting the sign should do the conversion
639 		 case SIDEMU_SIGNED_PCM:
640 			{
641 				zero16bit = 0;
642 				break;
643 			}
644 		 case SIDEMU_UNSIGNED_PCM:
645 		 default:
646 			{
647 				zero16bit = 0x8000;
648 				break;
649 			}
650 		}
651 	}
652 	else  // if ( config.bitsPerSample == SIDEMU_8BIT )
653 	{
654 		bitsIndex = 0;
655 		switch( config.sampleFormat )
656 		{
657 			// waveform and amplification tables are signed samples,
658 			// so adjusting the sign should do the conversion
659 		 case SIDEMU_SIGNED_PCM:
660 			{
661 				zero8bit = 0;
662 				break;
663 			}
664 		 case SIDEMU_UNSIGNED_PCM:
665 		 default:
666 			{
667 				zero8bit = 0x80;
668 				break;
669 			}
670 		}
671 	} // end else config.bitsPerSample
672 
673 	switch( config.channels )
674 	{
675 	 case SIDEMU_MONO:
676 		{
677 			monoIndex = 0;
678 			break;
679 		}
680 	 case SIDEMU_STEREO:
681 	 default:
682 		{
683 			monoIndex = 1;
684 			break;
685 		}
686 	}
687 
688 	if ( config.volumeControl == SIDEMU_NONE )
689 		controlIndex = 0;
690 	else if ( config.volumeControl == SIDEMU_HWMIXING )
691 		controlIndex = 1;
692 	else if ( config.volumeControl == SIDEMU_STEREOSURROUND )
693 		controlIndex = 3;
694 	else
695 		controlIndex = 2;
696 
697 	extern void* (*sidEmuFillFunc)(void*, udword);
698 	sidEmuFillFunc = fillfunctions[bitsIndex][monoIndex][controlIndex];
699 
700 	// Call a function which inits more local tables.
701 	extern void MixerInit(bool threeVoiceAmplify, ubyte zero8, uword zero16);
702 	MixerInit(isThreeVoiceAmplified,zero8bit,zero16bit);
703 
704 	// ----------------------------------------------------------------------
705 
706 	// ensure that samplebuffer will be divided into
707 	// correct number of samples
708 	//  8-bit mono: buflen x = x samples
709 	//      stereo:          = x/2 samples
710 	// 16-bit mono: buflen x = x/2 samples
711 	//      stereo:          = x/4 samples
712 	extern ubyte bufferScale;
713 	bufferScale = 0;
714 	// HWMIXING mode does not know about stereo.
715 	if ((config.channels == SIDEMU_STEREO)
716 		&& !(config.volumeControl == SIDEMU_HWMIXING))
717 		bufferScale++;
718 	if ( config.bitsPerSample == SIDEMU_16BIT )
719 		bufferScale++;
720 }
721 
722 
setDefaultVoiceVolumes()723 void emuEngine::setDefaultVoiceVolumes()
724 {
725 	// Adjust default mixing gain. Does not matter, whether this will be used.
726 	// Signed 8-bit samples will be added to base array index.
727 	// So middle must be 0x80.
728 	// [-80,-81,...,-FE,-FF,0,1,...,7E,7F]
729 	// Mono: left channel only used.
730 	if ( config.channels == SIDEMU_MONO )
731 	{
732 		setVoiceVolume(1,255,0,256);
733 		setVoiceVolume(2,255,0,256);
734 		setVoiceVolume(3,255,0,256);
735 		setVoiceVolume(4,255,0,256);
736 	}
737 	// Stereo:
738 	else
739 	{ // if ( config.channels == SIDEMU_STEREO )
740 		if ( config.volumeControl == SIDEMU_STEREOSURROUND )
741 		{
742 			setVoiceVolume(1,255,255,256);
743 			setVoiceVolume(2,255,255,256);
744 			setVoiceVolume(3,255,255,256);
745 			setVoiceVolume(4,255,255,256);
746 		}
747 		else
748 		{
749 			setVoiceVolume(1,255,0,256);
750 			setVoiceVolume(2,0,255,256);
751 			setVoiceVolume(3,255,0,256);
752 			setVoiceVolume(4,0,255,256);
753 		}
754 	}
755 }
756 
757 
filterTableInit()758 void emuEngine::filterTableInit()
759 {
760 	uword uk;
761 	// Parameter calculation has not been moved to a separate function
762 	// by purpose.
763 	const float filterRefFreq = 44100.0;
764 
765 	extern filterfloat filterTable[0x800];
766 	float yMax = 1.0;
767 	float yMin = 0.01;
768 	uk = 0;
769 	for ( float rk = 0; rk < 0x800; rk++ )
770 	{
771 		filterTable[uk] = (((exp(rk/0x800*log(config.filterFs))/config.filterFm)+config.filterFt)
772 			*filterRefFreq) / config.frequency;
773 		if ( filterTable[uk] < yMin )
774 			filterTable[uk] = yMin;
775 		if ( filterTable[uk] > yMax )
776 			filterTable[uk] = yMax;
777 		uk++;
778 	}
779 
780 	extern filterfloat bandPassParam[0x800];
781 	yMax = 0.22;
782 	yMin = 0.05;  // less for some R1/R4 chips
783 	float yAdd = (yMax-yMin)/2048.0;
784 	float yTmp = yMin;
785 	uk = 0;
786 	// Some C++ compilers still have non-local scope!
787 	for ( float rk2 = 0; rk2 < 0x800; rk2++ )
788 	{
789 		bandPassParam[uk] = (yTmp*filterRefFreq) / config.frequency;
790 		yTmp += yAdd;
791 		uk++;
792 	}
793 
794 	extern filterfloat filterResTable[16];
795 	float resDyMax = 1.0;
796 	float resDyMin = 2.0;
797 	float resDy = resDyMin;
798 	for ( uk = 0; uk < 16; uk++ )
799 	{
800 		filterResTable[uk] = resDy;
801 		resDy -= (( resDyMin - resDyMax ) / 15 );
802 	}
803 	filterResTable[0] = resDyMin;
804 	filterResTable[15] = resDyMax;
805 }
806 
setVoiceVolume(int voice,ubyte leftLevel,ubyte rightLevel,uword total)807 bool emuEngine::setVoiceVolume(int voice, ubyte leftLevel, ubyte rightLevel, uword total)
808 {
809 	if ( config.volumeControl == SIDEMU_NONE )
810 		return false;
811 	if ((voice < 1) || (voice > 4) || (total > 256))
812 		return false;
813 	if ( config.channels == SIDEMU_MONO )
814 		rightLevel = 0;
815 	extern void sidEmuSetVoiceVolume(int v,uword L,uword R,uword T);
816 	sidEmuSetVoiceVolume(voice,leftLevel,rightLevel,total);
817 	return true;
818 }
819 
getVoiceVolume(int voice)820 uword emuEngine::getVoiceVolume(int voice)
821 {
822 	extern uword sidEmuReturnVoiceVolume(int);
823 	if (( voice < 1 ) || ( voice > 4 ))
824 		return 0;
825 	else
826 		return sidEmuReturnVoiceVolume(voice);
827 }
828