//-----------------------------------------------------------------------------
// File: NetVoice.cpp
//
// Desc: DirectPlay Voice framework class. Feel free to use 
//       this class as a starting point for adding extra functionality.
//
//
// Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#define STRICT
#include <windows.h>
#include <dxerr8.h>
#include <dvoice.h>
#include "NetVoice.h"
#include "DXUtil.h"




//-----------------------------------------------------------------------------
// Name: CNetVoice
// Desc: 
//-----------------------------------------------------------------------------
CNetVoice::CNetVoice( LPDVMESSAGEHANDLER pfnDirectPlayClientVoiceMessageHandler,
                      LPDVMESSAGEHANDLER pfnDirectPlayServerVoiceMessageHandler )
{
    m_bHalfDuplex  = FALSE;
    m_pVoiceClient = NULL;
    m_pVoiceServer = NULL;
    m_pfnDirectPlayClientVoiceMessageHandler = pfnDirectPlayClientVoiceMessageHandler;
    m_pfnDirectPlayServerVoiceMessageHandler = pfnDirectPlayServerVoiceMessageHandler;
}




//-----------------------------------------------------------------------------
// Name: ~CNetVoice
// Desc: 
//-----------------------------------------------------------------------------
CNetVoice::~CNetVoice()
{
    Free();
}




