다음을 통해 공유


소리 추가

비고

이 항목은 DirectX 자습서 시리즈를 사용하여 간단한 UWP(유니버설 Windows 플랫폼) 게임 만들기의 일부입니다. 그 링크에 있는 주제가 시리즈의 맥락을 설정합니다.

이 항목에서는 XAudio2 API를 사용하여 간단한 사운드 엔진을 만듭니다. 만약 XAudio2에 익숙하지 않다면, 오디오 개념에 관한 짧은 소개를 포함했습니다.

비고

최신 게임 코드를 이 샘플에 대해 아직 다운로드하지 않았다면, Direct3D 샘플 게임로 이동하세요. 이 샘플은 UWP 기능 샘플의 대규모 컬렉션의 일부입니다. 샘플을 다운로드하는 방법에 대한 지침은 Windows 개발 샘플 애플리케이션을 참조하세요.

목표

XAudio2를 사용하여 샘플 게임에 소리를 추가합니다.

오디오 엔진 정의

샘플 게임에서 오디오 개체 및 동작은 다음 세 개의 파일에 정의됩니다.

  • Audio.h/.cpp: 소리 재생을 위한 XAudio2 리소스를 포함하는 Audio 개체를 정의합니다. 또한 게임이 일시 중지되거나 비활성화된 경우 오디오 재생을 일시 중지하고 다시 시작하기 위한 방법을 정의합니다.
  • MediaReader.h/.cpp: 로컬 스토리지에서 오디오 .wav 파일을 읽는 방법을 정의합니다.
  • SoundEffect.h/.cpp: 게임 내 사운드 재생을 위한 개체를 정의합니다.

개요

게임에 오디오 재생을 위한 설정에는 세 가지 주요 부분이 있습니다.

  1. 오디오 리소스 만들기 및 초기화
  2. 오디오 파일을 로드
  3. 소리를 개체에 연결

모두 Simple3DGame::Initialize 메서드에 정의되어 있습니다. 먼저 이 방법을 살펴본 다음 각 섹션에서 자세한 내용을 살펴보겠습니다.

설정 후 소리 효과를 트리거하여 재생하는 방법을 알아봅니다. 자세한 내용은 에서 사운드를로 재생하세요.

Simple3DGame::Initialize 메서드

Simple3DGame::Initialize에서, m_controllerm_renderer도 초기화되며, 우리는 오디오 엔진을 설정하고 소리를 재생할 준비를 합니다.

  • 오디오 클래스의 인스턴스인 m_audioController 만듭니다.
  • Audio::CreateDeviceIndependentResources 메서드를 사용하여 필요한 오디오 리소스를 만듭니다. 여기서는 음악 엔진 개체와 사운드 엔진 개체, 각 개체에 대한 마스터링 음성 등 두 개의 XAudio2 개체가 만들어졌습니다. 음악 엔진 개체를 사용하여 게임의 배경 음악을 재생할 수 있습니다. 사운드 엔진을 사용하여 게임에서 사운드 효과를 재생할 수 있습니다. 자세한 내용은 오디오 리소스 만들기 및 초기화을 참조하세요.
  • MediaReader 클래스의 인스턴스인 mediaReader를 만듭니다. SoundEffect 클래스의 도우미 클래스인 MediaReader는 파일 위치에서 동기적으로 작은 오디오 파일을 읽고 사운드 데이터를 바이트 배열로 반환합니다.
  • MediaReader::LoadMedia를 사용하여 해당 위치에서 사운드 파일을 로드하고 targetHitSound 변수를 만들어 로드된 .wav 사운드 데이터를 저장합니다. 자세한 내용은 오디오 파일로드를 참조하세요.

소리 효과는 게임 개체와 연결됩니다. 따라서 해당 게임 개체와 충돌이 발생하면 소리 효과가 재생되도록 트리거됩니다. 이 샘플 게임에서는 탄약(대상을 쏘는 데 사용하는 것)과 타겟을 위한 음향 효과가 있습니다.

  • GameObject 클래스에는 소리 효과를 개체에 연결하는 데 사용되는 HitSound 속성이 있습니다.
  • SoundEffect 클래스의 새 인스턴스를 만들고 초기화합니다. 초기화 중에 소리 효과에 대한 원본 음성이 생성됩니다.
  • 이 클래스는 오디오 클래스에서 제공하는 마스터링 음성을 사용하여 소리를 재생합니다. MediaReader 클래스를 사용하여 파일 위치에서 사운드 데이터를 읽습니다. 자세한 내용은 소리를 객체에 연결하는 방법을 보려면을 참조하세요.

비고

