Шумный аудиоклип после декодирования с base64

Я закодировал wav файл в base64 (audioClipName.txt в Ресурсах/Звуках).

ЗДЕСЬ ИСТОЧНИК ВОЛНЫ

Затем я попытался декодировать его, сделать из него 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;
}

Все отлично работает, за исключением того, что звук - это всего лишь один шум.

Я нашел этот здесь в переполнении стека, но ответ dosnt решил проблему.

Вот подробности о wav файле из Unity3D:

введите описание изображения здесь

Кто-нибудь знает, в чем проблема?

ИЗМЕНИТЬ

Я записал двоичные файлы, один сразу после декодирования из base64, второй после окончательной конвертации и сравнил его с исходным двоичным wav файлом:

введите описание изображения здесь

Как вы можете видеть, файл был закодирован правильно, потому что он просто декодирует его и записывает файл следующим образом:

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

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

предоставили те же файлы. Все файлы имеют некоторую длину.

Но последнее неверно, поэтому проблема заключается в преобразовании в массив float. Но я не понимаю, что может быть неправильно.

EDIT:

Вот код для записи 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);

Ответ 1

Волновой файл, который вы пытаетесь воспроизвести (meow.wav), имеет следующие свойства:

  • PCM
  • 2 канала
  • 44100 Гц
  • подписанный 16-битный little-endian

Ваша основная ошибка заключается в том, что вы интерпретируете двоичные данные, как если бы они уже представляли float. Это то, что делает BitConverter.ToSingle().

Но вам нужно сделать, чтобы создать подписанное 16-битное малоконечное значение (как указано в заголовке Wavefile) из каждых двух байтов, передать его в float и затем нормализовать его. И каждый два байта составляют образец в случае вашего файла (16-бит!), А не четыре байта. Данные немного endian (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-заголовок и адаптировать свои операции в соответствии с тем, что там указано (или выпустить, если он содержит неподдерживаемые параметры). Например, должен быть рассмотрен формат выборки (бит на выборку и конечный уровень), частоту дискретизации и количество каналов.

Ответ 2

В соответствии с документацией здесь,

Образцы должны быть плавающими в диапазоне от -1.0f до 1.0f (превышение этих пределов приведет к артефактам и поведению undefined). Счетчик выборки определяется длиной массива float, Используйте offsetSamples для записи в случайную позицию в клипе. Если длина смещения больше длины клипа, запись будет обертываться и записывать оставшиеся образцы с начала клипа.

похоже, что у вас есть именно этот эффект. Поэтому, я думаю, вам придется нормализовать массив до того, как его можно обработать.

Как вы работаете в единстве, я не уверен, какую функциональность вы можете использовать, поэтому я предоставил небольшой базовый метод расширения для массивов с плавающей запятой:

/// <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);