diff --git a/libs/openzap/mod_openzap/mod_openzap.c b/libs/openzap/mod_openzap/mod_openzap.c index c9159f3cb7..5cad5abb79 100644 --- a/libs/openzap/mod_openzap/mod_openzap.c +++ b/libs/openzap/mod_openzap/mod_openzap.c @@ -2878,7 +2878,7 @@ void dump_chan_xml(zap_span_t *span, uint32_t chan_id, switch_stream_handle_t *s ); } -#define OZ_SYNTAX "list || dump [] || q931_pcap on|off [pcapfilename without suffix]" +#define OZ_SYNTAX "list || dump [] || q931_pcap on|off [pcapfilename without suffix] || gains " SWITCH_STANDARD_API(oz_function) { char *mycmd = NULL, *argv[10] = { 0 }; @@ -3067,6 +3067,44 @@ SWITCH_STANDARD_API(oz_function) goto end; } + } else if (!strcasecmp(argv[0], "gains")) { + int i = 0; + float txgain = 0.0; + float rxgain = 0.0; + uint32_t chan_id = 0; + zap_span_t *span = NULL; + if (argc < 4) { + stream->write_function(stream, "-ERR Usage: oz gains []\n"); + goto end; + } + zap_span_find_by_name(argv[3], &span); + if (!span) { + stream->write_function(stream, "-ERR invalid span\n"); + goto end; + } + if (argc > 4) { + chan_id = atoi(argv[4]); + if (chan_id > span->chan_count) { + stream->write_function(stream, "-ERR invalid chan\n"); + goto end; + } + } + i = sscanf(argv[1], "%f", &rxgain); + i += sscanf(argv[2], "%f", &txgain); + if (i != 2) { + stream->write_function(stream, "-ERR invalid gains\n"); + goto end; + } + if (chan_id) { + zap_channel_command(span->channels[chan_id], ZAP_COMMAND_SET_RX_GAIN, &rxgain); + zap_channel_command(span->channels[chan_id], ZAP_COMMAND_SET_TX_GAIN, &txgain); + } else { + for (i = 1; i < span->chan_count; i++) { + zap_channel_command(span->channels[i], ZAP_COMMAND_SET_RX_GAIN, &rxgain); + zap_channel_command(span->channels[i], ZAP_COMMAND_SET_TX_GAIN, &txgain); + } + } + stream->write_function(stream, "+OK gains set to Rx %f and Tx %f\n", rxgain, txgain); } else { char *rply = zap_api_execute(cmd, NULL); diff --git a/libs/openzap/src/include/openzap.h b/libs/openzap/src/include/openzap.h index 18275c43bf..8fde5795d5 100644 --- a/libs/openzap/src/include/openzap.h +++ b/libs/openzap/src/include/openzap.h @@ -494,7 +494,8 @@ typedef enum { ZAP_TYPE_CHANNEL } zap_data_type_t; - +/* 2^8 table size, one for each byte value */ +#define ZAP_GAINS_TABLE_SIZE 256 struct zap_channel { zap_data_type_t data_type; uint32_t span_id; @@ -556,6 +557,10 @@ struct zap_channel { zap_hash_t *variable_hash; unsigned char rx_cas_bits; uint32_t pre_buffer_size; + unsigned char rxgain_table[ZAP_GAINS_TABLE_SIZE]; + unsigned char txgain_table[ZAP_GAINS_TABLE_SIZE]; + float rxgain; + float txgain; }; diff --git a/libs/openzap/src/include/zap_types.h b/libs/openzap/src/include/zap_types.h index 1ac2e86ba4..3bb726148e 100644 --- a/libs/openzap/src/include/zap_types.h +++ b/libs/openzap/src/include/zap_types.h @@ -69,6 +69,7 @@ typedef struct zap_interrupt zap_interrupt_t; #define ZAP_COMMAND_OBJ_INT *((int *)obj) #define ZAP_COMMAND_OBJ_CHAR_P (char *)obj +#define ZAP_COMMAND_OBJ_FLOAT *((float *)obj) #define ZAP_FSK_MOD_FACTOR 0x10000 #define ZAP_DEFAULT_DTMF_ON 250 #define ZAP_DEFAULT_DTMF_OFF 50 @@ -380,7 +381,9 @@ typedef enum { ZAP_CHANNEL_PROGRESS = (1 << 21), ZAP_CHANNEL_MEDIA = (1 << 22), ZAP_CHANNEL_ANSWERED = (1 << 23), - ZAP_CHANNEL_MUTE = (1 << 24) + ZAP_CHANNEL_MUTE = (1 << 24), + ZAP_CHANNEL_USE_RX_GAIN = (1 << 25), + ZAP_CHANNEL_USE_TX_GAIN = (1 << 26), } zap_channel_flag_t; typedef enum { diff --git a/libs/openzap/src/zap_io.c b/libs/openzap/src/zap_io.c index f0acf1ba27..8ca5e71b56 100644 --- a/libs/openzap/src/zap_io.c +++ b/libs/openzap/src/zap_io.c @@ -35,8 +35,6 @@ #ifndef WIN32 #endif #include "openzap.h" -//#include "zap_isdn.h" -//#include "zap_ss7_boost.h" #include #ifdef WIN32 #include @@ -569,8 +567,67 @@ OZ_DECLARE(zap_status_t) zap_span_load_tones(zap_span_t *span, const char *mapna } +#define ZAP_SLINEAR_MAX_VALUE 32767 +#define ZAP_SLINEAR_MIN_VALUE -32767 +static void reset_gain_table(unsigned char *gain_table, float new_gain, zap_codec_t codec_gain) +{ + /* sample value */ + unsigned sv = 0; + /* linear gain factor */ + float lingain = 0; + /* linear value for each table sample */ + float linvalue = 0; + /* amplified (or attenuated in case of negative amplification) sample value */ + int ampvalue = 0; + + /* gain tables are only for alaw and ulaw */ + if (codec_gain != ZAP_CODEC_ALAW && codec_gain != ZAP_CODEC_ULAW) { + zap_log(ZAP_LOG_WARNING, "Not resetting gain table because codec is not ALAW or ULAW but %d\n", codec_gain); + return; + } + + if (!new_gain) { + /* for a 0.0db gain table, each alaw/ulaw sample value is left untouched (0 ==0, 1 == 1, 2 == 2 etc)*/ + sv = 0; + while (1) { + gain_table[sv] = sv; + if (sv == (ZAP_GAINS_TABLE_SIZE - 1)) { + break; + } + sv++; + } + return; + } + + /* use the 20log rule to increase the gain: http://en.wikipedia.org/wiki/Gain, http:/en.wikipedia.org/wiki/20_log_rule#Definitions */ + lingain = pow(10.0, new_gain/ 20.0); + sv = 0; + while (1) { + /* get the linear value for this alaw/ulaw sample value */ + linvalue = codec_gain == ZAP_CODEC_ALAW ? alaw_to_linear(sv) : ulaw_to_linear(sv); + + /* multiply the linear value and the previously calculated linear gain */ + ampvalue = (int)(linvalue * lingain); + + /* chop it if goes beyond the limits */ + if (ampvalue > ZAP_SLINEAR_MAX_VALUE) { + ampvalue = ZAP_SLINEAR_MAX_VALUE; + } + + if (ampvalue < ZAP_SLINEAR_MIN_VALUE) { + ampvalue = ZAP_SLINEAR_MIN_VALUE; + } + gain_table[sv] = codec_gain == ZAP_CODEC_ALAW ? linear_to_alaw(ampvalue) : linear_to_ulaw(ampvalue); + if (sv == (ZAP_GAINS_TABLE_SIZE-1)) { + break; + } + sv++; + } +} + OZ_DECLARE(zap_status_t) zap_span_add_channel(zap_span_t *span, zap_socket_t sockfd, zap_chan_type_t type, zap_channel_t **chan) { + unsigned i = 0; if (span->chan_count < ZAP_MAX_CHANNELS_SPAN) { zap_channel_t *new_chan = span->channels[++span->chan_count]; @@ -608,6 +665,17 @@ OZ_DECLARE(zap_status_t) zap_span_add_channel(zap_span_t *span, zap_socket_t soc new_chan->dtmf_hangup_buf = calloc (span->dtmf_hangup_len + 1, sizeof (char)); + /* set 0.0db gain table */ + i = 0; + while (1) { + new_chan->txgain_table[i] = i; + new_chan->rxgain_table[i] = i; + if (i == (sizeof(new_chan->txgain_table)-1)) { + break; + } + i++; + } + zap_set_flag(new_chan, ZAP_CHANNEL_CONFIGURED | ZAP_CHANNEL_READY); *chan = new_chan; return ZAP_SUCCESS; @@ -1663,6 +1731,39 @@ OZ_DECLARE(zap_status_t) zap_channel_command(zap_channel_t *zchan, zap_command_t zap_mutex_unlock(zchan->pre_buffer_mutex); } break; + + case ZAP_COMMAND_SET_RX_GAIN: + { + zchan->rxgain = ZAP_COMMAND_OBJ_FLOAT; + reset_gain_table(zchan->rxgain_table, zchan->rxgain, zchan->native_codec); + if (zchan->rxgain == 0.0) { + zap_clear_flag(zchan, ZAP_CHANNEL_USE_RX_GAIN); + } else { + zap_set_flag(zchan, ZAP_CHANNEL_USE_RX_GAIN); + } + } + break; + case ZAP_COMMAND_GET_RX_GAIN: + { + ZAP_COMMAND_OBJ_FLOAT = zchan->rxgain; + } + break; + case ZAP_COMMAND_SET_TX_GAIN: + { + zchan->txgain = ZAP_COMMAND_OBJ_FLOAT; + reset_gain_table(zchan->txgain_table, zchan->txgain, zchan->native_codec); + if (zchan->txgain == 0.0) { + zap_clear_flag(zchan, ZAP_CHANNEL_USE_TX_GAIN); + } else { + zap_set_flag(zchan, ZAP_CHANNEL_USE_TX_GAIN); + } + } + break; + case ZAP_COMMAND_GET_TX_GAIN: + { + ZAP_COMMAND_OBJ_FLOAT = zchan->txgain; + } + break; default: break; } @@ -2042,10 +2143,10 @@ OZ_DECLARE(zap_status_t) zap_channel_read(zap_channel_t *zchan, void *data, zap_ zap_status_t status = ZAP_FAIL; zio_codec_t codec_func = NULL; zap_size_t max = *datalen; + unsigned i = 0; assert(zchan != NULL); assert(zchan->zio != NULL); - assert(zchan->zio != NULL); if (!zap_test_flag(zchan, ZAP_CHANNEL_OPEN)) { snprintf(zchan->last_error, sizeof(zchan->last_error), "channel not open"); @@ -2067,6 +2168,13 @@ OZ_DECLARE(zap_status_t) zap_channel_read(zap_channel_t *zchan, void *data, zap_ } if (status == ZAP_SUCCESS) { + if (zap_test_flag(zchan, ZAP_CHANNEL_USE_RX_GAIN) + && (zchan->native_codec == ZAP_CODEC_ALAW || zchan->native_codec == ZAP_CODEC_ULAW)) { + unsigned char *rdata = data; + for (i = 0; i < *datalen; i++) { + rdata[i] = zchan->rxgain_table[rdata[i]]; + } + } handle_dtmf(zchan, *datalen); } @@ -2265,6 +2373,7 @@ OZ_DECLARE(zap_status_t) zap_channel_write(zap_channel_t *zchan, void *data, zap zap_status_t status = ZAP_FAIL; zio_codec_t codec_func = NULL; zap_size_t max = datasize; + unsigned i = 0; assert(zchan != NULL); assert(zchan->zio != NULL); @@ -2314,6 +2423,13 @@ OZ_DECLARE(zap_status_t) zap_channel_write(zap_channel_t *zchan, void *data, zap } } + if (zap_test_flag(zchan, ZAP_CHANNEL_USE_TX_GAIN) + && (zchan->native_codec == ZAP_CODEC_ALAW || zchan->native_codec == ZAP_CODEC_ULAW)) { + unsigned char *wdata = data; + for (i = 0; i < *datalen; i++) { + wdata[i] = zchan->txgain_table[wdata[i]]; + } + } status = zchan->zio->write(zchan, data, datalen); return status;