我将原始无头 wav 音频数据设为MemoryStream
。
Stream rawAudioStream = Producer.GetRawAudioFileStream(...);
我知道那些数据流格式:
// WaveFormat(int rate, int bits, int channels);
WaveFormat waveformat = new WaveFormat(8000, 16, 1);
我想要的是以编程方式为这些内存流添加正确的标头信息,而无需将它们写入物理文件。
我该怎么做?
PS:我检查了NAudio库,但只找到了一种方法来创建标题,方法是将流写入真实物理文件,不适合我的情况。< / p>
var waveformat = new WaveFormat(8000,16,1);
var reader = new RawSourceWaveStream(rawAudioMemStream, waveformat);
using (var convertedStream = WaveFormatConversionStream.CreatePcmStream(reader))
{
WaveFileWriter.CreateWaveFile(fileName, convertedStream);
}
rawAudioMemStream.Close();
答案 0 :(得分:12)
以下代码会将Wav标头写入MemoryStream
的开头。这意味着您需要先将标题写入流 ,然后才能编写样本。否则,流的开头的样本将被元数据覆盖
// totalSampleCount needs to be the combined count of samples of all channels. So if the left and right channels contain 1000 samples each, then totalSampleCount should be 2000.
// isFloatingPoint should only be true if the audio data is in 32-bit floating-point format.
private void WriteWavHeader(MemoryStream stream, bool isFloatingPoint, ushort channelCount, ushort bitDepth, int sampleRate, int totalSampleCount)
{
stream.Position = 0;
// RIFF header.
// Chunk ID.
stream.Write(Encoding.ASCII.GetBytes("RIFF"), 0, 4);
// Chunk size.
stream.Write(BitConverter.GetBytes(((bitDepth / 8) * totalSampleCount) + 36), 0, 4);
// Format.
stream.Write(Encoding.ASCII.GetBytes("WAVE"), 0, 4);
// Sub-chunk 1.
// Sub-chunk 1 ID.
stream.Write(Encoding.ASCII.GetBytes("fmt "), 0, 4);
// Sub-chunk 1 size.
stream.Write(BitConverter.GetBytes(16), 0, 4);
// Audio format (floating point (3) or PCM (1)). Any other format indicates compression.
stream.Write(BitConverter.GetBytes((ushort)(isFloatingPoint ? 3 : 1)), 0, 2);
// Channels.
stream.Write(BitConverter.GetBytes(channelCount), 0, 2);
// Sample rate.
stream.Write(BitConverter.GetBytes(sampleRate), 0, 4);
// Bytes rate.
stream.Write(BitConverter.GetBytes(sampleRate * channelCount * (bitDepth / 8)), 0, 4);
// Block align.
stream.Write(BitConverter.GetBytes((ushort)channelCount * (bitDepth / 8)), 0, 2);
// Bits per sample.
stream.Write(BitConverter.GetBytes(bitDepth), 0, 2);
// Sub-chunk 2.
// Sub-chunk 2 ID.
stream.Write(Encoding.ASCII.GetBytes("data"), 0, 4);
// Sub-chunk 2 size.
stream.Write(BitConverter.GetBytes((bitDepth / 8) * totalSampleCount), 0, 4);
}
答案 1 :(得分:0)
创建自定义类以封送原始数据很容易:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class WavePcmFormat
{
/* ChunkID Contains the letters "RIFF" in ASCII form */
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private char[] chunkID = new char[] { 'R', 'I', 'F', 'F' };
/* ChunkSize 36 + SubChunk2Size */
[MarshalAs(UnmanagedType.U4, SizeConst = 4)]
private uint chunkSize = 0;
/* Format The "WAVE" format name */
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private char[] format = new char[] { 'W', 'A', 'V', 'E' };
/* Subchunk1ID Contains the letters "fmt " */
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private char[] subchunk1ID = new char[] { 'f', 'm', 't', ' ' };
/* Subchunk1Size 16 for PCM */
[MarshalAs(UnmanagedType.U4, SizeConst = 4)]
private uint subchunk1Size = 16;
/* AudioFormat PCM = 1 (i.e. Linear quantization) */
[MarshalAs(UnmanagedType.U2, SizeConst = 2)]
private ushort audioFormat = 1;
/* NumChannels Mono = 1, Stereo = 2, etc. */
[MarshalAs(UnmanagedType.U2, SizeConst = 2)]
private ushort numChannels = 1;
public ushort NumChannels { get => numChannels; set => numChannels = value; }
/* SampleRate 8000, 44100, etc. */
[MarshalAs(UnmanagedType.U4, SizeConst = 4)]
private uint sampleRate = 44100;
public uint SampleRate { get => sampleRate; set => sampleRate = value; }
/* ByteRate == SampleRate * NumChannels * BitsPerSample/8 */
[MarshalAs(UnmanagedType.U4, SizeConst = 4)]
private uint byteRate = 0;
/* BlockAlign == NumChannels * BitsPerSample/8 */
[MarshalAs(UnmanagedType.U2, SizeConst = 2)]
private ushort blockAlign = 0;
/* BitsPerSample 8 bits = 8, 16 bits = 16, etc. */
[MarshalAs(UnmanagedType.U2, SizeConst = 2)]
private ushort bitsPerSample = 16;
public ushort BitsPerSample { get => bitsPerSample; set => bitsPerSample = value; }
/* Subchunk2ID Contains the letters "data" */
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private char[] subchunk2ID = new char[] { 'd', 'a', 't', 'a' };
/* Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8 */
[MarshalAs(UnmanagedType.U4, SizeConst = 4)]
private uint subchunk2Size = 0;
/* Data The actual sound data. */
public byte[] Data { get; set; } = new byte[0];
public WavePcmFormat(byte[] data, ushort numChannels = 2, uint sampleRate = 44100, ushort bitsPerSample = 16)
{
Data = data;
NumChannels = numChannels;
SampleRate = sampleRate;
BitsPerSample = bitsPerSample;
}
private void CalculateSizes()
{
subchunk2Size = (uint)Data.Length;
blockAlign = (ushort)(NumChannels * BitsPerSample / 8);
byteRate = SampleRate * NumChannels * BitsPerSample / 8;
chunkSize = 36 + subchunk2Size;
}
public byte[] ToBytesArray()
{
CalculateSizes();
int headerSize = Marshal.SizeOf(this);
IntPtr headerPtr = Marshal.AllocHGlobal(headerSize);
Marshal.StructureToPtr(this, headerPtr, false);
byte[] rawData = new byte[headerSize + Data.Length];
Marshal.Copy(headerPtr, rawData, 0, headerSize);
Marshal.FreeHGlobal(headerPtr);
Array.Copy(Data, 0, rawData, 44, Data.Length);
return rawData;
}
}
用法:
var wav = new WavePcmFormat(rawDataWithoutHeader, numChannels: 1, sampleRate: SampleRateHertz, bitsPerSample: 16);
var rawDataWithHeader = wav.ToBytesArray();