FreeCalypso > hg > gsm-codec-lib
changeset 256:a33edf624061
libgsmfr2: start with API definition and port of libgsmfrp code
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Fri, 12 Apr 2024 20:49:53 +0000 |
parents | 07f936338de1 |
children | 77b7b7b7b384 |
files | libgsmfr2/Makefile libgsmfr2/comfort_noise.c libgsmfr2/pp_bad.c libgsmfr2/pp_good.c libgsmfr2/pp_internal.h libgsmfr2/pp_state.c libgsmfr2/prng.c libgsmfr2/sidclass.c libgsmfr2/silence_frame.c libgsmfr2/tw_gsmfr.h libgsmfr2/xmaxc_mean.c |
diffstat | 11 files changed, 700 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmfr2/Makefile Fri Apr 12 20:49:53 2024 +0000 @@ -0,0 +1,25 @@ +CC= gcc +CFLAGS= -O2 +OBJS= comfort_noise.o pp_bad.o pp_good.o pp_state.o prng.o sidclass.o \ + silence_frame.o xmaxc_mean.o +HDRS= pp_internal.h tw_gsmfr.h +LIB= libgsmfr2.a + +INSTALL_PREFIX= /usr/local + +all: ${LIB} + +${OBJS}: ${HDRS} + +${LIB}: ${OBJS} + ar rcu $@ ${OBJS} + ranlib $@ + +install: + mkdir -p ${INSTALL_PREFIX}/include + install -c -m 444 tw_gsmfr.h ${INSTALL_PREFIX}/include + mkdir -p ${INSTALL_PREFIX}/lib + install -c -m 444 ${LIB} ${INSTALL_PREFIX}/lib + +clean: + rm -f *.[oa] errs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmfr2/comfort_noise.c Fri Apr 12 20:49:53 2024 +0000 @@ -0,0 +1,111 @@ +/* + * In this module we implement comfort noise generation per GSM 06.12 + * or 3GPP TS 46.012. + */ + +#include <stdint.h> +#include <string.h> +#include "tw_gsmfr.h" +#include "pp_internal.h" + +static const uint8_t fold_table_8to6[24] = { + 1, 2, 3, 4, 5, 6, 1, 2, + 1, 2, 3, 4, 5, 6, 3, 4, + 1, 2, 3, 4, 5, 6, 5, 6, +}; + +static const uint8_t bc[4] = {0, 0, 0, 0}; +static const uint8_t Nc[4] = {40, 120, 40, 120}; + +static uint8_t random_1to6(struct gsmfr_preproc_state *st) +{ + uint8_t range8, range6; + + range8 = gsmfr_preproc_prng(st, 3); + range6 = fold_table_8to6[(st->cn_random_6fold << 3) | range8]; + st->cn_random_6fold++; + if (st->cn_random_6fold >= 3) + st->cn_random_6fold = 0; + return range6; +} + +void gsmfr_preproc_gen_cn(struct gsmfr_preproc_state *st, uint8_t *frame) +{ + unsigned sub, pulse; + uint8_t Mc, xmc[13]; + uint8_t *c; + + /* global bytes (magic and LARc) are fixed */ + memcpy(frame, st->sid_prefix, 5); + c = frame + 5; + /* now do the 4 subframes, mostly PRNG output */ + for (sub = 0; sub < 4; sub++) { + Mc = gsmfr_preproc_prng(st, 2); + for (pulse = 0; pulse < 13; pulse++) + xmc[pulse] = random_1to6(st); + /* packing code from libgsm */ + *c++ = ((Nc[sub] & 0x7F) << 1) + | ((bc[sub] >> 1) & 0x1); + *c++ = ((bc[sub] & 0x1) << 7) + | ((Mc & 0x3) << 5) + | ((st->sid_xmaxc >> 1) & 0x1F); + *c++ = ((st->sid_xmaxc & 0x1) << 7) + | ((xmc[0] & 0x7) << 4) + | ((xmc[1] & 0x7) << 1) + | ((xmc[2] >> 2) & 0x1); + *c++ = ((xmc[2] & 0x3) << 6) + | ((xmc[3] & 0x7) << 3) + | (xmc[4] & 0x7); + *c++ = ((xmc[5] & 0x7) << 5) + | ((xmc[6] & 0x7) << 2) + | ((xmc[7] >> 1) & 0x3); + *c++ = ((xmc[7] & 0x1) << 7) + | ((xmc[8] & 0x7) << 4) + | ((xmc[9] & 0x7) << 1) + | ((xmc[10] >> 2) & 0x1); + *c++ = ((xmc[10] & 0x3) << 6) + | ((xmc[11] & 0x7) << 3) + | (xmc[12] & 0x7); + } +} + +void gsmfr_preproc_sid2cn(struct gsmfr_preproc_state *st, uint8_t *frame) +{ + unsigned sub, pulse; + uint8_t Mc, xmc[13]; + uint8_t *c; + + /* save LARc and Xmaxc from the last subframe for subsequent CN gen */ + memcpy(st->sid_prefix, frame, 5); + st->sid_xmaxc = ((frame[27] & 0x1F) << 1) | (frame[28] >> 7); + /* ... and turn *this* frame into very first CN output */ + c = frame + 5; + for (sub = 0; sub < 4; sub++) { + Mc = gsmfr_preproc_prng(st, 2); + for (pulse = 0; pulse < 13; pulse++) + xmc[pulse] = random_1to6(st); + /* keep each of Xmaxc and replace the rest with CN */ + *c++ = ((Nc[sub] & 0x7F) << 1) + | ((bc[sub] >> 1) & 0x1); + *c &= 0x1F; + *c++ |= ((bc[sub] & 0x1) << 7) + | ((Mc & 0x3) << 5); + *c &= 0x80; + *c++ |= ((xmc[0] & 0x7) << 4) + | ((xmc[1] & 0x7) << 1) + | ((xmc[2] >> 2) & 0x1); + *c++ = ((xmc[2] & 0x3) << 6) + | ((xmc[3] & 0x7) << 3) + | (xmc[4] & 0x7); + *c++ = ((xmc[5] & 0x7) << 5) + | ((xmc[6] & 0x7) << 2) + | ((xmc[7] >> 1) & 0x3); + *c++ = ((xmc[7] & 0x1) << 7) + | ((xmc[8] & 0x7) << 4) + | ((xmc[9] & 0x7) << 1) + | ((xmc[10] >> 2) & 0x1); + *c++ = ((xmc[10] & 0x3) << 6) + | ((xmc[11] & 0x7) << 3) + | (xmc[12] & 0x7); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmfr2/pp_bad.c Fri Apr 12 20:49:53 2024 +0000 @@ -0,0 +1,137 @@ +/* + * In this module we implement our handling of BFI frame gaps + * and invalid SID frames. + */ + +#include <stdint.h> +#include <string.h> +#include "tw_gsmfr.h" +#include "pp_internal.h" + +static int reduce_xmaxc(uint8_t *frame) +{ + int mute_flag = 1; + unsigned sub, xmaxc; + + for (sub = 0; sub < 4; sub++) { + xmaxc = ((frame[sub*7+6] & 0x1F) << 1) | (frame[sub*7+7] >> 7); + if (xmaxc > 4) { + xmaxc -= 4; + mute_flag = 0; + } else + xmaxc = 0; + frame[sub*7+6] &= 0xE0; + frame[sub*7+6] |= xmaxc >> 1; + frame[sub*7+7] &= 0x7F; + frame[sub*7+7] |= (xmaxc & 1) << 7; + } + return mute_flag; +} + +static void random_grid_pos(struct gsmfr_preproc_state *st, uint8_t *frame) +{ + unsigned sub, Mc; + + for (sub = 0; sub < 4; sub++) { + Mc = gsmfr_preproc_prng(st, 2); + frame[sub*7+6] &= 0x9F; + frame[sub*7+6] |= Mc << 5; + } +} + +static int reduce_xmaxc_sid(struct gsmfr_preproc_state *st) +{ + if (st->sid_xmaxc > 4) { + st->sid_xmaxc -= 4; + return 0; + } else { + st->sid_xmaxc = 0; + return 1; + } +} + +void gsmfr_preproc_bfi(struct gsmfr_preproc_state *st, int taf, uint8_t *frame) +{ + int mute; + + switch (st->rx_state) { + case NO_DATA: + memcpy(frame, &gsmfr_preproc_silence_frame, + GSMFR_RTP_FRAME_LEN); + return; + case SPEECH: + memcpy(frame, &st->speech_frame, GSMFR_RTP_FRAME_LEN); + st->rx_state = SPEECH_MUTING; + return; + case SPEECH_MUTING: + mute = reduce_xmaxc(st->speech_frame); + memcpy(frame, &st->speech_frame, GSMFR_RTP_FRAME_LEN); + random_grid_pos(st, frame); + if (mute) + st->rx_state = NO_DATA; + return; + case COMFORT_NOISE: + if (taf) + st->rx_state = LOST_SID; + gsmfr_preproc_gen_cn(st, frame); + return; + case LOST_SID: + if (taf) { + st->rx_state = CN_MUTING; + reduce_xmaxc_sid(st); + } + gsmfr_preproc_gen_cn(st, frame); + return; + case CN_MUTING: + if (reduce_xmaxc_sid(st)) { + st->rx_state = NO_DATA; + memcpy(frame, &gsmfr_preproc_silence_frame, + GSMFR_RTP_FRAME_LEN); + } else + gsmfr_preproc_gen_cn(st, frame); + return; + } +} + +void gsmfr_preproc_invalid_sid(struct gsmfr_preproc_state *st, uint8_t *frame) +{ + int mute; + + switch (st->rx_state) { + case NO_DATA: + memcpy(frame, &gsmfr_preproc_silence_frame, + GSMFR_RTP_FRAME_LEN); + return; + case SPEECH: + /* + * Make CN out of the last good speech frame, following the + * "NOTE" at the end of section 6.1.2 in TS 46.031. + */ + st->rx_state = COMFORT_NOISE; + memcpy(st->sid_prefix, &st->speech_frame, 5); + st->sid_xmaxc = gsmfr_preproc_xmaxc_mean(st->speech_frame); + gsmfr_preproc_gen_cn(st, frame); + return; + case SPEECH_MUTING: + /* ignore invalid SID in this state and act as if we got BFI */ + mute = reduce_xmaxc(st->speech_frame); + memcpy(frame, &st->speech_frame, GSMFR_RTP_FRAME_LEN); + random_grid_pos(st, frame); + if (mute) + st->rx_state = NO_DATA; + return; + case COMFORT_NOISE: + case LOST_SID: + st->rx_state = COMFORT_NOISE; + gsmfr_preproc_gen_cn(st, frame); + return; + case CN_MUTING: + if (reduce_xmaxc_sid(st)) { + st->rx_state = NO_DATA; + memcpy(frame, &gsmfr_preproc_silence_frame, + GSMFR_RTP_FRAME_LEN); + } else + gsmfr_preproc_gen_cn(st, frame); + return; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmfr2/pp_good.c Fri Apr 12 20:49:53 2024 +0000 @@ -0,0 +1,32 @@ +/* + * In this module we implement preprocessing of received good frames. + */ + +#include <stdint.h> +#include <string.h> +#include "tw_gsmfr.h" +#include "pp_internal.h" + +void gsmfr_preproc_good_frame(struct gsmfr_preproc_state *st, uint8_t *frame) +{ + int sid; + + /* always set correct magic */ + frame[0] = 0xD0 | frame[0] & 0x0F; + /* now classify by SID */ + sid = gsmfr_preproc_sid_classify(frame); + switch (sid) { + case 0: /* good speech frame */ + st->rx_state = SPEECH; + memcpy(&st->speech_frame, frame, GSMFR_RTP_FRAME_LEN); + return; + case 1: /* invalid SID frame */ + /* state-dependent handling, similar to BFI */ + gsmfr_preproc_invalid_sid(st, frame); + return; + case 2: /* valid SID frame */ + st->rx_state = COMFORT_NOISE; + gsmfr_preproc_sid2cn(st, frame); + return; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmfr2/pp_internal.h Fri Apr 12 20:49:53 2024 +0000 @@ -0,0 +1,36 @@ +/* + * This header file is internal to libgsmfr2; + * here we define our state structure for the Rx DTX preprocessor component. + */ + +enum rx_dtx_st { + NO_DATA = 0, + SPEECH, + SPEECH_MUTING, + COMFORT_NOISE, + LOST_SID, + CN_MUTING, +}; + +struct gsmfr_preproc_state { + enum rx_dtx_st rx_state; + uint8_t speech_frame[GSMFR_RTP_FRAME_LEN]; + uint8_t sid_prefix[5]; + uint8_t sid_xmaxc; + uint32_t cn_random_lfsr; + unsigned cn_random_6fold; +}; + +/* we use the same LFSR PRNG for CN as ETSI EFR implementation */ +#define PN_INITIAL_SEED 0x70816958L /* Pseudo noise generator seed value */ + +/* internal functions */ +extern void gsmfr_preproc_gen_cn(struct gsmfr_preproc_state *state, + uint8_t *frame); +extern void gsmfr_preproc_sid2cn(struct gsmfr_preproc_state *state, + uint8_t *frame); +extern void gsmfr_preproc_invalid_sid(struct gsmfr_preproc_state *state, + uint8_t *frame); +extern uint16_t gsmfr_preproc_prng(struct gsmfr_preproc_state *state, + uint16_t no_bits); +extern uint8_t gsmfr_preproc_xmaxc_mean(const uint8_t *frame);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmfr2/pp_state.c Fri Apr 12 20:49:53 2024 +0000 @@ -0,0 +1,26 @@ +/* + * In this module we implement allocation and initialization + * of state structures for our GSM FR preprocessor. + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include "tw_gsmfr.h" +#include "pp_internal.h" + +struct gsmfr_preproc_state *gsmfr_preproc_create(void) +{ + struct gsmfr_preproc_state *st; + + st = malloc(sizeof(struct gsmfr_preproc_state)); + if (st) + gsmfr_preproc_reset(st); + return st; +} + +void gsmfr_preproc_reset(struct gsmfr_preproc_state *st) +{ + memset(st, 0, sizeof(struct gsmfr_preproc_state)); + st->cn_random_lfsr = PN_INITIAL_SEED; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmfr2/prng.c Fri Apr 12 20:49:53 2024 +0000 @@ -0,0 +1,53 @@ +/* + * We use a pseudorandom sequence generator function in two places: + * when generating comfort noise (GSM 06.12 section 6.1), and when + * randomizing grid position parameters as part of speech muting + * during error handling (GSM 06.11 section 6). + * + * Our PRNG is a copy of the pseudonoise() function from ETSI EFR code, + * where it is used for similar purposes. + */ + +#include <stdint.h> +#include "tw_gsmfr.h" +#include "pp_internal.h" + +uint16_t gsmfr_preproc_prng(struct gsmfr_preproc_state *st, uint16_t no_bits) +{ + uint16_t noise_bits, Sn, i; + + noise_bits = 0; + for (i = 0; i < no_bits; i++) + { + /* State n == 31 */ + if ((st->cn_random_lfsr & 0x00000001L) != 0) + { + Sn = 1; + } + else + { + Sn = 0; + } + + /* State n == 3 */ + if ((st->cn_random_lfsr & 0x10000000L) != 0) + { + Sn = Sn ^ 1; + } + else + { + Sn = Sn ^ 0; + } + + noise_bits = noise_bits << 1; + noise_bits = noise_bits | st->cn_random_lfsr & 1; + + st->cn_random_lfsr >>= 1; + if (Sn & 1) + { + st->cn_random_lfsr |= 0x40000000L; + } + } + + return noise_bits; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmfr2/sidclass.c Fri Apr 12 20:49:53 2024 +0000 @@ -0,0 +1,51 @@ +/* + * gsmfr_preproc_sid_classify() utility function classifies + * a GSM 06.10 frame in RTP encoding according to the rules + * of GSM 06.31 (or 3GPP TS 46.031) section 6.1.1, judging it + * as SID=0, SID=1 or SID=2. + */ + +#include <stdint.h> +#include "tw_gsmfr.h" + +static const unsigned short sid_field_bits[95] = { + 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73, + 75, 76, 78, 79, 81, 82, 84, 85, 87, 88, 90, 91, + 93, 94, 113, 114, 116, 117, 119, 120, 122, 123, + 125, 126, 128, 129, 131, 132, 134, 135, 137, + 138, 140, 141, 143, 144, 146, 147, 149, 150, + 169, 170, 172, 173, 175, 176, 178, 179, 181, + 182, 184, 185, 187, 188, 190, 191, 193, 194, + 196, 197, 199, 200, 202, 203, 205, 206, 225, + 226, 228, 229, 231, 232, 234, 235, 237, 240, + 243, 246, 249, 252, 255, 258, 261 +}; + +static inline int get_bit(const uint8_t *frame, unsigned bitnum) +{ + unsigned bytenum = bitnum >> 3; + unsigned bit_in_byte = 7 - (bitnum & 7); + unsigned bitmask = 1 << bit_in_byte; + + if (frame[bytenum] & bitmask) + return 1; + else + return 0; +} + +int gsmfr_preproc_sid_classify(const uint8_t *frame) +{ + unsigned idx, n; + + n = 0; + for (idx = 0; idx < 95; idx++) { + if (get_bit(frame, sid_field_bits[idx])) + n++; + if (n >= 16) + return 0; + } + if (n < 2) + return 2; + else + return 1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmfr2/silence_frame.c Fri Apr 12 20:49:53 2024 +0000 @@ -0,0 +1,16 @@ +/* + * Table 1 in section 6 of 3GPP TS 46.011 specifies a silence frame + * in the form of GSM 06.10 parameters; here we implement this exact + * silence frame in libgsm encoding, which is also RTP encoding. + */ + +#include <stdint.h> +#include "tw_gsmfr.h" + +const uint8_t gsmfr_preproc_silence_frame[GSMFR_RTP_FRAME_LEN] = { + 0xDA, 0xA7, 0xAA, 0xA5, 0x1A, + 0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B, + 0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B, + 0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B, + 0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B, +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmfr2/tw_gsmfr.h Fri Apr 12 20:49:53 2024 +0000 @@ -0,0 +1,113 @@ +/* + * This header file is the external public interface to libgsmfr2: + * Themyscira Wireless implementation of GSM FRv1 speech codec + * that includes not only the core transcoding functions of GSM 06.10, + * but also Rx DTX functions (substitution and muting per GSM 06.11, + * comfort noise per GSM 06.12, overall Rx DTX control per GSM 06.31) + * in the decoder direction and the optional homing frame mechanism. + * + * This header file should be installed in some system include directory + * such that it can be included by C sources as <tw_gsmfr.h>. + */ + +#ifndef __THEMWI_GSMFR_H +#define __THEMWI_GSMFR_H + +#include <stdint.h> + +#define GSMFR_RTP_FRAME_LEN 33 +#define GSMFR_NUM_PARAMS 76 + +/* + * GSM 06.10 encoder & decoder portion of the library + * + * This part is peculiar in that the same state structure is used for + * both the encoder and the decoder - however, each given instance + * of this state structure must be used for only one of the two. + */ + +struct gsmfr_0610_state; /* opaque to external users */ + +struct gsmfr_0610_state *gsmfr_0610_create(void); +/* use standard free() call to free it afterward */ + +/* reset state to initial */ +void gsmfr_0610_reset(struct gsmfr_0610_state *state); + +/* interface structure for passing frames of codec parameters */ + +struct gsmfr_param_frame { + int16_t LARc[8]; + int16_t Nc[4]; + int16_t bc[4]; + int16_t Mc[4]; + int16_t xmaxc[4]; + int16_t xMc[4][13]; +}; + +/* encoder public functions */ + +void gsmfr_0610_encode_params(struct gsmfr_0610_state *st, const int16_t *pcm, + struct gsmfr_param_frame *param); +void gsmfr_0610_encode_frame(struct gsmfr_0610_state *st, const int16_t *pcm, + uint8_t *frame); +void gsmfr_0610_encoder_homing(struct gsmfr_0610_state *st, const int16_t *pcm); + +/* decoder public functions */ + +void gsmfr_0610_decode_params(struct gsmfr_0610_state *st, + const struct gsmfr_param_frame *param, + int16_t *pcm); +void gsmfr_0610_decode_frame(struct gsmfr_0610_state *st, const uint8_t *frame, + int16_t *pcm); + +/* conversion between RTP packed format and struct gsmfr_param_frame */ + +void gsmfr_pack_frame(const struct gsmfr_param_frame *param, uint8_t *frame); +void gsmfr_unpack_frame(const uint8_t *frame, struct gsmfr_param_frame *param); + +/* Rx DTX handler preprocessor portion of the library */ + +struct gsmfr_preproc_state; /* opaque to external users */ + +struct gsmfr_preproc_state *gsmfr_preproc_create(void); +/* use standard free() call to free it afterward */ + +/* reset state to initial */ +void gsmfr_preproc_reset(struct gsmfr_preproc_state *state); + +/* main processing functions */ +void gsmfr_preproc_good_frame(struct gsmfr_preproc_state *state, + uint8_t *frame); +void gsmfr_preproc_bfi(struct gsmfr_preproc_state *state, int taf, + uint8_t *frame_out); + +/* utility function */ +int gsmfr_preproc_sid_classify(const uint8_t *frame); + +/* utility datum */ +extern const uint8_t gsmfr_preproc_silence_frame[GSMFR_RTP_FRAME_LEN]; + +/* + * Full GSM-FR decoder: Rx DTX handler followed by 06.10 decoder, + * plus the decoder homing function. + */ + +struct gsmfr_fulldec_state; /* opaque to external users */ + +struct gsmfr_fulldec_state *gsmfr_fulldec_create(void); +/* use standard free() call to free it afterward */ + +/* reset state to initial */ +void gsmfr_fulldec_reset(struct gsmfr_fulldec_state *state); + +/* main processing functions */ +void gsmfr_fulldec_good_frame(struct gsmfr_fulldec_state *state, + const uint8_t *frame, int16_t *pcm); +void gsmfr_fulldec_bfi(struct gsmfr_fulldec_state *state, int taf, + int16_t *pcm); + +/* utility datum */ +extern const uint8_t gsmfr_decoder_homing_frame[GSMFR_RTP_FRAME_LEN]; + +#endif /* include guard */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmfr2/xmaxc_mean.c Fri Apr 12 20:49:53 2024 +0000 @@ -0,0 +1,100 @@ +/* + * The function implemented in this module computes a mean Xmaxc + * for comfort noise generation from the 4 subframe Xmaxc parameters + * extracted from an input frame. + */ + +#include <stdint.h> +#include "tw_gsmfr.h" +#include "pp_internal.h" + +static const uint16_t dequant_table[64] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 36, 40, 44, 48, 52, 56, 60, + 64, 72, 80, 88, 96, 104, 112, 120, + 128, 144, 160, 176, 192, 208, 224, 240, + 256, 288, 320, 352, 384, 416, 448, 480, + 512, 576, 640, 704, 768, 832, 896, 960, +}; + +static const uint8_t requant_table[1024] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, + 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, + 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, + 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, + 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, + 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, + 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, +}; + +uint8_t gsmfr_preproc_xmaxc_mean(const uint8_t *frame) +{ + unsigned sub, xmaxc, sum; + + sum = 0; + for (sub = 0; sub < 4; sub++) { + xmaxc = ((frame[sub*7+6] & 0x1F) << 1) | (frame[sub*7+7] >> 7); + sum += dequant_table[xmaxc]; + } + xmaxc = requant_table[sum >> 2]; + return xmaxc; +}