Posted in:

I’m hoping to write a few brief code snippets to demonstrate various uses of NAudio, to eventually form the basis of an FAQ. This example shows how you can take a WAV file and trim a section out of it. You specify the TimeSpan to remove from the beginning and end, as well as an output WAV file. Please note this will only be reliable with PCM format WAV files.

public static class WavFileUtils
{
    public static void TrimWavFile(string inPath, string outPath, TimeSpan cutFromStart, TimeSpan cutFromEnd)
    {
        using (WaveFileReader reader = new WaveFileReader(inPath))
        {
            using (WaveFileWriter writer = new WaveFileWriter(outPath, reader.WaveFormat))
            {
                int bytesPerMillisecond = reader.WaveFormat.AverageBytesPerSecond / 1000;

                int startPos = (int)cutFromStart.TotalMilliseconds * bytesPerMillisecond;
                startPos = startPos - startPos % reader.WaveFormat.BlockAlign;

                int endBytes = (int)cutFromEnd.TotalMilliseconds * bytesPerMillisecond;
                endBytes = endBytes - endBytes % reader.WaveFormat.BlockAlign;
                int endPos = (int)reader.Length - endBytes; 

                TrimWavFile(reader, writer, startPos, endPos);
            }
        }
    }

    private static void TrimWavFile(WaveFileReader reader, WaveFileWriter writer, int startPos, int endPos)
    {
        reader.Position = startPos;
        byte[] buffer = new byte[1024];
        while (reader.Position < endPos)
        {
            int bytesRequired = (int)(endPos - reader.Position);
            if (bytesRequired > 0)
            {
                int bytesToRead = Math.Min(bytesRequired, buffer.Length);
                int bytesRead = reader.Read(buffer, 0, bytesToRead);
                if (bytesRead > 0)
                {
                    writer.WriteData(buffer, 0, bytesRead);
                }
            }
        }
    }
}
Want to get up to speed with the the fundamentals principles of digital audio and how to got about writing audio applications with NAudio? Be sure to check out my Pluralsight courses, Digital Audio Fundamentals, and Audio Programming with NAudio.

Comments

Comment by Frederic

Hello,

Was pleased with this example.
Tried it out with NAudio 1.3 (which was totally new to me) and worked perfectly on regular wav disk files.

My scenario involed in-memory Waves so I was pleased to see that (apparently starting with 1.3 release) WaveFileReader/Writer did support Streams.

Problem is that in the current WaveFileWriter class design, wave header and length is only updated when the object is disposed... which is ok when writing to a file, a little bit less when the stream is from another kind, because we might want to copy the result stream somewhere before disposing it.

Correct me if I'm wrong, but I was thinking of copying the block of code that updates the header from the Dispose method into the Flush method. Do you think that could do the job without breaking anything else?

UPDATE: Actually I did try this quick fix and it dit it, at least to me. Keep on the good work on this very helpful library.

Regards
Frederic

Comment by Anonymous

Can we use NAudio for web application..?

Anonymous
Comment by Mark H

the web server can use NAudio to manipulate audio files, but NAudio cannot be used on the client side in the browser

Comment by StarTraX

I have tried to implement this code in Visual Basic, but am getting an error
"Must read complete blocks" at
Dim bytesRead As Integer = reader.Read(buffer, 0, bytesToRead)
when invoking as follows:

Sub TrimWavFile(ByVal inPath As String, ByVal outPath As String, _
ByVal cutFromStart As TimeSpan, ByVal cutFromEnd As TimeSpan)
Dim reader As WaveFileReader = New WaveFileReader(inPath)
Dim writer As WaveFileWriter = New WaveFileWriter(outPath)
Dim bytesPerMillisecond As Integer = reader.WaveFormat.AverageBytesPerSecond / 1000
Dim startPos As Integer = cutFromStart.TotalMilliseconds * bytesPerMillisecond
startPos = startPos - startPos Mod reader.WaveFormat.BlockAlign
Dim endBytes As Integer = cutFromEnd.TotalMilliseconds * bytesPerMillisecond
endBytes = endBytes - endBytes Mod reader.WaveFormat.BlockAlign
Dim endPos As Integer = reader.Length - endBytes
TrimWavFile(reader, writer, startPos, endPos)
End Sub

Sub TrimWavFile(ByVal reader As WaveFileReader, ByVal writer As WaveFileWriter, ByVal startPos As Integer, ByVal endPos As Integer)
reader.Position = startPos
Dim buffer(1024) As Byte
While reader.Position < endPos
Dim bytesRequired As Integer = endPos - reader.Position
If bytesRequired > 0 Then
Dim bytesToRead As Integer = Math.Min(bytesRequired, buffer.Length)
Dim bytesRead As Integer = reader.Read(buffer, 0, bytesToRead)
If (bytesRead > 0) Then
writer.WriteData(buffer, 0, bytesRead)
End If
End If
End While
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
TrimWavFile("F:\VBProjectsDevelopment\3DTrackDisplay1\Sounds\tonovario.wav", _
"F:\VBProjectsDevelopment\3DTrackDisplay1\Sounds\tonovarioShort.wav", _
New TimeSpan(10000000), New TimeSpan(10000000))

End Sub

My C# coding level is about 3 out of 10 so maybe I've missed something in the migration.

Comment by Mark H

what is the value of bytesToRead?

Comment by sodaDreamer

Thanks for the snippet. This worked a treat for me!

I did spot one issue though - there's a rounding error when calculating bytesPerMillisecond. If you change it from an int to a double that should sort it.

Also, for what it's worth the 'Must read complete blocks' error is because the data must be read in multiples of reader.WaveFormat.BlockAlign. I had this issue reading a GSM file with a block align of 65, so I set the buffer size to BlockAlign * 100.

sodaDreamer
Comment by RelevantAds

Getting the "Must read complete blocks" error. Is this due to selecting a bad TimeSpan; or an oddly formed WAV file. My is 13kbps bitrate. I have 63 bytes left.

Comment by Mark H

instead of a buffer size of 1024, make sure it is sized to a multiple of your WaveFormat.BlockAlign.

Comment by Steve Van Bodegraven

Um, what's that while loop doing - while (reader.Position < endpos)= "" {= "" int= "" bytesrequired = "(int)(endPos" -= "" reader.position);= "" if= ""(bytesrequired = "" > 0)

Steve Van Bodegraven
Comment by Mark Heath

yikes, looks like source control got corrupted. will try to fix it, but basically just delete all instances of ="" and you should get the gist

Mark Heath
Comment by www3000

there is a little garbled in the sample code (ex:"" {="" int="" ), and I find the totally clean code here:
https://stackoverflow.com/a...

www3000
Comment by Mark Heath

thanks for reporting, not sure how that happened, but I've fixed it up now.

Mark Heath
Comment by Aldi

can we cut audio without writing to directory? please answer:)

Aldi
Comment by Mark Heath

Sure, you don't have to save the trimmed audio to disk if you don't want to. You could use the `Skip` and `Take` extension methods on `AudioFileReader` to create a playable `ISampleProvider` that only plays the desired region

Mark Heath
Comment by Aldi

I have a little problem sir, I made a small project to play audio, everything worked properly, but when I build my project to Android, the sound doesn't appear on my cellphone, can you explain, sir?
and sometimes I can't play music when I play the same waveout at almost the same time.The error code is like this :
MmException: AlreadyAllocated calling waveOutOpen
NAudio.MmException.Try (MmResult result, System.String function)
NAudio.Wave.WaveOut.Init (IWaveProvider waveProvider)
-
-
-
and this is the code that I made:
public class MyClass : MonoBehaviour {
WaveOut wo = new waveOut();
void Play(){
VorbisWaveReader reader = new VorbisWaveReader("MyAudio.ogg");
wo.Init(reader);
wo.Play();
}
}

Aldi
Comment by Mark Heath

WaveOut uses PInvoke to call Windows APIs so I'm amazed that you got any kind of error back at all from waveOutOpen which is called by WaveOut.Init when you run on Android. Are you using some kind of Windows emulation layer?

Mark Heath
Comment by Aldi

No, I didn't use it,but the audio still error even in unity editor on my computer

Aldi
Comment by Winston Fan

hi , is it possible to update metadata e.g. Genre, Description, Track Number etc. with NAudio? I have searched for a while and haven't found this functionality.

Winston Fan
Comment by Eric Stevens

Is there a way to trim multiple segments from the same input file?

Eric Stevens