diff spandsp-0.0.6pre17/src/fax.c @ 4:26cd8f1ef0b1

import spandsp-0.0.6pre17
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 15:50:58 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spandsp-0.0.6pre17/src/fax.c	Fri Jun 25 15:50:58 2010 +0200
@@ -0,0 +1,656 @@
+//#define LOG_FAX_AUDIO
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * fax.c - Analogue line ITU T.30 FAX transfer processing
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2003, 2005, 2006 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: fax.c,v 1.96.4.1 2009/12/19 10:44:10 steveu Exp $
+ */
+
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#include "floating_fudge.h"
+#include <assert.h>
+#include <fcntl.h>
+#include <time.h>
+#if defined(LOG_FAX_AUDIO)
+#include <unistd.h>
+#endif
+#include <tiffio.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/logging.h"
+#include "spandsp/queue.h"
+#include "spandsp/dc_restore.h"
+#include "spandsp/power_meter.h"
+#include "spandsp/complex.h"
+#include "spandsp/tone_detect.h"
+#include "spandsp/tone_generate.h"
+#include "spandsp/async.h"
+#include "spandsp/hdlc.h"
+#include "spandsp/silence_gen.h"
+#include "spandsp/fsk.h"
+#include "spandsp/v29tx.h"
+#include "spandsp/v29rx.h"
+#include "spandsp/v27ter_tx.h"
+#include "spandsp/v27ter_rx.h"
+#include "spandsp/v17tx.h"
+#include "spandsp/v17rx.h"
+#include "spandsp/super_tone_rx.h"
+#include "spandsp/modem_connect_tones.h"
+#include "spandsp/t4_rx.h"
+#include "spandsp/t4_tx.h"
+
+#include "spandsp/t30_fcf.h"
+#include "spandsp/t35.h"
+#include "spandsp/t30.h"
+#include "spandsp/t30_api.h"
+#include "spandsp/t30_logging.h"
+
+#include "spandsp/fax_modems.h"
+#include "spandsp/fax.h"
+
+#include "spandsp/private/logging.h"
+#include "spandsp/private/silence_gen.h"
+#include "spandsp/private/fsk.h"
+#include "spandsp/private/v17tx.h"
+#include "spandsp/private/v17rx.h"
+#include "spandsp/private/v27ter_tx.h"
+#include "spandsp/private/v27ter_rx.h"
+#include "spandsp/private/v29tx.h"
+#include "spandsp/private/v29rx.h"
+#include "spandsp/private/modem_connect_tones.h"
+#include "spandsp/private/hdlc.h"
+#include "spandsp/private/fax_modems.h"
+#include "spandsp/private/t4_rx.h"
+#include "spandsp/private/t4_tx.h"
+#include "spandsp/private/t30.h"
+#include "spandsp/private/fax.h"
+
+#define HDLC_FRAMING_OK_THRESHOLD       8
+
+static void fax_send_hdlc(void *user_data, const uint8_t *msg, int len)
+{
+    fax_state_t *s;
+
+    s = (fax_state_t *) user_data;
+    
+    hdlc_tx_frame(&s->modems.hdlc_tx, msg, len);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void tone_detected(void *user_data, int tone, int level, int delay)
+{
+    t30_state_t *s;
+
+    s = (t30_state_t *) user_data;
+    span_log(&s->logging, SPAN_LOG_FLOW, "%s detected (%ddBm0)\n", modem_connect_tone_to_str(tone), level);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void hdlc_underflow_handler(void *user_data)
+{
+    t30_state_t *s;
+
+    s = (t30_state_t *) user_data;
+    t30_front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void set_rx_handler(fax_state_t *s,
+                           span_rx_handler_t *rx_handler,
+                           span_rx_fillin_handler_t *fillin_handler,
+                           void *user_data)
+{
+    s->modems.rx_handler = rx_handler;
+    s->modems.rx_fillin_handler = fillin_handler;
+    s->modems.rx_user_data = user_data;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void set_tx_handler(fax_state_t *s, span_tx_handler_t *handler, void *user_data)
+{
+    s->modems.tx_handler = handler;
+    s->modems.tx_user_data = user_data;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void set_next_tx_handler(fax_state_t *s, span_tx_handler_t *handler, void *user_data)
+{
+    s->modems.next_tx_handler = handler;
+    s->modems.next_tx_user_data = user_data;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v17_v21_rx(void *user_data, const int16_t amp[], int len)
+{
+    fax_state_t *t;
+    fax_modems_state_t *s;
+
+    t = (fax_state_t *) user_data;
+    s = &t->modems;
+    v17_rx(&s->v17_rx, amp, len);
+    if (t->t30.rx_trained)
+    {
+        /* The fast modem has trained, so we no longer need to run the slow
+           one in parallel. */
+        span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.17 + V.21 to V.17 (%.2fdBm0)\n", v17_rx_signal_power(&s->v17_rx));
+        set_rx_handler(t, (span_rx_handler_t *) &v17_rx, (span_rx_fillin_handler_t *) &v17_rx_fillin, &s->v17_rx);
+    }
+    else
+    {
+        fsk_rx(&s->v21_rx, amp, len);
+        if (t->t30.rx_frame_received)
+        {
+            /* We have received something, and the fast modem has not trained. We must
+               be receiving valid V.21 */
+            span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.17 + V.21 to V.21 (%.2fdBm0)\n", fsk_rx_signal_power(&s->v21_rx));
+            set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &s->v21_rx);
+        }
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v17_v21_rx_fillin(void *user_data, int len)
+{
+    fax_state_t *t;
+    fax_modems_state_t *s;
+
+    t = (fax_state_t *) user_data;
+    s = &t->modems;
+    v17_rx_fillin(&s->v17_rx, len);
+    fsk_rx_fillin(&s->v21_rx, len);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v27ter_v21_rx(void *user_data, const int16_t amp[], int len)
+{
+    fax_state_t *t;
+    fax_modems_state_t *s;
+
+    t = (fax_state_t *) user_data;
+    s = &t->modems;
+    v27ter_rx(&s->v27ter_rx, amp, len);
+    if (t->t30.rx_trained)
+    {
+        /* The fast modem has trained, so we no longer need to run the slow
+           one in parallel. */
+        span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.27ter + V.21 to V.27ter (%.2fdBm0)\n", v27ter_rx_signal_power(&s->v27ter_rx));
+        set_rx_handler(t, (span_rx_handler_t *) &v27ter_rx, (span_rx_fillin_handler_t *) &v27ter_rx_fillin, &s->v27ter_rx);
+    }
+    else
+    {
+        fsk_rx(&s->v21_rx, amp, len);
+        if (t->t30.rx_frame_received)
+        {
+            /* We have received something, and the fast modem has not trained. We must
+               be receiving valid V.21 */
+            span_log(&s->logging, SPAN_LOG_FLOW, "Switching from V.27ter + V.21 to V.21 (%.2fdBm0)\n", fsk_rx_signal_power(&s->v21_rx));
+            set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &s->v21_rx);
+        }
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v27ter_v21_rx_fillin(void *user_data, int len)
+{
+    fax_state_t *t;
+    fax_modems_state_t *s;
+
+    t = (fax_state_t *) user_data;
+    s = &t->modems;
+    v27ter_rx_fillin(&s->v27ter_rx, len);
+    fsk_rx_fillin(&s->v21_rx, len);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v29_v21_rx(void *user_data, const int16_t amp[], int len)
+{
+    fax_state_t *t;
+    fax_modems_state_t *s;
+
+    t = (fax_state_t *) user_data;
+    s = &t->modems;
+    v29_rx(&s->v29_rx, amp, len);
+    if (t->t30.rx_trained)
+    {
+        /* The fast modem has trained, so we no longer need to run the slow
+           one in parallel. */
+        span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.29 + V.21 to V.29 (%.2fdBm0)\n", v29_rx_signal_power(&s->v29_rx));
+        set_rx_handler(t, (span_rx_handler_t *) &v29_rx, (span_rx_fillin_handler_t *) &v29_rx_fillin, &s->v29_rx);
+    }
+    else
+    {
+        fsk_rx(&s->v21_rx, amp, len);
+        if (t->t30.rx_frame_received)
+        {
+            /* We have received something, and the fast modem has not trained. We must
+               be receiving valid V.21 */
+            span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.29 + V.21 to V.21 (%.2fdBm0)\n", fsk_rx_signal_power(&s->v21_rx));
+            set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &s->v21_rx);
+        }
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v29_v21_rx_fillin(void *user_data, int len)
+{
+    fax_state_t *t;
+    fax_modems_state_t *s;
+
+    t = (fax_state_t *) user_data;
+    s = &t->modems;
+    v29_rx_fillin(&s->v29_rx, len);
+    fsk_rx_fillin(&s->v21_rx, len);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) fax_rx(fax_state_t *s, int16_t *amp, int len)
+{
+    int i;
+
+#if defined(LOG_FAX_AUDIO)
+    if (s->modems.audio_rx_log >= 0)
+        write(s->modems.audio_rx_log, amp, len*sizeof(int16_t));
+#endif
+    for (i = 0;  i < len;  i++)
+        amp[i] = dc_restore(&s->modems.dc_restore, amp[i]);
+    s->modems.rx_handler(s->modems.rx_user_data, amp, len);
+    t30_timer_update(&s->t30, len);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) fax_rx_fillin(fax_state_t *s, int len)
+{
+    /* To mitigate the effect of lost packets on a packet network we should
+       try to sustain the status quo. If there is no receive modem running, keep
+       things that way. If there is a receive modem running, try to sustain its
+       operation, without causing a phase hop, or letting its adaptive functions
+       diverge. */
+#if defined(LOG_FAX_AUDIO)
+    if (s->modems.audio_rx_log >= 0)
+    {
+        int i;
+#if defined(_MSC_VER)
+        int16_t *amp = (int16_t *) _alloca(sizeof(int16_t)*len);
+#else
+        int16_t amp[len];
+#endif
+
+        vec_zeroi16(amp, len);
+        write(s->modems.audio_rx_log, amp, len*sizeof(int16_t));
+    }
+#endif
+    /* Call the fillin function of the current modem (if there is one). */
+    s->modems.rx_fillin_handler(s->modems.rx_user_data, len);
+    t30_timer_update(&s->t30, len);
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int set_next_tx_type(fax_state_t *s)
+{
+    fax_modems_state_t *t;
+
+    t = &s->modems;
+    if (t->next_tx_handler)
+    {
+        set_tx_handler(s, t->next_tx_handler, t->next_tx_user_data);
+        t->next_tx_handler = NULL;
+        return 0;
+    }
+    /* If there is nothing else to change to, so use zero length silence */
+    silence_gen_alter(&t->silence_gen, 0);
+    set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen);
+    set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+    t->transmit = FALSE;
+    return -1;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) fax_tx(fax_state_t *s, int16_t *amp, int max_len)
+{
+    int len;
+#if defined(LOG_FAX_AUDIO)
+    int required_len;
+    
+    required_len = max_len;
+#endif
+    len = 0;
+    if (s->modems.transmit)
+    {
+        while ((len += s->modems.tx_handler(s->modems.tx_user_data, amp + len, max_len - len)) < max_len)
+        {
+            /* Allow for a change of tx handler within a block */
+            if (set_next_tx_type(s)  &&  s->modems.current_tx_type != T30_MODEM_NONE  &&  s->modems.current_tx_type != T30_MODEM_DONE)
+                t30_front_end_status(&s->t30, T30_FRONT_END_SEND_STEP_COMPLETE);
+            if (!s->modems.transmit)
+            {
+                if (s->modems.transmit_on_idle)
+                {
+                    /* Pad to the requested length with silence */
+                    memset(amp + len, 0, (max_len - len)*sizeof(int16_t));
+                    len = max_len;        
+                }
+                break;
+            }
+        }
+    }
+    else
+    {
+        if (s->modems.transmit_on_idle)
+        {
+            /* Pad to the requested length with silence */
+            memset(amp, 0, max_len*sizeof(int16_t));
+            len = max_len;        
+        }
+    }
+#if defined(LOG_FAX_AUDIO)
+    if (s->modems.audio_tx_log >= 0)
+    {
+        if (len < required_len)
+            memset(amp + len, 0, (required_len - len)*sizeof(int16_t));
+        write(s->modems.audio_tx_log, amp, required_len*sizeof(int16_t));
+    }
+#endif
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void fax_set_rx_type(void *user_data, int type, int bit_rate, int short_train, int use_hdlc)
+{
+    fax_state_t *s;
+    put_bit_func_t put_bit_func;
+    void *put_bit_user_data;
+    fax_modems_state_t *t;
+
+    s = (fax_state_t *) user_data;
+    t = &s->modems;
+    span_log(&s->logging, SPAN_LOG_FLOW, "Set rx type %d\n", type);
+    if (t->current_rx_type == type)
+        return;
+    t->current_rx_type = type;
+    t->rx_bit_rate = bit_rate;
+    if (use_hdlc)
+    {
+        put_bit_func = (put_bit_func_t) hdlc_rx_put_bit;
+        put_bit_user_data = (void *) &t->hdlc_rx;
+        hdlc_rx_init(&t->hdlc_rx, FALSE, TRUE, HDLC_FRAMING_OK_THRESHOLD, t30_hdlc_accept, &s->t30);
+    }
+    else
+    {
+        put_bit_func = t30_non_ecm_put_bit;
+        put_bit_user_data = (void *) &s->t30;
+    }
+    switch (type)
+    {
+    case T30_MODEM_V21:
+        fsk_rx_init(&t->v21_rx, &preset_fsk_specs[FSK_V21CH2], FSK_FRAME_MODE_SYNC, (put_bit_func_t) hdlc_rx_put_bit, put_bit_user_data);
+        fsk_rx_signal_cutoff(&t->v21_rx, -45.5f);
+        set_rx_handler(s, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &t->v21_rx);
+        break;
+    case T30_MODEM_V27TER:
+        v27ter_rx_restart(&t->v27ter_rx, bit_rate, FALSE);
+        v27ter_rx_set_put_bit(&t->v27ter_rx, put_bit_func, put_bit_user_data);
+        set_rx_handler(s, &v27ter_v21_rx, &v27ter_v21_rx_fillin, s);
+        break;
+    case T30_MODEM_V29:
+        v29_rx_restart(&t->v29_rx, bit_rate, FALSE);
+        v29_rx_set_put_bit(&t->v29_rx, put_bit_func, put_bit_user_data);
+        set_rx_handler(s, &v29_v21_rx, &v29_v21_rx_fillin, s);
+        break;
+    case T30_MODEM_V17:
+        v17_rx_restart(&t->v17_rx, bit_rate, short_train);
+        v17_rx_set_put_bit(&t->v17_rx, put_bit_func, put_bit_user_data);
+        set_rx_handler(s, &v17_v21_rx, &v17_v21_rx_fillin, s);
+        break;
+    case T30_MODEM_DONE:
+        span_log(&s->logging, SPAN_LOG_FLOW, "FAX exchange complete\n");
+    default:
+        set_rx_handler(s, (span_rx_handler_t *) &span_dummy_rx, (span_rx_fillin_handler_t *) &span_dummy_rx_fillin, s);
+        break;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void fax_set_tx_type(void *user_data, int type, int bit_rate, int short_train, int use_hdlc)
+{
+    fax_state_t *s;
+    get_bit_func_t get_bit_func;
+    void *get_bit_user_data;
+    fax_modems_state_t *t;
+    int tone;
+
+    s = (fax_state_t *) user_data;
+    t = &s->modems;
+    span_log(&s->logging, SPAN_LOG_FLOW, "Set tx type %d\n", type);
+    if (t->current_tx_type == type)
+        return;
+    if (use_hdlc)
+    {
+        get_bit_func = (get_bit_func_t) hdlc_tx_get_bit;
+        get_bit_user_data = (void *) &t->hdlc_tx;
+    }
+    else
+    {
+        get_bit_func = t30_non_ecm_get_bit;
+        get_bit_user_data = (void *) &s->t30;
+    }
+    switch (type)
+    {
+    case T30_MODEM_PAUSE:
+        silence_gen_alter(&t->silence_gen, ms_to_samples(short_train));
+        set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen);
+        set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+        t->transmit = TRUE;
+        break;
+    case T30_MODEM_CED:
+    case T30_MODEM_CNG:
+        if (type == T30_MODEM_CED)
+            tone = MODEM_CONNECT_TONES_FAX_CED;
+        else
+            tone = MODEM_CONNECT_TONES_FAX_CNG;
+        modem_connect_tones_tx_init(&t->connect_tx, tone);
+        set_tx_handler(s, (span_tx_handler_t *) &modem_connect_tones_tx, &t->connect_tx);
+        set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+        t->transmit = TRUE;
+        break;
+    case T30_MODEM_V21:
+        fsk_tx_init(&t->v21_tx, &preset_fsk_specs[FSK_V21CH2], get_bit_func, get_bit_user_data);
+        /* The spec says 1s +-15% of preamble. So, the minimum is 32 octets. */
+        hdlc_tx_flags(&t->hdlc_tx, 32);
+        /* Pause before switching from phase C, as per T.30 5.3.2.2. If we omit this, the receiver
+           might not see the carrier fall between the high speed and low speed sections. In practice,
+           a 75ms gap before any V.21 transmission is harmless, adds little to the overall length of
+           a call, and ensures the receiving end is ready. */
+        silence_gen_alter(&t->silence_gen, ms_to_samples(75));
+        set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen);
+        set_next_tx_handler(s, (span_tx_handler_t *) &fsk_tx, &t->v21_tx);
+        t->transmit = TRUE;
+        break;
+    case T30_MODEM_V27TER:
+        silence_gen_alter(&t->silence_gen, ms_to_samples(75));
+        /* For any fast modem, set 200ms of preamble flags */
+        hdlc_tx_flags(&t->hdlc_tx, bit_rate/(8*5));
+        v27ter_tx_restart(&t->v27ter_tx, bit_rate, t->use_tep);
+        v27ter_tx_set_get_bit(&t->v27ter_tx, get_bit_func, get_bit_user_data);
+        set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen);
+        set_next_tx_handler(s, (span_tx_handler_t *) &v27ter_tx, &t->v27ter_tx);
+        t->transmit = TRUE;
+        break;
+    case T30_MODEM_V29:
+        silence_gen_alter(&t->silence_gen, ms_to_samples(75));
+        /* For any fast modem, set 200ms of preamble flags */
+        hdlc_tx_flags(&t->hdlc_tx, bit_rate/(8*5));
+        v29_tx_restart(&t->v29_tx, bit_rate, t->use_tep);
+        v29_tx_set_get_bit(&t->v29_tx, get_bit_func, get_bit_user_data);
+        set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen);
+        set_next_tx_handler(s, (span_tx_handler_t *) &v29_tx, &t->v29_tx);
+        t->transmit = TRUE;
+        break;
+    case T30_MODEM_V17:
+        silence_gen_alter(&t->silence_gen, ms_to_samples(75));
+        /* For any fast modem, set 200ms of preamble flags */
+        hdlc_tx_flags(&t->hdlc_tx, bit_rate/(8*5));
+        v17_tx_restart(&t->v17_tx, bit_rate, t->use_tep, short_train);
+        v17_tx_set_get_bit(&t->v17_tx, get_bit_func, get_bit_user_data);
+        set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen);
+        set_next_tx_handler(s, (span_tx_handler_t *) &v17_tx, &t->v17_tx);
+        t->transmit = TRUE;
+        break;
+    case T30_MODEM_DONE:
+        span_log(&s->logging, SPAN_LOG_FLOW, "FAX exchange complete\n");
+        /* Fall through */
+    default:
+        silence_gen_alter(&t->silence_gen, 0);
+        set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen);
+        set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+        t->transmit = FALSE;
+        break;
+    }
+    t->tx_bit_rate = bit_rate;
+    t->current_tx_type = type;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) fax_set_transmit_on_idle(fax_state_t *s, int transmit_on_idle)
+{
+    s->modems.transmit_on_idle = transmit_on_idle;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) fax_set_tep_mode(fax_state_t *s, int use_tep)
+{
+    s->modems.use_tep = use_tep;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(t30_state_t *) fax_get_t30_state(fax_state_t *s)
+{
+    return &s->t30;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(logging_state_t *) fax_get_logging_state(fax_state_t *s)
+{
+    return &s->logging;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(fax_state_t *) fax_init(fax_state_t *s, int calling_party)
+{
+    if (s == NULL)
+    {
+        if ((s = (fax_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, 0, sizeof(*s));
+    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+    span_log_set_protocol(&s->logging, "FAX");
+    fax_modems_init(&s->modems,
+                    FALSE,
+                    t30_hdlc_accept,
+                    hdlc_underflow_handler,
+                    t30_non_ecm_put_bit,
+                    t30_non_ecm_get_bit,
+                    tone_detected,
+                    &s->t30);
+    t30_init(&s->t30,
+             calling_party,
+             fax_set_rx_type,
+             (void *) s,
+             fax_set_tx_type,
+             (void *) s,
+             fax_send_hdlc,
+             (void *) s);
+    t30_set_supported_modems(&s->t30, T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17);
+    t30_restart(&s->t30);
+#if defined(LOG_FAX_AUDIO)
+    {
+        char buf[100 + 1];
+        struct tm *tm;
+        time_t now;
+
+        time(&now);
+        tm = localtime(&now);
+        sprintf(buf,
+                "/tmp/fax-rx-audio-%p-%02d%02d%02d%02d%02d%02d",
+                s,
+                tm->tm_year%100,
+                tm->tm_mon + 1,
+                tm->tm_mday,
+                tm->tm_hour,
+                tm->tm_min,
+                tm->tm_sec);
+        s->modems.audio_rx_log = open(buf, O_CREAT | O_TRUNC | O_WRONLY, 0666);
+        sprintf(buf,
+                "/tmp/fax-tx-audio-%p-%02d%02d%02d%02d%02d%02d",
+                s,
+                tm->tm_year%100,
+                tm->tm_mon + 1,
+                tm->tm_mday,
+                tm->tm_hour,
+                tm->tm_min,
+                tm->tm_sec);
+        s->modems.audio_tx_log = open(buf, O_CREAT | O_TRUNC | O_WRONLY, 0666);
+    }
+#endif
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) fax_release(fax_state_t *s)
+{
+    t30_release(&s->t30);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) fax_free(fax_state_t *s)
+{
+    t30_release(&s->t30);
+    free(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.