view libgsmhr1/rxfe.c @ 581:e2d5cad04cbf

libgsmhr1 RxFE: store CN R0+LPC separately from speech In the original GSM 06.06 code the ECU for speech mode is entirely separate from the CN generator, maintaining separate state. (The main intertie between them is the speech vs CN state variable, distinguishing between speech and CN BFIs, in addition to the CN-specific function of distinguishing between initial and update SIDs.) In the present RxFE implementation I initially thought that we could use the same saved_frame buffer for both ECU and CN, overwriting just the first 4 params (R0 and LPC) when a valid SID comes in. However, I now realize it was a bad idea: the original code has a corner case (long sequence of speech-mode BFIs to put the ECU in state 6, then SID and CN-mode BFIs, then a good speech frame) that would be broken by that buffer reuse approach. We could eliminate this corner case by resetting the ECU state when passing through a CN insertion period, but doing so would needlessly increase the behavioral diffs between GSM 06.06 and our version. Solution: use a separate CN-specific buffer for CN R0+LPC parameters, and match the behavior of GSM 06.06 code in this regard.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 13 Feb 2025 10:02:45 +0000
parents d4979cdbc081
children
line wrap: on
line source

/*
 * 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->in_dtx = 1;
	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->cn_r0_lpc, 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;
		if (frame_class == VALIDSID)
			memcpy(st->cn_r0_lpc, prm_in, sizeof(Shortword) * 4);
		else
			memcpy(st->cn_r0_lpc, st->saved_frame,
				sizeof(Shortword) * 4);
		init_cn_gen(st);
		cn_output(st, prm_out);
		break;
	case CNICONT:
		if (dtxd_sp)
			*dtxd_sp = 0;
		if (frame_class == VALIDSID)
			memcpy(st->cn_r0_lpc, 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;
		if (st->dtx_muting) {
			if (fast_cn_muting)
				st->cn_r0_lpc[0] = 0;
			else {
				st->cn_r0_lpc[0] -= 2;
				if (st->cn_r0_lpc[0] < 0)
					st->cn_r0_lpc[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;
}