Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Opmerking
Dit onderwerp maakt deel uit van de tutorialreeks Een eenvoudig Universal Windows Platform (UWP) spel maken met DirectX. In het onderwerp op die koppeling wordt de context voor de reeks ingesteld.
In dit onderwerp maken we een eenvoudige geluidsengine met behulp van XAudio2 API's. Als u niet bekend bent met XAudio2, hebben we een korte inleiding opgenomen onder Audio-concepten.
Opmerking
Als u de nieuwste gamecode voor dit voorbeeld nog niet hebt gedownload, gaat u naar Direct3D-voorbeeldspel. Dit voorbeeld maakt deel uit van een grote verzameling UWP-functievoorbeelden. Zie Voorbeeldtoepassingen voor Windows-ontwikkelingvoor instructies over het downloaden van het voorbeeld.
Doelstelling
Voeg geluiden toe aan het voorbeeldspel met behulp van XAudio2.
De audio-engine definiëren
In het voorbeeldspel worden de audioobjecten en het gedrag gedefinieerd in drie bestanden:
- Audio.h/.cpp: definieert het object Audio, dat de XAudio2--bronnen bevat voor het afspelen van geluid. Het definieert ook de methode voor het onderbreken en hervatten van het afspelen van audio als de game wordt onderbroken of gedeactiveerd.
- MediaReader.h/.cpp: definieert de methoden voor het lezen van audio-.wav bestanden uit lokale opslag.
- SoundEffect.h/.cpp: Definieert een object voor het afspelen van geluid in de game.
Overzicht
Er zijn drie hoofdonderdelen die u kunt instellen voor het afspelen van audio in uw game.
Ze zijn allemaal gedefinieerd in de methode Simple3DGame::Initialize. Laten we deze methode dus eerst bekijken en vervolgens in elk van de secties meer details bekijken.
Na het instellen leert u hoe u de geluidseffecten activeert om af te spelen. Ga voor meer informatie naar Speel het geluid af.
Methode Simple3DGame::Initialize
In Simple3DGame::Initialiseer, waar m_controller en m_renderer ook worden geïnitialiseerd, stellen we de audio-engine in en maken we het klaar om geluiden af te spelen.
- Maak m_audioController, een exemplaar van de klasse Audio.
- Maak de audioresources die nodig zijn met behulp van de methode Audio::CreateDeviceIndependentResources. Hier zijn twee XAudio2 objecten gemaakt: een muziekengineobject en een geluidsengineobject, en voor elk van deze een mastering voice. Het object van de muziekengine kan worden gebruikt om achtergrondmuziek voor uw spel te spelen. De geluidsengine kan worden gebruikt om geluidseffecten in uw spel te spelen. Zie De audioresources maken en initialiserenvoor meer informatie.
- Maak mediaReader, een exemplaar van MediaReader--klasse. MediaReader, een helperklasse voor de klasse SoundEffect, leest kleine audiobestanden synchroon vanaf de bestandslocatie en retourneert geluidsgegevens als een bytematrix.
- Gebruik MediaReader::LoadMedia om geluidsbestanden vanaf de locatie te laden en een targetHitSound-variabele te maken om de geladen .wav geluidsgegevens vast te houden. Zie Audiobestand ladenvoor meer informatie.
Geluidseffecten zijn gekoppeld aan het gameobject. Dus wanneer er een botsing optreedt met dat gameobject, wordt het geluidseffect geactiveerd om af te spelen. In dit voorbeeldspel hebben we geluidseffecten voor de munitie (waarmee we op doelen schieten) en voor het doel.
- In de GameObject klasse is er een eigenschap HitSound die wordt gebruikt om het geluidseffect aan het object te koppelen.
- Maak een nieuw exemplaar van de klasse SoundEffect en initialiseer deze. Tijdens de initialisatie wordt een bronstem voor het geluidseffect gemaakt.
- Deze klasse speelt een geluid af met behulp van een mastering voice van de Audio class. Geluidsgegevens worden gelezen vanuit de bestandslocatie met behulp van de MediaReader--klasse. Zie Geluid koppelen aan objectvoor meer informatie.
Opmerking
De werkelijke trigger voor het afspelen van het geluid wordt bepaald door de beweging en botsing van deze spelobjecten. Daarom wordt de aanroep om deze geluiden daadwerkelijk af te spelen gedefinieerd in de methode Simple3DGame::UpdateDynamics. Ga voor meer informatie naar Speel het geluid af.
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]);
}
...
}
De audiobronnen maken en initialiseren
- Gebruik XAudio2Create, een XAudio2-API, om twee nieuwe XAudio2-objecten te maken die de muziek- en geluidseffectengines definiëren. Deze methode retourneert een aanwijzer naar de IXAudio2-interface van het object waarmee alle statussen van de audio-engine, de audioverwerkingsthread, de spraakgrafiek en meer worden beheerd.
- Nadat de motoren zijn geïnstantieerd, gebruik IXAudio2::CreateMasteringVoice om een mastering voice te maken voor elk van de geluidsengineobjecten.
Ga voor meer informatie naar Procedure: XAudio2 initialiseren.
Audio::CreateDeviceIndependentResources-methode
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;
}
Audiobestand laden
In het voorbeeldspel wordt de code voor het lezen van audiobestanden gedefinieerd in MediaReader.h/cpp__. Als u een gecodeerd .wav audiobestand wilt lezen, roept u MediaReader::LoadMedia-aan en geeft u de bestandsnaam van de .wav door als invoerparameter.
Methode MediaReader::LoadMedia
Deze methode maakt gebruik van de Media Foundation API's om in het .wav audiobestand te lezen als een Pulse Code Modulation -buffer (PCM).
De bronlezer instellen
- Gebruik MFCreateSourceReaderFromURL- om een mediabronlezer te maken (IMFSourceReader).
- Gebruik MFCreateMediaType om een mediatype (IMFMediaType) object (mediaType) te maken). Het vertegenwoordigt een beschrijving van een media-indeling.
- Geef op dat de gedecodeerde uitvoer van het mediaTypePCM-audio is. Dit is een audiotype dat XAudio2 kan gebruiken.
- Hiermee stelt u het gedecodeerde uitvoermediatype voor de bronlezer in door IMFSourceReader::SetCurrentMediaTypeaan te roepen.
Ga naar Bronlezervoor meer informatie over waarom we de bronlezer gebruiken.
De gegevensindeling van de audiostream beschrijven
- Gebruik IMFSourceReader::GetCurrentMediaType om het huidige mediatype voor de stream op te halen.
- Gebruik IMFMediaType::MFCreateWaveFormatExFromMFMediaType om het huidige audiomediatype te converteren naar een WAVEFORMATEX buffer, met behulp van de resultaten van de eerdere bewerking als invoer. Deze structuur geeft de gegevensindeling op van de golfaudiostream die wordt gebruikt nadat de audio is geladen.
De WAVEFORMATEX--indeling kan worden gebruikt om de PCM-buffer te beschrijven. In vergelijking met de WAVEFORMATEXTENSIBLE structuur kan deze alleen worden gebruikt om een subset van audiogolfindelingen te beschrijven. Zie voor meer informatie over de verschillen tussen WAVEFORMATEX en WAVEFORMATEXTENSIBLE, Extensible Wave-Format Descriptors.
De audiostream lezen
- Haal de duur in seconden van de audiostream op door IMFSourceReader::GetPresentationAttribute aan te roepen en converteert vervolgens de duur naar bytes.
- Lees het audiobestand als een stream door IMFSourceReader::ReadSampleaan te roepen. ReadSample- leest het volgende voorbeeld uit de mediabron.
- Gebruik IMFSample::ConvertToContiguousBuffer om de inhoud van de audiovoorbeeldbuffer (voorbeeld) te kopiëren naar een matrix (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;
}
Geluid aan object koppelen
Het koppelen van geluiden aan het object vindt plaats wanneer het spel wordt geïnitialiseerd, in de Simple3DGame::Initialiseer methode.
Samenvatting:
- In de GameObject klasse is er een eigenschap HitSound die wordt gebruikt om het geluidseffect aan het object te koppelen.
- Maak een nieuw exemplaar van het SoundEffect klasseobject en koppel het aan het gameobject. Deze klasse speelt een geluid af met behulp van XAudio2 API's. Het maakt gebruik van een mastering voice geleverd door de Audio klasse. De geluidsgegevens kunnen worden gelezen vanuit de bestandslocatie met behulp van de MediaReader klasse.
SoundEffect::Initialize wordt gebruikt om de instantie van SoundEffect te initialiseren met de volgende invoerparameters: pointer naar het geluid-engine-object (IXAudio2-objecten gemaakt in de methode Audio::CreateDeviceIndependentResources), pointer naar de indeling van het .wav-bestand met behulp van MediaReader::GetOutputWaveFormatEx, en de geluidsgegevens geladen met de methode MediaReader::LoadMedia. Tijdens de initialisatie wordt ook de bronstem voor het geluidseffect gemaakt.
Methode 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;
}
Het geluid afspelen
Triggers voor het afspelen van geluidseffecten worden gedefinieerd in Simple3DGame::UpdateDynamics methode omdat hier beweging van de objecten wordt bijgewerkt en conflicten tussen objecten worden bepaald.
Omdat de interactie tussen objecten sterk verschilt, afhankelijk van het spel, gaan we hier niet de dynamiek van de gameobjecten bespreken. Als u meer wilt weten over de implementatie, gaat u naar Simple3DGame::UpdateDynamics methode.
In principe, wanneer een botsing optreedt, wordt het geluidseffect geactiveerd om af te spelen door SoundEffect::PlaySoundaan te roepen. Met deze methode worden alle geluidseffecten die momenteel worden afgespeeld, gestopt en wordt de buffer in het geheugen in de wachtrij geplaatst met de gewenste geluidsgegevens. Er wordt gebruikgemaakt van bronstem om het volume in te stellen, geluidsgegevens in te dienen en het afspelen te starten.
methode SoundEffect::PlaySound
- Gebruikt het bron-spraakobject m_sourceVoice om het afspelen van de geluidsgegevensbuffer te starten m_soundData
- Hiermee wordt een XAUDIO2_BUFFERgemaakt, die een verwijzing naar de geluidsgegevensbuffer verstrekt en het vervolgens indient met een aanroep naar IXAudio2SourceVoice::SubmitSourceBuffer.
- Met de geluidsgegevens in de wachtrij, start SoundEffect::PlaySound het afspelen door IXAudio2SourceVoice::Startaan te roepen.
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()
);
}
Methode Simple3DGame::UpdateDynamics
De methode Simple3DGame::UpdateDynamics zorgt voor de interactie en botsing tussen gameobjecten. Wanneer objecten botsen (of kruisen), wordt het bijbehorende geluidseffect afgespeeld.
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
}
Volgende stappen
We hebben het UWP-framework, graphics, besturingselementen, gebruikersinterface en audio van een Windows 10-game behandeld. In het volgende deel van deze zelfstudie, Het voorbeeldspel uitbreiden, worden andere opties uitgelegd die kunnen worden gebruikt bij het ontwikkelen van een game.
Audioconcepten
Gebruik XAudio2 versie 2.9 voor ontwikkeling van Windows 10-games. Deze versie wordt geleverd met Windows 10. Ga naar XAudio2 Versionsvoor meer informatie.
AudioX2 is een API op laag niveau die signaalverwerking en mengbasis biedt. Zie XAudio2 Key Conceptsvoor meer informatie.
XAudio2-stemmen
Er zijn drie soorten XAudio2-spraakobjecten: bron-, submix- en masteringstemmen. Stemmen zijn de objecten die XAudio2 gebruiken om audiogegevens te verwerken, te bewerken en af te spelen.
- Bronstemmen werken op audiogegevens die door de client worden geleverd.
- Bron- en submixstemmen verzenden hun uitvoer naar een of meer submix- of masteringstemmen.
- Submix- en masteringstemmen mixen de audio van alle stemmen die ze voeden en werken aan het resultaat.
- Het masteren van stemmen ontvangt gegevens van bronstemmen en submixstemmen en verzendt die gegevens naar de audiohardware.
Ga naar XAudio2-stemmenvoor meer informatie.
Audiografiek
Audiograf is een verzameling XAudio2-stemmen. Audio begint aan één kant van een audiografiek in bronstemmen, doorloopt eventueel een of meer submixstemmen en eindigt bij een mastering-stem. Een audiografiek bevat een bronstem voor elk geluid dat momenteel wordt afgespeeld, nul of meer submixstemmen en één mastering voice. De eenvoudigste audiografiek en het minimum dat nodig is om een ruis te maken in XAudio2, is één bronstem die rechtstreeks naar een mastering-stem uitvoert. Ga naar Audiografiekenvoor meer informatie.
Aanvullende literatuur
- Handleiding: XAudio2 initialiseren
- Handleiding: Audiobestanden laden in XAudio2
- Handleiding: Een geluid afspelen met XAudio2
Belangrijke audio.h-bestanden
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:
...
};