How do I create a seekbar in C#\NAudio Music Player?
c#, naudio
Solution
Apart from the purely UI-based concerns, there are three basic things you need to be able to do:
Read song length.
Get playback position.
Set playback position.
Song length and current playback position are simple enough - they are available via the `TotalTime` and `CurrentTime` properties of the `WaveStream` object, which means your `audioFileReader` object supports them too. Once constructed, `audioFileReader.TotalTime` will give you a `TimeSpan` object with the total length of the file, and `audioFileReader.CurrentTime` will give you the current playback position.
You can also set the playback position by assigning to `audioFileReader.CurrentTime`... but doing so is a tricky process unless you know what you're doing. The following code to skip ahead 2.5 seconds works sometimes and crashes horribly at others:
audioFileReader.CurrentTime = audioFileReader.CurrentTime.Add(TimeSpan.FromSeconds(2.5));
The problem here is that the resultant stream position (in bytes) may not be correctly aligned to the start of a sample, for several reasons (including floating point math done in the background). This can quickly turn your output to garbage.
The better option is to use the `Position` property of the stream when you want to change playback position. `Position` is the currently playback position in bytes, so is a tiny bit harder to work on. Not too much though:
audioFileReader.Position += audioFileReader.WaveFormat.AverageBytesPerSecond;
If you're stepping forward or backwards an integer number of seconds, that's fine. If not, you need to make sure that you are always positioning at a sample boundary, using the `WaveFormat.BlockAlign` property to figure out where those boundaries are.
// Calculate new position
long newPos = audioFileReader.Position + (long)(audioFileReader.WaveFormat.AverageBytesPerSecond * 2.5);
// Force it to align to a block boundary
if ((newPos % audioFileReader.WaveFormat.BlockAlign) != 0)
newPos -= newPos % audioFileReader.WaveFormat.BlockAlign;
// Force new position into valid range
newPos = Math.Max(0, Math.Min(audioFileReader.Length, newPos));
// set position
audioFileReader.Position = newPos;
The simple thing to do here is define a set of extensions to the `WaveStream` class that will handle block aligning during a seek operation. The basic align-to-block operation can be called by variations that just calculate the new position from whatever you put in, so something like this:
public static class WaveStreamExtensions
{
// Set position of WaveStream to nearest block to supplied position
public static void SetPosition(this WaveStream strm, long position)
{
// distance from block boundary (may be 0)
long adj = position % strm.WaveFormat.BlockAlign;
// adjust position to boundary and clamp to valid range
long newPos = Math.Max(0, Math.Min(strm.Length, position - adj));
// set playback position
strm.Position = newPos;
}
// Set playback position of WaveStream by seconds
public static void SetPosition(this WaveStream strm, double seconds)
{
strm.SetPosition((long)(seconds * strm.WaveFormat.AverageBytesPerSecond));
}
// Set playback position of WaveStream by time (as a TimeSpan)
public static void SetPosition(this WaveStream strm, TimeSpan time)
{
strm.SetPosition(time.TotalSeconds);
}
// Set playback position of WaveStream relative to current position
public static void Seek(this WaveStream strm, double offset)
{
strm.SetPosition(strm.Position + (long)(offset* strm.WaveFormat.AverageBytesPerSecond));
}
}
With that in place, you can call `audioFileReader.SetPosition(10.0)` to jump to playback position `00:00:10.0`, call `audioFileReader.Seek(-5)` to jump back 5 seconds, etc. without worrying about seeking to a point half way through a sample.
So... add some buttons to your form and set them up to call the `Seek` method with +/- values to move around. Then add a slider of some sort that you can use to display and set the playback position. Throw in a timer to update the slider position to the current playback position and you're about done.
Problem
I am new to both NAudio and C# and I managed to create a simple MP3 player where you can choose a file and play it. It also has a play/pause button. I would now like to add a seek bar but have no clue on how to do this. Also is it possible to have seekbar in waveform style? The openButton click Handler ``` private void openButton_Click(object sender, EventArgs e) { OpenFileDialog open = new OpenFileDialog(); open.Filter = "Audio File|*.mp3;"; if (open.ShowDialog() != DialogResult.OK) return; CloseWaveOut(); // disposes the waveOutDevice and audiofilereader waveOutDevice = new WaveOut(); audioFileReader = new AudioFileReader(open.FileName); waveOutDevice.Init(audioFileReader); waveOutDevice.Play(); pauseButton.Enabled = true; } ```