[Sound-open-firmware] [PATCH v2 2/3] tune: new component for file IO

Ranjani Sridharan ranjani.sridharan at linux.intel.com
Thu May 10 00:58:59 CEST 2018


This patch adds a new file component that testbench
can use to read in samples and write out processed samples.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan at linux.intel.com>
---
 tune/include/file.h |  71 +++++++
 tune/src/file.c     | 602 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 673 insertions(+)
 create mode 100644 tune/include/file.h
 create mode 100644 tune/src/file.c

diff --git a/tune/include/file.h b/tune/include/file.h
new file mode 100644
index 0000000..07ce4aa
--- /dev/null
+++ b/tune/include/file.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2018, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of the Intel Corporation nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author: Seppo Ingalsuo <seppo.ingalsuo at linux.intel.com>
+ *         Liam Girdwood <liam.r.girdwood at linux.intel.com>
+ *         Keyon Jie <yang.jie at linux.intel.com>
+ *	   Ranjani Sridharan <ranjani.sridharan at linux.intel.com>
+ */
+#ifndef _FILE_H
+#define _FILE_H
+
+/* file component modes */
+enum file_mode {
+	FILE_READ = 0,
+	FILE_WRITE,
+	FILE_DUPLEX,
+};
+
+/* file component state */
+struct file_state {
+	char *fn;
+	FILE *rfh, *wfh; /* read/write file handle */
+	int reached_eof;
+	int n;
+	enum file_mode mode;
+};
+
+/* file comp data */
+struct file_comp_data {
+	uint32_t period_bytes;
+	uint32_t channels;
+	uint32_t frame_bytes;
+	uint32_t rate;
+	struct file_state fs;
+	int (*file_func)(struct comp_dev *dev, struct comp_buffer *sink,
+			 struct comp_buffer *source, uint32_t frames);
+
+};
+
+/* file IO ipc comp */
+struct sof_ipc_comp_file {
+	struct sof_ipc_comp comp;
+	struct sof_ipc_comp_config config;
+	char *fn;
+	enum file_mode mode;
+};
+#endif
diff --git a/tune/src/file.c b/tune/src/file.c
new file mode 100644
index 0000000..049432b
--- /dev/null
+++ b/tune/src/file.c
@@ -0,0 +1,602 @@
+/*
+ * Copyright (c) 2018, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of the Intel Corporation nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author: Seppo Ingalsuo <seppo.ingalsuo at linux.intel.com>
+ *	   Ranjani Sridharan <ranjani.sridharan at linux.intel.com>
+ */
+
+/* file component for reading/writing pcm samples to/from a file */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <sof/sof.h>
+#include <sof/lock.h>
+#include <sof/list.h>
+#include <sof/stream.h>
+#include <sof/work.h>
+#include <sof/clock.h>
+#include <sof/audio/component.h>
+#include <sof/audio/format.h>
+#include <sof/audio/pipeline.h>
+#include <uapi/ipc.h>
+#include "common_test.h"
+#include "file.h"
+
+static inline void buffer_check_wrap_32(int32_t **ptr, int32_t *end,
+					size_t size)
+{
+	if (*ptr >= end)
+		*ptr = (int32_t *) ((size_t)*ptr - size);
+}
+
+static inline void buffer_check_wrap_16(int16_t **ptr, int16_t *end,
+					size_t size)
+{
+	if (*ptr >= end)
+		*ptr = (int16_t *) ((size_t)*ptr - size);
+}
+
+/*
+ * Read 32-bit samples from file
+ * currently only supports txt files
+ * TODO: support binary input files
+ */
+static int read_samples_32(struct comp_dev *dev, struct comp_buffer *sink,
+			   int n, int fmt, int nch)
+{
+	struct file_comp_data *cd = comp_get_drvdata(dev);
+	int32_t *dest = (int32_t *)sink->w_ptr;
+	int32_t sample;
+	int n_samples = 0;
+	int i, n_wrap, n_min, ret;
+
+	while (n > 0) {
+		n_wrap = (int32_t *) sink->end_addr - dest;
+		/* check for buffer wrap and copy to the end of the buffer */
+		n_min = (n < n_wrap) ? n : n_wrap;
+		while (n_min > 0) {
+			n -= nch;
+			n_min -= nch;
+			/* copy sample per channel */
+			for (i = 0; i < nch; i++) {
+				/* read sample from file */
+				if (fmt == SOF_IPC_FRAME_S32_LE)
+					ret = fscanf(cd->fs.rfh, "%d", dest);
+				/* mask bits if 24-bit samples */
+				if (fmt == SOF_IPC_FRAME_S24_4LE) {
+					ret = fscanf(cd->fs.rfh, "%d", &sample);
+					*dest = sample & 0x00ffffff;
+				}
+				dest++;
+				/* quit if eof is reached */
+				if (ret == EOF) {
+					cd->fs.reached_eof = 1;
+					goto quit;
+				}
+				n_samples++;
+			}
+		}
+		/* check for buffer wrap and update pointer */
+		buffer_check_wrap_32(&dest, sink->end_addr,
+				     sink->size);
+	}
+quit:
+	return n_samples;
+}
+
+/*
+ * Read 16-bit samples from file
+ * currently only supports txt files
+ * TODO: support binary input files
+ */
+static int read_samples_16(struct comp_dev *dev, struct comp_buffer *sink,
+			    int n, int nch)
+{
+	struct file_comp_data *cd = comp_get_drvdata(dev);
+	int16_t *dest = (int16_t *)sink->w_ptr;
+	int i, n_wrap, n_min, ret;
+	int n_samples = 0;
+
+	/* copy samples */
+	while (n > 0) {
+		n_wrap = (int16_t *) sink->end_addr - dest;
+		/* check for buffer wrap and copy to the end of the buffer */
+		n_min = (n < n_wrap) ? n : n_wrap;
+		while (n_min > 0) {
+			n -= nch;
+			n_min -= nch;
+			/* copy sample per channel */
+			for (i = 0; i < nch; i++) {
+				/* read sample from file */
+				ret = fscanf(cd->fs.rfh, "%hd", dest);
+				dest++;
+				if (ret == EOF) {
+					cd->fs.reached_eof = 1;
+					goto quit;
+				}
+				n_samples++;
+			}
+		}
+		/* check for buffer wrap and update pointer */
+		buffer_check_wrap_16(&dest, sink->end_addr,
+				     sink->size);
+	}
+
+quit:
+	return n_samples;
+}
+
+/*
+ * Write 16-bit samples from file
+ * currently only supports txt files
+ * TODO: support binary input files
+ */
+static int write_samples_16(struct comp_dev *dev, struct comp_buffer *source,
+			    int n, int nch)
+{
+	struct file_comp_data *cd = comp_get_drvdata(dev);
+	int16_t *src = (int16_t *)source->r_ptr;
+	int i, n_wrap, n_min, ret;
+	int n_samples = 0;
+
+	/* copy samples */
+	while (n > 0) {
+		n_wrap = (int16_t *) source->end_addr - src;
+		/* check for buffer wrap and copy to the end of the buffer */
+		n_min = (n < n_wrap) ? n : n_wrap;
+		while (n_min > 0) {
+			n -= nch;
+			n_min -= nch;
+			/* copy sample per channel */
+			for (i = 0; i < nch; i++) {
+				/* write sample to file */
+				ret = fprintf(cd->fs.wfh, "%d\n",
+					      *src);
+				src++;
+				if (ret < 0)
+					goto quit;
+				n_samples++;
+			}
+		}
+		/* check for buffer wrap and update pointer */
+		buffer_check_wrap_16(&src, source->end_addr,
+				     source->size);
+	}
+quit:
+	return n_samples;
+}
+
+/*
+ * Write 32-bit samples from file
+ * currently only supports txt files
+ * TODO: support binary input files
+ */
+static int write_samples_32(struct comp_dev *dev, struct comp_buffer *source,
+			    int n, int fmt, int nch)
+{
+	struct file_comp_data *cd = comp_get_drvdata(dev);
+	int32_t *src = (int32_t *)source->r_ptr;
+	int32_t sample;
+	int i, n_wrap, n_min, ret;
+	int n_samples = 0;
+
+	/* copy samples */
+	while (n > 0) {
+		n_wrap = (int32_t *) source->end_addr - src;
+		/* check for buffer wrap and copy to the end of the buffer */
+		n_min = (n < n_wrap) ? n : n_wrap;
+		while (n_min > 0) {
+			n -= nch;
+			n_min -= nch;
+			/* copy sample per channel */
+			for (i = 0; i < nch; i++) {
+				/* write sample to file */
+				if (fmt == SOF_IPC_FRAME_S32_LE)
+					ret = fprintf(cd->fs.wfh, "%d\n",
+					      *src);
+				if (fmt == SOF_IPC_FRAME_S24_4LE) {
+					sample = *src << 8;
+					ret = fprintf(cd->fs.wfh, "%d\n",
+						      sample >> 8);
+				}
+				src++;
+				if (ret < 0) {
+					//cd->fs.write_fail = 1;
+					goto quit;
+				}
+				n_samples++;
+			}
+		}
+		/* check for buffer wrap and update pointer */
+		buffer_check_wrap_32(&src, source->end_addr,
+				     source->size);
+	}
+quit:
+	return n_samples;
+}
+
+/* function for processing 32-bit samples */
+static int file_s32_default(struct comp_dev *dev, struct comp_buffer *sink,
+			    struct comp_buffer *source, uint32_t frames)
+{
+	struct file_comp_data *cd = comp_get_drvdata(dev);
+	int nch = dev->params.channels;
+	int n_samples = 0;
+
+	switch (cd->fs.mode) {
+	case FILE_READ:
+		/* read samples */
+		n_samples = read_samples_32(dev, sink, frames * nch,
+					    SOF_IPC_FRAME_S32_LE, nch);
+		break;
+	case FILE_WRITE:
+		/* write samples */
+		n_samples = write_samples_32(dev, sink, frames * nch,
+					     SOF_IPC_FRAME_S32_LE, nch);
+		break;
+	default:
+		/* TODO: duplex mode */
+		break;
+	}
+
+	cd->fs.n += n_samples;
+	return n_samples;
+}
+
+/* function for processing 16-bit samples */
+static int file_s16(struct comp_dev *dev, struct comp_buffer *sink,
+		    struct comp_buffer *source, uint32_t frames)
+{
+	struct file_comp_data *cd = comp_get_drvdata(dev);
+	int nch = dev->params.channels;
+	int n_samples = 0;
+
+	switch (cd->fs.mode) {
+	case FILE_READ:
+		/* read samples */
+		n_samples = read_samples_16(dev, sink, frames * nch, nch);
+		break;
+	case FILE_WRITE:
+		/* write samples */
+		n_samples = write_samples_16(dev, source, frames * nch, nch);
+		break;
+	default:
+		/* TODO: duplex mode */
+		break;
+	}
+
+	cd->fs.n += n_samples;
+	return n_samples;
+}
+
+/* function for processing 24-bit samples */
+static int file_s24(struct comp_dev *dev, struct comp_buffer *sink,
+		    struct comp_buffer *source, uint32_t frames)
+{
+	struct file_comp_data *cd = comp_get_drvdata(dev);
+	int nch = dev->params.channels;
+	int n_samples = 0;
+
+	switch (cd->fs.mode) {
+	case FILE_READ:
+		/* read samples */
+		n_samples = read_samples_32(dev, sink, frames * nch,
+					    SOF_IPC_FRAME_S24_4LE, nch);
+		break;
+	case FILE_WRITE:
+		/* write samples */
+		n_samples = write_samples_32(dev, source, frames * nch,
+					     SOF_IPC_FRAME_S24_4LE, nch);
+		break;
+	default:
+		/* TODO: duplex mode */
+		break;
+	}
+
+	cd->fs.n += n_samples;
+	return n_samples;
+}
+
+static struct comp_dev *file_new(struct sof_ipc_comp *comp)
+{
+	struct comp_dev *dev;
+	struct sof_ipc_comp_file *file;
+	struct sof_ipc_comp_file *ipc_file
+		= (struct sof_ipc_comp_file *) comp;
+	struct file_comp_data *cd;
+
+	/* allocate memory for file comp */
+	dev = malloc(COMP_SIZE(struct sof_ipc_comp_file));
+	if (dev == NULL)
+		return NULL;
+
+	/* copy file comp config */
+	file = (struct sof_ipc_comp_file *) &dev->comp;
+	memcpy(file, ipc_file, sizeof(struct sof_ipc_comp_file));
+
+	/* allocate  memory for file comp data */
+	cd = malloc(sizeof(*cd));
+	if (cd == NULL) {
+		free(dev);
+		return NULL;
+	}
+
+	comp_set_drvdata(dev, cd);
+	/* default function for processing samples */
+	cd->file_func = file_s32_default;
+
+	/* Get filename from IPC and open file */
+	cd->fs.fn = strdup(ipc_file->fn);
+
+	/* set file comp mode */
+	cd->fs.mode = ipc_file->mode;
+
+	/* open file handle(s) depending on mode */
+	switch (cd->fs.mode) {
+	case FILE_READ:
+		cd->fs.rfh = fopen(cd->fs.fn, "r");
+		if (cd->fs.rfh == NULL) {
+			printf("error: opening file %s\n", cd->fs.fn);
+			free(cd);
+			free(dev);
+			return NULL;
+		}
+		break;
+	case FILE_WRITE:
+		cd->fs.wfh = fopen(cd->fs.fn, "w");
+		if (cd->fs.wfh == NULL) {
+			printf("error: opening file %s\n", cd->fs.fn);
+			free(cd);
+			free(dev);
+			return NULL;
+		}
+		break;
+	default:
+		/* TODO: duplex mode */
+		break;
+	}
+
+	cd->fs.reached_eof = 0;
+	cd->fs.n = 0;
+
+	return dev;
+}
+
+static void file_free(struct comp_dev *dev)
+{
+	struct file_data *td = comp_get_drvdata(dev);
+	struct file_comp_data *cd = comp_get_drvdata(dev);
+
+	fclose(cd->fs.rfh);
+	free(td);
+	free(dev);
+}
+
+/* set component audio stream parameters */
+static int file_params(struct comp_dev *dev)
+{
+	struct file_comp_data *cd = comp_get_drvdata(dev);
+	struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
+
+	/* Need to compute this in non-host endpoint */
+	dev->frame_bytes =
+		dev->params.sample_container_bytes * dev->params.channels;
+
+	/* calculate period size based on config */
+	cd->period_bytes = dev->frames * dev->frame_bytes;
+
+	/* File to sink supports only S32_LE/S16_LE/S24_4LE PCM formats */
+	if ((config->frame_fmt != SOF_IPC_FRAME_S32_LE)
+		&& (config->frame_fmt != SOF_IPC_FRAME_S24_4LE)
+		&& (config->frame_fmt != SOF_IPC_FRAME_S16_LE))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int fr_cmd(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata)
+{
+	return -EINVAL;
+}
+
+static int file_trigger(struct comp_dev *dev, int cmd)
+{
+	return comp_set_state(dev, cmd);
+}
+
+/* used to pass standard and bespoke commands (with data) to component */
+static int file_cmd(struct comp_dev *dev, int cmd, void *data)
+{
+	struct sof_ipc_ctrl_data *cdata = data;
+	int ret = 0;
+
+	switch (cmd) {
+	case COMP_CMD_SET_DATA:
+		ret = fr_cmd(dev, cdata);
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * copy and process stream samples
+ * returns the number of bytes copied
+ */
+static int file_copy(struct comp_dev *dev)
+{
+	struct comp_buffer *buffer;
+	struct file_comp_data *cd = comp_get_drvdata(dev);
+	int ret = 0, bytes;
+
+	switch (cd->fs.mode) {
+	case FILE_READ:
+		/* file component sink buffer */
+		buffer = list_first_item(&dev->bsink_list, struct comp_buffer,
+				       source_list);
+
+		/* test sink has enough free frames */
+		if (buffer->free >= cd->period_bytes && !cd->fs.reached_eof) {
+			/* read PCM samples from file */
+			ret = cd->file_func(dev, buffer, NULL, dev->frames);
+
+			/* update sink buffer pointers */
+			bytes = dev->params.sample_container_bytes;
+			if (ret > 0)
+				comp_update_buffer_produce(buffer,
+							   ret * bytes);
+		}
+		break;
+	case FILE_WRITE:
+		/* file component source buffer */
+		buffer = list_first_item(&dev->bsource_list,
+					 struct comp_buffer, sink_list);
+
+		/* test source has enough free frames */
+		if (buffer->avail >= cd->period_bytes) {
+			/* write PCM samples into file */
+			ret = cd->file_func(dev, NULL, buffer, dev->frames);
+
+			/* update source buffer pointers */
+			bytes = dev->params.sample_container_bytes;
+			if (ret > 0)
+				comp_update_buffer_consume(buffer,
+							   ret * bytes);
+		}
+		break;
+	default:
+		/* TODO: duplex mode */
+		break;
+	}
+
+	return ret;
+}
+
+static int file_prepare(struct comp_dev *dev)
+{
+	struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
+	struct comp_buffer *buffer = NULL;
+	struct file_comp_data *cd = comp_get_drvdata(dev);
+	int ret = 0, periods;
+
+	/* file component sink/source buffer */
+	switch (cd->fs.mode) {
+	case FILE_READ:
+		buffer = list_first_item(&dev->bsink_list, struct comp_buffer,
+					 source_list);
+		periods = config->periods_sink;
+		break;
+	case FILE_WRITE:
+		buffer = list_first_item(&dev->bsource_list,
+					 struct comp_buffer, sink_list);
+		periods = config->periods_source;
+		break;
+	default:
+		/* TODO: duplex mode */
+		break;
+	}
+
+	if (!buffer) {
+		printf("error: no sink/source buffer\n");
+		return -EINVAL;
+	}
+
+	switch (config->frame_fmt) {
+	case(SOF_IPC_FRAME_S16_LE):
+		/* set downstream buffer size */
+		ret = buffer_set_size(buffer, dev->frames * 2 *
+			periods * dev->params.channels);
+		if (ret < 0) {
+			printf("error: file buffer size set\n");
+			return ret;
+		}
+		buffer_reset_pos(buffer);
+
+		/* set file function */
+		cd->file_func = file_s16;
+		break;
+	case(SOF_IPC_FRAME_S24_4LE):
+		/* set downstream buffer size */
+		ret = buffer_set_size(buffer, dev->frames * 4 *
+			periods * dev->params.channels);
+		if (ret < 0) {
+			printf("error: file buffer size set\n");
+			return ret;
+		}
+		buffer_reset_pos(buffer);
+		/* set file function */
+		cd->file_func = file_s24;
+		break;
+	case(SOF_IPC_FRAME_S32_LE):
+		/* set downstream buffer size */
+		ret = buffer_set_size(buffer, dev->frames * 4 *
+			periods * dev->params.channels);
+		if (ret < 0) {
+			printf("error: file buffer size set\n");
+			return ret;
+		}
+		buffer_reset_pos(buffer);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	dev->state = COMP_STATE_PREPARE;
+
+	return ret;
+}
+
+static int file_reset(struct comp_dev *dev)
+{
+	dev->state = COMP_STATE_INIT;
+
+	return 0;
+}
+
+struct comp_driver comp_file = {
+	.type = SOF_COMP_FILEREAD,
+	.ops = {
+		.new = file_new,
+		.free = file_free,
+		.params = file_params,
+		.cmd = file_cmd,
+		.trigger = file_trigger,
+		.copy = file_copy,
+		.prepare = file_prepare,
+		.reset = file_reset,
+	},
+};
+
+void sys_comp_file_init(void)
+{
+	comp_register(&comp_file);
+}
-- 
2.14.1



More information about the Sound-open-firmware mailing list