Dataniedziela, 9 sierpnia 2026 Czas07:35:44
← Aplikacje mobilne (.NET MAUI)

Lekcja 19. Multimedia w .NET MAUI — odtwarzanie dźwięku

Średni

Po 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

  1. Dodaj suwak Slider (zakres 0-1) sterujący głośnością odtwarzania przez ustawianie _player.Volume w zdarzeniu ValueChanged.

  2. Zbuduj mini-odtwarzacz z listą kilku plików dźwiękowych (Picker albo CollectionView) — 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.

  3. 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łowniku Dictionary<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.