Playing wav files in Unity

Has anyone for any sample code to take a .wav file from the persistentDataPath, and play it?
I actually have a few I need to play back to back, so I’d need to know when each had finished playing so I could play the next one.

The wav files were generated by Unity, I just want to replay them now.

From what I understand I need to create and AudioClip and load the wavFile into it, then play through an AudioSource.

But I’m not sure how to do the load.

If someone has some simple working sample code that would be great.

Thanks in advance.

You can get it with WWW using a file path.

Thanks
I tried that. It isn’t erroring.
But isn’t doing anything either.

I can see looking at my debugtext that the files are correct.

Any ideas?

    public void ReplayAllSounds(){
            for (int x = 0; x < InteractionPanel.instance.saveClips.Count (); x++) {
            debugText.GetComponent<Text> ().text = Application.persistentDataPath + InteractionPanel.instance.saveClips[x];
             WWW audioLoader = new WWW(Application.persistentDataPath + InteractionPanel.instance.saveClips[x]);
            audioLoader.GetAudioClip ();
            while (!audioLoader.isDone)
            {
                Debug.Log("uploading");
            }
            go3AudioSource.Play ();

        }
    }

Sorry, I literally thought there would be something simple like PlayWav(“filename”)

1 Like

You code is incorrect!
I also recommend to use UnityWebRequest instead of WWW.
Here is documentation with script example:

Thanks. Could you elaborate on what the existing code is wrong before I look to convert to your suggested method.
Thanks

OK, having done more reading, it appears I need to use coroutines, as the clip before was completing in a single update?
Is that correct?

I had a bit of a play with the following

    // Replay All SOunds
    public void ReplayAllSounds(){
            for (int x = 0; x < InteractionPanel.instance.saveClips.Count (); x++) {
            //debugText.GetComponent<Text> ().text = debugText.GetComponent<Text> ().text + InteractionPanel.instance.saveClips[x];
            debugText.GetComponent<Text> ().text = Application.persistentDataPath + InteractionPanel.instance.saveClips[x];
             StartCoroutine(  GetAudioClip(Application.persistentDataPath + InteractionPanel.instance.saveClips[x]))   ;
            //go3AudioSource.PlayOneShot(myclip);
            go3AudioSource.Play ();

        }
    }

    IEnumerator GetAudioClip(string clipLocation)
    {
        using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(clipLocation, AudioType.WAV))
        {
            yield return www.SendWebRequest();

            {
                AudioClip myClip = DownloadHandlerAudioClip.GetContent(www);
                go3AudioSource.PlayOneShot(myClip);
                //go3AudioSource.Play ();
            }
        }
    }

but its struggling to do anythingwith the IEnumerator returned by the function.
Sorry, this is way outside my understanding.
And which bits need to go in the seperate function - just th download, or the play as well?

Either way, I’m really lost now.

Thanks

StartCoroutine() lets you run other method as yet another Update() loop, while IEnumerator returned by that method lets you control, when it has to do something.
When you “yield return www.SendWebRequest();” it suspends this coroutine until this UnityWebRequest is finished, that can be any number of frames, just the next line after “yield return” will be run in during first Update AFTER UnityWebRequest is complete.

The alternatives for coroutine are:

  • just start UnityWebRequest by calling SendWebRequest() and during the next frame check it’s isDone property; play audio when isDone becomes true; do this for each audio clip.
  • the SendWebRequest() returns an AsyncOperation, to which you can attach a completion handler, that will be called once loading finishes

Thanks.
Do you have any complete examples of this being done?
By complete I mean all components together?

Apologies, I’m really new to this stuff. I look at the documentation and bits make sense, but putting it together is difficult for me.

edit: the latter sounds easier if possible, I’d rather not spread my code into the update function

OK, I have tried again
No errors, and it gets as far as printing the “DONE”
But no sound. I realise I probably need a flag to say if I want to be lookking to play something, but for now I’d settle for something coming out of the speakers.

What am I missing?

And also, I dont appear to be telling the audio source what clip to play. Do I need to do that, and if so how?

    void Update () {
checkIfSoundFileLoaded();

    }

public void ReplayAllSounds(){
            for (int x = 0; x < InteractionPanel.instance.saveClips.Count (); x++) {
            //debugText.GetComponent<Text> ().text = debugText.GetComponent<Text> ().text + InteractionPanel.instance.saveClips[x];
            debugText.GetComponent<Text> ().text = Application.persistentDataPath + InteractionPanel.instance.saveClips[x];
            www = UnityWebRequestMultimedia.GetAudioClip(Application.persistentDataPath + InteractionPanel.instance.saveClips[x], AudioType.WAV);
            www.SendWebRequest( );

        }
    }

    void checkIfSoundFileLoaded(){
        if (www.isDone) {
            debugText.GetComponent<Text> ().text = "DONE";
            go3AudioSource.Play ();
        }

    }

