-
Notifications
You must be signed in to change notification settings - Fork 318
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Audio: Convert Google RTC audio processing to module API #8258
Audio: Convert Google RTC audio processing to module API #8258
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@singalsu good stuff !
@johnylin76 @cujomalainey fyi - pls review.
054d298
to
55f587d
Compare
@cujomalainey This is the first version that works with created test topology in sof-nocodec style, sharing it soon as well. I think it's best to get #8237 merged first, then I'll rebase this. |
55f587d
to
752aae6
Compare
Fixed conflict with 7112246 ("google_rtc_audio_processing: remove buffer_acquire") |
* The config blob is not referenced after reconfigure() returns | ||
* so it is safe to call comp_get_data_blob here which frees the | ||
* old blob. This assumes cmd() and prepare()/copy() cannot run | ||
* concurrently which is the case when there is no preemption. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in principle .copy()
can run concurrently with IPCs, in which case the timer interrupt can interrupt IPC processing, then the scheduler will choose between the IPC thread and the LL scheduler thread and will pick up the latter, since it has a higher priority. But if the component has only one sink and one source, mostly it's only the "stop" IPC that can arrive while the stream is already running, or can some configuration be sent to a running stream too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I preserved this part as close to the original so this should not change the operation. @lkoenig What do you think of this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Configurations can be sent when a stream is running.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lyakh @lkoenig I think it depends on the (unknown) internals of the processing whether new config data blob during copy() is a hazard or not. The open-source mock-up version ignores the blob content totally.
The reconfigure set here should ensure that the next copy() correctly re-initializes for the blob. Please let me know if this needs to be addressed now, I could propose a fix. Or (preferably) let @thesofproject/google address this in successive PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The data should be whole and not altered during the call of GoogleRtcAudioProcessingReconfigure
and GoogleRtcAudioProcessingParseSofConfigMessage
As of today we do not have concurrent access protection enabled in the GoogleRtcAudioProcessing
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@singalsu at least I would note in the comment, that a race is possible and should be fixed or at least further investigated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For DP processing there are more possibilities of race, especially if the module runs on secondary core. Processing data in this case is not synchronized with IPC set config in any way and may be even executed at the same time on 2 separate cores
@singalsu How do you test it ? it is used by chrome so it is better to sync with chrome team. |
Sanity checked the patch along with thesofproject/linux#4550 and #8101, #8246, #8204, no regression found with AEC mock module. |
Apologies for the delay here, I will look on monday, been a crazy week |
@singalsu still draft ? |
int n, m; | ||
|
||
channels = MIN(state->num_capture_channels, state->num_aec_reference_channels); | ||
channels = MIN(state->num_output_channels, channels); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@singalsu this works for the 2ch case where the PDM 0 is configured on the board but if PDM 1 is connected, this won't work correctly right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If reference is 2ch and mic & out 4ch then the input and reference would be mixed to first two channels. You are right that mic channels 3 and 4 would not be copied to output. I think at least those should be copied.
Would we wrap reference channels to first if there's less than in output, or just copy mic to output for non-matching channels?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in an ideal world, we would reduce our channels to only the ones we care about so we don't need these silly configs and only have real channels in the data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it to version where every output channel has a mix of mic and reference. Hope it's good for testing now.
|
||
memset(dest, 0, sizeof(int16_t) * state->num_output_channels * state->num_frames); | ||
for (n = 0; n < state->num_frames; ++n) { | ||
*out = *mic + *ref; | ||
for (m = 0; m < channels; m++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it a bug? since previous code does not have channels loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original code mixed only to first channel, with this loop we can mix to other output channels. But according to Ranjani's comment we should decide what to do with non-matching channels counts.
@@ -16,6 +17,7 @@ CONFIG_COMP_VOLUME=y | |||
CONFIG_COMP_VOLUME_LINEAR_RAMP=y | |||
CONFIG_COMP_VOLUME_WINDOWS_FADE=y | |||
CONFIG_DEBUG_MEMORY_USAGE_SCAN=n | |||
CONFIG_GOOGLE_RTC_AUDIO_PROCESSING_MOCK=y |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can just enable the stub config
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, helps with two other conversions also, done!
} | ||
|
||
static int google_rtc_audio_processing_prepare(struct comp_dev *dev) | ||
static int google_rtc_audio_processing_prepare(struct processing_module *mod, | ||
struct sof_source __sparse_cache **sources, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__sparse_cache have been depreciated and removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, thanks!
* The config blob is not referenced after reconfigure() returns | ||
* so it is safe to call comp_get_data_blob here which frees the | ||
* old blob. This assumes cmd() and prepare()/copy() cannot run | ||
* concurrently which is the case when there is no preemption. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For DP processing there are more possibilities of race, especially if the module runs on secondary core. Processing data in this case is not synchronized with IPC set config in any way and may be even executed at the same time on 2 separate cores
752aae6
to
90e67ac
Compare
#if CONFIG_IPC_MAJOR_4 | ||
if (IPC4_SINK_QUEUE_ID(source->id) == SOF_AEC_FEEDBACK_QUEUE_ID) { | ||
#else | ||
if (source->source->pipeline->pipeline_id != dev->pipeline->pipeline_id) { | ||
#endif | ||
cd->aec_reference = source; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am curious why you are opting for the index over the pointer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's because module adapter provides the input and ouput streams as arrays of pointers to process(). I use the index to access the right stream. This also eases the transition to source/sink api in pipeline2.0 with better abstraction where process() receives array of sof_source and sof_sink pointers. It let's us optimize further the pipeline buffers. I'm not sure keeping the old way would prevent that but I felt this is cleaner approach.
What do you think @marcinszkudlinski , was this the right thing to do?
} | ||
comp_update_buffer_consume(buffer, num_aec_reference_bytes); | ||
input_buffers[cd->aec_reference_source].consumed = num_aec_reference_bytes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this a hack for the same side effect as module_update_buffer_position
in which case i think we should fix the api
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, not very clean API -- frames as input and bytes as output. But as far as I understand the design is from reference firmware. Did you mean a similar helper function but for this multiple sources/sinks case? This component will be converted to source/sink API after this PR for DP mode so we could review and possibly make improvements to api there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, i think breaking into this struct is probably not the safest thing for API purposes on the buffer, we should fix the api or make a new call.
Marked this as DNM - I'm not sure if this PR step is necessary in converting this to DP. But please continue reviewing and sharing your thoughts so the next step can be better. |
All modules should use the module API as component API is deprecated and will gradually be removed/refactored.i.e. we only have 1 API for modules. |
Please proceed with merging |
@cujomalainey Is this version good for merge? It will be a short time version only. Work by @marcinszkudlinski for DP schduling is now done and needs this patch as base. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change looks good: I haven't look into too much details.
Did you run it with the mock an d got good audio?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice change so we can some at least build coverage.
ir = 0; | ||
for (io = 0; io < state->num_output_channels; io++) { | ||
out[io] = sat_int16((int32_t)mic[im++] + ref[ir++]); | ||
if (im == state->num_capture_channels) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might mix several time same channel like is output is 2 and input is one channel you'll get 2 times the first channel.
out[io] = sat_int16(
(im < state->num_capture_channels ? (int32_t)mic[im++] : 0) +
(ir < state->num_reference_channles ? ref[ir++] : 0)
);
Might be more appropriate (sorry for the vey poor codestyle).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't really know what's best for testing with mock-up. Sure, I can update it to be like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lkoenig Thanks, I included your suggestion as such, it works well!
Yep, I've listened without glitches heard a good bit of output music from mic and reference inputs feed. with and without mixing, and test topology suitable for UPX board to use this. I have not been able to test bytes control with mock other than it is received. I've changed from menu config the channels to 2 to hear both stereo channels from mockup mix. |
90e67ac
to
858614c
Compare
Sorry been busy internally, I trust @lkoenig 's review |
This patch converts the component to module API. The changes are: - google_rtc_audio_processing_params() is changed to update sources and sink params from IPC4 init IPC. It's not used for IPC3 where module adapter handles params(). - The commands handling is fixed to handle via module adapter the IPC3 and IPC4 commands. - The google_rtc_audio_processing_init() is updated to simpler module adapter client way. - The google_rtc_audio_processing_prepare() is updated to set buffers parameters for IPC4 and find sources buffers for microphone and reference by index instead of buffer pointer. That simplifies the processing function with module adapter. - The google_rtc_audio_processing_process() is updated from copy() to module adapter client way. - google_rtc_audio_processing_get_attribute() is removed - As overall change dev handle is changed to mod handle in nearly all functions. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
With this change e.g. in all 2ch case all output channels are mixed with matching microphone and reference channels. It helps with testing to hear and see that the mockup works. The mix is done for matching microphone, reference and output channels indices. With e.g. less reference channels, the remaining output channels are passed directly from microphone. The mixed samples are saturated to avoid nasty sounding overflows. Signed-off-by: Lionel Koenig <lionelk@google.com> Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
This patch allows to load and run it in testbench. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
858614c
to
30e323a
Compare
Fist commit is module adapter conversion
Second commit is improvement to mockup to mix and saturate all available matching channels.
Third commit adds build and load to testbench.
Related to, no more dependent of: