Aracılığıyla paylaş


Ses ekleme

Uyarı

Bu konu, DirectX öğretici serisi ile basit bir Evrensel Windows Platformu (UWP) oluşturma oyunun bir parçasıdır. Bu bağlantıdaki konu başlığı, serinin bağlamını ayarlar.

Bu konu başlığında XAudio2 API'lerini kullanarak basit bir ses altyapısı oluşturacağız. XAudio2konusunda yeniyse Ses kavramlarıaltında kısa bir giriş ekledik.

Uyarı

Bu örnek için en son oyun kodunu indirmediyseniz Direct3D örnek oyunbölümüne gidin. Bu örnek, büyük bir UWP özellik örnekleri koleksiyonunun bir parçasıdır. Örneği indirme talimatları için, Windows geliştirme için örnek uygulamalar sayfasına bakın.

Amaç

XAudio2 kullanarak örnek oyuna sesler ekleyin.

Ses altyapısını tanımlama

Örnek oyunda ses nesneleri ve davranışları üç dosyada tanımlanır:

  • Audio.h/.cpp: Ses oynatma için XAudio2 kaynaklarını içeren Audio nesnesini tanımlar. Ayrıca oyun duraklatılırsa veya devre dışı bırakılırsa ses oynatmayı askıya alma ve devam ettirme yöntemini tanımlar.
  • MediaReader.h/.cpp: Yerel depolamadan ses .wav dosyalarını okuma yöntemlerini tanımlar.
  • SoundEffect.h/.cpp: Oyun içi ses çalma için bir nesne tanımlar.

Genel Bakış

Oyununuzda ses oynatma için hazırlığın üç ana bölümü vardır.

  1. Ses kaynaklarını oluşturma ve başlatma
  2. Ses dosyasını yükleme
  3. Sesi nesneyle ilişkilendirme

Bunların tümü Simple3DGame::Initialize yönteminde tanımlanır. Şimdi önce bu yöntemi inceleyelim ve ardından bölümlerin her birinde daha fazla ayrıntıya bakalım.

Ayarladıktan sonra ses efektlerini tetiklemeyi öğreniriz. Daha fazla bilgi için göz atın ve sesioynatın.

Simple3DGame::Initialize yöntemi

Simple3DGame::Initializeiçinde, m_controller ve m_renderer de başlatılırken ses motorunu kurar ve sesleri çalmaya hazır hale getiririz.

  • Audio sınıfının bir örneği olan m_audioController oluşturun.
  • Audio::CreateDeviceIndependentResources yöntemini kullanarak gereken ses kaynaklarını oluşturun. Burada iki XAudio2 nesnesi oluşturuldu: bir müzik motoru nesnesi, bir ses motoru nesnesi ve her biri için birer master sesi. Müzik motoru nesnesi, oyununuz için arka plan müziği çalmak için kullanılabilir. Ses motoru, oyununuzda ses efektleri çalmak için kullanılabilir. Daha fazla bilgi için bkz. Ses kaynaklarını oluşturma ve başlatma.
  • MediaReader sınıfının bir örneği olan mediaReader'ı oluşturun. SoundEffect sınıfının yardımcı sınıfı olan MediaReader, küçük ses dosyalarını dosya konumundan zaman uyumlu olarak okur ve ses verilerini bayt dizisi olarak döndürür.
  • MediaReader::LoadMedia kullanarak ses dosyalarını konumundan yükleyin ve yüklenen .wav ses verilerini tutmak için bir targetHitSound değişkeni oluşturun. Daha fazla bilgi için bkz. ses dosyasını yükleme.

Ses efektleri oyun nesnesiyle ilişkilendirilir. Bu nedenle, bu oyun nesnesiyle bir çarpışma oluştuğunda, ses efektinin çalınmasını tetikler. Bu örnek oyunda, mermi (hedeflere ateş etmek için kullandığımız şey) ve hedef için ses efektlerine sahibiz.

  • GameObject sınıfında, ses efektini nesneyle ilişkilendirmek için kullanılan bir HitSound özelliği vardır.
  • SoundEffect sınıfının yeni bir örneğini oluşturun ve başlatın. Başlatma sırasında, ses efekti için bir kaynak ses oluşturulur.
  • Bu sınıf, Ses sınıfından sağlanan bir master sesi kullanarak bir ses çalar. Ses verileri MediaReader sınıfı kullanılarak dosya konumundan okunur. Daha fazla bilgi için bkz. Sesi nesneyle ilişkilendirme.

Uyarı

Sesin çalınması için asıl tetikleyici, bu oyun nesnelerinin hareketi ve çarpışması tarafından belirlenir. Bu nedenle, bu sesleri gerçekten çalma çağrısı Simple3DGame::UpdateDynamics yönteminde tanımlanır. Daha fazla bilgi için göz atın ve sesioynatın.

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]);
    }
    ...
}

