【问题标题】:How can I determine the length (i.e. duration) of a .wav file in C#?如何确定 C# 中 .wav 文件的长度(即持续时间)?
【发布时间】:2010-09-10 02:03:55
【问题描述】:

在未压缩的情况下,我知道我需要读取 wav 标头,提取通道数、位和采样率并从那里计算出来: (通道) * (位) * (样本/秒) * (秒) = (文件大小)

有没有更简单的方法——免费库,或者 .net 框架中的东西?

如果 .wav 文件被压缩(例如使用 mpeg 编解码器),我该怎么做?

【问题讨论】:

  • 长度是指音频的时间还是文件的大小?
  • 这么长:|-----------------|
  • 你的意思是时间长度?在这种情况下,它称为持续时间,您应该更改帖子的标题。

标签: c# audio compression


【解决方案1】:

如果您愿意走这条路,您可能会发现XNA library 支持使用 WAV 等。它旨在与 C# 一起用于游戏编程,因此可以满足您的需求。

【讨论】:

    【解决方案2】:

    CodeProject 上提供了一些教程(可能包含您可以利用的工作代码)。

    您唯一需要注意的是,WAV 文件由多个块组成是完全“正常的” - 因此您必须遍历整个文件以确保所有块都被考虑在内。

    【讨论】:

      【解决方案3】:

      您的应用程序究竟在用压缩的 WAV 做什么?压缩的 WAV 文件总是很棘手——在这种情况下,我总是尝试使用另一种容器格式,例如 OGG 或 WMA 文件。 XNA 库倾向于设计为使用特定格式 - 尽管在 XACT 中您可能会找到更通用的 wav 播放方法。一种可能的替代方法是查看 SDL C# 端口,尽管我只使用它来播放未压缩的 WAV - 打开后,您可以查询样本数以确定长度。

      【讨论】:

        【解决方案4】:
        【解决方案5】:

        您可以考虑使用 mciSendString(...) 函数(为清楚起见省略了错误检查):

        using System;
        using System.Text;
        using System.Runtime.InteropServices;
        
        namespace Sound
        {
            public static class SoundInfo
            {
                [DllImport("winmm.dll")]
                private static extern uint mciSendString(
                    string command,
                    StringBuilder returnValue,
                    int returnLength,
                    IntPtr winHandle);
        
                public static int GetSoundLength(string fileName)
                {
                    StringBuilder lengthBuf = new StringBuilder(32);
        
                    mciSendString(string.Format("open \"{0}\" type waveaudio alias wave", fileName), null, 0, IntPtr.Zero);
                    mciSendString("status wave length", lengthBuf, lengthBuf.Capacity, IntPtr.Zero);
                    mciSendString("close wave", null, 0, IntPtr.Zero);
        
                    int length = 0;
                    int.TryParse(lengthBuf.ToString(), out length);
        
                    return length;
                }
            }
        }
        

        【讨论】:

        • 我对此进行了测试,它也适用于压缩 wav - 谢谢 +1
        • 其实我认为这是最稳健的解决方案。我尝试加载 MediaPlayer 对象的其他建议之一似乎需要大约一秒钟,并添加了对 .NET 3.5 的依赖
        • 在 Windows 10 中使用它打开 D:\Long\Path\To\WavFileWithLongName.wav 我收到来自 mciSendString 的错误 304:The filename is invalid. Make sure the filename is not longer than 8 characters, followed by a period and an extension.
        • 听起来像是 Windows 10 中的一个错误,但您可以尝试使用 GetShortPathName msdn.microsoft.com/en-us/library/windows/desktop/… 获取 8 字符(兼容旧版)文件名
        • 我在 Windows 7 上也遇到了带有长文件名的错误 304。我通过使用 p/invoke GetShortPathName 传递一个短路径版本来解决它。
        【解决方案6】:

        我不得不说MediaInfo,我已经在我正在开发的音频/视频编码应用程序中使用它一年多了。它提供了 wav 文件以及几乎所有其他格式的所有信息。

        MediaInfoDll 附带示例 C# 代码,说明如何使其工作。

        【讨论】:

          【解决方案7】:

          我假设您对 .WAV 文件的结构有些熟悉:它包含一个 WAVEFORMATEX 标头结构,然后是一些包含各种信息的其他结构(或“块”)。有关文件格式的更多信息,请参阅Wikipedia

          首先,遍历 .wav 文件并将“数据”块的未填充长度相加(“数据”块包含文件的音频数据;通常只有其中一个,但有可能可能不止一个)。您现在有了音频数据的总大小(以字节为单位)。

          接下来,获取文件的 WAVEFORMATEX 标头结构的“每秒平均字节数”成员。

          最后,将音频数据的总大小除以每秒平均字节数 - 这将为您提供文件的持续时间,以秒为单位。

          这适用于未压缩和压缩文件。

          【讨论】:

            【解决方案8】:

            我在上面的 MediaPlayer 类的例子中遇到了困难。播放器打开文件可能需要一些时间。在“现实世界”中,您必须注册 MediaOpened 事件,在该事件触发后,NaturalDuration 有效。 在控制台应用程序中,您只需在打开后等待几秒钟即可。

            using System;
            using System.Text;
            using System.Windows.Media;
            using System.Windows;
            
            namespace ConsoleApplication2
            {
              class Program
              {
                static void Main(string[] args)
                {
                  if (args.Length == 0)
                    return;
                  Console.Write(args[0] + ": ");
                  MediaPlayer player = new MediaPlayer();
                  Uri path = new Uri(args[0]);
                  player.Open(path);
                  TimeSpan maxWaitTime = TimeSpan.FromSeconds(10);
                  DateTime end = DateTime.Now + maxWaitTime;
                  while (DateTime.Now < end)
                  {
                    System.Threading.Thread.Sleep(100);
                    Duration duration = player.NaturalDuration;
                    if (duration.HasTimeSpan)
                    {
                      Console.WriteLine(duration.TimeSpan.ToString());
                      break;
                    }
                  }
                  player.Close();
                }
              }
            }
            

            【讨论】:

              【解决方案9】:

              不要从已经接受的答案中拿走任何东西,但我能够使用 Microsoft.DirectX.AudioVideoPlayBack 命名空间获得音频文件的持续时间(几种不同的格式,包括我当时需要的 AC3)。这是DirectX 9.0 for Managed Code 的一部分。添加对它的引用使我的代码变得如此简单......

              Public Shared Function GetDuration(ByVal Path As String) As Integer
                  If File.Exists(Path) Then
                      Return CInt(New Audio(Path, False).Duration)
                  Else
                      Throw New FileNotFoundException("Audio File Not Found: " & Path)
                  End If
              End Function
              

              而且速度也很快!这是Audio 类的参考。

              【讨论】:

                【解决方案10】:

                下载 NAudio.dll 从链接 https://www.dll-files.com/naudio.dll.html

                然后使用这个函数

                public static TimeSpan GetWavFileDuration(string fileName)       
                {     
                    WaveFileReader wf = new WaveFileReader(fileName);
                    return wf.TotalTime; 
                }
                

                你会得到持续时间

                【讨论】:

                • 我发现 NAudio 报告的 TotalTime 通常比样本实际长。即使格式未压缩(即 WAV)
                • 你也可以安装NAudionuget包而不是下载DLL。
                • ftp 服务器上的文件怎么样?前任。 TimeSpan duration = GetFileDuration(@"voip/pub/monitor/filename") 传递放置在 ftp 服务器上的文件路径引发异常,因为不支持给定路径的格式。知道吗?
                【解决方案11】:
                Imports System.IO
                Imports System.Text
                
                Imports System.Math
                Imports System.BitConverter
                
                Public Class PulseCodeModulation
                    ' Pulse Code Modulation WAV (RIFF) file layout
                
                    ' Header chunk
                
                    ' Type   Byte Offset  Description
                    ' Dword       0       Always ASCII "RIFF"
                    ' Dword       4       Number of bytes in the file after this value (= File Size - 8)
                    ' Dword       8       Always ASCII "WAVE"
                
                    ' Format Chunk
                
                    ' Type   Byte Offset  Description
                    ' Dword       12      Always ASCII "fmt "
                    ' Dword       16      Number of bytes in this chunk after this value
                    ' Word        20      Data format PCM = 1 (i.e. Linear quantization)
                    ' Word        22      Channels Mono = 1, Stereo = 2
                    ' Dword       24      Sample Rate per second e.g. 8000, 44100
                    ' Dword       28      Byte Rate per second (= Sample Rate * Channels * (Bits Per Sample / 8))
                    ' Word        32      Block Align (= Channels * (Bits Per Sample / 8))
                    ' Word        34      Bits Per Sample e.g. 8, 16
                
                    ' Data Chunk
                
                    ' Type   Byte Offset  Description
                    ' Dword       36      Always ASCII "data"
                    ' Dword       40      The number of bytes of sound data (Samples * Channels * (Bits Per Sample / 8))
                    ' Buffer      44      The sound data
                
                    Dim HeaderData(43) As Byte
                
                    Private AudioFileReference As String
                
                    Public Sub New(ByVal AudioFileReference As String)
                        Try
                            Me.HeaderData = Read(AudioFileReference, 0, Me.HeaderData.Length)
                        Catch Exception As Exception
                            Throw
                        End Try
                
                        'Validate file format
                
                        Dim Encoder As New UTF8Encoding()
                
                        If "RIFF" <> Encoder.GetString(BlockCopy(Me.HeaderData, 0, 4)) Or _
                            "WAVE" <> Encoder.GetString(BlockCopy(Me.HeaderData, 8, 4)) Or _
                            "fmt " <> Encoder.GetString(BlockCopy(Me.HeaderData, 12, 4)) Or _
                            "data" <> Encoder.GetString(BlockCopy(Me.HeaderData, 36, 4)) Or _
                            16 <> ToUInt32(BlockCopy(Me.HeaderData, 16, 4), 0) Or _
                            1 <> ToUInt16(BlockCopy(Me.HeaderData, 20, 2), 0) _
                        Then
                            Throw New InvalidDataException("Invalid PCM WAV file")
                        End If
                
                        Me.AudioFileReference = AudioFileReference
                    End Sub
                
                    ReadOnly Property Channels() As Integer
                        Get
                            Return ToUInt16(BlockCopy(Me.HeaderData, 22, 2), 0) 'mono = 1, stereo = 2
                        End Get
                    End Property
                
                    ReadOnly Property SampleRate() As Integer
                        Get
                            Return ToUInt32(BlockCopy(Me.HeaderData, 24, 4), 0) 'per second
                        End Get
                    End Property
                
                    ReadOnly Property ByteRate() As Integer
                        Get
                            Return ToUInt32(BlockCopy(Me.HeaderData, 28, 4), 0) 'sample rate * channels * (bits per channel / 8)
                        End Get
                    End Property
                
                    ReadOnly Property BlockAlign() As Integer
                        Get
                            Return ToUInt16(BlockCopy(Me.HeaderData, 32, 2), 0) 'channels * (bits per sample / 8)
                        End Get
                    End Property
                
                    ReadOnly Property BitsPerSample() As Integer
                        Get
                            Return ToUInt16(BlockCopy(Me.HeaderData, 34, 2), 0)
                        End Get
                    End Property
                
                    ReadOnly Property Duration() As Integer
                        Get
                            Dim Size As Double = ToUInt32(BlockCopy(Me.HeaderData, 40, 4), 0)
                            Dim ByteRate As Double = ToUInt32(BlockCopy(Me.HeaderData, 28, 4), 0)
                            Return Ceiling(Size / ByteRate)
                        End Get
                    End Property
                
                    Public Sub Play()
                        Try
                            My.Computer.Audio.Play(Me.AudioFileReference, AudioPlayMode.Background)
                        Catch Exception As Exception
                            Throw
                        End Try
                    End Sub
                
                    Public Sub Play(playMode As AudioPlayMode)
                        Try
                            My.Computer.Audio.Play(Me.AudioFileReference, playMode)
                        Catch Exception As Exception
                            Throw
                        End Try
                    End Sub
                
                    Private Function Read(AudioFileReference As String, ByVal Offset As Long, ByVal Bytes As Long) As Byte()
                        Dim inputFile As System.IO.FileStream
                
                        Try
                            inputFile = IO.File.Open(AudioFileReference, IO.FileMode.Open)
                        Catch Exception As FileNotFoundException
                            Throw New FileNotFoundException("PCM WAV file not found")
                        Catch Exception As Exception
                            Throw
                        End Try
                
                        Dim BytesRead As Long
                        Dim Buffer(Bytes - 1) As Byte
                
                        Try
                            BytesRead = inputFile.Read(Buffer, Offset, Bytes)
                        Catch Exception As Exception
                            Throw
                        Finally
                            Try
                                inputFile.Close()
                            Catch Exception As Exception
                                'Eat the second exception so as to not mask the previous exception
                            End Try
                        End Try
                
                        If BytesRead < Bytes Then
                            Throw New InvalidDataException("PCM WAV file read failed")
                        End If
                
                        Return Buffer
                    End Function
                
                    Private Function BlockCopy(ByRef Source As Byte(), ByVal Offset As Long, ByVal Bytes As Long) As Byte()
                        Dim Destination(Bytes - 1) As Byte
                
                        Try
                            Buffer.BlockCopy(Source, Offset, Destination, 0, Bytes)
                        Catch Exception As Exception
                            Throw
                        End Try
                
                        Return Destination
                    End Function
                End Class
                

                【讨论】:

                  【解决方案12】:

                  How to determine the length of a .wav file in C#试试下面的代码

                      string path = @"c:\test.wav";
                      WaveReader wr = new WaveReader(File.OpenRead(path));
                      int durationInMS = wr.GetDurationInMS();
                      wr.Close();
                  

                  【讨论】:

                    【解决方案13】:

                    是的,有一个免费库可用于获取音频文件的持续时间。该库还提供了更多功能。

                    TagLib

                    TagLib 是根据 GNU 宽通用公共许可证 (LGPL) 和 Mozilla 公共许可证 (MPL) 分发的。

                    我在下面实现了以秒为单位返回持续时间的代码。

                    using TagLib.Mpeg;
                    
                    public static double GetSoundLength(string FilePath)
                    {
                        AudioFile ObjAF = new AudioFile(FilePath);
                        return ObjAF.Properties.Duration.TotalSeconds;
                    }
                    

                    【讨论】:

                    • 非常感谢
                    【解决方案14】:

                    时间 = 文件长度 / (采样率 * 通道 * 每个采样的位数 /8)

                    【讨论】:

                    • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
                    【解决方案15】:

                    我测试过自爆代码会失败,文件格式类似于“\\ip\dir\*.wav'

                     public static class SoundInfo
                       {
                         [DllImport("winmm.dll")]
                         private static extern uint mciSendString
                         (
                            string command,
                            StringBuilder returnValue,
                            int returnLength,
                            IntPtr winHandle
                         );
                    
                         public static int GetSoundLength(string fileName)
                          {
                            StringBuilder lengthBuf = new StringBuilder(32);
                    
                            mciSendString(string.Format("open \"{0}\" type waveaudio alias wave", fileName), null, 0, IntPtr.Zero);
                            mciSendString("status wave length", lengthBuf, lengthBuf.Capacity, IntPtr.Zero);
                            mciSendString("close wave", null, 0, IntPtr.Zero);
                    
                            int length = 0;
                            int.TryParse(lengthBuf.ToString(), out length);
                    
                            return length;
                        }
                    }
                    

                    当 naudio 工作时

                        public static int GetSoundLength(string fileName)
                         {
                            using (WaveFileReader wf = new WaveFileReader(fileName))
                            {
                                return (int)wf.TotalTime.TotalMilliseconds;
                            }
                         }`
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2011-12-11
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-03-30
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多