iPhone: Implementing Voice Chatting over Bluetooth

In my previous iPhone article, you learned about Bluetooth communication between two devices using the GameKit framework. Another cool feature of the GameKit framework that you will explore in this article is the support for voice chat.

Figure 1. Button Up: Populate the View window with three Round Rect Button views.

The Voice Chat Service in the GameKit allows two iPhones/iPod Touches to establish a voice chat. The voice chat takes place over either an Internet or Bluetooth connection. In this article, you will see how you can implement voice chatting over a Bluetooth communication channel.

Creating the Project

Let’s start by creating an iPhone application using Xcode. Using Xcode, create a new View-based Application project and name it as Bluetooth.

Add a new Framework to the project by right-clicking on the Frameworks group in Xcode and selecting Add, Existing Frameworks. Select GameKit.framework. Also, add the AVFoundation.Framework.

In the BluetoothViewController.h file, add the following statements in bold:

#import <UIKit/UIKit.h>#import <GameKit/GameKit.h>#import <AVFoundation/AVFoundation.h>@interface BluetoothViewController : UIViewController     <GKVoiceChatClient> {    GKSession *currentSession;        IBOutlet UIButton *connect;    IBOutlet UIButton *disconnect;}@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

Figure 2. Verify: Verify the connections for the outlets and actions.

Drag and drop a WAV file (in this article the WAV file is beep.wav) onto the Resources folder in Xcode.

Double-click the BluetoothViewController.xib file to edit it in Interface Builder. Populate the View window with the three Round Rect Button views (See Figure 1).

In the BluetoothViewController.xib window, perform the following connections:

    * Control-click the File's Owner item and drag and drop it over the Connect button. Select connect.

    * Control-click the File's Owner item and drag and drop it over the Disconnect button. Select disconnect.

    * Control-click the Connect button and drag and drop it over the File's Owner item. Select btnConnect:

    * Control-click the Disconnect button and drag and drop it over the File's Owner item. Select btnDisconnect:

    * Right-click on the Mute button and connect the Touch Down event to the File's Owner item. Select btnMute:

    * Right-click on the Mute button and connect the Touch Up Inside event to the File's Owner item. Select btnUnmute:

To verify that all the connections are made correctly, right-click on the File's Owner item and view its connections (see Figure 2).

In the BluetoothViewController.m file, add the following statements in bold:

#import "BluetoothViewController.h"#import #import @implementation [email protected] currentSession;@synthesize connect;@synthesize disconnect;GKPeerPickerController *picker;NSString *recorderFilePath;AVAudioPlayer *audioPlayer;- (void)viewDidLoad {    [connect setHidden:NO];    [disconnect setHidden:YES];    [super viewDidLoad];}- (IBAction) btnConnect:(id) sender {    //---Select a nearby Bluetooth device---    picker = [[GKPeerPickerController alloc] init];    picker.delegate = self;    picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby;          [connect setHidden:YES];    [disconnect setHidden:NO];        [picker show];    }-(IBAction) btnDisconnect:(id) sender {    //---disconnected from the other device---    [self.currentSession disconnectFromAllPeers];    [self.currentSession release];    currentSession = nil;        [connect setHidden:NO];    [disconnect setHidden:YES];}- (void)peerPickerController:(GKPeerPickerController *)picker               didConnectPeer:(NSString *)peerID                    toSession: (GKSession *) session {    self.currentSession = session;    session.delegate = self;    [session setDataReceiveHandler: self withContext:nil];    picker.delegate = nil;    [picker dismiss];    [picker autorelease];    }    - (void)peerPickerControllerDidCancel:(GKPeerPickerController *)picker {    picker.delegate = nil;    [picker autorelease];        [connect setHidden:NO];    [disconnect setHidden:YES];}-(IBAction) btnMute:(id) sender {    //---mute the voice chat---    [GKVoiceChatService defaultVoiceChatService].microphoneMuted = YES;    }-(IBAction) btnUnmute:(id) sender {    //---unmute the voice chat---    [GKVoiceChatService defaultVoiceChatService].microphoneMuted = NO;}//---returns a unique ID that identifies the local user----(NSString *) participantID{    return currentSession.peerID;}-(void) voiceChatService:(GKVoiceChatService *) voiceChatService                sendData:(NSData *) data         toParticipantID:(NSString *)participantID {    [currentSession sendData:data toPeers:        [NSArray arrayWithObject:participantID]         withDataMode:GKSendDataReliable error:nil];}- (void)session:(GKSession *)session            peer:(NSString *)peerID  didChangeState:(GKPeerConnectionState)state {    switch (state)    {        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];            [fileURL release];            [audioPlayer play];                                    NSError *error;            AVAudioSession *audioSession =                 [AVAudioSession sharedInstance];            if (![audioSession                  setCategory:AVAudioSessionCategoryPlayAndRecord                 error:&error]) {                NSLog(@"Error setting the AVAudioSessionCategoryPlayAndRecord 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];                        [self.currentSession release];            currentSession = nil;                                [connect setHidden:NO];            [disconnect setHidden:YES];                } break;    }}- (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];}- (void)dealloc {    if (currentSession) [currentSession release];    [connect release];    [disconnect release];    [super dealloc];}@end

