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