Bagikan melalui


Menambahkan audio ke sampel Marble Maze

Dokumen ini menjelaskan praktik utama yang perlu dipertimbangkan saat Anda bekerja dengan audio dan menunjukkan bagaimana Marble Maze menerapkan praktik ini. Marble Maze menggunakan Microsoft Media Foundation untuk memuat sumber daya audio dari file, dan XAudio2 untuk mencampur dan memutar audio dan menerapkan efek pada audio.

Marble Maze memainkan musik di latar belakang, dan juga menggunakan suara gameplay untuk menunjukkan peristiwa permainan, seperti ketika marmer mengenai dinding. Bagian penting dari implementasi ini adalah Marble Maze menggunakan gaung, atau gema, efek untuk mensimulasikan suara marmer saat terpental. Implementasi efek gaung menyebabkan gema menjangkau Anda lebih cepat dan keras di ruangan kecil; gema lebih tenang dan menjangkau Anda lebih lambat di kamar yang lebih besar.

Catatan

Kode sampel yang sesuai dengan dokumen ini ditemukan dalam sampel permainan DirectX Marble Maze.

Berikut adalah beberapa poin penting yang dibahas dokumen ini ketika Anda bekerja dengan audio dalam permainan Anda:

  • Pertimbangkan untuk menggunakan Media Foundation untuk mendekode aset audio dan XAudio2 untuk memutar audio. Namun, jika Anda memiliki mekanisme pemuatan aset audio yang ada yang berfungsi di aplikasi Platform Windows Universal (UWP), Anda dapat menggunakannya.

  • Grafik audio berisi satu suara sumber untuk setiap suara aktif, nol atau beberapa suara submix, dan satu suara master. Suara sumber dapat dimasukkan ke dalam suara submix dan/atau suara yang menguasai. Suara submix mengumpan ke suara submix lain atau suara yang menguasai.

  • Jika file musik latar belakang Anda besar, pertimbangkan untuk mengalirkan musik Anda ke buffer yang lebih kecil sehingga lebih sedikit memori yang digunakan.

  • Jika masuk akal untuk melakukannya, jeda pemutaran audio saat aplikasi kehilangan fokus atau visibilitas, atau ditangguhkan. Lanjutkan pemutaran saat aplikasi Anda mendapatkan kembali fokus, menjadi terlihat, atau dilanjutkan.

  • Atur kategori audio untuk mencerminkan peran setiap suara. Misalnya, Anda biasanya menggunakan AudioCategory_GameMedia untuk audio latar belakang game dan AudioCategory_GameEffects untuk efek suara.

  • Tangani perubahan perangkat, termasuk headphone, dengan merilis dan membuat ulang semua sumber daya dan antarmuka audio.

  • Pertimbangkan apakah akan memadatkan file audio saat meminimalkan ruang disk dan biaya streaming adalah persyaratan. Jika tidak, Anda dapat membiarkan audio tidak dikompresi sehingga dimuat lebih cepat.

Memperkenalkan XAudio2 dan Microsoft Media Foundation

XAudio2 adalah pustaka audio tingkat rendah untuk Windows yang secara khusus mendukung audio game. Ini menyediakan pemrosesan sinyal digital (DSP) dan mesin grafik audio untuk game. XAudio2 berkembang pada pendahulunya, DirectSound dan XAudio, dengan mendukung tren komputasi seperti arsitektur floating-point SIMD dan audio HD. Ini juga mendukung tuntutan pemrosesan suara yang lebih kompleks dari game saat ini.

Dokumen XAudio2 Key Concepts menjelaskan konsep utama untuk menggunakan XAudio2. Secara singkat, konsepnya adalah:

  • Antarmuka IXAudio2 adalah inti dari mesin XAudio2. Marble Maze menggunakan antarmuka ini untuk membuat suara dan menerima pemberitahuan ketika perangkat output berubah atau gagal.

  • Suara memproses, menyesuaikan, dan memutar data audio.

  • Suara sumber adalah kumpulan saluran audio (mono, 5.1, dan sebagainya) dan mewakili satu aliran data audio. Di XAudio2, suara sumber adalah tempat pemrosesan audio dimulai. Biasanya, data suara dimuat dari sumber eksternal, seperti file atau jaringan, dan dikirim ke suara sumber. Marble Maze menggunakan Media Foundation untuk memuat data suara dari file. Media Foundation diperkenalkan nanti dalam dokumen ini.

  • Suara submix memproses data audio. Pemrosesan ini dapat mencakup mengubah aliran audio atau menggabungkan beberapa aliran menjadi satu. Marble Maze menggunakan submix untuk membuat efek reverb.

  • Suara master menggabungkan data dari suara sumber dan submix dan mengirim data tersebut ke perangkat keras audio.

  • Grafik audio berisi satu suara sumber untuk setiap suara aktif, nol atau beberapa suara submix, dan hanya satu suara yang menguasai.

  • Panggilan balik menginformasikan kode klien bahwa beberapa peristiwa telah terjadi dalam suara atau di objek mesin. Dengan menggunakan panggilan balik, Anda dapat menggunakan kembali memori saat XAudio2 selesai dengan buffer, bereaksi saat perangkat audio berubah (misalnya, saat Anda menyambungkan atau memutuskan sambungan headphone), dan banyak lagi. Menangani perubahan headphone dan perangkat nanti dalam dokumen ini menjelaskan bagaimana Marble Maze menggunakan mekanisme ini untuk menangani perubahan perangkat.

Marble Maze menggunakan dua mesin audio (dengan kata lain, dua objek IXAudio2 ) untuk memproses audio. Satu mesin memproses musik latar belakang, dan mesin lainnya memproses suara gameplay.

Marble Maze juga harus membuat satu suara yang menguasai untuk setiap mesin. Ingat bahwa mesin master menggabungkan aliran audio ke dalam satu aliran dan mengirim aliran tersebut ke perangkat keras audio. Aliran musik latar belakang, suara sumber, menghasilkan data ke suara yang menguasai dan ke dua suara submix. Suara submix melakukan efek reverb.

Media Foundation adalah pustaka multimedia yang mendukung banyak format audio dan video. XAudio2 dan Media Foundation saling melengkapi. Marble Maze menggunakan Media Foundation untuk memuat aset audio dari file dan menggunakan XAudio2 untuk memutar audio. Anda tidak perlu menggunakan Media Foundation untuk memuat aset audio. Jika Anda memiliki mekanisme pemuatan aset audio yang sudah ada yang berfungsi di aplikasi Platform Windows Universal (UWP), gunakan mekanisme tersebut. Audio, video, dan kamera membahas beberapa cara menerapkan audio di aplikasi UWP.

