Mercurial > hg > audiostuff
view 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 source
/* 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; }