Large cleanup of DSP code
authorJason Parker <jparker@digium.com>
Wed, 26 Mar 2008 19:05:51 +0000 (19:05 +0000)
committerJason Parker <jparker@digium.com>
Wed, 26 Mar 2008 19:05:51 +0000 (19:05 +0000)
Per comments from dimas:
1. The code now generates DTMF_BEGIN frames in addition to DTMF_END ones.

2. "quelching" rewritten - now each detector (MF/DTMF/generic tone) may mark fragment of a frame for suppression (squelching, muting) with a call to mute_fragment. Actual muting happens only once at the very end of ast_dsp_process where all marked fragments are zeroed. This way every detector sees original data in the frame without any piece of a frame being zeroed by a detector which was run before.

3. DTMF detector tries to "mute" one block before and one block after the block where actual tone was detected. Muting of previois block is something new for this patch. Obviously this operation is not always possible - if current frame does not contain data for previous block - it is too late. But at least we make our best.
Muting of next block was already done by the old code but it only affects part of the next block which is in the frame being processed. New code keeps this information in state structures so it will mute proper number of samples in the next frame(s) too.

4. Removed ast_dsp_digitdetect and ast_dsp_getdigits APIs because these are not used.

5. DSP API extended a bit - ast_dsp_was_muted() function added which returns true if DSP code was muting any fragment in the last frame. chan_zap uses this function to decide it needs to turn on confmute on the channel.
This is to replace AST_FRAME_DTMF 'm'/'u' (mute/unmute) functionality.

(closes issue #11968)
Reported by: dimas
Patches:
      v2-11968-dsp.patch uploaded by dimas (license 88)
      v4-11968-zap.patch uploaded by dimas (license 88)
Tested by: dimas, qwell

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@111022 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_usbradio.c
channels/chan_vpb.cc
channels/chan_zap.c
include/asterisk/dsp.h
main/dsp.c

index 11c06e9..84a2ce2 100644 (file)
@@ -1210,8 +1210,6 @@ static struct ast_frame *usbradio_read(struct ast_channel *c)
        if (o->dsp) {
                f1 = ast_dsp_process(c, o->dsp, f);
                if ((f1->frametype == AST_FRAME_DTMF_END) || (f1->frametype == AST_FRAME_DTMF_BEGIN)) {
-                       if ((f1->subclass == 'm') || (f1->subclass == 'u'))
-                           f1->frametype = AST_FRAME_DTMF_BEGIN;
                        if (f1->frametype == AST_FRAME_DTMF_END)
                            ast_log(LOG_NOTICE,"Got DTMF char %c\n",f1->subclass);
                        return f1;
index be1ed53..bd67ab0 100644 (file)
@@ -2400,14 +2400,6 @@ static void *do_chanreads(void *pvt)
                                fr = ast_dsp_process(p->owner,p->vad,fr);
                                if (fr && (fr->frametype == AST_FRAME_DTMF))
                                        ast_debug(1, "%s: chanreads: Detected DTMF '%c'\n", p->dev, fr->subclass);
-                               if (fr->subclass == 'm') {
-                                       /* conf mute request */
-                                       fr->frametype = AST_FRAME_NULL;
-                                       fr->subclass = 0;
-                               } else if (fr->subclass == 'u') {
-                                       /* Unmute */
-                                       fr->frametype = AST_FRAME_NULL;
-                                       fr->subclass = 0;
                                } else if (fr->subclass == 'f') {
                                }
                        }
index effd5cc..beb3c33 100644 (file)
@@ -706,6 +706,7 @@ static struct zt_pvt {
        unsigned int loopedback:1;
 #endif
        char begindigit;
+       int muting;
 } *iflist = NULL, *ifend = NULL;
 
 /*! \brief Channel configuration from zapata.conf .
@@ -2988,6 +2989,7 @@ static int zt_hangup(struct ast_channel *ast)
 
        x = 0;
        zt_confmute(p, 0);
+       p->muting = 0;
        restore_gains(p);
        if (p->origcid_num) {
                ast_copy_string(p->cid_num, p->origcid_num, sizeof(p->cid_num));
@@ -4232,8 +4234,7 @@ static void zt_handle_dtmfup(struct ast_channel *ast, int index, struct ast_fram
                                ast_free(p->cidspill);
                        send_cwcidspill(p);
                }
-               if ((f->subclass != 'm') && (f->subclass != 'u')) 
-                       p->callwaitcas = 0;
+               p->callwaitcas = 0;
                p->subs[index].f.frametype = AST_FRAME_NULL;
                p->subs[index].f.subclass = 0;
                *dest = &p->subs[index].f;
@@ -4260,20 +4261,7 @@ static void zt_handle_dtmfup(struct ast_channel *ast, int index, struct ast_fram
                p->subs[index].f.frametype = AST_FRAME_NULL;
                p->subs[index].f.subclass = 0;
                *dest = &p->subs[index].f;
-       } else if (f->subclass == 'm') {
-               /* Confmute request */
-               zt_confmute(p, 1);
-               p->subs[index].f.frametype = AST_FRAME_NULL;
-               p->subs[index].f.subclass = 0;
-               *dest = &p->subs[index].f;              
-       } else if (f->subclass == 'u') {
-               /* Unmute */
-               zt_confmute(p, 0);
-               p->subs[index].f.frametype = AST_FRAME_NULL;
-               p->subs[index].f.subclass = 0;
-               *dest = &p->subs[index].f;              
-       } else
-               zt_confmute(p, 0);
+       }
 }
                        
 static struct ast_frame *zt_handle_event(struct ast_channel *ast)
