ict.shenzhi/Assets/Evereal/VideoCapture/Scripts/Internal/Encoder/WavEncoder.cs

142 lines
3.9 KiB
C#

/* Copyright (c) 2019-present Evereal. All rights reserved. */
using System;
using System.IO;
using System.Text;
using UnityEngine;
namespace Evereal.VideoCapture
{
public static class WavEncoder
{
private const uint HeaderSize = 44;
private const float RescaleFactor = 32767; //to convert float to Int16
public static void Save(string filepath, AudioClip clip, bool trim = false)
{
using (var fileStream = new FileStream(filepath, FileMode.Create))
using (var writer = new BinaryWriter(fileStream))
{
uint length;
var wav = GetWav(clip, out length, trim);
writer.Write(wav, 0, (int)length);
}
}
public static byte[] GetWav(AudioClip clip, out uint length, bool trim = false)
{
uint samples;
var data = ConvertAndWrite(clip, out length, out samples, trim);
WriteHeader(data, clip, length, samples);
return data;
}
private static byte[] ConvertAndWrite(AudioClip clip, out uint length, out uint samplesAfterTrimming, bool trim)
{
var samples = new float[clip.samples * clip.channels];
clip.GetData(samples, 0);
var sampleCount = samples.Length;
var start = 0;
var end = sampleCount - 1;
if (trim)
{
for (var i = 0; i < sampleCount; i++)
{
if ((short)(samples[i] * RescaleFactor) == 0)
continue;
start = i;
break;
}
for (var i = sampleCount - 1; i >= 0; i--)
{
if ((short)(samples[i] * RescaleFactor) == 0)
continue;
end = i;
break;
}
}
var buffer = new byte[(sampleCount * 2) + HeaderSize];
var p = HeaderSize;
for (var i = start; i <= end; i++)
{
var value = (short)(samples[i] * RescaleFactor);
buffer[p++] = (byte)(value >> 0);
buffer[p++] = (byte)(value >> 8);
}
length = p;
samplesAfterTrimming = (uint)(end - start + 1);
return buffer;
}
private static void AddDataToBuffer(byte[] buffer, ref uint offset, byte[] addBytes)
{
foreach (var b in addBytes)
{
buffer[offset++] = b;
}
}
private static void WriteHeader(byte[] stream, AudioClip clip, uint length, uint samples)
{
var hz = (uint)clip.frequency;
var channels = (ushort)clip.channels;
var offset = 0u;
var riff = Encoding.UTF8.GetBytes("RIFF");
AddDataToBuffer(stream, ref offset, riff);
var chunkSize = BitConverter.GetBytes(length - 8);
AddDataToBuffer(stream, ref offset, chunkSize);
var wave = Encoding.UTF8.GetBytes("WAVE");
AddDataToBuffer(stream, ref offset, wave);
var fmt = Encoding.UTF8.GetBytes("fmt ");
AddDataToBuffer(stream, ref offset, fmt);
var subChunk1 = BitConverter.GetBytes(16u);
AddDataToBuffer(stream, ref offset, subChunk1);
//const ushort two = 2;
const ushort one = 1;
var audioFormat = BitConverter.GetBytes(one);
AddDataToBuffer(stream, ref offset, audioFormat);
var numChannels = BitConverter.GetBytes(channels);
AddDataToBuffer(stream, ref offset, numChannels);
var sampleRate = BitConverter.GetBytes(hz);
AddDataToBuffer(stream, ref offset, sampleRate);
var byteRate = BitConverter.GetBytes(hz * channels * 2); // sampleRate * bytesPerSample*number of channels, here 44100*2*2
AddDataToBuffer(stream, ref offset, byteRate);
var blockAlign = (ushort)(channels * 2);
AddDataToBuffer(stream, ref offset, BitConverter.GetBytes(blockAlign));
ushort bps = 16;
var bitsPerSample = BitConverter.GetBytes(bps);
AddDataToBuffer(stream, ref offset, bitsPerSample);
var dataString = Encoding.UTF8.GetBytes("data");
AddDataToBuffer(stream, ref offset, dataString);
var subChunk2 = BitConverter.GetBytes(samples * 2);
AddDataToBuffer(stream, ref offset, subChunk2);
}
}
}