blob: 8991fec5ad0cf8c5d47c05b111480db2096f3dfe [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id: aectest.c 4537 2013-06-19 06:47:43Z riza $ */
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
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
36#define THIS_FILE "aectest.c"
37#define PTIME 20
38#define TAIL_LENGTH 200
39
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"
51" aectest [options] <PLAY.WAV> <REC.WAV> <OUTPUT.WAV> \n"
52" \n"
53" <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"
58" -d The delay between playback and capture in ms, at least 25 ms.\n"
59" Default is 25 ms. See note below. \n"
60" -l Set the echo tail length in ms. Default is 200 ms \n"
61" -r Set repeat count (default=1) \n"
62" -a Algorithm: 0=default, 1=speex, 3=echo suppress \n"
63" -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";
71
72/*
73 * Sample session:
74 *
75 * -d 100 -a 1 ../bin/orig8.wav ../bin/echo8.wav ../bin/result8.wav
76 */
77
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;
95 pjmedia_port *wav_play;
96 pjmedia_port *wav_rec;
97 pjmedia_port *wav_out;
98 pj_status_t status;
99 pjmedia_echo_state *ec;
100 pjmedia_frame play_frame, rec_frame;
101 unsigned opt = 0;
102 unsigned latency_ms = 25;
103 unsigned tail_ms = TAIL_LENGTH;
104 pj_timestamp t0, t1;
105 int i, repeat=1, interactive=0, c;
106
107 pj_optind = 0;
108 while ((c=pj_getopt(argc, argv, "d:l:a:r:i")) !=-1) {
109 switch (c) {
110 case 'd':
111 latency_ms = atoi(pj_optarg);
112 if (latency_ms < 25) {
113 puts("Invalid delay");
114 puts(desc);
115 }
116 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;
139 case 'r':
140 repeat = atoi(pj_optarg);
141 if (repeat < 1) {
142 puts("Invalid repeat count");
143 puts(desc);
144 return 1;
145 }
146 break;
147 case 'i':
148 interactive = 1;
149 break;
150 }
151 }
152
153 if (argc - pj_optind != 3) {
154 puts("Error: missing argument(s)");
155 puts(desc);
156 return 1;
157 }
158
159 /* 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
181 /* Open wav_play */
182 status = pjmedia_wav_player_port_create(pool, argv[pj_optind], PTIME,
183 PJMEDIA_FILE_NO_LOOP, 0,
184 &wav_play);
185 if (status != PJ_SUCCESS) {
186 app_perror(THIS_FILE, "Error opening playback WAV file", status);
187 return 1;
188 }
189
190 /* 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 }
198
199 /* play and rec WAVs must have the same clock rate */
200 if (PJMEDIA_PIA_SRATE(&wav_play->info) != PJMEDIA_PIA_SRATE(&wav_rec->info)) {
201 puts("Error: clock rate mismatch in the WAV files");
202 return 1;
203 }
204
205 /* .. and channel count */
206 if (PJMEDIA_PIA_CCNT(&wav_play->info) != PJMEDIA_PIA_CCNT(&wav_rec->info)) {
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 PJMEDIA_PIA_SRATE(&wav_play->info),
214 PJMEDIA_PIA_CCNT(&wav_play->info),
215 PJMEDIA_PIA_SPF(&wav_play->info),
216 PJMEDIA_PIA_BITS(&wav_play->info),
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, PJMEDIA_PIA_SRATE(&wav_play->info),
225 PJMEDIA_PIA_CCNT(&wav_play->info),
226 PJMEDIA_PIA_SPF(&wav_play->info),
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, PJMEDIA_PIA_SPF(&wav_play->info)<<1);
237 rec_frame.buf = pj_pool_alloc(pool, PJMEDIA_PIA_SPF(&wav_play->info)<<1);
238 pj_get_timestamp(&t0);
239 for (i=0; i < repeat; ++i) {
240 for (;;) {
241 play_frame.size = PJMEDIA_PIA_SPF(&wav_play->info) << 1;
242 status = pjmedia_port_get_frame(wav_play, &play_frame);
243 if (status != PJ_SUCCESS)
244 break;
245
246 status = pjmedia_echo_playback(ec, (short*)play_frame.buf);
247
248 rec_frame.size = PJMEDIA_PIA_SPF(&wav_play->info) << 1;
249 status = pjmedia_port_get_frame(wav_rec, &rec_frame);
250 if (status != PJ_SUCCESS)
251 break;
252
253 status = pjmedia_echo_capture(ec, (short*)rec_frame.buf, 0);
254
255 //status = pjmedia_echo_cancel(ec, (short*)rec_frame.buf,
256 // (short*)play_frame.buf, 0, NULL);
257
258 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);
263 }
264 pj_get_timestamp(&t1);
265
266 i = (int)pjmedia_wav_writer_port_get_pos(wav_out) / sizeof(pj_int16_t) * 1000 /
267 (PJMEDIA_PIA_SRATE(&wav_out->info) * PJMEDIA_PIA_CCNT(&wav_out->info));
268 PJ_LOG(3,(THIS_FILE, "Processed %3d.%03ds audio",
269 i / 1000, i % 1000));
270 PJ_LOG(3,(THIS_FILE, "Completed in %u msec\n", pj_elapsed_msec(&t0, &t1)));
271
272 /* Destroy file port(s) */
273 status = pjmedia_port_destroy( wav_play );
274 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
275 status = pjmedia_port_destroy( wav_rec );
276 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
277 status = pjmedia_port_destroy( wav_out );
278 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
279
280 /* Destroy ec */
281 pjmedia_echo_destroy(ec);
282
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
292 /* Shutdown PJLIB */
293 pj_shutdown();
294
295 if (interactive) {
296 char s[10], *dummy;
297 puts("ENTER to quit");
298 dummy = fgets(s, sizeof(s), stdin);
299 }
300
301 /* Done. */
302 return 0;
303}
304