📄 ch16p1_voicechat.cpp
字号:
// Ch16p1_VoiceChat.cpp : Defines the entry point for the application.
//
#include <objbase.h>
#include <windows.h>
#include <commctrl.h>
#include <dxerr8.h>
#include <dplay8.h>
#include <dplobby8.h>
#include <dvoice.h>
#include <cguid.h>
#pragma warning(disable: 4786)
#include <map>
#include <string>
#include <vector>
#include <sstream>
#include "resource.h"
#include "NetConnect.h" // header for network connection wizard
#include "NetVoice.h"
#include "DXUtil.h"
using namespace std;
CRITICAL_SECTION g_csAccessingPlayer;
class CPlayer
{
public:
CPlayer() {
m_RefCount = 0;
m_bHalfDuplex = false;
m_bVoiceEnabled = false;
m_bIsPlaying = false;
m_bIsRecording = false;
}
void AddRef() { m_RefCount++; }
void DeleteRef() { m_RefCount--; }
bool SafeToDelete() { return(m_RefCount == 0); }
string m_strName;
int m_RefCount;
bool m_bHalfDuplex;
bool m_bVoiceEnabled;
bool m_bIsPlaying;
bool m_bIsRecording;
};
// the players in the game
map<DPNID, CPlayer> g_Players;
vector<char *> g_ListboxStrings;
HWND g_hDlg = NULL; // our main dialog box handle - used inside the DP message handler
// this program's ID - used to find other instances of this program running on other computers
// {2B0C51EB-674B-4b6a-B3A5-56D9FF4A8A32}
static GUID g_GameGUID =
{ 0x2b0c51eb, 0x674b, 0x4b6a, { 0xb3, 0xa5, 0x56, 0xd9, 0xff, 0x4a, 0x8a, 0x32 } };
IDirectPlay8Peer* g_pDPPeer = NULL; // DirectPlay peer object
IDirectPlay8LobbiedApplication* g_pDPLobbiedApp = NULL; // DirectPlay lobbied app object
CNetConnectWizard* g_pNetConnectWizard = NULL; // Connection wizard
CNetVoice* g_pNetVoice = NULL; // DirectPlay voice helper class
///////////////////////////////////////////////////////////////////////////////
//
// RefreshPlayerList(): refreshes the player listbox
//
void RefreshPlayerList()
{
if (!IsWindow(GetDlgItem(g_hDlg, IDC_PLAYERLIST))) return; // g_hDlg may not be created yet!
EnterCriticalSection(&g_csAccessingPlayer);
// clear listbox
SendMessage(GetDlgItem(g_hDlg, IDC_PLAYERLIST), LB_RESETCONTENT, 0, 0);
// remove old strings from memory
for (vector<char *>::iterator ci = g_ListboxStrings.begin(); ci != g_ListboxStrings.end(); ++ci) {
delete (*ci);
}
g_ListboxStrings.clear();
// repopulate listbox
for (map<DPNID, CPlayer>::iterator iter = g_Players.begin(); iter != g_Players.end(); ++iter) {
char *s = new char[MAX_PATH];
_snprintf(s, MAX_PATH, "%s - %s %s (%s)",
iter->second.m_strName.c_str(),
iter->second.m_bIsPlaying ? "[PLAY]" : " ",
iter->second.m_bIsRecording ? "[REC]" : " ",
iter->second.m_bVoiceEnabled ?
(iter->second.m_bHalfDuplex ? "listen only" : "full duplex") :
"no voice");
g_ListboxStrings.push_back(s);
SendMessage(GetDlgItem(g_hDlg, IDC_PLAYERLIST), LB_ADDSTRING, 0, (LPARAM)s);
}
LeaveCriticalSection(&g_csAccessingPlayer);
}
///////////////////////////////////////////////////////////////////////////////
//
// MainDlgProc(): main dialog box message handler for our app.
//
INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg ) {
case WM_INITDIALOG:
g_hDlg = hDlg;
// Set default DirectPlayVoice setup options
DVCLIENTCONFIG config;
ZeroMemory( &config, sizeof(config) );
config.dwSize = sizeof(config);
config.dwFlags = DVCLIENTCONFIG_AUTOVOICEACTIVATED | DVCLIENTCONFIG_AUTORECORDVOLUME;
config.lPlaybackVolume = DVPLAYBACKVOLUME_DEFAULT;
config.dwBufferQuality = DVBUFFERQUALITY_DEFAULT;
config.dwBufferAggressiveness = DVBUFFERAGGRESSIVENESS_DEFAULT;
config.dwThreshold = DVTHRESHOLD_UNUSED;
config.lRecordVolume = DVRECORDVOLUME_LAST;
config.dwNotifyPeriod = 0;
GUID codecguid = DPVCTGUID_DEFAULT;
// initialize voice class
g_pNetVoice->Init( hDlg, g_pNetConnectWizard->IsHostPlayer(), TRUE, g_pDPPeer,
DVSESSIONTYPE_PEER, &codecguid, &config );
RefreshPlayerList();
break;
case WM_CLOSE:
EndDialog(hDlg, 0);
break;
}
return FALSE; // Didn't handle message
}
HRESULT WINAPI OnDirectPlayMessage( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer )
{
switch( dwMessageId ) {
case DPN_MSGID_CREATE_PLAYER:
{
HRESULT hr;
PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
pCreatePlayerMsg = reinterpret_cast<PDPNMSG_CREATE_PLAYER>(pMsgBuffer);
DWORD dwSize = 0;
DPN_PLAYER_INFO* pdpPlayerInfo = NULL;
EnterCriticalSection( &g_csAccessingPlayer );
// call GetPeerInfo to get new player's name
do {
hr = g_pDPPeer->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );
if( hr == DPNERR_BUFFERTOOSMALL ) {
pdpPlayerInfo = (DPN_PLAYER_INFO*) new unsigned char[ dwSize ];
ZeroMemory( pdpPlayerInfo, dwSize );
pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
}
} while (hr == DPNERR_CONNECTING || hr == DPNERR_BUFFERTOOSMALL );
// create player object and assign name to it
CPlayer newplayer;
char name[MAX_PATH];
DXUtil_ConvertWideStringToGeneric( name, pdpPlayerInfo->pwszName, MAX_PATH);
newplayer.m_strName = name;
newplayer.AddRef();
// add it to the array of players
g_Players[pCreatePlayerMsg->dpnidPlayer] = newplayer;
delete[] pdpPlayerInfo;
LeaveCriticalSection( &g_csAccessingPlayer );
RefreshPlayerList();
}
break;
case DPN_MSGID_DESTROY_PLAYER:
{
PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
pDestroyPlayerMsg = reinterpret_cast<PDPNMSG_DESTROY_PLAYER>(pMsgBuffer);
// delete this player from the array
EnterCriticalSection( &g_csAccessingPlayer );
g_Players[pDestroyPlayerMsg->dpnidPlayer].DeleteRef();
if (g_Players[pDestroyPlayerMsg->dpnidPlayer].SafeToDelete()) {
g_Players.erase(pDestroyPlayerMsg->dpnidPlayer);
}
LeaveCriticalSection( &g_csAccessingPlayer );
RefreshPlayerList();
}
break;
case DPN_MSGID_TERMINATE_SESSION:
{
// TODO
MessageBox(g_hDlg, "TERMINATE_SESSION message received, which usually means the connection was lost or terminated by the server.", "Ch16p1_VoiceChat", MB_ICONSTOP);
EndDialog( g_hDlg, 0 );
}
break;
}
// give the wizard a chance to react to the message
return g_pNetConnectWizard->MessageHandler( pvUserContext, dwMessageId, pMsgBuffer );
}
HRESULT WINAPI OnDirectPlayLobbyMessage( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer )
{
return g_pNetConnectWizard->LobbyMessageHandler( pvUserContext, dwMessageId, pMsgBuffer );
}
HRESULT CALLBACK OnVoiceServerMessage( LPVOID lpvUserContext, DWORD dwMessageType,
LPVOID lpMessage )
{
// no server messages this simple app responds to
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
//
// OnVoiceClientMessage: called by DirectPlay Voice for client messages.
HRESULT CALLBACK OnVoiceClientMessage( LPVOID lpvUserContext, DWORD dwMessageType,
LPVOID lpMessage )
{
switch( dwMessageType ) {
case DVMSGID_CREATEVOICEPLAYER:
{
DVMSG_CREATEVOICEPLAYER* pCreateVoicePlayerMsg = (DVMSG_CREATEVOICEPLAYER*) lpMessage;
EnterCriticalSection( &g_csAccessingPlayer );
// get player structure for this ID
if (g_Players.find(pCreateVoicePlayerMsg->dvidPlayer) != g_Players.end()) {
// we have an entry for this player (like we should!)
// create a reference to it and set some values
CPlayer &player = g_Players[pCreateVoicePlayerMsg->dvidPlayer];
player.AddRef(); // for the voice layer
player.m_bHalfDuplex = ((pCreateVoicePlayerMsg->dwFlags & DVPLAYERCAPS_HALFDUPLEX) != 0);
player.m_bVoiceEnabled = true;
// tuck the player ID away in the player context area so we can get to it later
pCreateVoicePlayerMsg->pvPlayerContext = reinterpret_cast<void *>(pCreateVoicePlayerMsg->dvidPlayer);
}
LeaveCriticalSection( &g_csAccessingPlayer );
}
break;
case DVMSGID_DELETEVOICEPLAYER:
{
DVMSG_DELETEVOICEPLAYER* pMsg = (DVMSG_DELETEVOICEPLAYER*) lpMessage;
EnterCriticalSection( &g_csAccessingPlayer );
g_Players[pMsg->dvidPlayer].DeleteRef();
if (g_Players[pMsg->dvidPlayer].SafeToDelete()) {
g_Players.erase(pMsg->dvidPlayer);
}
LeaveCriticalSection( &g_csAccessingPlayer );
RefreshPlayerList();
}
break;
case DVMSGID_RECORDSTART:
{
DVMSG_RECORDSTART* pMsg = (DVMSG_RECORDSTART*) lpMessage;
EnterCriticalSection( &g_csAccessingPlayer );
DPNID id = reinterpret_cast<DPNID>(pMsg->pvLocalPlayerContext);
if (g_Players[id].m_RefCount) {
g_Players[id].m_bIsRecording = true;
}
LeaveCriticalSection( &g_csAccessingPlayer );
RefreshPlayerList();
break;
}
case DVMSGID_RECORDSTOP:
{
DVMSG_RECORDSTOP* pMsg = (DVMSG_RECORDSTOP*) lpMessage;
EnterCriticalSection( &g_csAccessingPlayer );
DPNID id = reinterpret_cast<DPNID>(pMsg->pvLocalPlayerContext);
if (g_Players[id].m_RefCount) {
g_Players[id].m_bIsRecording = false;
}
LeaveCriticalSection( &g_csAccessingPlayer );
RefreshPlayerList();
break;
}
case DVMSGID_PLAYERVOICESTART:
{
DVMSG_PLAYERVOICESTART* pMsg = (DVMSG_PLAYERVOICESTART*) lpMessage;
EnterCriticalSection( &g_csAccessingPlayer );
DPNID id = reinterpret_cast<DPNID>(pMsg->pvPlayerContext);
if (g_Players[id].m_RefCount) {
g_Players[id].m_bIsPlaying = true;
}
LeaveCriticalSection( &g_csAccessingPlayer );
RefreshPlayerList();
break;
}
case DVMSGID_PLAYERVOICESTOP:
{
DVMSG_PLAYERVOICESTOP* pMsg = (DVMSG_PLAYERVOICESTOP*) lpMessage;
EnterCriticalSection( &g_csAccessingPlayer );
DPNID id = reinterpret_cast<DPNID>(pMsg->pvPlayerContext);
if (g_Players[id].m_RefCount) {
g_Players[id].m_bIsPlaying = false;
}
LeaveCriticalSection( &g_csAccessingPlayer );
RefreshPlayerList();
break;
}
}
return S_OK;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HRESULT hr;
try {
InitCommonControls();
// initialize critical section for updating player status
InitializeCriticalSection( &g_csAccessingPlayer );
// initialize COM
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// create directplay peer and lobby interfaces (used by wizard)
hr = CoCreateInstance( CLSID_DirectPlay8Peer, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlay8Peer,
(LPVOID*) &g_pDPPeer );
if (FAILED(hr)) throw("Error creating DirectPlay peer interface!");
hr = CoCreateInstance( CLSID_DirectPlay8LobbiedApplication, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlay8LobbiedApplication,
(LPVOID*) &g_pDPLobbiedApp );
if (FAILED(hr)) throw("Error creating DirectPlay lobbied application interface!");
// create DirectPlay network connection wizard
g_pNetConnectWizard = new CNetConnectWizard( hInstance, NULL, "Ch16p1_VoiceChat", &g_GameGUID );
g_pNetConnectWizard->Init( g_pDPPeer, g_pDPLobbiedApp );
// create DirectPlay voice wizard
g_pNetVoice = new CNetVoice( OnVoiceClientMessage, OnVoiceServerMessage );
// initialize peer and lobby interfaces (the wizard requires these to be initialized)
hr = g_pDPPeer->Initialize(NULL, OnDirectPlayMessage, 0);
if (FAILED(hr)) throw(hr, "Error initializing DirectPlay!");
hr = g_pDPLobbiedApp->Initialize( NULL, OnDirectPlayLobbyMessage, NULL, 0 );
if (FAILED(hr)) throw(hr, "Error initializing DirectPlay Lobbied Application interface!");
// display the DirectPlay network connection wizard. This wizard walks the user
// through connecting or starting a new game, and passes back their choices.
// set defaults
g_pNetConnectWizard->SetPlayerName( "Anonymous" );
g_pNetConnectWizard->SetSessionName( "New Game" );
g_pNetConnectWizard->SetPreferredProvider( "DirectPlay8 TCP/IP Service Provider" );
// display the connection wizard
hr = g_pNetConnectWizard->DoConnectWizard( FALSE );
if (FAILED(hr)) throw(hr, "Connection wizard failed.");
if( hr == NCW_S_QUIT )
{
// NCW_S_QUIT means the user chose to quit the app, so oblidge 'em
return(0);
}
// if we get here, we're connected to a directplay session. /////////////////////////////////////
// Display the "game" dialog - this dialog's WM_INITDIALOG handler creates the CNetVoice object
DialogBox( hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc );
// the dialog's done... uninit and delete everything
g_pNetConnectWizard->Shutdown();
if(g_pDPPeer) {
g_pDPPeer->Close(0);
SAFE_RELEASE(g_pDPPeer);
}
if(g_pDPLobbiedApp) {
g_pDPLobbiedApp->Close( 0 );
SAFE_RELEASE( g_pDPLobbiedApp );
}
SAFE_DELETE( g_pNetConnectWizard );
SAFE_DELETE( g_pNetVoice );
}
catch(char *msg)
{
MessageBox(NULL, msg, "Ch16p1_VoiceChat", MB_ICONSTOP);
}
DeleteCriticalSection( &g_csAccessingPlayer );
CoUninitialize();
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -