OSDN Git Service

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