osmo-cc-ss5-endpoint/src/common/dsp.c

706 lines
21 KiB
C

/* DSP functions
*
* (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#define CHAN dsp->name
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <errno.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include "../libdebug/debug.h"
#include "dsp.h"
#include "sit.h"
//#define DEBUG_DEMODULATOR
#define NUM_TONES 8
#define db2level(db) pow(10, (double)(db) / 20.0)
#define level2db(level) (20 * log10(level))
/* SS5 defines -9 dB (tx), -16 dB (min) for SF, R1 defines -8/-20 dB (tx), -27 dB (min) for SF */
static double tone_dbm[NUM_TONES] = { -7, -7, -7, -7, -7, -7, -9, -9 };
static double tone_freq[NUM_TONES] = { 700, 900, 1100, 1300, 1500, 1700, 2600, 2400 };
static double tone_width[NUM_TONES] = { 25, 25, 25, 25, 25, 25, 25, 25 };
static double tone_min_dbm[NUM_TONES] = { -14, -14, -14, -14, -14, -14, -16, -16 };
static double tone_min_ampl_sq[NUM_TONES];
static double tone_diff_db[NUM_TONES] = { 4, 4, 4, 4, 4, 4, 5, 5 };
static double tone_diff_ampl_sq[NUM_TONES];
void dsp_set_sf(double sf_db, double sf_min_db)
{
tone_dbm[6] = sf_db;
tone_dbm[7] = sf_db;
tone_min_dbm[6] = sf_min_db;
tone_min_dbm[7] = sf_min_db;
}
int dsp_init_inst(dsp_t *dsp, void *priv, const char *name, double samplerate, int crosstalk, int comfort_noise, int delay_ms, double sense_db, double interrupt_recognition, double split_recognition, double mf_recognition, double kp_digit_duration, double other_digit_duration, double digit_pause, double pulse_pause, double notch, int ss5)
{
double tone_amplitude[NUM_TONES];
int t;
int rc;
PDEBUG(DDSP, DEBUG_DEBUG, "Init DSP instance.\n");
memset(dsp, 0, sizeof(*dsp));
dsp->priv = priv;
strncpy(dsp->name, name, sizeof(dsp->name - 1));
dsp->samplerate = samplerate;
dsp->crosstalk = crosstalk;
dsp->comfort_noise = comfort_noise;
// NO interrupt recognition, because the filter itself has a slow response
// dsp->interrupt_recognition = (int)(1000.0 * interrupt_recognition);
dsp->split_recognition = (int)(1000.0 * split_recognition);
dsp->mf_recognition = (int)(1000.0 * mf_recognition);
dsp->kp_digit_duration = kp_digit_duration;
dsp->other_digit_duration = other_digit_duration;
dsp->digit_pause = digit_pause;
dsp->pulse_pause = pulse_pause;
dsp->ms_per_sample = 1000.0 / samplerate;
dsp->detect_tone = ' ';
/* all levels are relative to 1mW */
for (t = 0; t < NUM_TONES; t++) {
tone_amplitude[t] = db2level(tone_dbm[t]);
tone_min_ampl_sq[t] = pow(db2level(tone_min_dbm[t] - sense_db), 2);
tone_diff_ampl_sq[t] = pow(db2level(tone_diff_db[t]), 2);
}
/* init MF modulator */
dsp->mf_mod = mf_mod_init(samplerate, NUM_TONES, tone_freq, tone_amplitude);
if (!dsp->mf_mod)
return -EINVAL;
/* init MF demodulator */
dsp->mf_demod = mf_demod_init(samplerate, NUM_TONES - (!ss5), tone_freq, tone_width);
if (!dsp->mf_mod)
return -EINVAL;
/* alloc delay buffer */
if (delay_ms) {
dsp->delay_length = (int)(dsp->samplerate * (double)delay_ms / 1000.0);
dsp->delay_buffer = calloc(dsp->delay_length, sizeof(*dsp->delay_buffer));
}
/* allocate jitter buffer */
rc = jitter_create(&dsp->tx_dejitter, "tx", 8000, sizeof(sample_t), JITTER_DATA);
if (rc < 0)
abort();
/* notch filter */
if (notch) {
dsp->notch = 1;
iir_notch_init(&dsp->notch_filter, notch, (int)dsp->samplerate, 1, 4);
}
return 0;
}
void dsp_cleanup_inst(dsp_t *dsp)
{
PDEBUG(DDSP, DEBUG_DEBUG, "Cleanup DSP instance.\n");
/* free FM modulator */
if (dsp->mf_mod) {
mf_mod_exit(dsp->mf_mod);
dsp->mf_mod = NULL;
}
/* free FM demodulator */
if (dsp->mf_demod) {
mf_demod_exit(dsp->mf_demod);
dsp->mf_demod = NULL;
}
/* free delay buffer */
if (dsp->delay_buffer) {
free(dsp->delay_buffer);
dsp->delay_buffer = NULL;
}
/* free jitter buffer */
jitter_destroy(&dsp->tx_dejitter);
}
/*
* tone encoder
*/
static struct dsp_digits {
char tone;
uint32_t mask;
} dsp_digits[] = {
{ '1', 0x01 + 0x02 },
{ '2', 0x01 + 0x04 },
{ '3', 0x02 + 0x04 },
{ '4', 0x01 + 0x08 },
{ '5', 0x02 + 0x08 },
{ '6', 0x04 + 0x08 },
{ '7', 0x01 + 0x10 },
{ '8', 0x02 + 0x10 },
{ '9', 0x04 + 0x10 },
{ '0', 0x08 + 0x10 },
{ '*', 0x01 + 0x20 }, /* code 11 */
{ '#', 0x02 + 0x20 }, /* code 12 */
{ 'a', 0x04 + 0x20 }, /* KP1 */
{ 'b', 0x08 + 0x20 }, /* KP2 */
{ 'c', 0x10 + 0x20 }, /* ST */
{ 'B', 0x40 }, /* 2600 busy */
{ 'A', 0x80 }, /* 2400 answer, acknowledge */
{ 'C', 0x40 + 0x80 }, /* 2600+2400 clear forward */
{ ' ', 0 }, /* silence */
{ 0 , 0 },
};
/* set signaling tone duration threshold */
void set_sig_detect_duration(dsp_t *dsp, double duration_AB, double duration_C)
{
dsp->detect_count = 0;
dsp->sig_detect_duration_AB = (int)(1000.0 * duration_AB);
dsp->sig_detect_duration_C = (int)(1000.0 * duration_C);
}
/* set given tone with duration (ms) or continuously (0) */
int set_tone(dsp_t *dsp, char tone, double duration)
{
int i;
dsp->tone = 0;
if (!tone) {
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Remove tone\n");
return 0;
}
for (i = 0; dsp_digits[i].tone; i++) {
if (dsp_digits[i].tone == tone) {
dsp->tone_mask = dsp_digits[i].mask;
dsp->tone = tone;
dsp->tone_duration = (int)(dsp->samplerate * duration);
if (duration)
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Set tone=\'%c\' duration=%.0fms (mask = 0x%02x)\n", dsp->tone, 1000.0 * duration, dsp->tone_mask);
else
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Set tone=\'%c\' duration=continuous (mask = 0x%02x)\n", dsp->tone, dsp->tone_mask);
return 0;
}
}
PDEBUG_CHAN(DDSP, DEBUG_ERROR, "Tone '%c' does not exist.\n", tone);
return -EINVAL;
}
void set_tone_transparent(dsp_t *dsp, int transparent)
{
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Set tones %stransparent.\n", (!transparent) ? "non-" : "");
dsp->tone_transparent = transparent;
}
void set_sit(dsp_t *dsp, int on)
{
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Turn sit %s.\n", (on) ? "on" : "off");
dsp->sit_on = on;
dsp->sit_count = 0;
}
static int digit_to_pulses(char digit)
{
if (digit >= '1' && digit <= '9')
return digit - '0';
if (digit == '0')
return 10;
if (digit == '*')
return 11;
if (digit == '#')
return 12;
return 0;
}
#define SF_PULSE_BREAK 0.060
#define SF_PULSE_MAKE 0.040
/* get next tone from dial string, if any */
static void get_tone_from_dial_string(dsp_t *dsp)
{
char tone;
double duration;
dsp->tone = 0;
next_digit:
if (dsp->dial_index == dsp->dial_length) {
dsp->dial_length = 0;
dialing_complete(dsp->priv);
return;
}
/* get alternating tone/pause from dial string */
if (!dsp->digit_on) {
/* start digit */
if (!dsp->pulsedialing) {
/* MF digit on */
tone = dsp->dial_string[dsp->dial_index++];
if (tone == 'a' || tone == 'b')
duration = dsp->kp_digit_duration;
else
duration = dsp->other_digit_duration;
dsp->digit_on = 1;
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send digit \'%c\' from dial string\n", tone);
} else {
/* SF pulse */
dsp->pulse_num = digit_to_pulses(dsp->dial_string[dsp->dial_index++]);
if (dsp->pulse_num == 0) {
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Skipping digit \'%c\' from dial string that cannot be puled\n", dsp->dial_string[dsp->dial_index]);
goto next_digit;
}
/* pulse on */
tone = 'B';
dsp->pulse_on = 1;
duration = SF_PULSE_BREAK;
dsp->digit_on = 1;
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send digit \'%c\' from dial string as pulses\n", dsp->dial_string[dsp->dial_index]);
}
} else {
/* ongoing digit */
if (dsp->pulse_num) {
if (dsp->pulse_on) {
/* pulse off */
tone = ' ';
dsp->pulse_on = 0;
duration = SF_PULSE_MAKE;
if (++dsp->pulse_count == dsp->pulse_num) {
dsp->pulse_count = 0;
dsp->digit_on = 0;
duration = dsp->pulse_pause;
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send pause after pulsing digits from dial string\n");
}
} else {
/* pulse on */
tone = 'B';
dsp->pulse_on = 1;
duration = SF_PULSE_BREAK;
}
} else {
/* digit pause */
tone = ' ';
dsp->digit_on = 0;
duration = dsp->digit_pause;
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send pause after digit from dial string\n");
}
}
set_tone(dsp, tone, duration);
}
/* set given dial string */
void set_dial_string(dsp_t *dsp, const char *dial, int pulsedialing)
{
dsp->digit_on = 0;
strncpy(dsp->dial_string, dial, sizeof(dsp->dial_string) - 1);
dsp->dial_index = 0;
dsp->dial_length = strlen(dsp->dial_string);
dsp->pulse_on = 0;
dsp->pulse_count = 0;
dsp->pulsedialing = pulsedialing;
}
/* determine which tones to be modulated, get next tone, if elapsed */
static int assemble_tones(dsp_t *dsp, uint32_t *mask, int length)
{
int i;
for (i = 0; i < length; i++) {
/* if tone was done, try to get next digit */
if (!dsp->tone) {
if (!dsp->dial_length)
return i;
get_tone_from_dial_string(dsp);
if (!dsp->tone)
return i;
}
*mask++ = dsp->tone_mask;
if (dsp->tone_duration) {
/* count down duration, if tones is not continuous */
if (!(--dsp->tone_duration))
dsp->tone = 0;
}
}
return i;
}
/*
* tone deencoder
*/
/* detection array for one frequency */
static char decode_one[8] =
{ ' ', ' ', ' ', ' ', ' ', ' ', 'B', 'A' }; /* A = 2400, B = 2600 */
/* detection matrix for two frequencies */
static char decode_two[8][8] =
{
{ ' ', '1', '2', '4', '7', '*', ' ', ' ' }, /* * = code 11 */
{ '1', ' ', '3', '5', '8', '#', ' ', ' ' }, /* # = code 12 */
{ '2', '3', ' ', '6', '9', 'a', ' ', ' ' }, /* a = KP1 */
{ '4', '5', '6', ' ', '0', 'b', ' ', ' ' }, /* b = KP2 */
{ '7', '8', '9', '0', ' ', 'c', ' ', ' ' }, /* c = ST */
{ '*', '#', 'a', 'b', 'c', ' ', ' ', ' ' },
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'C' }, /* C = 2600+2400 */
{ ' ', ' ', ' ', ' ', ' ', ' ', 'C', ' ' }
};
/* determine which tone is played */
static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_squared, int length, int incoming)
{
int f1, f2;
double f1_level_squared, f2_level_squared;
char tone;
int s, t, l;
for (s = 0; s < length; s++) {
/* only perform tone detection/muting every millisecond */
dsp->ms_interval += dsp->ms_per_sample;
dsp->ms_count++;
if (dsp->ms_interval < 1.0)
continue;
/* mute/notch if split duration reached (this is the chunk, see below for the rest) */
if (dsp->split_recognition && dsp->split_count == dsp->split_recognition) {
/* l is the number of samples required to mute for this interval of one milliseconds */
l = (dsp->ms_count < s + 1) ? dsp->ms_count : s + 1;
if (dsp->notch)
iir_process(&dsp->notch_filter, samples + s + 1 - l, l);
else
memset(samples + s + 1 - l, 0, l * sizeof(*samples));
}
dsp->ms_interval -= 1.0;
dsp->ms_count = 0;
#ifdef DEBUG_DEMODULATOR
if (incoming) {
for (t = 0; t < dsp->mf_demod->tones; t++) {
char level[20];
int db;
memset(level, 32, sizeof(level));
db = roundf(level2db(sqrt(levels_squared[t][s])) + 25);
if (db >= 0 && db < (int)sizeof(level))
level[db] = '*';
level[sizeof(level)-1]=0;
printf("%s|", level);
}
printf("\n");
}
#endif
/* find the tone which is the loudest */
f1 = -1;
f1_level_squared = -1.0;
for (t = 0; t < dsp->mf_demod->tones; t++) {
if (levels_squared[t][s] > f1_level_squared) {
f1_level_squared = levels_squared[t][s];
f1 = t;
}
}
/* find the tone which is the second loudest */
f2 = -1;
f2_level_squared = -1.0;
for (t = 0; t < dsp->mf_demod->tones; t++) {
if (t == f1)
continue;
if (levels_squared[t][s] > f2_level_squared) {
f2_level_squared = levels_squared[t][s];
f2 = t;
}
}
/* now check if the minimum level is reached */
if (f1 >= 0 && f1_level_squared < tone_min_ampl_sq[f1])
f1 = -1;
if (f2 >= 0 && f2_level_squared < tone_min_ampl_sq[f2])
f2 = -1;
#ifdef DEBUG_DEMODULATOR
if (incoming)
printf("%s f1=%.0f (%.1f dBm) f2=%.0f (%.1f dBm)\n", CHAN, (f1 >= 0) ? tone_freq[f1] : 0, level2db(sqrt(f1_level_squared)), (f2 >= 0) ? tone_freq[f2] : 0, level2db(sqrt(f2_level_squared)));
#endif
/* check if no, one or two tones are detected */
if (f1 < 0)
tone = ' ';
else if (f2 < 0)
tone = decode_one[f1];
else {
if (f2_level_squared * tone_diff_ampl_sq[f2] < f1_level_squared)
tone = ' ';
else
tone = decode_two[f1][f2];
}
/* process interrupt counting, keep tone until interrupt counter expires */
if (dsp->interrupt_recognition) {
if (dsp->detect_tone != ' ' && tone != dsp->detect_tone) {
#ifdef DEBUG_DEMODULATOR
if (!dsp->interrupt_count) if (incoming)
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "interruption detected s=%d old='%c' new='%c'\n", s, dsp->detect_tone, tone);
#endif
if (dsp->interrupt_count < dsp->interrupt_recognition) {
dsp->interrupt_count++;
tone = dsp->detect_tone;
}
} else {
#ifdef DEBUG_DEMODULATOR
if (dsp->interrupt_count) if (incoming)
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "interruption ceased at count %d s=%d old='%c' new='%c'\n", dsp->interrupt_count, s, dsp->detect_tone, tone);
#endif
dsp->interrupt_count = 0;
}
}
/* split audio, after minimum duration of detecting a tone */
if (tone >= 'A' && tone <= 'C') {
if (dsp->split_count < dsp->split_recognition)
dsp->split_count++;
} else
dsp->split_count = 0;
/* some change in tone */
if (dsp->detect_tone != tone) {
if (dsp->detect_count == 0)
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Detected new tone '%c' (%.1f dBm)\n", tone, level2db(sqrt(f1_level_squared)));
switch (tone) {
case 'A':
case 'B':
/* tone appears, wait some time */
if (dsp->detect_count < dsp->sig_detect_duration_AB)
dsp->detect_count++;
else {
#ifdef DEBUG_DEMODULATOR
if (incoming)
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Tone stable '%c' (%.1f dBm)\n", tone, level2db(sqrt(f1_level_squared)));
#endif
/* sign tone detected */
dsp->detect_count = 0;
dsp->detect_tone = tone;
receive_signal(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
}
break;
case 'C':
/* tone appears, wait some time */
if (dsp->detect_count < dsp->sig_detect_duration_C)
dsp->detect_count++;
else {
/* sign tone detected */
dsp->detect_count = 0;
dsp->detect_tone = tone;
receive_signal(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
}
break;
case ' ':
/* tone ceases, there is no counting here, since interrupt counting does the job for us */
dsp->detect_count = 0;
dsp->detect_tone = tone;
if (incoming)
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "cease\n");
receive_signal(dsp->priv, tone, 0.0);
break;
default:
/* tone appears, wait some time */
if (dsp->detect_count < dsp->mf_recognition)
dsp->detect_count++;
else {
/* sign tone detected */
dsp->detect_count = 0;
dsp->detect_tone = tone;
receive_signal(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
}
}
} else
dsp->detect_count = 0;
}
/* mute/notch if split duration reached (this is the rest, see above for each chunk) */
if (dsp->split_recognition && dsp->split_count == dsp->split_recognition) {
/* l is the number of samples that are left */
l = (dsp->ms_count < s) ? dsp->ms_count : s;
if (dsp->notch)
iir_process(&dsp->notch_filter, samples + s - l, l);
else
memset(samples + s - l, 0, l * sizeof(*samples));
}
}
/* process audio from one link (source) to another (destination) */
static void process_audio(dsp_t *dsp_a, dsp_t *dsp_b, int length)
{
sample_t samples[2][length], s;
sample_t b1[length], b2[length], b3[length], b4[length], b5[length], b6[length], b7[length], b8[length];
sample_t *levels_squared[NUM_TONES] = { b1, b2, b3, b4, b5, b6, b7, b8 };
uint32_t mask[length];
int16_t data[160];
int count1, count2;
int i;
/* get audio from jitter buffer */
jitter_load(&dsp_a->tx_dejitter, samples[0], length);
jitter_load(&dsp_b->tx_dejitter, samples[1], length);
/* optionally add (-36 dBm) comfort noise */
if (!dsp_a->cc_callref && dsp_a->comfort_noise) {
for (i = 0; i < length; i++)
samples[0][i] += (double)((int8_t)random()) / 16384.0;
}
if (!dsp_b->cc_callref && dsp_b->comfort_noise) {
for (i = 0; i < length; i++)
samples[1][i] += (double)((int8_t)random()) / 16385.0;
}
/* send SIT, if on */
if (dsp_a->sit_on)
dsp_a->sit_count = sit_play(samples[0], dsp_a->sit_count, length);
if (dsp_b->sit_on)
dsp_b->sit_count = sit_play(samples[1], dsp_b->sit_count, length);
/* modulate tone/digit. if no tone has to be played (or it stopped), count is less than length */
count1 = assemble_tones(dsp_a, mask, length);
mf_mod(dsp_a->mf_mod, mask, samples[0], count1, dsp_a->tone_transparent);
count2 = assemble_tones(dsp_b, mask, length);
mf_mod(dsp_b->mf_mod, mask, samples[1], count2, dsp_b->tone_transparent);
/* ! here is the bridge from a to b and from b to a ! */
/* optionally add one way delay */
if (dsp_b->delay_buffer) {
for (i = 0; i < length; i++) {
s = dsp_b->delay_buffer[dsp_b->delay_index];
dsp_b->delay_buffer[dsp_b->delay_index] = samples[0][i];
if (++(dsp_b->delay_index) == dsp_b->delay_length)
dsp_b->delay_index = 0;
samples[0][i] = s;
}
}
if (dsp_a->delay_buffer) {
for (i = 0; i < length; i++) {
s = dsp_a->delay_buffer[dsp_a->delay_index];
dsp_a->delay_buffer[dsp_a->delay_index] = samples[1][i];
if (++(dsp_a->delay_index) == dsp_a->delay_length)
dsp_a->delay_index = 0;
samples[1][i] = s;
}
}
/* demodulate and call tone detector */
mf_demod(dsp_b->mf_demod, samples[0], length, levels_squared);
detect_tones(dsp_b, samples[0], levels_squared, length, 1);
mf_demod(dsp_a->mf_demod, samples[1], length, levels_squared);
detect_tones(dsp_a, samples[1], levels_squared, length, 0);
/* optionally add some crosstalk */
if (dsp_a->crosstalk) {
/* use count, since it carries number of samples with signalling */
for (i = 0; i < count1; i++)
samples[1][i] += samples[0][i] / 70.0;
}
if (dsp_b->crosstalk) {
/* use count, since it carries number of samples with signalling */
for (i = 0; i < count2; i++)
samples[0][i] += samples[1][i] / 70.0;
}
/* forward audio to CC if call exists */
if (dsp_b->cc_callref && dsp_b->codec) {
samples_to_int16_1mw(data, samples[0], length);
osmo_cc_rtp_send(dsp_b->codec, (uint8_t *)data, length * 2, 0, 1, length, dsp_b);
}
if (dsp_a->cc_callref && dsp_a->codec) {
samples_to_int16_1mw(data, samples[1], length);
osmo_cc_rtp_send(dsp_a->codec, (uint8_t *)data, length * 2, 0, 1, length, dsp_a);
}
}
/* clock is called every given number of samples (20ms) */
void audio_clock(dsp_t *sunset_list, dsp_t *sunrise_list, int len)
{
dsp_t *dsp_a, *dsp_b;
if (!sunset_list)
return;
if (!sunrise_list) {
/* each pair of links on the same endpoint are bridged */
for (dsp_b = sunset_list; dsp_b; dsp_b = dsp_b->next) {
dsp_a = dsp_b;
dsp_b = dsp_b->next;
if (!dsp_b)
break;
process_audio(dsp_a, dsp_b, len);
}
} else {
/* each link on two endpoints are bridged */
for (dsp_a = sunset_list, dsp_b = sunrise_list; dsp_a && dsp_b; dsp_a = dsp_a->next, dsp_b = dsp_b->next) {
process_audio(dsp_a, dsp_b, len);
}
}
}
/* take audio from CC and store in jitter buffer */
void down_audio(struct osmo_cc_session_codec *codec, uint8_t __attribute__((unused)) marker, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len)
{
dsp_t *dsp = codec->media->session->priv;
int count = len/2;
sample_t samples[count];
int16_to_samples_1mw(samples, (int16_t *)data, count);
jitter_save(&dsp->tx_dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint16_t *src = (uint16_t *)src_data, *dst;
int len = src_len / 2, i;
dst = malloc(len * 2);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = htons(src[i]);
*dst_data = (uint8_t *)dst;
*dst_len = len * 2;
}
void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint16_t *src = (uint16_t *)src_data, *dst;
int len = src_len / 2, i;
dst = malloc(len * 2);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = ntohs(src[i]);
*dst_data = (uint8_t *)dst;
*dst_len = len * 2;
}