Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Nota
Topik ini adalah bagian dari Membuat game Universal Windows Platform (UWP) sederhana dengan seri tutorial DirectX. Topik di tautan tersebut mengatur konteks untuk seri.
Dalam topik ini, kami membuat mesin suara sederhana menggunakan XAudio2 API. Jika Anda baru menggunakan XAudio2, kami telah menyertakan intro singkat di bawah Konsep Audio.
Nota
Jika Anda belum mengunduh kode game terbaru untuk sampel ini, buka permainan sampel Direct3D. Sampel ini adalah bagian dari koleksi besar sampel fitur UWP. Untuk petunjuk tentang cara mengunduh sampel, lihat Aplikasi sampel untuk pengembangan Windows.
Tujuan
Tambahkan suara ke dalam permainan sampel menggunakan XAudio2.
Menentukan mesin audio
Dalam permainan sampel, objek audio dan perilaku didefinisikan dalam tiga file:
: Menentukan objek AudioAudio.h /.cpp, yang berisi sumber daya XAudio2 untuk pemutaran suara. Ini juga mendefinisikan metode untuk menangguhkan dan melanjutkan pemutaran audio jika game dijeda atau dinonaktifkan. - MediaReader.h/.cpp: Menentukan metode untuk membaca file .wav audio dari penyimpanan lokal.
- SoundEffect.h/.cpp: Menentukan objek untuk pemutaran suara dalam game.
Gambaran Umum
Ada tiga bagian utama dalam menyiapkan pemutaran audio ke dalam game Anda.
Semuanya didefinisikan dalam metode Simple3DGame::Initialize. Pertama-tama, mari kita periksa metode ini dan kemudian mendalami detail lebih lanjut di setiap bagian.
Setelah menyiapkan, kita belajar cara memicu efek suara untuk diputar. Untuk informasi selengkapnya, buka dan putar suara di.
Simple3DGame::Inisialisasi metode
Dalam Simple3DGame::Initialize, di mana m_controller dan m_renderer juga diinisialisasi, kami menyiapkan mesin audio dan menyiapkannya untuk memutar suara.
- Buat m_audioController, yang merupakan instans dari kelas Audio.
- Buat sumber daya audio yang diperlukan menggunakan metode Audio::CreateDeviceIndependentResources. Di sini, dua objek
XAudio2 — objek mesin musik dan objek mesin suara, dan suara yang menguasai untuk masing-masing objek dibuat. Objek mesin musik dapat digunakan untuk memainkan musik latar belakang untuk permainan Anda. Mesin suara dapat digunakan untuk memainkan efek suara dalam permainan Anda. Untuk informasi selengkapnya, lihat Membuat dan menginisialisasi sumber daya audio. - Buat mediaReader, yang merupakan instans kelas MediaReader. MediaReader, yang merupakan kelas pembantu dari kelas SoundEffect , membaca file audio kecil secara sinkron dari lokasi file dan mengembalikan data suara dalam bentuk array byte.
- Gunakan
MediaReader::LoadMedia untuk memuat file suara dari lokasinya dan membuat variabel targetHitSounduntuk menyimpan data suara .wav yang dimuat. Untuk informasi selengkapnya, lihat Memuat file audio.
Efek suara dikaitkan dengan objek permainan. Jadi ketika tabrakan terjadi dengan objek game itu, itu memicu efek suara untuk dimainkan. Dalam permainan sampel ini, kita memiliki efek suara untuk amunisi (yang kita gunakan untuk menembak target) dan untuk target.
- Di kelas GameObject, ada properti HitSound yang digunakan untuk menghubungkan efek suara ke objek.
- Buat instans baru dari kelas SoundEffect dan inisialisasi. Selama inisialisasi, suara sumber untuk efek suara dibuat.
- Kelas ini memutar suara menggunakan pemrosesan suara dari kelas Audio. Data suara dibaca dari lokasi file menggunakan kelas MediaReader. Untuk informasi selengkapnya, lihat Mengaitkan suara dengan objek.
Nota
Pemicu aktual untuk memainkan suara ditentukan oleh gerakan dan tabrakan objek game ini. Oleh karena itu, panggilan untuk benar-benar memutar suara ini didefinisikan dalam metode
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]);
}
...
}
Membuat dan menginisialisasi sumber daya audio
- Gunakan XAudio2Buat, API XAudio2, untuk membuat dua objek XAudio2 baru yang menentukan mesin efek musik dan suara. Metode ini mengembalikan penunjuk ke antarmuka IXAudio2 objek yang mengelola semua status mesin audio, utas pemrosesan audio, grafik suara, dan banyak lagi.
- Setelah mesin yang telah dibuat, gunakan IXAudio2::CreateMasteringVoice untuk membuat suara mastering untuk setiap objek mesin suara.
Untuk informasi selengkapnya, buka Cara: Menginisialisasi XAudio2.
Metode 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;
}
Memuat file audio
Dalam permainan sampel, kode untuk membaca file format audio didefinisikan dalam MediaReader.h/cpp__. Untuk membaca file audio .wav yang dikodekan, panggil MediaReader::LoadMedia, meneruskan nama file .wav sebagai parameter input.
Metode MediaReader::LoadMedia
Metode ini menggunakan Media Foundation API untuk membaca file audio .wav sebagai buffer Pulse Code Modulation (PCM).
Menyiapkan Pembaca Sumber
- Gunakan MFCreateSourceReaderFromURL untuk membuat pembaca sumber media (IMFSourceReader).
- Gunakan
MFCreateMediaType untuk membuat objek jenis media ( IMFMediaType ) (mediaType ). Ini mewakili deskripsi format media. - Tentukan bahwa output mediaTypeyang didekodekan adalah audio PCM, yang merupakan jenis audio yang dapat digunakan XAudio2.
- Mengatur jenis media output yang didekodekan untuk pembaca sumber dengan memanggil IMFSourceReader::SetCurrentMediaType.
Untuk informasi selengkapnya tentang mengapa kami menggunakan Pembaca Sumber, buka Pembaca Sumber.
Menjelaskan format data aliran audio
- Gunakan IMFSourceReader::GetCurrentMediaType untuk mendapatkan jenis media saat ini dari stream.
- Gunakan IMFMediaType::MFCreateWaveFormatExFromMFMediaType untuk mengonversi jenis media audio saat ini ke dalam buffer WAVEFORMATEX dengan menggunakan hasil operasi sebelumnya sebagai input. Struktur ini menentukan format data aliran audio gelombang yang digunakan setelah audio dimuat.
Format
Membaca aliran audio
- Dapatkan durasi, dalam detik, aliran audio dengan memanggil IMFSourceReader::GetPresentationAttribute lalu mengonversi durasi menjadi byte.
- Baca file audio sebagai aliran dengan memanggil IMFSourceReader::ReadSample. ReadSample membaca sampel berikutnya dari sumber media.
- Gunakan IMFSample::ConvertToContiguousBuffer untuk menyalin konten buffer sampel audio (sampel) ke dalam array (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;
}
Mengaitkan suara ke objek
Mengaitkan suara ke objek terjadi ketika game menginisialisasi, dalam metode Simple3DGame::Initialize.
Ringkasan:
- Di kelas GameObject, ada properti HitSound yang digunakan untuk menghubungkan efek suara ke objek.
- Buat instans baru objek kelas SoundEffect dan kaitkan dengan objek game. Kelas ini memutar suara menggunakan API
XAudio2. Ini menggunakan suara mastering yang disediakan oleh kelas Audio. Data suara dapat dibaca dari lokasi file menggunakan kelas MediaReader.
SoundEffect::Initialize digunakan untuk menginisialisasi instance SoundEffect dengan parameter input berikut: pointer ke objek mesin suara (objek IXAudio2 yang dibuat di metode Audio::CreateDeviceIndependentResources), penunjuk ke format file .wav menggunakan MediaReader::GetOutputWaveFormatEx, dan data suara yang dimuat menggunakan metode MediaReader::LoadMedia. Selama inisialisasi, suara sumber untuk efek suara juga dibuat.
SoundEffect::Inisialisasi metode
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;
}
Putar suara
Pemicu untuk memutar efek suara didefinisikan dalam metode Simple3DGame::UpdateDynamics karena di sinilah pergerakan objek diperbarui dan tabrakan antar objek ditentukan.
Karena interaksi antara objek sangat berbeda, tergantung pada permainan, kita tidak akan membahas dinamika objek game di sini. Jika Anda tertarik untuk memahami implementasinya, buka metode Simple3DGame::UpdateDynamics.
Pada prinsipnya, ketika tabrakan terjadi, itu memicu pemutaran efek suara dengan memanggil SoundEffect::PlaySound. Metode ini menghentikan efek suara apa pun yang sedang diputar dan mengantrikan buffer di dalam memori dengan data suara yang diinginkan. Ini menggunakan suara sumber untuk mengatur volume, mengirimkan data suara, dan memulai pemutaran.
Metode SoundEffect::PlaySound
- Menggunakan objek suara sumber m_sourceVoice untuk memulai pemutaran buffer data suara m_soundData
- Membuat XAUDIO2_BUFFER, yang menyediakan referensi ke buffer data suara, lalu mengirimkannya dengan panggilan ke IXAudio2SourceVoice::SubmitSourceBuffer.
- Dengan data suara yang diantrekan, SoundEffect::PlaySound memulai pemutaran dengan memanggil 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()
);
}
Metode Simple3DGame::UpdateDynamics
Metode Simple3DGame::UpdateDynamics menjaga interaksi dan tabrakan antar objek game. Ketika objek bertabrakan (atau berpotongan), itu memicu efek suara terkait untuk dimainkan.
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
}
Langkah selanjutnya
Kami telah membahas kerangka kerja UWP, grafis, kontrol, antarmuka pengguna, dan audio game Windows 10. Bagian berikutnya dari tutorial ini, Memperluas contoh game, menjelaskan opsi lain yang dapat digunakan saat mengembangkan game.
Konsep audio
Untuk pengembangan game Windows 10, gunakan XAudio2 versi 2.9. Versi ini dikirim dengan Windows 10. Untuk informasi lebih lanjut, kunjungi Versi XAudio2.
AudioX2 adalah API tingkat rendah yang menyediakan fondasi pemrosesan dan pencampuran sinyal. Untuk informasi selengkapnya, lihat Konsep Kunci XAudio2.
Suara XAudio2
Ada tiga jenis objek suara XAudio2: sumber, submix, dan suara mastering. Suara adalah objek yang digunakan XAudio2 untuk memproses, memanipulasi, dan memutar data audio.
- Suara sumber beroperasi pada data audio yang disediakan oleh klien.
- Suara sumber dan suara submix mengirimkan outputnya ke satu atau beberapa suara submix atau suara mastering.
- Submix dan proses mastering mencampur audio dari semua suara yang terhubung, dan memproses hasilnya.
- Suara master menerima data dari suara sumber dan suara submix, dan mengirimkan data tersebut ke perangkat keras audio.
Untuk informasi selengkapnya, kunjungi XAudio2 suara.
Grafik audio
Diagram audio adalah kumpulan suara XAudio2. Audio dimulai di salah satu sisi grafik audio pada suara sumber, secara opsional melewati satu atau beberapa suara submix, dan berakhir pada suara mastering. Graf audio akan berisi suara sumber untuk setiap suara yang sedang diputar, nol atau lebih suara submix, dan satu suara utama. Grafik audio paling sederhana, dan batas minimum yang diperlukan untuk menghasilkan suara dalam XAudio2, adalah suara dari satu sumber yang langsung diteruskan ke suara master. Untuk informasi selengkapnya, buka grafik audio .
Pembacaan tambahan
- Cara: Menginisialisasi XAudio2
- Cara: Memuat File Data Audio di XAudio2
- Cara: Memutar Suara dengan XAudio2
File .h audio utama
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:
...
};