First of all, you are loading all of your sounds at once, not one by one. And you are only checking the last one for being done, you simply lose all other UnityWebRequest objects as you assign them all to the same variable.
You should also probably call Play() on audio source once, now you are calling it every frame after you’ve finished loading and I also don’t see where you take a loaded audio clip.

OK, thanks, I guess I can sort the logic out so it sets flags and does one at a time.
And only plays once.
Unfortunately it doesnt help that the code to generate said files only runs in android, so its difficult to debug.

What do you mean by ‘take a loaded audio clip’?
Do you mean assigned to the audio source?
And if so how?

OK, my latest is as follows:

    public void ReplayAllSounds(){
            //for (int x = 0; x < InteractionPanel.instance.saveClips.Count (); x++) {
        playingSoundFile = false;
            for (int x = 0; x < 1; x++) {
            //debugText.GetComponent<Text> ().text = debugText.GetComponent<Text> ().text + InteractionPanel.instance.saveClips[x];
            debugText.GetComponent<Text> ().text = Application.persistentDataPath + InteractionPanel.instance.saveClips[x];
            www = UnityWebRequestMultimedia.GetAudioClip(Application.persistentDataPath + InteractionPanel.instance.saveClips[x], AudioType.WAV);
            www.SendWebRequest( );
            playingSoundFile = false;

        }
    }

    void checkIfSoundFileLoaded(){
        if (www.isDone) {
            //go3AudioSource.clip = www.GetA
            debugText.GetComponent<Text> ().text = "ISDONE";
            //if (playingSoundFile == false)
            if (true){
                debugText.GetComponent<Text> ().text = "DONE";
                playingSoundFile = true;
                AudioClip audioClip = DownloadHandlerAudioClip.GetContent (www);
                go3AudioSource.clip = audioClip;
                go3AudioSource.Play ();

            } 
            }
        }

I need to hard code TRUE to even get it into the bit where it prints DONE (I’ve no idea why), but it is getting in there.
If I have the proper if statement I just get ISDONE
Now it prints DONE

Nothing playing though.

I cant even begin to look at the logic until I can get a single clip to load and play.
Help …
Please …
Thanks

Can you play an audio clip normally through that audio source? As in you assign an audio clip directly to the source, and set it to play on awake, are you getting a sound?

It might be that your audio source and audio listeners are too far apart and the audio source is in 3D or something like that.
Also make sure the audio clip you’re downloading can be played if it’s just dumped in Unity.

I have made a little progress.
I started a whole new app to do some testing, as I am not able to test my main one in the editor due to the files being created in Android.

I’ve succesfully got it to output sound. It was repeating the one sound over and over.
I then set on adding logic to move on, and it obviously got stuck in a loop.
It crashed my PC, and on loading back up I’ve lost all of the gameobjects.
Forunately it kept my code.

I just need to work out the best way of moving through my list of files, playing them, and moving on to the next once it has finished.

This is what I ended up with when I crashed the PC:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Text;

using System.Text.RegularExpressions;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System;
using UnityEngine.Networking;
public class TestSound : MonoBehaviour {
    public GameObject textBox;
    public AudioSource g03AudioSource;
    bool playingSoundFile = false;
    int clipCount = 1;
    int clipIndex = 0;
    UnityWebRequest www;
    // Use this for initialization
    void Start () {
        textBox.GetComponent<Text>().text = Application.persistentDataPath;
        ReplayAllSounds ();
    }
    
    // Update is called once per frame
    void Update () {
        checkIfSoundFileLoaded ();
    }
    // Replay All SOunds
    public void ReplayAllSounds(){
        //for (int x = 0; x < InteractionPanel.instance.saveClips.Count (); x++) {
        playingSoundFile = false;
        do {
            //debugText.GetComponent<Text> ().text = debugText.GetComponent<Text> ().text + InteractionPanel.instance.saveClips[x];
            textBox.GetComponent<Text> ().text = Application.persistentDataPath + "/test" + clipIndex + ".wav";
            //string wavFile = Application.persistentDataPath + InteractionPanel.instance.saveClips[x];
            //AudioClip audioClip = WavUtility.ToAudioClip (wavFile);
            //AudioSource.PlayClipAtPoint = audioClip;
            //AudioSource.PlayClipAtPoint ();
            www = UnityWebRequestMultimedia.GetAudioClip (Application.persistentDataPath + "/test" + clipIndex + ".wav", AudioType.WAV);
            www.SendWebRequest ();
            playingSoundFile = false;
        } while (clipIndex <= clipCount);

    }

    void checkIfSoundFileLoaded(){
        if (www.isDone) {
            //go3AudioSource.clip = www.GetA
            textBox.GetComponent<Text> ().text = "ISDONE";
            if (!g03AudioSource.isPlaying) {
                textBox.GetComponent<Text> ().text = "DONE";
                playingSoundFile = true;
                AudioClip audioClip = DownloadHandlerAudioClip.GetContent (www);
                g03AudioSource.clip = audioClip;
                g03AudioSource.Play ();

        } 
        else{
            // CLip Finished
            clipIndex++;
        }
        }
    }
}

OK, so in the above code it must have gone into an infinite loop in my while loop.
But I’m struggling to work out the best way of looping through.
Because I am going out to a background process, if I use a loop it just runs away through the for loop through all my values before the first sound has finished playing.

If I use a do/while it just spins until it crashes.

Could someone help me out with the best syntax to be able to loop through my list of files, but then checking whether the current one completes in the background before moving on.

Thanks

Right, apologies for the repeated posting.
Debugging this in the editor I think it seems to do what I want, although its not easy as it loads and plays the sounds in real time.
But it only ever plays 1 sound.
I have files for test0.wav, test1.wav, test2.wav
Does the web request need resetting - does it always think it is complete after the first one, even though it has moved on to a new request?

Or more generally, is there a better way to do this (most likely)?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Text;

using System.Text.RegularExpressions;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System;
using UnityEngine.Networking;
public class TestSound : MonoBehaviour {
    public GameObject textBox;
    public AudioSource g03AudioSource;
    bool playingSoundFile = false;
    bool loadingAudioClip = false;
    int clipCount = 2;
    int clipIndex = 0;
    UnityWebRequest www;
    // Use this for initialization
    void Start () {
        textBox.GetComponent<Text>().text = Application.persistentDataPath;

    }
   
    // Update is called once per frame
    void Update () {
        ReplayAllSounds ();
        checkIfSoundFileLoaded ();
    }
    // Replay All SOunds
    public void ReplayAllSounds(){
            if (clipIndex <= clipCount && playingSoundFile == false && loadingAudioClip == false){
            textBox.GetComponent<Text> ().text = Application.persistentDataPath + "/test" + clipIndex + ".wav";
            loadingAudioClip = true;
            playingSoundFile = false;
            www = UnityWebRequestMultimedia.GetAudioClip (Application.persistentDataPath + "/test" + clipIndex + ".wav", AudioType.WAV);
            www.SendWebRequest ();

        }

    }

    void checkIfSoundFileLoaded(){
        if (www.isDone ) { // If loaded
            textBox.GetComponent<Text> ().text = "ISLOADED " + clipIndex;
            if (!g03AudioSource.isPlaying) { // If not playing
                // First time not playing - startplaying
                if (playingSoundFile == false ) {
                    playingSoundFile = true;
                textBox.GetComponent<Text> ().text = "ISPLAYING "+ clipIndex;
                playingSoundFile = true; //set flag to say playing
                AudioClip audioClip = DownloadHandlerAudioClip.GetContent (www);
                g03AudioSource.clip = audioClip;
                g03AudioSource.Play (); // and play
                }
                    else
                        {
                        // we have already played and finished
                        playingSoundFile = false;
                        loadingAudioClip = false;
                        clipIndex++; // move on to next one
                    }
            }
        
            }
        }

}

From what I can tell g03AudioSource.isPlaying seems unreliable as a test to see whether the current clip is completed.
It seems to always return true

In fact, looking at the API docs it seems to imply this is only set when the Pause method is called.

Are there any known issues with its reliability to tell if the clip is complete?

Stepping back from this slightly, all I need to do is play a load of clips in the order they were created.
After this, I’d like to offer the option to save into a single wav file anyway.

So perhaps I’m better off cutting out the middle man and instead of playing the clips just combinging them into a single file.

To repeat my original question that was asked many hours ago, does anyone have any sample code for that?

Seems to force http://localhost even for local persistent path audio files such as /Users/me/blabla.wav becomes http://localhost/Users/me/blabla.wav

To the original post, I’d like to find a WAV parser to load WAV files, because Unity’s is broken in webGL. Note: some files work, and some files work on one server but not from another, even though they are identical. Why? I don’t work for Unity so I can’t say, except I need something that always works. Please.