Skip to content
Snippets Groups Projects
dsp.c 49.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    	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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} 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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    }
    
    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 = DSP_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("\n350:     425:     440:     480:     620:     950:     1400:    1800:    Energy:   \n");
    			printf("%.2e %.2e %.2e %.2e %.2e %.2e %.2e %.2e %.2e\n", 
    				hz[HZ_350], hz[HZ_425], hz[HZ_440], hz[HZ_480], hz[HZ_620], hz[HZ_950], hz[HZ_1400], hz[HZ_1800], dsp->genergy);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #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 = 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) {
    
    					if (dsp->tstate == DSP_TONE_STATE_SPECIAL1)
    						newstate = DSP_TONE_STATE_SPECIAL2;
    
    				} else if (hz[HZ_1800] > TONE_MIN_THRESH * TONE_THRESH) {
    
    					if (dsp->tstate == DSP_TONE_STATE_SPECIAL2)
    						newstate = DSP_TONE_STATE_SPECIAL3;
    
    				} else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) {
    
    					newstate = DSP_TONE_STATE_TALKING;
    
    					newstate = DSP_TONE_STATE_SILENCE;
    
    				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;
    
    					newstate = DSP_TONE_STATE_SILENCE;
    
    			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);
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (newstate == dsp->tstate) {
    				dsp->tcount++;
    
    				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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    			} else {
    #if 0
    
    				ast_log(LOG_NOTICE, "Stop state %d with duration %d\n", dsp->tstate, dsp->tcount);
    				ast_log(LOG_NOTICE, "Start state %d\n", newstate);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	accum = 0;
    	for (x=0;x<len; x++) 
    		accum += abs(s[x]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (accum < dsp->threshold) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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 {
    
    		/* Not silent */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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/100 >= silence2)
    
    				if (silence1 - silence1*BUSY_PERCENT/100 <= silence2)
    
    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/100) <= dsp->historicsilence[x])
    
    			if (avgsilence + (avgsilence*BUSY_PERCENT/100) >= dsp->historicsilence[x])
    
    				hitsilence++;
    		}
    #endif
    		if (avgtone > dsp->historicnoise[x]) {
    
    			if (avgtone - (avgtone*BUSY_PERCENT/100) <= dsp->historicnoise[x])
    
    			if (avgtone + (avgtone*BUSY_PERCENT/100) >= dsp->historicnoise[x])
    
    	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
    
    			if (avgtone - avgtone*BUSY_PERCENT/100 <= avgsilence)
    
    			if (avgtone + avgtone*BUSY_PERCENT/100 >= avgsilence)
    
    	/* 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)) {
    
    			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
    
    		ast_log(LOG_DEBUG, "ast_dsp_busydetect detected busy, avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
    
    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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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]); \
    
    Mark Spencer's avatar
    Mark Spencer committed
    				break; \
    			case AST_FORMAT_ALAW: \
    				for (x=0;x<len;x++) \
    
    					odata[x] = AST_LIN2A((unsigned short)shortdata[x]); \
    
    Mark Spencer's avatar
    Mark Spencer committed
    				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:
    
    		if (!(shortdata = alloca(af->datalen * 2))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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:
    
    		if (!(shortdata = alloca(af->datalen * 2))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_getformatname(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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    					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);
    		if (res) {
    			switch(res) {
    			case AST_CONTROL_ANSWER:
    			case AST_CONTROL_BUSY:
    			case AST_CONTROL_RINGING:
    			case AST_CONTROL_CONGESTION:
    
    				memset(&dsp->f, 0, sizeof(dsp->f));
    				dsp->f.frametype = AST_FRAME_CONTROL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				dsp->f.subclass = res;
    
    				dsp->f.src = "dsp_progress";
    
    Mark Spencer's avatar
    Mark Spencer committed
    				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);
    
    		}
    	}
    	dsp->freqcount = max;
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct ast_dsp *ast_dsp_new(void)
    {
    	struct ast_dsp *dsp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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_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);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_dsp_digitreset(struct ast_dsp *dsp)
    {
    	int i;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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]);
    
    			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;
    
    		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;
    
    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]);
    
    			goertzel_reset(&dsp->td.dtmf.row_out2nd[i]);
    			goertzel_reset(&dsp->td.dtmf.col_out2nd[i]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		goertzel_reset (&dsp->td.dtmf.fax_tone);
    
    		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;
    
    		dsp->td.dtmf.hits[2] = dsp->td.dtmf.hits[1] = dsp->td.dtmf.hits[0] =  dsp->td.dtmf.mhit = 0;
    
    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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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));	
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    int ast_dsp_digitmode(struct ast_dsp *dsp, int digitmode)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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;
    }
    
    
    int ast_dsp_get_tstate(struct ast_dsp *dsp) 
    {
    	return dsp->tstate;
    }
    
    int ast_dsp_get_tcount(struct ast_dsp *dsp) 
    {
    	return dsp->tcount;
    }