From: Pavel Hofman <pavel.hofman@insite.cz>
To: Takashi Iwai <tiwai@suse.de>
Cc: alsa-devel@alsa-project.org
Subject: Re: [PATCH] PCM parameters in file plugin
Date: Thu, 29 Jan 2009 11:54:18 +0100 [thread overview]
Message-ID: <49818ADA.4050505@insite.cz> (raw)
In-Reply-To: <s5hljsua538.wl%tiwai@suse.de>
[-- Attachment #1: Type: text/plain, Size: 617 bytes --]
Takashi Iwai wrote:
>
> Thanks, it gets better now.
>
>> +static int snd_pcm_file_replace_key(char format, const void *value_p,
>> + char **string_p, char **index_ch_p, int *len_p)
>
> I think it could be easier to make value_str in the caller side.
> That is,
> ........
Hi Takashi,
Thanks for your valuable comments. I am enclosing a fixed and tested
version.
Regards,
Pavel.
Signed-off-by: Pavel Hofman <pavel.hofman@insite.cz>
* added support for including pcm stream params in the output filename
* added support for piping the stream to a shell command if the filename
string starts with a pipe char
[-- Attachment #2: pcm_file.c.patch --]
[-- Type: text/x-diff, Size: 9914 bytes --]
diff --git a/src/pcm/pcm_file.c b/src/pcm/pcm_file.c
index 82823a0..6ddf14f 100644
--- a/src/pcm/pcm_file.c
+++ b/src/pcm/pcm_file.c
@@ -29,6 +29,7 @@
#include <endian.h>
#include <byteswap.h>
#include <ctype.h>
+#include <string.h>
#include "pcm_local.h"
#include "pcm_plugin.h"
@@ -39,6 +40,16 @@ const char *_snd_module_pcm_file = "";
#ifndef DOC_HIDDEN
+/* 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
+
typedef enum _snd_pcm_file_format {
SND_PCM_FILE_FORMAT_RAW,
SND_PCM_FILE_FORMAT_WAV
@@ -57,6 +68,9 @@ struct wav_fmt {
typedef struct {
snd_pcm_generic_t gen;
char *fname;
+ char *final_fname;
+ int trunc;
+ int perm;
int fd;
char *ifname;
int ifd;
@@ -84,6 +98,175 @@ typedef struct {
#define TO_LE16(x) bswap_16(x)
#endif
+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",
+ pcm->frame_bits/(8 * pcm->channels));
+ 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;
+}
+
static void setup_wav_header(snd_pcm_t *pcm, struct wav_fmt *fmt)
{
fmt->fmt = TO_LE16(0x01);
@@ -152,6 +335,8 @@ static void fixup_wav_header(snd_pcm_t *pcm)
}
#endif /* DOC_HIDDEN */
+
+
static void snd_pcm_file_write_bytes(snd_pcm_t *pcm, size_t bytes)
{
snd_pcm_file_t *file = pcm->private_data;
@@ -442,6 +627,13 @@ static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
a->first = slave->sample_bits * channel;
a->step = slave->frame_bits;
}
+ 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;
+ }
+ }
return 0;
}
@@ -452,6 +644,10 @@ static void snd_pcm_file_dump(snd_pcm_t *pcm, snd_output_t *out)
snd_output_printf(out, "File PCM (file=%s)\n", file->fname);
else
snd_output_printf(out, "File PCM (fd=%d)\n", file->fd);
+ if (file->final_fname)
+ snd_output_printf(out, "Final file PCM (file=%s)\n",
+ file->final_fname);
+
if (pcm->setup) {
snd_output_printf(out, "Its setup is:\n");
snd_pcm_dump_setup(pcm, out);
@@ -533,7 +729,6 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_file_t *file;
snd_pcm_file_format_t format;
struct timespec timespec;
- char *tmpname = NULL;
int err;
assert(pcmp);
@@ -546,58 +741,27 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
SNDERR("file format %s is unknown", fmt);
return -EINVAL;
}
- if (fname) {
- if (trunc)
- fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, perm);
- else {
- fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, perm);
- if (fd < 0) {
- int idx, len;
- len = strlen(fname) + 6;
- tmpname = malloc(len);
- if (!tmpname)
- return -ENOMEM;
- for (idx = 1; idx < 10000; idx++) {
- snprintf(tmpname, len,
- "%s.%04d", fname, idx);
- fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL, perm);
- if (fd >= 0) {
- fname = tmpname;
- break;
- }
- }
- }
- }
- if (fd < 0) {
- SYSERR("open %s for writing failed", fname);
- free(tmpname);
- return -errno;
- }
- }
file = calloc(1, sizeof(snd_pcm_file_t));
if (!file) {
- if (fname)
- close(fd);
- free(tmpname);
return -ENOMEM;
}
+ /* opening output fname is delayed until writing,
+ when PCM params are known */
+ if (fname)
+ file->fname = strdup(fname);
+ file->trunc = trunc;
+ file->perm = perm;
+
if (ifname) {
ifd = open(ifname, O_RDONLY); /* TODO: mind blocking mode */
if (ifd < 0) {
SYSERR("open %s for reading failed", ifname);
- if (fname)
- close(fd);
free(file);
- free(tmpname);
return -errno;
}
- }
-
- if (fname)
- file->fname = strdup(fname);
- if (ifname)
file->ifname = strdup(ifname);
+ }
file->fd = fd;
file->ifd = ifd;
file->format = format;
@@ -608,7 +772,6 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
if (err < 0) {
free(file->fname);
free(file);
- free(tmpname);
return err;
}
pcm->ops = &snd_pcm_file_ops;
@@ -625,8 +788,6 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_link_hw_ptr(pcm, slave);
snd_pcm_link_appl_ptr(pcm, slave);
*pcmp = pcm;
-
- free(tmpname);
return 0;
}
@@ -634,8 +795,9 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
\section pcm_plugins_file Plugin: File
-This plugin stores contents of a PCM stream to file, and optionally
-uses an existing file as an input data source (i.e., "virtual mic")
+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")
\code
pcm.name {
@@ -647,7 +809,17 @@ pcm.name {
# or
pcm { } # Slave PCM definition
}
- file STR # Output filename
+ 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)
+ # %b bytes per sample (replaced with: 2)
+ # %f sample format string
+ # (replaced with: S16_LE)
+ # %% replaced with %
or
file INT # Output file descriptor number
infile STR # Input filename - only raw format
@@ -773,7 +945,7 @@ int _snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
err = snd_pcm_slave_conf(root, slave, &sconf, 0);
if (err < 0)
return err;
- if (!fname && fd < 0 && !ifname) {
+ if ((!fname || strlen(fname) == 0) && fd < 0 && !ifname) {
snd_config_delete(sconf);
SNDERR("file is not defined");
return -EINVAL;
[-- Attachment #3: Type: text/plain, Size: 160 bytes --]
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
next prev parent reply other threads:[~2009-01-29 10:54 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-01-05 20:54 PCM parameters in file plugin Pavel Hofman
2009-01-06 9:51 ` Pavel Hofman
2009-01-06 10:31 ` Jaroslav Kysela
2009-01-06 11:33 ` Pavel Hofman
2009-01-19 22:20 ` [PATCH] " Pavel Hofman
2009-01-26 14:28 ` Pavel Hofman
2009-01-28 15:41 ` Takashi Iwai
2009-01-28 23:00 ` Pavel Hofman
2009-01-29 8:28 ` Takashi Iwai
2009-01-29 10:54 ` Pavel Hofman [this message]
2009-01-29 11:01 ` Takashi Iwai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=49818ADA.4050505@insite.cz \
--to=pavel.hofman@insite.cz \
--cc=alsa-devel@alsa-project.org \
--cc=tiwai@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.