153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2006 Lennart Poettering
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
853a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
953a5a1b3Sopenharmony_ci  or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1753a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <stdlib.h>
2553a5a1b3Sopenharmony_ci#include <stdio.h>
2653a5a1b3Sopenharmony_ci#include <errno.h>
2753a5a1b3Sopenharmony_ci#include <string.h>
2853a5a1b3Sopenharmony_ci#include <unistd.h>
2953a5a1b3Sopenharmony_ci
3053a5a1b3Sopenharmony_ci#include <jack/jack.h>
3153a5a1b3Sopenharmony_ci#include <jack/metadata.h>
3253a5a1b3Sopenharmony_ci#include <jack/uuid.h>
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_ci#include <pulse/util.h>
3553a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3653a5a1b3Sopenharmony_ci
3753a5a1b3Sopenharmony_ci#include <pulsecore/sink.h>
3853a5a1b3Sopenharmony_ci#include <pulsecore/module.h>
3953a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
4053a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h>
4153a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
4253a5a1b3Sopenharmony_ci#include <pulsecore/thread.h>
4353a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h>
4453a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h>
4553a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h>
4653a5a1b3Sopenharmony_ci
4753a5a1b3Sopenharmony_ci/* General overview:
4853a5a1b3Sopenharmony_ci *
4953a5a1b3Sopenharmony_ci * Because JACK has a very inflexible event loop management which
5053a5a1b3Sopenharmony_ci * doesn't allow us to add our own event sources to the event thread
5153a5a1b3Sopenharmony_ci * we cannot use the JACK real-time thread for dispatching our PA
5253a5a1b3Sopenharmony_ci * work. Instead, we run an additional RT thread which does most of
5353a5a1b3Sopenharmony_ci * the PA handling, and have the JACK RT thread request data from it
5453a5a1b3Sopenharmony_ci * via pa_asyncmsgq. The cost is an additional context switch which
5553a5a1b3Sopenharmony_ci * should hopefully not be that expensive if RT scheduling is
5653a5a1b3Sopenharmony_ci * enabled. A better fix would only be possible with additional event
5753a5a1b3Sopenharmony_ci * source support in JACK.
5853a5a1b3Sopenharmony_ci */
5953a5a1b3Sopenharmony_ci
6053a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering");
6153a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("JACK Sink");
6253a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false);
6353a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION);
6453a5a1b3Sopenharmony_ciPA_MODULE_USAGE(
6553a5a1b3Sopenharmony_ci        "sink_name=<name for the sink> "
6653a5a1b3Sopenharmony_ci        "sink_properties=<properties for the card> "
6753a5a1b3Sopenharmony_ci        "server_name=<jack server name> "
6853a5a1b3Sopenharmony_ci        "client_name=<jack client name> "
6953a5a1b3Sopenharmony_ci        "channels=<number of channels> "
7053a5a1b3Sopenharmony_ci        "channel_map=<channel map> "
7153a5a1b3Sopenharmony_ci        "connect=<connect ports?>");
7253a5a1b3Sopenharmony_ci
7353a5a1b3Sopenharmony_ci#define DEFAULT_SINK_NAME "jack_out"
7453a5a1b3Sopenharmony_ci#define METADATA_TYPE_INT "http://www.w3.org/2001/XMLSchema#int"
7553a5a1b3Sopenharmony_ci#define METADATA_KEY_ORDER "http://jackaudio.org/metadata/order"
7653a5a1b3Sopenharmony_ci
7753a5a1b3Sopenharmony_cistruct userdata {
7853a5a1b3Sopenharmony_ci    pa_core *core;
7953a5a1b3Sopenharmony_ci    pa_module *module;
8053a5a1b3Sopenharmony_ci    pa_sink *sink;
8153a5a1b3Sopenharmony_ci
8253a5a1b3Sopenharmony_ci    unsigned channels;
8353a5a1b3Sopenharmony_ci
8453a5a1b3Sopenharmony_ci    jack_port_t* port[PA_CHANNELS_MAX];
8553a5a1b3Sopenharmony_ci    jack_client_t *client;
8653a5a1b3Sopenharmony_ci
8753a5a1b3Sopenharmony_ci    void *buffer[PA_CHANNELS_MAX];
8853a5a1b3Sopenharmony_ci
8953a5a1b3Sopenharmony_ci    pa_thread_mq thread_mq;
9053a5a1b3Sopenharmony_ci    pa_asyncmsgq *jack_msgq;
9153a5a1b3Sopenharmony_ci    pa_rtpoll *rtpoll;
9253a5a1b3Sopenharmony_ci    pa_rtpoll_item *rtpoll_item;
9353a5a1b3Sopenharmony_ci
9453a5a1b3Sopenharmony_ci    pa_thread *thread;
9553a5a1b3Sopenharmony_ci
9653a5a1b3Sopenharmony_ci    jack_nframes_t frames_in_buffer;
9753a5a1b3Sopenharmony_ci    jack_nframes_t saved_frame_time;
9853a5a1b3Sopenharmony_ci    bool saved_frame_time_valid;
9953a5a1b3Sopenharmony_ci};
10053a5a1b3Sopenharmony_ci
10153a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = {
10253a5a1b3Sopenharmony_ci    "sink_name",
10353a5a1b3Sopenharmony_ci    "sink_properties",
10453a5a1b3Sopenharmony_ci    "server_name",
10553a5a1b3Sopenharmony_ci    "client_name",
10653a5a1b3Sopenharmony_ci    "channels",
10753a5a1b3Sopenharmony_ci    "channel_map",
10853a5a1b3Sopenharmony_ci    "connect",
10953a5a1b3Sopenharmony_ci    NULL
11053a5a1b3Sopenharmony_ci};
11153a5a1b3Sopenharmony_ci
11253a5a1b3Sopenharmony_cienum {
11353a5a1b3Sopenharmony_ci    SINK_MESSAGE_RENDER = PA_SINK_MESSAGE_MAX,
11453a5a1b3Sopenharmony_ci    SINK_MESSAGE_BUFFER_SIZE,
11553a5a1b3Sopenharmony_ci    SINK_MESSAGE_ON_SHUTDOWN
11653a5a1b3Sopenharmony_ci};
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_cistatic int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *memchunk) {
11953a5a1b3Sopenharmony_ci    struct userdata *u = PA_SINK(o)->userdata;
12053a5a1b3Sopenharmony_ci
12153a5a1b3Sopenharmony_ci    switch (code) {
12253a5a1b3Sopenharmony_ci
12353a5a1b3Sopenharmony_ci        case SINK_MESSAGE_RENDER:
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_ci            /* Handle the request from the JACK thread */
12653a5a1b3Sopenharmony_ci
12753a5a1b3Sopenharmony_ci            if (u->sink->thread_info.state == PA_SINK_RUNNING) {
12853a5a1b3Sopenharmony_ci                pa_memchunk chunk;
12953a5a1b3Sopenharmony_ci                size_t nbytes;
13053a5a1b3Sopenharmony_ci                void *p;
13153a5a1b3Sopenharmony_ci
13253a5a1b3Sopenharmony_ci                pa_assert(offset > 0);
13353a5a1b3Sopenharmony_ci                nbytes = (size_t) offset * pa_frame_size(&u->sink->sample_spec);
13453a5a1b3Sopenharmony_ci
13553a5a1b3Sopenharmony_ci                pa_sink_render_full(u->sink, nbytes, &chunk);
13653a5a1b3Sopenharmony_ci
13753a5a1b3Sopenharmony_ci                p = pa_memblock_acquire_chunk(&chunk);
13853a5a1b3Sopenharmony_ci                pa_deinterleave(p, u->buffer, u->channels, sizeof(float), (unsigned) offset);
13953a5a1b3Sopenharmony_ci                pa_memblock_release(chunk.memblock);
14053a5a1b3Sopenharmony_ci
14153a5a1b3Sopenharmony_ci                pa_memblock_unref(chunk.memblock);
14253a5a1b3Sopenharmony_ci            } else {
14353a5a1b3Sopenharmony_ci                unsigned c;
14453a5a1b3Sopenharmony_ci                pa_sample_spec ss;
14553a5a1b3Sopenharmony_ci
14653a5a1b3Sopenharmony_ci                /* Humm, we're not RUNNING, hence let's write some silence */
14753a5a1b3Sopenharmony_ci                /* This can happen if we're paused, or during shutdown (when we're unlinked but jack is still running). */
14853a5a1b3Sopenharmony_ci
14953a5a1b3Sopenharmony_ci                ss = u->sink->sample_spec;
15053a5a1b3Sopenharmony_ci                ss.channels = 1;
15153a5a1b3Sopenharmony_ci
15253a5a1b3Sopenharmony_ci                for (c = 0; c < u->channels; c++)
15353a5a1b3Sopenharmony_ci                    pa_silence_memory(u->buffer[c], (size_t) offset * pa_sample_size(&ss), &ss);
15453a5a1b3Sopenharmony_ci            }
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_ci            u->frames_in_buffer = (jack_nframes_t) offset;
15753a5a1b3Sopenharmony_ci            u->saved_frame_time = * (jack_nframes_t*) data;
15853a5a1b3Sopenharmony_ci            u->saved_frame_time_valid = true;
15953a5a1b3Sopenharmony_ci
16053a5a1b3Sopenharmony_ci            return 0;
16153a5a1b3Sopenharmony_ci
16253a5a1b3Sopenharmony_ci        case SINK_MESSAGE_BUFFER_SIZE:
16353a5a1b3Sopenharmony_ci            pa_sink_set_max_request_within_thread(u->sink, (size_t) offset * pa_frame_size(&u->sink->sample_spec));
16453a5a1b3Sopenharmony_ci            return 0;
16553a5a1b3Sopenharmony_ci
16653a5a1b3Sopenharmony_ci        case SINK_MESSAGE_ON_SHUTDOWN:
16753a5a1b3Sopenharmony_ci            pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
16853a5a1b3Sopenharmony_ci            return 0;
16953a5a1b3Sopenharmony_ci
17053a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_LATENCY: {
17153a5a1b3Sopenharmony_ci            jack_nframes_t ft, d;
17253a5a1b3Sopenharmony_ci            jack_latency_range_t r;
17353a5a1b3Sopenharmony_ci            size_t n;
17453a5a1b3Sopenharmony_ci            int32_t number_of_frames;
17553a5a1b3Sopenharmony_ci
17653a5a1b3Sopenharmony_ci            /* This is the "worst-case" latency */
17753a5a1b3Sopenharmony_ci            jack_port_get_latency_range(u->port[0], JackPlaybackLatency, &r);
17853a5a1b3Sopenharmony_ci            number_of_frames = r.max + u->frames_in_buffer;
17953a5a1b3Sopenharmony_ci
18053a5a1b3Sopenharmony_ci            if (u->saved_frame_time_valid) {
18153a5a1b3Sopenharmony_ci                /* Adjust the worst case latency by the time that
18253a5a1b3Sopenharmony_ci                 * passed since we last handed data to JACK */
18353a5a1b3Sopenharmony_ci
18453a5a1b3Sopenharmony_ci                ft = jack_frame_time(u->client);
18553a5a1b3Sopenharmony_ci                d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0;
18653a5a1b3Sopenharmony_ci                number_of_frames -= d;
18753a5a1b3Sopenharmony_ci            }
18853a5a1b3Sopenharmony_ci
18953a5a1b3Sopenharmony_ci            /* Convert it to usec */
19053a5a1b3Sopenharmony_ci            if (number_of_frames > 0) {
19153a5a1b3Sopenharmony_ci                n = number_of_frames * pa_frame_size(&u->sink->sample_spec);
19253a5a1b3Sopenharmony_ci                *((int64_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec);
19353a5a1b3Sopenharmony_ci            } else {
19453a5a1b3Sopenharmony_ci                n = - number_of_frames * pa_frame_size(&u->sink->sample_spec);
19553a5a1b3Sopenharmony_ci                *((int64_t*) data) = - (int64_t)pa_bytes_to_usec(n, &u->sink->sample_spec);
19653a5a1b3Sopenharmony_ci            }
19753a5a1b3Sopenharmony_ci
19853a5a1b3Sopenharmony_ci            return 0;
19953a5a1b3Sopenharmony_ci        }
20053a5a1b3Sopenharmony_ci
20153a5a1b3Sopenharmony_ci    }
20253a5a1b3Sopenharmony_ci
20353a5a1b3Sopenharmony_ci    return pa_sink_process_msg(o, code, data, offset, memchunk);
20453a5a1b3Sopenharmony_ci}
20553a5a1b3Sopenharmony_ci
20653a5a1b3Sopenharmony_ci/* JACK Callback: This is called when JACK needs some data */
20753a5a1b3Sopenharmony_cistatic int jack_process(jack_nframes_t nframes, void *arg) {
20853a5a1b3Sopenharmony_ci    struct userdata *u = arg;
20953a5a1b3Sopenharmony_ci    unsigned c;
21053a5a1b3Sopenharmony_ci    jack_nframes_t frame_time;
21153a5a1b3Sopenharmony_ci    pa_assert(u);
21253a5a1b3Sopenharmony_ci
21353a5a1b3Sopenharmony_ci    /* We just forward the request to our other RT thread */
21453a5a1b3Sopenharmony_ci
21553a5a1b3Sopenharmony_ci    for (c = 0; c < u->channels; c++)
21653a5a1b3Sopenharmony_ci        pa_assert_se(u->buffer[c] = jack_port_get_buffer(u->port[c], nframes));
21753a5a1b3Sopenharmony_ci
21853a5a1b3Sopenharmony_ci    frame_time = jack_frame_time(u->client);
21953a5a1b3Sopenharmony_ci
22053a5a1b3Sopenharmony_ci    pa_assert_se(pa_asyncmsgq_send(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RENDER, &frame_time, nframes, NULL) == 0);
22153a5a1b3Sopenharmony_ci    return 0;
22253a5a1b3Sopenharmony_ci}
22353a5a1b3Sopenharmony_ci
22453a5a1b3Sopenharmony_cistatic void thread_func(void *userdata) {
22553a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_ci    pa_assert(u);
22853a5a1b3Sopenharmony_ci
22953a5a1b3Sopenharmony_ci    pa_log_debug("Thread starting up");
23053a5a1b3Sopenharmony_ci
23153a5a1b3Sopenharmony_ci    if (u->core->realtime_scheduling)
23253a5a1b3Sopenharmony_ci        pa_thread_make_realtime(u->core->realtime_priority);
23353a5a1b3Sopenharmony_ci
23453a5a1b3Sopenharmony_ci    pa_thread_mq_install(&u->thread_mq);
23553a5a1b3Sopenharmony_ci
23653a5a1b3Sopenharmony_ci    for (;;) {
23753a5a1b3Sopenharmony_ci        int ret;
23853a5a1b3Sopenharmony_ci
23953a5a1b3Sopenharmony_ci        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
24053a5a1b3Sopenharmony_ci            pa_sink_process_rewind(u->sink, 0);
24153a5a1b3Sopenharmony_ci
24253a5a1b3Sopenharmony_ci        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
24353a5a1b3Sopenharmony_ci            goto fail;
24453a5a1b3Sopenharmony_ci
24553a5a1b3Sopenharmony_ci        if (ret == 0)
24653a5a1b3Sopenharmony_ci            goto finish;
24753a5a1b3Sopenharmony_ci    }
24853a5a1b3Sopenharmony_ci
24953a5a1b3Sopenharmony_cifail:
25053a5a1b3Sopenharmony_ci    /* If this was no regular exit from the loop we have to continue
25153a5a1b3Sopenharmony_ci     * processing messages until we received PA_MESSAGE_SHUTDOWN */
25253a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
25353a5a1b3Sopenharmony_ci    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
25453a5a1b3Sopenharmony_ci
25553a5a1b3Sopenharmony_cifinish:
25653a5a1b3Sopenharmony_ci    pa_log_debug("Thread shutting down");
25753a5a1b3Sopenharmony_ci}
25853a5a1b3Sopenharmony_ci
25953a5a1b3Sopenharmony_ci/* JACK Callback: This is called when JACK triggers an error */
26053a5a1b3Sopenharmony_cistatic void jack_error_func(const char*t) {
26153a5a1b3Sopenharmony_ci    char *s;
26253a5a1b3Sopenharmony_ci
26353a5a1b3Sopenharmony_ci    s = pa_xstrndup(t, strcspn(t, "\n\r"));
26453a5a1b3Sopenharmony_ci    pa_log_warn("JACK error >%s<", s);
26553a5a1b3Sopenharmony_ci    pa_xfree(s);
26653a5a1b3Sopenharmony_ci}
26753a5a1b3Sopenharmony_ci
26853a5a1b3Sopenharmony_ci/* JACK Callback: This is called when JACK is set up */
26953a5a1b3Sopenharmony_cistatic void jack_init(void *arg) {
27053a5a1b3Sopenharmony_ci    struct userdata *u = arg;
27153a5a1b3Sopenharmony_ci
27253a5a1b3Sopenharmony_ci    pa_log_info("JACK thread starting up.");
27353a5a1b3Sopenharmony_ci
27453a5a1b3Sopenharmony_ci    if (u->core->realtime_scheduling)
27553a5a1b3Sopenharmony_ci        pa_thread_make_realtime(u->core->realtime_priority+4);
27653a5a1b3Sopenharmony_ci}
27753a5a1b3Sopenharmony_ci
27853a5a1b3Sopenharmony_ci/* JACK Callback: This is called when JACK kicks us */
27953a5a1b3Sopenharmony_cistatic void jack_shutdown(void* arg) {
28053a5a1b3Sopenharmony_ci    struct userdata *u = arg;
28153a5a1b3Sopenharmony_ci
28253a5a1b3Sopenharmony_ci    pa_log_info("JACK thread shutting down.");
28353a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL);
28453a5a1b3Sopenharmony_ci}
28553a5a1b3Sopenharmony_ci
28653a5a1b3Sopenharmony_ci/* JACK Callback: This is called when JACK changes the buffer size */
28753a5a1b3Sopenharmony_cistatic int jack_buffer_size(jack_nframes_t nframes, void *arg) {
28853a5a1b3Sopenharmony_ci    struct userdata *u = arg;
28953a5a1b3Sopenharmony_ci
29053a5a1b3Sopenharmony_ci    pa_log_info("JACK buffer size changed.");
29153a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_BUFFER_SIZE, NULL, nframes, NULL, NULL);
29253a5a1b3Sopenharmony_ci    return 0;
29353a5a1b3Sopenharmony_ci}
29453a5a1b3Sopenharmony_ci
29553a5a1b3Sopenharmony_ciint pa__init(pa_module*m) {
29653a5a1b3Sopenharmony_ci    struct userdata *u = NULL;
29753a5a1b3Sopenharmony_ci    pa_sample_spec ss;
29853a5a1b3Sopenharmony_ci    pa_channel_map map;
29953a5a1b3Sopenharmony_ci    pa_modargs *ma = NULL;
30053a5a1b3Sopenharmony_ci    jack_status_t status;
30153a5a1b3Sopenharmony_ci    const char *server_name, *client_name;
30253a5a1b3Sopenharmony_ci    uint32_t channels = 0;
30353a5a1b3Sopenharmony_ci    bool do_connect = true;
30453a5a1b3Sopenharmony_ci    unsigned i;
30553a5a1b3Sopenharmony_ci    const char **ports = NULL, **p;
30653a5a1b3Sopenharmony_ci    pa_sink_new_data data;
30753a5a1b3Sopenharmony_ci    jack_latency_range_t r;
30853a5a1b3Sopenharmony_ci    jack_uuid_t port_uuid;
30953a5a1b3Sopenharmony_ci    char port_order[4];
31053a5a1b3Sopenharmony_ci    size_t n;
31153a5a1b3Sopenharmony_ci
31253a5a1b3Sopenharmony_ci    pa_assert(m);
31353a5a1b3Sopenharmony_ci
31453a5a1b3Sopenharmony_ci    jack_set_error_function(jack_error_func);
31553a5a1b3Sopenharmony_ci
31653a5a1b3Sopenharmony_ci    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
31753a5a1b3Sopenharmony_ci        pa_log("Failed to parse module arguments.");
31853a5a1b3Sopenharmony_ci        goto fail;
31953a5a1b3Sopenharmony_ci    }
32053a5a1b3Sopenharmony_ci
32153a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) {
32253a5a1b3Sopenharmony_ci        pa_log("Failed to parse connect= argument.");
32353a5a1b3Sopenharmony_ci        goto fail;
32453a5a1b3Sopenharmony_ci    }
32553a5a1b3Sopenharmony_ci
32653a5a1b3Sopenharmony_ci    server_name = pa_modargs_get_value(ma, "server_name", NULL);
32753a5a1b3Sopenharmony_ci    client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Sink");
32853a5a1b3Sopenharmony_ci
32953a5a1b3Sopenharmony_ci    m->userdata = u = pa_xnew0(struct userdata, 1);
33053a5a1b3Sopenharmony_ci    u->core = m->core;
33153a5a1b3Sopenharmony_ci    u->module = m;
33253a5a1b3Sopenharmony_ci    u->saved_frame_time_valid = false;
33353a5a1b3Sopenharmony_ci    u->rtpoll = pa_rtpoll_new();
33453a5a1b3Sopenharmony_ci
33553a5a1b3Sopenharmony_ci    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
33653a5a1b3Sopenharmony_ci        pa_log("pa_thread_mq_init() failed.");
33753a5a1b3Sopenharmony_ci        goto fail;
33853a5a1b3Sopenharmony_ci    }
33953a5a1b3Sopenharmony_ci
34053a5a1b3Sopenharmony_ci    /* The queue linking the JACK thread and our RT thread */
34153a5a1b3Sopenharmony_ci    u->jack_msgq = pa_asyncmsgq_new(0);
34253a5a1b3Sopenharmony_ci    if (!u->jack_msgq) {
34353a5a1b3Sopenharmony_ci        pa_log("pa_asyncmsgq_new() failed.");
34453a5a1b3Sopenharmony_ci        goto fail;
34553a5a1b3Sopenharmony_ci    }
34653a5a1b3Sopenharmony_ci
34753a5a1b3Sopenharmony_ci    /* The msgq from the JACK RT thread should have an even higher
34853a5a1b3Sopenharmony_ci     * priority than the normal message queues, to match the guarantee
34953a5a1b3Sopenharmony_ci     * all other drivers make: supplying the audio device with data is
35053a5a1b3Sopenharmony_ci     * the top priority -- and as long as that is possible we don't do
35153a5a1b3Sopenharmony_ci     * anything else */
35253a5a1b3Sopenharmony_ci    u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
35353a5a1b3Sopenharmony_ci
35453a5a1b3Sopenharmony_ci    if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
35553a5a1b3Sopenharmony_ci        pa_log("jack_client_open() failed.");
35653a5a1b3Sopenharmony_ci        goto fail;
35753a5a1b3Sopenharmony_ci    }
35853a5a1b3Sopenharmony_ci
35953a5a1b3Sopenharmony_ci    ports = jack_get_ports(u->client, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical|JackPortIsInput);
36053a5a1b3Sopenharmony_ci
36153a5a1b3Sopenharmony_ci    channels = 0;
36253a5a1b3Sopenharmony_ci    if (ports)
36353a5a1b3Sopenharmony_ci        for (p = ports; *p; p++)
36453a5a1b3Sopenharmony_ci            channels++;
36553a5a1b3Sopenharmony_ci
36653a5a1b3Sopenharmony_ci    if (!channels)
36753a5a1b3Sopenharmony_ci        channels = m->core->default_sample_spec.channels;
36853a5a1b3Sopenharmony_ci
36953a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 ||
37053a5a1b3Sopenharmony_ci        !pa_channels_valid(channels)) {
37153a5a1b3Sopenharmony_ci        pa_log("Failed to parse channels= argument.");
37253a5a1b3Sopenharmony_ci        goto fail;
37353a5a1b3Sopenharmony_ci    }
37453a5a1b3Sopenharmony_ci
37553a5a1b3Sopenharmony_ci    if (channels == m->core->default_channel_map.channels)
37653a5a1b3Sopenharmony_ci        map = m->core->default_channel_map;
37753a5a1b3Sopenharmony_ci    else
37853a5a1b3Sopenharmony_ci        pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA);
37953a5a1b3Sopenharmony_ci
38053a5a1b3Sopenharmony_ci    if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) {
38153a5a1b3Sopenharmony_ci        pa_log("Failed to parse channel_map= argument.");
38253a5a1b3Sopenharmony_ci        goto fail;
38353a5a1b3Sopenharmony_ci    }
38453a5a1b3Sopenharmony_ci
38553a5a1b3Sopenharmony_ci    pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client));
38653a5a1b3Sopenharmony_ci
38753a5a1b3Sopenharmony_ci    u->channels = ss.channels = (uint8_t) channels;
38853a5a1b3Sopenharmony_ci    ss.rate = jack_get_sample_rate(u->client);
38953a5a1b3Sopenharmony_ci    ss.format = PA_SAMPLE_FLOAT32NE;
39053a5a1b3Sopenharmony_ci
39153a5a1b3Sopenharmony_ci    pa_assert(pa_sample_spec_valid(&ss));
39253a5a1b3Sopenharmony_ci
39353a5a1b3Sopenharmony_ci    for (i = 0; i < ss.channels; i++) {
39453a5a1b3Sopenharmony_ci        if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0))) {
39553a5a1b3Sopenharmony_ci            pa_log("jack_port_register() failed.");
39653a5a1b3Sopenharmony_ci            goto fail;
39753a5a1b3Sopenharmony_ci        }
39853a5a1b3Sopenharmony_ci
39953a5a1b3Sopenharmony_ci        /* Set order of ports as JACK metadata, if possible. */
40053a5a1b3Sopenharmony_ci        /* See: https://jackaudio.org/api/group__Metadata.html */
40153a5a1b3Sopenharmony_ci        port_uuid = jack_port_uuid(u->port[i]);
40253a5a1b3Sopenharmony_ci
40353a5a1b3Sopenharmony_ci        if (!jack_uuid_empty(port_uuid)) {
40453a5a1b3Sopenharmony_ci            if (snprintf(port_order, 4, "%d", i+1) >= 4)
40553a5a1b3Sopenharmony_ci                pa_log("Port order metadata value > 999 truncated.");
40653a5a1b3Sopenharmony_ci            if (jack_set_property(u->client, port_uuid, METADATA_KEY_ORDER, port_order, METADATA_TYPE_INT) != 0)
40753a5a1b3Sopenharmony_ci                pa_log("jack_set_property() failed.");
40853a5a1b3Sopenharmony_ci        }
40953a5a1b3Sopenharmony_ci    }
41053a5a1b3Sopenharmony_ci
41153a5a1b3Sopenharmony_ci    pa_sink_new_data_init(&data);
41253a5a1b3Sopenharmony_ci    data.driver = __FILE__;
41353a5a1b3Sopenharmony_ci    data.module = m;
41453a5a1b3Sopenharmony_ci    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
41553a5a1b3Sopenharmony_ci    pa_sink_new_data_set_sample_spec(&data, &ss);
41653a5a1b3Sopenharmony_ci    pa_sink_new_data_set_channel_map(&data, &map);
41753a5a1b3Sopenharmony_ci    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
41853a5a1b3Sopenharmony_ci    if (server_name)
41953a5a1b3Sopenharmony_ci        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
42053a5a1b3Sopenharmony_ci    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "JACK sink (%s)", jack_get_client_name(u->client));
42153a5a1b3Sopenharmony_ci    pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
42253a5a1b3Sopenharmony_ci
42353a5a1b3Sopenharmony_ci    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
42453a5a1b3Sopenharmony_ci        pa_log("Invalid properties");
42553a5a1b3Sopenharmony_ci        pa_sink_new_data_done(&data);
42653a5a1b3Sopenharmony_ci        goto fail;
42753a5a1b3Sopenharmony_ci    }
42853a5a1b3Sopenharmony_ci
42953a5a1b3Sopenharmony_ci    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
43053a5a1b3Sopenharmony_ci    pa_sink_new_data_done(&data);
43153a5a1b3Sopenharmony_ci
43253a5a1b3Sopenharmony_ci    if (!u->sink) {
43353a5a1b3Sopenharmony_ci        pa_log("Failed to create sink.");
43453a5a1b3Sopenharmony_ci        goto fail;
43553a5a1b3Sopenharmony_ci    }
43653a5a1b3Sopenharmony_ci
43753a5a1b3Sopenharmony_ci    u->sink->parent.process_msg = sink_process_msg;
43853a5a1b3Sopenharmony_ci    u->sink->userdata = u;
43953a5a1b3Sopenharmony_ci
44053a5a1b3Sopenharmony_ci    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
44153a5a1b3Sopenharmony_ci    pa_sink_set_rtpoll(u->sink, u->rtpoll);
44253a5a1b3Sopenharmony_ci    pa_sink_set_max_request(u->sink, jack_get_buffer_size(u->client) * pa_frame_size(&u->sink->sample_spec));
44353a5a1b3Sopenharmony_ci
44453a5a1b3Sopenharmony_ci    jack_set_process_callback(u->client, jack_process, u);
44553a5a1b3Sopenharmony_ci    jack_on_shutdown(u->client, jack_shutdown, u);
44653a5a1b3Sopenharmony_ci    jack_set_thread_init_callback(u->client, jack_init, u);
44753a5a1b3Sopenharmony_ci    jack_set_buffer_size_callback(u->client, jack_buffer_size, u);
44853a5a1b3Sopenharmony_ci
44953a5a1b3Sopenharmony_ci    if (!(u->thread = pa_thread_new("jack-sink", thread_func, u))) {
45053a5a1b3Sopenharmony_ci        pa_log("Failed to create thread.");
45153a5a1b3Sopenharmony_ci        goto fail;
45253a5a1b3Sopenharmony_ci    }
45353a5a1b3Sopenharmony_ci
45453a5a1b3Sopenharmony_ci    if (jack_activate(u->client)) {
45553a5a1b3Sopenharmony_ci        pa_log("jack_activate() failed");
45653a5a1b3Sopenharmony_ci        goto fail;
45753a5a1b3Sopenharmony_ci    }
45853a5a1b3Sopenharmony_ci
45953a5a1b3Sopenharmony_ci    if (do_connect) {
46053a5a1b3Sopenharmony_ci        for (i = 0, p = ports; i < ss.channels; i++, p++) {
46153a5a1b3Sopenharmony_ci
46253a5a1b3Sopenharmony_ci            if (!p || !*p) {
46353a5a1b3Sopenharmony_ci                pa_log("Not enough physical output ports, leaving unconnected.");
46453a5a1b3Sopenharmony_ci                break;
46553a5a1b3Sopenharmony_ci            }
46653a5a1b3Sopenharmony_ci
46753a5a1b3Sopenharmony_ci            pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p);
46853a5a1b3Sopenharmony_ci
46953a5a1b3Sopenharmony_ci            if (jack_connect(u->client, jack_port_name(u->port[i]), *p)) {
47053a5a1b3Sopenharmony_ci                pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p);
47153a5a1b3Sopenharmony_ci                break;
47253a5a1b3Sopenharmony_ci            }
47353a5a1b3Sopenharmony_ci        }
47453a5a1b3Sopenharmony_ci    }
47553a5a1b3Sopenharmony_ci
47653a5a1b3Sopenharmony_ci    jack_port_get_latency_range(u->port[0], JackPlaybackLatency, &r);
47753a5a1b3Sopenharmony_ci    n = r.max * pa_frame_size(&u->sink->sample_spec);
47853a5a1b3Sopenharmony_ci    pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(n, &u->sink->sample_spec));
47953a5a1b3Sopenharmony_ci    pa_sink_put(u->sink);
48053a5a1b3Sopenharmony_ci
48153a5a1b3Sopenharmony_ci    if (ports)
48253a5a1b3Sopenharmony_ci        jack_free(ports);
48353a5a1b3Sopenharmony_ci    pa_modargs_free(ma);
48453a5a1b3Sopenharmony_ci
48553a5a1b3Sopenharmony_ci    return 0;
48653a5a1b3Sopenharmony_ci
48753a5a1b3Sopenharmony_cifail:
48853a5a1b3Sopenharmony_ci    if (ma)
48953a5a1b3Sopenharmony_ci        pa_modargs_free(ma);
49053a5a1b3Sopenharmony_ci
49153a5a1b3Sopenharmony_ci    if (ports)
49253a5a1b3Sopenharmony_ci        jack_free(ports);
49353a5a1b3Sopenharmony_ci
49453a5a1b3Sopenharmony_ci    pa__done(m);
49553a5a1b3Sopenharmony_ci
49653a5a1b3Sopenharmony_ci    return -1;
49753a5a1b3Sopenharmony_ci}
49853a5a1b3Sopenharmony_ci
49953a5a1b3Sopenharmony_ciint pa__get_n_used(pa_module *m) {
50053a5a1b3Sopenharmony_ci    struct userdata *u;
50153a5a1b3Sopenharmony_ci
50253a5a1b3Sopenharmony_ci    pa_assert(m);
50353a5a1b3Sopenharmony_ci    pa_assert_se(u = m->userdata);
50453a5a1b3Sopenharmony_ci
50553a5a1b3Sopenharmony_ci    return pa_sink_linked_by(u->sink);
50653a5a1b3Sopenharmony_ci}
50753a5a1b3Sopenharmony_ci
50853a5a1b3Sopenharmony_civoid pa__done(pa_module*m) {
50953a5a1b3Sopenharmony_ci    struct userdata *u;
51053a5a1b3Sopenharmony_ci
51153a5a1b3Sopenharmony_ci    pa_assert(m);
51253a5a1b3Sopenharmony_ci
51353a5a1b3Sopenharmony_ci    if (!(u = m->userdata))
51453a5a1b3Sopenharmony_ci        return;
51553a5a1b3Sopenharmony_ci
51653a5a1b3Sopenharmony_ci    if (u->sink)
51753a5a1b3Sopenharmony_ci        pa_sink_unlink(u->sink);
51853a5a1b3Sopenharmony_ci
51953a5a1b3Sopenharmony_ci    if (u->client)
52053a5a1b3Sopenharmony_ci        jack_client_close(u->client);
52153a5a1b3Sopenharmony_ci
52253a5a1b3Sopenharmony_ci    if (u->thread) {
52353a5a1b3Sopenharmony_ci        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
52453a5a1b3Sopenharmony_ci        pa_thread_free(u->thread);
52553a5a1b3Sopenharmony_ci    }
52653a5a1b3Sopenharmony_ci
52753a5a1b3Sopenharmony_ci    pa_thread_mq_done(&u->thread_mq);
52853a5a1b3Sopenharmony_ci
52953a5a1b3Sopenharmony_ci    if (u->sink)
53053a5a1b3Sopenharmony_ci        pa_sink_unref(u->sink);
53153a5a1b3Sopenharmony_ci
53253a5a1b3Sopenharmony_ci    if (u->rtpoll_item)
53353a5a1b3Sopenharmony_ci        pa_rtpoll_item_free(u->rtpoll_item);
53453a5a1b3Sopenharmony_ci
53553a5a1b3Sopenharmony_ci    if (u->jack_msgq)
53653a5a1b3Sopenharmony_ci        pa_asyncmsgq_unref(u->jack_msgq);
53753a5a1b3Sopenharmony_ci
53853a5a1b3Sopenharmony_ci    if (u->rtpoll)
53953a5a1b3Sopenharmony_ci        pa_rtpoll_free(u->rtpoll);
54053a5a1b3Sopenharmony_ci
54153a5a1b3Sopenharmony_ci    pa_xfree(u);
54253a5a1b3Sopenharmony_ci}
543