Blame src/pcm/pcm_file.c

Packit Service db8eaa
/**
Packit Service db8eaa
 * \file pcm/pcm_file.c
Packit Service db8eaa
 * \ingroup PCM_Plugins
Packit Service db8eaa
 * \brief PCM File Plugin Interface
Packit Service db8eaa
 * \author Abramo Bagnara <abramo@alsa-project.org>
Packit Service db8eaa
 * \date 2000-2001
Packit Service db8eaa
 */
Packit Service db8eaa
/*
Packit Service db8eaa
 *  PCM - File plugin
Packit Service db8eaa
 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
Packit Service db8eaa
 *
Packit Service db8eaa
 *
Packit Service db8eaa
 *   This library is free software; you can redistribute it and/or modify
Packit Service db8eaa
 *   it under the terms of the GNU Lesser General Public License as
Packit Service db8eaa
 *   published by the Free Software Foundation; either version 2.1 of
Packit Service db8eaa
 *   the License, or (at your option) any later version.
Packit Service db8eaa
 *
Packit Service db8eaa
 *   This program is distributed in the hope that it will be useful,
Packit Service db8eaa
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service db8eaa
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service db8eaa
 *   GNU Lesser General Public License for more details.
Packit Service db8eaa
 *
Packit Service db8eaa
 *   You should have received a copy of the GNU Lesser General Public
Packit Service db8eaa
 *   License along with this library; if not, write to the Free Software
Packit Service db8eaa
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit Service db8eaa
 *
Packit Service db8eaa
 */
Packit Service db8eaa
  
