Create a .WAV file from Unity AudioClip

↘锁芯ラ 提交于 2019-12-25 00:33:54

问题


So the problem is i want to create a .wav file from an Audioclip but i can't figure out how to do it. I've been looking for how a .wav file structure is but i'm not able to understand it as well as i want (Also i don't get how to get all the data from an Audioclip and convert it into bytes and set it to the file).

Here's what i've been trying so far:

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

回答1:


The problem here is that the float data is ranging from -1..1, and you're just casting that to a byte for 8 bits per sample, which means you're going to get mostly zerodata. You need to rescale that to occupy the full available numerical range, which for 8 bits (signed) means -128..127...

Try multiplying by 127 and casting to sbyte while writing.

Also, I believe PCM multi-channel WAV files are interleaved. So putting in all the data from one channel and then all the data from the other channel (as it is stored in memory) would not work. You actually have to alternate bytes between channels.




回答2:


I just coded this up today, for fun. I figured someone else wanted to make the conversion, too. That's how I came across your post. It can be a little bit tricky, but you need to follow the guide here. The specification is very picky, and you need to write the data in the correct endian byte order requested in the specification. Here is my code that will convert from an AudioClip to a 16-bit PCM Wave file format (Note: I only tested it on single channel audio from a microphone; perhaps someone else can double check if things are working for multiple channels as I suspect the odds look promising or you can make the tweaks and adjust accordingly):

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


来源:https://stackoverflow.com/questions/50864146/create-a-wav-file-from-unity-audioclip

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!