Saturday, August 27, 2016

Music Management in Epitasis

 While working in Unreal a need arised to have a music manager system for my project, Epitasis.

Coming from the Halo:CE engine originally where music management was quite simple (just controlled via commands in a script), I wanted to create something similar using blueprints and easy macros.

Trying to play songs simply in a sequence using the regular audio mixer did not bode well, as small delays started popping up during the transitions. Additionally, that audio mixer did not easily allow the user to access and play different sounds on command. So the simplest solution was to create a new blueprint class for music tracks.

This included:
  • Easy to use commands to call a child of the base music blueprint and start playing it
  • Correctly play tracks without delays
  • Use of intros, transitional intros, loops, alternate loops, transitional outro, and outro.
  • Functions to start playing alternate loop, stop music, and stop music using a fade out with timed duration.
  • Macro library that allowed the commands to be called anywhere (more on this later).
 So the base music class simply contains things some simply variables the user can modify, which are the different audio cues for the tracks.
  1. Cue_MusicIn
  2. Cue_MusicTransLoop
  3. Cue_MusicLoop
  4. Cue_MusicAltLoop
  5. Cue_MusicTransOut
  6. Cue_MusicOut
The only real ones you need to include in a child of this blueprint would be an intro and loop. Anything less could simply just use a Play Sound 2D node instead.

Lets look at some code:

This is the main event graph in the BP_BaseMusic class.




Some notes. I'm using Rama's Victory plugin to allow me to fade out my custom music sound class. This made it really simply for the fade out function, which is really the only reason its used. If you don't want a fade out function then you don't need to include it, but I found this to be the quickest / easiest way to implement a fade function without using an audio component and just simple Play Sound 2D nodes. This method also assumes all you cues are assigned to that sound class. The only limitation this has is that when you fade out this track you fade out ALL music, but in my case thats not a big deal.

The timeline for the fadeout is over one second long. We use the set play rate function and a input to control the speed of the fade out.

Otherwise, the code is quite straightforward. We get the audio cue's duration and delay by that and simply play the next cue (or loop). We also check if the transitional cues are valid, considering these are not always necessary, but more so there to help if needed for the composer.

We also had three functions: StopMusic, PlayMusic, and PlayAltLoop.

- StopMusic simply checks to see if the loop has even fired yet (in case it was called to early), and if it has, it then clears the loop music function and sets the "MusicFinished" boolean to true.

- PlayMusic simply calls the custom event BeginPlayMusic.

- PlayAltLoop simply sets the "PlayAltLoop" boolean variable to true or false based on a bool input when called. This is so it can be called again to be set to false.

I then created a simple macro library that has simple functions and inputs that can be called from any actor or level blueprint (theres another macro library specifically to be called from UMG if needed).


Start music - only input it requires is the child blueprint CLASS that contains all the sound cues we want to play. It returns the created instance.


Stop Music and Play Alternate - simply just get the instance and call the respective functions. Honestly these aren't necessary since you have to get the instance anyway but added for consistency.


Fade out macro - we get an input for the play rate and then find all currently playing music actors and fade them out.

From here, we can just easily call the start music macro, plug in a child of the base music class and let it play!



Lastly, I've been working with Andrzej Ojczenasz for the music of Epitasis. He's been creating some really great stuff, and I can't wait to share it with you all quite soon.

3 comments:

  1. Hey this is super cool stuff - I just found your blog from the Unreal Engine forums and I am very impressed with your work and documentation.

    Have you been running into issues of non-seamless playback when transitioning from soundcue to soundcue? I've tried your method of hooking up a delay node to the soundcue's duration before but because blueprints rely on gametime rather then sample time I often get a slight hitch before the next loop plays, especially if I have a low or unsteady framerate. Would love to hear if you have a solution for that.

    ReplyDelete
    Replies
    1. Thanks man!

      I've run into that issue occasionally, but its usually only on in-editor playtesting, and never in a compiled stand-alone build. Which are you using?

      That being said, I've only tested it on a handful of computers at this point (all with reasonable framerates, although some with crap settings), and haven't noticed it.

      Delete
    2. Interesting - I've found the problem is more noticeable at low or unsteady framerates (t.unsteadyframerate 1), especially in editor, but I've heard the issue in compiled PC and PS4 builds. Fortunately I'm pretty much only using track switching for a few intros, so I ended up just making sure the intro is the same length as the loop by padding it with silence, starting both tracks at a specified time in blueprints, then crossfading them on the delay rather than hard switching. It's pretty hacky but it gets the job done.

      Delete