Voice 2 SDK
From GameSpy SDK
Voice 2 SDK
Overview
The GameSpy Voice SDK 2 (GV) is a library that facilitates in-game voice communication between players. Someone with little or no voice experience can easily use GV without having to learn about the details of voice capture, playback, or encoding.
GV is simple enough to be easily and quickly added to an application, while also being powerful and flexible enough to fit within virtually any networking architecture. GV is also extremely efficient in its use of memory, bandwidth, and processor time.
GV delivers optimal performance, in a simple API, making it as easy as possible to add to any application.
Much of the power of GV comes from the fact that it does not impose any networking constraints on the application. It captures audio, encodes it, then passes it to the application. The application is responsible for routing the encoded audio to its final destination(s), most likely over its existing game networking. At the destination, the application gives the encoded audio back to GV, which then decodes it, mixes it into an audio stream, and plays it.
GV is written in standard ANSI C and can be used for Win32 or PS2 applications. Dedicated servers can be run on any platform. Just include all of the source files in your project, and you can start talking over the Internet.
GV relies on system-specific hardware libraries to interface with audio devices, and it uses freely available codec (compression and decompression) libraries to handle the actual voice compression. See the Requirements section of this document for futher information on these libraries.
The SDK package also includes a test app and a sample app. The test app shows a basic C implementation of the SDK. It will help in understand the SDK's core functionality and it will also provide you with a baseline implementation for testing the SDK on your target system. The sample app is written using Win32 and MFC. It shows a sample graphical implementation of the SDK. See the Samples section of this document for further information on the sample and test apps.
The rest of this document presents a simple set of instructions for using GV. See the reference documentation for further details.
File Manifest
The following files should be included with this package. If any of the files are missing, please contact devsupport@gamespy.com.
- File
- Description
- gv.h
- GV header (all user functions are prototyped here)
- gvCodec.c,h
- Encryption and decryption of audio frames
- gvCustomerDevice.c,h
- Custom capture/playback device interface
- gvDevice.c,h
- Device interface
- gvDirectSound.c,h
- DirectSound interface (only used with Win32)
- gvFrame.c,h
- Common code for handling audio frames
- gvLogitechPS2Codecs.c,h
- Interface to Logitech's PS2 voice codec library (only used with PS2)
- gvMain.c,h
- The main entry point for most GV functionality
- gvPS2Audio.c,h
- Interfaces with hardware-specific PS2 libraries (only used with PS2)
- gvPS2Eyetoy.c,h
- Eyetoy interface (only used with PS2)
- gvPS2Headset.c,h
- USB audio device (headset/microphone/speakers) interface (only used with PS2)
- gvPS2Spu2.c,h
- SPU2 (system/TV audio) interface (only used with PS2)
- gvSource.c,h
- Common code for handling audio sources
- gvSpeex.c,h
- Interface to the Speex codec library (only used with Win32)
- gvUtil.c,h
- Miscellaneous common code
- nonport.c,h
- Platform-specific code
- darray.c,h
- Code for managing dynamic arrays
- /Voice2Test/
- ANSI-C sample
- /Voice2BuddyMFC/
- Windows MFC sample
Requirements
GV relies on a few other libraries provided by both GameSpy and 3rd-parties. The 3rd party libraries are all freely available. See below for futher information, depending on your platform.
Note that you can use custom hardware devices and/or custom codecs instead of those provided by default. If doing so, you will not need the corresponding 3rd-party libraries.
Common Code
GV uses the GameSpy Common Code package, the latest version of which is available from http://www.gamespy.net. The Voice 2 zip and the Common Code zip should both be extracted into the same directory. This will result in the GV files being in a Voice2 subdirectory, and the common code will be in the directory's root.
Win32
GV supports the Win32 platform by using the DirectSound library (part of DirectX) for interfacting with voice capture and playback devices and the Speex library for compression and decompression of audio.
DirectX
The latest version of DirectX can be downloaded from http://msdn.microsoft.com/downloads/. The site also has information on redistributing DirectX with your application. GV was developed with DirectX 9.0. Though it should work with previous versions it is highly recommended that you use the latest version of DirectX. Once DirectX has been installed, ensure that its include folder is in your include path and its lib folder is in your link path. You will also need to setup any application that uses GV to link with dsound.lib and dxguid.lib.
Speex
Speex is a freely available, open source audio codec (http://speex.org/). Speex licensing information is available from http://www.xiph.org/licenses/bsd/speex/.
Please review the licensing information carefully before using the Speex library.
GV currently uses version 1.0.5 of Speex, which can be downloaded from http://downloads.us.xiph.org/releases/speex/speex-1.0.5.tar.gz. In order for the sample and test applications to function, you will need to extract the tar into the Voice2 directory. This will create a speex-1.0.5 subdirectory in the Voice2 directory. The SDK itself does not rely on Speex being in that directory, so you can put it anywhere you would like for your own application. However, wherever you put it, its include folder must be in your include path. You will also need to add all of the source files in the libspeex directory to your project, with the exception of any test*.c files.
When compiling the Speex libraries at the default MSVC++ warning level, there are a number of warnings due to missing explicit casts. It is fairly easy to disable these warnings using the following line:
#pragma warning ( disable : 4244 4305 4100 4127 )
To disable the warnings, put the line in all of the files which generate the warnings, or in a header which can then be easily included in any files which generate the warnings.
PS2
GV supports PS2 devices through three libraries provided by Logitech specifically for the PS2. lgAud is used to interface with USB audio devices, lgVid is used to interface with the microphone on EyeToy devices, and lgCodec is used to compress captured audio. lgAud and lgCodec are available at https://www.ps2-pro.com/projects/lgaud/, and lgVid is available at https://www.ps2-pro.com/projects/liblgvid/. SPU2 output (direct system output, generally through the TV) is also supported , allowing users without headsets or USB speakers to hear other users.
PS2 applications are required to load certain IRX modules to use the various hardware devices. These are lgaud.irx and usbd.irx for lgAud, lgvid.irx for lgVid, and libsd.irx and sdrdrv.irx for SPU2. Sample loading code is available in load_voice_modules() in ps2common.c. Applications must also link with liblgaud.a for lgAud, liblgvid.a for lgVid, and libscf.a and libsdr.a for SPU2. To exclude support for particular hardware device types, see the Advanced section of this document.
lgAud
GV was developed with version 1.10.001 of lgAud. A license agreement for lgAud, liblgaud_license_agreement.pdf, is in the tar.
Please read the licensing agreement carefully before using the library.
See the ReadMe.1st in the tar for information on installing lgAud. The lgAud documentation provides details on lgaud.irx loading parameters.
lgVid
GV was developed with version 1.07.008 of lgVid. The license agreement for lgVid can be found at the end of lgvid.pdf, which can be found in the tar's doc directory. GV now supports version 2.01.003. If you wish to support the older library, please use the following preprocessor directive in your project: GVI_LGVID_OLD_DRIVER
Please read the licensing agreement carefully before using the library.
See the README.1st in the tar for information on installing lgVid. The lgVid documentation provides details on lgvid.irx loading parameters.
lgCodec
GV was developed with version 1.00.002 of lgCodec. See the README in the tar for information on installing lgCodec. The bottom of the README also contains a "TERMS OF USE" section which you should read carefully before using the library. Any program that uses GV will need to link with liblgcodec.lib.
Note that because the PS2 uses a different codec library than the Win32 version of GV, users on the two different platforms cannot talk with each other. However they can be made compatible by using a custom codec.
PS3
Currently the SDK supports any PS3 compatible USB/Bluetooth Headset. Future versions will have support for other devices like the Eye Toy. Speex is currently the only codec supported on the PS3. Refer to the instructions in the Win32 section for obtaining speex.
Linux, Mac, etc.
Only Win32 and PS2 hardware devices are included in GV at present. However, because applications provide their own routing between users, dedicated servers do not need to use the GV SDK, which allows them to be run on any platform, such as Linux.
Also, because GV supports custom devices and codecs, clients can be written for other platforms as well, as long as the application provides the interface to the hardware and the codec.
Sample
The sample app included with this SDK, Voice2BuddyMFC, uses a few of the other GameSpy SDKs. These are GameSpy Transport 2 (GT2), NAT Negotiation, and GameSpy Presence (GP), which are all included under the same license as GV. If you do not have these SDKs, you can download them from http://gamespy.net/secure/download/. Extract them into the same root as the common code, just as you did with the GV code.
Samples
The GV package includes a test app and a sample app. The test app, Voice2Test, is a very simple implementation of the SDK, while the sample app, Voice2BuddyMFC, is designed to show a graphical implementation of the SDK.
Voice2Test
The test app is written in ANSI C, and it can be used as a simple test to make sure GV works on your system. It takes as its only argument a dotted IP address, to which it will send all captured voice packets. It also opens a socket on a fixed port and will play any voice packets it receives on the socket. The app will run until either Q is pressed on the PC, or the X is pressed on the PS2. To stop capture use V or the circle button, and to start capture again use C or the square button.
There are a set of defines at the top of the source file, Voice2Test.c, which can be used to change various aspects of the test app. LOCAL_ECHO can be set to 1 to turn local on (you will hear your own voice). RAW_CODEC can be set to 1 to switch on the use of a custom codec which does not actually do any compression (but can be easily modified to test a codec). SHOW_TALKERS can be set to 1 to have the app print out the list of talkers once a second. SHOW_VOLUME can be set to 1 to have the app print out the volume of all captured audio packets. CAPTURE_THRESHOLD sets the capture threshold. A value of 0.0 means there will be no threshold, anda value greater than 0.0 and less than or equal to 1.0 will set the threshold to that value.
Voice2BuddyMFC
The sample app is written with MFC and is designed to show a graphical implementation of GV. This sample app may be used to establish voice communication with a single member of your buddy list. (Your game, of course, is not limited to voice communication with a single player.)
Voice2BuddyMFC makes use of three other GameSpy SDKs.
- Presence SDK
- Used to retrieve buddy list.
- Nat Negotiation SDK
- Used to connect to buddies.
- Transport 2 SDK
- Used to manage connections to buddies and voice data transmission.
Note: These SDKs are not required to use GV. They are used in the sample application to simplify the matchmaking process. In a typical game a networking channel has already been established between players (or from host to player). Voice data may be sent using the network channel used for game data.
The Voice2Buddy sample has three major stages. These stages are Setup, Login and VoiceSession.
Setup
The Setup stage consists of a dialog in which the user may select input and output devices as well as set the voice activation level. It is recommend that you expose a similar dialog in your application, as the user may have multiple input and output devices from which to select (e.g. a USB headset for voice communication and speakers connected to the sound card for game audio). DirectX 9 has a standard dialog which may be displayed.
Login
The Login dialog contains the standard fields used to log into the GameSpy ID system. (Email, nickname and password.) After logging in, the user will be presented with a list of buddies that they may invite to a voice session. The buddy may then accept the invitation which will result in the creation of the VoiceSession.
VoiceSession
The third stage, VoiceSession, is not a stage within the SDK. GV does not require the creation of a "channel" or "server". The receiving application simply decides, "Do I want to play Voice data from this person?".
In the sample, all voice data is immediately presented to the SDK for playback. If you wish to mute a user, simply discard the voice data instead of passing it into GV.
Terms and Concepts
This section will explain some of the concepts and terminology used by GV. Reading this section will greatly assist in your ability to understand the rest of this document.
Audio
Samples and Rates
One of the primary functions of GV is to capture and playback audio. Digital audio consists of a set of samples captured at a uniform rate. The samples form a waveform - in other words, each sample is the value of the waveform at a particular instance in time. The number of bits used to store the value of each sample determines the bit rate, and the number of samples stored in one second determines the sample rate. GV uses a sample rate of 8000 samples per second and a bit rate of 16 bits per sample, which are de facto standards for Internet voice applications. These rates are defined at the top of gv.h. Also, GV has a GVSample type which represents a single sample as a signed short.
Frames
GV typically deals with audio in units called frames. A frame of audio will always represent the same number of samples, and so it will always represent the same amount of time. Typically frames represent 160 samples, which is 20ms at the standard sample rate (160 samples / 8000 samples per second = 0.02 seconds = 20ms). The exact number of samples per frame depends on the codec, but almost all codecs use 160 samples per frame.
A frame can either be raw or encoded. A raw frame is an array of GVSamples which will be, at the standard frame size and bit rate, 320 bytes. The size, however, can vary with codecs that use other frame sizes. Raw frames are used with custom devices, custom codecs, and filters, as they need to deal with the raw, uncompressed audio. An encoded frame is a raw frame that has been compressed by the codec. It is stored in an array of bytes, using the GVByte type (an unsigned char). The size of a compressed frame depends on the codec and can vary from less than 10 bytes to 300 or more bytes, although it will typically be in the range of about 15 to 35 bytes. Encoded frames are used with custom codecs, audio capture, and audio playback. GV provides functionality for obtaining the current codec's samples per frame (number of samples in a raw frame) and encoded frame size (number of bytes in an encoded frame).
Packets
A packet represents a set of one or more encoded frames stored in a contiguous block of memory (an array of GVBytes). Therefore the length of a packet will always be a multiple of the size of an encoded frame. When GV passes the application audio that it has captured and compressed, it does so in the form of a packet. This packet is ready to be sent over the Internet (along with some other meta-data described below). When it is received by another player, it can be passed directly to the SDK (along with the meta-data) to be played.
Frame Stamps
A frame stamp stores a value that GV uses to synchronize packets for playback. When a packet is captured, GV will pass a frame stamp to the application along with the packet. The frame stamp marks the time at which the first frame in the packet was captured. If the packet is sent over the Internet, the frame stamp should be sent along with it. On playback, the frame stamp must be passed back to GV along with the packet. A frame stamp is represented by the GVFrameStamp type, which is 2 bytes.
Sources
Sources are used by GV to uniquely identify users, which enables it to synchronize multiple incoming streams of audio. Whenever a packet is passed from the application to GV to be played, a source must be passed along with the packet. This allows GV to identify who spoke that particular packet. A source is represented by the GVSource type. By default this is an int, however the GV_CUSTOM_SOURCE_TYPE define can be used to have a GVSource represent any arbitrary type, such as a sockaddr_in (Internet IP and port). The source can be a player's index, connection ID, Internet address, or any data that uniquely identifies a talker. The source may need to be sent along with the packet.
One example of this would be a client-server application in which all of the packets pass through a server before being sent to their final destination. For a peer-to-peer game, however, in which packets are received directly from the player who generated them, the connection on which the packets were received could be used to identify the source.
Local Echo
Local echo is what happens when you locally playback any captured packets. It results in the user being able to hear himself talking, although there is a slight delay due to the fact that the audio is being processed by both GV and the application. Local echo is very simple with GV - just pass captured packets directly to a playback device.
Devices
A device is a particular piece of hardware on the system that can be used by GV to capture and/or playback audio. On some platforms, such as Win32, a device will only do one thing - capture or playback. Even though a sound card may be one physical piece of hardware, it is represented through GV as two separate devices, one which handles capture and one which handles playback. This is a consequence of how DirectSound works. On other platforms, such as the PS2, a single device may do both capture and playback, although it is still possible that it can only do one or the other. For example headsets will support both capture and playback, while EyeToy cameras can only be used for capture.
GV supports mixing and matching of devices. For example, a USB headset could be used for voice capture, a sound card outputting to speakers could be used for general game audio, and the headset could be used for voice communication from other players. There is nothing preventing one or more devices from being used for capture and a different set of one or more devices from being for playback. These devices can be a mix of devices supported internally and custom devices.
Device IDs
A device is uniquely identified by a GVDeviceID. This type is represented by a GUID on Win32 and by an int on the PS2. GV can provide you with a list of devices available on a particular system, and each device will have its own GVDeviceID. When you initialize a device through GV, you use the GVDeviceID to tell GV which device you are attempting to initialize.
Devices
When the application initializes a device through GV, it obtains a reference to the device as a GVDevice value. The application will need to use the GVDevice handle whenever it does anything related to the device. GV will manage the device until the application frees it (through GV). After it has been freed the GVDevice handle will no longer be valid and can be set to NULL, which will never represent a valid GVDevice.
Device Types
Any particular device is capable of capture, playback, or both. When an operation needs to specify that it applies to only capture, only playback, or both capture and playback, it uses the GVDeviceType type to specify. The typical values are GV_CAPTURE, GV_PLAYBACK, and GV_CAPTURE_AND_PLAYBACK. GV_CAPTURE and GV_PLAYBACK are bitfields, so GV_CAPTURE_AND_PLAYBACK is equal to (GV_CAPTURE|GV_PLAYBACK). If a particular GVDeviceType value is 0, that means that it does not apply to capture or playback. When the available devices are listed, there is a GVDeviceType for each field that specifies if it is the system default for capture, playback, both, or neither. If it is neither, then its value is 0.
Custom Devices
GV supports the use of custom devices. This allows you to write your own audio device interface. You can use this to, for example, pass the playback audio through your own internal audio system instead of directly to the sound card. See the Advanced section of this document for futher information.
Codecs
GV uses codecs to compress and decompress audio.
Codecs
There is a set of default codecs available on each system, represented by the GVCodec type. These range from codecs that preserve audio quality at the expense of greater bandwidth usage to lower audio quality codecs that use less bandwidth. GV does not support the use of multiple codecs - after GV is initialized, but before any devices are initialized, the application must set the codec to be used. The codec can then not be changed once devices have been initialized.
GV can only handle packets that were compressed using the codec it has been set to use. It is the applications responsibility to ensure that GV is not given packets that were compressed using another method.
Custom Codecs
GV supports the use of a custom codec, which allows developers to use any desired codec with GV. See the Advanced section of this document for futher information.
Implementation
This section will explain all of the basic operations that you can perform using GV. After reading this section you should be able to write an application that can capture and playback voice audio.
Initialization and Cleanup
Before doing anything else with GV, you must call gvStartup. On Win32 the prototype is:
GVBool gvStartup(HWND hWnd);
On other platforms the prototype is:
GVBool gvStartup(void);
The function does any necessary internal initialization. It will return GVFalse in case of an error initializing. The HWND passed to the Win32 version is the handle for the application's main window. This can be NULL if the application does not have a main window.
To cleanup the SDK, use gvCleanup:
void gvCleanup(void);
The function will do any necessary internal cleanup. GV cannot be used again until gvStartup is called.
Setting A Codec
The first thing to do after initializing the SDK is to set the codec you would like to use. The codec cannot be changed while any devices are initialized, so the codec an application will typically just set the codec once, when it starts using voice. For information on using a custom codec, see the Advanced section of this document.
GVBool gvSetCodec(GVCodec codec);
The codec must be one of the following values:
- GVCodecSuperHighQuality
- GVCodecHighQuality
- GVCodecAverage
- GVCodecLowBandwidth
- GVCodecSuperLowBandwidth
The codecs are arranged in order of descending quality and bandwidth. In other words, the codecs higher up on the list are of higher audio quality and use more bandwidth, while the codecs lower on the list are of lower audio quality and use less bandwidth.
The GVCodecAverage codec produces good quality audio with a reasonable bandwidth cost. It is generally the best codec to use, and you should only use another codec if you are restricted to lower bandwidth or need high quality audio.
The particular stats for a codec can be obtained using gvGetCodecInfo.
void gvGetCodecInfo(int * samplesPerFrame, int * encodedFrameSize, int * bitsPerSecond);
This function returns the samples per frame, encoded frame size (in bytes), and bits per second for the currently selected codec. Note that the bits per second doesn't take into account any overhead, such as the need to transmit a frame stamp value with each packet. It is based only on the encoded frame size and the number of frames per second.
It is important that all users have the same codec set. If users attempt to communicate using different codecs, the result will most likely be unpredictable audio.
Listing Devices
The application uses gvListDevices to get a list of the devices available on the system.
int gvListDevices(GVDeviceInfo devices[], int maxDevices, GVDeviceType types);
Pass in an array of GVDeviceInfos, which will be filled in by the function, the number of elements in the array, and the types of devices that you want to be listed. You can request capture devices with GV_CAPTURE, playback devices with GV_PLAYBACK, or capture and playback devices with GV_CAPTURE_AND_PLAYBACK. For GV_CAPTURE_AND_PLAYBACK, it can list capture devices, playback devices, and devices that support both capture and playback. The function will return the number of devices that it put in the list, which may be less than the value that was passed in for maxDevices. If 0 is returned, then either there was an error listing devices or no devices were found. For each device that is listed, a GVDeviceInfo will be filled in with details on the device.
typedef struct
{
GVDeviceID m_id;
char m_name[GV_DEVICE_NAME_LEN];
GVDeviceType m_deviceType;
GVDeviceType m_defaultDevice; // not supported on PS2
GVHardwareType m_hardwareType;
} GVDeviceInfo;
The m_id is used if you initialize this device with gvNewDevice. The m_name contains a user-readable name for the device. The m_deviceType indicates if this device is for capture, playback, or both capture and playback. The m_defaultDevice indicates if this device is the default capture device, default playback device, both, or neither. If neither, the value will be 0 (this will always be the case on the PS2, as it does not have a default device indicator). The m_hardwareType is used to give the application more information about the device's actual hardware. Under Win32 this will always be GVHardwareDirectSound (no further information is available). With the PS2 this will be GVHardwarePS2Spu2 for the SPU2 device, GVHardwarePS2Headset for USB headsets, GVHardwarePS2Microphones for USB microphones, GVHardwarePS2Speakers for USB speakers, or GVHardwarePS2Eyetoy for Eyetoy devices. The SPU2 device will always be the first device listed on the PS2.
Note that on the PS2, there is a small delay between when the IRX modules are loaded and when USB devices are actually detected. This is why Voice2Test pauses for one second right before calling gvListDevices. However in a real application this pause won't be necessary, as long as the IRX modules are loaded well before the user would get to a voice configuration screen.
Creating and Freeing Devices
To initialize a device, you use gvNewDevice.
GVDevice gvNewDevice(GVDeviceID deviceID, GVDeviceType type);
You pass the function the GVDeviceID for the device that you want to initialize. Also pass in a type, which will tell GV if you want to initialize the device for capture, playback, or both. A device that supports both capture and playback may be initialized for just one or the other (or both). If the device was successfully initialized, a handle to the device will be returned. If there was an error setting up the device, NULL will be returned.
With Win32, there are two globally defined default devices which you can use. The GVDeviceIDs for these are GVDefaultCaptureDeviceID and GVDefaultPlaybackDeviceID. So, instead of calling gvListDevices, you can simply use the defaults. However this is only recommended during development - for release your application should allow the user to choose which device to use. Also note that the default device IDs will not match the GVDeviceIDs of the default devices listed by gvListDevices, although they will represent the same physical hardware.
With the PS2, the SPU2 device is globally defined as GVPS2Spu2DeviceID. This allows you to use the SPU2 device for output without having to list devices. Again, this is only recommend for use during development. For release the application should list devices and either allow the user to choose a device, or use the SPU2 if no other playback devices are found. Unlike the Win32 default devices, GVPS2Spu2DeviceID will match the GVDeviceID of the SPU2 device (always the first in the list).
When you are done using the device, use gvFreeDevice so that GV can clean it up.
void gvFreeDevice(GVDevice device);
Once a device has been freed it can no longer be used. After calling this function you should set the variable in which you stored the device handle to NULL.
Starting and Stopping Devices
Once a device has been initialized, it is ready to start capturing or playing audio. After a capture device is started, it will begin capturing audio and passing it back to the application. After a playback device is started, it will play any audio that the application passes it. To start a device, use gvStartDevice.
GVBool gvStartDevice(GVDevice device, GVDeviceType type);
The device parameter is the handle of the device to start. The type parameter specifies if the device should start capturing (GV_CAPTURE), playing (GV_PLAYBACK), or capturing and playing (GV_CAPTURE_AND_PLAYBACK). For devices that support both capture and playback, each can be started independently. The function will return GVTrue if the device was started successfully, and it will return GVFalse if there was an error.
When you want a device to stop capturing or playing, use gvStopDevice.
void gvStopDevice(GVDevice device, GVDeviceType type);
When a capture device is stopped, it will stop passing captured audio to the application. When a playback device is stopped, it will stop playing audio. For devices that support both capture and playback, each can be stopped independently.
Use gvIsDeviceStarted to check if a device has been started or not.
GVBool gvIsDeviceStarted(GVDevice device, GVDeviceType type);
Capturing Packets
Once a capture device has been started, it will immediately start filling an internal buffer (which may or may not be on the actual sound hardware) with audio data. The application calls gvCapturePacket to take captured audio out of the buffer.
GVBool gvCapturePacket(GVDevice device, GVByte * packet, int * len, GVFrameStamp * frameStamp, GVScalar * volume);
The first parameter is a handle to the capture device. The packet parameter points to a block of memory that must be large enough to hold at least one encoded frame (gvGetCodecInfo can be used to get the size of an encoded frame). The function will fill this memory with as many encoded frames as it can. The len parameter must point to an int which is set to the maximum number of bytes that can be written to the block of memory pointed to be the packet parameter. After the function returns, if it was successful, len will point to the number of bytes that were written to the block of memory. Also, the frameStamp parameter will point to the frame stamp for the captured packet and the volume parameter will point to the peak volume for the audio in the frame. The volume ranges from 0.0 to 1.0, and it can be used to power a per-player graphic voice activity meter. If the function succeeds in getting a packet and encoding it into the provided memory block, it will return GVTrue. If it returns GVFalse, then there was either no audio data available to capture or some sort of error capturing the audio.
Once a packet has been captured, it is ready to be sent to other players. It is the application's responsibility to route the packet to its final destinations. It should also route the packet's frame stamp along with the packet. The application can choose to not send the packet anywhere, in effect muting the player. It can pass it back to the SDK to be played locally. It can send the packet directly to one or more other players for them to play. It can send the packet to a server which would then decide, possibly based on information such as players' positions or teams, who the packet should be sent to.
You can check how many bytes are available for capture before calling gvCapturePacket, using gvGetAvailableCaptureBytes.
int gvGetAvailableCaptureBytes(GVDevice device);
Simply provide the handle to a capture device and it will return the number of bytes that are currently available for capture. To determine the number of encoded frames that this is, divide the return value by the number of bytes in an encoded frame (which you can get with gvGetCodecInfo). Note that even if there are bytes available, gvCapturePacket may return GVFalse. This could happen if a capture threshold has been set, and the voice audio does not cross the threshold. In that case GV would skip over that captured audio, and its bytes would no longer count as available bytes.
Playing Packets
When the application receives a packet that it wants to play, it should pass it to gvPlayPacket.
void gvPlayPacket(GVDevice device, const GVByte * packet, int len, GVSource source, GVFrameStamp frameStamp);
The first parameter tells GV which device to use for playing the packet. The second parameter is pointer to the packet. And the next three parameters provide GV with the packet's length, the source that originally spoke the audio, and the packet's frame stamp. GV will schedule the packet to be played soon. A short delay is added which enables the packets to be synchronized before they are played, allowing for variations in Internet transit time and application timing. The packet is synchronized based on its source, so you must ensure that each unique talker has his own unique source, and all packets are played using the correct source. Note that the same packet can be played on multiple playback devices.
gvPlayPacket only schedules a packet to be played in the future. The application must also call gvThink on a regular basis to ensure that the packets are actually played.
void gvThink(void);
gvThink will check, for each device, how much space has become available for writing in the playback buffer (which may or may not be on the actual sound hardware). It will then check to see if the device has any sources that have audio which should be played during the time period that the newly available space represents. If so, the audio will be mixed into the playback buffer, and the audio will then be played when the playback position reaches that point in the buffer. If the playback device is stopped before that happens, then the audio will not be played, even if the device is then restarted.
gvThink should generally be called once for each run through the application's main loop, or approximately every 10-30ms. If it is not called often enough, the playback position will reach a point in the playback buffer that GV has not yet had a chance to mix to, resulting in an audible skipping effect.
Local Echo
To get local echo with GV, simply pass captured packets to a playback device. You'll need to ensure that you pass gvPlayPacket a GVSource that will not conflict with any of the remote talkers' sources. There will be a slight delay due to the fact that the audio is being processed by both GV and the application. Local echo will generally not be desired for regular use, but is very useful when a user is selecting and configuring devices.
Talking Sources
To determine if a particular source is currently talking, call gvIsSourceTalking.
GVBool gvIsSourceTalking(GVDevice device, GVSource source);
It will return GVTrue if the source is talking on the specified device. To get a list of all of the sources that are currently talking on a particular device, use gvListTalkingSources.
int gvListTalkingSources(GVDevice device, GVSource sources[], int maxSources);
The maxSources parameter should be the number of sources that can be stored in the sources array. The function will return the number of sources that were talking on the device, and it will store their GVSources in the sources array. 0 will be returned if there are no sources talking.
GV has a hardcoded limit that does not allow more than 8 sources to talk simultaneously. This allows it to preallocate memory that it needs to store for a source while it is talking. A user will typically not understand more than 2 or 3 users talking simultaneously, so the limit should be high enough. If the application attempts to play audio from more than 8 sources at a time, audio for the 9th source will be automatically dropped.
Muting Sources
All you need to do to mute a source in GV is ignore packets that originate with that source. Optionally you can send a message to the source telling him to stop sending you packets, which will cut down on network bandwidth. But the only requirement for muting is that the packets are not played.
Threshold
After a capture device has been started, it will continually generate packets and pass them to the application through gvCapturePacket. If a capture threshold is set on a capture device, then a packet will only be passed to the application if its peak volume is at least as high as the capture threshold. Use gvSetCaptureThreshold to set the threshold.
void gvSetCaptureThreshold(GVDevice device, GVScalar threshold);
The range for threshold is 0.0 to 1.0. A value of approximately 0.10 to 0.15 will generally work well, although ideally the user should have a way to configure the threshold. GV will continue passing packets to the application for about half a second after the peak volume drops below the threshold. This helps to catch speech in which the level trails off or has a small dip. The default threshold is 0.0, which means that all audio will be considered over the threshold and will be captured. To remove a threshold that has been set, simply call this function again with a threshold of 0.0.
Use gvGetCaptureThreshold to get the current value of threshold.
GVScalar gvGetCaptureThreshold(GVDevice device);
Volume
Use gvSetDeviceVolume to apply a volume control to a capture or playback device.
void gvSetDeviceVolume(GVDevice device, GVDeviceType type, GVScalar volume);
The volume range is 0.0 to 1.0. The type parameter controls if this gets set as a capture volume (GV_CAPTURE), a playback volume (GV_PLAYBACK), or for both capture and playback (GV_CAPTURE_AND_PLAYBACK). To get the volume use gvGetDeviceVolume.
GVScalar gvGetDeviceVolume(GVDevice device, GVDeviceType type);
It will return the volume in a range from 0.0 to 1.0. For a device that supports both capture and playback, this function can only be used to get either the capture volume or the playback volume, not both.
Advanced Implementation
This section describes some of the more advanced functionality of GV.
Detecting Devices Being Unplugged
A gvUnpluggedCallback allows an application to know if a device is unplugged or otherwise stops working.
typedef void (* gvUnpluggedCallback)(GVDevice device);
The callback function is passed the handle to the device that was unplugged. The device will be freed by GV immediately after this function returns.
Use gvSetUnpluggedCallback to set the unplugged callback for a particular device.
void gvSetUnpluggedCallback(GVDevice device, gvUnpluggedCallback unpluggedCallback);
Custom Device
A custom device allows an application to supply its own audio hardware interface. To create a custom device, use gvNewCustomDevice.
GVDevice gvNewCustomDevice(GVDeviceType type);
Specify if the custom device will handle capture (GV_CAPTURE), playback (GV_PLAYBACK), or both capture and playback (GV_CAPTURE_AND_PLAYBACK). The function will return a GVDevice if it is successful, or NULL if it cannot create the device. When an application has finished using a custom device, it should call gvFreeDevice to free its resources.
All custom devices must interface with GV using the defined sample rate (GV_SAMPLES_PER_SECOND) and bit rate (GV_BITS_PER_SAMPLE). The default sample rate is 8000Hz, and the default bits per sample is 16.
Custom devices will process frames of audio as quickly as you want. In other words, the application controls a custom device's clock rate. Because of this, you have to be sure that you request or provide samples at the correct sample rate. In other words, you should be going through approximately 8000 samples per second. Timing variations in the short run are not a problem (for example, if the system's clock is only accurate to within 30ms), but over time the sample rate must average out to approximately the correct rate (in a minute you should have requested approximately 60 * 8000 = 480000 samples). GV compensates for drift between clocks (known as clock skew), however with a custom device the application is still responsible for synching with the system clock.
If you are not using GV's default audio device support, you can define GV_NO_DEFAULT_HARDWARE. This will remove all internal references to audio hardware. That way, you don't need to setup DirectX or lgAud if you won't be using it. If GV_NO_DEFAULT_HARDWARE is defined, then gvListDevices and gvNewDevice will have no purpose, so they will not be included and cannot be called.
Custom Playback
With a custom playback device, you are taking over the function of a hardware sound device. GV delivers a mixed audio stream, which you can do whatever you want with, such as playing through a sound device, saving to disk, or displaying visually.
You provide a custom playback with packets the same way you would any other playback device, by using gvPlayPacket. You can also use all of the other standard playback device functions, such as gvIsSourceTalking, and gvSetDeviceVolume. However you do need not call gvThink with custom devices. Instead you use gvGetCustomPlaybackAudio.
GVBool gvGetCustomPlaybackAudio(GVDevice device, GVSample * audio, int numSamples);
The numSamples parameter specifies how many samples can be written to the memory pointed to by audio. numSamples must be a multiple of the samples per frame for the current codec (which you can check using gvGetCodecInfo). This is because GV mixes audio a frame at a time. The function will return GVTrue if it was able to fill the memory with audio.
Custom Capture
A custom capture device allows you to provide your own sound source in GV. It does this by replacing gvCapturePacket with a function that both provides GV with captured audio and then gets a packet containing that audio. This function is gvSetCustomCaptureAudio.
GVBool gvSetCustomCaptureAudio(GVDevice device, const GVSample * audio, int numSamples, GVByte * packet, int * packetLen, GVFrameStamp * frameStamp, GVScalar * volume);
The audio parameter points to the incoming audio stream, and numSamples is the number of samples to capture. numSamples must be a multiple of the samples per frame for the current codec (which you can check using gvGetCodecInfo). The audio will be encoded into a packet, which will be stored at the memory pointed to by the packet parameter. packetLen must be large enough to hold all of the encoded frames supplied by the audio parameter. If the function successfully encodes the audio into the packet, it returns GVTrue. It will return GVFalse if a threshold is set and the audio's peak volume did not cross the threshold. If the function succeeds, packetLen will store the number of bytes encoded into packet, frameStamp will point to the frame stamp for the packet, and volume will point to the peak volume for the packet.
Note that although the audio parameter is a const, if a capture volume or a capture filter is set on this device, then the memory will be modified. This is done to minimize the amount of memory and copying needed.
Custom capture devices can be used the same way as other capture devices. For example, you can use a capture threshold or set a volume. However there are two functions that are not support by custom capture devices: gvGetAvailableCaptureBytes and gvCapturePacket. That is because you are now providing the capture interface.
Custom Codec
An application can use a codec other than the ones provided with GV, by using a custom codec. To start using a custom codec, first fill in a GVCustomCodecInfo structure.
typedef struct
{
int m_samplesPerFrame;
int m_encodedFrameSize;
GVBool (* m_newDecoderCallback)(GVDecoderData * data);
void (* m_freeDecoderCallback)(GVDecoderData data);
void (* m_encodeCallback)(GVByte * out, const GVSample * in);
// decode must _add_ to, not set the output
void (* m_decodeCallback)(GVSample * out, const GVByte * in, GVDecoderData data);
} GVCustomCodecInfo;
m_samplesPerFrame is the number of samples that this codec expects in a raw (unencoded) frame of audio. This can be whatever value is used by the codec, however it is typically about 160 samples. m_encodedFrameSize is the number of bytes in an encoded frame of audio produced by this codec. The ratio of the samples per frame and encoded frame size is directly related to the codec's output stream bit rate.
m_newDecoderCallback is used to allocate a new decoder instance for each incoming source. Some codecs do not require this, and they should set the m_newDecoderCallback member to NULL. For codecs that do require per-source data, they should allocate a new decoder data state and set the data parameter to point to it, then return TRUE. If they cannot allocate a new decoder data, then they should return GVFalse.
The m_freeDecoderCallback is used to free decoder data allocated through m_newDecoderCallback. If a codec set m_newDecoderCallback to NULL, it should set m_freeDecoderCallback to NULL as well. Otherwise it should provide a function that frees the decoder data.
m_encodeCallback is used to encode data. The in parameter points to the input data, with is a single raw frame of samples. The number of samples passed to this function will always be m_samplesPerFrame. The out parameter points to the memory into which the callback should encode the input data. The memory will always be large enough to hold one frame of compressed audio, which will always be m_encodedFrameSize bytes long.
m_decodeCallback is used to decode data. The in parameter will point to an encoded frame of audio, which will be m_encodedFrameSize bytes long. The out parameter which will be large enough to hold m_samplesPerFrame samples of audio. The decoder data is provided for codecs that need it. The important thing to know with the decode callback is that it should not decode directly into the out buffer, but it should add to it. This allows GV to decode and mix at the same time, without having to decode into a temporary buffer which would then be mixed into the output stream.
Once you have filled in a GVCustomCodecInfo structure, use gvSetCustomCodec to set it as the codec. It will replace any codec that has been set with gvSetCodec.
void gvSetCustomCodec(GVCustomCodecInfo * info);
If you are not using GV's default codec support, you can define GV_NO_DEFAULT_CODEC. This will remove all internal references to the default codec. That way, you don't need to setup Speex or lgCodec if you won't be using it. If GV_NO_DEFAULT_CODEC is defined, then gvSetCodec will have no purpose, so it will not be included and cannot be called.
Filter
Filtering allows you to process, or just monitor, audio that has ben captured or is being played. A filter callback, prototyped as the gvFilterCallback type, is passed the device the filtering is happening on, the audio to filter, and the audio's frame stamp. The audio will always be a single raw frame of audio. Use gvGetCodecInfo to get the number of samples in a raw frame.
typedef void (* gvFilterCallback)(GVDevice device, GVSample * audio, GVFrameStamp frameStamp);
The callback can modify the audio in any way that it wants. However once the function returns it can no longer access the audio. For capture devices, audio will only be passed to the filter if it crosses the threshold (if one is set). For playback devices, audio is filtered after all of the sources have been mixed.
To set a filter on a device, use gvSetFilter.
void gvSetFilter(GVDevice device, GVDeviceType type, gvFilterCallback callback);
You can use the function to set a filter on any device, and to set it for capture or playback. A device can have only one capture filter at a time and only one playback filter at a time. To clear a filter, call this function with the callback set to NULL.
Device Wizard
With Win32 only, a device setup wizard is available if the user has DirectX 8 or greater. It can be instantiated for a pair of devices using gvRunSetupWizard.
GVBool gvRunSetupWizard(GVDeviceID captureDeviceID, GVDeviceID playbackDeviceID);
The function takes a capture device ID and a playback device ID. This function cannot be called successfully if these devices have been initialized with gvNewDevice - gvRunSetupWizard must be called first. The wizard will take over control of the program while it executes. It has the user speak into the capture device, and monitors the audio to automatically set system level capture and playback volumes. It will return GVTrue if the user successfully completes the wizard, GVFalse otherwise. If the wizard is successful, DirectX will store the results in the registry. Once it does this, gvAreDevicesSetup can be used to determine if the registry has information on the two specified devices. If so, it returns GVTrue, and the wizard does not need to be run again. If it returns GVFalse, then the wizard has not been run for the pair of devices.
GVBool gvAreDevicesSetup(GVDeviceID captureDevice, GVDeviceID playbackDevice);
Playback Delays
There are two delays that occur between when the application gives GV audio to play and the audio is actually heard. The first delay is a synchronization delay added automatically by GV. The second is a result of missing the audio into the hardware before it needs to play it, which ensures that the hardware always has audio ready to play, resulting in go audible gaps.
Synchronization Delay
The synchronization delay is controlled by the GVI_SYNCHRONIZATION_DELAY define at the top of gvSource.c. This controls how many milliseconds GV will wait before mixing the audio into the playback buffer. This allows for variations in Internet transit timing and in application timing. For example, if one packet takes 100 ms to arrive and the next takes 150 ms, the delay will ensure that the packets can still be played back as one smooth stream.
The greater the value of the delay, the longer the lag will be between when something is spoken and when it is heard. If the lag gets to be too large, it can be very noticeable to users. However a greater delay also allows for more variation in timing, resulting in a smoother experience on systems with a large amount of timing variation.
Playback Buffer Size
The playback buffer size controls approximately how far in advance the audio will be mixed into the playback buffer. This is because, as the playback position moves through the playback buffer, the space immediately behind the playback position becomes available for writing audio data that will be played when the position loops all the way back again. GV writes into the memory behind the playback position as soon as it becomes available. This ensures that there will be no skipping effect, which results from the playback position getting to a point that has not yet had new audio written to it. The user will hear the audio that was written to the buffer for the last loop, resulting in an audible glitch.
The buffer size is specified by the GVI_PLAYBACK_BUFFER_MILLISECONDS define, which is in gvDevice.h. If a custom playback device is being used, then the application is doing any audio buffering, so the define has no effect.
The most important thing that affects buffer size is the amount of time between calls to gvThink, which is when audio gets mixed into the playback buffer. The larger the buffer size, the longer the delay between when something is said and when it is heard. However a larger size allows for longer delay between calls to gvThink. With a smaller buffer size gvThink must be called more often, to ensure that the playback position does not loop all the way around without GV having a chance to write new audio data to it. In general, the buffer size should be at least twice as long as the maximum time between calls to gvThink.
Global Focus
On Win32 only, the default is for the playback device to have global focus. This means that even if the user switches to a different application, audio played through GV will still be heard. However if you define GV_NO_GLOBAL_FOCUS, then playback won't be heard if the application's window loses focus.
Excluding PS2 Hardware Types
You can exclude support for particular hardware device types on the PS2 by compiling the SDK with one or more defines set. Use GV_NO_PS2_SPU2 to exclude SPU2 support, GV_NO_PS2_HEADSET to exclude lgAud supprt (USB audio devices - headset/microphone/speakers), and/or define GV_NO_PS2_EYETOY to exclude lgVid support (Eyetoy).
Performance Data
Bandwidth and CPU Usage for Codecs
| OS | Codec |
Bandwidth
(bits-per-second) |
CPU Usage
(encoding %)* |
CPU Usage
(decoding %)* |
|---|---|---|---|---|
| Win32 | Super Low Bandwidth | 3600 | 2.4 | 0.2 |
| Low Bandwidth | 5600 | 1.4 | 0.2 | |
| Average | 8000 | 1.8 | 0.2 | |
| High Bandwidth | 14800 | 2.1 | 0.2 | |
| Super High Bandwidth | 24400 | 3.0 | 0.2 | |
| Any | No Compression | 128000 | 0.0 | 0.0 |
| PS2 | Super Low Bandwidth | 2489 | 4.5 | 18.8 |
| Low Bandwidth | 8000 | X** | X** | |
| Average | 13200 | 2.8 | 1.5 | |
| High Bandwidth | 24000 | 4.1 | 3.8 | |
| Super High Bandwidth | 64000 | 0.1 | 0.1 | |
| PS3 | Super Low Bandwidth | 3600 | 3.6 | 0.4 |
| Low Bandwidth | 5600 | 2.7 | 0.4 | |
| Average | 8000 | 3.5 | 0.4 | |
| High Bandwidth | 14800 | 4.5 | 0.4 | |
| Super High Bandwidth | 24400 | 6.0 | 0.4 | |
*CPU Usage for Win32 was tested on a Pentium 4 2.8Ghz PC with 1GB of ram and WinXP SP1.
**This codec is currently not working correctly on the PS2.
PS2 Memory Usage
Note that this memory usage information only applies to the PS2.
The SDK uses about 40-50KB of memory on the EE, and about 25-50KB of IOP memory. The exact amount of memory depends on which codec is used, which hardware types are supported (by default, all are supported), and which devices are used at runtime. All memory allocations take place in calls to gvStartup, gvSetCodec, and gvNewDevice. The SDK does not allocate additional memory while it is thinking, capturing, or playing packets. All allocated memory is freed in calls to gvFreeDevice and gvShutdown.
gvStartup
Under 1KB of EE memory is allocated at startup. This is used by lgAud and lgVid.
gvSetCodec
When a codec is set, both lgCodec and GV allocate EE memory. The exact amount depends on the codec. The Super Low Bandwidth codec uses about 20KB, the Average codec uses about 15KB, the High Quality codec uses about 16KB, and the Super High Quality codec uses about 35KB.
gvNewDevice
When a new device is instantiated with a call to gvNewDevice, both GV and lgAud or lgVid, if they are used, allocate EE memory. Memory is also allocated on the IOP, where the actual interaction with the device takes place. In addition SPU2 support uses about 25KB of static memory on the EE. The exact amount of allocated memory depends on the hardware type. A SPU2 device uses less than 1KB of EE memory and about 26KB of IOP memory. A USB audio device (headset/microphone/speakers) uses about 1KB of EE memory. It will also use about 4KB of IOP memory if it is used for playback, about 24KB of IOP memory if it is used for capture, and about 28KB of IOP memory if it is used for both capture and playback.
If you will not be supporting one or more types of PS2 hardware devices, you can save the memory that it would use by excluding it from the SDK. See the Advanced section for more information.
PS3 Memory Usage
The Voice SDK was tested on the PS3 PPU using a USB Headset.
The PS3 memory consumption when using no compression showed to be around 70KB. Using the Speex codec with settings Super High quality, High quality, Average, Low bandwith, Super Low bandwith used about 16KB, 12KB, 8KB, 7KB, 6KB respectively.
Appendix A: Voice SDK with Speex Codec on PS3 SPUs (***BETA***)
Background/Overview
This appendix assumes that all readers have experience with cell programming, and the SPURS library. Another assumption is that the development environment. The Voice SDK already has support for encoding and decoding using the Speex codec on the PPU processor (considered the main processor). It now supports encoding and decoding operations using Speex on the SPU processors. The SPURS library was the best choice, and we selected the SPURS taskset model. The SPURS taskset model involves coarse-grain processing on SPUs, which fits with Speex. The purpose of using SPUs via SPURS tasksets to perform encoding/decoding is to reclaim PPU processing time, allowing developers to use it for other purposes such as game logic, graphics code, etc. To get the most benefit from this Voice SDK feature, developers should have the Voice SDK calls that require the PPU in a separate PPU thread--The reason for this is because the Voice SDK synchronously performs encoding and decoding. Thus, putting the Voice SDK in a separate PPU thread will allow the game operations mentioned previously to execute without having to wait for encoding/decoding to complete on the Voice SDK thread. We also recommend developers put all Gamespy calls in a separate thread, which can be the same thread as the Voice SDK thread. Another important thing about DMA bandwidth, to take is that the SDK is not anywhere near the maximum DMA transfer speed. This is because of the low memory requirements displayed next.
Memory Consumption: Maximum Bytes consumed
| Codec Quality | 8 Khz Audio | 16 Khz Audio |
|---|---|---|
| SuperHighQuality | 123448 | 133848 |
| HighQuality | 118648 | 124248 |
| Average | 114648 | 118648 |
| LowBandwith | 113848 | 116248 |
| SuperLowBandwith | 113048 | 115448 |
Memory consumption was measured using the Voice2Test application for the PS3 (Voice2\Voice2Test\gvps3prodgspeexspu). The application was setup to use local echo to ensure both encoding and decoding occurred. The Voice SDK uses varying amounts of memory because it has to allocate buffers to keep track of the encoder/decoder state and to perform DMA transfers easily. Most of the memory consumed in these cases were allocations for the encoder/decoder state. Since most games will only allow one person to talk at a time, memory usage should be close to these figures.
PPU Impact for one frame (20 ms)
| Codec Quality | 8 KHz (Encode) | 8 KHz (Decode) | 16 KHz (Encode) | 16 KHz (Decode) |
|---|---|---|---|---|
| SuperHighQuality | 0.37% | 0.34% | 0.47% | 0.45% |
| HighQuality | 0.36% | 0.34% | 0.40% | 0.42% |
| Average | 0.36% | 0.34% | 0.37% | 0.37% |
| LowBandwith | 0.31% | 0.31% | 0.35% | 0.35% |
| SuperLowBandwith | 0.35% | 0.31% | 0.36% | 0.35% |
SPU Impact for one frame (20 ms)
| Codec Quality | 8 KHz (Encode) | 8 KHz (Decode) | 16 KHz (Encode) | 16 KHz (Decode) |
|---|---|---|---|---|
| SuperHighQuality | 10.5% | 0.75% | 23.86% | 1.52% |
| HighQuality | 7.57% | 0.84% | 18.00% | 1.53% |
| Average | 7.1% | 0.82% | 12.09% | 1.4% |
| LowBandwith | 5.78% | 0.89% | 6.86% | 1.36% |
| SuperLowBandwith | 6.02% | 0.89% | 11.46% | 1.34% |
The percentages listed above show the average portion of each frame spent on each processor. Sony's PA Suite and Voice2Test for the PS3 were used to calculate these percentages. According to the PPU/SPU impact charts, the Voice SDK does not utilize the PPU as much as the SPU. The PPU times reflect the amount of time spent in the encode/decode call that occurs internally in the Voice SDK which does not have processor intensive code. The rest of the time was spent on: the SPU performing the encode/decode operations, and the PPU mostly idling. Again, it is recommended that the Voice SDK is used in a PPU thread separate from the main thread because the PPU has to block until the SPU completes encode/decode operations and idles most of the time.
DMA Bandwidth (measured in bytes)
| DMA Data to and from SPU (one trip) | 8 KHz (Encode) | 8 KHz (Decode) | 16 KHz (Encode) | 16 KHz (Decode) |
|---|---|---|---|---|
| Encoded Buffer | 128 | 128 | 128 | 128 |
| State Buffer | 64672 | 41024 | 64672 | 41024 |
| Decoded Buffer | 160 | 160 | 320 | 320 |
| Task Descriptor | 46 | 46 | 46 | 46 |
| Task Output | 20 | 20 | 20 | 20 |
| ---------------------------------------------------------------------------------- | ||||
| Bytes per second used (50 frames/s * 2 transfers) | 6502600 | 4137800 | 6518600 | 4153800 |
The DMA bandwidth metrics above account for all DMA transfers that occur during
encode/decode operations. These metrics are based on a single frame, and are considered
a one way trip. The state buffers remain the same for both encoding and decoding
even when using different sampling rates. However, the buffer to perform DMA transfer
of decoded audio vary depending on the sample rate. The bytes per second shows the
bandwidth that is used for a full second of audio. It consists of 50 frames multiplied
by two transfers. These transfers account for a round trip. The DMA transfer performance
according to Sony's documentation is 13.2 GB/s for reads, and 22.8 GB/s for writes.
The numbers in the chart above are nowhere near the performance of DMA transfers
that Sony mentions in their documentation. Therefore, there should be plenty of
room for other SPURS tasks to occur in addition to this.
Implementation
Speex Task and Speex Task Manager
The Speex Task performs encode and decode operations using the speex codec, while the Speex Task Manager facilitates communication between the Voice SDK and the Speex Task. Both of these projects are required. They are availabel by downloading the GameSpy Common Code. The Speex Task Manager and the Speex Task are located in the following folders:
Gamespy\common\ps3\SpeexSpursTaskManager Gamespy\common\ps3\SpeexSpursTaskManager\SpeexSpursTask
The Voice SDK depends on these two projects and a new speex interface: gvSpeexSpu.c. These projects should be added to your Visual Studio solution. After adding the two projects to your solution, set the project dependencies to include these two projects for the project that has the Voice SDK. Refer to the Voice2Test sample that is setup for Speex SPU usage: Voice2\Voice2Test\gvps3prodgspeexspu.
As previously mentioned, extract the Speex codec into the Voice SDK folder in order to avoid seeing errors and warnings about missing Speex codec files. The rest of Voice SDK implementation can be found above.
Passing in an Existing Spurs Instance
In Voice2Test.c, there is an example of an existing SPURS instance being passed to a helper function. The Speex Spurs Task Manager includes the function. The game should have an initialized spurs instance, and pass it to the function spursConfiguration_initWithSpurs. Make sure to to include the file "spursConfiguration.h" prior to calling it. The function also takes in the number of SPUs to use for the Speex task, and the priorities of the task per SPU. This can be useful for developers that want to control the amount SPUs to dedicate to a given SPURS task. Here is an exerpt of the Voice2Test.c that shows usage of the function spursConfiguration_initWithSpurs:
#if defined(USER_CREATED_SPURS_INSTANCE)
#include "spursConfiguration.h"
#include <sys/spu_initialize.h>
int iReturn, iNumSpus, ppuThreadPriority, spuThreadPriority;
bool exitIfNoWork;
CellSpurs* myCellSpurs;
uint8_t auiLocalPriorities[8]={1,1,1,1,1,1,1,1};
#endif
...
static GVBool Initialize(const char * remoteIP)
{
...
iNumSpus = 1;
spuThreadPriority = 200;
ppuThreadPriority = 1000;
exitIfNoWork = false;
myCellSpurs = (CellSpurs*) gsimemalign(128, sizeof(CellSpurs));
// initializing spus themselves before using spurs
sys_spu_initialize(iNumSpus,0);
iReturn=cellSpursInitialize(myCellSpurs, iNumSpus,spuThreadPriority,ppuThreadPriority,exitIfNoWork);
if (iReturn!=CELL_OK)
{
printf("Error initializing spurs\n");
return GVFalse;
}
spursConfiguration_initWithSpurs(myCellSpurs, iNumSpus,auiLocalPriorities);
...
}
Emmbedding the Speex Task with your project
To Embed the Speex Task, There is an easy way to acomplish this. Simpley make the SpeexSpursTask project a dependency of the project containing the Voice SDK. Developers that like to go further can refer to the Post-Build configuration options in the SpeexSpursTask project. There is also a section in the SPURS tutorial for developers that wish to go further in customizing the embedding. Please refer to the SPURS Tutorial 3.5: Building the Program in Sony's Documentation.