소리를 재생하는 실제 트리거는 이러한 게임 개체의 움직임과 충돌에 의해 결정됩니다. 따라서 이러한 소리를 실제로 재생하는 호출은 Simple3DGame::UpdateDynamics 메서드에 정의됩니다. 자세한 내용은 에서 사운드를로 재생하세요.

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

오디오 리소스 만들기 및 초기화

  • XAudio2 API인 XAudio2Create를 사용하여 음악 및 음향 효과 엔진을 정의하는 두 개의 새로운 XAudio2 개체를 만듭니다. 이 메서드는 모든 오디오 엔진 상태, 오디오 처리 스레드, 음성 그래프 등을 관리하는 개체의 IXAudio2 인터페이스에 대한 포인터를 반환합니다.
  • 엔진이 인스턴스화되면 IXAudio2::CreateMasteringVoice를 사용하여 각 사운드 엔진 개체에 대한 마스터 음성을 만듭니다.

자세한 내용은 방법: XAudio2초기화로 이동합니다.

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

오디오 파일 로드

샘플 게임에서 오디오 형식 파일을 읽는 코드는 MediaReader.h/cpp__ 정의됩니다. 인코딩된 .wav 오디오 파일을 읽으려면 MediaReader::LoadMedia를 호출하여 .wav 파일 이름을 입력 매개 변수로 전달합니다.

MediaReader::LoadMedia 메서드

이 메서드는 Media Foundation API를 사용하여 .wav 오디오 파일을 펄스 부호 변조(PCM) 버퍼로 읽습니다.

원본 판독기 설정

  1. MFCreateSourceReaderFromURL을 사용하여 미디어 원본 판독기(IMFSourceReader)를 만듭니다.
  2. MFCreateMediaType를 사용하여 미디어 형식(IMFMediaType) 개체(mediaType)를 만듭니다. 미디어 형식에 대한 설명을 나타냅니다.
  3. mediaType디코딩된 출력이 XAudio2 사용할 수 오디오 형식인 PCM 오디오임을 지정합니다.
  4. IMFSourceReader::SetCurrentMediaType을 호출하여 원본 판독기에서 디코딩된 출력 미디어 형식을 설정합니다.

원본 판독기를 사용하는 이유에 대한 자세한 내용은 원본 판독기로 이동하세요.

오디오 스트림의 데이터 형식 설명

  1. IMFSourceReader::GetCurrentMediaType을 사용하여 스트림의 현재 미디어 형식을 가져옵니다.
  2. IMFMediaType::MFCreateWaveFormatExFromMFMediaType을 사용하여 이전 작업의 결과를 입력으로 사용하여 현재 오디오 미디어 형식을 WAVEFORMATEX 버퍼로 변환합니다. 이 구조는 오디오가 로드된 후 사용되는 웨이브 오디오 스트림의 데이터 형식을 지정합니다.

WAVEFORMATEX 형식을 사용하여 PCM 버퍼를 설명할 수 있습니다. WAVEFORMATEXTENSIBLE 구조체와 비교하여 오디오 웨이브 형식의 하위 집합을 설명하는 데만 사용할 수 있습니다. WAVEFORMATEXWAVEFORMATEXTENSIBLE의 차이점에 대한 자세한 정보는 Extensible Wave-Format Descriptors를 참조하십시오.

오디오 스트림 읽기

  1. IMFSourceReader::GetPresentationAttribute를 호출하여 오디오 스트림의 기간을 초 단위로 가져온 다음, 기간을 바이트로 변환합니다.
  2. IMFSourceReader::ReadSample을 호출하여 오디오 파일을 스트림으로 읽습니다. ReadSample 은 미디어 소스에서 다음 샘플을 읽습니다.
  3. IMFSample::ConvertToContiguousBuffer를 사용하여 오디오 샘플 버퍼(샘플)의 내용을 배열(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;
}

소리를 개체에 연관짓기

단순 3DGame::Initialize 메서드에서 게임이 초기화될 때 개체에 소리를 연결합니다.

요약:

  • GameObject 클래스에는 소리 효과를 개체에 연결하는 데 사용되는 HitSound 속성이 있습니다.
  • SoundEffect 클래스 개체의 새 인스턴스를 만들고 게임 개체와 연결합니다. 이 클래스는 XAudio2 API를 사용하여 소리를 재생합니다. 오디오 클래스에서 제공하는 마스터링 음성을 사용합니다. MediaReader 클래스를 사용하여 파일 위치에서 사운드 데이터를 읽을 수 있습니다.

