FreeCalypso > hg > gsm-codec-lib
changeset 579:1dc5d9320e96
libgsmhr1: implement RxFE block
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Thu, 13 Feb 2025 09:10:12 +0000 |
parents | 7756b23b78cd |
children | d4979cdbc081 |
files | libgsmhr1/Makefile libgsmhr1/namespace.list libgsmhr1/rxfe.c libgsmhr1/rxfe.h |
diffstat | 4 files changed, 356 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/libgsmhr1/Makefile Thu Feb 13 03:08:35 2025 +0000 +++ b/libgsmhr1/Makefile Thu Feb 13 09:10:12 2025 +0000 @@ -1,9 +1,9 @@ OBJS= dhf_packed.o dhf_params.o dtx_rxfe.o enc_out_order.o mathdp31.o \ mathhalf.o pack_frame.o paramval_cod.o paramval_common.o paramval_dec.o\ - rtp_in.o rtp_in_direct.o sid_cw_params.o sid_detect.o sid_reset.o \ - sp_rom.o twts002_in.o twts002_out.o unpack_frame.o + rtp_in.o rtp_in_direct.o rxfe.o sid_cw_params.o sid_detect.o \ + sid_reset.o sp_rom.o twts002_in.o twts002_out.o unpack_frame.o HDRS= dtx_const.h dtx_rxfe.h enc_out_order.h mathdp31.h mathhalf.h \ - namespace.h sp_rom.h tw_gsmhr.h typedefs.h + namespace.h rxfe.h sp_rom.h tw_gsmhr.h typedefs.h LIB= libgsmhr1.a include ../config.defs
--- a/libgsmhr1/namespace.list Thu Feb 13 03:08:35 2025 +0000 +++ b/libgsmhr1/namespace.list Thu Feb 13 09:10:12 2025 +0000 @@ -22,3 +22,4 @@ psrHPFCoefs psrNWCoefs pL_rFlatSstCoefs psrOldCont psrNewCont ppLr_gsTable avgGsHistQntz gsQuant getPnBits +rxfe_main
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmhr1/rxfe.c Thu Feb 13 09:10:12 2025 +0000 @@ -0,0 +1,316 @@ +/* + * Rx front end implementation, common between the full decoder and TFO. + */ + +#include <stdint.h> +#include <string.h> +#include "tw_gsmhr.h" +#include "typedefs.h" +#include "namespace.h" +#include "rxfe.h" +#include "dtx_const.h" +#include "dtx_rxfe.h" + +/* + * If the very first frame(s) we have to process out of reset (or after DHF) + * are either BFI or invalid SID, what is our default fallback frame? + * In the original GSM 06.06 code it is a frame of all zero params - but + * because that code provides only a full decoder and not TFO, this oddity + * is not readily visible. (The PCM output from the full decoder is all + * zeros.) OTOH, if we feed all zeros as PCM input to a homed standard + * encoder, we get this frame, repeating endlessly as long as all-zeros + * PCM input continues: + * + * R0=00 LPC=164,171,cb Int=0 Mode=0 + * s1=00,00,00 s2=00,00,00 s3=00,00,00 s4=00,00,00 + * + * This frame differs from all-zero params only in the LPC set, and this + * sane-LPC silence frame is the one we shall use as our reset-default + * fallback frame. + */ +static const Shortword default_lpc[3] = {0x164, 0x171, 0xcb}; + +/* + * UFI R0 handling tables: when we get a speech frame marked with BFI=0 UFI=1, + * we look at the R0 parameter, both in the received frame and in the saved + * one. Both of the following LUTs are indexed by old R0 just like in the + * original GSM 06.06 code, but the values in them are absolute thresholds + * for the new R0, instead of diff values used in the original code. + */ +static const Shortword ufi_r0_thresh_bfi[32] = { + 15, 16, 17, 15, 16, 17, 18, 18, + 18, 19, 19, 20, 21, 22, 22, 23, + 23, 23, 23, 24, 25, 25, 26, 26, + 26, 27, 28, 29, 30, 31, 32, 32 +}; + +static const Shortword ufi_r0_thresh_mute[32] = { + 14, 13, 13, 12, 13, 14, 15, 14, + 15, 16, 17, 18, 18, 19, 20, 20, + 21, 21, 21, 22, 23, 24, 25, 25, + 25, 26, 27, 28, 29, 30, 30, 30 +}; + +/* state transition tables for ECU state machine */ +static const uint8_t ecu_state_trans_good[8] = {0, 0, 0, 0, 0, 0, 7, 0}; +static const uint8_t ecu_state_trans_bad[8] = {1, 2, 3, 4, 5, 6, 6, 6}; + +void gsmhr_rxfe_reset(struct gsmhr_rxfe_state *st) +{ + memset(st, 0, sizeof(struct gsmhr_rxfe_state)); + memcpy(st->saved_frame + 1, default_lpc, sizeof(Shortword) * 3); + st->ecu_state = 7; +} + +static Shortword input_frame_class(Shortword bfi, Shortword ufi, Shortword sid) +{ + Shortword bfi_dtx; + + bfi_dtx = bfi || ufi; + if (sid == 2 && bfi_dtx == 0) + return VALIDSID; + if (sid == 0 && bfi_dtx == 0) + return GOODSPEECH; + if (sid == 0 && bfi_dtx != 0) + return UNUSABLE; + return INVALIDSID; +} + +static Shortword compute_last_lag(const Shortword *prm_in) +{ + Shortword accum, delta, i; + + accum = prm_in[6]; + for (i = 0; i < 3; i++) { + delta = prm_in[9 + i * 3] - 8; + accum += delta; + if (accum > 0xFF) + accum = 0xFF; + else if (accum < 0) + accum = 0; + } + return accum; +} + +static void save_speech_frame(struct gsmhr_rxfe_state *st, + const Shortword *prm_in) +{ + memcpy(st->saved_frame, prm_in, sizeof(Shortword) * 6); + if (prm_in[5]) { + /* voiced modes */ + st->saved_frame[6] = compute_last_lag(prm_in); + st->saved_frame[7] = prm_in[7]; + st->saved_frame[9] = 8; + st->saved_frame[10] = prm_in[10]; + st->saved_frame[12] = 8; + st->saved_frame[13] = prm_in[13]; + st->saved_frame[15] = 8; + st->saved_frame[16] = prm_in[16]; + } else { + /* unvoiced mode */ + st->saved_frame[6] = prm_in[6]; + st->saved_frame[7] = prm_in[7]; + st->saved_frame[9] = prm_in[9]; + st->saved_frame[10] = prm_in[10]; + st->saved_frame[12] = prm_in[12]; + st->saved_frame[13] = prm_in[13]; + st->saved_frame[15] = prm_in[15]; + st->saved_frame[16] = prm_in[16]; + } + /* all 4 gsp0 params come from the last subframe */ + st->saved_frame[8] = prm_in[17]; + st->saved_frame[11] = prm_in[17]; + st->saved_frame[14] = prm_in[17]; + st->saved_frame[17] = prm_in[17]; +} + +static void speech_ecu(struct gsmhr_rxfe_state *st, const Shortword *prm_in, + Shortword *prm_out, Shortword *mute_permit, + Shortword *dtxd_sp) +{ + Shortword bfi = prm_in[18]; + Shortword ufi = prm_in[19]; + uint8_t prev_state = st->ecu_state; + + if (ufi && !bfi) { + if (prm_in[0] >= ufi_r0_thresh_bfi[st->saved_frame[0]]) + bfi = 1; + else if (prm_in[0] >= ufi_r0_thresh_mute[st->saved_frame[0]]) { + if (mute_permit) + *mute_permit = 1; + } + } + if (bfi) + st->ecu_state = ecu_state_trans_bad[prev_state]; + else + st->ecu_state = ecu_state_trans_good[prev_state]; + switch (st->ecu_state) { + case 0: + memcpy(prm_out, prm_in, sizeof(Shortword) * GSMHR_NUM_PARAMS); + save_speech_frame(st, prm_in); + return; + /* all remaining cases return the saved frame, differ only in R0 */ + case 1: + case 2: + /* no muting yet */ + break; + case 3: + case 4: + case 5: + st->saved_frame[0] -= 2; + if (st->saved_frame[0] < 0) + st->saved_frame[0] = 0; + break; + case 6: + case 7: + st->saved_frame[0] = 0; + break; + } + memcpy(prm_out, st->saved_frame, sizeof(Shortword) * GSMHR_NUM_PARAMS); + if (bfi < 2) { + if (st->saved_frame[5] == 0 && prm_in[5] == 0) { + /* both unvoiced */ + prm_out[6] = prm_in[6]; + prm_out[7] = prm_in[7]; + prm_out[9] = prm_in[9]; + prm_out[10] = prm_in[10]; + prm_out[12] = prm_in[12]; + prm_out[13] = prm_in[13]; + prm_out[15] = prm_in[15]; + prm_out[16] = prm_in[16]; + } else if (st->saved_frame[5] != 0 && prm_in[5] != 0) { + /* both voiced */ + prm_out[7] = prm_in[7]; + prm_out[10] = prm_in[10]; + prm_out[13] = prm_in[13]; + prm_out[16] = prm_in[16]; + } + } + if (dtxd_sp && prev_state >= 6) + *dtxd_sp = 0; +} + +static void save_speech_gs(struct gsmhr_rxfe_state *st, + const Shortword *sp_param) +{ + Shortword vmode = sp_param[5]; + uint8_t ptr = st->gs_history_ptr; + int i; + + for (i = 0; i < N_SUB; i++) { + st->gs_history[ptr] = ppLr_gsTable[vmode][sp_param[8 + i * 3]]; + ptr++; + if (ptr >= GS_HISTORY_SIZE) + ptr = 0; + } + st->gs_history_ptr = ptr; +} + +static void init_cn_gen(struct gsmhr_rxfe_state *st) +{ + Longword L_RxDTXGs; + + st->cn_prng = PN_INIT_SEED; + avgGsHistQntz(st->gs_history, &L_RxDTXGs); + st->gs_cn_out = gsQuant(L_RxDTXGs, 0); + st->dtx_bfi_count = 0; + st->dtx_muting = 0; +} + +static void cn_output(struct gsmhr_rxfe_state *st, Shortword *prm_out) +{ + int i; + + memcpy(prm_out, st->saved_frame, sizeof(Shortword) * 4); + prm_out[4] = 1; /* soft interpolation */ + prm_out[5] = 0; /* unvoiced mode */ + for (i = 0; i < N_SUB; i++) { + prm_out[6 + i * 3] = getPnBits(7, &st->cn_prng); + prm_out[7 + i * 3] = getPnBits(7, &st->cn_prng); + prm_out[8 + i * 3] = st->gs_cn_out; + } +} + +void rxfe_main(struct gsmhr_rxfe_state *st, const Shortword *prm_in, + Shortword *prm_out, int fast_cn_muting, + Shortword *deco_mode_out, Shortword *mute_permit, + Shortword *dtxd_sp) +{ + Shortword frame_class, deco_mode; + + frame_class = input_frame_class(prm_in[18], prm_in[19], prm_in[20]); + if (!st->in_dtx) { + /* speech decoding mode */ + if (frame_class == VALIDSID || frame_class == INVALIDSID) + deco_mode = CNIFIRSTSID; + else + deco_mode = SPEECH; + } else { + /* comfort noise insertion mode */ + if (frame_class == VALIDSID || frame_class == INVALIDSID) + deco_mode = CNICONT; + else if (frame_class == UNUSABLE) + deco_mode = CNIBFI; + else + deco_mode = SPEECH; + } + /* Muting permission for the full decoder will be set only in one + * special case of speech UFI - default it to 0. + */ + if (mute_permit) + *mute_permit = 0; + /* now real per-mode processing */ + switch (deco_mode) { + case SPEECH: + /* Except for the special case of prolonged BFI that + * led to total muting, TFO with DTXd gets SP=1. + */ + if (dtxd_sp) + *dtxd_sp = 1; + st->in_dtx = 0; + speech_ecu(st, prm_in, prm_out, mute_permit, dtxd_sp); + save_speech_gs(st, prm_out); + break; + case CNIFIRSTSID: + if (dtxd_sp) + *dtxd_sp = 0; + st->in_dtx = 1; + if (frame_class == VALIDSID) + memcpy(st->saved_frame, prm_in, sizeof(Shortword) * 4); + init_cn_gen(st); + cn_output(st, prm_out); + break; + case CNICONT: + if (dtxd_sp) + *dtxd_sp = 0; + st->in_dtx = 1; + if (frame_class == VALIDSID) + memcpy(st->saved_frame, prm_in, sizeof(Shortword) * 4); + else + deco_mode = CNIBFI; /* for interpolation */ + st->dtx_bfi_count = 0; + st->dtx_muting = 0; + cn_output(st, prm_out); + break; + case CNIBFI: + if (dtxd_sp) + *dtxd_sp = 0; + st->in_dtx = 1; + if (st->dtx_muting) { + if (fast_cn_muting) + st->saved_frame[0] = 0; + else { + st->saved_frame[0] -= 2; + if (st->saved_frame[0] < 0) + st->saved_frame[0] = 0; + } + } + st->dtx_bfi_count++; + if (st->dtx_bfi_count >= (prm_in[21] ? 25 : 36)) + st->dtx_muting = 1; + cn_output(st, prm_out); + break; + } + if (deco_mode_out) + *deco_mode_out = deco_mode; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmhr1/rxfe.h Thu Feb 13 09:10:12 2025 +0000 @@ -0,0 +1,36 @@ +/* + * This library-internal header file provides definition for + * struct gsmhr_rxfe_state, the state structure for our Rx front end + * that can function either as part of the full endpoint decoder + * or standalone as a TFO transform. The internal interface function + * to the RxFE block as a whole (to be called from the full decoder + * or from the TFO wrapper) is also declared here. + */ + +#ifndef rxfe_h +#define rxfe_h + +#include <stdint.h> +#include "tw_gsmhr.h" +#include "typedefs.h" + +#define GS_HISTORY_SIZE 28 + +struct gsmhr_rxfe_state { + Shortword saved_frame[GSMHR_NUM_PARAMS]; + Longword gs_history[GS_HISTORY_SIZE]; + Longword cn_prng; + Shortword gs_cn_out; + uint8_t in_dtx; + uint8_t ecu_state; + uint8_t dtx_bfi_count; + uint8_t dtx_muting; + uint8_t gs_history_ptr; +}; + +void rxfe_main(struct gsmhr_rxfe_state *st, const Shortword *prm_in, + Shortword *prm_out, int fast_cn_muting, + Shortword *deco_mode_out, Shortword *mute_permit, + Shortword *dtxd_sp); + +#endif /* include guard */