[objc] view plain copy
- <span style="white-space:pre"> </span>语音技术近来可是出遍了风头,从iphone4s的siri,到微信的语音聊天等等,极大地方便了人们的社交生活,也体现了当今移动科技发展的迅猛。当然,作为一位移动开发的从业人员怎能落伍呢!今天我们就来简单的实现一下语音聊天的功能。
[objc] view plain copy
- <span style="white-space:pre"> </span>这次Demo使用的是Speex对录制的声音进行语音压缩,并且进行ogg的封装。由于本人水平有限,尝试了几次对ogg库的编译都不成功,于是终于在Code4App上找到了一个Demo,把它的speex和封装好的ogg拿了过来,如果大家对ios支持的语音格式不太了解
首先上图,聊天界面:
源码如下:
聊天界面头文件:
[objc] view plain copy
1. #import <UIKit/UIKit.h>
2. #import "RecorderManager.h"
3. #import "PlayerManager.h"
4.
5. @interface MainViewController : UIViewController<UITableViewDataSource, UITableViewDelegate, RecordingDelegate, PlayingDelegate, UIGestureRecognizerDelegate>
6.
7. - (IBAction)talk:(id)sender;
8.
9. @property (strong, nonatomic) IBOutlet UITableView *vTableView;
10.
11. @property (strong, nonatomic) IBOutlet UIButton *speekBtb;
12.
13. @property (assign) BOOL isSpeak;
14.
15. @property (strong, nonatomic) NSMutableArray *voiceArray;
16.
17. @property (strong, nonatomic) NSString *fileName;
18.
19. @property (assign) BOOL isPlaying;
20.
21. @end
实现:
[objc] view plain copy
1. #import "MainViewController.h"
2.
3. @interface MainViewController()
4.
5. @end
6.
7. @implementation MainViewController
8. @synthesize isSpeak = _isSpeak;
9. @synthesize voiceArray = _voiceArray;
10. @synthesize fileName = _fileName;
11. @synthesize isPlaying = _isPlaying;
12.
13. - (id)initWithNibName:(NSString *)nibNameOrNilNSBundle *)nibBundleOrNil
14. {
15. self = [super bundle:nibBundleOrNil];
16. if (self) {
17. // Custom initialization
18. }
19. return self;
20. }
21.
22. - (void)viewDidLoad
23. {
24. super viewDidLoad];
25. // Do any additional setup after loading the view from its nib.
26.
27. self.vTableView.delegate = self;
28. self.voiceArray = [[NSMutableArray init];
29.
30. UILongPressGestureRecognizer *guesture = [[UILongPressGestureRecognizerself@selector(handSpeakBtnPressed:)];
31. .delegate = self;
32. .minimumPressDuration = 0.01f;
33.
34. //录音按钮添加手势操作
35. addGestureRecognizer:guesture];
36. }
37.
38. - (void)didReceiveMemoryWarning
39. {
40. super didReceiveMemoryWarning];
41. // Dispose of any resources that can be recreated.
42. }
43.
44. #pragma mark tableView+++++++++++++++++++++++++++++++++++++++
45. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
46. return 1;
47. }
48.
49. - (void)tableView:(UITableView *)tableViewNSIndexPath *)indexPath{
50. UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
51.
52. //cell选中属性修改为无
53. setSelectionStyle:UITableViewCellSelectionStyleNone];
54. }
55.
56. - (UITableViewCell *)tableView:(UITableView *)tableViewNSIndexPath *)indexPath{
57.
58. static NSString *identifid = @"simpleCell";
59. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifid];
60. if(!cell){
61. init];
62. }
63.
64. NSMutableDictionary *dic = [self.voiceArray.row];
65.
66. //加载聊天内容
67. UIButton *chatView = [dic@"view"];
68. if([[dic@"from"]@"SELF"]){
69. //添加录音时长显示
70. UILabel *label = [[UILabel120, 25, 30, 30)];
71. int time = [[dic@"time"] intValue];
72. @"%d'", time]];
73. addSubview:label];
74.
75. //添加头像
76. float offset = (chatView.frame.size.height - 48)>0?(chatView.frame.size.height - 48)/2:10;
77. UIImageView *headIcon = [[UIImageViewself.view.frame.size.width - 48 -5, offset, 48, 48)];
78. @"h2.jpg"]];
79. addSubview:headIcon];
80. @"点击播放" forState:UIControlStateNormal];
81.
82. .tag = 100 + indexPath.row;
83. self@selector(playVoice:) forControlEvents:UIControlEventTouchUpInside];
84.
85. else if([[dic@"from"]@"OTHER"]){
86. //系统自动回复部分
87. float offset = (chatView.frame.size.height - 48)>0?(chatView.frame.size.height - 48)/2:10;
88. UIImageView *headIcon = [[UIImageView5, offset, 48, 48)];
89. @"h1.jpg"]];
90. addSubview:headIcon];
91. @"hello world" forState:UIControlStateNormal];
92. }
93.
94. addSubview:chatView];
95.
96. return cell;
97. }
98.
99. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
100. return [_voiceArray count];
101. }
102.
103. - (CGFloat)tableView:(UITableView *)tableViewNSIndexPath *)indexPath{
104. UIView *chatView = [[_voiceArray@"view"];
105. return chatView.frame.size.height+30;
106. }
107.
108. //添加手势操作,长按按钮
109. - (void)handSpeakBtnPressed:(UILongPressGestureRecognizer *)gestureRecognizer {
110. if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
111. @"UIGestureRecognizerStateBegan");
112.
113. self.speekBtb@"松开结束" forState:UIControlStateNormal];
114. self:nil];
115. }
116. if (gestureRecognizer.state == UIGestureRecognizerStateChanged) {
117. @"UIGestureRecognizerStateChanged");
118. }
119.
120. if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
121. @"UIGestureRecognizerStateEnded");
122.
123. self.speekBtb@"按住说话" forState:UIControlStateNormal];
124. self stopRecordVoice];
125. }
126. }
127.
128. //开始录音
129. - (IBAction)talk:(id)sender {
130. //若正在播放则立即返回
131. if(self.isPlaying){
132. return;
133. }
134.
135. if(!self.isSpeak){
136. self.isSpeak = YES;
137. .delegate = self;
138. startRecording];
139. }
140. }
141.
142. //结束录音
143. - (void)stopRecordVoice{
144. self.isSpeak = NO;
145. stopRecording];
146. }
147.
148. //播放录音
149. - (void)playVoice:(id)sender{
150. if(self.isSpeak){
151. return;
152. }
153.
154. if(!self.isPlaying){
155. UIButton *btn = (UIButton *)sender;
156. .tag;
157.
158. NSMutableDictionary *dic = [_voiceArray100)];
159. self.fileName = [dic@"voice"];
160.
161. .delegate = nil;
162.
163. self.isPlaying = YES;
164. self.fileNameself];
165. else{
166. self.isPlaying = NO;
167. stopPlaying];
168. }
169. }
170.
171. - (void)recordingFinishedWithFileName:(NSString *)filePath time:(NSTimeInterval)interval{
172. //录音保存的文件地址
173. self.fileName = filePath;
174. UIButton *view = [self@"点击播放"YES];
175. //时长
176. NSNumber *num = [[NSNumber initWithDouble:interval];
177.
178. NSMutableDictionary *dic = [[NSMutableDictionary@"SELF", @"from", view, @"view", self.fileName, @"voice", num, @"time", nil];
179. self.voiceArray addObject:dic];
180.
181. //系统默认回复消息
182. UIButton *otherView = [self@"你好!"NO];
183. NSMutableDictionary *m_dic = [[NSMutableDictionary@"OTHER", @"from", otherView, @"view", @"", @"voice", 0, @"time",nil];
184.
185. self.voiceArray addObject:m_dic];
186.
187. reloadData];
188. }
189.
190. //超时操作
191. - (void)recordingTimeout{
192. self.isSpeak = NO;
193. }
194.
195. //录音机停止采集声音
196. - (void)recordingStopped{
197. self.isSpeak = NO;
198. }
199.
200. //录制失败操作
201. - (void)recordingFailed:(NSString *)failureInfoString{
202. self.isSpeak = NO;
203. }
204.
205. //播放停止
206. - (void)playingStoped{
207. self.isPlaying = NO;
208. }
209.
210. //聊天气泡按钮生成
211. - (UIButton*)bubbleView:(NSString *)messageBOOL)isFromSelf
212. {
213. UIView *returnView = [self from:isFromSelf];
214. UIButton *cellView = [[UIButton initWithFrame:CGRectZero];
215. .backgroundColor = [UIColor clearColor];
216. clearColor]];
217.
218. NSString *picName = [NSString@"%@.png", isFromSelf?@"bubble2":@"bubble1"];
219. UIImage *bubble = [UIImage imageNamed:picName];
220.
221. UIImageView *bubbleView = [[UIImageView353]];
222. if(isFromSelf)
223. {
224. .frame = CGRectMake(9.0f, 20.0f, returnView.frame.size.width, returnView.frame.size.height);
225. .frame = CGRectMake(0.0f, 14.0f, returnView.frame.size.width+45.0f, returnView.frame.size.height + 20.0f);
226. .frame = CGRectMake(self.view.frame.size.width - bubbleView.frame.size.width - 60, 20.0f, bubbleView.frame.size.width, bubbleView.frame.size.height + 20.0f);
227. }
228. else
229. {
230. .frame = CGRectMake(88.0f, 20.0f, returnView.frame.size.width, returnView.frame.size.height);
231. .frame = CGRectMake(55.0f, 14.0f, returnView.frame.size.width + 45.0f, returnView.frame.size.height + 20.0f);
232. .frame = CGRectMake(50.0f, 20.0f, bubbleView.frame.size.width + 50.0f, bubbleView.frame.size.height + 20.0f);
233. }
234.
235. forState:UIControlStateNormal];
236. 13.0f]];
237. return cellView;
238. }
239.
240. #pragma mark -
241. #pragma mark assemble message at index
242. #define BUBBLEWIDTH 18
243. #define BUBBLEHEIGHT 18
244. #define MAX_WIDTH 140
245. - (UIView *)assembleMessageAtIndex:(NSString *)messageBOOL)fromself
246. {
247. init];
248. self _array:array];
249.
250. UIView *returnView = [[UIView initWithFrame:CGRectZero];
251. 0;
252. 0;
253. 0;
254. 0;
255.
256. if(array)
257. {
258. for(int i = 0; i < [array count]; i++)
259. {
260. NSString *msg = [array objectAtIndex:i];
261.
262. for (int index = 0; index < msg.length; index++)
263. {
264. NSString *m_ch = [msg1)];
265. if(upX >= MAX_WIDTH)
266. {
267. upY = upY + BUBBLEHEIGHT;
268. 0;
269. 140;
270. y = upY;
271. }
272.
273. UIFont *font = [UIFont13.0f];
274. 140, 40)];
275. UILabel *m_label = [[UILabel.width, m_size.height)];
276. addSubview:m_label];
277. .font = font;
278. .text = m_ch;
279. .backgroundColor = [UIColor clearColor];
280. .width;
281. if(x < 140)
282. {
283. x = upX;
284. }
285. }
286. }
287. }
288.
289. .frame = CGRectMake(15.0f, 1.0f, x, y);
290. return returnView;
291. }
292.
293. - (void)NSString *)messageNSMutableArray *)array
294. {
295. if(message != nil)
296. {
297. message];
298. }
299. }
300.
301.
302. - (void)dealloc{
303.
304. selfself@"isSpeak"];
305. self.fileName = nil;
306. }
307.
308. @end
好了一个简单的语音聊天程序就好了,是不是很简单,其中最重要的就是 RecorderManager以及PlayerManager两个类了,一个负责录音,一个负责播放,这两个类我准备放到下一篇博客中讲解一下,大家不妨通过去下载我的Demo自己动手试一试,下载地址:。
注意点:
1.在将Demo程序中的Classes以及Libs文件加入到工程中去的时候,请将echo_diagnostic.m文件以及以test开头的文件删掉,否则工程会报错。
2.在工程中将Preprocessor Macros选项中的内容删除。