Click here to Skip to main content
13,768,880 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

9.5K views
5 bookmarked
Posted 7 Feb 2017
Licenced MIT

How To Play Audio Files in Xamarin.Forms

, 7 Feb 2017
Rate this:
Please Sign up or sign in to vote.
Play any audio files by using DependencyService with MediaPlayer for Android and AVAudioPlayer for iOS

Audio, sound, or music can be an essential part of mobile apps. Unfortunately, Xamarin.Forms doesn't support this directly from their API. But of course, it can be implemented easily by using DependencyService. This time, I'll show you how to implement this Audio Player in Xamarin.Forms with the help of DependencyService. Also, I'll show you how to integrate it using a simple MVVM Pattern.

First thing first, create a new Xamarin.Forms Solution. Use Portable Class library and XAML for user interface. Let's name this solution AudioPlayer. Now that we've created a new solution, we'll have three projects: Portable Library, iOS, and Android. We'll implement one by one starting from our core Portable Library.

Audio Player Service Interface

Let's head to our portable library project, AudioPlayer. Create a new interface called IAudioPlayerService. This Audio Service will have 4 core functions:

  • Play(string pathToAudio): Play an audio file which path is defined by pathToAudio.
  • Play(): Same with previous function, but it'll play the audio from previous session if paused.
  • Pause(): This will pause current audio session. We'll use Play() to resume audio playing.
  • OnFinishedPlaying: This Action will be fired if audio player reached the end of file, which means audio playing is stopped.

Based on those functions, here is how our interface looks like:

public interface IAudioPlayerService
{
  void Play(string pathToAudioFile);
  void Play();
  void Pause();
  Action OnFinishedPlaying { get; set; }
}

The next step is to implement audio player service for each platform based on this interface.

Audio Player Service iOS Implementation

For this step, let's head to iOS project. Create a new class called AudioPlayerService and implement IAudioPlayerService interface. Also don't forget to put [assembly: Dependency(typeof(AudioPlayerService))] on top of namespace declaration.

In iOS environment, we're going to use AVAudioPlayer class to handle audio player. So we're going to declare _audioPlayer as a private variable. We won't need anything to initialize this class, so we're going to leave the constructor empty.

private AVAudioPlayer _audioPlayer = null;
public Action OnFinishedPlaying { get; set; }

public AudioPlayerService()
{
}

Next, we're going to implement Play(string) function. It'll look for the file under Resources folder in iOS project at first, then an AVAudioPlayer class will be created by using pathToAudioFile parameter as path to audio file relative to Resources folder. So for example, if you have file under Resources/file.mp3, you can just call this function like this: Play("file.mp3").

But before we play the audio file, we need to check if _audioPlayer is playing another file. All we need to do is remove FinishedPlaying event and stop the audio playing. After that, create an AVAudioPlayer object. Then add a FinishedPlaying event listener and finally, we can just call _audioPlayer.Play() to start playing the audio.

public void Play(string pathToAudioFile)
{
  // Check if _audioPlayer is currently playing
  if (_audioPlayer != null)
  {
    _audioPlayer.FinishedPlaying -= Player_FinishedPlaying;
    _audioPlayer.Stop();
  }

  string localUrl = pathToAudioFile;
  _audioPlayer = AVAudioPlayer.FromUrl(NSUrl.FromFilename(localUrl));
  _audioPlayer.FinishedPlaying += Player_FinishedPlaying;
  _audioPlayer.Play();
}

For the rest of the functions, we just need to call the equivalent of each native functions. The following snippet is what you need to implement those functions.

private void Player_FinishedPlaying(object sender, AVStatusEventArgs e)
{
  OnFinishedPlaying?.Invoke();
}

public void Pause()
{
  _audioPlayer?.Pause();
}

public void Play()
{
  _audioPlayer?.Play();
}

OnFinishedPlaying action is invoked under Player_FinishedPlaying event listener because it indicates that the player has finished playing the audio file.

Audio Player Service Android Implementation

Android implementation is similar to iOS. The equivalent native class that can be used to handle audio playing is MediaPlayer. So let's declare a private variable called _mediaPlayer. Also, we don't need anything for initialization of this class, so I just left the constructor empty.

private MediaPlayer _mediaPlayer;
public Action OnFinishedPlaying { get; set; }

public AudioPlayerService()
{
}

If iOS looks for the file under Resources folder, then Android uses Assets folder equivalently. So if your mp3 file is under Assets/file.mp3 is equivalent with iOS under Resources/file.mp3.

Similar with iOS version, before we play the audio file, we need to remove Completion event listener and stop _mediaPlayer if it's currently playing. The difference with iOS is we can't just create a new MediaPlayer native class with selected file name. We're going to need AssetFileDescriptor to read the file. Then the MediaPlayer needs to be prepared by calling PrepareAsync.

This preparation is handled asynchronously. So we need to implement an event listener called Prepared when preparation is completed. Then under Prepared event listener, we can implement Completion event listener to indicate that _mediaPlayer finished playing the audio file and also call Start() function to start audio playing process.

public void Play(string pathToAudioFile)
{
  if (_mediaPlayer != null)
  {
    _mediaPlayer.Completion -= MediaPlayer_Completion;
    _mediaPlayer.Stop();
  }

  var fullPath = pathToAudioFile;

  Android.Content.Res.AssetFileDescriptor afd = null;

  try
  {
    afd = Forms.Context.Assets.OpenFd(fullPath);
  }
  catch (Exception ex)
  {
    Console.WriteLine("Error openfd: " + ex);
  }
  if (afd != null)
  {
    System.Diagnostics.Debug.WriteLine("Length " + afd.Length);
    if (_mediaPlayer == null)
    {
      _mediaPlayer = new MediaPlayer();
      _mediaPlayer.Prepared += (sender, args) =>
      {
        _mediaPlayer.Start();
        _mediaPlayer.Completion += MediaPlayer_Completion;
      };
    }

    _mediaPlayer.Reset();
    _mediaPlayer.SetVolume(1.0f, 1.0f);

    _mediaPlayer.SetDataSource(afd.FileDescriptor, afd.StartOffset, afd.Length);
    _mediaPlayer.PrepareAsync();
  }
}

Like in iOS version, the rest of the functions have equivalent with native implementation. So what we need to do is to implement like the following snippet.

void MediaPlayer_Completion(object sender, EventArgs e)
{
  OnFinishedPlaying?.Invoke();
}

public void Pause()
{
  _mediaPlayer?.Pause();
}

public void Play()
{
  _mediaPlayer?.Start();
}

Same with iOS, OnFinishedPlaying action is invoked under MediaPlayer_Completion that indicates the media player finished playing the entire audio file.

Integrating Audio Player Service into Xamarin.Forms App

This final section is how to implement our audio player service inside Xamarin.Forms app. We're just going to need to implement a simple Page with one button to play or pause audio file. First, you need to put audio file(s) under respective folder. Put it under Droid/Assets/ for Android version and iOS/Resources for iOS version. If you don't have any audio file to test, you can download one of the awesome music from incompetech. For this tutorial, I use their music called Galway.

Let's create a new ViewModel called AudioPlayerViewModel. Don't forget to implement INotifyPropertyChanged or any MVVM framework you prefer.

Add a new property called CommandText. This property will be used as Button's text. It'll be written as Play if audio is stopped and Pause if audio is currently playing.

private string _commandText;
public string CommandText
{
  get { return _commandText;}
  set
  {
    _commandText = value;
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CommandText"));
  }
}

Add one parameter inside AudioPlayerViewModel to pass audio player service we've created. Then, add one variable _isStopped to indicate whether audio player service is finished playing or not.

private IAudioPlayerService _audioPlayer;
private bool _isStopped;
public event PropertyChangedEventHandler PropertyChanged;

public AudioPlayerViewModel(IAudioPlayerService audioPlayer)
{
  _audioPlayer = audioPlayer;
  _audioPlayer.OnFinishedPlaying = () => {
    _isStopped = true;
    CommandText = "Play";
  };
  CommandText = "Play";
  _isStopped = true;
}

Last one is to add PlayPauseCommand that will be fired if user touches the button. This is just a simple implementation. If audio is playing, it'll pause. If not, it'll resume playing.

private ICommand _playPauseCommand;
public ICommand PlayPauseCommand
{
  get
  {
    return _playPauseCommand ?? (_playPauseCommand = new Command(
      (obj) => 
    {
      if (CommandText == "Play")
      {
        if (_isStopped)
        {
          _isStopped = false;
          _audioPlayer.Play("Galway.mp3");
        }
        else
        {
          _audioPlayer.Play();
        }
        CommandText = "Pause";
      }
      else
      {
        _audioPlayer.Pause();
        CommandText = "Play";
      }
    }));
  }
}

Now let's head to our user interface. You can create whatever you want, but for the sake of this tutorial, I just need to add one button under StackLayout. Bind respective button's properties with the ViewModel's properties.

<?xml version="1.0" encoding="utf-8"?>
<ContentPage 

    xmlns="http://xamarin.com/schemas/2014/forms" 

    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 

    xmlns:local="clr-namespace:AudioPlayer" 

    x:Class="AudioPlayer.AudioPlayerPage">
    <StackLayout VerticalOptions="CenterAndExpand" 

    HorizontalOptions="Fill">
      <Button Text="{Binding CommandText}"

        Command="{Binding PlayPauseCommand}" />
      <Label VerticalOptions="Center" 

        HorizontalOptions="Center"

        HorizontalTextAlignment="Center"

        FontSize="Micro">
        <Label.Text>
"Galway" Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0 License
http://creativecommons.org/licenses/by/3.0/
        </Label.Text>
      </Label>
    </StackLayout>
</ContentPage>

Finally, add AudioPlayerViewModel class as your user interface's BindingContext. To get native audio player service, use DependencyService.Get() function and pass it into ViewModel's parameter.

public AudioPlayerPage()
{
  InitializeComponent();
  BindingContext = new AudioPlayerViewModel(DependencyService.Get<IAudioPlayerService>());
}

After all the above code has been implemented, you can try running it on your iOS or Android devices. Press the button to play or pause the audio.

Summary

Even though Xamarin.Forms doesn't provide anything out of the box, we can easily implement it by using Dependency Service and native implementation for each platforms. If you are stuck finding C# code for each native implementation, you can just find Java code or Objective-C / Swift and rewrite it in C#. Anyway, thanks for reading and hopefully it's useful for your next project.

You can download the final projects from GitHub.

Credits

This tutorial uses one of the music from incompetech with the following copyright statement:

"Galway" Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0 License
http://creativecommons.org/licenses/by/3.0/

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Junian Triajianto
Software Developer (Senior)
Indonesia Indonesia
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionUWP Pin
Member 1380011726-Apr-18 8:36
memberMember 1380011726-Apr-18 8:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web05-2016 | 2.8.181116.1 | Last Updated 8 Feb 2017
Article Copyright 2017 by Junian Triajianto
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid