blob: aaa9512b1bf42118e02388cef37ed3b29faf9bab [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjmedia-audiodev/audiodev.h>
21#include <pjmedia/delaybuf.h>
22#include <pj/assert.h>
23#include <pj/errno.h>
24#include <pj/os.h>
25#include <pj/log.h>
26#include <pj/string.h>
27#include <pj/unicode.h>
28#include <e32cons.h>
29
30#define THIS_FILE "app_main.cpp"
31#define CLOCK_RATE 8000
32#define CHANNEL_COUNT 1
33#define PTIME 20
34#define SAMPLES_PER_FRAME (CLOCK_RATE*PTIME/1000)
35#define BITS_PER_SAMPLE 16
36
37extern CConsoleBase* console;
38
39static pj_caching_pool cp;
40static pjmedia_aud_stream *strm;
41static unsigned rec_cnt, play_cnt;
42static pj_time_val t_start;
43static pjmedia_aud_param param;
44static pj_pool_t *pool;
45static pjmedia_delay_buf *delaybuf;
46static char frame_buf[256];
47
48static void copy_frame_ext(pjmedia_frame_ext *f_dst,
49 const pjmedia_frame_ext *f_src)
50{
51 pj_bzero(f_dst, sizeof(*f_dst));
52 if (f_src->subframe_cnt) {
53 f_dst->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
54 for (unsigned i = 0; i < f_src->subframe_cnt; ++i) {
55 pjmedia_frame_ext_subframe *sf;
56 sf = pjmedia_frame_ext_get_subframe(f_src, i);
57 pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
58 param.samples_per_frame);
59 }
60 } else {
61 f_dst->base.type = PJMEDIA_FRAME_TYPE_NONE;
62 }
63}
64
65/* Logging callback */
66static void log_writer(int level, const char *buf, unsigned len)
67{
68 static wchar_t buf16[PJ_LOG_MAX_SIZE];
69
70 PJ_UNUSED_ARG(level);
71
72 pj_ansi_to_unicode(buf, len, buf16, PJ_ARRAY_SIZE(buf16));
73
74 TPtrC16 aBuf((const TUint16*)buf16, (TInt)len);
75 console->Write(aBuf);
76}
77
78/* perror util */
79static void app_perror(const char *title, pj_status_t status)
80{
81 char errmsg[PJ_ERR_MSG_SIZE];
82 pj_strerror(status, errmsg, sizeof(errmsg));
83 PJ_LOG(1,(THIS_FILE, "Error: %s: %s", title, errmsg));
84}
85
86/* Application init */
87static pj_status_t app_init()
88{
89 unsigned i, count;
90 pj_status_t status;
91
92 /* Redirect log */
93 pj_log_set_log_func((void (*)(int,const char*,int)) &log_writer);
94 pj_log_set_decor(PJ_LOG_HAS_NEWLINE);
95 pj_log_set_level(3);
96
97 /* Init pjlib */
98 status = pj_init();
99 if (status != PJ_SUCCESS) {
100 app_perror("pj_init()", status);
101 return status;
102 }
103
104 pj_caching_pool_init(&cp, NULL, 0);
105
106 /* Init sound subsystem */
107 status = pjmedia_aud_subsys_init(&cp.factory);
108 if (status != PJ_SUCCESS) {
109 app_perror("pjmedia_snd_init()", status);
110 pj_caching_pool_destroy(&cp);
111 pj_shutdown();
112 return status;
113 }
114
115 count = pjmedia_aud_dev_count();
116 PJ_LOG(3,(THIS_FILE, "Device count: %d", count));
117 for (i=0; i<count; ++i) {
118 pjmedia_aud_dev_info info;
119 pj_status_t status;
120
121 status = pjmedia_aud_dev_get_info(i, &info);
122 pj_assert(status == PJ_SUCCESS);
123 PJ_LOG(3, (THIS_FILE, "%d: %s %d/%d %dHz",
124 i, info.name, info.input_count, info.output_count,
125 info.default_samples_per_sec));
126
127 unsigned j;
128
129 /* Print extended formats supported by this audio device */
130 PJ_LOG(3, (THIS_FILE, " Extended formats supported:"));
131 for (j = 0; j < info.ext_fmt_cnt; ++j) {
132 const char *fmt_name = NULL;
133
134 switch (info.ext_fmt[j].id) {
135 case PJMEDIA_FORMAT_PCMA:
136 fmt_name = "PCMA";
137 break;
138 case PJMEDIA_FORMAT_PCMU:
139 fmt_name = "PCMU";
140 break;
141 case PJMEDIA_FORMAT_AMR:
142 fmt_name = "AMR-NB";
143 break;
144 case PJMEDIA_FORMAT_G729:
145 fmt_name = "G729";
146 break;
147 case PJMEDIA_FORMAT_ILBC:
148 fmt_name = "ILBC";
149 break;
150 case PJMEDIA_FORMAT_PCM:
151 fmt_name = "PCM";
152 break;
153 default:
154 fmt_name = "Unknown";
155 break;
156 }
157 PJ_LOG(3, (THIS_FILE, " - %s", fmt_name));
158 }
159 }
160
161 /* Create pool */
162 pool = pj_pool_create(&cp.factory, THIS_FILE, 512, 512, NULL);
163 if (pool == NULL) {
164 app_perror("pj_pool_create()", status);
165 pj_caching_pool_destroy(&cp);
166 pj_shutdown();
167 return status;
168 }
169
170 /* Init delay buffer */
171 status = pjmedia_delay_buf_create(pool, THIS_FILE, CLOCK_RATE,
172 SAMPLES_PER_FRAME, CHANNEL_COUNT,
173 0, 0, &delaybuf);
174 if (status != PJ_SUCCESS) {
175 app_perror("pjmedia_delay_buf_create()", status);
176 //pj_caching_pool_destroy(&cp);
177 //pj_shutdown();
178 //return status;
179 }
180
181 return PJ_SUCCESS;
182}
183
184
185/* Sound capture callback */
186static pj_status_t rec_cb(void *user_data,
187 pjmedia_frame *frame)
188{
189 PJ_UNUSED_ARG(user_data);
190
191 if (param.ext_fmt.id == PJMEDIA_FORMAT_PCM) {
192 pjmedia_delay_buf_put(delaybuf, (pj_int16_t*)frame->buf);
193
194 if (frame->size != SAMPLES_PER_FRAME*2) {
195 PJ_LOG(3, (THIS_FILE, "Size captured = %u",
196 frame->size));
197 }
198 } else {
199 pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frame;
200 pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame_buf;
201
202 copy_frame_ext(f_dst, f_src);
203 }
204
205 ++rec_cnt;
206 return PJ_SUCCESS;
207}
208
209/* Play cb */
210static pj_status_t play_cb(void *user_data,
211 pjmedia_frame *frame)
212{
213 PJ_UNUSED_ARG(user_data);
214
215 if (param.ext_fmt.id == PJMEDIA_FORMAT_PCM) {
216 pjmedia_delay_buf_get(delaybuf, (pj_int16_t*)frame->buf);
217 frame->size = SAMPLES_PER_FRAME*2;
218 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
219 } else {
220 pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frame_buf;
221 pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame;
222
223 copy_frame_ext(f_dst, f_src);
224 }
225
226 ++play_cnt;
227 return PJ_SUCCESS;
228}
229
230/* Start sound */
231static pj_status_t snd_start(unsigned flag)
232{
233 pj_status_t status;
234
235 if (strm != NULL) {
236 app_perror("snd already open", PJ_EINVALIDOP);
237 return PJ_EINVALIDOP;
238 }
239
240 pjmedia_aud_dev_default_param(0, &param);
241 param.channel_count = CHANNEL_COUNT;
242 param.clock_rate = CLOCK_RATE;
243 param.samples_per_frame = SAMPLES_PER_FRAME;
244 param.dir = (pjmedia_dir) flag;
245 param.ext_fmt.id = PJMEDIA_FORMAT_AMR;
246 param.ext_fmt.bitrate = 12200;
247 param.output_route = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
248
249 status = pjmedia_aud_stream_create(&param, &rec_cb, &play_cb, NULL, &strm);
250 if (status != PJ_SUCCESS) {
251 app_perror("snd open", status);
252 return status;
253 }
254
255 rec_cnt = play_cnt = 0;
256 pj_gettimeofday(&t_start);
257
258 pjmedia_delay_buf_reset(delaybuf);
259
260 status = pjmedia_aud_stream_start(strm);
261 if (status != PJ_SUCCESS) {
262 app_perror("snd start", status);
263 pjmedia_aud_stream_destroy(strm);
264 strm = NULL;
265 return status;
266 }
267
268 return PJ_SUCCESS;
269}
270
271/* Stop sound */
272static pj_status_t snd_stop()
273{
274 pj_time_val now;
275 pj_status_t status;
276
277 if (strm == NULL) {
278 app_perror("snd not open", PJ_EINVALIDOP);
279 return PJ_EINVALIDOP;
280 }
281
282 status = pjmedia_aud_stream_stop(strm);
283 if (status != PJ_SUCCESS) {
284 app_perror("snd failed to stop", status);
285 }
286 status = pjmedia_aud_stream_destroy(strm);
287 strm = NULL;
288
289 pj_gettimeofday(&now);
290 PJ_TIME_VAL_SUB(now, t_start);
291
292 PJ_LOG(3,(THIS_FILE, "Duration: %d.%03d", now.sec, now.msec));
293 PJ_LOG(3,(THIS_FILE, "Captured: %d", rec_cnt));
294 PJ_LOG(3,(THIS_FILE, "Played: %d", play_cnt));
295
296 return status;
297}
298
299/* Shutdown application */
300static void app_fini()
301{
302 if (strm)
303 snd_stop();
304
305 pjmedia_aud_subsys_shutdown();
306 pjmedia_delay_buf_destroy(delaybuf);
307 pj_pool_release(pool);
308 pj_caching_pool_destroy(&cp);
309 pj_shutdown();
310}
311
312
313////////////////////////////////////////////////////////////////////////////
314/*
315 * The interractive console UI
316 */
317#include <e32base.h>
318
319class ConsoleUI : public CActive
320{
321public:
322 ConsoleUI(CConsoleBase *con);
323
324 // Run console UI
325 void Run();
326
327 // Stop
328 void Stop();
329
330protected:
331 // Cancel asynchronous read.
332 void DoCancel();
333
334 // Implementation: called when read has completed.
335 void RunL();
336
337private:
338 CConsoleBase *con_;
339};
340
341
342ConsoleUI::ConsoleUI(CConsoleBase *con)
343: CActive(EPriorityUserInput), con_(con)
344{
345 CActiveScheduler::Add(this);
346}
347
348// Run console UI
349void ConsoleUI::Run()
350{
351 con_->Read(iStatus);
352 SetActive();
353}
354
355// Stop console UI
356void ConsoleUI::Stop()
357{
358 DoCancel();
359}
360
361// Cancel asynchronous read.
362void ConsoleUI::DoCancel()
363{
364 con_->ReadCancel();
365}
366
367static void PrintMenu()
368{
369 PJ_LOG(3, (THIS_FILE, "\n\n"
370 "Menu:\n"
371 " a Start bidir sound\n"
372 " t Start recorder\n"
373 " p Start player\n"
374 " d Stop & close sound\n"
375 " w Quit\n"));
376}
377
378// Implementation: called when read has completed.
379void ConsoleUI::RunL()
380{
381 TKeyCode kc = con_->KeyCode();
382 pj_bool_t reschedule = PJ_TRUE;
383
384 switch (kc) {
385 case 'w':
386 snd_stop();
387 CActiveScheduler::Stop();
388 reschedule = PJ_FALSE;
389 break;
390 case 'a':
391 snd_start(PJMEDIA_DIR_CAPTURE_PLAYBACK);
392 break;
393 case 't':
394 snd_start(PJMEDIA_DIR_CAPTURE);
395 break;
396 case 'p':
397 snd_start(PJMEDIA_DIR_PLAYBACK);
398 break;
399 case 'd':
400 snd_stop();
401 break;
402 default:
403 PJ_LOG(3,(THIS_FILE, "Keycode '%c' (%d) is pressed",
404 kc, kc));
405 break;
406 }
407
408 PrintMenu();
409
410 if (reschedule)
411 Run();
412}
413
414
415////////////////////////////////////////////////////////////////////////////
416int app_main()
417{
418 if (app_init() != PJ_SUCCESS)
419 return -1;
420
421 // Run the UI
422 ConsoleUI *con = new ConsoleUI(console);
423
424 con->Run();
425
426 PrintMenu();
427 CActiveScheduler::Start();
428
429 delete con;
430
431 app_fini();
432 return 0;
433}
434