mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
Add Dreamcast backend.
This commit is contained in:
@@ -32,6 +32,11 @@
|
|||||||
/tests/_build/a.out
|
/tests/_build/a.out
|
||||||
/tests/_build/a.exe
|
/tests/_build/a.exe
|
||||||
/tests/debugging/archive/
|
/tests/debugging/archive/
|
||||||
|
/tests/dreamcast/miniaudio.elf
|
||||||
|
/tests/dreamcast/miniaudio_dreamcast_test.o
|
||||||
|
/tests/dreamcast/romdisk.img
|
||||||
|
/tests/dreamcast/romdisk.o
|
||||||
|
/tests/dreamcast/romdisk/
|
||||||
/tests/*.c
|
/tests/*.c
|
||||||
/tests/*.cpp
|
/tests/*.cpp
|
||||||
/website/docs/
|
/website/docs/
|
||||||
|
|||||||
+596
@@ -3734,6 +3734,7 @@ example, ALSA, which is specific to Linux, will not be included in the Windows b
|
|||||||
| AAudio | ma_device_backend_aaudio | Android 8+ |
|
| AAudio | ma_device_backend_aaudio | Android 8+ |
|
||||||
| 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 |
|
||||||
| Null | ma_device_backend_null | Cross Platform (not used on Web) |
|
| Null | ma_device_backend_null | Cross Platform (not used on Web) |
|
||||||
+-------------+------------------------------+--------------------------------------------------------+
|
+-------------+------------------------------+--------------------------------------------------------+
|
||||||
|
|
||||||
@@ -3800,6 +3801,62 @@ Some backends have some nuance details you may want to be aware of.
|
|||||||
https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device
|
https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device
|
||||||
may fail if you try to start playback without first handling some kind of user input.
|
may fail if you try to start playback without first handling some kind of user input.
|
||||||
|
|
||||||
|
15.6. Dreamcast
|
||||||
|
---------------
|
||||||
|
The Dreamcast backend uses KallistiOS.
|
||||||
|
|
||||||
|
miniaudio will call `snd_init()` automatically unless you disable it via the context config:
|
||||||
|
|
||||||
|
```c
|
||||||
|
contextConfig.dreamcast.noInit = MA_TRUE;
|
||||||
|
```
|
||||||
|
|
||||||
|
The following device configuration is recommended.
|
||||||
|
|
||||||
|
Format: ma_format_u8 or ma_format_s16
|
||||||
|
Channels: 1 or 2
|
||||||
|
Sample Rate: 44100, 22050 or 11025
|
||||||
|
Period Size: 1024 to 16384, multiple of 32. Recommended you keep it a power of two.
|
||||||
|
noFixedSizedCallback: True (This will tell miniaudio to bypass one of its internal buffers.)
|
||||||
|
|
||||||
|
If you follow the above rules, miniaudio will skip it's data conversion pipeline and will pass
|
||||||
|
through a direct pointer to an internal buffer to the data callback without any extra buffering or
|
||||||
|
data conversion. You are still free to use other formats, but it'll just have a bit more overhead.
|
||||||
|
The miniaudio resampler will have enough overhead that it's strongly recommended you keep the
|
||||||
|
sample rate set to one of the recommended values and pre-process your audio assets to match.
|
||||||
|
|
||||||
|
You can initialize multiple devices and specify which voice channel you want to use. There is a
|
||||||
|
maximum of 64 voices. If you request two channels when initializing the device it will use two of
|
||||||
|
them. You can specify the index of the voice channel you want to use in the dreamcast device
|
||||||
|
config. You can use multiple devices to have the hardware do mixing for you, but if you do this
|
||||||
|
make sure you manage your voice channel properly. To configure the voice channel, set it in the
|
||||||
|
`dreamcast` config:
|
||||||
|
|
||||||
|
```c
|
||||||
|
deviceConfig.dreamcast.voiceChannel = 2; // 0 if the default. The left channel will use `voiceChannel + 0`, the right channel will use `voiceChannel + 1`.
|
||||||
|
```
|
||||||
|
|
||||||
|
If you initialize more than one `ma_device` object, self manage a single `ma_context` object. It is
|
||||||
|
a single context to many devices:
|
||||||
|
|
||||||
|
```c
|
||||||
|
ma_context context;
|
||||||
|
ma_context_init(...);
|
||||||
|
|
||||||
|
deviceConfig.dreamcast.voiceChannel = 0;
|
||||||
|
ma_device_init(&context, &deviceConfig, &device0);
|
||||||
|
|
||||||
|
deviceConfig.dreamcast.voiceChannel = 2; // <-- Make sure the voice channel is different between devices. Remember that a stereo device consumes two channels.
|
||||||
|
ma_device_init(&context, &deviceConfig, &device1);
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the `ma_device` struct itself has memory overhead just from its own members.
|
||||||
|
|
||||||
|
By default audio will be processed on a background thread. You can disable threading and run your
|
||||||
|
device in single threaded mode by disabling it at compiling with the `MA_NO_THREADING` define. By
|
||||||
|
disabling threading at compile time you will also reduce memory usage of the `ma_device` struct.
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
16. Optimization Tips
|
16. Optimization Tips
|
||||||
@@ -7343,6 +7400,28 @@ MA_API ma_device_backend_vtable* ma_webaudio_get_vtable(void);
|
|||||||
/* END WEBAUDIO */
|
/* END WEBAUDIO */
|
||||||
|
|
||||||
|
|
||||||
|
/* BEG miniaudio_dreamcast.h */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ma_bool32 noInit; /* When set to true, will *not* call snd_init() when the context is initialized. */
|
||||||
|
} ma_context_config_dreamcast;
|
||||||
|
|
||||||
|
MA_API ma_context_config_dreamcast ma_context_config_dreamcast_init(void);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int voiceChannel; /* For a stereo stream, the left channel will be voiceChannel and the right channel will be voiceChannel+1. */
|
||||||
|
} ma_device_config_dreamcast;
|
||||||
|
|
||||||
|
MA_API ma_device_config_dreamcast ma_device_config_dreamcast_init(int voiceChannel);
|
||||||
|
|
||||||
|
|
||||||
|
extern ma_device_backend_vtable* ma_device_backend_dreamcast;
|
||||||
|
MA_API ma_device_backend_vtable* ma_dreamcast_get_vtable(void);
|
||||||
|
/* END miniaudio_dreamcast.h */
|
||||||
|
|
||||||
|
|
||||||
/* BEG NULL */
|
/* BEG NULL */
|
||||||
typedef struct ma_context_config_null
|
typedef struct ma_context_config_null
|
||||||
{
|
{
|
||||||
@@ -7773,6 +7852,7 @@ struct ma_device_config
|
|||||||
ma_device_config_aaudio aaudio;
|
ma_device_config_aaudio aaudio;
|
||||||
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_null null_backend;
|
ma_device_config_null null_backend;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -7912,6 +7992,7 @@ struct ma_context_config
|
|||||||
ma_context_config_aaudio aaudio;
|
ma_context_config_aaudio aaudio;
|
||||||
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_null null_backend;
|
ma_context_config_null null_backend;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -19861,6 +19942,9 @@ BACKENDS
|
|||||||
#if defined(MA_EMSCRIPTEN)
|
#if defined(MA_EMSCRIPTEN)
|
||||||
#define MA_SUPPORT_WEBAUDIO
|
#define MA_SUPPORT_WEBAUDIO
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(MA_DREAMCAST)
|
||||||
|
#define MA_SUPPORT_DREAMCAST
|
||||||
|
#endif
|
||||||
|
|
||||||
/* All platforms should support custom backends. */
|
/* All platforms should support custom backends. */
|
||||||
#define MA_SUPPORT_CUSTOM
|
#define MA_SUPPORT_CUSTOM
|
||||||
@@ -19913,6 +19997,9 @@ BACKENDS
|
|||||||
#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
|
#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
|
||||||
#define MA_HAS_WEBAUDIO
|
#define MA_HAS_WEBAUDIO
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(MA_SUPPORT_DREAMCAST) && !defined(MA_NO_DREAMCAST) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DREAMCAST))
|
||||||
|
#define MA_HAS_DREAMCAST
|
||||||
|
#endif
|
||||||
#if defined(MA_SUPPORT_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
|
||||||
@@ -47405,6 +47492,508 @@ MA_API ma_device_backend_vtable* ma_webaudio_get_vtable(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* BEG miniaudio_dreamcast.c */
|
||||||
|
#if defined(MA_HAS_DREAMCAST)
|
||||||
|
#include <kos.h>
|
||||||
|
#include <dc/sound/sound.h>
|
||||||
|
#include <dc/sound/aica_comm.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int _unused;
|
||||||
|
} ma_context_state_dreamcast;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ma_int8 voiceChannel;
|
||||||
|
ma_int8 subBufferIndex; /* Flip-flops between 0 and 1 and is used to determine which half of the buffer needs to be updated with fresh data. */
|
||||||
|
ma_int8 padding[2];
|
||||||
|
ma_uint32 buffer; /* Memory allocation on the SPU. Returned by snd_mem_malloc(). */
|
||||||
|
void* pIntermediaryBuffer; /* Big enough for one period (one half of the buffer). Used for transferring to the SPU buffer. */
|
||||||
|
} ma_device_state_dreamcast;
|
||||||
|
|
||||||
|
|
||||||
|
static ma_context_state_dreamcast* ma_context_get_backend_state__dreamcast(ma_context* pContext)
|
||||||
|
{
|
||||||
|
return (ma_context_state_dreamcast*)ma_context_get_backend_state(pContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_device_state_dreamcast* ma_device_get_backend_state__dreamcast(ma_device* pDevice)
|
||||||
|
{
|
||||||
|
return (ma_device_state_dreamcast*)ma_device_get_backend_state(pDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ma_uint32 ma_format_to_dreamcast(ma_format format)
|
||||||
|
{
|
||||||
|
if (format == ma_format_u8) {
|
||||||
|
return AICA_SM_8BIT;
|
||||||
|
} else {
|
||||||
|
return AICA_SM_16BIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ma_backend_info__dreamcast(ma_device_backend_info* pBackendInfo)
|
||||||
|
{
|
||||||
|
MA_ASSERT(pBackendInfo != NULL);
|
||||||
|
pBackendInfo->pName = "Dreamcast";
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_result ma_context_init__dreamcast(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState)
|
||||||
|
{
|
||||||
|
ma_context_state_dreamcast* pContextStateDreamcast;
|
||||||
|
const ma_context_config_dreamcast* pContextConfigDreamcast = (ma_context_config_dreamcast*)pContextBackendConfig;
|
||||||
|
ma_log* pLog = ma_context_get_log(pContext);
|
||||||
|
|
||||||
|
/* The context config is not currently being used for this backend. */
|
||||||
|
(void)pContextConfigDreamcast;
|
||||||
|
(void)pLog;
|
||||||
|
|
||||||
|
if (!pContextConfigDreamcast->noInit) {
|
||||||
|
snd_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't actually do anything with context state so there's no need wasting time and memory allocating anything. */
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
pContextStateDreamcast = (ma_context_state_dreamcast*)ma_calloc(sizeof(*pContextStateDreamcast), ma_context_get_allocation_callbacks(pContext));
|
||||||
|
if (pContextStateDreamcast == NULL) {
|
||||||
|
return MA_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
pContextStateDreamcast = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*ppContextState = pContextStateDreamcast;
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ma_context_uninit__dreamcast(ma_context* pContext)
|
||||||
|
{
|
||||||
|
ma_context_state_dreamcast* pContextStateDreamcast = ma_context_get_backend_state__dreamcast(pContext);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
ma_free(pContextStateDreamcast, ma_context_get_allocation_callbacks(pContext));
|
||||||
|
#else
|
||||||
|
(void)pContextStateDreamcast;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_result ma_context_enumerate_devices__dreamcast(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pCallbackUserData)
|
||||||
|
{
|
||||||
|
ma_context_state_dreamcast* pContextStateDreamcast = ma_context_get_backend_state__dreamcast(pContext);
|
||||||
|
ma_device_info deviceInfo;
|
||||||
|
|
||||||
|
(void)pContextStateDreamcast;
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* We can do u8 and s16, mono or stereo, and 11025, 22050 or 44100. */
|
||||||
|
ma_device_info_add_native_data_format(&deviceInfo, ma_format_s16, 1, 2, 11025, 44100);
|
||||||
|
ma_device_info_add_native_data_format(&deviceInfo, ma_format_u8, 1, 2, 11025, 44100);
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_result ma_device_init__dreamcast(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState)
|
||||||
|
{
|
||||||
|
ma_device_state_dreamcast* pDeviceStateDreamcast;
|
||||||
|
ma_device_config_dreamcast* pDeviceConfigDreamcast = (ma_device_config_dreamcast*)pDeviceBackendConfig;
|
||||||
|
ma_context_state_dreamcast* pContextStateDreamcast = ma_context_get_backend_state__dreamcast(ma_device_get_context(pDevice));
|
||||||
|
ma_device_config_dreamcast defaultConfig;
|
||||||
|
ma_device_type deviceType = ma_device_get_type(pDevice);
|
||||||
|
ma_uint32 bufferSizeInBytes;
|
||||||
|
ma_uint32 bpf;
|
||||||
|
|
||||||
|
(void)pContextStateDreamcast;
|
||||||
|
(void)pDescriptorCapture;
|
||||||
|
|
||||||
|
if (pDeviceConfigDreamcast == NULL) {
|
||||||
|
defaultConfig = ma_device_config_dreamcast_init(0);
|
||||||
|
pDeviceConfigDreamcast = &defaultConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can only support playback. */
|
||||||
|
if (deviceType != ma_device_type_playback) {
|
||||||
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
pDeviceStateDreamcast = (ma_device_state_dreamcast*)ma_calloc(sizeof(*pDeviceStateDreamcast), ma_device_get_allocation_callbacks(pDevice));
|
||||||
|
if (pDeviceStateDreamcast == NULL) {
|
||||||
|
return MA_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
pDeviceStateDreamcast->subBufferIndex = 1; /* Should start at one. */
|
||||||
|
|
||||||
|
/* Default to s16 for the format. Can support u8 and s16. */
|
||||||
|
if (pDescriptorPlayback->format != ma_format_u8 /*&& pDescriptorPlayback->format != ma_format_s16*/) {
|
||||||
|
pDescriptorPlayback->format = ma_format_s16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The channels needs to be mono or stereo. */
|
||||||
|
if (pDescriptorPlayback->channels == 0 || pDescriptorPlayback->channels > 2) {
|
||||||
|
pDescriptorPlayback->channels = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now that we know our channel count we need to verify our voice channel index. There is a maximum of 64. Stereo uses two of them. */
|
||||||
|
if (pDeviceConfigDreamcast->voiceChannel + pDescriptorPlayback->channels > 64) {
|
||||||
|
return MA_INVALID_ARGS; /* Voice channel exceeds limit. */
|
||||||
|
}
|
||||||
|
|
||||||
|
pDeviceStateDreamcast->voiceChannel = (ma_uint8)pDeviceConfigDreamcast->voiceChannel;
|
||||||
|
|
||||||
|
/* The sample rate is always 44100, 22050 or 11025. */
|
||||||
|
if (pDescriptorPlayback->sampleRate != 44100 && pDescriptorPlayback->sampleRate != 22050 && pDescriptorPlayback->sampleRate != 11025) {
|
||||||
|
pDescriptorPlayback->sampleRate = 44100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Channel map. */
|
||||||
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
|
||||||
|
|
||||||
|
/* The period count is always 2 (double buffered). */
|
||||||
|
pDescriptorPlayback->periodCount = 2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The buffer cannot exceed 65534. Since we'll always be using 2 periods (double buffering), the maximum period
|
||||||
|
size in frames is 32767 = 65534/2. We're also going to align this to 32 bytes.
|
||||||
|
*/
|
||||||
|
pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate);
|
||||||
|
if (pDescriptorPlayback->periodSizeInFrames > 32767) {
|
||||||
|
pDescriptorPlayback->periodSizeInFrames = 32767;
|
||||||
|
}
|
||||||
|
if (pDescriptorPlayback->periodSizeInFrames < 1024) {
|
||||||
|
pDescriptorPlayback->periodSizeInFrames = 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
bpf = ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels);
|
||||||
|
bufferSizeInBytes = (pDescriptorPlayback->periodSizeInFrames & ~31) * bpf * pDescriptorPlayback->periodCount;
|
||||||
|
pDescriptorPlayback->periodSizeInFrames = bufferSizeInBytes / bpf / pDescriptorPlayback->periodCount;
|
||||||
|
|
||||||
|
/* We need an intermediary buffer for transferring to the SPU buffer. */
|
||||||
|
pDeviceStateDreamcast->pIntermediaryBuffer = ma_aligned_malloc(pDescriptorPlayback->periodSizeInFrames * bpf, 32, ma_device_get_allocation_callbacks(pDevice));
|
||||||
|
if (pDeviceStateDreamcast->pIntermediaryBuffer == NULL) {
|
||||||
|
ma_free(pDeviceStateDreamcast, ma_device_get_allocation_callbacks(pDevice));
|
||||||
|
return MA_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_ZERO_MEMORY(pDeviceStateDreamcast->pIntermediaryBuffer, pDescriptorPlayback->periodSizeInFrames * bpf);
|
||||||
|
|
||||||
|
/* Now we can allocate our memory for the buffer on the SPU side. */
|
||||||
|
pDeviceStateDreamcast->buffer = snd_mem_malloc(bufferSizeInBytes);
|
||||||
|
if (pDeviceStateDreamcast->buffer == 0) {
|
||||||
|
ma_free(pDeviceStateDreamcast->pIntermediaryBuffer, ma_device_get_allocation_callbacks(pDevice));
|
||||||
|
ma_free(pDeviceStateDreamcast, ma_device_get_allocation_callbacks(pDevice));
|
||||||
|
return MA_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We will fill the buffer with initial silence. */
|
||||||
|
spu_memset_sq(pDeviceStateDreamcast->buffer, 0, bufferSizeInBytes); /* Can we safely assume the returned value from snd_mem_malloc() is aligned to 32 bytes? */
|
||||||
|
|
||||||
|
|
||||||
|
/* Done. */
|
||||||
|
*ppDeviceState = pDeviceStateDreamcast;
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ma_device_uninit__dreamcast(ma_device* pDevice)
|
||||||
|
{
|
||||||
|
ma_device_state_dreamcast* pDeviceStateDreamcast = ma_device_get_backend_state__dreamcast(pDevice);
|
||||||
|
|
||||||
|
snd_mem_free(pDeviceStateDreamcast->buffer);
|
||||||
|
ma_aligned_free(pDeviceStateDreamcast->pIntermediaryBuffer, ma_device_get_allocation_callbacks(pDevice));
|
||||||
|
ma_free(pDeviceStateDreamcast, ma_device_get_allocation_callbacks(pDevice));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ma_device_fill_sub_buffer__dreamcast(ma_device* pDevice, int subBufferIndex)
|
||||||
|
{
|
||||||
|
ma_device_state_dreamcast* pDeviceStateDreamcast = ma_device_get_backend_state__dreamcast(pDevice);
|
||||||
|
ma_uint32 offsetInSamples = 0;
|
||||||
|
ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
||||||
|
|
||||||
|
/* The offset is in samples, not frames. */
|
||||||
|
offsetInSamples = subBufferIndex * pDevice->playback.internalPeriodSizeInFrames;
|
||||||
|
if (pDevice->playback.internalFormat == ma_format_s16) {
|
||||||
|
offsetInSamples *= sizeof(ma_int16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do the data callback. */
|
||||||
|
ma_device_handle_backend_data_callback(pDevice, pDeviceStateDreamcast->pIntermediaryBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames);
|
||||||
|
|
||||||
|
/* If our format is u8 we want to convert this to signed. */
|
||||||
|
if (pDevice->playback.internalFormat == ma_format_u8) {
|
||||||
|
ma_uint32 sampleCount = pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalChannels;
|
||||||
|
ma_uint32 iSample;
|
||||||
|
|
||||||
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
||||||
|
((ma_int8*)pDeviceStateDreamcast->pIntermediaryBuffer)[iSample] = (ma_int8)128 - ((ma_uint8*)pDeviceStateDreamcast->pIntermediaryBuffer)[iSample];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pDevice->playback.internalChannels == 1) {
|
||||||
|
spu_memload_sq(pDeviceStateDreamcast->buffer + offsetInSamples, pDeviceStateDreamcast->pIntermediaryBuffer, pDevice->playback.internalPeriodSizeInFrames * bpf);
|
||||||
|
} else {
|
||||||
|
/* There is no snd_pcm8_split_sq() from what I can tell. Might need to figure that out for myself. */
|
||||||
|
ma_uint32 l = pDeviceStateDreamcast->buffer + offsetInSamples;
|
||||||
|
ma_uint32 r = pDeviceStateDreamcast->buffer + (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * bpf) + offsetInSamples;
|
||||||
|
ma_uint32 framesWritten = 0;
|
||||||
|
while (framesWritten < pDevice->playback.internalPeriodSizeInFrames) {
|
||||||
|
ma_uint32 framesToWrite;
|
||||||
|
ma_int8 tmpl[256] __attribute__((aligned(32)));
|
||||||
|
ma_int8 tmpr[256] __attribute__((aligned(32)));
|
||||||
|
void* tmp[2];
|
||||||
|
|
||||||
|
framesToWrite = pDevice->playback.internalPeriodSizeInFrames - framesWritten;
|
||||||
|
if (framesToWrite > ma_countof(tmpl)) {
|
||||||
|
framesToWrite = ma_countof(tmpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp[0] = tmpl;
|
||||||
|
tmp[1] = tmpr;
|
||||||
|
ma_deinterleave_pcm_frames(ma_format_u8, 2, framesToWrite, ma_offset_ptr(pDeviceStateDreamcast->pIntermediaryBuffer, framesWritten * bpf), tmp);
|
||||||
|
spu_memload_sq(l + (framesWritten * sizeof(ma_int8)), tmpl, framesToWrite * sizeof(ma_int8));
|
||||||
|
spu_memload_sq(r + (framesWritten * sizeof(ma_int8)), tmpr, framesToWrite * sizeof(ma_int8));
|
||||||
|
|
||||||
|
framesWritten += framesToWrite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pDevice->playback.internalChannels == 1) {
|
||||||
|
spu_memload_sq(pDeviceStateDreamcast->buffer + offsetInSamples, pDeviceStateDreamcast->pIntermediaryBuffer, pDevice->playback.internalPeriodSizeInFrames * bpf);
|
||||||
|
} else {
|
||||||
|
ma_uint32 l = pDeviceStateDreamcast->buffer + offsetInSamples;
|
||||||
|
ma_uint32 r = pDeviceStateDreamcast->buffer + (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * bpf) + offsetInSamples;
|
||||||
|
|
||||||
|
/*
|
||||||
|
There is an endian bug in snd_pcm16_split_sq(). It merges two signed 16-bit samples into a single 32-bit variable in preparation
|
||||||
|
for transfer, but the ordering is the wrong way around. This results in a low quality sound. I'm working around this by splitting
|
||||||
|
this in one pass and then doing the transfer in a second pass. Later on I would like to update this to do the splitting and copy
|
||||||
|
in a single pass to save on some data movement.
|
||||||
|
*/
|
||||||
|
#if 0
|
||||||
|
snd_pcm16_split_sq((uint32_t*)pDeviceStateDreamcast->pIntermediaryBuffer, l, r, pDevice->playback.internalPeriodSizeInFrames * bpf);
|
||||||
|
#else
|
||||||
|
ma_uint32 framesWritten = 0;
|
||||||
|
while (framesWritten < pDevice->playback.internalPeriodSizeInFrames) {
|
||||||
|
ma_uint32 framesToWrite;
|
||||||
|
ma_int16 tmpl[256] __attribute__((aligned(32)));
|
||||||
|
ma_int16 tmpr[256] __attribute__((aligned(32)));
|
||||||
|
void* tmp[2];
|
||||||
|
|
||||||
|
framesToWrite = pDevice->playback.internalPeriodSizeInFrames - framesWritten;
|
||||||
|
if (framesToWrite > ma_countof(tmpl)) {
|
||||||
|
framesToWrite = ma_countof(tmpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp[0] = tmpl;
|
||||||
|
tmp[1] = tmpr;
|
||||||
|
ma_deinterleave_pcm_frames(ma_format_s16, 2, framesToWrite, ma_offset_ptr(pDeviceStateDreamcast->pIntermediaryBuffer, framesWritten * bpf), tmp);
|
||||||
|
spu_memload_sq(l + (framesWritten * sizeof(ma_int16)), tmpl, framesToWrite * sizeof(ma_int16));
|
||||||
|
spu_memload_sq(r + (framesWritten * sizeof(ma_int16)), tmpr, framesToWrite * sizeof(ma_int16));
|
||||||
|
|
||||||
|
framesWritten += framesToWrite;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_result ma_device_start__dreamcast(ma_device* pDevice)
|
||||||
|
{
|
||||||
|
ma_device_state_dreamcast* pDeviceStateDreamcast = ma_device_get_backend_state__dreamcast(pDevice);
|
||||||
|
AICA_CMDSTR_CHANNEL(tmp, cmd, chan);
|
||||||
|
|
||||||
|
/* Make sure our sub-buffer index is reset or else we'll get a glitch when starting. */
|
||||||
|
pDeviceStateDreamcast->subBufferIndex = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
We'll want to fire the data callback and fill our buffer with an initial chunk of data. If we don't do this we'll end
|
||||||
|
up with one period of silence before hearing any audio.
|
||||||
|
*/
|
||||||
|
ma_device_fill_sub_buffer__dreamcast(pDevice, 0);
|
||||||
|
|
||||||
|
/* With the buffer pre-filled we can now start the voices. */
|
||||||
|
memset(tmp, 0, sizeof(tmp));
|
||||||
|
cmd->cmd = AICA_CMD_CHAN;
|
||||||
|
cmd->timestamp = 0;
|
||||||
|
cmd->cmd_id = (uint32_t)pDeviceStateDreamcast->voiceChannel;
|
||||||
|
cmd->size = AICA_CMDSTR_CHANNEL_SIZE;
|
||||||
|
chan->cmd = AICA_CH_CMD_START;
|
||||||
|
chan->base = pDeviceStateDreamcast->buffer;
|
||||||
|
chan->type = ma_format_to_dreamcast(pDevice->playback.internalFormat);
|
||||||
|
chan->length = pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods;
|
||||||
|
chan->loop = 1;
|
||||||
|
chan->loopstart = 0;
|
||||||
|
chan->loopend = pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods;
|
||||||
|
chan->freq = pDevice->playback.internalSampleRate;
|
||||||
|
chan->vol = 255; /* 0..255 */
|
||||||
|
chan->pan = 128; /* 0..255 (0=left, 128=center, 255=right) */
|
||||||
|
chan->pos = 0;
|
||||||
|
|
||||||
|
if (pDevice->playback.internalChannels == 1) {
|
||||||
|
snd_sh4_to_aica(tmp, cmd->size);
|
||||||
|
} else {
|
||||||
|
snd_sh4_to_aica_stop();
|
||||||
|
{
|
||||||
|
/* Left. Hard pan to the left. */
|
||||||
|
cmd->cmd_id = (uint32_t)pDeviceStateDreamcast->voiceChannel + 0;
|
||||||
|
chan->base = pDeviceStateDreamcast->buffer + 0;
|
||||||
|
chan->pan = 0;
|
||||||
|
snd_sh4_to_aica(tmp, cmd->size);
|
||||||
|
|
||||||
|
/* Right. Hard pan to the right. */
|
||||||
|
cmd->cmd_id = (uint32_t)pDeviceStateDreamcast->voiceChannel + 1;
|
||||||
|
chan->base = pDeviceStateDreamcast->buffer + (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
|
||||||
|
chan->pan = 255;
|
||||||
|
snd_sh4_to_aica(tmp, cmd->size);
|
||||||
|
}
|
||||||
|
snd_sh4_to_aica_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_result ma_device_stop__dreamcast(ma_device* pDevice)
|
||||||
|
{
|
||||||
|
ma_device_state_dreamcast* pDeviceStateDreamcast = ma_device_get_backend_state__dreamcast(pDevice);
|
||||||
|
AICA_CMDSTR_CHANNEL(tmp, cmd, chan);
|
||||||
|
|
||||||
|
memset(tmp, 0, sizeof(tmp));
|
||||||
|
|
||||||
|
cmd->cmd = AICA_CMD_CHAN;
|
||||||
|
cmd->timestamp = 0;
|
||||||
|
cmd->cmd_id = (uint32_t)pDeviceStateDreamcast->voiceChannel;
|
||||||
|
cmd->size = AICA_CMDSTR_CHANNEL_SIZE;
|
||||||
|
chan->cmd = AICA_CH_CMD_STOP;
|
||||||
|
|
||||||
|
if (pDevice->playback.internalChannels == 1) {
|
||||||
|
snd_sh4_to_aica(tmp, cmd->size);
|
||||||
|
} else {
|
||||||
|
snd_sh4_to_aica_stop();
|
||||||
|
{
|
||||||
|
/* Left. */
|
||||||
|
cmd->cmd_id = (uint32_t)pDeviceStateDreamcast->voiceChannel + 0;
|
||||||
|
snd_sh4_to_aica(tmp, cmd->size);
|
||||||
|
|
||||||
|
/* Right. */
|
||||||
|
cmd->cmd_id = (uint32_t)pDeviceStateDreamcast->voiceChannel + 1;
|
||||||
|
snd_sh4_to_aica(tmp, cmd->size);
|
||||||
|
}
|
||||||
|
snd_sh4_to_aica_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_result ma_device_step__dreamcast(ma_device* pDevice, ma_blocking_mode blockingMode)
|
||||||
|
{
|
||||||
|
ma_device_state_dreamcast* pDeviceStateDreamcast = ma_device_get_backend_state__dreamcast(pDevice);
|
||||||
|
|
||||||
|
/* Keep looping until we have processed some data, or we're in non-blocking mode. */
|
||||||
|
for (;;) {
|
||||||
|
int pos;
|
||||||
|
ma_int8 currentSubBufferIndex;
|
||||||
|
ma_bool32 wasDataProcessed = MA_FALSE;
|
||||||
|
|
||||||
|
if (!ma_device_is_started(pDevice)) {
|
||||||
|
return MA_DEVICE_NOT_STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're currently using a poll/sleep pattern. Should try figuring out a better way to do this later. */
|
||||||
|
pos = (int)snd_get_pos(pDeviceStateDreamcast->voiceChannel);
|
||||||
|
if (pos < pDevice->playback.internalPeriodSizeInFrames) {
|
||||||
|
currentSubBufferIndex = 0;
|
||||||
|
} else {
|
||||||
|
currentSubBufferIndex = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pDeviceStateDreamcast->subBufferIndex != currentSubBufferIndex) {
|
||||||
|
ma_device_fill_sub_buffer__dreamcast(pDevice, pDeviceStateDreamcast->subBufferIndex);
|
||||||
|
pDeviceStateDreamcast->subBufferIndex = currentSubBufferIndex;
|
||||||
|
|
||||||
|
wasDataProcessed = MA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasDataProcessed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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__dreamcast(ma_device* pDevice)
|
||||||
|
{
|
||||||
|
ma_device_state_dreamcast* pDeviceStateDreamcast = ma_device_get_backend_state__dreamcast(pDevice);
|
||||||
|
|
||||||
|
/* Nothing to do. */
|
||||||
|
(void)pDeviceStateDreamcast;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_device_backend_vtable ma_gDeviceBackendVTable_Dreamcast =
|
||||||
|
{
|
||||||
|
ma_backend_info__dreamcast,
|
||||||
|
ma_context_init__dreamcast,
|
||||||
|
ma_context_uninit__dreamcast,
|
||||||
|
ma_context_enumerate_devices__dreamcast,
|
||||||
|
ma_device_init__dreamcast,
|
||||||
|
ma_device_uninit__dreamcast,
|
||||||
|
ma_device_start__dreamcast,
|
||||||
|
ma_device_stop__dreamcast,
|
||||||
|
ma_device_step__dreamcast,
|
||||||
|
ma_device_wakeup__dreamcast
|
||||||
|
};
|
||||||
|
|
||||||
|
ma_device_backend_vtable* ma_device_backend_dreamcast = &ma_gDeviceBackendVTable_Dreamcast;
|
||||||
|
#else
|
||||||
|
ma_device_backend_vtable* ma_device_backend_dreamcast = NULL;
|
||||||
|
#endif /* MA_HAS_DREAMCAST */
|
||||||
|
|
||||||
|
|
||||||
|
MA_API ma_device_backend_vtable* ma_dreamcast_get_vtable(void)
|
||||||
|
{
|
||||||
|
return ma_device_backend_dreamcast;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_context_config_dreamcast ma_context_config_dreamcast_init(void)
|
||||||
|
{
|
||||||
|
ma_context_config_dreamcast config;
|
||||||
|
|
||||||
|
MA_ZERO_OBJECT(&config);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_device_config_dreamcast ma_device_config_dreamcast_init(int voiceChannel)
|
||||||
|
{
|
||||||
|
ma_device_config_dreamcast config;
|
||||||
|
|
||||||
|
MA_ZERO_OBJECT(&config);
|
||||||
|
config.voiceChannel = voiceChannel;
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
if (pBackendVTable == NULL || pBackendVTable->onBackendInfo == NULL || pBackendInfo == NULL) {
|
if (pBackendVTable == NULL || pBackendVTable->onBackendInfo == NULL || pBackendInfo == NULL) {
|
||||||
@@ -48432,6 +49021,9 @@ static const void* ma_context_config_find_backend_config(const ma_context_config
|
|||||||
if (pVTable == ma_device_backend_webaudio) {
|
if (pVTable == ma_device_backend_webaudio) {
|
||||||
return &pConfig->webaudio;
|
return &pConfig->webaudio;
|
||||||
}
|
}
|
||||||
|
if (pVTable == ma_device_backend_dreamcast) {
|
||||||
|
return &pConfig->dreamcast;
|
||||||
|
}
|
||||||
if (pVTable == ma_device_backend_null) {
|
if (pVTable == ma_device_backend_null) {
|
||||||
return &pConfig->null_backend;
|
return &pConfig->null_backend;
|
||||||
}
|
}
|
||||||
@@ -48461,6 +49053,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_aaudio, NULL); }
|
if (backendsCap > count) { pBackends[count++] = ma_device_backend_config_init(ma_device_backend_aaudio, 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_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_null, NULL); }
|
if (backendsCap > count) { pBackends[count++] = ma_device_backend_config_init(ma_device_backend_null, NULL); }
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
@@ -48926,6 +49519,9 @@ static const void* ma_device_config_find_backend_config(const ma_device_config*
|
|||||||
if (pVTable == ma_device_backend_webaudio) {
|
if (pVTable == ma_device_backend_webaudio) {
|
||||||
return &pConfig->webaudio;
|
return &pConfig->webaudio;
|
||||||
}
|
}
|
||||||
|
if (pVTable == ma_device_backend_dreamcast) {
|
||||||
|
return &pConfig->dreamcast;
|
||||||
|
}
|
||||||
if (pVTable == ma_device_backend_null) {
|
if (pVTable == ma_device_backend_null) {
|
||||||
return &pConfig->null_backend;
|
return &pConfig->null_backend;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
TARGET = miniaudio.elf
|
||||||
|
OBJS = miniaudio_dreamcast_test.o romdisk.o
|
||||||
|
KOS_ROMDISK_DIR = romdisk
|
||||||
|
|
||||||
|
all: clean $(TARGET)
|
||||||
|
|
||||||
|
include $(KOS_BASE)/Makefile.rules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm -f $(TARGET) $(OBJS)
|
||||||
|
-rm -f romdisk.o romdisk.img
|
||||||
|
|
||||||
|
$(TARGET): $(OBJS)
|
||||||
|
kos-cc -o $(TARGET) $(OBJS) -lpthread
|
||||||
|
|
||||||
|
run: $(TARGET)
|
||||||
|
$(KOS_LOADER) $(TARGET)
|
||||||
|
|
||||||
|
dist: $(TARGET)
|
||||||
|
rm -f $(OBJS) romdisk.o romdisk.img
|
||||||
|
$(KOS_STRIP) $(TARGET)
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#set the KOS environtment variables
|
||||||
|
source /opt/toolchains/dc/kos/environ.sh
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#set the KOS environtment variables
|
||||||
|
source /opt/toolchains/dc/kos/environ.sh
|
||||||
|
|
||||||
|
make
|
||||||
|
|
||||||
|
exit
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
//#define MA_NO_THREADING
|
||||||
|
#include "../../miniaudio.c"
|
||||||
|
|
||||||
|
ma_context context;
|
||||||
|
ma_device device;
|
||||||
|
ma_waveform waveform;
|
||||||
|
ma_decoder decoder;
|
||||||
|
|
||||||
|
typedef struct allocation_callbacks_data
|
||||||
|
{
|
||||||
|
ma_uint32 allocationCount;
|
||||||
|
ma_uint32 totalAllocationSizeInBytes;
|
||||||
|
} allocation_callbacks_data;
|
||||||
|
|
||||||
|
static void* test_malloc(size_t sz, void* pUserData)
|
||||||
|
{
|
||||||
|
allocation_callbacks_data* pData = (allocation_callbacks_data*)pUserData;
|
||||||
|
|
||||||
|
printf("malloc(%d)\n", (int)sz);
|
||||||
|
|
||||||
|
pData->allocationCount += 1;
|
||||||
|
pData->totalAllocationSizeInBytes += sz;
|
||||||
|
return malloc(sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* test_realloc(void* p, size_t sz, void* pUserData)
|
||||||
|
{
|
||||||
|
allocation_callbacks_data* pData = (allocation_callbacks_data*)pUserData;
|
||||||
|
|
||||||
|
printf("realloc(%d)\n", (int)sz);
|
||||||
|
|
||||||
|
pData->totalAllocationSizeInBytes += sz; /* Don't actually know how to properly track this. Might need to keep a list of allocations. */
|
||||||
|
return realloc(p, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_free(void* p, void* pUserData)
|
||||||
|
{
|
||||||
|
allocation_callbacks_data* pData = (allocation_callbacks_data*)pUserData;
|
||||||
|
|
||||||
|
printf("free()\n");
|
||||||
|
|
||||||
|
pData->allocationCount -= 1;
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
allocation_callbacks_data allocationCallbacksData;
|
||||||
|
|
||||||
|
|
||||||
|
static void data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
|
||||||
|
{
|
||||||
|
(void)pDevice;
|
||||||
|
(void)pFramesOut;
|
||||||
|
(void)pFramesIn;
|
||||||
|
(void)frameCount;
|
||||||
|
|
||||||
|
//printf("data_callback(%d)\n", (int)frameCount);
|
||||||
|
//printf("internalePeriodSizeInFrames = %d\n", device.playback.internalPeriodSizeInFrames);
|
||||||
|
|
||||||
|
//ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL);
|
||||||
|
ma_decoder_read_pcm_frames(&decoder, pFramesOut, frameCount, NULL);
|
||||||
|
|
||||||
|
// Just applying a volume factor to make testing a bit more bearable...
|
||||||
|
ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, ma_device_get_format(pDevice, ma_device_type_playback), ma_device_get_channels(pDevice, ma_device_type_playback), 0.2f);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
ma_result result;
|
||||||
|
ma_context_config contextConfig;
|
||||||
|
ma_device_config deviceConfig;
|
||||||
|
uint32 prevButtons = 0;
|
||||||
|
|
||||||
|
dbgio_init();
|
||||||
|
dbgio_dev_select("fb");
|
||||||
|
|
||||||
|
|
||||||
|
vid_init(DEFAULT_VID_MODE, PM_RGB565);
|
||||||
|
vid_clear(64, 192, 64);
|
||||||
|
|
||||||
|
bfont_draw_str(vram_s + 20 * 640 + 20, 640, 0, "MINIAUDIO");
|
||||||
|
|
||||||
|
printf("sizeof(ma_context) = %d\n", (int)sizeof(ma_context));
|
||||||
|
printf("sizeof(ma_device) = %d\n", (int)sizeof(ma_device));
|
||||||
|
printf("sizeof(ma_device.playback) = %d\n", (int)sizeof(device.playback));
|
||||||
|
printf("sizeof(ma_data_converter) = %d\n", (int)sizeof(ma_data_converter));
|
||||||
|
|
||||||
|
|
||||||
|
memset(&allocationCallbacksData, 0, sizeof(allocationCallbacksData));
|
||||||
|
|
||||||
|
contextConfig = ma_context_config_init();
|
||||||
|
contextConfig.allocationCallbacks.onMalloc = test_malloc;
|
||||||
|
contextConfig.allocationCallbacks.onRealloc = test_realloc;
|
||||||
|
contextConfig.allocationCallbacks.onFree = test_free;
|
||||||
|
contextConfig.allocationCallbacks.pUserData = &allocationCallbacksData;
|
||||||
|
|
||||||
|
result = ma_context_init(NULL, 0, &contextConfig, &context);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
printf("ma_context_init() failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = 44100;
|
||||||
|
deviceConfig.periodSizeInFrames = 2048;
|
||||||
|
deviceConfig.noFixedSizedCallback = MA_TRUE; /* With the period size between 2048 and 16384 we can be guaranteed a fixed sized callback. */
|
||||||
|
deviceConfig.dataCallback = data_callback;
|
||||||
|
deviceConfig.pUserData = NULL;
|
||||||
|
result = ma_device_init(&context, &deviceConfig, &device);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
printf("ma_device_init() failed.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("internalPeriodSizeInFrames = %d\n", device.playback.internalPeriodSizeInFrames);
|
||||||
|
printf("format: %d %d\n", device.playback.format, device.playback.internalFormat);
|
||||||
|
printf("channels: %d %d\n", device.playback.channels, device.playback.internalChannels);
|
||||||
|
printf("rate: %d %d\n", device.sampleRate, device.playback.internalSampleRate);
|
||||||
|
|
||||||
|
/* Waveform. */
|
||||||
|
{
|
||||||
|
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, 0);
|
||||||
|
ma_decoder_init_file("/rd/test.mp3", &decoderConfig, &decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Allocation Count = %d\n", (int)allocationCallbacksData.allocationCount);
|
||||||
|
printf("Allocation Size = %d\n", (int)allocationCallbacksData.totalAllocationSizeInBytes);
|
||||||
|
|
||||||
|
result = ma_device_start(&device);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
printf("ma_device_start() failed.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
thd_sleep(1);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
maple_device_t* dev;
|
||||||
|
cont_state_t* st;
|
||||||
|
|
||||||
|
dev = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); /* 0 = first controller found */
|
||||||
|
if (dev) {
|
||||||
|
st = (cont_state_t*)maple_dev_status(dev);
|
||||||
|
if (st) {
|
||||||
|
uint32 buttons;
|
||||||
|
uint32 pressed;
|
||||||
|
uint32 released;
|
||||||
|
|
||||||
|
buttons = st->buttons; /* bitmask of CONT_* */
|
||||||
|
pressed = (buttons ^ prevButtons) & buttons;
|
||||||
|
released = (buttons ^ prevButtons) & prevButtons;
|
||||||
|
prevButtons = buttons;
|
||||||
|
|
||||||
|
(void)released;
|
||||||
|
|
||||||
|
if (pressed & CONT_START) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttons & CONT_DPAD_LEFT) {
|
||||||
|
/* Held */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_device_step(&device, MA_BLOCKING_MODE_NON_BLOCKING);
|
||||||
|
|
||||||
|
thd_pass(); /* yield */
|
||||||
|
//thd_sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ma_device_stop(&device);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
printf("ma_device_start() failed.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_device_uninit(&device);
|
||||||
|
ma_context_uninit(&context);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user