private AudioClip ParseWAV(string clipName, byte[] data) { MemoryStream stream = new MemoryStream(data, false); BinaryReader reader = new BinaryReader(stream); IFF_FORM_CHUNK form = ReadType <IFF_FORM_CHUNK>(reader); if (GetID(form.form_id) != "RIFF" || GetID(form.id) != "WAVE") { _lastError = String.Format("Malformed WAV header: {0} != RIFF || {1} != WAVE", GetID(form.form_id), GetID(form.id)); return(null); } WAV_PCM header = new WAV_PCM(); bool bHeaderFound = false; while (reader.BaseStream.Position < reader.BaseStream.Length) { IFF_CHUNK chunk = ReadType <IFF_CHUNK>(reader); int ChunkLength = (int)chunk.length; if (ChunkLength < 0) // HACK: Deal with TextToSpeech bug where the chunk length is not set for the data chunk.. { ChunkLength = (int)(reader.BaseStream.Length - reader.BaseStream.Position); } if ((ChunkLength & 0x1) != 0) { ChunkLength += 1; } long ChunkEnd = reader.BaseStream.Position + ChunkLength; if (GetID(chunk.id) == "fmt ") { bHeaderFound = true; header = ReadType <WAV_PCM>(reader); } else if (GetID(chunk.id) == "data") { if (!bHeaderFound) { _lastError = "Failed to find header."; return(null); } byte[] waveform = reader.ReadBytes(ChunkLength); // convert into a float based wave form.. int channels = (int)header.channels; int bps = (int)header.bits_per_sample; float divisor = 1 << (bps - 1); int bytesps = bps / 8; int samples = waveform.Length / bytesps; _lastError = string.Format("WAV INFO, channels = {0}, bps = {1}, samples = {2}, rate = {3}", channels, bps, samples, header.sample_rate); float[] wf = new float[samples]; if (bps == 16) { for (int s = 0; s < samples; ++s) { wf[s] = ((float)BitConverter.ToInt16(waveform, s * bytesps)) / divisor; } } else if (bps == 32) { for (int s = 0; s < samples; ++s) { wf[s] = ((float)BitConverter.ToInt32(waveform, s * bytesps)) / divisor; } } else if (bps == 8) { for (int s = 0; s < samples; ++s) { wf[s] = ((float)BitConverter.ToChar(waveform, s * bytesps)) / divisor; } } else { _lastError = string.Format("Unspported BPS {0} in WAV data.", bps.ToString()); return(null); } AudioClip clip = AudioClip.Create(clipName, samples, channels, (int)header.sample_rate, false); clip.SetData(wf, 0); return(clip); } reader.BaseStream.Position = ChunkEnd; } return(null); }
/// <summary> /// Creates a WAV file from a AudioClip object. /// </summary> /// <param name="clip">The AudioClip object to generate the WAV file from.</param> /// <param name="bps">How many bits per sample we should use in the WAV file, 8, 16, or 32.</param> /// <returns>Returns a byte array of the raw data of the WAV file.</returns> public static byte[] CreateWAV(AudioClip clip, int bps = 16) { MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); IFF_FORM_CHUNK form = new IFF_FORM_CHUNK(); form.form_id = MakeID("RIFF"); form.form_length = 0xffffffff; form.id = MakeID("WAVE"); WriteType(writer, form); long form_start = writer.BaseStream.Position; float divisor = 1 << (bps - 1); WAV_PCM header = new WAV_PCM(); header.format_tag = 1; header.alignment = 2; header.bits_per_sample = (ushort)bps; header.sample_rate = (uint)clip.frequency; header.channels = (ushort)clip.channels; header.average_data_rate = (uint)((bps / 8) * clip.channels * clip.frequency); IFF_CHUNK fmt = new IFF_CHUNK(); fmt.id = MakeID("fmt "); fmt.length = (uint)Marshal.SizeOf(header); WriteType(writer, fmt); WriteType(writer, header); IFF_CHUNK data = new IFF_CHUNK(); data.id = MakeID("data"); data.length = (uint)((bps / 8) * clip.samples * clip.channels); WriteType(writer, data); float[] samples = new float[clip.samples * clip.channels]; clip.GetData(samples, 0); if (bps == 16) { for (int i = 0; i < samples.Length; ++i) { writer.Write((short)(samples[i] * divisor)); } } else if (bps == 32) { for (int i = 0; i < samples.Length; ++i) { writer.Write((int)(samples[i] * divisor)); } } else if (bps == 8) { for (int i = 0; i < samples.Length; ++i) { writer.Write((char)(samples[i] * divisor)); } } else { Log.Error("WaveFile.CreateWAV()", "Unsupported BPS {0} in WAV data.", bps.ToString()); return(null); } // lastly, update the form length.. form.form_length = (uint)(writer.BaseStream.Position - form_start); writer.Seek(0, SeekOrigin.Begin); WriteType(writer, form); #if NETFX_CORE ArraySegment <byte> bytes; if (stream.TryGetBuffer(out bytes)) { return(bytes.Array); } else { return(null); } #else return(stream.GetBuffer()); #endif }