as reported in mantis #6066, fix a bunch of cli bugs and
[asterisk/asterisk.git] / dsp.c
diff --git a/dsp.c b/dsp.c
old mode 100755 (executable)
new mode 100644 (file)
index ad90ff6..a11f272
--- a/dsp.c
+++ b/dsp.c
@@ -1,18 +1,30 @@
 /*
- * Asterisk -- A telephony toolkit for Linux.
+ * Asterisk -- An open source telephony toolkit.
  *
- * Convenience Signal Processing routines
- * 
- * Copyright (C) 2002, Digium
+ * Copyright (C) 1999 - 2005, Digium, Inc.
  *
  * Mark Spencer <markster@digium.com>
  *
- * This program is free software, distributed under the terms of
- * the GNU General Public License.
- *
  * Goertzel routines are borrowed from Steve Underwood's tremendous work on the
  * DTMF detector.
  *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Convenience Signal Processing routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Steve Underwood <steveu@coppice.org>
  */
 
 /* Some routines from tone_detect.c by Steven Underwood as published under the zapata library */
@@ -46,60 +58,86 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/dsp.h"
 #include "asterisk/ulaw.h"
 #include "asterisk/alaw.h"
+#include "asterisk/utils.h"
 
-/* Number of goertzels for progress detect */
-#define GSAMP_SIZE_NA 183                      /* North America - 350, 440, 480, 620, 950, 1400, 1800 Hz */
-#define GSAMP_SIZE_CR 188                      /* Costa Rica, Brazil - Only care about 425 Hz */
-
-#define PROG_MODE_NA           0
-#define PROG_MODE_CR           1       
+/*! Number of goertzels for progress detect */
+enum gsamp_size {
+       GSAMP_SIZE_NA = 183,                    /*!< North America - 350, 440, 480, 620, 950, 1400, 1800 Hz */
+       GSAMP_SIZE_CR = 188,                    /*!< Costa Rica, Brazil - Only care about 425 Hz */
+       GSAMP_SIZE_UK = 160                     /*!< UK disconnect goertzel feed - should trigger 400hz */
+};
 
-/* For US modes */
-#define HZ_350  0
-#define HZ_440  1
-#define HZ_480  2
-#define HZ_620  3
-#define HZ_950  4
-#define HZ_1400 5
-#define HZ_1800 6
+enum prog_mode {
+       PROG_MODE_NA = 0,
+       PROG_MODE_CR,
+       PROG_MODE_UK
+};
 
-/* For CR/BR modes */
-#define HZ_425 0
+enum freq_index { 
+       /*! For US modes { */
+       HZ_350 = 0,
+       HZ_440,
+       HZ_480,
+       HZ_620,
+       HZ_950,
+       HZ_1400,
+       HZ_1800, /*!< } */
+
+       /*! For CR/BR modes */
+       HZ_425 = 0,
+
+       /*! For UK mode */
+       HZ_400 = 0
+};
 
 static struct progalias {
        char *name;
-       int mode;
+       enum prog_mode mode;
 } aliases[] = {
        { "us", PROG_MODE_NA },
        { "ca", PROG_MODE_NA },
        { "cr", PROG_MODE_CR },
        { "br", PROG_MODE_CR },
+       { "uk", PROG_MODE_UK },
 };
 
 static struct progress {
-       int size;
+       enum gsamp_size size;
        int freqs[7];
 } modes[] = {
-       { GSAMP_SIZE_NA, { 350, 440, 480, 620, 950, 1400, 1800 } },     /* North America */
-       { GSAMP_SIZE_CR, { 425 } },
+       { GSAMP_SIZE_NA, { 350, 440, 480, 620, 950, 1400, 1800 } },     /*!< North America */
+       { GSAMP_SIZE_CR, { 425 } },                                     /*!< Costa Rica, Brazil */
+       { GSAMP_SIZE_UK, { 400 } },                                     /*!< UK */
 };
 
 #define DEFAULT_THRESHOLD      512
 
-#define BUSY_PERCENT           10      /* The percentage diffrence between the two last silence periods */
-#define BUSY_THRESHOLD         100     /* Max number of ms difference between max and min times in busy */
-#define BUSY_MIN               75      /* Busy must be at least 80 ms in half-cadence */
-#define BUSY_MAX               1100    /* Busy can't be longer than 1100 ms in half-cadence */
+enum busy_detect {
+       BUSY_PERCENT = 10,      /*!< The percentage difference between the two last silence periods */
+       BUSY_PAT_PERCENT = 7,   /*!< The percentage difference between measured and actual pattern */
+       BUSY_THRESHOLD = 100,   /*!< Max number of ms difference between max and min times in busy */
+       BUSY_MIN = 75,          /*!< Busy must be at least 80 ms in half-cadence */
+       BUSY_MAX =3100          /*!< Busy can't be longer than 3100 ms in half-cadence */
+};
 
-/* Remember last 15 units */
+/*! Remember last 15 units */
 #define DSP_HISTORY            15
 
-/* Define if you want the fax detector -- NOT RECOMMENDED IN -STABLE */
+/*! Define if you want the fax detector -- NOT RECOMMENDED IN -STABLE */
 #define FAX_DETECT
 
-#define TONE_THRESH            10.0    /* How much louder the tone should be than channel energy */
-#define TONE_MIN_THRESH        1e8     /* How much tone there should be at least to attempt */
-#define COUNT_THRESH           3       /* Need at least 50ms of stuff to count it */
+#define TONE_THRESH            10.0    /*!< How much louder the tone should be than channel energy */
+#define TONE_MIN_THRESH        1e8     /*!< How much tone there should be at least to attempt */
+
+/*! All THRESH_XXX values are in GSAMP_SIZE chunks (us = 22ms) */
+enum gsamp_thresh {
+       THRESH_RING = 8,                /*!< Need at least 150ms ring to accept */
+       THRESH_TALK = 2,                /*!< Talk detection does not work continuously */
+       THRESH_BUSY = 4,                /*!< Need at least 80ms to accept */
+       THRESH_CONGESTION = 4,          /*!< Need at least 80ms to accept */
+       THRESH_HANGUP = 60,             /*!< Need at least 1300ms to accept hangup */
+       THRESH_RING2ANSWER = 300        /*!< Timeout from start of ring to answer (about 6600 ms) */
+};
 
 #define        MAX_DTMF_DIGITS         128
 
@@ -142,6 +180,10 @@ static struct progress {
 #define BELL_MF_RELATIVE_PEAK  12.6    /* 11dB */
 #endif
 
+#if !defined(BUSYDETECT_MARTIN) && !defined(BUSYDETECT) && !defined(BUSYDETECT_TONEONLY) && !defined(BUSYDETECT_COMPARE_TONE_AND_SILENCE)
+#define BUSYDETECT_MARTIN
+#endif
+
 typedef struct {
        float v2;
        float v3;
@@ -289,15 +331,18 @@ struct ast_dsp {
        int totalsilence;
        int totalnoise;
        int features;
+       int ringtimeout;
        int busymaybe;
        int busycount;
+       int busy_tonelength;
+       int busy_quietlength;
        int historicnoise[DSP_HISTORY];
        int historicsilence[DSP_HISTORY];
        goertzel_state_t freqs[7];
        int freqcount;
        int gsamps;
-       int gsamp_size;
-       int progmode;
+       enum gsamp_size gsamp_size;
+       enum prog_mode progmode;
        int tstate;
        int tcount;
        int digitmode;
@@ -1060,32 +1105,66 @@ static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len)
                                } else
                                        newstate = DSP_TONE_STATE_SILENCE;
                                break;
+                       case PROG_MODE_UK:
+                               if (hz[HZ_400] > TONE_MIN_THRESH * TONE_THRESH) {
+                                       newstate = DSP_TONE_STATE_HUNGUP;
+                               }
+                               break;
                        default:
                                ast_log(LOG_WARNING, "Can't process in unknown prog mode '%d'\n", dsp->progmode);
                        }
                        if (newstate == dsp->tstate) {
                                dsp->tcount++;
-                               if (dsp->tcount == COUNT_THRESH) {
-                                       if ((dsp->features & DSP_PROGRESS_BUSY) && 
-                                           dsp->tstate == DSP_TONE_STATE_BUSY) {
-                                               res = AST_CONTROL_BUSY;
-                                               dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
-                                       } else if ((dsp->features & DSP_PROGRESS_TALK) && 
-                                                  dsp->tstate == DSP_TONE_STATE_TALKING) {
-                                               res = AST_CONTROL_ANSWER;
-                                               dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
-                                       } else if ((dsp->features & DSP_PROGRESS_RINGING) && 
-                                                  dsp->tstate == DSP_TONE_STATE_RINGING)
-                                               res = AST_CONTROL_RINGING;
-                                       else if ((dsp->features & DSP_PROGRESS_CONGESTION) && 
-                                                dsp->tstate == DSP_TONE_STATE_SPECIAL3) {
-                                               res = AST_CONTROL_CONGESTION;
-                                               dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
-                                       }
+                               if (dsp->ringtimeout)
+                                       dsp->ringtimeout++;
+                               switch (dsp->tstate) {
+                                       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) {
+#if 0
+                                       ast_log(LOG_NOTICE, "Consider call as answered because of timeout after last ring\n");
+#endif
+                                       res = AST_CONTROL_ANSWER;
+                                       dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
                                }
                        } else {
 #if 0
-                               printf("Newstate: %d\n", newstate);
+                               ast_log(LOG_NOTICE, "Stop state %d with duration %d\n", dsp->tstate, dsp->tcount);
+                               ast_log(LOG_NOTICE, "Start state %d\n", newstate);
 #endif
                                dsp->tstate = newstate;
                                dsp->tcount = 1;
@@ -1131,6 +1210,7 @@ static int __ast_dsp_silence(struct ast_dsp *dsp, short *s, int len, int *totals
                accum += abs(s[x]);
        accum /= len;
        if (accum < dsp->threshold) {
+               /* Silent */
                dsp->totalsilence += len/8;
                if (dsp->totalnoise) {
                        /* Move and save history */
@@ -1144,6 +1224,7 @@ static int __ast_dsp_silence(struct ast_dsp *dsp, short *s, int len, int *totals
                dsp->totalnoise = 0;
                res = 1;
        } else {
+               /* Not silent */
                dsp->totalnoise += len/8;
                if (dsp->totalsilence) {
                        int silence1 = dsp->historicsilence[DSP_HISTORY - 1];
@@ -1153,12 +1234,12 @@ static int __ast_dsp_silence(struct ast_dsp *dsp, short *s, int len, int *totals
                        dsp->historicsilence[DSP_HISTORY - 1] = dsp->totalsilence;
                        /* check if the previous sample differs only by BUSY_PERCENT from the one before it */
                        if (silence1 < silence2) {
-                               if (silence1 + silence1/BUSY_PERCENT >= silence2)
+                               if (silence1 + silence1*BUSY_PERCENT/100 >= silence2)
                                        dsp->busymaybe = 1;
                                else 
                                        dsp->busymaybe = 0;
                        } else {
-                               if (silence1 - silence1/BUSY_PERCENT <= silence2)
+                               if (silence1 - silence1*BUSY_PERCENT/100 <= silence2)
                                        dsp->busymaybe = 1;
                                else 
                                        dsp->busymaybe = 0;
@@ -1170,6 +1251,7 @@ static int __ast_dsp_silence(struct ast_dsp *dsp, short *s, int len, int *totals
                *totalsilence = dsp->totalsilence;
        return res;
 }
+
 #ifdef BUSYDETECT_MARTIN
 int ast_dsp_busydetect(struct ast_dsp *dsp)
 {
@@ -1193,18 +1275,18 @@ int ast_dsp_busydetect(struct ast_dsp *dsp)
        for (x=DSP_HISTORY - dsp->busycount;x<DSP_HISTORY;x++) {
 #ifndef BUSYDETECT_TONEONLY
                if (avgsilence > dsp->historicsilence[x]) {
-                       if (avgsilence - (avgsilence / BUSY_PERCENT) <= dsp->historicsilence[x])
+                       if (avgsilence - (avgsilence*BUSY_PERCENT/100) <= dsp->historicsilence[x])
                                hitsilence++;
                } else {
-                       if (avgsilence + (avgsilence / BUSY_PERCENT) >= dsp->historicsilence[x])
+                       if (avgsilence + (avgsilence*BUSY_PERCENT/100) >= dsp->historicsilence[x])
                                hitsilence++;
                }
 #endif
                if (avgtone > dsp->historicnoise[x]) {
-                       if (avgtone - (avgtone / BUSY_PERCENT) <= dsp->historicnoise[x])
+                       if (avgtone - (avgtone*BUSY_PERCENT/100) <= dsp->historicnoise[x])
                                hittone++;
                } else {
-                       if (avgtone + (avgtone / BUSY_PERCENT) >= dsp->historicnoise[x])
+                       if (avgtone + (avgtone*BUSY_PERCENT/100) >= dsp->historicnoise[x])
                                hittone++;
                }
        }
@@ -1220,19 +1302,39 @@ int ast_dsp_busydetect(struct ast_dsp *dsp)
 #error You cant use BUSYDETECT_TONEONLY together with BUSYDETECT_COMPARE_TONE_AND_SILENCE
 #endif
                if (avgtone > avgsilence) {
-                       if (avgtone - avgtone/(BUSY_PERCENT*2) <= avgsilence)
+                       if (avgtone - avgtone*BUSY_PERCENT/100 <= avgsilence)
                                res = 1;
                } else {
-                       if (avgtone + avgtone/(BUSY_PERCENT*2) >= avgsilence)
+                       if (avgtone + avgtone*BUSY_PERCENT/100 >= avgsilence)
                                res = 1;
                }
 #else
                res = 1;
 #endif
        }
+       /* If we know the expected busy tone length, check we are in the range */
+       if (res && (dsp->busy_tonelength > 0)) {
+               if (abs(avgtone - dsp->busy_tonelength) > (dsp->busy_tonelength*BUSY_PAT_PERCENT/100)) {
+#if 0
+                       ast_log(LOG_NOTICE, "busy detector: avgtone of %d not close enough to desired %d\n",
+                                               avgtone, dsp->busy_tonelength);
+#endif
+                       res = 0;
+               }
+       }
+       /* If we know the expected busy tone silent-period length, check we are in the range */
+       if (res && (dsp->busy_quietlength > 0)) {
+               if (abs(avgsilence - dsp->busy_quietlength) > (dsp->busy_quietlength*BUSY_PAT_PERCENT/100)) {
 #if 0
+                       ast_log(LOG_NOTICE, "busy detector: avgsilence of %d not close enough to desired %d\n",
+                                               avgsilence, dsp->busy_quietlength);
+#endif
+                       res = 0;
+               }
+       }
+#if 1
        if (res)
-               ast_log(LOG_NOTICE, "detected busy, avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
+               ast_log(LOG_DEBUG, "ast_dsp_busydetect detected busy, avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
 #endif
        return res;
 }
@@ -1342,8 +1444,7 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
                len = af->datalen / 2;
                break;
        case AST_FORMAT_ULAW:
-               shortdata = alloca(af->datalen * 2);
-               if (!shortdata) {
+               if (!(shortdata = alloca(af->datalen * 2))) {
                        ast_log(LOG_WARNING, "Unable to allocate stack space for data: %s\n", strerror(errno));
                        return af;
                }
@@ -1351,8 +1452,7 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
                        shortdata[x] = AST_MULAW(odata[x]);
                break;
        case AST_FORMAT_ALAW:
-               shortdata = alloca(af->datalen * 2);
-               if (!shortdata) {
+               if (!(shortdata = alloca(af->datalen * 2))) {
                        ast_log(LOG_WARNING, "Unable to allocate stack space for data: %s\n", strerror(errno));
                        return af;
                }
@@ -1478,6 +1578,7 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
                        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 = res;
@@ -1508,15 +1609,14 @@ static void ast_dsp_prog_reset(struct ast_dsp *dsp)
                }
        }
        dsp->freqcount = max;
+       dsp->ringtimeout= 0;
 }
 
 struct ast_dsp *ast_dsp_new(void)
 {
        struct ast_dsp *dsp;
-
-       dsp = malloc(sizeof(struct ast_dsp));
-       if (dsp) {
-               memset(dsp, 0, sizeof(struct ast_dsp));
+       
+       if ((dsp = ast_calloc(1, sizeof(*dsp)))) {              
                dsp->threshold = DEFAULT_THRESHOLD;
                dsp->features = DSP_FEATURE_SILENCE_SUPPRESS;
                dsp->busycount = DSP_HISTORY;
@@ -1552,6 +1652,13 @@ void ast_dsp_set_busy_count(struct ast_dsp *dsp, int cadences)
        dsp->busycount = cadences;
 }
 
+void ast_dsp_set_busy_pattern(struct ast_dsp *dsp, int tonelength, int quietlength)
+{
+       dsp->busy_tonelength = tonelength;
+       dsp->busy_quietlength = quietlength;
+       ast_log(LOG_DEBUG, "dsp busy pattern set to %d,%d\n", tonelength, quietlength);
+}
+
 void ast_dsp_digitreset(struct ast_dsp *dsp)
 {
        int i;
@@ -1612,6 +1719,7 @@ void ast_dsp_reset(struct ast_dsp *dsp)
                dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0;
        memset(dsp->historicsilence, 0, sizeof(dsp->historicsilence));
        memset(dsp->historicnoise, 0, sizeof(dsp->historicnoise));      
+       dsp->ringtimeout= 0;
 }
 
 int ast_dsp_digitmode(struct ast_dsp *dsp, int digitmode)