Update C sources, add new functions

This commit is contained in:
Milan Nikolic 2018-11-17 21:34:52 +01:00
parent adf3401c3c
commit 48689803b8
28 changed files with 2681 additions and 1918 deletions

198
raylib/external/dirent.h vendored Normal file
View file

@ -0,0 +1,198 @@
#ifndef DIRENT_H
#define DIRENT_H
/*
Declaration of POSIX directory browsing functions and types for Win32.
Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
History: Created March 1997. Updated June 2003.
Rights: See end of file.
*/
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct DIR DIR;
struct dirent
{
char *d_name;
};
DIR *opendir(const char *);
int closedir(DIR *);
struct dirent *readdir(DIR *);
void rewinddir(DIR *);
/*
Copyright Kevlin Henney, 1997, 2003. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose is hereby granted without fee, provided
that this copyright and permissions notice appear in all copies and
derivatives.
This software is supplied "as is" without express or implied warranty.
But that said, if there are any problems please get in touch.
*/
#ifdef __cplusplus
}
#endif
#endif // DIRENT_H
/*
Implementation of POSIX directory browsing functions and types for Win32.
Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
History: Created March 1997. Updated June 2003 and July 2012.
Rights: See end of file.
*/
#include <errno.h>
#include <io.h> /* _findfirst and _findnext set errno iff they return -1 */
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */
struct DIR
{
handle_type handle; /* -1 for failed rewind */
struct _finddata_t info;
struct dirent result; /* d_name null iff first time */
char *name; /* null-terminated char string */
};
DIR *opendir(const char *name)
{
DIR *dir = 0;
if(name && name[0])
{
size_t base_length = strlen(name);
const char *all = /* search pattern must end with suitable wildcard */
strchr("/\\", name[base_length - 1]) ? "*" : "/*";
if((dir = (DIR *) malloc(sizeof *dir)) != 0 &&
(dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0)
{
strcat(strcpy(dir->name, name), all);
if((dir->handle =
(handle_type) _findfirst(dir->name, &dir->info)) != -1)
{
dir->result.d_name = 0;
}
else /* rollback */
{
free(dir->name);
free(dir);
dir = 0;
}
}
else /* rollback */
{
free(dir);
dir = 0;
errno = ENOMEM;
}
}
else
{
errno = EINVAL;
}
return dir;
}
int closedir(DIR *dir)
{
int result = -1;
if(dir)
{
if(dir->handle != -1)
{
result = _findclose(dir->handle);
}
free(dir->name);
free(dir);
}
if(result == -1) /* map all errors to EBADF */
{
errno = EBADF;
}
return result;
}
struct dirent *readdir(DIR *dir)
{
struct dirent *result = 0;
if(dir && dir->handle != -1)
{
if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1)
{
result = &dir->result;
result->d_name = dir->info.name;
}
}
else
{
errno = EBADF;
}
return result;
}
void rewinddir(DIR *dir)
{
if(dir && dir->handle != -1)
{
_findclose(dir->handle);
dir->handle = (handle_type) _findfirst(dir->name, &dir->info);
dir->result.d_name = 0;
}
else
{
errno = EBADF;
}
}
#ifdef __cplusplus
}
#endif
/*
Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose is hereby granted without fee, provided
that this copyright and permissions notice appear in all copies and
derivatives.
This software is supplied "as is" without express or implied warranty.
But that said, if there are any problems please get in touch.
*/

File diff suppressed because it is too large Load diff

View file

