Before you read on, I'd like to forewarn that this project involves flashing, bright lights, repeating patterns, and loud noises, which could be an issue for people who have photo and/or audio sensitivity. Please exert discretion when going forward!
A tool I developed in Unity that can translate audio information into a visually representing spectrum graph, in a neonwave style. It accepts input from any audio source connected to the computer, including microphones as well as instruments with an input jack.
It can also detect the intensity of the input being received in order to trigger events to emphasize moments of higher intensity during the musical performance.
Engine: Unity 2021 LTS.
Production Period: Created for a class during my time in RMIT, made in around a month.
Notable Features:
Ability to recognize any input audio device connected to the computer, such as audio interfaces and microphones.
Sort frequency ranges into groups, which get represented by the bars that grow and shrink.
Trigger events (such as particles and post process effects) to take place when a certain amount of input is received.
Below is some footage of the tool being used by myself (apologies in advance to your ears for my very mediocre musical performance lol):
The goal I had in mind when programming this tool was actually to visualize audio from a microphone, not an instrument. Originally, I started with a snippet of code capable of translating the internal audio output of the computer itself into audio visualization data. I decided I wanted to see if I could get the audio of a microphone to come through to Unity to create a sort of Karaoke machine.
I eventually figured out how to expose the microphone's audio to Unity, however, just when I wanted silver, I struck gold instead! Not only did I expose my microphone's audio to Unity, I found that my webcam could be used as an audio input, as it also has a microphone on it, and Unity recognized it.
This got me thinking that the way Unity connects to microphones is flexible enough to allow me to try connecting some other audio source instead. I bought myself an audio interface (pictured below), connected that into my computer, and plugged my guitar in it. Just like that, the interface got recognized by Unity, and the audio from the guitar came through!
This tool works by combining 3 different scripts. I will break each of them down in detail (lots of text incoming!):
Inside of my project, I actually called this "Voice Detection", because as I said before, I originally wanted to use this script to just connect Unity to my microphone, not instruments, and I ended up leaving it with that name.
Each instrument you would like to visualize requires this script to be attached, as well as an Audio Source to go along with it.
The simplest yet most crucial script of all! As seen below, it simply has two fields. The first one, "Microphone", is a string that correlates to which specific device from your computer you wish for this object to draw audio input from. The array of strings "Options" contains every audio device that your computer currently has connected and available to draw information from.
As seen below, using the context menu function within my script, I exposed a function called "Detect Available Inputs", which will automatically check the computer's audio devices and populate the array with every input available:
Once the array is populated, you can then copy whichever audio device you wish to use into the "Microphone" field. You need to ensure this object also has an Audio Souce component to it, because the final step is entering play mode, which will make the script automatically assign itself as the "AudioClip" of the Audio Source of this object, as pictured below:
And just like that, we already have successfully gotten a microphone or instrument's audio coming out of Unity's Audio Source component!
This is where the main functions are executed to actually translate the audio being received into visual information. In a nutshell, it enables the user to choose the frequency ranges they desire to display, and how this frequency range will look in terms of colour and size, and what events it triggers.
For ease of understanding and succinctness, I shall clarify one term which I will use repeatedly about this script: "intensity of the instrument". What I mean by this is "how much stimulus the frequency range of this particular instrument is receiving on average". If said average is high, then it means the user is playing the desired range of this instrument quite powerfully, whereas if the average is low, it means they are either playing it softly or not at all.
Each instrument you would like to visualize requires this script to be attached, in the same way as the Device Identifier script.
It has a lot of exposed fields, and I will break down what each of the main ones do:
Spectrum - It's a massive array which includes the entire audio spectrum being captured, from frequencies 1 to 1024, so you can see the value in each individual frequency. Purely for debug but very helpful to ensure you know for sure the frequencies you are interested in actually have values being received.
Skybox Shader Properties - The skybox has a fragment shader attached to it, which creates the circumcentric circle pattern (which is actually a simple sine wave). The movement and visibility of this pattern is dictated by the instrument's intensity. If you are playing very quietly, the circles will be less visible and move slowly, whereas if you play notes with more strenght, it will move fast and shine bright. The variables here control the impact your instrument has on the speed and intensity of in the skybox.
Instrument Events - Whenever you reach a certain amount of intensity in this particular instrument, you can determine something to happen in response. I leveraged Unity's event systems to be able to trigger pretty much any public function of any component in any object, which is why this is formatted as an array of arrays, where each individual item is a Unity event. In the above example, if you reach the intensity required, you will cause the 2 specified particle systems to trigger.
Event Freq Trigger - This is the number that determines how much intensity is needed to trigger the events into action. A lower number means less intensity is required, and the opposite is also true.
Randomly Pick One Event - If false, it will trigger every event in the array at once when the required intensity is achieved. If true, it will choose only one at random and trigger only that.
Instrument Band Obj Properties - A "band" is the name given to an individual bar that shrinks and grows in accordance to a frequency range. Here you can define if they spawn automatically when you enter play mode, how large they are (span), and you can define the prefab that will be spawned per band.
Band Parameters - This is where the user can input how to calculate the bands of each instrument. Underneath the surface, this is an array of structs. Each item of the array represents a band, and each said item you can specify how each band will be calculated (the tool automatically spawns a band depending on how many items are inside of this array). All bands of each instrument contribute to an overall intensity value that the whole instrument emits, which is calculated by averaging all bands intensities together. Below is a breakdown of the main attributes you can define:
Band Range - An array of vector 2s. Here you can specify in between which two specific frequencies you want to capture information from. Lower numbers capture deeper notes, higher numbers capture higher notes. Here you can add more than just a single range, and if you do that, it adds all ranges together and averages their results.
Frequency Gate - A float that allows you to filter out undesired noises from frequencies you capture. It prevents stimulus coming from your specified Band Range to affect your visualization in case the value is not larger than the Frequency Gate. If at 0, no filtering occurs, whereas higher values make it harder for notes played to register.
Intensity and Height Multiplyer - They are 2 distinct things, not to be confused with each other. The reason I say this is because increasing either of the two will, on surface level, seemingly have the same effect, which is making the band grow taller, but internally they do separate things. The Height Multiplyer simply makes it so if you play a note, the bar can grow to a higher upper limit, making it taller when you play a note. The Intensity Multiplyer on the other hand, increases the intensity of the notes being registered internally, which therefore means this instrument's intensity output will be increased, which if exaggerated, will cause an instrument to overpower every other instrument, since different instruments have different input levels.
Lerp Time - How fast the bars grow and shrink. Higher values make the movement slower, lower makes it more immediate.
Colour Over Frequency and Multiplyer - A gradient where you can specify the colour of the band depending on how intensely the band's frequencies are being received. On the left side of the gradient is the colour when played softly, on the right, when played intensely. The Multiplyer simply makes it so the transition between colours changes faster (higher values) or slower (lower values).
Emission Intensity Curve and Multiplyer - The material of each band is emissive. The intensity of said emission, in the same way as the Colour Over Frequency, changes depending on how intense the input being received is, which can be specified along the curve.
Each instrument being captured must be a child of a parent object, which will have this component attached to it. This component is similar to the Audio Visualizer in that it is capable of translating input being received from instruments into visual effects. The difference in this case is that this component considers input from every instrument simultaneously, as opposed to each individually. The main thing that it is capable of doing I will highlight below:
Parent Events - When receiving the input from all the intruments at the same time, this script is able to average out the summed input to perform a very similar function to the instruments' events system, where if you reach a certain threshold of intensity, it triggers something to happen. The 2 key differences in this case are firstly, the intensity is the sum average of all instruments instead of just one. Secondly, the event here instead of triggering a particle system, it instead triggers a custom event I created which programmatically animates the post process volume (attached to this same object) to increase its chromatic aberration to the maximum, then to go back down.