Skip to content
Snippets Groups Projects
dsp.c 50 KiB
Newer Older
  • Learn to ignore specific revisions
  •             /* Look for two successive similar results */
                /* The logic in the next test is:
                   For KP we need 4 successive identical clean detects, with
                   two blocks of something different preceeding it. For anything
                   else we need two successive identical clean detects, with
                   two blocks of something different preceeding it. */
                if (hit == s->hits[4]
                    &&
                    hit == s->hits[3]
                    &&
                       ((hit != '*'  &&  hit != s->hits[2]  &&  hit != s->hits[1])
                        ||
                        (hit == '*'  &&  hit == s->hits[2]  &&  hit != s->hits[1]  &&  hit != s->hits[0])))
                {
                    s->detected_digits++;
                    if (s->current_digits < MAX_DTMF_DIGITS)
                    {
                        s->digits[s->current_digits++] = hit;
                        s->digits[s->current_digits] = '\0';
                    }
                    else
                    {
                        s->lost_digits++;
                    }
                }
            }
            else
            {
                hit = 0;
            }
            s->hits[0] = s->hits[1];
            s->hits[1] = s->hits[2];
            s->hits[2] = s->hits[3];
            s->hits[3] = s->hits[4];
            s->hits[4] = hit;
            /* Reinitialise the detector for the next block */
            for (i = 0;  i < 6;  i++)
           	    goertzel_reset(&s->tone_out[i]);
            s->current_sample = 0;
        }
    #endif	
    
    Mark Spencer's avatar
    Mark Spencer committed
        if ((!s->mhit) || (s->mhit != hit))
        {
    		s->mhit = 0;
    		return(0);
        }
        return (hit);
    }
    
    static int __ast_dsp_digitdetect(struct ast_dsp *dsp, short *s, int len, int *writeback)
    {
    	int res;
    	if (dsp->digitmode & DSP_DIGITMODE_MF)
    		res = mf_detect(&dsp->td.mf, s, len, dsp->digitmode & DSP_DIGITMODE_RELAXDTMF, writeback);
    	else
    		res = dtmf_detect(&dsp->td.dtmf, s, len, dsp->digitmode & DSP_DIGITMODE_RELAXDTMF, writeback);
    	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);
    }
    
    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 */
    	if ((p1 < TONE_MIN_THRESH) || (p2 < TONE_MIN_THRESH))
    		return 0;
    	/* Amplify ignored stuff */
    	i2 *= TONE_THRESH;
    	i1 *= TONE_THRESH;
    	e *= TONE_THRESH;
    	/* Check first tone */
    	if ((p1 < i1) || (p1 < i2) || (p1 < e))
    		return 0;
    	/* And second */
    	if ((p2 < i1) || (p2 < i2) || (p2 < e))
    		return 0;
    	/* Guess it's there... */
    	return 1;
    }
    
    int ast_dsp_getdigits (struct ast_dsp *dsp,
                  char *buf,
                  int max)
    {
    	if (dsp->digitmode & DSP_DIGITMODE_MF) {
    	    if (max > dsp->td.mf.current_digits)
    	        max = dsp->td.mf.current_digits;
    	    if (max > 0)
    	    {
    	        memcpy (buf, dsp->td.mf.digits, max);
    	        memmove (dsp->td.mf.digits, dsp->td.mf.digits + max, dsp->td.mf.current_digits - max);
    	        dsp->td.mf.current_digits -= max;
    	    }
    	    buf[max] = '\0';
    	    return  max;
    	} else {
    	    if (max > dsp->td.dtmf.current_digits)
    	        max = dsp->td.dtmf.current_digits;
    	    if (max > 0)
    	    {
    	        memcpy (buf, dsp->td.dtmf.digits, max);
    	        memmove (dsp->td.dtmf.digits, dsp->td.dtmf.digits + max, dsp->td.dtmf.current_digits - max);
    	        dsp->td.dtmf.current_digits -= max;
    	    }
    	    buf[max] = '\0';
    	    return  max;
    	}
    }
    
    static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len)
    {
    	int x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int pass;
    
    	int newstate = TONE_STATE_SILENCE;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res = 0;
    	while(len) {
    		/* Take the lesser of the number of samples we need and what we have */
    		pass = len;
    
    		if (pass > dsp->gsamp_size - dsp->gsamps) 
    			pass = dsp->gsamp_size - dsp->gsamps;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		for (x=0;x<pass;x++) {
    
    			for (y=0;y<dsp->freqcount;y++) 
    				goertzel_sample(&dsp->freqs[y], s[x]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			dsp->genergy += s[x] * s[x];
    		}
    		s += pass;
    		dsp->gsamps += pass;
    		len -= pass;
    
    		if (dsp->gsamps == dsp->gsamp_size) {
    			float hz[7];
    			for (y=0;y<7;y++)
    				hz[y] = goertzel_result(&dsp->freqs[y]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #if 0
    			printf("Got whole dsp state: 350: %e, 440: %e, 480: %e, 620: %e, 950: %e, 1400: %e, 1800: %e, Energy: %e\n", 
    				hz_350, hz_440, hz_480, hz_620, hz_950, hz_1400, hz_1800, dsp->genergy);
    #endif
    
    			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 = TONE_STATE_BUSY;
    				} else if (pair_there(hz[HZ_440], hz[HZ_480], hz[HZ_350], hz[HZ_620], dsp->genergy)) {
    					newstate = TONE_STATE_RINGING;
    				} else if (pair_there(hz[HZ_350], hz[HZ_440], hz[HZ_480], hz[HZ_620], dsp->genergy)) {
    					newstate = TONE_STATE_DIALTONE;
    				} else if (hz[HZ_950] > TONE_MIN_THRESH * TONE_THRESH) {
    					newstate = TONE_STATE_SPECIAL1;
    				} else if (hz[HZ_1400] > TONE_MIN_THRESH * TONE_THRESH) {
    					if (dsp->tstate == TONE_STATE_SPECIAL1)
    						newstate = TONE_STATE_SPECIAL2;
    				} else if (hz[HZ_1800] > TONE_MIN_THRESH * TONE_THRESH) {
    					if (dsp->tstate == TONE_STATE_SPECIAL2)
    						newstate = TONE_STATE_SPECIAL3;
    				} else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) {
    					newstate = TONE_STATE_TALKING;
    				} else
    					newstate = TONE_STATE_SILENCE;
    				break;
    			case PROG_MODE_CR:
    				if (hz[HZ_425] > TONE_MIN_THRESH * TONE_THRESH) {
    					newstate = TONE_STATE_RINGING;
    				} else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) {
    					newstate = TONE_STATE_TALKING;
    				} else
    					newstate = TONE_STATE_SILENCE;
    				break;
    			default:
    				ast_log(LOG_WARNING, "Can't process in unknown prog mode '%d'\n", dsp->progmode);
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (newstate == dsp->tstate) {
    				dsp->tcount++;
    				if (dsp->tcount == COUNT_THRESH) {
    					if (dsp->tstate == TONE_STATE_BUSY) {
    						res = AST_CONTROL_BUSY;
    						dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
    					} else if (dsp->tstate == TONE_STATE_TALKING) {
    						res = AST_CONTROL_ANSWER;
    						dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
    					} else if (dsp->tstate == TONE_STATE_RINGING)
    						res = AST_CONTROL_RINGING;
    					else if (dsp->tstate == TONE_STATE_SPECIAL3) {
    						res = AST_CONTROL_CONGESTION;
    						dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
    					}
    					
    				}
    			} else {
    #if 0
    				printf("Newstate: %d\n", newstate);
    #endif
    				dsp->tstate = newstate;
    				dsp->tcount = 1;
    			}
    			
    			/* Reset goertzel */						
    			for (x=0;x<7;x++)
    				dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0;
    			dsp->gsamps = 0;
    			dsp->genergy = 0.0;
    		}
    	}
    #if 0
    	if (res)
    		printf("Returning %d\n", res);
    #endif		
    	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 (inf->subclass != AST_FORMAT_SLINEAR) {
    		ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n");
    		return 0;
    	}
    	return __ast_dsp_call_progress(dsp, inf->data, inf->datalen / 2);
    }
    
    static int __ast_dsp_silence(struct ast_dsp *dsp, short *s, int len, int *totalsilence)
    {
    	int accum;
    	int x;
    	int res = 0;
    	
    	accum = 0;
    	for (x=0;x<len; x++) 
    		accum += abs(s[x]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (accum < dsp->threshold) {
    		dsp->totalsilence += len/8;
    		if (dsp->totalnoise) {
    			/* Move and save history */
    
    			memmove(dsp->historicnoise + DSP_HISTORY - dsp->busycount, dsp->historicnoise + DSP_HISTORY - dsp->busycount +1, dsp->busycount*sizeof(dsp->historicnoise[0]));
    
    Mark Spencer's avatar
    Mark Spencer committed
    			dsp->historicnoise[DSP_HISTORY - 1] = dsp->totalnoise;
    
    /* we don't want to check for busydetect that frequently */
    #if 0
    
    Mark Spencer's avatar
    Mark Spencer committed
    			dsp->busymaybe = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		dsp->totalnoise = 0;
    		res = 1;
    	} else {
    		dsp->totalnoise += len/8;
    		if (dsp->totalsilence) {
    
    			int silence1 = dsp->historicsilence[DSP_HISTORY - 1];
    			int silence2 = dsp->historicsilence[DSP_HISTORY - 2];
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Move and save history */
    
    			memmove(dsp->historicsilence + DSP_HISTORY - dsp->busycount, dsp->historicsilence + DSP_HISTORY - dsp->busycount + 1, dsp->busycount*sizeof(dsp->historicsilence[0]));
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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)
    					dsp->busymaybe = 1;
    				else 
    					dsp->busymaybe = 0;
    			} else {
    				if (silence1 - silence1/BUSY_PERCENT <= silence2)
    					dsp->busymaybe = 1;
    				else 
    					dsp->busymaybe = 0;
    			}
    					
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		dsp->totalsilence = 0;
    	}
    	if (totalsilence)
    		*totalsilence = dsp->totalsilence;
    	return res;
    }
    
    #ifdef BUSYDETECT_MARTIN
    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 (!dsp->busymaybe)
    		return res;
    	for (x=DSP_HISTORY - dsp->busycount;x<DSP_HISTORY;x++) {
    #ifndef BUSYDETECT_TONEONLY
    		avgsilence += dsp->historicsilence[x];
    #endif
    		avgtone += dsp->historicnoise[x];
    	}
    #ifndef BUSYDETECT_TONEONLY
    	avgsilence /= dsp->busycount;
    #endif
    	avgtone /= dsp->busycount;
    	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])
    				hitsilence++;
    		} else {
    			if (avgsilence + (avgsilence / BUSY_PERCENT) >= dsp->historicsilence[x])
    				hitsilence++;
    		}
    #endif
    		if (avgtone > dsp->historicnoise[x]) {
    			if (avgtone - (avgtone / BUSY_PERCENT) <= dsp->historicsilence[x])
    				hittone++;
    		} else {
    			if (avgtone + (avgtone / BUSY_PERCENT) >= dsp->historicsilence[x])
    				hittone++;
    		}
    	}
    #ifndef BUSYDETECT_TONEONLY
    	if ((hittone >= dsp->busycount - 1) && (hitsilence >= dsp->busycount - 1) && (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX) && (avgsilence >= BUSY_MIN && avgsilence <= BUSY_MAX)) {
    #else
    	if ((hittone >= dsp->busycount - 1) && (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX)) {
    #endif
    #ifdef BUSYDETECT_COMPARE_TONE_AND_SILENCE
    #ifdef BUSYDETECT_TONEONLY
    
    Martin Pycko's avatar
    Martin Pycko committed
    #error You cant use BUSYDETECT_TONEONLY together with BUSYDETECT_COMPARE_TONE_AND_SILENCE
    
    #endif
    		if (avgtone > avgsilence) {
    			if (avgtone - avgtone/(BUSY_PERCENT*2) <= avgsilence)
    				res = 1;
    		} else {
    			if (avgtone + avgtone/(BUSY_PERCENT*2) >= avgsilence)
    				res = 1;
    		}
    #else
    		res = 1;
    #endif
    	}
    #if 0
    	if (res)
    		ast_log(LOG_NOTICE, "detected busy, avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
    #endif
    	return res;
    }
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_dsp_busydetect(struct ast_dsp *dsp)
    {
    	int x;
    	int res = 0;
    	int max, min;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (dsp->busymaybe) {
    #if 0
    		printf("Maybe busy!\n");
    #endif		
    		dsp->busymaybe = 0;
    		min = 9999;
    		max = 0;
    		for (x=DSP_HISTORY - dsp->busycount;x<DSP_HISTORY;x++) {
    #if 0
    			printf("Silence: %d, Noise: %d\n", dsp->historicsilence[x], dsp->historicnoise[x]);
    #endif			
    			if (dsp->historicsilence[x] < min)
    				min = dsp->historicsilence[x];
    			if (dsp->historicnoise[x] < min)
    				min = dsp->historicnoise[x];
    			if (dsp->historicsilence[x] > max)
    				max = dsp->historicsilence[x];
    			if (dsp->historicnoise[x] > max)
    				max = dsp->historicnoise[x];
    		}
    		if ((max - min < BUSY_THRESHOLD) && (max < BUSY_MAX) && (min > BUSY_MIN)) {
    #if 0
    			printf("Busy!\n");
    #endif			
    			res = 1;
    		}
    #if 0
    		printf("Min: %d, max: %d\n", min, max);
    #endif		
    	}
    	return res;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
    {
    	short *s;
    	int len;
    	
    	if (f->frametype != AST_FRAME_VOICE) {
    		ast_log(LOG_WARNING, "Can't calculate silence on a non-voice frame\n");
    		return 0;
    	}
    	if (f->subclass != AST_FORMAT_SLINEAR) {
    		ast_log(LOG_WARNING, "Can only calculate silence on signed-linear frames :(\n");
    		return 0;
    	}
    	s = f->data;
    	len = f->datalen/2;
    	return __ast_dsp_silence(dsp, s, len, totalsilence);
    }
    
    
    struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, struct ast_frame *af)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int silence;
    	int res;
    	int digit;
    	int x;
    	unsigned 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(shortdata[x]); \
    				break; \
    			case AST_FORMAT_ALAW: \
    				for (x=0;x<len;x++) \
    					odata[x] = AST_LIN2A(shortdata[x]); \
    				break; \
    			} \
    		} \
    	} while(0) 
    
    	if (!af)
    		return NULL;
    	if (af->frametype != AST_FRAME_VOICE)
    		return af;
    	odata = af->data;
    	len = af->datalen;
    	/* Make sure we have short data */
    	switch(af->subclass) {
    	case AST_FORMAT_SLINEAR:
    		shortdata = af->data;
    		len = af->datalen / 2;
    		break;
    	case AST_FORMAT_ULAW:
    		shortdata = alloca(af->datalen * 2);
    		if (!shortdata) {
    			ast_log(LOG_WARNING, "Unable to allocate stack space for data: %s\n", strerror(errno));
    			return af;
    		}
    		for (x=0;x<len;x++) 
    			shortdata[x] = AST_MULAW(odata[x]);
    		break;
    	case AST_FORMAT_ALAW:
    		shortdata = alloca(af->datalen * 2);
    		if (!shortdata) {
    			ast_log(LOG_WARNING, "Unable to allocate stack space for data: %s\n", strerror(errno));
    			return af;
    		}
    		for (x=0;x<len;x++) 
    			shortdata[x] = AST_ALAW(odata[x]);
    		break;
    	default:
    
    		ast_log(LOG_WARNING, "Unable to process inband DTMF on %d frames\n", af->subclass);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return af;
    	}
    	silence = __ast_dsp_silence(dsp, shortdata, len, NULL);
    	if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) && silence) {
    		memset(&dsp->f, 0, sizeof(dsp->f));
    		dsp->f.frametype = AST_FRAME_NULL;
    		return &dsp->f;
    	}
    	if ((dsp->features & DSP_FEATURE_BUSY_DETECT) && ast_dsp_busydetect(dsp)) {
    
    		chan->_softhangup |= AST_SOFTHANGUP_DEV;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		memset(&dsp->f, 0, sizeof(dsp->f));
    		dsp->f.frametype = AST_FRAME_CONTROL;
    		dsp->f.subclass = AST_CONTROL_BUSY;
    
    		ast_log(LOG_DEBUG, "Requesting Hangup because the busy tone was detected on channel %s\n", chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return &dsp->f;
    	}
    	if ((dsp->features & DSP_FEATURE_DTMF_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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_frfree(af);
    					return &dsp->f;
    				}
    			} else {
    				if (digit) {
    					/* Thought we saw one last time.  Pretty sure we really have now */
    
    					if (dsp->thinkdigit) {
    
    						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;
    							dsp->f.subclass = dsp->thinkdigit;
    							FIX_INF(af);
    							if (chan)
    
    Mark Spencer's avatar
    Mark Spencer committed
    						dsp->thinkdigit = digit;
    
    					dsp->thinkdigit = digit;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				} else {
    					if (dsp->thinkdigit) {
    						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;
    							dsp->f.subclass = dsp->thinkdigit;
    
    							dsp->thinkdigit = 0;
    						} else {
    							dsp->f.frametype = AST_FRAME_DTMF;
    							dsp->f.subclass = 'u';
    							dsp->thinkdigit = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						}
    						FIX_INF(af);
    						if (chan)
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_frfree(af);
    						return &dsp->f;
    					}
    				}
    			}
    		} else if (!digit) {
    			/* Only check when there is *not* a hit... */
    			if (dsp->digitmode & DSP_DIGITMODE_MF) {
    				if (dsp->td.mf.current_digits) {
    					memset(&dsp->f, 0, sizeof(dsp->f));
    					dsp->f.frametype = AST_FRAME_DTMF;
    					dsp->f.subclass = dsp->td.mf.digits[0];
    					memmove(dsp->td.mf.digits, dsp->td.mf.digits + 1, dsp->td.mf.current_digits);
    					dsp->td.mf.current_digits--;
    					FIX_INF(af);
    					if (chan)
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_frfree(af);
    					return &dsp->f;
    				}
    			} else {
    				if (dsp->td.dtmf.current_digits) {
    					memset(&dsp->f, 0, sizeof(dsp->f));
    					dsp->f.frametype = AST_FRAME_DTMF;
    					dsp->f.subclass = dsp->td.dtmf.digits[0];
    					memmove(dsp->td.dtmf.digits, dsp->td.dtmf.digits + 1, dsp->td.dtmf.current_digits);
    					dsp->td.dtmf.current_digits--;
    					FIX_INF(af);
    					if (chan)
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_frfree(af);
    					return &dsp->f;
    				}
    			}
    		}
    	}
    	if ((dsp->features & DSP_FEATURE_CALL_PROGRESS)) {
    		res = __ast_dsp_call_progress(dsp, shortdata, len);
    		memset(&dsp->f, 0, sizeof(dsp->f));
    		dsp->f.frametype = AST_FRAME_CONTROL;
    		if (res) {
    			switch(res) {
    			case AST_CONTROL_ANSWER:
    			case AST_CONTROL_BUSY:
    			case AST_CONTROL_RINGING:
    			case AST_CONTROL_CONGESTION:
    				dsp->f.subclass = res;
    				if (chan) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    				break;
    			default:
    				ast_log(LOG_WARNING, "Don't know how to represent call progress message %d\n", res);
    			}
    		}
    	}
    	FIX_INF(af);
    	return af;
    }
    
    
    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<sizeof(modes[dsp->progmode].freqs) / sizeof(modes[dsp->progmode].freqs[0]);x++) {
    		if (modes[dsp->progmode].freqs[x]) {
    			goertzel_init(&dsp->freqs[x], (float)modes[dsp->progmode].freqs[x], dsp->gsamp_size);
    			max = x;
    		}
    	}
    	dsp->freqcount = max;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    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));
    		dsp->threshold = DEFAULT_THRESHOLD;
    		dsp->features = DSP_FEATURE_SILENCE_SUPPRESS;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Initialize DTMF detector */
    		ast_dtmf_detect_init(&dsp->td.dtmf);
    
    		/* Initialize initial DSP progress detect parameters */
    		ast_dsp_prog_reset(dsp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	return dsp;
    }
    
    void ast_dsp_set_features(struct ast_dsp *dsp, int features)
    {
    	dsp->features = features;
    }
    
    void ast_dsp_free(struct ast_dsp *dsp)
    {
    	free(dsp);
    }
    
    
    void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold)
    {
    	dsp->threshold = threshold;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_dsp_set_busy_count(struct ast_dsp *dsp, int cadences)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (cadences > DSP_HISTORY)
    		cadences = DSP_HISTORY;
    	dsp->busycount = cadences;
    }
    
    void ast_dsp_digitreset(struct ast_dsp *dsp)
    {
    	int i;
    	dsp->thinkdigit = 0;
    	if (dsp->digitmode & DSP_DIGITMODE_MF) {
    		memset(dsp->td.mf.digits, 0, sizeof(dsp->td.mf.digits));
    		dsp->td.mf.current_digits = 0;
    		/* Reinitialise the detector for the next block */
    		for (i = 0;  i < 6;  i++) {
    	       	goertzel_reset(&dsp->td.mf.tone_out[i]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		    goertzel_reset(&dsp->td.mf.tone_out2nd[i]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		dsp->td.mf.energy = 0.0;
    	    dsp->td.mf.hit1 = dsp->td.mf.hit2 = dsp->td.mf.hit3 = dsp->td.mf.hit4 = dsp->td.mf.mhit = 0;
    
    #else
    	    dsp->td.mf.hits[4] = dsp->td.mf.hits[3] = dsp->td.mf.hits[2] = dsp->td.mf.hits[1] = dsp->td.mf.hits[0] = dsp->td.mf.mhit = 0;
    #endif		
    		dsp->td.mf.current_sample = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    		memset(dsp->td.dtmf.digits, 0, sizeof(dsp->td.dtmf.digits));
    		dsp->td.dtmf.current_digits = 0;
    		/* Reinitialise the detector for the next block */
    		for (i = 0;  i < 4;  i++) {
    	       	goertzel_reset(&dsp->td.dtmf.row_out[i]);
    		    goertzel_reset(&dsp->td.dtmf.col_out[i]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	    	goertzel_reset(&dsp->td.dtmf.row_out2nd[i]);
    	    	goertzel_reset(&dsp->td.dtmf.col_out2nd[i]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	    goertzel_reset (&dsp->td.dtmf.fax_tone);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	    goertzel_reset (&dsp->td.dtmf.fax_tone2nd);
    
    	    dsp->td.dtmf.hit1 = dsp->td.dtmf.hit2 = dsp->td.dtmf.hit3 = dsp->td.dtmf.hit4 = dsp->td.dtmf.mhit = 0;
    #else
    	    dsp->td.dtmf.hits[2] = dsp->td.dtmf.hits[1] = dsp->td.dtmf.hits[0] =  dsp->td.dtmf.mhit = 0;
    #endif		
    
    Mark Spencer's avatar
    Mark Spencer committed
    		dsp->td.dtmf.energy = 0.0;
    		dsp->td.dtmf.current_sample = 0;
    	}
    }
    
    void ast_dsp_reset(struct ast_dsp *dsp)
    {
    	int x;
    	dsp->totalsilence = 0;
    	dsp->gsamps = 0;
    	for (x=0;x<4;x++)
    		dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0;
    	memset(dsp->historicsilence, 0, sizeof(dsp->historicsilence));
    	memset(dsp->historicnoise, 0, sizeof(dsp->historicnoise));
    	
    }
    
    int ast_dsp_digitmode(struct ast_dsp *dsp, int digitmode)
    {
    	int new, old;
    	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 */
    		if (new & DSP_DIGITMODE_MF)
    			ast_mf_detect_init(&dsp->td.mf);
    		else
    			ast_dtmf_detect_init(&dsp->td.dtmf);
    	}
    	dsp->digitmode = digitmode;
    	return 0;
    }
    
    int ast_dsp_set_call_progress_zone(struct ast_dsp *dsp, char *zone)
    {
    	int x;
    	for (x=0;x<sizeof(aliases) / sizeof(aliases[0]);x++) {
    		if (!strcasecmp(aliases[x].name, zone)) {
    			dsp->progmode = aliases[x].mode;
    			ast_dsp_prog_reset(dsp);
    			return 0;
    		}
    	}
    	return -1;
    }