Unable to Setup Quartz in C++?

I’m trying to setup Quartz through C++ to use for a rhythm project I’m playing around with. However, while the documentation for BP is fairly decent - it is entirely non-existent for C++.

This is how I try and initialize my Quartz clock and its settings;

BeginPlay() Method
void ANewConductor::BeginPlay()
{
	if (!IsValid(Chart)) return;
	
	AudioComponent->SetSound(Chart->Sound);
	
	UQuartzSubsystem* QuartzSubsystem = AudioComponent->GetQuartzSubsystem();
	const UWorld* World = AudioComponent->QuartzGetWorld();
	
	FQuartzClockSettings ClockSettings = FQuartzClockSettings();
	ClockSettings.TimeSignature.NumBeats = 4;
	ClockSettings.TimeSignature.BeatType = EQuartzTimeSignatureQuantization::QuarterNote;
	
	
	ClockHandle = QuartzSubsystem->CreateNewClock(World, FName("ConductorClock"), ClockSettings);
	
	FQuartzQuantizationBoundary QuantizationBoundary = FQuartzQuantizationBoundary();
	QuantizationBoundary.Quantization = EQuartzCommandQuantization::Beat;
	QuantizationBoundary.CountingReferencePoint = EQuarztQuantizationReference::BarRelative;
	
	FOnQuartzMetronomeEventBP MetronomeEvent = FOnQuartzMetronomeEventBP();
	MetronomeEvent.BindUFunction(this, "OnBar");
	
	FOnQuartzCommandEventBP CommandEvent = FOnQuartzCommandEventBP();
	CommandEvent.BindUFunction(this, "IgnoreMe");

	ClockHandle->SetBeatsPerMinute(World, QuantizationBoundary, CommandEvent, ClockHandle, Chart->BeatsPerMinute);
	ClockHandle->SubscribeToQuantizationEvent(World, EQuartzCommandQuantization::Bar, MetronomeEvent, ClockHandle);

	ClockHandle->StartClock(World, ClockHandle);

	AudioComponent->PlayQuantized(World, ClockHandle, QuantizationBoundary, CommandEvent, 0.0f, 0.0f, 0.25f);
}

And these are the errors;