Untuk informasi selengkapnya tentang XAudio2, lihat Panduan Pemrograman. Untuk informasi selengkapnya tentang Media Foundation, lihat Microsoft Media Foundation.

Menginisialisasi sumber daya audio

Marble Mazes menggunakan file Windows Media Audio (.wma) untuk musik latar belakang, dan file WAV (.wav) untuk suara gameplay. Format ini didukung oleh Media Foundation. Meskipun format file .wav secara asli didukung oleh XAudio2, game harus mengurai format file secara manual untuk mengisi struktur data XAudio2 yang sesuai. Marble Maze menggunakan Media Foundation untuk lebih mudah bekerja dengan file .wav. Untuk daftar lengkap format media yang didukung oleh Media Foundation, lihat Format Media yang Didukung di Media Foundation. Marble Maze tidak menggunakan format audio waktu desain dan run-time terpisah, dan tidak menggunakan dukungan kompresi XAudio2 ADPCM. Untuk informasi selengkapnya tentang kompresi ADPCM di XAudio2, lihat Gambaran Umum ADPCM.

Metode Audio::CreateResources , yang dipanggil dari MarbleMazeMain::LoadDeferredResources, memuat aliran audio dari file, menginisialisasi objek mesin XAudio2, dan membuat sumber, submix, dan suara master.

Membuat mesin XAudio2

Ingat bahwa Marble Maze membuat satu objek IXAudio2 untuk mewakili setiap mesin audio yang digunakannya. Untuk membuat mesin audio, panggil metode XAudio2Buat . Contoh berikut menunjukkan bagaimana Marble Maze membuat mesin audio yang memproses musik latar belakang.

// In Audio.h
class Audio
{
private:
    IXAudio2*                   m_musicEngine;
// ...
}

// In Audio.cpp
void Audio::CreateResources()
{
    try
    {
        // ...
        DX::ThrowIfFailed(
            XAudio2Create(&m_musicEngine)
            );
        // ...
    }
    // ...
}

Marble Maze melakukan langkah serupa untuk membuat mesin audio yang memainkan suara gameplay.

Cara bekerja dengan antarmuka IXAudio2 di aplikasi UWP berbeda dari aplikasi desktop dengan dua cara. Pertama, Anda tidak perlu memanggil CoInitializeEx sebelum memanggil XAudio2Create. Selain itu, IXAudio2 tidak lagi mendukung enumerasi perangkat. Untuk informasi tentang cara menghitung perangkat audio, lihat Menghitung perangkat.

Membuat suara yang menguasai

Contoh berikut menunjukkan bagaimana metode Audio::CreateResources membuat suara mastering untuk musik latar belakang menggunakan metode IXAudio2::CreateMasteringVoice . Dalam contoh ini, m_musicMasteringVoice adalah objek IXAudio2MasteringVoice . Kami menentukan dua saluran input; ini menyederhanakan logika untuk efek reverb.

Kami menentukan 48000 sebagai laju sampel input. Kami memilih laju sampel ini karena mewakili keseimbangan antara kualitas audio dan jumlah pemrosesan CPU yang diperlukan. Tingkat sampel yang lebih besar akan membutuhkan lebih banyak pemrosesan CPU tanpa memiliki manfaat kualitas yang nyata.

Terakhir, kami menentukan AudioCategory_GameMedia sebagai kategori aliran audio sehingga pengguna dapat mendengarkan musik dari aplikasi yang berbeda saat mereka memainkan game. Saat aplikasi musik diputar, Windows mematikan suara apa pun yang dibuat oleh opsi AudioCategory_GameMedia . Pengguna masih mendengar suara gameplay karena dibuat oleh opsi AudioCategory_GameEffects . Untuk informasi selengkapnya tentang kategori audio, lihat AUDIO_STREAM_CATEGORY.

// This sample plays the equivalent of background music, which we tag on the  
// mastering voice as AudioCategory_GameMedia. In ordinary usage, if we were  
// playing the music track with no effects, we could route it entirely through 
// Media Foundation. Here, we are using XAudio2 to apply a reverb effect to the 
// music, so we use Media Foundation to decode the data then we feed it through 
// the XAudio2 pipeline as a separate Mastering Voice, so that we can tag it 
// as Game Media. We default the mastering voice to 2 channels to simplify  
// the reverb logic.
DX::ThrowIfFailed(
    m_musicEngine->CreateMasteringVoice(
        &m_musicMasteringVoice,
        2,
        48000,
        0,
        nullptr,
        nullptr,
        AudioCategory_GameMedia
        )
);

Metode Audio::CreateResources melakukan langkah serupa untuk membuat suara master untuk suara gameplay, kecuali bahwa metode menentukan AudioCategory_GameEffects untuk parameter StreamCategory , yang merupakan default.

Membuat efek reverb

Untuk setiap suara, Anda dapat menggunakan XAudio2 untuk membuat urutan efek yang memproses audio. Urutan seperti itu dikenal sebagai rantai efek. Gunakan rantai efek saat Anda ingin menerapkan satu atau beberapa efek ke suara. Rantai efek dapat merusak; artinya, setiap efek dalam rantai dapat menimpa buffer audio. Properti ini penting karena XAudio2 tidak menjamin bahwa buffer output diinisialisasi dengan keheningan. Objek efek diwakili dalam XAudio2 oleh objek pemrosesan audio lintas platform (XAPO). Untuk informasi selengkapnya tentang XAPO, lihat Gambaran Umum XAPO.

Saat Anda membuat rantai efek, ikuti langkah-langkah berikut:

  1. Buat objek efek.

  2. Isi struktur XAUDIO2_EFFECT_DESCRIPTOR dengan data efek.

  3. Isi struktur XAUDIO2_EFFECT_CHAIN dengan data.

  4. Terapkan rantai efek ke suara.

  5. Isi struktur parameter efek dan terapkan ke efeknya.

  6. Nonaktifkan atau aktifkan efek jika sesuai.

Kelas Audio mendefinisikan metode CreateReverb untuk membuat rantai efek yang mengimplementasikan reverb. Metode ini memanggil metode XAudio2CreateReverb untuk membuat objek ComPtr<IUnknown> , soundEffectXAPO, yang bertindak sebagai suara submix untuk efek gaung.

Microsoft::WRL::ComPtr<IUnknown> soundEffectXAPO;

DX::ThrowIfFailed(
    XAudio2CreateReverb(&soundEffectXAPO)
    );

Struktur XAUDIO2_EFFECT_DESCRIPTOR berisi informasi tentang XAPO untuk digunakan dalam rantai efek, misalnya, jumlah target saluran output. Metode Audio::CreateReverb membuat objek XAUDIO2_EFFECT_DESCRIPTOR , soundEffectdescriptor, yang diatur ke status dinonaktifkan, menggunakan dua saluran output, dan mereferensikan soundEffectXAPO untuk efek reverb. soundEffectdescriptor dimulai dalam keadaan dinonaktifkan karena game harus mengatur parameter sebelum efek mulai memodifikasi suara game. Marble Maze menggunakan dua saluran output untuk menyederhanakan logika untuk efek reverb.

soundEffectdescriptor.InitialState = false;
soundEffectdescriptor.OutputChannels = 2;
soundEffectdescriptor.pEffect = soundEffectXAPO.Get();

Jika rantai efek Anda memiliki beberapa efek, setiap efek memerlukan objek . Struktur XAUDIO2_EFFECT_CHAIN menyimpan array objek XAUDIO2_EFFECT_DESCRIPTOR yang berpartisipasi dalam efek. Contoh berikut menunjukkan bagaimana metode Audio::CreateReverb menentukan satu efek untuk mengimplementasikan reverb.

XAUDIO2_EFFECT_CHAIN soundEffectChain;

// ...

soundEffectChain.EffectCount = 1;
soundEffectChain.pEffectDescriptors = &soundEffectdescriptor;

Metode Audio::CreateReverb memanggil metode IXAudio2::CreateSubmixVoice untuk membuat suara submix untuk efeknya. Ini menentukan objek XAUDIO2_EFFECT_CHAIN , soundEffectChain, untuk parameter pEffectChain untuk mengaitkan rantai efek dengan suara. Marble Maze juga menentukan dua saluran output dan laju sampel 48 kilohertz.

DX::ThrowIfFailed(
    engine->CreateSubmixVoice(newSubmix, 2, 48000, 0, 0, nullptr, &soundEffectChain)
    );

Tip

Jika Anda ingin melampirkan rantai efek yang ada ke suara submix yang ada, atau Anda ingin mengganti rantai efek saat ini, gunakan metode IXAudio2Voice::SetEffectChain .

Metode Audio::CreateReverb memanggil IXAudio2Voice::SetEffectParameters untuk mengatur parameter tambahan yang terkait dengan efeknya. Metode ini mengambil struktur parameter yang khusus untuk efeknya. Objek XAUDIO2FX_REVERB_PARAMETERS , m_reverbParametersSmall, yang berisi parameter efek untuk gaung, diinisialisasi dalam metode Audio::Initialize karena setiap efek gaung memiliki parameter yang sama. Contoh berikut menunjukkan bagaimana metode Audio::Initialize menginisialisasi parameter reverb untuk reverb near-field.

m_reverbParametersSmall.ReflectionsDelay = XAUDIO2FX_REVERB_DEFAULT_REFLECTIONS_DELAY;
m_reverbParametersSmall.ReverbDelay = XAUDIO2FX_REVERB_DEFAULT_REVERB_DELAY;
m_reverbParametersSmall.RearDelay = XAUDIO2FX_REVERB_DEFAULT_REAR_DELAY;
m_reverbParametersSmall.PositionLeft = XAUDIO2FX_REVERB_DEFAULT_POSITION;
m_reverbParametersSmall.PositionRight = XAUDIO2FX_REVERB_DEFAULT_POSITION;
m_reverbParametersSmall.PositionMatrixLeft = XAUDIO2FX_REVERB_DEFAULT_POSITION_MATRIX;
m_reverbParametersSmall.PositionMatrixRight = XAUDIO2FX_REVERB_DEFAULT_POSITION_MATRIX;
m_reverbParametersSmall.EarlyDiffusion = 4;
m_reverbParametersSmall.LateDiffusion = 15;
m_reverbParametersSmall.LowEQGain = XAUDIO2FX_REVERB_DEFAULT_LOW_EQ_GAIN;
m_reverbParametersSmall.LowEQCutoff = XAUDIO2FX_REVERB_DEFAULT_LOW_EQ_CUTOFF;
m_reverbParametersSmall.HighEQGain = XAUDIO2FX_REVERB_DEFAULT_HIGH_EQ_GAIN;
m_reverbParametersSmall.HighEQCutoff = XAUDIO2FX_REVERB_DEFAULT_HIGH_EQ_CUTOFF;
m_reverbParametersSmall.RoomFilterFreq = XAUDIO2FX_REVERB_DEFAULT_ROOM_FILTER_FREQ;
m_reverbParametersSmall.RoomFilterMain = XAUDIO2FX_REVERB_DEFAULT_ROOM_FILTER_MAIN;
m_reverbParametersSmall.RoomFilterHF = XAUDIO2FX_REVERB_DEFAULT_ROOM_FILTER_HF;
m_reverbParametersSmall.ReflectionsGain = XAUDIO2FX_REVERB_DEFAULT_REFLECTIONS_GAIN;
m_reverbParametersSmall.ReverbGain = XAUDIO2FX_REVERB_DEFAULT_REVERB_GAIN;
m_reverbParametersSmall.DecayTime = XAUDIO2FX_REVERB_DEFAULT_DECAY_TIME;
m_reverbParametersSmall.Density = XAUDIO2FX_REVERB_DEFAULT_DENSITY;
m_reverbParametersSmall.RoomSize = XAUDIO2FX_REVERB_DEFAULT_ROOM_SIZE;
m_reverbParametersSmall.WetDryMix = XAUDIO2FX_REVERB_DEFAULT_WET_DRY_MIX;
m_reverbParametersSmall.DisableLateField = TRUE;

Contoh ini menggunakan nilai default untuk sebagian besar parameter gaung, tetapi mengatur DisableLateField ke TRUE untuk menentukan gaung bidang dekat, EarlyDiffusion ke 4 untuk mensimulasikan permukaan dekat datar, dan LateDiffusion ke 15 untuk mensimulasikan permukaan yang sangat jauh. Permukaan dekat yang datar menyebabkan gema mencapai Anda lebih cepat dan keras; permukaan jauh yang berbeda menyebabkan gema menjadi lebih tenang dan mencapai Anda lebih lambat. Anda dapat bereksperimen dengan nilai gaung untuk mendapatkan efek yang diinginkan dalam game Anda atau menggunakan metode ReverbConvertI3DL2ToNative untuk menggunakan parameter I3DL2 (Interactive 3D Audio Rendering Guidelines Level 2.0) standar industri.

