Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* DFPWM decoder |
3 |
|
|
* Copyright (c) 2022 Jack Bruienne |
4 |
|
|
* Copyright (c) 2012, 2016 Ben "GreaseMonkey" Russell |
5 |
|
|
* |
6 |
|
|
* This file is part of FFmpeg. |
7 |
|
|
* |
8 |
|
|
* FFmpeg is free software; you can redistribute it and/or |
9 |
|
|
* modify it under the terms of the GNU Lesser General Public |
10 |
|
|
* License as published by the Free Software Foundation; either |
11 |
|
|
* version 2.1 of the License, or (at your option) any later version. |
12 |
|
|
* |
13 |
|
|
* FFmpeg is distributed in the hope that it will be useful, |
14 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 |
|
|
* Lesser General Public License for more details. |
17 |
|
|
* |
18 |
|
|
* You should have received a copy of the GNU Lesser General Public |
19 |
|
|
* License along with FFmpeg; if not, write to the Free Software |
20 |
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 |
|
|
*/ |
22 |
|
|
|
23 |
|
|
/** |
24 |
|
|
* @file |
25 |
|
|
* DFPWM1a decoder |
26 |
|
|
*/ |
27 |
|
|
|
28 |
|
|
#include "libavutil/internal.h" |
29 |
|
|
#include "avcodec.h" |
30 |
|
|
#include "codec_id.h" |
31 |
|
|
#include "codec_internal.h" |
32 |
|
|
#include "internal.h" |
33 |
|
|
|
34 |
|
|
typedef struct { |
35 |
|
|
int fq, q, s, lt; |
36 |
|
|
} DFPWMState; |
37 |
|
|
|
38 |
|
|
// DFPWM codec from https://github.com/ChenThread/dfpwm/blob/master/1a/ |
39 |
|
|
// Licensed in the public domain |
40 |
|
|
|
41 |
|
✗ |
static void au_decompress(DFPWMState *state, int fs, int len, uint8_t *outbuf, uint8_t *inbuf) |
42 |
|
|
{ |
43 |
|
|
unsigned d; |
44 |
|
✗ |
for (int i = 0; i < len; i++) { |
45 |
|
|
// get bits |
46 |
|
✗ |
d = *(inbuf++); |
47 |
|
✗ |
for (int j = 0; j < 8; j++) { |
48 |
|
|
int nq, lq, st, ns, ov; |
49 |
|
|
// set target |
50 |
|
✗ |
int t = ((d&1) ? 127 : -128); |
51 |
|
✗ |
d >>= 1; |
52 |
|
|
|
53 |
|
|
// adjust charge |
54 |
|
✗ |
nq = state->q + ((state->s * (t-state->q) + 512)>>10); |
55 |
|
✗ |
if(nq == state->q && nq != t) |
56 |
|
✗ |
nq += (t == 127 ? 1 : -1); |
57 |
|
✗ |
lq = state->q; |
58 |
|
✗ |
state->q = nq; |
59 |
|
|
|
60 |
|
|
// adjust strength |
61 |
|
✗ |
st = (t != state->lt ? 0 : 1023); |
62 |
|
✗ |
ns = state->s; |
63 |
|
✗ |
if(ns != st) |
64 |
|
✗ |
ns += (st != 0 ? 1 : -1); |
65 |
|
✗ |
if(ns < 8) ns = 8; |
66 |
|
✗ |
state->s = ns; |
67 |
|
|
|
68 |
|
|
// FILTER: perform antijerk |
69 |
|
✗ |
ov = (t != state->lt ? (nq+lq+1)>>1 : nq); |
70 |
|
|
|
71 |
|
|
// FILTER: perform LPF |
72 |
|
✗ |
state->fq += ((fs*(ov-state->fq) + 0x80)>>8); |
73 |
|
✗ |
ov = state->fq; |
74 |
|
|
|
75 |
|
|
// output sample |
76 |
|
✗ |
*(outbuf++) = ov + 128; |
77 |
|
|
|
78 |
|
✗ |
state->lt = t; |
79 |
|
|
} |
80 |
|
|
} |
81 |
|
|
} |
82 |
|
|
|
83 |
|
✗ |
static av_cold int dfpwm_dec_init(struct AVCodecContext *ctx) |
84 |
|
|
{ |
85 |
|
✗ |
DFPWMState *state = ctx->priv_data; |
86 |
|
|
|
87 |
|
✗ |
if (ctx->ch_layout.nb_channels <= 0) { |
88 |
|
✗ |
av_log(ctx, AV_LOG_ERROR, "Invalid number of channels\n"); |
89 |
|
✗ |
return AVERROR(EINVAL); |
90 |
|
|
} |
91 |
|
|
|
92 |
|
✗ |
state->fq = 0; |
93 |
|
✗ |
state->q = 0; |
94 |
|
✗ |
state->s = 0; |
95 |
|
✗ |
state->lt = -128; |
96 |
|
|
|
97 |
|
✗ |
ctx->sample_fmt = AV_SAMPLE_FMT_U8; |
98 |
|
✗ |
ctx->bits_per_raw_sample = 8; |
99 |
|
|
|
100 |
|
✗ |
return 0; |
101 |
|
|
} |
102 |
|
|
|
103 |
|
✗ |
static int dfpwm_dec_frame(struct AVCodecContext *ctx, AVFrame *frame, |
104 |
|
|
int *got_frame, struct AVPacket *packet) |
105 |
|
|
{ |
106 |
|
✗ |
DFPWMState *state = ctx->priv_data; |
107 |
|
|
int ret; |
108 |
|
|
|
109 |
|
✗ |
if (packet->size * 8LL % ctx->ch_layout.nb_channels) |
110 |
|
✗ |
return AVERROR_PATCHWELCOME; |
111 |
|
|
|
112 |
|
✗ |
frame->nb_samples = packet->size * 8LL / ctx->ch_layout.nb_channels; |
113 |
|
✗ |
if (frame->nb_samples <= 0) { |
114 |
|
✗ |
av_log(ctx, AV_LOG_ERROR, "invalid number of samples in packet\n"); |
115 |
|
✗ |
return AVERROR_INVALIDDATA; |
116 |
|
|
} |
117 |
|
|
|
118 |
|
✗ |
if ((ret = ff_get_buffer(ctx, frame, 0)) < 0) |
119 |
|
✗ |
return ret; |
120 |
|
|
|
121 |
|
✗ |
au_decompress(state, 140, packet->size, frame->data[0], packet->data); |
122 |
|
|
|
123 |
|
✗ |
*got_frame = 1; |
124 |
|
✗ |
return packet->size; |
125 |
|
|
} |
126 |
|
|
|
127 |
|
|
const FFCodec ff_dfpwm_decoder = { |
128 |
|
|
.p.name = "dfpwm", |
129 |
|
|
.p.long_name = NULL_IF_CONFIG_SMALL("DFPWM1a audio"), |
130 |
|
|
.p.type = AVMEDIA_TYPE_AUDIO, |
131 |
|
|
.p.id = AV_CODEC_ID_DFPWM, |
132 |
|
|
.priv_data_size = sizeof(DFPWMState), |
133 |
|
|
.init = dfpwm_dec_init, |
134 |
|
|
FF_CODEC_DECODE_CB(dfpwm_dec_frame), |
135 |
|
|
.p.capabilities = AV_CODEC_CAP_DR1, |
136 |
|
|
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, |
137 |
|
|
}; |
138 |
|
|
|