| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * G.728 decoder | ||
| 3 | * Copyright (c) 2025 Peter Ross | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with FFmpeg; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "avcodec.h" | ||
| 23 | #include "celp_filters.h" | ||
| 24 | #include "codec_internal.h" | ||
| 25 | #include "decode.h" | ||
| 26 | #include "get_bits.h" | ||
| 27 | #include "g728data.h" | ||
| 28 | #include "lpc_functions.h" | ||
| 29 | #include "ra288.h" | ||
| 30 | #include "libavutil/float_dsp.h" | ||
| 31 | #include "libavutil/mem.h" | ||
| 32 | #include "libavutil/mem_internal.h" | ||
| 33 | #include "libavutil/opt.h" | ||
| 34 | #include "libavutil/thread.h" | ||
| 35 | |||
| 36 | #define MAX_BACKWARD_FILTER_ORDER LPC | ||
| 37 | #define MAX_BACKWARD_FILTER_LEN NFRSZ | ||
| 38 | #define MAX_BACKWARD_FILTER_NONREC NONR | ||
| 39 | #define ATTEN 0.75f | ||
| 40 | #include "g728_template.c" | ||
| 41 | |||
| 42 | #define LPCW 10 /* Perceptual weighting filter order */ | ||
| 43 | #define GOFF 32.0f /* Log-gain offset value */ | ||
| 44 | |||
| 45 | static float g728_gq_db[8]; | ||
| 46 | static float g728_y_db[128]; | ||
| 47 | static DECLARE_ALIGNED(32, float, g728_wnr_r)[FFALIGN(NSBSZ,16)]; | ||
| 48 | static DECLARE_ALIGNED(32, float, g728_wnrg_r)[FFALIGN(NSBGSZ, 16)]; | ||
| 49 | static DECLARE_ALIGNED(32, float, g728_facv_f)[FFALIGN(LPC, 16)]; | ||
| 50 | |||
| 51 | 1 | static av_cold void g728_init_static_data(void) | |
| 52 | { | ||
| 53 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
|
9 | for(int i = 0; i < FF_ARRAY_ELEMS(amptable); i++) |
| 54 | 8 | g728_gq_db[i] = 10.0f*log10f(amptable[i] * amptable[i]); | |
| 55 | |||
| 56 |
2/2✓ Branch 0 taken 128 times.
✓ Branch 1 taken 1 times.
|
129 | for (int i = 0; i < FF_ARRAY_ELEMS(codetable); i++) { |
| 57 | float cby[IDIM]; | ||
| 58 |
2/2✓ Branch 0 taken 640 times.
✓ Branch 1 taken 128 times.
|
768 | for (int j = 0; j < IDIM; j++) |
| 59 | 640 | cby[j] = codetable[i][j] * (1.0f/(1<<11)); | |
| 60 | 128 | g728_y_db[i] = 10.0f*log10f(ff_scalarproduct_float_c(cby, cby, IDIM) / IDIM); | |
| 61 | } | ||
| 62 | |||
| 63 |
2/2✓ Branch 0 taken 105 times.
✓ Branch 1 taken 1 times.
|
106 | for (int i = 0; i < NSBSZ; i++) |
| 64 | 105 | g728_wnr_r[i] = g728_wnr[NSBSZ - 1 - i] * (1.0f/(1<<15)); | |
| 65 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1 times.
|
35 | for (int i = 0; i < NSBGSZ; i++) |
| 66 | 34 | g728_wnrg_r[i] = g728_wnrg[NSBGSZ - 1 - i] * (1.0f/(1<<15)); | |
| 67 |
2/2✓ Branch 0 taken 50 times.
✓ Branch 1 taken 1 times.
|
51 | for (int i = 0; i < LPC; i++) |
| 68 | 50 | g728_facv_f[i] = g728_facv[i] * (1.0f/(1<<14)); | |
| 69 | 1 | } | |
| 70 | |||
| 71 | typedef struct { | ||
| 72 | AVFloatDSPContext *fdsp; | ||
| 73 | int valid; | ||
| 74 | float a[LPC]; | ||
| 75 | DECLARE_ALIGNED(32, float, sb)[NSBSZ]; | ||
| 76 | DECLARE_ALIGNED(32, float, sbg)[NSBGSZ]; | ||
| 77 | DECLARE_ALIGNED(32, float, gp)[FFALIGN(LPCLG, 16)]; | ||
| 78 | DECLARE_ALIGNED(32, float, atmp)[FFALIGN(LPC, 16)]; | ||
| 79 | float rexp[LPC + 1]; | ||
| 80 | float rexpg[LPCLG + 1]; | ||
| 81 | float r[LPC + 1]; | ||
| 82 | float alpha; | ||
| 83 | } G728Context; | ||
| 84 | |||
| 85 | 2 | static av_cold int g728_decode_init(AVCodecContext *avctx) | |
| 86 | { | ||
| 87 | static AVOnce init_static_once = AV_ONCE_INIT; | ||
| 88 | 2 | G728Context *s = avctx->priv_data; | |
| 89 | |||
| 90 | 2 | s->fdsp = avpriv_float_dsp_alloc(avctx->flags & AV_CODEC_FLAG_BITEXACT); | |
| 91 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!s->fdsp) |
| 92 | ✗ | return AVERROR(ENOMEM); | |
| 93 | |||
| 94 | 2 | s->gp[0] = -1.0f; | |
| 95 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
|
10 | for (int i = 0; i < NUPDATE; i++) |
| 96 | 8 | s->sbg[NSBGSZ - 1 -i] = -GOFF; | |
| 97 | |||
| 98 | 2 | avctx->sample_fmt = AV_SAMPLE_FMT_FLT; | |
| 99 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!avctx->sample_rate) |
| 100 | ✗ | avctx->sample_rate = 8000; | |
| 101 | |||
| 102 | 2 | av_channel_layout_uninit(&avctx->ch_layout); | |
| 103 | 2 | avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; | |
| 104 | |||
| 105 | 2 | ff_thread_once(&init_static_once, g728_init_static_data); | |
| 106 | 2 | return 0; | |
| 107 | } | ||
| 108 | |||
| 109 | 2 | static av_cold int g728_decode_close(AVCodecContext *avctx) | |
| 110 | { | ||
| 111 | 2 | G728Context *s = avctx->priv_data; | |
| 112 | 2 | av_freep(&s->fdsp); | |
| 113 | 2 | return 0; | |
| 114 | } | ||
| 115 | |||
| 116 | 1048 | static int hybrid_window(AVFloatDSPContext *fdsp, | |
| 117 | int order, int n, int non_rec, float *out, | ||
| 118 | const float *hist, float *out2, const float *window) | ||
| 119 | { | ||
| 120 | 1048 | do_hybrid_window(fdsp->vector_fmul, order, n, non_rec, out, hist, out2, window); | |
| 121 | 1048 | return out[order] != 0.0f; | |
| 122 | } | ||
| 123 | |||
| 124 | 524 | static void decode_frame(G728Context *s, GetBitContext *gb, float *dst) | |
| 125 | { | ||
| 126 | 524 | float *gstate = s->sbg + NSBGSZ - 2; | |
| 127 | |||
| 128 |
2/2✓ Branch 0 taken 2096 times.
✓ Branch 1 taken 524 times.
|
2620 | for (int idx = 0; idx < NUPDATE; idx++) { |
| 129 | DECLARE_ALIGNED(32, float, et)[IDIM]; | ||
| 130 | 2096 | float *statelpc = s->sb + NSBSZ - NFRSZ + idx*IDIM; | |
| 131 | float gain, gain_db; | ||
| 132 | int is, ig; | ||
| 133 | |||
| 134 | 2096 | gain_db = 0.0f; | |
| 135 |
2/2✓ Branch 0 taken 20960 times.
✓ Branch 1 taken 2096 times.
|
23056 | for (int i = 0; i < LPCLG; i++) |
| 136 | 20960 | gain_db -= s->gp[i] * gstate[-i]; | |
| 137 | 2096 | gain_db = av_clipf(gain_db, -GOFF, 28.0f); | |
| 138 | |||
| 139 | 2096 | is = get_bits(gb, 7); // shape index | |
| 140 | 2096 | ig = get_bits(gb, 3); // gain index | |
| 141 | |||
| 142 | 2096 | gain = powf(10.0f, (gain_db + GOFF) * .05f) * amptable[ig] * (1.0f/(1<<11)); | |
| 143 |
2/2✓ Branch 0 taken 10480 times.
✓ Branch 1 taken 2096 times.
|
12576 | for (int i = 0; i < IDIM; i++) |
| 144 | 10480 | et[i] = codetable[is][i] * gain; | |
| 145 | |||
| 146 | 2096 | ff_celp_lp_synthesis_filterf(statelpc, s->a, et, IDIM, LPC); | |
| 147 | |||
| 148 |
2/2✓ Branch 0 taken 10480 times.
✓ Branch 1 taken 2096 times.
|
12576 | for (int i = 0; i < IDIM; i++) { |
| 149 | 10480 | statelpc[i] = av_clipf(statelpc[i], -4095.0f, 4095.0f); | |
| 150 | 10480 | dst[idx*IDIM + i] = statelpc[i] * (1.0f/(1<<12)); | |
| 151 | } | ||
| 152 | |||
| 153 | 2096 | gstate++; | |
| 154 |
2/2✓ Branch 0 taken 119 times.
✓ Branch 1 taken 1977 times.
|
2096 | *gstate = FFMAX(-GOFF, g728_gq_db[ig] + g728_y_db[is] + gain_db); |
| 155 | |||
| 156 |
2/2✓ Branch 0 taken 524 times.
✓ Branch 1 taken 1572 times.
|
2096 | if (idx == 0) { |
| 157 | DECLARE_ALIGNED(32, float, gptmp)[FFALIGN(LPCLG, 16)]; | ||
| 158 |
3/4✓ Branch 0 taken 518 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 518 times.
✗ Branch 4 not taken.
|
524 | if (s->valid && (s->valid = !compute_lpc_coefs(s->r + 1, LPCW, LPC, s->atmp, 0, 0, 1, &s->alpha))) { |
| 159 | 518 | s->fdsp->vector_fmul(s->atmp, s->atmp, g728_facv_f, FFALIGN(LPC, 16)); | |
| 160 | } | ||
| 161 |
3/4✓ Branch 1 taken 520 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 520 times.
✗ Branch 4 not taken.
|
1044 | if (hybrid_window(s->fdsp, LPCLG, NUPDATE, NONRLG, s->r, s->sbg, s->rexpg, g728_wnrg_r) && |
| 162 | 520 | !compute_lpc_coefs(s->r, 0, LPCLG, gptmp, 0, 0, 1, &s->alpha)) { | |
| 163 | 520 | s->fdsp->vector_fmul(s->gp, gptmp, gain_bw_tab, FFALIGN(LPCLG, 16)); | |
| 164 | } | ||
| 165 | 524 | memmove(s->sbg, s->sbg + NUPDATE, sizeof(float)*(LPCLG + NONRLG)); | |
| 166 | 524 | gstate = s->sbg + NSBGSZ - 1 - NUPDATE; | |
| 167 |
2/2✓ Branch 0 taken 524 times.
✓ Branch 1 taken 1048 times.
|
1572 | } else if (idx == 1) { |
| 168 |
2/2✓ Branch 0 taken 518 times.
✓ Branch 1 taken 6 times.
|
524 | if (s->valid) |
| 169 | 518 | memcpy(s->a, s->atmp, sizeof(float)*LPC); | |
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | 524 | s->valid = 0; | |
| 174 |
2/2✓ Branch 1 taken 520 times.
✓ Branch 2 taken 4 times.
|
524 | if (hybrid_window(s->fdsp, LPC, NFRSZ, NONR, s->r, s->sb, s->rexp, g728_wnr_r)) { |
| 175 | 520 | s->valid = !compute_lpc_coefs(s->r, 0, LPCW, s->atmp, 0, 0, 1, &s->alpha); | |
| 176 | } | ||
| 177 | |||
| 178 | 524 | memmove(s->sb, s->sb + NFRSZ, sizeof(float)*(LPC + NONR)); | |
| 179 | 524 | } | |
| 180 | |||
| 181 | 3 | static int g728_decode_frame(AVCodecContext *avctx, AVFrame *frame, | |
| 182 | int *got_frame_ptr, AVPacket *avpkt) | ||
| 183 | { | ||
| 184 | 3 | G728Context *s = avctx->priv_data; | |
| 185 | GetBitContext gb; | ||
| 186 | int ret; | ||
| 187 | 3 | int nb_frames = avpkt->size / 5; | |
| 188 | |||
| 189 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (!nb_frames) |
| 190 | ✗ | return AVERROR_INVALIDDATA; | |
| 191 | |||
| 192 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if ((ret = init_get_bits8(&gb, avpkt->data, avpkt->size)) < 0) |
| 193 | ✗ | return ret; | |
| 194 | |||
| 195 | #define SAMPLES_PER_FRAME 20 | ||
| 196 | |||
| 197 | 3 | frame->nb_samples = nb_frames * SAMPLES_PER_FRAME; | |
| 198 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |
| 199 | ✗ | return ret; | |
| 200 | |||
| 201 |
2/2✓ Branch 0 taken 524 times.
✓ Branch 1 taken 3 times.
|
527 | for (int i = 0; i < nb_frames; i++) |
| 202 | 524 | decode_frame(s, &gb, (float *)frame->data[0] + i * 20); | |
| 203 | |||
| 204 | 3 | *got_frame_ptr = 1; | |
| 205 | |||
| 206 | 3 | return nb_frames * 5; | |
| 207 | } | ||
| 208 | |||
| 209 | const FFCodec ff_g728_decoder = { | ||
| 210 | .p.name = "g728", | ||
| 211 | CODEC_LONG_NAME("G.728)"), | ||
| 212 | .p.type = AVMEDIA_TYPE_AUDIO, | ||
| 213 | .p.id = AV_CODEC_ID_G728, | ||
| 214 | .priv_data_size = sizeof(G728Context), | ||
| 215 | .init = g728_decode_init, | ||
| 216 | .close = g728_decode_close, | ||
| 217 | FF_CODEC_DECODE_CB(g728_decode_frame), | ||
| 218 | .p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | | ||
| 219 | AV_CODEC_CAP_DR1, | ||
| 220 | }; | ||
| 221 |