感谢原创人。致敬!!
【IT168技术】在我之前的iPhone文章中,我们已经介绍过使用GameKit框架在两个设备之间通过蓝牙通信,在这篇文章中,我将为大家介绍GameKit框架另一个很酷的功能 — 语音聊天。
Gamekit中的语音聊天服务允许两台iPhone/iPod Touch之间建立语音聊天,语音聊天要么通过互联网,要么通过蓝牙实现,你将在本文中看到如何通过蓝牙通信信道实现语音聊天。
创建项目
首先使用Xcode创建一个新的基于视图的iPhone应用程序,取名为Bluetooth,在Xcode的框架组上点击右键,选择“增加”,从现有框架中选择一个增加到项目上,这里选择GameKit.framework,同时再增加一个AVFoundation框架。
在BluetoothViewController.h文件中,增加下面的代码:
1
#import
&
lt;UIKit
/
UIKit.h
&
gt;
2
#import
&
lt;GameKit
/
GameKit.h
&
gt;
3
#import
&
lt;AVFoundation
/
AVFoundation.h
&
gt;
4
@interface BluetoothViewController : UIViewController
5
&
nbsp;
&
nbsp;
&
nbsp;
&
lt;GKVoiceChatClient
&
gt; {
6
7
&
nbsp;
&
nbsp;
&
nbsp;GKSession
*
currentSession;
8
&
nbsp;
&
nbsp;
&
nbsp;
9
&
nbsp;
&
nbsp;
&
nbsp;IBOutlet UIButton
*
connect;
10
&
nbsp;
&
nbsp;
&
nbsp;IBOutlet UIButton
*
disconnect;
11
}
12
@
property
(nonatomic, retain) GKSession
*
currentSession;
13
@
property
(nonatomic, retain) UIButton
*
connect;
14
@
property
(nonatomic, retain) UIButton
*
disconnect;
15
16
-
(IBAction) btnMute:(id) sender;
17
-
(IBAction) btnUnmute:(id) sender;
18
-
(IBAction) btnConnect:(id) sender;
19
-
(IBAction) btnDisconnect:(id) sender;
20
@
end
21
拖动一个wav文件(如无特殊说明,指beep.wav)到Xcode的Resources文件夹上。
双击BluetoothViewController.xib文件在Interface Builder编辑它,使用三个圆形按钮填充到视图窗口中,如图1所示。
图 1 使用三个圆形按钮填充视图窗口
在BluetoothViewController.xib窗口中,执行以下连接:
按住CTRL键,点击文件所有者项目,将其拖放到Connect按钮上,选择连接;
按住CTRL键,点击文件所有者项目,将其拖放到Disconnect按钮上,选择断开连接;
按住CTRL键,点击Connect按钮,将其拖放到文件所有者项目上,选择btnConnect;
按住CTRL键,点击Disconnect按钮,将其拖放到文件所有者项目上,选择btnDisconnect;
在Mute按钮上点击右键,将Touch Down事件连接到文件所有者项目上,选择btnMute;
在Mute按钮上点击右键,将Touch Up Inside事件连接到文件所有者项目上,选择btnUnmute;
为了验证所有连接是否正确,在文件所有者项目上点击右键,查看其连接,如图2所示。
图 2 验证出口和行为的连接
在BluetoothViewController.m文件中,增加下面的代码:
1
#import
"
BluetoothViewController.h
"
2
#import
<
GameKit
/
GameKit.h
>
3
#import
<
AVFoundation
/
AVFoundation.h
>
4
5
@implementation BluetoothViewController
6
@synthesize currentSession;
7
@synthesize connect;
8
@synthesize disconnect;
9
10
GKPeerPickerController
*
picker;
11
NSString
*
recorderFilePath;
12
13
AVAudioPlayer
*
audioPlayer;
14
15
-
(void)viewDidLoad {
16
17
[connect setHidden:NO];
18
[disconnect setHidden:YES];
19
20
[super viewDidLoad];
21
}
22
23
-
(IBAction) btnConnect:(id) sender {
24
25
//---
选择一个附近的蓝牙设备
---
26
picker
=
[[GKPeerPickerController alloc] init];
27
picker.delegate
=
self;
28
picker.connectionTypesMask
=
GKPeerPickerConnectionTypeNearby;
29
30
[connect setHidden:YES];
31
[disconnect setHidden:NO];
32
33
[picker show];
34
35
}
36
37
-
(IBAction) btnDisconnect:(id) sender {
38
39
//---
从其它设备断开连接
---
40
[self.currentSession disconnectFromAllPeers];
41
[self.currentSession release];
42
currentSession
=
nil;
43
44
[connect setHidden:NO];
45
[disconnect setHidden:YES];
46
47
}
48
49
-
(void)peerPickerController:(GKPeerPickerController
*
)picker
50
didConnectPeer:(NSString
*
)peerID
51
toSession: (GKSession
*
) session {
52
53
self.currentSession
=
session;
54
session.delegate
=
self;
55
[session setDataReceiveHandler: self withContext:nil];
56
picker.delegate
=
nil;
57
[picker dismiss];
58
[picker autorelease];
59
60
}
61
62
-
(void)peerPickerControllerDidCancel:(GKPeerPickerController
*
)picker {
63
64
picker.delegate
=
nil;
65
[picker autorelease];
66
67
[connect setHidden:NO];
68
[disconnect setHidden:YES];
69
70
}
71
72
-
(IBAction) btnMute:(id) sender {
73
74
//---
静音语音聊天
---
75
[GKVoiceChatService defaultVoiceChatService].microphoneMuted
=
YES;
76
77
}
78
79
-
(IBAction) btnUnmute:(id) sender {
80
81
//---
取消静音
---
82
[GKVoiceChatService defaultVoiceChatService].microphoneMuted
=
NO;
83
84
}
85
86
//---
返回一个表示本地用户的唯一ID
---
87
-
(NSString
*
) participantID
88
{
89
return currentSession.peerID;
90
}
91
92
-
(void) voiceChatService:(GKVoiceChatService
*
) voiceChatService
93
sendData:(NSData
*
) data
94
toParticipantID:(NSString
*
)participantID {
95
96
[currentSession sendData:data toPeers:
97
[NSArray arrayWithObject:participantID]
98
withDataMode:GKSendDataReliable
error
:nil];
99
100
}
101
102
-
(void)session:(GKSession
*
)session
103
peer:(NSString
*
)peerID
104
didChangeState:(GKPeerConnectionState)state {
105
106
switch (state)
107
{
108
case
GKPeerStateConnected:
109
{
110
//---
播放一个音频文件
---
111
NSString
*
soundFilePath
=
[[NSBundle mainBundle]
112
pathForResource:@
"
beep
"
ofType:@
"
wav
"
];
113
114
NSURL
*
fileURL
=
[[NSURL alloc]
115
initFileURLWithPath: soundFilePath];
116
117
AVAudioPlayer
*
audioPlayer
=
118
[[AVAudioPlayer alloc] initWithContentsOfURL:fileURL
119
error
:nil];
120
121
[fileURL release];
122
[audioPlayer play];
123
124
NSError
*
error
;
125
AVAudioSession
*
audioSession
=
126
[AVAudioSession sharedInstance];
127
128
if
(![audioSession
129
setCategory:AVAudioSessionCategoryPlayAndRecord
130
error
:
&
error
]) {
131
NSLog(@
"
Error setting the AVAudioSessionCategoryPlayAndRecord category: %@
"
,
132
[
error
localizedDescription]);
133
}
134
135
if
(![audioSession setActive: YES
error
:
&
error
]) {
136
NSLog(@
"
Error activating audioSession: %@
"
,
137
[
error
description]);
138
}
139
140
[GKVoiceChatService defaultVoiceChatService].client
=
self;
141
142
//---
初始化语音聊天
---
143
if
(![[GKVoiceChatService defaultVoiceChatService]
144
startVoiceChatWithParticipantID:peerID
error
:
&
error
]) {
145
NSLog(@
"
Error starting startVoiceChatWithParticipantID: %@
"
,
146
[
error
userInfo]);
147
}
148
} break;
149
150
case
GKPeerStateDisconnected:
151
{
152
[[GKVoiceChatService defaultVoiceChatService]
153
stopVoiceChatWithParticipantID:peerID];
154
155
[self.currentSession release];
156
currentSession
=
nil;
157
158
[connect setHidden:NO];
159
[disconnect setHidden:YES];
160
161
} break;
162
}
163
}
164
165
-
(void) receiveData:(NSData
*
)data
166
fromPeer:(NSString
*
)peer
167
inSession:(GKSession
*
)session
168
context:(void
*
)context {
169
170
//---
初始化完成后开始语音聊天
---
171
[[GKVoiceChatService defaultVoiceChatService]
172
receivedData:data fromParticipantID:peer];
173
174
}
175
176
-
(void)dealloc {
177
178
if
(currentSession) [currentSession release];
179
[connect release];
180
[disconnect release];
181
182
[super dealloc];
183
}
184
@
end
185
理解语音聊天是如何工作的
当两个蓝牙设备连接时,首先播放beep.wav文件发出嘟嘟声,然后启动语音会话(通过session:peer:didChangeState:方法实现)。
1
NSString
*
soundFilePath
=
[[NSBundle mainBundle]
2
pathForResource:@
"
beep
"
ofType:@
"
wav
"
];
3
4
NSURL
*
fileURL
=
[[NSURL alloc]
5
initFileURLWithPath: soundFilePath];
6
7
AVAudioPlayer
*
audioPlayer
=
8
[[AVAudioPlayer alloc] initWithContentsOfURL:fileURL
9
error
:nil];
10
11
[fileURL release];
12
[audioPlayer play];
13
14
NSError
*
error
;
15
AVAudioSession
*
audioSession
=
16
[AVAudioSession sharedInstance];
17
18
if
(![audioSession
19
setCategory:AVAudioSessionCategoryPlayAndRecord
20
error
:
&
error
]) {
21
NSLog(@
"
Error setting the AVAudioSessionCategoryPlayAndRecord category: %@
"
,
22
[
error
localizedDescription]);
23
}
24
25
if
(![audioSession setActive: YES
error
:
&
error
]) {
26
NSLog(@
"
Error activating audioSession: %@
"
,
27
[
error
description]);
28
}
29
30
[GKVoiceChatService defaultVoiceChatService].client
=
self;
31
提示:有趣的是,如果你不启动音频播放器,语音聊天不能正常工作。
然后检索GKVoiceChatService类的单一实例,调用它的startVoiceChatWithParticipantID:error:方法启动语音聊天。
1
if
(![[GKVoiceChatService defaultVoiceChatService]
2
startVoiceChatWithParticipantID:peerID
error
:
&
error
]) {
3
NSLog(@
"
Error starting startVoiceChatWithParticipantID: %@
"
,
4
[
error
userInfo]);
5
}
6
调用startVoiceChatWithParticipantID:error:方法将会请求voiceChatService:sendData:toParticipantID:方法,它将会使用当前的蓝牙会话发送配置数据到其它设备。
1
-
(void) voiceChatService:(GKVoiceChatService
*
) voiceChatService
2
sendData:(NSData
*
) data
3
toParticipantID:(NSString
*
)participantID {
4
5
[currentSession sendData:data toPeers:
6
[NSArray arrayWithObject:participantID]
7
withDataMode:GKSendDataReliable
error
:nil];
8
9
}
10
其它连接的设备接收到发来的配置数据后,通过调用receivedData:fromParticipantID:方法启动语音聊天服务。
GKVoiceChatService使用两个设备之间交互的配置信息创建它自己的连接传输语音数据,还可以将microphoneMuted属性的值设为YES使麦克风静音。
1
[GKVoiceChatService defaultVoiceChatService].microphoneMuted
=
YES;
测试应用程序
在开始测试之前,先要把应用程序两台iPhone或iPod Touch上,对于iPod Touch,你需要使用一个外置麦克风,因为它默认是不带有麦克风的,一个好选择是Griffin iTalk Pro,如图3所示,它是iPod Touch麦克风及其零配件插入的基座,另外也支持苹果头戴式耳机及其配件。
应用程序部署到设备上后,启动应用程序,按下Connect按钮使用蓝牙相互连接,当两个设备连接好后,就可以开始聊天了,如果要临时静音,按下Mute按钮一直不放即可,放开Mute按钮后,声音就又恢复了。
在这篇文章中,你看到了如何使用Gamekit框架提供的GKVoiceChatService类在两个设备之间无缝地建立语音通信,关于两个设备之间的语音是如何传输的暂时没有必要知道,所有需要了解的就是要知道调用什么方法初始化语音聊天,另外你还要知道,语音聊天不仅可以通过蓝牙通信信道实现,在其它任何通信信道上都可以实现,实际上,如果使用TCP/IP连接两台设备,也可以实现语音聊天,在今后的文章中,我将会涉及。