Sound Analyzer and Visualizer

What is this?

I made a simple system to help with sound analysis (using the PlaybackLoudness property) and sound visualization. You can find the ROBLOX Model here and the Github Repository here. This model contains the following two modulescripts:

  • Sound Analyzer
  • Sound Visualizer

Sound Analyzer

This modulescript will analyze a sound to extract useful information about its PlaybackLoudness. It can sample the sound at different frequencies and for different time periods using the PlaybackSpeed property of the sound. For this purpose, it exposes three important functions:

  • :processSoundByResolution(sound, sampleResolution) samples the sound, producing a number of points specified by the resolution

  • :processSoundByTime(sound, sampleTime) samples the complete sound in the given time

  • :processSoundByPlaybackSpeed(sound, samplePlaybackSpeed) samples the sound using the sound’s PlaybackSpeed property, this will take more time for longer sounds

Note: using extreme values for either of the three ways of processing sounds might not be accurate because PlaybackSpeed property can not go beyond the range 0.01 - 20

All three of these functions return a soundData table that contains the following information about the sound:

  • playbackLoudnessArray: This is a collection of the sampled PlaybackLoudness values

  • timePositionArray: This is a collection of TimePosition values of the sound at which the sound was sampled

  • Highest, Lowest, Mean, Median, Range: These are simple statistic measures of the sampled PlaybackLoudness values


Sound Visualizer

The soundData table contains all the information about the sound we need to create a basic visualizer. However, to assist you in that pursuit, I have also created the sound visualizer modulescript. This accepts a soundData table and exposes two functions:

  • :getPlaybackLoudness(timePosition) returns the linearly interpolated approximation of the PlaybackLoudness at the given timePosition. It will return nil if it is unable to do calculate, so make sure you account for that.

  • :getNormalizedPlaybackLoudness(timePosition) works almost exactly like the first function but returns the normalized PlaybackLoudness using the range value contained within soundData.


How to use it?

To visualize a sound, first, you have to analyze it to collect some statistical measures about the PlaybackLoudness property. To do this, you sample the sound and collect the PlaybackLoudness at certain TimePositions. Depending on how much time you spend (the sound has to be playing for PlaybackLoudness property to work), you can collect a variable number of sample points. Generally, the more sample points, the more accurate your visualization is going to be, but because of the lack of depth of information that PlaybackLoudness captures about the sound and because of human perception, the more points you collect the less “benefit” you get from them. Thus, as long as the visualization looks good enough, you don’t have to push for the absolute ton of sample points.

To collect the statistical PlaybackLoudness information of the sound, you can make use of the Sound Analyzer modulescript by doing something like this:

local soundAnalyzerModule = require(...)
local soundAnalyzerInstance = soundAnalyzerModule.new()
local soundData = soundAnalyzerModule:processSoundByResolution(script.Sound, 25)

You can use any of the three functions exposed by the modulescript. After you have your soundData, you can make use of the sound visualizer modulescript to start creating some awesome visualizations like this:

local soundVisualizerModule = require(...)
local soundVisualizerInstance = soundVisualizerModule.new(soundData)

local timeStamp = tick()
script.Sound:Play()
while wait() do
    local timePosition = tick() - timeStamp
    print(soundVisualizerInstance:getNormalizedPlaybackLoudness(timePosition))
end

Note: This snippet is just for demonstration purpose, don’t use it in real code as it contains very bad practices. I’m assuming you will have a more engaging audio visualization than using print.

17 Likes

Just curious, but why would you need to return range if highest and lowest are returned as well?

It just felt convenient for the time so I kept it.

Is there a video tutorial to using this or maybe some examples?