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