]> code.delx.au - pulseaudio/blob - src/pulsecore/sound-file-stream.c
Lots of assorted minor cleanups and fixes:
[pulseaudio] / src / pulsecore / sound-file-stream.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 Copyright 2004-2006 Lennart Poettering
7
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with PulseAudio; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 USA.
22 ***/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <errno.h>
34
35 #include <sndfile.h>
36
37 #include <pulse/xmalloc.h>
38
39 #include <pulsecore/core-error.h>
40 #include <pulsecore/sink-input.h>
41 #include <pulsecore/log.h>
42 #include <pulsecore/thread-mq.h>
43
44 #include "sound-file-stream.h"
45
46 #define BUF_SIZE (1024*16)
47
48 typedef struct file_stream {
49 pa_msgobject parent;
50 pa_core *core;
51 SNDFILE *sndfile;
52 pa_sink_input *sink_input;
53 pa_memchunk memchunk;
54 sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
55 size_t drop;
56 } file_stream;
57
58 enum {
59 FILE_STREAM_MESSAGE_UNLINK
60 };
61
62 PA_DECLARE_CLASS(file_stream);
63 #define FILE_STREAM(o) (file_stream_cast(o))
64 static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
65
66 static void file_stream_unlink(file_stream *u) {
67 pa_assert(u);
68
69 if (!u->sink_input)
70 return;
71
72 pa_sink_input_unlink(u->sink_input);
73
74 pa_sink_input_unref(u->sink_input);
75 u->sink_input = NULL;
76
77 /* Make sure we don't decrease the ref count twice. */
78 file_stream_unref(u);
79 }
80
81 static void file_stream_free(pa_object *o) {
82 file_stream *u = FILE_STREAM(o);
83 pa_assert(u);
84
85 file_stream_unlink(u);
86
87 if (u->memchunk.memblock)
88 pa_memblock_unref(u->memchunk.memblock);
89
90 if (u->sndfile)
91 sf_close(u->sndfile);
92
93 pa_xfree(u);
94 }
95
96 static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
97 file_stream *u = FILE_STREAM(o);
98 file_stream_assert_ref(u);
99
100 switch (code) {
101 case FILE_STREAM_MESSAGE_UNLINK:
102 file_stream_unlink(u);
103 break;
104 }
105
106 return 0;
107 }
108
109 static void sink_input_kill_cb(pa_sink_input *i) {
110 pa_sink_input_assert_ref(i);
111
112 file_stream_unlink(FILE_STREAM(i->userdata));
113 }
114
115 static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
116 file_stream *u;
117
118 pa_assert(i);
119 pa_assert(chunk);
120 u = FILE_STREAM(i->userdata);
121 file_stream_assert_ref(u);
122
123 if (!u->sndfile)
124 return -1;
125
126 for (;;) {
127
128 if (!u->memchunk.memblock) {
129
130 u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, BUF_SIZE);
131 u->memchunk.index = 0;
132
133 if (u->readf_function) {
134 sf_count_t n;
135 void *p;
136 size_t fs = pa_frame_size(&i->sample_spec);
137
138 p = pa_memblock_acquire(u->memchunk.memblock);
139 n = u->readf_function(u->sndfile, p, BUF_SIZE/fs);
140 pa_memblock_release(u->memchunk.memblock);
141
142 if (n <= 0)
143 n = 0;
144
145 u->memchunk.length = n * fs;
146 } else {
147 sf_count_t n;
148 void *p;
149
150 p = pa_memblock_acquire(u->memchunk.memblock);
151 n = sf_read_raw(u->sndfile, p, BUF_SIZE);
152 pa_memblock_release(u->memchunk.memblock);
153
154 if (n <= 0)
155 n = 0;
156
157 u->memchunk.length = n;
158 }
159
160 if (u->memchunk.length <= 0) {
161
162 pa_memblock_unref(u->memchunk.memblock);
163 pa_memchunk_reset(&u->memchunk);
164
165 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
166
167 sf_close(u->sndfile);
168 u->sndfile = NULL;
169
170 return -1;
171 }
172 }
173
174 pa_assert(u->memchunk.memblock);
175 pa_assert(u->memchunk.length > 0);
176
177 if (u->drop < u->memchunk.length) {
178 u->memchunk.index += u->drop;
179 u->memchunk.length -= u->drop;
180 u->drop = 0;
181 break;
182 }
183
184 u->drop -= u->memchunk.length;
185 pa_memblock_unref(u->memchunk.memblock);
186 pa_memchunk_reset(&u->memchunk);
187 }
188
189 *chunk = u->memchunk;
190 pa_memblock_ref(chunk->memblock);
191
192 pa_assert(chunk->length > 0);
193 pa_assert(u->drop <= 0);
194
195 return 0;
196 }
197
198 static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
199 file_stream *u;
200
201 pa_assert(i);
202 pa_assert(length > 0);
203 u = FILE_STREAM(i->userdata);
204 file_stream_assert_ref(u);
205
206 if (u->memchunk.memblock) {
207
208 if (length < u->memchunk.length) {
209 u->memchunk.index += length;
210 u->memchunk.length -= length;
211 return;
212 }
213
214 length -= u->memchunk.length;
215 pa_memblock_unref(u->memchunk.memblock);
216 pa_memchunk_reset(&u->memchunk);
217 }
218
219 u->drop += length;
220 }
221
222 int pa_play_file(
223 pa_sink *sink,
224 const char *fname,
225 const pa_cvolume *volume) {
226
227 file_stream *u = NULL;
228 SF_INFO sfinfo;
229 pa_sample_spec ss;
230 pa_sink_input_new_data data;
231 int fd;
232
233 pa_assert(sink);
234 pa_assert(fname);
235
236 u = pa_msgobject_new(file_stream);
237 u->parent.parent.free = file_stream_free;
238 u->parent.process_msg = file_stream_process_msg;
239 u->core = sink->core;
240 u->sink_input = NULL;
241 pa_memchunk_reset(&u->memchunk);
242 u->sndfile = NULL;
243 u->readf_function = NULL;
244 u->drop = 0;
245
246 memset(&sfinfo, 0, sizeof(sfinfo));
247
248 if ((fd = open(fname, O_RDONLY|O_NOCTTY)) < 0) {
249 pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
250 goto fail;
251 }
252
253 /* FIXME: For now we just use posix_fadvise to avoid page faults
254 * when accessing the file data. Eventually we should move the
255 * file reader into the main event loop and pass the data over the
256 * asyncmsgq. */
257
258 if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
259 pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
260 goto fail;
261 } else
262 pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
263
264 if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED) < 0) {
265 pa_log_warn("POSIX_FADV_WILLNEED failed: %s", pa_cstrerror(errno));
266 goto fail;
267 } else
268 pa_log_debug("POSIX_FADV_WILLNEED succeeded.");
269
270 if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
271 pa_log("Failed to open file %s", fname);
272 close(fd);
273 goto fail;
274 }
275
276 switch (sfinfo.format & 0xFF) {
277 case SF_FORMAT_PCM_16:
278 case SF_FORMAT_PCM_U8:
279 case SF_FORMAT_PCM_S8:
280 ss.format = PA_SAMPLE_S16NE;
281 u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
282 break;
283
284 case SF_FORMAT_ULAW:
285 ss.format = PA_SAMPLE_ULAW;
286 break;
287
288 case SF_FORMAT_ALAW:
289 ss.format = PA_SAMPLE_ALAW;
290 break;
291
292 case SF_FORMAT_FLOAT:
293 default:
294 ss.format = PA_SAMPLE_FLOAT32NE;
295 u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
296 break;
297 }
298
299 ss.rate = sfinfo.samplerate;
300 ss.channels = sfinfo.channels;
301
302 if (!pa_sample_spec_valid(&ss)) {
303 pa_log("Unsupported sample format in file %s", fname);
304 goto fail;
305 }
306
307 pa_sink_input_new_data_init(&data);
308 data.sink = sink;
309 data.driver = __FILE__;
310 data.name = fname;
311 pa_sink_input_new_data_set_sample_spec(&data, &ss);
312 pa_sink_input_new_data_set_volume(&data, volume);
313
314 if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
315 goto fail;
316
317 u->sink_input->peek = sink_input_peek_cb;
318 u->sink_input->drop = sink_input_drop_cb;
319 u->sink_input->kill = sink_input_kill_cb;
320 u->sink_input->userdata = u;
321
322 pa_sink_input_put(u->sink_input);
323
324 /* The reference to u is dangling here, because we want to keep
325 * this stream around until it is fully played. */
326
327 return 0;
328
329 fail:
330 if (u)
331 file_stream_unref(u);
332
333 return -1;
334 }