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 float g728_wnr_r[FFALIGN(NSBSZ,16)]; |
48 |
|
|
static float g728_wnrg_r[FFALIGN(NSBGSZ, 16)]; |
49 |
|
|
static float g728_facv_f[FFALIGN(LPC, 16)]; |
50 |
|
|
|
51 |
|
✗ |
static av_cold void g728_init_static_data(void) |
52 |
|
|
{ |
53 |
|
✗ |
for(int i = 0; i < FF_ARRAY_ELEMS(amptable); i++) |
54 |
|
✗ |
g728_gq_db[i] = 10.0f*log10f(amptable[i] * amptable[i]); |
55 |
|
|
|
56 |
|
✗ |
for (int i = 0; i < FF_ARRAY_ELEMS(codetable); i++) { |
57 |
|
|
float cby[IDIM]; |
58 |
|
✗ |
for (int j = 0; j < IDIM; j++) |
59 |
|
✗ |
cby[j] = codetable[i][j] * (1.0f/(1<<11)); |
60 |
|
✗ |
g728_y_db[i] = 10.0f*log10f(ff_scalarproduct_float_c(cby, cby, IDIM) / IDIM); |
61 |
|
|
} |
62 |
|
|
|
63 |
|
✗ |
for (int i = 0; i < NSBSZ; i++) |
64 |
|
✗ |
g728_wnr_r[i] = g728_wnr[NSBSZ - 1 - i] * (1.0f/(1<<15)); |
65 |
|
✗ |
for (int i = 0; i < NSBGSZ; i++) |
66 |
|
✗ |
g728_wnrg_r[i] = g728_wnrg[NSBGSZ - 1 - i] * (1.0f/(1<<15)); |
67 |
|
✗ |
for (int i = 0; i < LPC; i++) |
68 |
|
✗ |
g728_facv_f[i] = g728_facv[i] * (1.0f/(1<<14)); |
69 |
|
✗ |
} |
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 |
|
✗ |
static av_cold int g728_decode_init(AVCodecContext *avctx) |
86 |
|
|
{ |
87 |
|
|
static AVOnce init_static_once = AV_ONCE_INIT; |
88 |
|
✗ |
G728Context *s = avctx->priv_data; |
89 |
|
|
|
90 |
|
✗ |
s->fdsp = avpriv_float_dsp_alloc(avctx->flags & AV_CODEC_FLAG_BITEXACT); |
91 |
|
✗ |
if (!s->fdsp) |
92 |
|
✗ |
return AVERROR(ENOMEM); |
93 |
|
|
|
94 |
|
✗ |
s->gp[0] = -1.0f; |
95 |
|
✗ |
for (int i = 0; i < NUPDATE; i++) |
96 |
|
✗ |
s->sbg[NSBGSZ - 1 -i] = -GOFF; |
97 |
|
|
|
98 |
|
✗ |
avctx->sample_fmt = AV_SAMPLE_FMT_FLT; |
99 |
|
|
|
100 |
|
✗ |
av_channel_layout_uninit(&avctx->ch_layout); |
101 |
|
✗ |
avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; |
102 |
|
|
|
103 |
|
✗ |
ff_thread_once(&init_static_once, g728_init_static_data); |
104 |
|
✗ |
return 0; |
105 |
|
|
} |
106 |
|
|
|
107 |
|
✗ |
static av_cold int g728_decode_close(AVCodecContext *avctx) |
108 |
|
|
{ |
109 |
|
✗ |
G728Context *s = avctx->priv_data; |
110 |
|
✗ |
av_freep(&s->fdsp); |
111 |
|
✗ |
return 0; |
112 |
|
|
} |
113 |
|
|
|
114 |
|
✗ |
static int hybrid_window(AVFloatDSPContext *fdsp, |
115 |
|
|
int order, int n, int non_rec, float *out, |
116 |
|
|
const float *hist, float *out2, const float *window) |
117 |
|
|
{ |
118 |
|
✗ |
do_hybrid_window(fdsp->vector_fmul, order, n, non_rec, out, hist, out2, window); |
119 |
|
✗ |
return out[order] != 0.0f; |
120 |
|
|
} |
121 |
|
|
|
122 |
|
✗ |
static void decode_frame(G728Context *s, GetBitContext *gb, float *dst) |
123 |
|
|
{ |
124 |
|
✗ |
float *gstate = s->sbg + NSBGSZ - 2; |
125 |
|
|
|
126 |
|
✗ |
for (int idx = 0; idx < NUPDATE; idx++) { |
127 |
|
|
DECLARE_ALIGNED(32, float, et)[IDIM]; |
128 |
|
✗ |
float *statelpc = s->sb + NSBSZ - NFRSZ + idx*IDIM; |
129 |
|
|
float gain, gain_db; |
130 |
|
|
int is, ig; |
131 |
|
|
|
132 |
|
✗ |
gain_db = 0.0f; |
133 |
|
✗ |
for (int i = 0; i < LPCLG; i++) |
134 |
|
✗ |
gain_db -= s->gp[i] * gstate[-i]; |
135 |
|
✗ |
gain_db = av_clipf(gain_db, -GOFF, 28.0f); |
136 |
|
|
|
137 |
|
✗ |
is = get_bits(gb, 7); // shape index |
138 |
|
✗ |
ig = get_bits(gb, 3); // gain index |
139 |
|
|
|
140 |
|
✗ |
gain = powf(10.0f, (gain_db + GOFF) * .05f) * amptable[ig] * (1.0f/(1<<11)); |
141 |
|
✗ |
for (int i = 0; i < IDIM; i++) |
142 |
|
✗ |
et[i] = codetable[is][i] * gain; |
143 |
|
|
|
144 |
|
✗ |
ff_celp_lp_synthesis_filterf(statelpc, s->a, et, IDIM, LPC); |
145 |
|
|
|
146 |
|
✗ |
for (int i = 0; i < IDIM; i++) { |
147 |
|
✗ |
statelpc[i] = av_clipf(statelpc[i], -4095.0f, 4095.0f); |
148 |
|
✗ |
dst[idx*IDIM + i] = statelpc[i] * (1.0f/(1<<12)); |
149 |
|
|
} |
150 |
|
|
|
151 |
|
✗ |
gstate++; |
152 |
|
✗ |
*gstate = FFMAX(-GOFF, g728_gq_db[ig] + g728_y_db[is] + gain_db); |
153 |
|
|
|
154 |
|
✗ |
if (idx == 0) { |
155 |
|
|
DECLARE_ALIGNED(32, float, gptmp)[FFALIGN(LPCLG, 16)]; |
156 |
|
✗ |
if (s->valid && (s->valid = !compute_lpc_coefs(s->r + 1, LPCW, LPC, s->atmp, 0, 0, 1, &s->alpha))) { |
157 |
|
✗ |
s->fdsp->vector_fmul(s->atmp, s->atmp, g728_facv_f, FFALIGN(LPC, 16)); |
158 |
|
|
} |
159 |
|
✗ |
if (hybrid_window(s->fdsp, LPCLG, NUPDATE, NONRLG, s->r, s->sbg, s->rexpg, g728_wnrg_r) && |
160 |
|
✗ |
!compute_lpc_coefs(s->r, 0, LPCLG, gptmp, 0, 0, 1, &s->alpha)) { |
161 |
|
✗ |
s->fdsp->vector_fmul(s->gp, gptmp, gain_bw_tab, FFALIGN(LPCLG, 16)); |
162 |
|
|
} |
163 |
|
✗ |
memmove(s->sbg, s->sbg + NUPDATE, sizeof(float)*(LPCLG + NONRLG)); |
164 |
|
✗ |
gstate = s->sbg + NSBGSZ - 1 - NUPDATE; |
165 |
|
✗ |
} else if (idx == 1) { |
166 |
|
✗ |
if (s->valid) |
167 |
|
✗ |
memcpy(s->a, s->atmp, sizeof(float)*LPC); |
168 |
|
|
} |
169 |
|
|
} |
170 |
|
|
|
171 |
|
✗ |
s->valid = 0; |
172 |
|
✗ |
if (hybrid_window(s->fdsp, LPC, NFRSZ, NONR, s->r, s->sb, s->rexp, g728_wnr_r)) { |
173 |
|
✗ |
s->valid = !compute_lpc_coefs(s->r, 0, LPCW, s->atmp, 0, 0, 1, &s->alpha); |
174 |
|
|
} |
175 |
|
|
|
176 |
|
✗ |
memmove(s->sb, s->sb + NFRSZ, sizeof(float)*(LPC + NONR)); |
177 |
|
✗ |
} |
178 |
|
|
|
179 |
|
✗ |
static int g728_decode_frame(AVCodecContext *avctx, AVFrame *frame, |
180 |
|
|
int *got_frame_ptr, AVPacket *avpkt) |
181 |
|
|
{ |
182 |
|
✗ |
G728Context *s = avctx->priv_data; |
183 |
|
|
GetBitContext gb; |
184 |
|
|
int ret; |
185 |
|
✗ |
int nb_frames = avpkt->size / 5; |
186 |
|
|
|
187 |
|
✗ |
if (!nb_frames) |
188 |
|
✗ |
return AVERROR_INVALIDDATA; |
189 |
|
|
|
190 |
|
✗ |
if ((ret = init_get_bits8(&gb, avpkt->data, avpkt->size)) < 0) |
191 |
|
✗ |
return ret; |
192 |
|
|
|
193 |
|
|
#define SAMPLES_PER_FRAME 20 |
194 |
|
|
|
195 |
|
✗ |
frame->nb_samples = nb_frames * SAMPLES_PER_FRAME; |
196 |
|
✗ |
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |
197 |
|
✗ |
return ret; |
198 |
|
|
|
199 |
|
✗ |
for (int i = 0; i < nb_frames; i++) |
200 |
|
✗ |
decode_frame(s, &gb, (float *)frame->data[0] + i * 20); |
201 |
|
|
|
202 |
|
✗ |
*got_frame_ptr = 1; |
203 |
|
|
|
204 |
|
✗ |
return nb_frames * 5; |
205 |
|
|
} |
206 |
|
|
|
207 |
|
|
const FFCodec ff_g728_decoder = { |
208 |
|
|
.p.name = "g728", |
209 |
|
|
CODEC_LONG_NAME("G.728)"), |
210 |
|
|
.p.type = AVMEDIA_TYPE_AUDIO, |
211 |
|
|
.p.id = AV_CODEC_ID_G728, |
212 |
|
|
.priv_data_size = sizeof(G728Context), |
213 |
|
|
.init = g728_decode_init, |
214 |
|
|
.close = g728_decode_close, |
215 |
|
|
FF_CODEC_DECODE_CB(g728_decode_frame), |
216 |
|
|
.p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | |
217 |
|
|
AV_CODEC_CAP_DR1, |
218 |
|
|
.p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLT, |
219 |
|
|
AV_SAMPLE_FMT_NONE }, |
220 |
|
|
}; |
221 |
|
|
|