[hatari-devel] Preserving YM2149 sound phase

[ Thread Index | Date Index | More lists.tuxfamily.org/hatari-devel Archives ]


Hi Nicolas,

The following was posted on Atari Forum, and was
partially investigated:
****
http://www.atari-forum.com/viewtopic.php?f=18&t=27521&sid=a0d1b43f0f66988900541b039ab5f6d4#p285292

"Postby troed » Sun Dec 20, 2015 10:20 pm
Re: some questions about YM2149F vs emulators vs MIST

There are several ways in which Hatari doesn't sound like an
original machine. The first version of 7an's song for our
STNICCC demo "Closure" had to be scrapped altogether.
He wrote the song(s) in Hatari which apparently always plays
all voices in phase, while on actual hardware they fall in
and out of phase (sometimes cancelling each other out completely).
This is somewhat better emulated by SC68."
****

The enclosed patch tested ok on the few demos that I tried.
Please see if it works for you.

This patch preserves the value in posA, posB and posC
rather than over-writing the value when stepA, stepB
or stepC is zero. Thus reducing interference with the phase.

stepA, stepB and/or stepC are no longer set to zero when a
frequency is higher than the sampling rate, this affects
phase also.

I'm hoping this patch solves the phase problem.

Sincerely,
David Savinkoff
diff -ur hatari.old/src/sound.c hatari.new/src/sound.c
--- hatari/src/sound.c	2016-10-16 13:44:01.000000000 -0700
+++ hatari/src/sound.c	2016-10-17 13:16:57.000000000 -0700
@@ -200,6 +200,7 @@
 
 static ymu32	stepA , stepB , stepC;
 static ymu32	posA , posB , posC;
+static ymu32	dcA , dcB , dcC;
 static ymu32	mixerTA , mixerTB , mixerTC;
 static ymu32	mixerNA , mixerNB , mixerNC;
 
@@ -715,6 +716,10 @@
 	posB = 0;
 	posC = 0;
 
+	dcA = 0;
+	dcB = 0;
+	dcC = 0;
+
 	currentNoise = 0xffff;
 
 	RndRack = 1;
@@ -753,10 +758,9 @@
  * Compute tone's step based on the input period.
  * Although for tone we should have the same result when per==0 and per==1,
  * this gives some very sharp and unpleasant sounds in the emulation.
- * To get a better sound, we consider all per<=5 to give step=0, which will
- * produce a constant output at value '1'. This should be handled with some
- * proper filters to remove high frequencies as on a real ST (where per<=9
- * gives nearly no audible sound).
+ * To get a better sound, we consider all per<=5 to produce a constant output
+ * at value '1'. This is handled with filters to remove high frequencies as
+ * on a real ST (where per<=9 gives nearly no audible sound).
  * A common replay freq of 44.1 kHz will also not be high enough to correctly
  * render possible tone's freq of 125 or 62.5 kHz (when per==1 or per==2)
  */
@@ -769,13 +773,8 @@
 	per = rHigh&15;
 	per = (per<<8)+rLow;
 
-#if 0							/* need some high freq filters for this to work correctly */
 	if ( per == 0 )
 		per = 1;				/* result for Per=0 is the same as for Per=1 */	
-#else
-	if  (per <= (int)(YM_ATARI_CLOCK/(YM_REPLAY_FREQ*7)) )
-		return 0;				/* discard frequencies higher than 80% of nyquist rate. */
-#endif
 
 	step = YM_ATARI_CLOCK;
 	step <<= 24;
@@ -897,13 +896,13 @@
 //fprintf ( stderr , "env %x %x %x\n" , Env3Voices , envStep , envPos );
 
 	/* Tone3Voices will contain the output state of each voice : 0 or 0x1f */
-	bt = -( (posA>>24) & 1);			/* 0 if bit24=0 or 0xffffffff if bit24=1 */
+	bt = -( ((posA>>24) & 1) | dcA);		/* 0 if bit24=0 or 0xffffffff if bit24=1 */
 	bt = (bt | mixerTA) & (bn | mixerNA);		/* 0 or 0xffff */
 	Tone3Voices = bt & YM_MASK_1VOICE;		/* 0 or 0x1f */
-	bt = -( (posB>>24) & 1);
+	bt = -( ((posB>>24) & 1) | dcB);
 	bt = (bt | mixerTB) & (bn | mixerNB);
 	Tone3Voices |= ( bt & YM_MASK_1VOICE ) << 5;
-	bt = -( (posC>>24) & 1);
+	bt = -( ((posC>>24) & 1) | dcC);
 	bt = (bt | mixerTC) & (bn | mixerNC);
 	Tone3Voices |= ( bt & YM_MASK_1VOICE ) << 10;
 
@@ -913,13 +912,13 @@
 
 	/* D/A conversion of the 3 volumes into a sample using a precomputed conversion table */
 
-	if (stepA == 0  &&  (Tone3Voices & YM_MASK_A) > 1)
+	if (dcA != 0  &&  (Tone3Voices & YM_MASK_A) > 1)
 		Tone3Voices -= 1;     /* Voice A AC component removed; Transient DC component remains */
 
-	if (stepB == 0  &&  (Tone3Voices & YM_MASK_B) > 1<<5)
+	if (dcB != 0  &&  (Tone3Voices & YM_MASK_B) > 1<<5)
 		Tone3Voices -= 1<<5;  /* Voice B AC component removed; Transient DC component remains */
 
-	if (stepC == 0  &&  (Tone3Voices & YM_MASK_C) > 1<<10)
+	if (dcC != 0  &&  (Tone3Voices & YM_MASK_C) > 1<<10)
 		Tone3Voices -= 1<<10; /* Voice C AC component removed; Transient DC component remains */
 
 	sample = ymout5[ Tone3Voices ];			/* 16 bits signed value */
@@ -946,10 +945,14 @@
 
 /*-----------------------------------------------------------------------*/
 /**
- * Update internal variables (steps, volume masks, ...) each
- * time an YM register is changed.
+ * Update internal variables (steps, volume masks, ...) each time a YM register is changed.
+ * Note:
+ * (stepA > (1u<<24)/8*7) ? (dcA = 1):(dcA = 0);	// Assume output always 1 if dcA = 1 (for Digi-sample)
+ *
+ * _ (1u<<24) allows for 8 bits integer, 24 bit fraction
+ * _ /8*7 refers to 7/8 nyquist frequency cutoff with only dc offset remaining (dcA = 1).
+ *
  */
-#define BIT_SHIFT 24
 void	Sound_WriteReg( int reg , Uint8 data )
 {
 	switch (reg)
@@ -957,37 +960,37 @@
 		case 0:
 			SoundRegs[0] = data;
 			stepA = Ym2149_ToneStepCompute ( SoundRegs[1] , SoundRegs[0] );
-			if (!stepA) posA = 1u<<BIT_SHIFT;		// Assume output always 1 if 0 period (for Digi-sample)
+			(stepA > (1u<<24)/8*7) ? (dcA = 1):(dcA = 0);	// Assume output always 1 if dcA = 1 (for Digi-sample)
 			break;
 
 		case 1:
 			SoundRegs[1] = data & 0x0f;
 			stepA = Ym2149_ToneStepCompute ( SoundRegs[1] , SoundRegs[0] );
-			if (!stepA) posA = 1u<<BIT_SHIFT;		// Assume output always 1 if 0 period (for Digi-sample)
+			(stepA > (1u<<24)/8*7) ? (dcA = 1):(dcA = 0);	// Assume output always 1 if dcA = 1 (for Digi-sample)
 			break;
 
 		case 2:
 			SoundRegs[2] = data;
 			stepB = Ym2149_ToneStepCompute ( SoundRegs[3] , SoundRegs[2] );
-			if (!stepB) posB = 1u<<BIT_SHIFT;		// Assume output always 1 if 0 period (for Digi-sample)
+			(stepB > (1u<<24)/8*7) ? (dcB = 1):(dcB = 0);	// Assume output always 1 if dcB = 1 (for Digi-sample)
 			break;
 
 		case 3:
 			SoundRegs[3] = data & 0x0f;
 			stepB = Ym2149_ToneStepCompute ( SoundRegs[3] , SoundRegs[2] );
-			if (!stepB) posB = 1u<<BIT_SHIFT;		// Assume output always 1 if 0 period (for Digi-sample)
+			(stepB > (1u<<24)/8*7) ? (dcB = 1):(dcB = 0);	// Assume output always 1 if dcB = 1 (for Digi-sample)
 			break;
 
 		case 4:
 			SoundRegs[4] = data;
 			stepC = Ym2149_ToneStepCompute ( SoundRegs[5] , SoundRegs[4] );
-			if (!stepC) posC = 1u<<BIT_SHIFT;		// Assume output always 1 if 0 period (for Digi-sample)
+			(stepC > (1u<<24)/8*7) ? (dcC = 1):(dcC = 0);	// Assume output always 1 if dcC = 1 (for Digi-sample)
 			break;
 
 		case 5:
 			SoundRegs[5] = data & 0x0f;
 			stepC = Ym2149_ToneStepCompute ( SoundRegs[5] , SoundRegs[4] );
-			if (!stepC) posC = 1u<<BIT_SHIFT;		// Assume output always 1 if 0 period (for Digi-sample)
+			(stepC > (1u<<24)/8*7) ? (dcC = 1):(dcC = 0);	// Assume output always 1 if dcC = 1 (for Digi-sample)
 			break;
 
 		case 6:
@@ -1160,6 +1163,9 @@
 	MemorySnapShot_Store(&posA, sizeof(posA));
 	MemorySnapShot_Store(&posB, sizeof(posB));
 	MemorySnapShot_Store(&posC, sizeof(posC));
+	MemorySnapShot_Store(&dcA, sizeof(dcA));
+	MemorySnapShot_Store(&dcB, sizeof(dcB));
+	MemorySnapShot_Store(&dcC, sizeof(dcC));
 
 	MemorySnapShot_Store(&mixerTA, sizeof(mixerTA));
 	MemorySnapShot_Store(&mixerTB, sizeof(mixerTB));


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/