• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &timespec) == 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