Another cool feature of the Game Kit framework is its support for voice chat.
The Voice Chat service in the Game Kit enables two devices to establish a voice chat. The voice chat takes place over either an Internet connection or a Bluetooth connection. This section shows you how to implement voice chatting over a Bluetooth communication channel.
TRY IT OUT: Enabling Bluetooth Voice Chatting
#import <UIKit/UIKit.h> #import <GameKit/GameKit.h> #import <AVFoundation/AVFoundation.h> @interface BluetoothChatViewController : UIViewController <GKVoiceChatClient, GKPeerPickerControllerDelegate, GKSessionDelegate>
{ GKSession *currentSession; IBOutlet UIButton *connect; IBOutlet UIButton *disconnect; GKPeerPickerController *picker; } @property (nonatomic, retain) GKSession *currentSession; @property (nonatomic, retain) UIButton *connect; @property (nonatomic, retain) UIButton *disconnect; -(IBAction)btnMute:(id) sender; -(IBAction)btnUnmute:(id) sender; -(IBAction)btnConnect:(id) sender; -(IBAction)btnDisconnect:(id) sender; @end
#import “BluetoothChatViewController.h” @implementation BluetoothChatViewController @synthesize currentSession; @synthesize connect; @synthesize disconnect; NSString *recorderFilePath; AVAudioPlayer *audioPlayer; - (void)viewDidLoad { [connect setHidden:NO]; [disconnect setHidden:YES]; [super viewDidLoad]; } - (GKSession *)peerPickerController:(GKPeerPickerController *)picker sessionForConnectionType:(GKPeerPickerConnectionType)type { if (!self.currentSession) { self.currentSession = [[[GKSession alloc] initWithSessionID:@“Session_ID_Here” displayName:nil sessionMode:GKSessionModePeer] autorelease]; self.currentSession.delegate = self; } return self.currentSession; } //---select a nearby Bluetooth device---
-(IBAction) btnConnect:(id) sender { picker = [[GKPeerPickerController alloc] init]; picker.delegate = self; picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby; [connect setHidden:YES]; [disconnect setHidden:NO]; [picker show]; } //---disconnect from the other device--- -(IBAction) btnDisconnect:(id) sender { [self.currentSession disconnectFromAllPeers]; currentSession = nil; [connect setHidden:NO]; [disconnect setHidden:YES]; } //---did connect to a peer--- -(void) peerPickerController:(GKPeerPickerController *)pk didConnectPeer:(NSString *)peerID toSession:(GKSession *) session { self.currentSession = session; session.delegate = self; [session setDataReceiveHandler:self withContext:nil]; picker.delegate = nil; [picker dismiss]; [picker autorelease]; } //---connection was cancelled--- -(void) peerPickerControllerDidCancel:(GKPeerPickerController *)pk { picker.delegate = nil; [picker autorelease]; [connect setHidden:NO]; [disconnect setHidden:YES]; } //---mute the voice chat--- -(IBAction) btnMute:(id) sender { [GKVoiceChatService defaultVoiceChatService].microphoneMuted = YES; } //---unmute the voice chat--- -(IBAction) btnUnmute:(id) sender { [GKVoiceChatService defaultVoiceChatService].microphoneMuted = NO; } //---returns a unique ID that identifies the local user--- -(NSString *) participantID { return currentSession.peerID; } //---sends voice chat configuration data to the other party--- -(void) voiceChatService:(GKVoiceChatService *) voiceChatService
sendData:(NSData *) data toParticipantID:(NSString *) participantID { [currentSession sendData:data toPeers:[NSArray arrayWithObject:participantID] withDataMode:GKSendDataReliable error:nil]; } //---session state changed--- -(void) session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state { switch (state) { case GKPeerStateAvailable: NSLog(@“State Available”); break; case GKPeerStateConnecting: NSLog(@“State Connecting”); break; case GKPeerStateUnavailable: NSLog(@“State Unavailable”); break; case GKPeerStateConnected: { //---plays an audio file--- NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@“beep” ofType:@“wav”]; NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:soundFilePath]; AVAudioPlayer *audioPlayer = [[[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil] autorelease]; [fileURL release]; [audioPlayer play]; NSError *error; AVAudioSession *audioSession = [AVAudioSession sharedInstance]; if (![audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]) { NSLog(@“Error setting category: %@”, [error localizedDescription]); } if (![audioSession setActive:YES error:&error]) { NSLog(@“Error activating audioSession: %@”, [error description]); } [GKVoiceChatService defaultVoiceChatService].client = self; //---initiating the voice chat--- if (![[GKVoiceChatService defaultVoiceChatService] startVoiceChatWithParticipantID:peerID error:&error]) { NSLog(@“Error starting startVoiceChatWithParticipantID:%@”, [error userInfo]); )
} break; case GKPeerStateDisconnected: { [[GKVoiceChatService defaultVoiceChatService] stopVoiceChatWithParticipantID:peerID]; currentSession = nil; [connect setHidden:NO]; [disconnect setHidden:YES]; } break; } } //---data received from the other party--- -(void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context { //---start the voice chat when initiated by the client--- [[GKVoiceChatService defaultVoiceChatService] receivedData:data fromParticipantID:peer]; } //---session failed with error--- -(void) session:(GKSession *)session didFailWithError:(NSError *)error { NSLog(@“%@”,[error description]); } - (void)dealloc { [currentSession release]; [connect release]; [disconnect release]; [super dealloc]; }
How It Works
When two Bluetooth devices are connected, you first play the beep sound and start the audio session (via the session:peer:didChangeState: method):
//---plays an audio file--- NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@“beep” ofType:@“wav”]; NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:soundFilePath]; AVAudioPlayer *audioPlayer =
[[[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil] autorelease]; [fileURL release]; [audioPlayer play]; NSError *error; AVAudioSession *audioSession = [AVAudioSession sharedInstance]; if (![audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]) { NSLog(@“Error setting category: %@”, [error localizedDescription]); } if (![audioSession setActive:YES error:&error]) { NSLog(@“Error activating audioSession: %@”, [error description]); } [GKVoiceChatService defaultVoiceChatService].client = self;
You then retrieve a singleton instance of the GKVoiceChatService class and call its startVoiceChatWithParticipantID:error: method to start the voice chat:
//---initiating the voice chat--- if (![[GKVoiceChatService defaultVoiceChatService] startVoiceChatWithParticipantID:peerID error:&error]) { NSLog(@“Error starting startVoiceChatWithParticipantID:%@”, [error userInfo]); }
Notice that you needed to implement the participantID method declared in the GKVoiceChatClient protocol:
//---returns a unique ID that identifies the local user--- -(NSString *) participantID { return currentSession.peerID; }
This method should return a string that uniquely identifies the current user. Since you are using Bluetooth, you used the peerID property of the GKSession object.
Calling the startVoiceChatWithParticipantID:error: method invokes the voiceChatService:sendData:toParticipantID: method (defined in the GKVoiceChatClient protocol), which makes use of the current Bluetooth session to send the configuration data to the other connected device:
//---sends voice chat configuration data to the other party--- -(void) voiceChatService:(GKVoiceChatService *) voiceChatService sendData:(NSData *) data toParticipantID:(NSString *) participantID { [currentSession sendData:data toPeers:[NSArray arrayWithObject:participantID] withDataMode:GKSendDataReliable error:nil]; }
When it has received the configuration data, the other device starts the Voice Chat service by calling the receivedData:fromParticipantID: method (also defined in the GKVoiceChatClient protocol):
//---data received from the other party--- -(void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context { //---start the voice chat when initiated by the client--- [[GKVoiceChatService defaultVoiceChatService] receivedData:data fromParticipantID:peer]; }
The GKVoiceChatService uses the configuration information that was exchanged between the two devices and creates its own connection to transfer voice data.
You can mute the microphone by setting the microphoneMuted property to YES:
[GKVoiceChatService defaultVoiceChatService].microphoneMuted = YES;
18.191.180.252