简体   繁体   English

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

[英]Playing NAudio .mp3 through an Audio Source in Unity

I'm using a package called Visualizer Studio ( http://www.alteredr.com/visualizer-studio/ ) to make a game where objects react to music.我正在使用一个名为 Visualizer Studio ( http://www.alteredr.com/visualizer-studio/ ) 的包来制作一个对象对音乐做出反应的游戏。 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.它将是独立的/PC,而不是 WebPlayer。

My problem is that Visualizer Studio takes the audio data from an Audio Source in the scene.我的问题是 Visualizer Studio 从场景中的音频源获取音频数据。 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.因此,当我使用 NAudio 加载和流式传输 MP3 时,visualizer studio 听不到它们,我的对象也不会对音乐做出反应。

I'm using IWavePlayer right now in my code.我现在在我的代码中使用 IWavePlayer。 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.我已经尝试添加 audio.clip = www.GetAudioClip 和类似的功能,让音频剪辑加载正在播放的音乐,但无济于事。

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 ).我从这篇博文中得到了我用来选择和流式传输 .mp3 的代码(源代码在底部):( 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.需要明确的是,当我从文件浏览器中选择它们时,.mp3 确实会播放。 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.我已经联系了 Visualizer Studio 支持并在统一论坛上询问,但到目前为止没有人可以提供帮助。

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

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.传递给函数的“songToPlay”变量是一个简单的 int,它是从 CheckMusic 函数下创建的数组中获取的。 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.我在从 GUI 输入字段输入的选定目录中搜​​索可以更改为 WAV 或 OGG 的特定文件类型 (MP3),然后将这些文件输入到数组中。 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.重要的部分是 NAudio,dll 完成所有繁重的工作。 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()你需要做的就是使用aud.Read(float[]发送数据,歌曲起始点(通常为0),歌曲数据的长度(aud.length)。这里的Float[]和aud的长度相同。 length 所以创建相同长度的浮点数,读取文件,填充浮点数,创建剪辑,然后使用 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.缺点是以这种方式填充浮点需要 2-3 秒,并且是一个明显的阻力。 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.当您使用WaveOut实例时,您是在声卡上(相对)直接在 Unity 环境之外播放。 You need a way to introduce that data into Unity.您需要一种将数据引入 Unity 的方法。

I haven't worked with Unity so this might not be the best answer,but...我没有与 Unity 合作过,所以这可能不是最好的答案,但是......

You can introduce wave data into the scene using OnAudioFilterRead .您可以使用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.您可以使用它来创建程序声音,并且通过一点编码,它大概可以连接到 NAudio 波源。 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这是我发现可能有帮助的一篇文章: 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.可能会导致问题,最好使用支持在所有非 GUI 线程中运行的 WaveOutEvent()。

 mWaveOutDevice = new WaveOutEvent();

Related StackOverFlow Question 相关的 StackOverFlow 问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM