mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-03 09:01:54 -05:00
filter-chain: move the filter-graph to plugins
This commit is contained in:
parent
2e157f7248
commit
567b484386
27 changed files with 122 additions and 132 deletions
|
|
@ -71,122 +71,15 @@ pipewire_module_loopback = shared_library('pipewire-module-loopback',
|
|||
dependencies : [spa_dep, mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
||||
plugin_dependencies = []
|
||||
if get_option('spa-plugins').allowed()
|
||||
plugin_dependencies += audioconvert_dep
|
||||
endif
|
||||
|
||||
simd_cargs = []
|
||||
simd_dependencies = []
|
||||
|
||||
if have_sse
|
||||
filter_graph_sse = static_library('filter_graph_sse',
|
||||
['module-filter-chain/pffft.c',
|
||||
'module-filter-chain/audio-dsp-sse.c' ],
|
||||
include_directories : [configinc],
|
||||
c_args : [sse_args, '-O3', '-DHAVE_SSE'],
|
||||
dependencies : [ spa_dep ],
|
||||
install : false
|
||||
)
|
||||
simd_cargs += ['-DHAVE_SSE']
|
||||
simd_dependencies += filter_graph_sse
|
||||
endif
|
||||
if have_avx
|
||||
filter_graph_avx = static_library('filter_graph_avx',
|
||||
['module-filter-chain/audio-dsp-avx.c' ],
|
||||
include_directories : [configinc],
|
||||
c_args : [avx_args, fma_args,'-O3', '-DHAVE_AVX'],
|
||||
dependencies : [ spa_dep ],
|
||||
install : false
|
||||
)
|
||||
simd_cargs += ['-DHAVE_AVX']
|
||||
simd_dependencies += filter_graph_avx
|
||||
endif
|
||||
if have_neon
|
||||
filter_graph_neon = static_library('filter_graph_neon',
|
||||
['module-filter-chain/pffft.c' ],
|
||||
c_args : [neon_args, '-O3', '-DHAVE_NEON'],
|
||||
dependencies : [ spa_dep ],
|
||||
install : false
|
||||
)
|
||||
simd_cargs += ['-DHAVE_NEON']
|
||||
simd_dependencies += filter_graph_neon
|
||||
endif
|
||||
|
||||
filter_graph_c = static_library('filter_graph_c',
|
||||
['module-filter-chain/pffft.c',
|
||||
'module-filter-chain/audio-dsp.c',
|
||||
'module-filter-chain/audio-dsp-c.c' ],
|
||||
include_directories : [configinc],
|
||||
c_args : [simd_cargs, '-O3', '-DPFFFT_SIMD_DISABLE'],
|
||||
dependencies : [ spa_dep, fftw_dep],
|
||||
install : false
|
||||
)
|
||||
simd_dependencies += filter_graph_c
|
||||
|
||||
filter_graph = static_library('filter_graph',
|
||||
['module-filter-chain/biquad.c',
|
||||
'module-filter-chain/filter-graph.c' ],
|
||||
include_directories : [configinc],
|
||||
dependencies : [ spa_dep, sndfile_dep, plugin_dependencies ],
|
||||
install : false
|
||||
)
|
||||
simd_dependencies += filter_graph
|
||||
|
||||
filter_graph_dependencies = [
|
||||
spa_dep, mathlib, dl_lib, sndfile_dep, plugin_dependencies
|
||||
]
|
||||
|
||||
pipewire_module_filter_chain = shared_library('pipewire-module-filter-chain',
|
||||
[ 'module-filter-chain.c' ],
|
||||
include_directories : [configinc],
|
||||
install : true,
|
||||
install_dir : modules_install_dir,
|
||||
install_rpath: modules_install_dir,
|
||||
link_with : simd_dependencies,
|
||||
dependencies : [spa_dep, mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
||||
pipewire_module_filter_graph_builtin = shared_library('spa-filter-graph-plugin-builtin',
|
||||
[ 'module-filter-chain/builtin_plugin.c',
|
||||
'module-filter-chain/convolver.c' ],
|
||||
include_directories : [configinc],
|
||||
install : true,
|
||||
install_dir : spa_plugindir / 'filter-graph',
|
||||
dependencies : [ filter_graph_dependencies ]
|
||||
)
|
||||
|
||||
pipewire_module_filter_graph_ladspa = shared_library('spa-filter-graph-plugin-ladspa',
|
||||
[ 'module-filter-chain/ladspa_plugin.c' ],
|
||||
include_directories : [configinc],
|
||||
install : true,
|
||||
install : true,
|
||||
install_dir : spa_plugindir / 'filter-graph',
|
||||
dependencies : [ filter_graph_dependencies ]
|
||||
)
|
||||
|
||||
if libmysofa_dep.found()
|
||||
pipewire_module_filter_graph_sofa = shared_library('spa-filter-graph-plugin-sofa',
|
||||
[ 'module-filter-chain/sofa_plugin.c',
|
||||
'module-filter-chain/convolver.c' ],
|
||||
include_directories : [configinc],
|
||||
install : true,
|
||||
install_dir : spa_plugindir / 'filter-graph',
|
||||
dependencies : [ filter_graph_dependencies, libmysofa_dep ]
|
||||
)
|
||||
endif
|
||||
|
||||
if lilv_lib.found()
|
||||
pipewire_module_filter_graph_lv2 = shared_library('spa-filter-graph-plugin-lv2',
|
||||
[ 'module-filter-chain/lv2_plugin.c' ],
|
||||
include_directories : [configinc],
|
||||
install : true,
|
||||
install_dir : spa_plugindir / 'filter-graph',
|
||||
dependencies : [ filter_graph_dependencies, lilv_lib ]
|
||||
)
|
||||
endif
|
||||
|
||||
|
||||
pipewire_module_combine_stream = shared_library('pipewire-module-combine-stream',
|
||||
[ 'module-combine-stream.c' ],
|
||||
include_directories : [configinc],
|
||||
|
|
|
|||
|
|
@ -17,11 +17,10 @@
|
|||
#include <spa/param/tag-utils.h>
|
||||
#include <spa/param/audio/raw-json.h>
|
||||
#include <spa/pod/dynamic.h>
|
||||
#include <spa/filter-graph/filter-graph.h>
|
||||
|
||||
#include <pipewire/impl.h>
|
||||
|
||||
#include "module-filter-chain/filter-graph.h"
|
||||
|
||||
#define NAME "filter-chain"
|
||||
|
||||
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
|
||||
|
|
@ -1168,8 +1167,7 @@ static void impl_destroy(struct impl *impl)
|
|||
pw_core_disconnect(impl->core);
|
||||
|
||||
if (impl->handle)
|
||||
spa_handle_clear(impl->handle);
|
||||
free(impl->handle);
|
||||
pw_unload_spa_handle(impl->handle);
|
||||
|
||||
pw_properties_free(impl->capture_props);
|
||||
pw_properties_free(impl->playback_props);
|
||||
|
|
@ -1222,8 +1220,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
|
|||
uint32_t pid = getpid();
|
||||
const char *str;
|
||||
int res;
|
||||
const struct spa_support *support;
|
||||
uint32_t n_support;
|
||||
void *iface = NULL;
|
||||
|
||||
PW_LOG_TOPIC_INIT(mod_topic);
|
||||
|
|
@ -1330,20 +1326,13 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
|
|||
pw_properties_setf(props, "filter-graph.n_inputs", "%d", impl->capture_info.channels);
|
||||
pw_properties_setf(props, "filter-graph.n_outputs", "%d", impl->playback_info.channels);
|
||||
|
||||
impl->handle = calloc(1, spa_handle_factory_get_size(&spa_filter_graph_factory, &props->dict));
|
||||
pw_properties_set(props, SPA_KEY_LIBRARY_NAME, "filter-graph/libspa-filter-graph");
|
||||
impl->handle = pw_context_load_spa_handle(impl->context, "filter.graph", &props->dict);
|
||||
if (impl->handle == NULL) {
|
||||
res = -errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
support = pw_context_get_support(impl->context, &n_support);
|
||||
if ((res = spa_handle_factory_init(&spa_filter_graph_factory,
|
||||
impl->handle, &props->dict,
|
||||
support, n_support)) < 0) {
|
||||
pw_log_debug("can't make factory instance: %d (%s)",
|
||||
res, spa_strerror(res));
|
||||
goto error;
|
||||
}
|
||||
res = spa_handle_get_interface(impl->handle, SPA_TYPE_INTERFACE_FilterGraph, &iface);
|
||||
if (res < 0 || iface == NULL)
|
||||
goto error;
|
||||
|
|
|
|||
|
|
@ -1,219 +0,0 @@
|
|||
/* Spa */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <spa/utils/defs.h>
|
||||
|
||||
#include "config.h"
|
||||
#ifndef HAVE_FFTW
|
||||
#include "pffft.h"
|
||||
#endif
|
||||
#include "audio-dsp-impl.h"
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
void dsp_mix_gain_avx(void *obj,
|
||||
void * SPA_RESTRICT dst,
|
||||
const void * SPA_RESTRICT src[],
|
||||
float gain[], uint32_t n_src, uint32_t n_samples)
|
||||
{
|
||||
if (n_src == 0) {
|
||||
memset(dst, 0, n_samples * sizeof(float));
|
||||
} else if (n_src == 1 && gain[0] == 1.0f) {
|
||||
if (dst != src[0])
|
||||
spa_memcpy(dst, src[0], n_samples * sizeof(float));
|
||||
} else {
|
||||
uint32_t n, i, unrolled;
|
||||
__m256 in[4], g;
|
||||
const float **s = (const float **)src;
|
||||
float *d = dst;
|
||||
|
||||
if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 32))) {
|
||||
unrolled = n_samples & ~31;
|
||||
for (i = 0; i < n_src; i++) {
|
||||
if (SPA_UNLIKELY(!SPA_IS_ALIGNED(src[i], 32))) {
|
||||
unrolled = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
unrolled = 0;
|
||||
|
||||
for (n = 0; n < unrolled; n += 32) {
|
||||
g = _mm256_set1_ps(gain[0]);
|
||||
in[0] = _mm256_mul_ps(g, _mm256_load_ps(&s[0][n+ 0]));
|
||||
in[1] = _mm256_mul_ps(g, _mm256_load_ps(&s[0][n+ 8]));
|
||||
in[2] = _mm256_mul_ps(g, _mm256_load_ps(&s[0][n+16]));
|
||||
in[3] = _mm256_mul_ps(g, _mm256_load_ps(&s[0][n+24]));
|
||||
|
||||
for (i = 1; i < n_src; i++) {
|
||||
g = _mm256_set1_ps(gain[i]);
|
||||
in[0] = _mm256_add_ps(in[0], _mm256_mul_ps(g, _mm256_load_ps(&s[i][n+ 0])));
|
||||
in[1] = _mm256_add_ps(in[1], _mm256_mul_ps(g, _mm256_load_ps(&s[i][n+ 8])));
|
||||
in[2] = _mm256_add_ps(in[2], _mm256_mul_ps(g, _mm256_load_ps(&s[i][n+16])));
|
||||
in[3] = _mm256_add_ps(in[3], _mm256_mul_ps(g, _mm256_load_ps(&s[i][n+24])));
|
||||
}
|
||||
_mm256_store_ps(&d[n+ 0], in[0]);
|
||||
_mm256_store_ps(&d[n+ 8], in[1]);
|
||||
_mm256_store_ps(&d[n+16], in[2]);
|
||||
_mm256_store_ps(&d[n+24], in[3]);
|
||||
}
|
||||
for (; n < n_samples; n++) {
|
||||
__m128 in[1], g;
|
||||
g = _mm_set_ss(gain[0]);
|
||||
in[0] = _mm_mul_ss(g, _mm_load_ss(&s[0][n]));
|
||||
for (i = 1; i < n_src; i++) {
|
||||
g = _mm_set_ss(gain[i]);
|
||||
in[0] = _mm_add_ss(in[0], _mm_mul_ss(g, _mm_load_ss(&s[i][n])));
|
||||
}
|
||||
_mm_store_ss(&d[n], in[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dsp_sum_avx(void *obj, float *r, const float *a, const float *b, uint32_t n_samples)
|
||||
{
|
||||
uint32_t n, unrolled;
|
||||
__m256 in[4];
|
||||
|
||||
unrolled = n_samples & ~31;
|
||||
|
||||
if (SPA_LIKELY(SPA_IS_ALIGNED(r, 32)) &&
|
||||
SPA_LIKELY(SPA_IS_ALIGNED(a, 32)) &&
|
||||
SPA_LIKELY(SPA_IS_ALIGNED(b, 32))) {
|
||||
for (n = 0; n < unrolled; n += 32) {
|
||||
in[0] = _mm256_load_ps(&a[n+ 0]);
|
||||
in[1] = _mm256_load_ps(&a[n+ 8]);
|
||||
in[2] = _mm256_load_ps(&a[n+16]);
|
||||
in[3] = _mm256_load_ps(&a[n+24]);
|
||||
|
||||
in[0] = _mm256_add_ps(in[0], _mm256_load_ps(&b[n+ 0]));
|
||||
in[1] = _mm256_add_ps(in[1], _mm256_load_ps(&b[n+ 8]));
|
||||
in[2] = _mm256_add_ps(in[2], _mm256_load_ps(&b[n+16]));
|
||||
in[3] = _mm256_add_ps(in[3], _mm256_load_ps(&b[n+24]));
|
||||
|
||||
_mm256_store_ps(&r[n+ 0], in[0]);
|
||||
_mm256_store_ps(&r[n+ 8], in[1]);
|
||||
_mm256_store_ps(&r[n+16], in[2]);
|
||||
_mm256_store_ps(&r[n+24], in[3]);
|
||||
}
|
||||
} else {
|
||||
for (n = 0; n < unrolled; n += 32) {
|
||||
in[0] = _mm256_loadu_ps(&a[n+ 0]);
|
||||
in[1] = _mm256_loadu_ps(&a[n+ 8]);
|
||||
in[2] = _mm256_loadu_ps(&a[n+16]);
|
||||
in[3] = _mm256_loadu_ps(&a[n+24]);
|
||||
|
||||
in[0] = _mm256_add_ps(in[0], _mm256_loadu_ps(&b[n+ 0]));
|
||||
in[1] = _mm256_add_ps(in[1], _mm256_loadu_ps(&b[n+ 8]));
|
||||
in[2] = _mm256_add_ps(in[2], _mm256_loadu_ps(&b[n+16]));
|
||||
in[3] = _mm256_add_ps(in[3], _mm256_loadu_ps(&b[n+24]));
|
||||
|
||||
_mm256_storeu_ps(&r[n+ 0], in[0]);
|
||||
_mm256_storeu_ps(&r[n+ 8], in[1]);
|
||||
_mm256_storeu_ps(&r[n+16], in[2]);
|
||||
_mm256_storeu_ps(&r[n+24], in[3]);
|
||||
}
|
||||
}
|
||||
for (; n < n_samples; n++) {
|
||||
__m128 in[1];
|
||||
in[0] = _mm_load_ss(&a[n]);
|
||||
in[0] = _mm_add_ss(in[0], _mm_load_ss(&b[n]));
|
||||
_mm_store_ss(&r[n], in[0]);
|
||||
}
|
||||
}
|
||||
|
||||
inline static __m256 _mm256_mul_pz(__m256 ab, __m256 cd)
|
||||
{
|
||||
__m256 aa, bb, dc, x0, x1;
|
||||
aa = _mm256_moveldup_ps(ab);
|
||||
bb = _mm256_movehdup_ps(ab);
|
||||
x0 = _mm256_mul_ps(aa, cd);
|
||||
dc = _mm256_shuffle_ps(cd, cd, _MM_SHUFFLE(2,3,0,1));
|
||||
x1 = _mm256_mul_ps(bb, dc);
|
||||
return _mm256_addsub_ps(x0, x1);
|
||||
}
|
||||
|
||||
void dsp_fft_cmul_avx(void *obj, void *fft,
|
||||
float * SPA_RESTRICT dst, const float * SPA_RESTRICT a,
|
||||
const float * SPA_RESTRICT b, uint32_t len, const float scale)
|
||||
{
|
||||
#ifdef HAVE_FFTW
|
||||
__m256 s = _mm256_set1_ps(scale);
|
||||
__m256 aa[2], bb[2], dd[2];
|
||||
uint32_t i, unrolled;
|
||||
|
||||
if (SPA_IS_ALIGNED(a, 32) &&
|
||||
SPA_IS_ALIGNED(b, 32) &&
|
||||
SPA_IS_ALIGNED(dst, 32))
|
||||
unrolled = len & ~7;
|
||||
else
|
||||
unrolled = 0;
|
||||
|
||||
for (i = 0; i < unrolled; i+=8) {
|
||||
aa[0] = _mm256_load_ps(&a[2*i]); /* ar0 ai0 ar1 ai1 */
|
||||
aa[1] = _mm256_load_ps(&a[2*i+8]); /* ar1 ai1 ar2 ai2 */
|
||||
bb[0] = _mm256_load_ps(&b[2*i]); /* br0 bi0 br1 bi1 */
|
||||
bb[1] = _mm256_load_ps(&b[2*i+8]); /* br2 bi2 br3 bi3 */
|
||||
dd[0] = _mm256_mul_pz(aa[0], bb[0]);
|
||||
dd[1] = _mm256_mul_pz(aa[1], bb[1]);
|
||||
dd[0] = _mm256_mul_ps(dd[0], s);
|
||||
dd[1] = _mm256_mul_ps(dd[1], s);
|
||||
_mm256_store_ps(&dst[2*i], dd[0]);
|
||||
_mm256_store_ps(&dst[2*i+8], dd[1]);
|
||||
}
|
||||
for (; i < len; i++) {
|
||||
dst[2*i ] = (a[2*i] * b[2*i ] - a[2*i+1] * b[2*i+1]) * scale;
|
||||
dst[2*i+1] = (a[2*i] * b[2*i+1] + a[2*i+1] * b[2*i ]) * scale;
|
||||
}
|
||||
#else
|
||||
pffft_zconvolve(fft, a, b, dst, scale);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dsp_fft_cmuladd_avx(void *obj, void *fft,
|
||||
float * SPA_RESTRICT dst, const float * SPA_RESTRICT src,
|
||||
const float * SPA_RESTRICT a, const float * SPA_RESTRICT b,
|
||||
uint32_t len, const float scale)
|
||||
{
|
||||
#ifdef HAVE_FFTW
|
||||
__m256 s = _mm256_set1_ps(scale);
|
||||
__m256 aa[2], bb[2], dd[2], t[2];
|
||||
uint32_t i, unrolled;
|
||||
|
||||
if (SPA_IS_ALIGNED(a, 32) &&
|
||||
SPA_IS_ALIGNED(b, 32) &&
|
||||
SPA_IS_ALIGNED(src, 32) &&
|
||||
SPA_IS_ALIGNED(dst, 32))
|
||||
unrolled = len & ~7;
|
||||
else
|
||||
unrolled = 0;
|
||||
|
||||
for (i = 0; i < unrolled; i+=8) {
|
||||
aa[0] = _mm256_load_ps(&a[2*i]); /* ar0 ai0 ar1 ai1 */
|
||||
aa[1] = _mm256_load_ps(&a[2*i+8]); /* ar1 ai1 ar2 ai2 */
|
||||
bb[0] = _mm256_load_ps(&b[2*i]); /* br0 bi0 br1 bi1 */
|
||||
bb[1] = _mm256_load_ps(&b[2*i+8]); /* br2 bi2 br3 bi3 */
|
||||
dd[0] = _mm256_mul_pz(aa[0], bb[0]);
|
||||
dd[1] = _mm256_mul_pz(aa[1], bb[1]);
|
||||
dd[0] = _mm256_mul_ps(dd[0], s);
|
||||
dd[1] = _mm256_mul_ps(dd[1], s);
|
||||
t[0] = _mm256_load_ps(&src[2*i]);
|
||||
t[1] = _mm256_load_ps(&src[2*i+8]);
|
||||
t[0] = _mm256_add_ps(t[0], dd[0]);
|
||||
t[1] = _mm256_add_ps(t[1], dd[1]);
|
||||
_mm256_store_ps(&dst[2*i], t[0]);
|
||||
_mm256_store_ps(&dst[2*i+8], t[1]);
|
||||
}
|
||||
for (; i < len; i++) {
|
||||
dst[2*i ] = src[2*i ] + (a[2*i] * b[2*i ] - a[2*i+1] * b[2*i+1]) * scale;
|
||||
dst[2*i+1] = src[2*i+1] + (a[2*i] * b[2*i+1] + a[2*i+1] * b[2*i ]) * scale;
|
||||
}
|
||||
#else
|
||||
pffft_zconvolve_accumulate(fft, a, b, src, dst, scale);
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1,337 +0,0 @@
|
|||
/* Spa */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <spa/utils/defs.h>
|
||||
|
||||
#include "config.h"
|
||||
#ifdef HAVE_FFTW
|
||||
#include <fftw3.h>
|
||||
#else
|
||||
#include "pffft.h"
|
||||
#endif
|
||||
#include "audio-dsp-impl.h"
|
||||
|
||||
void dsp_clear_c(void *obj, void * SPA_RESTRICT dst, uint32_t n_samples)
|
||||
{
|
||||
memset(dst, 0, sizeof(float) * n_samples);
|
||||
}
|
||||
|
||||
void dsp_copy_c(void *obj, void * SPA_RESTRICT dst,
|
||||
const void * SPA_RESTRICT src, uint32_t n_samples)
|
||||
{
|
||||
if (dst != src)
|
||||
spa_memcpy(dst, src, sizeof(float) * n_samples);
|
||||
}
|
||||
|
||||
static inline void dsp_add_c(void *obj, void * SPA_RESTRICT dst,
|
||||
const void * SPA_RESTRICT src, uint32_t n_samples)
|
||||
{
|
||||
uint32_t i;
|
||||
const float *s = src;
|
||||
float *d = dst;
|
||||
for (i = 0; i < n_samples; i++)
|
||||
d[i] += s[i];
|
||||
}
|
||||
|
||||
static inline void dsp_gain_c(void *obj, void * SPA_RESTRICT dst,
|
||||
const void * SPA_RESTRICT src, float gain, uint32_t n_samples)
|
||||
{
|
||||
uint32_t i;
|
||||
const float *s = src;
|
||||
float *d = dst;
|
||||
if (gain == 0.0f)
|
||||
dsp_clear_c(obj, dst, n_samples);
|
||||
else if (gain == 1.0f)
|
||||
dsp_copy_c(obj, dst, src, n_samples);
|
||||
else {
|
||||
for (i = 0; i < n_samples; i++)
|
||||
d[i] = s[i] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dsp_gain_add_c(void *obj, void * SPA_RESTRICT dst,
|
||||
const void * SPA_RESTRICT src, float gain, uint32_t n_samples)
|
||||
{
|
||||
uint32_t i;
|
||||
const float *s = src;
|
||||
float *d = dst;
|
||||
|
||||
if (gain == 0.0f)
|
||||
return;
|
||||
else if (gain == 1.0f)
|
||||
dsp_add_c(obj, dst, src, n_samples);
|
||||
else {
|
||||
for (i = 0; i < n_samples; i++)
|
||||
d[i] += s[i] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dsp_mix_gain_c(void *obj,
|
||||
void * SPA_RESTRICT dst,
|
||||
const void * SPA_RESTRICT src[],
|
||||
float gain[], uint32_t n_src, uint32_t n_samples)
|
||||
{
|
||||
uint32_t i;
|
||||
if (n_src == 0) {
|
||||
dsp_clear_c(obj, dst, n_samples);
|
||||
} else {
|
||||
dsp_gain_c(obj, dst, src[0], gain[0], n_samples);
|
||||
for (i = 1; i < n_src; i++)
|
||||
dsp_gain_add_c(obj, dst, src[i], gain[i], n_samples);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dsp_mult1_c(void *obj, void * SPA_RESTRICT dst,
|
||||
const void * SPA_RESTRICT src, uint32_t n_samples)
|
||||
{
|
||||
uint32_t i;
|
||||
const float *s = src;
|
||||
float *d = dst;
|
||||
for (i = 0; i < n_samples; i++)
|
||||
d[i] *= s[i];
|
||||
}
|
||||
|
||||
void dsp_mult_c(void *obj,
|
||||
void * SPA_RESTRICT dst,
|
||||
const void * SPA_RESTRICT src[],
|
||||
uint32_t n_src, uint32_t n_samples)
|
||||
{
|
||||
uint32_t i;
|
||||
if (n_src == 0) {
|
||||
dsp_clear_c(obj, dst, n_samples);
|
||||
} else {
|
||||
dsp_copy_c(obj, dst, src[0], n_samples);
|
||||
for (i = 1; i < n_src; i++)
|
||||
dsp_mult1_c(obj, dst, src[i], n_samples);
|
||||
}
|
||||
}
|
||||
|
||||
static void biquad_run_c(void *obj, struct biquad *bq,
|
||||
float *out, const float *in, uint32_t n_samples)
|
||||
{
|
||||
float x, y, x1, x2;
|
||||
float b0, b1, b2, a1, a2;
|
||||
uint32_t i;
|
||||
|
||||
if (bq->type == BQ_NONE) {
|
||||
dsp_copy_c(obj, out, in, n_samples);
|
||||
return;
|
||||
}
|
||||
|
||||
x1 = bq->x1;
|
||||
x2 = bq->x2;
|
||||
b0 = bq->b0;
|
||||
b1 = bq->b1;
|
||||
b2 = bq->b2;
|
||||
a1 = bq->a1;
|
||||
a2 = bq->a2;
|
||||
for (i = 0; i < n_samples; i++) {
|
||||
x = in[i];
|
||||
y = b0 * x + x1;
|
||||
x1 = b1 * x - a1 * y + x2;
|
||||
x2 = b2 * x - a2 * y;
|
||||
out[i] = y;
|
||||
}
|
||||
#define F(x) (-FLT_MIN < (x) && (x) < FLT_MIN ? 0.0f : (x))
|
||||
bq->x1 = F(x1);
|
||||
bq->x2 = F(x2);
|
||||
#undef F
|
||||
}
|
||||
|
||||
void dsp_biquad_run_c(void *obj, struct biquad *bq, uint32_t n_bq, uint32_t bq_stride,
|
||||
float * SPA_RESTRICT out[], const float * SPA_RESTRICT in[],
|
||||
uint32_t n_src, uint32_t n_samples)
|
||||
{
|
||||
uint32_t i, j;
|
||||
const float *s;
|
||||
float *d;
|
||||
for (i = 0; i < n_src; i++, bq+=bq_stride) {
|
||||
s = in[i];
|
||||
d = out[i];
|
||||
if (s == NULL || d == NULL)
|
||||
continue;
|
||||
if (n_bq > 0)
|
||||
biquad_run_c(obj, &bq[0], d, s, n_samples);
|
||||
for (j = 1; j < n_bq; j++)
|
||||
biquad_run_c(obj, &bq[j], d, d, n_samples);
|
||||
}
|
||||
}
|
||||
|
||||
void dsp_sum_c(void *obj, float * dst,
|
||||
const float * SPA_RESTRICT a, const float * SPA_RESTRICT b, uint32_t n_samples)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < n_samples; i++)
|
||||
dst[i] = a[i] + b[i];
|
||||
}
|
||||
|
||||
void dsp_linear_c(void *obj, float * dst,
|
||||
const float * SPA_RESTRICT src, const float mult,
|
||||
const float add, uint32_t n_samples)
|
||||
{
|
||||
uint32_t i;
|
||||
if (add == 0.0f) {
|
||||
dsp_gain_c(obj, dst, src, mult, n_samples);
|
||||
} else {
|
||||
if (mult == 0.0f) {
|
||||
for (i = 0; i < n_samples; i++)
|
||||
dst[i] = add;
|
||||
} else if (mult == 1.0f) {
|
||||
for (i = 0; i < n_samples; i++)
|
||||
dst[i] = src[i] + add;
|
||||
} else {
|
||||
for (i = 0; i < n_samples; i++)
|
||||
dst[i] = mult * src[i] + add;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dsp_delay_c(void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer,
|
||||
uint32_t delay, float *dst, const float *src, uint32_t n_samples)
|
||||
{
|
||||
if (delay == 0) {
|
||||
dsp_copy_c(obj, dst, src, n_samples);
|
||||
} else {
|
||||
uint32_t w, o, i;
|
||||
|
||||
w = *pos;
|
||||
o = n_buffer - delay;
|
||||
|
||||
for (i = 0; i < n_samples; i++) {
|
||||
buffer[w] = buffer[w + n_buffer] = src[i];
|
||||
dst[i] = buffer[w + o];
|
||||
w = w + 1 > n_buffer ? 0 : w + 1;
|
||||
}
|
||||
*pos = w;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_FFTW
|
||||
struct fft_info {
|
||||
fftwf_plan plan_r2c;
|
||||
fftwf_plan plan_c2r;
|
||||
};
|
||||
#endif
|
||||
|
||||
void *dsp_fft_new_c(void *obj, uint32_t size, bool real)
|
||||
{
|
||||
#ifdef HAVE_FFTW
|
||||
struct fft_info *info = calloc(1, sizeof(struct fft_info));
|
||||
float *rdata;
|
||||
fftwf_complex *cdata;
|
||||
|
||||
if (info == NULL)
|
||||
return NULL;
|
||||
|
||||
rdata = fftwf_alloc_real(size * 2);
|
||||
cdata = fftwf_alloc_complex(size + 1);
|
||||
|
||||
info->plan_r2c = fftwf_plan_dft_r2c_1d(size, rdata, cdata, FFTW_ESTIMATE);
|
||||
info->plan_c2r = fftwf_plan_dft_c2r_1d(size, cdata, rdata, FFTW_ESTIMATE);
|
||||
|
||||
fftwf_free(rdata);
|
||||
fftwf_free(cdata);
|
||||
|
||||
return info;
|
||||
#else
|
||||
return pffft_new_setup(size, real ? PFFFT_REAL : PFFFT_COMPLEX);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dsp_fft_free_c(void *obj, void *fft)
|
||||
{
|
||||
#ifdef HAVE_FFTW
|
||||
struct fft_info *info = fft;
|
||||
fftwf_destroy_plan(info->plan_r2c);
|
||||
fftwf_destroy_plan(info->plan_c2r);
|
||||
free(info);
|
||||
#else
|
||||
pffft_destroy_setup(fft);
|
||||
#endif
|
||||
}
|
||||
|
||||
void *dsp_fft_memalloc_c(void *obj, uint32_t size, bool real)
|
||||
{
|
||||
#ifdef HAVE_FFTW
|
||||
if (real)
|
||||
return fftwf_alloc_real(size);
|
||||
else
|
||||
return fftwf_alloc_complex(size);
|
||||
#else
|
||||
if (real)
|
||||
return pffft_aligned_malloc(size * sizeof(float));
|
||||
else
|
||||
return pffft_aligned_malloc(size * 2 * sizeof(float));
|
||||
#endif
|
||||
}
|
||||
|
||||
void dsp_fft_memfree_c(void *obj, void *data)
|
||||
{
|
||||
#ifdef HAVE_FFTW
|
||||
fftwf_free(data);
|
||||
#else
|
||||
pffft_aligned_free(data);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dsp_fft_memclear_c(void *obj, void *data, uint32_t size, bool real)
|
||||
{
|
||||
#ifdef HAVE_FFTW
|
||||
spa_fga_dsp_clear(obj, data, real ? size : size * 2);
|
||||
#else
|
||||
spa_fga_dsp_clear(obj, data, real ? size : size * 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dsp_fft_run_c(void *obj, void *fft, int direction,
|
||||
const float * SPA_RESTRICT src, float * SPA_RESTRICT dst)
|
||||
{
|
||||
#ifdef HAVE_FFTW
|
||||
struct fft_info *info = fft;
|
||||
if (direction > 0)
|
||||
fftwf_execute_dft_r2c (info->plan_r2c, (float*)src, (fftwf_complex*)dst);
|
||||
else
|
||||
fftwf_execute_dft_c2r (info->plan_c2r, (fftwf_complex*)src, dst);
|
||||
#else
|
||||
pffft_transform(fft, src, dst, NULL, direction < 0 ? PFFFT_BACKWARD : PFFFT_FORWARD);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dsp_fft_cmul_c(void *obj, void *fft,
|
||||
float * SPA_RESTRICT dst, const float * SPA_RESTRICT a,
|
||||
const float * SPA_RESTRICT b, uint32_t len, const float scale)
|
||||
{
|
||||
#ifdef HAVE_FFTW
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
dst[2*i ] = (a[2*i] * b[2*i ] - a[2*i+1] * b[2*i+1]) * scale;
|
||||
dst[2*i+1] = (a[2*i] * b[2*i+1] + a[2*i+1] * b[2*i ]) * scale;
|
||||
}
|
||||
#else
|
||||
pffft_zconvolve(fft, a, b, dst, scale);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dsp_fft_cmuladd_c(void *obj, void *fft,
|
||||
float * SPA_RESTRICT dst, const float * SPA_RESTRICT src,
|
||||
const float * SPA_RESTRICT a, const float * SPA_RESTRICT b,
|
||||
uint32_t len, const float scale)
|
||||
{
|
||||
#ifdef HAVE_FFTW
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
dst[2*i ] = src[2*i ] + (a[2*i] * b[2*i ] - a[2*i+1] * b[2*i+1]) * scale;
|
||||
dst[2*i+1] = src[2*i+1] + (a[2*i] * b[2*i+1] + a[2*i+1] * b[2*i ]) * scale;
|
||||
}
|
||||
#else
|
||||
pffft_zconvolve_accumulate(fft, a, b, src, dst, scale);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
/* Spa */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef DSP_IMPL_H
|
||||
#define DSP_IMPL_H
|
||||
|
||||
#include "audio-dsp.h"
|
||||
|
||||
struct spa_fga_dsp * spa_fga_dsp_new(uint32_t cpu_flags);
|
||||
void spa_fga_dsp_free(struct spa_fga_dsp *dsp);
|
||||
|
||||
#define MAKE_CLEAR_FUNC(arch) \
|
||||
void dsp_clear_##arch(void *obj, void * SPA_RESTRICT dst, uint32_t n_samples)
|
||||
#define MAKE_COPY_FUNC(arch) \
|
||||
void dsp_copy_##arch(void *obj, void * SPA_RESTRICT dst, \
|
||||
const void * SPA_RESTRICT src, uint32_t n_samples)
|
||||
#define MAKE_MIX_GAIN_FUNC(arch) \
|
||||
void dsp_mix_gain_##arch(void *obj, void * SPA_RESTRICT dst, \
|
||||
const void * SPA_RESTRICT src[], float gain[], uint32_t n_src, uint32_t n_samples)
|
||||
#define MAKE_SUM_FUNC(arch) \
|
||||
void dsp_sum_##arch (void *obj, float * SPA_RESTRICT dst, \
|
||||
const float * SPA_RESTRICT a, const float * SPA_RESTRICT b, uint32_t n_samples)
|
||||
#define MAKE_LINEAR_FUNC(arch) \
|
||||
void dsp_linear_##arch (void *obj, float * SPA_RESTRICT dst, \
|
||||
const float * SPA_RESTRICT src, const float mult, const float add, uint32_t n_samples)
|
||||
#define MAKE_MULT_FUNC(arch) \
|
||||
void dsp_mult_##arch(void *obj, void * SPA_RESTRICT dst, \
|
||||
const void * SPA_RESTRICT src[], uint32_t n_src, uint32_t n_samples)
|
||||
#define MAKE_BIQUAD_RUN_FUNC(arch) \
|
||||
void dsp_biquad_run_##arch (void *obj, struct biquad *bq, uint32_t n_bq, uint32_t bq_stride, \
|
||||
float * SPA_RESTRICT out[], const float * SPA_RESTRICT in[], uint32_t n_src, uint32_t n_samples)
|
||||
#define MAKE_DELAY_FUNC(arch) \
|
||||
void dsp_delay_##arch (void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer, \
|
||||
uint32_t delay, float *dst, const float *src, uint32_t n_samples)
|
||||
|
||||
#define MAKE_FFT_NEW_FUNC(arch) \
|
||||
void *dsp_fft_new_##arch(void *obj, uint32_t size, bool real)
|
||||
#define MAKE_FFT_FREE_FUNC(arch) \
|
||||
void dsp_fft_free_##arch(void *obj, void *fft)
|
||||
#define MAKE_FFT_MEMALLOC_FUNC(arch) \
|
||||
void *dsp_fft_memalloc_##arch(void *obj, uint32_t size, bool real)
|
||||
#define MAKE_FFT_MEMFREE_FUNC(arch) \
|
||||
void dsp_fft_memfree_##arch(void *obj, void *mem)
|
||||
#define MAKE_FFT_MEMCLEAR_FUNC(arch) \
|
||||
void dsp_fft_memclear_##arch(void *obj, void *mem, uint32_t size, bool real)
|
||||
#define MAKE_FFT_RUN_FUNC(arch) \
|
||||
void dsp_fft_run_##arch(void *obj, void *fft, int direction, \
|
||||
const float * SPA_RESTRICT src, float * SPA_RESTRICT dst)
|
||||
#define MAKE_FFT_CMUL_FUNC(arch) \
|
||||
void dsp_fft_cmul_##arch(void *obj, void *fft, \
|
||||
float * SPA_RESTRICT dst, const float * SPA_RESTRICT a, \
|
||||
const float * SPA_RESTRICT b, uint32_t len, const float scale)
|
||||
#define MAKE_FFT_CMULADD_FUNC(arch) \
|
||||
void dsp_fft_cmuladd_##arch(void *obj, void *fft, \
|
||||
float * dst, const float * src, \
|
||||
const float * SPA_RESTRICT a, const float * SPA_RESTRICT b, \
|
||||
uint32_t len, const float scale)
|
||||
|
||||
|
||||
MAKE_CLEAR_FUNC(c);
|
||||
MAKE_COPY_FUNC(c);
|
||||
MAKE_MIX_GAIN_FUNC(c);
|
||||
MAKE_SUM_FUNC(c);
|
||||
MAKE_LINEAR_FUNC(c);
|
||||
MAKE_MULT_FUNC(c);
|
||||
MAKE_BIQUAD_RUN_FUNC(c);
|
||||
MAKE_DELAY_FUNC(c);
|
||||
|
||||
MAKE_FFT_NEW_FUNC(c);
|
||||
MAKE_FFT_FREE_FUNC(c);
|
||||
MAKE_FFT_MEMALLOC_FUNC(c);
|
||||
MAKE_FFT_MEMFREE_FUNC(c);
|
||||
MAKE_FFT_MEMCLEAR_FUNC(c);
|
||||
MAKE_FFT_RUN_FUNC(c);
|
||||
MAKE_FFT_CMUL_FUNC(c);
|
||||
MAKE_FFT_CMULADD_FUNC(c);
|
||||
|
||||
#if defined (HAVE_SSE)
|
||||
MAKE_MIX_GAIN_FUNC(sse);
|
||||
MAKE_SUM_FUNC(sse);
|
||||
MAKE_BIQUAD_RUN_FUNC(sse);
|
||||
MAKE_DELAY_FUNC(sse);
|
||||
MAKE_FFT_CMUL_FUNC(sse);
|
||||
MAKE_FFT_CMULADD_FUNC(sse);
|
||||
#endif
|
||||
#if defined (HAVE_AVX)
|
||||
MAKE_MIX_GAIN_FUNC(avx);
|
||||
MAKE_SUM_FUNC(avx);
|
||||
MAKE_FFT_CMUL_FUNC(avx);
|
||||
MAKE_FFT_CMULADD_FUNC(avx);
|
||||
#endif
|
||||
|
||||
#endif /* DSP_OPS_IMPL_H */
|
||||
|
|
@ -1,639 +0,0 @@
|
|||
/* Spa */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <complex.h>
|
||||
|
||||
#include <spa/utils/defs.h>
|
||||
|
||||
#include "config.h"
|
||||
#ifndef HAVE_FFTW
|
||||
#include "pffft.h"
|
||||
#endif
|
||||
|
||||
#include "audio-dsp-impl.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
|
||||
void dsp_mix_gain_sse(void *obj,
|
||||
void * SPA_RESTRICT dst,
|
||||
const void * SPA_RESTRICT src[],
|
||||
float gain[], uint32_t n_src, uint32_t n_samples)
|
||||
{
|
||||
if (n_src == 0) {
|
||||
memset(dst, 0, n_samples * sizeof(float));
|
||||
} else if (n_src == 1 && gain[0] == 1.0f) {
|
||||
if (dst != src[0])
|
||||
spa_memcpy(dst, src[0], n_samples * sizeof(float));
|
||||
} else {
|
||||
uint32_t n, i, unrolled;
|
||||
__m128 in[4], g;
|
||||
const float **s = (const float **)src;
|
||||
float *d = dst;
|
||||
|
||||
if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 16))) {
|
||||
unrolled = n_samples & ~15;
|
||||
for (i = 0; i < n_src; i++) {
|
||||
if (SPA_UNLIKELY(!SPA_IS_ALIGNED(src[i], 16))) {
|
||||
unrolled = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
unrolled = 0;
|
||||
|
||||
for (n = 0; n < unrolled; n += 16) {
|
||||
g = _mm_set1_ps(gain[0]);
|
||||
in[0] = _mm_mul_ps(g, _mm_load_ps(&s[0][n+ 0]));
|
||||
in[1] = _mm_mul_ps(g, _mm_load_ps(&s[0][n+ 4]));
|
||||
in[2] = _mm_mul_ps(g, _mm_load_ps(&s[0][n+ 8]));
|
||||
in[3] = _mm_mul_ps(g, _mm_load_ps(&s[0][n+12]));
|
||||
|
||||
for (i = 1; i < n_src; i++) {
|
||||
g = _mm_set1_ps(gain[i]);
|
||||
in[0] = _mm_add_ps(in[0], _mm_mul_ps(g, _mm_load_ps(&s[i][n+ 0])));
|
||||
in[1] = _mm_add_ps(in[1], _mm_mul_ps(g, _mm_load_ps(&s[i][n+ 4])));
|
||||
in[2] = _mm_add_ps(in[2], _mm_mul_ps(g, _mm_load_ps(&s[i][n+ 8])));
|
||||
in[3] = _mm_add_ps(in[3], _mm_mul_ps(g, _mm_load_ps(&s[i][n+12])));
|
||||
}
|
||||
_mm_store_ps(&d[n+ 0], in[0]);
|
||||
_mm_store_ps(&d[n+ 4], in[1]);
|
||||
_mm_store_ps(&d[n+ 8], in[2]);
|
||||
_mm_store_ps(&d[n+12], in[3]);
|
||||
}
|
||||
for (; n < n_samples; n++) {
|
||||
g = _mm_set_ss(gain[0]);
|
||||
in[0] = _mm_mul_ss(g, _mm_load_ss(&s[0][n]));
|
||||
for (i = 1; i < n_src; i++) {
|
||||
g = _mm_set_ss(gain[i]);
|
||||
in[0] = _mm_add_ss(in[0], _mm_mul_ss(g, _mm_load_ss(&s[i][n])));
|
||||
}
|
||||
_mm_store_ss(&d[n], in[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dsp_sum_sse(void *obj, float *r, const float *a, const float *b, uint32_t n_samples)
|
||||
{
|
||||
uint32_t n, unrolled;
|
||||
__m128 in[4];
|
||||
|
||||
unrolled = n_samples & ~15;
|
||||
|
||||
if (SPA_LIKELY(SPA_IS_ALIGNED(r, 16)) &&
|
||||
SPA_LIKELY(SPA_IS_ALIGNED(a, 16)) &&
|
||||
SPA_LIKELY(SPA_IS_ALIGNED(b, 16))) {
|
||||
for (n = 0; n < unrolled; n += 16) {
|
||||
in[0] = _mm_load_ps(&a[n+ 0]);
|
||||
in[1] = _mm_load_ps(&a[n+ 4]);
|
||||
in[2] = _mm_load_ps(&a[n+ 8]);
|
||||
in[3] = _mm_load_ps(&a[n+12]);
|
||||
|
||||
in[0] = _mm_add_ps(in[0], _mm_load_ps(&b[n+ 0]));
|
||||
in[1] = _mm_add_ps(in[1], _mm_load_ps(&b[n+ 4]));
|
||||
in[2] = _mm_add_ps(in[2], _mm_load_ps(&b[n+ 8]));
|
||||
in[3] = _mm_add_ps(in[3], _mm_load_ps(&b[n+12]));
|
||||
|
||||
_mm_store_ps(&r[n+ 0], in[0]);
|
||||
_mm_store_ps(&r[n+ 4], in[1]);
|
||||
_mm_store_ps(&r[n+ 8], in[2]);
|
||||
_mm_store_ps(&r[n+12], in[3]);
|
||||
}
|
||||
} else {
|
||||
for (n = 0; n < unrolled; n += 16) {
|
||||
in[0] = _mm_loadu_ps(&a[n+ 0]);
|
||||
in[1] = _mm_loadu_ps(&a[n+ 4]);
|
||||
in[2] = _mm_loadu_ps(&a[n+ 8]);
|
||||
in[3] = _mm_loadu_ps(&a[n+12]);
|
||||
|
||||
in[0] = _mm_add_ps(in[0], _mm_loadu_ps(&b[n+ 0]));
|
||||
in[1] = _mm_add_ps(in[1], _mm_loadu_ps(&b[n+ 4]));
|
||||
in[2] = _mm_add_ps(in[2], _mm_loadu_ps(&b[n+ 8]));
|
||||
in[3] = _mm_add_ps(in[3], _mm_loadu_ps(&b[n+12]));
|
||||
|
||||
_mm_storeu_ps(&r[n+ 0], in[0]);
|
||||
_mm_storeu_ps(&r[n+ 4], in[1]);
|
||||
_mm_storeu_ps(&r[n+ 8], in[2]);
|
||||
_mm_storeu_ps(&r[n+12], in[3]);
|
||||
}
|
||||
}
|
||||
for (; n < n_samples; n++) {
|
||||
in[0] = _mm_load_ss(&a[n]);
|
||||
in[0] = _mm_add_ss(in[0], _mm_load_ss(&b[n]));
|
||||
_mm_store_ss(&r[n], in[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static void dsp_biquad_run1_sse(void *obj, struct biquad *bq,
|
||||
float *out, const float *in, uint32_t n_samples)
|
||||
{
|
||||
__m128 x, y, z;
|
||||
__m128 b012;
|
||||
__m128 a12;
|
||||
__m128 x12;
|
||||
uint32_t i;
|
||||
|
||||
b012 = _mm_setr_ps(bq->b0, bq->b1, bq->b2, 0.0f); /* b0 b1 b2 0 */
|
||||
a12 = _mm_setr_ps(0.0f, bq->a1, bq->a2, 0.0f); /* 0 a1 a2 0 */
|
||||
x12 = _mm_setr_ps(bq->x1, bq->x2, 0.0f, 0.0f); /* x1 x2 0 0 */
|
||||
|
||||
for (i = 0; i < n_samples; i++) {
|
||||
x = _mm_load1_ps(&in[i]); /* x x x x */
|
||||
z = _mm_mul_ps(x, b012); /* b0*x b1*x b2*x 0 */
|
||||
z = _mm_add_ps(z, x12); /* b0*x+x1 b1*x+x2 b2*x 0 */
|
||||
_mm_store_ss(&out[i], z); /* out[i] = b0*x+x1 */
|
||||
y = _mm_shuffle_ps(z, z, _MM_SHUFFLE(0,0,0,0)); /* b0*x+x1 b0*x+x1 b0*x+x1 b0*x+x1 = y*/
|
||||
y = _mm_mul_ps(y, a12); /* 0 a1*y a2*y 0 */
|
||||
y = _mm_sub_ps(z, y); /* y x1 x2 0 */
|
||||
x12 = _mm_shuffle_ps(y, y, _MM_SHUFFLE(3,3,2,1)); /* x1 x2 0 0*/
|
||||
}
|
||||
#define F(x) (-FLT_MIN < (x) && (x) < FLT_MIN ? 0.0f : (x))
|
||||
bq->x1 = F(x12[0]);
|
||||
bq->x2 = F(x12[1]);
|
||||
#undef F
|
||||
}
|
||||
|
||||
static void dsp_biquad2_run_sse(void *obj, struct biquad *bq,
|
||||
float *out, const float *in, uint32_t n_samples)
|
||||
{
|
||||
__m128 x, y, z;
|
||||
__m128 b0, b1;
|
||||
__m128 a0, a1;
|
||||
__m128 x0, x1;
|
||||
uint32_t i;
|
||||
|
||||
b0 = _mm_setr_ps(bq[0].b0, bq[0].b1, bq[0].b2, 0.0f); /* b0 b1 b2 0 */
|
||||
a0 = _mm_setr_ps(0.0f, bq[0].a1, bq[0].a2, 0.0f); /* 0 a1 a2 0 */
|
||||
x0 = _mm_setr_ps(bq[0].x1, bq[0].x2, 0.0f, 0.0f); /* x1 x2 0 0 */
|
||||
|
||||
b1 = _mm_setr_ps(bq[1].b0, bq[1].b1, bq[1].b2, 0.0f); /* b0 b1 b2 0 */
|
||||
a1 = _mm_setr_ps(0.0f, bq[1].a1, bq[1].a2, 0.0f); /* 0 a1 a2 0 */
|
||||
x1 = _mm_setr_ps(bq[1].x1, bq[1].x2, 0.0f, 0.0f); /* x1 x2 0 0 */
|
||||
|
||||
for (i = 0; i < n_samples; i++) {
|
||||
x = _mm_load1_ps(&in[i]); /* x x x x */
|
||||
|
||||
z = _mm_mul_ps(x, b0); /* b0*x b1*x b2*x 0 */
|
||||
z = _mm_add_ps(z, x0); /* b0*x+x1 b1*x+x2 b2*x 0 */
|
||||
y = _mm_shuffle_ps(z, z, _MM_SHUFFLE(0,0,0,0)); /* b0*x+x1 b0*x+x1 b0*x+x1 b0*x+x1 = y*/
|
||||
x = _mm_mul_ps(y, a0); /* 0 a1*y a2*y 0 */
|
||||
x = _mm_sub_ps(z, x); /* y x1 x2 0 */
|
||||
x0 = _mm_shuffle_ps(x, x, _MM_SHUFFLE(3,3,2,1)); /* x1 x2 0 0*/
|
||||
|
||||
z = _mm_mul_ps(y, b1); /* b0*x b1*x b2*x 0 */
|
||||
z = _mm_add_ps(z, x1); /* b0*x+x1 b1*x+x2 b2*x 0 */
|
||||
x = _mm_shuffle_ps(z, z, _MM_SHUFFLE(0,0,0,0)); /* b0*x+x1 b0*x+x1 b0*x+x1 b0*x+x1 = y*/
|
||||
y = _mm_mul_ps(x, a1); /* 0 a1*y a2*y 0 */
|
||||
y = _mm_sub_ps(z, y); /* y x1 x2 0 */
|
||||
x1 = _mm_shuffle_ps(y, y, _MM_SHUFFLE(3,3,2,1)); /* x1 x2 0 0*/
|
||||
|
||||
_mm_store_ss(&out[i], x); /* out[i] = b0*x+x1 */
|
||||
}
|
||||
#define F(x) (-FLT_MIN < (x) && (x) < FLT_MIN ? 0.0f : (x))
|
||||
bq[0].x1 = F(x0[0]);
|
||||
bq[0].x2 = F(x0[1]);
|
||||
bq[1].x1 = F(x1[0]);
|
||||
bq[1].x2 = F(x1[1]);
|
||||
#undef F
|
||||
}
|
||||
|
||||
static void dsp_biquad_run2_sse(void *obj, struct biquad *bq, uint32_t bq_stride,
|
||||
float **out, const float **in, uint32_t n_samples)
|
||||
{
|
||||
__m128 x, y, z;
|
||||
__m128 b0, b1, b2;
|
||||
__m128 a1, a2;
|
||||
__m128 x1, x2;
|
||||
uint32_t i;
|
||||
|
||||
b0 = _mm_setr_ps(bq[0].b0, bq[bq_stride].b0, 0.0f, 0.0f); /* b00 b10 0 0 */
|
||||
b1 = _mm_setr_ps(bq[0].b1, bq[bq_stride].b1, 0.0f, 0.0f); /* b01 b11 0 0 */
|
||||
b2 = _mm_setr_ps(bq[0].b2, bq[bq_stride].b2, 0.0f, 0.0f); /* b02 b12 0 0 */
|
||||
a1 = _mm_setr_ps(bq[0].a1, bq[bq_stride].a1, 0.0f, 0.0f); /* b00 b10 0 0 */
|
||||
a2 = _mm_setr_ps(bq[0].a2, bq[bq_stride].a2, 0.0f, 0.0f); /* b01 b11 0 0 */
|
||||
x1 = _mm_setr_ps(bq[0].x1, bq[bq_stride].x1, 0.0f, 0.0f); /* b00 b10 0 0 */
|
||||
x2 = _mm_setr_ps(bq[0].x2, bq[bq_stride].x2, 0.0f, 0.0f); /* b01 b11 0 0 */
|
||||
|
||||
for (i = 0; i < n_samples; i++) {
|
||||
x = _mm_setr_ps(in[0][i], in[1][i], 0.0f, 0.0f);
|
||||
|
||||
y = _mm_mul_ps(x, b0); /* y = x * b0 */
|
||||
y = _mm_add_ps(y, x1); /* y = x * b0 + x1*/
|
||||
z = _mm_mul_ps(y, a1); /* z = a1 * y */
|
||||
x1 = _mm_mul_ps(x, b1); /* x1 = x * b1 */
|
||||
x1 = _mm_add_ps(x1, x2); /* x1 = x * b1 + x2*/
|
||||
x1 = _mm_sub_ps(x1, z); /* x1 = x * b1 + x2 - a1 * y*/
|
||||
z = _mm_mul_ps(y, a2); /* z = a2 * y */
|
||||
x2 = _mm_mul_ps(x, b2); /* x2 = x * b2 */
|
||||
x2 = _mm_sub_ps(x2, z); /* x2 = x * b2 - a2 * y*/
|
||||
|
||||
out[0][i] = y[0];
|
||||
out[1][i] = y[1];
|
||||
}
|
||||
#define F(x) (-FLT_MIN < (x) && (x) < FLT_MIN ? 0.0f : (x))
|
||||
bq[0*bq_stride].x1 = F(x1[0]);
|
||||
bq[0*bq_stride].x2 = F(x2[0]);
|
||||
bq[1*bq_stride].x1 = F(x1[1]);
|
||||
bq[1*bq_stride].x2 = F(x2[1]);
|
||||
#undef F
|
||||
}
|
||||
|
||||
|
||||
static void dsp_biquad2_run2_sse(void *obj, struct biquad *bq, uint32_t bq_stride,
|
||||
float **out, const float **in, uint32_t n_samples)
|
||||
{
|
||||
__m128 x, y, z;
|
||||
__m128 b00, b01, b02, b10, b11, b12;
|
||||
__m128 a01, a02, a11, a12;
|
||||
__m128 x01, x02, x11, x12;
|
||||
uint32_t i;
|
||||
|
||||
b00 = _mm_setr_ps(bq[0].b0, bq[bq_stride].b0, 0.0f, 0.0f); /* b00 b10 0 0 */
|
||||
b01 = _mm_setr_ps(bq[0].b1, bq[bq_stride].b1, 0.0f, 0.0f); /* b01 b11 0 0 */
|
||||
b02 = _mm_setr_ps(bq[0].b2, bq[bq_stride].b2, 0.0f, 0.0f); /* b02 b12 0 0 */
|
||||
a01 = _mm_setr_ps(bq[0].a1, bq[bq_stride].a1, 0.0f, 0.0f); /* b00 b10 0 0 */
|
||||
a02 = _mm_setr_ps(bq[0].a2, bq[bq_stride].a2, 0.0f, 0.0f); /* b01 b11 0 0 */
|
||||
x01 = _mm_setr_ps(bq[0].x1, bq[bq_stride].x1, 0.0f, 0.0f); /* b00 b10 0 0 */
|
||||
x02 = _mm_setr_ps(bq[0].x2, bq[bq_stride].x2, 0.0f, 0.0f); /* b01 b11 0 0 */
|
||||
|
||||
b10 = _mm_setr_ps(bq[1].b0, bq[bq_stride+1].b0, 0.0f, 0.0f); /* b00 b10 0 0 */
|
||||
b11 = _mm_setr_ps(bq[1].b1, bq[bq_stride+1].b1, 0.0f, 0.0f); /* b01 b11 0 0 */
|
||||
b12 = _mm_setr_ps(bq[1].b2, bq[bq_stride+1].b2, 0.0f, 0.0f); /* b02 b12 0 0 */
|
||||
a11 = _mm_setr_ps(bq[1].a1, bq[bq_stride+1].a1, 0.0f, 0.0f); /* b00 b10 0 0 */
|
||||
a12 = _mm_setr_ps(bq[1].a2, bq[bq_stride+1].a2, 0.0f, 0.0f); /* b01 b11 0 0 */
|
||||
x11 = _mm_setr_ps(bq[1].x1, bq[bq_stride+1].x1, 0.0f, 0.0f); /* b00 b10 0 0 */
|
||||
x12 = _mm_setr_ps(bq[1].x2, bq[bq_stride+1].x2, 0.0f, 0.0f); /* b01 b11 0 0 */
|
||||
|
||||
for (i = 0; i < n_samples; i++) {
|
||||
x = _mm_setr_ps(in[0][i], in[1][i], 0.0f, 0.0f);
|
||||
|
||||
y = _mm_mul_ps(x, b00); /* y = x * b0 */
|
||||
y = _mm_add_ps(y, x01); /* y = x * b0 + x1*/
|
||||
z = _mm_mul_ps(y, a01); /* z = a1 * y */
|
||||
x01 = _mm_mul_ps(x, b01); /* x1 = x * b1 */
|
||||
x01 = _mm_add_ps(x01, x02); /* x1 = x * b1 + x2*/
|
||||
x01 = _mm_sub_ps(x01, z); /* x1 = x * b1 + x2 - a1 * y*/
|
||||
z = _mm_mul_ps(y, a02); /* z = a2 * y */
|
||||
x02 = _mm_mul_ps(x, b02); /* x2 = x * b2 */
|
||||
x02 = _mm_sub_ps(x02, z); /* x2 = x * b2 - a2 * y*/
|
||||
|
||||
x = y;
|
||||
|
||||
y = _mm_mul_ps(x, b10); /* y = x * b0 */
|
||||
y = _mm_add_ps(y, x11); /* y = x * b0 + x1*/
|
||||
z = _mm_mul_ps(y, a11); /* z = a1 * y */
|
||||
x11 = _mm_mul_ps(x, b11); /* x1 = x * b1 */
|
||||
x11 = _mm_add_ps(x11, x12); /* x1 = x * b1 + x2*/
|
||||
x11 = _mm_sub_ps(x11, z); /* x1 = x * b1 + x2 - a1 * y*/
|
||||
z = _mm_mul_ps(y, a12); /* z = a2 * y*/
|
||||
x12 = _mm_mul_ps(x, b12); /* x2 = x * b2 */
|
||||
x12 = _mm_sub_ps(x12, z); /* x2 = x * b2 - a2 * y*/
|
||||
|
||||
out[0][i] = y[0];
|
||||
out[1][i] = y[1];
|
||||
}
|
||||
#define F(x) (-FLT_MIN < (x) && (x) < FLT_MIN ? 0.0f : (x))
|
||||
bq[0*bq_stride+0].x1 = F(x01[0]);
|
||||
bq[0*bq_stride+0].x2 = F(x02[0]);
|
||||
bq[1*bq_stride+0].x1 = F(x01[1]);
|
||||
bq[1*bq_stride+0].x2 = F(x02[1]);
|
||||
|
||||
bq[0*bq_stride+1].x1 = F(x11[0]);
|
||||
bq[0*bq_stride+1].x2 = F(x12[0]);
|
||||
bq[1*bq_stride+1].x1 = F(x11[1]);
|
||||
bq[1*bq_stride+1].x2 = F(x12[1]);
|
||||
#undef F
|
||||
}
|
||||
|
||||
static void dsp_biquad_run4_sse(void *obj, struct biquad *bq, uint32_t bq_stride,
|
||||
float **out, const float **in, uint32_t n_samples)
|
||||
{
|
||||
__m128 x, y, z;
|
||||
__m128 b0, b1, b2;
|
||||
__m128 a1, a2;
|
||||
__m128 x1, x2;
|
||||
uint32_t i;
|
||||
|
||||
b0 = _mm_setr_ps(bq[0].b0, bq[bq_stride].b0, bq[2*bq_stride].b0, bq[3*bq_stride].b0);
|
||||
b1 = _mm_setr_ps(bq[0].b1, bq[bq_stride].b1, bq[2*bq_stride].b1, bq[3*bq_stride].b1);
|
||||
b2 = _mm_setr_ps(bq[0].b2, bq[bq_stride].b2, bq[2*bq_stride].b2, bq[3*bq_stride].b2);
|
||||
a1 = _mm_setr_ps(bq[0].a1, bq[bq_stride].a1, bq[2*bq_stride].a1, bq[3*bq_stride].a1);
|
||||
a2 = _mm_setr_ps(bq[0].a2, bq[bq_stride].a2, bq[2*bq_stride].a2, bq[3*bq_stride].a2);
|
||||
x1 = _mm_setr_ps(bq[0].x1, bq[bq_stride].x1, bq[2*bq_stride].x1, bq[3*bq_stride].x1);
|
||||
x2 = _mm_setr_ps(bq[0].x2, bq[bq_stride].x2, bq[2*bq_stride].x2, bq[3*bq_stride].x2);
|
||||
|
||||
for (i = 0; i < n_samples; i++) {
|
||||
x = _mm_setr_ps(in[0][i], in[1][i], in[2][i], in[3][i]);
|
||||
|
||||
y = _mm_mul_ps(x, b0); /* y = x * b0 */
|
||||
y = _mm_add_ps(y, x1); /* y = x * b0 + x1*/
|
||||
z = _mm_mul_ps(y, a1); /* z = a1 * y */
|
||||
x1 = _mm_mul_ps(x, b1); /* x1 = x * b1 */
|
||||
x1 = _mm_add_ps(x1, x2); /* x1 = x * b1 + x2*/
|
||||
x1 = _mm_sub_ps(x1, z); /* x1 = x * b1 + x2 - a1 * y*/
|
||||
z = _mm_mul_ps(y, a2); /* z = a2 * y */
|
||||
x2 = _mm_mul_ps(x, b2); /* x2 = x * b2 */
|
||||
x2 = _mm_sub_ps(x2, z); /* x2 = x * b2 - a2 * y*/
|
||||
|
||||
out[0][i] = y[0];
|
||||
out[1][i] = y[1];
|
||||
out[2][i] = y[2];
|
||||
out[3][i] = y[3];
|
||||
}
|
||||
#define F(x) (-FLT_MIN < (x) && (x) < FLT_MIN ? 0.0f : (x))
|
||||
bq[0*bq_stride].x1 = F(x1[0]);
|
||||
bq[0*bq_stride].x2 = F(x2[0]);
|
||||
bq[1*bq_stride].x1 = F(x1[1]);
|
||||
bq[1*bq_stride].x2 = F(x2[1]);
|
||||
bq[2*bq_stride].x1 = F(x1[2]);
|
||||
bq[2*bq_stride].x2 = F(x2[2]);
|
||||
bq[3*bq_stride].x1 = F(x1[3]);
|
||||
bq[3*bq_stride].x2 = F(x2[3]);
|
||||
#undef F
|
||||
}
|
||||
|
||||
static void dsp_biquad2_run4_sse(void *obj, struct biquad *bq, uint32_t bq_stride,
|
||||
float **out, const float **in, uint32_t n_samples)
|
||||
{
|
||||
__m128 x, y, z;
|
||||
__m128 b00, b01, b02, b10, b11, b12;
|
||||
__m128 a01, a02, a11, a12;
|
||||
__m128 x01, x02, x11, x12;
|
||||
uint32_t i;
|
||||
|
||||
b00 = _mm_setr_ps(bq[0].b0, bq[bq_stride].b0, bq[2*bq_stride].b0, bq[3*bq_stride].b0);
|
||||
b01 = _mm_setr_ps(bq[0].b1, bq[bq_stride].b1, bq[2*bq_stride].b1, bq[3*bq_stride].b1);
|
||||
b02 = _mm_setr_ps(bq[0].b2, bq[bq_stride].b2, bq[2*bq_stride].b2, bq[3*bq_stride].b2);
|
||||
a01 = _mm_setr_ps(bq[0].a1, bq[bq_stride].a1, bq[2*bq_stride].a1, bq[3*bq_stride].a1);
|
||||
a02 = _mm_setr_ps(bq[0].a2, bq[bq_stride].a2, bq[2*bq_stride].a2, bq[3*bq_stride].a2);
|
||||
x01 = _mm_setr_ps(bq[0].x1, bq[bq_stride].x1, bq[2*bq_stride].x1, bq[3*bq_stride].x1);
|
||||
x02 = _mm_setr_ps(bq[0].x2, bq[bq_stride].x2, bq[2*bq_stride].x2, bq[3*bq_stride].x2);
|
||||
|
||||
b10 = _mm_setr_ps(bq[1].b0, bq[bq_stride+1].b0, bq[2*bq_stride+1].b0, bq[3*bq_stride+1].b0);
|
||||
b11 = _mm_setr_ps(bq[1].b1, bq[bq_stride+1].b1, bq[2*bq_stride+1].b1, bq[3*bq_stride+1].b1);
|
||||
b12 = _mm_setr_ps(bq[1].b2, bq[bq_stride+1].b2, bq[2*bq_stride+1].b2, bq[3*bq_stride+1].b2);
|
||||
a11 = _mm_setr_ps(bq[1].a1, bq[bq_stride+1].a1, bq[2*bq_stride+1].a1, bq[3*bq_stride+1].a1);
|
||||
a12 = _mm_setr_ps(bq[1].a2, bq[bq_stride+1].a2, bq[2*bq_stride+1].a2, bq[3*bq_stride+1].a2);
|
||||
x11 = _mm_setr_ps(bq[1].x1, bq[bq_stride+1].x1, bq[2*bq_stride+1].x1, bq[3*bq_stride+1].x1);
|
||||
x12 = _mm_setr_ps(bq[1].x2, bq[bq_stride+1].x2, bq[2*bq_stride+1].x2, bq[3*bq_stride+1].x2);
|
||||
|
||||
for (i = 0; i < n_samples; i++) {
|
||||
x = _mm_setr_ps(in[0][i], in[1][i], in[2][i], in[3][i]);
|
||||
|
||||
y = _mm_mul_ps(x, b00); /* y = x * b0 */
|
||||
y = _mm_add_ps(y, x01); /* y = x * b0 + x1*/
|
||||
z = _mm_mul_ps(y, a01); /* z = a1 * y */
|
||||
x01 = _mm_mul_ps(x, b01); /* x1 = x * b1 */
|
||||
x01 = _mm_add_ps(x01, x02); /* x1 = x * b1 + x2*/
|
||||
x01 = _mm_sub_ps(x01, z); /* x1 = x * b1 + x2 - a1 * y*/
|
||||
z = _mm_mul_ps(y, a02); /* z = a2 * y */
|
||||
x02 = _mm_mul_ps(x, b02); /* x2 = x * b2 */
|
||||
x02 = _mm_sub_ps(x02, z); /* x2 = x * b2 - a2 * y*/
|
||||
|
||||
x = y;
|
||||
|
||||
y = _mm_mul_ps(x, b10); /* y = x * b0 */
|
||||
y = _mm_add_ps(y, x11); /* y = x * b0 + x1*/
|
||||
z = _mm_mul_ps(y, a11); /* z = a1 * y */
|
||||
x11 = _mm_mul_ps(x, b11); /* x1 = x * b1 */
|
||||
x11 = _mm_add_ps(x11, x12); /* x1 = x * b1 + x2*/
|
||||
x11 = _mm_sub_ps(x11, z); /* x1 = x * b1 + x2 - a1 * y*/
|
||||
z = _mm_mul_ps(y, a12); /* z = a2 * y*/
|
||||
x12 = _mm_mul_ps(x, b12); /* x2 = x * b2 */
|
||||
x12 = _mm_sub_ps(x12, z); /* x2 = x * b2 - a2 * y*/
|
||||
|
||||
out[0][i] = y[0];
|
||||
out[1][i] = y[1];
|
||||
out[2][i] = y[2];
|
||||
out[3][i] = y[3];
|
||||
}
|
||||
#define F(x) (-FLT_MIN < (x) && (x) < FLT_MIN ? 0.0f : (x))
|
||||
bq[0*bq_stride+0].x1 = F(x01[0]);
|
||||
bq[0*bq_stride+0].x2 = F(x02[0]);
|
||||
bq[1*bq_stride+0].x1 = F(x01[1]);
|
||||
bq[1*bq_stride+0].x2 = F(x02[1]);
|
||||
bq[2*bq_stride+0].x1 = F(x01[2]);
|
||||
bq[2*bq_stride+0].x2 = F(x02[2]);
|
||||
bq[3*bq_stride+0].x1 = F(x01[3]);
|
||||
bq[3*bq_stride+0].x2 = F(x02[3]);
|
||||
|
||||
bq[0*bq_stride+1].x1 = F(x11[0]);
|
||||
bq[0*bq_stride+1].x2 = F(x12[0]);
|
||||
bq[1*bq_stride+1].x1 = F(x11[1]);
|
||||
bq[1*bq_stride+1].x2 = F(x12[1]);
|
||||
bq[2*bq_stride+1].x1 = F(x11[2]);
|
||||
bq[2*bq_stride+1].x2 = F(x12[2]);
|
||||
bq[3*bq_stride+1].x1 = F(x11[3]);
|
||||
bq[3*bq_stride+1].x2 = F(x12[3]);
|
||||
#undef F
|
||||
}
|
||||
|
||||
void dsp_biquad_run_sse(void *obj, struct biquad *bq, uint32_t n_bq, uint32_t bq_stride,
|
||||
float * SPA_RESTRICT out[], const float * SPA_RESTRICT in[],
|
||||
uint32_t n_src, uint32_t n_samples)
|
||||
{
|
||||
uint32_t i, j, bqs2 = bq_stride*2, bqs4 = bqs2*2;
|
||||
uint32_t iunrolled4 = n_src & ~3;
|
||||
uint32_t iunrolled2 = n_src & ~1;
|
||||
uint32_t junrolled2 = n_bq & ~1;
|
||||
|
||||
for (i = 0; i < iunrolled4; i+=4, bq+=bqs4) {
|
||||
const float *s[4] = { in[i], in[i+1], in[i+2], in[i+3] };
|
||||
float *d[4] = { out[i], out[i+1], out[i+2], out[i+3] };
|
||||
|
||||
if (s[0] == NULL || s[1] == NULL || s[2] == NULL || s[3] == NULL ||
|
||||
d[0] == NULL || d[1] == NULL || d[2] == NULL || d[3] == NULL)
|
||||
break;
|
||||
|
||||
j = 0;
|
||||
if (j < junrolled2) {
|
||||
dsp_biquad2_run4_sse(obj, &bq[j], bq_stride, d, s, n_samples);
|
||||
s[0] = d[0];
|
||||
s[1] = d[1];
|
||||
s[2] = d[2];
|
||||
s[3] = d[3];
|
||||
j+=2;
|
||||
}
|
||||
for (; j < junrolled2; j+=2) {
|
||||
dsp_biquad2_run4_sse(obj, &bq[j], bq_stride, d, s, n_samples);
|
||||
}
|
||||
if (j < n_bq) {
|
||||
dsp_biquad_run4_sse(obj, &bq[j], bq_stride, d, s, n_samples);
|
||||
}
|
||||
}
|
||||
for (; i < iunrolled2; i+=2, bq+=bqs2) {
|
||||
const float *s[2] = { in[i], in[i+1] };
|
||||
float *d[2] = { out[i], out[i+1] };
|
||||
|
||||
if (s[0] == NULL || s[1] == NULL || d[0] == NULL || d[1] == NULL)
|
||||
break;
|
||||
|
||||
j = 0;
|
||||
if (j < junrolled2) {
|
||||
dsp_biquad2_run2_sse(obj, &bq[j], bq_stride, d, s, n_samples);
|
||||
s[0] = d[0];
|
||||
s[1] = d[1];
|
||||
j+=2;
|
||||
}
|
||||
for (; j < junrolled2; j+=2) {
|
||||
dsp_biquad2_run2_sse(obj, &bq[j], bq_stride, d, s, n_samples);
|
||||
}
|
||||
if (j < n_bq) {
|
||||
dsp_biquad_run2_sse(obj, &bq[j], bq_stride, d, s, n_samples);
|
||||
}
|
||||
}
|
||||
for (; i < n_src; i++, bq+=bq_stride) {
|
||||
const float *s = in[i];
|
||||
float *d = out[i];
|
||||
if (s == NULL || d == NULL)
|
||||
continue;
|
||||
|
||||
j = 0;
|
||||
if (j < junrolled2) {
|
||||
dsp_biquad2_run_sse(obj, &bq[j], d, s, n_samples);
|
||||
s = d;
|
||||
j+=2;
|
||||
}
|
||||
for (; j < junrolled2; j+=2) {
|
||||
dsp_biquad2_run_sse(obj, &bq[j], d, s, n_samples);
|
||||
}
|
||||
if (j < n_bq) {
|
||||
dsp_biquad_run1_sse(obj, &bq[j], d, s, n_samples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dsp_delay_sse(void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer, uint32_t delay,
|
||||
float *dst, const float *src, uint32_t n_samples)
|
||||
{
|
||||
__m128 t[1];
|
||||
uint32_t w = *pos;
|
||||
uint32_t o = n_buffer - delay;
|
||||
uint32_t n, unrolled;
|
||||
|
||||
if (SPA_IS_ALIGNED(src, 16) &&
|
||||
SPA_IS_ALIGNED(dst, 16))
|
||||
unrolled = n_samples & ~3;
|
||||
else
|
||||
unrolled = 0;
|
||||
|
||||
for(n = 0; n < unrolled; n += 4) {
|
||||
t[0] = _mm_load_ps(&src[n]);
|
||||
_mm_storeu_ps(&buffer[w], t[0]);
|
||||
_mm_storeu_ps(&buffer[w+n_buffer], t[0]);
|
||||
t[0] = _mm_loadu_ps(&buffer[w+o]);
|
||||
_mm_store_ps(&dst[n], t[0]);
|
||||
w = w + 4 >= n_buffer ? 0 : w + 4;
|
||||
}
|
||||
for(; n < n_samples; n++) {
|
||||
t[0] = _mm_load_ss(&src[n]);
|
||||
_mm_store_ss(&buffer[w], t[0]);
|
||||
_mm_store_ss(&buffer[w+n_buffer], t[0]);
|
||||
t[0] = _mm_load_ss(&buffer[w+o]);
|
||||
_mm_store_ss(&dst[n], t[0]);
|
||||
w = w + 1 >= n_buffer ? 0 : w + 1;
|
||||
}
|
||||
*pos = w;
|
||||
}
|
||||
|
||||
inline static void _mm_mul_pz(__m128 *a, __m128 *b, __m128 *d)
|
||||
{
|
||||
__m128 ar, ai, br, bi, arbr, arbi, aibi, aibr, dr, di;
|
||||
ar = _mm_shuffle_ps(a[0], a[1], _MM_SHUFFLE(2,0,2,0)); /* ar0 ar1 ar2 ar3 */
|
||||
ai = _mm_shuffle_ps(a[0], a[1], _MM_SHUFFLE(3,1,3,1)); /* ai0 ai1 ai2 ai3 */
|
||||
br = _mm_shuffle_ps(b[0], b[1], _MM_SHUFFLE(2,0,2,0)); /* br0 br1 br2 br3 */
|
||||
bi = _mm_shuffle_ps(b[0], b[1], _MM_SHUFFLE(3,1,3,1)) /* bi0 bi1 bi2 bi3 */;
|
||||
|
||||
arbr = _mm_mul_ps(ar, br); /* ar * br */
|
||||
arbi = _mm_mul_ps(ar, bi); /* ar * bi */
|
||||
|
||||
aibi = _mm_mul_ps(ai, bi); /* ai * bi */
|
||||
aibr = _mm_mul_ps(ai, br); /* ai * br */
|
||||
|
||||
dr = _mm_sub_ps(arbr, aibi); /* ar * br - ai * bi */
|
||||
di = _mm_add_ps(arbi, aibr); /* ar * bi + ai * br */
|
||||
d[0] = _mm_unpacklo_ps(dr, di);
|
||||
d[1] = _mm_unpackhi_ps(dr, di);
|
||||
}
|
||||
|
||||
void dsp_fft_cmul_sse(void *obj, void *fft,
|
||||
float * SPA_RESTRICT dst, const float * SPA_RESTRICT a,
|
||||
const float * SPA_RESTRICT b, uint32_t len, const float scale)
|
||||
{
|
||||
#ifdef HAVE_FFTW
|
||||
__m128 s = _mm_set1_ps(scale);
|
||||
__m128 aa[2], bb[2], dd[2];
|
||||
uint32_t i, unrolled;
|
||||
|
||||
if (SPA_IS_ALIGNED(a, 16) &&
|
||||
SPA_IS_ALIGNED(b, 16) &&
|
||||
SPA_IS_ALIGNED(dst, 16))
|
||||
unrolled = len & ~3;
|
||||
else
|
||||
unrolled = 0;
|
||||
|
||||
for (i = 0; i < unrolled; i+=4) {
|
||||
aa[0] = _mm_load_ps(&a[2*i]); /* ar0 ai0 ar1 ai1 */
|
||||
aa[1] = _mm_load_ps(&a[2*i+4]); /* ar1 ai1 ar2 ai2 */
|
||||
bb[0] = _mm_load_ps(&b[2*i]); /* br0 bi0 br1 bi1 */
|
||||
bb[1] = _mm_load_ps(&b[2*i+4]); /* br2 bi2 br3 bi3 */
|
||||
_mm_mul_pz(aa, bb, dd);
|
||||
dd[0] = _mm_mul_ps(dd[0], s);
|
||||
dd[1] = _mm_mul_ps(dd[1], s);
|
||||
_mm_store_ps(&dst[2*i], dd[0]);
|
||||
_mm_store_ps(&dst[2*i+4], dd[1]);
|
||||
}
|
||||
for (; i < len; i++) {
|
||||
dst[2*i ] = (a[2*i] * b[2*i ] - a[2*i+1] * b[2*i+1]) * scale;
|
||||
dst[2*i+1] = (a[2*i] * b[2*i+1] + a[2*i+1] * b[2*i ]) * scale;
|
||||
}
|
||||
#else
|
||||
pffft_zconvolve(fft, a, b, dst, scale);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dsp_fft_cmuladd_sse(void *obj, void *fft,
|
||||
float * SPA_RESTRICT dst, const float * SPA_RESTRICT src,
|
||||
const float * SPA_RESTRICT a, const float * SPA_RESTRICT b,
|
||||
uint32_t len, const float scale)
|
||||
{
|
||||
#ifdef HAVE_FFTW
|
||||
__m128 s = _mm_set1_ps(scale);
|
||||
__m128 aa[2], bb[2], dd[2], t[2];
|
||||
uint32_t i, unrolled;
|
||||
|
||||
if (SPA_IS_ALIGNED(a, 16) &&
|
||||
SPA_IS_ALIGNED(b, 16) &&
|
||||
SPA_IS_ALIGNED(src, 16) &&
|
||||
SPA_IS_ALIGNED(dst, 16))
|
||||
unrolled = len & ~3;
|
||||
else
|
||||
unrolled = 0;
|
||||
|
||||
for (i = 0; i < unrolled; i+=4) {
|
||||
aa[0] = _mm_load_ps(&a[2*i]); /* ar0 ai0 ar1 ai1 */
|
||||
aa[1] = _mm_load_ps(&a[2*i+4]); /* ar1 ai1 ar2 ai2 */
|
||||
bb[0] = _mm_load_ps(&b[2*i]); /* br0 bi0 br1 bi1 */
|
||||
bb[1] = _mm_load_ps(&b[2*i+4]); /* br2 bi2 br3 bi3 */
|
||||
_mm_mul_pz(aa, bb, dd);
|
||||
dd[0] = _mm_mul_ps(dd[0], s);
|
||||
dd[1] = _mm_mul_ps(dd[1], s);
|
||||
t[0] = _mm_load_ps(&src[2*i]);
|
||||
t[1] = _mm_load_ps(&src[2*i+4]);
|
||||
t[0] = _mm_add_ps(t[0], dd[0]);
|
||||
t[1] = _mm_add_ps(t[1], dd[1]);
|
||||
_mm_store_ps(&dst[2*i], t[0]);
|
||||
_mm_store_ps(&dst[2*i+4], t[1]);
|
||||
}
|
||||
for (; i < len; i++) {
|
||||
dst[2*i ] = src[2*i ] + (a[2*i] * b[2*i ] - a[2*i+1] * b[2*i+1]) * scale;
|
||||
dst[2*i+1] = src[2*i+1] + (a[2*i] * b[2*i+1] + a[2*i+1] * b[2*i ]) * scale;
|
||||
}
|
||||
#else
|
||||
pffft_zconvolve_accumulate(fft, a, b, src, dst, scale);
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
/* Spa */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <spa/support/cpu.h>
|
||||
#include <spa/utils/defs.h>
|
||||
#include <spa/param/audio/format-utils.h>
|
||||
|
||||
#include "pffft.h"
|
||||
|
||||
#include "audio-dsp-impl.h"
|
||||
|
||||
struct dsp_info {
|
||||
uint32_t cpu_flags;
|
||||
|
||||
struct spa_fga_dsp_methods funcs;
|
||||
};
|
||||
|
||||
static const struct dsp_info dsp_table[] =
|
||||
{
|
||||
#if defined (HAVE_AVX)
|
||||
{ SPA_CPU_FLAG_AVX,
|
||||
.funcs.clear = dsp_clear_c,
|
||||
.funcs.copy = dsp_copy_c,
|
||||
.funcs.mix_gain = dsp_mix_gain_avx,
|
||||
.funcs.biquad_run = dsp_biquad_run_sse,
|
||||
.funcs.sum = dsp_sum_avx,
|
||||
.funcs.linear = dsp_linear_c,
|
||||
.funcs.mult = dsp_mult_c,
|
||||
.funcs.fft_new = dsp_fft_new_c,
|
||||
.funcs.fft_free = dsp_fft_free_c,
|
||||
.funcs.fft_memalloc = dsp_fft_memalloc_c,
|
||||
.funcs.fft_memfree = dsp_fft_memfree_c,
|
||||
.funcs.fft_memclear = dsp_fft_memclear_c,
|
||||
.funcs.fft_run = dsp_fft_run_c,
|
||||
.funcs.fft_cmul = dsp_fft_cmul_avx,
|
||||
.funcs.fft_cmuladd = dsp_fft_cmuladd_avx,
|
||||
.funcs.delay = dsp_delay_sse,
|
||||
},
|
||||
#endif
|
||||
#if defined (HAVE_SSE)
|
||||
{ SPA_CPU_FLAG_SSE,
|
||||
.funcs.clear = dsp_clear_c,
|
||||
.funcs.copy = dsp_copy_c,
|
||||
.funcs.mix_gain = dsp_mix_gain_sse,
|
||||
.funcs.biquad_run = dsp_biquad_run_sse,
|
||||
.funcs.sum = dsp_sum_sse,
|
||||
.funcs.linear = dsp_linear_c,
|
||||
.funcs.mult = dsp_mult_c,
|
||||
.funcs.fft_new = dsp_fft_new_c,
|
||||
.funcs.fft_free = dsp_fft_free_c,
|
||||
.funcs.fft_memalloc = dsp_fft_memalloc_c,
|
||||
.funcs.fft_memfree = dsp_fft_memfree_c,
|
||||
.funcs.fft_memclear = dsp_fft_memclear_c,
|
||||
.funcs.fft_run = dsp_fft_run_c,
|
||||
.funcs.fft_cmul = dsp_fft_cmul_sse,
|
||||
.funcs.fft_cmuladd = dsp_fft_cmuladd_sse,
|
||||
.funcs.delay = dsp_delay_sse,
|
||||
},
|
||||
#endif
|
||||
{ 0,
|
||||
.funcs.clear = dsp_clear_c,
|
||||
.funcs.copy = dsp_copy_c,
|
||||
.funcs.mix_gain = dsp_mix_gain_c,
|
||||
.funcs.biquad_run = dsp_biquad_run_c,
|
||||
.funcs.sum = dsp_sum_c,
|
||||
.funcs.linear = dsp_linear_c,
|
||||
.funcs.mult = dsp_mult_c,
|
||||
.funcs.fft_new = dsp_fft_new_c,
|
||||
.funcs.fft_free = dsp_fft_free_c,
|
||||
.funcs.fft_memalloc = dsp_fft_memalloc_c,
|
||||
.funcs.fft_memfree = dsp_fft_memfree_c,
|
||||
.funcs.fft_memclear = dsp_fft_memclear_c,
|
||||
.funcs.fft_run = dsp_fft_run_c,
|
||||
.funcs.fft_cmul = dsp_fft_cmul_c,
|
||||
.funcs.fft_cmuladd = dsp_fft_cmuladd_c,
|
||||
.funcs.delay = dsp_delay_c,
|
||||
},
|
||||
};
|
||||
|
||||
#define MATCH_CPU_FLAGS(a,b) ((a) == 0 || ((a) & (b)) == a)
|
||||
|
||||
static const struct dsp_info *find_dsp_info(uint32_t cpu_flags)
|
||||
{
|
||||
SPA_FOR_EACH_ELEMENT_VAR(dsp_table, t) {
|
||||
if (MATCH_CPU_FLAGS(t->cpu_flags, cpu_flags))
|
||||
return t;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void spa_fga_dsp_free(struct spa_fga_dsp *dsp)
|
||||
{
|
||||
free(dsp);
|
||||
}
|
||||
|
||||
struct spa_fga_dsp * spa_fga_dsp_new(uint32_t cpu_flags)
|
||||
{
|
||||
const struct dsp_info *info;
|
||||
struct spa_fga_dsp *dsp;
|
||||
|
||||
info = find_dsp_info(cpu_flags);
|
||||
if (info == NULL) {
|
||||
errno = ENOTSUP;
|
||||
return NULL;
|
||||
}
|
||||
dsp = calloc(1, sizeof(*dsp));
|
||||
if (dsp == NULL)
|
||||
return NULL;
|
||||
|
||||
pffft_select_cpu(cpu_flags);
|
||||
dsp->cpu_flags = cpu_flags;
|
||||
dsp->iface = SPA_INTERFACE_INIT(
|
||||
SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioDSP,
|
||||
SPA_VERSION_FGA_DSP,
|
||||
&info->funcs, dsp);
|
||||
|
||||
return dsp;
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
/* Spa */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef SPA_FGA_DSP_H
|
||||
#define SPA_FGA_DSP_H
|
||||
|
||||
#include <spa/utils/defs.h>
|
||||
#include <spa/utils/hook.h>
|
||||
|
||||
#include "biquad.h"
|
||||
|
||||
#define SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioDSP SPA_TYPE_INFO_INTERFACE_BASE "FilterGraph:AudioDSP"
|
||||
|
||||
#define SPA_VERSION_FGA_DSP 0
|
||||
struct spa_fga_dsp {
|
||||
struct spa_interface iface;
|
||||
uint32_t cpu_flags;
|
||||
};
|
||||
|
||||
struct spa_fga_dsp_methods {
|
||||
#define SPA_VERSION_FGA_DSP_METHODS 0
|
||||
uint32_t version;
|
||||
|
||||
void (*clear) (void *obj, void * SPA_RESTRICT dst, uint32_t n_samples);
|
||||
void (*copy) (void *obj,
|
||||
void * SPA_RESTRICT dst,
|
||||
const void * SPA_RESTRICT src, uint32_t n_samples);
|
||||
void (*mix_gain) (void *obj,
|
||||
void * SPA_RESTRICT dst,
|
||||
const void * SPA_RESTRICT src[],
|
||||
float gain[], uint32_t n_src, uint32_t n_samples);
|
||||
void (*sum) (void *obj,
|
||||
float * dst, const float * SPA_RESTRICT a,
|
||||
const float * SPA_RESTRICT b, uint32_t n_samples);
|
||||
|
||||
void *(*fft_new) (void *obj, uint32_t size, bool real);
|
||||
void (*fft_free) (void *obj, void *fft);
|
||||
void *(*fft_memalloc) (void *obj, uint32_t size, bool real);
|
||||
void (*fft_memfree) (void *obj, void *mem);
|
||||
void (*fft_memclear) (void *obj, void *mem, uint32_t size, bool real);
|
||||
void (*fft_run) (void *obj, void *fft, int direction,
|
||||
const float * SPA_RESTRICT src, float * SPA_RESTRICT dst);
|
||||
void (*fft_cmul) (void *obj, void *fft,
|
||||
float * SPA_RESTRICT dst, const float * SPA_RESTRICT a,
|
||||
const float * SPA_RESTRICT b, uint32_t len, const float scale);
|
||||
void (*fft_cmuladd) (void *obj, void *fft,
|
||||
float * dst, const float * src,
|
||||
const float * SPA_RESTRICT a, const float * SPA_RESTRICT b,
|
||||
uint32_t len, const float scale);
|
||||
void (*linear) (void *obj,
|
||||
float * dst, const float * SPA_RESTRICT src,
|
||||
const float mult, const float add, uint32_t n_samples);
|
||||
void (*mult) (void *obj,
|
||||
void * SPA_RESTRICT dst,
|
||||
const void * SPA_RESTRICT src[], uint32_t n_src, uint32_t n_samples);
|
||||
void (*biquad_run) (void *obj, struct biquad *bq, uint32_t n_bq, uint32_t bq_stride,
|
||||
float * SPA_RESTRICT out[], const float * SPA_RESTRICT in[],
|
||||
uint32_t n_src, uint32_t n_samples);
|
||||
void (*delay) (void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer, uint32_t delay,
|
||||
float *dst, const float *src, uint32_t n_samples);
|
||||
};
|
||||
|
||||
#define spa_fga_dsp_method_r(o,type,method,version,...) \
|
||||
({ \
|
||||
type _res = NULL; \
|
||||
struct spa_fga_dsp *_o = o; \
|
||||
spa_interface_call_fast_res(&_o->iface, \
|
||||
struct spa_fga_dsp_methods, _res, \
|
||||
method, version, ##__VA_ARGS__); \
|
||||
_res; \
|
||||
})
|
||||
|
||||
|
||||
#define spa_fga_dsp_method(o,method,version,...) \
|
||||
({ \
|
||||
struct spa_fga_dsp *_o = o; \
|
||||
spa_interface_call_fast(&_o->iface, \
|
||||
struct spa_fga_dsp_methods, \
|
||||
method, version, ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
|
||||
#define spa_fga_dsp_clear(o,...) spa_fga_dsp_method(o,clear,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_copy(o,...) spa_fga_dsp_method(o,copy,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_mix_gain(o,...) spa_fga_dsp_method(o,mix_gain,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_biquad_run(o,...) spa_fga_dsp_method(o,biquad_run,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_sum(o,...) spa_fga_dsp_method(o,sum,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_linear(o,...) spa_fga_dsp_method(o,linear,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_mult(o,...) spa_fga_dsp_method(o,mult,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_delay(o,...) spa_fga_dsp_method(o,delay,0,__VA_ARGS__)
|
||||
|
||||
#define spa_fga_dsp_fft_new(o,...) spa_fga_dsp_method_r(o,void*,fft_new,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_fft_free(o,...) spa_fga_dsp_method(o,fft_free,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_fft_memalloc(o,...) spa_fga_dsp_method_r(o,void*,fft_memalloc,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_fft_memfree(o,...) spa_fga_dsp_method(o,fft_memfree,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_fft_memclear(o,...) spa_fga_dsp_method(o,fft_memclear,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_fft_run(o,...) spa_fga_dsp_method(o,fft_run,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_fft_cmul(o,...) spa_fga_dsp_method(o,fft_cmul,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_fft_cmul(o,...) spa_fga_dsp_method(o,fft_cmul,0,__VA_ARGS__)
|
||||
#define spa_fga_dsp_fft_cmuladd(o,...) spa_fga_dsp_method(o,fft_cmuladd,0,__VA_ARGS__)
|
||||
|
||||
#endif /* SPA_FGA_DSP_H */
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
/* PipeWire */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef SPA_FGA_PLUGIN_H
|
||||
#define SPA_FGA_PLUGIN_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <spa/utils/defs.h>
|
||||
#include <spa/utils/hook.h>
|
||||
#include <spa/support/plugin.h>
|
||||
|
||||
#define SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin SPA_TYPE_INFO_INTERFACE_BASE "FilterGraph:AudioPlugin"
|
||||
|
||||
#define SPA_VERSION_FGA_PLUGIN 0
|
||||
struct spa_fga_plugin { struct spa_interface iface; };
|
||||
|
||||
struct spa_fga_plugin_methods {
|
||||
#define SPA_VERSION_FGA_PLUGIN_METHODS 0
|
||||
uint32_t version;
|
||||
|
||||
const struct spa_fga_descriptor *(*make_desc) (void *plugin, const char *name);
|
||||
};
|
||||
|
||||
struct spa_fga_port {
|
||||
uint32_t index;
|
||||
const char *name;
|
||||
#define SPA_FGA_PORT_INPUT (1ULL << 0)
|
||||
#define SPA_FGA_PORT_OUTPUT (1ULL << 1)
|
||||
#define SPA_FGA_PORT_CONTROL (1ULL << 2)
|
||||
#define SPA_FGA_PORT_AUDIO (1ULL << 3)
|
||||
uint64_t flags;
|
||||
|
||||
#define SPA_FGA_HINT_BOOLEAN (1ULL << 2)
|
||||
#define SPA_FGA_HINT_SAMPLE_RATE (1ULL << 3)
|
||||
#define SPA_FGA_HINT_INTEGER (1ULL << 5)
|
||||
uint64_t hint;
|
||||
float def;
|
||||
float min;
|
||||
float max;
|
||||
};
|
||||
|
||||
#define SPA_FGA_IS_PORT_INPUT(x) ((x) & SPA_FGA_PORT_INPUT)
|
||||
#define SPA_FGA_IS_PORT_OUTPUT(x) ((x) & SPA_FGA_PORT_OUTPUT)
|
||||
#define SPA_FGA_IS_PORT_CONTROL(x) ((x) & SPA_FGA_PORT_CONTROL)
|
||||
#define SPA_FGA_IS_PORT_AUDIO(x) ((x) & SPA_FGA_PORT_AUDIO)
|
||||
|
||||
struct spa_fga_descriptor {
|
||||
const char *name;
|
||||
#define SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA (1ULL << 0)
|
||||
#define SPA_FGA_DESCRIPTOR_COPY (1ULL << 1)
|
||||
uint64_t flags;
|
||||
|
||||
void (*free) (const struct spa_fga_descriptor *desc);
|
||||
|
||||
uint32_t n_ports;
|
||||
struct spa_fga_port *ports;
|
||||
|
||||
void *(*instantiate) (const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor *desc,
|
||||
unsigned long SampleRate, int index, const char *config);
|
||||
|
||||
void (*cleanup) (void *instance);
|
||||
|
||||
void (*connect_port) (void *instance, unsigned long port, float *data);
|
||||
void (*control_changed) (void *instance);
|
||||
|
||||
void (*activate) (void *instance);
|
||||
void (*deactivate) (void *instance);
|
||||
|
||||
void (*run) (void *instance, unsigned long SampleCount);
|
||||
};
|
||||
|
||||
static inline void spa_fga_descriptor_free(const struct spa_fga_descriptor *desc)
|
||||
{
|
||||
if (desc->free)
|
||||
desc->free(desc);
|
||||
}
|
||||
|
||||
#define spa_fga_plugin_method_r(o,method,version,...) \
|
||||
({ \
|
||||
const void * _res = NULL; \
|
||||
struct spa_fga_plugin *_o = o; \
|
||||
spa_interface_call_fast_res(&_o->iface, \
|
||||
struct spa_fga_plugin_methods, _res, \
|
||||
method, version, ##__VA_ARGS__); \
|
||||
_res; \
|
||||
})
|
||||
|
||||
#define spa_fga_plugin_method(o,method,version,...) \
|
||||
({ \
|
||||
struct spa_fga_plugin *_o = o; \
|
||||
spa_interface_call_fast(&_o->iface, \
|
||||
struct spa_fga_plugin_methods, \
|
||||
method, version, ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
|
||||
#define spa_fga_plugin_make_desc(o,...) spa_fga_plugin_method_r(o,make_desc,0,__VA_ARGS__)
|
||||
|
||||
typedef struct spa_fga_plugin *(spa_filter_graph_audio_plugin_load_func_t)(const struct spa_support *support,
|
||||
uint32_t n_support, const char *path, const struct spa_dict *info);
|
||||
|
||||
#define SPA_FILTER_GRAPH_AUDIO_PLUGIN_LOAD_FUNC_NAME "spa_filter_graph_audio_plugin_load"
|
||||
|
||||
|
||||
|
||||
#endif /* PLUGIN_H */
|
||||
|
|
@ -1,374 +0,0 @@
|
|||
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2010 Google Inc. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE.WEBKIT file.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "biquad.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
/* S = 1 in Q */
|
||||
#define BIQUAD_SHELVING_DEFAULT_Q 0.707106781186548
|
||||
|
||||
static void set_coefficient(struct biquad *bq, double b0, double b1, double b2,
|
||||
double a0, double a1, double a2)
|
||||
{
|
||||
double a0_inv = 1 / a0;
|
||||
bq->b0 = (float)(b0 * a0_inv);
|
||||
bq->b1 = (float)(b1 * a0_inv);
|
||||
bq->b2 = (float)(b2 * a0_inv);
|
||||
bq->a1 = (float)(a1 * a0_inv);
|
||||
bq->a2 = (float)(a2 * a0_inv);
|
||||
}
|
||||
|
||||
static void biquad_lowpass(struct biquad *bq, double cutoff, double resonance)
|
||||
{
|
||||
/* Limit cutoff to 0 to 1. */
|
||||
cutoff = fmax(0.0, fmin(cutoff, 1.0));
|
||||
|
||||
if (cutoff == 1 || cutoff == 0) {
|
||||
/* When cutoff is 1, the z-transform is 1.
|
||||
* When cutoff is zero, nothing gets through the filter, so set
|
||||
* coefficients up correctly.
|
||||
*/
|
||||
set_coefficient(bq, cutoff, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compute biquad coefficients for lowpass filter */
|
||||
resonance = fmax(0.0, resonance); /* can't go negative */
|
||||
double g = pow(10.0, 0.05 * resonance);
|
||||
double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2);
|
||||
|
||||
double theta = M_PI * cutoff;
|
||||
double sn = 0.5 * d * sin(theta);
|
||||
double beta = 0.5 * (1 - sn) / (1 + sn);
|
||||
double gamma = (0.5 + beta) * cos(theta);
|
||||
double alpha = 0.25 * (0.5 + beta - gamma);
|
||||
|
||||
double b0 = 2 * alpha;
|
||||
double b1 = 2 * 2 * alpha;
|
||||
double b2 = 2 * alpha;
|
||||
double a1 = 2 * -gamma;
|
||||
double a2 = 2 * beta;
|
||||
|
||||
set_coefficient(bq, b0, b1, b2, 1, a1, a2);
|
||||
}
|
||||
|
||||
static void biquad_highpass(struct biquad *bq, double cutoff, double resonance)
|
||||
{
|
||||
/* Limit cutoff to 0 to 1. */
|
||||
cutoff = fmax(0.0, fmin(cutoff, 1.0));
|
||||
|
||||
if (cutoff == 1 || cutoff == 0) {
|
||||
/* When cutoff is one, the z-transform is 0. */
|
||||
/* When cutoff is zero, we need to be careful because the above
|
||||
* gives a quadratic divided by the same quadratic, with poles
|
||||
* and zeros on the unit circle in the same place. When cutoff
|
||||
* is zero, the z-transform is 1.
|
||||
*/
|
||||
set_coefficient(bq, 1 - cutoff, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compute biquad coefficients for highpass filter */
|
||||
resonance = fmax(0.0, resonance); /* can't go negative */
|
||||
double g = pow(10.0, 0.05 * resonance);
|
||||
double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2);
|
||||
|
||||
double theta = M_PI * cutoff;
|
||||
double sn = 0.5 * d * sin(theta);
|
||||
double beta = 0.5 * (1 - sn) / (1 + sn);
|
||||
double gamma = (0.5 + beta) * cos(theta);
|
||||
double alpha = 0.25 * (0.5 + beta + gamma);
|
||||
|
||||
double b0 = 2 * alpha;
|
||||
double b1 = 2 * -2 * alpha;
|
||||
double b2 = 2 * alpha;
|
||||
double a1 = 2 * -gamma;
|
||||
double a2 = 2 * beta;
|
||||
|
||||
set_coefficient(bq, b0, b1, b2, 1, a1, a2);
|
||||
}
|
||||
|
||||
static void biquad_bandpass(struct biquad *bq, double frequency, double Q)
|
||||
{
|
||||
/* No negative frequencies allowed. */
|
||||
frequency = fmax(0.0, frequency);
|
||||
|
||||
/* Don't let Q go negative, which causes an unstable filter. */
|
||||
Q = fmax(0.0, Q);
|
||||
|
||||
if (frequency <= 0 || frequency >= 1) {
|
||||
/* When the cutoff is zero, the z-transform approaches 0, if Q
|
||||
* > 0. When both Q and cutoff are zero, the z-transform is
|
||||
* pretty much undefined. What should we do in this case?
|
||||
* For now, just make the filter 0. When the cutoff is 1, the
|
||||
* z-transform also approaches 0.
|
||||
*/
|
||||
set_coefficient(bq, 0, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (Q <= 0) {
|
||||
/* When Q = 0, the above formulas have problems. If we
|
||||
* look at the z-transform, we can see that the limit
|
||||
* as Q->0 is 1, so set the filter that way.
|
||||
*/
|
||||
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
double w0 = M_PI * frequency;
|
||||
double alpha = sin(w0) / (2 * Q);
|
||||
double k = cos(w0);
|
||||
|
||||
double b0 = alpha;
|
||||
double b1 = 0;
|
||||
double b2 = -alpha;
|
||||
double a0 = 1 + alpha;
|
||||
double a1 = -2 * k;
|
||||
double a2 = 1 - alpha;
|
||||
|
||||
set_coefficient(bq, b0, b1, b2, a0, a1, a2);
|
||||
}
|
||||
|
||||
static void biquad_lowshelf(struct biquad *bq, double frequency, double Q,
|
||||
double db_gain)
|
||||
{
|
||||
/* Clip frequencies to between 0 and 1, inclusive. */
|
||||
frequency = fmax(0.0, fmin(frequency, 1.0));
|
||||
|
||||
double A = pow(10.0, db_gain / 40);
|
||||
|
||||
if (frequency == 1) {
|
||||
/* The z-transform is a constant gain. */
|
||||
set_coefficient(bq, A * A, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (frequency <= 0) {
|
||||
/* When frequency is 0, the z-transform is 1. */
|
||||
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set Q to an equivalent value to S = 1 if not specified */
|
||||
if (Q <= 0)
|
||||
Q = BIQUAD_SHELVING_DEFAULT_Q;
|
||||
|
||||
double w0 = M_PI * frequency;
|
||||
double alpha = sin(w0) / (2 * Q);
|
||||
double k = cos(w0);
|
||||
double k2 = 2 * sqrt(A) * alpha;
|
||||
double a_plus_one = A + 1;
|
||||
double a_minus_one = A - 1;
|
||||
|
||||
double b0 = A * (a_plus_one - a_minus_one * k + k2);
|
||||
double b1 = 2 * A * (a_minus_one - a_plus_one * k);
|
||||
double b2 = A * (a_plus_one - a_minus_one * k - k2);
|
||||
double a0 = a_plus_one + a_minus_one * k + k2;
|
||||
double a1 = -2 * (a_minus_one + a_plus_one * k);
|
||||
double a2 = a_plus_one + a_minus_one * k - k2;
|
||||
|
||||
set_coefficient(bq, b0, b1, b2, a0, a1, a2);
|
||||
}
|
||||
|
||||
static void biquad_highshelf(struct biquad *bq, double frequency, double Q,
|
||||
double db_gain)
|
||||
{
|
||||
/* Clip frequencies to between 0 and 1, inclusive. */
|
||||
frequency = fmax(0.0, fmin(frequency, 1.0));
|
||||
|
||||
double A = pow(10.0, db_gain / 40);
|
||||
|
||||
if (frequency == 1) {
|
||||
/* The z-transform is 1. */
|
||||
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (frequency <= 0) {
|
||||
/* When frequency = 0, the filter is just a gain, A^2. */
|
||||
set_coefficient(bq, A * A, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set Q to an equivalent value to S = 1 if not specified */
|
||||
if (Q <= 0)
|
||||
Q = BIQUAD_SHELVING_DEFAULT_Q;
|
||||
|
||||
double w0 = M_PI * frequency;
|
||||
double alpha = sin(w0) / (2 * Q);
|
||||
double k = cos(w0);
|
||||
double k2 = 2 * sqrt(A) * alpha;
|
||||
double a_plus_one = A + 1;
|
||||
double a_minus_one = A - 1;
|
||||
|
||||
double b0 = A * (a_plus_one + a_minus_one * k + k2);
|
||||
double b1 = -2 * A * (a_minus_one + a_plus_one * k);
|
||||
double b2 = A * (a_plus_one + a_minus_one * k - k2);
|
||||
double a0 = a_plus_one - a_minus_one * k + k2;
|
||||
double a1 = 2 * (a_minus_one - a_plus_one * k);
|
||||
double a2 = a_plus_one - a_minus_one * k - k2;
|
||||
|
||||
set_coefficient(bq, b0, b1, b2, a0, a1, a2);
|
||||
}
|
||||
|
||||
static void biquad_peaking(struct biquad *bq, double frequency, double Q,
|
||||
double db_gain)
|
||||
{
|
||||
/* Clip frequencies to between 0 and 1, inclusive. */
|
||||
frequency = fmax(0.0, fmin(frequency, 1.0));
|
||||
|
||||
/* Don't let Q go negative, which causes an unstable filter. */
|
||||
Q = fmax(0.0, Q);
|
||||
|
||||
double A = pow(10.0, db_gain / 40);
|
||||
|
||||
if (frequency <= 0 || frequency >= 1) {
|
||||
/* When frequency is 0 or 1, the z-transform is 1. */
|
||||
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (Q <= 0) {
|
||||
/* When Q = 0, the above formulas have problems. If we
|
||||
* look at the z-transform, we can see that the limit
|
||||
* as Q->0 is A^2, so set the filter that way.
|
||||
*/
|
||||
set_coefficient(bq, A * A, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
double w0 = M_PI * frequency;
|
||||
double alpha = sin(w0) / (2 * Q);
|
||||
double k = cos(w0);
|
||||
|
||||
double b0 = 1 + alpha * A;
|
||||
double b1 = -2 * k;
|
||||
double b2 = 1 - alpha * A;
|
||||
double a0 = 1 + alpha / A;
|
||||
double a1 = -2 * k;
|
||||
double a2 = 1 - alpha / A;
|
||||
|
||||
set_coefficient(bq, b0, b1, b2, a0, a1, a2);
|
||||
}
|
||||
|
||||
static void biquad_notch(struct biquad *bq, double frequency, double Q)
|
||||
{
|
||||
/* Clip frequencies to between 0 and 1, inclusive. */
|
||||
frequency = fmax(0.0, fmin(frequency, 1.0));
|
||||
|
||||
/* Don't let Q go negative, which causes an unstable filter. */
|
||||
Q = fmax(0.0, Q);
|
||||
|
||||
if (frequency <= 0 || frequency >= 1) {
|
||||
/* When frequency is 0 or 1, the z-transform is 1. */
|
||||
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (Q <= 0) {
|
||||
/* When Q = 0, the above formulas have problems. If we
|
||||
* look at the z-transform, we can see that the limit
|
||||
* as Q->0 is 0, so set the filter that way.
|
||||
*/
|
||||
set_coefficient(bq, 0, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
double w0 = M_PI * frequency;
|
||||
double alpha = sin(w0) / (2 * Q);
|
||||
double k = cos(w0);
|
||||
|
||||
double b0 = 1;
|
||||
double b1 = -2 * k;
|
||||
double b2 = 1;
|
||||
double a0 = 1 + alpha;
|
||||
double a1 = -2 * k;
|
||||
double a2 = 1 - alpha;
|
||||
|
||||
set_coefficient(bq, b0, b1, b2, a0, a1, a2);
|
||||
}
|
||||
|
||||
static void biquad_allpass(struct biquad *bq, double frequency, double Q)
|
||||
{
|
||||
/* Clip frequencies to between 0 and 1, inclusive. */
|
||||
frequency = fmax(0.0, fmin(frequency, 1.0));
|
||||
|
||||
/* Don't let Q go negative, which causes an unstable filter. */
|
||||
Q = fmax(0.0, Q);
|
||||
|
||||
if (frequency <= 0 || frequency >= 1) {
|
||||
/* When frequency is 0 or 1, the z-transform is 1. */
|
||||
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Q <= 0) {
|
||||
/* When Q = 0, the above formulas have problems. If we
|
||||
* look at the z-transform, we can see that the limit
|
||||
* as Q->0 is -1, so set the filter that way.
|
||||
*/
|
||||
set_coefficient(bq, -1, 0, 0, 1, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
double w0 = M_PI * frequency;
|
||||
double alpha = sin(w0) / (2 * Q);
|
||||
double k = cos(w0);
|
||||
|
||||
double b0 = 1 - alpha;
|
||||
double b1 = -2 * k;
|
||||
double b2 = 1 + alpha;
|
||||
double a0 = 1 + alpha;
|
||||
double a1 = -2 * k;
|
||||
double a2 = 1 - alpha;
|
||||
|
||||
set_coefficient(bq, b0, b1, b2, a0, a1, a2);
|
||||
}
|
||||
|
||||
void biquad_set(struct biquad *bq, enum biquad_type type, double freq, double Q,
|
||||
double gain)
|
||||
{
|
||||
/* Clear history values. */
|
||||
bq->type = type;
|
||||
bq->x1 = 0;
|
||||
bq->x2 = 0;
|
||||
|
||||
switch (type) {
|
||||
case BQ_LOWPASS:
|
||||
biquad_lowpass(bq, freq, Q);
|
||||
break;
|
||||
case BQ_HIGHPASS:
|
||||
biquad_highpass(bq, freq, Q);
|
||||
break;
|
||||
case BQ_BANDPASS:
|
||||
biquad_bandpass(bq, freq, Q);
|
||||
break;
|
||||
case BQ_LOWSHELF:
|
||||
biquad_lowshelf(bq, freq, Q, gain);
|
||||
break;
|
||||
case BQ_HIGHSHELF:
|
||||
biquad_highshelf(bq, freq, Q, gain);
|
||||
break;
|
||||
case BQ_PEAKING:
|
||||
biquad_peaking(bq, freq, Q, gain);
|
||||
break;
|
||||
case BQ_NOTCH:
|
||||
biquad_notch(bq, freq, Q);
|
||||
break;
|
||||
case BQ_ALLPASS:
|
||||
biquad_allpass(bq, freq, Q);
|
||||
break;
|
||||
case BQ_NONE:
|
||||
case BQ_RAW:
|
||||
/* Default is an identity filter. */
|
||||
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef BIQUAD_H_
|
||||
#define BIQUAD_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* The type of the biquad filters */
|
||||
enum biquad_type {
|
||||
BQ_NONE,
|
||||
BQ_LOWPASS,
|
||||
BQ_HIGHPASS,
|
||||
BQ_BANDPASS,
|
||||
BQ_LOWSHELF,
|
||||
BQ_HIGHSHELF,
|
||||
BQ_PEAKING,
|
||||
BQ_NOTCH,
|
||||
BQ_ALLPASS,
|
||||
BQ_RAW,
|
||||
};
|
||||
|
||||
/* The biquad filter parameters. The transfer function H(z) is (b0 + b1 * z^(-1)
|
||||
* + b2 * z^(-2)) / (1 + a1 * z^(-1) + a2 * z^(-2)). The previous two inputs
|
||||
* are stored in x1 and x2, and the previous two outputs are stored in y1 and
|
||||
* y2.
|
||||
*
|
||||
* We use double during the coefficients calculation for better accuracy, but
|
||||
* float is used during the actual filtering for faster computation.
|
||||
*/
|
||||
struct biquad {
|
||||
enum biquad_type type;
|
||||
float b0, b1, b2;
|
||||
float a1, a2;
|
||||
float x1, x2;
|
||||
};
|
||||
|
||||
/* Initialize a biquad filter parameters from its type and parameters.
|
||||
* Args:
|
||||
* bq - The biquad filter we want to set.
|
||||
* type - The type of the biquad filter.
|
||||
* frequency - The value should be in the range [0, 1]. It is relative to
|
||||
* half of the sampling rate.
|
||||
* Q - Quality factor. See Web Audio API for details.
|
||||
* gain - The value is in dB. See Web Audio API for details.
|
||||
*/
|
||||
void biquad_set(struct biquad *bq, enum biquad_type type, double freq, double Q,
|
||||
double gain);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* BIQUAD_H_ */
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,392 +0,0 @@
|
|||
/* PipeWire */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2017 HiFi-LoFi */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/* Adapted from https://github.com/HiFi-LoFi/FFTConvolver */
|
||||
|
||||
#include "convolver.h"
|
||||
|
||||
#include <spa/utils/defs.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
struct convolver1 {
|
||||
struct spa_fga_dsp *dsp;
|
||||
|
||||
int blockSize;
|
||||
int segSize;
|
||||
int segCount;
|
||||
int fftComplexSize;
|
||||
|
||||
float **segments;
|
||||
float **segmentsIr;
|
||||
|
||||
float *fft_buffer;
|
||||
|
||||
void *fft;
|
||||
void *ifft;
|
||||
|
||||
float *pre_mult;
|
||||
float *conv;
|
||||
float *overlap;
|
||||
|
||||
float *inputBuffer;
|
||||
int inputBufferFill;
|
||||
|
||||
int current;
|
||||
float scale;
|
||||
};
|
||||
|
||||
static int next_power_of_two(int val)
|
||||
{
|
||||
int r = 1;
|
||||
while (r < val)
|
||||
r *= 2;
|
||||
return r;
|
||||
}
|
||||
|
||||
static void convolver1_reset(struct convolver1 *conv)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < conv->segCount; i++)
|
||||
spa_fga_dsp_fft_memclear(conv->dsp, conv->segments[i], conv->fftComplexSize, false);
|
||||
spa_fga_dsp_fft_memclear(conv->dsp, conv->overlap, conv->blockSize, true);
|
||||
spa_fga_dsp_fft_memclear(conv->dsp, conv->inputBuffer, conv->segSize, true);
|
||||
spa_fga_dsp_fft_memclear(conv->dsp, conv->pre_mult, conv->fftComplexSize, false);
|
||||
spa_fga_dsp_fft_memclear(conv->dsp, conv->conv, conv->fftComplexSize, false);
|
||||
conv->inputBufferFill = 0;
|
||||
conv->current = 0;
|
||||
}
|
||||
|
||||
static void convolver1_free(struct convolver1 *conv)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < conv->segCount; i++) {
|
||||
if (conv->segments)
|
||||
spa_fga_dsp_fft_memfree(conv->dsp, conv->segments[i]);
|
||||
if (conv->segmentsIr)
|
||||
spa_fga_dsp_fft_memfree(conv->dsp, conv->segmentsIr[i]);
|
||||
}
|
||||
if (conv->fft)
|
||||
spa_fga_dsp_fft_free(conv->dsp, conv->fft);
|
||||
if (conv->ifft)
|
||||
spa_fga_dsp_fft_free(conv->dsp, conv->ifft);
|
||||
if (conv->fft_buffer)
|
||||
spa_fga_dsp_fft_memfree(conv->dsp, conv->fft_buffer);
|
||||
free(conv->segments);
|
||||
free(conv->segmentsIr);
|
||||
spa_fga_dsp_fft_memfree(conv->dsp, conv->pre_mult);
|
||||
spa_fga_dsp_fft_memfree(conv->dsp, conv->conv);
|
||||
spa_fga_dsp_fft_memfree(conv->dsp, conv->overlap);
|
||||
spa_fga_dsp_fft_memfree(conv->dsp, conv->inputBuffer);
|
||||
free(conv);
|
||||
}
|
||||
|
||||
static struct convolver1 *convolver1_new(struct spa_fga_dsp *dsp, int block, const float *ir, int irlen)
|
||||
{
|
||||
struct convolver1 *conv;
|
||||
int i;
|
||||
|
||||
if (block == 0)
|
||||
return NULL;
|
||||
|
||||
while (irlen > 0 && fabs(ir[irlen-1]) < 0.000001f)
|
||||
irlen--;
|
||||
|
||||
conv = calloc(1, sizeof(*conv));
|
||||
if (conv == NULL)
|
||||
return NULL;
|
||||
|
||||
if (irlen == 0)
|
||||
return conv;
|
||||
|
||||
conv->dsp = dsp;
|
||||
conv->blockSize = next_power_of_two(block);
|
||||
conv->segSize = 2 * conv->blockSize;
|
||||
conv->segCount = (irlen + conv->blockSize-1) / conv->blockSize;
|
||||
conv->fftComplexSize = (conv->segSize / 2) + 1;
|
||||
|
||||
conv->fft = spa_fga_dsp_fft_new(conv->dsp, conv->segSize, true);
|
||||
if (conv->fft == NULL)
|
||||
goto error;
|
||||
conv->ifft = spa_fga_dsp_fft_new(conv->dsp, conv->segSize, true);
|
||||
if (conv->ifft == NULL)
|
||||
goto error;
|
||||
|
||||
conv->fft_buffer = spa_fga_dsp_fft_memalloc(conv->dsp, conv->segSize, true);
|
||||
if (conv->fft_buffer == NULL)
|
||||
goto error;
|
||||
|
||||
conv->segments = calloc(conv->segCount, sizeof(float*));
|
||||
conv->segmentsIr = calloc(conv->segCount, sizeof(float*));
|
||||
if (conv->segments == NULL || conv->segmentsIr == NULL)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < conv->segCount; i++) {
|
||||
int left = irlen - (i * conv->blockSize);
|
||||
int copy = SPA_MIN(conv->blockSize, left);
|
||||
|
||||
conv->segments[i] = spa_fga_dsp_fft_memalloc(conv->dsp, conv->fftComplexSize, false);
|
||||
conv->segmentsIr[i] = spa_fga_dsp_fft_memalloc(conv->dsp, conv->fftComplexSize, false);
|
||||
if (conv->segments[i] == NULL || conv->segmentsIr[i] == NULL)
|
||||
goto error;
|
||||
|
||||
spa_fga_dsp_copy(conv->dsp, conv->fft_buffer, &ir[i * conv->blockSize], copy);
|
||||
if (copy < conv->segSize)
|
||||
spa_fga_dsp_fft_memclear(conv->dsp, conv->fft_buffer + copy, conv->segSize - copy, true);
|
||||
|
||||
spa_fga_dsp_fft_run(conv->dsp, conv->fft, 1, conv->fft_buffer, conv->segmentsIr[i]);
|
||||
}
|
||||
conv->pre_mult = spa_fga_dsp_fft_memalloc(conv->dsp, conv->fftComplexSize, false);
|
||||
conv->conv = spa_fga_dsp_fft_memalloc(conv->dsp, conv->fftComplexSize, false);
|
||||
conv->overlap = spa_fga_dsp_fft_memalloc(conv->dsp, conv->blockSize, true);
|
||||
conv->inputBuffer = spa_fga_dsp_fft_memalloc(conv->dsp, conv->segSize, true);
|
||||
if (conv->pre_mult == NULL || conv->conv == NULL || conv->overlap == NULL ||
|
||||
conv->inputBuffer == NULL)
|
||||
goto error;
|
||||
conv->scale = 1.0f / conv->segSize;
|
||||
convolver1_reset(conv);
|
||||
|
||||
return conv;
|
||||
error:
|
||||
convolver1_free(conv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int convolver1_run(struct convolver1 *conv, const float *input, float *output, int len)
|
||||
{
|
||||
int i, processed = 0;
|
||||
|
||||
if (conv == NULL || conv->segCount == 0) {
|
||||
spa_fga_dsp_fft_memclear(conv->dsp, output, len, true);
|
||||
return len;
|
||||
}
|
||||
|
||||
while (processed < len) {
|
||||
const int processing = SPA_MIN(len - processed, conv->blockSize - conv->inputBufferFill);
|
||||
const int inputBufferPos = conv->inputBufferFill;
|
||||
|
||||
spa_fga_dsp_copy(conv->dsp, conv->inputBuffer + inputBufferPos, input + processed, processing);
|
||||
if (inputBufferPos == 0 && processing < conv->blockSize)
|
||||
spa_fga_dsp_fft_memclear(conv->dsp, conv->inputBuffer + processing, conv->blockSize - processing, true);
|
||||
|
||||
spa_fga_dsp_fft_run(conv->dsp, conv->fft, 1, conv->inputBuffer, conv->segments[conv->current]);
|
||||
|
||||
if (conv->segCount > 1) {
|
||||
if (conv->inputBufferFill == 0) {
|
||||
int indexAudio = (conv->current + 1) % conv->segCount;
|
||||
|
||||
spa_fga_dsp_fft_cmul(conv->dsp, conv->fft, conv->pre_mult,
|
||||
conv->segmentsIr[1],
|
||||
conv->segments[indexAudio],
|
||||
conv->fftComplexSize, conv->scale);
|
||||
|
||||
for (i = 2; i < conv->segCount; i++) {
|
||||
indexAudio = (conv->current + i) % conv->segCount;
|
||||
|
||||
spa_fga_dsp_fft_cmuladd(conv->dsp, conv->fft,
|
||||
conv->pre_mult,
|
||||
conv->pre_mult,
|
||||
conv->segmentsIr[i],
|
||||
conv->segments[indexAudio],
|
||||
conv->fftComplexSize, conv->scale);
|
||||
}
|
||||
}
|
||||
spa_fga_dsp_fft_cmuladd(conv->dsp, conv->fft,
|
||||
conv->conv,
|
||||
conv->pre_mult,
|
||||
conv->segments[conv->current],
|
||||
conv->segmentsIr[0],
|
||||
conv->fftComplexSize, conv->scale);
|
||||
} else {
|
||||
spa_fga_dsp_fft_cmul(conv->dsp, conv->fft,
|
||||
conv->conv,
|
||||
conv->segments[conv->current],
|
||||
conv->segmentsIr[0],
|
||||
conv->fftComplexSize, conv->scale);
|
||||
}
|
||||
|
||||
spa_fga_dsp_fft_run(conv->dsp, conv->ifft, -1, conv->conv, conv->fft_buffer);
|
||||
|
||||
spa_fga_dsp_sum(conv->dsp, output + processed, conv->fft_buffer + inputBufferPos,
|
||||
conv->overlap + inputBufferPos, processing);
|
||||
|
||||
conv->inputBufferFill += processing;
|
||||
if (conv->inputBufferFill == conv->blockSize) {
|
||||
conv->inputBufferFill = 0;
|
||||
|
||||
spa_fga_dsp_copy(conv->dsp, conv->overlap, conv->fft_buffer + conv->blockSize, conv->blockSize);
|
||||
|
||||
conv->current = (conv->current > 0) ? (conv->current - 1) : (conv->segCount - 1);
|
||||
}
|
||||
|
||||
processed += processing;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
struct convolver
|
||||
{
|
||||
struct spa_fga_dsp *dsp;
|
||||
int headBlockSize;
|
||||
int tailBlockSize;
|
||||
struct convolver1 *headConvolver;
|
||||
struct convolver1 *tailConvolver0;
|
||||
float *tailOutput0;
|
||||
float *tailPrecalculated0;
|
||||
struct convolver1 *tailConvolver;
|
||||
float *tailOutput;
|
||||
float *tailPrecalculated;
|
||||
float *tailInput;
|
||||
int tailInputFill;
|
||||
int precalculatedPos;
|
||||
};
|
||||
|
||||
void convolver_reset(struct convolver *conv)
|
||||
{
|
||||
if (conv->headConvolver)
|
||||
convolver1_reset(conv->headConvolver);
|
||||
if (conv->tailConvolver0) {
|
||||
convolver1_reset(conv->tailConvolver0);
|
||||
spa_fga_dsp_fft_memclear(conv->dsp, conv->tailOutput0, conv->tailBlockSize, true);
|
||||
spa_fga_dsp_fft_memclear(conv->dsp, conv->tailPrecalculated0, conv->tailBlockSize, true);
|
||||
}
|
||||
if (conv->tailConvolver) {
|
||||
convolver1_reset(conv->tailConvolver);
|
||||
spa_fga_dsp_fft_memclear(conv->dsp, conv->tailOutput, conv->tailBlockSize, true);
|
||||
spa_fga_dsp_fft_memclear(conv->dsp, conv->tailPrecalculated, conv->tailBlockSize, true);
|
||||
}
|
||||
conv->tailInputFill = 0;
|
||||
conv->precalculatedPos = 0;
|
||||
}
|
||||
|
||||
struct convolver *convolver_new(struct spa_fga_dsp *dsp, int head_block, int tail_block, const float *ir, int irlen)
|
||||
{
|
||||
struct convolver *conv;
|
||||
int head_ir_len;
|
||||
|
||||
if (head_block == 0 || tail_block == 0)
|
||||
return NULL;
|
||||
|
||||
head_block = SPA_MAX(1, head_block);
|
||||
if (head_block > tail_block)
|
||||
SPA_SWAP(head_block, tail_block);
|
||||
|
||||
while (irlen > 0 && fabs(ir[irlen-1]) < 0.000001f)
|
||||
irlen--;
|
||||
|
||||
conv = calloc(1, sizeof(*conv));
|
||||
if (conv == NULL)
|
||||
return NULL;
|
||||
|
||||
if (irlen == 0)
|
||||
return conv;
|
||||
|
||||
conv->dsp = dsp;
|
||||
conv->headBlockSize = next_power_of_two(head_block);
|
||||
conv->tailBlockSize = next_power_of_two(tail_block);
|
||||
|
||||
head_ir_len = SPA_MIN(irlen, conv->tailBlockSize);
|
||||
conv->headConvolver = convolver1_new(dsp, conv->headBlockSize, ir, head_ir_len);
|
||||
if (conv->headConvolver == NULL)
|
||||
goto error;
|
||||
|
||||
if (irlen > conv->tailBlockSize) {
|
||||
int conv1IrLen = SPA_MIN(irlen - conv->tailBlockSize, conv->tailBlockSize);
|
||||
conv->tailConvolver0 = convolver1_new(dsp, conv->headBlockSize, ir + conv->tailBlockSize, conv1IrLen);
|
||||
conv->tailOutput0 = spa_fga_dsp_fft_memalloc(conv->dsp, conv->tailBlockSize, true);
|
||||
conv->tailPrecalculated0 = spa_fga_dsp_fft_memalloc(conv->dsp, conv->tailBlockSize, true);
|
||||
if (conv->tailConvolver0 == NULL || conv->tailOutput0 == NULL ||
|
||||
conv->tailPrecalculated0 == NULL)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (irlen > 2 * conv->tailBlockSize) {
|
||||
int tailIrLen = irlen - (2 * conv->tailBlockSize);
|
||||
conv->tailConvolver = convolver1_new(dsp, conv->tailBlockSize, ir + (2 * conv->tailBlockSize), tailIrLen);
|
||||
conv->tailOutput = spa_fga_dsp_fft_memalloc(conv->dsp, conv->tailBlockSize, true);
|
||||
conv->tailPrecalculated = spa_fga_dsp_fft_memalloc(conv->dsp, conv->tailBlockSize, true);
|
||||
if (conv->tailConvolver == NULL || conv->tailOutput == NULL ||
|
||||
conv->tailPrecalculated == NULL)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (conv->tailConvolver0 || conv->tailConvolver) {
|
||||
conv->tailInput = spa_fga_dsp_fft_memalloc(conv->dsp, conv->tailBlockSize, true);
|
||||
if (conv->tailInput == NULL)
|
||||
goto error;
|
||||
}
|
||||
|
||||
convolver_reset(conv);
|
||||
|
||||
return conv;
|
||||
error:
|
||||
convolver_free(conv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void convolver_free(struct convolver *conv)
|
||||
{
|
||||
if (conv->headConvolver)
|
||||
convolver1_free(conv->headConvolver);
|
||||
if (conv->tailConvolver0)
|
||||
convolver1_free(conv->tailConvolver0);
|
||||
if (conv->tailConvolver)
|
||||
convolver1_free(conv->tailConvolver);
|
||||
spa_fga_dsp_fft_memfree(conv->dsp, conv->tailOutput0);
|
||||
spa_fga_dsp_fft_memfree(conv->dsp, conv->tailPrecalculated0);
|
||||
spa_fga_dsp_fft_memfree(conv->dsp, conv->tailOutput);
|
||||
spa_fga_dsp_fft_memfree(conv->dsp, conv->tailPrecalculated);
|
||||
spa_fga_dsp_fft_memfree(conv->dsp, conv->tailInput);
|
||||
free(conv);
|
||||
}
|
||||
|
||||
int convolver_run(struct convolver *conv, const float *input, float *output, int length)
|
||||
{
|
||||
convolver1_run(conv->headConvolver, input, output, length);
|
||||
|
||||
if (conv->tailInput) {
|
||||
int processed = 0;
|
||||
|
||||
while (processed < length) {
|
||||
int remaining = length - processed;
|
||||
int processing = SPA_MIN(remaining, conv->headBlockSize - (conv->tailInputFill % conv->headBlockSize));
|
||||
|
||||
if (conv->tailPrecalculated0)
|
||||
spa_fga_dsp_sum(conv->dsp, &output[processed], &output[processed],
|
||||
&conv->tailPrecalculated0[conv->precalculatedPos],
|
||||
processing);
|
||||
if (conv->tailPrecalculated)
|
||||
spa_fga_dsp_sum(conv->dsp, &output[processed], &output[processed],
|
||||
&conv->tailPrecalculated[conv->precalculatedPos],
|
||||
processing);
|
||||
conv->precalculatedPos += processing;
|
||||
|
||||
spa_fga_dsp_copy(conv->dsp, conv->tailInput + conv->tailInputFill, input + processed, processing);
|
||||
conv->tailInputFill += processing;
|
||||
|
||||
if (conv->tailPrecalculated0 && (conv->tailInputFill % conv->headBlockSize == 0)) {
|
||||
int blockOffset = conv->tailInputFill - conv->headBlockSize;
|
||||
convolver1_run(conv->tailConvolver0,
|
||||
conv->tailInput + blockOffset,
|
||||
conv->tailOutput0 + blockOffset,
|
||||
conv->headBlockSize);
|
||||
if (conv->tailInputFill == conv->tailBlockSize)
|
||||
SPA_SWAP(conv->tailPrecalculated0, conv->tailOutput0);
|
||||
}
|
||||
|
||||
if (conv->tailPrecalculated &&
|
||||
conv->tailInputFill == conv->tailBlockSize) {
|
||||
SPA_SWAP(conv->tailPrecalculated, conv->tailOutput);
|
||||
convolver1_run(conv->tailConvolver, conv->tailInput,
|
||||
conv->tailOutput, conv->tailBlockSize);
|
||||
}
|
||||
if (conv->tailInputFill == conv->tailBlockSize) {
|
||||
conv->tailInputFill = 0;
|
||||
conv->precalculatedPos = 0;
|
||||
}
|
||||
processed += processing;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
/* PipeWire */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "audio-dsp.h"
|
||||
|
||||
struct convolver *convolver_new(struct spa_fga_dsp *dsp, int block, int tail, const float *ir, int irlen);
|
||||
void convolver_free(struct convolver *conv);
|
||||
|
||||
void convolver_reset(struct convolver *conv);
|
||||
int convolver_run(struct convolver *conv, const float *input, float *output, int length);
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,104 +0,0 @@
|
|||
/* Simple Plugin API */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef SPA_FILTER_GRAPH_H
|
||||
#define SPA_FILTER_GRAPH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <spa/utils/defs.h>
|
||||
#include <spa/utils/hook.h>
|
||||
#include <spa/pod/builder.h>
|
||||
|
||||
/** \defgroup spa_filter_graph Filter Graph
|
||||
* a graph of filters
|
||||
*/
|
||||
|
||||
/**
|
||||
* \addtogroup spa_filter_graph
|
||||
* \{
|
||||
*/
|
||||
|
||||
/**
|
||||
* A graph of filters
|
||||
*/
|
||||
#define SPA_TYPE_INTERFACE_FilterGraph SPA_TYPE_INFO_INTERFACE_BASE "FilterGraph"
|
||||
|
||||
#define SPA_VERSION_FILTER_GRAPH 0
|
||||
struct spa_filter_graph { struct spa_interface iface; };
|
||||
|
||||
struct spa_filter_graph_events {
|
||||
#define SPA_VERSION_FILTER_GRAPH_EVENTS 0
|
||||
uint32_t version;
|
||||
|
||||
void (*apply_props) (void *object, enum spa_direction direction, const struct spa_pod *props);
|
||||
|
||||
void (*props_changed) (void *object, enum spa_direction direction);
|
||||
};
|
||||
|
||||
struct spa_filter_graph_chunk {
|
||||
void *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct spa_filter_graph_methods {
|
||||
#define SPA_VERSION_FILTER_GRAPH_METHODS 0
|
||||
uint32_t version;
|
||||
|
||||
int (*add_listener) (void *object,
|
||||
struct spa_hook *listener,
|
||||
const struct spa_filter_graph_events *events,
|
||||
void *data);
|
||||
|
||||
int (*enum_prop_info) (void *object, uint32_t idx, struct spa_pod_builder *b);
|
||||
int (*get_props) (void *object, struct spa_pod_builder *b, const struct spa_pod **props);
|
||||
int (*set_props) (void *object, enum spa_direction direction, const struct spa_pod *props);
|
||||
|
||||
int (*activate) (void *object, const struct spa_fraction *rate);
|
||||
int (*deactivate) (void *object);
|
||||
|
||||
int (*reset) (void *object);
|
||||
|
||||
int (*process) (void *object,
|
||||
const struct spa_filter_graph_chunk in[], uint32_t n_in,
|
||||
struct spa_filter_graph_chunk out[], uint32_t n_out);
|
||||
};
|
||||
|
||||
#define spa_filter_graph_method_r(o,method,version,...) \
|
||||
({ \
|
||||
volatile int _res = -ENOTSUP; \
|
||||
struct spa_filter_graph *_o = o; \
|
||||
spa_interface_call_fast_res(&_o->iface, \
|
||||
struct spa_filter_graph_methods, _res, \
|
||||
method, version, ##__VA_ARGS__); \
|
||||
_res; \
|
||||
})
|
||||
|
||||
#define spa_filter_graph_add_listener(o,...) spa_filter_graph_method_r(o,add_listener,0,__VA_ARGS__)
|
||||
|
||||
#define spa_filter_graph_enum_prop_info(o,...) spa_filter_graph_method_r(o,enum_prop_info,0,__VA_ARGS__)
|
||||
#define spa_filter_graph_get_props(o,...) spa_filter_graph_method_r(o,get_props,0,__VA_ARGS__)
|
||||
#define spa_filter_graph_set_props(o,...) spa_filter_graph_method_r(o,set_props,0,__VA_ARGS__)
|
||||
|
||||
#define spa_filter_graph_activate(o,...) spa_filter_graph_method_r(o,activate,0,__VA_ARGS__)
|
||||
#define spa_filter_graph_deactivate(o) spa_filter_graph_method_r(o,deactivate,0)
|
||||
|
||||
#define spa_filter_graph_reset(o) spa_filter_graph_method_r(o,reset,0)
|
||||
|
||||
#define spa_filter_graph_process(o,...) spa_filter_graph_method_r(o,process,0,__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* SPA_FILTER_GRAPH_H */
|
||||
|
||||
|
|
@ -1,603 +0,0 @@
|
|||
/* ladspa.h
|
||||
|
||||
Linux Audio Developer's Simple Plugin API Version 1.1[LGPL].
|
||||
Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis,
|
||||
Stefan Westerfeld.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public License
|
||||
as published by the Free Software Foundation; either version 2.1 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA. */
|
||||
|
||||
#ifndef LADSPA_INCLUDED
|
||||
#define LADSPA_INCLUDED
|
||||
|
||||
#define LADSPA_VERSION "1.1"
|
||||
#define LADSPA_VERSION_MAJOR 1
|
||||
#define LADSPA_VERSION_MINOR 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Overview:
|
||||
|
||||
There is a large number of synthesis packages in use or development
|
||||
on the Linux platform at this time. This API (`The Linux Audio
|
||||
Developer's Simple Plugin API') attempts to give programmers the
|
||||
ability to write simple `plugin' audio processors in C/C++ and link
|
||||
them dynamically (`plug') into a range of these packages (`hosts').
|
||||
It should be possible for any host and any plugin to communicate
|
||||
completely through this interface.
|
||||
|
||||
This API is deliberately short and simple. To achieve compatibility
|
||||
with a range of promising Linux sound synthesis packages it
|
||||
attempts to find the `greatest common divisor' in their logical
|
||||
behaviour. Having said this, certain limiting decisions are
|
||||
implicit, notably the use of a fixed type (LADSPA_Data) for all
|
||||
data transfer and absence of a parameterised `initialisation'
|
||||
phase. See below for the LADSPA_Data typedef.
|
||||
|
||||
Plugins are expected to distinguish between control and audio
|
||||
data. Plugins have `ports' that are inputs or outputs for audio or
|
||||
control data and each plugin is `run' for a `block' corresponding
|
||||
to a short time interval measured in samples. Audio data is
|
||||
communicated using arrays of LADSPA_Data, allowing a block of audio
|
||||
to be processed by the plugin in a single pass. Control data is
|
||||
communicated using single LADSPA_Data values. Control data has a
|
||||
single value at the start of a call to the `run()' or `run_adding()'
|
||||
function, and may be considered to remain this value for its
|
||||
duration. The plugin may assume that all its input and output ports
|
||||
have been connected to the relevant data location (see the
|
||||
`connect_port()' function below) before it is asked to run.
|
||||
|
||||
Plugins will reside in shared object files suitable for dynamic
|
||||
linking by dlopen() and family. The file will provide a number of
|
||||
`plugin types' that can be used to instantiate actual plugins
|
||||
(sometimes known as `plugin instances') that can be connected
|
||||
together to perform tasks.
|
||||
|
||||
This API contains very limited error-handling. */
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Fundamental data type passed in and out of plugin. This data type
|
||||
is used to communicate audio samples and control values. It is
|
||||
assumed that the plugin will work sensibly given any numeric input
|
||||
value although it may have a preferred range (see hints below).
|
||||
|
||||
For audio it is generally assumed that 1.0f is the `0dB' reference
|
||||
amplitude and is a `normal' signal level. */
|
||||
|
||||
typedef float LADSPA_Data;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Special Plugin Properties:
|
||||
|
||||
Optional features of the plugin type are encapsulated in the
|
||||
LADSPA_Properties type. This is assembled by ORing individual
|
||||
properties together. */
|
||||
|
||||
typedef int LADSPA_Properties;
|
||||
|
||||
/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a
|
||||
real-time dependency (e.g. listens to a MIDI device) and so its
|
||||
output must not be cached or subject to significant latency. */
|
||||
#define LADSPA_PROPERTY_REALTIME 0x1
|
||||
|
||||
/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin
|
||||
may cease to work correctly if the host elects to use the same data
|
||||
location for both input and output (see connect_port()). This
|
||||
should be avoided as enabling this flag makes it impossible for
|
||||
hosts to use the plugin to process audio `in-place.' */
|
||||
#define LADSPA_PROPERTY_INPLACE_BROKEN 0x2
|
||||
|
||||
/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin
|
||||
is capable of running not only in a conventional host but also in a
|
||||
`hard real-time' environment. To qualify for this the plugin must
|
||||
satisfy all of the following:
|
||||
|
||||
(1) The plugin must not use malloc(), free() or other heap memory
|
||||
management within its run() or run_adding() functions. All new
|
||||
memory used in run() must be managed via the stack. These
|
||||
restrictions only apply to the run() function.
|
||||
|
||||
(2) The plugin will not attempt to make use of any library
|
||||
functions with the exceptions of functions in the ANSI standard C
|
||||
and C maths libraries, which the host is expected to provide.
|
||||
|
||||
(3) The plugin will not access files, devices, pipes, sockets, IPC
|
||||
or any other mechanism that might result in process or thread
|
||||
blocking.
|
||||
|
||||
(4) The plugin will take an amount of time to execute a run() or
|
||||
run_adding() call approximately of form (A+B*SampleCount) where A
|
||||
and B depend on the machine and host in use. This amount of time
|
||||
may not depend on input signals or plugin state. The host is left
|
||||
the responsibility to perform timings to estimate upper bounds for
|
||||
A and B. */
|
||||
#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4
|
||||
|
||||
#define LADSPA_IS_REALTIME(x) ((x) & LADSPA_PROPERTY_REALTIME)
|
||||
#define LADSPA_IS_INPLACE_BROKEN(x) ((x) & LADSPA_PROPERTY_INPLACE_BROKEN)
|
||||
#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Plugin Ports:
|
||||
|
||||
Plugins have `ports' that are inputs or outputs for audio or
|
||||
data. Ports can communicate arrays of LADSPA_Data (for audio
|
||||
inputs/outputs) or single LADSPA_Data values (for control
|
||||
input/outputs). This information is encapsulated in the
|
||||
LADSPA_PortDescriptor type which is assembled by ORing individual
|
||||
properties together.
|
||||
|
||||
Note that a port must be an input or an output port but not both
|
||||
and that a port must be a control or audio port but not both. */
|
||||
|
||||
typedef int LADSPA_PortDescriptor;
|
||||
|
||||
/* Property LADSPA_PORT_INPUT indicates that the port is an input. */
|
||||
#define LADSPA_PORT_INPUT 0x1
|
||||
|
||||
/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */
|
||||
#define LADSPA_PORT_OUTPUT 0x2
|
||||
|
||||
/* Property LADSPA_PORT_CONTROL indicates that the port is a control
|
||||
port. */
|
||||
#define LADSPA_PORT_CONTROL 0x4
|
||||
|
||||
/* Property LADSPA_PORT_AUDIO indicates that the port is a audio
|
||||
port. */
|
||||
#define LADSPA_PORT_AUDIO 0x8
|
||||
|
||||
#define LADSPA_IS_PORT_INPUT(x) ((x) & LADSPA_PORT_INPUT)
|
||||
#define LADSPA_IS_PORT_OUTPUT(x) ((x) & LADSPA_PORT_OUTPUT)
|
||||
#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL)
|
||||
#define LADSPA_IS_PORT_AUDIO(x) ((x) & LADSPA_PORT_AUDIO)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Plugin Port Range Hints:
|
||||
|
||||
The host may wish to provide a representation of data entering or
|
||||
leaving a plugin (e.g. to generate a GUI automatically). To make
|
||||
this more meaningful, the plugin should provide `hints' to the host
|
||||
describing the usual values taken by the data.
|
||||
|
||||
Note that these are only hints. The host may ignore them and the
|
||||
plugin must not assume that data supplied to it is meaningful. If
|
||||
the plugin receives invalid input data it is expected to continue
|
||||
to run without failure and, where possible, produce a sensible
|
||||
output (e.g. a high-pass filter given a negative cutoff frequency
|
||||
might switch to an all-pass mode).
|
||||
|
||||
Hints are meaningful for all input and output ports but hints for
|
||||
input control ports are expected to be particularly useful.
|
||||
|
||||
More hint information is encapsulated in the
|
||||
LADSPA_PortRangeHintDescriptor type which is assembled by ORing
|
||||
individual hint types together. Hints may require further
|
||||
LowerBound and UpperBound information.
|
||||
|
||||
All the hint information for a particular port is aggregated in the
|
||||
LADSPA_PortRangeHint structure. */
|
||||
|
||||
typedef int LADSPA_PortRangeHintDescriptor;
|
||||
|
||||
/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field
|
||||
of the LADSPA_PortRangeHint should be considered meaningful. The
|
||||
value in this field should be considered the (inclusive) lower
|
||||
bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
|
||||
specified then the value of LowerBound should be multiplied by the
|
||||
sample rate. */
|
||||
#define LADSPA_HINT_BOUNDED_BELOW 0x1
|
||||
|
||||
/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field
|
||||
of the LADSPA_PortRangeHint should be considered meaningful. The
|
||||
value in this field should be considered the (inclusive) upper
|
||||
bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
|
||||
specified then the value of UpperBound should be multiplied by the
|
||||
sample rate. */
|
||||
#define LADSPA_HINT_BOUNDED_ABOVE 0x2
|
||||
|
||||
/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be
|
||||
considered a Boolean toggle. Data less than or equal to zero should
|
||||
be considered `off' or `false,' and data above zero should be
|
||||
considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in
|
||||
conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or
|
||||
LADSPA_HINT_DEFAULT_1. */
|
||||
#define LADSPA_HINT_TOGGLED 0x4
|
||||
|
||||
/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified
|
||||
should be interpreted as multiples of the sample rate. For
|
||||
instance, a frequency range from 0Hz to the Nyquist frequency (half
|
||||
the sample rate) could be requested by this hint in conjunction
|
||||
with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds
|
||||
at all must support this hint to retain meaning. */
|
||||
#define LADSPA_HINT_SAMPLE_RATE 0x8
|
||||
|
||||
/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the
|
||||
user will find it more intuitive to view values using a logarithmic
|
||||
scale. This is particularly useful for frequencies and gains. */
|
||||
#define LADSPA_HINT_LOGARITHMIC 0x10
|
||||
|
||||
/* Hint LADSPA_HINT_INTEGER indicates that a user interface would
|
||||
probably wish to provide a stepped control taking only integer
|
||||
values. Any bounds set should be slightly wider than the actual
|
||||
integer range required to avoid floating point rounding errors. For
|
||||
instance, the integer set {0,1,2,3} might be described as [-0.1,
|
||||
3.1]. */
|
||||
#define LADSPA_HINT_INTEGER 0x20
|
||||
|
||||
/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal'
|
||||
value for the port that is sensible as a default. For instance,
|
||||
this value is suitable for use as an initial value in a user
|
||||
interface or as a value the host might assign to a control port
|
||||
when the user has not provided one. Defaults are encoded using a
|
||||
mask so only one default may be specified for a port. Some of the
|
||||
hints make use of lower and upper bounds, in which case the
|
||||
relevant bound or bounds must be available and
|
||||
LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting
|
||||
default must be rounded if LADSPA_HINT_INTEGER is present. Default
|
||||
values were introduced in LADSPA v1.1. */
|
||||
#define LADSPA_HINT_DEFAULT_MASK 0x3C0
|
||||
|
||||
/* This default values indicates that no default is provided. */
|
||||
#define LADSPA_HINT_DEFAULT_NONE 0x0
|
||||
|
||||
/* This default hint indicates that the suggested lower bound for the
|
||||
port should be used. */
|
||||
#define LADSPA_HINT_DEFAULT_MINIMUM 0x40
|
||||
|
||||
/* This default hint indicates that a low value between the suggested
|
||||
lower and upper bounds should be chosen. For ports with
|
||||
LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 +
|
||||
log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper
|
||||
* 0.25). */
|
||||
#define LADSPA_HINT_DEFAULT_LOW 0x80
|
||||
|
||||
/* This default hint indicates that a middle value between the
|
||||
suggested lower and upper bounds should be chosen. For ports with
|
||||
LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 +
|
||||
log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper *
|
||||
0.5). */
|
||||
#define LADSPA_HINT_DEFAULT_MIDDLE 0xC0
|
||||
|
||||
/* This default hint indicates that a high value between the suggested
|
||||
lower and upper bounds should be chosen. For ports with
|
||||
LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 +
|
||||
log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper
|
||||
* 0.75). */
|
||||
#define LADSPA_HINT_DEFAULT_HIGH 0x100
|
||||
|
||||
/* This default hint indicates that the suggested upper bound for the
|
||||
port should be used. */
|
||||
#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140
|
||||
|
||||
/* This default hint indicates that the number 0 should be used. Note
|
||||
that this default may be used in conjunction with
|
||||
LADSPA_HINT_TOGGLED. */
|
||||
#define LADSPA_HINT_DEFAULT_0 0x200
|
||||
|
||||
/* This default hint indicates that the number 1 should be used. Note
|
||||
that this default may be used in conjunction with
|
||||
LADSPA_HINT_TOGGLED. */
|
||||
#define LADSPA_HINT_DEFAULT_1 0x240
|
||||
|
||||
/* This default hint indicates that the number 100 should be used. */
|
||||
#define LADSPA_HINT_DEFAULT_100 0x280
|
||||
|
||||
/* This default hint indicates that the Hz frequency of `concert A'
|
||||
should be used. This will be 440 unless the host uses an unusual
|
||||
tuning convention, in which case it may be within a few Hz. */
|
||||
#define LADSPA_HINT_DEFAULT_440 0x2C0
|
||||
|
||||
#define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW)
|
||||
#define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE)
|
||||
#define LADSPA_IS_HINT_TOGGLED(x) ((x) & LADSPA_HINT_TOGGLED)
|
||||
#define LADSPA_IS_HINT_SAMPLE_RATE(x) ((x) & LADSPA_HINT_SAMPLE_RATE)
|
||||
#define LADSPA_IS_HINT_LOGARITHMIC(x) ((x) & LADSPA_HINT_LOGARITHMIC)
|
||||
#define LADSPA_IS_HINT_INTEGER(x) ((x) & LADSPA_HINT_INTEGER)
|
||||
|
||||
#define LADSPA_IS_HINT_HAS_DEFAULT(x) ((x) & LADSPA_HINT_DEFAULT_MASK)
|
||||
#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
|
||||
== LADSPA_HINT_DEFAULT_MINIMUM)
|
||||
#define LADSPA_IS_HINT_DEFAULT_LOW(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
|
||||
== LADSPA_HINT_DEFAULT_LOW)
|
||||
#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
|
||||
== LADSPA_HINT_DEFAULT_MIDDLE)
|
||||
#define LADSPA_IS_HINT_DEFAULT_HIGH(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
|
||||
== LADSPA_HINT_DEFAULT_HIGH)
|
||||
#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
|
||||
== LADSPA_HINT_DEFAULT_MAXIMUM)
|
||||
#define LADSPA_IS_HINT_DEFAULT_0(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
|
||||
== LADSPA_HINT_DEFAULT_0)
|
||||
#define LADSPA_IS_HINT_DEFAULT_1(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
|
||||
== LADSPA_HINT_DEFAULT_1)
|
||||
#define LADSPA_IS_HINT_DEFAULT_100(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
|
||||
== LADSPA_HINT_DEFAULT_100)
|
||||
#define LADSPA_IS_HINT_DEFAULT_440(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
|
||||
== LADSPA_HINT_DEFAULT_440)
|
||||
|
||||
typedef struct _LADSPA_PortRangeHint {
|
||||
|
||||
/* Hints about the port. */
|
||||
LADSPA_PortRangeHintDescriptor HintDescriptor;
|
||||
|
||||
/* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When
|
||||
LADSPA_HINT_SAMPLE_RATE is also active then this value should be
|
||||
multiplied by the relevant sample rate. */
|
||||
LADSPA_Data LowerBound;
|
||||
|
||||
/* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When
|
||||
LADSPA_HINT_SAMPLE_RATE is also active then this value should be
|
||||
multiplied by the relevant sample rate. */
|
||||
LADSPA_Data UpperBound;
|
||||
|
||||
} LADSPA_PortRangeHint;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Plugin Handles:
|
||||
|
||||
This plugin handle indicates a particular instance of the plugin
|
||||
concerned. It is valid to compare this to NULL (0 for C++) but
|
||||
otherwise the host should not attempt to interpret it. The plugin
|
||||
may use it to reference internal instance data. */
|
||||
|
||||
typedef void * LADSPA_Handle;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Descriptor for a Type of Plugin:
|
||||
|
||||
This structure is used to describe a plugin type. It provides a
|
||||
number of functions to examine the type, instantiate it, link it to
|
||||
buffers and workspaces and to run it. */
|
||||
|
||||
typedef struct _LADSPA_Descriptor {
|
||||
|
||||
/* This numeric identifier indicates the plugin type
|
||||
uniquely. Plugin programmers may reserve ranges of IDs from a
|
||||
central body to avoid clashes. Hosts may assume that IDs are
|
||||
below 0x1000000. */
|
||||
unsigned long UniqueID;
|
||||
|
||||
/* This identifier can be used as a unique, case-sensitive
|
||||
identifier for the plugin type within the plugin file. Plugin
|
||||
types should be identified by file and label rather than by index
|
||||
or plugin name, which may be changed in new plugin
|
||||
versions. Labels must not contain white-space characters. */
|
||||
const char * Label;
|
||||
|
||||
/* This indicates a number of properties of the plugin. */
|
||||
LADSPA_Properties Properties;
|
||||
|
||||
/* This member points to the null-terminated name of the plugin
|
||||
(e.g. "Sine Oscillator"). */
|
||||
const char * Name;
|
||||
|
||||
/* This member points to the null-terminated string indicating the
|
||||
maker of the plugin. This can be an empty string but not NULL. */
|
||||
const char * Maker;
|
||||
|
||||
/* This member points to the null-terminated string indicating any
|
||||
copyright applying to the plugin. If no Copyright applies the
|
||||
string "None" should be used. */
|
||||
const char * Copyright;
|
||||
|
||||
/* This indicates the number of ports (input AND output) present on
|
||||
the plugin. */
|
||||
unsigned long PortCount;
|
||||
|
||||
/* This member indicates an array of port descriptors. Valid indices
|
||||
vary from 0 to PortCount-1. */
|
||||
const LADSPA_PortDescriptor * PortDescriptors;
|
||||
|
||||
/* This member indicates an array of null-terminated strings
|
||||
describing ports (e.g. "Frequency (Hz)"). Valid indices vary from
|
||||
0 to PortCount-1. */
|
||||
const char * const * PortNames;
|
||||
|
||||
/* This member indicates an array of range hints for each port (see
|
||||
above). Valid indices vary from 0 to PortCount-1. */
|
||||
const LADSPA_PortRangeHint * PortRangeHints;
|
||||
|
||||
/* This may be used by the plugin developer to pass any custom
|
||||
implementation data into an instantiate call. It must not be used
|
||||
or interpreted by the host. It is expected that most plugin
|
||||
writers will not use this facility as LADSPA_Handle should be
|
||||
used to hold instance data. */
|
||||
void * ImplementationData;
|
||||
|
||||
/* This member is a function pointer that instantiates a plugin. A
|
||||
handle is returned indicating the new plugin instance. The
|
||||
instantiation function accepts a sample rate as a parameter. The
|
||||
plugin descriptor from which this instantiate function was found
|
||||
must also be passed. This function must return NULL if
|
||||
instantiation fails.
|
||||
|
||||
Note that instance initialisation should generally occur in
|
||||
activate() rather than here. */
|
||||
LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor,
|
||||
unsigned long SampleRate);
|
||||
|
||||
/* This member is a function pointer that connects a port on an
|
||||
instantiated plugin to a memory location at which a block of data
|
||||
for the port will be read/written. The data location is expected
|
||||
to be an array of LADSPA_Data for audio ports or a single
|
||||
LADSPA_Data value for control ports. Memory issues will be
|
||||
managed by the host. The plugin must read/write the data at these
|
||||
locations every time run() or run_adding() is called and the data
|
||||
present at the time of this connection call should not be
|
||||
considered meaningful.
|
||||
|
||||
connect_port() may be called more than once for a plugin instance
|
||||
to allow the host to change the buffers that the plugin is
|
||||
reading or writing. These calls may be made before or after
|
||||
activate() or deactivate() calls.
|
||||
|
||||
connect_port() must be called at least once for each port before
|
||||
run() or run_adding() is called. When working with blocks of
|
||||
LADSPA_Data the plugin should pay careful attention to the block
|
||||
size passed to the run function as the block allocated may only
|
||||
just be large enough to contain the block of samples.
|
||||
|
||||
Plugin writers should be aware that the host may elect to use the
|
||||
same buffer for more than one port and even use the same buffer
|
||||
for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN).
|
||||
However, overlapped buffers or use of a single buffer for both
|
||||
audio and control data may result in unexpected behaviour. */
|
||||
void (*connect_port)(LADSPA_Handle Instance,
|
||||
unsigned long Port,
|
||||
LADSPA_Data * DataLocation);
|
||||
|
||||
/* This member is a function pointer that initialises a plugin
|
||||
instance and activates it for use. This is separated from
|
||||
instantiate() to aid real-time support and so that hosts can
|
||||
reinitialise a plugin instance by calling deactivate() and then
|
||||
activate(). In this case the plugin instance must reset all state
|
||||
information dependent on the history of the plugin instance
|
||||
except for any data locations provided by connect_port() and any
|
||||
gain set by set_run_adding_gain(). If there is nothing for
|
||||
activate() to do then the plugin writer may provide a NULL rather
|
||||
than an empty function.
|
||||
|
||||
When present, hosts must call this function once before run() (or
|
||||
run_adding()) is called for the first time. This call should be
|
||||
made as close to the run() call as possible and indicates to
|
||||
real-time plugins that they are now live. Plugins should not rely
|
||||
on a prompt call to run() after activate(). activate() may not be
|
||||
called again unless deactivate() is called first. Note that
|
||||
connect_port() may be called before or after a call to
|
||||
activate(). */
|
||||
void (*activate)(LADSPA_Handle Instance);
|
||||
|
||||
/* This method is a function pointer that runs an instance of a
|
||||
plugin for a block. Two parameters are required: the first is a
|
||||
handle to the particular instance to be run and the second
|
||||
indicates the block size (in samples) for which the plugin
|
||||
instance may run.
|
||||
|
||||
Note that if an activate() function exists then it must be called
|
||||
before run() or run_adding(). If deactivate() is called for a
|
||||
plugin instance then the plugin instance may not be reused until
|
||||
activate() has been called again.
|
||||
|
||||
If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE
|
||||
then there are various things that the plugin should not do
|
||||
within the run() or run_adding() functions (see above). */
|
||||
void (*run)(LADSPA_Handle Instance,
|
||||
unsigned long SampleCount);
|
||||
|
||||
/* This method is a function pointer that runs an instance of a
|
||||
plugin for a block. This has identical behaviour to run() except
|
||||
in the way data is output from the plugin. When run() is used,
|
||||
values are written directly to the memory areas associated with
|
||||
the output ports. However when run_adding() is called, values
|
||||
must be added to the values already present in the memory
|
||||
areas. Furthermore, output values written must be scaled by the
|
||||
current gain set by set_run_adding_gain() (see below) before
|
||||
addition.
|
||||
|
||||
run_adding() is optional. When it is not provided by a plugin,
|
||||
this function pointer must be set to NULL. When it is provided,
|
||||
the function set_run_adding_gain() must be provided also. */
|
||||
void (*run_adding)(LADSPA_Handle Instance,
|
||||
unsigned long SampleCount);
|
||||
|
||||
/* This method is a function pointer that sets the output gain for
|
||||
use when run_adding() is called (see above). If this function is
|
||||
never called the gain is assumed to default to 1. Gain
|
||||
information should be retained when activate() or deactivate()
|
||||
are called.
|
||||
|
||||
This function should be provided by the plugin if and only if the
|
||||
run_adding() function is provided. When it is absent this
|
||||
function pointer must be set to NULL. */
|
||||
void (*set_run_adding_gain)(LADSPA_Handle Instance,
|
||||
LADSPA_Data Gain);
|
||||
|
||||
/* This is the counterpart to activate() (see above). If there is
|
||||
nothing for deactivate() to do then the plugin writer may provide
|
||||
a NULL rather than an empty function.
|
||||
|
||||
Hosts must deactivate all activated units after they have been
|
||||
run() (or run_adding()) for the last time. This call should be
|
||||
made as close to the last run() call as possible and indicates to
|
||||
real-time plugins that they are no longer live. Plugins should
|
||||
not rely on prompt deactivation. Note that connect_port() may be
|
||||
called before or after a call to deactivate().
|
||||
|
||||
Deactivation is not similar to pausing as the plugin instance
|
||||
will be reinitialised when activate() is called to reuse it. */
|
||||
void (*deactivate)(LADSPA_Handle Instance);
|
||||
|
||||
/* Once an instance of a plugin has been finished with it can be
|
||||
deleted using the following function. The instance handle passed
|
||||
ceases to be valid after this call.
|
||||
|
||||
If activate() was called for a plugin instance then a
|
||||
corresponding call to deactivate() must be made before cleanup()
|
||||
is called. */
|
||||
void (*cleanup)(LADSPA_Handle Instance);
|
||||
|
||||
} LADSPA_Descriptor;
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
/* Accessing a Plugin: */
|
||||
|
||||
/* The exact mechanism by which plugins are loaded is host-dependent,
|
||||
however all most hosts will need to know is the name of shared
|
||||
object file containing the plugin types. To allow multiple hosts to
|
||||
share plugin types, hosts may wish to check for environment
|
||||
variable LADSPA_PATH. If present, this should contain a
|
||||
colon-separated path indicating directories that should be searched
|
||||
(in order) when loading plugin types.
|
||||
|
||||
A plugin programmer must include a function called
|
||||
"ladspa_descriptor" with the following function prototype within
|
||||
the shared object file. This function will have C-style linkage (if
|
||||
you are using C++ this is taken care of by the `extern "C"' clause
|
||||
at the top of the file).
|
||||
|
||||
A host will find the plugin shared object file by one means or
|
||||
another, find the ladspa_descriptor() function, call it, and
|
||||
proceed from there.
|
||||
|
||||
Plugin types are accessed by index (not ID) using values from 0
|
||||
upwards. Out of range indexes must result in this function
|
||||
returning NULL, so the plugin count can be determined by checking
|
||||
for the least index that results in NULL being returned. */
|
||||
|
||||
const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index);
|
||||
|
||||
/* Datatype corresponding to the ladspa_descriptor() function. */
|
||||
typedef const LADSPA_Descriptor *
|
||||
(*LADSPA_Descriptor_Function)(unsigned long Index);
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LADSPA_INCLUDED */
|
||||
|
||||
/* EOF */
|
||||
|
|
@ -1,385 +0,0 @@
|
|||
/* PipeWire */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <spa/utils/result.h>
|
||||
#include <spa/utils/defs.h>
|
||||
#include <spa/utils/list.h>
|
||||
#include <spa/utils/string.h>
|
||||
#include <spa/support/log.h>
|
||||
|
||||
#include "audio-plugin.h"
|
||||
#include "ladspa.h"
|
||||
|
||||
struct plugin {
|
||||
struct spa_handle handle;
|
||||
struct spa_fga_plugin plugin;
|
||||
|
||||
struct spa_log *log;
|
||||
|
||||
void *hndl;
|
||||
LADSPA_Descriptor_Function desc_func;
|
||||
};
|
||||
|
||||
struct descriptor {
|
||||
struct spa_fga_descriptor desc;
|
||||
const LADSPA_Descriptor *d;
|
||||
};
|
||||
|
||||
static void *ladspa_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor *desc,
|
||||
unsigned long SampleRate, int index, const char *config)
|
||||
{
|
||||
struct descriptor *d = (struct descriptor *)desc;
|
||||
return d->d->instantiate(d->d, SampleRate);
|
||||
}
|
||||
|
||||
static const LADSPA_Descriptor *find_desc(LADSPA_Descriptor_Function desc_func, const char *name)
|
||||
{
|
||||
unsigned long i;
|
||||
for (i = 0; ;i++) {
|
||||
const LADSPA_Descriptor *d = desc_func(i);
|
||||
if (d == NULL)
|
||||
break;
|
||||
if (spa_streq(d->Label, name))
|
||||
return d;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static float get_default(struct spa_fga_port *port, LADSPA_PortRangeHintDescriptor hint,
|
||||
LADSPA_Data lower, LADSPA_Data upper)
|
||||
{
|
||||
LADSPA_Data def;
|
||||
|
||||
switch (hint & LADSPA_HINT_DEFAULT_MASK) {
|
||||
case LADSPA_HINT_DEFAULT_MINIMUM:
|
||||
def = lower;
|
||||
break;
|
||||
case LADSPA_HINT_DEFAULT_MAXIMUM:
|
||||
def = upper;
|
||||
break;
|
||||
case LADSPA_HINT_DEFAULT_LOW:
|
||||
if (LADSPA_IS_HINT_LOGARITHMIC(hint))
|
||||
def = (LADSPA_Data) expf(logf(lower) * 0.75f + logf(upper) * 0.25f);
|
||||
else
|
||||
def = (LADSPA_Data) (lower * 0.75f + upper * 0.25f);
|
||||
break;
|
||||
case LADSPA_HINT_DEFAULT_MIDDLE:
|
||||
if (LADSPA_IS_HINT_LOGARITHMIC(hint))
|
||||
def = (LADSPA_Data) expf(logf(lower) * 0.5f + logf(upper) * 0.5f);
|
||||
else
|
||||
def = (LADSPA_Data) (lower * 0.5f + upper * 0.5f);
|
||||
break;
|
||||
case LADSPA_HINT_DEFAULT_HIGH:
|
||||
if (LADSPA_IS_HINT_LOGARITHMIC(hint))
|
||||
def = (LADSPA_Data) expf(logf(lower) * 0.25f + logf(upper) * 0.75f);
|
||||
else
|
||||
def = (LADSPA_Data) (lower * 0.25f + upper * 0.75f);
|
||||
break;
|
||||
case LADSPA_HINT_DEFAULT_0:
|
||||
def = 0.0f;
|
||||
break;
|
||||
case LADSPA_HINT_DEFAULT_1:
|
||||
def = 1.0f;
|
||||
break;
|
||||
case LADSPA_HINT_DEFAULT_100:
|
||||
def = 100.0f;
|
||||
break;
|
||||
case LADSPA_HINT_DEFAULT_440:
|
||||
def = 440.0f;
|
||||
break;
|
||||
default:
|
||||
if (upper == lower)
|
||||
def = upper;
|
||||
else
|
||||
def = SPA_CLAMPF(0.5f * upper, lower, upper);
|
||||
break;
|
||||
}
|
||||
if (LADSPA_IS_HINT_INTEGER(hint))
|
||||
def = roundf(def);
|
||||
return def;
|
||||
}
|
||||
|
||||
static void ladspa_port_update_ranges(struct descriptor *dd, struct spa_fga_port *port)
|
||||
{
|
||||
const LADSPA_Descriptor *d = dd->d;
|
||||
unsigned long p = port->index;
|
||||
LADSPA_PortRangeHintDescriptor hint = d->PortRangeHints[p].HintDescriptor;
|
||||
LADSPA_Data lower, upper;
|
||||
|
||||
lower = d->PortRangeHints[p].LowerBound;
|
||||
upper = d->PortRangeHints[p].UpperBound;
|
||||
|
||||
port->hint = hint;
|
||||
port->def = get_default(port, hint, lower, upper);
|
||||
port->min = lower;
|
||||
port->max = upper;
|
||||
}
|
||||
|
||||
static void ladspa_free(const struct spa_fga_descriptor *desc)
|
||||
{
|
||||
struct descriptor *d = (struct descriptor*)desc;
|
||||
free(d->desc.ports);
|
||||
free(d);
|
||||
}
|
||||
|
||||
static const struct spa_fga_descriptor *ladspa_plugin_make_desc(void *plugin, const char *name)
|
||||
{
|
||||
struct plugin *p = (struct plugin *)plugin;
|
||||
struct descriptor *desc;
|
||||
const LADSPA_Descriptor *d;
|
||||
uint32_t i;
|
||||
|
||||
d = find_desc(p->desc_func, name);
|
||||
if (d == NULL)
|
||||
return NULL;
|
||||
|
||||
desc = calloc(1, sizeof(*desc));
|
||||
desc->d = d;
|
||||
|
||||
desc->desc.instantiate = ladspa_instantiate;
|
||||
desc->desc.cleanup = d->cleanup;
|
||||
desc->desc.connect_port = d->connect_port;
|
||||
desc->desc.activate = d->activate;
|
||||
desc->desc.deactivate = d->deactivate;
|
||||
desc->desc.run = d->run;
|
||||
|
||||
desc->desc.free = ladspa_free;
|
||||
|
||||
desc->desc.name = d->Label;
|
||||
desc->desc.flags = 0;
|
||||
|
||||
desc->desc.n_ports = d->PortCount;
|
||||
desc->desc.ports = calloc(desc->desc.n_ports, sizeof(struct spa_fga_port));
|
||||
|
||||
for (i = 0; i < desc->desc.n_ports; i++) {
|
||||
desc->desc.ports[i].index = i;
|
||||
desc->desc.ports[i].name = d->PortNames[i];
|
||||
desc->desc.ports[i].flags = d->PortDescriptors[i];
|
||||
ladspa_port_update_ranges(desc, &desc->desc.ports[i]);
|
||||
}
|
||||
return &desc->desc;
|
||||
}
|
||||
|
||||
static struct spa_fga_plugin_methods impl_plugin = {
|
||||
SPA_VERSION_FGA_PLUGIN_METHODS,
|
||||
.make_desc = ladspa_plugin_make_desc,
|
||||
};
|
||||
|
||||
static int ladspa_handle_load_by_path(struct plugin *impl, const char *path)
|
||||
{
|
||||
int res;
|
||||
void *handle = NULL;
|
||||
LADSPA_Descriptor_Function desc_func;
|
||||
|
||||
handle = dlopen(path, RTLD_NOW);
|
||||
if (handle == NULL) {
|
||||
spa_log_debug(impl->log, "failed to open '%s': %s", path, dlerror());
|
||||
res = -ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
spa_log_info(impl->log, "successfully opened '%s'", path);
|
||||
|
||||
desc_func = (LADSPA_Descriptor_Function) dlsym(handle, "ladspa_descriptor");
|
||||
if (desc_func == NULL) {
|
||||
spa_log_warn(impl->log, "cannot find descriptor function in '%s': %s", path, dlerror());
|
||||
res = -ENOSYS;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
impl->hndl = handle;
|
||||
impl->desc_func = desc_func;
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
if (handle)
|
||||
dlclose(handle);
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline const char *split_walk(const char *str, const char *delimiter, size_t * len, const char **state)
|
||||
{
|
||||
const char *s = *state ? *state : str;
|
||||
|
||||
s += strspn(s, delimiter);
|
||||
if (*s == '\0')
|
||||
return NULL;
|
||||
|
||||
*len = strcspn(s, delimiter);
|
||||
*state = s + *len;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int load_ladspa_plugin(struct plugin *impl, const char *path)
|
||||
{
|
||||
int res = -ENOENT;
|
||||
|
||||
if (path[0] != '/') {
|
||||
const char *search_dirs, *p, *state = NULL;
|
||||
char filename[PATH_MAX];
|
||||
size_t len;
|
||||
|
||||
search_dirs = getenv("LADSPA_PATH");
|
||||
if (!search_dirs)
|
||||
search_dirs = "/usr/lib64/ladspa:/usr/lib/ladspa:" LIBDIR;
|
||||
|
||||
/*
|
||||
* set the errno for the case when `ladspa_handle_load_by_path()`
|
||||
* is never called, which can only happen if the supplied
|
||||
* LADSPA_PATH contains too long paths
|
||||
*/
|
||||
res = -ENAMETOOLONG;
|
||||
|
||||
while ((p = split_walk(search_dirs, ":", &len, &state))) {
|
||||
int namelen;
|
||||
|
||||
if (len >= sizeof(filename))
|
||||
continue;
|
||||
|
||||
namelen = snprintf(filename, sizeof(filename), "%.*s/%s.so", (int) len, p, path);
|
||||
if (namelen < 0 || (size_t) namelen >= sizeof(filename))
|
||||
continue;
|
||||
|
||||
res = ladspa_handle_load_by_path(impl, filename);
|
||||
if (res >= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
res = ladspa_handle_load_by_path(impl, path);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
|
||||
{
|
||||
struct plugin *impl;
|
||||
|
||||
spa_return_val_if_fail(handle != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(interface != NULL, -EINVAL);
|
||||
|
||||
impl = (struct plugin *) handle;
|
||||
|
||||
if (spa_streq(type, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin))
|
||||
*interface = &impl->plugin;
|
||||
else
|
||||
return -ENOENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int impl_clear(struct spa_handle *handle)
|
||||
{
|
||||
struct plugin *impl = (struct plugin *)handle;
|
||||
if (impl->hndl)
|
||||
dlclose(impl->hndl);
|
||||
impl->hndl = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
impl_get_size(const struct spa_handle_factory *factory,
|
||||
const struct spa_dict *params)
|
||||
{
|
||||
return sizeof(struct plugin);
|
||||
}
|
||||
|
||||
static int
|
||||
impl_init(const struct spa_handle_factory *factory,
|
||||
struct spa_handle *handle,
|
||||
const struct spa_dict *info,
|
||||
const struct spa_support *support,
|
||||
uint32_t n_support)
|
||||
{
|
||||
struct plugin *impl;
|
||||
uint32_t i;
|
||||
int res;
|
||||
const char *path = NULL;
|
||||
|
||||
handle->get_interface = impl_get_interface;
|
||||
handle->clear = impl_clear;
|
||||
|
||||
impl = (struct plugin *) handle;
|
||||
|
||||
impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
|
||||
|
||||
for (i = 0; info && i < info->n_items; i++) {
|
||||
const char *k = info->items[i].key;
|
||||
const char *s = info->items[i].value;
|
||||
if (spa_streq(k, "filter.graph.path"))
|
||||
path = s;
|
||||
}
|
||||
if (path == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if ((res = load_ladspa_plugin(impl, path)) < 0) {
|
||||
spa_log_error(impl->log, "failed to load plugin '%s': %s",
|
||||
path, spa_strerror(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
impl->plugin.iface = SPA_INTERFACE_INIT(
|
||||
SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin,
|
||||
SPA_VERSION_FGA_PLUGIN,
|
||||
&impl_plugin, impl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spa_interface_info impl_interfaces[] = {
|
||||
{ SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin },
|
||||
};
|
||||
|
||||
static int
|
||||
impl_enum_interface_info(const struct spa_handle_factory *factory,
|
||||
const struct spa_interface_info **info,
|
||||
uint32_t *index)
|
||||
{
|
||||
spa_return_val_if_fail(factory != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(info != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(index != NULL, -EINVAL);
|
||||
|
||||
switch (*index) {
|
||||
case 0:
|
||||
*info = &impl_interfaces[*index];
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
(*index)++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct spa_handle_factory spa_fga_plugin_ladspa_factory = {
|
||||
SPA_VERSION_HANDLE_FACTORY,
|
||||
"filter.graph.plugin.ladspa",
|
||||
NULL,
|
||||
impl_get_size,
|
||||
impl_init,
|
||||
impl_enum_interface_info,
|
||||
};
|
||||
|
||||
SPA_EXPORT
|
||||
int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
|
||||
{
|
||||
spa_return_val_if_fail(factory != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(index != NULL, -EINVAL);
|
||||
|
||||
switch (*index) {
|
||||
case 0:
|
||||
*factory = &spa_fga_plugin_ladspa_factory;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
(*index)++;
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -1,604 +0,0 @@
|
|||
/* PipeWire */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <lilv/lilv.h>
|
||||
|
||||
#include <spa/utils/defs.h>
|
||||
#include <spa/utils/list.h>
|
||||
#include <spa/utils/string.h>
|
||||
#include <spa/support/loop.h>
|
||||
#include <spa/support/log.h>
|
||||
|
||||
#if defined __has_include
|
||||
# if __has_include (<lv2/atom/atom.h>)
|
||||
|
||||
#include <lv2/atom/atom.h>
|
||||
#include <lv2/buf-size/buf-size.h>
|
||||
#include <lv2/worker/worker.h>
|
||||
#include <lv2/options/options.h>
|
||||
#include <lv2/parameters/parameters.h>
|
||||
|
||||
# else
|
||||
|
||||
#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/buf-size/buf-size.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/options/options.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/parameters/parameters.h>
|
||||
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
#include "audio-plugin.h"
|
||||
|
||||
static struct context *_context;
|
||||
|
||||
typedef struct URITable {
|
||||
char **data;
|
||||
size_t alloc;
|
||||
size_t len;
|
||||
} URITable;
|
||||
|
||||
static void uri_table_init(URITable *table)
|
||||
{
|
||||
table->data = NULL;
|
||||
table->len = table->alloc = 0;
|
||||
}
|
||||
|
||||
static void uri_table_destroy(URITable *table)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < table->len; i++)
|
||||
free(table->data[i]);
|
||||
free(table->data);
|
||||
uri_table_init(table);
|
||||
}
|
||||
|
||||
static LV2_URID uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
|
||||
{
|
||||
URITable *table = (URITable*)handle;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < table->len; i++)
|
||||
if (spa_streq(table->data[i], uri))
|
||||
return i+1;
|
||||
|
||||
if (table->len == table->alloc) {
|
||||
table->alloc += 64;
|
||||
table->data = realloc(table->data, table->alloc * sizeof(char *));
|
||||
}
|
||||
table->data[table->len++] = strdup(uri);
|
||||
return table->len;
|
||||
}
|
||||
|
||||
static const char *uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
|
||||
{
|
||||
URITable *table = (URITable*)handle;
|
||||
if (urid > 0 && urid <= table->len)
|
||||
return table->data[urid-1];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct context {
|
||||
int ref;
|
||||
LilvWorld *world;
|
||||
|
||||
LilvNode *lv2_InputPort;
|
||||
LilvNode *lv2_OutputPort;
|
||||
LilvNode *lv2_AudioPort;
|
||||
LilvNode *lv2_ControlPort;
|
||||
LilvNode *lv2_Optional;
|
||||
LilvNode *atom_AtomPort;
|
||||
LilvNode *atom_Sequence;
|
||||
LilvNode *urid_map;
|
||||
LilvNode *powerOf2BlockLength;
|
||||
LilvNode *fixedBlockLength;
|
||||
LilvNode *boundedBlockLength;
|
||||
LilvNode* worker_schedule;
|
||||
LilvNode* worker_iface;
|
||||
|
||||
URITable uri_table;
|
||||
LV2_URID_Map map;
|
||||
LV2_Feature map_feature;
|
||||
LV2_URID_Unmap unmap;
|
||||
LV2_Feature unmap_feature;
|
||||
|
||||
LV2_URID atom_Int;
|
||||
LV2_URID atom_Float;
|
||||
};
|
||||
|
||||
#define context_map(c,uri) ((c)->map.map((c)->map.handle,(uri)))
|
||||
|
||||
static void context_free(struct context *c)
|
||||
{
|
||||
if (c->world) {
|
||||
lilv_node_free(c->worker_schedule);
|
||||
lilv_node_free(c->powerOf2BlockLength);
|
||||
lilv_node_free(c->fixedBlockLength);
|
||||
lilv_node_free(c->boundedBlockLength);
|
||||
lilv_node_free(c->urid_map);
|
||||
lilv_node_free(c->atom_Sequence);
|
||||
lilv_node_free(c->atom_AtomPort);
|
||||
lilv_node_free(c->lv2_Optional);
|
||||
lilv_node_free(c->lv2_ControlPort);
|
||||
lilv_node_free(c->lv2_AudioPort);
|
||||
lilv_node_free(c->lv2_OutputPort);
|
||||
lilv_node_free(c->lv2_InputPort);
|
||||
lilv_world_free(c->world);
|
||||
}
|
||||
uri_table_destroy(&c->uri_table);
|
||||
free(c);
|
||||
}
|
||||
|
||||
static const LV2_Feature buf_size_features[3] = {
|
||||
{ LV2_BUF_SIZE__powerOf2BlockLength, NULL },
|
||||
{ LV2_BUF_SIZE__fixedBlockLength, NULL },
|
||||
{ LV2_BUF_SIZE__boundedBlockLength, NULL },
|
||||
};
|
||||
|
||||
static struct context *context_new(void)
|
||||
{
|
||||
struct context *c;
|
||||
|
||||
c = calloc(1, sizeof(*c));
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
uri_table_init(&c->uri_table);
|
||||
c->world = lilv_world_new();
|
||||
if (c->world == NULL)
|
||||
goto error;
|
||||
|
||||
lilv_world_load_all(c->world);
|
||||
|
||||
c->lv2_InputPort = lilv_new_uri(c->world, LV2_CORE__InputPort);
|
||||
c->lv2_OutputPort = lilv_new_uri(c->world, LV2_CORE__OutputPort);
|
||||
c->lv2_AudioPort = lilv_new_uri(c->world, LV2_CORE__AudioPort);
|
||||
c->lv2_ControlPort = lilv_new_uri(c->world, LV2_CORE__ControlPort);
|
||||
c->lv2_Optional = lilv_new_uri(c->world, LV2_CORE__connectionOptional);
|
||||
c->atom_AtomPort = lilv_new_uri(c->world, LV2_ATOM__AtomPort);
|
||||
c->atom_Sequence = lilv_new_uri(c->world, LV2_ATOM__Sequence);
|
||||
c->urid_map = lilv_new_uri(c->world, LV2_URID__map);
|
||||
c->powerOf2BlockLength = lilv_new_uri(c->world, LV2_BUF_SIZE__powerOf2BlockLength);
|
||||
c->fixedBlockLength = lilv_new_uri(c->world, LV2_BUF_SIZE__fixedBlockLength);
|
||||
c->boundedBlockLength = lilv_new_uri(c->world, LV2_BUF_SIZE__boundedBlockLength);
|
||||
c->worker_schedule = lilv_new_uri(c->world, LV2_WORKER__schedule);
|
||||
c->worker_iface = lilv_new_uri(c->world, LV2_WORKER__interface);
|
||||
|
||||
c->map.handle = &c->uri_table;
|
||||
c->map.map = uri_table_map;
|
||||
c->map_feature.URI = LV2_URID_MAP_URI;
|
||||
c->map_feature.data = &c->map;
|
||||
c->unmap.handle = &c->uri_table;
|
||||
c->unmap.unmap = uri_table_unmap;
|
||||
c->unmap_feature.URI = LV2_URID_UNMAP_URI;
|
||||
c->unmap_feature.data = &c->unmap;
|
||||
|
||||
c->atom_Int = context_map(c, LV2_ATOM__Int);
|
||||
c->atom_Float = context_map(c, LV2_ATOM__Float);
|
||||
|
||||
return c;
|
||||
error:
|
||||
context_free(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct context *context_ref(void)
|
||||
{
|
||||
if (_context == NULL) {
|
||||
_context = context_new();
|
||||
if (_context == NULL)
|
||||
return NULL;
|
||||
}
|
||||
_context->ref++;
|
||||
return _context;
|
||||
}
|
||||
|
||||
static void context_unref(struct context *context)
|
||||
{
|
||||
if (--_context->ref == 0) {
|
||||
context_free(_context);
|
||||
_context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct plugin {
|
||||
struct spa_handle handle;
|
||||
struct spa_fga_plugin plugin;
|
||||
|
||||
struct spa_log *log;
|
||||
struct spa_loop *data_loop;
|
||||
struct spa_loop *main_loop;
|
||||
|
||||
struct context *c;
|
||||
const LilvPlugin *p;
|
||||
};
|
||||
|
||||
struct descriptor {
|
||||
struct spa_fga_descriptor desc;
|
||||
struct plugin *p;
|
||||
};
|
||||
|
||||
struct instance {
|
||||
struct descriptor *desc;
|
||||
struct plugin *p;
|
||||
|
||||
LilvInstance *instance;
|
||||
LV2_Worker_Schedule work_schedule;
|
||||
LV2_Feature work_schedule_feature;
|
||||
LV2_Options_Option options[6];
|
||||
LV2_Feature options_feature;
|
||||
|
||||
const LV2_Feature *features[7];
|
||||
|
||||
const LV2_Worker_Interface *work_iface;
|
||||
|
||||
int32_t block_length;
|
||||
LV2_Atom empty_atom;
|
||||
};
|
||||
|
||||
static int
|
||||
do_respond(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
|
||||
size_t size, void *user_data)
|
||||
{
|
||||
struct instance *i = (struct instance*)user_data;
|
||||
i->work_iface->work_response(i->instance->lv2_handle, size, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Called by the plugin to respond to non-RT work. */
|
||||
static LV2_Worker_Status
|
||||
work_respond(LV2_Worker_Respond_Handle handle, uint32_t size, const void *data)
|
||||
{
|
||||
struct instance *i = (struct instance*)handle;
|
||||
spa_loop_invoke(i->p->data_loop, do_respond, 1, data, size, false, i);
|
||||
return LV2_WORKER_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
do_schedule(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
|
||||
size_t size, void *user_data)
|
||||
{
|
||||
struct instance *i = (struct instance*)user_data;
|
||||
i->work_iface->work(i->instance->lv2_handle, work_respond, i, size, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Called by the plugin to schedule non-RT work. */
|
||||
static LV2_Worker_Status
|
||||
work_schedule(LV2_Worker_Schedule_Handle handle, uint32_t size, const void *data)
|
||||
{
|
||||
struct instance *i = (struct instance*)handle;
|
||||
spa_loop_invoke(i->p->main_loop, do_schedule, 1, data, size, false, i);
|
||||
return LV2_WORKER_SUCCESS;
|
||||
}
|
||||
|
||||
static void *lv2_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor *desc,
|
||||
unsigned long SampleRate, int index, const char *config)
|
||||
{
|
||||
struct descriptor *d = (struct descriptor*)desc;
|
||||
struct plugin *p = d->p;
|
||||
struct context *c = p->c;
|
||||
struct instance *i;
|
||||
uint32_t n, n_features = 0;
|
||||
static const int32_t min_block_length = 1;
|
||||
static const int32_t max_block_length = 8192;
|
||||
static const int32_t seq_size = 32768;
|
||||
float fsample_rate = SampleRate;
|
||||
|
||||
i = calloc(1, sizeof(*i));
|
||||
if (i == NULL)
|
||||
return NULL;
|
||||
|
||||
i->block_length = 1024;
|
||||
i->desc = d;
|
||||
i->p = p;
|
||||
i->features[n_features++] = &c->map_feature;
|
||||
i->features[n_features++] = &c->unmap_feature;
|
||||
i->features[n_features++] = &buf_size_features[0];
|
||||
i->features[n_features++] = &buf_size_features[1];
|
||||
i->features[n_features++] = &buf_size_features[2];
|
||||
if (lilv_plugin_has_feature(p->p, c->worker_schedule)) {
|
||||
i->work_schedule.handle = i;
|
||||
i->work_schedule.schedule_work = work_schedule;
|
||||
i->work_schedule_feature.URI = LV2_WORKER__schedule;
|
||||
i->work_schedule_feature.data = &i->work_schedule;
|
||||
i->features[n_features++] = &i->work_schedule_feature;
|
||||
}
|
||||
|
||||
i->options[0] = (LV2_Options_Option) { LV2_OPTIONS_INSTANCE, 0,
|
||||
context_map(c, LV2_BUF_SIZE__minBlockLength), sizeof(int32_t),
|
||||
c->atom_Int, &min_block_length };
|
||||
i->options[1] = (LV2_Options_Option) { LV2_OPTIONS_INSTANCE, 0,
|
||||
context_map(c, LV2_BUF_SIZE__maxBlockLength), sizeof(int32_t),
|
||||
c->atom_Int, &max_block_length };
|
||||
i->options[2] = (LV2_Options_Option) { LV2_OPTIONS_INSTANCE, 0,
|
||||
context_map(c, LV2_BUF_SIZE__sequenceSize), sizeof(int32_t),
|
||||
c->atom_Int, &seq_size };
|
||||
i->options[3] = (LV2_Options_Option) { LV2_OPTIONS_INSTANCE, 0,
|
||||
context_map(c, "http://lv2plug.in/ns/ext/buf-size#nominalBlockLength"), sizeof(int32_t),
|
||||
c->atom_Int, &i->block_length },
|
||||
i->options[4] = (LV2_Options_Option) { LV2_OPTIONS_INSTANCE, 0,
|
||||
context_map(c, LV2_PARAMETERS__sampleRate), sizeof(float),
|
||||
c->atom_Float, &fsample_rate };
|
||||
i->options[5] = (LV2_Options_Option) { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL };
|
||||
|
||||
i->options_feature.URI = LV2_OPTIONS__options;
|
||||
i->options_feature.data = i->options;
|
||||
i->features[n_features++] = &i->options_feature;
|
||||
|
||||
i->instance = lilv_plugin_instantiate(p->p, SampleRate, i->features);
|
||||
if (i->instance == NULL) {
|
||||
free(i);
|
||||
return NULL;
|
||||
}
|
||||
if (lilv_plugin_has_extension_data(p->p, c->worker_iface)) {
|
||||
i->work_iface = (const LV2_Worker_Interface*)
|
||||
lilv_instance_get_extension_data(i->instance, LV2_WORKER__interface);
|
||||
}
|
||||
for (n = 0; n < desc->n_ports; n++) {
|
||||
const LilvPort *port = lilv_plugin_get_port_by_index(p->p, n);
|
||||
if (lilv_port_is_a(p->p, port, c->atom_AtomPort)) {
|
||||
lilv_instance_connect_port(i->instance, n, &i->empty_atom);
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void lv2_cleanup(void *instance)
|
||||
{
|
||||
struct instance *i = instance;
|
||||
lilv_instance_free(i->instance);
|
||||
free(i);
|
||||
}
|
||||
|
||||
static void lv2_connect_port(void *instance, unsigned long port, float *data)
|
||||
{
|
||||
struct instance *i = instance;
|
||||
lilv_instance_connect_port(i->instance, port, data);
|
||||
}
|
||||
|
||||
static void lv2_activate(void *instance)
|
||||
{
|
||||
struct instance *i = instance;
|
||||
lilv_instance_activate(i->instance);
|
||||
}
|
||||
|
||||
static void lv2_deactivate(void *instance)
|
||||
{
|
||||
struct instance *i = instance;
|
||||
lilv_instance_deactivate(i->instance);
|
||||
}
|
||||
|
||||
static void lv2_run(void *instance, unsigned long SampleCount)
|
||||
{
|
||||
struct instance *i = instance;
|
||||
lilv_instance_run(i->instance, SampleCount);
|
||||
if (i->work_iface != NULL && i->work_iface->end_run != NULL)
|
||||
i->work_iface->end_run(i->instance);
|
||||
}
|
||||
|
||||
static void lv2_free(const struct spa_fga_descriptor *desc)
|
||||
{
|
||||
struct descriptor *d = (struct descriptor*)desc;
|
||||
free((char*)d->desc.name);
|
||||
free(d->desc.ports);
|
||||
free(d);
|
||||
}
|
||||
|
||||
static const struct spa_fga_descriptor *lv2_plugin_make_desc(void *plugin, const char *name)
|
||||
{
|
||||
struct plugin *p = (struct plugin *)plugin;
|
||||
struct context *c = p->c;
|
||||
struct descriptor *desc;
|
||||
uint32_t i;
|
||||
float *mins, *maxes, *controls;
|
||||
|
||||
desc = calloc(1, sizeof(*desc));
|
||||
if (desc == NULL)
|
||||
return NULL;
|
||||
|
||||
desc->p = p;
|
||||
desc->desc.instantiate = lv2_instantiate;
|
||||
desc->desc.cleanup = lv2_cleanup;
|
||||
desc->desc.connect_port = lv2_connect_port;
|
||||
desc->desc.activate = lv2_activate;
|
||||
desc->desc.deactivate = lv2_deactivate;
|
||||
desc->desc.run = lv2_run;
|
||||
|
||||
desc->desc.free = lv2_free;
|
||||
|
||||
desc->desc.name = strdup(name);
|
||||
desc->desc.flags = 0;
|
||||
|
||||
desc->desc.n_ports = lilv_plugin_get_num_ports(p->p);
|
||||
desc->desc.ports = calloc(desc->desc.n_ports, sizeof(struct spa_fga_port));
|
||||
|
||||
mins = alloca(desc->desc.n_ports * sizeof(float));
|
||||
maxes = alloca(desc->desc.n_ports * sizeof(float));
|
||||
controls = alloca(desc->desc.n_ports * sizeof(float));
|
||||
|
||||
lilv_plugin_get_port_ranges_float(p->p, mins, maxes, controls);
|
||||
|
||||
for (i = 0; i < desc->desc.n_ports; i++) {
|
||||
const LilvPort *port = lilv_plugin_get_port_by_index(p->p, i);
|
||||
const LilvNode *symbol = lilv_port_get_symbol(p->p, port);
|
||||
struct spa_fga_port *fp = &desc->desc.ports[i];
|
||||
|
||||
fp->index = i;
|
||||
fp->name = strdup(lilv_node_as_string(symbol));
|
||||
|
||||
fp->flags = 0;
|
||||
if (lilv_port_is_a(p->p, port, c->lv2_InputPort))
|
||||
fp->flags |= SPA_FGA_PORT_INPUT;
|
||||
if (lilv_port_is_a(p->p, port, c->lv2_OutputPort))
|
||||
fp->flags |= SPA_FGA_PORT_OUTPUT;
|
||||
if (lilv_port_is_a(p->p, port, c->lv2_ControlPort))
|
||||
fp->flags |= SPA_FGA_PORT_CONTROL;
|
||||
if (lilv_port_is_a(p->p, port, c->lv2_AudioPort))
|
||||
fp->flags |= SPA_FGA_PORT_AUDIO;
|
||||
|
||||
fp->hint = 0;
|
||||
fp->min = mins[i];
|
||||
fp->max = maxes[i];
|
||||
fp->def = controls[i];
|
||||
}
|
||||
return &desc->desc;
|
||||
}
|
||||
|
||||
static struct spa_fga_plugin_methods impl_plugin = {
|
||||
SPA_VERSION_FGA_PLUGIN_METHODS,
|
||||
.make_desc = lv2_plugin_make_desc,
|
||||
};
|
||||
|
||||
static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
|
||||
{
|
||||
struct plugin *impl;
|
||||
|
||||
spa_return_val_if_fail(handle != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(interface != NULL, -EINVAL);
|
||||
|
||||
impl = (struct plugin *) handle;
|
||||
|
||||
if (spa_streq(type, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin))
|
||||
*interface = &impl->plugin;
|
||||
else
|
||||
return -ENOENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int impl_clear(struct spa_handle *handle)
|
||||
{
|
||||
struct plugin *p = (struct plugin *)handle;
|
||||
context_unref(p->c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
impl_get_size(const struct spa_handle_factory *factory,
|
||||
const struct spa_dict *params)
|
||||
{
|
||||
return sizeof(struct plugin);
|
||||
}
|
||||
|
||||
static int
|
||||
impl_init(const struct spa_handle_factory *factory,
|
||||
struct spa_handle *handle,
|
||||
const struct spa_dict *info,
|
||||
const struct spa_support *support,
|
||||
uint32_t n_support)
|
||||
{
|
||||
struct plugin *impl;
|
||||
uint32_t i;
|
||||
int res;
|
||||
const char *path = NULL;
|
||||
const LilvPlugins *plugins;
|
||||
LilvNode *uri;
|
||||
|
||||
handle->get_interface = impl_get_interface;
|
||||
handle->clear = impl_clear;
|
||||
|
||||
impl = (struct plugin *) handle;
|
||||
impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
|
||||
impl->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
|
||||
impl->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
|
||||
|
||||
for (i = 0; info && i < info->n_items; i++) {
|
||||
const char *k = info->items[i].key;
|
||||
const char *s = info->items[i].value;
|
||||
if (spa_streq(k, "filter.graph.path"))
|
||||
path = s;
|
||||
}
|
||||
if (path == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
impl->c = context_ref();
|
||||
if (impl->c == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
uri = lilv_new_uri(impl->c->world, path);
|
||||
if (uri == NULL) {
|
||||
spa_log_warn(impl->log, "invalid URI %s", path);
|
||||
res = -EINVAL;
|
||||
goto error_cleanup;
|
||||
}
|
||||
|
||||
plugins = lilv_world_get_all_plugins(impl->c->world);
|
||||
impl->p = lilv_plugins_get_by_uri(plugins, uri);
|
||||
lilv_node_free(uri);
|
||||
|
||||
if (impl->p == NULL) {
|
||||
spa_log_warn(impl->log, "can't load plugin %s", path);
|
||||
res = -EINVAL;
|
||||
goto error_cleanup;
|
||||
}
|
||||
impl->plugin.iface = SPA_INTERFACE_INIT(
|
||||
SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin,
|
||||
SPA_VERSION_FGA_PLUGIN,
|
||||
&impl_plugin, impl);
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup:
|
||||
if (impl->c)
|
||||
context_unref(impl->c);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static const struct spa_interface_info impl_interfaces[] = {
|
||||
{ SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin },
|
||||
};
|
||||
|
||||
static int
|
||||
impl_enum_interface_info(const struct spa_handle_factory *factory,
|
||||
const struct spa_interface_info **info,
|
||||
uint32_t *index)
|
||||
{
|
||||
spa_return_val_if_fail(factory != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(info != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(index != NULL, -EINVAL);
|
||||
|
||||
switch (*index) {
|
||||
case 0:
|
||||
*info = &impl_interfaces[*index];
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
(*index)++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct spa_handle_factory spa_fga_plugin_lv2_factory = {
|
||||
SPA_VERSION_HANDLE_FACTORY,
|
||||
"filter.graph.plugin.lv2",
|
||||
NULL,
|
||||
impl_get_size,
|
||||
impl_init,
|
||||
impl_enum_interface_info,
|
||||
};
|
||||
|
||||
SPA_EXPORT
|
||||
int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
|
||||
{
|
||||
spa_return_val_if_fail(factory != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(index != NULL, -EINVAL);
|
||||
|
||||
switch (*index) {
|
||||
case 0:
|
||||
*factory = &spa_fga_plugin_lv2_factory;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
(*index)++;
|
||||
return 1;
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,181 +0,0 @@
|
|||
/* Copyright (c) 2013 Julien Pommier ( pommier@modartt.com )
|
||||
|
||||
Based on original fortran 77 code from FFTPACKv4 from NETLIB,
|
||||
authored by Dr Paul Swarztrauber of NCAR, in 1985.
|
||||
|
||||
As confirmed by the NCAR fftpack software curators, the following
|
||||
FFTPACKv5 license applies to FFTPACKv4 sources. My changes are
|
||||
released under the same terms.
|
||||
|
||||
FFTPACK license:
|
||||
|
||||
http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html
|
||||
|
||||
Copyright (c) 2004 the University Corporation for Atmospheric
|
||||
Research ("UCAR"). All rights reserved. Developed by NCAR's
|
||||
Computational and Information Systems Laboratory, UCAR,
|
||||
www.cisl.ucar.edu.
|
||||
|
||||
Redistribution and use of the Software in source and binary forms,
|
||||
with or without modification, is permitted provided that the
|
||||
following conditions are met:
|
||||
|
||||
- Neither the names of NCAR's Computational and Information Systems
|
||||
Laboratory, the University Corporation for Atmospheric Research,
|
||||
nor the names of its sponsors or contributors may be used to
|
||||
endorse or promote products derived from this Software without
|
||||
specific prior written permission.
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notices, this list of conditions, and the disclaimer below.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions, and the disclaimer below in the
|
||||
documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
PFFFT : a Pretty Fast FFT.
|
||||
|
||||
This is basically an adaptation of the single precision fftpack
|
||||
(v4) as found on netlib taking advantage of SIMD instruction found
|
||||
on cpus such as intel x86 (SSE1), powerpc (Altivec), and arm (NEON).
|
||||
|
||||
For architectures where no SIMD instruction is available, the code
|
||||
falls back to a scalar version.
|
||||
|
||||
Restrictions:
|
||||
|
||||
- 1D transforms only, with 32-bit single precision.
|
||||
|
||||
- supports only transforms for inputs of length N of the form
|
||||
N=(2^a)*(3^b)*(5^c), a >= 5, b >=0, c >= 0 (32, 48, 64, 96, 128,
|
||||
144, 160, etc are all acceptable lengths). Performance is best for
|
||||
128<=N<=8192.
|
||||
|
||||
- all (float*) pointers in the functions below are expected to
|
||||
have an "simd-compatible" alignment, that is 16 bytes on x86 and
|
||||
powerpc CPUs.
|
||||
|
||||
You can allocate such buffers with the functions
|
||||
pffft_aligned_malloc / pffft_aligned_free (or with stuff like
|
||||
posix_memalign..)
|
||||
|
||||
*/
|
||||
|
||||
#ifndef PFFFT_H
|
||||
#define PFFFT_H
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* opaque struct holding internal stuff (precomputed twiddle factors)
|
||||
this struct can be shared by many threads as it contains only
|
||||
read-only data.
|
||||
*/
|
||||
typedef struct PFFFT_Setup PFFFT_Setup;
|
||||
|
||||
/* direction of the transform */
|
||||
typedef enum { PFFFT_FORWARD, PFFFT_BACKWARD } pffft_direction_t;
|
||||
|
||||
/* type of transform */
|
||||
typedef enum { PFFFT_REAL, PFFFT_COMPLEX } pffft_transform_t;
|
||||
|
||||
/*
|
||||
prepare for performing transforms of size N -- the returned
|
||||
PFFFT_Setup structure is read-only so it can safely be shared by
|
||||
multiple concurrent threads.
|
||||
*/
|
||||
PFFFT_Setup *pffft_new_setup(int N, pffft_transform_t transform);
|
||||
void pffft_destroy_setup(PFFFT_Setup *);
|
||||
/*
|
||||
Perform a Fourier transform , The z-domain data is stored in the
|
||||
most efficient order for transforming it back, or using it for
|
||||
convolution. If you need to have its content sorted in the
|
||||
"usual" way, that is as an array of interleaved complex numbers,
|
||||
either use pffft_transform_ordered , or call pffft_zreorder after
|
||||
the forward fft, and before the backward fft.
|
||||
|
||||
Transforms are not scaled: PFFFT_BACKWARD(PFFFT_FORWARD(x)) = N*x.
|
||||
Typically you will want to scale the backward transform by 1/N.
|
||||
|
||||
The 'work' pointer should point to an area of N (2*N for complex
|
||||
fft) floats, properly aligned. If 'work' is NULL, then stack will
|
||||
be used instead (this is probably the best strategy for small
|
||||
FFTs, say for N < 16384).
|
||||
|
||||
input and output may alias.
|
||||
*/
|
||||
void pffft_transform(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction);
|
||||
|
||||
/*
|
||||
Similar to pffft_transform, but makes sure that the output is
|
||||
ordered as expected (interleaved complex numbers). This is
|
||||
similar to calling pffft_transform and then pffft_zreorder.
|
||||
|
||||
input and output may alias.
|
||||
*/
|
||||
void pffft_transform_ordered(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction);
|
||||
|
||||
/*
|
||||
call pffft_zreorder(.., PFFFT_FORWARD) after pffft_transform(...,
|
||||
PFFFT_FORWARD) if you want to have the frequency components in
|
||||
the correct "canonical" order, as interleaved complex numbers.
|
||||
|
||||
(for real transforms, both 0-frequency and half frequency
|
||||
components, which are real, are assembled in the first entry as
|
||||
F(0)+i*F(n/2+1). Note that the original fftpack did place
|
||||
F(n/2+1) at the end of the arrays).
|
||||
|
||||
input and output should not alias.
|
||||
*/
|
||||
void pffft_zreorder(PFFFT_Setup *setup, const float *input, float *output, pffft_direction_t direction);
|
||||
|
||||
/*
|
||||
Perform a multiplication of the frequency components of dft_a and
|
||||
dft_b and accumulate them into dft_ab. The arrays should have
|
||||
been obtained with pffft_transform(.., PFFFT_FORWARD) and should
|
||||
*not* have been reordered with pffft_zreorder (otherwise just
|
||||
perform the operation yourself as the dft coefs are stored as
|
||||
interleaved complex numbers).
|
||||
|
||||
the operation performed is: dft_ab = dft_c + (dft_a * fdt_b)*scaling
|
||||
|
||||
The dft_a, dft_b and dft_ab pointers may alias.
|
||||
*/
|
||||
void pffft_zconvolve_accumulate(PFFFT_Setup *setup, const float *dft_a, const float *dft_b, const float *dft_c, float *dft_ab, float scaling);
|
||||
|
||||
void pffft_zconvolve(PFFFT_Setup *setup, const float *dft_a, const float *dft_b, float *dft_ab, float scaling);
|
||||
|
||||
/*
|
||||
the float buffers must have the correct alignment (16-byte boundary
|
||||
on intel and powerpc). This function may be used to obtain such
|
||||
correctly aligned buffers.
|
||||
*/
|
||||
void *pffft_aligned_malloc(size_t nb_bytes);
|
||||
void pffft_aligned_free(void *);
|
||||
|
||||
/* return 4 or 1 depending on whether support for SSE/Altivec instructions was enabled when building pffft.c */
|
||||
int pffft_simd_size(void);
|
||||
|
||||
void pffft_select_cpu(int flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // PFFFT_H
|
||||
|
|
@ -1,555 +0,0 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include <spa/utils/json.h>
|
||||
#include <spa/support/loop.h>
|
||||
#include <spa/support/log.h>
|
||||
|
||||
#include "audio-plugin.h"
|
||||
#include "convolver.h"
|
||||
#include "audio-dsp.h"
|
||||
|
||||
#include <mysofa.h>
|
||||
|
||||
struct plugin {
|
||||
struct spa_handle handle;
|
||||
struct spa_fga_plugin plugin;
|
||||
|
||||
struct spa_fga_dsp *dsp;
|
||||
struct spa_log *log;
|
||||
struct spa_loop *data_loop;
|
||||
struct spa_loop *main_loop;
|
||||
uint32_t quantum_limit;
|
||||
};
|
||||
|
||||
struct spatializer_impl {
|
||||
struct plugin *plugin;
|
||||
|
||||
struct spa_fga_dsp *dsp;
|
||||
struct spa_log *log;
|
||||
|
||||
unsigned long rate;
|
||||
float *port[6];
|
||||
int n_samples, blocksize, tailsize;
|
||||
float *tmp[2];
|
||||
|
||||
struct MYSOFA_EASY *sofa;
|
||||
unsigned int interpolate:1;
|
||||
struct convolver *l_conv[3];
|
||||
struct convolver *r_conv[3];
|
||||
};
|
||||
|
||||
static void * spatializer_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor,
|
||||
unsigned long SampleRate, int index, const char *config)
|
||||
{
|
||||
struct plugin *pl = SPA_CONTAINER_OF(plugin, struct plugin, plugin);
|
||||
struct spatializer_impl *impl;
|
||||
struct spa_json it[1];
|
||||
const char *val;
|
||||
char key[256];
|
||||
char filename[PATH_MAX] = "";
|
||||
int len;
|
||||
|
||||
errno = EINVAL;
|
||||
if (config == NULL) {
|
||||
spa_log_error(pl->log, "spatializer: no config was given");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) {
|
||||
spa_log_error(pl->log, "spatializer: expected object in config");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
impl = calloc(1, sizeof(*impl));
|
||||
if (impl == NULL) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
impl->plugin = pl;
|
||||
impl->dsp = pl->dsp;
|
||||
impl->log = pl->log;
|
||||
|
||||
while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) {
|
||||
if (spa_streq(key, "blocksize")) {
|
||||
if (spa_json_parse_int(val, len, &impl->blocksize) <= 0) {
|
||||
spa_log_error(impl->log, "spatializer:blocksize requires a number");
|
||||
errno = EINVAL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else if (spa_streq(key, "tailsize")) {
|
||||
if (spa_json_parse_int(val, len, &impl->tailsize) <= 0) {
|
||||
spa_log_error(impl->log, "spatializer:tailsize requires a number");
|
||||
errno = EINVAL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else if (spa_streq(key, "filename")) {
|
||||
if (spa_json_parse_stringn(val, len, filename, sizeof(filename)) <= 0) {
|
||||
spa_log_error(impl->log, "spatializer:filename requires a string");
|
||||
errno = EINVAL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!filename[0]) {
|
||||
spa_log_error(impl->log, "spatializer:filename was not given");
|
||||
errno = EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
int ret = MYSOFA_OK;
|
||||
|
||||
impl->sofa = mysofa_open_cached(filename, SampleRate, &impl->n_samples, &ret);
|
||||
|
||||
if (ret != MYSOFA_OK) {
|
||||
const char *reason;
|
||||
switch (ret) {
|
||||
case MYSOFA_INVALID_FORMAT:
|
||||
reason = "Invalid format";
|
||||
errno = EINVAL;
|
||||
break;
|
||||
case MYSOFA_UNSUPPORTED_FORMAT:
|
||||
reason = "Unsupported format";
|
||||
errno = ENOTSUP;
|
||||
break;
|
||||
case MYSOFA_NO_MEMORY:
|
||||
reason = "No memory";
|
||||
errno = ENOMEM;
|
||||
break;
|
||||
case MYSOFA_READ_ERROR:
|
||||
reason = "Read error";
|
||||
errno = ENOENT;
|
||||
break;
|
||||
case MYSOFA_INVALID_ATTRIBUTES:
|
||||
reason = "Invalid attributes";
|
||||
errno = EINVAL;
|
||||
break;
|
||||
case MYSOFA_INVALID_DIMENSIONS:
|
||||
reason = "Invalid dimensions";
|
||||
errno = EINVAL;
|
||||
break;
|
||||
case MYSOFA_INVALID_DIMENSION_LIST:
|
||||
reason = "Invalid dimension list";
|
||||
errno = EINVAL;
|
||||
break;
|
||||
case MYSOFA_INVALID_COORDINATE_TYPE:
|
||||
reason = "Invalid coordinate type";
|
||||
errno = EINVAL;
|
||||
break;
|
||||
case MYSOFA_ONLY_EMITTER_WITH_ECI_SUPPORTED:
|
||||
reason = "Only emitter with ECI supported";
|
||||
errno = ENOTSUP;
|
||||
break;
|
||||
case MYSOFA_ONLY_DELAYS_WITH_IR_OR_MR_SUPPORTED:
|
||||
reason = "Only delays with IR or MR supported";
|
||||
errno = ENOTSUP;
|
||||
break;
|
||||
case MYSOFA_ONLY_THE_SAME_SAMPLING_RATE_SUPPORTED:
|
||||
reason = "Only the same sampling rate supported";
|
||||
errno = ENOTSUP;
|
||||
break;
|
||||
case MYSOFA_RECEIVERS_WITH_RCI_SUPPORTED:
|
||||
reason = "Receivers with RCI supported";
|
||||
errno = ENOTSUP;
|
||||
break;
|
||||
case MYSOFA_RECEIVERS_WITH_CARTESIAN_SUPPORTED:
|
||||
reason = "Receivers with cartesian supported";
|
||||
errno = ENOTSUP;
|
||||
break;
|
||||
case MYSOFA_INVALID_RECEIVER_POSITIONS:
|
||||
reason = "Invalid receiver positions";
|
||||
errno = EINVAL;
|
||||
break;
|
||||
case MYSOFA_ONLY_SOURCES_WITH_MC_SUPPORTED:
|
||||
reason = "Only sources with MC supported";
|
||||
errno = ENOTSUP;
|
||||
break;
|
||||
default:
|
||||
case MYSOFA_INTERNAL_ERROR:
|
||||
errno = EIO;
|
||||
reason = "Internal error";
|
||||
break;
|
||||
}
|
||||
spa_log_error(impl->log, "Unable to load HRTF from %s: %s (%d)", filename, reason, ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (impl->blocksize <= 0)
|
||||
impl->blocksize = SPA_CLAMP(impl->n_samples, 64, 256);
|
||||
if (impl->tailsize <= 0)
|
||||
impl->tailsize = SPA_CLAMP(4096, impl->blocksize, 32768);
|
||||
|
||||
spa_log_info(impl->log, "using n_samples:%u %d:%d blocksize sofa:%s", impl->n_samples,
|
||||
impl->blocksize, impl->tailsize, filename);
|
||||
|
||||
impl->tmp[0] = calloc(impl->plugin->quantum_limit, sizeof(float));
|
||||
impl->tmp[1] = calloc(impl->plugin->quantum_limit, sizeof(float));
|
||||
impl->rate = SampleRate;
|
||||
return impl;
|
||||
error:
|
||||
if (impl->sofa)
|
||||
mysofa_close_cached(impl->sofa);
|
||||
free(impl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
do_switch(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
|
||||
size_t size, void *user_data)
|
||||
{
|
||||
struct spatializer_impl *impl = user_data;
|
||||
|
||||
if (impl->l_conv[0] == NULL) {
|
||||
SPA_SWAP(impl->l_conv[0], impl->l_conv[2]);
|
||||
SPA_SWAP(impl->r_conv[0], impl->r_conv[2]);
|
||||
} else {
|
||||
SPA_SWAP(impl->l_conv[1], impl->l_conv[2]);
|
||||
SPA_SWAP(impl->r_conv[1], impl->r_conv[2]);
|
||||
}
|
||||
impl->interpolate = impl->l_conv[0] && impl->l_conv[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spatializer_reload(void * Instance)
|
||||
{
|
||||
struct spatializer_impl *impl = Instance;
|
||||
float *left_ir = calloc(impl->n_samples, sizeof(float));
|
||||
float *right_ir = calloc(impl->n_samples, sizeof(float));
|
||||
float left_delay;
|
||||
float right_delay;
|
||||
float coords[3];
|
||||
|
||||
for (uint8_t i = 0; i < 3; i++)
|
||||
coords[i] = impl->port[3 + i][0];
|
||||
|
||||
spa_log_info(impl->log, "making spatializer with %f %f %f", coords[0], coords[1], coords[2]);
|
||||
|
||||
mysofa_s2c(coords);
|
||||
mysofa_getfilter_float(
|
||||
impl->sofa,
|
||||
coords[0],
|
||||
coords[1],
|
||||
coords[2],
|
||||
left_ir,
|
||||
right_ir,
|
||||
&left_delay,
|
||||
&right_delay
|
||||
);
|
||||
|
||||
// TODO: make use of delay
|
||||
if ((left_delay != 0.0f || right_delay != 0.0f) && (!isnan(left_delay) || !isnan(right_delay)))
|
||||
spa_log_warn(impl->log, "delay dropped l: %f, r: %f", left_delay, right_delay);
|
||||
|
||||
if (impl->l_conv[2])
|
||||
convolver_free(impl->l_conv[2]);
|
||||
if (impl->r_conv[2])
|
||||
convolver_free(impl->r_conv[2]);
|
||||
|
||||
impl->l_conv[2] = convolver_new(impl->dsp, impl->blocksize, impl->tailsize,
|
||||
left_ir, impl->n_samples);
|
||||
impl->r_conv[2] = convolver_new(impl->dsp, impl->blocksize, impl->tailsize,
|
||||
right_ir, impl->n_samples);
|
||||
|
||||
free(left_ir);
|
||||
free(right_ir);
|
||||
|
||||
if (impl->l_conv[2] == NULL || impl->r_conv[2] == NULL) {
|
||||
spa_log_error(impl->log, "reloading left or right convolver failed");
|
||||
return;
|
||||
}
|
||||
spa_loop_invoke(impl->plugin->data_loop, do_switch, 1, NULL, 0, true, impl);
|
||||
}
|
||||
|
||||
struct free_data {
|
||||
void *item[2];
|
||||
};
|
||||
|
||||
static int
|
||||
do_free(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
|
||||
size_t size, void *user_data)
|
||||
{
|
||||
const struct free_data *fd = data;
|
||||
if (fd->item[0])
|
||||
convolver_free(fd->item[0]);
|
||||
if (fd->item[1])
|
||||
convolver_free(fd->item[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spatializer_run(void * Instance, unsigned long SampleCount)
|
||||
{
|
||||
struct spatializer_impl *impl = Instance;
|
||||
|
||||
if (impl->interpolate) {
|
||||
uint32_t len = SPA_MIN(SampleCount, impl->plugin->quantum_limit);
|
||||
struct free_data free_data;
|
||||
float *l = impl->tmp[0], *r = impl->tmp[1];
|
||||
|
||||
convolver_run(impl->l_conv[0], impl->port[2], impl->port[0], len);
|
||||
convolver_run(impl->l_conv[1], impl->port[2], l, len);
|
||||
convolver_run(impl->r_conv[0], impl->port[2], impl->port[1], len);
|
||||
convolver_run(impl->r_conv[1], impl->port[2], r, len);
|
||||
|
||||
for (uint32_t i = 0; i < SampleCount; i++) {
|
||||
float t = (float)i / SampleCount;
|
||||
impl->port[0][i] = impl->port[0][i] * (1.0f - t) + l[i] * t;
|
||||
impl->port[1][i] = impl->port[1][i] * (1.0f - t) + r[i] * t;
|
||||
}
|
||||
free_data.item[0] = impl->l_conv[0];
|
||||
free_data.item[1] = impl->r_conv[0];
|
||||
impl->l_conv[0] = impl->l_conv[1];
|
||||
impl->r_conv[0] = impl->r_conv[1];
|
||||
impl->l_conv[1] = impl->r_conv[1] = NULL;
|
||||
impl->interpolate = false;
|
||||
|
||||
spa_loop_invoke(impl->plugin->main_loop, do_free, 1, &free_data, sizeof(free_data), false, impl);
|
||||
} else if (impl->l_conv[0] && impl->r_conv[0]) {
|
||||
convolver_run(impl->l_conv[0], impl->port[2], impl->port[0], SampleCount);
|
||||
convolver_run(impl->r_conv[0], impl->port[2], impl->port[1], SampleCount);
|
||||
}
|
||||
}
|
||||
|
||||
static void spatializer_connect_port(void * Instance, unsigned long Port,
|
||||
float * DataLocation)
|
||||
{
|
||||
struct spatializer_impl *impl = Instance;
|
||||
if (Port > 5)
|
||||
return;
|
||||
impl->port[Port] = DataLocation;
|
||||
}
|
||||
|
||||
static void spatializer_cleanup(void * Instance)
|
||||
{
|
||||
struct spatializer_impl *impl = Instance;
|
||||
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
if (impl->l_conv[i])
|
||||
convolver_free(impl->l_conv[i]);
|
||||
if (impl->r_conv[i])
|
||||
convolver_free(impl->r_conv[i]);
|
||||
}
|
||||
if (impl->sofa)
|
||||
mysofa_close_cached(impl->sofa);
|
||||
free(impl->tmp[0]);
|
||||
free(impl->tmp[1]);
|
||||
|
||||
free(impl);
|
||||
}
|
||||
|
||||
static void spatializer_control_changed(void * Instance)
|
||||
{
|
||||
spatializer_reload(Instance);
|
||||
}
|
||||
|
||||
static void spatializer_deactivate(void * Instance)
|
||||
{
|
||||
struct spatializer_impl *impl = Instance;
|
||||
if (impl->l_conv[0])
|
||||
convolver_reset(impl->l_conv[0]);
|
||||
if (impl->r_conv[0])
|
||||
convolver_reset(impl->r_conv[0]);
|
||||
impl->interpolate = false;
|
||||
}
|
||||
|
||||
static struct spa_fga_port spatializer_ports[] = {
|
||||
{ .index = 0,
|
||||
.name = "Out L",
|
||||
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO,
|
||||
},
|
||||
{ .index = 1,
|
||||
.name = "Out R",
|
||||
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO,
|
||||
},
|
||||
{ .index = 2,
|
||||
.name = "In",
|
||||
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO,
|
||||
},
|
||||
|
||||
{ .index = 3,
|
||||
.name = "Azimuth",
|
||||
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
|
||||
.def = 0.0f, .min = 0.0f, .max = 360.0f
|
||||
},
|
||||
{ .index = 4,
|
||||
.name = "Elevation",
|
||||
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
|
||||
.def = 0.0f, .min = -90.0f, .max = 90.0f
|
||||
},
|
||||
{ .index = 5,
|
||||
.name = "Radius",
|
||||
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
|
||||
.def = 1.0f, .min = 0.0f, .max = 100.0f
|
||||
},
|
||||
};
|
||||
|
||||
static const struct spa_fga_descriptor spatializer_desc = {
|
||||
.name = "spatializer",
|
||||
|
||||
.n_ports = 6,
|
||||
.ports = spatializer_ports,
|
||||
|
||||
.instantiate = spatializer_instantiate,
|
||||
.connect_port = spatializer_connect_port,
|
||||
.control_changed = spatializer_control_changed,
|
||||
.deactivate = spatializer_deactivate,
|
||||
.run = spatializer_run,
|
||||
.cleanup = spatializer_cleanup,
|
||||
};
|
||||
|
||||
static const struct spa_fga_descriptor * sofa_descriptor(unsigned long Index)
|
||||
{
|
||||
switch(Index) {
|
||||
case 0:
|
||||
return &spatializer_desc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const struct spa_fga_descriptor *sofa_plugin_make_desc(void *plugin, const char *name)
|
||||
{
|
||||
unsigned long i;
|
||||
for (i = 0; ;i++) {
|
||||
const struct spa_fga_descriptor *d = sofa_descriptor(i);
|
||||
if (d == NULL)
|
||||
break;
|
||||
if (spa_streq(d->name, name))
|
||||
return d;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct spa_fga_plugin_methods impl_plugin = {
|
||||
SPA_VERSION_FGA_PLUGIN_METHODS,
|
||||
.make_desc = sofa_plugin_make_desc,
|
||||
};
|
||||
|
||||
static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
|
||||
{
|
||||
struct plugin *impl;
|
||||
|
||||
spa_return_val_if_fail(handle != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(interface != NULL, -EINVAL);
|
||||
|
||||
impl = (struct plugin *) handle;
|
||||
|
||||
if (spa_streq(type, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin))
|
||||
*interface = &impl->plugin;
|
||||
else
|
||||
return -ENOENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int impl_clear(struct spa_handle *handle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
impl_get_size(const struct spa_handle_factory *factory,
|
||||
const struct spa_dict *params)
|
||||
{
|
||||
return sizeof(struct plugin);
|
||||
}
|
||||
|
||||
static int
|
||||
impl_init(const struct spa_handle_factory *factory,
|
||||
struct spa_handle *handle,
|
||||
const struct spa_dict *info,
|
||||
const struct spa_support *support,
|
||||
uint32_t n_support)
|
||||
{
|
||||
struct plugin *impl;
|
||||
|
||||
handle->get_interface = impl_get_interface;
|
||||
handle->clear = impl_clear;
|
||||
|
||||
impl = (struct plugin *) handle;
|
||||
|
||||
impl->plugin.iface = SPA_INTERFACE_INIT(
|
||||
SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin,
|
||||
SPA_VERSION_FGA_PLUGIN,
|
||||
&impl_plugin, impl);
|
||||
|
||||
impl->quantum_limit = 8192u;
|
||||
|
||||
impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
|
||||
impl->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
|
||||
impl->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
|
||||
impl->dsp = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioDSP);
|
||||
|
||||
for (uint32_t i = 0; info && i < info->n_items; i++) {
|
||||
const char *k = info->items[i].key;
|
||||
const char *s = info->items[i].value;
|
||||
if (spa_streq(k, "clock.quantum-limit"))
|
||||
spa_atou32(s, &impl->quantum_limit, 0);
|
||||
if (spa_streq(k, "filter.graph.audio.dsp"))
|
||||
sscanf(s, "pointer:%p", &impl->dsp);
|
||||
}
|
||||
|
||||
if (impl->data_loop == NULL || impl->main_loop == NULL) {
|
||||
spa_log_error(impl->log, "%p: could not find a data/main loop", impl);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (impl->dsp == NULL) {
|
||||
spa_log_error(impl->log, "%p: could not find DSP functions", impl);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spa_interface_info impl_interfaces[] = {
|
||||
{SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioPlugin,},
|
||||
};
|
||||
|
||||
static int
|
||||
impl_enum_interface_info(const struct spa_handle_factory *factory,
|
||||
const struct spa_interface_info **info,
|
||||
uint32_t *index)
|
||||
{
|
||||
spa_return_val_if_fail(factory != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(info != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(index != NULL, -EINVAL);
|
||||
|
||||
switch (*index) {
|
||||
case 0:
|
||||
*info = &impl_interfaces[*index];
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
(*index)++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct spa_handle_factory spa_fga_sofa_plugin_factory = {
|
||||
SPA_VERSION_HANDLE_FACTORY,
|
||||
"filter.graph.plugin.sofa",
|
||||
NULL,
|
||||
impl_get_size,
|
||||
impl_init,
|
||||
impl_enum_interface_info,
|
||||
};
|
||||
|
||||
SPA_EXPORT
|
||||
int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
|
||||
{
|
||||
spa_return_val_if_fail(factory != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(index != NULL, -EINVAL);
|
||||
|
||||
switch (*index) {
|
||||
case 0:
|
||||
*factory = &spa_fga_sofa_plugin_factory;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
(*index)++;
|
||||
return 1;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue