summaryrefslogtreecommitdiffstats
path: root/src/audio
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio')
-rw-r--r--src/audio/Makefile.am49
-rw-r--r--src/audio/README_G71194
-rw-r--r--src/audio/audio_codecs.cpp99
-rw-r--r--src/audio/audio_codecs.h107
-rw-r--r--src/audio/audio_decoder.cpp523
-rw-r--r--src/audio/audio_decoder.h201
-rw-r--r--src/audio/audio_device.cpp909
-rw-r--r--src/audio/audio_device.h126
-rw-r--r--src/audio/audio_encoder.cpp430
-rw-r--r--src/audio/audio_encoder.h183
-rw-r--r--src/audio/audio_rx.cpp915
-rw-r--r--src/audio/audio_rx.h217
-rw-r--r--src/audio/audio_session.cpp677
-rw-r--r--src/audio/audio_session.h182
-rw-r--r--src/audio/audio_tx.cpp1009
-rw-r--r--src/audio/audio_tx.h205
-rw-r--r--src/audio/dtmf_player.cpp183
-rw-r--r--src/audio/dtmf_player.h98
-rw-r--r--src/audio/freq_gen.cpp134
-rw-r--r--src/audio/freq_gen.h48
-rw-r--r--src/audio/g711.cpp311
-rw-r--r--src/audio/g711.h18
-rw-r--r--src/audio/g721.cpp174
-rw-r--r--src/audio/g723_16.cpp181
-rw-r--r--src/audio/g723_24.cpp159
-rw-r--r--src/audio/g723_40.cpp179
-rw-r--r--src/audio/g72x.cpp565
-rw-r--r--src/audio/g72x.h151
-rw-r--r--src/audio/gsm/COPYRIGHT16
-rw-r--r--src/audio/gsm/ChangeLog80
-rw-r--r--src/audio/gsm/INSTALL99
-rw-r--r--src/audio/gsm/MACHINES11
-rw-r--r--src/audio/gsm/Makefile.am32
-rw-r--r--src/audio/gsm/README37
-rw-r--r--src/audio/gsm/inc/config.h37
-rw-r--r--src/audio/gsm/inc/gsm.h71
-rw-r--r--src/audio/gsm/inc/private.h268
-rw-r--r--src/audio/gsm/inc/proto.h65
-rw-r--r--src/audio/gsm/inc/unproto.h23
-rw-r--r--src/audio/gsm/src/add.cpp235
-rw-r--r--src/audio/gsm/src/code.cpp100
-rw-r--r--src/audio/gsm/src/debug.cpp76
-rw-r--r--src/audio/gsm/src/decode.cpp63
-rw-r--r--src/audio/gsm/src/gsm_create.cpp45
-rw-r--r--src/audio/gsm/src/gsm_decode.cpp361
-rw-r--r--src/audio/gsm/src/gsm_destroy.cpp26
-rw-r--r--src/audio/gsm/src/gsm_encode.cpp451
-rw-r--r--src/audio/gsm/src/gsm_explode.cpp417
-rw-r--r--src/audio/gsm/src/gsm_implode.cpp515
-rw-r--r--src/audio/gsm/src/gsm_option.cpp69
-rw-r--r--src/audio/gsm/src/gsm_print.cpp167
-rw-r--r--src/audio/gsm/src/long_term.cpp949
-rw-r--r--src/audio/gsm/src/lpc.cpp341
-rw-r--r--src/audio/gsm/src/preprocess.cpp113
-rw-r--r--src/audio/gsm/src/rpe.cpp488
-rw-r--r--src/audio/gsm/src/short_term.cpp429
-rw-r--r--src/audio/gsm/src/table.cpp63
-rw-r--r--src/audio/media_buffer.cpp128
-rw-r--r--src/audio/media_buffer.h73
-rw-r--r--src/audio/rtp_telephone_event.cpp83
-rw-r--r--src/audio/rtp_telephone_event.h79
-rw-r--r--src/audio/tone_gen.cpp245
-rw-r--r--src/audio/tone_gen.h68
-rw-r--r--src/audio/twinkle_rtp_session.cpp117
-rw-r--r--src/audio/twinkle_rtp_session.h53
-rw-r--r--src/audio/twinkle_zrtp_ui.cpp296
-rw-r--r--src/audio/twinkle_zrtp_ui.h90
67 files changed, 15006 insertions, 0 deletions
diff --git a/src/audio/Makefile.am b/src/audio/Makefile.am
new file mode 100644
index 0000000..ab897eb
--- /dev/null
+++ b/src/audio/Makefile.am
@@ -0,0 +1,49 @@
+AM_CPPFLAGS = \
+ -Wall \
+ -I$(top_srcdir)/src \
+ $(CCRTP_CFLAGS) \
+ $(XML2_CFLAGS)
+
+noinst_LIBRARIES = libaudio.a
+
+libaudio_a_SOURCES =\
+ audio_device.cpp\
+ audio_decoder.cpp\
+ audio_encoder.cpp\
+ audio_codecs.cpp\
+ audio_rx.cpp\
+ audio_session.cpp\
+ audio_tx.cpp\
+ dtmf_player.cpp\
+ freq_gen.cpp\
+ g711.cpp\
+ g721.cpp\
+ g723_16.cpp\
+ g723_24.cpp\
+ g723_40.cpp\
+ g72x.cpp\
+ media_buffer.cpp\
+ rtp_telephone_event.cpp\
+ tone_gen.cpp\
+ twinkle_rtp_session.cpp\
+ twinkle_zrtp_ui.cpp\
+ audio_device.h\
+ audio_decoder.h\
+ audio_encoder.h\
+ audio_codecs.h\
+ audio_rx.h\
+ audio_session.h\
+ audio_tx.h\
+ dtmf_player.h\
+ freq_gen.h\
+ g711.h\
+ g72x.h\
+ media_buffer.h\
+ rtp_telephone_event.h\
+ tone_gen.h\
+ twinkle_rtp_session.h\
+ twinkle_zrtp_ui.h
+
+SUBDIRS = gsm
+
+EXTRA_DIST = README_G711
diff --git a/src/audio/README_G711 b/src/audio/README_G711
new file mode 100644
index 0000000..23b0e7d
--- /dev/null
+++ b/src/audio/README_G711
@@ -0,0 +1,94 @@
+The files in this directory comprise ANSI-C language reference implementations
+of the CCITT (International Telegraph and Telephone Consultative Committee)
+G.711, G.721 and G.723 voice compressions. They have been tested on Sun
+SPARCstations and passed 82 out of 84 test vectors published by CCITT
+(Dec. 20, 1988) for G.721 and G.723. [The two remaining test vectors,
+which the G.721 decoder implementation for u-law samples did not pass,
+may be in error because they are identical to two other vectors for G.723_40.]
+
+This source code is released by Sun Microsystems, Inc. to the public domain.
+Please give your acknowledgement in product literature if this code is used
+in your product implementation.
+
+Sun Microsystems supports some CCITT audio formats in Solaris 2.0 system
+software. However, Sun's implementations have been optimized for higher
+performance on SPARCstations.
+
+
+The source files for CCITT conversion routines in this directory are:
+
+ g72x.h header file for g721.c, g723_24.c and g723_40.c
+ g711.c CCITT G.711 u-law and A-law compression
+ g72x.c common denominator of G.721 and G.723 ADPCM codes
+ g721.c CCITT G.721 32Kbps ADPCM coder (with g72x.c)
+ g723_24.c CCITT G.723 24Kbps ADPCM coder (with g72x.c)
+ g723_40.c CCITT G.723 40Kbps ADPCM coder (with g72x.c)
+
+
+Simple conversions between u-law, A-law, and 16-bit linear PCM are invoked
+as follows:
+
+ unsigned char ucode, acode;
+ short pcm_val;
+
+ ucode = linear2ulaw(pcm_val);
+ ucode = alaw2ulaw(acode);
+
+ acode = linear2alaw(pcm_val);
+ acode = ulaw2alaw(ucode);
+
+ pcm_val = ulaw2linear(ucode);
+ pcm_val = alaw2linear(acode);
+
+
+The other CCITT compression routines are invoked as follows:
+
+ #include "g72x.h"
+
+ struct g72x_state state;
+ int sample, code;
+
+ g72x_init_state(&state);
+ code = {g721,g723_24,g723_40}_encoder(sample, coding, &state);
+ sample = {g721,g723_24,g723_40}_decoder(code, coding, &state);
+
+where
+ coding = AUDIO_ENCODING_ULAW for 8-bit u-law samples
+ AUDIO_ENCODING_ALAW for 8-bit A-law samples
+ AUDIO_ENCODING_LINEAR for 16-bit linear PCM samples
+
+
+
+This directory also includes the following sample programs:
+
+ encode.c CCITT ADPCM encoder
+ decode.c CCITT ADPCM decoder
+ Makefile makefile for the sample programs
+
+
+The sample programs contain examples of how to call the various compression
+routines and pack/unpack the bits. The sample programs read byte streams from
+stdin and write to stdout. The input/output data is raw data (no file header
+or other identifying information is embedded). The sample programs are
+invoked as follows:
+
+ encode [-3|4|5] [-a|u|l] <infile >outfile
+ decode [-3|4|5] [-a|u|l] <infile >outfile
+where:
+ -3 encode to (decode from) G.723 24kbps (3-bit) data
+ -4 encode to (decode from) G.721 32kbps (4-bit) data [the default]
+ -5 encode to (decode from) G.723 40kbps (5-bit) data
+ -a encode from (decode to) A-law data
+ -u encode from (decode to) u-law data [the default]
+ -l encode from (decode to) 16-bit linear data
+
+Examples:
+ # Read 16-bit linear and output G.721
+ encode -4 -l <pcmfile >g721file
+
+ # Read 40Kbps G.723 and output A-law
+ decode -5 -a <g723file >alawfile
+
+ # Compress and then decompress u-law data using 24Kbps G.723
+ encode -3 <ulawin | deoced -3 >ulawout
+
diff --git a/src/audio/audio_codecs.cpp b/src/audio/audio_codecs.cpp
new file mode 100644
index 0000000..75a9262
--- /dev/null
+++ b/src/audio/audio_codecs.cpp
@@ -0,0 +1,99 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <cstdlib>
+#include "audio_codecs.h"
+
+unsigned short audio_sample_rate(t_audio_codec codec) {
+ switch(codec) {
+ case CODEC_G711_ALAW:
+ case CODEC_G711_ULAW:
+ case CODEC_GSM:
+ case CODEC_SPEEX_NB:
+ case CODEC_ILBC:
+ case CODEC_G726_16:
+ case CODEC_G726_24:
+ case CODEC_G726_32:
+ case CODEC_G726_40:
+ case CODEC_TELEPHONE_EVENT:
+ return 8000;
+ case CODEC_SPEEX_WB:
+ return 16000;
+ case CODEC_SPEEX_UWB:
+ return 32000;
+ default:
+ // Use 8000 as default rate
+ return 8000;
+ }
+}
+
+bool is_speex_codec(t_audio_codec codec) {
+ return (codec == CODEC_SPEEX_NB ||
+ codec == CODEC_SPEEX_WB ||
+ codec == CODEC_SPEEX_UWB);
+}
+
+int resample(short *input_buf, int input_len, int input_sample_rate,
+ short *output_buf, int output_len, int output_sample_rate)
+{
+ if (input_sample_rate > output_sample_rate) {
+ int downsample_factor = input_sample_rate / output_sample_rate;
+ int output_idx = -1;
+ for (int i = 0; i < input_len; i += downsample_factor) {
+ output_idx = i / downsample_factor;
+ if (output_idx >= output_len) {
+ // Output buffer is full
+ return output_len;
+ }
+ output_buf[output_idx] = input_buf[i];
+ }
+ return output_idx + 1;
+ } else {
+ int upsample_factor = output_sample_rate / input_sample_rate;
+ int output_idx = -1;
+ for (int i = 0; i < input_len; i++) {
+ for (int j = 0; j < upsample_factor; j++) {
+ output_idx = i * upsample_factor + j;
+ if (output_idx >= output_len) {
+ // Output buffer is full
+ return output_len;
+ }
+ output_buf[output_idx] = input_buf[i];
+ }
+ }
+ return output_idx + 1;
+ }
+}
+
+short mix_linear_pcm(short pcm1, short pcm2) {
+ long mixed_sample = long(pcm1) + long(pcm2);
+
+ // Compress a 17 bit PCM value into a 16-bit value.
+ // The easy way is to divide the value by 2, but this lowers
+ // the volume.
+ // Only lower the volume for the loud values. As for a normal
+ // voice call the values are not that loud, this gives better
+ // quality.
+ if (mixed_sample > 16384) {
+ mixed_sample = 16384 + (mixed_sample - 16384) / 3;
+ } else if (mixed_sample < -16384) {
+ mixed_sample = -16384 - (-16384 - mixed_sample) / 3;
+ }
+
+ return short(mixed_sample);
+}
diff --git a/src/audio/audio_codecs.h b/src/audio/audio_codecs.h
new file mode 100644
index 0000000..54967b1
--- /dev/null
+++ b/src/audio/audio_codecs.h
@@ -0,0 +1,107 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _AUDIO_CODECS_H
+#define _AUDIO_CODECS_H
+
+#include "g711.h"
+#include "g72x.h"
+
+// Audio codecs
+enum t_audio_codec {
+ CODEC_NULL,
+ CODEC_UNSUPPORTED,
+ CODEC_G711_ALAW,
+ CODEC_G711_ULAW,
+ CODEC_GSM,
+ CODEC_SPEEX_NB,
+ CODEC_SPEEX_WB,
+ CODEC_SPEEX_UWB,
+ CODEC_ILBC,
+ CODEC_G726_16,
+ CODEC_G726_24,
+ CODEC_G726_32,
+ CODEC_G726_40,
+ CODEC_TELEPHONE_EVENT
+};
+
+// Default ptime values (ms) for audio codecs
+#define PTIME_G711_ALAW 20
+#define PTIME_G711_ULAW 20
+#define PTIME_G726 20
+#define PTIME_GSM 20
+#define PTIME_SPEEX 20
+#define MIN_PTIME 10
+#define MAX_PTIME 80
+
+// Audio sample settings
+#define AUDIO_SAMPLE_SIZE 16
+
+
+// Maximum length (in packets) for concealment of lost packets
+#define MAX_CONCEALMENT 2
+
+// Size of jitter buffer in ms
+// The jitter buffer is used to smooth playing out incoming RTP packets.
+// The size of the buffer is also used as the expiry time in the ccRTP
+// stack. Packets that have timestamp that is older than then size of
+// the jitter buffer will not be sent out anymore.
+#define JITTER_BUF_MS 80
+
+// Duration of the expiry timer in the RTP stack.
+// The ccRTP stack checks all data delivered to it against its clock.
+// If the data is too old it will not send it out. Data can be old
+// for several reasons:
+//
+// 1) The thread reading the soundcard has been paused for a while
+// 2) The audio card buffers sound before releasing it.
+//
+// Especially the latter seems to happen on some soundcards. Data
+// not older than defined delay are still allowed to go out. It's up
+// to the receiving and to deal with the jitter this may cause.
+#define MAX_OUT_AUDIO_DELAY_MS 160
+
+// Buffer sizes
+#define JITTER_BUF_SIZE(sample_rate) (JITTER_BUF_MS * (sample_rate)/1000 * AUDIO_SAMPLE_SIZE/8)
+
+// Log speex errors
+#define LOG_SPEEX_ERROR(func, spxfunc, spxerr) {\
+ log_file->write_header((func), LOG_NORMAL, LOG_DEBUG);\
+ log_file->write_raw("Speex error: ");\
+ log_file->write_raw((spxfunc));\
+ log_file->write_raw(" returned ");\
+ log_file->write_raw((spxerr));\
+ log_file->write_footer(); }
+
+// Return the sampling rate for a codec
+unsigned short audio_sample_rate(t_audio_codec codec);
+
+// Returns true if the codec is a speex codec
+bool is_speex_codec(t_audio_codec codec);
+
+// Resample the input buffer to the output buffer
+// Returns the number of samples put in the output buffer
+// If the output buffer is too small, the number of samples will be
+// truncated.
+int resample(short *input_buf, int input_len, int input_sample_rate,
+ short *output_buf, int output_len, int output_sample_rate);
+
+// Mix 2 16 bits signed linear PCM values
+short mix_linear_pcm(short pcm1, short pcm2);
+
+#endif
diff --git a/src/audio/audio_decoder.cpp b/src/audio/audio_decoder.cpp
new file mode 100644
index 0000000..e4c1d74
--- /dev/null
+++ b/src/audio/audio_decoder.cpp
@@ -0,0 +1,523 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <cassert>
+#include <iostream>
+#include "audio_decoder.h"
+#include "log.h"
+
+#ifdef HAVE_ILBC
+#ifndef HAVE_ILBC_CPP
+extern "C" {
+#endif
+#include <ilbc/iLBC_decode.h>
+#ifndef HAVE_ILBC_CPP
+}
+#endif
+#endif
+
+//////////////////////////////////////////
+// class t_audio_decoder
+//////////////////////////////////////////
+
+t_audio_decoder::t_audio_decoder(uint16 default_ptime, bool plc, t_user *user_config) :
+ _default_ptime(default_ptime),
+ _plc(plc),
+ _user_config(user_config)
+{}
+
+t_audio_codec t_audio_decoder::get_codec(void) const {
+ return _codec;
+}
+
+uint16 t_audio_decoder::get_default_ptime(void) const {
+ return _default_ptime;
+}
+
+bool t_audio_decoder::has_plc(void) const {
+ return _plc;
+}
+
+uint16 t_audio_decoder::conceal(int16 *pcm_buf, uint16 pcm_buf_size) {
+ return 0;
+}
+
+//////////////////////////////////////////
+// class t_g711a_audio_decoder
+//////////////////////////////////////////
+
+t_g711a_audio_decoder::t_g711a_audio_decoder(uint16 default_ptime, t_user *user_config) :
+ t_audio_decoder(default_ptime, false, user_config)
+{
+ _codec = CODEC_G711_ALAW;
+
+ if (default_ptime == 0) {
+ _default_ptime = PTIME_G711_ALAW;
+ }
+}
+
+uint16 t_g711a_audio_decoder::get_ptime(uint16 payload_size) const {
+ return payload_size / (audio_sample_rate(_codec) / 1000);
+}
+
+uint16 t_g711a_audio_decoder::decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size)
+{
+ assert(pcm_buf_size >= payload_size);
+
+ for (int i = 0; i < payload_size; i++) {
+ pcm_buf[i] = alaw2linear(payload[i]);
+ }
+ return payload_size;
+}
+
+bool t_g711a_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const
+{
+ return payload_size <= sample_buf_size;
+}
+
+//////////////////////////////////////////
+// class t_g711u_audio_decoder
+//////////////////////////////////////////
+
+t_g711u_audio_decoder::t_g711u_audio_decoder(uint16 default_ptime, t_user *user_config) :
+ t_audio_decoder(default_ptime, false, user_config)
+{
+ _codec = CODEC_G711_ULAW;
+
+ if (default_ptime == 0) {
+ _default_ptime = PTIME_G711_ULAW;
+ }
+}
+
+uint16 t_g711u_audio_decoder::get_ptime(uint16 payload_size) const {
+ return payload_size / (audio_sample_rate(_codec) / 1000);
+}
+
+uint16 t_g711u_audio_decoder::decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size)
+{
+ assert(pcm_buf_size >= payload_size);
+
+ for (int i = 0; i < payload_size; i++) {
+ pcm_buf[i] = ulaw2linear(payload[i]);
+ }
+ return payload_size;
+}
+
+bool t_g711u_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const
+{
+ return payload_size <= sample_buf_size;
+}
+
+//////////////////////////////////////////
+// class t_gsm_audio_decoder
+//////////////////////////////////////////
+
+t_gsm_audio_decoder::t_gsm_audio_decoder(t_user *user_config) :
+ t_audio_decoder(PTIME_GSM, false, user_config)
+{
+ _codec = CODEC_GSM;
+ gsm_decoder = gsm_create();
+}
+
+t_gsm_audio_decoder::~t_gsm_audio_decoder() {
+ gsm_destroy(gsm_decoder);
+}
+
+uint16 t_gsm_audio_decoder::get_ptime(uint16 payload_size) const {
+ return get_default_ptime();
+}
+
+uint16 t_gsm_audio_decoder::decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size)
+{
+ assert(pcm_buf_size >= 160);
+
+ gsm_decode(gsm_decoder, payload, pcm_buf);
+ return 160;
+}
+
+bool t_gsm_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const
+{
+ return payload_size == 33;
+}
+
+#ifdef HAVE_SPEEX
+//////////////////////////////////////////
+// class t_speex_audio_decoder
+//////////////////////////////////////////
+
+t_speex_audio_decoder::t_speex_audio_decoder(t_mode mode, t_user *user_config) :
+ t_audio_decoder(0, true, user_config)
+{
+ speex_bits_init(&speex_bits);
+ _mode = mode;
+
+ switch (mode) {
+ case MODE_NB:
+ _codec = CODEC_SPEEX_NB;
+ speex_dec_state = speex_decoder_init(&speex_nb_mode);
+ break;
+ case MODE_WB:
+ _codec = CODEC_SPEEX_WB;
+ speex_dec_state = speex_decoder_init(&speex_wb_mode);
+ break;
+ case MODE_UWB:
+ _codec = CODEC_SPEEX_UWB;
+ speex_dec_state = speex_decoder_init(&speex_uwb_mode);
+ break;
+ default:
+ assert(false);
+ }
+
+ int frame_size = 0;
+ speex_decoder_ctl(speex_dec_state, SPEEX_GET_FRAME_SIZE, &frame_size);
+
+ // Initialize decoder with user settings
+ int arg = (user_config->get_speex_penh() ? 1 : 0);
+ speex_decoder_ctl(speex_dec_state, SPEEX_SET_ENH, &arg);
+
+ _default_ptime = frame_size / (audio_sample_rate(_codec) / 1000);
+}
+
+t_speex_audio_decoder::~t_speex_audio_decoder() {
+ speex_bits_destroy(&speex_bits);
+ speex_decoder_destroy(speex_dec_state);
+}
+
+uint16 t_speex_audio_decoder::get_ptime(uint16 payload_size) const {
+ return get_default_ptime();
+}
+
+uint16 t_speex_audio_decoder::decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size)
+{
+ int retval;
+ int speex_frame_size;
+
+ speex_decoder_ctl(speex_dec_state, SPEEX_GET_FRAME_SIZE,
+ &speex_frame_size);
+
+ assert(pcm_buf_size >= speex_frame_size);
+
+ speex_bits_read_from(&speex_bits, reinterpret_cast<char *>(payload), payload_size);
+ retval = speex_decode_int(speex_dec_state, &speex_bits, pcm_buf);
+
+ if (retval < 0) {
+ LOG_SPEEX_ERROR("t_speex_audio_decoder::decode",
+ "speex_decode_int", retval);
+ return 0;
+ }
+
+ return speex_frame_size;
+}
+
+uint16 t_speex_audio_decoder::conceal(int16 *pcm_buf, uint16 pcm_buf_size) {
+ int retval;
+ int speex_frame_size;
+
+ speex_decoder_ctl(speex_dec_state, SPEEX_GET_FRAME_SIZE,
+ &speex_frame_size);
+
+ assert(pcm_buf_size >= speex_frame_size);
+
+ retval = speex_decode_int(speex_dec_state, NULL, pcm_buf);
+
+ if (retval < 0) {
+ LOG_SPEEX_ERROR("t_speex_audio_decoder::conceal",
+ "speex_decode_int", retval);
+ return 0;
+ }
+
+ return speex_frame_size;
+}
+
+bool t_speex_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const
+{
+ return true;
+}
+#endif
+
+#ifdef HAVE_ILBC
+//////////////////////////////////////////
+// class t_ilbc_audio_decoder
+//////////////////////////////////////////
+t_ilbc_audio_decoder::t_ilbc_audio_decoder(uint16 default_ptime, t_user *user_config) :
+ t_audio_decoder(default_ptime, true, user_config)
+{
+ _codec = CODEC_ILBC;
+ _last_received_ptime = 0;
+ initDecode(&_ilbc_decoder_20, 20, 1);
+ initDecode(&_ilbc_decoder_30, 30, 1);
+}
+
+uint16 t_ilbc_audio_decoder::get_ptime(uint16 payload_size) const {
+ if (payload_size == NO_OF_BYTES_20MS) {
+ return 20;
+ } else {
+ return 30;
+ }
+}
+
+uint16 t_ilbc_audio_decoder::decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size)
+{
+ float sample;
+ float block[BLOCKL_MAX];
+ int block_len;
+
+ if (get_ptime(payload_size) == 20) {
+ block_len = BLOCKL_20MS;
+ assert(pcm_buf_size >= block_len);
+ iLBC_decode(block, (unsigned char*)payload, &_ilbc_decoder_20, 1);
+ _last_received_ptime = 20;
+ } else {
+ block_len = BLOCKL_30MS;
+ assert(pcm_buf_size >= block_len);
+ iLBC_decode(block, (unsigned char*)payload, &_ilbc_decoder_30, 1);
+ _last_received_ptime = 30;
+ }
+
+ for (int i = 0; i < block_len; i++) {
+ sample = block[i];
+
+ if (sample < MIN_SAMPLE) sample = MIN_SAMPLE;
+ if (sample > MAX_SAMPLE) sample = MAX_SAMPLE;
+
+ pcm_buf[i] = static_cast<int16>(sample);
+ }
+
+ return block_len;
+}
+
+uint16 t_ilbc_audio_decoder::conceal(int16 *pcm_buf, uint16 pcm_buf_size) {
+ float sample;
+ float block[BLOCKL_MAX];
+ int block_len;
+
+ if (_last_received_ptime == 0) return 0;
+
+ if (_last_received_ptime == 20) {
+ block_len = BLOCKL_20MS;
+ assert(pcm_buf_size >= block_len);
+ iLBC_decode(block, NULL, &_ilbc_decoder_20, 0);
+ } else {
+ block_len = BLOCKL_30MS;
+ assert(pcm_buf_size >= block_len);
+ iLBC_decode(block, NULL, &_ilbc_decoder_30, 0);
+ }
+
+ for (int i = 0; i < block_len; i++) {
+ sample = block[i];
+
+ if (sample < MIN_SAMPLE) sample = MIN_SAMPLE;
+ if (sample > MAX_SAMPLE) sample = MAX_SAMPLE;
+
+ pcm_buf[i] = static_cast<int16>(sample);
+ }
+
+ return block_len;
+}
+
+bool t_ilbc_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const
+{
+ return payload_size == NO_OF_BYTES_20MS || payload_size == NO_OF_BYTES_30MS;
+}
+#endif
+
+//////////////////////////////////////////
+// class t_g726_audio_decoder
+//////////////////////////////////////////
+t_g726_audio_decoder::t_g726_audio_decoder(t_bit_rate bit_rate, uint16 default_ptime,
+ t_user *user_config) :
+ t_audio_decoder(default_ptime, false, user_config)
+{
+ _bit_rate = bit_rate;
+
+ if (default_ptime == 0) {
+ _default_ptime = PTIME_G726;
+ }
+
+ switch (_bit_rate) {
+ case BIT_RATE_16:
+ _codec = CODEC_G726_16;
+ _bits_per_sample = 2;
+ break;
+ case BIT_RATE_24:
+ _codec = CODEC_G726_24;
+ _bits_per_sample = 3;
+ break;
+ case BIT_RATE_32:
+ _codec = CODEC_G726_32;
+ _bits_per_sample = 4;
+ break;
+ case BIT_RATE_40:
+ _codec = CODEC_G726_40;
+ _bits_per_sample = 5;
+ break;
+ default:
+ assert(false);
+ }
+
+ _packing = user_config->get_g726_packing();
+
+ g72x_init_state(&_state);
+}
+
+uint16 t_g726_audio_decoder::get_ptime(uint16 payload_size) const {
+ return (payload_size * 8 / _bits_per_sample) / (audio_sample_rate(_codec) / 1000);
+}
+
+uint16 t_g726_audio_decoder::decode_16(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size)
+{
+ assert(payload_size * 4 <= pcm_buf_size);
+
+ for (int i = 0; i < payload_size; i++) {
+ for (int j = 0; j < 4; j++) {
+ uint8 w;
+ if (_packing == G726_PACK_RFC3551) {
+ w = (payload[i] >> (j*2)) & 0x3;
+ } else {
+ w = (payload[i] >> ((3-j)*2)) & 0x3;
+ }
+ pcm_buf[4*i+j] = g723_16_decoder(
+ w, AUDIO_ENCODING_LINEAR, &_state);
+ }
+ }
+
+ return payload_size * 4;
+}
+
+uint16 t_g726_audio_decoder::decode_24(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size)
+{
+ assert(payload_size % 3 == 0);
+ assert(payload_size * 8 / 3 <= pcm_buf_size);
+
+ for (int i = 0; i < payload_size; i += 3) {
+ uint32 v = (static_cast<uint32>(payload[i+2]) << 16) |
+ (static_cast<uint32>(payload[i+1]) << 8) |
+ static_cast<uint32>(payload[i]);
+
+ for (int j = 0; j < 8; j++) {
+ uint8 w;
+ if (_packing == G726_PACK_RFC3551) {
+ w = (v >> (j*3)) & 0x7;
+ } else {
+ w = (v >> ((7-j)*3)) & 0x7;
+ }
+ pcm_buf[8*(i/3)+j] = g723_24_decoder(
+ w, AUDIO_ENCODING_LINEAR, &_state);
+ }
+ }
+
+ return payload_size * 8 / 3;
+}
+
+uint16 t_g726_audio_decoder::decode_32(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size)
+{
+ assert(payload_size * 2 <= pcm_buf_size);
+
+ for (int i = 0; i < payload_size; i++) {
+ for (int j = 0; j < 2; j++) {
+ uint8 w;
+ if (_packing == G726_PACK_RFC3551) {
+ w = (payload[i] >> (j*4)) & 0xf;
+ } else {
+ w = (payload[i] >> ((1-j)*4)) & 0xf;
+ }
+ pcm_buf[2*i+j] = g721_decoder(
+ w, AUDIO_ENCODING_LINEAR, &_state);
+ }
+ }
+
+ return payload_size * 2;
+}
+
+uint16 t_g726_audio_decoder::decode_40(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size)
+{
+ assert(payload_size % 5 == 0);
+ assert(payload_size * 8 / 5 <= pcm_buf_size);
+
+ for (int i = 0; i < payload_size; i += 5) {
+ uint64 v = (static_cast<uint64>(payload[i+4]) << 32) |
+ (static_cast<uint64>(payload[i+3]) << 24) |
+ (static_cast<uint64>(payload[i+2]) << 16) |
+ (static_cast<uint64>(payload[i+1]) << 8) |
+ static_cast<uint64>(payload[i]);
+
+ for (int j = 0; j < 8; j++) {
+ uint8 w;
+ if (_packing == G726_PACK_RFC3551) {
+ w = (v >> (j*5)) & 0x1f;
+ } else {
+ w = (v >> ((7-j)*5)) & 0x1f;
+ }
+ pcm_buf[8*(i/5)+j] = g723_40_decoder(
+ w, AUDIO_ENCODING_LINEAR, &_state);
+ }
+ }
+
+ return payload_size * 8 / 5;
+}
+
+uint16 t_g726_audio_decoder::decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size)
+{
+ switch (_bit_rate) {
+ case BIT_RATE_16:
+ return decode_16(payload, payload_size, pcm_buf, pcm_buf_size);
+ break;
+ case BIT_RATE_24:
+ return decode_24(payload, payload_size, pcm_buf, pcm_buf_size);
+ break;
+ case BIT_RATE_32:
+ return decode_32(payload, payload_size, pcm_buf, pcm_buf_size);
+ break;
+ case BIT_RATE_40:
+ return decode_40(payload, payload_size, pcm_buf, pcm_buf_size);
+ break;
+ default:
+ assert(false);
+ }
+
+ return 0;
+}
+
+bool t_g726_audio_decoder::valid_payload_size(uint16 payload_size,
+ uint16 sample_buf_size) const
+{
+ switch (_bit_rate) {
+ case BIT_RATE_24:
+ // Payload size must be multiple of 3
+ if (payload_size % 3 != 0) return false;
+ break;
+ case BIT_RATE_40:
+ // Payload size must be multiple of 5
+ if (payload_size % 5 != 0) return false;
+ break;
+ default:
+ break;
+ }
+
+ return (payload_size * 8 / _bits_per_sample ) <= sample_buf_size;
+}
diff --git a/src/audio/audio_decoder.h b/src/audio/audio_decoder.h
new file mode 100644
index 0000000..5fa9c55
--- /dev/null
+++ b/src/audio/audio_decoder.h
@@ -0,0 +1,201 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+// Audio decoders
+
+#ifndef _AUDIO_DECODER_H
+#define _AUDIO_DECODER_H
+
+#include <cc++/config.h>
+#include "twinkle_config.h"
+#include "audio_codecs.h"
+#include "user.h"
+
+#ifdef HAVE_GSM
+#include <gsm/gsm.h>
+#else
+#include "gsm/inc/gsm.h"
+#endif
+
+#ifdef HAVE_SPEEX
+#include <speex/speex.h>
+#include <speex/speex_preprocess.h>
+#endif
+
+#ifdef HAVE_ILBC
+#ifndef HAVE_ILBC_CPP
+extern "C" {
+#endif
+#include <ilbc/iLBC_define.h>
+#ifndef HAVE_ILBC_CPP
+}
+#endif
+#endif
+
+// Abstract definition of an audio decoder
+class t_audio_decoder {
+protected:
+ t_audio_codec _codec;
+ uint16 _default_ptime;
+ bool _plc; // packet loss concealment
+ t_user *_user_config;
+
+ t_audio_decoder(uint16 default_ptime, bool plc, t_user *user_config);
+
+public:
+ virtual ~t_audio_decoder() {};
+
+ t_audio_codec get_codec(void) const;
+ uint16 get_default_ptime(void) const;
+ virtual uint16 get_ptime(uint16 payload_size) const = 0;
+
+ // Decode a buffer of encoded samples to 16-bit PCM.
+ // Returns the number of pcm samples written into pcm_buf
+ // Returns 0 if decoding failed
+ virtual uint16 decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size) = 0;
+
+ // Indicates if codec has PLC algorithm
+ bool has_plc(void) const;
+
+ // Create a payload to conceal a lost packet.
+ // Returns the number of pcm samples written into pcm_buf
+ // Returns 0 if decoding failed
+ virtual uint16 conceal(int16 *pcm_buf, uint16 pcm_buf_size);
+
+ // Determine if the payload size is valid for this decoder
+ virtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const = 0;
+};
+
+// G.711 A-law
+class t_g711a_audio_decoder : public t_audio_decoder {
+public:
+ t_g711a_audio_decoder(uint16 default_ptime, t_user *user_config);
+
+ virtual uint16 get_ptime(uint16 payload_size) const;
+ virtual uint16 decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size);
+ virtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;
+};
+
+// G.711 u-law
+class t_g711u_audio_decoder : public t_audio_decoder {
+public:
+ t_g711u_audio_decoder(uint16 default_ptime, t_user *user_config);
+
+ virtual uint16 get_ptime(uint16 payload_size) const;
+ virtual uint16 decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size);
+ virtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;
+};
+
+// GSM
+class t_gsm_audio_decoder : public t_audio_decoder {
+private:
+ gsm gsm_decoder;
+
+public:
+ t_gsm_audio_decoder(t_user *user_config);
+ virtual ~t_gsm_audio_decoder();
+
+ virtual uint16 get_ptime(uint16 payload_size) const;
+ virtual uint16 decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size);
+ virtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;
+};
+
+#ifdef HAVE_SPEEX
+// Speex
+class t_speex_audio_decoder : public t_audio_decoder {
+public:
+ enum t_mode {
+ MODE_NB, // Narrow band
+ MODE_WB, // Wide band
+ MODE_UWB // Ultra wide band
+ };
+
+private:
+ SpeexBits speex_bits;
+ void *speex_dec_state;
+ t_mode _mode;
+
+public:
+ t_speex_audio_decoder(t_mode mode, t_user *user_config);
+ virtual ~t_speex_audio_decoder();
+
+ virtual uint16 get_ptime(uint16 payload_size) const;
+ virtual uint16 decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size);
+ virtual uint16 conceal(int16 *pcm_buf, uint16 pcm_buf_size);
+ virtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;
+};
+#endif
+
+#ifdef HAVE_ILBC
+// iLBC
+class t_ilbc_audio_decoder : public t_audio_decoder {
+private:
+ iLBC_Dec_Inst_t _ilbc_decoder_20; // decoder for 20ms frames
+ iLBC_Dec_Inst_t _ilbc_decoder_30; // decoder for 30ms frames
+
+ // The number of ms received in the last frame, so the conceal function
+ // can determine which decoder to use to conceal a lost frame.
+ int _last_received_ptime;
+
+public:
+ t_ilbc_audio_decoder(uint16 default_ptime, t_user *user_config);
+
+ virtual uint16 get_ptime(uint16 payload_size) const;
+ virtual uint16 decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size);
+ virtual uint16 conceal(int16 *pcm_buf, uint16 pcm_buf_size);
+ virtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;
+};
+
+#endif
+
+// G.726
+class t_g726_audio_decoder : public t_audio_decoder {
+public:
+ enum t_bit_rate {
+ BIT_RATE_16,
+ BIT_RATE_24,
+ BIT_RATE_32,
+ BIT_RATE_40
+ };
+
+private:
+ uint16 decode_16(uint8 *payload, uint16 payload_size, int16 *pcm_buf, uint16 pcm_buf_size);
+ uint16 decode_24(uint8 *payload, uint16 payload_size, int16 *pcm_buf, uint16 pcm_buf_size);
+ uint16 decode_32(uint8 *payload, uint16 payload_size, int16 *pcm_buf, uint16 pcm_buf_size);
+ uint16 decode_40(uint8 *payload, uint16 payload_size, int16 *pcm_buf, uint16 pcm_buf_size);
+
+ struct g72x_state _state;
+ t_bit_rate _bit_rate;
+ uint8 _bits_per_sample;
+ t_g726_packing _packing;
+
+public:
+ t_g726_audio_decoder(t_bit_rate bit_rate, uint16 default_ptime, t_user *user_config);
+ virtual uint16 get_ptime(uint16 payload_size) const;
+ virtual uint16 decode(uint8 *payload, uint16 payload_size,
+ int16 *pcm_buf, uint16 pcm_buf_size);
+ virtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;
+};
+
+#endif
diff --git a/src/audio/audio_device.cpp b/src/audio/audio_device.cpp
new file mode 100644
index 0000000..3000a99
--- /dev/null
+++ b/src/audio/audio_device.cpp
@@ -0,0 +1,909 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "audio_device.h"
+#include <iostream>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include "sys_settings.h"
+#include "translator.h"
+#include "log.h"
+#include "userintf.h"
+#include "util.h"
+#include "audits/memman.h"
+
+#ifdef HAVE_LIBASOUND
+#include <alsa/asoundlib.h>
+#endif
+
+t_audio_io* t_audio_io::open(const t_audio_device& dev, bool playback, bool capture, bool blocking, int channels, t_audio_sampleformat format, int sample_rate, bool short_latency)
+{
+ t_audio_io* aio;
+
+ if(dev.type == t_audio_device::OSS) {
+ aio = new t_oss_io();
+ MEMMAN_NEW(aio);
+#ifdef HAVE_LIBASOUND
+ } else if (dev.type == t_audio_device::ALSA) {
+ aio = new t_alsa_io();
+ MEMMAN_NEW(aio);
+#endif
+ } else {
+ string msg("Audio device not implemented");
+ log_file->write_report(msg, "t_audio_io::open",
+ LOG_NORMAL, LOG_CRITICAL);
+ return 0;
+ }
+ if (aio->open(dev.device, playback, capture, blocking, channels, format,
+ sample_rate, short_latency)) {
+ return aio;
+ } else {
+ string msg("Open audio device failed");
+ log_file->write_report(msg, "t_audio_io::open",
+ LOG_NORMAL, LOG_CRITICAL);
+ MEMMAN_DELETE(aio);
+ delete aio;
+ return 0L;
+ }
+}
+
+bool t_audio_io::validate(const t_audio_device& dev, bool playback, bool capture) {
+ t_audio_io *aio = open(dev, playback, capture, false, 1, SAMPLEFORMAT_S16, 8000, true);
+
+ if (aio) {
+ MEMMAN_DELETE(aio);
+ delete aio;
+ return true;
+ }
+
+ return false;
+}
+
+t_audio_io::~t_audio_io() {}
+
+int t_audio_io::get_sample_rate(void) const {
+ return _sample_rate;
+}
+
+bool t_audio_io::open(const string& device, bool playback, bool capture, bool blocking, int channels, t_audio_sampleformat format, int sample_rate, bool short_latency)
+{
+ _sample_rate = sample_rate;
+ return true;
+}
+
+t_oss_io::t_oss_io() : fd(-1), play_buffersize(0), rec_buffersize(0) {
+}
+
+t_oss_io::~t_oss_io()
+{
+ if (fd > 0) {
+ int arg = 0;
+ ioctl(fd, SNDCTL_DSP_RESET, &arg);
+ close(fd);
+ }
+ fd = -1;
+}
+
+bool t_oss_io::open(const string& device, bool playback, bool capture, bool blocking, int channels, t_audio_sampleformat format, int sample_rate, bool short_latency)
+{
+ t_audio_io::open(device, playback, capture, blocking, channels, format, sample_rate,
+ short_latency);
+
+ int mode = 0;
+ int status;
+
+ log_file->write_header("t_oss_io::open", LOG_NORMAL);
+ log_file->write_raw("Opening OSS device: ");
+ log_file->write_raw(device);
+ log_file->write_endl();
+ if (playback) log_file->write_raw("play\n");
+ if (capture) log_file->write_raw("capture\n");
+ log_file->write_footer();
+
+ assert (playback || capture);
+ if (playback && capture) mode |= O_RDWR;
+ else if (playback) mode |= O_WRONLY;
+ else if (capture) mode |= O_RDONLY;
+
+ // On some systems opening the audio devices blocks if another
+ // process or thread has opened it already. To prevent a deadlock
+ // first try to open the device in non-blocking mode.
+ // If the device is still open by another twinkle thread then that
+ // is a bug, but this way at least non deadlock is caused.
+ if(blocking) {
+ fd = ::open(device.c_str(), mode | O_NONBLOCK);
+ if (fd == -1) {
+ string msg("OSS audio device open failed: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_oss_io::open",
+ LOG_NORMAL, LOG_CRITICAL);
+ return false;
+ } else {
+ close(fd);
+ fd = -1;
+ }
+ } else mode |= O_NONBLOCK;
+
+ fd = ::open(device.c_str(), mode);
+ if (fd < 0) {
+ string msg("OSS audio device open failed: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_oss_io::open",
+ LOG_NORMAL, LOG_CRITICAL);
+ return false;
+ }
+
+ // Full duplex
+ if (playback && capture) {
+ status = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
+ if (status == -1) {
+ string msg("SNDCTL_DSP_SETDUPLEX ioctl failed: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_oss_io::open",
+ LOG_NORMAL, LOG_CRITICAL);
+ ui->cb_display_msg(TRANSLATE("Sound card cannot be set to full duplex."),
+ MSG_CRITICAL);
+ close(fd);
+ return false;
+ }
+ }
+ // Set fragment size
+ int arg;
+ if (short_latency) {
+ switch (sys_config->get_oss_fragment_size()) {
+ case 16:
+ arg = 0x00ff0004; // 255 buffers of 2^4 bytes each
+ break;
+ case 32:
+ arg = 0x00ff0005; // 255 buffers of 2^5 bytes each
+ break;
+ case 64:
+ arg = 0x00ff0006; // 255 buffers of 2^5 bytes each
+ break;
+ case 128:
+ arg = 0x00ff0007; // 255 buffers of 2^7 bytes each
+ break;
+ case 256:
+ arg = 0x00ff0008; // 255 buffers of 2^8 bytes each
+ break;
+ default:
+ arg = 0x00ff0007; // 255 buffers of 2^7 bytes each
+ }
+ } else {
+ arg = 0x00ff000a; // 255 buffers of 2^10 bytes each
+ }
+ status = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg);
+ if (status == -1) {
+ string msg("SNDCTL_DSP_FRAGMENT ioctl failed: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_oss_io::open",
+ LOG_NORMAL, LOG_CRITICAL);
+ ui->cb_display_msg(TRANSLATE("Cannot set buffer size on sound card."),
+ MSG_CRITICAL);
+ close(fd);
+ return false;
+ }
+ // Channels
+ arg = channels;
+ status = ioctl(fd, SNDCTL_DSP_CHANNELS, &arg);
+ if (status == -1) {
+ string msg("SNDCTL_DSP_CHANNELS ioctl failed: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_oss_io::open",
+ LOG_NORMAL, LOG_CRITICAL);
+ msg = TRANSLATE("Sound card cannot be set to %1 channels.");
+ msg = replace_first(msg, "%1", int2str(channels));
+ ui->cb_display_msg(msg, MSG_CRITICAL);
+ return false;
+ }
+ if (arg != channels) {
+ log_file->write_report("Unable to set channels",
+ "t_oss_io::open",
+ LOG_NORMAL, LOG_CRITICAL);
+ string msg = "Sound card cannot be set to ";
+ msg = TRANSLATE("Sound card cannot be set to %1 channels.");
+ msg = replace_first(msg, "%1", int2str(channels));
+ ui->cb_display_msg(msg, MSG_CRITICAL);
+ return false;
+ }
+ // Sample format
+ int fmt;
+ switch (format) {
+ case SAMPLEFORMAT_S16:
+#ifdef WORDS_BIGENDIAN
+ fmt = AFMT_S16_BE;
+#else
+ fmt = AFMT_S16_LE;
+#endif
+ break;
+ case SAMPLEFORMAT_U16:
+#ifdef WORDS_BIGENDIAN
+ fmt = AFMT_U16_BE;
+#else
+ fmt = AFMT_U16_LE;
+#endif
+ break;
+ case SAMPLEFORMAT_S8:
+ fmt = AFMT_S8;
+ break;
+ case SAMPLEFORMAT_U8:
+ fmt = AFMT_U8;
+ break;
+ default:
+ fmt = 0; // fail
+ }
+ arg = fmt;
+ status = ioctl(fd, SNDCTL_DSP_SETFMT, &arg);
+ if (status == -1) {
+ string msg("SNDCTL_DSP_SETFMT ioctl failed: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_oss_io::open",
+ LOG_NORMAL, LOG_CRITICAL);
+ ui->cb_display_msg(TRANSLATE("Cannot set sound card to 16 bits recording."),
+ MSG_CRITICAL);
+ return false;
+ }
+
+ arg = fmt;
+ status = ioctl(fd, SOUND_PCM_WRITE_BITS, &arg);
+ if (status == -1) {
+ string msg("SOUND_PCM_WRITE_BITS ioctl failed: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_oss_io::open",
+ LOG_NORMAL, LOG_CRITICAL);
+ ui->cb_display_msg(TRANSLATE("Cannot set sound card to 16 bits playing."),
+ MSG_CRITICAL);
+ return false;
+ }
+
+ // Sample rate
+ arg = sample_rate;
+ status = ioctl(fd, SNDCTL_DSP_SPEED, &arg);
+ if (status == -1) {
+ string msg("SNDCTL_DSP_SPEED ioctl failed: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_oss_io::open",
+ LOG_NORMAL, LOG_CRITICAL);
+ msg = TRANSLATE("Cannot set sound card sample rate to %1");
+ msg = replace_first(msg, "%1", int2str(sample_rate));
+ ui->cb_display_msg(msg, MSG_CRITICAL);
+ return false;
+ }
+
+ play_buffersize = rec_buffersize = 0;
+ if (playback) play_buffersize = get_buffer_space(false);
+ if (capture) rec_buffersize = get_buffer_space(true);
+ return true;
+}
+
+void t_oss_io::enable(bool enable_playback, bool enable_recording) {
+ int arg, status;
+ arg = enable_recording ? PCM_ENABLE_INPUT : 0;
+ arg |= enable_playback ? PCM_ENABLE_OUTPUT : 0;
+ status = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &arg);
+ if (status == -1) {
+ string msg("SNDCTL_DSP_SETTRIGGER ioctl failed: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_oss_io::enable",
+ LOG_NORMAL, LOG_CRITICAL);
+ }
+}
+
+void t_oss_io::flush(bool playback_buffer, bool recording_buffer) {
+ for (int i = 0; i < 2; i++) {
+ // i == 0: flush playback buffer, 1: flush recording buffer
+ if (i == 0 && playback_buffer || i == 1 && recording_buffer) {
+ int skip_bytes = ( (i==0) ? play_buffersize :
+ rec_buffersize) - get_buffer_space(i == 1);
+ if(skip_bytes <= 0) continue;
+ unsigned char *trash = new unsigned char[skip_bytes];
+ MEMMAN_NEW_ARRAY(trash);
+ read(trash, skip_bytes);
+ MEMMAN_DELETE_ARRAY(trash);
+ delete [] trash;
+ }
+ }
+}
+
+int t_oss_io::get_buffer_space(bool is_recording_buffer)
+{
+ audio_buf_info dsp_info;
+ int status = ioctl(fd, is_recording_buffer ? SNDCTL_DSP_GETISPACE :
+ SNDCTL_DSP_GETOSPACE, &dsp_info);
+ if (status == -1) return 0;
+ return dsp_info.bytes;
+}
+
+int t_oss_io::get_buffer_size(bool is_recording_buffer)
+{
+ if (is_recording_buffer) return rec_buffersize;
+ else return play_buffersize;
+}
+
+bool t_oss_io::play_buffer_underrun(void) {
+ return get_buffer_space(false) >= get_buffer_size(false);
+}
+
+
+int t_oss_io::read(unsigned char* buf, int len) {
+ return ::read(fd, buf, len);
+}
+
+int t_oss_io::write(const unsigned char* buf, int len) {
+ return ::write(fd, buf, len);
+}
+
+
+#ifdef HAVE_LIBASOUND
+t_alsa_io::t_alsa_io() : pcm_play_ptr(0), pcm_rec_ptr(0), play_framesize(1), rec_framesize(1),
+ play_buffersize(0), rec_buffersize(0) {
+}
+
+t_alsa_io::~t_alsa_io() {
+ if (pcm_play_ptr) {
+ log_file->write_header("t_alsa_io::~t_alsa_io", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("snd_pcm_close, handle = ");
+ log_file->write_raw(ptr2str(pcm_play_ptr));
+ log_file->write_endl();
+ log_file->write_footer();
+
+ // Without the snd_pcm_hw_free, snd_pcm_close sometimes fails.
+ snd_pcm_hw_free(pcm_play_ptr);
+ snd_pcm_close(pcm_play_ptr);
+ pcm_play_ptr = 0;
+ }
+ if (pcm_rec_ptr) {
+ log_file->write_header("t_alsa_io::~t_alsa_io", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("snd_pcm_close, handle = ");
+ log_file->write_raw(ptr2str(pcm_rec_ptr));
+ log_file->write_endl();
+ log_file->write_footer();
+
+ snd_pcm_hw_free(pcm_rec_ptr);
+ snd_pcm_close(pcm_rec_ptr);
+ pcm_rec_ptr = 0;
+ }
+}
+
+
+bool t_alsa_io::open(const string& device, bool playback, bool capture, bool blocking, int channels, t_audio_sampleformat format, int sample_rate, bool short_latency)
+{
+ t_audio_io::open(device, playback, capture, blocking, channels, format, sample_rate,
+ short_latency);
+
+ int mode = 0;
+ string msg;
+
+ this->short_latency = short_latency;
+
+ const char* dev = device.c_str();
+ if(dev[0] == 0) dev = "default";
+
+ log_file->write_header("t_alsa_io::open", LOG_NORMAL);
+ log_file->write_raw("Opening ALSA device: ");
+ log_file->write_raw(dev);
+ log_file->write_endl();
+ if (playback) log_file->write_raw("play\n");
+ if (capture) log_file->write_raw("capture\n");
+ log_file->write_footer();
+
+ if (playback && capture) {
+ // Full duplex: Perform the operation in two steps
+ if (!open(device, true, false, blocking, channels, format,
+ sample_rate, short_latency))
+ return false;
+ if (!open(device, false, true, blocking, channels, format,
+ sample_rate, short_latency))
+ return false;
+
+ return true;
+ }
+ snd_pcm_t* pcm_ptr;
+
+#define HANDLE_ALSA_ERROR(func) string msg(func); msg += " failed: "; msg += snd_strerror(err); \
+ log_file->write_report(msg, "t_alsa_io::open", LOG_NORMAL, LOG_CRITICAL); msg = TRANSLATE("Opening ALSA driver failed") + ": " + msg; \
+ ui->cb_display_msg(msg, MSG_CRITICAL); if(pcm_ptr) snd_pcm_close(pcm_ptr); return false;
+
+ if (!blocking) mode = SND_PCM_NONBLOCK;
+
+ int err = snd_pcm_open(&pcm_ptr, dev, playback ? SND_PCM_STREAM_PLAYBACK :
+ SND_PCM_STREAM_CAPTURE, mode);
+ if (err < 0) {
+ string msg("snd_pcm_open failed: ");
+ msg += snd_strerror(err);
+ log_file->write_report(msg, "t_alsa_io::open",
+ LOG_NORMAL, LOG_CRITICAL);
+ msg = "Cannot open ALSA driver for PCM ";
+
+ if (playback) {
+ msg = TRANSLATE("Cannot open ALSA driver for PCM playback");
+ } else {
+ msg = TRANSLATE("Cannot open ALSA driver for PCM capture");
+ }
+ msg += ": ";
+ msg += snd_strerror(err);
+ ui->cb_display_msg(msg, MSG_CRITICAL);
+ return false;
+ }
+
+ log_file->write_header("t_alsa_io::open", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("snd_pcm_open succeeded, handle = ");
+ log_file->write_raw(ptr2str(pcm_ptr));
+ log_file->write_endl();
+ log_file->write_footer();
+
+ snd_pcm_hw_params_t *hw_params;
+ snd_pcm_sw_params_t *sw_params;
+
+ // Set HW params
+ if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
+ HANDLE_ALSA_ERROR("snd_pcm_hw_params_malloc");
+ }
+ MEMMAN_NEW(hw_params);
+
+ if ((err = snd_pcm_hw_params_any (pcm_ptr, hw_params)) < 0) {
+ HANDLE_ALSA_ERROR("snd_pcm_hw_params_any");
+ }
+
+ if ((err = snd_pcm_hw_params_set_access (pcm_ptr, hw_params,
+ SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+ {
+ HANDLE_ALSA_ERROR("snd_pcm_hw_params_set_access");
+ }
+
+ snd_pcm_format_t fmt;
+ int sample_bits;
+ switch (format) {
+ case SAMPLEFORMAT_S16:
+#ifdef WORDS_BIGENDIAN
+ fmt = SND_PCM_FORMAT_S16_BE;
+#else
+ fmt = SND_PCM_FORMAT_S16_LE;
+#endif
+ sample_bits = 16;
+ break;
+ case SAMPLEFORMAT_U16:
+#ifdef WORDS_BIGENDIAN
+ fmt = SND_PCM_FORMAT_U16_BE;
+#else
+ fmt = SND_PCM_FORMAT_U16_LE;
+#endif
+ sample_bits = 16;
+ break;
+ case SAMPLEFORMAT_S8:
+ fmt = SND_PCM_FORMAT_S8;
+ sample_bits = 8;
+ break;
+ case SAMPLEFORMAT_U8:
+ fmt = SND_PCM_FORMAT_U8;
+ sample_bits = 8;
+ break;
+ default:
+ {HANDLE_ALSA_ERROR("invalid sample format");}
+ }
+
+ if ((err = snd_pcm_hw_params_set_format (pcm_ptr, hw_params, fmt)) < 0) {
+ string s("snd_pcm_hw_params_set_format(");
+ s += int2str(fmt);
+ s += ")";
+ HANDLE_ALSA_ERROR(s);
+ }
+
+ // Some sound cards do not support the exact sample rate specified in the
+ // wav files. Still we first try to set the exact sample rate instead of
+ // specifying the 3rd parameter as -1 to choose an approximate.
+ // For some reason on my first sound card that supports the exact rate,
+ // I get mono sound when the -1 parameter is specified.
+ if ((err = snd_pcm_hw_params_set_rate (pcm_ptr, hw_params, sample_rate, 0)) < 0) {
+ msg = "Cannot set soundcard to exact sample rate of ";
+ msg += int2str(sample_rate);
+ msg += ".\nThe card will choose a lower approximate rate.";
+ log_file->write_report(msg, "t_alsa_io::open", LOG_NORMAL, LOG_WARNING);
+
+ if ((err = snd_pcm_hw_params_set_rate (pcm_ptr, hw_params, sample_rate, -1)) < 0) {
+ string s("snd_pcm_hw_params_set_rate(");
+ s += int2str(sample_rate);
+ s += ")";
+ HANDLE_ALSA_ERROR(s);
+ }
+ }
+
+ // Read back card rate for reporting in the log file.
+ unsigned int card_rate;
+ int card_dir;
+ snd_pcm_hw_params_get_rate(hw_params, &card_rate, &card_dir);
+
+ if ((err = snd_pcm_hw_params_set_channels (pcm_ptr, hw_params, channels)) < 0) {
+ string s("snd_pcm_hw_params_set_channels(");
+ s += int2str(channels);
+ s += ")";
+ HANDLE_ALSA_ERROR(s);
+ }
+
+ // Note: The buffersize is in FRAMES, not BYTES (one frame = sizeof(sample) * channels)
+ snd_pcm_uframes_t buffersize;
+ unsigned int periods = 8; // Double buffering
+ int dir = 1;
+
+ // Set the size of one period in samples
+ if (short_latency) {
+ if (playback) {
+ buffersize = sys_config->get_alsa_play_period_size();
+ } else {
+ buffersize = sys_config->get_alsa_capture_period_size();
+ }
+ } else {
+ buffersize = 1024;
+ }
+ if ((err = snd_pcm_hw_params_set_period_size_near (pcm_ptr, hw_params,
+ &buffersize, &dir)) < 0)
+ {
+ HANDLE_ALSA_ERROR("snd_pcm_hw_params_set_period_size_near");
+ }
+
+ // The number of periods determines the ALSA application buffer size.
+ // This size must be larger than the jitter buffer.
+ // TODO: use some more sophisticated algorithm here: read back the period
+ // size and calculate the number of periods needed (only in the
+ // short latency case)?
+ if (buffersize <= 64) {
+ periods *= 8;
+ } else if (buffersize <= 256) {
+ periods *= 4;
+ }
+
+ dir = 1;
+ if ((err = snd_pcm_hw_params_set_periods(pcm_ptr, hw_params, periods, dir)) < 0) {
+ if ((err = snd_pcm_hw_params_set_periods_near(pcm_ptr, hw_params,
+ &periods, &dir)) < 0)
+ {
+ HANDLE_ALSA_ERROR("snd_pcm_hw_params_set_periods");
+ }
+ }
+
+ if ((err = snd_pcm_hw_params (pcm_ptr, hw_params)) < 0) {
+ HANDLE_ALSA_ERROR("snd_pcm_hw_params");
+ }
+
+ // Find out if the sound card supports pause functionality
+ can_pause = (snd_pcm_hw_params_can_pause(hw_params) == 1);
+
+ MEMMAN_DELETE(hw_params);
+ snd_pcm_hw_params_free(hw_params);
+
+ // Set SW params
+ if ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) {
+ HANDLE_ALSA_ERROR("snd_pcm_sw_params_malloc");
+ }
+ MEMMAN_NEW(sw_params);
+
+ if ((err = snd_pcm_sw_params_current (pcm_ptr, sw_params)) < 0) {
+ HANDLE_ALSA_ERROR("snd_pcm_sw_params_current");
+ }
+ if ((err = snd_pcm_sw_params (pcm_ptr, sw_params)) < 0) {
+ HANDLE_ALSA_ERROR("snd_pcm_sw_params");
+ }
+
+ MEMMAN_DELETE(sw_params);
+ snd_pcm_sw_params_free(sw_params);
+
+ if ((err = snd_pcm_prepare (pcm_ptr)) < 0) {
+ HANDLE_ALSA_ERROR("snd_pcm_prepare");
+ }
+ if (playback) {
+ pcm_play_ptr = pcm_ptr;
+ play_framesize = (sample_bits / 8) * channels;
+ play_buffersize = (int)buffersize * periods * play_framesize;
+ play_periods = periods;
+
+ log_file->write_header("t_alsa_io::open", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("ALSA playback buffer settings.\n");
+ log_file->write_raw("Rate = ");
+ log_file->write_raw(card_rate);
+ log_file->write_raw(" frames/sec\n");
+ log_file->write_raw("Frame size = ");
+ log_file->write_raw(play_framesize);
+ log_file->write_raw(" bytes\n");
+ log_file->write_raw("Periods = ");
+ log_file->write_raw(play_periods);
+ log_file->write_endl();
+ log_file->write_raw("Period size = ");
+ log_file->write_raw(buffersize * play_framesize);
+ log_file->write_raw(" bytes\n");
+ log_file->write_raw("Buffer size = ");
+ log_file->write_raw(play_buffersize);
+ log_file->write_raw(" bytes\n");
+ log_file->write_raw("Can pause: ");
+ log_file->write_bool(can_pause);
+ log_file->write_endl();
+ log_file->write_footer();
+ } else {
+ // Since audio_rx checks buffer before reading, start manually
+ if ((err = snd_pcm_start(pcm_ptr)) < 0) {
+ HANDLE_ALSA_ERROR("snd_pcm_start");
+ }
+
+ pcm_rec_ptr = pcm_ptr;
+ rec_framesize = (sample_bits / 8) * channels;
+ rec_buffersize = (int)buffersize * periods * rec_framesize;
+ rec_periods = periods;
+
+ // HACK: snd_pcm_delay seems not to work for the dsnoop device.
+ // This code determines if snd_pcm is working. As the capture
+ // device just started, it should return zero or a small number.
+ // So if it returns that more than half of the buffer is filled
+ // already, it seems broken.
+ rec_delay_broken = false;
+ if (get_buffer_space(true) > rec_buffersize / 2) {
+ rec_delay_broken = true;
+ log_file->write_report(
+ "snd_pcm_delay does not work for capture buffer.",
+ "t_alsa_io::open", LOG_NORMAL, LOG_DEBUG);
+ }
+
+ log_file->write_header("t_alsa_io::open", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("ALSA capture buffer settings.\n");
+ log_file->write_raw("Rate = ");
+ log_file->write_raw(card_rate);
+ log_file->write_raw(" frames/sec\n");
+ log_file->write_raw("Frame size = ");
+ log_file->write_raw(rec_framesize);
+ log_file->write_raw(" bytes\n");
+ log_file->write_raw("Periods = ");
+ log_file->write_raw(rec_periods);
+ log_file->write_endl();
+ log_file->write_raw("Period size = ");
+ log_file->write_raw(buffersize * rec_framesize);
+ log_file->write_raw(" bytes\n");
+ log_file->write_raw("Buffer size = ");
+ log_file->write_raw(rec_buffersize);
+ log_file->write_raw(" bytes\n");
+ log_file->write_raw("Can pause: ");
+ log_file->write_bool(can_pause);
+ log_file->write_endl();
+ log_file->write_footer();
+ }
+
+ return true;
+#undef HANDLE_ALSA_ERROR
+}
+
+
+void t_alsa_io::enable(bool enable_playback, bool enable_recording) {
+ if (!can_pause) return;
+
+ if (pcm_play_ptr) {
+ snd_pcm_pause(pcm_play_ptr, (int)enable_playback);
+ }
+ if (pcm_rec_ptr) {
+ snd_pcm_pause(pcm_rec_ptr, (int)enable_recording);
+ }
+}
+
+void t_alsa_io::flush(bool playback_buffer, bool recording_buffer) {
+ if (playback_buffer && pcm_play_ptr) {
+ // snd_pcm_reset(pcm_play_ptr);
+ snd_pcm_drop(pcm_play_ptr);
+ snd_pcm_prepare(pcm_play_ptr);
+ snd_pcm_start(pcm_play_ptr);
+ }
+ if (recording_buffer && pcm_rec_ptr) {
+ // For some obscure reason snd_pcm_reset causes the CPU
+ // load to rise to 99.9% when playing and capturing is
+ // done on the same sound card.
+ // Therefor snd_pcm_reset is replaced by functions to
+ // stop the card, drop samples and start again.
+ // snd_pcm_reset(pcm_rec_ptr);
+ snd_pcm_drop(pcm_rec_ptr);
+ snd_pcm_prepare(pcm_rec_ptr);
+ snd_pcm_start(pcm_rec_ptr);
+ }
+}
+
+int t_alsa_io::get_buffer_space(bool is_recording_buffer) {
+ int rv;
+ int err;
+ snd_pcm_sframes_t delay;
+ snd_pcm_status_t* status;
+ snd_pcm_status_alloca(&status);
+
+ if(is_recording_buffer) {
+ if(!pcm_rec_ptr) return 0;
+
+ // This does not work as snd_pcm_status_get_avail_max does not return
+ // accurate results.
+ // snd_pcm_status(pcm_rec_ptr, status);
+ // rv = rec_framesize * snd_pcm_status_get_avail_max(status);
+
+ snd_pcm_hwsync(pcm_rec_ptr);
+ if ((err = snd_pcm_delay(pcm_rec_ptr, &delay)) < 0) {
+ string msg = "snd_pcm_delay for capture buffer failed: ";
+ msg += snd_strerror(err);
+ log_file->write_report(msg, "t_alsa_io::get_buffer_space",
+ LOG_NORMAL, LOG_DEBUG);
+ delay = 0;
+ snd_pcm_prepare(pcm_rec_ptr);
+ }
+
+ if (rec_delay_broken) {
+ rv = 0; // there is no way to get an accurate number
+ } else {
+ rv = int(delay * rec_framesize);
+ }
+
+ if (rv > rec_buffersize) {
+ rv = rec_buffersize; // capture buffer overrun
+ snd_pcm_prepare(pcm_rec_ptr);
+ }
+ } else {
+ if(!pcm_play_ptr) return 0;
+
+ snd_pcm_status(pcm_play_ptr, status);
+ rv = play_framesize * snd_pcm_status_get_avail_max(status);
+
+ if (rv > play_buffersize) {
+ rv = play_buffersize; // playback buffer underrun
+ snd_pcm_prepare(pcm_play_ptr);
+ }
+ }
+
+ return rv;
+}
+
+int t_alsa_io::get_buffer_size(bool is_recording_buffer)
+{
+ if (is_recording_buffer) return rec_buffersize;
+ else return play_buffersize;
+}
+
+bool t_alsa_io::play_buffer_underrun(void) {
+ if (!pcm_play_ptr) return false;
+ return snd_pcm_state(pcm_play_ptr) == SND_PCM_STATE_XRUN;
+}
+
+int t_alsa_io::read(unsigned char* buf, int len) {
+ string msg;
+
+ if (!pcm_rec_ptr) {
+ log_file->write_report("Illegal pcm_rec_prt.",
+ "t_alsa_io::read", LOG_NORMAL, LOG_CRITICAL);
+ return -1;
+ }
+
+ int len_frames = len / rec_framesize;
+ int read_frames = 0;
+
+ for(;;) {
+ int read = snd_pcm_readi(pcm_rec_ptr, buf, len_frames);
+ if (read == -EPIPE) {
+ msg = "Capture buffer overrun.";
+ log_file->write_report(msg, "t_alsa_io::read", LOG_NORMAL, LOG_DEBUG);
+ snd_pcm_prepare(pcm_rec_ptr);
+ snd_pcm_start(pcm_rec_ptr);
+ continue;
+ } else if (read <= 0) {
+ msg = "PCM read error: ";
+ msg += snd_strerror(read);
+ log_file->write_report(msg, "t_alsa_io::read", LOG_NORMAL, LOG_DEBUG);
+ return -1;
+ } else if (read < len_frames) {
+ buf += rec_framesize * read;
+ len_frames -= read;
+ read_frames += read;
+ continue;
+ }
+ return (read_frames + read) * rec_framesize;
+ }
+}
+int t_alsa_io::write(const unsigned char* buf, int len) {
+ int len_frames = len / play_framesize;
+ int frames_written = 0;
+ string msg;
+
+ for (;;) {
+ if(!pcm_play_ptr) return -1;
+ int written = snd_pcm_writei(pcm_play_ptr, buf, len_frames);
+ if (written == -EPIPE) {
+ msg = "Playback buffer underrun.";
+ log_file->write_report(msg, "t_alsa_io::write", LOG_NORMAL, LOG_DEBUG);
+ snd_pcm_prepare(pcm_play_ptr);
+ continue;
+ } else if (written == -EINVAL) {
+ msg = "Invalid argument passed to snd_pcm_writei: ";
+ msg += snd_strerror(written);
+ log_file->write_report(msg, "t_alsa_io::write", LOG_NORMAL, LOG_DEBUG);
+ } else if (written < 0) {
+ msg = "PCM write error: ";
+ msg += snd_strerror(written);
+ log_file->write_report(msg, "t_alsa_io::write", LOG_NORMAL, LOG_DEBUG);
+ return -1;
+ } else if (written < len_frames) {
+ buf += written * play_framesize;
+ len_frames -= written;
+ frames_written += written;
+ continue;
+ }
+ return (frames_written + written) * play_framesize;
+ }
+}
+
+
+// This function fills the specified list with ALSA hardware soundcards found on the system.
+// It uses plughw:xx instead of hw:xx for specifiers, because hw:xx are not practical to
+// use (e.g. they require a resampler/channel mixer in the application).
+// playback indicates if a list with playback or capture devices should be created.
+void alsa_fill_soundcards(list<t_audio_device>& l, bool playback)
+{
+ int err = 0;
+ int card = -1, device = -1;
+ snd_ctl_t *handle;
+ snd_ctl_card_info_t *info;
+ snd_pcm_info_t *pcminfo;
+ snd_ctl_card_info_alloca(&info);
+ snd_pcm_info_alloca(&pcminfo);
+
+ for (;;) {
+ err = snd_card_next(&card);
+ if (err < 0 || card < 0) break;
+ if (card >= 0) {
+ string name = "hw:";
+ name += int2str(card);
+ if ((err = snd_ctl_open(&handle, name.c_str(), 0)) < 0) continue;
+ if ((err = snd_ctl_card_info(handle, info)) < 0) {
+ snd_ctl_close(handle);
+ continue;
+ }
+
+ const char *card_name = snd_ctl_card_info_get_name(info);
+
+ for (;;) {
+ err = snd_ctl_pcm_next_device(handle, &device);
+ if (err < 0 || device < 0) break;
+
+ snd_pcm_info_set_device(pcminfo, device);
+ snd_pcm_info_set_subdevice(pcminfo, 0);
+
+ if (playback) {
+ snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
+ } else {
+ snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE);
+ }
+
+ if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) continue;
+
+ t_audio_device dev;
+ dev.device = string("plughw:") + int2str(card) +
+ string(",") + int2str(device);
+ dev.name = string(card_name) + " (";
+ dev.name += snd_pcm_info_get_name(pcminfo);
+ dev.name += ")";
+ dev.type = t_audio_device::ALSA;
+ l.push_back(dev);
+
+ }
+
+ snd_ctl_close(handle);
+ }
+ }
+}
+
+// endif ifdef HAVE_LIBASOUND
+#endif
diff --git a/src/audio/audio_device.h b/src/audio/audio_device.h
new file mode 100644
index 0000000..27503f8
--- /dev/null
+++ b/src/audio/audio_device.h
@@ -0,0 +1,126 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _AUDIO_DEVICE_H
+#define _AUDIO_DEVICE_H
+
+#include <string>
+#include "twinkle_config.h"
+
+using namespace std;
+
+#ifndef _SYS_SETTINGS_H
+class t_audio_device;
+#endif
+
+enum t_audio_sampleformat {
+ SAMPLEFORMAT_U8,
+ SAMPLEFORMAT_S8,
+ SAMPLEFORMAT_S16,
+ SAMPLEFORMAT_U16
+};
+
+class t_audio_io {
+public:
+ virtual ~t_audio_io();
+ virtual void enable(bool enable_playback, bool enable_recording) = 0;
+ virtual void flush(bool playback_buffer, bool recording_buffer) = 0;
+ // Returns the number of bytes that can be written/read without blocking
+ virtual int get_buffer_space(bool is_recording_buffer) = 0;
+ // Returns the size of the hardware buffer
+ virtual int get_buffer_size(bool is_recording_buffer) = 0;
+
+ /** Check if a play buffer underrun occurred. */
+ virtual bool play_buffer_underrun(void) = 0;
+
+ virtual int read(unsigned char* buf, int len) = 0;
+ virtual int write(const unsigned char* buf, int len) = 0;
+ virtual int get_sample_rate(void) const;
+
+ static t_audio_io* open(const t_audio_device& dev, bool playback,
+ bool capture, bool blocking, int channels, t_audio_sampleformat format,
+ int sample_rate, bool short_latency);
+
+ // Validate if an audio device can be opened.
+ static bool validate(const t_audio_device& dev, bool playback, bool capture);
+
+protected:
+ virtual bool open(const string& device, bool playback, bool capture,
+ bool blocking, int channels, t_audio_sampleformat format,
+ int sample_rate, bool short_latency);
+
+private:
+ int _sample_rate;
+
+};
+
+class t_oss_io : public t_audio_io {
+public:
+ t_oss_io();
+ virtual ~t_oss_io();
+ void enable(bool enable_playback, bool enable_recording);
+ void flush(bool playback_buffer, bool recording_buffer);
+ int get_buffer_space(bool is_recording_buffer);
+ int get_buffer_size(bool is_recording_buffer);
+ bool play_buffer_underrun(void);
+ int read(unsigned char* buf, int len);
+ int write(const unsigned char* buf, int len);
+protected:
+ bool open(const string& device, bool playback, bool capture, bool blocking,
+ int channels, t_audio_sampleformat format, int sample_rate,
+ bool short_latency);
+private:
+ int fd;
+ int play_buffersize, rec_buffersize;
+};
+
+#ifdef HAVE_LIBASOUND
+class t_alsa_io : public t_audio_io {
+public:
+ t_alsa_io();
+ virtual ~t_alsa_io();
+ void enable(bool enable_playback, bool enable_recording);
+ void flush(bool playback_buffer, bool recording_buffer);
+ int get_buffer_space(bool is_recording_buffer);
+ int get_buffer_size(bool is_recording_buffer);
+ bool play_buffer_underrun(void);
+ int read(unsigned char* buf, int len);
+ int write(const unsigned char* buf, int len);
+protected:
+ bool open(const string& device, bool playback, bool capture, bool blocking,
+ int channels, t_audio_sampleformat format, int sample_rate,
+ bool short_latency);
+private:
+ struct _snd_pcm *pcm_play_ptr, *pcm_rec_ptr;
+ int play_framesize, rec_framesize;
+ int play_buffersize, rec_buffersize;
+ int play_periods, rec_periods;
+ bool short_latency;
+
+ // snd_pcm_delay should return the number of bytes in the buffer.
+ // For some reason however, if the capture device is a software mixer,
+ // it returns inaccurate values.
+ // This flag if the functionality is broken.
+ bool rec_delay_broken;
+
+ // Indicates if snd_pcm_pause works for this device
+ bool can_pause;
+};
+#endif
+
+#endif
diff --git a/src/audio/audio_encoder.cpp b/src/audio/audio_encoder.cpp
new file mode 100644
index 0000000..94e82b4
--- /dev/null
+++ b/src/audio/audio_encoder.cpp
@@ -0,0 +1,430 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <cassert>
+#include <iostream>
+#include "audio_encoder.h"
+
+#ifdef HAVE_ILBC
+#ifndef HAVE_ILBC_CPP
+extern "C" {
+#endif
+#include <ilbc/iLBC_encode.h>
+#ifndef HAVE_ILBC_CPP
+}
+#endif
+#endif
+
+//////////////////////////////////////////
+// class t_audio_encoder
+//////////////////////////////////////////
+
+t_audio_encoder::t_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config) :
+ _payload_id(payload_id),
+ _ptime(ptime),
+ _user_config(user_config)
+{}
+
+t_audio_codec t_audio_encoder::get_codec(void) const {
+ return _codec;
+}
+
+uint16 t_audio_encoder::get_payload_id(void) const {
+ return _payload_id;
+}
+
+uint16 t_audio_encoder::get_ptime(void) const {
+ return _ptime;
+}
+
+uint16 t_audio_encoder::get_sample_rate(void) const {
+ return audio_sample_rate(_codec);
+}
+
+uint16 t_audio_encoder::get_max_payload_size(void) const {
+ return _max_payload_size;
+}
+
+
+//////////////////////////////////////////
+// class t_g711a_audio_encoder
+//////////////////////////////////////////
+
+t_g711a_audio_encoder::t_g711a_audio_encoder(uint16 payload_id, uint16 ptime,
+ t_user *user_config) :
+ t_audio_encoder(payload_id, ptime, user_config)
+{
+ _codec = CODEC_G711_ALAW;
+ if (ptime == 0) _ptime = PTIME_G711_ALAW;
+ _max_payload_size = audio_sample_rate(_codec)/1000 * _ptime;
+}
+
+uint16 t_g711a_audio_encoder::encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence)
+{
+ assert(nsamples <= payload_size);
+ silence = false;
+
+ for (int i = 0; i < nsamples; i++) {
+ payload[i] = linear2alaw(sample_buf[i]);
+ }
+
+ return nsamples;
+}
+
+
+//////////////////////////////////////////
+// class t_g711u_audio_encoder
+//////////////////////////////////////////
+
+t_g711u_audio_encoder::t_g711u_audio_encoder(uint16 payload_id, uint16 ptime,
+ t_user *user_config) :
+ t_audio_encoder(payload_id, ptime, user_config)
+{
+ _codec = CODEC_G711_ULAW;
+ if (ptime == 0) _ptime = PTIME_G711_ULAW;
+ _max_payload_size = audio_sample_rate(_codec)/1000 * _ptime;
+}
+
+uint16 t_g711u_audio_encoder::encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence)
+{
+ assert(nsamples <= payload_size);
+ silence = false;
+
+ for (int i = 0; i < nsamples; i++) {
+ payload[i] = linear2ulaw(sample_buf[i]);
+ }
+
+ return nsamples;
+}
+
+
+//////////////////////////////////////////
+// class t_gsm_audio_encoder
+//////////////////////////////////////////
+
+t_gsm_audio_encoder::t_gsm_audio_encoder(uint16 payload_id, uint16 ptime,
+ t_user *user_config) :
+ t_audio_encoder(payload_id, PTIME_GSM, user_config)
+{
+ _codec = CODEC_GSM;
+ _max_payload_size = 33;
+ gsm_encoder = gsm_create();
+}
+
+t_gsm_audio_encoder::~t_gsm_audio_encoder() {
+ gsm_destroy(gsm_encoder);
+}
+
+uint16 t_gsm_audio_encoder::encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence)
+{
+ assert(payload_size >= _max_payload_size);
+ silence = false;
+ gsm_encode(gsm_encoder, sample_buf, payload);
+ return _max_payload_size;
+}
+
+
+#ifdef HAVE_SPEEX
+//////////////////////////////////////////
+// class t_speex_audio_encoder
+//////////////////////////////////////////
+
+t_speex_audio_encoder::t_speex_audio_encoder(uint16 payload_id, uint16 ptime,
+ t_mode mode, t_user *user_config) :
+ t_audio_encoder(payload_id, PTIME_SPEEX, user_config)
+{
+ speex_bits_init(&speex_bits);
+ _mode = mode;
+
+ switch (mode) {
+ case MODE_NB:
+ _codec = CODEC_SPEEX_NB;
+ speex_enc_state = speex_encoder_init(&speex_nb_mode);
+ break;
+ case MODE_WB:
+ _codec = CODEC_SPEEX_WB;
+ speex_enc_state = speex_encoder_init(&speex_wb_mode);
+ break;
+ case MODE_UWB:
+ _codec = CODEC_SPEEX_UWB;
+ speex_enc_state = speex_encoder_init(&speex_uwb_mode);
+ break;
+ default:
+ assert(false);
+ }
+
+ int arg;
+
+ // Bit rate type
+ switch (_user_config->get_speex_bit_rate_type()) {
+ case BIT_RATE_CBR:
+ arg = 0;
+ speex_encoder_ctl(speex_enc_state, SPEEX_SET_VBR, &arg);
+ break;
+ case BIT_RATE_VBR:
+ arg = 1;
+ speex_encoder_ctl(speex_enc_state, SPEEX_SET_VBR, &arg);
+ break;
+ case BIT_RATE_ABR:
+ if (_codec == CODEC_SPEEX_NB) {
+ arg = user_config->get_speex_abr_nb();
+ speex_encoder_ctl(speex_enc_state, SPEEX_SET_ABR, &arg);
+ } else {
+ arg = user_config->get_speex_abr_wb();
+ speex_encoder_ctl(speex_enc_state, SPEEX_SET_ABR, &arg);
+ }
+ break;
+ default:
+ assert(false);
+ }
+
+ _max_payload_size = 1500;
+
+ /*** ENCODER OPTIONS ***/
+
+ // Discontinuos trasmission
+ arg = (_user_config->get_speex_dtx() ? 1 : 0);
+ speex_encoder_ctl(speex_enc_state, SPEEX_SET_DTX, &arg);
+
+ // Quality
+ arg = _user_config->get_speex_quality();
+ if (_user_config->get_speex_bit_rate_type() == BIT_RATE_VBR)
+ speex_encoder_ctl(speex_enc_state, SPEEX_SET_VBR_QUALITY, &arg);
+ else
+ speex_encoder_ctl(speex_enc_state, SPEEX_SET_QUALITY, &arg);
+
+ // Complexity
+ arg = _user_config->get_speex_complexity();
+ speex_encoder_ctl(speex_enc_state, SPEEX_SET_COMPLEXITY, &arg);
+}
+
+t_speex_audio_encoder::~t_speex_audio_encoder() {
+ speex_bits_destroy(&speex_bits);
+ speex_encoder_destroy(speex_enc_state);
+}
+
+uint16 t_speex_audio_encoder::encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence)
+{
+ assert(payload_size >= _max_payload_size);
+
+ silence = false;
+ speex_bits_reset(&speex_bits);
+
+ if (speex_encode_int(speex_enc_state, sample_buf, &speex_bits) == 0)
+ silence = true;
+
+ return speex_bits_write(&speex_bits, (char *)payload, payload_size);
+}
+#endif
+
+#ifdef HAVE_ILBC
+//////////////////////////////////////////
+// class t_ilbc_audio_encoder
+//////////////////////////////////////////
+
+t_ilbc_audio_encoder::t_ilbc_audio_encoder(uint16 payload_id, uint16 ptime,
+ t_user *user_config) :
+ t_audio_encoder(payload_id, (ptime < 25 ? 20 : 30), user_config)
+{
+ _codec = CODEC_ILBC;
+ _mode = _ptime;
+
+ if (_mode == 20) {
+ _max_payload_size = NO_OF_BYTES_20MS;
+ } else {
+ _max_payload_size = NO_OF_BYTES_30MS;
+ }
+
+ initEncode(&_ilbc_encoder, _mode);
+}
+
+uint16 t_ilbc_audio_encoder::encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence)
+{
+ assert(payload_size >= _max_payload_size);
+ assert(nsamples == _ilbc_encoder.blockl);
+
+ silence = false;
+ float block[nsamples];
+
+ for (int i = 0; i < nsamples; i++) {
+ block[i] = static_cast<float>(sample_buf[i]);
+ }
+
+ iLBC_encode((unsigned char*)payload, block, &_ilbc_encoder);
+
+ return _ilbc_encoder.no_of_bytes;
+}
+#endif
+
+//////////////////////////////////////////
+// class t_g726_encoder
+//////////////////////////////////////////
+
+t_g726_audio_encoder::t_g726_audio_encoder(uint16 payload_id, uint16 ptime,
+ t_bit_rate bit_rate, t_user *user_config) :
+ t_audio_encoder(payload_id, ptime, user_config)
+{
+ _bit_rate = bit_rate;
+
+ switch (bit_rate) {
+ case BIT_RATE_16:
+ _codec = CODEC_G726_16;
+ break;
+ case BIT_RATE_24:
+ _codec = CODEC_G726_24;
+ break;
+ case BIT_RATE_32:
+ _codec = CODEC_G726_32;
+ break;
+ case BIT_RATE_40:
+ _codec = CODEC_G726_40;
+ break;
+ default:
+ assert(false);
+ }
+
+ if (ptime == 0) _ptime = PTIME_G726;
+ _max_payload_size = audio_sample_rate(_codec)/1000 * _ptime;
+ _packing = user_config->get_g726_packing();
+
+ g72x_init_state(&_state);
+}
+
+uint16 t_g726_audio_encoder::encode_16(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size)
+{
+ assert(nsamples % 4 == 0);
+ assert(nsamples / 4 <= payload_size);
+
+ for (int i = 0; i < nsamples; i += 4) {
+ payload[i >> 2] = 0;
+ for (int j = 0; j < 4; j++) {
+ uint8 v = static_cast<uint8>(g723_16_encoder(sample_buf[i+j],
+ AUDIO_ENCODING_LINEAR, &_state));
+
+ if (_packing == G726_PACK_RFC3551) {
+ payload[i >> 2] |= v << (j * 2);
+ } else {
+ payload[i >> 2] |= v << ((3-j) * 2);
+ }
+ }
+ }
+
+ return nsamples >> 2;
+}
+
+uint16 t_g726_audio_encoder::encode_24(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size)
+{
+ assert(nsamples % 8 == 0);
+ assert(nsamples / 8 * 3 <= payload_size);
+
+ for (int i = 0; i < nsamples; i += 8) {
+ uint32 v = 0;
+ for (int j = 0; j < 8; j++) {
+ if (_packing == G726_PACK_RFC3551) {
+ v |= static_cast<uint32>(g723_24_encoder(sample_buf[i+j],
+ AUDIO_ENCODING_LINEAR, &_state)) << (j * 3);
+ } else {
+ v |= static_cast<uint32>(g723_24_encoder(sample_buf[i+j],
+ AUDIO_ENCODING_LINEAR, &_state)) << ((7-j) * 3);
+ }
+ }
+ payload[(i >> 3) * 3] = static_cast<uint8>(v & 0xff);
+ payload[(i >> 3) * 3 + 1] = static_cast<uint8>((v >> 8) & 0xff);
+ payload[(i >> 3) * 3 + 2] = static_cast<uint8>((v >> 16) & 0xff);
+ }
+
+ return (nsamples >> 3) * 3;
+}
+
+uint16 t_g726_audio_encoder::encode_32(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size)
+{
+ assert(nsamples % 2 == 0);
+ assert(nsamples / 2 <= payload_size);
+
+ for (int i = 0; i < nsamples; i += 2) {
+ payload[i >> 1] = 0;
+ for (int j = 0; j < 2; j++) {
+ uint8 v = static_cast<uint8>(g721_encoder(sample_buf[i+j],
+ AUDIO_ENCODING_LINEAR, &_state));
+
+ if (_packing == G726_PACK_RFC3551) {
+ payload[i >> 1] |= v << (j * 4);
+ } else {
+ payload[i >> 1] |= v << ((1-j) * 4);
+ }
+ }
+ }
+
+ return nsamples >> 1;
+}
+
+uint16 t_g726_audio_encoder::encode_40(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size)
+{
+ assert(nsamples % 8 == 0);
+ assert(nsamples / 8 * 5 <= payload_size);
+
+ for (int i = 0; i < nsamples; i += 8) {
+ uint64 v = 0;
+ for (int j = 0; j < 8; j++) {
+ if (_packing == G726_PACK_RFC3551) {
+ v |= static_cast<uint64>(g723_40_encoder(sample_buf[i+j],
+ AUDIO_ENCODING_LINEAR, &_state)) << (j * 5);
+ } else {
+ v |= static_cast<uint64>(g723_40_encoder(sample_buf[i+j],
+ AUDIO_ENCODING_LINEAR, &_state)) << ((7-j) * 5);
+ }
+ }
+ payload[(i >> 3) * 5] = static_cast<uint8>(v & 0xff);
+ payload[(i >> 3) * 5 + 1] = static_cast<uint8>((v >> 8) & 0xff);
+ payload[(i >> 3) * 5 + 2] = static_cast<uint8>((v >> 16) & 0xff);
+ payload[(i >> 3) * 5 + 3] = static_cast<uint8>((v >> 24) & 0xff);
+ payload[(i >> 3) * 5 + 4] = static_cast<uint8>((v >> 32) & 0xff);
+ }
+
+ return (nsamples >> 3) * 5;
+}
+
+uint16 t_g726_audio_encoder::encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence)
+{
+ silence = false;
+
+ switch (_bit_rate) {
+ case BIT_RATE_16:
+ return encode_16(sample_buf, nsamples, payload, payload_size);
+ case BIT_RATE_24:
+ return encode_24(sample_buf, nsamples, payload, payload_size);
+ case BIT_RATE_32:
+ return encode_32(sample_buf, nsamples, payload, payload_size);
+ case BIT_RATE_40:
+ return encode_40(sample_buf, nsamples, payload, payload_size);
+ default:
+ assert(false);
+ }
+
+ return 0;
+}
diff --git a/src/audio/audio_encoder.h b/src/audio/audio_encoder.h
new file mode 100644
index 0000000..d280567
--- /dev/null
+++ b/src/audio/audio_encoder.h
@@ -0,0 +1,183 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+// Audio encoders
+
+#ifndef _AUDIO_ENCODER_H
+#define _AUDIO_ENCODER_H
+
+#include <cc++/config.h>
+#include "twinkle_config.h"
+#include "audio_codecs.h"
+#include "user.h"
+
+#ifdef HAVE_GSM
+#include <gsm/gsm.h>
+#else
+#include "gsm/inc/gsm.h"
+#endif
+
+#ifdef HAVE_SPEEX
+#include <speex/speex.h>
+#endif
+
+#ifdef HAVE_ILBC
+#ifndef HAVE_ILBC_CPP
+extern "C" {
+#endif
+#include <ilbc/iLBC_define.h>
+#ifndef HAVE_ILBC_CPP
+}
+#endif
+#endif
+
+// Abstract definition of an audio encoder
+class t_audio_encoder {
+protected:
+ t_audio_codec _codec;
+ uint16 _payload_id; // payload id for the codec
+ uint16 _ptime; // in milliseconds
+ uint16 _max_payload_size; // maximum size of payload
+
+ t_user *_user_config;
+
+ t_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config);
+
+public:
+ virtual ~t_audio_encoder() {};
+
+ t_audio_codec get_codec(void) const;
+ uint16 get_payload_id(void) const;
+ uint16 get_ptime(void) const;
+ uint16 get_sample_rate(void) const;
+ uint16 get_max_payload_size(void) const;
+
+ // Encode a 16-bit PCM sample buffer to a encoded payload
+ // Returns the number of bytes written into the payload.
+ // The silence flag indicates if the returned sound samples represent silence
+ // that may be suppressed.
+ virtual uint16 encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence) = 0;
+};
+
+
+// G.711 A-law
+class t_g711a_audio_encoder : public t_audio_encoder {
+public:
+ t_g711a_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config);
+
+ virtual uint16 encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence);
+};
+
+
+// G.711 u-law
+class t_g711u_audio_encoder : public t_audio_encoder {
+public:
+ t_g711u_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config);
+
+ virtual uint16 encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence);
+};
+
+
+// GSM
+class t_gsm_audio_encoder : public t_audio_encoder {
+private:
+ gsm gsm_encoder;
+
+public:
+ t_gsm_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config);
+ virtual ~t_gsm_audio_encoder();
+
+ virtual uint16 encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence);
+};
+
+
+#ifdef HAVE_SPEEX
+class t_speex_audio_encoder : public t_audio_encoder {
+public:
+ enum t_mode {
+ MODE_NB, // Narrow band
+ MODE_WB, // Wide band
+ MODE_UWB // Ultra wide band
+ };
+
+private:
+ SpeexBits speex_bits;
+ void *speex_enc_state;
+ t_mode _mode;
+
+public:
+ t_speex_audio_encoder(uint16 payload_id, uint16 ptime, t_mode mode,
+ t_user *user_config);
+ virtual ~t_speex_audio_encoder();
+
+ virtual uint16 encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence);
+};
+#endif
+
+#ifdef HAVE_ILBC
+class t_ilbc_audio_encoder : public t_audio_encoder {
+private:
+ iLBC_Enc_Inst_t _ilbc_encoder;
+ uint8 _mode; // 20, 30 ms (frame size)
+
+public:
+ t_ilbc_audio_encoder(uint16 payload_id, uint16 ptime,
+ t_user *user_config);
+
+ virtual uint16 encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence);
+};
+#endif
+
+class t_g726_audio_encoder : public t_audio_encoder {
+public:
+ enum t_bit_rate {
+ BIT_RATE_16,
+ BIT_RATE_24,
+ BIT_RATE_32,
+ BIT_RATE_40
+ };
+
+private:
+ uint16 encode_16(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size);
+ uint16 encode_24(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size);
+ uint16 encode_32(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size);
+ uint16 encode_40(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size);
+
+ g72x_state _state;
+ t_bit_rate _bit_rate;
+ t_g726_packing _packing;
+
+public:
+ t_g726_audio_encoder(uint16 payload_id, uint16 ptime, t_bit_rate bit_rate,
+ t_user *user_config);
+
+ virtual uint16 encode(int16 *sample_buf, uint16 nsamples,
+ uint8 *payload, uint16 payload_size, bool &silence);
+};
+
+#endif
diff --git a/src/audio/audio_rx.cpp b/src/audio/audio_rx.cpp
new file mode 100644
index 0000000..bb6db2b
--- /dev/null
+++ b/src/audio/audio_rx.cpp
@@ -0,0 +1,915 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <iostream>
+#include <cstdio>
+#include <ctime>
+#include <cstdlib>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <cc++/config.h>
+
+#include "audio_rx.h"
+#include "log.h"
+#include "phone.h"
+#include "rtp_telephone_event.h"
+#include "userintf.h"
+#include "line.h"
+#include "sys_settings.h"
+#include "sequence_number.h"
+#include "audits/memman.h"
+
+extern t_phone *phone;
+
+#define SAMPLE_BUF_SIZE (audio_encoder->get_ptime() * audio_encoder->get_sample_rate()/1000 *\
+ AUDIO_SAMPLE_SIZE/8)
+
+// Debug macro to print timestamp
+#define DEBUG_TS(s) { gettimeofday(&debug_timer, NULL);\
+ cout << "DEBUG: ";\
+ cout << debug_timer.tv_sec * 1000 +\
+ debug_timer.tv_usec / 1000;\
+ cout << " " << (s) << endl;\
+ }
+
+//////////
+// PRIVATE
+//////////
+
+bool t_audio_rx::get_sound_samples(unsigned short &sound_payload_size, bool &silence) {
+ int status;
+ struct timespec sleeptimer;
+ //struct timeval debug_timer;
+
+ silence = false;
+
+ mtx_3way.lock();
+
+ if (is_3way && !is_main_rx_3way) {
+ // We are not the main receiver in a 3-way call, so
+ // get the sound samples from the local media buffer.
+ // This buffer will be filled by the main receiver.
+ if (!media_3way_peer_rx->get(input_sample_buf, SAMPLE_BUF_SIZE)) {
+ // The mutex is unlocked before going to sleep.
+ // First I had the mutex unlock after the sleep.
+ // That worked fine with LinuxThreading, but it does
+ // not work with NPTL. It causes a deadlock when
+ // the main receiver calls post_media_peer_rx_3way
+ // as NPTL does not fair scheduling. This thread
+ // simly gets the lock again and the main receiver
+ // dies from starvation.
+ mtx_3way.unlock();
+
+ // There is not enough data yet. Sleep for 1 ms.
+ sleeptimer.tv_sec = 0;
+ sleeptimer.tv_nsec = 1000000;
+ nanosleep(&sleeptimer, NULL);
+ return false;
+ }
+
+ mtx_3way.unlock();
+ } else {
+ // Don't keep the 3way mutex locked while waiting for the DSP.
+ mtx_3way.unlock();
+
+ // Get the sound samples from the DSP
+ status = input_device->read(input_sample_buf, SAMPLE_BUF_SIZE);
+
+ if (status != SAMPLE_BUF_SIZE) {
+ if (!logged_capture_failure) {
+ // Log this failure only once
+ log_file->write_header("t_audio_rx::get_sound_samples",
+ LOG_NORMAL, LOG_WARNING);
+ log_file->write_raw("Audio rx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": sound capture failed.\n");
+ log_file->write_raw("Status: ");
+ log_file->write_raw(status);
+ log_file->write_endl();
+ log_file->write_footer();
+ logged_capture_failure = true;
+ }
+
+ stop_running = true;
+ return false;
+ }
+
+ // If line is muted, then fill sample buffer with silence.
+ // Note that we keep reading the dsp, to prevent the DSP buffers
+ // from filling up.
+ if (get_line()->get_is_muted()) {
+ memset(input_sample_buf, 0, SAMPLE_BUF_SIZE);
+ }
+ }
+
+ // Convert buffer to a buffer of shorts as the samples are 16 bits
+ short *sb = (short *)input_sample_buf;
+
+ mtx_3way.lock();
+ if (is_3way) {
+ // Send the sound samples to the other receiver if we
+ // are the main receiver.
+ // There may be no other receiver when one of the far-ends
+ // has put the call on-hold.
+ if (is_main_rx_3way && peer_rx_3way) {
+ peer_rx_3way->post_media_peer_rx_3way(input_sample_buf, SAMPLE_BUF_SIZE,
+ audio_encoder->get_sample_rate());
+ }
+
+ // Mix the sound samples with the 3rd party
+ if (media_3way_peer_tx->get(mix_buf_3way, SAMPLE_BUF_SIZE)) {
+ short *mix_sb = (short *)mix_buf_3way;
+ for (int i = 0; i < SAMPLE_BUF_SIZE / 2; i++) {
+ sb[i] = mix_linear_pcm(sb[i], mix_sb[i]);
+ }
+ }
+ }
+
+ mtx_3way.unlock();
+
+ /*** PREPROCESSING & ENCODING ***/
+
+ bool preprocessing_silence = false;
+
+#ifdef HAVE_SPEEX
+ // speex acoustic echo cancellation
+ if (audio_session->get_do_echo_cancellation() && !audio_session->get_echo_captured_last()) {
+
+ spx_int16_t *input_buf = new spx_int16_t[SAMPLE_BUF_SIZE/2];
+ MEMMAN_NEW_ARRAY(input_buf);
+
+ for (int i = 0; i < SAMPLE_BUF_SIZE / 2; i++) {
+ input_buf[i] = sb[i];
+ }
+
+ speex_echo_capture(audio_session->get_speex_echo_state(), input_buf, sb);
+ audio_session->set_echo_captured_last(true);
+
+ MEMMAN_DELETE_ARRAY(input_buf);
+ delete [] input_buf;
+ }
+
+ // preprocessing
+ preprocessing_silence = !speex_preprocess_run(speex_preprocess_state, sb);
+
+ // According to the speex API documentation the return value
+ // from speex_preprocess_run() is only defined when VAD is
+ // enabled. So to be safe, reset the return value, if VAD is
+ // disabled.
+ if (!speex_dsp_vad) preprocessing_silence = false;
+#endif
+
+ // encoding
+ sound_payload_size = audio_encoder->encode(sb, nsamples, payload, payload_size, silence);
+
+ // recognizing silence (both from preprocessing and encoding)
+ silence = silence || preprocessing_silence;
+
+ return true;
+}
+
+bool t_audio_rx::get_dtmf_event(void) {
+ // DTMF events are not supported in a 3-way conference
+ if (is_3way) return false;
+
+ if (!sema_dtmf_q.try_down()) {
+ // No DTMF event available
+ return false;
+ }
+
+ // Get next DTMF event
+ mtx_dtmf_q.lock();
+ t_dtmf_event dtmf_event = dtmf_queue.front();
+ dtmf_queue.pop();
+ mtx_dtmf_q.unlock();
+
+ ui->cb_async_send_dtmf(get_line()->get_line_number(), dtmf_event.dtmf_tone);
+
+ // Create DTMF player
+ if (dtmf_event.inband) {
+ dtmf_player = new t_inband_dtmf_player(this, audio_encoder, user_config,
+ dtmf_event.dtmf_tone, timestamp, nsamples);
+ MEMMAN_NEW(dtmf_player);
+
+ // Log DTMF event
+ log_file->write_header("t_audio_rx::get_dtmf_event", LOG_NORMAL);
+ log_file->write_raw("Audio rx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": start inband DTMF tone - ");
+ log_file->write_raw(dtmf_event.dtmf_tone);
+ log_file->write_endl();
+ log_file->write_footer();
+ } else {
+ // The telephone events may have a different sampling rate than
+ // the audio codec. Change nsamples accordingly.
+ nsamples = audio_sample_rate(CODEC_TELEPHONE_EVENT)/1000 *
+ audio_encoder->get_ptime();
+
+ dtmf_player = new t_rtp_event_dtmf_player(this, audio_encoder, user_config,
+ dtmf_event.dtmf_tone, timestamp, nsamples);
+ MEMMAN_NEW(dtmf_player);
+
+ // Log DTMF event
+ log_file->write_header("t_audio_rx::get_dtmf_event", LOG_NORMAL);
+ log_file->write_raw("Audio rx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": start DTMF event - ");
+ log_file->write_raw(dtmf_event.dtmf_tone);
+ log_file->write_endl();
+ log_file->write_raw("Payload type: ");
+ log_file->write_raw(pt_telephone_event);
+ log_file->write_endl();
+ log_file->write_footer();
+
+ // Set RTP payload format
+ // HACK: the sample rate for telephone events is 8000, but the
+ // ccRTP stack does not handle it well when the sample rate
+ // changes. When the sample rate of the audio codec is kept
+ // on the ccRTP session settings, then all works fine.
+ rtp_session->setPayloadFormat(DynamicPayloadFormat(pt_telephone_event,
+ audio_encoder->get_sample_rate()));
+ // should be this: audio_sample_rate(CODEC_TELEPHONE_EVENT)
+
+ // As all RTP event contain the same timestamp, the ccRTP stack will
+ // discard packets when the timestamp gets to old.
+ // Increase the expire timeout value to prevent this.
+ rtp_session->setExpireTimeout((JITTER_BUF_MS +
+ user_config->get_dtmf_duration() + user_config->get_dtmf_pause()) * 1000);
+ }
+
+ return true;
+}
+
+void t_audio_rx::set_sound_payload_format(void) {
+ nsamples = audio_encoder->get_sample_rate()/1000 * audio_encoder->get_ptime();
+ rtp_session->setPayloadFormat(DynamicPayloadFormat(audio_encoder->get_payload_id(),
+ audio_encoder->get_sample_rate()));
+}
+
+//////////
+// PUBLIC
+//////////
+
+t_audio_rx::t_audio_rx(t_audio_session *_audio_session,
+ t_audio_io *_input_device, t_twinkle_rtp_session *_rtp_session,
+ t_audio_codec _codec, unsigned short _payload_id,
+ unsigned short _ptime) : sema_dtmf_q(0)
+{
+ audio_session = _audio_session;
+
+ user_config = audio_session->get_line()->get_user();
+ assert(user_config);
+
+ input_device = _input_device;
+ rtp_session = _rtp_session;
+ dtmf_player = NULL;
+ is_running = false;
+ stop_running = false;
+ logged_capture_failure = false;
+ use_nat_keepalive = phone->use_nat_keepalive(user_config);
+
+ pt_telephone_event = -1;
+
+ // Create audio encoder
+ switch (_codec) {
+ case CODEC_G711_ALAW:
+ audio_encoder = new t_g711a_audio_encoder(_payload_id, _ptime, user_config);
+ MEMMAN_NEW(audio_encoder);
+ break;
+ case CODEC_G711_ULAW:
+ audio_encoder = new t_g711u_audio_encoder(_payload_id, _ptime, user_config);
+ MEMMAN_NEW(audio_encoder);
+ break;
+ case CODEC_GSM:
+ audio_encoder = new t_gsm_audio_encoder(_payload_id, _ptime, user_config);
+ MEMMAN_NEW(audio_encoder);
+ break;
+#ifdef HAVE_SPEEX
+ case CODEC_SPEEX_NB:
+ audio_encoder = new t_speex_audio_encoder(_payload_id, _ptime,
+ t_speex_audio_encoder::MODE_NB, user_config);
+ MEMMAN_NEW(audio_encoder);
+ break;
+ case CODEC_SPEEX_WB:
+ audio_encoder = new t_speex_audio_encoder(_payload_id, _ptime,
+ t_speex_audio_encoder::MODE_WB, user_config);
+ MEMMAN_NEW(audio_encoder);
+ break;
+ case CODEC_SPEEX_UWB:
+ audio_encoder = new t_speex_audio_encoder(_payload_id, _ptime,
+ t_speex_audio_encoder::MODE_UWB, user_config);
+ MEMMAN_NEW(audio_encoder);
+ break;
+#endif
+#ifdef HAVE_ILBC
+ case CODEC_ILBC:
+ audio_encoder = new t_ilbc_audio_encoder(_payload_id, _ptime, user_config);
+ MEMMAN_NEW(audio_encoder);
+ break;
+#endif
+ case CODEC_G726_16:
+ audio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,
+ t_g726_audio_encoder::BIT_RATE_16, user_config);
+ MEMMAN_NEW(audio_encoder);
+ break;
+ case CODEC_G726_24:
+ audio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,
+ t_g726_audio_encoder::BIT_RATE_24, user_config);
+ MEMMAN_NEW(audio_encoder);
+ break;
+ case CODEC_G726_32:
+ audio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,
+ t_g726_audio_encoder::BIT_RATE_32, user_config);
+ MEMMAN_NEW(audio_encoder);
+ break;
+ case CODEC_G726_40:
+ audio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,
+ t_g726_audio_encoder::BIT_RATE_40, user_config);
+ MEMMAN_NEW(audio_encoder);
+ break;
+ default:
+ assert(false);
+ }
+
+ payload_size = audio_encoder->get_max_payload_size();
+
+ input_sample_buf = new unsigned char[SAMPLE_BUF_SIZE];
+ MEMMAN_NEW_ARRAY(input_sample_buf);
+
+ payload = new unsigned char[payload_size];
+ MEMMAN_NEW_ARRAY(payload);
+ nsamples = audio_encoder->get_sample_rate()/1000 * audio_encoder->get_ptime();
+
+ // Initialize 3-way settings to 'null'
+ media_3way_peer_tx = NULL;
+ media_3way_peer_rx = NULL;
+ peer_rx_3way = NULL;
+ mix_buf_3way = NULL;
+ is_3way = false;
+ is_main_rx_3way = false;
+
+#ifdef HAVE_SPEEX
+ // initializing speex preprocessing state
+ speex_preprocess_state = speex_preprocess_state_init(nsamples, audio_encoder->get_sample_rate());
+
+ int arg;
+ float farg;
+
+ // Noise reduction
+ arg = (user_config->get_speex_dsp_nrd() ? 1 : 0);
+ speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_DENOISE, &arg);
+ arg = -30;
+ speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &arg);
+
+ // Automatic gain control
+ arg = (user_config->get_speex_dsp_agc() ? 1 : 0);
+ speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_AGC, &arg);
+ farg = (float) (user_config->get_speex_dsp_agc_level()) * 327.68f;
+ speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &farg);
+ arg = 30;
+ speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &arg);
+
+ // Voice activity detection
+ arg = (user_config->get_speex_dsp_vad() ? 1 : 0);
+ speex_dsp_vad = (bool)arg;
+ speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_VAD, &arg);
+
+ // Acoustic echo cancellation
+ if (audio_session->get_do_echo_cancellation()) {
+ speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_ECHO_STATE,
+ audio_session->get_speex_echo_state());
+ }
+#endif
+}
+
+t_audio_rx::~t_audio_rx() {
+ struct timespec sleeptimer;
+
+ if (is_running) {
+ stop_running = true;
+ do {
+ sleeptimer.tv_sec = 0;
+ sleeptimer.tv_nsec = 10000000;
+ nanosleep(&sleeptimer, NULL);
+ } while (is_running);
+ }
+
+#ifdef HAVE_SPEEX
+ // cleaning speex preprocessing
+ if (audio_session->get_do_echo_cancellation()) {
+ speex_echo_state_reset(audio_session->get_speex_echo_state());
+ }
+ speex_preprocess_state_destroy(speex_preprocess_state);
+#endif
+
+ MEMMAN_DELETE_ARRAY(input_sample_buf);
+ delete [] input_sample_buf;
+
+ MEMMAN_DELETE_ARRAY(payload);
+ delete [] payload;
+
+ MEMMAN_DELETE(audio_encoder);
+ delete audio_encoder;
+
+ // Clean up resources for 3-way conference calls
+ if (media_3way_peer_tx) {
+ MEMMAN_DELETE(media_3way_peer_tx);
+ delete media_3way_peer_tx;
+ }
+ if (media_3way_peer_rx) {
+ MEMMAN_DELETE(media_3way_peer_rx);
+ delete media_3way_peer_rx;
+ }
+ if (mix_buf_3way) {
+ MEMMAN_DELETE_ARRAY(mix_buf_3way);
+ delete [] mix_buf_3way;
+ }
+
+ if (dtmf_player) {
+ MEMMAN_DELETE(dtmf_player);
+ delete dtmf_player;
+ }
+}
+
+void t_audio_rx::set_running(bool running) {
+ is_running = running;
+}
+
+// NOTE: no operations on the phone object are allowed inside the run() method.
+// Such an operation needs a lock on the transaction layer. The destructor
+// on audio_rx is called while this lock is locked. The destructor waits
+// in a busy loop for the run() method to finish. If the run() method would
+// need the phone lock, this would lead to a dead lock (and a long trip
+// in debug hell!)
+void t_audio_rx::run(void) {
+ //struct timeval debug_timer;
+ unsigned short sound_payload_size;
+ uint32 dtmf_rtp_timestamp;
+
+ phone->add_prohibited_thread();
+ ui->add_prohibited_thread();
+
+ // This flag indicates if we are currently in a silence period.
+ // The start of a new stream is assumed to start in silence, such
+ // that the very first RTP packet will be marked.
+ bool silence_period = true;
+ uint64 silence_nsamples = 0; // duration in samples
+
+ // This flag indicates if a sound frame can be suppressed
+ bool suppress_samples = false;
+
+ // The running flag is set already in t_audio_session::run to prevent
+ // a crash when the thread gets destroyed before it starts running.
+ // is_running = true;
+
+ // For a 3-way conference only the main receiver has access
+ // to the dsp.
+ if (!is_3way || is_main_rx_3way) {
+ // Enable recording
+ if (sys_config->equal_audio_dev(sys_config->get_dev_speaker(),
+ sys_config->get_dev_mic()))
+ {
+ input_device->enable(true, true);
+ } else {
+ input_device->enable(false, true);
+ }
+
+ // If the stream is stopped for call-hold, then the buffer might
+ // be filled with old sound samples.
+ input_device->flush(false, true);
+ }
+
+ // Synchronize the timestamp driven by the sampling rate
+ // of the recording with the timestamp of the RTP session.
+ // As the RTP session is already created in advance, the
+ // RTP clock is a bit ahead already.
+ timestamp = rtp_session->getCurrentTimestamp() + nsamples;
+
+ // This loop keeps running until the stop_running flag is set to true.
+ // When a call is being released the stop_running flag is set to true.
+ // At that moment the lock on the transaction layer (phone) is taken.
+ // So do not use operations that take the phone lock, otherwise a
+ // dead lock may occur during call release.
+ while (true) {
+ if (stop_running) break;
+
+ if (dtmf_player) {
+ rtp_session->setMark(false);
+ // Skip samples from sound card
+ input_device->read(input_sample_buf, SAMPLE_BUF_SIZE);
+ sound_payload_size = dtmf_player->get_payload(
+ payload, payload_size, timestamp, dtmf_rtp_timestamp);
+ silence_period = false;
+ } else if (get_dtmf_event()) {
+ // RFC 2833
+ // Set marker in first RTP packet of a DTMF event
+ rtp_session->setMark(true);
+ // Skip samples from sound card
+ input_device->read(input_sample_buf, SAMPLE_BUF_SIZE);
+ assert(dtmf_player);
+ sound_payload_size = dtmf_player->get_payload(
+ payload, payload_size, timestamp, dtmf_rtp_timestamp);
+ silence_period = false;
+ } else if (get_sound_samples(sound_payload_size, suppress_samples)) {
+ if (suppress_samples && use_nat_keepalive) {
+ if (!silence_period) silence_nsamples = 0;
+
+ // Send a silence packet at the NAT keep alive interval
+ // to keep the NAT bindings for RTP fresh.
+ silence_nsamples += SAMPLE_BUF_SIZE / 2;
+ if (silence_nsamples >
+ (uint64_t)user_config->get_timer_nat_keepalive() * 1000 *
+ audio_encoder->get_sample_rate())
+ {
+ suppress_samples = false;
+ }
+ }
+
+ if (silence_period && !suppress_samples) {
+ // RFC 3551 4.1
+ // Set marker bit in first RTP packet after silence
+ rtp_session->setMark(true);
+ } else {
+ rtp_session->setMark(false);
+ }
+ silence_period = suppress_samples;
+ } else {
+ continue;
+ }
+
+ // If timestamp is more than 1 payload size ahead of the clock of
+ // the ccRTP stack, then drop the current payload and do not advance
+ // the timestamp. This will happen if the DSP delivers more
+ // sound samples than the set sample rate. To compensate for this
+ // samples must be dropped.
+ uint32 current_timestamp = rtp_session->getCurrentTimestamp();
+ if (seq32_t(timestamp) <= seq32_t(current_timestamp + nsamples)) {
+ if (dtmf_player) {
+ // Send DTMF payload
+ rtp_session->putData(dtmf_rtp_timestamp, payload,
+ sound_payload_size);
+
+ // If DTMF has ended then set payload back to sound
+ if (dtmf_player->finished()) {
+ set_sound_payload_format();
+ MEMMAN_DELETE(dtmf_player);
+ delete dtmf_player;
+ dtmf_player = NULL;
+ }
+ } else if (!suppress_samples) {
+ // Send sound samples
+ // Set the expire timeout to the jitter buffer size.
+ // This allows for old packets still to be sent out.
+ rtp_session->setExpireTimeout(MAX_OUT_AUDIO_DELAY_MS * 1000);
+ rtp_session->putData(timestamp, payload, sound_payload_size);
+ }
+
+ timestamp += nsamples;
+ } else {
+ log_file->write_header("t_audio_rx::run", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("Audio rx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": discarded surplus of sound samples.\n");
+ log_file->write_raw("Timestamp: ");
+ log_file->write_raw(timestamp);
+ log_file->write_endl();
+ log_file->write_raw("Current timestamp: ");
+ log_file->write_raw(current_timestamp);
+ log_file->write_endl();
+ log_file->write_raw("nsamples: ");
+ log_file->write_raw(nsamples);
+ log_file->write_endl();
+ log_file->write_footer();
+ }
+
+ // If there is enough data in the DSP buffers to fill another
+ // RTP packet then do not sleep, but immediately go to the
+ // next cycle to play out the data. Probably this thread did
+ // not get enough time, so the buffer filled up. The far end
+ // jitter buffer has to cope with the jitter caused by this.
+ if (is_3way && !is_main_rx_3way) {
+ if (media_3way_peer_rx->size_content() >= SAMPLE_BUF_SIZE) {
+ continue;
+ }
+ } else {
+ if (input_device->get_buffer_space(true) >= SAMPLE_BUF_SIZE) continue;
+ }
+
+ // There is no data left in the DSP buffers to play out anymore.
+ // So the timestamp must be in sync with the clock of the ccRTP
+ // stack. It might get behind if the sound cards samples a bit
+ // slower than the set sample rate. Advance the timestamp to get
+ // in sync again.
+ current_timestamp = rtp_session->getCurrentTimestamp();
+ if (seq32_t(timestamp) <= seq32_t(current_timestamp -
+ (JITTER_BUF_MS / audio_encoder->get_ptime()) * nsamples))
+ {
+ timestamp += nsamples * (JITTER_BUF_MS / audio_encoder->get_ptime());
+ log_file->write_header("t_audio_rx::run", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("Audio rx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": timestamp forwarded by ");
+ log_file->write_raw(nsamples * (JITTER_BUF_MS /
+ audio_encoder->get_ptime()));
+ log_file->write_endl();
+ log_file->write_raw("Timestamp: ");
+ log_file->write_raw(timestamp);
+ log_file->write_endl();
+ log_file->write_raw("Current timestamp: ");
+ log_file->write_raw(current_timestamp);
+ log_file->write_endl();
+ log_file->write_raw("nsamples: ");
+ log_file->write_raw(nsamples);
+ log_file->write_endl();
+ log_file->write_footer();
+ }
+ }
+
+ phone->remove_prohibited_thread();
+ ui->remove_prohibited_thread();
+ is_running = false;
+}
+
+void t_audio_rx::set_pt_telephone_event(int pt) {
+ pt_telephone_event = pt;
+}
+
+void t_audio_rx::push_dtmf(char digit, bool inband) {
+ // Ignore invalid DTMF digits
+ if (!VALID_DTMF_SYM(digit)) return;
+
+ // Ignore DTMF tones in a 3-way conference
+ if (is_3way) return;
+
+ t_dtmf_event dtmf_event;
+ dtmf_event.dtmf_tone = char2dtmf_ev(digit);
+ dtmf_event.inband = inband;
+
+ mtx_dtmf_q.lock();
+ dtmf_queue.push(dtmf_event);
+ mtx_dtmf_q.unlock();
+ sema_dtmf_q.up();
+}
+
+t_line *t_audio_rx::get_line(void) const {
+ return audio_session->get_line();
+}
+
+void t_audio_rx::join_3way(bool main_rx, t_audio_rx *peer_rx) {
+ mtx_3way.lock();
+
+ if (is_3way) {
+ log_file->write_header("t_audio_rx::join_3way", LOG_NORMAL);
+ log_file->write_raw("ERROR: audio rx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(" - 3way is already active.\n");
+ log_file->write_footer();
+ mtx_3way.unlock();
+ return;
+ }
+
+ // Logging
+ log_file->write_header("t_audio_rx::join_3way");
+ log_file->write_raw("Audio rx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": join 3-way.\n");
+ if (main_rx) {
+ log_file->write_raw("Role is: mixer.\n");
+ } else {
+ log_file->write_raw("Role is: non-mixing.\n");
+ }
+ if (peer_rx) {
+ log_file->write_raw("A peer receiver already exists.\n");
+ } else {
+ log_file->write_raw("A peer receiver does not exist.\n");
+ }
+ log_file->write_footer();
+
+ // Create media buffers for the 2 far-ends of a 3-way call.
+ // The size of the media buffer is the size of the jitter buffer.
+ // This allows for jitter in the RTP streams and also for
+ // incompatible payload sizes. Eg. 1 far-end may send 20ms paylaods,
+ // while the other sends 30ms payloads. The outgoing RTP stream might
+ // even have another payload size.
+ // When the data has been captured from the soundcard, it will be
+ // checked if there is enough data available in the media buffers, i.e.
+ // the same amount of data as captured from the soundcard for mixing.
+ // If there is it will be retrieved and mixed.
+ // If there isn't the captured sound will simply be sent on its own
+ // to the far-end. Meanwhile the buffer will fill up with data such
+ // that from the next captured sample there will be sufficient data
+ // for mixing.
+ media_3way_peer_tx = new t_media_buffer(
+ JITTER_BUF_SIZE(audio_encoder->get_sample_rate()));
+ MEMMAN_NEW(media_3way_peer_tx);
+ media_3way_peer_rx = new t_media_buffer(
+ JITTER_BUF_SIZE(audio_encoder->get_sample_rate()));
+ MEMMAN_NEW(media_3way_peer_rx);
+
+ // Create a mix buffer for one sample frame.
+ mix_buf_3way = new unsigned char[SAMPLE_BUF_SIZE];
+ MEMMAN_NEW_ARRAY(mix_buf_3way);
+
+ peer_rx_3way = peer_rx;
+
+ is_3way = true;
+ is_main_rx_3way = main_rx;
+
+ // Stop DTMF tones as these are not supported in a 3way
+ if (dtmf_player) {
+ MEMMAN_DELETE(dtmf_player);
+ delete dtmf_player;
+ dtmf_player = NULL;
+ }
+
+ mtx_3way.unlock();
+}
+
+void t_audio_rx::set_peer_rx_3way(t_audio_rx *peer_rx) {
+ mtx_3way.lock();
+
+ if (!is_3way) {
+ mtx_3way.unlock();
+ return;
+ }
+
+ // Logging
+ log_file->write_header("t_audio_rx::set_peer_rx_3way");
+ log_file->write_raw("Audio rx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ if (peer_rx) {
+ log_file->write_raw(": set peer receiver.\n");
+ } else {
+ log_file->write_raw(": erase peer receiver.\n");
+ }
+ if (is_main_rx_3way) {
+ log_file->write_raw("Role is: mixer.\n");
+ } else {
+ log_file->write_raw("Role is: non-mixing.\n");
+ }
+ log_file->write_footer();
+
+ peer_rx_3way = peer_rx;
+
+ mtx_3way.unlock();
+}
+
+void t_audio_rx::set_main_rx_3way(bool main_rx) {
+ mtx_3way.lock();
+
+ if (!is_3way) {
+ mtx_3way.unlock();
+ return;
+ }
+
+ // Logging
+ log_file->write_header("t_audio_rx::set_main_rx_3way");
+ log_file->write_raw("Audio rx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ if (main_rx) {
+ log_file->write_raw(": change role to: mixer.\n");
+ } else {
+ log_file->write_raw(": change role to: non-mixing.\n");
+ }
+ log_file->write_footer();
+
+
+ // Initialize the DSP if we become the mixer and we were not before
+ if (main_rx && !is_main_rx_3way) {
+ // Enable recording
+ if (sys_config->equal_audio_dev(sys_config->get_dev_speaker(),
+ sys_config->get_dev_mic()))
+ {
+ input_device->enable(true, true);
+ } else {
+ input_device->enable(false, true);
+ }
+
+ // If the stream is stopped for call-hold, then the buffer might
+ // be filled with old sound samples.
+ input_device->flush(false, true);
+ }
+
+ is_main_rx_3way = main_rx;
+
+ mtx_3way.unlock();
+}
+
+void t_audio_rx::stop_3way(void) {
+ mtx_3way.lock();
+
+ if (!is_3way) {
+ log_file->write_header("t_audio_rx::stop_3way");
+ log_file->write_raw("ERROR: audio rx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(" - 3way is not active.\n");
+ log_file->write_footer();
+ mtx_3way.unlock();
+ return;
+ }
+
+ // Logging
+ log_file->write_header("t_audio_rx::stop_3way");
+ log_file->write_raw("Audio rx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": stop 3-way.\n");
+ log_file->write_footer();
+
+ is_3way = false;
+ is_main_rx_3way = false;
+
+ peer_rx_3way = NULL;
+
+ MEMMAN_DELETE(media_3way_peer_tx);
+ delete media_3way_peer_tx;
+ media_3way_peer_tx = NULL;
+ MEMMAN_DELETE(media_3way_peer_rx);
+ delete media_3way_peer_rx;
+ media_3way_peer_rx = NULL;
+ MEMMAN_DELETE_ARRAY(mix_buf_3way);
+ delete [] mix_buf_3way;
+ mix_buf_3way = NULL;
+
+ mtx_3way.unlock();
+}
+
+void t_audio_rx::post_media_peer_tx_3way(unsigned char *media, int len,
+ unsigned short peer_sample_rate)
+{
+ mtx_3way.lock();
+
+ if (!is_3way) {
+ // This is not a 3-way call. This is not necessarily an
+ // error condition. The 3rd party may be in the process of
+ // leaving the conference.
+ // Simply discard the posted media
+ mtx_3way.unlock();
+ return;
+ }
+
+ if (peer_sample_rate != audio_encoder->get_sample_rate()) {
+ // Resample media from peer to sample rate of this receiver
+ int output_len = (len / 2) * audio_encoder->get_sample_rate() / peer_sample_rate;
+ short *output_buf = new short[output_len];
+ MEMMAN_NEW_ARRAY(output_buf);
+ int resample_len = resample((short *)media, len / 2, peer_sample_rate,
+ output_buf, output_len, audio_encoder->get_sample_rate());
+ media_3way_peer_tx->add((unsigned char *)output_buf, resample_len * 2);
+ MEMMAN_DELETE_ARRAY(output_buf);
+ delete [] output_buf;
+ } else {
+ media_3way_peer_tx->add(media, len);
+ }
+
+ mtx_3way.unlock();
+}
+
+void t_audio_rx::post_media_peer_rx_3way(unsigned char *media, int len,
+ unsigned short peer_sample_rate)
+{
+ mtx_3way.lock();
+
+ if (!is_3way) {
+ // This is not a 3-way call. This is not necessarily an
+ // error condition. The 3rd party may be in the process of
+ // leaving the conference.
+ // Simply discard the posted media
+ mtx_3way.unlock();
+ return;
+ }
+
+ if (peer_sample_rate != audio_encoder->get_sample_rate()) {
+ // Resample media from peer to sample rate of this receiver
+ int output_len = (len / 2) * audio_encoder->get_sample_rate() / peer_sample_rate;
+ short *output_buf = new short[output_len];
+ MEMMAN_NEW_ARRAY(output_buf);
+ int resample_len = resample((short *)media, len / 2, peer_sample_rate,
+ output_buf, output_len, audio_encoder->get_sample_rate());
+ media_3way_peer_rx->add((unsigned char *)output_buf, resample_len * 2);
+ MEMMAN_DELETE_ARRAY(output_buf);
+ delete [] output_buf;
+ } else {
+ media_3way_peer_rx->add(media, len);
+ }
+
+ mtx_3way.unlock();
+}
+
+bool t_audio_rx::get_is_main_rx_3way(void) const {
+ return is_main_rx_3way;
+}
diff --git a/src/audio/audio_rx.h b/src/audio/audio_rx.h
new file mode 100644
index 0000000..5db8762
--- /dev/null
+++ b/src/audio/audio_rx.h
@@ -0,0 +1,217 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _AUDIO_RX_H
+#define _AUDIO_RX_H
+
+// Receive audio from the soundcard and send it to the RTP thread.
+
+#include <assert.h>
+#include <queue>
+#include <string>
+
+#include "audio_codecs.h"
+#include "audio_device.h"
+#include "audio_encoder.h"
+#include "dtmf_player.h"
+#include "media_buffer.h"
+#include "user.h"
+#include "threads/mutex.h"
+#include "threads/sema.h"
+#include "twinkle_rtp_session.h"
+#include "twinkle_config.h"
+
+#ifdef HAVE_SPEEX
+#include <speex/speex.h>
+#include <speex/speex_preprocess.h>
+#endif
+
+using namespace std;
+using namespace ost;
+
+// Forward declarations
+class t_audio_session;
+class t_line;
+
+class t_audio_rx {
+private:
+ // audio_session owning this audio receiver
+ t_audio_session *audio_session;
+
+ // User profile of user using the line
+ // This is a pointer to the user_config owned by a phone user.
+ // So this pointer should never be deleted.
+ t_user *user_config;
+
+ // file descriptor audio capture device
+ t_audio_io* input_device;
+
+ // RTP session
+ t_twinkle_rtp_session *rtp_session;
+
+ // Media buffer to buffer media from the peer audio trasmitter in a
+ // 3-way call. This media stream will be mixed with the
+ // audio captured from the soundcard.
+ t_media_buffer *media_3way_peer_tx;
+
+ // Media captured by the peer audio receiver in a 3-way conference
+ t_media_buffer *media_3way_peer_rx;
+
+ // The peer audio receiver in a 3-way conference.
+ t_audio_rx *peer_rx_3way;
+
+ // Buffer for mixing purposes in 3-way conference.
+ unsigned char *mix_buf_3way;
+
+ // Indicates if this receiver is part of a 3-way conference call
+ bool is_3way;
+
+ // Indicates if this is this receiver has to capture sound from the
+ // soundcard. In a 3-way call, one receiver captures sound, while the
+ // other receiver simply takes the sound from the main receiver.
+ bool is_main_rx_3way;
+
+ // Mutex to protect actions on 3-way conference data
+ t_mutex mtx_3way;
+
+ // Audio encoder
+ t_audio_encoder *audio_encoder;
+
+ // Buffer to store PCM samples for ptime ms
+ unsigned char *input_sample_buf;
+
+ // Indicates if NAT keep alive packets must be sent during silence
+ // suppression.
+ bool use_nat_keepalive;
+
+ // RTP payload
+ unsigned short payload_size;
+ unsigned char *payload;
+ unsigned short nsamples; // number of samples taken per packet
+
+ // Payload type for telephone-event payload.
+ int pt_telephone_event;
+
+ // Queue of DTMF tones to be sent
+ struct t_dtmf_event {
+ uint8 dtmf_tone;
+ bool inband;
+ };
+
+ queue<t_dtmf_event> dtmf_queue;
+ t_mutex mtx_dtmf_q;
+ t_semaphore sema_dtmf_q;
+
+ // DTMF player
+ t_dtmf_player *dtmf_player;
+
+ // Inidicates if the recording thread is running
+ volatile bool is_running;
+
+ // The thread exits when this indicator is set to true
+ volatile bool stop_running;
+
+ // Indicates if a capture failure was already logged (log throttling).
+ bool logged_capture_failure;
+
+ // Timestamp for next RTP packet
+ unsigned long timestamp;
+
+#ifdef HAVE_SPEEX
+ /** Speex preprocessor state */
+ SpeexPreprocessState *speex_preprocess_state;
+
+ /** Speex VAD enabled? */
+ bool speex_dsp_vad;
+#endif
+
+ // Get sound samples for 1 RTP packet from the soundcard.
+ // Returns false if the main loop has to start another cycle to get
+ // samples (eg. no samples available yet).
+ // If not enough samples are available yet, then a 1 ms sleep will be taken.
+ // Also returns false if capturing samples from the soundcard failed.
+ // Returns true if sounds samples are received. The samples are stored
+ // in the payload buffer in the proper encoding.
+ // The number bytes of the sound payload is returned in sound_payload_size
+ // The silence flag indicates if the returned sound samples represent silence
+ // that may be suppressed.
+ bool get_sound_samples(unsigned short &sound_payload_size, bool &silence);
+
+ // Get next DTMF event generated by the user.
+ // Returns false if there is no next DTMF event
+ bool get_dtmf_event(void);
+
+ // Set RTP payload for outgoing sound packets based on the codec.
+ void set_sound_payload_format(void);
+
+public:
+ // Create the audio receiver
+ // _fd file descriptor of capture device
+ // _rtp_session RTP socket tp send the RTP stream
+ // _codec audio codec to use
+ // _ptime length of the audio packets in ms
+ // _ptime = 0 means use default ptime value for the codec
+ t_audio_rx(t_audio_session *_audio_session, t_audio_io *_input_device,
+ t_twinkle_rtp_session *_rtp_session,
+ t_audio_codec _codec, unsigned short _payload_id,
+ unsigned short _ptime = 0);
+
+ ~t_audio_rx();
+
+ // Set the is running flag
+ void set_running(bool running);
+
+ void run(void);
+
+ // Set the dynamic payload type for telephone events
+ void set_pt_telephone_event(int pt);
+
+ // Push a new DTMF tone in the DTMF queue
+ void push_dtmf(char digit, bool inband);
+
+ // Get phone line belonging to this audio transmitter
+ t_line *get_line(void) const;
+
+ // Join a 3-way conference call.
+ // main_rx indicates if this receiver must be the main receiver capturing
+ // the sound from the soundcard.
+ // The peer_rx is the peer receiver (may be NULL(
+ void join_3way(bool main_rx, t_audio_rx *peer_rx);
+
+ // Change the peer receiver in a 3-way (set to NULL to erase).
+ void set_peer_rx_3way(t_audio_rx *peer_rx);
+
+ // Change the main rx role in a 3-way
+ void set_main_rx_3way(bool main_rx);
+
+ // Delete 3rd party from a 3-way conference.
+ void stop_3way(void);
+
+ // Post media from the peer transmitter in a 3-way.
+ void post_media_peer_tx_3way(unsigned char *media, int len,
+ unsigned short peer_sample_rate);
+
+ // Post media from the peer receiver in a 3-way.
+ void post_media_peer_rx_3way(unsigned char *media, int len,
+ unsigned short peer_sample_rate);
+
+ // Returns if this receiver is the main receiver in a 3-way
+ bool get_is_main_rx_3way(void) const;
+};
+
+#endif
diff --git a/src/audio/audio_session.cpp b/src/audio/audio_session.cpp
new file mode 100644
index 0000000..539e2b1
--- /dev/null
+++ b/src/audio/audio_session.cpp
@@ -0,0 +1,677 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+#include "twinkle_config.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <cstdlib>
+#include <cstdio>
+#include "audio_session.h"
+#include "line.h"
+#include "log.h"
+#include "sys_settings.h"
+#include "translator.h"
+#include "user.h"
+#include "userintf.h"
+#include "util.h"
+#include "audits/memman.h"
+
+#ifdef HAVE_ZRTP
+#include "twinkle_zrtp_ui.h"
+#endif
+
+static t_audio_session *_audio_session;
+
+///////////
+// PRIVATE
+///////////
+
+bool t_audio_session::is_3way(void) const {
+ t_line *l = get_line();
+ t_phone *p = l->get_phone();
+
+ return p->part_of_3way(l->get_line_number());
+}
+
+t_audio_session *t_audio_session::get_peer_3way(void) const {
+ t_line *l = get_line();
+ t_phone *p = l->get_phone();
+
+ t_line *peer_line = p->get_3way_peer_line(l->get_line_number());
+
+ return peer_line->get_audio_session();
+}
+
+bool t_audio_session::open_dsp(void) {
+ if (sys_config->equal_audio_dev(sys_config->get_dev_speaker(),
+ sys_config->get_dev_mic()))
+ {
+ return open_dsp_full_duplex();
+ }
+
+ return open_dsp_speaker() && open_dsp_mic();
+}
+
+bool t_audio_session::open_dsp_full_duplex(void) {
+
+ // Open audio device
+ speaker = t_audio_io::open(sys_config->get_dev_speaker(), true, true, true, 1,
+ SAMPLEFORMAT_S16, audio_sample_rate(codec), true);
+ if (!speaker) {
+ string msg(TRANSLATE2("CoreAudio", "Failed to open sound card"));
+ log_file->write_report(msg, "t_audio_session::open_dsp_full_duplex",
+ LOG_NORMAL, LOG_CRITICAL);
+ ui->cb_display_msg(msg, MSG_CRITICAL);
+ return false;
+ }
+
+ // Disable recording
+ // If recording is not disabled, then the capture buffers will
+ // already fill with data. Then when the audio_rx thread starts
+ // to read blocks of 160 samples, it gets all these initial blocks
+ // very quickly 1 per 12 ms I have seen. And hence the timestamps
+ // for these blocks get out of sync with the RTP stack.
+ // Also a large delay is introduced by this. So recording should
+ // be enabled just before the data is read from the device.
+ speaker->enable(true, false);
+
+ mic = speaker;
+ return true;
+}
+
+bool t_audio_session::open_dsp_speaker(void) {
+
+ speaker = t_audio_io::open(sys_config->get_dev_speaker(), true, false, true, 1,
+ SAMPLEFORMAT_S16, audio_sample_rate(codec), true);
+ if (!speaker) {
+ string msg(TRANSLATE2("CoreAudio", "Failed to open sound card"));
+ log_file->write_report(msg, "t_audio_session::open_dsp_speaker",
+ LOG_NORMAL, LOG_CRITICAL);
+ ui->cb_display_msg(msg, MSG_CRITICAL);
+ return false;
+ }
+
+ return true;
+}
+
+bool t_audio_session::open_dsp_mic(void) {
+ mic = t_audio_io::open(sys_config->get_dev_mic(), false, true, true, 1,
+ SAMPLEFORMAT_S16, audio_sample_rate(codec), true);
+ if (!mic) {
+ string msg(TRANSLATE2("CoreAudio", "Failed to open sound card"));
+ log_file->write_report(msg, "t_audio_session::open_dsp_mic",
+ LOG_NORMAL, LOG_CRITICAL);
+ ui->cb_display_msg(msg, MSG_CRITICAL);
+ return false;
+ }
+
+ // Disable recording
+ // If recording is not disabled, then the capture buffers will
+ // already fill with data. Then when the audio_rx thread starts
+ // to read blocks of 160 samples, it gets all these initial blocks
+ // very quickly 1 per 12 ms I have seen. And hence the timestamps
+ // for these blocks get out of sync with the RTP stack.
+ // Also a large delay is introduced by this. So recording should
+ // be enabled just before the data is read from the device.
+ speaker->enable(true, false);
+
+ return true;
+}
+
+///////////
+// PUBLIC
+///////////
+
+t_audio_session::t_audio_session(t_session *_session,
+ const string &_recv_host, unsigned short _recv_port,
+ const string &_dst_host, unsigned short _dst_port,
+ t_audio_codec _codec, unsigned short _ptime,
+ const map<unsigned short, t_audio_codec> &recv_payload2ac,
+ const map<t_audio_codec, unsigned short> &send_ac2payload,
+ bool encrypt)
+{
+ valid = false;
+
+ session = _session;
+ audio_rx = NULL;
+ audio_tx = NULL;
+ thr_audio_rx = NULL;
+ thr_audio_tx = NULL;
+ speaker = NULL;
+ mic = NULL;
+
+ codec = _codec;
+ ptime = _ptime;
+
+ is_encrypted = false;
+ zrtp_sas.clear();
+
+ // Assume the SAS is confirmed. When a SAS is received from the ZRTP
+ // stack, the confirmed flag will be cleared.
+ zrtp_sas_confirmed = true;
+
+ srtp_cipher_mode.clear();
+
+ log_file->write_header("t_audio_session::t_audio_session");
+ log_file->write_raw("Receive RTP from: ");
+ log_file->write_raw(_recv_host);
+ log_file->write_raw(":");
+ log_file->write_raw(_recv_port);
+ log_file->write_endl();
+ log_file->write_raw("Send RTP to: ");
+ log_file->write_raw(_dst_host);
+ log_file->write_raw(":");
+ log_file->write_raw(_dst_port);
+ log_file->write_endl();
+ log_file->write_footer();
+
+ t_user *user_config = get_line()->get_user();
+
+ // Create RTP session
+ try {
+ if (_recv_host.empty() || _recv_port == 0) {
+ rtp_session = new t_twinkle_rtp_session(
+ InetHostAddress("0.0.0.0"));
+ MEMMAN_NEW(rtp_session);
+ } else {
+ rtp_session = new t_twinkle_rtp_session(
+ InetHostAddress(_recv_host.c_str()), _recv_port);
+ MEMMAN_NEW(rtp_session);
+ }
+#ifdef HAVE_ZRTP
+ ZrtpQueue* zque = dynamic_cast<ZrtpQueue*>(rtp_session);
+ if (zque && rtp_session->is_zrtp_initialized()) {
+ zque->setEnableZrtp(encrypt);
+
+ if (user_config->get_zrtp_enabled()) {
+ // Create the ZRTP call back interface
+ TwinkleZrtpUI* twui = new TwinkleZrtpUI(this);
+
+ // The ZrtpQueue keeps track of the twui - the destructor of
+ // ZrtpQueue (aka t_twinkle_rtp_session) deletes this object,
+ // thus no other management is required.
+ zque->setUserCallback(twui);
+ }
+ }
+#endif
+ } catch(...) {
+ // If the RTPSession constructor throws an exception, no
+ // object is created, so clear the pointer.
+ rtp_session = NULL;
+ string msg(TRANSLATE2("CoreAudio", "Failed to create a UDP socket (RTP) on port %1"));
+ msg = replace_first(msg, "%1", int2str(_recv_port));
+ log_file->write_report(msg, "t_audio_session::t_audio_session",
+ LOG_NORMAL, LOG_CRITICAL);
+ ui->cb_show_msg(msg, MSG_CRITICAL);
+ return;
+ }
+
+ if (!_dst_host.empty() && _dst_port != 0) {
+ rtp_session->addDestination(
+ InetHostAddress(_dst_host.c_str()), _dst_port);
+ }
+
+ // Set payload format for outgoing RTP packets
+ map<t_audio_codec, unsigned short>::const_iterator it;
+ it = send_ac2payload.find(codec);
+ assert(it != send_ac2payload.end());
+ unsigned short payload_id = it->second;
+ rtp_session->setPayloadFormat(DynamicPayloadFormat(
+ payload_id, audio_sample_rate(codec)));
+
+ // Open and initialize sound card
+ t_audio_session *as_peer;
+ if (is_3way() && (as_peer = get_peer_3way())) {
+ speaker = as_peer->get_dsp_speaker();
+ mic = as_peer->get_dsp_mic();
+ if (!speaker || !mic) return;
+ } else {
+ if (!open_dsp()) return;
+ }
+
+#ifdef HAVE_SPEEX
+ // Speex AEC auxiliary data initialization
+ do_echo_cancellation = false;
+
+ if (user_config->get_speex_dsp_aec()) {
+ int nsamples = audio_sample_rate(codec) / 1000 * ptime;
+ speex_echo_state = speex_echo_state_init(nsamples, 5*nsamples);
+ do_echo_cancellation = true;
+ echo_captured_last = true;
+ }
+#endif
+
+ // Create recorder
+ if (!_recv_host.empty() && _recv_port != 0) {
+ audio_rx = new t_audio_rx(this, mic, rtp_session, codec,
+ payload_id, ptime);
+ MEMMAN_NEW(audio_rx);
+
+ // Setup 3-way configuration if this audio session is part of
+ // a 3-way.
+ if (is_3way()) {
+ t_audio_session *peer = get_peer_3way();
+ if (!peer || !peer->audio_rx) {
+ // There is no peer rx yet, so become the main rx
+ audio_rx->join_3way(true, NULL);
+
+ if (peer && peer->audio_tx) {
+ peer->audio_tx->set_peer_rx_3way(audio_rx);
+ }
+ } else {
+ // There is a peer rx already so that must be the
+ // main rx.
+ audio_rx->join_3way(false, peer->audio_rx);
+ peer->audio_rx->set_peer_rx_3way(audio_rx);
+
+ if (peer->audio_tx) {
+ peer->audio_tx->set_peer_rx_3way(audio_rx);
+ }
+ }
+ }
+ }
+
+ // Create player
+ if (!_dst_host.empty() && _dst_port != 0) {
+ audio_tx = new t_audio_tx(this, speaker, rtp_session, codec,
+ recv_payload2ac, ptime);
+ MEMMAN_NEW(audio_tx);
+
+ // Setup 3-way configuration if this audio session is part of
+ // a 3-way.
+ if (is_3way()) {
+ t_audio_session *peer = get_peer_3way();
+ if (!peer) {
+ // There is no peer tx yet, so become the mixer tx
+ audio_tx->join_3way(true, NULL, NULL);
+ } else if (!peer->audio_tx) {
+ // There is a peer audio session, but no peer tx,
+ // so become the mixer tx
+ audio_tx->join_3way(true, NULL, peer->audio_rx);
+ } else {
+ // There is a peer tx already. That must be the
+ // mixer.
+ audio_tx->join_3way(
+ false, peer->audio_tx, peer->audio_rx);
+ }
+ }
+ }
+ valid = true;
+}
+
+t_audio_session::~t_audio_session() {
+ // Delete of the audio_rx and audio_tx objects will terminate
+ // thread execution.
+ if (audio_rx) {
+ // Reconfigure 3-way configuration if this audio session is
+ // part of a 3-way.
+ if (is_3way()) {
+ t_audio_session *peer = get_peer_3way();
+ if (peer) {
+ // Make the peer audio rx the main rx and remove
+ // reference to this audio rx
+ if (peer->audio_rx) {
+ peer->audio_rx->set_peer_rx_3way(NULL);
+ peer->audio_rx->set_main_rx_3way(true);
+ }
+
+ // Remove reference to this audio rx
+ if (peer->audio_tx) {
+ peer->audio_tx->set_peer_rx_3way(NULL);
+ }
+ }
+ }
+ MEMMAN_DELETE(audio_rx);
+ delete audio_rx;
+ }
+
+ if (audio_tx) {
+ // Reconfigure 3-way configuration if this audio session is
+ // part of a 3-way.
+ if (is_3way()) {
+ t_audio_session *peer = get_peer_3way();
+ if (peer) {
+ // Make the peer audio tx the mixer and remove
+ // reference to this audio tx
+ if (peer->audio_tx) {
+ peer->audio_tx->set_peer_tx_3way(NULL);
+ peer->audio_tx->set_mixer_3way(true);
+ }
+ }
+ }
+ MEMMAN_DELETE(audio_tx);
+ delete audio_tx;
+ }
+
+ if (thr_audio_rx) {
+ MEMMAN_DELETE(thr_audio_rx);
+ delete thr_audio_rx;
+ }
+
+ if (thr_audio_tx) {
+ MEMMAN_DELETE(thr_audio_tx);
+ delete thr_audio_tx;
+ }
+
+ if (rtp_session) {
+ log_file->write_header("t_audio_session::~t_audio_session");
+ log_file->write_raw("Line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": stopping RTP session.\n");
+ log_file->write_footer();
+
+ MEMMAN_DELETE(rtp_session);
+ delete rtp_session;
+
+ log_file->write_header("t_audio_session::~t_audio_session");
+ log_file->write_raw("Line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": RTP session stopped.\n");
+ log_file->write_footer();
+ }
+
+ if (speaker && (!is_3way() || !get_peer_3way())) {
+ if (mic == speaker) mic = 0;
+ MEMMAN_DELETE(speaker);
+ delete speaker;
+ speaker = 0;
+ }
+
+ if (mic && (!is_3way() || !get_peer_3way())) {
+ MEMMAN_DELETE(mic);
+ delete mic;
+ mic = 0;
+ }
+
+#ifdef HAVE_SPEEX
+ // cleaning speech AEC
+ if (do_echo_cancellation) {
+ speex_echo_state_destroy(speex_echo_state);
+ }
+#endif
+}
+
+void t_audio_session::set_session(t_session *_session) {
+ mtx_session.lock();
+ session = _session;
+ mtx_session.unlock();
+}
+
+void t_audio_session::run(void) {
+ _audio_session = this;
+
+ log_file->write_header("t_audio_session::run");
+ log_file->write_raw("Line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": starting RTP session.\n");
+ log_file->write_footer();
+
+ rtp_session->startRunning();
+
+ log_file->write_header("t_audio_session::run");
+ log_file->write_raw("Line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": RTP session started.\n");
+ log_file->write_footer();
+
+ if (audio_rx) {
+ try {
+ // Set the running flag now instead of at the start of
+ // t_audio_tx::run as due to race conditions the thread might
+ // get destroyed before the run method starts running. The
+ // destructor still has to wait on the thread to finish.
+ audio_rx->set_running(true);
+
+ thr_audio_rx = new t_thread(main_audio_rx, NULL);
+ MEMMAN_NEW(thr_audio_rx);
+ // thr_audio_rx->set_sched_fifo(90);
+ thr_audio_rx->detach();
+ } catch (int) {
+ audio_rx->set_running(false);
+ string msg(TRANSLATE2("CoreAudio", "Failed to create audio receiver thread."));
+ log_file->write_report(msg, "t_audio_session::run",
+ LOG_NORMAL, LOG_CRITICAL);
+ ui->cb_show_msg(msg, MSG_CRITICAL);
+ exit(1);
+ }
+ }
+
+
+ if (audio_tx) {
+ try {
+ // See comment above for audio_rx
+ audio_tx->set_running(true);
+
+ thr_audio_tx = new t_thread(main_audio_tx, NULL);
+ MEMMAN_NEW(thr_audio_tx);
+ // thr_audio_tx->set_sched_fifo(90);
+ thr_audio_tx->detach();
+ } catch (int) {
+ audio_tx->set_running(false);
+ string msg(TRANSLATE2("CoreAudio", "Failed to create audio transmitter thread."));
+ log_file->write_report(msg, "t_audio_session::run",
+ LOG_NORMAL, LOG_CRITICAL);
+ ui->cb_show_msg(msg, MSG_CRITICAL);
+ exit(1);
+ }
+ }
+}
+
+void t_audio_session::set_pt_out_dtmf(unsigned short pt) {
+ if (audio_rx) audio_rx->set_pt_telephone_event(pt);
+}
+
+void t_audio_session::set_pt_in_dtmf(unsigned short pt, unsigned short pt_alt) {
+ if (audio_tx) audio_tx->set_pt_telephone_event(pt, pt_alt);
+}
+
+void t_audio_session::send_dtmf(char digit, bool inband) {
+ if (audio_rx) audio_rx->push_dtmf(digit, inband);
+}
+
+t_line *t_audio_session::get_line(void) const {
+ t_line *line;
+ mtx_session.lock();
+ line = session->get_line();
+ mtx_session.unlock();
+
+ return line;
+}
+
+void t_audio_session::start_3way(void) {
+ if (audio_rx) {
+ audio_rx->join_3way(true, NULL);
+ }
+
+ if (audio_tx) {
+ audio_tx->join_3way(true, NULL, NULL);
+ }
+}
+
+void t_audio_session::stop_3way(void) {
+ if (audio_rx) {
+ t_audio_session *peer = get_peer_3way();
+ if (peer) {
+ if (peer->audio_rx) {
+ peer->audio_rx->set_peer_rx_3way(NULL);
+ }
+
+ if (peer->audio_tx) {
+ peer->audio_tx->set_peer_rx_3way(NULL);
+ }
+ }
+ audio_rx->stop_3way();
+ }
+
+ if (audio_tx) {
+ t_audio_session *peer = get_peer_3way();
+ if (peer) {
+ if (peer->audio_tx) {
+ peer->audio_tx->set_peer_tx_3way(NULL);
+ }
+ }
+ audio_tx->stop_3way();
+ }
+}
+
+bool t_audio_session::is_valid(void) const {
+ return valid;
+}
+
+t_audio_io* t_audio_session::get_dsp_speaker(void) const {
+ return speaker;
+}
+
+t_audio_io* t_audio_session::get_dsp_mic(void) const {
+ return mic;
+}
+
+bool t_audio_session::matching_sample_rates(void) const {
+ int codec_sample_rate = audio_sample_rate(codec);
+ return (speaker->get_sample_rate() == codec_sample_rate &&
+ mic->get_sample_rate() == codec_sample_rate);
+}
+
+void t_audio_session::confirm_zrtp_sas(void) {
+#ifdef HAVE_ZRTP
+ ZrtpQueue* zque = dynamic_cast<ZrtpQueue*>(rtp_session);
+ if (zque) {
+ zque->SASVerified();
+ set_zrtp_sas_confirmed(true);
+ }
+#endif
+}
+
+void t_audio_session::reset_zrtp_sas_confirmation(void) {
+#ifdef HAVE_ZRTP
+ ZrtpQueue* zque = dynamic_cast<ZrtpQueue*>(rtp_session);
+ if (zque) {
+ zque->resetSASVerified();
+ set_zrtp_sas_confirmed(false);
+ }
+#endif
+}
+
+void t_audio_session::enable_zrtp(void) {
+#ifdef HAVE_ZRTP
+ ZrtpQueue* zque = dynamic_cast<ZrtpQueue*>(rtp_session);
+ if (zque) {
+ zque->setEnableZrtp(true);
+ }
+#endif
+}
+
+void t_audio_session::zrtp_request_go_clear(void) {
+#ifdef HAVE_ZRTP
+ ZrtpQueue* zque = dynamic_cast<ZrtpQueue*>(rtp_session);
+ if (zque) {
+ zque->requestGoClear();
+ }
+#endif
+}
+
+void t_audio_session::zrtp_go_clear_ok(void) {
+#ifdef HAVE_ZRTP
+ ZrtpQueue* zque = dynamic_cast<ZrtpQueue*>(rtp_session);
+ if (zque) {
+ zque->goClearOk();
+ }
+#endif
+}
+
+bool t_audio_session::get_is_encrypted(void) const {
+ mtx_zrtp_data.lock();
+ bool b = is_encrypted;
+ mtx_zrtp_data.unlock();
+ return b;
+}
+
+string t_audio_session::get_zrtp_sas(void) const {
+ mtx_zrtp_data.lock();
+ string s = zrtp_sas;
+ mtx_zrtp_data.unlock();
+ return s;
+}
+
+bool t_audio_session::get_zrtp_sas_confirmed(void) const {
+ mtx_zrtp_data.lock();
+ bool b = zrtp_sas_confirmed;
+ mtx_zrtp_data.unlock();
+ return b;
+}
+
+string t_audio_session::get_srtp_cipher_mode(void) const {
+ mtx_zrtp_data.lock();
+ string s = srtp_cipher_mode;
+ mtx_zrtp_data.unlock();
+ return s;
+}
+
+void t_audio_session::set_is_encrypted(bool on) {
+ mtx_zrtp_data.lock();
+ is_encrypted = on;
+ mtx_zrtp_data.unlock();
+}
+
+void t_audio_session::set_zrtp_sas(const string &sas) {
+ mtx_zrtp_data.lock();
+ zrtp_sas = sas;
+ mtx_zrtp_data.unlock();
+}
+
+void t_audio_session::set_zrtp_sas_confirmed(bool confirmed) {
+ mtx_zrtp_data.lock();
+ zrtp_sas_confirmed = confirmed;
+ mtx_zrtp_data.unlock();
+}
+
+void t_audio_session::set_srtp_cipher_mode(const string &cipher_mode) {
+ mtx_zrtp_data.lock();
+ srtp_cipher_mode = cipher_mode;
+ mtx_zrtp_data.unlock();
+}
+
+
+#ifdef HAVE_SPEEX
+bool t_audio_session::get_do_echo_cancellation(void) const {
+ return do_echo_cancellation;
+}
+
+bool t_audio_session::get_echo_captured_last(void) {
+ return echo_captured_last;
+}
+
+void t_audio_session::set_echo_captured_last(bool value) {
+ echo_captured_last = value;
+}
+
+SpeexEchoState *t_audio_session::get_speex_echo_state(void) {
+ return speex_echo_state;
+}
+#endif
+
+void *main_audio_rx(void *arg) {
+ _audio_session->audio_rx->run();
+ return NULL;
+}
+
+void *main_audio_tx(void *arg) {
+ _audio_session->audio_tx->run();
+ return NULL;
+}
diff --git a/src/audio/audio_session.h b/src/audio/audio_session.h
new file mode 100644
index 0000000..422dbcd
--- /dev/null
+++ b/src/audio/audio_session.h
@@ -0,0 +1,182 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _AUDIO_SESSION_H
+#define _AUDIO_SESSION_H
+
+#include <map>
+#include <string>
+#include "audio_rx.h"
+#include "audio_tx.h"
+#include "session.h"
+#include "twinkle_rtp_session.h"
+#include "threads/thread.h"
+#include "threads/mutex.h"
+
+#ifdef HAVE_SPEEX
+#include <speex/speex_echo.h>
+#endif
+
+using namespace std;
+using namespace ost;
+
+// Forward declarations
+class t_session;
+class t_line;
+
+class t_audio_session {
+private:
+ // SIP session owning this audio session
+ t_session *session;
+
+ /** Mutex for concurrent access to the session. */
+ mutable t_mutex mtx_session;
+
+ // This flag indicates if the created audio session is valid.
+ // It might be invalid because, the RTP session could not be created
+ // or the soundcard could not be opened.
+ bool valid;
+
+ // file descriptor audio device
+ t_audio_io *speaker;
+ t_audio_io *mic;
+ t_twinkle_rtp_session *rtp_session;
+
+ t_audio_codec codec;
+ unsigned short ptime; // in milliseconds
+
+ t_thread *thr_audio_rx; // recording thread
+ t_thread *thr_audio_tx; // playing thread
+
+ // ZRTP info
+ mutable t_mutex mtx_zrtp_data;
+ bool is_encrypted;
+ string zrtp_sas;
+ bool zrtp_sas_confirmed;
+ string srtp_cipher_mode;
+
+#ifdef HAVE_SPEEX
+ // Indicator whether to use (Speex) AEC
+ bool do_echo_cancellation;
+
+ // Indicator whether the last operation of (Speex) AEC,
+ // speex_echo_capture or speex_echo_playback, was the speex_echo_capture
+ bool echo_captured_last;
+
+ // speex AEC state
+ SpeexEchoState *speex_echo_state;
+#endif
+
+ // 3-way conference data
+ // Returns if this audio session is part of a 3-way conference
+ bool is_3way(void) const;
+
+ // Returns the peer audio session of a 3-way conference
+ t_audio_session *get_peer_3way(void) const;
+
+ // Open the sound card
+ bool open_dsp(void);
+ bool open_dsp_full_duplex(void);
+ bool open_dsp_speaker(void);
+ bool open_dsp_mic(void);
+
+public:
+
+ t_audio_rx *audio_rx;
+ t_audio_tx *audio_tx;
+
+
+ t_audio_session(t_session *_session,
+ const string &_recv_host, unsigned short _recv_port,
+ const string &_dst_host, unsigned short _dst_port,
+ t_audio_codec _codec, unsigned short _ptime,
+ const map<unsigned short, t_audio_codec> &recv_payload2ac,
+ const map<t_audio_codec, unsigned short> &send_ac2payload,
+ bool encrypt);
+
+ ~t_audio_session();
+
+ void run(void);
+
+ /**
+ * Change the owning session.
+ * @param _session New session owning this audio session.
+ */
+ void set_session(t_session *_session);
+
+ // Set outgoing/incoming DTMF dynamic payload types
+ void set_pt_out_dtmf(unsigned short pt);
+ void set_pt_in_dtmf(unsigned short pt, unsigned short pt_alt);
+
+ // Send DTMF digit
+ void send_dtmf(char digit, bool inband);
+
+ // Get the line that belongs to this audio session
+ t_line *get_line(void) const;
+
+ // Become the first session in a 3-way conference
+ void start_3way(void);
+
+ // Leave a 3-way conference
+ void stop_3way(void);
+
+ // Check if audio session is valid
+ bool is_valid(void) const;
+
+ // Get pointer for soundcard I/O object
+ t_audio_io* get_dsp_speaker(void) const;
+ t_audio_io* get_dsp_mic(void) const;
+
+ // Check if sample rate from speaker and mic match with sample rate
+ // from codec. The sample rates might not match due to 3-way conference
+ // calls with mixed sample rate
+ bool matching_sample_rates(void) const;
+
+ // ZRTP actions
+ void confirm_zrtp_sas(void);
+ void reset_zrtp_sas_confirmation(void);
+ void enable_zrtp(void);
+ void zrtp_request_go_clear(void);
+ void zrtp_go_clear_ok(void);
+
+ // ZRTP data manipulations
+ bool get_is_encrypted(void) const;
+ string get_zrtp_sas(void) const;
+ bool get_zrtp_sas_confirmed(void) const;
+ string get_srtp_cipher_mode(void) const;
+
+ void set_is_encrypted(bool on);
+ void set_zrtp_sas(const string &sas);
+ void set_zrtp_sas_confirmed(bool confirmed);
+ void set_srtp_cipher_mode(const string &cipher_mode);
+
+#ifdef HAVE_SPEEX
+ // speex acoustic echo cancellation (AEC) manipulations
+ bool get_do_echo_cancellation(void) const;
+ bool get_echo_captured_last(void);
+ void set_echo_captured_last(bool value);
+ SpeexEchoState *get_speex_echo_state(void);
+#endif
+
+};
+
+// Main functions for rx and tx threads
+void *main_audio_rx(void *arg);
+void *main_audio_tx(void *arg);
+
+#endif
diff --git a/src/audio/audio_tx.cpp b/src/audio/audio_tx.cpp
new file mode 100644
index 0000000..4901ede
--- /dev/null
+++ b/src/audio/audio_tx.cpp
@@ -0,0 +1,1009 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <assert.h>
+#include <iostream>
+#include <cstdio>
+#include <ctime>
+#include <sys/types.h>
+#include <sys/time.h>
+#include "audio_tx.h"
+#include "log.h"
+#include "phone.h"
+#include "userintf.h"
+#include "util.h"
+#include "line.h"
+#include "sequence_number.h"
+#include "audits/memman.h"
+
+extern t_phone *phone;
+
+#define SAMPLE_BUF_SIZE (MAX_PTIME * sc_sample_rate/1000 * AUDIO_SAMPLE_SIZE/8)
+
+// Debug macro to print timestamp
+#define DEBUG_TS(s) { gettimeofday(&debug_timer, NULL);\
+ cout << "DEBUG: ";\
+ cout << debug_timer.tv_sec * 1000 +\
+ debug_timer.tv_usec / 1000;\
+ cout << ":" << debug_timer.tv_sec * 1000 + debug_timer.tv_usec / 1000 - (debug_timer_prev.tv_sec * 1000 + debug_timer_prev.tv_usec / 1000);\
+ cout << " " << (s) << endl;\
+ debug_timer_prev = debug_timer;\
+ }
+
+//////////
+// PUBLIC
+//////////
+
+t_audio_tx::t_audio_tx(t_audio_session *_audio_session,
+ t_audio_io *_playback_device, t_twinkle_rtp_session *_rtp_session,
+ t_audio_codec _codec,
+ const map<unsigned short, t_audio_codec> &_payload2codec,
+ unsigned short _ptime)
+{
+ audio_session = _audio_session;
+
+ user_config = audio_session->get_line()->get_user();
+ assert(user_config);
+
+ playback_device = _playback_device;
+ rtp_session = _rtp_session;
+ codec = _codec;
+ sc_sample_rate = audio_sample_rate(_codec);
+ payload2codec = _payload2codec;
+ is_running = false;
+ stop_running = false;
+
+ // Create audio decoders
+ map_audio_decoder[CODEC_G711_ALAW] = new t_g711a_audio_decoder(_ptime, user_config);
+ MEMMAN_NEW(map_audio_decoder[CODEC_G711_ALAW]);
+
+ map_audio_decoder[CODEC_G711_ULAW] = new t_g711u_audio_decoder(_ptime, user_config);
+ MEMMAN_NEW(map_audio_decoder[CODEC_G711_ULAW]);
+
+ map_audio_decoder[CODEC_GSM] = new t_gsm_audio_decoder(user_config);
+ MEMMAN_NEW(map_audio_decoder[CODEC_GSM]);
+
+#ifdef HAVE_SPEEX
+ map_audio_decoder[CODEC_SPEEX_NB] = new t_speex_audio_decoder(
+ t_speex_audio_decoder::MODE_NB, user_config);
+ MEMMAN_NEW(map_audio_decoder[CODEC_SPEEX_NB]);
+
+ map_audio_decoder[CODEC_SPEEX_WB] = new t_speex_audio_decoder(
+ t_speex_audio_decoder::MODE_WB, user_config);
+ MEMMAN_NEW(map_audio_decoder[CODEC_SPEEX_WB]);
+
+ map_audio_decoder[CODEC_SPEEX_UWB] = new t_speex_audio_decoder(
+ t_speex_audio_decoder::MODE_UWB, user_config);
+ MEMMAN_NEW(map_audio_decoder[CODEC_SPEEX_UWB]);
+#endif
+#ifdef HAVE_ILBC
+ map_audio_decoder[CODEC_ILBC] = new t_ilbc_audio_decoder(_ptime, user_config);
+ MEMMAN_NEW(map_audio_decoder[CODEC_ILBC]);
+#endif
+ map_audio_decoder[CODEC_G726_16] = new t_g726_audio_decoder(
+ t_g726_audio_decoder::BIT_RATE_16, _ptime, user_config);
+ MEMMAN_NEW(map_audio_decoder[CODEC_G726_16]);
+
+ map_audio_decoder[CODEC_G726_24] = new t_g726_audio_decoder(
+ t_g726_audio_decoder::BIT_RATE_24, _ptime, user_config);
+ MEMMAN_NEW(map_audio_decoder[CODEC_G726_24]);
+
+ map_audio_decoder[CODEC_G726_32] = new t_g726_audio_decoder(
+ t_g726_audio_decoder::BIT_RATE_32, _ptime, user_config);
+ MEMMAN_NEW(map_audio_decoder[CODEC_G726_32]);
+
+ map_audio_decoder[CODEC_G726_40] = new t_g726_audio_decoder(
+ t_g726_audio_decoder::BIT_RATE_40, _ptime, user_config);
+ MEMMAN_NEW(map_audio_decoder[CODEC_G726_40]);
+
+ ptime = map_audio_decoder[codec]->get_default_ptime();
+
+ sample_buf = new unsigned char[SAMPLE_BUF_SIZE];
+ MEMMAN_NEW_ARRAY(sample_buf);
+
+ // Create concealment buffers
+ for (int i = 0; i < MAX_CONCEALMENT; i++) {
+ conceal_buf[i] = new unsigned char[SAMPLE_BUF_SIZE];
+ MEMMAN_NEW_ARRAY(conceal_buf[i]);
+ conceal_buflen[i] = 0;
+ }
+ conceal_num = 0;
+ conceal_pos = 0;
+
+ // Initialize jitter buffer
+ jitter_buf = new unsigned char[JITTER_BUF_SIZE(sc_sample_rate)];
+ MEMMAN_NEW_ARRAY(jitter_buf);
+ jitter_buf_len = 0;
+ load_jitter_buf = true;
+ soundcard_buf_size = playback_device->get_buffer_size(false);
+
+ // Initialize 3-way settings
+ is_3way = false;
+ is_3way_mixer = false;
+ media_3way_peer_tx = NULL;
+ peer_tx_3way = NULL;
+ peer_rx_3way = NULL;
+ mix_buf_3way = NULL;
+
+ // Initialize telephone event settings
+ pt_telephone_event = -1;
+ pt_telephone_event_alt = 1;
+}
+
+t_audio_tx::~t_audio_tx() {
+ struct timespec sleeptimer;
+
+ if (is_running) {
+ stop_running = true;
+ do {
+ sleeptimer.tv_sec = 0;
+ sleeptimer.tv_nsec = 10000000;
+ nanosleep(&sleeptimer, NULL);
+ continue;
+ } while (is_running);
+ }
+
+ MEMMAN_DELETE_ARRAY(sample_buf);
+ delete [] sample_buf;
+ MEMMAN_DELETE_ARRAY(jitter_buf);
+ delete [] jitter_buf;
+
+ for (int i = 0; i < MAX_CONCEALMENT; i++) {
+ MEMMAN_DELETE_ARRAY(conceal_buf[i]);
+ delete [] conceal_buf[i];
+ }
+
+ // Destroy audio decoders
+ for (map<t_audio_codec, t_audio_decoder *>::iterator i = map_audio_decoder.begin();
+ i != map_audio_decoder.end(); i++)
+ {
+ MEMMAN_DELETE(i->second);
+ delete i->second;
+ }
+
+ // Cleanup 3-way resources
+ if (media_3way_peer_tx) {
+ MEMMAN_DELETE(media_3way_peer_tx);
+ delete media_3way_peer_tx;
+ }
+ if (mix_buf_3way) {
+ MEMMAN_DELETE_ARRAY(mix_buf_3way);
+ delete [] mix_buf_3way;
+ }
+}
+
+void t_audio_tx::retain_for_concealment(unsigned char *buf, unsigned short len) {
+ if (conceal_num == 0) {
+ memcpy(conceal_buf[0], buf, len);
+ conceal_buflen[0] = len;
+ conceal_num = 1;
+ conceal_pos = 0;
+ return;
+ }
+
+ if (conceal_num < MAX_CONCEALMENT) {
+ memcpy(conceal_buf[conceal_num], buf, len);
+ conceal_buflen[conceal_num] = len;
+ conceal_num++;
+ return;
+ }
+
+ memcpy(conceal_buf[conceal_pos], buf, len);
+ conceal_buflen[conceal_pos] = len;
+ conceal_pos = (conceal_pos + 1) % MAX_CONCEALMENT;
+}
+
+void t_audio_tx::conceal(short num) {
+ // Some codecs have a PLC.
+ // Only use this PLC is the sound card sample rate equals the codec
+ // sample rate. If they differ, then we should resample the codec
+ // samples. As this should be a rare case, we are lazy here. In
+ // this rare case, use Twinkle's low-tech PLC.
+ if (map_audio_decoder[codec]->has_plc() && audio_sample_rate(codec) == sc_sample_rate) {
+ short *sb = (short *)sample_buf;
+ for (int i = 0; i < num; i++) {
+ int nsamples;
+ nsamples = map_audio_decoder[codec]->conceal(sb, SAMPLE_BUF_SIZE);
+ if (nsamples > 0) {
+ play_pcm(sample_buf, nsamples * 2);
+ }
+ }
+
+ return;
+ }
+
+ // Replay previous packets for other codecs
+ short i = (conceal_pos + (MAX_CONCEALMENT - num)) % MAX_CONCEALMENT;
+
+ if (i >= conceal_pos) {
+ for (int j = i; j < MAX_CONCEALMENT; j++) {
+ play_pcm(conceal_buf[j], conceal_buflen[j]);
+ }
+
+ for (int j = 0; j < conceal_pos; j++) {
+ play_pcm(conceal_buf[j], conceal_buflen[j]);
+ }
+ } else {
+ for (int j = i; j < conceal_pos; j++) {
+ play_pcm(conceal_buf[j], conceal_buflen[j]);
+ }
+ }
+}
+
+void t_audio_tx::clear_conceal_buf(void) {
+ conceal_pos = 0;
+ conceal_num = 0;
+}
+
+void t_audio_tx::play_pcm(unsigned char *buf, unsigned short len, bool only_3rd_party) {
+ int status;
+ //struct timeval debug_timer, debug_timer_prev;
+
+ unsigned char *playbuf = buf;
+
+ // If there is only sound from the 3rd party in a 3-way, then check
+ // if there is still enough sound in the buffer of the DSP to be
+ // played. If not, then play out the sound from the 3rd party only.
+ if (only_3rd_party) {
+ /* Does not work on all ALSA implementations.
+ if (playback_device->get_buffer_space(false) < soundcard_buf_size - len) {
+ */
+ if (!playback_device->play_buffer_underrun()) {
+ // There is still sound in the DSP buffers to be
+ // played, so let's wait. Maybe in the next cycle
+ // an RTP packet from the far-end will be received.
+ return;
+ }
+ }
+
+ // If we are in a 3-way then send the samples to the peer audio
+ // receiver for mixing
+ if (!only_3rd_party && is_3way && peer_rx_3way) {
+ peer_rx_3way->post_media_peer_tx_3way(buf, len, sc_sample_rate);
+ }
+
+ // If we are in a 3-way conference and we are not the mixer then
+ // send the sound samples to the mixer
+ if (is_3way && !is_3way_mixer) {
+ if (peer_tx_3way) {
+ peer_tx_3way->post_media_peer_tx_3way(buf, len, sc_sample_rate);
+ return;
+ } else {
+ // There is no peer.
+ return;
+ }
+ }
+
+ // Mix audio for 3-way conference
+ if (is_3way && is_3way_mixer) {
+ if (media_3way_peer_tx->get(mix_buf_3way, len)) {
+ short *mix_sb = (short *)mix_buf_3way;
+ short *sb = (short *)buf;
+ for (int i = 0; i < len / 2; i++) {
+ mix_sb[i] = mix_linear_pcm(sb[i], mix_sb[i]);
+ }
+
+ playbuf = mix_buf_3way;
+ }
+ }
+
+ // Fill jitter buffer before playing
+ if (load_jitter_buf) {
+ if (jitter_buf_len + len < JITTER_BUF_SIZE(sc_sample_rate)) {
+ memcpy(jitter_buf + jitter_buf_len, playbuf, len);
+ jitter_buf_len += len;
+ } else {
+ // Write the contents of the jitter buffer to the DSP.
+ // The buffers in the DSP will now function as jitter
+ // buffer.
+ status = playback_device->write(jitter_buf, jitter_buf_len);
+ if (status != jitter_buf_len) {
+ string msg("Writing to dsp failed: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_audio_tx::play_pcm",
+ LOG_NORMAL, LOG_CRITICAL);
+ }
+
+ // Write passed sound samples to DSP.
+ status = playback_device->write(playbuf, len);
+ if (status != len) {
+ string msg("Writing to dsp failed: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_audio_tx::play_pcm",
+ LOG_NORMAL, LOG_CRITICAL);
+ }
+
+ load_jitter_buf = false;
+ }
+
+ return;
+ }
+
+ // If buffer on soundcard is empty, then the jitter buffer needs
+ // to be refilled. This should only occur when no RTP packets
+ // have been received for a while (silence suppression or packet loss)
+ /*
+ * This code does not work on all ALSA implementations, e.g. ALSA via pulse audio
+ int bufferspace = playback_device->get_buffer_space(false);
+ if (bufferspace == soundcard_buf_size && len <= JITTER_BUF_SIZE(sc_sample_rate)) {
+ */
+ if (playback_device->play_buffer_underrun()) {
+ memcpy(jitter_buf, playbuf, len);
+ jitter_buf_len = len;
+ load_jitter_buf = true;
+ log_file->write_header("t_audio_tx::play_pcm", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": jitter buffer empty.\n");
+ log_file->write_footer();
+ return;
+ }
+
+ // If the play-out buffer contains the maximum number of
+ // packets then start skipping packets to prevent
+ // unacceptable delay.
+ // This can only happen if the thread did not get
+ // processing time for a while and RTP packets start to
+ // pile up.
+ // Or if a soundcard plays out the samples at just less then
+ // the requested sample rate.
+ /* Not needed anymore, the ::run loop already discards incoming RTP packets
+ with a late timestamp. This seems to solve the slow soundcard problem
+ better. The solution below caused annoying ticks in the playout.
+
+ if (soundcard_buf_size - bufferspace > JITTER_BUF_SIZE + len) {
+ log_file->write_header("t_audio_tx::play_pcm", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": jitter buffer overflow: ");
+ log_file->write_raw(bufferspace);
+ log_file->write_raw(" bytes.\n");
+ log_file->write_footer();
+ return;
+ }
+ */
+
+ // Write passed sound samples to DSP.
+ status = playback_device->write(playbuf, len);
+
+ if (status != len) {
+ string msg("Writing to dsp failed: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_audio_tx::play_pcm",
+ LOG_NORMAL, LOG_CRITICAL);
+ return;
+ }
+}
+
+void t_audio_tx::set_running(bool running) {
+ is_running = running;
+}
+
+void t_audio_tx::run(void) {
+ const AppDataUnit* adu;
+ struct timespec sleeptimer;
+ //struct timeval debug_timer, debug_timer_prev;
+ int last_seqnum = -1; // seqnum of last received RTP packet
+
+ // RTP packets with multiple SSRCs may be received. Each SSRC
+ // represents an audio stream. Twinkle will only play 1 audio stream.
+ // On a reception of a new SSRC, Twinkle will switch over to play the
+ // new stream. This supports devices that change SSRC during a call.
+ uint32 ssrc_current = 0;
+
+ bool recvd_dtmf = false; // indicates if last RTP packets is a DTMF event
+
+ // The running flag is set already in t_audio_session::run to prevent
+ // a crash when the thread gets destroyed before it starts running.
+ // is_running = true;
+
+ uint32 rtp_timestamp = 0;
+
+ // This thread may not take the lock on the transaction layer to
+ // prevent dead locks
+ phone->add_prohibited_thread();
+ ui->add_prohibited_thread();
+
+ while (true) {
+ do {
+ adu = NULL;
+ if (stop_running) break;
+ rtp_timestamp = rtp_session->getFirstTimestamp();
+ adu = rtp_session->getData(
+ rtp_session->getFirstTimestamp());
+ if (adu == NULL || adu->getSize() <= 0) {
+ // There is no packet available. This may have
+ // several reasons:
+ // - the thread scheduling granularity does
+ // not match ptime
+ // - packet lost
+ // - packet delayed
+ // Wait another cycle for a packet. The
+ // jitter buffer will cope with this variation.
+ if (adu) {
+ delete adu;
+ adu = NULL;
+ }
+
+ // If we are the mixer in a 3-way call and there
+ // is enough media from the other far-end then
+ // this must be sent to the dsp.
+ if (is_3way && is_3way_mixer &&
+ media_3way_peer_tx->size_content() >=
+ ptime * (audio_sample_rate(codec) / 1000) * 2)
+ {
+ // Fill the sample buffer with silence
+ int len = ptime * (audio_sample_rate(codec) / 1000) * 2;
+ memset(sample_buf, 0, len);
+ play_pcm(sample_buf, len, true);
+ }
+
+ // Sleep ptime ms
+ sleeptimer.tv_sec = 0;
+
+ if (ptime >= 20) {
+ sleeptimer.tv_nsec =
+ ptime * 1000000 - 10000000;
+ } else {
+ // With a thread schedule of 10ms
+ // granularity, this will schedule the
+ // thread every 10ms.
+ sleeptimer.tv_nsec = 5000000;
+ }
+ nanosleep(&sleeptimer, NULL);
+ }
+ } while (adu == NULL || (adu->getSize() <= 0));
+
+ if (stop_running) {
+ if (adu) delete adu;
+ break;
+ }
+
+ if (adu) {
+ // adu is created by ccRTP, but we have to delete it,
+ // so report it to MEMMAN
+ MEMMAN_NEW(const_cast<ost::AppDataUnit*>(adu));
+ }
+
+ // Check for a codec change
+ map<unsigned short, t_audio_codec>::const_iterator it_codec;
+ it_codec = payload2codec.find(adu->getType());
+ t_audio_codec recvd_codec = CODEC_NULL;
+ if (it_codec != payload2codec.end()) {
+ recvd_codec = it_codec->second;
+ }
+
+ // Switch over to new SSRC
+ if (last_seqnum == -1 || ssrc_current != adu->getSource().getID()) {
+ if (recvd_codec != CODEC_NULL) {
+ ssrc_current = adu->getSource().getID();
+
+ // An SSRC defines a sequence number space. So a new
+ // SSRC starts with a new random sequence number
+ last_seqnum = -1;
+
+ log_file->write_header("t_audio_tx::run",
+ LOG_NORMAL);
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": play SSRC ");
+ log_file->write_raw(ssrc_current);
+ log_file->write_endl();
+ log_file->write_footer();
+ } else {
+ // SSRC received had an unsupported codec
+ // Discard.
+ // KLUDGE: for now this supports a scenario where a
+ // far-end starts ZRTP negotiation by sending CN
+ // packets with a separate SSRC while ZRTP is disabled
+ // in Twinkle. Twinkle will then receive the CN packets
+ // and discard them here as CN is an unsupported codec.
+ log_file->write_header("t_audio_tx::run",
+ LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": SSRC received (");
+ log_file->write_raw(adu->getSource().getID());
+ log_file->write_raw(") has unsupported codec ");
+ log_file->write_raw(adu->getType());
+ log_file->write_endl();
+ log_file->write_footer();
+
+ MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
+ delete adu;
+ continue;
+ }
+ }
+
+ map<t_audio_codec, t_audio_decoder *>::const_iterator it_decoder;
+ it_decoder = map_audio_decoder.find(recvd_codec);
+ if (it_decoder != map_audio_decoder.end()) {
+ if (codec != recvd_codec) {
+ codec = recvd_codec;
+ get_line()->ci_set_recv_codec(codec);
+ ui->cb_async_recv_codec_changed(get_line()->get_line_number(),
+ codec);
+
+ log_file->write_header("t_audio_tx::run",
+ LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": codec change to ");
+ log_file->write_raw(ui->format_codec(codec));
+ log_file->write_endl();
+ log_file->write_footer();
+ }
+ } else {
+ if (adu->getType() == pt_telephone_event ||
+ adu->getType() == pt_telephone_event_alt)
+ {
+ recvd_dtmf = true;
+ } else {
+ if (codec != CODEC_UNSUPPORTED) {
+ codec = CODEC_UNSUPPORTED;
+ get_line()->ci_set_recv_codec(codec);
+ ui->cb_async_recv_codec_changed(
+ get_line()->get_line_number(), codec);
+
+ log_file->write_header("t_audio_tx::run",
+ LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": payload type ");
+ log_file->write_raw(adu->getType());
+ log_file->write_raw(" not supported\n");
+ log_file->write_footer();
+ }
+
+ last_seqnum = adu->getSeqNum();
+ MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
+ delete adu;
+ continue;
+ }
+ }
+
+ // DTMF event
+ if (recvd_dtmf) {
+ // NOTE: the DTMF tone will be detected here
+ // while there might still be data in the jitter
+ // buffer. If the jitter buffer was already sent
+ // to the DSP, then the DSP will continue to play
+ // out the buffer sound samples.
+
+ if (dtmf_previous_timestamp != rtp_timestamp) {
+ // A new DTMF tone has been received.
+ dtmf_previous_timestamp = rtp_timestamp;
+ t_rtp_telephone_event *e =
+ (t_rtp_telephone_event *)adu->getData();
+ ui->cb_async_dtmf_detected(get_line()->get_line_number(),
+ e->get_event());
+
+ // Log DTMF event
+ log_file->write_header("t_audio_tx::run");
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": detected DTMF event - ");
+ log_file->write_raw(e->get_event());
+ log_file->write_endl();
+ log_file->write_footer();
+ }
+
+ recvd_dtmf = false;
+ last_seqnum = adu->getSeqNum();
+ MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
+ delete adu;
+ continue;
+ }
+
+ // Discard invalide payload sizes
+ if (!map_audio_decoder[codec]->valid_payload_size(
+ adu->getSize(), SAMPLE_BUF_SIZE / 2))
+ {
+ log_file->write_header("t_audio_tx::run", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": RTP payload size (");
+ log_file->write_raw((unsigned long)(adu->getSize()));
+ log_file->write_raw(" bytes) invalid for \n");
+ log_file->write_raw(ui->format_codec(codec));
+ log_file->write_footer();
+ last_seqnum = adu->getSeqNum();
+ MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
+ delete adu;
+ continue;
+ }
+
+ unsigned short recvd_ptime;
+ recvd_ptime = map_audio_decoder[codec]->get_ptime(adu->getSize());
+
+ // Log a change of ptime
+ if (ptime != recvd_ptime) {
+ log_file->write_header("t_audio_tx::run", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": ptime changed from ");
+ log_file->write_raw(ptime);
+ log_file->write_raw(" ms to ");
+ log_file->write_raw(recvd_ptime);
+ log_file->write_raw(" ms\n");
+ log_file->write_footer();
+ ptime = recvd_ptime;
+ }
+
+ // Check for lost packets
+ // This must be done before decoding the received samples as the
+ // speex decoder has its own PLC algorithm for which it needs the decoding
+ // state before decoding the new samples.
+ seq16_t seq_recvd(adu->getSeqNum());
+ seq16_t seq_last(static_cast<uint16>(last_seqnum));
+ if (last_seqnum != -1 && seq_recvd - seq_last > 1) {
+ // Packets have been lost
+ uint16 num_lost = (seq_recvd - seq_last) - 1;
+ log_file->write_header("t_audio_tx::run", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": ");
+ log_file->write_raw(num_lost);
+ log_file->write_raw(" RTP packets lost.\n");
+ log_file->write_footer();
+
+ if (num_lost <= conceal_num) {
+ // Conceal packet loss
+ conceal(num_lost);
+ }
+ clear_conceal_buf();
+ }
+
+ // Determine if resampling is needed due to dynamic change to
+ // codec with other sample rate.
+ short downsample_factor = 1;
+ short upsample_factor = 1;
+ if (audio_sample_rate(codec) > sc_sample_rate) {
+ downsample_factor = audio_sample_rate(codec) / sc_sample_rate;
+ } else if (audio_sample_rate(codec) < sc_sample_rate) {
+ upsample_factor = sc_sample_rate / audio_sample_rate(codec);
+ }
+
+ // Create sample buffer. If no resampling is needed, the sample
+ // buffer from the audio_tx object can be used directly.
+ // Otherwise a temporary sample buffers is created that will
+ // be resampled to the object's sample buffer later.
+ short *sb;
+ int sb_size;
+ if (downsample_factor > 1) {
+ sb_size = SAMPLE_BUF_SIZE / 2 * downsample_factor;
+ sb = new short[sb_size];
+ MEMMAN_NEW_ARRAY(sb);
+ } else if (upsample_factor > 1) {
+ sb_size = SAMPLE_BUF_SIZE / 2;
+ sb = new short[SAMPLE_BUF_SIZE / 2];
+ MEMMAN_NEW_ARRAY(sb);
+ } else {
+ sb_size = SAMPLE_BUF_SIZE / 2;
+ sb = (short *)sample_buf;
+ }
+
+
+ // Decode the audio
+ unsigned char *payload = const_cast<uint8 *>(adu->getData());
+ short sample_size; // size in bytes
+
+ sample_size = 2 * map_audio_decoder[codec]->decode(payload, adu->getSize(), sb, sb_size);
+
+ // Resample if needed
+ if (downsample_factor > 1) {
+ short *p = sb;
+ sb = (short *)sample_buf;
+ for (int i = 0; i < sample_size / 2; i += downsample_factor) {
+ sb[i / downsample_factor] = p[i];
+ }
+ MEMMAN_DELETE_ARRAY(p);
+ delete [] p;
+ sample_size /= downsample_factor;
+ } else if (upsample_factor > 1) {
+ short *p = sb;
+ sb = (short *)sample_buf;
+ for (int i = 0; i < sample_size / 2; i++) {
+ for (int j = 0; j < upsample_factor; j++) {
+ sb[i * upsample_factor + j] = p[i];
+ }
+ }
+ MEMMAN_DELETE_ARRAY(p);
+ delete [] p;
+ sample_size *= upsample_factor;
+ }
+
+ // If the decoder deliverd 0 bytes, then it failed
+ if (sample_size == 0) {
+ last_seqnum = adu->getSeqNum();
+ MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
+ delete adu;
+ continue;
+ }
+
+ // Discard packet if we are lacking behind. This happens if the
+ // soundcard plays at a rate less than the requested sample rate.
+ if (rtp_session->isWaiting(&(adu->getSource()))) {
+
+ uint32 last_ts = rtp_session->getLastTimestamp(&(adu->getSource()));
+ uint32 diff;
+
+ diff = last_ts - rtp_timestamp;
+
+ if (diff > (uint32_t)(JITTER_BUF_SIZE(sc_sample_rate) / AUDIO_SAMPLE_SIZE) * 8)
+ {
+ log_file->write_header("t_audio_tx::run", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": discard delayed packet.\n");
+ log_file->write_raw("Timestamp: ");
+ log_file->write_raw(rtp_timestamp);
+ log_file->write_raw(", Last timestamp: ");
+ log_file->write_raw((long unsigned int)last_ts);
+ log_file->write_endl();
+ log_file->write_footer();
+
+ last_seqnum = adu->getSeqNum();
+ MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
+ delete adu;
+ continue;
+ }
+ }
+
+ play_pcm(sample_buf, sample_size);
+ retain_for_concealment(sample_buf, sample_size);
+ last_seqnum = adu->getSeqNum();
+ MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
+ delete adu;
+
+ // No sleep is done here but in the loop waiting
+ // for a new packet. If a packet is already available
+ // it can be send to the sound card immediately so
+ // the play-out buffer keeps filled.
+ // If the play-out buffer gets empty you hear a
+ // crack in the sound.
+
+
+#ifdef HAVE_SPEEX
+ // store decoded output for (optional) echo cancellation
+ if (audio_session->get_do_echo_cancellation()) {
+ if (audio_session->get_echo_captured_last()) {
+ speex_echo_playback(audio_session->get_speex_echo_state(), (spx_int16_t *) sb);
+ audio_session->set_echo_captured_last(false);;
+ }
+ }
+#endif
+
+ }
+
+ phone->remove_prohibited_thread();
+ ui->remove_prohibited_thread();
+ is_running = false;
+}
+
+void t_audio_tx::set_pt_telephone_event(int pt, int pt_alt) {
+ pt_telephone_event = pt;
+ pt_telephone_event_alt = pt_alt;
+}
+
+t_line *t_audio_tx::get_line(void) const {
+ return audio_session->get_line();
+}
+
+void t_audio_tx::join_3way(bool mixer, t_audio_tx *peer_tx, t_audio_rx *peer_rx) {
+ mtx_3way.lock();
+
+ if (is_3way) {
+ log_file->write_header("t_audio_tx::join_3way");
+ log_file->write_raw("ERROR: audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(" - 3way is already active.\n");
+ log_file->write_footer();
+ mtx_3way.unlock();
+ return;
+ }
+
+ // Logging
+ log_file->write_header("t_audio_tx::join_3way");
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": join 3-way.\n");
+ if (mixer) {
+ log_file->write_raw("Role is: mixer.\n");
+ } else {
+ log_file->write_raw("Role is: non-mixing.\n");
+ }
+ if (peer_tx) {
+ log_file->write_raw("A peer transmitter already exists.\n");
+ } else {
+ log_file->write_raw("A peer transmitter does not exist.\n");
+ }
+ if (peer_rx) {
+ log_file->write_raw("A peer receiver already exists.\n");
+ } else {
+ log_file->write_raw("A peer receiver does not exist.\n");
+ }
+ log_file->write_footer();
+
+ peer_tx_3way = peer_tx;
+ peer_rx_3way = peer_rx;
+ is_3way_mixer = mixer;
+ is_3way = true;
+
+ // Create buffers for mixing
+ mix_buf_3way = new unsigned char[SAMPLE_BUF_SIZE];
+ MEMMAN_NEW_ARRAY(mix_buf_3way);
+
+ // See comments in audio_rx.cpp for the size of this buffer.
+ media_3way_peer_tx = new t_media_buffer(JITTER_BUF_SIZE(sc_sample_rate));
+ MEMMAN_NEW(media_3way_peer_tx);
+
+ mtx_3way.unlock();
+}
+
+void t_audio_tx::set_peer_tx_3way(t_audio_tx *peer_tx) {
+ mtx_3way.lock();
+
+ if (!is_3way) {
+ mtx_3way.unlock();
+ return;
+ }
+
+ // Logging
+ log_file->write_header("t_audio_tx::set_peer_tx_3way");
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ if (peer_tx) {
+ log_file->write_raw(": set peer transmitter.\n");
+ } else {
+ log_file->write_raw(": erase peer transmitter.\n");
+ }
+ if (is_3way_mixer) {
+ log_file->write_raw("Role is: mixer.\n");
+ } else {
+ log_file->write_raw("Role is: non-mixing.\n");
+ }
+ log_file->write_footer();
+
+
+ peer_tx_3way = peer_tx;
+
+ mtx_3way.unlock();
+}
+
+void t_audio_tx::set_peer_rx_3way(t_audio_rx *peer_rx) {
+ mtx_3way.lock();
+
+ if (!is_3way) {
+ mtx_3way.unlock();
+ return;
+ }
+
+ // Logging
+ log_file->write_header("t_audio_tx::set_peer_rx_3way");
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ if (peer_rx) {
+ log_file->write_raw(": set peer receiver.\n");
+ } else {
+ log_file->write_raw(": erase peer receiver.\n");
+ }
+ if (is_3way_mixer) {
+ log_file->write_raw("Role is: mixer.\n");
+ } else {
+ log_file->write_raw("Role is: non-mixing.\n");
+ }
+ log_file->write_footer();
+
+ peer_rx_3way = peer_rx;
+
+ mtx_3way.unlock();
+}
+
+void t_audio_tx::set_mixer_3way(bool mixer) {
+ mtx_3way.lock();
+
+ if (!is_3way) {
+ mtx_3way.unlock();
+ return;
+ }
+
+ // Logging
+ log_file->write_header("t_audio_tx::set_mixer_3way");
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ if (mixer) {
+ log_file->write_raw(": change role to: mixer.\n");
+ } else {
+ log_file->write_raw(": change role to: non-mixing.\n");
+ }
+ log_file->write_footer();
+
+ is_3way_mixer = mixer;
+
+ mtx_3way.unlock();
+}
+
+void t_audio_tx::stop_3way(void) {
+ mtx_3way.lock();
+
+ if (!is_3way) {
+ log_file->write_header("t_audio_tx::stop_3way");
+ log_file->write_raw("ERROR: audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(" - 3way is not active.\n");
+ log_file->write_footer();
+ mtx_3way.unlock();
+ return;
+ }
+
+ // Logging
+ log_file->write_header("t_audio_tx::stop_3way");
+ log_file->write_raw("Audio tx line ");
+ log_file->write_raw(get_line()->get_line_number()+1);
+ log_file->write_raw(": stop 3-way.\n");
+ log_file->write_footer();
+
+ is_3way = false;
+ is_3way_mixer = false;
+
+ if (media_3way_peer_tx) {
+ MEMMAN_DELETE(media_3way_peer_tx);
+ delete media_3way_peer_tx;
+ media_3way_peer_tx = NULL;
+ }
+
+ if (mix_buf_3way) {
+ MEMMAN_DELETE_ARRAY(mix_buf_3way);
+ delete [] mix_buf_3way;
+ mix_buf_3way = NULL;
+ }
+
+ mtx_3way.unlock();
+}
+
+void t_audio_tx::post_media_peer_tx_3way(unsigned char *media, int len,
+ unsigned short peer_sample_rate)
+{
+ mtx_3way.lock();
+
+ if (!is_3way || !is_3way_mixer) {
+ mtx_3way.unlock();
+ return;
+ }
+
+ if (peer_sample_rate != sc_sample_rate) {
+ // Resample media from peer to sample rate of this transmitter
+ int output_len = (len / 2) * sc_sample_rate / peer_sample_rate;
+ short *output_buf = new short[output_len];
+ MEMMAN_NEW_ARRAY(output_buf);
+ int resample_len = resample((short *)media, len / 2, peer_sample_rate,
+ output_buf, output_len, sc_sample_rate);
+ media_3way_peer_tx->add((unsigned char *)output_buf, resample_len * 2);
+ MEMMAN_DELETE_ARRAY(output_buf);
+ delete [] output_buf;
+ } else {
+ media_3way_peer_tx->add(media, len);
+ }
+
+ mtx_3way.unlock();
+}
+
+bool t_audio_tx::get_is_3way_mixer(void) const {
+ return is_3way_mixer;
+}
diff --git a/src/audio/audio_tx.h b/src/audio/audio_tx.h
new file mode 100644
index 0000000..a51ef9a
--- /dev/null
+++ b/src/audio/audio_tx.h
@@ -0,0 +1,205 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _AUDIO_TX_H
+#define _AUDIO_TX_H
+
+// Receive RTP and send audio to soundcard
+
+#include <map>
+#include <string>
+#include "audio_codecs.h"
+#include "audio_decoder.h"
+#include "audio_rx.h"
+#include "media_buffer.h"
+#include "rtp_telephone_event.h"
+#include "user.h"
+#include "threads/mutex.h"
+#include "gsm/inc/gsm.h"
+#include "audio_device.h"
+#include "twinkle_rtp_session.h"
+#include "twinkle_config.h"
+
+using namespace std;
+using namespace ost;
+
+// Forward declarations
+class t_audio_session;
+class t_line;
+
+class t_audio_tx {
+private:
+ // audio_session owning this audio transmitter
+ t_audio_session *audio_session;
+
+ // User profile of user using the line
+ // This is a pointer to the user_config owned by a phone user.
+ // So this pointer should never be deleted.
+ t_user *user_config;
+
+ // file descriptor audio capture device
+ t_audio_io *playback_device;
+ t_twinkle_rtp_session *rtp_session;
+
+ // Indicates if this transmitter is part of a 3-way conference
+ bool is_3way;
+
+ // Indicates if this transmitter is the mixer in a 3-way conference.
+ // The mixer will mix this audio stream with the audio from the other
+ // party and send it to the soundcard.
+ // If the transmitter is part of a 3-way conference, but not the
+ // mixer, then it simply has to send its audio payload to the mixer.
+ bool is_3way_mixer;
+
+ // Media buffer for media from the other far-end. This buffer is
+ // used by the mixer.
+ t_media_buffer *media_3way_peer_tx;
+
+ // The peer audio transmitter in a 3-way conference
+ t_audio_tx *peer_tx_3way;
+
+ // The audio receiver that needs input from this transmitter in
+ // a 3-way conference.
+ t_audio_rx *peer_rx_3way;
+
+ // Buffer for mixing purposes in 3-way conference.
+ unsigned char *mix_buf_3way;
+
+ // Mutex to protect 3-way resources
+ t_mutex mtx_3way;
+
+ // Codec information
+ t_audio_codec codec;
+ map<unsigned short, t_audio_codec> payload2codec;
+ unsigned short ptime; // in milliseconds
+
+ // Sample rate of sound card.
+ // The sample rate of the sound card is set to the sample rate
+ // used for the initial codec. The far end may dynamically switch
+ // to a codec with another sample rate. This will not change the
+ // sample rate of the sound card! (capture and playback cannot
+ // be done at different sampling rates).
+ unsigned short sc_sample_rate;
+
+ // Mapping from codecs to decoders
+ map<t_audio_codec, t_audio_decoder *> map_audio_decoder;
+
+ // Buffer to store PCM samples of a received RTP packet
+ unsigned char *sample_buf;
+
+ // Jitter buffer (PCM).
+ // jitter_buf_len indicates the number of bytes in the jitter buffer.
+ // At the start of playing the samples are stored in the jitter buffer.
+ // Once the buffer is full, all samples are copied to the memory of
+ // the soundcard. From that point the soundcard itself is the jitter
+ // buffer.
+ unsigned char *jitter_buf;
+ unsigned short jitter_buf_len;
+ bool load_jitter_buf;
+
+ // Buffer to keep last played packets for concealment.
+ unsigned char *conceal_buf[MAX_CONCEALMENT];
+ unsigned short conceal_buflen[MAX_CONCEALMENT]; // length of packet
+ short conceal_pos; // points to the oldest packet.
+ short conceal_num; // number of retained packets.
+
+ unsigned short soundcard_buf_size;
+
+ // Payload type for telephone-event payload.
+ // Some endpoints ignore the payload type that was sent in an
+ // outgoing INVITE and simply sends it with the payload type,
+ // they indicated in the 200 OK. Accept both payloads for
+ // interoperability.
+ int pt_telephone_event;
+ int pt_telephone_event_alt;
+
+ // Timestamp of previous DTMF tone
+ unsigned long dtmf_previous_timestamp;
+
+ // Inidicates if the playing thread is running
+ volatile bool is_running;
+
+ // The thread exits when this indicator is set to true
+ volatile bool stop_running;
+
+ // Retain a packet (PCM encoded) for possible concealment.
+ void retain_for_concealment(unsigned char *buf, unsigned short len);
+
+ // Play last num packets again.
+ void conceal(short num);
+
+ // Erase concealment buffers.
+ void clear_conceal_buf(void);
+
+ // Play PCM encoded samples
+ // - only_3rd_party indicates if there is only 3rd_party audio available,
+ // i.e. due to jitter, packet loss or silence suppression
+ void play_pcm(unsigned char *buf, unsigned short len, bool only_3rd_party = false);
+
+public:
+ // Create the audio transmitter
+ // _fd file descriptor of capture device
+ // _rtp_session RTP socket tp send the RTP stream
+ // _codec audio codec to use
+ // _ptime length of the audio packets in ms
+ // _ptime = 0 means use default ptime value for the codec
+ t_audio_tx(t_audio_session *_audio_session, t_audio_io *_playback_device,
+ t_twinkle_rtp_session *_rtp_session,
+ t_audio_codec _codec,
+ const map<unsigned short, t_audio_codec> &_payload2codec,
+ unsigned short _ptime = 0);
+
+ ~t_audio_tx();
+
+ // Set the is running flag
+ void set_running(bool running);
+
+ void run(void);
+
+ // Set the dynamic payload type for telephone events
+ void set_pt_telephone_event(int pt, int pt_alt);
+
+ // Get phone line belonging to this audio transmitter
+ t_line *get_line(void) const;
+
+ // Join this transmitter in a 3way conference.
+ // mixer indicates if this transmitter must be the mixer.
+ // - peer_tx is the peer transmitter in a 3-way
+ // - audio_rx is the audio receiver needing the output from this
+ // transmitter for mixing.
+ void join_3way(bool mixer, t_audio_tx *peer_tx, t_audio_rx *peer_rx);
+
+ // Change the peer rx/tx (NULL to erase)
+ void set_peer_tx_3way(t_audio_tx *peer_tx);
+ void set_peer_rx_3way(t_audio_rx *peer_rx);
+
+ // Change the mixer role
+ void set_mixer_3way(bool mixer);
+
+ // Stop the 3-way conference and make it a 1-on-1 call again.
+ void stop_3way(void);
+
+ // Post media from the peer transmitter for a 3-way mixer.
+ void post_media_peer_tx_3way(unsigned char *media, int len,
+ unsigned short peer_sample_rate);
+
+ // Returns if this transmitter is the mixer in a 3-way
+ bool get_is_3way_mixer(void) const;
+};
+
+#endif
diff --git a/src/audio/dtmf_player.cpp b/src/audio/dtmf_player.cpp
new file mode 100644
index 0000000..d3d5909
--- /dev/null
+++ b/src/audio/dtmf_player.cpp
@@ -0,0 +1,183 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <cassert>
+#include <cstring>
+#include <sys/time.h>
+#include "dtmf_player.h"
+#include "audio_rx.h"
+#include "line.h"
+#include "rtp_telephone_event.h"
+#include "log.h"
+
+/////////////////////////////////////////
+// class t_dtmf_player
+/////////////////////////////////////////
+
+t_dtmf_player::t_dtmf_player(t_audio_rx *audio_rx, t_audio_encoder *audio_encoder,
+ t_user *user_config, uint8 dtmf_tone, uint32 dtmf_timestamp,
+ uint16 nsamples) :
+ _audio_rx(audio_rx),
+ _user_config(user_config),
+ _audio_encoder(audio_encoder),
+ _dtmf_pause(false),
+ _dtmf_stop(false),
+ _dtmf_current(dtmf_tone),
+ _dtmf_timestamp(dtmf_timestamp),
+ _dtmf_duration(0),
+ _nsamples(nsamples)
+{}
+
+uint32 t_dtmf_player::get_timestamp(void) {
+ return _dtmf_timestamp;
+}
+
+bool t_dtmf_player::finished(void) {
+ return _dtmf_stop;
+}
+
+/////////////////////////////////////////
+// class t_rtp_event_dtmf_player
+/////////////////////////////////////////
+
+t_rtp_event_dtmf_player::t_rtp_event_dtmf_player(t_audio_rx *audio_rx,
+ t_audio_encoder *audio_encoder, t_user *user_config,
+ uint8 dtmf_tone, uint32 dtmf_timestamp,
+ uint16 nsamples) :
+ t_dtmf_player(audio_rx, audio_encoder, user_config, dtmf_tone, dtmf_timestamp,
+ nsamples)
+{
+}
+
+uint16 t_rtp_event_dtmf_player::get_payload(uint8 *payload,
+ uint16 payload_size, uint32 timestamp, uint32 &rtp_timestamp)
+{
+ t_rtp_telephone_event *dtmf_payload = (t_rtp_telephone_event *)payload;
+ assert(sizeof(t_rtp_telephone_event) <= payload_size);
+
+ // RFC 2833 3.5, 3.6
+ dtmf_payload->set_event(_dtmf_current);
+ dtmf_payload->set_reserved(false);
+ dtmf_payload->set_volume(_user_config->get_dtmf_volume());
+
+ if (_dtmf_pause) {
+ // Trailing pause phase of a DTMF tone
+ // Repeat the last packet
+ dtmf_payload->set_end(true);
+
+ int pause_duration = timestamp - _dtmf_timestamp - _dtmf_duration +
+ _nsamples;
+ if (pause_duration / _nsamples * _audio_encoder->get_ptime() >=
+ _user_config->get_dtmf_pause())
+ {
+ // This is the last packet to be sent for the
+ // current DTMF tone.
+ _dtmf_stop = true;
+ log_file->write_header("t_rtp_event_dtmf_player::get_payload",
+ LOG_NORMAL);
+ log_file->write_raw("Audio rx line ");
+ log_file->write_raw(_audio_rx->get_line()->get_line_number()+1);
+ log_file->write_raw(": finish DTMF event - ");
+ log_file->write_raw(_dtmf_current);
+ log_file->write_endl();
+ log_file->write_footer();
+ }
+ } else {
+ // Play phase of a DTMF tone
+ // The duration counts from the start of the tone.
+ _dtmf_duration = timestamp - _dtmf_timestamp + _nsamples;
+
+ // Check if the tone must end
+ if (_dtmf_duration / _nsamples * _audio_encoder->get_ptime() >=
+ _user_config->get_dtmf_duration())
+ {
+ dtmf_payload->set_end(true);
+ _dtmf_pause = true;
+ } else {
+ dtmf_payload->set_end(false);
+ }
+ }
+
+ dtmf_payload->set_duration(_dtmf_duration);
+ rtp_timestamp = _dtmf_timestamp;
+ return sizeof(t_rtp_telephone_event);
+}
+
+
+/////////////////////////////////////////
+// class t_inband_dtmf_player
+/////////////////////////////////////////
+
+t_inband_dtmf_player::t_inband_dtmf_player(t_audio_rx *audio_rx,
+ t_audio_encoder *audio_encoder, t_user *user_config,
+ uint8 dtmf_tone, uint32 dtmf_timestamp,
+ uint16 nsamples) :
+ t_dtmf_player(audio_rx, audio_encoder, user_config, dtmf_tone, dtmf_timestamp,
+ nsamples),
+ _freq_gen(dtmf_tone, -(user_config->get_dtmf_volume()))
+{
+}
+
+uint16 t_inband_dtmf_player::get_payload(uint8 *payload,
+ uint16 payload_size, uint32 timestamp, uint32 &rtp_timestamp)
+{
+ int16 sample_buf[_nsamples];
+
+ if (_dtmf_pause) {
+ int pause_duration = timestamp - _dtmf_timestamp - _dtmf_duration +
+ _nsamples;
+
+ memset(sample_buf, 0, _nsamples * 2);
+
+ if (pause_duration / _nsamples * _audio_encoder->get_ptime() >=
+ _user_config->get_dtmf_pause())
+ {
+ // This is the last packet to be sent for the
+ // current DTMF tone.
+ _dtmf_stop = true;
+ log_file->write_header("t_inband_dtmf_player::get_payload", LOG_NORMAL);
+ log_file->write_raw("Audio rx line ");
+ log_file->write_raw(_audio_rx->get_line()->get_line_number()+1);
+ log_file->write_raw(": finish DTMF event - ");
+ log_file->write_raw(_dtmf_current);
+ log_file->write_endl();
+ log_file->write_footer();
+ }
+ } else {
+ // Timestamp and interval for _freq_gen must be in usec
+ uint32 ts_start = (timestamp - _dtmf_timestamp) * 1000000 /
+ _audio_encoder->get_sample_rate();
+ _freq_gen.get_samples(sample_buf, _nsamples, ts_start,
+ 1000000.0 / _audio_encoder->get_sample_rate());
+
+ // The duration counts from the start of the tone.
+ _dtmf_duration = timestamp - _dtmf_timestamp + _nsamples;
+
+ // Check if the tone must end
+ if (_dtmf_duration / _nsamples * _audio_encoder->get_ptime() >=
+ _user_config->get_dtmf_duration())
+ {
+ _dtmf_pause = true;
+ }
+ }
+
+ // Encode audio samples
+ bool silence;
+ rtp_timestamp = timestamp;
+ return _audio_encoder->encode(sample_buf, _nsamples, payload, payload_size, silence);
+}
diff --git a/src/audio/dtmf_player.h b/src/audio/dtmf_player.h
new file mode 100644
index 0000000..5fa88db
--- /dev/null
+++ b/src/audio/dtmf_player.h
@@ -0,0 +1,98 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+// Classes to generate RTP payloads for DTMF tones.
+
+#ifndef _DTMF_PLAYER_H
+#define _DTMF_PLAYER_H
+
+#include <cc++/config.h>
+#include "twinkle_config.h"
+#include "audio_encoder.h"
+#include "freq_gen.h"
+#include "user.h"
+
+// Forward declarations
+class t_audio_rx;
+
+// Abstract class defintion for DTMF player
+class t_dtmf_player {
+protected:
+ // Audio receiver owning the DTMF player.
+ t_audio_rx *_audio_rx;
+
+ t_user *_user_config;
+ t_audio_encoder *_audio_encoder;
+
+ // Settings for current DTMF tone
+ bool _dtmf_pause; // indicates if playing is in the pause phase
+ bool _dtmf_stop; // indicates if DTMF should be stopped
+ uint8 _dtmf_current; // Currently played DTMF tone
+ uint32 _dtmf_timestamp; // RTP timestamp (start of tone)
+ uint16 _dtmf_duration; // Duration of the tone currently played
+ uint16 _nsamples; // number of samples taken per packet
+
+public:
+ t_dtmf_player(t_audio_rx *audio_rx, t_audio_encoder *audio_encoder,
+ t_user *user_config, uint8 dtmf_tone, uint32 dtmf_timestamp,
+ uint16 nsamples);
+
+ virtual ~t_dtmf_player() {};
+
+ // Get payload for the DTMF tone
+ // rtp_timestamp will be set with the timestamp value to be put in the
+ // RTP header
+ // Returns the payload size
+ virtual uint16 get_payload(uint8 *payload, uint16 payload_size,
+ uint32 timestamp, uint32 &rtp_timestamp) = 0;
+
+ uint32 get_timestamp(void);
+
+ // Returns true when last payload has been delivered.
+ bool finished(void);
+};
+
+
+// DTMF player for RFC 2833 RTP telephone events
+class t_rtp_event_dtmf_player : public t_dtmf_player {
+public:
+ t_rtp_event_dtmf_player(t_audio_rx *audio_rx, t_audio_encoder *audio_encoder,
+ t_user *user_config, uint8 dtmf_tone, uint32 dtmf_timestamp,
+ uint16 nsamples);
+
+ virtual uint16 get_payload(uint8 *payload, uint16 payload_size,
+ uint32 timestamp, uint32 &rtp_timestamp);
+};
+
+
+// DTMF player for inband tones
+class t_inband_dtmf_player : public t_dtmf_player {
+private:
+ // Frequency generator to generate the inband tones.
+ t_freq_gen _freq_gen;
+
+public:
+ t_inband_dtmf_player(t_audio_rx *audio_rx, t_audio_encoder *audio_encoder,
+ t_user *user_config, uint8 dtmf_tone, uint32 dtmf_timestamp,
+ uint16 nsamples);
+
+ virtual uint16 get_payload(uint8 *payload, uint16 payload_size,
+ uint32 timestamp, uint32 &rtp_timestamp);
+};
+
+#endif
diff --git a/src/audio/freq_gen.cpp b/src/audio/freq_gen.cpp
new file mode 100644
index 0000000..5aad228
--- /dev/null
+++ b/src/audio/freq_gen.cpp
@@ -0,0 +1,134 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "freq_gen.h"
+#include <iostream>
+#include <cassert>
+#include <cmath>
+#include "rtp_telephone_event.h"
+
+#define PI 3.141592653589793
+
+t_freq_gen::t_freq_gen(vector<uint16> frequencies, int8 db_level) {
+ assert(frequencies.size() > 0);
+ assert(db_level <= 0);
+
+ _frequencies = frequencies;
+
+ // dB = 20 * log(amplitude / 32768)
+ // 32767 is used below as +32768 does not fit in 16 bits
+ _amplitude = int16(pow(10.0, db_level / 20.0) * 32767 / _frequencies.size());
+}
+
+t_freq_gen::t_freq_gen(uint8 dtmf, int8 db_level) : _frequencies(2)
+{
+ assert(db_level <= 0);
+
+ switch (dtmf) {
+ case TEL_EV_DTMF_1:
+ _frequencies[0] = 697;
+ _frequencies[1] = 1209;
+ break;
+ case TEL_EV_DTMF_2:
+ _frequencies[0] = 697;
+ _frequencies[1] = 1336;
+ break;
+ case TEL_EV_DTMF_3:
+ _frequencies[0] = 697;
+ _frequencies[1] = 1477;
+ break;
+ case TEL_EV_DTMF_A:
+ _frequencies[0] = 697;
+ _frequencies[1] = 1633;
+ break;
+ case TEL_EV_DTMF_4:
+ _frequencies[0] = 770;
+ _frequencies[1] = 1209;
+ break;
+ case TEL_EV_DTMF_5:
+ _frequencies[0] = 770;
+ _frequencies[1] = 1336;
+ break;
+ case TEL_EV_DTMF_6:
+ _frequencies[0] = 770;
+ _frequencies[1] = 1477;
+ break;
+ case TEL_EV_DTMF_B:
+ _frequencies[0] = 770;
+ _frequencies[1] = 1633;
+ break;
+ case TEL_EV_DTMF_7:
+ _frequencies[0] = 852;
+ _frequencies[1] = 1209;
+ break;
+ case TEL_EV_DTMF_8:
+ _frequencies[0] = 852;
+ _frequencies[1] = 1336;
+ break;
+ case TEL_EV_DTMF_9:
+ _frequencies[0] = 852;
+ _frequencies[1] = 1477;
+ break;
+ case TEL_EV_DTMF_C:
+ _frequencies[0] = 852;
+ _frequencies[1] = 1633;
+ break;
+ case TEL_EV_DTMF_STAR:
+ _frequencies[0] = 941;
+ _frequencies[1] = 1209;
+ break;
+ case TEL_EV_DTMF_0:
+ _frequencies[0] = 941;
+ _frequencies[1] = 1336;
+ break;
+ case TEL_EV_DTMF_POUND:
+ _frequencies[0] = 941;
+ _frequencies[1] = 1477;
+ break;
+ case TEL_EV_DTMF_D:
+ _frequencies[0] = 941;
+ _frequencies[1] = 1633;
+ break;
+ default:
+ assert(false);
+ }
+
+ // dB = 20 * log(amplitude / 32768)
+ // 32767 is used below as +32768 does not fit in 16 bits
+ _amplitude = int16(pow(10.0, db_level / 20.0) * 32767 / _frequencies.size());
+}
+
+int16 t_freq_gen::get_sample(uint32 ts_usec) const {
+ double freq_sum = 0.0;
+
+ for (vector<uint16>::const_iterator f = _frequencies.begin();
+ f != _frequencies.end(); f++)
+ {
+ freq_sum += sin(*f * 2.0 * PI * (double)ts_usec / 1000000.0);
+ }
+
+ return (int16)(_amplitude * freq_sum);
+}
+
+void t_freq_gen::get_samples(int16 *sample_buf, uint16 buf_len,
+ uint32 ts_start, double interval) const
+{
+ for (uint16 i = 0; i < buf_len; i++) {
+ sample_buf[i] = get_sample(uint32(ts_start + interval * i));
+ }
+}
diff --git a/src/audio/freq_gen.h b/src/audio/freq_gen.h
new file mode 100644
index 0000000..5659739
--- /dev/null
+++ b/src/audio/freq_gen.h
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+// Frequency generator
+//
+// This file contains definitions of the frequency generator.
+// The frequency generator generates tones build from a list of
+// frequencies.
+
+#ifndef _FREQ_GEN_H
+#define _FREQ_GEN_H
+
+#include <vector>
+#include <cc++/config.h>
+
+using namespace std;
+
+class t_freq_gen {
+private:
+ vector<uint16> _frequencies;
+ int16 _amplitude;
+
+public:
+ t_freq_gen(vector<uint16> frequencies, int8 db_level);
+ t_freq_gen(uint8 dtmf, int8 db_level);
+
+ // Get sound sample on a particular timestamp in us.
+ int16 get_sample(uint32 ts_usec) const;
+ void get_samples(int16 *sample_buf, uint16 buf_len,
+ uint32 ts_start, double interval) const;
+};
+
+#endif
diff --git a/src/audio/g711.cpp b/src/audio/g711.cpp
new file mode 100644
index 0000000..f02ffe3
--- /dev/null
+++ b/src/audio/g711.cpp
@@ -0,0 +1,311 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+
+/*
+ * December 30, 1994:
+ * Functions linear2alaw, linear2ulaw have been updated to correctly
+ * convert unquantized 16 bit values.
+ * Tables for direct u- to A-law and A- to u-law conversions have been
+ * corrected.
+ * Borge Lindberg, Center for PersonKommunikation, Aalborg University.
+ * bli@cpk.auc.dk
+ *
+ */
+
+#include "g711.h"
+
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+static short seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
+ 0x1FF, 0x3FF, 0x7FF, 0xFFF};
+static short seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
+ 0x3FF, 0x7FF, 0xFFF, 0x1FFF};
+
+/* copy from CCITT G.711 specifications */
+unsigned char _u2a[128] = { /* u- to A-law conversions */
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+/* corrected:
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ should be: */
+ 80, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128};
+
+unsigned char _a2u[128] = { /* A- to u-law conversions */
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+/* corrected:
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ should be: */
+ 73, 74, 75, 76, 77, 78, 79, 80,
+
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
+
+static short
+search(
+ short val,
+ short *table,
+ short size)
+{
+ short i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= *table++)
+ return (i);
+ }
+ return (size);
+}
+
+/*
+ * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
+ *
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char
+linear2alaw(
+ short pcm_val) /* 2's complement (16-bit range) */
+{
+ short mask;
+ short seg;
+ unsigned char aval;
+
+ pcm_val = pcm_val >> 3;
+
+ if (pcm_val >= 0) {
+ mask = 0xD5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ pcm_val = -pcm_val - 1;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_aend, 8);
+
+ /* Combine the sign, segment, and quantization bits. */
+
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ aval = (unsigned char) seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (pcm_val >> 1) & QUANT_MASK;
+ else
+ aval |= (pcm_val >> seg) & QUANT_MASK;
+ return (aval ^ mask);
+ }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+short
+alaw2linear(
+ unsigned char a_val)
+{
+ short t;
+ short seg;
+
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+#define BIAS (0x84) /* Bias for linear code. */
+#define CLIP 8159
+
+/*
+ * linear2ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char
+linear2ulaw(
+ short pcm_val) /* 2's complement (16-bit range) */
+{
+ short mask;
+ short seg;
+ unsigned char uval;
+
+ /* Get the sign and the magnitude of the value. */
+ pcm_val = pcm_val >> 2;
+ if (pcm_val < 0) {
+ pcm_val = -pcm_val;
+ mask = 0x7F;
+ } else {
+ mask = 0xFF;
+ }
+ if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */
+ pcm_val += (BIAS >> 2);
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_uend, 8);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
+ return (uval ^ mask);
+ }
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+short
+ulaw2linear(
+ unsigned char u_val)
+{
+ short t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/* A-law to u-law conversion */
+unsigned char
+alaw2ulaw(
+ unsigned char aval)
+{
+ aval &= 0xff;
+ return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+unsigned char
+ulaw2alaw(
+ unsigned char uval)
+{
+ uval &= 0xff;
+ return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
diff --git a/src/audio/g711.h b/src/audio/g711.h
new file mode 100644
index 0000000..ed2f007
--- /dev/null
+++ b/src/audio/g711.h
@@ -0,0 +1,18 @@
+#ifndef _G711_H
+#define _G711_H
+
+// The linear PCM codes are signed 16 bit values
+
+// G.711 A-law
+unsigned char linear2alaw(short pcm_val);
+short alaw2linear(unsigned char a_val);
+
+// G.711 u-law
+unsigned char linear2ulaw(short pcm_val);
+short ulaw2linear(unsigned char u_val);
+
+// A-law <-> u-law conversions
+unsigned char alaw2ulaw(unsigned char aval);
+unsigned char ulaw2alaw(unsigned char uval);
+
+#endif
diff --git a/src/audio/g721.cpp b/src/audio/g721.cpp
new file mode 100644
index 0000000..a08dc79
--- /dev/null
+++ b/src/audio/g721.cpp
@@ -0,0 +1,174 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * g721.c
+ *
+ * Description:
+ *
+ * g721_encoder(), g721_decoder()
+ *
+ * These routines comprise an implementation of the CCITT G.721 ADPCM
+ * coding algorithm. Essentially, this implementation is identical to
+ * the bit level description except for a few deviations which
+ * take advantage of work station attributes, such as hardware 2's
+ * complement arithmetic and large memory. Specifically, certain time
+ * consuming operations such as multiplications are replaced
+ * with lookup tables and software 2's complement operations are
+ * replaced with hardware 2's complement.
+ *
+ * The deviation from the bit level specification (lookup tables)
+ * preserves the bit level performance specifications.
+ *
+ * As outlined in the G.721 Recommendation, the algorithm is broken
+ * down into modules. Each section of code below is preceded by
+ * the name of the module which it is implementing.
+ *
+ */
+#include "g72x.h"
+#include "g711.h"
+
+static short qtab_721[7] = {-124, 80, 178, 246, 300, 349, 400};
+/*
+ * Maps G.721 code word to reconstructed scale factor normalized log
+ * magnitude values.
+ */
+static short _dqlntab[16] = {-2048, 4, 135, 213, 273, 323, 373, 425,
+ 425, 373, 323, 273, 213, 135, 4, -2048};
+
+/* Maps G.721 code word to log of scale factor multiplier. */
+static short _witab[16] = {-12, 18, 41, 64, 112, 198, 355, 1122,
+ 1122, 355, 198, 112, 64, 41, 18, -12};
+/*
+ * Maps G.721 code words to a set of values whose long and short
+ * term averages are computed and then compared to give an indication
+ * how stationary (steady state) the signal is.
+ */
+static short _fitab[16] = {0, 0, 0, 0x200, 0x200, 0x200, 0x600, 0xE00,
+ 0xE00, 0x600, 0x200, 0x200, 0x200, 0, 0, 0};
+
+/*
+ * g721_encoder()
+ *
+ * Encodes the input vale of linear PCM, A-law or u-law data sl and returns
+ * the resulting code. -1 is returned for unknown input coding value.
+ */
+int
+g721_encoder(
+ int sl,
+ int in_coding,
+ struct g72x_state *state_ptr)
+{
+ short sezi, se, sez; /* ACCUM */
+ short d; /* SUBTA */
+ short sr; /* ADDB */
+ short y; /* MIX */
+ short dqsez; /* ADDC */
+ short dq, i;
+
+ switch (in_coding) { /* linearize input sample to 14-bit PCM */
+ case AUDIO_ENCODING_ALAW:
+ sl = alaw2linear(sl) >> 2;
+ break;
+ case AUDIO_ENCODING_ULAW:
+ sl = ulaw2linear(sl) >> 2;
+ break;
+ case AUDIO_ENCODING_LINEAR:
+ sl >>= 2; /* 14-bit dynamic range */
+ break;
+ default:
+ return (-1);
+ }
+
+ sezi = predictor_zero(state_ptr);
+ sez = sezi >> 1;
+ se = (sezi + predictor_pole(state_ptr)) >> 1; /* estimated signal */
+
+ d = sl - se; /* estimation difference */
+
+ /* quantize the prediction difference */
+ y = step_size(state_ptr); /* quantizer step size */
+ i = quantize(d, y, qtab_721, 7); /* i = ADPCM code */
+
+ dq = reconstruct(i & 8, _dqlntab[i], y); /* quantized est diff */
+
+ sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconst. signal */
+
+ dqsez = sr + sez - se; /* pole prediction diff. */
+
+ update(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr);
+
+ return (i);
+}
+
+/*
+ * g721_decoder()
+ *
+ * Description:
+ *
+ * Decodes a 4-bit code of G.721 encoded data of i and
+ * returns the resulting linear PCM, A-law or u-law value.
+ * return -1 for unknown out_coding value.
+ */
+int
+g721_decoder(
+ int i,
+ int out_coding,
+ struct g72x_state *state_ptr)
+{
+ short sezi, sei, sez, se; /* ACCUM */
+ short y; /* MIX */
+ short sr; /* ADDB */
+ short dq;
+ short dqsez;
+
+ i &= 0x0f; /* mask to get proper bits */
+ sezi = predictor_zero(state_ptr);
+ sez = sezi >> 1;
+ sei = sezi + predictor_pole(state_ptr);
+ se = sei >> 1; /* se = estimated signal */
+
+ y = step_size(state_ptr); /* dynamic quantizer step size */
+
+ dq = reconstruct(i & 0x08, _dqlntab[i], y); /* quantized diff. */
+
+ sr = (dq < 0) ? (se - (dq & 0x3FFF)) : se + dq; /* reconst. signal */
+
+ dqsez = sr - se + sez; /* pole prediction diff. */
+
+ update(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr);
+
+ switch (out_coding) {
+ case AUDIO_ENCODING_ALAW:
+ return (tandem_adjust_alaw(sr, se, y, i, 8, qtab_721));
+ case AUDIO_ENCODING_ULAW:
+ return (tandem_adjust_ulaw(sr, se, y, i, 8, qtab_721));
+ case AUDIO_ENCODING_LINEAR:
+ return (sr << 2); /* sr was 14-bit dynamic range */
+ default:
+ return (-1);
+ }
+}
diff --git a/src/audio/g723_16.cpp b/src/audio/g723_16.cpp
new file mode 100644
index 0000000..eae6aa1
--- /dev/null
+++ b/src/audio/g723_16.cpp
@@ -0,0 +1,181 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/* 16kbps version created, used 24kbps code and changing as little as possible.
+ * G.726 specs are available from ITU's gopher or WWW site (http://www.itu.ch)
+ * If any errors are found, please contact me at mrand@tamu.edu
+ * -Marc Randolph
+ */
+
+/*
+ * g723_16.c
+ *
+ * Description:
+ *
+ * g723_16_encoder(), g723_16_decoder()
+ *
+ * These routines comprise an implementation of the CCITT G.726 16 Kbps
+ * ADPCM coding algorithm. Essentially, this implementation is identical to
+ * the bit level description except for a few deviations which take advantage
+ * of workstation attributes, such as hardware 2's complement arithmetic.
+ *
+ */
+#include "g72x.h"
+#include "g711.h"
+
+/*
+ * Maps G.723_16 code word to reconstructed scale factor normalized log
+ * magnitude values. Comes from Table 11/G.726
+ */
+static short _dqlntab[4] = { 116, 365, 365, 116};
+
+/* Maps G.723_16 code word to log of scale factor multiplier.
+ *
+ * _witab[4] is actually {-22 , 439, 439, -22}, but FILTD wants it
+ * as WI << 5 (multiplied by 32), so we'll do that here
+ */
+static short _witab[4] = {-704, 14048, 14048, -704};
+
+/*
+ * Maps G.723_16 code words to a set of values whose long and short
+ * term averages are computed and then compared to give an indication
+ * how stationary (steady state) the signal is.
+ */
+
+/* Comes from FUNCTF */
+static short _fitab[4] = {0, 0xE00, 0xE00, 0};
+
+/* Comes from quantizer decision level tables (Table 7/G.726)
+ */
+static short qtab_723_16[1] = {261};
+
+
+/*
+ * g723_16_encoder()
+ *
+ * Encodes a linear PCM, A-law or u-law input sample and returns its 2-bit code.
+ * Returns -1 if invalid input coding value.
+ */
+int
+g723_16_encoder(
+ int sl,
+ int in_coding,
+ struct g72x_state *state_ptr)
+{
+ short sei, sezi, se, sez; /* ACCUM */
+ short d; /* SUBTA */
+ short y; /* MIX */
+ short sr; /* ADDB */
+ short dqsez; /* ADDC */
+ short dq, i;
+
+ switch (in_coding) { /* linearize input sample to 14-bit PCM */
+ case AUDIO_ENCODING_ALAW:
+ sl = alaw2linear(sl) >> 2;
+ break;
+ case AUDIO_ENCODING_ULAW:
+ sl = ulaw2linear(sl) >> 2;
+ break;
+ case AUDIO_ENCODING_LINEAR:
+ sl >>= 2; /* sl of 14-bit dynamic range */
+ break;
+ default:
+ return (-1);
+ }
+
+ sezi = predictor_zero(state_ptr);
+ sez = sezi >> 1;
+ sei = sezi + predictor_pole(state_ptr);
+ se = sei >> 1; /* se = estimated signal */
+
+ d = sl - se; /* d = estimation diff. */
+
+ /* quantize prediction difference d */
+ y = step_size(state_ptr); /* quantizer step size */
+ i = quantize(d, y, qtab_723_16, 1); /* i = ADPCM code */
+
+ /* Since quantize() only produces a three level output
+ * (1, 2, or 3), we must create the fourth one on our own
+ */
+ if (i == 3) /* i code for the zero region */
+ if ((d & 0x8000) == 0) /* If d > 0, i=3 isn't right... */
+ i = 0;
+
+ dq = reconstruct(i & 2, _dqlntab[i], y); /* quantized diff. */
+
+ sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconstructed signal */
+
+ dqsez = sr + sez - se; /* pole prediction diff. */
+
+ update(2, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr);
+
+ return (i);
+}
+
+/*
+ * g723_16_decoder()
+ *
+ * Decodes a 2-bit CCITT G.723_16 ADPCM code and returns
+ * the resulting 16-bit linear PCM, A-law or u-law sample value.
+ * -1 is returned if the output coding is unknown.
+ */
+int
+g723_16_decoder(
+ int i,
+ int out_coding,
+ struct g72x_state *state_ptr)
+{
+ short sezi, sei, sez, se; /* ACCUM */
+ short y; /* MIX */
+ short sr; /* ADDB */
+ short dq;
+ short dqsez;
+
+ i &= 0x03; /* mask to get proper bits */
+ sezi = predictor_zero(state_ptr);
+ sez = sezi >> 1;
+ sei = sezi + predictor_pole(state_ptr);
+ se = sei >> 1; /* se = estimated signal */
+
+ y = step_size(state_ptr); /* adaptive quantizer step size */
+ dq = reconstruct(i & 0x02, _dqlntab[i], y); /* unquantize pred diff */
+
+ sr = (dq < 0) ? (se - (dq & 0x3FFF)) : (se + dq); /* reconst. signal */
+
+ dqsez = sr - se + sez; /* pole prediction diff. */
+
+ update(2, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr);
+
+ switch (out_coding) {
+ case AUDIO_ENCODING_ALAW:
+ return (tandem_adjust_alaw(sr, se, y, i, 2, qtab_723_16));
+ case AUDIO_ENCODING_ULAW:
+ return (tandem_adjust_ulaw(sr, se, y, i, 2, qtab_723_16));
+ case AUDIO_ENCODING_LINEAR:
+ return (sr << 2); /* sr was of 14-bit dynamic range */
+ default:
+ return (-1);
+ }
+}
diff --git a/src/audio/g723_24.cpp b/src/audio/g723_24.cpp
new file mode 100644
index 0000000..d70038a
--- /dev/null
+++ b/src/audio/g723_24.cpp
@@ -0,0 +1,159 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * g723_24.c
+ *
+ * Description:
+ *
+ * g723_24_encoder(), g723_24_decoder()
+ *
+ * These routines comprise an implementation of the CCITT G.723 24 Kbps
+ * ADPCM coding algorithm. Essentially, this implementation is identical to
+ * the bit level description except for a few deviations which take advantage
+ * of workstation attributes, such as hardware 2's complement arithmetic.
+ *
+ */
+#include "g72x.h"
+#include "g711.h"
+
+/*
+ * Maps G.723_24 code word to reconstructed scale factor normalized log
+ * magnitude values.
+ */
+static short _dqlntab[8] = {-2048, 135, 273, 373, 373, 273, 135, -2048};
+
+/* Maps G.723_24 code word to log of scale factor multiplier. */
+static short _witab[8] = {-128, 960, 4384, 18624, 18624, 4384, 960, -128};
+
+/*
+ * Maps G.723_24 code words to a set of values whose long and short
+ * term averages are computed and then compared to give an indication
+ * how stationary (steady state) the signal is.
+ */
+static short _fitab[8] = {0, 0x200, 0x400, 0xE00, 0xE00, 0x400, 0x200, 0};
+
+static short qtab_723_24[3] = {8, 218, 331};
+
+/*
+ * g723_24_encoder()
+ *
+ * Encodes a linear PCM, A-law or u-law input sample and returns its 3-bit code.
+ * Returns -1 if invalid input coding value.
+ */
+int
+g723_24_encoder(
+ int sl,
+ int in_coding,
+ struct g72x_state *state_ptr)
+{
+ short sei, sezi, se, sez; /* ACCUM */
+ short d; /* SUBTA */
+ short y; /* MIX */
+ short sr; /* ADDB */
+ short dqsez; /* ADDC */
+ short dq, i;
+
+ switch (in_coding) { /* linearize input sample to 14-bit PCM */
+ case AUDIO_ENCODING_ALAW:
+ sl = alaw2linear(sl) >> 2;
+ break;
+ case AUDIO_ENCODING_ULAW:
+ sl = ulaw2linear(sl) >> 2;
+ break;
+ case AUDIO_ENCODING_LINEAR:
+ sl >>= 2; /* sl of 14-bit dynamic range */
+ break;
+ default:
+ return (-1);
+ }
+
+ sezi = predictor_zero(state_ptr);
+ sez = sezi >> 1;
+ sei = sezi + predictor_pole(state_ptr);
+ se = sei >> 1; /* se = estimated signal */
+
+ d = sl - se; /* d = estimation diff. */
+
+ /* quantize prediction difference d */
+ y = step_size(state_ptr); /* quantizer step size */
+ i = quantize(d, y, qtab_723_24, 3); /* i = ADPCM code */
+ dq = reconstruct(i & 4, _dqlntab[i], y); /* quantized diff. */
+
+ sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconstructed signal */
+
+ dqsez = sr + sez - se; /* pole prediction diff. */
+
+ update(3, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr);
+
+ return (i);
+}
+
+/*
+ * g723_24_decoder()
+ *
+ * Decodes a 3-bit CCITT G.723_24 ADPCM code and returns
+ * the resulting 16-bit linear PCM, A-law or u-law sample value.
+ * -1 is returned if the output coding is unknown.
+ */
+int
+g723_24_decoder(
+ int i,
+ int out_coding,
+ struct g72x_state *state_ptr)
+{
+ short sezi, sei, sez, se; /* ACCUM */
+ short y; /* MIX */
+ short sr; /* ADDB */
+ short dq;
+ short dqsez;
+
+ i &= 0x07; /* mask to get proper bits */
+ sezi = predictor_zero(state_ptr);
+ sez = sezi >> 1;
+ sei = sezi + predictor_pole(state_ptr);
+ se = sei >> 1; /* se = estimated signal */
+
+ y = step_size(state_ptr); /* adaptive quantizer step size */
+ dq = reconstruct(i & 0x04, _dqlntab[i], y); /* unquantize pred diff */
+
+ sr = (dq < 0) ? (se - (dq & 0x3FFF)) : (se + dq); /* reconst. signal */
+
+ dqsez = sr - se + sez; /* pole prediction diff. */
+
+ update(3, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr);
+
+ switch (out_coding) {
+ case AUDIO_ENCODING_ALAW:
+ return (tandem_adjust_alaw(sr, se, y, i, 4, qtab_723_24));
+ case AUDIO_ENCODING_ULAW:
+ return (tandem_adjust_ulaw(sr, se, y, i, 4, qtab_723_24));
+ case AUDIO_ENCODING_LINEAR:
+ return (sr << 2); /* sr was of 14-bit dynamic range */
+ default:
+ return (-1);
+ }
+}
diff --git a/src/audio/g723_40.cpp b/src/audio/g723_40.cpp
new file mode 100644
index 0000000..d6ebd8a
--- /dev/null
+++ b/src/audio/g723_40.cpp
@@ -0,0 +1,179 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * g723_40.c
+ *
+ * Description:
+ *
+ * g723_40_encoder(), g723_40_decoder()
+ *
+ * These routines comprise an implementation of the CCITT G.723 40Kbps
+ * ADPCM coding algorithm. Essentially, this implementation is identical to
+ * the bit level description except for a few deviations which
+ * take advantage of workstation attributes, such as hardware 2's
+ * complement arithmetic.
+ *
+ * The deviation from the bit level specification (lookup tables),
+ * preserves the bit level performance specifications.
+ *
+ * As outlined in the G.723 Recommendation, the algorithm is broken
+ * down into modules. Each section of code below is preceded by
+ * the name of the module which it is implementing.
+ *
+ */
+#include "g72x.h"
+#include "g711.h"
+
+/*
+ * Maps G.723_40 code word to ructeconstructed scale factor normalized log
+ * magnitude values.
+ */
+static short _dqlntab[32] = {-2048, -66, 28, 104, 169, 224, 274, 318,
+ 358, 395, 429, 459, 488, 514, 539, 566,
+ 566, 539, 514, 488, 459, 429, 395, 358,
+ 318, 274, 224, 169, 104, 28, -66, -2048};
+
+/* Maps G.723_40 code word to log of scale factor multiplier. */
+static short _witab[32] = {448, 448, 768, 1248, 1280, 1312, 1856, 3200,
+ 4512, 5728, 7008, 8960, 11456, 14080, 16928, 22272,
+ 22272, 16928, 14080, 11456, 8960, 7008, 5728, 4512,
+ 3200, 1856, 1312, 1280, 1248, 768, 448, 448};
+
+/*
+ * Maps G.723_40 code words to a set of values whose long and short
+ * term averages are computed and then compared to give an indication
+ * how stationary (steady state) the signal is.
+ */
+static short _fitab[32] = {0, 0, 0, 0, 0, 0x200, 0x200, 0x200,
+ 0x200, 0x200, 0x400, 0x600, 0x800, 0xA00, 0xC00, 0xC00,
+ 0xC00, 0xC00, 0xA00, 0x800, 0x600, 0x400, 0x200, 0x200,
+ 0x200, 0x200, 0x200, 0, 0, 0, 0, 0};
+
+static short qtab_723_40[15] = {-122, -16, 68, 139, 198, 250, 298, 339,
+ 378, 413, 445, 475, 502, 528, 553};
+
+/*
+ * g723_40_encoder()
+ *
+ * Encodes a 16-bit linear PCM, A-law or u-law input sample and retuens
+ * the resulting 5-bit CCITT G.723 40Kbps code.
+ * Returns -1 if the input coding value is invalid.
+ */
+int
+g723_40_encoder(
+ int sl,
+ int in_coding,
+ struct g72x_state *state_ptr)
+{
+ short sei, sezi, se, sez; /* ACCUM */
+ short d; /* SUBTA */
+ short y; /* MIX */
+ short sr; /* ADDB */
+ short dqsez; /* ADDC */
+ short dq, i;
+
+ switch (in_coding) { /* linearize input sample to 14-bit PCM */
+ case AUDIO_ENCODING_ALAW:
+ sl = alaw2linear(sl) >> 2;
+ break;
+ case AUDIO_ENCODING_ULAW:
+ sl = ulaw2linear(sl) >> 2;
+ break;
+ case AUDIO_ENCODING_LINEAR:
+ sl >>= 2; /* sl of 14-bit dynamic range */
+ break;
+ default:
+ return (-1);
+ }
+
+ sezi = predictor_zero(state_ptr);
+ sez = sezi >> 1;
+ sei = sezi + predictor_pole(state_ptr);
+ se = sei >> 1; /* se = estimated signal */
+
+ d = sl - se; /* d = estimation difference */
+
+ /* quantize prediction difference */
+ y = step_size(state_ptr); /* adaptive quantizer step size */
+ i = quantize(d, y, qtab_723_40, 15); /* i = ADPCM code */
+
+ dq = reconstruct(i & 0x10, _dqlntab[i], y); /* quantized diff */
+
+ sr = (dq < 0) ? se - (dq & 0x7FFF) : se + dq; /* reconstructed signal */
+
+ dqsez = sr + sez - se; /* dqsez = pole prediction diff. */
+
+ update(5, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr);
+
+ return (i);
+}
+
+/*
+ * g723_40_decoder()
+ *
+ * Decodes a 5-bit CCITT G.723 40Kbps code and returns
+ * the resulting 16-bit linear PCM, A-law or u-law sample value.
+ * -1 is returned if the output coding is unknown.
+ */
+int
+g723_40_decoder(
+ int i,
+ int out_coding,
+ struct g72x_state *state_ptr)
+{
+ short sezi, sei, sez, se; /* ACCUM */
+ short y; /* MIX */
+ short sr; /* ADDB */
+ short dq;
+ short dqsez;
+
+ i &= 0x1f; /* mask to get proper bits */
+ sezi = predictor_zero(state_ptr);
+ sez = sezi >> 1;
+ sei = sezi + predictor_pole(state_ptr);
+ se = sei >> 1; /* se = estimated signal */
+
+ y = step_size(state_ptr); /* adaptive quantizer step size */
+ dq = reconstruct(i & 0x10, _dqlntab[i], y); /* estimation diff. */
+
+ sr = (dq < 0) ? (se - (dq & 0x7FFF)) : (se + dq); /* reconst. signal */
+
+ dqsez = sr - se + sez; /* pole prediction diff. */
+
+ update(5, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr);
+
+ switch (out_coding) {
+ case AUDIO_ENCODING_ALAW:
+ return (tandem_adjust_alaw(sr, se, y, i, 0x10, qtab_723_40));
+ case AUDIO_ENCODING_ULAW:
+ return (tandem_adjust_ulaw(sr, se, y, i, 0x10, qtab_723_40));
+ case AUDIO_ENCODING_LINEAR:
+ return (sr << 2); /* sr was of 14-bit dynamic range */
+ default:
+ return (-1);
+ }
+}
diff --git a/src/audio/g72x.cpp b/src/audio/g72x.cpp
new file mode 100644
index 0000000..40d1430
--- /dev/null
+++ b/src/audio/g72x.cpp
@@ -0,0 +1,565 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * g72x.c
+ *
+ * Common routines for G.721 and G.723 conversions.
+ */
+
+#include <cstdlib>
+#include "g72x.h"
+#include "g711.h"
+
+static short power2[15] = {1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80,
+ 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000};
+
+/*
+ * quan()
+ *
+ * quantizes the input val against the table of size short integers.
+ * It returns i if table[i - 1] <= val < table[i].
+ *
+ * Using linear search for simple coding.
+ */
+static int
+quan(
+ int val,
+ short *table,
+ int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (val < *table++)
+ break;
+ return (i);
+}
+
+/*
+ * fmult()
+ *
+ * returns the integer product of the 14-bit integer "an" and
+ * "floating point" representation (4-bit exponent, 6-bit mantessa) "srn".
+ */
+static int
+fmult(
+ int an,
+ int srn)
+{
+ short anmag, anexp, anmant;
+ short wanexp, wanmant;
+ short retval;
+
+ anmag = (an > 0) ? an : ((-an) & 0x1FFF);
+ anexp = quan(anmag, power2, 15) - 6;
+ anmant = (anmag == 0) ? 32 :
+ (anexp >= 0) ? anmag >> anexp : anmag << -anexp;
+ wanexp = anexp + ((srn >> 6) & 0xF) - 13;
+
+ wanmant = (anmant * (srn & 077) + 0x30) >> 4;
+ retval = (wanexp >= 0) ? ((wanmant << wanexp) & 0x7FFF) :
+ (wanmant >> -wanexp);
+
+ return (((an ^ srn) < 0) ? -retval : retval);
+}
+
+/*
+ * g72x_init_state()
+ *
+ * This routine initializes and/or resets the g72x_state structure
+ * pointed to by 'state_ptr'.
+ * All the initial state values are specified in the CCITT G.721 document.
+ */
+void
+g72x_init_state(
+ struct g72x_state *state_ptr)
+{
+ int cnta;
+
+ state_ptr->yl = 34816;
+ state_ptr->yu = 544;
+ state_ptr->dms = 0;
+ state_ptr->dml = 0;
+ state_ptr->ap = 0;
+ for (cnta = 0; cnta < 2; cnta++) {
+ state_ptr->a[cnta] = 0;
+ state_ptr->pk[cnta] = 0;
+ state_ptr->sr[cnta] = 32;
+ }
+ for (cnta = 0; cnta < 6; cnta++) {
+ state_ptr->b[cnta] = 0;
+ state_ptr->dq[cnta] = 32;
+ }
+ state_ptr->td = 0;
+}
+
+/*
+ * predictor_zero()
+ *
+ * computes the estimated signal from 6-zero predictor.
+ *
+ */
+int
+predictor_zero(
+ struct g72x_state *state_ptr)
+{
+ int i;
+ int sezi;
+
+ sezi = fmult(state_ptr->b[0] >> 2, state_ptr->dq[0]);
+ for (i = 1; i < 6; i++) /* ACCUM */
+ sezi += fmult(state_ptr->b[i] >> 2, state_ptr->dq[i]);
+ return (sezi);
+}
+/*
+ * predictor_pole()
+ *
+ * computes the estimated signal from 2-pole predictor.
+ *
+ */
+int
+predictor_pole(
+ struct g72x_state *state_ptr)
+{
+ return (fmult(state_ptr->a[1] >> 2, state_ptr->sr[1]) +
+ fmult(state_ptr->a[0] >> 2, state_ptr->sr[0]));
+}
+/*
+ * step_size()
+ *
+ * computes the quantization step size of the adaptive quantizer.
+ *
+ */
+int
+step_size(
+ struct g72x_state *state_ptr)
+{
+ int y;
+ int dif;
+ int al;
+
+ if (state_ptr->ap >= 256)
+ return (state_ptr->yu);
+ else {
+ y = state_ptr->yl >> 6;
+ dif = state_ptr->yu - y;
+ al = state_ptr->ap >> 2;
+ if (dif > 0)
+ y += (dif * al) >> 6;
+ else if (dif < 0)
+ y += (dif * al + 0x3F) >> 6;
+ return (y);
+ }
+}
+
+/*
+ * quantize()
+ *
+ * Given a raw sample, 'd', of the difference signal and a
+ * quantization step size scale factor, 'y', this routine returns the
+ * ADPCM codeword to which that sample gets quantized. The step
+ * size scale factor division operation is done in the log base 2 domain
+ * as a subtraction.
+ */
+int
+quantize(
+ int d, /* Raw difference signal sample */
+ int y, /* Step size multiplier */
+ short *table, /* quantization table */
+ int size) /* table size of short integers */
+{
+ short dqm; /* Magnitude of 'd' */
+ short exp; /* Integer part of base 2 log of 'd' */
+ short mant; /* Fractional part of base 2 log */
+ short dl; /* Log of magnitude of 'd' */
+ short dln; /* Step size scale factor normalized log */
+ int i;
+
+ /*
+ * LOG
+ *
+ * Compute base 2 log of 'd', and store in 'dl'.
+ */
+ dqm = abs(d);
+ exp = quan(dqm >> 1, power2, 15);
+ mant = ((dqm << 7) >> exp) & 0x7F; /* Fractional portion. */
+ dl = (exp << 7) + mant;
+
+ /*
+ * SUBTB
+ *
+ * "Divide" by step size multiplier.
+ */
+ dln = dl - (y >> 2);
+
+ /*
+ * QUAN
+ *
+ * Obtain codword i for 'd'.
+ */
+ i = quan(dln, table, size);
+ if (d < 0) /* take 1's complement of i */
+ return ((size << 1) + 1 - i);
+ else if (i == 0) /* take 1's complement of 0 */
+ return ((size << 1) + 1); /* new in 1988 */
+ else
+ return (i);
+}
+/*
+ * reconstruct()
+ *
+ * Returns reconstructed difference signal 'dq' obtained from
+ * codeword 'i' and quantization step size scale factor 'y'.
+ * Multiplication is performed in log base 2 domain as addition.
+ */
+int
+reconstruct(
+ int sign, /* 0 for non-negative value */
+ int dqln, /* G.72x codeword */
+ int y) /* Step size multiplier */
+{
+ short dql; /* Log of 'dq' magnitude */
+ short dex; /* Integer part of log */
+ short dqt;
+ short dq; /* Reconstructed difference signal sample */
+
+ dql = dqln + (y >> 2); /* ADDA */
+
+ if (dql < 0) {
+ return ((sign) ? -0x8000 : 0);
+ } else { /* ANTILOG */
+ dex = (dql >> 7) & 15;
+ dqt = 128 + (dql & 127);
+ dq = (dqt << 7) >> (14 - dex);
+ return ((sign) ? (dq - 0x8000) : dq);
+ }
+}
+
+
+/*
+ * update()
+ *
+ * updates the state variables for each output code
+ */
+void
+update(
+ int code_size, /* distinguish 723_40 with others */
+ int y, /* quantizer step size */
+ int wi, /* scale factor multiplier */
+ int fi, /* for long/short term energies */
+ int dq, /* quantized prediction difference */
+ int sr, /* reconstructed signal */
+ int dqsez, /* difference from 2-pole predictor */
+ struct g72x_state *state_ptr) /* coder state pointer */
+{
+ int cnt;
+ short mag, exp; /* Adaptive predictor, FLOAT A */
+ short a2p = 0; /* LIMC */
+ short a1ul; /* UPA1 */
+ short pks1; /* UPA2 */
+ short fa1;
+ char tr; /* tone/transition detector */
+ short ylint, thr2, dqthr;
+ short ylfrac, thr1;
+ short pk0;
+
+ pk0 = (dqsez < 0) ? 1 : 0; /* needed in updating predictor poles */
+
+ mag = dq & 0x7FFF; /* prediction difference magnitude */
+ /* TRANS */
+ ylint = state_ptr->yl >> 15; /* exponent part of yl */
+ ylfrac = (state_ptr->yl >> 10) & 0x1F; /* fractional part of yl */
+ thr1 = (32 + ylfrac) << ylint; /* threshold */
+ thr2 = (ylint > 9) ? 31 << 10 : thr1; /* limit thr2 to 31 << 10 */
+ dqthr = (thr2 + (thr2 >> 1)) >> 1; /* dqthr = 0.75 * thr2 */
+ if (state_ptr->td == 0) /* signal supposed voice */
+ tr = 0;
+ else if (mag <= dqthr) /* supposed data, but small mag */
+ tr = 0; /* treated as voice */
+ else /* signal is data (modem) */
+ tr = 1;
+
+ /*
+ * Quantizer scale factor adaptation.
+ */
+
+ /* FUNCTW & FILTD & DELAY */
+ /* update non-steady state step size multiplier */
+ state_ptr->yu = y + ((wi - y) >> 5);
+
+ /* LIMB */
+ if (state_ptr->yu < 544) /* 544 <= yu <= 5120 */
+ state_ptr->yu = 544;
+ else if (state_ptr->yu > 5120)
+ state_ptr->yu = 5120;
+
+ /* FILTE & DELAY */
+ /* update steady state step size multiplier */
+ state_ptr->yl += state_ptr->yu + ((-state_ptr->yl) >> 6);
+
+ /*
+ * Adaptive predictor coefficients.
+ */
+ if (tr == 1) { /* reset a's and b's for modem signal */
+ state_ptr->a[0] = 0;
+ state_ptr->a[1] = 0;
+ state_ptr->b[0] = 0;
+ state_ptr->b[1] = 0;
+ state_ptr->b[2] = 0;
+ state_ptr->b[3] = 0;
+ state_ptr->b[4] = 0;
+ state_ptr->b[5] = 0;
+ } else { /* update a's and b's */
+ pks1 = pk0 ^ state_ptr->pk[0]; /* UPA2 */
+
+ /* update predictor pole a[1] */
+ a2p = state_ptr->a[1] - (state_ptr->a[1] >> 7);
+ if (dqsez != 0) {
+ fa1 = (pks1) ? state_ptr->a[0] : -state_ptr->a[0];
+ if (fa1 < -8191) /* a2p = function of fa1 */
+ a2p -= 0x100;
+ else if (fa1 > 8191)
+ a2p += 0xFF;
+ else
+ a2p += fa1 >> 5;
+
+ if (pk0 ^ state_ptr->pk[1])
+ /* LIMC */
+ if (a2p <= -12160)
+ a2p = -12288;
+ else if (a2p >= 12416)
+ a2p = 12288;
+ else
+ a2p -= 0x80;
+ else if (a2p <= -12416)
+ a2p = -12288;
+ else if (a2p >= 12160)
+ a2p = 12288;
+ else
+ a2p += 0x80;
+ }
+
+ /* TRIGB & DELAY */
+ state_ptr->a[1] = a2p;
+
+ /* UPA1 */
+ /* update predictor pole a[0] */
+ state_ptr->a[0] -= state_ptr->a[0] >> 8;
+ if (dqsez != 0)
+ if (pks1 == 0)
+ state_ptr->a[0] += 192;
+ else
+ state_ptr->a[0] -= 192;
+
+ /* LIMD */
+ a1ul = 15360 - a2p;
+ if (state_ptr->a[0] < -a1ul)
+ state_ptr->a[0] = -a1ul;
+ else if (state_ptr->a[0] > a1ul)
+ state_ptr->a[0] = a1ul;
+
+ /* UPB : update predictor zeros b[6] */
+ for (cnt = 0; cnt < 6; cnt++) {
+ if (code_size == 5) /* for 40Kbps G.723 */
+ state_ptr->b[cnt] -= state_ptr->b[cnt] >> 9;
+ else /* for G.721 and 24Kbps G.723 */
+ state_ptr->b[cnt] -= state_ptr->b[cnt] >> 8;
+ if (dq & 0x7FFF) { /* XOR */
+ if ((dq ^ state_ptr->dq[cnt]) >= 0)
+ state_ptr->b[cnt] += 128;
+ else
+ state_ptr->b[cnt] -= 128;
+ }
+ }
+ }
+
+ for (cnt = 5; cnt > 0; cnt--)
+ state_ptr->dq[cnt] = state_ptr->dq[cnt-1];
+ /* FLOAT A : convert dq[0] to 4-bit exp, 6-bit mantissa f.p. */
+ if (mag == 0) {
+ state_ptr->dq[0] = (dq >= 0) ? 0x20 : 0xFC20;
+ } else {
+ exp = quan(mag, power2, 15);
+ state_ptr->dq[0] = (dq >= 0) ?
+ (exp << 6) + ((mag << 6) >> exp) :
+ (exp << 6) + ((mag << 6) >> exp) - 0x400;
+ }
+
+ state_ptr->sr[1] = state_ptr->sr[0];
+ /* FLOAT B : convert sr to 4-bit exp., 6-bit mantissa f.p. */
+ if (sr == 0) {
+ state_ptr->sr[0] = 0x20;
+ } else if (sr > 0) {
+ exp = quan(sr, power2, 15);
+ state_ptr->sr[0] = (exp << 6) + ((sr << 6) >> exp);
+ } else if (sr > -32768) {
+ mag = -sr;
+ exp = quan(mag, power2, 15);
+ state_ptr->sr[0] = (exp << 6) + ((mag << 6) >> exp) - 0x400;
+ } else
+ state_ptr->sr[0] = 0xFC20;
+
+ /* DELAY A */
+ state_ptr->pk[1] = state_ptr->pk[0];
+ state_ptr->pk[0] = pk0;
+
+ /* TONE */
+ if (tr == 1) /* this sample has been treated as data */
+ state_ptr->td = 0; /* next one will be treated as voice */
+ else if (a2p < -11776) /* small sample-to-sample correlation */
+ state_ptr->td = 1; /* signal may be data */
+ else /* signal is voice */
+ state_ptr->td = 0;
+
+ /*
+ * Adaptation speed control.
+ */
+ state_ptr->dms += (fi - state_ptr->dms) >> 5; /* FILTA */
+ state_ptr->dml += (((fi << 2) - state_ptr->dml) >> 7); /* FILTB */
+
+ if (tr == 1)
+ state_ptr->ap = 256;
+ else if (y < 1536) /* SUBTC */
+ state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
+ else if (state_ptr->td == 1)
+ state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
+ else if (abs((state_ptr->dms << 2) - state_ptr->dml) >=
+ (state_ptr->dml >> 3))
+ state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
+ else
+ state_ptr->ap += (-state_ptr->ap) >> 4;
+}
+
+/*
+ * tandem_adjust(sr, se, y, i, sign)
+ *
+ * At the end of ADPCM decoding, it simulates an encoder which may be receiving
+ * the output of this decoder as a tandem process. If the output of the
+ * simulated encoder differs from the input to this decoder, the decoder output
+ * is adjusted by one level of A-law or u-law codes.
+ *
+ * Input:
+ * sr decoder output linear PCM sample,
+ * se predictor estimate sample,
+ * y quantizer step size,
+ * i decoder input code,
+ * sign sign bit of code i
+ *
+ * Return:
+ * adjusted A-law or u-law compressed sample.
+ */
+int
+tandem_adjust_alaw(
+ int sr, /* decoder output linear PCM sample */
+ int se, /* predictor estimate sample */
+ int y, /* quantizer step size */
+ int i, /* decoder input code */
+ int sign,
+ short *qtab)
+{
+ unsigned char sp; /* A-law compressed 8-bit code */
+ short dx; /* prediction error */
+ char id; /* quantized prediction error */
+ int sd; /* adjusted A-law decoded sample value */
+ int im; /* biased magnitude of i */
+ int imx; /* biased magnitude of id */
+
+ if (sr <= -32768)
+ sr = -1;
+ sp = linear2alaw((sr >> 1) << 3); /* short to A-law compression */
+ dx = (alaw2linear(sp) >> 2) - se; /* 16-bit prediction error */
+ id = quantize(dx, y, qtab, sign - 1);
+
+ if (id == i) { /* no adjustment on sp */
+ return (sp);
+ } else { /* sp adjustment needed */
+ /* ADPCM codes : 8, 9, ... F, 0, 1, ... , 6, 7 */
+ im = i ^ sign; /* 2's complement to biased unsigned */
+ imx = id ^ sign;
+
+ if (imx > im) { /* sp adjusted to next lower value */
+ if (sp & 0x80) {
+ sd = (sp == 0xD5) ? 0x55 :
+ ((sp ^ 0x55) - 1) ^ 0x55;
+ } else {
+ sd = (sp == 0x2A) ? 0x2A :
+ ((sp ^ 0x55) + 1) ^ 0x55;
+ }
+ } else { /* sp adjusted to next higher value */
+ if (sp & 0x80)
+ sd = (sp == 0xAA) ? 0xAA :
+ ((sp ^ 0x55) + 1) ^ 0x55;
+ else
+ sd = (sp == 0x55) ? 0xD5 :
+ ((sp ^ 0x55) - 1) ^ 0x55;
+ }
+ return (sd);
+ }
+}
+
+int
+tandem_adjust_ulaw(
+ int sr, /* decoder output linear PCM sample */
+ int se, /* predictor estimate sample */
+ int y, /* quantizer step size */
+ int i, /* decoder input code */
+ int sign,
+ short *qtab)
+{
+ unsigned char sp; /* u-law compressed 8-bit code */
+ short dx; /* prediction error */
+ char id; /* quantized prediction error */
+ int sd; /* adjusted u-law decoded sample value */
+ int im; /* biased magnitude of i */
+ int imx; /* biased magnitude of id */
+
+ if (sr <= -32768)
+ sr = 0;
+ sp = linear2ulaw(sr << 2); /* short to u-law compression */
+ dx = (ulaw2linear(sp) >> 2) - se; /* 16-bit prediction error */
+ id = quantize(dx, y, qtab, sign - 1);
+ if (id == i) {
+ return (sp);
+ } else {
+ /* ADPCM codes : 8, 9, ... F, 0, 1, ... , 6, 7 */
+ im = i ^ sign; /* 2's complement to biased unsigned */
+ imx = id ^ sign;
+ if (imx > im) { /* sp adjusted to next lower value */
+ if (sp & 0x80)
+ sd = (sp == 0xFF) ? 0x7E : sp + 1;
+ else
+ sd = (sp == 0) ? 0 : sp - 1;
+
+ } else { /* sp adjusted to next higher value */
+ if (sp & 0x80)
+ sd = (sp == 0x80) ? 0x80 : sp - 1;
+ else
+ sd = (sp == 0x7F) ? 0xFE : sp + 1;
+ }
+ return (sd);
+ }
+}
diff --git a/src/audio/g72x.h b/src/audio/g72x.h
new file mode 100644
index 0000000..85201c3
--- /dev/null
+++ b/src/audio/g72x.h
@@ -0,0 +1,151 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * g72x.h
+ *
+ * Header file for CCITT conversion routines.
+ *
+ */
+#ifndef _G72X_H
+#define _G72X_H
+
+#define AUDIO_ENCODING_ULAW (1) /* ISDN u-law */
+#define AUDIO_ENCODING_ALAW (2) /* ISDN A-law */
+#define AUDIO_ENCODING_LINEAR (3) /* PCM 2's-complement (0-center) */
+
+/*
+ * The following is the definition of the state structure
+ * used by the G.721/G.723 encoder and decoder to preserve their internal
+ * state between successive calls. The meanings of the majority
+ * of the state structure fields are explained in detail in the
+ * CCITT Recommendation G.721. The field names are essentially indentical
+ * to variable names in the bit level description of the coding algorithm
+ * included in this Recommendation.
+ */
+struct g72x_state {
+ long yl; /* Locked or steady state step size multiplier. */
+ short yu; /* Unlocked or non-steady state step size multiplier. */
+ short dms; /* Short term energy estimate. */
+ short dml; /* Long term energy estimate. */
+ short ap; /* Linear weighting coefficient of 'yl' and 'yu'. */
+
+ short a[2]; /* Coefficients of pole portion of prediction filter. */
+ short b[6]; /* Coefficients of zero portion of prediction filter. */
+ short pk[2]; /*
+ * Signs of previous two samples of a partially
+ * reconstructed signal.
+ */
+ short dq[6]; /*
+ * Previous 6 samples of the quantized difference
+ * signal represented in an internal floating point
+ * format.
+ */
+ short sr[2]; /*
+ * Previous 2 samples of the quantized difference
+ * signal represented in an internal floating point
+ * format.
+ */
+ char td; /* delayed tone detect, new in 1988 version */
+};
+
+int predictor_zero(struct g72x_state *state_ptr);
+int predictor_pole(struct g72x_state *state_ptr);
+int step_size(struct g72x_state *state_ptr);
+
+int quantize(
+ int d, /* Raw difference signal sample */
+ int y, /* Step size multiplier */
+ short *table, /* quantization table */
+ int size); /* table size of short integers */
+
+int reconstruct(
+ int sign, /* 0 for non-negative value */
+ int dqln, /* G.72x codeword */
+ int y); /* Step size multiplier */
+
+void update(
+ int code_size, /* distinguish 723_40 with others */
+ int y, /* quantizer step size */
+ int wi, /* scale factor multiplier */
+ int fi, /* for long/short term energies */
+ int dq, /* quantized prediction difference */
+ int sr, /* reconstructed signal */
+ int dqsez, /* difference from 2-pole predictor */
+ struct g72x_state *state_ptr);
+
+int tandem_adjust_alaw(
+ int sr, /* decoder output linear PCM sample */
+ int se, /* predictor estimate sample */
+ int y, /* quantizer step size */
+ int i, /* decoder input code */
+ int sign,
+ short *qtab);
+
+int
+tandem_adjust_ulaw(
+ int sr, /* decoder output linear PCM sample */
+ int se, /* predictor estimate sample */
+ int y, /* quantizer step size */
+ int i, /* decoder input code */
+ int sign,
+ short *qtab);
+
+void g72x_init_state(struct g72x_state *);
+int g721_encoder(
+ int sample,
+ int in_coding,
+ struct g72x_state *state_ptr);
+int g721_decoder(
+ int code,
+ int out_coding,
+ struct g72x_state *state_ptr);
+int g723_16_encoder(
+ int sample,
+ int in_coding,
+ struct g72x_state *state_ptr);
+int g723_16_decoder(
+ int code,
+ int out_coding,
+ struct g72x_state *state_ptr);
+int g723_24_encoder(
+ int sample,
+ int in_coding,
+ struct g72x_state *state_ptr);
+int g723_24_decoder(
+ int code,
+ int out_coding,
+ struct g72x_state *state_ptr);
+int g723_40_encoder(
+ int sample,
+ int in_coding,
+ struct g72x_state *state_ptr);
+int g723_40_decoder(
+ int code,
+ int out_coding,
+ struct g72x_state *state_ptr);
+
+#endif /* !_G72X_H */
diff --git a/src/audio/gsm/COPYRIGHT b/src/audio/gsm/COPYRIGHT
new file mode 100644
index 0000000..eba0e52
--- /dev/null
+++ b/src/audio/gsm/COPYRIGHT
@@ -0,0 +1,16 @@
+Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
+Technische Universitaet Berlin
+
+Any use of this software is permitted provided that this notice is not
+removed and that neither the authors nor the Technische Universitaet Berlin
+are deemed to have made any representations as to the suitability of this
+software for any purpose nor are held responsible for any defects of
+this software. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+
+As a matter of courtesy, the authors request to be informed about uses
+this software has found, about bugs in this software, and about any
+improvements that may be of general interest.
+
+Berlin, 28.11.1994
+Jutta Degener
+Carsten Bormann
diff --git a/src/audio/gsm/ChangeLog b/src/audio/gsm/ChangeLog
new file mode 100644
index 0000000..4cf467d
--- /dev/null
+++ b/src/audio/gsm/ChangeLog
@@ -0,0 +1,80 @@
+
+Fri Jul 5 19:26:37 1996 Jutta Degener (jutta@cs.tu-berlin.de)
+
+ * Release 1.0 Patchlevel 10
+ src/toast_alaw.c: exchanged A-law tables for something
+ slightly more A-law.
+
+Tue Jul 2 12:18:20 1996 Jutta Degener (jutta@cs.tu-berlin.de)
+
+ * Release 1.0 Patchlevel 9
+ src/long_term.c: in FLOAT_MUL mode, an array was accessed past its end
+ src/gsm_option.c: three options related to WAV #49 packing
+ src/gsm_encode.c: support WAV #49-style encoding.
+ src/gsm_decode.c: support WAV #49-style decoding.
+ tls/sour.c: generate the WAV bit shifting code, encode
+ tls/ginger.c: generate the WAV bit shifting code, decode
+ The WAV code goes back to an inofficial patch #8 that
+ Jeff Chilton sent us (hence the jump from 7 to 9).
+ src/toast.c: add _fsetmode() calls to set stdin/stdout to
+ binary (from an OS/2 port by Arnd Gronenberg.)
+
+Tue Mar 7 01:55:10 1995 Jutta Degener (jutta@cs.tu-berlin.de)
+
+ * Release 1.0 Patchlevel 7
+ src/long_term.c: Yet another 16-bit overflow
+ src/toast.c: -C option to toast, cuts LPC time
+ src/gsm_option.c: corresponding LPC_CUT option to GSM library
+
+Fri Dec 30 23:33:50 1994 Jutta Degener (jutta@cs.tu-berlin.de)
+
+ * Release 1.0 Patchlevel 6
+ src/lpc.c: fixed 16-bit addition overflow in Autocorrelation code
+ src/add.c: gsm_L_asl should fall back on gsm_L_asr, not gsm_asr
+
+Mon Nov 28 20:49:57 1994 Jutta Degener (jutta@cs.tu-berlin.de)
+
+ * Release 1.0 Patchlevel 5
+ src/toast_audio.c: initialization should return -1 on error
+ src/gsm_destroy.c: #include configuration header file
+ src/add.c: gsm_sub should cast its parameters to longword
+ man/*: bug reports to {jutta,cabo}@cs.tu-berlin.de, not to toast@tub
+ inc/private.h: longword long by default, not int
+ inc/toast.h: read/write fopen modes "rb" and "wb", not just "r"
+ src/toast.c: better (or different, anyway) error handling in process()
+
+Tue May 10 19:41:34 1994 Jutta Degener (jutta at kugelbus)
+
+ * Release 1.0 Patchlevel 4
+ inc/private.h: GSM_ADD should cast to ulongword, not to unsigned.
+ src/long_term.c: missing cast to longword.
+ add-test/add_test.c: Test macros too, not only functions,
+ thanks to Simao Ferraz de Campos Neto, simao@dragon.cpqd.ansp.br
+ General cleanup: remove unused variables, add function prototypes.
+
+Tue Jan 25 22:53:40 1994 Jutta Degener (jutta at kugelbus)
+
+ * Release 1.0 Patchlevel 3
+ changed rpe.c's STEP macro to work with 16-bit integers,
+ thanks to Dr Alex Lee (alexlee@solomon.technet.sg);
+ removed non-fatal bugs from add-test.dta, private.h
+ and toast_audio.c, thanks to P. Emanuelsson.
+
+Fri Jan 29 19:02:12 1993 Jutta Degener (jutta at kraftbus)
+
+ * Release 1.0 Patchlevel 2
+ fixed L_add(0,-1) in src/add.c and inc/private.h,
+ thanks to Raphael Trommer at AT&T Bell Laboratories;
+ various other ANSI C compatibility details
+
+Fri Oct 30 17:58:54 1992 Jutta Degener (jutta at kraftbus)
+
+ * Release 1.0 Patchlevel 1
+ Switched uid/gid in toast's [f]chown calls.
+
+Wed Oct 28 14:12:35 1992 Carsten Bormann (cabo at kubus)
+
+ * Release 1.0: released
+ Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
diff --git a/src/audio/gsm/INSTALL b/src/audio/gsm/INSTALL
new file mode 100644
index 0000000..5850304
--- /dev/null
+++ b/src/audio/gsm/INSTALL
@@ -0,0 +1,99 @@
+How to get started:
+
+ Edit the Makefile.
+
+ You should configure a few machine-dependencies and what
+ compiler you want to use.
+
+ The code works both with ANSI and K&R-C. Use
+ -DNeedFunctionPrototypes to compile with, or
+ -UNeedFunctionPrototypes to compile without, function
+ prototypes in the header files.
+
+ Make addtst
+
+ The "add" program that will be compiled and run checks whether
+ the basic math functions of the gsm library work with your
+ compiler. If it prints anything to stderr, complain (to us).
+
+ Edit inc/config.h.
+
+ Make
+
+ Local versions of the gsm library and the "compress"-like filters
+ toast, untoast and tcat will be generated.
+
+ If the compilation aborts because of a missing function,
+ declaration, or header file, see if there's something in
+ inc/config.h to work around it. If not, complain.
+
+ Try it
+
+ Grab an audio file from somewhere (raw u-law or Sun .au is fine,
+ linear 16-bit in host byte order will do), copy it, toast it,
+ untoast it, and listen to the result.
+
+ The GSM-encoded and -decoded audio should have the quality
+ of a good phone line. If the resulting audio is noisier than
+ your original, or if you hear compression artifacts, complain;
+ that's a bug in our software, not a bug in the GSM encoding
+ standard itself.
+
+Installation
+
+ You can install the gsm library interface, or the toast binaries,
+ or both.
+
+ Edit the Makefile
+
+ Fill in the directories where you want to install the
+ library, header files, manual pages, and binaries.
+
+ Turn off the installation of one half of the distribution
+ (i.e., gsm library or toast binaries) by not setting the
+ corresponding directory root Makefile macro.
+
+ make install
+
+ will install the programs "toast" with two links named
+ "tcat" and "untoast", and the gsm library "libgsm.a" with
+ a "gsm.h" header file, and their respective manual pages.
+
+
+Optimizing
+
+ This code was developed on a machine without an integer
+ multiplication instruction, where we obtained the fastest result by
+ replacing some of the integer multiplications with floating point
+ multiplications.
+
+ If your machine does multiply integers fast enough,
+ leave USE_FLOAT_MUL undefined. The results should be the
+ same in both cases.
+
+ On machines with fast floating point arithmetic, defining
+ both USE_FLOAT_MUL and FAST makes a run-time library
+ option available that will (in a few crucial places) use
+ ``native'' floating point operations rather than the bit-by-bit
+ defined ones of the GSM standard. If you use this fast
+ option, the outcome will not be bitwise identical to the
+ results prescribed by the standard, but it is compatible with
+ the standard encoding, and a user is unlikely to notice a
+ difference.
+
+
+Bug Reports
+
+ Please direct bug reports, questions, and comments to
+ jutta@cs.tu-berlin.de and cabo@informatik.uni-bremen.de.
+
+
+Good luck,
+
+ Jutta Degener,
+ Carsten Bormann
+
+--
+Copyright 1992, 1993, 1994, by Jutta Degener and Carsten Bormann,
+Technische Universitaet Berlin. See the accompanying file "COPYRIGHT"
+for details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
diff --git a/src/audio/gsm/MACHINES b/src/audio/gsm/MACHINES
new file mode 100644
index 0000000..4adafd2
--- /dev/null
+++ b/src/audio/gsm/MACHINES
@@ -0,0 +1,11 @@
+The gsm library has been tested successfully on the following platforms:
+
+- Various Sun4's running SunOS 4.1.2
+- SPARC1 (SunOS 4.1.1)
+- Integrated Solutions 68k Optimum running 4.3BSD UNIX with a Green Hills cc
+- NeXTstation running NeXT-OS/Mach 3.0
+- No-name AT/386 with Xenix 2.3.2 (using -DSTUPID_COMPILER)
+- RS/6000-350 running AIX 3.2.0
+- RS/6000-320 running AIX 3.1.5
+- Alliant FX80 (Concentrix 5.7)
+- SGI Indigo XS4000 (IRIX 4.0.5F)
diff --git a/src/audio/gsm/Makefile.am b/src/audio/gsm/Makefile.am
new file mode 100644
index 0000000..3d437a1
--- /dev/null
+++ b/src/audio/gsm/Makefile.am
@@ -0,0 +1,32 @@
+AM_CPPFLAGS = \
+ -I$(srcdir)/inc \
+ -DSASR -DNeedFunctionPrototypes=1
+
+noinst_LIBRARIES = libgsm.a
+
+libgsm_a_SOURCES =\
+ src/add.cpp\
+ src/code.cpp\
+ src/debug.cpp\
+ src/decode.cpp\
+ src/long_term.cpp\
+ src/lpc.cpp\
+ src/preprocess.cpp\
+ src/rpe.cpp\
+ src/gsm_destroy.cpp\
+ src/gsm_decode.cpp\
+ src/gsm_encode.cpp\
+ src/gsm_explode.cpp\
+ src/gsm_implode.cpp\
+ src/gsm_create.cpp\
+ src/gsm_print.cpp\
+ src/gsm_option.cpp\
+ src/short_term.cpp\
+ src/table.cpp\
+ inc/gsm.h\
+ inc/proto.h\
+ inc/config.h\
+ inc/private.h\
+ inc/unproto.h
+
+EXTRA_DIST = COPYRIGHT MACHINES
diff --git a/src/audio/gsm/README b/src/audio/gsm/README
new file mode 100644
index 0000000..cb6af85
--- /dev/null
+++ b/src/audio/gsm/README
@@ -0,0 +1,37 @@
+
+GSM 06.10 13 kbit/s RPE/LTP speech compression available
+--------------------------------------------------------
+
+The Communications and Operating Systems Research Group (KBS) at the
+Technische Universitaet Berlin is currently working on a set of
+UNIX-based tools for computer-mediated telecooperation that will be
+made freely available.
+
+As part of this effort we are publishing an implementation of the
+European GSM 06.10 provisional standard for full-rate speech
+transcoding, prI-ETS 300 036, which uses RPE/LTP (residual pulse
+excitation/long term prediction) coding at 13 kbit/s.
+
+GSM 06.10 compresses frames of 160 13-bit samples (8 kHz sampling
+rate, i.e. a frame rate of 50 Hz) into 260 bits; for compatibility
+with typical UNIX applications, our implementation turns frames of 160
+16-bit linear samples into 33-byte frames (1650 Bytes/s).
+The quality of the algorithm is good enough for reliable speaker
+recognition; even music often survives transcoding in recognizable
+form (given the bandwidth limitations of 8 kHz sampling rate).
+
+The interfaces offered are a front end modelled after compress(1), and
+a library API. Compression and decompression run faster than realtime
+on most SPARCstations. The implementation has been verified against the
+ETSI standard test patterns.
+
+Jutta Degener (jutta@cs.tu-berlin.de)
+Carsten Bormann (cabo@cs.tu-berlin.de)
+
+Communications and Operating Systems Research Group, TU Berlin
+Fax: +49.30.31425156, Phone: +49.30.31424315
+
+--
+Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
diff --git a/src/audio/gsm/inc/config.h b/src/audio/gsm/inc/config.h
new file mode 100644
index 0000000..6aec1ae
--- /dev/null
+++ b/src/audio/gsm/inc/config.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/config.h,v 1.5 1996/07/02 11:26:20 jutta Exp $*/
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+/*efine SIGHANDLER_T int /* signal handlers are void */
+/*efine HAS_SYSV_SIGNAL 1 /* sigs not blocked/reset? */
+
+#define HAS_STDLIB_H 1 /* /usr/include/stdlib.h */
+/*efine HAS_LIMITS_H 1 /* /usr/include/limits.h */
+#define HAS_FCNTL_H 1 /* /usr/include/fcntl.h */
+/*efine HAS_ERRNO_DECL 1 /* errno.h declares errno */
+
+#define HAS_FSTAT 1 /* fstat syscall */
+#define HAS_FCHMOD 1 /* fchmod syscall */
+#define HAS_CHMOD 1 /* chmod syscall */
+#define HAS_FCHOWN 1 /* fchown syscall */
+#define HAS_CHOWN 1 /* chown syscall */
+/*efine HAS__FSETMODE 1 /* _fsetmode -- set file mode */
+
+#define HAS_STRING_H 1 /* /usr/include/string.h */
+/*efine HAS_STRINGS_H 1 /* /usr/include/strings.h */
+
+#define HAS_UNISTD_H 1 /* /usr/include/unistd.h */
+#define HAS_UTIME 1 /* POSIX utime(path, times) */
+/*efine HAS_UTIMES 1 /* use utimes() syscall instead */
+#define HAS_UTIME_H 1 /* UTIME header file */
+/*efine HAS_UTIMBUF 1 /* struct utimbuf */
+/*efine HAS_UTIMEUSEC 1 /* microseconds in utimbuf? */
+
+#endif /* CONFIG_H */
diff --git a/src/audio/gsm/inc/gsm.h b/src/audio/gsm/inc/gsm.h
new file mode 100644
index 0000000..4714ab6
--- /dev/null
+++ b/src/audio/gsm/inc/gsm.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*$Header: /home/kbs/jutta/src/gsm/gsm-1.0/inc/RCS/gsm.h,v 1.11 1996/07/05 18:02:56 jutta Exp $*/
+
+#ifndef GSM_H
+#define GSM_H
+
+#ifdef __cplusplus
+# define NeedFunctionPrototypes 1
+#endif
+
+#if __STDC__
+# define NeedFunctionPrototypes 1
+#endif
+
+#ifdef _NO_PROTO
+# undef NeedFunctionPrototypes
+#endif
+
+#ifdef NeedFunctionPrototypes
+# include <stdio.h> /* for FILE * */
+#endif
+
+#undef GSM_P
+#if NeedFunctionPrototypes
+# define GSM_P( protos ) protos
+#else
+# define GSM_P( protos ) ( /* protos */ )
+#endif
+
+/*
+ * Interface
+ */
+
+typedef struct gsm_state * gsm;
+typedef short gsm_signal; /* signed 16 bit */
+typedef unsigned char gsm_byte;
+typedef gsm_byte gsm_frame[33]; /* 33 * 8 bits */
+
+#define GSM_MAGIC 0xD /* 13 kbit/s RPE-LTP */
+
+#define GSM_PATCHLEVEL 10
+#define GSM_MINOR 0
+#define GSM_MAJOR 1
+
+#define GSM_OPT_VERBOSE 1
+#define GSM_OPT_FAST 2
+#define GSM_OPT_LTP_CUT 3
+#define GSM_OPT_WAV49 4
+#define GSM_OPT_FRAME_INDEX 5
+#define GSM_OPT_FRAME_CHAIN 6
+
+extern gsm gsm_create GSM_P((void));
+extern void gsm_destroy GSM_P((gsm));
+
+extern int gsm_print GSM_P((FILE *, gsm, gsm_byte *));
+extern int gsm_option GSM_P((gsm, int, int *));
+
+extern void gsm_encode GSM_P((gsm, gsm_signal *, gsm_byte *));
+extern int gsm_decode GSM_P((gsm, gsm_byte *, gsm_signal *));
+
+extern int gsm_explode GSM_P((gsm, gsm_byte *, gsm_signal *));
+extern void gsm_implode GSM_P((gsm, gsm_signal *, gsm_byte *));
+
+#undef GSM_P
+
+#endif /* GSM_H */
diff --git a/src/audio/gsm/inc/private.h b/src/audio/gsm/inc/private.h
new file mode 100644
index 0000000..fea5694
--- /dev/null
+++ b/src/audio/gsm/inc/private.h
@@ -0,0 +1,268 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/private.h,v 1.6 1996/07/02 10:15:26 jutta Exp $*/
+
+#ifndef PRIVATE_H
+#define PRIVATE_H
+
+typedef short word; /* 16 bit signed int */
+typedef long longword; /* 32 bit signed int */
+
+typedef unsigned short uword; /* unsigned word */
+typedef unsigned long ulongword; /* unsigned longword */
+
+struct gsm_state {
+
+ word dp0[ 280 ];
+
+ word z1; /* preprocessing.c, Offset_com. */
+ longword L_z2; /* Offset_com. */
+ int mp; /* Preemphasis */
+
+ word u[8]; /* short_term_aly_filter.c */
+ word LARpp[2][8]; /* */
+ word j; /* */
+
+ word ltp_cut; /* long_term.c, LTP crosscorr. */
+ word nrp; /* 40 */ /* long_term.c, synthesis */
+ word v[9]; /* short_term.c, synthesis */
+ word msr; /* decoder.c, Postprocessing */
+
+ char verbose; /* only used if !NDEBUG */
+ char fast; /* only used if FAST */
+
+ char wav_fmt; /* only used if WAV49 defined */
+ unsigned char frame_index; /* odd/even chaining */
+ unsigned char frame_chain; /* half-byte to carry forward */
+};
+
+
+#define MIN_WORD (-32767 - 1)
+#define MAX_WORD 32767
+
+#define MIN_LONGWORD (-2147483647 - 1)
+#define MAX_LONGWORD 2147483647
+
+#ifdef SASR /* flag: >> is a signed arithmetic shift right */
+#undef SASR
+#define SASR(x, by) ((x) >> (by))
+#else
+#define SASR(x, by) ((x) >= 0 ? (x) >> (by) : (~(-((x) + 1) >> (by))))
+#endif /* SASR */
+
+#include "proto.h"
+
+/*
+ * Prototypes from add.c
+ */
+extern word gsm_mult P((word a, word b));
+extern longword gsm_L_mult P((word a, word b));
+extern word gsm_mult_r P((word a, word b));
+
+extern word gsm_div P((word num, word denum));
+
+extern word gsm_add P(( word a, word b ));
+extern longword gsm_L_add P(( longword a, longword b ));
+
+extern word gsm_sub P((word a, word b));
+extern longword gsm_L_sub P((longword a, longword b));
+
+extern word gsm_abs P((word a));
+
+extern word gsm_norm P(( longword a ));
+
+extern longword gsm_L_asl P((longword a, int n));
+extern word gsm_asl P((word a, int n));
+
+extern longword gsm_L_asr P((longword a, int n));
+extern word gsm_asr P((word a, int n));
+
+/*
+ * Inlined functions from add.h
+ */
+
+/*
+ * #define GSM_MULT_R(a, b) (* word a, word b, !(a == b == MIN_WORD) *) \
+ * (0x0FFFF & SASR(((longword)(a) * (longword)(b) + 16384), 15))
+ */
+#define GSM_MULT_R(a, b) /* word a, word b, !(a == b == MIN_WORD) */ \
+ (SASR( ((longword)(a) * (longword)(b) + 16384), 15 ))
+
+# define GSM_MULT(a,b) /* word a, word b, !(a == b == MIN_WORD) */ \
+ (SASR( ((longword)(a) * (longword)(b)), 15 ))
+
+# define GSM_L_MULT(a, b) /* word a, word b */ \
+ (((longword)(a) * (longword)(b)) << 1)
+
+# define GSM_L_ADD(a, b) \
+ ( (a) < 0 ? ( (b) >= 0 ? (a) + (b) \
+ : (utmp = (ulongword)-((a) + 1) + (ulongword)-((b) + 1)) \
+ >= MAX_LONGWORD ? MIN_LONGWORD : -(longword)utmp-2 ) \
+ : ((b) <= 0 ? (a) + (b) \
+ : (utmp = (ulongword)(a) + (ulongword)(b)) >= MAX_LONGWORD \
+ ? MAX_LONGWORD : utmp))
+
+/*
+ * # define GSM_ADD(a, b) \
+ * ((ltmp = (longword)(a) + (longword)(b)) >= MAX_WORD \
+ * ? MAX_WORD : ltmp <= MIN_WORD ? MIN_WORD : ltmp)
+ */
+/* Nonportable, but faster: */
+
+#define GSM_ADD(a, b) \
+ ((ulongword)((ltmp = (longword)(a) + (longword)(b)) - MIN_WORD) > \
+ MAX_WORD - MIN_WORD ? (ltmp > 0 ? MAX_WORD : MIN_WORD) : ltmp)
+
+# define GSM_SUB(a, b) \
+ ((ltmp = (longword)(a) - (longword)(b)) >= MAX_WORD \
+ ? MAX_WORD : ltmp <= MIN_WORD ? MIN_WORD : ltmp)
+
+# define GSM_ABS(a) ((a) < 0 ? ((a) == MIN_WORD ? MAX_WORD : -(a)) : (a))
+
+/* Use these if necessary:
+
+# define GSM_MULT_R(a, b) gsm_mult_r(a, b)
+# define GSM_MULT(a, b) gsm_mult(a, b)
+# define GSM_L_MULT(a, b) gsm_L_mult(a, b)
+
+# define GSM_L_ADD(a, b) gsm_L_add(a, b)
+# define GSM_ADD(a, b) gsm_add(a, b)
+# define GSM_SUB(a, b) gsm_sub(a, b)
+
+# define GSM_ABS(a) gsm_abs(a)
+
+*/
+
+/*
+ * More prototypes from implementations..
+ */
+extern void Gsm_Coder P((
+ struct gsm_state * S,
+ word * s, /* [0..159] samples IN */
+ word * LARc, /* [0..7] LAR coefficients OUT */
+ word * Nc, /* [0..3] LTP lag OUT */
+ word * bc, /* [0..3] coded LTP gain OUT */
+ word * Mc, /* [0..3] RPE grid selection OUT */
+ word * xmaxc,/* [0..3] Coded maximum amplitude OUT */
+ word * xMc /* [13*4] normalized RPE samples OUT */));
+
+extern void Gsm_Long_Term_Predictor P(( /* 4x for 160 samples */
+ struct gsm_state * S,
+ word * d, /* [0..39] residual signal IN */
+ word * dp, /* [-120..-1] d' IN */
+ word * e, /* [0..40] OUT */
+ word * dpp, /* [0..40] OUT */
+ word * Nc, /* correlation lag OUT */
+ word * bc /* gain factor OUT */));
+
+extern void Gsm_LPC_Analysis P((
+ struct gsm_state * S,
+ word * s, /* 0..159 signals IN/OUT */
+ word * LARc)); /* 0..7 LARc's OUT */
+
+extern void Gsm_Preprocess P((
+ struct gsm_state * S,
+ word * s, word * so));
+
+extern void Gsm_Encoding P((
+ struct gsm_state * S,
+ word * e,
+ word * ep,
+ word * xmaxc,
+ word * Mc,
+ word * xMc));
+
+extern void Gsm_Short_Term_Analysis_Filter P((
+ struct gsm_state * S,
+ word * LARc, /* coded log area ratio [0..7] IN */
+ word * d /* st res. signal [0..159] IN/OUT */));
+
+extern void Gsm_Decoder P((
+ struct gsm_state * S,
+ word * LARcr, /* [0..7] IN */
+ word * Ncr, /* [0..3] IN */
+ word * bcr, /* [0..3] IN */
+ word * Mcr, /* [0..3] IN */
+ word * xmaxcr, /* [0..3] IN */
+ word * xMcr, /* [0..13*4] IN */
+ word * s)); /* [0..159] OUT */
+
+extern void Gsm_Decoding P((
+ struct gsm_state * S,
+ word xmaxcr,
+ word Mcr,
+ word * xMcr, /* [0..12] IN */
+ word * erp)); /* [0..39] OUT */
+
+extern void Gsm_Long_Term_Synthesis_Filtering P((
+ struct gsm_state* S,
+ word Ncr,
+ word bcr,
+ word * erp, /* [0..39] IN */
+ word * drp)); /* [-120..-1] IN, [0..40] OUT */
+
+void Gsm_RPE_Decoding P((
+ struct gsm_state *S,
+ word xmaxcr,
+ word Mcr,
+ word * xMcr, /* [0..12], 3 bits IN */
+ word * erp)); /* [0..39] OUT */
+
+void Gsm_RPE_Encoding P((
+ struct gsm_state * S,
+ word * e, /* -5..-1][0..39][40..44 IN/OUT */
+ word * xmaxc, /* OUT */
+ word * Mc, /* OUT */
+ word * xMc)); /* [0..12] OUT */
+
+extern void Gsm_Short_Term_Synthesis_Filter P((
+ struct gsm_state * S,
+ word * LARcr, /* log area ratios [0..7] IN */
+ word * drp, /* received d [0...39] IN */
+ word * s)); /* signal s [0..159] OUT */
+
+extern void Gsm_Update_of_reconstructed_short_time_residual_signal P((
+ word * dpp, /* [0...39] IN */
+ word * ep, /* [0...39] IN */
+ word * dp)); /* [-120...-1] IN/OUT */
+
+/*
+ * Tables from table.c
+ */
+#ifndef GSM_TABLE_C
+
+extern word gsm_A[8], gsm_B[8], gsm_MIC[8], gsm_MAC[8];
+extern word gsm_INVA[8];
+extern word gsm_DLB[4], gsm_QLB[4];
+extern word gsm_H[11];
+extern word gsm_NRFAC[8];
+extern word gsm_FAC[8];
+
+#endif /* GSM_TABLE_C */
+
+/*
+ * Debugging
+ */
+#ifdef NDEBUG
+
+# define gsm_debug_words(a, b, c, d) /* nil */
+# define gsm_debug_longwords(a, b, c, d) /* nil */
+# define gsm_debug_word(a, b) /* nil */
+# define gsm_debug_longword(a, b) /* nil */
+
+#else /* !NDEBUG => DEBUG */
+
+ extern void gsm_debug_words P((char * name, int, int, word *));
+ extern void gsm_debug_longwords P((char * name, int, int, longword *));
+ extern void gsm_debug_longword P((char * name, longword));
+ extern void gsm_debug_word P((char * name, word));
+
+#endif /* !NDEBUG */
+
+#include "unproto.h"
+
+#endif /* PRIVATE_H */
diff --git a/src/audio/gsm/inc/proto.h b/src/audio/gsm/inc/proto.h
new file mode 100644
index 0000000..2851c08
--- /dev/null
+++ b/src/audio/gsm/inc/proto.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/proto.h,v 1.1 1992/10/28 00:11:08 jutta Exp $*/
+
+#ifndef PROTO_H
+#define PROTO_H
+
+#if __cplusplus
+# define NeedFunctionPrototypes 1
+#endif
+
+#if __STDC__
+# define NeedFunctionPrototypes 1
+#endif
+
+#ifdef _NO_PROTO
+# undef NeedFunctionPrototypes
+#endif
+
+#undef P /* gnu stdio.h actually defines this... */
+#undef P0
+#undef P1
+#undef P2
+#undef P3
+#undef P4
+#undef P5
+#undef P6
+#undef P7
+#undef P8
+
+#if NeedFunctionPrototypes
+
+# define P( protos ) protos
+
+# define P0() (void)
+# define P1(x, a) (a)
+# define P2(x, a, b) (a, b)
+# define P3(x, a, b, c) (a, b, c)
+# define P4(x, a, b, c, d) (a, b, c, d)
+# define P5(x, a, b, c, d, e) (a, b, c, d, e)
+# define P6(x, a, b, c, d, e, f) (a, b, c, d, e, f)
+# define P7(x, a, b, c, d, e, f, g) (a, b, c, d, e, f, g)
+# define P8(x, a, b, c, d, e, f, g, h) (a, b, c, d, e, f, g, h)
+
+#else /* !NeedFunctionPrototypes */
+
+# define P( protos ) ( /* protos */ )
+
+# define P0() ()
+# define P1(x, a) x a;
+# define P2(x, a, b) x a; b;
+# define P3(x, a, b, c) x a; b; c;
+# define P4(x, a, b, c, d) x a; b; c; d;
+# define P5(x, a, b, c, d, e) x a; b; c; d; e;
+# define P6(x, a, b, c, d, e, f) x a; b; c; d; e; f;
+# define P7(x, a, b, c, d, e, f, g) x a; b; c; d; e; f; g;
+# define P8(x, a, b, c, d, e, f, g, h) x a; b; c; d; e; f; g; h;
+
+#endif /* !NeedFunctionPrototypes */
+
+#endif /* PROTO_H */
diff --git a/src/audio/gsm/inc/unproto.h b/src/audio/gsm/inc/unproto.h
new file mode 100644
index 0000000..eaf866f
--- /dev/null
+++ b/src/audio/gsm/inc/unproto.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/unproto.h,v 1.1 1992/10/28 00:11:08 jutta Exp $*/
+
+#ifdef PROTO_H /* sic */
+#undef PROTO_H
+
+#undef P
+#undef P0
+#undef P1
+#undef P2
+#undef P3
+#undef P4
+#undef P5
+#undef P6
+#undef P7
+#undef P8
+
+#endif /* PROTO_H */
diff --git a/src/audio/gsm/src/add.cpp b/src/audio/gsm/src/add.cpp
new file mode 100644
index 0000000..258a6fc
--- /dev/null
+++ b/src/audio/gsm/src/add.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/add.c,v 1.6 1996/07/02 09:57:33 jutta Exp $ */
+
+/*
+ * See private.h for the more commonly used macro versions.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "private.h"
+#include "gsm.h"
+#include "proto.h"
+
+#define saturate(x) \
+ ((x) < MIN_WORD ? MIN_WORD : (x) > MAX_WORD ? MAX_WORD: (x))
+
+word gsm_add P2((a,b), word a, word b)
+{
+ longword sum = (longword)a + (longword)b;
+ return saturate(sum);
+}
+
+word gsm_sub P2((a,b), word a, word b)
+{
+ longword diff = (longword)a - (longword)b;
+ return saturate(diff);
+}
+
+word gsm_mult P2((a,b), word a, word b)
+{
+ if (a == MIN_WORD && b == MIN_WORD) return MAX_WORD;
+ else return SASR( (longword)a * (longword)b, 15 );
+}
+
+word gsm_mult_r P2((a,b), word a, word b)
+{
+ if (b == MIN_WORD && a == MIN_WORD) return MAX_WORD;
+ else {
+ longword prod = (longword)a * (longword)b + 16384;
+ prod >>= 15;
+ return prod & 0xFFFF;
+ }
+}
+
+word gsm_abs P1((a), word a)
+{
+ return a < 0 ? (a == MIN_WORD ? MAX_WORD : -a) : a;
+}
+
+longword gsm_L_mult P2((a,b),word a, word b)
+{
+ assert( a != MIN_WORD || b != MIN_WORD );
+ return ((longword)a * (longword)b) << 1;
+}
+
+longword gsm_L_add P2((a,b), longword a, longword b)
+{
+ if (a < 0) {
+ if (b >= 0) return a + b;
+ else {
+ ulongword A = (ulongword)-(a + 1) + (ulongword)-(b + 1);
+ return A >= MAX_LONGWORD ? MIN_LONGWORD :-(longword)A-2;
+ }
+ }
+ else if (b <= 0) return a + b;
+ else {
+ ulongword A = (ulongword)a + (ulongword)b;
+ return A > MAX_LONGWORD ? MAX_LONGWORD : A;
+ }
+}
+
+longword gsm_L_sub P2((a,b), longword a, longword b)
+{
+ if (a >= 0) {
+ if (b >= 0) return a - b;
+ else {
+ /* a>=0, b<0 */
+
+ ulongword A = (ulongword)a + -(b + 1);
+ return A >= MAX_LONGWORD ? MAX_LONGWORD : (A + 1);
+ }
+ }
+ else if (b <= 0) return a - b;
+ else {
+ /* a<0, b>0 */
+
+ ulongword A = (ulongword)-(a + 1) + b;
+ return A >= MAX_LONGWORD ? MIN_LONGWORD : -(longword)A - 1;
+ }
+}
+
+static unsigned char const bitoff[ 256 ] = {
+ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+word gsm_norm P1((a), longword a )
+/*
+ * the number of left shifts needed to normalize the 32 bit
+ * variable L_var1 for positive values on the interval
+ *
+ * with minimum of
+ * minimum of 1073741824 (01000000000000000000000000000000) and
+ * maximum of 2147483647 (01111111111111111111111111111111)
+ *
+ *
+ * and for negative values on the interval with
+ * minimum of -2147483648 (-10000000000000000000000000000000) and
+ * maximum of -1073741824 ( -1000000000000000000000000000000).
+ *
+ * in order to normalize the result, the following
+ * operation must be done: L_norm_var1 = L_var1 << norm( L_var1 );
+ *
+ * (That's 'ffs', only from the left, not the right..)
+ */
+{
+ assert(a != 0);
+
+ if (a < 0) {
+ if (a <= -1073741824) return 0;
+ a = ~a;
+ }
+
+ return a & 0xffff0000
+ ? ( a & 0xff000000
+ ? -1 + bitoff[ 0xFF & (a >> 24) ]
+ : 7 + bitoff[ 0xFF & (a >> 16) ] )
+ : ( a & 0xff00
+ ? 15 + bitoff[ 0xFF & (a >> 8) ]
+ : 23 + bitoff[ 0xFF & a ] );
+}
+
+longword gsm_L_asl P2((a,n), longword a, int n)
+{
+ if (n >= 32) return 0;
+ if (n <= -32) return -(a < 0);
+ if (n < 0) return gsm_L_asr(a, -n);
+ return a << n;
+}
+
+word gsm_asl P2((a,n), word a, int n)
+{
+ if (n >= 16) return 0;
+ if (n <= -16) return -(a < 0);
+ if (n < 0) return gsm_asr(a, -n);
+ return a << n;
+}
+
+longword gsm_L_asr P2((a,n), longword a, int n)
+{
+ if (n >= 32) return -(a < 0);
+ if (n <= -32) return 0;
+ if (n < 0) return a << -n;
+
+# ifdef SASR
+ return a >> n;
+# else
+ if (a >= 0) return a >> n;
+ else return -(longword)( -(ulongword)a >> n );
+# endif
+}
+
+word gsm_asr P2((a,n), word a, int n)
+{
+ if (n >= 16) return -(a < 0);
+ if (n <= -16) return 0;
+ if (n < 0) return a << -n;
+
+# ifdef SASR
+ return a >> n;
+# else
+ if (a >= 0) return a >> n;
+ else return -(word)( -(uword)a >> n );
+# endif
+}
+
+/*
+ * (From p. 46, end of section 4.2.5)
+ *
+ * NOTE: The following lines gives [sic] one correct implementation
+ * of the div(num, denum) arithmetic operation. Compute div
+ * which is the integer division of num by denum: with denum
+ * >= num > 0
+ */
+
+word gsm_div P2((num,denum), word num, word denum)
+{
+ longword L_num = num;
+ longword L_denum = denum;
+ word div = 0;
+ int k = 15;
+
+ /* The parameter num sometimes becomes zero.
+ * Although this is explicitly guarded against in 4.2.5,
+ * we assume that the result should then be zero as well.
+ */
+
+ /* assert(num != 0); */
+
+ assert(num >= 0 && denum >= num);
+ if (num == 0)
+ return 0;
+
+ while (k--) {
+ div <<= 1;
+ L_num <<= 1;
+
+ if (L_num >= L_denum) {
+ L_num -= L_denum;
+ div++;
+ }
+ }
+
+ return div;
+}
diff --git a/src/audio/gsm/src/code.cpp b/src/audio/gsm/src/code.cpp
new file mode 100644
index 0000000..5082829
--- /dev/null
+++ b/src/audio/gsm/src/code.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/code.c,v 1.3 1996/07/02 09:59:05 jutta Exp $ */
+
+#include "config.h"
+#include <string.h>
+
+
+#ifdef HAS_STDLIB_H
+#include <stdlib.h>
+#else
+# include "proto.h"
+ extern char * memcpy P((char *, char *, int));
+#endif
+
+#include "private.h"
+#include "gsm.h"
+#include "proto.h"
+
+/*
+ * 4.2 FIXED POINT IMPLEMENTATION OF THE RPE-LTP CODER
+ */
+
+void Gsm_Coder P8((S,s,LARc,Nc,bc,Mc,xmaxc,xMc),
+
+ struct gsm_state * S,
+
+ word * s, /* [0..159] samples IN */
+
+/*
+ * The RPE-LTD coder works on a frame by frame basis. The length of
+ * the frame is equal to 160 samples. Some computations are done
+ * once per frame to produce at the output of the coder the
+ * LARc[1..8] parameters which are the coded LAR coefficients and
+ * also to realize the inverse filtering operation for the entire
+ * frame (160 samples of signal d[0..159]). These parts produce at
+ * the output of the coder:
+ */
+
+ word * LARc, /* [0..7] LAR coefficients OUT */
+
+/*
+ * Procedure 4.2.11 to 4.2.18 are to be executed four times per
+ * frame. That means once for each sub-segment RPE-LTP analysis of
+ * 40 samples. These parts produce at the output of the coder:
+ */
+
+ word * Nc, /* [0..3] LTP lag OUT */
+ word * bc, /* [0..3] coded LTP gain OUT */
+ word * Mc, /* [0..3] RPE grid selection OUT */
+ word * xmaxc,/* [0..3] Coded maximum amplitude OUT */
+ word * xMc /* [13*4] normalized RPE samples OUT */
+)
+{
+ int k;
+ word * dp = S->dp0 + 120; /* [ -120...-1 ] */
+ word * dpp = dp; /* [ 0...39 ] */
+
+ static word e[50];
+
+ word so[160];
+
+ Gsm_Preprocess (S, s, so);
+ Gsm_LPC_Analysis (S, so, LARc);
+ Gsm_Short_Term_Analysis_Filter (S, LARc, so);
+
+ for (k = 0; k <= 3; k++, xMc += 13) {
+
+ Gsm_Long_Term_Predictor ( S,
+ so+k*40, /* d [0..39] IN */
+ dp, /* dp [-120..-1] IN */
+ e + 5, /* e [0..39] OUT */
+ dpp, /* dpp [0..39] OUT */
+ Nc++,
+ bc++);
+
+ Gsm_RPE_Encoding ( S,
+ e + 5, /* e ][0..39][ IN/OUT */
+ xmaxc++, Mc++, xMc );
+ /*
+ * Gsm_Update_of_reconstructed_short_time_residual_signal
+ * ( dpp, e + 5, dp );
+ */
+
+ { register int i;
+ register longword ltmp;
+ for (i = 0; i <= 39; i++)
+ dp[ i ] = GSM_ADD( e[5 + i], dpp[i] );
+ }
+ dp += 40;
+ dpp += 40;
+
+ }
+ (void)memcpy( (char *)S->dp0, (char *)(S->dp0 + 160),
+ 120 * sizeof(*S->dp0) );
+}
diff --git a/src/audio/gsm/src/debug.cpp b/src/audio/gsm/src/debug.cpp
new file mode 100644
index 0000000..04c3907
--- /dev/null
+++ b/src/audio/gsm/src/debug.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/debug.c,v 1.2 1993/01/29 18:22:20 jutta Exp $ */
+
+#include "private.h"
+
+#ifndef NDEBUG
+
+/* If NDEBUG _is_ defined and no debugging should be performed,
+ * calls to functions in this module are #defined to nothing
+ * in private.h.
+ */
+
+#include <stdio.h>
+#include "proto.h"
+
+void gsm_debug_words P4( (name, from, to, ptr),
+ char * name,
+ int from,
+ int to,
+ word * ptr)
+{
+ int nprinted = 0;
+
+ fprintf( stderr, "%s [%d .. %d]: ", name, from, to );
+ while (from <= to) {
+ fprintf(stderr, "%d ", ptr[ from ] );
+ from++;
+ if (nprinted++ >= 7) {
+ nprinted = 0;
+ if (from < to) putc('\n', stderr);
+ }
+ }
+ putc('\n', stderr);
+}
+
+void gsm_debug_longwords P4( (name, from, to, ptr),
+ char * name,
+ int from,
+ int to,
+ longword * ptr)
+{
+ int nprinted = 0;
+
+ fprintf( stderr, "%s [%d .. %d]: ", name, from, to );
+ while (from <= to) {
+
+ fprintf(stderr, "%d ", ptr[ from ] );
+ from++;
+ if (nprinted++ >= 7) {
+ nprinted = 0;
+ if (from < to) putc('\n', stderr);
+ }
+ }
+ putc('\n', stderr);
+}
+
+void gsm_debug_longword P2( (name, value),
+ char * name,
+ longword value )
+{
+ fprintf(stderr, "%s: %d\n", name, (long)value );
+}
+
+void gsm_debug_word P2( (name, value),
+ char * name,
+ word value )
+{
+ fprintf(stderr, "%s: %d\n", name, (long)value);
+}
+
+#endif
diff --git a/src/audio/gsm/src/decode.cpp b/src/audio/gsm/src/decode.cpp
new file mode 100644
index 0000000..d51c54a
--- /dev/null
+++ b/src/audio/gsm/src/decode.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/decode.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */
+
+#include <stdio.h>
+
+#include "private.h"
+#include "gsm.h"
+#include "proto.h"
+
+/*
+ * 4.3 FIXED POINT IMPLEMENTATION OF THE RPE-LTP DECODER
+ */
+
+static void Postprocessing P2((S,s),
+ struct gsm_state * S,
+ register word * s)
+{
+ register int k;
+ register word msr = S->msr;
+ register longword ltmp; /* for GSM_ADD */
+ register word tmp;
+
+ for (k = 160; k--; s++) {
+ tmp = GSM_MULT_R( msr, 28180 );
+ msr = GSM_ADD(*s, tmp); /* Deemphasis */
+ *s = GSM_ADD(msr, msr) & 0xFFF8; /* Truncation & Upscaling */
+ }
+ S->msr = msr;
+}
+
+void Gsm_Decoder P8((S,LARcr, Ncr,bcr,Mcr,xmaxcr,xMcr,s),
+ struct gsm_state * S,
+
+ word * LARcr, /* [0..7] IN */
+
+ word * Ncr, /* [0..3] IN */
+ word * bcr, /* [0..3] IN */
+ word * Mcr, /* [0..3] IN */
+ word * xmaxcr, /* [0..3] IN */
+ word * xMcr, /* [0..13*4] IN */
+
+ word * s) /* [0..159] OUT */
+{
+ int j, k;
+ word erp[40], wt[160];
+ word * drp = S->dp0 + 120;
+
+ for (j=0; j <= 3; j++, xmaxcr++, bcr++, Ncr++, Mcr++, xMcr += 13) {
+
+ Gsm_RPE_Decoding( S, *xmaxcr, *Mcr, xMcr, erp );
+ Gsm_Long_Term_Synthesis_Filtering( S, *Ncr, *bcr, erp, drp );
+
+ for (k = 0; k <= 39; k++) wt[ j * 40 + k ] = drp[ k ];
+ }
+
+ Gsm_Short_Term_Synthesis_Filter( S, LARcr, wt, s );
+ Postprocessing(S, s);
+}
diff --git a/src/audio/gsm/src/gsm_create.cpp b/src/audio/gsm/src/gsm_create.cpp
new file mode 100644
index 0000000..a0bf634
--- /dev/null
+++ b/src/audio/gsm/src/gsm_create.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+static char const ident[] = "$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_create.c,v 1.4 1996/07/02 09:59:05 jutta Exp $";
+
+#include "config.h"
+
+#ifdef HAS_STRING_H
+#include <string.h>
+#else
+# include "proto.h"
+ extern char * memset P((char *, int, int));
+#endif
+
+#ifdef HAS_STDLIB_H
+# include <stdlib.h>
+#else
+# ifdef HAS_MALLOC_H
+# include <malloc.h>
+# else
+ extern char * malloc();
+# endif
+#endif
+
+#include <stdio.h>
+
+#include "gsm.h"
+#include "private.h"
+#include "proto.h"
+
+gsm gsm_create P0()
+{
+ gsm r;
+
+ r = (gsm)malloc(sizeof(struct gsm_state));
+ if (!r) return r;
+
+ memset((char *)r, 0, sizeof(*r));
+ r->nrp = 40;
+
+ return r;
+}
diff --git a/src/audio/gsm/src/gsm_decode.cpp b/src/audio/gsm/src/gsm_decode.cpp
new file mode 100644
index 0000000..973c08b
--- /dev/null
+++ b/src/audio/gsm/src/gsm_decode.cpp
@@ -0,0 +1,361 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_decode.c,v 1.2 1996/07/02 09:59:05 jutta Exp $ */
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+int gsm_decode P3((s, c, target), gsm s, gsm_byte * c, gsm_signal * target)
+{
+ word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4];
+
+#ifdef WAV49
+ if (s->wav_fmt) {
+
+ uword sr = 0;
+
+ s->frame_index = !s->frame_index;
+ if (s->frame_index) {
+
+ sr = *c++;
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 2;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 4;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 2;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2; /* 5 */
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+ xmc[0] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 10 */
+ xmc[6] = sr & 0x7; sr >>= 3;
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+ xmc[13] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 15 */
+ xmc[14] = sr & 0x7; sr >>= 3;
+ xmc[15] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4; /* 20 */
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+ xmc[26] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 25 */
+ xmc[35] = sr & 0x7; sr >>= 3;
+ xmc[36] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+ xmc[39] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 30 */
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ xmc[44] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+
+ s->frame_chain = sr & 0xf;
+ }
+ else {
+ sr = s->frame_chain;
+ sr |= (uword)*c++ << 4; /* 1 */
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr = *c++;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 3;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 5 */
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+ xmc[0] = sr & 0x7; sr >>= 3;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ xmc[6] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 10 */
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+ xmc[13] = sr & 0x7; sr >>= 3;
+ xmc[14] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 15 */
+ xmc[15] = sr & 0x7; sr >>= 3;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1; /* 20 */
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+ xmc[26] = sr & 0x7; sr >>= 3;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ xmc[35] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 25 */
+ xmc[36] = sr & 0x7; sr >>= 3;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+ xmc[39] = sr & 0x7; sr >>= 3;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 30 */
+ xmc[44] = sr & 0x7; sr >>= 3;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+ }
+ }
+ else
+#endif
+ {
+ /* GSM_MAGIC = (*c >> 4) & 0xF; */
+
+ if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1;
+
+ LARc[0] = (*c++ & 0xF) << 2; /* 1 */
+ LARc[0] |= (*c >> 6) & 0x3;
+ LARc[1] = *c++ & 0x3F;
+ LARc[2] = (*c >> 3) & 0x1F;
+ LARc[3] = (*c++ & 0x7) << 2;
+ LARc[3] |= (*c >> 6) & 0x3;
+ LARc[4] = (*c >> 2) & 0xF;
+ LARc[5] = (*c++ & 0x3) << 2;
+ LARc[5] |= (*c >> 6) & 0x3;
+ LARc[6] = (*c >> 3) & 0x7;
+ LARc[7] = *c++ & 0x7;
+ Nc[0] = (*c >> 1) & 0x7F;
+ bc[0] = (*c++ & 0x1) << 1;
+ bc[0] |= (*c >> 7) & 0x1;
+ Mc[0] = (*c >> 5) & 0x3;
+ xmaxc[0] = (*c++ & 0x1F) << 1;
+ xmaxc[0] |= (*c >> 7) & 0x1;
+ xmc[0] = (*c >> 4) & 0x7;
+ xmc[1] = (*c >> 1) & 0x7;
+ xmc[2] = (*c++ & 0x1) << 2;
+ xmc[2] |= (*c >> 6) & 0x3;
+ xmc[3] = (*c >> 3) & 0x7;
+ xmc[4] = *c++ & 0x7;
+ xmc[5] = (*c >> 5) & 0x7;
+ xmc[6] = (*c >> 2) & 0x7;
+ xmc[7] = (*c++ & 0x3) << 1; /* 10 */
+ xmc[7] |= (*c >> 7) & 0x1;
+ xmc[8] = (*c >> 4) & 0x7;
+ xmc[9] = (*c >> 1) & 0x7;
+ xmc[10] = (*c++ & 0x1) << 2;
+ xmc[10] |= (*c >> 6) & 0x3;
+ xmc[11] = (*c >> 3) & 0x7;
+ xmc[12] = *c++ & 0x7;
+ Nc[1] = (*c >> 1) & 0x7F;
+ bc[1] = (*c++ & 0x1) << 1;
+ bc[1] |= (*c >> 7) & 0x1;
+ Mc[1] = (*c >> 5) & 0x3;
+ xmaxc[1] = (*c++ & 0x1F) << 1;
+ xmaxc[1] |= (*c >> 7) & 0x1;
+ xmc[13] = (*c >> 4) & 0x7;
+ xmc[14] = (*c >> 1) & 0x7;
+ xmc[15] = (*c++ & 0x1) << 2;
+ xmc[15] |= (*c >> 6) & 0x3;
+ xmc[16] = (*c >> 3) & 0x7;
+ xmc[17] = *c++ & 0x7;
+ xmc[18] = (*c >> 5) & 0x7;
+ xmc[19] = (*c >> 2) & 0x7;
+ xmc[20] = (*c++ & 0x3) << 1;
+ xmc[20] |= (*c >> 7) & 0x1;
+ xmc[21] = (*c >> 4) & 0x7;
+ xmc[22] = (*c >> 1) & 0x7;
+ xmc[23] = (*c++ & 0x1) << 2;
+ xmc[23] |= (*c >> 6) & 0x3;
+ xmc[24] = (*c >> 3) & 0x7;
+ xmc[25] = *c++ & 0x7;
+ Nc[2] = (*c >> 1) & 0x7F;
+ bc[2] = (*c++ & 0x1) << 1; /* 20 */
+ bc[2] |= (*c >> 7) & 0x1;
+ Mc[2] = (*c >> 5) & 0x3;
+ xmaxc[2] = (*c++ & 0x1F) << 1;
+ xmaxc[2] |= (*c >> 7) & 0x1;
+ xmc[26] = (*c >> 4) & 0x7;
+ xmc[27] = (*c >> 1) & 0x7;
+ xmc[28] = (*c++ & 0x1) << 2;
+ xmc[28] |= (*c >> 6) & 0x3;
+ xmc[29] = (*c >> 3) & 0x7;
+ xmc[30] = *c++ & 0x7;
+ xmc[31] = (*c >> 5) & 0x7;
+ xmc[32] = (*c >> 2) & 0x7;
+ xmc[33] = (*c++ & 0x3) << 1;
+ xmc[33] |= (*c >> 7) & 0x1;
+ xmc[34] = (*c >> 4) & 0x7;
+ xmc[35] = (*c >> 1) & 0x7;
+ xmc[36] = (*c++ & 0x1) << 2;
+ xmc[36] |= (*c >> 6) & 0x3;
+ xmc[37] = (*c >> 3) & 0x7;
+ xmc[38] = *c++ & 0x7;
+ Nc[3] = (*c >> 1) & 0x7F;
+ bc[3] = (*c++ & 0x1) << 1;
+ bc[3] |= (*c >> 7) & 0x1;
+ Mc[3] = (*c >> 5) & 0x3;
+ xmaxc[3] = (*c++ & 0x1F) << 1;
+ xmaxc[3] |= (*c >> 7) & 0x1;
+ xmc[39] = (*c >> 4) & 0x7;
+ xmc[40] = (*c >> 1) & 0x7;
+ xmc[41] = (*c++ & 0x1) << 2;
+ xmc[41] |= (*c >> 6) & 0x3;
+ xmc[42] = (*c >> 3) & 0x7;
+ xmc[43] = *c++ & 0x7; /* 30 */
+ xmc[44] = (*c >> 5) & 0x7;
+ xmc[45] = (*c >> 2) & 0x7;
+ xmc[46] = (*c++ & 0x3) << 1;
+ xmc[46] |= (*c >> 7) & 0x1;
+ xmc[47] = (*c >> 4) & 0x7;
+ xmc[48] = (*c >> 1) & 0x7;
+ xmc[49] = (*c++ & 0x1) << 2;
+ xmc[49] |= (*c >> 6) & 0x3;
+ xmc[50] = (*c >> 3) & 0x7;
+ xmc[51] = *c & 0x7; /* 33 */
+ }
+
+ Gsm_Decoder(s, LARc, Nc, bc, Mc, xmaxc, xmc, target);
+
+ return 0;
+}
diff --git a/src/audio/gsm/src/gsm_destroy.cpp b/src/audio/gsm/src/gsm_destroy.cpp
new file mode 100644
index 0000000..03c8659
--- /dev/null
+++ b/src/audio/gsm/src/gsm_destroy.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_destroy.c,v 1.3 1994/11/28 19:52:25 jutta Exp $ */
+
+#include "gsm.h"
+#include "config.h"
+#include "proto.h"
+
+#ifdef HAS_STDLIB_H
+# include <stdlib.h>
+#else
+# ifdef HAS_MALLOC_H
+# include <malloc.h>
+# else
+ extern void free();
+# endif
+#endif
+
+void gsm_destroy P1((S), gsm S)
+{
+ if (S) free((char *)S);
+}
diff --git a/src/audio/gsm/src/gsm_encode.cpp b/src/audio/gsm/src/gsm_encode.cpp
new file mode 100644
index 0000000..51bc556
--- /dev/null
+++ b/src/audio/gsm/src/gsm_encode.cpp
@@ -0,0 +1,451 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_encode.c,v 1.2 1996/07/02 09:59:05 jutta Exp $ */
+
+#include "private.h"
+#include "gsm.h"
+#include "proto.h"
+
+void gsm_encode P3((s, source, c), gsm s, gsm_signal * source, gsm_byte * c)
+{
+ word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4];
+
+ Gsm_Coder(s, source, LARc, Nc, bc, Mc, xmaxc, xmc);
+
+
+ /* variable size
+
+ GSM_MAGIC 4
+
+ LARc[0] 6
+ LARc[1] 6
+ LARc[2] 5
+ LARc[3] 5
+ LARc[4] 4
+ LARc[5] 4
+ LARc[6] 3
+ LARc[7] 3
+
+ Nc[0] 7
+ bc[0] 2
+ Mc[0] 2
+ xmaxc[0] 6
+ xmc[0] 3
+ xmc[1] 3
+ xmc[2] 3
+ xmc[3] 3
+ xmc[4] 3
+ xmc[5] 3
+ xmc[6] 3
+ xmc[7] 3
+ xmc[8] 3
+ xmc[9] 3
+ xmc[10] 3
+ xmc[11] 3
+ xmc[12] 3
+
+ Nc[1] 7
+ bc[1] 2
+ Mc[1] 2
+ xmaxc[1] 6
+ xmc[13] 3
+ xmc[14] 3
+ xmc[15] 3
+ xmc[16] 3
+ xmc[17] 3
+ xmc[18] 3
+ xmc[19] 3
+ xmc[20] 3
+ xmc[21] 3
+ xmc[22] 3
+ xmc[23] 3
+ xmc[24] 3
+ xmc[25] 3
+
+ Nc[2] 7
+ bc[2] 2
+ Mc[2] 2
+ xmaxc[2] 6
+ xmc[26] 3
+ xmc[27] 3
+ xmc[28] 3
+ xmc[29] 3
+ xmc[30] 3
+ xmc[31] 3
+ xmc[32] 3
+ xmc[33] 3
+ xmc[34] 3
+ xmc[35] 3
+ xmc[36] 3
+ xmc[37] 3
+ xmc[38] 3
+
+ Nc[3] 7
+ bc[3] 2
+ Mc[3] 2
+ xmaxc[3] 6
+ xmc[39] 3
+ xmc[40] 3
+ xmc[41] 3
+ xmc[42] 3
+ xmc[43] 3
+ xmc[44] 3
+ xmc[45] 3
+ xmc[46] 3
+ xmc[47] 3
+ xmc[48] 3
+ xmc[49] 3
+ xmc[50] 3
+ xmc[51] 3
+ */
+
+#ifdef WAV49
+
+ if (s->wav_fmt) {
+ s->frame_index = !s->frame_index;
+ if (s->frame_index) {
+
+ uword sr;
+
+ sr = 0;
+ sr = sr >> 6 | LARc[0] << 10;
+ sr = sr >> 6 | LARc[1] << 10;
+ *c++ = sr >> 4;
+ sr = sr >> 5 | LARc[2] << 11;
+ *c++ = sr >> 7;
+ sr = sr >> 5 | LARc[3] << 11;
+ sr = sr >> 4 | LARc[4] << 12;
+ *c++ = sr >> 6;
+ sr = sr >> 4 | LARc[5] << 12;
+ sr = sr >> 3 | LARc[6] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | LARc[7] << 13;
+ sr = sr >> 7 | Nc[0] << 9;
+ *c++ = sr >> 5;
+ sr = sr >> 2 | bc[0] << 14;
+ sr = sr >> 2 | Mc[0] << 14;
+ sr = sr >> 6 | xmaxc[0] << 10;
+ *c++ = sr >> 3;
+ sr = sr >> 3 | xmc[0] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[1] << 13;
+ sr = sr >> 3 | xmc[2] << 13;
+ sr = sr >> 3 | xmc[3] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[4] << 13;
+ sr = sr >> 3 | xmc[5] << 13;
+ sr = sr >> 3 | xmc[6] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[7] << 13;
+ sr = sr >> 3 | xmc[8] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[9] << 13;
+ sr = sr >> 3 | xmc[10] << 13;
+ sr = sr >> 3 | xmc[11] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[12] << 13;
+ sr = sr >> 7 | Nc[1] << 9;
+ *c++ = sr >> 5;
+ sr = sr >> 2 | bc[1] << 14;
+ sr = sr >> 2 | Mc[1] << 14;
+ sr = sr >> 6 | xmaxc[1] << 10;
+ *c++ = sr >> 3;
+ sr = sr >> 3 | xmc[13] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[14] << 13;
+ sr = sr >> 3 | xmc[15] << 13;
+ sr = sr >> 3 | xmc[16] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[17] << 13;
+ sr = sr >> 3 | xmc[18] << 13;
+ sr = sr >> 3 | xmc[19] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[20] << 13;
+ sr = sr >> 3 | xmc[21] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[22] << 13;
+ sr = sr >> 3 | xmc[23] << 13;
+ sr = sr >> 3 | xmc[24] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[25] << 13;
+ sr = sr >> 7 | Nc[2] << 9;
+ *c++ = sr >> 5;
+ sr = sr >> 2 | bc[2] << 14;
+ sr = sr >> 2 | Mc[2] << 14;
+ sr = sr >> 6 | xmaxc[2] << 10;
+ *c++ = sr >> 3;
+ sr = sr >> 3 | xmc[26] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[27] << 13;
+ sr = sr >> 3 | xmc[28] << 13;
+ sr = sr >> 3 | xmc[29] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[30] << 13;
+ sr = sr >> 3 | xmc[31] << 13;
+ sr = sr >> 3 | xmc[32] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[33] << 13;
+ sr = sr >> 3 | xmc[34] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[35] << 13;
+ sr = sr >> 3 | xmc[36] << 13;
+ sr = sr >> 3 | xmc[37] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[38] << 13;
+ sr = sr >> 7 | Nc[3] << 9;
+ *c++ = sr >> 5;
+ sr = sr >> 2 | bc[3] << 14;
+ sr = sr >> 2 | Mc[3] << 14;
+ sr = sr >> 6 | xmaxc[3] << 10;
+ *c++ = sr >> 3;
+ sr = sr >> 3 | xmc[39] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[40] << 13;
+ sr = sr >> 3 | xmc[41] << 13;
+ sr = sr >> 3 | xmc[42] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[43] << 13;
+ sr = sr >> 3 | xmc[44] << 13;
+ sr = sr >> 3 | xmc[45] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[46] << 13;
+ sr = sr >> 3 | xmc[47] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[48] << 13;
+ sr = sr >> 3 | xmc[49] << 13;
+ sr = sr >> 3 | xmc[50] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[51] << 13;
+ sr = sr >> 4;
+ *c = sr >> 8;
+ s->frame_chain = *c;
+ }
+ else {
+ uword sr;
+
+ sr = 0;
+ sr = sr >> 4 | s->frame_chain << 12;
+ sr = sr >> 6 | LARc[0] << 10;
+ *c++ = sr >> 6;
+ sr = sr >> 6 | LARc[1] << 10;
+ *c++ = sr >> 8;
+ sr = sr >> 5 | LARc[2] << 11;
+ sr = sr >> 5 | LARc[3] << 11;
+ *c++ = sr >> 6;
+ sr = sr >> 4 | LARc[4] << 12;
+ sr = sr >> 4 | LARc[5] << 12;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | LARc[6] << 13;
+ sr = sr >> 3 | LARc[7] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 7 | Nc[0] << 9;
+ sr = sr >> 2 | bc[0] << 14;
+ *c++ = sr >> 7;
+ sr = sr >> 2 | Mc[0] << 14;
+ sr = sr >> 6 | xmaxc[0] << 10;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[0] << 13;
+ sr = sr >> 3 | xmc[1] << 13;
+ sr = sr >> 3 | xmc[2] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[3] << 13;
+ sr = sr >> 3 | xmc[4] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[5] << 13;
+ sr = sr >> 3 | xmc[6] << 13;
+ sr = sr >> 3 | xmc[7] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[8] << 13;
+ sr = sr >> 3 | xmc[9] << 13;
+ sr = sr >> 3 | xmc[10] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[11] << 13;
+ sr = sr >> 3 | xmc[12] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 7 | Nc[1] << 9;
+ sr = sr >> 2 | bc[1] << 14;
+ *c++ = sr >> 7;
+ sr = sr >> 2 | Mc[1] << 14;
+ sr = sr >> 6 | xmaxc[1] << 10;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[13] << 13;
+ sr = sr >> 3 | xmc[14] << 13;
+ sr = sr >> 3 | xmc[15] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[16] << 13;
+ sr = sr >> 3 | xmc[17] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[18] << 13;
+ sr = sr >> 3 | xmc[19] << 13;
+ sr = sr >> 3 | xmc[20] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[21] << 13;
+ sr = sr >> 3 | xmc[22] << 13;
+ sr = sr >> 3 | xmc[23] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[24] << 13;
+ sr = sr >> 3 | xmc[25] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 7 | Nc[2] << 9;
+ sr = sr >> 2 | bc[2] << 14;
+ *c++ = sr >> 7;
+ sr = sr >> 2 | Mc[2] << 14;
+ sr = sr >> 6 | xmaxc[2] << 10;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[26] << 13;
+ sr = sr >> 3 | xmc[27] << 13;
+ sr = sr >> 3 | xmc[28] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[29] << 13;
+ sr = sr >> 3 | xmc[30] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[31] << 13;
+ sr = sr >> 3 | xmc[32] << 13;
+ sr = sr >> 3 | xmc[33] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[34] << 13;
+ sr = sr >> 3 | xmc[35] << 13;
+ sr = sr >> 3 | xmc[36] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[37] << 13;
+ sr = sr >> 3 | xmc[38] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 7 | Nc[3] << 9;
+ sr = sr >> 2 | bc[3] << 14;
+ *c++ = sr >> 7;
+ sr = sr >> 2 | Mc[3] << 14;
+ sr = sr >> 6 | xmaxc[3] << 10;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[39] << 13;
+ sr = sr >> 3 | xmc[40] << 13;
+ sr = sr >> 3 | xmc[41] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[42] << 13;
+ sr = sr >> 3 | xmc[43] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[44] << 13;
+ sr = sr >> 3 | xmc[45] << 13;
+ sr = sr >> 3 | xmc[46] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[47] << 13;
+ sr = sr >> 3 | xmc[48] << 13;
+ sr = sr >> 3 | xmc[49] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[50] << 13;
+ sr = sr >> 3 | xmc[51] << 13;
+ *c++ = sr >> 8;
+ }
+ }
+
+ else
+
+#endif /* WAV49 */
+ {
+
+ *c++ = ((GSM_MAGIC & 0xF) << 4) /* 1 */
+ | ((LARc[0] >> 2) & 0xF);
+ *c++ = ((LARc[0] & 0x3) << 6)
+ | (LARc[1] & 0x3F);
+ *c++ = ((LARc[2] & 0x1F) << 3)
+ | ((LARc[3] >> 2) & 0x7);
+ *c++ = ((LARc[3] & 0x3) << 6)
+ | ((LARc[4] & 0xF) << 2)
+ | ((LARc[5] >> 2) & 0x3);
+ *c++ = ((LARc[5] & 0x3) << 6)
+ | ((LARc[6] & 0x7) << 3)
+ | (LARc[7] & 0x7);
+ *c++ = ((Nc[0] & 0x7F) << 1)
+ | ((bc[0] >> 1) & 0x1);
+ *c++ = ((bc[0] & 0x1) << 7)
+ | ((Mc[0] & 0x3) << 5)
+ | ((xmaxc[0] >> 1) & 0x1F);
+ *c++ = ((xmaxc[0] & 0x1) << 7)
+ | ((xmc[0] & 0x7) << 4)
+ | ((xmc[1] & 0x7) << 1)
+ | ((xmc[2] >> 2) & 0x1);
+ *c++ = ((xmc[2] & 0x3) << 6)
+ | ((xmc[3] & 0x7) << 3)
+ | (xmc[4] & 0x7);
+ *c++ = ((xmc[5] & 0x7) << 5) /* 10 */
+ | ((xmc[6] & 0x7) << 2)
+ | ((xmc[7] >> 1) & 0x3);
+ *c++ = ((xmc[7] & 0x1) << 7)
+ | ((xmc[8] & 0x7) << 4)
+ | ((xmc[9] & 0x7) << 1)
+ | ((xmc[10] >> 2) & 0x1);
+ *c++ = ((xmc[10] & 0x3) << 6)
+ | ((xmc[11] & 0x7) << 3)
+ | (xmc[12] & 0x7);
+ *c++ = ((Nc[1] & 0x7F) << 1)
+ | ((bc[1] >> 1) & 0x1);
+ *c++ = ((bc[1] & 0x1) << 7)
+ | ((Mc[1] & 0x3) << 5)
+ | ((xmaxc[1] >> 1) & 0x1F);
+ *c++ = ((xmaxc[1] & 0x1) << 7)
+ | ((xmc[13] & 0x7) << 4)
+ | ((xmc[14] & 0x7) << 1)
+ | ((xmc[15] >> 2) & 0x1);
+ *c++ = ((xmc[15] & 0x3) << 6)
+ | ((xmc[16] & 0x7) << 3)
+ | (xmc[17] & 0x7);
+ *c++ = ((xmc[18] & 0x7) << 5)
+ | ((xmc[19] & 0x7) << 2)
+ | ((xmc[20] >> 1) & 0x3);
+ *c++ = ((xmc[20] & 0x1) << 7)
+ | ((xmc[21] & 0x7) << 4)
+ | ((xmc[22] & 0x7) << 1)
+ | ((xmc[23] >> 2) & 0x1);
+ *c++ = ((xmc[23] & 0x3) << 6)
+ | ((xmc[24] & 0x7) << 3)
+ | (xmc[25] & 0x7);
+ *c++ = ((Nc[2] & 0x7F) << 1) /* 20 */
+ | ((bc[2] >> 1) & 0x1);
+ *c++ = ((bc[2] & 0x1) << 7)
+ | ((Mc[2] & 0x3) << 5)
+ | ((xmaxc[2] >> 1) & 0x1F);
+ *c++ = ((xmaxc[2] & 0x1) << 7)
+ | ((xmc[26] & 0x7) << 4)
+ | ((xmc[27] & 0x7) << 1)
+ | ((xmc[28] >> 2) & 0x1);
+ *c++ = ((xmc[28] & 0x3) << 6)
+ | ((xmc[29] & 0x7) << 3)
+ | (xmc[30] & 0x7);
+ *c++ = ((xmc[31] & 0x7) << 5)
+ | ((xmc[32] & 0x7) << 2)
+ | ((xmc[33] >> 1) & 0x3);
+ *c++ = ((xmc[33] & 0x1) << 7)
+ | ((xmc[34] & 0x7) << 4)
+ | ((xmc[35] & 0x7) << 1)
+ | ((xmc[36] >> 2) & 0x1);
+ *c++ = ((xmc[36] & 0x3) << 6)
+ | ((xmc[37] & 0x7) << 3)
+ | (xmc[38] & 0x7);
+ *c++ = ((Nc[3] & 0x7F) << 1)
+ | ((bc[3] >> 1) & 0x1);
+ *c++ = ((bc[3] & 0x1) << 7)
+ | ((Mc[3] & 0x3) << 5)
+ | ((xmaxc[3] >> 1) & 0x1F);
+ *c++ = ((xmaxc[3] & 0x1) << 7)
+ | ((xmc[39] & 0x7) << 4)
+ | ((xmc[40] & 0x7) << 1)
+ | ((xmc[41] >> 2) & 0x1);
+ *c++ = ((xmc[41] & 0x3) << 6) /* 30 */
+ | ((xmc[42] & 0x7) << 3)
+ | (xmc[43] & 0x7);
+ *c++ = ((xmc[44] & 0x7) << 5)
+ | ((xmc[45] & 0x7) << 2)
+ | ((xmc[46] >> 1) & 0x3);
+ *c++ = ((xmc[46] & 0x1) << 7)
+ | ((xmc[47] & 0x7) << 4)
+ | ((xmc[48] & 0x7) << 1)
+ | ((xmc[49] >> 2) & 0x1);
+ *c++ = ((xmc[49] & 0x3) << 6)
+ | ((xmc[50] & 0x7) << 3)
+ | (xmc[51] & 0x7);
+
+ }
+}
diff --git a/src/audio/gsm/src/gsm_explode.cpp b/src/audio/gsm/src/gsm_explode.cpp
new file mode 100644
index 0000000..ca48f9b
--- /dev/null
+++ b/src/audio/gsm/src/gsm_explode.cpp
@@ -0,0 +1,417 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_explode.c,v 1.2 1996/07/02 14:32:42 jutta Exp jutta $ */
+
+#include "private.h"
+#include "gsm.h"
+#include "proto.h"
+
+int gsm_explode P3((s, c, target), gsm s, gsm_byte * c, gsm_signal * target)
+{
+# define LARc target
+# define Nc *((gsm_signal (*) [17])(target + 8))
+# define bc *((gsm_signal (*) [17])(target + 9))
+# define Mc *((gsm_signal (*) [17])(target + 10))
+# define xmaxc *((gsm_signal (*) [17])(target + 11))
+
+
+#ifdef WAV49
+ if (s->wav_fmt) {
+
+ uword sr = 0;
+
+ if (s->frame_index == 1) {
+
+ sr = *c++;
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 2;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 4;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 2;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2; /* 5 */
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (target + 12)
+ xmc[0] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 10 */
+ xmc[6] = sr & 0x7; sr >>= 3;
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (target + 29 - 13)
+
+ xmc[13] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 15 */
+ xmc[14] = sr & 0x7; sr >>= 3;
+ xmc[15] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4; /* 20 */
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+
+#undef xmc
+#define xmc (target + 46 - 26)
+
+ xmc[26] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 25 */
+ xmc[35] = sr & 0x7; sr >>= 3;
+ xmc[36] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (target + 63 - 39)
+
+ xmc[39] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 30 */
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ xmc[44] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+
+ s->frame_chain = sr & 0xf;
+ }
+ else {
+ sr = s->frame_chain;
+ sr |= (uword)*c++ << 4; /* 1 */
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr = *c++;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 3;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 5 */
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (target + 12)
+ xmc[0] = sr & 0x7; sr >>= 3;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ xmc[6] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 10 */
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (target + 29 - 13)
+
+ xmc[13] = sr & 0x7; sr >>= 3;
+ xmc[14] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 15 */
+ xmc[15] = sr & 0x7; sr >>= 3;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1; /* 20 */
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (target + 46 - 26)
+ xmc[26] = sr & 0x7; sr >>= 3;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ xmc[35] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 25 */
+ xmc[36] = sr & 0x7; sr >>= 3;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+
+#undef xmc
+#define xmc (target + 63 - 39)
+
+ xmc[39] = sr & 0x7; sr >>= 3;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 30 */
+ xmc[44] = sr & 0x7; sr >>= 3;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+ }
+ }
+ else
+#endif
+ {
+ /* GSM_MAGIC = (*c >> 4) & 0xF; */
+
+ if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1;
+
+ LARc[0] = (*c++ & 0xF) << 2; /* 1 */
+ LARc[0] |= (*c >> 6) & 0x3;
+ LARc[1] = *c++ & 0x3F;
+ LARc[2] = (*c >> 3) & 0x1F;
+ LARc[3] = (*c++ & 0x7) << 2;
+ LARc[3] |= (*c >> 6) & 0x3;
+ LARc[4] = (*c >> 2) & 0xF;
+ LARc[5] = (*c++ & 0x3) << 2;
+ LARc[5] |= (*c >> 6) & 0x3;
+ LARc[6] = (*c >> 3) & 0x7;
+ LARc[7] = *c++ & 0x7;
+
+ Nc[0] = (*c >> 1) & 0x7F;
+
+ bc[0] = (*c++ & 0x1) << 1;
+ bc[0] |= (*c >> 7) & 0x1;
+
+ Mc[0] = (*c >> 5) & 0x3;
+
+ xmaxc[0] = (*c++ & 0x1F) << 1;
+ xmaxc[0] |= (*c >> 7) & 0x1;
+
+#undef xmc
+#define xmc (target + 12)
+
+ xmc[0] = (*c >> 4) & 0x7;
+ xmc[1] = (*c >> 1) & 0x7;
+ xmc[2] = (*c++ & 0x1) << 2;
+ xmc[2] |= (*c >> 6) & 0x3;
+ xmc[3] = (*c >> 3) & 0x7;
+ xmc[4] = *c++ & 0x7;
+ xmc[5] = (*c >> 5) & 0x7;
+ xmc[6] = (*c >> 2) & 0x7;
+ xmc[7] = (*c++ & 0x3) << 1; /* 10 */
+ xmc[7] |= (*c >> 7) & 0x1;
+ xmc[8] = (*c >> 4) & 0x7;
+ xmc[9] = (*c >> 1) & 0x7;
+ xmc[10] = (*c++ & 0x1) << 2;
+ xmc[10] |= (*c >> 6) & 0x3;
+ xmc[11] = (*c >> 3) & 0x7;
+ xmc[12] = *c++ & 0x7;
+
+ Nc[1] = (*c >> 1) & 0x7F;
+
+ bc[1] = (*c++ & 0x1) << 1;
+ bc[1] |= (*c >> 7) & 0x1;
+
+ Mc[1] = (*c >> 5) & 0x3;
+
+ xmaxc[1] = (*c++ & 0x1F) << 1;
+ xmaxc[1] |= (*c >> 7) & 0x1;
+
+#undef xmc
+#define xmc (target + 29 - 13)
+
+ xmc[13] = (*c >> 4) & 0x7;
+ xmc[14] = (*c >> 1) & 0x7;
+ xmc[15] = (*c++ & 0x1) << 2;
+ xmc[15] |= (*c >> 6) & 0x3;
+ xmc[16] = (*c >> 3) & 0x7;
+ xmc[17] = *c++ & 0x7;
+ xmc[18] = (*c >> 5) & 0x7;
+ xmc[19] = (*c >> 2) & 0x7;
+ xmc[20] = (*c++ & 0x3) << 1;
+ xmc[20] |= (*c >> 7) & 0x1;
+ xmc[21] = (*c >> 4) & 0x7;
+ xmc[22] = (*c >> 1) & 0x7;
+ xmc[23] = (*c++ & 0x1) << 2;
+ xmc[23] |= (*c >> 6) & 0x3;
+ xmc[24] = (*c >> 3) & 0x7;
+ xmc[25] = *c++ & 0x7;
+
+ Nc[2] = (*c >> 1) & 0x7F;
+
+ bc[2] = (*c++ & 0x1) << 1; /* 20 */
+ bc[2] |= (*c >> 7) & 0x1;
+
+ Mc[2] = (*c >> 5) & 0x3;
+
+ xmaxc[2] = (*c++ & 0x1F) << 1;
+ xmaxc[2] |= (*c >> 7) & 0x1;
+
+#undef xmc
+#define xmc (target + 46 - 26)
+
+ xmc[26] = (*c >> 4) & 0x7;
+ xmc[27] = (*c >> 1) & 0x7;
+ xmc[28] = (*c++ & 0x1) << 2;
+ xmc[28] |= (*c >> 6) & 0x3;
+ xmc[29] = (*c >> 3) & 0x7;
+ xmc[30] = *c++ & 0x7;
+ xmc[31] = (*c >> 5) & 0x7;
+ xmc[32] = (*c >> 2) & 0x7;
+ xmc[33] = (*c++ & 0x3) << 1;
+ xmc[33] |= (*c >> 7) & 0x1;
+ xmc[34] = (*c >> 4) & 0x7;
+ xmc[35] = (*c >> 1) & 0x7;
+ xmc[36] = (*c++ & 0x1) << 2;
+ xmc[36] |= (*c >> 6) & 0x3;
+ xmc[37] = (*c >> 3) & 0x7;
+ xmc[38] = *c++ & 0x7;
+
+ Nc[3] = (*c >> 1) & 0x7F;
+
+ bc[3] = (*c++ & 0x1) << 1;
+ bc[3] |= (*c >> 7) & 0x1;
+
+ Mc[3] = (*c >> 5) & 0x3;
+
+ xmaxc[3] = (*c++ & 0x1F) << 1;
+ xmaxc[3] |= (*c >> 7) & 0x1;
+
+#undef xmc
+#define xmc (target + 63 - 39)
+
+ xmc[39] = (*c >> 4) & 0x7;
+ xmc[40] = (*c >> 1) & 0x7;
+ xmc[41] = (*c++ & 0x1) << 2;
+ xmc[41] |= (*c >> 6) & 0x3;
+ xmc[42] = (*c >> 3) & 0x7;
+ xmc[43] = *c++ & 0x7; /* 30 */
+ xmc[44] = (*c >> 5) & 0x7;
+ xmc[45] = (*c >> 2) & 0x7;
+ xmc[46] = (*c++ & 0x3) << 1;
+ xmc[46] |= (*c >> 7) & 0x1;
+ xmc[47] = (*c >> 4) & 0x7;
+ xmc[48] = (*c >> 1) & 0x7;
+ xmc[49] = (*c++ & 0x1) << 2;
+ xmc[49] |= (*c >> 6) & 0x3;
+ xmc[50] = (*c >> 3) & 0x7;
+ xmc[51] = *c & 0x7; /* 33 */
+ }
+
+ return 0;
+}
diff --git a/src/audio/gsm/src/gsm_implode.cpp b/src/audio/gsm/src/gsm_implode.cpp
new file mode 100644
index 0000000..39b79cb
--- /dev/null
+++ b/src/audio/gsm/src/gsm_implode.cpp
@@ -0,0 +1,515 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_implode.c,v 1.2 1996/07/02 14:32:43 jutta Exp jutta $ */
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+void gsm_implode P3((s, source, c), gsm s, gsm_signal * source, gsm_byte * c)
+{
+ /* variable size index
+
+ GSM_MAGIC 4 -
+
+ LARc[0] 6 0
+ LARc[1] 6 1
+ LARc[2] 5 2
+ LARc[3] 5 3
+ LARc[4] 4 4
+ LARc[5] 4 5
+ LARc[6] 3 6
+ LARc[7] 3 7
+
+ Nc[0] 7 8
+ bc[0] 2 9
+ Mc[0] 2 10
+ xmaxc[0] 6 11
+ xmc[0] 3 12
+ xmc[1] 3 13
+ xmc[2] 3 14
+ xmc[3] 3 15
+ xmc[4] 3 16
+ xmc[5] 3 17
+ xmc[6] 3 18
+ xmc[7] 3 19
+ xmc[8] 3 20
+ xmc[9] 3 21
+ xmc[10] 3 22
+ xmc[11] 3 23
+ xmc[12] 3 24
+
+ Nc[1] 7 25
+ bc[1] 2 26
+ Mc[1] 2 27
+ xmaxc[1] 6 28
+ xmc[13] 3 29
+ xmc[14] 3 30
+ xmc[15] 3 31
+ xmc[16] 3 32
+ xmc[17] 3 33
+ xmc[18] 3 34
+ xmc[19] 3 35
+ xmc[20] 3 36
+ xmc[21] 3 37
+ xmc[22] 3 38
+ xmc[23] 3 39
+ xmc[24] 3 40
+ xmc[25] 3 41
+
+ Nc[2] 7 42
+ bc[2] 2 43
+ Mc[2] 2 44
+ xmaxc[2] 6 45
+ xmc[26] 3 46
+ xmc[27] 3 47
+ xmc[28] 3 48
+ xmc[29] 3 49
+ xmc[30] 3 50
+ xmc[31] 3 51
+ xmc[32] 3 52
+ xmc[33] 3 53
+ xmc[34] 3 54
+ xmc[35] 3 55
+ xmc[36] 3 56
+ xmc[37] 3 57
+ xmc[38] 3 58
+
+ Nc[3] 7 59
+ bc[3] 2 60
+ Mc[3] 2 61
+ xmaxc[3] 6 62
+ xmc[39] 3 63
+ xmc[40] 3 64
+ xmc[41] 3 65
+ xmc[42] 3 66
+ xmc[43] 3 67
+ xmc[44] 3 68
+ xmc[45] 3 69
+ xmc[46] 3 70
+ xmc[47] 3 71
+ xmc[48] 3 72
+ xmc[49] 3 73
+ xmc[50] 3 74
+ xmc[51] 3 75
+ */
+
+ /* There are 76 parameters per frame. The first eight are
+ * unique. The remaining 68 are four identical subframes of
+ * 17 parameters each. gsm_implode converts from a representation
+ * of these parameters as values in one array of signed words
+ * to the "packed" version of a GSM frame.
+ */
+
+# define LARc source
+# define Nc *((gsm_signal (*) [17])(source + 8))
+# define bc *((gsm_signal (*) [17])(source + 9))
+# define Mc *((gsm_signal (*) [17])(source + 10))
+# define xmaxc *((gsm_signal (*) [17])(source + 11))
+
+#ifdef WAV49
+ if (s->wav_fmt) {
+
+ uword sr = 0;
+ if (s->frame_index == 0) {
+
+ sr = *c++;
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 2;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 4;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 2;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2; /* 5 */
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 12)
+ xmc[0] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 10 */
+ xmc[6] = sr & 0x7; sr >>= 3;
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 29 - 13)
+ xmc[13] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 15 */
+ xmc[14] = sr & 0x7; sr >>= 3;
+ xmc[15] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4; /* 20 */
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 46 - 26)
+ xmc[26] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 25 */
+ xmc[35] = sr & 0x7; sr >>= 3;
+ xmc[36] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 63 - 39)
+
+ xmc[39] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 30 */
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ xmc[44] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+
+ s->frame_chain = sr & 0xf;
+ }
+ else {
+ sr = s->frame_chain;
+ sr |= (uword)*c++ << 4; /* 1 */
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr = *c++;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 3;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 5 */
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 12)
+ xmc[0] = sr & 0x7; sr >>= 3;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ xmc[6] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 10 */
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 29 - 13)
+ xmc[13] = sr & 0x7; sr >>= 3;
+ xmc[14] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 15 */
+ xmc[15] = sr & 0x7; sr >>= 3;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1; /* 20 */
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 46 - 26)
+ xmc[26] = sr & 0x7; sr >>= 3;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ xmc[35] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 25 */
+ xmc[36] = sr & 0x7; sr >>= 3;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 63 - 39)
+
+ xmc[39] = sr & 0x7; sr >>= 3;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 30 */
+ xmc[44] = sr & 0x7; sr >>= 3;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+ }
+ }
+ else
+#endif
+ {
+
+ *c++ = ((GSM_MAGIC & 0xF) << 4) /* 1 */
+ | ((LARc[0] >> 2) & 0xF);
+ *c++ = ((LARc[0] & 0x3) << 6)
+ | (LARc[1] & 0x3F);
+ *c++ = ((LARc[2] & 0x1F) << 3)
+ | ((LARc[3] >> 2) & 0x7);
+ *c++ = ((LARc[3] & 0x3) << 6)
+ | ((LARc[4] & 0xF) << 2)
+ | ((LARc[5] >> 2) & 0x3);
+ *c++ = ((LARc[5] & 0x3) << 6)
+ | ((LARc[6] & 0x7) << 3)
+ | (LARc[7] & 0x7);
+
+
+ *c++ = ((Nc[0] & 0x7F) << 1)
+
+
+ | ((bc[0] >> 1) & 0x1);
+ *c++ = ((bc[0] & 0x1) << 7)
+
+
+ | ((Mc[0] & 0x3) << 5)
+
+ | ((xmaxc[0] >> 1) & 0x1F);
+ *c++ = ((xmaxc[0] & 0x1) << 7)
+
+#undef xmc
+#define xmc (source + 12)
+
+ | ((xmc[0] & 0x7) << 4)
+ | ((xmc[1] & 0x7) << 1)
+ | ((xmc[2] >> 2) & 0x1);
+ *c++ = ((xmc[2] & 0x3) << 6)
+ | ((xmc[3] & 0x7) << 3)
+ | (xmc[4] & 0x7);
+ *c++ = ((xmc[5] & 0x7) << 5) /* 10 */
+ | ((xmc[6] & 0x7) << 2)
+ | ((xmc[7] >> 1) & 0x3);
+ *c++ = ((xmc[7] & 0x1) << 7)
+ | ((xmc[8] & 0x7) << 4)
+ | ((xmc[9] & 0x7) << 1)
+ | ((xmc[10] >> 2) & 0x1);
+ *c++ = ((xmc[10] & 0x3) << 6)
+ | ((xmc[11] & 0x7) << 3)
+ | (xmc[12] & 0x7);
+
+
+ *c++ = ((Nc[1] & 0x7F) << 1)
+
+
+ | ((bc[1] >> 1) & 0x1);
+ *c++ = ((bc[1] & 0x1) << 7)
+
+
+ | ((Mc[1] & 0x3) << 5)
+
+
+ | ((xmaxc[1] >> 1) & 0x1F);
+ *c++ = ((xmaxc[1] & 0x1) << 7)
+
+#undef xmc
+#define xmc (source + 29 - 13)
+
+ | ((xmc[13] & 0x7) << 4)
+ | ((xmc[14] & 0x7) << 1)
+ | ((xmc[15] >> 2) & 0x1);
+ *c++ = ((xmc[15] & 0x3) << 6)
+ | ((xmc[16] & 0x7) << 3)
+ | (xmc[17] & 0x7);
+ *c++ = ((xmc[18] & 0x7) << 5)
+ | ((xmc[19] & 0x7) << 2)
+ | ((xmc[20] >> 1) & 0x3);
+ *c++ = ((xmc[20] & 0x1) << 7)
+ | ((xmc[21] & 0x7) << 4)
+ | ((xmc[22] & 0x7) << 1)
+ | ((xmc[23] >> 2) & 0x1);
+ *c++ = ((xmc[23] & 0x3) << 6)
+ | ((xmc[24] & 0x7) << 3)
+ | (xmc[25] & 0x7);
+
+
+ *c++ = ((Nc[2] & 0x7F) << 1) /* 20 */
+
+
+ | ((bc[2] >> 1) & 0x1);
+ *c++ = ((bc[2] & 0x1) << 7)
+
+
+ | ((Mc[2] & 0x3) << 5)
+
+
+ | ((xmaxc[2] >> 1) & 0x1F);
+ *c++ = ((xmaxc[2] & 0x1) << 7)
+
+#undef xmc
+#define xmc (source + 46 - 26)
+
+ | ((xmc[26] & 0x7) << 4)
+ | ((xmc[27] & 0x7) << 1)
+ | ((xmc[28] >> 2) & 0x1);
+ *c++ = ((xmc[28] & 0x3) << 6)
+ | ((xmc[29] & 0x7) << 3)
+ | (xmc[30] & 0x7);
+ *c++ = ((xmc[31] & 0x7) << 5)
+ | ((xmc[32] & 0x7) << 2)
+ | ((xmc[33] >> 1) & 0x3);
+ *c++ = ((xmc[33] & 0x1) << 7)
+ | ((xmc[34] & 0x7) << 4)
+ | ((xmc[35] & 0x7) << 1)
+ | ((xmc[36] >> 2) & 0x1);
+ *c++ = ((xmc[36] & 0x3) << 6)
+ | ((xmc[37] & 0x7) << 3)
+ | (xmc[38] & 0x7);
+
+
+ *c++ = ((Nc[3] & 0x7F) << 1)
+
+
+ | ((bc[3] >> 1) & 0x1);
+ *c++ = ((bc[3] & 0x1) << 7)
+
+
+ | ((Mc[3] & 0x3) << 5)
+
+
+ | ((xmaxc[3] >> 1) & 0x1F);
+ *c++ = ((xmaxc[3] & 0x1) << 7)
+
+#undef xmc
+#define xmc (source + 63 - 39)
+
+ | ((xmc[39] & 0x7) << 4)
+ | ((xmc[40] & 0x7) << 1)
+ | ((xmc[41] >> 2) & 0x1);
+ *c++ = ((xmc[41] & 0x3) << 6) /* 30 */
+ | ((xmc[42] & 0x7) << 3)
+ | (xmc[43] & 0x7);
+ *c++ = ((xmc[44] & 0x7) << 5)
+ | ((xmc[45] & 0x7) << 2)
+ | ((xmc[46] >> 1) & 0x3);
+ *c++ = ((xmc[46] & 0x1) << 7)
+ | ((xmc[47] & 0x7) << 4)
+ | ((xmc[48] & 0x7) << 1)
+ | ((xmc[49] >> 2) & 0x1);
+ *c++ = ((xmc[49] & 0x3) << 6)
+ | ((xmc[50] & 0x7) << 3)
+ | (xmc[51] & 0x7);
+ }
+}
diff --git a/src/audio/gsm/src/gsm_option.cpp b/src/audio/gsm/src/gsm_option.cpp
new file mode 100644
index 0000000..274b7b1
--- /dev/null
+++ b/src/audio/gsm/src/gsm_option.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_option.c,v 1.3 1996/07/02 09:59:05 jutta Exp $ */
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+int gsm_option P3((r, opt, val), gsm r, int opt, int * val)
+{
+ int result = -1;
+
+ switch (opt) {
+ case GSM_OPT_LTP_CUT:
+#ifdef LTP_CUT
+ result = r->ltp_cut;
+ if (val) r->ltp_cut = *val;
+#endif
+ break;
+
+ case GSM_OPT_VERBOSE:
+#ifndef NDEBUG
+ result = r->verbose;
+ if (val) r->verbose = *val;
+#endif
+ break;
+
+ case GSM_OPT_FAST:
+
+#if defined(FAST) && defined(USE_FLOAT_MUL)
+ result = r->fast;
+ if (val) r->fast = !!*val;
+#endif
+ break;
+
+ case GSM_OPT_FRAME_CHAIN:
+
+#ifdef WAV49
+ result = r->frame_chain;
+ if (val) r->frame_chain = *val;
+#endif
+ break;
+
+ case GSM_OPT_FRAME_INDEX:
+
+#ifdef WAV49
+ result = r->frame_index;
+ if (val) r->frame_index = *val;
+#endif
+ break;
+
+ case GSM_OPT_WAV49:
+
+#ifdef WAV49
+ result = r->wav_fmt;
+ if (val) r->wav_fmt = !!*val;
+#endif
+ break;
+
+ default:
+ break;
+ }
+ return result;
+}
diff --git a/src/audio/gsm/src/gsm_print.cpp b/src/audio/gsm/src/gsm_print.cpp
new file mode 100644
index 0000000..47f753b
--- /dev/null
+++ b/src/audio/gsm/src/gsm_print.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_print.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */
+
+#include <stdio.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+int gsm_print P3((f, s, c), FILE * f, gsm s, gsm_byte * c)
+{
+ word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4];
+
+ /* GSM_MAGIC = (*c >> 4) & 0xF; */
+
+ if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1;
+
+ LARc[0] = (*c++ & 0xF) << 2; /* 1 */
+ LARc[0] |= (*c >> 6) & 0x3;
+ LARc[1] = *c++ & 0x3F;
+ LARc[2] = (*c >> 3) & 0x1F;
+ LARc[3] = (*c++ & 0x7) << 2;
+ LARc[3] |= (*c >> 6) & 0x3;
+ LARc[4] = (*c >> 2) & 0xF;
+ LARc[5] = (*c++ & 0x3) << 2;
+ LARc[5] |= (*c >> 6) & 0x3;
+ LARc[6] = (*c >> 3) & 0x7;
+ LARc[7] = *c++ & 0x7;
+
+
+ Nc[0] = (*c >> 1) & 0x7F;
+ bc[0] = (*c++ & 0x1) << 1;
+ bc[0] |= (*c >> 7) & 0x1;
+ Mc[0] = (*c >> 5) & 0x3;
+ xmaxc[0] = (*c++ & 0x1F) << 1;
+ xmaxc[0] |= (*c >> 7) & 0x1;
+ xmc[0] = (*c >> 4) & 0x7;
+ xmc[1] = (*c >> 1) & 0x7;
+ xmc[2] = (*c++ & 0x1) << 2;
+ xmc[2] |= (*c >> 6) & 0x3;
+ xmc[3] = (*c >> 3) & 0x7;
+ xmc[4] = *c++ & 0x7;
+ xmc[5] = (*c >> 5) & 0x7;
+ xmc[6] = (*c >> 2) & 0x7;
+ xmc[7] = (*c++ & 0x3) << 1; /* 10 */
+ xmc[7] |= (*c >> 7) & 0x1;
+ xmc[8] = (*c >> 4) & 0x7;
+ xmc[9] = (*c >> 1) & 0x7;
+ xmc[10] = (*c++ & 0x1) << 2;
+ xmc[10] |= (*c >> 6) & 0x3;
+ xmc[11] = (*c >> 3) & 0x7;
+ xmc[12] = *c++ & 0x7;
+
+ Nc[1] = (*c >> 1) & 0x7F;
+ bc[1] = (*c++ & 0x1) << 1;
+ bc[1] |= (*c >> 7) & 0x1;
+ Mc[1] = (*c >> 5) & 0x3;
+ xmaxc[1] = (*c++ & 0x1F) << 1;
+ xmaxc[1] |= (*c >> 7) & 0x1;
+ xmc[13] = (*c >> 4) & 0x7;
+ xmc[14] = (*c >> 1) & 0x7;
+ xmc[15] = (*c++ & 0x1) << 2;
+ xmc[15] |= (*c >> 6) & 0x3;
+ xmc[16] = (*c >> 3) & 0x7;
+ xmc[17] = *c++ & 0x7;
+ xmc[18] = (*c >> 5) & 0x7;
+ xmc[19] = (*c >> 2) & 0x7;
+ xmc[20] = (*c++ & 0x3) << 1;
+ xmc[20] |= (*c >> 7) & 0x1;
+ xmc[21] = (*c >> 4) & 0x7;
+ xmc[22] = (*c >> 1) & 0x7;
+ xmc[23] = (*c++ & 0x1) << 2;
+ xmc[23] |= (*c >> 6) & 0x3;
+ xmc[24] = (*c >> 3) & 0x7;
+ xmc[25] = *c++ & 0x7;
+
+
+ Nc[2] = (*c >> 1) & 0x7F;
+ bc[2] = (*c++ & 0x1) << 1; /* 20 */
+ bc[2] |= (*c >> 7) & 0x1;
+ Mc[2] = (*c >> 5) & 0x3;
+ xmaxc[2] = (*c++ & 0x1F) << 1;
+ xmaxc[2] |= (*c >> 7) & 0x1;
+ xmc[26] = (*c >> 4) & 0x7;
+ xmc[27] = (*c >> 1) & 0x7;
+ xmc[28] = (*c++ & 0x1) << 2;
+ xmc[28] |= (*c >> 6) & 0x3;
+ xmc[29] = (*c >> 3) & 0x7;
+ xmc[30] = *c++ & 0x7;
+ xmc[31] = (*c >> 5) & 0x7;
+ xmc[32] = (*c >> 2) & 0x7;
+ xmc[33] = (*c++ & 0x3) << 1;
+ xmc[33] |= (*c >> 7) & 0x1;
+ xmc[34] = (*c >> 4) & 0x7;
+ xmc[35] = (*c >> 1) & 0x7;
+ xmc[36] = (*c++ & 0x1) << 2;
+ xmc[36] |= (*c >> 6) & 0x3;
+ xmc[37] = (*c >> 3) & 0x7;
+ xmc[38] = *c++ & 0x7;
+
+ Nc[3] = (*c >> 1) & 0x7F;
+ bc[3] = (*c++ & 0x1) << 1;
+ bc[3] |= (*c >> 7) & 0x1;
+ Mc[3] = (*c >> 5) & 0x3;
+ xmaxc[3] = (*c++ & 0x1F) << 1;
+ xmaxc[3] |= (*c >> 7) & 0x1;
+
+ xmc[39] = (*c >> 4) & 0x7;
+ xmc[40] = (*c >> 1) & 0x7;
+ xmc[41] = (*c++ & 0x1) << 2;
+ xmc[41] |= (*c >> 6) & 0x3;
+ xmc[42] = (*c >> 3) & 0x7;
+ xmc[43] = *c++ & 0x7; /* 30 */
+ xmc[44] = (*c >> 5) & 0x7;
+ xmc[45] = (*c >> 2) & 0x7;
+ xmc[46] = (*c++ & 0x3) << 1;
+ xmc[46] |= (*c >> 7) & 0x1;
+ xmc[47] = (*c >> 4) & 0x7;
+ xmc[48] = (*c >> 1) & 0x7;
+ xmc[49] = (*c++ & 0x1) << 2;
+ xmc[49] |= (*c >> 6) & 0x3;
+ xmc[50] = (*c >> 3) & 0x7;
+ xmc[51] = *c & 0x7; /* 33 */
+
+ fprintf(f,
+ "LARc:\t%2.2d %2.2d %2.2d %2.2d %2.2d %2.2d %2.2d %2.2d\n",
+ LARc[0],LARc[1],LARc[2],LARc[3],LARc[4],LARc[5],LARc[6],LARc[7]);
+
+ fprintf(f, "#1: Nc %4.4d bc %d Mc %d xmaxc %d\n",
+ Nc[0], bc[0], Mc[0], xmaxc[0]);
+ fprintf(f,
+"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
+ xmc[0],xmc[1],xmc[2],xmc[3],xmc[4],xmc[5],xmc[6],
+ xmc[7],xmc[8],xmc[9],xmc[10],xmc[11],xmc[12] );
+
+ fprintf(f, "#2: Nc %4.4d bc %d Mc %d xmaxc %d\n",
+ Nc[1], bc[1], Mc[1], xmaxc[1]);
+ fprintf(f,
+"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
+ xmc[13+0],xmc[13+1],xmc[13+2],xmc[13+3],xmc[13+4],xmc[13+5],
+ xmc[13+6], xmc[13+7],xmc[13+8],xmc[13+9],xmc[13+10],xmc[13+11],
+ xmc[13+12] );
+
+ fprintf(f, "#3: Nc %4.4d bc %d Mc %d xmaxc %d\n",
+ Nc[2], bc[2], Mc[2], xmaxc[2]);
+ fprintf(f,
+"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
+ xmc[26+0],xmc[26+1],xmc[26+2],xmc[26+3],xmc[26+4],xmc[26+5],
+ xmc[26+6], xmc[26+7],xmc[26+8],xmc[26+9],xmc[26+10],xmc[26+11],
+ xmc[26+12] );
+
+ fprintf(f, "#4: Nc %4.4d bc %d Mc %d xmaxc %d\n",
+ Nc[3], bc[3], Mc[3], xmaxc[3]);
+ fprintf(f,
+"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
+ xmc[39+0],xmc[39+1],xmc[39+2],xmc[39+3],xmc[39+4],xmc[39+5],
+ xmc[39+6], xmc[39+7],xmc[39+8],xmc[39+9],xmc[39+10],xmc[39+11],
+ xmc[39+12] );
+
+ return 0;
+}
diff --git a/src/audio/gsm/src/long_term.cpp b/src/audio/gsm/src/long_term.cpp
new file mode 100644
index 0000000..b2d2d3f
--- /dev/null
+++ b/src/audio/gsm/src/long_term.cpp
@@ -0,0 +1,949 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/long_term.c,v 1.6 1996/07/02 12:33:19 jutta Exp $ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+/*
+ * 4.2.11 .. 4.2.12 LONG TERM PREDICTOR (LTP) SECTION
+ */
+
+
+/*
+ * This module computes the LTP gain (bc) and the LTP lag (Nc)
+ * for the long term analysis filter. This is done by calculating a
+ * maximum of the cross-correlation function between the current
+ * sub-segment short term residual signal d[0..39] (output of
+ * the short term analysis filter; for simplification the index
+ * of this array begins at 0 and ends at 39 for each sub-segment of the
+ * RPE-LTP analysis) and the previous reconstructed short term
+ * residual signal dp[ -120 .. -1 ]. A dynamic scaling must be
+ * performed to avoid overflow.
+ */
+
+ /* The next procedure exists in six versions. First two integer
+ * version (if USE_FLOAT_MUL is not defined); then four floating
+ * point versions, twice with proper scaling (USE_FLOAT_MUL defined),
+ * once without (USE_FLOAT_MUL and FAST defined, and fast run-time
+ * option used). Every pair has first a Cut version (see the -C
+ * option to toast or the LTP_CUT option to gsm_option()), then the
+ * uncut one. (For a detailed explanation of why this is altogether
+ * a bad idea, see Henry Spencer and Geoff Collyer, ``#ifdef Considered
+ * Harmful''.)
+ */
+
+#ifndef USE_FLOAT_MUL
+
+#ifdef LTP_CUT
+
+static void Cut_Calculation_of_the_LTP_parameters P5((st, d,dp,bc_out,Nc_out),
+
+ struct gsm_state * st,
+
+ register word * d, /* [0..39] IN */
+ register word * dp, /* [-120..-1] IN */
+ word * bc_out, /* OUT */
+ word * Nc_out /* OUT */
+)
+{
+ register int k, lambda;
+ word Nc, bc;
+ word wt[40];
+
+ longword L_result;
+ longword L_max, L_power;
+ word R, S, dmax, scal, best_k;
+ word ltp_cut;
+
+ register word temp, wt_k;
+
+ /* Search of the optimum scaling of d[0..39].
+ */
+ dmax = 0;
+ for (k = 0; k <= 39; k++) {
+ temp = d[k];
+ temp = GSM_ABS( temp );
+ if (temp > dmax) {
+ dmax = temp;
+ best_k = k;
+ }
+ }
+ temp = 0;
+ if (dmax == 0) scal = 0;
+ else {
+ assert(dmax > 0);
+ temp = gsm_norm( (longword)dmax << 16 );
+ }
+ if (temp > 6) scal = 0;
+ else scal = 6 - temp;
+ assert(scal >= 0);
+
+ /* Search for the maximum cross-correlation and coding of the LTP lag
+ */
+ L_max = 0;
+ Nc = 40; /* index for the maximum cross-correlation */
+ wt_k = SASR(d[best_k], scal);
+
+ for (lambda = 40; lambda <= 120; lambda++) {
+ L_result = (longword)wt_k * dp[best_k - lambda];
+ if (L_result > L_max) {
+ Nc = lambda;
+ L_max = L_result;
+ }
+ }
+ *Nc_out = Nc;
+ L_max <<= 1;
+
+ /* Rescaling of L_max
+ */
+ assert(scal <= 100 && scal >= -100);
+ L_max = L_max >> (6 - scal); /* sub(6, scal) */
+
+ assert( Nc <= 120 && Nc >= 40);
+
+ /* Compute the power of the reconstructed short term residual
+ * signal dp[..]
+ */
+ L_power = 0;
+ for (k = 0; k <= 39; k++) {
+
+ register longword L_temp;
+
+ L_temp = SASR( dp[k - Nc], 3 );
+ L_power += L_temp * L_temp;
+ }
+ L_power <<= 1; /* from L_MULT */
+
+ /* Normalization of L_max and L_power
+ */
+
+ if (L_max <= 0) {
+ *bc_out = 0;
+ return;
+ }
+ if (L_max >= L_power) {
+ *bc_out = 3;
+ return;
+ }
+
+ temp = gsm_norm( L_power );
+
+ R = SASR( L_max << temp, 16 );
+ S = SASR( L_power << temp, 16 );
+
+ /* Coding of the LTP gain
+ */
+
+ /* Table 4.3a must be used to obtain the level DLB[i] for the
+ * quantization of the LTP gain b to get the coded version bc.
+ */
+ for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break;
+ *bc_out = bc;
+}
+
+#endif /* LTP_CUT */
+
+static void Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out),
+ register word * d, /* [0..39] IN */
+ register word * dp, /* [-120..-1] IN */
+ word * bc_out, /* OUT */
+ word * Nc_out /* OUT */
+)
+{
+ register int k, lambda;
+ word Nc, bc;
+ word wt[40];
+
+ longword L_max, L_power;
+ word R, S, dmax, scal;
+ register word temp;
+
+ /* Search of the optimum scaling of d[0..39].
+ */
+ dmax = 0;
+
+ for (k = 0; k <= 39; k++) {
+ temp = d[k];
+ temp = GSM_ABS( temp );
+ if (temp > dmax) dmax = temp;
+ }
+
+ temp = 0;
+ if (dmax == 0) scal = 0;
+ else {
+ assert(dmax > 0);
+ temp = gsm_norm( (longword)dmax << 16 );
+ }
+
+ if (temp > 6) scal = 0;
+ else scal = 6 - temp;
+
+ assert(scal >= 0);
+
+ /* Initialization of a working array wt
+ */
+
+ for (k = 0; k <= 39; k++) wt[k] = SASR( d[k], scal );
+
+ /* Search for the maximum cross-correlation and coding of the LTP lag
+ */
+ L_max = 0;
+ Nc = 40; /* index for the maximum cross-correlation */
+
+ for (lambda = 40; lambda <= 120; lambda++) {
+
+# undef STEP
+# define STEP(k) (longword)wt[k] * dp[k - lambda]
+
+ register longword L_result;
+
+ L_result = STEP(0) ; L_result += STEP(1) ;
+ L_result += STEP(2) ; L_result += STEP(3) ;
+ L_result += STEP(4) ; L_result += STEP(5) ;
+ L_result += STEP(6) ; L_result += STEP(7) ;
+ L_result += STEP(8) ; L_result += STEP(9) ;
+ L_result += STEP(10) ; L_result += STEP(11) ;
+ L_result += STEP(12) ; L_result += STEP(13) ;
+ L_result += STEP(14) ; L_result += STEP(15) ;
+ L_result += STEP(16) ; L_result += STEP(17) ;
+ L_result += STEP(18) ; L_result += STEP(19) ;
+ L_result += STEP(20) ; L_result += STEP(21) ;
+ L_result += STEP(22) ; L_result += STEP(23) ;
+ L_result += STEP(24) ; L_result += STEP(25) ;
+ L_result += STEP(26) ; L_result += STEP(27) ;
+ L_result += STEP(28) ; L_result += STEP(29) ;
+ L_result += STEP(30) ; L_result += STEP(31) ;
+ L_result += STEP(32) ; L_result += STEP(33) ;
+ L_result += STEP(34) ; L_result += STEP(35) ;
+ L_result += STEP(36) ; L_result += STEP(37) ;
+ L_result += STEP(38) ; L_result += STEP(39) ;
+
+ if (L_result > L_max) {
+
+ Nc = lambda;
+ L_max = L_result;
+ }
+ }
+
+ *Nc_out = Nc;
+
+ L_max <<= 1;
+
+ /* Rescaling of L_max
+ */
+ assert(scal <= 100 && scal >= -100);
+ L_max = L_max >> (6 - scal); /* sub(6, scal) */
+
+ assert( Nc <= 120 && Nc >= 40);
+
+ /* Compute the power of the reconstructed short term residual
+ * signal dp[..]
+ */
+ L_power = 0;
+ for (k = 0; k <= 39; k++) {
+
+ register longword L_temp;
+
+ L_temp = SASR( dp[k - Nc], 3 );
+ L_power += L_temp * L_temp;
+ }
+ L_power <<= 1; /* from L_MULT */
+
+ /* Normalization of L_max and L_power
+ */
+
+ if (L_max <= 0) {
+ *bc_out = 0;
+ return;
+ }
+ if (L_max >= L_power) {
+ *bc_out = 3;
+ return;
+ }
+
+ temp = gsm_norm( L_power );
+
+ R = SASR( L_max << temp, 16 );
+ S = SASR( L_power << temp, 16 );
+
+ /* Coding of the LTP gain
+ */
+
+ /* Table 4.3a must be used to obtain the level DLB[i] for the
+ * quantization of the LTP gain b to get the coded version bc.
+ */
+ for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break;
+ *bc_out = bc;
+}
+
+#else /* USE_FLOAT_MUL */
+
+#ifdef LTP_CUT
+
+static void Cut_Calculation_of_the_LTP_parameters P5((st, d,dp,bc_out,Nc_out),
+ struct gsm_state * st, /* IN */
+ register word * d, /* [0..39] IN */
+ register word * dp, /* [-120..-1] IN */
+ word * bc_out, /* OUT */
+ word * Nc_out /* OUT */
+)
+{
+ register int k, lambda;
+ word Nc, bc;
+ word ltp_cut;
+
+ float wt_float[40];
+ float dp_float_base[120], * dp_float = dp_float_base + 120;
+
+ longword L_max, L_power;
+ word R, S, dmax, scal;
+ register word temp;
+
+ /* Search of the optimum scaling of d[0..39].
+ */
+ dmax = 0;
+
+ for (k = 0; k <= 39; k++) {
+ temp = d[k];
+ temp = GSM_ABS( temp );
+ if (temp > dmax) dmax = temp;
+ }
+
+ temp = 0;
+ if (dmax == 0) scal = 0;
+ else {
+ assert(dmax > 0);
+ temp = gsm_norm( (longword)dmax << 16 );
+ }
+
+ if (temp > 6) scal = 0;
+ else scal = 6 - temp;
+
+ assert(scal >= 0);
+ ltp_cut = (longword)SASR(dmax, scal) * st->ltp_cut / 100;
+
+
+ /* Initialization of a working array wt
+ */
+
+ for (k = 0; k < 40; k++) {
+ register word w = SASR( d[k], scal );
+ if (w < 0 ? w > -ltp_cut : w < ltp_cut) {
+ wt_float[k] = 0.0;
+ }
+ else {
+ wt_float[k] = w;
+ }
+ }
+ for (k = -120; k < 0; k++) dp_float[k] = dp[k];
+
+ /* Search for the maximum cross-correlation and coding of the LTP lag
+ */
+ L_max = 0;
+ Nc = 40; /* index for the maximum cross-correlation */
+
+ for (lambda = 40; lambda <= 120; lambda += 9) {
+
+ /* Calculate L_result for l = lambda .. lambda + 9.
+ */
+ register float *lp = dp_float - lambda;
+
+ register float W;
+ register float a = lp[-8], b = lp[-7], c = lp[-6],
+ d = lp[-5], e = lp[-4], f = lp[-3],
+ g = lp[-2], h = lp[-1];
+ register float E;
+ register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0,
+ S5 = 0, S6 = 0, S7 = 0, S8 = 0;
+
+# undef STEP
+# define STEP(K, a, b, c, d, e, f, g, h) \
+ if ((W = wt_float[K]) != 0.0) { \
+ E = W * a; S8 += E; \
+ E = W * b; S7 += E; \
+ E = W * c; S6 += E; \
+ E = W * d; S5 += E; \
+ E = W * e; S4 += E; \
+ E = W * f; S3 += E; \
+ E = W * g; S2 += E; \
+ E = W * h; S1 += E; \
+ a = lp[K]; \
+ E = W * a; S0 += E; } else (a = lp[K])
+
+# define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h)
+# define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a)
+# define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b)
+# define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c)
+# define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d)
+# define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e)
+# define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f)
+# define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g)
+
+ STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3);
+ STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7);
+
+ STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11);
+ STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15);
+
+ STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19);
+ STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23);
+
+ STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27);
+ STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31);
+
+ STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35);
+ STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39);
+
+ if (S0 > L_max) { L_max = S0; Nc = lambda; }
+ if (S1 > L_max) { L_max = S1; Nc = lambda + 1; }
+ if (S2 > L_max) { L_max = S2; Nc = lambda + 2; }
+ if (S3 > L_max) { L_max = S3; Nc = lambda + 3; }
+ if (S4 > L_max) { L_max = S4; Nc = lambda + 4; }
+ if (S5 > L_max) { L_max = S5; Nc = lambda + 5; }
+ if (S6 > L_max) { L_max = S6; Nc = lambda + 6; }
+ if (S7 > L_max) { L_max = S7; Nc = lambda + 7; }
+ if (S8 > L_max) { L_max = S8; Nc = lambda + 8; }
+
+ }
+ *Nc_out = Nc;
+
+ L_max <<= 1;
+
+ /* Rescaling of L_max
+ */
+ assert(scal <= 100 && scal >= -100);
+ L_max = L_max >> (6 - scal); /* sub(6, scal) */
+
+ assert( Nc <= 120 && Nc >= 40);
+
+ /* Compute the power of the reconstructed short term residual
+ * signal dp[..]
+ */
+ L_power = 0;
+ for (k = 0; k <= 39; k++) {
+
+ register longword L_temp;
+
+ L_temp = SASR( dp[k - Nc], 3 );
+ L_power += L_temp * L_temp;
+ }
+ L_power <<= 1; /* from L_MULT */
+
+ /* Normalization of L_max and L_power
+ */
+
+ if (L_max <= 0) {
+ *bc_out = 0;
+ return;
+ }
+ if (L_max >= L_power) {
+ *bc_out = 3;
+ return;
+ }
+
+ temp = gsm_norm( L_power );
+
+ R = SASR( L_max << temp, 16 );
+ S = SASR( L_power << temp, 16 );
+
+ /* Coding of the LTP gain
+ */
+
+ /* Table 4.3a must be used to obtain the level DLB[i] for the
+ * quantization of the LTP gain b to get the coded version bc.
+ */
+ for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break;
+ *bc_out = bc;
+}
+
+#endif /* LTP_CUT */
+
+static void Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out),
+ register word * d, /* [0..39] IN */
+ register word * dp, /* [-120..-1] IN */
+ word * bc_out, /* OUT */
+ word * Nc_out /* OUT */
+)
+{
+ register int k, lambda;
+ word Nc, bc;
+
+ float wt_float[40];
+ float dp_float_base[120], * dp_float = dp_float_base + 120;
+
+ longword L_max, L_power;
+ word R, S, dmax, scal;
+ register word temp;
+
+ /* Search of the optimum scaling of d[0..39].
+ */
+ dmax = 0;
+
+ for (k = 0; k <= 39; k++) {
+ temp = d[k];
+ temp = GSM_ABS( temp );
+ if (temp > dmax) dmax = temp;
+ }
+
+ temp = 0;
+ if (dmax == 0) scal = 0;
+ else {
+ assert(dmax > 0);
+ temp = gsm_norm( (longword)dmax << 16 );
+ }
+
+ if (temp > 6) scal = 0;
+ else scal = 6 - temp;
+
+ assert(scal >= 0);
+
+ /* Initialization of a working array wt
+ */
+
+ for (k = 0; k < 40; k++) wt_float[k] = SASR( d[k], scal );
+ for (k = -120; k < 0; k++) dp_float[k] = dp[k];
+
+ /* Search for the maximum cross-correlation and coding of the LTP lag
+ */
+ L_max = 0;
+ Nc = 40; /* index for the maximum cross-correlation */
+
+ for (lambda = 40; lambda <= 120; lambda += 9) {
+
+ /* Calculate L_result for l = lambda .. lambda + 9.
+ */
+ register float *lp = dp_float - lambda;
+
+ register float W;
+ register float a = lp[-8], b = lp[-7], c = lp[-6],
+ d = lp[-5], e = lp[-4], f = lp[-3],
+ g = lp[-2], h = lp[-1];
+ register float E;
+ register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0,
+ S5 = 0, S6 = 0, S7 = 0, S8 = 0;
+
+# undef STEP
+# define STEP(K, a, b, c, d, e, f, g, h) \
+ W = wt_float[K]; \
+ E = W * a; S8 += E; \
+ E = W * b; S7 += E; \
+ E = W * c; S6 += E; \
+ E = W * d; S5 += E; \
+ E = W * e; S4 += E; \
+ E = W * f; S3 += E; \
+ E = W * g; S2 += E; \
+ E = W * h; S1 += E; \
+ a = lp[K]; \
+ E = W * a; S0 += E
+
+# define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h)
+# define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a)
+# define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b)
+# define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c)
+# define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d)
+# define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e)
+# define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f)
+# define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g)
+
+ STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3);
+ STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7);
+
+ STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11);
+ STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15);
+
+ STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19);
+ STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23);
+
+ STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27);
+ STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31);
+
+ STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35);
+ STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39);
+
+ if (S0 > L_max) { L_max = S0; Nc = lambda; }
+ if (S1 > L_max) { L_max = S1; Nc = lambda + 1; }
+ if (S2 > L_max) { L_max = S2; Nc = lambda + 2; }
+ if (S3 > L_max) { L_max = S3; Nc = lambda + 3; }
+ if (S4 > L_max) { L_max = S4; Nc = lambda + 4; }
+ if (S5 > L_max) { L_max = S5; Nc = lambda + 5; }
+ if (S6 > L_max) { L_max = S6; Nc = lambda + 6; }
+ if (S7 > L_max) { L_max = S7; Nc = lambda + 7; }
+ if (S8 > L_max) { L_max = S8; Nc = lambda + 8; }
+ }
+ *Nc_out = Nc;
+
+ L_max <<= 1;
+
+ /* Rescaling of L_max
+ */
+ assert(scal <= 100 && scal >= -100);
+ L_max = L_max >> (6 - scal); /* sub(6, scal) */
+
+ assert( Nc <= 120 && Nc >= 40);
+
+ /* Compute the power of the reconstructed short term residual
+ * signal dp[..]
+ */
+ L_power = 0;
+ for (k = 0; k <= 39; k++) {
+
+ register longword L_temp;
+
+ L_temp = SASR( dp[k - Nc], 3 );
+ L_power += L_temp * L_temp;
+ }
+ L_power <<= 1; /* from L_MULT */
+
+ /* Normalization of L_max and L_power
+ */
+
+ if (L_max <= 0) {
+ *bc_out = 0;
+ return;
+ }
+ if (L_max >= L_power) {
+ *bc_out = 3;
+ return;
+ }
+
+ temp = gsm_norm( L_power );
+
+ R = SASR( L_max << temp, 16 );
+ S = SASR( L_power << temp, 16 );
+
+ /* Coding of the LTP gain
+ */
+
+ /* Table 4.3a must be used to obtain the level DLB[i] for the
+ * quantization of the LTP gain b to get the coded version bc.
+ */
+ for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break;
+ *bc_out = bc;
+}
+
+#ifdef FAST
+#ifdef LTP_CUT
+
+static void Cut_Fast_Calculation_of_the_LTP_parameters P5((st,
+ d,dp,bc_out,Nc_out),
+ struct gsm_state * st, /* IN */
+ register word * d, /* [0..39] IN */
+ register word * dp, /* [-120..-1] IN */
+ word * bc_out, /* OUT */
+ word * Nc_out /* OUT */
+)
+{
+ register int k, lambda;
+ register float wt_float;
+ word Nc, bc;
+ word wt_max, best_k, ltp_cut;
+
+ float dp_float_base[120], * dp_float = dp_float_base + 120;
+
+ register float L_result, L_max, L_power;
+
+ wt_max = 0;
+
+ for (k = 0; k < 40; ++k) {
+ if ( d[k] > wt_max) wt_max = d[best_k = k];
+ else if (-d[k] > wt_max) wt_max = -d[best_k = k];
+ }
+
+ assert(wt_max >= 0);
+ wt_float = (float)wt_max;
+
+ for (k = -120; k < 0; ++k) dp_float[k] = (float)dp[k];
+
+ /* Search for the maximum cross-correlation and coding of the LTP lag
+ */
+ L_max = 0;
+ Nc = 40; /* index for the maximum cross-correlation */
+
+ for (lambda = 40; lambda <= 120; lambda++) {
+ L_result = wt_float * dp_float[best_k - lambda];
+ if (L_result > L_max) {
+ Nc = lambda;
+ L_max = L_result;
+ }
+ }
+
+ *Nc_out = Nc;
+ if (L_max <= 0.) {
+ *bc_out = 0;
+ return;
+ }
+
+ /* Compute the power of the reconstructed short term residual
+ * signal dp[..]
+ */
+ dp_float -= Nc;
+ L_power = 0;
+ for (k = 0; k < 40; ++k) {
+ register float f = dp_float[k];
+ L_power += f * f;
+ }
+
+ if (L_max >= L_power) {
+ *bc_out = 3;
+ return;
+ }
+
+ /* Coding of the LTP gain
+ * Table 4.3a must be used to obtain the level DLB[i] for the
+ * quantization of the LTP gain b to get the coded version bc.
+ */
+ lambda = L_max / L_power * 32768.;
+ for (bc = 0; bc <= 2; ++bc) if (lambda <= gsm_DLB[bc]) break;
+ *bc_out = bc;
+}
+
+#endif /* LTP_CUT */
+
+static void Fast_Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out),
+ register word * d, /* [0..39] IN */
+ register word * dp, /* [-120..-1] IN */
+ word * bc_out, /* OUT */
+ word * Nc_out /* OUT */
+)
+{
+ register int k, lambda;
+ word Nc, bc;
+
+ float wt_float[40];
+ float dp_float_base[120], * dp_float = dp_float_base + 120;
+
+ register float L_max, L_power;
+
+ for (k = 0; k < 40; ++k) wt_float[k] = (float)d[k];
+ for (k = -120; k < 0; ++k) dp_float[k] = (float)dp[k];
+
+ /* Search for the maximum cross-correlation and coding of the LTP lag
+ */
+ L_max = 0;
+ Nc = 40; /* index for the maximum cross-correlation */
+
+ for (lambda = 40; lambda <= 120; lambda += 9) {
+
+ /* Calculate L_result for l = lambda .. lambda + 9.
+ */
+ register float *lp = dp_float - lambda;
+
+ register float W;
+ register float a = lp[-8], b = lp[-7], c = lp[-6],
+ d = lp[-5], e = lp[-4], f = lp[-3],
+ g = lp[-2], h = lp[-1];
+ register float E;
+ register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0,
+ S5 = 0, S6 = 0, S7 = 0, S8 = 0;
+
+# undef STEP
+# define STEP(K, a, b, c, d, e, f, g, h) \
+ W = wt_float[K]; \
+ E = W * a; S8 += E; \
+ E = W * b; S7 += E; \
+ E = W * c; S6 += E; \
+ E = W * d; S5 += E; \
+ E = W * e; S4 += E; \
+ E = W * f; S3 += E; \
+ E = W * g; S2 += E; \
+ E = W * h; S1 += E; \
+ a = lp[K]; \
+ E = W * a; S0 += E
+
+# define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h)
+# define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a)
+# define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b)
+# define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c)
+# define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d)
+# define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e)
+# define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f)
+# define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g)
+
+ STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3);
+ STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7);
+
+ STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11);
+ STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15);
+
+ STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19);
+ STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23);
+
+ STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27);
+ STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31);
+
+ STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35);
+ STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39);
+
+ if (S0 > L_max) { L_max = S0; Nc = lambda; }
+ if (S1 > L_max) { L_max = S1; Nc = lambda + 1; }
+ if (S2 > L_max) { L_max = S2; Nc = lambda + 2; }
+ if (S3 > L_max) { L_max = S3; Nc = lambda + 3; }
+ if (S4 > L_max) { L_max = S4; Nc = lambda + 4; }
+ if (S5 > L_max) { L_max = S5; Nc = lambda + 5; }
+ if (S6 > L_max) { L_max = S6; Nc = lambda + 6; }
+ if (S7 > L_max) { L_max = S7; Nc = lambda + 7; }
+ if (S8 > L_max) { L_max = S8; Nc = lambda + 8; }
+ }
+ *Nc_out = Nc;
+
+ if (L_max <= 0.) {
+ *bc_out = 0;
+ return;
+ }
+
+ /* Compute the power of the reconstructed short term residual
+ * signal dp[..]
+ */
+ dp_float -= Nc;
+ L_power = 0;
+ for (k = 0; k < 40; ++k) {
+ register float f = dp_float[k];
+ L_power += f * f;
+ }
+
+ if (L_max >= L_power) {
+ *bc_out = 3;
+ return;
+ }
+
+ /* Coding of the LTP gain
+ * Table 4.3a must be used to obtain the level DLB[i] for the
+ * quantization of the LTP gain b to get the coded version bc.
+ */
+ lambda = L_max / L_power * 32768.;
+ for (bc = 0; bc <= 2; ++bc) if (lambda <= gsm_DLB[bc]) break;
+ *bc_out = bc;
+}
+
+#endif /* FAST */
+#endif /* USE_FLOAT_MUL */
+
+
+/* 4.2.12 */
+
+static void Long_term_analysis_filtering P6((bc,Nc,dp,d,dpp,e),
+ word bc, /* IN */
+ word Nc, /* IN */
+ register word * dp, /* previous d [-120..-1] IN */
+ register word * d, /* d [0..39] IN */
+ register word * dpp, /* estimate [0..39] OUT */
+ register word * e /* long term res. signal [0..39] OUT */
+)
+/*
+ * In this part, we have to decode the bc parameter to compute
+ * the samples of the estimate dpp[0..39]. The decoding of bc needs the
+ * use of table 4.3b. The long term residual signal e[0..39]
+ * is then calculated to be fed to the RPE encoding section.
+ */
+{
+ register int k;
+ register longword ltmp;
+
+# undef STEP
+# define STEP(BP) \
+ for (k = 0; k <= 39; k++) { \
+ dpp[k] = GSM_MULT_R( BP, dp[k - Nc]); \
+ e[k] = GSM_SUB( d[k], dpp[k] ); \
+ }
+
+ switch (bc) {
+ case 0: STEP( 3277 ); break;
+ case 1: STEP( 11469 ); break;
+ case 2: STEP( 21299 ); break;
+ case 3: STEP( 32767 ); break;
+ }
+}
+
+void Gsm_Long_Term_Predictor P7((S,d,dp,e,dpp,Nc,bc), /* 4x for 160 samples */
+
+ struct gsm_state * S,
+
+ word * d, /* [0..39] residual signal IN */
+ word * dp, /* [-120..-1] d' IN */
+
+ word * e, /* [0..39] OUT */
+ word * dpp, /* [0..39] OUT */
+ word * Nc, /* correlation lag OUT */
+ word * bc /* gain factor OUT */
+)
+{
+ assert( d ); assert( dp ); assert( e );
+ assert( dpp); assert( Nc ); assert( bc );
+
+#if defined(FAST) && defined(USE_FLOAT_MUL)
+ if (S->fast)
+#if defined (LTP_CUT)
+ if (S->ltp_cut)
+ Cut_Fast_Calculation_of_the_LTP_parameters(S,
+ d, dp, bc, Nc);
+ else
+#endif /* LTP_CUT */
+ Fast_Calculation_of_the_LTP_parameters(d, dp, bc, Nc );
+ else
+#endif /* FAST & USE_FLOAT_MUL */
+#ifdef LTP_CUT
+ if (S->ltp_cut)
+ Cut_Calculation_of_the_LTP_parameters(S, d, dp, bc, Nc);
+ else
+#endif
+ Calculation_of_the_LTP_parameters(d, dp, bc, Nc);
+
+ Long_term_analysis_filtering( *bc, *Nc, dp, d, dpp, e );
+}
+
+/* 4.3.2 */
+void Gsm_Long_Term_Synthesis_Filtering P5((S,Ncr,bcr,erp,drp),
+ struct gsm_state * S,
+
+ word Ncr,
+ word bcr,
+ register word * erp, /* [0..39] IN */
+ register word * drp /* [-120..-1] IN, [-120..40] OUT */
+)
+/*
+ * This procedure uses the bcr and Ncr parameter to realize the
+ * long term synthesis filtering. The decoding of bcr needs
+ * table 4.3b.
+ */
+{
+ register longword ltmp; /* for ADD */
+ register int k;
+ word brp, drpp, Nr;
+
+ /* Check the limits of Nr.
+ */
+ Nr = Ncr < 40 || Ncr > 120 ? S->nrp : Ncr;
+ S->nrp = Nr;
+ assert(Nr >= 40 && Nr <= 120);
+
+ /* Decoding of the LTP gain bcr
+ */
+ brp = gsm_QLB[ bcr ];
+
+ /* Computation of the reconstructed short term residual
+ * signal drp[0..39]
+ */
+ assert(brp != MIN_WORD);
+
+ for (k = 0; k <= 39; k++) {
+ drpp = GSM_MULT_R( brp, drp[ k - Nr ] );
+ drp[k] = GSM_ADD( erp[k], drpp );
+ }
+
+ /*
+ * Update of the reconstructed short term residual signal
+ * drp[ -1..-120 ]
+ */
+
+ for (k = 0; k <= 119; k++) drp[ -120 + k ] = drp[ -80 + k ];
+}
diff --git a/src/audio/gsm/src/lpc.cpp b/src/audio/gsm/src/lpc.cpp
new file mode 100644
index 0000000..21b9faf
--- /dev/null
+++ b/src/audio/gsm/src/lpc.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/lpc.c,v 1.5 1994/12/30 23:14:54 jutta Exp $ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+#undef P
+
+/*
+ * 4.2.4 .. 4.2.7 LPC ANALYSIS SECTION
+ */
+
+/* 4.2.4 */
+
+
+static void Autocorrelation P2((s, L_ACF),
+ word * s, /* [0..159] IN/OUT */
+ longword * L_ACF) /* [0..8] OUT */
+/*
+ * The goal is to compute the array L_ACF[k]. The signal s[i] must
+ * be scaled in order to avoid an overflow situation.
+ */
+{
+ register int k, i;
+
+ word temp, smax, scalauto;
+
+#ifdef USE_FLOAT_MUL
+ float float_s[160];
+#endif
+
+ /* Dynamic scaling of the array s[0..159]
+ */
+
+ /* Search for the maximum.
+ */
+ smax = 0;
+ for (k = 0; k <= 159; k++) {
+ temp = GSM_ABS( s[k] );
+ if (temp > smax) smax = temp;
+ }
+
+ /* Computation of the scaling factor.
+ */
+ if (smax == 0) scalauto = 0;
+ else {
+ assert(smax > 0);
+ scalauto = 4 - gsm_norm( (longword)smax << 16 );/* sub(4,..) */
+ }
+
+ /* Scaling of the array s[0...159]
+ */
+
+ if (scalauto > 0) {
+
+# ifdef USE_FLOAT_MUL
+# define SCALE(n) \
+ case n: for (k = 0; k <= 159; k++) \
+ float_s[k] = (float) \
+ (s[k] = GSM_MULT_R(s[k], 16384 >> (n-1)));\
+ break;
+# else
+# define SCALE(n) \
+ case n: for (k = 0; k <= 159; k++) \
+ s[k] = GSM_MULT_R( s[k], 16384 >> (n-1) );\
+ break;
+# endif /* USE_FLOAT_MUL */
+
+ switch (scalauto) {
+ SCALE(1)
+ SCALE(2)
+ SCALE(3)
+ SCALE(4)
+ }
+# undef SCALE
+ }
+# ifdef USE_FLOAT_MUL
+ else for (k = 0; k <= 159; k++) float_s[k] = (float) s[k];
+# endif
+
+ /* Compute the L_ACF[..].
+ */
+ {
+# ifdef USE_FLOAT_MUL
+ register float * sp = float_s;
+ register float sl = *sp;
+
+# define STEP(k) L_ACF[k] += (longword)(sl * sp[ -(k) ]);
+# else
+ word * sp = s;
+ word sl = *sp;
+
+# define STEP(k) L_ACF[k] += ((longword)sl * sp[ -(k) ]);
+# endif
+
+# define NEXTI sl = *++sp
+
+
+ for (k = 9; k--; L_ACF[k] = 0) ;
+
+ STEP (0);
+ NEXTI;
+ STEP(0); STEP(1);
+ NEXTI;
+ STEP(0); STEP(1); STEP(2);
+ NEXTI;
+ STEP(0); STEP(1); STEP(2); STEP(3);
+ NEXTI;
+ STEP(0); STEP(1); STEP(2); STEP(3); STEP(4);
+ NEXTI;
+ STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5);
+ NEXTI;
+ STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6);
+ NEXTI;
+ STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6); STEP(7);
+
+ for (i = 8; i <= 159; i++) {
+
+ NEXTI;
+
+ STEP(0);
+ STEP(1); STEP(2); STEP(3); STEP(4);
+ STEP(5); STEP(6); STEP(7); STEP(8);
+ }
+
+ for (k = 9; k--; L_ACF[k] <<= 1) ;
+
+ }
+ /* Rescaling of the array s[0..159]
+ */
+ if (scalauto > 0) {
+ assert(scalauto <= 4);
+ for (k = 160; k--; *s++ <<= scalauto) ;
+ }
+}
+
+#if defined(USE_FLOAT_MUL) && defined(FAST)
+
+static void Fast_Autocorrelation P2((s, L_ACF),
+ word * s, /* [0..159] IN/OUT */
+ longword * L_ACF) /* [0..8] OUT */
+{
+ register int k, i;
+ float f_L_ACF[9];
+ float scale;
+
+ float s_f[160];
+ register float *sf = s_f;
+
+ for (i = 0; i < 160; ++i) sf[i] = s[i];
+ for (k = 0; k <= 8; k++) {
+ register float L_temp2 = 0;
+ register float *sfl = sf - k;
+ for (i = k; i < 160; ++i) L_temp2 += sf[i] * sfl[i];
+ f_L_ACF[k] = L_temp2;
+ }
+ scale = MAX_LONGWORD / f_L_ACF[0];
+
+ for (k = 0; k <= 8; k++) {
+ L_ACF[k] = f_L_ACF[k] * scale;
+ }
+}
+#endif /* defined (USE_FLOAT_MUL) && defined (FAST) */
+
+/* 4.2.5 */
+
+static void Reflection_coefficients P2( (L_ACF, r),
+ longword * L_ACF, /* 0...8 IN */
+ register word * r /* 0...7 OUT */
+)
+{
+ register int i, m, n;
+ register word temp;
+ register longword ltmp;
+ word ACF[9]; /* 0..8 */
+ word P[ 9]; /* 0..8 */
+ word K[ 9]; /* 2..8 */
+
+ /* Schur recursion with 16 bits arithmetic.
+ */
+
+ if (L_ACF[0] == 0) {
+ for (i = 8; i--; *r++ = 0) ;
+ return;
+ }
+
+ assert( L_ACF[0] != 0 );
+ temp = gsm_norm( L_ACF[0] );
+
+ assert(temp >= 0 && temp < 32);
+
+ /* ? overflow ? */
+ for (i = 0; i <= 8; i++) ACF[i] = SASR( L_ACF[i] << temp, 16 );
+
+ /* Initialize array P[..] and K[..] for the recursion.
+ */
+
+ for (i = 1; i <= 7; i++) K[ i ] = ACF[ i ];
+ for (i = 0; i <= 8; i++) P[ i ] = ACF[ i ];
+
+ /* Compute reflection coefficients
+ */
+ for (n = 1; n <= 8; n++, r++) {
+
+ temp = P[1];
+ temp = GSM_ABS(temp);
+ if (P[0] < temp) {
+ for (i = n; i <= 8; i++) *r++ = 0;
+ return;
+ }
+
+ *r = gsm_div( temp, P[0] );
+
+ assert(*r >= 0);
+ if (P[1] > 0) *r = -*r; /* r[n] = sub(0, r[n]) */
+ assert (*r != MIN_WORD);
+ if (n == 8) return;
+
+ /* Schur recursion
+ */
+ temp = GSM_MULT_R( P[1], *r );
+ P[0] = GSM_ADD( P[0], temp );
+
+ for (m = 1; m <= 8 - n; m++) {
+ temp = GSM_MULT_R( K[ m ], *r );
+ P[m] = GSM_ADD( P[ m+1 ], temp );
+
+ temp = GSM_MULT_R( P[ m+1 ], *r );
+ K[m] = GSM_ADD( K[ m ], temp );
+ }
+ }
+}
+
+/* 4.2.6 */
+
+static void Transformation_to_Log_Area_Ratios P1((r),
+ register word * r /* 0..7 IN/OUT */
+)
+/*
+ * The following scaling for r[..] and LAR[..] has been used:
+ *
+ * r[..] = integer( real_r[..]*32768. ); -1 <= real_r < 1.
+ * LAR[..] = integer( real_LAR[..] * 16384 );
+ * with -1.625 <= real_LAR <= 1.625
+ */
+{
+ register word temp;
+ register int i;
+
+
+ /* Computation of the LAR[0..7] from the r[0..7]
+ */
+ for (i = 1; i <= 8; i++, r++) {
+
+ temp = *r;
+ temp = GSM_ABS(temp);
+ assert(temp >= 0);
+
+ if (temp < 22118) {
+ temp >>= 1;
+ } else if (temp < 31130) {
+ assert( temp >= 11059 );
+ temp -= 11059;
+ } else {
+ assert( temp >= 26112 );
+ temp -= 26112;
+ temp <<= 2;
+ }
+
+ *r = *r < 0 ? -temp : temp;
+ assert( *r != MIN_WORD );
+ }
+}
+
+/* 4.2.7 */
+
+static void Quantization_and_coding P1((LAR),
+ register word * LAR /* [0..7] IN/OUT */
+)
+{
+ register word temp;
+ longword ltmp;
+
+
+ /* This procedure needs four tables; the following equations
+ * give the optimum scaling for the constants:
+ *
+ * A[0..7] = integer( real_A[0..7] * 1024 )
+ * B[0..7] = integer( real_B[0..7] * 512 )
+ * MAC[0..7] = maximum of the LARc[0..7]
+ * MIC[0..7] = minimum of the LARc[0..7]
+ */
+
+# undef STEP
+# define STEP( A, B, MAC, MIC ) \
+ temp = GSM_MULT( A, *LAR ); \
+ temp = GSM_ADD( temp, B ); \
+ temp = GSM_ADD( temp, 256 ); \
+ temp = SASR( temp, 9 ); \
+ *LAR = temp>MAC ? MAC - MIC : (temp<MIC ? 0 : temp - MIC); \
+ LAR++;
+
+ STEP( 20480, 0, 31, -32 );
+ STEP( 20480, 0, 31, -32 );
+ STEP( 20480, 2048, 15, -16 );
+ STEP( 20480, -2560, 15, -16 );
+
+ STEP( 13964, 94, 7, -8 );
+ STEP( 15360, -1792, 7, -8 );
+ STEP( 8534, -341, 3, -4 );
+ STEP( 9036, -1144, 3, -4 );
+
+# undef STEP
+}
+
+void Gsm_LPC_Analysis P3((S, s,LARc),
+ struct gsm_state *S,
+ word * s, /* 0..159 signals IN/OUT */
+ word * LARc) /* 0..7 LARc's OUT */
+{
+ longword L_ACF[9];
+
+#if defined(USE_FLOAT_MUL) && defined(FAST)
+ if (S->fast) Fast_Autocorrelation (s, L_ACF );
+ else
+#endif
+ Autocorrelation (s, L_ACF );
+ Reflection_coefficients (L_ACF, LARc );
+ Transformation_to_Log_Area_Ratios (LARc);
+ Quantization_and_coding (LARc);
+}
diff --git a/src/audio/gsm/src/preprocess.cpp b/src/audio/gsm/src/preprocess.cpp
new file mode 100644
index 0000000..fa16efe
--- /dev/null
+++ b/src/audio/gsm/src/preprocess.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/preprocess.c,v 1.2 1994/05/10 20:18:45 jutta Exp $ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+/* 4.2.0 .. 4.2.3 PREPROCESSING SECTION
+ *
+ * After A-law to linear conversion (or directly from the
+ * Ato D converter) the following scaling is assumed for
+ * input to the RPE-LTP algorithm:
+ *
+ * in: 0.1.....................12
+ * S.v.v.v.v.v.v.v.v.v.v.v.v.*.*.*
+ *
+ * Where S is the sign bit, v a valid bit, and * a "don't care" bit.
+ * The original signal is called sop[..]
+ *
+ * out: 0.1................... 12
+ * S.S.v.v.v.v.v.v.v.v.v.v.v.v.0.0
+ */
+
+
+void Gsm_Preprocess P3((S, s, so),
+ struct gsm_state * S,
+ word * s,
+ word * so ) /* [0..159] IN/OUT */
+{
+
+ word z1 = S->z1;
+ longword L_z2 = S->L_z2;
+ word mp = S->mp;
+
+ word s1;
+ longword L_s2;
+
+ longword L_temp;
+
+ word msp, lsp;
+ word SO;
+
+ longword ltmp; /* for ADD */
+ ulongword utmp; /* for L_ADD */
+
+ register int k = 160;
+
+ while (k--) {
+
+ /* 4.2.1 Downscaling of the input signal
+ */
+ SO = SASR( *s, 3 ) << 2;
+ s++;
+
+ assert (SO >= -0x4000); /* downscaled by */
+ assert (SO <= 0x3FFC); /* previous routine. */
+
+
+ /* 4.2.2 Offset compensation
+ *
+ * This part implements a high-pass filter and requires extended
+ * arithmetic precision for the recursive part of this filter.
+ * The input of this procedure is the array so[0...159] and the
+ * output the array sof[ 0...159 ].
+ */
+ /* Compute the non-recursive part
+ */
+
+ s1 = SO - z1; /* s1 = gsm_sub( *so, z1 ); */
+ z1 = SO;
+
+ assert(s1 != MIN_WORD);
+
+ /* Compute the recursive part
+ */
+ L_s2 = s1;
+ L_s2 <<= 15;
+
+ /* Execution of a 31 bv 16 bits multiplication
+ */
+
+ msp = SASR( L_z2, 15 );
+ lsp = L_z2-((longword)msp<<15); /* gsm_L_sub(L_z2,(msp<<15)); */
+
+ L_s2 += GSM_MULT_R( lsp, 32735 );
+ L_temp = (longword)msp * 32735; /* GSM_L_MULT(msp,32735) >> 1;*/
+ L_z2 = GSM_L_ADD( L_temp, L_s2 );
+
+ /* Compute sof[k] with rounding
+ */
+ L_temp = GSM_L_ADD( L_z2, 16384 );
+
+ /* 4.2.3 Preemphasis
+ */
+
+ msp = GSM_MULT_R( mp, -28180 );
+ mp = SASR( L_temp, 15 );
+ *so++ = GSM_ADD( mp, msp );
+ }
+
+ S->z1 = z1;
+ S->L_z2 = L_z2;
+ S->mp = mp;
+}
diff --git a/src/audio/gsm/src/rpe.cpp b/src/audio/gsm/src/rpe.cpp
new file mode 100644
index 0000000..eda4f18
--- /dev/null
+++ b/src/audio/gsm/src/rpe.cpp
@@ -0,0 +1,488 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/rpe.c,v 1.3 1994/05/10 20:18:46 jutta Exp $ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+/* 4.2.13 .. 4.2.17 RPE ENCODING SECTION
+ */
+
+/* 4.2.13 */
+
+static void Weighting_filter P2((e, x),
+ register word * e, /* signal [-5..0.39.44] IN */
+ word * x /* signal [0..39] OUT */
+)
+/*
+ * The coefficients of the weighting filter are stored in a table
+ * (see table 4.4). The following scaling is used:
+ *
+ * H[0..10] = integer( real_H[ 0..10] * 8192 );
+ */
+{
+ /* word wt[ 50 ]; */
+
+ register longword L_result;
+ register int k /* , i */ ;
+
+ /* Initialization of a temporary working array wt[0...49]
+ */
+
+ /* for (k = 0; k <= 4; k++) wt[k] = 0;
+ * for (k = 5; k <= 44; k++) wt[k] = *e++;
+ * for (k = 45; k <= 49; k++) wt[k] = 0;
+ *
+ * (e[-5..-1] and e[40..44] are allocated by the caller,
+ * are initially zero and are not written anywhere.)
+ */
+ e -= 5;
+
+ /* Compute the signal x[0..39]
+ */
+ for (k = 0; k <= 39; k++) {
+
+ L_result = 8192 >> 1;
+
+ /* for (i = 0; i <= 10; i++) {
+ * L_temp = GSM_L_MULT( wt[k+i], gsm_H[i] );
+ * L_result = GSM_L_ADD( L_result, L_temp );
+ * }
+ */
+
+#undef STEP
+#define STEP( i, H ) (e[ k + i ] * (longword)H)
+
+ /* Every one of these multiplications is done twice --
+ * but I don't see an elegant way to optimize this.
+ * Do you?
+ */
+
+#ifdef STUPID_COMPILER
+ L_result += STEP( 0, -134 ) ;
+ L_result += STEP( 1, -374 ) ;
+ /* + STEP( 2, 0 ) */
+ L_result += STEP( 3, 2054 ) ;
+ L_result += STEP( 4, 5741 ) ;
+ L_result += STEP( 5, 8192 ) ;
+ L_result += STEP( 6, 5741 ) ;
+ L_result += STEP( 7, 2054 ) ;
+ /* + STEP( 8, 0 ) */
+ L_result += STEP( 9, -374 ) ;
+ L_result += STEP( 10, -134 ) ;
+#else
+ L_result +=
+ STEP( 0, -134 )
+ + STEP( 1, -374 )
+ /* + STEP( 2, 0 ) */
+ + STEP( 3, 2054 )
+ + STEP( 4, 5741 )
+ + STEP( 5, 8192 )
+ + STEP( 6, 5741 )
+ + STEP( 7, 2054 )
+ /* + STEP( 8, 0 ) */
+ + STEP( 9, -374 )
+ + STEP(10, -134 )
+ ;
+#endif
+
+ /* L_result = GSM_L_ADD( L_result, L_result ); (* scaling(x2) *)
+ * L_result = GSM_L_ADD( L_result, L_result ); (* scaling(x4) *)
+ *
+ * x[k] = SASR( L_result, 16 );
+ */
+
+ /* 2 adds vs. >>16 => 14, minus one shift to compensate for
+ * those we lost when replacing L_MULT by '*'.
+ */
+
+ L_result = SASR( L_result, 13 );
+ x[k] = ( L_result < MIN_WORD ? MIN_WORD
+ : (L_result > MAX_WORD ? MAX_WORD : L_result ));
+ }
+}
+
+/* 4.2.14 */
+
+static void RPE_grid_selection P3((x,xM,Mc_out),
+ word * x, /* [0..39] IN */
+ word * xM, /* [0..12] OUT */
+ word * Mc_out /* OUT */
+)
+/*
+ * The signal x[0..39] is used to select the RPE grid which is
+ * represented by Mc.
+ */
+{
+ /* register word temp1; */
+ register int /* m, */ i;
+ register longword L_result, L_temp;
+ longword EM; /* xxx should be L_EM? */
+ word Mc;
+
+ longword L_common_0_3;
+
+ EM = 0;
+ Mc = 0;
+
+ /* for (m = 0; m <= 3; m++) {
+ * L_result = 0;
+ *
+ *
+ * for (i = 0; i <= 12; i++) {
+ *
+ * temp1 = SASR( x[m + 3*i], 2 );
+ *
+ * assert(temp1 != MIN_WORD);
+ *
+ * L_temp = GSM_L_MULT( temp1, temp1 );
+ * L_result = GSM_L_ADD( L_temp, L_result );
+ * }
+ *
+ * if (L_result > EM) {
+ * Mc = m;
+ * EM = L_result;
+ * }
+ * }
+ */
+
+#undef STEP
+#define STEP( m, i ) L_temp = SASR( x[m + 3 * i], 2 ); \
+ L_result += L_temp * L_temp;
+
+ /* common part of 0 and 3 */
+
+ L_result = 0;
+ STEP( 0, 1 ); STEP( 0, 2 ); STEP( 0, 3 ); STEP( 0, 4 );
+ STEP( 0, 5 ); STEP( 0, 6 ); STEP( 0, 7 ); STEP( 0, 8 );
+ STEP( 0, 9 ); STEP( 0, 10); STEP( 0, 11); STEP( 0, 12);
+ L_common_0_3 = L_result;
+
+ /* i = 0 */
+
+ STEP( 0, 0 );
+ L_result <<= 1; /* implicit in L_MULT */
+ EM = L_result;
+
+ /* i = 1 */
+
+ L_result = 0;
+ STEP( 1, 0 );
+ STEP( 1, 1 ); STEP( 1, 2 ); STEP( 1, 3 ); STEP( 1, 4 );
+ STEP( 1, 5 ); STEP( 1, 6 ); STEP( 1, 7 ); STEP( 1, 8 );
+ STEP( 1, 9 ); STEP( 1, 10); STEP( 1, 11); STEP( 1, 12);
+ L_result <<= 1;
+ if (L_result > EM) {
+ Mc = 1;
+ EM = L_result;
+ }
+
+ /* i = 2 */
+
+ L_result = 0;
+ STEP( 2, 0 );
+ STEP( 2, 1 ); STEP( 2, 2 ); STEP( 2, 3 ); STEP( 2, 4 );
+ STEP( 2, 5 ); STEP( 2, 6 ); STEP( 2, 7 ); STEP( 2, 8 );
+ STEP( 2, 9 ); STEP( 2, 10); STEP( 2, 11); STEP( 2, 12);
+ L_result <<= 1;
+ if (L_result > EM) {
+ Mc = 2;
+ EM = L_result;
+ }
+
+ /* i = 3 */
+
+ L_result = L_common_0_3;
+ STEP( 3, 12 );
+ L_result <<= 1;
+ if (L_result > EM) {
+ Mc = 3;
+ EM = L_result;
+ }
+
+ /**/
+
+ /* Down-sampling by a factor 3 to get the selected xM[0..12]
+ * RPE sequence.
+ */
+ for (i = 0; i <= 12; i ++) xM[i] = x[Mc + 3*i];
+ *Mc_out = Mc;
+}
+
+/* 4.12.15 */
+
+static void APCM_quantization_xmaxc_to_exp_mant P3((xmaxc,exp_out,mant_out),
+ word xmaxc, /* IN */
+ word * exp_out, /* OUT */
+ word * mant_out ) /* OUT */
+{
+ word exp, mant;
+
+ /* Compute exponent and mantissa of the decoded version of xmaxc
+ */
+
+ exp = 0;
+ if (xmaxc > 15) exp = SASR(xmaxc, 3) - 1;
+ mant = xmaxc - (exp << 3);
+
+ if (mant == 0) {
+ exp = -4;
+ mant = 7;
+ }
+ else {
+ while (mant <= 7) {
+ mant = mant << 1 | 1;
+ exp--;
+ }
+ mant -= 8;
+ }
+
+ assert( exp >= -4 && exp <= 6 );
+ assert( mant >= 0 && mant <= 7 );
+
+ *exp_out = exp;
+ *mant_out = mant;
+}
+
+static void APCM_quantization P5((xM,xMc,mant_out,exp_out,xmaxc_out),
+ word * xM, /* [0..12] IN */
+
+ word * xMc, /* [0..12] OUT */
+ word * mant_out, /* OUT */
+ word * exp_out, /* OUT */
+ word * xmaxc_out /* OUT */
+)
+{
+ int i, itest;
+
+ word xmax, xmaxc, temp, temp1, temp2;
+ word exp, mant;
+
+
+ /* Find the maximum absolute value xmax of xM[0..12].
+ */
+
+ xmax = 0;
+ for (i = 0; i <= 12; i++) {
+ temp = xM[i];
+ temp = GSM_ABS(temp);
+ if (temp > xmax) xmax = temp;
+ }
+
+ /* Qantizing and coding of xmax to get xmaxc.
+ */
+
+ exp = 0;
+ temp = SASR( xmax, 9 );
+ itest = 0;
+
+ for (i = 0; i <= 5; i++) {
+
+ itest |= (temp <= 0);
+ temp = SASR( temp, 1 );
+
+ assert(exp <= 5);
+ if (itest == 0) exp++; /* exp = add (exp, 1) */
+ }
+
+ assert(exp <= 6 && exp >= 0);
+ temp = exp + 5;
+
+ assert(temp <= 11 && temp >= 0);
+ xmaxc = gsm_add( SASR(xmax, temp), exp << 3 );
+
+ /* Quantizing and coding of the xM[0..12] RPE sequence
+ * to get the xMc[0..12]
+ */
+
+ APCM_quantization_xmaxc_to_exp_mant( xmaxc, &exp, &mant );
+
+ /* This computation uses the fact that the decoded version of xmaxc
+ * can be calculated by using the exponent and the mantissa part of
+ * xmaxc (logarithmic table).
+ * So, this method avoids any division and uses only a scaling
+ * of the RPE samples by a function of the exponent. A direct
+ * multiplication by the inverse of the mantissa (NRFAC[0..7]
+ * found in table 4.5) gives the 3 bit coded version xMc[0..12]
+ * of the RPE samples.
+ */
+
+
+ /* Direct computation of xMc[0..12] using table 4.5
+ */
+
+ assert( exp <= 4096 && exp >= -4096);
+ assert( mant >= 0 && mant <= 7 );
+
+ temp1 = 6 - exp; /* normalization by the exponent */
+ temp2 = gsm_NRFAC[ mant ]; /* inverse mantissa */
+
+ for (i = 0; i <= 12; i++) {
+
+ assert(temp1 >= 0 && temp1 < 16);
+
+ temp = xM[i] << temp1;
+ temp = GSM_MULT( temp, temp2 );
+ temp = SASR(temp, 12);
+ xMc[i] = temp + 4; /* see note below */
+ }
+
+ /* NOTE: This equation is used to make all the xMc[i] positive.
+ */
+
+ *mant_out = mant;
+ *exp_out = exp;
+ *xmaxc_out = xmaxc;
+}
+
+/* 4.2.16 */
+
+static void APCM_inverse_quantization P4((xMc,mant,exp,xMp),
+ register word * xMc, /* [0..12] IN */
+ word mant,
+ word exp,
+ register word * xMp) /* [0..12] OUT */
+/*
+ * This part is for decoding the RPE sequence of coded xMc[0..12]
+ * samples to obtain the xMp[0..12] array. Table 4.6 is used to get
+ * the mantissa of xmaxc (FAC[0..7]).
+ */
+{
+ int i;
+ word temp, temp1, temp2, temp3;
+ longword ltmp;
+
+ assert( mant >= 0 && mant <= 7 );
+
+ temp1 = gsm_FAC[ mant ]; /* see 4.2-15 for mant */
+ temp2 = gsm_sub( 6, exp ); /* see 4.2-15 for exp */
+ temp3 = gsm_asl( 1, gsm_sub( temp2, 1 ));
+
+ for (i = 13; i--;) {
+
+ assert( *xMc <= 7 && *xMc >= 0 ); /* 3 bit unsigned */
+
+ /* temp = gsm_sub( *xMc++ << 1, 7 ); */
+ temp = (*xMc++ << 1) - 7; /* restore sign */
+ assert( temp <= 7 && temp >= -7 ); /* 4 bit signed */
+
+ temp <<= 12; /* 16 bit signed */
+ temp = GSM_MULT_R( temp1, temp );
+ temp = GSM_ADD( temp, temp3 );
+ *xMp++ = gsm_asr( temp, temp2 );
+ }
+}
+
+/* 4.2.17 */
+
+static void RPE_grid_positioning P3((Mc,xMp,ep),
+ word Mc, /* grid position IN */
+ register word * xMp, /* [0..12] IN */
+ register word * ep /* [0..39] OUT */
+)
+/*
+ * This procedure computes the reconstructed long term residual signal
+ * ep[0..39] for the LTP analysis filter. The inputs are the Mc
+ * which is the grid position selection and the xMp[0..12] decoded
+ * RPE samples which are upsampled by a factor of 3 by inserting zero
+ * values.
+ */
+{
+ int i = 13;
+
+ assert(0 <= Mc && Mc <= 3);
+
+ switch (Mc) {
+ case 3: *ep++ = 0;
+ case 2: do {
+ *ep++ = 0;
+ case 1: *ep++ = 0;
+ case 0: *ep++ = *xMp++;
+ } while (--i);
+ }
+ while (++Mc < 4) *ep++ = 0;
+
+ /*
+
+ int i, k;
+ for (k = 0; k <= 39; k++) ep[k] = 0;
+ for (i = 0; i <= 12; i++) {
+ ep[ Mc + (3*i) ] = xMp[i];
+ }
+ */
+}
+
+/* 4.2.18 */
+
+/* This procedure adds the reconstructed long term residual signal
+ * ep[0..39] to the estimated signal dpp[0..39] from the long term
+ * analysis filter to compute the reconstructed short term residual
+ * signal dp[-40..-1]; also the reconstructed short term residual
+ * array dp[-120..-41] is updated.
+ */
+
+#if 0 /* Has been inlined in code.c */
+void Gsm_Update_of_reconstructed_short_time_residual_signal P3((dpp, ep, dp),
+ word * dpp, /* [0...39] IN */
+ word * ep, /* [0...39] IN */
+ word * dp) /* [-120...-1] IN/OUT */
+{
+ int k;
+
+ for (k = 0; k <= 79; k++)
+ dp[ -120 + k ] = dp[ -80 + k ];
+
+ for (k = 0; k <= 39; k++)
+ dp[ -40 + k ] = gsm_add( ep[k], dpp[k] );
+}
+#endif /* Has been inlined in code.c */
+
+void Gsm_RPE_Encoding P5((S,e,xmaxc,Mc,xMc),
+
+ struct gsm_state * S,
+
+ word * e, /* -5..-1][0..39][40..44 IN/OUT */
+ word * xmaxc, /* OUT */
+ word * Mc, /* OUT */
+ word * xMc) /* [0..12] OUT */
+{
+ word x[40];
+ word xM[13], xMp[13];
+ word mant, exp;
+
+ Weighting_filter(e, x);
+ RPE_grid_selection(x, xM, Mc);
+
+ APCM_quantization( xM, xMc, &mant, &exp, xmaxc);
+ APCM_inverse_quantization( xMc, mant, exp, xMp);
+
+ RPE_grid_positioning( *Mc, xMp, e );
+
+}
+
+void Gsm_RPE_Decoding P5((S, xmaxcr, Mcr, xMcr, erp),
+ struct gsm_state * S,
+
+ word xmaxcr,
+ word Mcr,
+ word * xMcr, /* [0..12], 3 bits IN */
+ word * erp /* [0..39] OUT */
+)
+{
+ word exp, mant;
+ word xMp[ 13 ];
+
+ APCM_quantization_xmaxc_to_exp_mant( xmaxcr, &exp, &mant );
+ APCM_inverse_quantization( xMcr, mant, exp, xMp );
+ RPE_grid_positioning( Mcr, xMp, erp );
+
+}
diff --git a/src/audio/gsm/src/short_term.cpp b/src/audio/gsm/src/short_term.cpp
new file mode 100644
index 0000000..f7829ba
--- /dev/null
+++ b/src/audio/gsm/src/short_term.cpp
@@ -0,0 +1,429 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/short_term.c,v 1.2 1994/05/10 20:18:47 jutta Exp $ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+/*
+ * SHORT TERM ANALYSIS FILTERING SECTION
+ */
+
+/* 4.2.8 */
+
+static void Decoding_of_the_coded_Log_Area_Ratios P2((LARc,LARpp),
+ word * LARc, /* coded log area ratio [0..7] IN */
+ word * LARpp) /* out: decoded .. */
+{
+ register word temp1 /* , temp2 */;
+ register long ltmp; /* for GSM_ADD */
+
+ /* This procedure requires for efficient implementation
+ * two tables.
+ *
+ * INVA[1..8] = integer( (32768 * 8) / real_A[1..8])
+ * MIC[1..8] = minimum value of the LARc[1..8]
+ */
+
+ /* Compute the LARpp[1..8]
+ */
+
+ /* for (i = 1; i <= 8; i++, B++, MIC++, INVA++, LARc++, LARpp++) {
+ *
+ * temp1 = GSM_ADD( *LARc, *MIC ) << 10;
+ * temp2 = *B << 1;
+ * temp1 = GSM_SUB( temp1, temp2 );
+ *
+ * assert(*INVA != MIN_WORD);
+ *
+ * temp1 = GSM_MULT_R( *INVA, temp1 );
+ * *LARpp = GSM_ADD( temp1, temp1 );
+ * }
+ */
+
+#undef STEP
+#define STEP( B, MIC, INVA ) \
+ temp1 = GSM_ADD( *LARc++, MIC ) << 10; \
+ temp1 = GSM_SUB( temp1, B << 1 ); \
+ temp1 = GSM_MULT_R( INVA, temp1 ); \
+ *LARpp++ = GSM_ADD( temp1, temp1 );
+
+ STEP( 0, -32, 13107 );
+ STEP( 0, -32, 13107 );
+ STEP( 2048, -16, 13107 );
+ STEP( -2560, -16, 13107 );
+
+ STEP( 94, -8, 19223 );
+ STEP( -1792, -8, 17476 );
+ STEP( -341, -4, 31454 );
+ STEP( -1144, -4, 29708 );
+
+ /* NOTE: the addition of *MIC is used to restore
+ * the sign of *LARc.
+ */
+}
+
+/* 4.2.9 */
+/* Computation of the quantized reflection coefficients
+ */
+
+/* 4.2.9.1 Interpolation of the LARpp[1..8] to get the LARp[1..8]
+ */
+
+/*
+ * Within each frame of 160 analyzed speech samples the short term
+ * analysis and synthesis filters operate with four different sets of
+ * coefficients, derived from the previous set of decoded LARs(LARpp(j-1))
+ * and the actual set of decoded LARs (LARpp(j))
+ *
+ * (Initial value: LARpp(j-1)[1..8] = 0.)
+ */
+
+static void Coefficients_0_12 P3((LARpp_j_1, LARpp_j, LARp),
+ register word * LARpp_j_1,
+ register word * LARpp_j,
+ register word * LARp)
+{
+ register int i;
+ register longword ltmp;
+
+ for (i = 1; i <= 8; i++, LARp++, LARpp_j_1++, LARpp_j++) {
+ *LARp = GSM_ADD( SASR( *LARpp_j_1, 2 ), SASR( *LARpp_j, 2 ));
+ *LARp = GSM_ADD( *LARp, SASR( *LARpp_j_1, 1));
+ }
+}
+
+static void Coefficients_13_26 P3((LARpp_j_1, LARpp_j, LARp),
+ register word * LARpp_j_1,
+ register word * LARpp_j,
+ register word * LARp)
+{
+ register int i;
+ register longword ltmp;
+ for (i = 1; i <= 8; i++, LARpp_j_1++, LARpp_j++, LARp++) {
+ *LARp = GSM_ADD( SASR( *LARpp_j_1, 1), SASR( *LARpp_j, 1 ));
+ }
+}
+
+static void Coefficients_27_39 P3((LARpp_j_1, LARpp_j, LARp),
+ register word * LARpp_j_1,
+ register word * LARpp_j,
+ register word * LARp)
+{
+ register int i;
+ register longword ltmp;
+
+ for (i = 1; i <= 8; i++, LARpp_j_1++, LARpp_j++, LARp++) {
+ *LARp = GSM_ADD( SASR( *LARpp_j_1, 2 ), SASR( *LARpp_j, 2 ));
+ *LARp = GSM_ADD( *LARp, SASR( *LARpp_j, 1 ));
+ }
+}
+
+
+static void Coefficients_40_159 P2((LARpp_j, LARp),
+ register word * LARpp_j,
+ register word * LARp)
+{
+ register int i;
+
+ for (i = 1; i <= 8; i++, LARp++, LARpp_j++)
+ *LARp = *LARpp_j;
+}
+
+/* 4.2.9.2 */
+
+static void LARp_to_rp P1((LARp),
+ register word * LARp) /* [0..7] IN/OUT */
+/*
+ * The input of this procedure is the interpolated LARp[0..7] array.
+ * The reflection coefficients, rp[i], are used in the analysis
+ * filter and in the synthesis filter.
+ */
+{
+ register int i;
+ register word temp;
+ register longword ltmp;
+
+ for (i = 1; i <= 8; i++, LARp++) {
+
+ /* temp = GSM_ABS( *LARp );
+ *
+ * if (temp < 11059) temp <<= 1;
+ * else if (temp < 20070) temp += 11059;
+ * else temp = GSM_ADD( temp >> 2, 26112 );
+ *
+ * *LARp = *LARp < 0 ? -temp : temp;
+ */
+
+ if (*LARp < 0) {
+ temp = *LARp == MIN_WORD ? MAX_WORD : -(*LARp);
+ *LARp = - ((temp < 11059) ? temp << 1
+ : ((temp < 20070) ? temp + 11059
+ : GSM_ADD( temp >> 2, 26112 )));
+ } else {
+ temp = *LARp;
+ *LARp = (temp < 11059) ? temp << 1
+ : ((temp < 20070) ? temp + 11059
+ : GSM_ADD( temp >> 2, 26112 ));
+ }
+ }
+}
+
+
+/* 4.2.10 */
+static void Short_term_analysis_filtering P4((S,rp,k_n,s),
+ struct gsm_state * S,
+ register word * rp, /* [0..7] IN */
+ register int k_n, /* k_end - k_start */
+ register word * s /* [0..n-1] IN/OUT */
+)
+/*
+ * This procedure computes the short term residual signal d[..] to be fed
+ * to the RPE-LTP loop from the s[..] signal and from the local rp[..]
+ * array (quantized reflection coefficients). As the call of this
+ * procedure can be done in many ways (see the interpolation of the LAR
+ * coefficient), it is assumed that the computation begins with index
+ * k_start (for arrays d[..] and s[..]) and stops with index k_end
+ * (k_start and k_end are defined in 4.2.9.1). This procedure also
+ * needs to keep the array u[0..7] in memory for each call.
+ */
+{
+ register word * u = S->u;
+ register int i;
+ register word di, zzz, ui, sav, rpi;
+ register longword ltmp;
+
+ for (; k_n--; s++) {
+
+ di = sav = *s;
+
+ for (i = 0; i < 8; i++) { /* YYY */
+
+ ui = u[i];
+ rpi = rp[i];
+ u[i] = sav;
+
+ zzz = GSM_MULT_R(rpi, di);
+ sav = GSM_ADD( ui, zzz);
+
+ zzz = GSM_MULT_R(rpi, ui);
+ di = GSM_ADD( di, zzz );
+ }
+
+ *s = di;
+ }
+}
+
+#if defined(USE_FLOAT_MUL) && defined(FAST)
+
+static void Fast_Short_term_analysis_filtering P4((S,rp,k_n,s),
+ struct gsm_state * S,
+ register word * rp, /* [0..7] IN */
+ register int k_n, /* k_end - k_start */
+ register word * s /* [0..n-1] IN/OUT */
+)
+{
+ register word * u = S->u;
+ register int i;
+
+ float uf[8],
+ rpf[8];
+
+ register float scalef = 3.0517578125e-5;
+ register float sav, di, temp;
+
+ for (i = 0; i < 8; ++i) {
+ uf[i] = u[i];
+ rpf[i] = rp[i] * scalef;
+ }
+ for (; k_n--; s++) {
+ sav = di = *s;
+ for (i = 0; i < 8; ++i) {
+ register float rpfi = rpf[i];
+ register float ufi = uf[i];
+
+ uf[i] = sav;
+ temp = rpfi * di + ufi;
+ di += rpfi * ufi;
+ sav = temp;
+ }
+ *s = di;
+ }
+ for (i = 0; i < 8; ++i) u[i] = uf[i];
+}
+#endif /* ! (defined (USE_FLOAT_MUL) && defined (FAST)) */
+
+static void Short_term_synthesis_filtering P5((S,rrp,k,wt,sr),
+ struct gsm_state * S,
+ register word * rrp, /* [0..7] IN */
+ register int k, /* k_end - k_start */
+ register word * wt, /* [0..k-1] IN */
+ register word * sr /* [0..k-1] OUT */
+)
+{
+ register word * v = S->v;
+ register int i;
+ register word sri, tmp1, tmp2;
+ register longword ltmp; /* for GSM_ADD & GSM_SUB */
+
+ while (k--) {
+ sri = *wt++;
+ for (i = 8; i--;) {
+
+ /* sri = GSM_SUB( sri, gsm_mult_r( rrp[i], v[i] ) );
+ */
+ tmp1 = rrp[i];
+ tmp2 = v[i];
+ tmp2 = ( tmp1 == MIN_WORD && tmp2 == MIN_WORD
+ ? MAX_WORD
+ : 0x0FFFF & (( (longword)tmp1 * (longword)tmp2
+ + 16384) >> 15)) ;
+
+ sri = GSM_SUB( sri, tmp2 );
+
+ /* v[i+1] = GSM_ADD( v[i], gsm_mult_r( rrp[i], sri ) );
+ */
+ tmp1 = ( tmp1 == MIN_WORD && sri == MIN_WORD
+ ? MAX_WORD
+ : 0x0FFFF & (( (longword)tmp1 * (longword)sri
+ + 16384) >> 15)) ;
+
+ v[i+1] = GSM_ADD( v[i], tmp1);
+ }
+ *sr++ = v[0] = sri;
+ }
+}
+
+
+#if defined(FAST) && defined(USE_FLOAT_MUL)
+
+static void Fast_Short_term_synthesis_filtering P5((S,rrp,k,wt,sr),
+ struct gsm_state * S,
+ register word * rrp, /* [0..7] IN */
+ register int k, /* k_end - k_start */
+ register word * wt, /* [0..k-1] IN */
+ register word * sr /* [0..k-1] OUT */
+)
+{
+ register word * v = S->v;
+ register int i;
+
+ float va[9], rrpa[8];
+ register float scalef = 3.0517578125e-5, temp;
+
+ for (i = 0; i < 8; ++i) {
+ va[i] = v[i];
+ rrpa[i] = (float)rrp[i] * scalef;
+ }
+ while (k--) {
+ register float sri = *wt++;
+ for (i = 8; i--;) {
+ sri -= rrpa[i] * va[i];
+ if (sri < -32768.) sri = -32768.;
+ else if (sri > 32767.) sri = 32767.;
+
+ temp = va[i] + rrpa[i] * sri;
+ if (temp < -32768.) temp = -32768.;
+ else if (temp > 32767.) temp = 32767.;
+ va[i+1] = temp;
+ }
+ *sr++ = va[0] = sri;
+ }
+ for (i = 0; i < 9; ++i) v[i] = va[i];
+}
+
+#endif /* defined(FAST) && defined(USE_FLOAT_MUL) */
+
+void Gsm_Short_Term_Analysis_Filter P3((S,LARc,s),
+
+ struct gsm_state * S,
+
+ word * LARc, /* coded log area ratio [0..7] IN */
+ word * s /* signal [0..159] IN/OUT */
+)
+{
+ word * LARpp_j = S->LARpp[ S->j ];
+ word * LARpp_j_1 = S->LARpp[ S->j ^= 1 ];
+
+ word LARp[8];
+
+#undef FILTER
+#if defined(FAST) && defined(USE_FLOAT_MUL)
+# define FILTER (* (S->fast \
+ ? Fast_Short_term_analysis_filtering \
+ : Short_term_analysis_filtering ))
+
+#else
+# define FILTER Short_term_analysis_filtering
+#endif
+
+ Decoding_of_the_coded_Log_Area_Ratios( LARc, LARpp_j );
+
+ Coefficients_0_12( LARpp_j_1, LARpp_j, LARp );
+ LARp_to_rp( LARp );
+ FILTER( S, LARp, 13, s);
+
+ Coefficients_13_26( LARpp_j_1, LARpp_j, LARp);
+ LARp_to_rp( LARp );
+ FILTER( S, LARp, 14, s + 13);
+
+ Coefficients_27_39( LARpp_j_1, LARpp_j, LARp);
+ LARp_to_rp( LARp );
+ FILTER( S, LARp, 13, s + 27);
+
+ Coefficients_40_159( LARpp_j, LARp);
+ LARp_to_rp( LARp );
+ FILTER( S, LARp, 120, s + 40);
+}
+
+void Gsm_Short_Term_Synthesis_Filter P4((S, LARcr, wt, s),
+ struct gsm_state * S,
+
+ word * LARcr, /* received log area ratios [0..7] IN */
+ word * wt, /* received d [0..159] IN */
+
+ word * s /* signal s [0..159] OUT */
+)
+{
+ word * LARpp_j = S->LARpp[ S->j ];
+ word * LARpp_j_1 = S->LARpp[ S->j ^=1 ];
+
+ word LARp[8];
+
+#undef FILTER
+#if defined(FAST) && defined(USE_FLOAT_MUL)
+
+# define FILTER (* (S->fast \
+ ? Fast_Short_term_synthesis_filtering \
+ : Short_term_synthesis_filtering ))
+#else
+# define FILTER Short_term_synthesis_filtering
+#endif
+
+ Decoding_of_the_coded_Log_Area_Ratios( LARcr, LARpp_j );
+
+ Coefficients_0_12( LARpp_j_1, LARpp_j, LARp );
+ LARp_to_rp( LARp );
+ FILTER( S, LARp, 13, wt, s );
+
+ Coefficients_13_26( LARpp_j_1, LARpp_j, LARp);
+ LARp_to_rp( LARp );
+ FILTER( S, LARp, 14, wt + 13, s + 13 );
+
+ Coefficients_27_39( LARpp_j_1, LARpp_j, LARp);
+ LARp_to_rp( LARp );
+ FILTER( S, LARp, 13, wt + 27, s + 27 );
+
+ Coefficients_40_159( LARpp_j, LARp );
+ LARp_to_rp( LARp );
+ FILTER(S, LARp, 120, wt + 40, s + 40);
+}
diff --git a/src/audio/gsm/src/table.cpp b/src/audio/gsm/src/table.cpp
new file mode 100644
index 0000000..0ed6f70
--- /dev/null
+++ b/src/audio/gsm/src/table.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/table.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */
+
+/* Most of these tables are inlined at their point of use.
+ */
+
+/* 4.4 TABLES USED IN THE FIXED POINT IMPLEMENTATION OF THE RPE-LTP
+ * CODER AND DECODER
+ *
+ * (Most of them inlined, so watch out.)
+ */
+
+#define GSM_TABLE_C
+#include "private.h"
+#include "gsm.h"
+
+/* Table 4.1 Quantization of the Log.-Area Ratios
+ */
+/* i 1 2 3 4 5 6 7 8 */
+word gsm_A[8] = {20480, 20480, 20480, 20480, 13964, 15360, 8534, 9036};
+word gsm_B[8] = { 0, 0, 2048, -2560, 94, -1792, -341, -1144};
+word gsm_MIC[8] = { -32, -32, -16, -16, -8, -8, -4, -4 };
+word gsm_MAC[8] = { 31, 31, 15, 15, 7, 7, 3, 3 };
+
+
+/* Table 4.2 Tabulation of 1/A[1..8]
+ */
+word gsm_INVA[8]={ 13107, 13107, 13107, 13107, 19223, 17476, 31454, 29708 };
+
+
+/* Table 4.3a Decision level of the LTP gain quantizer
+ */
+/* bc 0 1 2 3 */
+word gsm_DLB[4] = { 6554, 16384, 26214, 32767 };
+
+
+/* Table 4.3b Quantization levels of the LTP gain quantizer
+ */
+/* bc 0 1 2 3 */
+word gsm_QLB[4] = { 3277, 11469, 21299, 32767 };
+
+
+/* Table 4.4 Coefficients of the weighting filter
+ */
+/* i 0 1 2 3 4 5 6 7 8 9 10 */
+word gsm_H[11] = {-134, -374, 0, 2054, 5741, 8192, 5741, 2054, 0, -374, -134 };
+
+
+/* Table 4.5 Normalized inverse mantissa used to compute xM/xmax
+ */
+/* i 0 1 2 3 4 5 6 7 */
+word gsm_NRFAC[8] = { 29128, 26215, 23832, 21846, 20165, 18725, 17476, 16384 };
+
+
+/* Table 4.6 Normalized direct mantissa used to compute xM/xmax
+ */
+/* i 0 1 2 3 4 5 6 7 */
+word gsm_FAC[8] = { 18431, 20479, 22527, 24575, 26623, 28671, 30719, 32767 };
diff --git a/src/audio/media_buffer.cpp b/src/audio/media_buffer.cpp
new file mode 100644
index 0000000..cc62605
--- /dev/null
+++ b/src/audio/media_buffer.cpp
@@ -0,0 +1,128 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "media_buffer.h"
+#include <cstring>
+#include "audits/memman.h"
+
+////////////
+// PUBLIC
+////////////
+
+t_media_buffer::t_media_buffer(int size) {
+ buf_size = size;
+ empty = true;
+ buffer = new unsigned char[size];
+ MEMMAN_NEW_ARRAY(buffer);
+ pos_start = 0;
+ pos_end = 0;
+}
+
+t_media_buffer::~t_media_buffer() {
+ MEMMAN_DELETE_ARRAY(buffer);
+ delete [] buffer;
+}
+
+void t_media_buffer::add(unsigned char *data, int len) {
+ int data_start, data_end;
+
+ mtx.lock();
+
+ // The amount of data should fit in the buffer.
+ if (len > buf_size) {
+ mtx.unlock();
+ return;
+ }
+
+ int current_size_content = size_content();
+ if (empty) {
+ data_start = 0;
+ data_end = len - 1;
+ pos_start = 0;
+ empty = false;
+ } else {
+ data_start = (pos_end + 1) % buf_size;
+ data_end = (data_start + len - 1) % buf_size;
+ }
+
+ // Copy the data into the buffer
+ if (data_end >= data_start) {
+ memcpy(buffer + data_start, data, len);
+ } else {
+ // The data wraps around the end of the buffer
+ memcpy(buffer + data_start, data, buf_size - data_start);
+ memcpy(buffer, data + buf_size - data_start, data_end + 1);
+ }
+
+ // Check if the new data wrapped over the start of the old data.
+ // If so, then advance the start of the old data behind the end of the new
+ // data as new data has erased the oldest data.
+ if (buf_size - current_size_content < len) {
+ pos_start = (data_end + 1) % buf_size;
+ }
+
+ pos_end = data_end;
+
+ mtx.unlock();
+}
+
+bool t_media_buffer::get(unsigned char *data, int len) {
+ mtx.lock();
+
+ if (len > size_content()) {
+ mtx.unlock();
+ return false;
+ }
+
+ // Retrieve the data from the buffer
+ if (pos_start + len <= buf_size) {
+ memcpy(data, buffer + pos_start, len);
+ } else {
+ // The data to be retrieved wraps around the end of
+ // the buffer.
+ memcpy(data, buffer + pos_start, buf_size - pos_start);
+ memcpy(data + buf_size - pos_start, buffer, len - buf_size + pos_start);
+ }
+
+ pos_start = (pos_start + len) % buf_size;
+
+ // Check if buffer is empty
+ if (pos_start == (pos_end + 1) % buf_size) {
+ empty = true;
+ }
+
+ mtx.unlock();
+ return true;
+}
+
+int t_media_buffer::size_content(void) {
+ int len;
+
+ mtx.lock();
+
+ if (empty) {
+ len = 0;
+ } else if (pos_end >= pos_start) {
+ len = pos_end - pos_start + 1;
+ } else {
+ len = pos_end + buf_size - pos_start + 1;
+ }
+
+ mtx.unlock();
+ return len;
+}
diff --git a/src/audio/media_buffer.h b/src/audio/media_buffer.h
new file mode 100644
index 0000000..80658f6
--- /dev/null
+++ b/src/audio/media_buffer.h
@@ -0,0 +1,73 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _MEDIA_BUFFER_H
+#define _MEDIA_BUFFER_H
+
+#include "threads/mutex.h"
+
+// A buffer for buffering media streams.
+// Used for conference calls to buffer one stream that needs to be
+// mixed with the main stream.
+
+class t_media_buffer {
+private:
+ // The buffer. It is used as a ring buffer
+ unsigned char *buffer;
+
+ // Size of the buffer
+ int buf_size;
+
+ // Begin and end position of the buffer content.
+ // pos_end points to the last byte of content.
+ int pos_start;
+ int pos_end;
+
+ // Inidicates if buffer is empty
+ bool empty;
+
+ // Mutex to protect operations on the buffer
+ t_recursive_mutex mtx;
+
+ // Prevent this constructor from being used.
+ t_media_buffer() {};
+
+public:
+ // Create a media buffer of size size.
+ t_media_buffer(int size);
+ ~t_media_buffer();
+
+ // Add data to buffer. If there is more data than buffer
+ // space left, then old content will be removed from the
+ // buffer.
+ // - data is the data to be added
+ // - len is the number of bytes to be added
+ void add(unsigned char *data, int len);
+
+ // Get data from the buffer. If there is not enough data
+ // in the buffer, then no data is retrieved at all.
+ // False is returned if data cannot be retrieved.
+ // - data must point to a buffer of at least size len
+ // - len is the amount of bytes to be retrieved
+ bool get(unsigned char *data, int len);
+
+ // Return the number of bytes contained by the buffer.
+ int size_content(void);
+};
+
+#endif
diff --git a/src/audio/rtp_telephone_event.cpp b/src/audio/rtp_telephone_event.cpp
new file mode 100644
index 0000000..a076531
--- /dev/null
+++ b/src/audio/rtp_telephone_event.cpp
@@ -0,0 +1,83 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "cassert"
+#include "rtp_telephone_event.h"
+#include <netinet/in.h>
+
+void t_rtp_telephone_event::set_event(unsigned char _event) {
+ event = _event;
+}
+
+void t_rtp_telephone_event::set_volume(unsigned char _volume) {
+ volume = _volume;
+}
+
+void t_rtp_telephone_event::set_reserved(bool _reserved) {
+ reserved = _reserved;
+}
+
+void t_rtp_telephone_event::set_end(bool _end) {
+ end = _end;
+}
+
+void t_rtp_telephone_event::set_duration(unsigned short _duration) {
+ duration = htons(_duration);
+}
+
+unsigned char t_rtp_telephone_event::get_event(void) const {
+ return event;
+}
+
+unsigned char t_rtp_telephone_event::get_volume(void) const {
+ return volume;
+}
+
+bool t_rtp_telephone_event::get_reserved(void) const {
+ return reserved;
+}
+
+bool t_rtp_telephone_event::get_end(void) const {
+ return end;
+}
+
+unsigned short t_rtp_telephone_event::get_duration(void) const {
+ return ntohs(duration);
+}
+
+unsigned char char2dtmf_ev(char sym) {
+ if (sym >= '0' && sym <= '9') return (sym - '0' + TEL_EV_DTMF_0);
+ if (sym >= 'A' && sym <= 'D') return (sym - 'A' + TEL_EV_DTMF_A);
+ if (sym >= 'a' && sym <= 'd') return (sym- 'a' + TEL_EV_DTMF_A);
+ if (sym == '*') return TEL_EV_DTMF_STAR;
+ if (sym == '#') return TEL_EV_DTMF_POUND;
+ assert(false);
+}
+
+char dtmf_ev2char(unsigned char ev) {
+ if (ev <= TEL_EV_DTMF_9) {
+ return ev + '0' - TEL_EV_DTMF_0;
+ }
+ if (ev >= TEL_EV_DTMF_A && ev <= TEL_EV_DTMF_D) {
+ return ev + 'A' - TEL_EV_DTMF_A;
+ }
+ if (ev == TEL_EV_DTMF_STAR) return '*';
+ if (ev == TEL_EV_DTMF_POUND) return '#';
+ assert(false);
+}
+
diff --git a/src/audio/rtp_telephone_event.h b/src/audio/rtp_telephone_event.h
new file mode 100644
index 0000000..372504c
--- /dev/null
+++ b/src/audio/rtp_telephone_event.h
@@ -0,0 +1,79 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+// RFC 2833
+// RTP payload format for DTMF telephone events
+
+#ifndef _RTP_TELEPHONE_EVENT_H
+#define _RTP_TELEPHONE_EVENT_H
+
+
+// RFC 2833 3.10
+// DTMF events
+#define TEL_EV_DTMF_0 0
+#define TEL_EV_DTMF_1 1
+#define TEL_EV_DTMF_2 2
+#define TEL_EV_DTMF_3 3
+#define TEL_EV_DTMF_4 4
+#define TEL_EV_DTMF_5 5
+#define TEL_EV_DTMF_6 6
+#define TEL_EV_DTMF_7 7
+#define TEL_EV_DTMF_8 8
+#define TEL_EV_DTMF_9 9
+#define TEL_EV_DTMF_STAR 10
+#define TEL_EV_DTMF_POUND 11
+#define TEL_EV_DTMF_A 12
+#define TEL_EV_DTMF_B 13
+#define TEL_EV_DTMF_C 14
+#define TEL_EV_DTMF_D 15
+
+#define VALID_DTMF_EV(ev) ( (ev) <= TEL_EV_DTMF_D )
+#define VALID_DTMF_SYM(s) ( ((s) >= '0' && (s) <= '9') || \
+ ((s) >= 'a' && (s) <= 'd') || \
+ ((s) >= 'A' && (s) <= 'D') || \
+ (s) == '*' || (s) == '#' )
+
+// RFC 2833 3.5
+// Payload format (in network order!!)
+struct t_rtp_telephone_event {
+private:
+ unsigned char event : 8;
+ unsigned char volume : 6;
+ bool reserved : 1;
+ bool end : 1;
+ unsigned short duration : 16;
+
+public:
+ // Values set/get are in host order
+ void set_event(unsigned char _event);
+ void set_volume(unsigned char _volume);
+ void set_reserved(bool _reserved);
+ void set_end(bool _end);
+ void set_duration(unsigned short _duration);
+
+ unsigned char get_event(void) const;
+ unsigned char get_volume(void) const;
+ bool get_reserved(void) const;
+ bool get_end(void) const;
+ unsigned short get_duration(void) const;
+};
+
+unsigned char char2dtmf_ev(char sym);
+char dtmf_ev2char(unsigned char ev);
+
+#endif
diff --git a/src/audio/tone_gen.cpp b/src/audio/tone_gen.cpp
new file mode 100644
index 0000000..0f68c94
--- /dev/null
+++ b/src/audio/tone_gen.cpp
@@ -0,0 +1,245 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <sys/soundcard.h>
+#include <iostream>
+#include "tone_gen.h"
+#include "log.h"
+#include "sys_settings.h"
+#include "user.h"
+#include "userintf.h"
+#include "util.h"
+#include "audits/memman.h"
+#include "audio_device.h"
+
+// Number of samples read at once from the wav file
+#define NUM_SAMPLES_PER_TURN 1024
+
+// Duration of one turn in ms
+#define DURATION_TURN (NUM_SAMPLES_PER_TURN * 1000 / wav_info.samplerate)
+
+
+// Main function for play thread
+void *tone_gen_play(void *arg) {
+ ui->add_prohibited_thread();
+
+ t_tone_gen *tg = (t_tone_gen *)arg;
+ tg->play();
+
+ ui->remove_prohibited_thread();
+
+ return NULL;
+}
+
+t_tone_gen::t_tone_gen(const string &filename, const t_audio_device &_dev_tone) :
+ dev_tone(_dev_tone),
+ sema_finished(0)
+{
+ string f;
+
+ wav_file = NULL;
+ aio = 0;
+ valid = false;
+ data_buf = NULL;
+ thr_play = NULL;
+ loop = false;
+ pause = 0;
+
+ if (filename.size() == 0) return;
+
+ // Add share directory to filename
+ if (filename[0] != '/') {
+ f = sys_config->get_dir_share();
+ f += "/";
+ f += filename;
+ } else {
+ f = filename;
+ }
+
+ wav_filename = f;
+
+ memset(&wav_info, 0, sizeof(SF_INFO));
+ wav_file = sf_open(f.c_str(), SFM_READ, &wav_info);
+ if (!wav_file) {
+ string msg("Cannot open ");
+ msg += f;
+ log_file->write_report(msg, "t_tone_gen::t_tone_gen",
+ LOG_NORMAL, LOG_WARNING);
+ ui->cb_display_msg(msg, MSG_WARNING);
+ return;
+ }
+
+ log_file->write_header("t_tone_gen::t_tone_gen");
+ log_file->write_raw("Opened ");
+ log_file->write_raw(f);
+ log_file->write_endl();
+ log_file->write_footer();
+
+ valid = true;
+ stop_playing = false;
+}
+
+t_tone_gen::~t_tone_gen() {
+ if (wav_file) {
+ sf_close(wav_file);
+ }
+ if (aio) {
+ MEMMAN_DELETE(aio);
+ delete aio;
+ }
+ aio = 0;
+ if (data_buf) {
+ MEMMAN_DELETE_ARRAY(data_buf);
+ delete [] data_buf;
+ }
+ if (thr_play) {
+ MEMMAN_DELETE(thr_play);
+ delete thr_play;
+ }
+
+ log_file->write_report("Deleted tone generator.",
+ "t_tone_gen::~t_tone_gen");
+}
+
+bool t_tone_gen::is_valid(void) const {
+ return valid;
+}
+
+void t_tone_gen::play(void) {
+ if (!valid) {
+ log_file->write_report(
+ "Tone generator is invalid. Cannot play tone",
+ "t_tone_gen::play", LOG_NORMAL, LOG_WARNING);
+ sema_finished.up();
+ return;
+ }
+
+ aio = t_audio_io::open(dev_tone, true, false, true, wav_info.channels,
+ SAMPLEFORMAT_S16, wav_info.samplerate, false);
+ if (!aio) {
+ string msg("Failed to open sound card: ");
+ msg += get_error_str(errno);
+ log_file->write_report(msg, "t_tone_gen::play",
+ LOG_NORMAL, LOG_WARNING);
+ ui->cb_display_msg(msg, MSG_WARNING);
+ sema_finished.up();
+ return;
+ }
+
+ log_file->write_report("Start playing tone.",
+ "t_tone_gen::play");
+
+ do {
+ // Each samples consists of #channels shorts
+ data_buf = new short[NUM_SAMPLES_PER_TURN * wav_info.channels];
+ MEMMAN_NEW_ARRAY(data_buf);
+
+ sf_count_t frames_read = NUM_SAMPLES_PER_TURN;
+ while (frames_read == NUM_SAMPLES_PER_TURN) {
+ if (stop_playing) break;
+
+ // Play sample
+ frames_read = sf_readf_short(wav_file, data_buf, NUM_SAMPLES_PER_TURN);
+ if (frames_read > 0) {
+ aio->write((unsigned char*)data_buf,
+ frames_read * wav_info.channels * 2);
+ }
+ }
+
+ MEMMAN_DELETE_ARRAY(data_buf);
+ delete [] data_buf;
+ data_buf = NULL;
+
+ if (stop_playing) break;
+
+ // Pause between repetitions
+ if (loop) {
+ // Play silence
+ if (pause > 0) {
+ data_buf = new short[NUM_SAMPLES_PER_TURN * wav_info.channels];
+ MEMMAN_NEW_ARRAY(data_buf);
+ memset(data_buf, 0, NUM_SAMPLES_PER_TURN * wav_info.channels * 2);
+
+ for (int i = 0; i < pause; i += DURATION_TURN) {
+ aio->write((unsigned char*)data_buf,
+ NUM_SAMPLES_PER_TURN * wav_info.channels * 2);
+ if (stop_playing) break;
+ }
+
+ MEMMAN_DELETE_ARRAY(data_buf);
+ delete [] data_buf;
+ data_buf = NULL;
+ }
+
+ if (stop_playing) break;
+
+ // Set file pointer back to start of data
+ sf_seek(wav_file, 0, SEEK_SET);
+ }
+ } while (loop);
+
+ log_file->write_report("Tone ended.",
+ "t_tone_gen::play_tone");
+
+ sema_finished.up();
+}
+
+void t_tone_gen::start_play_thread(bool _loop, int _pause) {
+ loop = _loop;
+ pause = _pause;
+ thr_play = new t_thread(tone_gen_play, this);
+ MEMMAN_NEW(thr_play);
+ thr_play->detach();
+}
+
+void t_tone_gen::stop(void) {
+ log_file->write_report("Stopping tone.",
+ "t_tone_gen::stop");
+
+ if (stop_playing) {
+ log_file->write_report("Tone has stopped already.",
+ "t_tone_gen::stop");
+ return;
+ }
+
+ // This will stop the playing thread.
+ stop_playing = true;
+
+ // The semaphore will be upped by the playing thread as soon
+ // as playing finishes.
+ sema_finished.down();
+
+ log_file->write_report("Tone stopped.",
+ "t_tone_gen::stop");
+
+ if (aio) {
+ MEMMAN_DELETE(aio);
+ delete aio;
+ aio = 0;
+ }
+}
+
+
diff --git a/src/audio/tone_gen.h b/src/audio/tone_gen.h
new file mode 100644
index 0000000..1323fd4
--- /dev/null
+++ b/src/audio/tone_gen.h
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _TONE_GEN_H
+#define _TONE_GEN_H
+
+#include <string>
+#include <fstream>
+#include <cc++/config.h>
+#include <sndfile.h>
+#include "sys_settings.h"
+#include "threads/mutex.h"
+#include "threads/thread.h"
+#include "threads/sema.h"
+
+#ifndef _AUDIO_DEVICE_H
+class t_audio_io;
+#endif
+
+using namespace std;
+
+class t_tone_gen {
+private:
+ string wav_filename; // name of wav file
+ SNDFILE *wav_file; // SNDFILE pointer to wav file
+ SF_INFO wav_info; // Information about format of the wav file
+ t_audio_device dev_tone; // device to play tone
+ t_audio_io* aio; // soundcard
+ bool valid; // wav file is in a valid format
+ bool stop_playing; // indicates if playing should stop
+ t_thread *thr_play; // playing thread
+ bool loop; // repeat playing
+ int pause; // pause (ms) between repetitions
+ short *data_buf; // buffer for reading sound samples
+ t_semaphore sema_finished; // indicates if playing finished
+
+public:
+ t_tone_gen(const string &filename, const t_audio_device &_dev_tone);
+ ~t_tone_gen();
+
+ bool is_valid(void) const;
+
+ // Play the wav file
+ // loop = true -> repeat playing
+ // pause is pause in ms between repetitions
+ void start_play_thread(bool _loop, int _pause);
+ void play(void);
+
+ // Stop playing
+ void stop(void);
+};
+
+#endif
diff --git a/src/audio/twinkle_rtp_session.cpp b/src/audio/twinkle_rtp_session.cpp
new file mode 100644
index 0000000..8d2da01
--- /dev/null
+++ b/src/audio/twinkle_rtp_session.cpp
@@ -0,0 +1,117 @@
+
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <unistd.h>
+#include "twinkle_rtp_session.h"
+#include "log.h"
+#include "sys_settings.h"
+
+#define TWINKLE_ZID_FILE ".twinkle.zid"
+
+t_twinkle_rtp_session::~t_twinkle_rtp_session() {}
+
+#ifdef HAVE_ZRTP
+void t_twinkle_rtp_session::init_zrtp(void) {
+ string zid_filename = sys_config->get_dir_user();
+ zid_filename += '/';
+ zid_filename += TWINKLE_ZID_FILE;
+
+ if (initialize(zid_filename.c_str()) >=0) {
+ zrtp_initialized = true;
+ return;
+ }
+
+ // ZID file initialization failed. Maybe the ZID file
+ // is corrupt. Try to remove it
+ if (unlink(zid_filename.c_str()) < 0) {
+ string msg = "Failed to remove ";
+ msg += zid_filename;
+ log_file->write_report(msg,
+ "t_twinkle_rtp_session::init_zrtp",
+ LOG_NORMAL, LOG_CRITICAL);
+ return;
+ }
+
+ // Try to initialize once more
+ if (initialize(zid_filename.c_str()) >= 0) {
+ zrtp_initialized = true;
+ } else {
+ string msg = "Failed to initialize ZRTP - ";
+ msg += zid_filename;
+ log_file->write_report(msg,
+ "t_twinkle_rtp_session::init_zrtp",
+ LOG_NORMAL, LOG_CRITICAL);
+ }
+}
+
+bool t_twinkle_rtp_session::is_zrtp_initialized(void) const {
+ return zrtp_initialized;
+}
+
+t_twinkle_rtp_session::t_twinkle_rtp_session(const InetHostAddress &host) :
+ SymmetricZRTPSession(host),
+ zrtp_initialized(false)
+{
+ init_zrtp();
+}
+
+t_twinkle_rtp_session::t_twinkle_rtp_session(const InetHostAddress &host, unsigned short port) :
+ SymmetricZRTPSession(host, port) ,
+ zrtp_initialized(false)
+{
+ init_zrtp();
+}
+#else
+t_twinkle_rtp_session::t_twinkle_rtp_session(const InetHostAddress &host) :
+ SymmetricRTPSession(host)
+{
+}
+
+t_twinkle_rtp_session::t_twinkle_rtp_session(const InetHostAddress &host, unsigned short port) :
+ SymmetricRTPSession(host, port)
+{
+}
+#endif
+
+uint32 t_twinkle_rtp_session::getLastTimestamp(const SyncSource *src) const {
+ if ( src && !isMine(*src) ) return 0L;
+
+ recvLock.readLock();
+
+ uint32 ts = 0;
+ if (src != NULL) {
+ SyncSourceLink* srcm = getLink(*src);
+ IncomingRTPPktLink* l = srcm->getFirst();
+
+ while (l) {
+ ts = l->getTimestamp();
+ l = l->getSrcNext();
+ }
+ } else {
+ IncomingRTPPktLink* l = recvFirst;
+
+ while (l) {
+ ts = l->getTimestamp();
+ l = l->getNext();
+ }
+ }
+
+ recvLock.unlock();
+ return ts;
+}
diff --git a/src/audio/twinkle_rtp_session.h b/src/audio/twinkle_rtp_session.h
new file mode 100644
index 0000000..2bb9f62
--- /dev/null
+++ b/src/audio/twinkle_rtp_session.h
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef TWINKLE_RTP_SESSION_H
+#define TWINKLE_RTP_SESSION_H
+
+#include "twinkle_config.h"
+
+#include <string>
+
+#ifdef HAVE_ZRTP
+#include <libzrtpcpp/zrtpccrtp.h>
+#else
+#include <ccrtp/rtp.h>
+#endif
+
+using namespace std;
+using namespace ost;
+
+#ifdef HAVE_ZRTP
+class t_twinkle_rtp_session : public SymmetricZRTPSession {
+private:
+ bool zrtp_initialized;
+ void init_zrtp(void);
+public:
+ bool is_zrtp_initialized(void) const;
+#else
+class t_twinkle_rtp_session : public SymmetricRTPSession {
+#endif
+public:
+ virtual ~t_twinkle_rtp_session();
+
+ t_twinkle_rtp_session(const InetHostAddress &host);
+ t_twinkle_rtp_session(const InetHostAddress &host, unsigned short port);
+ uint32 getLastTimestamp(const SyncSource *src=NULL) const;
+};
+
+#endif
diff --git a/src/audio/twinkle_zrtp_ui.cpp b/src/audio/twinkle_zrtp_ui.cpp
new file mode 100644
index 0000000..d0709ca
--- /dev/null
+++ b/src/audio/twinkle_zrtp_ui.cpp
@@ -0,0 +1,296 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+// Author: Werner Dittmann <Werner.Dittmann@t-online.de>, (C) 2006
+// Michel de Boer <michel@twinklephone.com>
+#include "twinkle_zrtp_ui.h"
+
+#ifdef HAVE_ZRTP
+
+#include "phone.h"
+#include "line.h"
+#include "log.h"
+#include "util.h"
+
+using namespace std;
+using namespace GnuZrtpCodes;
+
+extern t_phone *phone;
+
+// Initialize static data
+map<int32, std::string>TwinkleZrtpUI::infoMap;
+map<int32, std::string>TwinkleZrtpUI::warningMap;
+map<int32, std::string>TwinkleZrtpUI::severeMap;
+map<int32, std::string>TwinkleZrtpUI::zrtpMap;
+
+bool TwinkleZrtpUI::mapsDone = false;
+std::string TwinkleZrtpUI::unknownCode = "Unknown error code";
+
+TwinkleZrtpUI::TwinkleZrtpUI(t_audio_session* session) :
+ audioSession(session)
+{
+ if (mapsDone) {
+ return;
+ }
+
+ // Initialize error mapping
+ infoMap.insert(pair<int32, std::string>(InfoHelloReceived,
+ string("Hello received, preparing a Commit")));
+ infoMap.insert(pair<int32, std::string>(InfoCommitDHGenerated,
+ string("Commit: Generated a public DH key")));
+ infoMap.insert(pair<int32, std::string>(InfoRespCommitReceived,
+ string("Responder: Commit received, preparing DHPart1")));
+ infoMap.insert(pair<int32, std::string>(InfoDH1DHGenerated,
+ string("DH1Part: Generated a public DH key")));
+ infoMap.insert(pair<int32, std::string>(InfoInitDH1Received,
+ string("Initiator: DHPart1 received, preparing DHPart2")));
+ infoMap.insert(pair<int32, std::string>(InfoRespDH2Received,
+ string("Responder: DHPart2 received, preparing Confirm1")));
+ infoMap.insert(pair<int32, std::string>(InfoInitConf1Received,
+ string("Initiator: Confirm1 received, preparing Confirm2")));
+ infoMap.insert(pair<int32, std::string>(InfoRespConf2Received,
+ string("Responder: Confirm2 received, preparing Conf2Ack")));
+ infoMap.insert(pair<int32, std::string>(InfoRSMatchFound,
+ string("At least one retained secrets matches - security OK")));
+ infoMap.insert(pair<int32, std::string>(InfoSecureStateOn,
+ string("Entered secure state")));
+ infoMap.insert(pair<int32, std::string>(InfoSecureStateOff,
+ string("No more security for this session")));
+
+ warningMap.insert(pair<int32, std::string>(WarningDHAESmismatch,
+ string("Commit contains an AES256 cipher but does not offer a Diffie-Helman 4096")));
+ warningMap.insert(pair<int32, std::string>(WarningGoClearReceived,
+ string("Received a GoClear message")));
+ warningMap.insert(pair<int32, std::string>(WarningDHShort,
+ string("Hello offers an AES256 cipher but does not offer a Diffie-Helman 4096")));
+ warningMap.insert(pair<int32, std::string>(WarningNoRSMatch,
+ string("No retained shared secrets available - must verify SAS")));
+ warningMap.insert(pair<int32, std::string>(WarningCRCmismatch,
+ string("Internal ZRTP packet checksum mismatch - packet dropped")));
+ warningMap.insert(pair<int32, std::string>(WarningSRTPauthError,
+ string("Dropping packet because SRTP authentication failed!")));
+ warningMap.insert(pair<int32, std::string>(WarningSRTPreplayError,
+ string("Dropping packet because SRTP replay check failed!")));
+ warningMap.insert(pair<int32, std::string>(WarningNoExpectedRSMatch,
+ string("Valid retained shared secrets availabe but no matches found - must verify SAS")));
+
+ severeMap.insert(pair<int32, std::string>(SevereHelloHMACFailed,
+ string("Hash HMAC check of Hello failed!")));
+ severeMap.insert(pair<int32, std::string>(SevereCommitHMACFailed,
+ string("Hash HMAC check of Commit failed!")));
+ severeMap.insert(pair<int32, std::string>(SevereDH1HMACFailed,
+ string("Hash HMAC check of DHPart1 failed!")));
+ severeMap.insert(pair<int32, std::string>(SevereDH2HMACFailed,
+ string("Hash HMAC check of DHPart2 failed!")));
+ severeMap.insert(pair<int32, std::string>(SevereCannotSend,
+ string("Cannot send data - connection or peer down?")));
+ severeMap.insert(pair<int32, std::string>(SevereProtocolError,
+ string("Internal protocol error occured!")));
+ severeMap.insert(pair<int32, std::string>(SevereNoTimer,
+ string("Cannot start a timer - internal resources exhausted?")));
+ severeMap.insert(pair<int32, std::string>(SevereTooMuchRetries,
+ string("Too much retries during ZRTP negotiation - connection or peer down?")));
+
+ zrtpMap.insert(pair<int32, std::string>(MalformedPacket,
+ string("Malformed packet (CRC OK, but wrong structure)")));
+ zrtpMap.insert(pair<int32, std::string>(CriticalSWError,
+ string("Critical software error")));
+ zrtpMap.insert(pair<int32, std::string>(UnsuppZRTPVersion,
+ string("Unsupported ZRTP version")));
+ zrtpMap.insert(pair<int32, std::string>(HelloCompMismatch,
+ string("Hello components mismatch")));
+ zrtpMap.insert(pair<int32, std::string>(UnsuppHashType,
+ string("Hash type not supported")));
+ zrtpMap.insert(pair<int32, std::string>(UnsuppCiphertype,
+ string("Cipher type not supported")));
+ zrtpMap.insert(pair<int32, std::string>(UnsuppPKExchange,
+ string("Public key exchange not supported")));
+ zrtpMap.insert(pair<int32, std::string>(UnsuppSRTPAuthTag,
+ string("SRTP auth. tag not supported")));
+ zrtpMap.insert(pair<int32, std::string>(UnsuppSASScheme,
+ string("SAS scheme not supported")));
+ zrtpMap.insert(pair<int32, std::string>(NoSharedSecret,
+ string("No shared secret available, DH mode required")));
+ zrtpMap.insert(pair<int32, std::string>(DHErrorWrongPV,
+ string("DH Error: bad pvi or pvr ( == 1, 0, or p-1)")));
+ zrtpMap.insert(pair<int32, std::string>(DHErrorWrongHVI,
+ string("DH Error: hvi != hashed data")));
+ zrtpMap.insert(pair<int32, std::string>(SASuntrustedMiTM,
+ string("Received relayed SAS from untrusted MiTM")));
+ zrtpMap.insert(pair<int32, std::string>(ConfirmHMACWrong,
+ string("Auth. Error: Bad Confirm pkt HMAC")));
+ zrtpMap.insert(pair<int32, std::string>(NonceReused,
+ string("Nonce reuse")));
+ zrtpMap.insert(pair<int32, std::string>(EqualZIDHello,
+ string("Equal ZIDs in Hello")));
+ zrtpMap.insert(pair<int32, std::string>(GoCleatNotAllowed,
+ string("GoClear packet received, but not allowed")));
+
+ mapsDone = true;
+
+}
+
+void TwinkleZrtpUI::secureOn(std::string cipher) {
+ audioSession->set_is_encrypted(true);
+ audioSession->set_srtp_cipher_mode(cipher);
+
+ t_line *line = audioSession->get_line();
+ int lineno = line->get_line_number();
+
+ log_file->write_header("TwinkleZrtpUI::secureOn");
+ log_file->write_raw("Line ");
+ log_file->write_raw(lineno + 1);
+ log_file->write_raw(": audio encryption enabled: ");
+ log_file->write_raw(cipher);
+ log_file->write_endl();
+ log_file->write_footer();
+
+ ui->cb_async_line_encrypted(lineno, true);
+ ui->cb_async_line_state_changed();
+}
+
+void TwinkleZrtpUI::secureOff() {
+ audioSession->set_is_encrypted(false);
+
+ t_line *line = audioSession->get_line();
+ int lineno = line->get_line_number();
+
+ log_file->write_header("TwinkleZrtpUI::secureOff");
+ log_file->write_raw("Line ");
+ log_file->write_raw(lineno + 1);
+ log_file->write_raw(": audio encryption disabled.\n");
+ log_file->write_footer();
+
+ ui->cb_async_line_encrypted(lineno, false);
+ ui->cb_async_line_state_changed();
+}
+
+void TwinkleZrtpUI::showSAS(std::string sas, bool verified) {
+ audioSession->set_zrtp_sas(sas);
+ audioSession->set_zrtp_sas_confirmed(verified);
+
+ t_line *line = audioSession->get_line();
+ int lineno = line->get_line_number();
+
+ log_file->write_header("TwinkleZrtpUI::showSAS");
+ log_file->write_raw("Line ");
+ log_file->write_raw(lineno + 1);
+ log_file->write_raw(": SAS =");
+ log_file->write_raw(sas);
+ log_file->write_endl();
+ log_file->write_footer();
+
+ if (!verified) {
+ ui->cb_async_show_zrtp_sas(lineno, sas);
+ }
+ ui->cb_async_line_state_changed();
+}
+
+void TwinkleZrtpUI::confirmGoClear() {
+ t_line *line = audioSession->get_line();
+ int lineno = line->get_line_number();
+
+ ui->cb_async_zrtp_confirm_go_clear(lineno);
+}
+
+void TwinkleZrtpUI::showMessage(MessageSeverity sev, int subCode) {
+ t_line *line = audioSession->get_line();
+ int lineno = line->get_line_number();
+
+ string msg = "Line ";
+ msg += int2str(lineno + 1);
+ msg += ": ";
+ msg += *mapCodesToString(sev, subCode);
+
+ switch (sev) {
+ case Info:
+ log_file->write_report(msg, "TwinkleZrtpUI::showMessage", LOG_NORMAL,
+ LOG_INFO);
+ break;
+ case Warning:
+ log_file->write_report(msg, "TwinkleZrtpUI::showMessage", LOG_NORMAL,
+ LOG_WARNING);
+ break;
+ default:
+ log_file->write_report(msg, "TwinkleZrtpUI::showMessage", LOG_NORMAL,
+ LOG_CRITICAL);
+ }
+}
+
+void TwinkleZrtpUI::zrtpNegotiationFailed(MessageSeverity severity, int subCode) {
+ t_line *line = audioSession->get_line();
+ int lineno = line->get_line_number();
+
+ string m = "Line ";
+ m += int2str(lineno + 1);
+ m += ": ZRTP negotiation failed.\n";
+ m += *mapCodesToString(severity, subCode);
+
+ switch (severity) {
+ case Info:
+ log_file->write_report(m, "TwinkleZrtpUI::zrtpNegotiationFailed", LOG_NORMAL,
+ LOG_INFO);
+ break;
+ case Warning:
+ log_file->write_report(m, "TwinkleZrtpUI::zrtpNegotiationFailed", LOG_NORMAL,
+ LOG_WARNING);
+ break;
+ default:
+ log_file->write_report(m, "TwinkleZrtpUI::zrtpNegotiationFailed", LOG_NORMAL,
+ LOG_CRITICAL);
+ }
+}
+
+void TwinkleZrtpUI::zrtpNotSuppOther() {
+ t_line *line = audioSession->get_line();
+ int lineno = line->get_line_number();
+
+ string msg = "Line ";
+ msg += int2str(lineno + 1);
+ msg += ": remote party does not support ZRTP.";
+ log_file->write_report(msg, "TwinkleZrtpUI::zrtpNotSuppOther");
+}
+
+const string *const TwinkleZrtpUI::mapCodesToString(MessageSeverity severity, int subCode) {
+ string *m = &unknownCode;
+
+ switch (severity) {
+ case Info:
+ m = &infoMap[subCode];
+ break;
+ case Warning:
+ m = &warningMap[subCode];
+ break;
+ case Severe:
+ m = &severeMap[subCode];
+ break;
+ case ZrtpError:
+ if (subCode < 0) {
+ subCode *= -1;
+ }
+ m = &zrtpMap[subCode];
+ break;
+ default:
+ break;
+ }
+
+ return m;
+}
+
+#endif
+
diff --git a/src/audio/twinkle_zrtp_ui.h b/src/audio/twinkle_zrtp_ui.h
new file mode 100644
index 0000000..66861dc
--- /dev/null
+++ b/src/audio/twinkle_zrtp_ui.h
@@ -0,0 +1,90 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+// Author: Werner Dittmann <Werner.Dittmann@t-online.de>, (C) 2006
+// Michel de Boer <michel@twinklephone.com>
+
+/**
+ * @file
+ * User interface call back functions for libzrtpcpp.
+ */
+
+#ifndef __TWINKLEZRTPUI_H_
+#define __TWINKLEZRTPUI_H_
+
+#include "twinkle_config.h"
+
+#ifdef HAVE_ZRTP
+
+#include <iostream>
+#include <libzrtpcpp/ZrtpQueue.h>
+#include <libzrtpcpp/ZrtpUserCallback.h>
+#include "audio_session.h"
+#include "userintf.h"
+
+using namespace GnuZrtpCodes;
+
+/** User interface for libzrtpcpp. */
+class TwinkleZrtpUI : public ZrtpUserCallback {
+
+ public:
+ /**
+ * Constructor.
+ * @param session [in] The audio session that is encrypted by ZRTP.
+ */
+ TwinkleZrtpUI(t_audio_session* session);
+ virtual ~TwinkleZrtpUI() {};
+
+ //@{
+ /** @name ZRTP call back functions called from the ZRTP thread */
+ virtual void secureOn(std::string cipher);
+ virtual void secureOff();
+ virtual void showSAS(std::string sas, bool verified);
+ virtual void confirmGoClear();
+ virtual void showMessage(MessageSeverity sev, int subCode);
+ virtual void zrtpNegotiationFailed(MessageSeverity severity, int subCode);
+ virtual void zrtpNotSuppOther();
+ //}
+
+ private:
+ /** Audio session associated with this user interface. */
+ t_audio_session* audioSession;
+
+ //@{
+ /** @name Message mappings for libzrtpcpp */
+ static map<int32, std::string> infoMap; /**< Info messages */
+ static map<int32, std::string> warningMap; /**< Warnings */
+ static map<int32, std::string> severeMap; /**< Severe errors */
+ static map<int32, std::string> zrtpMap; /**< ZRTP errors */
+ static bool mapsDone; /**< Flag to indicate that maps are initialized */
+ static std::string unknownCode; /**< Unknown error code */
+ //@}
+
+ /**
+ * Map a message code returned by libzrtpcpp to a message text.
+ * @param severity [in] The severity of the message.
+ * @param subCode [in] The message code.
+ * @return The message text.
+ */
+ const string *const mapCodesToString(MessageSeverity severity, int subCode);
+
+};
+
+#endif // HAVE_ZRTP
+#endif // __TWINKLEZRTPUI_H_
+