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