diff --git a/gst/dtmf/Makefile.am b/gst/dtmf/Makefile.am index 3bdabbb438..cbb351c9dc 100644 --- a/gst/dtmf/Makefile.am +++ b/gst/dtmf/Makefile.am @@ -1,14 +1,18 @@ plugin_LTLIBRARIES = libgstdtmf.la libgstdtmf_la_SOURCES = gstdtmfsrc.c \ + gstdtmfdetect.c \ gstrtpdtmfsrc.c \ gstrtpdtmfdepay.c \ + tone_detect.c \ gstdtmf.c noinst_HEADERS = gstdtmfsrc.h \ + gstdtmfdetect.h \ gstrtpdtmfsrc.h \ gstrtpdtmfdepay.h \ - gstrtpdtmfcommon.h + gstrtpdtmfcommon.h \ + tone_detect.h libgstdtmf_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(ERROR_CFLAGS) -DEXTERN_BUF -DRTP_SUPPORT libgstdtmf_la_LIBADD = $(GST_LIBS_LIBS) -lm diff --git a/gst/dtmf/gstdtmf.c b/gst/dtmf/gstdtmf.c index d50cf03fc2..9d5854fc1b 100644 --- a/gst/dtmf/gstdtmf.c +++ b/gst/dtmf/gstdtmf.c @@ -3,6 +3,7 @@ #include "config.h" #endif +#include "gstdtmfdetect.h" #include "gstdtmfsrc.h" #include "gstrtpdtmfsrc.h" #include "gstrtpdtmfdepay.h" @@ -11,13 +12,15 @@ static gboolean plugin_init (GstPlugin * plugin) { + if (!gst_dtmf_detect_plugin_init (plugin)) + return FALSE; + if (!gst_dtmf_src_plugin_init (plugin)) return FALSE; if (!gst_rtp_dtmf_src_plugin_init (plugin)) return FALSE; - if (!gst_rtp_dtmf_depay_plugin_init (plugin)) return FALSE; diff --git a/gst/dtmf/gstdtmfdetect.c b/gst/dtmf/gstdtmfdetect.c new file mode 100644 index 0000000000..5cb7c2cabc --- /dev/null +++ b/gst/dtmf/gstdtmfdetect.c @@ -0,0 +1,308 @@ +/* + * GStreamer - DTMF Detection + * + * Copyright 2009 Nokia Corporation + * Copyright 2009 Collabora Ltd, + * @author: Olivier Crete + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/** + * SECTION:element-dtmfdetect + * @short_description: Detects DTMF tones + * + * This element will detect DTMF tones and emit messages + * + * The message is called "dtmf-event" and has the following fields + * + * + * + * + * + * + * + * + * Name + * GType + * Possible values + * Purpose + * + * + * + * + * type + * G_TYPE_INT + * 0-1 + * The application uses this field to specify which of the two methods + * specified in RFC 2833 to use. The value should be 0 for tones and 1 for + * named events. Tones are specified by their frequencies and events are specied + * by their number. This element can only take events as input. Do not confuse + * with "method" which specified the output. + * + * + * + * number + * G_TYPE_INT + * 0-16 + * The event number. + * + * + * method + * G_TYPE_INT + * 2 + * This field will always been 2 (ie sound) from this element. + * + * + * + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstdtmfdetect.h" + +#include + +GST_DEBUG_CATEGORY (dtmf_detect_debug); +#define GST_CAT_DEFAULT (dtmf_detect_debug) + +/* elementfactory information */ +static const GstElementDetails gst_dtmf_detect_details = +GST_ELEMENT_DETAILS ("DTMF detector element", + "Detect", + "This element detects DTMF tones", + "Olivier Crete "); + + +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "width = (int) 16, " + "depth = (int) 16, " + "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", " + "signed = (bool) true, rate = (int) [1, MAX], channels = (int) 1")); + +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "width = (int) 16, " + "depth = (int) 16, " + "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", " + "signed = (bool) true, rate = (int) [1, MAX], channels = (int) 1")); + +/* signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, +}; + +static gboolean gst_dtmf_detect_set_caps (GstBaseTransform * trans, + GstCaps * incaps, GstCaps * outcaps); +static GstFlowReturn gst_dtmf_detect_transform_ip (GstBaseTransform * trans, + GstBuffer * buf); +static gboolean gst_dtmf_detect_event (GstBaseTransform * trans, + GstEvent * event); + +static void +_do_init (GType type) +{ + GST_DEBUG_CATEGORY_INIT (dtmf_detect_debug, "dtmfdetect", 0, "dtmfdetect"); +} + +GST_BOILERPLATE_FULL (GstDtmfDetect, gst_dtmf_detect, GstBaseTransform, + GST_TYPE_BASE_TRANSFORM, _do_init); + +static void +gst_dtmf_detect_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&srctemplate)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sinktemplate)); + + gst_element_class_set_details (element_class, &gst_dtmf_detect_details); +} + +static void +gst_dtmf_detect_class_init (GstDtmfDetectClass * klass) +{ + GstBaseTransformClass *gstbasetransform_class; + + gstbasetransform_class = (GstBaseTransformClass *) klass; + + gstbasetransform_class->set_caps = + GST_DEBUG_FUNCPTR (gst_dtmf_detect_set_caps); + gstbasetransform_class->transform_ip = + GST_DEBUG_FUNCPTR (gst_dtmf_detect_transform_ip); + gstbasetransform_class->event = GST_DEBUG_FUNCPTR (gst_dtmf_detect_event); +} + +static void +gst_dtmf_detect_init (GstDtmfDetect * dtmfdetect, GstDtmfDetectClass * klass) +{ + gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (dtmfdetect), TRUE); + gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (dtmfdetect), TRUE); +} + +static gboolean +gst_dtmf_detect_set_caps (GstBaseTransform * trans, GstCaps * incaps, + GstCaps * outcaps) +{ + GstDtmfDetect *self = GST_DTMF_DETECT (trans); + GstStructure *s = gst_caps_get_structure (incaps, 0); + + if (!gst_structure_get_int (s, "rate", &self->rate)) + return FALSE; + + zap_dtmf_detect_init (&self->dtmf_state); + + return TRUE; +} + + +static GstFlowReturn +gst_dtmf_detect_transform_ip (GstBaseTransform * trans, GstBuffer * buf) +{ + GstDtmfDetect *self = GST_DTMF_DETECT (trans); + int dtmf_count; + char dtmfbuf[MAX_DTMF_DIGITS] = ""; + int i; + + if (GST_BUFFER_IS_DISCONT (buf)) + zap_dtmf_detect_init (&self->dtmf_state); + + + zap_dtmf_detect (&self->dtmf_state, (int16_t *) GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf) / 2, FALSE); + + dtmf_count = zap_dtmf_get (&self->dtmf_state, dtmfbuf, MAX_DTMF_DIGITS); + + if (dtmf_count) + GST_DEBUG_OBJECT (self, "Got %d DTMF events: %s", dtmf_count, dtmfbuf); + else + GST_LOG_OBJECT (self, "Got no DTMF events"); + + for (i = 0; i < dtmf_count; i++) { + GstMessage *dtmf_message = NULL; + GstStructure *structure; + gint dtmf_payload_event; + + GST_DEBUG_OBJECT (self, "Got DTMF event %c", dtmfbuf[i]); + + switch (dtmfbuf[i]) { + case '0': + dtmf_payload_event = 0; + break; + case '1': + dtmf_payload_event = 1; + break; + case '2': + dtmf_payload_event = 2; + break; + case '3': + dtmf_payload_event = 3; + break; + case '4': + dtmf_payload_event = 4; + break; + case '5': + dtmf_payload_event = 5; + break; + case '6': + dtmf_payload_event = 6; + break; + case '7': + dtmf_payload_event = 7; + break; + case '8': + dtmf_payload_event = 8; + break; + case '9': + dtmf_payload_event = 9; + break; + case '*': + dtmf_payload_event = 10; + break; + case '#': + dtmf_payload_event = 11; + break; + case 'A': + dtmf_payload_event = 12; + break; + case 'B': + dtmf_payload_event = 13; + break; + case 'C': + dtmf_payload_event = 14; + break; + case 'D': + dtmf_payload_event = 15; + break; + default: + continue; + } + + structure = gst_structure_new ("dtmf-event", + "type", G_TYPE_INT, 1, + "number", G_TYPE_INT, dtmf_payload_event, + "method", G_TYPE_INT, 2, NULL); + dtmf_message = gst_message_new_element (GST_OBJECT (self), structure); + gst_element_post_message (GST_ELEMENT (self), dtmf_message); + } + + return GST_FLOW_OK; +} + + +static gboolean +gst_dtmf_detect_event (GstBaseTransform * trans, GstEvent * event) +{ + GstDtmfDetect *self = GST_DTMF_DETECT (trans); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + zap_dtmf_detect_init (&self->dtmf_state); + break; + default: + break; + } + + return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_TRANSFORM_CLASS, event, + (trans, event), TRUE); +} + + +gboolean +gst_dtmf_detect_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "dtmfdetect", + GST_RANK_MARGINAL, GST_TYPE_DTMF_DETECT); +} diff --git a/gst/dtmf/gstdtmfdetect.h b/gst/dtmf/gstdtmfdetect.h new file mode 100644 index 0000000000..b3bfab94ae --- /dev/null +++ b/gst/dtmf/gstdtmfdetect.h @@ -0,0 +1,73 @@ +/* + * GStreamer - DTMF Detection + * + * Copyright 2009 Nokia Corporation + * Copyright 2009 Collabora Ltd, + * @author: Olivier Crete + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef __GST_DTMF_DETECT_H__ +#define __GST_DTMF_DETECT_H__ + +#include +#include + +#include "tone_detect.h" + +G_BEGIN_DECLS + +/* #define's don't like whitespacey bits */ +#define GST_TYPE_DTMF_DETECT \ + (gst_dtmf_detect_get_type()) +#define GST_DTMF_DETECT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GST_TYPE_DTMF_DETECT,GstDtmfDetect)) +#define GST_DTMF_DETECT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + GST_TYPE_DTMF_DETECT,GstDtmfDetectClass)) +#define GST_IS_DTMF_DETECT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DTMF_DETECT)) +#define GST_IS_DTMF_DETECT_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DTMF_DETECT)) + +typedef struct _GstDtmfDetect GstDtmfDetect; +typedef struct _GstDtmfDetectClass GstDtmfDetectClass; +typedef struct _GstDtmfDetectPrivate GstDtmfDetectPrivate; + +struct _GstDtmfDetect +{ + GstBaseTransform parent; + + gint rate; + + dtmf_detect_state_t dtmf_state; +}; + +struct _GstDtmfDetectClass +{ + GstBaseTransformClass parent_class; +}; + +GType gst_dtmf_detect_get_type (void); + +gboolean gst_dtmf_detect_plugin_init (GstPlugin *plugin); + +G_END_DECLS + +#endif /* __GST_DTMF_DETECT_H__ */ diff --git a/gst/dtmf/tone_detect.c b/gst/dtmf/tone_detect.c new file mode 100644 index 0000000000..4785e514fe --- /dev/null +++ b/gst/dtmf/tone_detect.c @@ -0,0 +1,517 @@ +/* + * DTMF Receiver module, part of: + * BSD Telephony Of Mexico "Zapata" Telecom Library, version 1.10 12/9/01 + * + * Part of the "Zapata" Computer Telephony Technology. + * + * See http://www.bsdtelephony.com.mx + * + * + * The technologies, software, hardware, designs, drawings, scheumatics, board + * layouts and/or artwork, concepts, methodologies (including the use of all + * of these, and that which is derived from the use of all of these), all other + * intellectual properties contained herein, and all intellectual property + * rights have been and shall continue to be expressly for the benefit of all + * mankind, and are perpetually placed in the public domain, and may be used, + * copied, and/or modified by anyone, in any manner, for any legal purpose, + * without restriction. + * + * This module written by Stephen Underwood. + */ + +/* + tone_detect.c - General telephony tone detection, and specific + detection of DTMF. + + Copyright (C) 2001 Steve Underwood + + Despite my general liking of the GPL, I place this code in the + public domain for the benefit of all mankind - even the slimy + ones who might try to proprietize my work and use it to my + detriment. +*/ + +#include +#include +#include +#include +#include +#include +#include "tone_detect.h" + +#define FALSE 0 +#define TRUE (!FALSE) + +//#define USE_3DNOW + +/* Basic DTMF specs: + * + * Minimum tone on = 40ms + * Minimum tone off = 50ms + * Maximum digit rate = 10 per second + * Normal twist <= 8dB accepted + * Reverse twist <= 4dB accepted + * S/N >= 15dB will detect OK + * Attenuation <= 26dB will detect OK + * Frequency tolerance +- 1.5% will detect, +-3.5% will reject + */ + +#define SAMPLE_RATE 8000.0 + +#define DTMF_THRESHOLD 8.0e7 +#define FAX_THRESHOLD 8.0e7 +#define FAX_2ND_HARMONIC 2.0 /* 4dB */ +#define DTMF_NORMAL_TWIST 6.3 /* 8dB */ +#define DTMF_REVERSE_TWIST ((isradio) ? 4.0 : 2.5) /* 4dB normal */ +#define DTMF_RELATIVE_PEAK_ROW 6.3 /* 8dB */ +#define DTMF_RELATIVE_PEAK_COL 6.3 /* 8dB */ +#define DTMF_2ND_HARMONIC_ROW ((isradio) ? 1.7 : 2.5) /* 4dB normal */ +#define DTMF_2ND_HARMONIC_COL 63.1 /* 18dB */ + +static tone_detection_descriptor_t dtmf_detect_row[4]; +static tone_detection_descriptor_t dtmf_detect_col[4]; +static tone_detection_descriptor_t dtmf_detect_row_2nd[4]; +static tone_detection_descriptor_t dtmf_detect_col_2nd[4]; +static tone_detection_descriptor_t fax_detect; +static tone_detection_descriptor_t fax_detect_2nd; + +static float dtmf_row[] = +{ + 697.0, 770.0, 852.0, 941.0 +}; +static float dtmf_col[] = +{ + 1209.0, 1336.0, 1477.0, 1633.0 +}; + +static float fax_freq = 1100.0; + +static char dtmf_positions[] = "123A" "456B" "789C" "*0#D"; + +static void goertzel_init(goertzel_state_t *s, + tone_detection_descriptor_t *t) +{ + s->v2 = + s->v3 = 0.0; + s->fac = t->fac; +} +/*- End of function --------------------------------------------------------*/ + +#if defined(USE_3DNOW) +static inline void _dtmf_goertzel_update(goertzel_state_t *s, + float x[], + int samples) +{ + int n; + float v; + int i; + float vv[16]; + + vv[4] = s[0].v2; + vv[5] = s[1].v2; + vv[6] = s[2].v2; + vv[7] = s[3].v2; + vv[8] = s[0].v3; + vv[9] = s[1].v3; + vv[10] = s[2].v3; + vv[11] = s[3].v3; + vv[12] = s[0].fac; + vv[13] = s[1].fac; + vv[14] = s[2].fac; + vv[15] = s[3].fac; + + //v1 = s->v2; + //s->v2 = s->v3; + //s->v3 = s->fac*s->v2 - v1 + x[0]; + + __asm__ __volatile__ ( + " femms;\n" + + " movq 16(%%edx),%%mm2;\n" + " movq 24(%%edx),%%mm3;\n" + " movq 32(%%edx),%%mm4;\n" + " movq 40(%%edx),%%mm5;\n" + " movq 48(%%edx),%%mm6;\n" + " movq 56(%%edx),%%mm7;\n" + + " jmp 1f;\n" + " .align 32;\n" + + " 1: ;\n" + " prefetch (%%eax);\n" + " movq %%mm3,%%mm1;\n" + " movq %%mm2,%%mm0;\n" + " movq %%mm5,%%mm3;\n" + " movq %%mm4,%%mm2;\n" + + " pfmul %%mm7,%%mm5;\n" + " pfmul %%mm6,%%mm4;\n" + " pfsub %%mm1,%%mm5;\n" + " pfsub %%mm0,%%mm4;\n" + + " movq (%%eax),%%mm0;\n" + " movq %%mm0,%%mm1;\n" + " punpckldq %%mm0,%%mm1;\n" + " add $4,%%eax;\n" + " pfadd %%mm1,%%mm5;\n" + " pfadd %%mm1,%%mm4;\n" + + " dec %%ecx;\n" + + " jnz 1b;\n" + + " movq %%mm2,16(%%edx);\n" + " movq %%mm3,24(%%edx);\n" + " movq %%mm4,32(%%edx);\n" + " movq %%mm5,40(%%edx);\n" + + " femms;\n" + : + : "c" (samples), "a" (x), "d" (vv) + : "memory", "eax", "ecx"); + + s[0].v2 = vv[4]; + s[1].v2 = vv[5]; + s[2].v2 = vv[6]; + s[3].v2 = vv[7]; + s[0].v3 = vv[8]; + s[1].v3 = vv[9]; + s[2].v3 = vv[10]; + s[3].v3 = vv[11]; +} +#endif +/*- End of function --------------------------------------------------------*/ + +void zap_goertzel_update(goertzel_state_t *s, + int16_t x[], + int samples) +{ + int i; + float v1; + + for (i = 0; i < samples; i++) + { + v1 = s->v2; + s->v2 = s->v3; + s->v3 = s->fac*s->v2 - v1 + x[i]; + } +} +/*- End of function --------------------------------------------------------*/ + +float zap_goertzel_result (goertzel_state_t *s) +{ + return s->v3*s->v3 + s->v2*s->v2 - s->v2*s->v3*s->fac; +} +/*- End of function --------------------------------------------------------*/ + +void zap_dtmf_detect_init (dtmf_detect_state_t *s) +{ + int i; + float theta; + + s->hit1 = + s->hit2 = 0; + + for (i = 0; i < 4; i++) + { + theta = 2.0*M_PI*(dtmf_row[i]/SAMPLE_RATE); + dtmf_detect_row[i].fac = 2.0*cos(theta); + + theta = 2.0*M_PI*(dtmf_col[i]/SAMPLE_RATE); + dtmf_detect_col[i].fac = 2.0*cos(theta); + + theta = 2.0*M_PI*(dtmf_row[i]*2.0/SAMPLE_RATE); + dtmf_detect_row_2nd[i].fac = 2.0*cos(theta); + + theta = 2.0*M_PI*(dtmf_col[i]*2.0/SAMPLE_RATE); + dtmf_detect_col_2nd[i].fac = 2.0*cos(theta); + + goertzel_init (&s->row_out[i], &dtmf_detect_row[i]); + goertzel_init (&s->col_out[i], &dtmf_detect_col[i]); + goertzel_init (&s->row_out2nd[i], &dtmf_detect_row_2nd[i]); + goertzel_init (&s->col_out2nd[i], &dtmf_detect_col_2nd[i]); + + s->energy = 0.0; + } + + /* Same for the fax dector */ + theta = 2.0*M_PI*(fax_freq/SAMPLE_RATE); + fax_detect.fac = 2.0 * cos(theta); + goertzel_init (&s->fax_tone, &fax_detect); + + /* Same for the fax dector 2nd harmonic */ + theta = 2.0*M_PI*(fax_freq * 2.0/SAMPLE_RATE); + fax_detect_2nd.fac = 2.0 * cos(theta); + goertzel_init (&s->fax_tone2nd, &fax_detect_2nd); + + s->current_sample = 0; + s->detected_digits = 0; + s->lost_digits = 0; + s->digits[0] = '\0'; + s->mhit = 0; +} +/*- End of function --------------------------------------------------------*/ + +int zap_dtmf_detect (dtmf_detect_state_t *s, + int16_t amp[], + int samples, + int isradio) +{ + + float row_energy[4]; + float col_energy[4]; + float fax_energy; + float fax_energy_2nd; + float famp; + float v1; + int i; + int j; + int sample; + int best_row; + int best_col; + int hit; + int limit; + + hit = 0; + for (sample = 0; sample < samples; sample = limit) + { + /* 102 is optimised to meet the DTMF specs. */ + if ((samples - sample) >= (102 - s->current_sample)) + limit = sample + (102 - s->current_sample); + else + limit = samples; +#if defined(USE_3DNOW) + _dtmf_goertzel_update (s->row_out, amp + sample, limit - sample); + _dtmf_goertzel_update (s->col_out, amp + sample, limit - sample); + _dtmf_goertzel_update (s->row_out2nd, amp + sample, limit2 - sample); + _dtmf_goertzel_update (s->col_out2nd, amp + sample, limit2 - sample); + /* XXX Need to fax detect for 3dnow too XXX */ + #warning "Fax Support Broken" +#else + /* The following unrolled loop takes only 35% (rough estimate) of the + time of a rolled loop on the machine on which it was developed */ + for (j = sample; j < limit; j++) + { + famp = amp[j]; + + s->energy += famp*famp; + + /* With GCC 2.95, the following unrolled code seems to take about 35% + (rough estimate) as long as a neat little 0-3 loop */ + v1 = s->row_out[0].v2; + s->row_out[0].v2 = s->row_out[0].v3; + s->row_out[0].v3 = s->row_out[0].fac*s->row_out[0].v2 - v1 + famp; + + v1 = s->col_out[0].v2; + s->col_out[0].v2 = s->col_out[0].v3; + s->col_out[0].v3 = s->col_out[0].fac*s->col_out[0].v2 - v1 + famp; + + v1 = s->row_out[1].v2; + s->row_out[1].v2 = s->row_out[1].v3; + s->row_out[1].v3 = s->row_out[1].fac*s->row_out[1].v2 - v1 + famp; + + v1 = s->col_out[1].v2; + s->col_out[1].v2 = s->col_out[1].v3; + s->col_out[1].v3 = s->col_out[1].fac*s->col_out[1].v2 - v1 + famp; + + v1 = s->row_out[2].v2; + s->row_out[2].v2 = s->row_out[2].v3; + s->row_out[2].v3 = s->row_out[2].fac*s->row_out[2].v2 - v1 + famp; + + v1 = s->col_out[2].v2; + s->col_out[2].v2 = s->col_out[2].v3; + s->col_out[2].v3 = s->col_out[2].fac*s->col_out[2].v2 - v1 + famp; + + v1 = s->row_out[3].v2; + s->row_out[3].v2 = s->row_out[3].v3; + s->row_out[3].v3 = s->row_out[3].fac*s->row_out[3].v2 - v1 + famp; + + v1 = s->col_out[3].v2; + s->col_out[3].v2 = s->col_out[3].v3; + s->col_out[3].v3 = s->col_out[3].fac*s->col_out[3].v2 - v1 + famp; + + v1 = s->col_out2nd[0].v2; + s->col_out2nd[0].v2 = s->col_out2nd[0].v3; + s->col_out2nd[0].v3 = s->col_out2nd[0].fac*s->col_out2nd[0].v2 - v1 + famp; + + v1 = s->row_out2nd[0].v2; + s->row_out2nd[0].v2 = s->row_out2nd[0].v3; + s->row_out2nd[0].v3 = s->row_out2nd[0].fac*s->row_out2nd[0].v2 - v1 + famp; + + v1 = s->col_out2nd[1].v2; + s->col_out2nd[1].v2 = s->col_out2nd[1].v3; + s->col_out2nd[1].v3 = s->col_out2nd[1].fac*s->col_out2nd[1].v2 - v1 + famp; + + v1 = s->row_out2nd[1].v2; + s->row_out2nd[1].v2 = s->row_out2nd[1].v3; + s->row_out2nd[1].v3 = s->row_out2nd[1].fac*s->row_out2nd[1].v2 - v1 + famp; + + v1 = s->col_out2nd[2].v2; + s->col_out2nd[2].v2 = s->col_out2nd[2].v3; + s->col_out2nd[2].v3 = s->col_out2nd[2].fac*s->col_out2nd[2].v2 - v1 + famp; + + v1 = s->row_out2nd[2].v2; + s->row_out2nd[2].v2 = s->row_out2nd[2].v3; + s->row_out2nd[2].v3 = s->row_out2nd[2].fac*s->row_out2nd[2].v2 - v1 + famp; + + v1 = s->col_out2nd[3].v2; + s->col_out2nd[3].v2 = s->col_out2nd[3].v3; + s->col_out2nd[3].v3 = s->col_out2nd[3].fac*s->col_out2nd[3].v2 - v1 + famp; + + v1 = s->row_out2nd[3].v2; + s->row_out2nd[3].v2 = s->row_out2nd[3].v3; + s->row_out2nd[3].v3 = s->row_out2nd[3].fac*s->row_out2nd[3].v2 - v1 + famp; + + /* Update fax tone */ + v1 = s->fax_tone.v2; + s->fax_tone.v2 = s->fax_tone.v3; + s->fax_tone.v3 = s->fax_tone.fac*s->fax_tone.v2 - v1 + famp; + + v1 = s->fax_tone.v2; + s->fax_tone2nd.v2 = s->fax_tone2nd.v3; + s->fax_tone2nd.v3 = s->fax_tone2nd.fac*s->fax_tone2nd.v2 - v1 + famp; + } +#endif + s->current_sample += (limit - sample); + if (s->current_sample < 102) + continue; + + /* Detect the fax energy, too */ + fax_energy = zap_goertzel_result(&s->fax_tone); + + /* We are at the end of a DTMF detection block */ + /* Find the peak row and the peak column */ + row_energy[0] = zap_goertzel_result (&s->row_out[0]); + col_energy[0] = zap_goertzel_result (&s->col_out[0]); + + for (best_row = best_col = 0, i = 1; i < 4; i++) + { + row_energy[i] = zap_goertzel_result (&s->row_out[i]); + if (row_energy[i] > row_energy[best_row]) + best_row = i; + col_energy[i] = zap_goertzel_result (&s->col_out[i]); + if (col_energy[i] > col_energy[best_col]) + best_col = i; + } + hit = 0; + /* Basic signal level test and the twist test */ + if (row_energy[best_row] >= DTMF_THRESHOLD + && + col_energy[best_col] >= DTMF_THRESHOLD + && + col_energy[best_col] < row_energy[best_row]*DTMF_REVERSE_TWIST + && + col_energy[best_col]*DTMF_NORMAL_TWIST > row_energy[best_row]) + { + /* Relative peak test */ + for (i = 0; i < 4; i++) + { + if ((i != best_col && col_energy[i]*DTMF_RELATIVE_PEAK_COL > col_energy[best_col]) + || + (i != best_row && row_energy[i]*DTMF_RELATIVE_PEAK_ROW > row_energy[best_row])) + { + break; + } + } + /* ... and second harmonic test */ + if (i >= 4 + && + (row_energy[best_row] + col_energy[best_col]) > 42.0*s->energy + && + zap_goertzel_result (&s->col_out2nd[best_col])*DTMF_2ND_HARMONIC_COL < col_energy[best_col] + && + zap_goertzel_result (&s->row_out2nd[best_row])*DTMF_2ND_HARMONIC_ROW < row_energy[best_row]) + { + hit = dtmf_positions[(best_row << 2) + best_col]; + /* Look for two successive similar results */ + /* The logic in the next test is: + We need two successive identical clean detects, with + something different preceeding it. This can work with + back to back differing digits. More importantly, it + can work with nasty phones that give a very wobbly start + to a digit. */ + if (hit == s->hit3 && s->hit3 != s->hit2) + { + s->mhit = hit; + s->digit_hits[(best_row << 2) + best_col]++; + s->detected_digits++; + if (s->current_digits < MAX_DTMF_DIGITS) + { + s->digits[s->current_digits++] = hit; + s->digits[s->current_digits] = '\0'; + } + else + { + s->lost_digits++; + } + } + } + } + if (!hit && (fax_energy >= FAX_THRESHOLD) && (fax_energy > s->energy * 21.0)) { + fax_energy_2nd = zap_goertzel_result(&s->fax_tone2nd); + if (fax_energy_2nd * FAX_2ND_HARMONIC < fax_energy) { +#if 0 + printf("Fax energy/Second Harmonic: %f/%f\n", fax_energy, fax_energy_2nd); +#endif + /* XXX Probably need better checking than just this the energy XXX */ + hit = 'f'; + s->fax_hits++; + } /* Don't reset fax hits counter */ + } else { + if (s->fax_hits > 5) { + s->mhit = 'f'; + s->detected_digits++; + if (s->current_digits < MAX_DTMF_DIGITS) + { + s->digits[s->current_digits++] = hit; + s->digits[s->current_digits] = '\0'; + } + else + { + s->lost_digits++; + } + } + s->fax_hits = 0; + } + s->hit1 = s->hit2; + s->hit2 = s->hit3; + s->hit3 = hit; + /* Reinitialise the detector for the next block */ + for (i = 0; i < 4; i++) + { + goertzel_init (&s->row_out[i], &dtmf_detect_row[i]); + goertzel_init (&s->col_out[i], &dtmf_detect_col[i]); + goertzel_init (&s->row_out2nd[i], &dtmf_detect_row_2nd[i]); + goertzel_init (&s->col_out2nd[i], &dtmf_detect_col_2nd[i]); + } + goertzel_init (&s->fax_tone, &fax_detect); + goertzel_init (&s->fax_tone2nd, &fax_detect_2nd); + s->energy = 0.0; + s->current_sample = 0; + } + if ((!s->mhit) || (s->mhit != hit)) + { + s->mhit = 0; + return(0); + } + return (hit); +} +/*- End of function --------------------------------------------------------*/ + +int zap_dtmf_get (dtmf_detect_state_t *s, + char *buf, + int max) +{ + if (max > s->current_digits) + max = s->current_digits; + if (max > 0) + { + memcpy (buf, s->digits, max); + memmove (s->digits, s->digits + max, s->current_digits - max); + s->current_digits -= max; + } + buf[max] = '\0'; + return max; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/gst/dtmf/tone_detect.h b/gst/dtmf/tone_detect.h new file mode 100644 index 0000000000..9b11dbae30 --- /dev/null +++ b/gst/dtmf/tone_detect.h @@ -0,0 +1,86 @@ +/* + * Header file for DTMF Receiver module, part of: + * BSD Telephony Of Mexico "Zapata" Telecom Library, version 1.10 12/9/01 + * + * Part of the "Zapata" Computer Telephony Technology. + * + * See http://www.bsdtelephony.com.mx + * + * + * The technologies, software, hardware, designs, drawings, scheumatics, board + * layouts and/or artwork, concepts, methodologies (including the use of all + * of these, and that which is derived from the use of all of these), all other + * intellectual properties contained herein, and all intellectual property + * rights have been and shall continue to be expressly for the benefit of all + * mankind, and are perpetually placed in the public domain, and may be used, + * copied, and/or modified by anyone, in any manner, for any legal purpose, + * without restriction. + * + * This module written by Stephen Underwood. + */ +/* + tone_detect.h - General telephony tone detection, and specific + detection of DTMF. + + Copyright (C) 2001 Steve Underwood + + Despite my general liking of the GPL, I place this code in the + public domain for the benefit of all mankind - even the slimy + ones who might try to proprietize my work and use it to my + detriment. +*/ + +typedef struct +{ + float v2; + float v3; + float fac; +} goertzel_state_t; + +#define MAX_DTMF_DIGITS 128 + +typedef struct +{ + int hit1; + int hit2; + int hit3; + int hit4; + int mhit; + + goertzel_state_t row_out[4]; + goertzel_state_t col_out[4]; + goertzel_state_t row_out2nd[4]; + goertzel_state_t col_out2nd[4]; + goertzel_state_t fax_tone; + goertzel_state_t fax_tone2nd; + float energy; + + int current_sample; + char digits[MAX_DTMF_DIGITS + 1]; + int current_digits; + int detected_digits; + int lost_digits; + int digit_hits[16]; + int fax_hits; +} dtmf_detect_state_t; + +typedef struct +{ + float fac; +} tone_detection_descriptor_t; + +void zap_goertzel_update(goertzel_state_t *s, + int16_t x[], + int samples); +float zap_goertzel_result (goertzel_state_t *s); + +void zap_dtmf_detect_init (dtmf_detect_state_t *s); +int zap_dtmf_detect (dtmf_detect_state_t *s, + int16_t amp[], + int samples, + int isradio); +int zap_dtmf_get (dtmf_detect_state_t *s, + char *buf, + int max); + +/*- End of file ------------------------------------------------------------*/