• C#基于NAudio工具对Wav音频文件剪切(限PCM格式)


    C#基于NAudio工具对Wav音频文件进行剪切,将一个音频文件剪切成多个音频文件

    注:调用方法前需要导入NAudio.dll或者在NuGet程序管理器搜索NAudio并安装

    本文是按时间剪切,直接撸代码:

    using NAudio.Wave;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace XXX.util
    {
        public static class WavFileUtils
        {
            /// <summary>
            /// 基于NAudio工具对Wav音频文件剪切(限PCM格式)
            /// </summary>
            /// <param name="inPath">目标文件</param>
            /// <param name="outPath">输出文件</param>
            /// <param name="cutFromStart">开始时间</param>
            /// <param name="cutFromEnd">结束时间</param>
            public static void TrimWavFile(string inPath, string outPath, TimeSpan cutFromStart, TimeSpan cutFromEnd)
            {
                using (WaveFileReader reader = new WaveFileReader(inPath))
                {
                    int fileLength = (int)reader.Length;using (WaveFileWriter writer = new WaveFileWriter(outPath, reader.WaveFormat))
                    {
                        float bytesPerMillisecond = reader.WaveFormat.AverageBytesPerSecond / 1000f;
                        int startPos = (int)Math.Round(cutFromStart.TotalMilliseconds * bytesPerMillisecond);
                        startPos = startPos - startPos % reader.WaveFormat.BlockAlign;
                        int endPos = (int)Math.Round(cutFromEnd.TotalMilliseconds * bytesPerMillisecond);
                        endPos = endPos - endPos % reader.WaveFormat.BlockAlign;
                        //判断结束位置是否越界
                        endPos = endPos > fileLength ? fileLength : endPos;
                        TrimWavFile(reader, writer, startPos, endPos);
                    }
                }
            }
    
            /// <summary>
            /// 重新合并wav文件
            /// </summary>
            /// <param name="reader">读取流</param>
            /// <param name="writer">写入流</param>
            /// <param name="startPos">开始流</param>
            /// <param name="endPos">结束流</param>
            private static void TrimWavFile(WaveFileReader reader, WaveFileWriter writer, int startPos, int endPos)
            {
                reader.Position = startPos;
                byte[] buffer = new byte[1024];
                while (reader.Position < endPos)
                {
                    int bytesRequired = (int)(endPos - reader.Position);
                    if (bytesRequired > 0)
                    {
                        int bytesToRead = Math.Min(bytesRequired, buffer.Length);
                        int bytesRead = reader.Read(buffer, 0, bytesToRead);
                        if (bytesRead > 0)
                        {
                            writer.Write(buffer, 0, bytesRead);
                        }
                    }
                }
            }
    
            /// <summary>
            /// 基于NAudio工具对MP3音频文件剪切指定片段(补充)
            /// </summary>
            /// <param name="inputPath">目标文件路径</param>
            /// <param name="outputPath">输出文件路径</param>
            /// <param name="begin">开始时间</param>
            /// <param name="end">结束时间</param>
            public static void TrimMp3File(string inputPath, string outputPath, TimeSpan? begin, TimeSpan? end)
            {
              if (begin.HasValue && end.HasValue && begin > end)
             throw new ArgumentOutOfRangeException("end", "end should be greater than begin");
    
           using (var reader = new Mp3FileReader(inputPath))
           using (var writer = File.Create(outputPath))
           {
             Mp3Frame frame;
             while ((frame = reader.ReadNextFrame()) != null)
             if (reader.CurrentTime >= begin || !begin.HasValue)
              {
               if (reader.CurrentTime <= end || !end.HasValue)
                 writer.Write(frame.RawData, 0, frame.RawData.Length);
               else 
                 break;
             }
           }
         }
        }
    }                

    调用:

    string filePath = "D:\\wav\\test.wav";//需要切割的文件路径
    int cutTimeSpan = 20;//切割的时间片段时间(秒)
    FileInfo fi = new FileInfo(filePath);
    //获取录音文件时长(秒)
    int fileTime = (int)Util.Cover(Util.GetVoiceTime(filePath)) / 1000;
    //计算文件需要切割多少等份
    decimal fileNum = Math.Ceiling((decimal)fileTime / cutTimeSpan);
    int i = 0;
    while (i < fileNum)
    {
        string nowTime = Util.GetTimeStamp();//当前时间戳
        //切割后保存的文件绝对地址
        var outputPath = System.IO.Path.Combine(fi.Directory.FullName, string.Format("{0}_{1}{2}", fi.Name.Replace(fi.Extension, ""), nowTime, fi.Extension));
        //切割的开始时间
        TimeSpan cutFromStart = TimeSpan.FromSeconds(i * cutTimeSpan);
        //切割的结束时间
        TimeSpan cutFromEnd = cutFromStart + TimeSpan.FromSeconds(cutTimeSpan);
        //音频切割
        WavFileUtils.TrimWavFile(recordFile.FilePath, outputPath, cutFromStart, cutFromEnd);
        i++;
    }

    Util 类:

    using Shell32;
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;
    using System.Text.RegularExpressions;
    using System.Threading;
    using System.Windows.Forms;
    
    namespace XXX.util
    {
        class Util
        {
            /// <summary>
            /// 获取时间戳
            /// </summary>
            /// <returns></returns>
            public static string GetTimeStamp()
            {
                TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
                return Convert.ToInt64(ts.TotalMilliseconds).ToString();
            }
    
            /// <summary>
            /// 返回音频时长
            /// </summary>
            /// <param name="SongPath">音频文件路径</param>
            /// <returns></returns>
            public static string GetVoiceTime(string SongPath)
            {
                string dirName = Path.GetDirectoryName(SongPath);
                string SongName = Path.GetFileName(SongPath);
                ShellClass sh = new ShellClass();
                Folder dir = sh.NameSpace(dirName);
                FolderItem item = dir.ParseName(SongName);
                string SongTime = Regex.Match(dir.GetDetailsOf(item, -1), "\\d:\\d{2}:\\d{2}").Value;//返回音频时长
                return SongTime;
            }
    
          /// <summary>
            /// 时间格式转毫秒值
            /// </summary>
            /// <param name="time">时间字符串</param>
            /// <returns></returns>
            public static long Cover(string time)
            {
                string[] a = time.Split(':');
                if (long.Parse(a[0]) == 0 && long.Parse(a[1]) == 0)
                {
                    return long.Parse(a[2]) * 1000;
                }
                else if (long.Parse(a[0]) == 0 && long.Parse(a[1]) != 0)
                {
                    return (long.Parse(a[1]) * 60 + long.Parse(a[2])) * 1000;
                }
                else if (long.Parse(a[0]) != 0 && long.Parse(a[1]) == 0)
                {
                    return ((long.Parse(a[0]) * 60 * 60) + long.Parse(a[2])) * 1000;
                }
                else if (long.Parse(a[0]) != 0 && long.Parse(a[1]) != 0)
                {
                    return (((long.Parse(a[0]) * 60) + long.Parse(a[1])) * 60) * 1000;
                }
                return 0;
            }
        }
    }

     效果图:

    参考:

    https://github.com/naudio/NAudio

    https://stackoverflow.com/questions/42170640/trim-a-wav-file

    https://markheath.net/post/trimming-wav-file-using-naudio

    总结:实践是检验真理的唯一标准。

  • 相关阅读:
    android入门教程(十六)之 使用Intent传递数据
    Android入门教程(十八)之ListView (二) (转)
    Android入门教程(十四)之ListView的应用 (转)
    Android入门教程(十七)之GridView(转自http://blog.csdn.net/hellogv/)
    (原创)Android入门教程(十五)之 Activity生命周期及其配置使用
    Android入门教程(十九)之ListView (三) (转)
    Android入门教程(十三)之自定义下拉菜单模式Spinner与setDropDownViewResource的应用(转)
    Android入门教程(十)之Menu功能菜单设计 (转)
    [vp]ARC059
    欧拉路学习笔记
  • 原文地址:https://www.cnblogs.com/qiantao/p/15618359.html
Copyright © 2020-2023  润新知