[asterisk-commits] irroot: branch irroot/distrotech-customers-1.8 r329328 - in /team/irroot/dist...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jul 22 15:03:07 CDT 2011


Author: irroot
Date: Fri Jul 22 15:03:04 2011
New Revision: 329328

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=329328
Log:
V.21 1850hz Detector instead of CED
Add Noise detection to WaitFAX

Modified:
    team/irroot/distrotech-customers-1.8/include/asterisk/dsp.h
    team/irroot/distrotech-customers-1.8/main/dsp.c
    team/irroot/distrotech-customers-1.8/res/res_fax.c

Modified: team/irroot/distrotech-customers-1.8/include/asterisk/dsp.h
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers-1.8/include/asterisk/dsp.h?view=diff&rev=329328&r1=329327&r2=329328
==============================================================================
--- team/irroot/distrotech-customers-1.8/include/asterisk/dsp.h (original)
+++ team/irroot/distrotech-customers-1.8/include/asterisk/dsp.h Fri Jul 22 15:03:04 2011
@@ -43,9 +43,11 @@
 #define DSP_FEATURE_CALL_PROGRESS	(DSP_PROGRESS_TALK | DSP_PROGRESS_RINGING | DSP_PROGRESS_BUSY | DSP_PROGRESS_CONGESTION)
 #define DSP_FEATURE_WAITDIALTONE	(1 << 20)		/*!< Enable dial tone detection */
 
-#define DSP_FAXMODE_DETECT_CNG	(1 << 0)
-#define DSP_FAXMODE_DETECT_CED	(1 << 1)
-#define DSP_FAXMODE_DETECT_ALL	(DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED)
+#define DSP_FAXMODE_DETECT_CNG		(1 << 0)
+#define DSP_FAXMODE_DETECT_CED		(1 << 1)
+#define DSP_FAXMODE_DETECT_SQUELCH	(1 << 2)
+#define DSP_FAXMODE_DETECT_V21		(1 << 3)
+#define DSP_FAXMODE_DETECT_ALL	(DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED | DSP_FAXMODE_DETECT_V21)
 
 #define DSP_TONE_STATE_SILENCE  0
 #define DSP_TONE_STATE_RINGING  1

Modified: team/irroot/distrotech-customers-1.8/main/dsp.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers-1.8/main/dsp.c?view=diff&rev=329328&r1=329327&r2=329328
==============================================================================
--- team/irroot/distrotech-customers-1.8/main/dsp.c (original)
+++ team/irroot/distrotech-customers-1.8/main/dsp.c Fri Jul 22 15:03:04 2011
@@ -251,6 +251,20 @@
 	int last_hit;		/* Indicates if the last processed block was a hit */
 
 } tone_detect_state_t;
+
+typedef struct
+{
+	int block_size;
+	goertzel_state_t tone;
+	float energy;		/* Accumulated energy of the current block */
+	int samples_pending;	/* Samples remain to complete the current block */
+
+	float threshold;	/* Energy of the tone relative to energy from all other signals to consider a hit */
+
+	int hit_count;
+	int miss_count;
+
+} v21_detect_state_t;
 
 typedef struct
 {
@@ -399,6 +413,7 @@
 	digit_detect_state_t digit_state;
 	tone_detect_state_t cng_tone_state;
 	tone_detect_state_t ced_tone_state;
+	v21_detect_state_t v21_state;
 };
 
 static void mute_fragment(struct ast_dsp *dsp, fragment_t *fragment)
@@ -471,10 +486,59 @@
 	ast_debug(1, "Setup tone %d Hz, %d ms, block_size=%d, hits_required=%d\n", freq, duration, s->block_size, s->hits_required);
 }
 
+static void ast_v21_detect_init(v21_detect_state_t *s)
+{
+	float x;
+	int periods_in_block;
+
+	/* If we want to remove tone, it is important to have block size not
+	   to exceed frame size. Otherwise by the moment tone is detected it is too late
+	   to squelch it from previous frames. Block size is 20ms at the given sample rate.*/
+	s->block_size = SAMPLES_IN_FRAME;
+
+	periods_in_block = s->block_size * 1850 / SAMPLE_RATE;
+
+	/* Make sure we will have at least 5 periods at target frequency for analisys.
+	   This may make block larger than expected packet and will make squelching impossible
+	   but at least we will be detecting the tone */
+	if (periods_in_block < 5)
+		periods_in_block = 5;
+
+	/* Now calculate final block size. It will contain integer number of periods */
+	s->block_size = periods_in_block * SAMPLE_RATE / 1850;
+
+	goertzel_init(&s->tone, 1850.0, s->block_size);
+
+	s->samples_pending = s->block_size;
+	s->hit_count = 0;
+	s->miss_count = 0;
+	s->energy = 0.0;
+
+	/* We want tone energy to be amp decibels above the rest of the signal (the noise).
+	   According to Parseval's theorem the energy computed in time domain equals to energy
+	   computed in frequency domain. So subtracting energy in the frequency domain (Goertzel result)
+	   from the energy in the time domain we will get energy of the remaining signal (without the tone
+	   we are detecting). We will be checking that
+		10*log(Ew / (Et - Ew)) > amp
+	   Calculate threshold so that we will be actually checking
+		Ew > Et * threshold
+	*/
+
+	x = pow(10.0, 16 / 10.0);
+	s->threshold = x / (x + 1);
+
+	ast_debug(1, "Setup v21 detector, block_size=%d\n", s->block_size);
+}
+
 static void ast_fax_detect_init(struct ast_dsp *s)
 {
 	ast_tone_detect_init(&s->cng_tone_state, FAX_TONE_CNG_FREQ, FAX_TONE_CNG_DURATION, FAX_TONE_CNG_DB);
 	ast_tone_detect_init(&s->ced_tone_state, FAX_TONE_CED_FREQ, FAX_TONE_CED_DURATION, FAX_TONE_CED_DB);
+	ast_v21_detect_init(&s->v21_state);
+	if (s->faxmode & DSP_FAXMODE_DETECT_SQUELCH) {
+		s->cng_tone_state.squelch = 1;
+		s->ced_tone_state.squelch = 1;
+	}
 }
 
 static void ast_dtmf_detect_init (dtmf_detect_state_t *s)
@@ -519,6 +583,89 @@
 	} else {
 		ast_dtmf_detect_init(&s->td.dtmf);
 	}
+}
+
+/*! \brief Detect a v21 preamble.
+ * This code is derived from the tone_detect code and detects a pattern of 1850
+ * Hz tone found in a v21 preamble.
+ */
+static int v21_detect(struct ast_dsp *dsp, v21_detect_state_t *s, int16_t *amp, int samples)
+{
+	float tone_energy;
+	int i;
+	int hit = 0;
+	int limit;
+	int res = 0;
+	int16_t *ptr;
+	int start, end;
+
+	for (start = 0;  start < samples;  start = end) {
+		/* Process in blocks. */
+		limit = samples - start;
+		if (limit > s->samples_pending) {
+			limit = s->samples_pending;
+		}
+		end = start + limit;
+
+		for (i = limit, ptr = amp ; i > 0; i--, ptr++) {
+			/* signed 32 bit int should be enough to suqare any possible signed 16 bit value */
+			s->energy += (int32_t) *ptr * (int32_t) *ptr;
+
+			goertzel_sample(&s->tone, *ptr);
+		}
+
+		s->samples_pending -= limit;
+
+		if (s->samples_pending) {
+			/* Finished incomplete (last) block */
+			break;
+		}
+
+		tone_energy = goertzel_result(&s->tone);
+
+		/* Scale to make comparable */
+		tone_energy *= 2.0;
+		s->energy *= s->block_size;
+
+		ast_debug(10, "v21 1850 Ew=%.2E, Et=%.2E, s/n=%10.2f\n", tone_energy, s->energy, tone_energy / (s->energy - tone_energy));
+
+		hit = 0;
+		if (tone_energy > s->energy * s->threshold) {
+			ast_debug(10, "Hit! count=%d; miss_count=%d\n", s->hit_count, s->miss_count);
+			hit = 1;
+		}
+
+		if (hit) {
+			if (s->hit_count == 0 || s->miss_count == 3) {
+				s->hit_count++;
+			} else {
+				s->hit_count = 0;
+			}
+
+			s->miss_count = 0;
+		} else {
+			s->miss_count++;
+			if (s->miss_count > 3) {
+				s->hit_count = 0;
+			}
+		}
+
+		if (s->hit_count == 4) {
+			ast_debug(1, "v21 preamble detected\n");
+			res = 1;
+		}
+
+		/* Reinitialise the detector for the next block */
+		goertzel_reset(&s->tone);
+
+		/* Advance to the next block */
+		s->energy = 0.0;
+		s->samples_pending = s->block_size;
+
+		amp += limit;
+	}
+
+	return res;
 }
 
 static int tone_detect(struct ast_dsp *dsp, tone_detect_state_t *s, int16_t *amp, int samples)
