I need to stream or download and play an audio getted from an URL in Unity3D, running on iOS.
The Audio comes from a text-to-audio service and I need to play it on Unity:
http://api.ispeech.org/api/rest?apikey=...&action=convert&voice=eurspanishfemale&text=hola+que+tal
I've been googling all the morning and not found a valid solution...
There is a code snippet in the Unity3D documentation (WWW-audioClip,WWW.GetAudioClip), but is not working, I have debugged and the error says it couldn't open the file.
using UnityEngine;
using System.Collections;
public class AudioURLScript : MonoBehaviour {
public string url = "http://api.ispeech.org/api/rest?apikey=...&action=convert&voice=eurspanishfemale&text=hola+que+tal";
public AudioSource source;
void Start() {
WWW www = new WWW("file://"+url);
source = GetComponent<AudioSource>();
source.clip = www.GetAudioClip(false,true);
}
void Update() {
if (!source.isPlaying && source.clip.isReadyToPlay)
source.Play();
}
}
Thanks
SOLUTION
This is my working solution right now.
void Start(){
StartCoroutine(DownloadAndPlay("http://api.ispeech.org/api/rest?apikey=...&action=convert&voice=eurspanishfemale&text=Hola+que+tal"));
}
IEnumerator DownloadAndPlay(string url)
{
WWW www = new WWW(url);
yield return www;
AudioSource audio = GetComponent<AudioSource>();
audio.clip = www.GetAudioClip(false, true,AudioType.MPEG);
audio.Play();
}
You don't mention the platform you are on, so I'm going to assume Windows.
Unity Windows runtime only supports WAV or OGG. The link to the audio service file you provided is downloading as a MP2 Audio File (common in broadcasting). Unity will not be able to play that (or MP3).
For reference, Android and iOS platforms do support MP3 (but not MP2).
So, you're first issue is to make sure your audio source is in compatible format.
The code sample is incorrect for 3 reasons;
the URL is https: (which tells unity to download from the internet) but then you pre-pend file: to it (which tells unity to load from the local file system). So you should pick one or the other.
it wouldn't work if you picked https because that particular link (I know it's just an example) but it requires a user to be logged in to the service (and using cookies to know that), so it doesn't send you an audio file, it sends you a HTML page telling the user to login or register.
As #fafase says, the WWW must be placed within a co-routine, so that it can download over multiple frames.
OK, here's what I suggest.
If you can know the audio files a head of time, download them and transcode to OGG (if windows) or MP3 (if mobile) and upload them to your own server (say Amazon S3, or a $10 a month unlimited website).
Then, use this code to download and play it:
StartCoroutine(DownloadAndPlay("http://myserver.com/audio/test.ogg"));
IEnumerator DownloadAndPlay(string url)
{
WWW www = new WWW(url);
yield return www;
AudioSource audio = GetComponent<AudioSource>();
audio.clip = www.GetAudioClip(false, false);
audio.Play();
}
A WWW object is a wrapper for a HTTP request, containing the creation of the connection, the transfer of the data and the closing of the connection (and some extra actions).
This does not happen in one frame and requires a coroutine.
void Start() {
StartCoroutine(GetAudio(url));
}
private IEnumerator GetAudio(string url)
{
WWW www = new WWW("file://"+url);
yield return www;
if(string.IsNullOrEmpty(www.error) == false)
{
Debug.Log("Did not work");
yield break;
}
source = GetComponent<AudioSource>();
source.clip = www.GetAudioClip(false,true);
}
The WWW package got deprecated and Unity3D recommended using the UnityWebRequest package for API communications.
Try the following code snippet to download audio from a URL and play it using the AudioSource gameObject.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class DownloadAndPlay : MonoBehaviour
{
public AudioSource audioSource;
// Start is called before the first frame update
void Start()
{
string URL = "http://api.ispeech.org/api/rest?apikey=...&action=convert&voice=eurspanishfemale&text=hola+que+tal";
StartCoroutine(DownloadAudio(URL));
}
// Update is called once per frame
void Update()
{
}
IEnumerator DownloadAudio(string URL) {
using (UnityWebRequest www_audio = UnityWebRequestMultimedia.GetAudioClip(URL, AudioType.MPEG)) {
yield return www_audio.SendWebRequest();
if (www_audio.isNetworkError) {
Debug.Log(www_audio.error);
} else {
AudioClip clip = DownloadHandlerAudioClip.GetContent(www_audio);
audioSource.clip = clip;
audioSource.Play();
}
}
}
}
For more: https://docs.unity3d.com/ScriptReference/Networking.UnityWebRequest.html
Related
I'm making a game for webgl. But when i upload the assetbundles on the link and then loading it in the game it show error.
error:
Failed to decompress data for the AssetBundle 'Memory'.
UnityEngine.WWW:get_assetBundle()
NullReferenceException: Object reference not set to an instance of an object
DownloadScript+d__2.MoveNext () (at Assets/DownloadScript.cs:27)
When i load asset from my system(local), it load it perfectly. But when i upload it on a live link it is not loading it.
This is my code to load it from server
'''
public class DownloadScript : MonoBehaviour
{
public string url;
// Start is called before the first frame update
[System.Obsolete]
void Start()
{
StartCoroutine(DownloadModel());
}
[System.Obsolete]
IEnumerator DownloadModel()
{
WWW wwws = new WWW(url);
yield return wwws;
//UnityWebRequest www = UnityWebRequest.GetAssetBundle();
//yield return www.SendWebRequest();
AssetBundle assetBundle = wwws.assetBundle;
Instantiate(assetBundle.LoadAsset("cube"));
}
'''
Code to load from local system:
'''
AssetBundle myLoadedAssetbundle;
public string path;
public string bundleAsset;
void Start()
{
LoadAssetBundle(path);
//InstantiateObjectFromBundle(bundleAsset);
StartCoroutine(DownloadAndCache(path));
}
void LoadAssetBundle(string bundleUrl)
{
myLoadedAssetbundle = AssetBundle.LoadFromFile(bundleUrl);
//myLoadedAssetbundle = AssetBundle.
Debug.Log(myLoadedAssetbundle == null ? "Failed to load AssetBundle" : "AssetBundle Succesfully Loaded");
}
void InstantiateObjectFromBundle(string assetName)
{
var prefab = myLoadedAssetbundle.LoadAsset(assetName);
Instantiate(prefab);
}
'''
Are you sure you are also building your assetbundles with the right platform in mind? Assetbundles built for desktop wont necessarily work for webgl.
I'm trying to build a connection between the HoloLens and XAMMP server.
when i test the app on unity, it works fine but it doesn't work on the device.
Ps: you can access the server when writing the IP address of the server In Microsoft Edge.
Windows defender firewall is disabled
Network capabilities are checked (publishing settings)
XAMPP server is reconfigured to be accessible to all devices
what am I missing here ? how can I make this work ?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class test : MonoBehaviour
{
public Text playerDisplay;
public Text score;
public Button submit;
public InputField nameField;
public GameObject bb;
public Text Error;
public void CallLogIn()
{
StartCoroutine(Loginplayer());
}
IEnumerator Loginplayer()
{
WWWForm form = new WWWForm();
form.AddField("name", nameField.text);
WWW www = new WWW("http://192.168.1.100/sqlconnect/test.php", form);
yield return www;
if (www.text[0] == '0')
{
DBManager.username = nameField.text;
DBManager.score = int.Parse(www.text.Split('\t')[1]);
playerDisplay.text = "player: " + DBManager.username;
score.text = "score: " + DBManager.score;
bb.SetActive(true);
}
else
{
Error.text = "save failes. Error #" + www.error;
bb.SetActive(false);
}
DBManager.logedOut();
}
}
I encountered this kind of issue as well. I tried to connect the HoloLens to different entities (MySQL DB, OPCUA Server) but it always fails on the HoloLens (working fine in the editor). Searching on google only revealed similar problems from other people, but no solutions.
So my solution is to use the WWW class to receive data from a HTML-Webserver that hosts plain text. This server (I used nodejs and python) executes the query to the database depending on the GET-request it receives from the HoloLens.
I'm using Unity 5 and C# and I would like to load a movie from anywhere on disk at run-time rather than from the Resources folder.
This is possible to do with regular image textures using the .LoadImage (bytes) method in Texture2D type for example, but there does not appear to be an equivalent for MovieTexture.
The code shows how to do it from the resources folder, but how could i modify it to load from disk?
Thanks
public void loadVideo (GameObject container, string video)
{
// Load MovieTexture from Resources Folder
MovieTexture mat = (MovieTexture)Resources.Load(Path.GetFileNameWithoutExtension(video), typeof(MovieTexture));
MeshRenderer ren = container.GetComponent<MeshRenderer>();
ren.material.mainTexture = mat;
mat.Play();
}
You can. Use the WWW class to to this.
IEnumerator loadAndPlay(){
WWW diskMovieDir = new WWW("file:///C:/yourvideoDir/video.mp4");
//Wait for file finish loading
while(!diskMovieDir.isDone){
yield return null;
}
//Save the loaded movie from WWW to movetexture
MovieTexture movieToPlay = diskMovieDir.movie;
//Hook the movie texture to the current renderer
MeshRenderer ren = container.GetComponent<MeshRenderer>();
ren.material.mainTexture = movieToPlay ;
movieToPlay.play();
}
Now you can call it by starting coroutine.
StartCoroutine(loadAndPlay());
I did not compile this so there might be a compile error but you can easily fix it if there is one. This should also work without problems if it compiles. Note that the url must start with file:///. If that didnt work, try file://
I am having issue with the asset bundle from unity. I have a bundle with multiple assets in it with the same name, but different extension, like bonus.png bonus.prefab.
When I try to instanciate a prefab named bonus, the assetBundle.LoadAssets("bonus") function doesnt return me a gameobject like it usuallly do. This seems to be happening only if i have multiple asset with the same name.
AssetBundleRequest request = assetBundle.LoadAssetAsync(m_ObjectsToPool[i].m_Name, typeof(GameObject));
yield return request;
GameObject prefab = request.asset as GameObject;
if (prefab != null)
{
PoolManager.Instance.AddLoadedPrefabToPool(prefab, m_ObjectsToPool[i].m_Name, null, m_ObjectsToPool[i].m_Amount);
}
But if i call loadallassets(typeof(GameObject)) and then use a for loop, i can find my asset and instanciante it properly. but that is just a dirty way of doing it.
You should be able to load assets of different types with the same name.
What version of Unity are you using?
Your code above looks correct (except for the typo calling it LoadAssetAsync?), and we don't know for sure it is failing to enter the if() statement?
The following worked for me on Unity 4.5.5f1
Added ExportAssetBundles.cs context menu script to my Unity build from Unity Docs
Created something to export
found a JPG dragged it in and named it "marble.jpg"
created a cube, named it "marble" and dragged it into project to create a prefab
created a material, named it "marble", added the jpg and dragged it onto the cube, pressed apply.
Hilighted all 3 and exported asset bundle using "Build Assets From Selection - Track Selection" and saved it to "c:\temp\marble.unity3d"
Added a script to load that asset bundle on start (similiar to yours):
using UnityEngine;
using System.Collections;
public class BundleTest : MonoBehaviour {
void Start () {
StartCoroutine(StartCo());
}
IEnumerator StartCo() {
string url = "file://c:\\temp\\marble.unity3d";
WWW www = new WWW(url);
yield return www;
AssetBundle bundle = www.assetBundle;
AssetBundleRequest request = bundle.LoadAsync ("marble", typeof(GameObject));
yield return request;
Debug.Log ("loaded asset: " + request.asset.ToString());
GameObject prefab = request.asset as GameObject;
GameObject go = (GameObject)Instantiate(prefab);
bundle.Unload(false);
www.Dispose();
}
}
Ran Unity and verified I see the game object correctly.
It used to work when I was on unity 4.6, but I am now using the beta 5.0.17. And no its not a typo, it is really laodassetasync I have, I also tried with loadasset only with the same result. Right now I am using
var allAssets = assetBundle.LoadAllAssets(typeof(GameObject));
for (int i = 0; i < m_ObjectsToPool.Count; i++)
{
bool found = false;
for (int j = 0; j < allAssets.Length; j++)
{
if (allAssets[j].name == m_ObjectsToPool[i].m_Name)
{
PoolManager.Instance.AddLoadedPrefabToPool((GameObject)allAssets[j], m_ObjectsToPool[i].m_Name, null, m_ObjectsToPool[i].m_Amount);
found = true;
break;
}
}
if (!found)
{
Debug.Log(string.Format("{0} object could not be found in assetbundle", m_ObjectsToPool[i].m_Name));
}
}
and it is working, but i dont really like this way of doing it.
Having same issue with Unity 5.1, I've found this solution:
Besides refering asset by base name (no directory, no extension parts of path), you can also use full relative path (including file extension, relative to project root).
Let's say I've got bundle created from directory Assets/Bundle, it contains following files:
Prefabs/Box.prefab
Prefabs/Box2.prefab
Models/Box.fbx
Materials/Box.mat
Textures/Box.png
So the documented way of loading prefab that fails in our cases is
// May return nulll
bundle.LoadAsset<GameObject>("Box")
And this is the not documented way that's working correctly in our case
bundle.LoadAsset<GameObject>("assets/bundle/prefabs/box.prefab")
If you are not sure what is the correct asset name you should use for LoadAsset, you can use following code to list all asset names in bundle
string[] assets = bundle.GetAllAssetNames();
foreach (string asset in assets)
{
Debug.Log(asset);
}
I'm using a package called Visualizer Studio (http://www.alteredr.com/visualizer-studio/) to make a game where objects react to music. My goal is to have the user be able to load a song at any time during the game. It's going to be standalone/PC, not WebPlayer.
My problem is that Visualizer Studio takes the audio data from an Audio Source in the scene. So when I use NAudio to load and stream MP3s, visualizer studio doesn't hear them, and my objects don't react to the music.
I'm using IWavePlayer right now in my code. I've tried adding audio.clip = www.GetAudioClip and similar functions to have the audio clip load the music that is being played, but to no avail.
I got the code I'm using to select and stream .mp3s from this blog post (the source code is at the bottom): (http://denis-potapenko.blogspot.com/2013/04/task-6-loading-mp3-audio-via-www-class.html). It's unchanged at the moment, because nothing I was trying was working.
To be clear, the .mp3s DO play when I select them from a file browser. I just need them to play via an audio source in my scene. Please, there has to be a way to do this. I've contacted visualizer studio support and asked on the unity forums, but nobody can help so far.
Please keep in mind that I'm not exactly a great programmer, so you might have to dumb answers down for me. Thanks for you patience in advance. Anyway,
Here's my code I'm using to open a file browser and stream audio:
using UnityEngine;
using System.Collections;
using System.IO;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using NAudio;
using NAudio.Wave;
public class AppRoot : MonoBehaviour
{
///////////////////////////////////////////////////////////////////////////
#region Variables
private static AppRoot mInstance = null;
private const string cLocalPath = "file://localhost/";
private IWavePlayer mWaveOutDevice;
private WaveStream mMainOutputStream;
private WaveChannel32 mVolumeStream;
#endregion
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
#region Interface
public AppRoot()
{
mInstance = this;
}
public void Start()
{
}
public void Update()
{
if(Input.GetKeyDown(KeyCode.F))
{
UnloadAudio();
LoadAudio();
}
}
public void OnGUI()
{
if (GUI.Button(new Rect(100, Screen.height - 200 + 100, Screen.width - 200, 35), "Load audio"))
{
UnloadAudio();
LoadAudio();
}
}
public void OnApplicationQuit()
{
UnloadAudio();
}
#endregion
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
#region Implementation
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();
}
}
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;
}
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;
}
}
#endregion
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
#region Properties
private static AppRoot Instance
{
get
{
return mInstance;
}
}
#endregion
///////////////////////////////////////////////////////////////////////////
}
Sincerely,
Jonathan
http://answers.unity3d.com/answers/1128204/view.html
With the "latest" version of NAudio.dll and NAudio.WindowsMediaFormat.dll inserted into your Resources folder utilize this code to do what you described:
var musicInput : GameObject;
private var aud : AudioFileReader;
private var craftClip : AudioClip;
private var AudioData : float[];
private var readBuffer : float[];
private var soundSystem : AudioSource;
private var musicPath : String[];
//Check if there's a pref set for the music path. Use it AND add all the files from it
function CheckMusic()
{
var pathname = musicInput.GetComponent.<InputField>();
if(PlayerPrefs.HasKey("musicpath") == false)
{
PlayerPrefs.SetString("musicpath", "Enter Music Directory");
}
else
{
pathname.text = PlayerPrefs.GetString("musicpath");
musicPath = Directory.GetFiles(PlayerPrefs.GetString("musicpath"),"*.mp3");
}
}
function LoadSong(songToPlay : int)
{
//Download the song via WWW
var currentSong : WWW = new WWW(musicPath[songToPlay]);
//Wait for the song to download
if(currentSong.error == null)
{
//Set the title of the song
playingSong.text = Path.GetFileNameWithoutExtension(musicPath[songToPlay]);
//Parse the file with NAudio
aud = new AudioFileReader(musicPath[songToPlay]);
//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, aud.Length);
//Create a clip file the size needed to collect the sound data
craftClip = AudioClip.Create(Path.GetFileNameWithoutExtension(musicPath[songToPlay]),aud.Length,aud.WaveFormat.Channels,aud.WaveFormat.SampleRate, false);
//Fill the file with the sound data
craftClip.SetData(AudioData,0);
//Set the file as the current active sound clip
soundSystem.clip = craftClip;
}
}
And I quote
The "songToPlay" variable that is passed to the function is a simple int that is acquired from the array created under the CheckMusic function. I search a chosen directory entered from a GUI Inputfield for a specific file type (MP3) which can be changed to WAV or OGG and then input those files to an array. Other code chooses the number of the song on the array to play and you can change that to anything you like. The important part is that the NAudio,dll does all the heavy lifting. All you need to do is use the aud.Read(float[] to send data to, song starting point(usually 0),length of song data (aud.length). The Float[] here is the same length as aud.length so create the float of the same length, read the file, fill the float, create the clip, then dump the float data in with AudioClip.SetData()
Right now this code works and it does the job. Downside is it takes
2-3 seconds to fill the float in this way and is a noticeable drag. It
also tends to chew up memory pretty fast, but it gets the job done.
Hope it helps as a starting point for those who are looking to do
this. I know I needed it.
When you use a WaveOut instance you are playing (relatively) directly on the soundcard, outside of the Unity environment. You need a way to introduce that data into Unity.
I haven't worked with Unity so this might not be the best answer,but...
You can introduce wave data into the scene using OnAudioFilterRead. You can use this to create procedural sound, and with a little coding it can presumably be hooked up to a NAudio wave source. This will introduce the audio data directly into the game for your in-game code to deal with.
Here's an article I found that might help: Procedural Audio with Unity
mWaveOutDevice = new WaveOut();
Maybe causing the issue, it is better practice to use WaveOutEvent() which supports running in all non GUI threads.
mWaveOutDevice = new WaveOutEvent();
Related StackOverFlow Question