source: roaraudio/roarclients/roarphone.c @ 2570:2e2f30d32a85

Last change on this file since 2570:2e2f30d32a85 was 2570:2e2f30d32a85, checked in by phi, 15 years ago

use RE VIO for server stream, added some error messages

File size: 13.1 KB
Line 
1//roarphone.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2009
5 *
6 *  This file is part of roarclients a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  RoarAudio is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 */
24
25#include <roaraudio.h>
26#include <libroardsp/libroardsp.h>
27#include <libroareio/libroareio.h>
28
29#if defined(ROAR_HAVE_LIBSPEEX) && !defined(ROAR_HAVE_LIBSPEEXDSP)
30#define _SPEEX_API_OLD
31#elif defined(ROAR_HAVE_LIBSPEEX) && defined(ROAR_HAVE_LIBSPEEXDSP)
32#define _SPEEX_API_NEW
33#endif
34
35#ifdef _SPEEX_API_OLD
36#include <speex/speex_echo.h>
37#endif
38
39#define TIMEDIV  100
40
41#define DRIVER  "oss"
42
43// anti echo:
44#define AE_NONE      0
45#define AE_SIMPLE    1
46#define AE_SPEEX     2
47#define AE_ROARD     3
48
49#define DTX_F        25
50
51#define CON_NONE     0x00
52#define CON_CON      0x01
53#define CON_STREAM   0x02
54
55struct {
56 int antiecho;
57 int samples;
58 int transcode;
59 int64_t dtx_threshold;
60} g_conf;
61
62int dtx_counter = 0;
63
64struct {
65 int state;
66 struct roar_connection con;
67 struct roar_stream     stream;
68 struct roar_vio_calls * svio;
69} g_cons;
70
71struct roar_bixcoder transcoder[1];
72
73struct {
74 struct {
75  char key[ROAR_META_MAX_NAMELEN];
76  char value[LIBROAR_BUFFER_MSGDATA];
77 } tmp;
78 char * rn;
79 char * nick;
80 char * org;
81 char * email;
82 char * hp;
83 char * loc;
84 char * thumbnail;
85} g_meta;
86
87void usage (void) {
88 printf("roarphone [OPTIONS]...\n");
89
90 printf("\nServer Options:\n\n");
91
92 printf("  --server   SERVER    - Set server hostname\n"
93       );
94
95 printf("\nAudio Options:\n\n");
96 printf("  --rate     RATE      - Set sample rate\n"
97        "  --bits     BITS      - Set bits per sample\n"
98        "  --chans    CHANNELS  - Set number of channels\n"
99       );
100
101 printf("\nCodec Options:\n\n");
102 printf("  --codec    CODEC     - Set the codec\n"
103        "  --transcode          - Use local transcodeing\n"
104       );
105
106 printf("\nDriver Options:\n\n");
107 printf("  --driver   DRIVER    - Set the driver\n"
108        "  --device   DEVICE    - Set the device\n"
109       );
110
111 printf("\nGeneral Options:\n\n");
112 printf("  --antiecho AEMODE    - Set the anti echo mode\n"
113        "  --threshold DTXTHRES - Set the DTX threshold, disabled by default\n"
114        "  --help               - Show this help\n"
115       );
116
117 printf("\nMeta Data Options:\n\n");
118 printf("  --m-rn    REALNAME   - Sets the real name\n"
119        "  --m-nick  NICK       - Sets the nick name\n"
120        "  --m-email EMAIL      - Sets the email address\n"
121        "  --m-hp    HOMEPAGE   - Sets the homepage URL\n"
122        "  --m-thumbn THUMBNAIL - Sets a URL to a thumbnail\n"
123        "  --m-loc   LOCATION   - Sets the location (room number)\n"
124        "  --m-org ORGANIZATION - Sets the organization/company name\n"
125       );
126}
127
128int open_stream (struct roar_vio_calls * vio, char * server, struct roar_audio_info * info) {
129 int fh;
130
131 g_cons.svio = vio;
132
133 if ( !(g_cons.state & CON_CON) )
134  if ( roar_simple_connect(&(g_cons.con), server, "roarphone") == -1 )
135   return -1;
136
137 g_cons.state |= CON_CON;
138
139 // TODO: find out if this can be done more nicely by using VIOs
140
141 if ( (fh = roar_simple_new_stream_obj(&(g_cons.con), &(g_cons.stream),
142                                       info->rate, info->channels, info->bits, info->codec,
143                                       ROAR_DIR_BIDIR
144                                      )) == -1 )
145  return -1;
146
147 roar_vio_open_fh_socket(vio, fh);
148
149 g_cons.state |= CON_STREAM;
150
151 return 0;
152}
153
154#define _SET_META(ivar,itype) if ( (ivar) != NULL ) {  \
155                              meta.value = (ivar);     \
156                              meta.type  = (itype);    \
157                              roar_stream_meta_set(&(g_cons.con), &(g_cons.stream), ROAR_META_MODE_SET, &meta); \
158                              }
159int set_meta (void) {
160 struct roar_meta   meta;
161
162 meta.value  = g_meta.tmp.value;
163 meta.key[0] = 0;
164 meta.type   = ROAR_META_TYPE_NONE;
165
166 roar_stream_meta_set(&(g_cons.con), &(g_cons.stream), ROAR_META_MODE_CLEAR, &meta);
167
168 _SET_META(g_meta.thumbnail, ROAR_META_TYPE_THUMBNAIL);
169 _SET_META(g_meta.loc,       ROAR_META_TYPE_LOCATION);
170 _SET_META(g_meta.hp,        ROAR_META_TYPE_HOMEPAGE);
171 _SET_META(g_meta.org,       ROAR_META_TYPE_ORGANIZATION);
172
173 if ( g_meta.nick != NULL ) {
174  if ( g_meta.rn  != NULL ) {
175   snprintf(g_meta.tmp.value, LIBROAR_BUFFER_MSGDATA-1, "%s (%s)", g_meta.rn, g_meta.nick);
176   g_meta.tmp.value[LIBROAR_BUFFER_MSGDATA-1] = 0;
177   _SET_META(g_meta.tmp.value, ROAR_META_TYPE_AUTHOR);
178  } else {
179   _SET_META(g_meta.nick, ROAR_META_TYPE_AUTHOR);
180  }
181 } else {
182  if ( g_meta.rn  != NULL ) {
183   _SET_META(g_meta.rn, ROAR_META_TYPE_AUTHOR);
184  }
185 }
186
187 // TODO: make this more nice...
188 if ( g_meta.email != NULL ) {
189  if ( g_meta.nick != NULL ) {
190   if ( g_meta.rn != NULL ) {
191    snprintf(g_meta.tmp.value, LIBROAR_BUFFER_MSGDATA-1, "%s (%s) <%s>", g_meta.rn, g_meta.nick, g_meta.email);
192   } else {
193    snprintf(g_meta.tmp.value, LIBROAR_BUFFER_MSGDATA-1, "%s <%s>", g_meta.nick, g_meta.email);
194   }
195  } else {
196   if ( g_meta.rn != NULL ) {
197    snprintf(g_meta.tmp.value, LIBROAR_BUFFER_MSGDATA-1, "%s <%s>", g_meta.rn, g_meta.email);
198   } else {
199    snprintf(g_meta.tmp.value, LIBROAR_BUFFER_MSGDATA-1, "<%s>", g_meta.email);
200   }
201  }
202  g_meta.tmp.value[LIBROAR_BUFFER_MSGDATA-1] = 0;
203  _SET_META(g_meta.tmp.value, ROAR_META_TYPE_CONTACT);
204 }
205
206 return 0;
207}
208
209#ifdef _SPEEX_API_OLD
210int anti_echo_speex16(int16_t * buf, int16_t * aebuf, size_t len, struct roar_audio_info * info) {
211 static SpeexEchoState * state = NULL;
212 size_t samples = info->rate / TIMEDIV;
213 static int16_t * obuf = NULL;
214
215 if ( info->channels != 1 )
216  return -1;
217
218 if (len != samples)
219  return -1;
220
221 ROAR_DBG("anti_echo_speex16(*) = ?");
222
223 if ( state == NULL ) {
224  if ( (state = speex_echo_state_init(samples, 100*samples)) == NULL )
225   return -1;
226
227  // todo: set sample rate.
228 }
229
230 ROAR_DBG("anti_echo_speex16(*) = ?");
231
232 if ( obuf == NULL ) {
233  if ( (obuf = malloc(2*samples)) == NULL )
234   return -1;
235 }
236
237 ROAR_DBG("anti_echo_speex16(*) = ?");
238
239/*
240 speex_echo_cancellation(state, buf, aebuf, obuf);
241*/
242
243 speex_echo_cancel(state, buf, aebuf, obuf, NULL);
244
245 memcpy(buf, obuf, 2*samples);
246
247 ROAR_DBG("anti_echo_speex16(*) = 0");
248
249 return 0;
250}
251#endif
252
253int anti_echo16(int16_t * buf, int16_t * aebuf, size_t len, struct roar_audio_info * info) {
254 size_t i;
255
256 switch (g_conf.antiecho) {
257  case AE_NONE:
258    return 0;
259   break;
260  case AE_SIMPLE:
261    for (i = 0; i < len; i++)
262     buf[i] -= aebuf[i];
263    return 0;
264   break;
265#ifdef _SPEEX_API_OLD
266  case AE_SPEEX:
267    return anti_echo_speex16(buf, aebuf, len, info);
268   break;
269#endif
270  case AE_ROARD:
271    return 0;
272   break;
273  default:
274    return -1;
275   break;
276 }
277
278 return -1;
279}
280
281int zero_if_noise16 (int16_t * data, size_t samples) {
282 int64_t rms = roar_rms2_1_16(data, samples);
283
284 if ( rms < g_conf.dtx_threshold ) {
285  if ( dtx_counter ) {
286   dtx_counter--;
287  } else {
288   memset(data, 0, samples*2);
289  }
290 } else {
291  dtx_counter = DTX_F;
292 }
293
294 return 0;
295}
296
297int run_stream (struct roar_vio_calls * s0, struct roar_vio_calls * s1, struct roar_audio_info * info) {
298 size_t len;
299 void * outbuf, * micbuf;
300 ssize_t outlen, miclen;
301
302 ROAR_DBG("run_stream(*): g_conf.samples = %i, info->bits = %i", g_conf.samples, info->bits);
303 len = g_conf.samples * info->bits / 8;
304 ROAR_DBG("run_stream(*): len=%lu", (unsigned long) len);
305
306 if ( (outbuf = malloc(2*len)) == NULL )
307  return -1;
308
309 micbuf = outbuf + len;
310
311 while (1) {
312  if ( (miclen = roar_vio_read(s0, micbuf, len)) <= 0 )
313   break;
314
315  if ( g_conf.dtx_threshold > 0 )
316   if ( info->bits == 16 )
317    zero_if_noise16(micbuf, miclen/2);
318
319  if ( g_conf.transcode ) {
320   if ( roar_bixcoder_write_packet(transcoder, micbuf, miclen) == -1 )
321    break;
322  } else {
323   if ( roar_vio_write(s1, micbuf, miclen) != miclen )
324    break;
325  }
326
327  if ( g_conf.transcode ) {
328   ROAR_DBG("run_stream(*): outbuf=%p, len=%lu", outbuf, (unsigned long) len);
329   if ( roar_bixcoder_read_packet(transcoder, outbuf, len) == -1 )
330    break;
331
332   outlen = len;
333  } else {
334   if ( (outlen = roar_vio_read(s1, outbuf, len)) <= 0 )
335    break;
336  }
337
338  if ( g_conf.antiecho != AE_NONE && info->bits == 16 )
339   anti_echo16(outbuf, micbuf, ROAR_MIN(miclen, outlen)/2, info);
340
341  if ( roar_vio_write(s0, outbuf, outlen) != outlen )
342   break;
343 }
344
345 free(outbuf);
346
347 return 0;
348}
349
350int main (int argc, char * argv[]) {
351 struct roar_audio_info info = {.rate     = ROAR_RATE_DEFAULT,
352                                .bits     = ROAR_BITS_DEFAULT,
353                                .channels = ROAR_CHANNELS_DEFAULT,
354                                .codec    = ROAR_CODEC_DEFAULT
355                               };
356 struct roar_audio_info dinfo;
357 struct roar_vio_calls dvio, svio, svio_real;
358 char * driver   = DRIVER;
359 char * device   = NULL;
360 char * server   = NULL;
361 char * k;
362 int    i;
363
364 memset(&g_conf, 0, sizeof(g_conf));
365
366 g_conf.antiecho      = AE_ROARD;
367 g_conf.dtx_threshold = -1;
368
369 memset(&g_cons, 0, sizeof(g_cons));
370 g_cons.state = CON_NONE;
371
372 memset(&g_meta, 0, sizeof(g_meta));
373
374 for (i = 1; i < argc; i++) {
375  k = argv[i];
376
377  if ( strcmp(k, "--server") == 0 ) {
378   server = argv[++i];
379  } else if ( strcmp(k, "--rate") == 0 ) {
380   info.rate = atoi(argv[++i]);
381  } else if ( strcmp(k, "--bits") == 0 ) {
382   info.bits = atoi(argv[++i]);
383  } else if ( strcmp(k, "--channels") == 0 || strcmp(k, "--chans") == 0 ) {
384   info.channels = atoi(argv[++i]);
385  } else if ( strcmp(k, "--codec") == 0 ) {
386   info.codec = roar_str2codec(argv[++i]);
387  } else if ( strcmp(k, "--driver") == 0 ) {
388   driver = argv[++i];
389  } else if ( strcmp(k, "--device") == 0 ) {
390   device = argv[++i];
391  } else if ( strcmp(k, "--antiecho") == 0 ) {
392   k = argv[++i];
393   if ( !strcmp(k, "none") ) {
394    g_conf.antiecho = AE_NONE;
395   } else if ( !strcmp(k, "simple") ) {
396    g_conf.antiecho = AE_SIMPLE;
397   } else if ( !strcmp(k, "speex") ) {
398    g_conf.antiecho = AE_SPEEX;
399   } else if ( !strcmp(k, "roard") ) {
400    g_conf.antiecho = AE_ROARD;
401   } else {
402    fprintf(stderr, "Error: unknown mode: %s\n", k);
403    return 1;
404   }
405  } else if ( strcmp(k, "--threshold") == 0 ) {
406   g_conf.dtx_threshold = atol(argv[++i]);
407
408   // use threshold^2 or threshold < 0 for not using DTX
409   if ( g_conf.dtx_threshold > 0 )
410    g_conf.dtx_threshold *= g_conf.dtx_threshold;
411  } else if ( strcmp(k, "--transcode") == 0 ) {
412   g_conf.transcode = 1;
413
414  // META DATA:
415  } else if ( strcmp(k, "--m-rn") == 0 ) {
416   g_meta.rn = argv[++i];
417  } else if ( strcmp(k, "--m-nick") == 0 ) {
418   g_meta.nick = argv[++i];
419  } else if ( strcmp(k, "--m-email") == 0 ) {
420   g_meta.email = argv[++i];
421  } else if ( strcmp(k, "--m-hp") == 0 ) {
422   g_meta.hp = argv[++i];
423  } else if ( strcmp(k, "--m-thumbn") == 0 ) {
424   g_meta.thumbnail = argv[++i];
425  } else if ( strcmp(k, "--m-loc") == 0 ) {
426   g_meta.loc = argv[++i];
427  } else if ( strcmp(k, "--m-org") == 0 ) {
428   g_meta.org = argv[++i];
429
430
431  } else if ( strcmp(k, "--help") == 0 ) {
432   usage();
433   return 0;
434  } else {
435   fprintf(stderr, "Error: unknown argument: %s\n", k);
436   usage();
437   return 1;
438  }
439 }
440
441 // ignore errors, maybe it will work even if this fails
442 // (btw. it will never fail without crashing the rest of the app ;)
443 roar_libroar_set_server(server);
444
445 if ( g_conf.antiecho == AE_SPEEX ) {
446  ROAR_WARN("Speex Antiecho is obsolete and may be removed in future versions. Use --antiecho roard");
447 }
448
449 g_conf.samples = info.channels * info.rate / TIMEDIV;
450
451 memcpy(&dinfo, &info, sizeof(dinfo));
452
453 if ( g_conf.transcode ) {
454  dinfo.bits  = 16;
455  dinfo.codec = ROAR_CODEC_DEFAULT;
456
457  switch (info.codec) {
458   case ROAR_CODEC_ALAW:
459   case ROAR_CODEC_MULAW:
460     info.bits = 8;
461    break;
462   case ROAR_CODEC_ROAR_CELT:
463     info.bits = 16;
464    break;
465   case ROAR_CODEC_ROAR_SPEEX:
466     info.bits = 16;
467    break;
468  }
469 }
470
471 if ( roar_cdriver_open(&dvio, driver, device, &dinfo, ROAR_DIR_BIDIR) == -1 ) {
472  ROAR_ERR("Can not open sound card.");
473  return 1;
474 }
475
476 ROAR_DBG("main(*): CALL open_stream(&svio, server, &info)");
477 if ( open_stream(&svio_real, server, &info) == -1 ) {
478  ROAR_ERR("Can not open connection to server.");
479  roar_vio_close(&dvio);
480  return 2;
481 }
482 ROAR_DBG("main(*): RET");
483
484 if ( roar_vio_open_re(&svio, &svio_real) == -1 ) {
485  ROAR_ERR("Can not open connection to server (RE VIO).");
486  roar_vio_close(&dvio);
487  return 2;
488 }
489
490 set_meta();
491
492 if ( g_conf.transcode ) {
493  dinfo.codec = info.codec;
494
495  if ( roar_bixcoder_init(transcoder, &dinfo, &svio) == -1 ) {
496   roar_vio_close(&svio);
497   roar_vio_close(&dvio);
498   return 10;
499  }
500
501  g_conf.samples = 8 * roar_bixcoder_packet_size(transcoder, -1) / dinfo.bits;
502 }
503
504 ROAR_DBG("main(*): CALL run_stream(&dvio, &svio, &info);");
505 run_stream(&dvio, &svio, &info);
506 ROAR_DBG("main(*): RET");
507
508 roar_bixcoder_close(transcoder);
509
510 roar_vio_close(&svio);
511 roar_vio_close(&dvio);
512
513 roar_disconnect(&(g_cons.con));
514
515 return 0;
516}
517
518//ll
Note: See TracBrowser for help on using the repository browser.