Understanding How Voice Chat Works

When two Bluetooth devices are connected, you first play the beep sound and start the audio session (you do so via the session:peer:didChangeState: method):

            NSString *soundFilePath = [[NSBundle mainBundle]                 pathForResource:@"beep" ofType:@"wav"];            NSURL *fileURL = [[NSURL alloc]                 initFileURLWithPath: soundFilePath];            AVAudioPlayer *audioPlayer =                [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL                      error:nil];            [fileURL release];            [audioPlayer play];                                    NSError *error;            AVAudioSession *audioSession =                 [AVAudioSession sharedInstance];            if (![audioSession                  setCategory:AVAudioSessionCategoryPlayAndRecord                 error:&error]) {                NSLog(@"Error setting the AVAudioSessionCategoryPlayAndRecord category: %@",                 [error localizedDescription]);            }                        if (![audioSession setActive: YES error: &error]) {                NSLog(@"Error activating audioSession: %@",                     [error description]);            }                        [GKVoiceChatService defaultVoiceChatService].client = self;

Tip: Interestingly, if you do not start the audio player, the voice chat will not work.

You then retrieve a singleton instance of the GKVoiceChatService class and call its startVoiceChatWithParticipantID:error: method to start the voice chat:

            if (![[GKVoiceChatService defaultVoiceChatService]                  startVoiceChatWithParticipantID:peerID error:&error]) {                NSLog(@"Error starting startVoiceChatWithParticipantID: %@",                     [error userInfo]);            }

Calling the startVoiceChatWithParticipantID:error: method: will invoke the voiceChatService:sendData:toParticipantID: method, which will make use of the current Bluetooth session to send the configuration data to the other connected device:

-(void) voiceChatService:(GKVoiceChatService *) voiceChatService                sendData:(NSData *) data         toParticipantID:(NSString *)participantID {    [currentSession sendData:data toPeers:        [NSArray arrayWithObject:participantID]         withDataMode:GKSendDataReliable error:nil];}

The other connected device, when it has received the configuration data, starts the voice chat service by calling the receivedData:fromParticipantID: method:

- (void) receiveData:(NSData *)data             fromPeer:(NSString *)peer            inSession:(GKSession *)session              context:(void *)context {    [[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 also mute the microphone by setting the microphoneMuted property to YES:

    [GKVoiceChatService defaultVoiceChatService].microphoneMuted = YES; 

Testing the Application

Figure 3. Chatter: The Griffin iTalk Pro.

To test the application, deploy the application onto two devices - either iPhone or iPod Touch. For iPod Touch, you need to connect it to an external microphone as it does not come with one. A good recommendation is the Griffin iTalk Pro (see Figure 3) - a mic and microphone accessory that plugs into the base of the iPod Touch. Alternatively, the Apple Earphones with Remote and Mic also works.

With the application deployed on the two devices, run the application and press the Connect button to use Bluetooth to connect to each other. As soon as the two devices are connected, you can now start chatting! To temporarily mute the conversation, press and hold on the Mute button. When it is released, the conversation resumes. Have fun!

In this article, you saw how the GameKit framework provides the GKVoiceChatService class that makes voice communication between two devices seamless. There is no need for you to know how the voices are transported between two devices - all you need to know is to call the relevant methods to initialize the chat. One important point you need to know though - voice chat works not just over Bluetooth; it works over any communication channel. In fact, if you have two devices connected using TCP/IP, you too can stream the voices over the wire. I will explore this in more detail in a future article.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

The Latest

your company's audio

4 Areas of Your Company Where Your Audio Really Matters

Your company probably relies on audio more than you realize. Whether you’re creating a spoken text message to a colleague or giving a speech, you want your audio to shine. Otherwise, you could cause avoidable friction points and potentially hurt your brand reputation. For example, let’s say you create a

chrome os developer mode

How to Turn on Chrome OS Developer Mode

Google’s Chrome OS is a popular operating system that is widely used on Chromebooks and other devices. While it is designed to be simple and user-friendly, there are times when users may want to access additional features and functionality. One way to do this is by turning on Chrome OS

homes in the real estate industry

Exploring the Latest Tech Trends Impacting the Real Estate Industry

The real estate industry is changing thanks to the newest technological advancements. These new developments — from blockchain and AI to virtual reality and 3D printing — are poised to change how we buy and sell homes. Real estate brokers, buyers, sellers, wholesale real estate professionals, fix and flippers, and beyond may