Commits

spencercw committed 0d48a35

Improvements to the sound emulation.

- Refactored channel 3.
- Doubled the frequency of channels 1 and 2 to match the real device.

Comments (0)

Files changed (2)

gb_emulator/src/gb_memory.cpp

 		ioPorts[ptr] = val & 0xc7;
 		break;
 
+	// Sound mode 5; sound on/off
+	case NR52:
+		ioPorts[ptr] |= val & 0x80;
+		break;
+
 	// LCD control
 	case LCDC:
 		if (!(val & 0x80) && (ioPorts[ptr] & 0x80) && ioPorts[LCDC_Y] < 144)

gb_emulator/src/gb_sound.cpp

 
 		// Toggle hi/lo wave and reset the countdown if there is any sound to play
 		hi1_ = !hi1_;
-		countdown1_ += 1 / actFrequency1_ * (hi1_ ? duty1_ : 2 - duty1_) * sampleRate_;
+		countdown1_ += 1 / actFrequency1_ / 2 * (hi1_ ? duty1_ : 2 - duty1_) * sampleRate_;
 
 		// Adjust the envelope amplitude
 		if (envelope1_ && envelopeStep1_)
 
 		// Toggle hi/lo wave and reset the countdown if there is any sound to play
 		hi2_ = !hi2_;
-		countdown2_ += 1 / actFrequency2_ * (hi2_ ? duty2_ : 2 - duty2_) * sampleRate_;
+		countdown2_ += 1 / actFrequency2_ / 2 * (hi2_ ? duty2_ : 2 - duty2_) * sampleRate_;
 
 		// Adjust the envelope amplitude
 		if (envelope2_ && envelopeStep2_)
 
 double GbSound::sound3()
 {
+	// Return silence if nothing is playing and nothing is set to play
+	if (((countdown3_ <= 0 && !(gb_.mem_.ioPorts[NR52] & 0x04)) ||
+		!(gb_.mem_.ioPorts[NR30] & 0x80)) && !(gb_.mem_.ioPorts[NR34] & 0x80))
+	{
+		gb_.mem_.ioPorts[NR52] &= ~0x04;
+		return 0;
+	}
+
 	if (countdown3_ <= 0)
 	{
 		// Update the sound parameters if necessary
-		if (!waveIndex3_ && gb_.mem_.ioPorts[NR34] & 0x80)
+		if (gb_.mem_.ioPorts[NR34] & 0x80)
 		{
 			gb_.mem_.ioPorts[NR34] &= ~0x80;
+			gb_.mem_.ioPorts[NR30] |=  0x80;
+			gb_.mem_.ioPorts[NR52] |=  0x04;
 
 			// Calculate the tone frequency
 			gbFrequency3_ =
 			// Save the playback duration
 			if (gb_.mem_.ioPorts[NR34] & 0x40)
 			{
-				duration3_ = (256 - gb_.mem_.ioPorts[NR31]) * (1. / 256);
+				duration3_ = (256 - gb_.mem_.ioPorts[NR31]) * (1. / 256) * sampleRate_;
 			}
 			else
 			{
 			}
 
 			// Save the output level
-			uint8_t outLevel = (gb_.mem_.ioPorts[NR32] & 0x60) >> 5;
-			if (!outLevel)
-			{
-				// TODO: Fix
-				//outLevel3_ = 8;
-			}
-			else
-			{
-				outLevel3_ = outLevel - 1;
-			}
-
+			outLevel3_ = (gb_.mem_.ioPorts[NR32] & 0x60) >> 5;
 			waveIndex3_ = 0;
 		}
-		
-		// No sound initialising; check whether anything is playing at all
-		else if (!actFrequency3_ || !(gb_.mem_.ioPorts[NR30] & 0x80) || duration3_ < 0)
-		{
-			gb_.mem_.ioPorts[NR52] &= ~0x04;
-			return 0;
-		}
-
-		// Sound should be playing; increment the index after each sample has had an up/down state
 		else
 		{
 			waveIndex3_ = (waveIndex3_ + 1) % 32;
 		}
 
-		// Set the index into the wave data and reset the countdown if there is any sound to play
-		countdown3_ += 1 / actFrequency3_ / 16 * sampleRate_;
-		duration3_  -= 1 / actFrequency3_ / 16 * sampleRate_;
+		double duration = 1 / actFrequency3_ / 16 * sampleRate_;
+		countdown3_ += duration;
+		duration3_  -= duration;
+
+		// Reset the playback bit if the duration has elapsed
+		if (duration3_ <= 0)
+		{
+			gb_.mem_.ioPorts[NR52] &= ~0x04;
+		}
+
+		// Depending on the parameters the duration might be shorter than a single sample, so just
+		// skip through the wave RAM to catch up.
+		while (countdown3_ < 0)
+		{
+			waveIndex3_ = (waveIndex3_ + 1) % 32;
+			countdown3_ += duration;
+			duration3_  -= duration;
+		}
 	}
 
 	double amplitude;
-	if (!(gb_.mem_.ioPorts[NR51] & 0x44))
+	if (!outLevel3_)
 	{
-		gb_.mem_.ioPorts[NR52] &= ~0x04;
 		amplitude = 0;
 	}
 	else
 	{
-		gb_.mem_.ioPorts[NR52] |= 0x04;
-
 		uint8_t sample = wavePattern3_[waveIndex3_ / 2];
 		// Samples are packed two to the byte
 		if (waveIndex3_ % 2)
 			sample >>= 4;
 		}
 		// Shift right to adjust the output level
-		sample >>= outLevel3_;
-
+		sample >>= outLevel3_ - 1;
 		amplitude = 0x200 * ((sample - 7.5) / 7.5);
 	}
 
 		else
 		{
 			// Increment the LFSR counter
-			++counterIndex4_;
-			counterIndex4_ %= counterDataSize4_;
+			counterIndex4_ = (counterIndex4_ + 1) % counterDataSize4_;
 
 			// Adjust the envelope amplitude
 			if (envelope4_ > 0 && !envelopeDirection4_ && envelopeStep4_)
 		countdown4_ += duration;
 		duration4_  -= duration;
 
-		// Reset the channel 4 playback bit if the duration has elapsed
+		// Reset the playback bit if the duration has elapsed
 		if (duration4_ <= 0)
 		{
 			gb_.mem_.ioPorts[NR52] &= ~0x08;
 		// skip through the LFSR to catch up.
 		while (countdown4_ < 0)
 		{
-			++counterIndex4_;
-			counterIndex4_ %= counterDataSize4_;
+			counterIndex4_ = (counterIndex4_ + 1) % counterDataSize4_;
 			countdown4_ += duration;
+			duration4_  -= duration;
 		}
 	}