Determine WAV Duration Time in Visual Studio 2019

Viewed 433

First, Thank You for considering my question. I have searched for a solution to this problem, and have not found anything that makes use of the modern Visual Studio features/libraries/namespaces, etc...

I need to determine the duration (length of time) of a WAV audio file. I would like to get the results in milliseconds - I can format the data to my needs from there.

Can you please provide an example of how this might be done?

THANK YOU

2 Answers

For those searching for this solution, here is the debugged code. Note that the MilliSecond to timestring conversion was not included here. Also Note that based on the comments made to this posting, there is no standard Visual Basic Library that will derive the duration of a WAV file.


Option Explicit On Option Strict On Imports System.IO

Public Shared Function GetWAVDuration(ByVal strPathAndFilename As String) As String

    REM *** SEE ALSO
    REM *** http://soundfile.sapp.org/doc/WaveFormat/
    REM *** CAUTION
    REM *** On the WaveFormat web page, the positions of the values
    REM *** SampleRate and BitsPerSample have been reversed.
    REM *** https://stackoverflow.com/questions/65588931/determine-wav-duration-time-in-visual-studio-2019/65616287#65616287

    REM *** DEFINE LOCAL VARIABLES

    REM *** Define Visual Basic BYTE Array Types
    Dim byNumberOfChannels() As Byte
    Dim bySamplesPerSec() As Byte
    Dim byBitsPerSample() As Byte
    Dim bySubChunkToSizeData() As Byte

    Dim nNumberOfChannels As Int16
    Dim nSamplesPerSec As Int16
    Dim nBitsPerSample As Int32
    Dim nSubChunkToSizeData As Int32

    Dim dNumberOfSamples As Double
    Dim nDurationInMillis As Int32

    Dim strDuration As String

    REM *** INITIALIZE LOCAL VARIABLES
    byNumberOfChannels = New Byte(2) {}
    bySamplesPerSec = New Byte(2) {}
    byBitsPerSample = New Byte(4) {}
    bySubChunkToSizeData = New Byte(4) {}

    nNumberOfChannels = 0
    nSamplesPerSec = 0
    nBitsPerSample = 0L
    nSubChunkToSizeData = 0L

    dNumberOfSamples = 0.0
    nDurationInMillis = 0

    strDuration = ""

    REM *** Initialize the return string value
    GetWAVDuration = ""

    REM ***************************************************************************

    REM *** Open the Input File for READ Operations
    Using fsFileStream = File.OpenRead(strPathAndFilename)

        REM *** Get the Number of Audio Channels
        fsFileStream.Seek(22, SeekOrigin.Begin)
        fsFileStream.Read(byNumberOfChannels, 0, 2)

        REM *** Get the Number of Bits Per Audio Sample
        fsFileStream.Seek(24, SeekOrigin.Begin)
        fsFileStream.Read(byBitsPerSample, 0, 4)

        REM *** Get the number of samples taken per second
        fsFileStream.Seek(34, SeekOrigin.Begin)
        fsFileStream.Read(bySamplesPerSec, 0, 2)

        REM *** Retrieve the size of the WAV data
        REM *** payload in the file
        fsFileStream.Seek(40, SeekOrigin.Begin)
        fsFileStream.Read(bySubChunkToSizeData, 0, 4)

    End Using

    REM *** Convert Values from their BYTE representation

    nNumberOfChannels = BitConverter.ToInt16(byNumberOfChannels, 0)
    nBitsPerSample = BitConverter.ToInt32(byBitsPerSample, 0)
    nSamplesPerSec = BitConverter.ToInt16(bySamplesPerSec, 0)
    nSubChunkToSizeData = BitConverter.ToInt32(bySubChunkToSizeData, 0)

    REM *** Compute the Duration of the WAV File
    REM *** Derives the duration in milliseconds

    REM *** Determine the number of Sound Samples 
    dNumberOfSamples = (nSubChunkToSizeData * 8) / (nNumberOfChannels * nBitsPerSample)
    nDurationInMillis = Convert.ToInt32(1000 * Convert.ToSingle(dNumberOfSamples) / Convert.ToSingle(nSamplesPerSec))

    REM *** Convert the time in Milliseconds to a string format
    REM *** represented by "hh:mm:ss"
    strDuration = ConvertMillisToTimeString(nDurationInMillis)

    REM *** POST METHOD RETURNS
    GetWAVDuration = strDuration

End Function

Metadata for a file will be defined in the standard. The wave file header has been covered in another SO question but that is with a focus on using C sharp.

VB.NET and C sharp implementations should be relatively similar. There are some limitations to VB.NET which means this kind of operation is best done with another language. That said, there is surprisingly little immediately available for the novice VB.NET programmer for doing this, which is a red flag in itself that it is probably wielding VB.NET in the wrong fashion.

There is a project that tries to provide a .NET framework for grabbing metadata, but if you don't want to burden yourself with a dependency like that for just a Wav file then you can get duration with the following.

Dim wavFilePath As String = "Path\To\Your\Wav"
Dim fileReader As New System.IO.FileStream(wavFilePath, System.IO.FileMode.Open)
Dim numChannelsData(2) As Byte
Dim bitsPerSampleData(2) As Byte
Dim sampleRateData(4) As Byte
Dim subChunk2SizeData(4) As Byte

fileReader.Seek(22, System.IO.SeekOrigin.Begin)
fileReader.Read(numChannelsData, 0, 2)
fileReader.Seek(34, System.IO.SeekOrigin.Begin)
fileReader.Read(bitsPerSampleData, 0, 2)
fileReader.Seek(24, System.IO.SeekOrigin.Begin)
fileReader.Read(sampleRateData, 0, 4)
fileReader.Seek(40, System.IO.SeekOrigin.Begin)
fileReader.Read(subChunk2SizeData, 0, 4)

Dim numChannels As Int16 = BitConverter.ToInt16(numChannelsData)
Dim bitsPerSample = BitConverter.ToInt32(sampleRateData)
Dim sampleRate = BitConverter.ToInt16(bitsPerSampleData)
Dim subChunk2Size = BitConverter.ToInt32(subChunk2SizeData)

Dim numSamples = (subChunk2Size * 8) / (numChannels * bitsPerSample)
Dim durationInMilliseconds As Single = 1000 * Convert.ToSingle(numSamples) / (Convert.ToSingle(sampleRate))

Console.WriteLine(durationInMilliseconds)
Related