Errors
- Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0x0000000000000198
- [Callstack] 0x00007fff06f1adbb UnrealEditor-AudioMixer.dll!UQuartzClockHandle::SetBeatsPerMinute() [D:\build\++UE5\Sync\Engine\Source\Runtime\AudioMixer\Private\Quartz\AudioMixerClockHandle.cpp:463]
- [Callstack] 0x00007ffeed1d33cc UnrealEditor-MusicGame.dll!ANewConductor::BeginPlay() [C:\Users\Dealman\Documents\Unreal Projects\MusicGame\Source\MusicGame\Private\NewConductor.cpp:54]
- [Callstack] 0x00007fff120c4fef UnrealEditor-Engine.dll!AActor::DispatchBeginPlay() [D:\build\++UE5\Sync\Engine\Source\Runtime\Engine\Private\Actor.cpp:3849]
- [Callstack] 0x00007fff13ebcae2 UnrealEditor-Engine.dll!AWorldSettings::NotifyBeginPlay() [D:\build\++UE5\Sync\Engine\Source\Runtime\Engine\Private\WorldSettings.cpp:283]
- [Callstack] 0x00007fff129f9d11 UnrealEditor-Engine.dll!AGameStateBase::HandleBeginPlay() [D:\build\++UE5\Sync\Engine\Source\Runtime\Engine\Private\GameStateBase.cpp:205]
- [Callstack] 0x00007fff13de6298 UnrealEditor-Engine.dll!UWorld::BeginPlay() [D:\build\++UE5\Sync\Engine\Source\Runtime\Engine\Private\World.cpp:4908]
- [Callstack] 0x00007fff129b45d5 UnrealEditor-Engine.dll!UGameInstance::StartPlayInEditorGameInstance() [D:\build\++UE5\Sync\Engine\Source\Runtime\Engine\Private\GameInstance.cpp:500]
- [Callstack] 0x00007fff10099918 UnrealEditor-UnrealEd.dll!UEditorEngine::CreateInnerProcessPIEGameInstance() [D:\build\++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp:3052]
- [Callstack] 0x00007fff100cece6 UnrealEditor-UnrealEd.dll!UEditorEngine::OnLoginPIEComplete_Deferred() [D:\build\++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp:1568]
- [Callstack] 0x00007fff1009a3c2 UnrealEditor-UnrealEd.dll!UEditorEngine::CreateNewPlayInEditorInstance() [D:\build\++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp:1830]
- [Callstack] 0x00007fff100ed4aa UnrealEditor-UnrealEd.dll!UEditorEngine::StartPlayInEditorSession() [D:\build\++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp:2798]
- [Callstack] 0x00007fff100f0cb7 UnrealEditor-UnrealEd.dll!UEditorEngine::StartQueuedPlaySessionRequestImpl() [D:\build\++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp:1148]
- [Callstack] 0x00007fff100f05a1 UnrealEditor-UnrealEd.dll!UEditorEngine::StartQueuedPlaySessionRequest() [D:\build\++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp:1051]
- [Callstack] 0x00007fff0fa8dcf3 UnrealEditor-UnrealEd.dll!UEditorEngine::Tick() [D:\build\++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\EditorEngine.cpp:1665]
- [Callstack] 0x00007fff1041aac6 UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Tick() [D:\build\++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\UnrealEdEngine.cpp:474]
- [Callstack] 0x00007ff7e6d482f6 UnrealEditor.exe!FEngineLoop::Tick() [D:\build\++UE5\Sync\Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp:5215]
- [Callstack] 0x00007ff7e6d60d9c UnrealEditor.exe!GuardedMain() [D:\build\++UE5\Sync\Engine\Source\Runtime\Launch\Private\Launch.cpp:183]
- [Callstack] 0x00007ff7e6d60e8a UnrealEditor.exe!GuardedMainWrapper() [D:\build\++UE5\Sync\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:147]
- [Callstack] 0x00007ff7e6d63c4d UnrealEditor.exe!LaunchWindowsStartup() [D:\build\++UE5\Sync\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:283]
- [Callstack] 0x00007ff7e6d75564 UnrealEditor.exe!WinMain() [D:\build\++UE5\Sync\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:330]
- [Callstack] 0x00007ff7e6d78736 UnrealEditor.exe!__scrt_common_main_seh() [d:\a01\_work\6\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288]
- [Callstack] 0x00007fffb7177034 KERNEL32.DLL!UnknownFunction []
- [Callstack] 0x00007fffb76c2651 ntdll.dll!UnknownFunction []

The line in particular from my class that show up in the error is this;

ClockHandle->SubscribeToQuantizationEvent(World, EQuartzCommandQuantization::Bar, MetronomeEvent, ClockHandle);

And this is how my event is defined;

// Header
UFUNCTION()
void OnBar(FName ClockName, EQuartzCommandQuantization QuantizationType, int32 NumBars, int32 Beat, float BeatFraction);

// Code
void ANewConductor::OnBar(FName ClockName, EQuartzCommandQuantization QuantizationType, int32 NumBars, int32 Beat, float BeatFraction)
{
	GEngine->AddOnScreenDebugMessage(1, 0.3f, FColor::Green, FString::Printf(TEXT("Bar Event")));
}

I’ve been stumped by this for quite a while now, have tried so much random stuff I can’t even remember what I’ve tried. Might be worth to mention that I’m still fairly new to C++.

Please, oh almighty @MaxHayes bestow thy infinite wisdom upon me :smile:

What engine version are you on? There is a lot of work I’ve done coming out in 5.1 to make managing Quartz from C++ a bit easier.

Can you tell where you are crashing with a debugger attached? Something inside of the UClockHandle::SetBeatsPerMinute() function is null. With a debugger attached, you could see what it invalid.

Might be worth to mention that I’m still fairly new to C++.

If you aren’t running with a debugger attached, this is the first thing I’d figure out. It will make life sooo much easier :). That and poking around on how to debug Unreal in general (specifically, un-optimizing modules) will give you way more info as you’re working.

That said, if I were to hip-fire a guess, I’d say make sure that these functions aren’t just returning null:

UQuartzSubsystem* QuartzSubsystem = AudioComponent->GetQuartzSubsystem();
const UWorld* World = AudioComponent->QuartzGetWorld();

This is one of the things that has been improved in 5.1, but those things are lazy-initialized all over Audio Component with code like this:

	//Initialize the tickable object portion of the Audio Component, if it hasn't been initialized already
	if (!FQuartzTickableObject::IsInitialized())
	{
		Init(GetWorld());
	}
	check(FQuartzTickableObject::IsInitialized());

So I’d guess your problem is those things haven’t been initialized on the AudioComponent by the time you’re calling those functions.

It would be safer to obtain that information yourself.

AActor inherits from UObject (I’m assuming that your ANewConductor class inherits from AActor). So you can get the world from your own actor there instead of the AudioComponent, replacing it with :

const UWorld* World = GetWorld();

Then right below that, you can use the UWorld to get the Quartz Subsystem directly (using a static method on that class):

UQuartzSubsystem::Get(World);

This code may or may not work depending on which engine version you’re on.

Good luck!

1 Like

Oh goodness, it was staring me in the face all this time. I’m using Rider and working with a debugger attached is definitely something I need to dive into to make my life easier. :joy:

As I was trying a bunch of random stuff, me using QuartzGetWorld() gave me a different error message, so I guess I immediately assumed it might have been the appropriate way to do it. This whole world context stuff is something I need to look up to get a better grasp on how it works. So far GetWorld() has been enough in pretty much every use-case. Seems like that is still true…

However, looking at it a bit deeper - it seems like this method returns a nullptr.

// AudioMixerClockHandle.h
UWorld* WorldPtr{ nullptr };
UWorld* QuartzGetWorld() const { return GCObjectMembers.WorldPtr; }

But when using the normal GetWorld() I also got an error, something about Tuples. I can try and reproduce it if it’s of interest.

So I think ultimately what fixed it, was this line of code;

UQuartzSubsystem* QuartzSubsystem = UQuartzSubsystem::Get(World);

Once I popped that in, it no longer crashes and it starts playing the music. Thank you! :pray:

I’m hoping to figure out how to make use of Quartz to get audio synced up, would love to create something similar to BeatStar or Clone Hero and ultimately release it as a open source project that everyone can make use of and contribute to. Ambitious goal, I know :smile:

Excited to see what’s new in 5.1, oh and I’m on 5.0.3 - the latest available via the Epic Games Launcher.

Edit:

It seems like using Quartz breaks the default Tick event, is this a known bug or intended behaviour?

I thought I messed something up so I tried to make a new fairly barebones class, and the moment I start using Quartz - the Tick event no longer fires.

Are we supposed to subscribe to a quantization event using EQuartzCommandQuantization::Tick instead?

1 Like

Glad to hear it.

I don’t know what this means. What is “the tick event” your context?

Regarding using a debugger: I would call it a hard requirement in general :). Its an immediate return on investment (as in, it is worth it to take the time and learn to use the debugger during the next bug your are investigating).

Sorry if I was a bit vague, I found the issue. For whatever reason I had removed Super::BeginPlay(); from the BeginPlay() method, this also needs to be at the top of the method - otherwise the AConductor::Tick method stops executing.

So was yet another case of me being unfamiliar with the C++ side of Unreal. :wink:

The very basic functionality is working for the game now, got a lot of research and testing to do to get the syncing to be a bit better. But it’s good enough for a very first iteration, I think. Thanks for your help! :+1:

1 Like

This

UQuartzSubsystem* QuartzSubsystem = UQuartzSubsystem::Get(World);

Has saved me !!!

2 Likes