153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2010 Wim Taymans <wim.taymans@gmail.com> 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci Based on module-virtual-sink.c 753a5a1b3Sopenharmony_ci module-virtual-source.c 853a5a1b3Sopenharmony_ci module-loopback.c 953a5a1b3Sopenharmony_ci 1053a5a1b3Sopenharmony_ci Copyright 2010 Intel Corporation 1153a5a1b3Sopenharmony_ci Contributor: Pierre-Louis Bossart <pierre-louis.bossart@intel.com> 1253a5a1b3Sopenharmony_ci 1353a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 1453a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 1553a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 1653a5a1b3Sopenharmony_ci or (at your option) any later version. 1753a5a1b3Sopenharmony_ci 1853a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1953a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 2053a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 2153a5a1b3Sopenharmony_ci General Public License for more details. 2253a5a1b3Sopenharmony_ci 2353a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 2453a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 2553a5a1b3Sopenharmony_ci***/ 2653a5a1b3Sopenharmony_ci 2753a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2853a5a1b3Sopenharmony_ci#include <config.h> 2953a5a1b3Sopenharmony_ci#endif 3053a5a1b3Sopenharmony_ci 3153a5a1b3Sopenharmony_ci#include <stdio.h> 3253a5a1b3Sopenharmony_ci#include <math.h> 3353a5a1b3Sopenharmony_ci 3453a5a1b3Sopenharmony_ci#include "echo-cancel.h" 3553a5a1b3Sopenharmony_ci 3653a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3753a5a1b3Sopenharmony_ci#include <pulse/timeval.h> 3853a5a1b3Sopenharmony_ci#include <pulse/rtclock.h> 3953a5a1b3Sopenharmony_ci 4053a5a1b3Sopenharmony_ci#include <pulsecore/i18n.h> 4153a5a1b3Sopenharmony_ci#include <pulsecore/atomic.h> 4253a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 4353a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h> 4453a5a1b3Sopenharmony_ci#include <pulsecore/sink.h> 4553a5a1b3Sopenharmony_ci#include <pulsecore/module.h> 4653a5a1b3Sopenharmony_ci#include <pulsecore/core-rtclock.h> 4753a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 4853a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 4953a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 5053a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h> 5153a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h> 5253a5a1b3Sopenharmony_ci#include <pulsecore/ltdl-helper.h> 5353a5a1b3Sopenharmony_ci 5453a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Wim Taymans"); 5553a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Echo Cancellation"); 5653a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 5753a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false); 5853a5a1b3Sopenharmony_ciPA_MODULE_USAGE( 5953a5a1b3Sopenharmony_ci _("source_name=<name for the source> " 6053a5a1b3Sopenharmony_ci "source_properties=<properties for the source> " 6153a5a1b3Sopenharmony_ci "source_master=<name of source to filter> " 6253a5a1b3Sopenharmony_ci "sink_name=<name for the sink> " 6353a5a1b3Sopenharmony_ci "sink_properties=<properties for the sink> " 6453a5a1b3Sopenharmony_ci "sink_master=<name of sink to filter> " 6553a5a1b3Sopenharmony_ci "adjust_time=<how often to readjust rates in s> " 6653a5a1b3Sopenharmony_ci "adjust_threshold=<how much drift to readjust after in ms> " 6753a5a1b3Sopenharmony_ci "format=<sample format> " 6853a5a1b3Sopenharmony_ci "rate=<sample rate> " 6953a5a1b3Sopenharmony_ci "channels=<number of channels> " 7053a5a1b3Sopenharmony_ci "channel_map=<channel map> " 7153a5a1b3Sopenharmony_ci "aec_method=<implementation to use> " 7253a5a1b3Sopenharmony_ci "aec_args=<parameters for the AEC engine> " 7353a5a1b3Sopenharmony_ci "save_aec=<save AEC data in /tmp> " 7453a5a1b3Sopenharmony_ci "autoloaded=<set if this module is being loaded automatically> " 7553a5a1b3Sopenharmony_ci "use_volume_sharing=<yes or no> " 7653a5a1b3Sopenharmony_ci "use_master_format=<yes or no> " 7753a5a1b3Sopenharmony_ci )); 7853a5a1b3Sopenharmony_ci 7953a5a1b3Sopenharmony_ci/* NOTE: Make sure the enum and ec_table are maintained in the correct order */ 8053a5a1b3Sopenharmony_citypedef enum { 8153a5a1b3Sopenharmony_ci PA_ECHO_CANCELLER_INVALID = -1, 8253a5a1b3Sopenharmony_ci PA_ECHO_CANCELLER_NULL, 8353a5a1b3Sopenharmony_ci#ifdef HAVE_SPEEX 8453a5a1b3Sopenharmony_ci PA_ECHO_CANCELLER_SPEEX, 8553a5a1b3Sopenharmony_ci#endif 8653a5a1b3Sopenharmony_ci#ifdef HAVE_ADRIAN_EC 8753a5a1b3Sopenharmony_ci PA_ECHO_CANCELLER_ADRIAN, 8853a5a1b3Sopenharmony_ci#endif 8953a5a1b3Sopenharmony_ci#ifdef HAVE_WEBRTC 9053a5a1b3Sopenharmony_ci PA_ECHO_CANCELLER_WEBRTC, 9153a5a1b3Sopenharmony_ci#endif 9253a5a1b3Sopenharmony_ci} pa_echo_canceller_method_t; 9353a5a1b3Sopenharmony_ci 9453a5a1b3Sopenharmony_ci#ifdef HAVE_WEBRTC 9553a5a1b3Sopenharmony_ci#define DEFAULT_ECHO_CANCELLER "webrtc" 9653a5a1b3Sopenharmony_ci#else 9753a5a1b3Sopenharmony_ci#define DEFAULT_ECHO_CANCELLER "speex" 9853a5a1b3Sopenharmony_ci#endif 9953a5a1b3Sopenharmony_ci 10053a5a1b3Sopenharmony_cistatic const pa_echo_canceller ec_table[] = { 10153a5a1b3Sopenharmony_ci { 10253a5a1b3Sopenharmony_ci /* Null, Dummy echo canceller (just copies data) */ 10353a5a1b3Sopenharmony_ci .init = pa_null_ec_init, 10453a5a1b3Sopenharmony_ci .run = pa_null_ec_run, 10553a5a1b3Sopenharmony_ci .done = pa_null_ec_done, 10653a5a1b3Sopenharmony_ci }, 10753a5a1b3Sopenharmony_ci#ifdef HAVE_SPEEX 10853a5a1b3Sopenharmony_ci { 10953a5a1b3Sopenharmony_ci /* Speex */ 11053a5a1b3Sopenharmony_ci .init = pa_speex_ec_init, 11153a5a1b3Sopenharmony_ci .run = pa_speex_ec_run, 11253a5a1b3Sopenharmony_ci .done = pa_speex_ec_done, 11353a5a1b3Sopenharmony_ci }, 11453a5a1b3Sopenharmony_ci#endif 11553a5a1b3Sopenharmony_ci#ifdef HAVE_ADRIAN_EC 11653a5a1b3Sopenharmony_ci { 11753a5a1b3Sopenharmony_ci /* Adrian Andre's NLMS implementation */ 11853a5a1b3Sopenharmony_ci .init = pa_adrian_ec_init, 11953a5a1b3Sopenharmony_ci .run = pa_adrian_ec_run, 12053a5a1b3Sopenharmony_ci .done = pa_adrian_ec_done, 12153a5a1b3Sopenharmony_ci }, 12253a5a1b3Sopenharmony_ci#endif 12353a5a1b3Sopenharmony_ci#ifdef HAVE_WEBRTC 12453a5a1b3Sopenharmony_ci { 12553a5a1b3Sopenharmony_ci /* WebRTC's audio processing engine */ 12653a5a1b3Sopenharmony_ci .init = pa_webrtc_ec_init, 12753a5a1b3Sopenharmony_ci .play = pa_webrtc_ec_play, 12853a5a1b3Sopenharmony_ci .record = pa_webrtc_ec_record, 12953a5a1b3Sopenharmony_ci .set_drift = pa_webrtc_ec_set_drift, 13053a5a1b3Sopenharmony_ci .run = pa_webrtc_ec_run, 13153a5a1b3Sopenharmony_ci .done = pa_webrtc_ec_done, 13253a5a1b3Sopenharmony_ci }, 13353a5a1b3Sopenharmony_ci#endif 13453a5a1b3Sopenharmony_ci}; 13553a5a1b3Sopenharmony_ci 13653a5a1b3Sopenharmony_ci#define DEFAULT_RATE 32000 13753a5a1b3Sopenharmony_ci#define DEFAULT_CHANNELS 1 13853a5a1b3Sopenharmony_ci#define DEFAULT_ADJUST_TIME_USEC (1*PA_USEC_PER_SEC) 13953a5a1b3Sopenharmony_ci#define DEFAULT_ADJUST_TOLERANCE (5*PA_USEC_PER_MSEC) 14053a5a1b3Sopenharmony_ci#define DEFAULT_SAVE_AEC false 14153a5a1b3Sopenharmony_ci#define DEFAULT_AUTOLOADED false 14253a5a1b3Sopenharmony_ci#define DEFAULT_USE_MASTER_FORMAT false 14353a5a1b3Sopenharmony_ci 14453a5a1b3Sopenharmony_ci#define MEMBLOCKQ_MAXLENGTH (16*1024*1024) 14553a5a1b3Sopenharmony_ci 14653a5a1b3Sopenharmony_ci#define MAX_LATENCY_BLOCKS 10 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci/* Can only be used in main context */ 14953a5a1b3Sopenharmony_ci#define IS_ACTIVE(u) (((u)->source->state == PA_SOURCE_RUNNING) && \ 15053a5a1b3Sopenharmony_ci ((u)->sink->state == PA_SINK_RUNNING)) 15153a5a1b3Sopenharmony_ci 15253a5a1b3Sopenharmony_ci/* This module creates a new (virtual) source and sink. 15353a5a1b3Sopenharmony_ci * 15453a5a1b3Sopenharmony_ci * The data sent to the new sink is kept in a memblockq before being 15553a5a1b3Sopenharmony_ci * forwarded to the real sink_master. 15653a5a1b3Sopenharmony_ci * 15753a5a1b3Sopenharmony_ci * Data read from source_master is matched against the saved sink data and 15853a5a1b3Sopenharmony_ci * echo canceled data is then pushed onto the new source. 15953a5a1b3Sopenharmony_ci * 16053a5a1b3Sopenharmony_ci * Both source and sink masters have their own threads to push/pull data 16153a5a1b3Sopenharmony_ci * respectively. We however perform all our actions in the source IO thread. 16253a5a1b3Sopenharmony_ci * To do this we send all played samples to the source IO thread where they 16353a5a1b3Sopenharmony_ci * are then pushed into the memblockq. 16453a5a1b3Sopenharmony_ci * 16553a5a1b3Sopenharmony_ci * Alignment is performed in two steps: 16653a5a1b3Sopenharmony_ci * 16753a5a1b3Sopenharmony_ci * 1) when something happens that requires quick adjustment of the alignment of 16853a5a1b3Sopenharmony_ci * capture and playback samples, we perform a resync. This adjusts the 16953a5a1b3Sopenharmony_ci * position in the playback memblock to the requested sample. Quick 17053a5a1b3Sopenharmony_ci * adjustments include moving the playback samples before the capture 17153a5a1b3Sopenharmony_ci * samples (because else the echo canceller does not work) or when the 17253a5a1b3Sopenharmony_ci * playback pointer drifts too far away. 17353a5a1b3Sopenharmony_ci * 17453a5a1b3Sopenharmony_ci * 2) periodically check the difference between capture and playback. We use a 17553a5a1b3Sopenharmony_ci * low and high watermark for adjusting the alignment. Playback should always 17653a5a1b3Sopenharmony_ci * be before capture and the difference should not be bigger than one frame 17753a5a1b3Sopenharmony_ci * size. We would ideally like to resample the sink_input but most driver 17853a5a1b3Sopenharmony_ci * don't give enough accuracy to be able to do that right now. 17953a5a1b3Sopenharmony_ci */ 18053a5a1b3Sopenharmony_ci 18153a5a1b3Sopenharmony_cistruct userdata; 18253a5a1b3Sopenharmony_ci 18353a5a1b3Sopenharmony_cistruct pa_echo_canceller_msg { 18453a5a1b3Sopenharmony_ci pa_msgobject parent; 18553a5a1b3Sopenharmony_ci bool dead; 18653a5a1b3Sopenharmony_ci struct userdata *userdata; 18753a5a1b3Sopenharmony_ci}; 18853a5a1b3Sopenharmony_ci 18953a5a1b3Sopenharmony_ciPA_DEFINE_PRIVATE_CLASS(pa_echo_canceller_msg, pa_msgobject); 19053a5a1b3Sopenharmony_ci#define PA_ECHO_CANCELLER_MSG(o) (pa_echo_canceller_msg_cast(o)) 19153a5a1b3Sopenharmony_ci 19253a5a1b3Sopenharmony_cistruct snapshot { 19353a5a1b3Sopenharmony_ci pa_usec_t sink_now; 19453a5a1b3Sopenharmony_ci pa_usec_t sink_latency; 19553a5a1b3Sopenharmony_ci size_t sink_delay; 19653a5a1b3Sopenharmony_ci int64_t send_counter; 19753a5a1b3Sopenharmony_ci 19853a5a1b3Sopenharmony_ci pa_usec_t source_now; 19953a5a1b3Sopenharmony_ci pa_usec_t source_latency; 20053a5a1b3Sopenharmony_ci size_t source_delay; 20153a5a1b3Sopenharmony_ci int64_t recv_counter; 20253a5a1b3Sopenharmony_ci size_t rlen; 20353a5a1b3Sopenharmony_ci size_t plen; 20453a5a1b3Sopenharmony_ci}; 20553a5a1b3Sopenharmony_ci 20653a5a1b3Sopenharmony_cistruct userdata { 20753a5a1b3Sopenharmony_ci pa_core *core; 20853a5a1b3Sopenharmony_ci pa_module *module; 20953a5a1b3Sopenharmony_ci 21053a5a1b3Sopenharmony_ci bool dead; 21153a5a1b3Sopenharmony_ci bool save_aec; 21253a5a1b3Sopenharmony_ci 21353a5a1b3Sopenharmony_ci pa_echo_canceller *ec; 21453a5a1b3Sopenharmony_ci uint32_t source_output_blocksize; 21553a5a1b3Sopenharmony_ci uint32_t source_blocksize; 21653a5a1b3Sopenharmony_ci uint32_t sink_blocksize; 21753a5a1b3Sopenharmony_ci 21853a5a1b3Sopenharmony_ci bool need_realign; 21953a5a1b3Sopenharmony_ci 22053a5a1b3Sopenharmony_ci /* to wakeup the source I/O thread */ 22153a5a1b3Sopenharmony_ci pa_asyncmsgq *asyncmsgq; 22253a5a1b3Sopenharmony_ci pa_rtpoll_item *rtpoll_item_read, *rtpoll_item_write; 22353a5a1b3Sopenharmony_ci 22453a5a1b3Sopenharmony_ci pa_source *source; 22553a5a1b3Sopenharmony_ci bool source_auto_desc; 22653a5a1b3Sopenharmony_ci pa_source_output *source_output; 22753a5a1b3Sopenharmony_ci pa_memblockq *source_memblockq; /* echo canceller needs fixed sized chunks */ 22853a5a1b3Sopenharmony_ci size_t source_skip; 22953a5a1b3Sopenharmony_ci 23053a5a1b3Sopenharmony_ci pa_sink *sink; 23153a5a1b3Sopenharmony_ci bool sink_auto_desc; 23253a5a1b3Sopenharmony_ci pa_sink_input *sink_input; 23353a5a1b3Sopenharmony_ci pa_memblockq *sink_memblockq; 23453a5a1b3Sopenharmony_ci int64_t send_counter; /* updated in sink IO thread */ 23553a5a1b3Sopenharmony_ci int64_t recv_counter; 23653a5a1b3Sopenharmony_ci size_t sink_skip; 23753a5a1b3Sopenharmony_ci 23853a5a1b3Sopenharmony_ci /* Bytes left over from previous iteration */ 23953a5a1b3Sopenharmony_ci size_t sink_rem; 24053a5a1b3Sopenharmony_ci size_t source_rem; 24153a5a1b3Sopenharmony_ci 24253a5a1b3Sopenharmony_ci pa_atomic_t request_resync; 24353a5a1b3Sopenharmony_ci 24453a5a1b3Sopenharmony_ci pa_time_event *time_event; 24553a5a1b3Sopenharmony_ci pa_usec_t adjust_time; 24653a5a1b3Sopenharmony_ci int adjust_threshold; 24753a5a1b3Sopenharmony_ci 24853a5a1b3Sopenharmony_ci FILE *captured_file; 24953a5a1b3Sopenharmony_ci FILE *played_file; 25053a5a1b3Sopenharmony_ci FILE *canceled_file; 25153a5a1b3Sopenharmony_ci FILE *drift_file; 25253a5a1b3Sopenharmony_ci 25353a5a1b3Sopenharmony_ci bool use_volume_sharing; 25453a5a1b3Sopenharmony_ci 25553a5a1b3Sopenharmony_ci struct { 25653a5a1b3Sopenharmony_ci pa_cvolume current_volume; 25753a5a1b3Sopenharmony_ci } thread_info; 25853a5a1b3Sopenharmony_ci}; 25953a5a1b3Sopenharmony_ci 26053a5a1b3Sopenharmony_cistatic void source_output_snapshot_within_thread(struct userdata *u, struct snapshot *snapshot); 26153a5a1b3Sopenharmony_ci 26253a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 26353a5a1b3Sopenharmony_ci "source_name", 26453a5a1b3Sopenharmony_ci "source_properties", 26553a5a1b3Sopenharmony_ci "source_master", 26653a5a1b3Sopenharmony_ci "sink_name", 26753a5a1b3Sopenharmony_ci "sink_properties", 26853a5a1b3Sopenharmony_ci "sink_master", 26953a5a1b3Sopenharmony_ci "adjust_time", 27053a5a1b3Sopenharmony_ci "adjust_threshold", 27153a5a1b3Sopenharmony_ci "format", 27253a5a1b3Sopenharmony_ci "rate", 27353a5a1b3Sopenharmony_ci "channels", 27453a5a1b3Sopenharmony_ci "channel_map", 27553a5a1b3Sopenharmony_ci "aec_method", 27653a5a1b3Sopenharmony_ci "aec_args", 27753a5a1b3Sopenharmony_ci "save_aec", 27853a5a1b3Sopenharmony_ci "autoloaded", 27953a5a1b3Sopenharmony_ci "use_volume_sharing", 28053a5a1b3Sopenharmony_ci "use_master_format", 28153a5a1b3Sopenharmony_ci NULL 28253a5a1b3Sopenharmony_ci}; 28353a5a1b3Sopenharmony_ci 28453a5a1b3Sopenharmony_cienum { 28553a5a1b3Sopenharmony_ci SOURCE_OUTPUT_MESSAGE_POST = PA_SOURCE_OUTPUT_MESSAGE_MAX, 28653a5a1b3Sopenharmony_ci SOURCE_OUTPUT_MESSAGE_REWIND, 28753a5a1b3Sopenharmony_ci SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, 28853a5a1b3Sopenharmony_ci SOURCE_OUTPUT_MESSAGE_APPLY_DIFF_TIME 28953a5a1b3Sopenharmony_ci}; 29053a5a1b3Sopenharmony_ci 29153a5a1b3Sopenharmony_cienum { 29253a5a1b3Sopenharmony_ci SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT 29353a5a1b3Sopenharmony_ci}; 29453a5a1b3Sopenharmony_ci 29553a5a1b3Sopenharmony_cienum { 29653a5a1b3Sopenharmony_ci ECHO_CANCELLER_MESSAGE_SET_VOLUME, 29753a5a1b3Sopenharmony_ci}; 29853a5a1b3Sopenharmony_ci 29953a5a1b3Sopenharmony_cistatic int64_t calc_diff(struct userdata *u, struct snapshot *snapshot) { 30053a5a1b3Sopenharmony_ci int64_t diff_time, buffer_latency; 30153a5a1b3Sopenharmony_ci pa_usec_t plen, rlen, source_delay, sink_delay, recv_counter, send_counter; 30253a5a1b3Sopenharmony_ci 30353a5a1b3Sopenharmony_ci /* get latency difference between playback and record */ 30453a5a1b3Sopenharmony_ci plen = pa_bytes_to_usec(snapshot->plen, &u->sink_input->sample_spec); 30553a5a1b3Sopenharmony_ci rlen = pa_bytes_to_usec(snapshot->rlen, &u->source_output->sample_spec); 30653a5a1b3Sopenharmony_ci if (plen > rlen) 30753a5a1b3Sopenharmony_ci buffer_latency = plen - rlen; 30853a5a1b3Sopenharmony_ci else 30953a5a1b3Sopenharmony_ci buffer_latency = 0; 31053a5a1b3Sopenharmony_ci 31153a5a1b3Sopenharmony_ci source_delay = pa_bytes_to_usec(snapshot->source_delay, &u->source_output->sample_spec); 31253a5a1b3Sopenharmony_ci sink_delay = pa_bytes_to_usec(snapshot->sink_delay, &u->sink_input->sample_spec); 31353a5a1b3Sopenharmony_ci buffer_latency += source_delay + sink_delay; 31453a5a1b3Sopenharmony_ci 31553a5a1b3Sopenharmony_ci /* add the latency difference due to samples not yet transferred */ 31653a5a1b3Sopenharmony_ci send_counter = pa_bytes_to_usec(snapshot->send_counter, &u->sink->sample_spec); 31753a5a1b3Sopenharmony_ci recv_counter = pa_bytes_to_usec(snapshot->recv_counter, &u->sink->sample_spec); 31853a5a1b3Sopenharmony_ci if (recv_counter <= send_counter) 31953a5a1b3Sopenharmony_ci buffer_latency += (int64_t) (send_counter - recv_counter); 32053a5a1b3Sopenharmony_ci else 32153a5a1b3Sopenharmony_ci buffer_latency = PA_CLIP_SUB(buffer_latency, (int64_t) (recv_counter - send_counter)); 32253a5a1b3Sopenharmony_ci 32353a5a1b3Sopenharmony_ci /* capture and playback are perfectly aligned when diff_time is 0 */ 32453a5a1b3Sopenharmony_ci diff_time = (snapshot->sink_now + snapshot->sink_latency - buffer_latency) - 32553a5a1b3Sopenharmony_ci (snapshot->source_now - snapshot->source_latency); 32653a5a1b3Sopenharmony_ci 32753a5a1b3Sopenharmony_ci pa_log_debug("Diff %lld (%lld - %lld + %lld) %lld %lld %lld %lld", (long long) diff_time, 32853a5a1b3Sopenharmony_ci (long long) snapshot->sink_latency, 32953a5a1b3Sopenharmony_ci (long long) buffer_latency, (long long) snapshot->source_latency, 33053a5a1b3Sopenharmony_ci (long long) source_delay, (long long) sink_delay, 33153a5a1b3Sopenharmony_ci (long long) (send_counter - recv_counter), 33253a5a1b3Sopenharmony_ci (long long) (snapshot->sink_now - snapshot->source_now)); 33353a5a1b3Sopenharmony_ci 33453a5a1b3Sopenharmony_ci return diff_time; 33553a5a1b3Sopenharmony_ci} 33653a5a1b3Sopenharmony_ci 33753a5a1b3Sopenharmony_ci/* Called from main context */ 33853a5a1b3Sopenharmony_cistatic void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) { 33953a5a1b3Sopenharmony_ci struct userdata *u = userdata; 34053a5a1b3Sopenharmony_ci uint32_t old_rate, base_rate, new_rate; 34153a5a1b3Sopenharmony_ci int64_t diff_time; 34253a5a1b3Sopenharmony_ci /*size_t fs*/ 34353a5a1b3Sopenharmony_ci struct snapshot latency_snapshot; 34453a5a1b3Sopenharmony_ci 34553a5a1b3Sopenharmony_ci pa_assert(u); 34653a5a1b3Sopenharmony_ci pa_assert(a); 34753a5a1b3Sopenharmony_ci pa_assert(u->time_event == e); 34853a5a1b3Sopenharmony_ci pa_assert_ctl_context(); 34953a5a1b3Sopenharmony_ci 35053a5a1b3Sopenharmony_ci if (!IS_ACTIVE(u)) 35153a5a1b3Sopenharmony_ci return; 35253a5a1b3Sopenharmony_ci 35353a5a1b3Sopenharmony_ci /* update our snapshots */ 35453a5a1b3Sopenharmony_ci pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, &latency_snapshot, 0, NULL); 35553a5a1b3Sopenharmony_ci pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, &latency_snapshot, 0, NULL); 35653a5a1b3Sopenharmony_ci 35753a5a1b3Sopenharmony_ci /* calculate drift between capture and playback */ 35853a5a1b3Sopenharmony_ci diff_time = calc_diff(u, &latency_snapshot); 35953a5a1b3Sopenharmony_ci 36053a5a1b3Sopenharmony_ci /*fs = pa_frame_size(&u->source_output->sample_spec);*/ 36153a5a1b3Sopenharmony_ci old_rate = u->sink_input->sample_spec.rate; 36253a5a1b3Sopenharmony_ci base_rate = u->source_output->sample_spec.rate; 36353a5a1b3Sopenharmony_ci 36453a5a1b3Sopenharmony_ci if (diff_time < 0) { 36553a5a1b3Sopenharmony_ci /* recording before playback, we need to adjust quickly. The echo 36653a5a1b3Sopenharmony_ci * canceller does not work in this case. */ 36753a5a1b3Sopenharmony_ci pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_APPLY_DIFF_TIME, 36853a5a1b3Sopenharmony_ci NULL, diff_time, NULL, NULL); 36953a5a1b3Sopenharmony_ci /*new_rate = base_rate - ((pa_usec_to_bytes(-diff_time, &u->source_output->sample_spec) / fs) * PA_USEC_PER_SEC) / u->adjust_time;*/ 37053a5a1b3Sopenharmony_ci new_rate = base_rate; 37153a5a1b3Sopenharmony_ci } 37253a5a1b3Sopenharmony_ci else { 37353a5a1b3Sopenharmony_ci if (diff_time > u->adjust_threshold) { 37453a5a1b3Sopenharmony_ci /* diff too big, quickly adjust */ 37553a5a1b3Sopenharmony_ci pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_APPLY_DIFF_TIME, 37653a5a1b3Sopenharmony_ci NULL, diff_time, NULL, NULL); 37753a5a1b3Sopenharmony_ci } 37853a5a1b3Sopenharmony_ci 37953a5a1b3Sopenharmony_ci /* recording behind playback, we need to slowly adjust the rate to match */ 38053a5a1b3Sopenharmony_ci /*new_rate = base_rate + ((pa_usec_to_bytes(diff_time, &u->source_output->sample_spec) / fs) * PA_USEC_PER_SEC) / u->adjust_time;*/ 38153a5a1b3Sopenharmony_ci 38253a5a1b3Sopenharmony_ci /* assume equal samplerates for now */ 38353a5a1b3Sopenharmony_ci new_rate = base_rate; 38453a5a1b3Sopenharmony_ci } 38553a5a1b3Sopenharmony_ci 38653a5a1b3Sopenharmony_ci /* make sure we don't make too big adjustments because that sounds horrible */ 38753a5a1b3Sopenharmony_ci if (new_rate > base_rate * 1.1 || new_rate < base_rate * 0.9) 38853a5a1b3Sopenharmony_ci new_rate = base_rate; 38953a5a1b3Sopenharmony_ci 39053a5a1b3Sopenharmony_ci if (new_rate != old_rate) { 39153a5a1b3Sopenharmony_ci pa_log_info("Old rate %lu Hz, new rate %lu Hz", (unsigned long) old_rate, (unsigned long) new_rate); 39253a5a1b3Sopenharmony_ci 39353a5a1b3Sopenharmony_ci pa_sink_input_set_rate(u->sink_input, new_rate); 39453a5a1b3Sopenharmony_ci } 39553a5a1b3Sopenharmony_ci 39653a5a1b3Sopenharmony_ci pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time); 39753a5a1b3Sopenharmony_ci} 39853a5a1b3Sopenharmony_ci 39953a5a1b3Sopenharmony_ci/* Called from source I/O thread context */ 40053a5a1b3Sopenharmony_cistatic int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { 40153a5a1b3Sopenharmony_ci struct userdata *u = PA_SOURCE(o)->userdata; 40253a5a1b3Sopenharmony_ci 40353a5a1b3Sopenharmony_ci switch (code) { 40453a5a1b3Sopenharmony_ci 40553a5a1b3Sopenharmony_ci case PA_SOURCE_MESSAGE_GET_LATENCY: 40653a5a1b3Sopenharmony_ci 40753a5a1b3Sopenharmony_ci /* The source is _put() before the source output is, so let's 40853a5a1b3Sopenharmony_ci * make sure we don't access it in that time. Also, the 40953a5a1b3Sopenharmony_ci * source output is first shut down, the source second. */ 41053a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) || 41153a5a1b3Sopenharmony_ci !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) { 41253a5a1b3Sopenharmony_ci *((int64_t*) data) = 0; 41353a5a1b3Sopenharmony_ci return 0; 41453a5a1b3Sopenharmony_ci } 41553a5a1b3Sopenharmony_ci 41653a5a1b3Sopenharmony_ci *((int64_t*) data) = 41753a5a1b3Sopenharmony_ci 41853a5a1b3Sopenharmony_ci /* Get the latency of the master source */ 41953a5a1b3Sopenharmony_ci pa_source_get_latency_within_thread(u->source_output->source, true) + 42053a5a1b3Sopenharmony_ci /* Add the latency internal to our source output on top */ 42153a5a1b3Sopenharmony_ci pa_bytes_to_usec(pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq), &u->source_output->source->sample_spec) + 42253a5a1b3Sopenharmony_ci /* and the buffering we do on the source */ 42353a5a1b3Sopenharmony_ci pa_bytes_to_usec(u->source_output_blocksize, &u->source_output->source->sample_spec); 42453a5a1b3Sopenharmony_ci 42553a5a1b3Sopenharmony_ci /* Add resampler delay */ 42653a5a1b3Sopenharmony_ci *((int64_t*) data) += pa_resampler_get_delay_usec(u->source_output->thread_info.resampler); 42753a5a1b3Sopenharmony_ci 42853a5a1b3Sopenharmony_ci return 0; 42953a5a1b3Sopenharmony_ci 43053a5a1b3Sopenharmony_ci case PA_SOURCE_MESSAGE_SET_VOLUME_SYNCED: 43153a5a1b3Sopenharmony_ci u->thread_info.current_volume = u->source->reference_volume; 43253a5a1b3Sopenharmony_ci break; 43353a5a1b3Sopenharmony_ci } 43453a5a1b3Sopenharmony_ci 43553a5a1b3Sopenharmony_ci return pa_source_process_msg(o, code, data, offset, chunk); 43653a5a1b3Sopenharmony_ci} 43753a5a1b3Sopenharmony_ci 43853a5a1b3Sopenharmony_ci/* Called from sink I/O thread context */ 43953a5a1b3Sopenharmony_cistatic int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { 44053a5a1b3Sopenharmony_ci struct userdata *u = PA_SINK(o)->userdata; 44153a5a1b3Sopenharmony_ci 44253a5a1b3Sopenharmony_ci switch (code) { 44353a5a1b3Sopenharmony_ci 44453a5a1b3Sopenharmony_ci case PA_SINK_MESSAGE_GET_LATENCY: 44553a5a1b3Sopenharmony_ci 44653a5a1b3Sopenharmony_ci /* The sink is _put() before the sink input is, so let's 44753a5a1b3Sopenharmony_ci * make sure we don't access it in that time. Also, the 44853a5a1b3Sopenharmony_ci * sink input is first shut down, the sink second. */ 44953a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || 45053a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) { 45153a5a1b3Sopenharmony_ci *((int64_t*) data) = 0; 45253a5a1b3Sopenharmony_ci return 0; 45353a5a1b3Sopenharmony_ci } 45453a5a1b3Sopenharmony_ci 45553a5a1b3Sopenharmony_ci *((int64_t*) data) = 45653a5a1b3Sopenharmony_ci 45753a5a1b3Sopenharmony_ci /* Get the latency of the master sink */ 45853a5a1b3Sopenharmony_ci pa_sink_get_latency_within_thread(u->sink_input->sink, true) + 45953a5a1b3Sopenharmony_ci 46053a5a1b3Sopenharmony_ci /* Add the latency internal to our sink input on top */ 46153a5a1b3Sopenharmony_ci pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec); 46253a5a1b3Sopenharmony_ci 46353a5a1b3Sopenharmony_ci /* Add resampler delay */ 46453a5a1b3Sopenharmony_ci *((int64_t*) data) += pa_resampler_get_delay_usec(u->sink_input->thread_info.resampler); 46553a5a1b3Sopenharmony_ci 46653a5a1b3Sopenharmony_ci return 0; 46753a5a1b3Sopenharmony_ci } 46853a5a1b3Sopenharmony_ci 46953a5a1b3Sopenharmony_ci return pa_sink_process_msg(o, code, data, offset, chunk); 47053a5a1b3Sopenharmony_ci} 47153a5a1b3Sopenharmony_ci 47253a5a1b3Sopenharmony_ci/* Called from main context */ 47353a5a1b3Sopenharmony_cistatic int source_set_state_in_main_thread_cb(pa_source *s, pa_source_state_t state, pa_suspend_cause_t suspend_cause) { 47453a5a1b3Sopenharmony_ci struct userdata *u; 47553a5a1b3Sopenharmony_ci 47653a5a1b3Sopenharmony_ci pa_source_assert_ref(s); 47753a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 47853a5a1b3Sopenharmony_ci 47953a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(state) || 48053a5a1b3Sopenharmony_ci !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->state)) 48153a5a1b3Sopenharmony_ci return 0; 48253a5a1b3Sopenharmony_ci 48353a5a1b3Sopenharmony_ci if (state == PA_SOURCE_RUNNING) { 48453a5a1b3Sopenharmony_ci /* restart timer when both sink and source are active */ 48553a5a1b3Sopenharmony_ci if ((u->sink->state == PA_SINK_RUNNING) && u->adjust_time) 48653a5a1b3Sopenharmony_ci pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time); 48753a5a1b3Sopenharmony_ci 48853a5a1b3Sopenharmony_ci pa_atomic_store(&u->request_resync, 1); 48953a5a1b3Sopenharmony_ci pa_source_output_cork(u->source_output, false); 49053a5a1b3Sopenharmony_ci } else if (state == PA_SOURCE_SUSPENDED) { 49153a5a1b3Sopenharmony_ci pa_source_output_cork(u->source_output, true); 49253a5a1b3Sopenharmony_ci } 49353a5a1b3Sopenharmony_ci 49453a5a1b3Sopenharmony_ci return 0; 49553a5a1b3Sopenharmony_ci} 49653a5a1b3Sopenharmony_ci 49753a5a1b3Sopenharmony_ci/* Called from main context */ 49853a5a1b3Sopenharmony_cistatic int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t suspend_cause) { 49953a5a1b3Sopenharmony_ci struct userdata *u; 50053a5a1b3Sopenharmony_ci 50153a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 50253a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 50353a5a1b3Sopenharmony_ci 50453a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(state) || 50553a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->state)) 50653a5a1b3Sopenharmony_ci return 0; 50753a5a1b3Sopenharmony_ci 50853a5a1b3Sopenharmony_ci if (state == PA_SINK_RUNNING) { 50953a5a1b3Sopenharmony_ci /* restart timer when both sink and source are active */ 51053a5a1b3Sopenharmony_ci if ((u->source->state == PA_SOURCE_RUNNING) && u->adjust_time) 51153a5a1b3Sopenharmony_ci pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time); 51253a5a1b3Sopenharmony_ci 51353a5a1b3Sopenharmony_ci pa_atomic_store(&u->request_resync, 1); 51453a5a1b3Sopenharmony_ci pa_sink_input_cork(u->sink_input, false); 51553a5a1b3Sopenharmony_ci } else if (state == PA_SINK_SUSPENDED) { 51653a5a1b3Sopenharmony_ci pa_sink_input_cork(u->sink_input, true); 51753a5a1b3Sopenharmony_ci } 51853a5a1b3Sopenharmony_ci 51953a5a1b3Sopenharmony_ci return 0; 52053a5a1b3Sopenharmony_ci} 52153a5a1b3Sopenharmony_ci 52253a5a1b3Sopenharmony_ci/* Called from the IO thread. */ 52353a5a1b3Sopenharmony_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) { 52453a5a1b3Sopenharmony_ci struct userdata *u; 52553a5a1b3Sopenharmony_ci 52653a5a1b3Sopenharmony_ci pa_assert(s); 52753a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 52853a5a1b3Sopenharmony_ci 52953a5a1b3Sopenharmony_ci /* When set to running or idle for the first time, request a rewind 53053a5a1b3Sopenharmony_ci * of the master sink to make sure we are heard immediately */ 53153a5a1b3Sopenharmony_ci if (PA_SINK_IS_OPENED(new_state) && s->thread_info.state == PA_SINK_INIT) { 53253a5a1b3Sopenharmony_ci pa_log_debug("Requesting rewind due to state change."); 53353a5a1b3Sopenharmony_ci pa_sink_input_request_rewind(u->sink_input, 0, false, true, true); 53453a5a1b3Sopenharmony_ci } 53553a5a1b3Sopenharmony_ci 53653a5a1b3Sopenharmony_ci return 0; 53753a5a1b3Sopenharmony_ci} 53853a5a1b3Sopenharmony_ci 53953a5a1b3Sopenharmony_ci/* Called from source I/O thread context */ 54053a5a1b3Sopenharmony_cistatic void source_update_requested_latency_cb(pa_source *s) { 54153a5a1b3Sopenharmony_ci struct userdata *u; 54253a5a1b3Sopenharmony_ci pa_usec_t latency; 54353a5a1b3Sopenharmony_ci 54453a5a1b3Sopenharmony_ci pa_source_assert_ref(s); 54553a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 54653a5a1b3Sopenharmony_ci 54753a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) || 54853a5a1b3Sopenharmony_ci !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) 54953a5a1b3Sopenharmony_ci return; 55053a5a1b3Sopenharmony_ci 55153a5a1b3Sopenharmony_ci pa_log_debug("Source update requested latency"); 55253a5a1b3Sopenharmony_ci 55353a5a1b3Sopenharmony_ci /* Cap the maximum latency so we don't have to process too large chunks */ 55453a5a1b3Sopenharmony_ci latency = PA_MIN(pa_source_get_requested_latency_within_thread(s), 55553a5a1b3Sopenharmony_ci pa_bytes_to_usec(u->source_blocksize, &s->sample_spec) * MAX_LATENCY_BLOCKS); 55653a5a1b3Sopenharmony_ci 55753a5a1b3Sopenharmony_ci pa_source_output_set_requested_latency_within_thread(u->source_output, latency); 55853a5a1b3Sopenharmony_ci} 55953a5a1b3Sopenharmony_ci 56053a5a1b3Sopenharmony_ci/* Called from sink I/O thread context */ 56153a5a1b3Sopenharmony_cistatic void sink_update_requested_latency_cb(pa_sink *s) { 56253a5a1b3Sopenharmony_ci struct userdata *u; 56353a5a1b3Sopenharmony_ci pa_usec_t latency; 56453a5a1b3Sopenharmony_ci 56553a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 56653a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 56753a5a1b3Sopenharmony_ci 56853a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || 56953a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) 57053a5a1b3Sopenharmony_ci return; 57153a5a1b3Sopenharmony_ci 57253a5a1b3Sopenharmony_ci pa_log_debug("Sink update requested latency"); 57353a5a1b3Sopenharmony_ci 57453a5a1b3Sopenharmony_ci /* Cap the maximum latency so we don't have to process too large chunks */ 57553a5a1b3Sopenharmony_ci latency = PA_MIN(pa_sink_get_requested_latency_within_thread(s), 57653a5a1b3Sopenharmony_ci pa_bytes_to_usec(u->sink_blocksize, &s->sample_spec) * MAX_LATENCY_BLOCKS); 57753a5a1b3Sopenharmony_ci 57853a5a1b3Sopenharmony_ci pa_sink_input_set_requested_latency_within_thread(u->sink_input, latency); 57953a5a1b3Sopenharmony_ci} 58053a5a1b3Sopenharmony_ci 58153a5a1b3Sopenharmony_ci/* Called from sink I/O thread context */ 58253a5a1b3Sopenharmony_cistatic void sink_request_rewind_cb(pa_sink *s) { 58353a5a1b3Sopenharmony_ci struct userdata *u; 58453a5a1b3Sopenharmony_ci 58553a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 58653a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 58753a5a1b3Sopenharmony_ci 58853a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || 58953a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) 59053a5a1b3Sopenharmony_ci return; 59153a5a1b3Sopenharmony_ci 59253a5a1b3Sopenharmony_ci pa_log_debug("Sink request rewind %lld", (long long) s->thread_info.rewind_nbytes); 59353a5a1b3Sopenharmony_ci 59453a5a1b3Sopenharmony_ci /* Just hand this one over to the master sink */ 59553a5a1b3Sopenharmony_ci pa_sink_input_request_rewind(u->sink_input, 59653a5a1b3Sopenharmony_ci s->thread_info.rewind_nbytes, true, false, false); 59753a5a1b3Sopenharmony_ci} 59853a5a1b3Sopenharmony_ci 59953a5a1b3Sopenharmony_ci/* Called from main context */ 60053a5a1b3Sopenharmony_cistatic void source_set_volume_cb(pa_source *s) { 60153a5a1b3Sopenharmony_ci struct userdata *u; 60253a5a1b3Sopenharmony_ci 60353a5a1b3Sopenharmony_ci pa_source_assert_ref(s); 60453a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 60553a5a1b3Sopenharmony_ci 60653a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(s->state) || 60753a5a1b3Sopenharmony_ci !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->state)) 60853a5a1b3Sopenharmony_ci return; 60953a5a1b3Sopenharmony_ci 61053a5a1b3Sopenharmony_ci pa_source_output_set_volume(u->source_output, &s->real_volume, s->save_volume, true); 61153a5a1b3Sopenharmony_ci} 61253a5a1b3Sopenharmony_ci 61353a5a1b3Sopenharmony_ci/* Called from main context */ 61453a5a1b3Sopenharmony_cistatic void sink_set_volume_cb(pa_sink *s) { 61553a5a1b3Sopenharmony_ci struct userdata *u; 61653a5a1b3Sopenharmony_ci 61753a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 61853a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 61953a5a1b3Sopenharmony_ci 62053a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(s->state) || 62153a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->state)) 62253a5a1b3Sopenharmony_ci return; 62353a5a1b3Sopenharmony_ci 62453a5a1b3Sopenharmony_ci pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, true); 62553a5a1b3Sopenharmony_ci} 62653a5a1b3Sopenharmony_ci 62753a5a1b3Sopenharmony_ci/* Called from main context. */ 62853a5a1b3Sopenharmony_cistatic void source_get_volume_cb(pa_source *s) { 62953a5a1b3Sopenharmony_ci struct userdata *u; 63053a5a1b3Sopenharmony_ci pa_cvolume v; 63153a5a1b3Sopenharmony_ci 63253a5a1b3Sopenharmony_ci pa_source_assert_ref(s); 63353a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 63453a5a1b3Sopenharmony_ci 63553a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(s->state) || 63653a5a1b3Sopenharmony_ci !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->state)) 63753a5a1b3Sopenharmony_ci return; 63853a5a1b3Sopenharmony_ci 63953a5a1b3Sopenharmony_ci pa_source_output_get_volume(u->source_output, &v, true); 64053a5a1b3Sopenharmony_ci 64153a5a1b3Sopenharmony_ci if (pa_cvolume_equal(&s->real_volume, &v)) 64253a5a1b3Sopenharmony_ci /* no change */ 64353a5a1b3Sopenharmony_ci return; 64453a5a1b3Sopenharmony_ci 64553a5a1b3Sopenharmony_ci s->real_volume = v; 64653a5a1b3Sopenharmony_ci pa_source_set_soft_volume(s, NULL); 64753a5a1b3Sopenharmony_ci} 64853a5a1b3Sopenharmony_ci 64953a5a1b3Sopenharmony_ci/* Called from main context */ 65053a5a1b3Sopenharmony_cistatic void source_set_mute_cb(pa_source *s) { 65153a5a1b3Sopenharmony_ci struct userdata *u; 65253a5a1b3Sopenharmony_ci 65353a5a1b3Sopenharmony_ci pa_source_assert_ref(s); 65453a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 65553a5a1b3Sopenharmony_ci 65653a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(s->state) || 65753a5a1b3Sopenharmony_ci !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->state)) 65853a5a1b3Sopenharmony_ci return; 65953a5a1b3Sopenharmony_ci 66053a5a1b3Sopenharmony_ci pa_source_output_set_mute(u->source_output, s->muted, s->save_muted); 66153a5a1b3Sopenharmony_ci} 66253a5a1b3Sopenharmony_ci 66353a5a1b3Sopenharmony_ci/* Called from main context */ 66453a5a1b3Sopenharmony_cistatic void sink_set_mute_cb(pa_sink *s) { 66553a5a1b3Sopenharmony_ci struct userdata *u; 66653a5a1b3Sopenharmony_ci 66753a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 66853a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 66953a5a1b3Sopenharmony_ci 67053a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(s->state) || 67153a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->state)) 67253a5a1b3Sopenharmony_ci return; 67353a5a1b3Sopenharmony_ci 67453a5a1b3Sopenharmony_ci pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted); 67553a5a1b3Sopenharmony_ci} 67653a5a1b3Sopenharmony_ci 67753a5a1b3Sopenharmony_ci/* Called from source I/O thread context. */ 67853a5a1b3Sopenharmony_cistatic void apply_diff_time(struct userdata *u, int64_t diff_time) { 67953a5a1b3Sopenharmony_ci int64_t diff; 68053a5a1b3Sopenharmony_ci 68153a5a1b3Sopenharmony_ci if (diff_time < 0) { 68253a5a1b3Sopenharmony_ci diff = pa_usec_to_bytes(-diff_time, &u->sink_input->sample_spec); 68353a5a1b3Sopenharmony_ci 68453a5a1b3Sopenharmony_ci if (diff > 0) { 68553a5a1b3Sopenharmony_ci /* add some extra safety samples to compensate for jitter in the 68653a5a1b3Sopenharmony_ci * timings */ 68753a5a1b3Sopenharmony_ci diff += 10 * pa_frame_size (&u->sink_input->sample_spec); 68853a5a1b3Sopenharmony_ci 68953a5a1b3Sopenharmony_ci pa_log("Playback after capture (%lld), drop sink %lld", (long long) diff_time, (long long) diff); 69053a5a1b3Sopenharmony_ci 69153a5a1b3Sopenharmony_ci u->sink_skip = diff; 69253a5a1b3Sopenharmony_ci u->source_skip = 0; 69353a5a1b3Sopenharmony_ci } 69453a5a1b3Sopenharmony_ci } else if (diff_time > 0) { 69553a5a1b3Sopenharmony_ci diff = pa_usec_to_bytes(diff_time, &u->source_output->sample_spec); 69653a5a1b3Sopenharmony_ci 69753a5a1b3Sopenharmony_ci if (diff > 0) { 69853a5a1b3Sopenharmony_ci pa_log("Playback too far ahead (%lld), drop source %lld", (long long) diff_time, (long long) diff); 69953a5a1b3Sopenharmony_ci 70053a5a1b3Sopenharmony_ci u->source_skip = diff; 70153a5a1b3Sopenharmony_ci u->sink_skip = 0; 70253a5a1b3Sopenharmony_ci } 70353a5a1b3Sopenharmony_ci } 70453a5a1b3Sopenharmony_ci} 70553a5a1b3Sopenharmony_ci 70653a5a1b3Sopenharmony_ci/* Called from source I/O thread context. */ 70753a5a1b3Sopenharmony_cistatic void do_resync(struct userdata *u) { 70853a5a1b3Sopenharmony_ci int64_t diff_time; 70953a5a1b3Sopenharmony_ci struct snapshot latency_snapshot; 71053a5a1b3Sopenharmony_ci 71153a5a1b3Sopenharmony_ci pa_log("Doing resync"); 71253a5a1b3Sopenharmony_ci 71353a5a1b3Sopenharmony_ci /* update our snapshot */ 71453a5a1b3Sopenharmony_ci /* 1. Get sink input latency snapshot, might cause buffers to be sent to source thread */ 71553a5a1b3Sopenharmony_ci pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, &latency_snapshot, 0, NULL); 71653a5a1b3Sopenharmony_ci /* 2. Pick up any in-flight buffers (and discard if needed) */ 71753a5a1b3Sopenharmony_ci while (pa_asyncmsgq_process_one(u->asyncmsgq)) 71853a5a1b3Sopenharmony_ci ; 71953a5a1b3Sopenharmony_ci /* 3. Now get the source output latency snapshot */ 72053a5a1b3Sopenharmony_ci source_output_snapshot_within_thread(u, &latency_snapshot); 72153a5a1b3Sopenharmony_ci 72253a5a1b3Sopenharmony_ci /* calculate drift between capture and playback */ 72353a5a1b3Sopenharmony_ci diff_time = calc_diff(u, &latency_snapshot); 72453a5a1b3Sopenharmony_ci 72553a5a1b3Sopenharmony_ci /* and adjust for the drift */ 72653a5a1b3Sopenharmony_ci apply_diff_time(u, diff_time); 72753a5a1b3Sopenharmony_ci} 72853a5a1b3Sopenharmony_ci 72953a5a1b3Sopenharmony_ci/* 1. Calculate drift at this point, pass to canceller 73053a5a1b3Sopenharmony_ci * 2. Push out playback samples in blocksize chunks 73153a5a1b3Sopenharmony_ci * 3. Push out capture samples in blocksize chunks 73253a5a1b3Sopenharmony_ci * 4. ??? 73353a5a1b3Sopenharmony_ci * 5. Profit 73453a5a1b3Sopenharmony_ci * 73553a5a1b3Sopenharmony_ci * Called from source I/O thread context. 73653a5a1b3Sopenharmony_ci */ 73753a5a1b3Sopenharmony_cistatic void do_push_drift_comp(struct userdata *u) { 73853a5a1b3Sopenharmony_ci size_t rlen, plen; 73953a5a1b3Sopenharmony_ci pa_memchunk rchunk, pchunk, cchunk; 74053a5a1b3Sopenharmony_ci uint8_t *rdata, *pdata, *cdata; 74153a5a1b3Sopenharmony_ci float drift; 74253a5a1b3Sopenharmony_ci int unused PA_GCC_UNUSED; 74353a5a1b3Sopenharmony_ci 74453a5a1b3Sopenharmony_ci rlen = pa_memblockq_get_length(u->source_memblockq); 74553a5a1b3Sopenharmony_ci plen = pa_memblockq_get_length(u->sink_memblockq); 74653a5a1b3Sopenharmony_ci 74753a5a1b3Sopenharmony_ci /* Estimate snapshot drift as follows: 74853a5a1b3Sopenharmony_ci * pd: amount of data consumed since last time 74953a5a1b3Sopenharmony_ci * rd: amount of data consumed since last time 75053a5a1b3Sopenharmony_ci * 75153a5a1b3Sopenharmony_ci * drift = (pd - rd) / rd; 75253a5a1b3Sopenharmony_ci * 75353a5a1b3Sopenharmony_ci * We calculate pd and rd as the memblockq length less the number of 75453a5a1b3Sopenharmony_ci * samples left from the last iteration (to avoid double counting 75553a5a1b3Sopenharmony_ci * those remainder samples. 75653a5a1b3Sopenharmony_ci */ 75753a5a1b3Sopenharmony_ci drift = ((float)(plen - u->sink_rem) - (rlen - u->source_rem)) / ((float)(rlen - u->source_rem)); 75853a5a1b3Sopenharmony_ci u->sink_rem = plen % u->sink_blocksize; 75953a5a1b3Sopenharmony_ci u->source_rem = rlen % u->source_output_blocksize; 76053a5a1b3Sopenharmony_ci 76153a5a1b3Sopenharmony_ci if (u->save_aec) { 76253a5a1b3Sopenharmony_ci if (u->drift_file) 76353a5a1b3Sopenharmony_ci fprintf(u->drift_file, "d %a\n", drift); 76453a5a1b3Sopenharmony_ci } 76553a5a1b3Sopenharmony_ci 76653a5a1b3Sopenharmony_ci /* Send in the playback samples first */ 76753a5a1b3Sopenharmony_ci while (plen >= u->sink_blocksize) { 76853a5a1b3Sopenharmony_ci pa_memblockq_peek_fixed_size(u->sink_memblockq, u->sink_blocksize, &pchunk); 76953a5a1b3Sopenharmony_ci pdata = pa_memblock_acquire(pchunk.memblock); 77053a5a1b3Sopenharmony_ci pdata += pchunk.index; 77153a5a1b3Sopenharmony_ci 77253a5a1b3Sopenharmony_ci u->ec->play(u->ec, pdata); 77353a5a1b3Sopenharmony_ci 77453a5a1b3Sopenharmony_ci if (u->save_aec) { 77553a5a1b3Sopenharmony_ci if (u->drift_file) 77653a5a1b3Sopenharmony_ci fprintf(u->drift_file, "p %d\n", u->sink_blocksize); 77753a5a1b3Sopenharmony_ci if (u->played_file) 77853a5a1b3Sopenharmony_ci unused = fwrite(pdata, 1, u->sink_blocksize, u->played_file); 77953a5a1b3Sopenharmony_ci } 78053a5a1b3Sopenharmony_ci 78153a5a1b3Sopenharmony_ci pa_memblock_release(pchunk.memblock); 78253a5a1b3Sopenharmony_ci pa_memblockq_drop(u->sink_memblockq, u->sink_blocksize); 78353a5a1b3Sopenharmony_ci pa_memblock_unref(pchunk.memblock); 78453a5a1b3Sopenharmony_ci 78553a5a1b3Sopenharmony_ci plen -= u->sink_blocksize; 78653a5a1b3Sopenharmony_ci } 78753a5a1b3Sopenharmony_ci 78853a5a1b3Sopenharmony_ci /* And now the capture samples */ 78953a5a1b3Sopenharmony_ci while (rlen >= u->source_output_blocksize) { 79053a5a1b3Sopenharmony_ci pa_memblockq_peek_fixed_size(u->source_memblockq, u->source_output_blocksize, &rchunk); 79153a5a1b3Sopenharmony_ci 79253a5a1b3Sopenharmony_ci rdata = pa_memblock_acquire(rchunk.memblock); 79353a5a1b3Sopenharmony_ci rdata += rchunk.index; 79453a5a1b3Sopenharmony_ci 79553a5a1b3Sopenharmony_ci cchunk.index = 0; 79653a5a1b3Sopenharmony_ci cchunk.length = u->source_output_blocksize; 79753a5a1b3Sopenharmony_ci cchunk.memblock = pa_memblock_new(u->source->core->mempool, cchunk.length); 79853a5a1b3Sopenharmony_ci cdata = pa_memblock_acquire(cchunk.memblock); 79953a5a1b3Sopenharmony_ci 80053a5a1b3Sopenharmony_ci u->ec->set_drift(u->ec, drift); 80153a5a1b3Sopenharmony_ci u->ec->record(u->ec, rdata, cdata); 80253a5a1b3Sopenharmony_ci 80353a5a1b3Sopenharmony_ci if (u->save_aec) { 80453a5a1b3Sopenharmony_ci if (u->drift_file) 80553a5a1b3Sopenharmony_ci fprintf(u->drift_file, "c %d\n", u->source_output_blocksize); 80653a5a1b3Sopenharmony_ci if (u->captured_file) 80753a5a1b3Sopenharmony_ci unused = fwrite(rdata, 1, u->source_output_blocksize, u->captured_file); 80853a5a1b3Sopenharmony_ci if (u->canceled_file) 80953a5a1b3Sopenharmony_ci unused = fwrite(cdata, 1, u->source_output_blocksize, u->canceled_file); 81053a5a1b3Sopenharmony_ci } 81153a5a1b3Sopenharmony_ci 81253a5a1b3Sopenharmony_ci pa_memblock_release(cchunk.memblock); 81353a5a1b3Sopenharmony_ci pa_memblock_release(rchunk.memblock); 81453a5a1b3Sopenharmony_ci 81553a5a1b3Sopenharmony_ci pa_memblock_unref(rchunk.memblock); 81653a5a1b3Sopenharmony_ci 81753a5a1b3Sopenharmony_ci pa_source_post(u->source, &cchunk); 81853a5a1b3Sopenharmony_ci pa_memblock_unref(cchunk.memblock); 81953a5a1b3Sopenharmony_ci 82053a5a1b3Sopenharmony_ci pa_memblockq_drop(u->source_memblockq, u->source_output_blocksize); 82153a5a1b3Sopenharmony_ci rlen -= u->source_output_blocksize; 82253a5a1b3Sopenharmony_ci } 82353a5a1b3Sopenharmony_ci} 82453a5a1b3Sopenharmony_ci 82553a5a1b3Sopenharmony_ci/* This one's simpler than the drift compensation case -- we just iterate over 82653a5a1b3Sopenharmony_ci * the capture buffer, and pass the canceller blocksize bytes of playback and 82753a5a1b3Sopenharmony_ci * capture data. If playback is currently inactive, we just push silence. 82853a5a1b3Sopenharmony_ci * 82953a5a1b3Sopenharmony_ci * Called from source I/O thread context. */ 83053a5a1b3Sopenharmony_cistatic void do_push(struct userdata *u) { 83153a5a1b3Sopenharmony_ci size_t rlen, plen; 83253a5a1b3Sopenharmony_ci pa_memchunk rchunk, pchunk, cchunk; 83353a5a1b3Sopenharmony_ci uint8_t *rdata, *pdata, *cdata; 83453a5a1b3Sopenharmony_ci int unused PA_GCC_UNUSED; 83553a5a1b3Sopenharmony_ci 83653a5a1b3Sopenharmony_ci rlen = pa_memblockq_get_length(u->source_memblockq); 83753a5a1b3Sopenharmony_ci plen = pa_memblockq_get_length(u->sink_memblockq); 83853a5a1b3Sopenharmony_ci 83953a5a1b3Sopenharmony_ci while (rlen >= u->source_output_blocksize) { 84053a5a1b3Sopenharmony_ci 84153a5a1b3Sopenharmony_ci /* take fixed blocks from recorded and played samples */ 84253a5a1b3Sopenharmony_ci pa_memblockq_peek_fixed_size(u->source_memblockq, u->source_output_blocksize, &rchunk); 84353a5a1b3Sopenharmony_ci pa_memblockq_peek_fixed_size(u->sink_memblockq, u->sink_blocksize, &pchunk); 84453a5a1b3Sopenharmony_ci 84553a5a1b3Sopenharmony_ci /* we ran out of played data and pchunk has been filled with silence bytes */ 84653a5a1b3Sopenharmony_ci if (plen < u->sink_blocksize) 84753a5a1b3Sopenharmony_ci pa_memblockq_seek(u->sink_memblockq, u->sink_blocksize - plen, PA_SEEK_RELATIVE, true); 84853a5a1b3Sopenharmony_ci 84953a5a1b3Sopenharmony_ci rdata = pa_memblock_acquire(rchunk.memblock); 85053a5a1b3Sopenharmony_ci rdata += rchunk.index; 85153a5a1b3Sopenharmony_ci pdata = pa_memblock_acquire(pchunk.memblock); 85253a5a1b3Sopenharmony_ci pdata += pchunk.index; 85353a5a1b3Sopenharmony_ci 85453a5a1b3Sopenharmony_ci cchunk.index = 0; 85553a5a1b3Sopenharmony_ci cchunk.length = u->source_blocksize; 85653a5a1b3Sopenharmony_ci cchunk.memblock = pa_memblock_new(u->source->core->mempool, cchunk.length); 85753a5a1b3Sopenharmony_ci cdata = pa_memblock_acquire(cchunk.memblock); 85853a5a1b3Sopenharmony_ci 85953a5a1b3Sopenharmony_ci if (u->save_aec) { 86053a5a1b3Sopenharmony_ci if (u->captured_file) 86153a5a1b3Sopenharmony_ci unused = fwrite(rdata, 1, u->source_output_blocksize, u->captured_file); 86253a5a1b3Sopenharmony_ci if (u->played_file) 86353a5a1b3Sopenharmony_ci unused = fwrite(pdata, 1, u->sink_blocksize, u->played_file); 86453a5a1b3Sopenharmony_ci } 86553a5a1b3Sopenharmony_ci 86653a5a1b3Sopenharmony_ci /* perform echo cancellation */ 86753a5a1b3Sopenharmony_ci u->ec->run(u->ec, rdata, pdata, cdata); 86853a5a1b3Sopenharmony_ci 86953a5a1b3Sopenharmony_ci if (u->save_aec) { 87053a5a1b3Sopenharmony_ci if (u->canceled_file) 87153a5a1b3Sopenharmony_ci unused = fwrite(cdata, 1, u->source_blocksize, u->canceled_file); 87253a5a1b3Sopenharmony_ci } 87353a5a1b3Sopenharmony_ci 87453a5a1b3Sopenharmony_ci pa_memblock_release(cchunk.memblock); 87553a5a1b3Sopenharmony_ci pa_memblock_release(pchunk.memblock); 87653a5a1b3Sopenharmony_ci pa_memblock_release(rchunk.memblock); 87753a5a1b3Sopenharmony_ci 87853a5a1b3Sopenharmony_ci /* drop consumed source samples */ 87953a5a1b3Sopenharmony_ci pa_memblockq_drop(u->source_memblockq, u->source_output_blocksize); 88053a5a1b3Sopenharmony_ci pa_memblock_unref(rchunk.memblock); 88153a5a1b3Sopenharmony_ci rlen -= u->source_output_blocksize; 88253a5a1b3Sopenharmony_ci 88353a5a1b3Sopenharmony_ci /* drop consumed sink samples */ 88453a5a1b3Sopenharmony_ci pa_memblockq_drop(u->sink_memblockq, u->sink_blocksize); 88553a5a1b3Sopenharmony_ci pa_memblock_unref(pchunk.memblock); 88653a5a1b3Sopenharmony_ci 88753a5a1b3Sopenharmony_ci if (plen >= u->sink_blocksize) 88853a5a1b3Sopenharmony_ci plen -= u->sink_blocksize; 88953a5a1b3Sopenharmony_ci else 89053a5a1b3Sopenharmony_ci plen = 0; 89153a5a1b3Sopenharmony_ci 89253a5a1b3Sopenharmony_ci /* forward the (echo-canceled) data to the virtual source */ 89353a5a1b3Sopenharmony_ci pa_source_post(u->source, &cchunk); 89453a5a1b3Sopenharmony_ci pa_memblock_unref(cchunk.memblock); 89553a5a1b3Sopenharmony_ci } 89653a5a1b3Sopenharmony_ci} 89753a5a1b3Sopenharmony_ci 89853a5a1b3Sopenharmony_ci/* Called from source I/O thread context. */ 89953a5a1b3Sopenharmony_cistatic void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { 90053a5a1b3Sopenharmony_ci struct userdata *u; 90153a5a1b3Sopenharmony_ci size_t rlen, plen, to_skip; 90253a5a1b3Sopenharmony_ci pa_memchunk rchunk; 90353a5a1b3Sopenharmony_ci 90453a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 90553a5a1b3Sopenharmony_ci pa_source_output_assert_io_context(o); 90653a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 90753a5a1b3Sopenharmony_ci 90853a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state)) 90953a5a1b3Sopenharmony_ci return; 91053a5a1b3Sopenharmony_ci 91153a5a1b3Sopenharmony_ci if (!PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) { 91253a5a1b3Sopenharmony_ci pa_log("Push when no link?"); 91353a5a1b3Sopenharmony_ci return; 91453a5a1b3Sopenharmony_ci } 91553a5a1b3Sopenharmony_ci 91653a5a1b3Sopenharmony_ci /* handle queued messages, do any message sending of our own */ 91753a5a1b3Sopenharmony_ci while (pa_asyncmsgq_process_one(u->asyncmsgq) > 0) 91853a5a1b3Sopenharmony_ci ; 91953a5a1b3Sopenharmony_ci 92053a5a1b3Sopenharmony_ci pa_memblockq_push_align(u->source_memblockq, chunk); 92153a5a1b3Sopenharmony_ci 92253a5a1b3Sopenharmony_ci rlen = pa_memblockq_get_length(u->source_memblockq); 92353a5a1b3Sopenharmony_ci plen = pa_memblockq_get_length(u->sink_memblockq); 92453a5a1b3Sopenharmony_ci 92553a5a1b3Sopenharmony_ci /* Let's not do anything else till we have enough data to process */ 92653a5a1b3Sopenharmony_ci if (rlen < u->source_output_blocksize) 92753a5a1b3Sopenharmony_ci return; 92853a5a1b3Sopenharmony_ci 92953a5a1b3Sopenharmony_ci /* See if we need to drop samples in order to sync */ 93053a5a1b3Sopenharmony_ci if (pa_atomic_cmpxchg (&u->request_resync, 1, 0)) { 93153a5a1b3Sopenharmony_ci do_resync(u); 93253a5a1b3Sopenharmony_ci } 93353a5a1b3Sopenharmony_ci 93453a5a1b3Sopenharmony_ci /* Okay, skip cancellation for skipped source samples if needed. */ 93553a5a1b3Sopenharmony_ci if (PA_UNLIKELY(u->source_skip)) { 93653a5a1b3Sopenharmony_ci /* The slightly tricky bit here is that we drop all but modulo 93753a5a1b3Sopenharmony_ci * blocksize bytes and then adjust for that last bit on the sink side. 93853a5a1b3Sopenharmony_ci * We do this because the source data is coming at a fixed rate, which 93953a5a1b3Sopenharmony_ci * means the only way to try to catch up is drop sink samples and let 94053a5a1b3Sopenharmony_ci * the canceller cope up with this. */ 94153a5a1b3Sopenharmony_ci to_skip = rlen >= u->source_skip ? u->source_skip : rlen; 94253a5a1b3Sopenharmony_ci to_skip -= to_skip % u->source_output_blocksize; 94353a5a1b3Sopenharmony_ci 94453a5a1b3Sopenharmony_ci if (to_skip) { 94553a5a1b3Sopenharmony_ci pa_memblockq_peek_fixed_size(u->source_memblockq, to_skip, &rchunk); 94653a5a1b3Sopenharmony_ci pa_source_post(u->source, &rchunk); 94753a5a1b3Sopenharmony_ci 94853a5a1b3Sopenharmony_ci pa_memblock_unref(rchunk.memblock); 94953a5a1b3Sopenharmony_ci pa_memblockq_drop(u->source_memblockq, to_skip); 95053a5a1b3Sopenharmony_ci 95153a5a1b3Sopenharmony_ci rlen -= to_skip; 95253a5a1b3Sopenharmony_ci u->source_skip -= to_skip; 95353a5a1b3Sopenharmony_ci } 95453a5a1b3Sopenharmony_ci 95553a5a1b3Sopenharmony_ci if (rlen && u->source_skip % u->source_output_blocksize) { 95653a5a1b3Sopenharmony_ci u->sink_skip += (uint64_t) (u->source_output_blocksize - (u->source_skip % u->source_output_blocksize)) * u->sink_blocksize / u->source_output_blocksize; 95753a5a1b3Sopenharmony_ci u->source_skip -= (u->source_skip % u->source_output_blocksize); 95853a5a1b3Sopenharmony_ci } 95953a5a1b3Sopenharmony_ci } 96053a5a1b3Sopenharmony_ci 96153a5a1b3Sopenharmony_ci /* And for the sink, these samples have been played back already, so we can 96253a5a1b3Sopenharmony_ci * just drop them and get on with it. */ 96353a5a1b3Sopenharmony_ci if (PA_UNLIKELY(u->sink_skip)) { 96453a5a1b3Sopenharmony_ci to_skip = plen >= u->sink_skip ? u->sink_skip : plen; 96553a5a1b3Sopenharmony_ci 96653a5a1b3Sopenharmony_ci pa_memblockq_drop(u->sink_memblockq, to_skip); 96753a5a1b3Sopenharmony_ci 96853a5a1b3Sopenharmony_ci plen -= to_skip; 96953a5a1b3Sopenharmony_ci u->sink_skip -= to_skip; 97053a5a1b3Sopenharmony_ci } 97153a5a1b3Sopenharmony_ci 97253a5a1b3Sopenharmony_ci /* process and push out samples */ 97353a5a1b3Sopenharmony_ci if (u->ec->params.drift_compensation) 97453a5a1b3Sopenharmony_ci do_push_drift_comp(u); 97553a5a1b3Sopenharmony_ci else 97653a5a1b3Sopenharmony_ci do_push(u); 97753a5a1b3Sopenharmony_ci} 97853a5a1b3Sopenharmony_ci 97953a5a1b3Sopenharmony_ci/* Called from sink I/O thread context. */ 98053a5a1b3Sopenharmony_cistatic int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { 98153a5a1b3Sopenharmony_ci struct userdata *u; 98253a5a1b3Sopenharmony_ci 98353a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 98453a5a1b3Sopenharmony_ci pa_assert(chunk); 98553a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 98653a5a1b3Sopenharmony_ci 98753a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state)) 98853a5a1b3Sopenharmony_ci return -1; 98953a5a1b3Sopenharmony_ci 99053a5a1b3Sopenharmony_ci if (u->sink->thread_info.rewind_requested) 99153a5a1b3Sopenharmony_ci pa_sink_process_rewind(u->sink, 0); 99253a5a1b3Sopenharmony_ci 99353a5a1b3Sopenharmony_ci pa_sink_render_full(u->sink, nbytes, chunk); 99453a5a1b3Sopenharmony_ci 99553a5a1b3Sopenharmony_ci if (i->thread_info.underrun_for > 0) { 99653a5a1b3Sopenharmony_ci pa_log_debug("Handling end of underrun."); 99753a5a1b3Sopenharmony_ci pa_atomic_store(&u->request_resync, 1); 99853a5a1b3Sopenharmony_ci } 99953a5a1b3Sopenharmony_ci 100053a5a1b3Sopenharmony_ci /* let source thread handle the chunk. pass the sample count as well so that 100153a5a1b3Sopenharmony_ci * the source IO thread can update the right variables. */ 100253a5a1b3Sopenharmony_ci pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_POST, 100353a5a1b3Sopenharmony_ci NULL, 0, chunk, NULL); 100453a5a1b3Sopenharmony_ci u->send_counter += chunk->length; 100553a5a1b3Sopenharmony_ci 100653a5a1b3Sopenharmony_ci return 0; 100753a5a1b3Sopenharmony_ci} 100853a5a1b3Sopenharmony_ci 100953a5a1b3Sopenharmony_ci/* Called from source I/O thread context. */ 101053a5a1b3Sopenharmony_cistatic void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) { 101153a5a1b3Sopenharmony_ci struct userdata *u; 101253a5a1b3Sopenharmony_ci 101353a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 101453a5a1b3Sopenharmony_ci pa_source_output_assert_io_context(o); 101553a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 101653a5a1b3Sopenharmony_ci 101753a5a1b3Sopenharmony_ci /* If the source is not yet linked, there is nothing to rewind */ 101853a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state)) 101953a5a1b3Sopenharmony_ci return; 102053a5a1b3Sopenharmony_ci 102153a5a1b3Sopenharmony_ci pa_source_process_rewind(u->source, nbytes); 102253a5a1b3Sopenharmony_ci 102353a5a1b3Sopenharmony_ci /* go back on read side, we need to use older sink data for this */ 102453a5a1b3Sopenharmony_ci pa_memblockq_rewind(u->sink_memblockq, nbytes); 102553a5a1b3Sopenharmony_ci 102653a5a1b3Sopenharmony_ci /* manipulate write index */ 102753a5a1b3Sopenharmony_ci pa_memblockq_seek(u->source_memblockq, -nbytes, PA_SEEK_RELATIVE, true); 102853a5a1b3Sopenharmony_ci 102953a5a1b3Sopenharmony_ci pa_log_debug("Source rewind (%lld) %lld", (long long) nbytes, 103053a5a1b3Sopenharmony_ci (long long) pa_memblockq_get_length (u->source_memblockq)); 103153a5a1b3Sopenharmony_ci} 103253a5a1b3Sopenharmony_ci 103353a5a1b3Sopenharmony_ci/* Called from sink I/O thread context. */ 103453a5a1b3Sopenharmony_cistatic void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { 103553a5a1b3Sopenharmony_ci struct userdata *u; 103653a5a1b3Sopenharmony_ci 103753a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 103853a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 103953a5a1b3Sopenharmony_ci 104053a5a1b3Sopenharmony_ci /* If the sink is not yet linked, there is nothing to rewind */ 104153a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state)) 104253a5a1b3Sopenharmony_ci return; 104353a5a1b3Sopenharmony_ci 104453a5a1b3Sopenharmony_ci pa_log_debug("Sink process rewind %lld", (long long) nbytes); 104553a5a1b3Sopenharmony_ci 104653a5a1b3Sopenharmony_ci pa_sink_process_rewind(u->sink, nbytes); 104753a5a1b3Sopenharmony_ci 104853a5a1b3Sopenharmony_ci pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL); 104953a5a1b3Sopenharmony_ci u->send_counter -= nbytes; 105053a5a1b3Sopenharmony_ci} 105153a5a1b3Sopenharmony_ci 105253a5a1b3Sopenharmony_ci/* Called from source I/O thread context. */ 105353a5a1b3Sopenharmony_cistatic void source_output_snapshot_within_thread(struct userdata *u, struct snapshot *snapshot) { 105453a5a1b3Sopenharmony_ci size_t delay, rlen, plen; 105553a5a1b3Sopenharmony_ci pa_usec_t now, latency; 105653a5a1b3Sopenharmony_ci 105753a5a1b3Sopenharmony_ci now = pa_rtclock_now(); 105853a5a1b3Sopenharmony_ci latency = pa_source_get_latency_within_thread(u->source_output->source, false); 105953a5a1b3Sopenharmony_ci /* Add resampler delay */ 106053a5a1b3Sopenharmony_ci latency += pa_resampler_get_delay_usec(u->source_output->thread_info.resampler); 106153a5a1b3Sopenharmony_ci 106253a5a1b3Sopenharmony_ci delay = pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq); 106353a5a1b3Sopenharmony_ci 106453a5a1b3Sopenharmony_ci delay = (u->source_output->thread_info.resampler ? pa_resampler_request(u->source_output->thread_info.resampler, delay) : delay); 106553a5a1b3Sopenharmony_ci rlen = pa_memblockq_get_length(u->source_memblockq); 106653a5a1b3Sopenharmony_ci plen = pa_memblockq_get_length(u->sink_memblockq); 106753a5a1b3Sopenharmony_ci 106853a5a1b3Sopenharmony_ci snapshot->source_now = now; 106953a5a1b3Sopenharmony_ci snapshot->source_latency = latency; 107053a5a1b3Sopenharmony_ci snapshot->source_delay = delay; 107153a5a1b3Sopenharmony_ci snapshot->recv_counter = u->recv_counter; 107253a5a1b3Sopenharmony_ci snapshot->rlen = rlen + u->sink_skip; 107353a5a1b3Sopenharmony_ci snapshot->plen = plen + u->source_skip; 107453a5a1b3Sopenharmony_ci} 107553a5a1b3Sopenharmony_ci 107653a5a1b3Sopenharmony_ci/* Called from source I/O thread context. */ 107753a5a1b3Sopenharmony_cistatic int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { 107853a5a1b3Sopenharmony_ci struct userdata *u = PA_SOURCE_OUTPUT(obj)->userdata; 107953a5a1b3Sopenharmony_ci 108053a5a1b3Sopenharmony_ci switch (code) { 108153a5a1b3Sopenharmony_ci 108253a5a1b3Sopenharmony_ci case SOURCE_OUTPUT_MESSAGE_POST: 108353a5a1b3Sopenharmony_ci 108453a5a1b3Sopenharmony_ci pa_source_output_assert_io_context(u->source_output); 108553a5a1b3Sopenharmony_ci 108653a5a1b3Sopenharmony_ci if (u->source_output->source->thread_info.state == PA_SOURCE_RUNNING) 108753a5a1b3Sopenharmony_ci pa_memblockq_push_align(u->sink_memblockq, chunk); 108853a5a1b3Sopenharmony_ci else 108953a5a1b3Sopenharmony_ci pa_memblockq_flush_write(u->sink_memblockq, true); 109053a5a1b3Sopenharmony_ci 109153a5a1b3Sopenharmony_ci u->recv_counter += (int64_t) chunk->length; 109253a5a1b3Sopenharmony_ci 109353a5a1b3Sopenharmony_ci return 0; 109453a5a1b3Sopenharmony_ci 109553a5a1b3Sopenharmony_ci case SOURCE_OUTPUT_MESSAGE_REWIND: 109653a5a1b3Sopenharmony_ci pa_source_output_assert_io_context(u->source_output); 109753a5a1b3Sopenharmony_ci 109853a5a1b3Sopenharmony_ci /* manipulate write index, never go past what we have */ 109953a5a1b3Sopenharmony_ci if (PA_SOURCE_IS_OPENED(u->source_output->source->thread_info.state)) 110053a5a1b3Sopenharmony_ci pa_memblockq_seek(u->sink_memblockq, -offset, PA_SEEK_RELATIVE, true); 110153a5a1b3Sopenharmony_ci else 110253a5a1b3Sopenharmony_ci pa_memblockq_flush_write(u->sink_memblockq, true); 110353a5a1b3Sopenharmony_ci 110453a5a1b3Sopenharmony_ci pa_log_debug("Sink rewind (%lld)", (long long) offset); 110553a5a1b3Sopenharmony_ci 110653a5a1b3Sopenharmony_ci u->recv_counter -= offset; 110753a5a1b3Sopenharmony_ci 110853a5a1b3Sopenharmony_ci return 0; 110953a5a1b3Sopenharmony_ci 111053a5a1b3Sopenharmony_ci case SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT: { 111153a5a1b3Sopenharmony_ci struct snapshot *snapshot = (struct snapshot *) data; 111253a5a1b3Sopenharmony_ci 111353a5a1b3Sopenharmony_ci source_output_snapshot_within_thread(u, snapshot); 111453a5a1b3Sopenharmony_ci return 0; 111553a5a1b3Sopenharmony_ci } 111653a5a1b3Sopenharmony_ci 111753a5a1b3Sopenharmony_ci case SOURCE_OUTPUT_MESSAGE_APPLY_DIFF_TIME: 111853a5a1b3Sopenharmony_ci apply_diff_time(u, offset); 111953a5a1b3Sopenharmony_ci return 0; 112053a5a1b3Sopenharmony_ci 112153a5a1b3Sopenharmony_ci } 112253a5a1b3Sopenharmony_ci 112353a5a1b3Sopenharmony_ci return pa_source_output_process_msg(obj, code, data, offset, chunk); 112453a5a1b3Sopenharmony_ci} 112553a5a1b3Sopenharmony_ci 112653a5a1b3Sopenharmony_ci/* Called from sink I/O thread context. */ 112753a5a1b3Sopenharmony_cistatic int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { 112853a5a1b3Sopenharmony_ci struct userdata *u = PA_SINK_INPUT(obj)->userdata; 112953a5a1b3Sopenharmony_ci 113053a5a1b3Sopenharmony_ci switch (code) { 113153a5a1b3Sopenharmony_ci 113253a5a1b3Sopenharmony_ci case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: { 113353a5a1b3Sopenharmony_ci size_t delay; 113453a5a1b3Sopenharmony_ci pa_usec_t now, latency; 113553a5a1b3Sopenharmony_ci struct snapshot *snapshot = (struct snapshot *) data; 113653a5a1b3Sopenharmony_ci 113753a5a1b3Sopenharmony_ci pa_sink_input_assert_io_context(u->sink_input); 113853a5a1b3Sopenharmony_ci 113953a5a1b3Sopenharmony_ci now = pa_rtclock_now(); 114053a5a1b3Sopenharmony_ci latency = pa_sink_get_latency_within_thread(u->sink_input->sink, false); 114153a5a1b3Sopenharmony_ci /* Add resampler delay */ 114253a5a1b3Sopenharmony_ci latency += pa_resampler_get_delay_usec(u->sink_input->thread_info.resampler); 114353a5a1b3Sopenharmony_ci 114453a5a1b3Sopenharmony_ci delay = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq); 114553a5a1b3Sopenharmony_ci 114653a5a1b3Sopenharmony_ci delay = (u->sink_input->thread_info.resampler ? pa_resampler_request(u->sink_input->thread_info.resampler, delay) : delay); 114753a5a1b3Sopenharmony_ci 114853a5a1b3Sopenharmony_ci snapshot->sink_now = now; 114953a5a1b3Sopenharmony_ci snapshot->sink_latency = latency; 115053a5a1b3Sopenharmony_ci snapshot->sink_delay = delay; 115153a5a1b3Sopenharmony_ci snapshot->send_counter = u->send_counter; 115253a5a1b3Sopenharmony_ci return 0; 115353a5a1b3Sopenharmony_ci } 115453a5a1b3Sopenharmony_ci } 115553a5a1b3Sopenharmony_ci 115653a5a1b3Sopenharmony_ci return pa_sink_input_process_msg(obj, code, data, offset, chunk); 115753a5a1b3Sopenharmony_ci} 115853a5a1b3Sopenharmony_ci 115953a5a1b3Sopenharmony_ci/* Called from sink I/O thread context. */ 116053a5a1b3Sopenharmony_cistatic void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { 116153a5a1b3Sopenharmony_ci struct userdata *u; 116253a5a1b3Sopenharmony_ci 116353a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 116453a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 116553a5a1b3Sopenharmony_ci 116653a5a1b3Sopenharmony_ci pa_log_debug("Sink input update max rewind %lld", (long long) nbytes); 116753a5a1b3Sopenharmony_ci 116853a5a1b3Sopenharmony_ci /* FIXME: Too small max_rewind: 116953a5a1b3Sopenharmony_ci * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */ 117053a5a1b3Sopenharmony_ci pa_memblockq_set_maxrewind(u->sink_memblockq, nbytes); 117153a5a1b3Sopenharmony_ci pa_sink_set_max_rewind_within_thread(u->sink, nbytes); 117253a5a1b3Sopenharmony_ci} 117353a5a1b3Sopenharmony_ci 117453a5a1b3Sopenharmony_ci/* Called from source I/O thread context. */ 117553a5a1b3Sopenharmony_cistatic void source_output_update_max_rewind_cb(pa_source_output *o, size_t nbytes) { 117653a5a1b3Sopenharmony_ci struct userdata *u; 117753a5a1b3Sopenharmony_ci 117853a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 117953a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 118053a5a1b3Sopenharmony_ci 118153a5a1b3Sopenharmony_ci pa_log_debug("Source output update max rewind %lld", (long long) nbytes); 118253a5a1b3Sopenharmony_ci 118353a5a1b3Sopenharmony_ci pa_source_set_max_rewind_within_thread(u->source, nbytes); 118453a5a1b3Sopenharmony_ci} 118553a5a1b3Sopenharmony_ci 118653a5a1b3Sopenharmony_ci/* Called from sink I/O thread context. */ 118753a5a1b3Sopenharmony_cistatic void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { 118853a5a1b3Sopenharmony_ci struct userdata *u; 118953a5a1b3Sopenharmony_ci 119053a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 119153a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 119253a5a1b3Sopenharmony_ci 119353a5a1b3Sopenharmony_ci pa_log_debug("Sink input update max request %lld", (long long) nbytes); 119453a5a1b3Sopenharmony_ci 119553a5a1b3Sopenharmony_ci pa_sink_set_max_request_within_thread(u->sink, nbytes); 119653a5a1b3Sopenharmony_ci} 119753a5a1b3Sopenharmony_ci 119853a5a1b3Sopenharmony_ci/* Called from sink I/O thread context. */ 119953a5a1b3Sopenharmony_cistatic void sink_input_update_sink_requested_latency_cb(pa_sink_input *i) { 120053a5a1b3Sopenharmony_ci struct userdata *u; 120153a5a1b3Sopenharmony_ci pa_usec_t latency; 120253a5a1b3Sopenharmony_ci 120353a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 120453a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 120553a5a1b3Sopenharmony_ci 120653a5a1b3Sopenharmony_ci latency = pa_sink_get_requested_latency_within_thread(i->sink); 120753a5a1b3Sopenharmony_ci 120853a5a1b3Sopenharmony_ci pa_log_debug("Sink input update requested latency %lld", (long long) latency); 120953a5a1b3Sopenharmony_ci} 121053a5a1b3Sopenharmony_ci 121153a5a1b3Sopenharmony_ci/* Called from source I/O thread context. */ 121253a5a1b3Sopenharmony_cistatic void source_output_update_source_requested_latency_cb(pa_source_output *o) { 121353a5a1b3Sopenharmony_ci struct userdata *u; 121453a5a1b3Sopenharmony_ci pa_usec_t latency; 121553a5a1b3Sopenharmony_ci 121653a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 121753a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 121853a5a1b3Sopenharmony_ci 121953a5a1b3Sopenharmony_ci latency = pa_source_get_requested_latency_within_thread(o->source); 122053a5a1b3Sopenharmony_ci 122153a5a1b3Sopenharmony_ci pa_log_debug("Source output update requested latency %lld", (long long) latency); 122253a5a1b3Sopenharmony_ci} 122353a5a1b3Sopenharmony_ci 122453a5a1b3Sopenharmony_ci/* Called from sink I/O thread context. */ 122553a5a1b3Sopenharmony_cistatic void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { 122653a5a1b3Sopenharmony_ci struct userdata *u; 122753a5a1b3Sopenharmony_ci 122853a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 122953a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 123053a5a1b3Sopenharmony_ci 123153a5a1b3Sopenharmony_ci pa_log_debug("Sink input update latency range %lld %lld", 123253a5a1b3Sopenharmony_ci (long long) i->sink->thread_info.min_latency, 123353a5a1b3Sopenharmony_ci (long long) i->sink->thread_info.max_latency); 123453a5a1b3Sopenharmony_ci 123553a5a1b3Sopenharmony_ci pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); 123653a5a1b3Sopenharmony_ci} 123753a5a1b3Sopenharmony_ci 123853a5a1b3Sopenharmony_ci/* Called from source I/O thread context. */ 123953a5a1b3Sopenharmony_cistatic void source_output_update_source_latency_range_cb(pa_source_output *o) { 124053a5a1b3Sopenharmony_ci struct userdata *u; 124153a5a1b3Sopenharmony_ci 124253a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 124353a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 124453a5a1b3Sopenharmony_ci 124553a5a1b3Sopenharmony_ci pa_log_debug("Source output update latency range %lld %lld", 124653a5a1b3Sopenharmony_ci (long long) o->source->thread_info.min_latency, 124753a5a1b3Sopenharmony_ci (long long) o->source->thread_info.max_latency); 124853a5a1b3Sopenharmony_ci 124953a5a1b3Sopenharmony_ci pa_source_set_latency_range_within_thread(u->source, o->source->thread_info.min_latency, o->source->thread_info.max_latency); 125053a5a1b3Sopenharmony_ci} 125153a5a1b3Sopenharmony_ci 125253a5a1b3Sopenharmony_ci/* Called from sink I/O thread context. */ 125353a5a1b3Sopenharmony_cistatic void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) { 125453a5a1b3Sopenharmony_ci struct userdata *u; 125553a5a1b3Sopenharmony_ci 125653a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 125753a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 125853a5a1b3Sopenharmony_ci 125953a5a1b3Sopenharmony_ci pa_log_debug("Sink input update fixed latency %lld", 126053a5a1b3Sopenharmony_ci (long long) i->sink->thread_info.fixed_latency); 126153a5a1b3Sopenharmony_ci 126253a5a1b3Sopenharmony_ci pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); 126353a5a1b3Sopenharmony_ci} 126453a5a1b3Sopenharmony_ci 126553a5a1b3Sopenharmony_ci/* Called from source I/O thread context. */ 126653a5a1b3Sopenharmony_cistatic void source_output_update_source_fixed_latency_cb(pa_source_output *o) { 126753a5a1b3Sopenharmony_ci struct userdata *u; 126853a5a1b3Sopenharmony_ci 126953a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 127053a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 127153a5a1b3Sopenharmony_ci 127253a5a1b3Sopenharmony_ci pa_log_debug("Source output update fixed latency %lld", 127353a5a1b3Sopenharmony_ci (long long) o->source->thread_info.fixed_latency); 127453a5a1b3Sopenharmony_ci 127553a5a1b3Sopenharmony_ci pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency); 127653a5a1b3Sopenharmony_ci} 127753a5a1b3Sopenharmony_ci 127853a5a1b3Sopenharmony_ci/* Called from source I/O thread context. */ 127953a5a1b3Sopenharmony_cistatic void source_output_attach_cb(pa_source_output *o) { 128053a5a1b3Sopenharmony_ci struct userdata *u; 128153a5a1b3Sopenharmony_ci 128253a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 128353a5a1b3Sopenharmony_ci pa_source_output_assert_io_context(o); 128453a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 128553a5a1b3Sopenharmony_ci 128653a5a1b3Sopenharmony_ci pa_source_set_rtpoll(u->source, o->source->thread_info.rtpoll); 128753a5a1b3Sopenharmony_ci pa_source_set_latency_range_within_thread(u->source, o->source->thread_info.min_latency, o->source->thread_info.max_latency); 128853a5a1b3Sopenharmony_ci pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency); 128953a5a1b3Sopenharmony_ci pa_source_set_max_rewind_within_thread(u->source, pa_source_output_get_max_rewind(o)); 129053a5a1b3Sopenharmony_ci 129153a5a1b3Sopenharmony_ci pa_log_debug("Source output %d attach", o->index); 129253a5a1b3Sopenharmony_ci 129353a5a1b3Sopenharmony_ci if (PA_SOURCE_IS_LINKED(u->source->thread_info.state)) 129453a5a1b3Sopenharmony_ci pa_source_attach_within_thread(u->source); 129553a5a1b3Sopenharmony_ci 129653a5a1b3Sopenharmony_ci u->rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read( 129753a5a1b3Sopenharmony_ci o->source->thread_info.rtpoll, 129853a5a1b3Sopenharmony_ci PA_RTPOLL_LATE, 129953a5a1b3Sopenharmony_ci u->asyncmsgq); 130053a5a1b3Sopenharmony_ci} 130153a5a1b3Sopenharmony_ci 130253a5a1b3Sopenharmony_ci/* Called from sink I/O thread context. */ 130353a5a1b3Sopenharmony_cistatic void sink_input_attach_cb(pa_sink_input *i) { 130453a5a1b3Sopenharmony_ci struct userdata *u; 130553a5a1b3Sopenharmony_ci 130653a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 130753a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 130853a5a1b3Sopenharmony_ci 130953a5a1b3Sopenharmony_ci pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll); 131053a5a1b3Sopenharmony_ci pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); 131153a5a1b3Sopenharmony_ci 131253a5a1b3Sopenharmony_ci /* (8.1) IF YOU NEED A FIXED BLOCK SIZE ADD THE LATENCY FOR ONE 131353a5a1b3Sopenharmony_ci * BLOCK MINUS ONE SAMPLE HERE. SEE (7) */ 131453a5a1b3Sopenharmony_ci pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); 131553a5a1b3Sopenharmony_ci 131653a5a1b3Sopenharmony_ci /* (8.2) IF YOU NEED A FIXED BLOCK SIZE ROUND 131753a5a1b3Sopenharmony_ci * pa_sink_input_get_max_request(i) UP TO MULTIPLES OF IT 131853a5a1b3Sopenharmony_ci * HERE. SEE (6) */ 131953a5a1b3Sopenharmony_ci pa_sink_set_max_request_within_thread(u->sink, pa_sink_input_get_max_request(i)); 132053a5a1b3Sopenharmony_ci 132153a5a1b3Sopenharmony_ci /* FIXME: Too small max_rewind: 132253a5a1b3Sopenharmony_ci * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */ 132353a5a1b3Sopenharmony_ci pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i)); 132453a5a1b3Sopenharmony_ci 132553a5a1b3Sopenharmony_ci pa_log_debug("Sink input %d attach", i->index); 132653a5a1b3Sopenharmony_ci 132753a5a1b3Sopenharmony_ci u->rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write( 132853a5a1b3Sopenharmony_ci i->sink->thread_info.rtpoll, 132953a5a1b3Sopenharmony_ci PA_RTPOLL_LATE, 133053a5a1b3Sopenharmony_ci u->asyncmsgq); 133153a5a1b3Sopenharmony_ci 133253a5a1b3Sopenharmony_ci if (PA_SINK_IS_LINKED(u->sink->thread_info.state)) 133353a5a1b3Sopenharmony_ci pa_sink_attach_within_thread(u->sink); 133453a5a1b3Sopenharmony_ci} 133553a5a1b3Sopenharmony_ci 133653a5a1b3Sopenharmony_ci/* Called from source I/O thread context. */ 133753a5a1b3Sopenharmony_cistatic void source_output_detach_cb(pa_source_output *o) { 133853a5a1b3Sopenharmony_ci struct userdata *u; 133953a5a1b3Sopenharmony_ci 134053a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 134153a5a1b3Sopenharmony_ci pa_source_output_assert_io_context(o); 134253a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 134353a5a1b3Sopenharmony_ci 134453a5a1b3Sopenharmony_ci if (PA_SOURCE_IS_LINKED(u->source->thread_info.state)) 134553a5a1b3Sopenharmony_ci pa_source_detach_within_thread(u->source); 134653a5a1b3Sopenharmony_ci pa_source_set_rtpoll(u->source, NULL); 134753a5a1b3Sopenharmony_ci 134853a5a1b3Sopenharmony_ci pa_log_debug("Source output %d detach", o->index); 134953a5a1b3Sopenharmony_ci 135053a5a1b3Sopenharmony_ci if (u->rtpoll_item_read) { 135153a5a1b3Sopenharmony_ci pa_rtpoll_item_free(u->rtpoll_item_read); 135253a5a1b3Sopenharmony_ci u->rtpoll_item_read = NULL; 135353a5a1b3Sopenharmony_ci } 135453a5a1b3Sopenharmony_ci} 135553a5a1b3Sopenharmony_ci 135653a5a1b3Sopenharmony_ci/* Called from sink I/O thread context. */ 135753a5a1b3Sopenharmony_cistatic void sink_input_detach_cb(pa_sink_input *i) { 135853a5a1b3Sopenharmony_ci struct userdata *u; 135953a5a1b3Sopenharmony_ci 136053a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 136153a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 136253a5a1b3Sopenharmony_ci 136353a5a1b3Sopenharmony_ci if (PA_SINK_IS_LINKED(u->sink->thread_info.state)) 136453a5a1b3Sopenharmony_ci pa_sink_detach_within_thread(u->sink); 136553a5a1b3Sopenharmony_ci 136653a5a1b3Sopenharmony_ci pa_sink_set_rtpoll(u->sink, NULL); 136753a5a1b3Sopenharmony_ci 136853a5a1b3Sopenharmony_ci pa_log_debug("Sink input %d detach", i->index); 136953a5a1b3Sopenharmony_ci 137053a5a1b3Sopenharmony_ci if (u->rtpoll_item_write) { 137153a5a1b3Sopenharmony_ci pa_rtpoll_item_free(u->rtpoll_item_write); 137253a5a1b3Sopenharmony_ci u->rtpoll_item_write = NULL; 137353a5a1b3Sopenharmony_ci } 137453a5a1b3Sopenharmony_ci} 137553a5a1b3Sopenharmony_ci 137653a5a1b3Sopenharmony_ci/* Called from source I/O thread context except when cork() is called without valid source. */ 137753a5a1b3Sopenharmony_cistatic void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) { 137853a5a1b3Sopenharmony_ci struct userdata *u; 137953a5a1b3Sopenharmony_ci 138053a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 138153a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 138253a5a1b3Sopenharmony_ci 138353a5a1b3Sopenharmony_ci pa_log_debug("Source output %d state %d", o->index, state); 138453a5a1b3Sopenharmony_ci} 138553a5a1b3Sopenharmony_ci 138653a5a1b3Sopenharmony_ci/* Called from sink I/O thread context. */ 138753a5a1b3Sopenharmony_cistatic void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { 138853a5a1b3Sopenharmony_ci struct userdata *u; 138953a5a1b3Sopenharmony_ci 139053a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 139153a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 139253a5a1b3Sopenharmony_ci 139353a5a1b3Sopenharmony_ci pa_log_debug("Sink input %d state %d", i->index, state); 139453a5a1b3Sopenharmony_ci} 139553a5a1b3Sopenharmony_ci 139653a5a1b3Sopenharmony_ci/* Called from main context. */ 139753a5a1b3Sopenharmony_cistatic void source_output_kill_cb(pa_source_output *o) { 139853a5a1b3Sopenharmony_ci struct userdata *u; 139953a5a1b3Sopenharmony_ci 140053a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 140153a5a1b3Sopenharmony_ci pa_assert_ctl_context(); 140253a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 140353a5a1b3Sopenharmony_ci 140453a5a1b3Sopenharmony_ci u->dead = true; 140553a5a1b3Sopenharmony_ci 140653a5a1b3Sopenharmony_ci /* The order here matters! We first kill the source so that streams can 140753a5a1b3Sopenharmony_ci * properly be moved away while the source output is still connected to 140853a5a1b3Sopenharmony_ci * the master. */ 140953a5a1b3Sopenharmony_ci pa_source_output_cork(u->source_output, true); 141053a5a1b3Sopenharmony_ci pa_source_unlink(u->source); 141153a5a1b3Sopenharmony_ci pa_source_output_unlink(u->source_output); 141253a5a1b3Sopenharmony_ci 141353a5a1b3Sopenharmony_ci pa_source_output_unref(u->source_output); 141453a5a1b3Sopenharmony_ci u->source_output = NULL; 141553a5a1b3Sopenharmony_ci 141653a5a1b3Sopenharmony_ci pa_source_unref(u->source); 141753a5a1b3Sopenharmony_ci u->source = NULL; 141853a5a1b3Sopenharmony_ci 141953a5a1b3Sopenharmony_ci pa_log_debug("Source output kill %d", o->index); 142053a5a1b3Sopenharmony_ci 142153a5a1b3Sopenharmony_ci pa_module_unload_request(u->module, true); 142253a5a1b3Sopenharmony_ci} 142353a5a1b3Sopenharmony_ci 142453a5a1b3Sopenharmony_ci/* Called from main context */ 142553a5a1b3Sopenharmony_cistatic void sink_input_kill_cb(pa_sink_input *i) { 142653a5a1b3Sopenharmony_ci struct userdata *u; 142753a5a1b3Sopenharmony_ci 142853a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 142953a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 143053a5a1b3Sopenharmony_ci 143153a5a1b3Sopenharmony_ci u->dead = true; 143253a5a1b3Sopenharmony_ci 143353a5a1b3Sopenharmony_ci /* The order here matters! We first kill the sink so that streams 143453a5a1b3Sopenharmony_ci * can properly be moved away while the sink input is still connected 143553a5a1b3Sopenharmony_ci * to the master. */ 143653a5a1b3Sopenharmony_ci pa_sink_input_cork(u->sink_input, true); 143753a5a1b3Sopenharmony_ci pa_sink_unlink(u->sink); 143853a5a1b3Sopenharmony_ci pa_sink_input_unlink(u->sink_input); 143953a5a1b3Sopenharmony_ci 144053a5a1b3Sopenharmony_ci pa_sink_input_unref(u->sink_input); 144153a5a1b3Sopenharmony_ci u->sink_input = NULL; 144253a5a1b3Sopenharmony_ci 144353a5a1b3Sopenharmony_ci pa_sink_unref(u->sink); 144453a5a1b3Sopenharmony_ci u->sink = NULL; 144553a5a1b3Sopenharmony_ci 144653a5a1b3Sopenharmony_ci pa_log_debug("Sink input kill %d", i->index); 144753a5a1b3Sopenharmony_ci 144853a5a1b3Sopenharmony_ci pa_module_unload_request(u->module, true); 144953a5a1b3Sopenharmony_ci} 145053a5a1b3Sopenharmony_ci 145153a5a1b3Sopenharmony_ci/* Called from main context. */ 145253a5a1b3Sopenharmony_cistatic bool source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) { 145353a5a1b3Sopenharmony_ci struct userdata *u; 145453a5a1b3Sopenharmony_ci 145553a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 145653a5a1b3Sopenharmony_ci pa_assert_ctl_context(); 145753a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 145853a5a1b3Sopenharmony_ci 145953a5a1b3Sopenharmony_ci if (u->dead) 146053a5a1b3Sopenharmony_ci return false; 146153a5a1b3Sopenharmony_ci 146253a5a1b3Sopenharmony_ci return (u->source != dest) && (u->sink != dest->monitor_of); 146353a5a1b3Sopenharmony_ci} 146453a5a1b3Sopenharmony_ci 146553a5a1b3Sopenharmony_ci/* Called from main context */ 146653a5a1b3Sopenharmony_cistatic bool sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { 146753a5a1b3Sopenharmony_ci struct userdata *u; 146853a5a1b3Sopenharmony_ci 146953a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 147053a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 147153a5a1b3Sopenharmony_ci 147253a5a1b3Sopenharmony_ci if (u->dead) 147353a5a1b3Sopenharmony_ci return false; 147453a5a1b3Sopenharmony_ci 147553a5a1b3Sopenharmony_ci return u->sink != dest; 147653a5a1b3Sopenharmony_ci} 147753a5a1b3Sopenharmony_ci 147853a5a1b3Sopenharmony_ci/* Called from main context. */ 147953a5a1b3Sopenharmony_cistatic void source_output_moving_cb(pa_source_output *o, pa_source *dest) { 148053a5a1b3Sopenharmony_ci struct userdata *u; 148153a5a1b3Sopenharmony_ci uint32_t idx; 148253a5a1b3Sopenharmony_ci pa_source_output *output; 148353a5a1b3Sopenharmony_ci 148453a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 148553a5a1b3Sopenharmony_ci pa_assert_ctl_context(); 148653a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 148753a5a1b3Sopenharmony_ci 148853a5a1b3Sopenharmony_ci if (dest) { 148953a5a1b3Sopenharmony_ci pa_source_set_asyncmsgq(u->source, dest->asyncmsgq); 149053a5a1b3Sopenharmony_ci pa_source_update_flags(u->source, PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY, dest->flags); 149153a5a1b3Sopenharmony_ci } else 149253a5a1b3Sopenharmony_ci pa_source_set_asyncmsgq(u->source, NULL); 149353a5a1b3Sopenharmony_ci 149453a5a1b3Sopenharmony_ci /* Propagate asyncmsq change to attached virtual sources */ 149553a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(output, u->source->outputs, idx) { 149653a5a1b3Sopenharmony_ci if (output->destination_source && output->moving) 149753a5a1b3Sopenharmony_ci output->moving(output, u->source); 149853a5a1b3Sopenharmony_ci } 149953a5a1b3Sopenharmony_ci 150053a5a1b3Sopenharmony_ci if (u->source_auto_desc && dest) { 150153a5a1b3Sopenharmony_ci const char *y, *z; 150253a5a1b3Sopenharmony_ci pa_proplist *pl; 150353a5a1b3Sopenharmony_ci 150453a5a1b3Sopenharmony_ci pl = pa_proplist_new(); 150553a5a1b3Sopenharmony_ci if (u->sink_input->sink) { 150653a5a1b3Sopenharmony_ci pa_proplist_sets(pl, PA_PROP_DEVICE_MASTER_DEVICE, u->sink_input->sink->name); 150753a5a1b3Sopenharmony_ci y = pa_proplist_gets(u->sink_input->sink->proplist, PA_PROP_DEVICE_DESCRIPTION); 150853a5a1b3Sopenharmony_ci } else 150953a5a1b3Sopenharmony_ci y = "<unknown>"; /* Probably in the middle of a move */ 151053a5a1b3Sopenharmony_ci z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION); 151153a5a1b3Sopenharmony_ci pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "%s (echo cancelled with %s)", z ? z : dest->name, 151253a5a1b3Sopenharmony_ci y ? y : u->sink_input->sink->name); 151353a5a1b3Sopenharmony_ci 151453a5a1b3Sopenharmony_ci pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, pl); 151553a5a1b3Sopenharmony_ci pa_proplist_free(pl); 151653a5a1b3Sopenharmony_ci } 151753a5a1b3Sopenharmony_ci} 151853a5a1b3Sopenharmony_ci 151953a5a1b3Sopenharmony_ci/* Called from main context */ 152053a5a1b3Sopenharmony_cistatic void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { 152153a5a1b3Sopenharmony_ci struct userdata *u; 152253a5a1b3Sopenharmony_ci 152353a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 152453a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 152553a5a1b3Sopenharmony_ci 152653a5a1b3Sopenharmony_ci if (dest) { 152753a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); 152853a5a1b3Sopenharmony_ci pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); 152953a5a1b3Sopenharmony_ci } else 153053a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, NULL); 153153a5a1b3Sopenharmony_ci 153253a5a1b3Sopenharmony_ci if (u->sink_auto_desc && dest) { 153353a5a1b3Sopenharmony_ci const char *y, *z; 153453a5a1b3Sopenharmony_ci pa_proplist *pl; 153553a5a1b3Sopenharmony_ci 153653a5a1b3Sopenharmony_ci pl = pa_proplist_new(); 153753a5a1b3Sopenharmony_ci if (u->source_output->source) { 153853a5a1b3Sopenharmony_ci pa_proplist_sets(pl, PA_PROP_DEVICE_MASTER_DEVICE, u->source_output->source->name); 153953a5a1b3Sopenharmony_ci y = pa_proplist_gets(u->source_output->source->proplist, PA_PROP_DEVICE_DESCRIPTION); 154053a5a1b3Sopenharmony_ci } else 154153a5a1b3Sopenharmony_ci y = "<unknown>"; /* Probably in the middle of a move */ 154253a5a1b3Sopenharmony_ci z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION); 154353a5a1b3Sopenharmony_ci pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "%s (echo cancelled with %s)", z ? z : dest->name, 154453a5a1b3Sopenharmony_ci y ? y : u->source_output->source->name); 154553a5a1b3Sopenharmony_ci 154653a5a1b3Sopenharmony_ci pa_sink_update_proplist(u->sink, PA_UPDATE_REPLACE, pl); 154753a5a1b3Sopenharmony_ci pa_proplist_free(pl); 154853a5a1b3Sopenharmony_ci } 154953a5a1b3Sopenharmony_ci} 155053a5a1b3Sopenharmony_ci 155153a5a1b3Sopenharmony_ci/* Called from main context */ 155253a5a1b3Sopenharmony_cistatic void sink_input_volume_changed_cb(pa_sink_input *i) { 155353a5a1b3Sopenharmony_ci struct userdata *u; 155453a5a1b3Sopenharmony_ci 155553a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 155653a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 155753a5a1b3Sopenharmony_ci 155853a5a1b3Sopenharmony_ci pa_sink_volume_changed(u->sink, &i->volume); 155953a5a1b3Sopenharmony_ci} 156053a5a1b3Sopenharmony_ci 156153a5a1b3Sopenharmony_ci/* Called from main context */ 156253a5a1b3Sopenharmony_cistatic void sink_input_mute_changed_cb(pa_sink_input *i) { 156353a5a1b3Sopenharmony_ci struct userdata *u; 156453a5a1b3Sopenharmony_ci 156553a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 156653a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 156753a5a1b3Sopenharmony_ci 156853a5a1b3Sopenharmony_ci pa_sink_mute_changed(u->sink, i->muted); 156953a5a1b3Sopenharmony_ci} 157053a5a1b3Sopenharmony_ci 157153a5a1b3Sopenharmony_ci/* Called from main context */ 157253a5a1b3Sopenharmony_cistatic int canceller_process_msg_cb(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { 157353a5a1b3Sopenharmony_ci struct pa_echo_canceller_msg *msg; 157453a5a1b3Sopenharmony_ci struct userdata *u; 157553a5a1b3Sopenharmony_ci 157653a5a1b3Sopenharmony_ci pa_assert(o); 157753a5a1b3Sopenharmony_ci 157853a5a1b3Sopenharmony_ci msg = PA_ECHO_CANCELLER_MSG(o); 157953a5a1b3Sopenharmony_ci 158053a5a1b3Sopenharmony_ci /* When the module is unloaded, there may still remain queued messages for 158153a5a1b3Sopenharmony_ci * the canceller. Messages are sent to the main thread using the master 158253a5a1b3Sopenharmony_ci * source's asyncmsgq, and that message queue isn't (and can't be, at least 158353a5a1b3Sopenharmony_ci * with the current asyncmsgq API) cleared from the canceller messages when 158453a5a1b3Sopenharmony_ci * module-echo-cancel is unloaded. 158553a5a1b3Sopenharmony_ci * 158653a5a1b3Sopenharmony_ci * The userdata may already have been freed at this point, but the 158753a5a1b3Sopenharmony_ci * asyncmsgq holds a reference to the pa_echo_canceller_msg object, which 158853a5a1b3Sopenharmony_ci * contains a flag to indicate that all remaining messages have to be 158953a5a1b3Sopenharmony_ci * ignored. */ 159053a5a1b3Sopenharmony_ci if (msg->dead) 159153a5a1b3Sopenharmony_ci return 0; 159253a5a1b3Sopenharmony_ci 159353a5a1b3Sopenharmony_ci u = msg->userdata; 159453a5a1b3Sopenharmony_ci 159553a5a1b3Sopenharmony_ci switch (code) { 159653a5a1b3Sopenharmony_ci case ECHO_CANCELLER_MESSAGE_SET_VOLUME: { 159753a5a1b3Sopenharmony_ci pa_volume_t v = PA_PTR_TO_UINT(userdata); 159853a5a1b3Sopenharmony_ci pa_cvolume vol; 159953a5a1b3Sopenharmony_ci 160053a5a1b3Sopenharmony_ci if (u->use_volume_sharing) { 160153a5a1b3Sopenharmony_ci pa_cvolume_set(&vol, u->source->sample_spec.channels, v); 160253a5a1b3Sopenharmony_ci pa_source_set_volume(u->source, &vol, true, false); 160353a5a1b3Sopenharmony_ci } else { 160453a5a1b3Sopenharmony_ci pa_cvolume_set(&vol, u->source_output->sample_spec.channels, v); 160553a5a1b3Sopenharmony_ci pa_source_output_set_volume(u->source_output, &vol, false, true); 160653a5a1b3Sopenharmony_ci } 160753a5a1b3Sopenharmony_ci 160853a5a1b3Sopenharmony_ci break; 160953a5a1b3Sopenharmony_ci } 161053a5a1b3Sopenharmony_ci 161153a5a1b3Sopenharmony_ci default: 161253a5a1b3Sopenharmony_ci pa_assert_not_reached(); 161353a5a1b3Sopenharmony_ci break; 161453a5a1b3Sopenharmony_ci } 161553a5a1b3Sopenharmony_ci 161653a5a1b3Sopenharmony_ci return 0; 161753a5a1b3Sopenharmony_ci} 161853a5a1b3Sopenharmony_ci 161953a5a1b3Sopenharmony_ci/* Called by the canceller, so source I/O thread context. */ 162053a5a1b3Sopenharmony_cipa_volume_t pa_echo_canceller_get_capture_volume(pa_echo_canceller *ec) { 162153a5a1b3Sopenharmony_ci#ifndef ECHO_CANCEL_TEST 162253a5a1b3Sopenharmony_ci return pa_cvolume_avg(&ec->msg->userdata->thread_info.current_volume); 162353a5a1b3Sopenharmony_ci#else 162453a5a1b3Sopenharmony_ci return PA_VOLUME_NORM; 162553a5a1b3Sopenharmony_ci#endif 162653a5a1b3Sopenharmony_ci} 162753a5a1b3Sopenharmony_ci 162853a5a1b3Sopenharmony_ci/* Called by the canceller, so source I/O thread context. */ 162953a5a1b3Sopenharmony_civoid pa_echo_canceller_set_capture_volume(pa_echo_canceller *ec, pa_volume_t v) { 163053a5a1b3Sopenharmony_ci#ifndef ECHO_CANCEL_TEST 163153a5a1b3Sopenharmony_ci if (pa_cvolume_avg(&ec->msg->userdata->thread_info.current_volume) != v) { 163253a5a1b3Sopenharmony_ci pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(ec->msg), ECHO_CANCELLER_MESSAGE_SET_VOLUME, PA_UINT_TO_PTR(v), 163353a5a1b3Sopenharmony_ci 0, NULL, NULL); 163453a5a1b3Sopenharmony_ci } 163553a5a1b3Sopenharmony_ci#endif 163653a5a1b3Sopenharmony_ci} 163753a5a1b3Sopenharmony_ci 163853a5a1b3Sopenharmony_ciuint32_t pa_echo_canceller_blocksize_power2(unsigned rate, unsigned ms) { 163953a5a1b3Sopenharmony_ci unsigned nframes = (rate * ms) / 1000; 164053a5a1b3Sopenharmony_ci uint32_t y = 1 << ((8 * sizeof(uint32_t)) - 2); 164153a5a1b3Sopenharmony_ci 164253a5a1b3Sopenharmony_ci pa_assert(rate >= 4000); 164353a5a1b3Sopenharmony_ci pa_assert(ms >= 1); 164453a5a1b3Sopenharmony_ci 164553a5a1b3Sopenharmony_ci /* nframes should be a power of 2, round down to nearest power of two */ 164653a5a1b3Sopenharmony_ci while (y > nframes) 164753a5a1b3Sopenharmony_ci y >>= 1; 164853a5a1b3Sopenharmony_ci 164953a5a1b3Sopenharmony_ci pa_assert(y >= 1); 165053a5a1b3Sopenharmony_ci return y; 165153a5a1b3Sopenharmony_ci} 165253a5a1b3Sopenharmony_ci 165353a5a1b3Sopenharmony_cistatic pa_echo_canceller_method_t get_ec_method_from_string(const char *method) { 165453a5a1b3Sopenharmony_ci if (pa_streq(method, "null")) 165553a5a1b3Sopenharmony_ci return PA_ECHO_CANCELLER_NULL; 165653a5a1b3Sopenharmony_ci#ifdef HAVE_SPEEX 165753a5a1b3Sopenharmony_ci if (pa_streq(method, "speex")) 165853a5a1b3Sopenharmony_ci return PA_ECHO_CANCELLER_SPEEX; 165953a5a1b3Sopenharmony_ci#endif 166053a5a1b3Sopenharmony_ci#ifdef HAVE_ADRIAN_EC 166153a5a1b3Sopenharmony_ci if (pa_streq(method, "adrian")) 166253a5a1b3Sopenharmony_ci return PA_ECHO_CANCELLER_ADRIAN; 166353a5a1b3Sopenharmony_ci#endif 166453a5a1b3Sopenharmony_ci#ifdef HAVE_WEBRTC 166553a5a1b3Sopenharmony_ci if (pa_streq(method, "webrtc")) 166653a5a1b3Sopenharmony_ci return PA_ECHO_CANCELLER_WEBRTC; 166753a5a1b3Sopenharmony_ci#endif 166853a5a1b3Sopenharmony_ci return PA_ECHO_CANCELLER_INVALID; 166953a5a1b3Sopenharmony_ci} 167053a5a1b3Sopenharmony_ci 167153a5a1b3Sopenharmony_ci/* Common initialisation bits between module-echo-cancel and the standalone 167253a5a1b3Sopenharmony_ci * test program. 167353a5a1b3Sopenharmony_ci * 167453a5a1b3Sopenharmony_ci * Called from main context. */ 167553a5a1b3Sopenharmony_cistatic int init_common(pa_modargs *ma, struct userdata *u, pa_sample_spec *source_ss, pa_channel_map *source_map) { 167653a5a1b3Sopenharmony_ci const char *ec_string; 167753a5a1b3Sopenharmony_ci pa_echo_canceller_method_t ec_method; 167853a5a1b3Sopenharmony_ci 167953a5a1b3Sopenharmony_ci if (pa_modargs_get_sample_spec_and_channel_map(ma, source_ss, source_map, PA_CHANNEL_MAP_DEFAULT) < 0) { 168053a5a1b3Sopenharmony_ci pa_log("Invalid sample format specification or channel map"); 168153a5a1b3Sopenharmony_ci goto fail; 168253a5a1b3Sopenharmony_ci } 168353a5a1b3Sopenharmony_ci 168453a5a1b3Sopenharmony_ci u->ec = pa_xnew0(pa_echo_canceller, 1); 168553a5a1b3Sopenharmony_ci if (!u->ec) { 168653a5a1b3Sopenharmony_ci pa_log("Failed to alloc echo canceller"); 168753a5a1b3Sopenharmony_ci goto fail; 168853a5a1b3Sopenharmony_ci } 168953a5a1b3Sopenharmony_ci 169053a5a1b3Sopenharmony_ci ec_string = pa_modargs_get_value(ma, "aec_method", DEFAULT_ECHO_CANCELLER); 169153a5a1b3Sopenharmony_ci if ((ec_method = get_ec_method_from_string(ec_string)) < 0) { 169253a5a1b3Sopenharmony_ci pa_log("Invalid echo canceller implementation '%s'", ec_string); 169353a5a1b3Sopenharmony_ci goto fail; 169453a5a1b3Sopenharmony_ci } 169553a5a1b3Sopenharmony_ci 169653a5a1b3Sopenharmony_ci pa_log_info("Using AEC engine: %s", ec_string); 169753a5a1b3Sopenharmony_ci 169853a5a1b3Sopenharmony_ci u->ec->init = ec_table[ec_method].init; 169953a5a1b3Sopenharmony_ci u->ec->play = ec_table[ec_method].play; 170053a5a1b3Sopenharmony_ci u->ec->record = ec_table[ec_method].record; 170153a5a1b3Sopenharmony_ci u->ec->set_drift = ec_table[ec_method].set_drift; 170253a5a1b3Sopenharmony_ci u->ec->run = ec_table[ec_method].run; 170353a5a1b3Sopenharmony_ci u->ec->done = ec_table[ec_method].done; 170453a5a1b3Sopenharmony_ci 170553a5a1b3Sopenharmony_ci return 0; 170653a5a1b3Sopenharmony_ci 170753a5a1b3Sopenharmony_cifail: 170853a5a1b3Sopenharmony_ci return -1; 170953a5a1b3Sopenharmony_ci} 171053a5a1b3Sopenharmony_ci 171153a5a1b3Sopenharmony_ci/* Called from main context. */ 171253a5a1b3Sopenharmony_ciint pa__init(pa_module*m) { 171353a5a1b3Sopenharmony_ci struct userdata *u; 171453a5a1b3Sopenharmony_ci pa_sample_spec source_output_ss, source_ss, sink_ss; 171553a5a1b3Sopenharmony_ci pa_channel_map source_output_map, source_map, sink_map; 171653a5a1b3Sopenharmony_ci pa_modargs *ma; 171753a5a1b3Sopenharmony_ci pa_source *source_master=NULL; 171853a5a1b3Sopenharmony_ci pa_sink *sink_master=NULL; 171953a5a1b3Sopenharmony_ci bool autoloaded; 172053a5a1b3Sopenharmony_ci pa_source_output_new_data source_output_data; 172153a5a1b3Sopenharmony_ci pa_sink_input_new_data sink_input_data; 172253a5a1b3Sopenharmony_ci pa_source_new_data source_data; 172353a5a1b3Sopenharmony_ci pa_sink_new_data sink_data; 172453a5a1b3Sopenharmony_ci pa_memchunk silence; 172553a5a1b3Sopenharmony_ci uint32_t temp; 172653a5a1b3Sopenharmony_ci uint32_t nframes = 0; 172753a5a1b3Sopenharmony_ci bool use_master_format; 172853a5a1b3Sopenharmony_ci pa_usec_t blocksize_usec; 172953a5a1b3Sopenharmony_ci 173053a5a1b3Sopenharmony_ci pa_assert(m); 173153a5a1b3Sopenharmony_ci 173253a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 173353a5a1b3Sopenharmony_ci pa_log("Failed to parse module arguments."); 173453a5a1b3Sopenharmony_ci goto fail; 173553a5a1b3Sopenharmony_ci } 173653a5a1b3Sopenharmony_ci 173753a5a1b3Sopenharmony_ci if (!(source_master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source_master", NULL), PA_NAMEREG_SOURCE))) { 173853a5a1b3Sopenharmony_ci pa_log("Master source not found"); 173953a5a1b3Sopenharmony_ci goto fail; 174053a5a1b3Sopenharmony_ci } 174153a5a1b3Sopenharmony_ci pa_assert(source_master); 174253a5a1b3Sopenharmony_ci 174353a5a1b3Sopenharmony_ci if (!(sink_master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink_master", NULL), PA_NAMEREG_SINK))) { 174453a5a1b3Sopenharmony_ci pa_log("Master sink not found"); 174553a5a1b3Sopenharmony_ci goto fail; 174653a5a1b3Sopenharmony_ci } 174753a5a1b3Sopenharmony_ci pa_assert(sink_master); 174853a5a1b3Sopenharmony_ci 174953a5a1b3Sopenharmony_ci if (source_master->monitor_of == sink_master) { 175053a5a1b3Sopenharmony_ci pa_log("Can't cancel echo between a sink and its monitor"); 175153a5a1b3Sopenharmony_ci goto fail; 175253a5a1b3Sopenharmony_ci } 175353a5a1b3Sopenharmony_ci 175453a5a1b3Sopenharmony_ci /* Set to true if we just want to inherit sample spec and channel map from the sink and source master */ 175553a5a1b3Sopenharmony_ci use_master_format = DEFAULT_USE_MASTER_FORMAT; 175653a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "use_master_format", &use_master_format) < 0) { 175753a5a1b3Sopenharmony_ci pa_log("use_master_format= expects a boolean argument"); 175853a5a1b3Sopenharmony_ci goto fail; 175953a5a1b3Sopenharmony_ci } 176053a5a1b3Sopenharmony_ci 176153a5a1b3Sopenharmony_ci source_ss = source_master->sample_spec; 176253a5a1b3Sopenharmony_ci sink_ss = sink_master->sample_spec; 176353a5a1b3Sopenharmony_ci 176453a5a1b3Sopenharmony_ci if (use_master_format) { 176553a5a1b3Sopenharmony_ci source_map = source_master->channel_map; 176653a5a1b3Sopenharmony_ci sink_map = sink_master->channel_map; 176753a5a1b3Sopenharmony_ci } else { 176853a5a1b3Sopenharmony_ci source_ss = source_master->sample_spec; 176953a5a1b3Sopenharmony_ci source_ss.rate = DEFAULT_RATE; 177053a5a1b3Sopenharmony_ci source_ss.channels = DEFAULT_CHANNELS; 177153a5a1b3Sopenharmony_ci pa_channel_map_init_auto(&source_map, source_ss.channels, PA_CHANNEL_MAP_DEFAULT); 177253a5a1b3Sopenharmony_ci 177353a5a1b3Sopenharmony_ci sink_ss = sink_master->sample_spec; 177453a5a1b3Sopenharmony_ci sink_ss.rate = DEFAULT_RATE; 177553a5a1b3Sopenharmony_ci sink_ss.channels = DEFAULT_CHANNELS; 177653a5a1b3Sopenharmony_ci pa_channel_map_init_auto(&sink_map, sink_ss.channels, PA_CHANNEL_MAP_DEFAULT); 177753a5a1b3Sopenharmony_ci } 177853a5a1b3Sopenharmony_ci 177953a5a1b3Sopenharmony_ci u = pa_xnew0(struct userdata, 1); 178053a5a1b3Sopenharmony_ci if (!u) { 178153a5a1b3Sopenharmony_ci pa_log("Failed to alloc userdata"); 178253a5a1b3Sopenharmony_ci goto fail; 178353a5a1b3Sopenharmony_ci } 178453a5a1b3Sopenharmony_ci u->core = m->core; 178553a5a1b3Sopenharmony_ci u->module = m; 178653a5a1b3Sopenharmony_ci m->userdata = u; 178753a5a1b3Sopenharmony_ci u->dead = false; 178853a5a1b3Sopenharmony_ci 178953a5a1b3Sopenharmony_ci u->use_volume_sharing = true; 179053a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &u->use_volume_sharing) < 0) { 179153a5a1b3Sopenharmony_ci pa_log("use_volume_sharing= expects a boolean argument"); 179253a5a1b3Sopenharmony_ci goto fail; 179353a5a1b3Sopenharmony_ci } 179453a5a1b3Sopenharmony_ci 179553a5a1b3Sopenharmony_ci temp = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC; 179653a5a1b3Sopenharmony_ci if (pa_modargs_get_value_u32(ma, "adjust_time", &temp) < 0) { 179753a5a1b3Sopenharmony_ci pa_log("Failed to parse adjust_time value"); 179853a5a1b3Sopenharmony_ci goto fail; 179953a5a1b3Sopenharmony_ci } 180053a5a1b3Sopenharmony_ci 180153a5a1b3Sopenharmony_ci if (temp != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC) 180253a5a1b3Sopenharmony_ci u->adjust_time = temp * PA_USEC_PER_SEC; 180353a5a1b3Sopenharmony_ci else 180453a5a1b3Sopenharmony_ci u->adjust_time = DEFAULT_ADJUST_TIME_USEC; 180553a5a1b3Sopenharmony_ci 180653a5a1b3Sopenharmony_ci temp = DEFAULT_ADJUST_TOLERANCE / PA_USEC_PER_MSEC; 180753a5a1b3Sopenharmony_ci if (pa_modargs_get_value_u32(ma, "adjust_threshold", &temp) < 0) { 180853a5a1b3Sopenharmony_ci pa_log("Failed to parse adjust_threshold value"); 180953a5a1b3Sopenharmony_ci goto fail; 181053a5a1b3Sopenharmony_ci } 181153a5a1b3Sopenharmony_ci 181253a5a1b3Sopenharmony_ci if (temp != DEFAULT_ADJUST_TOLERANCE / PA_USEC_PER_MSEC) 181353a5a1b3Sopenharmony_ci u->adjust_threshold = temp * PA_USEC_PER_MSEC; 181453a5a1b3Sopenharmony_ci else 181553a5a1b3Sopenharmony_ci u->adjust_threshold = DEFAULT_ADJUST_TOLERANCE; 181653a5a1b3Sopenharmony_ci 181753a5a1b3Sopenharmony_ci u->save_aec = DEFAULT_SAVE_AEC; 181853a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "save_aec", &u->save_aec) < 0) { 181953a5a1b3Sopenharmony_ci pa_log("Failed to parse save_aec value"); 182053a5a1b3Sopenharmony_ci goto fail; 182153a5a1b3Sopenharmony_ci } 182253a5a1b3Sopenharmony_ci 182353a5a1b3Sopenharmony_ci autoloaded = DEFAULT_AUTOLOADED; 182453a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "autoloaded", &autoloaded) < 0) { 182553a5a1b3Sopenharmony_ci pa_log("Failed to parse autoloaded value"); 182653a5a1b3Sopenharmony_ci goto fail; 182753a5a1b3Sopenharmony_ci } 182853a5a1b3Sopenharmony_ci 182953a5a1b3Sopenharmony_ci if (init_common(ma, u, &source_ss, &source_map) < 0) 183053a5a1b3Sopenharmony_ci goto fail; 183153a5a1b3Sopenharmony_ci 183253a5a1b3Sopenharmony_ci u->asyncmsgq = pa_asyncmsgq_new(0); 183353a5a1b3Sopenharmony_ci if (!u->asyncmsgq) { 183453a5a1b3Sopenharmony_ci pa_log("pa_asyncmsgq_new() failed."); 183553a5a1b3Sopenharmony_ci goto fail; 183653a5a1b3Sopenharmony_ci } 183753a5a1b3Sopenharmony_ci 183853a5a1b3Sopenharmony_ci u->need_realign = true; 183953a5a1b3Sopenharmony_ci 184053a5a1b3Sopenharmony_ci source_output_ss = source_ss; 184153a5a1b3Sopenharmony_ci source_output_map = source_map; 184253a5a1b3Sopenharmony_ci 184353a5a1b3Sopenharmony_ci if (sink_ss.rate != source_ss.rate) { 184453a5a1b3Sopenharmony_ci pa_log_info("Sample rates of play and out stream differ. Adjusting rate of play stream."); 184553a5a1b3Sopenharmony_ci sink_ss.rate = source_ss.rate; 184653a5a1b3Sopenharmony_ci } 184753a5a1b3Sopenharmony_ci 184853a5a1b3Sopenharmony_ci pa_assert(u->ec->init); 184953a5a1b3Sopenharmony_ci if (!u->ec->init(u->core, u->ec, &source_output_ss, &source_output_map, &sink_ss, &sink_map, &source_ss, &source_map, &nframes, pa_modargs_get_value(ma, "aec_args", NULL))) { 185053a5a1b3Sopenharmony_ci pa_log("Failed to init AEC engine"); 185153a5a1b3Sopenharmony_ci goto fail; 185253a5a1b3Sopenharmony_ci } 185353a5a1b3Sopenharmony_ci 185453a5a1b3Sopenharmony_ci pa_assert(source_output_ss.rate == source_ss.rate); 185553a5a1b3Sopenharmony_ci pa_assert(sink_ss.rate == source_ss.rate); 185653a5a1b3Sopenharmony_ci 185753a5a1b3Sopenharmony_ci u->source_output_blocksize = nframes * pa_frame_size(&source_output_ss); 185853a5a1b3Sopenharmony_ci u->source_blocksize = nframes * pa_frame_size(&source_ss); 185953a5a1b3Sopenharmony_ci u->sink_blocksize = nframes * pa_frame_size(&sink_ss); 186053a5a1b3Sopenharmony_ci 186153a5a1b3Sopenharmony_ci if (u->ec->params.drift_compensation) 186253a5a1b3Sopenharmony_ci pa_assert(u->ec->set_drift); 186353a5a1b3Sopenharmony_ci 186453a5a1b3Sopenharmony_ci /* Create source */ 186553a5a1b3Sopenharmony_ci pa_source_new_data_init(&source_data); 186653a5a1b3Sopenharmony_ci source_data.driver = __FILE__; 186753a5a1b3Sopenharmony_ci source_data.module = m; 186853a5a1b3Sopenharmony_ci if (!(source_data.name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL)))) 186953a5a1b3Sopenharmony_ci source_data.name = pa_sprintf_malloc("%s.echo-cancel", source_master->name); 187053a5a1b3Sopenharmony_ci pa_source_new_data_set_sample_spec(&source_data, &source_ss); 187153a5a1b3Sopenharmony_ci pa_source_new_data_set_channel_map(&source_data, &source_map); 187253a5a1b3Sopenharmony_ci pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, source_master->name); 187353a5a1b3Sopenharmony_ci pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); 187453a5a1b3Sopenharmony_ci if (!autoloaded) 187553a5a1b3Sopenharmony_ci pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); 187653a5a1b3Sopenharmony_ci 187753a5a1b3Sopenharmony_ci if (pa_modargs_get_proplist(ma, "source_properties", source_data.proplist, PA_UPDATE_REPLACE) < 0) { 187853a5a1b3Sopenharmony_ci pa_log("Invalid properties"); 187953a5a1b3Sopenharmony_ci pa_source_new_data_done(&source_data); 188053a5a1b3Sopenharmony_ci goto fail; 188153a5a1b3Sopenharmony_ci } 188253a5a1b3Sopenharmony_ci 188353a5a1b3Sopenharmony_ci if ((u->source_auto_desc = !pa_proplist_contains(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) { 188453a5a1b3Sopenharmony_ci const char *y, *z; 188553a5a1b3Sopenharmony_ci 188653a5a1b3Sopenharmony_ci y = pa_proplist_gets(sink_master->proplist, PA_PROP_DEVICE_DESCRIPTION); 188753a5a1b3Sopenharmony_ci z = pa_proplist_gets(source_master->proplist, PA_PROP_DEVICE_DESCRIPTION); 188853a5a1b3Sopenharmony_ci pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s (echo cancelled with %s)", 188953a5a1b3Sopenharmony_ci z ? z : source_master->name, y ? y : sink_master->name); 189053a5a1b3Sopenharmony_ci } 189153a5a1b3Sopenharmony_ci 189253a5a1b3Sopenharmony_ci u->source = pa_source_new(m->core, &source_data, (source_master->flags & (PA_SOURCE_LATENCY | PA_SOURCE_DYNAMIC_LATENCY)) 189353a5a1b3Sopenharmony_ci | (u->use_volume_sharing ? PA_SOURCE_SHARE_VOLUME_WITH_MASTER : 0)); 189453a5a1b3Sopenharmony_ci pa_source_new_data_done(&source_data); 189553a5a1b3Sopenharmony_ci 189653a5a1b3Sopenharmony_ci if (!u->source) { 189753a5a1b3Sopenharmony_ci pa_log("Failed to create source."); 189853a5a1b3Sopenharmony_ci goto fail; 189953a5a1b3Sopenharmony_ci } 190053a5a1b3Sopenharmony_ci 190153a5a1b3Sopenharmony_ci u->source->parent.process_msg = source_process_msg_cb; 190253a5a1b3Sopenharmony_ci u->source->set_state_in_main_thread = source_set_state_in_main_thread_cb; 190353a5a1b3Sopenharmony_ci u->source->update_requested_latency = source_update_requested_latency_cb; 190453a5a1b3Sopenharmony_ci pa_source_set_set_mute_callback(u->source, source_set_mute_cb); 190553a5a1b3Sopenharmony_ci if (!u->use_volume_sharing) { 190653a5a1b3Sopenharmony_ci pa_source_set_get_volume_callback(u->source, source_get_volume_cb); 190753a5a1b3Sopenharmony_ci pa_source_set_set_volume_callback(u->source, source_set_volume_cb); 190853a5a1b3Sopenharmony_ci pa_source_enable_decibel_volume(u->source, true); 190953a5a1b3Sopenharmony_ci } 191053a5a1b3Sopenharmony_ci u->source->userdata = u; 191153a5a1b3Sopenharmony_ci 191253a5a1b3Sopenharmony_ci pa_source_set_asyncmsgq(u->source, source_master->asyncmsgq); 191353a5a1b3Sopenharmony_ci 191453a5a1b3Sopenharmony_ci /* Create sink */ 191553a5a1b3Sopenharmony_ci pa_sink_new_data_init(&sink_data); 191653a5a1b3Sopenharmony_ci sink_data.driver = __FILE__; 191753a5a1b3Sopenharmony_ci sink_data.module = m; 191853a5a1b3Sopenharmony_ci if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL)))) 191953a5a1b3Sopenharmony_ci sink_data.name = pa_sprintf_malloc("%s.echo-cancel", sink_master->name); 192053a5a1b3Sopenharmony_ci pa_sink_new_data_set_sample_spec(&sink_data, &sink_ss); 192153a5a1b3Sopenharmony_ci pa_sink_new_data_set_channel_map(&sink_data, &sink_map); 192253a5a1b3Sopenharmony_ci pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, sink_master->name); 192353a5a1b3Sopenharmony_ci pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); 192453a5a1b3Sopenharmony_ci if (!autoloaded) 192553a5a1b3Sopenharmony_ci pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); 192653a5a1b3Sopenharmony_ci 192753a5a1b3Sopenharmony_ci if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) { 192853a5a1b3Sopenharmony_ci pa_log("Invalid properties"); 192953a5a1b3Sopenharmony_ci pa_sink_new_data_done(&sink_data); 193053a5a1b3Sopenharmony_ci goto fail; 193153a5a1b3Sopenharmony_ci } 193253a5a1b3Sopenharmony_ci 193353a5a1b3Sopenharmony_ci if ((u->sink_auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) { 193453a5a1b3Sopenharmony_ci const char *y, *z; 193553a5a1b3Sopenharmony_ci 193653a5a1b3Sopenharmony_ci y = pa_proplist_gets(source_master->proplist, PA_PROP_DEVICE_DESCRIPTION); 193753a5a1b3Sopenharmony_ci z = pa_proplist_gets(sink_master->proplist, PA_PROP_DEVICE_DESCRIPTION); 193853a5a1b3Sopenharmony_ci pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s (echo cancelled with %s)", 193953a5a1b3Sopenharmony_ci z ? z : sink_master->name, y ? y : source_master->name); 194053a5a1b3Sopenharmony_ci } 194153a5a1b3Sopenharmony_ci 194253a5a1b3Sopenharmony_ci u->sink = pa_sink_new(m->core, &sink_data, (sink_master->flags & (PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY)) 194353a5a1b3Sopenharmony_ci | (u->use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0)); 194453a5a1b3Sopenharmony_ci pa_sink_new_data_done(&sink_data); 194553a5a1b3Sopenharmony_ci 194653a5a1b3Sopenharmony_ci if (!u->sink) { 194753a5a1b3Sopenharmony_ci pa_log("Failed to create sink."); 194853a5a1b3Sopenharmony_ci goto fail; 194953a5a1b3Sopenharmony_ci } 195053a5a1b3Sopenharmony_ci 195153a5a1b3Sopenharmony_ci u->sink->parent.process_msg = sink_process_msg_cb; 195253a5a1b3Sopenharmony_ci u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb; 195353a5a1b3Sopenharmony_ci u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb; 195453a5a1b3Sopenharmony_ci u->sink->update_requested_latency = sink_update_requested_latency_cb; 195553a5a1b3Sopenharmony_ci u->sink->request_rewind = sink_request_rewind_cb; 195653a5a1b3Sopenharmony_ci pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb); 195753a5a1b3Sopenharmony_ci if (!u->use_volume_sharing) { 195853a5a1b3Sopenharmony_ci pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); 195953a5a1b3Sopenharmony_ci pa_sink_enable_decibel_volume(u->sink, true); 196053a5a1b3Sopenharmony_ci } 196153a5a1b3Sopenharmony_ci u->sink->userdata = u; 196253a5a1b3Sopenharmony_ci 196353a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, sink_master->asyncmsgq); 196453a5a1b3Sopenharmony_ci 196553a5a1b3Sopenharmony_ci /* Create source output */ 196653a5a1b3Sopenharmony_ci pa_source_output_new_data_init(&source_output_data); 196753a5a1b3Sopenharmony_ci source_output_data.driver = __FILE__; 196853a5a1b3Sopenharmony_ci source_output_data.module = m; 196953a5a1b3Sopenharmony_ci pa_source_output_new_data_set_source(&source_output_data, source_master, false, true); 197053a5a1b3Sopenharmony_ci source_output_data.destination_source = u->source; 197153a5a1b3Sopenharmony_ci 197253a5a1b3Sopenharmony_ci pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Source Stream"); 197353a5a1b3Sopenharmony_ci pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); 197453a5a1b3Sopenharmony_ci pa_source_output_new_data_set_sample_spec(&source_output_data, &source_output_ss); 197553a5a1b3Sopenharmony_ci pa_source_output_new_data_set_channel_map(&source_output_data, &source_output_map); 197653a5a1b3Sopenharmony_ci source_output_data.flags |= PA_SOURCE_OUTPUT_START_CORKED; 197753a5a1b3Sopenharmony_ci 197853a5a1b3Sopenharmony_ci if (autoloaded) 197953a5a1b3Sopenharmony_ci source_output_data.flags |= PA_SOURCE_OUTPUT_DONT_MOVE; 198053a5a1b3Sopenharmony_ci 198153a5a1b3Sopenharmony_ci pa_source_output_new(&u->source_output, m->core, &source_output_data); 198253a5a1b3Sopenharmony_ci pa_source_output_new_data_done(&source_output_data); 198353a5a1b3Sopenharmony_ci 198453a5a1b3Sopenharmony_ci if (!u->source_output) 198553a5a1b3Sopenharmony_ci goto fail; 198653a5a1b3Sopenharmony_ci 198753a5a1b3Sopenharmony_ci u->source_output->parent.process_msg = source_output_process_msg_cb; 198853a5a1b3Sopenharmony_ci u->source_output->push = source_output_push_cb; 198953a5a1b3Sopenharmony_ci u->source_output->process_rewind = source_output_process_rewind_cb; 199053a5a1b3Sopenharmony_ci u->source_output->update_max_rewind = source_output_update_max_rewind_cb; 199153a5a1b3Sopenharmony_ci u->source_output->update_source_requested_latency = source_output_update_source_requested_latency_cb; 199253a5a1b3Sopenharmony_ci u->source_output->update_source_latency_range = source_output_update_source_latency_range_cb; 199353a5a1b3Sopenharmony_ci u->source_output->update_source_fixed_latency = source_output_update_source_fixed_latency_cb; 199453a5a1b3Sopenharmony_ci u->source_output->kill = source_output_kill_cb; 199553a5a1b3Sopenharmony_ci u->source_output->attach = source_output_attach_cb; 199653a5a1b3Sopenharmony_ci u->source_output->detach = source_output_detach_cb; 199753a5a1b3Sopenharmony_ci u->source_output->state_change = source_output_state_change_cb; 199853a5a1b3Sopenharmony_ci u->source_output->may_move_to = source_output_may_move_to_cb; 199953a5a1b3Sopenharmony_ci u->source_output->moving = source_output_moving_cb; 200053a5a1b3Sopenharmony_ci u->source_output->userdata = u; 200153a5a1b3Sopenharmony_ci 200253a5a1b3Sopenharmony_ci u->source->output_from_master = u->source_output; 200353a5a1b3Sopenharmony_ci 200453a5a1b3Sopenharmony_ci /* Create sink input */ 200553a5a1b3Sopenharmony_ci pa_sink_input_new_data_init(&sink_input_data); 200653a5a1b3Sopenharmony_ci sink_input_data.driver = __FILE__; 200753a5a1b3Sopenharmony_ci sink_input_data.module = m; 200853a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_sink(&sink_input_data, sink_master, false, true); 200953a5a1b3Sopenharmony_ci sink_input_data.origin_sink = u->sink; 201053a5a1b3Sopenharmony_ci pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Sink Stream"); 201153a5a1b3Sopenharmony_ci pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); 201253a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_sample_spec(&sink_input_data, &sink_ss); 201353a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_channel_map(&sink_input_data, &sink_map); 201453a5a1b3Sopenharmony_ci sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE | PA_SINK_INPUT_START_CORKED; 201553a5a1b3Sopenharmony_ci 201653a5a1b3Sopenharmony_ci if (autoloaded) 201753a5a1b3Sopenharmony_ci sink_input_data.flags |= PA_SINK_INPUT_DONT_MOVE; 201853a5a1b3Sopenharmony_ci 201953a5a1b3Sopenharmony_ci pa_sink_input_new(&u->sink_input, m->core, &sink_input_data); 202053a5a1b3Sopenharmony_ci pa_sink_input_new_data_done(&sink_input_data); 202153a5a1b3Sopenharmony_ci 202253a5a1b3Sopenharmony_ci if (!u->sink_input) 202353a5a1b3Sopenharmony_ci goto fail; 202453a5a1b3Sopenharmony_ci 202553a5a1b3Sopenharmony_ci u->sink_input->parent.process_msg = sink_input_process_msg_cb; 202653a5a1b3Sopenharmony_ci u->sink_input->pop = sink_input_pop_cb; 202753a5a1b3Sopenharmony_ci u->sink_input->process_rewind = sink_input_process_rewind_cb; 202853a5a1b3Sopenharmony_ci u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; 202953a5a1b3Sopenharmony_ci u->sink_input->update_max_request = sink_input_update_max_request_cb; 203053a5a1b3Sopenharmony_ci u->sink_input->update_sink_requested_latency = sink_input_update_sink_requested_latency_cb; 203153a5a1b3Sopenharmony_ci u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb; 203253a5a1b3Sopenharmony_ci u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb; 203353a5a1b3Sopenharmony_ci u->sink_input->kill = sink_input_kill_cb; 203453a5a1b3Sopenharmony_ci u->sink_input->attach = sink_input_attach_cb; 203553a5a1b3Sopenharmony_ci u->sink_input->detach = sink_input_detach_cb; 203653a5a1b3Sopenharmony_ci u->sink_input->state_change = sink_input_state_change_cb; 203753a5a1b3Sopenharmony_ci u->sink_input->may_move_to = sink_input_may_move_to_cb; 203853a5a1b3Sopenharmony_ci u->sink_input->moving = sink_input_moving_cb; 203953a5a1b3Sopenharmony_ci if (!u->use_volume_sharing) 204053a5a1b3Sopenharmony_ci u->sink_input->volume_changed = sink_input_volume_changed_cb; 204153a5a1b3Sopenharmony_ci u->sink_input->mute_changed = sink_input_mute_changed_cb; 204253a5a1b3Sopenharmony_ci u->sink_input->userdata = u; 204353a5a1b3Sopenharmony_ci 204453a5a1b3Sopenharmony_ci u->sink->input_to_master = u->sink_input; 204553a5a1b3Sopenharmony_ci 204653a5a1b3Sopenharmony_ci pa_sink_input_get_silence(u->sink_input, &silence); 204753a5a1b3Sopenharmony_ci 204853a5a1b3Sopenharmony_ci u->source_memblockq = pa_memblockq_new("module-echo-cancel source_memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, 204953a5a1b3Sopenharmony_ci &source_output_ss, 1, 1, 0, &silence); 205053a5a1b3Sopenharmony_ci u->sink_memblockq = pa_memblockq_new("module-echo-cancel sink_memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, 205153a5a1b3Sopenharmony_ci &sink_ss, 0, 1, 0, &silence); 205253a5a1b3Sopenharmony_ci 205353a5a1b3Sopenharmony_ci pa_memblock_unref(silence.memblock); 205453a5a1b3Sopenharmony_ci 205553a5a1b3Sopenharmony_ci if (!u->source_memblockq || !u->sink_memblockq) { 205653a5a1b3Sopenharmony_ci pa_log("Failed to create memblockq."); 205753a5a1b3Sopenharmony_ci goto fail; 205853a5a1b3Sopenharmony_ci } 205953a5a1b3Sopenharmony_ci 206053a5a1b3Sopenharmony_ci if (u->adjust_time > 0 && !u->ec->params.drift_compensation) 206153a5a1b3Sopenharmony_ci u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time, time_callback, u); 206253a5a1b3Sopenharmony_ci else if (u->ec->params.drift_compensation) { 206353a5a1b3Sopenharmony_ci pa_log_info("Canceller does drift compensation -- built-in compensation will be disabled"); 206453a5a1b3Sopenharmony_ci u->adjust_time = 0; 206553a5a1b3Sopenharmony_ci /* Perform resync just once to give the canceller a leg up */ 206653a5a1b3Sopenharmony_ci pa_atomic_store(&u->request_resync, 1); 206753a5a1b3Sopenharmony_ci } 206853a5a1b3Sopenharmony_ci 206953a5a1b3Sopenharmony_ci if (u->save_aec) { 207053a5a1b3Sopenharmony_ci pa_log("Creating AEC files in /tmp"); 207153a5a1b3Sopenharmony_ci u->captured_file = fopen("/tmp/aec_rec.sw", "wb"); 207253a5a1b3Sopenharmony_ci if (u->captured_file == NULL) 207353a5a1b3Sopenharmony_ci perror ("fopen failed"); 207453a5a1b3Sopenharmony_ci u->played_file = fopen("/tmp/aec_play.sw", "wb"); 207553a5a1b3Sopenharmony_ci if (u->played_file == NULL) 207653a5a1b3Sopenharmony_ci perror ("fopen failed"); 207753a5a1b3Sopenharmony_ci u->canceled_file = fopen("/tmp/aec_out.sw", "wb"); 207853a5a1b3Sopenharmony_ci if (u->canceled_file == NULL) 207953a5a1b3Sopenharmony_ci perror ("fopen failed"); 208053a5a1b3Sopenharmony_ci if (u->ec->params.drift_compensation) { 208153a5a1b3Sopenharmony_ci u->drift_file = fopen("/tmp/aec_drift.txt", "w"); 208253a5a1b3Sopenharmony_ci if (u->drift_file == NULL) 208353a5a1b3Sopenharmony_ci perror ("fopen failed"); 208453a5a1b3Sopenharmony_ci } 208553a5a1b3Sopenharmony_ci } 208653a5a1b3Sopenharmony_ci 208753a5a1b3Sopenharmony_ci u->ec->msg = pa_msgobject_new(pa_echo_canceller_msg); 208853a5a1b3Sopenharmony_ci u->ec->msg->parent.process_msg = canceller_process_msg_cb; 208953a5a1b3Sopenharmony_ci u->ec->msg->userdata = u; 209053a5a1b3Sopenharmony_ci 209153a5a1b3Sopenharmony_ci u->thread_info.current_volume = u->source->reference_volume; 209253a5a1b3Sopenharmony_ci 209353a5a1b3Sopenharmony_ci /* We don't want to deal with too many chunks at a time */ 209453a5a1b3Sopenharmony_ci blocksize_usec = pa_bytes_to_usec(u->source_blocksize, &u->source->sample_spec); 209553a5a1b3Sopenharmony_ci if (u->source->flags & PA_SOURCE_DYNAMIC_LATENCY) 209653a5a1b3Sopenharmony_ci pa_source_set_latency_range(u->source, blocksize_usec, blocksize_usec * MAX_LATENCY_BLOCKS); 209753a5a1b3Sopenharmony_ci pa_source_output_set_requested_latency(u->source_output, blocksize_usec * MAX_LATENCY_BLOCKS); 209853a5a1b3Sopenharmony_ci 209953a5a1b3Sopenharmony_ci blocksize_usec = pa_bytes_to_usec(u->sink_blocksize, &u->sink->sample_spec); 210053a5a1b3Sopenharmony_ci if (u->sink->flags & PA_SINK_DYNAMIC_LATENCY) 210153a5a1b3Sopenharmony_ci pa_sink_set_latency_range(u->sink, blocksize_usec, blocksize_usec * MAX_LATENCY_BLOCKS); 210253a5a1b3Sopenharmony_ci pa_sink_input_set_requested_latency(u->sink_input, blocksize_usec * MAX_LATENCY_BLOCKS); 210353a5a1b3Sopenharmony_ci 210453a5a1b3Sopenharmony_ci /* The order here is important. The input/output must be put first, 210553a5a1b3Sopenharmony_ci * otherwise streams might attach to the sink/source before the 210653a5a1b3Sopenharmony_ci * sink input or source output is attached to the master. */ 210753a5a1b3Sopenharmony_ci pa_sink_input_put(u->sink_input); 210853a5a1b3Sopenharmony_ci pa_source_output_put(u->source_output); 210953a5a1b3Sopenharmony_ci 211053a5a1b3Sopenharmony_ci pa_sink_put(u->sink); 211153a5a1b3Sopenharmony_ci pa_source_put(u->source); 211253a5a1b3Sopenharmony_ci 211353a5a1b3Sopenharmony_ci pa_source_output_cork(u->source_output, false); 211453a5a1b3Sopenharmony_ci pa_sink_input_cork(u->sink_input, false); 211553a5a1b3Sopenharmony_ci 211653a5a1b3Sopenharmony_ci pa_modargs_free(ma); 211753a5a1b3Sopenharmony_ci 211853a5a1b3Sopenharmony_ci return 0; 211953a5a1b3Sopenharmony_ci 212053a5a1b3Sopenharmony_cifail: 212153a5a1b3Sopenharmony_ci if (ma) 212253a5a1b3Sopenharmony_ci pa_modargs_free(ma); 212353a5a1b3Sopenharmony_ci 212453a5a1b3Sopenharmony_ci pa__done(m); 212553a5a1b3Sopenharmony_ci 212653a5a1b3Sopenharmony_ci return -1; 212753a5a1b3Sopenharmony_ci} 212853a5a1b3Sopenharmony_ci 212953a5a1b3Sopenharmony_ci/* Called from main context. */ 213053a5a1b3Sopenharmony_ciint pa__get_n_used(pa_module *m) { 213153a5a1b3Sopenharmony_ci struct userdata *u; 213253a5a1b3Sopenharmony_ci 213353a5a1b3Sopenharmony_ci pa_assert(m); 213453a5a1b3Sopenharmony_ci pa_assert_se(u = m->userdata); 213553a5a1b3Sopenharmony_ci 213653a5a1b3Sopenharmony_ci return pa_sink_linked_by(u->sink) + pa_source_linked_by(u->source); 213753a5a1b3Sopenharmony_ci} 213853a5a1b3Sopenharmony_ci 213953a5a1b3Sopenharmony_ci/* Called from main context. */ 214053a5a1b3Sopenharmony_civoid pa__done(pa_module*m) { 214153a5a1b3Sopenharmony_ci struct userdata *u; 214253a5a1b3Sopenharmony_ci 214353a5a1b3Sopenharmony_ci pa_assert(m); 214453a5a1b3Sopenharmony_ci 214553a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 214653a5a1b3Sopenharmony_ci return; 214753a5a1b3Sopenharmony_ci 214853a5a1b3Sopenharmony_ci u->dead = true; 214953a5a1b3Sopenharmony_ci 215053a5a1b3Sopenharmony_ci /* See comments in source_output_kill_cb() above regarding 215153a5a1b3Sopenharmony_ci * destruction order! */ 215253a5a1b3Sopenharmony_ci 215353a5a1b3Sopenharmony_ci if (u->time_event) 215453a5a1b3Sopenharmony_ci u->core->mainloop->time_free(u->time_event); 215553a5a1b3Sopenharmony_ci 215653a5a1b3Sopenharmony_ci if (u->source_output) 215753a5a1b3Sopenharmony_ci pa_source_output_cork(u->source_output, true); 215853a5a1b3Sopenharmony_ci if (u->sink_input) 215953a5a1b3Sopenharmony_ci pa_sink_input_cork(u->sink_input, true); 216053a5a1b3Sopenharmony_ci 216153a5a1b3Sopenharmony_ci if (u->source) 216253a5a1b3Sopenharmony_ci pa_source_unlink(u->source); 216353a5a1b3Sopenharmony_ci if (u->sink) 216453a5a1b3Sopenharmony_ci pa_sink_unlink(u->sink); 216553a5a1b3Sopenharmony_ci 216653a5a1b3Sopenharmony_ci if (u->source_output) { 216753a5a1b3Sopenharmony_ci pa_source_output_unlink(u->source_output); 216853a5a1b3Sopenharmony_ci pa_source_output_unref(u->source_output); 216953a5a1b3Sopenharmony_ci } 217053a5a1b3Sopenharmony_ci 217153a5a1b3Sopenharmony_ci if (u->sink_input) { 217253a5a1b3Sopenharmony_ci pa_sink_input_unlink(u->sink_input); 217353a5a1b3Sopenharmony_ci pa_sink_input_unref(u->sink_input); 217453a5a1b3Sopenharmony_ci } 217553a5a1b3Sopenharmony_ci 217653a5a1b3Sopenharmony_ci if (u->source) 217753a5a1b3Sopenharmony_ci pa_source_unref(u->source); 217853a5a1b3Sopenharmony_ci if (u->sink) 217953a5a1b3Sopenharmony_ci pa_sink_unref(u->sink); 218053a5a1b3Sopenharmony_ci 218153a5a1b3Sopenharmony_ci if (u->source_memblockq) 218253a5a1b3Sopenharmony_ci pa_memblockq_free(u->source_memblockq); 218353a5a1b3Sopenharmony_ci if (u->sink_memblockq) 218453a5a1b3Sopenharmony_ci pa_memblockq_free(u->sink_memblockq); 218553a5a1b3Sopenharmony_ci 218653a5a1b3Sopenharmony_ci if (u->ec) { 218753a5a1b3Sopenharmony_ci if (u->ec->done) 218853a5a1b3Sopenharmony_ci u->ec->done(u->ec); 218953a5a1b3Sopenharmony_ci 219053a5a1b3Sopenharmony_ci if (u->ec->msg) { 219153a5a1b3Sopenharmony_ci u->ec->msg->dead = true; 219253a5a1b3Sopenharmony_ci pa_echo_canceller_msg_unref(u->ec->msg); 219353a5a1b3Sopenharmony_ci } 219453a5a1b3Sopenharmony_ci 219553a5a1b3Sopenharmony_ci pa_xfree(u->ec); 219653a5a1b3Sopenharmony_ci } 219753a5a1b3Sopenharmony_ci 219853a5a1b3Sopenharmony_ci if (u->asyncmsgq) 219953a5a1b3Sopenharmony_ci pa_asyncmsgq_unref(u->asyncmsgq); 220053a5a1b3Sopenharmony_ci 220153a5a1b3Sopenharmony_ci if (u->save_aec) { 220253a5a1b3Sopenharmony_ci if (u->played_file) 220353a5a1b3Sopenharmony_ci fclose(u->played_file); 220453a5a1b3Sopenharmony_ci if (u->captured_file) 220553a5a1b3Sopenharmony_ci fclose(u->captured_file); 220653a5a1b3Sopenharmony_ci if (u->canceled_file) 220753a5a1b3Sopenharmony_ci fclose(u->canceled_file); 220853a5a1b3Sopenharmony_ci if (u->drift_file) 220953a5a1b3Sopenharmony_ci fclose(u->drift_file); 221053a5a1b3Sopenharmony_ci } 221153a5a1b3Sopenharmony_ci 221253a5a1b3Sopenharmony_ci pa_xfree(u); 221353a5a1b3Sopenharmony_ci} 221453a5a1b3Sopenharmony_ci 221553a5a1b3Sopenharmony_ci#ifdef ECHO_CANCEL_TEST 221653a5a1b3Sopenharmony_ci/* 221753a5a1b3Sopenharmony_ci * Stand-alone test program for running in the canceller on pre-recorded files. 221853a5a1b3Sopenharmony_ci */ 221953a5a1b3Sopenharmony_ciint main(int argc, char* argv[]) { 222053a5a1b3Sopenharmony_ci struct userdata u; 222153a5a1b3Sopenharmony_ci pa_sample_spec source_output_ss, source_ss, sink_ss; 222253a5a1b3Sopenharmony_ci pa_channel_map source_output_map, source_map, sink_map; 222353a5a1b3Sopenharmony_ci pa_modargs *ma = NULL; 222453a5a1b3Sopenharmony_ci uint8_t *rdata = NULL, *pdata = NULL, *cdata = NULL; 222553a5a1b3Sopenharmony_ci int unused PA_GCC_UNUSED; 222653a5a1b3Sopenharmony_ci int ret = 0, i; 222753a5a1b3Sopenharmony_ci char c; 222853a5a1b3Sopenharmony_ci float drift; 222953a5a1b3Sopenharmony_ci uint32_t nframes; 223053a5a1b3Sopenharmony_ci 223153a5a1b3Sopenharmony_ci if (!getenv("MAKE_CHECK")) 223253a5a1b3Sopenharmony_ci pa_log_set_level(PA_LOG_DEBUG); 223353a5a1b3Sopenharmony_ci 223453a5a1b3Sopenharmony_ci pa_memzero(&u, sizeof(u)); 223553a5a1b3Sopenharmony_ci 223653a5a1b3Sopenharmony_ci if (argc < 4 || argc > 7) { 223753a5a1b3Sopenharmony_ci goto usage; 223853a5a1b3Sopenharmony_ci } 223953a5a1b3Sopenharmony_ci 224053a5a1b3Sopenharmony_ci u.captured_file = fopen(argv[2], "rb"); 224153a5a1b3Sopenharmony_ci if (u.captured_file == NULL) { 224253a5a1b3Sopenharmony_ci perror ("Could not open capture file"); 224353a5a1b3Sopenharmony_ci goto fail; 224453a5a1b3Sopenharmony_ci } 224553a5a1b3Sopenharmony_ci u.played_file = fopen(argv[1], "rb"); 224653a5a1b3Sopenharmony_ci if (u.played_file == NULL) { 224753a5a1b3Sopenharmony_ci perror ("Could not open play file"); 224853a5a1b3Sopenharmony_ci goto fail; 224953a5a1b3Sopenharmony_ci } 225053a5a1b3Sopenharmony_ci u.canceled_file = fopen(argv[3], "wb"); 225153a5a1b3Sopenharmony_ci if (u.canceled_file == NULL) { 225253a5a1b3Sopenharmony_ci perror ("Could not open canceled file"); 225353a5a1b3Sopenharmony_ci goto fail; 225453a5a1b3Sopenharmony_ci } 225553a5a1b3Sopenharmony_ci 225653a5a1b3Sopenharmony_ci u.core = pa_xnew0(pa_core, 1); 225753a5a1b3Sopenharmony_ci u.core->cpu_info.cpu_type = PA_CPU_X86; 225853a5a1b3Sopenharmony_ci u.core->cpu_info.flags.x86 |= PA_CPU_X86_SSE; 225953a5a1b3Sopenharmony_ci 226053a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(argc > 4 ? argv[4] : NULL, valid_modargs))) { 226153a5a1b3Sopenharmony_ci pa_log("Failed to parse module arguments."); 226253a5a1b3Sopenharmony_ci goto fail; 226353a5a1b3Sopenharmony_ci } 226453a5a1b3Sopenharmony_ci 226553a5a1b3Sopenharmony_ci source_ss.format = PA_SAMPLE_FLOAT32LE; 226653a5a1b3Sopenharmony_ci source_ss.rate = DEFAULT_RATE; 226753a5a1b3Sopenharmony_ci source_ss.channels = DEFAULT_CHANNELS; 226853a5a1b3Sopenharmony_ci pa_channel_map_init_auto(&source_map, source_ss.channels, PA_CHANNEL_MAP_DEFAULT); 226953a5a1b3Sopenharmony_ci 227053a5a1b3Sopenharmony_ci sink_ss.format = PA_SAMPLE_FLOAT32LE; 227153a5a1b3Sopenharmony_ci sink_ss.rate = DEFAULT_RATE; 227253a5a1b3Sopenharmony_ci sink_ss.channels = DEFAULT_CHANNELS; 227353a5a1b3Sopenharmony_ci pa_channel_map_init_auto(&sink_map, sink_ss.channels, PA_CHANNEL_MAP_DEFAULT); 227453a5a1b3Sopenharmony_ci 227553a5a1b3Sopenharmony_ci if (init_common(ma, &u, &source_ss, &source_map) < 0) 227653a5a1b3Sopenharmony_ci goto fail; 227753a5a1b3Sopenharmony_ci 227853a5a1b3Sopenharmony_ci source_output_ss = source_ss; 227953a5a1b3Sopenharmony_ci source_output_map = source_map; 228053a5a1b3Sopenharmony_ci 228153a5a1b3Sopenharmony_ci if (!u.ec->init(u.core, u.ec, &source_output_ss, &source_output_map, &sink_ss, &sink_map, &source_ss, &source_map, &nframes, 228253a5a1b3Sopenharmony_ci pa_modargs_get_value(ma, "aec_args", NULL))) { 228353a5a1b3Sopenharmony_ci pa_log("Failed to init AEC engine"); 228453a5a1b3Sopenharmony_ci goto fail; 228553a5a1b3Sopenharmony_ci } 228653a5a1b3Sopenharmony_ci u.source_output_blocksize = nframes * pa_frame_size(&source_output_ss); 228753a5a1b3Sopenharmony_ci u.source_blocksize = nframes * pa_frame_size(&source_ss); 228853a5a1b3Sopenharmony_ci u.sink_blocksize = nframes * pa_frame_size(&sink_ss); 228953a5a1b3Sopenharmony_ci 229053a5a1b3Sopenharmony_ci if (u.ec->params.drift_compensation) { 229153a5a1b3Sopenharmony_ci if (argc < 6) { 229253a5a1b3Sopenharmony_ci pa_log("Drift compensation enabled but drift file not specified"); 229353a5a1b3Sopenharmony_ci goto fail; 229453a5a1b3Sopenharmony_ci } 229553a5a1b3Sopenharmony_ci 229653a5a1b3Sopenharmony_ci u.drift_file = fopen(argv[5], "rt"); 229753a5a1b3Sopenharmony_ci 229853a5a1b3Sopenharmony_ci if (u.drift_file == NULL) { 229953a5a1b3Sopenharmony_ci perror ("Could not open drift file"); 230053a5a1b3Sopenharmony_ci goto fail; 230153a5a1b3Sopenharmony_ci } 230253a5a1b3Sopenharmony_ci } 230353a5a1b3Sopenharmony_ci 230453a5a1b3Sopenharmony_ci rdata = pa_xmalloc(u.source_output_blocksize); 230553a5a1b3Sopenharmony_ci pdata = pa_xmalloc(u.sink_blocksize); 230653a5a1b3Sopenharmony_ci cdata = pa_xmalloc(u.source_blocksize); 230753a5a1b3Sopenharmony_ci 230853a5a1b3Sopenharmony_ci if (!u.ec->params.drift_compensation) { 230953a5a1b3Sopenharmony_ci while (fread(rdata, u.source_output_blocksize, 1, u.captured_file) > 0) { 231053a5a1b3Sopenharmony_ci if (fread(pdata, u.sink_blocksize, 1, u.played_file) == 0) { 231153a5a1b3Sopenharmony_ci perror("Played file ended before captured file"); 231253a5a1b3Sopenharmony_ci goto fail; 231353a5a1b3Sopenharmony_ci } 231453a5a1b3Sopenharmony_ci 231553a5a1b3Sopenharmony_ci u.ec->run(u.ec, rdata, pdata, cdata); 231653a5a1b3Sopenharmony_ci 231753a5a1b3Sopenharmony_ci unused = fwrite(cdata, u.source_blocksize, 1, u.canceled_file); 231853a5a1b3Sopenharmony_ci } 231953a5a1b3Sopenharmony_ci } else { 232053a5a1b3Sopenharmony_ci while (fscanf(u.drift_file, "%c", &c) > 0) { 232153a5a1b3Sopenharmony_ci switch (c) { 232253a5a1b3Sopenharmony_ci case 'd': 232353a5a1b3Sopenharmony_ci if (!fscanf(u.drift_file, "%a", &drift)) { 232453a5a1b3Sopenharmony_ci perror("Drift file incomplete"); 232553a5a1b3Sopenharmony_ci goto fail; 232653a5a1b3Sopenharmony_ci } 232753a5a1b3Sopenharmony_ci 232853a5a1b3Sopenharmony_ci u.ec->set_drift(u.ec, drift); 232953a5a1b3Sopenharmony_ci 233053a5a1b3Sopenharmony_ci break; 233153a5a1b3Sopenharmony_ci 233253a5a1b3Sopenharmony_ci case 'c': 233353a5a1b3Sopenharmony_ci if (!fscanf(u.drift_file, "%d", &i)) { 233453a5a1b3Sopenharmony_ci perror("Drift file incomplete"); 233553a5a1b3Sopenharmony_ci goto fail; 233653a5a1b3Sopenharmony_ci } 233753a5a1b3Sopenharmony_ci 233853a5a1b3Sopenharmony_ci if (fread(rdata, i, 1, u.captured_file) <= 0) { 233953a5a1b3Sopenharmony_ci perror("Captured file ended prematurely"); 234053a5a1b3Sopenharmony_ci goto fail; 234153a5a1b3Sopenharmony_ci } 234253a5a1b3Sopenharmony_ci 234353a5a1b3Sopenharmony_ci u.ec->record(u.ec, rdata, cdata); 234453a5a1b3Sopenharmony_ci 234553a5a1b3Sopenharmony_ci unused = fwrite(cdata, i, 1, u.canceled_file); 234653a5a1b3Sopenharmony_ci 234753a5a1b3Sopenharmony_ci break; 234853a5a1b3Sopenharmony_ci 234953a5a1b3Sopenharmony_ci case 'p': 235053a5a1b3Sopenharmony_ci if (!fscanf(u.drift_file, "%d", &i)) { 235153a5a1b3Sopenharmony_ci perror("Drift file incomplete"); 235253a5a1b3Sopenharmony_ci goto fail; 235353a5a1b3Sopenharmony_ci } 235453a5a1b3Sopenharmony_ci 235553a5a1b3Sopenharmony_ci if (fread(pdata, i, 1, u.played_file) <= 0) { 235653a5a1b3Sopenharmony_ci perror("Played file ended prematurely"); 235753a5a1b3Sopenharmony_ci goto fail; 235853a5a1b3Sopenharmony_ci } 235953a5a1b3Sopenharmony_ci 236053a5a1b3Sopenharmony_ci u.ec->play(u.ec, pdata); 236153a5a1b3Sopenharmony_ci 236253a5a1b3Sopenharmony_ci break; 236353a5a1b3Sopenharmony_ci } 236453a5a1b3Sopenharmony_ci } 236553a5a1b3Sopenharmony_ci 236653a5a1b3Sopenharmony_ci if (fread(rdata, i, 1, u.captured_file) > 0) 236753a5a1b3Sopenharmony_ci pa_log("All capture data was not consumed"); 236853a5a1b3Sopenharmony_ci if (fread(pdata, i, 1, u.played_file) > 0) 236953a5a1b3Sopenharmony_ci pa_log("All playback data was not consumed"); 237053a5a1b3Sopenharmony_ci } 237153a5a1b3Sopenharmony_ci 237253a5a1b3Sopenharmony_ci u.ec->done(u.ec); 237353a5a1b3Sopenharmony_ci u.ec->msg->dead = true; 237453a5a1b3Sopenharmony_ci pa_echo_canceller_msg_unref(u.ec->msg); 237553a5a1b3Sopenharmony_ci 237653a5a1b3Sopenharmony_ciout: 237753a5a1b3Sopenharmony_ci if (u.captured_file) 237853a5a1b3Sopenharmony_ci fclose(u.captured_file); 237953a5a1b3Sopenharmony_ci if (u.played_file) 238053a5a1b3Sopenharmony_ci fclose(u.played_file); 238153a5a1b3Sopenharmony_ci if (u.canceled_file) 238253a5a1b3Sopenharmony_ci fclose(u.canceled_file); 238353a5a1b3Sopenharmony_ci if (u.drift_file) 238453a5a1b3Sopenharmony_ci fclose(u.drift_file); 238553a5a1b3Sopenharmony_ci 238653a5a1b3Sopenharmony_ci pa_xfree(rdata); 238753a5a1b3Sopenharmony_ci pa_xfree(pdata); 238853a5a1b3Sopenharmony_ci pa_xfree(cdata); 238953a5a1b3Sopenharmony_ci 239053a5a1b3Sopenharmony_ci pa_xfree(u.ec); 239153a5a1b3Sopenharmony_ci pa_xfree(u.core); 239253a5a1b3Sopenharmony_ci 239353a5a1b3Sopenharmony_ci if (ma) 239453a5a1b3Sopenharmony_ci pa_modargs_free(ma); 239553a5a1b3Sopenharmony_ci 239653a5a1b3Sopenharmony_ci return ret; 239753a5a1b3Sopenharmony_ci 239853a5a1b3Sopenharmony_ciusage: 239953a5a1b3Sopenharmony_ci pa_log("Usage: %s play_file rec_file out_file [module args] [drift_file]", argv[0]); 240053a5a1b3Sopenharmony_ci 240153a5a1b3Sopenharmony_cifail: 240253a5a1b3Sopenharmony_ci ret = -1; 240353a5a1b3Sopenharmony_ci goto out; 240453a5a1b3Sopenharmony_ci} 240553a5a1b3Sopenharmony_ci#endif /* ECHO_CANCEL_TEST */ 2406