1

I am currently using the below code in my Unity project to stream mp3 files from a directory. It works great however, it freezes while the file is read in and the float is filled. As this project is in VR the freezes are very jarring. How can I resolve it so that it can read in without the lockup? Do I try and put it on another thread?

When commenting out the below lines there are no hitches.

aud = new AudioFileReader(musicPath);

aud.Read(AudioData, 0, (int)aud.Length);

    public void LoadSong(string musicPath){

    //Set title of song
    songTitle = Path.GetFileNameWithoutExtension(musicPath);
    if(songTitle != currentlyPlaying && songTitle != lastPlayedTitle){
        //Parse the file with NAudio
        aud = new AudioFileReader(musicPath);

        //Create an empty float to fill with song data
        AudioData = new float[aud.Length];
        //Read the file and fill the float
        aud.Read(AudioData, 0, (int)aud.Length);

        //Create a clip file the size needed to collect the sound data
        craftClip = AudioClip.Create(songTitle, (int)aud.Length, aud.WaveFormat.Channels, aud.WaveFormat.SampleRate, false);
        //Fill the file with the sound data
        craftClip.SetData(AudioData, 0);

        if(craftClip.isReadyToPlay){
            playMusic(craftClip, songTitle);
            aud.Dispose();
         }

        }

        else
        {
            playMusic(lastPlayedAudioFile, lastPlayedTitle);
        }

}

I have also tried to download the file as mentioned in this question https://answers.unity.com/questions/933090/wwwgetaudioclip-streaming-of-mp3-on-this-plattform.html using the below code and I receive the Streaming of 'mp3' on this platform is not supported error despite including the file type. Here is the code I used.

public class AudioDownloadTest : MonoBehaviour {
public AudioSource playThis;
public AudioClip clipy;
string pathh = @"D:\TestFiles\IntheAirTonightShort.mp3";

IEnumerator Download(string pathh){
    WWW song = new WWW(pathh);
    yield return song;
    clipy = song.GetAudioClip(false,false,AudioType.MPEG);
    playThis.clip = clipy;
}

void Start () {
    StartCoroutine(Download(pathh));
}

}

I have managed to improve the situation slightly by storing the last played song so that if the user selects the previous song that was played there is no delay.

Many thanks

