706 lines
21 KiB
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;
|
|
}
|
|
|