Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

stijnvanbeek
Copy link
Contributor

The AudioPlaybackComponent that plays the audiofile internally has 2 BufferPlayerNodes, one for each channel. The FFTNode is mono and only connects to the first channel BufferPlayerNode. The OutputComponent contains two OutputNodes that each connect to their own BufferPlayerNode. However this is what happens chronologically on init:

  • The AudioServices initialize and start the audio device and processing the NodeManager.
  • The AudioPlaybackComponent is initialized with both BufferPlayerNodes
  • Second the FFTAudioNodeComponent is initialized, that contains only one FFTNode that only connects to the first BufferPlayerNode
  • The NodeManager is already processing, calling FFTNode::process() because it's a root process, which in turn calls process on the BufferPlayerNode it is connected to. This results in the playback position in the audiofile of only the first channel BufferPlayerNode to be increased!
  • Only now the OutputComponent is initialized, connecting it's 2 OutputNodes to both BufferPlayerNodes, causing also the other channel to be processed. However, the playback position of the left and right BufferPlayerNodes is already out of sync.

I verified this and I can see that indeed the FFTNode is already being processed a few times before the OutputComponent is initialized. This also explains why the delay is more noticeable in debug mode.

I solved the problem by setting PlaybackComponent::mAutoPlay to false and manually started playback in the init() of the app. This makes sure playback only starts after all components are initialized and all nodes are connected.

@cklosters
Copy link
Member

cklosters commented Jan 20, 2025

Thanks for the clarification and 'manual' fix @stijnvanbeek, this will do for now, however: It would be nice if initialization and playback is properly synchronized on startup, without the user having to be aware of the underlying structure and manually starting playback on init. Maybe we can solve using the on postResourcesLoaded() of the audio service?

@stijnvanbeek
Copy link
Contributor Author

stijnvanbeek commented Jan 20, 2025

I solved it now differently by handling the mAutoPlay property in the first update() call instead of on init(). Then the user can be assured the whole DSP patch is finished once playback starts.

@cklosters
Copy link
Member

cklosters commented Jan 20, 2025

I solved it now differently by handling the mAutoPlay property in the first update() call instead of on init(). Then the user can be assured the whole DSP patch is finished once playback starts.

This works but we normally try to avoid / don't allow this pattern (bool on update) because it introduces an indirection that should / can be avoided using a better approach. I therefore prefer the manual solution until we 'properly' fix it. What we normally (you could) do is cache the loaded objects - they (de)register themselves with the service on init & destroy - and start playback on post load when autoplay is set to true. This removes the need to introduce a flag that is evaluated every time on update.

Something like the above or a different solution. I therefore propose to roll-back your latest change, merge the manual fix and create a seperate issue for the inconvenience?

@stijnvanbeek
Copy link
Contributor Author

Will do. In the meantime, do you have an example in the source of objects being cached on init and processed somehow post init?

The thing is I don't want the AudioService to be aware of the BufferPlayerNode, the PlaybackComponent or the AutoPlay property. These are all high level constructs for specific functionality.

@cklosters
Copy link
Member

cklosters commented Jan 20, 2025

Will do. In the meantime, do you have an example in the source of objects being cached on init and processed somehow post init?

See for example: SceneService::registerScene and SceneService::unregisterScene.

Another (more advanced) example is lights in render advanced: RenderAdvancedService::postResourcesLoaded(), RenderAdvancedService::registerLightComponent, RenderAdvancedService::removeLightComponent etc..

The thing is I don't want the AudioService to be aware of the BufferPlayerNode, the PlaybackComponent or the AutoPlay property. These are all high level constructs for specific functionality.

Many modules use their service to catch and handle global state changes (limited to the scope of their module) - it is what it is partly designed for - the postResourcesLoaded virtual function was introduced specifically for this reason. You can forward declare your resources and include the header in the cpp. Because the service is tied to your module and the resources associated with it I see no reason why it is 'wrong' to reference them. We do it for lights in the render advanced service, I don't see why it would be a problem here for players - they belong to the module and therefore service of that module.

@stijnvanbeek
Copy link
Contributor Author

stijnvanbeek commented Jan 20, 2025

I could give the AudioComponentInstance baseclass a virtual start() method?
And then call if from AudioService::postResourcesLoaded.
That could also be nice for starting oscillators and stuff like that.

@cklosters
Copy link
Member

cklosters commented Jan 20, 2025

I could give the AudioComponentInstance baseclass a virtual start() method? And then call if from AudioService::postResourcesLoaded.

Yes that should work just fine - however, do not use (rely) on ResourceManager::getObjects<T> inside AudioService::postResourcesLoaded to query players - long story short: the resource manager doesn't delete renamed objects that are hot-reloaded, which is a tricky (more fundamental) discussion because that would introduce resource ptrs to be null (allowed?). Therefore best to register on init() and unregister on destroy() - this ensures your cache is up to date when AudioService::postResourcesLoaded is called.

@cklosters cklosters merged commit ce93962 into main Jan 21, 2025
@cklosters cklosters deleted the fft-audiofile-stereosync branch January 21, 2025 09:18
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.

2 participants