<commit_before>/*
 * Copyright (C) 2013-2018 Jolla Ltd.
 *
 * Contact: <NAME> <<EMAIL>>
 *
 * These PulseAudio Modules are free software; you can redistribute
 * it and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation
 * version 2.1 of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
 * USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <signal.h>
#include <stdio.h>

#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif

#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/volume.h>
#include <pulse/xmalloc.h>

#include <pulsecore/core.h>
#include <pulsecore/i18n.h>
#include <pulsecore/module.h>
#include <pulsecore/memchunk.h>
#include <pulsecore/source.h>
#include <pulsecore/modargs.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/time-smoother.h>
#include <pulsecore/resampler.h>

#include "droid-source.h"
#include <droid/droid-util.h>
#include <droid/conversion.h>

struct userdata {
    pa_core *core;
    pa_module *module;
    pa_card *card;
    pa_source *source;

    pa_thread *thread;
    pa_thread_mq thread_mq;
    pa_rtpoll *rtpoll;

    pa_memchunk memchunk;
    audio_devices_t primary_devices;

    size_t source_buffer_size;
    size_t buffer_size;
    pa_usec_t timestamp;

    pa_resampler *resampler;

    pa_droid_card_data *card_data;
    pa_droid_hw_module *hw_module;
    pa_droid_stream *stream;
    bool stream_valid;
};

enum {
    SOURCE_MESSAGE_DO_ROUTING = PA_SOURCE_MESSAGE_MAX
};

#define DEFAULT_MODULE_ID "primary"

#define DROID_AUDIO_SOURCE "droid.audio_source"
#define DROID_AUDIO_SOURCE_UNDEFINED "undefined"

static void userdata_free(struct userdata *u);
static int suspend(struct userdata *u);
static void unsuspend(struct userdata *u);

static int do_routing(struct userdata *u, audio_devices_t devices) {
    int ret;
    audio_devices_t old_device;

    pa_assert(u);
    pa_assert(u->stream);

    if (u->primary_devices == devices)
        pa_log_debug("Refresh active device routing.");

    old_device = u->primary_devices;
    u->primary_devices = devices;

    ret = pa_droid_stream_set_route(u->stream, devices);

    if (ret < 0)
        u->primary_devices = old_device;

    return ret;
}

static bool parse_device_list(const char *str, audio_devices_t *dst) {
    pa_assert(str);
    pa_assert(dst);

    char *dev;
    const char *state = NULL;

    *dst = 0;

    while ((dev = pa_split(str, "|", &state))) {
        audio_devices_t d;

        if (!pa_string_convert_input_device_str_to_num(dev, &d)) {
            pa_log_warn("Unknown device %s", dev);
            pa_xfree(dev);
            return false;
        }

        *dst |= d;

        pa_xfree(dev);
    }

    return true;
}

static int thread_read(struct userdata *u) {
    void *p;
    ssize_t readd;
    pa_memchunk chunk;

    chunk.index = 0;
    chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) u->buffer_size);

    if (!u->stream_valid) {
        /* try to resume or post silence */
        unsuspend(u);
        if (!u->stream_valid) {
            p = pa_memblock_acquire(chunk.memblock);
            chunk.length = pa_memblock_get_length(chunk.memblock);
            pa_silence_memory(p, chunk.length, &u->source->sample_spec);
            pa_source_post(u->source, &chunk);
            pa_memblock_release(chunk.memblock);
            goto end;
        }
    }

    p = pa_memblock_acquire(chunk.memblock);
    readd = pa_droid_stream_read(u->stream, p, pa_memblock_get_length(chunk.memblock));
    pa_memblock_release(chunk.memblock);

    if (readd < 0) {
        pa_log("Failed to read from stream. (err %i)", readd);
        goto end;
    }

    u->timestamp += pa_bytes_to_usec(readd, &u->source->sample_spec);

    chunk.length = readd;

    if (u->resampler) {
        pa_memchunk rchunk;

        pa_resampler_run(u->resampler, &chunk, &rchunk);

        if (rchunk.length > 0)
            pa_source_post(u->source, &rchunk);
        if (rchunk.memblock)
            pa_memblock_unref(rchunk.memblock);

        goto end;
    }

    if (chunk.length > 0)
        pa_source_post(u->source, &chunk);

end:
    pa_memblock_unref(chunk.memblock);

    return 0;
}

