通过Unity中的音频源播放NAudio .mp3

时间:2013-10-13 20:00:35

标签: c# audio unity3d mp3 naudio

我正在使用一个名为Visualizer Studio(http://www.alteredr.com/visualizer-studio/)的软件包来制作一个对象对音乐作出反应的游戏。我的目标是让用户能够在游戏过程中随时加载歌曲。它将是独立的/ PC,而不是WebPlayer。

我的问题是Visualizer Studio从场景中的音频源获取音频数据。因此,当我使用NAudio加载和流式传输MP3时,visualizer工作室不会听到它们,我的对象也不会对音乐作出反应。

我现在正在我的代码中使用IWavePlayer。我已经尝试添加audio.clip = www.GetAudioClip和类似功能,让音频片段加载正在播放的音乐,但无济于事。

我从这篇博客文章中获取了用于选择和流式传输.mp3s的代码(源代码位于底部):( http://denis-potapenko.blogspot.com/2013/04/task-6-loading-mp3-audio-via-www-class.html)。目前它没有改变,因为我没有尝试过任何工作。

要清楚,当我从文件浏览器中选择.mp3s时,它会播放。我只需要它们通过我的场景中的音频源播放。请,有一种方法可以做到这一点。我联系了visualizer studio支持并在团结论坛上询问过,但到目前为止还没有人可以提供帮助。

请记住,我不是一个优秀的程序员,所以你可能不得不为我愚蠢的回答。感谢您提前耐心等待。无论如何,

这是我用来打开文件浏览器和流音频的代码:

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
    ///////////////////////////////////////////////////////////////////////////
}

此致 乔纳森

3 个答案:

答案 0 :(得分:1)

使用WaveOut实例时,您(相对)直接在声卡上播放,外部。您需要一种方法将该数据引入Unity。

我没有使用Unity,所以这可能不是最佳答案,但是...

您可以使用OnAudioFilterRead将波浪数据引入场景。您可以使用它来创建程序性声音,并且通过一些编码,它可以被连接到NAudio波源。这将直接将音频数据引入游戏中,以便您处理游戏中的代码。

以下是我发现的可能有用的文章:Procedural Audio with Unity

答案 1 :(得分:1)

 mWaveOutDevice = new WaveOut();

可能导致问题,最好使用支持在所有非GUI线程中运行的WaveOutEvent()。

 mWaveOutDevice = new WaveOutEvent();

Related StackOverFlow Question

答案 2 :(得分:1)

http://answers.unity3d.com/answers/1128204/view.html

使用"最新的"插入到您的Resources文件夹中的NAudio.dll和NAudio.WindowsMediaFormat.dll版本利用此代码执行您所描述的操作:

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;
     }
 }

我引用

  

&#34; songToPlay&#34;传递给函数的变量是从CheckMusic函数下创建的数组中获取的简单int。我搜索从GUI输入字段输入的选定目录中的特定文件类型(MP3),可以将其更改为WAV或OGG,然后将这些文件输入到数组中。其他代码选择要播放的阵列上的歌曲编号,您可以将其更改为您喜欢的任何内容。重要的是NAudio,dll完成所有繁重的工作。您需要做的就是使用aud.Read(float []发送数据,歌曲起始点(通常为0),歌曲数据长度(aud.length)。这里的Float []长度与aud相同。 length所以创建相同长度的浮点数,读取文件,填充浮点数,创建剪辑,然后使用AudioClip.SetData()

转储浮点数据      

现在这段代码可以正常运行。缺点是它需要   用这种方式填充浮子需要2-3秒,这是一个明显的阻力。它   也倾向于快速咀嚼内存,但它完成了工作。   希望它有助于作为那些希望做的人的起点   这个。我知道我需要它。