How to make an audio progress bar

One of the features of the Cheap Ass Gamer app I’ve spent a lot of time on is the podcast controls. A progress bar is really useful to the user to instantly get an idea of how long the track is and how far they are in to it. To my knowledge, there is no in-built functions for creating an audio progress bar. With help from the internet, this is how I got my progress bar working. I’m of course using Silverlight for Windows Phone in C#.

Obviously you’re going to need a progress bar control or if you want to be able to scrub through the song, I suggest the Super Slider from the Coding4Fun Toolkit. The important thing is they both have these properties: ‘Value’ - the current fill amount of the bar, and ‘Maximum’ - the size of the bar. Make sure the ‘Maximum’ of the progress bar is set before the music starts playing:

progressBar.Maximum = BackgroundAudioPlayer.Instance.Track.Duration.TotalSeconds;

In order to have the bar keep track of the current position of the music track, a timer will need to be created. When the music starts, create a DispatchTimer and event handlers:

DispatcherTimer playTimer;
playTimer = new DispatcherTimer();
playTimer.Interval = TimeSpan.FromMilliseconds(1000); //one second
playTimer.Tick += new EventHandler(playTimer_Tick);
playTimer.Start();

I chose a second interval for the event to be called, as this is a reasonable time for music and it shouldn’t be too much of a strain on the phone. After every second the method ‘playTimer_Tick’ will be called. This is where we can update the progress bar:

public void playTimer_Tick(object sender, EventArgs e)
{
  if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing)
  {
    progressBar.Value = BackgroundAudioPlayer.Instance.Position.TotalSeconds;
    try
    {
      CurrentTime.Text = String.Format(@"{0:hh\:mm\:ss}",
      BackgroundAudioPlayer.Instance.Position).Remove(8);
    }
    catch
    {
      CurrentTime.Text = String.Format(@"{0:hh\:mm\:ss}",
      BackgroundAudioPlayer.Instance.Position);
    }
  }
}

The current time is a Text Block element as you can see in the bottom left of the screenshot. It’s nested in a try catch because I found the ‘Remove(8)’ function, which is necessary to remove the miliseconds from the time, can cause a rare exception when the string is less than 8 characters. If this happens, I just perform the same assignment without the function.

And that’s basically it to get the audio progress bar working. If you change track, I suggest having an event to change the Maximum value of the bar to the next track length, like this:

BackgroundAudioPlayer.Instance.PlayStateChanged += new EventHandler(audio_StateChanged);

public void audio_StateChanged(object sender, EventArgs e)
{
  if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing)
  {
    progressBar.Maximum = BackgroundAudioPlayer.Instance.Track.Duration.TotalSeconds;
    try
    {
      EndTime.Text = String.Format(@"{0:hh\:mm\:ss}",
      BackgroundAudioPlayer.Instance.Track.Duration).Remove(8);
    }
    catch
    {
      EndTime.Text = String.Format(@"{0:hh\:mm\:ss}",
      BackgroundAudioPlayer.Instance.Track.Duration);
    }
  }
}

Additionally, you can use the progress bar as a scrub bar so users can change the position of the track themselves…

You can use an event for when the user finishes changing the slider value to change the track position like this:

ManipulationCompleted="scrubChange" //In the slider XAML

private void scrubChange(object sender, RoutedEventArgs e)
{
  if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing)
  {
    TimeSpan ts = new TimeSpan(0, 0, (int)progressBar.Value);
    BackgroundAudioPlayer.Instance.Position = ts;
  }
}

Then, most importantly, in the audio player agent code you need to add this for it to work:

protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
{
  switch (action)
  {
  ...
  ...
  case UserAction.Seek:
    player.Position = (TimeSpan)param;
    break;
  }
  NotifyComplete();
}

That’s about it. You could use a boolean to stop the progress slider from updating while the user changes the position, but I’ll leave that to you. I hope this has been helpful to someone :)