1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2008 Lennart Poettering 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <stdlib.h> 25#include <stdio.h> 26#include <errno.h> 27#include <unistd.h> 28 29#include <pulse/rtclock.h> 30#include <pulse/timeval.h> 31#include <pulse/xmalloc.h> 32 33#include <pulsecore/source.h> 34#include <pulsecore/module.h> 35#include <pulsecore/core-util.h> 36#include <pulsecore/modargs.h> 37#include <pulsecore/log.h> 38#include <pulsecore/thread.h> 39#include <pulsecore/thread-mq.h> 40#include <pulsecore/rtpoll.h> 41 42PA_MODULE_AUTHOR("Lennart Poettering"); 43PA_MODULE_DESCRIPTION("Sine wave generator source"); 44PA_MODULE_VERSION(PACKAGE_VERSION); 45PA_MODULE_LOAD_ONCE(false); 46PA_MODULE_USAGE( 47 "source_name=<name for the source> " 48 "source_properties=<properties for the source> " 49 "rate=<sample rate> " 50 "frequency=<frequency in Hz>"); 51 52#define DEFAULT_SOURCE_NAME "sine_input" 53#define BLOCK_USEC (PA_USEC_PER_SEC * 2) 54 55struct userdata { 56 pa_core *core; 57 pa_module *module; 58 pa_source *source; 59 60 pa_thread *thread; 61 pa_thread_mq thread_mq; 62 pa_rtpoll *rtpoll; 63 64 pa_memchunk memchunk; 65 size_t peek_index; 66 67 pa_usec_t block_usec; /* how much to push at once */ 68 pa_usec_t timestamp; /* when to push next */ 69}; 70 71static const char* const valid_modargs[] = { 72 "source_name", 73 "source_properties", 74 "rate", 75 "frequency", 76 NULL 77}; 78 79static int source_process_msg( 80 pa_msgobject *o, 81 int code, 82 void *data, 83 int64_t offset, 84 pa_memchunk *chunk) { 85 86 struct userdata *u = PA_SOURCE(o)->userdata; 87 88 switch (code) { 89 90 case PA_SOURCE_MESSAGE_GET_LATENCY: { 91 pa_usec_t now, left_to_fill; 92 93 now = pa_rtclock_now(); 94 left_to_fill = u->timestamp > now ? u->timestamp - now : 0ULL; 95 96 *((int64_t*) data) = (int64_t)u->block_usec - left_to_fill; 97 98 return 0; 99 } 100 } 101 102 return pa_source_process_msg(o, code, data, offset, chunk); 103} 104 105/* Called from the IO thread. */ 106static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) { 107 struct userdata *u; 108 109 pa_assert(s); 110 pa_assert_se(u = s->userdata); 111 112 if (new_state == PA_SOURCE_RUNNING) 113 u->timestamp = pa_rtclock_now(); 114 115 return 0; 116} 117 118static void source_update_requested_latency_cb(pa_source *s) { 119 struct userdata *u; 120 121 pa_source_assert_ref(s); 122 pa_assert_se(u = s->userdata); 123 124 u->block_usec = pa_source_get_requested_latency_within_thread(s); 125 126 if (u->block_usec == (pa_usec_t) -1) 127 u->block_usec = s->thread_info.max_latency; 128 129 pa_log_debug("new block msec = %lu", (unsigned long) (u->block_usec / PA_USEC_PER_MSEC)); 130} 131 132static void process_render(struct userdata *u, pa_usec_t now) { 133 pa_assert(u); 134 135 while (u->timestamp < now + u->block_usec) { 136 pa_memchunk chunk; 137 size_t k; 138 139 k = pa_usec_to_bytes_round_up(now + u->block_usec - u->timestamp, &u->source->sample_spec); 140 141 chunk = u->memchunk; 142 chunk.index += u->peek_index; 143 chunk.length = PA_MIN(chunk.length - u->peek_index, k); 144 145/* pa_log_debug("posting %lu", (unsigned long) chunk.length); */ 146 pa_source_post(u->source, &chunk); 147 148 u->peek_index += chunk.length; 149 while (u->peek_index >= u->memchunk.length) 150 u->peek_index -= u->memchunk.length; 151 152 u->timestamp += pa_bytes_to_usec(chunk.length, &u->source->sample_spec); 153 } 154} 155 156static void thread_func(void *userdata) { 157 struct userdata *u = userdata; 158 159 pa_assert(u); 160 161 pa_log_debug("Thread starting up"); 162 163 pa_thread_mq_install(&u->thread_mq); 164 165 u->timestamp = pa_rtclock_now(); 166 167 for (;;) { 168 int ret; 169 170 if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { 171 pa_usec_t now; 172 173 now = pa_rtclock_now(); 174 175 if (u->timestamp <= now) 176 process_render(u, now); 177 178 pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp); 179 } else 180 pa_rtpoll_set_timer_disabled(u->rtpoll); 181 182 /* Hmm, nothing to do. Let's sleep */ 183 if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) 184 goto fail; 185 186 if (ret == 0) 187 goto finish; 188 } 189 190fail: 191 /* If this was no regular exit from the loop we have to continue 192 * processing messages until we received PA_MESSAGE_SHUTDOWN */ 193 pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); 194 pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); 195 196finish: 197 pa_log_debug("Thread shutting down"); 198} 199 200int pa__init(pa_module*m) { 201 struct userdata *u; 202 pa_modargs *ma; 203 pa_source_new_data data; 204 uint32_t frequency; 205 pa_sample_spec ss; 206 207 pa_assert(m); 208 209 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 210 pa_log("failed to parse module arguments."); 211 goto fail; 212 } 213 214 ss.format = PA_SAMPLE_FLOAT32; 215 ss.channels = 1; 216 ss.rate = 44100; 217 218 if (pa_modargs_get_sample_rate(ma, &ss.rate) < 0) { 219 pa_log("Invalid rate specification"); 220 goto fail; 221 } 222 223 frequency = 440; 224 if (pa_modargs_get_value_u32(ma, "frequency", &frequency) < 0 || frequency < 1 || frequency > ss.rate/2) { 225 pa_log("Invalid frequency specification"); 226 goto fail; 227 } 228 229 m->userdata = u = pa_xnew0(struct userdata, 1); 230 u->core = m->core; 231 u->module = m; 232 u->rtpoll = pa_rtpoll_new(); 233 234 if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) { 235 pa_log("pa_thread_mq_init() failed."); 236 goto fail; 237 } 238 239 u->peek_index = 0; 240 pa_memchunk_sine(&u->memchunk, m->core->mempool, ss.rate, frequency); 241 242 pa_source_new_data_init(&data); 243 data.driver = __FILE__; 244 data.module = m; 245 pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME)); 246 pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Sine source at %u Hz", (unsigned) frequency); 247 pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract"); 248 pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency); 249 pa_source_new_data_set_sample_spec(&data, &ss); 250 251 if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { 252 pa_log("Invalid properties"); 253 pa_source_new_data_done(&data); 254 goto fail; 255 } 256 257 u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY); 258 pa_source_new_data_done(&data); 259 260 if (!u->source) { 261 pa_log("Failed to create source."); 262 goto fail; 263 } 264 265 u->source->parent.process_msg = source_process_msg; 266 u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb; 267 u->source->update_requested_latency = source_update_requested_latency_cb; 268 u->source->userdata = u; 269 270 u->block_usec = BLOCK_USEC; 271 272 pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); 273 pa_source_set_rtpoll(u->source, u->rtpoll); 274 pa_source_set_fixed_latency(u->source, u->block_usec); 275 276 if (!(u->thread = pa_thread_new("sine-source", thread_func, u))) { 277 pa_log("Failed to create thread."); 278 goto fail; 279 } 280 281 pa_source_put(u->source); 282 283 pa_modargs_free(ma); 284 285 return 0; 286 287fail: 288 if (ma) 289 pa_modargs_free(ma); 290 291 pa__done(m); 292 293 return -1; 294} 295 296int pa__get_n_used(pa_module *m) { 297 struct userdata *u; 298 299 pa_assert(m); 300 pa_assert_se(u = m->userdata); 301 302 return pa_source_linked_by(u->source); 303} 304 305void pa__done(pa_module*m) { 306 struct userdata *u; 307 308 pa_assert(m); 309 310 if (!(u = m->userdata)) 311 return; 312 313 if (u->source) 314 pa_source_unlink(u->source); 315 316 if (u->thread) { 317 pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); 318 pa_thread_free(u->thread); 319 } 320 321 pa_thread_mq_done(&u->thread_mq); 322 323 if (u->source) 324 pa_source_unref(u->source); 325 326 if (u->memchunk.memblock) 327 pa_memblock_unref(u->memchunk.memblock); 328 329 if (u->rtpoll) 330 pa_rtpoll_free(u->rtpoll); 331 332 pa_xfree(u); 333} 334