Monday, April 22, 2013

Task #6: Loading MP3 audio via WWW class in Unity3d

Practical use of Unity3d engine.



Task #6: Loading MP3 audio via WWW class in Unity3d


  Let’s solve the task: Application should implement mp3 audio loading from file system via WWW class in Unity3d.
  Since Unity3d doesn’t support mp3 audio loading via WWW class for windows, we will load mp3 binary data via class WWW and pass binary data to NAudio library. NAudio library is free, and able to play mp3 audio from binary data (read more here: http://naudio.codeplex.com/). 
  At first, download NAudio library, and copy NAudio.dll and NAudio.WindowsMediaFormat.dll files to Assets folder. 
  Then, enable full .NET Framework version in Unity3d. To do this choose menu item «File/Build Settings/Player Settings/Other Settings/Optimization/Api Compatibility Level», and set «.NET 2.0» parameter. 
  On fig.1 you can see process of choosing full .NET Framework.
Figure 1. – Choose full .NET Framework implementation 


  Let’s start to write scripts. At first, check that NAudio library is already attached to project. To do this, expand «References» section in project and check existing of NAudio libraries. If they are not attached, attach them. «References» section is shown on fig. 2.
Figure 2. – «References» section 
Now, add using statements. 
using NAudio;
using NAudio.Wave; 
Create class variables.
private IWavePlayer mWaveOutDevice;
private WaveStream mMainOutputStream;
private WaveChannel32 mVolumeStream;
Create function that wills load audio from bytes array.
private bool LoadAudioFromData(byte[] data)
{
    try
    {
        MemoryStream tmpStr = new MemoryStream(data);
        mMainOutputStream = new Mp3FileReader(tmpStr);
        mVolumeStream = new WaveChannel32(mMainOutputStream);

        mWaveOutDevice = new WaveOut();
        mWaveOutDevice.Init(mVolumeStream);

        return true;
    }
    catch (System.Exception ex)
    {
        Debug.LogWarning("Error! " + ex.Message);
    }

    return false;
}
The algorithm of the function is:
  • Create instance of the «MemoryStream» class; 
  • Since we will load audio in mp3 format, create instance of the «Mp3FileReader» class; 
  • Create instance of the «WaveChannel32» class; 
  • At last, create instance of the «WaveOut» class to play audio; 

Create «LoadAudio» function. 
private void LoadAudio()
{
    System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();
    ofd.Title = "Open audio file";
    ofd.Filter = "MP3 audio (*.mp3) | *.mp3";
    if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
    {
        WWW www = new WWW(cLocalPath + ofd.FileName);
        Debug.Log("path = " + cLocalPath + ofd.FileName);
        while (!www.isDone) { };
        if (!string.IsNullOrEmpty(www.error))
        {
            System.Windows.Forms.MessageBox.Show("Error! Cannot open file: " + ofd.FileName + "; " + www.error);
            return;
        }

        byte[] imageData = www.bytes;

        if (!LoadAudioFromData(imageData))
        {
            System.Windows.Forms.MessageBox.Show("Cannot open mp3 file!");
            return;
        }
        
        mWaveOutDevice.Play();

        Resources.UnloadUnusedAssets();
    }
}
The algorithm of the function is: 
  • Show «OpenFileDialog» to user. Set filters to mp3 files; 
  • Load mp3 binary data via WWW class;
  • Pass loaded data to «LoadAudioFromData» function. If function returns true – play audio; 

To unload audio from memory, create «UnloadAudio» function. 
private void UnloadAudio()
{
    if (mWaveOutDevice != null)
    {
        mWaveOutDevice.Stop();
    }
    if (mMainOutputStream != null)
    {
        // this one really closes the file and ACM conversion
        mVolumeStream.Close();
        mVolumeStream = null;

        // this one does the metering stream
        mMainOutputStream.Close();
        mMainOutputStream = null;
    }
    if (mWaveOutDevice != null)
    {
        mWaveOutDevice.Dispose();
        mWaveOutDevice = null;
    }
}
The algorithm of the function is:
  • Stop playing audio; 
  • Close «WaveChannel32» and «WaveStream»; 
  • Free memory for instance of the «IWavePlayer» class; 

  That's all. Also, there is a good example of loading mp3 audio by means of NAudio library, you can see here: http://naudio.codeplex.com/wikipage?title=MP3.
  If you need more detailed article description, please write it in comments. 

  Demo of the application you can download here:
https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson6/Builds/Builds.zip 
  Free source codes you can download here: 
https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson6/Sources/Source.zip
https://github.com/den-potapenko/Unity3dArticles/tree/master/Lesson6  

7 comments:

  1. Hi Denis,

    Thanks so much for the awesome tutorial and code.

    I have the file browser working, and the MP3s loading, but my game I'm making depends on an audio source playing the MP3 through it's audio clip. I feel like I'm so close to getting this to work.

    I tried different variations of audio.clip = www.audioClip; and audio.clip = www.GetAudioClip(); in void LoadAudio(), but to no avail. The script is attached to an audiosource.

    Can you help?

    Sincerely,
    Jonathan

    ReplyDelete
    Replies
    1. Hi Jonathan,

      As far as i remember, Unity3d WWW.audioClip supports only Ogg files. MP3 files are not supported via WWW class.

      You can read about this here:
      http://docs.unity3d.com/Documentation/ScriptReference/WWW-audioClip.html
      http://docs.unity3d.com/Documentation/Manual/AudioFiles.html

      So, for loading MP3 files i created such tutorial. But this tutorial uses external *.dll file (from NAudio library), and its works on PC and standalone, but Unity3d WebPlayer doesn't supports external *.dll plugins.

      But i think there is a solution for you - it is use not NAudio *.dll file, but embed the source code of NAudio to you project. Since NAudio is a free and open source library, you can download the source code and embed it to your project.

      So, the solution for you:
      - Download the source code of NAudio from here: http://naudio.codeplex.com/releases/view/96875;
      - Embed it to your project;
      - Compile it, and create Unity3d WebPlayer build;
      - Thats all :)

      If you have any questions, please ask me. Also, please tell me about your results. If this solution is not working for some reason, also please tell me.

      Best regards,
      Denis

      Delete
  2. First, thanks for the detailed and quick response, I really appreciate it!

    I should have been a bit more clear though, my bad. I actually don't need anything to work in the unity web player, my game will be standalone. What I need is, using your AppRoot code from the tutorial, to take the Mp3 file the user selects, and have an Audio Source in Unity play that mp3.

    Right now, I'm using your source code to load an mp3 through the file browser, and that works great, but unfortunately like I said I need the audio source specifically to play it.

    Theoretically it sounds easy. But I've tried to get the audio.clip to load the mp3 the user selects in multiple ways, but I can't figure it out.

    I tried looking around in the NAudio source code like you said, and embedding it in my project, but that just produced errors. I may not have been doing it right.

    I'm not much of a programmer, and I've never really worked in unity before this, so you might have to dumb it down a bit for me. Sorry for the inconvenience, and thanks again for the help.

    Sincerely,
    Jonathan

    ReplyDelete
    Replies
    1. Jonathan,

      Please tell me, why do you need AudioSource object?

      Best regards,
      Denis

      Delete
    2. I'm using this program - http://u3d.as/content/altered-reality-entertainment/visualizer-studio/2dq

      It manipulates object properties according to music, and it takes the music data from an audio source. I don't know if it's capable of getting data from another source, but right now it only lets me select audio sources. Screenshot - http://i.imgur.com/3zjJUBV.png

      Delete
    3. Jonathan,

      I see. Unfortunately, the way presented in this article is custom solution for playing audio Unity3d. But, if you already bought this asset, you may just to inform developers of the asset, that you want to play music from custom audio source. I think they know the solution, because AudioSource for developers is only the way the get the audio data. And i think this is not the single way to do it :)

      Best regards,
      Denis

      Delete
    4. Ok, thanks for the advice, I contacted the developers! Once again thanks for your help.

      Sincerely,
      Jonathan

      Delete