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

📄 sound.cpp

📁 nes游戏模拟器
💻 CPP
📖 第 1 页 / 共 2 页
字号:
//------------------------------------------------------------------------------
// Name: Sound.cpp
// Desc: Holds all the functions that have to do with sound.
//------------------------------------------------------------------------------


#include "Sound.h"


// Length table lookup table...sounds wierd.
static const BYTE abyLengthTable[32] =
{
	0x05, 0x7F, 0x0A, 0x01, 0x13, 0x02, 0x28, 0x03,
	0x50, 0x04, 0x1E, 0x05, 0x07, 0x06, 0x0D, 0x07,
	0x06, 0x08, 0x0C, 0x09, 0x18, 0x0A, 0x30, 0x0B,
	0x60, 0x0C, 0x24, 0x0D, 0x08, 0x0E, 0x10, 0x0F
};

static const BYTE abyDutyCycleTablePos[4] = { 2,  4,  8, 12};
static const BYTE abyDutyCycleTableNeg[4] = {14, 12,  8,  4};


//------------------------------------------------------------------------------
// Global variables for this file.
//------------------------------------------------------------------------------
//CSound*        pcsndSquare1 = NULL; // Buffer for square channel 1.
//CSoundManager* pcsndmanMain = NULL; // Main sound manager for our program.
//LPSTR          strSquareWaveFile = "square1000hz44100.wav";


NESAPU         APU;                 // Apu strucure.


BOOL bCalculateSound = FALSE; // Tells the sound functions to use new or old data.

DWORD dwNumCyclesPerSample = CPU_FREQUENCY / OPTIONS_NUM_SAMPLESPERSEC;

// DirectSound stuff.
LPDIRECTSOUND       lpDS           = NULL;
LPDIRECTSOUNDBUFFER lpdsbSoundBuf = NULL;
DWORD               dwMidSoundBuf = 0;


//------------------------------------------------------------------------------
// Name: CreateSound()
// Desc: Creates DirectSound and sets the format of the primary buffer. Next
//       it creates all the secondary buffers we need for streaming.
//------------------------------------------------------------------------------
BOOL CreateSound(HWND hwnd)
{
	HRESULT hr;
	LPDIRECTSOUNDBUFFER lpdsbPrimary;
	DSBUFFERDESC dsbdesc;
	WAVEFORMATEX wfm;

	// Create DirectSound.
	if (FAILED(DirectSoundCreate(NULL, &lpDS, NULL)))
		return FALSE;

	// Set the cooperative level.
	if (FAILED(lpDS->SetCooperativeLevel(hwnd, DSSCL_PRIORITY)))
		return FALSE;

	// Set up the DSBUFFERDESC structure.
	memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
	dsbdesc.dwSize = sizeof(DSBUFFERDESC);
	dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
	dsbdesc.dwBufferBytes = 0;
	dsbdesc.lpwfxFormat = NULL;

	// Set up strucure for the desired sound format.
	memset(&wfm, 0, sizeof(WAVEFORMATEX));
	wfm.wFormatTag = WAVE_FORMAT_PCM;
	wfm.nChannels = OPTIONS_NUM_CHANNELS;
	wfm.nSamplesPerSec = OPTIONS_NUM_SAMPLESPERSEC;
	wfm.wBitsPerSample = OPTIONS_NUM_BITSPERSAMPLE;
	wfm.nBlockAlign = (wfm.wBitsPerSample / 8) * wfm.nChannels;
	wfm.nAvgBytesPerSec = wfm.nSamplesPerSec * wfm.nBlockAlign;

	// Gain access to the primary buffer.
	hr = lpDS->CreateSoundBuffer(&dsbdesc, &lpdsbPrimary, NULL);

	// Set the primary buffer to the desired format. If this fails,
	// we will just ignore it and go with the default format.
	if (SUCCEEDED(hr))
		hr = lpdsbPrimary->SetFormat(&wfm);

	// Create the secondary streaming buffers.
	CreateStreamingSoundBuffer(lpdsbSoundBuf, &dwMidSoundBuf);

	return TRUE;
} // end CreateDirectSound()


//------------------------------------------------------------------------------
// Name: DestroySound()
// Desc: Shuts down DirectSound, this releases the secondary buffers as well.
//------------------------------------------------------------------------------
VOID DestroySound()
{
	// Stop playing the buffer first.
	lpdsbSoundBuf->Stop();

	// Now release DirectSound.
	if (lpDS != NULL)
	{
		lpDS->Release();
		lpDS = NULL;
	}
} // end DestroyDirectSound();


