blob: 8525b2b81b25007ceb5847797cb8b69f643e4e85 [file] [log] [blame]
Ranjani Sridharan1d5b67f2018-05-31 19:29:06 -07001/*
2 * Copyright (c) 2018, Intel Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the Intel Corporation nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Author(s): Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
29 * Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
30 */
31
32/* file component for reading/writing pcm samples to/from a file */
33
34#include <stdio.h>
35#include <stdint.h>
36#include <stddef.h>
37#include <errno.h>
38#include <inttypes.h>
39#include <sof/sof.h>
40#include <sof/lock.h>
41#include <sof/list.h>
42#include <sof/stream.h>
43#include <sof/work.h>
44#include <sof/clock.h>
45#include <sof/audio/component.h>
46#include <sof/audio/format.h>
47#include <sof/audio/pipeline.h>
48#include <uapi/ipc.h>
49#include "host/common_test.h"
50#include "host/file.h"
51
52static inline void buffer_check_wrap_32(int32_t **ptr, int32_t *end,
53 size_t size)
54{
55 if (*ptr >= end)
56 *ptr = (int32_t *)((size_t)*ptr - size);
57}
58
59static inline void buffer_check_wrap_16(int16_t **ptr, int16_t *end,
60 size_t size)
61{
62 if (*ptr >= end)
63 *ptr = (int16_t *)((size_t)*ptr - size);
64}
65
66/*
67 * Read 32-bit samples from file
68 * currently only supports txt files
69 */
70static int read_samples_32(struct comp_dev *dev, struct comp_buffer *sink,
71 int n, int fmt, int nch)
72{
73 struct file_comp_data *cd = comp_get_drvdata(dev);
74 int32_t *dest = (int32_t *)sink->w_ptr;
75 int32_t sample;
76 int n_samples = 0;
77 int i, n_wrap, n_min, ret;
78
79 while (n > 0) {
80 n_wrap = (int32_t *)sink->end_addr - dest;
81
82 /* check for buffer wrap and copy to the end of the buffer */
83 n_min = (n < n_wrap) ? n : n_wrap;
84 while (n_min > 0) {
85 n -= nch;
86 n_min -= nch;
87
88 /* copy sample per channel */
89 for (i = 0; i < nch; i++) {
90 /* read sample from file */
91 switch (cd->fs.f_format) {
92 /* text input file */
93 case FILE_TEXT:
94 if (fmt == SOF_IPC_FRAME_S32_LE)
95 ret = fscanf(cd->fs.rfh, "%d",
96 dest);
97
98 /* mask bits if 24-bit samples */
99 if (fmt == SOF_IPC_FRAME_S24_4LE) {
100 ret = fscanf(cd->fs.rfh, "%d",
101 &sample);
102 *dest = sample & 0x00ffffff;
103 }
104 /* quit if eof is reached */
105 if (ret == EOF) {
106 cd->fs.reached_eof = 1;
107 goto quit;
108 }
109 break;
110
111 /* raw input file */
112 default:
113 if (fmt == SOF_IPC_FRAME_S32_LE)
114 ret = fread(dest,
115 sizeof(int32_t),
116 1, cd->fs.rfh);
117
118 /* mask bits if 24-bit samples */
119 if (fmt == SOF_IPC_FRAME_S24_4LE) {
120 ret = fread(&sample,
121 sizeof(int32_t),
122 1, cd->fs.rfh);
123 *dest = sample & 0x00ffffff;
124 }
125 /* quit if eof is reached */
126 if (ret != 1) {
127 cd->fs.reached_eof = 1;
128 goto quit;
129 }
130 break;
131 }
132 dest++;
133 n_samples++;
134 }
135 }
136 /* check for buffer wrap and update pointer */
137 buffer_check_wrap_32(&dest, sink->end_addr,
138 sink->size);
139 }
140quit:
141 return n_samples;
142}
143
144/*
145 * Read 16-bit samples from file
146 * currently only supports txt files
147 */
148static int read_samples_16(struct comp_dev *dev, struct comp_buffer *sink,
149 int n, int nch)
150{
151 struct file_comp_data *cd = comp_get_drvdata(dev);
152 int16_t *dest = (int16_t *)sink->w_ptr;
153 int i, n_wrap, n_min, ret;
154 int n_samples = 0;
155
156 /* copy samples */
157 while (n > 0) {
158 n_wrap = (int16_t *)sink->end_addr - dest;
159
160 /* check for buffer wrap and copy to the end of the buffer */
161 n_min = (n < n_wrap) ? n : n_wrap;
162 while (n_min > 0) {
163 n -= nch;
164 n_min -= nch;
165
166 /* copy sample per channel */
167 for (i = 0; i < nch; i++) {
168 /* read sample from file */
169 ret = fscanf(cd->fs.rfh, "%hd", dest);
170 switch (cd->fs.f_format) {
171 /* text input file */
172 case FILE_TEXT:
173 ret = fscanf(cd->fs.rfh, "%hd", dest);
174 if (ret == EOF) {
175 cd->fs.reached_eof = 1;
176 goto quit;
177 }
178 break;
179
180 /* rw pcm input file */
181 default:
182 ret = fread(dest, sizeof(int16_t), 1,
183 cd->fs.rfh);
184 if (ret != 1) {
185 cd->fs.reached_eof = 1;
186 goto quit;
187 }
188 break;
189 }
190
191 dest++;
192 n_samples++;
193 }
194 }
195 /* check for buffer wrap and update pointer */
196 buffer_check_wrap_16(&dest, sink->end_addr,
197 sink->size);
198 }
199
200quit:
201 return n_samples;
202}
203
204/*
205 * Write 16-bit samples from file
206 * currently only supports txt files
207 */
208static int write_samples_16(struct comp_dev *dev, struct comp_buffer *source,
209 int n, int nch)
210{
211 struct file_comp_data *cd = comp_get_drvdata(dev);
212 int16_t *src = (int16_t *)source->r_ptr;
213 int i, n_wrap, n_min, ret;
214 int n_samples = 0;
215
216 /* copy samples */
217 while (n > 0) {
218 n_wrap = (int16_t *)source->end_addr - src;
219
220 /* check for buffer wrap and copy to the end of the buffer */
221 n_min = (n < n_wrap) ? n : n_wrap;
222 while (n_min > 0) {
223 n -= nch;
224 n_min -= nch;
225
226 /* copy sample per channel */
227 for (i = 0; i < nch; i++) {
228 switch (cd->fs.f_format) {
229 /* text output file */
230 case FILE_TEXT:
231 ret = fprintf(cd->fs.wfh,
232 "%d\n", *src);
233 if (ret < 0)
234 goto quit;
235 break;
236
237 /* raw pcm output file */
238 default:
239 ret = fwrite(src,
240 sizeof(int16_t),
241 1, cd->fs.wfh);
242 if (ret != 1)
243 goto quit;
244 break;
245 }
246
247 src++;
248 n_samples++;
249 }
250 }
251 /* check for buffer wrap and update pointer */
252 buffer_check_wrap_16(&src, source->end_addr,
253 source->size);
254 }
255quit:
256 return n_samples;
257}
258
259/*
260 * Write 32-bit samples from file
261 * currently only supports txt files
262 */
263static int write_samples_32(struct comp_dev *dev, struct comp_buffer *source,
264 int n, int fmt, int nch)
265{
266 struct file_comp_data *cd = comp_get_drvdata(dev);
267 int32_t *src = (int32_t *)source->r_ptr;
268 int i, n_wrap, n_min, ret;
269 int n_samples = 0;
270 int32_t sample;
271
272 /* copy samples */
273 while (n > 0) {
274 n_wrap = (int32_t *)source->end_addr - src;
275
276 /* check for buffer wrap and copy to the end of the buffer */
277 n_min = (n < n_wrap) ? n : n_wrap;
278 while (n_min > 0) {
279 n -= nch;
280 n_min -= nch;
281
282 /* copy sample per channel */
283 for (i = 0; i < nch; i++) {
284 switch (cd->fs.f_format) {
285 /* text output file */
286 case FILE_TEXT:
287 if (fmt == SOF_IPC_FRAME_S32_LE)
288 ret = fprintf(cd->fs.wfh,
289 "%d\n", *src);
290 if (fmt == SOF_IPC_FRAME_S24_4LE) {
291 sample = *src << 8;
292 ret = fprintf(cd->fs.wfh,
293 "%d\n",
294 sample >> 8);
295 }
296 if (ret < 0)
297 goto quit;
298 break;
299
300 /* raw pcm output file */
301 default:
302 if (fmt == SOF_IPC_FRAME_S32_LE)
303 ret = fwrite(src,
304 sizeof(int32_t),
305 1, cd->fs.wfh);
306 if (fmt == SOF_IPC_FRAME_S24_4LE) {
307 sample = *src << 8;
308 sample >>= 8;
309 ret = fwrite(&sample,
310 sizeof(int32_t),
311 1, cd->fs.wfh);
312 }
313 if (ret != 1)
314 goto quit;
315 break;
316 }
317
318 /* increment read pointer */
319 src++;
320
321 /* increment number of samples written */
322 n_samples++;
323 }
324 }
325 /* check for buffer wrap and update pointer */
326 buffer_check_wrap_32(&src, source->end_addr,
327 source->size);
328 }
329quit:
330 return n_samples;
331}
332
333/* function for processing 32-bit samples */
334static int file_s32_default(struct comp_dev *dev, struct comp_buffer *sink,
335 struct comp_buffer *source, uint32_t frames)
336{
337 struct file_comp_data *cd = comp_get_drvdata(dev);
338 int nch = dev->params.channels;
339 int n_samples = 0;
340
341 switch (cd->fs.mode) {
342 case FILE_READ:
343 /* read samples */
344 n_samples = read_samples_32(dev, sink, frames * nch,
345 SOF_IPC_FRAME_S32_LE, nch);
346 break;
347 case FILE_WRITE:
348 /* write samples */
349 n_samples = write_samples_32(dev, source, frames * nch,
350 SOF_IPC_FRAME_S32_LE, nch);
351 break;
352 default:
353 /* TODO: duplex mode */
354 break;
355 }
356
357 cd->fs.n += n_samples;
358 return n_samples;
359}
360
361/* function for processing 16-bit samples */
362static int file_s16(struct comp_dev *dev, struct comp_buffer *sink,
363 struct comp_buffer *source, uint32_t frames)
364{
365 struct file_comp_data *cd = comp_get_drvdata(dev);
366 int nch = dev->params.channels;
367 int n_samples = 0;
368
369 switch (cd->fs.mode) {
370 case FILE_READ:
371 /* read samples */
372 n_samples = read_samples_16(dev, sink, frames * nch, nch);
373 break;
374 case FILE_WRITE:
375 /* write samples */
376 n_samples = write_samples_16(dev, source, frames * nch, nch);
377 break;
378 default:
379 /* TODO: duplex mode */
380 break;
381 }
382
383 cd->fs.n += n_samples;
384 return n_samples;
385}
386
387/* function for processing 24-bit samples */
388static int file_s24(struct comp_dev *dev, struct comp_buffer *sink,
389 struct comp_buffer *source, uint32_t frames)
390{
391 struct file_comp_data *cd = comp_get_drvdata(dev);
392 int nch = dev->params.channels;
393 int n_samples = 0;
394
395 switch (cd->fs.mode) {
396 case FILE_READ:
397 /* read samples */
398 n_samples = read_samples_32(dev, sink, frames * nch,
399 SOF_IPC_FRAME_S24_4LE, nch);
400 break;
401 case FILE_WRITE:
402 /* write samples */
403 n_samples = write_samples_32(dev, source, frames * nch,
404 SOF_IPC_FRAME_S24_4LE, nch);
405 break;
406 default:
407 /* TODO: duplex mode */
408 break;
409 }
410
411 cd->fs.n += n_samples;
412 return n_samples;
413}
414
415static enum file_format get_file_format(char *filename)
416{
417 char *ext = strrchr(filename, '.');
418
419 if (!strcmp(ext, ".txt"))
420 return FILE_TEXT;
421
422 return FILE_RAW;
423}
424
425static struct comp_dev *file_new(struct sof_ipc_comp *comp)
426{
427 struct comp_dev *dev;
428 struct sof_ipc_comp_file *file;
429 struct sof_ipc_comp_file *ipc_file =
430 (struct sof_ipc_comp_file *)comp;
431 struct file_comp_data *cd;
432
433 /* allocate memory for file comp */
434 dev = malloc(COMP_SIZE(struct sof_ipc_comp_file));
435 if (!dev)
436 return NULL;
437
438 /* copy file comp config */
439 file = (struct sof_ipc_comp_file *)&dev->comp;
440 memcpy(file, ipc_file, sizeof(struct sof_ipc_comp_file));
441
442 /* allocate memory for file comp data */
443 cd = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, sizeof(*cd));
444 if (!cd) {
445 free(dev);
446 return NULL;
447 }
448
449 comp_set_drvdata(dev, cd);
450
451 /* default function for processing samples */
452 cd->file_func = file_s32_default;
453
454 /* get filename from IPC and open file */
455 cd->fs.fn = strdup(ipc_file->fn);
456
457 /* set file format */
458 cd->fs.f_format = get_file_format(cd->fs.fn);
459
460 /* set file comp mode */
461 cd->fs.mode = ipc_file->mode;
462
463 /* open file handle(s) depending on mode */
464 switch (cd->fs.mode) {
465 case FILE_READ:
466 cd->fs.rfh = fopen(cd->fs.fn, "r");
467 if (!cd->fs.rfh) {
468 fprintf(stderr, "error: opening file %s\n", cd->fs.fn);
469 free(cd);
470 free(dev);
471 return NULL;
472 }
473 break;
474 case FILE_WRITE:
475 cd->fs.wfh = fopen(cd->fs.fn, "w");
476 if (!cd->fs.wfh) {
477 fprintf(stderr, "error: opening file %s\n", cd->fs.fn);
478 free(cd);
479 free(dev);
480 return NULL;
481 }
482 break;
483 default:
484 /* TODO: duplex mode */
485 break;
486 }
487
488 cd->fs.reached_eof = 0;
489 cd->fs.n = 0;
490
491 dev->state = COMP_STATE_READY;
492
493 return dev;
494}
495
496static void file_free(struct comp_dev *dev)
497{
498 struct file_comp_data *cd = comp_get_drvdata(dev);
499
500 if (cd->fs.mode == FILE_READ)
501 fclose(cd->fs.rfh);
502 else
503 fclose(cd->fs.wfh);
504
505 free(cd->fs.fn);
506 free(cd);
507 free(dev);
508
509 debug_print("free file component\n");
510}
511
512/* set component audio stream parameters */
513static int file_params(struct comp_dev *dev)
514{
515 struct file_comp_data *cd = comp_get_drvdata(dev);
516 struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
517
518 /* for file endpoint set the following from topology config */
519 if (cd->fs.mode == FILE_WRITE) {
520 dev->params.frame_fmt = config->frame_fmt;
521 if (dev->params.frame_fmt == SOF_IPC_FRAME_S16_LE)
522 dev->params.sample_container_bytes = 2;
523 else
524 dev->params.sample_container_bytes = 4;
525 }
526
527 /* Need to compute this in non-host endpoint */
528 dev->frame_bytes =
529 dev->params.sample_container_bytes * dev->params.channels;
530
531 /* calculate period size based on config */
532 cd->period_bytes = dev->frames * dev->frame_bytes;
533
534 /* File to sink supports only S32_LE/S16_LE/S24_4LE PCM formats */
535 if (config->frame_fmt != SOF_IPC_FRAME_S32_LE &&
536 config->frame_fmt != SOF_IPC_FRAME_S24_4LE &&
537 config->frame_fmt != SOF_IPC_FRAME_S16_LE)
538 return -EINVAL;
539
540 return 0;
541}
542
543static int fr_cmd(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata)
544{
545 return -EINVAL;
546}
547
548static int file_trigger(struct comp_dev *dev, int cmd)
549{
550 return comp_set_state(dev, cmd);
551}
552
553/* used to pass standard and bespoke commands (with data) to component */
554static int file_cmd(struct comp_dev *dev, int cmd, void *data)
555{
556 struct sof_ipc_ctrl_data *cdata = data;
557 int ret = 0;
558
559 switch (cmd) {
560 case COMP_CMD_SET_DATA:
561 ret = fr_cmd(dev, cdata);
562 break;
563 default:
564 break;
565 }
566
567 return ret;
568}
569
570/*
571 * copy and process stream samples
572 * returns the number of bytes copied
573 */
574static int file_copy(struct comp_dev *dev)
575{
576 struct comp_buffer *buffer;
577 struct file_comp_data *cd = comp_get_drvdata(dev);
578 int ret = 0, bytes;
579
580 switch (cd->fs.mode) {
581 case FILE_READ:
582 /* file component sink buffer */
583 buffer = list_first_item(&dev->bsink_list, struct comp_buffer,
584 source_list);
585
586 /* test sink has enough free frames */
587 if (buffer->free >= cd->period_bytes && !cd->fs.reached_eof) {
588 /* read PCM samples from file */
589 ret = cd->file_func(dev, buffer, NULL, dev->frames);
590
591 /* update sink buffer pointers */
592 bytes = dev->params.sample_container_bytes;
593 if (ret > 0)
594 comp_update_buffer_produce(buffer,
595 ret * bytes);
596 }
597 break;
598 case FILE_WRITE:
599 /* file component source buffer */
600 buffer = list_first_item(&dev->bsource_list,
601 struct comp_buffer, sink_list);
602
603 /* test source has enough free frames */
604 if (buffer->avail >= cd->period_bytes) {
605 /* write PCM samples into file */
606 ret = cd->file_func(dev, NULL, buffer, dev->frames);
607
608 /* update source buffer pointers */
609 bytes = dev->params.sample_container_bytes;
610 if (ret > 0)
611 comp_update_buffer_consume(buffer,
612 ret * bytes);
613 }
614 break;
615 default:
616 /* TODO: duplex mode */
617 break;
618 }
619
620 return ret;
621}
622
623static int file_prepare(struct comp_dev *dev)
624{
625 struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
626 struct comp_buffer *buffer = NULL;
627 struct file_comp_data *cd = comp_get_drvdata(dev);
628 int ret = 0, periods;
629
630 /* file component sink/source buffer period count */
631 switch (cd->fs.mode) {
632 case FILE_READ:
633 buffer = list_first_item(&dev->bsink_list, struct comp_buffer,
634 source_list);
635 periods = config->periods_sink;
636 break;
637 case FILE_WRITE:
638 buffer = list_first_item(&dev->bsource_list,
639 struct comp_buffer, sink_list);
640 periods = config->periods_source;
641 break;
642 default:
643 /* TODO: duplex mode */
644 break;
645 }
646
647 if (!buffer) {
Ranjani Sridharan42b1a2e2018-06-27 19:39:20 -0700648 fprintf(stderr, "error: no sink/source buffer\n");
Ranjani Sridharan1d5b67f2018-05-31 19:29:06 -0700649 return -EINVAL;
650 }
651
652 /* set downstream buffer size */
653 switch (config->frame_fmt) {
654 case(SOF_IPC_FRAME_S16_LE):
655 ret = buffer_set_size(buffer, dev->frames * 2 *
656 periods * dev->params.channels);
657 if (ret < 0) {
Ranjani Sridharan42b1a2e2018-06-27 19:39:20 -0700658 fprintf(stderr, "error: file buffer size set\n");
Ranjani Sridharan1d5b67f2018-05-31 19:29:06 -0700659 return ret;
660 }
661 buffer_reset_pos(buffer);
662
663 /* set file function */
664 cd->file_func = file_s16;
665 break;
666 case(SOF_IPC_FRAME_S24_4LE):
667 ret = buffer_set_size(buffer, dev->frames * 4 *
668 periods * dev->params.channels);
669 if (ret < 0) {
670 fprintf(stderr, "error: file buffer size set\n");
671 return ret;
672 }
673 buffer_reset_pos(buffer);
674
675 /* set file function */
676 cd->file_func = file_s24;
677 break;
678 case(SOF_IPC_FRAME_S32_LE):
679 ret = buffer_set_size(buffer, dev->frames * 4 *
680 periods * dev->params.channels);
681 if (ret < 0) {
682 fprintf(stderr, "error: file buffer size set\n");
683 return ret;
684 }
685 buffer_reset_pos(buffer);
686 break;
687 default:
688 return -EINVAL;
689 }
690
691 dev->state = COMP_STATE_PREPARE;
692
693 return ret;
694}
695
696static int file_reset(struct comp_dev *dev)
697{
698 dev->state = COMP_STATE_INIT;
699
700 return 0;
701}
702
703struct comp_driver comp_file = {
704 .type = SOF_COMP_FILEREAD,
705 .ops = {
706 .new = file_new,
707 .free = file_free,
708 .params = file_params,
709 .cmd = file_cmd,
710 .trigger = file_trigger,
711 .copy = file_copy,
712 .prepare = file_prepare,
713 .reset = file_reset,
714 },
715};
716
717void sys_comp_file_init(void)
718{
719 comp_register(&comp_file);
720}