Microsoft DirectX 8.1 (C++) |
Microsoft® DirectPlay® Voice is a full-voice communications application programming interface (API) that uses DirectPlay for network session management and network transport. This tutorial extends the preceding tutorials to describe how to add voice communications to a peer-to-peer network application. Much of this information also applies to client/server applications. For further discussion of DirectPlay Voice, see Understanding DirectPlay Voice. The complete sample code for this tutorial is included with the Microsoft® DirectX® software development kit (SDK) and can be found at (SDK root)\Samples\Multimedia\DirectPlay\Tutorials\Tut08_Voice.
Refer to the preceding tutorials for a discussion of the initial steps in the process.
Note The error handling code for the examples in this document has been deleted for clarity. See the tutorial sample for a complete version of the code.
When you run this tutorial sample, a Microsoft® MS-DOS® command window opens and you have the choice to either Host or Connect.
If you choose Host, do the following:
If you choose Connect, do the following:
You can run this sample twice—once to host a session and once to connect. When connecting, enter your computer's IP address. Once you start a session and have at least two players connected (the host and a peer), the voice recording starts automatically. Speak into a microphone and your voice will play out on the speakers.
Before you can start up a DirectPlay Voice session, you must have a valid DirectPlay object connected to or hosting a session. For full details on creating, connecting and hosting DirectPlay objects, see previous tutorials.
To enable voice communications, one peer in the session must become the voice session host. To become the host, you must create a voice server object and obtain a pointer to its IDirectPlayVoiceServer interface. You use this interface to perform host-specific tasks during the voice session. You must then call IDirectPlayVoiceServer::Initialize to initialize the object. As with most DirectPlay objects, the primary purpose of initialization is to provide DirectPlay with a pointer to your voice server callback message handler.
Note The voice server host can be a different peer than the session host.
The following excerpt from the tutorial sample illustrates how to create and initialize a voice server object.
IDirectPlayVoiceServer* g_pVoiceServer = NULL; . . . // Create the voice server object hr = CoCreateInstance(CLSID_DirectPlayVoiceServer, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlayVoiceServer, (LPVOID*) &g_pVoiceServer); // Initialize the object hr = g_pVoiceServer->Initialize(g_pDP, DirectVoiceServerMessageHandler, NULL, 0, 0);
Before any clients can connect to a voice session, the voice session host must start the session by calling IDirectPlayVoiceServer::StartSession. Once the session has been started, voice clients can connect to the voice session.
The DVSESSIONDESC structure that you pass to this method contains the information DirectPlay needs to start the session. In particular, you must specify which of several DirectPlay Voice topologies you want to use. This tutorial uses the peer-to-peer topology. For further information on this subject, see DirectPlay Voice Topologies.
The following excerpt from the tutorial sample illustrates how to start a peer-to-peer session using the default CODEC.
IDirectPlayVoiceServer* g_pVoiceServer = NULL; . . . ZeroMemory(&dvSessionDesc, sizeof(DVSESSIONDESC)); dvSessionDesc.dwSize = sizeof(DVSESSIONDESC); dvSessionDesc.dwSessionType = DVSESSIONTYPE_PEER; dvSessionDesc.dwBufferQuality = DVBUFFERQUALITY_DEFAULT; dvSessionDesc.guidCT = DPVCTGUID_DEFAULT; dvSessionDesc.dwBufferAggressiveness = DVBUFFERAGGRESSIVENESS_DEFAULT; hr = g_pVoiceServer->StartSession(&dvSessionDesc, 0 );
Before connecting a voice client to a voice session, you must test the audio configuration. To do so, you must to create a DirectPlayVoiceTest object and call IDirectPlayVoiceTest::CheckAudioSetup. Call the method first with the DVFLAGS_QUERYONLY set to determine whether the test has already been run.
If the test has not been run, the method returns DVERR_RUNSETUP. You should then call the function again without the DVFLAGS_QUERYONLY flag, and DirectPlay will launch the Sound Hardware Test Wizard. If the method returns a success code, you can continue. Otherwise, you must first handle the error condition. After testing is complete, release the DirectPlayVoiceTest object. Once the audio configuration has been tested, your client can connect to the voice session.
The following excerpt from the tutorial sample illustrates how to test voice setup.
IDirectPlayVoiceTest* pVoiceTest = NULL; . . . // Create the IDirectPlayVoiceTest Object hr = CoCreateInstance(CLSID_DirectPlayVoiceTest, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlayVoiceTest, (LPVOID*) &pVoiceTest ); guidPlayback = DSDEVID_DefaultVoicePlayback; guidCapture = DSDEVID_DefaultVoiceCapture; hr = pVoiceTest->CheckAudioSetup(&guidPlayback, &guidCapture, NULL, DVFLAGS_QUERYONLY); if( hr == DVERR_RUNSETUP) { // The test has not been run yet. hr = pVoiceTest->CheckAudioSetup(&guidPlayback, &guidCapture, hwnd, DVFLAGS_ALLOWBACK ); } else if( FAILED( hr)) { /* Handle Errors */ } else { // The test has been passed, proceed. } . . .
All clients that want to participate in the voice session must connect to the session, including the host. The first step in connecting to a voice session is to create and initialize a voice client object (CLSID_DirectPlayVoiceClient).
Once you have created the object, initialize it by calling IDirectPlayVoiceClient::Initialize. Pass this method a pointer to your voice callback message handler. This message handler receives voice-related messages from DirectPlay Voice during the voice session. You must also pass the method a pointer to an IDirectPlay8Peer interface. The DirectPlay object that exposes this interface must be either connected to or hosting a session before you call IDirectPlayVoiceClient::Initialize.
The following excerpt from the tutorial sample illustrates how to create and initialize a voice client object.
// Create the IDirectPlayVoiceClient object. IDirectPlayVoiceClient* g_pVoiceClient = NULL; . . . hr = CoCreateInstance(CLSID_DirectPlayVoiceClient, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlayVoiceClient, (LPVOID*) &g_pVoiceClient ); // Initialize the object. hr = g_pVoiceClient->Initialize(g_pDP, DirectVoiceClientMessageHandler, NULL, 0, 0 );
Once the DirectPlayVoice client object is created and initialized, you can connect your client to the voice session by calling IDirectPlayVoiceClient::Connect. You need to pass two structures to this method: DVSOUNDDEVICECONFIG and DVCLIENTCONFIG. The DVSOUNDDEVICECONFIG structure contains information about the sound device configuration. The DVCLIENTCONFIG structure is used to configure run-time parameters. Once you have initialized the structures, connect to the voice session by passing the structures to IDirectPlayVoiceClient::Connect.
The following excerpt from the tutorial sample illustrates how to initialize a DVSOUNDDEVICECONFIG structure. In this example, the default voice capture device and default voice playback device are used for audio capture and playback. Additionally, this example enables automatic microphone selection.
ZeroMemory(&dvSoundDeviceConfig, sizeof(DVSOUNDDEVICECONFIG)); dvSoundDeviceConfig.dwSize = sizeof(DVSOUNDDEVICECONFIG); dvSoundDeviceConfig.dwFlags = DVSOUNDCONFIG_AUTOSELECT; dvSoundDeviceConfig.guidPlaybackDevice = DSDEVID_DefaultVoicePlayback; dvSoundDeviceConfig.lpdsPlaybackDevice = NULL; dvSoundDeviceConfig.guidCaptureDevice = DSDEVID_DefaultVoiceCapture; dvSoundDeviceConfig.lpdsCaptureDevice = NULL; dvSoundDeviceConfig.hwndAppWindow = GetConsoleHwnd(); dvSoundDeviceConfig.lpdsMainBuffer = NULL; dvSoundDeviceConfig.dwMainBufferFlags = 0; dvSoundDeviceConfig.dwMainBufferPriority = 0;
The following excerpt from the tutorial sample illustrates how to initialize a DVCLIENTCONFIG structure. In this example the system is configured for automatic voice activation and automatic gain control.
dvClientConfig.dwSize = sizeof(DVCLIENTCONFIG); dvClientConfig.dwFlags = DVCLIENTCONFIG_AUTOVOICEACTIVATED | DVCLIENTCONFIG_AUTORECORDVOLUME; dvClientConfig.lRecordVolume = DVRECORDVOLUME_LAST; dvClientConfig.lPlaybackVolume = DVPLAYBACKVOLUME_DEFAULT; dvClientConfig.dwThreshold = DVTHRESHOLD_UNUSED; dvClientConfig.dwBufferQuality = DVBUFFERQUALITY_DEFAULT; dvClientConfig.dwBufferAggressiveness = DVBUFFERAGGRESSIVENESS_DEFAULT; dvClientConfig.dwNotifyPeriod = 0;
Before transmitting audio to other voice session clients you must first create a transmission targets list that specifies who should receive audio transmissions. You can send audio to any combination of individual players and/or groups of players. Sending to a group allows you to reach multiple players with a single send. Each player or group is identified by a DVID value, which corresponds to equivalent DirectPlay 8 DPNID value. The transmission targets list may contain up to 64 players and/or groups. If no targets are specified, no audio data is transmitted.
If voice activation is enabled, voice transmission begins when the voice activation module detects speech activity. If voice activation is disabled voice transmission begins when valid set of targets is specified.
The following excerpt from the tutorial sample illustrates how to set a client's transmission targets to all players in the voice session.
DVID dvid = DVID_ALLPLAYERS; hr = g_pVoiceClient->SetTransmitTargets(&dvid, 1, 0 );
Once the session is over, clients, including the host, must shut down their voice client objects. To do so, the client must disconnect from the DirectPlay Voice session by calling IDirectPlayVoiceClient::Disconnect. They can then release the voice client object.
A voice session host terminates a voice session by calling IDirectPlayVoiceServer::StopSession. The session host must then release the DirectPlay Voice server object.
The following excerpt from the tutorial sample illustrates how to terminate a voice session and shut down the voice server and client objects.
// Shut down the voice client object. hr = g_pVoiceClient->Disconnect( 0 ); g_pVoiceClient->Release(); // Terminate the voice session. hr = g_pVoiceServer->StopSession( 0 ); g_pVoiceServer->Release();