Skip to content
Snippets Groups Projects
dsp.c 41.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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, int needlock)
    {
    	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 detect process %d frames\n", af->subclass);
    		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)
    						ast_queue_frame(chan, af, needlock);
    					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)
    								ast_queue_frame(chan, af, needlock);
    							ast_frfree(af);
    						}
    
    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)
    							ast_queue_frame(chan, af, needlock);
    						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)
    						ast_queue_frame(chan, af, needlock);
    					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)
    						ast_queue_frame(chan, af, needlock);
    					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) 
    					ast_queue_frame(chan, &dsp->f, needlock);
    				break;
    			default:
    				ast_log(LOG_WARNING, "Don't know how to represent call progress message %d\n", res);
    			}
    		}
    	}
    	FIX_INF(af);
    	return af;
    }
    
    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 goertzels */
    		goertzel_init(&dsp->freqs[HZ_350], 350.0);
    		goertzel_init(&dsp->freqs[HZ_440], 440.0);
    		goertzel_init(&dsp->freqs[HZ_480], 480.0);
    		goertzel_init(&dsp->freqs[HZ_620], 620.0);
    		goertzel_init(&dsp->freqs[HZ_950], 950.0);
    		goertzel_init(&dsp->freqs[HZ_1400], 1400.0);
    		goertzel_init(&dsp->freqs[HZ_1800], 1800.0);
    		/* Initialize DTMF detector */
    		ast_dtmf_detect_init(&dsp->td.dtmf);
    	}
    	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]);
    		    goertzel_reset(&dsp->td.mf.tone_out2nd[i]);
    		}
    		dsp->td.mf.energy = 0.0;
    		dsp->td.mf.current_sample = 0;
    	    dsp->td.mf.hit1 = dsp->td.mf.hit2 = dsp->td.mf.hit3 = dsp->td.mf.hit4 = dsp->td.mf.mhit = 0;
    	} 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]);
    		}
    	    goertzel_reset (&dsp->td.dtmf.fax_tone);
    	    goertzel_reset (&dsp->td.dtmf.fax_tone2nd);
    		dsp->td.dtmf.energy = 0.0;
    		dsp->td.dtmf.current_sample = 0;
    	    dsp->td.dtmf.hit1 = dsp->td.dtmf.hit2 = dsp->td.dtmf.hit3 = dsp->td.dtmf.hit4 = dsp->td.dtmf.mhit = 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;
    }