.NET MAUIで音源再生

.NET MAUIで、AndroidとiOSで、音源を音量指定込みで再生したい、そんな話。

実装準備

音源ファイルはResource/Rawへ

今回はmp3ファイルで実験。

NuGetに追加

この辺は公式サイトに従って準備していく。

<PackageReference Include="CommunityToolkit.Maui" Version="9.0.0" />
<PackageReference Include="CommunityToolkit.Maui.MediaElement" Version="3.1.1" />
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.40" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.40" />

実装

OS別準備

AndroidはAndroidManifestを修正

これも公式に沿って、LaunchMode = “LaunchMode.SingleTask”とResizeableActivity = “true”をつける。

iOSはMauiProgramを修正

using Microsoft.Extensions.Logging;
using CommunityToolkit.Maui;

namespace HogeHoge
{
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
#if IOS
            AVFoundation.AVAudioSession.SharedInstance().SetActive(true);
            AVFoundation.AVAudioSession.SharedInstance().SetCategory(AVFoundation.AVAudioSessionCategory.Playback);
#endif

MauiProgramにUseMauiCommunityToolkitMediaElementを追加

var builder = MauiApp.CreateBuilder();
builder
    .UseMauiApp<App>()
    .UseMauiCommunityToolkitMediaElement()
    .ConfigureFonts(fonts =>
    {
        fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
        fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
    });

レイアウトにMediaElementを追加

XAMLに記載するときは、embed://ファイル名で記載する。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             x:Class="HogehogeSample.MainPage">
<!-- 中略 -->
            <toolkit:MediaElement x:Name="mediaElement" Source="embed://AAAA.mp3" ShouldAutoPlay="False"/>
<!-- 以下省略 -->

再生音源を変更しながら再生

プロジェクトを新規作成したときの、OnCounterClickedで実験。

// 音を鳴らしてみる
// 前に鳴らすたものがあれば停止してシークを初期化
mediaElement.Stop();

// 音源を変えてみる
if (count % 2 == 0)
{
    mediaElement.Source = MediaSource.FromResource("BBBB.mp3");
}
else
{
    mediaElement.Source = MediaSource.FromResource("AAAA.mp3");
}
// いざ再生
mediaElement.Play();

音量調整

ファイル自体の音量と、OSの音量の違いに注意。
前者はmediaElement.Volume = 0~1;で良さそう。

mediaElement.Volume = 0.5;
#if IOS
    var musicPlayerController = MPMusicPlayerApplicationController.ApplicationMusicPlayer;
    musicPlayerController.Volume = 0.83F;
#elif ANDROID
    var audioManager = (AudioManager)Android.App.Application.Context.GetSystemService(Android.Content.Context.AudioService);
    // ボリュームを50%に設定
    audioManager.SetStreamVolume(Android.Media.Stream.Music, 50, VolumeNotificationFlags.PlaySound);
#endif

一応動くけど、MPMusicPlayerApplicationController.ApplicationMusicPlayer.Volumeは、iOS 7.0以降で廃止されてんぞ警告が出る。
MPVolumeViewを使うらしいが、時間を置く必要があるらしい
ボリューム変更処理を行うクラスをシングルトンとして、アプリ起動時に作っておけばいっか?
iOSとAndroidで完全に分離した方が良いのか?

まず共通部に宣言しておく

internal partial class MasterVolumeControl
{
    private static MasterVolumeControl _singleton = null;

    public static MasterVolumeControl GetInstance()
    {
        if(_singleton == null)
        {
            _singleton = new MasterVolumeControl();
        }
        return _singleton;
    }

    internal partial void setMasterVolume(float orgMasterVolume);
}

Platforms\Androidに実装

internal partial class MasterVolumeControl
{
    [SupportedOSPlatformGuard("Android28.0")]
    internal static bool Is28OrAbove => Build.VERSION.SdkInt >= BuildVersionCodes.P;

    internal partial void setMasterVolume(float orgMasterVolume)
    {
        var audioManager = Android.App.Application.Context.GetSystemService(Context.AudioService) as AudioManager;
        if (audioManager == null)
        {
            throw new Exception("GetSystemService is failed.");
        }
        int max = audioManager.GetStreamMaxVolume(Android.Media.Stream.Music);
        int min = 0;
        if (Is28OrAbove)
        {
            min = audioManager.GetStreamMinVolume(Android.Media.Stream.Music);
        }
        else
        {
            min = audioManager.GetStreamVolume(0);
        }
        int vol = (int)(min + ((max - min) * (orgMasterVolume / 1.0F)));

        audioManager.SetStreamVolume(Android.Media.Stream.Music, vol, VolumeNotificationFlags.PlaySound);
    }
}

Platforms\iOSに実装

internal partial class MasterVolumeControl
{
    MPVolumeView mPVolumeView;

    internal MasterVolumeControl()
    {
        // 時間を置く必要があるらしいので、最初に作っておく
        mPVolumeView = new MPVolumeView();
    }

    internal partial void setMasterVolume(float orgMasterVolume)
    {
        /* 下記方法はiOS 7.0以降で廃止された
        var musicPlayerController = MPMusicPlayerController.ApplicationMusicPlayer;
        musicPlayerController.Volume = orgMasterVolume;
        */
        foreach (UIView subview in mPVolumeView.Subviews)
        {
            if (subview is UISlider)
            {
                ((UISlider)subview).Value = orgMasterVolume;
            }
        }
    }
}

音量調整するなら元に戻さなきゃダメでしょ!

ということは、アプリ起動時あたりで音量を覚えて置く必要があるね。
取得なら素直にできそう。
Androidはこう。

internal partial float getMasterVolume()
{
    float orgMasterVolume;

    var audioManager = Android.App.Application.Context.GetSystemService(Context.AudioService) as AudioManager;
    if (audioManager == null)
    {
        throw new Exception("GetSystemService is failed.");
    }
    int max = audioManager.GetStreamMaxVolume(Android.Media.Stream.Music);
    int vol = audioManager.GetStreamVolume(Android.Media.Stream.Music);

    orgMasterVolume = (float)vol / (float)max;
    return orgMasterVolume;
}

iOSはもっとシンプル。

internal partial float getMasterVolume()
{
    float orgMasterVolume = AVAudioSession.SharedInstance().OutputVolume;
    return orgMasterVolume;
}

他にもニッチなIT関連要素をまとめていますので、よければ一覧記事もご覧ください。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)