Files
SLikeNet/Samples/RakVoiceDSound/DSoundVoiceAdapter.cpp
2025-11-24 14:19:51 +05:30

403 lines
12 KiB
C++

/*
* Original work: Copyright (c) 2014, Oculus VR, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* RakNet License.txt file in the licenses directory of this source tree. An additional grant
* of patent rights can be found in the RakNet Patents.txt file in the same directory.
*
*
* Modified work: Copyright (c) 2016-2020, SLikeSoft UG (haftungsbeschränkt)
*
* This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
* license found in the license.txt file in the root directory of this source tree.
*/
#include "DSoundVoiceAdapter.h"
#include "slikenet/assert.h"
#include "slikenet/peerinterface.h"
#include <tchar.h>
/// To test sending to myself
//#define _TEST_LOOPBACK
using namespace SLNet;
DSoundVoiceAdapter DSoundVoiceAdapter::instance;
DSoundVoiceAdapter::DSoundVoiceAdapter()
{
m_rakVoice = 0;
ds = 0;
dsC = 0;
dsbIncoming = 0;
dsbOutgoing = 0;
m_mute = false;
memset(incomingBufferNotifications,0,sizeof(incomingBufferNotifications));
memset(outgoingBufferNotifications,0,sizeof(outgoingBufferNotifications));
}
DSoundVoiceAdapter* DSoundVoiceAdapter::Instance()
{
return &instance;
}
bool DSoundVoiceAdapter::SetupAdapter(RakVoice *rakVoice, HWND hwnd, DWORD dwCoopLevel)
{
// Check if it was already initialized
RakAssert(ds == 0);
if (ds != 0)
return false;
HRESULT hr;
if (FAILED(hr = DirectSoundCreate8(nullptr, &ds, nullptr)))
{
DXTRACE_ERR_MSGBOX(_T("DirectSoundCreate8, when initiliazing DirectSound"), hr);
return false;
}
if (FAILED(hr = ds->SetCooperativeLevel(hwnd, dwCoopLevel)))
{
DXTRACE_ERR_MSGBOX(_T("IDirectSound8::SetCooperativeLevel"), hr);
Release();
return false;
}
// Check if the rest of the initialization fails, and if so, release any allocated resources
if (SetupAdapter(rakVoice))
{
return true;
}
else
{
Release();
return false;
}
}
bool DSoundVoiceAdapter::SetupAdapter(RakVoice *rakVoice, IDirectSound8 *pDS)
{
// Check if it was already initialized
RakAssert(ds == 0);
if (ds != 0)
return false;
// User provided his own device object, so use it and add another reference
pDS->AddRef();
ds = pDS;
// Check if the rest of the initialization fails, and if so, release any allocated resources
if (SetupAdapter(rakVoice))
{
return true;
}
else
{
Release();
return false;
}
}
bool DSoundVoiceAdapter::SetupAdapter(RakVoice *rakVoice)
{
RakAssert(rakVoice);
// Make sure rakVoice was initialized
RakAssert((rakVoice->IsInitialized())&&(rakVoice->GetRakPeerInterface()!= nullptr));
m_rakVoice = rakVoice;
if (!SetupIncomingBuffer())
return false;
return SetupOutgoingBuffer();
}
bool DSoundVoiceAdapter::SetupIncomingBuffer()
{
//
//
// Create the buffer for incoming sound
//
//
WAVEFORMATEX wfx;
DSBUFFERDESC dsbdesc;
LPDIRECTSOUNDBUFFER pDsb = nullptr;
HRESULT hr;
// Set up WAV format structure.
memset(&wfx, 0, sizeof(WAVEFORMATEX));
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 1;
wfx.nSamplesPerSec = m_rakVoice->GetSampleRate();
wfx.nBlockAlign = 2;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.wBitsPerSample = 16;
// Set up DSBUFFERDESC structure.
memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY
| DSBCAPS_LOCSOFTWARE; // Create in software, because DX documentation says its the best option when using notifications
dsbdesc.dwBufferBytes = m_rakVoice->GetBufferSizeBytes()*FRAMES_IN_SOUND;
dsbdesc.lpwfxFormat = &wfx;
// Create buffer.
if (FAILED(hr = ds->CreateSoundBuffer(&dsbdesc, &pDsb, nullptr)))
{
DXTRACE_ERR_MSGBOX(_T("IDirectSound8::CreateSoundBuffer, when creating buffer for incoming sound )"), hr);
return false;
}
hr = pDsb->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*) &dsbIncoming);
pDsb->Release();
if (FAILED(hr))
{
DXTRACE_ERR_MSGBOX(_T("IDirectSoundBuffer::QueryInterface, when getting IDirectSoundBuffer8 interface for incoming sound"), hr);
return false;
}
//
// Setup the notification events
//
for (int i=0; i<FRAMES_IN_SOUND; i++)
{
incomingBufferNotifications[i].dwOffset = i*m_rakVoice->GetBufferSizeBytes();
#if defined(WINDOWS_PHONE_8)
if ((incomingBufferNotifications[i].hEventNotify = CreateEventEx(0, 0, CREATE_EVENT_MANUAL_RESET, 0))== nullptr)
#else
if ((incomingBufferNotifications[i].hEventNotify = CreateEvent(nullptr, TRUE, FALSE, nullptr))== nullptr)
#endif
{
DXTRACE_ERR_MSGBOX(_T("CreateEvent"), GetLastError());
return false;
}
}
IDirectSoundNotify8 *dsbNotify=0;
if (FAILED(hr=dsbIncoming->QueryInterface(IID_IDirectSoundNotify8, (LPVOID*) &dsbNotify)))
{
DXTRACE_ERR_MSGBOX(_T("IDirectSoundBuffer8::QueryInterface, when getting IDirectSoundNotify8 interface for incoming sound"), hr);
return false;
}
hr = dsbNotify->SetNotificationPositions(FRAMES_IN_SOUND, incomingBufferNotifications);
dsbNotify->Release();
if (FAILED(hr))
{
DXTRACE_ERR_MSGBOX(_T("IDirectSoundNotify8::SetNotificationPositions, when setting notifications for incoming sound"), hr);
return false;
}
if (FAILED(hr = dsbIncoming->Play(0,0,DSBPLAY_LOOPING)))
{
DXTRACE_ERR_MSGBOX(_T("IDirectSoundBuffer8::Play, when starting incoming sound buffer"), hr);
return false;
}
return true;
}
bool DSoundVoiceAdapter::SetupOutgoingBuffer()
{
HRESULT hr;
//
//
// Create the buffer for outgoing sound
//
//
if (FAILED(hr=DirectSoundCaptureCreate8(nullptr, &dsC, nullptr)))
{
DXTRACE_ERR_MSGBOX(_T("DirectSoundCaptureCreate8"), hr);
return false;
}
// Set up WAV format structure.
WAVEFORMATEX wfx;
memset(&wfx, 0, sizeof(WAVEFORMATEX));
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 1;
wfx.nSamplesPerSec = m_rakVoice->GetSampleRate();
wfx.nBlockAlign = 2;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.wBitsPerSample = 16;
// Set up DSCBUFFERDESC structure.
DSCBUFFERDESC dscbd;
memset(&dscbd, 0, sizeof(DSCBUFFERDESC));
dscbd.dwSize = sizeof(DSCBUFFERDESC);
dscbd.dwFlags = 0;
dscbd.dwBufferBytes = m_rakVoice->GetBufferSizeBytes()*FRAMES_IN_SOUND;
dscbd.dwReserved = 0;
dscbd.lpwfxFormat = &wfx;
dscbd.dwFXCount = 0;
dscbd.lpDSCFXDesc = nullptr;
// Create capture buffer.
LPDIRECTSOUNDCAPTUREBUFFER pDscb = nullptr;
if (FAILED(hr = dsC->CreateCaptureBuffer(&dscbd, &pDscb, nullptr)))
{
DXTRACE_ERR_MSGBOX(_T("IDirectSoundCapture8::CreateCaptureBuffer, when creating buffer for outgoing sound )"), hr);
return false;
}
hr = pDscb->QueryInterface(IID_IDirectSoundCaptureBuffer8, (LPVOID*) &dsbOutgoing);
pDscb->Release();
if (FAILED(hr))
{
DXTRACE_ERR_MSGBOX(_T("IDirectSoundBuffer::QueryInterface, when getting IDirectSoundCaptureBuffer8 interface for outgoing sound"), hr);
return false;
}
//
// Setup the notification events
//
for (int i=0; i<FRAMES_IN_SOUND; i++)
{
outgoingBufferNotifications[i].dwOffset = i*m_rakVoice->GetBufferSizeBytes();
if ((outgoingBufferNotifications[i].hEventNotify = CreateEventEx(0, 0, CREATE_EVENT_MANUAL_RESET, 0))== nullptr)
{
DXTRACE_ERR_MSGBOX(_T("CreateEvent"), GetLastError());
return false;
}
}
IDirectSoundNotify8 *dsbNotify=0;
if (FAILED(hr=dsbOutgoing->QueryInterface(IID_IDirectSoundNotify8, (LPVOID*) &dsbNotify)))
{
DXTRACE_ERR_MSGBOX(_T("IDirectSoundCaptureBuffer8::QueryInterface, when getting IDirectSoundNotify8 interface for outgoing sound"), hr);
return false;
}
hr = dsbNotify->SetNotificationPositions(FRAMES_IN_SOUND, outgoingBufferNotifications);
dsbNotify->Release();
if (FAILED(hr))
{
DXTRACE_ERR_MSGBOX(_T("IDirectSoundNotify8::SetNotificationPositions, when setting notifications for outgoing sound"), hr);
return false;
}
if (FAILED(hr = dsbOutgoing->Start(DSCBSTART_LOOPING)))
{
DXTRACE_ERR_MSGBOX(_T("IDirectSoundCaptureBuffer8::Start, when starting outgoing sound buffer"), hr);
return false;
}
return true;
}
void DSoundVoiceAdapter::Release()
{
// Release DirectSound buffer used for incoming voice
if (dsbIncoming)
{
dsbIncoming->Stop();
dsbIncoming->Release();
dsbIncoming = 0;
}
// Release DirectSound buffer used for outgoing voice
if (dsbOutgoing)
{
dsbOutgoing->Stop();
dsbOutgoing->Release();
dsbOutgoing = 0;
}
// Release DirectSound device object
if (ds)
{
ds->Release();
ds = 0;
}
if (dsC)
{
dsC->Release();
dsC = 0;
}
// Release the notification events
for (int i=0; i<FRAMES_IN_SOUND;i++)
{
if (incomingBufferNotifications[i].hEventNotify!=0 && CloseHandle(incomingBufferNotifications[i].hEventNotify)==0)
{
DXTRACE_ERR_MSGBOX(_T("CloseHandle"), GetLastError());
}
if (outgoingBufferNotifications[i].hEventNotify!=0 && CloseHandle(outgoingBufferNotifications[i].hEventNotify)==0)
{
DXTRACE_ERR_MSGBOX(_T("CloseHandle"), GetLastError());
}
}
memset(incomingBufferNotifications,0, sizeof(incomingBufferNotifications));
memset(outgoingBufferNotifications,0, sizeof(outgoingBufferNotifications));
}
IDirectSound8* DSoundVoiceAdapter::GetDSDeviceObject()
{
return ds;
}
void DSoundVoiceAdapter::Update()
{
HRESULT hr;
void *audioPtr;
DWORD audioPtrbytes;
for (int i=0; i<FRAMES_IN_SOUND; i++)
{
//
// Update incoming sound
//
if (WaitForSingleObject(incomingBufferNotifications[i].hEventNotify, 0)==WAIT_OBJECT_0)
{
// The lock offset is the buffer right before the one the event refers to
DWORD dwOffset = (i==0) ? incomingBufferNotifications[FRAMES_IN_SOUND-1].dwOffset : incomingBufferNotifications[i-1].dwOffset;
hr = dsbIncoming->Lock(dwOffset, m_rakVoice->GetBufferSizeBytes(), &audioPtr, &audioPtrbytes, nullptr, nullptr, 0);
// #med - should change GetBufferSizeBytes()-interface to unsigned int return
RakAssert(audioPtrbytes==static_cast<unsigned int>(m_rakVoice->GetBufferSizeBytes()));
if (SUCCEEDED(hr))
{
m_rakVoice->ReceiveFrame(audioPtr);
dsbIncoming->Unlock(audioPtr, audioPtrbytes, nullptr, 0);
}
ResetEvent(incomingBufferNotifications[i].hEventNotify);
}
//
// Update outgoing sound
//
if (WaitForSingleObject(outgoingBufferNotifications[i].hEventNotify, 0)==WAIT_OBJECT_0)
{
/* If we're set to mute, we don't send anything, and just reset the event */
if (!m_mute)
{
// The lock offset is the buffer right before the one the event refers to
DWORD dwOffset = (i==0) ? outgoingBufferNotifications[FRAMES_IN_SOUND-1].dwOffset : outgoingBufferNotifications[i-1].dwOffset;
hr = dsbOutgoing->Lock(dwOffset, m_rakVoice->GetBufferSizeBytes(), &audioPtr, &audioPtrbytes, nullptr, nullptr, 0);
// #med - should change GetBufferSizeBytes()-interface to unsigned int return
RakAssert(audioPtrbytes == static_cast<unsigned int>(m_rakVoice->GetBufferSizeBytes()));
if (SUCCEEDED(hr))
{
BroadcastFrame(audioPtr);
dsbOutgoing->Unlock(audioPtr, audioPtrbytes, nullptr, 0);
}
}
ResetEvent(outgoingBufferNotifications[i].hEventNotify);
}
}
}
void DSoundVoiceAdapter::BroadcastFrame(void *ptr)
{
#ifndef _TEST_LOOPBACK
unsigned i;
unsigned int numPeers = m_rakVoice->GetRakPeerInterface()->GetMaximumNumberOfPeers();
for (i=0; i < numPeers; i++)
{
m_rakVoice->SendFrame(m_rakVoice->GetRakPeerInterface()->GetGUIDFromIndex(i), ptr);
}
#else
m_rakVoice->SendFrame(SLNet::UNASSIGNED_SYSTEM_ADDRESS, ptr);
#endif
}
void DSoundVoiceAdapter::SetMute(bool mute)
{
m_mute = mute;
}