Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Poznámka:
Toto téma je součástí Vytvoření jednoduché hry pro Univerzální platformu Windows (UPW) pomocí série kurzů DirectX. Téma na tomto odkazu nastaví kontext pro řadu.
V tomto tématu vytvoříme jednoduchý zvukový modul pomocí rozhraní API XAudio2. Pokud s XAudio2začínáte , přidali jsme krátký úvod do audio konceptů.
Poznámka:
Pokud jste si nestáhli nejnovější kód hry pro tuto ukázku, přejděte na ukázkovou hru Direct3D. Tato ukázka je součástí velké kolekce ukázek funkcí UPW. Pokyny ke stažení ukázky najdete v tématu Ukázkové aplikace pro vývoj ve Windows.
Účel
Přidejte do ukázkové hry zvuky pomocí XAudio2.
Definování zvukového stroje
V ukázkové hře jsou zvukové objekty a chování definovány ve třech souborech:
- cs-CZ: Audio.h/.cpp: Definuje objekt Audio, který obsahuje prostředky XAudio2 pro přehrávání zvuku. Definuje také metodu pozastavení a obnovení přehrávání zvuku, pokud je hra pozastavena nebo deaktivována.
- MediaReader.h/.cpp: Definuje metody čtení zvukových .wav souborů z místního úložiště.
- SoundEffect.h/.cpp: Definuje objekt pro přehrávání zvuku ve hře.
Přehled
Existují tři hlavní části nastavení přehrávání zvuku do hry.
Všechny jsou definovány v metodě Simple3DGame::Initialize. Pojďme se tedy nejprve podívat na tuto metodu a pak se podívat na další podrobnosti v jednotlivých částech.
Po nastavení se naučíme aktivovat zvukové efekty, které se mají přehrát. Další informace najdete v části Přehrát zvuk.
Simple3DGame::Initialize – metoda
V Simple3DGame::Initialize, kde se také inicializují m_controller a m_renderer, nastavíme zvukový modul a připravíme ho k přehrávání zvuků.
- Vytvořte m_audioController, což je instance třídy Audio.
- Vytvořte zvukové prostředky potřebné pomocí metody Audio::CreateDeviceIndependentResources. V této části byly vytvořeny dva objekty XAudio2 – objekt hudebního stroje a objekt zvukového stroje a hlas mastering pro každý z nich. Objekt hudebního stroje lze použít k přehrávání hudby na pozadí pro vaši hru. Zvukový stroj lze použít k přehrávání zvukových efektů ve hře. Další informace najdete v tématu Vytvoření a inicializace zvukových prostředků.
- Vytvořte mediaReader, což je instance třídy MediaReader. MediaReader, což je pomocná třída pro SoundEffect, která čte malé zvukové soubory synchronně z umístění souboru a vrací data zvuku jako pole bajtů.
- Pomocí MediaReader::LoadMedia načtěte zvukové soubory z jeho umístění a vytvořte targetHitSound proměnnou pro uložení načtených .wav zvukových dat. Další informace najdete v tématu Načtení zvukového souboru.
Zvukové efekty jsou přidruženy k hernímu objektu. Takže když dojde ke kolizi s tímto herním objektem, aktivuje zvukový efekt, který se má přehrát. V této ukázkové hře máme zvukové efekty pro munici (kterou používáme ke střelbě na cíle) a pro cíle.
- Ve třídě GameObject existuje vlastnost HitSound, která se používá k přidružení zvukového efektu k objektu.
- Vytvořte novou instanci třídy SoundEffect a inicializujte ji. Během inicializace se vytvoří zdrojový hlas zvukového efektu.
- Tato třída přehrává zvuk pomocí masteringového hlasu poskytovaného třídou Audio. Zvuková data se čtou z umístění souboru pomocí třídy MediaReader. Další informace naleznete v tématu Přidružení zvuku k objektu.
Poznámka:
Skutečný trigger pro přehrávání zvuku je určen pohybem a kolizí těchto herních objektů. Volání pro přehrání těchto zvuků je definováno v metodě Simple3DGame::UpdateDynamics. Další informace najdete v části Přehrát zvuk.
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]);
}
...
}
Vytvoření a inicializace zvukových prostředků
- Pomocí XAudio2Create, rozhraní API XAudio2, vytvořte dva nové objekty XAudio2 definující hudební a zvukové efektové moduly. Tato metoda vrátí ukazatel na rozhraní IXAudio2, které spravuje všechny stavy zvukového systému, vlákno pro zpracování zvuku, graf hlasů a další.
- Po vytvoření instance zvukových enginů použijte IXAudio2::CreateMasteringVoice k vytvoření masteringového hlasu pro každý ze zvukových motorů.
Další informace najdete v tématu Postupy: Jak inicializovat 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;
}
Načtení zvukového souboru
V ukázkové hře je kód pro čtení souborů zvukového formátu definován v MediaReader.h/cpp__. Chcete-li číst kódovaný zvukový soubor .wav, zavolejte MediaReader::LoadMedia, předejte název souboru .wav jako vstupní parametr.
MediaReader::LoadMedia – metoda
Tato metoda používá rozhraní API Media Foundation, která umožňují čtení zvukového souboru .wav jako vyrovnávací paměti PCM (pulzně kódová modulace).
Nastavení čtečky zdroje
- Použijte MFCreateSourceReaderFromURL k vytvoření čtečky zdroje médií (IMFSourceReader).
- Pomocí MFCreateMediaType vytvořte objekt typu média (IMFMediaType) (mediaType). Představuje popis formátu média.
- Určete, že mediaTypedekódovaný výstup je zvuk PCM, což je typ zvuku, který XAudio2 může použít.
- Nastaví dekódovaný výstupní typ média pro čtečku zdroje voláním IMFSourceReader::SetCurrentMediaType.
Další informace o tom, proč používáme Čtečku zdrojového kódu, najdete v tématu Čtenář zdroje.
Popis formátu dat zvukového streamu
- K získání aktuálního typu média pro stream použijte IMFSourceReader::GetCurrentMediaType.
- Pomocí IMFMediaType::MFCreateWaveFormatExFromMFMediaType převeďte aktuální typ zvukového média na vyrovnávací paměť WAVEFORMATEX pomocí výsledků předchozí operace jako vstupu. Tato struktura určuje formát dat zvukového streamu vln, který se používá po načtení zvuku.
K popisu vyrovnávací paměti PCM lze použít formát WAVEFORMATEX. Ve srovnání se strukturou WAVEFORMATEXTENSIBLE lze použít pouze k popisu podmnožiny zvukových vlnových formátů. Další informace o rozdílech mezi WAVEFORMATEX a WAVEFORMATEXTENSIBLEnaleznete v tématu rozšiřitelné Wave-Format popisovače.
Čtení zvukového streamu
- Získejte dobu trvání zvukového streamu v sekundách voláním MMFSourceReader::GetPresentationAttribute a poté převede dobu trvání na bajty.
- Přečtěte zvukový soubor jako datový proud voláním IMFSourceReader::ReadSample. ReadSample přečte další ukázku ze zdroje médií.
- Pomocí IMFSample::ConvertToContiguousBuffer zkopírujte obsah vyrovnávací paměti zvukového vzorku (vzorku) do pole (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;
}
Přidružení zvuku k objektu
Přiřazení zvuků objektu probíhá při inicializaci hry v metodě Simple3DGame::Initialize.
Rekapitulace:
- Ve třídě GameObject existuje vlastnost HitSound, která se používá k přidružení zvukového efektu k objektu.
- Vytvořte novou instanci objektu třídy SoundEffect a přidružte ji k objektu hry. Tato třída přehrává zvuk pomocí rozhraní API XAudio2. Používá masteringový hlas poskytovaný třídou Audio. Zvuková data lze číst z umístění souboru pomocí třídy MediaReader.
SoundEffect::Initialize se používá k initalizaci instance SoundEffect s následujícími vstupními parametry: ukazatel na objekt zvukového stroje (objekty IXAudio2 vytvořené v Audio::CreateDeviceIndependentResources metoda), ukazatel na formát souboru .wav pomocí MediaReader::GetOutputWaveFormatExa zvuková data načtená pomocí MediaReader::LoadMedia metoda. Během inicializace se vytvoří také zdrojový hlas zvukového efektu.
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;
}
Přehraj zvuk
Spouštěče pro přehrávání zvukových efektů jsou definovány ve Simple3DGame::UpdateDynamics metodě, protože se zde aktualizuje pohyb objektů a určuje se kolize mezi objekty.
Vzhledem k tomu, že interakce mezi objekty se výrazně liší v závislosti na hře, nebudeme zde diskutovat o dynamikě herních objektů. Pokud vás zajímá jeho implementace, přejděte na Simple3DGame::UpdateDynamics metoda.
V zásadě, když dojde ke kolizi, způsobí přehrání zvukového efektu zavoláním SoundEffect::PlaySound. Tato metoda zastaví jakékoli zvukové efekty, které se právě přehrávají, a do fronty zařadí paměti buffer s požadovanými zvukovými daty. Používá zdrojový hlas k nastavení hlasitosti, odesílání zvukových dat a spuštění přehrávání.
Metoda SoundEffect::PlaySound
- Používá zdrojový hlasový objekt m_sourceVoice ke spuštění přehrávání datového bufferu pro zvuk m_soundData
- Vytvoří XAUDIO2_BUFFER, kterému přiřazuje odkaz na vyrovnávací paměť zvukových dat, a pak ji odešle prostřednictvím volání IXAudio2SourceVoice::SubmitSourceBuffer.
- Když jsou zvuková data ve frontě, SoundEffect::PlaySound zahájí přehrávání zavoláním 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 postará o interakci a kolizi mezi herními objekty. Když objekty kolidují (nebo se protínají), spustí se přidružený zvukový efekt.
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
}
Další kroky
Probrali jsme architekturu UPW, grafiku, ovládací prvky, uživatelské rozhraní a zvuk hry s Windows 10. Další část tohoto kurzu, rozšíření ukázkové hry, vysvětluje další možnosti, které lze použít při vývoji hry.
Koncepty zvuku
Pro vývoj her pro Windows 10 použijte XAudio2 verze 2.9. Tato verze je dodávána s Windows 10. Další informace najdete v XAudio2 Verze.
AudioX2 je rozhraní API nízké úrovně, které poskytuje základ pro zpracování a míchání signálů. Další informace naleznete v tématu XAudio2 Klíčové koncepty.
Hlasy XAudio2
Jsou tři typy hlasových objektů XAudio2: zdrojový, submixový a masteringový hlas. Hlasy jsou objekty, které XAudio2 používá ke zpracování, manipulaci a přehrávání zvukových dat.
- Zdrojové hlasy pracují se zvukovými daty poskytovanými klientem.
- Zdrojové a submixované hlasy odesílají svůj výstup do jednoho či více submixů nebo masteringových hlasů.
- Submixy a mastering směšují zvuk ze všech hlasů, které do nich vstupují, a pracují s výsledkem.
- Hlavní hlasy přijímají data ze zdrojových hlasů a submixových hlasů a odesílají tato data do zvukového hardwaru.
Další informace najdete v hlasech XAudio2 .
Zvukový graf
Audio graph je kolekce XAudio2 hlasů. Zvuk začíná na jedné straně zvukového grafu ve zdrojových hlasech, volitelně prochází jedním nebo více submixovými hlasy a končí hlavním hlasem. Zvukový graf bude obsahovat zdrojový hlas pro každý právě přehrávaný zvuk, žádný nebo více submixových hlasů a jeden masteringový hlas. Nejjednodušší zvukový graf a minimum potřebné k vytvoření zvuku v XAudio2 je jediný zdrojový hlas, který přímo vstupuje do hlavního (mastering) hlasu. Pro více informací přejděte na Audio graphs.
Další čtení
- Postupy: Inicializace XAudio2
- Jak na to: Načtení zvukových datových souborů v XAudio2
- Jak přehrát zvuk pomocí XAudio2
Klíčové zvukové soubory .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:
...
};