153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2008 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 <unistd.h>
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#include <pulse/rtclock.h>
3053a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
3153a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3253a5a1b3Sopenharmony_ci
3353a5a1b3Sopenharmony_ci#include <pulsecore/source.h>
3453a5a1b3Sopenharmony_ci#include <pulsecore/module.h>
3553a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
3653a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h>
3753a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
3853a5a1b3Sopenharmony_ci#include <pulsecore/thread.h>
3953a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h>
4053a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h>
4153a5a1b3Sopenharmony_ci
4253a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering");
4353a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Sine wave generator source");
4453a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION);
4553a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false);
4653a5a1b3Sopenharmony_ciPA_MODULE_USAGE(
4753a5a1b3Sopenharmony_ci        "source_name=<name for the source> "
4853a5a1b3Sopenharmony_ci        "source_properties=<properties for the source> "
4953a5a1b3Sopenharmony_ci        "rate=<sample rate> "
5053a5a1b3Sopenharmony_ci        "frequency=<frequency in Hz>");
5153a5a1b3Sopenharmony_ci
5253a5a1b3Sopenharmony_ci#define DEFAULT_SOURCE_NAME "sine_input"
5353a5a1b3Sopenharmony_ci#define BLOCK_USEC (PA_USEC_PER_SEC * 2)
5453a5a1b3Sopenharmony_ci
5553a5a1b3Sopenharmony_cistruct userdata {
5653a5a1b3Sopenharmony_ci    pa_core *core;
5753a5a1b3Sopenharmony_ci    pa_module *module;
5853a5a1b3Sopenharmony_ci    pa_source *source;
5953a5a1b3Sopenharmony_ci
6053a5a1b3Sopenharmony_ci    pa_thread *thread;
6153a5a1b3Sopenharmony_ci    pa_thread_mq thread_mq;
6253a5a1b3Sopenharmony_ci    pa_rtpoll *rtpoll;
6353a5a1b3Sopenharmony_ci
6453a5a1b3Sopenharmony_ci    pa_memchunk memchunk;
6553a5a1b3Sopenharmony_ci    size_t peek_index;
6653a5a1b3Sopenharmony_ci
6753a5a1b3Sopenharmony_ci    pa_usec_t block_usec; /* how much to push at once */
6853a5a1b3Sopenharmony_ci    pa_usec_t timestamp;  /* when to push next */
6953a5a1b3Sopenharmony_ci};
7053a5a1b3Sopenharmony_ci
7153a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = {
7253a5a1b3Sopenharmony_ci    "source_name",
7353a5a1b3Sopenharmony_ci    "source_properties",
7453a5a1b3Sopenharmony_ci    "rate",
7553a5a1b3Sopenharmony_ci    "frequency",
7653a5a1b3Sopenharmony_ci    NULL
7753a5a1b3Sopenharmony_ci};
7853a5a1b3Sopenharmony_ci
7953a5a1b3Sopenharmony_cistatic int source_process_msg(
8053a5a1b3Sopenharmony_ci        pa_msgobject *o,
8153a5a1b3Sopenharmony_ci        int code,
8253a5a1b3Sopenharmony_ci        void *data,
8353a5a1b3Sopenharmony_ci        int64_t offset,
8453a5a1b3Sopenharmony_ci        pa_memchunk *chunk) {
8553a5a1b3Sopenharmony_ci
8653a5a1b3Sopenharmony_ci    struct userdata *u = PA_SOURCE(o)->userdata;
8753a5a1b3Sopenharmony_ci
8853a5a1b3Sopenharmony_ci    switch (code) {
8953a5a1b3Sopenharmony_ci
9053a5a1b3Sopenharmony_ci        case PA_SOURCE_MESSAGE_GET_LATENCY: {
9153a5a1b3Sopenharmony_ci            pa_usec_t now, left_to_fill;
9253a5a1b3Sopenharmony_ci
9353a5a1b3Sopenharmony_ci            now = pa_rtclock_now();
9453a5a1b3Sopenharmony_ci            left_to_fill = u->timestamp > now ? u->timestamp - now : 0ULL;
9553a5a1b3Sopenharmony_ci
9653a5a1b3Sopenharmony_ci            *((int64_t*) data) = (int64_t)u->block_usec - left_to_fill;
9753a5a1b3Sopenharmony_ci
9853a5a1b3Sopenharmony_ci            return 0;
9953a5a1b3Sopenharmony_ci        }
10053a5a1b3Sopenharmony_ci    }
10153a5a1b3Sopenharmony_ci
10253a5a1b3Sopenharmony_ci    return pa_source_process_msg(o, code, data, offset, chunk);
10353a5a1b3Sopenharmony_ci}
10453a5a1b3Sopenharmony_ci
10553a5a1b3Sopenharmony_ci/* Called from the IO thread. */
10653a5a1b3Sopenharmony_cistatic int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
10753a5a1b3Sopenharmony_ci    struct userdata *u;
10853a5a1b3Sopenharmony_ci
10953a5a1b3Sopenharmony_ci    pa_assert(s);
11053a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
11153a5a1b3Sopenharmony_ci
11253a5a1b3Sopenharmony_ci    if (new_state == PA_SOURCE_RUNNING)
11353a5a1b3Sopenharmony_ci        u->timestamp = pa_rtclock_now();
11453a5a1b3Sopenharmony_ci
11553a5a1b3Sopenharmony_ci    return 0;
11653a5a1b3Sopenharmony_ci}
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_cistatic void source_update_requested_latency_cb(pa_source *s) {
11953a5a1b3Sopenharmony_ci    struct userdata *u;
12053a5a1b3Sopenharmony_ci
12153a5a1b3Sopenharmony_ci    pa_source_assert_ref(s);
12253a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_ci    u->block_usec = pa_source_get_requested_latency_within_thread(s);
12553a5a1b3Sopenharmony_ci
12653a5a1b3Sopenharmony_ci    if (u->block_usec == (pa_usec_t) -1)
12753a5a1b3Sopenharmony_ci        u->block_usec = s->thread_info.max_latency;
12853a5a1b3Sopenharmony_ci
12953a5a1b3Sopenharmony_ci    pa_log_debug("new block msec = %lu", (unsigned long) (u->block_usec / PA_USEC_PER_MSEC));
13053a5a1b3Sopenharmony_ci}
13153a5a1b3Sopenharmony_ci
13253a5a1b3Sopenharmony_cistatic void process_render(struct userdata *u, pa_usec_t now) {
13353a5a1b3Sopenharmony_ci    pa_assert(u);
13453a5a1b3Sopenharmony_ci
13553a5a1b3Sopenharmony_ci    while (u->timestamp < now + u->block_usec) {
13653a5a1b3Sopenharmony_ci        pa_memchunk chunk;
13753a5a1b3Sopenharmony_ci        size_t k;
13853a5a1b3Sopenharmony_ci
13953a5a1b3Sopenharmony_ci        k = pa_usec_to_bytes_round_up(now + u->block_usec - u->timestamp, &u->source->sample_spec);
14053a5a1b3Sopenharmony_ci
14153a5a1b3Sopenharmony_ci        chunk = u->memchunk;
14253a5a1b3Sopenharmony_ci        chunk.index += u->peek_index;
14353a5a1b3Sopenharmony_ci        chunk.length = PA_MIN(chunk.length - u->peek_index, k);
14453a5a1b3Sopenharmony_ci
14553a5a1b3Sopenharmony_ci/*         pa_log_debug("posting %lu", (unsigned long) chunk.length); */
14653a5a1b3Sopenharmony_ci        pa_source_post(u->source, &chunk);
14753a5a1b3Sopenharmony_ci
14853a5a1b3Sopenharmony_ci        u->peek_index += chunk.length;
14953a5a1b3Sopenharmony_ci        while (u->peek_index >= u->memchunk.length)
15053a5a1b3Sopenharmony_ci            u->peek_index -= u->memchunk.length;
15153a5a1b3Sopenharmony_ci
15253a5a1b3Sopenharmony_ci        u->timestamp += pa_bytes_to_usec(chunk.length, &u->source->sample_spec);
15353a5a1b3Sopenharmony_ci    }
15453a5a1b3Sopenharmony_ci}
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_cistatic void thread_func(void *userdata) {
15753a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
15853a5a1b3Sopenharmony_ci
15953a5a1b3Sopenharmony_ci    pa_assert(u);
16053a5a1b3Sopenharmony_ci
16153a5a1b3Sopenharmony_ci    pa_log_debug("Thread starting up");
16253a5a1b3Sopenharmony_ci
16353a5a1b3Sopenharmony_ci    pa_thread_mq_install(&u->thread_mq);
16453a5a1b3Sopenharmony_ci
16553a5a1b3Sopenharmony_ci    u->timestamp = pa_rtclock_now();
16653a5a1b3Sopenharmony_ci
16753a5a1b3Sopenharmony_ci    for (;;) {
16853a5a1b3Sopenharmony_ci        int ret;
16953a5a1b3Sopenharmony_ci
17053a5a1b3Sopenharmony_ci        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
17153a5a1b3Sopenharmony_ci            pa_usec_t now;
17253a5a1b3Sopenharmony_ci
17353a5a1b3Sopenharmony_ci            now = pa_rtclock_now();
17453a5a1b3Sopenharmony_ci
17553a5a1b3Sopenharmony_ci            if (u->timestamp <= now)
17653a5a1b3Sopenharmony_ci                process_render(u, now);
17753a5a1b3Sopenharmony_ci
17853a5a1b3Sopenharmony_ci            pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);
17953a5a1b3Sopenharmony_ci        } else
18053a5a1b3Sopenharmony_ci            pa_rtpoll_set_timer_disabled(u->rtpoll);
18153a5a1b3Sopenharmony_ci
18253a5a1b3Sopenharmony_ci        /* Hmm, nothing to do. Let's sleep */
18353a5a1b3Sopenharmony_ci        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
18453a5a1b3Sopenharmony_ci            goto fail;
18553a5a1b3Sopenharmony_ci
18653a5a1b3Sopenharmony_ci        if (ret == 0)
18753a5a1b3Sopenharmony_ci            goto finish;
18853a5a1b3Sopenharmony_ci    }
18953a5a1b3Sopenharmony_ci
19053a5a1b3Sopenharmony_cifail:
19153a5a1b3Sopenharmony_ci    /* If this was no regular exit from the loop we have to continue
19253a5a1b3Sopenharmony_ci     * processing messages until we received PA_MESSAGE_SHUTDOWN */
19353a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
19453a5a1b3Sopenharmony_ci    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
19553a5a1b3Sopenharmony_ci
19653a5a1b3Sopenharmony_cifinish:
19753a5a1b3Sopenharmony_ci    pa_log_debug("Thread shutting down");
19853a5a1b3Sopenharmony_ci}
19953a5a1b3Sopenharmony_ci
20053a5a1b3Sopenharmony_ciint pa__init(pa_module*m) {
20153a5a1b3Sopenharmony_ci    struct userdata *u;
20253a5a1b3Sopenharmony_ci    pa_modargs *ma;
20353a5a1b3Sopenharmony_ci    pa_source_new_data data;
20453a5a1b3Sopenharmony_ci    uint32_t frequency;
20553a5a1b3Sopenharmony_ci    pa_sample_spec ss;
20653a5a1b3Sopenharmony_ci
20753a5a1b3Sopenharmony_ci    pa_assert(m);
20853a5a1b3Sopenharmony_ci
20953a5a1b3Sopenharmony_ci    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
21053a5a1b3Sopenharmony_ci        pa_log("failed to parse module arguments.");
21153a5a1b3Sopenharmony_ci        goto fail;
21253a5a1b3Sopenharmony_ci    }
21353a5a1b3Sopenharmony_ci
21453a5a1b3Sopenharmony_ci    ss.format = PA_SAMPLE_FLOAT32;
21553a5a1b3Sopenharmony_ci    ss.channels = 1;
21653a5a1b3Sopenharmony_ci    ss.rate = 44100;
21753a5a1b3Sopenharmony_ci
21853a5a1b3Sopenharmony_ci    if (pa_modargs_get_sample_rate(ma, &ss.rate) < 0) {
21953a5a1b3Sopenharmony_ci        pa_log("Invalid rate specification");
22053a5a1b3Sopenharmony_ci        goto fail;
22153a5a1b3Sopenharmony_ci    }
22253a5a1b3Sopenharmony_ci
22353a5a1b3Sopenharmony_ci    frequency = 440;
22453a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_u32(ma, "frequency", &frequency) < 0 || frequency < 1 || frequency > ss.rate/2) {
22553a5a1b3Sopenharmony_ci        pa_log("Invalid frequency specification");
22653a5a1b3Sopenharmony_ci        goto fail;
22753a5a1b3Sopenharmony_ci    }
22853a5a1b3Sopenharmony_ci
22953a5a1b3Sopenharmony_ci    m->userdata = u = pa_xnew0(struct userdata, 1);
23053a5a1b3Sopenharmony_ci    u->core = m->core;
23153a5a1b3Sopenharmony_ci    u->module = m;
23253a5a1b3Sopenharmony_ci    u->rtpoll = pa_rtpoll_new();
23353a5a1b3Sopenharmony_ci
23453a5a1b3Sopenharmony_ci    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
23553a5a1b3Sopenharmony_ci        pa_log("pa_thread_mq_init() failed.");
23653a5a1b3Sopenharmony_ci        goto fail;
23753a5a1b3Sopenharmony_ci    }
23853a5a1b3Sopenharmony_ci
23953a5a1b3Sopenharmony_ci    u->peek_index = 0;
24053a5a1b3Sopenharmony_ci    pa_memchunk_sine(&u->memchunk, m->core->mempool, ss.rate, frequency);
24153a5a1b3Sopenharmony_ci
24253a5a1b3Sopenharmony_ci    pa_source_new_data_init(&data);
24353a5a1b3Sopenharmony_ci    data.driver = __FILE__;
24453a5a1b3Sopenharmony_ci    data.module = m;
24553a5a1b3Sopenharmony_ci    pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
24653a5a1b3Sopenharmony_ci    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Sine source at %u Hz", (unsigned) frequency);
24753a5a1b3Sopenharmony_ci    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");
24853a5a1b3Sopenharmony_ci    pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);
24953a5a1b3Sopenharmony_ci    pa_source_new_data_set_sample_spec(&data, &ss);
25053a5a1b3Sopenharmony_ci
25153a5a1b3Sopenharmony_ci    if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
25253a5a1b3Sopenharmony_ci        pa_log("Invalid properties");
25353a5a1b3Sopenharmony_ci        pa_source_new_data_done(&data);
25453a5a1b3Sopenharmony_ci        goto fail;
25553a5a1b3Sopenharmony_ci    }
25653a5a1b3Sopenharmony_ci
25753a5a1b3Sopenharmony_ci    u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY);
25853a5a1b3Sopenharmony_ci    pa_source_new_data_done(&data);
25953a5a1b3Sopenharmony_ci
26053a5a1b3Sopenharmony_ci    if (!u->source) {
26153a5a1b3Sopenharmony_ci        pa_log("Failed to create source.");
26253a5a1b3Sopenharmony_ci        goto fail;
26353a5a1b3Sopenharmony_ci    }
26453a5a1b3Sopenharmony_ci
26553a5a1b3Sopenharmony_ci    u->source->parent.process_msg = source_process_msg;
26653a5a1b3Sopenharmony_ci    u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
26753a5a1b3Sopenharmony_ci    u->source->update_requested_latency = source_update_requested_latency_cb;
26853a5a1b3Sopenharmony_ci    u->source->userdata = u;
26953a5a1b3Sopenharmony_ci
27053a5a1b3Sopenharmony_ci    u->block_usec = BLOCK_USEC;
27153a5a1b3Sopenharmony_ci
27253a5a1b3Sopenharmony_ci    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
27353a5a1b3Sopenharmony_ci    pa_source_set_rtpoll(u->source, u->rtpoll);
27453a5a1b3Sopenharmony_ci    pa_source_set_fixed_latency(u->source, u->block_usec);
27553a5a1b3Sopenharmony_ci
27653a5a1b3Sopenharmony_ci    if (!(u->thread = pa_thread_new("sine-source", thread_func, u))) {
27753a5a1b3Sopenharmony_ci        pa_log("Failed to create thread.");
27853a5a1b3Sopenharmony_ci        goto fail;
27953a5a1b3Sopenharmony_ci    }
28053a5a1b3Sopenharmony_ci
28153a5a1b3Sopenharmony_ci    pa_source_put(u->source);
28253a5a1b3Sopenharmony_ci
28353a5a1b3Sopenharmony_ci    pa_modargs_free(ma);
28453a5a1b3Sopenharmony_ci
28553a5a1b3Sopenharmony_ci    return 0;
28653a5a1b3Sopenharmony_ci
28753a5a1b3Sopenharmony_cifail:
28853a5a1b3Sopenharmony_ci    if (ma)
28953a5a1b3Sopenharmony_ci        pa_modargs_free(ma);
29053a5a1b3Sopenharmony_ci
29153a5a1b3Sopenharmony_ci    pa__done(m);
29253a5a1b3Sopenharmony_ci
29353a5a1b3Sopenharmony_ci    return -1;
29453a5a1b3Sopenharmony_ci}
29553a5a1b3Sopenharmony_ci
29653a5a1b3Sopenharmony_ciint pa__get_n_used(pa_module *m) {
29753a5a1b3Sopenharmony_ci    struct userdata *u;
29853a5a1b3Sopenharmony_ci
29953a5a1b3Sopenharmony_ci    pa_assert(m);
30053a5a1b3Sopenharmony_ci    pa_assert_se(u = m->userdata);
30153a5a1b3Sopenharmony_ci
30253a5a1b3Sopenharmony_ci    return pa_source_linked_by(u->source);
30353a5a1b3Sopenharmony_ci}
30453a5a1b3Sopenharmony_ci
30553a5a1b3Sopenharmony_civoid pa__done(pa_module*m) {
30653a5a1b3Sopenharmony_ci    struct userdata *u;
30753a5a1b3Sopenharmony_ci
30853a5a1b3Sopenharmony_ci    pa_assert(m);
30953a5a1b3Sopenharmony_ci
31053a5a1b3Sopenharmony_ci    if (!(u = m->userdata))
31153a5a1b3Sopenharmony_ci        return;
31253a5a1b3Sopenharmony_ci
31353a5a1b3Sopenharmony_ci    if (u->source)
31453a5a1b3Sopenharmony_ci        pa_source_unlink(u->source);
31553a5a1b3Sopenharmony_ci
31653a5a1b3Sopenharmony_ci    if (u->thread) {
31753a5a1b3Sopenharmony_ci        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
31853a5a1b3Sopenharmony_ci        pa_thread_free(u->thread);
31953a5a1b3Sopenharmony_ci    }
32053a5a1b3Sopenharmony_ci
32153a5a1b3Sopenharmony_ci    pa_thread_mq_done(&u->thread_mq);
32253a5a1b3Sopenharmony_ci
32353a5a1b3Sopenharmony_ci    if (u->source)
32453a5a1b3Sopenharmony_ci        pa_source_unref(u->source);
32553a5a1b3Sopenharmony_ci
32653a5a1b3Sopenharmony_ci    if (u->memchunk.memblock)
32753a5a1b3Sopenharmony_ci        pa_memblock_unref(u->memchunk.memblock);
32853a5a1b3Sopenharmony_ci
32953a5a1b3Sopenharmony_ci    if (u->rtpoll)
33053a5a1b3Sopenharmony_ci        pa_rtpoll_free(u->rtpoll);
33153a5a1b3Sopenharmony_ci
33253a5a1b3Sopenharmony_ci    pa_xfree(u);
33353a5a1b3Sopenharmony_ci}
334