- Home /
How to generate waveform from AudioClip?
Hi there,
How would I go about generating a static waveform preview like the one seen here? Not moving, not updating as the song plays. Just a preview of the audio.
I would like to make an identical function using 2D Rectangles, NOT a debug line or gui texture.
Not a visualizer, not a debug line. A waveform preview of the entire song as seen in the image above. An example would be awesome if anyone has one to share :)!
I've had tons of trouble finding info on AudioClip.getData and how to use it, very little is out there about waveform preview generation. Tons of info however is available on visualizer generation :( which is not what i want.
Did you try Googling? I found several examples using the NAudio framework. Like http://www.youtube.com/watch?v=ZnFoVuOVrUQ
Yes, but i made the mistake of googling for solutions pertaining specifically to Unity (why my search results produced little help) and forgot there's a whole nother' world of C# out there :)..thanks I see tons of tutorials now
Answer by CleverToucan · Jan 30, 2019 at 01:35 PM
I edited @Breakypower's code in the previous answer to fix a couple bugs and optimize the generation:
public Texture2D PaintWaveformSpectrum(AudioClip audio, float saturation, int width, int height, Color col) {
Texture2D tex = new Texture2D(width, height, TextureFormat.RGBA32, false);
float[] samples = new float[audio.samples];
float[] waveform = new float[width];
audio.GetData(samples, 0);
int packSize = ( audio.samples / width ) + 1;
int s = 0;
for (int i = 0; i < audio.samples; i += packSize) {
waveform[s] = Mathf.Abs(samples[i]);
s++;
}
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
tex.SetPixel(x, y, Color.black);
}
}
for (int x = 0; x < waveform.Length; x++) {
for (int y = 0; y <= waveform[x] * ((float)height * .75f); y++) {
tex.SetPixel(x, ( height / 2 ) + y, col);
tex.SetPixel(x, ( height / 2 ) - y, col);
}
}
tex.Apply();
return tex;
}
It produces very lovely results for my purposes:
hiya, just a heads up, the current code doesn't take into account of multi channel clips.
the following tweaks should fix the problem.
// multiply it by channel to account of clips with multiple channel
float[] samples = new float[audio.samples * audio.channels];
and change the first loop to loop through sample.length ins$$anonymous$$d
for (int i = 0; i < samples.Length; i += packSize)
{
waveform[waveIndex] = $$anonymous$$athf.Abs(samples[i]);
waveIndex++;
}
Thanks for the fix $$anonymous$$iniFish! To save everyone else the headache I developed trying to figure out why your stuff didn't immediately work for me, remember that after applying the above changes, remember to also adjust the packsize variable. So you should also have
int packSize = (samples.Length / width) + 1;
Answer by Breakypower · Apr 06, 2018 at 09:43 PM
I do this, and it's works perfectly :
public static float[] GetWaveform (AudioClip audio, int size, float sat) {
float[] samples = new float[audio.samples];
float[] waveform = new float[size];
audio.GetData(samples, 0);
int packSize = audio.samples / size;
float max = 0f;
int c = 0;
int s = 0;
for (int i = 0; i < 2 * audio.samples; i++) {
waveform [c] += Mathf.Abs (samples [i]);
s++;
if (s > packSize) {
if (max < waveform [c])
max = waveform [c];
c++;
s = 0;
}
}
for (int i = 0; i < size; i++) {
waveform [i] /= (max * sat);
if (waveform [i] > 1f)
waveform [i] = 1f;
}
return waveform;
}
public static Texture2D PaintWaveformSpectrum(float[] waveform, int height, Color c) {
Texture2D tex = new Texture2D (waveform.Length, height, TextureFormat.RGBA32, false);
for (int x = 0; x < waveform.Length; x++) {
for (int y = 0; y <= waveform [x] * (float)height / 2f; y++) {
tex.SetPixel (x, (height / 2) + y, c);
tex.SetPixel (x, (height / 2) - y, c);
}
}
tex.Apply ();
return tex;
}
and use it like this:
Texture2D tex = AudioGetter.PaintWaveformSpectrum (AudioGetter.GetWaveform (clip, 500, 1f), 50, c);
GetComponent<Image> ().overrideSprite = Sprite.Create (tex, new Rect (0f, 0f, tex.width, tex.height), new Vector2 (0.5f, 0.5f));
When I test your code I get an out of range error when c becomes 500 at waveform [c] += $$anonymous$$athf.Abs (samples [i]);
Why do you have the size at 500 and why is the code able to go above that at all? I don't understand your code enough to be able to work around it so a little help would be appreciated!
You got that error because some audio has multiple chanels, the correct code is:
public static float[] GetWaveform(AudioClip audio, int size, float sat)
{
float[] samples = new float[audio.channels * audio.samples];
float[] waveform = new float[size];
audio.GetData(samples, 0);
int packSize = audio.samples * audio.channels / size;
float max = 0f;
int c = 0;
int s = 0;
for (int i = 0; i < audio.channels * audio.samples; i++)
{
waveform[c] += $$anonymous$$athf.Abs(samples[i]);
s++;
if (s > packSize)
{
if (max < waveform[c])
max = waveform[c];
c++;
s = 0;
}
}
for (int i = 0; i < size; i++)
{
waveform[i] /= (max * sat);
if (waveform[i] > 1f)
waveform[i] = 1f;
}
return waveform;
}