BLOG | DOCUMENTATION | TRAC

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 /* $Id: aectest.c 5535 2017-01-19 10:31:38Z 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 
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 
40 static 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, 2=echo suppress, 3=WebRtc \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 
78 static 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  */
90 int 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;
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  break;
127  case 1:
128  opt = PJMEDIA_ECHO_SPEEX;
129  break;
130  case 2:
131  opt = PJMEDIA_ECHO_SIMPLE;
132  break;
133  case 3:
134  opt = PJMEDIA_ECHO_WEBRTC;
135  break;
136  default:
137  puts("Invalid algorithm");
138  puts(desc);
139  return 1;
140  }
141  }
142  break;
143  case 'r':
144  repeat = atoi(pj_optarg);
145  if (repeat < 1) {
146  puts("Invalid repeat count");
147  puts(desc);
148  return 1;
149  }
150  break;
151  case 'i':
152  interactive = 1;
153  break;
154  }
155  }
156 
157  if (argc - pj_optind != 3) {
158  puts("Error: missing argument(s)");
159  puts(desc);
160  return 1;
161  }
162 
163  /* Must init PJLIB first: */
164  status = pj_init();
165  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
166 
167  /* Must create a pool factory before we can allocate any memory. */
169 
170  /*
171  * Initialize media endpoint.
172  * This will implicitly initialize PJMEDIA too.
173  */
174  status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
175  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
176 
177  /* Create memory pool for our file player */
178  pool = pj_pool_create( &cp.factory, /* pool factory */
179  "wav", /* pool name. */
180  4000, /* init size */
181  4000, /* increment size */
182  NULL /* callback on error */
183  );
184 
185  /* Open wav_play */
186  status = pjmedia_wav_player_port_create(pool, argv[pj_optind], PTIME,
188  &wav_play);
189  if (status != PJ_SUCCESS) {
190  app_perror(THIS_FILE, "Error opening playback WAV file", status);
191  return 1;
192  }
193 
194  /* Open recorded wav */
195  status = pjmedia_wav_player_port_create(pool, argv[pj_optind+1], PTIME,
197  &wav_rec);
198  if (status != PJ_SUCCESS) {
199  app_perror(THIS_FILE, "Error opening recorded WAV file", status);
200  return 1;
201  }
202 
203  /* play and rec WAVs must have the same clock rate */
204  if (PJMEDIA_PIA_SRATE(&wav_play->info) != PJMEDIA_PIA_SRATE(&wav_rec->info)) {
205  puts("Error: clock rate mismatch in the WAV files");
206  return 1;
207  }
208 
209  /* .. and channel count */
210  if (PJMEDIA_PIA_CCNT(&wav_play->info) != PJMEDIA_PIA_CCNT(&wav_rec->info)) {
211  puts("Error: clock rate mismatch in the WAV files");
212  return 1;
213  }
214 
215  /* Create output wav */
216  status = pjmedia_wav_writer_port_create(pool, argv[pj_optind+2],
217  PJMEDIA_PIA_SRATE(&wav_play->info),
218  PJMEDIA_PIA_CCNT(&wav_play->info),
219  PJMEDIA_PIA_SPF(&wav_play->info),
220  PJMEDIA_PIA_BITS(&wav_play->info),
221  0, 0, &wav_out);
222  if (status != PJ_SUCCESS) {
223  app_perror(THIS_FILE, "Error opening output WAV file", status);
224  return 1;
225  }
226 
227  /* Create echo canceller */
228  status = pjmedia_echo_create2(pool, PJMEDIA_PIA_SRATE(&wav_play->info),
229  PJMEDIA_PIA_CCNT(&wav_play->info),
230  PJMEDIA_PIA_SPF(&wav_play->info),
231  tail_ms, latency_ms,
232  opt, &ec);
233  if (status != PJ_SUCCESS) {
234  app_perror(THIS_FILE, "Error creating EC", status);
235  return 1;
236  }
237 
238 
239  /* Processing loop */
240  play_frame.buf = pj_pool_alloc(pool, PJMEDIA_PIA_SPF(&wav_play->info)<<1);
241  rec_frame.buf = pj_pool_alloc(pool, PJMEDIA_PIA_SPF(&wav_play->info)<<1);
242  pj_get_timestamp(&t0);
243  for (i=0; i < repeat; ++i) {
244  for (;;) {
245  play_frame.size = PJMEDIA_PIA_SPF(&wav_play->info) << 1;
246  status = pjmedia_port_get_frame(wav_play, &play_frame);
247  if (status != PJ_SUCCESS)
248  break;
249 
250  status = pjmedia_echo_playback(ec, (short*)play_frame.buf);
251 
252  rec_frame.size = PJMEDIA_PIA_SPF(&wav_play->info) << 1;
253  status = pjmedia_port_get_frame(wav_rec, &rec_frame);
254  if (status != PJ_SUCCESS)
255  break;
256 
257  status = pjmedia_echo_capture(ec, (short*)rec_frame.buf, 0);
258 
259  //status = pjmedia_echo_cancel(ec, (short*)rec_frame.buf,
260  // (short*)play_frame.buf, 0, NULL);
261 
262  pjmedia_port_put_frame(wav_out, &rec_frame);
263  }
264 
265  pjmedia_wav_player_port_set_pos(wav_play, 0);
267  }
268  pj_get_timestamp(&t1);
269 
270  i = (int)pjmedia_wav_writer_port_get_pos(wav_out) / sizeof(pj_int16_t) * 1000 /
271  (PJMEDIA_PIA_SRATE(&wav_out->info) * PJMEDIA_PIA_CCNT(&wav_out->info));
272  PJ_LOG(3,(THIS_FILE, "Processed %3d.%03ds audio",
273  i / 1000, i % 1000));
274  PJ_LOG(3,(THIS_FILE, "Completed in %u msec\n", pj_elapsed_msec(&t0, &t1)));
275 
276  /* Destroy file port(s) */
277  status = pjmedia_port_destroy( wav_play );
278  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
279  status = pjmedia_port_destroy( wav_rec );
280  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
281  status = pjmedia_port_destroy( wav_out );
282  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
283 
284  /* Destroy ec */
286 
287  /* Release application pool */
288  pj_pool_release( pool );
289 
290  /* Destroy media endpoint. */
291  pjmedia_endpt_destroy( med_endpt );
292 
293  /* Destroy pool factory */
295 
296  /* Shutdown PJLIB */
297  pj_shutdown();
298 
299  if (interactive) {
300  char s[10], *dummy;
301  puts("ENTER to quit");
302  dummy = fgets(s, sizeof(s), stdin);
303  PJ_UNUSED_ARG(dummy);
304  }
305 
306  /* Done. */
307  return 0;
308 }
309 
PJMEDIA main header file.
#define PJ_ASSERT_RETURN(expr, retval)
pj_status_t pjmedia_port_get_frame(pjmedia_port *port, pjmedia_frame *frame)
pj_size_t size
Definition: frame.h:60
pjmedia_port_info info
Definition: port.h:366
void pj_pool_release(pj_pool_t *pool)
Definition: echo.h:80
pj_status_t pjmedia_port_destroy(pjmedia_port *port)
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)
struct pjmedia_endpt pjmedia_endpt
Definition: types.h:187
Definition: echo.h:72
pj_status_t pj_get_timestamp(pj_timestamp *ts)
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)
#define PJ_LOG(level, arg)
int pj_status_t
short pj_int16_t
pj_pool_factory_policy pj_pool_factory_default_policy
void * pj_pool_alloc(pj_pool_t *pool, pj_size_t size)
Definition: port.h:364
pj_status_t pjmedia_wav_player_port_set_pos(pjmedia_port *port, pj_uint32_t offset)
pj_pool_factory factory
pj_status_t pjmedia_echo_capture(pjmedia_echo_state *echo, pj_int16_t *rec_frm, unsigned options)
typedefPJ_BEGIN_DECL struct pjmedia_echo_state pjmedia_echo_state
Definition: echo.h:52
pj_status_t pjmedia_echo_playback(pjmedia_echo_state *echo, pj_int16_t *play_frm)
unsigned PJMEDIA_PIA_CCNT(const pjmedia_port_info *pia)
Definition: port.h:257
pj_status_t pjmedia_endpt_create(pj_pool_factory *pf, pj_ioqueue_t *ioqueue, unsigned worker_cnt, pjmedia_endpt **p_endpt)
Definition: endpoint.h:105
void pj_caching_pool_init(pj_caching_pool *ch_pool, const pj_pool_factory_policy *policy, pj_size_t max_capacity)
void * buf
Definition: frame.h:59
pj_status_t pjmedia_port_put_frame(pjmedia_port *port, pjmedia_frame *frame)
pj_str_t pj_strerror(pj_status_t statcode, char *buf, pj_size_t bufsize)
Definition: wav_port.h:50
Definition: echo.h:87
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)
unsigned PJMEDIA_PIA_SPF(const pjmedia_port_info *pia)
Definition: port.h:298
typedef int(PJ_THREAD_FUNC pj_thread_proc)(void *)
#define PJ_ERR_MSG_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)
unsigned PJMEDIA_PIA_SRATE(const pjmedia_port_info *pia)
Definition: port.h:244
pj_ssize_t pjmedia_wav_writer_port_get_pos(pjmedia_port *port)
unsigned PJMEDIA_PIA_BITS(const pjmedia_port_info *pia)
Definition: port.h:270
void pj_shutdown(void)
Definition: frame.h:56
PJ_SUCCESS
pj_status_t pj_init(void)
pj_status_t pjmedia_endpt_destroy(pjmedia_endpt *endpt)
Definition: endpoint.h:146
pj_status_t pjmedia_echo_destroy(pjmedia_echo_state *echo)
#define PJ_UNUSED_ARG(arg)
void pj_caching_pool_destroy(pj_caching_pool *ch_pool)
pj_uint32_t pj_elapsed_msec(const pj_timestamp *start, const pj_timestamp *stop)

 


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