mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-21 15:56:58 +02:00
Add PS Vita backend.
This commit is contained in:
@@ -43,6 +43,7 @@
|
|||||||
/tests/xbox/*.iso
|
/tests/xbox/*.iso
|
||||||
/tests/xbox/*.exe
|
/tests/xbox/*.exe
|
||||||
/tests/xbox/test.mp3
|
/tests/xbox/test.mp3
|
||||||
|
/tests/vita/build/
|
||||||
/tests/*.c
|
/tests/*.c
|
||||||
/tests/*.cpp
|
/tests/*.cpp
|
||||||
/website/docs/
|
/website/docs/
|
||||||
|
|||||||
@@ -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_WEBAUDIO "Disable the Web Audio backend" OFF)
|
||||||
option(MINIAUDIO_NO_DREAMCAST "Disable the Dreamcast 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_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_NULL "Disable the null backend" OFF)
|
||||||
option(MINIAUDIO_NO_SDL2 "Disable the SDL2 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)
|
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_WEBAUDIO "Enable the Web Audio backend" OFF)
|
||||||
option(MINIAUDIO_ENABLE_DREAMCAST "Enable the Dreamcast 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_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_NULL "Enable the null backend" OFF)
|
||||||
option(MINIAUDIO_ENABLE_SDL2 "Enable the SDL2 backend" OFF)
|
option(MINIAUDIO_ENABLE_SDL2 "Enable the SDL2 backend" OFF)
|
||||||
option(MINIAUDIO_NO_DECODING "Disable decoding APIs" 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(WEBAUDIO)
|
||||||
normalize_backend_enabled_option(DREAMCAST)
|
normalize_backend_enabled_option(DREAMCAST)
|
||||||
normalize_backend_enabled_option(XAUDIO)
|
normalize_backend_enabled_option(XAUDIO)
|
||||||
|
normalize_backend_enabled_option(VITA)
|
||||||
normalize_backend_enabled_option(NULL)
|
normalize_backend_enabled_option(NULL)
|
||||||
normalize_backend_enabled_option(SDL2)
|
normalize_backend_enabled_option(SDL2)
|
||||||
|
|
||||||
@@ -300,6 +303,9 @@ endif()
|
|||||||
if(MINIAUDIO_NO_XAUDIO)
|
if(MINIAUDIO_NO_XAUDIO)
|
||||||
list(APPEND COMPILE_DEFINES MA_NO_XAUDIO)
|
list(APPEND COMPILE_DEFINES MA_NO_XAUDIO)
|
||||||
endif()
|
endif()
|
||||||
|
if(MINIAUDIO_NO_VITA)
|
||||||
|
list(APPEND COMPILE_DEFINES MA_NO_VITA)
|
||||||
|
endif()
|
||||||
if(MINIAUDIO_NO_NULL)
|
if(MINIAUDIO_NO_NULL)
|
||||||
list(APPEND COMPILE_DEFINES MA_NO_NULL)
|
list(APPEND COMPILE_DEFINES MA_NO_NULL)
|
||||||
endif()
|
endif()
|
||||||
@@ -357,6 +363,9 @@ if(MINIAUDIO_ENABLE_ONLY_SPECIFIC_BACKENDS)
|
|||||||
if(MINIAUDIO_ENABLE_XAUDIO)
|
if(MINIAUDIO_ENABLE_XAUDIO)
|
||||||
list(APPEND COMPILE_DEFINES MA_ENABLE_XAUDIO)
|
list(APPEND COMPILE_DEFINES MA_ENABLE_XAUDIO)
|
||||||
endif()
|
endif()
|
||||||
|
if(MINIAUDIO_ENABLE_VITA)
|
||||||
|
list(APPEND COMPILE_DEFINES MA_ENABLE_VITA)
|
||||||
|
endif()
|
||||||
if(MINIAUDIO_ENABLE_NULL)
|
if(MINIAUDIO_ENABLE_NULL)
|
||||||
list(APPEND COMPILE_DEFINES MA_ENABLE_NULL)
|
list(APPEND COMPILE_DEFINES MA_ENABLE_NULL)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
+408
@@ -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) |
|
| 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 |
|
| 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) |
|
| 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
|
are not forced to use this configuration, but if you deviate from it miniaudio will need to do data
|
||||||
conversion.
|
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
|
16. Optimization Tips
|
||||||
=====================
|
=====================
|
||||||
@@ -7482,6 +7506,34 @@ MA_API ma_device_config_xaudio ma_device_config_xaudio_init(void);
|
|||||||
/* END miniaudio_xaudio.h */
|
/* 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 */
|
/* BEG miniaudio_null.h */
|
||||||
typedef struct ma_context_config_null
|
typedef struct ma_context_config_null
|
||||||
{
|
{
|
||||||
@@ -7914,6 +7966,7 @@ struct ma_device_config
|
|||||||
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_xaudio xaudio;
|
||||||
|
ma_device_config_vita vita;
|
||||||
ma_device_config_null null_backend;
|
ma_device_config_null null_backend;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -8055,6 +8108,7 @@ struct ma_context_config
|
|||||||
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_xaudio xaudio;
|
||||||
|
ma_context_config_vita vita;
|
||||||
ma_context_config_null null_backend;
|
ma_context_config_null null_backend;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -20338,6 +20392,9 @@ BACKENDS
|
|||||||
#if defined(MA_XBOX)
|
#if defined(MA_XBOX)
|
||||||
#define MA_SUPPORT_XAUDIO
|
#define MA_SUPPORT_XAUDIO
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(MA_VITA)
|
||||||
|
#define MA_SUPPORT_VITA
|
||||||
|
#endif
|
||||||
|
|
||||||
/* All platforms should support custom backends. */
|
/* All platforms should support custom backends. */
|
||||||
#define MA_SUPPORT_CUSTOM
|
#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))
|
#if defined(MA_SUPPORT_XAUDIO) && !defined(MA_NO_XAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_XAUDIO))
|
||||||
#define MA_HAS_XAUDIO
|
#define MA_HAS_XAUDIO
|
||||||
#endif
|
#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))
|
#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
|
||||||
@@ -48766,6 +48826,347 @@ MA_API ma_device_config_xaudio ma_device_config_xaudio_init(void)
|
|||||||
/* END miniaudio_xaudio.c */
|
/* END miniaudio_xaudio.c */
|
||||||
|
|
||||||
|
|
||||||
|
/* BEG miniaudio_vita.c */
|
||||||
|
#if defined(MA_HAS_VITA)
|
||||||
|
#include <psp2/audioout.h>
|
||||||
|
|
||||||
|
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)
|
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) {
|
if (pVTable == ma_device_backend_xaudio) {
|
||||||
return &pConfig->xaudio;
|
return &pConfig->xaudio;
|
||||||
}
|
}
|
||||||
|
if (pVTable == ma_device_backend_vita) {
|
||||||
|
return &pConfig->vita;
|
||||||
|
}
|
||||||
if (pVTable == ma_device_backend_null) {
|
if (pVTable == ma_device_backend_null) {
|
||||||
return &pConfig->null_backend;
|
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_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_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); }
|
if (backendsCap > count) { pBackends[count++] = ma_device_backend_config_init(ma_device_backend_null, NULL); }
|
||||||
|
|
||||||
return count;
|
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) {
|
if (pVTable == ma_device_backend_xaudio) {
|
||||||
return &pConfig->xaudio;
|
return &pConfig->xaudio;
|
||||||
}
|
}
|
||||||
|
if (pVTable == ma_device_backend_vita) {
|
||||||
|
return &pConfig->vita;
|
||||||
|
}
|
||||||
if (pVTable == ma_device_backend_null) {
|
if (pVTable == ma_device_backend_null) {
|
||||||
return &pConfig->null_backend;
|
return &pConfig->null_backend;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}
|
||||||
|
)
|
||||||
@@ -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 <psp2/ctrl.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user