Xamarin.Formsで音楽(BGM)を再生する方法

ここではアプリ内でのBGMの再生を想定して説明します。
現時点でiOSとAndroidを共通で使えるXamarin.Formsのコンポーネントは提供されていませんので、プレーヤーは以下のものを利用しています。(※2016/8/22時点では動画コンポーネントのみ)
・iOS側:AVAudioPlayer
・Android側:MediaPlayer

事前準備

空のXamarin.Formプロジェクト(Portable Class Library)を作成します。
※ここでは、プロジェクト名を「AudioPlaySample」として作成してますが、好きに読み替えてください。

新規プロジェクトを作成すると、「AudioPlaySample」と「Droid」、「iOS」の3つのプロジェクトが作成されていると思います。

次に、そこに再生するための音楽(MP3形式)を登録します。
※ここでは、フリーBGMサイト「DOVA-SYNDROME」から、「Shall we meet?」を利用しています。

Androidアプリ側は「Droid」内の「Resources」フォルダを利用します。
「Resources」フォルダ内に「raw」フォルダを作成し、その中に音楽ファイルを入れます。
(Droid>Resources>raw>shall_we_meet.mp3])

iOSアプリ側は「iOS」内の「Resources」フォルダを利用します。
「Resources」フォルダ内にそのまま、音楽ファイルを入れます。
(iOS>Resources>shall_we_meet.mp3])

画面の作成

「AudioPlaySample.cs」のAppの部分に以下の形で、Content画面をコードで作成します。
※もし、XAMLで対応したい方は、イベント記述をうまく拾ってください。

まずはコマンド用に「class App」内に以下のIMediaPlayerインターフェイスを作成します。
・Play(非同期)
・Stop

共通処理[AudioPlaySample>AudioPlaySample.cs]

	public interface IMediaPlayer
	{
		Task PlayAsync(string title);
		void Stop();
	}

次に、以下のコードでサクッと画面を作成します。(XAMLで対応しても構いません。)

	public App()
	{
		Button btnPlay = new Button {
			Text = "Play/Pause",
			Command = new Command((obj) => {
				DependencyService.Get<IMediaPlayer>().PlayAsync("shall_we_meet");
			})
		};

		Button btnStop = new Button {
			Text = "Stop",
			Command = new Command((obj) => {
				DependencyService.Get<IMediaPlayer>().Stop();
			})
		};

		// The root page of your application
		var content = new ContentPage {
			Title = "AudioPlaySample",
			Content = new StackLayout {
				VerticalOptions = LayoutOptions.Center,
				Children = {
					btnPlay,
					btnStop
				}
			},
			Padding = new Thickness(10)
		};
		MainPage = new NavigationPage(content);
	}

DependencyServiceを使ったオーディオサービスの作成

各プラットフォームごとにオーディオサービスの作成をします。
各プラットフォームに空のクラスで「MediaPlayer_Droid.cs」「MediaPlayer_iOS.cs」を作成して、以下のように修正します。

Android側[Droid>MediaPlayer_Droid.cs]

using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using Android.Media;

[assembly: Dependency(typeof(AudioPlaySample.Droid.MediaPlayer_Droid))]
namespace AudioPlaySample.Droid
{
	public class MediaPlayer_Droid : App.IMediaPlayer
	{
		MediaPlayer player = null;

		private async Task StartPlayerAsync(string title)
		{
			var resourceId = (int)typeof(Resource.Raw).GetField(title).GetValue(null);

			await Task.Run(() => {
				if (player == null) {
					player = new MediaPlayer();
					player.SetAudioStreamType(Stream.Music);

					player = MediaPlayer.Create(
								global::Android.App.Application.Context,
								resourceId
							);
					player.Looping = true;
					player.Start();
				} else {
					if (player.IsPlaying == true) {
						player.Pause();
					} else {
						player.Start();
					}
				}
			});
		}

		private void StopPlayer()
		{
			if ((player != null)) {
				if (player.IsPlaying) {
					player.Stop();
				}
				player.Release();
				player = null;
			}
		}

		public async Task PlayAsync(string title)
		{
			await StartPlayerAsync(title);
		}

		public void Stop()
		{
			StopPlayer();
		}
	}
}

iOS側[iOS>MediaPlayer_iOS.cs]

using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using AVFoundation;
using Foundation;

[assembly: Dependency(typeof(AudioPlaySample.iOS.MediaPlayer_iOS))]
namespace AudioPlaySample.iOS
{
	public class MediaPlayer_iOS : App.IMediaPlayer
	{
		AVAudioPlayer player = null;

		private async Task StartPlayerAsync(string title)
		{
			await Task.Run(() => {
				if (player == null)
				{
					var url = NSUrl.FromFilename(title + ".mp3");

					NSError _err = null;

					player = new AVAudioPlayer(
								url,
								AVFileType.MpegLayer3,
								out _err
						     );

					player.NumberOfLoops = -1;//player.Looping = true;
					player.PrepareToPlay();
					player.Play();
				} else {
					if (player.Playing == true) {
						player.Stop();
					} else {
						player.Play();
					}
				}
			});
		}

		private void StopPlayer()
		{
			if (player != null) {
				if (player.Playing) {
					player.Stop();
				}
				player.Dispose();
				player = null;
			}
		}

		public async Task PlayAsync(string title)
		{
			await StartPlayerAsync(title);
		}

		public void Stop()
		{
			StopPlayer();
		}
	}
}

動作確認

サンプル画面では固定をしていますが、BGMファイル名を引数で指定すればリソース内にある音楽を選んで再生することが出来ます。
また、再生ボタンをもう一度押すことで、一時停止が可能になっています。

サンプルコードはこちら(GitHub)
(※サンプルでは経過時間の取得にも対応しています。)

備考

Xamarin.Formsとして比較的同じような機能を使って、シンプルに共用化を行いましたが、Androidでは音楽用のコンポーネントはほとんどなく、iOS側では逆に様々なメディアコンポーネントがあります。

今回はリソースからの呼び出しで対応しましたが、呼び出し先もファイルやAssets、メディアサービスなど多岐にわたりますし、メディアフォーマットでも対応、非対応があります。

そのため、利用状況によっては各プラットフォームでのコンポーネント選択が必要となってきますので、注意してください。
(音楽プレーヤーアプリを作成する場合なら、Xamarin.Formsを利用して共通化せずにXamarin.Nativeを使った方が効率はいいでしょう。)

Search index:
[Xamarin.Forms] Background Audio play
[Xamarin.Forms] background music player

この記事をシェアする
Tweet about this on Twitter
Twitter
Share on Facebook
Facebook
0

Comments

Comments