1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2004-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 <unistd.h> 27#include <fcntl.h> 28#include <errno.h> 29 30#include <sndfile.h> 31 32#include <pulse/xmalloc.h> 33#include <pulse/util.h> 34 35#include <pulsecore/core-error.h> 36#include <pulsecore/sink-input.h> 37#include <pulsecore/log.h> 38#include <pulsecore/thread-mq.h> 39#include <pulsecore/core-util.h> 40#include <pulsecore/mix.h> 41#include <pulsecore/sndfile-util.h> 42 43#include "sound-file-stream.h" 44 45#define MEMBLOCKQ_MAXLENGTH (16*1024*1024) 46 47typedef struct file_stream { 48 pa_msgobject parent; 49 pa_core *core; 50 pa_sink_input *sink_input; 51 52 SNDFILE *sndfile; 53 sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames); 54 55 /* We need this memblockq here to easily fulfill rewind requests 56 * (even beyond the file start!) */ 57 pa_memblockq *memblockq; 58} file_stream; 59 60enum { 61 FILE_STREAM_MESSAGE_UNLINK 62}; 63 64PA_DEFINE_PRIVATE_CLASS(file_stream, pa_msgobject); 65#define FILE_STREAM(o) (file_stream_cast(o)) 66 67/* Called from main context */ 68static void file_stream_unlink(file_stream *u) { 69 pa_assert(u); 70 71 if (!u->sink_input) 72 return; 73 74 pa_sink_input_unlink(u->sink_input); 75 pa_sink_input_unref(u->sink_input); 76 u->sink_input = NULL; 77 78 /* Make sure we don't decrease the ref count twice. */ 79 file_stream_unref(u); 80} 81 82/* Called from main context */ 83static void file_stream_free(pa_object *o) { 84 file_stream *u = FILE_STREAM(o); 85 pa_assert(u); 86 87 if (u->memblockq) 88 pa_memblockq_free(u->memblockq); 89 90 if (u->sndfile) 91 sf_close(u->sndfile); 92 93 pa_xfree(u); 94} 95 96/* Called from main context */ 97static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { 98 file_stream *u = FILE_STREAM(o); 99 file_stream_assert_ref(u); 100 101 switch (code) { 102 case FILE_STREAM_MESSAGE_UNLINK: 103 file_stream_unlink(u); 104 break; 105 } 106 107 return 0; 108} 109 110/* Called from main context */ 111static void sink_input_kill_cb(pa_sink_input *i) { 112 file_stream *u; 113 114 pa_sink_input_assert_ref(i); 115 u = FILE_STREAM(i->userdata); 116 file_stream_assert_ref(u); 117 118 file_stream_unlink(u); 119} 120 121/* Called from IO thread context */ 122static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { 123 file_stream *u; 124 125 pa_sink_input_assert_ref(i); 126 u = FILE_STREAM(i->userdata); 127 file_stream_assert_ref(u); 128 129 /* If we are added for the first time, ask for a rewinding so that 130 * we are heard right-away. */ 131 if (PA_SINK_INPUT_IS_LINKED(state) && 132 i->thread_info.state == PA_SINK_INPUT_INIT && i->sink) 133 pa_sink_input_request_rewind(i, 0, false, true, true); 134} 135 136/* Called from IO thread context */ 137static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { 138 file_stream *u; 139 140 pa_sink_input_assert_ref(i); 141 pa_assert(chunk); 142 u = FILE_STREAM(i->userdata); 143 file_stream_assert_ref(u); 144 145 if (!u->memblockq) 146 return -1; 147 148 for (;;) { 149 pa_memchunk tchunk; 150 size_t fs; 151 void *p; 152 sf_count_t n; 153 154 if (pa_memblockq_peek(u->memblockq, chunk) >= 0) { 155 chunk->length = PA_MIN(chunk->length, length); 156 pa_memblockq_drop(u->memblockq, chunk->length); 157 return 0; 158 } 159 160 if (!u->sndfile) 161 break; 162 163 tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length); 164 tchunk.index = 0; 165 166 p = pa_memblock_acquire(tchunk.memblock); 167 168 if (u->readf_function) { 169 fs = pa_frame_size(&i->sample_spec); 170 n = u->readf_function(u->sndfile, p, (sf_count_t) (length/fs)); 171 } else { 172 fs = 1; 173 n = sf_read_raw(u->sndfile, p, (sf_count_t) length); 174 } 175 176 pa_memblock_release(tchunk.memblock); 177 178 if (n <= 0) { 179 pa_memblock_unref(tchunk.memblock); 180 181 sf_close(u->sndfile); 182 u->sndfile = NULL; 183 break; 184 } 185 186 tchunk.length = (size_t) n * fs; 187 188 pa_memblockq_push_align(u->memblockq, &tchunk); 189 pa_memblock_unref(tchunk.memblock); 190 } 191 192 if (pa_sink_input_safe_to_remove(i)) { 193 pa_memblockq_free(u->memblockq); 194 u->memblockq = NULL; 195 196 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); 197 } 198 199 return -1; 200} 201 202static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { 203 file_stream *u; 204 205 pa_sink_input_assert_ref(i); 206 u = FILE_STREAM(i->userdata); 207 file_stream_assert_ref(u); 208 209 if (!u->memblockq) 210 return; 211 212 pa_memblockq_rewind(u->memblockq, nbytes); 213} 214 215static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { 216 file_stream *u; 217 218 pa_sink_input_assert_ref(i); 219 u = FILE_STREAM(i->userdata); 220 file_stream_assert_ref(u); 221 222 if (!u->memblockq) 223 return; 224 225 pa_memblockq_set_maxrewind(u->memblockq, nbytes); 226} 227 228int pa_play_file( 229 pa_sink *sink, 230 const char *fname, 231 const pa_cvolume *volume) { 232 233 file_stream *u = NULL; 234 pa_sample_spec ss; 235 pa_channel_map cm; 236 pa_sink_input_new_data data; 237 int fd; 238 SF_INFO sfi; 239 pa_memchunk silence; 240 241 pa_assert(sink); 242 pa_assert(fname); 243 244 u = pa_msgobject_new(file_stream); 245 u->parent.parent.free = file_stream_free; 246 u->parent.process_msg = file_stream_process_msg; 247 u->core = sink->core; 248 u->sink_input = NULL; 249 u->sndfile = NULL; 250 u->readf_function = NULL; 251 u->memblockq = NULL; 252 253 if ((fd = pa_open_cloexec(fname, O_RDONLY, 0)) < 0) { 254 pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno)); 255 goto fail; 256 } 257 258 /* FIXME: For now we just use posix_fadvise to avoid page faults 259 * when accessing the file data. Eventually we should move the 260 * file reader into the main event loop and pass the data over the 261 * asyncmsgq. */ 262 263#ifdef HAVE_POSIX_FADVISE 264 if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) { 265 pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno)); 266 goto fail; 267 } else 268 pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded."); 269 270 if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED) < 0) { 271 pa_log_warn("POSIX_FADV_WILLNEED failed: %s", pa_cstrerror(errno)); 272 goto fail; 273 } else 274 pa_log_debug("POSIX_FADV_WILLNEED succeeded."); 275#endif 276 277 pa_zero(sfi); 278 if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfi, 1))) { 279 pa_log("Failed to open file %s", fname); 280 goto fail; 281 } 282 283 fd = -1; 284 285 if (pa_sndfile_read_sample_spec(u->sndfile, &ss) < 0) { 286 pa_log("Failed to determine file sample format."); 287 goto fail; 288 } 289 290 if (pa_sndfile_read_channel_map(u->sndfile, &cm) < 0) { 291 if (ss.channels > 2) 292 pa_log_info("Failed to determine file channel map, synthesizing one."); 293 pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT); 294 } 295 296 u->readf_function = pa_sndfile_readf_function(&ss); 297 298 pa_sink_input_new_data_init(&data); 299 pa_sink_input_new_data_set_sink(&data, sink, false, true); 300 data.driver = __FILE__; 301 pa_sink_input_new_data_set_sample_spec(&data, &ss); 302 pa_sink_input_new_data_set_channel_map(&data, &cm); 303 pa_sink_input_new_data_set_volume(&data, volume); 304 pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname)); 305 pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname); 306 pa_sndfile_init_proplist(u->sndfile, data.proplist); 307 308 pa_sink_input_new(&u->sink_input, sink->core, &data); 309 pa_sink_input_new_data_done(&data); 310 311 if (!u->sink_input) 312 goto fail; 313 314 u->sink_input->pop = sink_input_pop_cb; 315 u->sink_input->process_rewind = sink_input_process_rewind_cb; 316 u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; 317 u->sink_input->kill = sink_input_kill_cb; 318 u->sink_input->state_change = sink_input_state_change_cb; 319 u->sink_input->userdata = u; 320 321 pa_sink_input_get_silence(u->sink_input, &silence); 322 u->memblockq = pa_memblockq_new("sound-file-stream memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, &silence); 323 pa_memblock_unref(silence.memblock); 324 325 pa_sink_input_put(u->sink_input); 326 327 /* The reference to u is dangling here, because we want to keep 328 * this stream around until it is fully played. */ 329 330 return 0; 331 332fail: 333 file_stream_unref(u); 334 335 if (fd >= 0) 336 pa_close(fd); 337 338 return -1; 339} 340