@@ -1385,6 +1532,10 @@
 		if ((dsp->faxmode & DSP_FAXMODE_DETECT_CED) && tone_detect(dsp, &dsp->ced_tone_state, shortdata, len)) {
 			fax_digit = 'e';
 		}
+
+		if ((dsp->faxmode & DSP_FAXMODE_DETECT_V21) && v21_detect(dsp, &dsp->v21_state, shortdata, len)) {
+			fax_digit = 'g';
+		}
 	}
 
 	if (dsp->features & (DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_BUSY_DETECT)) {
@@ -1640,9 +1791,9 @@
 int ast_dsp_set_faxmode(struct ast_dsp *dsp, int faxmode)
 {
 	if (dsp->faxmode != faxmode) {
+		dsp->faxmode = faxmode;
 		ast_fax_detect_init(dsp);
 	}
-	dsp->faxmode = faxmode;
 	return 0;
 }
 

Modified: team/irroot/distrotech-customers-1.8/res/res_fax.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers-1.8/res/res_fax.c?view=diff&rev=329328&r1=329327&r2=329328
==============================================================================
--- team/irroot/distrotech-customers-1.8/res/res_fax.c (original)
+++ team/irroot/distrotech-customers-1.8/res/res_fax.c Fri Jul 22 15:03:04 2011
@@ -274,6 +274,8 @@
 	int framehook;
 	/*! \brief bridged */
 	int bridged:1;
+	/*! \brief 1 if a v21 preamble has been detected */
+	int detected_v21:1;
 	/*! \brief a flag to track the state of our negotiation */
 	enum ast_t38_state t38_state;
 	/*! \brief Original audio formats */
@@ -799,7 +801,7 @@
 	}
 }
 
