WARNING: The online documentation has moved to https://docs.pjsip.org.

Visit the new documentation at https://docs.pjsip.org:

BLOG | DOCUMENTATION | GITHUB

Home --> Documentations --> PJMEDIA Reference

Samples: AEC Test (aectest.c)

Play a file to speaker, run AEC, and record the microphone input to see if echo is coming.

This file is pjsip-apps/src/samples/aectest.c

1/*
2 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
3 * Copyright (C) 2003-2008 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
20
31#include <pjmedia.h>
32#include <pjlib-util.h> /* pj_getopt */
33#include <pjlib.h>
34
35#define THIS_FILE "aectest.c"
36#define PTIME 20
37#define TAIL_LENGTH 200
38
39static const char *desc =
40" FILE \n"
41" \n"
42" aectest.c \n"
43" \n"
44" PURPOSE \n"
45" \n"
46" Test the AEC effectiveness. \n"
47" \n"
48" USAGE \n"
49" \n"
50" aectest [options] <PLAY.WAV> <REC.WAV> <OUTPUT.WAV> \n"
51" \n"
52" <PLAY.WAV> is the signal played to the speaker. \n"
53" <REC.WAV> is the signal captured from the microphone. \n"
54" <OUTPUT.WAV> is the output file to store the test result \n"
55"\n"
56" options:\n"
57" -d The delay between playback and capture in ms, at least 25 ms.\n"
58" Default is 25 ms. See note below. \n"
59" -l Set the echo tail length in ms. Default is 200 ms \n"
60" -r Set repeat count (default=1) \n"
61" -a Algorithm: 0=default, 1=speex, 2=echo suppress, 3=WebRtc \n"
62" -i Interactive \n"
63"\n"
64" Note that for the AEC internal buffering mechanism, it is required\n"
65" that the echoed signal (in REC.WAV) is delayed from the \n"
66" corresponding reference signal (in PLAY.WAV) at least as much as \n"
67" frame time + PJMEDIA_WSOLA_DELAY_MSEC. In this application, frame \n"
68" time is 20 ms and default PJMEDIA_WSOLA_DELAY_MSEC is 5 ms, hence \n"
69" 25 ms delay is the minimum value. \n";
70
71/*
72 * Sample session:
73 *
74 * -d 100 -a 1 ../bin/orig8.wav ../bin/echo8.wav ../bin/result8.wav
75 */
76
77static void app_perror(const char *sender, const char *title, pj_status_t st)
78{
79 char errmsg[PJ_ERR_MSG_SIZE];
80
81 pj_strerror(st, errmsg, sizeof(errmsg));
82 PJ_LOG(3,(sender, "%s: %s", title, errmsg));
83}
84
85
86/*
87 * main()
88 */
89int main(int argc, char *argv[])
90{
92 pjmedia_endpt *med_endpt;
93 pj_pool_t *pool;
94 pjmedia_port *wav_play;
95 pjmedia_port *wav_rec;
96 pjmedia_port *wav_out;
97 pj_status_t status;
99 pjmedia_frame play_frame, rec_frame;
100 unsigned opt = 0;
101 unsigned latency_ms = 25;
102 unsigned tail_ms = TAIL_LENGTH;
103 pj_timestamp t0, t1;
104 int i, repeat=1, interactive=0, c;
105
106 pj_optind = 0;
107 while ((c=pj_getopt(argc, argv, "d:l:a:r:i")) !=-1) {
108 switch (c) {
109 case 'd':
110 latency_ms = atoi(pj_optarg);
111 if (latency_ms < 25) {
112 puts("Invalid delay");
113 puts(desc);
114 }
115 break;
116 case 'l':
117 tail_ms = atoi(pj_optarg);
118 break;
119 case 'a':
120 {
121 int alg = atoi(pj_optarg);
122 switch (alg) {
123 case 0:
124 opt = 0;
125 break;
126 case 1:
127 opt = PJMEDIA_ECHO_SPEEX;
128 break;
129 case 2:
131 break;
132 case 3:
134 break;
135 default:
136 puts("Invalid algorithm");
137 puts(desc);
138 return 1;
139 }
140 }
141 break;
142 case 'r':
143 repeat = atoi(pj_optarg);
144 if (repeat < 1) {
145 puts("Invalid repeat count");
146 puts(desc);
147 return 1;
148 }
149 break;
150 case 'i':
151 interactive = 1;
152 break;
153 }
154 }
155
156 if (argc - pj_optind != 3) {
157 puts("Error: missing argument(s)");
158 puts(desc);
159 return 1;
160 }
161
162 /* Must init PJLIB first: */
163 status = pj_init();
164 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
165
166 /* Must create a pool factory before we can allocate any memory. */
168
169 /*
170 * Initialize media endpoint.
171 * This will implicitly initialize PJMEDIA too.
172 */
173 status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
174 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
175
176 /* Create memory pool for our file player */
177 pool = pj_pool_create( &cp.factory, /* pool factory */
178 "wav", /* pool name. */
179 4000, /* init size */
180 4000, /* increment size */
181 NULL /* callback on error */
182 );
183
184 /* Open wav_play */
185 status = pjmedia_wav_player_port_create(pool, argv[pj_optind], PTIME,
187 &wav_play);
188 if (status != PJ_SUCCESS) {
189 app_perror(THIS_FILE, "Error opening playback WAV file", status);
190 return 1;
191 }
192
193 /* Open recorded wav */
194 status = pjmedia_wav_player_port_create(pool, argv[pj_optind+1], PTIME,
196 &wav_rec);
197 if (status != PJ_SUCCESS) {
198 app_perror(THIS_FILE, "Error opening recorded WAV file", status);
199 return 1;
200 }
201
202 /* play and rec WAVs must have the same clock rate */
203 if (PJMEDIA_PIA_SRATE(&wav_play->info) != PJMEDIA_PIA_SRATE(&wav_rec->info)) {
204 puts("Error: clock rate mismatch in the WAV files");
205 return 1;
206 }
207
208 /* .. and channel count */
209 if (PJMEDIA_PIA_CCNT(&wav_play->info) != PJMEDIA_PIA_CCNT(&wav_rec->info)) {
210 puts("Error: clock rate mismatch in the WAV files");
211 return 1;
212 }
213
214 /* Create output wav */
215 status = pjmedia_wav_writer_port_create(pool, argv[pj_optind+2],
216 PJMEDIA_PIA_SRATE(&wav_play->info),
217 PJMEDIA_PIA_CCNT(&wav_play->info),
218 PJMEDIA_PIA_SPF(&wav_play->info),
219 PJMEDIA_PIA_BITS(&wav_play->info),
220 0, 0, &wav_out);
221 if (status != PJ_SUCCESS) {
222 app_perror(THIS_FILE, "Error opening output WAV file", status);
223 return 1;
224 }
225
226 /* Create echo canceller */
227 status = pjmedia_echo_create2(pool, PJMEDIA_PIA_SRATE(&wav_play->info),
228 PJMEDIA_PIA_CCNT(&wav_play->info),
229 PJMEDIA_PIA_SPF(&wav_play->info),
230 tail_ms, latency_ms,
231 opt, &ec);
232 if (status != PJ_SUCCESS) {
233 app_perror(THIS_FILE, "Error creating EC", status);
234 return 1;
235 }
236
237
238 /* Processing loop */
239 play_frame.buf = pj_pool_alloc(pool, PJMEDIA_PIA_SPF(&wav_play->info)<<1);
240 rec_frame.buf = pj_pool_alloc(pool, PJMEDIA_PIA_SPF(&wav_play->info)<<1);
241 pj_get_timestamp(&t0);
242 for (i=0; i < repeat; ++i) {
243 for (;;) {
244 play_frame.size = PJMEDIA_PIA_SPF(&wav_play->info) << 1;
245 status = pjmedia_port_get_frame(wav_play, &play_frame);
246 if (status != PJ_SUCCESS)
247 break;
248
249 status = pjmedia_echo_playback(ec, (short*)play_frame.buf);
250
251 rec_frame.size = PJMEDIA_PIA_SPF(&wav_play->info) << 1;
252 status = pjmedia_port_get_frame(wav_rec, &rec_frame);
253 if (status != PJ_SUCCESS)
254 break;
255
256 status = pjmedia_echo_capture(ec, (short*)rec_frame.buf, 0);
257
258 //status = pjmedia_echo_cancel(ec, (short*)rec_frame.buf,
259 // (short*)play_frame.buf, 0, NULL);
260
261 pjmedia_port_put_frame(wav_out, &rec_frame);
262 }
263
266 }
267 pj_get_timestamp(&t1);
268
269 i = (int)pjmedia_wav_writer_port_get_pos(wav_out) / sizeof(pj_int16_t) * 1000 /
270 (PJMEDIA_PIA_SRATE(&wav_out->info) * PJMEDIA_PIA_CCNT(&wav_out->info));
271 PJ_LOG(3,(THIS_FILE, "Processed %3d.%03ds audio",
272 i / 1000, i % 1000));
273 PJ_LOG(3,(THIS_FILE, "Completed in %u msec\n", pj_elapsed_msec(&t0, &t1)));
274
275 /* Destroy file port(s) */
276 status = pjmedia_port_destroy( wav_play );
277 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
278 status = pjmedia_port_destroy( wav_rec );
279 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
280 status = pjmedia_port_destroy( wav_out );
281 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
282
283 /* Destroy ec */
285
286 /* Release application pool */
287 pj_pool_release( pool );
288
289 /* Destroy media endpoint. */
290 pjmedia_endpt_destroy( med_endpt );
291
292 /* Destroy pool factory */
294
295 /* Shutdown PJLIB */
296 pj_shutdown();
297
298 if (interactive) {
299 char s[10], *dummy;
300 puts("ENTER to quit");
301 dummy = fgets(s, sizeof(s), stdin);
302 PJ_UNUSED_ARG(dummy);
303 }
304
305 /* Done. */
306 return 0;
307}
308
pj_status_t pjmedia_echo_create2(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned latency_ms, unsigned options, pjmedia_echo_state **p_echo)
pj_status_t pjmedia_echo_destroy(pjmedia_echo_state *echo)
pj_status_t pjmedia_echo_capture(pjmedia_echo_state *echo, pj_int16_t *rec_frm, unsigned options)
pj_status_t pjmedia_echo_playback(pjmedia_echo_state *echo, pj_int16_t *play_frm)
struct pjmedia_echo_state pjmedia_echo_state
Definition: echo.h:51
@ PJMEDIA_ECHO_SIMPLE
Definition: echo.h:76
@ PJMEDIA_ECHO_WEBRTC
Definition: echo.h:82
@ PJMEDIA_ECHO_SPEEX
Definition: echo.h:69
pj_status_t pjmedia_wav_player_port_set_pos(pjmedia_port *port, pj_uint32_t offset)
pj_status_t pjmedia_wav_player_port_create(pj_pool_t *pool, const char *filename, unsigned ptime, unsigned flags, pj_ssize_t buff_size, pjmedia_port **p_port)
@ PJMEDIA_FILE_NO_LOOP
Definition: wav_port.h:49
pj_ssize_t pjmedia_wav_writer_port_get_pos(pjmedia_port *port)
pj_status_t pjmedia_wav_writer_port_create(pj_pool_t *pool, const char *filename, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned flags, pj_ssize_t buff_size, pjmedia_port **p_port)
pj_status_t pjmedia_port_put_frame(pjmedia_port *port, pjmedia_frame *frame)
pj_status_t pjmedia_port_destroy(pjmedia_port *port)
unsigned PJMEDIA_PIA_BITS(const pjmedia_port_info *pia)
Definition: port.h:283
unsigned PJMEDIA_PIA_SPF(const pjmedia_port_info *pia)
Definition: port.h:311
unsigned PJMEDIA_PIA_CCNT(const pjmedia_port_info *pia)
Definition: port.h:270
pj_status_t pjmedia_port_get_frame(pjmedia_port *port, pjmedia_frame *frame)
unsigned PJMEDIA_PIA_SRATE(const pjmedia_port_info *pia)
Definition: port.h:257
struct pjmedia_endpt pjmedia_endpt
Definition: pjmedia/types.h:186
pj_status_t pjmedia_endpt_create(pj_pool_factory *pf, pj_ioqueue_t *ioqueue, unsigned worker_cnt, pjmedia_endpt **p_endpt)
Definition: endpoint.h:127
pj_status_t pjmedia_endpt_destroy(pjmedia_endpt *endpt)
Definition: endpoint.h:168
pj_status_t pj_init(void)
short pj_int16_t
int pj_status_t
void pj_shutdown(void)
PJ_SUCCESS
void pj_caching_pool_destroy(pj_caching_pool *ch_pool)
void pj_caching_pool_init(pj_caching_pool *ch_pool, const pj_pool_factory_policy *policy, pj_size_t max_capacity)
#define PJ_LOG(level, arg)
pj_pool_factory_policy pj_pool_factory_default_policy
void * pj_pool_alloc(pj_pool_t *pool, pj_size_t size)
pj_pool_t * pj_pool_create(pj_pool_factory *factory, const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback)
void pj_pool_release(pj_pool_t *pool)
pj_uint32_t pj_elapsed_msec(const pj_timestamp *start, const pj_timestamp *stop)
pj_status_t pj_get_timestamp(pj_timestamp *ts)
#define PJ_ASSERT_RETURN(expr, retval)
#define PJ_UNUSED_ARG(arg)
#define PJ_ERR_MSG_SIZE
pj_str_t pj_strerror(pj_status_t statcode, char *buf, pj_size_t bufsize)
PJMEDIA main header file.
pj_pool_factory factory
Definition: frame.h:56
void * buf
Definition: frame.h:58
pj_size_t size
Definition: frame.h:59
Definition: port.h:378
pjmedia_port_info info
Definition: port.h:379

 


PJMEDIA small footprint Open Source media stack
Copyright (C) 2006-2008 Teluu Inc.