How to Determine the Length (I.E. Duration) of a .Wav File in C#

How can I determine the length (i.e. duration) of a .wav file in C#?

You may consider using the mciSendString(...) function (error checking is omitted for clarity):

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

Detecting the duration of .wav file in resources

You can use NAudio to read the resource stream and get the duration

using NAudio.Wave;
...
WaveFileReader reader = new WaveFileReader(MyProject.Resource.AudioResource);
TimeSpan span = reader.TotalTime;

You can also do what Matthew Watson suggested and simply keep the length as an additional string resource.

Also, this SO question has tons of different ways to determine duration but most of them are for files, not for streams grabbed from resources.

Length in time of a wave file

You can use CSCore or NAudio:

CSCore (extracted from this sample, current playback position and total duration are used here):

using System;
using CSCore;
using CSCore.Codecs.WAV;

IWaveSource wavSource = new WaveFileReader(stream);
TimeSpan totalTime = wavSource.GetLength();

NAudio:

using System;
using NAudio.Wave;

using (var wfr = new WaveFileReader(stream))
{
TimeSpan totalTime = wfr.TotalTime;
}

Also see the MSDN docs for TimeSpan.

The duration is calculated from the total length of the WAVE data (which can be an estimate for compressed files) and the average bytes per second (as per the NAudio source in property TotalTime):

totalTimeInSeconds = LengthInBytes / AverageBytesPerSecond;

Determine WAV Duration Time in Visual Studio 2019

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

Opening an audio (wav) file from a MemoryStream to determine the duration

I agree with Alex.
I took the time to put together a small program with three lines of code that prints the duration of a wav file.

        var stream=new MemoryStream(File.ReadAllBytes("test.wav"));
var wave = new WaveFileReader(stream);
Console.WriteLine(wave.TotalTime); // wave.TotalTime -> TimeSpan

Download NAudio library: you will find NAudio.dll in the package.

Just reference NAudio.dll in your project.

At time of writing it's release 1.3.

Like the author says on his blog, WaveFileReader accept a Stream too; not just a file path.

Remember that version 1.3 is built for x86. If you want it to work on x64 you need to force your project to x86.
If you want NAudio.dll for x64 (like me) you need to recompile with 'any cpu'.
For me both solutions worked like a charm.

Get length of .wav from sox output

The stat effect sends its output to stderr, use 2>&1 to redirect to stdout. Use sed to extract the relevant bits:

sox out.wav -n stat 2>&1 | sed -n 's#^Length (seconds):[^0-9]*\([0-9.]*\)$#\1#p'

calculating the amount of noise in a wav file compared to a source file

One way to build a crude estimation of the noise would be to compute the standard deviation of the peak values of the signal.

Given that you know the expected frequency, you can divide the signal into chunks of one wavelength, i.e if your signal is a 3KHz and your sample rate is 16KHz, then your chunk size is 5.3333 samples, for each chunk find the highest value, then for that sequence of values, find the stddev.

Alternatively you can for each chunk track the min and max values, then over the whole sample, find the mean of the min and max, and the range for the min (i.e. the highest and lowest values of the min value) then the SNR is ~ (mean_max - mean_min) / (min_range)

How to read the data in a wav file to an array

WAV files (at least, uncompressed ones) are fairly straightforward. There's a header, then the data follows it.

Here's a great reference: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ (mirror)



Related Topics



Leave a reply



Submit