diff intercom/intercomd.cpp @ 2:13be24d74cd2

import intercom-0.4.1
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 09:57:52 +0200
parents
children c6c5a16ce2f2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/intercom/intercomd.cpp	Fri Jun 25 09:57:52 2010 +0200
@@ -0,0 +1,1376 @@
+/* intercomd.cpp
+ *
+ * Copyright (C) DFS Deutsche Flugsicherung (2004, 2005). 
+ * All Rights Reserved.
+ * Author: Andre Adrian
+ *
+ * Voice over IP Intercom with Telephone conference and Acoustic Echo
+ * Cancellation using unicast RTP messages (RFC3550, RFC3551)
+ *
+ * Attention. This source code is for Linux only! You need:
+ *  same endian for CPU and soundcard for 16bit PCM audio sample
+ *  Open Source Sound (OSS) support
+ *  ALSA Sound support for hardware (2-channel) AEC
+ * 
+ * Format Sourcecode:
+indent -kr -i2 -nlp -ci2 -l72 -lc72 -nut intercomd.cpp
+ *
+ * global Replace (one replace per line only!)
+sed -i "s/PIT_MIN/G729_PIT_MIN/" *.h *.cpp 
+ *
+ */
+
+const char VERSION[] = "0.4.1";
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+/* Error handling */
+#include <assert.h>
+#include <errno.h>
+extern int errno;
+
+/* low level io */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <sys/time.h>
+
+/* Socket io */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/* IETF iLBC codec */
+extern "C" {
+  #include "ilbc/iLBC_define.h"
+  #include "ilbc/iLBC_encode.h"
+  #include "ilbc/iLBC_decode.h"
+}
+
+/* ITU-T G.711 codec */ 
+extern "C" {
+  #include "g711/g711.h"
+}
+
+/* 3GPP GSM codec */ 
+extern "C" {
+  #include "gsm/private.h"
+  #include "gsm/gsm.h"
+  #include "gsm/rpeltp.h"
+}
+
+/* ITU-T G.726 codec */ 
+extern "C" {
+  #include "g726/g726.h"
+  #include "g726/g726_rfc3551.h"
+} 
+
+/* Speex codec */
+extern "C" {
+  #include <speex/speex.h>
+  /* Note: if you use the RPM speex-devel on SuSE 9.1 change the
+   * header file above from <speex/speex.h> to <speex.h> */
+}
+
+#include "sinuston.h"
+
+typedef unsigned char Byte;
+typedef short Word16;
+
+#ifdef EFRSTUB
+/* EFR stubs - because of function name conflict in EFR and G.729 */
+class EFR {
+public:
+  int EFR::initEncode(int dtx_mode_) {
+    return 0;
+  };
+  int EFR::encode(              /* (o) len in byte of encoded_data */
+    Byte * encoded_data,        /* (o) The encoded bytes */
+    Word16 * data               /* (i) The signal block to encode */
+    ) {
+    return 0;
+  };
+  int EFR::initDecode() {
+    return 0;
+  };
+  int EFR::decode(              /* (o) Number of decoded samples or 0 for error */
+    Word16 * decoded_data,      /* (o) Decoded signal block */
+    Byte * encoded_data,        /* (i) Encoded bytes */
+    Word16 mode                 /* (i) 0=PL, 1=Normal */
+    ) {
+    return 0;
+  };
+};
+#else
+  /* 3GPP GSM-EFR codec */
+#include "efr/typedef.h"
+#include "efr/efr.h"
+#endif
+
+#ifdef G729STUB
+/* G.729 stubs - because of function name conflict in EFR and G.729 */
+class G729 {
+public:
+  int G729::initEncode(int dummy) {
+    return 0;
+  };
+
+  int G729::encode(             /* len in byte of encoded_data */
+    Byte * encoded_data,        /* (o) The encoded bytes */
+    Word16 * data               /* (i) The signal block to encode */
+    ) {
+    return 0;
+  };
+  int G729::initDecode() {
+    return 0;
+  };
+
+  int G729::decode(             /* (o) Number of decoded samples */
+    Word16 * decoded_data,      /* (o) Decoded signal block */
+    Byte * encoded_data,        /* (i) Encoded bytes */
+    Word16 mode                 /* (i) 0=PL, 1=Normal */
+    ) {
+    return 0;
+  };
+};
+#else
+  /* ITU-T G.729 codec */
+#include "g729/typedef.h"
+#include "g729/ld8k.h"
+#include "g729/g729.h"
+#endif
+
+
+/* intercom */
+#include "rtp.h"
+#include "udp.h"
+#include "tcp.h"
+#include "aec.h"
+#include "oss.h"
+#include "cirbuf.h"
+#include "intercomd.h"
+
+/* Design Constants */
+#define PARTNERS    5           /* maximum telephony partners */
+
+/* End of Design Constants */
+#define MAXPACKSIZE 160         /* maximum size of 20ms encoded audio */
+#define ILBC_MODE   20
+#define FRAMESIZE   (20*8*WIDEB*2)    /* compression frame size */
+#define MTU	    1460        /* max. MTU size without Headers */
+
+/* Globals */
+int wdisplay = NO;  /* export aec.cpp */
+
+
+/* audio */
+static CIRBUF mic_cirbuf;
+static CIRBUF spk_cirbuf;
+static AEC aec;
+static int channels = 1;
+static int audio_wr_fd = -1;  // Audio Output (speaker)
+static int packetlc = NO;
+static SINUSTON sinuston;   // Erweiterung Christian Dorge
+static int sinuson = 0;     // Sinustonaktivierung
+
+/* network transmitting to partners */
+static in_addr_t to_ip[PARTNERS];
+static RTP to_rtp[PARTNERS];
+static UDP to_udp[PARTNERS];
+static iLBC_Enc_Inst_t enc_ilbc[PARTNERS];
+static G726_state enc_g726[PARTNERS];
+static EFR enc_efr[PARTNERS];
+static G729 enc_g729[PARTNERS];
+static gsm enc_gsm[PARTNERS];
+static SpeexBits enc_spxbits[PARTNERS];
+static void *enc_spx[PARTNERS];
+static CIRBUF conf_cirbuf[PARTNERS];
+static char tx_buf[PARTNERS][MTU];
+static char *tx_pbuf[PARTNERS];
+static int tx_frame[PARTNERS];
+static int to_partners = 0;
+static int telephone_conference = NO;
+static int tx_frames = 4;
+static int tx_payloadt = PT_iLBC;       /* default Codec */
+static int tx_packsize = NO_OF_BYTES_20MS;
+static unsigned long to_ssrc = 0;
+
+/* network receiving from partners */
+static in_addr_t from_ip[PARTNERS];
+static unsigned long from_ssrc[PARTNERS];
+static int from_cnt[PARTNERS];
+static CIRBUF from_cirbuf[PARTNERS];
+static iLBC_Dec_Inst_t dec_ilbc[PARTNERS];
+static G726_state dec_g726[PARTNERS];
+static EFR dec_efr[PARTNERS];
+static G729 dec_g729[PARTNERS];
+static gsm dec_gsm[PARTNERS];
+static SpeexBits dec_spxbits[PARTNERS];
+static void *dec_spx[PARTNERS];
+static int from_partners = 0;
+static int from_rx_pt[PARTNERS];
+static RTP from_rtp[PARTNERS];
+
+/*----------------------------------------------------------------*
+*  iLBC Encoder interface function 
+*---------------------------------------------------------------*/
+
+short encode(                   /* (o) Number of bytes encoded */
+  iLBC_Enc_Inst_t * iLBCenc_inst,       /* (i/o) Encoder instance */
+  unsigned char *encoded_data,  /* (o) The encoded bytes */
+  short *data                   /* (i) The signal block to encode */
+  )
+{
+  float block[BLOCKL_MAX];
+  int k;
+
+  /* convert signal to float */
+
+  for (k = 0; k < iLBCenc_inst->blockl; k++)
+    block[k] = (float) data[k];
+
+  /* do the actual encoding */
+
+  iLBC_encode(encoded_data, block, iLBCenc_inst);
+
+  return (iLBCenc_inst->no_of_bytes);
+}
+
+short spx_encode(               /* (o) Number of bytes encoded */
+  SpeexBits *bits,
+  void *enc_state,
+  unsigned char *encoded_data,  /* (o) The encoded bytes */
+  short *data                   /* (i) The signal block to encode */
+  )
+{
+  float block[20*WIDEB*8];
+  unsigned int k;
+
+  /* convert signal to float */
+
+  for (k = 0; k < sizeof(block) / sizeof(float); k++)
+    block[k] = (float) data[k];
+
+  /* do the actual encoding */
+
+  speex_bits_reset(bits);
+  speex_encode(enc_state, block, bits);
+  int bytes = speex_bits_write(bits, (char *)encoded_data, 200);
+
+	// RPM speex-devel 1.0.3 has different size then Speex 1.1.7
+	if (bytes != SZ_SPX) {
+		static int errcnt = 0;
+		if (++errcnt > 0) {
+			errcnt = -250;
+  		syslog(LOG_WARNING, "%s:%d: %s: Error bytes = %d, Update Speex lib to 1.1\n", \
+  		__FILE__, __LINE__, __PRETTY_FUNCTION__, bytes); \
+		}
+	}
+	return SZ_SPX;
+}  
+
+void tx_buf_init(int i)
+{
+  tx_pbuf[i] = tx_buf[i];
+  tx_frame[i] = 0;
+}
+
+int encode_any(int i, Byte * encoded_data, short *buf)
+{
+  static int once = 0;
+  int len = 0;
+  switch (tx_payloadt) {
+  case PT_PCMA:
+    len = 160;
+    alaw_compress(len, buf, encoded_data);
+    break;
+  case PT_PCMU:
+    len = 160;
+    ulaw_compress(len, buf, encoded_data);
+    break;
+  case PT_GSM:
+    gsm_encode(enc_gsm[i], buf, encoded_data);
+    len = 33;
+    break;
+  case PT_EFR:
+    len = enc_efr[i].encode(encoded_data, buf);
+    break;
+  case PT_G729:
+    /* G.729 is a 10ms per frame codec */
+    enc_g729[i].encode(encoded_data, buf);
+    len = 2 * enc_g729[i].encode(encoded_data + 10, buf + 80);
+    break;
+  case PT_iLBC:
+    len = encode(&enc_ilbc[i], encoded_data, buf);
+    break;
+  case PT_SPX:
+    len = spx_encode(&enc_spxbits[i], enc_spx[i], encoded_data, buf);
+    if (!once) {
+      printf("speex encode len = %d\n", len);
+      once = 1;
+    }
+    break;
+  case PT_G726:
+    len = g726_encode(&enc_g726[i], encoded_data, buf);
+    break;
+  }
+  return len;
+}
+
+static int packet_loss = 0;
+
+int audio_read(int audio_rd_fd)
+{
+  /* fat software interrupt routine: read audio, send UDP packets */
+
+  short mic_buf[FRAGSIZE / 2];
+  if (1 == channels) {
+    size_t len = read(audio_rd_fd, mic_buf, FRAGSIZE);
+    return_if(len != FRAGSIZE, ERROR);
+
+    if (0 == to_partners) {
+      /* start assembling send packets only if we have a target */
+      return OKAY;
+    }
+
+    short spk_buf[FRAGSIZE / 2];
+    spk_cirbuf.pop((char *) spk_buf, FRAGSIZE);
+
+    /* Acoustic Echo Cancellation - using software buffers */
+    int i;
+    for (i = 0; i < FRAGSIZE / 2; ++i) {
+      mic_buf[i] = aec.doAEC(mic_buf[i], spk_buf[i]);
+    }
+  } else {
+    short mic2_buf[FRAGSIZE];
+    size_t len = read(audio_rd_fd, mic2_buf, 2 * FRAGSIZE);
+    return_if(len != 2 * FRAGSIZE, ERROR);
+
+    if (0 == to_partners) {
+      /* start assembling send packets only if we have a target */
+      return OKAY;
+    }
+
+    /* Acoustic Echo Cancellation - using hardware audio mixer */
+    int i;
+    for (i = 0; i < FRAGSIZE / 2; ++i) {
+      mic_buf[i] = aec.doAEC(mic2_buf[2 * i], mic2_buf[2 * i + 1]);
+    }
+  }
+
+  int ret = mic_cirbuf.push((char *) mic_buf, FRAGSIZE);
+  if (ret < 0) {
+    syslog(LOG_WARNING, "mic_cirbuf.push overrun\n");
+  }
+
+  if (mic_cirbuf.getlen() >= FRAMESIZE) {
+    /* My RFC3551 interpretation: Only one RTP Header for packets 
+     * with a number of frames */
+    int i;
+    for (i = 0; i < PARTNERS; ++i) {
+      if (to_ip[i] && 0 == tx_frame[i]) {
+        to_rtp[i].reset_csrc();
+        
+        /* add ssrc from partners in csrc field */
+        if (telephone_conference) {
+          int j;
+          for (j = 0; j < PARTNERS; ++j) {
+            /* do not mix in origin */
+            if (from_ssrc[j] && (to_ip[i] != from_ip[j])) {     
+              to_rtp[i].add_csrc(from_ssrc[j]);
+            }
+          }
+        }
+        /* put RTP header */
+        tx_pbuf[i] = RTP_network_copy(tx_pbuf[i], &to_rtp[i]);
+      }
+    }
+
+    /* put payload (audio) */
+    short micbuf[FRAMESIZE / 2];
+    mic_cirbuf.pop((char *) micbuf, FRAMESIZE);
+
+    if (telephone_conference) {
+      /* telephone conference mix - everybody from/to everybody else */
+      short from_buf[PARTNERS][FRAMESIZE / sizeof(short)];
+      short sum_buf[FRAMESIZE / sizeof(short)];
+      int i, k;
+      unsigned int j;
+
+      /* get audio from other partners */
+      for (i = 0; i < PARTNERS; ++i) {
+        conf_cirbuf[i].pop((char *) from_buf[i], FRAMESIZE);
+      }
+
+      for (i = 0; i < PARTNERS; ++i) {
+        if (to_ip[i]) {
+          for (j = 0; j < FRAMESIZE / sizeof(short); ++j) {
+            /* mix */
+            long sum = micbuf[j];
+            for (k = 0; k < PARTNERS; ++k) {
+              if (to_ip[i] != from_ip[k]) {     /* do not mix in origin */
+                sum += from_buf[k][j];
+              }
+            }
+            /* clip */
+            if (sum > 32767) {
+              sum_buf[j] = 32767;
+            } else if (sum < -32767) {
+              sum_buf[j] = -32767;
+            } else {
+              sum_buf[j] = sum;
+            }
+          }
+          /* do encoding (audio compression) */
+          Byte encoded_data[MAXPACKSIZE];
+          int len = encode_any(i, encoded_data, sum_buf);
+
+          /* distribute to transmit buffers */
+          memcpy(tx_pbuf[i], encoded_data, len);
+          tx_pbuf[i] += len;
+          assert(tx_pbuf[i] - tx_buf[i] <= MTU);
+        }
+      }
+    } else {
+      /* intercom conference mixing - central node from/to other nodes */
+      /* do encoding (audio compression) */
+      Byte encoded_data[MAXPACKSIZE];
+      int len = encode_any(0, encoded_data, micbuf);
+
+      /* distribute to transmit buffers */
+      int i;
+      for (i = 0; i < PARTNERS; ++i) {
+        if (to_ip[i]) {
+          memcpy(tx_pbuf[i], encoded_data, len);
+          tx_pbuf[i] += len;
+          assert(tx_pbuf[i] - tx_buf[i] <= MTU);
+        }
+      }
+    }
+
+    /* transmit data packet(s) */
+    for (i = 0; i < PARTNERS; ++i) {
+      if (to_ip[i] && ++tx_frame[i] >= tx_frames) {
+
+        if (random() >= packet_loss) {  // simulate packet loss
+          to_udp[i].send(tx_buf[i], tx_pbuf[i] - tx_buf[i]);
+        }
+
+        /* prepare next go */
+        tx_buf_init(i);
+        to_rtp[i].next(tx_frames * 20 * 8);
+      }
+    }
+  }
+  return OKAY;
+}
+
+void partner_timeout()
+{
+  /* Delete old from_ssrc[] entries - this is not very quick! */
+  int i;
+  for (i = 0; i < PARTNERS; ++i) {
+    if (from_ssrc[i] && from_cnt[i] == 0) {
+      char s[20];
+      print_gui("d %s\n", iptoa(s, from_ip[i]));
+      from_ssrc[i] = 0;
+      from_ip[i] = 0;
+      from_rx_pt[i] = -1;
+      from_rtp[i].reset_csrc();
+      --from_partners;
+      if (0 == from_partners) {
+        /* no more demand for PCM out */
+        close(audio_wr_fd);
+        audio_wr_fd = -1;
+      }
+    }
+    from_cnt[i] = 0;
+  }
+}
+
+
+int partner_lookup(unsigned long ssrc, in_addr_t ip)
+{
+  /* search */
+  int i;
+  for (i = 0; i < PARTNERS; ++i) {
+    if (from_ssrc[i] == ssrc) {
+      ++from_cnt[i];
+      return i;                 /* old entry */
+    }
+  }
+  /* add new entry */
+  for (i = 0; i < PARTNERS; ++i) {
+    if (0 == from_ssrc[i]) {
+      if (0 == from_partners) {
+        spk_cirbuf.init();
+      }
+      from_ssrc[i] = ssrc;
+      from_ip[i] = ip;
+      from_cnt[i] = 1;
+
+      initDecode(dec_ilbc + i, ILBC_MODE, 1);
+      enc_efr[i].initDecode();
+      enc_g729[i].initDecode();
+      rpeltp_delete(dec_gsm[i]);
+      dec_gsm[i] = rpeltp_init();
+      speex_bits_destroy(dec_spxbits + i);
+      speex_decoder_destroy(dec_spx[i]);
+      speex_bits_init(dec_spxbits + i);
+      dec_spx[i] = speex_decoder_init(&speex_wb_mode);
+      int value = SPX_ENH;
+      speex_decoder_ctl(dec_spx[i], SPEEX_SET_ENH, &value);
+
+      from_cirbuf[i].init();
+      conf_cirbuf[i].init();
+      ++from_partners;
+      char s[20];
+      print_gui("r %s\n", iptoa(s, ip));
+      
+      printf("from_ssrc[%d] = %08lx\n", i, from_ssrc[i]);
+      return i;
+    }
+  }
+  return ERROR;
+}
+
+/*----------------------------------------------------------------*
+*  iLBC Decoder interface function 
+*---------------------------------------------------------------*/
+
+short decode(                   /* (o) Number of decoded samples */
+  iLBC_Dec_Inst_t * iLBCdec_inst,       /* (i/o) Decoder instance */
+  short *decoded_data,          /* (o) Decoded signal block */
+  unsigned char *encoded_data,  /* (i) Encoded bytes */
+  short mode                    /* (i) 0=PL, 1=Normal */
+  )
+{
+  int k;
+  float decblock[BLOCKL_MAX], dtmp;
+
+  /* check if mode is valid */
+
+  if (mode < 0 || mode > 1) {
+    printf("\nERROR - Wrong mode - 0, 1 allowed\n");
+    exit(3);
+  }
+
+  /* do actual decoding of block */
+  iLBC_decode(decblock, encoded_data, iLBCdec_inst, mode);
+
+  /* convert to short */
+
+  for (k = 0; k < iLBCdec_inst->blockl; k++) {
+    dtmp = decblock[k];
+
+    if (dtmp < MIN_SAMPLE)
+      dtmp = MIN_SAMPLE;
+    else if (dtmp > MAX_SAMPLE)
+      dtmp = MAX_SAMPLE;
+    decoded_data[k] = (short) dtmp;
+  }
+
+  return (iLBCdec_inst->blockl);
+}
+
+short spx_decode(               /* (o) Number of decoded samples */
+  SpeexBits *bits,
+  void *dec_state,
+  short *decoded_data,          /* (o) Decoded signal block */
+  unsigned char *encoded_data,  /* (i) Encoded bytes */
+  short mode                    /* (i) 0=PL, 1=Normal */
+  )
+{
+  unsigned int k;
+  float decblock[20*WIDEB*8];
+
+  /* do actual decoding of block */
+  if (bits)
+    speex_bits_read_from(bits, (char *)encoded_data, SZ_SPX);
+  speex_decode(dec_state, bits, decblock);
+
+  /* convert to short */
+
+  for (k = 0; k < sizeof(decblock) / sizeof(float); k++) {
+    decoded_data[k] = (short)decblock[k];
+  }
+
+  return 20*WIDEB*8;
+}
+
+void audio_write()
+{
+  int i;
+  unsigned int j;
+  short from_buf[PARTNERS][FRAGSIZE / sizeof(short)];
+  short sum_buf[FRAGSIZE / sizeof(short)];
+  int playout = 0;      // is 1 if we have audio, not silence
+
+  /* get audio */
+  for (i = 0; i < PARTNERS; ++i) {
+    int ret = from_cirbuf[i].pop((char *) from_buf[i], FRAGSIZE);
+    if (OKAY == ret) playout = 1;
+    if (packetlc && ERROR == ret && from_ssrc[i] && audio_wr_fd >= 0) {
+      // printf("audio_write() PLC ch=%d ret=%d \n", i, ret);
+
+      /* do Packet Loss Concealment */
+      short decoded_data[FRAMESIZE / 2];
+      int len;
+      switch (from_rx_pt[i]) {
+      case PT_iLBC:
+        len = decode(dec_ilbc + i, decoded_data, NULL, 0);
+        from_cirbuf[i].push((char *) decoded_data, 2 * len);
+        from_cirbuf[i].pop((char *) from_buf[i], FRAGSIZE);
+        playout = 1;
+        break;
+      case PT_SPX:
+        Byte spx_dummy[SZ_SPX];
+        memset(spx_dummy, 0, SZ_SPX);
+        len = spx_decode(NULL, dec_spx[i], 
+         decoded_data, spx_dummy, 1);
+        from_cirbuf[i].push((char *) decoded_data, 2 * len);
+        from_cirbuf[i].pop((char *) from_buf[i], FRAGSIZE);
+        playout = 1;
+        break;
+      case PT_EFR:
+        Byte efr_dummy[31];
+        memset(efr_dummy, 0, 31);
+        efr_dummy[0] = 0xC0;
+        len = dec_efr[i].decode(decoded_data, efr_dummy, 0);
+        from_cirbuf[i].push((char *) decoded_data, 2 * len);
+        from_cirbuf[i].pop((char *) from_buf[i], FRAGSIZE);
+        playout = 1;
+        break;
+      case PT_G729:
+        Byte g729_dummy[20];
+        memset(g729_dummy, 0, 20);
+        len = dec_g729[i].decode(decoded_data, g729_dummy, 0);
+        len +=
+          dec_g729[i].decode(decoded_data + 80, g729_dummy + 10, 0);
+        from_cirbuf[i].push((char *) decoded_data, 2 * len);
+        from_cirbuf[i].pop((char *) from_buf[i], FRAGSIZE);
+        playout = 1;
+        break;
+      }
+    }
+  }
+
+  // if (!playout) return; // knistern with AD1985
+  
+  // audio mixing with sinuston
+  for (j = 0; j < FRAGSIZE / sizeof(short); ++j) {
+    /* mix */
+    long sum = 0;
+    for (i = 0; i < PARTNERS; ++i) {
+      sum += from_buf[i][j];
+    }
+    /* clip */
+    if (sum > 32767) {
+      sum = 32767;
+    } else if (sum < -32767) {
+      sum = -32767;
+    }
+    if (sinuson && playout) {
+      // Sinuston dazumischen 11jul2005 adrian
+      sum = sinuston.mischen(sum);
+    }      
+    sum_buf[j] = sum; 
+  }
+
+  if (1 == channels) {
+    if (from_partners > 0) {
+      /* save for 1-channel AEC */
+      int ret = spk_cirbuf.push((char *) sum_buf, FRAGSIZE);
+      if (ret < 0) {
+        /* syslog(LOG_WARNING, "spk_cirbuf.push overrun\n"); */
+      }
+    }
+    if (audio_wr_fd < 0) {
+      /* late audio write open */
+      audio_wr_fd = audio_init("/dev/dsp", channels, O_WRONLY);
+    }
+    if (audio_wr_fd >= 0) {
+      write(audio_wr_fd, sum_buf, FRAGSIZE);
+    }
+  } else {
+    short sum2_buf[FRAGSIZE];
+    int i;
+    for (i = 0; i < FRAGSIZE / 2; ++i) {
+      sum2_buf[2 * i] = 0;              /* left channel nothing */
+      sum2_buf[2 * i + 1] = sum_buf[i]; /* right channel spk */
+    }
+    if (audio_wr_fd < 0) {
+      /* late audio write open */
+      audio_wr_fd = audio_init("/dev/dsp", channels, O_WRONLY);
+    }
+    if (audio_wr_fd >= 0) {
+      write(audio_wr_fd, sum2_buf, 2 * FRAGSIZE);
+    }
+  }
+}
+
+static unsigned short rtp_port = 5004;  /* see RFC3551 */
+
+int udp_read(int udp_fd)
+{
+  /* software interrupt routine */
+  char buf[MTU];
+  struct sockaddr_in from_sock;
+  socklen_t from_socklen = sizeof(sockaddr_in);
+  RTP rtp_in;
+
+  int len = recvfrom(udp_fd, buf, MTU, 0,
+    (struct sockaddr *) &from_sock, &from_socklen);
+
+  /* check Port number */
+  in_addr_t from_ip = ntohl(from_sock.sin_addr.s_addr);
+  in_port_t from_port = ntohs(from_sock.sin_port);
+  return_if(from_port != rtp_port, ERROR);
+
+  char *pbuf = RTP_host_copy(&rtp_in, buf);
+  len -= (pbuf - buf);
+  return_if(len < 0, ERROR);
+  int rc = rtp_in.check();
+  return_if(rc, ERROR);
+
+  int partner = partner_lookup(rtp_in.getssrc(), from_ip);
+  return_if(partner < 0, ERROR);
+
+  /* Avoid Megaphone (station with itself) audio loop */
+  if (to_ssrc == rtp_in.getssrc()) {
+    // return ERROR;
+  }
+  
+  /* Avoid telephone conference style audio loop (multipath) */
+  int i;
+  for (i = 0; i < PARTNERS; ++i) {
+    /* drop packets with SSRC the same as an known CSRC */
+    if (i != partner && from_rtp[i].find_csrc(rtp_in.getssrc())) {
+      return ERROR;
+    }
+  }
+  /* to avoid silence with audio loops: Only if above test passed
+   * copy rtp_in */
+  from_rtp[partner] = rtp_in;   /* die CSRC */
+
+  int rx_frames = 0;
+  int rx_pt = rtp_in.get_pt();
+  /* detect number of frames and dynamic payload type from length */
+  switch (len) {
+  case 1 * 20:
+    rx_frames = 1;
+    break;                      /* G729 */
+  case 1 * 33:
+    rx_frames = 1;
+    break;                      /* GSM */
+  case 1 * 31:
+    rx_frames = 1;
+    rx_pt = PT_EFR;
+    break;
+  case 1 * 38:
+    rx_frames = 1;
+    rx_pt = PT_iLBC;
+    break;
+  case 1 * SZ_SPX:
+    rx_frames = 1;
+    rx_pt = PT_SPX;
+    break;
+  case 1 * 80:                 /* or 4*20 */
+    switch (rx_pt) {
+    case PT_G729:
+      rx_frames = 4;
+      break;
+    default:
+      rx_frames = 1;
+      rx_pt = PT_G726;
+      break;
+    }
+    break;
+  case 1 * 160:                /* or 2*80 */
+    switch (rx_pt) {
+    case PT_PCMA:
+    case PT_PCMU:
+      rx_frames = 1;            /* G711 */
+      break;
+    default:
+      rx_frames = 2;            /* G726 32kbps */
+      rx_pt = PT_G726;
+      break;
+    }
+    break;
+
+  case 2 * 20:
+    rx_frames = 2;
+    break;
+  case 2 * 33:
+    rx_frames = 2;
+    break;
+  case 2 * 31:
+    rx_frames = 2;
+    rx_pt = PT_EFR;
+    break;
+  case 2 * 38:
+    rx_frames = 2;
+    rx_pt = PT_iLBC;
+    break;
+  case 2 * SZ_SPX:
+    rx_frames = 2;
+    rx_pt = PT_SPX;
+    break;
+  case 2 * 160:                /* or 4*80 */
+    switch (rx_pt) {
+    case PT_PCMA:
+    case PT_PCMU:
+      rx_frames = 2;            /* G711 */
+      break;
+    default:
+      rx_frames = 4;            /* G726 32kbps */
+      rx_pt = PT_G726;
+      break;
+    }
+    break;
+
+  case 3 * 20:
+    rx_frames = 3;
+    break;
+  case 3 * 33:
+    rx_frames = 3;
+    break;
+  case 3 * 31:
+    rx_frames = 3;
+    rx_pt = PT_EFR;
+    break;
+  case 3 * 38:
+    rx_frames = 3;
+    rx_pt = PT_iLBC;
+    break;
+  case 3 * SZ_SPX:
+    rx_frames = 3;
+    rx_pt = PT_SPX;
+    break;
+  case 3 * 80:
+    rx_frames = 3;
+    rx_pt = PT_G726;
+    break;
+  case 3 * 160:
+    rx_frames = 3;
+    break;
+
+  case 4 * 33:
+    rx_frames = 4;
+    break;
+  case 4 * 31:
+    rx_frames = 4;
+    rx_pt = PT_EFR;
+    break;
+  case 4 * 38:
+    rx_frames = 4;
+    rx_pt = PT_iLBC;
+    break;
+  case 4 * SZ_SPX:
+    rx_frames = 4;
+    rx_pt = PT_SPX;
+    break;
+  case 4 * 160:
+    rx_frames = 4;
+    break;
+  };
+  return_if(0 == rx_frames, ERROR);
+
+  from_rx_pt[partner] = rx_pt;
+  for (i = 0; i < rx_frames; ++i) {
+    /* do decoding (audio decompression) */
+    short decoded_data[FRAMESIZE / 2];
+    switch (rx_pt) {
+    case PT_PCMA:
+      len = 160;
+      alaw_expand(len, (Byte *) pbuf, decoded_data);
+      pbuf += 160;
+      break;
+    case PT_PCMU:
+      len = 160;
+      ulaw_expand(len, (Byte *) pbuf, decoded_data);
+      pbuf += 160;
+      break;
+    case PT_GSM:
+      len = 160;
+      gsm_decode(dec_gsm[partner], (Byte *) pbuf, decoded_data);
+      pbuf += 33;
+      break;
+    case PT_EFR:
+      len = dec_efr[partner].decode(decoded_data, (Byte *) pbuf, 1);
+      pbuf += 31;
+      break;
+    case PT_G729:
+      len = dec_g729[partner].decode(decoded_data, (Byte *) pbuf, 1);
+      len +=
+        dec_g729[partner].decode(decoded_data + 80,
+        (Byte *) (pbuf + 10), 1);
+      pbuf += 20;
+      break;
+    case PT_iLBC:
+      len = decode(dec_ilbc + partner, decoded_data, (Byte *) pbuf, 1);
+      pbuf += 38;
+      break;
+    case PT_SPX:
+      len = spx_decode(dec_spxbits + partner, dec_spx[partner], 
+       decoded_data, (Byte *) pbuf, 1);
+      pbuf += SZ_SPX;
+      break;
+    case PT_G726:
+      len =
+        g726_decode(dec_g726 + partner, decoded_data, (Byte *) pbuf, 1);
+      pbuf += 80;
+      break;
+    }
+    return_if(len != FRAMESIZE / 2, ERROR);
+    
+    int ret = from_cirbuf[partner].push((char *) decoded_data, 2 * len);
+    if (ret < 0) {
+      syslog(LOG_WARNING, "from_cirbuf[%d].push overrun=%d\n", partner,
+        ret / FRAGSIZE);
+    }
+    if (telephone_conference) {
+      ret = conf_cirbuf[partner].push((char *) decoded_data, 2 * len);
+      if (ret < 0) {
+        /* syslog(LOG_WARNING, "conf_cirbuf[%d].push overrun=%d\n", 
+           partner, ret/FRAGSIZE); */
+      }
+    }
+  }
+  return OKAY;
+}
+
+void spx_encoder_init(int i)
+{
+  speex_bits_init(enc_spxbits + i);
+  enc_spx[i] = speex_encoder_init(&speex_wb_mode);
+  int value = SPX_QUALITY;
+  speex_encoder_ctl(enc_spx[i], SPEEX_SET_QUALITY, &value);
+  value = SPX_BITRATE;
+  speex_encoder_ctl(enc_spx[i], SPEEX_SET_BITRATE, &value);
+  value = SPX_COMPLEXITY;
+  speex_encoder_ctl(enc_spx[i], SPEEX_SET_COMPLEXITY, &value);
+}
+
+// user command
+void command(char *cmd, int udp_fd)
+{
+  /* delete special characters like \r, \n */
+  unsigned int i;
+  for (i = 0; i < strlen(cmd); ++i) {
+    if (cmd[i] < ' ') {         /* hack: assume ASCII coding */
+      cmd[i] = 0;
+      break;
+    }
+  }
+  in_addr_t ip;
+  switch (cmd[0]) {
+  default:
+    printf("voipconf commands:\n"
+      "c IP-Adress           - connect to IP-Adress\n"
+      "h IP-Adress           - hang-up IP-Adress\n\n");
+    fflush(stdout);
+    break;
+  case 'p':
+    /* do nothing */
+    break;
+  case 'c':
+    ip = atoip(cmd + 2);
+    for (i = 0; i < PARTNERS; ++i) {
+      if (0 == to_ip[i]) {
+        if (0 == to_partners) {
+          mic_cirbuf.init();
+        }
+        tx_buf_init(i);
+        to_ip[i] = ip;
+        to_rtp[i].init(tx_payloadt, to_ssrc);
+        to_udp[i].send_init(cmd + 2, rtp_port, udp_fd);
+        switch (tx_payloadt) {
+        case PT_EFR:
+          enc_efr[i].initEncode(0);
+          break;
+        case PT_G729:
+          enc_g729[i].initEncode(0);
+          break;
+        case PT_iLBC:
+          initEncode(enc_ilbc + i, ILBC_MODE);
+          break;
+        case PT_GSM:
+          rpeltp_delete(enc_gsm[i]);
+          enc_gsm[i] = rpeltp_init();
+          break;
+        case PT_SPX:
+          speex_bits_destroy(enc_spxbits + i);
+          speex_encoder_destroy(enc_spx[i]);
+          spx_encoder_init(i);
+          break;
+        }
+        ++to_partners;
+        break;
+      }
+    }
+    break;
+  case 'h':
+    ip = atoip(cmd + 2);
+    for (i = 0; i < PARTNERS; ++i) {
+      if (ip == to_ip[i]) {
+        to_ip[i] = 0;
+        to_udp[i].send_close();
+        --to_partners;
+
+        print_gui("%s\n", cmd); /* Tcl/Tk needs \n */
+        break;
+      }
+    }
+    break;
+  }
+
+  /* syslog(LOG_WARNING, "cmd=%s to_partners=%d\n", cmd, to_partners); */
+}
+
+#define CMDLEN	80
+
+// local signalling from intercom.tcl
+int gui_read(int gui_fd, int udp_fd)
+{
+  char cmd[CMDLEN];
+
+  int len = read(gui_fd, cmd, CMDLEN);
+
+  if (len <= 0) {
+    syslog(LOG_WARNING, "gui_read() close\n");
+    int ret = shutdown(gui_fd, SHUT_RDWR);
+    assert_errno(ret >= 0);
+
+    return -1;
+  }
+
+  command(cmd, udp_fd);
+
+  return gui_fd;
+}
+
+static int gui_fd = -1;
+
+int print_gui(const char *fmt, ...)
+{
+/* in fmt: Formatstring as printf  */
+/* in ...: Parameter(s) as printf  */
+
+  if (gui_fd >= 0) {
+    char s[MTU];
+    va_list ap;
+    va_start(ap, fmt);
+    (void) vsnprintf(s, MTU, fmt, ap);
+    va_end(ap);
+
+    int len = strlen(s);
+
+    return write(gui_fd, s, len);
+  } else {
+    return ERROR;
+  }
+}
+
+struct timeval difftimeval(struct timeval time1, struct timeval time2)
+{
+  struct timeval diff;
+
+  diff.tv_usec = time1.tv_usec - time2.tv_usec;
+  if (diff.tv_usec < 0) {
+    diff.tv_usec += 1000000;
+    time2.tv_usec += 1;
+  }
+  diff.tv_sec = time1.tv_sec - time2.tv_sec;
+
+  return diff;
+}
+
+float dB2q(float dB)
+{
+  /* Dezibel to Ratio */
+  return powf(10.0f, dB / 20.0f);
+}
+float q2dB(float q)
+{
+  /* Ratio to Dezibel */
+  return 20.0f * log10f(q);
+}
+
+/* program main loop. OS Event handler */
+int loop(int audio_rd_fd, int udp_fd, int gui_listen_fd)
+{
+
+  struct timeval timeout;
+  fd_set read_fds;
+  int max_fd = 64;              /* should be max(fd, ..) + 1 */
+  static struct timeval last_partner_timeout;
+  static struct timeval last_getambient;
+  static struct timeval last_spkout;
+  static long remainder_spkout = 0;
+  struct timezone tz;
+  static int mic_int, spk_int, time_int;
+  
+  openlog(NULL, LOG_PERROR, LOG_WARNING);
+
+  gettimeofday(&last_partner_timeout, &tz);
+  gettimeofday(&last_getambient, &tz);
+  gettimeofday(&last_spkout, &tz);
+  for (;;) {
+    timeout.tv_sec = 0;
+    timeout.tv_usec = FRAGTIME * 1000;
+    FD_ZERO(&read_fds);
+    FD_SET(audio_rd_fd, &read_fds);
+    FD_SET(udp_fd, &read_fds);
+    FD_SET(gui_listen_fd, &read_fds);
+    if (gui_fd >= 0) {
+      FD_SET(gui_fd, &read_fds);
+    }
+    // int ret = select(max_fd, &read_fds, NULL, NULL, &timeout);
+    int ret = select(max_fd, &read_fds, NULL, NULL, NULL);
+    assert_errno(ret >= 0);
+
+    if (FD_ISSET(audio_rd_fd, &read_fds)) {
+      audio_read(audio_rd_fd);
+      ++mic_int;
+    }
+    if (FD_ISSET(udp_fd, &read_fds)) {
+      udp_read(udp_fd);
+    }
+    if (FD_ISSET(gui_listen_fd, &read_fds)) {
+      gui_fd = tcp_server_init2(gui_listen_fd);
+    }
+    if (gui_fd >= 0) {
+      if (FD_ISSET(gui_fd, &read_fds)) {
+        gui_fd = gui_read(gui_fd, udp_fd);
+      }
+    }
+#if 0
+    if ((mic_int % (5000 / FRAGTIME)) == 0) {
+      printf("int: mic %d spk %d time %d\n", mic_int, spk_int,
+        time_int);
+    }
+#endif
+
+    struct timeval now, diff;
+    if (packetlc) {
+      gettimeofday(&now, &tz);
+      diff = difftimeval(now, last_spkout);
+      if (diff.tv_usec + remainder_spkout >= FRAGTIME * 1000) {
+        ++time_int;
+        last_spkout = now;
+        /* Linux time resolution is more coarse (15ms) then mic 
+         * time interval (4ms), therefore remainder calculation */
+        remainder_spkout =
+          diff.tv_usec + remainder_spkout - FRAGTIME * 1000;
+        if (audio_wr_fd < 0) {
+          /* new output: fill up Open Sound System (OSS) buffer 
+           * to compensate Operation System Jitter */
+          // audio_write();
+        }
+        audio_write();
+        ++spk_int;
+      }
+    } else {
+       /* because of problems with Intel ICH5/Analog Devices AD1985
+        * not together with audio_read() */
+      audio_write();
+    }
+
+    gettimeofday(&now, &tz);
+    diff = difftimeval(now, last_partner_timeout);
+    if (diff.tv_usec >= 160000) {       /* 2*PACKETDURATION in usec */
+      last_partner_timeout = now;
+      partner_timeout();
+    }
+#if 0
+    gettimeofday(&now, &tz);
+    diff = difftimeval(now, last_getambient);
+    if (diff.tv_sec >= 2) {
+      last_getambient = now;
+      // if (to_partners > 0) {
+        // float ambient = aec.getambient();
+        // float ambientdB = q2dB(ambient / 32767.0f);
+        // syslog(LOG_WARNING, "Ambient = %2.0f dB\n", ambientdB);
+        float xfastdB = q2dB(aec.xfast / 32767.0f);
+        float dfastdB = q2dB(aec.dfast / 32767.0f);
+        float efastdB = q2dB(aec.efast / 32767.0f);
+        float xslowdB = q2dB(aec.xslow / 32767.0f);
+        float dslowdB = q2dB(aec.dslow / 32767.0f);
+        syslog(LOG_WARNING, "ratio= %2.0f / %2.0f / %2.0f / %2.0f e= %2.0f\n", 
+         dfastdB, xfastdB, dslowdB, xslowdB, efastdB);
+      // }
+    }
+#endif    
+  }
+  return ERROR;
+}
+
+int main(int argc, char *argv[])
+{
+  int i;
+  float ambient = 0.0f;
+  float dB;
+
+#if WIDEB==1      
+  tx_payloadt = PT_iLBC;
+  tx_packsize = SZ_iLBC;
+  printf("Narrowband DFS VoIP Intercom %s\n", VERSION);
+#else
+  tx_payloadt = PT_SPX;
+  tx_packsize = SZ_SPX;
+  printf("Wideband DFS VoIP Intercom %s\n", VERSION);
+#endif
+  for (i = 1; i < argc && '-' == argv[i][0]; ++i) {
+    switch (argv[i][1]) {
+    case 'a':                  /* set Ambient (No Talking) Noise level */
+      ambient = atof(argv[++i]);
+      break;
+    case 'b':                  /* No AES */
+      dB = atof(argv[++i]);
+      if (dB < 0.0f) {
+        aec.setaes(dB2q(dB));
+      } else {
+        aec.setaes(0.0f);   // 0 is no AES
+      }
+      break;
+    case 'e':  
+      sinuson = 1;                 /* Sinuston 2040Hz zumischen */
+      break;
+    case 'k':                  /* packet loss concealment */
+      packetlc = YES;
+      break;
+    case 'f':                  /* Frames */
+      tx_frames = atoi(argv[++i]);
+      if (tx_frames < 1)
+        tx_frames = 1;
+      if (tx_frames > 4)
+        tx_frames = 4;
+      break;
+    case 'l':                  /* use hardware AEC and Line-in for microphone */
+      channels = 2;
+      break;
+    case 'm':                  /* use hardware AEC and Mic-in for microphone */
+      channels = 2;
+      break;
+    case 'p':                  /* RTP Portnumber */
+      rtp_port = atoi(argv[++i]);
+      rtp_port &= 0xFFFE;       /* RFC3551: RTP port has even port number */
+      break;
+    case 's':                  /* simulated Packet loss */
+      packet_loss = (RAND_MAX / 100) * atoi(argv[++i]);
+      break;
+    case 't':                  /* Telephone conference call (true conference) */
+      telephone_conference = YES;
+      break;
+    case 'w':                  /* visualize vector w */
+      aec.openwdisplay();
+      break;
+#if WIDEB==1      
+    case 'A':                  /* set G.711 A-law */
+      tx_payloadt = PT_PCMA;
+      tx_packsize = 160;        /* see RFC3551 */
+      break;
+    case 'U':                  /* set G.711 u-law */
+      tx_payloadt = PT_PCMU;
+      tx_packsize = 160;        /* see RFC3551 */
+      break;
+    case '6':                  /* set G.726 codec */
+      tx_payloadt = PT_G726;
+      tx_packsize = 80;         /* see RFC3551 */
+      break;
+    case 'G':                  /* set GSM codec */
+      tx_payloadt = PT_GSM;
+      tx_packsize = 33;         /* see RFC3551 */
+      break;
+    case 'E':                  /* set GSM-EFR codec */
+      tx_payloadt = PT_EFR;
+      tx_packsize = 31;         /* see RFC3551 */
+      break;
+    case '9':                  /* set G.729 codec */
+      tx_payloadt = PT_G729;
+      tx_packsize = 20;         /* see RFC3551 */
+      break;
+#else
+    case 'S':                  /* set Speex codec */
+      tx_payloadt = PT_SPX;
+      tx_packsize = SZ_SPX;    /* 16.8kBit/s Wideband */
+      break;
+#endif    
+    }
+  }
+
+  /* do -a option even if codec options follows */
+  if (ambient != 0.0f) {
+    aec.setambient(MAXPCM * dB2q(ambient));
+  }
+
+  /* init Sinuston fuer Transmitter */
+  sinuston.frequenz(2040, WIDEB*8000, -24.0f);
+  
+  /* open Audio Receive */
+  int audio_rd_fd = audio_init("/dev/dsp", channels, O_RDONLY);
+  assert(audio_rd_fd >= 0);
+
+  /* open Network Receive */
+  int udp_fd = UDP_recv_init(rtp_port);
+  assert(udp_fd >= 0);
+
+  /* open Graphical User Interface as TCP server */
+  int gui_listen_fd = tcp_server_init(4999);
+
+  /* codec Initialization */
+  int j;
+  for (j = 0; j < PARTNERS; ++j) {
+    /* iLBC */
+    initEncode(enc_ilbc + j, ILBC_MODE);
+    initDecode(dec_ilbc + j, ILBC_MODE, 1);
+
+    /* GSM-EFR */
+    enc_efr[j].initEncode(0);
+    dec_efr[j].initDecode();
+
+    /* G.729 */
+    enc_g729[j].initEncode(0);
+    dec_g729[j].initDecode();
+
+    /* GSM */
+    enc_gsm[i] = rpeltp_init();
+    dec_gsm[i] = rpeltp_init();
+    
+    /* Speex */
+    spx_encoder_init(j);
+    
+    speex_bits_init(dec_spxbits + j);
+    dec_spx[j] = speex_decoder_init(&speex_wb_mode);  // Wideband
+    int value = SPX_ENH;
+    speex_decoder_ctl(dec_spx[j], SPEEX_SET_ENH, &value);
+  }
+
+  /* open Network Transmit Partner (Connections) */
+  to_ssrc = random32(tx_payloadt);
+  printf("to_ssrc = %08lx\n", to_ssrc);
+  for (; i < argc; ++i) {
+    if (0 == to_partners) {
+      mic_cirbuf.init();
+    }
+    tx_buf_init(to_partners);
+    to_ip[to_partners] = atoip(argv[i]);
+    to_rtp[to_partners].init(tx_payloadt, to_ssrc);
+    to_udp[to_partners].send_init(argv[i], rtp_port, udp_fd);
+    ++to_partners;
+  }
+
+  loop(audio_rd_fd, udp_fd, gui_listen_fd);
+
+  return OKAY;
+}

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.