欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#實現(xiàn)將音頻PCM數(shù)據(jù)封裝成wav文件

 更新時間:2023年10月10日 13:57:37   作者:CodeOfCC  
這篇文章主要為大家詳細(xì)介紹了C#如何實現(xiàn)將音頻PCM數(shù)據(jù)封裝成wav文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言

之前實現(xiàn)了《C++ 將音頻PCM數(shù)據(jù)封裝成wav文件》,最近將其改成了C#版本。使用C#實現(xiàn)錄音功能時還是需要寫wav文件的,直接用C#實現(xiàn)也是比較簡單的,這樣可以免去了不必要的依賴。

一、如何實現(xiàn)

首先需要構(gòu)造wav頭部,wav文件音頻信息全部保存在頭部,我們要做的就是在PCM數(shù)據(jù)的前面加入wav頭,并且記錄PCM的相關(guān)參數(shù)。

1.定義頭結(jié)構(gòu)

只定義PCM格式的wav文件頭,包含3部分riff、format、data。需要使用[StructLayout(LayoutKind.Sequential)]描述結(jié)構(gòu)體,確保內(nèi)存連續(xù)。

WAV頭部

//WAV頭部結(jié)構(gòu)-PCM格式
[StructLayout(LayoutKind.Sequential)]
struct WavPCMFileHeader
{
    RIFF riff=new RIFF();
    Format format = new Format();
    Data data = new Data();
    public WavPCMFileHeader() { }
}

RTTF部分

[StructLayout(LayoutKind.Sequential)]
struct RIFF
{
    byte r = (byte)'R';
    byte i = (byte)'I';
    byte f = (byte)'F';
    byte t = (byte)'F';
    public uint fileLength = 0;
    byte w = (byte)'W';
    byte a = (byte)'A';
    byte v = (byte)'V';
    byte e = (byte)'E';
    public RIFF() { }
}

Format部分

[StructLayout(LayoutKind.Sequential)]
struct Format
{
    byte f = (byte)'f';
    byte m = (byte)'m';
    byte t = (byte)'t';
    byte s = (byte)' ';
    public uint blockSize = 16;
    public ushort formatTag=0;
    public ushort channels = 0;
    public uint samplesPerSec = 0;
    public uint avgBytesPerSec = 0;
    public ushort blockAlign = 0;
    public ushort bitsPerSample = 0;
    public Format() { }
}

Data部分

[StructLayout(LayoutKind.Sequential)]
struct Data
{
    byte d = (byte)'d';
    byte a = (byte)'a';
    byte t = (byte)'t';
    byte a2 = (byte)'a';
    public uint dataLength=0;
    public Data() { }
}

2.預(yù)留頭部空間

創(chuàng)建文件時預(yù)留頭部空間

_stream = File.Open(fileName, FileMode.Create);
_stream!.Seek(Marshal.SizeOf<WavPCMFileHeader>(), SeekOrigin.Begin);

3.寫入PCM數(shù)據(jù)

寫入數(shù)據(jù)

_stream!.Write(data);

4.寫入頭部信息

關(guān)閉文件時,回到起始位置寫入頭部信息

//寫入頭部信息
_stream!.Seek(0, SeekOrigin.Begin);
WavPCMFileHeader h = new WavPCMFileHeader(_channels, _sampleRate, _bitsPerSample, (uint)(_stream.Length - Marshal.SizeOf<WavPCMFileHeader>()));
_stream!.Write(StructToBytes(h));
_stream!.Close();
_stream = null;

二、完整代碼

.net6.0WavWriter.cs

using System.Runtime.InteropServices;
/************************************************************************
* @Project:  	AC::WavWriter
* @Decription:  wav文件寫入工具
* @Verision:  	v1.0.0.0
* @Author:  	Xin Nie
* @Create:  	2023/10/8 09:27:00
* @LastUpdate:  2023/10/8 18:28:00
************************************************************************
* Copyright @ 2025. All rights reserved.
************************************************************************/
namespace AC
{  
    /// <summary>
    /// wav寫入工具,目前只支持pcm格式
    /// </summary>
    public class WavWriter:IDisposable
    {
        ushort _channels;
        uint _sampleRate;
        ushort _bitsPerSample;
        FileStream? _stream;
        /// <summary>
        /// 創(chuàng)建對象
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="channels">聲道數(shù)</param>
        /// <param name="sampleRate">采樣率,單位hz</param>
        /// <param name="bitsPerSample">位深</param>
        public static WavWriter Create(string fileName, ushort channels, uint sampleRate, ushort bitsPerSample)
        {
          return new WavWriter(fileName, channels, sampleRate, bitsPerSample);       
        }
        /// <summary>
        /// 構(gòu)造方法
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="channels">聲道數(shù)</param>
        /// <param name="sampleRate">采樣率,單位hz</param>
        /// <param name="bitsPerSample">位深</param>
          WavWriter(string fileName, ushort channels, uint sampleRate, ushort bitsPerSample)
        {
            _stream = File.Open(fileName, FileMode.Create);
            _channels = channels;
            _sampleRate = sampleRate;
            _bitsPerSample = bitsPerSample;
            _stream!.Seek(Marshal.SizeOf<WavPCMFileHeader>(), SeekOrigin.Begin);
        }
        /// <summary>
        /// 寫入PCM數(shù)據(jù)
        /// </summary>
        /// <param name="data">PCM數(shù)據(jù)</param>
        public void Write(byte[] data)
        {
            _stream!.Write(data);
        }
        /// <summary>
        /// 寫入PCM數(shù)據(jù)
        /// </summary>
        /// <param name="stream">PCM數(shù)據(jù)</param>
        public void Write(Stream stream)
        {
            stream.CopyTo(_stream!);
        }
        /// <summary>
        /// 關(guān)閉文件
        /// </summary>
        public void Close()
        {
            //寫入頭部信息
            _stream!.Seek(0, SeekOrigin.Begin);
            WavPCMFileHeader h = new WavPCMFileHeader(_channels, _sampleRate, _bitsPerSample, (uint)(_stream.Length - Marshal.SizeOf<WavPCMFileHeader>()));
            _stream!.Write(StructToBytes(h));
            _stream!.Close();
            _stream = null;
        }
        public void Dispose()
        {
            Close();
        }
        static byte[] StructToBytes<T>(T obj)
        {
            int size = Marshal.SizeOf(typeof(T));
            IntPtr bufferPtr = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.StructureToPtr(obj!, bufferPtr, false);
                byte[] bytes = new byte[size];
                Marshal.Copy(bufferPtr, bytes, 0, size);
                return bytes;
            }
            catch (Exception ex)
            {
                throw new Exception("Error in StructToBytes ! " + ex.Message);
            }
            finally
            {
                Marshal.FreeHGlobal(bufferPtr);
            }
        }     
    }
    //WAV頭部結(jié)構(gòu)-PCM格式
    [StructLayout(LayoutKind.Sequential)]
    struct WavPCMFileHeader
    {
        [StructLayout(LayoutKind.Sequential)]
        struct RIFF
        {
            byte r = (byte)'R';
            byte i = (byte)'I';
            byte f = (byte)'F';
            byte t = (byte)'F';
            public uint fileLength = 0;
            byte w = (byte)'W';
            byte a = (byte)'A';
            byte v = (byte)'V';
            byte e = (byte)'E';
            public RIFF() { }
        }
        [StructLayout(LayoutKind.Sequential)]
        struct Format
        {
            byte f = (byte)'f';
            byte m = (byte)'m';
            byte t = (byte)'t';
            byte s = (byte)' ';
            public uint blockSize = 16;
            public ushort formatTag=0;
            public ushort channels = 0;
            public uint samplesPerSec = 0;
            public uint avgBytesPerSec = 0;
            public ushort blockAlign = 0;
            public ushort bitsPerSample = 0;
            public Format() { }
        }
        [StructLayout(LayoutKind.Sequential)]
        struct Data
        {
            byte d = (byte)'d';
            byte a = (byte)'a';
            byte t = (byte)'t';
            byte a2 = (byte)'a';
            public uint dataLength=0;
            public Data() { }
        }
        RIFF riff=new RIFF();
        Format format = new Format();
        Data data = new Data();
        public WavPCMFileHeader() { }
        public WavPCMFileHeader(ushort nCh, uint nSampleRate, ushort bitsPerSample, uint dataSize)
        {
            riff.fileLength = (uint)(36 + dataSize);
            format.formatTag = 1;
            format.channels = nCh;
            format.samplesPerSec = nSampleRate;
            format.avgBytesPerSec = nSampleRate * nCh * bitsPerSample / 8;
            format.blockAlign = (ushort)(nCh * bitsPerSample / 8);
            format.bitsPerSample = bitsPerSample;
            data.dataLength = dataSize;
        }
    };
}

三、使用示例

using AC;
try
{
    using (var ww = WavWriter.Create("test.wav", 2, 44100, 16))
    {
        byte[]data;
        //獲取PCM數(shù)據(jù)
		//略
		//獲取PCM數(shù)據(jù)-end
	    //寫入PCM數(shù)據(jù)
	    ww.Write(data);
    }
}
catch (Exception e)
{
    Console.WriteLine(e.Message);
}

總結(jié)

PCM封裝成wav還是相對較簡單的,只要了解wav頭結(jié)構(gòu),然后自定義其頭結(jié)構(gòu),然后再進(jìn)行一定的測試,就可以實現(xiàn)這樣一個功能。

以上就是C#實現(xiàn)將音頻PCM數(shù)據(jù)封裝成wav文件的詳細(xì)內(nèi)容,更多關(guān)于C# PCM數(shù)據(jù)封裝成wav的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C#定時任務(wù)框架Quartz.NET介紹與用法

    C#定時任務(wù)框架Quartz.NET介紹與用法

    這篇文章介紹了C#定時任務(wù)框架Quartz.NET的用法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02
  • NPOI實現(xiàn)兩級分組合并功能(示例講解)

    NPOI實現(xiàn)兩級分組合并功能(示例講解)

    下面小編就為大家分享一篇NPOI實現(xiàn)兩級分組合并功能的示例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • c# datetime方法應(yīng)用介紹

    c# datetime方法應(yīng)用介紹

    本文將詳細(xì)介紹c# datetime方法應(yīng)用,需要了解更多的朋友可以參考下
    2012-11-11
  • C# InitializeComponent()方法案例詳解

    C# InitializeComponent()方法案例詳解

    這篇文章主要介紹了C# InitializeComponent()方法案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • C#調(diào)用C++動態(tài)庫接口函數(shù)和回調(diào)函數(shù)方法

    C#調(diào)用C++動態(tài)庫接口函數(shù)和回調(diào)函數(shù)方法

    這篇文章主要介紹了C#調(diào)用C++動態(tài)庫接口函數(shù)和回調(diào)函數(shù)方法,通過C++端編寫接口展開內(nèi)容,文章介紹詳細(xì)具有一定的參考價值,需要的小伙伴可以參考一下
    2022-03-03
  • 深入理解C# 委托與事件

    深入理解C# 委托與事件

    本文主要介紹了深入理解C# 委托與事件,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2008-05-05
  • C# 設(shè)計模式系列教程-命令模式

    C# 設(shè)計模式系列教程-命令模式

    在軟件系統(tǒng)中,行為請求者與行為實現(xiàn)者通常是一種緊耦合的關(guān)系,但某些場合,比如需要對行為進(jìn)行記錄、撤銷或重做、事務(wù)等處理時,這種無法抵御變化的緊耦合的設(shè)計就不太合適。
    2016-06-06
  • 在C#中對TCP客戶端的狀態(tài)封裝詳解

    在C#中對TCP客戶端的狀態(tài)封裝詳解

    本篇文章小編為大家介紹,在C#中對TCP客戶端的狀態(tài)封裝詳解,需要的朋友參考下
    2013-04-04
  • C#從實體對象集合中導(dǎo)出Excel的代碼

    C#從實體對象集合中導(dǎo)出Excel的代碼

    數(shù)據(jù)的導(dǎo)出是項目中經(jīng)常要實現(xiàn)的功能,就拿最常見的要導(dǎo)出成Excel來說,網(wǎng)上看來看去,都是介紹從Datatable中導(dǎo)出
    2008-08-08
  • C#執(zhí)行EXE文件與輸出消息的提取操作

    C#執(zhí)行EXE文件與輸出消息的提取操作

    這篇文章主要介紹了C#執(zhí)行EXE文件與輸出消息的提取操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04

最新評論