从Unity AudioClip创建.WAV文件

时间:2018-06-14 18:55:13

标签: c# visual-studio unity3d wav

所以问题是我想从Audioclip创建一个.wav文件,但我无法弄清楚如何做到这一点。我一直在寻找.wav文件结构是如何但我无法理解它我想要的(我也不知道如何从Audioclip获取所有数据并将其转换为字节并设置它到文件)。

这是我到目前为止所尝试的内容:

   public static bool ToWAV(this AudioClip audio, string path)
    {
        try
        {
            int samples = audio.samples;
            int channels = audio.channels;
            int sampleRate = audio.frequency;

            FileStream stream = new FileStream(path, FileMode.OpenOrCreate);
            BinaryWriter bw = new BinaryWriter(stream);

            //RIFF HEADER
            bw.Write(System.Text.Encoding.ASCII.GetBytes("RIFF"));
            bw.Write(36 + samples * channels);
            bw.Write(System.Text.Encoding.ASCII.GetBytes("WAVE"));

            //FMT SUBCHUNK
            bw.Write(System.Text.Encoding.ASCII.GetBytes("fmt "));
            bw.Write(16); //Chunk size
            bw.Write(1); //Compression
            bw.Write(channels); //Num Channels
            bw.Write(sampleRate); //SampleRate
            bw.Write(sampleRate * channels); //ByteRate
            bw.Write(channels); //BlockAlign
            bw.Write(8); //BitsPerSample

            //DATA SUBCHUNK
            bw.Write(System.Text.Encoding.ASCII.GetBytes("data"));
            bw.Write(samples * channels); //Chunk size

            float[] data = new float[audio.samples * audio.channels];
            audio.GetData(data, 0);

            for (int d = 0; d < data.Length; d++)
            {
                bw.Write((byte)data[d]);
            }

            return true;
        }
        catch (System.Exception e)
        {
            Debug.LogError("Failed to create .WAV at: " + path + " - Error: " + e.Message);
            return false;
        }
    }

2 个答案:

答案 0 :(得分:1)

这里的问题是浮点数据的范围是-1..1,而您只是将其强制转换为每个样本8位的字节,这意味着您将主要获得零数据。您需要重新缩放以占据全部可用的数字范围,这对于8位(有符号)表示-128..127 ...

尝试在写入时乘以127并转换为sbyte。

此外,我相信PCM多通道WAV文件是交错的。因此,将来自一个通道的所有数据然后放入来自另一个通道的所有数据(因为它们存储在内存中)将不起作用。实际上,您必须在通道之间交替字节。

答案 1 :(得分:0)

我今天才把这个编码好玩了。我发现其他人也想进行转换。这就是我遇到您的帖子的方式。这可能有些棘手,但您需要遵循指南here。规范非常挑剔,您需要按照规范中要求的正确字节序写入数据。这是我的代码,将从AudioClip转换为16位PCM Wave文件格式(注意:我仅在麦克风的单通道音频上对其进行了测试;也许其他人可以仔细检查是否为多个渠道工作,因为我怀疑赔率看起来很有希望,或者您可以进行调整并相应地进行调整):

void ExportClipData(AudioClip clip)
{
    var data = new float[clip.samples * clip.channels];
    clip.GetData(data, 0);
    var path = Path.Combine(Application.persistentDataPath, "Recording.wav");
    using (var stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write))
    {
        // The following values are based on http://soundfile.sapp.org/doc/WaveFormat/
        var bitsPerSample = (ushort)16;
        var chunkID = "RIFF";
        var format = "WAVE";
        var subChunk1ID = "fmt ";
        var subChunk1Size = (uint)16;
        var audioFormat = (ushort)1;
        var numChannels = (ushort)clip.channels;
        var sampleRate = (uint)clip.frequency;
        var byteRate = (uint)(sampleRate * clip.channels * bitsPerSample / 8);  // SampleRate * NumChannels * BitsPerSample/8
        var blockAlign = (ushort)(numChannels * bitsPerSample / 8); // NumChannels * BitsPerSample/8
        var subChunk2ID = "data";
        var subChunk2Size = (uint)(data.Length * clip.channels * bitsPerSample / 8); // NumSamples * NumChannels * BitsPerSample/8
        var chunkSize = (uint)(36 + subChunk2Size); // 36 + SubChunk2Size
        // Start writing the file.
        WriteString(stream, chunkID);
        WriteInteger(stream, chunkSize);
        WriteString(stream, format);
        WriteString(stream, subChunk1ID);
        WriteInteger(stream, subChunk1Size);
        WriteShort(stream, audioFormat);
        WriteShort(stream, numChannels);
        WriteInteger(stream, sampleRate);
        WriteInteger(stream, byteRate);
        WriteShort(stream, blockAlign);
        WriteShort(stream, bitsPerSample);
        WriteString(stream, subChunk2ID);
        WriteInteger(stream, subChunk2Size);
        foreach (var sample in data)
        {
            // De-normalize the samples to 16 bits.
            var deNormalizedSample = (short)0;
            if (sample > 0)
            {
                var temp = sample * short.MaxValue;
                if (temp > short.MaxValue)
                    temp = short.MaxValue;
                deNormalizedSample = (short)temp;
            }
            if (sample < 0)
            {
                var temp = sample * (-short.MinValue);
                if (temp < short.MinValue)
                    temp = short.MinValue;
                deNormalizedSample = (short)temp;
            }
            WriteShort(stream, (ushort)deNormalizedSample);
        }
    }
}

void WriteString(Stream stream, string value)
{
    foreach (var character in value)
        stream.WriteByte((byte)character);
}

void WriteInteger(Stream stream, uint value)
{
    stream.WriteByte((byte)(value & 0xFF));
    stream.WriteByte((byte)((value >> 8) & 0xFF));
    stream.WriteByte((byte)((value >> 16) & 0xFF));
    stream.WriteByte((byte)((value >> 24) & 0xFF));
}

void WriteShort(Stream stream, ushort value)
{
    stream.WriteByte((byte)(value & 0xFF));
    stream.WriteByte((byte)((value >> 8) & 0xFF));
}