153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2010 Intel Corporation 553a5a1b3Sopenharmony_ci Contributor: Pierre-Louis Bossart <pierre-louis.bossart@intel.com> 653a5a1b3Sopenharmony_ci Copyright 2012 Niels Ole Salscheider <niels_ole@salscheider-online.de> 753a5a1b3Sopenharmony_ci Contributor: Alexander E. Patrakov <patrakov@gmail.com> 853a5a1b3Sopenharmony_ci Copyright 2020 Christopher Snowhill <kode54@gmail.com> 953a5a1b3Sopenharmony_ci 1053a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 1153a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 1253a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 1353a5a1b3Sopenharmony_ci or (at your option) any later version. 1453a5a1b3Sopenharmony_ci 1553a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1653a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1753a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1853a5a1b3Sopenharmony_ci General Public License for more details. 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 2153a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 2253a5a1b3Sopenharmony_ci***/ 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2553a5a1b3Sopenharmony_ci#include <config.h> 2653a5a1b3Sopenharmony_ci#endif 2753a5a1b3Sopenharmony_ci 2853a5a1b3Sopenharmony_ci#include <math.h> 2953a5a1b3Sopenharmony_ci 3053a5a1b3Sopenharmony_ci#include <fftw3.h> 3153a5a1b3Sopenharmony_ci 3253a5a1b3Sopenharmony_ci#include <pulse/gccmacro.h> 3353a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3453a5a1b3Sopenharmony_ci 3553a5a1b3Sopenharmony_ci#include <pulsecore/i18n.h> 3653a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h> 3753a5a1b3Sopenharmony_ci#include <pulsecore/sink.h> 3853a5a1b3Sopenharmony_ci#include <pulsecore/module.h> 3953a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 4053a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 4153a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 4253a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h> 4353a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h> 4453a5a1b3Sopenharmony_ci#include <pulsecore/ltdl-helper.h> 4553a5a1b3Sopenharmony_ci#include <pulsecore/sound-file.h> 4653a5a1b3Sopenharmony_ci#include <pulsecore/resampler.h> 4753a5a1b3Sopenharmony_ci 4853a5a1b3Sopenharmony_ci 4953a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Christopher Snowhill"); 5053a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION(_("Virtual surround sink")); 5153a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 5253a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false); 5353a5a1b3Sopenharmony_ciPA_MODULE_USAGE( 5453a5a1b3Sopenharmony_ci _("sink_name=<name for the sink> " 5553a5a1b3Sopenharmony_ci "sink_properties=<properties for the sink> " 5653a5a1b3Sopenharmony_ci "master=<name of sink to filter> " 5753a5a1b3Sopenharmony_ci "sink_master=<name of sink to filter> " 5853a5a1b3Sopenharmony_ci "format=<sample format> " 5953a5a1b3Sopenharmony_ci "rate=<sample rate> " 6053a5a1b3Sopenharmony_ci "channels=<number of channels> " 6153a5a1b3Sopenharmony_ci "channel_map=<channel map> " 6253a5a1b3Sopenharmony_ci "use_volume_sharing=<yes or no> " 6353a5a1b3Sopenharmony_ci "force_flat_volume=<yes or no> " 6453a5a1b3Sopenharmony_ci "hrir=/path/to/left_hrir.wav " 6553a5a1b3Sopenharmony_ci "hrir_left=/path/to/left_hrir.wav " 6653a5a1b3Sopenharmony_ci "hrir_right=/path/to/optional/right_hrir.wav " 6753a5a1b3Sopenharmony_ci "autoloaded=<set if this module is being loaded automatically> " 6853a5a1b3Sopenharmony_ci )); 6953a5a1b3Sopenharmony_ci 7053a5a1b3Sopenharmony_ci#define MEMBLOCKQ_MAXLENGTH (16*1024*1024) 7153a5a1b3Sopenharmony_ci#define DEFAULT_AUTOLOADED false 7253a5a1b3Sopenharmony_ci 7353a5a1b3Sopenharmony_cistruct userdata { 7453a5a1b3Sopenharmony_ci pa_module *module; 7553a5a1b3Sopenharmony_ci 7653a5a1b3Sopenharmony_ci bool autoloaded; 7753a5a1b3Sopenharmony_ci 7853a5a1b3Sopenharmony_ci pa_sink *sink; 7953a5a1b3Sopenharmony_ci pa_sink_input *sink_input; 8053a5a1b3Sopenharmony_ci 8153a5a1b3Sopenharmony_ci pa_memblockq *memblockq_sink; 8253a5a1b3Sopenharmony_ci 8353a5a1b3Sopenharmony_ci bool auto_desc; 8453a5a1b3Sopenharmony_ci 8553a5a1b3Sopenharmony_ci size_t fftlen; 8653a5a1b3Sopenharmony_ci size_t hrir_samples; 8753a5a1b3Sopenharmony_ci size_t inputs; 8853a5a1b3Sopenharmony_ci 8953a5a1b3Sopenharmony_ci fftwf_plan *p_fw, p_bw; 9053a5a1b3Sopenharmony_ci fftwf_complex *f_in, *f_out, **f_ir; 9153a5a1b3Sopenharmony_ci float *revspace, *outspace[2], **inspace; 9253a5a1b3Sopenharmony_ci}; 9353a5a1b3Sopenharmony_ci 9453a5a1b3Sopenharmony_ci#define BLOCK_SIZE (512) 9553a5a1b3Sopenharmony_ci 9653a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 9753a5a1b3Sopenharmony_ci "sink_name", 9853a5a1b3Sopenharmony_ci "sink_properties", 9953a5a1b3Sopenharmony_ci "master", /* Will be deprecated. */ 10053a5a1b3Sopenharmony_ci "sink_master", 10153a5a1b3Sopenharmony_ci "format", 10253a5a1b3Sopenharmony_ci "rate", 10353a5a1b3Sopenharmony_ci "channels", 10453a5a1b3Sopenharmony_ci "channel_map", 10553a5a1b3Sopenharmony_ci "use_volume_sharing", 10653a5a1b3Sopenharmony_ci "force_flat_volume", 10753a5a1b3Sopenharmony_ci "autoloaded", 10853a5a1b3Sopenharmony_ci "hrir", 10953a5a1b3Sopenharmony_ci "hrir_left", 11053a5a1b3Sopenharmony_ci "hrir_right", 11153a5a1b3Sopenharmony_ci NULL 11253a5a1b3Sopenharmony_ci}; 11353a5a1b3Sopenharmony_ci 11453a5a1b3Sopenharmony_ci/* Vector size of 4 floats */ 11553a5a1b3Sopenharmony_ci#define v_size 4 11653a5a1b3Sopenharmony_cistatic void * alloc(size_t x, size_t s) { 11753a5a1b3Sopenharmony_ci size_t f; 11853a5a1b3Sopenharmony_ci float *t; 11953a5a1b3Sopenharmony_ci 12053a5a1b3Sopenharmony_ci f = PA_ROUND_UP(x*s, sizeof(float)*v_size); 12153a5a1b3Sopenharmony_ci pa_assert_se(t = fftwf_malloc(f)); 12253a5a1b3Sopenharmony_ci pa_memzero(t, f); 12353a5a1b3Sopenharmony_ci 12453a5a1b3Sopenharmony_ci return t; 12553a5a1b3Sopenharmony_ci} 12653a5a1b3Sopenharmony_ci 12753a5a1b3Sopenharmony_cistatic size_t sink_input_samples(size_t nbytes) 12853a5a1b3Sopenharmony_ci{ 12953a5a1b3Sopenharmony_ci return nbytes / 8; 13053a5a1b3Sopenharmony_ci} 13153a5a1b3Sopenharmony_ci 13253a5a1b3Sopenharmony_cistatic size_t sink_input_bytes(size_t nsamples) 13353a5a1b3Sopenharmony_ci{ 13453a5a1b3Sopenharmony_ci return nsamples * 8; 13553a5a1b3Sopenharmony_ci} 13653a5a1b3Sopenharmony_ci 13753a5a1b3Sopenharmony_cistatic size_t sink_samples(const struct userdata *u, size_t nbytes) 13853a5a1b3Sopenharmony_ci{ 13953a5a1b3Sopenharmony_ci return nbytes / (u->inputs * 4); 14053a5a1b3Sopenharmony_ci} 14153a5a1b3Sopenharmony_ci 14253a5a1b3Sopenharmony_cistatic size_t sink_bytes(const struct userdata *u, size_t nsamples) 14353a5a1b3Sopenharmony_ci{ 14453a5a1b3Sopenharmony_ci return nsamples * (u->inputs * 4); 14553a5a1b3Sopenharmony_ci} 14653a5a1b3Sopenharmony_ci 14753a5a1b3Sopenharmony_ci/* Mirror channels for symmetrical impulse */ 14853a5a1b3Sopenharmony_cistatic pa_channel_position_t mirror_channel(pa_channel_position_t channel) { 14953a5a1b3Sopenharmony_ci switch (channel) { 15053a5a1b3Sopenharmony_ci case PA_CHANNEL_POSITION_FRONT_LEFT: 15153a5a1b3Sopenharmony_ci return PA_CHANNEL_POSITION_FRONT_RIGHT; 15253a5a1b3Sopenharmony_ci 15353a5a1b3Sopenharmony_ci case PA_CHANNEL_POSITION_FRONT_RIGHT: 15453a5a1b3Sopenharmony_ci return PA_CHANNEL_POSITION_FRONT_LEFT; 15553a5a1b3Sopenharmony_ci 15653a5a1b3Sopenharmony_ci case PA_CHANNEL_POSITION_REAR_LEFT: 15753a5a1b3Sopenharmony_ci return PA_CHANNEL_POSITION_REAR_RIGHT; 15853a5a1b3Sopenharmony_ci 15953a5a1b3Sopenharmony_ci case PA_CHANNEL_POSITION_REAR_RIGHT: 16053a5a1b3Sopenharmony_ci return PA_CHANNEL_POSITION_REAR_LEFT; 16153a5a1b3Sopenharmony_ci 16253a5a1b3Sopenharmony_ci case PA_CHANNEL_POSITION_SIDE_LEFT: 16353a5a1b3Sopenharmony_ci return PA_CHANNEL_POSITION_SIDE_RIGHT; 16453a5a1b3Sopenharmony_ci 16553a5a1b3Sopenharmony_ci case PA_CHANNEL_POSITION_SIDE_RIGHT: 16653a5a1b3Sopenharmony_ci return PA_CHANNEL_POSITION_SIDE_LEFT; 16753a5a1b3Sopenharmony_ci 16853a5a1b3Sopenharmony_ci case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: 16953a5a1b3Sopenharmony_ci return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; 17053a5a1b3Sopenharmony_ci 17153a5a1b3Sopenharmony_ci case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: 17253a5a1b3Sopenharmony_ci return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; 17353a5a1b3Sopenharmony_ci 17453a5a1b3Sopenharmony_ci case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: 17553a5a1b3Sopenharmony_ci return PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; 17653a5a1b3Sopenharmony_ci 17753a5a1b3Sopenharmony_ci case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: 17853a5a1b3Sopenharmony_ci return PA_CHANNEL_POSITION_TOP_FRONT_LEFT; 17953a5a1b3Sopenharmony_ci 18053a5a1b3Sopenharmony_ci case PA_CHANNEL_POSITION_TOP_REAR_LEFT: 18153a5a1b3Sopenharmony_ci return PA_CHANNEL_POSITION_TOP_REAR_RIGHT; 18253a5a1b3Sopenharmony_ci 18353a5a1b3Sopenharmony_ci case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: 18453a5a1b3Sopenharmony_ci return PA_CHANNEL_POSITION_TOP_REAR_LEFT; 18553a5a1b3Sopenharmony_ci 18653a5a1b3Sopenharmony_ci default: 18753a5a1b3Sopenharmony_ci return channel; 18853a5a1b3Sopenharmony_ci } 18953a5a1b3Sopenharmony_ci} 19053a5a1b3Sopenharmony_ci 19153a5a1b3Sopenharmony_ci/* Normalize the hrir */ 19253a5a1b3Sopenharmony_cistatic void normalize_hrir(float * hrir_data, unsigned hrir_samples, unsigned hrir_channels) { 19353a5a1b3Sopenharmony_ci /* normalize hrir to avoid audible clipping 19453a5a1b3Sopenharmony_ci * 19553a5a1b3Sopenharmony_ci * The following heuristic tries to avoid audible clipping. It cannot avoid 19653a5a1b3Sopenharmony_ci * clipping in the worst case though, because the scaling factor would 19753a5a1b3Sopenharmony_ci * become too large resulting in a too quiet signal. 19853a5a1b3Sopenharmony_ci * The idea of the heuristic is to avoid clipping when a single click is 19953a5a1b3Sopenharmony_ci * played back on all channels. The scaling factor describes the additional 20053a5a1b3Sopenharmony_ci * factor that is necessary to avoid clipping for "normal" signals. 20153a5a1b3Sopenharmony_ci * 20253a5a1b3Sopenharmony_ci * This algorithm doesn't pretend to be perfect, it's just something that 20353a5a1b3Sopenharmony_ci * appears to work (not too quiet, no audible clipping) on the material that 20453a5a1b3Sopenharmony_ci * it has been tested on. If you find a real-world example where this 20553a5a1b3Sopenharmony_ci * algorithm results in audible clipping, please write a patch that adjusts 20653a5a1b3Sopenharmony_ci * the scaling factor constants or improves the algorithm (or if you can't 20753a5a1b3Sopenharmony_ci * write a patch, at least report the problem to the PulseAudio mailing list 20853a5a1b3Sopenharmony_ci * or bug tracker). */ 20953a5a1b3Sopenharmony_ci 21053a5a1b3Sopenharmony_ci const float scaling_factor = 2.5; 21153a5a1b3Sopenharmony_ci 21253a5a1b3Sopenharmony_ci float hrir_sum, hrir_max; 21353a5a1b3Sopenharmony_ci unsigned i, j; 21453a5a1b3Sopenharmony_ci 21553a5a1b3Sopenharmony_ci hrir_max = 0; 21653a5a1b3Sopenharmony_ci for (i = 0; i < hrir_samples; i++) { 21753a5a1b3Sopenharmony_ci hrir_sum = 0; 21853a5a1b3Sopenharmony_ci for (j = 0; j < hrir_channels; j++) 21953a5a1b3Sopenharmony_ci hrir_sum += fabs(hrir_data[i * hrir_channels + j]); 22053a5a1b3Sopenharmony_ci 22153a5a1b3Sopenharmony_ci if (hrir_sum > hrir_max) 22253a5a1b3Sopenharmony_ci hrir_max = hrir_sum; 22353a5a1b3Sopenharmony_ci } 22453a5a1b3Sopenharmony_ci 22553a5a1b3Sopenharmony_ci for (i = 0; i < hrir_samples; i++) { 22653a5a1b3Sopenharmony_ci for (j = 0; j < hrir_channels; j++) 22753a5a1b3Sopenharmony_ci hrir_data[i * hrir_channels + j] /= hrir_max * scaling_factor; 22853a5a1b3Sopenharmony_ci } 22953a5a1b3Sopenharmony_ci} 23053a5a1b3Sopenharmony_ci 23153a5a1b3Sopenharmony_ci/* Normalize a stereo hrir */ 23253a5a1b3Sopenharmony_cistatic void normalize_hrir_stereo(float * hrir_data, float * hrir_right_data, unsigned hrir_samples, unsigned hrir_channels) { 23353a5a1b3Sopenharmony_ci const float scaling_factor = 2.5; 23453a5a1b3Sopenharmony_ci 23553a5a1b3Sopenharmony_ci float hrir_sum, hrir_max; 23653a5a1b3Sopenharmony_ci unsigned i, j; 23753a5a1b3Sopenharmony_ci 23853a5a1b3Sopenharmony_ci hrir_max = 0; 23953a5a1b3Sopenharmony_ci for (i = 0; i < hrir_samples; i++) { 24053a5a1b3Sopenharmony_ci hrir_sum = 0; 24153a5a1b3Sopenharmony_ci for (j = 0; j < hrir_channels; j++) { 24253a5a1b3Sopenharmony_ci hrir_sum += fabs(hrir_data[i * hrir_channels + j]); 24353a5a1b3Sopenharmony_ci hrir_sum += fabs(hrir_right_data[i * hrir_channels + j]); 24453a5a1b3Sopenharmony_ci } 24553a5a1b3Sopenharmony_ci 24653a5a1b3Sopenharmony_ci if (hrir_sum > hrir_max) 24753a5a1b3Sopenharmony_ci hrir_max = hrir_sum; 24853a5a1b3Sopenharmony_ci } 24953a5a1b3Sopenharmony_ci 25053a5a1b3Sopenharmony_ci for (i = 0; i < hrir_samples; i++) { 25153a5a1b3Sopenharmony_ci for (j = 0; j < hrir_channels; j++) { 25253a5a1b3Sopenharmony_ci hrir_data[i * hrir_channels + j] /= hrir_max * scaling_factor; 25353a5a1b3Sopenharmony_ci hrir_right_data[i * hrir_channels + j] /= hrir_max * scaling_factor; 25453a5a1b3Sopenharmony_ci } 25553a5a1b3Sopenharmony_ci } 25653a5a1b3Sopenharmony_ci} 25753a5a1b3Sopenharmony_ci 25853a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 25953a5a1b3Sopenharmony_cistatic int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { 26053a5a1b3Sopenharmony_ci struct userdata *u = PA_SINK(o)->userdata; 26153a5a1b3Sopenharmony_ci 26253a5a1b3Sopenharmony_ci switch (code) { 26353a5a1b3Sopenharmony_ci 26453a5a1b3Sopenharmony_ci case PA_SINK_MESSAGE_GET_LATENCY: 26553a5a1b3Sopenharmony_ci 26653a5a1b3Sopenharmony_ci /* The sink is _put() before the sink input is, so let's 26753a5a1b3Sopenharmony_ci * make sure we don't access it in that time. Also, the 26853a5a1b3Sopenharmony_ci * sink input is first shut down, the sink second. */ 26953a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || 27053a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) { 27153a5a1b3Sopenharmony_ci *((pa_usec_t*) data) = 0; 27253a5a1b3Sopenharmony_ci return 0; 27353a5a1b3Sopenharmony_ci } 27453a5a1b3Sopenharmony_ci 27553a5a1b3Sopenharmony_ci *((pa_usec_t*) data) = 27653a5a1b3Sopenharmony_ci 27753a5a1b3Sopenharmony_ci /* Get the latency of the master sink */ 27853a5a1b3Sopenharmony_ci pa_sink_get_latency_within_thread(u->sink_input->sink, true) + 27953a5a1b3Sopenharmony_ci 28053a5a1b3Sopenharmony_ci /* Add the latency internal to our sink input on top */ 28153a5a1b3Sopenharmony_ci pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec); 28253a5a1b3Sopenharmony_ci 28353a5a1b3Sopenharmony_ci /* Add resampler latency */ 28453a5a1b3Sopenharmony_ci *((int64_t*) data) += pa_resampler_get_delay_usec(u->sink_input->thread_info.resampler); 28553a5a1b3Sopenharmony_ci 28653a5a1b3Sopenharmony_ci return 0; 28753a5a1b3Sopenharmony_ci } 28853a5a1b3Sopenharmony_ci 28953a5a1b3Sopenharmony_ci return pa_sink_process_msg(o, code, data, offset, chunk); 29053a5a1b3Sopenharmony_ci} 29153a5a1b3Sopenharmony_ci 29253a5a1b3Sopenharmony_ci/* Called from main context */ 29353a5a1b3Sopenharmony_cistatic int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t suspend_cause) { 29453a5a1b3Sopenharmony_ci struct userdata *u; 29553a5a1b3Sopenharmony_ci 29653a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 29753a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 29853a5a1b3Sopenharmony_ci 29953a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(state) || 30053a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->state)) 30153a5a1b3Sopenharmony_ci return 0; 30253a5a1b3Sopenharmony_ci 30353a5a1b3Sopenharmony_ci pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); 30453a5a1b3Sopenharmony_ci return 0; 30553a5a1b3Sopenharmony_ci} 30653a5a1b3Sopenharmony_ci 30753a5a1b3Sopenharmony_ci/* Called from the IO thread. */ 30853a5a1b3Sopenharmony_cistatic int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) { 30953a5a1b3Sopenharmony_ci struct userdata *u; 31053a5a1b3Sopenharmony_ci 31153a5a1b3Sopenharmony_ci pa_assert(s); 31253a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 31353a5a1b3Sopenharmony_ci 31453a5a1b3Sopenharmony_ci /* When set to running or idle for the first time, request a rewind 31553a5a1b3Sopenharmony_ci * of the master sink to make sure we are heard immediately */ 31653a5a1b3Sopenharmony_ci if (PA_SINK_IS_OPENED(new_state) && s->thread_info.state == PA_SINK_INIT) { 31753a5a1b3Sopenharmony_ci pa_log_debug("Requesting rewind due to state change."); 31853a5a1b3Sopenharmony_ci pa_sink_input_request_rewind(u->sink_input, 0, false, true, true); 31953a5a1b3Sopenharmony_ci } 32053a5a1b3Sopenharmony_ci 32153a5a1b3Sopenharmony_ci return 0; 32253a5a1b3Sopenharmony_ci} 32353a5a1b3Sopenharmony_ci 32453a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 32553a5a1b3Sopenharmony_cistatic void sink_request_rewind_cb(pa_sink *s) { 32653a5a1b3Sopenharmony_ci struct userdata *u; 32753a5a1b3Sopenharmony_ci size_t nbytes_sink, nbytes_input; 32853a5a1b3Sopenharmony_ci 32953a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 33053a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 33153a5a1b3Sopenharmony_ci 33253a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || 33353a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) 33453a5a1b3Sopenharmony_ci return; 33553a5a1b3Sopenharmony_ci 33653a5a1b3Sopenharmony_ci nbytes_sink = s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq_sink); 33753a5a1b3Sopenharmony_ci nbytes_input = sink_input_bytes(sink_samples(u, nbytes_sink)); 33853a5a1b3Sopenharmony_ci 33953a5a1b3Sopenharmony_ci /* Just hand this one over to the master sink */ 34053a5a1b3Sopenharmony_ci pa_sink_input_request_rewind(u->sink_input, nbytes_input, true, false, false); 34153a5a1b3Sopenharmony_ci} 34253a5a1b3Sopenharmony_ci 34353a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 34453a5a1b3Sopenharmony_cistatic void sink_update_requested_latency_cb(pa_sink *s) { 34553a5a1b3Sopenharmony_ci struct userdata *u; 34653a5a1b3Sopenharmony_ci 34753a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 34853a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 34953a5a1b3Sopenharmony_ci 35053a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || 35153a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) 35253a5a1b3Sopenharmony_ci return; 35353a5a1b3Sopenharmony_ci 35453a5a1b3Sopenharmony_ci /* Just hand this one over to the master sink */ 35553a5a1b3Sopenharmony_ci pa_sink_input_set_requested_latency_within_thread( 35653a5a1b3Sopenharmony_ci u->sink_input, 35753a5a1b3Sopenharmony_ci pa_sink_get_requested_latency_within_thread(s)); 35853a5a1b3Sopenharmony_ci} 35953a5a1b3Sopenharmony_ci 36053a5a1b3Sopenharmony_ci/* Called from main context */ 36153a5a1b3Sopenharmony_cistatic void sink_set_volume_cb(pa_sink *s) { 36253a5a1b3Sopenharmony_ci struct userdata *u; 36353a5a1b3Sopenharmony_ci 36453a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 36553a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 36653a5a1b3Sopenharmony_ci 36753a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(s->state) || 36853a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->state)) 36953a5a1b3Sopenharmony_ci return; 37053a5a1b3Sopenharmony_ci 37153a5a1b3Sopenharmony_ci pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, true); 37253a5a1b3Sopenharmony_ci} 37353a5a1b3Sopenharmony_ci 37453a5a1b3Sopenharmony_ci/* Called from main context */ 37553a5a1b3Sopenharmony_cistatic void sink_set_mute_cb(pa_sink *s) { 37653a5a1b3Sopenharmony_ci struct userdata *u; 37753a5a1b3Sopenharmony_ci 37853a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 37953a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 38053a5a1b3Sopenharmony_ci 38153a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(s->state) || 38253a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->state)) 38353a5a1b3Sopenharmony_ci return; 38453a5a1b3Sopenharmony_ci 38553a5a1b3Sopenharmony_ci pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted); 38653a5a1b3Sopenharmony_ci} 38753a5a1b3Sopenharmony_ci 38853a5a1b3Sopenharmony_cistatic size_t memblockq_missing(pa_memblockq *bq) { 38953a5a1b3Sopenharmony_ci size_t l, tlength; 39053a5a1b3Sopenharmony_ci pa_assert(bq); 39153a5a1b3Sopenharmony_ci 39253a5a1b3Sopenharmony_ci tlength = pa_memblockq_get_tlength(bq); 39353a5a1b3Sopenharmony_ci if ((l = pa_memblockq_get_length(bq)) >= tlength) 39453a5a1b3Sopenharmony_ci return 0; 39553a5a1b3Sopenharmony_ci 39653a5a1b3Sopenharmony_ci l = tlength - l; 39753a5a1b3Sopenharmony_ci return l >= pa_memblockq_get_minreq(bq) ? l : 0; 39853a5a1b3Sopenharmony_ci} 39953a5a1b3Sopenharmony_ci 40053a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 40153a5a1b3Sopenharmony_cistatic int sink_input_pop_cb(pa_sink_input *i, size_t nbytes_input, pa_memchunk *chunk) { 40253a5a1b3Sopenharmony_ci struct userdata *u; 40353a5a1b3Sopenharmony_ci float *src, *dst; 40453a5a1b3Sopenharmony_ci int c, ear; 40553a5a1b3Sopenharmony_ci size_t s, bytes_missing, fftlen; 40653a5a1b3Sopenharmony_ci pa_memchunk tchunk; 40753a5a1b3Sopenharmony_ci float fftlen_if, *revspace; 40853a5a1b3Sopenharmony_ci 40953a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 41053a5a1b3Sopenharmony_ci pa_assert(chunk); 41153a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 41253a5a1b3Sopenharmony_ci 41353a5a1b3Sopenharmony_ci /* Hmm, process any rewind request that might be queued up */ 41453a5a1b3Sopenharmony_ci pa_sink_process_rewind(u->sink, 0); 41553a5a1b3Sopenharmony_ci 41653a5a1b3Sopenharmony_ci while ((bytes_missing = memblockq_missing(u->memblockq_sink)) != 0) { 41753a5a1b3Sopenharmony_ci pa_memchunk nchunk; 41853a5a1b3Sopenharmony_ci 41953a5a1b3Sopenharmony_ci pa_sink_render(u->sink, bytes_missing, &nchunk); 42053a5a1b3Sopenharmony_ci pa_memblockq_push(u->memblockq_sink, &nchunk); 42153a5a1b3Sopenharmony_ci pa_memblock_unref(nchunk.memblock); 42253a5a1b3Sopenharmony_ci } 42353a5a1b3Sopenharmony_ci 42453a5a1b3Sopenharmony_ci pa_memblockq_rewind(u->memblockq_sink, sink_bytes(u, u->fftlen - BLOCK_SIZE)); 42553a5a1b3Sopenharmony_ci pa_memblockq_peek_fixed_size(u->memblockq_sink, sink_bytes(u, u->fftlen), &tchunk); 42653a5a1b3Sopenharmony_ci 42753a5a1b3Sopenharmony_ci pa_memblockq_drop(u->memblockq_sink, tchunk.length); 42853a5a1b3Sopenharmony_ci 42953a5a1b3Sopenharmony_ci /* Now tchunk contains enough data to perform the FFT 43053a5a1b3Sopenharmony_ci * This should be equal to u->fftlen */ 43153a5a1b3Sopenharmony_ci 43253a5a1b3Sopenharmony_ci chunk->index = 0; 43353a5a1b3Sopenharmony_ci chunk->length = sink_input_bytes(BLOCK_SIZE); 43453a5a1b3Sopenharmony_ci chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); 43553a5a1b3Sopenharmony_ci 43653a5a1b3Sopenharmony_ci src = pa_memblock_acquire_chunk(&tchunk); 43753a5a1b3Sopenharmony_ci 43853a5a1b3Sopenharmony_ci for (c = 0; c < u->inputs; c++) { 43953a5a1b3Sopenharmony_ci for (s = 0, fftlen = u->fftlen; s < fftlen; s++) { 44053a5a1b3Sopenharmony_ci u->inspace[c][s] = src[s * u->inputs + c]; 44153a5a1b3Sopenharmony_ci } 44253a5a1b3Sopenharmony_ci } 44353a5a1b3Sopenharmony_ci 44453a5a1b3Sopenharmony_ci pa_memblock_release(tchunk.memblock); 44553a5a1b3Sopenharmony_ci pa_memblock_unref(tchunk.memblock); 44653a5a1b3Sopenharmony_ci 44753a5a1b3Sopenharmony_ci fftlen_if = 1.0f / (float)u->fftlen; 44853a5a1b3Sopenharmony_ci revspace = u->revspace + u->fftlen - BLOCK_SIZE; 44953a5a1b3Sopenharmony_ci 45053a5a1b3Sopenharmony_ci pa_memzero(u->outspace[0], BLOCK_SIZE * 4); 45153a5a1b3Sopenharmony_ci pa_memzero(u->outspace[1], BLOCK_SIZE * 4); 45253a5a1b3Sopenharmony_ci 45353a5a1b3Sopenharmony_ci for (c = 0; c < u->inputs; c++) { 45453a5a1b3Sopenharmony_ci fftwf_complex *f_in = u->f_in; 45553a5a1b3Sopenharmony_ci fftwf_complex *f_out = u->f_out; 45653a5a1b3Sopenharmony_ci 45753a5a1b3Sopenharmony_ci fftwf_execute(u->p_fw[c]); 45853a5a1b3Sopenharmony_ci 45953a5a1b3Sopenharmony_ci for (ear = 0; ear < 2; ear++) { 46053a5a1b3Sopenharmony_ci fftwf_complex *f_ir = u->f_ir[c * 2 + ear]; 46153a5a1b3Sopenharmony_ci float *outspace = u->outspace[ear]; 46253a5a1b3Sopenharmony_ci 46353a5a1b3Sopenharmony_ci for (s = 0, fftlen = u->fftlen / 2 + 1; s < fftlen; s++) { 46453a5a1b3Sopenharmony_ci float re = f_ir[s][0] * f_in[s][0] - f_ir[s][1] * f_in[s][1]; 46553a5a1b3Sopenharmony_ci float im = f_ir[s][1] * f_in[s][0] + f_ir[s][0] * f_in[s][1]; 46653a5a1b3Sopenharmony_ci f_out[s][0] = re; 46753a5a1b3Sopenharmony_ci f_out[s][1] = im; 46853a5a1b3Sopenharmony_ci } 46953a5a1b3Sopenharmony_ci 47053a5a1b3Sopenharmony_ci fftwf_execute(u->p_bw); 47153a5a1b3Sopenharmony_ci 47253a5a1b3Sopenharmony_ci for (s = 0, fftlen = BLOCK_SIZE; s < fftlen; ++s) 47353a5a1b3Sopenharmony_ci outspace[s] += revspace[s] * fftlen_if; 47453a5a1b3Sopenharmony_ci } 47553a5a1b3Sopenharmony_ci } 47653a5a1b3Sopenharmony_ci 47753a5a1b3Sopenharmony_ci dst = pa_memblock_acquire_chunk(chunk); 47853a5a1b3Sopenharmony_ci 47953a5a1b3Sopenharmony_ci for (s = 0, fftlen = BLOCK_SIZE; s < fftlen; s++) { 48053a5a1b3Sopenharmony_ci float output; 48153a5a1b3Sopenharmony_ci float *outspace = u->outspace[0]; 48253a5a1b3Sopenharmony_ci 48353a5a1b3Sopenharmony_ci output = outspace[s]; 48453a5a1b3Sopenharmony_ci if (output < -1.0) output = -1.0; 48553a5a1b3Sopenharmony_ci if (output > 1.0) output = 1.0; 48653a5a1b3Sopenharmony_ci dst[s * 2 + 0] = output; 48753a5a1b3Sopenharmony_ci 48853a5a1b3Sopenharmony_ci outspace = u->outspace[1]; 48953a5a1b3Sopenharmony_ci 49053a5a1b3Sopenharmony_ci output = outspace[s]; 49153a5a1b3Sopenharmony_ci if (output < -1.0) output = -1.0; 49253a5a1b3Sopenharmony_ci if (output > 1.0) output = 1.0; 49353a5a1b3Sopenharmony_ci dst[s * 2 + 1] = output; 49453a5a1b3Sopenharmony_ci } 49553a5a1b3Sopenharmony_ci 49653a5a1b3Sopenharmony_ci pa_memblock_release(chunk->memblock); 49753a5a1b3Sopenharmony_ci 49853a5a1b3Sopenharmony_ci return 0; 49953a5a1b3Sopenharmony_ci} 50053a5a1b3Sopenharmony_ci 50153a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 50253a5a1b3Sopenharmony_cistatic void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes_input) { 50353a5a1b3Sopenharmony_ci struct userdata *u; 50453a5a1b3Sopenharmony_ci size_t amount = 0; 50553a5a1b3Sopenharmony_ci size_t nbytes_sink; 50653a5a1b3Sopenharmony_ci 50753a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 50853a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 50953a5a1b3Sopenharmony_ci 51053a5a1b3Sopenharmony_ci nbytes_sink = sink_bytes(u, sink_input_samples(nbytes_input)); 51153a5a1b3Sopenharmony_ci 51253a5a1b3Sopenharmony_ci if (u->sink->thread_info.rewind_nbytes > 0) { 51353a5a1b3Sopenharmony_ci size_t max_rewrite; 51453a5a1b3Sopenharmony_ci 51553a5a1b3Sopenharmony_ci max_rewrite = nbytes_sink + pa_memblockq_get_length(u->memblockq_sink); 51653a5a1b3Sopenharmony_ci amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite); 51753a5a1b3Sopenharmony_ci u->sink->thread_info.rewind_nbytes = 0; 51853a5a1b3Sopenharmony_ci 51953a5a1b3Sopenharmony_ci if (amount > 0) { 52053a5a1b3Sopenharmony_ci pa_memblockq_seek(u->memblockq_sink, - (int64_t) amount, PA_SEEK_RELATIVE, true); 52153a5a1b3Sopenharmony_ci } 52253a5a1b3Sopenharmony_ci } 52353a5a1b3Sopenharmony_ci 52453a5a1b3Sopenharmony_ci pa_sink_process_rewind(u->sink, amount); 52553a5a1b3Sopenharmony_ci 52653a5a1b3Sopenharmony_ci pa_memblockq_rewind(u->memblockq_sink, nbytes_sink); 52753a5a1b3Sopenharmony_ci} 52853a5a1b3Sopenharmony_ci 52953a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 53053a5a1b3Sopenharmony_cistatic void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes_input) { 53153a5a1b3Sopenharmony_ci struct userdata *u; 53253a5a1b3Sopenharmony_ci size_t nbytes_sink, nbytes_memblockq; 53353a5a1b3Sopenharmony_ci 53453a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 53553a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 53653a5a1b3Sopenharmony_ci 53753a5a1b3Sopenharmony_ci nbytes_sink = sink_bytes(u, sink_input_samples(nbytes_input)); 53853a5a1b3Sopenharmony_ci nbytes_memblockq = sink_bytes(u, sink_input_samples(nbytes_input) + u->fftlen); 53953a5a1b3Sopenharmony_ci 54053a5a1b3Sopenharmony_ci /* FIXME: Too small max_rewind: 54153a5a1b3Sopenharmony_ci * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */ 54253a5a1b3Sopenharmony_ci pa_memblockq_set_maxrewind(u->memblockq_sink, nbytes_memblockq); 54353a5a1b3Sopenharmony_ci pa_sink_set_max_rewind_within_thread(u->sink, nbytes_sink); 54453a5a1b3Sopenharmony_ci} 54553a5a1b3Sopenharmony_ci 54653a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 54753a5a1b3Sopenharmony_cistatic void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes_input) { 54853a5a1b3Sopenharmony_ci struct userdata *u; 54953a5a1b3Sopenharmony_ci 55053a5a1b3Sopenharmony_ci size_t nbytes_sink; 55153a5a1b3Sopenharmony_ci 55253a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 55353a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 55453a5a1b3Sopenharmony_ci 55553a5a1b3Sopenharmony_ci nbytes_sink = sink_bytes(u, sink_input_samples(nbytes_input)); 55653a5a1b3Sopenharmony_ci 55753a5a1b3Sopenharmony_ci nbytes_sink = PA_ROUND_UP(nbytes_sink, sink_bytes(u, BLOCK_SIZE)); 55853a5a1b3Sopenharmony_ci pa_sink_set_max_request_within_thread(u->sink, nbytes_sink); 55953a5a1b3Sopenharmony_ci} 56053a5a1b3Sopenharmony_ci 56153a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 56253a5a1b3Sopenharmony_cistatic void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { 56353a5a1b3Sopenharmony_ci struct userdata *u; 56453a5a1b3Sopenharmony_ci 56553a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 56653a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 56753a5a1b3Sopenharmony_ci 56853a5a1b3Sopenharmony_ci pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); 56953a5a1b3Sopenharmony_ci} 57053a5a1b3Sopenharmony_ci 57153a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 57253a5a1b3Sopenharmony_cistatic void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) { 57353a5a1b3Sopenharmony_ci struct userdata *u; 57453a5a1b3Sopenharmony_ci 57553a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 57653a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 57753a5a1b3Sopenharmony_ci 57853a5a1b3Sopenharmony_ci pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); 57953a5a1b3Sopenharmony_ci} 58053a5a1b3Sopenharmony_ci 58153a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 58253a5a1b3Sopenharmony_cistatic void sink_input_detach_cb(pa_sink_input *i) { 58353a5a1b3Sopenharmony_ci struct userdata *u; 58453a5a1b3Sopenharmony_ci 58553a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 58653a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 58753a5a1b3Sopenharmony_ci 58853a5a1b3Sopenharmony_ci if (PA_SINK_IS_LINKED(u->sink->thread_info.state)) 58953a5a1b3Sopenharmony_ci pa_sink_detach_within_thread(u->sink); 59053a5a1b3Sopenharmony_ci 59153a5a1b3Sopenharmony_ci pa_sink_set_rtpoll(u->sink, NULL); 59253a5a1b3Sopenharmony_ci} 59353a5a1b3Sopenharmony_ci 59453a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 59553a5a1b3Sopenharmony_cistatic void sink_input_attach_cb(pa_sink_input *i) { 59653a5a1b3Sopenharmony_ci struct userdata *u; 59753a5a1b3Sopenharmony_ci size_t max_request; 59853a5a1b3Sopenharmony_ci 59953a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 60053a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 60153a5a1b3Sopenharmony_ci 60253a5a1b3Sopenharmony_ci pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll); 60353a5a1b3Sopenharmony_ci pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); 60453a5a1b3Sopenharmony_ci 60553a5a1b3Sopenharmony_ci pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); 60653a5a1b3Sopenharmony_ci 60753a5a1b3Sopenharmony_ci max_request = sink_bytes(u, sink_input_samples(pa_sink_input_get_max_request(i))); 60853a5a1b3Sopenharmony_ci max_request = PA_ROUND_UP(max_request, sink_bytes(u, BLOCK_SIZE)); 60953a5a1b3Sopenharmony_ci pa_sink_set_max_request_within_thread(u->sink, max_request); 61053a5a1b3Sopenharmony_ci 61153a5a1b3Sopenharmony_ci /* FIXME: Too small max_rewind: 61253a5a1b3Sopenharmony_ci * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */ 61353a5a1b3Sopenharmony_ci pa_sink_set_max_rewind_within_thread(u->sink, sink_bytes(u, sink_input_samples(pa_sink_input_get_max_rewind(i)))); 61453a5a1b3Sopenharmony_ci 61553a5a1b3Sopenharmony_ci pa_sink_attach_within_thread(u->sink); 61653a5a1b3Sopenharmony_ci} 61753a5a1b3Sopenharmony_ci 61853a5a1b3Sopenharmony_ci/* Called from main context */ 61953a5a1b3Sopenharmony_cistatic void sink_input_kill_cb(pa_sink_input *i) { 62053a5a1b3Sopenharmony_ci struct userdata *u; 62153a5a1b3Sopenharmony_ci 62253a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 62353a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 62453a5a1b3Sopenharmony_ci 62553a5a1b3Sopenharmony_ci /* The order here matters! We first kill the sink input, followed 62653a5a1b3Sopenharmony_ci * by the sink. That means the sink callbacks must be protected 62753a5a1b3Sopenharmony_ci * against an unconnected sink input! */ 62853a5a1b3Sopenharmony_ci pa_sink_input_cork(u->sink_input, true); 62953a5a1b3Sopenharmony_ci pa_sink_input_unlink(u->sink_input); 63053a5a1b3Sopenharmony_ci pa_sink_unlink(u->sink); 63153a5a1b3Sopenharmony_ci 63253a5a1b3Sopenharmony_ci pa_sink_input_unref(u->sink_input); 63353a5a1b3Sopenharmony_ci u->sink_input = NULL; 63453a5a1b3Sopenharmony_ci 63553a5a1b3Sopenharmony_ci pa_sink_unref(u->sink); 63653a5a1b3Sopenharmony_ci u->sink = NULL; 63753a5a1b3Sopenharmony_ci 63853a5a1b3Sopenharmony_ci pa_module_unload_request(u->module, true); 63953a5a1b3Sopenharmony_ci} 64053a5a1b3Sopenharmony_ci 64153a5a1b3Sopenharmony_ci/* Called from main context */ 64253a5a1b3Sopenharmony_cistatic bool sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { 64353a5a1b3Sopenharmony_ci struct userdata *u; 64453a5a1b3Sopenharmony_ci 64553a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 64653a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 64753a5a1b3Sopenharmony_ci 64853a5a1b3Sopenharmony_ci if (u->autoloaded) 64953a5a1b3Sopenharmony_ci return false; 65053a5a1b3Sopenharmony_ci 65153a5a1b3Sopenharmony_ci return u->sink != dest; 65253a5a1b3Sopenharmony_ci} 65353a5a1b3Sopenharmony_ci 65453a5a1b3Sopenharmony_ci/* Called from main context */ 65553a5a1b3Sopenharmony_cistatic void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { 65653a5a1b3Sopenharmony_ci struct userdata *u; 65753a5a1b3Sopenharmony_ci 65853a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 65953a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 66053a5a1b3Sopenharmony_ci 66153a5a1b3Sopenharmony_ci if (dest) { 66253a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); 66353a5a1b3Sopenharmony_ci pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); 66453a5a1b3Sopenharmony_ci } else 66553a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, NULL); 66653a5a1b3Sopenharmony_ci 66753a5a1b3Sopenharmony_ci if (u->auto_desc && dest) { 66853a5a1b3Sopenharmony_ci const char *z; 66953a5a1b3Sopenharmony_ci pa_proplist *pl; 67053a5a1b3Sopenharmony_ci 67153a5a1b3Sopenharmony_ci pl = pa_proplist_new(); 67253a5a1b3Sopenharmony_ci z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION); 67353a5a1b3Sopenharmony_ci pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Virtual Surround Sink %s on %s", 67453a5a1b3Sopenharmony_ci pa_proplist_gets(u->sink->proplist, "device.vsurroundsink.name"), z ? z : dest->name); 67553a5a1b3Sopenharmony_ci 67653a5a1b3Sopenharmony_ci pa_sink_update_proplist(u->sink, PA_UPDATE_REPLACE, pl); 67753a5a1b3Sopenharmony_ci pa_proplist_free(pl); 67853a5a1b3Sopenharmony_ci } 67953a5a1b3Sopenharmony_ci} 68053a5a1b3Sopenharmony_ci 68153a5a1b3Sopenharmony_ci/* Called from main context */ 68253a5a1b3Sopenharmony_cistatic void sink_input_volume_changed_cb(pa_sink_input *i) { 68353a5a1b3Sopenharmony_ci struct userdata *u; 68453a5a1b3Sopenharmony_ci 68553a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 68653a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 68753a5a1b3Sopenharmony_ci 68853a5a1b3Sopenharmony_ci pa_sink_volume_changed(u->sink, &i->volume); 68953a5a1b3Sopenharmony_ci} 69053a5a1b3Sopenharmony_ci 69153a5a1b3Sopenharmony_ci/* Called from main context */ 69253a5a1b3Sopenharmony_cistatic void sink_input_mute_changed_cb(pa_sink_input *i) { 69353a5a1b3Sopenharmony_ci struct userdata *u; 69453a5a1b3Sopenharmony_ci 69553a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 69653a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 69753a5a1b3Sopenharmony_ci 69853a5a1b3Sopenharmony_ci pa_sink_mute_changed(u->sink, i->muted); 69953a5a1b3Sopenharmony_ci} 70053a5a1b3Sopenharmony_ci 70153a5a1b3Sopenharmony_ciint pa__init(pa_module*m) { 70253a5a1b3Sopenharmony_ci struct userdata *u; 70353a5a1b3Sopenharmony_ci pa_sample_spec ss_input, ss_output; 70453a5a1b3Sopenharmony_ci pa_channel_map map_output; 70553a5a1b3Sopenharmony_ci pa_modargs *ma; 70653a5a1b3Sopenharmony_ci const char *master_name; 70753a5a1b3Sopenharmony_ci const char *hrir_left_file; 70853a5a1b3Sopenharmony_ci const char *hrir_right_file; 70953a5a1b3Sopenharmony_ci pa_sink *master=NULL; 71053a5a1b3Sopenharmony_ci pa_sink_input_new_data sink_input_data; 71153a5a1b3Sopenharmony_ci pa_sink_new_data sink_data; 71253a5a1b3Sopenharmony_ci bool use_volume_sharing = true; 71353a5a1b3Sopenharmony_ci bool force_flat_volume = false; 71453a5a1b3Sopenharmony_ci pa_memchunk silence; 71553a5a1b3Sopenharmony_ci const char* z; 71653a5a1b3Sopenharmony_ci unsigned i, j, ear, found_channel_left, found_channel_right; 71753a5a1b3Sopenharmony_ci 71853a5a1b3Sopenharmony_ci pa_sample_spec ss; 71953a5a1b3Sopenharmony_ci pa_channel_map map; 72053a5a1b3Sopenharmony_ci 72153a5a1b3Sopenharmony_ci float *hrir_data=NULL, *hrir_right_data=NULL; 72253a5a1b3Sopenharmony_ci float *hrir_temp_data; 72353a5a1b3Sopenharmony_ci size_t hrir_samples; 72453a5a1b3Sopenharmony_ci size_t hrir_copied_length, hrir_total_length; 72553a5a1b3Sopenharmony_ci int hrir_channels; 72653a5a1b3Sopenharmony_ci int fftlen; 72753a5a1b3Sopenharmony_ci 72853a5a1b3Sopenharmony_ci float *impulse_temp=NULL; 72953a5a1b3Sopenharmony_ci 73053a5a1b3Sopenharmony_ci unsigned *mapping_left=NULL; 73153a5a1b3Sopenharmony_ci unsigned *mapping_right=NULL; 73253a5a1b3Sopenharmony_ci 73353a5a1b3Sopenharmony_ci fftwf_plan p; 73453a5a1b3Sopenharmony_ci 73553a5a1b3Sopenharmony_ci pa_channel_map hrir_map, hrir_right_map; 73653a5a1b3Sopenharmony_ci 73753a5a1b3Sopenharmony_ci pa_sample_spec hrir_left_temp_ss; 73853a5a1b3Sopenharmony_ci pa_memchunk hrir_left_temp_chunk, hrir_left_temp_chunk_resampled; 73953a5a1b3Sopenharmony_ci pa_resampler *resampler; 74053a5a1b3Sopenharmony_ci 74153a5a1b3Sopenharmony_ci 74253a5a1b3Sopenharmony_ci pa_sample_spec hrir_right_temp_ss; 74353a5a1b3Sopenharmony_ci pa_memchunk hrir_right_temp_chunk, hrir_right_temp_chunk_resampled; 74453a5a1b3Sopenharmony_ci 74553a5a1b3Sopenharmony_ci pa_assert(m); 74653a5a1b3Sopenharmony_ci 74753a5a1b3Sopenharmony_ci hrir_left_temp_chunk.memblock = NULL; 74853a5a1b3Sopenharmony_ci hrir_left_temp_chunk_resampled.memblock = NULL; 74953a5a1b3Sopenharmony_ci hrir_right_temp_chunk.memblock = NULL; 75053a5a1b3Sopenharmony_ci hrir_right_temp_chunk_resampled.memblock = NULL; 75153a5a1b3Sopenharmony_ci 75253a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 75353a5a1b3Sopenharmony_ci pa_log("Failed to parse module arguments."); 75453a5a1b3Sopenharmony_ci goto fail; 75553a5a1b3Sopenharmony_ci } 75653a5a1b3Sopenharmony_ci 75753a5a1b3Sopenharmony_ci master_name = pa_modargs_get_value(ma, "sink_master", NULL); 75853a5a1b3Sopenharmony_ci if (!master_name) { 75953a5a1b3Sopenharmony_ci master_name = pa_modargs_get_value(ma, "master", NULL); 76053a5a1b3Sopenharmony_ci if (master_name) 76153a5a1b3Sopenharmony_ci pa_log_warn("The 'master' module argument is deprecated and may be removed in the future, " 76253a5a1b3Sopenharmony_ci "please use the 'sink_master' argument instead."); 76353a5a1b3Sopenharmony_ci } 76453a5a1b3Sopenharmony_ci 76553a5a1b3Sopenharmony_ci if (!(master = pa_namereg_get(m->core, master_name, PA_NAMEREG_SINK))) { 76653a5a1b3Sopenharmony_ci pa_log("Master sink not found"); 76753a5a1b3Sopenharmony_ci goto fail; 76853a5a1b3Sopenharmony_ci } 76953a5a1b3Sopenharmony_ci 77053a5a1b3Sopenharmony_ci hrir_left_file = pa_modargs_get_value(ma, "hrir_left", NULL); 77153a5a1b3Sopenharmony_ci if (!hrir_left_file) { 77253a5a1b3Sopenharmony_ci hrir_left_file = pa_modargs_get_value(ma, "hrir", NULL); 77353a5a1b3Sopenharmony_ci if (!hrir_left_file) { 77453a5a1b3Sopenharmony_ci pa_log("Either the 'hrir' or 'hrir_left' module arguments are required."); 77553a5a1b3Sopenharmony_ci goto fail; 77653a5a1b3Sopenharmony_ci } 77753a5a1b3Sopenharmony_ci } 77853a5a1b3Sopenharmony_ci 77953a5a1b3Sopenharmony_ci hrir_right_file = pa_modargs_get_value(ma, "hrir_right", NULL); 78053a5a1b3Sopenharmony_ci 78153a5a1b3Sopenharmony_ci pa_assert(master); 78253a5a1b3Sopenharmony_ci 78353a5a1b3Sopenharmony_ci if (pa_sound_file_load(master->core->mempool, hrir_left_file, &hrir_left_temp_ss, &hrir_map, &hrir_left_temp_chunk, NULL) < 0) { 78453a5a1b3Sopenharmony_ci pa_log("Cannot load hrir file."); 78553a5a1b3Sopenharmony_ci goto fail; 78653a5a1b3Sopenharmony_ci } 78753a5a1b3Sopenharmony_ci 78853a5a1b3Sopenharmony_ci if (hrir_right_file) { 78953a5a1b3Sopenharmony_ci if (pa_sound_file_load(master->core->mempool, hrir_right_file, &hrir_right_temp_ss, &hrir_right_map, &hrir_right_temp_chunk, NULL) < 0) { 79053a5a1b3Sopenharmony_ci pa_log("Cannot load hrir_right file."); 79153a5a1b3Sopenharmony_ci goto fail; 79253a5a1b3Sopenharmony_ci } 79353a5a1b3Sopenharmony_ci if (!pa_sample_spec_equal(&hrir_left_temp_ss, &hrir_right_temp_ss)) { 79453a5a1b3Sopenharmony_ci pa_log("Both hrir_left and hrir_right must have the same sample format"); 79553a5a1b3Sopenharmony_ci goto fail; 79653a5a1b3Sopenharmony_ci } 79753a5a1b3Sopenharmony_ci if (!pa_channel_map_equal(&hrir_map, &hrir_right_map)) { 79853a5a1b3Sopenharmony_ci pa_log("Both hrir_left and hrir_right must have the same channel layout"); 79953a5a1b3Sopenharmony_ci goto fail; 80053a5a1b3Sopenharmony_ci } 80153a5a1b3Sopenharmony_ci } 80253a5a1b3Sopenharmony_ci 80353a5a1b3Sopenharmony_ci ss_input.format = PA_SAMPLE_FLOAT32NE; 80453a5a1b3Sopenharmony_ci ss_input.rate = master->sample_spec.rate; 80553a5a1b3Sopenharmony_ci ss_input.channels = hrir_left_temp_ss.channels; 80653a5a1b3Sopenharmony_ci 80753a5a1b3Sopenharmony_ci ss = ss_input; 80853a5a1b3Sopenharmony_ci map = hrir_map; 80953a5a1b3Sopenharmony_ci if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { 81053a5a1b3Sopenharmony_ci pa_log("Invalid sample format specification or channel map"); 81153a5a1b3Sopenharmony_ci goto fail; 81253a5a1b3Sopenharmony_ci } 81353a5a1b3Sopenharmony_ci 81453a5a1b3Sopenharmony_ci ss.format = PA_SAMPLE_FLOAT32NE; 81553a5a1b3Sopenharmony_ci ss_input.rate = ss.rate; 81653a5a1b3Sopenharmony_ci ss_input.channels = ss.channels; 81753a5a1b3Sopenharmony_ci 81853a5a1b3Sopenharmony_ci ss_output = ss_input; 81953a5a1b3Sopenharmony_ci ss_output.channels = 2; 82053a5a1b3Sopenharmony_ci 82153a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) { 82253a5a1b3Sopenharmony_ci pa_log("use_volume_sharing= expects a boolean argument"); 82353a5a1b3Sopenharmony_ci goto fail; 82453a5a1b3Sopenharmony_ci } 82553a5a1b3Sopenharmony_ci 82653a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) { 82753a5a1b3Sopenharmony_ci pa_log("force_flat_volume= expects a boolean argument"); 82853a5a1b3Sopenharmony_ci goto fail; 82953a5a1b3Sopenharmony_ci } 83053a5a1b3Sopenharmony_ci 83153a5a1b3Sopenharmony_ci if (use_volume_sharing && force_flat_volume) { 83253a5a1b3Sopenharmony_ci pa_log("Flat volume can't be forced when using volume sharing."); 83353a5a1b3Sopenharmony_ci goto fail; 83453a5a1b3Sopenharmony_ci } 83553a5a1b3Sopenharmony_ci 83653a5a1b3Sopenharmony_ci pa_channel_map_init_stereo(&map_output); 83753a5a1b3Sopenharmony_ci 83853a5a1b3Sopenharmony_ci u = pa_xnew0(struct userdata, 1); 83953a5a1b3Sopenharmony_ci u->module = m; 84053a5a1b3Sopenharmony_ci m->userdata = u; 84153a5a1b3Sopenharmony_ci 84253a5a1b3Sopenharmony_ci /* Create sink */ 84353a5a1b3Sopenharmony_ci pa_sink_new_data_init(&sink_data); 84453a5a1b3Sopenharmony_ci sink_data.driver = __FILE__; 84553a5a1b3Sopenharmony_ci sink_data.module = m; 84653a5a1b3Sopenharmony_ci if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL)))) 84753a5a1b3Sopenharmony_ci sink_data.name = pa_sprintf_malloc("%s.vsurroundsink", master->name); 84853a5a1b3Sopenharmony_ci pa_sink_new_data_set_sample_spec(&sink_data, &ss_input); 84953a5a1b3Sopenharmony_ci pa_sink_new_data_set_channel_map(&sink_data, &map); 85053a5a1b3Sopenharmony_ci pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); 85153a5a1b3Sopenharmony_ci pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); 85253a5a1b3Sopenharmony_ci pa_proplist_sets(sink_data.proplist, "device.vsurroundsink.name", sink_data.name); 85353a5a1b3Sopenharmony_ci 85453a5a1b3Sopenharmony_ci if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) { 85553a5a1b3Sopenharmony_ci pa_log("Invalid properties"); 85653a5a1b3Sopenharmony_ci pa_sink_new_data_done(&sink_data); 85753a5a1b3Sopenharmony_ci goto fail; 85853a5a1b3Sopenharmony_ci } 85953a5a1b3Sopenharmony_ci 86053a5a1b3Sopenharmony_ci u->autoloaded = DEFAULT_AUTOLOADED; 86153a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "autoloaded", &u->autoloaded) < 0) { 86253a5a1b3Sopenharmony_ci pa_log("Failed to parse autoloaded value"); 86353a5a1b3Sopenharmony_ci goto fail; 86453a5a1b3Sopenharmony_ci } 86553a5a1b3Sopenharmony_ci 86653a5a1b3Sopenharmony_ci if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) { 86753a5a1b3Sopenharmony_ci z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); 86853a5a1b3Sopenharmony_ci pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Surround Sink %s on %s", sink_data.name, z ? z : master->name); 86953a5a1b3Sopenharmony_ci } 87053a5a1b3Sopenharmony_ci 87153a5a1b3Sopenharmony_ci u->sink = pa_sink_new(m->core, &sink_data, (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)) 87253a5a1b3Sopenharmony_ci | (use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0)); 87353a5a1b3Sopenharmony_ci pa_sink_new_data_done(&sink_data); 87453a5a1b3Sopenharmony_ci 87553a5a1b3Sopenharmony_ci if (!u->sink) { 87653a5a1b3Sopenharmony_ci pa_log("Failed to create sink."); 87753a5a1b3Sopenharmony_ci goto fail; 87853a5a1b3Sopenharmony_ci } 87953a5a1b3Sopenharmony_ci 88053a5a1b3Sopenharmony_ci u->sink->parent.process_msg = sink_process_msg_cb; 88153a5a1b3Sopenharmony_ci u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb; 88253a5a1b3Sopenharmony_ci u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb; 88353a5a1b3Sopenharmony_ci u->sink->update_requested_latency = sink_update_requested_latency_cb; 88453a5a1b3Sopenharmony_ci u->sink->request_rewind = sink_request_rewind_cb; 88553a5a1b3Sopenharmony_ci pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb); 88653a5a1b3Sopenharmony_ci if (!use_volume_sharing) { 88753a5a1b3Sopenharmony_ci pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); 88853a5a1b3Sopenharmony_ci pa_sink_enable_decibel_volume(u->sink, true); 88953a5a1b3Sopenharmony_ci } 89053a5a1b3Sopenharmony_ci /* Normally this flag would be enabled automatically but we can force it. */ 89153a5a1b3Sopenharmony_ci if (force_flat_volume) 89253a5a1b3Sopenharmony_ci u->sink->flags |= PA_SINK_FLAT_VOLUME; 89353a5a1b3Sopenharmony_ci u->sink->userdata = u; 89453a5a1b3Sopenharmony_ci 89553a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); 89653a5a1b3Sopenharmony_ci 89753a5a1b3Sopenharmony_ci /* Create sink input */ 89853a5a1b3Sopenharmony_ci pa_sink_input_new_data_init(&sink_input_data); 89953a5a1b3Sopenharmony_ci sink_input_data.driver = __FILE__; 90053a5a1b3Sopenharmony_ci sink_input_data.module = m; 90153a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_sink(&sink_input_data, master, false, true); 90253a5a1b3Sopenharmony_ci sink_input_data.origin_sink = u->sink; 90353a5a1b3Sopenharmony_ci pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Surround Sink Stream from %s", pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)); 90453a5a1b3Sopenharmony_ci pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); 90553a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss_output); 90653a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_channel_map(&sink_input_data, &map_output); 90753a5a1b3Sopenharmony_ci 90853a5a1b3Sopenharmony_ci pa_sink_input_new(&u->sink_input, m->core, &sink_input_data); 90953a5a1b3Sopenharmony_ci pa_sink_input_new_data_done(&sink_input_data); 91053a5a1b3Sopenharmony_ci 91153a5a1b3Sopenharmony_ci if (!u->sink_input) 91253a5a1b3Sopenharmony_ci goto fail; 91353a5a1b3Sopenharmony_ci 91453a5a1b3Sopenharmony_ci u->sink_input->pop = sink_input_pop_cb; 91553a5a1b3Sopenharmony_ci u->sink_input->process_rewind = sink_input_process_rewind_cb; 91653a5a1b3Sopenharmony_ci u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; 91753a5a1b3Sopenharmony_ci u->sink_input->update_max_request = sink_input_update_max_request_cb; 91853a5a1b3Sopenharmony_ci u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb; 91953a5a1b3Sopenharmony_ci u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb; 92053a5a1b3Sopenharmony_ci u->sink_input->kill = sink_input_kill_cb; 92153a5a1b3Sopenharmony_ci u->sink_input->attach = sink_input_attach_cb; 92253a5a1b3Sopenharmony_ci u->sink_input->detach = sink_input_detach_cb; 92353a5a1b3Sopenharmony_ci u->sink_input->may_move_to = sink_input_may_move_to_cb; 92453a5a1b3Sopenharmony_ci u->sink_input->moving = sink_input_moving_cb; 92553a5a1b3Sopenharmony_ci u->sink_input->volume_changed = use_volume_sharing ? NULL : sink_input_volume_changed_cb; 92653a5a1b3Sopenharmony_ci u->sink_input->mute_changed = sink_input_mute_changed_cb; 92753a5a1b3Sopenharmony_ci u->sink_input->userdata = u; 92853a5a1b3Sopenharmony_ci 92953a5a1b3Sopenharmony_ci u->sink->input_to_master = u->sink_input; 93053a5a1b3Sopenharmony_ci 93153a5a1b3Sopenharmony_ci pa_sink_input_get_silence(u->sink_input, &silence); 93253a5a1b3Sopenharmony_ci 93353a5a1b3Sopenharmony_ci resampler = pa_resampler_new(u->sink->core->mempool, &hrir_left_temp_ss, &hrir_map, &ss_input, &hrir_map, u->sink->core->lfe_crossover_freq, 93453a5a1b3Sopenharmony_ci PA_RESAMPLER_SRC_SINC_BEST_QUALITY, PA_RESAMPLER_NO_REMAP); 93553a5a1b3Sopenharmony_ci 93653a5a1b3Sopenharmony_ci hrir_samples = hrir_left_temp_chunk.length / pa_frame_size(&hrir_left_temp_ss) * ss_input.rate / hrir_left_temp_ss.rate; 93753a5a1b3Sopenharmony_ci 93853a5a1b3Sopenharmony_ci hrir_total_length = hrir_samples * pa_frame_size(&ss_input); 93953a5a1b3Sopenharmony_ci hrir_channels = ss_input.channels; 94053a5a1b3Sopenharmony_ci 94153a5a1b3Sopenharmony_ci hrir_data = (float *) pa_xmalloc(hrir_total_length); 94253a5a1b3Sopenharmony_ci hrir_copied_length = 0; 94353a5a1b3Sopenharmony_ci 94453a5a1b3Sopenharmony_ci u->hrir_samples = hrir_samples; 94553a5a1b3Sopenharmony_ci u->inputs = hrir_channels; 94653a5a1b3Sopenharmony_ci 94753a5a1b3Sopenharmony_ci /* add silence to the hrir until we get enough samples out of the resampler */ 94853a5a1b3Sopenharmony_ci while (hrir_copied_length < hrir_total_length) { 94953a5a1b3Sopenharmony_ci pa_resampler_run(resampler, &hrir_left_temp_chunk, &hrir_left_temp_chunk_resampled); 95053a5a1b3Sopenharmony_ci if (hrir_left_temp_chunk.memblock != hrir_left_temp_chunk_resampled.memblock) { 95153a5a1b3Sopenharmony_ci /* Silence input block */ 95253a5a1b3Sopenharmony_ci pa_silence_memblock(hrir_left_temp_chunk.memblock, &hrir_left_temp_ss); 95353a5a1b3Sopenharmony_ci } 95453a5a1b3Sopenharmony_ci 95553a5a1b3Sopenharmony_ci if (hrir_left_temp_chunk_resampled.memblock) { 95653a5a1b3Sopenharmony_ci /* Copy hrir data */ 95753a5a1b3Sopenharmony_ci hrir_temp_data = (float *) pa_memblock_acquire(hrir_left_temp_chunk_resampled.memblock); 95853a5a1b3Sopenharmony_ci 95953a5a1b3Sopenharmony_ci if (hrir_total_length - hrir_copied_length >= hrir_left_temp_chunk_resampled.length) { 96053a5a1b3Sopenharmony_ci memcpy(hrir_data + hrir_copied_length, hrir_temp_data, hrir_left_temp_chunk_resampled.length); 96153a5a1b3Sopenharmony_ci hrir_copied_length += hrir_left_temp_chunk_resampled.length; 96253a5a1b3Sopenharmony_ci } else { 96353a5a1b3Sopenharmony_ci memcpy(hrir_data + hrir_copied_length, hrir_temp_data, hrir_total_length - hrir_copied_length); 96453a5a1b3Sopenharmony_ci hrir_copied_length = hrir_total_length; 96553a5a1b3Sopenharmony_ci } 96653a5a1b3Sopenharmony_ci 96753a5a1b3Sopenharmony_ci pa_memblock_release(hrir_left_temp_chunk_resampled.memblock); 96853a5a1b3Sopenharmony_ci pa_memblock_unref(hrir_left_temp_chunk_resampled.memblock); 96953a5a1b3Sopenharmony_ci hrir_left_temp_chunk_resampled.memblock = NULL; 97053a5a1b3Sopenharmony_ci } 97153a5a1b3Sopenharmony_ci } 97253a5a1b3Sopenharmony_ci 97353a5a1b3Sopenharmony_ci pa_memblock_unref(hrir_left_temp_chunk.memblock); 97453a5a1b3Sopenharmony_ci hrir_left_temp_chunk.memblock = NULL; 97553a5a1b3Sopenharmony_ci 97653a5a1b3Sopenharmony_ci if (hrir_right_file) { 97753a5a1b3Sopenharmony_ci pa_resampler_reset(resampler); 97853a5a1b3Sopenharmony_ci 97953a5a1b3Sopenharmony_ci hrir_right_data = (float *) pa_xmalloc(hrir_total_length); 98053a5a1b3Sopenharmony_ci hrir_copied_length = 0; 98153a5a1b3Sopenharmony_ci 98253a5a1b3Sopenharmony_ci while (hrir_copied_length < hrir_total_length) { 98353a5a1b3Sopenharmony_ci pa_resampler_run(resampler, &hrir_right_temp_chunk, &hrir_right_temp_chunk_resampled); 98453a5a1b3Sopenharmony_ci if (hrir_right_temp_chunk.memblock != hrir_right_temp_chunk_resampled.memblock) { 98553a5a1b3Sopenharmony_ci /* Silence input block */ 98653a5a1b3Sopenharmony_ci pa_silence_memblock(hrir_right_temp_chunk.memblock, &hrir_right_temp_ss); 98753a5a1b3Sopenharmony_ci } 98853a5a1b3Sopenharmony_ci 98953a5a1b3Sopenharmony_ci if (hrir_right_temp_chunk_resampled.memblock) { 99053a5a1b3Sopenharmony_ci /* Copy hrir data */ 99153a5a1b3Sopenharmony_ci hrir_temp_data = (float *) pa_memblock_acquire(hrir_right_temp_chunk_resampled.memblock); 99253a5a1b3Sopenharmony_ci 99353a5a1b3Sopenharmony_ci if (hrir_total_length - hrir_copied_length >= hrir_right_temp_chunk_resampled.length) { 99453a5a1b3Sopenharmony_ci memcpy(hrir_right_data + hrir_copied_length, hrir_temp_data, hrir_right_temp_chunk_resampled.length); 99553a5a1b3Sopenharmony_ci hrir_copied_length += hrir_right_temp_chunk_resampled.length; 99653a5a1b3Sopenharmony_ci } else { 99753a5a1b3Sopenharmony_ci memcpy(hrir_right_data + hrir_copied_length, hrir_temp_data, hrir_total_length - hrir_copied_length); 99853a5a1b3Sopenharmony_ci hrir_copied_length = hrir_total_length; 99953a5a1b3Sopenharmony_ci } 100053a5a1b3Sopenharmony_ci 100153a5a1b3Sopenharmony_ci pa_memblock_release(hrir_right_temp_chunk_resampled.memblock); 100253a5a1b3Sopenharmony_ci pa_memblock_unref(hrir_right_temp_chunk_resampled.memblock); 100353a5a1b3Sopenharmony_ci hrir_right_temp_chunk_resampled.memblock = NULL; 100453a5a1b3Sopenharmony_ci } 100553a5a1b3Sopenharmony_ci } 100653a5a1b3Sopenharmony_ci 100753a5a1b3Sopenharmony_ci pa_memblock_unref(hrir_right_temp_chunk.memblock); 100853a5a1b3Sopenharmony_ci hrir_right_temp_chunk.memblock = NULL; 100953a5a1b3Sopenharmony_ci } 101053a5a1b3Sopenharmony_ci 101153a5a1b3Sopenharmony_ci pa_resampler_free(resampler); 101253a5a1b3Sopenharmony_ci 101353a5a1b3Sopenharmony_ci if (hrir_right_data) 101453a5a1b3Sopenharmony_ci normalize_hrir_stereo(hrir_data, hrir_right_data, hrir_samples, hrir_channels); 101553a5a1b3Sopenharmony_ci else 101653a5a1b3Sopenharmony_ci normalize_hrir(hrir_data, hrir_samples, hrir_channels); 101753a5a1b3Sopenharmony_ci 101853a5a1b3Sopenharmony_ci /* create mapping between hrir and input */ 101953a5a1b3Sopenharmony_ci mapping_left = (unsigned *) pa_xnew0(unsigned, hrir_channels); 102053a5a1b3Sopenharmony_ci mapping_right = (unsigned *) pa_xnew0(unsigned, hrir_channels); 102153a5a1b3Sopenharmony_ci for (i = 0; i < map.channels; i++) { 102253a5a1b3Sopenharmony_ci found_channel_left = 0; 102353a5a1b3Sopenharmony_ci found_channel_right = 0; 102453a5a1b3Sopenharmony_ci 102553a5a1b3Sopenharmony_ci for (j = 0; j < hrir_map.channels; j++) { 102653a5a1b3Sopenharmony_ci if (hrir_map.map[j] == map.map[i]) { 102753a5a1b3Sopenharmony_ci mapping_left[i] = j; 102853a5a1b3Sopenharmony_ci found_channel_left = 1; 102953a5a1b3Sopenharmony_ci } 103053a5a1b3Sopenharmony_ci 103153a5a1b3Sopenharmony_ci if (hrir_map.map[j] == mirror_channel(map.map[i])) { 103253a5a1b3Sopenharmony_ci mapping_right[i] = j; 103353a5a1b3Sopenharmony_ci found_channel_right = 1; 103453a5a1b3Sopenharmony_ci } 103553a5a1b3Sopenharmony_ci } 103653a5a1b3Sopenharmony_ci 103753a5a1b3Sopenharmony_ci if (!found_channel_left) { 103853a5a1b3Sopenharmony_ci pa_log("Cannot find mapping for channel %s", pa_channel_position_to_string(map.map[i])); 103953a5a1b3Sopenharmony_ci goto fail; 104053a5a1b3Sopenharmony_ci } 104153a5a1b3Sopenharmony_ci if (!found_channel_right) { 104253a5a1b3Sopenharmony_ci pa_log("Cannot find mapping for channel %s", pa_channel_position_to_string(mirror_channel(map.map[i]))); 104353a5a1b3Sopenharmony_ci goto fail; 104453a5a1b3Sopenharmony_ci } 104553a5a1b3Sopenharmony_ci } 104653a5a1b3Sopenharmony_ci 104753a5a1b3Sopenharmony_ci fftlen = (hrir_samples + BLOCK_SIZE + 1); /* Grow a bit for overlap */ 104853a5a1b3Sopenharmony_ci { 104953a5a1b3Sopenharmony_ci /* Round up to a power of two */ 105053a5a1b3Sopenharmony_ci int pow = 1; 105153a5a1b3Sopenharmony_ci while (fftlen > 2) { pow++; fftlen /= 2; } 105253a5a1b3Sopenharmony_ci fftlen = 2 << pow; 105353a5a1b3Sopenharmony_ci } 105453a5a1b3Sopenharmony_ci 105553a5a1b3Sopenharmony_ci u->fftlen = fftlen; 105653a5a1b3Sopenharmony_ci 105753a5a1b3Sopenharmony_ci u->f_in = (fftwf_complex*) alloc(sizeof(fftwf_complex), (fftlen/2+1)); 105853a5a1b3Sopenharmony_ci u->f_out = (fftwf_complex*) alloc(sizeof(fftwf_complex), (fftlen/2+1)); 105953a5a1b3Sopenharmony_ci 106053a5a1b3Sopenharmony_ci u->f_ir = (fftwf_complex**) alloc(sizeof(fftwf_complex*), (hrir_channels*2)); 106153a5a1b3Sopenharmony_ci for (i = 0, j = hrir_channels*2; i < j; i++) 106253a5a1b3Sopenharmony_ci u->f_ir[i] = (fftwf_complex*) alloc(sizeof(fftwf_complex), (fftlen/2+1)); 106353a5a1b3Sopenharmony_ci 106453a5a1b3Sopenharmony_ci u->revspace = (float*) alloc(sizeof(float), fftlen); 106553a5a1b3Sopenharmony_ci 106653a5a1b3Sopenharmony_ci u->outspace[0] = (float*) alloc(sizeof(float), BLOCK_SIZE); 106753a5a1b3Sopenharmony_ci u->outspace[1] = (float*) alloc(sizeof(float), BLOCK_SIZE); 106853a5a1b3Sopenharmony_ci 106953a5a1b3Sopenharmony_ci u->inspace = (float**) alloc(sizeof(float*), hrir_channels); 107053a5a1b3Sopenharmony_ci for (i = 0; i < hrir_channels; i++) 107153a5a1b3Sopenharmony_ci u->inspace[i] = (float*) alloc(sizeof(float), fftlen); 107253a5a1b3Sopenharmony_ci 107353a5a1b3Sopenharmony_ci u->p_fw = (fftwf_plan*) alloc(sizeof(fftwf_plan), hrir_channels); 107453a5a1b3Sopenharmony_ci for (i = 0; i < hrir_channels; i++) 107553a5a1b3Sopenharmony_ci pa_assert_se(u->p_fw[i] = fftwf_plan_dft_r2c_1d(fftlen, u->inspace[i], u->f_in, FFTW_ESTIMATE)); 107653a5a1b3Sopenharmony_ci 107753a5a1b3Sopenharmony_ci pa_assert_se(u->p_bw = fftwf_plan_dft_c2r_1d(fftlen, u->f_out, u->revspace, FFTW_ESTIMATE)); 107853a5a1b3Sopenharmony_ci 107953a5a1b3Sopenharmony_ci impulse_temp = (float*) alloc(sizeof(float), fftlen); 108053a5a1b3Sopenharmony_ci 108153a5a1b3Sopenharmony_ci if (hrir_right_data) { 108253a5a1b3Sopenharmony_ci for (i = 0; i < hrir_channels; i++) { 108353a5a1b3Sopenharmony_ci for (ear = 0; ear < 2; ear++) { 108453a5a1b3Sopenharmony_ci size_t index = i * 2 + ear; 108553a5a1b3Sopenharmony_ci size_t impulse_index = mapping_left[i]; 108653a5a1b3Sopenharmony_ci float *impulse = (ear == 0) ? hrir_data : hrir_right_data; 108753a5a1b3Sopenharmony_ci for (j = 0; j < hrir_samples; j++) { 108853a5a1b3Sopenharmony_ci impulse_temp[j] = impulse[j * hrir_channels + impulse_index]; 108953a5a1b3Sopenharmony_ci } 109053a5a1b3Sopenharmony_ci 109153a5a1b3Sopenharmony_ci p = fftwf_plan_dft_r2c_1d(fftlen, impulse_temp, u->f_ir[index], FFTW_ESTIMATE); 109253a5a1b3Sopenharmony_ci if (p) { 109353a5a1b3Sopenharmony_ci fftwf_execute(p); 109453a5a1b3Sopenharmony_ci fftwf_destroy_plan(p); 109553a5a1b3Sopenharmony_ci } else { 109653a5a1b3Sopenharmony_ci pa_log("fftw plan creation failed for %s ear speaker index %d", (ear == 0) ? "left" : "right", i); 109753a5a1b3Sopenharmony_ci goto fail; 109853a5a1b3Sopenharmony_ci } 109953a5a1b3Sopenharmony_ci } 110053a5a1b3Sopenharmony_ci } 110153a5a1b3Sopenharmony_ci } else { 110253a5a1b3Sopenharmony_ci for (i = 0; i < hrir_channels; i++) { 110353a5a1b3Sopenharmony_ci for (ear = 0; ear < 2; ear++) { 110453a5a1b3Sopenharmony_ci size_t index = i * 2 + ear; 110553a5a1b3Sopenharmony_ci size_t impulse_index = (ear == 0) ? mapping_left[i] : mapping_right[i]; 110653a5a1b3Sopenharmony_ci for (j = 0; j < hrir_samples; j++) { 110753a5a1b3Sopenharmony_ci impulse_temp[j] = hrir_data[j * hrir_channels + impulse_index]; 110853a5a1b3Sopenharmony_ci } 110953a5a1b3Sopenharmony_ci 111053a5a1b3Sopenharmony_ci p = fftwf_plan_dft_r2c_1d(fftlen, impulse_temp, u->f_ir[index], FFTW_ESTIMATE); 111153a5a1b3Sopenharmony_ci if (p) { 111253a5a1b3Sopenharmony_ci fftwf_execute(p); 111353a5a1b3Sopenharmony_ci fftwf_destroy_plan(p); 111453a5a1b3Sopenharmony_ci } else { 111553a5a1b3Sopenharmony_ci pa_log("fftw plan creation failed for %s ear speaker index %d", (ear == 0) ? "left" : "right", i); 111653a5a1b3Sopenharmony_ci goto fail; 111753a5a1b3Sopenharmony_ci } 111853a5a1b3Sopenharmony_ci } 111953a5a1b3Sopenharmony_ci } 112053a5a1b3Sopenharmony_ci } 112153a5a1b3Sopenharmony_ci 112253a5a1b3Sopenharmony_ci pa_xfree(impulse_temp); 112353a5a1b3Sopenharmony_ci 112453a5a1b3Sopenharmony_ci pa_xfree(hrir_data); 112553a5a1b3Sopenharmony_ci if (hrir_right_data) 112653a5a1b3Sopenharmony_ci pa_xfree(hrir_right_data); 112753a5a1b3Sopenharmony_ci 112853a5a1b3Sopenharmony_ci pa_xfree(mapping_left); 112953a5a1b3Sopenharmony_ci pa_xfree(mapping_right); 113053a5a1b3Sopenharmony_ci 113153a5a1b3Sopenharmony_ci u->memblockq_sink = pa_memblockq_new("module-virtual-surround-sink memblockq (input)", 0, MEMBLOCKQ_MAXLENGTH, sink_bytes(u, BLOCK_SIZE), &ss_input, 0, 0, sink_bytes(u, u->fftlen), &silence); 113253a5a1b3Sopenharmony_ci pa_memblock_unref(silence.memblock); 113353a5a1b3Sopenharmony_ci 113453a5a1b3Sopenharmony_ci pa_memblockq_seek(u->memblockq_sink, sink_bytes(u, u->fftlen - BLOCK_SIZE), PA_SEEK_RELATIVE, false); 113553a5a1b3Sopenharmony_ci pa_memblockq_flush_read(u->memblockq_sink); 113653a5a1b3Sopenharmony_ci 113753a5a1b3Sopenharmony_ci pa_sink_put(u->sink); 113853a5a1b3Sopenharmony_ci pa_sink_input_put(u->sink_input); 113953a5a1b3Sopenharmony_ci 114053a5a1b3Sopenharmony_ci pa_modargs_free(ma); 114153a5a1b3Sopenharmony_ci 114253a5a1b3Sopenharmony_ci return 0; 114353a5a1b3Sopenharmony_ci 114453a5a1b3Sopenharmony_cifail: 114553a5a1b3Sopenharmony_ci if (impulse_temp) 114653a5a1b3Sopenharmony_ci pa_xfree(impulse_temp); 114753a5a1b3Sopenharmony_ci 114853a5a1b3Sopenharmony_ci if (mapping_left) 114953a5a1b3Sopenharmony_ci pa_xfree(mapping_left); 115053a5a1b3Sopenharmony_ci 115153a5a1b3Sopenharmony_ci if (mapping_right) 115253a5a1b3Sopenharmony_ci pa_xfree(mapping_right); 115353a5a1b3Sopenharmony_ci 115453a5a1b3Sopenharmony_ci if (hrir_data) 115553a5a1b3Sopenharmony_ci pa_xfree(hrir_data); 115653a5a1b3Sopenharmony_ci 115753a5a1b3Sopenharmony_ci if (hrir_right_data) 115853a5a1b3Sopenharmony_ci pa_xfree(hrir_right_data); 115953a5a1b3Sopenharmony_ci 116053a5a1b3Sopenharmony_ci if (hrir_left_temp_chunk.memblock) 116153a5a1b3Sopenharmony_ci pa_memblock_unref(hrir_left_temp_chunk.memblock); 116253a5a1b3Sopenharmony_ci 116353a5a1b3Sopenharmony_ci if (hrir_left_temp_chunk_resampled.memblock) 116453a5a1b3Sopenharmony_ci pa_memblock_unref(hrir_left_temp_chunk_resampled.memblock); 116553a5a1b3Sopenharmony_ci 116653a5a1b3Sopenharmony_ci if (hrir_right_temp_chunk.memblock) 116753a5a1b3Sopenharmony_ci pa_memblock_unref(hrir_right_temp_chunk.memblock); 116853a5a1b3Sopenharmony_ci 116953a5a1b3Sopenharmony_ci if (hrir_right_temp_chunk_resampled.memblock) 117053a5a1b3Sopenharmony_ci pa_memblock_unref(hrir_right_temp_chunk_resampled.memblock); 117153a5a1b3Sopenharmony_ci 117253a5a1b3Sopenharmony_ci if (ma) 117353a5a1b3Sopenharmony_ci pa_modargs_free(ma); 117453a5a1b3Sopenharmony_ci 117553a5a1b3Sopenharmony_ci pa__done(m); 117653a5a1b3Sopenharmony_ci 117753a5a1b3Sopenharmony_ci return -1; 117853a5a1b3Sopenharmony_ci} 117953a5a1b3Sopenharmony_ci 118053a5a1b3Sopenharmony_ciint pa__get_n_used(pa_module *m) { 118153a5a1b3Sopenharmony_ci struct userdata *u; 118253a5a1b3Sopenharmony_ci 118353a5a1b3Sopenharmony_ci pa_assert(m); 118453a5a1b3Sopenharmony_ci pa_assert_se(u = m->userdata); 118553a5a1b3Sopenharmony_ci 118653a5a1b3Sopenharmony_ci return pa_sink_linked_by(u->sink); 118753a5a1b3Sopenharmony_ci} 118853a5a1b3Sopenharmony_ci 118953a5a1b3Sopenharmony_civoid pa__done(pa_module*m) { 119053a5a1b3Sopenharmony_ci size_t i, j; 119153a5a1b3Sopenharmony_ci struct userdata *u; 119253a5a1b3Sopenharmony_ci 119353a5a1b3Sopenharmony_ci pa_assert(m); 119453a5a1b3Sopenharmony_ci 119553a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 119653a5a1b3Sopenharmony_ci return; 119753a5a1b3Sopenharmony_ci 119853a5a1b3Sopenharmony_ci /* See comments in sink_input_kill_cb() above regarding 119953a5a1b3Sopenharmony_ci * destruction order! */ 120053a5a1b3Sopenharmony_ci 120153a5a1b3Sopenharmony_ci if (u->sink_input) 120253a5a1b3Sopenharmony_ci pa_sink_input_unlink(u->sink_input); 120353a5a1b3Sopenharmony_ci 120453a5a1b3Sopenharmony_ci if (u->sink) 120553a5a1b3Sopenharmony_ci pa_sink_unlink(u->sink); 120653a5a1b3Sopenharmony_ci 120753a5a1b3Sopenharmony_ci if (u->sink_input) 120853a5a1b3Sopenharmony_ci pa_sink_input_unref(u->sink_input); 120953a5a1b3Sopenharmony_ci 121053a5a1b3Sopenharmony_ci if (u->sink) 121153a5a1b3Sopenharmony_ci pa_sink_unref(u->sink); 121253a5a1b3Sopenharmony_ci 121353a5a1b3Sopenharmony_ci if (u->memblockq_sink) 121453a5a1b3Sopenharmony_ci pa_memblockq_free(u->memblockq_sink); 121553a5a1b3Sopenharmony_ci 121653a5a1b3Sopenharmony_ci if (u->p_fw) { 121753a5a1b3Sopenharmony_ci for (i = 0, j = u->inputs; i < j; i++) { 121853a5a1b3Sopenharmony_ci if (u->p_fw[i]) 121953a5a1b3Sopenharmony_ci fftwf_destroy_plan(u->p_fw[i]); 122053a5a1b3Sopenharmony_ci } 122153a5a1b3Sopenharmony_ci fftwf_free(u->p_fw); 122253a5a1b3Sopenharmony_ci } 122353a5a1b3Sopenharmony_ci 122453a5a1b3Sopenharmony_ci if (u->p_bw) 122553a5a1b3Sopenharmony_ci fftwf_destroy_plan(u->p_bw); 122653a5a1b3Sopenharmony_ci 122753a5a1b3Sopenharmony_ci if (u->f_ir) { 122853a5a1b3Sopenharmony_ci for (i = 0, j = u->inputs * 2; i < j; i++) { 122953a5a1b3Sopenharmony_ci if (u->f_ir[i]) 123053a5a1b3Sopenharmony_ci fftwf_free(u->f_ir[i]); 123153a5a1b3Sopenharmony_ci } 123253a5a1b3Sopenharmony_ci fftwf_free(u->f_ir); 123353a5a1b3Sopenharmony_ci } 123453a5a1b3Sopenharmony_ci 123553a5a1b3Sopenharmony_ci if (u->f_out) 123653a5a1b3Sopenharmony_ci fftwf_free(u->f_out); 123753a5a1b3Sopenharmony_ci 123853a5a1b3Sopenharmony_ci if (u->f_in) 123953a5a1b3Sopenharmony_ci fftwf_free(u->f_in); 124053a5a1b3Sopenharmony_ci 124153a5a1b3Sopenharmony_ci if (u->revspace) 124253a5a1b3Sopenharmony_ci fftwf_free(u->revspace); 124353a5a1b3Sopenharmony_ci 124453a5a1b3Sopenharmony_ci if (u->outspace[0]) 124553a5a1b3Sopenharmony_ci fftwf_free(u->outspace[0]); 124653a5a1b3Sopenharmony_ci if (u->outspace[1]) 124753a5a1b3Sopenharmony_ci fftwf_free(u->outspace[1]); 124853a5a1b3Sopenharmony_ci 124953a5a1b3Sopenharmony_ci if (u->inspace) { 125053a5a1b3Sopenharmony_ci for (i = 0, j = u->inputs; i < j; i++) { 125153a5a1b3Sopenharmony_ci if (u->inspace[i]) 125253a5a1b3Sopenharmony_ci fftwf_free(u->inspace[i]); 125353a5a1b3Sopenharmony_ci } 125453a5a1b3Sopenharmony_ci fftwf_free(u->inspace); 125553a5a1b3Sopenharmony_ci } 125653a5a1b3Sopenharmony_ci 125753a5a1b3Sopenharmony_ci pa_xfree(u); 125853a5a1b3Sopenharmony_ci} 1259