Playing NAudio .mp3 through an Audio Source in Unity
Asked Answered
C

3

2

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

Clementia answered 13/10, 2013 at 20:0 Comment(3)
@Steven This is certainly a Unity-related question. Why did you remove the tag?Gehring
@Corey: Nope, it isn't. Unity is a "dependency injection container for .NET.", but you are talking about Unity3d which is "a commercial development platform for creating game".Virge
@Steve thanks for the correction, and for adding the right tag back in :)Gehring
C
2

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.

Chane answered 13/3, 2017 at 1:5 Comment(0)
G
1

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

Gehring answered 16/10, 2013 at 13:53 Comment(0)
B
1
 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

Blair answered 25/4, 2014 at 22:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.