📄 sound.cpp
字号:
//------------------------------------------------------------------------------
// 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 + -