JamieD
  • 25
  • 5
  • Can you comment out the code from `aud.Read(AudioData, 0, (int)aud.Length)` and below to see if the problem is actually there? – Programmer Nov 23 '18 at 12:17
  • Could have sworn I had already done that but maybe not :/ It still occurs but for a smaller amount of time, apologies let me take another look. – JamieD Nov 23 '18 at 12:27
  • `currentSong.isDone` is bot being used properly. Your LoadSong function should be a coroutine function and you should put in the isDone loop and call the function with StartCoroutine. See the [duplicate](https://stackoverflow.com/a/44682772/3785314) for more example. – Programmer Nov 23 '18 at 12:34
  • Removing the WWW related code still causes the issue, it is not required for functionality as the path to the audio file is passed directly to AudioFileReader. I will change my question to feature code without the WWW usage. – JamieD Nov 23 '18 at 12:39
  • When the new AudioFileReader is created there is a hitch and then it is lengthened when the file is read. It will vary dependent on the size of the audio file but the hitches definitely occur at those points. – JamieD Nov 23 '18 at 12:48
  • Thanks for that but unfortunately WWW does not support MP3 playback on PC and that is why I am using the NAudio solution. https://answers.unity.com/questions/433428/load-mp3-from-harddrive-on-pc-again.html – JamieD Nov 23 '18 at 12:55
  • The code in the duplicate link does not work as MP3 is not a supported audio type and I cannot find documentation saying that MP3 is supported from Unity in this scenario where it is being fetched from an external directory. I am using the latest non beta version of 2018.2 – JamieD Nov 23 '18 at 13:14
  • I'm sorry but I don't think you understand the issue that I am facing. I cannot stream MP3 audio from an external directory (not within resources) within Unity on the PC platform, this is available on other platforms such as mobile. I cannot find any indication of support now being available for PC and it appears that the issue was to do with a patent https://feedback.unity3d.com/suggestions/streaming-mp3-support-now-that-patent-has-expired. Unless you can actually point me to a proven solution within Unity to stream MP3 from an external path on PC then please unmark it as a duplicate. – JamieD Nov 23 '18 at 14:12
  • The suggestions provided and the duplicate link do not resolve my issue, please unmark it as a duplicate so it can be viewed by others. – JamieD Nov 24 '18 at 02:25
  • I am using 2018.2.15f, I can not find anything in the release notes of the 2018.3 beta (or other releases) to support what you claim. Please provide some actual information rather than speculation because I do not wish to invite conflicts with a new version of Unity without having evidence that it will help my issue. I have updated my question with my attempt at using WWW which I believe is what you referred to earlier. – JamieD Nov 24 '18 at 04:19
  • Running the WWW code in Unity 2018.3.0b11 resorts in the same error of "Streaming of 'mpeg' on this platform is not supported UnityEngine.WWW:GetAudioClip(Boolean, Boolean, AudioType) d__3:MoveNext() (at Assets/AudioDownloadTest.cs:13) UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)" This is using the second section of code within my question. – JamieD Nov 24 '18 at 12:14
  • I have reopened the question because you've tried what's in the duplicate and it didn't work for you – Programmer Nov 24 '18 at 12:20
  • Thank you for your help. Apologies about some of my annoyed comments, I will make sure I word my questions clearer in future. – JamieD Nov 24 '18 at 12:37
  • Np. Is AudioData is part of NAudio? This time, are really sure that the freezing is from `AudioFileReader` and `aud.Read(AudioData, 0, (int)aud.Length)` ? – Programmer Nov 24 '18 at 12:42
  • Yep that is correct and I am 100% that is the case as commenting out those functions resolve the freezes. The AudioFileReader line creates a small freeze but the the aud.Read is the significant one, I believe it is because it is filling the float array which is AudioData. I have looked into creating a separate thread to run it on but threading is something I need to learn more about before attempting a solution. – JamieD Nov 24 '18 at 13:04

1 Answers1

1

Audio is usually loaded with the WWW.GetAudioClip function but since you ran into exceptions with it, you decided to use NAudio. The freezing that happens when AudioFileReader(musicPath) and aud.Read(AudioData, 0, (int)aud.Length) executes makes sense since this constructor and function are trying to load an audio file on the main thread and creating a PCM data with them.

Because AudioFileReader is not a Unity API, you should be able to use it in from Thread. Once you're doing reading the audio's float data from another Thread, you can then do a callback to the main Thread to create AudioClip with the AudioClip.Create function since you can't use this API from the main Thread.

Grab UnityThread that enables easy callback on the main thread from this post then see below for what your new LoadSong function should look like. I used ThreadPool but you can use any other Thread API in C# if you wish.

void Awake()
{
    //Enable Callback on the main Thread
    UnityThread.initUnityThread();
}

public void LoadSong(string musicPath)
{
    ThreadPool.QueueUserWorkItem(delegate
    {
        //Set title of song
        songTitle = Path.GetFileNameWithoutExtension(musicPath);
        if (songTitle != currentlyPlaying && songTitle != lastPlayedTitle)
        {
            //Parse the file with NAudio
            aud = new AudioFileReader(musicPath);

            //Create an empty float to fill with song data
            AudioData = new float[aud.Length];
            //Read the file and fill the float
            aud.Read(AudioData, 0, (int)aud.Length);

            //Now, create the clip on the main Thread and also play it
            UnityThread.executeInUpdate(() =>
            {
                //Create a clip file the size needed to collect the sound data
                craftClip = AudioClip.Create(songTitle, (int)aud.Length, aud.WaveFormat.Channels, aud.WaveFormat.SampleRate, false);
                //Fill the file with the sound data
                craftClip.SetData(AudioData, 0);

                if (craftClip.isReadyToPlay)
                {
                    playMusic(craftClip, songTitle);

                    /*Disposing on main thread may also introduce freezing so do that in a Thread too*/
                    ThreadPool.QueueUserWorkItem(delegate { aud.Dispose(); });
                }
            });
        }
        else
        {
            UnityThread.executeInUpdate(() =>
            {
                playMusic(lastPlayedAudioFile, lastPlayedTitle);
            });
        }
    });
}
Programmer
  • 121,791
  • 22
  • 236
  • 328