Home > Software engineering >  Split Audio Waveform sprite that it width is out of range in a Scroll Rect
Split Audio Waveform sprite that it width is out of range in a Scroll Rect

Time:10-01

I'm new to Unity 3D and trying to split a texture2D sprite that contains an audio waveform in a Scroll Rect. The waveform comes from an audio source imported by the user and added to a scroll rect horizontally like a timeline. The script that creates the waveform works but the variable of the width (that came from another script, but this is not the problem) exceeds the limits of a Texture2D, only if I put manually a width less than 16000 the waveform appear but not to the maximum of the scroll rect. Usually, a song with 3-4min has a width of 55000-60000 width, and this can't be rendered. I need to split that waveform texture2D sprite horizontally into multiple parts (or Childs) together and render them only when appearing on the screen. How can I do that? Thank you in advance.

This creates the Waveform Sprite, and should split the sprite into multiple sprites and put together horizontally, render them only when appear on the screen):

public void LoadWaveform(AudioClip clip)
{
    Texture2D texwav = waveformSprite.GetWaveform(clip);
    Rect rect = new Rect(Vector2.zero, new Vector2(Realwidth, 180));

    waveformImage.sprite = Sprite.Create(texwav, rect, Vector2.zero);
    waveformImage.SetNativeSize();
}

This creates the waveform from an audio clip (getting from the internet and modifying for my project) :

public class WaveformSprite : MonoBehaviour
{
private int width = 16000; //This should be the variable from another script
private int height = 180;
public Color background = Color.black;
public Color foreground = Color.yellow;

private int samplesize;
private float[] samples = null;
private float[] waveform = null;
private float arrowoffsetx;

public Texture2D GetWaveform(AudioClip clip)
{
    int halfheight = height / 2;
    float heightscale = (float)height * 0.75f;

    // get the sound data
    Texture2D tex = new Texture2D(width, height, TextureFormat.RGBA32, false);
    waveform = new float[width];

    Debug.Log("NUMERO DE SAMPLES: "   clip.samples);

    var clipSamples = clip.samples;

    samplesize = clipSamples * clip.channels;
    samples = new float[samplesize];
    clip.GetData(samples, 0);

    int packsize = (samplesize / width);
    for (int w = 0; w < width; w  )
    {
        waveform[w] = Mathf.Abs(samples[w * packsize]);
    }

    // map the sound data to texture
    // 1 - clear
    for (int x = 0; x < width; x  )
    {
        for (int y = 0; y < height; y  )
        {
            tex.SetPixel(x, y, background);
        }
    }

    // 2 - plot
    for (int x = 0; x < width; x  )
    {
        for (int y = 0; y < waveform[x] * heightscale; y  )
        {
            tex.SetPixel(x, halfheight   y, foreground);
            tex.SetPixel(x, halfheight - y, foreground);
        }
    }

    tex.Apply();

    return tex;
}

}

CodePudding user response:

Instead of reading all the samples in one loop to populate waveform[], read only the amount needed for the current texture (utilizing an offset to track position in the array).

Calculate the number of textures your function will output.

var textureCount = Mathf.CeilToInt(totalWidth / maxTextureWidth); // max texture width 16,000

Create an outer loop to generate each texture.

for (int i = 0; i < textureCount; i  )

Calculate the current textures width (used for the waveform array and drawing loops).

var textureWidth = Mathf.CeilToInt(Mathf.Min(totalWidth - (maxTextureWidth * i), maxWidth));

Utilize an offset for populating the waveform array.

for (int w = 0; w < textureWidth; w  )
{
    waveform[w] = Mathf.Abs(samples[(w   offset) * packSize]);
}

With offset increasing at the end of the textures loop by the number of samples used for that texture (ie texture width).

offset  = textureWidth;

In the end the function will return an array of Texture2d instead of one.

  • Related