//------------------------------------------------------------------------------
// Name: FillBufferWithSilence()
// Desc: Does exactly what is says. For 8-bit waves 0x80 is silent, for 
//       16-bit wave files 0 is silence.
//------------------------------------------------------------------------------
BOOL FillBufferWithSilence(LPDIRECTSOUNDBUFFER& lpdsbSound)
{
	WAVEFORMATEX    wfx;
	DWORD           dwSizeWritten;

	PBYTE   pb1;
	DWORD   cb1;

	if (FAILED(lpdsbSound->GetFormat(&wfx, sizeof(WAVEFORMATEX), &dwSizeWritten)))
		return FALSE;

	if (SUCCEEDED(lpdsbSound->Lock(0, 0, (LPVOID*)&pb1, &cb1, NULL, NULL, DSBLOCK_ENTIREBUFFER)))
	{
		FillMemory(pb1, cb1, (wfx.wBitsPerSample == 8) ? 128 : 0);

		lpdsbSound->Unlock(pb1, cb1, NULL, 0);
		return TRUE;
	}

	return FALSE;
} // end FillBufferWithSilence()


//------------------------------------------------------------------------------
// Name: CreateStreamingSoundBuffer()
// Desc: Creates a secondary buffer for streaming and fills it with silence. 
//       Also returns the halfway point for later use.
//------------------------------------------------------------------------------
BOOL CreateStreamingSoundBuffer(LPDIRECTSOUNDBUFFER& lpdsbSound, DWORD* dwMidBuffer)
{
	HRESULT hr;
	DSBUFFERDESC dsbdesc;
	WAVEFORMATEX wfm;

	// Set up strucure for the desired sound format.
	memset(&wfm, 0, sizeof(WAVEFORMATEX));
	wfm.wFormatTag = WAVE_FORMAT_PCM;
	wfm.nChannels = OPTIONS_NUM_CHANNELS;
	wfm.nSamplesPerSec = OPTIONS_NUM_SAMPLESPERSEC;
	wfm.wBitsPerSample = OPTIONS_NUM_BITSPERSAMPLE;
	wfm.nBlockAlign = (wfm.wBitsPerSample / 8) * wfm.nChannels;
	wfm.nAvgBytesPerSec = wfm.nSamplesPerSec * wfm.nBlockAlign;

	// Set up the DSBUFFERDESC structure.
	memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
	dsbdesc.dwSize = sizeof(DSBUFFERDESC);
	dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
	dsbdesc.dwBufferBytes = wfm.nAvgBytesPerSec * OPTIONS_NUM_SECONDSFORBUFFER;
	dsbdesc.lpwfxFormat = &wfm;

	// Create the secondary buffer.
	hr = lpDS->CreateSoundBuffer(&dsbdesc, &lpdsbSound, NULL);
	if (FAILED(hr))
		return FALSE;

	FillBufferWithSilence(lpdsbSound);

	// Start playing the buffer.
	lpdsbSound->Play(0, 0, DSBPLAY_LOOPING);

	// Save the middle of the buffer so we can write to either
	// half depending on which half of the buffer is playing.
	*dwMidBuffer = dsbdesc.dwBufferBytes / 2;

	// Success!!!!!
	return TRUE;
} // end CreateStreamingSoundBuffer()


//------------------------------------------------------------------------------
// Name: APU_DoFrame()
// Desc: Takes care of the sound for a frame.
//------------------------------------------------------------------------------
BOOL APU_DoFrame()
{
	// Stuff necessary for working with streaming buffers.
	HRESULT hr;
	DWORD   dwWritePos;
	DWORD   dwBytesLocked1;
	DWORD   dwBytesLocked2;
	DWORD   dwByteNum;
	VOID*   pvData1;
	VOID*   pvData2;
	VOID*   pvDataSave;

	static DWORD dwLastEndWritePos = 0;

	// Stuff for nintendo sound files.
	WORD wTotalOutputVol; // Sum of all the sound channel's volumes.


	// If the buffer is invalid then return.
	if (lpdsbSoundBuf == NULL)
		return FALSE;

	// Get the current write position within the buffer.
	if (FAILED(lpdsbSoundBuf->GetCurrentPosition(NULL, &dwWritePos)))
		return FALSE;

	// If the play cursor has just reached the first or second half
	// of the buffer, it's time to stream data to the other half.
	LONG lTemp = dwLastEndWritePos - 1000;
	if (lTemp < 0)
		lTemp = 0;

	//if (dwWritePos >= (dwLastEndWritePos))// || ())
	if (dwWritePos >= (DWORD)lTemp)// || ())
	{
		// Lock the buffer so we can write to it.
		hr = lpdsbSoundBuf->Lock(dwLastEndWritePos, OPTIONS_NUM_BYTESTOLOCK, 
			&pvData1, &dwBytesLocked1, &pvData2, &dwBytesLocked2, 0);

		if (SUCCEEDED(hr))
		{
			// For the first part of the loop calculate the sound values.
			bCalculateSound = TRUE;

			// Fill the whole locked portion of the sound buffer with
			// out sound data from each of the Nintendo's sound channels.
			// This involves two for loops since the buffer may wrap around.
			
			// Save a temp pointer to the first portions of the sound buffer.
			pvDataSave = pvData1;

			// First portion of the buffer.
			for (dwByteNum = 0; dwByteNum < dwBytesLocked1; 
				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);
				}

⌨️ 快捷键说明

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