blob: dcd6ed882db98a1591f58e23cb3e4d4b7ad3b953 [file] [log] [blame]
Benny Prijono96811bf2007-08-31 15:04:52 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjmedia/sound.h>
20#include <pj/errno.h>
21#include <pj/os.h>
22#include <pj/log.h>
23#include <pj/string.h>
24#include <pj/unicode.h>
25#include <e32cons.h>
26
27#define THIS_FILE "app_main.cpp"
28#define CLOCK_RATE 8000
29#define CHANNEL_COUNT 1
30#define PTIME 100
31#define SAMPLES_PER_FRAME (2048)
32#define BITS_PER_SAMPLE 16
33
34
35extern CConsoleBase* console;
36
37static pj_caching_pool cp;
38static pjmedia_snd_stream *strm;
39static unsigned rec_cnt, play_cnt;
40static pj_time_val t_start;
41
42
43/* Logging callback */
44static void log_writer(int level, const char *buf, unsigned len)
45{
46 static wchar_t buf16[PJ_LOG_MAX_SIZE];
47
48 PJ_UNUSED_ARG(level);
49
50 pj_ansi_to_unicode(buf, len, buf16, PJ_ARRAY_SIZE(buf16));
51
52 TPtrC16 aBuf((const TUint16*)buf16, (TInt)len);
53 console->Write(aBuf);
54}
55
56/* perror util */
57static void app_perror(const char *title, pj_status_t status)
58{
59 char errmsg[PJ_ERR_MSG_SIZE];
60 pj_strerror(status, errmsg, sizeof(errmsg));
61 PJ_LOG(1,(THIS_FILE, "Error: %s: %s", title, errmsg));
62}
63
64/* Application init */
65static pj_status_t app_init()
66{
67 unsigned i, count;
68 pj_status_t status;
69
70 /* Redirect log */
71 pj_log_set_log_func((void (*)(int,const char*,int)) &log_writer);
72 pj_log_set_decor(PJ_LOG_HAS_NEWLINE);
73
74 /* Init pjlib */
75 status = pj_init();
76 if (status != PJ_SUCCESS) {
77 app_perror("pj_init()", status);
78 return status;
79 }
80
81 pj_caching_pool_init(&cp, NULL, 0);
82
83 /* Init sound subsystem */
84 status = pjmedia_snd_init(&cp.factory);
85 if (status != PJ_SUCCESS) {
86 app_perror("pjmedia_snd_init()", status);
87 pj_caching_pool_destroy(&cp);
88 pj_shutdown();
89 return status;
90 }
91
92 count = pjmedia_snd_get_dev_count();
93 PJ_LOG(3,(THIS_FILE, "Device count: %d", count));
94 for (i=0; i<count; ++i) {
95 const pjmedia_snd_dev_info *info;
96
97 info = pjmedia_snd_get_dev_info(i);
98 PJ_LOG(3, (THIS_FILE, "%d: %s %d/%d %dHz",
99 i, info->name, info->input_count, info->output_count,
100 info->default_samples_per_sec));
101 }
102
103 return PJ_SUCCESS;
104}
105
106
107/* Sound capture callback */
108static pj_status_t rec_cb(void *user_data,
109 pj_uint32_t timestamp,
110 void *input,
111 unsigned size)
112{
113 PJ_UNUSED_ARG(user_data);
114 PJ_UNUSED_ARG(timestamp);
115 PJ_UNUSED_ARG(input);
116 PJ_UNUSED_ARG(size);
117
118 ++rec_cnt;
119 return PJ_SUCCESS;
120}
121
122/* Play cb */
123static pj_status_t play_cb(void *user_data,
124 pj_uint32_t timestamp,
125 void *output,
126 unsigned size)
127{
128 PJ_UNUSED_ARG(user_data);
129 PJ_UNUSED_ARG(timestamp);
130
131 pj_bzero(output, size);
132
133 ++play_cnt;
134 return PJ_SUCCESS;
135}
136
137/* Start sound */
138static pj_status_t snd_start(unsigned flag)
139{
140 pj_status_t status;
141
142 if (strm != NULL) {
143 app_perror("snd already open", PJ_EINVALIDOP);
144 return PJ_EINVALIDOP;
145 }
146
147 if (flag==PJMEDIA_DIR_CAPTURE_PLAYBACK)
148 status = pjmedia_snd_open(-1, -1, CLOCK_RATE, CHANNEL_COUNT,
149 SAMPLES_PER_FRAME, BITS_PER_SAMPLE,
150 &rec_cb, &play_cb, NULL, &strm);
151 else if (flag==PJMEDIA_DIR_CAPTURE)
152 status = pjmedia_snd_open_rec(-1, CLOCK_RATE, CHANNEL_COUNT,
153 SAMPLES_PER_FRAME, BITS_PER_SAMPLE,
154 &rec_cb, NULL, &strm);
155 else
156 status = pjmedia_snd_open_player(-1, CLOCK_RATE, CHANNEL_COUNT,
157 SAMPLES_PER_FRAME, BITS_PER_SAMPLE,
158 &play_cb, NULL, &strm);
159
160 if (status != PJ_SUCCESS) {
161 app_perror("snd open", status);
162 return status;
163 }
164
165 rec_cnt = play_cnt = 0;
166 pj_gettimeofday(&t_start);
167
168 status = pjmedia_snd_stream_start(strm);
169 if (status != PJ_SUCCESS) {
170 app_perror("snd start", status);
171 pjmedia_snd_stream_close(strm);
172 strm = NULL;
173 return status;
174 }
175
176 return PJ_SUCCESS;
177}
178
179/* Stop sound */
180static pj_status_t snd_stop()
181{
182 pj_time_val now;
183 pj_status_t status;
184
185 if (strm == NULL) {
186 app_perror("snd not open", PJ_EINVALIDOP);
187 return PJ_EINVALIDOP;
188 }
189
190 status = pjmedia_snd_stream_close(strm);
191 strm = NULL;
192
193 pj_gettimeofday(&now);
194 PJ_TIME_VAL_SUB(now, t_start);
195
196 PJ_LOG(3,(THIS_FILE, "Duration: %d.%03d", now.sec, now.msec));
197 PJ_LOG(3,(THIS_FILE, "Captured: %d", rec_cnt));
198 PJ_LOG(3,(THIS_FILE, "Played: %d", play_cnt));
199
200 return status;
201}
202
203/* Shutdown application */
204static void app_fini()
205{
206 if (strm)
207 snd_stop();
208
209 pjmedia_snd_deinit();
210 pj_caching_pool_destroy(&cp);
211 pj_shutdown();
212}
213
214
215////////////////////////////////////////////////////////////////////////////
216/*
217 * The interractive console UI
218 */
219#include <e32base.h>
220
221class ConsoleUI : public CActive
222{
223public:
224 ConsoleUI(CActiveSchedulerWait *asw, CConsoleBase *con);
225
226 // Run console UI
227 void Run();
228
229 // Stop
230 void Stop();
231
232protected:
233 // Cancel asynchronous read.
234 void DoCancel();
235
236 // Implementation: called when read has completed.
237 void RunL();
238
239private:
240 CActiveSchedulerWait *asw_;
241 CConsoleBase *con_;
242};
243
244
245ConsoleUI::ConsoleUI(CActiveSchedulerWait *asw, CConsoleBase *con)
246: CActive(EPriorityHigh), asw_(asw), con_(con)
247{
248 CActiveScheduler::Add(this);
249}
250
251// Run console UI
252void ConsoleUI::Run()
253{
254 con_->Read(iStatus);
255 SetActive();
256}
257
258// Stop console UI
259void ConsoleUI::Stop()
260{
261 DoCancel();
262}
263
264// Cancel asynchronous read.
265void ConsoleUI::DoCancel()
266{
267 con_->ReadCancel();
268}
269
270static void PrintMenu()
271{
272 PJ_LOG(3, (THIS_FILE, "\n\n"
273 "Menu:\n"
274 " b Start bidir sound\n"
275 " r Start recorder\n"
276 " p Start player\n"
277 " c Stop & close sound\n"
278 " q Quit\n"));
279}
280
281// Implementation: called when read has completed.
282void ConsoleUI::RunL()
283{
284 TKeyCode kc = con_->KeyCode();
285 pj_bool_t reschedule = PJ_TRUE;
286
287 switch (kc) {
288 case 'q':
289 asw_->AsyncStop();
290 reschedule = PJ_FALSE;
291 break;
292 case 'b':
293 snd_start(PJMEDIA_DIR_CAPTURE_PLAYBACK);
294 break;
295 case 'r':
296 snd_start(PJMEDIA_DIR_CAPTURE);
297 break;
298 case 'p':
299 snd_start(PJMEDIA_DIR_PLAYBACK);
300 break;
301 case 'c':
302 snd_stop();
303 break;
304 default:
305 PJ_LOG(3,(THIS_FILE, "Keycode '%c' (%d) is pressed",
306 kc, kc));
307 break;
308 }
309
310 PrintMenu();
311
312 if (reschedule)
313 Run();
314}
315
316
317////////////////////////////////////////////////////////////////////////////
318int app_main()
319{
320 if (app_init() != PJ_SUCCESS)
321 return -1;
322
323 // Run the UI
324 CActiveSchedulerWait *asw = new CActiveSchedulerWait;
325 ConsoleUI *con = new ConsoleUI(asw, console);
326
327 con->Run();
328
329 PrintMenu();
330 asw->Start();
331
332 delete con;
333 delete asw;
334
335 app_fini();
336 return 0;
337}
338