【问题标题】:Converting mp3 byte array to float array to play with Unity将 mp3 字节数组转换为浮点数组以使用 Unity
【发布时间】:2018-06-26 21:24:57
【问题描述】:

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

我能够在 Unity 中将此音频作为字节数组(似乎总是小端序),我想转换为浮点数组,每个值的范围从 -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 设置数据或播放器时犯了错误。

非常感谢任何帮助/建议!

[附加资源:我进入unity的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 脚本的解码功能,看起来我会很高兴!]

【问题讨论】:

    标签: c# unity3d audio mp3 ros


    【解决方案1】:

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

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

    这意味着您有两个选择。

    从您的库中导出 Wave 格式(原始 PCM)

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

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

    如果将第 22 行的第三个构造函数参数更改为 "wave",通读代码,它会将数据导出为 .wav 格式,因此不需要在 Unity 中解码。如果这是一个选项,这将需要您重新编译您的 C++ 代码。请注意,音频数据(波形格式)在内存中会稍大(比 mp3)。

    请参阅audio_capture.cpp 文件的第 98 -> 109 行,了解它检查波形或 mp3 格式的位置。

    在 Unity 中解码 MP3 音频

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

    我不建议您编写自己的 mp3 解码器,除非只是为了挑战或学习目的。


    抛开所有想法,我的第一次尝试是重新编译您的 C++ 库,参数为“wave”,如上所述!

    我希望这会有所帮助:)

    【讨论】:

    • 嗨,这是一个非常酷的建议!我认为可以使用 WAV 格式。我是否必须以任何方式更改我的统一代码以处理使用 WAV 编码的标头或其他信息?
    • 嗨 @NishanthJKumar,the wave header's 往往比 mp3 更容易处理。只要您正确提取数据块,这应该没问题。因为我看不到你如何解析标题的代码,所以我不能说,但是如果你正在努力解析波形文件,那么有很多人已经构建了 wave parsers already。如果你搜索的话,我相信你会发现更多的解析器:)
    【解决方案2】:

    首先,仅当您的数据是 16 位 PCM 时,将 byte[] 转换为 float[] 才有效。如果是 16 位。

    如果您的音频真的是使用 MPEG-1/MPEG-2 Audio Layer 3 格式压缩的,那么获取流将不仅仅是转换数据格式的问题,它需要被解码(首先压缩)。我会尝试让发件人生成标准的非编码 PCM 格式,并且您的代码应该开始工作

    【讨论】:

    • 谢谢,我之前没有意识到,这很有帮助!我会看看我能做些什么
    【解决方案3】:
    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# 脚本,添加到一个游戏对象,然后编写以下代码行:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多