Skip to content
Snippets Groups Projects
dsp.c 71.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		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;
    
    Jason Parker's avatar
    Jason Parker committed
    
    		/* If we had a hit in this block, include it into mute fragment */
    		if (squelch && hit) {
    			if (mute.end < sample - MF_GSIZE) {
    				/* There is a gap between fragments */
    				mute_fragment(dsp, &mute);
    				mute.start = (sample > MF_GSIZE) ? (sample - MF_GSIZE) : 0;
    			}
    
    			mute.end = limit + MF_GSIZE;
    
    		/* Reinitialise the detector for the next block */
    
    		for (i = 0; i < 6; i++) {
    
    			goertzel_reset(&s->td.mf.tone_out[i]);
    
    		s->td.mf.current_sample = 0;
    
    Jason Parker's avatar
    Jason Parker committed
    	if (squelch && mute.end) {
    		if (mute.end > samples) {
    			s->td.mf.mute_samples = mute.end - samples;
    			mute.end = samples;
    
    Jason Parker's avatar
    Jason Parker committed
    		mute_fragment(dsp, &mute);
    
    Jason Parker's avatar
    Jason Parker committed
    	return (s->td.mf.current_hit); /* return the debounced hit */
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    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)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Amplify ignored stuff */
    	i2 *= TONE_THRESH;
    	i1 *= TONE_THRESH;
    	e *= TONE_THRESH;
    	/* Check first tone */
    
    	if ((p1 < i1) || (p1 < i2) || (p1 < e)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* And second */
    
    	if ((p2 < i1) || (p2 < i2) || (p2 < e)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Guess it's there... */
    	return 1;
    }
    
    static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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;
    
    	int freqcount = dsp->freqcount > FREQ_ARRAY_SIZE ? FREQ_ARRAY_SIZE : dsp->freqcount;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* 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;
    
    			samp = s[x];
    			dsp->genergy += (int32_t) samp * (int32_t) samp;
    
    			for (y = 0; y < freqcount; y++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		s += pass;
    		dsp->gsamps += pass;
    		len -= pass;
    
    		if (dsp->gsamps == dsp->gsamp_size) {
    
    			float hz[FREQ_ARRAY_SIZE];
    			for (y = 0; y < FREQ_ARRAY_SIZE; y++) {
    
    				hz[y] = goertzel_result(&dsp->freqs[y]);
    
    			case PROG_MODE_NA:
    				if (pair_there(hz[HZ_480], hz[HZ_620], hz[HZ_350], hz[HZ_440], dsp->genergy)) {
    
    					newstate = DSP_TONE_STATE_BUSY;
    
    				} else if (pair_there(hz[HZ_440], hz[HZ_480], hz[HZ_350], hz[HZ_620], dsp->genergy)) {
    
    					newstate = DSP_TONE_STATE_RINGING;
    
    				} else if (pair_there(hz[HZ_350], hz[HZ_440], hz[HZ_480], hz[HZ_620], dsp->genergy)) {
    
    					newstate = DSP_TONE_STATE_DIALTONE;
    
    				} else if (hz[HZ_950] > TONE_MIN_THRESH * TONE_THRESH) {
    
    					newstate = DSP_TONE_STATE_SPECIAL1;
    
    				} else if (hz[HZ_1400] > TONE_MIN_THRESH * TONE_THRESH) {
    
    					/* End of SPECIAL1 or middle of SPECIAL2 */
    					if (dsp->tstate == DSP_TONE_STATE_SPECIAL1 || dsp->tstate == DSP_TONE_STATE_SPECIAL2) {
    
    						newstate = DSP_TONE_STATE_SPECIAL2;
    
    				} else if (hz[HZ_1800] > TONE_MIN_THRESH * TONE_THRESH) {
    
    					/* End of SPECIAL2 or middle of SPECIAL3 */
    					if (dsp->tstate == DSP_TONE_STATE_SPECIAL2 || dsp->tstate == DSP_TONE_STATE_SPECIAL3) {
    
    						newstate = DSP_TONE_STATE_SPECIAL3;
    
    				} 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;
    
    				if (hz[HZ_400UK] > TONE_MIN_THRESH * TONE_THRESH) {
    
    				} else if (pair_there(hz[HZ_350UK], hz[HZ_440UK], hz[HZ_400UK], hz[HZ_400UK], dsp->genergy)) {
    					newstate = DSP_TONE_STATE_DIALTONE;
    
    				ast_log(LOG_WARNING, "Can't process in unknown prog mode '%u'\n", dsp->progmode);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (newstate == dsp->tstate) {
    				dsp->tcount++;
    
    				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) {
    
    					ast_debug(1, "Consider call as answered because of timeout after last ring\n");
    
    					res = AST_CONTROL_ANSWER;
    					dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    			} else {
    
    				ast_debug(5, "Stop state %d with duration %d\n", dsp->tstate, dsp->tcount);
    				ast_debug(5, "Start state %d\n", newstate);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				dsp->tstate = newstate;
    				dsp->tcount = 1;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    				dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			dsp->gsamps = 0;
    			dsp->genergy = 0.0;
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    int ast_dsp_call_progress(struct ast_dsp *dsp, struct ast_frame *inf)
    {
    	if (inf->frametype != AST_FRAME_VOICE) {
    		ast_log(LOG_WARNING, "Can't check call progress of non-voice frames\n");
    		return 0;
    	}
    
    	if (!ast_format_cache_is_slinear(inf->subclass.format)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n");
    		return 0;
    	}
    
    	return __ast_dsp_call_progress(dsp, inf->data.ptr, inf->datalen / 2);
    
    static int __ast_dsp_silence_noise(struct ast_dsp *dsp, short *s, int len, int *totalsilence, int *totalnoise, int *frames_energy)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int accum;
    	int x;
    	int res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	accum = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		accum += abs(s[x]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (accum < dsp->threshold) {
    
    		dsp->totalsilence += len / (dsp->sample_rate / 1000);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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 */
    
    		dsp->totalnoise += len / (dsp->sample_rate / 1000);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		*totalsilence = dsp->totalsilence;
    
    		*totalnoise = dsp->totalnoise;
    
    	if (frames_energy) {
    		*frames_energy = accum;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    int ast_dsp_busydetect(struct ast_dsp *dsp)
    {
    	int res = 0, x;
    #ifndef BUSYDETECT_TONEONLY
    	int avgsilence = 0, hitsilence = 0;
    #endif
    	int avgtone = 0, hittone = 0;
    
    
    	/* if we have a 4 length pattern, the way busymaybe is set doesn't help us. */
    	if (dsp->busy_cadence.length != 4) {
    		if (!dsp->busymaybe) {
    			return res;
    		}
    
    	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]) {
    
    			if (avgtone - (avgtone * BUSY_PERCENT / 100) <= dsp->historicnoise[x]) {
    
    			if (avgtone + (avgtone * BUSY_PERCENT / 100) >= dsp->historicnoise[x]) {
    
    Paul Belanger's avatar
    Paul Belanger committed
    	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
    		if (avgtone > avgsilence) {
    
    			if (avgtone - avgtone*BUSY_PERCENT/100 <= avgsilence) {
    
    			if (avgtone + avgtone*BUSY_PERCENT/100 >= avgsilence) {
    
    
    	/* If we have a 4-length pattern, we can go ahead and just check it in a different way. */
    	if (dsp->busy_cadence.length == 4) {
    		int x;
    		int errors = 0;
    		int errors_max = ((4 * dsp->busycount) / 100.0) * BUSY_PAT_PERCENT;
    
    		for (x = DSP_HISTORY - (dsp->busycount); x < DSP_HISTORY; x += 2) {
    			int temp_error;
    			temp_error = abs(dsp->historicnoise[x] - dsp->busy_cadence.pattern[0]);
    			if ((temp_error * 100) / dsp->busy_cadence.pattern[0] > BUSY_PERCENT) {
    				errors++;
    			}
    
    			temp_error = abs(dsp->historicnoise[x + 1] - dsp->busy_cadence.pattern[2]);
    			if ((temp_error * 100) / dsp->busy_cadence.pattern[2] > BUSY_PERCENT) {
    				errors++;
    			}
    
    			temp_error = abs(dsp->historicsilence[x] - dsp->busy_cadence.pattern[1]);
    			if ((temp_error * 100) / dsp->busy_cadence.pattern[1] > BUSY_PERCENT) {
    				errors++;
    			}
    
    			temp_error = abs(dsp->historicsilence[x + 1] - dsp->busy_cadence.pattern[3]);
    			if ((temp_error * 100) / dsp->busy_cadence.pattern[3] > BUSY_PERCENT) {
    				errors++;
    			}
    		}
    
    		ast_debug(5, "errors = %d  max = %d\n", errors, errors_max);
    
    		if (errors <= errors_max) {
    			return 1;
    		}
    	}
    
    
    	/* If we know the expected busy tone length, check we are in the range */
    
    	if (res && (dsp->busy_cadence.pattern[0] > 0)) {
    
    		if (abs(avgtone - dsp->busy_cadence.pattern[0]) > MAX(dsp->busy_cadence.pattern[0]*BUSY_PAT_PERCENT/100, 20)) {
    
    #ifdef BUSYDETECT_DEBUG
    			ast_debug(5, "busy detector: avgtone of %d not close enough to desired %d\n",
    
    				avgtone, dsp->busy_cadence.pattern[0]);
    
    #ifndef BUSYDETECT_TONEONLY
    
    	/* If we know the expected busy tone silent-period length, check we are in the range */
    
    	if (res && (dsp->busy_cadence.pattern[1] > 0)) {
    
    		if (abs(avgsilence - dsp->busy_cadence.pattern[1]) > MAX(dsp->busy_cadence.pattern[1]*BUSY_PAT_PERCENT/100, 20)) {
    
    #ifdef BUSYDETECT_DEBUG
    		ast_debug(5, "busy detector: avgsilence of %d not close enough to desired %d\n",
    
    			avgsilence, dsp->busy_cadence.pattern[1]);
    
    #if !defined(BUSYDETECT_TONEONLY) && defined(BUSYDETECT_DEBUG)
    
    		ast_debug(5, "ast_dsp_busydetect detected busy, avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
    	} else {
    		ast_debug(5, "busy detector: FAILED with avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
    
    Gregory Nietsky's avatar
     
    Gregory Nietsky committed
    static int ast_dsp_silence_noise_with_energy(struct ast_dsp *dsp, struct ast_frame *f, int *total, int *frames_energy, int noise)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	short *s;
    	int len;
    
    Gregory Nietsky's avatar
     
    Gregory Nietsky committed
    	int x;
    	unsigned char *odata;
    
    	if (!f) {
    		return 0;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (f->frametype != AST_FRAME_VOICE) {
    		ast_log(LOG_WARNING, "Can't calculate silence on a non-voice frame\n");
    		return 0;
    	}
    
    
    	if (ast_format_cache_is_slinear(f->subclass.format)) {
    		s = f->data.ptr;
    		len = f->datalen/2;
    	} else {
    
    Gregory Nietsky's avatar
     
    Gregory Nietsky committed
    		odata = f->data.ptr;
    		len = f->datalen;
    
    		if (ast_format_cmp(f->subclass.format, ast_format_ulaw)) {
    			s = ast_alloca(len * 2);
    			for (x = 0; x < len; x++) {
    				s[x] = AST_MULAW(odata[x]);
    			}
    		} else if (ast_format_cmp(f->subclass.format, ast_format_alaw)) {
    			s = ast_alloca(len * 2);
    			for (x = 0; x < len; x++) {
    				s[x] = AST_ALAW(odata[x]);
    			}
    		} else {
    			ast_log(LOG_WARNING, "Can only calculate silence on signed-linear, alaw or ulaw frames :(\n");
    
    Gregory Nietsky's avatar
     
    Gregory Nietsky committed
    			return 0;
    		}
    	}
    
    Gregory Nietsky's avatar
     
    Gregory Nietsky committed
    	if (noise) {
    		return __ast_dsp_silence_noise(dsp, s, len, NULL, total, frames_energy);
    	} else {
    		return __ast_dsp_silence_noise(dsp, s, len, total, NULL, frames_energy);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    }
    
    int ast_dsp_silence_with_energy(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence, int *frames_energy)
    {
    
    Gregory Nietsky's avatar
     
    Gregory Nietsky committed
    	return ast_dsp_silence_noise_with_energy(dsp, f, totalsilence, frames_energy, 0);
    }
    
    Gregory Nietsky's avatar
     
    Gregory Nietsky committed
    int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
    {
    	return ast_dsp_silence_noise_with_energy(dsp, f, totalsilence, NULL, 0);
    
    int ast_dsp_noise(struct ast_dsp *dsp, struct ast_frame *f, int *totalnoise)
    {
    
    Gregory Nietsky's avatar
     
    Gregory Nietsky committed
    	return ast_dsp_silence_noise_with_energy(dsp, f, totalnoise, NULL, 1);
    
    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;
    
    Jason Parker's avatar
    Jason Parker committed
    	int digit = 0, fax_digit = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	unsigned char *odata;
    	int len;
    
    Jason Parker's avatar
    Jason Parker committed
    	struct ast_frame *outf = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return af;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	len = af->datalen;
    	/* Make sure we have short data */
    
    	if (ast_format_cache_is_slinear(af->subclass.format)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		len = af->datalen / 2;
    
    	} else if (ast_format_cmp(af->subclass.format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) {
    		shortdata = ast_alloca(af->datalen * 2);
    		for (x = 0; x < len; x++) {
    			shortdata[x] = AST_MULAW(odata[x]);
    		}
    	} else if (ast_format_cmp(af->subclass.format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) {
    		shortdata = ast_alloca(af->datalen * 2);
    		for (x = 0; x < len; x++) {
    			shortdata[x] = AST_ALAW(odata[x]);
    		}
    
    		/*Display warning only once. Otherwise you would get hundreds of warnings every second */
    		if (dsp->display_inband_dtmf_warning) {
    			ast_log(LOG_WARNING, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_format_get_name(af->subclass.format));
    
    		dsp->display_inband_dtmf_warning = 0;
    		return af;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Jason Parker's avatar
    Jason Parker committed
    
    	/* Initially we do not want to mute anything */
    	dsp->mute_fragments = 0;
    
    
    	/* Need to run the silence detection stuff for silence suppression and busy detection */
    	if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) || (dsp->features & DSP_FEATURE_BUSY_DETECT)) {
    
    		res = __ast_dsp_silence_noise(dsp, shortdata, len, &silence, NULL, NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) && silence) {
    		memset(&dsp->f, 0, sizeof(dsp->f));
    		dsp->f.frametype = AST_FRAME_NULL;
    
    		ast_frfree(af);
    
    		return ast_frisolate(&dsp->f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	if ((dsp->features & DSP_FEATURE_BUSY_DETECT) && ast_dsp_busydetect(dsp)) {
    
    		ast_channel_softhangup_internal_flag_add(chan, 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.integer = AST_CONTROL_BUSY;
    
    		ast_frfree(af);
    
    		ast_debug(1, "Requesting Hangup because the busy tone was detected on channel %s\n", ast_channel_name(chan));
    
    		return ast_frisolate(&dsp->f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Jason Parker's avatar
    Jason Parker committed
    
    	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 | DSP_FEATURE_BUSY_DETECT)) {
    
    Paul Belanger's avatar
    Paul Belanger committed
    		if (dsp->digitmode & DSP_DIGITMODE_MF) {
    
    Jason Parker's avatar
    Jason Parker committed
    			digit = mf_detect(dsp, &dsp->digit_state, shortdata, len, (dsp->digitmode & DSP_DIGITMODE_NOQUELCH) == 0, (dsp->digitmode & DSP_DIGITMODE_RELAXDTMF));
    
    Paul Belanger's avatar
    Paul Belanger committed
    		} else {
    
    Jason Parker's avatar
    Jason Parker committed
    			digit = dtmf_detect(dsp, &dsp->digit_state, shortdata, len, (dsp->digitmode & DSP_DIGITMODE_NOQUELCH) == 0, (dsp->digitmode & DSP_DIGITMODE_RELAXDTMF));
    
    Jason Parker's avatar
    Jason Parker committed
    
    		if (dsp->digit_state.current_digits) {
    
    Jason Parker's avatar
    Jason Parker committed
    			char event_digit = 0;
    
    			if (!dsp->dtmf_began) {
    				/* We have not reported DTMF_BEGIN for anything yet */
    
    
    				if (dsp->features & DSP_FEATURE_DIGIT_DETECT) {
    					event = AST_FRAME_DTMF_BEGIN;
    					event_digit = dsp->digit_state.digits[0];
    				}
    
    Jason Parker's avatar
    Jason Parker committed
    				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 */
    
    				if (dsp->features & DSP_FEATURE_DIGIT_DETECT) {
    					event = AST_FRAME_DTMF_END;
    					event_digit = dsp->digit_state.digits[0];
    
    					event_len = dsp->digit_state.digitlen[0] * 1000 / dsp->sample_rate;
    
    				memmove(&dsp->digit_state.digits[0], &dsp->digit_state.digits[1], dsp->digit_state.current_digits);
    
    				memmove(&dsp->digit_state.digitlen[0], &dsp->digit_state.digitlen[1], dsp->digit_state.current_digits * sizeof(dsp->digit_state.digitlen[0]));
    
    Jason Parker's avatar
    Jason Parker committed
    				dsp->digit_state.current_digits--;
    				dsp->dtmf_began = 0;
    
    
    				if (dsp->features & DSP_FEATURE_BUSY_DETECT) {
    
    Paul Belanger's avatar
    Paul Belanger committed
    					/* Reset Busy Detector as we have some confirmed activity */
    
    Alec L Davis's avatar
    Alec L Davis committed
    					memset(dsp->historicsilence, 0, sizeof(dsp->historicsilence));
    
    					memset(dsp->historicnoise, 0, sizeof(dsp->historicnoise));
    
    					ast_debug(1, "DTMF Detected - Reset busydetector\n");
    
    Jason Parker's avatar
    Jason Parker committed
    			}
    
    			if (event) {
    				memset(&dsp->f, 0, sizeof(dsp->f));
    				dsp->f.frametype = event;
    
    				dsp->f.subclass.integer = event_digit;
    
    Jason Parker's avatar
    Jason Parker committed
    				outf = &dsp->f;
    				goto done;
    
    Jason Parker's avatar
    Jason Parker committed
    
    	if (fax_digit) {
    		/* Fax was detected - digit is either 'f' or 'e' */
    
    		memset(&dsp->f, 0, sizeof(dsp->f));
    		dsp->f.frametype = AST_FRAME_DTMF;
    
    		dsp->f.subclass.integer = fax_digit;
    
    Jason Parker's avatar
    Jason Parker committed
    		outf = &dsp->f;
    		goto done;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if ((dsp->features & DSP_FEATURE_CALL_PROGRESS)) {
    		res = __ast_dsp_call_progress(dsp, shortdata, len);
    		if (res) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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;
    
    				dsp->f.subclass.integer = res;
    
    				dsp->f.src = "dsp_progress";
    
    Paul Belanger's avatar
    Paul Belanger 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);
    			}
    		}
    
    	} else if ((dsp->features & DSP_FEATURE_WAITDIALTONE)) {
    		res = __ast_dsp_call_progress(dsp, shortdata, len);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Jason Parker's avatar
    Jason Parker committed
    
    done:
    	/* Mute fragment of the frame */
    	for (x = 0; x < dsp->mute_fragments; x++) {
    		memset(shortdata + dsp->mute_data[x].start, 0, sizeof(int16_t) * (dsp->mute_data[x].end - dsp->mute_data[x].start));
    	}
    
    
    	if (ast_format_cmp(af->subclass.format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) {
    
    Jason Parker's avatar
    Jason Parker committed
    			odata[x] = AST_LIN2MU((unsigned short) shortdata[x]);
    
    	} else if (ast_format_cmp(af->subclass.format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) {
    
    Jason Parker's avatar
    Jason Parker committed
    			odata[x] = AST_LIN2A((unsigned short) shortdata[x]);
    
    Jason Parker's avatar
    Jason Parker committed
    	}
    
    	if (outf) {
    
    Jason Parker's avatar
    Jason Parker committed
    			ast_queue_frame(chan, af);
    
    Jason Parker's avatar
    Jason Parker committed
    		ast_frfree(af);
    
    		return ast_frisolate(outf);
    
    Jason Parker's avatar
    Jason Parker committed
    	} else {
    		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 < FREQ_ARRAY_SIZE; x++) {
    
    		if (modes[dsp->progmode].freqs[x]) {
    
    			goertzel_init(&dsp->freqs[x], (float)modes[dsp->progmode].freqs[x], dsp->sample_rate);
    
    		}
    	}
    	dsp->freqcount = max;
    
    unsigned int ast_dsp_get_sample_rate(const struct ast_dsp *dsp)
    {
    	return dsp->sample_rate;
    }
    
    static struct ast_dsp *__ast_dsp_new(unsigned int sample_rate)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_dsp *dsp;
    
    Paul Belanger's avatar
    Paul Belanger committed
    
    	if ((dsp = ast_calloc(1, sizeof(*dsp)))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		dsp->threshold = DEFAULT_THRESHOLD;
    		dsp->features = DSP_FEATURE_SILENCE_SUPPRESS;
    
    		dsp->digitmode = DSP_DIGITMODE_DTMF;
    		dsp->faxmode = DSP_FAXMODE_DETECT_CNG;
    
    		dsp->sample_rate = sample_rate;
    
    		dsp->freqcount = 0;
    
    		/* Initialize digit detector */
    
    		ast_digit_detect_init(&dsp->digit_state, dsp->digitmode & DSP_DIGITMODE_MF, dsp->sample_rate);
    
    		dsp->display_inband_dtmf_warning = 1;
    
    		/* Initialize initial DSP progress detect parameters */
    		ast_dsp_prog_reset(dsp);
    
    		/* Initialize fax detector */
    		ast_fax_detect_init(dsp);
    
    struct ast_dsp *ast_dsp_new(void)
    {
    	return __ast_dsp_new(DEFAULT_SAMPLE_RATE);
    }
    
    struct ast_dsp *ast_dsp_new_with_rate(unsigned int sample_rate)
    {
    	return __ast_dsp_new(sample_rate);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_dsp_set_features(struct ast_dsp *dsp, int features)
    {
    	dsp->features = features;
    
    	if (!(features & DSP_FEATURE_DIGIT_DETECT)) {
    		dsp->display_inband_dtmf_warning = 0;
    	}
    
    
    int ast_dsp_get_features(struct ast_dsp *dsp)
    {
            return (dsp->features);
    }
    
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_dsp_free(struct ast_dsp *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
    		cadences = DSP_HISTORY;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	dsp->busycount = cadences;
    }
    
    
    void ast_dsp_set_busy_pattern(struct ast_dsp *dsp, const struct ast_dsp_busy_pattern *cadence)
    
    	dsp->busy_cadence = *cadence;
    	ast_debug(1, "dsp busy pattern set to %d,%d,%d,%d\n", cadence->pattern[0], cadence->pattern[1], (cadence->length == 4) ? cadence->pattern[2] : 0, (cadence->length == 4) ? cadence->pattern[3] : 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_dsp_digitreset(struct ast_dsp *dsp)
    {
    	int i;
    
    Jason Parker's avatar
    Jason Parker committed
    	dsp->dtmf_began = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (dsp->digitmode & DSP_DIGITMODE_MF) {
    
    		mf_detect_state_t *s = &dsp->digit_state.td.mf;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Reinitialise the detector for the next block */
    
    		for (i = 0; i < 6; i++) {
    
    			goertzel_reset(&s->tone_out[i]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		s->hits[4] = s->hits[3] = s->hits[2] = s->hits[1] = s->hits[0] = 0;
    		s->current_hit = 0;
    
    		s->current_sample = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    
    		dtmf_detect_state_t *s = &dsp->digit_state.td.dtmf;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Reinitialise the detector for the next block */
    
    		for (i = 0; i < 4; i++) {
    
    			goertzel_reset(&s->row_out[i]);
    			goertzel_reset(&s->col_out[i]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		s->energy = 0.0;
    		s->current_sample = 0;
    
    Jason Parker's avatar
    Jason Parker committed
    		s->hits = 0;
    		s->misses = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    
    	dsp->digit_state.digits[0] = '\0';
    	dsp->digit_state.current_digits = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    void ast_dsp_reset(struct ast_dsp *dsp)
    {
    	int x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	dsp->totalsilence = 0;
    	dsp->gsamps = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	memset(dsp->historicsilence, 0, sizeof(dsp->historicsilence));
    
    Paul Belanger's avatar
    Paul Belanger committed
    	memset(dsp->historicnoise, 0, sizeof(dsp->historicnoise));
    
    int ast_dsp_set_digitmode(struct ast_dsp *dsp, int digitmode)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    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 */
    
    		ast_digit_detect_init(&dsp->digit_state, new & DSP_DIGITMODE_MF, dsp->sample_rate);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	dsp->digitmode = digitmode;
    	return 0;
    }
    
    int ast_dsp_set_faxmode(struct ast_dsp *dsp, int faxmode)
    {
    	if (dsp->faxmode != faxmode) {
    
    Gregory Nietsky's avatar
     
    Gregory Nietsky committed
    		dsp->faxmode = faxmode;
    
    		ast_fax_detect_init(dsp);
    	}
    	return 0;
    }
    
    
    int ast_dsp_set_call_progress_zone(struct ast_dsp *dsp, char *zone)
    {
    	int x;
    
    	for (x = 0; x < ARRAY_LEN(aliases); x++) {
    
    		if (!strcasecmp(aliases[x].name, zone)) {
    			dsp->progmode = aliases[x].mode;
    			ast_dsp_prog_reset(dsp);
    			return 0;
    		}
    	}
    	return -1;
    }
    
    Jason Parker's avatar
    Jason Parker committed
    int ast_dsp_was_muted(struct ast_dsp *dsp)
    {
    	return (dsp->mute_fragments > 0);
    }
    
    
    Paul Belanger's avatar
    Paul Belanger committed
    int ast_dsp_get_tstate(struct ast_dsp *dsp)
    
    Paul Belanger's avatar
    Paul Belanger committed
    int ast_dsp_get_tcount(struct ast_dsp *dsp)
    
    
    static int _dsp_init(int reload)
    {
    	struct ast_config *cfg;
    
    Paul Belanger's avatar
    Paul Belanger committed
    	struct ast_variable *v;
    	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
    	int cfg_threshold;
    
    Paul Belanger's avatar
    Paul Belanger committed
    	if ((cfg = ast_config_load2(CONFIG_FILE_NAME, "dsp", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
    
    Paul Belanger's avatar
    Paul Belanger committed
    	thresholds[THRESHOLD_SILENCE] = DEFAULT_SILENCE_THRESHOLD;
    
    	dtmf_normal_twist = DEF_DTMF_NORMAL_TWIST;
    	dtmf_reverse_twist = DEF_DTMF_REVERSE_TWIST;
    	relax_dtmf_normal_twist = DEF_RELAX_DTMF_NORMAL_TWIST;
    	relax_dtmf_reverse_twist = DEF_RELAX_DTMF_REVERSE_TWIST;
    
            dtmf_hits_to_begin = DEF_DTMF_HITS_TO_BEGIN;
            dtmf_misses_to_end = DEF_DTMF_MISSES_TO_END;
    
    Paul Belanger's avatar
    Paul Belanger committed
    
    	if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
    
    Paul Belanger's avatar
    Paul Belanger committed
    	for (v = ast_variable_browse(cfg, "default"); v; v = v->next) {
    		if (!strcasecmp(v->name, "silencethreshold")) {
    			if (sscanf(v->value, "%30d", &cfg_threshold) < 1) {
    
    				ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
    
    Paul Belanger's avatar
    Paul Belanger committed
    			} else if (cfg_threshold < 0) {
    				ast_log(LOG_WARNING, "Invalid silence threshold '%d' specified, using default\n", cfg_threshold);
    			} else {
    				thresholds[THRESHOLD_SILENCE] = cfg_threshold;
    			}
    
    		} else if (!strcasecmp(v->name, "dtmf_normal_twist")) {
    			if (sscanf(v->value, "%30f", &cfg_twist) < 1) {
    				ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
    			} else if ((cfg_twist < 2.0) || (cfg_twist > 100.0)) {		/* < 3.0dB or > 20dB */
    				ast_log(LOG_WARNING, "Invalid dtmf_normal_twist value '%.2f' specified, using default of %.2f\n", cfg_twist, dtmf_normal_twist);
    			} else {
    				dtmf_normal_twist = cfg_twist;
    			}
    		} else if (!strcasecmp(v->name, "dtmf_reverse_twist")) {
    			if (sscanf(v->value, "%30f", &cfg_twist) < 1) {
    				ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
    			} else if ((cfg_twist < 2.0) || (cfg_twist > 100.0)) {		/* < 3.0dB or > 20dB */
    				ast_log(LOG_WARNING, "Invalid dtmf_reverse_twist value '%.2f' specified, using default of %.2f\n", cfg_twist, dtmf_reverse_twist);
    			} else {
    				dtmf_reverse_twist = cfg_twist;
    			}
    		} else if (!strcasecmp(v->name, "relax_dtmf_normal_twist")) {
    			if (sscanf(v->value, "%30f", &cfg_twist) < 1) {
    				ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
    			} else if ((cfg_twist < 2.0) || (cfg_twist > 100.0)) {		/* < 3.0dB or > 20dB */
    				ast_log(LOG_WARNING, "Invalid relax_dtmf_normal_twist value '%.2f' specified, using default of %.2f\n", cfg_twist, relax_dtmf_normal_twist);
    			} else {
    				relax_dtmf_normal_twist = cfg_twist;
    			}
    		} else if (!strcasecmp(v->name, "relax_dtmf_reverse_twist")) {
    			if (sscanf(v->value, "%30f", &cfg_twist) < 1) {
    				ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
    			} else if ((cfg_twist < 2.0) || (cfg_twist > 100.0)) {		/* < 3.0dB or > 20dB */
    				ast_log(LOG_WARNING, "Invalid relax_dtmf_reverse_twist value '%.2f' specified, using default of %.2f\n", cfg_twist, relax_dtmf_reverse_twist);
    			} else {
    				relax_dtmf_reverse_twist = cfg_twist;
    			}
    
    		} else if (!strcasecmp(v->name, "dtmf_hits_to_begin")) {
    			if (sscanf(v->value, "%30d", &cfg_threshold) < 1) {
    				ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
    			} else if (cfg_threshold < 1) {		/* must be 1 or greater */
    				ast_log(LOG_WARNING, "Invalid dtmf_hits_to_begin value '%d' specified, using default of %d\n", cfg_threshold, dtmf_hits_to_begin);
    			} else {
    				dtmf_hits_to_begin = cfg_threshold;
    			}
    		} else if (!strcasecmp(v->name, "dtmf_misses_to_end")) {
    			if (sscanf(v->value, "%30d", &cfg_threshold) < 1) {
    				ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
    			} else if (cfg_threshold < 1) {		/* must be 1 or greater */
    				ast_log(LOG_WARNING, "Invalid dtmf_misses_to_end value '%d' specified, using default of %d\n", cfg_threshold, dtmf_misses_to_end);
    			} else {
    				dtmf_misses_to_end = cfg_threshold;
    			}
    
    Paul Belanger's avatar
    Paul Belanger committed
    	ast_config_destroy(cfg);
    
    
    	return 0;
    }
    
    int ast_dsp_get_threshold_from_settings(enum threshold which)
    {
    	return thresholds[which];
    }
    
    
    #ifdef TEST_FRAMEWORK
    static void test_tone_sample_gen(short *slin_buf, int samples, int rate, int freq, short amplitude)
    {
    	int idx;
    	double sample_step = 2.0 * M_PI * freq / rate;/* radians per step */
    
    	for (idx = 0; idx < samples; ++idx) {
    		slin_buf[idx] = amplitude * sin(sample_step * idx);
    	}
    }
    #endif
    
    #ifdef TEST_FRAMEWORK
    static void test_tone_sample_gen_add(short *slin_buf, int samples, int rate, int freq, short amplitude)
    {
    	int idx;
    	double sample_step = 2.0 * M_PI * freq / rate;/* radians per step */
    
    	for (idx = 0; idx < samples; ++idx) {
    		slin_buf[idx] += amplitude * sin(sample_step * idx);
    	}
    }
    #endif
    
    #ifdef TEST_FRAMEWORK
    static void test_dual_sample_gen(short *slin_buf, int samples, int rate, int f1, short a1, int f2, short a2)
    {
    	test_tone_sample_gen(slin_buf, samples, rate, f1, a1);
    	test_tone_sample_gen_add(slin_buf, samples, rate, f2, a2);
    }
    #endif
    
    #ifdef TEST_FRAMEWORK
    #define TONE_AMPLITUDE_MAX	0x7fff	/* Max signed linear amplitude */
    #define TONE_AMPLITUDE_MIN	80		/* Min signed linear amplitude detectable */
    
    static int test_tone_amplitude_sweep(struct ast_test *test, struct ast_dsp *dsp, tone_detect_state_t *tone_state)
    {
    	short slin_buf[tone_state->block_size];
    	int result;
    	int idx;
    	struct {
    		short amp_val;
    		int detect;
    	} amp_tests[] = {
    		{ .amp_val = TONE_AMPLITUDE_MAX,	.detect = 1, },