问题
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