Add XAudio backend.

This adds support for the original Xbox via NXDK.
This commit is contained in:
David Reid
2026-02-27 10:58:24 +10:00
parent b5eb987b86
commit 30e2ca2b46
6 changed files with 555 additions and 0 deletions
+6
View File
@@ -37,6 +37,12 @@
/tests/dreamcast/romdisk.img /tests/dreamcast/romdisk.img
/tests/dreamcast/romdisk.o /tests/dreamcast/romdisk.o
/tests/dreamcast/romdisk/test.mp3 /tests/dreamcast/romdisk/test.mp3
/tests/xbox/bin/
/tests/xbox/*.d
/tests/xbox/*.obj
/tests/xbox/*.iso
/tests/xbox/*.exe
/tests/xbox/test.mp3
/tests/*.c /tests/*.c
/tests/*.cpp /tests/*.cpp
/website/docs/ /website/docs/
+2
View File
@@ -180,6 +180,7 @@ Supported Platforms
- Raspberry Pi - Raspberry Pi
- Emscripten / HTML5 - Emscripten / HTML5
- Dreamcast (via KallistiOS) - Dreamcast (via KallistiOS)
- Original Xbox (via NXDK)
miniaudio should compile clean on other platforms, but it will not include any support for playback or capture miniaudio should compile clean on other platforms, but it will not include any support for playback or capture
by default. To support that, you would need to implement a custom backend. You can do this without needing to by default. To support that, you would need to implement a custom backend. You can do this without needing to
@@ -202,6 +203,7 @@ Backends
- OpenSL|ES (Android only) - OpenSL|ES (Android only)
- Web Audio (Emscripten) - Web Audio (Emscripten)
- Dreamcast (via KallistiOS) - Dreamcast (via KallistiOS)
- XAudio (Original Xbox via NXDK)
- Null (Silence) - Null (Silence)
- Custom - Custom
+463
View File
@@ -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+) | | OpenSL|ES | ma_device_backend_opensl | Android (API level 16+) |
| Web Audio | ma_device_backend_webaudio | Web (via Emscripten) | | Web Audio | ma_device_backend_webaudio | Web (via Emscripten) |
| Dreamcast | ma_device_backend_dreamcast | KallistiOS | | Dreamcast | ma_device_backend_dreamcast | KallistiOS |
| XAudio | ma_device_backend_xaudio | NXDK |
| Null | ma_device_backend_null | Cross Platform (not used on Web) | | 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 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. `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 16. Optimization Tips
===================== =====================
@@ -7426,6 +7447,28 @@ MA_API ma_device_backend_vtable* ma_dreamcast_get_vtable(void);
/* END miniaudio_dreamcast.h */ /* 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 */ /* BEG miniaudio_null.h */
typedef struct ma_context_config_null typedef struct ma_context_config_null
{ {
@@ -7857,6 +7900,7 @@ struct ma_device_config
ma_device_config_opensl opensl; ma_device_config_opensl opensl;
ma_device_config_webaudio webaudio; ma_device_config_webaudio webaudio;
ma_device_config_dreamcast dreamcast; ma_device_config_dreamcast dreamcast;
ma_device_config_xaudio xaudio;
ma_device_config_null null_backend; ma_device_config_null null_backend;
}; };
@@ -7997,6 +8041,7 @@ struct ma_context_config
ma_context_config_opensl opensl; ma_context_config_opensl opensl;
ma_context_config_webaudio webaudio; ma_context_config_webaudio webaudio;
ma_context_config_dreamcast dreamcast; ma_context_config_dreamcast dreamcast;
ma_context_config_xaudio xaudio;
ma_context_config_null null_backend; ma_context_config_null null_backend;
}; };
@@ -19974,6 +20019,9 @@ BACKENDS
#if defined(MA_DREAMCAST) #if defined(MA_DREAMCAST)
#define MA_SUPPORT_DREAMCAST #define MA_SUPPORT_DREAMCAST
#endif #endif
#if defined(MA_XBOX)
#define MA_SUPPORT_XAUDIO
#endif
/* All platforms should support custom backends. */ /* All platforms should support custom backends. */
#define MA_SUPPORT_CUSTOM #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)) #if defined(MA_SUPPORT_DREAMCAST) && !defined(MA_NO_DREAMCAST) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DREAMCAST))
#define MA_HAS_DREAMCAST #define MA_HAS_DREAMCAST
#endif #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)) #if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL))
#define MA_HAS_NULL #define MA_HAS_NULL
#endif #endif
@@ -47980,6 +48031,411 @@ MA_API ma_device_config_dreamcast ma_device_config_dreamcast_init(int voiceChann
/* END miniaudio_dreamcast.c */ /* 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) 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) { if (pVTable == ma_device_backend_dreamcast) {
return &pConfig->dreamcast; return &pConfig->dreamcast;
} }
if (pVTable == ma_device_backend_xaudio) {
return &pConfig->xaudio;
}
if (pVTable == ma_device_backend_null) { if (pVTable == ma_device_backend_null) {
return &pConfig->null_backend; 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_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_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_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); } if (backendsCap > count) { pBackends[count++] = ma_device_backend_config_init(ma_device_backend_null, NULL); }
return count; 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) { if (pVTable == ma_device_backend_dreamcast) {
return &pConfig->dreamcast; return &pConfig->dreamcast;
} }
if (pVTable == ma_device_backend_xaudio) {
return &pConfig->xaudio;
}
if (pVTable == ma_device_backend_null) { if (pVTable == ma_device_backend_null) {
return &pConfig->null_backend; return &pConfig->null_backend;
} }
+13
View File
@@ -0,0 +1,13 @@
XBE_TITLE = miniaudio_xbox
GEN_XISO = $(XBE_TITLE).iso
SRCS = $(CURDIR)/miniaudio_xbox.c
NXDK_DIR ?= /opt/toolchains/nxdk
all:
include $(NXDK_DIR)/Makefile
TARGET += $(OUTPUT_DIR)/test.mp3
$(GEN_XISO): $(OUTPUT_DIR)/test.mp3
$(OUTPUT_DIR)/test.mp3: $(CURDIR)/test.mp3 $(OUTPUT_DIR)
$(VE)cp '$<' '$@'
+1
View File
@@ -0,0 +1 @@
Add a "test.mp3" to this folder.
+70
View File
@@ -0,0 +1,70 @@
#include "../../miniaudio.c"
#include <hal/debug.h>
#include <hal/video.h>
static void data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
{
ma_data_source* pDataSource = (ma_data_source*)ma_device_get_user_data(pDevice);
ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, NULL);
(void)pFramesIn;
(void)pDevice;
}
int main()
{
ma_result result;
ma_device_config deviceConfig;
ma_device device;
ma_waveform waveform;
ma_decoder decoder;
XVideoSetMode(640, 480, 32, REFRESH_DEFAULT);
deviceConfig = ma_device_config_init(ma_device_type_playback);
/*deviceConfig.threadingMode = MA_THREADING_MODE_SINGLE_THREADED;*/
deviceConfig.playback.format = ma_format_s16;
deviceConfig.playback.channels = 2;
deviceConfig.sampleRate = 0;
deviceConfig.dataCallback = data_callback;
deviceConfig.pUserData = &decoder;
deviceConfig.periodSizeInFrames = 1024;
result = ma_device_init(NULL, &deviceConfig, &device);
if (result != MA_SUCCESS) {
debugPrint("Failed to initialize device.\n");
return 1;
}
/* Initialize the waveform before starting the device. */
{
ma_waveform_config waveformConfig = ma_waveform_config_init(device.playback.format, device.playback.channels, device.sampleRate, ma_waveform_type_sine, 0.1f, 400);
ma_waveform_init(&waveformConfig, &waveform);
}
/* Decoder. */
{
ma_decoder_config decoderConfig = ma_decoder_config_init(device.playback.format, device.playback.channels, device.sampleRate);
ma_decoder_init_file("D:\\test.mp3", &decoderConfig, &decoder);
}
result = ma_device_start(&device);
if (result != MA_SUCCESS) {
debugPrint("Failed to start device.\n");
return 1;
}
for (;;) {
/*debugPrint("Looping...\n");*/
if (ma_device_get_threading_mode(&device) == MA_THREADING_MODE_SINGLE_THREADED) {
ma_device_step(&device, MA_BLOCKING_MODE_NON_BLOCKING);
} else {
ma_sleep(500);
}
}
ma_waveform_uninit(&waveform);
ma_device_uninit(&device);
return 0;
}