Lekcja 19. Multimedia w .NET MAUI — odtwarzanie dźwięku
ŚredniPo co się tego uczymy?
Niektóre zadania egzaminacyjne i realne aplikacje (odtwarzacz muzyki, aplikacja do nauki wymowy, gra z efektami dźwiękowymi) wymagają odtwarzania dźwięku. W przeciwieństwie do WPF (gdzie służy do tego wbudowana kontrolka MediaElement), .NET MAUI nie ma prostego, wbudowanego mechanizmu audio — trzeba skorzystać z popularnego, stabilnego pakietu NuGet. Ta krótka lekcja pokazuje minimalny, w pełni działający przykład.
Teoria
.NET MAUI samo w sobie nie oferuje żadnej gotowej kontrolki do odtwarzania dźwięku (jak MediaElement w WPF) — do prostego i stabilnego odtwarzania audio korzysta się z popularnego, darmowego pakietu NuGet Plugin.Maui.Audio (autor: jfversluis). Zalety tego pakietu: działa w pełni OFFLINE, działa zarówno na Androidzie, jak i na Windows, NIE wymaga żadnych dodatkowych kontrolek w XAML (sterowanie odbywa się całkowicie z kodu C#), i jest wystarczająco stabilny, żeby pojawiać się w zadaniach egzaminacyjnych.
Dodawanie pliku dźwiękowego do projektu. Plik audio (np. audio.mp3) umieszcza się w folderze Resources/Raw projektu MAUI (to specjalny folder na "surowe" zasoby, dostępne z kodu przez nazwę pliku). Ważne zasady: właściwość pliku Build Action musi być ustawiona na MauiAsset, a nazwa pliku powinna być zapisana małymi literami (wielkość liter ma znaczenie na Androidzie, w przeciwieństwie do Windows).
Rejestracja menedżera audio (krok OBOWIĄZKOWY). Plugin.Maui.Audio korzysta z mechanizmu wstrzykiwania zależności (Dependency Injection) — klasę AudioManager, która zarządza odtwarzaniem, trzeba zarejestrować w kontenerze usług aplikacji w pliku MauiProgram.cs: builder.Services.AddSingleton(AudioManager.Current);. Bez tej jednej linijki dźwięk w ogóle się nie uruchomi — to najczęstsza pułapka przy pierwszym kontakcie z tym pakietem.
Odtwarzanie w kodzie. Trzy kluczowe elementy API: IAudioManager (pobierany przez AudioManager.Current) tworzy odtwarzacze; FileSystem.OpenAppPackageFileAsync("audio.mp3") otwiera strumień (Stream) do pliku z folderu Resources/Raw dołączonego do aplikacji; _audioManager.CreatePlayer(stream) tworzy obiekt IAudioPlayer z tego strumienia. Sam odtwarzacz ma proste metody: Play(), Stop(), Pause(), oraz właściwość Volume (głośność).
Schemat
MauiProgram.cs (start aplikacji)
│
│ builder.Services.AddSingleton(AudioManager.Current); ← OBOWIĄZKOWE
▼
MainPage.xaml.cs
│
│ _audioManager = AudioManager.Current;
▼
Play_Clicked
│
│ stream = FileSystem.OpenAppPackageFileAsync("audio.mp3")
│ (plik z Resources/Raw, Build Action: MauiAsset)
▼
│ _player = _audioManager.CreatePlayer(stream);
▼
│ _player.Play();
▼
dźwięk słyszalny na urządzeniu
Stop_Clicked → _player?.Stop();
Przykład z życia
Aplikacja do nauki języka obcego może odtwarzać krótkie nagranie wymowy słówka po kliknięciu przycisku "Odsłuchaj", a prosta gra mobilna może odtwarzać efekt dźwiękowy po trafieniu w cel — w obu przypadkach plik audio jest dołączony do aplikacji i działa całkowicie offline, bez potrzeby połączenia z internetem, dokładnie tak jak w poniższym minimalnym przykładzie.
MainPage.xaml
<!-- MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="AudioMauiApp.MainPage">
<VerticalStackLayout Padding="30" Spacing="20">
<Label Text="AudioMauiApp - odtwarzanie dźwięku" FontSize="22" HorizontalOptions="Center" />
<Button Text="Odtwórz dźwięk" Clicked="Play_Clicked" />
<Button Text="Zatrzymaj" Clicked="Stop_Clicked" />
</VerticalStackLayout>
</ContentPage>
MauiProgram.cs + MainPage.xaml.cs
// MauiProgram.cs - rejestracja AudioManager (KROK OBOWIĄZKOWY)
using Microsoft.Extensions.Logging;
using Plugin.Maui.Audio;
namespace AudioMauiApp;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
// Bez tej linii dźwięk się nie uruchomi!
builder.Services.AddSingleton(AudioManager.Current);
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
// ---------------------------------------------------------------------
// MainPage.xaml.cs
using Plugin.Maui.Audio;
namespace AudioMauiApp;
public partial class MainPage : ContentPage
{
private readonly IAudioManager _audioManager;
private IAudioPlayer? _player;
public MainPage()
{
InitializeComponent();
_audioManager = AudioManager.Current;
}
private async void Play_Clicked(object sender, EventArgs e)
{
if (_player == null)
{
var stream = await FileSystem.OpenAppPackageFileAsync("audio.mp3");
_player = _audioManager.CreatePlayer(stream);
}
_player.Play();
}
private void Stop_Clicked(object sender, EventArgs e)
{
_player?.Stop();
}
}
Komentarz i wyjaśnienie kodu
Zwróć uwagę na warunek if (_player == null) w Play_Clicked — odtwarzacz jest tworzony TYLKO RAZ, przy pierwszym kliknięciu (bo otwieranie strumienia pliku jest operacją kosztowną), a przy kolejnych kliknięciach po prostu wywołujemy Play() na już istniejącym obiekcie. Gdyby tworzyć nowy _player za każdym razem, poprzednie odtwarzanie mogłoby nie zostać poprawnie zatrzymane.
FileSystem.OpenAppPackageFileAsync(...) to metoda ASYNCHRONICZNA, bo odczyt pliku (nawet dołączonego do aplikacji) wymaga operacji We/Wy — stąd Play_Clicked jest oznaczone jako async void. Operator ?. w _player?.Stop() zabezpiecza przed błędem, gdyby ktoś kliknął "Zatrzymaj" zanim jeszcze cokolwiek zostało odtworzone (czyli _player nadal jest null).
Ćwiczenie samodzielne
Utwórz nowy projekt MAUI, zainstaluj pakiet Plugin.Maui.Audio, dodaj dowolny krótki plik MP3 do Resources/Raw (pamiętając o Build Action: MauiAsset i małych literach w nazwie), i odtwórz powyższy przykład na emulatorze albo urządzeniu z Androidem.
Zadania do pracy własnej
Dodaj suwak
Slider(zakres 0-1) sterujący głośnością odtwarzania przez ustawianie_player.Volumew zdarzeniuValueChanged.Zbuduj mini-odtwarzacz z listą kilku plików dźwiękowych (
PickeralboCollectionView) — wybór pozycji z listy powinien tworzyć NOWY odtwarzacz dla wybranego pliku (pamiętając o zatrzymaniu/zwolnieniu poprzedniego, jeśli wciąż gra) i rozpoczynać jego odtwarzanie.Zbuduj prostą "tablicę dźwięków" (soundboard) z siatką (
Grid) kilku przycisków, gdzie każdy przycisk odtwarza inny, krótki efekt dźwiękowy natychmiast po kliknięciu — dla płynności utwórz WSZYSTKIE odtwarzacze z wyprzedzeniem (np. w konstruktorze strony, w słownikuDictionary<string, IAudioPlayer>), zamiast tworzyć nowy odtwarzacz przy każdym kliknięciu.
Typowe błędy
Brak rejestracji AudioManager.Current w MauiProgram.cs — to zdecydowanie najczęstszy błąd; bez tej linii próba odtworzenia dźwięku zakończy się wyjątkiem albo cichym brakiem działania.
Zły Build Action pliku audio — jeśli plik w Resources/Raw nie ma ustawionego MauiAsset, OpenAppPackageFileAsync nie znajdzie go w gotowej aplikacji, mimo że plik widoczny jest w Eksploratorze rozwiązań.
Wielkie litery w nazwie pliku — na Androidzie nazwy zasobów są wrażliwe na wielkość liter; plik Audio.mp3 w projekcie, ale wywołanie OpenAppPackageFileAsync("audio.mp3") w kodzie, zakończy się błędem "plik nie znaleziony" WYŁĄCZNIE na Androidzie (na Windows może zadziałać, co bywa mylące).
Nawiązanie do egzaminu zawodowego
To uzupełnienie wymagania INF.04.6.2 o element UI "dźwięk", wymieniony wprost w podstawie programowej obok grafiki i animacji. Temat rzadziej spotykany na egzaminie niż formularze czy listy danych, ale wart znajomości przy zadaniach dotyczących aplikacji edukacyjnych, prezentacyjnych czy prostych gier.