@@ -5463,7 +5451,17 @@ static struct ast_frame  *zt_read(struct ast_channel *ast)
        }
        if (p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect  || p->callprogress) && !index) {
                /* Perform busy detection. etc on the zap line */
+               int mute;
+
                f = ast_dsp_process(ast, p->dsp, &p->subs[index].f);
+
+               /* Check if DSP code thinks we should be muting this frame and mute the conference if so */
+               mute = ast_dsp_was_muted(p->dsp);
+               if (p->muting != mute) {
+                       p->muting = mute;
+                       zt_confmute(p, mute);
+               }
+
                if (f) {
                        if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_BUSY)) {
                                if ((ast->_state == AST_STATE_UP) && !p->outgoing) {
@@ -5996,6 +5994,7 @@ static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int
        i->fake_event = 0;
        /* Assure there is no confmute on this channel */
        zt_confmute(i, 0);
+       i->muting = 0;
        /* Configure the new channel jb */
        ast_jb_configure(tmp, &global_jbconf);
 
index b8e21b4..81735d6 100644 (file)
@@ -120,6 +120,10 @@ int ast_dsp_set_digitmode(struct ast_dsp *dsp, int digitmode);
 /*! \brief Set fax mode */
 int ast_dsp_set_faxmode(struct ast_dsp *dsp, int faxmode);
 
+/*! \brief Returns true if DSP code was muting any fragment of the last processed frame.
+  Muting (squelching) happens when DSP code removes DTMF/MF/generic tones from the audio */
+int ast_dsp_was_muted(struct ast_dsp *dsp);
+
 /*! \brief Get tstate (Tone State) */
 int ast_dsp_get_tstate(struct ast_dsp *dsp);
 
index 3d639a3..084a37c 100644 (file)
@@ -150,13 +150,13 @@ enum gsamp_thresh {
 #define FAX_2ND_HARMONIC       2.0     /* 4dB */
 #define DTMF_NORMAL_TWIST      6.3     /* 8dB */
 #ifdef RADIO_RELAX
-#define DTMF_REVERSE_TWIST          ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 6.5 : 2.5)     /* 4dB normal */
+#define DTMF_REVERSE_TWIST          (relax ? 6.5 : 2.5)     /* 4dB normal */
 #else
-#define DTMF_REVERSE_TWIST          ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 4.0 : 2.5)     /* 4dB normal */
+#define DTMF_REVERSE_TWIST          (relax ? 4.0 : 2.5)     /* 4dB normal */
 #endif
 #define DTMF_RELATIVE_PEAK_ROW 6.3     /* 8dB */
 #define DTMF_RELATIVE_PEAK_COL 6.3     /* 8dB */
-#define DTMF_2ND_HARMONIC_ROW       ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 1.7 : 2.5)     /* 4dB normal */
+#define DTMF_2ND_HARMONIC_ROW       (relax ? 1.7 : 2.5)     /* 4dB normal */
 #define DTMF_2ND_HARMONIC_COL  63.1    /* 18dB */
 #define DTMF_TO_TOTAL_ENERGY   42.0
 
@@ -193,6 +193,16 @@ enum gsamp_thresh {
  */
 #define SAMPLES_IN_FRAME       160
 
+/* MF goertzel size */
+#define MF_GSIZE               120
+
+/* DTMF goertzel size */
+#define DTMF_GSIZE             102
+
+/* How many successive hits needed to consider begin of a digit */
+#define DTMF_HITS_TO_BEGIN     2
+/* How many successive misses needed to consider end of a digit */
+#define DTMF_MISSES_TO_END     3
 
 #define CONFIG_FILE_NAME "dsp.conf"
 
@@ -217,6 +227,7 @@ typedef struct
        goertzel_state_t tone;
        float energy;           /* Accumulated energy of the current block */
        int samples_pending;    /* Samples remain to complete the current block */
+       int mute_samples;       /* How many additional samples needs to be muted to suppress already detected tone */
 
        int hits_required;      /* How many successive blocks with tone we are looking for */
        float threshold;        /* Energy of the tone relative to energy from all other signals to consider a hit */
@@ -230,10 +241,15 @@ typedef struct
 {
        goertzel_state_t row_out[4];
        goertzel_state_t col_out[4];
+       int hits_to_begin;              /* How many successive hits needed to consider begin of a digit */
+       int misses_to_end;              /* How many successive misses needed to consider end of a digit */
+       int hits;                       /* How many successive hits we have seen already */
+       int misses;                     /* How many successive misses we have seen already */
        int lasthit;
        int current_hit;
        float energy;
        int current_sample;
+       int mute_samples;
 } dtmf_detect_state_t;
 
 typedef struct
@@ -242,6 +258,7 @@ typedef struct
        int current_hit;
        int hits[5];
        int current_sample;
+       int mute_samples;
 } mf_detect_state_t;
 
 typedef struct
@@ -324,6 +341,24 @@ static inline void goertzel_reset(goertzel_state_t *s)
        s->v2 = s->v3 = s->chunky = 0.0;
 }
 
+typedef struct {
+       int start;
+       int end;
+} fragment_t;
+
+/* Note on tone suppression (squelching). Individual detectors (DTMF/MF/generic tone)
+ * report fragmens of the frame in which detected tone resides and which needs
+ * to be "muted" in order to suppress the tone. To mark fragment for muting,
+ * detectors call mute_fragment passing fragment_t there. Multiple fragments
+ * can be marked and ast_dsp_process later will mute all of them.
+ *
+ * Note: When tone starts in the middle of a Goertzel block, it won't be properly
+ * detected in that block, only in the next. If we only mute the next block
+ * where tone is actually detected, the user will still hear beginning
+ * of the tone in preceeding block. This is why we usually want to mute some amount
+ * of samples preceeding and following the block where tone was detected.
+*/
+
 struct ast_dsp {
        struct ast_frame f;
        int threshold;
@@ -346,13 +381,25 @@ struct ast_dsp {
        int tcount;
        int digitmode;
        int faxmode;
-       int thinkdigit;
+       int dtmf_began;
        float genergy;
+       int mute_fragments;
+       fragment_t mute_data[5];
        digit_detect_state_t digit_state;
        tone_detect_state_t cng_tone_state;
        tone_detect_state_t ced_tone_state;
 };
 
+static void mute_fragment(struct ast_dsp *dsp, fragment_t *fragment)
+{
+       if (dsp->mute_fragments >= sizeof(dsp->mute_data) / sizeof(dsp->mute_data[0])) {
+               ast_log(LOG_ERROR, "Too many fragments to mute. Ignoring\n");
+               return;
+       }
+
+       dsp->mute_data[dsp->mute_fragments++] = *fragment;
+}
+
 static void ast_tone_detect_init(tone_detect_state_t *s, int freq, int duration, int amp)
 {
        int duration_samples;
@@ -426,11 +473,16 @@ static void ast_dtmf_detect_init (dtmf_detect_state_t *s)
        s->lasthit = 0;
        s->current_hit = 0;
        for (i = 0;  i < 4;  i++) {
-               goertzel_init (&s->row_out[i], dtmf_row[i], 102);
-               goertzel_init (&s->col_out[i], dtmf_col[i], 102);
+               goertzel_init (&s->row_out[i], dtmf_row[i], DTMF_GSIZE);
+               goertzel_init (&s->col_out[i], dtmf_col[i], DTMF_GSIZE);
                s->energy = 0.0;
        }
        s->current_sample = 0;
+       s->hits = 0;
+       s->misses = 0;
+
+       s->hits_to_begin = DTMF_HITS_TO_BEGIN;
+       s->misses_to_end = DTMF_MISSES_TO_END;
 }
 
 static void ast_mf_detect_init (mf_detect_state_t *s)
@@ -457,8 +509,7 @@ static void ast_digit_detect_init(digit_detect_state_t *s, int mf)
                ast_dtmf_detect_init(&s->td.dtmf);
 }
 
-static int tone_detect(tone_detect_state_t *s, int16_t *amp, int samples, 
-                int *writeback)
+static int tone_detect(struct ast_dsp *dsp, tone_detect_state_t *s, int16_t *amp, int samples)
 {
        float tone_energy;
        int i;
@@ -466,10 +517,20 @@ static int tone_detect(tone_detect_state_t *s, int16_t *amp, int samples,
        int limit;
        int res = 0;
        int16_t *ptr;
+       int start, end;
+       fragment_t mute = {0, 0};
 
-       while (1) {
+       if (s->squelch && s->mute_samples > 0) {
+               mute.end = (s->mute_samples < samples) ? s->mute_samples : samples;
+               s->mute_samples -= mute.end;
+       }
+
+       for (start = 0;  start < samples;  start = end) {
                /* Process in blocks. */
-               limit = (samples < s->samples_pending) ? samples : s->samples_pending;
+               limit = samples - start;
+               if (limit > s->samples_pending)
+                       limit = s->samples_pending;
+               end = start + limit;
 
                for (i = limit, ptr = amp ; i > 0; i--, ptr++) {
                        /* signed 32 bit int should be enough to suqare any possible signed 16 bit value */
@@ -482,36 +543,21 @@ static int tone_detect(tone_detect_state_t *s, int16_t *amp, int samples,
 
                if (s->samples_pending) {
                        /* Finished incomplete (last) block */
-                       if (s->last_hit && s->squelch) {
-                               /* If we had a hit last time, go ahead and clear this out since likely it
-                                  will be another hit */
-                               memset(amp, 0, sizeof(*amp) * limit);
-                               if (writeback)
-                                       *writeback = 1;
-                       }
                        break;
                }
 
-
                tone_energy = goertzel_result(&s->tone);
 
                /* Scale to make comparable */
                tone_energy *= 2.0;
                s->energy *= s->block_size;
 
-               ast_debug(10, "tone %d, Ew=%f, Et=%f, s/n=%10.2f\n", s->freq, tone_energy, s->energy, tone_energy / (s->energy - tone_energy));
+               ast_debug(10, "tone %d, Ew=%.2E, Et=%.2E, s/n=%10.2f\n", s->freq, tone_energy, s->energy, tone_energy / (s->energy - tone_energy));
                hit = 0;
                if (tone_energy > s->energy * s->threshold) {
 
                        ast_debug(10, "Hit! count=%d\n", s->hit_count);
                        hit = 1;
-
-                       if (s->squelch) {
-                               /* Zero out frame data */
-                               memset(amp, 0, sizeof(*amp) * limit);
-                               if (writeback)
-                                       *writeback = 1;
-                       }
                }
 
                if (s->hit_count)
@@ -534,6 +580,17 @@ static int tone_detect(tone_detect_state_t *s, int16_t *amp, int samples,
 
                s->last_hit = hit;
 
+               /* If we had a hit in this block, include it into mute fragment */
+               if (s->squelch && hit) {
+                       if (mute.end < start - s->block_size) {
+                               /* There is a gap between fragments */
+                               mute_fragment(dsp, &mute);
+                               mute.start = (start > s->block_size) ? (start - s->block_size) : 0;
+                       }
+                       mute.end = end + s->block_size;
+               }
+
+               /* Reinitialise the detector for the next block */
                /* Reset for the next block */
                goertzel_reset(&s->tone);
 
@@ -542,7 +599,14 @@ static int tone_detect(tone_detect_state_t *s, int16_t *amp, int samples,
                s->samples_pending = s->block_size;
 
                amp += limit;
-               samples -= limit;
+       }
+
+       if (s->squelch && mute.end) {
+               if (mute.end > samples) {
+                       s->mute_samples = mute.end - samples;
+                       mute.end = samples;
+               }
+               mute_fragment(dsp, &mute);
        }
 
        return res;
@@ -560,8 +624,7 @@ static void store_digit(digit_detect_state_t *s, char digit)
        }
 }
 
-static int dtmf_detect(digit_detect_state_t *s, int16_t amp[], int samples, 
-                int digitmode, int *writeback)
+static int dtmf_detect(struct ast_dsp *dsp, digit_detect_state_t *s, int16_t amp[], int samples, int squelch, int relax)
 {
        float row_energy[4];
        float col_energy[4];
@@ -573,12 +636,18 @@ static int dtmf_detect(digit_detect_state_t *s, int16_t amp[], int samples,
        int best_col;
        int hit;
        int limit;
+       fragment_t mute = {0, 0};
+
+       if (squelch && s->td.dtmf.mute_samples > 0) {
+               mute.end = (s->td.dtmf.mute_samples < samples) ? s->td.dtmf.mute_samples : samples;
+               s->td.dtmf.mute_samples -= mute.end;
+       }
 
        hit = 0;
        for (sample = 0;  sample < samples;  sample = limit) {
-               /* 102 is optimised to meet the DTMF specs. */
-               if ((samples - sample) >= (102 - s->td.dtmf.current_sample))
-                       limit = sample + (102 - s->td.dtmf.current_sample);
+               /* DTMF_GSIZE is optimised to meet the DTMF specs. */
+               if ((samples - sample) >= (DTMF_GSIZE - s->td.dtmf.current_sample))
+                       limit = sample + (DTMF_GSIZE - s->td.dtmf.current_sample);
                else
                        limit = samples;
                /* The following unrolled loop takes only 35% (rough estimate) of the 
@@ -598,14 +667,7 @@ static int dtmf_detect(digit_detect_state_t *s, int16_t amp[], int samples,
                        goertzel_sample(s->td.dtmf.col_out + 3, amp[j]);
                }
                s->td.dtmf.current_sample += (limit - sample);
-               if (s->td.dtmf.current_sample < 102) {
-                       if (hit && !((digitmode & DSP_DIGITMODE_NOQUELCH))) {
-                               /* If we had a hit last time, go ahead and clear this out since likely it
-                                  will be another hit */
-                               for (i=sample;i<limit;i++) 
-                                       amp[i] = 0;
-                               *writeback = 1;
-                       }
+               if (s->td.dtmf.current_sample < DTMF_GSIZE) {
                        continue;
                }
                /* We are at the end of a DTMF detection block */
@@ -641,31 +703,53 @@ static int dtmf_detect(digit_detect_state_t *s, int16_t amp[], int samples,
                            (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY*s->td.dtmf.energy) {
                                /* Got a hit */
                                hit = dtmf_positions[(best_row << 2) + best_col];
-                               if (!(digitmode & DSP_DIGITMODE_NOQUELCH)) {
-                                       /* Zero out frame data if this is part DTMF */
-                                       for (i=sample;i<limit;i++) 
-                                               amp[i] = 0;
-                                       *writeback = 1;
-                               }
                        }
                } 
 
-               /* The logic in the next test is:
-                  For digits we need two successive identical clean detects, with
-                  something different preceeding it. This can work with
-                  back to back differing digits. More importantly, it
-                  can work with nasty phones that give a very wobbly start
-                  to a digit */
-               if (hit != s->td.dtmf.current_hit) {
-                       if (hit && s->td.dtmf.lasthit == hit) {
-                               s->td.dtmf.current_hit = hit;
+               if (s->td.dtmf.current_hit) {
+                       /* We are in the middle of a digit already */
+                       if (hit != s->td.dtmf.current_hit) {
+                               s->td.dtmf.misses++;
+                               if (s->td.dtmf.misses == s->td.dtmf.misses_to_end) {
+                                       /* There were enough misses to consider digit ended */
+                                       s->td.dtmf.current_hit = 0;
+                               }
+                       } else {
+                               s->td.dtmf.misses = 0;
+                       }
+               }
+
+               /* Look for a start of a new digit no matter if we are already in the middle of some
+                  digit or not. This is because hits_to_begin may be smaller than misses_to_end
+                  and we may find begin of new digit before we consider last one ended. */
+               if (hit) {
+                       if (hit == s->td.dtmf.lasthit) {
+                               s->td.dtmf.hits++;
+                       } else {
+                               s->td.dtmf.hits = 1;
+                       }
+
+                       if (s->td.dtmf.hits == s->td.dtmf.hits_to_begin && hit != s->td.dtmf.current_hit) {
                                store_digit(s, hit);
-                       } else if (s->td.dtmf.lasthit != s->td.dtmf.current_hit) {
-                               s->td.dtmf.current_hit = 0;
+                               s->td.dtmf.current_hit = hit;
+                               s->td.dtmf.misses = 0;
                        }
+               } else {
+                       s->td.dtmf.hits = 0;
                }
+
                s->td.dtmf.lasthit = hit;
 
+               /* If we had a hit in this block, include it into mute fragment */
+               if (squelch && hit) {
+                       if (mute.end < sample - DTMF_GSIZE) {
+                               /* There is a gap between fragments */
+                               mute_fragment(dsp, &mute);
+                               mute.start = (sample > DTMF_GSIZE) ? (sample - DTMF_GSIZE) : 0;
+                       }
+                       mute.end = limit + DTMF_GSIZE;
+               }
+
                /* Reinitialise the detector for the next block */
                for (i = 0;  i < 4;  i++) {
                        goertzel_reset(&s->td.dtmf.row_out[i]);
@@ -674,14 +758,20 @@ static int dtmf_detect(digit_detect_state_t *s, int16_t amp[], int samples,
                s->td.dtmf.energy = 0.0;
                s->td.dtmf.current_sample = 0;
        }
+
+       if (squelch && mute.end) {
+               if (mute.end > samples) {
+                       s->td.dtmf.mute_samples = mute.end - samples;
+                       mute.end = samples;
+               }
+               mute_fragment(dsp, &mute);
+       }
+
        return (s->td.dtmf.current_hit);        /* return the debounced hit */
 }
 
-/* MF goertzel size */
-#define MF_GSIZE 120
-
-static int mf_detect(digit_detect_state_t *s, int16_t amp[],
-       int samples, int digitmode, int *writeback)
+static int mf_detect(struct ast_dsp *dsp, digit_detect_state_t *s, int16_t amp[],
+                 int samples, int squelch, int relax)
 {
        float energy[6];
        int best;
@@ -692,10 +782,17 @@ static int mf_detect(digit_detect_state_t *s, int16_t amp[],
        int sample;
        int hit;
        int limit;
+       fragment_t mute = {0, 0};
+
+       if (squelch && s->td.mf.mute_samples > 0) {
+               mute.end = (s->td.mf.mute_samples < samples) ? s->td.mf.mute_samples : samples;
+               s->td.mf.mute_samples -= mute.end;
+       }
 
        hit = 0;
        for (sample = 0;  sample < samples;  sample = limit) {
                /* 80 is optimised to meet the MF specs. */
+               /* XXX So then why is MF_GSIZE defined as 120? */
                if ((samples - sample) >= (MF_GSIZE - s->td.mf.current_sample))
                        limit = sample + (MF_GSIZE - s->td.mf.current_sample);
                else
@@ -715,13 +812,6 @@ static int mf_detect(digit_detect_state_t *s, int16_t amp[],
                }
                s->td.mf.current_sample += (limit - sample);
                if (s->td.mf.current_sample < MF_GSIZE) {
-                       if (hit && !((digitmode & DSP_DIGITMODE_NOQUELCH))) {
-                               /* If we had a hit last time, go ahead and clear this out since likely it
-                                  will be another hit */
-                               for (i=sample;i<limit;i++) 
-                                       amp[i] = 0;
-                               *writeback = 1;
-                       }
                        continue;
                }
                /* We're at the end of an MF detection block.  */
@@ -801,58 +891,32 @@ static int mf_detect(digit_detect_state_t *s, int16_t amp[],
                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 + DTMF_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;
        }
 
-       return (s->td.mf.current_hit); /* return the debounced hit */
-}
-
-static int __ast_dsp_digitdetect(struct ast_dsp *dsp, short *s, int len, int *writeback)
-{
-       int res = 0;
-       
-       if ((dsp->features & DSP_FEATURE_DIGIT_DETECT) && (dsp->digitmode & DSP_DIGITMODE_MF))
-               res = mf_detect(&dsp->digit_state, s, len, dsp->digitmode & DSP_DIGITMODE_RELAXDTMF, writeback);
-       else if (dsp->features & DSP_FEATURE_DIGIT_DETECT)
-               res = dtmf_detect(&dsp->digit_state, s, len, dsp->digitmode & DSP_DIGITMODE_RELAXDTMF, writeback);
-
-       if ((dsp->features & DSP_FEATURE_FAX_DETECT) && (dsp->faxmode & DSP_FAXMODE_DETECT_CNG)) {
-               if (tone_detect(&dsp->cng_tone_state, s, len, NULL)) {
-                       store_digit(&dsp->digit_state, 'f');
-                       res = 'f';
+       if (squelch && mute.end) {
+               if (mute.end > samples) {
+                       s->td.mf.mute_samples = mute.end - samples;
+                       mute.end = samples;
                }
+               mute_fragment(dsp, &mute);
        }
 
-       if ((dsp->features & DSP_FEATURE_FAX_DETECT) && (dsp->faxmode & DSP_FAXMODE_DETECT_CED)) {
-               if (tone_detect(&dsp->ced_tone_state, s, len, NULL)) {
-                       store_digit(&dsp->digit_state, 'e');
-                       res = 'e';
-               }
-       }
-
-       return res;
-}
-
-int ast_dsp_digitdetect(struct ast_dsp *dsp, struct ast_frame *inf)
-{
-       short *s;
-       int len;
-       int ign=0;
-
-       if (inf->frametype != AST_FRAME_VOICE) {
-               ast_log(LOG_WARNING, "Can't check call progress of non-voice frames\n");
-               return 0;
-       }
-       if (inf->subclass != AST_FORMAT_SLINEAR) {
-               ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n");
-               return 0;
-       }
-       s = inf->data;
-       len = inf->datalen / 2;
-       return __ast_dsp_digitdetect(dsp, s, len, &ign);
+       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)
@@ -875,19 +939,6 @@ static inline int pair_there(float p1, float p2, float i1, float i2, float e)
        return 1;
 }
 
-int ast_dsp_getdigits(struct ast_dsp *dsp, char *buf, int max)
-{
-       if (max > dsp->digit_state.current_digits)
-               max = dsp->digit_state.current_digits;
-       if (max > 0) {
-               memcpy(buf, dsp->digit_state.digits, max);
-               memmove(dsp->digit_state.digits, dsp->digit_state.digits + max, dsp->digit_state.current_digits - max);
-               dsp->digit_state.current_digits -= max;
-       }
-       buf[max] = '\0';
-       return  max;
-}
-
 static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len)
 {
        int x;
@@ -1212,34 +1263,18 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
 {
        int silence;
        int res;
-       int digit;
+       int digit = 0, fax_digit = 0;
        int x;
        short *shortdata;
        unsigned char *odata;
        int len;
-       int writeback = 0;
-
-#define FIX_INF(inf) do { \
-               if (writeback) { \
-                       switch (inf->subclass) { \
-                       case AST_FORMAT_SLINEAR: \
-                               break; \
-                       case AST_FORMAT_ULAW: \
-                               for (x=0;x<len;x++) \
-                                       odata[x] = AST_LIN2MU((unsigned short)shortdata[x]); \
-                               break; \
-                       case AST_FORMAT_ALAW: \
-                               for (x=0;x<len;x++) \
-                                       odata[x] = AST_LIN2A((unsigned short)shortdata[x]); \
-                               break; \
-                       } \
-               } \
-       } while(0) 
+       struct ast_frame *outf = NULL;
 
        if (!af)
                return NULL;
        if (af->frametype != AST_FRAME_VOICE)
                return af;
+
        odata = af->data;
        len = af->datalen;
        /* Make sure we have short data */
@@ -1262,6 +1297,10 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
                ast_log(LOG_WARNING, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_getformatname(af->subclass));
                return af;
        }
+
+       /* Initially we do not want to mute anything */
+       dsp->mute_fragments = 0;
+
        res = __ast_dsp_silence_noise(dsp, shortdata, len, &silence, NULL);
        if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) && silence) {
                memset(&dsp->f, 0, sizeof(dsp->f));
@@ -1278,88 +1317,64 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
                ast_debug(1, "Requesting Hangup because the busy tone was detected on channel %s\n", chan->name);
                return &dsp->f;
        }
-       if (((dsp->features & DSP_FEATURE_DIGIT_DETECT) || (dsp->features & DSP_FEATURE_FAX_DETECT))) {
-               digit = __ast_dsp_digitdetect(dsp, shortdata, len, &writeback);
-#if 0
-               if (digit)
-                       printf("Performing digit detection returned %d, digitmode is %d\n", digit, dsp->digitmode);
-#endif
-               if (dsp->digitmode & (DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX)) {
-                       if (!dsp->thinkdigit) {
-                               if (digit) {
-                                       /* Looks like we might have something.  
-                                        * Request a conference mute for the moment */
-                                       memset(&dsp->f, 0, sizeof(dsp->f));
-                                       dsp->f.frametype = AST_FRAME_DTMF;
-                                       dsp->f.subclass = 'm';
-                                       dsp->thinkdigit = 'x';
-                                       FIX_INF(af);
-                                       if (chan)
-                                               ast_queue_frame(chan, af);
-                                       ast_frfree(af);
-                                       return &dsp->f;
-                               }
-                       } else {
-                               if (digit) {
-                                       /* Thought we saw one last time.  Pretty sure we really have now */
-                                       if ((dsp->thinkdigit != 'x') && (dsp->thinkdigit != digit)) {
-                                               /* If we found a digit, and we're changing digits, go
-                                                  ahead and send this one, but DON'T stop confmute because
-                                                  we're detecting something else, too... */
-                                               memset(&dsp->f, 0, sizeof(dsp->f));
-                                               dsp->f.frametype = AST_FRAME_DTMF_END;
-                                               dsp->f.subclass = dsp->thinkdigit;
-                                               FIX_INF(af);
-                                               if (chan)
-                                                       ast_queue_frame(chan, af);
-                                               ast_frfree(af);
-                                       } else {
-                                               dsp->thinkdigit = digit;
-                                               memset(&dsp->f, 0, sizeof(dsp->f));
-                                               dsp->f.frametype = AST_FRAME_DTMF_BEGIN;
-                                               dsp->f.subclass = dsp->thinkdigit;
-                                               FIX_INF(af);
-                                               if (chan)
-                                                       ast_queue_frame(chan, af);
-                                               ast_frfree(af);
-                                       }
-                                       return &dsp->f;
-                               } else {
-                                       memset(&dsp->f, 0, sizeof(dsp->f));
-                                       if (dsp->thinkdigit != 'x') {
-                                               /* If we found a digit, send it now */
-                                               dsp->f.frametype = AST_FRAME_DTMF_END;
-                                               dsp->f.subclass = dsp->thinkdigit;
-                                               dsp->thinkdigit = 0;
-                                       } else {
-                                               dsp->f.frametype = AST_FRAME_DTMF;
-                                               dsp->f.subclass = 'u';
-                                               dsp->thinkdigit = 0;
-                                       }
-                                       FIX_INF(af);
-                                       if (chan)
-                                               ast_queue_frame(chan, af);
-                                       ast_frfree(af);
-                                       return &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';
+               }
+       }
+
+       if ((dsp->features & DSP_FEATURE_DIGIT_DETECT)) {
+               if ((dsp->digitmode & DSP_DIGITMODE_MF))
+                       digit = mf_detect(dsp, &dsp->digit_state, shortdata, len, (dsp->digitmode & DSP_DIGITMODE_NOQUELCH) == 0, (dsp->digitmode & DSP_DIGITMODE_RELAXDTMF));
+               else
+                       digit = dtmf_detect(dsp, &dsp->digit_state, shortdata, len, (dsp->digitmode & DSP_DIGITMODE_NOQUELCH) == 0, (dsp->digitmode & DSP_DIGITMODE_RELAXDTMF));
+
+               if (dsp->digit_state.current_digits) {
+                       int event = 0;
+                       char event_digit = 0;
+
+                       if (!dsp->dtmf_began) {
+                               /* We have not reported DTMF_BEGIN for anything yet */
+
+                               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 */
+       
+                               event = AST_FRAME_DTMF_END;
+                               event_digit = dsp->digit_state.digits[0];
+                               memmove(dsp->digit_state.digits, dsp->digit_state.digits + 1, dsp->digit_state.current_digits);
+                               dsp->digit_state.current_digits--;
+                               dsp->dtmf_began = 0;
+                       }
+
+                       if (event) {
+                               memset(&dsp->f, 0, sizeof(dsp->f));
+                               dsp->f.frametype = event;
+                               dsp->f.subclass = event_digit;
+                               outf = &dsp->f;
+                               goto done;
                        }
-               } else if (dsp->digit_state.current_digits > 1 ||
-                       (dsp->digit_state.current_digits == 1 && digit != dsp->digit_state.digits[0])) {
-                       /* Since we basically generate DTMF_END frames we do it only when a digit
-                          has finished. */
-
-                       memset(&dsp->f, 0, sizeof(dsp->f));
-                       dsp->f.frametype = AST_FRAME_DTMF;
-                       dsp->f.subclass = dsp->digit_state.digits[0];
-                       memmove(dsp->digit_state.digits, dsp->digit_state.digits + 1, dsp->digit_state.current_digits);
-                       dsp->digit_state.current_digits--;
-                       FIX_INF(af);
-                       if (chan)
-                               ast_queue_frame(chan, af);
-                       ast_frfree(af);
-                       return &dsp->f;
                }
        }
+
+       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 = fax_digit;
+               outf = &dsp->f;
+               goto done;
+       }
+
        if ((dsp->features & DSP_FEATURE_CALL_PROGRESS)) {
                res = __ast_dsp_call_progress(dsp, shortdata, len);
                if (res) {
@@ -1381,8 +1396,34 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
                        }
                }
        }
-       FIX_INF(af);
-       return af;
+
+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));
+       }
+
+       switch (af->subclass) {
+       case AST_FORMAT_SLINEAR:
+               break;
+       case AST_FORMAT_ULAW:
+               for (x = 0; x < len; x++)
+                       odata[x] = AST_LIN2MU((unsigned short) shortdata[x]);
+               break;
+       case AST_FORMAT_ALAW:
+               for (x = 0; x < len; x++)
+                       odata[x] = AST_LIN2A((unsigned short) shortdata[x]);
+               break;
+       }
+
+       if (outf) {
+               if (chan) 
+                       ast_queue_frame(chan, af);
+               ast_frfree(af);
+               return outf;
+       } else {
+               return af;
+       }
 }
 
 static void ast_dsp_prog_reset(struct ast_dsp *dsp)
@@ -1457,7 +1498,7 @@ void ast_dsp_digitreset(struct ast_dsp *dsp)
 {
        int i;
        
-       dsp->thinkdigit = 0;
+       dsp->dtmf_began = 0;
        if (dsp->digitmode & DSP_DIGITMODE_MF) {
                mf_detect_state_t *s = &dsp->digit_state.td.mf;
                /* Reinitialise the detector for the next block */
@@ -1476,6 +1517,8 @@ void ast_dsp_digitreset(struct ast_dsp *dsp)
                s->lasthit = s->current_hit = 0;
                s->energy = 0.0;
                s->current_sample = 0;
+               s->hits = 0;
+               s->misses = 0;
        }
 
        dsp->digit_state.digits[0] = '\0';
@@ -1533,6 +1576,11 @@ int ast_dsp_set_call_progress_zone(struct ast_dsp *dsp, char *zone)
        return -1;
 }
 
+int ast_dsp_was_muted(struct ast_dsp *dsp)
+{
+       return (dsp->mute_fragments > 0);
+}
+
 int ast_dsp_get_tstate(struct ast_dsp *dsp) 
 {
        return dsp->tstate;