Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/audio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD)
list(APPEND base_files host-legacy.c)
sof_list_append_ifdef(CONFIG_COMP_DAI base_files dai-legacy.c)
endif()
if(CONFIG_COMP_TEMPLATE_COMP)
add_subdirectory(template_comp)
endif()
endif()

### Common files (also used in shared library build)
Expand Down
2 changes: 2 additions & 0 deletions src/audio/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ rsource "mfcc/Kconfig"

rsource "codec/Kconfig"

rsource "template_comp/Kconfig"

endmenu # "Audio components"

menu "Data formats"
Expand Down
15 changes: 15 additions & 0 deletions src/audio/template_comp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: BSD-3-Clause

if(CONFIG_COMP_TEMPLATE_COMP STREQUAL "m")
add_subdirectory(llext ${PROJECT_BINARY_DIR}/template_comp_llext)
add_dependencies(app template_comp)
else()
add_local_sources(sof template.c)
add_local_sources(sof template-generic.c)

if(CONFIG_IPC_MAJOR_3)
add_local_sources(sof template-ipc3.c)
elseif(CONFIG_IPC_MAJOR_4)
add_local_sources(sof template-ipc4.c)
endif()
endif()
11 changes: 11 additions & 0 deletions src/audio/template_comp/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: BSD-3-Clause

config COMP_TEMPLATE_COMP
tristate "Template_comp example component"
default y
help
Select for template_comp component. Reason for existence
is to provide a minimal component example and use as
placeholder in processing pipelines. As example processing
it swaps or reverses the channels when the switch control
is enabled.
7 changes: 7 additions & 0 deletions src/audio/template_comp/llext/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) 2025 Intel Corporation.
# SPDX-License-Identifier: Apache-2.0

sof_llext_build("template_comp"
SOURCES ../template_comp.c
LIB openmodules
)
6 changes: 6 additions & 0 deletions src/audio/template_comp/llext/llext.toml.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include <tools/rimage/config/platform.toml>
#define LOAD_TYPE "2"
#include "../template_comp.toml"

[module]
count = __COUNTER__
230 changes: 230 additions & 0 deletions src/audio/template_comp/template-generic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
// SPDX-License-Identifier: BSD-3-Clause
//
// Copyright(c) 2025 Intel Corporation.

#include <sof/audio/module_adapter/module/generic.h>
#include <sof/audio/component.h>
#include <sof/audio/sink_api.h>
#include <sof/audio/sink_source_utils.h>
#include <sof/audio/source_api.h>
#include <stdint.h>
#include "template.h"

#if CONFIG_FORMAT_S16LE
/**
* template_comp_s16() - Process S16_LE format.
* @mod: Pointer to module data.
* @source: Source for PCM samples data.
* @sink: Sink for PCM samples data.
* @frames: Number of audio data frames to process.
*
* This is the processing function for 16-bit signed integer PCM formats. The
* audio samples in every frame are re-order to channels order defined in
* component data channel_map[].
*
* Return: Value zero for success, otherwise an error code.
*/
static int template_comp_s16(const struct processing_module *mod,
struct sof_source *source,
struct sof_sink *sink,
uint32_t frames)
{
struct template_comp_comp_data *cd = module_get_private_data(mod);
int16_t *x, *x_start, *x_end;
int16_t *y, *y_start, *y_end;
size_t size;
int x_size, y_size;
int source_samples_without_wrap;
int samples_without_wrap;
int samples = frames * cd->channels;
int bytes = frames * cd->frame_bytes;
int ret;
int ch;
int i;

/* Get pointer to source data in circular buffer, get buffer start and size to
* check for wrap. The size in bytes is converted to number of s16 samples to
* control the samples process loop. If the number of bytes requested is not
* possible, an error is returned.
*/
ret = source_get_data(source, bytes, (void const **)&x, (void const **)&x_start, &size);
if (ret)
return ret;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be better to put if (ret) return ret; directly after line 50, before x_size calculation. Particularly since this is a template, so a copy-paste source

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, though I tried to make more compact looking code this way.


x_size = size >> 1; /* Bytes to number of s16 samples */

/* Similarly get pointer to sink data in circular buffer, buffer start and size. */
ret = sink_get_buffer(sink, bytes, (void **)&y, (void **)&y_start, &size);
if (ret)
return ret;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto, and in other formats below too of course

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, fixed now.


y_size = size >> 1; /* Bytes to number of s16 samples */

/* Set helper pointers to buffer end for wrap check. Then loop until all
* samples are processed.
*/
x_end = x_start + x_size;
y_end = y_start + y_size;
while (samples) {
/* Find out samples to process before first wrap or end of data. */
source_samples_without_wrap = x_end - x;
samples_without_wrap = y_end - y;
samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap);
samples_without_wrap = MIN(samples_without_wrap, samples);

/* Since the example processing is for frames of audio channels, process
* with step of channels count.
*/
for (i = 0; i < samples_without_wrap; i += cd->channels) {
/* In inner loop process the frame. As example re-arrange the channels
* as defined in array channel_map[].
*/
for (ch = 0; ch < cd->channels; ch++) {
*y = *(x + cd->channel_map[ch]);
y++;
}
x += cd->channels;
}

/* One of the buffers needs a wrap (or end of data), so check for wrap */
x = (x >= x_end) ? x - x_size : x;
y = (y >= y_end) ? y - y_size : y;

/* Update processed samples count for next loop iteration. */
samples -= samples_without_wrap;
}

/* Update the source and sink for bytes consumed and produced. Return success. */
source_release_data(source, bytes);
sink_commit_buffer(sink, bytes);
return 0;
}
#endif /* CONFIG_FORMAT_S16LE */

#if CONFIG_FORMAT_S32LE || CONFIG_FORMAT_S32LE
/**
* template_comp_s32() - Process S32_LE or S24_4LE format.
* @mod: Pointer to module data.
* @source: Source for PCM samples data.
* @sink: Sink for PCM samples data.
* @frames: Number of audio data frames to process.
*
* Processing function for signed integer 32-bit PCM formats. The same
* function works for s24 and s32 formats since the samples values are
* not modified in computation. The audio samples in every frame are
* re-order to channels order defined in component data channel_map[].
*
* Return: Value zero for success, otherwise an error code.
*/
static int template_comp_s32(const struct processing_module *mod,
struct sof_source *source,
struct sof_sink *sink,
uint32_t frames)
{
struct template_comp_comp_data *cd = module_get_private_data(mod);
int32_t *x, *x_start, *x_end;
int32_t *y, *y_start, *y_end;
size_t size;
int x_size, y_size;
int source_samples_without_wrap;
int samples_without_wrap;
int samples = frames * cd->channels;
int bytes = frames * cd->frame_bytes;
int ret;
int ch;
int i;

/* Get pointer to source data in circular buffer, get buffer start and size to
* check for wrap. The size in bytes is converted to number of s16 samples to
* control the samples process loop. If the number of bytes requested is not
* possible, an error is returned.
*/
ret = source_get_data(source, bytes, (void const **)&x, (void const **)&x_start, &size);
if (ret)
return ret;

x_size = size >> 2; /* Bytes to number of s32 samples */

/* Similarly get pointer to sink data in circular buffer, buffer start and size. */
ret = sink_get_buffer(sink, bytes, (void **)&y, (void **)&y_start, &size);
if (ret)
return ret;

y_size = size >> 2; /* Bytes to number of s32 samples */

/* Set helper pointers to buffer end for wrap check. Then loop until all
* samples are processed.
*/
x_end = x_start + x_size;
y_end = y_start + y_size;
while (samples) {
/* Find out samples to process before first wrap or end of data. */
source_samples_without_wrap = x_end - x;
samples_without_wrap = y_end - y;
samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap);
samples_without_wrap = MIN(samples_without_wrap, samples);

/* Since the example processing is for frames of audio channels, process
* with step of channels count.
*/
for (i = 0; i < samples_without_wrap; i += cd->channels) {
/* In inner loop process the frame. As example re-arrange the channels
* as defined in array channel_map[].
*/
for (ch = 0; ch < cd->channels; ch++) {
*y = *(x + cd->channel_map[ch]);
y++;
}
x += cd->channels;
}

/* One of the buffers needs a wrap (or end of data), so check for wrap */
x = (x >= x_end) ? x - x_size : x;
y = (y >= y_end) ? y - y_size : y;

/* Update processed samples count for next loop iteration. */
samples -= samples_without_wrap;
}

/* Update the source and sink for bytes consumed and produced. Return success. */
source_release_data(source, bytes);
sink_commit_buffer(sink, bytes);
return 0;
}
#endif /* CONFIG_FORMAT_S32LE || CONFIG_FORMAT_S24LE */

/* This struct array defines the used processing functions for
* the PCM formats
*/
const struct template_comp_proc_fnmap template_comp_proc_fnmap[] = {
#if CONFIG_FORMAT_S16LE
{ SOF_IPC_FRAME_S16_LE, template_comp_s16 },
#endif
#if CONFIG_FORMAT_S24LE
{ SOF_IPC_FRAME_S24_4LE, template_comp_s32 },
#endif
#if CONFIG_FORMAT_S32LE
{ SOF_IPC_FRAME_S32_LE, template_comp_s32 },
#endif
};

/**
* template_comp_find_proc_func() - Find suitable processing function.
* @src_fmt: Enum value for PCM format.
*
* This function finds the suitable processing function to use for
* the used PCM format. If not found, return NULL.
*
* Return: Pointer to processing function for the requested PCM format.
*/
template_comp_func template_comp_find_proc_func(enum sof_ipc_frame src_fmt)
{
int i;

/* Find suitable processing function from map */
for (i = 0; i < ARRAY_SIZE(template_comp_proc_fnmap); i++)
if (src_fmt == template_comp_proc_fnmap[i].frame_fmt)
return template_comp_proc_fnmap[i].template_comp_proc_func;

return NULL;
}
96 changes: 96 additions & 0 deletions src/audio/template_comp/template-ipc3.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: BSD-3-Clause
//
// Copyright(c) 2025 Intel Corporation.

#include <sof/audio/module_adapter/module/generic.h>
#include <sof/audio/component.h>
#include "template.h"

LOG_MODULE_DECLARE(template_comp, CONFIG_SOF_LOG_LEVEL);

/* This function handles the real-time controls. The ALSA controls have the
* param_id set to indicate the control type. The control ID, from topology,
* is used to separate the controls instances of same type. In control payload
* the num_elems defines to how many channels the control is applied to.
*/
__cold int template_comp_set_config(struct processing_module *mod, uint32_t param_id,
enum module_cfg_fragment_position pos,
uint32_t data_offset_size, const uint8_t *fragment,
size_t fragment_size, uint8_t *response,
size_t response_size)
{
struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment;
struct template_comp_comp_data *cd = module_get_private_data(mod);
struct comp_dev *dev = mod->dev;

assert_can_be_cold();

comp_dbg(dev, "template_comp_set_config()");

switch (cdata->cmd) {
case SOF_CTRL_CMD_SWITCH:
if (cdata->index != 0) {
comp_err(dev, "Illegal switch control index = %d.", cdata->index);
return -EINVAL;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto - you also save 2 levels of indentation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, good finding. Note that I wanted to keep index != 0 to make it obvious these are numbers assigned for controls in topology. I trust the compiler to find the best instructions to handle it.


if (cdata->num_elems != 1) {
comp_err(dev, "Illegal switch control num_elems = %d.", cdata->num_elems);
return -EINVAL;
}

cd->enable = cdata->chanv[0].value;
comp_info(dev, "Setting enable = %d.", cd->enable);
return 0;

case SOF_CTRL_CMD_ENUM:
comp_err(dev, "Illegal enum control, no support in this component.");
return -EINVAL;
case SOF_CTRL_CMD_BINARY:
comp_err(dev, "Illegal bytes control, no support in this component.");
return -EINVAL;
}

comp_err(dev, "Illegal control, unknown type.");
return -EINVAL;
}

__cold int template_comp_get_config(struct processing_module *mod,
uint32_t config_id, uint32_t *data_offset_size,
uint8_t *fragment, size_t fragment_size)
{
struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment;
struct template_comp_comp_data *cd = module_get_private_data(mod);
struct comp_dev *dev = mod->dev;

assert_can_be_cold();

comp_info(dev, "template_comp_get_config()");

switch (cdata->cmd) {
case SOF_CTRL_CMD_SWITCH:
if (cdata->index != 0) {
comp_err(dev, "Illegal switch control index = %d.", cdata->index);
return -EINVAL;
}

if (cdata->num_elems != 1) {
comp_err(dev, "Illegal switch control num_elems = %d.", cdata->num_elems);
return -EINVAL;
}

cdata->chanv[0].value = cd->enable;
return 0;

case SOF_CTRL_CMD_ENUM:
comp_err(dev, "Illegal enum control, no support in this component.");
return -EINVAL;

case SOF_CTRL_CMD_BINARY:
comp_err(dev, "Illegal bytes control, no support in this component.");
return -EINVAL;
}

comp_err(dev, "Illegal control, unknown type.");
return -EINVAL;
}
Loading
Loading