mirror of https://git.ffmpeg.org/ffmpeg.git
avfilter/af_sofalizer: allow user to setup custom virtual speakers positions
Signed-off-by: Paul B Mahol <onemda@gmail.com>
This commit is contained in:
parent
f3ec8ac0f4
commit
a55c953ef0
|
@ -3220,6 +3220,16 @@ Set processing type. Can be @var{time} or @var{freq}. @var{time} is
|
|||
processing audio in time domain which is slow but gives high quality output.
|
||||
@var{freq} is processing audio in frequency domain which is fast but gives
|
||||
mediocre output. Default is @var{freq}.
|
||||
|
||||
@item speakers
|
||||
Set custom positions of virtual loudspeakers. Syntax for this option is:
|
||||
<CH> <AZIM> <ELEV>[|<CH> <AZIM> <ELEV>|...].
|
||||
Each virtual loudspeaker is described with short channel name following with
|
||||
azimuth and elevation in degreees.
|
||||
Each virtual loudspeaker description is separated by '|'.
|
||||
For example to override front left and front right channel positions use:
|
||||
'speakers=FL 45 15|FR 345 15'.
|
||||
Descriptions with unrecognised channel names are ignored.
|
||||
@end table
|
||||
|
||||
@section stereotools
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <netcdf.h>
|
||||
|
||||
#include "libavcodec/avfft.h"
|
||||
#include "libavutil/avstring.h"
|
||||
#include "libavutil/channel_layout.h"
|
||||
#include "libavutil/float_dsp.h"
|
||||
#include "libavutil/intmath.h"
|
||||
#include "libavutil/opt.h"
|
||||
|
@ -52,6 +54,12 @@ typedef struct NCSofa { /* contains data of one SOFA file */
|
|||
float *data_ir; /* IRs (time-domain) */
|
||||
} NCSofa;
|
||||
|
||||
typedef struct VirtualSpeaker {
|
||||
uint8_t set;
|
||||
float azim;
|
||||
float elev;
|
||||
} VirtualSpeaker;
|
||||
|
||||
typedef struct SOFAlizerContext {
|
||||
const AVClass *class;
|
||||
|
||||
|
@ -61,6 +69,7 @@ typedef struct SOFAlizerContext {
|
|||
int sample_rate; /* sample rate from SOFA file */
|
||||
float *speaker_azim; /* azimuth of the virtual loudspeakers */
|
||||
float *speaker_elev; /* elevation of the virtual loudspeakers */
|
||||
char *speakers_pos; /* custom positions of the virtual loudspeakers */
|
||||
float gain_lfe; /* gain applied to LFE channel */
|
||||
int lfe_channel; /* LFE channel position in channel layout */
|
||||
|
||||
|
@ -89,6 +98,8 @@ typedef struct SOFAlizerContext {
|
|||
float radius; /* distance virtual loudspeakers to listener (in metres) */
|
||||
int type; /* processing type */
|
||||
|
||||
VirtualSpeaker vspkrpos[64];
|
||||
|
||||
FFTContext *fft[2], *ifft[2];
|
||||
FFTComplex *data_hrtf[2];
|
||||
|
||||
|
@ -362,6 +373,62 @@ error:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int parse_channel_name(char **arg, int *rchannel)
|
||||
{
|
||||
char buf[8];
|
||||
int len, i, channel_id = 0;
|
||||
int64_t layout, layout0;
|
||||
|
||||
/* try to parse a channel name, e.g. "FL" */
|
||||
if (sscanf(*arg, "%7[A-Z]%n", buf, &len)) {
|
||||
layout0 = layout = av_get_channel_layout(buf);
|
||||
/* channel_id <- first set bit in layout */
|
||||
for (i = 32; i > 0; i >>= 1) {
|
||||
if (layout >= (int64_t)1 << i) {
|
||||
channel_id += i;
|
||||
layout >>= i;
|
||||
}
|
||||
}
|
||||
/* reject layouts that are not a single channel */
|
||||
if (channel_id >= 64 || layout0 != (int64_t)1 << channel_id)
|
||||
return AVERROR(EINVAL);
|
||||
*rchannel = channel_id;
|
||||
*arg += len;
|
||||
return 0;
|
||||
}
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
static void parse_speaker_pos(AVFilterContext *ctx, int64_t in_channel_layout)
|
||||
{
|
||||
SOFAlizerContext *s = ctx->priv;
|
||||
char *arg, *tokenizer, *p, *args = av_strdup(s->speakers_pos);
|
||||
|
||||
if (!args)
|
||||
return;
|
||||
p = args;
|
||||
|
||||
while ((arg = av_strtok(p, "|", &tokenizer))) {
|
||||
float azim, elev;
|
||||
int out_ch_id;
|
||||
|
||||
p = NULL;
|
||||
if (parse_channel_name(&arg, &out_ch_id))
|
||||
continue;
|
||||
if (sscanf(arg, "%f %f", &azim, &elev) == 2) {
|
||||
s->vspkrpos[out_ch_id].set = 1;
|
||||
s->vspkrpos[out_ch_id].azim = azim;
|
||||
s->vspkrpos[out_ch_id].elev = elev;
|
||||
} else if (sscanf(arg, "%f", &azim) == 1) {
|
||||
s->vspkrpos[out_ch_id].set = 1;
|
||||
s->vspkrpos[out_ch_id].azim = azim;
|
||||
s->vspkrpos[out_ch_id].elev = 0;
|
||||
}
|
||||
}
|
||||
|
||||
av_free(args);
|
||||
}
|
||||
|
||||
static int get_speaker_pos(AVFilterContext *ctx,
|
||||
float *speaker_azim, float *speaker_elev)
|
||||
{
|
||||
|
@ -376,6 +443,9 @@ static int get_speaker_pos(AVFilterContext *ctx,
|
|||
|
||||
s->lfe_channel = -1;
|
||||
|
||||
if (s->speakers_pos)
|
||||
parse_speaker_pos(ctx, channels_layout);
|
||||
|
||||
/* set speaker positions according to input channel configuration: */
|
||||
for (m = 0, ch = 0; ch < n_conv && m < 64; m++) {
|
||||
uint64_t mask = channels_layout & (1 << m);
|
||||
|
@ -417,6 +487,12 @@ static int get_speaker_pos(AVFilterContext *ctx,
|
|||
default:
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
if (s->vspkrpos[m].set) {
|
||||
azim[ch] = s->vspkrpos[m].azim;
|
||||
elev[ch] = s->vspkrpos[m].elev;
|
||||
}
|
||||
|
||||
if (mask)
|
||||
ch++;
|
||||
}
|
||||
|
@ -1114,6 +1190,7 @@ static const AVOption sofalizer_options[] = {
|
|||
{ "type", "set processing", OFFSET(type), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, .flags = FLAGS, "type" },
|
||||
{ "time", "time domain", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = FLAGS, "type" },
|
||||
{ "freq", "frequency domain", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = FLAGS, "type" },
|
||||
{ "speakers", "set speaker custom positions", OFFSET(speakers_pos), AV_OPT_TYPE_STRING, {.str=0}, 0, 0, .flags = FLAGS },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue