Cross Platform C#

Sound-Effect Audio with Xamarin.Forms

We all know how to enjoy audio and video on our phones, tablets and computers. We click on a link or button and the screen is typically taken over as some video shows up and the accompanying audio begins to play.

For Xamarin.Forms mobile developers, a related issue that might come up is how to play some audio to provide a sound effect in an app, and specifically how would you play it in response to something like a button press.

In this article, I'll look at exactly that: how to play audio in the background when the user touches/clicks on a button.

Introduction
Xamarin.Forms is a high-level, cross-platform toolkit that runs on a number of platforms. I'll concentrate on iOS and Android. The problem with audio and video is that they are pretty much device-specific operations. iOS has its API for playing audio. Android has its API for playing audio, and the two APIs are not nearly the same. Unfortunately, Xamarin.Forms is designed mostly for working with data on forms. Clearly data on forms is a very common need and handles probably 75 percent to 100 percent of many applications' needs. The problem is that last few percent.

I was recently talking with a friend of mine and the talk turned toward what would be a cool app that might be of interest but doesn't currently have a lot of competition in a certain segment. We started brainstorming and we ran through a couple of ideas. I built a simple version of one of the ideas and asked him what he thought about it. He played with it on his Samsung Galaxy S8 and responded that it would be really cool if a sound effect played when he pushed a certain button. He said, "It would bring things to life for me." I thought about it and wondered if Xamarin.Forms could really do this and if the underlying platforms would support such functionality.

Dependency Services to the Rescue
Xamarin.Forms has a way to make native calls out on the device that the code is running on. This feature is called Dependency Services. The steps to call a Dependency Service are:

  1. Create an interface within the main/shared application for all of the possible calls that the application may need to make. In this example, the interface is fairly simple.
    public interface IAudio
    {
       bool PlayAudio();
    }
  2. Implement the interface in all of the necessary projects. In this instance, the only projects will be in iOS and Android. The Android implementation is:
    [assembly: Dependency(typeof(Audio))]
    namespace Xamarin.Droid.DependencyServices
    {
      public class Audio : IAudio
      {
        private MediaPlayer _mediaPlayer;
    
        public bool PlayAudio()
        {
          _mediaPlayer = MediaPlayer.Create(global::Android.App.Application.Context, Resource.Raw.filename);
          _mediaPlayer.Start();
          return true;
        }
      }
    }
    
    In the Android application's project, the mp3 -- as shown by Resource.Raw.filename -- is stored in the Resources/Raw directory in the Android project. The mp3 has its Build Type set to AndroidResource. The iOS implementation is:
    [assembly: Dependency(typeof(Audio))]
    namespace Pooper.Xamarin.iOS.DependencyServices
    {
      public class Audio : IAudio
      {
        AVAudioPlayer _player;
        public bool PlayAudio()
        {
    
          var fileName = "filename.mp3";
          string sFilePath = NSBundle.MainBundle.PathForResource
            (Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName));
          var url = NSUrl.FromString(sFilePath);
          _player = AVAudioPlayer.FromUrl(url);
          _player.FinishedPlaying += (object sender, AVStatusEventArgs e) => {
          _player = null;
          };
          _player.Play();
          return true;
        }
      }
    }
    
    Note that the mp3 file is to be stored in the Resources directory in the iOS project of the Xamarin.Forms solution and Build Action is set to Bundle Resource.

  3. Instantiate an instance of a class that implements the interface. In the Xamarin.Forms project, there is a class-level object that will serve as the audio class:
    IAudio audio;
    In the constructor of the class, the object is instantiated by the following code:
    audio = DependencyService.Get();
  4. Call the necessary methods on the instantiated objects as defined in the interfaces. For this, the method would be:
    audio.PlayAudio();
Congratulations, now an application can play an audio file upon a user interaction. Even better, the user doesn't get any type of new control in their display that be disoriented.

What About Context?
If a developer has used Xamarin.Forms, they have of course likely used the Xamarin.Forms context for Android. Android developers are used to working with context. With Xamarin.Forms 2.4 and earlier, the context is accessed by the static property Xamarin.Forms.Forms.Context. In Xamarin.Forms 2.5 and later, the built-in Forms.Context is obsolete.

Why was there a need for a change? There are more features being added to Xamarin.Forms, and Xamarin.Forms is going to be used in different ways. For example, Xamarin.Forms can be embedded in native platforms apps more easily now. The context is more complicated to keep track of. It is going to become the responsibility of the developer to keep track of such context going forward.

What is a developer to do? You have several options. In my example, it was rather simple to use this property:

global::Android.App.Application.Context

Another option is to do something like the following code. With it, whenever an application needs to access the context, it is possible to access via the static variable Instance:

internal static MainActivity Instance { get; private set; }
protected override void OnCreate (Bundle bundle)
{
  Instance = this;
  TabLayoutResource = Resource.Layout.Tabbar;
  ToolbarResource = Resource.Layout.Toolbar; 
  base.OnCreate (bundle);
  global::Xamarin.Forms.Forms.Init (this, bundle);
  ImageCircleRenderer.Init();
  LoadApplication (new YourApp.App ());
}

There may be some other situations where developers may need to track and work with the context a bit more. Be aware of this and watch the Xamarin support forums for discussions and more information regarding the Xamarin.Forms Android Context.

Summary
While Xamarin.Forms does not natively support audio, it is fairly easy to play audio in an application based on Xamarin.Forms. With Xamarin.Forms and Dependency Services, this works and works fairly easily. Hopefully, this article helps developers add a small amount of sexiness to their applications!

About the Author

Wallace (Wally) B. McClure has authored books on iPhone programming with Mono/Monotouch, Android programming with Mono for Android, application architecture, ADO.NET, SQL Server and AJAX. He's a Microsoft MVP, an ASPInsider and a partner at Scalable Development Inc. He maintains a blog, and can be followed on Twitter.

comments powered by Disqus

Featured

Subscribe on YouTube