2008-09-17 55 views
24

En la situación no comprimida, sé que necesito leer el encabezado wav, extraer el número de canales, bits y frecuencia de muestreo y resolverlo desde allí: (canales) * (bits) * (muestras/s) * (segundos) = (tamaño del archivo)¿Cómo puedo determinar la longitud de un archivo .wav en C#?

¿Hay una manera más simple, una biblioteca gratuita, o algo en el marco de .NET tal vez?

¿Cómo haré esto si el archivo .wav está comprimido (con el códec mpeg por ejemplo)?

+0

Por longitud, ¿te refieres a la duración del audio o al tamaño del archivo? –

+5

este largo: | ----------------- | – nickf

Respuesta

17

Es posible considerar el uso de la función mciSendString (...) (la comprobación del error se ha omitido para mayor claridad):

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; 
     } 
    } 
} 
+0

Probé esto y funcionó con un wav comprimido también - gracias +1 –

+0

De hecho, creo que esta es la solución más robusta. Una de las otras sugerencias que probé al cargar un objeto MediaPlayer, parece tomar alrededor de un segundo y agrega una dependencia en .NET 3.5 –

+0

Al usar esto en Windows 10 para abrir 'D: \ Long \ Path \ To \ WavFileWithLongName.wav' I Obtiene el error 304 de 'mciSendString':' El nombre del archivo no es válido.Asegúrese de que el nombre del archivo no sea más de 8 caracteres, seguido de un punto y una extensión. – damian

1

Puede encontrar que el XNA library tiene cierto soporte para trabajar con WAV, etc., si está dispuesto a seguir esa ruta. Está diseñado para trabajar con C# para la programación de juegos, por lo que podría ocuparse de lo que necesita.

1

Hay un poco de un tutorial (con, presumiblemente, código de trabajo que puede aprovechar) en CodeProject.

Lo único que debe tenerse un poco de cuidado es que es perfectamente "normal" que un archivo WAV esté compuesto de múltiples fragmentos, por lo que debe desplazarse por todo el archivo para asegurarse de que se incluyen todos los fragmentos .

1

¿Qué está haciendo exactamente tu aplicación con WAV comprimidas? Los archivos WAV comprimidos siempre son complicados: siempre trato de utilizar un formato de contenedor alternativo en este caso, como los archivos OGG o WMA. Las bibliotecas XNA tienden a estar diseñadas para trabajar con formatos específicos, aunque es posible que dentro de XACT encuentres un método de reproducción wav más genérico. Una posible alternativa es examinar el puerto SDL C#, aunque solo lo he usado para reproducir WAV sin comprimir: una vez abierto, puede consultar el número de muestras para determinar la longitud.

1

Voy a tener que decir MediaInfo, lo he estado utilizando durante más de un año con una aplicación de codificación de audio/video en la que estoy trabajando. Proporciona toda la información para archivos wav junto con casi cualquier otro formato.

MediaInfoDll Viene con el código C de muestra sobre cómo hacer que funcione.

0

Supongo que está familiarizado con la estructura de un archivo .WAV: contiene una estructura de encabezado WAVEFORMATEX, seguida de varias otras estructuras (o "fragmentos") que contienen varios tipos de información . Consulte Wikipedia para obtener más información sobre el formato de archivo.

Primero, itere a través del archivo .wav y sume las longitudes no acolchadas de los fragmentos de "datos" (el fragmento "datos" contiene los datos de audio del archivo, por lo general solo hay uno, pero es posible que podría haber más de uno). Ahora tiene el tamaño total, en bytes, de los datos de audio.

A continuación, obtenga el miembro "promedio de bytes por segundo" de la estructura del encabezado WAVEFORMATEX del archivo.

Finalmente, divida el tamaño total de los datos de audio por el promedio de bytes por segundo; esto le dará la duración del archivo, en segundos.

Esto funciona razonablemente bien para archivos comprimidos y sin comprimir.

2

Tuve dificultades con el ejemplo de la clase MediaPlayer anterior. Podría tomar algún tiempo, antes de que el jugador haya abierto el archivo. En el "mundo real" tiene que registrarse para el evento MediaOpened, después de que se haya disparado, la NaturalDuration es válida. En una aplicación de consola solo tienes que esperar unos segundos después de la apertura.

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

Por no quita nada a la respuesta ya aceptado, pero yo era capaz de conseguir la duración de un archivo de audio (varios formatos diferentes, incluyendo AC3, que es lo que necesitaba en ese momento) con el Microsoft.DirectX.AudioVideoPlayBack espacio de nombres Esto es parte de DirectX 9.0 for Managed Code. Añadiendo una referencia al hecho de que mi código tan simple como esto ...

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 

y es bastante rápido, también! Aquí hay una referencia para la clase Audio.

-8

Descargar "PresentationCore.dll" y "WindowsBase.dll" de:

http://www.search-dll.com/dll-files/download/windowsbase.dll.html

pegar los archivos en que carpeta bin solicitud de referencia. Debería funcionar ahora.

+3

-1, esto es absolutamente incorrecto. PresentationCore y WindowsBase son parte del framework y no deben descargarse * desde cualquier lugar en t'internet. – Rob

25

Descargar NAudio.dll desde el enlace http://naudio.codeplex.com/

y luego utilizar esta función

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

obtendrá la duración

+1

Descubrí que TotalTime informado por NAudio suele ser más largo que la muestra en realidad. Incluso cuando el formato no está comprimido (es decir, WAV) – MutantNinjaCodeMonkey

+0

También puede instalar el paquete 'NAudio' nuget en lugar de descargar DLL. –

-1
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 
+2

Um, la pregunta que se hace es por el código C#, no por VB.NET. –

1

Sí, hay una biblioteca gratuita que se puede usar para obtener la duración del archivo de audio. Esta biblioteca también proporciona muchas más funcionalidades.

TagLib

TagLib se distribuye bajo la GNU Lesser General Public License (LGPL) y la Licencia Pública de Mozilla (MPL).

Implementé a continuación el código que devuelve la duración del tiempo en segundos.

using TagLib.Mpeg; 

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