diff --git a/.gitignore b/.gitignore index b2c31501..f4e483a6 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ /tests/xbox/*.iso /tests/xbox/*.exe /tests/xbox/test.mp3 +/tests/vita/build/ /tests/*.c /tests/*.cpp /website/docs/ diff --git a/CMakeLists.txt b/CMakeLists.txt index fe3f12ba..86f571e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ option(MINIAUDIO_NO_OPENSL "Disable the OpenSL|ES backend" option(MINIAUDIO_NO_WEBAUDIO "Disable the Web Audio backend" OFF) option(MINIAUDIO_NO_DREAMCAST "Disable the Dreamcast backend" OFF) option(MINIAUDIO_NO_XAUDIO "Disable the XAudio (OG Xbox) backend" OFF) +option(MINIAUDIO_NO_VITA "Disable the Vita backend" OFF) option(MINIAUDIO_NO_NULL "Disable the null backend" OFF) option(MINIAUDIO_NO_SDL2 "Disable the SDL2 backend" OFF) option(MINIAUDIO_ENABLE_ONLY_SPECIFIC_BACKENDS "Only enable specific backends. Backends can be enabled with MINIAUDIO_ENABLE_[BACKEND]." OFF) @@ -57,6 +58,7 @@ option(MINIAUDIO_ENABLE_OPENSL "Enable the OpenSL|ES backend" option(MINIAUDIO_ENABLE_WEBAUDIO "Enable the Web Audio backend" OFF) option(MINIAUDIO_ENABLE_DREAMCAST "Enable the Dreamcast backend" OFF) option(MINIAUDIO_ENABLE_XAUDIO "Enable the XAudio (OG Xbox) backend" OFF) +option(MINIAUDIO_ENABLE_VITA "Enable the Vita backend" OFF) option(MINIAUDIO_ENABLE_NULL "Enable the null backend" OFF) option(MINIAUDIO_ENABLE_SDL2 "Enable the SDL2 backend" OFF) option(MINIAUDIO_NO_DECODING "Disable decoding APIs" OFF) @@ -117,6 +119,7 @@ normalize_backend_enabled_option(OPENSL) normalize_backend_enabled_option(WEBAUDIO) normalize_backend_enabled_option(DREAMCAST) normalize_backend_enabled_option(XAUDIO) +normalize_backend_enabled_option(VITA) normalize_backend_enabled_option(NULL) normalize_backend_enabled_option(SDL2) @@ -300,6 +303,9 @@ endif() if(MINIAUDIO_NO_XAUDIO) list(APPEND COMPILE_DEFINES MA_NO_XAUDIO) endif() +if(MINIAUDIO_NO_VITA) + list(APPEND COMPILE_DEFINES MA_NO_VITA) +endif() if(MINIAUDIO_NO_NULL) list(APPEND COMPILE_DEFINES MA_NO_NULL) endif() @@ -357,6 +363,9 @@ if(MINIAUDIO_ENABLE_ONLY_SPECIFIC_BACKENDS) if(MINIAUDIO_ENABLE_XAUDIO) list(APPEND COMPILE_DEFINES MA_ENABLE_XAUDIO) endif() + if(MINIAUDIO_ENABLE_VITA) + list(APPEND COMPILE_DEFINES MA_ENABLE_VITA) + endif() if(MINIAUDIO_ENABLE_NULL) list(APPEND COMPILE_DEFINES MA_ENABLE_NULL) endif() diff --git a/miniaudio.h b/miniaudio.h index 5df03bca..cf7f7c81 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -3740,6 +3740,7 @@ example, ALSA, which is specific to Linux, will not be included in the Windows b | Web Audio | ma_device_backend_webaudio | Web (via Emscripten) | | Dreamcast | ma_device_backend_dreamcast | KallistiOS | | XAudio | ma_device_backend_xaudio | NXDK | + | PS Vita | ma_device_backend_vita | Vita SDK | | Null | ma_device_backend_null | Cross Platform (not used on Web) | +-------------+------------------------------+--------------------------------------------------------+ @@ -3883,6 +3884,29 @@ use a period size smaller than 1024, but in my testing I found that it can resul are not forced to use this configuration, but if you deviate from it miniaudio will need to do data conversion. +15.8. PlayStation Vita +---------------------- +The Vita backend uses Vita SDK. + +The following device configuration is optimal: + + Format: ma_foramt_s16 + Channels: 2 + Sample Rate: 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100 or 48000 + Period Size: Multiple of 64, between 64 and 65472 + noFixedSizedCallback: True + +If you follow this configuration you can be guaranteed an optimized passthrough pipeline, except +when using a sample rate other than 48000 on the Main port (see below). + +The BGM port (SCE_AUDIO_OUT_PORT_TYPE_BGM) is used by default. You can use the Main port by +configuring it in the device config: + + deviceConfig.vita.portType = MA_VITA_PORT_TYPE_MAIN; // Or MA_VITA_PORT_TYPE_BGM + +When using MA_VITA_PORT_TYPE_MAIN, the native sample rate will always be 48000 and using anything +else will invoke miniaudio's resampler which will have overhead. + 16. Optimization Tips ===================== @@ -7482,6 +7506,34 @@ MA_API ma_device_config_xaudio ma_device_config_xaudio_init(void); /* END miniaudio_xaudio.h */ +/* BEG miniaudio_vita.h */ +extern ma_device_backend_vtable* ma_device_backend_vita; +MA_API ma_device_backend_vtable* ma_vita_get_vtable(void); + + +typedef struct +{ + int _unused; +} ma_context_config_vita; + +MA_API ma_context_config_vita ma_context_config_vita_init(void); + + +typedef enum +{ + MA_VITA_PORT_TYPE_BGM = 0, + MA_VITA_PORT_TYPE_MAIN = 1 +} ma_vita_port_type; + +typedef struct +{ + ma_vita_port_type portType; +} ma_device_config_vita; + +MA_API ma_device_config_vita ma_device_config_vita_init(void); +/* END miniaudio_vita.h */ + + /* BEG miniaudio_null.h */ typedef struct ma_context_config_null { @@ -7914,6 +7966,7 @@ struct ma_device_config ma_device_config_webaudio webaudio; ma_device_config_dreamcast dreamcast; ma_device_config_xaudio xaudio; + ma_device_config_vita vita; ma_device_config_null null_backend; }; @@ -8055,6 +8108,7 @@ struct ma_context_config ma_context_config_webaudio webaudio; ma_context_config_dreamcast dreamcast; ma_context_config_xaudio xaudio; + ma_context_config_vita vita; ma_context_config_null null_backend; }; @@ -20338,6 +20392,9 @@ BACKENDS #if defined(MA_XBOX) #define MA_SUPPORT_XAUDIO #endif +#if defined(MA_VITA) + #define MA_SUPPORT_VITA +#endif /* All platforms should support custom backends. */ #define MA_SUPPORT_CUSTOM @@ -20396,6 +20453,9 @@ BACKENDS #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_VITA) && !defined(MA_NO_VITA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_VITA)) + #define MA_HAS_VITA +#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 @@ -48766,6 +48826,347 @@ MA_API ma_device_config_xaudio ma_device_config_xaudio_init(void) /* END miniaudio_xaudio.c */ +/* BEG miniaudio_vita.c */ +#if defined(MA_HAS_VITA) +#include + +typedef struct ma_context_state_vita +{ + int _unused; +} ma_context_state_vita; + +typedef struct ma_device_state_vita +{ + int port; + ma_bool32 isRunning; /* Used for tracking whether or not the background thread should be terminated. */ + ma_thread thread; /* Need to call sceAudioOutOutput() from a separate thread because it is always blocking. */ + ma_uint32 subBufferIndex; + ma_uint32 validSubBufferCount; + void* pSubBuffers; +} ma_device_state_vita; + + +static ma_context_state_vita* ma_context_get_backend_state__vita(ma_context* pContext) +{ + return (ma_context_state_vita*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_vita* ma_device_get_backend_state__vita(ma_device* pDevice) +{ + return (ma_device_state_vita*)ma_device_get_backend_state(pDevice); +} + + +static void ma_backend_info__vita(ma_device_backend_info* pBackendInfo) +{ + pBackendInfo->pName = "PlayStation Vita"; +} + +static ma_result ma_context_init__vita(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_vita* pContextStateVita; + const ma_context_config_vita* pContextConfigVita = (ma_context_config_vita*)pContextBackendConfig; + + pContextStateVita = (ma_context_state_vita*)ma_calloc(sizeof(*pContextStateVita), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateVita == NULL) { + return MA_OUT_OF_MEMORY; + } + + (void)pContextConfigVita; + (void)pContext; + + *ppContextState = pContextStateVita; + + return MA_SUCCESS; +} + +static void ma_context_uninit__vita(ma_context* pContext) +{ + ma_context_state_vita* pContextStateVita = ma_context_get_backend_state__vita(pContext); + ma_free(pContextStateVita, ma_context_get_allocation_callbacks(pContext)); +} + +static ma_result ma_context_enumerate_devices__vita(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pCallbackUserData) +{ + ma_context_state_vita* pContextStateVita = ma_context_get_backend_state__vita(pContext); + ma_device_info deviceInfo; + ma_device_enumeration_result enumerationResult; + + (void)pContextStateVita; + + /* Playback. */ + 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); + + /* Only s16 and mono/stereo is natively supported. */ + ma_device_info_add_native_data_format(&deviceInfo, ma_format_s16, 1, 2, 8000, 48000); + + enumerationResult = callback(ma_device_type_playback, &deviceInfo, pCallbackUserData); + if (enumerationResult == MA_DEVICE_ENUMERATION_ABORT) { + return MA_SUCCESS; + } + + return MA_SUCCESS; +} + +static void* ma_device_get_sub_buffer__vita(ma_device* pDevice, ma_uint32 subBufferIndex) +{ + ma_device_state_vita* pDeviceStateVita = ma_device_get_backend_state__vita(pDevice); + return ma_offset_ptr(pDeviceStateVita->pSubBuffers, pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels) * subBufferIndex); +} + +static ma_thread_result MA_THREADCALL ma_device_audio_thread__vita(void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + ma_device_state_vita* pDeviceStateVita = ma_device_get_backend_state__vita(pDevice); + + while (ma_atomic_load_explicit_32(&pDeviceStateVita->isRunning, ma_atomic_memory_order_relaxed)) { + if (ma_atomic_load_explicit_32(&pDeviceStateVita->validSubBufferCount, ma_atomic_memory_order_acquire) > 0) { + ma_uint32 subBufferIndex = ma_atomic_load_explicit_32(&pDeviceStateVita->subBufferIndex, ma_atomic_memory_order_relaxed); + + sceAudioOutOutput(pDeviceStateVita->port, ma_device_get_sub_buffer__vita(pDevice, subBufferIndex)); + + ma_atomic_store_explicit_32(&pDeviceStateVita->subBufferIndex, (subBufferIndex + 1) & 1, ma_atomic_memory_order_relaxed); + ma_atomic_fetch_sub_explicit_32(&pDeviceStateVita->validSubBufferCount, 1, ma_atomic_memory_order_release); + } else { + /* Just a dumb sleep so we don't peg the CPU while the device is not stopped. */ + ma_sleep(10); + } + } + + return (ma_thread_result)0; +} + +static ma_result ma_device_init__vita(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) +{ + ma_result result; + ma_device_state_vita* pDeviceStateVita; + ma_device_config_vita* pDeviceConfigVita = (ma_device_config_vita*)pDeviceBackendConfig; + ma_context_state_vita* pContextStateVita = ma_context_get_backend_state__vita(ma_device_get_context(pDevice)); + ma_device_config_vita defaultConfig; + ma_device_type deviceType = ma_device_get_type(pDevice); + ma_log* pLog = ma_device_get_log(pDevice); + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 periodSizeInFrames; + SceAudioOutPortType portType; + + (void)pContextStateVita; + (void)pDescriptorCapture; + + /* Use a default config if one was not provided. This is not mandated by miniaudio, but it's good practice. */ + if (pDeviceConfigVita == NULL) { + defaultConfig = ma_device_config_vita_init(); + pDeviceConfigVita = &defaultConfig; + } + + /* Return an error for any unsupported device types. */ + if (deviceType != ma_device_type_playback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + pDeviceStateVita = (ma_device_state_vita*)ma_calloc(sizeof(*pDeviceStateVita), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateVita == NULL) { + return MA_OUT_OF_MEMORY; + } + + /* Port type. */ + portType = SCE_AUDIO_OUT_PORT_TYPE_BGM; + if (pDeviceConfigVita->portType == MA_VITA_PORT_TYPE_MAIN) { + portType = SCE_AUDIO_OUT_PORT_TYPE_MAIN; + } + + /* Format is always s16. */ + format = ma_format_s16; + + /* Channels is always mono or stereo. Default to stereo. */ + channels = pDescriptorPlayback->channels; + if (channels != 1 && channels != 2) { + channels = 1; + } + + /* Sample rate. */ + sampleRate = 0; + + if (pDescriptorPlayback->sampleRate != 0) { + if (portType == SCE_AUDIO_OUT_PORT_TYPE_BGM) { + ma_uint32 bgmSampleRates[] = {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}; + ma_uint32 iSampleRate; + + for (iSampleRate = 0; iSampleRate < ma_countof(bgmSampleRates); iSampleRate += 1) { + if (pDescriptorPlayback->sampleRate == bgmSampleRates[iSampleRate]) { + sampleRate = bgmSampleRates[iSampleRate]; + } + } + } + } + + if (sampleRate == 0) { + sampleRate = 48000; + } + + + /* The period size must be a multiple of 64. */ + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, sampleRate); + periodSizeInFrames = ma_clamp((periodSizeInFrames + 63) & ~63, SCE_AUDIO_MIN_LEN, SCE_AUDIO_MAX_LEN); + + pDeviceStateVita->pSubBuffers = ma_malloc(periodSizeInFrames * ma_get_bytes_per_frame(format, channels) * 2, ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateVita->pSubBuffers == NULL) { + return MA_OUT_OF_MEMORY; + } + + pDeviceStateVita->port = sceAudioOutOpenPort(portType, (int)periodSizeInFrames, (int)sampleRate, (channels == 1) ? SCE_AUDIO_OUT_MODE_MONO : SCE_AUDIO_OUT_MODE_STEREO); + if (pDeviceStateVita->port < 0) { + ma_free(pDeviceStateVita->pSubBuffers, ma_device_get_allocation_callbacks(pDevice)); + ma_log_postf(pLog, MA_LOG_LEVEL_ERROR, "[Vita] Failed to open port."); + return MA_ERROR; + } + + /* Make sure the running status is set appropriately so the audio thread doesn't immediately terminate itself. */ + ma_atomic_store_explicit_32(&pDeviceStateVita->isRunning, 1, ma_atomic_memory_order_relaxed); + + /* + Because sceAudioOutOutput() is always blocking, in order to do non-blocking processing, we'll need to call + it on a separate thread. Make sure the thread is created last. + */ + result = ma_thread_create(&pDeviceStateVita->thread, ma_thread_priority_default, 0, ma_device_audio_thread__vita, pDevice, ma_device_get_allocation_callbacks(pDevice)); + if (result != MA_SUCCESS) { + sceAudioOutReleasePort(pDeviceStateVita->port); + ma_free(pDeviceStateVita->pSubBuffers, ma_device_get_allocation_callbacks(pDevice)); + ma_log_postf(pLog, MA_LOG_LEVEL_ERROR, "[Vita] Failed to create audio thread."); + return MA_ERROR; + } + + /* Update the descriptor with the actual internal settings. */ + pDescriptorPlayback->format = format; + pDescriptorPlayback->channels = channels; + pDescriptorPlayback->sampleRate = sampleRate; + pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; + pDescriptorPlayback->periodCount = 2; + + *ppDeviceState = pDeviceStateVita; + + return MA_SUCCESS; +} + +static void ma_device_uninit__vita(ma_device* pDevice) +{ + ma_device_state_vita* pDeviceStateVita = ma_device_get_backend_state__vita(pDevice); + + /* Kill the thread first. */ + ma_atomic_store_explicit_32(&pDeviceStateVita->isRunning, 0, ma_atomic_memory_order_relaxed); + ma_thread_wait(&pDeviceStateVita->thread); + + sceAudioOutReleasePort(pDeviceStateVita->port); + ma_free(pDeviceStateVita->pSubBuffers, ma_device_get_allocation_callbacks(pDevice)); + ma_free(pDeviceStateVita, ma_device_get_allocation_callbacks(pDevice)); +} + +static ma_result ma_device_start__vita(ma_device* pDevice) +{ + ma_device_state_vita* pDeviceStateVita = ma_device_get_backend_state__vita(pDevice); + + ma_atomic_store_explicit_32(&pDeviceStateVita->subBufferIndex, 0, ma_atomic_memory_order_relaxed); + ma_atomic_store_explicit_32(&pDeviceStateVita->validSubBufferCount, 0, ma_atomic_memory_order_relaxed); + + /* Don't actually do anything here. We start by simply outputting data. Stopping is just not outputting data. */ + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__vita(ma_device* pDevice) +{ + ma_device_state_vita* pDeviceStateVita = ma_device_get_backend_state__vita(pDevice); + + /* Wait for the buffers to be drained. */ + while (ma_atomic_load_explicit_32(&pDeviceStateVita->validSubBufferCount, ma_atomic_memory_order_relaxed) > 0) { + ma_sleep(1); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_step__vita(ma_device* pDevice, ma_blocking_mode blockingMode) +{ + ma_device_state_vita* pDeviceStateVita = ma_device_get_backend_state__vita(pDevice); + + for (;;) { + if (!ma_device_is_started(pDevice)) { + return MA_DEVICE_NOT_STARTED; + } + + if (ma_atomic_load_explicit_32(&pDeviceStateVita->validSubBufferCount, ma_atomic_memory_order_acquire) < 2) { + ma_uint32 subBufferIndex = ma_atomic_load_explicit_32(&pDeviceStateVita->subBufferIndex, ma_atomic_memory_order_relaxed); + ma_device_handle_backend_data_callback(pDevice, ma_device_get_sub_buffer__vita(pDevice, subBufferIndex), NULL, pDevice->playback.internalPeriodSizeInFrames); + ma_atomic_fetch_add_explicit_32(&pDeviceStateVita->validSubBufferCount, 1, ma_atomic_memory_order_release); + + return MA_SUCCESS; + } + + /* Getting here means there was no data to process. */ + if (blockingMode == MA_BLOCKING_MODE_NON_BLOCKING) { + return MA_SUCCESS; + } + + /* Getting here means there was no data to process and we're running in blocking mode. Sleep for a bit and keep trying. */ + ma_sleep(1); + } +} + +static void ma_device_wakeup__vita(ma_device* pDevice) +{ + /* Nothing to do here. */ + (void)pDevice; +} + +static ma_device_backend_vtable ma_gDeviceBackendVTable_Vita = +{ + ma_backend_info__vita, + ma_context_init__vita, + ma_context_uninit__vita, + ma_context_enumerate_devices__vita, + ma_device_init__vita, + ma_device_uninit__vita, + ma_device_start__vita, + ma_device_stop__vita, + ma_device_step__vita, + ma_device_wakeup__vita +}; + +ma_device_backend_vtable* ma_device_backend_vita = &ma_gDeviceBackendVTable_Vita; +#else +ma_device_backend_vtable* ma_device_backend_vita = NULL; +#endif /* MA_HAS_VITA */ + +MA_API ma_device_backend_vtable* ma_vita_get_vtable(void) +{ + return ma_device_backend_vita; +} + +MA_API ma_context_config_vita ma_context_config_vita_init(void) +{ + ma_context_config_vita config; + + MA_ZERO_OBJECT(&config); + + return config; +} + +MA_API ma_device_config_vita ma_device_config_vita_init(void) +{ + ma_device_config_vita config; + + MA_ZERO_OBJECT(&config); + config.portType = MA_VITA_PORT_TYPE_BGM; + + return config; +} +/* END miniaudio_vita.c */ + + MA_API void ma_get_device_backend_info(ma_device_backend_vtable* pBackendVTable, ma_device_backend_info* pBackendInfo) { @@ -49800,6 +50201,9 @@ static const void* ma_context_config_find_backend_config(const ma_context_config if (pVTable == ma_device_backend_xaudio) { return &pConfig->xaudio; } + if (pVTable == ma_device_backend_vita) { + return &pConfig->vita; + } if (pVTable == ma_device_backend_null) { return &pConfig->null_backend; } @@ -49831,6 +50235,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_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_vita, NULL); } if (backendsCap > count) { pBackends[count++] = ma_device_backend_config_init(ma_device_backend_null, NULL); } return count; @@ -50302,6 +50707,9 @@ static const void* ma_device_config_find_backend_config(const ma_device_config* if (pVTable == ma_device_backend_xaudio) { return &pConfig->xaudio; } + if (pVTable == ma_device_backend_vita) { + return &pConfig->vita; + } if (pVTable == ma_device_backend_null) { return &pConfig->null_backend; } diff --git a/tests/vita/CMakeLists.txt b/tests/vita/CMakeLists.txt new file mode 100644 index 00000000..2b869433 --- /dev/null +++ b/tests/vita/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.16) + +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + if(DEFINED ENV{VITASDK}) + set(CMAKE_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake" CACHE PATH "toolchain file") + else() + message(FATAL_ERROR "Please define VITASDK to point to your SDK path!") + endif() +endif() + + +project(miniaudio_vita) +include("${VITASDK}/share/vita.cmake" REQUIRED) + +set(VITA_APP_NAME "miniaudio_vita") +set(VITA_TITLEID "MINIAUDIO00001") +set(VITA_VERSION "01.00") + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +add_executable(${PROJECT_NAME} + miniaudio_vita.c +) + +target_link_libraries(${PROJECT_NAME} + m + SceAudio_stub + SceDisplay_stub + SceCtrl_stub +) + +vita_create_self(${PROJECT_NAME}.self ${PROJECT_NAME}) +vita_create_vpk(${PROJECT_NAME}.vpk ${VITA_TITLEID} ${PROJECT_NAME}.self + VERSION ${VITA_VERSION} + NAME ${VITA_APP_NAME} +) diff --git a/tests/vita/miniaudio_vita.c b/tests/vita/miniaudio_vita.c new file mode 100644 index 00000000..303eca1d --- /dev/null +++ b/tests/vita/miniaudio_vita.c @@ -0,0 +1,114 @@ +/* Uncomment this and update the path to enable the debug screen. */ +/*#include "/opt/toolchains/vitasdk/samples/common/debugScreen.c"*/ + +#if defined(DEBUG_SCREEN_H) +#define HAS_DEBUG_SCREEN +#endif + +#ifdef HAS_DEBUG_SCREEN +#define printf psvDebugScreenPrintf +#endif + +#define MA_DEBUG_OUTPUT +#include "../../miniaudio.c" + +#include + +static void data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + static int c = 0; + + 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; + SceCtrlData ctrlPeek, ctrlPress; + + #ifdef HAS_DEBUG_SCREEN + psvDebugScreenInit(); + #endif + + 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 = 48000; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &waveform; + deviceConfig.periodSizeInFrames = 1024; + + #if 0 + ma_device_backend_config backend = ma_device_backend_config_init(ma_device_backend_vita, NULL); + result = ma_device_init_ex(&backend, 1, NULL, &deviceConfig, &device); + #else + result = ma_device_init(NULL, &deviceConfig, &device); + #endif + if (result != MA_SUCCESS) { + 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. */ + #if 0 + { + ma_decoder_config decoderConfig = ma_decoder_config_init(device.playback.format, device.playback.channels, device.sampleRate); + ma_decoder_init_file("test.mp3", &decoderConfig, &decoder); + } + #endif + + result = ma_device_start(&device); + if (result != MA_SUCCESS) { + return 1; + } + + for (;;) { + ctrlPress = ctrlPeek; + sceCtrlPeekBufferPositive(0, &ctrlPeek, 1); + ctrlPress.buttons = ctrlPeek.buttons & ~ctrlPress.buttons; + + if(ctrlPress.buttons == SCE_CTRL_SQUARE) { + if (ma_device_is_started(&device)) { + printf("STOPPING\n"); + ma_device_stop(&device); + } else { + printf("STARTING\n"); + ma_device_start(&device); + } + } + + if(ctrlPress.buttons == SCE_CTRL_CROSS) { + break; + } + + if (ma_device_get_threading_mode(&device) == MA_THREADING_MODE_SINGLE_THREADED) { + ma_device_step(&device, MA_BLOCKING_MODE_NON_BLOCKING); + + /* + I found that if I don't relax the CPU a bit I'll get glitching when running in vita3k. Maybe the loop is pinning the CPU + at 100% and starving the audio system? + */ + ma_sleep(1); + } else { + ma_sleep(1); + } + } + + ma_waveform_uninit(&waveform); + ma_device_uninit(&device); + return 0; +}