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
Show Controls Added Programmatically in Winforms App in Design View
How to Translate Cultureinfo Language Names
Handling Multiple Requests with C# Httplistener
How am I Misusing the Null-Coalescing Operator? Is This Evaluating "Null" Correctly
Regular Expression to Allow Backslash in C#
Rounding Integers to Nearest Multiple of 10
Binding Datagrid to Observablecollection<Dictionary>
What to Use: Var or Object Name Type
How to Load All Assemblies from Within Your /Bin Directory
JSONconvert.Serializeobject Always Return {} in Xamarinforms
Using Bindingoperations.Enablecollectionsynchronization