]> code.delx.au - pulseaudio/blob - src/pulsecore/sound-file-stream.c
use posix_fadvise to avoid page faults when reading audio files from disk
[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
43 #include "sound-file-stream.h"
44
45 #define BUF_SIZE (1024*16)
46
47 typedef struct file_stream {
48 pa_msgobject parent;
49 pa_core *core;
50 SNDFILE *sndfile;
51 pa_sink_input *sink_input;
52 pa_memchunk memchunk;
53 sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
54 size_t drop;
55 } file_stream;
56
57 enum {
58 FILE_STREAM_MESSAGE_UNLINK
59 };
60
61 PA_DECLARE_CLASS(file_stream);
62 #define FILE_STREAM(o) (file_stream_cast(o))
63 static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
64
65 static void file_stream_unlink(file_stream *u) {
66 pa_assert(u);
67
68 if (!u->sink_input)
69 return;
70
71 pa_sink_input_disconnect(u->sink_input);
72
73 pa_sink_input_unref(u->sink_input);
74 u->sink_input = NULL;
75
76 /* Make sure we don't decrease the ref count twice. */
77 file_stream_unref(u);
78 }
79
80 static void file_stream_free(pa_object *o) {
81 file_stream *u = FILE_STREAM(o);
82 pa_assert(u);
83
84 file_stream_unlink(u);
85
86 if (u->memchunk.memblock)
87 pa_memblock_unref(u->memchunk.memblock);
88
89 if (u->sndfile)
90 sf_close(u->sndfile);
91
92 pa_xfree(u);
93 }
94
95 static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
96 file_stream *u = FILE_STREAM(o);
97 file_stream_assert_ref(u);
98
99 switch (code) {
100 case FILE_STREAM_MESSAGE_UNLINK:
101 file_stream_unlink(u);
102 break;
103 }
104
105 return 0;
106 }
107
108 static void sink_input_kill_cb(pa_sink_input *i) {
109 pa_sink_input_assert_ref(i);
110
111 file_stream_unlink(FILE_STREAM(i->userdata));
112 }
113
114 static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
115 file_stream *u;
116
117 pa_assert(i);
118 pa_assert(chunk);
119 u = FILE_STREAM(i->userdata);
120 file_stream_assert_ref(u);
121
122 if (!u->sndfile)
123 return -1;
124
125 for (;;) {
126
127 if (!u->memchunk.memblock) {
128
129 u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, BUF_SIZE);
130 u->memchunk.index = 0;
131
132 if (u->readf_function) {
133 sf_count_t n;
134 void *p;
135 size_t fs = pa_frame_size(&i->sample_spec);
136
137 p = pa_memblock_acquire(u->memchunk.memblock);
138 n = u->readf_function(u->sndfile, p, BUF_SIZE/fs);
139 pa_memblock_release(u->memchunk.memblock);
140
141 if (n <= 0)
142 n = 0;
143
144 u->memchunk.length = n * fs;
145 } else {
146 sf_count_t n;
147 void *p;
148
149 p = pa_memblock_acquire(u->memchunk.memblock);
150 n = sf_read_raw(u->sndfile, p, BUF_SIZE);
151 pa_memblock_release(u->memchunk.memblock);
152
153 if (n <= 0)
154 n = 0;
155
156 u->memchunk.length = n;
157 }
158
159 if (u->memchunk.length <= 0) {
160
161 pa_memblock_unref(u->memchunk.memblock);
162 pa_memchunk_reset(&u->memchunk);
163
164 pa_asyncmsgq_post(u->core->asyncmsgq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
165
166 sf_close(u->sndfile);
167 u->sndfile = NULL;
168
169 return -1;
170 }
171 }
172
173 pa_assert(u->memchunk.memblock);
174 pa_assert(u->memchunk.length > 0);
175
176 if (u->drop < u->memchunk.length) {
177 u->memchunk.index += u->drop;
178 u->memchunk.length -= u->drop;
179 u->drop = 0;
180 break;
181 }
182
183 u->drop -= u->memchunk.length;
184 pa_memblock_unref(u->memchunk.memblock);
185 pa_memchunk_reset(&u->memchunk);
186 }
187
188 *chunk = u->memchunk;
189 pa_memblock_ref(chunk->memblock);
190
191 pa_assert(chunk->length > 0);
192 pa_assert(u->drop <= 0);
193
194 return 0;
195 }
196
197 static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
198 file_stream *u;
199
200 pa_assert(i);
201 pa_assert(length > 0);
202 u = FILE_STREAM(i->userdata);
203 file_stream_assert_ref(u);
204
205 if (u->memchunk.memblock) {
206
207 if (length < u->memchunk.length) {
208 u->memchunk.index += length;
209 u->memchunk.length -= length;
210 return;
211 }
212
213 length -= u->memchunk.length;
214 pa_memblock_unref(u->memchunk.memblock);
215 pa_memchunk_reset(&u->memchunk);
216 }
217
218 u->drop += length;
219 }
220
221 int pa_play_file(
222 pa_sink *sink,
223 const char *fname,
224 const pa_cvolume *volume) {
225
226 file_stream *u = NULL;
227 SF_INFO sfinfo;
228 pa_sample_spec ss;
229 pa_sink_input_new_data data;
230 int fd;
231
232 pa_assert(sink);
233 pa_assert(fname);
234
235 u = pa_msgobject_new(file_stream);
236 u->parent.parent.free = file_stream_free;
237 u->parent.process_msg = file_stream_process_msg;
238 u->core = sink->core;
239 u->sink_input = NULL;
240 pa_memchunk_reset(&u->memchunk);
241 u->sndfile = NULL;
242 u->readf_function = NULL;
243 u->drop = 0;
244
245 memset(&sfinfo, 0, sizeof(sfinfo));
246
247 if ((fd = open(fname, O_RDONLY|O_NOCTTY)) < 0) {
248 pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
249 goto fail;
250 }
251
252 /* FIXME: For now we just use posix_fadvise to avoid page faults
253 * when accessing the file data. Eventually we should move the
254 * file reader into the main event loop and pass the data over the
255 * asyncmsgq. */
256
257 if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
258 pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
259 goto fail;
260 } else
261 pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
262
263 if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED) < 0) {
264 pa_log_warn("POSIX_FADV_WILLNEED failed: %s", pa_cstrerror(errno));
265 goto fail;
266 } else
267 pa_log_debug("POSIX_FADV_WILLNEED succeeded.");
268
269 if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
270 pa_log("Failed to open file %s", fname);
271 close(fd);
272 goto fail;
273 }
274
275 switch (sfinfo.format & 0xFF) {
276 case SF_FORMAT_PCM_16:
277 case SF_FORMAT_PCM_U8:
278 case SF_FORMAT_PCM_S8:
279 ss.format = PA_SAMPLE_S16NE;
280 u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
281 break;
282
283 case SF_FORMAT_ULAW:
284 ss.format = PA_SAMPLE_ULAW;
285 break;
286
287 case SF_FORMAT_ALAW:
288 ss.format = PA_SAMPLE_ALAW;
289 break;
290
291 case SF_FORMAT_FLOAT:
292 default:
293 ss.format = PA_SAMPLE_FLOAT32NE;
294 u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
295 break;
296 }
297
298 ss.rate = sfinfo.samplerate;
299 ss.channels = sfinfo.channels;
300
301 if (!pa_sample_spec_valid(&ss)) {
302 pa_log("Unsupported sample format in file %s", fname);
303 goto fail;
304 }
305
306 pa_sink_input_new_data_init(&data);
307 data.sink = sink;
308 data.driver = __FILE__;
309 data.name = fname;
310 pa_sink_input_new_data_set_sample_spec(&data, &ss);
311 pa_sink_input_new_data_set_volume(&data, volume);
312
313 if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
314 goto fail;
315
316 u->sink_input->peek = sink_input_peek_cb;
317 u->sink_input->drop = sink_input_drop_cb;
318 u->sink_input->kill = sink_input_kill_cb;
319 u->sink_input->userdata = u;
320
321 pa_sink_input_put(u->sink_input);
322
323 /* The reference to u is dangling here, because we want to keep
324 * this stream around until it is fully played. */
325
326 return 0;
327
328 fail:
329 if (u)
330 file_stream_unref(u);
331
332 return -1;
333 }