Skip to content
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

Merged
merged 3 commits into from
Oct 23, 2023

Conversation

singalsu
Copy link
Collaborator

@singalsu singalsu commented Sep 25, 2023

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:

Copy link
Member

@lgirdwood lgirdwood left a 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.

@singalsu
Copy link
Collaborator Author

@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.

@singalsu
Copy link
Collaborator Author

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.
Copy link
Collaborator

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?

Copy link
Collaborator Author

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?

Copy link
Contributor

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.

Copy link
Collaborator Author

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.

Copy link
Contributor

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.

Copy link
Collaborator

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

Copy link
Contributor

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

@RanderWang
Copy link
Collaborator

@singalsu How do you test it ? it is used by chrome so it is better to sync with chrome team.

@singalsu
Copy link
Collaborator Author

singalsu commented Sep 28, 2023

@singalsu How do you test it ? it is used by chrome so it is better to sync with chrome team.

I tested this in UPX nocodec mode with new very simple topology in PR #8266. Testing with Chrome sounds like a good idea but I can't do it myself. I'll see if I can get help from them to test this.

@yongzhi1
Copy link
Contributor

Sanity checked the patch along with thesofproject/linux#4550 and #8101, #8246, #8204, no regression found with AEC mock module.

@cujomalainey
Copy link
Member

Apologies for the delay here, I will look on monday, been a crazy week

@lgirdwood
Copy link
Member

@singalsu still draft ?

@singalsu
Copy link
Collaborator Author

singalsu commented Oct 2, 2023

@singalsu still draft ?

I think it's best to wait for merge of #8237 that is a larger change.

int n, m;

channels = MIN(state->num_capture_channels, state->num_aec_reference_channels);
channels = MIN(state->num_output_channels, channels);
Copy link
Collaborator

@ranj063 ranj063 Oct 4, 2023

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?

Copy link
Collaborator Author

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?

Copy link
Member

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.

Copy link
Collaborator Author

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++)
Copy link
Contributor

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.

Copy link
Collaborator Author

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.

@cujomalainey cujomalainey mentioned this pull request Oct 9, 2023
@cujomalainey
Copy link
Member

@singalsu still draft ?

I think it's best to wait for merge of #8237 that is a larger change.

Let's go with this first since mine won't work without yours unless I update the mock

@singalsu singalsu marked this pull request as ready for review October 9, 2023 14:50
@@ -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
Copy link
Member

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

Copy link
Collaborator Author

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,
Copy link
Contributor

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

Copy link
Collaborator Author

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.
Copy link
Contributor

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

#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;
Copy link
Member

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

Copy link
Collaborator Author

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;
Copy link
Member

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

Copy link
Collaborator Author

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.

FYI @marcinszkudlinski

Copy link
Member

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.

@singalsu singalsu changed the title Audio: Convert Google RTC audio processing to module API [DNM] Audio: Convert Google RTC audio processing to module API Oct 11, 2023
@singalsu
Copy link
Collaborator Author

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.

@lgirdwood
Copy link
Member

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.

@marcinszkudlinski
Copy link
Contributor

Please proceed with merging
I'm finishing with changes to sink/src interface and DP processing of the module, all my changes have been based on top of this PR
So pls proceed and merge it once ready

@singalsu singalsu changed the title [DNM] Audio: Convert Google RTC audio processing to module API Audio: Convert Google RTC audio processing to module API Oct 20, 2023
@singalsu
Copy link
Collaborator Author

@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.

Copy link
Contributor

@lkoenig lkoenig left a 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?

Copy link
Contributor

@lkoenig lkoenig left a 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.

src/arch/host/configs/library_defconfig Show resolved Hide resolved
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)
Copy link
Contributor

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).

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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!

@singalsu
Copy link
Collaborator Author

singalsu commented Oct 20, 2023

The change looks good: I haven't look into too much details. Did you run it with the mock an d got good audio?

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.

@cujomalainey
Copy link
Member

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>
@cujomalainey cujomalainey merged commit 3d040c7 into thesofproject:main Oct 23, 2023
40 of 42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants