Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/265.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为内存中的流创建有效的wav文件头_C#_Audio_Wav_Pcm - Fatal编程技术网

C# 为内存中的流创建有效的wav文件头

C# 为内存中的流创建有效的wav文件头,c#,audio,wav,pcm,C#,Audio,Wav,Pcm,我的原始无头wav音频数据为MemoryStreams Stream rawAudioStream = Producer.GetRawAudioFileStream(...); 我知道这些数据流的数据格式: // WaveFormat(int rate, int bits, int channels); WaveFormat waveformat = new WaveFormat(8000, 16, 1); 我想要的是以编程方式为这些内存流添加正确的头信息,而不将它们写入物理文件 我该怎么做

我的原始无头wav音频数据为
MemoryStream
s

Stream rawAudioStream = Producer.GetRawAudioFileStream(...);
我知道这些数据流的数据格式:

// WaveFormat(int rate, int bits, int channels);
WaveFormat waveformat = new WaveFormat(8000, 16, 1);
我想要的是以编程方式为这些内存流添加正确的头信息,而不将它们写入物理文件

我该怎么做

PS:我检查了NAudio库,但只找到了一种通过将流写入真正的物理文件来创建头的方法,这不适合我的情况

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

下面的代码将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);
}

创建自定义类以封送原始数据很容易:

    [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();

CreateWaveFile()只是一种方便的方法。您还可以使用新操作符创建一个WaveFileWriter,它有一个接受流的构造函数。你可以传递一个记忆流。并不是说它有什么区别,它同样快。我如何从流中获取totalSampleCount?这是我能从长度中得到的东西?