4 * \brief PCM File Plugin Interface
5 * \author Abramo Bagnara <abramo@alsa-project.org>
10 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
13 * This library is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser General Public License as
15 * published by the Free Software Foundation; either version 2.1 of
16 * the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #include "pcm_local.h"
31 #include "pcm_plugin.h"
34 /* entry for static linking */
35 const char *_snd_module_pcm_file = "";
40 typedef enum _snd_pcm_file_format {
41 SND_PCM_FILE_FORMAT_RAW
42 } snd_pcm_file_format_t;
50 snd_pcm_uframes_t appl_ptr;
51 snd_pcm_uframes_t file_ptr_bytes;
52 snd_pcm_uframes_t wbuf_size;
53 size_t wbuf_size_bytes;
54 size_t wbuf_used_bytes;
56 snd_pcm_channel_area_t *wbuf_areas;
60 #endif /* DOC_HIDDEN */
62 static void snd_pcm_file_write_bytes(snd_pcm_t *pcm, size_t bytes)
64 snd_pcm_file_t *file = pcm->private_data;
65 assert(bytes <= file->wbuf_used_bytes);
67 snd_pcm_sframes_t err;
69 size_t cont = file->wbuf_size_bytes - file->file_ptr_bytes;
72 err = write(file->fd, file->wbuf + file->file_ptr_bytes, n);
74 SYSERR("write failed");
78 file->wbuf_used_bytes -= err;
79 file->file_ptr_bytes += err;
80 if (file->file_ptr_bytes == file->wbuf_size_bytes)
81 file->file_ptr_bytes = 0;
82 if ((snd_pcm_uframes_t)err != n)
87 static void snd_pcm_file_add_frames(snd_pcm_t *pcm,
88 const snd_pcm_channel_area_t *areas,
89 snd_pcm_uframes_t offset,
90 snd_pcm_uframes_t frames)
92 snd_pcm_file_t *file = pcm->private_data;
94 snd_pcm_uframes_t n = frames;
95 snd_pcm_uframes_t cont = file->wbuf_size - file->appl_ptr;
96 snd_pcm_uframes_t avail = file->wbuf_size - snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes);
101 snd_pcm_areas_copy(file->wbuf_areas, file->appl_ptr,
103 pcm->channels, n, pcm->format);
106 if (file->appl_ptr == file->wbuf_size)
108 file->wbuf_used_bytes += snd_pcm_frames_to_bytes(pcm, n);
109 if (file->wbuf_used_bytes > file->buffer_bytes)
110 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes - file->buffer_bytes);
111 assert(file->wbuf_used_bytes < file->wbuf_size_bytes);
115 static int snd_pcm_file_close(snd_pcm_t *pcm)
117 snd_pcm_file_t *file = pcm->private_data;
119 if (file->close_slave)
120 err = snd_pcm_close(file->slave);
122 free((void *)file->fname);
129 static int snd_pcm_file_nonblock(snd_pcm_t *pcm, int nonblock)
131 snd_pcm_file_t *file = pcm->private_data;
132 return snd_pcm_nonblock(file->slave, nonblock);
135 static int snd_pcm_file_async(snd_pcm_t *pcm, int sig, pid_t pid)
137 snd_pcm_file_t *file = pcm->private_data;
138 return snd_pcm_async(file->slave, sig, pid);
141 static int snd_pcm_file_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
143 snd_pcm_file_t *file = pcm->private_data;
144 return snd_pcm_info(file->slave, info);
147 static int snd_pcm_file_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
149 snd_pcm_file_t *file = pcm->private_data;
150 return snd_pcm_channel_info(file->slave, info);
153 static int snd_pcm_file_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
155 snd_pcm_file_t *file = pcm->private_data;
156 return snd_pcm_status(file->slave, status);
159 static snd_pcm_state_t snd_pcm_file_state(snd_pcm_t *pcm)
161 snd_pcm_file_t *file = pcm->private_data;
162 return snd_pcm_state(file->slave);
165 static int snd_pcm_file_avail(snd_pcm_t *pcm, snd_pcm_uframes_t *availp)
167 snd_pcm_file_t *file = pcm->private_data;
168 return snd_pcm_avail(file->slave, availp);
171 static int snd_pcm_file_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
173 snd_pcm_file_t *file = pcm->private_data;
174 return snd_pcm_delay(file->slave, delayp);
177 static int snd_pcm_file_prepare(snd_pcm_t *pcm)
179 snd_pcm_file_t *file = pcm->private_data;
180 return snd_pcm_prepare(file->slave);
183 static int snd_pcm_file_reset(snd_pcm_t *pcm)
185 snd_pcm_file_t *file = pcm->private_data;
186 int err = snd_pcm_reset(file->slave);
188 /* FIXME: Questionable here */
189 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
190 assert(file->wbuf_used_bytes == 0);
195 static int snd_pcm_file_start(snd_pcm_t *pcm)
197 snd_pcm_file_t *file = pcm->private_data;
198 return snd_pcm_start(file->slave);
201 static int snd_pcm_file_drop(snd_pcm_t *pcm)
203 snd_pcm_file_t *file = pcm->private_data;
204 int err = snd_pcm_drop(file->slave);
206 /* FIXME: Questionable here */
207 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
208 assert(file->wbuf_used_bytes == 0);
213 static int snd_pcm_file_drain(snd_pcm_t *pcm)
215 snd_pcm_file_t *file = pcm->private_data;
216 int err = snd_pcm_drain(file->slave);
218 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
219 assert(file->wbuf_used_bytes == 0);
224 static int snd_pcm_file_pause(snd_pcm_t *pcm, int enable)
226 snd_pcm_file_t *file = pcm->private_data;
227 return snd_pcm_pause(file->slave, enable);
230 static snd_pcm_sframes_t snd_pcm_file_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
232 snd_pcm_file_t *file = pcm->private_data;
233 snd_pcm_sframes_t err = snd_pcm_rewind(file->slave, frames);
235 snd_pcm_uframes_t n = snd_pcm_frames_to_bytes(pcm, frames);
236 snd_pcm_sframes_t ptr;
237 assert(n >= file->wbuf_used_bytes);
238 ptr = file->appl_ptr - err;
240 ptr += file->wbuf_size;
241 file->wbuf_used_bytes -= n;
246 static int snd_pcm_file_resume(snd_pcm_t *pcm)
248 snd_pcm_file_t *file = pcm->private_data;
249 return snd_pcm_resume(file->slave);
252 static snd_pcm_sframes_t snd_pcm_file_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
254 snd_pcm_file_t *file = pcm->private_data;
255 snd_pcm_channel_area_t areas[pcm->channels];
256 snd_pcm_sframes_t n = snd_pcm_writei(file->slave, buffer, size);
258 snd_pcm_areas_from_buf(pcm, areas, (void*) buffer);
259 snd_pcm_file_add_frames(pcm, areas, 0, n);
264 static snd_pcm_sframes_t snd_pcm_file_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
266 snd_pcm_file_t *file = pcm->private_data;
267 snd_pcm_channel_area_t areas[pcm->channels];
268 snd_pcm_sframes_t n = snd_pcm_writen(file->slave, bufs, size);
270 snd_pcm_areas_from_bufs(pcm, areas, bufs);
271 snd_pcm_file_add_frames(pcm, areas, 0, n);
276 static snd_pcm_sframes_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
278 snd_pcm_file_t *file = pcm->private_data;
279 snd_pcm_channel_area_t areas[pcm->channels];
280 snd_pcm_sframes_t n = snd_pcm_readi(file->slave, buffer, size);
282 snd_pcm_areas_from_buf(pcm, areas, buffer);
283 snd_pcm_file_add_frames(pcm, areas, 0, n);
288 static snd_pcm_sframes_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
290 snd_pcm_file_t *file = pcm->private_data;
291 snd_pcm_channel_area_t areas[pcm->channels];
292 snd_pcm_sframes_t n = snd_pcm_writen(file->slave, bufs, size);
294 snd_pcm_areas_from_bufs(pcm, areas, bufs);
295 snd_pcm_file_add_frames(pcm, areas, 0, n);
300 static snd_pcm_sframes_t snd_pcm_file_mmap_commit(snd_pcm_t *pcm,
301 snd_pcm_uframes_t offset,
302 snd_pcm_uframes_t size)
304 snd_pcm_file_t *file = pcm->private_data;
305 snd_pcm_uframes_t ofs;
306 snd_pcm_uframes_t siz = size;
307 const snd_pcm_channel_area_t *areas;
308 snd_pcm_sframes_t result;
310 snd_pcm_mmap_begin(file->slave, &areas, &ofs, &siz);
311 assert(ofs == offset && siz == size);
312 result = snd_pcm_mmap_commit(file->slave, ofs, siz);
314 snd_pcm_file_add_frames(pcm, areas, ofs, result);
318 static snd_pcm_sframes_t snd_pcm_file_avail_update(snd_pcm_t *pcm)
320 snd_pcm_file_t *file = pcm->private_data;
321 return snd_pcm_avail_update(file->slave);
324 static int snd_pcm_file_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
326 snd_pcm_file_t *file = pcm->private_data;
327 return snd_pcm_hw_refine(file->slave, params);
330 static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
332 snd_pcm_file_t *file = pcm->private_data;
333 unsigned int channel;
334 snd_pcm_t *slave = file->slave;
335 int err = _snd_pcm_hw_params(slave, params);
338 file->buffer_bytes = snd_pcm_frames_to_bytes(slave, slave->buffer_size);
339 file->wbuf_size = slave->buffer_size * 2;
340 file->wbuf_size_bytes = snd_pcm_frames_to_bytes(slave, file->wbuf_size);
342 file->wbuf = malloc(file->wbuf_size_bytes);
343 file->wbuf_areas = malloc(sizeof(*file->wbuf_areas) * slave->channels);
344 file->appl_ptr = file->file_ptr_bytes = 0;
345 for (channel = 0; channel < slave->channels; ++channel) {
346 snd_pcm_channel_area_t *a = &file->wbuf_areas[channel];
347 a->addr = file->wbuf;
348 a->first = slave->sample_bits * channel;
349 a->step = slave->frame_bits;
354 static int snd_pcm_file_hw_free(snd_pcm_t *pcm)
356 snd_pcm_file_t *file = pcm->private_data;
359 free(file->wbuf_areas);
361 file->wbuf_areas = 0;
363 return snd_pcm_hw_free(file->slave);
366 static int snd_pcm_file_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
368 snd_pcm_file_t *file = pcm->private_data;
369 return snd_pcm_sw_params(file->slave, params);
372 static int snd_pcm_file_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
377 static int snd_pcm_file_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
382 static void snd_pcm_file_dump(snd_pcm_t *pcm, snd_output_t *out)
384 snd_pcm_file_t *file = pcm->private_data;
386 snd_output_printf(out, "File PCM (file=%s)\n", file->fname);
388 snd_output_printf(out, "File PCM (fd=%d)\n", file->fd);
390 snd_output_printf(out, "Its setup is:\n");
391 snd_pcm_dump_setup(pcm, out);
393 snd_output_printf(out, "Slave: ");
394 snd_pcm_dump(file->slave, out);
397 static snd_pcm_ops_t snd_pcm_file_ops = {
398 close: snd_pcm_file_close,
399 info: snd_pcm_file_info,
400 hw_refine: snd_pcm_file_hw_refine,
401 hw_params: snd_pcm_file_hw_params,
402 hw_free: snd_pcm_file_hw_free,
403 sw_params: snd_pcm_file_sw_params,
404 channel_info: snd_pcm_file_channel_info,
405 dump: snd_pcm_file_dump,
406 nonblock: snd_pcm_file_nonblock,
407 async: snd_pcm_file_async,
408 mmap: snd_pcm_file_mmap,
409 munmap: snd_pcm_file_munmap,
412 static snd_pcm_fast_ops_t snd_pcm_file_fast_ops = {
413 status: snd_pcm_file_status,
414 state: snd_pcm_file_state,
415 avail: snd_pcm_file_avail,
416 delay: snd_pcm_file_delay,
417 prepare: snd_pcm_file_prepare,
418 reset: snd_pcm_file_reset,
419 start: snd_pcm_file_start,
420 drop: snd_pcm_file_drop,
421 drain: snd_pcm_file_drain,
422 pause: snd_pcm_file_pause,
423 rewind: snd_pcm_file_rewind,
424 resume: snd_pcm_file_resume,
425 writei: snd_pcm_file_writei,
426 writen: snd_pcm_file_writen,
427 readi: snd_pcm_file_readi,
428 readn: snd_pcm_file_readn,
429 avail_update: snd_pcm_file_avail_update,
430 mmap_commit: snd_pcm_file_mmap_commit,
434 * \brief Creates a new File PCM
435 * \param pcmp Returns created PCM handle
436 * \param name Name of PCM
437 * \param fname Filename (or NULL if file descriptor is available)
438 * \param fd File descriptor
439 * \param fmt File format ("raw" is supported only)
440 * \param slave Slave PCM handle
441 * \param close_slave When set, the slave PCM handle is closed with copy PCM
442 * \retval zero on success otherwise a negative error code
443 * \warning Using of this function might be dangerous in the sense
444 * of compatibility reasons. The prototype might be freely
447 int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
448 const char *fname, int fd, const char *fmt,
449 snd_pcm_t *slave, int close_slave)
452 snd_pcm_file_t *file;
453 snd_pcm_file_format_t format;
457 strcmp(fmt, "raw") == 0)
458 format = SND_PCM_FILE_FORMAT_RAW;
460 SNDERR("file format %s is unknown", fmt);
464 fd = open(fname, O_WRONLY|O_CREAT, 0666);
466 SYSERR("open %s failed", fname);
470 file = calloc(1, sizeof(snd_pcm_file_t));
478 file->fname = strdup(fname);
480 file->format = format;
482 file->close_slave = close_slave;
484 err = snd_pcm_new(&pcm, SND_PCM_TYPE_FILE, name, slave->stream, slave->mode);
491 pcm->ops = &snd_pcm_file_ops;
492 pcm->fast_ops = &snd_pcm_file_fast_ops;
493 pcm->private_data = file;
494 pcm->poll_fd = slave->poll_fd;
495 snd_pcm_link_hw_ptr(pcm, slave);
496 snd_pcm_link_appl_ptr(pcm, slave);
502 /*! \page pcm_plugins
504 \section pcm_plugins_file Plugin: File
506 This plugin stores contents of a PCM stream to file.
511 slave STR # Slave name
513 slave { # Slave definition
514 pcm STR # Slave PCM name
516 pcm { } # Slave PCM definition
520 file INT # File descriptor number
521 [format STR] # File format (only "raw" at the moment)
525 \subsection pcm_plugins_file_funcref Function reference
528 <LI>snd_pcm_file_open()
529 <LI>_snd_pcm_file_open()
535 * \brief Creates a new File PCM
536 * \param pcmp Returns created PCM handle
537 * \param name Name of PCM
538 * \param root Root configuration node
539 * \param conf Configuration node with File PCM description
540 * \param stream Stream type
541 * \param mode Stream mode
542 * \retval zero on success otherwise a negative error code
543 * \warning Using of this function might be dangerous in the sense
544 * of compatibility reasons. The prototype might be freely
547 int _snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
548 snd_config_t *root, snd_config_t *conf,
549 snd_pcm_stream_t stream, int mode)
551 snd_config_iterator_t i, next;
554 snd_config_t *slave = NULL, *sconf;
555 const char *fname = NULL;
556 const char *format = NULL;
558 snd_config_for_each(i, next, conf) {
559 snd_config_t *n = snd_config_iterator_entry(i);
561 if (snd_config_get_id(n, &id) < 0)
563 if (snd_pcm_conf_generic_id(id))
565 if (strcmp(id, "slave") == 0) {
569 if (strcmp(id, "format") == 0) {
570 err = snd_config_get_string(n, &format);
572 SNDERR("Invalid type for %s", id);
577 if (strcmp(id, "file") == 0) {
578 err = snd_config_get_string(n, &fname);
580 err = snd_config_get_integer(n, &fd);
582 SNDERR("Invalid type for %s", id);
588 SNDERR("Unknown field %s", id);
592 SNDERR("slave is not defined");
595 err = snd_pcm_slave_conf(root, slave, &sconf, 0);
598 if (!fname && fd < 0) {
599 snd_config_delete(sconf);
600 SNDERR("file is not defined");
603 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
604 snd_config_delete(sconf);
607 err = snd_pcm_file_open(pcmp, name, fname, fd, format, spcm, 1);
613 SND_DLSYM_BUILD_VERSION(_snd_pcm_file_open, SND_PCM_DLSYM_VERSION);