-/*! \5Bbrief Release a session token.
+/*! \brief Release a session token.
  * \param s a session returned from fax_session_reserve()
  * \param token a token generated from fax_session_reserve()
  *
@@ -2499,10 +2501,10 @@
 	gateway->framehook = -1;
 
 	ast_dsp_set_features(gateway->chan_dsp, DSP_FEATURE_FAX_DETECT);
-	ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_CED);
+	ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_V21);
 
 	ast_dsp_set_features(gateway->peer_dsp, DSP_FEATURE_FAX_DETECT);
-	ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_CED);
+	ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_V21);
 
 	details->caps = AST_FAX_TECH_GATEWAY;
 	if (!(gateway->s = fax_session_reserve(details, &gateway->token))) {
@@ -2557,12 +2559,53 @@
 	return 0;
 }
 
-static struct ast_frame *fax_gateway_detect_ced(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
+static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_frame *f)
+{
+	struct ast_frame *fp;
+	struct ast_control_t38_parameters t38_parameters = {
+		.request_response = AST_T38_REQUEST_NEGOTIATE,
+	};
+	struct ast_frame control_frame = {
+		.src = "res_fax",
+		.frametype = AST_FRAME_CONTROL,
+		.datalen = sizeof(t38_parameters),
+		.subclass.integer = AST_CONTROL_T38_PARAMETERS,
+		.data.ptr = &t38_parameters,
+	};
+
+	struct ast_fax_session_details *details = find_details(chan);
+
+	if (!details) {
+		ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name);
+		ast_framehook_detach(chan, gateway->framehook);
+		return f;
+	}
+
+	t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
+	ao2_ref(details, -1);
+
+	if (!(fp = ast_frisolate(&control_frame))) {
+		ast_log(LOG_ERROR, "error generating T.38 request control frame on chan %s for T.38 gateway session\n", chan->name);
+		return f;
+	}
+
+	gateway->t38_state = T38_STATE_NEGOTIATING;
+	gateway->timeout_start = ast_tvnow();
+
+	ast_debug(1, "requesting T.38 for gateway session for %s\n", chan->name);
+	return fp;
+}
+
+static struct ast_frame *fax_gateway_detect_v21(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
 {
 	struct ast_frame *dfr = ast_frdup(f);
 	struct ast_dsp *active_dsp = (active == chan) ? gateway->chan_dsp : gateway->peer_dsp;
 	struct ast_channel *other = (active == chan) ? peer : chan;
 
+	if (gateway->detected_v21) {
+		return f;
+	}
+
 	if (!dfr) {
 		return f;
 	}
@@ -2571,43 +2614,13 @@
 		return f;
 	}
 
-	if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'e') {
+	if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'g') {
+		gateway->detected_v21 = 1;
 		if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) {
-			struct ast_control_t38_parameters t38_parameters = {
-				.request_response = AST_T38_REQUEST_NEGOTIATE,
-			};
-			struct ast_frame control_frame = {
-				.src = "res_fax",
-				.frametype = AST_FRAME_CONTROL,
-				.datalen = sizeof(t38_parameters),
-				.subclass.integer = AST_CONTROL_T38_PARAMETERS,
-				.data.ptr = &t38_parameters,
-			};
-
-			struct ast_fax_session_details *details = find_details(chan);
-			ast_frfree(dfr);
-
-			if (!details) {
-				ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name);
-				ast_framehook_detach(chan, gateway->framehook);
-				return f;
-			}
-
-			t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
-			ao2_ref(details, -1);
-
-			if (!(dfr = ast_frisolate(&control_frame))) {
-				ast_log(LOG_ERROR, "error generating T.38 request control frame on chan %s for T.38 gateway session\n", chan->name);
-				return f;
-			}
-
-			gateway->t38_state = T38_STATE_NEGOTIATING;
-			gateway->timeout_start = ast_tvnow();
-
-			ast_debug(1, "detected CED tone on %s, requesting T.38 on %s for T.38 gateway session\n", active->name, other->name);
-			return dfr;
+			ast_debug(1, "detected v21 preamble from %s\n", active->name);
+			return fax_gateway_request_t38(gateway, chan, f);
 		} else {
-			ast_debug(1, "detected CED tone on %s, but %s does not support T.38 for T.38 gateway session\n", active->name, other->name);
+			ast_debug(1, "detected v21 preamble on %s, but %s does not support T.38 for T.38 gateway session\n", active->name, other->name);
 		}
 	}
 
@@ -2660,6 +2673,7 @@
 
 	if (control_params->request_response == AST_T38_REQUEST_NEGOTIATE) {
 		enum ast_t38_state state = ast_channel_get_t38_state(other);
+
 		if (state == T38_STATE_UNKNOWN) {
 			/* we detected a request to negotiate T.38 and the
 			 * other channel appears to support T.38, we'll pass
@@ -2699,7 +2713,7 @@
 			return &ast_null_frame;
 		} else if (gateway->t38_state == T38_STATE_NEGOTIATING) {
 			/* we got a request to negotiate T.38 after we already
-			 * sent one to the other party based on CED tone
+			 * sent one to the other party based on v21 preamble
 			 * detection. We'll just pretend we passed this request
 			 * through in the first place. */
 
@@ -2707,12 +2721,12 @@
 			gateway->t38_state = T38_STATE_UNKNOWN;
 			gateway->timeout_start = ast_tvnow();
 
-			ast_debug(1, "%s is attempting to negotiate T.38 after we already sent a negotiation request based on CED detection\n", active->name);
+			ast_debug(1, "%s is attempting to negotiate T.38 after we already sent a negotiation request based on v21 preamble detection\n", active->name);
 			ao2_ref(details, -1);
 			return &ast_null_frame;
 		} else if (gateway->t38_state == T38_STATE_NEGOTIATED) {
 			/* we got a request to negotiate T.38 after we already
-			 * sent one to the other party based on CED tone
+			 * sent one to the other party based on v21 preamble
 			 * detection and received a response. We need to
 			 * respond to this and shut down the gateway. */
 
@@ -2933,7 +2947,8 @@
 
 		gateway->timeout_start = ast_tvnow();
 
-		/* we are bridged, change r/w formats to SLIN for CED detection and T.30 */
+		/* we are bridged, change r/w formats to SLIN for v21 preamble
+		 * detection and T.30 */
 		gateway->chan_read_format = chan->readformat;
 		gateway->chan_write_format = chan->readformat;
 
@@ -3008,16 +3023,16 @@
 		return fax_gateway_detect_t38(gateway, chan, peer, active, f);
 	}
 
