Search Unity

Create waveform before the audio starts

Discussion in 'Audio & Video' started by CrazyPizza, Feb 18, 2015.

  1. CrazyPizza

    CrazyPizza

    Joined:
    Nov 30, 2012
    Posts:
    5
    Hello Unity forums,
    I'm starting a new project and I've come across a series of questions about audio; I'd like to create a waveform for an user selected song (mp3 since it will be played on mobile) before it starts to play. I want to get the waveform because the level of the game will be created on that.

    So my main question is: how do I get a static waveform of an audioClip? (Like the waveform preview in Unity)

    I've done some googling and found many helpful answers like this or this and I understand that I have to use AudioClip.getData to get the samples, however I don't really understand what these samples are and the code in the second link draws the waveform while the audio is playing.

    From what I understand each sample (over 16 million for my test song) represents a state of the audio in that particular moment, is that correct? So if I get the samples with getData I can pick up each one and draw the waveform? There are millions of samples though and using them all would be impractical, so would taking a chunk of them, average them and use that result as a waveform point work?

    I don't need a perfectly matching waveform (because I think that would be impossible?), an average one is ok too.
    Drawing it would help while testing but that's secondary at the moment. I need help understanding how this whole waveform thing works.

    Thanks in advance for the help guys!
     
  2. HelloMeow

    HelloMeow

    Joined:
    May 11, 2014
    Posts:
    281
    The samples are the wave form. They're values ranging from -1 to 1 that describe the signal.


    I think Unity might alternate between channels. So if an AudioClip has 2 channels all even numbered samples are for one channel and the odd numbered ones are for the other. I'm not sure about this.

    Reducing the samples is a good idea, because audio often has a sample rate of 44100 samples per second. You don't need that kind of precision. You could easily reduce that to 60. When averaging the samples I think it might be a good idea to use their absolute value.

    I couldn't resist trying this for myself. This doesn't take different channels into account, since I find the way Unity handles channels and how it's documented really confusing. It looks a lot like the preview nonetheless.
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class WaveFormTest : MonoBehaviour {
    6.  
    7.     int resolution = 60;
    8.  
    9.     float[] waveForm;
    10.     float[] samples;
    11.  
    12.     // Use this for initialization
    13.     void Start () {
    14.  
    15.         resolution = audio.clip.frequency / resolution;
    16.  
    17.         samples = new float[audio.clip.samples*audio.clip.channels];
    18.         audio.clip.GetData(samples,0);
    19.  
    20.         waveForm = new float[(samples.Length/resolution)];
    21.  
    22.         for (int i = 0; i < waveForm.Length; i++)
    23.         {
    24.             waveForm[i] = 0;
    25.  
    26.             for(int ii = 0; ii<resolution; ii++)
    27.             {
    28.                 waveForm[i] += Mathf.Abs(samples[(i * resolution) + ii]);
    29.             }          
    30.  
    31.             waveForm[i] /= resolution;
    32.         }
    33.     }
    34.  
    35.     // Update is called once per frame
    36.     void Update () {
    37.         for (int i = 0; i < waveForm.Length - 1; i++)
    38.         {
    39.             Vector3 sv = new Vector3(i * .01f, waveForm[i]*10 , 0);
    40.             Vector3 ev = new Vector3(i * .01f, -waveForm[i] * 10, 0);
    41.  
    42.             Debug.DrawLine(sv, ev, Color.yellow);
    43.         }
    44.  
    45.         int current = audio.timeSamples / resolution;
    46.         current *= 2;
    47.  
    48.         Vector3 c = new Vector3(current*.01f,0,0);
    49.  
    50.         Debug.DrawLine(c, c + Vector3.up * 10, Color.white);
    51.     }
    52. }
    53.  
    54.  
     
    Last edited: Feb 18, 2015
    Michael-Polla likes this.
  3. Kaze_Senshi

    Kaze_Senshi

    Joined:
    Feb 19, 2012
    Posts:
    243
    When you say "Multiple Channels" are you talking about the channels used by the Stereo soundclips? If it is about that then you could try to check the field "Mono" on the desired SoundClip
     
  4. HelloMeow

    HelloMeow

    Joined:
    May 11, 2014
    Posts:
    281
    That isn't available for all clips (apparently, since it's grayed out for my imported songs) and it will make the song mono instead of stereo, which isn't ideal.
     
  5. atmospherium

    atmospherium

    Joined:
    May 12, 2013
    Posts:
    38
    You can find the channel count for an audio clip by using audio.clip.channels (http://docs.unity3d.com/ScriptReference/AudioClip-channels.html). Audio clips are interleaved, so if you iterate using that value (i += audio.clip.channels) you can get the values for a single channel without impacting the clip itself.
     
  6. CrazyPizza

    CrazyPizza

    Joined:
    Nov 30, 2012
    Posts:
    5
    Thanks for all the answers guys, I finally have some time to work on this and you helped me greatly so far
     
  7. CrazyPizza

    CrazyPizza

    Joined:
    Nov 30, 2012
    Posts:
    5
    I need some help understanding better what you did in this code. From what I understand the resolution is the parameter that defines the fidelty of the waveform right? 40k = 1:1 fidelty, is that correct?

    There's only one thing i'm not sure about in your code:

    When you say

    Code (CSharp):
    1. waveForm[i] += Mathf.Abs(samples[(i * resolution) + ii]);
    with samples[(i * resolution) + ii] you are taking all the samples starting from that resolution point (i*resolution) until the next one (+ ii), is that correct?

    Again thanks for the help, your code helped me greatly
     
  8. HelloMeow

    HelloMeow

    Joined:
    May 11, 2014
    Posts:
    281
    That is correct. I use the resolution variable a bit ambiguously in the example though. At first it means how many samples per second the waveform array will contain. Which is 60 in this case.

    Then I immediately convert it to how many samples from the AudioClip are needed per sample in the waveform array. Which is 735 in most cases. (44100/60=735)