--- /dev/null
+/*
+ * GStreamer - DTMF Detection
+ *
+ * Copyright 2009 Nokia Corporation
+ * Copyright 2009 Collabora Ltd,
+ * @author: Olivier Crete <olivier.crete@collabora.co.uk>
+ *
+ * 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
+ * <informaltable>
+ * <tgroup cols='4'>
+ * <colspec colname='Name' />
+ * <colspec colname='Type' />
+ * <colspec colname='Possible values' />
+ * <colspec colname='Purpose' />
+ * <thead>
+ * <row>
+ * <entry>Name</entry>
+ * <entry>GType</entry>
+ * <entry>Possible values</entry>
+ * <entry>Purpose</entry>
+ * </row>
+ * </thead>
+ * <tbody>
+ * <row>
+ * <entry>type</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-1</entry>
+ * <entry>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.
+ * </entry>
+ * </row>
+ * <row>
+ * <entry>number</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-16</entry>
+ * <entry>The event number.</entry>
+ * </row>
+ * <row>
+ * <entry>method</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>2</entry>
+ * <entry>This field will always been 2 (ie sound) from this element.
+ * </entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstdtmfdetect.h"
+
+#include <string.h>
+
+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 <olivier.crete@collabora.co.uk>");
+
+
+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);
+}
--- /dev/null
+/*
+ * 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 <steveu@coppice.org>
+
+ 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 <math.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <fcntl.h>
+#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 ------------------------------------------------------------*/