从base64

时间:2016-02-05 16:19:35

标签: c# audio unity3d base64

我在base64中编码了wav文件(参考资料/声音中的audioClipName.txt)。

HERE IS THE SOURCE WAVE FILE

然后我尝试对其进行解码,从中制作一个AudioClip并按照以下方式播放:

public static void CreateAudioClip()
{
    string s = Resources.Load<TextAsset> ("Sounds/audioClipName").text;

    byte[] bytes = System.Convert.FromBase64String (s);
    float[] f = ConvertByteToFloat(bytes);

    AudioClip audioClip = AudioClip.Create("testSound", f.Length, 2, 44100, false, false);
    audioClip.SetData(f, 0);

    AudioSource as = GameObject.FindObjectOfType<AudioSource> ();
    as.PlayOneShot (audioClip);
}

private static 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);
    }

    return floatArr;
}

除了声音只是一种噪音外,每件事情都很好。

我在堆栈溢出时发现this,但答案dosnt解决了这个问题。

以下是Unity3D中wav文件的详细信息:

enter image description here

有谁知道这里的问题是什么?

修改

我写下了二进制文件,一个刚从base64解码后,最后一次转换后第二个,并将其与原始的二进制wav文件进行比较:

enter image description here

正如您所看到的,文件被正确编码,因为它只是解码它并将文件写成这样:

string scat = Resources.Load<TextAsset> ("Sounds/test").text;

byte[] bcat = System.Convert.FromBase64String (scat);
System.IO.File.WriteAllBytes ("Assets/just_decoded.wav", bcat);

给出了相同的文件。所有文件都有一定的长度。

但是最后一个是错的,所以问题在于转换为float数组。但我不明白可能出现的问题。

编辑:

以下是写下final.wav的代码:

string scat = Resources.Load<TextAsset> ("Sounds/test").text;

byte[] bcat = System.Convert.FromBase64String (scat);
float[] f = ConvertByteToFloat(bcat);

byte[] byteArray = new byte[f.Length * 4];
Buffer.BlockCopy(f, 0, byteArray, 0, byteArray.Length);

System.IO.File.WriteAllBytes ("Assets/final.wav", byteArray);

2 个答案:

答案 0 :(得分:7)

您尝试播放的wave文件(meow.wav)具有以下属性:

  • PCM
  • 2个频道
  • 44100 Hz
  • 签署了16位小端

您的主要错误是,您正在解释二进制数据,就好像它已经代表一个浮点数。这就是BitConverter.ToSingle()的作用。

但是需要做的是,从每两个字节创建一个带符号的16位little-endian值(如Wavefile-header中所指定的) cast 它到一个浮点数然后标准化它。每个两个字节在您的文件(16位!)的情况下进行采样,而不是四个字节。数据小端(s16le),因此如果主机不是,则只需交换它。

这将是更正的转换函数:

private static float[] ConvertByteToFloat(byte[] array) {
    float[] floatArr = new float[array.Length / 2];

    for (int i = 0; i < floatArr.Length; i++) {
        floatArr[i] = ((float) BitConverter.ToInt16(array, i * 2))/32768.0;
    }

    return floatArr;
}

你应该跳过波形文件的标题(真实音频数据从偏移44开始)。

对于干净的解决方案,您必须正确解释Wave-header并根据其中指定的内容调整您的操作(如果它包含不受支持的参数,则进行挽救)。 例如,必须注意样本格式(每个样本的比特数和字节数),采样率和通道数。

答案 1 :(得分:1)

根据文件here

  

样本应该是从-1.0f到1.0f的浮点数(超过这些限制将导致伪像和未定义的行为)。样本计数由float数组的长度决定。使用offsetSamples写入剪辑中的随机位置。如果偏移的长度大于剪辑长度,则写入将环绕并从剪辑的开头写入剩余的样本。

似乎你有这种效果。所以我猜你必须先对数组进行规范化处理才能进行处理。

当你在统一操作时,我不确定你可以使用什么功能,所以我为float数组提供了一些基本的扩展方法:

/// <summary>
/// Normalizes the values within this array.
/// </summary>
/// <param name="data">The array which holds the values to be normalized.</param>
static void Normalize(this float[] data)
{
    float max = float.MinValue;

    // Find maximum
    for (int i = 0; i < data.Length; i++)
    {
        if (Math.Abs(data[i]) > max)
        {
            max = Math.Abs(data[i]);
        }
    }

    // Divide all by max
    for (int i = 0; i < data.Length; i++)
    {
        data[i] = data[i] / max;
    }
}

在进一步处理数据之前使用此扩展方法,如下所示:

byte[] bytes = System.Convert.FromBase64String (s);
float[] f = ConvertByteToFloat(bytes);

// Normalize the values before using them
f.Normalize();

AudioClip audioClip = AudioClip.Create("testSound", f.Length, 2, 44100, false, false);
audioClip.SetData(f, 0);