C#实现Wav文件截取、淡入淡出处理(PCM 格式)

因工具需要 对 Wav 文件做处理 包含转格 截取 淡入淡出处理 ,此处将代码记下 以备后用。

View Code
  1   /// <summary>
  2     /// 文件截取处理
  3     /// </summary>
  4     public class WavScissorHelper
  5     {
  6         public static bool GetWavFileScissor(string OriginalAudioFilePath, string DestinedAudioFilePath, int beginCutTime, int endCutTime, ref string errorInfo)
  7         {
  8             if (!File.Exists(OriginalAudioFilePath))
  9             {
 10                 errorInfo = string.Format("源文件:[{0}]不存在!", OriginalAudioFilePath);
 11                 return false;
 12             }
 13             if (!Directory.Exists(Path.GetDirectoryName(DestinedAudioFilePath)))
 14             {
 15                 errorInfo = string.Format("文件夹:[{0}]不存在!", Path.GetDirectoryName(DestinedAudioFilePath));
 16                 return false;
 17             }
 18             if ((beginCutTime < 0) || (endCutTime < 0)
 19                 || (beginCutTime > endCutTime))
 20             {
 21                 errorInfo = string.Format("截取时间[{0}][{1}]有误!", beginCutTime, endCutTime);
 22                 return false;
 23             }
 24             try
 25             {
 26                 WavFile oldFile = new WavFile(OriginalAudioFilePath);
 27                 if (endCutTime > oldFile.PlayTime)
 28                 {
 29                     errorInfo = string.Format("结束截取时间[{0}]大于音频文件播放时间[{1}]!", endCutTime, oldFile.PlayTime);
 30                     return false;
 31                 }
 32                 int timeSpan = endCutTime - beginCutTime;
 33 
 34                 WavFile newFile = new WavFile(DestinedAudioFilePath);
 35                 newFile.WavFormat = oldFile.WavFormat;
 36 
 37                 byte[] newAudioBytes = new byte[oldFile.WavFormat.ByteRate * timeSpan];
 38                 Array.Copy(oldFile.AudioDataBytes, oldFile.WavFormat.ByteRate * beginCutTime, newAudioBytes, 0, newAudioBytes.Length);
 39 
 40                 newFile.WriteWavFile(newAudioBytes);
 41                 return true;
 42             }
 43             catch (Exception e)
 44             {
 45                 return false;
 46 
 47             }
 48         }
 49 
 50     }
 51 
 52     /// <summary>
 53     /// 淡入淡出处理
 54     /// </summary>
 55     public class WavFadeHelper
 56     {
 57         private WavFile _wavFile;
 58         
 59         public WavFadeHelper(string wavFilePath)
 60         {
 61             _wavFile = new WavFile(wavFilePath);
 62         }
 63 
 64 
 65         public bool FadeAtFirstSec(FadeMode fadeMode, long fadeLength)
 66         {
 67             return FadeAtSec(fadeMode, 0, fadeLength);
 68         }
 69 
 70         public bool FadeAtEndSec(FadeMode fadeMode, long fadeLength)
 71         {
 72             long totalTime = _wavFile.PlayTime;
 73             if (totalTime < fadeLength)
 74                 fadeLength = totalTime;
 75             long startTime = totalTime - fadeLength;
 76 
 77             return FadeAtSec(fadeMode, startTime, startTime + fadeLength);
 78         }
 79 
 80         public bool FadeAtSec(FadeMode fadeMode, long startSec, long endSec)
 81         {
 82             //总播放时间
 83             long totalTime = _wavFile.PlayTime;
 84 
 85             //采样位数        
 86             // 16 bit 纵坐标为采样系数 细化为 2^16= 65535份 16位二进制表示 [ 0000 0000 0000 0000 ]
 87             // 即为2Byte (每单位时间内产生 2Byte 的音频数据)
 88 
 89             int byteNumPerSample = _wavFile.WavFormat.BitsPerSample / 8;
 90 
 91             //采样频率
 92             // 11025Hz 横坐标为采样频率 单位时间为: 1/11025 s           
 93             //单位时间内产生的的音频数据大小为 2Byte * 11025Hz * 声道数           
 94 
 95 
 96             //audioData 是按字节处理 16bit采样 每次获取两个字节进行处理
 97 
 98             //开始位置 存储单元索引
 99             long startIndex = startSec * byteNumPerSample * _wavFile.WavFormat.SampleRate;
100             if (startIndex % byteNumPerSample > 0)
101                 startIndex -= startIndex % byteNumPerSample;
102 
103             //结束位置 存储单元索引
104             long endIndex = endSec * byteNumPerSample * _wavFile.WavFormat.SampleRate;
105             if (endIndex > _wavFile.AudioDataBytes.Length - 1)
106                 endIndex = _wavFile.AudioDataBytes.Length - 1;
107 
108             //字节数组 音频数据
109             byte[] audioDatas = _wavFile.AudioDataBytes;
110 
111             //初始衰减率
112             double rate = 1.0;
113 
114             //对每个单位时间内的 样点幅值 进行衰减
115             //从淡化开始时间到结束时间共有 [ 采样频率 * 时间段(s) * 采样位数/8(Byte) ] 个存储单元 
116             //每次取出 采样位数/8(Byte) 个存储单元进行衰减
117 
118             // i 的值为每次衰减处理时的 字节读取位置 (索引)
119             for (int i = (int)startIndex; i < endIndex; i += byteNumPerSample)
120             {
121                 //衰减率的方向与淡入淡出有关
122                 //衰减率的大小与当前读取处理字节的索引位置有关
123                 if (fadeMode == FadeMode.FadeOut)
124                     rate = (double)(endIndex - i) / (endIndex - startIndex);
125                 else
126                     rate = (double)(i - startIndex) / (endIndex - startIndex);
127 
128                 //单位时间内的字节数为1 即采用8bit采样位数  每次只需要对一个字节进行衰减 
129                 if (byteNumPerSample == 1)
130                 {
131                     //获取需要衰减的字节值
132                     byte audioData = audioDatas[i];
133 
134                     //进行衰减线性处理
135                     byte changedAudioData = (byte)((double)audioData * rate);
136                     //byte changedAudioData = Convert.ToByte((double)audioData * rate);
137 
138                     //对原来的值进行更改
139                     audioDatas[i] = changedAudioData;
140                 }
141                 else if (byteNumPerSample == 2)
142                 {
143                     //16bit 量化 每单位时间产生2Byte数据 因此取 2Byte 的数据进行衰减处理
144                     byte[] audioData = new byte[2];
145                     Array.Copy(audioDatas, i, audioData, 0, 2);
146 
147                     //单个样点幅值
148                     double sample = 0;
149 
150                     //转换为整数进行线性处理
151                     sample = (double)BitConverter.ToInt16(audioData, 0) * rate;
152 
153                     //转换为字节存储
154                     byte[] buffer = BitConverter.GetBytes(Convert.ToInt16(sample));
155                     if (!BitConverter.IsLittleEndian)
156                         Array.Reverse(buffer);
157 
158                     Array.Copy(buffer, 0, audioDatas, i, 2);
159                 }
160             }
161             _wavFile.WriteWavFile(audioDatas);
162 
163             return true;
164         }
165     }
166 
167     /// <summary>
168     /// 淡入淡出模式
169     /// </summary>
170     public enum FadeMode
171     {
172         FadeIn,
173         FadeOut
174     }
175 
176     /// <summary>
177     /// WAV 文件
178     /// </summary>
179     public class WavFile
180     {
181         private WavFormat _wavFormat;   //文件格式
182         private long _fileLength;       //文件长度
183         private string _filePath;       //文件路径
184         private byte[] _audioData;      //语音数据
185 
186         private WavFile() { }
187         public WavFile(string filePath)
188         {
189             _filePath = filePath;
190         }
191 
192         /// <summary>
193         /// 文件格式
194         /// </summary>
195         public WavFormat WavFormat
196         {
197             get
198             {
199                 if (_wavFormat == null)
200                     _wavFormat = GetWavFormat();
201 
202                 return _wavFormat;
203             }
204             set { _wavFormat = value; }
205         }
206 
207         /// <summary>
208         /// 文件大小
209         /// </summary>
210         public long FileLength
211         {
212             get
213             {
214                 if (_wavFormat == null)
215                     _wavFormat = GetWavFormat();
216 
217                 return _fileLength;
218             }
219             set { _fileLength = value; }
220         }
221 
222         /// <summary>
223         /// 播放时长
224         /// </summary>
225         public long PlayTime
226         {
227             get
228             {
229                 if (_wavFormat == null)
230                     _wavFormat = GetWavFormat();
231 
232                 return _audioData.Length / _wavFormat.ByteRate;
233             }
234         }
235 
236         /// <summary>
237         /// 语音数据
238         /// </summary>
239         public byte[] AudioDataBytes
240         {
241             get
242             {
243                 if (_wavFormat == null)
244                     _wavFormat = GetWavFormat();
245 
246                 return _audioData;
247             }
248             set { _audioData = value; }
249         }
250 
251         /// <summary>
252         /// 设置Wav文件格式
253         /// </summary>
254         /// <param name="bitsPerSample">采样位数</param>
255         /// <param name="channels">声道数</param>
256         /// <param name="sampleRate">采样率</param>
257         public void SetWavFormat(int bitsPerSample, int channels, int sampleRate)
258         {
259             _wavFormat = new WavFormat();
260             _wavFormat.BitsPerSample = bitsPerSample;
261             _wavFormat.Channels = channels;
262             _wavFormat.SampleRate = sampleRate;
263         }
264 
265         /// <summary>
266         /// 写文件
267         /// </summary>
268         /// <param name="audioData">音频数据</param>
269         public void WriteWavFile(byte[] audioData)
270         {
271             WriteWavFile(_wavFormat, audioData, 0, audioData.Length);
272         }
273 
274         /// <summary>
275         /// 写文件
276         /// </summary>
277         /// <param name="wavFormat">文件格式</param>
278         /// <param name="audioData">音频数据</param>
279         /// <param name="startIndex">audioData数组开始索引位置</param>
280         /// <param name="length">写入audioData数组长度</param>
281         public void WriteWavFile(WavFormat wavFormat, byte[] audioData, int startIndex, int length)
282         {
283             FileStream fs = null;
284             BinaryWriter bw = null;
285             try
286             {
287                 fs = new FileStream(_filePath, FileMode.Create, FileAccess.Write);
288                 bw = new BinaryWriter(fs);
289                 fs.Position = 0;
290                 bw.Write(new char[4] { 'R', 'I', 'F', 'F' });
291                 //ChunkFileSize
292                 bw.Write((int)(length + 44 - 8));
293                 bw.Write(new char[8] { 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ' });
294                 bw.Write((int)16);
295                 bw.Write((short)1);
296                 bw.Write((short)wavFormat.Channels);
297                 bw.Write(wavFormat.SampleRate);
298                 bw.Write((int)(wavFormat.SampleRate * ((wavFormat.BitsPerSample * wavFormat.Channels) / 8)));
299                 bw.Write((short)((wavFormat.BitsPerSample * wavFormat.Channels) / 8));
300                 bw.Write((short)wavFormat.BitsPerSample);
301                 bw.Write(new char[4] { 'd', 'a', 't', 'a' });
302                 bw.Write(length);
303                 bw.Write(audioData, startIndex, length);
304             }
305             finally
306             {
307                 if (bw != null)
308                     bw.Close();
309                 if (fs != null)
310                 {
311                     fs.Close();
312                     fs.Dispose();
313                 }
314             }
315         }
316 
317         /// <summary>
318         /// 获取Wav文件格式
319         /// </summary>
320         /// <returns></returns>
321         private WavFormat GetWavFormat()
322         {
323             FileStream fs = null;
324             BinaryReader br = null;
325             WavFormat wavFormat = new WavFormat();
326 
327             try
328             {
329                 fs = new FileStream(_filePath, FileMode.Open, FileAccess.Read);
330                 br = new BinaryReader(fs);
331 
332                 _fileLength = fs.Length;
333 
334                 fs.Position = 22;
335                 wavFormat.Channels = br.ReadInt16();
336                 fs.Position = 24;
337                 wavFormat.SampleRate = br.ReadInt32();
338                 fs.Position = 28;
339                 wavFormat.ByteRate = br.ReadInt32();
340                 fs.Position = 34;
341                 wavFormat.BitsPerSample = br.ReadInt16();
342 
343                 //The audio data
344                 fs.Position = 44;
345                 int dataByteSize = (int)(fs.Length - 44);
346                 if (_audioData == null)
347                     _audioData = new byte[dataByteSize];
348                 fs.Read(_audioData, 0, dataByteSize);
349 
350             }
351             finally
352             {
353                 if (br != null)
354                     br.Close();
355                 if (fs != null)
356                 {
357                     fs.Close();
358                     fs.Dispose();
359                 }
360             }
361             return wavFormat;
362         }
363     }
364 
365     /// <summary>
366     /// WAV 文件格式
367     /// </summary>
368     public class WavFormat
369     {
370         /// <summary>
371         /// 采样位数
372         /// </summary>
373         public int BitsPerSample;
374         /// <summary>
375         /// 声道数
376         /// </summary>
377         public int Channels;
378         /// <summary>
379         /// 采样率
380         /// </summary>
381         public int SampleRate;
382         /// <summary>
383         /// 传输速率(播放速率)
384         /// </summary>
385         public long ByteRate;
386     }
原文地址:https://www.cnblogs.com/ryhan/p/2453988.html