Update C sources, add new functions
This commit is contained in:
parent
adf3401c3c
commit
48689803b8
28 changed files with 2681 additions and 1918 deletions
198
raylib/external/dirent.h
vendored
Normal file
198
raylib/external/dirent.h
vendored
Normal 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.
|
||||
|
||||
*/
|
660
raylib/external/dr_mp3.h
vendored
660
raylib/external/dr_mp3.h
vendored
File diff suppressed because it is too large
Load diff
20
raylib/external/glfw/include/GLFW/glfw3.h
vendored
20
raylib/external/glfw/include/GLFW/glfw3.h
vendored
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
5
raylib/external/glfw/src/input.c
vendored
5
raylib/external/glfw/src/input.c
vendored
|
@ -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];
|
||||
|
||||
|
|
2
raylib/external/glfw/src/internal.h
vendored
2
raylib/external/glfw/src/internal.h
vendored
|
@ -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;
|
||||
|
|
4
raylib/external/glfw/src/win32_platform.h
vendored
4
raylib/external/glfw/src/win32_platform.h
vendored
|
@ -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)
|
||||
|
|
13
raylib/external/glfw/src/window.c
vendored
13
raylib/external/glfw/src/window.c
vendored
|
@ -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);
|
||||
}
|
||||
|
|
5
raylib/external/glfw/src/x11_window.c
vendored
5
raylib/external/glfw/src/x11_window.c
vendored
|
@ -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);
|
||||
|
|
477
raylib/external/mini_al.h
vendored
477
raylib/external/mini_al.h
vendored
|
@ -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.
|
||||
|
|
2
raylib/external/stb_vorbis.c
vendored
2
raylib/external/stb_vorbis.c
vendored
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue