获取音频 mp3 文件列表并在 Unity 3d 中播放它们
Get list of audio mp3 files and play them in Unity 3d
我正在尝试构建一个音频播放器,它可以从外部源获取 mp3 文件以及播放、停止、下一首和上一首按钮的逻辑,但我还不知道该怎么做。
http://localhost:8383/Unity3d/Audio/BT - Paul Van Dyk - Namistai.mp3"
http://localhost:8383/Unity3d/Audio/Stevie Wonder - Skeletons.mp3
然后使用 C#,我为 URI 打开一个 UnityWebRequest,将它保存到一个 AudioClip 变量中,然后保存到一个 AudioSource 变量中,但是我发现使用长度 property/saving 到一些某种音频剪辑数组。
下面是我的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Networking;
using UnityEngine.UI;
[RequireComponent(typeof(AudioSource))]
public class AudioManager : MonoBehaviour
{
public AudioClip[] musicClips;
private int currentTrack;
private AudioSource source;
public List<string> externalAudio;
IEnumerator GetAudioClip()
{
externalAudio.Add("http://localhost:8383/Unity3d/Audio/Stevie%20Wonder%20-%20Skeletons.mp3");
externalAudio.Add("http://localhost:8383/Unity3d/Audio/York%20-%20On%20The%20Beach.mp3");
externalAudio.Add("http://localhost:8383/Unity3d/Audio/BT%20-%20Paul%20Van%20Dyk%20-%20Namistai.mp3");
externalAudio.Add("http://localhost:8383/Unity3d/Audio/S.O.S.%20BAND-JUST%20BE%20GOOD%20TO%20ME%20(SINGLE).mp3");
externalAudio.Add("http://localhost:8383/Unity3d/Audio/Michael%20Jackson%20-%20Beat%20It%20(Official%20Video).mp3");
Debug.Log("Hello World");
Debug.Log(UnityWebRequestMultimedia.GetAudioClip("http://localhost:8383/Unity3d/Audio/Stevie%20Wonder%20-%20Skeletons.mp3", AudioType.MPEG));
foreach (var audioClip in externalAudio)
{
Debug.Log(audioClip);
using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(audioClip, AudioType.MPEG))
{
UnityWebRequest request = new UnityWebRequest();
yield return www.SendWebRequest();
if (request.result == UnityWebRequest.Result.ConnectionError)
{
Debug.Log(www.error);
}
else
{
musicClips = DownloadHandlerAudioClip.GetContent(www);
source.clip = musicClips;
Debug.Log("Audio is playing.");
}
}
}
}
// Start is called before the first frame update
void Start()
{
source = GetComponent<AudioSource>();
//PLAY MUSIC
PlayMusic();
}
public void PlayMusic()
{
if (source.isPlaying)
{
return;
}
GetAudioClip();
currentTrack--;
if (currentTrack < 0)
{
currentTrack = 0;
}
StartCoroutine(WaitForMusicEnd());
}
IEnumerator WaitForMusicEnd()
{
while (source.isPlaying)
{
yield return null;
}
NextTitle();
}
public void NextTitle()
{
source.Stop();
currentTrack++;
if (currentTrack > musicClips.Length - 1)
{
currentTrack = 0;
}
source.clip = musicClips[currentTrack];
source.Play();
//Show title
StartCoroutine("WaitForMusicEnd");
}
public void PreviousTitle()
{
source.Stop();
currentTrack--;
if (currentTrack < 0)
{
currentTrack = musicClips.Length - 1;
}
source.clip = musicClips[currentTrack];
source.Play();
//Show title
StartCoroutine("WaitForMusicEnd");
}
public void StopMusic()
{
StopCoroutine("WaitForMusicEnd");
source.Stop();
}
}
如何将外部音频保存到 AudioClip 列表中,然后在 play/stop 函数逻辑中使用它们?
所以这有点宽泛,但这是我可能会做的。
如前所述,让您的服务器提供下载 URL 列表。这样您就不必将它们硬编码到您的客户端中。
对于上一点,您要做的第一件事是一个普通的 GET 请求来接收该 URL 列表,例如简单地用 \n
分隔(换行符)
因此您收到的第一条字符串消息可能类似于
http://localhost:8383/Moonbeam%20Challenge/Audio/Stevie%20Wonder%20-%20Skeletons.mp3
http://localhost:8383/Moonbeam%20Challenge/Audio/York%20-%20On%20The%20Beach.mp3
http://localhost:8383/Moonbeam%20Challenge/Audio/BT%20-%20Paul%20Van%20Dyk%20-%20Namistai.mp3
http://localhost:8383/Moonbeam%20Challenge/Audio/S.O.S.%20BAND-JUST%20BE%20GOOD%20TO%20ME%20(SINGLE).mp3
http://localhost:8383/Moonbeam%20Challenge/Audio/Michael%20Jackson%20-%20Beat%20It%20(Official%20Video).mp3
收到的这份清单将您 Split
放入各个 URL
var urls = urlList.Split('\n');
对于这些,您可以开始您的个人 GetAudioClip
请求。
您作为元素存储在 array/a 列表中的结果。您没有分配整个 musicClips
。我宁愿使用
List<AudioClip> musicClips = new List<AudioClip>();
然后从您的下载中执行
musicClips.Add(DownloadHandlerAudioClip.GetContent(www));
您也不希望下载后立即将每个下载的剪辑分配给 AudioSource 并播放,而是等到所有剪辑都下载完毕(或至少是第一个,具体取决于根据您的需要)
所以这看起来有点像
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Networking;
using UnityEngine.UI;
using System.Linq;
[RequireComponent(typeof(AudioSource))]
public class AudioManager : MonoBehaviour
{
[Header("References")]
[SerializeField]
private AudioSource source;
[Header("Settings")]
[SerializeField] private string baseURL;
[SerializeField] private string[] externalAudio;
[SerializeField] private bool autoStartPlayAfterDownloads = true;
[Header("Debugging")]
[SerializeField] private List<AudioClip> musicClips = new List<AudioClip>();
[SerializeField] private int currentTrack;
[SerializeField] private bool isInitialized;
private Coroutine currentPlayTrack;
// Yes, if you make Start return IEnumerator then Units
// automatically runs it as a Coroutine
private IEnumerator Start()
{
// block input from the outside until this controller is finished with the downloads
isInitialized = false;
if (!source) source = GetComponent<AudioSource>();
// Here either use the approach with first receiving a list from the server
yield return GetAudioClipsInfo();
// OR if you already have the clip urls assigned via the Inspector use one of
yield return GetAudioClipsSequencial();
// OR
yield return GetAudioClipsParallel();
// allow to do things from this point on
isInitialized = true;
if(autoStartPlayAfterDownloads) PlayFirstTitle();
}
// This is the routine that downloads the URL list from the server
// then it starts the individual downloads
private IEnumerator GetAudioClipsInfo()
{
// Make the first request for receiving the list of URLs from the server
// If not using this but already assigning the urls
// via Inspector then simply directly use the "GetAudioClips" below
using (var request = UnityWebRequest.Get(baseURL))
{
yield return request.SendWebRequest();
switch (request.result)
{
case UnityWebRequest.Result.ConnectionError:
case UnityWebRequest.Result.DataProcessingError:
case UnityWebRequest.Result.ProtocolError:
Debug.LogError($"Could not get list of clips! Error: {request.error}", this);
yield break;
}
var urlList = request.downloadHandler.text;
externalAudio = urlList.Split('\n');
// Here you can either go for the sequencial downloads
yield return GetAudioClipsSequencial();
// OR run them all parallel
yield return GetAudioClipsParallel();
}
}
// This version starts one download at a time, waits until it is done
// then starts the next
private IEnumerator GetAudioClipsSequencial()
{
musicClips.Clear();
foreach (var url in externalAudio)
{
Debug.Log(url);
using (var www = UnityWebRequestMultimedia.GetAudioClip(url, AudioType.MPEG))
{
yield return www.SendWebRequest();
switch (www.result)
{
case UnityWebRequest.Result.ConnectionError:
case UnityWebRequest.Result.DataProcessingError:
case UnityWebRequest.Result.ProtocolError:
Debug.LogError($"Could not get clip from \"{url}\"! Error: {www.error}", this);
continue;
}
musicClips.Add(DownloadHandlerAudioClip.GetContent(www));
}
}
}
// This version starts all downloads at once and waits until they are all done
// probably faster than the sequencial version
private IEnumerator GetAudioClipsParallel()
{
musicClips.Clear();
var requests = new List<UnityWebRequest>();
foreach (var url in externalAudio)
{
Debug.Log(url);
var www = UnityWebRequestMultimedia.GetAudioClip(url, AudioType.MPEG);
// Start the request without waiting
www.SendWebRequest();
requests.Add(www);
}
// Wait for all requests to finish
yield return new WaitWhile(() => requests.Any(r => !r.isDone));
// Now examine and use all results
foreach (var www in requests)
{
switch (www.result)
{
case UnityWebRequest.Result.ConnectionError:
case UnityWebRequest.Result.DataProcessingError:
case UnityWebRequest.Result.ProtocolError:
Debug.LogError($"Could not get clip from \"{www.url}\"! Error: {www.error}", this);
continue;
}
musicClips.Add(DownloadHandlerAudioClip.GetContent(www));
www.Dispose();
}
}
public void PlayFirstTitle()
{
if (!isInitialized) return;
if (source.isPlaying) return;
if (currentPlayTrack != null)
{
StopCoroutine(currentPlayTrack);
}
currentPlayTrack = StartCoroutine(PlayTrack(0));
}
private IEnumerator PlayTrack(int index)
{
// Make sure the index is within the given clips range
index = Mathf.Clamp(index, 0, musicClips.Count);
// update the current track to make next and previous work
currentTrack = index;
// get clip by index
var clip = musicClips[currentTrack];
// Assign and play
source.clip = clip;
source.Play();
// wait for clip end
while (source.isPlaying)
{
yield return null;
}
NextTitle();
}
public void NextTitle()
{
if (!isInitialized) return;
if (currentPlayTrack != null)
{
StopCoroutine(currentPlayTrack);
}
source.Stop();
currentTrack = (currentTrack + 1) % musicClips.Count;
currentPlayTrack = StartCoroutine(PlayTrack(currentTrack));
}
public void PreviousTitle()
{
if (!isInitialized) return;
if (currentPlayTrack != null)
{
StopCoroutine(currentPlayTrack);
}
source.Stop();
currentTrack--;
if (currentTrack < 0)
{
currentTrack = musicClips.Count - 1;
}
currentPlayTrack = StartCoroutine(PlayTrack(currentTrack));
}
public void StopMusic()
{
if (!isInitialized) return;
if (currentPlayTrack != null)
{
StopCoroutine(currentPlayTrack);
}
source.Stop();
}
}
我正在尝试构建一个音频播放器,它可以从外部源获取 mp3 文件以及播放、停止、下一首和上一首按钮的逻辑,但我还不知道该怎么做。
http://localhost:8383/Unity3d/Audio/BT - Paul Van Dyk - Namistai.mp3" http://localhost:8383/Unity3d/Audio/Stevie Wonder - Skeletons.mp3
然后使用 C#,我为 URI 打开一个 UnityWebRequest,将它保存到一个 AudioClip 变量中,然后保存到一个 AudioSource 变量中,但是我发现使用长度 property/saving 到一些某种音频剪辑数组。
下面是我的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Networking;
using UnityEngine.UI;
[RequireComponent(typeof(AudioSource))]
public class AudioManager : MonoBehaviour
{
public AudioClip[] musicClips;
private int currentTrack;
private AudioSource source;
public List<string> externalAudio;
IEnumerator GetAudioClip()
{
externalAudio.Add("http://localhost:8383/Unity3d/Audio/Stevie%20Wonder%20-%20Skeletons.mp3");
externalAudio.Add("http://localhost:8383/Unity3d/Audio/York%20-%20On%20The%20Beach.mp3");
externalAudio.Add("http://localhost:8383/Unity3d/Audio/BT%20-%20Paul%20Van%20Dyk%20-%20Namistai.mp3");
externalAudio.Add("http://localhost:8383/Unity3d/Audio/S.O.S.%20BAND-JUST%20BE%20GOOD%20TO%20ME%20(SINGLE).mp3");
externalAudio.Add("http://localhost:8383/Unity3d/Audio/Michael%20Jackson%20-%20Beat%20It%20(Official%20Video).mp3");
Debug.Log("Hello World");
Debug.Log(UnityWebRequestMultimedia.GetAudioClip("http://localhost:8383/Unity3d/Audio/Stevie%20Wonder%20-%20Skeletons.mp3", AudioType.MPEG));
foreach (var audioClip in externalAudio)
{
Debug.Log(audioClip);
using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(audioClip, AudioType.MPEG))
{
UnityWebRequest request = new UnityWebRequest();
yield return www.SendWebRequest();
if (request.result == UnityWebRequest.Result.ConnectionError)
{
Debug.Log(www.error);
}
else
{
musicClips = DownloadHandlerAudioClip.GetContent(www);
source.clip = musicClips;
Debug.Log("Audio is playing.");
}
}
}
}
// Start is called before the first frame update
void Start()
{
source = GetComponent<AudioSource>();
//PLAY MUSIC
PlayMusic();
}
public void PlayMusic()
{
if (source.isPlaying)
{
return;
}
GetAudioClip();
currentTrack--;
if (currentTrack < 0)
{
currentTrack = 0;
}
StartCoroutine(WaitForMusicEnd());
}
IEnumerator WaitForMusicEnd()
{
while (source.isPlaying)
{
yield return null;
}
NextTitle();
}
public void NextTitle()
{
source.Stop();
currentTrack++;
if (currentTrack > musicClips.Length - 1)
{
currentTrack = 0;
}
source.clip = musicClips[currentTrack];
source.Play();
//Show title
StartCoroutine("WaitForMusicEnd");
}
public void PreviousTitle()
{
source.Stop();
currentTrack--;
if (currentTrack < 0)
{
currentTrack = musicClips.Length - 1;
}
source.clip = musicClips[currentTrack];
source.Play();
//Show title
StartCoroutine("WaitForMusicEnd");
}
public void StopMusic()
{
StopCoroutine("WaitForMusicEnd");
source.Stop();
}
}
如何将外部音频保存到 AudioClip 列表中,然后在 play/stop 函数逻辑中使用它们?
所以这有点宽泛,但这是我可能会做的。
如前所述,让您的服务器提供下载 URL 列表。这样您就不必将它们硬编码到您的客户端中。
对于上一点,您要做的第一件事是一个普通的 GET 请求来接收该 URL 列表,例如简单地用
\n
分隔(换行符)因此您收到的第一条字符串消息可能类似于
http://localhost:8383/Moonbeam%20Challenge/Audio/Stevie%20Wonder%20-%20Skeletons.mp3 http://localhost:8383/Moonbeam%20Challenge/Audio/York%20-%20On%20The%20Beach.mp3 http://localhost:8383/Moonbeam%20Challenge/Audio/BT%20-%20Paul%20Van%20Dyk%20-%20Namistai.mp3 http://localhost:8383/Moonbeam%20Challenge/Audio/S.O.S.%20BAND-JUST%20BE%20GOOD%20TO%20ME%20(SINGLE).mp3 http://localhost:8383/Moonbeam%20Challenge/Audio/Michael%20Jackson%20-%20Beat%20It%20(Official%20Video).mp3
收到的这份清单将您
Split
放入各个 URLvar urls = urlList.Split('\n');
对于这些,您可以开始您的个人
GetAudioClip
请求。您作为元素存储在 array/a 列表中的结果。您没有分配整个
musicClips
。我宁愿使用List<AudioClip> musicClips = new List<AudioClip>();
然后从您的下载中执行
musicClips.Add(DownloadHandlerAudioClip.GetContent(www));
您也不希望下载后立即将每个下载的剪辑分配给 AudioSource 并播放,而是等到所有剪辑都下载完毕(或至少是第一个,具体取决于根据您的需要)
所以这看起来有点像
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Networking;
using UnityEngine.UI;
using System.Linq;
[RequireComponent(typeof(AudioSource))]
public class AudioManager : MonoBehaviour
{
[Header("References")]
[SerializeField]
private AudioSource source;
[Header("Settings")]
[SerializeField] private string baseURL;
[SerializeField] private string[] externalAudio;
[SerializeField] private bool autoStartPlayAfterDownloads = true;
[Header("Debugging")]
[SerializeField] private List<AudioClip> musicClips = new List<AudioClip>();
[SerializeField] private int currentTrack;
[SerializeField] private bool isInitialized;
private Coroutine currentPlayTrack;
// Yes, if you make Start return IEnumerator then Units
// automatically runs it as a Coroutine
private IEnumerator Start()
{
// block input from the outside until this controller is finished with the downloads
isInitialized = false;
if (!source) source = GetComponent<AudioSource>();
// Here either use the approach with first receiving a list from the server
yield return GetAudioClipsInfo();
// OR if you already have the clip urls assigned via the Inspector use one of
yield return GetAudioClipsSequencial();
// OR
yield return GetAudioClipsParallel();
// allow to do things from this point on
isInitialized = true;
if(autoStartPlayAfterDownloads) PlayFirstTitle();
}
// This is the routine that downloads the URL list from the server
// then it starts the individual downloads
private IEnumerator GetAudioClipsInfo()
{
// Make the first request for receiving the list of URLs from the server
// If not using this but already assigning the urls
// via Inspector then simply directly use the "GetAudioClips" below
using (var request = UnityWebRequest.Get(baseURL))
{
yield return request.SendWebRequest();
switch (request.result)
{
case UnityWebRequest.Result.ConnectionError:
case UnityWebRequest.Result.DataProcessingError:
case UnityWebRequest.Result.ProtocolError:
Debug.LogError($"Could not get list of clips! Error: {request.error}", this);
yield break;
}
var urlList = request.downloadHandler.text;
externalAudio = urlList.Split('\n');
// Here you can either go for the sequencial downloads
yield return GetAudioClipsSequencial();
// OR run them all parallel
yield return GetAudioClipsParallel();
}
}
// This version starts one download at a time, waits until it is done
// then starts the next
private IEnumerator GetAudioClipsSequencial()
{
musicClips.Clear();
foreach (var url in externalAudio)
{
Debug.Log(url);
using (var www = UnityWebRequestMultimedia.GetAudioClip(url, AudioType.MPEG))
{
yield return www.SendWebRequest();
switch (www.result)
{
case UnityWebRequest.Result.ConnectionError:
case UnityWebRequest.Result.DataProcessingError:
case UnityWebRequest.Result.ProtocolError:
Debug.LogError($"Could not get clip from \"{url}\"! Error: {www.error}", this);
continue;
}
musicClips.Add(DownloadHandlerAudioClip.GetContent(www));
}
}
}
// This version starts all downloads at once and waits until they are all done
// probably faster than the sequencial version
private IEnumerator GetAudioClipsParallel()
{
musicClips.Clear();
var requests = new List<UnityWebRequest>();
foreach (var url in externalAudio)
{
Debug.Log(url);
var www = UnityWebRequestMultimedia.GetAudioClip(url, AudioType.MPEG);
// Start the request without waiting
www.SendWebRequest();
requests.Add(www);
}
// Wait for all requests to finish
yield return new WaitWhile(() => requests.Any(r => !r.isDone));
// Now examine and use all results
foreach (var www in requests)
{
switch (www.result)
{
case UnityWebRequest.Result.ConnectionError:
case UnityWebRequest.Result.DataProcessingError:
case UnityWebRequest.Result.ProtocolError:
Debug.LogError($"Could not get clip from \"{www.url}\"! Error: {www.error}", this);
continue;
}
musicClips.Add(DownloadHandlerAudioClip.GetContent(www));
www.Dispose();
}
}
public void PlayFirstTitle()
{
if (!isInitialized) return;
if (source.isPlaying) return;
if (currentPlayTrack != null)
{
StopCoroutine(currentPlayTrack);
}
currentPlayTrack = StartCoroutine(PlayTrack(0));
}
private IEnumerator PlayTrack(int index)
{
// Make sure the index is within the given clips range
index = Mathf.Clamp(index, 0, musicClips.Count);
// update the current track to make next and previous work
currentTrack = index;
// get clip by index
var clip = musicClips[currentTrack];
// Assign and play
source.clip = clip;
source.Play();
// wait for clip end
while (source.isPlaying)
{
yield return null;
}
NextTitle();
}
public void NextTitle()
{
if (!isInitialized) return;
if (currentPlayTrack != null)
{
StopCoroutine(currentPlayTrack);
}
source.Stop();
currentTrack = (currentTrack + 1) % musicClips.Count;
currentPlayTrack = StartCoroutine(PlayTrack(currentTrack));
}
public void PreviousTitle()
{
if (!isInitialized) return;
if (currentPlayTrack != null)
{
StopCoroutine(currentPlayTrack);
}
source.Stop();
currentTrack--;
if (currentTrack < 0)
{
currentTrack = musicClips.Count - 1;
}
currentPlayTrack = StartCoroutine(PlayTrack(currentTrack));
}
public void StopMusic()
{
if (!isInitialized) return;
if (currentPlayTrack != null)
{
StopCoroutine(currentPlayTrack);
}
source.Stop();
}
}