Unity C# Add Song on runtime then play that song - c#

I am trying to allow the user to Import a song using a file browser Add on from the unity store. Currently I am trying to just play the song in the menu, just to see if I have loaded it correctly but it seems I cannot get past this stage. The WWW function makes no sense to me and I will need someone to explain it very simply. In my eyes this should open the file browser, the user then selects that file, then the selected file should be loaded into the audio clip. then the debug play button should play that audio clip, but from pausing the game I can see that the file browser finds the file and has the correct string but the audio is never assigned to the clip correctly. I am using .wav and .ogg files so no problem with formats.
public FileBrowser fb = new FileBrowser();
public bool toggleBrowser = true;
public string userAudio;
public AudioSource aSource;
public AudioListener aListener;
public AudioClip aClip;
void Start()
{
if (aSource == null)
{
aSource = gameObject.AddComponent<AudioSource>();
aListener = gameObject.AddComponent<AudioListener>();
}
}
void OnGUI()
{
// File browser, cancel returns to menu and select chooses music file to load
if (fb.draw())
{
if (fb.outputFile == null)
{
Application.LoadLevel("_WaveRider_Menu");
}
else
{
Debug.Log("Ouput File = \"" + fb.outputFile.ToString() + "\"");
// Taking the selected file and assigning it a string
userAudio = fb.outputFile.ToString();
}
}
}
public void playTrack()
{
//assigns the clip to the audio source and plays it
aSource.clip = aClip;
aSource.Play();
}
IEnumerator LoadFilePC(string filePath)
{
filePath = userAudio;
print("loading " + filePath);
//Loading the string file from the File browser
WWW www = new WWW("file:///" + filePath);
//create audio clip from the www
aClip = www.GetAudioClip(false);
while (!aClip.isReadyToPlay)
{
yield return www;
}
}
}

There are a few things that may be causing you some trouble.
I'm not sure how exactly the file browser add-on works, but in the code you posted the PlayTrack() function is never called, and the LoadFilePC coroutine is never started. Even if you're loading the file correctly, it's never being assigned to the AudioSource or playing.
Try adding "playTrack();" at the end of your coroutine, and starting it using the MonoBehaviour.StartCoroutine() function after the file is selected.

Related

Unity Android load local MP3 or WAV as AudioClip

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.
void Start() {
audio = GetComponent<AudioSource>();
string fullPath = Path.Combine("file://" + previewSong);
StartCoroutine(GetAudioClip(fullPath));
}
IEnumerator GetAudioClip(string fullPath)
{
using (var uwr = UnityWebRequestMultimedia.GetAudioClip(fullPath, AudioType.MPEG))
{
((DownloadHandlerAudioClip)uwr.downloadHandler).streamAudio = true;
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError)
{
debugSongPath2.text = uwr.error;
yield break;
}
DownloadHandlerAudioClip dlHandler = (DownloadHandlerAudioClip)uwr.downloadHandler;
if (dlHandler.isDone)
{
audio.clip = dlHandler.audioClip;
if (audio.clip != null)
{
audio.clip = DownloadHandlerAudioClip.GetContent(uwr);
Debug.Log("Playing song using Audio Source!");
}
else
{
Debug.Log("Couldn't find a valid AudioClip.");
}
}
else
{
Debug.Log("The download process is not completely finished.");
}
}
}
The errors I experience vary depending on how I form the start of the URL.
Path.Combine("file:/" + previewSong);
malformed URL
Path.Combine("file://" + previewSong);
http:/1.1 404 not found
Path.Combine("file:///" + previewSong);
unknown error
Path.Combine("file:////" + previewSong);
unknown error
When I output the URLs on my phone they look correct. Here's an example path:
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.
Change your build settings to give write access to external sd card. I am using below code to get audio from specified location in android. I tried your code but somehow its not working.
IEnumerator GetAudioClip2(string fullPath)
{
debugSongPath2.text = previewSong;
using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(fullPath, AudioType.MPEG))
{
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.Log(www.error);
}
else
{
AudioClip myClip = DownloadHandlerAudioClip.GetContent(www);
audioSource.clip = myClip;
audioSource.Play();
}
}
}
My full path was "file:///storage/emulated/0/Samsung/Music/Over the Horizon.mp3"
The issue was with the new Android 11 scoped storage which disables READ_EXTERNAL_STORAGE. I had to add the following to my AndroidManifest.xml:
<manifest ... >
<!-- This attribute is "false" by default on apps targeting Android 10 or higher. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
https://developer.android.com/training/data-storage/use-cases#opt-out-scoped-storage

