Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
F
ffmpeg.wasm-core
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Linshizhi
ffmpeg.wasm-core
Commits
ae1ce0db
Commit
ae1ce0db
authored
Aug 22, 2017
by
Muhammad Faiz
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
avfilter/af_firequalizer: add min_phase option
Signed-off-by:
Muhammad Faiz
<
mfcc64@gmail.com
>
parent
e51073fe
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
147 additions
and
3 deletions
+147
-3
filters.texi
doc/filters.texi
+3
-0
af_firequalizer.c
libavfilter/af_firequalizer.c
+144
-3
No files found.
doc/filters.texi
View file @
ae1ce0db
...
...
@@ -2667,6 +2667,9 @@ Default is linlog.
@item fft2
Enable 2-channel convolution using complex FFT. This improves speed significantly.
Default is disabled.
@item min_phase
Enable minimum phase impulse response. Default is disabled.
@end table
@subsection Examples
...
...
libavfilter/af_firequalizer.c
View file @
ae1ce0db
...
...
@@ -70,13 +70,17 @@ typedef struct FIREqualizerContext {
RDFTContext
*
rdft
;
RDFTContext
*
irdft
;
FFTContext
*
fft_ctx
;
RDFTContext
*
cepstrum_rdft
;
RDFTContext
*
cepstrum_irdft
;
int
analysis_rdft_len
;
int
rdft_len
;
int
cepstrum_len
;
float
*
analysis_buf
;
float
*
dump_buf
;
float
*
kernel_tmp_buf
;
float
*
kernel_buf
;
float
*
cepstrum_buf
;
float
*
conv_buf
;
OverlapIndex
*
conv_idx
;
int
fir_len
;
...
...
@@ -99,6 +103,7 @@ typedef struct FIREqualizerContext {
char
*
dumpfile
;
int
dumpscale
;
int
fft2
;
int
min_phase
;
int
nb_gain_entry
;
int
gain_entry_err
;
...
...
@@ -135,6 +140,7 @@ static const AVOption firequalizer_options[] = {
{
"dumpfile"
,
"set dump file"
,
OFFSET
(
dumpfile
),
AV_OPT_TYPE_STRING
,
{
.
str
=
NULL
},
0
,
0
,
FLAGS
},
{
"dumpscale"
,
"set dump scale"
,
OFFSET
(
dumpscale
),
AV_OPT_TYPE_INT
,
{
.
i64
=
SCALE_LINLOG
},
0
,
NB_SCALE
-
1
,
FLAGS
,
"scale"
},
{
"fft2"
,
"set 2-channels fft"
,
OFFSET
(
fft2
),
AV_OPT_TYPE_BOOL
,
{
.
i64
=
0
},
0
,
1
,
FLAGS
},
{
"min_phase"
,
"set minimum phase mode"
,
OFFSET
(
min_phase
),
AV_OPT_TYPE_BOOL
,
{
.
i64
=
0
},
0
,
1
,
FLAGS
},
{
NULL
}
};
...
...
@@ -147,13 +153,18 @@ static void common_uninit(FIREqualizerContext *s)
av_rdft_end
(
s
->
rdft
);
av_rdft_end
(
s
->
irdft
);
av_fft_end
(
s
->
fft_ctx
);
av_rdft_end
(
s
->
cepstrum_rdft
);
av_rdft_end
(
s
->
cepstrum_irdft
);
s
->
analysis_rdft
=
s
->
analysis_irdft
=
s
->
rdft
=
s
->
irdft
=
NULL
;
s
->
fft_ctx
=
NULL
;
s
->
cepstrum_rdft
=
NULL
;
s
->
cepstrum_irdft
=
NULL
;
av_freep
(
&
s
->
analysis_buf
);
av_freep
(
&
s
->
dump_buf
);
av_freep
(
&
s
->
kernel_tmp_buf
);
av_freep
(
&
s
->
kernel_buf
);
av_freep
(
&
s
->
cepstrum_buf
);
av_freep
(
&
s
->
conv_buf
);
av_freep
(
&
s
->
conv_idx
);
}
...
...
@@ -235,6 +246,46 @@ static void fast_convolute(FIREqualizerContext *av_restrict s, const float *av_r
}
}
static
void
fast_convolute_nonlinear
(
FIREqualizerContext
*
av_restrict
s
,
const
float
*
av_restrict
kernel_buf
,
float
*
av_restrict
conv_buf
,
OverlapIndex
*
av_restrict
idx
,
float
*
av_restrict
data
,
int
nsamples
)
{
if
(
nsamples
<=
s
->
nsamples_max
)
{
float
*
buf
=
conv_buf
+
idx
->
buf_idx
*
s
->
rdft_len
;
float
*
obuf
=
conv_buf
+
!
idx
->
buf_idx
*
s
->
rdft_len
+
idx
->
overlap_idx
;
int
k
;
memcpy
(
buf
,
data
,
nsamples
*
sizeof
(
*
data
));
memset
(
buf
+
nsamples
,
0
,
(
s
->
rdft_len
-
nsamples
)
*
sizeof
(
*
data
));
av_rdft_calc
(
s
->
rdft
,
buf
);
buf
[
0
]
*=
kernel_buf
[
0
];
buf
[
1
]
*=
kernel_buf
[
1
];
for
(
k
=
2
;
k
<
s
->
rdft_len
;
k
+=
2
)
{
float
re
,
im
;
re
=
buf
[
k
]
*
kernel_buf
[
k
]
-
buf
[
k
+
1
]
*
kernel_buf
[
k
+
1
];
im
=
buf
[
k
]
*
kernel_buf
[
k
+
1
]
+
buf
[
k
+
1
]
*
kernel_buf
[
k
];
buf
[
k
]
=
re
;
buf
[
k
+
1
]
=
im
;
}
av_rdft_calc
(
s
->
irdft
,
buf
);
for
(
k
=
0
;
k
<
s
->
rdft_len
-
idx
->
overlap_idx
;
k
++
)
buf
[
k
]
+=
obuf
[
k
];
memcpy
(
data
,
buf
,
nsamples
*
sizeof
(
*
data
));
idx
->
buf_idx
=
!
idx
->
buf_idx
;
idx
->
overlap_idx
=
nsamples
;
}
else
{
while
(
nsamples
>
s
->
nsamples_max
*
2
)
{
fast_convolute_nonlinear
(
s
,
kernel_buf
,
conv_buf
,
idx
,
data
,
s
->
nsamples_max
);
data
+=
s
->
nsamples_max
;
nsamples
-=
s
->
nsamples_max
;
}
fast_convolute_nonlinear
(
s
,
kernel_buf
,
conv_buf
,
idx
,
data
,
nsamples
/
2
);
fast_convolute_nonlinear
(
s
,
kernel_buf
,
conv_buf
,
idx
,
data
+
nsamples
/
2
,
nsamples
-
nsamples
/
2
);
}
}
static
void
fast_convolute2
(
FIREqualizerContext
*
av_restrict
s
,
const
float
*
av_restrict
kernel_buf
,
FFTComplex
*
av_restrict
conv_buf
,
OverlapIndex
*
av_restrict
idx
,
float
*
av_restrict
data0
,
float
*
av_restrict
data1
,
int
nsamples
)
{
...
...
@@ -310,22 +361,32 @@ static void dump_fir(AVFilterContext *ctx, FILE *fp, int ch)
double
delay
=
s
->
zero_phase
?
0
.
0
:
(
double
)
center
/
rate
;
double
vx
,
ya
,
yb
;
if
(
!
s
->
min_phase
)
{
s
->
analysis_buf
[
0
]
*=
s
->
rdft_len
/
2
;
for
(
x
=
1
;
x
<=
center
;
x
++
)
{
s
->
analysis_buf
[
x
]
*=
s
->
rdft_len
/
2
;
s
->
analysis_buf
[
s
->
analysis_rdft_len
-
x
]
*=
s
->
rdft_len
/
2
;
}
}
else
{
for
(
x
=
0
;
x
<
s
->
fir_len
;
x
++
)
s
->
analysis_buf
[
x
]
*=
s
->
rdft_len
/
2
;
}
if
(
ch
)
fprintf
(
fp
,
"
\n\n
"
);
fprintf
(
fp
,
"# time[%d] (time amplitude)
\n
"
,
ch
);
if
(
!
s
->
min_phase
)
{
for
(
x
=
center
;
x
>
0
;
x
--
)
fprintf
(
fp
,
"%15.10f %15.10f
\n
"
,
delay
-
(
double
)
x
/
rate
,
(
double
)
s
->
analysis_buf
[
s
->
analysis_rdft_len
-
x
]);
for
(
x
=
0
;
x
<=
center
;
x
++
)
fprintf
(
fp
,
"%15.10f %15.10f
\n
"
,
delay
+
(
double
)
x
/
rate
,
(
double
)
s
->
analysis_buf
[
x
]);
}
else
{
for
(
x
=
0
;
x
<
s
->
fir_len
;
x
++
)
fprintf
(
fp
,
"%15.10f %15.10f
\n
"
,
(
double
)
x
/
rate
,
(
double
)
s
->
analysis_buf
[
x
]);
}
av_rdft_calc
(
s
->
analysis_rdft
,
s
->
analysis_buf
);
...
...
@@ -337,7 +398,9 @@ static void dump_fir(AVFilterContext *ctx, FILE *fp, int ch)
if
(
xlog
)
vx
=
log2
(
0
.
05
*
vx
);
ya
=
s
->
dump_buf
[
i
];
yb
=
s
->
analysis_buf
[
i
];
yb
=
s
->
min_phase
&&
(
i
>
1
)
?
hypotf
(
s
->
analysis_buf
[
i
],
s
->
analysis_buf
[
i
+
1
])
:
s
->
analysis_buf
[
i
];
if
(
s
->
min_phase
)
yb
=
fabs
(
yb
);
if
(
ylog
)
{
ya
=
20
.
0
*
log10
(
fabs
(
ya
));
yb
=
20
.
0
*
log10
(
fabs
(
yb
));
...
...
@@ -487,6 +550,53 @@ enum VarOffset {
VAR_NB
};
static
void
generate_min_phase_kernel
(
FIREqualizerContext
*
s
,
float
*
rdft_buf
)
{
int
k
,
cepstrum_len
=
s
->
cepstrum_len
,
rdft_len
=
s
->
rdft_len
;
double
norm
=
2
.
0
/
cepstrum_len
;
memset
(
s
->
cepstrum_buf
,
0
,
cepstrum_len
*
sizeof
(
*
s
->
cepstrum_buf
));
memcpy
(
s
->
cepstrum_buf
,
rdft_buf
,
rdft_len
/
2
*
sizeof
(
*
rdft_buf
));
memcpy
(
s
->
cepstrum_buf
+
cepstrum_len
-
rdft_len
/
2
,
rdft_buf
+
rdft_len
/
2
,
rdft_len
/
2
*
sizeof
(
*
rdft_buf
));
av_rdft_calc
(
s
->
cepstrum_rdft
,
s
->
cepstrum_buf
);
s
->
cepstrum_buf
[
0
]
=
log
(
FFMAX
(
s
->
cepstrum_buf
[
0
],
1e-10
));
s
->
cepstrum_buf
[
1
]
=
log
(
FFMAX
(
s
->
cepstrum_buf
[
1
],
1e-10
));
for
(
k
=
2
;
k
<
cepstrum_len
;
k
+=
2
)
{
s
->
cepstrum_buf
[
k
]
=
log
(
FFMAX
(
s
->
cepstrum_buf
[
k
],
1e-10
));
s
->
cepstrum_buf
[
k
+
1
]
=
0
;
}
av_rdft_calc
(
s
->
cepstrum_irdft
,
s
->
cepstrum_buf
);
memset
(
s
->
cepstrum_buf
+
cepstrum_len
/
2
+
1
,
0
,
(
cepstrum_len
/
2
-
1
)
*
sizeof
(
*
s
->
cepstrum_buf
));
for
(
k
=
1
;
k
<
cepstrum_len
/
2
;
k
++
)
s
->
cepstrum_buf
[
k
]
*=
2
;
av_rdft_calc
(
s
->
cepstrum_rdft
,
s
->
cepstrum_buf
);
s
->
cepstrum_buf
[
0
]
=
exp
(
s
->
cepstrum_buf
[
0
]
*
norm
)
*
norm
;
s
->
cepstrum_buf
[
1
]
=
exp
(
s
->
cepstrum_buf
[
1
]
*
norm
)
*
norm
;
for
(
k
=
2
;
k
<
cepstrum_len
;
k
+=
2
)
{
double
mag
=
exp
(
s
->
cepstrum_buf
[
k
]
*
norm
)
*
norm
;
double
ph
=
s
->
cepstrum_buf
[
k
+
1
]
*
norm
;
s
->
cepstrum_buf
[
k
]
=
mag
*
cos
(
ph
);
s
->
cepstrum_buf
[
k
+
1
]
=
mag
*
sin
(
ph
);
}
av_rdft_calc
(
s
->
cepstrum_irdft
,
s
->
cepstrum_buf
);
memset
(
rdft_buf
,
0
,
s
->
rdft_len
*
sizeof
(
*
rdft_buf
));
memcpy
(
rdft_buf
,
s
->
cepstrum_buf
,
s
->
fir_len
*
sizeof
(
*
rdft_buf
));
if
(
s
->
dumpfile
)
{
memset
(
s
->
analysis_buf
,
0
,
s
->
analysis_rdft_len
*
sizeof
(
*
s
->
analysis_buf
));
memcpy
(
s
->
analysis_buf
,
s
->
cepstrum_buf
,
s
->
fir_len
*
sizeof
(
*
s
->
analysis_buf
));
}
}
static
int
generate_kernel
(
AVFilterContext
*
ctx
,
const
char
*
gain
,
const
char
*
gain_entry
)
{
FIREqualizerContext
*
s
=
ctx
->
priv
;
...
...
@@ -549,7 +659,7 @@ static int generate_kernel(AVFilterContext *ctx, const char *gain, const char *g
if
(
xlog
)
vars
[
VAR_F
]
=
log2
(
0
.
05
*
vars
[
VAR_F
]);
result
=
av_expr_eval
(
gain_expr
,
vars
,
ctx
);
s
->
analysis_buf
[
2
*
k
]
=
ylog
?
pow
(
10
.
0
,
0
.
05
*
result
)
:
result
;
s
->
analysis_buf
[
2
*
k
]
=
ylog
?
pow
(
10
.
0
,
0
.
05
*
result
)
:
s
->
min_phase
?
fabs
(
result
)
:
result
;
s
->
analysis_buf
[
2
*
k
+
1
]
=
0
.
0
;
}
...
...
@@ -604,6 +714,8 @@ static int generate_kernel(AVFilterContext *ctx, const char *gain, const char *g
memset
(
s
->
analysis_buf
+
center
+
1
,
0
,
(
s
->
analysis_rdft_len
-
s
->
fir_len
)
*
sizeof
(
*
s
->
analysis_buf
));
memcpy
(
rdft_buf
,
s
->
analysis_buf
,
s
->
rdft_len
/
2
*
sizeof
(
*
s
->
analysis_buf
));
memcpy
(
rdft_buf
+
s
->
rdft_len
/
2
,
s
->
analysis_buf
+
s
->
analysis_rdft_len
-
s
->
rdft_len
/
2
,
s
->
rdft_len
/
2
*
sizeof
(
*
s
->
analysis_buf
));
if
(
s
->
min_phase
)
generate_min_phase_kernel
(
s
,
rdft_buf
);
av_rdft_calc
(
s
->
rdft
,
rdft_buf
);
for
(
k
=
0
;
k
<
s
->
rdft_len
;
k
++
)
{
...
...
@@ -616,10 +728,12 @@ static int generate_kernel(AVFilterContext *ctx, const char *gain, const char *g
}
}
if
(
!
s
->
min_phase
)
{
rdft_buf
[
s
->
rdft_len
-
1
]
=
rdft_buf
[
1
];
for
(
k
=
0
;
k
<
s
->
rdft_len
/
2
;
k
++
)
rdft_buf
[
k
]
=
rdft_buf
[
2
*
k
];
rdft_buf
[
s
->
rdft_len
/
2
]
=
rdft_buf
[
s
->
rdft_len
-
1
];
}
if
(
dump_fp
)
dump_fir
(
ctx
,
dump_fp
,
ch
);
...
...
@@ -670,6 +784,25 @@ static int config_input(AVFilterLink *inlink)
if
(
s
->
fft2
&&
!
s
->
multi
&&
inlink
->
channels
>
1
&&
!
(
s
->
fft_ctx
=
av_fft_init
(
rdft_bits
,
0
)))
return
AVERROR
(
ENOMEM
);
if
(
s
->
min_phase
)
{
int
cepstrum_bits
=
rdft_bits
+
2
;
if
(
cepstrum_bits
>
RDFT_BITS_MAX
)
{
av_log
(
ctx
,
AV_LOG_ERROR
,
"too large delay, please decrease it.
\n
"
);
return
AVERROR
(
EINVAL
);
}
cepstrum_bits
=
FFMIN
(
RDFT_BITS_MAX
,
cepstrum_bits
+
1
);
s
->
cepstrum_rdft
=
av_rdft_init
(
cepstrum_bits
,
DFT_R2C
);
s
->
cepstrum_irdft
=
av_rdft_init
(
cepstrum_bits
,
IDFT_C2R
);
if
(
!
s
->
cepstrum_rdft
||
!
s
->
cepstrum_irdft
)
return
AVERROR
(
ENOMEM
);
s
->
cepstrum_len
=
1
<<
cepstrum_bits
;
s
->
cepstrum_buf
=
av_malloc_array
(
s
->
cepstrum_len
,
sizeof
(
*
s
->
cepstrum_buf
));
if
(
!
s
->
cepstrum_buf
)
return
AVERROR
(
ENOMEM
);
}
for
(
;
rdft_bits
<=
RDFT_BITS_MAX
;
rdft_bits
++
)
{
s
->
analysis_rdft_len
=
1
<<
rdft_bits
;
if
(
inlink
->
sample_rate
<=
s
->
accuracy
*
s
->
analysis_rdft_len
)
...
...
@@ -712,6 +845,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
FIREqualizerContext
*
s
=
ctx
->
priv
;
int
ch
;
if
(
!
s
->
min_phase
)
{
for
(
ch
=
0
;
ch
+
1
<
inlink
->
channels
&&
s
->
fft_ctx
;
ch
+=
2
)
{
fast_convolute2
(
s
,
s
->
kernel_buf
,
(
FFTComplex
*
)(
s
->
conv_buf
+
2
*
ch
*
s
->
rdft_len
),
s
->
conv_idx
+
ch
,
(
float
*
)
frame
->
extended_data
[
ch
],
...
...
@@ -723,11 +857,18 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
s
->
conv_buf
+
2
*
ch
*
s
->
rdft_len
,
s
->
conv_idx
+
ch
,
(
float
*
)
frame
->
extended_data
[
ch
],
frame
->
nb_samples
);
}
}
else
{
for
(
ch
=
0
;
ch
<
inlink
->
channels
;
ch
++
)
{
fast_convolute_nonlinear
(
s
,
s
->
kernel_buf
+
(
s
->
multi
?
ch
*
s
->
rdft_len
:
0
),
s
->
conv_buf
+
2
*
ch
*
s
->
rdft_len
,
s
->
conv_idx
+
ch
,
(
float
*
)
frame
->
extended_data
[
ch
],
frame
->
nb_samples
);
}
}
s
->
next_pts
=
AV_NOPTS_VALUE
;
if
(
frame
->
pts
!=
AV_NOPTS_VALUE
)
{
s
->
next_pts
=
frame
->
pts
+
av_rescale_q
(
frame
->
nb_samples
,
av_make_q
(
1
,
inlink
->
sample_rate
),
inlink
->
time_base
);
if
(
s
->
zero_phase
)
if
(
s
->
zero_phase
&&
!
s
->
min_phase
)
frame
->
pts
-=
av_rescale_q
(
s
->
fir_len
/
2
,
av_make_q
(
1
,
inlink
->
sample_rate
),
inlink
->
time_base
);
}
s
->
frame_nsamples_max
=
FFMAX
(
s
->
frame_nsamples_max
,
frame
->
nb_samples
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment