⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sound.cpp

📁 nes游戏模拟器
💻 CPP
📖 第 1 页 / 共 2 页
字号:
				// Stop calculating the sound.
				if (bCalculateSound == TRUE)
					bCalculateSound = FALSE;
			}

			// If the locked portion of the buffer wrapped around to the 
			// beginning of the buffer then we need to write to it.
			if (dwBytesLocked2 > 0)
			{
				// Save a temp pointer to the second portions of the sound buffer.
				pvDataSave = pvData2;

				// Second portion of the buffer.
				for (dwByteNum = 0; dwByteNum < dwBytesLocked2; 
					dwByteNum += OPTIONS_NUM_CHANNELS * (OPTIONS_NUM_BITSPERSAMPLE / 8))
				{
					// Clear the last volume outta there.
					wTotalOutputVol = 0;

					// Process the square channel 1.
					wTotalOutputVol += (WORD)APU_DoSquare1();

					// Write the data to the sound buffer and move the pointer 
					// to the buffer to the next data position.
					if (OPTIONS_NUM_BITSPERSAMPLE == 8)
					{
						*((BYTE*)pvDataSave) = (BYTE)wTotalOutputVol;
						pvDataSave = (BYTE*)pvDataSave + (OPTIONS_NUM_CHANNELS * 1);
					}
					else
					{
						*((WORD*)pvDataSave) = wTotalOutputVol;
						pvDataSave = (BYTE*)pvDataSave + (OPTIONS_NUM_CHANNELS * 2);
					}
				}
			}

			// Unlock the buffer now that were done with it.
			lpdsbSoundBuf->Unlock(pvData1, dwBytesLocked1, pvData2, dwBytesLocked2);
		}

		// Save the position of the last place we wrote to so 
		// we can continue the next time this function is called.
		dwLastEndWritePos = (DWORD)(dwLastEndWritePos+dwBytesLocked1+dwBytesLocked2);

		if (dwLastEndWritePos >= ((OPTIONS_NUM_BITSPERSAMPLE/8)*OPTIONS_NUM_CHANNELS*OPTIONS_NUM_SAMPLESPERSEC)*OPTIONS_NUM_SECONDSFORBUFFER)
		{
			dwLastEndWritePos -= ((OPTIONS_NUM_BITSPERSAMPLE/8)*OPTIONS_NUM_CHANNELS*OPTIONS_NUM_SAMPLESPERSEC)*OPTIONS_NUM_SECONDSFORBUFFER;
		}
	}


	// Victory is ours!!!!
	return TRUE;
} // end APU_DoFrame()


