SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
SDL/audio/xaudio2.c
1 #define COBJMACROS
2 #include "audio.h"
3 #include <Windows.h>
4 #ifdef REDIST_XAUDIO
5 #include <xaudio2redist.h>
6 #else
7 #include <xaudio2.h>
8 #endif
9 #include <initguid.h>
10 #include <Mmdeviceapi.h>
11
12 // This is a hack, but Windows itself is a hack so I don't care
13 #define DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
14 DEFINE_GUID(CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
15
16 #define DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
17 DEFINE_GUID(IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
18
19 DEFINE_CLSID(MMDeviceEnumerator, bcde0395, e52f, 467c, 8e, 3d, c4, 57, 92, 91, 69, 2e);
20 DEFINE_IID(IMMDeviceEnumerator, a95664d2, 9614, 4f35, a7, 46, de, 8d, b6, 36, 17, e6);
21
22
23 static unsigned audio_frequency = 48000;
24 static IXAudio2 *xaudio2 = NULL;
25 static IXAudio2MasteringVoice *master_voice = NULL;
26 static IXAudio2SourceVoice *source_voice = NULL;
27 static bool playing = false;
28 static GB_sample_t sample_pool[0x2000];
29 static unsigned pos = 0;
30
31 #define BATCH_SIZE 256
32
33 static WAVEFORMATEX wave_format = {
34 .wFormatTag = WAVE_FORMAT_PCM,
35 .nChannels = 2,
36 .nBlockAlign = 4,
37 .wBitsPerSample = 16,
38 .cbSize = 0
39 };
40
41 static bool _audio_is_playing(void)
42 {
43 return playing;
44 }
45
46 static void _audio_clear_queue(void)
47 {
48 pos = 0;
49 IXAudio2SourceVoice_FlushSourceBuffers(source_voice);
50 }
51
52 static void _audio_set_paused(bool paused)
53 {
54 if (paused) {
55 playing = false;
56 IXAudio2SourceVoice_Stop(source_voice, 0, XAUDIO2_COMMIT_NOW);
57 _audio_clear_queue();
58 }
59 else {
60 playing = true;
61 IXAudio2SourceVoice_Start(source_voice, 0, XAUDIO2_COMMIT_NOW);
62 }
63
64 }
65
66
67 #define _DEFINE_PROPERTYKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) static const PROPERTYKEY name = { { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }, pid }
68 _DEFINE_PROPERTYKEY(_PKEY_AudioEngine_DeviceFormat, 0xf19f064d, 0x82c, 0x4e27, 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, 0);
69
70
71 static void update_frequency(void)
72 {
73 HRESULT hr;
74 IMMDevice *device = NULL;
75 IMMDeviceEnumerator *enumerator = NULL;
76 IPropertyStore *store = NULL;
77 PWAVEFORMATEX deviceFormatProperties;
78 PROPVARIANT prop;
79
80 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (LPVOID *)&enumerator);
81 if (FAILED(hr)) return;
82
83 // get default audio endpoint
84
85 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, eRender, eMultimedia, &device);
86 if (FAILED(hr)) return;
87
88 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &store);
89 if (FAILED(hr)) return;
90
91 hr = IPropertyStore_GetValue(store, &_PKEY_AudioEngine_DeviceFormat, &prop);
92 if (FAILED(hr)) return;
93
94 deviceFormatProperties = (PWAVEFORMATEX)prop.blob.pBlobData;
95 audio_frequency = deviceFormatProperties->nSamplesPerSec;
96 if (audio_frequency < 8000 || audio_frequency > 192000) {
97 // Bogus value, revert to 48KHz
98 audio_frequency = 48000;
99 }
100 }
101
102 static unsigned _audio_get_frequency(void)
103 {
104 return audio_frequency;
105 }
106
107 static size_t _audio_get_queue_length(void)
108 {
109 static XAUDIO2_VOICE_STATE state;
110 IXAudio2SourceVoice_GetState(source_voice, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
111
112 return state.BuffersQueued * BATCH_SIZE + (pos & (BATCH_SIZE - 1));
113 }
114
115 static void _audio_queue_sample(GB_sample_t *sample)
116 {
117 if (!playing) return;
118
119 static XAUDIO2_BUFFER buffer = {.AudioBytes = sizeof(*sample) * BATCH_SIZE, };
120 sample_pool[pos] = *sample;
121 buffer.pAudioData = (void *)&sample_pool[pos & ~(BATCH_SIZE - 1)];
122 pos++;
123 pos &= 0x1fff;
124 if ((pos & (BATCH_SIZE - 1)) == 0) {
125 IXAudio2SourceVoice_SubmitSourceBuffer(source_voice, &buffer, NULL);
126 }
127 }
128
129 static bool _audio_init(void)
130 {
131 HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
132 if (FAILED(hr)) {
133 fprintf(stderr, "CoInitializeEx failed: %lx\n", hr);
134 return false;
135 }
136
137 hr = XAudio2Create(&xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
138 if (FAILED(hr)) {
139 fprintf(stderr, "XAudio2Create failed: %lx\n", hr);
140 return false;
141 }
142
143 update_frequency();
144
145 hr = IXAudio2_CreateMasteringVoice(xaudio2, &master_voice,
146 2, // 2 channels
147 audio_frequency,
148 0, // Flags
149 0, // Device index
150 NULL, // Effect chain
151 AudioCategory_GameMedia // Category
152 );
153 if (FAILED(hr)) {
154 fprintf(stderr, "CreateMasteringVoice failed: %lx\n", hr);
155 return false;
156 }
157
158 wave_format.nSamplesPerSec = audio_frequency;
159 wave_format.nAvgBytesPerSec = audio_frequency * 4;
160 hr = IXAudio2_CreateSourceVoice(xaudio2, &source_voice, &wave_format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, NULL, NULL);
161
162 if (FAILED(hr)) {
163 fprintf(stderr, "CreateSourceVoice failed: %lx\n", hr);
164 return false;
165 }
166
167 return true;
168 }
169
170 static void _audio_deinit(void)
171 {
172 _audio_set_paused(true);
173 }
174
175 GB_AUDIO_DRIVER(XAudio2);
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.