Playing AudioClip fetched from Resources folder

I need to fetch the audioClip in the resources folder so as I understand I need to use Resources.Load<AudioClip>(path) or Resources.Load(path) as AudioClip but this just doesn't work, it doesn't even return a null it just stops the code
my code sample:
private void FetchAudioClipAndStartPlay(int userPos, int index)
{
AudioClip clip = Resources.Load($"Audio/Demo_ENG/D{userPos}a") as AudioClip;
Debug.Log("Starting Coroutine " + index);
StartCoroutine(PlayAudioClipAndStartRetrievingFromDatabase(index, clip));
}
IEnumerator PlayAudioClipAndStartRetrievingFromDatabase(int index, AudioClip clip)
{
Debug.Log("Starting to play " + index);
audioSource.PlayOneShot(clip, 1f);
yield return new WaitForSeconds(clip.length);
if (index < numberOfPlayers)
{
RetrieveFromDatabase(index++);
}
}
(it doesn't enter the coroutine)
All the audio files are .mp3
Any guidance and tips are very appreciated!
Unity does not support loading mp3 at runtime.
Unity does not recommend using the Resources folder:
Don't use it.
This strong recommendation is made for several reasons:
Use of the Resources folder makes fine-grained memory management more
difficult Improper use of Resources folders will increase application
startup time and the length of builds
As the number of Resources
folders increases, management of the Assets within those folders
becomes very difficult
The Resources system degrades a project's
ability to deliver custom content to specific platforms and
eliminates the possibility of incremental content upgrades
AssetBundle Variants are Unity's primary tool for adjusting content
on a per-device basis
Check this answer for a solution how to load audio files:
public void LoadSong()
{
StartCoroutine(LoadSongCoroutine());
}
IEnumerator LoadSongCoroutine()
{
string url = string.Format("file://{0}", path);
WWW www = new WWW(url);
yield return www;
song.clip = www.GetAudioClip(false, false);
songName = song.clip.name;
length = song.clip.length;
}

Where to put media resource and start media player?

I want to play a song in a Xamarin app using the standard android media player.
I've got two questions that i haven't been able to answer myself:
Here do I put the media file (.mp3-file) or how do i link to the file (it's also saved on my SD-Card) I tried saving it in the raw-folder, but that doesn't seem to work
How do I correctly launch the player? Either my code doesn't work or the player doesn't find the file because it isn't stored correctly.
However, that's the method I use to prepare the media player:
protected MediaPlayer player;
public void StartPlayer() {
if (player == null) {
player = new MediaPlayer();
player.SetDataSource("Ressources.raw.test");
player.Prepare();
player.Start();
text = "Running";
} else {
player.Reset();
player.SetDataSource("Ressources.raw.test");
player.Prepare();
player.Start();
}
}
I tried saving it in the raw-folder, but that doesn't seem to work
You can't directly use string "Ressources.raw.test" as the DataSource. If you need play a song from Resource/Raw folder, here is two solutions.
Solution 1:
You could try using MediaPlayer.Create(Context context, int resid)
method:
Convenience method to create a MediaPlayer for a given resource id. On success, prepare() will already have been called and must not be called again.
Usage:
MediaPlayer player = MediaPlayer.Create(this, Resource.Raw.test);
player.Start();
Solution 2:
Convert your Resource/Raw/test path to Android.Net.Uri:
player = new MediaPlayer();
Android.Net.Uri uri = Android.Net.Uri.Parse("android.resource://" + PackageName + "/" + Resource.Raw.test);
player.SetDataSource(this, uri2);
player.Prepare();
player.Start();
how do I link to the file (it's also saved on my SD-Card)
Solution:
Try using SetDataSource (Context context, Uri uri) method, for example:
player = new MediaPlayer();
//My file path is Path == file:///storage/emulated/0/netease/cloudmusic/Music/love.MP3
var uri = Android.Net.Uri.Parse("file://" + Android.OS.Environment.ExternalStorageDirectory + "/netease/cloudmusic/Music/love.MP3");
player.SetDataSource(this, uri);
player.Prepare();
player.Start();

Unity Asset Bundles

I am creating an editor for a game using Unity game engine and I want to allow a designer to make new spells. What I am trying to achieve is that a designer will download a Unity particle system (or GameObject), save it on the computer and will give the game full path to it. Then the game should load the object and use it as a particle for a new spell.
I need to be able to load a Unity GameObject (particle system) during runtime from a folder. I tried it with Asset Bundles, but I don’t know how to use Asset Bundles when I only want to download the file from the computer (local file system) not from the server. Asset bundles work perfectly in Debug but not when I build and run it (It tries to connect to some server instead).
I use the default script for Asset Bundles:
public class LoadAssets : MonoBehaviour
public const string AssetBundlesOutputPath = "/AssetBundles/";
public string assetBundleName;
public string assetName;
public GameObject loadedObject;
public bool finished;
public MessageWindow messWindow;
// Use this for initialization
//originally was start here might create problems
public IEnumerator Begin ()
{
messWindow.changeMessageAndShow("Asset " + assetName + " is loading.","Loading");
yield return StartCoroutine(Initialize() );
// Load asset.
yield return StartCoroutine(InstantiateGameObjectAsync (assetBundleName, assetName) );
finished=true;
messWindow.Close();
}
public GameObject LoadObject()
{
StartCoroutine(Begin());
//TODO wait for end of couroutine
return loadedObject;
}
// Initialize the downloading url and AssetBundleManifest object.
protected IEnumerator Initialize()
{
// Don't destroy this gameObject as we depend on it to run the loading script.
DontDestroyOnLoad(gameObject);
// With this code, when in-editor or using a development builds: Always use the AssetBundle Server
// (This is very dependent on the production workflow of the project.
// Another approach would be to make this configurable in the standalone player.)
#if DEVELOPMENT_BUILD || UNITY_EDITOR
AssetBundleManager.SetDevelopmentAssetBundleServer ();
#else
// Use the following code if AssetBundles are embedded in the project for example via StreamingAssets folder etc:
// Or customize the URL based on your deployment or configuration
//AssetBundleManager.SetSourceAssetBundleURL("http://www.MyWebsite/MyAssetBundles");
#endif
AssetBundleManager.SetSourceAssetBundleURL(Application.dataPath + "/");
// Initialize AssetBundleManifest which loads the AssetBundleManifest object.
var request = AssetBundleManager.Initialize();
if (request != null)
yield return StartCoroutine(request);
}
protected IEnumerator InstantiateGameObjectAsync (string assetBundleName, string assetName)
{
// This is simply to get the elapsed time for this phase of AssetLoading.
float startTime = Time.realtimeSinceStartup;
// Load asset from assetBundle.
AssetBundleLoadAssetOperation request = AssetBundleManager.LoadAssetAsync(assetBundleName, assetName, typeof(GameObject) );
if (request == null)
yield break;
yield return StartCoroutine(request);
// Get the asset.
GameObject prefab = request.GetAsset<GameObject> ();
if (prefab != null)
{
loadedObject = GameObject.Instantiate(prefab);
DontDestroyOnLoad(loadedObject);
}
// Calculate and display the elapsed time.
float elapsedTime = Time.realtimeSinceStartup - startTime;
Debug.Log(assetName + (prefab == null ? " was not" : " was")+ " loaded successfully in " + elapsedTime + " seconds" );
}
}
Thank you so much.
To load Asset bundle from local system you need Local AssetBundle Server. When Local Asset Server is enabled, AssetBundles must be built and placed in a folder explicitly called AssetBundles in the root of the Project, which is on the same level as the Assets folder.
The built AssetBundles will be available to the Editor and all builds running locally that can reach the Editor on the local network.
Use Assets>AssetBundles>Local AssetBundle Server to enable the Local AssetBundle Server.
Reference to asset bundles and asset bundle manager.

Unity 5.3 Assetbundles

I am using the AssetBundleManager API in my unity 5.3 project.
My set-up: I have two scenes. One main scene and another scene that will be loaded as a multi scene into the main scene, which works fine in the editor right now.
Now I wanted to built my main scene to my android device, but when I click on the load level button, nothing happens. I already made a built of my assetbundle which is placed in the AssetBundles folder of my root. This is what I read in the documentations and what should be right?
What am I missing, do I need to put the assetbundle files anywhere else?
I am using this script, which I got from the example of the AssetBundleManager Plugin.
using UnityEngine;
using System.Collections;
using AssetBundles;
using UnityEngine.SceneManagement;
public class LoadScenes : MonoBehaviour
{
public string sceneAssetBundle;
public string sceneName;
public bool load;
public bool destroy;
string curLevel;
// Use this for initialization
IEnumerator Start ()
{
yield return StartCoroutine(Initialize() );
// Load level.
//yield return StartCoroutine(InitializeLevelAsync (sceneName, true) );
}
void Update(){
if (load) {
load = false;
// Load level.
StartCoroutine(InitializeLevelAsync (sceneName, true) );
}
if (destroy) {
destroy = false;
SceneManager.UnloadScene(sceneName);
}
}
public void loadLevel(string level){
curLevel = level;
StartCoroutine (InitializeLevelAsync (level, true));
}
public void unloadLevel(){
SceneManager.UnloadScene(curLevel);
}
// Initialize the downloading url and AssetBundleManifest object.
protected IEnumerator Initialize()
{
// Don't destroy this gameObject as we depend on it to run the loading script.
DontDestroyOnLoad(gameObject);
// With this code, when in-editor or using a development builds: Always use the AssetBundle Server
// (This is very dependent on the production workflow of the project.
// Another approach would be to make this configurable in the standalone player.)
#if DEVELOPMENT_BUILD || UNITY_EDITOR
AssetBundleManager.SetDevelopmentAssetBundleServer ();
#else
// Use the following code if AssetBundles are embedded in the project for example via StreamingAssets folder etc:
AssetBundleManager.SetSourceAssetBundleURL(Application.dataPath + "/");
// Or customize the URL based on your deployment or configuration
//AssetBundleManager.SetSourceAssetBundleURL("http://www.MyWebsite/MyAssetBundles");
#endif
// Initialize AssetBundleManifest which loads the AssetBundleManifest object.
var request = AssetBundleManager.Initialize();
if (request != null)
yield return StartCoroutine(request);
}
protected IEnumerator InitializeLevelAsync (string levelName, bool isAdditive)
{
// This is simply to get the elapsed time for this phase of AssetLoading.
float startTime = Time.realtimeSinceStartup;
// Load level from assetBundle.
AssetBundleLoadOperation request = AssetBundleManager.LoadLevelAsync(sceneAssetBundle, levelName, isAdditive);
if (request == null)
yield break;
yield return StartCoroutine(request);
// Calculate and display the elapsed time.
float elapsedTime = Time.realtimeSinceStartup - startTime;
Debug.Log("Finished loading scene " + levelName + " in " + elapsedTime + " seconds" );
}
}
Instead of using:
AssetBundleManager.SetSourceAssetBundleURL(Application.dataPath + "/");
Try using Application.persistentDataPath, hopefully that'll solve the problem.
Link to the manual.
You will have to set the datapath to your folder where the assetbundle is, i.e myassetbundle.unity3d
For example:-
AssetBundleManager.SetSourceAssetBundleURL(Application.dataPath +
"file:////D:\ptech-user\Documents\assetbundle3\Assets\Assets\AssetBundle
");

Categories

Resources