将 mp3 字节数组转换为浮点数组以与 Unity 一起玩
Converting mp3 byte array to float array to play with Unity
所以我目前正在尝试从外部麦克风(在这种情况下实际上是在机器人上)获取音频并将其流式传输到 Unity 以在场景中播放。我相当确定此音频以 mp3 格式编码,采样率为 16000 Hz,比特率为 192 kHz。
我可以在 Unity 中将此音频作为字节数组(似乎总是 Little Endian)获取,我想转换为浮点数组,每个值的范围从 -1.0f 到 + 1.0f 以便我可以使用 AudioClip.SetData 在 Unity 场景中播放它。我的问题是我目前无法做到这一点。
我的第一次尝试是基于这个 Whosebug 答案: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 设置数据或播放器时犯了错误。
任何 help/suggestions 都非常感谢!
[补充资源:我进入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)。这工作得很好 - 所以如果我可以在那一秒 link 复制 audio_play 脚本的解码功能,看来我会很高兴!]
首先,仅当您的数据是 16 位 PCM 时,像这样将 byte[] 转换为 float[] 才有效。如果它是 16 位的。
如果你的音频确实是用MPEG-1/MPEG-2 Audio Layer 3 格式压缩的,那么进入流将不是简单的转换数据格式的问题,它需要解码(先压缩)。我会尝试让发件人生成标准的非编码 PCM 格式,您的代码应该开始工作
在 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 格式,因此不需要解码在团结中。如果这是一个选项,这将要求您重新编译 C++ 代码。请注意,音频数据(波形格式)在内存中会略大(比 mp3)。
请参阅 audio_capture.cpp 文件的第 98 -> 109 行以了解检查 wave 或 mp3 格式的位置。
在 Unity 中解码 MP3 音频
否则你可以尝试在Unity中解码mp3数据。这很可能涉及使用 mp3 库(我发现的第一个库是 MP3Sharp). Otherwise there's a Unity asset called uAudio,它声明要进行实时 mp3 compression/decompression;这可能比使用通用的 mp3 解码器更简单,因为它已经设计好了对于 Unity。
我不建议自己编写 mp3 解码器,除非只是为了挑战或学习目的。
抛开所有想法,我的第一个尝试是使用上述 "wave" 参数重新编译您的 C++ 库!
希望对您有所帮助:)
下载此 .dll:https://www.dllme.com/dll/files/naudio_dll.html 并导入文件夹 Plugins
下载此 C# 脚本:https://www.dropbox.com/s/wks0ujanr0pm6nj/NAudioPlayer.cs?dl=0
下载并导入此 Unity Asset Store:https://assetstore.unity.com/packages/tools/gui/runtime-file-browser-113006
创建一个 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();
}
}
所以我目前正在尝试从外部麦克风(在这种情况下实际上是在机器人上)获取音频并将其流式传输到 Unity 以在场景中播放。我相当确定此音频以 mp3 格式编码,采样率为 16000 Hz,比特率为 192 kHz。
我可以在 Unity 中将此音频作为字节数组(似乎总是 Little Endian)获取,我想转换为浮点数组,每个值的范围从 -1.0f 到 + 1.0f 以便我可以使用 AudioClip.SetData 在 Unity 场景中播放它。我的问题是我目前无法做到这一点。
我的第一次尝试是基于这个 Whosebug 答案: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 设置数据或播放器时犯了错误。
任何 help/suggestions 都非常感谢!
[补充资源:我进入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)。这工作得很好 - 所以如果我可以在那一秒 link 复制 audio_play 脚本的解码功能,看来我会很高兴!]
首先,仅当您的数据是 16 位 PCM 时,像这样将 byte[] 转换为 float[] 才有效。如果它是 16 位的。
如果你的音频确实是用MPEG-1/MPEG-2 Audio Layer 3 格式压缩的,那么进入流将不是简单的转换数据格式的问题,它需要解码(先压缩)。我会尝试让发件人生成标准的非编码 PCM 格式,您的代码应该开始工作
在 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 格式,因此不需要解码在团结中。如果这是一个选项,这将要求您重新编译 C++ 代码。请注意,音频数据(波形格式)在内存中会略大(比 mp3)。
请参阅 audio_capture.cpp 文件的第 98 -> 109 行以了解检查 wave 或 mp3 格式的位置。
在 Unity 中解码 MP3 音频
否则你可以尝试在Unity中解码mp3数据。这很可能涉及使用 mp3 库(我发现的第一个库是 MP3Sharp). Otherwise there's a Unity asset called uAudio,它声明要进行实时 mp3 compression/decompression;这可能比使用通用的 mp3 解码器更简单,因为它已经设计好了对于 Unity。
我不建议自己编写 mp3 解码器,除非只是为了挑战或学习目的。
抛开所有想法,我的第一个尝试是使用上述 "wave" 参数重新编译您的 C++ 库!
希望对您有所帮助:)
下载此 .dll:https://www.dllme.com/dll/files/naudio_dll.html 并导入文件夹 Plugins
下载此 C# 脚本:https://www.dropbox.com/s/wks0ujanr0pm6nj/NAudioPlayer.cs?dl=0
下载并导入此 Unity Asset Store:https://assetstore.unity.com/packages/tools/gui/runtime-file-browser-113006
创建一个 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();
}
}