//-----------------------------------------------------------------------------
// Name: Init()
// Desc: Initializes DirectPlay Voice.  
// Params:  hDlg: the HWND of the parent window for use by the voice setup wizard
//          bCreateSession:     if TRUE then it creates the DirectPlay Voice sesson.
//          bConnectToSession:  if TRUE then it connects to the DirectPlay Voice
//                              session.  
//          pDirectPlay:        inteface to the IDirectPlay8Client or 
//                              IDirectPlay8Peer interface 
//          pGuidCT:            guid of the voice compression codec
//          pdvClientConfig:    client config. Can be NULL if bConnectToSession is FALSE.
//          lpds:               pointer to an existing DirectSound object, or NULL 
//                              if none exists yet.
//-----------------------------------------------------------------------------
HRESULT CNetVoice::Init( HWND hDlg, BOOL bCreateSession, BOOL bConnectToSession,
                         LPUNKNOWN pDirectPlay, DWORD dwSessionType, 
                         GUID* pGuidCT, DVCLIENTCONFIG* pdvClientConfig, LPDIRECTSOUND lpds )
{
    HRESULT hr;

    // Typically the host player creates the voice session 
    if( bCreateSession )
    {
        if( FAILED( hr = VoiceSessionCreate( pDirectPlay, dwSessionType, pGuidCT ) ) )
            return DXTRACE_ERR( TEXT("VoiceSessionCreate"), hr );
    }

    if( bConnectToSession )
    {
        // Test the voice setup to make sure the voice setup wizard has been run
        if( FAILED( hr = VoiceSessionTestAudioSetup( hDlg ) ) )
        {
            if( hr == DVERR_USERCANCEL || hr == DVERR_ALREADYPENDING )
                return hr;
            
            return DXTRACE_ERR( TEXT("VoiceSessionTestAudioSetup"), hr );
        }

        // Typically all of the clients connect to the voice session
        if( FAILED( hr = VoiceSessionConnect( hDlg, pDirectPlay, pdvClientConfig, lpds ) ) )
            return DXTRACE_ERR( TEXT("VoiceSessionConnect"), hr );
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: Free()
// Desc: Frees DirectPlayVoice
//-----------------------------------------------------------------------------
HRESULT CNetVoice::Free()
{
    HRESULT hr;

    if( m_pVoiceClient )
    {
        // Have all the clients disconnect from the session
        if( FAILED( hr = VoiceSessionDisconnect() ) )
            return DXTRACE_ERR( TEXT("VoiceSessionDisconnect"), hr );
    }

    if( m_pVoiceServer )
    {
        // Have all the host player destroy the session 
        if( FAILED( hr = VoiceSessionDestroy() ) )
            return DXTRACE_ERR( TEXT("VoiceSessionDestroy"), hr );
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: HostMigrate()
// Desc: Starts the DirectPlayVoice session
//       The host player should call this to create the voice session.  It
//       stores the server interface, and addref's it.
//-----------------------------------------------------------------------------
HRESULT CNetVoice::HostMigrate( LPDIRECTPLAYVOICESERVER pdvServerInterface )
{
    if( pdvServerInterface == NULL )
        return E_INVALIDARG;

    SAFE_RELEASE( m_pVoiceServer );

    m_pVoiceServer = pdvServerInterface;

    // Addref the server since we are storing the pointer.
    m_pVoiceServer->AddRef();

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: VoiceSessionTestAudioSetup()
// Desc: Uses IDirectPlayVoiceSetup to test the voice setup.
//       All clients should call this once to test the voice audio setup.
//-----------------------------------------------------------------------------
HRESULT CNetVoice::VoiceSessionTestAudioSetup( HWND hDlg )
{
    HRESULT hr;
    LPDIRECTPLAYVOICETEST pVoiceSetup = NULL;

    // Create a DirectPlayVoice setup interface.
    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlayVoiceTest, NULL, 
                                       CLSCTX_INPROC_SERVER,
                                       IID_IDirectPlayVoiceTest, 
                                       (LPVOID*) &pVoiceSetup) ) )
        return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );

    // Check to see if the audio tests have been run yet
    GUID guidPlayback = DSDEVID_DefaultVoicePlayback;
    GUID guidCapture  = DSDEVID_DefaultVoiceCapture;
    hr = pVoiceSetup->CheckAudioSetup( &guidPlayback, 
                                       &guidCapture, 
                                       hDlg, DVFLAGS_QUERYONLY );
    if( hr == DVERR_RUNSETUP )
    {
        // Perform the audio tests, since they need to be done before 
        // any of the DPVoice calls will work.
        hr = pVoiceSetup->CheckAudioSetup( &guidPlayback, &guidCapture, hDlg, DVFLAGS_ALLOWBACK );
        if( FAILED(hr) )
        {
            SAFE_RELEASE( pVoiceSetup );
            
            if( hr == DVERR_USERCANCEL || hr == DVERR_ALREADYPENDING )
                return hr;

            return DXTRACE_ERR( TEXT("CheckAudioSetup"), hr );
        }
    }

    // Done with setup
    SAFE_RELEASE( pVoiceSetup );

    return hr;
}




//-----------------------------------------------------------------------------
// Name: VoiceSessionCreate()
// Desc: Starts the DirectPlayVoice session
//       The host player should call this to create the voice session.
//-----------------------------------------------------------------------------
HRESULT CNetVoice::VoiceSessionCreate( LPUNKNOWN pDirectPlay, DWORD dwSessionType, 
                                       GUID* pGuidCT )
{
    HRESULT hr;

    // Create a DirectPlayVoice server interface.
    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlayVoiceServer, NULL, 
                                       CLSCTX_INPROC_SERVER,
                                       IID_IDirectPlayVoiceServer, 
                                       (LPVOID*) &m_pVoiceServer ) ) )
        return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );

    // Init the DirectPlayVoice server
    if( FAILED( hr = m_pVoiceServer->Initialize( pDirectPlay, m_pfnDirectPlayServerVoiceMessageHandler, 
                                                 NULL, 0, 0 ) ) )
        return DXTRACE_ERR( TEXT("Initialize"), hr );

    // Setup and start a DirectPlay session based on globals set by user choices 
    // in the config dlg box.
    DVSESSIONDESC dvSessionDesc;
    ZeroMemory( &dvSessionDesc, sizeof(DVSESSIONDESC) );
    dvSessionDesc.dwSize                 = sizeof( DVSESSIONDESC );
    dvSessionDesc.dwBufferAggressiveness = DVBUFFERAGGRESSIVENESS_DEFAULT;
    dvSessionDesc.dwBufferQuality        = DVBUFFERQUALITY_DEFAULT;
    dvSessionDesc.dwFlags                = 0;
    dvSessionDesc.dwSessionType          = dwSessionType;
    dvSessionDesc.guidCT                 = *pGuidCT;

    if( FAILED( hr = m_pVoiceServer->StartSession( &dvSessionDesc, 0 ) ) )
        return DXTRACE_ERR( TEXT("StartSession"), hr );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: VoiceSessionConnect()
