1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2004-2006 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 <stdio.h> 25 26#include <pulse/xmalloc.h> 27 28#include <pulsecore/sink-input.h> 29#include <pulsecore/module.h> 30#include <pulsecore/modargs.h> 31#include <pulsecore/namereg.h> 32#include <pulsecore/log.h> 33 34PA_MODULE_AUTHOR("Lennart Poettering"); 35PA_MODULE_DESCRIPTION("Sine wave generator"); 36PA_MODULE_VERSION(PACKAGE_VERSION); 37PA_MODULE_LOAD_ONCE(false); 38PA_MODULE_USAGE( 39 "sink=<sink to connect to> " 40 "rate=<sample rate> " 41 "frequency=<frequency in Hz>"); 42 43struct userdata { 44 pa_core *core; 45 pa_module *module; 46 pa_sink_input *sink_input; 47 pa_memchunk memchunk; 48 size_t peek_index; 49}; 50 51static const char* const valid_modargs[] = { 52 "sink", 53 "rate", 54 "frequency", 55 NULL, 56}; 57 58static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { 59 struct userdata *u; 60 61 pa_sink_input_assert_ref(i); 62 pa_assert_se(u = i->userdata); 63 pa_assert(chunk); 64 65 *chunk = u->memchunk; 66 pa_memblock_ref(chunk->memblock); 67 68 chunk->index += u->peek_index; 69 chunk->length -= u->peek_index; 70 71 u->peek_index = 0; 72 73 return 0; 74} 75 76static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { 77 struct userdata *u; 78 79 pa_sink_input_assert_ref(i); 80 pa_assert_se(u = i->userdata); 81 82 nbytes %= u->memchunk.length; 83 84 if (u->peek_index >= nbytes) 85 u->peek_index -= nbytes; 86 else 87 u->peek_index = u->memchunk.length + u->peek_index - nbytes; 88} 89 90static void sink_input_kill_cb(pa_sink_input *i) { 91 struct userdata *u; 92 93 pa_sink_input_assert_ref(i); 94 pa_assert_se(u = i->userdata); 95 96 pa_sink_input_unlink(u->sink_input); 97 pa_sink_input_unref(u->sink_input); 98 u->sink_input = NULL; 99 100 pa_module_unload_request(u->module, true); 101} 102 103/* Called from IO thread context */ 104static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { 105 struct userdata *u; 106 107 pa_sink_input_assert_ref(i); 108 pa_assert_se(u = i->userdata); 109 110 /* If we are added for the first time, ask for a rewinding so that 111 * we are heard right-away. */ 112 if (PA_SINK_INPUT_IS_LINKED(state) && 113 i->thread_info.state == PA_SINK_INPUT_INIT && i->sink) 114 pa_sink_input_request_rewind(i, 0, false, true, true); 115} 116 117int pa__init(pa_module*m) { 118 pa_modargs *ma = NULL; 119 struct userdata *u; 120 pa_sink *sink; 121 pa_sample_spec ss; 122 uint32_t frequency; 123 pa_sink_input_new_data data; 124 125 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 126 pa_log("Failed to parse module arguments"); 127 goto fail; 128 } 129 130 if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK))) { 131 pa_log("No such sink."); 132 goto fail; 133 } 134 135 ss.format = PA_SAMPLE_FLOAT32; 136 ss.rate = sink->sample_spec.rate; 137 ss.channels = 1; 138 139 if (pa_modargs_get_sample_rate(ma, &ss.rate) < 0) { 140 pa_log("Invalid rate specification"); 141 goto fail; 142 } 143 144 frequency = 440; 145 if (pa_modargs_get_value_u32(ma, "frequency", &frequency) < 0 || frequency < 1 || frequency > ss.rate/2) { 146 pa_log("Invalid frequency specification"); 147 goto fail; 148 } 149 150 m->userdata = u = pa_xnew0(struct userdata, 1); 151 u->core = m->core; 152 u->module = m; 153 u->sink_input = NULL; 154 155 u->peek_index = 0; 156 pa_memchunk_sine(&u->memchunk, m->core->mempool, ss.rate, frequency); 157 158 pa_sink_input_new_data_init(&data); 159 data.driver = __FILE__; 160 data.module = m; 161 pa_sink_input_new_data_set_sink(&data, sink, false, true); 162 pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "%u Hz Sine", frequency); 163 pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract"); 164 pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency); 165 pa_sink_input_new_data_set_sample_spec(&data, &ss); 166 167 pa_sink_input_new(&u->sink_input, m->core, &data); 168 pa_sink_input_new_data_done(&data); 169 170 if (!u->sink_input) 171 goto fail; 172 173 u->sink_input->pop = sink_input_pop_cb; 174 u->sink_input->process_rewind = sink_input_process_rewind_cb; 175 u->sink_input->kill = sink_input_kill_cb; 176 u->sink_input->state_change = sink_input_state_change_cb; 177 u->sink_input->userdata = u; 178 179 pa_sink_input_put(u->sink_input); 180 181 pa_modargs_free(ma); 182 return 0; 183 184fail: 185 if (ma) 186 pa_modargs_free(ma); 187 188 pa__done(m); 189 return -1; 190} 191 192void pa__done(pa_module*m) { 193 struct userdata *u; 194 195 pa_assert(m); 196 197 if (!(u = m->userdata)) 198 return; 199 200 if (u->sink_input) { 201 pa_sink_input_unlink(u->sink_input); 202 pa_sink_input_unref(u->sink_input); 203 } 204 205 if (u->memchunk.memblock) 206 pa_memblock_unref(u->memchunk.memblock); 207 208 pa_xfree(u); 209} 210