Search Unity

Split Audio Waveform sprite that it width is out of range in a Scroll Rect

Discussion in '2D' started by Pedrinbeep, Sep 28, 2022.

  1. Pedrinbeep

    Pedrinbeep

    Joined:
    Jul 30, 2022
    Posts:
    7
    Hi! 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):

    Code (CSharp):
    1. public void LoadWaveform(AudioClip clip)
    2.     {
    3.         Texture2D texwav = waveformSprite.GetWaveform(clip);
    4.         Rect rect = new Rect(Vector2.zero, new Vector2(Realwidth, 180));
    5.  
    6.         waveformImage.sprite = Sprite.Create(texwav, rect, Vector2.zero);
    7.         waveformImage.SetNativeSize();
    8.     }


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

    Code (CSharp):
    1. public class WaveformSprite : MonoBehaviour
    2. {
    3.     private int width = 16000; // This should be a variable that came from another script
    4.     private int height = 180;
    5.     public Color background = Color.black;
    6.     public Color foreground = Color.yellow;
    7.  
    8.     private int samplesize;
    9.     private float[] samples = null;
    10.     private float[] waveform = null;
    11.     private float arrowoffsetx;
    12.  
    13.     public Texture2D GetWaveform(AudioClip clip)
    14.     {
    15.         int halfheight = height / 2;
    16.         float heightscale = (float)height * 0.75f;
    17.  
    18.         // get the sound data
    19.         Texture2D tex = new Texture2D(width, height, TextureFormat.RGBA32, false);
    20.         waveform = new float[width];
    21.  
    22.         var clipSamples = clip.samples;
    23.  
    24.         samplesize = clipSamples * clip.channels;
    25.         samples = new float[samplesize];
    26.         clip.GetData(samples, 0);
    27.  
    28.         int packsize = (samplesize / width);
    29.         for (int w = 0; w < width; w++)
    30.         {
    31.             waveform[w] = Mathf.Abs(samples[w * packsize]);
    32.         }
    33.  
    34.         // map the sound data to texture
    35.         // 1 - clear
    36.         for (int x = 0; x < width; x++)
    37.         {
    38.             for (int y = 0; y < height; y++)
    39.             {
    40.                 tex.SetPixel(x, y, background);
    41.             }
    42.         }
    43.  
    44.         // 2 - plot
    45.         for (int x = 0; x < width; x++)
    46.         {
    47.             for (int y = 0; y < waveform[x] * heightscale; y++)
    48.             {
    49.                 tex.SetPixel(x, halfheight + y, foreground);
    50.                 tex.SetPixel(x, halfheight - y, foreground);
    51.             }
    52.         }
    53.  
    54.         tex.Apply();
    55.  
    56.         return tex;
    57.     }
    58. }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,736
    You just have to "chunk it" and change your API so it returns a series of sprites.

    Depending on the overall final size, which affects how much memory will be required, you might need to also limit the total number of created textures and begin discarding ones that are offscreen.

    The latter would imply some sort of paging API, and an awareness of which chunks are offscreen and can be discarded.
     
  3. Pedrinbeep

    Pedrinbeep

    Joined:
    Jul 30, 2022
    Posts:
    7
    I have the same idea of how to proceed but I don't know how to code it. Any code example to chunk the sprite and return a series of sprites? Thank you
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,736
    No code lying around... every pager / chunker I've written is tightly bound into whatever game it is involved in, and as such it is covered with warts and minutiae.

    If you make each chunk a fixed square size you can simplify a lot of the math: it's just a series of squares placed adjacently.

    I imagine a simple paging API to have things like;

    - query number of chunks
    - give me chunk X
    - release chunk X (This lets the chunker manage resources)

    That's where I would start... write the API and mock it out, then plug it into your scrollview. For squares outside of the visible rect, just have a blank tiny 32x32 sprite that you use repeatedly, because the user will never know.
     
  5. Pedrinbeep

    Pedrinbeep

    Joined:
    Jul 30, 2022
    Posts:
    7
    Okey, I will try to find a way to split the sprite and continue with the rest things.