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