SoundEffect::Initialize는 다음 입력 매개 변수를 사용하여 SoundEffect 인스턴스를 초기화하는 데 사용됩니다: 사운드 엔진 객체에 대한 포인터(이는 Audio::CreateDeviceIndependentResources 메서드에서 생성된 IXAudio2 객체로 구성됨), MediaReader::GetOutputWaveFormatEx을 이용하여 얻은 .wav 파일 포맷에 대한 포인터, 그리고 MediaReader::LoadMedia 메서드를 사용하여 로드된 사운드 데이터. 초기화 중에 소리 효과에 대한 원본 음성도 생성됩니다.

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

소리 재생

소리 효과를 재생하는 트리거는 개체의 이동이 업데이트되고 개체 간의 충돌이 결정되기 때문에 Simple3DGame::UpdateDynamics 메서드에 정의됩니다.

개체 간의 상호 작용은 게임에 따라 크게 다르므로 여기서는 게임 개체의 역학에 대해 논의하지 않을 것입니다. 구현을 이해하려면 Simple3DGame::UpdateDynamics 메서드로 이동합니다.

원칙적으로 충돌이 발생하면 SoundEffect::PlaySound를 호출하여 소리 효과를 재생하도록 작동됩니다. 이 메서드는 현재 재생 중인 모든 소리 효과를 중지하고 원하는 사운드 데이터로 메모리 내 버퍼를 큐에 대기합니다. 원본 음성을 사용하여 볼륨을 설정하고, 소리 데이터를 제출하고, 재생을 시작합니다.

SoundEffect::PlaySound 메서드

  • 소스 음성 개체 m_sourceVoice 사용하여 소리 데이터 버퍼 m_soundData 재생을 시작합니다.
  • 사운드 데이터 버퍼에 대한 참조를 제공할 XAUDIO2_BUFFER을 만든 다음, IXAudio2SourceVoice::SubmitSourceBuffer를 호출하여 이를 제출합니다.
  • 사운드 데이터가 대기 중이면 SoundEffect::PlaySoundIXAudio2SourceVoice::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()
        );
}

Simple3DGame::UpdateDynamics 메서드

Simple3DGame::UpdateDynamics 메서드는 게임 개체 간의 상호 작용과 충돌을 처리합니다. 개체가 충돌하거나 교차하면 연결된 소리 효과가 재생되도록 트리거됩니다.

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
}

다음 단계

Windows 10 게임의 UWP 프레임워크, 그래픽, 컨트롤, 사용자 인터페이스 및 오디오를 다루었습니다. 이 자습서의 다음 부분인 샘플 게임 확장은 게임을 개발할 때 사용할 수 있는 다른 옵션에 대해 설명합니다.

오디오 개념

Windows 10 게임 개발의 경우 XAudio2 버전 2.9를 사용합니다. 이 버전은 Windows 10과 함께 제공됩니다. 자세한 내용은 XAudio2 버전로 이동하세요.

AudioX2 는 신호 처리 및 혼합 기반을 제공하는 하위 수준 API입니다. 자세한 내용은 XAudio2 주요 개념을 참조하세요.

XAudio2 음성 채널

XAudio2 음성 개체에는 소스, 서브믹스 및 마스터 음성의 세 가지 유형이 있습니다. 음성은 XAudio2가 오디오 데이터를 처리, 조작 및 재생하는 데 사용하는 개체입니다.

  • 원본 음성은 클라이언트에서 제공하는 오디오 데이터에서 작동합니다.
  • 원본 음성과 서브믹스 음성은 그 출력을 하나 이상의 서브믹스 또는 마스터링 음성으로 전달합니다.
  • 서브믹스 및 마스터링 음성은 모든 입력 음성의 오디오를 혼합하고 그 결과에 따라 작동합니다.
  • 마스터 음성은 원본 음성 및 서브믹스 음성에서 데이터를 수신하고 해당 데이터를 오디오 하드웨어로 보냅니다.

추가 정보는 XAudio2 음성에으로 가십시오.

오디오 그래프

오디오 그래프는 XAudio2 보이스모음입니다. 오디오는 원본 음성에서 오디오 그래프의 한쪽에서 시작하고, 필요에 따라 하나 이상의 서브믹스 음성을 통과하고, 마스터링 음성으로 끝납니다. 오디오 그래프에는 현재 재생 중인 각 소리에 대한 원본 음성, 0개 이상의 서브믹스 음성 및 하나의 마스터링 음성이 포함됩니다. 가장 간단한 오디오 그래프와 XAudio2에서 노이즈를 만드는 데 필요한 최소값은 마스터링 음성으로 직접 출력되는 단일 소스 음성입니다. 자세한 내용은 오디오 그래프을(를) 참조하세요.

추가 읽을거리

주요 오디오 .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:
    ...
};