Packit Service db8eaa
#include "bswap.h"
Packit Service db8eaa
#include <ctype.h>
Packit Service db8eaa
#include <string.h>
Packit Service db8eaa
#include "pcm_local.h"
Packit Service db8eaa
#include "pcm_plugin.h"
Packit Service db8eaa
Packit Service db8eaa
#ifndef PIC
Packit Service db8eaa
/* entry for static linking */
Packit Service db8eaa
const char *_snd_module_pcm_file = "";
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
#ifndef DOC_HIDDEN
Packit Service db8eaa
Packit Service db8eaa
/* keys to be replaced by real values in the filename */
Packit Service db8eaa
#define LEADING_KEY	'%'	/* i.e. %r, %c, %b ... */
Packit Service db8eaa
#define RATE_KEY	'r'
Packit Service db8eaa
#define CHANNELS_KEY	'c'
Packit Service db8eaa
#define BWIDTH_KEY	'b'
Packit Service db8eaa
#define FORMAT_KEY	'f'
Packit Service db8eaa
Packit Service db8eaa
/* maximum length of a value */
Packit Service db8eaa
#define VALUE_MAXLEN	64
Packit Service db8eaa
Packit Service db8eaa
typedef enum _snd_pcm_file_format {
Packit Service db8eaa
	SND_PCM_FILE_FORMAT_RAW,
Packit Service db8eaa
	SND_PCM_FILE_FORMAT_WAV
Packit Service db8eaa
} snd_pcm_file_format_t;
Packit Service db8eaa
Packit Service db8eaa
/* WAV format chunk */
Packit Service db8eaa
struct wav_fmt {
Packit Service db8eaa
	short fmt;
Packit Service db8eaa
	short chan;
Packit Service db8eaa
	int rate;
Packit Service db8eaa
	int bps;
Packit Service db8eaa
	short bwidth;
Packit Service db8eaa
	short bits;
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
typedef struct {
Packit Service db8eaa
	snd_pcm_generic_t gen;
Packit Service db8eaa
	char *fname;
Packit Service db8eaa
	char *final_fname;
Packit Service db8eaa
	int trunc;
Packit Service db8eaa
	int perm;
Packit Service db8eaa
	int fd;
Packit Service db8eaa
	FILE *pipe;
Packit Service db8eaa
	char *ifname;
Packit Service db8eaa
	int ifd;
Packit Service db8eaa
	int format;
Packit Service db8eaa
	snd_pcm_uframes_t appl_ptr;
Packit Service db8eaa
	snd_pcm_uframes_t file_ptr_bytes;
Packit Service db8eaa
	snd_pcm_uframes_t wbuf_size;
Packit Service db8eaa
	snd_pcm_uframes_t rbuf_size;
Packit Service db8eaa
	size_t wbuf_size_bytes;
Packit Service db8eaa
	size_t wbuf_used_bytes;
Packit Service db8eaa
	char *wbuf;
Packit Service db8eaa
	size_t rbuf_size_bytes;
Packit Service db8eaa
	size_t rbuf_used_bytes;
Packit Service db8eaa
	char *rbuf;
Packit Service db8eaa
	snd_pcm_channel_area_t *wbuf_areas;
Packit Service db8eaa
	size_t buffer_bytes;
Packit Service db8eaa
	struct wav_fmt wav_header;
Packit Service db8eaa
	size_t filelen;
Packit Service db8eaa
	char ifmmap_overwritten;
Packit Service db8eaa
} snd_pcm_file_t;
Packit Service db8eaa
Packit Service db8eaa
#if __BYTE_ORDER == __LITTLE_ENDIAN
Packit Service db8eaa
#define TO_LE32(x)	(x)
Packit Service db8eaa
#define TO_LE16(x)	(x)
Packit Service db8eaa
#else
Packit Service db8eaa
#define TO_LE32(x)	bswap_32(x)
Packit Service db8eaa
#define TO_LE16(x)	bswap_16(x)
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
static ssize_t safe_write(int fd, const void *buf, size_t len)
Packit Service db8eaa
{
Packit Service db8eaa
	while (1) {
Packit Service db8eaa
		ssize_t r = write(fd, buf, len);
Packit Service db8eaa
		if (r < 0) {
Packit Service db8eaa
			if (errno == EINTR)
Packit Service db8eaa
				continue;
Packit Service db8eaa
			if (errno == EPIPE)
Packit Service db8eaa
				return -EIO;
Packit Service db8eaa
			return -errno;
Packit Service db8eaa
		}
Packit Service db8eaa
		return r;
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_file_append_value(char **string_p, char **index_ch_p,
Packit Service db8eaa
		int *len_p, const char *value)
Packit Service db8eaa
{
Packit Service db8eaa
	char *string, *index_ch;
Packit Service db8eaa
	int index, len, value_len;
Packit Service db8eaa
	/* input pointer values */
Packit Service db8eaa
	len = *(len_p);
Packit Service db8eaa
	string = *(string_p);
Packit Service db8eaa
	index_ch = *(index_ch_p);
Packit Service db8eaa
Packit Service db8eaa
	value_len = strlen(value);
Packit Service db8eaa
	/* reallocation to accommodate the value */
Packit Service db8eaa
	index = index_ch - string;
Packit Service db8eaa
	len += value_len;
Packit Service db8eaa
	string = realloc(string, len + 1);
Packit Service db8eaa
	if (!string)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	index_ch = string + index;
Packit Service db8eaa
	/* concatenating the new value */
Packit Service db8eaa
	strcpy(index_ch, value);
Packit Service db8eaa
	index_ch += value_len;
Packit Service db8eaa
	/* return values */
Packit Service db8eaa
	*(len_p) = len;
Packit Service db8eaa
	*(string_p) = string;
Packit Service db8eaa
	*(index_ch_p) = index_ch;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_file_replace_fname(snd_pcm_file_t *file, char **new_fname_p)
Packit Service db8eaa
{
Packit Service db8eaa
	char value[VALUE_MAXLEN];
Packit Service db8eaa
	char *fname = file->fname;
Packit Service db8eaa
	char *new_fname = NULL;
Packit Service db8eaa
	char *old_last_ch, *old_index_ch, *new_index_ch;
Packit Service db8eaa
	int old_len, new_len, err;
Packit Service db8eaa
Packit Service db8eaa
	snd_pcm_t *pcm = file->gen.slave;
Packit Service db8eaa
Packit Service db8eaa
	/* we want to keep fname, const */
Packit Service db8eaa
	old_len = new_len = strlen(fname);
Packit Service db8eaa
	old_last_ch = fname + old_len - 1;
Packit Service db8eaa
	new_fname = malloc(new_len + 1);
Packit Service db8eaa
	if (!new_fname)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
Packit Service db8eaa
	old_index_ch = fname;	/* first character of the old name */
Packit Service db8eaa
	new_index_ch = new_fname;	/* first char of the new name */
Packit Service db8eaa
Packit Service db8eaa
	while (old_index_ch <= old_last_ch) {
Packit Service db8eaa
		if (*(old_index_ch) == LEADING_KEY &&
Packit Service db8eaa
				old_index_ch != old_last_ch) {
Packit Service db8eaa
			/* is %, not last char, skipping and checking
Packit Service db8eaa
			 next char */
Packit Service db8eaa
			switch (*(++old_index_ch)) {
Packit Service db8eaa
			case RATE_KEY:
Packit Service db8eaa
				snprintf(value, sizeof(value), "%d",
Packit Service db8eaa
						pcm->rate);
Packit Service db8eaa
				err = snd_pcm_file_append_value(&new_fname,
Packit Service db8eaa
					&new_index_ch, &new_len, value);
Packit Service db8eaa
				if (err < 0)
Packit Service db8eaa
					return err;
Packit Service db8eaa
				break;
Packit Service db8eaa
Packit Service db8eaa
			case CHANNELS_KEY:
Packit Service db8eaa
				snprintf(value, sizeof(value), "%d",
Packit Service db8eaa
						pcm->channels);
Packit Service db8eaa
				err = snd_pcm_file_append_value(&new_fname,
Packit Service db8eaa
					&new_index_ch, &new_len, value);
Packit Service db8eaa
				if (err < 0)
Packit Service db8eaa
					return err;
Packit Service db8eaa
				break;
Packit Service db8eaa
Packit Service db8eaa
			case BWIDTH_KEY:
Packit Service db8eaa
				snprintf(value, sizeof(value), "%d",
Packit Service db8eaa
					pcm->frame_bits/pcm->channels);
Packit Service db8eaa
				err = snd_pcm_file_append_value(&new_fname,
Packit Service db8eaa
						&new_index_ch, &new_len, value);
Packit Service db8eaa
				if (err < 0)
Packit Service db8eaa
					return err;
Packit Service db8eaa
				break;
Packit Service db8eaa
Packit Service db8eaa
			case FORMAT_KEY:
Packit Service db8eaa
				err = snd_pcm_file_append_value(&new_fname,
Packit Service db8eaa
					&new_index_ch, &new_len,
Packit Service db8eaa
					snd_pcm_format_name(pcm->format));
Packit Service db8eaa
				if (err < 0)
Packit Service db8eaa
					return err;
Packit Service db8eaa
				break;
Packit Service db8eaa
Packit Service db8eaa
			default:
Packit Service db8eaa
				/* non-key char, just copying */
Packit Service db8eaa
				*(new_index_ch++) = *(old_index_ch);
Packit Service db8eaa
			}
Packit Service db8eaa
			/* next old char */
Packit Service db8eaa
			old_index_ch++;
Packit Service db8eaa
		} else {
Packit Service db8eaa
			/* plain copying, shifting both strings to next chars */
Packit Service db8eaa
			*(new_index_ch++) = *(old_index_ch++);
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	/* closing the new string */
Packit Service db8eaa
	*(new_index_ch) = '\0';
Packit Service db8eaa
	*(new_fname_p) = new_fname;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_file_open_output_file(snd_pcm_file_t *file)
Packit Service db8eaa
{
Packit Service db8eaa
	int err, fd;
Packit Service db8eaa
Packit Service db8eaa
	/* fname can contain keys, generating final_fname */
Packit Service db8eaa
	err = snd_pcm_file_replace_fname(file, &(file->final_fname));
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	/*printf("DEBUG - original fname: %s, final fname: %s\n",
Packit Service db8eaa
	  file->fname, file->final_fname);*/
Packit Service db8eaa
Packit Service db8eaa
	if (file->final_fname[0] == '|') {
Packit Service db8eaa
		/* pipe mode */
Packit Service db8eaa
		FILE *pipe;
Packit Service db8eaa
		/* clearing */
Packit Service db8eaa
		pipe = popen(file->final_fname + 1, "w");
Packit Service db8eaa
		if (!pipe) {
Packit Service db8eaa
			SYSERR("running %s for writing failed",
Packit Service db8eaa
					file->final_fname);
Packit Service db8eaa
			return -errno;
Packit Service db8eaa
		}
Packit Service db8eaa
		fd = fileno(pipe);
Packit Service db8eaa
		file->pipe = pipe;
Packit Service db8eaa
	} else {
Packit Service db8eaa
		file->pipe = NULL;
Packit Service db8eaa
		if (file->trunc)
Packit Service db8eaa
			fd = open(file->final_fname, O_WRONLY|O_CREAT|O_TRUNC,
Packit Service db8eaa
					file->perm);
Packit Service db8eaa
		else {
Packit Service db8eaa
			fd = open(file->final_fname, O_WRONLY|O_CREAT|O_EXCL,
Packit Service db8eaa
					file->perm);
Packit Service db8eaa
			if (fd < 0) {
Packit Service db8eaa
				char *tmpfname = NULL;
Packit Service db8eaa
				int idx, len;
Packit Service db8eaa
				len = strlen(file->final_fname) + 6;
Packit Service db8eaa
				tmpfname = malloc(len);
Packit Service db8eaa
				if (!tmpfname)
Packit Service db8eaa
					return -ENOMEM;
Packit Service db8eaa
				for (idx = 1; idx < 10000; idx++) {
Packit Service db8eaa
					snprintf(tmpfname, len,
Packit Service db8eaa
						"%s.%04d", file->final_fname,
Packit Service db8eaa
						idx);
Packit Service db8eaa
					fd = open(tmpfname,
Packit Service db8eaa
							O_WRONLY|O_CREAT|O_EXCL,
Packit Service db8eaa
							file->perm);
Packit Service db8eaa
					if (fd >= 0) {
Packit Service db8eaa
						free(file->final_fname);
Packit Service db8eaa
						file->final_fname = tmpfname;
Packit Service db8eaa
						break;
Packit Service db8eaa
					}
Packit Service db8eaa
				}
Packit Service db8eaa
				if (fd < 0) {
Packit Service db8eaa
					SYSERR("open %s for writing failed",
Packit Service db8eaa
							file->final_fname);
Packit Service db8eaa
					free(tmpfname);
Packit Service db8eaa
					return -errno;
Packit Service db8eaa
				}
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	file->fd = fd;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* fill areas with data from input file, return bytes red */
Packit Service db8eaa
static int snd_pcm_file_areas_read_infile(snd_pcm_t *pcm,
Packit Service db8eaa
					  const snd_pcm_channel_area_t *areas,
Packit Service db8eaa
					  snd_pcm_uframes_t offset,
Packit Service db8eaa
					  snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	snd_pcm_channel_area_t areas_if[pcm->channels];
Packit Service db8eaa
	ssize_t bytes;
Packit Service db8eaa
Packit Service db8eaa
	if (file->ifd < 0)
Packit Service db8eaa
		return -EBADF;
Packit Service db8eaa
Packit Service db8eaa
	if (file->rbuf == NULL)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
Packit Service db8eaa
	if (file->rbuf_size < frames) {
Packit Service db8eaa
		SYSERR("requested more frames than pcm buffer");
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	bytes = snd_pcm_frames_to_bytes(pcm, frames);
Packit Service db8eaa
	if (bytes < 0)
Packit Service db8eaa
		return bytes;
Packit Service db8eaa
	bytes = read(file->ifd, file->rbuf, bytes);
Packit Service db8eaa
	if (bytes < 0) {
Packit Service db8eaa
		SYSERR("read from file failed, error: %d", bytes);
Packit Service db8eaa
		return bytes;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	snd_pcm_areas_from_buf(pcm, areas_if, file->rbuf);
Packit Service db8eaa
	snd_pcm_areas_copy(areas, offset, areas_if, 0, pcm->channels, snd_pcm_bytes_to_frames(pcm, bytes), pcm->format);
Packit Service db8eaa
Packit Service db8eaa
	return bytes;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void setup_wav_header(snd_pcm_t *pcm, struct wav_fmt *fmt)
Packit Service db8eaa
{
Packit Service db8eaa
	fmt->fmt = TO_LE16(0x01);
Packit Service db8eaa
	fmt->chan = TO_LE16(pcm->channels);
Packit Service db8eaa
	fmt->rate = TO_LE32(pcm->rate);
Packit Service db8eaa
	fmt->bwidth = pcm->frame_bits / 8;
Packit Service db8eaa
	fmt->bps = fmt->bwidth * pcm->rate;
Packit Service db8eaa
	fmt->bits = snd_pcm_format_width(pcm->format);
Packit Service db8eaa
	fmt->bps = TO_LE32(fmt->bps);
Packit Service db8eaa
	fmt->bwidth = TO_LE16(fmt->bwidth);
Packit Service db8eaa
	fmt->bits = TO_LE16(fmt->bits);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int write_wav_header(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	ssize_t res;
Packit Service db8eaa
Packit Service db8eaa
	static const char header[] = {
Packit Service db8eaa
		'R', 'I', 'F', 'F',
Packit Service db8eaa
		0x24, 0, 0, 0,
Packit Service db8eaa
		'W', 'A', 'V', 'E',
Packit Service db8eaa
		'f', 'm', 't', ' ',
Packit Service db8eaa
		0x10, 0, 0, 0,
Packit Service db8eaa
	};
Packit Service db8eaa
	static const char header2[] = {
Packit Service db8eaa
		'd', 'a', 't', 'a',
Packit Service db8eaa
		0, 0, 0, 0
Packit Service db8eaa
	};
Packit Service db8eaa
	
Packit Service db8eaa
	setup_wav_header(pcm, &file->wav_header);
Packit Service db8eaa
Packit Service db8eaa
	res = safe_write(file->fd, header, sizeof(header));
Packit Service db8eaa
	if (res != sizeof(header))
Packit Service db8eaa
		goto write_error;
Packit Service db8eaa
Packit Service db8eaa
	res = safe_write(file->fd, &file->wav_header, sizeof(file->wav_header));
Packit Service db8eaa
	if (res != sizeof(file->wav_header))
Packit Service db8eaa
		goto write_error;
Packit Service db8eaa
Packit Service db8eaa
	res = safe_write(file->fd, header2, sizeof(header2));
Packit Service db8eaa
	if (res != sizeof(header2))
Packit Service db8eaa
		goto write_error;
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
Packit Service db8eaa
write_error:
Packit Service db8eaa
	/*
Packit Service db8eaa
	 * print real errno if available and return EIO, reason for this is
Packit Service db8eaa
	 * to block possible EPIPE in case file->fd is a pipe. EPIPE from
Packit Service db8eaa
	 * file->fd conflicts with EPIPE from playback stream which should
Packit Service db8eaa
	 * be used to signal XRUN on playback device
Packit Service db8eaa
	 */
Packit Service db8eaa
	if (res < 0)
Packit Service db8eaa
		SYSERR("%s write header failed, file data may be corrupt", file->fname);
Packit Service db8eaa
	else
Packit Service db8eaa
		SNDERR("%s write header incomplete, file data may be corrupt", file->fname);
Packit Service db8eaa
Packit Service db8eaa
	memset(&file->wav_header, 0, sizeof(struct wav_fmt));
Packit Service db8eaa
Packit Service db8eaa
	return -EIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* fix up the length fields in WAV header */
Packit Service db8eaa
static void fixup_wav_header(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	int len, ret;
Packit Service db8eaa
Packit Service db8eaa
	/* RIFF length */
Packit Service db8eaa
	if (lseek(file->fd, 4, SEEK_SET) == 4) {
Packit Service db8eaa
		len = (file->filelen + 0x24) > 0x7fffffff ?
Packit Service db8eaa
			0x7fffffff : (int)(file->filelen + 0x24);
Packit Service db8eaa
		len = TO_LE32(len);
Packit Service db8eaa
		ret = safe_write(file->fd, &len, 4);
Packit Service db8eaa
		if (ret < 0)
Packit Service db8eaa
			return;
Packit Service db8eaa
	}
Packit Service db8eaa
	/* data length */
Packit Service db8eaa
	if (lseek(file->fd, 0x28, SEEK_SET) == 0x28) {
Packit Service db8eaa
		len = file->filelen > 0x7fffffff ?
Packit Service db8eaa
			0x7fffffff : (int)file->filelen;
Packit Service db8eaa
		len = TO_LE32(len);
Packit Service db8eaa
		ret = safe_write(file->fd, &len, 4);
Packit Service db8eaa
		if (ret < 0)
Packit Service db8eaa
			return;
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
#endif /* DOC_HIDDEN */
Packit Service db8eaa
Packit Service db8eaa
Packit Service db8eaa
Packit Service db8eaa
/* return error code in case write failed */
Packit Service db8eaa
static int snd_pcm_file_write_bytes(snd_pcm_t *pcm, size_t bytes)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	snd_pcm_sframes_t err = 0;
Packit Service db8eaa
	assert(bytes <= file->wbuf_used_bytes);
Packit Service db8eaa
Packit Service db8eaa
	if (file->format == SND_PCM_FILE_FORMAT_WAV &&
Packit Service db8eaa
	    !file->wav_header.fmt) {
Packit Service db8eaa
		err = write_wav_header(pcm);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			file->wbuf_used_bytes = 0;
Packit Service db8eaa
			file->file_ptr_bytes = 0;
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	while (bytes > 0) {
Packit Service db8eaa
		size_t n = bytes;
Packit Service db8eaa
		size_t cont = file->wbuf_size_bytes - file->file_ptr_bytes;
Packit Service db8eaa
		if (n > cont)
Packit Service db8eaa
			n = cont;
Packit Service db8eaa
		err = safe_write(file->fd, file->wbuf + file->file_ptr_bytes, n);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			file->wbuf_used_bytes = 0;
Packit Service db8eaa
			file->file_ptr_bytes = 0;
Packit Service db8eaa
			SYSERR("%s write failed, file data may be corrupt", file->fname);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
		bytes -= err;
Packit Service db8eaa
		file->wbuf_used_bytes -= err;
Packit Service db8eaa
		file->file_ptr_bytes += err;
Packit Service db8eaa
		if (file->file_ptr_bytes == file->wbuf_size_bytes)
Packit Service db8eaa
			file->file_ptr_bytes = 0;
Packit Service db8eaa
		file->filelen += err;
Packit Service db8eaa
		if ((snd_pcm_uframes_t)err != n)
Packit Service db8eaa
			break;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_file_add_frames(snd_pcm_t *pcm,
Packit Service db8eaa
				   const snd_pcm_channel_area_t *areas,
Packit Service db8eaa
				   snd_pcm_uframes_t offset,
Packit Service db8eaa
				   snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	while (frames > 0) {
Packit Service db8eaa
		int err = 0;
Packit Service db8eaa
		snd_pcm_uframes_t n = frames;
Packit Service db8eaa
		snd_pcm_uframes_t cont = file->wbuf_size - file->appl_ptr;
Packit Service db8eaa
		snd_pcm_uframes_t avail = file->wbuf_size - snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes);
Packit Service db8eaa
		if (n > cont)
Packit Service db8eaa
			n = cont;
Packit Service db8eaa
		if (n > avail)
Packit Service db8eaa
			n = avail;
Packit Service db8eaa
		snd_pcm_areas_copy(file->wbuf_areas, file->appl_ptr, 
Packit Service db8eaa
				   areas, offset,
Packit Service db8eaa
				   pcm->channels, n, pcm->format);
Packit Service db8eaa
		frames -= n;
Packit Service db8eaa
		offset += n;
Packit Service db8eaa
		file->appl_ptr += n;
Packit Service db8eaa
		if (file->appl_ptr == file->wbuf_size)
Packit Service db8eaa
			file->appl_ptr = 0;
Packit Service db8eaa
		file->wbuf_used_bytes += snd_pcm_frames_to_bytes(pcm, n);
Packit Service db8eaa
		if (file->wbuf_used_bytes > file->buffer_bytes) {
Packit Service db8eaa
			err = snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes - file->buffer_bytes);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
		}
Packit Service db8eaa
		assert(file->wbuf_used_bytes < file->wbuf_size_bytes);
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_file_close(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	if (file->fname) {
Packit Service db8eaa
		if (file->wav_header.fmt)
Packit Service db8eaa
			fixup_wav_header(pcm);
Packit Service db8eaa
		free((void *)file->fname);
Packit Service db8eaa
		if (file->pipe) {
Packit Service db8eaa
			pclose(file->pipe);
Packit Service db8eaa
		} else if (file->fd >= 0) {
Packit Service db8eaa
			close(file->fd);
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	if (file->ifname) {
Packit Service db8eaa
		free((void *)file->ifname);
Packit Service db8eaa
		close(file->ifd);
Packit Service db8eaa
	}
Packit Service db8eaa
	return snd_pcm_generic_close(pcm);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_file_reset(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	int err = snd_pcm_reset(file->gen.slave);
Packit Service db8eaa
	if (err >= 0) {
Packit Service db8eaa
		/* FIXME: Questionable here */
Packit Service db8eaa
		snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
Packit Service db8eaa
		assert(file->wbuf_used_bytes == 0);
Packit Service db8eaa
	}
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_file_drop(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	int err = snd_pcm_drop(file->gen.slave);
Packit Service db8eaa
	if (err >= 0) {
Packit Service db8eaa
		/* FIXME: Questionable here */
Packit Service db8eaa
		snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
Packit Service db8eaa
		assert(file->wbuf_used_bytes == 0);
Packit Service db8eaa
	}
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* locking */
Packit Service db8eaa
static int snd_pcm_file_drain(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	int err = snd_pcm_drain(file->gen.slave);
Packit Service db8eaa
	if (err >= 0) {
Packit Service db8eaa
		__snd_pcm_lock(pcm);
Packit Service db8eaa
		snd_pcm_file_write_bytes(pcm, file->wbuf_used_bytes);
Packit Service db8eaa
		assert(file->wbuf_used_bytes == 0);
Packit Service db8eaa
		__snd_pcm_unlock(pcm);
Packit Service db8eaa
	}
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_file_rewindable(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	snd_pcm_sframes_t res = snd_pcm_rewindable(file->gen.slave);
Packit Service db8eaa
	snd_pcm_sframes_t n = snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes);
Packit Service db8eaa
	if (res > n)
Packit Service db8eaa
		res = n;
Packit Service db8eaa
	return res;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_file_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	snd_pcm_sframes_t err;
Packit Service db8eaa
	snd_pcm_uframes_t n;
Packit Service db8eaa
	
Packit Service db8eaa
	n = snd_pcm_frames_to_bytes(pcm, frames);
Packit Service db8eaa
	if (n > file->wbuf_used_bytes)
Packit Service db8eaa
		frames = snd_pcm_bytes_to_frames(pcm, file->wbuf_used_bytes);
Packit Service db8eaa
	err = snd_pcm_rewind(file->gen.slave, frames);
Packit Service db8eaa
	if (err > 0) {
Packit Service db8eaa
		file->appl_ptr = (file->appl_ptr - err + file->wbuf_size) % file->wbuf_size;
Packit Service db8eaa
		n = snd_pcm_frames_to_bytes(pcm, err);
Packit Service db8eaa
		file->wbuf_used_bytes -= n;
Packit Service db8eaa
	}
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_file_forwardable(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	snd_pcm_sframes_t res = snd_pcm_forwardable(file->gen.slave);
Packit Service db8eaa
	snd_pcm_sframes_t n = snd_pcm_bytes_to_frames(pcm, file->wbuf_size_bytes - file->wbuf_used_bytes);
Packit Service db8eaa
	if (res > n)
Packit Service db8eaa
		res = n;
Packit Service db8eaa
	return res;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_file_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	snd_pcm_sframes_t err;
Packit Service db8eaa
	snd_pcm_uframes_t n;
Packit Service db8eaa
	
Packit Service db8eaa
	n = snd_pcm_frames_to_bytes(pcm, frames);
Packit Service db8eaa
	if (file->wbuf_used_bytes + n > file->wbuf_size_bytes)
Packit Service db8eaa
		frames = snd_pcm_bytes_to_frames(pcm, file->wbuf_size_bytes - file->wbuf_used_bytes);
Packit Service db8eaa
	err = INTERNAL(snd_pcm_forward)(file->gen.slave, frames);
Packit Service db8eaa
	if (err > 0) {
Packit Service db8eaa
		file->appl_ptr = (file->appl_ptr + err) % file->wbuf_size;
Packit Service db8eaa
		n = snd_pcm_frames_to_bytes(pcm, err);
Packit Service db8eaa
		file->wbuf_used_bytes += n;
Packit Service db8eaa
	}
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* locking */
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_file_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	snd_pcm_channel_area_t areas[pcm->channels];
Packit Service db8eaa
	snd_pcm_sframes_t n = _snd_pcm_writei(file->gen.slave, buffer, size);
Packit Service db8eaa
	if (n > 0) {
Packit Service db8eaa
		snd_pcm_areas_from_buf(pcm, areas, (void*) buffer);
Packit Service db8eaa
		__snd_pcm_lock(pcm);
Packit Service db8eaa
		if (snd_pcm_file_add_frames(pcm, areas, 0, n) < 0) {
Packit Service db8eaa
			__snd_pcm_unlock(pcm);
Packit Service db8eaa
			return -EIO;
Packit Service db8eaa
		}
Packit Service db8eaa
		__snd_pcm_unlock(pcm);
Packit Service db8eaa
	}
Packit Service db8eaa
	return n;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* locking */
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_file_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	snd_pcm_channel_area_t areas[pcm->channels];
Packit Service db8eaa
	snd_pcm_sframes_t n = _snd_pcm_writen(file->gen.slave, bufs, size);
Packit Service db8eaa
	if (n > 0) {
Packit Service db8eaa
		snd_pcm_areas_from_bufs(pcm, areas, bufs);
Packit Service db8eaa
		__snd_pcm_lock(pcm);
Packit Service db8eaa
		if (snd_pcm_file_add_frames(pcm, areas, 0, n) < 0) {
Packit Service db8eaa
			__snd_pcm_unlock(pcm);
Packit Service db8eaa
			return -EIO;
Packit Service db8eaa
		}
Packit Service db8eaa
		__snd_pcm_unlock(pcm);
Packit Service db8eaa
	}
Packit Service db8eaa
	return n;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* locking */
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	snd_pcm_channel_area_t areas[pcm->channels];
Packit Service db8eaa
	snd_pcm_sframes_t frames;
Packit Service db8eaa
Packit Service db8eaa
	frames = _snd_pcm_readi(file->gen.slave, buffer, size);
Packit Service db8eaa
	if (frames <= 0)
Packit Service db8eaa
		return frames;
Packit Service db8eaa
Packit Service db8eaa
	snd_pcm_areas_from_buf(pcm, areas, buffer);
Packit Service db8eaa
	snd_pcm_file_areas_read_infile(pcm, areas, 0, frames);
Packit Service db8eaa
	__snd_pcm_lock(pcm);
Packit Service db8eaa
	if (snd_pcm_file_add_frames(pcm, areas, 0, frames) < 0) {
Packit Service db8eaa
		__snd_pcm_unlock(pcm);
Packit Service db8eaa
		return -EIO;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	__snd_pcm_unlock(pcm);
Packit Service db8eaa
Packit Service db8eaa
	return frames;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* locking */
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	snd_pcm_channel_area_t areas[pcm->channels];
Packit Service db8eaa
	snd_pcm_sframes_t frames;
Packit Service db8eaa
Packit Service db8eaa
	frames = _snd_pcm_readn(file->gen.slave, bufs, size);
Packit Service db8eaa
	if (frames <= 0)
Packit Service db8eaa
		return frames;
Packit Service db8eaa
Packit Service db8eaa
	snd_pcm_areas_from_bufs(pcm, areas, bufs);
Packit Service db8eaa
	snd_pcm_file_areas_read_infile(pcm, areas, 0, frames);
Packit Service db8eaa
	__snd_pcm_lock(pcm);
Packit Service db8eaa
	if (snd_pcm_file_add_frames(pcm, areas, 0, frames) < 0) {
Packit Service db8eaa
		__snd_pcm_unlock(pcm);
Packit Service db8eaa
		return -EIO;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	__snd_pcm_unlock(pcm);
Packit Service db8eaa
Packit Service db8eaa
	return frames;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_file_mmap_commit(snd_pcm_t *pcm,
Packit Service db8eaa
					          snd_pcm_uframes_t offset,
Packit Service db8eaa
						  snd_pcm_uframes_t size)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	snd_pcm_uframes_t ofs;
Packit Service db8eaa
	snd_pcm_uframes_t siz = size;
Packit Service db8eaa
	const snd_pcm_channel_area_t *areas;
Packit Service db8eaa
	snd_pcm_sframes_t result;
Packit Service db8eaa
Packit Service db8eaa
	file->ifmmap_overwritten = 0;
Packit Service db8eaa
Packit Service db8eaa
	result = snd_pcm_mmap_begin(file->gen.slave, &areas, &ofs, &siz;;
Packit Service db8eaa
	if (result >= 0) {
Packit Service db8eaa
		assert(ofs == offset && siz == size);
Packit Service db8eaa
		result = snd_pcm_mmap_commit(file->gen.slave, ofs, siz);
Packit Service db8eaa
		if (result > 0) {
Packit Service db8eaa
			if (snd_pcm_file_add_frames(pcm, areas, ofs, result) < 0)
Packit Service db8eaa
				return -EIO;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	return result;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_file_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas,
Packit Service db8eaa
	snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	int result;
Packit Service db8eaa
Packit Service db8eaa
	result = snd_pcm_mmap_begin(file->gen.slave, areas, offset, frames);
Packit Service db8eaa
	if (result < 0)
Packit Service db8eaa
		return result;
Packit Service db8eaa
Packit Service db8eaa
	if (pcm->stream != SND_PCM_STREAM_CAPTURE)
Packit Service db8eaa
		return result;
Packit Service db8eaa
Packit Service db8eaa
	/* user may run mmap_begin without mmap_commit multiple times in row */
Packit Service db8eaa
	if (file->ifmmap_overwritten)
Packit Service db8eaa
		return result;
Packit Service db8eaa
	file->ifmmap_overwritten = 1;
Packit Service db8eaa
Packit Service db8eaa
	snd_pcm_file_areas_read_infile(pcm, *areas, *offset, *frames);
Packit Service db8eaa
Packit Service db8eaa
	return result;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_file_hw_free(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	free(file->wbuf);
Packit Service db8eaa
	free(file->wbuf_areas);
Packit Service db8eaa
	free(file->final_fname);
Packit Service db8eaa
	free(file->rbuf);
Packit Service db8eaa
	file->wbuf = NULL;
Packit Service db8eaa
	file->wbuf_areas = NULL;
Packit Service db8eaa
	file->final_fname = NULL;
Packit Service db8eaa
	file->rbuf = NULL;
Packit Service db8eaa
	return snd_pcm_hw_free(file->gen.slave);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	unsigned int channel;
Packit Service db8eaa
	snd_pcm_t *slave = file->gen.slave;
Packit Service db8eaa
	int err = _snd_pcm_hw_params_internal(slave, params);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	file->buffer_bytes = snd_pcm_frames_to_bytes(slave, slave->buffer_size);
Packit Service db8eaa
	file->wbuf_size = slave->buffer_size * 2;
Packit Service db8eaa
	file->wbuf_size_bytes = snd_pcm_frames_to_bytes(slave, file->wbuf_size);
Packit Service db8eaa
	file->wbuf_used_bytes = 0;
Packit Service db8eaa
	file->ifmmap_overwritten = 0;
Packit Service db8eaa
	assert(!file->wbuf);
Packit Service db8eaa
	file->wbuf = malloc(file->wbuf_size_bytes);
Packit Service db8eaa
	if (file->wbuf == NULL) {
Packit Service db8eaa
		snd_pcm_file_hw_free(pcm);
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
	file->wbuf_areas = malloc(sizeof(*file->wbuf_areas) * slave->channels);
Packit Service db8eaa
	if (file->wbuf_areas == NULL) {
Packit Service db8eaa
		snd_pcm_file_hw_free(pcm);
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
	assert(!file->rbuf);
Packit Service db8eaa
	file->rbuf_size = slave->buffer_size;
Packit Service db8eaa
	file->rbuf_size_bytes = snd_pcm_frames_to_bytes(slave, file->rbuf_size);
Packit Service db8eaa
	file->rbuf_used_bytes = 0;
Packit Service db8eaa
	file->rbuf = malloc(file->rbuf_size_bytes);
Packit Service db8eaa
	if (file->rbuf == NULL) {
Packit Service db8eaa
		snd_pcm_file_hw_free(pcm);
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
	file->appl_ptr = file->file_ptr_bytes = 0;
Packit Service db8eaa
	for (channel = 0; channel < slave->channels; ++channel) {
Packit Service db8eaa
		snd_pcm_channel_area_t *a = &file->wbuf_areas[channel];
Packit Service db8eaa
		a->addr = file->wbuf;
Packit Service db8eaa
		a->first = slave->sample_bits * channel;
Packit Service db8eaa
		a->step = slave->frame_bits;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (file->fd < 0) {
Packit Service db8eaa
		err = snd_pcm_file_open_output_file(file);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			SYSERR("failed opening output file %s", file->fname);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* pointer may have changed - e.g if plug is used. */
Packit Service db8eaa
	snd_pcm_unlink_hw_ptr(pcm, file->gen.slave);
Packit Service db8eaa
	snd_pcm_unlink_appl_ptr(pcm, file->gen.slave);
Packit Service db8eaa
Packit Service db8eaa
	snd_pcm_link_hw_ptr(pcm, file->gen.slave);
Packit Service db8eaa
	snd_pcm_link_appl_ptr(pcm, file->gen.slave);
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void snd_pcm_file_dump(snd_pcm_t *pcm, snd_output_t *out)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_file_t *file = pcm->private_data;
Packit Service db8eaa
	if (file->fname)
Packit Service db8eaa
		snd_output_printf(out, "File PCM (file=%s)\n", file->fname);
Packit Service db8eaa
	else
Packit Service db8eaa
		snd_output_printf(out, "File PCM (fd=%d)\n", file->fd);
Packit Service db8eaa
	if (file->final_fname)
Packit Service db8eaa
		snd_output_printf(out, "Final file PCM (file=%s)\n",
Packit Service db8eaa
				file->final_fname);
Packit Service db8eaa
Packit Service db8eaa
	if (pcm->setup) {
Packit Service db8eaa
		snd_output_printf(out, "Its setup is:\n");
Packit Service db8eaa
		snd_pcm_dump_setup(pcm, out);
Packit Service db8eaa
	}
Packit Service db8eaa
	snd_output_printf(out, "Slave: ");
Packit Service db8eaa
	snd_pcm_dump(file->gen.slave, out);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static const snd_pcm_ops_t snd_pcm_file_ops = {
Packit Service db8eaa
	.close = snd_pcm_file_close,
Packit Service db8eaa
	.info = snd_pcm_generic_info,
Packit Service db8eaa
	.hw_refine = snd_pcm_generic_hw_refine,
Packit Service db8eaa
	.hw_params = snd_pcm_file_hw_params,
Packit Service db8eaa
	.hw_free = snd_pcm_file_hw_free,
Packit Service db8eaa
	.sw_params = snd_pcm_generic_sw_params,
Packit Service db8eaa
	.channel_info = snd_pcm_generic_channel_info,
Packit Service db8eaa
	.dump = snd_pcm_file_dump,
Packit Service db8eaa
	.nonblock = snd_pcm_generic_nonblock,
Packit Service db8eaa
	.async = snd_pcm_generic_async,
Packit Service db8eaa
	.mmap = snd_pcm_generic_mmap,
Packit Service db8eaa
	.munmap = snd_pcm_generic_munmap,
Packit Service db8eaa
	.query_chmaps = snd_pcm_generic_query_chmaps,
Packit Service db8eaa
	.get_chmap = snd_pcm_generic_get_chmap,
Packit Service db8eaa
	.set_chmap = snd_pcm_generic_set_chmap,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
static const snd_pcm_fast_ops_t snd_pcm_file_fast_ops = {
Packit Service db8eaa
	.status = snd_pcm_generic_status,
Packit Service db8eaa
	.state = snd_pcm_generic_state,
Packit Service db8eaa
	.hwsync = snd_pcm_generic_hwsync,
Packit Service db8eaa
	.delay = snd_pcm_generic_delay,
Packit Service db8eaa
	.prepare = snd_pcm_generic_prepare,
Packit Service db8eaa
	.reset = snd_pcm_file_reset,
Packit Service db8eaa
	.start = snd_pcm_generic_start,
Packit Service db8eaa
	.drop = snd_pcm_file_drop,
Packit Service db8eaa
	.drain = snd_pcm_file_drain,
Packit Service db8eaa
	.pause = snd_pcm_generic_pause,
Packit Service db8eaa
	.rewindable = snd_pcm_file_rewindable,
Packit Service db8eaa
	.rewind = snd_pcm_file_rewind,
Packit Service db8eaa
	.forwardable = snd_pcm_file_forwardable,
Packit Service db8eaa
	.forward = snd_pcm_file_forward,
Packit Service db8eaa
	.resume = snd_pcm_generic_resume,
Packit Service db8eaa
	.link = snd_pcm_generic_link,
Packit Service db8eaa
	.link_slaves = snd_pcm_generic_link_slaves,
Packit Service db8eaa
	.unlink = snd_pcm_generic_unlink,
Packit Service db8eaa
	.writei = snd_pcm_file_writei,
Packit Service db8eaa
	.writen = snd_pcm_file_writen,
Packit Service db8eaa
	.readi = snd_pcm_file_readi,
Packit Service db8eaa
	.readn = snd_pcm_file_readn,
Packit Service db8eaa
	.avail_update = snd_pcm_generic_avail_update,
Packit Service db8eaa
	.mmap_commit = snd_pcm_file_mmap_commit,
Packit Service db8eaa
	.poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
Packit Service db8eaa
	.poll_descriptors = snd_pcm_generic_poll_descriptors,
Packit Service db8eaa
	.poll_revents = snd_pcm_generic_poll_revents,
Packit Service db8eaa
	.htimestamp = snd_pcm_generic_htimestamp,
Packit Service db8eaa
	.mmap_begin = snd_pcm_file_mmap_begin,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
/**
Packit Service db8eaa
 * \brief Creates a new File PCM
Packit Service db8eaa
 * \param pcmp Returns created PCM handle
Packit Service db8eaa
 * \param name Name of PCM
Packit Service db8eaa
 * \param fname Output filename (or NULL if file descriptor fd is available)
Packit Service db8eaa
 * \param fd Output file descriptor
Packit Service db8eaa
 * \param ifname Input filename (or NULL if file descriptor ifd is available)
Packit Service db8eaa
 * \param ifd Input file descriptor (if (ifd < 0) && (ifname == NULL), no input
Packit Service db8eaa
 *            redirection will be performed)
Packit Service db8eaa
 * \param trunc Truncate the file if it already exists
Packit Service db8eaa
 * \param fmt File format ("raw" or "wav" are available)
Packit Service db8eaa
 * \param perm File permission
Packit Service db8eaa
 * \param slave Slave PCM handle
Packit Service db8eaa
 * \param close_slave When set, the slave PCM handle is closed with copy PCM
Packit Service db8eaa
 * \param stream the direction of PCM stream
Packit Service db8eaa
 * \retval zero on success otherwise a negative error code
Packit Service db8eaa
 * \warning Using of this function might be dangerous in the sense
Packit Service db8eaa
 *          of compatibility reasons. The prototype might be freely
Packit Service db8eaa
 *          changed in future.
Packit Service db8eaa
 */
Packit Service db8eaa
int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
Packit Service db8eaa
		      const char *fname, int fd, const char *ifname, int ifd,
Packit Service db8eaa
		      int trunc,
Packit Service db8eaa
		      const char *fmt, int perm, snd_pcm_t *slave, int close_slave,
Packit Service db8eaa
		      snd_pcm_stream_t stream)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_t *pcm;
Packit Service db8eaa
	snd_pcm_file_t *file;
Packit Service db8eaa
	snd_pcm_file_format_t format;
Packit Service db8eaa
	struct timespec timespec;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	assert(pcmp);
Packit Service db8eaa
	if (fmt == NULL ||
Packit Service db8eaa
	    strcmp(fmt, "raw") == 0)
Packit Service db8eaa
		format = SND_PCM_FILE_FORMAT_RAW;
Packit Service db8eaa
	else if (!strcmp(fmt, "wav"))
Packit Service db8eaa
		format = SND_PCM_FILE_FORMAT_WAV;
Packit Service db8eaa
	else {
Packit Service db8eaa
		SNDERR("file format %s is unknown", fmt);
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	file = calloc(1, sizeof(snd_pcm_file_t));
Packit Service db8eaa
	if (!file) {
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* opening output fname is delayed until writing,
Packit Service db8eaa
	 when PCM params are known */
Packit Service db8eaa
	if (fname)
Packit Service db8eaa
		file->fname = strdup(fname);
Packit Service db8eaa
	file->trunc = trunc;
Packit Service db8eaa
	file->perm = perm;
Packit Service db8eaa
Packit Service db8eaa
	if (ifname && (stream == SND_PCM_STREAM_CAPTURE)) {
Packit Service db8eaa
		ifd = open(ifname, O_RDONLY);	/* TODO: mind blocking mode */
Packit Service db8eaa
		if (ifd < 0) {
Packit Service db8eaa
			SYSERR("open %s for reading failed", ifname);
Packit Service db8eaa
			free(file->fname);
Packit Service db8eaa
			free(file);
Packit Service db8eaa
			return -errno;
Packit Service db8eaa
		}
Packit Service db8eaa
		file->ifname = strdup(ifname);
Packit Service db8eaa
	}
Packit Service db8eaa
	file->fd = fd;
Packit Service db8eaa
	file->ifd = ifd;
Packit Service db8eaa
	file->format = format;
Packit Service db8eaa
	file->gen.slave = slave;
Packit Service db8eaa
	file->gen.close_slave = close_slave;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_pcm_new(&pcm, SND_PCM_TYPE_FILE, name, slave->stream, slave->mode);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		free(file->fname);
Packit Service db8eaa
		free(file->ifname);
Packit Service db8eaa
		free(file);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	pcm->ops = &snd_pcm_file_ops;
Packit Service db8eaa
	pcm->fast_ops = &snd_pcm_file_fast_ops;
Packit Service db8eaa
	pcm->private_data = file;
Packit Service db8eaa
	pcm->poll_fd = slave->poll_fd;
Packit Service db8eaa
	pcm->poll_events = slave->poll_events;
Packit Service db8eaa
	pcm->mmap_shadow = 1;
Packit Service db8eaa
	pcm->tstamp_type = SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
Packit Service db8eaa
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
Packit Service db8eaa
	if (clock_gettime(CLOCK_MONOTONIC, &timespec) == 0)
Packit Service db8eaa
		pcm->tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC;
Packit Service db8eaa
#endif
Packit Service db8eaa
	pcm->stream = stream;
Packit Service db8eaa
	snd_pcm_link_hw_ptr(pcm, slave);
Packit Service db8eaa
	snd_pcm_link_appl_ptr(pcm, slave);
Packit Service db8eaa
	*pcmp = pcm;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*! \page pcm_plugins
Packit Service db8eaa
Packit Service db8eaa
\section pcm_plugins_file Plugin: File
Packit Service db8eaa
Packit Service db8eaa
This plugin stores contents of a PCM stream to file or pipes the stream
Packit Service db8eaa
to a command, and optionally uses an existing file as an input data source
Packit Service db8eaa
(i.e., "virtual mic")
Packit Service db8eaa
Packit Service db8eaa
\code
Packit Service db8eaa
pcm.name {
Packit Service db8eaa
        type file               # File PCM
Packit Service db8eaa
        slave STR               # Slave name
Packit Service db8eaa
        # or
Packit Service db8eaa
        slave {                 # Slave definition
Packit Service db8eaa
                pcm STR         # Slave PCM name
Packit Service db8eaa
                # or
Packit Service db8eaa
                pcm { }         # Slave PCM definition
Packit Service db8eaa
        }
Packit Service db8eaa
	file STR		# Output filename (or shell command the stream
Packit Service db8eaa
				# will be piped to if STR starts with the pipe
Packit Service db8eaa
				# char).
Packit Service db8eaa
				# STR can contain format keys, replaced by
Packit Service db8eaa
				# real values corresponding to the stream:
Packit Service db8eaa
				# %r	rate (replaced with: 48000)
Packit Service db8eaa
				# %c	channels (replaced with: 2)
Packit Service db8eaa
				# %b	bits per sample (replaced with: 16)
Packit Service db8eaa
				# %f	sample format string
Packit Service db8eaa
				#			(replaced with: S16_LE)
Packit Service db8eaa
				# %%	replaced with %
Packit Service db8eaa
	or
Packit Service db8eaa
	file INT		# Output file descriptor number
Packit Service db8eaa
	infile STR		# Input filename - only raw format
Packit Service db8eaa
	or
Packit Service db8eaa
	infile INT		# Input file descriptor number
Packit Service db8eaa
	[format STR]		# File format ("raw" or "wav")
Packit Service db8eaa
	[perm INT]		# Output file permission (octal, def. 0600)
Packit Service db8eaa
}
Packit Service db8eaa
\endcode
Packit Service db8eaa
Packit Service db8eaa
\subsection pcm_plugins_file_funcref Function reference
Packit Service db8eaa
Packit Service db8eaa
    Packit Service db8eaa
      
  • snd_pcm_file_open()
  • Packit Service db8eaa
      
  • _snd_pcm_file_open()
  • Packit Service db8eaa
    Packit Service db8eaa
    Packit Service db8eaa
    */
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Creates a new File PCM
    Packit Service db8eaa
     * \param pcmp Returns created PCM handle
    Packit Service db8eaa
     * \param name Name of PCM
    Packit Service db8eaa
     * \param root Root configuration node
    Packit Service db8eaa
     * \param conf Configuration node with File PCM description
    Packit Service db8eaa
     * \param stream Stream type
    Packit Service db8eaa
     * \param mode Stream mode
    Packit Service db8eaa
     * \retval zero on success otherwise a negative error code
    Packit Service db8eaa
     * \warning Using of this function might be dangerous in the sense
    Packit Service db8eaa
     *          of compatibility reasons. The prototype might be freely
    Packit Service db8eaa
     *          changed in future.
    Packit Service db8eaa
     */
    Packit Service db8eaa
    int _snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
    Packit Service db8eaa
    		       snd_config_t *root, snd_config_t *conf, 
    Packit Service db8eaa
    		       snd_pcm_stream_t stream, int mode)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	snd_config_iterator_t i, next;
    Packit Service db8eaa
    	int err;
    Packit Service db8eaa
    	snd_pcm_t *spcm;
    Packit Service db8eaa
    	snd_config_t *slave = NULL, *sconf;
    Packit Service db8eaa
    	const char *fname = NULL, *ifname = NULL;
    Packit Service db8eaa
    	const char *format = NULL;
    Packit Service db8eaa
    	long fd = -1, ifd = -1, trunc = 1;
    Packit Service db8eaa
    	long perm = 0600;
    Packit Service db8eaa
    	snd_config_for_each(i, next, conf) {
    Packit Service db8eaa
    		snd_config_t *n = snd_config_iterator_entry(i);
    Packit Service db8eaa
    		const char *id;
    Packit Service db8eaa
    		if (snd_config_get_id(n, &id) < 0)
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		if (snd_pcm_conf_generic_id(id))
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		if (strcmp(id, "slave") == 0) {
    Packit Service db8eaa
    			slave = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "format") == 0) {
    Packit Service db8eaa
    			err = snd_config_get_string(n, &format);
    Packit Service db8eaa
    			if (err < 0) {
    Packit Service db8eaa
    				SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    				return -EINVAL;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "file") == 0) {
    Packit Service db8eaa
    			err = snd_config_get_string(n, &fname);
    Packit Service db8eaa
    			if (err < 0) {
    Packit Service db8eaa
    				err = snd_config_get_integer(n, &fd;;
    Packit Service db8eaa
    				if (err < 0) {
    Packit Service db8eaa
    					SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    					return -EINVAL;
    Packit Service db8eaa
    				}
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "infile") == 0) {
    Packit Service db8eaa
    			err = snd_config_get_string(n, &ifname);
    Packit Service db8eaa
    			if (err < 0) {
    Packit Service db8eaa
    				err = snd_config_get_integer(n, &ifd;;
    Packit Service db8eaa
    				if (err < 0) {
    Packit Service db8eaa
    					SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    					return -EINVAL;
    Packit Service db8eaa
    				}
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "perm") == 0) {
    Packit Service db8eaa
    			err = snd_config_get_integer(n, &perm);
    Packit Service db8eaa
    			if (err < 0) {
    Packit Service db8eaa
    				SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    				return err;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			if ((perm & ~0777) != 0) {
    Packit Service db8eaa
    				SNDERR("The field perm must be a valid file permission");
    Packit Service db8eaa
    				return -EINVAL;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "truncate") == 0) {
    Packit Service db8eaa
    			err = snd_config_get_bool(n);
    Packit Service db8eaa
    			if (err < 0)
    Packit Service db8eaa
    				return -EINVAL;
    Packit Service db8eaa
    			trunc = err;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		SNDERR("Unknown field %s", id);
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (!format) {
    Packit Service db8eaa
    		snd_config_t *n;
    Packit Service db8eaa
    		/* read defaults */
    Packit Service db8eaa
    		if (snd_config_search(root, "defaults.pcm.file_format", &n) >= 0) {
    Packit Service db8eaa
    			err = snd_config_get_string(n, &format);
    Packit Service db8eaa
    			if (err < 0) {
    Packit Service db8eaa
    				SNDERR("Invalid file format");
    Packit Service db8eaa
    				return -EINVAL;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (!slave) {
    Packit Service db8eaa
    		SNDERR("slave is not defined");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	err = snd_pcm_slave_conf(root, slave, &sconf, 0);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	if ((!fname || strlen(fname) == 0) && fd < 0) {
    Packit Service db8eaa
    		snd_config_delete(sconf);
    Packit Service db8eaa
    		SNDERR("file is not defined");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
    Packit Service db8eaa
    	snd_config_delete(sconf);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	err = snd_pcm_file_open(pcmp, name, fname, fd, ifname, ifd,
    Packit Service db8eaa
    				trunc, format, perm, spcm, 1, stream);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		snd_pcm_close(spcm);
    Packit Service db8eaa
    	return err;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    #ifndef DOC_HIDDEN
    Packit Service db8eaa
    SND_DLSYM_BUILD_VERSION(_snd_pcm_file_open, SND_PCM_DLSYM_VERSION);
    Packit Service db8eaa
    #endif