On Thu, 2018-05-10 at 16:40 +0100, Liam Girdwood wrote:
On Wed, 2018-05-09 at 15:58 -0700, Ranjani Sridharan wrote:
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@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@linux.intel.com
Liam Girdwood <liam.r.girdwood@linux.intel.com>
Keyon Jie <yang.jie@linux.intel.com>
Ranjani Sridharan <ranjani.sridharan@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@linux.intel.com
Ranjani Sridharan <ranjani.sridharan@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 */
Can you put newlines before all comments/blocks (in all files), it's breaks things up nicely :) (I don't care what checkpatch says about this - will fix).
Yes, sure.
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);
Why do we do an fscanf() here ?
The testbench currently only handles txt input files generated using octave. My next action item to be able to read raw pcm files as well.
/* 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;
code to be removed ?
Yes, will remove it.
Liam
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);
+}