This time, my goal was to make a dynamic “Music System”, using the new Quartz subsystem that comes with Unreal 4.26 and up. Learning to use the Quartz subsystem is crucial to make “sample perfect” transitions between music segments; something almost imposible to do in the previous Audio Engine. Aaron McLeran (Lead Audio Programmer at Epic) and his team made this posible, by creating a system that schedule the audio rendering.
Before I start showcasing the system, I want to thanks Dan Reynolds (Technical Sound Designer at Epic) and Valkyrie Sound . Without their videos explaining Quartz, I would had a much worse time figuring up how to use it. Also, I want to add that the amazing music that you will hear, was composed by Ariel Contreras-Esquivel for Blue Fire.
With the empty Unreal Project, I started to think about the characteristics of the system I wanted to make. I knew I wanted to be dynamic, extensible, scalable and that can support any number of music segments, for horizontal remixing purposes. By the way, I’m borrowing the “music segments” concept from Wwise, wich I’ve used extensively in Blue Fire. I knew it wasn’t going to be a good idea to try to made the whole music system in one try, so I made a little “roadmap” with baby steps that bring me closer to my goal.
So, I first select the music that I wanted to use, and divide them in segments. 4 Music Segments, some with “Pre Roll” or “Pre Entry” as in Wwise, and some without. In this case, all the segments are 8 Bars long, but it wouldn’t matter if one or more of them are longer or shorter.
I will not go step by step on the roadmap. It’ll be boring and messy. After failing, breaking things, experimenting, and rewatching over and over Vakyrie Sounds and Dan Reynolds video’s, I’ve came up with a system that use Primary Data Assets and Data Assets as a way to handle the properties of the music. I think that using Primary Data Assets is a great way to make clean, modular and expandable music system. All the final assets needed are this ones.
The “PDA_Music” is the Primary Data Asset, and is the responsible of handling the different variables that will affect the music system.
I made use of Structs to handle each Music Segment data. I kept it simple. Just bars lenght, pre roll lenght in bars, and a Sound Base
I made a new Struct, that has an Array of the “StructSegment” struct. This made posible to add as many music segments as I wanted. This is the structure that can be found inside the Primary Data Asset “PDA_Music”
Then I had to create another Struct, with the same variables than “Struct Segment”. In the Music Master BP, I transfer the data from Struct Segment to this new Struct. This was the only way I’ve found to then use and manage the properties inside the blueprint. I’m sure this it’s not clean way to solve the issue, but it worked, so just be aware of that if you use this blog as a guide.
The Data Assets
Now it’s time to set a Data Asset. When you create a new Data Asset, it will ask you to reference a Primary Data Asset. In this case, the “PDA_Music”. The Data Asset created will “inherit” the variables created in the PDA. Each Data Asset created, will work as a container of the properties data for every single piece of music. Here’s an example.
The Quartz Subsystem
With the structs, the PDA and the Data Assets created, it’s time to make the Quartz subsystem Get the data from the DA’s, that the designers will place inside a public variable in the BP_Music_Master_Controller, once you drop the Actor inside your level.
In Begin Play I’m creating the Audio Components for each music segment founded in the struct, then duplicate that same logic but for make the segments able to loop. then set the structs, and creating the UI, basically. Please note that you can double click the images to see them larger.
At the end, I’m calling a custom event that will create a Quartz Clock, and then play the music. But first, let’s see what I’m doing with the “Set Components” function.
In simple terms, I’m creating Audio Components “on the fly”, and taking the Sound Base from each Struct Segment, and setting that Sound Base to the created Component. I’m looping this Flip Flop, the same amount as the lenght of the Music Segments founded in the StructofStructSegments. I’m adding this Audio Component’s to an Array, for further handling. I’m repeating this same logic again, but adding the created Audio Components to a different Array.
Here I’m reading the Data Asset struct and transferring the data to a local Struct.
Then I create the Widget that allow me to test easily the music transitions, and then the button functionality. You will understand this logic later when I show how the UI works.
Time to create the “clock” and play Quartz. I’m not going to explain how Quartz work, since Dan Reynolds and Valkyrie Sound have made a great job at doing that. I’m geting the BPM from the PDA, and setting it in the clock handle. If the UI was not created, call Play Quartz to start the music, and subscribing to Quantization Event.
On the Quantization Event delegate is where I’m calling a segment to loop, when the lenght of the Bar is reached. If ForcedSwitchSegment is true, I’m fading out the currently playing Audio Component, and start another one.
The whole logic
Here’s the whole Event Graph and all the variables created.
For the UI, I’ve created a single button “Entry Widget”, and a Blueprint Widget “Music Widget”.
Inside the “Button Container” vertical box in the hirearchy, is where the desired amount of buttons will be created; one for each music segment founded.
And finally, I’m receiving the calls for the Custom Events created to Pause, Resume and Stop the music.
The Final Result
The final result, is a Music System that reads the data from Data Assets, that can loop music segments, switch to any other number of music segments, at the end or at next bar, fading out the previous one.
A few disclaimers here! First I want to emphasize that this “system” it’s made for experimentation and research only. This hasn’t been tested in any real game application. I’m sure this same idea can be made a lot better, in a much safer and clean way. Do not take this blog as a guide. This is only a place to portray my learnings. I hope that you find this blog interesting, and be able to improve on this idea if you like. If you have any comments, please reach out through my Twitter, I would love to see if you have any suggestions to further improve this system!
Here’s a video showcase!
The next time, I’m planning to add a layering and stinger system too. So… please stay tuned! Thanks for reading! Till the next one!