@ -819,6 +819,12 @@ extern "C" {
* Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib).
*/
#define GLFW_HOVERED 0x0002000B
/*! @brief Input focus on calling show window hint and attribute
*
* Input focus [window hint](@ref GLFW_FOCUS_ON_SHOW_hint) or
* [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib).
*/
#define GLFW_FOCUS_ON_SHOW 0x0002000C
/*! @brief Framebuffer bit depth hint.
*
@ -3085,6 +3091,11 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window);
* hidden. If the window is already visible or is in full screen mode, this
* function does nothing.
*
* By default, windowed mode windows are focused when shown
* Set the [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) window hint
* to change this behavior for all newly created windows, or change the
* behavior for an existing window with @ref glfwSetWindowAttrib.
*
* @param[in] window The window to make visible.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
@ -3132,6 +3143,10 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window);
* initially created. Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to
* disable this behavior.
*
* Also by default, windowed mode windows are focused when shown
* with @ref glfwShowWindow. Set the
* [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) to disable this behavior.
*
* __Do not use this function__ to steal focus from other applications unless
* you are certain that is what the user wants. Focus stealing can be
* extremely disruptive.
@ -3306,8 +3321,9 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib);
*
* The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib),
* [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib),
* [GLFW_FLOATING](@ref GLFW_FLOATING_attrib) and
* [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib).
* [GLFW_FLOATING](@ref GLFW_FLOATING_attrib),
* [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and
* [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib).
*
* Some of these attributes are ignored for full screen windows. The new
* value will take effect if the window is later made windowed.

View file

@ -101,7 +101,10 @@ extern "C" {
#if defined(__OBJC__)
#import <Cocoa/Cocoa.h>
#else
typedef void* id;
// RAY: Added protection in case OBJC types defined
#if !OBJC_TYPES_DEFINED
typedef void* id;
#endif
#endif
#elif defined(GLFW_EXPOSE_NATIVE_X11)
#include <X11/Xlib.h>

View file

@ -32,7 +32,6 @@
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// Internal key state used for sticky keys
#define _GLFW_STICK 3
@ -1086,7 +1085,9 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string)
while (*c)
{
if (isxdigit(*c))
if ((*c >= '0' && *c <= '9') ||
(*c >= 'a' && *c <= 'f') ||
(*c >= 'A' && *c <= 'F'))
{
char line[1024];

View file

@ -267,6 +267,7 @@ struct _GLFWwndconfig
GLFWbool floating;
GLFWbool maximized;
GLFWbool centerCursor;
GLFWbool focusOnShow;
struct {
GLFWbool retina;
char frameName[256];
@ -372,6 +373,7 @@ struct _GLFWwindow
GLFWbool decorated;
GLFWbool autoIconify;
GLFWbool floating;
GLFWbool focusOnShow;
GLFWbool shouldClose;
void* userPointer;
GLFWvidmode videoMode;

View file

@ -242,7 +242,9 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(
#include "egl_context.h"
#include "osmesa_context.h"
#define _GLFW_WNDCLASSNAME L"GLFW30"
#if !defined(_GLFW_WNDCLASSNAME)
#define _GLFW_WNDCLASSNAME L"GLFW30"
#endif
#define _glfw_dlopen(name) LoadLibraryA(name)
#define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle)

View file

@ -201,6 +201,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
window->decorated = wndconfig.decorated;
window->autoIconify = wndconfig.autoIconify;
window->floating = wndconfig.floating;
window->focusOnShow = wndconfig.focusOnShow;
window->cursorMode = GLFW_CURSOR_NORMAL;
window->minwidth = GLFW_DONT_CARE;
@ -267,6 +268,7 @@ void glfwDefaultWindowHints(void)
_glfw.hints.window.focused = GLFW_TRUE;
_glfw.hints.window.autoIconify = GLFW_TRUE;
_glfw.hints.window.centerCursor = GLFW_TRUE;
_glfw.hints.window.focusOnShow = GLFW_TRUE;
// The default is 24 bits of color, 24 bits of depth and 8 bits of stencil,
// double buffered
@ -370,6 +372,9 @@ GLFWAPI void glfwWindowHint(int hint, int value)
case GLFW_CENTER_CURSOR:
_glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE;
return;
case GLFW_FOCUS_ON_SHOW:
_glfw.hints.window.focusOnShow = value ? GLFW_TRUE : GLFW_FALSE;
return;
case GLFW_CLIENT_API:
_glfw.hints.context.client = value;
return;
@ -755,7 +760,9 @@ GLFWAPI void glfwShowWindow(GLFWwindow* handle)
return;
_glfwPlatformShowWindow(window);
_glfwPlatformFocusWindow(window);
if (window->focusOnShow)
_glfwPlatformFocusWindow(window);
}
GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle)
@ -810,6 +817,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
return _glfwPlatformWindowMaximized(window);
case GLFW_HOVERED:
return _glfwPlatformWindowHovered(window);
case GLFW_FOCUS_ON_SHOW:
return window->focusOnShow;
case GLFW_TRANSPARENT_FRAMEBUFFER:
return _glfwPlatformFramebufferTransparent(window);
case GLFW_RESIZABLE:
@ -886,6 +895,8 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value)
if (!window->monitor)
_glfwPlatformSetWindowFloating(window, value);
}
else if (attrib == GLFW_FOCUS_ON_SHOW)
window->focusOnShow = value;
else
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib);
}

View file

@ -2672,8 +2672,9 @@ void _glfwPlatformPollEvents(void)
#if defined(__linux__)
_glfwDetectJoystickConnectionLinux();
#endif
int count = XPending(_glfw.x11.display);
while (count--)
XPending(_glfw.x11.display);
while (XQLength(_glfw.x11.display))
{
XEvent event;
XNextEvent(_glfw.x11.display, &event);

View file

@ -1,5 +1,5 @@
// Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file.
// mini_al - v0.8.8 - 2018-09-14
// mini_al - v0.8.10 - 2018-10-21
//
// David Reid - davidreidsoftware@gmail.com
@ -1114,6 +1114,11 @@ void mal_pcm_f32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dithe
void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode);
// Deinterleaves an interleaved buffer.
void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
// Interleaves a group of deinterleaved buffers.
void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -1629,6 +1634,7 @@ struct mal_context
mal_proc pa_stream_get_sample_spec;
mal_proc pa_stream_get_channel_map;
mal_proc pa_stream_get_buffer_attr;
mal_proc pa_stream_set_buffer_attr;
mal_proc pa_stream_get_device_name;
mal_proc pa_stream_set_write_callback;
mal_proc pa_stream_set_read_callback;
@ -4864,7 +4870,7 @@ mal_bool32 mal_device__get_current_frame__null(mal_device* pDevice, mal_uint32*
mal_assert(pCurrentPos != NULL);
*pCurrentPos = 0;
mal_uint64 currentFrameAbs = (mal_uint64)(mal_timer_get_time_in_seconds(&pDevice->null_device.timer) * pDevice->sampleRate) / pDevice->channels;
mal_uint64 currentFrameAbs = (mal_uint64)(mal_timer_get_time_in_seconds(&pDevice->null_device.timer) * pDevice->sampleRate);
*pCurrentPos = (mal_uint32)(currentFrameAbs % pDevice->bufferSizeInFrames);
return MAL_TRUE;
@ -4917,11 +4923,11 @@ mal_uint32 mal_device__wait_for_frames__null(mal_device* pDevice)
while (!pDevice->null_device.breakFromMainLoop) {
mal_uint32 framesAvailable = mal_device__get_available_frames__null(pDevice);
if (framesAvailable > 0) {
if (framesAvailable >= (pDevice->bufferSizeInFrames/pDevice->periods)) {
return framesAvailable;
}
mal_sleep(16);
mal_sleep(pDevice->bufferSizeInMilliseconds/pDevice->periods);
}
// We'll get here if the loop was terminated. Just return whatever's available.
@ -11784,6 +11790,7 @@ typedef mal_pa_stream_state_t (* mal_pa_stream_get_state_proc)
typedef const mal_pa_sample_spec* (* mal_pa_stream_get_sample_spec_proc) (mal_pa_stream* s);
typedef const mal_pa_channel_map* (* mal_pa_stream_get_channel_map_proc) (mal_pa_stream* s);
typedef const mal_pa_buffer_attr* (* mal_pa_stream_get_buffer_attr_proc) (mal_pa_stream* s);
typedef mal_pa_operation* (* mal_pa_stream_set_buffer_attr_proc) (mal_pa_stream* s, const mal_pa_buffer_attr* attr, mal_pa_stream_success_cb_t cb, void* userdata);
typedef const char* (* mal_pa_stream_get_device_name_proc) (mal_pa_stream* s);
typedef void (* mal_pa_stream_set_write_callback_proc) (mal_pa_stream* s, mal_pa_stream_request_cb_t cb, void* userdata);
typedef void (* mal_pa_stream_set_read_callback_proc) (mal_pa_stream* s, mal_pa_stream_request_cb_t cb, void* userdata);
@ -12305,6 +12312,10 @@ void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes,
mal_context* pContext = pDevice->pContext;
mal_assert(pContext != NULL);
#ifdef MAL_DEBUG_OUTPUT
printf("[PulseAudio] write_callback: sizeInBytes=%d\n", (int)sizeInBytes);
#endif
size_t bytesRemaining = sizeInBytes;
while (bytesRemaining > 0) {
size_t bytesToReadFromClient = bytesRemaining;
@ -12319,19 +12330,35 @@ void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes,
return;
}
#ifdef MAL_DEBUG_OUTPUT
printf(" bytesToReadFromClient=%d", (int)bytesToReadFromClient);
#endif
if (pBuffer != NULL && bytesToReadFromClient > 0) {
mal_uint32 framesToReadFromClient = (mal_uint32)bytesToReadFromClient / (pDevice->internalChannels*mal_get_bytes_per_sample(pDevice->internalFormat));
if (framesToReadFromClient > 0) {
mal_device__read_frames_from_client(pDevice, framesToReadFromClient, pBuffer);
#ifdef MAL_DEBUG_OUTPUT
printf(", framesToReadFromClient=%d\n", (int)framesToReadFromClient);
#endif
error = ((mal_pa_stream_write_proc)pContext->pulse.pa_stream_write)((mal_pa_stream*)pDevice->pulse.pStream, pBuffer, bytesToReadFromClient, NULL, 0, MAL_PA_SEEK_RELATIVE);
if (error < 0) {
mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to write data to the PulseAudio stream.", mal_result_from_pulse(error));
return;
}
} else {
#ifdef MAL_DEBUG_OUTPUT
printf(", framesToReadFromClient=0\n");
#endif
}
bytesRemaining -= bytesToReadFromClient;
} else {
#ifdef MAL_DEBUG_OUTPUT
printf(", framesToReadFromClient=0\n");
#endif
}
}
}
@ -12460,6 +12487,7 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
mal_pa_sample_spec ss;
mal_pa_channel_map cmap;
mal_pa_buffer_attr attr;
mal_pa_stream_flags_t streamFlags;
const mal_pa_sample_spec* pActualSS = NULL;
const mal_pa_channel_map* pActualCMap = NULL;
@ -12533,53 +12561,6 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
}
#if 0
mal_pa_sample_spec deviceSS;
mal_pa_channel_map deviceCMap;
if (type == mal_device_type_playback) {
deviceSS = sinkInfo.sample_spec;
deviceCMap = sinkInfo.channel_map;
} else {
deviceSS = sourceInfo.sample_spec;
deviceCMap = sourceInfo.channel_map;
}
if (pDevice->usingDefaultFormat) {
ss.format = deviceSS.format;
} else {
ss.format = mal_format_to_pulse(pConfig->format);
}
if (ss.format == MAL_PA_SAMPLE_INVALID) {
ss.format = MAL_PA_SAMPLE_S16LE;
}
if (pDevice->usingDefaultChannels) {
ss.channels = deviceSS.channels;
} else {
ss.channels = pConfig->channels;
}
if (pDevice->usingDefaultSampleRate) {
ss.rate = deviceSS.rate;
} else {
ss.rate = pConfig->sampleRate;
}
if (pDevice->usingDefaultChannelMap) {
cmap = deviceCMap;
} else {
cmap.channels = pConfig->channels;
for (mal_uint32 iChannel = 0; iChannel < pConfig->channels; ++iChannel) {
cmap.map[iChannel] = mal_channel_position_to_pulse(pConfig->channelMap[iChannel]);
}
if (((mal_pa_channel_map_valid_proc)pContext->pulse.pa_channel_map_valid)(&cmap) == 0 || ((mal_pa_channel_map_compatible_proc)pContext->pulse.pa_channel_map_compatible)(&cmap, &ss) == 0) {
((mal_pa_channel_map_init_extend_proc)pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MAL_PA_CHANNEL_MAP_DEFAULT); // The channel map is invalid, so just fall back to the default.
}
}
#else
if (type == mal_device_type_playback) {
ss = sinkInfo.sample_spec;
cmap = sinkInfo.channel_map;
@ -12587,7 +12568,7 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
ss = sourceInfo.sample_spec;
cmap = sourceInfo.channel_map;
}
#endif
// Buffer size.
bufferSizeInFrames = pDevice->bufferSizeInFrames;
@ -12606,10 +12587,14 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
}
attr.maxlength = bufferSizeInFrames * mal_get_bytes_per_sample(mal_format_from_pulse(ss.format))*ss.channels;
attr.tlength = attr.maxlength / pConfig->periods;
attr.tlength = attr.maxlength;
attr.prebuf = (mal_uint32)-1;
attr.minreq = attr.tlength;
attr.fragsize = attr.tlength;
attr.minreq = attr.maxlength / pConfig->periods;
attr.fragsize = attr.maxlength / pConfig->periods;
#ifdef MAL_DEBUG_OUTPUT
printf("[PulseAudio] attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; bufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, bufferSizeInFrames);
#endif
char streamName[256];
if (pConfig->pulse.pStreamName != NULL) {
@ -12628,11 +12613,15 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
}
streamFlags = MAL_PA_STREAM_START_CORKED;
if (dev != NULL) {
streamFlags |= MAL_PA_STREAM_DONT_MOVE | MAL_PA_STREAM_FIX_FORMAT | MAL_PA_STREAM_FIX_RATE | MAL_PA_STREAM_FIX_CHANNELS;
}
if (type == mal_device_type_playback) {
error = ((mal_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, MAL_PA_STREAM_START_CORKED, NULL, NULL);
error = ((mal_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, streamFlags, NULL, NULL);
} else {
error = ((mal_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, MAL_PA_STREAM_START_CORKED);
error = ((mal_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, streamFlags);
}
if (error != MAL_PA_OK) {
@ -12652,6 +12641,21 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
// Internal format.
pActualSS = ((mal_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((mal_pa_stream*)pDevice->pulse.pStream);
if (pActualSS != NULL) {
// If anything has changed between the requested and the actual sample spec, we need to update the buffer.
if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
attr.maxlength = bufferSizeInFrames * mal_get_bytes_per_sample(mal_format_from_pulse(pActualSS->format))*pActualSS->channels;
attr.tlength = attr.maxlength;
attr.prebuf = (mal_uint32)-1;
attr.minreq = attr.maxlength / pConfig->periods;
attr.fragsize = attr.maxlength / pConfig->periods;
pOP = ((mal_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((mal_pa_stream*)pDevice->pulse.pStream, &attr, NULL, NULL);
if (pOP != NULL) {
mal_device__wait_for_operation__pulse(pDevice, pOP);
((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
}
}
ss = *pActualSS;
}
@ -12680,6 +12684,10 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
pDevice->bufferSizeInFrames = attr.maxlength / (mal_get_bytes_per_sample(pDevice->internalFormat)*pDevice->internalChannels);
pDevice->periods = attr.maxlength / attr.tlength;
#ifdef MAL_DEBUG_OUTPUT
printf("[PulseAudio] actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; pDevice->bufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->bufferSizeInFrames);
#endif
// Grab the name of the device if we can.
dev = ((mal_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((mal_pa_stream*)pDevice->pulse.pStream);
@ -12930,6 +12938,7 @@ mal_result mal_context_init__pulse(mal_context* pContext)
pContext->pulse.pa_stream_get_sample_spec = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
pContext->pulse.pa_stream_get_channel_map = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_channel_map");
pContext->pulse.pa_stream_get_buffer_attr = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
pContext->pulse.pa_stream_set_buffer_attr = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
pContext->pulse.pa_stream_get_device_name = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_device_name");
pContext->pulse.pa_stream_set_write_callback = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_write_callback");
pContext->pulse.pa_stream_set_read_callback = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_read_callback");
@ -12972,6 +12981,7 @@ mal_result mal_context_init__pulse(mal_context* pContext)
mal_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
mal_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
mal_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
mal_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
mal_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
mal_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
mal_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
@ -13013,6 +13023,7 @@ mal_result mal_context_init__pulse(mal_context* pContext)
pContext->pulse.pa_stream_get_sample_spec = (mal_proc)_pa_stream_get_sample_spec;
pContext->pulse.pa_stream_get_channel_map = (mal_proc)_pa_stream_get_channel_map;
pContext->pulse.pa_stream_get_buffer_attr = (mal_proc)_pa_stream_get_buffer_attr;
pContext->pulse.pa_stream_set_buffer_attr = (mal_proc)_pa_stream_set_buffer_attr;
pContext->pulse.pa_stream_get_device_name = (mal_proc)_pa_stream_get_device_name;
pContext->pulse.pa_stream_set_write_callback = (mal_proc)_pa_stream_set_write_callback;
pContext->pulse.pa_stream_set_read_callback = (mal_proc)_pa_stream_set_read_callback;
@ -14900,27 +14911,62 @@ OSStatus mal_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* p
#if defined(MAL_DEBUG_OUTPUT)
printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
#endif
// For now we can assume everything is interleaved.
for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
mal_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
if (frameCountForThisBuffer > 0) {
mal_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData);
}
#if defined(MAL_DEBUG_OUTPUT)
printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
#endif
} else {
// This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
// not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams. We just
// output silence here.
mal_zero_memory(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
#if defined(MAL_DEBUG_OUTPUT)
printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
#endif
// We need to check whether or not we are outputting interleaved or non-interleaved samples. The
// way we do this is slightly different for each type.
mal_stream_layout layout = mal_stream_layout_interleaved;
if (pBufferList->mBuffers[0].mNumberChannels != pDevice->internalChannels) {
layout = mal_stream_layout_deinterleaved;
}
if (layout == mal_stream_layout_interleaved) {
// For now we can assume everything is interleaved.
for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
mal_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
if (frameCountForThisBuffer > 0) {
mal_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData);
}
#if defined(MAL_DEBUG_OUTPUT)
printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
#endif
} else {
// This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
// not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams. We just
// output silence here.
mal_zero_memory(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
#if defined(MAL_DEBUG_OUTPUT)
printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
#endif
}
}
} else {
// This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This
// assumes each buffer is the same size.
mal_uint8 tempBuffer[4096];
for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->internalChannels) {
mal_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
mal_uint32 framesRemaining = frameCountPerBuffer;
while (framesRemaining > 0) {
mal_uint32 framesToRead = sizeof(tempBuffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
if (framesToRead > framesRemaining) {
framesToRead = framesRemaining;
}
mal_device__read_frames_from_client(pDevice, framesToRead, tempBuffer);
void* ppDeinterleavedBuffers[MAL_MAX_CHANNELS];
for (mal_uint32 iChannel = 0; iChannel < pDevice->internalChannels; ++iChannel) {
ppDeinterleavedBuffers[iChannel] = (void*)mal_offset_ptr(pBufferList->mBuffers[iBuffer].mData, (frameCountPerBuffer - framesRemaining) * mal_get_bytes_per_sample(pDevice->internalFormat));
}
mal_deinterleave_pcm_frames(pDevice->internalFormat, pDevice->internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
framesRemaining -= framesToRead;
}
}
}
@ -14941,6 +14987,13 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA
AudioBufferList* pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
mal_assert(pRenderedBufferList);
// We need to check whether or not we are outputting interleaved or non-interleaved samples. The
// way we do this is slightly different for each type.
mal_stream_layout layout = mal_stream_layout_interleaved;
if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->internalChannels) {
layout = mal_stream_layout_deinterleaved;
}
#if defined(MAL_DEBUG_OUTPUT)
printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
#endif
@ -14953,16 +15006,58 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA
return status;
}
// For now we can assume everything is interleaved.
for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
mal_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
#if defined(MAL_DEBUG_OUTPUT)
printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
#endif
} else {
// This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
// not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams.
if (layout == mal_stream_layout_interleaved) {
for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
mal_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
#if defined(MAL_DEBUG_OUTPUT)
printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
#endif
} else {
// This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
// not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams.
mal_uint8 silentBuffer[4096];
mal_zero_memory(silentBuffer, sizeof(silentBuffer));
mal_uint32 framesRemaining = frameCount;
while (framesRemaining > 0) {
mal_uint32 framesToSend = sizeof(silentBuffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
if (framesToSend > framesRemaining) {
framesToSend = framesRemaining;
}
mal_device__send_frames_to_client(pDevice, framesToSend, silentBuffer);
framesRemaining -= framesToSend;
}
#if defined(MAL_DEBUG_OUTPUT)
printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderBufferList->mBuffers[iBuffer].mNumberChannels, pRenderBufferList->mBuffers[iBuffer].mDataByteSize);
#endif
}
}
} else {
// This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This
// assumes each buffer is the same size.
mal_uint8 tempBuffer[4096];
for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->internalChannels) {
mal_uint32 framesRemaining = frameCount;
while (framesRemaining > 0) {
mal_uint32 framesToSend = sizeof(tempBuffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
if (framesToSend > framesRemaining) {
framesToSend = framesRemaining;
}
void* ppDeinterleavedBuffers[MAL_MAX_CHANNELS];
for (mal_uint32 iChannel = 0; iChannel < pDevice->internalChannels; ++iChannel) {
ppDeinterleavedBuffers[iChannel] = (void*)mal_offset_ptr(pRenderedBufferList->mBuffers[iBuffer].mData, (frameCount - framesRemaining) * mal_get_bytes_per_sample(pDevice->internalFormat));
}
mal_interleave_pcm_frames(pDevice->internalFormat, pDevice->internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
mal_device__send_frames_to_client(pDevice, framesToSend, tempBuffer);
framesRemaining -= framesToSend;
}
}
}
@ -14976,42 +15071,53 @@ void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPro
mal_device* pDevice = (mal_device*)pUserData;
mal_assert(pDevice != NULL);
UInt32 isRunning;
UInt32 isRunningSize = sizeof(isRunning);
OSStatus status = ((mal_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
if (status != noErr) {
return; // Don't really know what to do in this case... just ignore it, I suppose...
}
if (!isRunning) {
// The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider:
//
// 1) When the device is unplugged, this will be called _before_ the default device change notification.
// 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
//
// For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag.
if (pDevice->isDefaultDevice && mal_device__get_state(pDevice) != MAL_STATE_STOPPING && mal_device__get_state(pDevice) != MAL_STATE_STOPPED) {
// It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
// via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
// device to be seamless to the client (we don't want them receiving the onStop event and thinking that the device has stopped when it
// hasn't!).
if (pDevice->coreaudio.isSwitchingDevice) {
return;
}
// Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio
// will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most
// likely be successful in switching to the new device.
//
// TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted.
return;
}
// Getting here means we need to stop the device.
// There's been a report of a deadlock here when triggered by mal_device_uninit(). It looks like
// AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in mal_device_uninit)
// can try waiting on the same lock. I'm going to try working around this by not calling any Core
// Audio APIs in the callback when the device has been stopped or uninitialized.
if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED || mal_device__get_state(pDevice) == MAL_STATE_STOPPING) {
mal_stop_proc onStop = pDevice->onStop;
if (onStop) {
onStop(pDevice);
}
} else {
UInt32 isRunning;
UInt32 isRunningSize = sizeof(isRunning);
OSStatus status = ((mal_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
if (status != noErr) {
return; // Don't really know what to do in this case... just ignore it, I suppose...
}
if (!isRunning) {
// The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider:
//
// 1) When the device is unplugged, this will be called _before_ the default device change notification.
// 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
//
// For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag.
if (pDevice->isDefaultDevice && mal_device__get_state(pDevice) != MAL_STATE_STOPPING && mal_device__get_state(pDevice) != MAL_STATE_STOPPED) {
// It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
// via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
// device to be seamless to the client (we don't want them receiving the onStop event and thinking that the device has stopped when it
// hasn't!).
if (pDevice->coreaudio.isSwitchingDevice) {
return;
}
// Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio
// will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most
// likely be successful in switching to the new device.
//
// TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted.
return;
}
// Getting here means we need to stop the device.
mal_stop_proc onStop = pDevice->onStop;
if (onStop) {
onStop(pDevice);
}
}
}
}
@ -15288,11 +15394,14 @@ mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device
if (result != MAL_SUCCESS) {
return result;
}
pData->bufferSizeInFramesOut = actualBufferSizeInFrames * pData->periodsOut;
#else
actualBufferSizeInFrames = 4096;
pData->bufferSizeInFramesOut = actualBufferSizeInFrames;
#endif
pData->bufferSizeInFramesOut = actualBufferSizeInFrames * pData->periodsOut;
// During testing I discovered that the buffer size can be too big. You'll get an error like this:
//
@ -15556,6 +15665,15 @@ mal_result mal_context_uninit__coreaudio(mal_context* pContext)
mal_result mal_context_init__coreaudio(mal_context* pContext)
{
mal_assert(pContext != NULL);
#if defined(MAL_APPLE_MOBILE)
@autoreleasepool {
AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
mal_assert(pAudioSession != NULL);
[pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord error:nil];
}
#endif
#if !defined(MAL_NO_RUNTIME_LINKING) && !defined(MAL_APPLE_MOBILE)
pContext->coreaudio.hCoreFoundation = mal_dlopen("CoreFoundation.framework/CoreFoundation");
@ -19725,7 +19843,7 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
// SDL wants the buffer size to be a power of 2. The SDL_AudioSpec property for this is only a Uint16, so we need
// to explicitly clamp this because it will be easy to overflow.
mal_uint32 bufferSize = pConfig->bufferSizeInFrames;
mal_uint32 bufferSize = pDevice->bufferSizeInFrames;
if (bufferSize > 32768) {
bufferSize = 32768;
} else {
@ -19760,7 +19878,7 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE);
if (pDevice->sdl.deviceID == 0) {
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL2 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
} else
#endif
@ -19778,10 +19896,12 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
desiredSpec.format = MAL_AUDIO_S16;
}
pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec);
if (pDevice->sdl.deviceID != 0) {
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
int deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec);
if (deviceID < 0) {
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL1 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
pDevice->sdl.deviceID = (mal_uint32)deviceID;
}
pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format);
@ -20429,6 +20549,14 @@ mal_result mal_context_init(const mal_backend backends[], mal_uint32 backendCoun
mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. mal_context_get_device_info() is not thread safe.", MAL_FAILED_TO_CREATE_MUTEX);
}
#ifdef MAL_DEBUG_OUTPUT
printf("[mini_al] Endian: %s\n", mal_is_little_endian() ? "LE" : "BE");
printf("[mini_al] SSE2: %s\n", mal_has_sse2() ? "YES" : "NO");
printf("[mini_al] AVX2: %s\n", mal_has_avx2() ? "YES" : "NO");
printf("[mini_al] AVX512F: %s\n", mal_has_avx512f() ? "YES" : "NO");
printf("[mini_al] NEON: %s\n", mal_has_neon() ? "YES" : "NO");
#endif
pContext->backend = backend;
return result;
}
@ -20767,7 +20895,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
#ifdef MAL_DEBUG_OUTPUT
printf("[WASAPI] %s (%s)\n", pDevice->name, (pDevice->type == mal_device_type_playback) ? "Playback" : "Capture");
printf("[%s] %s (%s)\n", mal_get_backend_name(pDevice->pContext->backend), pDevice->name, (pDevice->type == mal_device_type_playback) ? "Playback" : "Capture");
printf(" Format: %s -> %s\n", mal_get_format_name(pDevice->format), mal_get_format_name(pDevice->internalFormat));
printf(" Channels: %d -> %d\n", pDevice->channels, pDevice->internalChannels);
printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->internalSampleRate);
@ -20949,7 +21077,7 @@ mal_result mal_device_stop(mal_device* pDevice)
// Asynchronous backends need to be handled differently.
if (mal_context_is_backend_asynchronous(pDevice->pContext)) {
pDevice->pContext->onDeviceStop(pDevice);
result = pDevice->pContext->onDeviceStop(pDevice);
} else {
// Synchronous backends.
@ -26138,6 +26266,92 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form
}
}
void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
{
if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
return; // Invalid args.
}
// For efficiency we do this per format.
switch (format) {
case mal_format_s16:
{
const mal_int16* pSrcS16 = (const mal_int16*)pInterleavedPCMFrames;
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
mal_int16* pDstS16 = (mal_int16*)ppDeinterleavedPCMFrames[iChannel];
pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
}
}
} break;
case mal_format_f32:
{
const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
}
}
} break;
default:
{
mal_uint32 sampleSizeInBytes = mal_get_bytes_per_sample(format);
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
void* pDst = mal_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
const void* pSrc = mal_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
memcpy(pDst, pSrc, sampleSizeInBytes);
}
}
} break;
}
}
void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
{
switch (format)
{
case mal_format_s16:
{
mal_int16* pDstS16 = (mal_int16*)pInterleavedPCMFrames;
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
const mal_int16* pSrcS16 = (const mal_int16*)ppDeinterleavedPCMFrames[iChannel];
pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
}
}
} break;
case mal_format_f32:
{
float* pDstF32 = (float*)pInterleavedPCMFrames;
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
}
}
} break;
default:
{
mal_uint32 sampleSizeInBytes = mal_get_bytes_per_sample(format);
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
void* pDst = mal_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
const void* pSrc = mal_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
memcpy(pDst, pSrc, sampleSizeInBytes);
}
}
} break;
}
}
typedef struct
@ -28296,6 +28510,13 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount
// REVISION HISTORY
// ================
//
// v0.8.10 - 2018-10-21
// - Core Audio: Fix a hang when uninitializing a device.
// - Fix a bug where an incorrect value is returned from mal_device_stop().
//
// v0.8.9 - 2018-09-28
// - Fix a bug with the SDL backend where device initialization fails.
//
// v0.8.8 - 2018-09-14
// - Fix Linux build with the ALSA backend.
// - Minor documentation fix.

View file

@ -193,7 +193,7 @@
#undef __forceinline
#endif
#define __forceinline
//#define alloca __builtin_alloca
#define alloca __builtin_alloca
#elif !defined(_MSC_VER)
#if __GNUC__
#define __forceinline inline