Re: PCM parameters in file plugin

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


Hi,

Thanks a lot for your hint. Here is a preliminary version of the patch for review (no docs updated, no clean formatting).

The changes are moving the opening of output file to hw_params callback, support for replacement codes in the file parameter, and support for piping to external commands when the file parameter starts with |.

This is a working ".asoundrc on steroids" (upsampling to 96kHz using the quality algorithm of sox)

pcm.raw {
         type file
         slave {
                 pcm null
         }
         format "raw"
file "| sox -V -c %c -%b -r %r -s -t raw - -t raw -4 - rate -v -s 96000 | aplay -v -t raw -r 96000 -c %c -f S32_LE -Dhw:0"
}

Thanks a lot for comments.

Regards,

Pavel.
diff --git a/src/pcm/pcm_file.c b/src/pcm/pcm_file.c
index 82823a0..df2ee0e 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,21 @@ const char *_snd_module_pcm_file = "";
 
 #ifndef DOC_HIDDEN
 
+#define RATE_KEY "%r"
+#define CHANNELS_KEY "%c"
+#define BWIDTH_KEY "%b"
+#define FORMAT_KEY "%f"
+#define DECIMAL_INT_LEN 7
+
+static const char* keys[] = {
+	RATE_KEY,
+	CHANNELS_KEY,
+	BWIDTH_KEY,
+	FORMAT_KEY,
+};
+
+#define NUM_OF(x) (sizeof (x) / sizeof *(x))
+
 typedef enum _snd_pcm_file_format {
 	SND_PCM_FILE_FORMAT_RAW,
 	SND_PCM_FILE_FORMAT_WAV
@@ -57,6 +73,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 +103,198 @@ typedef struct {
 #define TO_LE16(x)	bswap_16(x)
 #endif
 
+/* old_string MUST contain 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 */
+		if((c = (char *) strstr(old_string+str_index, key)) != 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 */
+		if((c = (char *) strstr(old_string+str_index, key)) != 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(DECIMAL_INT_LEN);
+	if (! value) {
+		return -ENOMEM;
+	}
+	/* we want to keep fname, const */
+	old_fname = *(new_fname_p) = fname;
+
+	if (strstr(old_fname, RATE_KEY)) {
+		sprintf(value, "%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)) {
+		sprintf(value, "%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)) {
+		sprintf(value, "%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)) {
+		sprintf(value, "%d", 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;
+
+	n = NUM_OF(keys);
+	for (i = 0; i < n; ++i) {
+		if (strstr(string, keys[i]) != 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 +363,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 +655,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 +672,9 @@ 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 +756,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 +768,26 @@ 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 +798,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 +814,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;
 }
 
_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxx
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux