diff options
Diffstat (limited to 'src/audio')
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_ + |