blob: 5e51d86e3a12c541fc4458c6f7236ee751cf9bcc [file] [log] [blame]
Benny Prijono69f73122006-08-04 11:08:00 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono69f73122006-08-04 11:08:00 +00005 *
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
21
22/**
23 * \page page_pjmedia_samples_aectest_c Samples: AEC Test (aectest.c)
24 *
25 * Play a file to speaker, run AEC, and record the microphone input
26 * to see if echo is coming.
27 *
28 * This file is pjsip-apps/src/samples/aectest.c
29 *
30 * \includelineno aectest.c
31 */
32#include <pjmedia.h>
33#include <pjlib-util.h> /* pj_getopt */
34#include <pjlib.h>
35
Benny Prijono95250c72008-08-09 05:40:22 +000036#define THIS_FILE "aectest.c"
Benny Prijono69f73122006-08-04 11:08:00 +000037#define PTIME 20
Benny Prijono95250c72008-08-09 05:40:22 +000038#define TAIL_LENGTH 200
Benny Prijono69f73122006-08-04 11:08:00 +000039
40static const char *desc =
41" FILE \n"
42" \n"
43" aectest.c \n"
44" \n"
45" PURPOSE \n"
46" \n"
47" Test the AEC effectiveness. \n"
48" \n"
49" USAGE \n"
50" \n"
Benny Prijono95250c72008-08-09 05:40:22 +000051" aectest [options] <PLAY.WAV> <REC.WAV> <OUTPUT.WAV> \n"
Benny Prijono69f73122006-08-04 11:08:00 +000052" \n"
Benny Prijono95250c72008-08-09 05:40:22 +000053" <PLAY.WAV> is the signal played to the speaker. \n"
54" <REC.WAV> is the signal captured from the microphone. \n"
55" <OUTPUT.WAV> is the output file to store the test result \n"
56"\n"
57" options:\n"
Nanang Izzuddin6050f882010-03-03 14:47:35 +000058" -d The delay between playback and capture in ms, at least 25 ms.\n"
59" Default is 25 ms. See note below. \n"
Benny Prijono95250c72008-08-09 05:40:22 +000060" -l Set the echo tail length in ms. Default is 200 ms \n"
Benny Prijonoa7908d72008-08-10 16:15:14 +000061" -r Set repeat count (default=1) \n"
Benny Prijono6a237632008-08-13 13:53:18 +000062" -a Algorithm: 0=default, 1=speex, 3=echo suppress \n"
Nanang Izzuddin6050f882010-03-03 14:47:35 +000063" -i Interactive \n"
64"\n"
65" Note that for the AEC internal buffering mechanism, it is required\n"
66" that the echoed signal (in REC.WAV) is delayed from the \n"
67" corresponding reference signal (in PLAY.WAV) at least as much as \n"
68" frame time + PJMEDIA_WSOLA_DELAY_MSEC. In this application, frame \n"
69" time is 20 ms and default PJMEDIA_WSOLA_DELAY_MSEC is 5 ms, hence \n"
70" 25 ms delay is the minimum value. \n";
Benny Prijono69f73122006-08-04 11:08:00 +000071
Benny Prijono95250c72008-08-09 05:40:22 +000072/*
73 * Sample session:
74 *
75 * -d 100 -a 1 ../bin/orig8.wav ../bin/echo8.wav ../bin/result8.wav
76 */
Benny Prijono69f73122006-08-04 11:08:00 +000077
78static void app_perror(const char *sender, const char *title, pj_status_t st)
79{
80 char errmsg[PJ_ERR_MSG_SIZE];
81
82 pj_strerror(st, errmsg, sizeof(errmsg));
83 PJ_LOG(3,(sender, "%s: %s", title, errmsg));
84}
85
86
87/*
88 * main()
89 */
90int main(int argc, char *argv[])
91{
92 pj_caching_pool cp;
93 pjmedia_endpt *med_endpt;
94 pj_pool_t *pool;
Benny Prijono95250c72008-08-09 05:40:22 +000095 pjmedia_port *wav_play;
96 pjmedia_port *wav_rec;
97 pjmedia_port *wav_out;
Benny Prijono69f73122006-08-04 11:08:00 +000098 pj_status_t status;
Benny Prijono95250c72008-08-09 05:40:22 +000099 pjmedia_echo_state *ec;
100 pjmedia_frame play_frame, rec_frame;
101 unsigned opt = 0;
Nanang Izzuddin6050f882010-03-03 14:47:35 +0000102 unsigned latency_ms = 25;
Benny Prijono95250c72008-08-09 05:40:22 +0000103 unsigned tail_ms = TAIL_LENGTH;
104 pj_timestamp t0, t1;
Benny Prijono6a237632008-08-13 13:53:18 +0000105 int i, repeat=1, interactive=0, c;
Benny Prijono69f73122006-08-04 11:08:00 +0000106
Benny Prijono95250c72008-08-09 05:40:22 +0000107 pj_optind = 0;
Benny Prijono6a237632008-08-13 13:53:18 +0000108 while ((c=pj_getopt(argc, argv, "d:l:a:r:i")) !=-1) {
Benny Prijono95250c72008-08-09 05:40:22 +0000109 switch (c) {
110 case 'd':
111 latency_ms = atoi(pj_optarg);
Nanang Izzuddin6050f882010-03-03 14:47:35 +0000112 if (latency_ms < 25) {
113 puts("Invalid delay");
114 puts(desc);
115 }
Benny Prijono95250c72008-08-09 05:40:22 +0000116 break;
117 case 'l':
118 tail_ms = atoi(pj_optarg);
119 break;
120 case 'a':
121 {
122 int alg = atoi(pj_optarg);
123 switch (alg) {
124 case 0:
125 opt = 0;
126 case 1:
127 opt = PJMEDIA_ECHO_SPEEX;
128 break;
129 case 3:
130 opt = PJMEDIA_ECHO_SIMPLE;
131 break;
132 default:
133 puts("Invalid algorithm");
134 puts(desc);
135 return 1;
136 }
137 }
138 break;
Benny Prijonoa7908d72008-08-10 16:15:14 +0000139 case 'r':
140 repeat = atoi(pj_optarg);
141 if (repeat < 1) {
Nanang Izzuddin6050f882010-03-03 14:47:35 +0000142 puts("Invalid repeat count");
Benny Prijonoa7908d72008-08-10 16:15:14 +0000143 puts(desc);
144 return 1;
145 }
146 break;
Benny Prijono6a237632008-08-13 13:53:18 +0000147 case 'i':
148 interactive = 1;
149 break;
Benny Prijono95250c72008-08-09 05:40:22 +0000150 }
151 }
Benny Prijono69f73122006-08-04 11:08:00 +0000152
Benny Prijono95250c72008-08-09 05:40:22 +0000153 if (argc - pj_optind != 3) {
154 puts("Error: missing argument(s)");
Benny Prijono69f73122006-08-04 11:08:00 +0000155 puts(desc);
156 return 1;
157 }
158
Benny Prijono69f73122006-08-04 11:08:00 +0000159 /* Must init PJLIB first: */
160 status = pj_init();
161 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
162
163 /* Must create a pool factory before we can allocate any memory. */
164 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
165
166 /*
167 * Initialize media endpoint.
168 * This will implicitly initialize PJMEDIA too.
169 */
170 status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
171 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
172
173 /* Create memory pool for our file player */
174 pool = pj_pool_create( &cp.factory, /* pool factory */
175 "wav", /* pool name. */
176 4000, /* init size */
177 4000, /* increment size */
178 NULL /* callback on error */
179 );
180
Benny Prijono95250c72008-08-09 05:40:22 +0000181 /* Open wav_play */
182 status = pjmedia_wav_player_port_create(pool, argv[pj_optind], PTIME,
183 PJMEDIA_FILE_NO_LOOP, 0,
184 &wav_play);
Benny Prijono69f73122006-08-04 11:08:00 +0000185 if (status != PJ_SUCCESS) {
Benny Prijono95250c72008-08-09 05:40:22 +0000186 app_perror(THIS_FILE, "Error opening playback WAV file", status);
Benny Prijono69f73122006-08-04 11:08:00 +0000187 return 1;
188 }
Benny Prijono69f73122006-08-04 11:08:00 +0000189
Benny Prijono95250c72008-08-09 05:40:22 +0000190 /* Open recorded wav */
191 status = pjmedia_wav_player_port_create(pool, argv[pj_optind+1], PTIME,
192 PJMEDIA_FILE_NO_LOOP, 0,
193 &wav_rec);
194 if (status != PJ_SUCCESS) {
195 app_perror(THIS_FILE, "Error opening recorded WAV file", status);
196 return 1;
197 }
Benny Prijono69f73122006-08-04 11:08:00 +0000198
Benny Prijono95250c72008-08-09 05:40:22 +0000199 /* play and rec WAVs must have the same clock rate */
200 if (wav_play->info.clock_rate != wav_rec->info.clock_rate) {
201 puts("Error: clock rate mismatch in the WAV files");
202 return 1;
203 }
Benny Prijono69f73122006-08-04 11:08:00 +0000204
Benny Prijono95250c72008-08-09 05:40:22 +0000205 /* .. and channel count */
206 if (wav_play->info.channel_count != wav_rec->info.channel_count) {
207 puts("Error: clock rate mismatch in the WAV files");
208 return 1;
209 }
210
211 /* Create output wav */
212 status = pjmedia_wav_writer_port_create(pool, argv[pj_optind+2],
213 wav_play->info.clock_rate,
214 wav_play->info.channel_count,
215 wav_play->info.samples_per_frame,
216 wav_play->info.bits_per_sample,
217 0, 0, &wav_out);
218 if (status != PJ_SUCCESS) {
219 app_perror(THIS_FILE, "Error opening output WAV file", status);
220 return 1;
221 }
222
223 /* Create echo canceller */
224 status = pjmedia_echo_create2(pool, wav_play->info.clock_rate,
225 wav_play->info.channel_count,
226 wav_play->info.samples_per_frame,
227 tail_ms, latency_ms,
228 opt, &ec);
229 if (status != PJ_SUCCESS) {
230 app_perror(THIS_FILE, "Error creating EC", status);
231 return 1;
232 }
233
234
235 /* Processing loop */
236 play_frame.buf = pj_pool_alloc(pool, wav_play->info.samples_per_frame<<1);
237 rec_frame.buf = pj_pool_alloc(pool, wav_play->info.samples_per_frame<<1);
238 pj_get_timestamp(&t0);
Benny Prijonoa7908d72008-08-10 16:15:14 +0000239 for (i=0; i < repeat; ++i) {
240 for (;;) {
241 play_frame.size = wav_play->info.samples_per_frame << 1;
242 status = pjmedia_port_get_frame(wav_play, &play_frame);
243 if (status != PJ_SUCCESS)
244 break;
Benny Prijono95250c72008-08-09 05:40:22 +0000245
Benny Prijonoa7908d72008-08-10 16:15:14 +0000246 status = pjmedia_echo_playback(ec, (short*)play_frame.buf);
Benny Prijono95250c72008-08-09 05:40:22 +0000247
Benny Prijonoa7908d72008-08-10 16:15:14 +0000248 rec_frame.size = wav_play->info.samples_per_frame << 1;
249 status = pjmedia_port_get_frame(wav_rec, &rec_frame);
250 if (status != PJ_SUCCESS)
251 break;
Benny Prijono95250c72008-08-09 05:40:22 +0000252
Benny Prijonoa7908d72008-08-10 16:15:14 +0000253 status = pjmedia_echo_capture(ec, (short*)rec_frame.buf, 0);
Benny Prijono95250c72008-08-09 05:40:22 +0000254
Benny Prijonoa7908d72008-08-10 16:15:14 +0000255 //status = pjmedia_echo_cancel(ec, (short*)rec_frame.buf,
256 // (short*)play_frame.buf, 0, NULL);
Benny Prijono95250c72008-08-09 05:40:22 +0000257
Benny Prijonoa7908d72008-08-10 16:15:14 +0000258 pjmedia_port_put_frame(wav_out, &rec_frame);
259 }
260
261 pjmedia_wav_player_port_set_pos(wav_play, 0);
262 pjmedia_wav_player_port_set_pos(wav_rec, 0);
Benny Prijono95250c72008-08-09 05:40:22 +0000263 }
264 pj_get_timestamp(&t1);
265
Nanang Izzuddin6050f882010-03-03 14:47:35 +0000266 i = pjmedia_wav_writer_port_get_pos(wav_out) / sizeof(pj_int16_t) * 1000 /
Benny Prijono6a237632008-08-13 13:53:18 +0000267 (wav_out->info.clock_rate * wav_out->info.channel_count);
268 PJ_LOG(3,(THIS_FILE, "Processed %3d.%03ds audio",
269 i / 1000, i % 1000));
Benny Prijono95250c72008-08-09 05:40:22 +0000270 PJ_LOG(3,(THIS_FILE, "Completed in %u msec\n", pj_elapsed_msec(&t0, &t1)));
Benny Prijono69f73122006-08-04 11:08:00 +0000271
272 /* Destroy file port(s) */
Benny Prijono95250c72008-08-09 05:40:22 +0000273 status = pjmedia_port_destroy( wav_play );
Benny Prijono69f73122006-08-04 11:08:00 +0000274 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
Benny Prijono95250c72008-08-09 05:40:22 +0000275 status = pjmedia_port_destroy( wav_rec );
276 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
277 status = pjmedia_port_destroy( wav_out );
Benny Prijono69f73122006-08-04 11:08:00 +0000278 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
279
Benny Prijono95250c72008-08-09 05:40:22 +0000280 /* Destroy ec */
281 pjmedia_echo_destroy(ec);
Benny Prijono69f73122006-08-04 11:08:00 +0000282
283 /* Release application pool */
284 pj_pool_release( pool );
285
286 /* Destroy media endpoint. */
287 pjmedia_endpt_destroy( med_endpt );
288
289 /* Destroy pool factory */
290 pj_caching_pool_destroy( &cp );
291
Benny Prijonoaf1bb1e2006-11-21 12:39:31 +0000292 /* Shutdown PJLIB */
293 pj_shutdown();
Benny Prijono69f73122006-08-04 11:08:00 +0000294
Benny Prijono6a237632008-08-13 13:53:18 +0000295 if (interactive) {
Benny Prijono4d69c3d2010-08-05 10:37:47 +0000296 char s[10], *dummy;
Benny Prijonoa7908d72008-08-10 16:15:14 +0000297 puts("ENTER to quit");
Benny Prijono4d69c3d2010-08-05 10:37:47 +0000298 dummy = fgets(s, sizeof(s), stdin);
Benny Prijonoa7908d72008-08-10 16:15:14 +0000299 }
Benny Prijonoa7908d72008-08-10 16:15:14 +0000300
Benny Prijono69f73122006-08-04 11:08:00 +0000301 /* Done. */
302 return 0;
303}
304