Ses kaynaklarını oluşturma ve başlatma

  • Müzik ve ses efekti altyapılarını tanımlayan iki yeni XAudio2 nesnesi oluşturmak için XAudio2Create,bir XAudio2 API'sini kullanın. Bu yöntem, nesnenin tüm ses altyapısı durumlarını, ses işleme yazışmasını, ses grafiğini ve daha fazlasını yöneten IXAudio2 arabirimine bir işaretçi döndürür.
  • Motorlar örneklendirildikten sonra, ses motoru nesnelerinin her biri için bir master sesi oluşturmak üzere IXAudio2::CreateMasteringVoice kullanın.

Daha fazla bilgi için Nasıl yapılır: XAudio2başlatma bölümüne gidin.

Audio::CreateDeviceIndependentResources yöntemi

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;
}

Ses dosyasını yükleme

Örnek oyunda, ses biçimi dosyalarını okumak için kod MediaReader.h/cpp__ içinde tanımlanır. Kodlanmış bir .wav ses dosyasını okumak için MediaReader::LoadMedia çağrısı yapın ve .wav dosya adını giriş parametresi olarak geçirin.

MediaReader::LoadMedia yöntemi

Bu yöntem, .wav ses dosyasını Pulse Code Modulation (PCM) arabelleği olarak okumak için Media Foundation API'lerini kullanır.

Kaynak Okuyucu'yı ayarlama

  1. Medya kaynak okuyucusu (IMFSourceReader) oluşturmak için MFCreateSourceReaderFromURL kullanın.
  2. MFCreateMediaType kullanarak bir medya türü (IMFMediaType) nesnesi (mediaType) oluşturun. Medya biçiminin açıklamasını temsil eder.
  3. mediaType'ın çözümlenmiş çıktısının, XAudio2 'ün kullanabileceği bir ses türü olan PCM ses olduğunu belirtin.
  4. KAYNAK okuyucu için çözülen çıkış medya türünü , IMFSourceReader::SetCurrentMediaType çağrısı yaparak ayarlar.

Kaynak Okuyucuyu neden kullandığımız hakkında daha fazla bilgi için Kaynak Okuyucu'ya gidin.

Ses akışının veri biçimini açıklama

  1. Akışın geçerli medya türünü almak için IMFSourceReader::GetCurrentMediaType kullanın.
  2. Önceki işlemin sonuçlarını giriş olarak kullanarak geçerli ses medya türünü WAVEFORMATEX arabelleğe dönüştürmek için IMFMediaType::MFCreateWaveFormatExFromMFMediaType kullanın. Bu yapı, ses yüklendikten sonra kullanılan dalga ses akışının veri biçimini belirtir.

WAVEFORMATEX biçimi, PCM arabelleği tanımlamak için kullanılabilir. WAVEFORMATEXTENSIBLE yapısıyla karşılaştırıldığında, yalnızca ses dalgası biçimlerinin bir alt kümesini tanımlamak için kullanılabilir. WAVEFORMATEX ile WAVEFORMATEXTENSIBLE arasındaki farklar hakkında daha fazla bilgi için bkz. Genişletilebilir Wave-Format Tanımlayıcıları.

Ses akışını okuma

  1. IMFSourceReader::GetPresentationAttribute çağrısı yaparak ses akışının süresini saniye cinsinden alın ve süreyi baytlara dönüştürür.
  2. IMFSourceReader::ReadSampleçağrısını yaparak ses dosyasını akış şeklinde okuyun. ReadSample medya kaynağından sonraki örneği okur.
  3. Ses örneği arabelleğinin (örnek) içeriğini bir diziye (mediaBuffer) kopyalamak için IMFSample::ConvertToContiguousBuffer kullanın.
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;
}

Sesi nesneyle ilişkilendirme

Simple3DGame::Initialize yönteminde, oyun başlatıldığında sesler nesneyle ilişkilendirilir.

Özet

  • GameObject sınıfında, ses efektini nesneyle ilişkilendirmek için kullanılan bir HitSound özelliği vardır.
  • SoundEffect sınıf nesnesinin yeni bir örneğini oluşturun ve bunu oyun nesnesiyle ilişkilendirin. Bu sınıf , XAudio2 API'lerini kullanarak bir ses çalar. Ses sınıfı tarafından sağlanan bir master ses kullanır. Ses verileri MediaReader sınıfı kullanılarak dosya konumundan okunabilir.