//------------------------------------------------------------------------------
// Name: APU_DoSquare1()
// Desc: Process the first square wave sound channel.
//------------------------------------------------------------------------------
WORD APU_DoSquare1()
{


	static DWORD dwNumCyclesElapsed = 0; // Keeps track of where we are in the wave.
	static DWORD dwDutyFlip = 0;         // How many times the programmable timer must 
	                                     // reload untill a duty flip.
	static DWORD dwWaveLength = 0;       // How many cycles till the programmable timer reloads.

	static BYTE  byOutput = 0x80; // The returned output volume.
	static BOOL  bDutyFlip = FALSE; // Has a duty flip happended?

	static BOOL bCalcSquare = FALSE;
	static BOOL bCalcOnNextFlag = FALSE;

	if (bCalculateSound)
		bCalcOnNextFlag = TRUE;
	
	// If the calculate flag is set, then we need to calculate everything
	// that is needed to return an output volume to the calling function.
	// Otherwise we just return the precalculated data that was stored
	// in the static variables.
	if (bCalcSquare)
	{
CalculateIt:
		// First thing we need to do is start over by reseting the elapsed cycles.
		dwNumCyclesElapsed = 0;
		// Reset the duty toggle.
		bDutyFlip = FALSE;

		bCalcSquare = FALSE;
		bCalcOnNextFlag = FALSE;

		//---------------------------------------------------------------------
		// Do the length counter part.
		//---------------------------------------------------------------------
		
		// If the length counter is enabled then we need to process it.
		if (!(CPU.Memory[0x4000] & 0x20))
		{
			// If the length counter is not zero then decrement the value.
			if (APU.sndchanSquare1.byLengthCtr)
				APU.sndchanSquare1.byLengthCtr--;
		}


		//---------------------------------------------------------------------
		// TODO: This is where the sweeping unit needs to be emulated.
		//---------------------------------------------------------------------


		//---------------------------------------------------------------------
		// Emulate the programmable timer to get the wavelength.
		//---------------------------------------------------------------------

		// Take the 3 least significant bits from $4003 and
		// use those as the bits 8-10 for our wavelength. Bits
		// 0-7 come from $4002 to produce our 11-bit wavelength.
		// The we need to add one to it.
		dwWaveLength = ((((WORD)(CPU.Memory[0x4003]&0x7)) << 8) | 
			CPU.Memory[0x4002]) + 1;		

		//---------------------------------------------------------------------
		// Emulate the duty flip part.
		//---------------------------------------------------------------------
		dwDutyFlip = abyDutyCycleTablePos[CPU.Memory[0x4000]>>6];
		
		//---------------------------------------------------------------------
		// Now finally send the signal through the volume/envelope decay unit.
		//---------------------------------------------------------------------

		// If the envelope decay bit is set, then the volume goes staight
		// to the DAC...or in our case DirectSound. This means that
		// the envelope decay is disabled.
		if (!(CPU.Memory[0x4000] & 0x10))
		{
			// There are a few conditions when 0 is sent straight
			// to the DAC for the volume. They are as follows:
			// 1.  If the length counter is 0
			// 2.  Something to do with the sweep unit.
			// 3.  On the negative portion of the output frequency 
			//     signal coming from the duty cycle.
			//
			// Otherwise bits (0-3) of $4000 are sent straight the
			// DAC for the volume.
			if (APU.sndchanSquare1.byLengthCtr == 0) // TODO: implement other conditions.
				byOutput = 0x80;
			else
				byOutput = ((CPU.Memory[0x4000] & 0x0F) << 3);
		}
		else
			byOutput = ((CPU.Memory[0x4000] & 0x0F) << 3);
	}

	// If we are done with the wave we need to start over.
	if (dwNumCyclesElapsed >= dwWaveLength)
	{
		// We need to flip the volume to negative if the amount of
		// times for a duty flip has passed.
		if ((dwDutyFlip--) == 0)
		{
			// If the duty flip is positive then we load the counter with the negative
			// value. If the duty flip is negative, then we load the counter
			// with the positive counter.
			if (bDutyFlip)
			{
				dwDutyFlip = abyDutyCycleTablePos[CPU.Memory[0x4000]>>6];

				if (bCalcOnNextFlag)
				{
					bCalcSquare = FALSE;
					//return (WORD)byOutput;
					goto CalculateIt;
				}
			}
			else
			{
				dwDutyFlip = abyDutyCycleTableNeg[CPU.Memory[0x4000]>>6];
			}

			// Flip the output wave.
			byOutput = -byOutput;

			// Toggle the duty flip indicator.
			bDutyFlip ^= TRUE;
		}

		dwNumCyclesElapsed -= dwWaveLength;
	}
	else
		// Keep moving along the phase of the wave form.
		dwNumCyclesElapsed += dwNumCyclesPerSample;

	// Return the final value.
	return (WORD)byOutput;
} // end APU_DoSquare1()


//------------------------------------------------------------------------------
// Name: APU_Read()
// Desc: Handles all the reads from the APU.
//------------------------------------------------------------------------------
BYTE APU_Read(WORD wReg)
{
	BYTE byRetVal = 0;

	switch (wReg)
	{
		case 0x4015:
			// If the length counter is non-zero set bit 1.
			if (APU.sndchanSquare1.byLengthCtr) byRetVal |= 0x01;

			// Return the byte.
			return byRetVal;

		default:
			return 0;
	}

	return 0;
} // end APU_Read()


//------------------------------------------------------------------------------
// Name: APU_Write()
// Desc: Handles all the writes to the APU.
//------------------------------------------------------------------------------
VOID APU_Write(WORD wReg, BYTE byData)
{
	// The old length counters for the channel since writing 0 into
	// $4015 sets the length counter to 0, but writing 1 sets the
	// length counter back to its origional value.
	static BYTE byOldLengthCounter = 0;

	switch (wReg)
	{
		case 0x4003:
			// Use a lookup table to set the length counter.
			// Bits 3-7 are used for this but they must be
			// right shifted to use the lookup table.
			APU.sndchanSquare1.byLengthCtr = abyLengthTable[byData >> 3];

			return;

		case 0x4015:
			// Deal with writes for the first square channel.
			if (byData & 0x1)
				APU.sndchanSquare1.byLengthCtr = byOldLengthCounter;
			else
			{
				// If the bit is not set the length counter is forced to 0,
				// but make sure to save the value for when a 1 gets written.
				byOldLengthCounter = APU.sndchanSquare1.byLengthCtr;
				APU.sndchanSquare1.byLengthCtr = 0;
			}
			return;

		default:
			return;
	}

	return;
} // end APU_Write()

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -