Udostępnij za pomocą


Dodawanie dźwięku

Uwaga / Notatka

Ten temat jest częścią serii samouczków pt. Tworzenie prostej gry na uniwersalną platformę Windows (UWP) z użyciem DirectX. Ten temat pod tym linkiem ustawia kontekst serii.

W tym temacie utworzymy prosty silnik dźwiękowy przy użyciu API XAudio2. Jeśli dopiero zaczynasz XAudio2, umieściliśmy krótki wstęp dotyczący pojęć audio.

Uwaga / Notatka

Jeśli nie pobrałeś najnowszego kodu gry tego przykładu, przejdź na przykład gry Direct3D. Ten przykład jest częścią dużej kolekcji przykładów funkcji platformy UWP. Aby uzyskać instrukcje dotyczące pobierania przykładu, zobacz Przykładowe aplikacje do programowania systemu Windows.

Cel

Dodaj dźwięki do przykładowej gry przy użyciu XAudio2.

Zdefiniuj aparat audio

W przykładowej grze obiekty audio i zachowania są definiowane w trzech plikach:

  • Audio.h/.cpp: Definiuje obiekt Audio, który zawiera zasoby XAudio2 na potrzeby odtwarzania dźwięku. Definiuje również metodę zawieszania i wznawiania odtwarzania audio, jeśli gra jest wstrzymana lub dezaktywowana.
  • MediaReader.h/.cpp: definiuje metody odczytywania plików .wav audio z magazynu lokalnego.
  • SoundEffect.h/.cpp: Definiuje obiekt do odtwarzania dźwięku w grze.

Przegląd

Aby skonfigurować odtwarzanie audio w swojej grze, należy uwzględnić trzy główne elementy.

  1. tworzenie i inicjowanie zasobów audio
  2. Załaduj plik audio
  3. Kojarzenie dźwięku z obiektem

Wszystkie są zdefiniowane w metodzie Simple3DGame::Initialize. Najpierw przyjrzyjmy się tej metodzie, a następnie przyjrzyjmy się bliżej poszczególnym sekcjom.

Po skonfigurowaniu nauczymy się wyzwalać efekty dźwiękowe do odtwarzania. Aby uzyskać więcej informacji, przejdź do i odtwórz dźwięk.

Metoda Simple3DGame::Initialize

W Simple3DGame::Initialize, gdzie również inicjowane są m_controller i m_renderer, konfigurujemy silnik audio i przygotowujemy go do odtwarzania dźwięków.

  • Utwórz m_audioController, jako wystąpienie klasy Audio.
  • Utwórz zasoby audio potrzebne przy użyciu metody Audio::CreateDeviceIndependentResources. W tym miejscu utworzono dwa obiekty XAudio2 — obiekt silnika muzycznego i obiekt silnika dźwiękowego oraz głos główny dla każdego z nich. Obiekt silnika muzycznego może służyć do odtwarzania muzyki w tle twojej gry. Silnik dźwiękowy może być używany do odtwarzania efektów dźwiękowych w twojej grze. Aby uzyskać więcej informacji, zobacz Tworzenie i inicjowanie zasobów audio.
  • Utwórz mediaReader, czyli wystąpienie klasy MediaReader. MediaReader, która jest klasą pomocnika klasy SoundEffect, odczytuje małe pliki audio synchronicznie z lokalizacji pliku i zwraca dane dźwiękowe jako tablicę bajtów.
  • Użyj MediaReader::LoadMedia, aby załadować pliki dźwiękowe z jego lokalizacji i utworzyć targetHitSound zmienną do przechowywania załadowanych .wav danych dźwiękowych. Aby uzyskać więcej informacji, zobacz Załaduj plik audio.

Efekty dźwiękowe są skojarzone z obiektem gry. Więc gdy dojdzie do kolizji z tym obiektem gry, wyzwala efekt dźwiękowy, który ma być odtwarzany. W tej przykładowej grze mamy efekty dźwiękowe dla amunicji (czego używamy do strzelania do celów) i dla celów.

  • W klasie GameObject istnieje właściwość HitSound używana do skojarzenia efektu dźwiękowego z obiektem.
  • Utwórz nowe wystąpienie klasy SoundEffect i zainicjuj je. Podczas inicjowania tworzony jest głos źródłowy dla efektu dźwięku.
  • Ta klasa odtwarza dźwięk przy użyciu głosu masteringu dostarczonego z klasy Audio . Dane dźwiękowe są odczytywane z lokalizacji pliku przy użyciu klasy MediaReader. Aby uzyskać więcej informacji, zobacz Kojarzenie dźwięku z obiektem.

Uwaga / Notatka

Rzeczywisty wyzwalacz do odtwarzania dźwięku jest określany przez ruch i kolizję tych obiektów gry. W związku z tym wywołanie rzeczywistego odtwarzania tych dźwięków jest zdefiniowane w metodzie Simple3DGame::UpdateDynamics. Aby uzyskać więcej informacji, przejdź do i odtwórz dźwięk.

void Simple3DGame::Initialize(
    _In_ std::shared_ptr<MoveLookController> const& controller,
    _In_ std::shared_ptr<GameRenderer> const& renderer
    )
{
    // The following member is defined in the header file:
    // Audio m_audioController;

    ...

    // Create the audio resources needed.
    // Two XAudio2 objects are created - one for music engine,
    // the other for sound engine. A mastering voice is also
    // created for each of the objects.
    m_audioController.CreateDeviceIndependentResources();

    m_ammo.resize(GameConstants::MaxAmmo);

    ...

    // Create a media reader which is used to read audio files from its file location.
    MediaReader mediaReader;
    auto targetHitSoundX = mediaReader.LoadMedia(L"Assets\\hit.wav");

    // Instantiate the targets for use in the game.
    // Each target has a different initial position, size, and orientation.
    // But share a common set of material properties.
    for (int a = 1; a < GameConstants::MaxTargets; a++)
    {
        ...
        // Create a new sound effect object and associate it
        // with the game object's (target) HitSound property.
        target->HitSound(std::make_shared<SoundEffect>());

        // Initialize the sound effect object with
        // the sound effect engine, format of the audio wave, and audio data
        // During initialization, source voice of this sound effect is also created.
        target->HitSound()->Initialize(
            m_audioController.SoundEffectEngine(),
            mediaReader.GetOutputWaveFormatEx(),
            targetHitSoundX
            );
        ...
    }

    // Instantiate a set of spheres to be used as ammunition for the game
    // and set the material properties of the spheres.
    auto ammoHitSound = mediaReader.LoadMedia(L"Assets\\bounce.wav");

    for (int a = 0; a < GameConstants::MaxAmmo; a++)
    {
        m_ammo[a] = std::make_shared<Sphere>();
        m_ammo[a]->Radius(GameConstants::AmmoRadius);
        m_ammo[a]->HitSound(std::make_shared<SoundEffect>());
        m_ammo[a]->HitSound()->Initialize(
            m_audioController.SoundEffectEngine(),
            mediaReader.GetOutputWaveFormatEx(),
            ammoHitSound
            );
        m_ammo[a]->Active(false);
        m_renderObjects.push_back(m_ammo[a]);
    }
    ...
}

Tworzenie i inicjowanie zasobów audio

  • Użyj XAudio2Create, interfejsu API XAudio2, aby utworzyć dwa nowe obiekty XAudio2, które będą reprezentować silniki muzyczne i efektów dźwiękowych. Ta metoda zwraca wskaźnik do interfejsu IXAudio2 obiektu, który zarządza wszystkimi stanami silnika audio, wątkiem przetwarzania dźwięku, grafem głosu.
  • Po utworzeniu instancji silników użyj IXAudio2::CreateMasteringVoice, aby utworzyć głos główny dla każdego obiektu aparatu dźwiękowego.

Aby uzyskać więcej informacji, przejdź do Jak zainicjować XAudio2.

Metoda Audio::CreateDeviceIndependentResources

void Audio::CreateDeviceIndependentResources()
{
    UINT32 flags = 0;

    winrt::check_hresult(
        XAudio2Create(m_musicEngine.put(), flags)
        );

    HRESULT hr = m_musicEngine->CreateMasteringVoice(&m_musicMasteringVoice);
    if (FAILED(hr))
    {
        // Unable to create an audio device
        m_audioAvailable = false;
        return;
    }

    winrt::check_hresult(
        XAudio2Create(m_soundEffectEngine.put(), flags)
        );

    winrt::check_hresult(
        m_soundEffectEngine->CreateMasteringVoice(&m_soundEffectMasteringVoice)
        );

    m_audioAvailable = true;
}

Ładowanie pliku audio

W przykładowej grze kod odczytu plików w formacie audio jest zdefiniowany w MediaReader.h/cpp__. Aby odczytać zakodowany plik audio .wav, wywołaj metodę MediaReader::LoadMedia, przekazując nazwę pliku .wav jako parametr wejściowy.

metoda MediaReader::LoadMedia

Ta metoda używa interfejsów API Media Foundation do odczytywania pliku audio .wav jako buforu modulacji kodu impulsowego (PCM).

Konfigurowanie czytnika źródła

  1. Użyj MFCreateSourceReaderFromURL, aby utworzyć czytnik źródła multimediów (IMFSourceReader).
  2. Użyj MFCreateMediaType, aby utworzyć obiekt typu mediów (IMFMediaType) (mediaType). Reprezentuje opis formatu nośnika.
  3. Określ, że mediaTypedekodowane dane wyjściowe to dźwięk PCM, który jest typem audio, który XAudio2 może wykorzystać.
  4. Ustawia typ zdekodowanego nośnika wyjściowego dla czytnika źródłowego, wywołując IMFSourceReader::SetCurrentMediaType.

Aby uzyskać więcej informacji na temat tego, dlaczego używamy Source Reader, przejdź do Source Reader.

Opis formatu danych strumienia audio

  1. Użyj IMFSourceReader::GetCurrentMediaType, aby uzyskać bieżący typ nośnika dla strumienia.
  2. Użyj IMFMediaType::MFCreateWaveFormatExFromMFMediaType, aby przekonwertować bieżący typ nośnika audio na bufor WAVEFORMATEX, używając wyników wcześniejszej operacji jako danych wejściowych. Ta struktura określa format danych strumienia dźwięku falowego, który jest używany po załadowaniu dźwięku.

Format WAVEFORMATEX może służyć do opisania buforu PCM. W porównaniu do struktury WAVEFORMATEXTENSIBLE można jej użyć tylko do opisania podzestawu formatów fal dźwiękowych. Aby uzyskać więcej informacji na temat różnic między WAVEFORMATEX i WAVEFORMATEXTENSIBLE, zobacz Rozszerzalne Deskryptory Wave-Format.

Odczytywanie strumienia audio

  1. Pobierz czas trwania strumienia audio w sekundach, wywołaj metodę IMFSourceReader::GetPresentationAttribute, a następnie przekonwertuj czas trwania na bajty.
  2. Odczytaj plik audio jako strumień, wywołując IMFSourceReader::ReadSample. ReadSample odczytuje następną próbkę ze źródła multimediów.
  3. Użyj IMFSample::ConvertToContiguousBuffer, aby skopiować zawartość buforu próbki audio (próbka) do tablicy (mediaBuffer).
