diff --git a/examples/Makefile b/examples/Makefile index a4f565836..0252f59b7 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -396,7 +396,6 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif ifeq ($(PLATFORM),PLATFORM_NX) LDLIBS = -lraylib -lEGL -lGLESv2 -lglapi -ldrm_nouveau -lnx -lm - LDLIBS += `$(PREFIX)pkg-config --libs sdl2` endif # Define source code object files required diff --git a/projects/switch-nx/Makefile b/projects/switch-nx/Makefile index b06faff91..0af5706e0 100644 --- a/projects/switch-nx/Makefile +++ b/projects/switch-nx/Makefile @@ -64,8 +64,6 @@ ASFLAGS := -g $(ARCH) LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) LIBS := -lraylib -lEGL -lGLESv2 -lglapi -ldrm_nouveau -lnx -lm -# remove the next line if you dont need audio -LIBS += `$(PREFIX)pkg-config --libs sdl2` #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/src/external/miniaudio_sdl.h b/src/external/miniaudio_sdl.h deleted file mode 100644 index 5bbf0ea86..000000000 --- a/src/external/miniaudio_sdl.h +++ /dev/null @@ -1,233 +0,0 @@ -/****************************************************************************** - -SDL Backend - -******************************************************************************/ - -struct -{ - ma_uint32 deviceID; -} device_sdl; - -struct -{ - ma_handle hSDL; - ma_proc SDL_InitSubSystem; - ma_proc SDL_QuitSubSystem; - ma_proc SDL_GetNumAudioDevices; - ma_proc SDL_GetAudioDeviceName; - ma_proc SDL_CloseAudioDevice; - ma_proc SDL_OpenAudioDevice; - ma_proc SDL_PauseAudioDevice; -} context_sdl; - - -#define MA_SDL_INIT_AUDIO 0x00000010 -#define MA_AUDIO_U8 0x0008 -#define MA_AUDIO_S16 0x8010 -#define MA_AUDIO_S32 0x8020 -#define MA_AUDIO_F32 0x8120 -#define MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001 -#define MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002 -#define MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004 -#define MA_SDL_AUDIO_ALLOW_ANY_CHANGE (MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE | MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE) - -#include -#include - -typedef int (* MA_PFN_SDL_InitSubSystem)(ma_uint32 flags); -typedef void (* MA_PFN_SDL_QuitSubSystem)(ma_uint32 flags); -typedef int (* MA_PFN_SDL_GetNumAudioDevices)(int iscapture); -typedef const char* (* MA_PFN_SDL_GetAudioDeviceName)(int index, int iscapture); -typedef void (* MA_PFN_SDL_CloseAudioDevice)(SDL_AudioDeviceID dev); -typedef SDL_AudioDeviceID (* MA_PFN_SDL_OpenAudioDevice)(const char* device, int iscapture, const SDL_AudioSpec* desired, SDL_AudioSpec* obtained, int allowed_changes); -typedef void (* MA_PFN_SDL_PauseAudioDevice)(SDL_AudioDeviceID dev, int pause_on); - -static SDL_AudioFormat ma_format_to_sdl(ma_format format) -{ - switch (format) - { - case ma_format_unknown: return 0; - case ma_format_u8: return MA_AUDIO_U8; - case ma_format_s16: return MA_AUDIO_S16; - case ma_format_s24: return MA_AUDIO_S32; // Closest match. - case ma_format_s32: return MA_AUDIO_S32; - case ma_format_f32: return MA_AUDIO_F32; - default: return 0; - } -} - -static ma_format ma_format_from_sdl(SDL_AudioFormat format) -{ - switch (format) - { - case MA_AUDIO_U8: return ma_format_u8; - case MA_AUDIO_S16: return ma_format_s16; - case MA_AUDIO_S32: return ma_format_s32; - case MA_AUDIO_F32: return ma_format_f32; - default: return ma_format_unknown; - } -} - -static ma_result ma_context_get_device_info__sdl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(deviceType == ma_device_type_playback); - - pDeviceInfo->isDefault = MA_TRUE; - - SDL_AudioSpec desiredSpec, obtainedSpec; - MA_ZERO_OBJECT(&desiredSpec); - - SDL_AudioDeviceID tempDeviceID = ((MA_PFN_SDL_OpenAudioDevice)context_sdl.SDL_OpenAudioDevice)(NULL, 0, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE); - ((MA_PFN_SDL_CloseAudioDevice)context_sdl.SDL_CloseAudioDevice)(tempDeviceID); - - ma_format format = ma_format_from_sdl(obtainedSpec.format); - if (format == ma_format_unknown) { - format = ma_format_f32; - } - - pDeviceInfo->nativeDataFormatCount = 0; - ma_device_info_add_native_data_format(pDeviceInfo, format, obtainedSpec.channels, obtainedSpec.freq, 0); - - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__sdl(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ((MA_PFN_SDL_CloseAudioDevice)context_sdl.SDL_CloseAudioDevice)(device_sdl.deviceID); - - return MA_SUCCESS; -} - - -static void ma_audio_callback__sdl(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_uint32 bytesPerFrame = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - ma_uint32 frameCount = (ma_uint32)bufferSizeInBytes / bytesPerFrame; - - ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, frameCount); -} - -static ma_result ma_device_init__sdl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pConfig->deviceType == ma_device_type_playback); - - if (pDescriptorPlayback->sampleRate == 0) { - pDescriptorPlayback->sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - pDescriptorPlayback->periodSizeInFrames = pDescriptorPlayback->periodCount * ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - - /* SDL wants the buffer size to be a power of 2 for some reason. */ - if (pDescriptorPlayback->periodSizeInFrames > 32768) { - pDescriptorPlayback->periodSizeInFrames = 32768; - } else { - pDescriptorPlayback->periodSizeInFrames = ma_next_power_of_2(pDescriptorPlayback->periodSizeInFrames); - } - - MA_ASSERT(pDescriptorPlayback->periodSizeInFrames <= 32768); - - /* We now have enough information to set up the device. */ - SDL_AudioSpec desiredSpec, obtainedSpec; - MA_ZERO_OBJECT(&desiredSpec); - desiredSpec.freq = (int)pDescriptorPlayback->sampleRate; - desiredSpec.format = ma_format_to_sdl(pDescriptorPlayback->format); - desiredSpec.channels = (ma_uint8)pDescriptorPlayback->channels; - desiredSpec.samples = (ma_uint16)pDescriptorPlayback->periodSizeInFrames; - desiredSpec.callback = ma_audio_callback__sdl; - desiredSpec.userdata = pDevice; - - /* We'll fall back to f32 if we don't have an appropriate mapping between SDL and miniaudio. */ - if (desiredSpec.format == ma_format_unknown) { - desiredSpec.format = MA_AUDIO_F32; - } - - device_sdl.deviceID = ((MA_PFN_SDL_OpenAudioDevice)context_sdl.SDL_OpenAudioDevice)(NULL, 0, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE); - - /* The descriptor needs to be updated with our actual settings. */ - pDescriptorPlayback->format = ma_format_from_sdl(obtainedSpec.format); - pDescriptorPlayback->channels = obtainedSpec.channels; - pDescriptorPlayback->sampleRate = (ma_uint32)obtainedSpec.freq; - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - pDescriptorPlayback->periodSizeInFrames = obtainedSpec.samples; - pDescriptorPlayback->periodCount = 1; /* SDL doesn't use the notion of period counts, so just set to 1. */ - - return MA_SUCCESS; -} - -static ma_result ma_device_start__sdl(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ((MA_PFN_SDL_PauseAudioDevice)context_sdl.SDL_PauseAudioDevice)(device_sdl.deviceID, 0); - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__sdl(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ((MA_PFN_SDL_PauseAudioDevice)context_sdl.SDL_PauseAudioDevice)(device_sdl.deviceID, 1); - - ma_device__set_state(pDevice, ma_device_state_stopped); - ma_stop_proc onStop = pDevice->onStop; - if (onStop) { - onStop(pDevice); - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__sdl(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ((MA_PFN_SDL_QuitSubSystem)context_sdl.SDL_QuitSubSystem)(MA_SDL_INIT_AUDIO); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__sdl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; /* Unused. */ - - context_sdl.SDL_InitSubSystem = (ma_proc)SDL_InitSubSystem; - context_sdl.SDL_QuitSubSystem = (ma_proc)SDL_QuitSubSystem; - context_sdl.SDL_GetNumAudioDevices = (ma_proc)SDL_GetNumAudioDevices; - context_sdl.SDL_GetAudioDeviceName = (ma_proc)SDL_GetAudioDeviceName; - context_sdl.SDL_CloseAudioDevice = (ma_proc)SDL_CloseAudioDevice; - context_sdl.SDL_OpenAudioDevice = (ma_proc)SDL_OpenAudioDevice; - context_sdl.SDL_PauseAudioDevice = (ma_proc)SDL_PauseAudioDevice; - - int resultSDL = ((MA_PFN_SDL_InitSubSystem)context_sdl.SDL_InitSubSystem)(MA_SDL_INIT_AUDIO); - if (resultSDL != 0) { - return MA_ERROR; - } - - pCallbacks->onContextInit = ma_context_init__sdl; - pCallbacks->onContextUninit = ma_context_uninit__sdl; - pCallbacks->onContextEnumerateDevices = NULL; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sdl; - pCallbacks->onDeviceInit = ma_device_init__sdl; - pCallbacks->onDeviceUninit = ma_device_uninit__sdl; - pCallbacks->onDeviceStart = ma_device_start__sdl; - pCallbacks->onDeviceStop = ma_device_stop__sdl; - pCallbacks->onDeviceRead = NULL; - pCallbacks->onDeviceWrite = NULL; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} diff --git a/src/raudio.c b/src/raudio.c index 75367b072..a2f436fe0 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -164,6 +164,7 @@ typedef struct tagBITMAPINFOHEADER { #define MA_FREE RL_FREE #if defined(PLATFORM_NX) + #define MA_NO_RUNTIME_LINKING #define MA_ENABLE_ONLY_SPECIFIC_BACKENDS #define MA_ENABLE_CUSTOM #endif @@ -176,7 +177,7 @@ typedef struct tagBITMAPINFOHEADER { //#define MA_DEBUG_OUTPUT #include "external/miniaudio.h" // Audio device initialization and management #if defined(PLATFORM_NX) - #include "external/miniaudio_sdl.h" + #include // Custom audio device for switch #endif #undef PlaySound // Win32 API: windows.h > mmsystem.h defines PlaySound macro @@ -432,7 +433,7 @@ void InitAudioDevice(void) ma_backend backends[] = { ma_backend_custom }; - ctxConfig.custom.onContextInit = ma_context_init__sdl; + ctxConfig.custom.onContextInit = ma_context_init__audren; ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &ctxConfig, &AUDIO.System.context); #else ma_result result = ma_context_init(NULL, 0, &ctxConfig, &AUDIO.System.context); diff --git a/src/switch-nx/miniaudio_audren.h b/src/switch-nx/miniaudio_audren.h new file mode 100644 index 000000000..4968a3004 --- /dev/null +++ b/src/switch-nx/miniaudio_audren.h @@ -0,0 +1,266 @@ +/****************************************************************************** + +Audren Backend + +******************************************************************************/ +#ifndef miniaudio_audren_h +#define miniaudio_audren_h + +#include +#include +#include +#include + +#define LIBNX_AUDREN_BUFFER_COUNT 5 + +static const AudioRendererConfig audio_renderer_config = +{ + .output_rate = AudioRendererOutputRate_48kHz, + .num_voices = 24, + .num_effects = 0, + .num_sinks = 1, + .num_mix_objs = 1, + .num_mix_buffers = 2, +}; + +static const int sample_rate = 48000; +static const int num_channels = 2; +static const uint8_t sink_channels[] = { 0, 1 }; + +typedef struct +{ + AudioDriver drv; + char* mempool; + AudioDriverWaveBuf wavebufs[LIBNX_AUDREN_BUFFER_COUNT]; + AudioDriverWaveBuf* current_wavebuf; + char* current_pool_ptr; + size_t current_size; + size_t buffer_size; + size_t samples; + Mutex update_lock; +} libnx_audren_t; + +static ssize_t libnx_audren_audio_get_free_wavebuf_idx(libnx_audren_t* aud) +{ + unsigned i; + + for (i = 0; i < LIBNX_AUDREN_BUFFER_COUNT; i++) + { + if (aud->wavebufs[i].state == AudioDriverWaveBufState_Free || aud->wavebufs[i].state == AudioDriverWaveBufState_Done) + return i; + } + + return -1; +} + +static size_t libnx_audren_audio_append(libnx_audren_t* aud, const char *buf, size_t size) +{ + void *dstbuf = NULL; + ssize_t free_idx = -1; + + if (!aud->current_wavebuf) + { + free_idx = libnx_audren_audio_get_free_wavebuf_idx(aud); + if (free_idx == -1) + return 0; + + aud->current_wavebuf = &aud->wavebufs[free_idx]; + aud->current_pool_ptr = aud->mempool + (free_idx * aud->buffer_size); + aud->current_size = 0; + } + + if (size > aud->buffer_size - aud->current_size) + size = aud->buffer_size - aud->current_size; + + dstbuf = aud->current_pool_ptr + aud->current_size; + memcpy(dstbuf, buf, size); + armDCacheFlush(dstbuf, size); + + aud->current_size += size; + + if (aud->current_size == aud->buffer_size) + { + audrvVoiceAddWaveBuf(&aud->drv, 0, aud->current_wavebuf); + + mutexLock(&aud->update_lock); + audrvUpdate(&aud->drv); + mutexUnlock(&aud->update_lock); + + if (!audrvVoiceIsPlaying(&aud->drv, 0)) + { + audrvVoiceStart(&aud->drv, 0); + } + + aud->current_wavebuf = NULL; + } + + return size; +} + +static ma_result ma_device_write__audren(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pFrames != NULL); + MA_ASSERT(pDevice->pContext->pUserData != NULL); + libnx_audren_t *aud = (libnx_audren_t*)pDevice->pContext->pUserData; + + if (pFramesWritten != NULL) + *pFramesWritten = 0; + + if (ma_device_get_state(pDevice) != ma_device_state_started) + return MA_DEVICE_NOT_INITIALIZED; + + char *buf = (char*)pFrames; + size_t size = frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + size_t written = 0; + + while (written < size) + { + written += libnx_audren_audio_append(aud, buf + written, size - written); + if (written != size) + { + mutexLock(&aud->update_lock); + audrvUpdate(&aud->drv); + mutexUnlock(&aud->update_lock); + audrenWaitFrame(); + } + } + + if (written == 0) + return MA_IO_ERROR; + + *pFramesWritten = frameCount; + + return MA_SUCCESS; +} + +static ma_result ma_device_init__audren(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDevice->pContext->pUserData != NULL); + libnx_audren_t *aud = (libnx_audren_t*)pDevice->pContext->pUserData; + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + pDescriptorPlayback->format = ma_format_s16; + pDescriptorPlayback->channels = num_channels; + pDescriptorPlayback->sampleRate = sample_rate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); + pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); + + aud->buffer_size = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); + aud->samples = (aud->buffer_size / num_channels / sizeof(int16_t)); + aud->current_size = 0; + + size_t mempool_size = (aud->buffer_size * LIBNX_AUDREN_BUFFER_COUNT + (AUDREN_MEMPOOL_ALIGNMENT-1)) &~ (AUDREN_MEMPOOL_ALIGNMENT-1); + aud->mempool = memalign(AUDREN_MEMPOOL_ALIGNMENT, mempool_size); + + audrenInitialize(&audio_renderer_config); + audrvCreate(&aud->drv, &audio_renderer_config, num_channels); + + unsigned i; + for(i = 0; i < LIBNX_AUDREN_BUFFER_COUNT; i++) + { + aud->wavebufs[i].data_raw = aud->mempool; + aud->wavebufs[i].size = mempool_size; + aud->wavebufs[i].start_sample_offset = i * aud->samples; + aud->wavebufs[i].end_sample_offset = aud->wavebufs[i].start_sample_offset + aud->samples; + } + aud->current_wavebuf = NULL; + + int mpid = audrvMemPoolAdd(&aud->drv, aud->mempool, mempool_size); + audrvMemPoolAttach(&aud->drv, mpid); + audrvDeviceSinkAdd(&aud->drv, AUDREN_DEFAULT_DEVICE_NAME, num_channels, sink_channels); + audrenStartAudioRenderer(); + audrvVoiceInit(&aud->drv, 0, num_channels, PcmFormat_Int16, sample_rate); + audrvVoiceSetDestinationMix(&aud->drv, 0, AUDREN_FINAL_MIX_ID); + + unsigned j; + for(i = 0; i < num_channels; i++) + for(j = 0; j < num_channels; j++) + audrvVoiceSetMixFactor(&aud->drv, 0, i == j ? 1.0f : 0.0f, i, j); + + mutexInit(&aud->update_lock); + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__audren(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(deviceType == ma_device_type_playback); + + pDeviceInfo->isDefault = MA_TRUE; + pDeviceInfo->nativeDataFormatCount = 0; + + ma_device_info_add_native_data_format(pDeviceInfo, ma_format_s16, num_channels, sample_rate, 0); + + return MA_SUCCESS; +} + +static ma_result ma_device_start__audren(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDevice->pContext->pUserData != NULL); + libnx_audren_t *aud = (libnx_audren_t*)pDevice->pContext->pUserData; + + audrvVoiceStart(&aud->drv, 0); + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__audren(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDevice->pContext->pUserData != NULL); + libnx_audren_t *aud = (libnx_audren_t*)pDevice->pContext->pUserData; + + audrvVoiceStop(&aud->drv, 0); + + return MA_SUCCESS; +} + +static ma_result ma_device_uninit__audren(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDevice->pContext->pUserData != NULL); + libnx_audren_t *aud = (libnx_audren_t*)pDevice->pContext->pUserData; + + audrvVoiceStop(&aud->drv, 0); + audrvClose(&aud->drv); + audrenExit(); + + if (aud->mempool) + free(aud->mempool); + free(aud); + + return MA_SUCCESS; +} + +static ma_result ma_context_init__audren(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + MA_ASSERT(pContext != NULL); + + pContext->pUserData = (libnx_audren_t*)calloc(1, sizeof(libnx_audren_t)); + + (void)pConfig; /* Unused. */ + + pCallbacks->onContextInit = ma_context_init__audren; + pCallbacks->onContextUninit = NULL; + pCallbacks->onContextEnumerateDevices = NULL; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audren; + pCallbacks->onDeviceInit = ma_device_init__audren; + pCallbacks->onDeviceUninit = ma_device_uninit__audren; + pCallbacks->onDeviceStart = ma_device_start__audren; + pCallbacks->onDeviceStop = ma_device_stop__audren; + pCallbacks->onDeviceRead = NULL; + pCallbacks->onDeviceWrite = ma_device_write__audren; + pCallbacks->onDeviceDataLoop = NULL; + + return MA_SUCCESS; +} + +#endif