Pages

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  

43 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
  3. doesnt work mp3 files in unity 3d

    ReplyDelete
    Replies
    1. Yes, so please use my example to resolve this issue :)

      Delete
  4. Hi Denis !

    First of all, thank you a lot for your tutorials ! Unfortunately, the way i want to select the mp3 files is a bit different than yours and i can't success to adapt your code. What i want is the files loading during the launching time of the application without any selection in a Browser . After loading, stock them all in an array ( or list , etc .. ) in order to assign them to different AudioSources ..

    Can you help me ?

    Thank youuu

    Nathan

    ReplyDelete
    Replies
    1. Hello Nathan,

      You cannot create AudioSource and set the loaded audio file for it in real time in Unity3d. That is why I showed how to play audio, without actually creating audio source. That is what Unity3d supports now. Perhaps in future, this feature will be implemented.

      Regards,
      Denis

      Delete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Hi Denis,

    Could you try running this same code using an .m4a file and reporting your results? Naudio is supposed to support m4a files through MediaFoundation - this works fine in their demo apps but crashes within unity.

    Thanks!

    ReplyDelete
    Replies
    1. Hello Martin,

      I am sorry, but now, I have not time for this :(

      Delete
  7. Hello and thanks for the tutorial!

    What I have done is, converting the mp3 file to wav in real time before playing, extract the mp3 tag and use the audioclip/audiosource with the wav file. This way I can use the best of both methods.

    Greetings!

    ReplyDelete
    Replies
    1. Hello Robert,

      I will try to help but always have not a lot of time for this. Also we used another .NET library to process the mp3 files. So this plugin can be embedded to the WebBuild!

      Regards,
      Denis

      Delete
    2. Hello, could you please share code, how you convert mp3 to wav at runtime?

      Delete
  8. i'm look close to goal
    any way to add loading mp3 in list please ?

    ReplyDelete
    Replies
    1. Hello Kilik,
      do you mean to load a lot of mp3 files? Or something else?

      Delete
    2. i have buy little asset for do this job
      but i need help for my projet it's simple mix audio and save
      is you can help i can share it

      Delete
    3. Just use the similar code to create several streams.

      Delete
  9. Hello thanks for the tutorial, LoadAudioFromData(byte[] data) method throws exception: Error! Msacm32.dll.

    ReplyDelete
    Replies
    1. Hello Jakhongir,
      Please share your project with me, and I will help you!

      Delete
  10. This comment has been removed by the author.

    ReplyDelete
  11. Also I get this:
    DllNotFoundException: Msacm32.dll
    NAudio.Wave.Compression.AcmStream.SuggestPcmFormat (NAudio.Wave.WaveFormat compressedFormat)
    NAudio.Wave.AcmMp3FrameDecompressor..ctor (NAudio.Wave.WaveFormat sourceFormat)
    NAudio.Wave.Mp3FileReader.CreateAcmFrameDecompressor (NAudio.Wave.WaveFormat mp3Format)
    NAudio.Wave.Mp3FileReader..ctor (System.IO.Stream inputStream, NAudio.Wave.FrameDecompressorBuilder frameDecompressorBuilder)

    ReplyDelete
    Replies
    1. Hello Jakhongir,
      Please share your project with me, and I will help you! Thank you in advance

      Delete
  12. This comment has been removed by the author.

    ReplyDelete
  13. Sorry if there was already an answer for this but skimming through I did not see if anyone had asked but will this work on a Mac in Unity 5 & up?

    ReplyDelete
    Replies
    1. Hello Michael,

      Its ok :) I just checked in Unity3d 5.2.0f3 - everything works fine!

      Delete
  14. This comment has been removed by the author.

    ReplyDelete
  15. This comment has been removed by the author.

    ReplyDelete
  16. Im getting

    `MemoryStream' could not be found. Are you missing a using directive or an assembly reference? error

    ReplyDelete
    Replies
    1. Please use sources to build the project and proper Unity3d version

      Delete
  17. Have you found, how to decide error DllNotFoundException: Msacm32.dll on Mac?

    ReplyDelete
  18. it ALWAYS says
    Error! Invalid MP3 file - no MP3 Frames Detected
    UnityEngine.Debug:LogWarning(Object)
    Testmp3:LoadAudioFromData(Byte[]) (at Assets/Testmp3.cs:37)
    Testmp3:LoadAudio() (at Assets/Testmp3.cs:72)
    Testmp3:prova() (at Assets/Testmp3.cs:43)
    UnityEngine.EventSystems.EventSystem:Update()


    NOT WORK......

    ReplyDelete
    Replies
    1. and the variable cLocalPath is NEVER declared -.-

      Delete
    2. Please download sources. In article presented only part of them

      Delete
  19. I cannot wait to dig deep and kickoff utilizing resources that I received from you. Your exuberance is refreshing. BTS Boy With Luv Dance Practice RM mp3 download

    ReplyDelete
  20. Pretty good post. I have just stumbled upon your blog and enjoyed reading your blog posts very much. I am looking for new posts to get more precious info. Big thanks for the useful info. sbunoah ewe getsemane live mp3 download

    ReplyDelete