static void thread_func(void *userdata) {
    struct userdata *u = userdata;

    pa_assert(u);
    pa_assert(u->stream);

    pa_log_debug("Thread starting up.");

    if (u->core->realtime_scheduling)
        pa_make_realtime(u->core->realtime_priority);

    pa_thread_mq_install(&u->thread_mq);

    u->timestamp = pa_rtclock_now();

    for (;;) {
        int ret;

        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
            thread_read(u);

            pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);
        } else
            pa_rtpoll_set_timer_disabled(u->rtpoll);

        /* Sleep */
#if (PULSEAUDIO_VERSION == 5)
        if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0)
#elif (PULSEAUDIO_VERSION >= 6)
        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
#endif
            goto fail;

        if (ret == 0)
            goto finish;

    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down.");
}

/* Called from IO context */
static int suspend(struct userdata *u) {
    int ret;

    pa_assert(u);
    pa_assert(u->stream);

    ret = pa_droid_stream_suspend(u->stream, true);

    if (ret == 0)
        pa_log_info("Device suspended.");

    return ret;
}

/* Called from IO context */
static void unsuspend(struct userdata *u) {
    pa_assert(u);
    pa_assert(u->stream);

    if (pa_droid_stream_suspend(u->stream, false) >= 0) {
        u->stream_valid = true;
        pa_log_info("Resuming...");
    } else
        u->stream_valid = false;
}

/* Called from IO context */
static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
    struct userdata *u;
    int r;

    pa_assert(s);
    pa_assert_se(u = s->userdata);

    /* It may be that only the suspend cause is changing, in which case there's
     * nothing more to do. */
    if (new_state == s->thread_info.state)
        return 0;

    switch (new_state) {
        case PA_SOURCE_SUSPENDED:
            if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
                if ((r = suspend(u)) < 0)
                    return r;
            }

            break;

        case PA_SOURCE_IDLE:
            /* Fall through */
        case PA_SOURCE_RUNNING:
            if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
                unsuspend(u);
                u->timestamp = pa_rtclock_now();
            }
            break;

        case PA_SOURCE_UNLINKED:
            /* Suspending since some implementations do not want to free running stream. */
            suspend(u);
            break;

        /* not needed */
        case PA_SOURCE_INIT:
        case PA_SOURCE_INVALID_STATE:
            break;
    }

    return 0;
}

/* Called from IO context */
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
    struct userdata *u = PA_SOURCE(o)->userdata;

    switch (code) {
        case SOURCE_MESSAGE_DO_ROUTING: {
            audio_devices_t device = PA_PTR_TO_UINT(data);

            pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));

            suspend(u);
            do_routing(u, device);
            unsuspend(u);
            break;
        }

#if PULSEAUDIO_VERSION < 12
        case PA_SOURCE_MESSAGE_SET_STATE: {
            int r;
            if ((r = source_set_state_in_io_thread_cb(u->source, PA_PTR_TO_UINT(data), 0)) < 0)
                return r;
        }
#endif
    }

    return pa_source_process_msg(o, code, data, offset, chunk);
}

static int source_set_port_cb(pa_source *s, pa_device_port *p) {
    struct userdata *u = s->userdata;
    pa_droid_port_data *data;

    pa_assert(u);
    pa_assert(p);

    data = PA_DEVICE_PORT_DATA(p);

    if (!data->device) {
        /* If there is no device defined, just return 0 to say everything is ok.
         * Then next port change can be whatever source port, even the one enabled
         * before parking. */
        pa_log_debug("Source set port to parking");
        return 0;
    }

    pa_log_debug("Source set port %u", data->device);

    if (!PA_SOURCE_IS_OPENED(pa_source_get_state(u->source)))
        do_routing(u, data->device);
    else {
        pa_asyncmsgq_post(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_DO_ROUTING, PA_UINT_TO_PTR(data->device), 0, NULL, NULL);
    }

    return 0;
}

static void source_set_name(pa_modargs *ma, pa_source_new_data *data, const char *module_id) {
    const char *tmp;

    pa_assert(ma);
    pa_assert(data);
<commit_msg>source: Format debug log line better.<commit_after>/*
 * Copyright (C) 2013-2018 Jolla Ltd.
 *
 * Contact: <NAME> <<EMAIL>>
 *
 * These PulseAudio Modules are free software; you can redistribute
 * it and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation
 * version 2.1 of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
 * USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <signal.h>
#include <stdio.h>

#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif

#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/volume.h>
#include <pulse/xmalloc.h>

#include <pulsecore/core.h>
#include <pulsecore/i18n.h>
#include <pulsecore/module.h>
#include <pulsecore/memchunk.h>
#include <pulsecore/source.h>
#include <pulsecore/modargs.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/time-smoother.h>
#include <pulsecore/resampler.h>

#include "droid-source.h"
#include <droid/droid-util.h>
#include <droid/conversion.h>

struct userdata {
    pa_core *core;
    pa_module *module;
    pa_card *card;
    pa_source *source;

    pa_thread *thread;
    pa_thread_mq thread_mq;
    pa_rtpoll *rtpoll;

    pa_memchunk memchunk;
    audio_devices_t primary_devices;

    size_t source_buffer_size;
    size_t buffer_size;
    pa_usec_t timestamp;

    pa_resampler *resampler;

    pa_droid_card_data *card_data;
    pa_droid_hw_module *hw_module;
    pa_droid_stream *stream;
    bool stream_valid;
};

enum {
    SOURCE_MESSAGE_DO_ROUTING = PA_SOURCE_MESSAGE_MAX
};

#define DEFAULT_MODULE_ID "primary"

#define DROID_AUDIO_SOURCE "droid.audio_source"
#define DROID_AUDIO_SOURCE_UNDEFINED "undefined"

static void userdata_free(struct userdata *u);
static int suspend(struct userdata *u);
static void unsuspend(struct userdata *u);

static int do_routing(struct userdata *u, audio_devices_t devices) {
    int ret;
    audio_devices_t old_device;

    pa_assert(u);
    pa_assert(u->stream);

    if (u->primary_devices == devices)
        pa_log_debug("Refresh active device routing.");

    old_device = u->primary_devices;
    u->primary_devices = devices;

    ret = pa_droid_stream_set_route(u->stream, devices);

    if (ret < 0)
        u->primary_devices = old_device;

    return ret;
}

static bool parse_device_list(const char *str, audio_devices_t *dst) {
    pa_assert(str);
    pa_assert(dst);

    char *dev;
    const char *state = NULL;

    *dst = 0;

    while ((dev = pa_split(str, "|", &state))) {
        audio_devices_t d;

        if (!pa_string_convert_input_device_str_to_num(dev, &d)) {
            pa_log_warn("Unknown device %s", dev);
            pa_xfree(dev);
            return false;
        }

        *dst |= d;

        pa_xfree(dev);
    }

    return true;
}

static int thread_read(struct userdata *u) {
    void *p;
    ssize_t readd;
    pa_memchunk chunk;

    chunk.index = 0;
    chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) u->buffer_size);

    if (!u->stream_valid) {
        /* try to resume or post silence */
        unsuspend(u);
        if (!u->stream_valid) {
            p = pa_memblock_acquire(chunk.memblock);
            chunk.length = pa_memblock_get_length(chunk.memblock);
            pa_silence_memory(p, chunk.length, &u->source->sample_spec);
            pa_source_post(u->source, &chunk);
            pa_memblock_release(chunk.memblock);
            goto end;
        }
    }

    p = pa_memblock_acquire(chunk.memblock);
    readd = pa_droid_stream_read(u->stream, p, pa_memblock_get_length(chunk.memblock));
    pa_memblock_release(chunk.memblock);

    if (readd < 0) {
        pa_log("Failed to read from stream. (err %i)", readd);
        goto end;
    }

    u->timestamp += pa_bytes_to_usec(readd, &u->source->sample_spec);

    chunk.length = readd;

    if (u->resampler) {
        pa_memchunk rchunk;

        pa_resampler_run(u->resampler, &chunk, &rchunk);

        if (rchunk.length > 0)
            pa_source_post(u->source, &rchunk);
        if (rchunk.memblock)
            pa_memblock_unref(rchunk.memblock);

        goto end;
    }

    if (chunk.length > 0)
        pa_source_post(u->source, &chunk);

end:
    pa_memblock_unref(chunk.memblock);

    return 0;
}

