Newer
Older
s->td.mf.hits[2] = s->td.mf.hits[3];
s->td.mf.hits[3] = s->td.mf.hits[4];
s->td.mf.hits[4] = hit;
/* If we had a hit in this block, include it into mute fragment */
if (squelch && hit) {
if (mute.end < sample - MF_GSIZE) {
/* There is a gap between fragments */
mute_fragment(dsp, &mute);
mute.start = (sample > MF_GSIZE) ? (sample - MF_GSIZE) : 0;
}
mute.end = limit + MF_GSIZE;
/* Reinitialise the detector for the next block */
for (i = 0; i < 6; i++) {
goertzel_reset(&s->td.mf.tone_out[i]);
s->td.mf.current_sample = 0;
if (squelch && mute.end) {
if (mute.end > samples) {
s->td.mf.mute_samples = mute.end - samples;
mute.end = samples;
return (s->td.mf.current_hit); /* return the debounced hit */
}
static inline int pair_there(float p1, float p2, float i1, float i2, float e)
{
/* See if p1 and p2 are there, relative to i1 and i2 and total energy */
/* Make sure absolute levels are high enough */
Tilghman Lesher
committed
if ((p1 < TONE_MIN_THRESH) || (p2 < TONE_MIN_THRESH)) {
Tilghman Lesher
committed
}
/* Amplify ignored stuff */
i2 *= TONE_THRESH;
i1 *= TONE_THRESH;
e *= TONE_THRESH;
/* Check first tone */
Tilghman Lesher
committed
if ((p1 < i1) || (p1 < i2) || (p1 < e)) {
Tilghman Lesher
committed
}
Tilghman Lesher
committed
if ((p2 < i1) || (p2 < i2) || (p2 < e)) {
Tilghman Lesher
committed
}
/* Guess it's there... */
return 1;
}
static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len)
{
Alec L Davis
committed
short samp;
int newstate = DSP_TONE_STATE_SILENCE;
int freqcount = dsp->freqcount > FREQ_ARRAY_SIZE ? FREQ_ARRAY_SIZE : dsp->freqcount;
Joshua Colp
committed
while (len) {
/* Take the lesser of the number of samples we need and what we have */
pass = len;
Tilghman Lesher
committed
if (pass > dsp->gsamp_size - dsp->gsamps) {
Tilghman Lesher
committed
}
for (x = 0; x < pass; x++) {
Alec L Davis
committed
samp = s[x];
dsp->genergy += (int32_t) samp * (int32_t) samp;
Alec L Davis
committed
goertzel_sample(&dsp->freqs[y], samp);
Tilghman Lesher
committed
}
}
s += pass;
dsp->gsamps += pass;
len -= pass;
float hz[FREQ_ARRAY_SIZE];
for (y = 0; y < FREQ_ARRAY_SIZE; y++) {
hz[y] = goertzel_result(&dsp->freqs[y]);
Tilghman Lesher
committed
}
Joshua Colp
committed
switch (dsp->progmode) {
case PROG_MODE_NA:
if (pair_there(hz[HZ_480], hz[HZ_620], hz[HZ_350], hz[HZ_440], dsp->genergy)) {
newstate = DSP_TONE_STATE_BUSY;
} else if (pair_there(hz[HZ_440], hz[HZ_480], hz[HZ_350], hz[HZ_620], dsp->genergy)) {
newstate = DSP_TONE_STATE_RINGING;
} else if (pair_there(hz[HZ_350], hz[HZ_440], hz[HZ_480], hz[HZ_620], dsp->genergy)) {
newstate = DSP_TONE_STATE_DIALTONE;
} else if (hz[HZ_950] > TONE_MIN_THRESH * TONE_THRESH) {
newstate = DSP_TONE_STATE_SPECIAL1;
} else if (hz[HZ_1400] > TONE_MIN_THRESH * TONE_THRESH) {
/* End of SPECIAL1 or middle of SPECIAL2 */
if (dsp->tstate == DSP_TONE_STATE_SPECIAL1 || dsp->tstate == DSP_TONE_STATE_SPECIAL2) {
newstate = DSP_TONE_STATE_SPECIAL2;
} else if (hz[HZ_1800] > TONE_MIN_THRESH * TONE_THRESH) {
/* End of SPECIAL2 or middle of SPECIAL3 */
if (dsp->tstate == DSP_TONE_STATE_SPECIAL2 || dsp->tstate == DSP_TONE_STATE_SPECIAL3) {
newstate = DSP_TONE_STATE_SPECIAL3;
Tilghman Lesher
committed
}
} else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) {
newstate = DSP_TONE_STATE_TALKING;
Tilghman Lesher
committed
} else {
newstate = DSP_TONE_STATE_SILENCE;
Tilghman Lesher
committed
}
break;
case PROG_MODE_CR:
if (hz[HZ_425] > TONE_MIN_THRESH * TONE_THRESH) {
newstate = DSP_TONE_STATE_RINGING;
} else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) {
newstate = DSP_TONE_STATE_TALKING;
Tilghman Lesher
committed
} else {
newstate = DSP_TONE_STATE_SILENCE;
Tilghman Lesher
committed
}
case PROG_MODE_UK:
Tilghman Lesher
committed
if (hz[HZ_400UK] > TONE_MIN_THRESH * TONE_THRESH) {
newstate = DSP_TONE_STATE_HUNGUP;
Tilghman Lesher
committed
} else if (pair_there(hz[HZ_350UK], hz[HZ_440UK], hz[HZ_400UK], hz[HZ_400UK], dsp->genergy)) {
newstate = DSP_TONE_STATE_DIALTONE;
}
break;
ast_log(LOG_WARNING, "Can't process in unknown prog mode '%u'\n", dsp->progmode);
if (newstate == dsp->tstate) {
dsp->tcount++;
Tilghman Lesher
committed
if (dsp->ringtimeout) {
dsp->ringtimeout++;
Tilghman Lesher
committed
}
switch (dsp->tstate) {
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
case DSP_TONE_STATE_RINGING:
if ((dsp->features & DSP_PROGRESS_RINGING) &&
(dsp->tcount == THRESH_RING)) {
res = AST_CONTROL_RINGING;
dsp->ringtimeout = 1;
}
break;
case DSP_TONE_STATE_BUSY:
if ((dsp->features & DSP_PROGRESS_BUSY) &&
(dsp->tcount == THRESH_BUSY)) {
res = AST_CONTROL_BUSY;
dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
}
break;
case DSP_TONE_STATE_TALKING:
if ((dsp->features & DSP_PROGRESS_TALK) &&
(dsp->tcount == THRESH_TALK)) {
res = AST_CONTROL_ANSWER;
dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
}
break;
case DSP_TONE_STATE_SPECIAL3:
if ((dsp->features & DSP_PROGRESS_CONGESTION) &&
(dsp->tcount == THRESH_CONGESTION)) {
res = AST_CONTROL_CONGESTION;
dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
}
break;
case DSP_TONE_STATE_HUNGUP:
if ((dsp->features & DSP_FEATURE_CALL_PROGRESS) &&
(dsp->tcount == THRESH_HANGUP)) {
res = AST_CONTROL_HANGUP;
dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
}
break;
if (dsp->ringtimeout == THRESH_RING2ANSWER) {
Russell Bryant
committed
ast_debug(1, "Consider call as answered because of timeout after last ring\n");
res = AST_CONTROL_ANSWER;
dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
Russell Bryant
committed
ast_debug(5, "Stop state %d with duration %d\n", dsp->tstate, dsp->tcount);
ast_debug(5, "Start state %d\n", newstate);
dsp->tstate = newstate;
dsp->tcount = 1;
}
Tilghman Lesher
committed
for (x = 0; x < 7; x++) {
Tilghman Lesher
committed
}
dsp->gsamps = 0;
dsp->genergy = 0.0;
}
}
Russell Bryant
committed
return res;
}
int ast_dsp_call_progress(struct ast_dsp *dsp, struct ast_frame *inf)
{
if (inf->frametype != AST_FRAME_VOICE) {
ast_log(LOG_WARNING, "Can't check call progress of non-voice frames\n");
return 0;
}
if (!ast_format_cache_is_slinear(inf->subclass.format)) {
ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n");
return 0;
}
Michiel van Baak
committed
return __ast_dsp_call_progress(dsp, inf->data.ptr, inf->datalen / 2);
static int __ast_dsp_silence_noise(struct ast_dsp *dsp, short *s, int len, int *totalsilence, int *totalnoise, int *frames_energy)
Tilghman Lesher
committed
if (!len) {
Tilghman Lesher
committed
}
Tilghman Lesher
committed
for (x = 0; x < len; x++) {
Tilghman Lesher
committed
}
Martin Pycko
committed
accum /= len;
dsp->totalsilence += len / (dsp->sample_rate / 1000);
if (dsp->totalnoise) {
/* Move and save history */
Tilghman Lesher
committed
memmove(dsp->historicnoise + DSP_HISTORY - dsp->busycount, dsp->historicnoise + DSP_HISTORY - dsp->busycount + 1, dsp->busycount * sizeof(dsp->historicnoise[0]));
dsp->historicnoise[DSP_HISTORY - 1] = dsp->totalnoise;
Martin Pycko
committed
/* we don't want to check for busydetect that frequently */
#if 0
Martin Pycko
committed
#endif
}
dsp->totalnoise = 0;
res = 1;
} else {
dsp->totalnoise += len / (dsp->sample_rate / 1000);
Martin Pycko
committed
int silence1 = dsp->historicsilence[DSP_HISTORY - 1];
int silence2 = dsp->historicsilence[DSP_HISTORY - 2];
Tilghman Lesher
committed
memmove(dsp->historicsilence + DSP_HISTORY - dsp->busycount, dsp->historicsilence + DSP_HISTORY - dsp->busycount + 1, dsp->busycount * sizeof(dsp->historicsilence[0]));
dsp->historicsilence[DSP_HISTORY - 1] = dsp->totalsilence;
Martin Pycko
committed
/* check if the previous sample differs only by BUSY_PERCENT from the one before it */
if (silence1 < silence2) {
Tilghman Lesher
committed
if (silence1 + silence1 * BUSY_PERCENT / 100 >= silence2) {
Martin Pycko
committed
dsp->busymaybe = 1;
Tilghman Lesher
committed
} else {
Martin Pycko
committed
dsp->busymaybe = 0;
Tilghman Lesher
committed
}
Martin Pycko
committed
} else {
Tilghman Lesher
committed
if (silence1 - silence1 * BUSY_PERCENT / 100 <= silence2) {
Martin Pycko
committed
dsp->busymaybe = 1;
Tilghman Lesher
committed
} else {
Martin Pycko
committed
dsp->busymaybe = 0;
Tilghman Lesher
committed
}
Martin Pycko
committed
}
Tilghman Lesher
committed
if (totalsilence) {
Tilghman Lesher
committed
}
if (totalnoise) {
*totalnoise = dsp->totalnoise;
Tilghman Lesher
committed
}
if (frames_energy) {
*frames_energy = accum;
}
Martin Pycko
committed
int ast_dsp_busydetect(struct ast_dsp *dsp)
{
int res = 0, x;
#ifndef BUSYDETECT_TONEONLY
int avgsilence = 0, hitsilence = 0;
#endif
int avgtone = 0, hittone = 0;
/* if we have a 4 length pattern, the way busymaybe is set doesn't help us. */
if (dsp->busy_cadence.length != 4) {
if (!dsp->busymaybe) {
return res;
}
Tilghman Lesher
committed
}
Tilghman Lesher
committed
for (x = DSP_HISTORY - dsp->busycount; x < DSP_HISTORY; x++) {
Martin Pycko
committed
#ifndef BUSYDETECT_TONEONLY
avgsilence += dsp->historicsilence[x];
#endif
avgtone += dsp->historicnoise[x];
}
#ifndef BUSYDETECT_TONEONLY
avgsilence /= dsp->busycount;
#endif
avgtone /= dsp->busycount;
Tilghman Lesher
committed
for (x = DSP_HISTORY - dsp->busycount; x < DSP_HISTORY; x++) {
Martin Pycko
committed
#ifndef BUSYDETECT_TONEONLY
if (avgsilence > dsp->historicsilence[x]) {
Tilghman Lesher
committed
if (avgsilence - (avgsilence * BUSY_PERCENT / 100) <= dsp->historicsilence[x]) {
Martin Pycko
committed
hitsilence++;
Tilghman Lesher
committed
}
Martin Pycko
committed
} else {
Tilghman Lesher
committed
if (avgsilence + (avgsilence * BUSY_PERCENT / 100) >= dsp->historicsilence[x]) {
Martin Pycko
committed
hitsilence++;
Tilghman Lesher
committed
}
Martin Pycko
committed
}
#endif
if (avgtone > dsp->historicnoise[x]) {
Tilghman Lesher
committed
if (avgtone - (avgtone * BUSY_PERCENT / 100) <= dsp->historicnoise[x]) {
Martin Pycko
committed
hittone++;
Tilghman Lesher
committed
}
Martin Pycko
committed
} else {
Tilghman Lesher
committed
if (avgtone + (avgtone * BUSY_PERCENT / 100) >= dsp->historicnoise[x]) {
Martin Pycko
committed
hittone++;
Tilghman Lesher
committed
}
Martin Pycko
committed
}
}
#ifndef BUSYDETECT_TONEONLY
if ((hittone >= dsp->busycount - 1) && (hitsilence >= dsp->busycount - 1) &&
(avgtone >= BUSY_MIN && avgtone <= BUSY_MAX) &&
(avgsilence >= BUSY_MIN && avgsilence <= BUSY_MAX)) {
Martin Pycko
committed
#else
if ((hittone >= dsp->busycount - 1) && (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX)) {
#endif
#ifdef BUSYDETECT_COMPARE_TONE_AND_SILENCE
if (avgtone > avgsilence) {
Tilghman Lesher
committed
if (avgtone - avgtone*BUSY_PERCENT/100 <= avgsilence) {
Martin Pycko
committed
res = 1;
Tilghman Lesher
committed
}
Martin Pycko
committed
} else {
Tilghman Lesher
committed
if (avgtone + avgtone*BUSY_PERCENT/100 >= avgsilence) {
Martin Pycko
committed
res = 1;
Tilghman Lesher
committed
}
Martin Pycko
committed
}
#else
res = 1;
#endif
}
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
/* If we have a 4-length pattern, we can go ahead and just check it in a different way. */
if (dsp->busy_cadence.length == 4) {
int x;
int errors = 0;
int errors_max = ((4 * dsp->busycount) / 100.0) * BUSY_PAT_PERCENT;
for (x = DSP_HISTORY - (dsp->busycount); x < DSP_HISTORY; x += 2) {
int temp_error;
temp_error = abs(dsp->historicnoise[x] - dsp->busy_cadence.pattern[0]);
if ((temp_error * 100) / dsp->busy_cadence.pattern[0] > BUSY_PERCENT) {
errors++;
}
temp_error = abs(dsp->historicnoise[x + 1] - dsp->busy_cadence.pattern[2]);
if ((temp_error * 100) / dsp->busy_cadence.pattern[2] > BUSY_PERCENT) {
errors++;
}
temp_error = abs(dsp->historicsilence[x] - dsp->busy_cadence.pattern[1]);
if ((temp_error * 100) / dsp->busy_cadence.pattern[1] > BUSY_PERCENT) {
errors++;
}
temp_error = abs(dsp->historicsilence[x + 1] - dsp->busy_cadence.pattern[3]);
if ((temp_error * 100) / dsp->busy_cadence.pattern[3] > BUSY_PERCENT) {
errors++;
}
}
ast_debug(5, "errors = %d max = %d\n", errors, errors_max);
if (errors <= errors_max) {
return 1;
}
}
/* If we know the expected busy tone length, check we are in the range */
if (res && (dsp->busy_cadence.pattern[0] > 0)) {
if (abs(avgtone - dsp->busy_cadence.pattern[0]) > MAX(dsp->busy_cadence.pattern[0]*BUSY_PAT_PERCENT/100, 20)) {
#ifdef BUSYDETECT_DEBUG
ast_debug(5, "busy detector: avgtone of %d not close enough to desired %d\n",
avgtone, dsp->busy_cadence.pattern[0]);
#endif
res = 0;
}
}
/* If we know the expected busy tone silent-period length, check we are in the range */
if (res && (dsp->busy_cadence.pattern[1] > 0)) {
if (abs(avgsilence - dsp->busy_cadence.pattern[1]) > MAX(dsp->busy_cadence.pattern[1]*BUSY_PAT_PERCENT/100, 20)) {
#ifdef BUSYDETECT_DEBUG
ast_debug(5, "busy detector: avgsilence of %d not close enough to desired %d\n",
avgsilence, dsp->busy_cadence.pattern[1]);
#endif
res = 0;
}
}
#if !defined(BUSYDETECT_TONEONLY) && defined(BUSYDETECT_DEBUG)
if (res) {
ast_debug(5, "ast_dsp_busydetect detected busy, avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
} else {
ast_debug(5, "busy detector: FAILED with avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
Martin Pycko
committed
#endif
return res;
}
static int ast_dsp_silence_noise_with_energy(struct ast_dsp *dsp, struct ast_frame *f, int *total, int *frames_energy, int noise)
int x;
unsigned char *odata;
if (!f) {
return 0;
}
if (f->frametype != AST_FRAME_VOICE) {
ast_log(LOG_WARNING, "Can't calculate silence on a non-voice frame\n");
return 0;
}
if (ast_format_cache_is_slinear(f->subclass.format)) {
s = f->data.ptr;
len = f->datalen/2;
} else {
if (ast_format_cmp(f->subclass.format, ast_format_ulaw)) {
s = ast_alloca(len * 2);
for (x = 0; x < len; x++) {
s[x] = AST_MULAW(odata[x]);
}
} else if (ast_format_cmp(f->subclass.format, ast_format_alaw)) {
s = ast_alloca(len * 2);
for (x = 0; x < len; x++) {
s[x] = AST_ALAW(odata[x]);
}
} else {
ast_log(LOG_WARNING, "Can only calculate silence on signed-linear, alaw or ulaw frames :(\n");
if (noise) {
return __ast_dsp_silence_noise(dsp, s, len, NULL, total, frames_energy);
} else {
return __ast_dsp_silence_noise(dsp, s, len, total, NULL, frames_energy);
}
int ast_dsp_silence_with_energy(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence, int *frames_energy)
{
return ast_dsp_silence_noise_with_energy(dsp, f, totalsilence, frames_energy, 0);
}
int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
{
return ast_dsp_silence_noise_with_energy(dsp, f, totalsilence, NULL, 0);
int ast_dsp_noise(struct ast_dsp *dsp, struct ast_frame *f, int *totalnoise)
{
return ast_dsp_silence_noise_with_energy(dsp, f, totalnoise, NULL, 1);
}
Mark Spencer
committed
struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, struct ast_frame *af)
short *shortdata;
Tilghman Lesher
committed
if (!af) {
Tilghman Lesher
committed
}
if (af->frametype != AST_FRAME_VOICE) {
Tilghman Lesher
committed
}
Michiel van Baak
committed
odata = af->data.ptr;
len = af->datalen;
/* Make sure we have short data */
if (ast_format_cache_is_slinear(af->subclass.format)) {
Michiel van Baak
committed
shortdata = af->data.ptr;
} else if (ast_format_cmp(af->subclass.format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) {
shortdata = ast_alloca(af->datalen * 2);
for (x = 0; x < len; x++) {
shortdata[x] = AST_MULAW(odata[x]);
}
} else if (ast_format_cmp(af->subclass.format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) {
shortdata = ast_alloca(af->datalen * 2);
for (x = 0; x < len; x++) {
shortdata[x] = AST_ALAW(odata[x]);
}
/*Display warning only once. Otherwise you would get hundreds of warnings every second */
if (dsp->display_inband_dtmf_warning) {
ast_log(LOG_WARNING, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_format_get_name(af->subclass.format));
Tilghman Lesher
committed
}
dsp->display_inband_dtmf_warning = 0;
return af;
/* Initially we do not want to mute anything */
dsp->mute_fragments = 0;
/* Need to run the silence detection stuff for silence suppression and busy detection */
if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) || (dsp->features & DSP_FEATURE_BUSY_DETECT)) {
res = __ast_dsp_silence_noise(dsp, shortdata, len, &silence, NULL, NULL);
}
if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) && silence) {
memset(&dsp->f, 0, sizeof(dsp->f));
dsp->f.frametype = AST_FRAME_NULL;
return ast_frisolate(&dsp->f);
}
if ((dsp->features & DSP_FEATURE_BUSY_DETECT) && ast_dsp_busydetect(dsp)) {
ast_channel_softhangup_internal_flag_add(chan, AST_SOFTHANGUP_DEV);
memset(&dsp->f, 0, sizeof(dsp->f));
dsp->f.frametype = AST_FRAME_CONTROL;
dsp->f.subclass.integer = AST_CONTROL_BUSY;
ast_debug(1, "Requesting Hangup because the busy tone was detected on channel %s\n", ast_channel_name(chan));
return ast_frisolate(&dsp->f);
if ((dsp->features & DSP_FEATURE_FAX_DETECT)) {
if ((dsp->faxmode & DSP_FAXMODE_DETECT_CNG) && tone_detect(dsp, &dsp->cng_tone_state, shortdata, len)) {
fax_digit = 'f';
}
if ((dsp->faxmode & DSP_FAXMODE_DETECT_CED) && tone_detect(dsp, &dsp->ced_tone_state, shortdata, len)) {
fax_digit = 'e';
}
}
Alec L Davis
committed
if (dsp->features & (DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_BUSY_DETECT)) {
digit = mf_detect(dsp, &dsp->digit_state, shortdata, len, (dsp->digitmode & DSP_DIGITMODE_NOQUELCH) == 0, (dsp->digitmode & DSP_DIGITMODE_RELAXDTMF));
digit = dtmf_detect(dsp, &dsp->digit_state, shortdata, len, (dsp->digitmode & DSP_DIGITMODE_NOQUELCH) == 0, (dsp->digitmode & DSP_DIGITMODE_RELAXDTMF));
Tilghman Lesher
committed
int event = 0, event_len = 0;
char event_digit = 0;
if (!dsp->dtmf_began) {
/* We have not reported DTMF_BEGIN for anything yet */
Alec L Davis
committed
if (dsp->features & DSP_FEATURE_DIGIT_DETECT) {
event = AST_FRAME_DTMF_BEGIN;
event_digit = dsp->digit_state.digits[0];
}
dsp->dtmf_began = 1;
} else if (dsp->digit_state.current_digits > 1 || digit != dsp->digit_state.digits[0]) {
/* Digit changed. This means digit we have reported with DTMF_BEGIN ended */
Alec L Davis
committed
if (dsp->features & DSP_FEATURE_DIGIT_DETECT) {
event = AST_FRAME_DTMF_END;
event_digit = dsp->digit_state.digits[0];
event_len = dsp->digit_state.digitlen[0] * 1000 / dsp->sample_rate;
Alec L Davis
committed
}
Tilghman Lesher
committed
memmove(&dsp->digit_state.digits[0], &dsp->digit_state.digits[1], dsp->digit_state.current_digits);
memmove(&dsp->digit_state.digitlen[0], &dsp->digit_state.digitlen[1], dsp->digit_state.current_digits * sizeof(dsp->digit_state.digitlen[0]));
dsp->digit_state.current_digits--;
dsp->dtmf_began = 0;
if (dsp->features & DSP_FEATURE_BUSY_DETECT) {
/* Reset Busy Detector as we have some confirmed activity */
memset(dsp->historicsilence, 0, sizeof(dsp->historicsilence));
memset(dsp->historicnoise, 0, sizeof(dsp->historicnoise));
Alec L Davis
committed
ast_debug(1, "DTMF Detected - Reset busydetector\n");
}
if (event) {
memset(&dsp->f, 0, sizeof(dsp->f));
dsp->f.frametype = event;
dsp->f.subclass.integer = event_digit;
Tilghman Lesher
committed
dsp->f.len = event_len;
if (fax_digit) {
/* Fax was detected - digit is either 'f' or 'e' */
memset(&dsp->f, 0, sizeof(dsp->f));
dsp->f.frametype = AST_FRAME_DTMF;
dsp->f.subclass.integer = fax_digit;
if ((dsp->features & DSP_FEATURE_CALL_PROGRESS)) {
res = __ast_dsp_call_progress(dsp, shortdata, len);
if (res) {
Joshua Colp
committed
switch (res) {
case AST_CONTROL_ANSWER:
case AST_CONTROL_BUSY:
case AST_CONTROL_RINGING:
case AST_CONTROL_CONGESTION:
case AST_CONTROL_HANGUP:
memset(&dsp->f, 0, sizeof(dsp->f));
dsp->f.frametype = AST_FRAME_CONTROL;
dsp->f.subclass.integer = res;
dsp->f.src = "dsp_progress";
Mark Spencer
committed
ast_queue_frame(chan, &dsp->f);
break;
default:
ast_log(LOG_WARNING, "Don't know how to represent call progress message %d\n", res);
}
}
Tilghman Lesher
committed
} else if ((dsp->features & DSP_FEATURE_WAITDIALTONE)) {
res = __ast_dsp_call_progress(dsp, shortdata, len);
done:
/* Mute fragment of the frame */
for (x = 0; x < dsp->mute_fragments; x++) {
memset(shortdata + dsp->mute_data[x].start, 0, sizeof(int16_t) * (dsp->mute_data[x].end - dsp->mute_data[x].start));
}
if (ast_format_cmp(af->subclass.format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) {
Tilghman Lesher
committed
for (x = 0; x < len; x++) {
odata[x] = AST_LIN2MU((unsigned short) shortdata[x]);
Tilghman Lesher
committed
}
} else if (ast_format_cmp(af->subclass.format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) {
Tilghman Lesher
committed
for (x = 0; x < len; x++) {
odata[x] = AST_LIN2A((unsigned short) shortdata[x]);
Tilghman Lesher
committed
}
Tilghman Lesher
committed
if (chan) {
Tilghman Lesher
committed
}
static void ast_dsp_prog_reset(struct ast_dsp *dsp)
{
int max = 0;
int x;
dsp->gsamp_size = modes[dsp->progmode].size;
dsp->gsamps = 0;
for (x = 0; x < FREQ_ARRAY_SIZE; x++) {
Alec L Davis
committed
goertzel_init(&dsp->freqs[x], (float)modes[dsp->progmode].freqs[x], dsp->sample_rate);
dsp->ringtimeout = 0;
unsigned int ast_dsp_get_sample_rate(const struct ast_dsp *dsp)
{
return dsp->sample_rate;
}
static struct ast_dsp *__ast_dsp_new(unsigned int sample_rate)
if ((dsp = ast_calloc(1, sizeof(*dsp)))) {
dsp->threshold = DEFAULT_THRESHOLD;
dsp->features = DSP_FEATURE_SILENCE_SUPPRESS;
Martin Pycko
committed
dsp->busycount = DSP_HISTORY;
dsp->digitmode = DSP_DIGITMODE_DTMF;
dsp->faxmode = DSP_FAXMODE_DETECT_CNG;
dsp->sample_rate = sample_rate;
/* Initialize digit detector */
ast_digit_detect_init(&dsp->digit_state, dsp->digitmode & DSP_DIGITMODE_MF, dsp->sample_rate);
dsp->display_inband_dtmf_warning = 1;
/* Initialize initial DSP progress detect parameters */
ast_dsp_prog_reset(dsp);
/* Initialize fax detector */
ast_fax_detect_init(dsp);
struct ast_dsp *ast_dsp_new(void)
{
return __ast_dsp_new(DEFAULT_SAMPLE_RATE);
}
struct ast_dsp *ast_dsp_new_with_rate(unsigned int sample_rate)
{
return __ast_dsp_new(sample_rate);
}
void ast_dsp_set_features(struct ast_dsp *dsp, int features)
{
dsp->features = features;
if (!(features & DSP_FEATURE_DIGIT_DETECT)) {
dsp->display_inband_dtmf_warning = 0;
}
int ast_dsp_get_features(struct ast_dsp *dsp)
{
return (dsp->features);
}
Tilghman Lesher
committed
ast_free(dsp);
void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold)
{
dsp->threshold = threshold;
}
void ast_dsp_set_busy_count(struct ast_dsp *dsp, int cadences)
{
Tilghman Lesher
committed
if (cadences < 4) {
Martin Pycko
committed
cadences = 4;
Tilghman Lesher
committed
}
if (cadences > DSP_HISTORY) {
Tilghman Lesher
committed
}
void ast_dsp_set_busy_pattern(struct ast_dsp *dsp, const struct ast_dsp_busy_pattern *cadence)
dsp->busy_cadence = *cadence;
ast_debug(1, "dsp busy pattern set to %d,%d,%d,%d\n", cadence->pattern[0], cadence->pattern[1], (cadence->length == 4) ? cadence->pattern[2] : 0, (cadence->length == 4) ? cadence->pattern[3] : 0);
void ast_dsp_digitreset(struct ast_dsp *dsp)
{
int i;
mf_detect_state_t *s = &dsp->digit_state.td.mf;
/* Reinitialise the detector for the next block */
for (i = 0; i < 6; i++) {
goertzel_reset(&s->tone_out[i]);
Alec L Davis
committed
s->hits[4] = s->hits[3] = s->hits[2] = s->hits[1] = s->hits[0] = 0;
s->current_hit = 0;
dtmf_detect_state_t *s = &dsp->digit_state.td.dtmf;
/* Reinitialise the detector for the next block */
for (i = 0; i < 4; i++) {
goertzel_reset(&s->row_out[i]);
goertzel_reset(&s->col_out[i]);
Alec L Davis
committed
s->lasthit = 0;
s->current_hit = 0;
s->energy = 0.0;
s->current_sample = 0;
dsp->digit_state.digits[0] = '\0';
dsp->digit_state.current_digits = 0;
}
void ast_dsp_reset(struct ast_dsp *dsp)
{
int x;
Tilghman Lesher
committed
for (x = 0; x < 4; x++) {
Tilghman Lesher
committed
}
memset(dsp->historicsilence, 0, sizeof(dsp->historicsilence));
memset(dsp->historicnoise, 0, sizeof(dsp->historicnoise));
dsp->ringtimeout = 0;
Jason Parker
committed
int ast_dsp_set_digitmode(struct ast_dsp *dsp, int digitmode)
old = dsp->digitmode & (DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX);
new = digitmode & (DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX);
if (old != new) {
/* Must initialize structures if switching from MF to DTMF or vice-versa */
ast_digit_detect_init(&dsp->digit_state, new & DSP_DIGITMODE_MF, dsp->sample_rate);
}
dsp->digitmode = digitmode;
return 0;
}
Martin Pycko
committed
int ast_dsp_set_faxmode(struct ast_dsp *dsp, int faxmode)
{
if (dsp->faxmode != faxmode) {
ast_fax_detect_init(dsp);
}
return 0;
}
int ast_dsp_set_call_progress_zone(struct ast_dsp *dsp, char *zone)
{
int x;
for (x = 0; x < ARRAY_LEN(aliases); x++) {
if (!strcasecmp(aliases[x].name, zone)) {
dsp->progmode = aliases[x].mode;
ast_dsp_prog_reset(dsp);
return 0;
}
}
return -1;
}
int ast_dsp_was_muted(struct ast_dsp *dsp)
{
return (dsp->mute_fragments > 0);
}
{
return dsp->tstate;
}
{
return dsp->tcount;
}
static int _dsp_init(int reload)
{
struct ast_config *cfg;
struct ast_variable *v;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
int cfg_threshold;
float cfg_twist;
if ((cfg = ast_config_load2(CONFIG_FILE_NAME, "dsp", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
return 0;
}
thresholds[THRESHOLD_SILENCE] = DEFAULT_SILENCE_THRESHOLD;
dtmf_normal_twist = DEF_DTMF_NORMAL_TWIST;
dtmf_reverse_twist = DEF_DTMF_REVERSE_TWIST;
relax_dtmf_normal_twist = DEF_RELAX_DTMF_NORMAL_TWIST;
relax_dtmf_reverse_twist = DEF_RELAX_DTMF_REVERSE_TWIST;
dtmf_hits_to_begin = DEF_DTMF_HITS_TO_BEGIN;
dtmf_misses_to_end = DEF_DTMF_MISSES_TO_END;
if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
Tilghman Lesher
committed
return 0;
}
for (v = ast_variable_browse(cfg, "default"); v; v = v->next) {
if (!strcasecmp(v->name, "silencethreshold")) {
if (sscanf(v->value, "%30d", &cfg_threshold) < 1) {
ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
} else if (cfg_threshold < 0) {
ast_log(LOG_WARNING, "Invalid silence threshold '%d' specified, using default\n", cfg_threshold);
} else {
thresholds[THRESHOLD_SILENCE] = cfg_threshold;
}
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
} else if (!strcasecmp(v->name, "dtmf_normal_twist")) {
if (sscanf(v->value, "%30f", &cfg_twist) < 1) {
ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
} else if ((cfg_twist < 2.0) || (cfg_twist > 100.0)) { /* < 3.0dB or > 20dB */
ast_log(LOG_WARNING, "Invalid dtmf_normal_twist value '%.2f' specified, using default of %.2f\n", cfg_twist, dtmf_normal_twist);
} else {
dtmf_normal_twist = cfg_twist;
}
} else if (!strcasecmp(v->name, "dtmf_reverse_twist")) {
if (sscanf(v->value, "%30f", &cfg_twist) < 1) {
ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
} else if ((cfg_twist < 2.0) || (cfg_twist > 100.0)) { /* < 3.0dB or > 20dB */
ast_log(LOG_WARNING, "Invalid dtmf_reverse_twist value '%.2f' specified, using default of %.2f\n", cfg_twist, dtmf_reverse_twist);
} else {
dtmf_reverse_twist = cfg_twist;
}
} else if (!strcasecmp(v->name, "relax_dtmf_normal_twist")) {
if (sscanf(v->value, "%30f", &cfg_twist) < 1) {
ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
} else if ((cfg_twist < 2.0) || (cfg_twist > 100.0)) { /* < 3.0dB or > 20dB */
ast_log(LOG_WARNING, "Invalid relax_dtmf_normal_twist value '%.2f' specified, using default of %.2f\n", cfg_twist, relax_dtmf_normal_twist);
} else {
relax_dtmf_normal_twist = cfg_twist;
}
} else if (!strcasecmp(v->name, "relax_dtmf_reverse_twist")) {
if (sscanf(v->value, "%30f", &cfg_twist) < 1) {
ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
} else if ((cfg_twist < 2.0) || (cfg_twist > 100.0)) { /* < 3.0dB or > 20dB */
ast_log(LOG_WARNING, "Invalid relax_dtmf_reverse_twist value '%.2f' specified, using default of %.2f\n", cfg_twist, relax_dtmf_reverse_twist);
} else {
relax_dtmf_reverse_twist = cfg_twist;
}
} else if (!strcasecmp(v->name, "dtmf_hits_to_begin")) {
if (sscanf(v->value, "%30d", &cfg_threshold) < 1) {
ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
} else if (cfg_threshold < 1) { /* must be 1 or greater */
ast_log(LOG_WARNING, "Invalid dtmf_hits_to_begin value '%d' specified, using default of %d\n", cfg_threshold, dtmf_hits_to_begin);
} else {
dtmf_hits_to_begin = cfg_threshold;
}
} else if (!strcasecmp(v->name, "dtmf_misses_to_end")) {
if (sscanf(v->value, "%30d", &cfg_threshold) < 1) {
ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
} else if (cfg_threshold < 1) { /* must be 1 or greater */
ast_log(LOG_WARNING, "Invalid dtmf_misses_to_end value '%d' specified, using default of %d\n", cfg_threshold, dtmf_misses_to_end);
} else {
dtmf_misses_to_end = cfg_threshold;
}
Tilghman Lesher
committed
}
return 0;
}
int ast_dsp_get_threshold_from_settings(enum threshold which)
{
return thresholds[which];
}
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
#ifdef TEST_FRAMEWORK
static void test_tone_sample_gen(short *slin_buf, int samples, int rate, int freq, short amplitude)
{
int idx;
double sample_step = 2.0 * M_PI * freq / rate;/* radians per step */
for (idx = 0; idx < samples; ++idx) {
slin_buf[idx] = amplitude * sin(sample_step * idx);
}
}
#endif
#ifdef TEST_FRAMEWORK
static void test_tone_sample_gen_add(short *slin_buf, int samples, int rate, int freq, short amplitude)
{
int idx;
double sample_step = 2.0 * M_PI * freq / rate;/* radians per step */
for (idx = 0; idx < samples; ++idx) {
slin_buf[idx] += amplitude * sin(sample_step * idx);
}
}
#endif
#ifdef TEST_FRAMEWORK
static void test_dual_sample_gen(short *slin_buf, int samples, int rate, int f1, short a1, int f2, short a2)
{
test_tone_sample_gen(slin_buf, samples, rate, f1, a1);
test_tone_sample_gen_add(slin_buf, samples, rate, f2, a2);
}
#endif
#ifdef TEST_FRAMEWORK
#define TONE_AMPLITUDE_MAX 0x7fff /* Max signed linear amplitude */
#define TONE_AMPLITUDE_MIN 80 /* Min signed linear amplitude detectable */
static int test_tone_amplitude_sweep(struct ast_test *test, struct ast_dsp *dsp, tone_detect_state_t *tone_state)
{
short slin_buf[tone_state->block_size];
int result;
int idx;
struct {
short amp_val;
int detect;
} amp_tests[] = {
{ .amp_val = TONE_AMPLITUDE_MAX, .detect = 1, },