std::vector<byte> MediaReader::LoadMedia(_In_ winrt::hstring const& filename)
{
    winrt::check_hresult(
        MFStartup(MF_VERSION)
        );

    // Creates a media source reader.
    winrt::com_ptr<IMFSourceReader> reader;
    winrt::check_hresult(
        MFCreateSourceReaderFromURL(
        (m_installedLocationPath + filename).c_str(),
            nullptr,
            reader.put()
            )
        );

    // Set the decoded output format as PCM.
    // XAudio2 on Windows can process PCM and ADPCM-encoded buffers.
    // When using MediaFoundation, this sample always decodes into PCM.
    winrt::com_ptr<IMFMediaType> mediaType;
    winrt::check_hresult(
        MFCreateMediaType(mediaType.put())
        );

    // Define the major category of the media as audio. For more info about major media types,
    // go to: https://msdn.microsoft.com/library/windows/desktop/aa367377.aspx
    winrt::check_hresult(
        mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio)
        );

    // Define the sub-type of the media as uncompressed PCM audio. For more info about audio sub-types,
    // go to: https://msdn.microsoft.com/library/windows/desktop/aa372553.aspx
    winrt::check_hresult(
        mediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM)
        );

    // Sets the media type for a stream. This media type defines that format that the Source Reader 
    // produces as output. It can differ from the native format provided by the media source.
    // For more info, go to https://msdn.microsoft.com/library/windows/desktop/dd374667.aspx
    winrt::check_hresult(
        reader->SetCurrentMediaType(static_cast<uint32_t>(MF_SOURCE_READER_FIRST_AUDIO_STREAM), 0, mediaType.get())
        );

    // Get the current media type for the stream.
    // For more info, go to:
    // https://msdn.microsoft.com/library/windows/desktop/dd374660.aspx
    winrt::com_ptr<IMFMediaType> outputMediaType;
    winrt::check_hresult(
        reader->GetCurrentMediaType(static_cast<uint32_t>(MF_SOURCE_READER_FIRST_AUDIO_STREAM), outputMediaType.put())
        );

    // Converts the current media type into the WaveFormatEx buffer structure.
    UINT32 size = 0;
    WAVEFORMATEX* waveFormat;
    winrt::check_hresult(
        MFCreateWaveFormatExFromMFMediaType(outputMediaType.get(), &waveFormat, &size)
        );

    // Copies the waveFormat's block of memory to the starting address of the m_waveFormat variable in MediaReader.
    // Then free the waveFormat memory block.
    // For more info, go to https://msdn.microsoft.com/library/windows/desktop/aa366535.aspx and
    // https://msdn.microsoft.com/library/windows/desktop/ms680722.aspx
    CopyMemory(&m_waveFormat, waveFormat, sizeof(m_waveFormat));
    CoTaskMemFree(waveFormat);

    PROPVARIANT propVariant;
    winrt::check_hresult(
        reader->GetPresentationAttribute(static_cast<uint32_t>(MF_SOURCE_READER_MEDIASOURCE), MF_PD_DURATION, &propVariant)
        );

    // 'duration' is in 100ns units; convert to seconds, and round up
    // to the nearest whole byte.
    LONGLONG duration = propVariant.uhVal.QuadPart;
    unsigned int maxStreamLengthInBytes =
        static_cast<unsigned int>(
            ((duration * static_cast<ULONGLONG>(m_waveFormat.nAvgBytesPerSec)) + 10000000) /
            10000000
            );

    std::vector<byte> fileData(maxStreamLengthInBytes);

    winrt::com_ptr<IMFSample> sample;
    winrt::com_ptr<IMFMediaBuffer> mediaBuffer;
    DWORD flags = 0;

    int positionInData = 0;
    bool done = false;
    while (!done)
    {
        // Read audio data.
        ...
    }

    return fileData;
}

Kojarzenie dźwięku z obiektem

Kojarzenie dźwięków z obiektem odbywa się, gdy gra się inicjuje, w Simple3DGame::Initialize metodzie.

Podsumowanie:

  • W klasie GameObject istnieje właściwość HitSound używana do skojarzenia efektu dźwiękowego z obiektem.
  • Utwórz nowe wystąpienie obiektu klasy SoundEffect i skojarz je z obiektem gry. Ta klasa odtwarza dźwięk przy użyciu interfejsów API XAudio2. Używa głosu masteringowego dostarczonego przez klasę Audio. Dane dźwiękowe mogą być odczytywane z lokalizacji pliku przy użyciu klasy MediaReader.

SoundEffect::Initialize służy do inicjowania wystąpienia SoundEffect z następującymi parametrami wejściowymi: wskaźnik do obiektu silnika dźwięku (obiekty IXAudio2 utworzone w metodzie Audio::CreateDeviceIndependentResources), wskaźnik do formatu pliku .wav przy użyciu MediaReader::GetOutputWaveFormatEx, oraz dane dźwiękowe, które są ładowane przy użyciu metody MediaReader::LoadMedia. Podczas inicjowania tworzony jest również głos źródłowy dla efektu dźwięku.

Metoda SoundEffect::Initialize

void SoundEffect::Initialize(
    _In_ IXAudio2* masteringEngine,
    _In_ WAVEFORMATEX* sourceFormat,
    _In_ std::vector<byte> const& soundData)
{
    m_soundData = soundData;

    if (masteringEngine == nullptr)
    {
        // Audio is not available so just return.
        m_audioAvailable = false;
        return;
    }

    // Create a source voice for this sound effect.
    winrt::check_hresult(
        masteringEngine->CreateSourceVoice(
            &m_sourceVoice,
            sourceFormat
            )
        );
    m_audioAvailable = true;
}

Odtwarzanie dźwięku

Wyzwalacze odtwarzania efektów dźwiękowych są definiowane w Simple3DGame::UpdateDynamics metodzie, ponieważ to tam, gdzie aktualizowany jest ruch obiektów i określane są kolizje między nimi.

Ponieważ interakcja między obiektami różni się znacznie, w zależności od gry, nie będziemy tutaj omawiać dynamiki obiektów gry. Jeśli chcesz zrozumieć jego implementację, przejdź do metody Simple3DGame::UpdateDynamics.

W zasadzie, gdy wystąpi kolizja, uruchamia odtworzenie efektu dźwiękowego poprzez wywołanie SoundEffect::PlaySound. Ta metoda zatrzymuje wszelkie efekty dźwiękowe, które są obecnie odtwarzane, i umieszcza w kolejce bufor pamięciowy z żądanymi danymi dźwiękowymi. Używa głosu źródłowego do ustawiania głośności, przesyłania danych dźwiękowych i uruchamiania odtwarzania.

SoundEffect::metoda PlaySound

  • Używa źródłowego obiektu głosowego m_sourceVoice, aby rozpocząć odtwarzanie buforu danych dźwiękowych m_soundData
  • Tworzy XAUDIO2_BUFFER, do którego zawiera odwołanie do buforu danych dźwiękowych, a następnie przesyła je za pomocą wywołania do IXAudio2SourceVoice::SubmitSourceBuffer.
  • Po ustawieniu danych dźwiękowych w kolejce SoundEffect::PlaySound uruchamia odtwarzanie, wywołując IXAudio2SourceVoice::Start.
void SoundEffect::PlaySound(_In_ float volume)
{
    XAUDIO2_BUFFER buffer = { 0 };

    if (!m_audioAvailable)
    {
        // Audio is not available so just return.
        return;
    }

    // Interrupt sound effect if it is currently playing.
    winrt::check_hresult(
        m_sourceVoice->Stop()
        );
    winrt::check_hresult(
        m_sourceVoice->FlushSourceBuffers()
        );

    // Queue the memory buffer for playback and start the voice.
    buffer.AudioBytes = (UINT32)m_soundData.size();
    buffer.pAudioData = m_soundData.data();
    buffer.Flags = XAUDIO2_END_OF_STREAM;

    winrt::check_hresult(
        m_sourceVoice->SetVolume(volume)
        );
    winrt::check_hresult(
        m_sourceVoice->SubmitSourceBuffer(&buffer)
        );
    winrt::check_hresult(
        m_sourceVoice->Start()
        );
}

Metoda Simple3DGame::UpdateDynamics

Metoda Simple3DGame::UpdateDynamics dba o interakcję i kolizję między obiektami gry. Kiedy obiekty zderzają się (lub przecinają), uruchamiany jest skojarzony efekt dźwiękowy.

void Simple3DGame::UpdateDynamics()
{
    ...
    // Check for collisions between ammo.
#pragma region inter-ammo collision detection
if (m_ammoCount > 1)
{
    ...
    // Check collision between instances One and Two.
    ...
    if (distanceSquared < (GameConstants::AmmoSize * GameConstants::AmmoSize))
    {
        // The two ammo are intersecting.
        ...
        // Start playing the sounds for the impact between the two balls.
        m_ammo[one]->PlaySound(impact, m_player->Position());
        m_ammo[two]->PlaySound(impact, m_player->Position());
    }
}
#pragma endregion

#pragma region Ammo-Object intersections
    // Check for intersections between the ammo and the other objects in the scene.
    // ...
    // Ball is in contact with Object.
    // ...

    // Make sure that the ball is actually headed towards the object. At grazing angles there
    // could appear to be an impact when the ball is actually already hit and moving away.

    if (impact > 0.0f)
    {
        ...
        // Play the sound associated with the Ammo hitting something.
        m_objects[i]->PlaySound(impact, m_player->Position());

        if (m_objects[i]->Target() && !m_objects[i]->Hit())
        {
            // The object is a target and isn't currently hit, so mark
            // it as hit and play the sound associated with the impact.
            m_objects[i]->Hit(true);
            m_objects[i]->HitTime(timeTotal);
            m_totalHits++;

            m_objects[i]->PlaySound(impact, m_player->Position());
        }
        ...
    }
#pragma endregion

#pragma region Apply Gravity and world intersection
            // Apply gravity and check for collision against enclosing volume.
            ...
                if (position.z < limit)
                {
                    // The ammo instance hit the a wall in the min Z direction.
                    // Align the ammo instance to the wall, invert the Z component of the velocity and
                    // play the impact sound.
                    position.z = limit;
                    m_ammo[i]->PlaySound(-velocity.z, m_player->Position());
                    velocity.z = -velocity.z * GameConstants::Physics::GroundRestitution;
                }
                ...
#pragma endregion
}

Dalsze kroki

Omówiliśmy platformę UWP, grafikę, kontrolki, interfejs użytkownika i dźwięk gry z systemem Windows 10. W następnej części tego samouczka Rozszerzanie przykładowej gry, wyjaśniono inne opcje, które mogą być używane podczas tworzenia gry.

Pojęcia dotyczące dźwięku

W przypadku tworzenia gier z systemem Windows 10 użyj interfejsu XAudio2 w wersji 2.9. Ta wersja jest dostarczana z systemem Windows 10. Aby uzyskać więcej informacji, przejdź do wersji XAudio2.

AudioX2 to interfejs API niskiego poziomu, który zapewnia podstawy przetwarzania i mieszania sygnałów. Aby uzyskać więcej informacji, zobacz XAudio2 Key Concepts.

Głosy XAudio2

Istnieją trzy typy obiektów głosowych XAudio2: źródło, podmiks i głosy mistrzowskie. Głosy to obiekty XAudio2 używane do przetwarzania, manipulowania i odtwarzania danych dźwiękowych.

  • Głosy źródłowe działają na danych dźwiękowych dostarczonych przez klienta.
  • Głosy źródłowe i podmiksowe wysyłają swój sygnał wyjściowy do co najmniej jednego podmiksu lub głosów masterujących.
  • Submix i mastering głosów miksują dźwięk ze wszystkich źródeł dźwiękowych, które je zasilają, i pracują na uzyskanym efekcie.
  • Głosy master odbierają dane ze źródłowych głosów i głosów podrzędnych oraz wysyłają te dane do sprzętu audio.

Aby uzyskać więcej informacji, przejdź do kanałów XAudio2.

Wykres dźwiękowy

Graf dźwiękowy to zbiór głosów XAudio2 . Dźwięk rozpoczyna się po jednej stronie grafu audio w głosach źródłowych, opcjonalnie przepływa przez jedną lub więcej ścieżek submiksowych i kończy na głosie głównym. Wykres dźwiękowy będzie zawierać głos źródłowy dla każdego aktualnie odtwarzanego dźwięku, zero lub więcej głosów podrzędnych i jeden głos masteringowy. Najprostszy graf dźwiękowy i minimum potrzebne do wygenerowania dźwięku w XAudio2 to pojedyncze źródło głosowe wyprowadzające dźwięk bezpośrednio do głosu masteringowego. Aby uzyskać więcej informacji, zobacz Wykresy audio.

Dalsza lektura

Kluczowe pliki audio .h

Audio.h

// Audio:
// This class uses XAudio2 to provide sound output. It creates two
// engines - one for music and the other for sound effects - each as
// a separate mastering voice.
// The SuspendAudio and ResumeAudio methods can be used to stop
// and start all audio playback.

class Audio
{
public:
    Audio();

    void Initialize();
    void CreateDeviceIndependentResources();
    IXAudio2* MusicEngine();
    IXAudio2* SoundEffectEngine();
    void SuspendAudio();
    void ResumeAudio();

private:
    ...
};

MediaReader.h

// MediaReader:
// This is a helper class for the SoundEffect class. It reads small audio files
// synchronously from the package installed folder and returns sound data as a
// vector of bytes.

class MediaReader
{
public:
    MediaReader();

    std::vector<byte> LoadMedia(_In_ winrt::hstring const& filename);
    WAVEFORMATEX* GetOutputWaveFormatEx();

private:
    winrt::Windows::Storage::StorageFolder  m_installedLocation{ nullptr };
    winrt::hstring                          m_installedLocationPath;
    WAVEFORMATEX                            m_waveFormat;
};

SoundEffect.h

// SoundEffect:
// This class plays a sound using XAudio2. It uses a mastering voice provided
// from the Audio class. The sound data can be read from disk using the MediaReader
// class.

class SoundEffect
{
public:
    SoundEffect();

    void Initialize(
        _In_ IXAudio2* masteringEngine,
        _In_ WAVEFORMATEX* sourceFormat,
        _In_ std::vector<byte> const& soundData
        );

    void PlaySound(_In_ float volume);

private:
    ...
};