diff --git a/libs/spandsp/src/oki_adpcm.c b/libs/spandsp/src/oki_adpcm.c index 787df3e322..09aea00a27 100644 --- a/libs/spandsp/src/oki_adpcm.c +++ b/libs/spandsp/src/oki_adpcm.c @@ -26,8 +26,6 @@ * * The actual OKI ADPCM encode and decode method is derived from freely * available code, whose exact origins seem uncertain. - * - * $Id: oki_adpcm.c,v 1.32 2009/02/10 13:06:46 steveu Exp $ */ /*! \file */ @@ -153,7 +151,7 @@ static const float cutoff_coeffs[] = static int16_t decode(oki_adpcm_state_t *s, uint8_t adpcm) { - int16_t e; + int16_t d; int16_t ss; int16_t linear; @@ -170,20 +168,20 @@ static int16_t decode(oki_adpcm_state_t *s, uint8_t adpcm) */ ss = step_size[s->step_index]; - e = ss >> 3; + d = ss >> 3; if (adpcm & 0x01) - e += (ss >> 2); + d += (ss >> 2); /*endif*/ if (adpcm & 0x02) - e += (ss >> 1); + d += (ss >> 1); /*endif*/ if (adpcm & 0x04) - e += ss; + d += ss; /*endif*/ if (adpcm & 0x08) - e = -e; + d = -d; /*endif*/ - linear = s->last + e; + linear = s->last + d; /* Saturate the values to +/- 2^11 (supposed to be 12 bits) */ if (linear > 2047) @@ -206,32 +204,32 @@ static int16_t decode(oki_adpcm_state_t *s, uint8_t adpcm) static uint8_t encode(oki_adpcm_state_t *s, int16_t linear) { - int16_t e; + int16_t d; int16_t ss; uint8_t adpcm; ss = step_size[s->step_index]; - e = (linear >> 4) - s->last; + d = (linear >> 4) - s->last; adpcm = (uint8_t) 0x00; - if (e < 0) + if (d < 0) { adpcm = (uint8_t) 0x08; - e = -e; + d = -d; } /*endif*/ - if (e >= ss) + if (d >= ss) { adpcm |= (uint8_t) 0x04; - e -= ss; + d -= ss; } /*endif*/ - if (e >= (ss >> 1)) + if (d >= (ss >> 1)) { adpcm |= (uint8_t) 0x02; - e -= ss; + d -= (ss >> 1); } /*endif*/ - if (e >= (ss >> 2)) + if (d >= (ss >> 2)) adpcm |= (uint8_t) 0x01; /*endif*/ diff --git a/libs/spandsp/src/spandsp/private/t38_gateway.h b/libs/spandsp/src/spandsp/private/t38_gateway.h index 9f4eed57e0..54a935a270 100644 --- a/libs/spandsp/src/spandsp/private/t38_gateway.h +++ b/libs/spandsp/src/spandsp/private/t38_gateway.h @@ -21,8 +21,6 @@ * 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: t38_gateway.h,v 1.5 2009/11/07 08:58:58 steveu Exp $ */ /*! \file */ @@ -35,7 +33,7 @@ */ typedef struct { - /*! Core T.38 IFP support */ + /*! \brief Core T.38 IFP support */ t38_core_state_t t38; /*! \brief TRUE if the NSF, NSC, and NSS are to be suppressed by altering @@ -61,9 +59,10 @@ typedef struct { /*! \brief The FAX modem set for the audio side fo the gateway. */ fax_modems_state_t modems; - /*! \brief The current receive signal handler. Actual receiving hop between this + /*! \brief The current receive signal handler. Actual receiving hops between this and a dummy receive routine. */ span_rx_handler_t *base_rx_handler; + span_rx_fillin_handler_t *base_rx_fillin_handler; } t38_gateway_audio_state_t; /*! @@ -144,6 +143,8 @@ typedef struct int supported_modems; /*! \brief TRUE if ECM FAX mode is allowed through the gateway. */ int ecm_allowed; + /*! \brief Required time between T.38 transmissions, in ms. */ + int ms_per_tx_chunk; /*! \brief TRUE if in image data modem is to use short training. This usually follows image_data_mode, but in ECM mode T.30 defines recovery diff --git a/libs/spandsp/src/spandsp/private/v17rx.h b/libs/spandsp/src/spandsp/private/v17rx.h index 87931b1d89..bc8ad1b63c 100644 --- a/libs/spandsp/src/spandsp/private/v17rx.h +++ b/libs/spandsp/src/spandsp/private/v17rx.h @@ -21,8 +21,6 @@ * 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: v17rx.h,v 1.2.4.1 2009/12/24 16:52:30 steveu Exp $ */ #if !defined(_SPANDSP_PRIVATE_V17RX_H_) @@ -30,10 +28,11 @@ /* Target length for the equalizer is about 63 taps, to deal with the worst stuff in V.56bis. */ +/*! The length of the equalizer buffer */ +#define V17_EQUALIZER_LEN 33 + /*! Samples before the target position in the equalizer buffer */ -#define V17_EQUALIZER_PRE_LEN 8 -/*! Samples after the target position in the equalizer buffer */ -#define V17_EQUALIZER_POST_LEN 8 +#define V17_EQUALIZER_PRE_LEN 16 /*! The number of taps in the pulse shaping/bandpass filter */ #define V17_RX_FILTER_STEPS 27 @@ -137,7 +136,7 @@ struct v17_rx_state_s int eq_step; /*! \brief Current write offset into the equalizer buffer. */ int eq_put_step; - /*! \brief Symbol counter to the next equalizer update. */ + /*! \brief Symbol count to the next equalizer update. */ int eq_skip; /*! \brief The current half of the baud. */ @@ -152,11 +151,11 @@ struct v17_rx_state_s /*! \brief The current delta factor for updating the equalizer coefficients. */ float eq_delta; /*! \brief The adaptive equalizer coefficients. */ - complexi16_t eq_coeff[V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN]; + complexi16_t eq_coeff[V17_EQUALIZER_LEN]; /*! \brief A saved set of adaptive equalizer coefficients for use after restarts. */ - complexi16_t eq_coeff_save[V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN]; + complexi16_t eq_coeff_save[V17_EQUALIZER_LEN]; /*! \brief The equalizer signal buffer. */ - complexi16_t eq_buf[V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN]; + complexi16_t eq_buf[V17_EQUALIZER_LEN]; /*! Low band edge filter for symbol sync. */ int32_t symbol_sync_low[2]; @@ -175,11 +174,11 @@ struct v17_rx_state_s /*! \brief The current delta factor for updating the equalizer coefficients. */ float eq_delta; /*! \brief The adaptive equalizer coefficients. */ - complexf_t eq_coeff[V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN]; + complexf_t eq_coeff[V17_EQUALIZER_LEN]; /*! \brief A saved set of adaptive equalizer coefficients for use after restarts. */ - complexf_t eq_coeff_save[V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN]; + complexf_t eq_coeff_save[V17_EQUALIZER_LEN]; /*! \brief The equalizer signal buffer. */ - complexf_t eq_buf[V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN]; + complexf_t eq_buf[V17_EQUALIZER_LEN]; /*! Low band edge filter for symbol sync. */ float symbol_sync_low[2]; diff --git a/libs/spandsp/src/spandsp/v17rx.h b/libs/spandsp/src/spandsp/v17rx.h index 2ab84151d3..da4514d316 100644 --- a/libs/spandsp/src/spandsp/v17rx.h +++ b/libs/spandsp/src/spandsp/v17rx.h @@ -287,7 +287,7 @@ SPAN_DECLARE_NONSTD(int) v17_rx(v17_rx_state_t *s, const int16_t amp[], int len) \param len The number of samples to fake. \return The number of samples unprocessed. */ -SPAN_DECLARE(int) v17_rx_fillin(v17_rx_state_t *s, int len); +SPAN_DECLARE_NONSTD(int) v17_rx_fillin(v17_rx_state_t *s, int len); /*! Get a snapshot of the current equalizer coefficients. \brief Get a snapshot of the current equalizer coefficients. diff --git a/libs/spandsp/src/t38_gateway.c b/libs/spandsp/src/t38_gateway.c index 6e542f3e39..824ee16e4b 100644 --- a/libs/spandsp/src/t38_gateway.c +++ b/libs/spandsp/src/t38_gateway.c @@ -22,8 +22,6 @@ * 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: t38_gateway.c,v 1.171.4.2 2009/12/19 10:44:10 steveu Exp $ */ /*! \file */ @@ -106,7 +104,8 @@ /* This is the target time per transmission chunk. The actual packet timing will sync to the data octets. */ /*! The default number of milliseconds per transmitted IFP when sending bulk T.38 data */ -#define MS_PER_TX_CHUNK 30 +#define DEFAULT_MS_PER_TX_CHUNK 30 + /*! The number of bytes which must be in the audio to T.38 HDLC buffer before we start outputting them as IFP messages. */ #define HDLC_START_BUFFER_LEVEL 8 @@ -185,11 +184,15 @@ static void non_ecm_remove_fill_and_put_bit(void *user_data, int bit); static void non_ecm_push_residue(t38_gateway_state_t *s); static void tone_detected(void *user_data, int tone, int level, int delay); -static void set_rx_handler(t38_gateway_state_t *s, span_rx_handler_t *handler, void *user_data) +static void set_rx_handler(t38_gateway_state_t *s, span_rx_handler_t *handler, span_rx_fillin_handler_t *fillin_handler, void *user_data) { if (s->audio.modems.rx_handler != span_dummy_rx) + { s->audio.modems.rx_handler = handler; + s->audio.modems.rx_fillin_handler = fillin_handler; + } s->audio.base_rx_handler = handler; + s->audio.base_rx_fillin_handler = fillin_handler; s->audio.modems.rx_user_data = user_data; } /*- End of function --------------------------------------------------------*/ @@ -211,6 +214,20 @@ static void set_next_tx_handler(t38_gateway_state_t *s, span_tx_handler_t *handl static void set_rx_active(t38_gateway_state_t *s, int active) { s->audio.modems.rx_handler = (active) ? s->audio.base_rx_handler : span_dummy_rx; + s->audio.modems.rx_fillin_handler = (active) ? s->audio.base_rx_fillin_handler : span_dummy_rx_fillin; +} +/*- End of function --------------------------------------------------------*/ + +static int v17_v21_rx_fillin(void *user_data, int len) +{ + t38_gateway_state_t *t; + fax_modems_state_t *s; + + t = (t38_gateway_state_t *) user_data; + s = &t->audio.modems; + v17_rx_fillin(&s->v17_rx, len); + fsk_rx_fillin(&s->v21_rx, len); + return 0; } /*- End of function --------------------------------------------------------*/ @@ -227,7 +244,7 @@ static int v17_v21_rx(void *user_data, const int16_t amp[], int len) /* 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, &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 { @@ -235,7 +252,7 @@ static int v17_v21_rx(void *user_data, const int16_t amp[], int len) if (s->rx_signal_present) { 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, &s->v21_rx); + set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &s->v21_rx); } /*endif*/ } @@ -244,6 +261,19 @@ static int v17_v21_rx(void *user_data, const int16_t amp[], int len) } /*- End of function --------------------------------------------------------*/ +static int v27ter_v21_rx_fillin(void *user_data, int len) +{ + t38_gateway_state_t *t; + fax_modems_state_t *s; + + t = (t38_gateway_state_t *) user_data; + s = &t->audio.modems; + v27ter_rx_fillin(&s->v27ter_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) { t38_gateway_state_t *t; @@ -257,7 +287,7 @@ static int v27ter_v21_rx(void *user_data, const int16_t amp[], int len) /* 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, &s->v27ter_rx); + set_rx_handler(t, (span_rx_handler_t *) &v27ter_rx, (span_rx_fillin_handler_t *) &v27ter_v21_rx_fillin, &s->v27ter_rx); } else { @@ -265,7 +295,7 @@ static int v27ter_v21_rx(void *user_data, const int16_t amp[], int len) if (s->rx_signal_present) { span_log(&t->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, &s->v21_rx); + set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &s->v21_rx); } /*endif*/ } @@ -274,6 +304,19 @@ static int v27ter_v21_rx(void *user_data, const int16_t amp[], int len) } /*- End of function --------------------------------------------------------*/ +static int v29_v21_rx_fillin(void *user_data, int len) +{ + t38_gateway_state_t *t; + fax_modems_state_t *s; + + t = (t38_gateway_state_t *) user_data; + s = &t->audio.modems; + v29_rx_fillin(&s->v29_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) { t38_gateway_state_t *t; @@ -287,7 +330,7 @@ static int v29_v21_rx(void *user_data, const int16_t amp[], int len) /* 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, &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 { @@ -295,7 +338,7 @@ static int v29_v21_rx(void *user_data, const int16_t amp[], int len) if (s->rx_signal_present) { 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, &s->v21_rx); + set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &s->v21_rx); } /*endif*/ } @@ -651,7 +694,7 @@ static void edit_control_messages(t38_gateway_state_t *s, int from_modem, uint8_ { case T30_DIS: /* Make sure the V.8 capability doesn't pass through. If it - did then two V.34 capable FAX machines might start some + did, two V.34 capable FAX machines might start some V.8 re-negotiation. */ buf[3] &= ~DISBIT6; break; @@ -827,7 +870,6 @@ static void monitor_control_messages(t38_gateway_state_t *s, if (len >= 6) { j = (buf[5] & (DISBIT7 | DISBIT6 | DISBIT5)) >> 4; - span_log(&s->logging, SPAN_LOG_FLOW, "Min bits test = 0x%X\n", buf[5]); s->core.min_row_bits = (s->core.fast_bit_rate*minimum_scan_line_times[j])/1000; } else @@ -1468,7 +1510,7 @@ static void set_octets_per_data_packet(t38_gateway_state_t *s, int bit_rate) { int octets; - octets = MS_PER_TX_CHUNK*bit_rate/(8*1000); + octets = s->core.ms_per_tx_chunk*bit_rate/(8*1000); if (octets < 1) octets = 1; /*endif*/ @@ -2029,23 +2071,23 @@ static int restart_rx_modem(t38_gateway_state_t *s) case T38_V17_RX: v17_rx_restart(&s->audio.modems.v17_rx, s->core.fast_bit_rate, s->core.short_train); v17_rx_set_put_bit(&s->audio.modems.v17_rx, put_bit_func, put_bit_user_data); - set_rx_handler(s, (span_rx_handler_t *) &v17_v21_rx, s); + set_rx_handler(s, &v17_v21_rx, &v17_v21_rx_fillin, s); s->core.fast_rx_active = T38_V17_RX; break; case T38_V27TER_RX: v27ter_rx_restart(&s->audio.modems.v27ter_rx, s->core.fast_bit_rate, FALSE); v27ter_rx_set_put_bit(&s->audio.modems.v27ter_rx, put_bit_func, put_bit_user_data); - set_rx_handler(s, (span_rx_handler_t *) &v27ter_v21_rx, s); + set_rx_handler(s, &v27ter_v21_rx, &v27ter_v21_rx_fillin, s); s->core.fast_rx_active = T38_V27TER_RX; break; case T38_V29_RX: v29_rx_restart(&s->audio.modems.v29_rx, s->core.fast_bit_rate, FALSE); v29_rx_set_put_bit(&s->audio.modems.v29_rx, put_bit_func, put_bit_user_data); - set_rx_handler(s, (span_rx_handler_t *) &v29_v21_rx, s); + set_rx_handler(s, &v29_v21_rx, &v29_v21_rx_fillin, s); s->core.fast_rx_active = T38_V29_RX; break; default: - set_rx_handler(s, (span_rx_handler_t *) &fsk_rx, &(s->audio.modems.v21_rx)); + set_rx_handler(s, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &(s->audio.modems.v21_rx)); s->core.fast_rx_active = T38_NONE; break; } @@ -2054,15 +2096,8 @@ static int restart_rx_modem(t38_gateway_state_t *s) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], int len) +static void update_rx_timing(t38_gateway_state_t *s, int len) { - int i; - -#if defined(LOG_FAX_AUDIO) - if (s->audio.modems.audio_rx_log >= 0) - write(s->audio.modems.audio_rx_log, amp, len*sizeof(int16_t)); - /*endif*/ -#endif if (s->core.samples_to_timeout > 0) { if ((s->core.samples_to_timeout -= len) <= 0) @@ -2096,6 +2131,19 @@ SPAN_DECLARE_NONSTD(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], i /*endif*/ } /*endif*/ +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE_NONSTD(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], int len) +{ + int i; + +#if defined(LOG_FAX_AUDIO) + if (s->audio.modems.audio_rx_log >= 0) + write(s->audio.modems.audio_rx_log, amp, len*sizeof(int16_t)); + /*endif*/ +#endif + update_rx_timing(s, len); for (i = 0; i < len; i++) amp[i] = dc_restore(&(s->audio.modems.dc_restore), amp[i]); /*endfor*/ @@ -2106,7 +2154,28 @@ SPAN_DECLARE_NONSTD(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], i SPAN_DECLARE_NONSTD(int) t38_gateway_rx_fillin(t38_gateway_state_t *s, int len) { - /* TODO: handle things properly */ + /* 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->audio.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 + update_rx_timing(s, len); + /* TODO: handle the modems properly */ + s->audio.modems.rx_fillin_handler(s->audio.modems.rx_user_data, len); return 0; } /*- End of function --------------------------------------------------------*/ @@ -2301,11 +2370,12 @@ SPAN_DECLARE(t38_gateway_state_t *) t38_gateway_init(t38_gateway_state_t *s, t38_gateway_t38_init(s, tx_packet_handler, tx_packet_user_data); set_rx_active(s, TRUE); - t38_gateway_set_supported_modems(s, T30_SUPPORT_V27TER | T30_SUPPORT_V29); + t38_gateway_set_supported_modems(s, T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17); t38_gateway_set_nsx_suppression(s, (const uint8_t *) "\x00\x00\x00", 3, (const uint8_t *) "\x00\x00\x00", 3); s->core.to_t38.octets_per_data_packet = 1; s->core.ecm_allowed = TRUE; + s->core.ms_per_tx_chunk = DEFAULT_MS_PER_TX_CHUNK; t38_non_ecm_buffer_init(&s->core.non_ecm_to_modem, FALSE, 0); restart_rx_modem(s); s->core.timed_mode = TIMED_MODE_STARTUP; diff --git a/libs/spandsp/src/v17rx.c b/libs/spandsp/src/v17rx.c index 3620bc83f6..b5ba221cae 100644 --- a/libs/spandsp/src/v17rx.c +++ b/libs/spandsp/src/v17rx.c @@ -22,8 +22,6 @@ * 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: v17rx.c,v 1.153.4.6 2009/12/28 12:20:46 steveu Exp $ */ /*! \file */ @@ -98,9 +96,6 @@ /*! The 16 bit pattern used in the bridge section of the training sequence */ #define V17_BRIDGE_WORD 0x8880 -/*! The length of the equalizer buffer */ -#define V17_EQUALIZER_LEN (V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN) - enum { TRAINING_STAGE_NORMAL_OPERATION = 0, @@ -233,6 +228,7 @@ static void equalizer_restore(v17_rx_state_t *s) s->eq_put_step = RX_PULSESHAPER_COEFF_SETS*10/(3*2) - 1; s->eq_step = 0; + s->eq_skip = 0; } /*- End of function --------------------------------------------------------*/ @@ -253,6 +249,7 @@ static void equalizer_reset(v17_rx_state_t *s) s->eq_put_step = RX_PULSESHAPER_COEFF_SETS*10/(3*2) - 1; s->eq_step = 0; + s->eq_skip = 0; } /*- End of function --------------------------------------------------------*/ @@ -565,7 +562,7 @@ static __inline__ void symbol_sync(v17_rx_state_t *s) Passband Timing Recovery in an All-Digital Modem Receiver IEEE TRANSACTIONS ON COMMUNICATIONS, VOL. COM-26, NO. 5, MAY 1978 */ - /* This is slightly rearranged for figure 3b of the Godard paper, as this saves a couple of + /* This is slightly rearranged from figure 3b of the Godard paper, as this saves a couple of maths operations */ #if defined(SPANDSP_USE_FIXED_POINTx) /* TODO: The scalings used here need more thorough evaluation, to see if overflows are possible. */ @@ -579,12 +576,12 @@ static __inline__ void symbol_sync(v17_rx_state_t *s) s->symbol_sync_dc_filter[0] = v; /* A little integration will now filter away much of the HF noise */ s->baud_phase -= p; - if (abs(s->baud_phase) > 100*FP_FACTOR) + v = labs(s->baud_phase); + if (v > 100*FP_FACTOR) { - if (s->baud_phase > 0) - i = (s->baud_phase > 1000*FP_FACTOR) ? 15 : 1; - else - i = (s->baud_phase < -1000*FP_FACTOR) ? -15 : -1; + i = (v > 1000*FP_FACTOR) ? 15 : 1; + if (s->baud_phase < 0) + i = -i; //printf("v = %10.5f %5d - %f %f %d %d\n", v, i, p, s->baud_phase, s->total_baud_timing_correction); s->eq_put_step += i; s->total_baud_timing_correction += i; @@ -600,12 +597,12 @@ static __inline__ void symbol_sync(v17_rx_state_t *s) s->symbol_sync_dc_filter[0] = v; /* A little integration will now filter away much of the HF noise */ s->baud_phase -= p; - if (fabsf(s->baud_phase) > 100.0f) + v = fabsf(s->baud_phase); + if (v > 100.0f) { - if (s->baud_phase > 0.0f) - i = (s->baud_phase > 1000.0f) ? 15 : 1; - else - i = (s->baud_phase < -1000.0f) ? -15 : -1; + i = (v > 1000.0f) ? 15 : 1; + if (s->baud_phase < 0.0f) + i = -i; //printf("v = %10.5f %5d - %f %f %d\n", v, i, p, s->baud_phase, s->total_baud_timing_correction); s->eq_put_step += i; s->total_baud_timing_correction += i; @@ -732,18 +729,14 @@ static void process_half_baud(v17_rx_state_t *s, const complexf_t *sample) if (s->training_count == 100) { i = s->training_count; - /* Avoid the possibility of a divide by zero */ - if (i) - { - j = i & 0xF; - ang = (s->angles[j] - s->start_angles[0])/i - + (s->angles[j | 0x1] - s->start_angles[1])/i; - s->carrier_phase_rate += 3*(ang/20); - //span_log(&s->logging, SPAN_LOG_FLOW, "Angles %x, %x, %x, %x, dist %d\n", s->angles[j], s->start_angles[0], s->angles[j | 0x1], s->start_angles[1], i); + j = i & 0xF; + ang = (s->angles[j] - s->start_angles[0])/i + + (s->angles[j | 0x1] - s->start_angles[1])/i; + s->carrier_phase_rate += 3*(ang/20); + //span_log(&s->logging, SPAN_LOG_FLOW, "Angles %x, %x, %x, %x, dist %d\n", s->angles[j], s->start_angles[0], s->angles[j | 0x1], s->start_angles[1], i); - s->start_angles[0] = s->angles[j]; - s->start_angles[1] = s->angles[j | 0x1]; - } + s->start_angles[0] = s->angles[j]; + s->start_angles[1] = s->angles[j | 0x1]; //span_log(&s->logging, SPAN_LOG_FLOW, "%d %d %d %d %d\n", s->angles[s->training_count & 0xF], s->start_angles[0], s->angles[(s->training_count | 0x1) & 0xF], s->start_angles[1], s->training_count); span_log(&s->logging, SPAN_LOG_FLOW, "First coarse carrier frequency %7.2f (%d)\n", dds_frequencyf(s->carrier_phase_rate), s->training_count); @@ -1219,7 +1212,7 @@ SPAN_DECLARE_NONSTD(int) v17_rx(v17_rx_state_t *s, const int16_t amp[], int len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(int) v17_rx_fillin(v17_rx_state_t *s, int len) +SPAN_DECLARE_NONSTD(int) v17_rx_fillin(v17_rx_state_t *s, int len) { int i; @@ -1343,15 +1336,14 @@ SPAN_DECLARE(int) v17_rx_restart(v17_rx_state_t *s, int bit_rate, int short_trai s->distances[0] = 0; s->trellis_ptr = 14; - span_log(&s->logging, SPAN_LOG_FLOW, "Phase rates %f %f\n", dds_frequencyf(s->carrier_phase_rate), dds_frequencyf(s->carrier_phase_rate_save)); s->carrier_phase = 0; power_meter_init(&(s->power), 4); if (s->short_train) { s->carrier_phase_rate = s->carrier_phase_rate_save; - s->agc_scaling = s->agc_scaling_save; equalizer_restore(s); + s->agc_scaling = s->agc_scaling_save; /* Don't allow any frequency correction at all, until we start to pull the phase in. */ #if defined(SPANDSP_USE_FIXED_POINTx) s->carrier_track_i = 0; @@ -1366,18 +1358,19 @@ SPAN_DECLARE(int) v17_rx_restart(v17_rx_state_t *s, int bit_rate, int short_trai s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ); equalizer_reset(s); #if defined(SPANDSP_USE_FIXED_POINTx) - s->agc_scaling_save = 0; + //s->agc_scaling_save = 0; s->agc_scaling = (float) FP_FACTOR*32768.0f*0.0017f/RX_PULSESHAPER_GAIN; s->carrier_track_i = 5000; s->carrier_track_p = 40000; #else - s->agc_scaling_save = 0.0f; + //s->agc_scaling_save = 0.0f; s->agc_scaling = 0.0017f/RX_PULSESHAPER_GAIN; s->carrier_track_i = 5000.0f; s->carrier_track_p = 40000.0f; #endif } s->last_sample = 0; + span_log(&s->logging, SPAN_LOG_FLOW, "Phase rates %f %f\n", dds_frequencyf(s->carrier_phase_rate), dds_frequencyf(s->carrier_phase_rate_save)); /* Initialise the working data for symbol timing synchronisation */ #if defined(SPANDSP_USE_FIXED_POINTx) @@ -1432,8 +1425,13 @@ SPAN_DECLARE(v17_rx_state_t *) v17_rx_init(v17_rx_state_t *s, int bit_rate, put_ s->short_train = FALSE; //s->scrambler_tap = 18 - 1; v17_rx_signal_cutoff(s, -45.5f); - s->agc_scaling = 0.0017f/RX_PULSESHAPER_GAIN; +#if defined(SPANDSP_USE_FIXED_POINTx) + s->agc_scaling_save = 0; + s->agc_scaling = (float) FP_FACTOR*32768.0f*0.0017f/RX_PULSESHAPER_GAIN; +#else s->agc_scaling_save = 0.0f; + s->agc_scaling = 0.0017f/RX_PULSESHAPER_GAIN; +#endif s->carrier_phase_rate_save = dds_phase_ratef(CARRIER_NOMINAL_FREQ); v17_rx_restart(s, bit_rate, s->short_train); return s; diff --git a/libs/spandsp/tests/oki_adpcm_tests.c b/libs/spandsp/tests/oki_adpcm_tests.c index 9bb72521dc..0b761d9607 100644 --- a/libs/spandsp/tests/oki_adpcm_tests.c +++ b/libs/spandsp/tests/oki_adpcm_tests.c @@ -22,8 +22,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id: oki_adpcm_tests.c,v 1.37 2009/05/30 15:23:14 steveu Exp $ */ /*! \file */ @@ -85,24 +83,33 @@ int main(int argc, char *argv[]) int hist_out; oki_adpcm_state_t *oki_enc_state; oki_adpcm_state_t *oki_dec_state; + oki_adpcm_state_t *oki_dec_state2; int xx; int total_pre_samples; int total_compressed_bytes; int total_post_samples; + int successive_08_bytes; + int successive_80_bytes; + int encoded_fd; + const char *encoded_file_name; const char *in_file_name; int log_encoded_data; int opt; bit_rate = 32000; + encoded_file_name = NULL; in_file_name = IN_FILE_NAME; log_encoded_data = FALSE; - while ((opt = getopt(argc, argv, "2i:l")) != -1) + while ((opt = getopt(argc, argv, "2d:i:l")) != -1) { switch (opt) { case '2': bit_rate = 24000; break; + case 'd': + encoded_file_name = optarg; + break; case 'i': in_file_name = optarg; break; @@ -116,24 +123,39 @@ int main(int argc, char *argv[]) } } - if ((inhandle = sf_open_telephony_read(in_file_name, 1)) == NULL) + if (encoded_file_name) { - fprintf(stderr, " Cannot open audio file '%s'\n", in_file_name); - exit(2); + if ((encoded_fd = open(encoded_file_name, O_RDONLY)) < 0) + { + fprintf(stderr, " Cannot open encoded file '%s'\n", encoded_file_name); + exit(2); + } } + else + { + if ((inhandle = sf_open_telephony_read(in_file_name, 1)) == NULL) + { + fprintf(stderr, " Cannot open audio file '%s'\n", in_file_name); + exit(2); + } + if ((oki_enc_state = oki_adpcm_init(NULL, bit_rate)) == NULL) + { + fprintf(stderr, " Cannot create encoder\n"); + exit(2); + } + } + if ((outhandle = sf_open_telephony_write(OUT_FILE_NAME, 1)) == NULL) { fprintf(stderr, " Cannot create audio file '%s'\n", OUT_FILE_NAME); exit(2); } - - if ((oki_enc_state = oki_adpcm_init(NULL, bit_rate)) == NULL) + if ((oki_dec_state = oki_adpcm_init(NULL, bit_rate)) == NULL) { - fprintf(stderr, " Cannot create encoder\n"); + fprintf(stderr, " Cannot create decoder\n"); exit(2); } - - if ((oki_dec_state = oki_adpcm_init(NULL, bit_rate)) == NULL) + if ((oki_dec_state2 = oki_adpcm_init(NULL, bit_rate)) == NULL) { fprintf(stderr, " Cannot create decoder\n"); exit(2); @@ -151,72 +173,125 @@ int main(int argc, char *argv[]) total_pre_samples = 0; total_compressed_bytes = 0; total_post_samples = 0; - while ((frames = sf_readf_short(inhandle, pre_amp, 159))) + if (encoded_file_name) { - total_pre_samples += frames; - oki_bytes = oki_adpcm_encode(oki_enc_state, oki_data, pre_amp, frames); - if (log_encoded_data) - write(1, oki_data, oki_bytes); - total_compressed_bytes += oki_bytes; - dec_frames = oki_adpcm_decode(oki_dec_state, post_amp, oki_data, oki_bytes); - total_post_samples += dec_frames; - for (i = 0; i < frames; i++) + /* Decode a file of OKI ADPCM code to a linear wave file */ + while ((oki_bytes = read(encoded_fd, oki_data, 80)) > 0) { - history[hist_in++] = pre_amp[i]; - if (hist_in >= HIST_LEN) - hist_in = 0; - pre_energy += (double) pre_amp[i] * (double) pre_amp[i]; + total_compressed_bytes += oki_bytes; + dec_frames = oki_adpcm_decode(oki_dec_state, post_amp, oki_data, oki_bytes); + total_post_samples += dec_frames; + for (i = 0; i < dec_frames; i++) + { + post_energy += (double) post_amp[i] * (double) post_amp[i]; + xx = post_amp[i] - history[hist_out++]; + if (hist_out >= HIST_LEN) + hist_out = 0; + diff_energy += (double) xx * (double) xx; + } + outframes = sf_writef_short(outhandle, post_amp, dec_frames); } - for (i = 0; i < dec_frames; i++) - { - post_energy += (double) post_amp[i] * (double) post_amp[i]; - xx = post_amp[i] - history[hist_out++]; - if (hist_out >= HIST_LEN) - hist_out = 0; - diff_energy += (double) xx * (double) xx; - //post_amp[i] = xx; - } - outframes = sf_writef_short(outhandle, post_amp, dec_frames); + close(encoded_fd); } - if (sf_close(inhandle) != 0) + else { - fprintf(stderr, " Cannot close audio file '%s'\n", in_file_name); - exit(2); + /* Perform a linear wave file -> OKI ADPCM -> linear wave file cycle. Along the way + check the decoder resets on the sequence specified for this codec, and the gain + and worst case sample distortion. */ + successive_08_bytes = 0; + successive_80_bytes = 0; + while ((frames = sf_readf_short(inhandle, pre_amp, 159))) + { + total_pre_samples += frames; + oki_bytes = oki_adpcm_encode(oki_enc_state, oki_data, pre_amp, frames); + if (log_encoded_data) + write(1, oki_data, oki_bytes); + total_compressed_bytes += oki_bytes; + /* Look for a condition which is defined as something which should cause a reset of + the decoder (48 samples of 0, 8, 0, 8, etc.), and verify that it really does. Use + a second decode, which we feed byte by byte, for this. */ + for (i = 0; i < oki_bytes; i++) + { + oki_adpcm_decode(oki_dec_state2, post_amp, &oki_data[i], 1); + if (oki_data[i] == 0x08) + successive_08_bytes++; + else + successive_08_bytes = 0; + if (oki_data[i] == 0x80) + successive_80_bytes++; + else + successive_80_bytes = 0; + if (successive_08_bytes == 24 || successive_80_bytes == 24) + { + if (oki_dec_state2->step_index != 0) + { + fprintf(stderr, "Decoder reset failure\n"); + exit(2); + } + } + } + dec_frames = oki_adpcm_decode(oki_dec_state, post_amp, oki_data, oki_bytes); + total_post_samples += dec_frames; + for (i = 0; i < frames; i++) + { + history[hist_in++] = pre_amp[i]; + if (hist_in >= HIST_LEN) + hist_in = 0; + pre_energy += (double) pre_amp[i] * (double) pre_amp[i]; + } + for (i = 0; i < dec_frames; i++) + { + post_energy += (double) post_amp[i] * (double) post_amp[i]; + xx = post_amp[i] - history[hist_out++]; + if (hist_out >= HIST_LEN) + hist_out = 0; + diff_energy += (double) xx * (double) xx; + //post_amp[i] = xx; + } + outframes = sf_writef_short(outhandle, post_amp, dec_frames); + } + printf("Pre samples: %d\n", total_pre_samples); + printf("Compressed bytes: %d\n", total_compressed_bytes); + printf("Post samples: %d\n", total_post_samples); + + printf("Output energy is %f%% of input energy.\n", 100.0*post_energy/pre_energy); + printf("Residual energy is %f%% of the total.\n", 100.0*diff_energy/post_energy); + if (bit_rate == 32000) + { + if (fabs(1.0 - post_energy/pre_energy) > 0.01 + || + fabs(diff_energy/post_energy) > 0.01) + { + printf("Tests failed.\n"); + exit(2); + } + } + else + { + if (fabs(1.0 - post_energy/pre_energy) > 0.11 + || + fabs(diff_energy/post_energy) > 0.05) + { + printf("Tests failed.\n"); + exit(2); + } + } + + + oki_adpcm_release(oki_enc_state); + if (sf_close(inhandle) != 0) + { + fprintf(stderr, " Cannot close audio file '%s'\n", in_file_name); + exit(2); + } } + oki_adpcm_release(oki_dec_state); + oki_adpcm_release(oki_dec_state2); if (sf_close(outhandle) != 0) { fprintf(stderr, " Cannot close audio file '%s'\n", OUT_FILE_NAME); exit(2); } - oki_adpcm_release(oki_enc_state); - oki_adpcm_release(oki_dec_state); - - printf("Pre samples: %d\n", total_pre_samples); - printf("Compressed bytes: %d\n", total_compressed_bytes); - printf("Post samples: %d\n", total_post_samples); - - printf("Output energy is %f%% of input energy.\n", 100.0*post_energy/pre_energy); - printf("Residual energy is %f%% of the total.\n", 100.0*diff_energy/post_energy); - if (bit_rate == 32000) - { - if (fabs(1.0 - post_energy/pre_energy) > 0.05 - || - fabs(diff_energy/post_energy) > 0.03) - { - printf("Tests failed.\n"); - exit(2); - } - } - else - { - if (fabs(1.0 - post_energy/pre_energy) > 0.20 - || - fabs(diff_energy/post_energy) > 0.10) - { - printf("Tests failed.\n"); - exit(2); - } - } printf("Tests passed.\n"); return 0;