Contoh berikut menunjukkan bagaimana Audio::CreateReverb mengatur parameter reverb. newSubmix adalah objek IXAudio2SubmixVoice**. parameter adalah objek XAUDIO2FX_REVERB_PARAMETERS*.

DX::ThrowIfFailed(
    (*newSubmix)->SetEffectParameters(0, parameters, sizeof(m_reverbParametersSmall))
    );

Metode Audio::CreateReverb selesai dengan mengaktifkan efek menggunakan IXAudio2Voice::EnableEffect jika bendera enableEffect diatur. Ini juga mengatur volumenya menggunakan IXAudio2Voice::SetVolume dan matriks output menggunakan IXAudio2Voice::SetOutputMatrix. Bagian ini mengatur volume menjadi penuh (1.0) dan kemudian menentukan matriks volume untuk diam untuk input kiri dan kanan dan speaker output kiri dan kanan. Kami melakukan ini karena kode lain kemudian memudar silang antara dua gaung (mensimulasikan transisi dari dekat dinding menjadi di ruangan besar), atau membisukan kedua gaung jika diperlukan. Ketika jalur reverb dinyalakan, game menetapkan matriks {1.0f, 0.0f, 0.0f, 1.0f} untuk merutekan output reverb kiri ke input kiri suara master dan output reverb kanan ke input kanan suara mastering.

if (enableEffect)
{
    DX::ThrowIfFailed(
        (*newSubmix)->EnableEffect(0)
        );    
}

DX::ThrowIfFailed(
    (*newSubmix)->SetVolume (1.0f)
    );

float outputMatrix[4] = {0, 0, 0, 0};
DX::ThrowIfFailed(
    (*newSubmix)->SetOutputMatrix(masteringVoice, 2, 2, outputMatrix)
    );

Marble Maze memanggil metode Audio::CreateReverb empat kali: dua kali untuk musik latar belakang dan dua kali untuk suara gameplay. Berikut ini menunjukkan bagaimana Marble Maze memanggil metode CreateReverb untuk musik latar belakang.

CreateReverb(
    m_musicEngine, 
    m_musicMasteringVoice, 
    &m_reverbParametersSmall, 
    &m_musicReverbVoiceSmallRoom, 
    true
    );
CreateReverb(
    m_musicEngine, 
    m_musicMasteringVoice, 
    &m_reverbParametersLarge, 
    &m_musicReverbVoiceLargeRoom, 
    true
    );

Untuk daftar kemungkinan sumber efek untuk digunakan dengan XAudio2, lihat Efek Audio XAudio2.

Memuat data audio dari file

Marble Maze mendefinisikan kelas MediaStreamer , yang menggunakan Media Foundation untuk memuat sumber daya audio dari file. Marble Maze menggunakan satu objek MediaStreamer untuk memuat setiap file audio.

Marble Maze memanggil metode MediaStreamer::Initialize untuk menginisialisasi setiap aliran audio. Berikut cara metode Audio::CreateResources memanggil MediaStreamer::Initialize untuk menginisialisasi aliran audio untuk musik latar belakang:

// Media Foundation is a convenient way to get both file I/O and format decode for 
// audio assets. You can replace the streamer in this sample with your own file I/O 
// and decode routines.
m_musicStreamer.Initialize(L"Media\\Audio\\background.wma");

Metode MediaStreamer::Initialize dimulai dengan memanggil metode MFStartup untuk menginisialisasi Media Foundation. MF_VERSION adalah makro yang didefinisikan dalam mfapi.h, dan itulah yang kami tentukan sebagai versi Media Foundation untuk digunakan.

DX::ThrowIfFailed(
    MFStartup(MF_VERSION)
    );

MediaStreamer::Initialize kemudian memanggil MFCreateSourceReaderFromURL untuk membuat objek IMFSourceReader . Objek IMFSourceReader , m_reader, membaca data media dari file yang ditentukan oleh url.

DX::ThrowIfFailed(
    MFCreateSourceReaderFromURL(url, nullptr, &m_reader)
    );

Metode MediaStreamer::Initialize kemudian membuat objek IMFMediaType menggunakan MFCreateMediaType untuk menjelaskan format aliran audio. Format audio memiliki dua jenis: jenis utama dan subjenis. Jenis utama menentukan format keseluruhan media, seperti video, audio, skrip, dan sebagainya. Subjenis menentukan format, seperti PCM, ADPCM, atau WMA.

Metode MediaStreamer::Initialize menggunakan metode IMFAttributes::SetGUID untuk menentukan jenis utama (MF_MT_MAJOR_TYPE) sebagai audio (MFMediaType_Audio) dan jenis minor (MF_MT_SUBTYPE) sebagai audio PCM yang tidak dikompresi (MFAudioFormat_PCM). MF_MT_MAJOR_TYPE dan MF_MT_SUBTYPE adalah Atribut Media Foundation. MFMediaType_Audio dan MFAudioFormat_PCM adalah GUID jenis dan subjenis; lihat Jenis Media Audio untuk informasi selengkapnya. Metode IMFSourceReader::SetCurrentMediaType mengaitkan jenis media dengan pembaca aliran.

// Set the decoded output format as PCM. 
// XAudio2 on Windows can process PCM and ADPCM-encoded buffers. 
// When this sample uses Media Foundation, it always decodes into PCM.

DX::ThrowIfFailed(
    MFCreateMediaType(&mediaType)
    );

DX::ThrowIfFailed(
    mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio)
    );

DX::ThrowIfFailed(
    mediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM)
    );

DX::ThrowIfFailed(
    m_reader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, mediaType.Get())
    );

Metode MediaStreamer::Initialize kemudian mendapatkan format media output lengkap dari Media Foundation menggunakan IMFSourceReader::GetCurrentMediaType dan memanggil metode MFCreateWaveFormatExFromMFMediaType untuk mengonversi jenis media audio Media Foundation ke struktur WAVEFORMATEX . Struktur WAVEFORMATEX mendefinisikan format data waveform-audio. Marble Maze menggunakan struktur ini untuk membuat suara sumber dan menerapkan filter low-pass ke suara bergulir marmer.

// Get the complete WAVEFORMAT from the Media Type.
DX::ThrowIfFailed(
    m_reader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, &outputMediaType)
    );

uint32 formatSize = 0;
WAVEFORMATEX* waveFormat;
DX::ThrowIfFailed(
    MFCreateWaveFormatExFromMFMediaType(outputMediaType.Get(), &waveFormat, &formatSize)
    );
CopyMemory(&m_waveFormat, waveFormat, sizeof(m_waveFormat));
CoTaskMemFree(waveFormat);

Penting

Metode MFCreateWaveFormatExFromMFMediaType menggunakan CoTaskMemAlloc untuk mengalokasikan objek WAVEFORMATEX . Oleh karena itu, pastikan Anda memanggil CoTaskMemFree ketika Anda selesai menggunakan objek ini.

 

Metode MediaStreamer::Initialize selesai dengan menghitung panjang aliran, m_maxStreamLengthInBytes, dalam byte. Untuk melakukannya, metode ini memanggil metode IMFSourceReader::GetPresentationAttribute untuk mendapatkan durasi aliran audio dalam unit 100 nanodetik, mengonversi durasi menjadi bagian, lalu mengalikan dengan tingkat transfer data rata-rata dalam byte per detik. Marble Maze nantinya menggunakan nilai ini untuk mengalokasikan buffer yang menyimpan setiap suara gameplay.

// Get the total length of the stream, in bytes.
PROPVARIANT var;
DX::ThrowIfFailed(
    m_reader->
        GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &var)
    );

// duration is in 100ns units; convert to seconds, and round up
// to the nearest whole byte.
ULONGLONG duration = var.uhVal.QuadPart;
m_maxStreamLengthInBytes =
    static_cast<unsigned int>(
        ((duration * static_cast<ULONGLONG>(m_waveFormat.nAvgBytesPerSec)) + 10000000)
        / 10000000
        );

Membuat suara sumber

Marble Maze menciptakan suara sumber XAudio2 untuk memainkan setiap suara permainan dan musiknya dalam suara sumber. Kelas Audio mendefinisikan objek IXAudio2SourceVoice untuk musik latar belakang dan array objek SoundEffectData untuk menahan suara gameplay. Struktur SoundEffectData menyimpan objek IXAudio2SourceVoice untuk efek dan juga mendefinisikan data terkait efek lainnya, seperti buffer audio. Audio.h mendefinisikan enumerasi SoundEvent . Marble Maze menggunakan enumerasi ini untuk mengidentifikasi setiap suara gameplay. Kelas Audio juga menggunakan enumerasi ini untuk mengindeks array objek SoundEffectData .

enum SoundEvent
{
    RollingEvent        = 0,
    FallingEvent        = 1,
    CollisionEvent      = 2,
    CheckpointEvent     = 3,
    MenuChangeEvent     = 4,
    MenuSelectedEvent   = 5,
    LastSoundEvent,
};

Tabel berikut ini memperlihatkan hubungan antara masing-masing nilai ini, file yang berisi data suara terkait, dan deskripsi singkat tentang apa yang diwakili setiap suara. File audio terletak di folder \Media\Audio .

Nilai SoundEvent Nama file Deskripsi
RollingEvent MarbleRoll.wav Dimainkan sebagai gulungan marmer.
FallingEvent MarbleFall.wav Dimainkan ketika kelereng jatuh dari labirin.
CollisionEvent MarbleHit.wav Dimainkan ketika marmer bertabrakan dengan labirin.
CheckpointEvent Checkpoint.wav Dimainkan ketika kelereng melewati titik pemeriksaan.
MenuChangeEvent MenuChange.wav Dimainkan ketika pengguna mengubah item menu saat ini.
MenuSelectedEvent MenuPilih.wav Diputar saat pengguna memilih item menu.

 

Contoh berikut menunjukkan bagaimana metode Audio::CreateResources membuat suara sumber untuk musik latar belakang. Struktur XAUDIO2_SEND_DESCRIPTOR menentukan suara tujuan target dari suara lain dan menentukan apakah filter harus digunakan. Marble Maze memanggil metode Audio::SetSoundEffectFilter untuk menggunakan filter untuk mengubah suara bola saat bergulir. Struktur XAUDIO2_VOICE_SENDS menentukan kumpulan suara untuk menerima data dari satu suara output. Marble Maze mengirimkan data dari suara sumber ke suara yang menguasai (untuk bagian suara yang kering, atau tidak berubah) dan ke dua suara submix yang mengimplementasikan suara basah, atau gaung, sebagian dari suara yang diputar.

Metode IXAudio2::CreateSourceVoice membuat dan mengonfigurasi suara sumber. Dibutuhkan struktur WAVEFORMATEX yang menentukan format buffer audio yang dikirim ke suara. Seperti disebutkan sebelumnya, Marble Maze menggunakan format PCM.

XAUDIO2_SEND_DESCRIPTOR descriptors[3];
descriptors[0].pOutputVoice = m_musicMasteringVoice;
descriptors[0].Flags = 0;
descriptors[1].pOutputVoice = m_musicReverbVoiceSmallRoom;
descriptors[1].Flags = 0;
descriptors[2].pOutputVoice = m_musicReverbVoiceLargeRoom;
descriptors[2].Flags = 0;
XAUDIO2_VOICE_SENDS sends = {0};
sends.SendCount = 3;
sends.pSends = descriptors;
WAVEFORMATEX& waveFormat = m_musicStreamer.GetOutputWaveFormatEx();

DX::ThrowIfFailed(
    m_musicEngine->CreateSourceVoice(&m_musicSourceVoice, &waveFormat, 0, 1.0f, &m_voiceContext, &sends, nullptr)
    );

DX::ThrowIfFailed(
    m_musicMasteringVoice->SetVolume(0.4f)
    );

Memutar musik latar belakang

Suara sumber dibuat dalam status berhenti. Marble Maze memulai musik latar belakang dalam perulangan permainan. Panggilan pertama ke MarbleMazeMain::Update memanggil Audio::Start untuk memulai musik latar belakang.

if (!m_audio.m_isAudioStarted)
{
    m_audio.Start();
}

Metode Audio::Start memanggil IXAudio2SourceVoice::Start untuk mulai memproses suara sumber untuk musik latar belakang.

void Audio::Start()
{     
    if (m_engineExperiencedCriticalError)
    {
        return;
    }

    HRESULT hr = m_musicSourceVoice->Start(0);

    if SUCCEEDED(hr) {
        m_isAudioStarted = true;
    }
    else
    {
        m_engineExperiencedCriticalError = true;
    }
}

Suara sumber meneruskan data audio tersebut ke tahap berikutnya dari grafik audio. Dalam kasus Marble Maze, tahap berikutnya berisi dua suara submix yang menerapkan dua efek gaung ke audio. Satu suara submix menerapkan reverb bidang akhir yang dekat; yang kedua menerapkan gaung bidang yang jauh terlambat.

Jumlah kontribusi setiap suara submix terhadap campuran akhir ditentukan oleh ukuran dan bentuk ruangan. Gaung bidang dekat berkontribusi lebih banyak ketika bola berada di dekat dinding atau di ruangan kecil, dan gaung ladang akhir berkontribusi lebih banyak ketika bola berada di ruang yang besar. Teknik ini menghasilkan efek gema yang lebih realistis saat marmer bergerak melalui labirin. Untuk mempelajari selengkapnya tentang bagaimana Marble Maze mengimplementasikan efek ini, lihat Audio::SetRoomSize dan Physics::CalculateCurrentRoomSize dalam kode sumber Marble Maze.

Catatan

Dalam gim di mana sebagian besar ukuran kamar relatif sama, Anda dapat menggunakan model reverb yang lebih mendasar. Misalnya, Anda dapat menggunakan satu pengaturan gaung untuk semua kamar atau Anda dapat membuat pengaturan gaung yang telah ditentukan sebelumnya untuk setiap kamar.

Metode Audio::CreateResources menggunakan Media Foundation untuk memuat musik latar belakang. Namun, pada titik ini, suara sumber tidak memiliki data audio untuk dikerjakan. Selain itu, karena musik latar belakang berputar, suara sumber harus diperbarui secara teratur dengan data sehingga musik terus diputar.

Agar suara sumber tetap terisi dengan data, perulangan game memperbarui buffer audio setiap bingkai. Metode MarbleMazeMain::Render memanggil Audio::Render untuk memproses buffer audio musik latar belakang. Kelas Audio mendefinisikan array tiga buffer audio, m_audioBuffers. Setiap buffer menyimpan data 64 KB (65536 byte). Perulangan membaca data dari objek Media Foundation dan menulis data tersebut ke suara sumber hingga suara sumber memiliki tiga buffer yang diantrekan.

Perhatian

Meskipun Marble Maze menggunakan buffer 64 KB untuk menyimpan data musik, Anda mungkin perlu menggunakan buffer yang lebih besar atau lebih kecil. Jumlah ini tergantung pada persyaratan permainan Anda.

// This sample processes audio buffers during the render cycle of the application.
// As long as the sample maintains a high-enough frame rate, this approach should
// not glitch audio. In game code, it is best for audio buffers to be processed
// on a separate thread that is not synced to the main render loop of the game.
void Audio::Render()
{
    if (m_engineExperiencedCriticalError)
    {
        m_engineExperiencedCriticalError = false;
        ReleaseResources();
        Initialize();
        CreateResources();
        Start();
        if (m_engineExperiencedCriticalError)
        {
            return;
        }
    }

    try
    {
        bool streamComplete;
        XAUDIO2_VOICE_STATE state;
        uint32 bufferLength;
        XAUDIO2_BUFFER buf = {0};

        // Use MediaStreamer to stream the buffers.
        m_musicSourceVoice->GetState(&state);
        while (state.BuffersQueued <= MAX_BUFFER_COUNT - 1)
        {
            streamComplete = m_musicStreamer.GetNextBuffer(
                m_audioBuffers[m_currentBuffer],
                STREAMING_BUFFER_SIZE,
                &bufferLength
                );

            if (bufferLength > 0)
            {
                buf.AudioBytes = bufferLength;
                buf.pAudioData = m_audioBuffers[m_currentBuffer];
                buf.Flags = (streamComplete) ? XAUDIO2_END_OF_STREAM : 0;
                buf.pContext = 0;
                DX::ThrowIfFailed(
                    m_musicSourceVoice->SubmitSourceBuffer(&buf)
                    );

                m_currentBuffer++;
                m_currentBuffer %= MAX_BUFFER_COUNT;
            }

            if (streamComplete)
            {
                // Loop the stream.
                m_musicStreamer.Restart();
                break;
            }

            m_musicSourceVoice->GetState(&state);
        }
    }
    catch (...)
    {
        m_engineExperiencedCriticalError = true;
    }
}

Perulangan juga menangani ketika objek Media Foundation mencapai akhir aliran. Dalam hal ini, metode ini memanggil metode IMFSourceReader::SetCurrentPosition untuk mengatur ulang posisi sumber audio.

void MediaStreamer::Restart()
{
    if (m_reader == nullptr)
    {
        return;
    }

    PROPVARIANT var = {0};
    var.vt = VT_I8;

    DX::ThrowIfFailed(
        m_reader->SetCurrentPosition(GUID_NULL, var)
        );
}

Untuk mengimplementasikan perulangan audio untuk buffer tunggal (atau untuk seluruh suara yang sepenuhnya dimuat ke dalam memori), Anda dapat mengatur bidang XAUDIO2_BUFFER::LoopCount ke XAUDIO2_LOOP_INFINITE saat Menginisialisasi suara. Marble Maze menggunakan teknik ini untuk memainkan suara bergulir untuk marmer.

if (sound == RollingEvent)
{
    m_soundEffects[sound].m_audioBuffer.LoopCount = XAUDIO2_LOOP_INFINITE;
}

Namun, untuk musik latar belakang, Marble Maze mengelola buffer secara langsung sehingga dapat mengontrol jumlah memori yang digunakan dengan lebih baik. Ketika file musik Anda besar, Anda dapat mengalirkan data musik ke buffer yang lebih kecil. Melakukannya dapat membantu menyeimbangkan ukuran memori dengan frekuensi kemampuan permainan untuk memproses dan mengalirkan data audio.

Tip

Jika game Anda memiliki kecepatan bingkai yang rendah atau bervariasi, memproses audio pada utas utama dapat menghasilkan jeda atau pop yang tidak terduga dalam audio karena mesin audio memiliki data audio buffer yang tidak mencukupi untuk dikerjakan. Jika game Anda sensitif terhadap masalah ini, pertimbangkan untuk memproses audio pada utas terpisah yang tidak melakukan penyajian. Pendekatan ini sangat berguna pada komputer yang memiliki beberapa prosesor karena game Anda dapat menggunakan prosesor menganggur.

Bereaksi terhadap peristiwa game

Kelas Audio menyediakan metode seperti PlaySoundEffect, IsSoundEffectStarted, StopSoundEffect, SetSoundEffectVolume, SetSoundEffectPitch, dan SetSoundEffectFilter untuk memungkinkan permainan mengontrol kapan suara dimainkan dan berhenti, dan untuk mengontrol properti suara seperti volume dan pitch. Misalnya, jika marmer jatuh dari labirin, MarbleMazeMain::Update memanggil metode Audio::P laySoundEffect untuk memutar suara FallingEvent .

m_audio.PlaySoundEffect(FallingEvent);

Metode Audio::P laySoundEffect memanggil metode IXAudio2SourceVoice::Start untuk memulai pemutaran suara. Jika metode IXAudio2SourceVoice::Start telah dipanggil, metode tersebut tidak dimulai lagi. Audio::P laySoundEffect kemudian melakukan logika kustom untuk suara tertentu.

void Audio::PlaySoundEffect(SoundEvent sound)
{
    XAUDIO2_BUFFER buf = {0};
    XAUDIO2_VOICE_STATE state = {0};

    if (m_engineExperiencedCriticalError)
    {
        // If there's an error, then we'll recreate the engine on the next
        // render pass.
        return;
    }

    SoundEffectData* soundEffect = &m_soundEffects[sound];
    HRESULT hr = soundEffect->m_soundEffectSourceVoice->Start();

    if FAILED(hr)
    {
        m_engineExperiencedCriticalError = true;
        return;
    }

    // For one-off voices, submit a new buffer if there's none queued up,
    // and allow up to two collisions to be queued up. 
    if (sound != RollingEvent)
    {
        XAUDIO2_VOICE_STATE state = {0};

        soundEffect->m_soundEffectSourceVoice->
            GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED);

        if (state.BuffersQueued == 0)
        {
            soundEffect->m_soundEffectSourceVoice->
                SubmitSourceBuffer(&soundEffect->m_audioBuffer);
        }
        else if (state.BuffersQueued < 2 && sound == CollisionEvent)
        {
            soundEffect->m_soundEffectSourceVoice->
                SubmitSourceBuffer(&soundEffect->m_audioBuffer);
        }

        // For the menu clicks, we want to stop the voice and replay the click
        // right away.
        // Note that stopping and then flushing could cause a glitch due to the
        // waveform not being at a zero-crossing, but due to the nature of the 
        // sound (fast and 'clicky'), we don't mind.
        if (state.BuffersQueued > 0 && sound == MenuChangeEvent)
        {
            soundEffect->m_soundEffectSourceVoice->Stop();
            soundEffect->m_soundEffectSourceVoice->FlushSourceBuffers();

            soundEffect->m_soundEffectSourceVoice->
                SubmitSourceBuffer(&soundEffect->m_audioBuffer);

            soundEffect->m_soundEffectSourceVoice->Start();
        }
    }

    m_soundEffects[sound].m_soundEffectStarted = true;
}

Untuk suara selain bergulir, metode Audio::P laySoundEffect memanggil IXAudio2SourceVoice::GetState untuk menentukan jumlah buffer yang diputar suara sumber. Ini memanggil IXAudio2SourceVoice::SubmitSourceBuffer untuk menambahkan data audio untuk suara ke antrean input suara jika tidak ada buffer yang aktif. Metode Audio::P laySoundEffect juga memungkinkan suara tabrakan diputar dua kali secara berurutan. Ini terjadi, misalnya, ketika marmer bertabrakan dengan sudut labirin.

Seperti yang sudah dijelaskan, kelas Audio menggunakan bendera XAUDIO2_LOOP_INFINITE saat menginisialisasi suara untuk peristiwa bergulir. Suara memulai pemutaran yang diulang saat pertama kali Audio::P laySoundEffect dipanggil untuk peristiwa ini. Untuk menyederhanakan logika pemutaran untuk suara bergulir, Marble Maze mematikan suara alih-alih menghentikannya. Ketika marmer berubah kecepatan, Marble Maze mengubah nada dan volume suara untuk memberikan efek yang lebih realistis. Berikut ini menunjukkan bagaimana metode MarbleMazeMain::Update memperbarui nada dan volume marmer saat kecepatannya berubah dan cara mematikan suara dengan mengatur volumenya ke nol saat marmer berhenti.

// Play the roll sound only if the marble is actually rolling.
if (ci.isRollingOnFloor && volume > 0)
{
    if (!m_audio.IsSoundEffectStarted(RollingEvent))
    {
        m_audio.PlaySoundEffect(RollingEvent);
    }

    // Update the volume and pitch by the velocity.
    m_audio.SetSoundEffectVolume(RollingEvent, volume);
    m_audio.SetSoundEffectPitch(RollingEvent, pitch);

    // The rolling sound has at most 8000Hz sounds, so we linearly
    // ramp up the low-pass filter the faster we go.
    // We also reduce the Q-value of the filter, starting with a
    // relatively broad cutoff and get progressively tighter.
    m_audio.SetSoundEffectFilter(
        RollingEvent,
        600.0f + 8000.0f * volume,
        XAUDIO2_MAX_FILTER_ONEOVERQ - volume*volume
        );
}
else
{
    m_audio.SetSoundEffectVolume(RollingEvent, 0);
}

Bereaksi terhadap menangguhkan dan melanjutkan peristiwa

Struktur aplikasi Marble Maze menjelaskan bagaimana Marble Maze mendukung penangguhan dan resume. Ketika permainan ditangguhkan, permainan menjeda audio. Ketika permainan dilanjutkan, permainan melanjutkan audio yang ditinggalkannya. Kami melakukannya untuk mengikuti praktik terbaik untuk tidak menggunakan sumber daya ketika Anda tahu bahwa sumber daya tersebut tidak diperlukan.

Metode Audio::SuspendAudio dipanggil ketika game ditangguhkan. Metode ini memanggil metode IXAudio2::StopEngine untuk menghentikan semua audio. Meskipun IXAudio2::StopEngine segera menghentikan semua output audio, IXAudio2 mempertahankan grafik audio dan parameter efeknya (misalnya, efek gaung yang diterapkan saat marmer memantul).

// Uses the IXAudio2::StopEngine method to stop all audio immediately.  
// It leaves the audio graph untouched, which preserves all effect parameters   
// and effect histories (like reverb effects) voice states, pending buffers,  
// cursor positions and so on. 
// When the engines are restarted, the resulting audio will sound as if it had  
// never been stopped except for the period of silence. 
void Audio::SuspendAudio()
{
    if (m_engineExperiencedCriticalError)
    {
        return;
    }

    if (m_isAudioStarted)
    {
        m_musicEngine->StopEngine();
        m_soundEffectEngine->StopEngine();
    }

    m_isAudioStarted = false;
}