-	/* not in gateway mode yet, listen for CED */
-	/* XXX this should detect a v21 preamble instead of CED */
-	if (gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) {
-		return fax_gateway_detect_ced(gateway, chan, peer, active, f);
+	if (!gateway->detected_v21 && gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) {
+		/* not in gateway mode and have not detected v21 yet, listen
+		 * for v21 */
+		return fax_gateway_detect_v21(gateway, chan, peer, active, f);
 	}
 
 	/* in gateway mode, gateway some packets */
 	if (gateway->t38_state == T38_STATE_NEGOTIATED) {
 		/* framehooks are called in __ast_read() before frame format
-		 * translation is does, so we need to translate here */
+		 * translation is done, so we need to translate here */
 		if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.codec != AST_FORMAT_SLINEAR)) {
 			if (active->readtrans && (f = ast_translate(active->readtrans, f, 1)) == NULL) {
 				f = &ast_null_frame;
@@ -3032,6 +3047,25 @@
 		gateway->s->tech->write(gateway->s, f);
 		f = &ast_null_frame;
 		return f;
+	}
+
+	/* force silence on the line if T.38 negotiation might be taking place */
+	if (gateway->t38_state != T38_STATE_UNAVAILABLE && gateway->t38_state != T38_STATE_REJECTED) {
+		if (f->frametype == AST_FRAME_VOICE && f->subclass.format.id == AST_FORMAT_SLINEAR) {
+			short silence_buf[f->samples];
+			struct ast_frame silence_frame = {
+				.frametype = AST_FRAME_VOICE,
+				.data.ptr = silence_buf,
+				.samples = f->samples,
+				.datalen = sizeof(silence_buf),
+			};
+			ast_format_set(&silence_frame.subclass.format, AST_FORMAT_SLINEAR, 0);
+			memset(silence_buf, 0, sizeof(silence_buf));
+
+			return ast_frisolate(&silence_frame);
+		} else {
+			return &ast_null_frame;
+		}
 	}
 
 	return f;
@@ -3132,8 +3166,6 @@
 			f = ast_dsp_process(chan, dsp, f);
 			if ((f->frametype ==  AST_FRAME_DTMF) && (f->subclass.integer == 'f')) {
 				res = 1;
-				ast_dsp_free(dsp);
-				dsp = NULL;
 			} else if ((f->frametype == AST_FRAME_VOICE) && (noiselim > 0)) {
 				ast_dsp_noise(dsp, f, &dspnoise);
 				if (dspnoise > noiselim) {
@@ -3188,8 +3220,8 @@
 static int waitfax_exec(struct ast_channel *chan, const char *data)
 {
 	int res = 0;
-	int timeout = 0;
-	int noiselim = 0;
+	unsigned int timeout;
+	unsigned int noiselim;
 	int ptres = -1;
 	char *parse;
 	struct ast_fax_session_details *details;
@@ -3210,12 +3242,10 @@
 	parse = ast_strdupa(data);
 	AST_STANDARD_APP_ARGS(args, parse);
 
-	if (args.timeout) {
-		timeout = atoi(args.timeout) * 1000;
-	}
-
-	if (args.noise) {
-		noiselim = atoi(args.noise);
+	if (!ast_strlen_zero(args.timeout) && sscanf(args.timeout, "%u", &timeout) == 1) {
+		timeout = timeout * 1000;
+	} else {
+		timeout = 0;
 	}
 
 	if (timeout <= 0) {
@@ -3224,6 +3254,10 @@
 		ast_log(LOG_ERROR, "Application WaitFAX requires a valid timeout\n");
 		ao2_ref(details, -1);
 		return 0;
+	}
+
+	if (ast_strlen_zero(args.noise) || sscanf(args.noise, "%u", &noiselim) != 1) {
+		noiselim = 0;
 	}
 
 	if (chan->_state != AST_STATE_UP) {




More information about the asterisk-commits mailing list