// Desc: Connects to the DirectPlayVoice session.  
///      All clients should call this once to join the voice session.
//-----------------------------------------------------------------------------
HRESULT CNetVoice::VoiceSessionConnect( HWND hDlg, LPUNKNOWN pDirectPlay, 
                                        DVCLIENTCONFIG* pdvClientConfig, LPDIRECTSOUND lpds )
{
    HRESULT hr;

    // Create a DirectPlayVoice client interface.
    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlayVoiceClient, NULL, 
                                       CLSCTX_INPROC_SERVER,
                                       IID_IDirectPlayVoiceClient, 
                                       (LPVOID*) &m_pVoiceClient ) ) )
        return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );

    // Init the DirectPlayVoice client, passing in VoiceMessageHandler() as the
    // callback handler for any messages sent to us.
    if( FAILED( hr = m_pVoiceClient->Initialize( pDirectPlay, 
                                                 m_pfnDirectPlayClientVoiceMessageHandler, 
                                                 (LPVOID*) hDlg, // context value
                                                 0, 0 ) ) )
         return DXTRACE_ERR( TEXT("Initialize"), hr );

    // Setup the DirectPlayVoice sound devices.  This just uses the defaults.
    DVSOUNDDEVICECONFIG dvSoundDeviceConfig;
    dvSoundDeviceConfig.dwSize                    = sizeof( DVSOUNDDEVICECONFIG );
    dvSoundDeviceConfig.dwFlags                   = 0;
    dvSoundDeviceConfig.guidPlaybackDevice        = DSDEVID_DefaultVoicePlayback; 
    dvSoundDeviceConfig.lpdsPlaybackDevice        = lpds;
    dvSoundDeviceConfig.guidCaptureDevice         = DSDEVID_DefaultVoiceCapture; 
    dvSoundDeviceConfig.lpdsCaptureDevice         = NULL;
    dvSoundDeviceConfig.hwndAppWindow             = hDlg;
    dvSoundDeviceConfig.lpdsMainBuffer            = NULL;
    dvSoundDeviceConfig.dwMainBufferFlags         = 0;
    dvSoundDeviceConfig.dwMainBufferPriority      = 0;

    // Connect to the voice session
    if( FAILED( hr = m_pVoiceClient->Connect( &dvSoundDeviceConfig, 
                                              pdvClientConfig, 
                                              DVFLAGS_SYNC ) ) )
        return DXTRACE_ERR( TEXT("Connect"), hr );
        
    // Talk to everyone in the session
    DVID dvid = DVID_ALLPLAYERS;
    if( FAILED( hr = m_pVoiceClient->SetTransmitTargets( &dvid, 1, 0 ) ) )
        return DXTRACE_ERR( TEXT("SetTransmitTargets"), hr );

    // Get the sound device config and extract if its half duplex
    DWORD dwSize = 0;
    DVSOUNDDEVICECONFIG* pdvSoundDeviceConfig = NULL;
    hr = m_pVoiceClient->GetSoundDeviceConfig( pdvSoundDeviceConfig, &dwSize );
    if( FAILED(hr) && hr != DVERR_BUFFERTOOSMALL )
        return DXTRACE_ERR( TEXT("GetSoundDeviceConfig"), hr );
    pdvSoundDeviceConfig = (DVSOUNDDEVICECONFIG*) new BYTE[ dwSize ];
    ZeroMemory( pdvSoundDeviceConfig, dwSize );
    pdvSoundDeviceConfig->dwSize = sizeof(DVSOUNDDEVICECONFIG);
    hr = m_pVoiceClient->GetSoundDeviceConfig( pdvSoundDeviceConfig, &dwSize );
    if( FAILED(hr) )
        return DXTRACE_ERR( TEXT("GetSoundDeviceConfig"), hr );

    m_bHalfDuplex = ( (pdvSoundDeviceConfig->dwFlags & DVSOUNDCONFIG_HALFDUPLEX) != 0 );

    SAFE_DELETE_ARRAY( pdvSoundDeviceConfig );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: ChangeVoiceClientSettings()
// Desc: Changes the client config to globals set by user choices 
//       in the config dlg box.
//-----------------------------------------------------------------------------
HRESULT CNetVoice::ChangeVoiceClientSettings( DVCLIENTCONFIG* pdvClientConfig )
{
    HRESULT hr;

    if( m_pVoiceClient == NULL )
        return CO_E_NOTINITIALIZED;

    // Change the client config
    if( FAILED( hr = m_pVoiceClient->SetClientConfig( pdvClientConfig) ) )
        return DXTRACE_ERR( TEXT("SetClientConfig"), hr );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: VoiceSessionDisconnect()
// Desc: Disconnects from the DirectPlayVoice session
//       All clients should call this once to leave the voice session.
//-----------------------------------------------------------------------------
HRESULT CNetVoice::VoiceSessionDisconnect()
{
    if( m_pVoiceClient )
    {
        m_pVoiceClient->Disconnect( DVFLAGS_SYNC );
        SAFE_RELEASE( m_pVoiceClient );
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: VoiceSessionDestroy()
// Desc: Stops the DirectPlayVoice session
//       The host player should call this once to destroy the voice session.
//-----------------------------------------------------------------------------
HRESULT CNetVoice::VoiceSessionDestroy()
{
    if( m_pVoiceServer )
    {
        m_pVoiceServer->StopSession( 0 );
        SAFE_RELEASE( m_pVoiceServer );
    }

    return S_OK;
}





