Initial Import
[profile/ivi/alsa-lib.git] / src / pcm / pcm_file.c
1 /**
2  * \file pcm/pcm_file.c
3  * \ingroup PCM_Plugins
4  * \brief PCM File Plugin Interface
5  * \author Abramo Bagnara <abramo@alsa-project.org>
6  * \date 2000-2001
7  */
8 /*
9  *  PCM - File plugin
10  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
11  *
12  *
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.
17  *
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.
22  *
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
26  *
27  */
28   
29 #include <endian.h>
30 #include <byteswap.h>
31 #include <ctype.h>
32 #include <string.h>
33 #include "pcm_local.h"
34 #include "pcm_plugin.h"
35
36 #ifndef PIC
37 /* entry for static linking */
38 const char *_snd_module_pcm_file = "";
39 #endif
40
41 #ifndef DOC_HIDDEN
42
43 /* keys to be replaced by real values in the filename */
44 #define LEADING_KEY     '%'     /* i.e. %r, %c, %b ... */
45 #define RATE_KEY        'r'
46 #define CHANNELS_KEY    'c'
47 #define BWIDTH_KEY      'b'
48 #define FORMAT_KEY      'f'
49
50 /* maximum length of a value */
51 #define VALUE_MAXLEN    64
52
53 typedef enum _snd_pcm_file_format {
54         SND_PCM_FILE_FORMAT_RAW,
55         SND_PCM_FILE_FORMAT_WAV
56 } snd_pcm_file_format_t;
57
58 /* WAV format chunk */
59 struct wav_fmt {
60         short fmt;
61         short chan;
62         int rate;
63         int bps;
64         short bwidth;
65         short bits;
66 };
67
68 typedef struct {
69         snd_pcm_generic_t gen;
70         char *fname;
71         char *final_fname;
72         int trunc;
73         int perm;
74         int fd;
75         char *ifname;
76         int ifd;
77         int format;
78         snd_pcm_uframes_t appl_ptr;
79         snd_pcm_uframes_t file_ptr_bytes;
80         snd_pcm_uframes_t wbuf_size;
81         size_t wbuf_size_bytes;
82         size_t wbuf_used_bytes;
83         char *wbuf;
84         size_t rbuf_size_bytes;
85         size_t rbuf_used_bytes;
86         char *rbuf;
87         snd_pcm_channel_area_t *wbuf_areas;
88         size_t buffer_bytes;
89         struct wav_fmt wav_header;
90         size_t filelen;
91 } snd_pcm_file_t;
92
93 #if __BYTE_ORDER == __LITTLE_ENDIAN
94 #define TO_LE32(x)      (x)
95 #define TO_LE16(x)      (x)
96 #else
97 #define TO_LE32(x)      bswap_32(x)
98 #define TO_LE16(x)      bswap_16(x)
99 #endif
100
101 static int snd_pcm_file_append_value(char **string_p, char **index_ch_p,
102                 int *len_p, const char *value)
103 {
104         char *string, *index_ch;
105         int index, len, value_len;
106         /* input pointer values */
107         len = *(len_p);
108         string = *(string_p);
109         index_ch = *(index_ch_p);
110
111         value_len = strlen(value);
112         /* reallocation to accommodate the value */
113         index = index_ch - string;
114         len += value_len;
115         string = realloc(string, len + 1);
116         if (!string)
117                 return -ENOMEM;
118         index_ch = string + index;
119         /* concatenating the new value */
120         strcpy(index_ch, value);
121         index_ch += value_len;
122         /* return values */
123         *(len_p) = len;
124         *(string_p) = string;
125         *(index_ch_p) = index_ch;
126         return 0;
127 }
128
129 static int snd_pcm_file_replace_fname(snd_pcm_file_t *file, char **new_fname_p)
130 {
131         char value[VALUE_MAXLEN];
132         char *fname = file->fname;
133         char *new_fname = NULL;
134         char *old_last_ch, *old_index_ch, *new_index_ch;
135         int old_len, new_len, err;
136
137         snd_pcm_t *pcm = file->gen.slave;
138
139         /* we want to keep fname, const */
140         old_len = new_len = strlen(fname);
141         old_last_ch = fname + old_len - 1;
142         new_fname = malloc(new_len + 1);
143         if (!new_fname)
144                 return -ENOMEM;
145
146         old_index_ch = fname;   /* first character of the old name */
147         new_index_ch = new_fname;       /* first char of the new name */
148
149         while (old_index_ch <= old_last_ch) {
150                 if (*(old_index_ch) == LEADING_KEY &&
151                                 old_index_ch != old_last_ch) {
152                         /* is %, not last char, skipping and checking
153                          next char */
154                         switch (*(++old_index_ch)) {
155                         case RATE_KEY:
156                                 snprintf(value, sizeof(value), "%d",
157                                                 pcm->rate);
158                                 err = snd_pcm_file_append_value(&new_fname,
159                                         &new_index_ch, &new_len, value);
160                                 if (err < 0)
161                                         return err;
162                                 break;
163
164                         case CHANNELS_KEY:
165                                 snprintf(value, sizeof(value), "%d",
166                                                 pcm->channels);
167                                 err = snd_pcm_file_append_value(&new_fname,
168                                         &new_index_ch, &new_len, value);
169                                 if (err < 0)
170                                         return err;
171                                 break;
172
173                         case BWIDTH_KEY:
174                                 snprintf(value, sizeof(value), "%d",
175                                         pcm->frame_bits/pcm->channels);
176                                 err = snd_pcm_file_append_value(&new_fname,
177                                                 &new_index_ch, &new_len, value);
178                                 if (err < 0)
179                                         return err;
180                                 break;
181
182                         case FORMAT_KEY:
183                                 err = snd_pcm_file_append_value(&new_fname,
184                                         &new_index_ch, &new_len,
185                                         snd_pcm_format_name(pcm->format));
186                                 if (err < 0)
187                                         return err;
188                                 break;
189
190                         default:
191                                 /* non-key char, just copying */
192                                 *(new_index_ch++) = *(old_index_ch);
193                         }
194                         /* next old char */
195                         old_index_ch++;
196                 } else {
197                         /* plain copying, shifting both strings to next chars */
198                         *(new_index_ch++) = *(old_index_ch++);
199                 }
200         }
201         /* closing the new string */
202         *(new_index_ch) = '\0';
203         *(new_fname_p) = new_fname;
204         return 0;
205
206 }
207
208 static int snd_pcm_file_open_output_file(snd_pcm_file_t *file)
209 {
210         int err, fd;
211
212         /* fname can contain keys, generating final_fname */
213         err = snd_pcm_file_replace_fname(file, &(file->final_fname));
214         if (err < 0)
215                 return err;
216         /*printf("DEBUG - original fname: %s, final fname: %s\n",
217           file->fname, file->final_fname);*/
218
219         if (file->final_fname[0] == '|') {
220                 /* pipe mode */
221                 FILE *pipe;
222                 /* clearing */
223                 pipe = popen(file->final_fname + 1, "w");
224                 if (!pipe) {
225                         SYSERR("running %s for writing failed",
226                                         file->final_fname);
227                         return -errno;
228                 }
229                 fd = fileno(pipe);
230         } else {
231                 if (file->trunc)
232                         fd = open(file->final_fname, O_WRONLY|O_CREAT|O_TRUNC,
233                                         file->perm);
234                 else {
235                         fd = open(file->final_fname, O_WRONLY|O_CREAT|O_EXCL,
236                                         file->perm);
237                         if (fd < 0) {
238                                 char *tmpfname = NULL;
239                                 int idx, len;
240                                 len = strlen(file->final_fname) + 6;
241                                 tmpfname = malloc(len);
242                                 if (!tmpfname)
243                                         return -ENOMEM;
244                                 for (idx = 1; idx < 10000; idx++) {
245                                         snprintf(tmpfname, len,
246                                                 "%s.%04d", file->final_fname,
247                                                 idx);
248                                         fd = open(tmpfname,
249                                                         O_WRONLY|O_CREAT|O_EXCL,
250                                                         file->perm);
251                                         if (fd >= 0) {
252                                                 free(file->final_fname);
253                                                 file->final_fname = tmpfname;
254                                                 break;
255                                         }
256                                 }
257                                 if (fd < 0) {
258                                         SYSERR("open %s for writing failed",
259                                                         file->final_fname);
260                                         free(tmpfname);
261                                         return -errno;
262                                 }
263                         }
264                 }
265         }
266         file->fd = fd;
267         return 0;
268 }
269
270 static void setup_wav_header(snd_pcm_t *pcm, struct wav_fmt *fmt)
271 {
272         fmt->fmt = TO_LE16(0x01);
273         fmt->chan = TO_LE16(pcm->channels);
274         fmt->rate = TO_LE32(pcm->rate);
275         fmt->bwidth = pcm->frame_bits / 8;
276         fmt->bps = fmt->bwidth * pcm->rate;
277         fmt->bits = snd_pcm_format_width(pcm->format);
278         fmt->bps = TO_LE32(fmt->bps);
279         fmt->bwidth = TO_LE16(fmt->bwidth);
280         fmt->bits = TO_LE16(fmt->bits);
281 }
282
283 static int write_wav_header(snd_pcm_t *pcm)
284 {
285         snd_pcm_file_t *file = pcm->private_data;
286         static const char header[] = {
287                 'R', 'I', 'F', 'F',
288                 0x24, 0, 0, 0,
289                 'W', 'A', 'V', 'E',
290                 'f', 'm', 't', ' ',
291                 0x10, 0, 0, 0,
292         };
293         static const char header2[] = {
294                 'd', 'a', 't', 'a',
295                 0, 0, 0, 0
296         };
297         
298         setup_wav_header(pcm, &file->wav_header);
299
300         if (write(file->fd, header, sizeof(header)) != sizeof(header) ||
301             write(file->fd, &file->wav_header, sizeof(file->wav_header)) !=
302             sizeof(file->wav_header) ||
303             write(file->fd, header2, sizeof(header2)) != sizeof(header2)) {
304                 int err = errno;
305                 SYSERR("Write error.\n");
306                 return -err;
307         }
308         return 0;
309 }
310
311 /* fix up the length fields in WAV header */
312 static void fixup_wav_header(snd_pcm_t *pcm)
313 {
314         snd_pcm_file_t *file = pcm->private_data;
315         int len, ret;
316
317         /* RIFF length */
318         if (lseek(file->fd, 4, SEEK_SET) == 4) {
319                 len = (file->filelen + 0x24) > 0x7fffffff ?
320                         0x7fffffff : (int)(file->filelen + 0x24);
321                 len = TO_LE32(len);
322                 ret = write(file->fd, &len, 4);
323                 if (ret < 0)
324                         return;
325         }
326         /* data length */
327         if (lseek(file->fd, 0x28, SEEK_SET) == 0x28) {
328                 len = file->filelen > 0x7fffffff ?
329                         0x7fffffff : (int)file->filelen;
330                 len = TO_LE32(len);
331                 ret = write(file->fd, &len, 4);
332                 if (ret < 0)
333                         return;
334         }
335 }
336 #endif /* DOC_HIDDEN */
337
338
339
340 static void snd_pcm_file_write_bytes(snd_pcm_t *pcm, size_t bytes)
341 {
342         snd_pcm_file_t *file = pcm->private_data;
343         assert(bytes <= file->wbuf_used_bytes);
344
345         if (file->format == SND_PCM_FILE_FORMAT_WAV &&
346             !file->wav_header.fmt) {
347                 if (write_wav_header(pcm) < 0)
348                         return;
349         }
350
351         while (bytes > 0) {
352                 snd_pcm_sframes_t err;
353                 size_t n = bytes;
354                 size_t cont = file->wbuf_size_bytes - file->file_ptr_bytes;
355                 if (n > cont)
356                         n = cont;
357                 err = write(file->fd, file->wbuf + file->file_ptr_bytes, n);
358                 if (err < 0) {
359                         SYSERR("write failed");
360                         break;
361                 }
362                 bytes -= err;
363                 file->wbuf_used_bytes -= err;
364                 file->file_ptr_bytes += err;
365                 if (file->file_ptr_bytes == file->wbuf_size_bytes)
366                         file->file_ptr_bytes = 0;
367                 file->filelen += err;
368                 if ((snd_pcm_uframes_t)err != n)
369                         break;
370         }
371 }
372
373 static void snd_pcm_file_add_frames(snd_pcm_t *pcm, 
374                                     const snd_pcm_channel_area_t *areas,
375                                     snd_pcm_uframes_t offset,
376                                     snd_pcm_uframes_t frames)
377 {
378         snd_pcm_file_t *file = pcm->private_data;
379         while (frames > 0) {
380                 snd_pcm_uframes_t n = frames;
381                 snd_pcm_uframes_t cont = file->wbuf_size - file->appl_ptr;
382                 snd_pcm_uframes_t avail = file->wbuf_size - snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes);
383                 if (n > cont)
384                         n = cont;
385                 if (n > avail)
386                         n = avail;
387                 snd_pcm_areas_copy(file->wbuf_areas, file->appl_ptr, 
388                                    areas, offset,
389                                    pcm->channels, n, pcm->format);
390                 frames -= n;
391                 offset += n;
392                 file->appl_ptr += n;
393                 if (file->appl_ptr == file->wbuf_size)
394                         file->appl_ptr = 0;
395                 file->wbuf_used_bytes += snd_pcm_frames_to_bytes(pcm, n);
396                 if (file->wbuf_used_bytes > file->buffer_bytes)
397                         snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes - file->buffer_bytes);
398                 assert(file->wbuf_used_bytes < file->wbuf_size_bytes);
399         }
400 }
401
402 static int snd_pcm_file_close(snd_pcm_t *pcm)
403 {
404         snd_pcm_file_t *file = pcm->private_data;
405         if (file->fname) {
406                 if (file->wav_header.fmt)
407                         fixup_wav_header(pcm);
408                 free((void *)file->fname);
409                 close(file->fd);
410         }
411         if (file->ifname) {
412                 free((void *)file->ifname);
413                 close(file->ifd);
414         }
415         return snd_pcm_generic_close(pcm);
416 }
417
418 static int snd_pcm_file_reset(snd_pcm_t *pcm)
419 {
420         snd_pcm_file_t *file = pcm->private_data;
421         int err = snd_pcm_reset(file->gen.slave);
422         if (err >= 0) {
423                 /* FIXME: Questionable here */
424                 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
425                 assert(file->wbuf_used_bytes == 0);
426         }
427         return err;
428 }
429
430 static int snd_pcm_file_drop(snd_pcm_t *pcm)
431 {
432         snd_pcm_file_t *file = pcm->private_data;
433         int err = snd_pcm_drop(file->gen.slave);
434         if (err >= 0) {
435                 /* FIXME: Questionable here */
436                 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
437                 assert(file->wbuf_used_bytes == 0);
438         }
439         return err;
440 }
441
442 static int snd_pcm_file_drain(snd_pcm_t *pcm)
443 {
444         snd_pcm_file_t *file = pcm->private_data;
445         int err = snd_pcm_drain(file->gen.slave);
446         if (err >= 0) {
447                 snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
448                 assert(file->wbuf_used_bytes == 0);
449         }
450         return err;
451 }
452
453 static snd_pcm_sframes_t snd_pcm_file_rewindable(snd_pcm_t *pcm)
454 {
455         snd_pcm_file_t *file = pcm->private_data;
456         snd_pcm_sframes_t res = snd_pcm_rewindable(pcm);
457         snd_pcm_sframes_t n = snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes);
458         if (res > n)
459                 res = n;
460         return res;
461 }
462
463 static snd_pcm_sframes_t snd_pcm_file_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
464 {
465         snd_pcm_file_t *file = pcm->private_data;
466         snd_pcm_sframes_t err;
467         snd_pcm_uframes_t n;
468         
469         n = snd_pcm_frames_to_bytes(pcm, frames);
470         if (n > file->wbuf_used_bytes)
471                 frames = snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes);
472         err = snd_pcm_rewind(file->gen.slave, frames);
473         if (err > 0) {
474                 file->appl_ptr = (file->appl_ptr - err + file->wbuf_size) % file->wbuf_size;
475                 n = snd_pcm_frames_to_bytes(pcm, err);
476                 file->wbuf_used_bytes -= n;
477         }
478         return err;
479 }
480
481 static snd_pcm_sframes_t snd_pcm_file_forwardable(snd_pcm_t *pcm)
482 {
483         snd_pcm_file_t *file = pcm->private_data;
484         snd_pcm_sframes_t res = snd_pcm_forwardable(pcm);
485         snd_pcm_sframes_t n = snd_pcm_bytes_to_frames(pcm, file->wbuf_size_bytes - file->wbuf_used_bytes);
486         if (res > n)
487                 res = n;
488         return res;
489 }
490
491 static snd_pcm_sframes_t snd_pcm_file_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
492 {
493         snd_pcm_file_t *file = pcm->private_data;
494         snd_pcm_sframes_t err;
495         snd_pcm_uframes_t n;
496         
497         n = snd_pcm_frames_to_bytes(pcm, frames);
498         if (file->wbuf_used_bytes + n > file->wbuf_size_bytes)
499                 frames = snd_pcm_bytes_to_frames(pcm, file->wbuf_size_bytes - file->wbuf_used_bytes);
500         err = INTERNAL(snd_pcm_forward)(file->gen.slave, frames);
501         if (err > 0) {
502                 file->appl_ptr = (file->appl_ptr + err) % file->wbuf_size;
503                 n = snd_pcm_frames_to_bytes(pcm, err);
504                 file->wbuf_used_bytes += n;
505         }
506         return err;
507 }
508
509 static snd_pcm_sframes_t snd_pcm_file_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
510 {
511         snd_pcm_file_t *file = pcm->private_data;
512         snd_pcm_channel_area_t areas[pcm->channels];
513         snd_pcm_sframes_t n = snd_pcm_writei(file->gen.slave, buffer, size);
514         if (n > 0) {
515                 snd_pcm_areas_from_buf(pcm, areas, (void*) buffer);
516                 snd_pcm_file_add_frames(pcm, areas, 0, n);
517         }
518         return n;
519 }
520
521 static snd_pcm_sframes_t snd_pcm_file_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
522 {
523         snd_pcm_file_t *file = pcm->private_data;
524         snd_pcm_channel_area_t areas[pcm->channels];
525         snd_pcm_sframes_t n = snd_pcm_writen(file->gen.slave, bufs, size);
526         if (n > 0) {
527                 snd_pcm_areas_from_bufs(pcm, areas, bufs);
528                 snd_pcm_file_add_frames(pcm, areas, 0, n);
529         }
530         return n;
531 }
532
533 static snd_pcm_sframes_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
534 {
535         snd_pcm_file_t *file = pcm->private_data;
536         snd_pcm_channel_area_t areas[pcm->channels];
537         snd_pcm_sframes_t n;
538
539         n = snd_pcm_readi(file->gen.slave, buffer, size);
540         if (n <= 0)
541                 return n;
542         if (file->ifd >= 0) {
543                 n = read(file->ifd, buffer, n * pcm->frame_bits / 8);
544                 if (n < 0)
545                         return n;
546                 return n * 8 / pcm->frame_bits;
547         }
548         snd_pcm_areas_from_buf(pcm, areas, buffer);
549         snd_pcm_file_add_frames(pcm, areas, 0, n);
550         return n;
551 }
552
553 static snd_pcm_sframes_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
554 {
555         snd_pcm_file_t *file = pcm->private_data;
556         snd_pcm_channel_area_t areas[pcm->channels];
557         snd_pcm_sframes_t n;
558
559         if (file->ifd >= 0) {
560                 SNDERR("DEBUG: Noninterleaved read not yet implemented.\n");
561                 return 0;       /* TODO: Noninterleaved read */
562         }
563
564         n = snd_pcm_readn(file->gen.slave, bufs, size);
565         if (n > 0) {
566                 snd_pcm_areas_from_bufs(pcm, areas, bufs);
567                 snd_pcm_file_add_frames(pcm, areas, 0, n);
568         }
569         return n;
570 }
571
572 static snd_pcm_sframes_t snd_pcm_file_mmap_commit(snd_pcm_t *pcm,
573                                                   snd_pcm_uframes_t offset,
574                                                   snd_pcm_uframes_t size)
575 {
576         snd_pcm_file_t *file = pcm->private_data;
577         snd_pcm_uframes_t ofs;
578         snd_pcm_uframes_t siz = size;
579         const snd_pcm_channel_area_t *areas;
580         snd_pcm_sframes_t result;
581
582         snd_pcm_mmap_begin(file->gen.slave, &areas, &ofs, &siz);
583         assert(ofs == offset && siz == size);
584         result = snd_pcm_mmap_commit(file->gen.slave, ofs, siz);
585         if (result > 0)
586                 snd_pcm_file_add_frames(pcm, areas, ofs, result);
587         return result;
588 }
589
590 static int snd_pcm_file_hw_free(snd_pcm_t *pcm)
591 {
592         snd_pcm_file_t *file = pcm->private_data;
593         free(file->wbuf);
594         free(file->wbuf_areas);
595         file->wbuf = NULL;
596         file->wbuf_areas = NULL;
597         return snd_pcm_hw_free(file->gen.slave);
598 }
599
600 static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
601 {
602         snd_pcm_file_t *file = pcm->private_data;
603         unsigned int channel;
604         snd_pcm_t *slave = file->gen.slave;
605         int err = _snd_pcm_hw_params(slave, params);
606         if (err < 0)
607                 return err;
608         file->buffer_bytes = snd_pcm_frames_to_bytes(slave, slave->buffer_size);
609         file->wbuf_size = slave->buffer_size * 2;
610         file->wbuf_size_bytes = snd_pcm_frames_to_bytes(slave, file->wbuf_size);
611         file->wbuf_used_bytes = 0;
612         assert(!file->wbuf);
613         file->wbuf = malloc(file->wbuf_size_bytes);
614         if (file->wbuf == NULL) {
615                 snd_pcm_file_hw_free(pcm);
616                 return -ENOMEM;
617         }
618         file->wbuf_areas = malloc(sizeof(*file->wbuf_areas) * slave->channels);
619         if (file->wbuf_areas == NULL) {
620                 snd_pcm_file_hw_free(pcm);
621                 return -ENOMEM;
622         }
623         file->appl_ptr = file->file_ptr_bytes = 0;
624         for (channel = 0; channel < slave->channels; ++channel) {
625                 snd_pcm_channel_area_t *a = &file->wbuf_areas[channel];
626                 a->addr = file->wbuf;
627                 a->first = slave->sample_bits * channel;
628                 a->step = slave->frame_bits;
629         }
630         if (file->fd < 0) {
631                 err = snd_pcm_file_open_output_file(file);
632                 if (err < 0) {
633                         SYSERR("failed opening output file %s", file->fname);
634                         return err;
635                 }
636         }
637         return 0;
638 }
639
640 static void snd_pcm_file_dump(snd_pcm_t *pcm, snd_output_t *out)
641 {
642         snd_pcm_file_t *file = pcm->private_data;
643         if (file->fname)
644                 snd_output_printf(out, "File PCM (file=%s)\n", file->fname);
645         else
646                 snd_output_printf(out, "File PCM (fd=%d)\n", file->fd);
647         if (file->final_fname)
648                 snd_output_printf(out, "Final file PCM (file=%s)\n",
649                                 file->final_fname);
650
651         if (pcm->setup) {
652                 snd_output_printf(out, "Its setup is:\n");
653                 snd_pcm_dump_setup(pcm, out);
654         }
655         snd_output_printf(out, "Slave: ");
656         snd_pcm_dump(file->gen.slave, out);
657 }
658
659 static const snd_pcm_ops_t snd_pcm_file_ops = {
660         .close = snd_pcm_file_close,
661         .info = snd_pcm_generic_info,
662         .hw_refine = snd_pcm_generic_hw_refine,
663         .hw_params = snd_pcm_file_hw_params,
664         .hw_free = snd_pcm_file_hw_free,
665         .sw_params = snd_pcm_generic_sw_params,
666         .channel_info = snd_pcm_generic_channel_info,
667         .dump = snd_pcm_file_dump,
668         .nonblock = snd_pcm_generic_nonblock,
669         .async = snd_pcm_generic_async,
670         .mmap = snd_pcm_generic_mmap,
671         .munmap = snd_pcm_generic_munmap,
672 };
673
674 static const snd_pcm_fast_ops_t snd_pcm_file_fast_ops = {
675         .status = snd_pcm_generic_status,
676         .state = snd_pcm_generic_state,
677         .hwsync = snd_pcm_generic_hwsync,
678         .delay = snd_pcm_generic_delay,
679         .prepare = snd_pcm_generic_prepare,
680         .reset = snd_pcm_file_reset,
681         .start = snd_pcm_generic_start,
682         .drop = snd_pcm_file_drop,
683         .drain = snd_pcm_file_drain,
684         .pause = snd_pcm_generic_pause,
685         .rewindable = snd_pcm_file_rewindable,
686         .rewind = snd_pcm_file_rewind,
687         .forwardable = snd_pcm_file_forwardable,
688         .forward = snd_pcm_file_forward,
689         .resume = snd_pcm_generic_resume,
690         .link = snd_pcm_generic_link,
691         .link_slaves = snd_pcm_generic_link_slaves,
692         .unlink = snd_pcm_generic_unlink,
693         .writei = snd_pcm_file_writei,
694         .writen = snd_pcm_file_writen,
695         .readi = snd_pcm_file_readi,
696         .readn = snd_pcm_file_readn,
697         .avail_update = snd_pcm_generic_avail_update,
698         .mmap_commit = snd_pcm_file_mmap_commit,
699         .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
700         .poll_descriptors = snd_pcm_generic_poll_descriptors,
701         .poll_revents = snd_pcm_generic_poll_revents,
702 };
703
704 /**
705  * \brief Creates a new File PCM
706  * \param pcmp Returns created PCM handle
707  * \param name Name of PCM
708  * \param fname Output filename (or NULL if file descriptor fd is available)
709  * \param fd Output file descriptor
710  * \param ifname Input filename (or NULL if file descriptor ifd is available)
711  * \param ifd Input file descriptor (if (ifd < 0) && (ifname == NULL), no input
712  *            redirection will be performed)
713  * \param trunc Truncate the file if it already exists
714  * \param fmt File format ("raw" or "wav" are available)
715  * \param perm File permission
716  * \param slave Slave PCM handle
717  * \param close_slave When set, the slave PCM handle is closed with copy PCM
718  * \retval zero on success otherwise a negative error code
719  * \warning Using of this function might be dangerous in the sense
720  *          of compatibility reasons. The prototype might be freely
721  *          changed in future.
722  */
723 int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
724                       const char *fname, int fd, const char *ifname, int ifd,
725                       int trunc,
726                       const char *fmt, int perm, snd_pcm_t *slave, int close_slave)
727 {
728         snd_pcm_t *pcm;
729         snd_pcm_file_t *file;
730         snd_pcm_file_format_t format;
731         struct timespec timespec;
732         int err;
733
734         assert(pcmp);
735         if (fmt == NULL ||
736             strcmp(fmt, "raw") == 0)
737                 format = SND_PCM_FILE_FORMAT_RAW;
738         else if (!strcmp(fmt, "wav"))
739                 format = SND_PCM_FILE_FORMAT_WAV;
740         else {
741                 SNDERR("file format %s is unknown", fmt);
742                 return -EINVAL;
743         }
744         file = calloc(1, sizeof(snd_pcm_file_t));
745         if (!file) {
746                 return -ENOMEM;
747         }
748
749         /* opening output fname is delayed until writing,
750          when PCM params are known */
751         if (fname)
752                 file->fname = strdup(fname);
753         file->trunc = trunc;
754         file->perm = perm;
755
756         if (ifname) {
757                 ifd = open(ifname, O_RDONLY);   /* TODO: mind blocking mode */
758                 if (ifd < 0) {
759                         SYSERR("open %s for reading failed", ifname);
760                         free(file);
761                         return -errno;
762                 }
763                 file->ifname = strdup(ifname);
764         }
765         file->fd = fd;
766         file->ifd = ifd;
767         file->format = format;
768         file->gen.slave = slave;
769         file->gen.close_slave = close_slave;
770
771         err = snd_pcm_new(&pcm, SND_PCM_TYPE_FILE, name, slave->stream, slave->mode);
772         if (err < 0) {
773                 free(file->fname);
774                 free(file);
775                 return err;
776         }
777         pcm->ops = &snd_pcm_file_ops;
778         pcm->fast_ops = &snd_pcm_file_fast_ops;
779         pcm->private_data = file;
780         pcm->poll_fd = slave->poll_fd;
781         pcm->poll_events = slave->poll_events;
782         pcm->mmap_shadow = 1;
783 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
784         pcm->monotonic = clock_gettime(CLOCK_MONOTONIC, &timespec) == 0;
785 #else
786         pcm->monotonic = 0;
787 #endif
788         snd_pcm_link_hw_ptr(pcm, slave);
789         snd_pcm_link_appl_ptr(pcm, slave);
790         *pcmp = pcm;
791         return 0;
792 }
793
794 /*! \page pcm_plugins
795
796 \section pcm_plugins_file Plugin: File
797
798 This plugin stores contents of a PCM stream to file or pipes the stream
799 to a command, and optionally uses an existing file as an input data source
800 (i.e., "virtual mic")
801
802 \code
803 pcm.name {
804         type file               # File PCM
805         slave STR               # Slave name
806         # or
807         slave {                 # Slave definition
808                 pcm STR         # Slave PCM name
809                 # or
810                 pcm { }         # Slave PCM definition
811         }
812         file STR                # Output filename (or shell command the stream
813                                 # will be piped to if STR starts with the pipe
814                                 # char).
815                                 # STR can contain format keys, replaced by
816                                 # real values corresponding to the stream:
817                                 # %r    rate (replaced with: 48000)
818                                 # %c    channels (replaced with: 2)
819                                 # %b    bits per sample (replaced with: 16)
820                                 # %f    sample format string
821                                 #                       (replaced with: S16_LE)
822                                 # %%    replaced with %
823         or
824         file INT                # Output file descriptor number
825         infile STR              # Input filename - only raw format
826         or
827         infile INT              # Input file descriptor number
828         [format STR]            # File format ("raw" or "wav")
829         [perm INT]              # Output file permission (octal, def. 0600)
830 }
831 \endcode
832
833 \subsection pcm_plugins_file_funcref Function reference
834
835 <UL>
836   <LI>snd_pcm_file_open()
837   <LI>_snd_pcm_file_open()
838 </UL>
839
840 */
841
842 /**
843  * \brief Creates a new File PCM
844  * \param pcmp Returns created PCM handle
845  * \param name Name of PCM
846  * \param root Root configuration node
847  * \param conf Configuration node with File PCM description
848  * \param stream Stream type
849  * \param mode Stream mode
850  * \retval zero on success otherwise a negative error code
851  * \warning Using of this function might be dangerous in the sense
852  *          of compatibility reasons. The prototype might be freely
853  *          changed in future.
854  */
855 int _snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
856                        snd_config_t *root, snd_config_t *conf, 
857                        snd_pcm_stream_t stream, int mode)
858 {
859         snd_config_iterator_t i, next;
860         int err;
861         snd_pcm_t *spcm;
862         snd_config_t *slave = NULL, *sconf;
863         const char *fname = NULL, *ifname = NULL;
864         const char *format = NULL;
865         long fd = -1, ifd = -1, trunc = 1;
866         long perm = 0600;
867         snd_config_for_each(i, next, conf) {
868                 snd_config_t *n = snd_config_iterator_entry(i);
869                 const char *id;
870                 if (snd_config_get_id(n, &id) < 0)
871                         continue;
872                 if (snd_pcm_conf_generic_id(id))
873                         continue;
874                 if (strcmp(id, "slave") == 0) {
875                         slave = n;
876                         continue;
877                 }
878                 if (strcmp(id, "format") == 0) {
879                         err = snd_config_get_string(n, &format);
880                         if (err < 0) {
881                                 SNDERR("Invalid type for %s", id);
882                                 return -EINVAL;
883                         }
884                         continue;
885                 }
886                 if (strcmp(id, "file") == 0) {
887                         err = snd_config_get_string(n, &fname);
888                         if (err < 0) {
889                                 err = snd_config_get_integer(n, &fd);
890                                 if (err < 0) {
891                                         SNDERR("Invalid type for %s", id);
892                                         return -EINVAL;
893                                 }
894                         }
895                         continue;
896                 }
897                 if (strcmp(id, "infile") == 0) {
898                         err = snd_config_get_string(n, &ifname);
899                         if (err < 0) {
900                                 err = snd_config_get_integer(n, &ifd);
901                                 if (err < 0) {
902                                         SNDERR("Invalid type for %s", id);
903                                         return -EINVAL;
904                                 }
905                         }
906                         continue;
907                 }
908                 if (strcmp(id, "perm") == 0) {
909                         err = snd_config_get_integer(n, &perm);
910                         if (err < 0) {
911                                 SNDERR("Invalid type for %s", id);
912                                 return err;
913                         }
914                         if ((perm & ~0777) != 0) {
915                                 SNDERR("The field perm must be a valid file permission");
916                                 return -EINVAL;
917                         }
918                         continue;
919                 }
920                 if (strcmp(id, "truncate") == 0) {
921                         err = snd_config_get_bool(n);
922                         if (err < 0)
923                                 return -EINVAL;
924                         trunc = err;
925                         continue;
926                 }
927                 SNDERR("Unknown field %s", id);
928                 return -EINVAL;
929         }
930         if (!format) {
931                 snd_config_t *n;
932                 /* read defaults */
933                 if (snd_config_search(root, "defaults.pcm.file_format", &n) >= 0) {
934                         err = snd_config_get_string(n, &format);
935                         if (err < 0) {
936                                 SNDERR("Invalid file format");
937                                 return -EINVAL;
938                         }
939                 }
940         }
941         if (!slave) {
942                 SNDERR("slave is not defined");
943                 return -EINVAL;
944         }
945         err = snd_pcm_slave_conf(root, slave, &sconf, 0);
946         if (err < 0)
947                 return err;
948         if ((!fname || strlen(fname) == 0) && fd < 0 && !ifname) {
949                 snd_config_delete(sconf);
950                 SNDERR("file is not defined");
951                 return -EINVAL;
952         }
953         err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
954         snd_config_delete(sconf);
955         if (err < 0)
956                 return err;
957         err = snd_pcm_file_open(pcmp, name, fname, fd, ifname, ifd,
958                                 trunc, format, perm, spcm, 1);
959         if (err < 0)
960                 snd_pcm_close(spcm);
961         return err;
962 }
963 #ifndef DOC_HIDDEN
964 SND_DLSYM_BUILD_VERSION(_snd_pcm_file_open, SND_PCM_DLSYM_VERSION);
965 #endif