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

📄 ch16p1_voicechat.cpp

📁 游戏音频程序设计-Beginning.Game.Audio.Programming
💻 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 + -