将mp3字节数组转换为float数组以与Unity一起播放

时间:2018-06-26 21:24:58

标签: c# unity3d audio mp3 ros

因此,我目前正在尝试从外部麦克风(在这种情况下实际上是在机器人上)获取音频,并将其流式传输到Unity中以在场景中播放。我相当确定此音频是以mp3格式编码的,采样率为16000 Hz,比特率为192 kHz。

我可以在Unity中以字节数组(似乎总是Little Endian)的形式获取此音频,并且我想转换为一个浮点数组,每个值的范围为-1.0f至+ 1.0f这样我就可以使用AudioClip.SetData在Unity场景中播放它。我的问题是我到目前为止无法做到这一点。

我的第一次尝试是基于以下StackOverflow答案:create AudioClip from byte[] 使用以下函数进行转换:

private float[] ConvertByteToFloat(byte[] array) {
        float[] floatArr = new float[array.Length / 4];
        for (int i = 0; i < floatArr.Length; i++) {
            if (BitConverter.IsLittleEndian) {
                Array.Reverse(array, i * 4, 4);
            }
            floatArr[i] = BitConverter.ToSingle(array, i * 4) / 0x80000000;
        }
        return floatArr;
    }

然后我像这样调用它:

scaledAudio = ConvertByteToFloat(audioData);
AudioClip audioClip = AudioClip.Create("RobotAudio", scaledAudio.Length, 1, 16000, false);
audioClip.SetData(scaledAudio, 0);
AudioSource.PlayClipAtPoint(audioClip, robot.transform.position);

但是结果是很多静态的,并且在记录一些输出时,我意识到我得到了一堆NaN ...

我读到某个地方可以使用BitConverter.ToInt16()函数提取mp3音频,因此我相应地更改了ConvertByteToFloat函数,如下所示:

private float[] ConvertByteToFloat16(byte[] array) {
            float[] floatArr = new float[array.Length / 2];
            for (int i = 0; i < floatArr.Length; i++) {
                if (BitConverter.IsLittleEndian) {
                    Array.Reverse(array, i * 2, 2);
                }
                floatArr[i] = (float) (BitConverter.ToInt16(array, i * 2) / 32767f);
            }
            return floatArr;
        }

[注意:结果除以32767f,因为我读到这是可能出现的最大值,并且我想将其缩小到-1.0f和1.0f之间]

其中的数字看起来更有希望。它们确实都在-1.0f和1.0f之间。但是当我尝试使用Unity播放音频时,我听到的只是静态的。

问题几乎可以肯定是在byte []到float []的转换中,但是我在为AudioClip或AudioSource设置数据或播放器时可能犯了一个错误。

非常感谢您的帮助/建议!

[其他资源:我成为一个整体的byte []来自这里:https://github.com/ros-drivers/audio_common/blob/master/audio_capture/src/audio_capture.cpp 有一个相关的脚本,用于获取此捕获程序编码的数据并进行播放(https://github.com/ros-drivers/audio_common/blob/master/audio_play/src/audio_play.cpp)。这很好用-因此,如果我可以在第二个链接中复制audio_play脚本的解码功能,那似乎就可以了!]

3 个答案:

答案 0 :(得分:2)

file you linked中,它表示在安装过程中将数据编码为mp3编码格式(左侧行号)。

21 >> // Need to encoding or publish raw wave data
22 >> ros::param::param<std::string>("~format", _format, "mp3");

这意味着您有两个选择。

从库中导出波形格式(Raw PCM)

更改C ++库的输出格式以导出原始wave文件格式。

21 >> // Need to encoding or publish raw wave data
22 >> ros::param::param<std::string>("~format", _format, "wave");

如果将第22行的第三个构造函数参数更改为“ wave” ,则会仔细阅读代码,它将数据导出为.wav格式,因此不需要在Unity中解码。如果可以的话,这将要求您重新编译C ++代码。请注意,音频数据(波形格式)在内存中的大小会略大于mp3。

有关检查wave或mp3格式的位置,请参见audio_capture.cpp文件的第98行-> 109。

在Unity中解码MP3音频

否则,您可以尝试在Unity中解码mp3数据。这很可能涉及使用mp3库(我发现的第一个库是MP3Sharp)。否则,会有一个称为uAudio的Unity资产声明要进行实时mp3压缩/解压缩;这可能比使用通用的mp3解码器更简单,因为它已经为Unity设计。

除非为了挑战或出于学习目的,否则我不建议编写自己的mp3解码器。


除了所有想法外,我的第一个尝试就是如上所述重新编译C ++库,并使用“ wave”作为参数!

我希望这会有所帮助:)

答案 1 :(得分:0)

首先,仅将byte []转换为float []即可,仅当您的数据是16位PCM时才起作用。如果是16位的话。

如果您的音频确实是使用MPEG-1 / MPEG-2音频第3层格式压缩的,则进入流将不仅仅是转换数据格式,而是需要对其进行解码(首先压缩)。我会尝试让发送方产生标准的,未编码的PCM格式,并且您的代码应该开始起作用

答案 2 :(得分:0)

  1. 下载此.dll:https://www.dllme.com/dll/files/naudio_dll.html并导入文件夹Plugins

  2. 下载此c#脚本:https://www.dropbox.com/s/wks0ujanr0pm6nj/NAudioPlayer.cs?dl=0

  3. 下载并导入此Unity Asset Store:https://assetstore.unity.com/packages/tools/gui/runtime-file-browser-113006

  4. 创建一个c#脚本,添加到gameObject中,并编写以下代码行:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.IO;
    using UnityEngine.UI;
    using System.Runtime;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Runtime.Serialization;
    using NAudio;
    using NAudio.Wave;
    using UnityEngine.Networking;
    using SimpleFileBrowser;

    public class ReadMp3 : MonoBehaviour{
    private AudioSource audioSource;
    public Text pathText;

    private void Start()
    {
        audioSource = GetComponent<AudioSource>();
    }
    public void ReadMp3Sounds()
    {
        FileBrowser.SetFilters(false, new FileBrowser.Filter("Sounds", ".mp3"));
        FileBrowser.SetDefaultFilter(".mp3");
        StartCoroutine(ShowLoadDialogCoroutine());
    }

    IEnumerator ShowLoadDialogCoroutine()
    {

        yield return FileBrowser.WaitForLoadDialog(false, null, "Select Sound", "Select");

        pathText.text = FileBrowser.Result;

        if (FileBrowser.Success)
        {
            byte[] SoundFile = FileBrowserHelpers.ReadBytesFromFile(FileBrowser.Result);
            yield return SoundFile;

            audioSource.clip = NAudioPlayer.FromMp3Data(SoundFile);
            audioSource.Play();   
        }
    }
相关问题