Alsa-Devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Pavel Hofman <pavel.hofman@insite.cz>
To: Jaroslav Kysela <perex@perex.cz>
Cc: alsa-devel@alsa-project.org
Subject: Re: [PATCH] PCM parameters in file plugin
Date: Mon, 19 Jan 2009 23:20:49 +0100	[thread overview]
Message-ID: <4974FCC1.70907@insite.cz> (raw)
In-Reply-To: <49634196.1070500@insite.cz>

[-- Attachment #1: Type: text/plain, Size: 1514 bytes --]

Pavel Hofman wrote:
> Jaroslav Kysela wrote:
>> On Tue, 6 Jan 2009, Pavel Hofman wrote:
>>
>>> Pavel Hofman napsal(a):
>>>> Hello,
>>>>
>>>> I am working on a file plugin patch to allow adding PCM parameters 
>>>> (rate, format) to  the name of the file created (plus the ability to 
>>>> run an external command via popen, such as upsampling with sox). I 
>>>> got stuck in trying to obtain these parameters in 
>>>> pcm_file.c:_snd_pcm_file_open(). Is it actually possible or the 
>>>> information is not available at the time of opening the file (and 
>>>> the whole patch is thus nonsense)?
>>>>
>>>> Thanks a lot for help.
>>>>
>>> I am now opening the output file in pcm_file.c:snd_pcm_file_write_bytes.
>>> Surprisingly it seems to work fine. Could such hack be accepted as a
>>> patch to the plugin (if coded properly)? Is there a better place to 
>>> open the file?
>>
>> The file should be opened in the hw_params callback (all PCM 
>> parameters are known in this time). Also, extending filename with PCM 
>> parameters should be optional (configurable). But the idea looks nice.
>>
>>                         Jaroslav
> 

Hello,

please find enclosed a patch for the file plugin. The
checkpatch.pl script throws 5 bogus errors in doxygen comments.

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

Thanks a lot,

Pavel.


[-- Attachment #2: pcm_file.c.diff --]
[-- Type: text/x-patch, Size: 10892 bytes --]

diff --git a/src/pcm/pcm_file.c b/src/pcm/pcm_file.c
index 82823a0..40f9760 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,24 @@ const char *_snd_module_pcm_file = "";
 
 #ifndef DOC_HIDDEN
 
+/* keys to be replaced by real values in the filename */
+#define RATE_KEY	"%r"
+#define CHANNELS_KEY	"%c"
+#define BWIDTH_KEY	"%b"
+#define FORMAT_KEY	"%f"
+
+/* maximum length of a value */
+#define VALUE_MAXLEN	10
+
+static const char *keys[] = {
+	RATE_KEY,
+	CHANNELS_KEY,
+	BWIDTH_KEY,
+	FORMAT_KEY,
+};
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof *(x))
+
 typedef enum _snd_pcm_file_format {
 	SND_PCM_FILE_FORMAT_RAW,
 	SND_PCM_FILE_FORMAT_WAV
@@ -57,6 +76,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 +106,211 @@ typedef struct {
 #define TO_LE16(x)	bswap_16(x)
 #endif
 
+/* old_string MUST contain the key! */
+int snd_pcm_file_replace_key(char *old_string, char *key, char *value,
+		char **newstring_p)
+{
+	int str_index, newstr_index, key_index, end, new_len, old_len, cpy_len,
+	    first_key_index;
+	char *c, *first_c, *newstring;
+	int keys_count;
+
+	first_c = c = (char *) strstr(old_string, key);
+	new_len        = strlen(value);
+	old_len        = strlen(key);
+	end            = strlen(old_string)   - old_len;
+	first_key_index = key_index = c - old_string;
+
+	/* first run - finding nb of keys for calculating allocation size */
+	str_index = 0;
+	keys_count = 0;
+	while (str_index <= end && c != NULL) {
+		keys_count++;
+		str_index    = key_index + old_len;
+		/* Check for another pattern match */
+		c = (char *) strstr(old_string+str_index, key);
+		if (c != NULL)
+			key_index = c - old_string;
+	}
+	/* now we can allocate exactly the right size */
+	newstring = malloc(strlen(old_string) +
+			keys_count*(new_len - old_len) + 1);
+	if (!newstring)
+		return -ENOMEM;
+	/* second run - actually replacing */
+	str_index = 0;
+	newstr_index = 0;
+	key_index = first_key_index;
+	c = first_c;
+	while (str_index <= end && c != NULL) {
+		/* Copy characters from the left of matched pattern occurence */
+		cpy_len = key_index-str_index;
+		strncpy(newstring+newstr_index, old_string+str_index, cpy_len);
+		newstr_index += cpy_len;
+		str_index    += cpy_len;
+
+		/* Copy replacement characters instead of matched pattern */
+		strcpy(newstring+newstr_index, value);
+		newstr_index += new_len;
+		str_index    += old_len;
+
+		/* Check for another pattern match */
+		c = (char *) strstr(old_string+str_index, key);
+		if (c != NULL)
+			key_index = c - old_string;
+	}
+	/* Copy remaining characters from the right of last matched pattern */
+	strcpy(newstring+newstr_index, old_string+str_index);
+	*(newstring_p) = newstring;
+	return 0;
+}
+
+int snd_pcm_file_replace_fname(snd_pcm_file_t *file, char **new_fname_p)
+{
+	char *value;
+	char *old_fname = NULL;
+	int err;
+	char *fname = file->fname;
+	snd_pcm_t *pcm = file->gen.slave;
+
+	value = malloc(VALUE_MAXLEN);
+	if (!value)
+		return -ENOMEM;
+	/* we want to keep fname, const */
+	old_fname = *(new_fname_p) = fname;
+
+	if (strstr(old_fname, RATE_KEY)) {
+		snprintf(value, VALUE_MAXLEN, "%d", pcm->rate);
+		err = snd_pcm_file_replace_key(old_fname, RATE_KEY, value,
+				new_fname_p);
+		/* fname must not be freed */
+		if (old_fname != fname)
+			free(old_fname);
+		old_fname = *(new_fname_p);
+		if (err < 0)
+			return err;
+	}
+	if (strstr(old_fname, CHANNELS_KEY)) {
+		snprintf(value, VALUE_MAXLEN, "%d", pcm->channels);
+		err = snd_pcm_file_replace_key(old_fname, CHANNELS_KEY, value,
+				new_fname_p);
+		if (old_fname != fname)
+			free(old_fname);
+		old_fname = *(new_fname_p);
+		if (err < 0)
+			return err;
+	}
+	if (strstr(old_fname, BWIDTH_KEY)) {
+		snprintf(value, VALUE_MAXLEN, "%d",
+				pcm->frame_bits/(8 * pcm->channels));
+		err = snd_pcm_file_replace_key(old_fname, BWIDTH_KEY, value,
+				new_fname_p);
+		if (old_fname != fname)
+			free(old_fname);
+		old_fname = *(new_fname_p);
+		if (err < 0)
+			return err;
+	}
+	if (strstr(old_fname, FORMAT_KEY)) {
+		snprintf(value, VALUE_MAXLEN, "%s",
+				snd_pcm_format_name(pcm->format));
+		err = snd_pcm_file_replace_key(old_fname, FORMAT_KEY, value,
+				new_fname_p);
+		if (old_fname != fname)
+			free(old_fname);
+		old_fname = *(new_fname_p);
+		if (err < 0)
+			return err;
+	}
+	free(value);
+	return 0;
+
+}
+
+int snd_pcm_file_has_keys(char *string)
+{
+	int i, n = ARRAY_SIZE(keys);
+	static const char **key = keys;
+
+	for (i = 0; i < n; i++) {
+		if (strstr(string, *key++) != NULL)
+			return 1;
+	}
+	/* none found */
+	return 0;
+}
+
+int snd_pcm_file_open_output_file(snd_pcm_file_t *file)
+{
+	int err, fd;
+
+	/* fname can contain keys, generating final_fname */
+	if (snd_pcm_file_has_keys(file->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);*/
+	} else {
+		/* no changes */
+		file->final_fname = malloc(strlen(file->fname) + 1);
+		if (!file->final_fname)
+			return -ENOMEM;
+		strcpy(file->final_fname, file->fname);
+	}
+
+	if (file->final_fname[0] == '|') {
+		/* pipe mode */
+		FILE *pipe;
+		/* clearing */
+		file->final_fname[0] = ' ';
+		pipe = popen(file->final_fname, "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 +379,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 +671,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 +688,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 +773,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 +785,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 +816,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 +832,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 +839,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 +853,16 @@ 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)
 	or
 	file INT		# Output file descriptor number
 	infile STR		# Input filename - only raw format


[-- 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

  reply	other threads:[~2009-01-19 22:20 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       ` Pavel Hofman [this message]
2009-01-26 14:28         ` [PATCH] " 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
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=4974FCC1.70907@insite.cz \
    --to=pavel.hofman@insite.cz \
    --cc=alsa-devel@alsa-project.org \
    --cc=perex@perex.cz \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox