Audio: template_comp: Add a new SOF template component#9952
Audio: template_comp: Add a new SOF template component#9952lgirdwood merged 2 commits intothesofproject:mainfrom
Conversation
| *y = *(x + cd->channels_order[ch]); | ||
| y++; | ||
| } | ||
| x += cd->channels; |
There was a problem hiding this comment.
maybe in a follow-up - should we add template-generic.c and template-hifi5.c with different versions of these functions? Then also a template_comp.h header becomes justified, otherwise it isn't really needed?
There was a problem hiding this comment.
I need to complete this task quickly for coming power-on and move on to other critical things, so I'd like to postpone the HiFi5 version. But sure it makes sense, and shows how to use the Xtensa version helper macros.
There was a problem hiding this comment.
Also per my own experience the xt-clang compiler for HiFi5 is good, it's hard to beat the performance of good C code (e.g. hot code loops unrolled by four or eight) with hand written intrinsics. Intrinsics are good for saturated math with rounding that is not efficient with C but otherwise the gain can be low.
There was a problem hiding this comment.
@singalsu I think we can move this functions now into a file name template-generic.c. It doesn't pollute the template_comp.c code and makes everything easier to read.
| return NULL; | ||
| } | ||
|
|
||
| static int template_comp_process(struct processing_module *mod, |
There was a problem hiding this comment.
I strongly suggest using sink/source in the template for new modules
There was a problem hiding this comment.
I've converted it now to sink/source. But as not experienced with it, not sure if the best way for everything. Some things look now a bit more complex than before. Can you please check again this!
There was a problem hiding this comment.
@marcinszkudlinski has your request been addressed correctly? Should your request for change be dropped?
61de80a to
5f3c2c4
Compare
|
Into this version I fixed the rimage .toml for ACE platforms, it loads and runs now. @marcinszkudlinski Thanks for suggestion, good idea! I'll update next this to sink/source api. |
|
@singalsu we already have similar component Looks good. I'm working on a presentation for EOSS/ZDS on how to add a new component to SOF and I could use this PR as an example. Is this component working only with IPC4? |
lgirdwood
left a comment
There was a problem hiding this comment.
The code and topology look fine (we need the kcontrol logic), but we need
- comments everywhere ! This should be like a book on how to create a module, AI should be able to understand it.
- Based on all the files changes in this PR, can we add a Readme.md file that lists all the setpc needed (based on the steps taken in this PR.
| */ | ||
| SOF_DEFINE_REG_UUID(template_comp); | ||
|
|
||
| DECLARE_TR_CTX(template_comp_tr, SOF_UUID(template_comp_uuid), LOG_LEVEL_INFO); |
There was a problem hiding this comment.
Can we comment everything ! e.g. what is it for, why needed, how to use etc etc.
| @@ -0,0 +1,21 @@ | |||
| #ifndef LOAD_TYPE | |||
There was a problem hiding this comment.
I would also put a lot cf comments in here too.
There was a problem hiding this comment.
Unfortunately I don't know the meaning of these. We have a document start in https://github.com/thesofproject/sof-docs/pull/451/files but it was never merged to sof-docs where it should be described.
abonislawski
left a comment
There was a problem hiding this comment.
Definitely need more comments, this template seems quite complicated
| return 0; | ||
| } | ||
|
|
||
| __cold static int template_comp_free(struct processing_module *mod) |
There was a problem hiding this comment.
e.g. here a short comment on why __cold
5f3c2c4 to
19822f1
Compare
| if (cd->enable) { | ||
| /* Process the data with the channels | ||
| * swap example function | ||
| */ |
There was a problem hiding this comment.
can this comment just fit on a single line?
|
@singalsu I left some comments. Would it also be possible to add IPC3 support? |
Thanks! Yes I could add (control handling is main difference), but I'm not able to test it. Also need a topology. Could it be another PR after this? |
| struct template_comp_comp_data { | ||
| template_comp_func template_comp_func; /**< processing function */ | ||
| int channels_order[PLATFORM_MAX_CHANNELS]; /**< channels ordering */ | ||
| int source_format; /**< audio format for input */ |
There was a problem hiding this comment.
I forgot to remove this, not used.
Ok, could be a separate PR. I can help test it. |
Yep, testing help and topology for IPC3 platform would be great. For Intel builds we can't have IPC3 and sink/source api without tricky back-porting. |
@dbaluta @singalsu can we then split this into |
9f24b39 to
99720e6
Compare
| 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; | ||
|
|
||
| ret = source_get_data(source, bytes, (void const **)&x, (void const **)&x_start, &size); | ||
| x_size = size >> 1; /* Bytes to number of s16 samples */ | ||
| if (ret) | ||
| return ret; | ||
|
|
||
| ret = sink_get_buffer(sink, bytes, (void **)&y, (void **)&y_start, &size); | ||
| y_size = size >> 1; /* Bytes to number of s16 samples */ | ||
| if (ret) | ||
| return ret; | ||
|
|
||
| x_end = x_start + x_size; | ||
| y_end = y_start + y_size; | ||
| while (samples) { | ||
| 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); |
There was a problem hiding this comment.
@singalsu can you have a comment for each step as this is a learning module. I'm good to merge if you can give a good commentry in the module and I can come back and add more context on the infra.
There was a problem hiding this comment.
Yep, I added comments to things happening in processing function.
99720e6 to
c95ce87
Compare
| * as defined in array channels_order[]. | ||
| */ | ||
| for (ch = 0; ch < cd->channels; ch++) { | ||
| *y = *(x + cd->channels_order[ch]); |
There was a problem hiding this comment.
isn't it similar to channel mapping in some other components, would channel_map be a better name for it?
There was a problem hiding this comment.
I'm changing it since you prefer it, though this is not the same. Channels map elsewhere is an enum describing the stream, e.g. stereo or 5.1.
There was a problem hiding this comment.
Adding, yes you are right, there's in IPC4 copiers usage a channel map vector.
| ret = source_get_data(source, bytes, (void const **)&x, (void const **)&x_start, &size); | ||
| x_size = size >> 1; /* Bytes to number of s16 samples */ | ||
| if (ret) | ||
| return ret; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
OK, though I tried to make more compact looking code this way.
| ret = sink_get_buffer(sink, bytes, (void **)&y, (void **)&y_start, &size); | ||
| y_size = size >> 1; /* Bytes to number of s16 samples */ | ||
| if (ret) | ||
| return ret; |
There was a problem hiding this comment.
ditto, and in other formats below too of course
| comp_err(dev, "Illegal switch control num_elems = %d.", | ||
| cdata->num_elems); | ||
| return -EINVAL; | ||
| } |
There was a problem hiding this comment.
also a nitpick, but since this is a template:
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);
i.e. check and return in case invalid, then process with standard processing without an else
| } else { | ||
| comp_err(dev, "Illegal switch control index = %d.", cdata->index); | ||
| return -EINVAL; | ||
| } |
There was a problem hiding this comment.
ditto - you also save 2 levels of indentation
There was a problem hiding this comment.
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.
src/audio/template_comp/template.c
Outdated
| frames = MIN(frames, sink_frames); | ||
| if (cd->enable) { | ||
| /* Process the data with the channels swap example function. */ | ||
| ret = cd->template_comp_func(mod, source, sink, frames); |
There was a problem hiding this comment.
could do return cd->template_comp_func(...); and remove the else below and the ret variable
src/audio/template_comp/template.c
Outdated
| enum sof_ipc_frame source_format; | ||
| int i; | ||
|
|
||
| comp_info(dev, "template_comp_prepare()"); |
There was a problem hiding this comment.
we don't even make .prepare() functions __cold because they should run reasonably fast. Better make this comp_dbg()
src/audio/template_comp/template.c
Outdated
| { | ||
| struct template_comp_comp_data *cd = module_get_private_data(mod); | ||
|
|
||
| comp_info(mod->dev, "template_comp_reset()"); |
src/audio/template_comp/template.c
Outdated
|
|
||
| assert_can_be_cold(); | ||
|
|
||
| comp_info(mod->dev, "template_comp_free()"); |
There was a problem hiding this comment.
even in __cold functions I'd make these comp_dbg() - sometimes these are useful, e.g. to see which component ID is which type, but IMHO if we do this in all modules - that would be too much. At least would be good to have them consistent. Maybe use comp_info() in all .init() and comp_dbg() in all others
There was a problem hiding this comment.
OK but hesitating a bit with this -- can no more see the life cycle of component from normal trace.
There was a problem hiding this comment.
maybe we should make a decision - how we log .init(), .free(), .set_config() and follow that consistently. Of course for debugging the more the better - if it all were captured perfectly with no negative effects on system performance, but unfortunately both happen: (1) if too much is logged, the logging subsystem will drop log entries automatically, and it can then drop important ones, and (2) performance can suffer
There was a problem hiding this comment.
Yep, the init, free, set_config would be my preference as minimum. The components are not causing as much traces traffic as the other framework.
There was a problem hiding this comment.
Yep, the init, free, set_config would be my preference as minimum. The components are not causing as much traces traffic as the other framework.
@singalsu I wouldn't be against this in principle, if we really weren't already hitting a margin, where adding just a couple more logs triggers Zephyr to drop some. E.g. looking at mtrace in https://sof-ci.01.org/sofpr/PR9952/build12397/devicetest/index.html?model=PTLH_RVP_NOCODEC&testcase=check-playback-all-formats which is good because it runs 1 stream at a time. A stream there begins with
[ 588.396178] <inf> ipc: ipc_cmd: rx : 0x44000000|0x3060004c
which is MOD_LARGE_CONFIG_SET. This doesn't yet belong to the actual streaming but the next IPC
[ 588.398488] <inf> ipc: ipc_cmd: rx : 0x11000005|0x0
is already GLB_CREATE_PIPELINE. The last log entry, that belongs to stream configuration before starting streaming is
[ 588.421185] <wrn> host_comp: host_get_copy_bytes_normal: comp:0 0x3 no bytes to copy, available samples: 0, free_samples: 192
which is less than 3ms later. In this time we generate 66 log entries. And - again - this is just one stream. If we do init-free-set_config for each component, in this case we have 7 components in 2 pipelines, would be 14 lines alone (.free() isn't called during construction, but might interfere if we run multiple streams). Of those 66 lines 22 lines are IPCs (I think we do need these, I find them very useful), 12 "pipe: pipeline_connect:" lines (also rather useful IMHO), 6 "buffer_new: buffer new size ..." lines (do we need these?)... So 14 lines more seems a rather significant addition. But you can of course make such a test PR and we'll see what mtrace logs will look like there
There was a problem hiding this comment.
I prefer the simple human readable traces, I've unless desperate never tried to decode in my mind the ipx rx/tx hex commands. We also see those IPC hex dumps in kernel messages.
This patch contains all needed to add a new minimal SOF component to FW build for TGL and more recent platforms plus sof-testbench4 simulation. the component name is template_comp and it is easy to duplicate for new component development from scratch. The component supports one switch kcontrol. When enabled the component swaps or reverses the channels order if there are two or more channels. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
This patch adds the template_comp widget class to topology2 and provides three topologies with word lengths 16/24/32 bits to test the component in playback and capture. The topologies are built to development directory with names sof-hda-benchmark-template_comp<16/24/32>.tplg. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
c95ce87 to
ef5b0ba
Compare
marcinszkudlinski
left a comment
There was a problem hiding this comment.
looks OK, just minor suggestion for comments regarding multiple inputs/outputs
|
|
||
| comp_dbg(dev, "template_comp_prepare()"); | ||
|
|
||
| if (num_of_sources != 1 || num_of_sinks != 1) |
There was a problem hiding this comment.
add a comment - there may be more than 1 input and/or output.
There was a problem hiding this comment.
@singalsu can you update incrementally, will merge this.
There was a problem hiding this comment.
Yep, I'll do another PR to address Marcin's additions.
There was a problem hiding this comment.
| { | ||
| struct template_comp_comp_data *cd = module_get_private_data(mod); | ||
| struct comp_dev *dev = mod->dev; | ||
| struct sof_source *source = sources[0]; |
No description provided.