Commits

spencercw committed 5b1b33c

#27 Apply changes to the sound parameters immediately.

Comments (0)

Files changed (3)

gb_emulator/include/gb_emulator/gb_sound.hpp

 	//! Generates a CPU cycles's worth of sound.
 	int poll();
 
+	//! Handles a write to a sound register.
+	void writeIoPort(uint8_t ptr, uint8_t val);
+
 	//! Starts recording to the given file.
 	/**
 	 * This does nothing if already recording.

gb_emulator/src/gb_memory.cpp

 		ioPorts[ptr] = val & 0x07;
 		break;
 
-	// Sound mode 1
+	// Sound modes
+	case NR10:
+	case NR11:
+	case NR12:
+	case NR13:
 	case NR14:
-		{
-			ioPorts[ptr] = val;
-			uint16_t gbFrequency = ((val & 0x07) << 8) | ioPorts[NR13];
-			gb_.sound_.actFrequency1_ = 131072. / (0x800 - gbFrequency);
-		}
-		break;
-
-	// Sound mode 3
+	case NR21:
+	case NR22:
+	case NR23:
+	case NR24:
+	case NR30:
+	case NR31:
 	case NR32:
+	case NR33:
+	case NR34:
+	case NR41:
+	case NR42:
+	case NR43:
+	case NR44:
 		ioPorts[ptr] = val;
-		gb_.sound_.outLevel3_ = (ioPorts[ptr] & 0x60) >> 5;
+		gb_.sound_.writeIoPort(ptr, val);
 		break;
 
 	// Sound mode 5; sound on/off

gb_emulator/src/gb_sound.cpp

 static const unsigned SOUND_BUF_SIZE = 4096;
 static const double DUTY_RATIOS[] = { 0.25, 0.5, 1, 1.5 };
 
+// Constants for channel 4
+static const double BASE_FREQUENCY = 4194304 / 8;
+static const double CLOCK_DIVIDER_FREQ[] = {
+	BASE_FREQUENCY * 2,
+	BASE_FREQUENCY,
+	BASE_FREQUENCY / 2,
+	BASE_FREQUENCY / 3,
+	BASE_FREQUENCY / 4,
+	BASE_FREQUENCY / 5,
+	BASE_FREQUENCY / 6,
+	BASE_FREQUENCY / 7
+};
+// The last two values in this array shouldn't be used and are just there to prevent a crash. I'm
+// not sure what the Game Boy actually does if those values are used.
+static const double PRE_SCALER[] = { 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400,
+	0x800, 0x1000, 0x2000, 0x4000, 1, 1 };
+static const uint32_t *LFSR[] = { lfsr15, lfsr7 };
+static const unsigned LFSR_SIZE[] = { 32768, 128 };
+
 struct SdlAudioLock
 {
 	SdlAudioLock()
 	return cycles;
 }
 
+void GbSound::writeIoPort(uint8_t ptr, uint8_t val)
+{
+	switch (ptr)
+	{
+	// Sonud channel 1
+	case NR10:
+		sweepEnabled1_ = (val & 0x70) != 0;
+		if (sweepEnabled1_)
+		{
+			sweepStep1_ = sampleRate_ / (128. / (((val & 0x70) >> 4) + 1));
+			sweepShifts1_ = val & 0x07;
+			sweepType1_  = (val & 0x08) == 0x08;
+		}
+		break;
+
+	case NR11:
+		duty1_ = DUTY_RATIOS[(val & 0xc0) >> 6];
+		if (gb_.mem_.ioPorts[NR14] & 0x40)
+		{
+			duration1_ = (64 - (val & 0x3f)) * (1. / 256) * sampleRate_;
+		}
+		else
+		{
+			duration1_ = numeric_limits<double>::infinity();
+		}
+		break;
+
+	case NR12:
+		envelope1_ = val >> 4;
+		envelopeDirection1_ = (val & 0x08) == 0x08;
+		envelopeStep1_ = ((val & 0x07) * (1. / 64)) * sampleRate_;
+		break;
+
+	case NR13:
+		gbFrequency1_ = (val | ((gb_.mem_.ioPorts[NR14] & 0x07) << 8));
+		actFrequency1_ = 131072. / (0x800 - gbFrequency1_);
+		break;
+
+	case NR14:
+		gbFrequency1_ = (gb_.mem_.ioPorts[NR13] | ((val & 0x07) << 8));
+		actFrequency1_ = 131072. / (0x800 - gbFrequency1_);
+		if (val & 0x40)
+		{
+			duration1_ = (64 - (gb_.mem_.ioPorts[NR11] & 0x3f)) * (1. / 256) * sampleRate_;
+		}
+		else
+		{
+			duration1_ = numeric_limits<double>::infinity();
+		}
+		break;
+
+	// Sound channel 2
+	case NR21:
+		duty2_ = DUTY_RATIOS[(val & 0xc0) >> 6];
+		if (gb_.mem_.ioPorts[NR24] & 0x40)
+		{
+			duration2_ = (64 - (val & 0x3f)) * (1. / 256) * sampleRate_;
+		}
+		else
+		{
+			duration2_ = numeric_limits<double>::infinity();
+		}
+		break;
+
+	case NR22:
+		envelope2_ = val >> 4;
+		envelopeDirection2_ = (val & 0x08) == 0x08;
+		envelopeStep2_ = ((val & 0x07) * (1. / 64)) * sampleRate_;
+		break;
+
+	case NR23:
+		gbFrequency2_ = (val | ((gb_.mem_.ioPorts[NR24] & 0x07) << 8));
+		actFrequency2_ = 131072. / (0x800 - gbFrequency2_);
+		break;
+
+	case NR24:
+		gbFrequency2_ = (gb_.mem_.ioPorts[NR23] | ((val & 0x07) << 8));
+		actFrequency2_ = 131072. / (0x800 - gbFrequency2_);
+		if (val & 0x40)
+		{
+			duration2_ = (64 - (gb_.mem_.ioPorts[NR21] & 0x3f)) * (1. / 256) * sampleRate_;
+		}
+		else
+		{
+			duration2_ = numeric_limits<double>::infinity();
+		}
+		break;
+
+	// Sound channel 3
+	case NR31:
+		if (gb_.mem_.ioPorts[NR34] & 0x40)
+		{
+			duration3_ = (256 - val) * (1. / 256) * sampleRate_;
+		}
+		else
+		{
+			duration3_ = numeric_limits<double>::infinity();
+		}
+		break;
+
+	case NR32:
+		outLevel3_ = (gb_.mem_.ioPorts[NR32] & 0x60) >> 5;
+		break;
+
+	case NR33:
+		gbFrequency3_ = (val | ((gb_.mem_.ioPorts[NR34] & 0x07) << 8));
+		actFrequency3_ = 131072. / (0x800 - gbFrequency3_);
+		break;
+
+	case NR34:
+		gbFrequency3_ = (gb_.mem_.ioPorts[NR33] | ((val & 0x07) << 8));
+		actFrequency3_ = 131072. / (0x800 - gbFrequency3_);
+		if (val & 0x40)
+		{
+			duration3_ = (256 - gb_.mem_.ioPorts[NR31]) * (1. / 256) * sampleRate_;
+		}
+		else
+		{
+			duration3_ = numeric_limits<double>::infinity();
+		}
+		break;
+
+	// Sound channel 4
+	case NR41:
+		if (gb_.mem_.ioPorts[NR44] & 0x40)
+		{
+			duration4_ = (64 - (val & 0x3f)) * (1. / 256) * sampleRate_;
+		}
+		else
+		{
+			duration4_ = numeric_limits<double>::infinity();
+		}
+		break;
+
+	case NR42:
+		envelope4_ = val >> 4;
+		envelopeDirection4_ = (val & 0x08) == 0x08;
+		envelopeStep4_ = ((val & 0x07) * (1. / 64)) * sampleRate_;
+		break;
+
+	case NR43:
+		{
+			actFrequency4_  = CLOCK_DIVIDER_FREQ[val & 0x07];
+			actFrequency4_ /= PRE_SCALER[val >> 4];
+
+			bool lfsrSelection = (val & 0x08) == 0x08;
+			counterData4_ = LFSR[lfsrSelection];
+			counterDataSize4_ = LFSR_SIZE[lfsrSelection];
+			counterIndex4_ = 0;
+		}
+		break;
+
+	case NR44:
+		if (val & 0x40)
+		{
+			duration4_ = (64 - (gb_.mem_.ioPorts[NR41] & 0x3f)) * (1. / 256) * sampleRate_;
+		}
+		else
+		{
+			duration4_ = numeric_limits<double>::infinity();
+		}
+		break;
+	}
+}
+
 void GbSound::record(const fs::path &path)
 {
 	SdlAudioLock lock;
 			gb_.mem_.ioPorts[NR52] |=  0x08;
 
 			// Get the frequency
-			static const double baseFrequency = 4194304 / 8;
-			static const double clockDivFreqs[] = {
-				baseFrequency * 2,
-				baseFrequency,
-				baseFrequency / 2,
-				baseFrequency / 3,
-				baseFrequency / 4,
-				baseFrequency / 5,
-				baseFrequency / 6,
-				baseFrequency / 7
-			};
-			actFrequency4_ = clockDivFreqs[gb_.mem_.ioPorts[NR43] & 0x07];
-
-			// The last two values in this array shouldn't be used and are just there to prevent a
-			// crash. I'm not sure what the Game Boy actually does if those values are used.
-			static const double preScaler[] = { 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200,
-				0x400, 0x800, 0x1000, 0x2000, 0x4000, 1, 1 };
-			actFrequency4_ /= preScaler[gb_.mem_.ioPorts[NR43] >> 4];
+			actFrequency4_  = CLOCK_DIVIDER_FREQ[gb_.mem_.ioPorts[NR43] & 0x07];
+			actFrequency4_ /= PRE_SCALER[gb_.mem_.ioPorts[NR43] >> 4];
 
 			// Save the playback duration
 			if (gb_.mem_.ioPorts[NR44] & 0x40)
 			}
 
 			// Select the appropriate LFSR data array
-			static const uint32_t *lfsr[] = { lfsr15, lfsr7 };
-			static const unsigned lfsrSize[] = { 32768, 128 };
-
 			bool lfsrSelection = (gb_.mem_.ioPorts[NR43] & 0x08) == 0x08;
-			counterData4_ = lfsr[lfsrSelection];
-			counterDataSize4_ = lfsrSize[lfsrSelection];
+			counterData4_ = LFSR[lfsrSelection];
+			counterDataSize4_ = LFSR_SIZE[lfsrSelection];
 			counterIndex4_ = 0;
 
 			// Set the envelope parameters