DirectSound

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
DirectSound
by on (#128790)
Currently, I am trying to implement sound emulation for the first time ever. However, I have no idea about the points listed below.

Here is the code that I am using at the moment. It is enough to play the garbage that is already contained in the buffer with some form of initialisation. All of the code has had the error checking removed for readability.

Code:
unsigned __int8 * SoundBuffer;

void CDirectX::CreateSound()
{
   DirectSoundCreate8(NULL, &lpDS8, NULL);

   lpDS8->SetCooperativeLevel(hWnd, DSSCL_NORMAL);

   ZeroMemory(&dsbd, sizeof(DSBUFFERDESC));

   dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
   dsbd.dwSize = sizeof(DSBUFFERDESC);

   lpDS8->CreateSoundBuffer(&dsbd, &lpDSBPrimary, NULL);

   lpDSBPrimary->GetFormat(&wfe, sizeof(WAVEFORMATEX), NULL);

   ZeroMemory(&dsbd, sizeof(DSBUFFERDESC));

   dsbd.dwBufferBytes = wfe.nAvgBytesPerSec;
   dsbd.dwFlags = DSBCAPS_GLOBALFOCUS;
   dsbd.dwSize = sizeof(DSBUFFERDESC);
   dsbd.lpwfxFormat = &wfe;

   lpDS8->CreateSoundBuffer(&dsbd, &lpDSBSecondary, NULL);

   SoundData = new unsigned __int8[wfe.nSamplesPerSec];
   ZeroMemory(&SoundData, sizeof(SoundData));

   PlaySound();

   lpDSBSecondary->Play(NULL, 0, DSBPLAY_LOOPING);
}

void CDirectX::PlaySound()
{
   ppvAudioPtr1 = NULL;

   lpDSBSecondary->Lock(0, NULL, &ppvAudioPtr1, &pdwAudioBytes1, NULL, NULL, DSBLOCK_ENTIREBUFFER);

   memcpy(ppvAudioPtr1, SoundData, pdwAudioBytes1);

   lpDSBSecondary->Unlock(ppvAudioPtr1, pdwAudioBytes1, NULL, NULL);
}


I then want to fill SoundData with the necessary data and then use PlaySound to Lock/Write/Unlock the buffer.

1. How do I clear the SoundData buffer so that no sounds play? Any attempt to set the array to $00 crashes.

2. Should SoundData be 8-bit?

3. Do I update SoundData 60/50 times per second like I update the graphics?

To be honest its hard for me to even put my questions into text. A straight out explanation of everything would be nice.
Re: DirectSound
by on (#128804)
WedNESday wrote:
1. How do I clear the SoundData buffer so that no sounds play? Any attempt to set the array to $00 crashes.

2. Should SoundData be 8-bit?

3. Do I update SoundData 60/50 times per second like I update the graphics?

To be honest its hard for me to even put my questions into text. A straight out explanation of everything would be nice.


1. When you say "set the array to $00", do you mean ZeroMemory the whole array?
2. The wave format from the "GetFormat" has a bits per sample value.
3. This is what I am doing too, but the audio of my emulator isn't very good.
Re: DirectSound
by on (#128812)
mkwong98 wrote:
WedNESday wrote:
1. How do I clear the SoundData buffer so that no sounds play? Any attempt to set the array to $00 crashes.

2. Should SoundData be 8-bit?

3. Do I update SoundData 60/50 times per second like I update the graphics?

To be honest its hard for me to even put my questions into text. A straight out explanation of everything would be nice.


1. When you say "set the array to $00", do you mean ZeroMemory the whole array?
2. The wave format from the "GetFormat" has a bits per sample value.
3. This is what I am doing too, but the audio of my emulator isn't very good.

1. Fixed.
2. Fixed.
3. To be honest for my Atari 2600 emulator I am getting good results.

Code:
temp = 0;

while (temp < DirectX.wfe.nAvgBytesPerSec)
{
   if (temp & 0x04)
      DirectX.SoundData[temp++] = 0x40;
   else
      DirectX.SoundData[temp++] = 0x00;
}


What I need to know is, when I fill my buffer up with data to write it to the Secondary buffer, with what do I fill it? The above is just a quick thing I did to produce some sound. The & 0x04 produces quite a high pitched sound effect.
Re: DirectSound
by on (#128819)
WedNESday wrote:
What I need to know is, when I fill my buffer up with data to write it to the Secondary buffer, with what do I fill it? The above is just a quick thing I did to produce some sound. The & 0x04 produces quite a high pitched sound effect.


Fill it with the combined output of the 5 channels.
For example:
1. I'm filling the buffer 60 times per second,
2. the buffer can hold 0.1 seconds worth of sound data,
3. channel square 1 is generating a square wave at 600 Hz with max vol,
4. other channels are silent
5. the output of channel square 1 make up 25% of the final sound
6. the buffer uses unsigned 8 bit value for 1 sample
7. the buffer is mono
Then when I fill the buffer, I'm filling 0.016667 seconds of data with 10 cycles of square wave at 25% of max vol. ie 10 pairs of 0x00 and 0x40 stretched to fill 1/6 of the buffer.
This is over simplified but should give you some ideas.
Re: DirectSound
by on (#128822)
WedNESday wrote:
2. Should SoundData be 8-bit?

No, it should be at least 16-bit - DPCM alone will take up half of the range, and the rest of the builtin sound channels will push you to the limit, at which point any expansion sound you add will overflow it completely.
Re: DirectSound
by on (#130477)
Quietust wrote:
No, it should be at least 16-bit - DPCM alone will take up half of the range, and the rest of the builtin sound channels will push you to the limit, at which point any expansion sound you add will overflow it completely.


If it's signed 8-bit, the DPCM will take over the entire positive range. Adding other channels would probably cause terrible distortion :-) But yes, audio is usually 16-bits per sample, signed PCM @ 44.1 KHz.
Re: DirectSound
by on (#130480)
Besides, with all the DC (due to using only positive output levels) and the ultrasound (due to use of squarish waves at 1.8 MHz sample rate) that the APU puts out, you'll need a bit of extra headroom to run a band-pass filter.