Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Megjegyzés:
Ez a témakör a Egyszerű Univerzális Windows Platform (UWP) játék készítése DirectX-szel című oktatóanyag-sorozat része. A hivatkozás témaköre beállítja a sorozat kontextusát.
Ebben a témakörben egy egyszerű hangmotort hozunk létre XAudio2 API-k használatával. Ha új az XAudio2, mellékeltünk egy rövid bevezetőt a Hangfogalmak alatt.
Megjegyzés:
Ha még nem töltötte le a minta legújabb játékkódját, lépjen tovább a Direct3D minta játék-ra. Ez a minta az UWP-szolgáltatásminták nagy gyűjteményének része. A minta letöltésére vonatkozó utasításokért lásd a Windows-fejlesztéshez készült mintaalkalmazásokat.
Célkitűzés
Vegyen fel hangokat a mintajátékba az XAudio2 használatával.
A hangmotor definiálása
A mintajátékban a hangobjektumok és a viselkedések három fájlban vannak definiálva:
- Audio.h/.cpp: Meghatározza a hangobjektumot , amely a hanglejátszáshoz szükséges XAudio2-erőforrásokat tartalmazza. Emellett meghatározza a hanglejátszás felfüggesztésének és folytatásának módját is, ha a játék szünetel vagy inaktivált.
- MediaReader.h/.cpp: Meghatározza a hang .wav fájlok helyi tárolóból való olvasásának módszereit.
- SoundEffect.h/.cpp: Egy objektumot határoz meg a játékon belüli hanglejátszáshoz.
Áttekintés
Három fő részből áll a hanglejátszás beállítása a játékba.
Ezek mindegyike a Simple3DGame::Initialize metódusban van definiálva. Először vizsgáljuk meg ezt a módszert, majd vizsgáljuk meg az egyes szakaszok további részleteit.
A beállítás után megtanuljuk, hogyan aktiválható a hangeffektusok lejátszása. További információ: Hang lejátszása.
Simple3DGame::Inicializálási módszer
A Simple3DGame::Initializefüggvényben, ahol a m_controller és a m_renderer is inicializálva van, beállítjuk a hangmotort, és felkészítjük a hangok lejátszására.
- Hozza létre m_audioController-et, amely a Audio osztály példánya.
- Hozza létre a szükséges hangerőforrásokat a Hang::CreateDeviceIndependentResources metódussal. Itt két XAudio2 objektumot hoztak létre – egy zenemotor-objektumot és egy hangmotor-objektumot, valamint mindegyikhez egy mesterhangot. A zenemotor objektum használható háttérzene lejátszására a játékhoz. A hangmotor használható hangeffektusok lejátszására a játékban. További információ: Hangerőforrások létrehozása és inicializálása.
- Hozzon létre mediaReadert, amely a MediaReader osztály egy példánya. A MediaReader, amely a SoundEffect osztály segédosztálya, szinkron módon olvassa be a kis hangfájlokat a fájl helyéről, és hangadatokat ad vissza bájttömbként.
- A MediaReader::LoadMedia használatával betölthet hangfájlokat a helyéről, és létrehozhat egy targetHitSound változót a betöltött .wav hangadatok tárolásához. További információért lásd: Hangfájl betöltése.
A hangeffektusok a játékobjektumhoz vannak társítva. Tehát amikor ütközés történik a játék objektummal, az aktiválja a lejátszandó hangeffektust. Ebben a mintajátékban hangeffektusokkal rendelkezünk a lőszerhez (a célpontok lőéséhez használt eszközökkel) és a célhoz.
- A GameObject osztályban van egy HitSound tulajdonság, amely a hangeffektus objektumhoz való társítására szolgál.
- Hozzon létre egy új példányt a SoundEffect osztályból, és inicializálja azt. Az inicializálás során létrejön a hangeffektus forráshangja.
- Ez az osztály a Hang osztályból származó mesterhang használatával játszik le hangot. A hangadatok a MediaReader osztály használatával olvashatók a fájl helyről. További információ: Hang társítása objektumhoz.
Megjegyzés:
A hang lejátszásának tényleges eseményindítóját a játékobjektumok mozgása és ütközése határozza meg. Ezért ezeknek a hangoknak a lejátszására irányuló hívás a Simple3DGame::UpdateDynamics metódusban van definiálva. További információ: Hang lejátszása.
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]);
}
...
}
A hangerőforrások létrehozása és inicializálása
- A XAudio2Create, egy XAudio2 API használatával hozzon létre két új XAudio2 objektumot, amelyek meghatározzák a zene- és hangeffektusmotorokat. Ez a módszer egy mutatót ad vissza az objektum IXAudio2 felületére, amely kezeli az összes hangmotor-állapotot, a hangfeldolgozási szálat, a hanggráfot stb.
- A motorok példányosítása után a IXAudio2::CreateMasteringVoice használatával hozzon létre egy mesterhangzást minden egyes hangmotor objektumhoz.
További információ : Útmutató: XAudio2 inicializálása.
Audió::CreateDeviceIndependentResources metódus
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;
}
Hangfájl betöltése
A mintajátékban a hangformátum-fájlok olvasására szolgáló kód meg van határozva a MediaReader.h/cpp__. Kódolt .wav hangfájl olvasásához hívja meg a MediaReader::LoadMedia parancsot, és adja meg a .wav fájlnevét bemeneti paraméterként.
MediaReader::LoadMedia módszer
Ez a módszer a Media Foundation API-kkal olvassa be a .wav hangfájlt Pulse Code Modulation (PCM) pufferként.
A Forrásolvasó beállítása
- Az MFCreateSourceReaderFromURL használatával hozzon létre egy médiaforrás-olvasót (IMFSourceReader).
- Az MFCreateMediaType használatával hozzon létre egy médiatípusú (IMFMediaType) objektumot (mediaType). Ez egy médiaformátum leírását jelöli.
- Adja meg, hogy a mediaType dekódolt kimenete PCM-hang, amely az XAudio2 által használható hangtípus.
- Beállítja a forrásolvasó dekódolt kimeneti médiatípusát az IMFSourceReader::SetCurrentMediaType meghívásával.
A Forrásolvasó használatának okáról a Forrásolvasóban talál további információt.
A hangstream adatformátumának ismertetése
- Használja az IMFSourceReader::GetCurrentMediaType parancsot a stream aktuális médiatípusának lekéréséhez.
- Az IMFMediaType::MFCreateWaveFormatExFromMFMediaType használatával konvertálja az aktuális hanganyagtípust WAVEFORMATEX pufferzé a korábbi művelet eredményeinek bemenetként való felhasználásával. Ez a struktúra a hang betöltése után használt hullámos hangstream adatformátumát határozza meg.
A WAVEFORMATEX formátum a PCM-puffer leírására használható. A WAVEFORMATEXTENSIBLE struktúrához képest csak a hanghullám-formátumok egy részhalmazának leírására használható. A WAVEFORMATEX és a WAVEFORMATEXTENSIBLE közötti különbségekről további információt az Extensible Wave-Format Leírók című témakörben talál.
Hangfolyam olvasása
- Az IMFSourceReader::GetPresentationAttribute meghívásával másodpercben lekérheti a hangstream időtartamát, majd bájttá alakítja az időtartamot.
- Olvassa be a hangfájlt folyamként IMFSourceReader::ReadSamplemeghívásával. ReadSample leolvassa a következő mintát a médiaforrásból.
- Használja a IMFSample::ConvertToContiguousBuffer függvényt, hogy a hangmintapuffer tartalmát (minta) belemásolja egy tömbbe (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;
}
Hang társítása objektumhoz
A hangok társítása az objektumhoz a játék inicializálásakor történik a Simple3DGame::Initialize metódusban.
Emlékeztető:
- A GameObject osztályban van egy HitSound tulajdonság, amely a hangeffektus objektumhoz való társítására szolgál.
- Hozzon létre egy új példányt a SoundEffect osztály objektumából, és társítsa azt a játékobjektumhoz. Ez az osztály XAudio2 API-kkal játszik le hangot. A Audio osztály által biztosított mastering hangot használja. A hangadatok a MediaReader osztály használatával olvashatók a fájl helyről.
SoundEffect::Initialize a SoundEffect-példány inicializálására szolgál a következő bemeneti paraméterekkel: mutató a hangmotor objektumára (IXAudio2 objektumok a Audio::CreateDeviceIndependentResources metódusban létrehozva), mutató a .wav fájl formátumára a MediaReader::GetOutputWaveFormatExhasználatával, valamint a hangadatokra, amelyeket a MediaReader::LoadMedia metódussal töltöttek be. Az inicializálás során a hangeffektus forráshangja is létrejön.
SoundEffect::Inicializálási módszer
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;
}
A hang lejátszása
A hangeffektusok lejátszására szolgáló triggerek a Simple3DGame::UpdateDynamics metódusban vannak definiálva, mivel itt frissülnek az objektumok mozgása, és az objektumok közötti ütközés lesz meghatározva.
Mivel az objektumok közötti interakció nagyban különbözik, a játéktól függően itt nem fogjuk megvitatni a játékobjektumok dinamikáját. Ha szeretné megismerni a megvalósítást, nyissa meg a Simple3DGame::UpdateDynamics metódust .
Elvileg ütközés esetén az SoundEffect::PlaySoundmeghívásával aktiválódik a hangeffektus. Ez a módszer leállítja a jelenleg lejátszott hangeffektusokat, és a kívánt hangadatokkal várólistára állítja a memóriabeli puffert. Forráshang használatával állítja be a hangerőt, hangadatokat küld, és elindítja a lejátszást.
SoundEffect::PlaySound metódus
- A forrás hangobjektum m_sourceVoice használatával indítja el a hangadat-puffer lejátszását m_soundData
- Létrehoz egy XAUDIO2_BUFFER-t, amely a hangadatpufferre mutat, majd az IXAudio2SourceVoice::SubmitSourceBufferfüggvényt meghívva továbbítja azt.
- A hangadatok várólistára helyezése után a SoundEffect::PlaySound a IXAudio2SourceVoice::Starthívásával indítja el a visszajátszást.
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 metódus
A Simple3DGame::UpdateDynamics metódus gondoskodik a játékobjektumok közötti interakcióról és ütközésről. Amikor az objektumok ütköznek (vagy metszenek), a kapcsolódó hangeffektust aktiválja a lejátszáshoz.
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
}
Következő lépések
A Windows 10-es játékok UWP-keretrendszerével, grafikáival, vezérlőivel, felhasználói felületével és hanganyagával foglalkoztunk. Az oktatóanyag következő része, a mintajáték kiterjesztése ismerteti a játék fejlesztésekor használható egyéb lehetőségeket.
Hangkoncepciók
Windows 10-es játékok fejlesztéséhez használja az XAudio2 2.9-es verzióját. Ezt a verziót a Windows 10-ben szállítjuk. További információ: XAudio2-verziók.
Az AudioX2 egy alacsony szintű API, amely jelfeldolgozási és keverési alapokat biztosít. További információ: XAudio2 Key Concepts.
XAudio2-hangok
Az XAudio2 hangobjektumoknak három típusa van: forrás, részmix és mesterhangok. A hangok azok az objektumok, amelyeket az XAudio2 használ a hangadatok feldolgozására, manipulálására és lejátszására.
- A forráshangok az ügyfél által biztosított hangadatokon működnek.
- A forráshangok és részkeverék hangok egy vagy több részmixbe vagy master hangra küldik a kimenetüket.
- A szubmix és a mastering hangok összekeverik az őket tápláló összes hangot, és dolgoznak az eredménnyel.
- A mesterhangok a forráshangoktól és a szubmix hangoktól fogadják az adatokat, és továbbítják az adatokat a hanghardvernek.
További információért látogasson el XAudio2 hangok.
Hanggráf
A hanggráf XAudio2 hanggyűjteménye. A hang egy hanggráf egyik oldalán indul el a forráshangokban, opcionálisan átmegy egy vagy több szubmix hangon, és mesterhangra végződik. A hanggráfok minden lejátszott hanghoz tartalmaznak forráshangot, nulla vagy több submix hangot és egy mesterhangot. Az XAudio2-ben a legegyszerűbb hanggráf, és a zaj létrehozásához szükséges minimális konfiguráció az, amikor egyetlen forráshang közvetlenül egy mastering hangra ad ki. További információ: Hangdiagramok.
További olvasnivaló
- Útmutató: XAudio2 inicializálása
- Útmutató: Hangadatfájlok betöltése az XAudio2
- Útmutató: Hogyan játsszunk le hangot az XAudio2-vel
Kulcsfontosságú hang .h fájlok
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:
...
};