diff options
author | Sylvain Munaut <tnt@246tNt.com> | 2013-12-08 19:24:02 +0100 |
---|---|---|
committer | Sylvain Munaut <tnt@246tNt.com> | 2013-12-17 18:15:42 +0100 |
commit | e8730785b39a5881c0233155ecc5fc0bfea935de (patch) | |
tree | dd6ffe64363440cd0765519ad9c75b8e9430d78a | |
parent | 255a32a391afdc4a04e087c4e2cb467e47069a5a (diff) |
codec: First code import
Lots of fixups still needed before merge into master
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | include/osmocom/gmr1/Makefile.am | 2 | ||||
-rw-r--r-- | include/osmocom/gmr1/codec/Makefile.am | 2 | ||||
-rw-r--r-- | include/osmocom/gmr1/codec/codec.h | 50 | ||||
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/codec/Makefile.am | 8 | ||||
-rw-r--r-- | src/codec/ambe.c | 140 | ||||
-rw-r--r-- | src/codec/codec.c | 103 | ||||
-rw-r--r-- | src/codec/frame.c | 205 | ||||
-rw-r--r-- | src/codec/math.c | 108 | ||||
-rw-r--r-- | src/codec/mbelib.c | 517 | ||||
-rw-r--r-- | src/codec/mbelib.h | 51 | ||||
-rw-r--r-- | src/codec/private.h | 133 | ||||
-rw-r--r-- | src/codec/tables.c | 1134 | ||||
-rw-r--r-- | src/codec/tone.c | 210 | ||||
-rw-r--r-- | src/gmr1_ambe_decode.c | 195 |
16 files changed, 2864 insertions, 3 deletions
diff --git a/configure.ac b/configure.ac index 60c850b..5fc0560 100644 --- a/configure.ac +++ b/configure.ac @@ -45,9 +45,11 @@ AC_OUTPUT( include/Makefile include/osmocom/Makefile include/osmocom/gmr1/Makefile + include/osmocom/gmr1/codec/Makefile include/osmocom/gmr1/l1/Makefile include/osmocom/gmr1/sdr/Makefile src/Makefile + src/codec/Makefile src/l1/Makefile src/sdr/Makefile Makefile diff --git a/include/osmocom/gmr1/Makefile.am b/include/osmocom/gmr1/Makefile.am index 91c5c44..c6d331f 100644 --- a/include/osmocom/gmr1/Makefile.am +++ b/include/osmocom/gmr1/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = l1 sdr +SUBDIRS = codec l1 sdr noinst_HEADERS = gsmtap.h diff --git a/include/osmocom/gmr1/codec/Makefile.am b/include/osmocom/gmr1/codec/Makefile.am new file mode 100644 index 0000000..e6e9cdc --- /dev/null +++ b/include/osmocom/gmr1/codec/Makefile.am @@ -0,0 +1,2 @@ +noinst_HEADERS = \ + codec.h diff --git a/include/osmocom/gmr1/codec/codec.h b/include/osmocom/gmr1/codec/codec.h new file mode 100644 index 0000000..ff92e89 --- /dev/null +++ b/include/osmocom/gmr1/codec/codec.h @@ -0,0 +1,50 @@ +/* GMR-1 AMBE vocoder */ + +/* (C) 2013 by Sylvain Munaut <tnt@246tNt.com> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OSMO_GMR1_CODEC_H__ +#define __OSMO_GMR1_CODEC_H__ + +/*! \defgroup codec AMBE vocoder + * \ingroup codec + * @{ + */ + +/*! \file codec/codec.h + * \brief Osmocom GMR-1 AMBE vocoder header + */ + +#include <stdint.h> + + +struct gmr1_codec; + +struct gmr1_codec *gmr1_codec_alloc(void); +void gmr1_codec_release(struct gmr1_codec *codec); + +int gmr1_codec_decode_frame(struct gmr1_codec *codec, + int16_t *audio, int N, + const uint8_t *frame, int bad); + +int gmr1_codec_decode_dtx(struct gmr1_codec *codec, + int16_t *audio, int N); + + +/*! @} */ + +#endif /* __OSMO_GMR1_CODEC_H__ */ diff --git a/src/Makefile.am b/src/Makefile.am index 7887412..bedaad0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,10 +1,10 @@ -SUBDIRS = l1 sdr +SUBDIRS = codec l1 sdr AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMODSP_CFLAGS) AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMODSP_LIBS) -bin_PROGRAMS = gmr1_rx gmr1_gen_mat +bin_PROGRAMS = gmr1_rx gmr1_gen_mat gmr1_ambe_decode gmr1_rx_SOURCES = gmr1_rx.c gsmtap.c gmr1_rx_LDADD = $(top_builddir)/src/l1/libgmr1-l1.a \ @@ -13,3 +13,6 @@ gmr1_rx_LDADD = $(top_builddir)/src/l1/libgmr1-l1.a \ gmr1_gen_mat_SOURCES = gmr1_gen_mat.c gmr1_gen_mat_LDADD = $(top_builddir)/src/l1/libgmr1-l1.a + +gmr1_ambe_decode_SOURCES = gmr1_ambe_decode.c +gmr1_ambe_decode_LDADD = $(top_builddir)/src/codec/libgmr1-codec.a diff --git a/src/codec/Makefile.am b/src/codec/Makefile.am new file mode 100644 index 0000000..bdecf58 --- /dev/null +++ b/src/codec/Makefile.am @@ -0,0 +1,8 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) + +noinst_LIBRARIES = libgmr1-codec.a + +libgmr1_codec_a_SOURCES = \ + ambe.c codec.c frame.c math.c mbelib.c tables.c tone.c diff --git a/src/codec/ambe.c b/src/codec/ambe.c new file mode 100644 index 0000000..ea46b20 --- /dev/null +++ b/src/codec/ambe.c @@ -0,0 +1,140 @@ +/* GMR-1 AMBE vocoder - internal API */ + +/* (C) 2013 by Sylvain Munaut <tnt@246tNt.com> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/*! \addtogroup codec/private + * @{ + */ + +/*! \file codec/ambe.c + * \brief Osmocom GMR-1 AMBE internal API + */ + +#include <errno.h> +#include <math.h> +#include <stdint.h> +#include <string.h> + +#include "private.h" + + +void +ambe_decode_init(struct ambe_decoder *dec) +{ + memset(dec, 0x00, sizeof(struct ambe_decoder)); + mbe_initMbeParms(&dec->mp_cur, &dec->mp_prev, &dec->mp_prev_enh); +} + +void +ambe_decode_fini(struct ambe_decoder *dec) +{ + /* nothing to do */ +} + + +static enum ambe_frame_type +ambe_classify_frame(const uint8_t *frame) +{ + switch (frame[0] & 0xfc) { + case 0xfc: + return AMBE_TONE; + + case 0xf8: + return AMBE_SILENCE; + + default: + return AMBE_SPEECH; + }; +} + +static int +ambe_decode_speech(struct ambe_decoder *dec, + int16_t *audio, int N, + const uint8_t *frame, int bad) +{ + struct ambe_raw_params rp; + struct ambe_subframe sf[2]; + float unvc; + int i; + + /* Unpack frame */ + ambe_frame_unpack_raw(&rp, frame); + + /* Decode subframe parameters */ + ambe_frame_decode_params(sf, &dec->sf_prev, &rp); + + /* Convert to mbelib's format */ + dec->mp_cur.w0 = sf[1].f0 * (2.0f * (float)M_PI); + dec->mp_cur.L = sf[1].L; + + unvc = 0.2046f / sqrtf(dec->mp_cur.w0); /* ??? */ + + for (i=1; i<=dec->mp_cur.L; i++) { + int j = (int)((i-1) * 16.0f * sf[1].f0); + dec->mp_cur.Vl[i] = sf[1].v_uv[j]; + dec->mp_cur.Ml[i] = powf(2.0, sf[1].Mlog[i-1]) / 8.0f; + if (!dec->mp_cur.Vl[i]) + dec->mp_cur.Ml[i] *= unvc; + } + + /* Synthesize speech (using mbelib for now) */ + mbe_moveMbeParms(&dec->mp_cur, &dec->mp_prev); + mbe_spectralAmpEnhance(&dec->mp_cur); + mbe_synthesizeSpeech(audio, &dec->mp_cur, &dec->mp_prev_enh, 2); + mbe_moveMbeParms(&dec->mp_cur, &dec->mp_prev_enh); + + /* Save subframe */ + memcpy(&dec->sf_prev, &sf[1], sizeof(struct ambe_subframe)); + + /* Done */ + return 0; +} + +int +ambe_decode_frame(struct ambe_decoder *dec, + int16_t *audio, int N, + const uint8_t *frame, int bad) +{ + switch(ambe_classify_frame(frame)) { + case AMBE_SPEECH: + return ambe_decode_speech(dec, audio, N, frame, bad); + + case AMBE_SILENCE: + /* FIXME: Comfort noise */ + memset(audio, 0, 160*sizeof(int16_t)); + return 0; + + case AMBE_TONE: + /* FIXME: Tone gen */ + memset(audio, 0, 160*sizeof(int16_t)); + return 0; + } + + return -EINVAL; +} + +int +ambe_decode_dtx(struct ambe_decoder *dec, + int16_t *audio, int N) +{ + /* FIXME: Comfort noise */ + memset(audio, 0x00, sizeof(int16_t) * N); + return 0; +} + +/*! @} */ diff --git a/src/codec/codec.c b/src/codec/codec.c new file mode 100644 index 0000000..c422b5b --- /dev/null +++ b/src/codec/codec.c @@ -0,0 +1,103 @@ +/* GMR-1 AMBE vocoder */ + +/* (C) 2013 by Sylvain Munaut <tnt@246tNt.com> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/*! \addtogroup codec + * @{ + */ + +/*! \file codec/codec.c + * \brief Osmocom GMR-1 AMBE vocoder public API implementation + */ + +#include <string.h> +#include <stdint.h> +#include <stdlib.h> + +#include <osmocom/gmr1/codec/codec.h> + +#include "private.h" + + +/*! \brief Structure for GMR1 codec state */ +struct gmr1_codec +{ + struct ambe_decoder dec; /*< \brief decoder state */ +}; + + +/*! \brief Allocates and inits a codec object + * \returns A newly allocated codec, to be freed with \ref gmr1_codec_release + */ +struct gmr1_codec * +gmr1_codec_alloc(void) +{ + struct gmr1_codec *codec; + + codec = calloc(1, sizeof(struct gmr1_codec)); + if (!codec) + return NULL; + + ambe_decode_init(&codec->dec); + + return codec; +} + +/*! \brief Release a codec object created by \ref gmr1_codec_alloc + * \param[in] codec The codec object to release + */ +void +gmr1_codec_release(struct gmr1_codec *codec) +{ + if (!codec) + return; + + ambe_decode_fini(&codec->dec); + + free(codec); +} + +/*! \brief Decodes an AMBE frame to audio + * \param[in] codec Codec object + * \param[out] audio Output audio buffer + * \param[in] N number of audio samples to produce (152..168) + * \param[in] frame Frame data (10 bytes = 80 bits) + * \param[in] bad Bad Frame Indicator. Set to 1 if frame is corrupt + * \returns 0 for success. Negative error code otherwise. + */ +int +gmr1_codec_decode_frame(struct gmr1_codec *codec, + int16_t *audio, int N, + const uint8_t *frame, int bad) +{ + return ambe_decode_frame(&codec->dec, audio, N, frame, bad); +} + +/*! \brief Generates audio for DTX period + * \param[in] codec Codec object + * \param[out] audio Output audio buffer + * \param[in] N number of audio samples to produce (152..168) + */ +int +gmr1_codec_decode_dtx(struct gmr1_codec *codec, + int16_t *audio, int N) +{ + return ambe_decode_dtx(&codec->dec, audio, N); +} + +/*! @} */ diff --git a/src/codec/frame.c b/src/codec/frame.c new file mode 100644 index 0000000..9c6ee0f --- /dev/null +++ b/src/codec/frame.c @@ -0,0 +1,205 @@ +/* GMR-1 AMBE vocoder - Speech parameters to/from frame */ + +/* (C) 2013 by Sylvain Munaut <tnt@246tNt.com> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/*! \addtogroup codec/private + * @{ + */ + +/*! \file codec/frame.c + * \brief Osmocom GMR-1 AMBE speech parameters to/from frame + */ + +#include <math.h> +#include <stdint.h> +#include <string.h> + +#include "private.h" + + +static inline uint8_t +_get_bits(const uint8_t *frame, int p, int l, int s) +{ + uint8_t v; + + if ((p & 7) + l > 8) { + v = ((frame[p>>3] << 8) | frame[(p>>3)+1]) >> (16 - (p&7) - l); + } else { + v = frame[p>>3] >> (8 - (p&7) - l); + } + + return (v & ((1<<l)-1)) << s; +} + +void +ambe_frame_unpack_raw(struct ambe_raw_params *rp, const uint8_t *frame) +{ + const uint8_t *p = frame; + + rp->pitch = _get_bits(p, 0, 7, 0); + rp->pitch_interp = _get_bits(p, 48, 2, 0); + rp->gain = _get_bits(p, 7, 6, 2) | _get_bits(p, 50, 2, 0); + rp->v_uv = _get_bits(p, 13, 6, 0); + rp->sf1_prba12 = _get_bits(p, 19, 6, 1) | _get_bits(p, 52, 1, 0); + rp->sf1_prba34 = _get_bits(p, 25, 3, 3) | _get_bits(p, 53, 3, 0); + rp->sf1_prba57 = _get_bits(p, 28, 3, 4) | _get_bits(p, 56, 4, 0); + rp->sf1_hoc[0] = _get_bits(p, 31, 3, 4) | _get_bits(p, 60, 4, 0); + rp->sf1_hoc[1] = _get_bits(p, 34, 3, 3) | _get_bits(p, 64, 3, 0); + rp->sf1_hoc[2] = _get_bits(p, 37, 2, 4) | _get_bits(p, 67, 4, 0); + rp->sf1_hoc[3] = _get_bits(p, 39, 2, 3) | _get_bits(p, 71, 3, 0); + rp->sf0_mag_interp = _get_bits(p, 46, 2, 0); + rp->sf0_perr_14 = _get_bits(p, 41, 3, 3) | _get_bits(p, 74, 3, 0); + rp->sf0_perr_58 = _get_bits(p, 44, 2, 3) | _get_bits(p, 77, 3, 0); +} + +int +ambe_frame_decode_params(struct ambe_subframe *sf, + struct ambe_subframe *sf_prev, + struct ambe_raw_params *rp) +{ + uint16_t v_uv; + int i; + + /* w0 : fundamental */ + sf[1].f0 = powf(2.0, -4.312 - 2.1336e-2 * (rp->pitch /* + 0.5 */)); + + /* FIXME: sf[0] interpolation */ + + /* Harmonics count (total and per-block) */ + sf[1].L = (int)floorf(0.4761f / sf[1].f0); + + if (sf[1].L < 9) + sf[1].L = 9; + else if (sf[1].L > 56) + sf[1].L = 56; + + sf[1].Lb[0] = ambe_hpg_tbl[sf[1].L - 9][0]; + sf[1].Lb[1] = ambe_hpg_tbl[sf[1].L - 9][1]; + sf[1].Lb[2] = ambe_hpg_tbl[sf[1].L - 9][2]; + sf[1].Lb[3] = ambe_hpg_tbl[sf[1].L - 9][3]; + + /* Voicing decision */ + v_uv = ambe_v_uv_tbl[rp->v_uv]; + + for (i=0; i<8; i++) { + sf[0].v_uv[i] = (v_uv >> ( 7-i)) & 1; + sf[1].v_uv[i] = (v_uv >> (15-i)) & 1; + } + + /* Gain */ + float gain = ambe_gain_tbl[rp->gain][1]; + + gain += 0.5f * sf_prev->gain; + if (gain > 13.0f) + gain = 13.0f; + sf[1].gain = gain; + + gain -= 0.5f * log2f(sf[1].L); + + /* Prediction */ + { + float avg, step, pos; + + avg = 0.0f; + step = (float)sf_prev->L / (float)sf[1].L; + pos = step; + + for (i=0; i<sf[1].L; i++) + { + int posi = (int)floorf(pos); + + if (posi == 0) { + sf[1].Mlog[i] = sf_prev->Mlog[0]; + } else if (posi >= sf_prev->L) { + sf[1].Mlog[i] = sf_prev->Mlog[sf_prev->L-1]; + } else { + float alpha = pos - posi; + sf[1].Mlog[i] = + sf_prev->Mlog[posi-1] * (1.0f - alpha) + + sf_prev->Mlog[posi] * alpha; + } + + avg += sf[1].Mlog[i]; + pos += step; + } + + avg /= sf[1].L; + + for (i=0; i<sf[1].L; i++) + sf[1].Mlog[i] = 0.65f * (sf[1].Mlog[i] - avg); + } + + /* PRBA */ + float prba[8]; + float Ri[8]; + + prba[0] = 0.0f; + prba[1] = ambe_prba12_tbl[rp->sf1_prba12][0]; + prba[2] = ambe_prba12_tbl[rp->sf1_prba12][1]; + prba[3] = ambe_prba34_tbl[rp->sf1_prba34][0]; + prba[4] = ambe_prba34_tbl[rp->sf1_prba34][1]; + prba[5] = ambe_prba57_tbl[rp->sf1_prba57][0]; + prba[6] = ambe_prba57_tbl[rp->sf1_prba57][1]; + prba[7] = ambe_prba57_tbl[rp->sf1_prba57][2]; + + ambe_idct(Ri, prba, 8, 8); + + /* Process each block */ + float rconst = (1.0f / (2.0f * (float)M_SQRT2)); + float sum = 0.0f; + int j=0; + int k; + + for (i=0; i<4; i++) { + const float *hoc_tbl[] = { + ambe_hoc0_tbl[rp->sf1_hoc[0]], + ambe_hoc1_tbl[rp->sf1_hoc[1]], + ambe_hoc2_tbl[rp->sf1_hoc[2]], + ambe_hoc3_tbl[rp->sf1_hoc[3]], + }; + float C[6], c[17]; + + /* From PRBA through 2x2 xform */ + C[0] = (Ri[i<<1] + Ri[(i<<1)+1]) * 0.5f; + C[1] = (Ri[i<<1] - Ri[(i<<1)+1]) * rconst; + + /* HOC */ + C[2] = hoc_tbl[i][0]; + C[3] = hoc_tbl[i][1]; + C[4] = hoc_tbl[i][2]; + C[5] = hoc_tbl[i][3]; + + /* De-DCT */ + ambe_idct(c, C, sf[1].Lb[i], 6); + + /* Set magnitudes */ + for (k=0; k<sf[1].Lb[i]; k++) + sf[1].Mlog[j++] += c[k]; + + sum += C[0] * sf[1].Lb[i]; + } + + float g = gain - (sum / sf[1].L); + + for (i=0; i<sf[1].L; i++) + sf[1].Mlog[i] += g; + + return 0; +} + +/*! @} */ diff --git a/src/codec/math.c b/src/codec/math.c new file mode 100644 index 0000000..334677a --- /dev/null +++ b/src/codec/math.c @@ -0,0 +1,108 @@ +/* GMR-1 AMBE vocoder - Math functions */ + +/* (C) 2013 by Sylvain Munaut <tnt@246tNt.com> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/*! \addtogroup codec/private + * @{ + */ + +/*! \file codec/math.c + * \brief Osmocom GMR-1 AMBE vocoder math functions + */ + +#include <math.h> + + +#define M_PIf ((float)M_PI) /*!< \brief Value of pi as a float */ + + +/*! \brief Table for \ref cosf_fast */ +static float cos_tbl[1024]; + +/*! \brief Initializes \ref cos_tbl for \ref cosf_fast */ +static void __attribute__ ((constructor)) +cos_tbl_init(void) +{ + int i; + + for (i=0; i<1024; i++) + cos_tbl[i] = cosf((M_PIf * i) / 512.0f); +} + +/*! \brief Fast Cosinus approximation using a simple table + * \param[in] angle The angle value + * \returns The cosinus of the angle + */ +float +cosf_fast(float angle) +{ + const float f = 512.0f / M_PIf; + return cos_tbl[(int)(angle*f) & 1023]; +} + + +/*! \brief Forward Discrete Cosine Transform (fDCT) + * \param[out] out fDCT result buffer (freq domain, M elements) + * \param[in] in fDCT input buffer (time domain, N elements) + * \param[in] N Number of points of the DCT + * \param[in] M Limit to the number of frequency components (M <= N) + */ +void +ambe_fdct(float *out, float *in, int N, int M) +{ + int i, j; + + for (i=0; i<M; i++) + { + float v = 0.0f; + + for (j=0; j<N; j++) + { + v += in[j] * cosf_fast( (M_PIf / N) * (j + .5f) * i ); + } + + out[i] = v / (float)N; + } +} + + +/*! \brief Inverse Discrete Cosine Transform (iDCT) + * \param[out] out iDCT result buffer (time domain, N elements) + * \param[in] in iDCT input buffer (freq domain, M elements) + * \param[in] N Number of points of the DCT + * \param[in] M Limit to the number of frequency components (M <= N) + */ +void +ambe_idct(float *out, float *in, int N, int M) +{ + int i, j; + + for (i=0; i<N; i++) + { + float v = in[0]; + + for (j=1; j<M; j++) + { + v += 2.0f * in[j] * cosf_fast( (M_PIf / N) * j * (i + .5f) ); + } + + out[i] = v; + } +} + +/*! @} */ diff --git a/src/codec/mbelib.c b/src/codec/mbelib.c new file mode 100644 index 0000000..d6561f4 --- /dev/null +++ b/src/codec/mbelib.c @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2010 mbelib Author + * GPG Key ID: 0xEA5EFE2C (9E7A 5527 9CDC EBF7 BF1B D772 4F98 E863 EA5E FE2C) + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <math.h> + +#include "mbelib.h" +#include "private.h" + + +/* + * Speech Synthesis Window 8k version + */ +static const float Ws[321] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0.02, 0.04, 0.06, 0.08, 0.1, 0.12, 0.14, 0.16, 0.18, + 0.2, 0.22, 0.24, 0.26, 0.28, 0.3, 0.32, 0.34, 0.36, 0.38, + 0.4, 0.42, 0.44, 0.46, 0.48, 0.5, 0.52, 0.54, 0.56, 0.58, + 0.6, 0.62, 0.64, 0.66, 0.68, 0.7, 0.72, 0.74, 0.76, 0.78, + 0.8, 0.82, 0.84, 0.86, 0.88, 0.9, 0.92, 0.94, 0.96, 0.98, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0.98, 0.96, 0.94, 0.92, 0.9, 0.88, 0.86, 0.84, 0.82, 0.8, + 0.78, 0.76, 0.74, 0.72, 0.7, 0.68, 0.66, 0.64, 0.62, 0.6, + 0.58, 0.56, 0.54, 0.52, 0.5, 0.48, 0.46, 0.44, 0.42, 0.4, + 0.38, 0.36, 0.34, 0.32, 0.3, 0.28, 0.26, 0.24, 0.22, 0.2, + 0.18, 0.16, 0.14, 0.12, 0.1, 0.08, 0.06, 0.04, 0.02, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +void +mbe_printVersion (char *str) +{ + sprintf (str, "%s", MBELIB_VERSION); +} + +void +mbe_moveMbeParms (mbe_parms * cur_mp, mbe_parms * prev_mp) +{ + + int l; + + prev_mp->w0 = cur_mp->w0; + prev_mp->L = cur_mp->L; + prev_mp->K = cur_mp->K; // necessary? + prev_mp->Ml[0] = (float) 0; + prev_mp->gamma = cur_mp->gamma; + prev_mp->repeat = cur_mp->repeat; + for (l = 0; l <= 56; l++) + { + prev_mp->Ml[l] = cur_mp->Ml[l]; + prev_mp->Vl[l] = cur_mp->Vl[l]; + prev_mp->log2Ml[l] = cur_mp->log2Ml[l]; + prev_mp->PHIl[l] = cur_mp->PHIl[l]; + prev_mp->PSIl[l] = cur_mp->PSIl[l]; + } +} + +void +mbe_useLastMbeParms (mbe_parms * cur_mp, mbe_parms * prev_mp) +{ + + int l; + + cur_mp->w0 = prev_mp->w0; + cur_mp->L = prev_mp->L; + cur_mp->K = prev_mp->K; // necessary? + cur_mp->Ml[0] = (float) 0; + cur_mp->gamma = prev_mp->gamma; + cur_mp->repeat = prev_mp->repeat; + for (l = 0; l <= 56; l++) + { + cur_mp->Ml[l] = prev_mp->Ml[l]; + cur_mp->Vl[l] = prev_mp->Vl[l]; + cur_mp->log2Ml[l] = prev_mp->log2Ml[l]; + cur_mp->PHIl[l] = prev_mp->PHIl[l]; + cur_mp->PSIl[l] = prev_mp->PSIl[l]; + } +} + +void +mbe_initMbeParms (mbe_parms * cur_mp, mbe_parms * prev_mp, mbe_parms * prev_mp_enhanced) +{ + + int l; + + prev_mp->w0 = 0.09378; + prev_mp->L = 30; + prev_mp->K = 10; + prev_mp->gamma = (float) 0; + for (l = 0; l <= 56; l++) + { + prev_mp->Ml[l] = (float) 0; + prev_mp->Vl[l] = 0; + prev_mp->log2Ml[l] = (float) 0; // log2 of 1 == 0 + prev_mp->PHIl[l] = (float) 0; + prev_mp->PSIl[l] = (M_PI / (float) 2); + } + prev_mp->repeat = 0; + mbe_moveMbeParms (prev_mp, cur_mp); + mbe_moveMbeParms (prev_mp, prev_mp_enhanced); +} + +void +mbe_spectralAmpEnhance (mbe_parms * cur_mp) +{ + + float Rm0, Rm1, R2m0, R2m1, Wl[57]; + int l; + float sum, gamma, M; + + Rm0 = 0; + Rm1 = 0; + for (l = 1; l <= cur_mp->L; l++) + { + Rm0 = Rm0 + powf (cur_mp->Ml[l], (float) 2); + Rm1 = Rm1 + (powf (cur_mp->Ml[l], (float) 2) * cosf_fast (cur_mp->w0 * (float) l)); + } + + R2m0 = powf (Rm0, (float) 2); + R2m1 = powf (Rm1, (float) 2); + + for (l = 1; l <= cur_mp->L; l++) + { + if (cur_mp->Ml[l] != 0) + { + Wl[l] = sqrtf (cur_mp->Ml[l]) * powf ((((float) 0.96 * M_PI * ((R2m0 + R2m1) - ((float) 2 * Rm0 * Rm1 * cosf_fast (cur_mp->w0 * (float) l)))) / (cur_mp->w0 * Rm0 * (R2m0 - R2m1))), (float) 0.25); + + if ((8 * l) <= cur_mp->L) + { + } + else if (Wl[l] > 1.2) + { + cur_mp->Ml[l] = 1.2 * cur_mp->Ml[l]; + } + else if (Wl[l] < 0.5) + { + cur_mp->Ml[l] = 0.5 * cur_mp->Ml[l]; + } + else + { + cur_mp->Ml[l] = Wl[l] * cur_mp->Ml[l]; + } + } + } + + // generate scaling factor + sum = 0; + for (l = 1; l <= cur_mp->L; l++) + { + M = cur_mp->Ml[l]; + if (M < 0) + { + M = -M; + } + sum += powf (M, 2); + } + if (sum == 0) + { + gamma = (float) 1.0; + } + else + { + gamma = sqrtf (Rm0 / sum); + } + + // apply scaling factor + for (l = 1; l <= cur_mp->L; l++) + { + cur_mp->Ml[l] = gamma * cur_mp->Ml[l]; + } +} + +void +mbe_synthesizeSilencef (float *aout_buf) +{ + + int n; + float *aout_buf_p; + + aout_buf_p = aout_buf; + for (n = 0; n < 160; n++) + { + *aout_buf_p = (float) 0; + aout_buf_p++; + } +} + +void +mbe_synthesizeSilence (short *aout_buf) +{ + + int n; + short *aout_buf_p; + + aout_buf_p = aout_buf; + for (n = 0; n < 160; n++) + { + *aout_buf_p = (short) 0; + aout_buf_p++; + } +} + +void +mbe_synthesizeSpeechf (float *aout_buf, mbe_parms * cur_mp, mbe_parms * prev_mp, int uvquality) +{ + + int i, l, n, maxl; + float *Ss, loguvquality; + float C1, C2, C3, C4; + /* float deltaphil, deltawl, thetaln, aln; */ + int numUv; + float cw0, pw0, cw0l, pw0l; + float uvsine, uvrand, uvthreshold, uvthresholdf; + float uvstep, uvoffset; + float qfactor; + float rphase[64], rphase2[64]; + + const int N = 160; + + uvthresholdf = (float) 2700; + uvthreshold = ((uvthresholdf * M_PI) / (float) 4000); + + // voiced/unvoiced/gain settings + uvsine = (float) 1.3591409 *M_E; + uvrand = (float) 2.0; + + if ((uvquality < 1) || (uvquality > 64)) + { + printf ("\nmbelib: Error - uvquality must be within the range 1 - 64\n"); + exit (1); + } + + // calculate loguvquality + if (uvquality == 1) + { + loguvquality = (float) 1 / M_E; + } + else + { + loguvquality = log ((float) uvquality) / (float) uvquality; + } + + // calculate unvoiced step and offset values + uvstep = (float) 1.0 / (float) uvquality; + qfactor = loguvquality; + uvoffset = (uvstep * (float) (uvquality - 1)) / (float) 2; + + // count number of unvoiced bands + numUv = 0; + for (l = 1; l <= cur_mp->L; l++) + { + if (cur_mp->Vl[l] == 0) + { + numUv++; + } + } + + cw0 = cur_mp->w0; + pw0 = prev_mp->w0; + + // init aout_buf + Ss = aout_buf; + for (n = 0; n < N; n++) + { + *Ss = (float) 0; + Ss++; + } + + // eq 128 and 129 + if (cur_mp->L > prev_mp->L) + { + maxl = cur_mp->L; + for (l = prev_mp->L + 1; l <= maxl; l++) + { + prev_mp->Ml[l] = (float) 0; + prev_mp->Vl[l] = 1; + } + } + else + { + maxl = prev_mp->L; + for (l = cur_mp->L + 1; l <= maxl; l++) + { + cur_mp->Ml[l] = (float) 0; + cur_mp->Vl[l] = 1; + } + } + + // update phil from eq 139,140 + for (l = 1; l <= 56; l++) + { + cur_mp->PSIl[l] = prev_mp->PSIl[l] + ((pw0 + cw0) * ((float) (l * N) / (float) 2)); + if (l <= (int) (cur_mp->L / 4)) + { + cur_mp->PHIl[l] = cur_mp->PSIl[l]; + } + else + { + cur_mp->PHIl[l] = cur_mp->PSIl[l] + ((numUv * ((((float) random () / (float) 2147483647) * M_PI * (float) 2) - M_PI)) / cur_mp->L); + } + } + + for (l = 1; l <= maxl; l++) + { + cw0l = (cw0 * (float) l); + pw0l = (pw0 * (float) l); + if ((cur_mp->Vl[l] == 0) && (prev_mp->Vl[l] == 1)) + { + Ss = aout_buf; + // init random phase + for (i = 0; i < uvquality; i++) + { + rphase[i] = ((((float) random () / (float) 2147483647) * M_PI * (float) 2) - M_PI); + } + for (n = 0; n < N; n++) + { + C1 = 0; + // eq 131 + C1 = Ws[n + N] * prev_mp->Ml[l] * cosf_fast ((pw0l * (float) n) + prev_mp->PHIl[l]); + C3 = 0; + // unvoiced multisine mix + for (i = 0; i < uvquality; i++) + { + C3 = C3 + cosf_fast ((cw0 * (float) n * ((float) l + ((float) i * uvstep) - uvoffset)) + rphase[i]); + if (cw0l > uvthreshold) + { + C3 = C3 + ((cw0l - uvthreshold) * uvrand * ((float) random () / (float) 2147483647)); + } + } + C3 = C3 * uvsine * Ws[n] * cur_mp->Ml[l] * qfactor; + *Ss = *Ss + C1 + C3; + Ss++; + } + } + else if ((cur_mp->Vl[l] == 1) && (prev_mp->Vl[l] == 0)) + { + Ss = aout_buf; + // init random phase + for (i = 0; i < uvquality; i++) + { + rphase[i] = ((((float) random () / (float) 2147483647) * M_PI * (float) 2) - M_PI); + } + for (n = 0; n < N; n++) + { + C1 = 0; + // eq 132 + C1 = Ws[n] * cur_mp->Ml[l] * cosf_fast ((cw0l * (float) (n - N)) + cur_mp->PHIl[l]); + C3 = 0; + // unvoiced multisine mix + for (i = 0; i < uvquality; i++) + { + C3 = C3 + cosf_fast ((pw0 * (float) n * ((float) l + ((float) i * uvstep) - uvoffset)) + rphase[i]); + if (pw0l > uvthreshold) + { + C3 = C3 + ((pw0l - uvthreshold) * uvrand * ((float) random () / (float) 2147483647)); + } + } + C3 = C3 * uvsine * Ws[n + N] * prev_mp->Ml[l] * qfactor; + *Ss = *Ss + C1 + C3; + Ss++; + } + } + // else if (((cur_mp->Vl[l] == 1) || (prev_mp->Vl[l] == 1)) && ((l >= 8) || (fabsf (cw0 - pw0) >= ((float) 0.1 * cw0)))) + else if ((cur_mp->Vl[l] == 1) && (prev_mp->Vl[l] == 1)) + { + Ss = aout_buf; + for (n = 0; n < N; n++) + { + C1 = 0; + // eq 133-1 + C1 = Ws[n + N] * prev_mp->Ml[l] * cosf_fast ((pw0l * (float) n) + prev_mp->PHIl[l]); + C2 = 0; + // eq 133-2 + C2 = Ws[n] * cur_mp->Ml[l] * cosf_fast ((cw0l * (float) (n - N)) + cur_mp->PHIl[l]); + *Ss = *Ss + C1 + C2; + Ss++; + } + } + /* + // expensive and unnecessary? + else if ((cur_mp->Vl[l] == 1) || (prev_mp->Vl[l] == 1)) + { + Ss = aout_buf; + // eq 137 + deltaphil = cur_mp->PHIl[l] - prev_mp->PHIl[l] - (((pw0 + cw0) * (float) (l * N)) / (float) 2); + // eq 138 + deltawl = ((float) 1 / (float) N) * (deltaphil - ((float) 2 * M_PI * (int) ((deltaphil + M_PI) / (M_PI * (float) 2)))); + for (n = 0; n < N; n++) + { + // eq 136 + thetaln = prev_mp->PHIl[l] + ((pw0l + deltawl) * (float) n) + (((cw0 - pw0) * ((float) (l * n * n)) / (float) (2 * N))); + // eq 135 + aln = prev_mp->Ml[l] + (((float) n / (float) N) * (cur_mp->Ml[l] - prev_mp->Ml[l])); + // eq 134 + *Ss = *Ss + (aln * cosf_fast (thetaln)); + Ss++; + } + } + */ + else + { + Ss = aout_buf; + // init random phase + for (i = 0; i < uvquality; i++) + { + rphase[i] = ((((float) random () / (float) 2147483647) * M_PI * (float) 2) - M_PI); + } + // init random phase + for (i = 0; i < uvquality; i++) + { + rphase2[i] = ((((float) random () / (float) 2147483647) * M_PI * (float) 2) - M_PI); + } + for (n = 0; n < N; n++) + { + C3 = 0; + // unvoiced multisine mix + for (i = 0; i < uvquality; i++) + { + C3 = C3 + cosf_fast ((pw0 * (float) n * ((float) l + ((float) i * uvstep) - uvoffset)) + rphase[i]); + if (pw0l > uvthreshold) + { + C3 = C3 + ((pw0l - uvthreshold) * uvrand * ((float) random () / (float) 2147483647)); + } + } + C3 = C3 * uvsine * Ws[n + N] * prev_mp->Ml[l] * qfactor; + C4 = 0; + // unvoiced multisine mix + for (i = 0; i < uvquality; i++) + { + C4 = C4 + cosf_fast ((cw0 * (float) n * ((float) l + ((float) i * uvstep) - uvoffset)) + rphase2[i]); + if (cw0l > uvthreshold) + { + C4 = C4 + ((cw0l - uvthreshold) * uvrand * ((float) random () / (float) 2147483647)); + } + } + C4 = C4 * uvsine * Ws[n] * cur_mp->Ml[l] * qfactor; + *Ss = *Ss + C3 + C4; + Ss++; + } + } + } +} + +void +mbe_synthesizeSpeech (short *aout_buf, mbe_parms * cur_mp, mbe_parms * prev_mp, int uvquality) +{ + float float_buf[160]; + + mbe_synthesizeSpeechf (float_buf, cur_mp, prev_mp, uvquality); + mbe_floattoshort (float_buf, aout_buf); +} + +void +mbe_floattoshort (float *float_buf, short *aout_buf) +{ + + short *aout_buf_p; + float *float_buf_p; + int i, again; + float audio; + + again = 7; + aout_buf_p = aout_buf; + float_buf_p = float_buf; + for (i = 0; i < 160; i++) + { + audio = again * *float_buf_p; + if (audio > 32760) + { +#ifdef MBE_DEBUG + printf ("audio clip: %f\n", audio); +#endif + audio = 32760; + } + else if (audio < -32760) + { +#ifdef MBE_DEBUG + printf ("audio clip: %f\n", audio); +#endif + audio = -32760; + } + *aout_buf_p = (short) (audio); + aout_buf_p++; + float_buf_p++; + } +} diff --git a/src/codec/mbelib.h b/src/codec/mbelib.h new file mode 100644 index 0000000..1353bd1 --- /dev/null +++ b/src/codec/mbelib.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 mbelib Author + * GPG Key ID: 0xEA5EFE2C (9E7A 5527 9CDC EBF7 BF1B D772 4F98 E863 EA5E FE2C) + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __MBELIB_H__ +#define __MBELIB_H__ + +#define MBELIB_VERSION "1.2.5" + +struct mbe_parameters +{ + float w0; + int L; + int K; + int Vl[57]; + float Ml[57]; + float log2Ml[57]; + float PHIl[57]; + float PSIl[57]; + float gamma; + int un; + int repeat; +}; + +typedef struct mbe_parameters mbe_parms; + +void mbe_printVersion (char *str); +void mbe_moveMbeParms (mbe_parms * cur_mp, mbe_parms * prev_mp); +void mbe_useLastMbeParms (mbe_parms * cur_mp, mbe_parms * prev_mp); +void mbe_initMbeParms (mbe_parms * cur_mp, mbe_parms * prev_mp, mbe_parms * prev_mp_enhanced); +void mbe_spectralAmpEnhance (mbe_parms * cur_mp); +void mbe_synthesizeSilencef (float *aout_buf); +void mbe_synthesizeSilence (short *aout_buf); +void mbe_synthesizeSpeechf (float *aout_buf, mbe_parms * cur_mp, mbe_parms * prev_mp, int uvquality); +void mbe_synthesizeSpeech (short *aout_buf, mbe_parms * cur_mp, mbe_parms * prev_mp, int uvquality); +void mbe_floattoshort (float *float_buf, short *aout_buf); + +#endif /* __MBELIB_H__ */ diff --git a/src/codec/private.h b/src/codec/private.h new file mode 100644 index 0000000..c1e0e0c --- /dev/null +++ b/src/codec/private.h @@ -0,0 +1,133 @@ +/* GMR-1 AMBE vocoder private header */ + +/* (C) 2013 by Sylvain Munaut <tnt@246tNt.com> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OSMO_GMR1_CODEC_PRIVATE_H__ +#define __OSMO_GMR1_CODEC_PRIVATE_H__ + +/*! \defgroup codec/private AMBE vocoder - internal API + * \ingroup codec/private + * @{ + */ + +/*! \file codec/private.h + * \brief Osmocom GMR-1 AMBE vocoder private header + */ + +#include <stdint.h> + +#include "mbelib.h" + + +#define AMBE_RATE 8000 /*< \brief AMBE sample rate */ + + +/*! \brief AMBE possible frame types */ +enum ambe_frame_type +{ + AMBE_SPEECH, /*< \brief Speed frame */ + AMBE_SILENCE, /*< \brief Silence indication frame */ + AMBE_TONE, /*< \brief Tone frame */ +}; + +/*! \brief AMBE encoded frame raw parameters */ +struct ambe_raw_params +{ + uint8_t pitch; /*!< \brief Pitch */ + uint8_t pitch_interp; /*!< \brief Pitch interpolation selection */ + uint8_t gain; /*!< \brief Gain VQ */ + uint8_t v_uv; /*!< \brief V/UV decision VQ */ + + uint8_t sf1_prba12; /*!< \brief sf1 PRBA[1,2] VQ */ + uint8_t sf1_prba34; /*!< \brief sf1 PRBA[3,4] VQ */ + uint8_t sf1_prba57; /*!< \brief sf1 PRBA[5,6,7] VQ */ + uint8_t sf1_hoc[4]; /*!< \brief sf1 HOCs VQ */ + + uint8_t sf0_mag_interp; /*!< \brief sf0 mag interpolation selection */ + uint8_t sf0_perr_14; /*!< \brief sf0 mag prediction error VQ [1,4] */ + uint8_t sf0_perr_58; /*!< \brief sf0 mag prediction error VQ [5,8] */ +}; + +/*! \brief AMBE subframe parameters */ +struct ambe_subframe +{ + float f0; /*!< \brief fundamental normalized frequency */ + int L; /*!< \brief Number of harmonics */ + int Lb[4]; /*!< \brief Harmonics per block */ + int v_uv[8]; /*!< \brief Voicing state */ + float gain; /*!< \brief Gain */ + float Mlog[56]; /*!< \brief log spectral magnitudes */ +}; + +/*! \brief AMBE decoder state */ +struct ambe_decoder +{ + float tone_phase_f1; /*!< \brief Phase frequency 1 for tone frames */ + float tone_phase_f2; /*!< \brief Phase frequency 2 for tone frames */ + + struct ambe_subframe sf_prev; /*!< \brief Previous subframe */ + + mbe_parms mp_cur; /*!< \brief mbelib current frame */ + mbe_parms mp_prev; /*!< \brief mbelib previous frame */ + mbe_parms mp_prev_enh; /*!< \brief mbelib previous frame (enhanced) */ +}; + +/* From ambe.c */ +void ambe_decode_init(struct ambe_decoder *dec); +void ambe_decode_fini(struct ambe_decoder *dec); + +int ambe_decode_frame(struct ambe_decoder *dec, + int16_t *audio, int N, + const uint8_t *frame, int bad); +int ambe_decode_dtx(struct ambe_decoder *dec, + int16_t *audio, int N); + +/* From frame.c */ +void ambe_frame_unpack_raw(struct ambe_raw_params *rp, const uint8_t *frame); +int ambe_frame_decode_params(struct ambe_subframe *sf, + struct ambe_subframe *sf_prev, + struct ambe_raw_params *rp); + +/* From math.c */ +float cosf_fast(float angle); +void ambe_fdct(float *out, float *in, int N, int M); +void ambe_idct(float *out, float *in, int N, int M); + +/* From tables.c */ +extern const uint8_t ambe_hpg_tbl[48][4]; +extern const float ambe_gain_tbl[256][2]; +extern const uint16_t ambe_v_uv_tbl[64]; +extern const float ambe_prba12_tbl[128][2]; +extern const float ambe_prba34_tbl[64][2]; +extern const float ambe_prba57_tbl[128][3]; +extern const float ambe_hoc0_tbl[128][4]; +extern const float ambe_hoc1_tbl[64][4]; +extern const float ambe_hoc2_tbl[64][4]; +extern const float ambe_hoc3_tbl[64][4]; +extern const float ambe_sf0_interp_tbl[4][2]; +extern const float ambe_sf0_perr14_tbl[64][4]; +extern const float ambe_sf0_perr58_tbl[32][4]; + +/* From tone.c */ +int ambe_decode_tone(struct ambe_decoder *dec, + int16_t *audio, int N, const uint8_t *frame); + + +/*! @} */ + +#endif /* __OSMO_GMR1_CODEC_PRIVATE_H__ */ diff --git a/src/codec/tables.c b/src/codec/tables.c new file mode 100644 index 0000000..a405cea --- /dev/null +++ b/src/codec/tables.c @@ -0,0 +1,1134 @@ +/* GMR-1 AMBE vocoder - Tables */ + +/* (C) 2013 by Sylvain Munaut <tnt@246tNt.com> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/*! \addtogroup codec/private + * @{ + */ + +/*! \file codec/tables.c + * \brief Osmocom GMR-1 AMBE vocoder tables + */ + +#include <stdint.h> + +/*! \brief Number of harmonics per group for a given L (starts at L=9) */ +const uint8_t ambe_hpg_tbl[48][4] = { + { 2, 2, 2, 3} /* L = 9 */, + { 2, 2, 3, 3}, + { 2, 3, 3, 3}, + { 2, 3, 3, 4}, + { 3, 3, 3, 4}, + { 3, 3, 4, 4}, + { 3, 3, 4, 5}, + { 3, 4, 4, 5}, + { 3, 4, 5, 5}, + { 4, 4, 5, 5}, + { 4, 4, 5, 6}, + { 4, 4, 6, 6}, + { 4, 5, 6, 6}, + { 4, 5, 6, 7}, + { 5, 5, 6, 7}, + { 5, 5, 7, 7}, + { 5, 6, 7, 7}, + { 5, 6, 7, 8}, + { 5, 6, 8, 8}, + { 6, 6, 8, 8}, + { 6, 6, 8, 9}, + { 6, 7, 8, 9}, + { 6, 7, 9, 9}, + { 6, 7, 9, 10}, + { 7, 7, 9, 10}, + { 7, 8, 9, 10}, + { 7, 8, 10, 10}, + { 7, 8, 10, 11}, + { 8, 8, 10, 11}, + { 8, 9, 10, 11}, + { 8, 9, 11, 11}, + { 8, 9, 11, 12}, + { 8, 9, 11, 13}, + { 8, 9, 12, 13}, + { 8, 10, 12, 13}, + { 9, 10, 12, 13}, + { 9, 10, 12, 14}, + { 9, 10, 13, 14}, + { 9, 11, 13, 14}, + { 10, 11, 13, 14}, + { 10, 11, 13, 15}, + { 10, 11, 14, 15}, + { 10, 12, 14, 15}, + { 10, 12, 14, 16}, + { 11, 12, 14, 16}, + { 11, 12, 15, 16}, + { 11, 12, 15, 17}, + { 11, 13, 15, 17}, /* L = 56 */ +}; + +/* \brief Gain (subframe 0, subframe 1) */ +const float ambe_gain_tbl[256][2] = { + { -0.250000f, -0.250000f }, + { +0.500000f, -0.500000f }, + { +0.750000f, +0.750000f }, + { +1.000000f, +0.000000f }, + { +2.694336f, -1.780273f }, + { +1.505859f, -0.089844f }, + { +1.200195f, +0.719727f }, + { +1.519531f, +0.910156f }, + { +1.575195f, +1.520508f }, + { +1.758789f, +1.686523f }, + { +1.962891f, +1.791992f }, + { +1.825195f, +1.992188f }, + { +0.467773f, +1.686523f }, + { +1.291016f, +1.441406f }, + { +1.486328f, +1.935547f }, + { +1.854492f, +2.328125f }, + { +1.998047f, +0.444336f }, + { +2.457031f, +0.266602f }, + { +1.852539f, +0.899414f }, + { +2.326172f, +0.845703f }, + { +1.783203f, +1.342773f }, + { +2.064453f, +1.244141f }, + { +2.036133f, +1.547852f }, + { +2.357422f, +1.338867f }, + { +2.664062f, +1.724609f }, + { +2.283203f, +1.666992f }, + { +2.495117f, +1.903320f }, + { +2.194336f, +1.882812f }, + { +3.264648f, +0.294922f }, + { +2.809570f, +1.036133f }, + { +3.358398f, +1.009766f }, + { +2.734375f, +1.429688f }, + { +0.196289f, +3.059570f }, + { +1.047852f, +2.736328f }, + { +1.536133f, +2.499023f }, + { +1.562500f, +3.231445f }, + { +2.076172f, +2.083008f }, + { +2.325195f, +2.013672f }, + { +2.333008f, +2.181641f }, + { +2.583008f, +2.100586f }, + { +3.077148f, +1.631836f }, + { +2.842773f, +1.973633f }, + { +3.270508f, +1.856445f }, + { +2.766602f, +2.154297f }, + { +2.156250f, +2.318359f }, + { +2.423828f, +2.339844f }, + { +2.262695f, +2.541992f }, + { +2.005859f, +2.839844f }, + { +2.625977f, +2.315430f }, + { +2.644531f, +2.464844f }, + { +2.846680f, +2.389648f }, + { +2.655273f, +2.627930f }, + { +2.484375f, +2.551758f }, + { +2.388672f, +2.776367f }, + { +2.597656f, +2.848633f }, + { +2.865234f, +2.641602f }, + { +1.152344f, +3.904297f }, + { +2.350586f, +3.088867f }, + { +2.094727f, +3.514648f }, + { +2.540039f, +3.231445f }, + { +3.636719f, +1.640625f }, + { +3.167969f, +2.131836f }, + { +3.000000f, +2.323242f }, + { +2.825195f, +2.525391f }, + { +4.083984f, +1.436523f }, + { +3.498047f, +2.150391f }, + { +3.985352f, +1.961914f }, + { +3.478516f, +2.397461f }, + { +3.007812f, +2.522461f }, + { +2.817383f, +2.783203f }, + { +3.228516f, +2.431641f }, + { +3.122070f, +2.636719f }, + { +3.022461f, +2.736328f }, + { +2.793945f, +2.946289f }, + { +2.982422f, +2.849609f }, + { +2.958984f, +3.000977f }, + { +2.800781f, +3.152344f }, + { +2.799805f, +3.389648f }, + { +3.030273f, +3.177734f }, + { +3.288086f, +3.029297f }, + { +2.651367f, +3.675781f }, + { +1.701172f, +4.336914f }, + { +2.294922f, +3.939453f }, + { +1.406250f, +5.000000f }, + { +3.373047f, +2.619141f }, + { +3.228516f, +2.765625f }, + { +3.164062f, +2.910156f }, + { +3.128906f, +3.040039f }, + { +3.361328f, +2.868164f }, + { +3.669922f, +2.584961f }, + { +3.898438f, +2.388672f }, + { +3.523438f, +2.802734f }, + { +3.094727f, +3.301758f }, + { +2.993164f, +3.511719f }, + { +3.252930f, +3.336914f }, + { +3.231445f, +3.168945f }, + { +3.501953f, +3.005859f }, + { +3.379883f, +3.152344f }, + { +3.753906f, +2.853516f }, + { +3.506836f, +3.168945f }, + { +2.879883f, +4.072266f }, + { +2.498047f, +4.471680f }, + { +2.228516f, +5.155273f }, + { +0.569336f, +6.061523f }, + { +4.692383f, +1.986328f }, + { +4.386719f, +2.451172f }, + { +4.104492f, +2.850586f }, + { +3.869141f, +3.131836f }, + { +3.184570f, +3.526367f }, + { +3.349609f, +3.495117f }, + { +3.109375f, +3.801758f }, + { +3.483398f, +3.559570f }, + { +3.402344f, +3.304688f }, + { +3.688477f, +3.081055f }, + { +3.582031f, +3.269531f }, + { +3.523438f, +3.406250f }, + { +3.708008f, +3.312500f }, + { +3.632812f, +3.520508f }, + { +3.740234f, +3.449219f }, + { +3.905273f, +3.368164f }, + { +3.341797f, +3.716797f }, + { +3.558594f, +3.694336f }, + { +3.231445f, +4.089844f }, + { +3.433594f, +3.908203f }, + { +4.726562f, +2.729492f }, + { +4.135742f, +3.233398f }, + { +4.406250f, +3.089844f }, + { +4.085938f, +3.517578f }, + { +3.725586f, +3.644531f }, + { +3.850586f, +3.572266f }, + { +3.831055f, +3.752930f }, + { +3.979492f, +3.644531f }, + { +3.062500f, +4.463867f }, + { +3.684570f, +3.842773f }, + { +3.667969f, +4.026367f }, + { +3.492188f, +4.209961f }, + { +1.938477f, +5.909180f }, + { +2.965820f, +4.902344f }, + { +2.994141f, +5.447266f }, + { +3.461914f, +5.069336f }, + { +3.943359f, +3.811523f }, + { +4.340820f, +3.446289f }, + { +4.105469f, +3.754883f }, + { +4.304688f, +3.649414f }, + { +4.032227f, +3.921875f }, + { +3.861328f, +3.956055f }, + { +3.805664f, +4.154297f }, + { +3.969727f, +4.078125f }, + { +3.420898f, +4.545898f }, + { +3.715820f, +4.432617f }, + { +3.923828f, +4.300781f }, + { +4.065430f, +4.208008f }, + { +4.115234f, +4.025391f }, + { +4.187500f, +3.890625f }, + { +4.367188f, +3.826172f }, + { +4.265625f, +4.010742f }, + { +4.611328f, +3.655273f }, + { +4.742188f, +3.284180f }, + { +5.435547f, +2.901367f }, + { +4.902344f, +3.943359f }, + { +3.744141f, +4.716797f }, + { +3.986328f, +4.517578f }, + { +4.599609f, +3.930664f }, + { +5.066406f, +3.545898f }, + { +4.199219f, +4.132812f }, + { +4.423828f, +4.046875f }, + { +4.211914f, +4.260742f }, + { +4.338867f, +4.162109f }, + { +4.345703f, +4.291992f }, + { +4.469727f, +4.196289f }, + { +4.630859f, +4.108398f }, + { +4.467773f, +4.321289f }, + { +4.099609f, +4.374023f }, + { +4.261719f, +4.399414f }, + { +4.178711f, +4.560547f }, + { +4.014648f, +4.748047f }, + { +1.478516f, +7.333984f }, + { +2.419922f, +6.616211f }, + { +3.361328f, +6.083984f }, + { +3.667969f, +5.605469f }, + { +4.393555f, +4.422852f }, + { +4.605469f, +4.262695f }, + { +4.362305f, +4.538086f }, + { +4.517578f, +4.436523f }, + { +3.839844f, +5.159180f }, + { +4.111328f, +5.006836f }, + { +4.354492f, +4.989258f }, + { +4.483398f, +4.814453f }, + { +4.535156f, +4.541992f }, + { +4.637695f, +4.434570f }, + { +4.817383f, +4.365234f }, + { +4.662109f, +4.547852f }, + { +4.298828f, +4.725586f }, + { +4.445312f, +4.653320f }, + { +4.577148f, +4.662109f }, + { +4.666992f, +4.790039f }, + { +5.015625f, +4.486328f }, + { +5.151367f, +4.260742f }, + { +5.334961f, +3.931641f }, + { +5.895508f, +3.884766f }, + { +4.785156f, +4.228516f }, + { +4.825195f, +4.518555f }, + { +4.718750f, +4.651367f }, + { +4.845703f, +4.656250f }, + { +4.833008f, +4.784180f }, + { +4.771484f, +4.909180f }, + { +4.584961f, +4.994141f }, + { +4.899414f, +4.937500f }, + { +4.381836f, +5.285156f }, + { +4.125977f, +5.541992f }, + { +4.750000f, +5.098633f }, + { +4.757812f, +5.340820f }, + { +4.940430f, +5.166016f }, + { +5.069336f, +4.881836f }, + { +5.010742f, +5.030273f }, + { +5.323242f, +4.840820f }, + { +4.972656f, +4.762695f }, + { +5.098633f, +4.670898f }, + { +5.342773f, +4.547852f }, + { +5.499023f, +4.323242f }, + { +4.230469f, +6.020508f }, + { +4.221680f, +6.533203f }, + { +4.645508f, +5.621094f }, + { +4.752930f, +5.988281f }, + { +5.166016f, +5.011719f }, + { +5.136719f, +5.173828f }, + { +5.053711f, +5.355469f }, + { +5.316406f, +5.132812f }, + { +5.524414f, +5.062500f }, + { +5.836914f, +4.714844f }, + { +5.773438f, +5.334961f }, + { +6.409180f, +4.941406f }, + { +5.280273f, +5.305664f }, + { +5.263672f, +5.515625f }, + { +5.007812f, +5.597656f }, + { +5.294922f, +5.808594f }, + { +2.746094f, +8.125977f }, + { +3.668945f, +7.252930f }, + { +4.895508f, +6.830078f }, + { +5.036133f, +6.223633f }, + { +5.495117f, +5.355469f }, + { +5.525391f, +5.579102f }, + { +5.774414f, +5.652344f }, + { +5.636719f, +5.845703f }, + { +5.508789f, +6.226562f }, + { +5.951172f, +5.958008f }, + { +6.026367f, +6.290039f }, + { +5.965820f, +6.783203f }, + { +6.294922f, +5.596680f }, + { +7.150391f, +5.895508f }, + { +6.835938f, +6.656250f }, + { +7.827148f, +7.060547f }, + { +6.593750f, +7.486328f }, + { +5.545898f, +7.523438f }, + { +6.250977f, +8.595703f }, + { +7.577148f, +8.196289f }, +}; + +/*! \brief V/UV decisions (subframe 0 = low byte. MSBs = low freq) */ +const uint16_t ambe_v_uv_tbl[64] = { + 0x0000, 0xffff, 0xc0c0, 0xe0e0, 0xf0f0, 0x8080, 0xfefe, 0x0080, + 0xfcfc, 0x80c0, 0xf8f8, 0x8000, 0x00c0, 0xfffe, 0xfeff, 0xc000, + 0xf0e0, 0xc0e0, 0xfbfb, 0xfcfe, 0xf8f0, 0xfdfd, 0xfffb, 0xe000, + 0xe0f0, 0xfcff, 0xfdff, 0xe0c0, 0xfffc, 0xf0f8, 0xfefc, 0xfffd, + 0xf8fc, 0xfcf8, 0xfcfd, 0x4000, 0xf0c0, 0xf9f9, 0xfbff, 0xefef, + 0xf3f3, 0xc080, 0xf0e0, 0xfff8, 0xf0fc, 0xf1f1, 0x0040, 0xc0d0, + 0xc0f0, 0xf7f7, 0xfaf8, 0xfafa, 0xfcf0, 0xfef8, 0x00e0, 0x0100, + 0x1000, 0xe0e1, 0xe0f8, 0xf2f0, 0xf8ff, 0xfafe, 0xfff7, 0xdfdf, +}; + +/* \brief PRBA[1:2] */ +const float ambe_prba12_tbl[128][2] = { + { -0.042480f, +0.271484f }, + { -0.082031f, +0.219727f }, + { -0.579102f, +0.062988f }, + { -0.708984f, +0.114746f }, + { +0.277344f, +0.117676f }, + { +0.254883f, +0.074707f }, + { -0.012695f, -0.114258f }, + { -0.069824f, -0.086914f }, + { -0.416992f, +0.171387f }, + { -0.502930f, +0.271484f }, + { -0.884277f, -0.095703f }, + { -1.116699f, -0.163574f }, + { -0.106445f, +0.016602f }, + { -0.168457f, +0.039062f }, + { -0.341309f, -0.233398f }, + { -0.450684f, -0.319824f }, + { +0.094238f, +0.693848f }, + { -0.014648f, +0.551758f }, + { -0.008301f, +0.385742f }, + { -0.109863f, +0.407715f }, + { +0.467285f, +0.452148f }, + { +0.358398f, +0.437012f }, + { +0.174805f, +0.209473f }, + { +0.150391f, +0.166992f }, + { -0.273438f, +0.550781f }, + { -0.449707f, +0.567871f }, + { -0.829590f, +0.413574f }, + { -1.075684f, +0.278320f }, + { +0.103516f, +0.246094f }, + { +0.063477f, +0.287109f }, + { -0.171387f, +0.147949f }, + { -0.252441f, +0.134766f }, + { +0.204102f, +0.019043f }, + { +0.175781f, -0.017578f }, + { -0.077637f, -0.202148f }, + { -0.160156f, -0.187988f }, + { +0.602539f, -0.154297f }, + { +0.534668f, -0.104980f }, + { +0.256836f, -0.355957f }, + { +0.154297f, -0.436523f }, + { -0.178223f, -0.069824f }, + { -0.257324f, -0.095215f }, + { -0.726074f, -0.431641f }, + { -0.867188f, -0.691406f }, + { +0.190430f, -0.197754f }, + { +0.136719f, -0.255371f }, + { -0.173340f, -0.553711f }, + { -0.392578f, -0.628418f }, + { +0.321777f, +0.302246f }, + { +0.277344f, +0.261230f }, + { +0.115234f, +0.098145f }, + { +0.068359f, +0.083008f }, + { +1.054688f, +0.336914f }, + { +0.906738f, +0.352539f }, + { +0.413086f, +0.034180f }, + { +0.368164f, +0.011719f }, + { +0.065430f, +0.160645f }, + { +0.020508f, +0.185547f }, + { -0.308594f, +0.020020f }, + { -0.388184f, -0.018555f }, + { +0.355469f, +0.086426f }, + { +0.318359f, +0.065430f }, + { +0.114746f, -0.064453f }, + { +0.064941f, -0.067383f }, + { +0.292969f, +0.191406f }, + { +0.243164f, +0.180664f }, + { +0.023438f, +0.008301f }, + { -0.015625f, -0.019043f }, + { +0.728516f, +0.210938f }, + { +0.659668f, +0.122070f }, + { +0.339844f, -0.103516f }, + { +0.331543f, -0.056152f }, + { -0.018066f, +0.077148f }, + { -0.058594f, +0.115723f }, + { -0.502441f, -0.130371f }, + { -0.628906f, -0.166504f }, + { +0.285645f, +0.003418f }, + { +0.252930f, -0.024902f }, + { +0.096191f, -0.151367f }, + { +0.045898f, -0.202637f }, + { +0.625000f, +0.731445f }, + { +0.407715f, +0.644531f }, + { +0.228027f, +0.348145f }, + { +0.180664f, +0.297852f }, + { +1.320801f, +0.676270f }, + { +1.041992f, +0.653809f }, + { +0.565430f, +0.208008f }, + { +0.488281f, +0.207031f }, + { +0.166016f, +0.472168f }, + { +0.112305f, +0.392578f }, + { -0.203125f, +0.273438f }, + { -0.295898f, +0.312012f }, + { +0.487793f, +0.313965f }, + { +0.413574f, +0.292969f }, + { +0.198242f, +0.115723f }, + { +0.184082f, +0.077637f }, + { +0.577148f, +0.010254f }, + { +0.505371f, +0.014648f }, + { +0.325195f, -0.218750f }, + { +0.287109f, -0.166504f }, + { +1.284668f, -0.212402f }, + { +1.041016f, -0.184082f }, + { +0.804688f, -0.460449f }, + { +0.680176f, -0.321777f }, + { +0.239746f, -0.093750f }, + { +0.191406f, -0.091797f }, + { -0.043945f, -0.354492f }, + { -0.177246f, -0.346191f }, + { +0.455566f, -0.203125f }, + { +0.473145f, -0.290039f }, + { +0.396973f, -0.534668f }, + { +0.155762f, -0.726074f }, + { +0.699219f, +0.478516f }, + { +0.643066f, +0.364746f }, + { +0.500977f, +0.110840f }, + { +0.437988f, +0.127441f }, + { +1.643555f, +0.210938f }, + { +1.371582f, +0.206543f }, + { +0.811035f, -0.143555f }, + { +0.718750f, -0.047852f }, + { +0.382812f, +0.208008f }, + { +0.357910f, +0.161621f }, + { +0.130371f, +0.032715f }, + { +0.093750f, +0.011230f }, + { +1.021973f, +0.114258f }, + { +0.864746f, +0.076172f }, + { +0.428711f, -0.104492f }, + { +0.425781f, -0.049316f }, +}; + +/* \brief PRBA[3:4] */ +const float ambe_prba34_tbl[64][2] = { + { +0.439453f, +0.273438f }, + { +0.307129f, +0.220215f }, + { +0.338867f, +0.147949f }, + { +0.253906f, +0.131836f }, + { +0.557617f, +0.126953f }, + { +0.386719f, +0.126465f }, + { +0.408691f, +0.048828f }, + { +0.298828f, +0.062500f }, + { +0.344238f, -0.062012f }, + { +0.259766f, -0.041016f }, + { +0.258789f, -0.108398f }, + { +0.194824f, -0.082520f }, + { +0.456543f, -0.156738f }, + { +0.312988f, -0.124512f }, + { +0.318848f, -0.236328f }, + { +0.220703f, -0.167969f }, + { +0.171387f, +0.083008f }, + { +0.113770f, +0.094727f }, + { +0.111816f, +0.051270f }, + { +0.060547f, +0.074707f }, + { +0.188477f, +0.023926f }, + { +0.138184f, +0.039551f }, + { +0.135254f, -0.012207f }, + { +0.086426f, +0.014160f }, + { +0.082031f, -0.087891f }, + { +0.009766f, -0.101562f }, + { +0.036621f, -0.141113f }, + { -0.057617f, -0.141602f }, + { +0.099609f, -0.158691f }, + { +0.011719f, -0.175781f }, + { +0.067383f, -0.262207f }, + { -0.056152f, -0.254395f }, + { +0.182129f, +0.352539f }, + { +0.044434f, +0.348633f }, + { +0.120605f, +0.258789f }, + { +0.036133f, +0.237305f }, + { +0.180664f, +0.225586f }, + { +0.088867f, +0.221191f }, + { +0.130859f, +0.172852f }, + { +0.059082f, +0.164062f }, + { +0.012207f, +0.025879f }, + { -0.026367f, +0.057617f }, + { -0.041504f, +0.008301f }, + { -0.084473f, +0.027832f }, + { +0.021484f, -0.024414f }, + { -0.021484f, +0.000000f }, + { -0.031738f, -0.044434f }, + { -0.082520f, -0.031738f }, + { -0.068359f, +0.224121f }, + { -0.163086f, +0.300781f }, + { -0.152832f, +0.183594f }, + { -0.283203f, +0.206543f }, + { -0.034668f, +0.145508f }, + { -0.102051f, +0.166992f }, + { -0.099121f, +0.110840f }, + { -0.179199f, +0.111328f }, + { -0.181641f, -0.009277f }, + { -0.288086f, +0.006836f }, + { -0.241211f, -0.063965f }, + { -0.434082f, -0.058105f }, + { -0.164062f, -0.092285f }, + { -0.271973f, -0.109375f }, + { -0.203613f, -0.181152f }, + { -0.333496f, -0.238770f }, +}; + +/* \brief PRBA[5:7] */ +const float ambe_prba57_tbl[128][3] = { + { +0.012695f, -0.156250f, -0.120605f }, + { -0.009277f, -0.090332f, -0.065918f }, + { -0.062500f, -0.173340f, -0.151855f }, + { -0.061035f, -0.105469f, -0.102051f }, + { +0.079102f, -0.242676f, -0.139160f }, + { +0.050781f, -0.146973f, -0.065918f }, + { -0.039551f, -0.263672f, -0.182129f }, + { -0.022949f, -0.181641f, -0.088379f }, + { -0.033203f, -0.136719f, -0.044922f }, + { -0.039062f, -0.071777f, -0.019043f }, + { -0.084961f, -0.215820f, -0.066406f }, + { -0.068359f, -0.129395f, -0.027832f }, + { +0.020020f, -0.228027f, -0.042969f }, + { +0.001953f, -0.132812f, -0.005859f }, + { -0.052734f, -0.330566f, -0.035645f }, + { -0.038086f, -0.213867f, +0.008301f }, + { +0.192871f, -0.128906f, -0.024902f }, + { +0.136719f, -0.054688f, +0.038574f }, + { +0.111328f, -0.106934f, -0.008789f }, + { +0.066895f, -0.054688f, +0.019531f }, + { +0.255859f, -0.254883f, +0.000977f }, + { +0.220215f, -0.125977f, +0.077148f }, + { +0.133789f, -0.203613f, +0.004395f }, + { +0.116211f, -0.105469f, +0.049316f }, + { +0.143555f, -0.127441f, +0.070312f }, + { +0.109375f, -0.051270f, +0.123047f }, + { +0.067871f, -0.123535f, +0.059082f }, + { +0.056641f, -0.055664f, +0.076172f }, + { +0.175293f, -0.220215f, +0.117188f }, + { +0.160156f, -0.116211f, +0.196777f }, + { +0.074219f, -0.223145f, +0.091309f }, + { +0.082031f, -0.122559f, +0.128906f }, + { -0.172852f, -0.069336f, -0.072754f }, + { -0.152344f, -0.026855f, +0.002930f }, + { -0.268066f, -0.087891f, -0.121094f }, + { -0.246094f, -0.022949f, -0.011230f }, + { -0.175781f, -0.148926f, -0.092773f }, + { -0.144043f, -0.090820f, -0.020996f }, + { -0.303223f, -0.214355f, -0.161621f }, + { -0.247070f, -0.124512f, -0.029785f }, + { -0.212402f, -0.088867f, +0.005371f }, + { -0.199707f, -0.001953f, +0.068848f }, + { -0.338379f, -0.093262f, -0.021973f }, + { -0.323730f, -0.006836f, +0.092773f }, + { -0.186523f, -0.202148f, +0.004883f }, + { -0.185059f, -0.101074f, +0.049316f }, + { -0.303223f, -0.264160f, +0.009277f }, + { -0.290527f, -0.139648f, +0.086426f }, + { -0.033203f, -0.073730f, +0.076172f }, + { -0.023438f, -0.025879f, +0.125000f }, + { -0.095215f, -0.081543f, +0.071289f }, + { -0.096680f, -0.020508f, +0.113770f }, + { -0.025391f, -0.136719f, +0.108398f }, + { -0.004395f, -0.078613f, +0.167969f }, + { -0.094727f, -0.167969f, +0.101074f }, + { -0.091797f, -0.093750f, +0.150879f }, + { -0.049316f, -0.080078f, +0.143555f }, + { -0.028320f, +0.005859f, +0.210449f }, + { -0.142090f, -0.110840f, +0.149414f }, + { -0.139648f, +0.005371f, +0.204102f }, + { -0.020996f, -0.185547f, +0.189453f }, + { -0.020020f, -0.087402f, +0.273926f }, + { -0.124512f, -0.266113f, +0.179688f }, + { -0.151855f, -0.124023f, +0.257812f }, + { +0.153320f, +0.056641f, -0.220215f }, + { +0.109863f, +0.121094f, -0.139160f }, + { +0.038574f, +0.007324f, -0.212891f }, + { +0.039062f, +0.062012f, -0.131836f }, + { +0.208008f, -0.072754f, -0.209473f }, + { +0.171875f, +0.010742f, -0.116699f }, + { +0.077148f, -0.097168f, -0.245605f }, + { +0.078613f, -0.015625f, -0.123535f }, + { +0.098633f, +0.010742f, -0.134277f }, + { +0.083496f, +0.059570f, -0.069336f }, + { +0.010254f, -0.018066f, -0.134766f }, + { +0.021973f, +0.028809f, -0.078125f }, + { +0.128418f, -0.071289f, -0.119141f }, + { +0.112793f, -0.009766f, -0.056152f }, + { +0.054199f, -0.070312f, -0.132812f }, + { +0.051758f, -0.022461f, -0.059570f }, + { +0.295410f, +0.126953f, -0.076172f }, + { +0.230957f, +0.245117f, +0.011230f }, + { +0.184082f, +0.102539f, -0.039551f }, + { +0.139648f, +0.199219f, -0.003418f }, + { +0.329590f, -0.016113f, -0.005859f }, + { +0.338379f, +0.108887f, +0.095703f }, + { +0.212891f, +0.009277f, -0.000488f }, + { +0.215820f, +0.087891f, +0.057617f }, + { +0.202148f, +0.129883f, +0.047852f }, + { +0.184570f, +0.232910f, +0.157227f }, + { +0.138672f, +0.083008f, +0.021973f }, + { +0.137207f, +0.145508f, +0.088379f }, + { +0.241211f, +0.018066f, +0.099121f }, + { +0.244629f, +0.101562f, +0.227539f }, + { +0.149414f, +0.028320f, +0.074219f }, + { +0.161621f, +0.065918f, +0.151855f }, + { -0.058594f, +0.127930f, -0.189453f }, + { -0.042480f, +0.219238f, -0.128418f }, + { -0.152832f, +0.079102f, -0.203125f }, + { -0.160156f, +0.169922f, -0.109375f }, + { -0.063965f, +0.027832f, -0.131836f }, + { -0.037598f, +0.109863f, -0.095703f }, + { -0.132812f, -0.030762f, -0.219238f }, + { -0.126953f, +0.062500f, -0.115723f }, + { -0.095703f, +0.088379f, -0.087402f }, + { -0.108887f, +0.182617f, -0.033203f }, + { -0.198242f, +0.083496f, -0.091797f }, + { -0.241211f, +0.182129f, -0.003906f }, + { -0.077148f, +0.007324f, -0.062500f }, + { -0.085449f, +0.081543f, -0.041016f }, + { -0.134277f, +0.003906f, -0.103516f }, + { -0.155762f, +0.080566f, -0.021484f }, + { +0.024902f, +0.109863f, -0.007812f }, + { +0.011719f, +0.178223f, +0.041992f }, + { -0.029297f, +0.061035f, +0.004395f }, + { -0.057129f, +0.124512f, +0.041504f }, + { +0.045410f, +0.042969f, +0.017090f }, + { +0.049805f, +0.100098f, +0.057617f }, + { -0.004395f, +0.001953f, +0.008301f }, + { -0.008789f, +0.056152f, +0.041992f }, + { -0.011719f, +0.113770f, +0.062012f }, + { -0.020020f, +0.237793f, +0.119141f }, + { -0.083008f, +0.055176f, +0.051270f }, + { -0.099121f, +0.133789f, +0.111328f }, + { +0.024902f, +0.041504f, +0.084961f }, + { +0.022461f, +0.121582f, +0.146484f }, + { -0.040527f, +0.007812f, +0.048340f }, + { -0.042480f, +0.068359f, +0.100586f }, +}; + +/* \brief HOC for 1st frequency block */ +const float ambe_hoc0_tbl[128][4] = { + { +0.368652f, -0.230469f, -0.191406f, +0.158203f }, + { +0.300781f, -0.079102f, -0.018066f, +0.124512f }, + { +0.318848f, -0.202637f, +0.081055f, -0.000977f }, + { +0.380371f, +0.044922f, +0.263184f, +0.108398f }, + { +0.503418f, -0.068848f, -0.130371f, -0.029785f }, + { +0.493652f, +0.189453f, -0.176758f, +0.151855f }, + { +0.473145f, -0.065430f, +0.067383f, -0.150391f }, + { +0.617188f, +0.196777f, +0.101074f, -0.020508f }, + { +0.133301f, -0.141602f, -0.314453f, +0.069336f }, + { +0.171875f, +0.024902f, -0.153809f, +0.085449f }, + { +0.188477f, -0.050781f, -0.064941f, -0.079590f }, + { +0.287598f, +0.139648f, +0.000000f, +0.042969f }, + { +0.294922f, -0.118164f, -0.231445f, -0.161621f }, + { +0.299805f, +0.123047f, -0.267090f, -0.032227f }, + { +0.305176f, +0.091309f, -0.084473f, -0.280273f }, + { +0.353027f, +0.357422f, -0.059082f, -0.093262f }, + { +0.181641f, -0.402832f, -0.132812f, +0.117188f }, + { +0.056641f, -0.196289f, -0.078613f, +0.199219f }, + { +0.073242f, -0.285156f, +0.083984f, +0.112305f }, + { +0.130371f, -0.092773f, +0.102051f, +0.252441f }, + { +0.127930f, -0.209473f, +0.001953f, -0.077148f }, + { +0.140137f, -0.115723f, -0.056152f, +0.062012f }, + { +0.093262f, -0.223145f, +0.181641f, -0.052734f }, + { +0.140137f, -0.081055f, +0.076660f, -0.004883f }, + { +0.151855f, -0.540039f, -0.249512f, -0.097656f }, + { +0.043457f, -0.258301f, -0.175781f, -0.067383f }, + { -0.006836f, -0.333008f, +0.044922f, -0.077637f }, + { -0.005371f, -0.128906f, +0.011719f, +0.031738f }, + { -0.057617f, -0.366699f, -0.193848f, -0.279297f }, + { +0.010742f, -0.143555f, -0.141113f, -0.193359f }, + { +0.091309f, -0.168945f, +0.073730f, -0.280762f }, + { +0.033203f, -0.056152f, +0.043457f, -0.147461f }, + { +0.061035f, -0.031250f, -0.012695f, +0.002441f }, + { +0.027344f, +0.083984f, +0.070801f, +0.087891f }, + { +0.109863f, +0.031250f, +0.213379f, +0.060547f }, + { +0.114746f, +0.263672f, +0.135254f, +0.241211f }, + { +0.098633f, +0.059570f, +0.027344f, -0.082031f }, + { +0.096191f, +0.186035f, +0.120605f, +0.023438f }, + { +0.153320f, +0.126953f, +0.167969f, -0.131348f }, + { +0.187500f, +0.382812f, +0.271973f, +0.023926f }, + { -0.037109f, -0.004395f, -0.013672f, -0.053711f }, + { -0.092285f, +0.080566f, -0.042969f, -0.004395f }, + { -0.035645f, +0.056641f, +0.065430f, -0.040527f }, + { -0.039551f, +0.233398f, +0.034180f, +0.030273f }, + { -0.083496f, +0.086426f, -0.035156f, -0.126465f }, + { -0.012207f, +0.125488f, -0.012207f, -0.067871f }, + { +0.020020f, +0.161621f, +0.066406f, -0.288086f }, + { +0.031738f, +0.359375f, +0.072754f, -0.100586f }, + { -0.165039f, -0.411621f, +0.177734f, +0.067871f }, + { -0.206543f, -0.182617f, +0.192383f, +0.162598f }, + { -0.123047f, -0.277344f, +0.314453f, -0.124512f }, + { -0.083008f, +0.016602f, +0.373047f, +0.057129f }, + { -0.126953f, -0.173828f, +0.100586f, -0.009277f }, + { -0.104492f, -0.090332f, +0.078125f, +0.043945f }, + { -0.062500f, -0.104004f, +0.129395f, -0.098633f }, + { -0.089355f, +0.000000f, +0.173340f, -0.002441f }, + { -0.292969f, -0.209961f, +0.100098f, -0.048340f }, + { -0.272461f, -0.037598f, +0.094238f, +0.042480f }, + { -0.202637f, -0.133301f, +0.220215f, -0.114258f }, + { -0.260742f, -0.022461f, +0.145508f, -0.096191f }, + { -0.166992f, -0.216797f, +0.104980f, -0.225098f }, + { -0.135254f, -0.033691f, +0.068359f, -0.055664f }, + { -0.112305f, -0.006348f, +0.209473f, -0.226562f }, + { -0.170410f, +0.009277f, +0.118164f, -0.137207f }, + { +0.031738f, -0.041992f, -0.145020f, +0.123535f }, + { -0.055176f, +0.095215f, -0.163086f, +0.234375f }, + { -0.012695f, -0.008301f, -0.062012f, +0.044922f }, + { +0.026855f, +0.073242f, -0.078613f, +0.127441f }, + { +0.061035f, +0.071777f, -0.164062f, -0.074707f }, + { +0.032715f, +0.158203f, -0.273926f, +0.091309f }, + { +0.043457f, +0.069824f, -0.086426f, +0.019043f }, + { +0.110352f, +0.188965f, -0.123047f, +0.001465f }, + { -0.098633f, -0.028320f, -0.292480f, -0.001953f }, + { -0.182129f, +0.115723f, -0.262207f, +0.152344f }, + { -0.104492f, +0.087891f, -0.128418f, +0.059570f }, + { -0.074219f, +0.251465f, -0.176270f, +0.064453f }, + { +0.066406f, +0.044434f, -0.376465f, -0.171387f }, + { -0.137207f, +0.248047f, -0.447266f, -0.061035f }, + { -0.079102f, +0.166992f, -0.187500f, -0.085938f }, + { +0.023926f, +0.445801f, -0.224121f, -0.011719f }, + { -0.135254f, -0.413574f, -0.166016f, +0.082520f }, + { -0.253906f, -0.163086f, -0.117188f, +0.361328f }, + { -0.112793f, -0.232422f, -0.071289f, +0.090820f }, + { -0.142090f, -0.087891f, -0.008789f, +0.218262f }, + { -0.152344f, -0.179688f, -0.167480f, -0.075684f }, + { -0.175293f, -0.068848f, -0.040527f, +0.104492f }, + { -0.083496f, -0.076660f, +0.010742f, -0.019531f }, + { -0.116211f, -0.021484f, -0.002930f, +0.035156f }, + { -0.355469f, -0.489258f, -0.295410f, -0.100098f }, + { -0.340332f, -0.144043f, -0.349121f, +0.018555f }, + { -0.284180f, -0.334961f, -0.099121f, -0.101074f }, + { -0.315918f, -0.142578f, -0.134277f, +0.039551f }, + { -0.216797f, -0.330566f, -0.449219f, -0.278809f }, + { -0.331543f, -0.104004f, -0.266113f, -0.172363f }, + { -0.222168f, -0.113770f, -0.056641f, -0.235352f }, + { -0.184082f, -0.051270f, -0.050293f, -0.050293f }, + { -0.216797f, +0.050293f, -0.051758f, +0.013672f }, + { -0.307129f, +0.110352f, -0.047852f, +0.108887f }, + { -0.177734f, +0.058105f, +0.038574f, -0.020508f }, + { -0.186523f, +0.168457f, +0.119629f, +0.154785f }, + { -0.202637f, +0.151855f, -0.071289f, -0.025879f }, + { -0.166504f, +0.268555f, -0.055176f, -0.051270f }, + { -0.174316f, +0.195312f, +0.121582f, -0.076660f }, + { -0.192871f, +0.410156f, +0.173340f, +0.062012f }, + { -0.344238f, +0.020508f, -0.108887f, -0.073730f }, + { -0.358398f, +0.202148f, -0.198242f, +0.062012f }, + { -0.256836f, +0.097656f, +0.051270f, -0.108887f }, + { -0.347656f, +0.192383f, +0.016602f, -0.018066f }, + { -0.279297f, +0.163574f, -0.087891f, -0.258301f }, + { -0.338379f, +0.336426f, -0.150391f, -0.093262f }, + { -0.157227f, +0.295410f, -0.005371f, -0.210938f }, + { -0.230469f, +0.612793f, -0.034668f, -0.157715f }, + { -0.537109f, -0.526367f, -0.042969f, +0.113770f }, + { -0.418945f, -0.219238f, -0.023926f, +0.166504f }, + { -0.529785f, -0.210938f, +0.204102f, +0.054688f }, + { -0.502441f, +0.004883f, +0.093750f, +0.241211f }, + { -0.563477f, -0.197754f, -0.055176f, -0.049805f }, + { -0.459473f, +0.037598f, +0.038086f, -0.033691f }, + { -0.433594f, -0.070801f, +0.233887f, -0.039062f }, + { -0.403320f, +0.151855f, +0.285156f, +0.062500f }, + { -0.892090f, -0.333984f, -0.130371f, -0.081543f }, + { -0.641113f, -0.040039f, -0.223145f, +0.060059f }, + { -0.837891f, -0.131348f, +0.168945f, +0.107422f }, + { -0.689453f, +0.212402f, -0.022949f, +0.026855f }, + { -0.541504f, -0.253906f, -0.076172f, -0.286133f }, + { -0.517578f, -0.001465f, -0.104492f, -0.234863f }, + { -0.566406f, -0.024414f, +0.240723f, -0.166992f }, + { -0.461914f, +0.264160f, +0.172363f, -0.184082f }, +}; + +/* \brief HOC for 2nd frequency block */ +const float ambe_hoc1_tbl[64][4] = { + { -0.364258f, +0.365723f, +0.244141f, +0.045410f }, + { -0.579102f, +0.200684f, -0.009766f, +0.049316f }, + { -0.227051f, +0.341309f, -0.048828f, -0.008301f }, + { -0.329102f, +0.122559f, -0.037598f, -0.017090f }, + { -0.258301f, +0.068848f, +0.211426f, +0.120605f }, + { -0.601074f, -0.050293f, +0.181641f, +0.054199f }, + { -0.243652f, +0.145996f, +0.137207f, -0.101562f }, + { -0.426270f, -0.007812f, +0.097168f, -0.137695f }, + { +0.023926f, +0.219727f, +0.226074f, -0.005371f }, + { -0.070801f, +0.079102f, +0.071289f, -0.009277f }, + { +0.080566f, +0.355957f, +0.059570f, -0.162598f }, + { +0.003906f, +0.132812f, +0.021484f, -0.132812f }, + { -0.000488f, -0.001465f, +0.284180f, -0.035645f }, + { -0.137207f, -0.091309f, +0.169434f, -0.136719f }, + { +0.102539f, +0.044922f, +0.177734f, -0.221191f }, + { -0.154785f, +0.020020f, +0.014160f, -0.318848f }, + { -0.086426f, +0.261719f, +0.030762f, +0.185059f }, + { -0.242676f, +0.156250f, -0.208008f, +0.111816f }, + { +0.051758f, +0.322266f, -0.170898f, +0.121094f }, + { -0.092285f, +0.228516f, -0.233398f, -0.122070f }, + { +0.007324f, +0.097656f, +0.082520f, +0.166504f }, + { -0.112305f, +0.073730f, -0.078613f, +0.075684f }, + { +0.019531f, +0.155273f, -0.075684f, +0.012207f }, + { -0.011719f, +0.042969f, -0.159180f, -0.062500f }, + { +0.355469f, +0.314941f, +0.152832f, +0.054199f }, + { +0.173340f, +0.168945f, -0.030273f, +0.022949f }, + { +0.320312f, +0.309570f, -0.178711f, -0.014648f }, + { +0.189941f, +0.147949f, -0.199707f, -0.141113f }, + { +0.215332f, +0.123047f, +0.171387f, +0.117188f }, + { +0.154297f, +0.027344f, +0.068359f, +0.029785f }, + { +0.299805f, +0.122559f, +0.015625f, -0.117676f }, + { +0.220703f, -0.037598f, -0.042969f, -0.210449f }, + { -0.127441f, -0.118652f, +0.057617f, +0.198242f }, + { -0.282227f, -0.165527f, -0.117676f, +0.165527f }, + { -0.155273f, -0.030762f, -0.020996f, -0.026855f }, + { -0.260742f, -0.062500f, -0.218750f, -0.112305f }, + { -0.260742f, -0.344727f, +0.182129f, +0.091797f }, + { -0.519043f, -0.346680f, -0.088867f, +0.025879f }, + { -0.222168f, -0.140625f, +0.066895f, +0.032715f }, + { -0.240234f, -0.235840f, -0.047363f, -0.126953f }, + { +0.021973f, -0.033203f, +0.010742f, +0.057617f }, + { -0.035645f, -0.162598f, +0.003418f, -0.014160f }, + { +0.052734f, -0.026367f, +0.004883f, -0.048340f }, + { +0.004883f, -0.104492f, -0.074707f, -0.147949f }, + { +0.040527f, -0.177246f, +0.221680f, +0.105957f }, + { -0.020508f, -0.354980f, +0.113770f, -0.000488f }, + { +0.127441f, -0.160645f, +0.110840f, -0.043945f }, + { +0.054688f, -0.319824f, +0.056152f, -0.223145f }, + { +0.102051f, +0.036621f, -0.136719f, +0.276367f }, + { -0.058105f, -0.117676f, -0.179199f, +0.076172f }, + { +0.122559f, +0.030762f, -0.126953f, +0.084473f }, + { +0.071777f, -0.081055f, -0.339355f, -0.026855f }, + { +0.137695f, -0.191895f, +0.022461f, +0.213379f }, + { -0.035156f, -0.339844f, -0.115234f, +0.175293f }, + { +0.124512f, -0.161133f, -0.121582f, +0.054199f }, + { -0.000977f, -0.270508f, -0.201172f, -0.075195f }, + { +0.356934f, -0.011230f, -0.075195f, +0.129395f }, + { +0.287598f, -0.123535f, -0.037598f, -0.047363f }, + { +0.569336f, +0.065918f, -0.143555f, +0.021973f }, + { +0.340820f, -0.035156f, -0.280762f, -0.009277f }, + { +0.378906f, -0.094238f, +0.183594f, -0.002930f }, + { +0.308105f, -0.334473f, +0.076172f, +0.063477f }, + { +0.587891f, -0.206055f, +0.034668f, +0.000977f }, + { +0.332520f, -0.322754f, -0.201172f, -0.001465f }, +}; + +/* \brief HOC for 3rd frequency block */ +const float ambe_hoc2_tbl[64][4] = { + { -0.107422f, +0.202637f, +0.255859f, -0.011719f }, + { -0.214844f, -0.538086f, -0.206543f, +0.003418f }, + { -0.027832f, -0.366211f, -0.125488f, +0.198730f }, + { +0.109375f, -0.022461f, +0.040039f, -0.047852f }, + { -0.603516f, -0.089844f, +0.060059f, +0.119629f }, + { +0.167480f, -0.050781f, -0.051270f, +0.058594f }, + { -0.235352f, -0.060059f, -0.284180f, +0.049805f }, + { +0.449219f, -0.298828f, -0.132324f, -0.052734f }, + { +0.189941f, -0.092285f, -0.009277f, +0.264648f }, + { -0.048828f, +0.046387f, -0.372559f, -0.064941f }, + { +0.484375f, +0.374023f, +0.152344f, -0.004395f }, + { +0.045410f, -0.102539f, +0.215820f, +0.086914f }, + { -0.036133f, +0.066895f, +0.014648f, -0.071289f }, + { -0.209961f, +0.056152f, +0.125488f, -0.217773f }, + { +0.041992f, +0.019043f, +0.048828f, +0.265137f }, + { -0.182129f, +0.083984f, -0.065430f, +0.020996f }, + { -0.022949f, -0.003418f, -0.002930f, +0.066895f }, + { -0.396484f, +0.604492f, -0.018555f, -0.075684f }, + { -0.437988f, -0.310547f, -0.026855f, +0.006836f }, + { -0.156738f, +0.153320f, -0.177734f, -0.184082f }, + { +0.028809f, +0.010742f, -0.126953f, -0.048340f }, + { +0.111328f, +0.125488f, +0.041504f, +0.038574f }, + { -0.100586f, +0.170410f, +0.048340f, +0.182129f }, + { -0.133301f, -0.140137f, -0.099609f, +0.027344f }, + { +0.587891f, -0.200684f, +0.144531f, +0.144043f }, + { -0.081543f, +0.023926f, +0.148926f, +0.001465f }, + { +0.227051f, -0.032227f, +0.150391f, +0.036133f }, + { -0.266602f, -0.096191f, +0.338867f, -0.118164f }, + { +0.357422f, -0.079102f, -0.019531f, +0.098145f }, + { -0.059082f, +0.314453f, +0.041992f, -0.142578f }, + { -0.124023f, +0.358887f, -0.032715f, +0.105469f }, + { +0.006348f, -0.163574f, -0.011719f, -0.043457f }, + { -0.362305f, +0.057129f, +0.021484f, +0.104980f }, + { -0.263184f, -0.176270f, +0.031738f, +0.164551f }, + { +0.124512f, +0.143555f, +0.284180f, +0.124512f }, + { +0.164551f, -0.268555f, -0.051270f, +0.056152f }, + { -0.062012f, +0.035156f, -0.174316f, +0.189941f }, + { +0.413574f, +0.090332f, -0.321777f, -0.175293f }, + { -0.005371f, -0.098633f, +0.035156f, -0.225586f }, + { -0.347656f, -0.123047f, -0.103516f, -0.166504f }, + { -0.021973f, +0.187988f, -0.074707f, +0.003906f }, + { +0.109375f, +0.112305f, -0.050293f, -0.279297f }, + { +0.183105f, +0.375488f, -0.160156f, -0.106445f }, + { -0.210938f, -0.065430f, +0.076172f, -0.016113f }, + { +0.116699f, -0.221191f, -0.288574f, -0.043945f }, + { -0.092773f, -0.245605f, -0.222656f, -0.220215f }, + { +0.183105f, +0.332520f, +0.093262f, +0.028809f }, + { -0.301758f, +0.245117f, +0.083496f, -0.078125f }, + { +0.253418f, +0.088379f, -0.116211f, -0.001953f }, + { +0.442383f, -0.003418f, +0.195312f, -0.007812f }, + { +0.220703f, -0.336426f, +0.158691f, -0.011719f }, + { -0.311035f, +0.226562f, -0.162109f, +0.028809f }, + { -0.541016f, +0.119141f, +0.203125f, -0.046387f }, + { +0.080566f, +0.074219f, +0.226562f, -0.167969f }, + { +0.281738f, -0.106445f, -0.126953f, -0.123047f }, + { -0.213379f, -0.111328f, +0.261230f, +0.187500f }, + { -0.010742f, -0.242676f, +0.092285f, +0.127441f }, + { +0.166504f, -0.171875f, +0.140625f, -0.141602f }, + { +0.302246f, +0.090820f, +0.070801f, -0.118164f }, + { +0.257812f, +0.179688f, -0.015625f, +0.187012f }, + { +0.548340f, +0.153809f, -0.108398f, -0.020508f }, + { -0.093262f, -0.379395f, +0.070312f, -0.080078f }, + { +0.236328f, +0.011719f, -0.321289f, +0.164062f }, + { +0.104492f, +0.204590f, -0.219238f, +0.086914f }, +}; + +/* \brief HOC for last frequency block */ +const float ambe_hoc3_tbl[64][4] = { + { +0.168457f, +0.110352f, +0.166504f, -0.110840f }, + { +0.338379f, +0.063965f, +0.101074f, +0.089844f }, + { +0.256836f, -0.126465f, +0.064941f, -0.130371f }, + { +0.408691f, -0.185547f, -0.034180f, +0.050781f }, + { +0.114258f, +0.246582f, -0.041992f, -0.150879f }, + { +0.395996f, +0.225586f, -0.069336f, -0.021973f }, + { +0.150879f, -0.011719f, -0.199707f, -0.177734f }, + { +0.257812f, +0.024414f, -0.229004f, +0.114258f }, + { -0.027832f, -0.088867f, +0.277832f, -0.020020f }, + { +0.104492f, -0.083496f, +0.072266f, +0.071777f }, + { -0.129395f, -0.252930f, +0.079590f, -0.026855f }, + { +0.073730f, -0.316406f, -0.042969f, +0.115723f }, + { -0.029297f, -0.039551f, +0.025879f, -0.170898f }, + { +0.093750f, +0.014160f, -0.037598f, -0.006348f }, + { -0.190918f, -0.177246f, -0.153809f, -0.147461f }, + { -0.033203f, -0.144531f, -0.168457f, +0.036133f }, + { -0.096191f, +0.198242f, +0.156250f, +0.006348f }, + { +0.113770f, +0.264160f, +0.106934f, +0.123047f }, + { -0.062500f, +0.043457f, +0.015137f, +0.023438f }, + { +0.010254f, +0.083496f, -0.049805f, +0.229980f }, + { -0.226074f, +0.414062f, +0.004395f, -0.054199f }, + { +0.035645f, +0.371094f, -0.165039f, +0.094727f }, + { -0.137207f, +0.166504f, -0.085449f, -0.091309f }, + { -0.058594f, +0.130859f, -0.217285f, +0.048340f }, + { -0.296875f, +0.042480f, +0.116699f, -0.131348f }, + { -0.204590f, +0.000488f, +0.152344f, +0.177734f }, + { -0.495605f, -0.143066f, +0.133789f, +0.004395f }, + { -0.298828f, -0.233887f, -0.041016f, +0.205078f }, + { -0.548828f, +0.221191f, +0.065430f, +0.030273f }, + { -0.257812f, +0.170410f, -0.021484f, +0.109863f }, + { -0.424316f, +0.047852f, -0.164062f, +0.008301f }, + { -0.226562f, -0.054199f, -0.056641f, +0.061523f }, + { +0.000000f, -2.388672f, +0.000000f, -2.138672f }, + { +0.000000f, -2.013672f, +0.000000f, -1.888672f }, + { +0.000000f, +0.000000f, +0.334961f, +0.334961f }, + { +0.665039f, +0.665039f, +1.000000f, +1.000000f }, + { +1.225098f, +1.225098f, +1.465820f, +1.079590f }, + { +1.676270f, +1.337402f, +1.780273f, +1.127441f }, + { +1.450195f, +1.450195f, +1.649902f, +1.649902f }, + { +1.850098f, +1.850098f, +2.049805f, +1.673340f }, + { +1.395020f, +1.629883f, +1.756836f, +2.109863f }, + { +1.799316f, +2.498535f, +2.039551f, +2.101074f }, + { +2.191895f, +2.130859f, +2.202637f, +2.261719f }, + { +2.370117f, +2.280762f, +2.432617f, +2.474609f }, + { +2.104980f, +2.435059f, +1.996094f, +2.682129f }, + { +2.223633f, +2.595215f, +2.170410f, +2.867676f }, + { +2.138672f, +1.921387f, +2.373535f, +2.008789f }, + { +2.457031f, +1.873047f, +2.549805f, +2.189941f }, + { +2.389648f, +2.715332f, +2.370605f, +2.928223f }, + { +2.570312f, +2.886230f, +2.679688f, +3.064941f }, + { +0.299805f, +0.799805f, +0.483887f, +1.406250f }, + { +0.446289f, +2.411133f, +0.918457f, +1.759277f }, + { +1.305664f, +2.787598f, +1.577637f, +2.526855f }, + { +1.667969f, +2.817871f, +1.482910f, +2.258301f }, + { +0.966797f, +2.099121f, +0.885254f, +2.812988f }, + { +1.198242f, +2.594727f, +1.218262f, +3.094238f }, + { +2.020020f, +3.117676f, +2.213379f, +3.314453f }, + { +2.476074f, +3.186523f, +2.285156f, +3.489746f }, + { +1.679688f, +3.270996f, +1.525391f, +3.752930f }, + { +1.190430f, +3.576172f, +1.341797f, +4.195801f }, + { +0.269531f, +3.548828f, +0.729004f, +3.265625f }, + { +0.982910f, +3.903320f, +0.971680f, +4.340332f }, + { +1.841309f, +3.510742f, +1.849121f, +4.137207f }, + { +1.952637f, +3.729004f, +2.333496f, +3.879395f }, +}; + +/* \brief Interpolation ratios for subframe 0 magnitude prediction */ +const float ambe_sf0_interp_tbl[4][2] = { + { +0.90f, +0.10f }, + { +0.70f, +0.30f }, + { +0.50f, +0.50f }, + { +0.17f, +0.83f }, +}; + +/* \brief Prediction Error [1:4] for subframe 0 */ +const float ambe_sf0_perr14_tbl[64][4] = { + { -0.106445f, +0.011230f, -0.166992f, -0.022461f }, + { -0.056641f, +0.166504f, -0.139648f, -0.102051f }, + { -0.004883f, +0.052246f, -0.106934f, +0.084961f }, + { +0.031250f, +0.188477f, -0.142578f, +0.077148f }, + { -0.089355f, -0.052246f, -0.041016f, -0.159180f }, + { -0.043945f, +0.056152f, -0.008301f, -0.088379f }, + { -0.001953f, +0.006348f, -0.062012f, -0.007324f }, + { -0.060059f, +0.075684f, -0.046387f, +0.006348f }, + { -0.340332f, -0.018555f, -0.109863f, +0.066895f }, + { -0.197754f, +0.207520f, -0.016113f, +0.043457f }, + { -0.138672f, -0.012695f, -0.009766f, +0.166992f }, + { -0.083008f, +0.154297f, +0.046875f, +0.236816f }, + { -0.465332f, -0.145020f, +0.042480f, -0.085938f }, + { -0.201172f, +0.037598f, +0.015137f, -0.050293f }, + { -0.211914f, -0.160156f, +0.062500f, +0.029785f }, + { -0.292969f, +0.076172f, +0.160156f, +0.107910f }, + { +0.110840f, +0.004883f, -0.197754f, -0.041504f }, + { +0.135742f, +0.063965f, -0.063477f, +0.041016f }, + { +0.184570f, +0.004395f, -0.186523f, +0.133789f }, + { +0.144043f, +0.087891f, -0.006348f, +0.194336f }, + { +0.043945f, -0.115723f, -0.107422f, -0.091797f }, + { +0.095215f, -0.048340f, -0.037598f, +0.004883f }, + { +0.108398f, -0.133789f, -0.107910f, +0.060059f }, + { +0.085449f, -0.027832f, -0.033203f, +0.095215f }, + { -0.079590f, -0.103516f, -0.095703f, +0.035156f }, + { -0.003906f, -0.009277f, +0.000977f, +0.024902f }, + { -0.001953f, -0.091309f, -0.061035f, +0.164551f }, + { +0.002441f, +0.032715f, +0.020020f, +0.109863f }, + { -0.158691f, -0.269531f, -0.145996f, -0.108887f }, + { -0.060547f, -0.063965f, +0.024902f, +0.010742f }, + { -0.067383f, -0.315918f, -0.055176f, +0.086426f }, + { +0.007812f, -0.086426f, +0.082520f, +0.125488f }, + { +0.065430f, +0.081543f, -0.076172f, -0.069824f }, + { +0.112305f, +0.234375f, -0.011719f, -0.095215f }, + { +0.035645f, +0.092285f, -0.001465f, +0.006348f }, + { +0.099609f, +0.224609f, +0.073242f, +0.075195f }, + { +0.080078f, +0.014648f, +0.022949f, -0.147461f }, + { +0.103516f, +0.095215f, +0.136230f, -0.152344f }, + { +0.053223f, +0.017578f, +0.009277f, -0.038574f }, + { +0.111816f, +0.073730f, +0.125488f, +0.003906f }, + { -0.020996f, +0.013184f, +0.068848f, -0.052734f }, + { -0.047852f, +0.137695f, +0.079590f, -0.054199f }, + { -0.075195f, +0.050781f, +0.046875f, +0.030762f }, + { -0.008301f, +0.155273f, +0.186523f, +0.063477f }, + { -0.093750f, -0.066406f, +0.119629f, -0.083008f }, + { -0.101562f, +0.080078f, +0.179688f, -0.135254f }, + { -0.076172f, -0.001465f, +0.160156f, +0.064941f }, + { -0.078613f, -0.005859f, +0.341309f, +0.011230f }, + { +0.291016f, +0.043945f, -0.125488f, -0.176270f }, + { +0.203613f, +0.027832f, +0.007324f, -0.041016f }, + { +0.312500f, -0.001953f, -0.063965f, +0.053711f }, + { +0.380371f, +0.174316f, +0.078125f, +0.021973f }, + { +0.175293f, -0.284180f, -0.069824f, -0.177246f }, + { +0.192871f, -0.101562f, +0.063965f, -0.082520f }, + { +0.282227f, -0.250977f, +0.011719f, +0.054199f }, + { +0.394043f, -0.064453f, +0.111816f, -0.040527f }, + { +0.039062f, -0.067871f, +0.031738f, -0.043457f }, + { +0.040527f, -0.026367f, +0.070801f, +0.014648f }, + { +0.117188f, -0.115723f, +0.039062f, +0.036133f }, + { +0.145508f, +0.000000f, +0.072754f, +0.079590f }, + { +0.027832f, -0.163574f, +0.114746f, -0.115234f }, + { +0.062500f, -0.047852f, +0.152832f, -0.059082f }, + { +0.027344f, -0.190430f, +0.152344f, +0.042480f }, + { +0.178223f, -0.077148f, +0.190918f, +0.095215f }, +}; + +/* \brief Prediction Error [5:8] for subframe 0 */ +const float ambe_sf0_perr58_tbl[32][4] = { + { -0.006836f, +0.065430f, +0.135254f, +0.032715f }, + { -0.019531f, +0.053223f, +0.054688f, -0.052734f }, + { -0.143066f, +0.037598f, +0.059082f, +0.054688f }, + { -0.104004f, +0.000000f, +0.097168f, -0.123047f }, + { -0.003906f, -0.020996f, +0.027832f, +0.066895f }, + { -0.026367f, -0.023926f, +0.021973f, -0.013672f }, + { -0.067383f, -0.093262f, +0.105469f, +0.100586f }, + { -0.109863f, -0.104980f, +0.027832f, -0.027832f }, + { +0.137207f, +0.000000f, +0.125488f, +0.075684f }, + { +0.092773f, +0.020508f, +0.126953f, -0.112305f }, + { +0.043945f, -0.060547f, +0.069824f, -0.016113f }, + { +0.020996f, -0.105957f, +0.146484f, -0.070312f }, + { +0.191406f, -0.145508f, +0.053711f, +0.034668f }, + { +0.141113f, -0.080566f, +0.025391f, -0.080078f }, + { +0.056152f, -0.142578f, +0.020996f, +0.047363f }, + { +0.000488f, -0.165527f, +0.020996f, -0.149902f }, + { +0.020996f, +0.133301f, +0.024902f, +0.095215f }, + { -0.061523f, +0.183594f, -0.054688f, -0.006836f }, + { -0.021973f, +0.042969f, -0.031738f, +0.020020f }, + { -0.090332f, +0.081543f, -0.018066f, -0.071777f }, + { -0.032227f, +0.068359f, -0.101074f, +0.124023f }, + { +0.017090f, +0.067383f, -0.151855f, -0.009766f }, + { -0.060059f, -0.056152f, -0.078613f, +0.083496f }, + { -0.148438f, +0.006836f, -0.104980f, -0.041016f }, + { +0.171387f, +0.106445f, -0.006836f, +0.029785f }, + { +0.111328f, +0.072754f, -0.046387f, -0.137695f }, + { +0.058594f, +0.019531f, +0.007324f, -0.016602f }, + { +0.007812f, -0.005371f, -0.022461f, -0.098145f }, + { +0.105957f, -0.038086f, -0.065430f, +0.114258f }, + { +0.124512f, -0.021484f, -0.109863f, -0.014160f }, + { +0.041504f, -0.032227f, -0.036133f, +0.019043f }, + { -0.015137f, -0.137207f, -0.127930f, -0.061035f }, +}; + +/*! @} */ diff --git a/src/codec/tone.c b/src/codec/tone.c new file mode 100644 index 0000000..e958706 --- /dev/null +++ b/src/codec/tone.c @@ -0,0 +1,210 @@ +/* GMR-1 AMBE vocoder - Tone frames */ + +/* (C) 2013 by Sylvain Munaut <tnt@246tNt.com> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/*! \addtogroup codec/private + * @{ + */ + +/*! \file codec/tone.c + * \brief Osmocom GMR-1 AMBE vocoder tone frames handling + */ + +#include <errno.h> +#include <math.h> +#include <stdint.h> +#include <string.h> + +#include "private.h" + + +/*! \brief Structure describing a dual-frequency tone */ +struct tone_desc { + char *name; + int f1; + int f2; +}; + +/*! \brief DTMF tones descriptions */ +static const struct tone_desc dtmf_tones[] = { + { "1", 1209, 697 }, + { "4", 1209, 770 }, + { "7", 1209, 852 }, + { "*", 1209, 941 }, + { "2", 1336, 697 }, + { "5", 1336, 770 }, + { "8", 1336, 852 }, + { "0", 1336, 941 }, + { "3", 1477, 697 }, + { "6", 1477, 770 }, + { "9", 1477, 852 }, + { "#", 1477, 941 }, + { "A", 1633, 697 }, + { "B", 1633, 770 }, + { "C", 1633, 852 }, + { "D", 1633, 941 }, +}; + +/*! \brief KNOX tones descriptions */ +static const struct tone_desc knox_tones[] = { + { "1", 1052, 606 }, + { "4", 1052, 672 }, + { "7", 1052, 743 }, + { "*", 1052, 820 }, + { "2", 1162, 606 }, + { "5", 1162, 672 }, + { "8", 1162, 743 }, + { "0", 1162, 820 }, + { "3", 1297, 606 }, + { "6", 1297, 672 }, + { "9", 1297, 743 }, + { "#", 1297, 820 }, + { "A", 1430, 606 }, + { "B", 1430, 672 }, + { "C", 1430, 743 }, + { "D", 1430, 820 }, +}; + +/*! \brief Call progress tones descriptions */ +static const struct tone_desc call_progress_tones[] = { + { "Dial", 440, 350 }, + { "Ring", 480, 440 }, + { "Busy", 630, 480 }, + { "????", 490, 350 }, +}; + + +/*! \brief Synthesize and add a tone to a given audio buffer + * \param[out] audio Audio buffer to mix the tone into + * \param[in] N number of audio samples to generate + * \param[in] ampl Tone amplitude + * \param[in] freq_hz Tone frequency in Hertz + * \param[inout] phase_p Pointer to phase variable to use + */ +static void +tone_gen(int16_t *audio, int N, int ampl, int freq_hz, float *phase_p) +{ + float phase, phase_step; + int i; + + phase = *phase_p; + phase_step = (2.0f * M_PI * freq_hz) / AMBE_RATE; + + for (i=0; i<N; i++) + { + audio[i] += (int16_t)(ampl * cosf(phase)); + phase += phase_step; + } + + *phase_p = phase; +} + + +/*! \brief Decodes an AMBE tone frame + * \param[in] dec AMBE decoder state + * \param[out] audio Output audio buffer + * \param[in] N number of audio samples to produce (152..168) + * \param[in] frame Frame data (10 bytes = 80 bits). Must be tone frame ! + * \returns 0 for success. -EINVAL if frame was invalid. + */ +int +ambe_decode_tone(struct ambe_decoder *dec, + int16_t *audio, int N, const uint8_t *frame) +{ + int p_sf_sel, p_log_ampl, p_freq; + int i, j, cnt; + int start, stop; + int amplitude; + + /* Decode parameters */ + p_sf_sel = frame[0] & 3; + p_log_ampl = frame[1]; + + p_freq = 0; + for (i=0; i<8; i++) { + cnt = 0; + for (j=0; j<8; j++) + cnt += (frame[j] >> (7-i)) & 1; + p_freq = (p_freq << 1) | (cnt >= 4); + } + + /* Clear audio */ + memset(audio, 0x00, sizeof(int16_t) * N); + + /* Audio start / stop */ + start = (p_sf_sel & 2) ? 0 : N << 1; + stop = (p_sf_sel & 1) ? ((N << 1) - 1) : (N - 1); + + if (start < stop) + return 0; + + /* Compute amplitude */ + amplitude = (int)(32767.0f * exp2f(((float)p_log_ampl-255.0f)/17.0f)); + + /* Interpret frequency code */ + if (p_freq == 0xff) + { + /* Inactive, nothing to do */ + } + else if ((p_freq >= 0xa0) && (p_freq <= 0xa3)) + { + /* Call progress tone */ + int cpi = p_freq & 0xf; + + tone_gen(&audio[start], stop-start+1, amplitude >> 1, + call_progress_tones[cpi].f1, &dec->tone_phase_f1); + tone_gen(&audio[start], stop-start+1, amplitude >> 1, + call_progress_tones[cpi].f2, &dec->tone_phase_f2); + } + else if ((p_freq >= 0x90) && (p_freq <= 0x9f)) + { + /* Call progress tone */ + int ki = p_freq & 0xf; + + tone_gen(&audio[start], stop-start+1, amplitude >> 1, + knox_tones[ki].f1, &dec->tone_phase_f1); + tone_gen(&audio[start], stop-start+1, amplitude >> 1, + knox_tones[ki].f2, &dec->tone_phase_f2); + } + else if ((p_freq >= 0x80) && (p_freq <= 0x8f)) + { + /* Call progress tone */ + int di = p_freq & 0xf; + + tone_gen(&audio[start], stop-start+1, amplitude >> 1, + dtmf_tones[di].f1, &dec->tone_phase_f1); + tone_gen(&audio[start], stop-start+1, amplitude >> 1, + dtmf_tones[di].f2, &dec->tone_phase_f2); + } + else if (p_freq < 0x7f) + { + int freq_hz = (p_freq * 125) >> 2; /* 31.25 Hz increments */ + + tone_gen(&audio[start], stop-start+1, amplitude, + freq_hz, &dec->tone_phase_f1); + } + else + { + /* Invalid */ + return -EINVAL; + } + + return 0; +} + +/*! @} */ diff --git a/src/gmr1_ambe_decode.c b/src/gmr1_ambe_decode.c new file mode 100644 index 0000000..5f61ac0 --- /dev/null +++ b/src/gmr1_ambe_decode.c @@ -0,0 +1,195 @@ +/* GMR-1 Codec decoder tool */ + +/* (C) 2013 by Sylvain Munaut <tnt@246tNt.com> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <string.h> + +#include <osmocom/gmr1/codec/codec.h> + + +static const uint8_t wav_hdr[] = { + /* WAV header */ + 'R', 'I', 'F', 'F', /* ChunkID */ + 0x00, 0x00, 0x00, 0x00, /* ChunkSize */ + 'W', 'A', 'V', 'E', /* Format */ + + /* Sub chunk: format */ + 'f', 'm', 't', ' ', /* Subchunk1ID */ + 0x10, 0x00, 0x00, 0x00, /* Subchunk1Size */ + 0x01, 0x00, /* AudioFormat: PCM */ + 0x01, 0x00, /* NumChannels: Mono */ + 0x40, 0x1f, 0x00, 0x00, /* SampleRate: 8000 Hz */ + 0x80, 0x3e, 0x00, 0x00, /* ByteRate: 16k/s */ + 0x02, 0x00, /* BlockAlign: 2 bytes */ + 0x10, 0x00, /* BitsPerSample: 16 */ + + /* Sub chunk: data */ + 'd', 'a', 't', 'a', /* Subchunk2ID */ + 0x00, 0x00, 0x00, 0x00, /* Subchunk2Size */ +}; + +static uint32_t +le32(uint32_t v) +{ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return v; +#else + return ((v & 0x000000ff) << 24) | + ((v & 0x0000ff00) << 8) | + ((v & 0x00ff0000) >> 8) | + ((v & 0xff000000) >> 24); +#endif +} + +static uint16_t +le16(uint16_t v) +{ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return v; +#else + return ((v & 0x00ff) << 8) | + ((v & 0xff00) >> 8); +#endif +} + +int main(int argc, char *argv[]) +{ + struct gmr1_codec *codec = NULL; + FILE *fin, *fout; + int is_wave = 0, l, rv; + + /* Arguments */ + if (argc > 3) { + fprintf(stderr, "Usage: %s [in_file [out_file]]\n", argv[0]); + return -1; + } + + if ((argc < 2) || !strcmp(argv[1], "-")) + fin = stdin; + else { + fin = fopen(argv[1], "rb"); + if (!fin) { + fprintf(stderr, "[!] Unable to open input file\n"); + return -1; + } + } + + if ((argc < 3) || !strcmp(argv[2], "-")) + fout = stdout; + else { + fout = fopen(argv[2], "wb"); + if (!fout) { + fprintf(stderr, "[!] Unable to open output file\n"); + return -1; + } + + l = strlen(argv[2]); + + if ((l > 4) && (!strcmp(".wav", &argv[2][l-4]))) + is_wave = 1; + } + + /* Write inital wave header */ + if (is_wave) { + rv = fwrite(wav_hdr, sizeof(wav_hdr), 1, fout); + if (rv != 1) { + fprintf(stderr, "[!] Failed to write WAV header\n"); + goto exit; + } + } + + /* Init decoder */ + codec = gmr1_codec_alloc(); + if (!codec) + goto exit; + + /* Process all frames */ + l = 0; + + while (!feof(fin)) + { + uint8_t frame[10]; + int16_t audio[160]; + int rv, i; + + /* Read input frame */ + rv = fread(frame, 1, 10, fin); + if (rv != 10) + break; + + /* Decompress */ + rv = gmr1_codec_decode_frame(codec, audio, 160, frame, 0); + if (rv) { + fprintf(stderr, "[!] codec error\n"); + break; + } + + /* Write audio output */ + for (i=0; i<160; i++) + audio[i] = le16(audio[i]); + + rv = fwrite(audio, 2, 160, fout); + if (rv != 160) { + fprintf(stderr, "[!] short write\n"); + break; + } + + /* Keep track of number of samples */ + l += 160; + } + + /* Release decoder */ + gmr1_codec_release(codec); + + /* Fix wave header */ + if (is_wave) + { + uint32_t v; + + /* Fixup Subchunk2Size */ + v = le32(l * 2); + + rv = fseek(fout, 40, SEEK_SET); + if (rv < 0) + goto exit; + + rv = fwrite(&v, 4, 1, fout); + if (rv < 0) + goto exit; + + /* Fixup ChunkSize */ + v = le32(l * 2 + 36); + + rv = fseek(fout, 4, SEEK_SET); + if (rv < 0) + goto exit; + + rv = fwrite(&v, 4, 1, fout); + if (rv < 0) + goto exit; + } + +exit: + /* Close in/out */ + fclose(fout); + fclose(fin); + + /* All done ! */ + return 0; +} |