Code Examples
Copy-paste ready examples for common music engine scenarios.
Stinger Handle Control
Example 1: Play and Control a Stinger
Scenario: Play a stinger when the player is hit, allow pausing/resuming, and track when it finishes.
Blueprint:
Event Character Takes Damage
↓
Play Stinger With Handle
├─ Stinger Sound: HitSound_Stinger
├─ Duck Music: true
└─ (store result) → HitStingerHandle
↓
Wait 1 second
↓
Is Stinger Playing (HitStingerHandle) → bIsPlaying
├─ If True:
│ ↓
│ Pause Stinger (HitStingerHandle)
│ ↓
│ Wait 2 seconds
│ ↓
│ Resume Stinger (HitStingerHandle)
│
└─ If False: (already finished)
↓
Log "Stinger already finished"
C++:
void AMyCharacter::TakeDamage(float Damage)
{
// Play stinger and store handle
UERP_MusicSubsystem* MusicSys = GetGameInstance()->GetSubsystem<UERP_MusicSubsystem>();
if (MusicSys)
{
HitStingerHandle = MusicSys->PlayStingerWithHandle(
HitStingerSound,
true, // bDuckMusic
0.3f, // DuckVolume
0.2f, // DuckFadeTime
0.5f // RestoreFadeTime
);
// Later: check if still playing
if (MusicSys->IsStingerPlaying(HitStingerHandle))
{
MusicSys->PauseStinger(HitStingerHandle);
// ... and resume later
}
}
}
Example 2: Looping Stinger with Loop Events
Scenario: Play a loop stinger 3 times, reacting each time a loop completes.
Blueprint:
Event BeginPlay
↓
Create FERP_EnhancedStingerParams struct
├─ Stinger Sound: LoopSound
├─ bLoop: true
├─ Loop Count: 3
├─ Loop Delay: 0.2
└─ (set other params)
↓
Play Stinger Enhanced
├─ Params: [from above]
└─ (store result) → LoopStingerHandle
↓
Bind Event to On Stinger Loop Completed
↓
[Event] On Stinger Loop Completed
├─ Handle == LoopStingerHandle ?
│ ├─ Yes: Show HUD notification "Loop X complete"
│ │ (Loop Index = 0, 1, 2, then OnStingerCompleted)
│ └─ No: Ignore
C++:
void AMyCharacter::PlayLoopingStinger()
{
UERP_MusicSubsystem* MusicSys = GetGameInstance()->GetSubsystem<UERP_MusicSubsystem>();
if (!MusicSys) return;
// Create params
FERP_EnhancedStingerParams Params;
Params.StingerSound = TSoftObjectPtr<USoundBase>(FSoftObjectPath(TEXT("/Game/Audio/Stingers/Loop_Stinger")));
Params.bDuckMusic = true;
Params.DuckVolume = 0.3f;
Params.bLoop = true;
Params.LoopCount = 3;
Params.LoopDelay = 0.2f;
// Play and store handle
LoopStingerHandle = MusicSys->PlayStingerEnhanced(Params);
// Bind to loop completed event
MusicSys->OnStingerLoopCompleted.AddDynamic(this, &AMyCharacter::OnLoopStingerCompleted);
}
void AMyCharacter::OnLoopStingerCompleted(FERP_StingerHandle Handle, int32 LoopIndex)
{
if (Handle == LoopStingerHandle)
{
UE_LOG(LogTemp, Warning, TEXT("Loop stinger iteration %d completed"), LoopIndex);
// Show HUD, play effect, etc.
}
}
Example 3: Dynamic Volume Control During Playback
Scenario: Play a stinger and gradually decrease its volume while keeping music at normal level.
Blueprint:
Event BeginPlay
↓
Play Stinger With Handle
├─ Stinger Sound: FadeStinger
├─ Duck Music: false ← Don't duck music
└─ (store) → MyStinger
↓
Create Timeline (0 to 2 seconds)
│
└─ [At each frame]:
├─ Map time (0.0 → 1.0) to volume (1.0 → 0.0)
├─ Set Stinger Volume
│ ├─ Handle: MyStinger
│ ├─ Volume: [mapped value]
│ └─ Fade Time: 0.0 (no fade, immediate)
└─ [Timeline finished]:
└─ Stop Stinger (MyStinger)
C++:
void AMyCharacter::PlayFadingStinger()
{
UERP_MusicSubsystem* MusicSys = GetGameInstance()->GetSubsystem<UERP_MusicSubsystem>();
if (!MusicSys) return;
FadingStingerHandle = MusicSys->PlayStingerWithHandle(
FadeStingerSound,
false, // Don't duck music
0.0f, 0.0f, 0.0f
);
// Start fade-out using Quartz clock
if (MusicSys->IsClockRunning())
{
// Quantized fade
GetWorld()->GetTimerManager().SetTimer(
FadingStingerTimerHandle,
[this, MusicSys]()
{
// Gradually reduce volume
static float Volume = 1.0f;
Volume -= 0.1f;
if (Volume <= 0.0f)
{
MusicSys->StopStinger(FadingStingerHandle);
Volume = 1.0f;
GetWorld()->GetTimerManager().ClearTimer(FadingStingerTimerHandle);
}
else
{
MusicSys->SetStingerVolume(FadingStingerHandle, Volume, 0.1f);
}
},
0.1f, // Every 0.1 seconds
true // Loop
);
}
}
Example 4: 3D Positioned Stinger (Synchronized)
Scenario: Play explosion stingers at multiple locations, synchronized across networked clients.
Blueprint:
Event Server_ExplosionAtLocation (Server RPC)
├─ ExplosionLocation (vector)
└─ ExplosionID (int)
↓
Create FERP_EnhancedStingerParams
├─ Stinger Sound: Explosion_Musical
├─ Location: ExplosionLocation
├─ Volume Multiplier: 1.0
├─ Pitch Multiplier: 1.0 + (Random -0.1 to 0.1) ← Variation
├─ Attenuation Start Distance: 2000.0
├─ Sync Mode: Synchronized3D
└─ (other params)
↓
Get or Create Sync Component
↓
Sync Play Stinger (params)
└─ (returns handle)
↓
Store handle indexed by ExplosionID
C++:
void AExplosionManager::Server_DetonateExplosion_Implementation(FVector Location, int32 ExplosionID)
{
UERP_MusicSubsystem* MusicSys = GetGameInstance()->GetSubsystem<UERP_MusicSubsystem>();
UERP_MusicSyncComponent* SyncComp = MusicSys->GetOrCreateSyncComponent();
if (!SyncComp) return;
// Create params for positioned stinger
FERP_EnhancedStingerParams Params;
Params.StingerSound = TSoftObjectPtr<USoundBase>(FSoftObjectPath(TEXT("/Game/Audio/Stingers/Explosion")));
Params.Location = Location;
Params.VolumeMultiplier = 1.0f;
Params.PitchMultiplier = 1.0f + FMath::RandRange(-0.1f, 0.1f);
Params.AttenuationStartDistance = 2000.0f;
Params.SyncMode = ERP_EStingerSyncMode::Synchronized3D; // All clients hear it
Params.bDuckMusic = true;
// Play synchronized
FERP_StingerHandle Handle = SyncComp->SyncPlayStinger(Params);
// Track by ID
ActiveExplosionStingers.Add(ExplosionID, Handle);
// Later: when explosion ends
// SyncComp->SyncStopStinger(Handle);
}
Example 5: Stinger Queue (Play Multiple Sequentially)
Scenario: Play multiple stingers in sequence, one after another.
Blueprint:
Event BeginPlay
├─ Create array: StingerQueue = [Stinger1, Stinger2, Stinger3]
├─ CurrentQueueIndex = 0
└─ Play next stinger from queue
↓
[Bind] On Stinger Completed
↓
[Event] On Stinger Completed
├─ Increment CurrentQueueIndex
├─ If more stingers: Play next stinger
└─ If done: Stop queue
C++:
void AMusicalSequencer::PlayStingerQueue(const TArray<USoundBase*>& Stingers)
{
StingerQueue = Stingers;
CurrentStingerIndex = 0;
UERP_MusicSubsystem* MusicSys = GetGameInstance()->GetSubsystem<UERP_MusicSubsystem>();
if (MusicSys)
{
MusicSys->OnStingerCompleted.AddDynamic(this, &AMusicalSequencer::OnQueueStingerCompleted);
PlayNextStinger();
}
}
void AMusicalSequencer::PlayNextStinger()
{
if (CurrentStingerIndex >= StingerQueue.Num())
{
UE_LOG(LogTemp, Log, TEXT("Stinger queue completed"));
return;
}
UERP_MusicSubsystem* MusicSys = GetGameInstance()->GetSubsystem<UERP_MusicSubsystem>();
if (MusicSys)
{
CurrentStingerHandle = MusicSys->PlayStingerWithHandle(
StingerQueue[CurrentStingerIndex],
true, // Duck
0.3f, 0.2f, 0.5f
);
}
}
void AMusicalSequencer::OnQueueStingerCompleted(FERP_StingerHandle Handle)
{
if (Handle == CurrentStingerHandle)
{
CurrentStingerIndex++;
PlayNextStinger();
}
}
Layer Management
Example: Boss Phase Music
Scenario: Boss has 3 phases with different music. Transition between phases at bar boundaries.
Blueprint:
Event Boss Phase 1 Starts
├─ Initialize Quartz Clock (120 BPM, 4/4)
├─ Start Music Clock
└─ Push Music Layer
├─ Layer: BossPhase1_Music
└─ Quantization: Bar
↓
[At 50% Health] Event Boss Phase 2
├─ Pop Music Layer
│ ├─ Layer Name: BossPhase1_Music
│ └─ Quantization: Bar ← Wait for bar boundary
└─ Push Music Layer
├─ Layer: BossPhase2_Music
└─ Quantization: Bar
↓
[At 25% Health] Event Boss Phase 3
├─ (Same transition pattern)
C++:
void ABossCharacter::UpdateBossPhase()
{
float HealthPercent = CurrentHealth / MaxHealth;
UERP_MusicSubsystem* MusicSys = GetGameInstance()->GetSubsystem<UERP_MusicSubsystem>();
if (!MusicSys) return;
if (HealthPercent <= 0.50f && CurrentPhase == 1)
{
CurrentPhase = 2;
MusicSys->PopMusicLayer(FName(TEXT("BossPhase1")), ERP_EQuantization::Bar);
FERP_MusicLayer Phase2Layer;
Phase2Layer.LayerName = FName(TEXT("BossPhase2"));
Phase2Layer.Music = TSoftObjectPtr<USoundBase>(FSoftObjectPath(TEXT("/Game/Audio/Music/BossPhase2")));
Phase2Layer.Priority = 50;
Phase2Layer.LayerMode = ERP_EMusicLayerMode::Replace;
MusicSys->PushMusicLayer(Phase2Layer, ERP_EQuantization::Bar);
}
}
Audio Ducking
Example: Dialogue Ducking
Scenario: Play dialogue and automatically duck music during playback.
Blueprint:
Event Play Dialogue
↓
Spawn Sound 2D
├─ Sound: Character_Dialogue
├─ Volume: 1.0
└─ (returns AudioComponent)
↓ [Result]
Enable Audio Ducking
├─ Audio Component: [from above]
├─ Duck Volume: 0.4
└─ Fade Time: 0.3
↓
(Music auto-restores when dialogue finishes)
C++:
void ADialogueManager::PlayCharacterLine(const FString& DialogueText, USoundBase* DialogueAudio)
{
// Spawn audio
UAudioComponent* DialogueComponent = UGameplayStatics::SpawnSound2D(
GetWorld(),
DialogueAudio,
1.0f, // Volume
1.0f, // Pitch
0.0f, // Start time
nullptr, // Owner
nullptr, // Instigator
true // bFollow
);
// Auto-duck music
UERP_MusicSubsystem* MusicSys = GetGameInstance()->GetSubsystem<UERP_MusicSubsystem>();
if (MusicSys && DialogueComponent)
{
MusicSys->EnableAudioDucking(DialogueComponent, 0.4f, 0.3f);
}
}
Multiplayer Network Sync
Example: Synchronized Boss Music Across Clients
Scenario: Multiple players fight a boss. Boss music syncs across all clients, and late-joiners sync to the current playback point.
Blueprint (Server Event):
Event Boss Combat Started (Authority check)
↓
If Has Authority:
├─ Get or Create Sync Component
└─ Create Music Layer
├─ Name: "BossCombat"
├─ Music: BossCombat_Music
└─ Priority: 100
↓
Sync Push Music Layer
├─ Layer: [from above]
├─ Quantization: Bar
└─ (syncs to all clients)
↓
(Later) Boss Defeated (Multicast)
├─ Multicast: Remove Boss Music
└─ Sync Pop Music Layer
├─ Layer Name: "BossCombat"
└─ Quantization: Bar
C++:
void ABossAIController::StartBossCombat()
{
if (!HasAuthority()) return;
UERP_MusicSubsystem* MusicSys = GetGameInstance()->GetSubsystem<UERP_MusicSubsystem>();
UERP_MusicSyncComponent* SyncComp = MusicSys->GetOrCreateSyncComponent();
if (!SyncComp) return;
// Create synced layer
FERP_MusicLayer BossLayer;
BossLayer.LayerName = FName(TEXT("BossCombat"));
BossLayer.Music = BossCombatMusic;
BossLayer.Priority = 100;
BossLayer.LayerMode = ERP_EMusicLayerMode::Replace;
BossLayer.FadeInTime = 1.0f;
// Sync to all clients at bar boundary
SyncComp->SyncPushMusicLayer(BossLayer, ERP_EQuantization::Bar);
UE_LOG(LogTemp, Log, TEXT("Boss combat music synced to all clients"));
}
Advanced: Quartz Integration
Example: Beat-Synced Stinger
Scenario: Play a stinger synchronized to the next beat of the music.
Blueprint:
Event OnPlayerPickup
↓
Get Music Subsystem
↓
[Check] Is Clock Running?
├─ Yes:
│ ├─ Play Stinger Quantized
│ │ ├─ Stinger Sound: Pickup_Chime
│ │ ├─ Quantization: Beat ← Wait for next beat
│ │ └─ Duck Music: false
│ └─ (music and stinger sync perfectly)
│
└─ No:
└─ Play Stinger (immediate, no sync)
C++:
void APickupActor::OnPickupByCharacter(ACharacter* Character)
{
UERP_MusicSubsystem* MusicSys = GetGameInstance()->GetSubsystem<UERP_MusicSubsystem>();
if (!MusicSys) return;
if (MusicSys->IsClockRunning())
{
// Play synchronized to next beat
MusicSys->PlayStingerQuantized(
PickupSound,
ERP_EQuantization::Beat,
false // Don't duck
);
}
else
{
// Fall back to immediate playback
MusicSys->PlayStingerWithHandle(PickupSound, false, 0.0f, 0.0f, 0.0f);
}
}