static void thread_func(void *userdata) {
    struct userdata *u = userdata;

    pa_assert(u);
    pa_assert(u->stream);

    pa_log_debug("Thread starting up.");

    if (u->core->realtime_scheduling)
        pa_make_realtime(u->core->realtime_priority);

    pa_thread_mq_install(&u->thread_mq);

    u->timestamp = pa_rtclock_now();

    for (;;) {
        int ret;

        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
            thread_read(u);

            pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);
        } else
            pa_rtpoll_set_timer_disabled(u->rtpoll);

        /* Sleep */
#if (PULSEAUDIO_VERSION == 5)
        if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0)
#elif (PULSEAUDIO_VERSION >= 6)
        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
#endif
            goto fail;

        if (ret == 0)
            goto finish;

    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down.");
}

/* Called from IO context */
static int suspend(struct userdata *u) {
    int ret;

    pa_assert(u);
    pa_assert(u->stream);

    ret = pa_droid_stream_suspend(u->stream, true);

    if (ret == 0)
        pa_log_info("Device suspended.");

    return ret;
}

/* Called from IO context */
static void unsuspend(struct userdata *u) {
    pa_assert(u);
    pa_assert(u->stream);

    if (pa_droid_stream_suspend(u->stream, false) >= 0) {
        u->stream_valid = true;
        pa_log_info("Resuming...");
    } else
        u->stream_valid = false;
}

/* Called from IO context */
static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
    struct userdata *u;
    int r;

    pa_assert(s);
    pa_assert_se(u = s->userdata);

    /* It may be that only the suspend cause is changing, in which case there's
     * nothing more to do. */
    if (new_state == s->thread_info.state)
        return 0;

    switch (new_state) {
        case PA_SOURCE_SUSPENDED:
            if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
                if ((r = suspend(u)) < 0)
                    return r;
            }

            break;

        case PA_SOURCE_IDLE:
            /* Fall through */
        case PA_SOURCE_RUNNING:
            if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
                unsuspend(u);
                u->timestamp = pa_rtclock_now();
            }
            break;

        case PA_SOURCE_UNLINKED:
            /* Suspending since some implementations do not want to free running stream. */
            suspend(u);
            break;

        /* not needed */
        case PA_SOURCE_INIT:
        case PA_SOURCE_INVALID_STATE:
            break;
    }

    return 0;
}

/* Called from IO context */
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
    struct userdata *u = PA_SOURCE(o)->userdata;

    switch (code) {
        case SOURCE_MESSAGE_DO_ROUTING: {
            audio_devices_t device = PA_PTR_TO_UINT(data);

            pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));

            suspend(u);
            do_routing(u, device);
            unsuspend(u);
            break;
        }

#if PULSEAUDIO_VERSION < 12
        case PA_SOURCE_MESSAGE_SET_STATE: {
            int r;
            if ((r = source_set_state_in_io_thread_cb(u->source, PA_PTR_TO_UINT(data), 0)) < 0)
                return r;
        }
#endif
    }

    return pa_source_process_msg(o, code, data, offset, chunk);
}

static int source_set_port_cb(pa_source *s, pa_device_port *p) {
    struct userdata *u = s->userdata;
    pa_droid_port_data *data;

    pa_assert(u);
    pa_assert(p);

    data = PA_DEVICE_PORT_DATA(p);

    if (!data->device) {
        /* If there is no device defined, just return 0 to say everything is ok.
         * Then next port change can be whatever source port, even the one enabled
         * before parking. */
        pa_log_debug("Source set port to parking");
        return 0;
    }

    pa_log_debug("Source set port %#010x", data->device);

    if (!PA_SOURCE_IS_OPENED(pa_source_get_state(u->source)))
        do_routing(u, data->device);
    else {
        pa_asyncmsgq_post(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_DO_ROUTING, PA_UINT_TO_PTR(data->device), 0, NULL, NULL);
    }

    return 0;
}

static void source_set_name(pa_modargs *ma, pa_source_new_data *data, const char *module_id) {
    const char *tmp;

    pa_assert(ma);
    pa_assert(data);

{'fraction_non_alphanumeric': 0.11250383710222041, 'fraction_numerical': 0.005371943108564412, 'mean_word_length': 2.8006999805560957, 'pattern_counts': {'":': 0, '<': 75, '<?xml version=': 0, '>': 201, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3186505', 'n_tokens_mistral': 6865, 'n_tokens_neox': 6642, 'n_words': 1543}