Search Unity

Load local MP3 or WAV from Android as AudioClip - UnityWebRequest HTTP 404 error

Discussion in 'Scripting' started by TimTro, Dec 23, 2020.

  1. TimTro

    TimTro

    Joined:
    Jun 1, 2015
    Posts:
    20
    I'm able to load audio using UnityWebRequestMultimedia.GetAudioClip in the Unity Editor with the following code but when I run it on Android I am unable to load any files from the user's device.

    Code (CSharp):
    1. void Start() {
    2.     audio = GetComponent<AudioSource>();
    3.     string fullPath = Path.Combine("file://" + previewSong);
    4.     StartCoroutine(GetAudioClip(fullPath));
    5. }
    6.  
    7. IEnumerator GetAudioClip(string fullPath)
    8. {
    9.     using (var uwr = UnityWebRequestMultimedia.GetAudioClip(fullPath, AudioType.MPEG))
    10.     {
    11.         ((DownloadHandlerAudioClip)uwr.downloadHandler).streamAudio = true;
    12.  
    13.         yield return uwr.SendWebRequest();
    14.  
    15.         if (uwr.isNetworkError || uwr.isHttpError)
    16.         {
    17.             debugSongPath2.text = uwr.error;
    18.             yield break;
    19.         }
    20.  
    21.         DownloadHandlerAudioClip dlHandler = (DownloadHandlerAudioClip)uwr.downloadHandler;
    22.  
    23.         if (dlHandler.isDone)
    24.         {
    25.             audio.clip = dlHandler.audioClip;
    26.  
    27.             if (audio.clip != null)
    28.             {
    29.                 audio.clip = DownloadHandlerAudioClip.GetContent(uwr);
    30.  
    31.                 Debug.Log("Playing song using Audio Source!");
    32.             }
    33.             else
    34.             {
    35.                 Debug.Log("Couldn't find a valid AudioClip.");
    36.             }
    37.         }
    38.         else
    39.         {
    40.             Debug.Log("The download process is not completely finished.");
    41.         }
    42.     }
    43. }
    The errors I experience vary depending on how I form the start of the URL.

    Code (CSharp):
    1. Path.Combine("file:/" + previewSong);
    2. malformed URL
    3.  
    4. Path.Combine("file://" + previewSong);
    5. http:/1.1 404 not found
    6.  
    7. Path.Combine("file:///" + previewSong);
    8. unknown error
    9.  
    10. Path.Combine("file:////" + previewSong);
    11. unknown error
    When I output the URLs on my Android phone they look correct. Here's an example path:

    Code (csharp):
    1. file:///storage/emulated/0/Music/Deorro/Deorro - Five Hours.mp3
    I was previously loading audio from this URL successfully with WWW.GetAudioClip but that's obsolete. This leads me to believe the URL is correct. I've tried building with and without Development Mode. Not sure what else to try. I'd like to get this working with UnityWebRequestMultimedia but if there is an alternative, more effective way of loading local files I'm open to it.
     
  2. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,736
    Does your application have permissions to read that file? If it's a file on disk, try reading it using File.ReadAllBytes().
     
    Bunny83 likes this.
  3. nilsdr

    nilsdr

    Joined:
    Oct 24, 2017
    Posts:
    374
    So how is previewsong constructed? It should use the streaming assets path (assuming its in the build)
     
  4. TimTro

    TimTro

    Joined:
    Jun 1, 2015
    Posts:
    20
    @Aurimas-Cernius thanks, my AndroidManifest.xml has the necessary user permissions as far as I know.

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    I looked into using File.ReadAllBytes() but it looks like I'd have to convert the byte array to an AudioClip and there seems to be significant differences in the byte arrays of MP3s and WAVs.

    I thought UnityWebRequestMultimedia.GetAudioClip was built for this purpose. I would much prefer to use this method than convert a byte array but if I have to go that route it's fine. The biggest hurdle for me there is finding documentation that outlines the process.

    @nilsdr the file is on the user's device. I am retrieving the absolute path using an Android file picker solution.
     
    DavidZobrist likes this.
  5. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,736
    I mentioned File.ReadAllBytes() for testing purposes. If you can read file that way, then the path is correct and it's a matter of constructing the correct URI for UnityWebRequest.
     
  6. TimTro

    TimTro

    Joined:
    Jun 1, 2015
    Posts:
    20
    @Aurimas-Cernius that makes sense. File.Exists returns true on Android when I plug in the file path. What issues could I be running into with my URI?

    Thanks!
     
  7. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,736
    Maybe spaces in path have something to do with it. Try creating System.Uri object passing the path to constructor and then pass that object to UnityWebRequest.
     
  8. TimTro

    TimTro

    Joined:
    Jun 1, 2015
    Posts:
    20
    Thanks @Aurimas-Cernius just tried passing the path to a System.Uri object and I'm running into the same issue, http:/1.1 404 not found.

    This seems like an issue specific to my app's settings. I downloaded the DJ PRO plugin from the Asset Store because the demo worked on my phone but when I implement the same plugin on my app with the same song and song path I get an http:/1.1 404 not found error. Could this be an issue with the Android Manifest? Permissions? Something else? Seems strange.
     
  9. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,736
    Have tried reading the file via File.ReadAllBytes? If that function reads the file correctly, UnityWebRequest should too (and it's a bug if it doesn't).
     
  10. TimTro

    TimTro

    Joined:
    Jun 1, 2015
    Posts:
    20
    DavidZobrist likes this.
  11. DavidZobrist

    DavidZobrist

    Joined:
    Sep 3, 2017
    Posts:
    234
    I got the same issue.
    I did not attempt to use the manifest solution above because a permission check just before returns true for both read and write on external storage.

    Code (CSharp):
    1.   Debug.Log("ExternalStorageRead " + Permission.HasUserAuthorizedPermission(Permission.ExternalStorageRead));
    2.         Debug.Log("ExternalStorageWrite " + Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite));

    I have space in my fileNames but this works fine in the editor on the local machine.
    But it does not on android ( pico neo 3)

    Saved location:
    Code (CSharp):
    1.   /storage/emulated/0/Android/data/com.name.name/files/PsyCurioSaves/3/userCreatedContant_audio/Art_11 (1)patientID3themeKey0recording.wav
    2.  

    Uri / Url
    Code (CSharp):
    1.  
    2. http://localhost/storage/emulated/0/Android/data/com.name.name/files/PsyCurioSaves/3/userCreatedContant_audio/Art_11 (1)patientID3themeKey0recording.wav
    3.  
    maybe it doesnt like the explicit http://localhost/ ? ( added by the webrequest)


    I do read images from the disk via
    File.ReadAllBytes with success aswell.

    I cant do ReadAllBytes with the .wav files as I dont know how to convert them into .wav after having the bytes. Thats what the web request is for.
     
    Last edited: Oct 26, 2022
  12. DavidZobrist

    DavidZobrist

    Joined:
    Sep 3, 2017
    Posts:
    234
    Thats my request code:
    ( which works for the editor / local machine)

    Code (CSharp):
    1.  Debug.Log("start laoding in");
    2.         if (File.Exists(audioFilePath))
    3.         {
    4.        
    5.             using (UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(audioFilePath, AudioType.WAV))
    6.             {
    7.                 yield return request.SendWebRequest();
    8.  
    9.                 if (request.isNetworkError)
    10.                 {
    11.                     Debug.Log("www.error " + request.error);
    12.                     Debug.Log(" www.uri " + request.uri);
    13.                     Debug.Log(" www.url " + request.url);
    14.                     Debug.Log(" www.result " + request.result);
    15.                 }
    16.                 else
    17.                 {
    18.                     AudioClip myClip = DownloadHandlerAudioClip.GetContent(request);
    19.                     audioSource.clip = myClip;
    20.                     Debug.Log("done");
    21.                 }
    22.             }
    23.         }
     
  13. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,736
    UnityWebRequest expects URI, not path on a disk.
     
  14. DavidZobrist

    DavidZobrist

    Joined:
    Sep 3, 2017
    Posts:
    234
    @Aurimas-Cernius
    thanks! I dont have much experience with that.

    I do create an uri like this now and test:
    Code (CSharp):
    1. System.Uri _uri = new Uri(audioFilePath);
    and i pass it over to:
    Code (CSharp):
    1. UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(_uri, AudioType.WAV))
    This attempt results in path starting with something like file:/// ..
    and remains working locally on the machine (pc).
    So I will test on the device again soon.

    I always have to build to the device to test this (40 minutes) so it is pretty time intensive..
     
    hotchick likes this.
  15. DavidZobrist

    DavidZobrist

    Joined:
    Sep 3, 2017
    Posts:
    234
    that worked

    thanks :D