Metode Audio::ResumeAudio dipanggil ketika game dilanjutkan. Metode ini menggunakan metode IXAudio2::StartEngine untuk menghidupkan ulang audio. Karena panggilan ke IXAudio2::StopEngine mempertahankan grafik audio dan parameter efeknya, output audio dilanjutkan di tempat terakhirnya.

// Restarts the audio streams. A call to this method must match a previous call
// to SuspendAudio. This method causes audio to continue where it left off.
// If there is a problem with the restart, the m_engineExperiencedCriticalError
// flag is set. The next call to Render will recreate all the resources and
// reset the audio pipeline.
void Audio::ResumeAudio()
{
    if (m_engineExperiencedCriticalError)
    {
        return;
    }

    HRESULT hr = m_musicEngine->StartEngine();
    HRESULT hr2 = m_soundEffectEngine->StartEngine();

    if (FAILED(hr) || FAILED(hr2))
    {
        m_engineExperiencedCriticalError = true;
    }
}

Menangani perubahan headphone dan perangkat

Marble Maze menggunakan panggilan balik mesin untuk menangani kegagalan mesin XAudio2, seperti saat perangkat audio berubah. Kemungkinan penyebab perubahan perangkat adalah ketika pengguna game terhubung atau memutuskan sambungan headphone. Kami menyarankan agar Anda menerapkan panggilan balik mesin yang menangani perubahan perangkat. Jika tidak, game anda akan berhenti memainkan suara ketika pengguna mencolokkan atau melepas headphone, sampai game dihidupkan ulang.

Audio.h mendefinisikan kelas AudioEngineCallbacks . Kelas ini mengimplementasikan antarmuka IXAudio2EngineCallback .

class AudioEngineCallbacks: public IXAudio2EngineCallback
{
private:
    Audio* m_audio;

public :
    AudioEngineCallbacks(){};
    void Initialize(Audio* audio);

    // Called by XAudio2 just before an audio processing pass begins.
    void _stdcall OnProcessingPassStart(){};

    // Called just after an audio processing pass ends.
    void  _stdcall OnProcessingPassEnd(){};

    // Called when a critical system error causes XAudio2
    // to be closed and restarted. The error code is given in Error.
    void  _stdcall OnCriticalError(HRESULT Error);
};

Antarmuka IXAudio2EngineCallback memungkinkan kode Anda diberi tahu saat peristiwa pemrosesan audio terjadi dan ketika mesin mengalami kesalahan kritis. Untuk mendaftar panggilan balik, Marble Maze memanggil metode IXAudio2::RegisterForCallbacks di Audio::CreateResources, setelah membuat objek IXAudio2 untuk mesin musik.

m_musicEngineCallback.Initialize(this);
m_musicEngine->RegisterForCallbacks(&m_musicEngineCallback);

Marble Maze tidak memerlukan pemberitahuan saat pemrosesan audio dimulai atau berakhir. Oleh karena itu, ini mengimplementasikan metode IXAudio2EngineCallback::OnProcessingPassStart dan IXAudio2EngineCallback::OnProcessingPassEnd untuk tidak melakukan apa-apa. Untuk metode IXAudio2EngineCallback::OnCriticalError , Marble Maze memanggil metode SetEngineExperiencedCriticalError , yang mengatur bendera m_engineExperiencedCriticalError .

// Audio.cpp

// Called when a critical system error causes XAudio2 
// to be closed and restarted. The error code is given in Error. 
void  _stdcall AudioEngineCallbacks::OnCriticalError(HRESULT Error)
{
    m_audio->SetEngineExperiencedCriticalError();
}
// Audio.h (Audio class)

// This flag can be used to tell when the audio system 
// is experiencing critical errors.
// XAudio2 gives a critical error when the user unplugs
// the headphones and a new speaker configuration is generated.
void SetEngineExperiencedCriticalError()
{
    m_engineExperiencedCriticalError = true;
}

Ketika terjadi kesalahan kritis, pemrosesan audio berhenti dan semua panggilan tambahan ke XAudio2 gagal. Untuk memulihkan dari situasi ini, Anda harus merilis instans XAudio2 dan membuat yang baru. Metode Audio::Render , yang dipanggil dari perulangan game setiap bingkai, pertama-tama memeriksa bendera m_engineExperiencedCriticalError . Jika bendera ini diatur, bendera akan menghapus bendera, merilis instans XAudio2 saat ini, menginisialisasi sumber daya, lalu memulai musik latar belakang.

if (m_engineExperiencedCriticalError)
{
    m_engineExperiencedCriticalError = false;
    ReleaseResources();
    Initialize();
    CreateResources();
    Start();
    if (m_engineExperiencedCriticalError)
    {
        return;
    }
}

Marble Maze juga menggunakan bendera m_engineExperiencedCriticalError untuk melindungi dari panggilan ke XAudio2 saat tidak ada perangkat audio yang tersedia. Misalnya, metode MarbleMazeMain::Update tidak memproses audio untuk peristiwa bergulir atau tabrakan saat bendera ini diatur. Aplikasi ini mencoba memperbaiki mesin audio setiap bingkai jika diperlukan; namun, bendera m_engineExperiencedCriticalError mungkin selalu diatur jika komputer tidak memiliki perangkat audio atau headphone dilepas dan tidak ada perangkat audio lain yang tersedia.

Perhatian

Sebagai aturan, jangan melakukan operasi pemblokiran dalam isi panggilan balik mesin. Melakukannya dapat menyebabkan masalah performa. Marble Maze menetapkan bendera dalam panggilan balik OnCriticalError dan kemudian menangani kesalahan selama fase pemrosesan audio reguler. Untuk informasi selengkapnya tentang panggilan balik XAudio2, lihat Panggilan Balik XAudio2.

Kesimpulan

Yang membungkus sampel permainan Marble Maze! Meskipun ini adalah permainan yang relatif sederhana, ini berisi banyak bagian penting yang masuk ke permainan UWP DirectX apa pun, dan merupakan contoh yang baik untuk diikuti ketika membuat game Anda sendiri.

Sekarang setelah Anda selesai mengikuti, coba tinkering dengan kode sumber dan lihat apa yang terjadi. Atau lihat Membuat game UWP sederhana dengan DirectX, sampel game UWP DirectX lainnya.

Siap untuk melaju lebih jauh dengan DirectX? Kemudian lihat panduan kami di pemrograman DirectX.

Jika Anda tertarik dengan pengembangan game di UWP secara umum, lihat dokumentasi di pemrograman Game.