custom IQuartzQuantizedCommand commands for UAudioComponent and other FQuartzTickableObject objects

How does one create/schedule a custom IQuartzQuantizedCommand such as a scheduled AC stop (or any other commands on an FQuartzTickableObject instance?)

From what I can tell, there is no way to quartz schedule an AudioComponent without subclassing it (and potentially the ClockHandle and Quartz too). Even the delayed stop function is a bit a of a hack (long fade).

I traced how PlayQuantized works and here is what I found out: UAudioComponent::PlayQuantized() creates an AudioComponentQuartzCommandData which is queued up by the clock handle InClockHandle->QueueQuantizedSound() (public).
The ClockHandle then creates a Audio::FQuantizedQueueCommand/FQuantizedPlayCommand command pointer and use UQuartzSubsystem::CreateRequestDataForSchedulePlaySound. (Now Quartz has a deprecated AddCommandToClock method which might have been what to use to implement a scheduled stop, but the deprecation message says: Obtain and use a UQuartzClockHandle / FQuartzClockProxy instead)

Then once the clock says it’s time, it triggers the following chain of events. UAudioComponent::PlayQueuedQuantizedInternal (public) which creates a PlayInternalRequestData which is sent to PlayInternal (protected) which creates an FActiveSound with the scheduled data, which is finally added to the audio device new active sounds via: AudioDevice->AddNewActiveSound

I was hoping I could create a FQuantizedStopCommand (IQuartzQuantizedCommand) and then schedule it directly on the clock. But the clock doesn’t seem to support that anymore:

UE_DEPRECATED(5.1, "This function should not be called directly, and the original functionality has been moved into FQuartzTickable")
	virtual void ProcessCommand(const Audio::FQuartzQuantizedCommandDelegateData& Data) override {}

Now the clock handle is QuartzTickable object and offers a few methods that seem to be related, but I’m getting lost in the indirection and I haven’t even gotten to the part where I implement the Stop command on the AudioComponent :stuck_out_tongue: Any pointers/help would be greatly appreciated.

@MaxHayes replied on Discord and here is his answer:

In C++ land, Audio::FQuartzClockProxy::SendCommandToClock() lets you queue it directly.
The UQuartzClockHandle is just a wrapper around existing commands you can send.

“RawHandle” is the FQuartzClockProxy.

Note by Matt: you can get the proxy from the transport subsystem: by calling UQuartzSubsystem::GetProxyForClock()

But: is it correct that your stop doesn’t need to be sample accurate, just musically accurate?

You could do a quick prototype by calling PlayQuantized w/ a silent sound, and stop the first sound via the command delegate.

FWIW these subscribers are the command queues for the FQuartzTickableObject s. If you wanted to write a custom one, you’d need to check out FQuartzMetronomeDelegateData and FQuartzQuantizedCommandDelegateData (the two existing types of events sent from the AudioRenderThread back to FQuartzTickableObjects).

That comment should say “ProcessCommand()”

Audio component consumption:

UQartzClockHandle consumption:

Lastly, a truly sample-accurate stop would require some changes to AudioMixerSourceManager like done for PlayQuantized.