Commits

spencercw committed 2c35bc0

#31 Fix the implementation of frequency sweeping.

Comments (0)

Files changed (2)

gb_emulator/include/gb_emulator/gb_sound.hpp

 	double countdown1_, countdown2_, countdown3_, countdown4_;
 	// Whether the sweep shift is enabled
 	bool sweepEnabled1_;
+	// The next frequency that will be played in the sweep
+	uint16_t sweepNextFrequency1_;
 	// Amount the frequency is shifted by at each sweep shift
 	uint8_t sweepShifts1_;
 	// Type of shift. 0 = addition, 1 = subtraction

gb_emulator/src/gb_sound.cpp

 	countdown3_(0),
 	countdown4_(0),
 	sweepEnabled1_(false),
+	sweepNextFrequency1_(0),
 	sweepShifts1_(0),
 	sweepType1_(0),
 	sweepStep1_(0),
 		return 0;
 	}
 
-	if (countdown1_ <= 0)
+	if (countdown1_ <= 0 || gb_.mem_.ioPorts[NR14] & 0x80)
 	{
 		// Update the sound parameters if necessary
 		if (gb_.mem_.ioPorts[NR14] & 0x80)
 		{
 			gb_.mem_.ioPorts[NR14] &= ~0x80;
 			gb_.mem_.ioPorts[NR52] |=  0x01;
+			countdown1_ = 0;
 
 			// Calculate the tone frequency
 			gbFrequency1_ =
 				(gb_.mem_.ioPorts[NR13] | ((gb_.mem_.ioPorts[NR14] & 0x07) << 8));
+			actFrequency1_ = 131072. / (0x800 - gbFrequency1_);
 
 			// Set the wave pattern duty
 			duty1_ = DUTY_RATIOS[(gb_.mem_.ioPorts[NR11] & 0xc0) >> 6];
 				sweepStep1_ = sampleRate_ / (128. / (((gb_.mem_.ioPorts[NR10] & 0x70) >> 4) + 1));
 				sweepShifts1_ = gb_.mem_.ioPorts[NR10] & 0x07;
 				sweepType1_  = (gb_.mem_.ioPorts[NR10] & 0x08) == 0x08;
+				sweepCountdown1_ = sweepStep1_;
 
-				// The sweep unit is always one step ahead of what is actually played, so the first
+				// The sweep unit is always one step ahead of what is actually played, so the last
 				// note is never heard (unless the number of shifts is zero)
+				sweepNextFrequency1_ = 0;
 				if (sweepShifts1_ && !doSweep())
 				{
 					return 0;
 					sweepCountdown1_ = sweepStep1_;
 				}
 			}
-			actFrequency1_ = 131072. / (0x800 - gbFrequency1_);
 
 			// Save the playback duration
 			if (gb_.mem_.ioPorts[NR14] & 0x40)
 		}
 		else
 		{
+			// Toggle hi/lo wave
+			hi1_ = !hi1_;
+
 			// Adjust the envelope amplitude
 			adjustEnvelope(envelope1_, envelopeDirection1_, envelopeStep1_, envelopeCountdown1_);
 		}
 
-		// Toggle hi/lo wave
-		hi1_ = !hi1_;
-
 		double duration = 1 / actFrequency1_ / 2 * (hi1_ ? duty1_ : 2 - duty1_) * sampleRate_;
 		countdown1_ += duration;
 		duration1_  -= duration;
 		return 0;
 	}
 
-	if (countdown2_ <= 0)
+	if (countdown2_ <= 0 || gb_.mem_.ioPorts[NR24] & 0x80)
 	{
 		// Update the sound parameters if necessary
 		if (gb_.mem_.ioPorts[NR24] & 0x80)
 		{
 			gb_.mem_.ioPorts[NR24] &= ~0x80;
 			gb_.mem_.ioPorts[NR52] |=  0x02;
+			countdown2_ = 0;
 
 			// Calculate the tone frequency
 			gbFrequency2_ =
 		}
 		else
 		{
+			// Toggle hi/lo wave
+			hi2_ = !hi2_;
+
 			// Adjust the envelope amplitude
 			adjustEnvelope(envelope2_, envelopeDirection2_, envelopeStep2_, envelopeCountdown2_);
 		}
 
-		// Toggle hi/lo wave
-		hi2_ = !hi2_;
-
 		double duration = 1 / actFrequency2_ / 2 * (hi2_ ? duty2_ : 2 - duty2_) * sampleRate_;
 		countdown2_ += duration;
 		duration2_  -= duration;
 		return 0;
 	}
 
-	if (countdown3_ <= 0)
+	if (countdown3_ <= 0 || gb_.mem_.ioPorts[NR34] & 0x80)
 	{
 		// Update the sound parameters if necessary
 		if (gb_.mem_.ioPorts[NR34] & 0x80)
 			gb_.mem_.ioPorts[NR34] &= ~0x80;
 			gb_.mem_.ioPorts[NR30] |=  0x80;
 			gb_.mem_.ioPorts[NR52] |=  0x04;
+			countdown3_ = 0;
 
 			// Calculate the tone frequency
 			gbFrequency3_ =
 		return 0;
 	}
 
-	if (countdown4_ <= 0)
+	if (countdown4_ <= 0 || gb_.mem_.ioPorts[NR44] & 0x80)
 	{
 		// Update the sound parameters if necessary
 		if (gb_.mem_.ioPorts[NR44] & 0x80)
 		{
 			gb_.mem_.ioPorts[NR44] &= ~0x80;
 			gb_.mem_.ioPorts[NR52] |=  0x08;
+			countdown4_ = 0;
 
 			// Get the frequency
 			actFrequency4_  = CLOCK_DIVIDER_FREQ[gb_.mem_.ioPorts[NR43] & 0x07];
 
 bool GbSound::doSweep()
 {
-	int16_t difference = gbFrequency1_ >> sweepShifts1_;
-	if (sweepType1_)
+	// Change the frequency if this isn't the first time we've been called for this sweep
+	if (sweepNextFrequency1_)
 	{
-		difference = -difference;
-	}
-	uint16_t newGbFrequency = gbFrequency1_ + difference;
-	// Check for overflow
-	if (newGbFrequency & 0xf800)
-	{
-		gb_.mem_.ioPorts[NR52] &= ~0x01;
-		return false;
-	}
-	else
-	{
-		gbFrequency1_ = newGbFrequency;
+		gbFrequency1_ = sweepNextFrequency1_;
 		actFrequency1_ = 131072. / (0x800 - gbFrequency1_);
 		sweepCountdown1_ += sweepStep1_;
 			
 		gb_.mem_.ioPorts[NR13]  = static_cast<uint8_t>(gbFrequency1_);
 		gb_.mem_.ioPorts[NR14] &= ~0x07;
 		gb_.mem_.ioPorts[NR14] |= static_cast<uint8_t>(gbFrequency1_ >> 8);
+	}
 
-		return true;
+	// Calculate the next frequency
+	int16_t difference = gbFrequency1_ >> sweepShifts1_;
+	if (sweepType1_)
+	{
+		difference = -difference;
 	}
+	sweepNextFrequency1_ = gbFrequency1_ + difference;
+
+	// Check for overflow. If it does we stop now, so the last note is not played, even though it is
+	// in range
+	if (sweepNextFrequency1_ & 0xf800)
+	{
+		gb_.mem_.ioPorts[NR52] &= ~0x01;
+		return false;
+	}
+
+	return true;
 }
 
 void GbSound::adjustEnvelope(uint8_t &envelope, bool envelopeDirection, double envelopeStep,
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.