|
|
|
@@ -3739,6 +3739,7 @@ example, ALSA, which is specific to Linux, will not be included in the Windows b
|
|
|
|
|
| OpenSL|ES | ma_device_backend_opensl | Android (API level 16+) |
|
|
|
|
|
| Web Audio | ma_device_backend_webaudio | Web (via Emscripten) |
|
|
|
|
|
| Dreamcast | ma_device_backend_dreamcast | KallistiOS |
|
|
|
|
|
| XAudio | ma_device_backend_xaudio | NXDK |
|
|
|
|
|
| Null | ma_device_backend_null | Cross Platform (not used on Web) |
|
|
|
|
|
+-------------+------------------------------+--------------------------------------------------------+
|
|
|
|
|
|
|
|
|
@@ -3862,6 +3863,26 @@ disabling threading at compile time you will also reduce memory usage of the `ma
|
|
|
|
|
When running in single threaded mode you need to manually step the device periodically with
|
|
|
|
|
`ma_device_step()`, which you would normally do each frame.
|
|
|
|
|
|
|
|
|
|
15.7. XAudio (Original Xbox)
|
|
|
|
|
----------------------------
|
|
|
|
|
The XAudio backend is used for the original Xbox and uses NXDK.
|
|
|
|
|
|
|
|
|
|
You should only initialize a single `ma_device` object. Do not create more than one device thinking
|
|
|
|
|
that mixing will be done in hardware. Do your mixing in software.
|
|
|
|
|
|
|
|
|
|
The following device configuration is optimal:
|
|
|
|
|
|
|
|
|
|
Format: ma_foramt_s16
|
|
|
|
|
Channels: 2
|
|
|
|
|
Sample Rate: 48000
|
|
|
|
|
Period Size: 1024 to 16383
|
|
|
|
|
noFixedSizedCallback: True
|
|
|
|
|
|
|
|
|
|
If you follow this configuration you can be guaranteed an optimized passthrough pipeline. You can
|
|
|
|
|
use a period size smaller than 1024, but in my testing I found that it can result in glitches. You
|
|
|
|
|
are not forced to use this configuration, but if you deviate from it miniaudio will need to do data
|
|
|
|
|
conversion.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16. Optimization Tips
|
|
|
|
|
=====================
|
|
|
|
@@ -7426,6 +7447,28 @@ MA_API ma_device_backend_vtable* ma_dreamcast_get_vtable(void);
|
|
|
|
|
/* END miniaudio_dreamcast.h */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* BEG miniaudio_xaudio.h */
|
|
|
|
|
extern ma_device_backend_vtable* ma_device_backend_xaudio;
|
|
|
|
|
MA_API ma_device_backend_vtable* ma_xaudio_get_vtable(void);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
int _unused;
|
|
|
|
|
} ma_context_config_xaudio;
|
|
|
|
|
|
|
|
|
|
MA_API ma_context_config_xaudio ma_context_config_xaudio_init(void);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
int _unused;
|
|
|
|
|
} ma_device_config_xaudio;
|
|
|
|
|
|
|
|
|
|
MA_API ma_device_config_xaudio ma_device_config_xaudio_init(void);
|
|
|
|
|
/* END miniaudio_xaudio.h */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* BEG miniaudio_null.h */
|
|
|
|
|
typedef struct ma_context_config_null
|
|
|
|
|
{
|
|
|
|
@@ -7857,6 +7900,7 @@ struct ma_device_config
|
|
|
|
|
ma_device_config_opensl opensl;
|
|
|
|
|
ma_device_config_webaudio webaudio;
|
|
|
|
|
ma_device_config_dreamcast dreamcast;
|
|
|
|
|
ma_device_config_xaudio xaudio;
|
|
|
|
|
ma_device_config_null null_backend;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@@ -7997,6 +8041,7 @@ struct ma_context_config
|
|
|
|
|
ma_context_config_opensl opensl;
|
|
|
|
|
ma_context_config_webaudio webaudio;
|
|
|
|
|
ma_context_config_dreamcast dreamcast;
|
|
|
|
|
ma_context_config_xaudio xaudio;
|
|
|
|
|
ma_context_config_null null_backend;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@@ -19974,6 +20019,9 @@ BACKENDS
|
|
|
|
|
#if defined(MA_DREAMCAST)
|
|
|
|
|
#define MA_SUPPORT_DREAMCAST
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(MA_XBOX)
|
|
|
|
|
#define MA_SUPPORT_XAUDIO
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* All platforms should support custom backends. */
|
|
|
|
|
#define MA_SUPPORT_CUSTOM
|
|
|
|
@@ -20029,6 +20077,9 @@ BACKENDS
|
|
|
|
|
#if defined(MA_SUPPORT_DREAMCAST) && !defined(MA_NO_DREAMCAST) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DREAMCAST))
|
|
|
|
|
#define MA_HAS_DREAMCAST
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(MA_SUPPORT_XAUDIO) && !defined(MA_NO_XAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_XAUDIO))
|
|
|
|
|
#define MA_HAS_XAUDIO
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL))
|
|
|
|
|
#define MA_HAS_NULL
|
|
|
|
|
#endif
|
|
|
|
@@ -47980,6 +48031,411 @@ MA_API ma_device_config_dreamcast ma_device_config_dreamcast_init(int voiceChann
|
|
|
|
|
/* END miniaudio_dreamcast.c */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* BEG miniaudio_dreamcast.c */
|
|
|
|
|
#if defined(MA_HAS_XAUDIO)
|
|
|
|
|
#include <hal/audio.h>
|
|
|
|
|
#include <hal/debug.h>
|
|
|
|
|
|
|
|
|
|
typedef struct ma_context_state_xaudio
|
|
|
|
|
{
|
|
|
|
|
int _unused;
|
|
|
|
|
} ma_context_state_xaudio;
|
|
|
|
|
|
|
|
|
|
typedef struct ma_device_state_xaudio
|
|
|
|
|
{
|
|
|
|
|
void* pBuffer;
|
|
|
|
|
ma_uint16 subBufferIndex;
|
|
|
|
|
ma_uint16 subBufferCount;
|
|
|
|
|
ma_uint16 emptyBufferCount; /* How many sub-buffers are available for filling. */
|
|
|
|
|
ma_spinlock lock; /* Using a dumb lock for thread-safety for now, but this can be optimized by having subBufferIndex and emptyBufferCount encoded into a single 32-bit value and using it atomically. */
|
|
|
|
|
ma_timer timer;
|
|
|
|
|
double queuedAudioTime;
|
|
|
|
|
double periodTimeInSeconds;
|
|
|
|
|
} ma_device_state_xaudio;
|
|
|
|
|
|
|
|
|
|
static ma_context_state_xaudio* ma_context_get_backend_state__xaudio(ma_context* pContext)
|
|
|
|
|
{
|
|
|
|
|
return (ma_context_state_xaudio*)ma_context_get_backend_state(pContext);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ma_device_state_xaudio* ma_device_get_backend_state__xaudio(ma_device* pDevice)
|
|
|
|
|
{
|
|
|
|
|
return (ma_device_state_xaudio*)ma_device_get_backend_state(pDevice);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void ma_backend_info__xaudio(ma_device_backend_info* pBackendInfo)
|
|
|
|
|
{
|
|
|
|
|
pBackendInfo->pName = "XAudio";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ma_result ma_context_init__xaudio(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState)
|
|
|
|
|
{
|
|
|
|
|
ma_context_state_xaudio* pContextStateXAudio;
|
|
|
|
|
const ma_context_config_xaudio* pContextConfigXAudio = (ma_context_config_xaudio*)pContextBackendConfig;
|
|
|
|
|
|
|
|
|
|
/* The context config is not currently being used for this backend. */
|
|
|
|
|
(void)pContextConfigXAudio;
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
{
|
|
|
|
|
pContextStateXAudio = (ma_context_state_xaudio*)ma_calloc(sizeof(*pContextStateXAudio), ma_context_get_allocation_callbacks(pContext));
|
|
|
|
|
if (pContextStateXAudio == NULL) {
|
|
|
|
|
return MA_OUT_OF_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
{
|
|
|
|
|
(void)pContextStateXAudio;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return MA_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ma_context_uninit__xaudio(ma_context* pContext)
|
|
|
|
|
{
|
|
|
|
|
ma_context_state_xaudio* pContextStateXAudio = ma_context_get_backend_state__xaudio(pContext);
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
{
|
|
|
|
|
ma_free(pContextStateSDL, ma_context_get_allocation_callbacks(pContext));
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
{
|
|
|
|
|
(void)pContextStateXAudio;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ma_result ma_context_enumerate_devices__xaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pCallbackUserData)
|
|
|
|
|
{
|
|
|
|
|
ma_context_state_xaudio* pContextStateXAudio = ma_context_get_backend_state__xaudio(pContext);
|
|
|
|
|
ma_device_info deviceInfo;
|
|
|
|
|
|
|
|
|
|
(void)pContextStateXAudio;
|
|
|
|
|
|
|
|
|
|
/* We're only outputting a single default playback device. */
|
|
|
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
|
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
|
|
|
deviceInfo.id.custom.i = 0;
|
|
|
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "Default Playback Device", (size_t)-1);
|
|
|
|
|
|
|
|
|
|
/* Audio is always s16, stereo, 48000. */
|
|
|
|
|
ma_device_info_add_native_data_format(&deviceInfo, ma_format_s16, 2, 2, 48000, 48000);
|
|
|
|
|
|
|
|
|
|
return MA_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void* ma_device_get_sub_buffer__xaudio(ma_device* pDevice, ma_uint32 subBufferIndex)
|
|
|
|
|
{
|
|
|
|
|
ma_device_state_xaudio* pDeviceStateXAudio = ma_device_get_backend_state__xaudio(pDevice);
|
|
|
|
|
return ma_offset_ptr(pDeviceStateXAudio->pBuffer, pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels) * subBufferIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
static void ma_device_callback__xaudio(void* pUnused, void* pUserData)
|
|
|
|
|
{
|
|
|
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
|
|
|
ma_device_state_xaudio* pDeviceStateXAudio = ma_device_get_backend_state__xaudio(pDevice);
|
|
|
|
|
ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
|
|
|
|
|
|
|
|
(void)pUnused;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
I'm not sure how to handle the situation where we don't have any audio data to submit. Can I just
|
|
|
|
|
not call XAudioProvideSamples(), or should I submit a chunk of silence? What's making me question
|
|
|
|
|
this is that during initialization of the audio system, at no point are we configuring a period
|
|
|
|
|
size so how does the audio system know when the fire this callback? If I don't submit anything,
|
|
|
|
|
how frequently does this callback get fired to request more data?
|
|
|
|
|
*/
|
|
|
|
|
ma_spinlock_lock(&pDeviceStateXAudio->lock);
|
|
|
|
|
{
|
|
|
|
|
if (pDeviceStateXAudio->emptyBufferCount == 2) {
|
|
|
|
|
/* We don't have anything available. Submit silence. */
|
|
|
|
|
ma_int16 pSilence[2048];
|
|
|
|
|
unsigned short silenceFrameCount = (unsigned short)(ma_countof(pSilence) / pDevice->playback.internalChannels);
|
|
|
|
|
|
|
|
|
|
MA_ZERO_MEMORY(pSilence, sizeof(pSilence));
|
|
|
|
|
XAudioProvideSamples((void*)pSilence, silenceFrameCount, 0);
|
|
|
|
|
} else {
|
|
|
|
|
while (pDeviceStateXAudio->emptyBufferCount < pDeviceStateXAudio->subBufferCount) {
|
|
|
|
|
XAudioProvideSamples(ma_device_get_sub_buffer__xaudio(pDevice, pDeviceStateXAudio->subBufferIndex), (unsigned short)pDevice->playback.internalPeriodSizeInFrames, 0);
|
|
|
|
|
|
|
|
|
|
pDeviceStateXAudio->subBufferIndex = (pDeviceStateXAudio->subBufferIndex + 1) & 1;
|
|
|
|
|
pDeviceStateXAudio->emptyBufferCount += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ma_spinlock_unlock(&pDeviceStateXAudio->lock);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static ma_result ma_device_init__xaudio(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState)
|
|
|
|
|
{
|
|
|
|
|
ma_device_state_xaudio* pDeviceStateXAudio;
|
|
|
|
|
ma_device_config_xaudio* pDeviceConfigXAudio = (ma_device_config_xaudio*)pDeviceBackendConfig;
|
|
|
|
|
ma_context_state_xaudio* pContextStateXAudio = ma_context_get_backend_state__xaudio(ma_device_get_context(pDevice));
|
|
|
|
|
ma_device_type deviceType = ma_device_get_type(pDevice);
|
|
|
|
|
ma_result result;
|
|
|
|
|
int sampleSizeInBits;
|
|
|
|
|
ma_format format;
|
|
|
|
|
ma_uint32 channels;
|
|
|
|
|
ma_uint32 sampleRate;
|
|
|
|
|
ma_uint32 bufferSizeInFrames;
|
|
|
|
|
|
|
|
|
|
/* Only playback is supported. */
|
|
|
|
|
if (deviceType != ma_device_type_playback) {
|
|
|
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(void)pDescriptorCapture;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* We need to allocate our backend-specific data. */
|
|
|
|
|
pDeviceStateXAudio = (ma_device_state_xaudio*)ma_calloc(sizeof(*pDeviceStateXAudio), ma_device_get_allocation_callbacks(pDevice));
|
|
|
|
|
if (pDeviceStateXAudio == NULL) {
|
|
|
|
|
return MA_OUT_OF_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pDeviceStateXAudio->emptyBufferCount = 2; /* Both buffers are empty from the start. */
|
|
|
|
|
ma_timer_init(&pDeviceStateXAudio->timer);
|
|
|
|
|
|
|
|
|
|
/* Audio is always s16, stereo, 48000. */
|
|
|
|
|
format = ma_format_s16;
|
|
|
|
|
channels = 2;
|
|
|
|
|
sampleRate = 48000;
|
|
|
|
|
|
|
|
|
|
/* Calculate an appropriate buffer size. */
|
|
|
|
|
bufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, sampleRate);
|
|
|
|
|
|
|
|
|
|
/* TODO: Do we need to have a minimum buffer size? Need to experiment with this. */
|
|
|
|
|
if (bufferSizeInFrames < 1024) {
|
|
|
|
|
bufferSizeInFrames = 1024;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
XAudioProvideSamples() takes the size of the buffer in bytes, but that parameter is typed as
|
|
|
|
|
an unsigned 16-bit value. We need to make sure the size of the buffer does not exceed this.
|
|
|
|
|
*/
|
|
|
|
|
if (bufferSizeInFrames > 65535 / ma_get_bytes_per_frame(format, channels)) {
|
|
|
|
|
bufferSizeInFrames = 65535 / ma_get_bytes_per_frame(format, channels);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pDeviceStateXAudio->periodTimeInSeconds = bufferSizeInFrames / (double)sampleRate;
|
|
|
|
|
pDeviceStateXAudio->subBufferCount = 3;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Allocate two buffers for double buffering. The nxdk "xaudio" example uses MmAllocateContiguousMemoryEx(), but
|
|
|
|
|
I'm not sure if that's strictly required. It'd be nice if instead we could use miniaudio's allocation callbacks
|
|
|
|
|
and just allocate this buffer in the same allocation as the state struct, but the example explicitly using
|
|
|
|
|
MmAllocateContiguousMemoryEx() makes me feel a bit uneasy about that. Advice welcome.
|
|
|
|
|
*/
|
|
|
|
|
pDeviceStateXAudio->pBuffer = MmAllocateContiguousMemoryEx(bufferSizeInFrames * ma_get_bytes_per_frame(format, channels) * pDeviceStateXAudio->subBufferCount, 0, 0x03FFAFFF, 0, (PAGE_READWRITE | PAGE_WRITECOMBINE));
|
|
|
|
|
if (pDeviceStateXAudio->pBuffer == NULL) {
|
|
|
|
|
ma_free(pDeviceStateXAudio, ma_device_get_allocation_callbacks(pDevice));
|
|
|
|
|
return MA_OUT_OF_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
NOTE:
|
|
|
|
|
There is a bug in xemu that is resulting in the callback never getting fired. Since I would
|
|
|
|
|
like miniaudio to work on xemu I'm going to not use the callback technique. If this is fixed in
|
|
|
|
|
the future I may update this.
|
|
|
|
|
|
|
|
|
|
With the callback technique `XAudioProvideSamples()` would be called from the callback, but
|
|
|
|
|
we're instead going to manually keep track of a timer and call it from our step function.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* The audio system must be initialized before it'll be usable. */
|
|
|
|
|
XAudioInit((int)ma_get_bytes_per_sample(format) * 8, (int)channels, NULL, pDevice);
|
|
|
|
|
|
|
|
|
|
/* We're done. */
|
|
|
|
|
pDescriptorPlayback->format = format;
|
|
|
|
|
pDescriptorPlayback->channels = channels;
|
|
|
|
|
pDescriptorPlayback->sampleRate = sampleRate;
|
|
|
|
|
pDescriptorPlayback->periodSizeInFrames = bufferSizeInFrames;
|
|
|
|
|
pDescriptorPlayback->periodCount = pDeviceStateXAudio->subBufferCount;
|
|
|
|
|
|
|
|
|
|
*ppDeviceState = pDeviceStateXAudio;
|
|
|
|
|
|
|
|
|
|
return MA_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ma_device_uninit__xaudio(ma_device* pDevice)
|
|
|
|
|
{
|
|
|
|
|
ma_device_state_xaudio* pDeviceStateXAudio = ma_device_get_backend_state__xaudio(pDevice);
|
|
|
|
|
/*ma_context_state_xaudio* pContextStateXAudio = ma_context_get_backend_state__xaudio(ma_device_get_context(pDevice));*/
|
|
|
|
|
|
|
|
|
|
MmFreeContiguousMemory(pDeviceStateXAudio->pBuffer);
|
|
|
|
|
ma_free(pDeviceStateXAudio, ma_device_get_allocation_callbacks(pDevice));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ma_device_fill_sub_buffer__xaudio(ma_device* pDevice, ma_uint32 subBufferIndex)
|
|
|
|
|
{
|
|
|
|
|
/* Do the data callback. */
|
|
|
|
|
ma_device_handle_backend_data_callback(pDevice, ma_device_get_sub_buffer__xaudio(pDevice, subBufferIndex), NULL, pDevice->playback.internalPeriodSizeInFrames);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ma_result ma_device_start__xaudio(ma_device* pDevice)
|
|
|
|
|
{
|
|
|
|
|
ma_device_state_xaudio* pDeviceStateXAudio = ma_device_get_backend_state__xaudio(pDevice);
|
|
|
|
|
|
|
|
|
|
/* Clear everything out and reload our buffers. We need to submit data to XAudio before we start playing. */
|
|
|
|
|
#if 0
|
|
|
|
|
pDeviceStateXAudio->subBufferIndex = 0;
|
|
|
|
|
pDeviceStateXAudio->emptyBufferCount = 0;
|
|
|
|
|
ma_device_fill_sub_buffer__xaudio(pDevice, 0);
|
|
|
|
|
ma_device_fill_sub_buffer__xaudio(pDevice, 1);
|
|
|
|
|
ma_device_callback__xaudio(NULL, pDevice);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
XAudioPlay();
|
|
|
|
|
pDeviceStateXAudio->queuedAudioTime = ma_timer_get_time_in_seconds(&pDeviceStateXAudio->timer);
|
|
|
|
|
|
|
|
|
|
return MA_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ma_result ma_device_stop__xaudio(ma_device* pDevice)
|
|
|
|
|
{
|
|
|
|
|
ma_device_state_xaudio* pDeviceStateXAudio = ma_device_get_backend_state__xaudio(pDevice);
|
|
|
|
|
|
|
|
|
|
(void)pDeviceStateXAudio;
|
|
|
|
|
XAudioPause();
|
|
|
|
|
|
|
|
|
|
return MA_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ma_result ma_device_step__xaudio(ma_device* pDevice, ma_blocking_mode blockingMode)
|
|
|
|
|
{
|
|
|
|
|
ma_device_state_xaudio* pDeviceStateXAudio = ma_device_get_backend_state__xaudio(pDevice);
|
|
|
|
|
|
|
|
|
|
/* Keep looping until we have processed some data, or we're in non-blocking mode. */
|
|
|
|
|
for (;;) {
|
|
|
|
|
if (!ma_device_is_started(pDevice)) {
|
|
|
|
|
return MA_DEVICE_NOT_STARTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
{
|
|
|
|
|
ma_spinlock_lock(&pDeviceStateXAudio->lock);
|
|
|
|
|
{
|
|
|
|
|
if (pDeviceStateXAudio->emptyBufferCount > 0) {
|
|
|
|
|
ma_uint16 iEmptyBuffer;
|
|
|
|
|
ma_uint16 subBufferIndex;
|
|
|
|
|
|
|
|
|
|
if (pDeviceStateXAudio->emptyBufferCount == 2) {
|
|
|
|
|
subBufferIndex = pDeviceStateXAudio->subBufferIndex;
|
|
|
|
|
ma_device_fill_sub_buffer__xaudio(pDevice, subBufferIndex);
|
|
|
|
|
pDeviceStateXAudio->emptyBufferCount -= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MA_ASSERT(pDeviceStateXAudio->emptyBufferCount == 1);
|
|
|
|
|
|
|
|
|
|
subBufferIndex = (pDeviceStateXAudio->subBufferIndex + 1) & ~1;
|
|
|
|
|
ma_device_fill_sub_buffer__xaudio(pDevice, subBufferIndex);
|
|
|
|
|
pDeviceStateXAudio->emptyBufferCount -= 1;
|
|
|
|
|
|
|
|
|
|
ma_spinlock_unlock(&pDeviceStateXAudio->lock);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ma_spinlock_unlock(&pDeviceStateXAudio->lock);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
{
|
|
|
|
|
double currentTimeInSeconds;
|
|
|
|
|
double marginTimeInSeconds;
|
|
|
|
|
|
|
|
|
|
currentTimeInSeconds = ma_timer_get_time_in_seconds(&pDeviceStateXAudio->timer);
|
|
|
|
|
|
|
|
|
|
/* If we underflow reset the timer to help mitigate getting stuck in a permanent glitched state. */
|
|
|
|
|
if (currentTimeInSeconds > pDeviceStateXAudio->queuedAudioTime) {
|
|
|
|
|
pDeviceStateXAudio->queuedAudioTime = currentTimeInSeconds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The margin time is how much audio time we want queued ahead of the current time. */
|
|
|
|
|
marginTimeInSeconds = pDeviceStateXAudio->periodTimeInSeconds * (pDeviceStateXAudio->subBufferCount - 1);
|
|
|
|
|
|
|
|
|
|
if (pDeviceStateXAudio->queuedAudioTime - currentTimeInSeconds < marginTimeInSeconds) {
|
|
|
|
|
ma_device_fill_sub_buffer__xaudio(pDevice, pDeviceStateXAudio->subBufferIndex);
|
|
|
|
|
XAudioProvideSamples(ma_device_get_sub_buffer__xaudio(pDevice, pDeviceStateXAudio->subBufferIndex), (unsigned short)(pDevice->playback.internalPeriodSizeInFrames*ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)), 0);
|
|
|
|
|
|
|
|
|
|
//debugPrint("TESTING: %d %d\n", (int)(currentTimeInSeconds * 1000), (int)(pDeviceStateXAudio->queuedAudioTime * 1000));
|
|
|
|
|
pDeviceStateXAudio->subBufferIndex += 1;
|
|
|
|
|
if (pDeviceStateXAudio->subBufferIndex == pDeviceStateXAudio->subBufferCount) {
|
|
|
|
|
pDeviceStateXAudio->subBufferIndex = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pDeviceStateXAudio->queuedAudioTime += pDeviceStateXAudio->periodTimeInSeconds;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Getting here means there is no data to process. In non blocking mode we just bomb out. In blocking mode we wait a bit. */
|
|
|
|
|
if (blockingMode == MA_BLOCKING_MODE_NON_BLOCKING) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Getting here means we're in blocking mode. */
|
|
|
|
|
ma_sleep(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return MA_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ma_device_wakeup__xaudio(ma_device* pDevice)
|
|
|
|
|
{
|
|
|
|
|
/* Do nothing. */
|
|
|
|
|
(void)pDevice;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ma_device_backend_vtable ma_gDeviceBackendVTable_XAudio =
|
|
|
|
|
{
|
|
|
|
|
ma_backend_info__xaudio,
|
|
|
|
|
ma_context_init__xaudio,
|
|
|
|
|
ma_context_uninit__xaudio,
|
|
|
|
|
ma_context_enumerate_devices__xaudio,
|
|
|
|
|
ma_device_init__xaudio,
|
|
|
|
|
ma_device_uninit__xaudio,
|
|
|
|
|
ma_device_start__xaudio,
|
|
|
|
|
ma_device_stop__xaudio,
|
|
|
|
|
ma_device_step__xaudio,
|
|
|
|
|
ma_device_wakeup__xaudio
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ma_device_backend_vtable* ma_device_backend_xaudio = &ma_gDeviceBackendVTable_XAudio;
|
|
|
|
|
#else
|
|
|
|
|
ma_device_backend_vtable* ma_device_backend_xaudio = NULL;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
MA_API ma_device_backend_vtable* ma_xaudio_get_vtable(void)
|
|
|
|
|
{
|
|
|
|
|
return ma_device_backend_xaudio;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MA_API ma_context_config_xaudio ma_context_config_xaudio_init(void)
|
|
|
|
|
{
|
|
|
|
|
ma_context_config_xaudio config;
|
|
|
|
|
|
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MA_API ma_device_config_xaudio ma_device_config_xaudio_init(void)
|
|
|
|
|
{
|
|
|
|
|
ma_device_config_xaudio config;
|
|
|
|
|
|
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
/* END miniaudio_dreamcast.c */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MA_API void ma_get_device_backend_info(ma_device_backend_vtable* pBackendVTable, ma_device_backend_info* pBackendInfo)
|
|
|
|
|
{
|
|
|
|
@@ -49011,6 +49467,9 @@ static const void* ma_context_config_find_backend_config(const ma_context_config
|
|
|
|
|
if (pVTable == ma_device_backend_dreamcast) {
|
|
|
|
|
return &pConfig->dreamcast;
|
|
|
|
|
}
|
|
|
|
|
if (pVTable == ma_device_backend_xaudio) {
|
|
|
|
|
return &pConfig->xaudio;
|
|
|
|
|
}
|
|
|
|
|
if (pVTable == ma_device_backend_null) {
|
|
|
|
|
return &pConfig->null_backend;
|
|
|
|
|
}
|
|
|
|
@@ -49041,6 +49500,7 @@ MA_API ma_uint32 ma_get_stock_device_backends(ma_device_backend_config* pBackend
|
|
|
|
|
if (backendsCap > count) { pBackends[count++] = ma_device_backend_config_init(ma_device_backend_opensl, NULL); }
|
|
|
|
|
if (backendsCap > count) { pBackends[count++] = ma_device_backend_config_init(ma_device_backend_webaudio, NULL); }
|
|
|
|
|
if (backendsCap > count) { pBackends[count++] = ma_device_backend_config_init(ma_device_backend_dreamcast, NULL); }
|
|
|
|
|
if (backendsCap > count) { pBackends[count++] = ma_device_backend_config_init(ma_device_backend_xaudio, NULL); }
|
|
|
|
|
if (backendsCap > count) { pBackends[count++] = ma_device_backend_config_init(ma_device_backend_null, NULL); }
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
@@ -49509,6 +49969,9 @@ static const void* ma_device_config_find_backend_config(const ma_device_config*
|
|
|
|
|
if (pVTable == ma_device_backend_dreamcast) {
|
|
|
|
|
return &pConfig->dreamcast;
|
|
|
|
|
}
|
|
|
|
|
if (pVTable == ma_device_backend_xaudio) {
|
|
|
|
|
return &pConfig->xaudio;
|
|
|
|
|
}
|
|
|
|
|
if (pVTable == ma_device_backend_null) {
|
|
|
|
|
return &pConfig->null_backend;
|
|
|
|
|
}
|
|
|
|
|