SoundEffect::Initialize, SoundEffect örneğini şu giriş parametreleriyle başlatmak için kullanılır: ses altyapısı nesnesi işaretçisi (IXAudio2 nesneleri, Audio::CreateDeviceIndependentResources yöntemi içinde oluşturulan), .wav dosyasının formatına ilişkin işaretçi, MediaReader::GetOutputWaveFormatExkullanılarak ve MediaReader::LoadMedia yöntemi kullanılarak yüklenen ses verileri. Başlatma sırasında, ses efekti için kaynak ses de oluşturulur.

SoundEffect::Initialize yöntemi

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;
}

Sesi oynat

Ses efektlerini oynatma tetikleyicileri Simple3DGame::UpdateDynamics yönteminde tanımlanır çünkü bu, nesnelerin hareketinin güncelleştirildiği ve nesneler arasındaki çakışmanın belirlendiği yerdir.

Nesneler arasındaki etkileşim büyük ölçüde farklılık gösterdiğinden, oyuna bağlı olarak, burada oyun nesnelerinin dinamiklerini tartışmayacağız. Uygulamasını anlamak istiyorsanız Simple3DGame::UpdateDynamics yöntemine gidin.

Prensip olarak, bir çarpışma meydana geldiğinde, SoundEffect::PlaySoundçağrısını yaparak ses efektinin çalmasını tetikler. Bu yöntem, şu anda çalmakta olan ses efektlerini durdurur ve bellekteki arabelleği istenen ses verileriyle kuyruğa alır. Ses düzeyini ayarlamak, ses verilerini göndermek ve kayıttan yürütmeyi başlatmak için kaynak sesi kullanır.

SoundEffect::PlaySound metodu

  • Ses veri arabelleğinin kayıttan yürütülmesini başlatmak için kaynak ses nesnesi m_sourceVoice kullanır m_soundData
  • Ses veri arabelleğine bir başvuru sağladığı XAUDIO2_BUFFERoluşturur ve ardından bunu IXAudio2SourceVoice::SubmitSourceBufferçağrısını kullanarak gönderir.
  • Ses verileri kuyruğa alındıktan sonra, SoundEffect::PlaySound, IXAudio2SourceVoice::Startişlevini çağırarak çalmaya başlar.
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()
        );
}

Simple3DGame::UpdateDynamics yöntemi

Simple3DGame::UpdateDynamics yöntemi, oyun nesneleri arasındaki etkileşimi ve çakışmayı üstlenir. Nesneler çarpıştığında (veya kesiştiğinde), ilişkili ses efektini çalmaya tetikler.

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
}

Sonraki Adımlar

Bir Windows 10 oyununun UWP çerçevesini, grafiklerini, denetimlerini, kullanıcı arabirimini ve sesini ele aldık. Bu öğreticinin bir sonraki bölümü, Örnek oyun'i genişletme, bir oyun geliştirirken kullanılabilecek diğer seçenekleri açıklar.

Ses kavramları

Windows 10 oyunları geliştirme için XAudio2 sürüm 2.9'ı kullanın. Bu sürüm Windows 10 ile birlikte gönderilir. Daha fazla bilgi için XAudio2 Sürümleri'ne gidin.

AudioX2 , sinyal işleme ve karıştırma temeli sağlayan düşük düzeyli bir API'dir. Daha fazla bilgi için bkz. XAudio2 Temel Kavramları.

XAudio2 sesleri

Üç tür XAudio2 ses nesnesi vardır: kaynak, alt miks ve ana ses. Sesler, XAudio2'nin ses verilerini işlemek, manipüle etmek ve çalmak için kullandığı nesnelerdir.

  • Kaynak sesler, istemci tarafından sağlanan ses verileri üzerinde çalışır.
  • Kaynak ve alt miks sesleri çıkışlarını bir veya daha fazla alt miks veya ana miks sese gönderir.
  • Submix ve mastering sesleri, onları besleyen tüm seslerden gelen sesi karıştırır ve sonuç üzerinde çalışır.
  • Master sesler, kaynaktan gelen sesler ve alt karışım seslerinden veri alır ve bu verileri ses donanımına gönderir.

Daha fazla bilgi için XAudio2 seslerisayfasına gidin.

Ses grafı

Ses grafı,XAudio2 seslerinden oluşan bir koleksiyondur. Ses, kaynak seslerde ses grafiğinin bir tarafında başlar, isteğe bağlı olarak bir veya daha fazla alt sesten geçer ve ana seste biter. Bir ses grafiği, şu anda çalmakta olan her ses için bir kaynak sesi, sıfır veya daha fazla alt karışım sesi ve bir ana karışım sesi içerir. En basit ses grafiği ve XAudio2'de gürültü oluşturmak için gereken asgari gereklilik, doğrudan yönetici sese çıkan tek bir kaynak sestir. Daha fazla bilgi için Ses grafikleri'ne gidin.

Ek okuma

Anahtar ses .h dosyaları

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:
    ...
};