From 22da9087b1e9ea8df1fa00778b70c115d34f0c03 Mon Sep 17 00:00:00 2001 From: Kirottu <56396750+Kirottu@users.noreply.github.com> Date: Wed, 6 Jan 2021 21:45:32 +0200 Subject: [PATCH 01/23] Include SUPPORT_DATA_STORAGE flag for building with CMake (#1515) * Update CMakeOptions.txt * Update config.h.in --- src/CMakeOptions.txt | 1 + src/config.h.in | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/CMakeOptions.txt b/src/CMakeOptions.txt index a0f864457..7eff4c89c 100644 --- a/src/CMakeOptions.txt +++ b/src/CMakeOptions.txt @@ -32,6 +32,7 @@ option(SUPPORT_GIF_RECORDING "Allow automatic gif recording of current screen pr option(SUPPORT_BUSY_WAIT_LOOP "Use busy wait loop for timing sync instead of a high-resolution timer" OFF) option(SUPPORT_EVENTS_WAITING "Wait for events passively (sleeping while no events) instead of polling them actively every frame" OFF) option(SUPPORT_HIGH_DPI "Support high DPI displays" OFF) +option(SUPPORT_DATA_STORAGE "Support for persistent data storage" ON) # rlgl.h option(SUPPORT_VR_SIMULATOR "Support VR simulation functionality (stereo rendering)" ON) diff --git a/src/config.h.in b/src/config.h.in index 5f645233e..4cb217b5c 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -21,6 +21,8 @@ #cmakedefine SUPPORT_HIGH_DPI 1 // Support CompressData() and DecompressData() functions #cmakedefine SUPPORT_COMPRESSION_API 1 +// Support for persistent data storage +#cmakedefine SUPPORT_DATA_STORAGE 1 // rlgl.h // Support VR simulation functionality (stereo rendering) From 49f9bff26045a171c902399e1d8c478857b8c777 Mon Sep 17 00:00:00 2001 From: badlydrawnrod Date: Wed, 6 Jan 2021 19:46:12 +0000 Subject: [PATCH 02/23] Fix keyboard state change detection on RPI (#1488) * Fix keyboard state change detection on RPI * Rework RaspberryPi evdev keyboard input. - Extract evdev keyboard handling into PollKeyboardEvents() - Move keyboard polling to main thread - Rename EventThreadSpawn() to ConfigureEvdevDevice() as it doesn't necessarily spawn threads - Remove unused code (KeyEventFifo and lastKeyPressed) * Replace tabs with 4 spaces. --- src/core.c | 150 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 84 insertions(+), 66 deletions(-) diff --git a/src/core.c b/src/core.c index e9c682757..32b30191b 100644 --- a/src/core.c +++ b/src/core.c @@ -333,12 +333,6 @@ typedef struct { bool isKeyboard; // True if device has letter keycodes bool isGamepad; // True if device has gamepad buttons } InputEventWorker; - -typedef struct { - int contents[8]; // Key events FIFO contents (8 positions) - char head; // Key events FIFO head position - char tail; // Key events FIFO tail position -} KeyEventFifo; #endif typedef struct { int x; int y; } Point; @@ -420,7 +414,7 @@ typedef struct CoreData { #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) int defaultMode; // Default keyboard mode struct termios defaultSettings; // Default keyboard settings - KeyEventFifo lastKeyPressed; // Buffer for holding keydown events as they arrive (Needed due to multitreading of event workers) + int fd; // File descriptor for the evdev keyboard #endif } Keyboard; struct { @@ -559,7 +553,8 @@ static void RestoreTerminal(void); // Restore terminal #endif static void InitEvdevInput(void); // Evdev inputs initialization -static void EventThreadSpawn(char *device); // Identifies a input device and spawns a thread to handle it if needed +static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate +static void PollKeyboardEvents(void); // Process evdev keyboard events. static void *EventThread(void *arg); // Input device events reading thread static void InitGamepad(void); // Init raw gamepad input @@ -899,6 +894,13 @@ void CloseWindow(void) CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called + // Close the evdev keyboard + if (CORE.Input.Keyboard.fd != -1) + { + close(CORE.Input.Keyboard.fd); + CORE.Input.Keyboard.fd = -1; + } + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) { if (CORE.Input.eventWorker[i].threadId) @@ -906,6 +908,7 @@ void CloseWindow(void) pthread_join(CORE.Input.eventWorker[i].threadId, NULL); } } + if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL); #endif @@ -4274,15 +4277,8 @@ static void PollInputEvents(void) #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) // Register previous keys states for (int i = 0; i < 512; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; - - // Grab a keypress from the evdev fifo if avalable - if (CORE.Input.Keyboard.lastKeyPressed.head != CORE.Input.Keyboard.lastKeyPressed.tail) - { - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = CORE.Input.Keyboard.lastKeyPressed.contents[CORE.Input.Keyboard.lastKeyPressed.tail]; // Read the key from the buffer - CORE.Input.Keyboard.keyPressedQueueCount++; - - CORE.Input.Keyboard.lastKeyPressed.tail = (CORE.Input.Keyboard.lastKeyPressed.tail + 1) & 0x07; // Increment the tail pointer forwards and binary wraparound after 7 (fifo is 8 elements long) - } + + PollKeyboardEvents(); // Register previous mouse states CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; @@ -5352,6 +5348,9 @@ static void InitEvdevInput(void) char path[MAX_FILEPATH_LENGTH]; DIR *directory; struct dirent *entity; + + // Initialise keyboard file descriptor + CORE.Input.Keyboard.fd = -1; // Reset variables for (int i = 0; i < MAX_TOUCH_POINTS; ++i) @@ -5360,10 +5359,6 @@ static void InitEvdevInput(void) CORE.Input.Touch.position[i].y = -1; } - // Reset keypress buffer - CORE.Input.Keyboard.lastKeyPressed.head = 0; - CORE.Input.Keyboard.lastKeyPressed.tail = 0; - // Reset keyboard key state for (int i = 0; i < 512; i++) CORE.Input.Keyboard.currentKeyState[i] = 0; @@ -5377,7 +5372,7 @@ static void InitEvdevInput(void) if (strncmp("event", entity->d_name, strlen("event")) == 0) // Search for devices named "event*" { sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name); - EventThreadSpawn(path); // Identify the device and spawn a thread for it + ConfigureEvdevDevice(path); // Configure the device if appropriate } } @@ -5386,8 +5381,8 @@ static void InitEvdevInput(void) else TRACELOG(LOG_WARNING, "RPI: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH); } -// Identifies a input device and spawns a thread to handle it if needed -static void EventThreadSpawn(char *device) +// Identifies a input device and configures it for use if appropriate +static void ConfigureEvdevDevice(char *device) { #define BITS_PER_LONG (8*sizeof(long)) #define NBITS(x) ((((x) - 1)/BITS_PER_LONG) + 1) @@ -5459,7 +5454,7 @@ static void EventThreadSpawn(char *device) // Identify the device //------------------------------------------------------------------------------------------------------- - ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the avalable device properties + ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the available device properties // Check for absolute input devices if (TEST_BIT(evBits, EV_ABS)) @@ -5535,15 +5530,22 @@ static void EventThreadSpawn(char *device) // Decide what to do with the device //------------------------------------------------------------------------------------------------------- - if (worker->isTouch || worker->isMouse || worker->isKeyboard) + if (worker->isKeyboard && CORE.Input.Keyboard.fd == -1) + { + // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a + // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate + // threads so that they don't drop events when the frame rate is slow. + TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device); + CORE.Input.Keyboard.fd = worker->fd; + } + else if (worker->isTouch || worker->isMouse) { // Looks like an interesting device - TRACELOG(LOG_INFO, "RPI: Opening input device: %s (%s%s%s%s%s)", device, + TRACELOG(LOG_INFO, "RPI: Opening input device: %s (%s%s%s%s)", device, worker->isMouse? "mouse " : "", worker->isMultitouch? "multitouch " : "", worker->isTouch? "touchscreen " : "", - worker->isGamepad? "gamepad " : "", - worker->isKeyboard? "keyboard " : ""); + worker->isGamepad? "gamepad " : ""); // Create a thread for this device int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker); @@ -5563,7 +5565,7 @@ static void EventThreadSpawn(char *device) if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum; } - // Find toucnscreens with lower indexes + // Find touchscreens with lower indexes for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) { if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber)) @@ -5582,8 +5584,7 @@ static void EventThreadSpawn(char *device) //------------------------------------------------------------------------------------------------------- } -// Input device events reading thread -static void *EventThread(void *arg) +static void PollKeyboardEvents(void) { // Scancode to keycode mapping for US keyboards // TODO: Probably replace this with a keymap from the X11 to get the correct regional map for the keyboard: @@ -5605,12 +5606,62 @@ static void *EventThread(void *arg) 227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242, 243,244,245,246,247,248,0,0,0,0,0,0,0, }; + int fd = CORE.Input.Keyboard.fd; + if (fd == -1) return; + + struct input_event event; + int keycode; + + // Try to read data from the keyboard and only continue if successful + while (read(fd, &event, sizeof(event)) == (int)sizeof(event)) + { + // Button parsing + if (event.type == EV_KEY) + { + // Keyboard button parsing + if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 + { + keycode = keymap_US[event.code & 0xFF]; // The code we get is a scancode so we look up the apropriate keycode + + // Make sure we got a valid keycode + if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState))) + { + // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt + // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL, + // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat + CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1)? 1 : 0; + if (event.value >= 1) + { + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; // Register last key pressed + CORE.Input.Keyboard.keyPressedQueueCount++; + } + + #if defined(SUPPORT_SCREEN_CAPTURE) + // Check screen capture key (raylib key: KEY_F12) + if (CORE.Input.Keyboard.currentKeyState[301] == 1) + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } + #endif + + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; + + TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", event.value == 0 ? "UP":"DOWN", event.code, keycode); + } + } + } + } +} + +// Input device events reading thread +static void *EventThread(void *arg) +{ struct input_event event; InputEventWorker *worker = (InputEventWorker *)arg; int touchAction = -1; bool gestureUpdate = false; - int keycode; while (!CORE.Window.shouldClose) { @@ -5710,39 +5761,6 @@ static void *EventThread(void *arg) if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_RIGHT_BUTTON] = event.value; if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_MIDDLE_BUTTON] = event.value; - - // Keyboard button parsing - if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 - { - keycode = keymap_US[event.code & 0xFF]; // The code we get is a scancode so we look up the apropriate keycode - - // Make sure we got a valid keycode - if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState))) - { - // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt - // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL, - // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat - CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1)? 1 : 0; - if (event.value >= 1) - { - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; // Register last key pressed - CORE.Input.Keyboard.keyPressedQueueCount++; - } - - #if defined(SUPPORT_SCREEN_CAPTURE) - // Check screen capture key (raylib key: KEY_F12) - if (CORE.Input.Keyboard.currentKeyState[301] == 1) - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } - #endif - - if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; - - TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", event.value == 0 ? "UP":"DOWN", event.code, keycode); - } - } } // Screen confinement From 33ed14230608667c18b7d28d2f03722354e1c1a2 Mon Sep 17 00:00:00 2001 From: Dan J <2598904+resttime@users.noreply.github.com> Date: Thu, 7 Jan 2021 03:02:48 -0600 Subject: [PATCH 03/23] Add info to readme for conan dependency manager (#1516) Co-authored-by: resttime --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index bbf7b99cd..28d3bb815 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,14 @@ You can download and install raylib using the [vcpkg](https://github.com/Microso *The raylib port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.* +#### Installing and building raylib via conan + +You can download and install raylib using the [conan](https://conan.io) dependency manager: + + https://docs.conan.io/en/latest/getting_started.html + +*The raylib recipe in conan is kept up to date by conan team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/conan-io/conan-center-index) on the conan-center-index repository.* + #### Building raylib on multiple platforms [raylib Wiki](https://github.com/raysan5/raylib/wiki#development-platforms) contains detailed instructions on building and usage on multiple platforms. From b76dc06297289245b477a2d98e44271149524cdd Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Thu, 7 Jan 2021 14:13:44 -0800 Subject: [PATCH 04/23] It's top-bottom not bottom-top in GL space. (#1517) --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 572e86c47..8d0dc373e 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1100,7 +1100,7 @@ void rlFrustum(double left, double right, double bottom, double top, double znea // Multiply the current matrix by an orthographic matrix generated by parameters void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar) { - if (((right - left) > 0) && ((bottom - top) > 0)) + if (((right - left) > 0) && ((top - bottom) > 0)) { Matrix matOrtho = MatrixOrtho(left, right, bottom, top, znear, zfar); *RLGL.State.currentMatrix = MatrixMultiply(*RLGL.State.currentMatrix, matOrtho); From bbc09288bd1732611ebd930eaf28d43f1310b784 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 9 Jan 2021 12:37:21 +0100 Subject: [PATCH 05/23] rlOrtho() reverted change --- src/rlgl.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 8d0dc373e..d8e3de9d6 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1100,12 +1100,11 @@ void rlFrustum(double left, double right, double bottom, double top, double znea // Multiply the current matrix by an orthographic matrix generated by parameters void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar) { - if (((right - left) > 0) && ((top - bottom) > 0)) - { - Matrix matOrtho = MatrixOrtho(left, right, bottom, top, znear, zfar); - *RLGL.State.currentMatrix = MatrixMultiply(*RLGL.State.currentMatrix, matOrtho); - } - else *RLGL.State.currentMatrix = MatrixIdentity(); + // NOTE: If left-right and top-botton values are equal it could create + // a division by zero on MatrixOrtho(), response to it is platform/compiler dependant + Matrix matOrtho = MatrixOrtho(left, right, bottom, top, znear, zfar); + + *RLGL.State.currentMatrix = MatrixMultiply(*RLGL.State.currentMatrix, matOrtho); } #endif From 04a1bb13907a043fbd0c30fb714e376b67dc54ca Mon Sep 17 00:00:00 2001 From: Dmitry Matveyev Date: Wed, 13 Jan 2021 01:12:14 +0600 Subject: [PATCH 06/23] Reorder typedefs in physac.h to be in header part (#1528) --- src/physac.h | 122 +++++++++++++++++++++++++-------------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/src/physac.h b/src/physac.h index f03074dd9..c3898d2fc 100644 --- a/src/physac.h +++ b/src/physac.h @@ -125,11 +125,72 @@ } Vector2; #endif +//---------------------------------------------------------------------------------- +// Data Types Structure Definition +//---------------------------------------------------------------------------------- + typedef enum PhysicsShapeType { PHYSICS_CIRCLE, PHYSICS_POLYGON } PhysicsShapeType; // Previously defined to be used in PhysicsShape struct as circular dependencies typedef struct PhysicsBodyData *PhysicsBody; +// Matrix2x2 type (used for polygon shape rotation matrix) +typedef struct Matrix2x2 { + float m00; + float m01; + float m10; + float m11; +} Matrix2x2; + +typedef struct PolygonData { + unsigned int vertexCount; // Current used vertex and normals count + Vector2 positions[PHYSAC_MAX_VERTICES]; // Polygon vertex positions vectors + Vector2 normals[PHYSAC_MAX_VERTICES]; // Polygon vertex normals vectors +} PolygonData; + +typedef struct PhysicsShape { + PhysicsShapeType type; // Physics shape type (circle or polygon) + PhysicsBody body; // Shape physics body reference + float radius; // Circle shape radius (used for circle shapes) + Matrix2x2 transform; // Vertices transform matrix 2x2 + PolygonData vertexData; // Polygon shape vertices position and normals data (just used for polygon shapes) +} PhysicsShape; + +typedef struct PhysicsBodyData { + unsigned int id; // Reference unique identifier + bool enabled; // Enabled dynamics state (collisions are calculated anyway) + Vector2 position; // Physics body shape pivot + Vector2 velocity; // Current linear velocity applied to position + Vector2 force; // Current linear force (reset to 0 every step) + float angularVelocity; // Current angular velocity applied to orient + float torque; // Current angular force (reset to 0 every step) + float orient; // Rotation in radians + float inertia; // Moment of inertia + float inverseInertia; // Inverse value of inertia + float mass; // Physics body mass + float inverseMass; // Inverse value of mass + float staticFriction; // Friction when the body has not movement (0 to 1) + float dynamicFriction; // Friction when the body has movement (0 to 1) + float restitution; // Restitution coefficient of the body (0 to 1) + bool useGravity; // Apply gravity force to dynamics + bool isGrounded; // Physics grounded on other body state + bool freezeOrient; // Physics rotation constraint + PhysicsShape shape; // Physics body shape information (type, radius, vertices, normals) +} PhysicsBodyData; + +typedef struct PhysicsManifoldData { + unsigned int id; // Reference unique identifier + PhysicsBody bodyA; // Manifold first physics body reference + PhysicsBody bodyB; // Manifold second physics body reference + float penetration; // Depth of penetration from collision + Vector2 normal; // Normal direction vector from 'a' to 'b' + Vector2 contacts[2]; // Points of contact during collision + unsigned int contactsCount; // Current collision number of contacts + float restitution; // Mixed restitution during collision + float dynamicFriction; // Mixed dynamic friction during collision + float staticFriction; // Mixed static friction during collision +} PhysicsManifoldData, *PhysicsManifold; + #if defined(__cplusplus) extern "C" { // Prevents name mangling of functions #endif @@ -221,67 +282,6 @@ PHYSACDEF void ClosePhysics(void); #define PHYSAC_K 1.0f/3.0f #define PHYSAC_VECTOR_ZERO (Vector2){ 0.0f, 0.0f } -//---------------------------------------------------------------------------------- -// Data Types Structure Definition -//---------------------------------------------------------------------------------- - -// Matrix2x2 type (used for polygon shape rotation matrix) -typedef struct Matrix2x2 { - float m00; - float m01; - float m10; - float m11; -} Matrix2x2; - -typedef struct PolygonData { - unsigned int vertexCount; // Current used vertex and normals count - Vector2 positions[PHYSAC_MAX_VERTICES]; // Polygon vertex positions vectors - Vector2 normals[PHYSAC_MAX_VERTICES]; // Polygon vertex normals vectors -} PolygonData; - -typedef struct PhysicsShape { - PhysicsShapeType type; // Physics shape type (circle or polygon) - PhysicsBody body; // Shape physics body reference - float radius; // Circle shape radius (used for circle shapes) - Matrix2x2 transform; // Vertices transform matrix 2x2 - PolygonData vertexData; // Polygon shape vertices position and normals data (just used for polygon shapes) -} PhysicsShape; - -typedef struct PhysicsBodyData { - unsigned int id; // Reference unique identifier - bool enabled; // Enabled dynamics state (collisions are calculated anyway) - Vector2 position; // Physics body shape pivot - Vector2 velocity; // Current linear velocity applied to position - Vector2 force; // Current linear force (reset to 0 every step) - float angularVelocity; // Current angular velocity applied to orient - float torque; // Current angular force (reset to 0 every step) - float orient; // Rotation in radians - float inertia; // Moment of inertia - float inverseInertia; // Inverse value of inertia - float mass; // Physics body mass - float inverseMass; // Inverse value of mass - float staticFriction; // Friction when the body has not movement (0 to 1) - float dynamicFriction; // Friction when the body has movement (0 to 1) - float restitution; // Restitution coefficient of the body (0 to 1) - bool useGravity; // Apply gravity force to dynamics - bool isGrounded; // Physics grounded on other body state - bool freezeOrient; // Physics rotation constraint - PhysicsShape shape; // Physics body shape information (type, radius, vertices, normals) -} PhysicsBodyData; - -typedef struct PhysicsManifoldData { - unsigned int id; // Reference unique identifier - PhysicsBody bodyA; // Manifold first physics body reference - PhysicsBody bodyB; // Manifold second physics body reference - float penetration; // Depth of penetration from collision - Vector2 normal; // Normal direction vector from 'a' to 'b' - Vector2 contacts[2]; // Points of contact during collision - unsigned int contactsCount; // Current collision number of contacts - float restitution; // Mixed restitution during collision - float dynamicFriction; // Mixed dynamic friction during collision - float staticFriction; // Mixed static friction during collision -} PhysicsManifoldData, *PhysicsManifold; - //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- From 477653a0d6f1141c174a23e00e82a8546791f559 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 12 Jan 2021 20:24:31 +0100 Subject: [PATCH 07/23] Update Makefile --- templates/advance_game/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/advance_game/Makefile b/templates/advance_game/Makefile index c37ced5e4..7097e2c2c 100644 --- a/templates/advance_game/Makefile +++ b/templates/advance_game/Makefile @@ -276,7 +276,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),LINUX) # Reset everything. # Precedence: immediately local, installed version, raysan5 provided libs -I$(RAYLIB_H_INSTALL_PATH) -I$(RAYLIB_PATH)/release/include - #INCLUDE_PATHS = -I$(RAYLIB_H_INSTALL_PATH) -isystem. -isystem$(RAYLIB_PATH)/src -isystem$(RAYLIB_PATH)/release/include -isystem$(RAYLIB_PATH)/src/external INCLUDE_PATHS = -I$(RAYLIB_H_INSTALL_PATH) -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/release/include -I$(RAYLIB_PATH)/src/external endif endif From c256b2662973cb2c253814790c56418ba118e0cb Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 12 Jan 2021 20:24:58 +0100 Subject: [PATCH 08/23] Reorder function --- src/text.c | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/text.c b/src/text.c index e22d8db8f..5c10dd439 100644 --- a/src/text.c +++ b/src/text.c @@ -838,30 +838,6 @@ void DrawText(const char *text, int posX, int posY, int fontSize, Color color) } } -// Draw one character (codepoint) -void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint) -{ - // Character index position in sprite font - // NOTE: In case a codepoint is not available in the font, index returned points to '?' - int index = GetGlyphIndex(font, codepoint); - float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor - - // Character destination rectangle on screen - // NOTE: We consider charsPadding on drawing - Rectangle dstRec = { position.x + font.chars[index].offsetX*scaleFactor - (float)font.charsPadding*scaleFactor, - position.y + font.chars[index].offsetY*scaleFactor - (float)font.charsPadding*scaleFactor, - (font.recs[index].width + 2.0f*font.charsPadding)*scaleFactor, - (font.recs[index].height + 2.0f*font.charsPadding)*scaleFactor }; - - // Character source rectangle from font texture atlas - // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects - Rectangle srcRec = { font.recs[index].x - (float)font.charsPadding, font.recs[index].y - (float)font.charsPadding, - font.recs[index].width + 2.0f*font.charsPadding, font.recs[index].height + 2.0f*font.charsPadding }; - - // Draw the character texture on the screen - DrawTexturePro(font.texture, srcRec, dstRec, (Vector2){ 0, 0 }, 0.0f, tint); -} - // Draw text using Font // NOTE: chars spacing is NOT proportional to fontSize void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint) @@ -915,7 +891,7 @@ void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, flo // Draw text using font inside rectangle limits with support for text selection void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint) { - int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop + int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop int textOffsetY = 0; // Offset between lines (on line break '\n') float textOffsetX = 0.0f; // Offset X to next character to draw @@ -1043,6 +1019,30 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f } } +// Draw one character (codepoint) +void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint) +{ + // Character index position in sprite font + // NOTE: In case a codepoint is not available in the font, index returned points to '?' + int index = GetGlyphIndex(font, codepoint); + float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor + + // Character destination rectangle on screen + // NOTE: We consider charsPadding on drawing + Rectangle dstRec = { position.x + font.chars[index].offsetX*scaleFactor - (float)font.charsPadding*scaleFactor, + position.y + font.chars[index].offsetY*scaleFactor - (float)font.charsPadding*scaleFactor, + (font.recs[index].width + 2.0f*font.charsPadding)*scaleFactor, + (font.recs[index].height + 2.0f*font.charsPadding)*scaleFactor }; + + // Character source rectangle from font texture atlas + // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects + Rectangle srcRec = { font.recs[index].x - (float)font.charsPadding, font.recs[index].y - (float)font.charsPadding, + font.recs[index].width + 2.0f*font.charsPadding, font.recs[index].height + 2.0f*font.charsPadding }; + + // Draw the character texture on the screen + DrawTexturePro(font.texture, srcRec, dstRec, (Vector2){ 0, 0 }, 0.0f, tint); +} + // Measure string width for default font int MeasureText(const char *text, int fontSize) { From dfa11e22cf48fffd40f87017ed3c359c0b640e74 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 12 Jan 2021 20:25:09 +0100 Subject: [PATCH 09/23] Add comments --- src/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core.c b/src/core.c index 32b30191b..cd486b292 100644 --- a/src/core.c +++ b/src/core.c @@ -2558,6 +2558,7 @@ unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLe unsigned char *compData = NULL; #if defined(SUPPORT_COMPRESSION_API) + // TODO: WARNING: This function actually codes (and compresses) a valid zlib stream compData = stbi_zlib_compress(data, dataLength, compDataLength, COMPRESSION_QUALITY_DEFLATE); #endif @@ -2570,6 +2571,7 @@ unsigned char *DecompressData(unsigned char *compData, int compDataLength, int * char *data = NULL; #if defined(SUPPORT_COMPRESSION_API) + // TODO: WARNING: This function actually decodes (and decompresses) a valid zlib stream data = stbi_zlib_decode_malloc((char *)compData, compDataLength, dataLength); #endif From 5d1d59069209b44e72e5c0eabb753fbae95f8a55 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 12 Jan 2021 21:15:11 +0100 Subject: [PATCH 10/23] REDESIGN: Compresion API Now it compresses/decompresses valid DEFLATE streams instead of zlib streams. It uses the minimal and efficient libraries: sdefl/sinfl. --- src/config.h | 2 + src/core.c | 26 +- src/external/sdefl.h | 697 +++++++++++++++++++++++++++++++++++++++++++ src/external/sinfl.h | 464 ++++++++++++++++++++++++++++ 4 files changed, 1185 insertions(+), 4 deletions(-) create mode 100644 src/external/sdefl.h create mode 100644 src/external/sinfl.h diff --git a/src/config.h b/src/config.h index ef664ba12..f84aa0170 100644 --- a/src/config.h +++ b/src/config.h @@ -77,6 +77,8 @@ #define STORAGE_DATA_FILE "storage.data" // Automatic storage filename +#define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB + //------------------------------------------------------------------------------------ // Module: rlgl - Configuration Flags diff --git a/src/core.c b/src/core.c index cd486b292..ec962d8b8 100644 --- a/src/core.c +++ b/src/core.c @@ -150,6 +150,14 @@ #include "external/msf_gif.h" // Support GIF recording #endif +#if defined(SUPPORT_COMPRESSION_API) + #define SINFL_IMPLEMENTATION + #include "external/sinfl.h" + + #define SDEFL_IMPLEMENTATION + #include "external/sdefl.h" +#endif + #include // Required for: srand(), rand(), atexit() #include // Required for: sprintf() [Used in OpenURL()] #include // Required for: strrchr(), strcmp(), strlen() @@ -2558,8 +2566,13 @@ unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLe unsigned char *compData = NULL; #if defined(SUPPORT_COMPRESSION_API) - // TODO: WARNING: This function actually codes (and compresses) a valid zlib stream - compData = stbi_zlib_compress(data, dataLength, compDataLength, COMPRESSION_QUALITY_DEFLATE); + // Compress data and generate a valid DEFLATE stream + struct sdefl sdefl = { 0 }; + int bounds = sdefl_bound(dataLength); + compData = (unsigned char *)RL_CALLOC(bounds, 1); + *compDataLength = sdeflate(&sdefl, compData, data, dataLength, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbwi + + TraceLog(LOG_INFO, "SYSTEM: Data compressed: Original size: %i -> Comp. size: %i\n", dataLength, compDataLength); #endif return compData; @@ -2571,8 +2584,13 @@ unsigned char *DecompressData(unsigned char *compData, int compDataLength, int * char *data = NULL; #if defined(SUPPORT_COMPRESSION_API) - // TODO: WARNING: This function actually decodes (and decompresses) a valid zlib stream - data = stbi_zlib_decode_malloc((char *)compData, compDataLength, dataLength); + // Decompress data from a valid DEFLATE stream + data = RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 0); + int length = sinflate(data, compData, compDataLength); + RL_REALLOC(data, length); + *dataLength = length; + + TraceLog(LOG_INFO, "SYSTEM: Data compressed: Original size: %i -> Comp. size: %i\n", dataLength, compDataLength); #endif return (unsigned char *)data; diff --git a/src/external/sdefl.h b/src/external/sdefl.h new file mode 100644 index 000000000..218e75498 --- /dev/null +++ b/src/external/sdefl.h @@ -0,0 +1,697 @@ +/* +# Small Deflate +`sdefl` is a small bare bone lossless compression library in ANSI C (ISO C90) +which implements the Deflate (RFC 1951) compressed data format specification standard. +It is mainly tuned to get as much speed and compression ratio from as little code +as needed to keep the implementation as concise as possible. + +## Features +- Portable single header and source file duo written in ANSI C (ISO C90) +- Dual license with either MIT or public domain +- Small implementation + - Deflate: 525 LoC + - Inflate: 320 LoC +- Webassembly: + - Deflate ~3.7 KB (~2.2KB compressed) + - Inflate ~3.6 KB (~2.2KB compressed) + +## Usage: +This file behaves differently depending on what symbols you define +before including it. + +Header-File mode: +If you do not define `SDEFL_IMPLEMENTATION` before including this file, it +will operate in header only mode. In this mode it declares all used structs +and the API of the library without including the implementation of the library. + +Implementation mode: +If you define `SDEFL_IMPLEMENTATION` before including this file, it will +compile the implementation . Make sure that you only include +this file implementation in *one* C or C++ file to prevent collisions. + +### Benchmark + +| Compressor name | Compression| Decompress.| Compr. size | Ratio | +| ------------------------| -----------| -----------| ----------- | ----- | +| sdefl 1.0 -0 | 127 MB/s | 233 MB/s | 40004116 | 39.88 | +| sdefl 1.0 -1 | 111 MB/s | 259 MB/s | 38940674 | 38.82 | +| sdefl 1.0 -5 | 45 MB/s | 275 MB/s | 36577183 | 36.46 | +| sdefl 1.0 -7 | 38 MB/s | 276 MB/s | 36523781 | 36.41 | +| zlib 1.2.11 -1 | 72 MB/s | 307 MB/s | 42298774 | 42.30 | +| zlib 1.2.11 -6 | 24 MB/s | 313 MB/s | 36548921 | 36.55 | +| zlib 1.2.11 -9 | 20 MB/s | 314 MB/s | 36475792 | 36.48 | +| miniz 1.0 -1 | 122 MB/s | 208 MB/s | 48510028 | 48.51 | +| miniz 1.0 -6 | 27 MB/s | 260 MB/s | 36513697 | 36.51 | +| miniz 1.0 -9 | 23 MB/s | 261 MB/s | 36460101 | 36.46 | +| libdeflate 1.3 -1 | 147 MB/s | 667 MB/s | 39597378 | 39.60 | +| libdeflate 1.3 -6 | 69 MB/s | 689 MB/s | 36648318 | 36.65 | +| libdeflate 1.3 -9 | 13 MB/s | 672 MB/s | 35197141 | 35.20 | +| libdeflate 1.3 -12 | 8.13 MB/s | 670 MB/s | 35100568 | 35.10 | + +### Compression +Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia): + +| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | +| :------ | ---------: | -----------------: | ---------: | ----------: | +| dickens | 10.192.446 | 4,260,187| 3,845,261| 3,833,657 | +| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | +| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | +| nci | 33.553.445 | 4,030,283 | 3,094,526 | 3,006,075 | +| ooffice | 6.152.192 | 3,320,063 | 3,186,373 | 3,183,815 | +| osdb | 10.085.684 | 3,919,646 | 3,649,510 | 3,649,477 | +| reymont | 6.627.202 | 2,263,378 | 1,857,588 | 1,827,237 | +| samba | 21.606.400 | 6,121,797 | 5,462,670 | 5,450,762 | +| sao | 7.251.944 | 5,612,421 | 5,485,380 | 5,481,765 | +| webster | 41.458.703 | 13,972,648 | 12,059,432 | 11,991,421 | +| xml | 5.345.280 | 886,620| 674,009 | 662,141 | +| x-ray | 8.474.240 | 6,304,655 | 6,244,779 | 6,244,779 | + +## License +``` +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2020 Micha Mettke +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +``` +*/ +#ifndef SDEFL_H_INCLUDED +#define SDEFL_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define SDEFL_MAX_OFF (1 << 15) +#define SDEFL_WIN_SIZ SDEFL_MAX_OFF +#define SDEFL_WIN_MSK (SDEFL_WIN_SIZ-1) + +#define SDEFL_HASH_BITS 15 +#define SDEFL_HASH_SIZ (1 << SDEFL_HASH_BITS) +#define SDEFL_HASH_MSK (SDEFL_HASH_SIZ-1) + +#define SDEFL_MIN_MATCH 4 +#define SDEFL_BLK_MAX (256*1024) +#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX + SDEFL_MIN_MATCH)/SDEFL_MIN_MATCH) + +#define SDEFL_SYM_MAX (288) +#define SDEFL_OFF_MAX (32) +#define SDEFL_PRE_MAX (19) + +#define SDEFL_LVL_MIN 0 +#define SDEFL_LVL_DEF 5 +#define SDEFL_LVL_MAX 8 + +struct sdefl_freq { + unsigned lit[SDEFL_SYM_MAX]; + unsigned off[SDEFL_OFF_MAX]; +}; +struct sdefl_code_words { + unsigned lit[SDEFL_SYM_MAX]; + unsigned off[SDEFL_OFF_MAX]; +}; +struct sdefl_lens { + unsigned char lit[SDEFL_SYM_MAX]; + unsigned char off[SDEFL_OFF_MAX]; +}; +struct sdefl_codes { + struct sdefl_code_words word; + struct sdefl_lens len; +}; +struct sdefl_seqt { + int off, len; +}; +struct sdefl { + int bits, bitcnt; + int tbl[SDEFL_HASH_SIZ]; + int prv[SDEFL_WIN_SIZ]; + + int seq_cnt; + struct sdefl_seqt seq[SDEFL_SEQ_SIZ]; + struct sdefl_freq freq; + struct sdefl_codes cod; +}; +extern int sdefl_bound(int in_len); +extern int sdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); +extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); + +#ifdef __cplusplus +} +#endif + +#endif /* SDEFL_H_INCLUDED */ + +#ifdef SDEFL_IMPLEMENTATION + +#include /* assert */ +#include /* memcpy */ +#include /* CHAR_BIT */ + +#define SDEFL_NIL (-1) +#define SDEFL_MAX_MATCH 258 +#define SDEFL_MAX_CODE_LEN (15) +#define SDEFL_SYM_BITS (10u) +#define SDEFL_SYM_MSK ((1u << SDEFL_SYM_BITS)-1u) +#define SDEFL_LIT_LEN_CODES (14) +#define SDEFL_OFF_CODES (15) +#define SDEFL_PRE_CODES (7) +#define SDEFL_CNT_NUM(n) ((((n)+3u/4u)+3u)&~3u) +#define SDEFL_EOB (256) + +#define sdefl_npow2(n) (1 << (sdefl_ilog2((n)-1) + 1)) + +static int +sdefl_ilog2(int n) { + if (!n) return 0; +#ifdef _MSC_VER + unsigned long msbp = 0; + _BitScanReverse(&msbp, (unsigned long)n); + return (int)msbp; +#elif defined(__GNUC__) || defined(__clang__) + return (int)sizeof(unsigned long) * CHAR_BIT - 1 - __builtin_clzl((unsigned long)n); +#else + #define lt(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n + static const char tbl[256] = { + 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,lt(4), lt(5), lt(5), lt(6), lt(6), lt(6), lt(6), + lt(7), lt(7), lt(7), lt(7), lt(7), lt(7), lt(7), lt(7)}; + int tt, t; + if ((tt = (n >> 16))) { + return (t = (tt >> 8)) ? 24 + tbl[t] : 16 + tbl[tt]; + } else { + return (t = (n >> 8)) ? 8 + tbl[t] : tbl[n]; + } + #undef lt +#endif +} +static unsigned +sdefl_uload32(const void *p) { + /* hopefully will be optimized to an unaligned read */ + unsigned n = 0; + memcpy(&n, p, sizeof(n)); + return n; +} +static unsigned +sdefl_hash32(const void *p) { + unsigned n = sdefl_uload32(p); + return (n * 0x9E377989) >> (32 - SDEFL_HASH_BITS); +} +static void +sdefl_put(unsigned char **dst, struct sdefl *s, int code, int bitcnt) { + s->bits |= (code << s->bitcnt); + s->bitcnt += bitcnt; + while (s->bitcnt >= 8) { + unsigned char *tar = *dst; + *tar = (unsigned char)(s->bits & 0xFF); + s->bits >>= 8; + s->bitcnt -= 8; + *dst = *dst + 1; + } +} +static void +sdefl_heap_sub(unsigned A[], unsigned len, unsigned sub) { + unsigned c, p = sub; + unsigned v = A[sub]; + while ((c = p << 1) <= len) { + if (c < len && A[c + 1] > A[c]) c++; + if (v >= A[c]) break; + A[p] = A[c], p = c; + } + A[p] = v; +} +static void +sdefl_heap_array(unsigned *A, unsigned len) { + unsigned sub; + for (sub = len >> 1; sub >= 1; sub--) + sdefl_heap_sub(A, len, sub); +} +static void +sdefl_heap_sort(unsigned *A, unsigned n) { + A--; + sdefl_heap_array(A, n); + while (n >= 2) { + unsigned tmp = A[n]; + A[n--] = A[1]; + A[1] = tmp; + sdefl_heap_sub(A, n, 1); + } +} +static unsigned +sdefl_sort_sym(unsigned sym_cnt, unsigned *freqs, + unsigned char *lens, unsigned *sym_out) { + unsigned cnts[SDEFL_CNT_NUM(SDEFL_SYM_MAX)] = {0}; + unsigned cnt_num = SDEFL_CNT_NUM(sym_cnt); + unsigned used_sym = 0; + unsigned sym, i; + for (sym = 0; sym < sym_cnt; sym++) + cnts[freqs[sym] < cnt_num-1 ? freqs[sym]: cnt_num-1]++; + for (i = 1; i < cnt_num; i++) { + unsigned cnt = cnts[i]; + cnts[i] = used_sym; + used_sym += cnt; + } + for (sym = 0; sym < sym_cnt; sym++) { + unsigned freq = freqs[sym]; + if (freq) { + unsigned idx = freq < cnt_num-1 ? freq : cnt_num-1; + sym_out[cnts[idx]++] = sym | (freq << SDEFL_SYM_BITS); + } else lens[sym] = 0; + } + sdefl_heap_sort(sym_out + cnts[cnt_num-2], cnts[cnt_num-1] - cnts[cnt_num-2]); + return used_sym; +} +static void +sdefl_build_tree(unsigned *A, unsigned sym_cnt) { + unsigned i = 0, b = 0, e = 0; + do { + unsigned m, n, freq_shift; + if (i != sym_cnt && (b == e || (A[i] >> SDEFL_SYM_BITS) <= (A[b] >> SDEFL_SYM_BITS))) + m = i++; + else m = b++; + if (i != sym_cnt && (b == e || (A[i] >> SDEFL_SYM_BITS) <= (A[b] >> SDEFL_SYM_BITS))) + n = i++; + else n = b++; + + freq_shift = (A[m] & ~SDEFL_SYM_MSK) + (A[n] & ~SDEFL_SYM_MSK); + A[m] = (A[m] & SDEFL_SYM_MSK) | (e << SDEFL_SYM_BITS); + A[n] = (A[n] & SDEFL_SYM_MSK) | (e << SDEFL_SYM_BITS); + A[e] = (A[e] & SDEFL_SYM_MSK) | freq_shift; + } while (sym_cnt - ++e > 1); +} +static void +sdefl_gen_len_cnt(unsigned *A, unsigned root, unsigned *len_cnt, + unsigned max_code_len) { + int n; + unsigned i; + for (i = 0; i <= max_code_len; i++) + len_cnt[i] = 0; + len_cnt[1] = 2; + + A[root] &= SDEFL_SYM_MSK; + for (n = (int)root - 1; n >= 0; n--) { + unsigned p = A[n] >> SDEFL_SYM_BITS; + unsigned pdepth = A[p] >> SDEFL_SYM_BITS; + unsigned depth = pdepth + 1; + unsigned len = depth; + + A[n] = (A[n] & SDEFL_SYM_MSK) | (depth << SDEFL_SYM_BITS); + if (len >= max_code_len) { + len = max_code_len; + do len--; while (!len_cnt[len]); + } + len_cnt[len]--; + len_cnt[len+1] += 2; + } +} +static void +sdefl_gen_codes(unsigned *A, unsigned char *lens, const unsigned *len_cnt, + unsigned max_code_word_len, unsigned sym_cnt) { + unsigned i, sym, len, nxt[SDEFL_MAX_CODE_LEN + 1]; + for (i = 0, len = max_code_word_len; len >= 1; len--) { + unsigned cnt = len_cnt[len]; + while (cnt--) lens[A[i++] & SDEFL_SYM_MSK] = (unsigned char)len; + } + nxt[0] = nxt[1] = 0; + for (len = 2; len <= max_code_word_len; len++) + nxt[len] = (nxt[len-1] + len_cnt[len-1]) << 1; + for (sym = 0; sym < sym_cnt; sym++) + A[sym] = nxt[lens[sym]]++; +} +static unsigned +sdefl_rev(unsigned c, unsigned char n) { + c = ((c & 0x5555) << 1) | ((c & 0xAAAA) >> 1); + c = ((c & 0x3333) << 2) | ((c & 0xCCCC) >> 2); + c = ((c & 0x0F0F) << 4) | ((c & 0xF0F0) >> 4); + c = ((c & 0x00FF) << 8) | ((c & 0xFF00) >> 8); + return c >> (16-n); +} +static void +sdefl_huff(unsigned char *lens, unsigned *codes, unsigned *freqs, + unsigned num_syms, unsigned max_code_len) { + unsigned c, *A = codes; + unsigned len_cnt[SDEFL_MAX_CODE_LEN + 1]; + unsigned used_syms = sdefl_sort_sym(num_syms, freqs, lens, A); + if (!used_syms) return; + if (used_syms == 1) { + unsigned s = A[0] & SDEFL_SYM_MSK; + unsigned i = s ? s : 1; + codes[0] = 0, lens[0] = 1; + codes[i] = 1, lens[i] = 1; + return; + } + sdefl_build_tree(A, used_syms); + sdefl_gen_len_cnt(A, used_syms-2, len_cnt, max_code_len); + sdefl_gen_codes(A, lens, len_cnt, max_code_len, num_syms); + for (c = 0; c < num_syms; c++) { + codes[c] = sdefl_rev(codes[c], lens[c]); + } +} +struct sdefl_symcnt { + int items; + int lit; + int off; +}; +static void +sdefl_precode(struct sdefl_symcnt *cnt, unsigned *freqs, unsigned *items, + const unsigned char *litlen, const unsigned char *offlen) { + unsigned *at = items; + unsigned run_start = 0; + + unsigned total = 0; + unsigned char lens[SDEFL_SYM_MAX + SDEFL_OFF_MAX]; + for (cnt->lit = SDEFL_SYM_MAX; cnt->lit > 257; cnt->lit--) + if (litlen[cnt->lit - 1]) break; + for (cnt->off = SDEFL_OFF_MAX; cnt->off > 1; cnt->off--) + if (offlen[cnt->off - 1]) break; + + total = (unsigned)(cnt->lit + cnt->off); + memcpy(lens, litlen, sizeof(unsigned char) * cnt->lit); + memcpy(lens + cnt->lit, offlen, sizeof(unsigned char) * cnt->off); + do { + unsigned len = lens[run_start]; + unsigned run_end = run_start; + do run_end++; while (run_end != total && len == lens[run_end]); + if (!len) { + while ((run_end - run_start) >= 11) { + unsigned n = (run_end - run_start) - 11; + unsigned xbits = n < 0x7f ? n : 0x7f; + freqs[18]++; + *at++ = 18u | (xbits << 5u); + run_start += 11 + xbits; + } + if ((run_end - run_start) >= 3) { + unsigned n = (run_end - run_start) - 3; + unsigned xbits = n < 0x7 ? n : 0x7; + freqs[17]++; + *at++ = 17u | (xbits << 5u); + run_start += 3 + xbits; + } + } else if ((run_end - run_start) >= 4) { + freqs[len]++; + *at++ = len; + run_start++; + do { + unsigned xbits = (run_end - run_start) - 3; + xbits = xbits < 0x03 ? xbits : 0x03; + *at++ = 16 | (xbits << 5); + run_start += 3 + xbits; + freqs[16]++; + } while ((run_end - run_start) >= 3); + } + while (run_start != run_end) { + freqs[len]++; + *at++ = len; + run_start++; + } + } while (run_start != total); + cnt->items = (int)(at - items); +} +struct sdefl_match_codes { + int ls, lc; + int dc, dx; +}; +static void +sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { + static const short dxmax[] = {0,6,12,24,48,96,192,384,768,1536,3072,6144,12288,24576}; + static const unsigned char lslot[258+1] = { + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, + 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, + 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, + 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 28 + }; + cod->ls = lslot[len]; + cod->lc = 257 + cod->ls; + cod->dx = sdefl_ilog2(sdefl_npow2(dist) >> 2); + cod->dc = cod->dx ? ((cod->dx + 1) << 1) + (dist > dxmax[cod->dx]) : dist-1; +} +static void +sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { + static const char lxn[] = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + static const short lmin[] = {3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43, + 51,59,67,83,99,115,131,163,195,227,258}; + static const short dmin[] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257, + 385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}; + + struct sdefl_match_codes cod; + sdefl_match_codes(&cod, dist, len); + sdefl_put(dst, s, (int)s->cod.word.lit[cod.lc], s->cod.len.lit[cod.lc]); + sdefl_put(dst, s, len - lmin[cod.ls], lxn[cod.ls]); + sdefl_put(dst, s, (int)s->cod.word.off[cod.dc], s->cod.len.off[cod.dc]); + sdefl_put(dst, s, dist - dmin[cod.dc], cod.dx); +} +static void +sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, + const unsigned char *in) { + int j, i = 0, item_cnt = 0; + struct sdefl_symcnt symcnt = {0}; + unsigned codes[SDEFL_PRE_MAX]; + unsigned char lens[SDEFL_PRE_MAX]; + unsigned freqs[SDEFL_PRE_MAX] = {0}; + unsigned items[SDEFL_SYM_MAX + SDEFL_OFF_MAX]; + static const unsigned char perm[SDEFL_PRE_MAX] = {16,17,18,0,8,7,9,6,10,5,11, + 4,12,3,13,2,14,1,15}; + + /* huffman codes */ + s->freq.lit[SDEFL_EOB]++; + sdefl_huff(s->cod.len.lit, s->cod.word.lit, s->freq.lit, SDEFL_SYM_MAX, SDEFL_LIT_LEN_CODES); + sdefl_huff(s->cod.len.off, s->cod.word.off, s->freq.off, SDEFL_OFF_MAX, SDEFL_OFF_CODES); + sdefl_precode(&symcnt, freqs, items, s->cod.len.lit, s->cod.len.off); + sdefl_huff(lens, codes, freqs, SDEFL_PRE_MAX, SDEFL_PRE_CODES); + for (item_cnt = SDEFL_PRE_MAX; item_cnt > 4; item_cnt--) { + if (lens[perm[item_cnt - 1]]) break; + } + /* block header */ + sdefl_put(dst, s, is_last ? 0x01 : 0x00, 1); /* block */ + sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ + sdefl_put(dst, s, symcnt.lit - 257, 5); + sdefl_put(dst, s, symcnt.off - 1, 5); + sdefl_put(dst, s, item_cnt - 4, 4); + for (i = 0; i < item_cnt; ++i) + sdefl_put(dst, s, lens[perm[i]], 3); + for (i = 0; i < symcnt.items; ++i) { + unsigned sym = items[i] & 0x1F; + sdefl_put(dst, s, (int)codes[sym], lens[sym]); + if (sym < 16) continue; + if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); + else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); + else sdefl_put(dst, s, items[i] >> 5, 7); + } + /* block sequences */ + for (i = 0; i < s->seq_cnt; ++i) { + if (s->seq[i].off >= 0) + for (j = 0; j < s->seq[i].len; ++j) { + int c = in[s->seq[i].off + j]; + sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); + } + else sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + } + sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); + memset(&s->freq, 0, sizeof(s->freq)); + s->seq_cnt = 0; +} +static void +sdefl_seq(struct sdefl *s, int off, int len) { + assert(s->seq_cnt + 2 < SDEFL_SEQ_SIZ); + s->seq[s->seq_cnt].off = off; + s->seq[s->seq_cnt].len = len; + s->seq_cnt++; +} +static void +sdefl_reg_match(struct sdefl *s, int off, int len) { + struct sdefl_match_codes cod; + sdefl_match_codes(&cod, off, len); + s->freq.lit[cod.lc]++; + s->freq.off[cod.dc]++; +} +struct sdefl_match { + int off; + int len; +}; +static void +sdefl_fnd(struct sdefl_match *m, const struct sdefl *s, + int chain_len, int max_match, const unsigned char *in, int p) { + int i = s->tbl[sdefl_hash32(&in[p])]; + int limit = ((p-SDEFL_WIN_SIZ) limit) { + if (in[i+m->len] == in[p+m->len] && + (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))){ + int n = SDEFL_MIN_MATCH; + while (n < max_match && in[i+n] == in[p+n]) n++; + if (n > m->len) { + m->len = n, m->off = p - i; + if (n == max_match) break; + } + } + if (!(--chain_len)) break; + i = s->prv[i&SDEFL_WIN_MSK]; + } +} +static int +sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, + int in_len, int lvl) { + unsigned char *q = out; + static const unsigned char pref[] = {8,10,14,24,30,48,65,96,130}; + int max_chain = (lvl < 8) ? (1 << (lvl + 1)): (1 << 13); + int n, i = 0, litlen = 0; + for (n = 0; n < SDEFL_HASH_SIZ; ++n) { + s->tbl[n] = SDEFL_NIL; + } + do {int blk_end = i + SDEFL_BLK_MAX < in_len ? i + SDEFL_BLK_MAX : in_len; + while (i < blk_end) { + struct sdefl_match m = {0}; + int max_match = ((in_len-i)>SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH:(in_len-i); + int nice_match = pref[lvl] < max_match ? pref[lvl] : max_match; + int run = 1, inc = 1, run_inc; + if (max_match > SDEFL_MIN_MATCH) { + sdefl_fnd(&m, s, max_chain, max_match, in, i); + } + if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len < nice_match){ + struct sdefl_match m2 = {0}; + sdefl_fnd(&m2, s, max_chain, m.len+1, in, i+1); + m.len = (m2.len > m.len) ? 0 : m.len; + } + if (m.len >= SDEFL_MIN_MATCH) { + if (litlen) { + sdefl_seq(s, i - litlen, litlen); + litlen = 0; + } + sdefl_seq(s, -m.off, m.len); + sdefl_reg_match(s, m.off, m.len); + if (lvl < 2 && m.len >= nice_match) { + inc = m.len; + } else { + run = m.len; + } + } else { + s->freq.lit[in[i]]++; + litlen++; + } + run_inc = run * inc; + if (in_len - (i + run_inc) > SDEFL_MIN_MATCH) { + while (run-- > 0) { + unsigned h = sdefl_hash32(&in[i]); + s->prv[i&SDEFL_WIN_MSK] = s->tbl[h]; + s->tbl[h] = i, i += inc; + } + } else { + i += run_inc; + } + } + if (litlen) { + sdefl_seq(s, i - litlen, litlen); + litlen = 0; + } + sdefl_flush(&q, s, blk_end == in_len, in); + } while (i < in_len); + + if (s->bitcnt) + sdefl_put(&q, s, 0x00, 8 - s->bitcnt); + return (int)(q - out); +} +extern int +sdeflate(struct sdefl *s, void *out, const void *in, int n, int lvl) { + s->bits = s->bitcnt = 0; + return sdefl_compr(s, (unsigned char*)out, (const unsigned char*)in, n, lvl); +} +static unsigned +sdefl_adler32(unsigned adler32, const unsigned char *in, int in_len) { + #define SDEFL_ADLER_INIT (1) + const unsigned ADLER_MOD = 65521; + unsigned s1 = adler32 & 0xffff; + unsigned s2 = adler32 >> 16; + unsigned blk_len, i; + + blk_len = in_len % 5552; + while (in_len) { + for (i = 0; i + 7 < blk_len; i += 8) { + s1 += in[0]; s2 += s1; + s1 += in[1]; s2 += s1; + s1 += in[2]; s2 += s1; + s1 += in[3]; s2 += s1; + s1 += in[4]; s2 += s1; + s1 += in[5]; s2 += s1; + s1 += in[6]; s2 += s1; + s1 += in[7]; s2 += s1; + in += 8; + } + for (; i < blk_len; ++i) { + s1 += *in++, s2 += s1; + } + s1 %= ADLER_MOD; + s2 %= ADLER_MOD; + in_len -= blk_len; + blk_len = 5552; + } + return (unsigned)(s2 << 16) + (unsigned)s1; +} +extern int +zsdeflate(struct sdefl *s, void *out, const void *in, int n, int lvl) { + int p = 0; + unsigned a = 0; + unsigned char *q = (unsigned char*)out; + + s->bits = s->bitcnt = 0; + sdefl_put(&q, s, 0x78, 8); /* deflate, 32k window */ + sdefl_put(&q, s, 0x01, 8); /* fast compression */ + q += sdefl_compr(s, q, (const unsigned char*)in, n, lvl); + + /* append adler checksum */ + a = sdefl_adler32(SDEFL_ADLER_INIT, (const unsigned char*)in, n); + for (p = 0; p < 4; ++p) { + sdefl_put(&q, s, (a >> 24) & 0xFF, 8); + a <<= 8; + } + return (int)(q - (unsigned char*)out); +} +extern int +sdefl_bound(int len) { + int a = 128 + (len * 110) / 100; + int b = 128 + len + ((len / (31 * 1024)) + 1) * 5; + return (a > b) ? a : b; +} +#endif /* SDEFL_IMPLEMENTATION */ + diff --git a/src/external/sinfl.h b/src/external/sinfl.h new file mode 100644 index 000000000..20ec4eeed --- /dev/null +++ b/src/external/sinfl.h @@ -0,0 +1,464 @@ +/* +# Small Deflate +`sdefl` is a small bare bone lossless compression library in ANSI C (ISO C90) +which implements the Deflate (RFC 1951) compressed data format specification standard. +It is mainly tuned to get as much speed and compression ratio from as little code +as needed to keep the implementation as concise as possible. + +## Features +- Portable single header and source file duo written in ANSI C (ISO C90) +- Dual license with either MIT or public domain +- Small implementation + - Deflate: 525 LoC + - Inflate: 320 LoC +- Webassembly: + - Deflate ~3.7 KB (~2.2KB compressed) + - Inflate ~3.6 KB (~2.2KB compressed) + +## Usage: +This file behaves differently depending on what symbols you define +before including it. + +Header-File mode: +If you do not define `SINFL_IMPLEMENTATION` before including this file, it +will operate in header only mode. In this mode it declares all used structs +and the API of the library without including the implementation of the library. + +Implementation mode: +If you define `SINFL_IMPLEMENTATION` before including this file, it will +compile the implementation. Make sure that you only include +this file implementation in *one* C or C++ file to prevent collisions. + +### Benchmark + +| Compressor name | Compression| Decompress.| Compr. size | Ratio | +| ------------------------| -----------| -----------| ----------- | ----- | +| sdefl 1.0 -0 | 127 MB/s | 233 MB/s | 40004116 | 39.88 | +| sdefl 1.0 -1 | 111 MB/s | 259 MB/s | 38940674 | 38.82 | +| sdefl 1.0 -5 | 45 MB/s | 275 MB/s | 36577183 | 36.46 | +| sdefl 1.0 -7 | 38 MB/s | 276 MB/s | 36523781 | 36.41 | +| zlib 1.2.11 -1 | 72 MB/s | 307 MB/s | 42298774 | 42.30 | +| zlib 1.2.11 -6 | 24 MB/s | 313 MB/s | 36548921 | 36.55 | +| zlib 1.2.11 -9 | 20 MB/s | 314 MB/s | 36475792 | 36.48 | +| miniz 1.0 -1 | 122 MB/s | 208 MB/s | 48510028 | 48.51 | +| miniz 1.0 -6 | 27 MB/s | 260 MB/s | 36513697 | 36.51 | +| miniz 1.0 -9 | 23 MB/s | 261 MB/s | 36460101 | 36.46 | +| libdeflate 1.3 -1 | 147 MB/s | 667 MB/s | 39597378 | 39.60 | +| libdeflate 1.3 -6 | 69 MB/s | 689 MB/s | 36648318 | 36.65 | +| libdeflate 1.3 -9 | 13 MB/s | 672 MB/s | 35197141 | 35.20 | +| libdeflate 1.3 -12 | 8.13 MB/s | 670 MB/s | 35100568 | 35.10 | + +### Compression +Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia): + +| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | +| :------ | ---------: | -----------------: | ---------: | ----------: | +| dickens | 10.192.446 | 4,260,187| 3,845,261| 3,833,657 | +| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | +| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | +| nci | 33.553.445 | 4,030,283 | 3,094,526 | 3,006,075 | +| ooffice | 6.152.192 | 3,320,063 | 3,186,373 | 3,183,815 | +| osdb | 10.085.684 | 3,919,646 | 3,649,510 | 3,649,477 | +| reymont | 6.627.202 | 2,263,378 | 1,857,588 | 1,827,237 | +| samba | 21.606.400 | 6,121,797 | 5,462,670 | 5,450,762 | +| sao | 7.251.944 | 5,612,421 | 5,485,380 | 5,481,765 | +| webster | 41.458.703 | 13,972,648 | 12,059,432 | 11,991,421 | +| xml | 5.345.280 | 886,620| 674,009 | 662,141 | +| x-ray | 8.474.240 | 6,304,655 | 6,244,779 | 6,244,779 | + +## License +``` +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2020 Micha Mettke +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +``` +*/ +#ifndef SINFL_H_INCLUDED +#define SINFL_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define SINFL_PRE_TBL_SIZE 128 +#define SINFL_LIT_TBL_SIZE 1334 +#define SINFL_OFF_TBL_SIZE 402 + +struct sinfl { + int bits, bitcnt; + unsigned lits[SINFL_LIT_TBL_SIZE]; + unsigned dsts[SINFL_OFF_TBL_SIZE]; +}; +extern int sinflate(void *out, const void *in, int size); +extern int zsinflate(void *out, const void *in, int size); + +#ifdef __cplusplus +} +#endif + +#endif /* SINFL_H_INCLUDED */ + +#ifdef SINFL_IMPLEMENTATION + +#include /* memcpy, memset */ + +static int +sinfl_bsr(unsigned n) { +#ifdef _MSC_VER + _BitScanReverse(&n, n); + return n; +#elif defined(__GNUC__) || defined(__clang__) + return 31 - __builtin_clz(n); +#endif +} +static int +sinfl_get(const unsigned char **src, const unsigned char *end, struct sinfl *s, + int n) { + const unsigned char *in = *src; + int v = s->bits & ((1 << n)-1); + s->bits >>= n; + s->bitcnt = s->bitcnt - n; + s->bitcnt = s->bitcnt < 0 ? 0 : s->bitcnt; + while (s->bitcnt < 16 && in < end) { + s->bits |= (*in++) << s->bitcnt; + s->bitcnt += 8; + } + *src = in; + return v; +} +struct sinfl_gen { + int len; + int cnt; + int word; + short* sorted; +}; +static int +sinfl_build_tbl(struct sinfl_gen *gen, unsigned *tbl, int tbl_bits, + const int *cnt) { + int tbl_end = 0; + while (!(gen->cnt = cnt[gen->len])) { + ++gen->len; + } + tbl_end = 1 << gen->len; + while (gen->len <= tbl_bits) { + do {unsigned bit = 0; + tbl[gen->word] = (*gen->sorted++ << 16) | gen->len; + if (gen->word == tbl_end - 1) { + for (; gen->len < tbl_bits; gen->len++) { + memcpy(&tbl[tbl_end], tbl, (size_t)tbl_end * sizeof(tbl[0])); + tbl_end <<= 1; + } + return 1; + } + bit = 1 << sinfl_bsr((unsigned)(gen->word ^ (tbl_end - 1))); + gen->word &= bit - 1; + gen->word |= bit; + } while (--gen->cnt); + do { + if (++gen->len <= tbl_bits) { + memcpy(&tbl[tbl_end], tbl, (size_t)tbl_end * sizeof(tbl[0])); + tbl_end <<= 1; + } + } while (!(gen->cnt = cnt[gen->len])); + } + return 0; +} +static void +sinfl_build_subtbl(struct sinfl_gen *gen, unsigned *tbl, int tbl_bits, + const int *cnt) { + int sub_bits = 0; + int sub_start = 0; + int sub_prefix = -1; + int tbl_end = 1 << tbl_bits; + while (1) { + unsigned entry; + int bit, stride, i; + /* start new subtable */ + if ((gen->word & ((1 << tbl_bits)-1)) != sub_prefix) { + int used = 0; + sub_prefix = gen->word & ((1 << tbl_bits)-1); + sub_start = tbl_end; + sub_bits = gen->len - tbl_bits; + used = gen->cnt; + while (used < (1 << sub_bits)) { + sub_bits++; + used = (used << 1) + cnt[tbl_bits + sub_bits]; + } + tbl_end = sub_start + (1 << sub_bits); + tbl[sub_prefix] = (sub_start << 16) | 0x10 | (sub_bits & 0xf); + } + /* fill subtable */ + entry = (*gen->sorted << 16) | ((gen->len - tbl_bits) & 0xf); + gen->sorted++; + i = sub_start + (gen->word >> tbl_bits); + stride = 1 << (gen->len - tbl_bits); + do { + tbl[i] = entry; + i += stride; + } while (i < tbl_end); + if (gen->word == (1 << gen->len)-1) { + return; + } + bit = 1 << sinfl_bsr(gen->word ^ ((1 << gen->len) - 1)); + gen->word &= bit - 1; + gen->word |= bit; + gen->cnt--; + while (!gen->cnt) { + gen->cnt = cnt[++gen->len]; + } + } +} +static void +sinfl_build(unsigned *tbl, unsigned char *lens, int tbl_bits, int maxlen, + int symcnt) { + int i, used = 0; + short sort[288]; + int cnt[16] = {0}, off[16]= {0}; + struct sinfl_gen gen = {0}; + gen.sorted = sort; + gen.len = 1; + + for (i = 0; i < symcnt; ++i) + cnt[lens[i]]++; + off[1] = cnt[0]; + for (i = 1; i < maxlen; ++i) { + off[i + 1] = off[i] + cnt[i]; + used = (used << 1) + cnt[i]; + } + used = (used << 1) + cnt[i]; + for (i = 0; i < symcnt; ++i) + gen.sorted[off[lens[i]]++] = (short)i; + gen.sorted += off[0]; + + if (used < (1 << maxlen)){ + for (i = 0; i < 1 << tbl_bits; ++i) + tbl[i] = (0 << 16u) | 1; + return; + } + if (!sinfl_build_tbl(&gen, tbl, tbl_bits, cnt)){ + sinfl_build_subtbl(&gen, tbl, tbl_bits, cnt); + } +} +static int +sinfl_decode(const unsigned char **in, const unsigned char *end, + struct sinfl *s, const unsigned *tbl, int bit_len) { + int idx = s->bits & ((1 << bit_len) - 1); + unsigned key = tbl[idx]; + if (key & 0x10) { + /* sub-table lookup */ + int len = key & 0x0f; + sinfl_get(in, end, s, bit_len); + idx = s->bits & ((1 << len)-1); + key = tbl[((key >> 16) & 0xffff) + (unsigned)idx]; + } + sinfl_get(in, end, s, key & 0x0f); + return (key >> 16) & 0x0fff; +} +static int +sinfl_decompress(unsigned char *out, const unsigned char *in, int size) { + static const unsigned char order[] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + static const short dbase[30+2] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, + 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}; + static const unsigned char dbits[30+2] = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9, + 10,10,11,11,12,12,13,13,0,0}; + static const short lbase[29+2] = {3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35, + 43,51,59,67,83,99,115,131,163,195,227,258,0,0}; + static const unsigned char lbits[29+2] = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4, + 4,4,4,5,5,5,5,0,0,0}; + + const unsigned char *e = in + size, *o = out; + enum sinfl_states {hdr,stored,fixed,dyn,blk}; + enum sinfl_states state = hdr; + struct sinfl s = {0}; + int last = 0; + + sinfl_get(&in,e,&s,0); /* buffer input */ + while (in < e || s.bitcnt) { + switch (state) { + case hdr: { + int type = 0; /* block header */ + last = sinfl_get(&in,e,&s,1); + type = sinfl_get(&in,e,&s,2); + + switch (type) {default: return (int)(out-o); + case 0x00: state = stored; break; + case 0x01: state = fixed; break; + case 0x02: state = dyn; break;} + } break; + case stored: { + int len, nlen; /* uncompressed block */ + sinfl_get(&in,e,&s,s.bitcnt & 7); + len = sinfl_get(&in,e,&s,16); + nlen = sinfl_get(&in,e,&s,16); + in -= 2; s.bitcnt = 0; + + if (len > (e-in) || !len) + return (int)(out-o); + memcpy(out, in, (size_t)len); + in += len, out += len; + state = hdr; + } break; + case fixed: { + /* fixed huffman codes */ + int n; unsigned char lens[288+32]; + for (n = 0; n <= 143; n++) lens[n] = 8; + for (n = 144; n <= 255; n++) lens[n] = 9; + for (n = 256; n <= 279; n++) lens[n] = 7; + for (n = 280; n <= 287; n++) lens[n] = 8; + for (n = 0; n < 32; n++) lens[288+n] = 5; + + /* build lit/dist tables */ + sinfl_build(s.lits, lens, 10, 15, 288); + sinfl_build(s.dsts, lens + 288, 8, 15, 32); + state = blk; + } break; + case dyn: { + /* dynamic huffman codes */ + int n, i; + unsigned hlens[SINFL_PRE_TBL_SIZE]; + unsigned char nlens[19] = {0}, lens[288+32]; + int nlit = 257 + sinfl_get(&in,e,&s,5); + int ndist = 1 + sinfl_get(&in,e,&s,5); + int nlen = 4 + sinfl_get(&in,e,&s,4); + for (n = 0; n < nlen; n++) + nlens[order[n]] = (unsigned char)sinfl_get(&in,e,&s,3); + sinfl_build(hlens, nlens, 7, 7, 19); + + /* decode code lengths */ + for (n = 0; n < nlit + ndist;) { + int sym = sinfl_decode(&in, e, &s, hlens, 7); + switch (sym) {default: lens[n++] = (unsigned char)sym; break; + case 16: for (i=3+sinfl_get(&in,e,&s,2);i;i--,n++) lens[n]=lens[n-1]; break; + case 17: for (i=3+sinfl_get(&in,e,&s,3);i;i--,n++) lens[n]=0; break; + case 18: for (i=11+sinfl_get(&in,e,&s,7);i;i--,n++) lens[n]=0; break;} + } + /* build lit/dist tables */ + sinfl_build(s.lits, lens, 10, 15, nlit); + sinfl_build(s.dsts, lens + nlit, 8, 15, ndist); + state = blk; + } break; + case blk: { + /* decompress block */ + int i, sym = sinfl_decode(&in, e, &s, s.lits, 10); + if (sym > 256) {sym -= 257; /* match symbol */ + {int len = sinfl_get(&in, e, &s, lbits[sym]) + lbase[sym]; + int dsym = sinfl_decode(&in, e, &s, s.dsts, 8); + int offs = sinfl_get(&in, e, &s, dbits[dsym]) + dbase[dsym]; + if (offs > (int)(out-o)) { + return (int)(out-o); + } else if (offs == 1) { + /* rle match copying */ + unsigned char c = *(out - offs); + unsigned long w = (c << 24) | (c << 16) | (c << 8) | c; + for (i = 0; i < len >> 2; ++i) { + memcpy(out, &w, 4); + out += 4; + } + len = len & 3; + } else if (offs >= 4) { + /* copy match */ + int wcnt = len >> 2; + for (i = 0; i < wcnt; ++i) { + unsigned long w = 0; + memcpy(&w, out - offs, 4); + memcpy(out, &w, 4); + out += 4; + } + len = len & 3; + } + for (i = 0; i < len; ++i) + {*out = *(out-offs), out++;} + } + } else if (sym == 256) { + /* end of block */ + if (last) return (int)(out-o); + state = hdr; + break; + /* literal */ + } else *out++ = (unsigned char)sym; + } break;} + } + return (int)(out-o); +} +extern int +sinflate(void *out, const void *in, int size) { + return sinfl_decompress((unsigned char*)out, (const unsigned char*)in, size); +} +static unsigned +sinfl_adler32(unsigned adler32, const unsigned char *in, int in_len) { + const unsigned ADLER_MOD = 65521; + unsigned s1 = adler32 & 0xffff; + unsigned s2 = adler32 >> 16; + unsigned blk_len, i; + + blk_len = in_len % 5552; + while (in_len) { + for (i=0; i + 7 < blk_len; i += 8) { + s1 += in[0]; s2 += s1; + s1 += in[1]; s2 += s1; + s1 += in[2]; s2 += s1; + s1 += in[3]; s2 += s1; + s1 += in[4]; s2 += s1; + s1 += in[5]; s2 += s1; + s1 += in[6]; s2 += s1; + s1 += in[7]; s2 += s1; + in += 8; + } + for (; i < blk_len; ++i) + s1 += *in++, s2 += s1; + s1 %= ADLER_MOD; s2 %= ADLER_MOD; + in_len -= blk_len; + blk_len = 5552; + } return (unsigned)(s2 << 16) + (unsigned)s1; +} +extern int +zsinflate(void *out, const void *mem, int size) { + const unsigned char *in = (const unsigned char*)mem; + if (size >= 6) { + const unsigned char *eob = in + size - 4; + int n = sinfl_decompress((unsigned char*)out, in + 2u, size); + unsigned a = sinfl_adler32(1u, (unsigned char*)out, n); + unsigned h = eob[0] << 24 | eob[1] << 16 | eob[2] << 8 | eob[3] << 0; + return a == h ? n : -1; + } else { + return -1; + } +} +#endif + From a0a840101c499d19f9b455916a231123a0cd8e45 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 12 Jan 2021 22:51:26 +0100 Subject: [PATCH 11/23] Update miniaudio to v0.10.30 #1518 --- src/external/miniaudio.h | 2944 ++++++++++++++++++++++++++------------ 1 file changed, 1991 insertions(+), 953 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 7c1c03957..085f307db 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.10.25 - 2020-11-15 +miniaudio - v0.10.30 - 2021-01-10 David Reid - mackron@gmail.com @@ -239,7 +239,7 @@ first is to use the `MA_NO_RUNTIME_LINKING` option, like so: ```c #ifdef __APPLE__ #define MA_NO_RUNTIME_LINKING - #endif + #endif #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" ``` @@ -268,6 +268,9 @@ The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio( AAudio is the highest priority backend on Android. This should work out of the box without needing any kind of compiler configuration. Support for AAudio starts with Android 8 which means older versions will fall back to OpenSL|ES which requires API level 16+. +There have been reports that the OpenSL|ES backend fails to initialize on some Android based devices due to `dlopen()` failing to open "libOpenSLES.so". If +this happens on your platform you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. + 2.6. Emscripten --------------- The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. You cannot use -std=c* compiler flags, nor -ansi. @@ -342,8 +345,10 @@ The Emscripten build emits Web Audio JavaScript directly and should compile clea | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's notarization process. When enabling this, you may need to avoid | | | using `-std=c89` or `-std=c99` on Linux builds or else you may end up with compilation errors due to conflicts with `timespec` | | | and `timeval` data types. | + | | | + | | You may need to enable this if your target platform does not allow runtime linking via `dlopen()`. | +-----------------------+---------------------------------------------------------------------------------------------------------------------------------+ - | MA_LOG_LEVEL [level] | Sets the logging level. Set level to one of the following: | + | MA_LOG_LEVEL [level] | Sets the logging level. Set `level` to one of the following: | | | | | | ``` | | | MA_LOG_LEVEL_VERBOSE | @@ -352,7 +357,7 @@ The Emscripten build emits Web Audio JavaScript directly and should compile clea | | MA_LOG_LEVEL_ERROR | | | ``` | +-----------------------+---------------------------------------------------------------------------------------------------------------------------------+ - | MA_DEBUG_OUTPUT | Enable printf() debug output. | + | MA_DEBUG_OUTPUT | Enable `printf()` debug output. | +-----------------------+---------------------------------------------------------------------------------------------------------------------------------+ | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | +-----------------------+---------------------------------------------------------------------------------------------------------------------------------+ @@ -410,7 +415,8 @@ All formats are native-endian. 4. Decoding =========== -The `ma_decoder` API is used for reading audio files. The following formats are supported: +The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from devices and can be used independently. The following formats are +supported: +---------+------------------+----------+ | Format | Decoding Backend | Built-In | @@ -463,14 +469,14 @@ with `ma_decoder_init()`. Here is an example for loading a decoder from a file: ma_decoder_uninit(&decoder); ``` -When initializing a decoder, you can optionally pass in a pointer to a ma_decoder_config object (the NULL argument in the example above) which allows you to -configure the output format, channel count, sample rate and channel map: +When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object (the `NULL` argument in the example above) which allows you +to configure the output format, channel count, sample rate and channel map: ```c ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000); ``` -When passing in NULL for decoder config in `ma_decoder_init*()`, the output format will be the same as that defined by the decoding backend. +When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the same as that defined by the decoding backend. Data is read from the decoder as PCM frames. This will return the number of PCM frames actually read. If the return value is less than the requested number of PCM frames it means you've reached the end: @@ -671,10 +677,10 @@ be one of the following: | ma_standard_channel_map_vorbis | Vorbis channel map. | | ma_standard_channel_map_sound4 | FreeBSD's sound(4). | | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. | - | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering | + | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering | +-----------------------------------+-----------------------------------------------------------+ -Below are the channel maps used by default in miniaudio (ma_standard_channel_map_default): +Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`): +---------------+---------------------------------+ | Channel Count | Mapping | @@ -841,7 +847,7 @@ The API for the linear resampler is the same as the main resampler API, only it' ------------------------- The Speex resampler is made up of third party code which is released under the BSD license. Because it is licensed differently to miniaudio, which is public domain, it is strictly opt-in and all of it's code is stored in separate files. If you opt-in to the Speex resampler you must consider the license text in it's -source files. To opt-in, you must first #include the following file before the implementation of miniaudio.h: +source files. To opt-in, you must first `#include` the following file before the implementation of miniaudio.h: ```c #include "extras/speex_resampler/ma_speex_resampler.h" @@ -924,8 +930,8 @@ The data converter supports multiple channels and is always interleaved (both in Sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the only configuration property that can be changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of `ma_data_converter_config` is -set to MA_TRUE. To change the sample rate, use `ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. The resampling -algorithm cannot be changed after initialization. +set to `MA_TRUE`. To change the sample rate, use `ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. The +resampling algorithm cannot be changed after initialization. Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process frames, use `ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames you can fit in the output buffer and the number @@ -1016,7 +1022,7 @@ Filtering can be applied in-place by passing in the same pointer for both the in ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount); ``` -The maximum filter order is limited to MA_MAX_FILTER_ORDER which is set to 8. If you need more, you can chain first and second order filters together. +The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, you can chain first and second order filters together. ```c for (iFilter = 0; iFilter < filterCount; iFilter += 1) { @@ -1145,8 +1151,8 @@ miniaudio supports generation of sine, square, triangle and sawtooth waveforms. ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount); ``` -The amplitude, frequency and sample rate can be changed dynamically with `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()` and -`ma_waveform_set_sample_rate()` respectively. +The amplitude, frequency, type, and sample rate can be changed dynamically with `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, +`ma_waveform_set_type()`, and `ma_waveform_set_sample_rate()` respectively. You can invert the waveform by setting the amplitude to a negative value. You can use this to control whether or not a sawtooth has a positive or negative ramp, for example. @@ -1188,7 +1194,9 @@ miniaudio supports generation of white, pink and Brownian noise via the `ma_nois ``` The noise API uses simple LCG random number generation. It supports a custom seed which is useful for things like automated testing requiring reproducibility. -Setting the seed to zero will default to MA_DEFAULT_LCG_SEED. +Setting the seed to zero will default to `MA_DEFAULT_LCG_SEED`. + +The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`, `ma_noise_set_seed()`, and `ma_noise_set_type()` respectively. By default, the noise API will use different values for different channels. So, for example, the left side in a stereo stream will be different to the right side. To instead have each channel use the same random value, set the `duplicateChannels` member of the noise config to true, like so: @@ -1235,10 +1243,10 @@ Audio buffers are initialised using the standard configuration system used every ma_audio_buffer_uninit(&buffer); ``` -In the example above, the memory pointed to by `pExistingData` will _not_ be copied and is how an application can do self-managed memory allocation. If you +In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an application can do self-managed memory allocation. If you would rather make a copy of the data, use `ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. -Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure _and_ the raw audio data in a contiguous block of memory. That is, +Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the raw audio data in a contiguous block of memory. That is, the raw audio data will be located immediately after the `ma_audio_buffer` structure. To do this, use `ma_audio_buffer_alloc_and_init()`: ```c @@ -1256,7 +1264,7 @@ the raw audio data will be located immediately after the `ma_audio_buffer` struc } ... - + ma_audio_buffer_uninit_and_free(&buffer); ``` @@ -1326,14 +1334,14 @@ routines. Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` be Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is offset from each other based on the stride. To manage your sub-buffers you can use `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and `ma_pcm_rb_get_subbuffer_ptr()`. -Use 'ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section of the ring buffer. You specify the number of frames you +Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section of the ring buffer. You specify the number of frames you need, and on output it will set to what was actually acquired. If the read or write pointer is positioned such that the number of frames requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number of frames you're given may be less than the number you requested. After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is where the read/write pointers are updated. When you commit you need to pass in the buffer that was returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and -`ma_pcm_rb_commit_write()` is what's used to increment the pointers. +`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was originally requested. If you want to correct for drift between the write pointer and the read pointer you can use a combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and `ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only move the read pointer forward via @@ -1370,6 +1378,7 @@ The following backends are supported by miniaudio. | AAudio | ma_backend_aaudio | Android 8+ | | OpenSL ES | ma_backend_opensl | Android (API level 16+) | | Web Audio | ma_backend_webaudio | Web (via Emscripten) | + | Custom | ma_backend_custom | Cross Platform | | Null | ma_backend_null | Cross Platform (not used on Web) | +-------------+-----------------------+--------------------------------------------------------+ @@ -1425,7 +1434,7 @@ Some backends have some nuance details you may want to be aware of. ======================= - Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though not all have been tested. -- The contents of the output buffer passed into the data callback will always be pre-initialized to zero unless the `noPreZeroedOutputBuffer` config variable +- The contents of the output buffer passed into the data callback will always be pre-initialized to silence unless the `noPreZeroedOutputBuffer` config variable in `ma_device_config` is set to true, in which case it'll be undefined which will require you to write something to the entire buffer. - By default miniaudio will automatically clip samples. This only applies when the playback sample format is configured as `ma_format_f32`. If you are doing clipping yourself, you can disable this overhead by setting `noClip` to true in the device config. @@ -1447,7 +1456,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 10 -#define MA_VERSION_REVISION 25 +#define MA_VERSION_REVISION 30 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -1567,6 +1576,8 @@ typedef ma_uint16 wchar_t; #else #define MA_INLINE inline __attribute__((always_inline)) #endif +#elif defined(__WATCOMC__) + #define MA_INLINE __inline #else #define MA_INLINE #endif @@ -1763,6 +1774,7 @@ typedef int ma_result; #define MA_NO_DEVICE -104 #define MA_API_NOT_FOUND -105 #define MA_INVALID_DEVICE_CONFIG -106 +#define MA_LOOP -107 /* State errors. */ #define MA_DEVICE_NOT_INITIALIZED -200 @@ -1881,7 +1893,7 @@ typedef struct #ifndef MA_NO_THREADING -/* Thread priorties should be ordered such that the default priority of the worker thread is 0. */ +/* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ typedef enum { ma_thread_priority_idle = -5, @@ -2502,11 +2514,11 @@ typedef struct float f32[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; ma_int32 s16[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; } weights; - ma_bool32 isPassthrough : 1; - ma_bool32 isSimpleShuffle : 1; - ma_bool32 isSimpleMonoExpansion : 1; - ma_bool32 isStereoToMono : 1; - ma_uint8 shuffleTable[MA_MAX_CHANNELS]; + ma_bool8 isPassthrough; + ma_bool8 isSimpleShuffle; + ma_bool8 isSimpleMonoExpansion; + ma_bool8 isStereoToMono; + ma_uint8 shuffleTable[MA_MAX_CHANNELS]; } ma_channel_converter; MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter); @@ -2556,11 +2568,11 @@ typedef struct ma_data_converter_config config; ma_channel_converter channelConverter; ma_resampler resampler; - ma_bool32 hasPreFormatConversion : 1; - ma_bool32 hasPostFormatConversion : 1; - ma_bool32 hasChannelConverter : 1; - ma_bool32 hasResampler : 1; - ma_bool32 isPassthrough : 1; + ma_bool8 hasPreFormatConversion; + ma_bool8 hasPostFormatConversion; + ma_bool8 hasChannelConverter; + ma_bool8 hasResampler; + ma_bool8 isPassthrough; } ma_data_converter; MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_data_converter* pConverter); @@ -2612,12 +2624,20 @@ Interleaves a group of deinterleaved buffers. */ MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames); + /************************************************************************************************************************************************************ Channel Maps ************************************************************************************************************************************************************/ +/* +Initializes a blank channel map. + +When a blank channel map is specified anywhere it indicates that the native channel map should be used. +*/ +MA_API void ma_channel_map_init_blank(ma_uint32 channels, ma_channel* pChannelMap); + /* Helper for retrieving a standard channel map. @@ -2710,8 +2730,8 @@ typedef struct ma_uint32 subbufferStrideInBytes; volatile ma_uint32 encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. */ volatile ma_uint32 encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. */ - ma_bool32 ownsBuffer : 1; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ - ma_bool32 clearOnWriteAcquire : 1; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ + ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ + ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ ma_allocation_callbacks allocationCallbacks; } ma_rb; @@ -3238,7 +3258,7 @@ typedef struct a device with settings outside of this range, but it just means the data will be converted using miniaudio's data conversion pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided here mainly for informational purposes or in the rare case that someone might find it useful. - + These will be set to 0 when returned by ma_context_enumerate_devices() or ma_context_get_devices(). */ ma_uint32 formatCount; @@ -3256,7 +3276,7 @@ typedef struct ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */ ma_uint32 channels; /* If set to 0, all channels are supported. */ ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */ - ma_uint32 flags; + ma_uint32 flags; } nativeDataFormats[64]; } ma_device_info; @@ -3268,8 +3288,8 @@ struct ma_device_config ma_uint32 periodSizeInMilliseconds; ma_uint32 periods; ma_performance_profile performanceProfile; - ma_bool32 noPreZeroedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to zero. */ - ma_bool32 noClip; /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */ + ma_bool8 noPreZeroedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to zero. */ + ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */ ma_device_callback_proc dataCallback; ma_stop_proc stopCallback; void* pUserData; @@ -3291,6 +3311,7 @@ struct ma_device_config ma_format format; ma_uint32 channels; ma_channel channelMap[MA_MAX_CHANNELS]; + ma_channel_mix_mode channelMixMode; ma_share_mode shareMode; } playback; struct @@ -3299,15 +3320,16 @@ struct ma_device_config ma_format format; ma_uint32 channels; ma_channel channelMap[MA_MAX_CHANNELS]; + ma_channel_mix_mode channelMixMode; ma_share_mode shareMode; } capture; struct { - ma_bool32 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool32 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool32 noAutoStreamRouting; /* Disables automatic stream routing. */ - ma_bool32 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ + ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ + ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ + ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ + ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ } wasapi; struct { @@ -3423,7 +3445,7 @@ sample rate will need to be determined before calculating the period size in fra object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set). Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses -asynchronous reading and writing, `onDeviceStart()` is optional, so long as the device is automatically started in `onDeviceWrite()`. +asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented. The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and @@ -3434,11 +3456,8 @@ This allows miniaudio to then process any necessary data conversion and then pas If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceWorkerThread()` callback which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. -The audio thread follows this general flow: - - 1) Start the device before entering the main loop. - 2) Run data delivery logic in a loop while `ma_device_get_state() == MA_STATE_STARTED` and no errors have been encounted. - 3) Stop thd device after leaving the main loop. +The audio thread should run data delivery logic in a loop while `ma_device_get_state() == MA_STATE_STARTED` and no errors have been +encounted. Do not start or stop the device here. That will be handled from outside the `onDeviceAudioThread()` callback. The invocation of the `onDeviceAudioThread()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this callback. When the device is stopped, the `ma_device_get_state() == MA_STATE_STARTED` condition will fail and the loop will be terminated @@ -3495,19 +3514,19 @@ struct ma_context_config struct ma_context { ma_backend_callbacks callbacks; - ma_backend backend; /* DirectSound, ALSA, etc. */ + ma_backend backend; /* DirectSound, ALSA, etc. */ ma_log_proc logCallback; ma_thread_priority threadPriority; size_t threadStackSize; void* pUserData; ma_allocation_callbacks allocationCallbacks; - ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */ - ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */ - ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */ + ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */ + ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */ + ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */ ma_uint32 playbackDeviceInfoCount; ma_uint32 captureDeviceInfoCount; - ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */ - ma_bool32 isBackendAsynchronous : 1; /* Set when the context is initialized. Set to 1 for asynchronous backends such as Core Audio and JACK. Do not modify. */ + ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */ + ma_bool8 isBackendAsynchronous; /* Set when the context is initialized. Set to 1 for asynchronous backends such as Core Audio and JACK. Do not modify. */ ma_result (* onUninit )(ma_context* pContext); ma_result (* onEnumDevices )(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); /* Return false from the callback to stop enumeration. */ @@ -3721,14 +3740,14 @@ struct ma_context ma_handle hCoreFoundation; ma_proc CFStringGetCString; ma_proc CFRelease; - + ma_handle hCoreAudio; ma_proc AudioObjectGetPropertyData; ma_proc AudioObjectGetPropertyDataSize; ma_proc AudioObjectSetPropertyData; ma_proc AudioObjectAddPropertyListener; ma_proc AudioObjectRemovePropertyListener; - + ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */ ma_proc AudioComponentFindNext; ma_proc AudioComponentInstanceDispose; @@ -3741,9 +3760,9 @@ struct ma_context ma_proc AudioUnitSetProperty; ma_proc AudioUnitInitialize; ma_proc AudioUnitRender; - + /*AudioComponent*/ ma_ptr component; - + ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ } coreaudio; #endif @@ -3908,12 +3927,12 @@ struct ma_device ma_event stopEvent; ma_thread thread; ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ - ma_bool32 usingDefaultSampleRate : 1; - ma_bool32 usingDefaultBufferSize : 1; - ma_bool32 usingDefaultPeriods : 1; - ma_bool32 isOwnerOfContext : 1; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ - ma_bool32 noPreZeroedOutputBuffer : 1; - ma_bool32 noClip : 1; + ma_bool8 usingDefaultSampleRate; + ma_bool8 usingDefaultBufferSize; + ma_bool8 usingDefaultPeriods; + ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ + ma_bool8 noPreZeroedOutputBuffer; + ma_bool8 noClip; volatile float masterVolumeFactor; /* Volatile so we can use some thread safety when applying volume to periods. */ ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ struct @@ -3933,9 +3952,6 @@ struct ma_device ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */ ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ - ma_bool32 usingDefaultFormat : 1; - ma_bool32 usingDefaultChannels : 1; - ma_bool32 usingDefaultChannelMap : 1; ma_format format; ma_uint32 channels; ma_channel channelMap[MA_MAX_CHANNELS]; @@ -3945,16 +3961,17 @@ struct ma_device ma_channel internalChannelMap[MA_MAX_CHANNELS]; ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; + ma_channel_mix_mode channelMixMode; ma_data_converter converter; + ma_bool8 usingDefaultFormat; + ma_bool8 usingDefaultChannels; + ma_bool8 usingDefaultChannelMap; } playback; struct { ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */ ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ - ma_bool32 usingDefaultFormat : 1; - ma_bool32 usingDefaultChannels : 1; - ma_bool32 usingDefaultChannelMap : 1; ma_format format; ma_uint32 channels; ma_channel channelMap[MA_MAX_CHANNELS]; @@ -3964,7 +3981,11 @@ struct ma_device ma_channel internalChannelMap[MA_MAX_CHANNELS]; ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; + ma_channel_mix_mode channelMixMode; ma_data_converter converter; + ma_bool8 usingDefaultFormat; + ma_bool8 usingDefaultChannels; + ma_bool8 usingDefaultChannelMap; } capture; union @@ -3976,26 +3997,27 @@ struct ma_device /*IAudioClient**/ ma_ptr pAudioClientCapture; /*IAudioRenderClient**/ ma_ptr pRenderClient; /*IAudioCaptureClient**/ ma_ptr pCaptureClient; - /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ + /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ ma_IMMNotificationClient notificationClient; - /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ - /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ - ma_uint32 actualPeriodSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ + /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ + /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ + ma_uint32 actualPeriodSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ ma_uint32 actualPeriodSizeInFramesCapture; ma_uint32 originalPeriodSizeInFrames; ma_uint32 originalPeriodSizeInMilliseconds; ma_uint32 originalPeriods; - ma_bool32 hasDefaultPlaybackDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */ - ma_bool32 hasDefaultCaptureDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */ + ma_performance_profile originalPerformanceProfile; + volatile ma_bool32 hasDefaultPlaybackDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */ + volatile ma_bool32 hasDefaultCaptureDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */ ma_uint32 periodSizeInFramesPlayback; ma_uint32 periodSizeInFramesCapture; - ma_bool32 isStartedCapture; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */ - ma_bool32 isStartedPlayback; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */ - ma_bool32 noAutoConvertSRC : 1; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool32 noDefaultQualitySRC : 1; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool32 noHardwareOffloading : 1; - ma_bool32 allowCaptureAutoStreamRouting : 1; - ma_bool32 allowPlaybackAutoStreamRouting : 1; + volatile ma_bool32 isStartedCapture; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */ + volatile ma_bool32 isStartedPlayback; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */ + ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ + ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ + ma_bool8 noHardwareOffloading; + ma_bool8 allowCaptureAutoStreamRouting; + ma_bool8 allowPlaybackAutoStreamRouting; } wasapi; #endif #ifdef MA_SUPPORT_DSOUND @@ -4032,8 +4054,8 @@ struct ma_device { /*snd_pcm_t**/ ma_ptr pPCMPlayback; /*snd_pcm_t**/ ma_ptr pPCMCapture; - ma_bool32 isUsingMMapPlayback : 1; - ma_bool32 isUsingMMapCapture : 1; + ma_bool8 isUsingMMapPlayback; + ma_bool8 isUsingMMapCapture; } alsa; #endif #ifdef MA_SUPPORT_PULSEAUDIO @@ -4052,7 +4074,6 @@ struct ma_device /*jack_port_t**/ ma_ptr pPortsCapture[MA_MAX_CHANNELS]; float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ float* pIntermediaryBufferCapture; - ma_pcm_rb duplexRB; } jack; #endif #ifdef MA_SUPPORT_COREAUDIO @@ -4132,7 +4153,6 @@ struct ma_device { int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */ int indexCapture; - ma_pcm_rb duplexRB; /* In external capture format. */ } webaudio; #endif #ifdef MA_SUPPORT_NULL @@ -4141,15 +4161,16 @@ struct ma_device ma_thread deviceThread; ma_event operationEvent; ma_event operationCompletionEvent; + ma_semaphore operationSemaphore; ma_uint32 operation; - ma_result operationResult; + volatile ma_result operationResult; ma_timer timer; double priorRunTime; ma_uint32 currentPeriodFramesRemainingPlayback; ma_uint32 currentPeriodFramesRemainingCapture; ma_uint64 lastProcessedFramePlayback; ma_uint64 lastProcessedFrameCapture; - ma_bool32 isStarted; + volatile ma_bool32 isStarted; } null_device; #endif }; @@ -5552,17 +5573,17 @@ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend); /* Locks a spinlock. */ -MA_API ma_result ma_spinlock_lock(ma_spinlock* pSpinlock); +MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock); /* Locks a spinlock, but does not yield() when looping. */ -MA_API ma_result ma_spinlock_lock_noyield(ma_spinlock* pSpinlock); +MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock); /* Unlocks a spinlock. */ -MA_API ma_result ma_spinlock_unlock(ma_spinlock* pSpinlock); +MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock); /* @@ -5655,6 +5676,8 @@ Offsets a pointer by the specified number of PCM frames. */ MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); +static MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_const_ptr((void*)p, offsetInFrames, ma_format_f32, channels); } +static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); } /* @@ -6103,10 +6126,9 @@ MA_API ma_uint64 ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFram MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex); MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude); MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency); +MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); - - typedef enum { ma_noise_type_white, @@ -6148,6 +6170,9 @@ typedef struct MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise); MA_API ma_uint64 ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount); +MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude); +MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed); +MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type); #endif /* MA_NO_GENERATION */ @@ -6667,11 +6692,16 @@ static MA_INLINE void ma_yield() { #if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) /* x86/x64 */ - #if defined(_MSC_VER) && !defined(__clang__) + #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__) #if _MSC_VER >= 1400 _mm_pause(); #else - __asm pause; + #if defined(__DMC__) + /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */ + __asm nop; + #else + __asm pause; + #endif #endif #else __asm__ __volatile__ ("pause"); @@ -7665,12 +7695,13 @@ _wfopen() isn't always available in all compilation environments. * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). * MinGW-64 (both 32- and 64-bit) seems to support it. * MinGW wraps it in !defined(__STRICT_ANSI__). + * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. */ #if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || !defined(__STRICT_ANSI__) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) #define MA_HAS_WFOPEN #endif #endif @@ -8138,7 +8169,7 @@ typedef unsigned char c89atomic_flag; #elif defined(__arm__) || defined(_M_ARM) #define C89ATOMIC_ARM #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) #define C89ATOMIC_INLINE __forceinline #elif defined(__GNUC__) #if defined(__STRICT_ANSI__) @@ -8146,10 +8177,12 @@ typedef unsigned char c89atomic_flag; #else #define C89ATOMIC_INLINE inline __attribute__((always_inline)) #endif +#elif defined(__WATCOMC__) || defined(__DMC__) + #define C89ATOMIC_INLINE __inline #else #define C89ATOMIC_INLINE #endif -#if defined(_MSC_VER) +#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) #define c89atomic_memory_order_relaxed 0 #define c89atomic_memory_order_consume 1 #define c89atomic_memory_order_acquire 2 @@ -8158,24 +8191,56 @@ typedef unsigned char c89atomic_flag; #define c89atomic_memory_order_seq_cst 5 #if _MSC_VER >= 1400 #include - #define c89atomic_exchange_explicit_8( dst, src, order) (c89atomic_uint8 )_InterlockedExchange8 ((volatile char* )dst, (char )src) - #define c89atomic_exchange_explicit_16(dst, src, order) (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src) - #define c89atomic_exchange_explicit_32(dst, src, order) (c89atomic_uint32)_InterlockedExchange ((volatile long* )dst, (long )src) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + (void)order; + return (c89atomic_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); + } + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + (void)order; + return (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); + } + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + (void)order; + return (c89atomic_uint32)_InterlockedExchange((volatile long*)dst, (long)src); + } #if defined(C89ATOMIC_64BIT) - #define c89atomic_exchange_explicit_64(dst, src, order) (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + (void)order; + return (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); + } #endif - #define c89atomic_fetch_add_explicit_8( dst, src, order) (c89atomic_uint8 )_InterlockedExchangeAdd8 ((volatile char* )dst, (char )src) - #define c89atomic_fetch_add_explicit_16(dst, src, order) (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src) - #define c89atomic_fetch_add_explicit_32(dst, src, order) (c89atomic_uint32)_InterlockedExchangeAdd ((volatile long* )dst, (long )src) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + (void)order; + return (c89atomic_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); + } + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + (void)order; + return (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); + } + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + (void)order; + return (c89atomic_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); + } #if defined(C89ATOMIC_64BIT) - #define c89atomic_fetch_add_explicit_64(dst, src, order) (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + (void)order; + return (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); + } #endif #define c89atomic_compare_and_swap_8( dst, expected, desired) (c89atomic_uint8 )_InterlockedCompareExchange8 ((volatile char* )dst, (char )desired, (char )expected) #define c89atomic_compare_and_swap_16(dst, expected, desired) (c89atomic_uint16)_InterlockedCompareExchange16((volatile short* )dst, (short )desired, (short )expected) #define c89atomic_compare_and_swap_32(dst, expected, desired) (c89atomic_uint32)_InterlockedCompareExchange ((volatile long* )dst, (long )desired, (long )expected) #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile long long*)dst, (long long)desired, (long long)expected) #if defined(C89ATOMIC_X64) - #define c89atomic_thread_fence(order) __faststorefence() + #define c89atomic_thread_fence(order) __faststorefence(), (void)order #else static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order) { @@ -8185,98 +8250,126 @@ typedef unsigned char c89atomic_flag; } #endif #else - #if defined(__i386) || defined(_M_IX86) - static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(int order) + #if defined(C89ATOMIC_X86) + static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(c89atomic_memory_order order) { - volatile c89atomic_uint32 barrier; (void)order; __asm { - xchg barrier, eax + lock add [esp], 0 } } - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, int order) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { + c89atomic_uint8 result = 0; (void)order; __asm { mov ecx, dst mov al, src lock xchg [ecx], al + mov result, al } + return result; } - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, int order) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { + c89atomic_uint16 result = 0; (void)order; __asm { mov ecx, dst mov ax, src lock xchg [ecx], ax + mov result, ax } + return result; } - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, int order) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { + c89atomic_uint32 result = 0; (void)order; __asm { mov ecx, dst mov eax, src lock xchg [ecx], eax + mov result, eax } + return result; } - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, int order) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { + c89atomic_uint8 result = 0; (void)order; __asm { mov ecx, dst mov al, src lock xadd [ecx], al + mov result, al } + return result; } - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, int order) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { + c89atomic_uint16 result = 0; (void)order; __asm { mov ecx, dst mov ax, src lock xadd [ecx], ax + mov result, ax } + return result; } - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, int order) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { + c89atomic_uint32 result = 0; (void)order; __asm { mov ecx, dst mov eax, src lock xadd [ecx], eax + mov result, eax } + return result; } static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) { + c89atomic_uint8 result = 0; __asm { mov ecx, dst mov al, expected mov dl, desired lock cmpxchg [ecx], dl + mov result, al } + return result; } static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) { + c89atomic_uint16 result = 0; __asm { mov ecx, dst mov ax, expected mov dx, desired lock cmpxchg [ecx], dx + mov result, ax } + return result; } static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) { + c89atomic_uint32 result = 0; __asm { mov ecx, dst mov eax, expected mov edx, desired lock cmpxchg [ecx], edx + mov result, eax } + return result; } static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) { + c89atomic_uint32 resultEAX = 0; + c89atomic_uint32 resultEDX = 0; __asm { mov esi, dst mov eax, dword ptr expected @@ -8284,24 +8377,43 @@ typedef unsigned char c89atomic_flag; mov ebx, dword ptr desired mov ecx, dword ptr desired + 4 lock cmpxchg8b qword ptr [esi] + mov resultEAX, eax + mov resultEDX, edx } + return ((c89atomic_uint64)resultEDX << 32) | resultEAX; } #else - error "Unsupported architecture." + #error Unsupported architecture. #endif #endif #define c89atomic_compiler_fence() c89atomic_thread_fence(c89atomic_memory_order_seq_cst) #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) - #define c89atomic_load_explicit_8( ptr, order) c89atomic_compare_and_swap_8 (ptr, 0, 0) - #define c89atomic_load_explicit_16(ptr, order) c89atomic_compare_and_swap_16(ptr, 0, 0) - #define c89atomic_load_explicit_32(ptr, order) c89atomic_compare_and_swap_32(ptr, 0, 0) - #define c89atomic_load_explicit_64(ptr, order) c89atomic_compare_and_swap_64(ptr, 0, 0) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile c89atomic_uint8* ptr, c89atomic_memory_order order) + { + (void)order; + return c89atomic_compare_and_swap_8(ptr, 0, 0); + } + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile c89atomic_uint16* ptr, c89atomic_memory_order order) + { + (void)order; + return c89atomic_compare_and_swap_16(ptr, 0, 0); + } + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile c89atomic_uint32* ptr, c89atomic_memory_order order) + { + (void)order; + return c89atomic_compare_and_swap_32(ptr, 0, 0); + } + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile c89atomic_uint64* ptr, c89atomic_memory_order order) + { + (void)order; + return c89atomic_compare_and_swap_64(ptr, 0, 0); + } #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order) #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order) #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order) #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order) #if defined(C89ATOMIC_32BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, int order) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { volatile c89atomic_uint64 oldValue; do { @@ -8310,7 +8422,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, int order) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { volatile c89atomic_uint64 oldValue; volatile c89atomic_uint64 newValue; @@ -8322,7 +8434,7 @@ typedef unsigned char c89atomic_flag; return oldValue; } #endif - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, int order) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { volatile c89atomic_uint8 oldValue; volatile c89atomic_uint8 newValue; @@ -8333,7 +8445,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, int order) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { volatile c89atomic_uint16 oldValue; volatile c89atomic_uint16 newValue; @@ -8344,7 +8456,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, int order) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { volatile c89atomic_uint32 oldValue; volatile c89atomic_uint32 newValue; @@ -8355,7 +8467,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, int order) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { volatile c89atomic_uint64 oldValue; volatile c89atomic_uint64 newValue; @@ -8366,7 +8478,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, int order) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { volatile c89atomic_uint8 oldValue; volatile c89atomic_uint8 newValue; @@ -8377,7 +8489,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, int order) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { volatile c89atomic_uint16 oldValue; volatile c89atomic_uint16 newValue; @@ -8388,7 +8500,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, int order) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { volatile c89atomic_uint32 oldValue; volatile c89atomic_uint32 newValue; @@ -8399,7 +8511,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, int order) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { volatile c89atomic_uint64 oldValue; volatile c89atomic_uint64 newValue; @@ -8410,7 +8522,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, int order) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { volatile c89atomic_uint8 oldValue; volatile c89atomic_uint8 newValue; @@ -8421,7 +8533,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, int order) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { volatile c89atomic_uint16 oldValue; volatile c89atomic_uint16 newValue; @@ -8432,7 +8544,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, int order) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { volatile c89atomic_uint32 oldValue; volatile c89atomic_uint32 newValue; @@ -8443,7 +8555,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, int order) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { volatile c89atomic_uint64 oldValue; volatile c89atomic_uint64 newValue; @@ -8454,7 +8566,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, int order) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { volatile c89atomic_uint8 oldValue; volatile c89atomic_uint8 newValue; @@ -8465,7 +8577,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, int order) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { volatile c89atomic_uint16 oldValue; volatile c89atomic_uint16 newValue; @@ -8476,7 +8588,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, int order) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { volatile c89atomic_uint32 oldValue; volatile c89atomic_uint32 newValue; @@ -8487,7 +8599,7 @@ typedef unsigned char c89atomic_flag; (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, int order) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { volatile c89atomic_uint64 oldValue; volatile c89atomic_uint64 newValue; @@ -8585,71 +8697,497 @@ typedef unsigned char c89atomic_flag; #define c89atomic_memory_order_release 4 #define c89atomic_memory_order_acq_rel 5 #define c89atomic_memory_order_seq_cst 6 - #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #define c89atomic_thread_fence(order) __sync_synchronize() - #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) - { - if (order > c89atomic_memory_order_acquire) { - __sync_synchronize(); + #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory") + #if defined(__GNUC__) + #define c89atomic_thread_fence(order) __sync_synchronize(), (void)order + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + if (order > c89atomic_memory_order_acquire) { + __sync_synchronize(); + } + return __sync_lock_test_and_set(dst, src); } - return __sync_lock_test_and_set(dst, src); - } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + volatile c89atomic_uint16 oldValue; + do { + oldValue = *dst; + } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + volatile c89atomic_uint32 oldValue; + do { + oldValue = *dst; + } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + volatile c89atomic_uint64 oldValue; + do { + oldValue = *dst; + } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + #define c89atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #else + #if defined(C89ATOMIC_X86) + #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") + #elif defined(C89ATOMIC_X64) + #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") + #else + #error Unsupported architecture. Please submit a feature request. + #endif + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) + { + volatile c89atomic_uint8 result; + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) + { + volatile c89atomic_uint16 result; + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) + { + volatile c89atomic_uint32 result; + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) + { + volatile c89atomic_uint64 result; + #if defined(C89ATOMIC_X86) + volatile c89atomic_uint32 resultEAX; + volatile c89atomic_uint32 resultEDX; + __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); + result = ((c89atomic_uint64)resultEDX << 32) | resultEAX; + #elif defined(C89ATOMIC_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + volatile c89atomic_uint8 result = 0; + (void)order; + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + volatile c89atomic_uint16 result = 0; + (void)order; + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + volatile c89atomic_uint32 result; + (void)order; + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + volatile c89atomic_uint64 result; + (void)order; + #if defined(C89ATOMIC_X86) + do { + result = *dst; + } while (c89atomic_compare_and_swap_64(dst, result, src) != result); + #elif defined(C89ATOMIC_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + c89atomic_uint8 result; + (void)order; + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + c89atomic_uint16 result; + (void)order; + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + c89atomic_uint32 result; + (void)order; + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + #if defined(C89ATOMIC_X86) + volatile c89atomic_uint64 oldValue; + volatile c89atomic_uint64 newValue; + (void)order; + do { + oldValue = *dst; + newValue = oldValue + src; + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + return oldValue; + #elif defined(C89ATOMIC_X64) + volatile c89atomic_uint64 result; + (void)order; + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + return result; + #endif + } + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + volatile c89atomic_uint8 oldValue; + volatile c89atomic_uint8 newValue; + do { + oldValue = *dst; + newValue = (c89atomic_uint8)(oldValue - src); + } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + volatile c89atomic_uint16 oldValue; + volatile c89atomic_uint16 newValue; + do { + oldValue = *dst; + newValue = (c89atomic_uint16)(oldValue - src); + } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + volatile c89atomic_uint32 oldValue; + volatile c89atomic_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + volatile c89atomic_uint64 oldValue; + volatile c89atomic_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + volatile c89atomic_uint8 oldValue; + volatile c89atomic_uint8 newValue; + do { + oldValue = *dst; + newValue = (c89atomic_uint8)(oldValue & src); + } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + volatile c89atomic_uint16 oldValue; + volatile c89atomic_uint16 newValue; + do { + oldValue = *dst; + newValue = (c89atomic_uint16)(oldValue & src); + } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + volatile c89atomic_uint32 oldValue; + volatile c89atomic_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + volatile c89atomic_uint64 oldValue; + volatile c89atomic_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + volatile c89atomic_uint8 oldValue; + volatile c89atomic_uint8 newValue; + do { + oldValue = *dst; + newValue = (c89atomic_uint8)(oldValue ^ src); + } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + volatile c89atomic_uint16 oldValue; + volatile c89atomic_uint16 newValue; + do { + oldValue = *dst; + newValue = (c89atomic_uint16)(oldValue ^ src); + } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + volatile c89atomic_uint32 oldValue; + volatile c89atomic_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + volatile c89atomic_uint64 oldValue; + volatile c89atomic_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + { + volatile c89atomic_uint8 oldValue; + volatile c89atomic_uint8 newValue; + do { + oldValue = *dst; + newValue = (c89atomic_uint8)(oldValue | src); + } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + { + volatile c89atomic_uint16 oldValue; + volatile c89atomic_uint16 newValue; + do { + oldValue = *dst; + newValue = (c89atomic_uint16)(oldValue | src); + } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + { + volatile c89atomic_uint32 oldValue; + volatile c89atomic_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + { + volatile c89atomic_uint64 oldValue; + volatile c89atomic_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile c89atomic_uint8* ptr, c89atomic_memory_order order) { - volatile c89atomic_uint16 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; - return oldValue; + return c89atomic_compare_and_swap_8(ptr, 0, 0); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile c89atomic_uint16* ptr, c89atomic_memory_order order) { - volatile c89atomic_uint32 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; - return oldValue; + return c89atomic_compare_and_swap_16(ptr, 0, 0); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile c89atomic_uint32* ptr, c89atomic_memory_order order) { - volatile c89atomic_uint64 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; - return oldValue; + return c89atomic_compare_and_swap_32(ptr, 0, 0); + } + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile c89atomic_uint64* ptr, c89atomic_memory_order order) + { + (void)order; + return c89atomic_compare_and_swap_64(ptr, 0, 0); } - #define c89atomic_fetch_add_explicit_8( dst, src, order) __sync_fetch_and_add(dst, src) - #define c89atomic_fetch_add_explicit_16(dst, src, order) __sync_fetch_and_add(dst, src) - #define c89atomic_fetch_add_explicit_32(dst, src, order) __sync_fetch_and_add(dst, src) - #define c89atomic_fetch_add_explicit_64(dst, src, order) __sync_fetch_and_add(dst, src) - #define c89atomic_fetch_sub_explicit_8( dst, src, order) __sync_fetch_and_sub(dst, src) - #define c89atomic_fetch_sub_explicit_16(dst, src, order) __sync_fetch_and_sub(dst, src) - #define c89atomic_fetch_sub_explicit_32(dst, src, order) __sync_fetch_and_sub(dst, src) - #define c89atomic_fetch_sub_explicit_64(dst, src, order) __sync_fetch_and_sub(dst, src) - #define c89atomic_fetch_or_explicit_8( dst, src, order) __sync_fetch_and_or(dst, src) - #define c89atomic_fetch_or_explicit_16(dst, src, order) __sync_fetch_and_or(dst, src) - #define c89atomic_fetch_or_explicit_32(dst, src, order) __sync_fetch_and_or(dst, src) - #define c89atomic_fetch_or_explicit_64(dst, src, order) __sync_fetch_and_or(dst, src) - #define c89atomic_fetch_xor_explicit_8( dst, src, order) __sync_fetch_and_xor(dst, src) - #define c89atomic_fetch_xor_explicit_16(dst, src, order) __sync_fetch_and_xor(dst, src) - #define c89atomic_fetch_xor_explicit_32(dst, src, order) __sync_fetch_and_xor(dst, src) - #define c89atomic_fetch_xor_explicit_64(dst, src, order) __sync_fetch_and_xor(dst, src) - #define c89atomic_fetch_and_explicit_8( dst, src, order) __sync_fetch_and_and(dst, src) - #define c89atomic_fetch_and_explicit_16(dst, src, order) __sync_fetch_and_and(dst, src) - #define c89atomic_fetch_and_explicit_32(dst, src, order) __sync_fetch_and_and(dst, src) - #define c89atomic_fetch_and_explicit_64(dst, src, order) __sync_fetch_and_and(dst, src) - #define c89atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_load_explicit_8( ptr, order) c89atomic_compare_and_swap_8 (ptr, 0, 0) - #define c89atomic_load_explicit_16(ptr, order) c89atomic_compare_and_swap_16(ptr, 0, 0) - #define c89atomic_load_explicit_32(ptr, order) c89atomic_compare_and_swap_32(ptr, 0, 0) - #define c89atomic_load_explicit_64(ptr, order) c89atomic_compare_and_swap_64(ptr, 0, 0) #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order) #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order) #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order) @@ -8666,7 +9204,7 @@ typedef unsigned char c89atomic_flag; #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order) #endif #if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) -c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, volatile c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint8 expectedValue; c89atomic_uint8 result; @@ -8681,7 +9219,7 @@ c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_u return 0; } } -c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, volatile c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint16 expectedValue; c89atomic_uint16 result; @@ -8696,7 +9234,7 @@ c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_ return 0; } } -c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, volatile c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint32 expectedValue; c89atomic_uint32 result; @@ -8711,7 +9249,7 @@ c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_ return 0; } } -c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint64 expectedValue; c89atomic_uint64 result; @@ -8732,86 +9270,144 @@ c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_ #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) #endif #if !defined(C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE) - #define c89atomic_is_lock_free_8( ptr) 1 - #define c89atomic_is_lock_free_16(ptr) 1 - #define c89atomic_is_lock_free_32(ptr) 1 + static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_8(volatile void* ptr) + { + (void)ptr; + return 1; + } + static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_16(volatile void* ptr) + { + (void)ptr; + return 1; + } + static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_32(volatile void* ptr) + { + (void)ptr; + return 1; + } + static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_64(volatile void* ptr) + { + (void)ptr; #if defined(C89ATOMIC_64BIT) - #define c89atomic_is_lock_free_64(ptr) 1 + return 1; #else #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) - #define c89atomic_is_lock_free_64(ptr) 1 + return 1; #else - #define c89atomic_is_lock_free_64(ptr) 0 + return 0; #endif #endif + } #endif #if defined(C89ATOMIC_64BIT) - #define c89atomic_is_lock_free_ptr(ptr) c89atomic_is_lock_free_64((volatile c89atomic_uint64*)ptr) - #define c89atomic_load_explicit_ptr(ptr, order) (void*)c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order) - #define c89atomic_store_explicit_ptr(dst, src, order) (void*)c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order) - #define c89atomic_exchange_explicit_ptr(dst, src, order) (void*)c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order) - #define c89atomic_compare_exchange_strong_explicit_ptr(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (volatile c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_ptr(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (volatile c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) - #define c89atomic_compare_and_swap_ptr(dst, expected, desired) (void*)c89atomic_compare_and_swap_64 ((volatile c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)desired) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr) + { + return c89atomic_is_lock_free_64((volatile c89atomic_uint64*)ptr); + } + static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order) + { + return (void*)c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order); + } + static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) + { + c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); + } + static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) + { + return (void*)c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); + } + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + { + return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); + } + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, volatile void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + { + return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); + } + static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + { + return (void*)c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)desired); + } #elif defined(C89ATOMIC_32BIT) - #define c89atomic_is_lock_free_ptr(ptr) c89atomic_is_lock_free_32((volatile c89atomic_uint32*)ptr) - #define c89atomic_load_explicit_ptr(ptr, order) (void*)c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order) - #define c89atomic_store_explicit_ptr(dst, src, order) (void*)c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order) - #define c89atomic_exchange_explicit_ptr(dst, src, order) (void*)c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order) - #define c89atomic_compare_exchange_strong_explicit_ptr(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (volatile c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_ptr(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (volatile c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) - #define c89atomic_compare_and_swap_ptr(dst, expected, desired) (void*)c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)desired) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr) + { + return c89atomic_is_lock_free_32((volatile c89atomic_uint32*)ptr); + } + static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order) + { + return (void*)c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order); + } + static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) + { + c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order); + } + static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) + { + return (void*)c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order); + } + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + { + return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); + } + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, volatile void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + { + return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); + } + static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + { + return (void*)c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)desired); + } #else - error "Unsupported architecture." + #error Unsupported architecture. #endif #define c89atomic_flag_test_and_set(ptr) c89atomic_flag_test_and_set_explicit(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_flag_clear(ptr) c89atomic_flag_clear_explicit(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_8( ptr) c89atomic_test_and_set_explicit_8 (ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_store_ptr(dst, src) c89atomic_store_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) +#define c89atomic_load_ptr(ptr) c89atomic_load_explicit_ptr((volatile void**)ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_ptr(dst, src) c89atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void*)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void*)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_8( ptr) c89atomic_test_and_set_explicit_8( ptr, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_16(ptr) c89atomic_test_and_set_explicit_16(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_32(ptr) c89atomic_test_and_set_explicit_32(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_64(ptr) c89atomic_test_and_set_explicit_64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_8( ptr) c89atomic_clear_explicit_8 (ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_8( ptr) c89atomic_clear_explicit_8( ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_16(ptr) c89atomic_clear_explicit_16(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_32(ptr) c89atomic_clear_explicit_32(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_64(ptr) c89atomic_clear_explicit_64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_8( dst, src) c89atomic_store_explicit_8 ( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_16( dst, src) c89atomic_store_explicit_16( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_32( dst, src) c89atomic_store_explicit_32( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_64( dst, src) c89atomic_store_explicit_64( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_ptr(dst, src) c89atomic_store_explicit_ptr(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_8( ptr) c89atomic_load_explicit_8 ( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_16( ptr) c89atomic_load_explicit_16( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_32( ptr) c89atomic_load_explicit_32( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_64( ptr) c89atomic_load_explicit_64( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_ptr(ptr) c89atomic_load_explicit_ptr(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_8( dst, src) c89atomic_exchange_explicit_8 ( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_16( dst, src) c89atomic_exchange_explicit_16( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_32( dst, src) c89atomic_exchange_explicit_32( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_64( dst, src) c89atomic_exchange_explicit_64( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_ptr(dst, src) c89atomic_exchange_explicit_ptr(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_8 ( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_16( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_16( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_32( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_32( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_64( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_64( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_8 ( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_16( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_16( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_32( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_32( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_64( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_64( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_8( dst, src) c89atomic_fetch_add_explicit_8 (dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_8( dst, src) c89atomic_store_explicit_8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_16(dst, src) c89atomic_store_explicit_16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_32(dst, src) c89atomic_store_explicit_32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_64(dst, src) c89atomic_store_explicit_64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_load_8( ptr) c89atomic_load_explicit_8( ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_16(ptr) c89atomic_load_explicit_16(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_32(ptr) c89atomic_load_explicit_32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_64(ptr) c89atomic_load_explicit_64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_8( dst, src) c89atomic_exchange_explicit_8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_16(dst, src) c89atomic_exchange_explicit_16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_32(dst, src) c89atomic_exchange_explicit_32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_64(dst, src) c89atomic_exchange_explicit_64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_16( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_32( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_64( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_8( dst, src) c89atomic_fetch_add_explicit_8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_add_16(dst, src) c89atomic_fetch_add_explicit_16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_add_32(dst, src) c89atomic_fetch_add_explicit_32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_add_64(dst, src) c89atomic_fetch_add_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_8( dst, src) c89atomic_fetch_sub_explicit_8 (dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_8( dst, src) c89atomic_fetch_sub_explicit_8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_sub_16(dst, src) c89atomic_fetch_sub_explicit_16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_sub_32(dst, src) c89atomic_fetch_sub_explicit_32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_sub_64(dst, src) c89atomic_fetch_sub_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_8( dst, src) c89atomic_fetch_or_explicit_8 (dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_8( dst, src) c89atomic_fetch_or_explicit_8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_or_16(dst, src) c89atomic_fetch_or_explicit_16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_or_32(dst, src) c89atomic_fetch_or_explicit_32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_or_64(dst, src) c89atomic_fetch_or_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_8( dst, src) c89atomic_fetch_xor_explicit_8 (dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_8( dst, src) c89atomic_fetch_xor_explicit_8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_xor_16(dst, src) c89atomic_fetch_xor_explicit_16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_xor_32(dst, src) c89atomic_fetch_xor_explicit_32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_xor_64(dst, src) c89atomic_fetch_xor_explicit_64(dst, src, c89atomic_memory_order_seq_cst) @@ -8819,6 +9415,177 @@ c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_ #define c89atomic_fetch_and_16(dst, src) c89atomic_fetch_and_explicit_16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_and_32(dst, src) c89atomic_fetch_and_explicit_32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_and_64(dst, src) c89atomic_fetch_and_explicit_64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_explicit_i8( ptr, order) c89atomic_test_and_set_explicit_8( (c89atomic_uint8* )ptr, order) +#define c89atomic_test_and_set_explicit_i16(ptr, order) c89atomic_test_and_set_explicit_16((c89atomic_uint16*)ptr, order) +#define c89atomic_test_and_set_explicit_i32(ptr, order) c89atomic_test_and_set_explicit_32((c89atomic_uint32*)ptr, order) +#define c89atomic_test_and_set_explicit_i64(ptr, order) c89atomic_test_and_set_explicit_64((c89atomic_uint64*)ptr, order) +#define c89atomic_clear_explicit_i8( ptr, order) c89atomic_clear_explicit_8( (c89atomic_uint8* )ptr, order) +#define c89atomic_clear_explicit_i16(ptr, order) c89atomic_clear_explicit_16((c89atomic_uint16*)ptr, order) +#define c89atomic_clear_explicit_i32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) +#define c89atomic_clear_explicit_i64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) +#define c89atomic_store_explicit_i8( dst, src, order) c89atomic_store_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_store_explicit_i16(dst, src, order) c89atomic_store_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_store_explicit_i32(dst, src, order) c89atomic_store_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_store_explicit_i64(dst, src, order) c89atomic_store_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_load_explicit_i8( ptr, order) c89atomic_load_explicit_8( (c89atomic_uint8* )ptr, order) +#define c89atomic_load_explicit_i16(ptr, order) c89atomic_load_explicit_16((c89atomic_uint16*)ptr, order) +#define c89atomic_load_explicit_i32(ptr, order) c89atomic_load_explicit_32((c89atomic_uint32*)ptr, order) +#define c89atomic_load_explicit_i64(ptr, order) c89atomic_load_explicit_64((c89atomic_uint64*)ptr, order) +#define c89atomic_exchange_explicit_i8( dst, src, order) c89atomic_exchange_explicit_8 ((c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_exchange_explicit_i16(dst, src, order) c89atomic_exchange_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_exchange_explicit_i32(dst, src, order) c89atomic_exchange_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_exchange_explicit_i64(dst, src, order) c89atomic_exchange_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) +#define c89atomic_fetch_add_explicit_i8( dst, src, order) c89atomic_fetch_add_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_fetch_add_explicit_i16(dst, src, order) c89atomic_fetch_add_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_fetch_add_explicit_i32(dst, src, order) c89atomic_fetch_add_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_fetch_add_explicit_i64(dst, src, order) c89atomic_fetch_add_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_fetch_sub_explicit_i8( dst, src, order) c89atomic_fetch_sub_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_fetch_sub_explicit_i16(dst, src, order) c89atomic_fetch_sub_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_fetch_sub_explicit_i32(dst, src, order) c89atomic_fetch_sub_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_fetch_sub_explicit_i64(dst, src, order) c89atomic_fetch_sub_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_fetch_or_explicit_i8( dst, src, order) c89atomic_fetch_or_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_fetch_or_explicit_i16(dst, src, order) c89atomic_fetch_or_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_fetch_or_explicit_i32(dst, src, order) c89atomic_fetch_or_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_fetch_or_explicit_i64(dst, src, order) c89atomic_fetch_or_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_fetch_xor_explicit_i8( dst, src, order) c89atomic_fetch_xor_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_fetch_xor_explicit_i16(dst, src, order) c89atomic_fetch_xor_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_fetch_xor_explicit_i32(dst, src, order) c89atomic_fetch_xor_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_fetch_xor_explicit_i64(dst, src, order) c89atomic_fetch_xor_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_fetch_and_explicit_i8( dst, src, order) c89atomic_fetch_and_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_fetch_and_explicit_i16(dst, src, order) c89atomic_fetch_and_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_fetch_and_explicit_i32(dst, src, order) c89atomic_fetch_and_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_fetch_and_explicit_i64(dst, src, order) c89atomic_fetch_and_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_test_and_set_i8( ptr) c89atomic_test_and_set_explicit_i8( ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_i16(ptr) c89atomic_test_and_set_explicit_i16(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_i32(ptr) c89atomic_test_and_set_explicit_i32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_i64(ptr) c89atomic_test_and_set_explicit_i64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_i8( ptr) c89atomic_clear_explicit_i8( ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_i16(ptr) c89atomic_clear_explicit_i16(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_i32(ptr) c89atomic_clear_explicit_i32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_i64(ptr) c89atomic_clear_explicit_i64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_store_i8( dst, src) c89atomic_store_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_i16(dst, src) c89atomic_store_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_i32(dst, src) c89atomic_store_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_i64(dst, src) c89atomic_store_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_load_i8( ptr) c89atomic_load_explicit_i8( ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_i16(ptr) c89atomic_load_explicit_i16(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_i32(ptr) c89atomic_load_explicit_i32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_i64(ptr) c89atomic_load_explicit_i64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_i8( dst, src) c89atomic_exchange_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_i16(dst, src) c89atomic_exchange_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_i32(dst, src) c89atomic_exchange_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_i64(dst, src) c89atomic_exchange_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_i8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_i16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_i32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_i64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_i8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_i16(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_i32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_i64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_i8( dst, src) c89atomic_fetch_add_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_i16(dst, src) c89atomic_fetch_add_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_i32(dst, src) c89atomic_fetch_add_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_i64(dst, src) c89atomic_fetch_add_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_i8( dst, src) c89atomic_fetch_sub_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_i16(dst, src) c89atomic_fetch_sub_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_i32(dst, src) c89atomic_fetch_sub_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_i64(dst, src) c89atomic_fetch_sub_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_i8( dst, src) c89atomic_fetch_or_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_i16(dst, src) c89atomic_fetch_or_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_i32(dst, src) c89atomic_fetch_or_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_i64(dst, src) c89atomic_fetch_or_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_i8( dst, src) c89atomic_fetch_xor_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_i16(dst, src) c89atomic_fetch_xor_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_i32(dst, src) c89atomic_fetch_xor_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_i64(dst, src) c89atomic_fetch_xor_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_i8( dst, src) c89atomic_fetch_and_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_i16(dst, src) c89atomic_fetch_and_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_i32(dst, src) c89atomic_fetch_and_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_i64(dst, src) c89atomic_fetch_and_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +typedef union +{ + c89atomic_uint32 i; + float f; +} c89atomic_if32; +typedef union +{ + c89atomic_uint64 i; + double f; +} c89atomic_if64; +#define c89atomic_clear_explicit_f32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) +#define c89atomic_clear_explicit_f64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) +static C89ATOMIC_INLINE void c89atomic_store_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +{ + c89atomic_if32 x; + x.f = src; + c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); +} +static C89ATOMIC_INLINE void c89atomic_store_explicit_f64(volatile float* dst, float src, c89atomic_memory_order order) +{ + c89atomic_if64 x; + x.f = src; + c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); +} +static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile float* ptr, c89atomic_memory_order order) +{ + c89atomic_if32 r; + r.i = c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order); + return r.f; +} +static C89ATOMIC_INLINE double c89atomic_load_explicit_f64(volatile double* ptr, c89atomic_memory_order order) +{ + c89atomic_if64 r; + r.i = c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order); + return r.f; +} +static C89ATOMIC_INLINE float c89atomic_exchange_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +{ + c89atomic_if32 r; + c89atomic_if32 x; + x.f = src; + r.i = c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + return r.f; +} +static C89ATOMIC_INLINE double c89atomic_exchange_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +{ + c89atomic_if64 r; + c89atomic_if64 x; + x.f = src; + r.i = c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + return r.f; +} +#define c89atomic_clear_f32(ptr) c89atomic_clear_explicit_f32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_f64(ptr) c89atomic_clear_explicit_f64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_store_f32(dst, src) c89atomic_store_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_f64(dst, src) c89atomic_store_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_load_f32(ptr) c89atomic_load_explicit_f32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_f64(ptr) c89atomic_load_explicit_f64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_f32(dst, src) c89atomic_exchange_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_f64(dst, src) c89atomic_exchange_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +typedef c89atomic_flag c89atomic_spinlock; +static C89ATOMIC_INLINE void c89atomic_spinlock_lock(volatile c89atomic_spinlock* pSpinlock) +{ + for (;;) { + if (c89atomic_flag_test_and_set_explicit(pSpinlock, c89atomic_memory_order_acquire) == 0) { + break; + } + while (c89atomic_load_explicit_8(pSpinlock, c89atomic_memory_order_relaxed) == 1) { + } + } +} +static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlock* pSpinlock) +{ + c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release); +} #if defined(__cplusplus) } #endif @@ -9019,7 +9786,7 @@ Threading #endif typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); -static MA_INLINE ma_result ma_spinlock_lock_ex(ma_spinlock* pSpinlock, ma_bool32 yield) +static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield) { if (pSpinlock == NULL) { return MA_INVALID_ARGS; @@ -9040,17 +9807,17 @@ static MA_INLINE ma_result ma_spinlock_lock_ex(ma_spinlock* pSpinlock, ma_bool32 return MA_SUCCESS; } -MA_API ma_result ma_spinlock_lock(ma_spinlock* pSpinlock) +MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock) { return ma_spinlock_lock_ex(pSpinlock, MA_TRUE); } -MA_API ma_result ma_spinlock_lock_noyield(ma_spinlock* pSpinlock) +MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock) { return ma_spinlock_lock_ex(pSpinlock, MA_FALSE); } -MA_API ma_result ma_spinlock_unlock(ma_spinlock* pSpinlock) +MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) { if (pSpinlock == NULL) { return MA_INVALID_ARGS; @@ -9259,8 +10026,6 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority } } } - - pthread_attr_destroy(&attr); } #else /* It's the emscripten build. We'll have a few unused parameters. */ @@ -9269,6 +10034,12 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority #endif result = pthread_create(pThread, pAttr, entryProc, pData); + + /* The thread attributes object is no longer required. */ + if (pAttr != NULL) { + pthread_attr_destroy(pAttr); + } + if (result != 0) { return ma_result_from_errno(result); } @@ -9724,7 +10495,7 @@ not officially supporting this, but I'm leaving it here in case it's useful for /* Disable run-time linking on certain backends. */ #ifndef MA_NO_RUNTIME_LINKING - #if defined(MA_ANDROID) || defined(MA_EMSCRIPTEN) + #if defined(MA_EMSCRIPTEN) #define MA_NO_RUNTIME_LINKING #endif #endif @@ -10227,7 +10998,7 @@ int ma_vscprintf(const char* format, va_list args) return -1; } - for (;;) { + for (;;) { char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, NULL); /* TODO: Add support for custom memory allocators? */ if (pNewTempBuffer == NULL) { ma_free(pTempBuffer, NULL); @@ -10246,7 +11017,7 @@ int ma_vscprintf(const char* format, va_list args) /* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */ tempBufferCap *= 2; - } + } return result; #endif @@ -10256,7 +11027,7 @@ int ma_vscprintf(const char* format, va_list args) /* Posts a formatted log message. */ static void ma_post_log_messagev(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* pFormat, va_list args) { -#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) { char pFormattedMessage[1024]; vsnprintf(pFormattedMessage, sizeof(pFormattedMessage), pFormat, args); @@ -10350,7 +11121,7 @@ Timing *******************************************************************************/ #ifdef MA_WIN32 - static LARGE_INTEGER g_ma_TimerFrequency = {{0}}; + static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ static void ma_timer_init(ma_timer* pTimer) { LARGE_INTEGER counter; @@ -10589,7 +11360,7 @@ static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* { float masterVolumeFactor; - masterVolumeFactor = pDevice->masterVolumeFactor; + ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */ if (pDevice->onData) { if (!pDevice->noPreZeroedOutputBuffer && pFramesOut != NULL) { @@ -10959,15 +11730,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice, } } - /* The device needs to be started immediately. */ - if (pCallbacks->onDeviceStart != NULL) { - result = pCallbacks->onDeviceStart(pDevice); - if (result != MA_SUCCESS) { - return result; - } - } else { - /* Getting here means no start callback is defined. This is OK, as the backend may auto-start the device when reading or writing data. */ - } + /* NOTE: The device was started outside of this function, in the worker thread. */ while (ma_device_get_state(pDevice) == MA_STATE_STARTED && !exitLoop) { switch (pDevice->type) { @@ -11111,11 +11874,6 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice, } } - /* We've exited the loop so we'll need to stop the device. */ - if (pCallbacks->onDeviceStop != NULL) { - pCallbacks->onDeviceStop(pDevice); - } - return result; } @@ -11139,33 +11897,28 @@ static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) MA_ASSERT(pDevice != NULL); for (;;) { /* Keep the thread alive until the device is uninitialized. */ + ma_uint32 operation; + /* Wait for an operation to be requested. */ ma_event_wait(&pDevice->null_device.operationEvent); /* At this point an event should have been triggered. */ + operation = c89atomic_load_32(&pDevice->null_device.operation); /* Starting the device needs to put the thread into a loop. */ - if (pDevice->null_device.operation == MA_DEVICE_OP_START__NULL) { - c89atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL); - + if (operation == MA_DEVICE_OP_START__NULL) { /* Reset the timer just in case. */ ma_timer_init(&pDevice->null_device.timer); - /* Keep looping until an operation has been requested. */ - while (pDevice->null_device.operation != MA_DEVICE_OP_NONE__NULL && pDevice->null_device.operation != MA_DEVICE_OP_START__NULL) { - ma_sleep(10); /* Don't hog the CPU. */ - } - /* Getting here means a suspend or kill operation has been requested. */ c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS); ma_event_signal(&pDevice->null_device.operationCompletionEvent); + ma_semaphore_release(&pDevice->null_device.operationSemaphore); continue; } /* Suspending the device means we need to stop the timer and just continue the loop. */ - if (pDevice->null_device.operation == MA_DEVICE_OP_SUSPEND__NULL) { - c89atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL); - + if (operation == MA_DEVICE_OP_SUSPEND__NULL) { /* We need to add the current run time to the prior run time, then reset the timer. */ pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer); ma_timer_init(&pDevice->null_device.timer); @@ -11173,22 +11926,24 @@ static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) /* We're done. */ c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS); ma_event_signal(&pDevice->null_device.operationCompletionEvent); + ma_semaphore_release(&pDevice->null_device.operationSemaphore); continue; } /* Killing the device means we need to get out of this loop so that this thread can terminate. */ - if (pDevice->null_device.operation == MA_DEVICE_OP_KILL__NULL) { - c89atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL); + if (operation == MA_DEVICE_OP_KILL__NULL) { c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS); ma_event_signal(&pDevice->null_device.operationCompletionEvent); + ma_semaphore_release(&pDevice->null_device.operationSemaphore); break; } /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */ - if (pDevice->null_device.operation == MA_DEVICE_OP_NONE__NULL) { + if (operation == MA_DEVICE_OP_NONE__NULL) { MA_ASSERT(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */ c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, (c89atomic_uint32)MA_INVALID_OPERATION); ma_event_signal(&pDevice->null_device.operationCompletionEvent); + ma_semaphore_release(&pDevice->null_device.operationSemaphore); continue; /* Continue the loop. Don't terminate. */ } } @@ -11198,16 +11953,34 @@ static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation) { + ma_result result; + + /* + The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later + to support queing of operations. + */ + result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); + if (result != MA_SUCCESS) { + return result; /* Failed to wait for the event. */ + } + + /* + When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to + signal an event to the worker thread to let it know that it can start work. + */ c89atomic_exchange_32(&pDevice->null_device.operation, operation); + + /* Once the operation code has been set, the worker thread can start work. */ if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) { return MA_ERROR; } + /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */ if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) { return MA_ERROR; } - return pDevice->null_device.operationResult; + return c89atomic_load_i32(&pDevice->null_device.operationResult); } static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice) @@ -11219,7 +11992,6 @@ static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice internalSampleRate = pDevice->playback.internalSampleRate; } - return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate); } @@ -11246,13 +12018,13 @@ static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enu cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } + (void)cbResult; /* Silence a static analysis warning. */ + return MA_SUCCESS; } -static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo) +static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { - ma_uint32 iFormat; - MA_ASSERT(pContext != NULL); if (pDeviceID != NULL && pDeviceID->nullbackend != 0) { @@ -11267,38 +12039,55 @@ static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_devic } /* Support everything on the null backend. */ - pDeviceInfo->formatCount = ma_format_count - 1; /* Minus one because we don't want to include ma_format_unknown. */ - for (iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) { - pDeviceInfo->formats[iFormat] = (ma_format)(iFormat + 1); /* +1 to skip over ma_format_unknown. */ - } - - pDeviceInfo->minChannels = 1; - pDeviceInfo->maxChannels = MA_MAX_CHANNELS; - pDeviceInfo->minSampleRate = MA_SAMPLE_RATE_8000; - pDeviceInfo->maxSampleRate = MA_SAMPLE_RATE_384000; + pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; + pDeviceInfo->nativeDataFormats[0].channels = 0; + pDeviceInfo->nativeDataFormats[0].sampleRate = 0; + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormatCount = 1; (void)pContext; - (void)shareMode; return MA_SUCCESS; } -static void ma_device_uninit__null(ma_device* pDevice) +static ma_result ma_device_uninit__null(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); /* Keep it clean and wait for the device thread to finish before returning. */ ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL); + /* Wait for the thread to finish before continuing. */ + ma_thread_wait(&pDevice->null_device.deviceThread); + /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */ + ma_semaphore_uninit(&pDevice->null_device.operationSemaphore); ma_event_uninit(&pDevice->null_device.operationCompletionEvent); ma_event_uninit(&pDevice->null_device.operationEvent); + + return MA_SUCCESS; } -static ma_result ma_device_init__null(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) +static ma_uint32 ma_calculate_buffer_size_in_frames__null(const ma_device_config* pConfig, const ma_device_descriptor* pDescriptor) +{ + if (pDescriptor->periodSizeInFrames == 0) { + if (pDescriptor->periodSizeInMilliseconds == 0) { + if (pConfig->performanceProfile == ma_performance_profile_low_latency) { + return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pDescriptor->sampleRate); + } else { + return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pDescriptor->sampleRate); + } + } else { + return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, pDescriptor->sampleRate); + } + } else { + return pDescriptor->periodSizeInFrames; + } +} + +static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { ma_result result; - ma_uint32 periodSizeInFrames; MA_ASSERT(pDevice != NULL); @@ -11308,26 +12097,29 @@ static ma_result ma_device_init__null(ma_context* pContext, const ma_device_conf return MA_DEVICE_TYPE_NOT_SUPPORTED; } - periodSizeInFrames = pConfig->periodSizeInFrames; - if (periodSizeInFrames == 0) { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pConfig->sampleRate); + /* The null backend supports everything exactly as we specify it. */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT; + pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; + pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE; + + if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) { + ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptorCapture->channels, pDescriptorCapture->channelMap); + } + + pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames__null(pConfig, pDescriptorCapture); } - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "NULL Capture Device", (size_t)-1); - pDevice->capture.internalFormat = pConfig->capture.format; - pDevice->capture.internalChannels = pConfig->capture.channels; - ma_channel_map_copy(pDevice->capture.internalChannelMap, pConfig->capture.channelMap, ma_min(pConfig->capture.channels, MA_MAX_CHANNELS)); - pDevice->capture.internalPeriodSizeInFrames = periodSizeInFrames; - pDevice->capture.internalPeriods = pConfig->periods; - } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "NULL Playback Device", (size_t)-1); - pDevice->playback.internalFormat = pConfig->playback.format; - pDevice->playback.internalChannels = pConfig->playback.channels; - ma_channel_map_copy(pDevice->playback.internalChannelMap, pConfig->playback.channelMap, ma_min(pConfig->playback.channels, MA_MAX_CHANNELS)); - pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames; - pDevice->playback.internalPeriods = pConfig->periods; + pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT; + pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; + pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE; + + if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) { + ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); + } + + pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames__null(pConfig, pDescriptorPlayback); } /* @@ -11344,7 +12136,12 @@ static ma_result ma_device_init__null(ma_context* pContext, const ma_device_conf return result; } - result = ma_thread_create(&pDevice->thread, pContext->threadPriority, 0, ma_device_thread__null, pDevice); + result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */ + if (result != MA_SUCCESS) { + return result; + } + + result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice); if (result != MA_SUCCESS) { return result; } @@ -11382,7 +12179,7 @@ static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrame *pFramesWritten = 0; } - wasStartedOnEntry = pDevice->null_device.isStarted; + wasStartedOnEntry = c89atomic_load_32(&pDevice->null_device.isStarted); /* Keep going until everything has been read. */ totalPCMFramesProcessed = 0; @@ -11408,7 +12205,7 @@ static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrame if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) { pDevice->null_device.currentPeriodFramesRemainingPlayback = 0; - if (!pDevice->null_device.isStarted && !wasStartedOnEntry) { + if (!c89atomic_load_32(&pDevice->null_device.isStarted) && !wasStartedOnEntry) { result = ma_device_start__null(pDevice); if (result != MA_SUCCESS) { break; @@ -11428,7 +12225,7 @@ static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrame ma_uint64 currentFrame; /* Stop waiting if the device has been stopped. */ - if (!pDevice->null_device.isStarted) { + if (!c89atomic_load_32(&pDevice->null_device.isStarted)) { break; } @@ -11499,7 +12296,7 @@ static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_u ma_uint64 currentFrame; /* Stop waiting if the device has been stopped. */ - if (!pDevice->null_device.isStarted) { + if (!c89atomic_load_32(&pDevice->null_device.isStarted)) { break; } @@ -11523,16 +12320,6 @@ static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_u return result; } -static ma_result ma_device_main_loop__null(ma_device* pDevice) -{ - ma_backend_callbacks callbacks; - callbacks.onDeviceStart = ma_device_start__null; - callbacks.onDeviceStop = ma_device_stop__null; - callbacks.onDeviceRead = ma_device_read__null; - callbacks.onDeviceWrite = ma_device_write__null; - return ma_device_audio_thread__default_read_write(pDevice, &callbacks); -} - static ma_result ma_context_uninit__null(ma_context* pContext) { MA_ASSERT(pContext != NULL); @@ -11542,20 +12329,24 @@ static ma_result ma_context_uninit__null(ma_context* pContext) return MA_SUCCESS; } -static ma_result ma_context_init__null(const ma_context_config* pConfig, ma_context* pContext) +static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { MA_ASSERT(pContext != NULL); (void)pConfig; + (void)pContext; - pContext->onUninit = ma_context_uninit__null; - pContext->onEnumDevices = ma_context_enumerate_devices__null; - pContext->onGetDeviceInfo = ma_context_get_device_info__null; - pContext->onDeviceInit = ma_device_init__null; - pContext->onDeviceUninit = ma_device_uninit__null; - pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */ - pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */ - pContext->onDeviceMainLoop = ma_device_main_loop__null; + pCallbacks->onContextInit = ma_context_init__null; + pCallbacks->onContextUninit = ma_context_uninit__null; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null; + pCallbacks->onDeviceInit = ma_device_init__null; + pCallbacks->onDeviceUninit = ma_device_uninit__null; + pCallbacks->onDeviceStart = ma_device_start__null; + pCallbacks->onDeviceStop = ma_device_stop__null; + pCallbacks->onDeviceRead = ma_device_read__null; + pCallbacks->onDeviceWrite = ma_device_write__null; + pCallbacks->onDeviceAudioThread = NULL; /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */ /* The null backend always works. */ return MA_SUCCESS; @@ -11584,7 +12375,7 @@ WIN32 COMMON #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar) #endif -#if !defined(MAXULONG_PTR) +#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) typedef size_t DWORD_PTR; #endif @@ -11872,12 +12663,14 @@ typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMas #ifndef PROPERTYKEY_DEFINED #define PROPERTYKEY_DEFINED +#ifndef __WATCOMC__ typedef struct { GUID fmtid; DWORD pid; } PROPERTYKEY; #endif +#endif /* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */ static MA_INLINE void ma_PropVariantInit(PROPVARIANT* pProp) @@ -11890,7 +12683,9 @@ static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */ +#ifndef MA_WIN32_DESKTOP static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */ +#endif static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */ static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */ @@ -11980,7 +12775,7 @@ typedef enum typedef struct { - UINT32 cbSize; + ma_uint32 cbSize; BOOL bIsOffload; MA_AUDIO_STREAM_CATEGORY eCategory; } ma_AudioClientProperties; @@ -12267,9 +13062,9 @@ typedef struct HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); /* IAudioClient3 */ - HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, UINT32* pDefaultPeriodInFrames, UINT32* pFundamentalPeriodInFrames, UINT32* pMinPeriodInFrames, UINT32* pMaxPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, UINT32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); } ma_IAudioClient3Vtbl; struct ma_IAudioClient3 { @@ -12293,9 +13088,9 @@ static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, co static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } -static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, UINT32* pDefaultPeriodInFrames, UINT32* pFundamentalPeriodInFrames, UINT32* pMinPeriodInFrames, UINT32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, UINT32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } +static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } /* IAudioRenderClient */ @@ -12362,7 +13157,7 @@ typedef struct struct ma_completion_handler_uwp { ma_completion_handler_uwp_vtbl* lpVtbl; - ma_uint32 counter; + volatile ma_uint32 counter; HANDLE hEvent; }; @@ -12576,7 +13371,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged c89atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultPlaybackDeviceChanged, MA_TRUE); } if (dataFlow == ma_eCapture || pThis->pDevice->type == ma_device_type_loopback) { - c89atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultCaptureDeviceChanged, MA_TRUE); + c89atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultCaptureDeviceChanged, MA_TRUE); } (void)pDefaultDeviceID; @@ -12669,10 +13464,8 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context first. If this fails, fall back to a search. */ hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL); - ma_PropVariantClear(pContext, &var); - if (SUCCEEDED(hr)) { - /* The format returned by PKEY_AudioEngine_DeviceFormat is not supported. */ + /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */ ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo); } else { /* @@ -12737,6 +13530,8 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context } } + ma_PropVariantClear(pContext, &var); + if (!found) { ma_IPropertyStore_Release(pProperties); return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to find suitable device format for device info retrieval.", MA_FORMAT_NOT_SUPPORTED); @@ -12800,6 +13595,8 @@ static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi( MA_ASSERT(pContext != NULL); MA_ASSERT(pDeviceEnumerator != NULL); + (void)pContext; + /* Grab the EDataFlow type from the device type. */ dataFlow = ma_device_type_to_EDataFlow(deviceType); @@ -13259,6 +14056,7 @@ typedef struct ma_bool32 usingDefaultSampleRate; ma_bool32 usingDefaultChannelMap;*/ ma_share_mode shareMode; + ma_performance_profile performanceProfile; ma_bool32 noAutoConvertSRC; ma_bool32 noDefaultQualitySRC; ma_bool32 noHardwareOffloading; @@ -13419,7 +14217,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device in this case so that the caller can detect it and fall back to shared mode if desired. We should never get here if shared mode was requested, but just for completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED. */ - if (shareMode == ma_share_mode_exclusive) { + if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { result = MA_SHARE_MODE_NOT_SUPPORTED; } else { result = MA_FORMAT_NOT_SUPPORTED; @@ -13439,7 +14237,15 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS; pData->periodSizeInFramesOut = pData->periodSizeInFramesIn; if (pData->periodSizeInFramesOut == 0) { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.Format.nSamplesPerSec); + if (pData->periodSizeInMillisecondsIn == 0) { + if (pData->performanceProfile == ma_performance_profile_low_latency) { + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.Format.nSamplesPerSec); + } else { + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.Format.nSamplesPerSec); + } + } else { + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.Format.nSamplesPerSec); + } } periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.Format.nSamplesPerSec; @@ -13522,14 +14328,14 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device ma_IAudioClient3* pAudioClient3 = NULL; hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3); if (SUCCEEDED(hr)) { - UINT32 defaultPeriodInFrames; - UINT32 fundamentalPeriodInFrames; - UINT32 minPeriodInFrames; - UINT32 maxPeriodInFrames; + ma_uint32 defaultPeriodInFrames; + ma_uint32 fundamentalPeriodInFrames; + ma_uint32 minPeriodInFrames; + ma_uint32 maxPeriodInFrames; hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); if (SUCCEEDED(hr)) { - UINT32 desiredPeriodInFrames = pData->periodSizeInFramesOut; - UINT32 actualPeriodInFrames = desiredPeriodInFrames; + ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut; + ma_uint32 actualPeriodInFrames = desiredPeriodInFrames; /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */ actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames; @@ -13712,6 +14518,7 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames; data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds; data.periodsIn = pDevice->wasapi.originalPeriods; + data.performanceProfile = pDevice->wasapi.originalPerformanceProfile; data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; @@ -13749,7 +14556,7 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualPeriodSizeInFramesCapture); /* The device may be in a started state. If so we need to immediately restart it. */ - if (pDevice->wasapi.isStartedCapture) { + if (c89atomic_load_32(&pDevice->wasapi.isStartedCapture)) { HRESULT hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device after reinitialization.", ma_result_from_HRESULT(hr)); @@ -13785,7 +14592,7 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualPeriodSizeInFramesPlayback); /* The device may be in a started state. If so we need to immediately restart it. */ - if (pDevice->wasapi.isStartedPlayback) { + if (c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) { HRESULT hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); if (FAILED(hr)) { return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device after reinitialization.", ma_result_from_HRESULT(hr)); @@ -13827,6 +14634,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; data.periodsIn = pDescriptorCapture->periodCount; data.shareMode = pDescriptorCapture->shareMode; + data.performanceProfile = pConfig->performanceProfile; data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; @@ -13841,6 +14649,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount; + pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; /* The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, @@ -13885,6 +14694,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; data.periodsIn = pDescriptorPlayback->periodCount; data.shareMode = pDescriptorPlayback->shareMode; + data.performanceProfile = pConfig->performanceProfile; data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; @@ -13912,6 +14722,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount; + pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; /* The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled @@ -14044,11 +14855,11 @@ static ma_bool32 ma_device_is_reroute_required__wasapi(ma_device* pDevice, ma_de MA_ASSERT(pDevice != NULL); if (deviceType == ma_device_type_playback) { - return pDevice->wasapi.hasDefaultPlaybackDeviceChanged; + return c89atomic_load_32(&pDevice->wasapi.hasDefaultPlaybackDeviceChanged); } if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - return pDevice->wasapi.hasDefaultCaptureDeviceChanged; + return c89atomic_load_32(&pDevice->wasapi.hasDefaultCaptureDeviceChanged); } return MA_FALSE; @@ -14453,7 +15264,7 @@ static ma_result ma_device_audio_thread__wasapi(ma_device* pDevice) mappedDeviceBufferSizeInFramesPlayback = 0; } - if (!pDevice->wasapi.isStartedPlayback) { + if (!c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) { ma_uint32 startThreshold = pDevice->playback.internalPeriodSizeInFrames * 1; /* Prevent a deadlock. If we don't clamp against the actual buffer size we'll never end up starting the playback device which will result in a deadlock. */ @@ -14619,7 +15430,7 @@ static ma_result ma_device_audio_thread__wasapi(ma_device* pDevice) } framesWrittenToPlaybackDevice += framesAvailablePlayback; - if (!pDevice->wasapi.isStartedPlayback) { + if (!c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) { if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames*1) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); if (FAILED(hr)) { @@ -14667,7 +15478,7 @@ static ma_result ma_device_audio_thread__wasapi(ma_device* pDevice) The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. */ - if (pDevice->wasapi.isStartedPlayback) { + if (c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) { /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ DWORD waitTime = pDevice->wasapi.actualPeriodSizeInFramesPlayback / pDevice->playback.internalSampleRate; @@ -14732,7 +15543,7 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ ma_result result = MA_SUCCESS; MA_ASSERT(pContext != NULL); - + (void)pConfig; #ifdef MA_WIN32_DESKTOP @@ -14803,7 +15614,7 @@ DirectSound Backend #ifdef MA_HAS_DSOUND /*#include */ -static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}}; +/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/ /* miniaudio only uses priority or exclusive modes. */ #define MA_DSSCL_NORMAL 1 @@ -15370,6 +16181,8 @@ static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; ma_device_info deviceInfo; + (void)lpcstrModule; + MA_ZERO_OBJECT(&deviceInfo); /* ID. */ @@ -15392,8 +16205,6 @@ static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid } else { return TRUE; /* Continue enumeration. */ } - - (void)lpcstrModule; } static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) @@ -15520,10 +16331,11 @@ static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_dev /* Channels. Only a single channel count is reported for DirectSound. */ if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { /* It supports at least stereo, but could support more. */ + DWORD speakerConfig; + channels = 2; /* Look at the speaker configuration to get a better idea on the channel count. */ - DWORD speakerConfig; hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig); if (SUCCEEDED(hr)) { ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL); @@ -15595,7 +16407,7 @@ static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_dev pDeviceInfo->formats[0] = ma_format_s24; } else if (bitsPerSample == 32) { pDeviceInfo->formats[0] = ma_format_s32; - } else { + } else { return MA_FORMAT_NOT_SUPPORTED; } @@ -15685,13 +16497,21 @@ static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 c return MA_SUCCESS; } -static ma_uint32 ma_calculate_period_size_in_frames__dsound(ma_uint32 periodSizeInFrames, ma_uint32 periodSizeInMilliseconds, ma_uint32 sampleRate) +static ma_uint32 ma_calculate_period_size_in_frames__dsound(ma_uint32 periodSizeInFrames, ma_uint32 periodSizeInMilliseconds, ma_uint32 sampleRate, ma_performance_profile performanceProfile) { /* DirectSound has a minimum period size of 20ms. */ ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(20, sampleRate); if (periodSizeInFrames == 0) { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, sampleRate); + if (periodSizeInMilliseconds == 0) { + if (performanceProfile == ma_performance_profile_low_latency) { + periodSizeInFrames = minPeriodSizeInFrames; + } else { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, sampleRate); + } + } else { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, sampleRate); + } } if (periodSizeInFrames < minPeriodSizeInFrames) { @@ -15750,7 +16570,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames__dsound(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodSizeInMilliseconds, wf.Format.nSamplesPerSec); + periodSizeInFrames = ma_calculate_period_size_in_frames__dsound(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodSizeInMilliseconds, wf.Format.nSamplesPerSec, pConfig->performanceProfile); periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; MA_ZERO_OBJECT(&descDS); @@ -15907,7 +16727,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames__dsound(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); + periodSizeInFrames = ma_calculate_period_size_in_frames__dsound(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS; /* @@ -16992,13 +17812,21 @@ static ma_result ma_device_uninit__winmm(ma_device* pDevice) return MA_SUCCESS; } -static ma_uint32 ma_calculate_period_size_in_frames__winmm(ma_uint32 periodSizeInFrames, ma_uint32 periodSizeInMilliseconds, ma_uint32 sampleRate) +static ma_uint32 ma_calculate_period_size_in_frames__winmm(ma_uint32 periodSizeInFrames, ma_uint32 periodSizeInMilliseconds, ma_uint32 sampleRate, ma_performance_profile performanceProfile) { - /* DirectSound has a minimum period size of 40ms. */ + /* WinMM has a minimum period size of 40ms. */ ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, sampleRate); if (periodSizeInFrames == 0) { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, sampleRate); + if (periodSizeInMilliseconds == 0) { + if (performanceProfile == ma_performance_profile_low_latency) { + periodSizeInFrames = minPeriodSizeInFrames; + } else { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, sampleRate); + } + } else { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, sampleRate); + } } if (periodSizeInFrames < minPeriodSizeInFrames) { @@ -17026,8 +17854,8 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi } /* No exlusive mode with WinMM. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } @@ -17074,7 +17902,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi pDescriptorCapture->sampleRate = wf.nSamplesPerSec; ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDescriptorCapture->channels, pDescriptorCapture->channelMap); pDescriptorCapture->periodCount = pDescriptorCapture->periodCount; - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames__winmm(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate); + pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames__winmm(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate, pConfig->performanceProfile); } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { @@ -17112,7 +17940,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi pDescriptorPlayback->sampleRate = wf.nSamplesPerSec; ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount; - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames__winmm(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); + pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames__winmm(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); } /* @@ -20752,9 +21580,7 @@ static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operat for (;;) { ma_mainloop_lock__pulse(pContext, "ma_wait_for_operation__pulse"); - { - state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP); - } + state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP); ma_mainloop_unlock__pulse(pContext, "ma_wait_for_operation__pulse"); if (state != MA_PA_OPERATION_RUNNING) { @@ -20787,9 +21613,7 @@ static ma_result ma_context_wait_for_pa_context_to_connect__pulse(ma_context* pC for (;;) { ma_mainloop_lock__pulse(pContext, "ma_context_wait_for_pa_context_to_connect__pulse"); - { - state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pContext->pulse.pPulseContext); - } + state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pContext->pulse.pPulseContext); ma_mainloop_unlock__pulse(pContext, "ma_context_wait_for_pa_context_to_connect__pulse"); if (state == MA_PA_CONTEXT_READY) { @@ -20813,9 +21637,7 @@ static ma_result ma_context_wait_for_pa_stream_to_connect__pulse(ma_context* pCo for (;;) { ma_mainloop_lock__pulse(pContext, "ma_context_wait_for_pa_stream_to_connect__pulse"); - { - state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)(pStream); - } + state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)(pStream); ma_mainloop_unlock__pulse(pContext, "ma_context_wait_for_pa_stream_to_connect__pulse"); if (state == MA_PA_STREAM_READY) { @@ -20902,7 +21724,10 @@ static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const cha { ma_pa_operation* pOP; + ma_mainloop_lock__pulse(pContext, "ma_context_get_sink_info__pulse"); pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo); + ma_mainloop_unlock__pulse(pContext, "ma_context_get_sink_info__pulse"); + if (pOP == NULL) { return MA_ERROR; } @@ -20915,7 +21740,10 @@ static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const c { ma_pa_operation* pOP; + ma_mainloop_lock__pulse(pContext, "ma_context_get_source_info__pulse"); pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo); + ma_mainloop_unlock__pulse(pContext, "ma_context_get_source_info__pulse"); + if (pOP == NULL) { return MA_ERROR; } @@ -21059,7 +21887,10 @@ static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_en /* Playback. */ if (!callbackData.isTerminated) { + ma_mainloop_lock__pulse(pContext, "ma_context_enumerate_devices__pulse"); pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData); + ma_mainloop_unlock__pulse(pContext, "ma_context_enumerate_devices__pulse"); + if (pOP == NULL) { result = MA_ERROR; goto done; @@ -21075,7 +21906,10 @@ static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_en /* Capture. */ if (!callbackData.isTerminated) { + ma_mainloop_lock__pulse(pContext, "ma_context_enumerate_devices__pulse"); pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData); + ma_mainloop_unlock__pulse(pContext, "ma_context_enumerate_devices__pulse"); + if (pOP == NULL) { result = MA_ERROR; goto done; @@ -21184,11 +22018,13 @@ static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_devi result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex); + ma_mainloop_lock__pulse(pContext, "ma_context_get_device_info__pulse"); if (deviceType == ma_device_type_playback) { pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceID->pulse, ma_context_get_device_info_sink_callback__pulse, &callbackData); } else { pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceID->pulse, ma_context_get_device_info_source_callback__pulse, &callbackData); } + ma_mainloop_unlock__pulse(pContext, "ma_context_get_device_info__pulse"); if (pOP != NULL) { ma_wait_for_operation_and_unref__pulse(pContext, pOP); @@ -21215,15 +22051,19 @@ static void ma_device_uninit__pulse(ma_device* pDevice) pContext = pDevice->pContext; MA_ASSERT(pContext != NULL); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } + ma_mainloop_lock__pulse(pContext, "ma_device_uninit__pulse"); + { + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + } } + ma_mainloop_unlock__pulse(pContext, "ma_device_uninit__pulse"); if (pDevice->type == ma_device_type_duplex) { ma_pcm_rb_uninit(&pDevice->pulse.duplexRB); @@ -21244,6 +22084,7 @@ static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFra static ma_pa_stream* ma_context__pa_stream_new__pulse(ma_context* pContext, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) { + ma_pa_stream* pStream; static int g_StreamCounter = 0; char actualStreamName[256]; @@ -21255,7 +22096,11 @@ static ma_pa_stream* ma_context__pa_stream_new__pulse(ma_context* pContext, cons } g_StreamCounter += 1; - return ((ma_pa_stream_new_proc)pContext->pulse.pa_stream_new)((ma_pa_context*)pContext->pulse.pPulseContext, actualStreamName, ss, cmap); + ma_mainloop_lock__pulse(pContext, "ma_context__pa_stream_new__pulse"); + pStream = ((ma_pa_stream_new_proc)pContext->pulse.pa_stream_new)((ma_pa_context*)pContext->pulse.pPulseContext, actualStreamName, ss, cmap); + ma_mainloop_unlock__pulse(pContext, "ma_context__pa_stream_new__pulse"); + + return pStream; } @@ -21480,7 +22325,9 @@ static ma_result ma_device_init__pulse(ma_context* pContext, const ma_device_con /* The callback needs to be set before connecting the stream. */ + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); ((ma_pa_stream_set_read_callback_proc)pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice); + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); /* Connect after we've got all of our internal state set up. */ @@ -21489,7 +22336,9 @@ static ma_result ma_device_init__pulse(ma_context* pContext, const ma_device_con streamFlags |= MA_PA_STREAM_DONT_MOVE; } + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); error = ((ma_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); if (error != MA_PA_OK) { result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.", ma_result_from_pulse(error)); goto on_error1; @@ -21501,40 +22350,52 @@ static ma_result ma_device_init__pulse(ma_context* pContext, const ma_device_con } - /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualSS != NULL) { - ss = *pActualSS; - } + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); + { + /* Internal format. */ + pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (pActualSS != NULL) { + ss = *pActualSS; + } - pDevice->capture.internalFormat = ma_format_from_pulse(ss.format); - pDevice->capture.internalChannels = ss.channels; - pDevice->capture.internalSampleRate = ss.rate; + pDevice->capture.internalFormat = ma_format_from_pulse(ss.format); + pDevice->capture.internalChannels = ss.channels; + pDevice->capture.internalSampleRate = ss.rate; - /* Internal channel map. */ - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualCMap != NULL) { - cmap = *pActualCMap; - } - for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - pDevice->capture.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); - } + /* Internal channel map. */ + pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (pActualCMap != NULL) { + cmap = *pActualCMap; + } + for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { + pDevice->capture.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); + } - /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualAttr != NULL) { - attr = *pActualAttr; + /* Buffer. */ + pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (pActualAttr != NULL) { + attr = *pActualAttr; + } + pDevice->capture.internalPeriods = attr.maxlength / attr.fragsize; + pDevice->capture.internalPeriodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels) / pDevice->capture.internalPeriods; + #ifdef MA_DEBUG_OUTPUT + printf("[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->capture.internalPeriodSizeInFrames); + #endif } - pDevice->capture.internalPeriods = attr.maxlength / attr.fragsize; - pDevice->capture.internalPeriodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels) / pDevice->capture.internalPeriods; - #ifdef MA_DEBUG_OUTPUT - printf("[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->capture.internalPeriodSizeInFrames); - #endif + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); /* Name. */ + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); devCapture = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); if (devCapture != NULL) { - ma_wait_for_operation_and_unref__pulse(pContext, ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, devCapture, ma_device_source_name_callback, pDevice)); + ma_pa_operation* pOP; + + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); + pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, devCapture, ma_device_source_name_callback, pDevice); + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); + + ma_wait_for_operation_and_unref__pulse(pContext, pOP); } } @@ -21567,7 +22428,9 @@ static ma_result ma_device_init__pulse(ma_context* pContext, const ma_device_con Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a device state of MA_STATE_UNINITIALIZED. */ + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); ((ma_pa_stream_set_write_callback_proc)pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice); + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); /* Connect after we've got all of our internal state set up. */ @@ -21576,7 +22439,9 @@ static ma_result ma_device_init__pulse(ma_context* pContext, const ma_device_con streamFlags |= MA_PA_STREAM_DONT_MOVE; } + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); error = ((ma_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); if (error != MA_PA_OK) { result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.", ma_result_from_pulse(error)); goto on_error3; @@ -21588,40 +22453,52 @@ static ma_result ma_device_init__pulse(ma_context* pContext, const ma_device_con } - /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualSS != NULL) { - ss = *pActualSS; - } + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); + { + /* Internal format. */ + pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (pActualSS != NULL) { + ss = *pActualSS; + } - pDevice->playback.internalFormat = ma_format_from_pulse(ss.format); - pDevice->playback.internalChannels = ss.channels; - pDevice->playback.internalSampleRate = ss.rate; + pDevice->playback.internalFormat = ma_format_from_pulse(ss.format); + pDevice->playback.internalChannels = ss.channels; + pDevice->playback.internalSampleRate = ss.rate; - /* Internal channel map. */ - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualCMap != NULL) { - cmap = *pActualCMap; - } - for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - pDevice->playback.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); - } + /* Internal channel map. */ + pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (pActualCMap != NULL) { + cmap = *pActualCMap; + } + for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { + pDevice->playback.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); + } - /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualAttr != NULL) { - attr = *pActualAttr; + /* Buffer. */ + pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (pActualAttr != NULL) { + attr = *pActualAttr; + } + pDevice->playback.internalPeriods = attr.maxlength / attr.tlength; + pDevice->playback.internalPeriodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels) / pDevice->playback.internalPeriods; + #ifdef MA_DEBUG_OUTPUT + printf("[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->playback.internalPeriodSizeInFrames); + #endif } - pDevice->playback.internalPeriods = attr.maxlength / attr.tlength; - pDevice->playback.internalPeriodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels) / pDevice->playback.internalPeriods; - #ifdef MA_DEBUG_OUTPUT - printf("[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->playback.internalPeriodSizeInFrames); - #endif + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); /* Name. */ + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); devPlayback = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); if (devPlayback != NULL) { - ma_wait_for_operation_and_unref__pulse(pContext, ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, devPlayback, ma_device_sink_name_callback, pDevice)); + ma_pa_operation* pOP; + + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); + pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, devPlayback, ma_device_sink_name_callback, pDevice); + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); + + ma_wait_for_operation_and_unref__pulse(pContext, pOP); } } @@ -21652,19 +22529,27 @@ static ma_result ma_device_init__pulse(ma_context* pContext, const ma_device_con on_error4: if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); } on_error3: if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); } on_error2: if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); } on_error1: if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_mainloop_lock__pulse(pContext, "ma_device_init__pulse"); ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + ma_mainloop_unlock__pulse(pContext, "ma_device_init__pulse"); } on_error0: return result; @@ -21699,7 +22584,10 @@ static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_typ pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback); MA_ASSERT(pStream != NULL); + ma_mainloop_lock__pulse(pContext, "ma_device__cork_stream__pulse"); pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful); + ma_mainloop_unlock__pulse(pContext, "ma_device__cork_stream__pulse"); + if (pOP == NULL) { return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream.", (cork == 0) ? MA_FAILED_TO_START_BACKEND_DEVICE : MA_FAILED_TO_STOP_BACKEND_DEVICE); } @@ -21736,9 +22624,7 @@ static ma_result ma_device_start__pulse(ma_device* pDevice) if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { /* We need to fill some data before uncorking. Not doing this will result in the write callback never getting fired. */ ma_mainloop_lock__pulse(pDevice->pContext, "ma_device_start__pulse"); - { - result = ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); - } + result = ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); ma_mainloop_unlock__pulse(pDevice->pContext, "ma_device_start__pulse"); if (result != MA_SUCCESS) { @@ -21770,7 +22656,13 @@ static ma_result ma_device_stop__pulse(ma_device* pDevice) if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { /* The stream needs to be drained if it's a playback device. */ - ma_wait_for_operation_and_unref__pulse(pDevice->pContext, ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful)); + ma_pa_operation* pOP; + + ma_mainloop_lock__pulse(pDevice->pContext, "ma_device_stop__pulse"); + pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful); + ma_mainloop_unlock__pulse(pDevice->pContext, "ma_device_stop__pulse"); + + ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP); result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); if (result != MA_SUCCESS) { @@ -21782,7 +22674,7 @@ static ma_result ma_device_stop__pulse(ma_device* pDevice) pDevice->onStop(pDevice); } - return result; + return MA_SUCCESS; } static ma_result ma_context_uninit__pulse(ma_context* pContext) @@ -21790,8 +22682,12 @@ static ma_result ma_context_uninit__pulse(ma_context* pContext) MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_pulseaudio); - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); + ma_mainloop_lock__pulse(pContext, "ma_context_uninit__pulse"); + { + ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext); + ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); + } + ma_mainloop_unlock__pulse(pContext, "ma_context_uninit__pulse"); /* The mainloop needs to be stopped before freeing. */ ((ma_pa_threaded_mainloop_stop_proc)pContext->pulse.pa_threaded_mainloop_stop)((ma_pa_threaded_mainloop*)pContext->pulse.pMainLoop); @@ -22014,9 +22910,27 @@ static ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_con return result; } + /* We should start the mainloop locked and unlock once ready to wait . */ + ma_mainloop_lock__pulse(pContext, "ma_context_init__pulse"); + + /* With the mainloop created we can now start it. */ + result = ma_result_from_pulse(((ma_pa_threaded_mainloop_start_proc)pContext->pulse.pa_threaded_mainloop_start)((ma_pa_threaded_mainloop*)pContext->pulse.pMainLoop)); + if (result != MA_SUCCESS) { + ma_mainloop_unlock__pulse(pContext, "ma_context_init__pulse"); + ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to start mainloop.", result); + ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); + ((ma_pa_threaded_mainloop_free_proc)pContext->pulse.pa_threaded_mainloop_free)((ma_pa_threaded_mainloop*)(pContext->pulse.pMainLoop)); + #ifndef MA_NO_RUNTIME_LINKING + ma_dlclose(pContext, pContext->pulse.pulseSO); + #endif + return result; + } + pContext->pulse.pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_threaded_mainloop_get_api_proc)pContext->pulse.pa_threaded_mainloop_get_api)((ma_pa_threaded_mainloop*)pContext->pulse.pMainLoop), pConfig->pulse.pApplicationName); if (pContext->pulse.pPulseContext == NULL) { + ma_mainloop_unlock__pulse(pContext, "ma_context_init__pulse"); result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context.", MA_FAILED_TO_INIT_BACKEND); + ((ma_pa_threaded_mainloop_stop_proc)pContext->pulse.pa_threaded_mainloop_stop)((ma_pa_threaded_mainloop*)(pContext->pulse.pMainLoop)); ((ma_pa_threaded_mainloop_free_proc)pContext->pulse.pa_threaded_mainloop_free)((ma_pa_threaded_mainloop*)(pContext->pulse.pMainLoop)); #ifndef MA_NO_RUNTIME_LINKING ma_dlclose(pContext, pContext->pulse.pulseSO); @@ -22027,7 +22941,9 @@ static ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_con /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pContext->pulse.pPulseContext, pConfig->pulse.pServerName, (pConfig->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); if (result != MA_SUCCESS) { + ma_mainloop_unlock__pulse(pContext, "ma_context_init__pulse"); ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.", result); + ((ma_pa_threaded_mainloop_stop_proc)pContext->pulse.pa_threaded_mainloop_stop)((ma_pa_threaded_mainloop*)(pContext->pulse.pMainLoop)); ((ma_pa_threaded_mainloop_free_proc)pContext->pulse.pa_threaded_mainloop_free)((ma_pa_threaded_mainloop*)(pContext->pulse.pMainLoop)); #ifndef MA_NO_RUNTIME_LINKING ma_dlclose(pContext, pContext->pulse.pulseSO); @@ -22035,18 +22951,10 @@ static ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_con return result; } - /* We now need to start the mainloop. Once the loop has started we can then wait for the PulseAudio context to connect. */ - result = ma_result_from_pulse(((ma_pa_threaded_mainloop_start_proc)pContext->pulse.pa_threaded_mainloop_start)((ma_pa_threaded_mainloop*)pContext->pulse.pMainLoop)); - if (result != MA_SUCCESS) { - ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to start mainloop.", result); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_threaded_mainloop_free_proc)pContext->pulse.pa_threaded_mainloop_free)((ma_pa_threaded_mainloop*)(pContext->pulse.pMainLoop)); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->pulse.pulseSO); - #endif - return result; - } + /* Can now unlock. */ + ma_mainloop_unlock__pulse(pContext, "ma_context_init__pulse"); + /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ result = ma_context_wait_for_pa_context_to_connect__pulse(pContext); if (result != MA_SUCCESS) { ((ma_pa_threaded_mainloop_stop_proc)pContext->pulse.pa_threaded_mainloop_stop)((ma_pa_threaded_mainloop*)(pContext->pulse.pMainLoop)); @@ -22185,10 +23093,12 @@ static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enu cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } + (void)cbResult; /* For silencing a static analysis warning. */ + return MA_SUCCESS; } -static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo) +static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { ma_jack_client_t* pClient; ma_result result; @@ -22196,11 +23106,6 @@ static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_devic MA_ASSERT(pContext != NULL); - /* No exclusive mode with the JACK backend. */ - if (shareMode == ma_share_mode_exclusive) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - if (pDeviceID != NULL && pDeviceID->jack != 0) { return MA_NO_DEVICE; /* Don't know the device. */ } @@ -22216,8 +23121,7 @@ static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_devic pDeviceInfo->isDefault = MA_TRUE; /* Jack only supports f32 and has a specific channel count and sample rate. */ - pDeviceInfo->formatCount = 1; - pDeviceInfo->formats[0] = ma_format_f32; + pDeviceInfo->nativeDataFormats[0].format = ma_format_f32; /* The channel count and sample rate can only be determined by opening the device. */ result = ma_context_open_client__jack(pContext, &pClient); @@ -22225,11 +23129,8 @@ static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_devic return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", result); } - pDeviceInfo->minSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); - pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate; - - pDeviceInfo->minChannels = 0; - pDeviceInfo->maxChannels = 0; + pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); + pDeviceInfo->nativeDataFormats[0].channels = 0; ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); if (ppPorts == NULL) { @@ -22237,11 +23138,13 @@ static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_devic return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); } - while (ppPorts[pDeviceInfo->minChannels] != NULL) { - pDeviceInfo->minChannels += 1; - pDeviceInfo->maxChannels += 1; + while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) { + pDeviceInfo->nativeDataFormats[0].channels += 1; } + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormatCount = 1; + ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); @@ -22250,7 +23153,7 @@ static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_devic } -static void ma_device_uninit__jack(ma_device* pDevice) +static ma_result ma_device_uninit__jack(ma_device* pDevice) { ma_context* pContext; @@ -22271,9 +23174,7 @@ static void ma_device_uninit__jack(ma_device* pDevice) ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); } - if (pDevice->type == ma_device_type_duplex) { - ma_pcm_rb_uninit(&pDevice->jack.duplexRB); - } + return MA_SUCCESS; } static void ma_device__jack_shutdown_callback(void* pUserData) @@ -22347,19 +23248,11 @@ static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* } } - if (pDevice->type == ma_device_type_duplex) { - ma_device__handle_duplex_callback_capture(pDevice, frameCount, pDevice->jack.pIntermediaryBufferCapture, &pDevice->jack.duplexRB); - } else { - ma_device__send_frames_to_client(pDevice, frameCount, pDevice->jack.pIntermediaryBufferCapture); - } + ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->type == ma_device_type_duplex) { - ma_device__handle_duplex_callback_playback(pDevice, frameCount, pDevice->jack.pIntermediaryBufferPlayback, &pDevice->jack.duplexRB); - } else { - ma_device__read_frames_from_client(pDevice, frameCount, pDevice->jack.pIntermediaryBufferPlayback); - } + ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount); /* Channels need to be deinterleaved. */ for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { @@ -22380,13 +23273,11 @@ static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* return 0; } -static ma_result ma_device_init__jack(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) +static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { ma_result result; - ma_uint32 periods; ma_uint32 periodSizeInFrames; - MA_ASSERT(pContext != NULL); MA_ASSERT(pConfig != NULL); MA_ASSERT(pDevice != NULL); @@ -22395,72 +23286,71 @@ static ma_result ma_device_init__jack(ma_context* pContext, const ma_device_conf } /* Only supporting default devices with JACK. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID != NULL && pConfig->playback.pDeviceID->jack != 0) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID != NULL && pConfig->capture.pDeviceID->jack != 0)) { + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { return MA_NO_DEVICE; } /* No exclusive mode with the JACK backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } /* Open the client. */ - result = ma_context_open_client__jack(pContext, (ma_jack_client_t**)&pDevice->jack.pClient); + result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient); if (result != MA_SUCCESS) { return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", result); } /* Callbacks. */ - if (((ma_jack_set_process_callback_proc)pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { + if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); } - if (((ma_jack_set_buffer_size_callback_proc)pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { + if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); } - ((ma_jack_on_shutdown_proc)pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); + ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); /* The buffer size in frames can change. */ - periods = pConfig->periods; - periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); + periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { const char** ppPorts; - pDevice->capture.internalFormat = ma_format_f32; - pDevice->capture.internalChannels = 0; - pDevice->capture.internalSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap); + pDescriptorCapture->format = ma_format_f32; + pDescriptorCapture->channels = 0; + pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDescriptorCapture->channels, pDescriptorCapture->channelMap); - ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); + ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); if (ppPorts == NULL) { return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); } - while (ppPorts[pDevice->capture.internalChannels] != NULL) { + while (ppPorts[pDescriptorCapture->channels] != NULL) { char name[64]; ma_strcpy_s(name, sizeof(name), "capture"); - ma_itoa_s((int)pDevice->capture.internalChannels, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ + ma_itoa_s((int)pDescriptorCapture->channels, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ - pDevice->jack.pPortsCapture[pDevice->capture.internalChannels] = ((ma_jack_port_register_proc)pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); - if (pDevice->jack.pPortsCapture[pDevice->capture.internalChannels] == NULL) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); + pDevice->jack.pPortsCapture[pDescriptorCapture->channels] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); + if (pDevice->jack.pPortsCapture[pDescriptorCapture->channels] == NULL) { + ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); ma_device_uninit__jack(pDevice); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); } - pDevice->capture.internalChannels += 1; + pDescriptorCapture->channels += 1; } - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); + ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - pDevice->capture.internalPeriodSizeInFrames = periodSizeInFrames; - pDevice->capture.internalPeriods = periods; + pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; + pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - pDevice->jack.pIntermediaryBufferCapture = (float*)ma__calloc_from_callbacks(pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels), &pContext->allocationCallbacks); + pDevice->jack.pIntermediaryBufferCapture = (float*)ma__calloc_from_callbacks(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); if (pDevice->jack.pIntermediaryBufferCapture == NULL) { ma_device_uninit__jack(pDevice); return MA_OUT_OF_MEMORY; @@ -22470,63 +23360,43 @@ static ma_result ma_device_init__jack(ma_context* pContext, const ma_device_conf if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { const char** ppPorts; - pDevice->playback.internalFormat = ma_format_f32; - pDevice->playback.internalChannels = 0; - pDevice->playback.internalSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap); + pDescriptorPlayback->format = ma_format_f32; + pDescriptorPlayback->channels = 0; + pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); - ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); + ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); if (ppPorts == NULL) { return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); } - while (ppPorts[pDevice->playback.internalChannels] != NULL) { + while (ppPorts[pDescriptorPlayback->channels] != NULL) { char name[64]; ma_strcpy_s(name, sizeof(name), "playback"); - ma_itoa_s((int)pDevice->playback.internalChannels, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ + ma_itoa_s((int)pDescriptorPlayback->channels, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ - pDevice->jack.pPortsPlayback[pDevice->playback.internalChannels] = ((ma_jack_port_register_proc)pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); - if (pDevice->jack.pPortsPlayback[pDevice->playback.internalChannels] == NULL) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); + pDevice->jack.pPortsPlayback[pDescriptorPlayback->channels] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); + if (pDevice->jack.pPortsPlayback[pDescriptorPlayback->channels] == NULL) { + ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); ma_device_uninit__jack(pDevice); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); } - pDevice->playback.internalChannels += 1; + pDescriptorPlayback->channels += 1; } - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); + ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames; - pDevice->playback.internalPeriods = periods; + pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; + pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - pDevice->jack.pIntermediaryBufferPlayback = (float*)ma__calloc_from_callbacks(pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels), &pContext->allocationCallbacks); + pDevice->jack.pIntermediaryBufferPlayback = (float*)ma__calloc_from_callbacks(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); if (pDevice->jack.pIntermediaryBufferPlayback == NULL) { ma_device_uninit__jack(pDevice); return MA_OUT_OF_MEMORY; } } - if (pDevice->type == ma_device_type_duplex) { - ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods); - result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->pContext->allocationCallbacks, &pDevice->jack.duplexRB); - if (result != MA_SUCCESS) { - ma_device_uninit__jack(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to initialize ring buffer.", result); - } - - /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */ - { - ma_uint32 marginSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods; - void* pMarginData; - ma_pcm_rb_acquire_write(&pDevice->jack.duplexRB, &marginSizeInFrames, &pMarginData); - { - MA_ZERO_MEMORY(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); - } - ma_pcm_rb_commit_write(&pDevice->jack.duplexRB, marginSizeInFrames, pMarginData); - } - } - return MA_SUCCESS; } @@ -22622,7 +23492,7 @@ static ma_result ma_context_uninit__jack(ma_context* pContext) return MA_SUCCESS; } -static ma_result ma_context_init__jack(const ma_context_config* pConfig, ma_context* pContext) +static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { #ifndef MA_NO_RUNTIME_LINKING const char* libjackNames[] = { @@ -22725,15 +23595,18 @@ static ma_result ma_context_init__jack(const ma_context_config* pConfig, ma_cont ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient); } - pContext->isBackendAsynchronous = MA_TRUE; - pContext->onUninit = ma_context_uninit__jack; - pContext->onEnumDevices = ma_context_enumerate_devices__jack; - pContext->onGetDeviceInfo = ma_context_get_device_info__jack; - pContext->onDeviceInit = ma_device_init__jack; - pContext->onDeviceUninit = ma_device_uninit__jack; - pContext->onDeviceStart = ma_device_start__jack; - pContext->onDeviceStop = ma_device_stop__jack; + pCallbacks->onContextInit = ma_context_init__jack; + pCallbacks->onContextUninit = ma_context_uninit__jack; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack; + pCallbacks->onDeviceInit = ma_device_init__jack; + pCallbacks->onDeviceUninit = ma_device_uninit__jack; + pCallbacks->onDeviceStart = ma_device_start__jack; + pCallbacks->onDeviceStop = ma_device_stop__jack; + pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */ + pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */ + pCallbacks->onDeviceAudioThread = NULL; /* Not used because JACK is asynchronous. */ return MA_SUCCESS; } @@ -24239,7 +25112,7 @@ static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInF MA_ASSERT(sizeInFrames > 0); MA_ASSERT(format != ma_format_unknown); MA_ASSERT(channels > 0); - + allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */ if (layout == ma_stream_layout_interleaved) { /* Interleaved case. This is the simple case because we just have one buffer. */ @@ -24248,14 +25121,14 @@ static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInF /* Non-interleaved case. This is the more complex case because there's more than one buffer. */ allocationSize += sizeof(AudioBuffer) * channels; } - + allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels); pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(allocationSize, pAllocationCallbacks); if (pBufferList == NULL) { return NULL; } - + audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format)); if (layout == ma_stream_layout_interleaved) { @@ -24272,7 +25145,7 @@ static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInF pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer); } } - + return pBufferList; } @@ -24281,22 +25154,22 @@ static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice MA_ASSERT(pDevice != NULL); MA_ASSERT(format != ma_format_unknown); MA_ASSERT(channels > 0); - + /* Only resize the buffer if necessary. */ if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) { AudioBufferList* pNewAudioBufferList; - + pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); if (pNewAudioBufferList != NULL) { return MA_OUT_OF_MEMORY; } - + /* At this point we'll have a new AudioBufferList and we can free the old one. */ ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList; pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames; } - + /* Getting here means the capacity of the audio is fine. */ return MA_SUCCESS; } @@ -24916,11 +25789,11 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) m_pDevice->sampleRate = (ma_uint32)pSession.sampleRate; if (m_pDevice->type == ma_device_type_capture || m_pDevice->type == ma_device_type_duplex) { - m_pDevice->capture.channels = (ma_uint32)pSession.inputNumberOfChannels; + m_pDevice->capture.internalChannels = (ma_uint32)pSession.inputNumberOfChannels; ma_device__post_init_setup(m_pDevice, ma_device_type_capture); } if (m_pDevice->type == ma_device_type_playback || m_pDevice->type == ma_device_type_duplex) { - m_pDevice->playback.channels = (ma_uint32)pSession.outputNumberOfChannels; + m_pDevice->playback.internalChannels = (ma_uint32)pSession.outputNumberOfChannels; ma_device__post_init_setup(m_pDevice, ma_device_type_playback); } } @@ -25003,12 +25876,12 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev OSStatus status; UInt32 enableIOFlag; AudioStreamBasicDescription bestFormat; - ma_uint32 actualPeriodSizeInFrames; + UInt32 actualPeriodSizeInFrames; AURenderCallbackStruct callbackInfo; #if defined(MA_APPLE_DESKTOP) AudioObjectID deviceObjectID; #else - ma_uint32 actualPeriodSizeInFramesSize = sizeof(actualPeriodSizeInFrames); + UInt32 actualPeriodSizeInFramesSize = sizeof(actualPeriodSizeInFrames); #endif /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ @@ -25310,13 +26183,13 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev return ma_result_from_OSStatus(status); } - pData->periodSizeInFramesOut = actualPeriodSizeInFrames; + pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames; /* We need a buffer list if this is an input device. We render into this in the input callback. */ if (deviceType == ma_device_type_capture) { ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; AudioBufferList* pBufferList; - + pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks); if (pBufferList == NULL) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); @@ -28625,20 +29498,23 @@ AAudio Backend ******************************************************************************/ #ifdef MA_HAS_AAUDIO + /*#include */ -#define MA_AAUDIO_UNSPECIFIED 0 +typedef int32_t ma_aaudio_result_t; +typedef int32_t ma_aaudio_direction_t; +typedef int32_t ma_aaudio_sharing_mode_t; +typedef int32_t ma_aaudio_format_t; +typedef int32_t ma_aaudio_stream_state_t; +typedef int32_t ma_aaudio_performance_mode_t; +typedef int32_t ma_aaudio_usage_t; +typedef int32_t ma_aaudio_content_type_t; +typedef int32_t ma_aaudio_input_preset_t; +typedef int32_t ma_aaudio_data_callback_result_t; +typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; +typedef struct ma_AAudioStream_t* ma_AAudioStream; -typedef int32_t ma_aaudio_result_t; -typedef int32_t ma_aaudio_direction_t; -typedef int32_t ma_aaudio_sharing_mode_t; -typedef int32_t ma_aaudio_format_t; -typedef int32_t ma_aaudio_stream_state_t; -typedef int32_t ma_aaudio_performance_mode_t; -typedef int32_t ma_aaudio_usage_t; -typedef int32_t ma_aaudio_content_type_t; -typedef int32_t ma_aaudio_input_preset_t; -typedef int32_t ma_aaudio_data_callback_result_t; +#define MA_AAUDIO_UNSPECIFIED 0 /* Result codes. miniaudio only cares about the success code. */ #define MA_AAUDIO_OK 0 @@ -28712,9 +29588,6 @@ typedef int32_t ma_aaudio_data_callback_result_t; #define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 #define MA_AAUDIO_CALLBACK_RESULT_STOP 1 -/* Objects. */ -typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; -typedef struct ma_AAudioStream_t* ma_AAudioStream; typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error); @@ -29324,10 +30197,10 @@ static ma_result ma_context_uninit__aaudio(ma_context* pContext) static ma_result ma_context_init__aaudio(const ma_context_config* pConfig, ma_context* pContext) { + size_t i; const char* libNames[] = { "libaaudio.so" }; - size_t i; for (i = 0; i < ma_countof(libNames); ++i) { pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]); @@ -29834,7 +30707,7 @@ static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBuff */ /* Don't do anything if the device is not started. */ - if (pDevice->state != MA_STATE_STARTED) { + if (ma_device_get_state(pDevice) != MA_STATE_STARTED) { return; } @@ -29872,7 +30745,7 @@ static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBuf (void)pBufferQueue; /* Don't do anything if the device is not started. */ - if (pDevice->state != MA_STATE_STARTED) { + if (ma_device_get_state(pDevice) != MA_STATE_STARTED) { return; } @@ -30503,15 +31376,19 @@ static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext) static ma_result ma_context_init__opensl(const ma_context_config* pConfig, ma_context* pContext) { ma_result result; + +#if !defined(MA_NO_RUNTIME_LINKING) size_t i; const char* libOpenSLESNames[] = { "libOpenSLES.so" }; +#endif MA_ASSERT(pContext != NULL); (void)pConfig; +#if !defined(MA_NO_RUNTIME_LINKING) /* Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime @@ -30526,49 +31403,68 @@ static ma_result ma_context_init__opensl(const ma_context_config* pConfig, ma_co } if (pContext->opensl.libOpenSLES == NULL) { - return MA_NO_BACKEND; /* Couldn't find libOpenSLES.so */ + ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Could not find libOpenSLES.so"); + return MA_NO_BACKEND; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); if (result != MA_SUCCESS) { + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); if (result != MA_SUCCESS) { + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); if (result != MA_SUCCESS) { + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); if (result != MA_SUCCESS) { + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); if (result != MA_SUCCESS) { + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); if (result != MA_SUCCESS) { + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); if (result != MA_SUCCESS) { + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine"); if (pContext->opensl.slCreateEngine == NULL) { + ma_dlclose(pContext, pContext->opensl.libOpenSLES); ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Cannot find symbol slCreateEngine."); return MA_NO_BACKEND; } +#else + pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE; + pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES; + pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD; + pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY; + pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX; + pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION; + pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine; +#endif /* Initialize global data first if applicable. */ @@ -30579,7 +31475,9 @@ static ma_result ma_context_init__opensl(const ma_context_config* pConfig, ma_co ma_spinlock_unlock(&g_maOpenSLSpinlock); if (result != MA_SUCCESS) { - return result; /* Failed to initialize the OpenSL engine. */ + ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Failed to initialize OpenSL engine."); + return result; } @@ -30618,20 +31516,12 @@ extern "C" { #endif void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames) { - if (pDevice->type == ma_device_type_duplex) { - ma_device__handle_duplex_callback_capture(pDevice, (ma_uint32)frameCount, pFrames, &pDevice->webaudio.duplexRB); - } else { - ma_device__send_frames_to_client(pDevice, (ma_uint32)frameCount, pFrames); /* Send directly to the client. */ - } + ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount); } void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames) { - if (pDevice->type == ma_device_type_duplex) { - ma_device__handle_duplex_callback_playback(pDevice, (ma_uint32)frameCount, pFrames, &pDevice->webaudio.duplexRB); - } else { - ma_device__read_frames_from_client(pDevice, (ma_uint32)frameCount, pFrames); /* Read directly from the device. */ - } + ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount); } #ifdef __cplusplus } @@ -30669,20 +31559,14 @@ static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma return MA_SUCCESS; } -static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo) +static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { MA_ASSERT(pContext != NULL); - /* No exclusive mode with Web Audio. */ - if (shareMode == ma_share_mode_exclusive) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { return MA_NO_DEVICE; } - MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio)); /* Only supporting default devices for now. */ @@ -30697,14 +31581,10 @@ static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_d pDeviceInfo->isDefault = MA_TRUE; /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */ - pDeviceInfo->minChannels = 1; - pDeviceInfo->maxChannels = MA_MAX_CHANNELS; - if (pDeviceInfo->maxChannels > 32) { - pDeviceInfo->maxChannels = 32; /* Maximum output channel count is 32 for createScriptProcessor() (JavaScript). */ - } - - /* We can query the sample rate by just using a temporary audio context. */ - pDeviceInfo->minSampleRate = EM_ASM_INT({ + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; + pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */ + pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({ try { var temp = new (window.AudioContext || window.webkitAudioContext)(); var sampleRate = temp.sampleRate; @@ -30714,14 +31594,12 @@ static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_d return 0; } }, 0); /* Must pass in a dummy argument for C99 compatibility. */ - pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate; - if (pDeviceInfo->minSampleRate == 0) { + + if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) { return MA_NO_DEVICE; } - /* Web Audio only supports f32. */ - pDeviceInfo->formatCount = 1; - pDeviceInfo->formats[0] = ma_format_f32; + pDeviceInfo->nativeDataFormatCount = 1; return MA_SUCCESS; } @@ -30765,7 +31643,7 @@ static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_ty }, deviceIndex, deviceType); } -static void ma_device_uninit__webaudio(ma_device* pDevice) +static ma_result ma_device_uninit__webaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); @@ -30777,51 +31655,60 @@ static void ma_device_uninit__webaudio(ma_device* pDevice) ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback); } - if (pDevice->type == ma_device_type_duplex) { - ma_pcm_rb_uninit(&pDevice->webaudio.duplexRB); - } + return MA_SUCCESS; } -static ma_result ma_device_init_by_type__webaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice) +static ma_uint32 ma_calculate_period_size_in_frames__dsound(ma_uint32 periodSizeInFrames, ma_uint32 periodSizeInMilliseconds, ma_uint32 sampleRate, ma_performance_profile performanceProfile) +{ + /* + There have been reports of the default buffer size being too small on some browsers. There have been reports of the default buffer + size being too small on some browsers. If we're using default buffer size, we'll make sure the period size is a big biffer than our + standard defaults. + */ + if (periodSizeInFrames == 0) { + if (periodSizeInMilliseconds == 0) { + if (performanceProfile == ma_performance_profile_low_latency) { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, sampleRate); /* 1 frame @ 30 FPS */ + } else { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, sampleRate); + } + } else { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, sampleRate); + } + } + + /* The size of the buffer must be a power of 2 and between 256 and 16384. */ + if (periodSizeInFrames < 256) { + periodSizeInFrames = 256; + } else if (periodSizeInFrames > 16384) { + periodSizeInFrames = 16384; + } else { + periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames); + } + + return periodSizeInFrames; +} + +static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) { int deviceIndex; - ma_uint32 internalPeriodSizeInFrames; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 periodSizeInFrames; - MA_ASSERT(pContext != NULL); + MA_ASSERT(pDevice != NULL); MA_ASSERT(pConfig != NULL); MA_ASSERT(deviceType != ma_device_type_duplex); - MA_ASSERT(pDevice != NULL); if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { return MA_NO_DEVICE; } - /* - Try calculating an appropriate buffer size. There have been reports of the default buffer size being too small on some browsers. If we're using default buffer size, we'll make sure - the period size is a big biffer than our standard defaults. - */ - internalPeriodSizeInFrames = pConfig->periodSizeInFrames; - if (internalPeriodSizeInFrames == 0) { - ma_uint32 periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; - if (pDevice->usingDefaultBufferSize) { - if (pConfig->performanceProfile == ma_performance_profile_low_latency) { - periodSizeInMilliseconds = 33; /* 1 frame @ 30 FPS */ - } else { - periodSizeInMilliseconds = 333; - } - } + /* We're going to calculate some stuff in C just to simplify the JS code. */ + channels = (pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; + sampleRate = (pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; + periodSizeInFrames = ma_calculate_period_size_in_frames__dsound(pDescriptor->periodSizeInFrames, pDescriptor->periodSizeInMilliseconds, pDescriptor->sampleRate, pConfig->performanceProfile); - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, pConfig->sampleRate); - } - - /* The size of the buffer must be a power of 2 and between 256 and 16384. */ - if (internalPeriodSizeInFrames < 256) { - internalPeriodSizeInFrames = 256; - } else if (internalPeriodSizeInFrames > 16384) { - internalPeriodSizeInFrames = 16384; - } else { - internalPeriodSizeInFrames = ma_next_power_of_2(internalPeriodSizeInFrames); - } /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */ deviceIndex = EM_ASM_INT({ @@ -30985,34 +31872,29 @@ static ma_result ma_device_init_by_type__webaudio(ma_context* pContext, const ma } return miniaudio.track_device(device); - }, (deviceType == ma_device_type_capture) ? pConfig->capture.channels : pConfig->playback.channels, pConfig->sampleRate, internalPeriodSizeInFrames, deviceType == ma_device_type_capture, pDevice); + }, channels, sampleRate, periodSizeInFrames, deviceType == ma_device_type_capture, pDevice); if (deviceIndex < 0) { return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } if (deviceType == ma_device_type_capture) { - pDevice->webaudio.indexCapture = deviceIndex; - pDevice->capture.internalFormat = ma_format_f32; - pDevice->capture.internalChannels = pConfig->capture.channels; - ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap); - pDevice->capture.internalSampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); - pDevice->capture.internalPeriodSizeInFrames = internalPeriodSizeInFrames; - pDevice->capture.internalPeriods = 1; + pDevice->webaudio.indexCapture = deviceIndex; } else { - pDevice->webaudio.indexPlayback = deviceIndex; - pDevice->playback.internalFormat = ma_format_f32; - pDevice->playback.internalChannels = pConfig->playback.channels; - ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap); - pDevice->playback.internalSampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); - pDevice->playback.internalPeriodSizeInFrames = internalPeriodSizeInFrames; - pDevice->playback.internalPeriods = 1; + pDevice->webaudio.indexPlayback = deviceIndex; } + pDescriptor->format = ma_format_f32; + pDescriptor->channels = channels; + ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDescriptor->channels, pDescriptor->channelMap); + pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); + pDescriptor->periodSizeInFrames = periodSizeInFrames; + pDescriptor->periodCount = 1; + return MA_SUCCESS; } -static ma_result ma_device_init__webaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) +static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { ma_result result; @@ -31021,20 +31903,20 @@ static ma_result ma_device_init__webaudio(ma_context* pContext, const ma_device_ } /* No exclusive mode with Web Audio. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__webaudio(pContext, pConfig, ma_device_type_capture, pDevice); + result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); if (result != MA_SUCCESS) { return result; } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__webaudio(pContext, pConfig, ma_device_type_playback, pDevice); + result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); if (result != MA_SUCCESS) { if (pConfig->deviceType == ma_device_type_duplex) { ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture); @@ -31043,36 +31925,6 @@ static ma_result ma_device_init__webaudio(ma_context* pContext, const ma_device_ } } - /* - We need a ring buffer for moving data from the capture device to the playback device. The capture callback is the producer - and the playback callback is the consumer. The buffer needs to be large enough to hold internalPeriodSizeInFrames based on - the external sample rate. - */ - if (pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames) * 2; - result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->pContext->allocationCallbacks, &pDevice->webaudio.duplexRB); - if (result != MA_SUCCESS) { - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture); - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback); - } - return result; - } - - /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */ - { - ma_uint32 marginSizeInFrames = rbSizeInFrames / 3; /* <-- Dividing by 3 because internalPeriods is always set to 1 for WebAudio. */ - void* pMarginData; - ma_pcm_rb_acquire_write(&pDevice->webaudio.duplexRB, &marginSizeInFrames, &pMarginData); - { - MA_ZERO_MEMORY(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); - } - ma_pcm_rb_commit_write(&pDevice->webaudio.duplexRB, marginSizeInFrames, pMarginData); - } - } - return MA_SUCCESS; } @@ -31140,12 +31992,14 @@ static ma_result ma_context_uninit__webaudio(ma_context* pContext) return MA_SUCCESS; } -static ma_result ma_context_init__webaudio(const ma_context_config* pConfig, ma_context* pContext) +static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { int resultFromJS; MA_ASSERT(pContext != NULL); + (void)pConfig; /* Unused. */ + /* Here is where our global JavaScript object is initialized. */ resultFromJS = EM_ASM_INT({ if ((window.AudioContext || window.webkitAudioContext) === undefined) { @@ -31155,7 +32009,7 @@ static ma_result ma_context_init__webaudio(const ma_context_config* pConfig, ma_ if (typeof(miniaudio) === 'undefined') { miniaudio = {}; miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */ - + miniaudio.track_device = function(device) { /* Try inserting into a free slot first. */ for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { @@ -31164,16 +32018,16 @@ static ma_result ma_context_init__webaudio(const ma_context_config* pConfig, ma_ return iDevice; } } - + /* Getting here means there is no empty slots in the array so we just push to the end. */ miniaudio.devices.push(device); return miniaudio.devices.length - 1; }; - + miniaudio.untrack_device_by_index = function(deviceIndex) { /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */ miniaudio.devices[deviceIndex] = null; - + /* Trim the array if possible. */ while (miniaudio.devices.length > 0) { if (miniaudio.devices[miniaudio.devices.length-1] == null) { @@ -31183,7 +32037,7 @@ static ma_result ma_context_init__webaudio(const ma_context_config* pConfig, ma_ } } }; - + miniaudio.untrack_device = function(device) { for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { if (miniaudio.devices[iDevice] == device) { @@ -31191,12 +32045,12 @@ static ma_result ma_context_init__webaudio(const ma_context_config* pConfig, ma_ } } }; - + miniaudio.get_device_by_index = function(deviceIndex) { return miniaudio.devices[deviceIndex]; }; } - + return 1; }, 0); /* Must pass in a dummy argument for C99 compatibility. */ @@ -31204,18 +32058,18 @@ static ma_result ma_context_init__webaudio(const ma_context_config* pConfig, ma_ return MA_FAILED_TO_INIT_BACKEND; } + pCallbacks->onContextInit = ma_context_init__webaudio; + pCallbacks->onContextUninit = ma_context_uninit__webaudio; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio; + pCallbacks->onDeviceInit = ma_device_init__webaudio; + pCallbacks->onDeviceUninit = ma_device_uninit__webaudio; + pCallbacks->onDeviceStart = ma_device_start__webaudio; + pCallbacks->onDeviceStop = ma_device_stop__webaudio; + pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */ + pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */ + pCallbacks->onDeviceAudioThread = NULL; /* Not needed because WebAudio is asynchronous. */ - pContext->isBackendAsynchronous = MA_TRUE; - - pContext->onUninit = ma_context_uninit__webaudio; - pContext->onEnumDevices = ma_context_enumerate_devices__webaudio; - pContext->onGetDeviceInfo = ma_context_get_device_info__webaudio; - pContext->onDeviceInit = ma_device_init__webaudio; - pContext->onDeviceUninit = ma_device_uninit__webaudio; - pContext->onDeviceStart = ma_device_start__webaudio; - pContext->onDeviceStop = ma_device_stop__webaudio; - - (void)pConfig; /* Unused. */ return MA_SUCCESS; } #endif /* Web Audio */ @@ -31265,7 +32119,11 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d if (pDevice->capture.internalChannels == pDevice->capture.channels) { ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels); } else { - ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->capture.channels, pDevice->capture.channelMap); + if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) { + ma_channel_map_init_blank(pDevice->capture.channels, pDevice->capture.channelMap); + } else { + ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->capture.channels, pDevice->capture.channelMap); + } } } } @@ -31282,7 +32140,11 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d if (pDevice->playback.internalChannels == pDevice->playback.channels) { ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels); } else { - ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->playback.channels, pDevice->playback.channelMap); + if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) { + ma_channel_map_init_blank(pDevice->playback.channels, pDevice->playback.channelMap); + } else { + ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->playback.channels, pDevice->playback.channelMap); + } } } } @@ -31307,6 +32169,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d converterConfig.channelsOut = pDevice->capture.channels; converterConfig.sampleRateOut = pDevice->sampleRate; ma_channel_map_copy(converterConfig.channelMapOut, pDevice->capture.channelMap, ma_min(pDevice->capture.channels, MA_MAX_CHANNELS)); + converterConfig.channelMixMode = pDevice->capture.channelMixMode; converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; converterConfig.resampling.algorithm = pDevice->resampling.algorithm; converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; @@ -31329,6 +32192,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d converterConfig.channelsOut = pDevice->playback.internalChannels; converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; ma_channel_map_copy(converterConfig.channelMapOut, pDevice->playback.internalChannelMap, ma_min(pDevice->playback.internalChannels, MA_MAX_CHANNELS)); + converterConfig.channelMixMode = pDevice->playback.channelMixMode; converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; converterConfig.resampling.algorithm = pDevice->resampling.algorithm; converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; @@ -31392,6 +32256,14 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) */ MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STARTING); + /* If the device has a start callback, start it now. */ + if (pDevice->pContext->callbacks.onDeviceStart != NULL) { + ma_result result = pDevice->pContext->callbacks.onDeviceStart(pDevice); + if (result != MA_SUCCESS) { + pDevice->workResult = result; /* Failed to start the device. */ + } + } + /* Make sure the state is set appropriately. */ ma_device__set_state(pDevice, MA_STATE_STARTED); ma_event_signal(&pDevice->startEvent); @@ -31410,7 +32282,6 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "No main loop implementation.", MA_API_NOT_FOUND); } } - /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. Note that this @@ -31418,8 +32289,14 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) don't want to be doing this a second time. */ if (ma_device_get_state(pDevice) != MA_STATE_UNINITIALIZED) { - if (pDevice->pContext->onDeviceStop) { - pDevice->pContext->onDeviceStop(pDevice); + if (ma_context__is_using_new_callbacks(pDevice->pContext)) { + if (pDevice->pContext->callbacks.onDeviceStop != NULL) { + pDevice->pContext->callbacks.onDeviceStop(pDevice); + } + } else { + if (pDevice->pContext->onDeviceStop != NULL) { + pDevice->pContext->onDeviceStop(pDevice); + } } } @@ -31725,6 +32602,18 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC pContext->callbacks.onContextInit = ma_context_init__winmm; } break; #endif + #ifdef MA_HAS_JACK + case ma_backend_jack: + { + pContext->callbacks.onContextInit = ma_context_init__jack; + } break; + #endif + #ifdef MA_HAS_WEBAUDIO + case ma_backend_webaudio: + { + pContext->callbacks.onContextInit = ma_context_init__webaudio; + } break; + #endif #ifdef MA_HAS_CUSTOM case ma_backend_custom: { @@ -31732,11 +32621,18 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC pContext->callbacks = pConfig->custom; } break; #endif + #ifdef MA_HAS_NULL + case ma_backend_null: + { + pContext->callbacks.onContextInit = ma_context_init__null; + } break; + #endif default: break; } if (pContext->callbacks.onContextInit != NULL) { + ma_post_log_messagef(pContext, NULL, MA_LOG_LEVEL_VERBOSE, "Attempting to initialize %s backend...", ma_get_backend_name(backend)); result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks); } else { result = MA_NO_BACKEND; @@ -31764,61 +32660,69 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC #ifdef MA_HAS_ALSA case ma_backend_alsa: { + ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, "Attempting to initialize ALSA backend..."); result = ma_context_init__alsa(pConfig, pContext); } break; #endif #ifdef MA_HAS_PULSEAUDIO case ma_backend_pulseaudio: { + ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, "Attempting to initialize PulseAudio backend..."); result = ma_context_init__pulse(pConfig, pContext); } break; #endif #ifdef MA_HAS_JACK case ma_backend_jack: { - result = ma_context_init__jack(pConfig, pContext); + /*result = ma_context_init__jack(pConfig, pContext);*/ } break; #endif #ifdef MA_HAS_COREAUDIO case ma_backend_coreaudio: { + ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, "Attempting to initialize CoreAudio backend..."); result = ma_context_init__coreaudio(pConfig, pContext); } break; #endif #ifdef MA_HAS_SNDIO case ma_backend_sndio: { + ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, "Attempting to initialize sndio backend..."); result = ma_context_init__sndio(pConfig, pContext); } break; #endif #ifdef MA_HAS_AUDIO4 case ma_backend_audio4: { + ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, "Attempting to initialize audio(4) backend..."); result = ma_context_init__audio4(pConfig, pContext); } break; #endif #ifdef MA_HAS_OSS case ma_backend_oss: { + ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, "Attempting to initialize OSS backend..."); result = ma_context_init__oss(pConfig, pContext); } break; #endif #ifdef MA_HAS_AAUDIO case ma_backend_aaudio: { + ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, "Attempting to initialize AAudio backend..."); result = ma_context_init__aaudio(pConfig, pContext); } break; #endif #ifdef MA_HAS_OPENSL case ma_backend_opensl: { + ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, "Attempting to initialize OpenSL backend..."); result = ma_context_init__opensl(pConfig, pContext); } break; #endif #ifdef MA_HAS_WEBAUDIO case ma_backend_webaudio: { - result = ma_context_init__webaudio(pConfig, pContext); + /*result = ma_context_init__webaudio(pConfig, pContext);*/ } break; #endif #ifdef MA_HAS_CUSTOM @@ -31830,7 +32734,7 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC #ifdef MA_HAS_NULL case ma_backend_null: { - result = ma_context_init__null(pConfig, pContext); + /*result = ma_context_init__null(pConfig, pContext);*/ } break; #endif @@ -31859,6 +32763,8 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC pContext->backend = backend; return result; + } else { + ma_post_log_messagef(pContext, NULL, MA_LOG_LEVEL_VERBOSE, "Failed to initialize %s backend.", ma_get_backend_name(backend)); } } @@ -31945,7 +32851,7 @@ static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_ const ma_uint32 bufferExpansionCount = 2; const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount; - if (pContext->deviceInfoCapacity >= totalDeviceInfoCount) { + if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) { ma_uint32 oldCapacity = pContext->deviceInfoCapacity; ma_uint32 newCapacity = oldCapacity + bufferExpansionCount; ma_device_info* pNewInfos = (ma_device_info*)ma__realloc_from_callbacks(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, sizeof(*pContext->pDeviceInfos)*oldCapacity, &pContext->allocationCallbacks); @@ -32017,7 +32923,7 @@ MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** p } else { result = pContext->onEnumDevices(pContext, ma_context_get_devices__enum_callback, NULL); } - + if (result == MA_SUCCESS) { /* Playback devices. */ if (ppPlaybackDeviceInfos != NULL) { @@ -32090,7 +32996,7 @@ MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceInfo.maxChannels = 0; deviceInfo.minSampleRate = 0xFFFFFFFF; deviceInfo.maxSampleRate = 0; - + for (iNativeFormat = 0; iNativeFormat < deviceInfo.nativeDataFormatCount; iNativeFormat += 1) { /* Formats. */ if (deviceInfo.nativeDataFormats[iNativeFormat].format == ma_format_unknown) { @@ -32318,7 +33224,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC config.periodSizeInMilliseconds = (config.performanceProfile == ma_performance_profile_low_latency) ? MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY : MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; pDevice->usingDefaultBufferSize = MA_TRUE; } - + MA_ASSERT(config.capture.channels <= MA_MAX_CHANNELS); MA_ASSERT(config.playback.channels <= MA_MAX_CHANNELS); @@ -32328,15 +33234,18 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC pDevice->resampling.linear.lpfOrder = config.resampling.linear.lpfOrder; pDevice->resampling.speex.quality = config.resampling.speex.quality; - pDevice->capture.shareMode = config.capture.shareMode; - pDevice->capture.format = config.capture.format; - pDevice->capture.channels = config.capture.channels; + pDevice->capture.shareMode = config.capture.shareMode; + pDevice->capture.format = config.capture.format; + pDevice->capture.channels = config.capture.channels; ma_channel_map_copy(pDevice->capture.channelMap, config.capture.channelMap, config.capture.channels); - - pDevice->playback.shareMode = config.playback.shareMode; - pDevice->playback.format = config.playback.format; - pDevice->playback.channels = config.playback.channels; + pDevice->capture.channelMixMode = config.capture.channelMixMode; + + pDevice->playback.shareMode = config.playback.shareMode; + pDevice->playback.format = config.playback.format; + pDevice->playback.channels = config.playback.channels; ma_channel_map_copy(pDevice->playback.channelMap, config.playback.channelMap, config.playback.channels); + pDevice->playback.channelMixMode = config.playback.channelMixMode; + /* The internal format, channel count and sample rate can be modified by the backend. */ @@ -32349,7 +33258,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC pDevice->playback.internalChannels = pDevice->playback.channels; pDevice->playback.internalSampleRate = pDevice->sampleRate; ma_channel_map_copy(pDevice->playback.internalChannelMap, pDevice->playback.channelMap, pDevice->playback.channels); - + result = ma_mutex_init(&pDevice->lock); if (result != MA_SUCCESS) { return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create mutex.", result); @@ -32358,7 +33267,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC /* When the device is started, the worker thread is the one that does the actual startup of the backend device. We use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device. - + Each of these semaphores is released internally by the worker thread when the work is completed. The start semaphore is also used to wake up the worker thread. */ @@ -32399,10 +33308,6 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; descriptorPlayback.periodCount = pConfig->periods; - if (descriptorPlayback.periodSizeInMilliseconds == 0 && descriptorPlayback.periodSizeInFrames == 0) { - descriptorPlayback.periodSizeInMilliseconds = (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY : MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; - } - if (descriptorPlayback.periodCount == 0) { descriptorPlayback.periodCount = MA_DEFAULT_PERIODS; } @@ -32419,10 +33324,6 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; descriptorCapture.periodCount = pConfig->periods; - if (descriptorCapture.periodSizeInMilliseconds == 0 && descriptorCapture.periodSizeInFrames == 0) { - descriptorCapture.periodSizeInMilliseconds = (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY : MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; - } - if (descriptorCapture.periodCount == 0) { descriptorCapture.periodCount = MA_DEFAULT_PERIODS; } @@ -32521,7 +33422,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC return result; } } - + ma_device__post_init_setup(pDevice, pConfig->deviceType); @@ -32537,6 +33438,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC /* Wait for the worker thread to put the device into it's stopped state for real. */ ma_event_wait(&pDevice->stopEvent); + MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STOPPED); } else { /* If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done @@ -32612,7 +33514,7 @@ MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backen } else { allocationCallbacks = ma_allocation_callbacks_init_default(); } - + pContext = (ma_context*)ma__malloc_from_callbacks(sizeof(*pContext), &allocationCallbacks); if (pContext == NULL) { @@ -32682,7 +33584,7 @@ MA_API void ma_device_uninit(ma_device* pDevice) pDevice->pContext->onDeviceUninit(pDevice); } } - + ma_event_uninit(&pDevice->stopEvent); ma_event_uninit(&pDevice->startEvent); @@ -32715,7 +33617,6 @@ MA_API ma_result ma_device_start(ma_device* pDevice) return ma_post_error(pDevice, MA_LOG_LEVEL_WARNING, "ma_device_start() called when the device is already started.", MA_INVALID_OPERATION); /* Already started. Returning an error to let the application know because it probably means they're doing something wrong. */ } - result = MA_ERROR; ma_mutex_lock(&pDevice->lock); { /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ @@ -32738,7 +33639,7 @@ MA_API ma_result ma_device_start(ma_device* pDevice) result = MA_INVALID_OPERATION; } } - + if (result == MA_SUCCESS) { ma_device__set_state(pDevice, MA_STATE_STARTED); } @@ -32783,7 +33684,6 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) return ma_post_error(pDevice, MA_LOG_LEVEL_WARNING, "ma_device_stop() called when the device is already stopped.", MA_INVALID_OPERATION); /* Already stopped. Returning an error to let the application know because it probably means they're doing something wrong. */ } - result = MA_ERROR; ma_mutex_lock(&pDevice->lock); { /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ @@ -32810,20 +33710,7 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) ma_device__set_state(pDevice, MA_STATE_STOPPED); } else { - /* Synchronous backends. Devices can optionally have a stop operation here. */ - if (ma_context__is_using_new_callbacks(pDevice->pContext)) { - if (pDevice->pContext->callbacks.onDeviceStop != NULL) { - result = pDevice->pContext->callbacks.onDeviceStop(pDevice); - } else { - result = MA_SUCCESS; - } - } else { - if (pDevice->pContext->onDeviceStop != NULL) { - result = pDevice->pContext->onDeviceStop(pDevice); - } else { - result = MA_SUCCESS; - } - } + /* Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. */ /* We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be @@ -32849,7 +33736,7 @@ MA_API ma_uint32 ma_device_get_state(const ma_device* pDevice) return MA_STATE_UNINITIALIZED; } - return pDevice->state; + return c89atomic_load_32((ma_uint32*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ } MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) @@ -32862,7 +33749,7 @@ MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) return MA_INVALID_ARGS; } - pDevice->masterVolumeFactor = volume; + c89atomic_exchange_f32(&pDevice->masterVolumeFactor, volume); return MA_SUCCESS; } @@ -32878,7 +33765,7 @@ MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume) return MA_INVALID_ARGS; } - *pVolume = pDevice->masterVolumeFactor; + *pVolume = c89atomic_load_f32(&pDevice->masterVolumeFactor); return MA_SUCCESS; } @@ -32966,7 +33853,7 @@ MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate) { - return bufferSizeInMilliseconds * (sampleRate/1000); + return bufferSizeInMilliseconds * (sampleRate/1000); } MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels) @@ -33972,7 +34859,7 @@ static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma } else { x = 0x7FFFFFFF; } - + x = x >> 24; x = x + 128; dst_u8[i] = (ma_uint8)x; @@ -34342,7 +35229,7 @@ static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma } else { x = 0x7FFFFFFF; } - + x = x >> 24; x = x + 128; dst_u8[i] = (ma_uint8)x; @@ -34423,7 +35310,7 @@ static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, m } else { x = 0x7FFFFFFF; } - + x = x >> 16; dst_s16[i] = (ma_int16)x; } @@ -34814,7 +35701,7 @@ static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, m float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax); float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax); float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - + float x0 = src_f32[i+0]; float x1 = src_f32[i+1]; float x2 = src_f32[i+2]; @@ -34932,7 +35819,7 @@ static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uin x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f)); _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1))); - + i += 8; } @@ -35521,7 +36408,7 @@ MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_ } } } break; - + case ma_format_f32: { const float* pSrcF32 = (const float*)pInterleavedPCMFrames; @@ -35534,7 +36421,7 @@ MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_ } } } break; - + default: { ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); @@ -35567,7 +36454,7 @@ MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_ui } } } break; - + case ma_format_f32: { float* pDstF32 = (float*)pInterleavedPCMFrames; @@ -35580,7 +36467,7 @@ MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_ui } } } break; - + default: { ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); @@ -35703,7 +36590,7 @@ static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed( const float b2 = pBQ->b2.f32; const float a1 = pBQ->a1.f32; const float a2 = pBQ->a2.f32; - + for (c = 0; c < pBQ->channels; c += 1) { float r1 = pBQ->r1[c].f32; float r2 = pBQ->r2[c].f32; @@ -35733,7 +36620,7 @@ static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed( const ma_int32 b2 = pBQ->b2.s32; const ma_int32 a1 = pBQ->a1.s32; const ma_int32 a2 = pBQ->a2.s32; - + for (c = 0; c < pBQ->channels; c += 1) { ma_int32 r1 = pBQ->r1[c].s32; ma_int32 r2 = pBQ->r2[c].s32; @@ -35809,7 +36696,7 @@ Low-Pass Filter MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) { ma_lpf1_config config; - + MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; @@ -35823,7 +36710,7 @@ MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) { ma_lpf2_config config; - + MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; @@ -35900,7 +36787,7 @@ static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, co ma_uint32 c; const float a = pLPF->a.f32; const float b = 1 - a; - + for (c = 0; c < pLPF->channels; c += 1) { float r1 = pLPF->r1[c].f32; float x = pX[c]; @@ -35918,7 +36805,7 @@ static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, ma_uint32 c; const ma_int32 a = pLPF->a.s32; const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - + for (c = 0; c < pLPF->channels; c += 1) { ma_int32 r1 = pLPF->r1[c].s32; ma_int32 x = pX[c]; @@ -36317,7 +37204,7 @@ High-Pass Filtering MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) { ma_hpf1_config config; - + MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; @@ -36330,7 +37217,7 @@ MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) { ma_hpf2_config config; - + MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; @@ -36407,7 +37294,7 @@ static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, co ma_uint32 c; const float a = 1 - pHPF->a.f32; const float b = 1 - a; - + for (c = 0; c < pHPF->channels; c += 1) { float r1 = pHPF->r1[c].f32; float x = pX[c]; @@ -36425,7 +37312,7 @@ static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, ma_uint32 c; const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32); const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - + for (c = 0; c < pHPF->channels; c += 1) { ma_int32 r1 = pHPF->r1[c].s32; ma_int32 x = pX[c]; @@ -36806,7 +37693,7 @@ Band-Pass Filtering MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) { ma_bpf2_config config; - + MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; @@ -37755,7 +38642,7 @@ static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma b = x * ((1<> shift); } @@ -38149,7 +39036,7 @@ MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResamp } MA_ASSERT(n != 0); - + return ma_linear_resampler_set_rate(pResampler, n, d); } @@ -38182,7 +39069,7 @@ MA_API ma_uint64 ma_linear_resampler_get_expected_output_frame_count(ma_linear_r ma_uint64 outputFrameCount; ma_uint64 preliminaryInputFrameCountFromFrac; ma_uint64 preliminaryInputFrameCount; - + if (pResampler == NULL) { return 0; } @@ -38449,7 +39336,7 @@ static ma_result ma_resampler_process_pcm_frames__read(ma_resampler* pResampler, break; #endif } - + default: break; } @@ -38648,7 +39535,7 @@ MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float rat } MA_ASSERT(n != 0); - + return ma_resampler_set_rate(pResampler, n, d); } } @@ -38869,34 +39756,34 @@ static float ma_calculate_channel_position_rectangular_weight(ma_channel channel /* Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to the following output configuration: - + - front/left - side/left - back/left - + The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. - + Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works across 3 spatial dimensions. - + The first thing to do is figure out how each speaker's volume is spread over each of plane: - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane - side/left: 1 plane (left only) = 1/1 = entire volume from left plane - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane - + The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be taken by the other to produce the final contribution. */ /* Contribution = Sum(Volume to Give * Volume to Take) */ - float contribution = + float contribution = g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] + g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] + g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] + @@ -38992,7 +39879,7 @@ MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pC } } } - + /* If the input and output channels and channel maps are the same we should use a passthrough. */ @@ -39034,7 +39921,7 @@ MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pC /* Here is where we do a bit of pre-processing to know how each channel should be combined to make up the output. Rules: - + 1) If it's a passthrough, do nothing - it's just a simple memcpy(). 2) If the channel counts are the same and every channel position in the input map is present in the output map, use a simple shuffle. An example might be different 5.1 channel layouts. @@ -39081,7 +39968,7 @@ MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pC /* Here is where weights are calculated. Note that we calculate the weights at all times, even when using a passthrough and simple shuffling. We use different algorithms for calculating weights depending on our mixing mode. - + In simple mode we don't do any blending (except for converting between mono, which is done in a later step). Instead we just map 1:1 matching channels. In this mode, if no channels in the input channel map correspond to anything in the output channel map, nothing will be heard! @@ -39229,8 +40116,24 @@ MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pC } } break; - case ma_channel_mix_mode_custom_weights: case ma_channel_mix_mode_simple: + { + /* In simple mode, excess channels need to be silenced or dropped. */ + ma_uint32 iChannel; + for (iChannel = 0; iChannel < ma_min(pConverter->channelsIn, pConverter->channelsOut); iChannel += 1) { + if (pConverter->format == ma_format_f32) { + if (pConverter->weights.f32[iChannel][iChannel] == 0) { + pConverter->weights.f32[iChannel][iChannel] = 1; + } + } else { + if (pConverter->weights.s16[iChannel][iChannel] == 0) { + pConverter->weights.s16[iChannel][iChannel] = ma_channel_converter_float_to_fixed(1); + } + } + } + } break; + + case ma_channel_mix_mode_custom_weights: default: { /* Fallthrough. */ @@ -39332,7 +40235,7 @@ static ma_result ma_channel_converter_process_pcm_frames__simple_shuffle(ma_chan pFramesInS32 += pConverter->channelsIn; } } break; - + case ma_format_f32: { /* */ float* pFramesOutF32 = ( float*)pFramesOut; @@ -39427,7 +40330,7 @@ static ma_result ma_channel_converter_process_pcm_frames__simple_mono_expansion( } } } break; - + case ma_format_f32: { /* */ float* pFramesOutF32 = ( float*)pFramesOut; @@ -39507,7 +40410,7 @@ static ma_result ma_channel_converter_process_pcm_frames__stereo_to_mono(ma_chan pFramesOutS32[iFrame] = (ma_int16)(((ma_int32)pFramesInS32[iFrame*2+0] + (ma_int32)pFramesInS32[iFrame*2+1]) / 2); } } break; - + case ma_format_f32: { /* */ float* pFramesOutF32 = ( float*)pFramesOut; @@ -39609,7 +40512,7 @@ static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_con } } } break; - + case ma_format_f32: { /* */ float* pFramesOutF32 = ( float*)pFramesOut; @@ -39692,7 +40595,7 @@ MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn config.channelsOut = ma_min(channelsOut, MA_MAX_CHANNELS); config.sampleRateIn = sampleRateIn; config.sampleRateOut = sampleRateOut; - + return config; } @@ -39740,14 +40643,14 @@ MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_channel_converter_config channelConverterConfig; channelConverterConfig = ma_channel_converter_config_init(midFormat, pConverter->config.channelsIn, pConverter->config.channelMapIn, pConverter->config.channelsOut, pConverter->config.channelMapOut, pConverter->config.channelMixMode); - + /* Channel weights. */ for (iChannelIn = 0; iChannelIn < pConverter->config.channelsIn; iChannelIn += 1) { for (iChannelOut = 0; iChannelOut < pConverter->config.channelsOut; iChannelOut += 1) { channelConverterConfig.weights[iChannelIn][iChannelOut] = pConverter->config.channelWeights[iChannelIn][iChannelOut]; } } - + result = ma_channel_converter_init(&channelConverterConfig, &pConverter->channelConverter); if (result != MA_SUCCESS) { return result; @@ -39842,7 +40745,7 @@ static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_conve ma_uint64 frameCount; MA_ASSERT(pConverter != NULL); - + frameCountIn = 0; if (pFrameCountIn != NULL) { frameCountIn = *pFrameCountIn; @@ -39880,7 +40783,7 @@ static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_conve ma_uint64 frameCount; MA_ASSERT(pConverter != NULL); - + frameCountIn = 0; if (pFrameCountIn != NULL) { frameCountIn = *pFrameCountIn; @@ -40452,7 +41355,7 @@ static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_co if (pFrameCountOut != NULL) { *pFrameCountOut = framesProcessedOut; } - + return MA_SUCCESS; } @@ -40590,6 +41493,15 @@ MA_API ma_uint64 ma_data_converter_get_output_latency(ma_data_converter* pConver Channel Maps **************************************************************************************************************************************************************/ +MA_API void ma_channel_map_init_blank(ma_uint32 channels, ma_channel* pChannelMap) +{ + if (pChannelMap == NULL) { + return; + } + + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels); +} + static void ma_get_standard_channel_map_microsoft(ma_uint32 channels, ma_channel* pChannelMap) { /* Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */ @@ -41191,7 +42103,7 @@ MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelM { ma_get_standard_channel_map_sound4(channels, pChannelMap); } break; - + case ma_standard_channel_map_sndio: { ma_get_standard_channel_map_sndio(channels, pChannelMap); @@ -41357,13 +42269,13 @@ static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffs static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) { MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(pRB->encodedReadOffset)); + return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedReadOffset))); } static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) { MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(pRB->encodedWriteOffset)); + return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedWriteOffset))); } static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) @@ -41456,8 +42368,8 @@ MA_API void ma_rb_reset(ma_rb* pRB) return; } - pRB->encodedReadOffset = 0; - pRB->encodedWriteOffset = 0; + c89atomic_exchange_32(&pRB->encodedReadOffset, 0); + c89atomic_exchange_32(&pRB->encodedWriteOffset, 0); } MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) @@ -41476,10 +42388,10 @@ MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppB } /* The returned buffer should never move ahead of the write pointer. */ - writeOffset = pRB->encodedWriteOffset; + writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - readOffset = pRB->encodedReadOffset; + readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); /* @@ -41520,7 +42432,7 @@ MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBuffer return MA_INVALID_ARGS; } - readOffset = pRB->encodedReadOffset; + readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ @@ -41556,10 +42468,10 @@ MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** pp } /* The returned buffer should never overtake the read buffer. */ - readOffset = pRB->encodedReadOffset; + readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = pRB->encodedWriteOffset; + writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); /* @@ -41606,7 +42518,7 @@ MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBuffe return MA_INVALID_ARGS; } - writeOffset = pRB->encodedWriteOffset; + writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ @@ -41641,13 +42553,12 @@ MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) return MA_INVALID_ARGS; } - readOffset = pRB->encodedReadOffset; + readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = pRB->encodedWriteOffset; + writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - newReadOffsetInBytes = readOffsetInBytes; newReadOffsetLoopFlag = readOffsetLoopFlag; /* We cannot go past the write buffer. */ @@ -41686,13 +42597,12 @@ MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) return MA_INVALID_ARGS; } - readOffset = pRB->encodedReadOffset; + readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = pRB->encodedWriteOffset; + writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - newWriteOffsetInBytes = writeOffsetInBytes; newWriteOffsetLoopFlag = writeOffsetLoopFlag; /* We cannot go past the write buffer. */ @@ -41729,10 +42639,10 @@ MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) return 0; } - readOffset = pRB->encodedReadOffset; + readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = pRB->encodedWriteOffset; + writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); if (readOffsetLoopFlag == writeOffsetLoopFlag) { @@ -42252,7 +43162,7 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi /* If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is - not necessarily considered an error. + not necessarily considered an error. */ if (result != MA_SUCCESS && result != MA_AT_END) { break; @@ -42325,6 +43235,18 @@ MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_ ma_uint32 sampleRate; ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource; + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + + if (pChannels != NULL) { + *pChannels = 0; + } + + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pCallbacks == NULL || pCallbacks->onGetDataFormat == NULL) { return MA_INVALID_ARGS; } @@ -42613,7 +43535,7 @@ MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer) MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) { ma_uint64 totalFramesRead = 0; - + if (pAudioBuffer == NULL) { return 0; } @@ -42633,7 +43555,7 @@ MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, } if (pFramesOut != NULL) { - ma_copy_pcm_frames(pFramesOut, ma_offset_ptr(pAudioBuffer->pData, pAudioBuffer->cursor * ma_get_bytes_per_frame(pAudioBuffer->format, pAudioBuffer->channels)), frameCount, pAudioBuffer->format, pAudioBuffer->channels); + ma_copy_pcm_frames(pFramesOut, ma_offset_ptr(pAudioBuffer->pData, pAudioBuffer->cursor * ma_get_bytes_per_frame(pAudioBuffer->format, pAudioBuffer->channels)), framesToRead, pAudioBuffer->format, pAudioBuffer->channels); } totalFramesRead += framesToRead; @@ -43066,7 +43988,7 @@ static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void BOOL readResult; bytesRemaining = sizeInBytes - totalBytesRead; - if (bytesRemaining > 0xFFFFFFFF) { + if (bytesRemaining >= 0xFFFFFFFF) { bytesToRead = 0xFFFFFFFF; } else { bytesToRead = (DWORD)bytesRemaining; @@ -43111,7 +44033,7 @@ static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, con BOOL writeResult; bytesRemaining = sizeInBytes - totalBytesWritten; - if (bytesRemaining > 0xFFFFFFFF) { + if (bytesRemaining >= 0xFFFFFFFF) { bytesToWrite = 0xFFFFFFFF; } else { bytesToWrite = (DWORD)bytesRemaining; @@ -43152,7 +44074,7 @@ static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i dwMoveMethod = FILE_BEGIN; } -#if defined(_MSC_VER) && _MSC_VER <= 1200 +#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) /* No SetFilePointerEx() so restrict to 31 bits. */ if (origin > 0x7FFFFFFF) { return MA_OUT_OF_RANGE; @@ -43174,7 +44096,7 @@ static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i LARGE_INTEGER liZero; LARGE_INTEGER liTell; BOOL result; -#if defined(_MSC_VER) && _MSC_VER <= 1200 +#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) LONG tell; #endif @@ -43182,7 +44104,7 @@ static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i liZero.QuadPart = 0; -#if defined(_MSC_VER) && _MSC_VER <= 1200 +#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) result = SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); liTell.QuadPart = tell; #else @@ -43299,13 +44221,13 @@ static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void MA_ASSERT(pDst != NULL); (void)pVFS; - + result = fread(pDst, 1, sizeInBytes, (FILE*)file); if (pBytesRead != NULL) { *pBytesRead = result; } - + if (result != sizeInBytes) { if (feof((FILE*)file)) { return MA_END_OF_FILE; @@ -43346,7 +44268,7 @@ static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_i MA_ASSERT(file != NULL); (void)pVFS; - + #if defined(_WIN32) #if defined(_MSC_VER) && _MSC_VER > 1200 result = _fseeki64((FILE*)file, offset, origin); @@ -43476,6 +44398,10 @@ static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) { + if (pBytesRead != NULL) { + *pBytesRead = 0; + } + if (file == NULL || pDst == NULL) { return MA_INVALID_ARGS; } @@ -43489,6 +44415,10 @@ static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) { + if (pBytesWritten != NULL) { + *pBytesWritten = 0; + } + if (file == NULL || pSrc == NULL) { return MA_INVALID_ARGS; } @@ -43662,7 +44592,7 @@ extern "C" { #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) #define DRWAV_VERSION_MAJOR 0 #define DRWAV_VERSION_MINOR 12 -#define DRWAV_VERSION_REVISION 14 +#define DRWAV_VERSION_REVISION 16 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #include typedef signed char drwav_int8; @@ -44035,7 +44965,7 @@ extern "C" { #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) #define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 22 +#define DRFLAC_VERSION_REVISION 25 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include typedef signed char drflac_int8; @@ -44396,7 +45326,7 @@ extern "C" { #define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) #define DRMP3_VERSION_MAJOR 0 #define DRMP3_VERSION_MINOR 6 -#define DRMP3_VERSION_REVISION 19 +#define DRMP3_VERSION_REVISION 25 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #include typedef signed char drmp3_int8; @@ -44524,6 +45454,8 @@ typedef drmp3_int32 drmp3_result; #else #define DRMP3_INLINE inline __attribute__((always_inline)) #endif +#elif defined(__WATCOMC__) + #define DRMP3_INLINE __inline #else #define DRMP3_INLINE #endif @@ -44542,12 +45474,6 @@ typedef struct DRMP3_API void drmp3dec_init(drmp3dec *dec); DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info); DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples); -#ifndef DRMP3_DEFAULT_CHANNELS -#define DRMP3_DEFAULT_CHANNELS 2 -#endif -#ifndef DRMP3_DEFAULT_SAMPLE_RATE -#define DRMP3_DEFAULT_SAMPLE_RATE 44100 -#endif typedef enum { drmp3_seek_origin_start, @@ -44748,9 +45674,9 @@ static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_ MA_COPY_MEMORY(pDecoder->outputChannelMap, pConfig->channelMap, sizeof(pConfig->channelMap)); } - + converterConfig = ma_data_converter_config_init( - pDecoder->internalFormat, pDecoder->outputFormat, + pDecoder->internalFormat, pDecoder->outputFormat, pDecoder->internalChannels, pDecoder->outputChannels, pDecoder->internalSampleRate, pDecoder->outputSampleRate ); @@ -44844,6 +45770,8 @@ static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, MA_ASSERT(pConfig != NULL); MA_ASSERT(pDecoder != NULL); + (void)pConfig; + pWav = (drwav*)ma__malloc_from_callbacks(sizeof(*pWav), &pDecoder->allocationCallbacks); if (pWav == NULL) { return MA_OUT_OF_MEMORY; @@ -45105,6 +46033,8 @@ static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, MA_ASSERT(pConfig != NULL); MA_ASSERT(pDecoder != NULL); + (void)pConfig; + pMP3 = (drmp3*)ma__malloc_from_callbacks(sizeof(*pMP3), &pDecoder->allocationCallbacks); if (pMP3 == NULL) { return MA_OUT_OF_MEMORY; @@ -45183,7 +46113,7 @@ static ma_uint64 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, m for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < pDecoder->internalChannels; ++iChannel) { - pFramesOutF[iChannel] = pVorbis->ppPacketData[iChannel][pVorbis->framesConsumed+iFrame]; + pFramesOutF[iChannel] = pVorbis->ppPacketData[iChannel][pVorbis->framesConsumed+iFrame]; } pFramesOutF += pDecoder->internalChannels; } @@ -45570,6 +46500,8 @@ static ma_result ma_decoder_init_raw__internal(const ma_decoder_config* pConfigI MA_ASSERT(pConfigOut != NULL); MA_ASSERT(pDecoder != NULL); + (void)pConfigOut; + pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__raw; pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__raw; pDecoder->onUninit = ma_decoder_internal_on_uninit__raw; @@ -46268,7 +47200,7 @@ static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* e ext1 = extension; ext2 = ma_path_extension_w(path); -#if defined(_MSC_VER) || defined(__DMC__) +#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) return _wcsicmp(ext1, ext2) == 0; #else /* @@ -46309,7 +47241,7 @@ static size_t ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, si MA_ASSERT(pBufferOut != NULL); ma_vfs_or_default_read(pDecoder->backend.vfs.pVFS, pDecoder->backend.vfs.file, pBufferOut, bytesToRead, &bytesRead); - + return bytesRead; } @@ -46847,7 +47779,7 @@ MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO ma_uint64 totalFramesReadOut; ma_uint64 totalFramesReadIn; void* pRunningFramesOut; - + if (pDecoder == NULL) { return 0; } @@ -46871,7 +47803,7 @@ MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO totalFramesReadOut = 0; totalFramesReadIn = 0; pRunningFramesOut = pFramesOut; - + while (totalFramesReadOut < frameCount) { ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels); @@ -46895,6 +47827,8 @@ MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO if (requiredInputFrameCount > 0) { framesReadThisIterationIn = pDecoder->onReadPCMFrames(pDecoder, pIntermediaryBuffer, framesToReadThisIterationIn); totalFramesReadIn += framesReadThisIterationIn; + } else { + framesReadThisIterationIn = 0; } /* @@ -46989,7 +47923,7 @@ static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_dec void* pPCMFramesOut; MA_ASSERT(pDecoder != NULL); - + totalFrameCount = 0; bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); @@ -47036,7 +47970,7 @@ static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_dec } } - + if (pConfigOut != NULL) { pConfigOut->format = pDecoder->outputFormat; pConfigOut->channels = pDecoder->outputChannels; @@ -47106,7 +48040,7 @@ MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder } config = ma_decoder_config_init_copy(pConfig); - + result = ma_decoder_init_memory(pData, dataSize, &config, &decoder); if (result != MA_SUCCESS) { return result; @@ -47163,7 +48097,7 @@ static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc; allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; - + if (!drwav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { return MA_ERROR; } @@ -47440,6 +48374,16 @@ static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSou return MA_SUCCESS; } +static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency) +{ + return (1.0 / (sampleRate / frequency)); +} + +static void ma_waveform__update_advance(ma_waveform* pWaveform) +{ + pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); +} + MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform) { if (pWaveform == NULL) { @@ -47453,7 +48397,7 @@ MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform pWaveform->ds.onGetCursor = ma_waveform__data_source_on_get_cursor; pWaveform->ds.onGetLength = NULL; /* Intentionally set to NULL since there's no notion of a length in waveforms. */ pWaveform->config = *pConfig; - pWaveform->advance = 1.0 / pWaveform->config.sampleRate; + pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); pWaveform->time = 0; return MA_SUCCESS; @@ -47476,6 +48420,18 @@ MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double freque } pWaveform->config.frequency = frequency; + ma_waveform__update_advance(pWaveform); + + return MA_SUCCESS; +} + +MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.type = type; return MA_SUCCESS; } @@ -47485,26 +48441,27 @@ MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 s return MA_INVALID_ARGS; } - pWaveform->advance = 1.0 / sampleRate; + pWaveform->config.sampleRate = sampleRate; + ma_waveform__update_advance(pWaveform); + return MA_SUCCESS; } -static float ma_waveform_sine_f32(double time, double frequency, double amplitude) +static float ma_waveform_sine_f32(double time, double amplitude) { - return (float)(ma_sin(MA_TAU_D * time * frequency) * amplitude); + return (float)(ma_sin(MA_TAU_D * time) * amplitude); } -static ma_int16 ma_waveform_sine_s16(double time, double frequency, double amplitude) +static ma_int16 ma_waveform_sine_s16(double time, double amplitude) { - return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, frequency, amplitude)); + return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); } -static float ma_waveform_square_f32(double time, double frequency, double amplitude) +static float ma_waveform_square_f32(double time, double amplitude) { - double t = time * frequency; - double f = t - (ma_int64)t; + double f = time - (ma_int64)time; double r; - + if (f < 0.5) { r = amplitude; } else { @@ -47514,15 +48471,14 @@ static float ma_waveform_square_f32(double time, double frequency, double amplit return (float)r; } -static ma_int16 ma_waveform_square_s16(double time, double frequency, double amplitude) +static ma_int16 ma_waveform_square_s16(double time, double amplitude) { - return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, frequency, amplitude)); + return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, amplitude)); } -static float ma_waveform_triangle_f32(double time, double frequency, double amplitude) +static float ma_waveform_triangle_f32(double time, double amplitude) { - double t = time * frequency; - double f = t - (ma_int64)t; + double f = time - (ma_int64)time; double r; r = 2 * ma_abs(2 * (f - 0.5)) - 1; @@ -47530,15 +48486,14 @@ static float ma_waveform_triangle_f32(double time, double frequency, double ampl return (float)(r * amplitude); } -static ma_int16 ma_waveform_triangle_s16(double time, double frequency, double amplitude) +static ma_int16 ma_waveform_triangle_s16(double time, double amplitude) { - return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, frequency, amplitude)); + return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude)); } -static float ma_waveform_sawtooth_f32(double time, double frequency, double amplitude) +static float ma_waveform_sawtooth_f32(double time, double amplitude) { - double t = time * frequency; - double f = t - (ma_int64)t; + double f = time - (ma_int64)time; double r; r = 2 * (f - 0.5); @@ -47546,9 +48501,9 @@ static float ma_waveform_sawtooth_f32(double time, double frequency, double ampl return (float)(r * amplitude); } -static ma_int16 ma_waveform_sawtooth_s16(double time, double frequency, double amplitude) +static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude) { - return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, frequency, amplitude)); + return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude)); } static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) @@ -47564,7 +48519,7 @@ static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFra if (pWaveform->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude); + float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -47574,7 +48529,7 @@ static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFra } else if (pWaveform->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude); + ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -47583,7 +48538,7 @@ static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFra } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude); + float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -47606,7 +48561,7 @@ static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pF if (pWaveform->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude); + float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -47616,7 +48571,7 @@ static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pF } else if (pWaveform->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_square_s16(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude); + ma_int16 s = ma_waveform_square_s16(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -47625,7 +48580,7 @@ static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pF } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude); + float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -47648,7 +48603,7 @@ static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* if (pWaveform->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude); + float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -47658,7 +48613,7 @@ static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* } else if (pWaveform->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude); + ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -47667,7 +48622,7 @@ static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude); + float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -47690,7 +48645,7 @@ static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* if (pWaveform->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude); + float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -47700,7 +48655,7 @@ static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* } else if (pWaveform->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude); + ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -47709,7 +48664,7 @@ static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude); + float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -47791,7 +48746,7 @@ MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_uint64 framesRead = ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount); - + if (pFramesRead != NULL) { *pFramesRead = framesRead; } @@ -47864,6 +48819,37 @@ MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise) return MA_SUCCESS; } +MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude) +{ + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + pNoise->config.amplitude = amplitude; + return MA_SUCCESS; +} + +MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed) +{ + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + pNoise->lcg.state = seed; + return MA_SUCCESS; +} + + +MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) +{ + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + pNoise->config.type = type; + return MA_SUCCESS; +} + static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise) { return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude); @@ -47914,7 +48900,7 @@ static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, voi } else { ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); ma_uint32 bpf = bps * pNoise->config.channels; - + if (pNoise->config.duplicateChannels) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_noise_f32_white(pNoise); @@ -48031,7 +49017,7 @@ static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void } else { ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); ma_uint32 bpf = bps * pNoise->config.channels; - + if (pNoise->config.duplicateChannels) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_noise_f32_pink(pNoise, 0); @@ -48056,7 +49042,7 @@ static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel) { double result; - + result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]); result /= 1.005; /* Don't escape the -1..1 range on average. */ @@ -48111,7 +49097,7 @@ static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, } else { ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); ma_uint32 bpf = bps * pNoise->config.channels; - + if (pNoise->config.duplicateChannels) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_noise_f32_brownian(pNoise, 0); @@ -48228,6 +49214,8 @@ code below please report the bug to the respective repository for the relevant p #else #define DRWAV_INLINE inline __attribute__((always_inline)) #endif +#elif defined(__WATCOMC__) + #define DRWAV_INLINE __inline #else #define DRWAV_INLINE #endif @@ -48292,7 +49280,6 @@ DRWAV_API const char* drwav_version_string(void) #endif static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; @@ -49818,7 +50805,7 @@ static drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char return DRWAV_SUCCESS; } #if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || !defined(__STRICT_ANSI__) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) #define DRWAV_HAS_WFOPEN #endif #endif @@ -50255,7 +51242,7 @@ DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 frames } bytesToRead = framesToRead * bytesPerFrame; if (bytesToRead > DRWAV_SIZE_MAX) { - framesToRead = DRWAV_SIZE_MAX / bytesPerFrame; + bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame; } if (bytesToRead == 0) { return 0; @@ -51999,6 +52986,8 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) #else #define DRFLAC_INLINE inline __attribute__((always_inline)) #endif +#elif defined(__WATCOMC__) + #define DRFLAC_INLINE __inline #else #define DRFLAC_INLINE #endif @@ -52006,7 +52995,7 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) #define DRFLAC_X64 #elif defined(__i386) || defined(_M_IX86) #define DRFLAC_X86 -#elif defined(__arm__) || defined(_M_ARM) +#elif defined(__arm__) || defined(_M_ARM) || defined(_M_ARM64) #define DRFLAC_ARM #endif #if !defined(DR_FLAC_NO_SIMD) @@ -57352,7 +58341,7 @@ static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const ch return DRFLAC_SUCCESS; } #if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || !defined(__STRICT_ANSI__) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) #define DRFLAC_HAS_WFOPEN #endif #endif @@ -60186,7 +61175,7 @@ DRMP3_API const char* drmp3_version_string(void) #define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) #define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) #if !defined(DR_MP3_NO_SIMD) -#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(_M_ARM64) || defined(__x86_64__) || defined(__aarch64__)) +#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) #define DR_MP3_ONLY_SIMD #endif #if ((defined(_MSC_VER) && _MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__)) @@ -60259,7 +61248,7 @@ end: return g_have_simd - 1; #endif } -#elif defined(__ARM_NEON) || defined(__aarch64__) +#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) #include #define DRMP3_HAVE_SSE 0 #define DRMP3_HAVE_SIMD 1 @@ -60288,7 +61277,7 @@ static int drmp3_have_simd(void) #else #define DRMP3_HAVE_SIMD 0 #endif -#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) +#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) #define DRMP3_HAVE_ARMV6 1 static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(int32_t a) { @@ -62151,7 +63140,7 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm return DRMP3_FALSE; } if (!drmp3_decode_next_frame(pMP3)) { - drmp3_uninit(pMP3); + drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); return DRMP3_FALSE; } pMP3->channels = pMP3->mp3FrameChannels; @@ -62662,7 +63651,7 @@ static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char return DRMP3_SUCCESS; } #if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || !defined(__STRICT_ANSI__) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) #define DRMP3_HAS_WFOPEN #endif #endif @@ -62738,19 +63727,31 @@ static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek } DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) { + drmp3_bool32 result; FILE* pFile; if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) { return DRMP3_FALSE; } - return drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != DRMP3_TRUE) { + fclose(pFile); + return result; + } + return DRMP3_TRUE; } DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) { + drmp3_bool32 result; FILE* pFile; if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) { return DRMP3_FALSE; } - return drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != DRMP3_TRUE) { + fclose(pFile); + return result; + } + return DRMP3_TRUE; } #endif DRMP3_API void drmp3_uninit(drmp3* pMP3) @@ -62760,7 +63761,11 @@ DRMP3_API void drmp3_uninit(drmp3* pMP3) } #ifndef DR_MP3_NO_STDIO if (pMP3->onRead == drmp3__on_read_stdio) { - fclose((FILE*)pMP3->pUserData); + FILE* pFile = (FILE*)pMP3->pUserData; + if (pFile != NULL) { + fclose(pFile); + pMP3->pUserData = NULL; + } } #endif drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); @@ -63679,6 +64684,39 @@ The following miscellaneous changes have also been made. /* REVISION HISTORY ================ +v0.10.30 - 2021-01-10 + - Fix a crash in ma_audio_buffer_read_pcm_frames(). + - Update spinlock APIs to take a volatile parameter as input. + - Silence some unused parameter warnings. + - Fix a warning on GCC when compiling as C++. + +v0.10.29 - 2020-12-26 + - Fix some subtle multi-threading bugs on non-x86 platforms. + - Fix a bug resulting in superfluous memory allocations when enumerating devices. + - Core Audio: Fix a compilation error when compiling for iOS. + +v0.10.28 - 2020-12-16 + - Fix a crash when initializing a POSIX thread. + - OpenSL|ES: Respect the MA_NO_RUNTIME_LINKING option. + +v0.10.27 - 2020-12-04 + - Add support for dynamically configuring some properties of `ma_noise` objects post-initialization. + - Add support for configuring the channel mixing mode in the device config. + - Fix a bug with simple channel mixing mode (drop or silence excess channels). + - Fix some bugs with trying to access uninitialized variables. + - Fix some errors with stopping devices for synchronous backends where the backend's stop callback would get fired twice. + - Fix a bug in the decoder due to using an uninitialized variable. + - Fix some data race errors. + +v0.10.26 - 2020-11-24 + - WASAPI: Fix a bug where the exclusive mode format may not be retrieved correctly due to accessing freed memory. + - Fix a bug with ma_waveform where glitching occurs after changing frequency. + - Fix compilation with OpenWatcom. + - Fix compilation with TCC. + - Fix compilation with Digital Mars. + - Fix compilation warnings. + - Remove bitfields from public structures to aid in binding maintenance. + v0.10.25 - 2020-11-15 - PulseAudio: Fix a bug where the stop callback isn't fired. - WebAudio: Fix an error that occurs when Emscripten increases the size of it's heap. From 3d22709808fd9ba2843e6cad5e106bcd90eecaca Mon Sep 17 00:00:00 2001 From: Davidson Francis Date: Wed, 13 Jan 2021 19:07:34 -0300 Subject: [PATCH 12/23] Fixes Android builds on Linux environments (#1530) * simple_game: Configure Make and makefile for Linux environments * simple_game: Fix build issues on Android plaform with Linux HOST The Makefile.Android.linux file was out of date with the rest of the project, so this commit updates the paths, as well as leaving some as optional, if it is already configured as an environment variable. In addition, it corrects the build error related to static raylib: the makefile was trying to generate libmain.so using the path of the NDK libraries, instead of using those of the Android system, which resulted in crashes in the generated apk. --- templates/simple_game/Makefile | 18 ++++++++++++++++++ templates/simple_game/Makefile.Android.linux | 13 ++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/templates/simple_game/Makefile b/templates/simple_game/Makefile index b5b2032cd..e612ee403 100644 --- a/templates/simple_game/Makefile +++ b/templates/simple_game/Makefile @@ -101,6 +101,15 @@ ifeq ($(PLATFORM),PLATFORM_DRM) PLATFORM_OS=LINUX endif endif +ifeq ($(PLATFORM),PLATFORM_ANDROID) + UNAMEOS=$(shell uname) + ifeq ($(UNAMEOS),Linux) + PLATFORM_OS=LINUX + endif + ifeq ($(UNAMEOS),Darwin) + PLATFORM_OS=OSX + endif +endif # RAYLIB_PATH adjustment for different platforms. # If using GNU make, we can get the full path to the top of the tree. Windows? BSD? @@ -388,6 +397,15 @@ OBJS = $(patsubst %.c, %.o, $(PROJECT_SOURCE_FILES)) # For Android platform we call a custom Makefile.Android ifeq ($(PLATFORM),PLATFORM_ANDROID) MAKEFILE_PARAMS = -f Makefile.Android + # For Linux and macOS set make and makefile + ifeq ($(PLATFORM_OS),LINUX) + MAKE = make + MAKEFILE_PARAMS = -f Makefile.Android.linux + endif + ifeq ($(PLATFORM_OS),OSX) + MAKE = make + MAKEFILE_PARAMS = -f Makefile.Android.macos + endif export PROJECT_NAME export PROJECT_SOURCE_FILES else diff --git a/templates/simple_game/Makefile.Android.linux b/templates/simple_game/Makefile.Android.linux index fd4dfacd4..1fbdc3146 100644 --- a/templates/simple_game/Makefile.Android.linux +++ b/templates/simple_game/Makefile.Android.linux @@ -28,7 +28,7 @@ # Define required raylib variables PLATFORM ?= PLATFORM_ANDROID -RAYLIB_PATH ?= $(HOME)/raylib_sources.ln +RAYLIB_PATH ?= ../../ # Define Android architecture (armeabi-v7a, arm64-v8a, x86, x86-64) and API version ANDROID_ARCH ?= ARM64 @@ -48,9 +48,9 @@ endif # no need to define JAVA_HOME , JAVA_BIN in linux environment (binaries are in $PATH) #JAVA_HOME ?= C:/JavaJDK/ #JAVA_BIN ?= $(JAVA_HOME)/bin/ -ANDROID_HOME = /opt/android-sdk +ANDROID_HOME ?= /opt/android-sdk #ANDROID_TOOLCHAIN = C:/android_toolchain_$(ANDROID_ARCH)_API$(ANDROID_API_VERSION) -ANDROID_TOOLCHAIN = /opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/ +ANDROID_TOOLCHAIN ?= /opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/ # find the highest version available of build tools ANDROID_BUILD_TOOLS = $(shell ls -1d $(ANDROID_HOME)/build-tools/* | tail -n 1)/ @@ -94,11 +94,10 @@ APP_SCREEN_ORIENTATION ?= landscape APP_KEYSTORE_PASS ?= raylib # Library type used for raylib: STATIC (.a) or SHARED (.so/.dll) -# attention, ne fonctionne pas avec STATIC (manque la libm quelque part) -RAYLIB_LIBTYPE ?= SHARED +RAYLIB_LIBTYPE ?= STATIC # Library path for libraylib.a/libraylib.so -RAYLIB_LIB_PATH = $(RAYLIB_PATH)/src/android/ +RAYLIB_LIB_PATH = $(RAYLIB_PATH)/src/ # Shared libs must be added to APK if required # NOTE: Generated NativeLoader.java automatically load those libraries @@ -141,7 +140,7 @@ LDFLAGS += -Wl,--build-id -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl # Force linking of library module to define symbol LDFLAGS += -u ANativeActivity_onCreate # Library paths containing required libs -LDFLAGS += -L. -L$(PROJECT_BUILD_PATH)/obj -L$(PROJECT_BUILD_PATH)/lib/$(ANDROID_ARCH_NAME) -L$(ANDROID_TOOLCHAIN)/sysroot/usr/lib/$(ANDROID_TOOLCHAIN_LIBS) +LDFLAGS += -L. -L$(PROJECT_BUILD_PATH)/obj -L$(PROJECT_BUILD_PATH)/lib/$(ANDROID_ARCH_NAME) # Define any libraries to link into executable # if you want to link libraries (libname.so or libname.a), use the -lname From 9821725c6bafeaf4153ab8604e692dfe13cbff09 Mon Sep 17 00:00:00 2001 From: hristo Date: Thu, 14 Jan 2021 00:10:02 +0200 Subject: [PATCH 13/23] Big cmake changes (#1514) * Delete emscripten.cmake This file is not needed at this point. EMSDK provides a toolchain file that has a lot more things in it and is better supported. Project currently works fine with the documentation provided in Emscripten SDK on how to build projects. * First pass file separation. The main two files are cleaner now. Only important things can be seen. Major changes include: - raylib_static is now the alias instead of raylib - Repeating segments are removed and pulled into separate files into /cmake - File is reordered to make more sense - Installs are better structured - Library is build into an output directory "raylib" instead of "src" - All public header files are now set as a public header file - Source files need to be listed (it is a bad practice to capture them using wildcards and file globs) - CMakeLists are better commented * Second pass on the example dirs. They are quite complex so I'm more hesitant to do major changes. Also it works pretty well. Noticed that I forgot one of the seperated files and added it into src/CMakeLists.txt. * Returned the header copy as it was convenient to have the public headers copied. * A better description to the variable RAYLIB_IS_MAIN Co-authored-by: Rob Loach * Remove debug message Co-authored-by: Rob Loach * Improvements based on review. * Simplify the install condition to not be platform specific as it was before. Co-authored-by: Alexander Neumann <30894796+Neumann-A@users.noreply.github.com> * Remove some CMAKE variables as they don't affect the build in any way Co-authored-by: Alexander Neumann <30894796+Neumann-A@users.noreply.github.com> Co-authored-by: Rob Loach Co-authored-by: Alexander Neumann <30894796+Neumann-A@users.noreply.github.com> --- CMakeLists.txt | 54 +--- src/CMakeOptions.txt => CMakeOptions.txt | 39 +-- cmake/BuildOptions.cmake | 39 +++ cmake/BuildType.cmake | 43 --- cmake/CompilerFlags.cmake | 33 ++ cmake/GlfwImport.cmake | 29 ++ cmake/InstallConfigurations.cmake | 29 ++ cmake/LibraryConfigurations.cmake | 109 +++++++ cmake/PackConfigurations.cmake | 13 + cmake/emscripten.cmake | 23 -- examples/CMakeLists.txt | 15 - src/CMakeLists.txt | 390 ++++++----------------- src/config.h | 2 +- 13 files changed, 369 insertions(+), 449 deletions(-) rename src/CMakeOptions.txt => CMakeOptions.txt (85%) create mode 100644 cmake/BuildOptions.cmake delete mode 100644 cmake/BuildType.cmake create mode 100644 cmake/CompilerFlags.cmake create mode 100644 cmake/GlfwImport.cmake create mode 100644 cmake/InstallConfigurations.cmake create mode 100644 cmake/LibraryConfigurations.cmake create mode 100644 cmake/PackConfigurations.cmake delete mode 100644 cmake/emscripten.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index e3fbd1177..8c5d2113c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,57 +1,27 @@ cmake_minimum_required(VERSION 3.0) +project(raylib) + +# Directory for easier includes set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +# RAYLIB_IS_MAIN determines whether the project is being used from root, or as a dependency. if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") set(RAYLIB_IS_MAIN TRUE) else() set(RAYLIB_IS_MAIN FALSE) endif() -# Config options -option(BUILD_EXAMPLES "Build the examples." ${RAYLIB_IS_MAIN}) -option(ENABLE_ASAN "Enable AddressSanitizer (ASAN) for debugging (degrades performance)" OFF) -option(ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer (UBSan) for debugging" OFF) -option(ENABLE_MSAN "Enable MemorySanitizer (MSan) for debugging (not recommended to run with ASAN)" OFF) +# Sets compiler flags and language standard +include(CompilerFlags) -# This helps support the case where emsdk toolchain file is used -# either by setting it with -DCMAKE_TOOLCHAIN_FILE=/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -# or by using "emcmake cmake -B build -S ." as described in https://emscripten.org/docs/compiling/Building-Projects.html -if(EMSCRIPTEN) - SET(PLATFORM Web CACHE STRING "Forcing PLATFORM_WEB because EMSCRIPTEN was detected") -endif() +# Registers build options that are exposed to cmake +include(CMakeOptions.txt) -if(CMAKE_VERSION VERSION_LESS "3.1") - if(CMAKE_C_COMPILER_ID STREQUAL "GNU") - set(CMAKE_C_FLAGS "-std=gnu99 ${CMAKE_C_FLAGS}") - endif() -else() - set (CMAKE_C_STANDARD 99) -endif() +# Checks a few environment and compiler configurations +include(BuildOptions) -include(AddIfFlagCompiles) -add_if_flag_compiles(-Werror=pointer-arith CMAKE_C_FLAGS) -add_if_flag_compiles(-Werror=implicit-function-declaration CMAKE_C_FLAGS) -# src/external/jar_xm.h does shady stuff -add_if_flag_compiles(-fno-strict-aliasing CMAKE_C_FLAGS) - -if (ENABLE_ASAN) - add_if_flag_compiles(-fno-omit-frame-pointer CMAKE_C_FLAGS CMAKE_LINKER_FLAGS) - add_if_flag_compiles(-fsanitize=address CMAKE_C_FLAGS CMAKE_LINKER_FLAGS) -endif() -if (ENABLE_UBSAN) - add_if_flag_compiles(-fno-omit-frame-pointer CMAKE_C_FLAGS CMAKE_LINKER_FLAGS) - add_if_flag_compiles(-fsanitize=undefined CMAKE_C_FLAGS CMAKE_LINKER_FLAGS) -endif() -if (ENABLE_MSAN) - add_if_flag_compiles(-fno-omit-frame-pointer CMAKE_C_FLAGS CMAKE_LINKER_FLAGS) - add_if_flag_compiles(-fsanitize=memory CMAKE_C_FLAGS CMAKE_LINKER_FLAGS) -endif() - -if (ENABLE_MSAN AND ENABLE_ASAN) - MESSAGE(WARNING "Compiling with both AddressSanitizer and MemorySanitizer is not recommended") -endif() - -add_subdirectory(src) +# Main sources directory (the second parameter sets the output directory name to raylib) +add_subdirectory(src raylib) if (${BUILD_EXAMPLES}) MESSAGE(STATUS "Building examples is enabled") diff --git a/src/CMakeOptions.txt b/CMakeOptions.txt similarity index 85% rename from src/CMakeOptions.txt rename to CMakeOptions.txt index 7eff4c89c..f39d17a1e 100644 --- a/src/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -6,6 +6,12 @@ enum_option(PLATFORM "Desktop;Web;Android;Raspberry Pi;DRM" "Platform to build f enum_option(OPENGL_VERSION "OFF;3.3;2.1;1.1;ES 2.0" "Force a specific OpenGL Version?") +# Configuration options +option(BUILD_EXAMPLES "Build the examples." ${RAYLIB_IS_MAIN}) +option(ENABLE_ASAN "Enable AddressSanitizer (ASAN) for debugging (degrades performance)" OFF) +option(ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer (UBSan) for debugging" OFF) +option(ENABLE_MSAN "Enable MemorySanitizer (MSan) for debugging (not recommended to run with ASAN)" OFF) + # Shared library is always PIC. Static library should be PIC too if linked into a shared library option(WITH_PIC "Compile static library as position-independent code" OFF) option(SHARED "Build raylib as a dynamic library" OFF) @@ -80,36 +86,3 @@ option(SUPPORT_FILEFORMAT_FLAC "Support loading FLAC for sound" ${OFF}) # utils.c option(SUPPORT_TRACELOG "Show TraceLog() output messages. NOTE: By default LOG_DEBUG traces not shown" ON) - -if(NOT (STATIC OR SHARED)) - message(FATAL_ERROR "Nothing to do if both -DSHARED=OFF and -DSTATIC=OFF...") -endif() - -if (DEFINED BUILD_SHARED_LIBS) - set(SHARED ${BUILD_SHARED_LIBS}) - if (${BUILD_SHARED_LIBS}) - set(STATIC OFF) - else() - set(STATIC ON) - endif() -endif() -if(DEFINED SHARED_RAYLIB) - set(SHARED ${SHARED_RAYLIB}) - message(DEPRECATION "-DSHARED_RAYLIB is deprecated. Please use -DSHARED instead.") -endif() -if(DEFINED STATIC_RAYLIB) - set(STATIC ${STATIC_RAYLIB}) - message(DEPRECATION "-DSTATIC_RAYLIB is deprecated. Please use -DSTATIC instead.") -endif() - -if(${PLATFORM} MATCHES "Desktop" AND APPLE) - if(MACOS_FATLIB) - if (CMAKE_OSX_ARCHITECTURES) - message(FATAL_ERROR "User supplied -DCMAKE_OSX_ARCHITECTURES overrides -DMACOS_FATLIB=ON") - else() - set(CMAKE_OSX_ARCHITECTURES "x86_64;i386") - endif() - endif() -endif() - -# vim: ft=cmake diff --git a/cmake/BuildOptions.cmake b/cmake/BuildOptions.cmake new file mode 100644 index 000000000..00586c23c --- /dev/null +++ b/cmake/BuildOptions.cmake @@ -0,0 +1,39 @@ +if(NOT (STATIC OR SHARED)) + message(FATAL_ERROR "Nothing to do if both -DSHARED=OFF and -DSTATIC=OFF...") +endif() + +if (DEFINED BUILD_SHARED_LIBS) + set(SHARED ${BUILD_SHARED_LIBS}) + if (${BUILD_SHARED_LIBS}) + set(STATIC OFF) + else() + set(STATIC ON) + endif() +endif() +if(DEFINED SHARED_RAYLIB) + set(SHARED ${SHARED_RAYLIB}) + message(DEPRECATION "-DSHARED_RAYLIB is deprecated. Please use -DSHARED instead.") +endif() +if(DEFINED STATIC_RAYLIB) + set(STATIC ${STATIC_RAYLIB}) + message(DEPRECATION "-DSTATIC_RAYLIB is deprecated. Please use -DSTATIC instead.") +endif() + +if(${PLATFORM} MATCHES "Desktop" AND APPLE) + if(MACOS_FATLIB) + if (CMAKE_OSX_ARCHITECTURES) + message(FATAL_ERROR "User supplied -DCMAKE_OSX_ARCHITECTURES overrides -DMACOS_FATLIB=ON") + else() + set(CMAKE_OSX_ARCHITECTURES "x86_64;i386") + endif() + endif() +endif() + +# This helps support the case where emsdk toolchain file is used +# either by setting it with -DCMAKE_TOOLCHAIN_FILE=/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake +# or by using "emcmake cmake -B build -S ." as described in https://emscripten.org/docs/compiling/Building-Projects.html +if(EMSCRIPTEN) + SET(PLATFORM Web CACHE STRING "Forcing PLATFORM_WEB because EMSCRIPTEN was detected") +endif() + +# vim: ft=cmake diff --git a/cmake/BuildType.cmake b/cmake/BuildType.cmake deleted file mode 100644 index 80ccdee2c..000000000 --- a/cmake/BuildType.cmake +++ /dev/null @@ -1,43 +0,0 @@ -# Set a default build type if none was specified -set(default_build_type "Release") -if(EXISTS "${CMAKE_SOURCE_DIR}/.git") - set(default_build_type "Debug") -endif() - -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to '${default_build_type}' as none was specified.") - set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE - STRING "Choose the type of build." FORCE) - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" - "MinSizeRel" "RelWithDebInfo") -endif() - -# Taken from the https://github.com/OpenChemistry/tomviz project -# Copyright (c) 2014-2017, Kitware, Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cmake/CompilerFlags.cmake b/cmake/CompilerFlags.cmake new file mode 100644 index 000000000..41fee6922 --- /dev/null +++ b/cmake/CompilerFlags.cmake @@ -0,0 +1,33 @@ +include(AddIfFlagCompiles) + +add_if_flag_compiles(-Werror=pointer-arith CMAKE_C_FLAGS) +add_if_flag_compiles(-Werror=implicit-function-declaration CMAKE_C_FLAGS) +# src/external/jar_xm.h does shady stuff +add_if_flag_compiles(-fno-strict-aliasing CMAKE_C_FLAGS) + +if (ENABLE_ASAN) + add_if_flag_compiles(-fno-omit-frame-pointer CMAKE_C_FLAGS CMAKE_LINKER_FLAGS) + add_if_flag_compiles(-fsanitize=address CMAKE_C_FLAGS CMAKE_LINKER_FLAGS) +endif() +if (ENABLE_UBSAN) + add_if_flag_compiles(-fno-omit-frame-pointer CMAKE_C_FLAGS CMAKE_LINKER_FLAGS) + add_if_flag_compiles(-fsanitize=undefined CMAKE_C_FLAGS CMAKE_LINKER_FLAGS) +endif() +if (ENABLE_MSAN) + add_if_flag_compiles(-fno-omit-frame-pointer CMAKE_C_FLAGS CMAKE_LINKER_FLAGS) + add_if_flag_compiles(-fsanitize=memory CMAKE_C_FLAGS CMAKE_LINKER_FLAGS) +endif() + +if (ENABLE_MSAN AND ENABLE_ASAN) + MESSAGE(WARNING "Compiling with both AddressSanitizer and MemorySanitizer is not recommended") +endif() + +add_definitions("-DRAYLIB_CMAKE=1") + +if(CMAKE_VERSION VERSION_LESS "3.1") + if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + add_if_flag_compiles(-std=gnu99 CMAKE_C_FLAGS) + endif() +else() + set (CMAKE_C_STANDARD 99) +endif() diff --git a/cmake/GlfwImport.cmake b/cmake/GlfwImport.cmake new file mode 100644 index 000000000..12dbfeb9f --- /dev/null +++ b/cmake/GlfwImport.cmake @@ -0,0 +1,29 @@ + +if(USE_EXTERNAL_GLFW STREQUAL "ON") + find_package(glfw3 3.2.1 REQUIRED) +elseif(USE_EXTERNAL_GLFW STREQUAL "IF_POSSIBLE") + find_package(glfw3 3.2.1 QUIET) +endif() +if (glfw3_FOUND) + set(LIBS_PRIVATE ${LIBS_PRIVATE} glfw) +endif() + +# Explicitly check against "ON", because USE_EXTERNAL_GLFW is a tristate option +# Also adding only on desktop (web also uses glfw but it is more limited and is added using an emcc linker flag) +if(NOT glfw3_FOUND AND NOT USE_EXTERNAL_GLFW STREQUAL "ON" AND "${PLATFORM}" MATCHES "Desktop") + MESSAGE(STATUS "Using raylib's GLFW") + set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) + set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + set(GLFW_INSTALL OFF CACHE BOOL "" FORCE) + set(BUILD_SHARED_LIBS OFF CACHE BOOL " " FORCE) + set(GLFW_USE_WAYLAND ${USE_WAYLAND} CACHE BOOL "" FORCE) + + add_subdirectory(external/glfw) + + list(APPEND raylib_sources $) + include_directories(BEFORE SYSTEM external/glfw/include) +else() + MESSAGE(STATUS "Using external GLFW") + set(GLFW_PKG_DEPS glfw3) +endif() \ No newline at end of file diff --git a/cmake/InstallConfigurations.cmake b/cmake/InstallConfigurations.cmake new file mode 100644 index 000000000..a26fedf55 --- /dev/null +++ b/cmake/InstallConfigurations.cmake @@ -0,0 +1,29 @@ +install( + TARGETS raylib EXPORT raylib-targets + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) + +# PKG_CONFIG_LIBS_PRIVATE is used in raylib.pc.in +if (STATIC) + include(LibraryPathToLinkerFlags) + library_path_to_linker_flags(__PKG_CONFIG_LIBS_PRIVATE "${LIBS_PRIVATE}") + set(PKG_CONFIG_LIBS_PRIVATE ${__PKG_CONFIG_LIBS_PRIVATE} ${GLFW_PKG_LIBS}) + string(REPLACE ";" " " PKG_CONFIG_LIBS_PRIVATE "${PKG_CONFIG_LIBS_PRIVATE}") +elseif (SHARED) + set(PKG_CONFIG_LIBS_EXTRA "") +endif () + +join_paths(libdir_for_pc_file "\${exec_prefix}" "${CMAKE_INSTALL_LIBDIR}") +join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") +configure_file(../raylib.pc.in raylib.pc @ONLY) +configure_file(../cmake/raylib-config-version.cmake raylib-config-version.cmake @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/raylib.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/raylib-config-version.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/raylib") +install(FILES ${PROJECT_SOURCE_DIR}/../cmake/raylib-config.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/raylib") + +# populates raylib_{FOUND, INCLUDE_DIRS, LIBRARIES, LDFLAGS, DEFINITIONS} +include(PopulateConfigVariablesLocally) +populate_config_variables_locally(raylib) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake new file mode 100644 index 000000000..8cca24649 --- /dev/null +++ b/cmake/LibraryConfigurations.cmake @@ -0,0 +1,109 @@ + +### Config options ### +# Translate the config options to what raylib wants +configure_file(config.h.in ${CMAKE_BINARY_DIR}/cmake/config.h) + +if(${PLATFORM} MATCHES "Desktop") + set(PLATFORM_CPP "PLATFORM_DESKTOP") + + if(APPLE) + # Need to force OpenGL 3.3 on OS X + # See: https://github.com/raysan5/raylib/issues/341 + set(GRAPHICS "GRAPHICS_API_OPENGL_33") + find_library(OPENGL_LIBRARY OpenGL) + set(LIBS_PRIVATE ${OPENGL_LIBRARY}) + link_libraries("${LIBS_PRIVATE}") + if (NOT CMAKE_SYSTEM STRLESS "Darwin-18.0.0") + add_definitions(-DGL_SILENCE_DEPRECATION) + MESSAGE(AUTHOR_WARNING "OpenGL is deprecated starting with macOS 10.14 (Mojave)!") + endif() + elseif(WIN32) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + set(LIBS_PRIVATE ${LIBS_PRIVATE} winmm) + else() + find_library(pthread NAMES pthread) + find_package(OpenGL QUIET) + if ("${OPENGL_LIBRARIES}" STREQUAL "") + set(OPENGL_LIBRARIES "GL") + endif() + + if ("${CMAKE_SYSTEM_NAME}" MATCHES "(Net|Open)BSD") + find_library(OSS_LIBRARY ossaudio) + endif() + + set(LIBS_PRIVATE m pthread ${OPENGL_LIBRARIES} ${OSS_LIBRARY}) + endif() + +elseif(${PLATFORM} MATCHES "Web") + set(PLATFORM_CPP "PLATFORM_WEB") + set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") + set(CMAKE_C_FLAGS "-s USE_GLFW=3 -s ASSERTIONS=1 --profiling") + set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") + +elseif(${PLATFORM} MATCHES "Android") + set(PLATFORM_CPP "PLATFORM_ANDROID") + set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") + include(AddIfFlagCompiles) + add_if_flag_compiles(-ffunction-sections CMAKE_C_FLAGS) + add_if_flag_compiles(-funwind-tables CMAKE_C_FLAGS) + add_if_flag_compiles(-fstack-protector-strong CMAKE_C_FLAGS) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + add_if_flag_compiles(-Wa,--noexecstack CMAKE_C_FLAGS) + add_if_flag_compiles(-no-canonical-prefixes CMAKE_C_FLAGS) + add_definitions(-DANDROID -D__ANDROID_API__=21) + include_directories(external/android/native_app_glue) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs,libatomic.a -Wl,--build-id -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings -uANativeActivity_onCreate") + + find_library(OPENGL_LIBRARY OpenGL) + set(LIBS_PRIVATE m log android EGL GLESv2 OpenSLES atomic c) + +elseif(${PLATFORM} MATCHES "Raspberry Pi") + set(PLATFORM_CPP "PLATFORM_RPI") + set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") + + add_definitions(-D_DEFAULT_SOURCE) + + find_library(GLESV2 brcmGLESv2 HINTS /opt/vc/lib) + find_library(EGL brcmEGL HINTS /opt/vc/lib) + find_library(BCMHOST bcm_host HINTS /opt/vc/lib) + include_directories(/opt/vc/include /opt/vc/include/interface/vmcs_host/linux /opt/vc/include/interface/vcos/pthreads) + link_directories(/opt/vc/lib) + set(LIBS_PRIVATE ${GLESV2} ${EGL} ${BCMHOST} pthread rt m dl) + +elseif(${PLATFORM} MATCHES "DRM") + set(PLATFORM_CPP "PLATFORM_DRM") + set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") + + add_definitions(-D_DEFAULT_SOURCE) + add_definitions(-DEGL_NO_X11) + add_definitions(-DPLATFORM_DRM) + + find_library(GLESV2 GLESv2) + find_library(EGL EGL) + find_library(DRM drm) + find_library(GBM gbm) + + include_directories(/usr/include/libdrm) + set(LIBS_PRIVATE ${GLESV2} ${EGL} ${DRM} ${GBM} pthread m dl) + +endif() + +if (${OPENGL_VERSION}) + set(${SUGGESTED_GRAPHICS} "${GRAPHICS}") + if (${OPENGL_VERSION} MATCHES "3.3") + set(GRAPHICS "GRAPHICS_API_OPENGL_33") + elseif (${OPENGL_VERSION} MATCHES "2.1") + set(GRAPHICS "GRAPHICS_API_OPENGL_21") + elseif (${OPENGL_VERSION} MATCHES "1.1") + set(GRAPHICS "GRAPHICS_API_OPENGL_11") + elseif (${OPENGL_VERSION} MATCHES "ES 2.0") + set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") + endif() + if ("${SUGGESTED_GRAPHICS}" AND NOT "${SUGGESTED_GRAPHICS}" STREQUAL "${GRAPHICS}") + message(WARNING "You are overriding the suggested GRAPHICS=${SUGGESTED_GRAPHICS} with ${GRAPHICS}! This may fail") + endif() +endif() + +if(NOT GRAPHICS) + set(GRAPHICS "GRAPHICS_API_OPENGL_33") +endif() \ No newline at end of file diff --git a/cmake/PackConfigurations.cmake b/cmake/PackConfigurations.cmake new file mode 100644 index 000000000..74eded025 --- /dev/null +++ b/cmake/PackConfigurations.cmake @@ -0,0 +1,13 @@ +# Packaging +SET(CPACK_PACKAGE_NAME "raylib") +SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Simple and easy-to-use library to enjoy videogames programming") +SET(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") +SET(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") +SET(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") +SET(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") +SET(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/../README.md") +SET(CPACK_RESOURCE_FILE_WELCOME "${PROJECT_SOURCE_DIR}/../README.md") +SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/../LICENSE") +SET(CPACK_PACKAGE_FILE_NAME "raylib-${PROJECT_VERSION}$ENV{RAYLIB_PACKAGE_SUFFIX}") +SET(CPACK_GENERATOR "ZIP;TGZ") # Remove this, if you want the NSIS installer on Windows +include(CPack) \ No newline at end of file diff --git a/cmake/emscripten.cmake b/cmake/emscripten.cmake deleted file mode 100644 index 1780dee98..000000000 --- a/cmake/emscripten.cmake +++ /dev/null @@ -1,23 +0,0 @@ -SET(CMAKE_SYSTEM_NAME Linux) -SET(CMAKE_SYSTEM_PROCESSOR x86) - -if (CMAKE_HOST_WIN32) - SET(EMSCRIPTEN_EXTENSION ".bat") -else () - SET(EMSCRIPTEN_EXTENSION "") -endif() - -SET(CMAKE_C_COMPILER emcc${EMSCRIPTEN_EXTENSION}) -SET(CMAKE_CXX_COMPILER em++${EMSCRIPTEN_EXTENSION}) - -if(NOT DEFINED CMAKE_AR) - find_program(CMAKE_AR NAMES emar${EMSCRIPTEN_EXTENSION}) -endif() -if(NOT DEFINED CMAKE_RANLIB) - find_program(CMAKE_RANLIB NAMES emranlib${EMSCRIPTEN_EXTENSION}) -endif() - -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) -set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 57ba74d47..e3ef3a0fc 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -28,7 +28,6 @@ if (APPLE AND NOT CMAKE_SYSTEM STRLESS "Darwin-18.0.0") add_definitions(-DGL_SILENCE_DEPRECATION) MESSAGE(AUTHOR_WARNING "OpenGL is deprecated starting with macOS 10.14 (Mojave)!") endif() -list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/rlgl_standalone.c) include(CheckIncludeFile) CHECK_INCLUDE_FILE("stdatomic.h" HAVE_STDATOMIC_H) @@ -119,19 +118,5 @@ foreach(example_source ${example_sources}) endif() endforeach() -if (${PLATFORM} MATCHES "Desktop") - # rlgl_standalone can't be linked with raylib because of duplicate rlgl symbols - foreach (example_source "others/rlgl_standalone.c") - # Create the basename for the example - get_filename_component(example_name ${example_source} NAME) - string(REPLACE ".c" "" example_name ${example_name}) - add_executable(${example_name} ${example_source}) - add_dependencies(${example_name} raylib) - target_link_libraries(${example_name} ${raylib_LDFLAGS}) - target_include_directories(${example_name} PRIVATE ${raylib_INCLUDE_DIRS}) - - endforeach() -endif() - # Copy all of the resource files to the destination file(COPY ${example_resources} DESTINATION "resources/") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2e9861f8c..b94ac1498 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,326 +1,132 @@ # Setup the project and settings project(raylib C) -include(GNUInstallDirs) -include(JoinPaths) -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake") - set(PROJECT_VERSION 3.5.0) set(API_VERSION 351) -include("CMakeOptions.txt") -include(BuildType) -configure_file(config.h.in ${CMAKE_BINARY_DIR}/cmake/config.h) -include_directories(${CMAKE_BINARY_DIR} .) +include(GNUInstallDirs) +include(JoinPaths) + +# Sets build type if not set by now +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + if(RAYLIB_IS_MAIN) + set(default_build_type Debug) + endif() + + message(STATUS "Setting build type to '${default_build_type}' as none was specified.") + + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() # Get the sources together -file(GLOB raylib_sources *.c) -list(REMOVE_ITEM raylib_sources ${CMAKE_CURRENT_SOURCE_DIR}/rglfw.c) +set(raylib_public_headers + raylib.h + rlgl.h + physac.h + raymath.h + raudio.h + ) -if(USE_EXTERNAL_GLFW STREQUAL "ON") - find_package(glfw3 3.2.1 REQUIRED) -elseif(USE_EXTERNAL_GLFW STREQUAL "IF_POSSIBLE") - find_package(glfw3 3.2.1 QUIET) -endif() -if (glfw3_FOUND) - set(LIBS_PRIVATE ${LIBS_PRIVATE} glfw) -endif() +set(raylib_sources + core.c + models.c + shapes.c + text.c + textures.c + utils.c + ) -# Explicitly check against "ON", because USE_EXTERNAL_GLFW is a tristate option -if(NOT glfw3_FOUND AND NOT USE_EXTERNAL_GLFW STREQUAL "ON" AND "${PLATFORM}" MATCHES "Desktop") - MESSAGE(STATUS "Using raylib's GLFW") - set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) - set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) - set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) - set(GLFW_INSTALL OFF CACHE BOOL "" FORCE) - set(BUILD_SHARED_LIBS OFF CACHE BOOL " " FORCE) - set(GLFW_USE_WAYLAND ${USE_WAYLAND} CACHE BOOL "" FORCE) +# cmake/GlfwImport.cmake handles the details around the inclusion of glfw +include(GlfwImport) - add_subdirectory(external/glfw) - list(APPEND raylib_sources $) - include_directories(BEFORE SYSTEM external/glfw/include) -else() - MESSAGE(STATUS "Using external GLFW") - set(GLFW_PKG_DEPS glfw3) -endif() +if (USE_AUDIO) + MESSAGE(STATUS "Audio Backend: miniaudio") + list(APPEND raylib_sources raudio.c) +else () + MESSAGE(STATUS "Audio Backend: None (-DUSE_AUDIO=OFF)") +endif () -add_definitions("-DRAYLIB_CMAKE=1") +include(LibraryConfigurations) -if(USE_AUDIO) - MESSAGE(STATUS "Audio Backend: miniaudio") - set(sources ${raylib_sources}) -else() - MESSAGE(STATUS "Audio Backend: None (-DUSE_AUDIO=OFF)") - set(RAYLIB_MODULE_AUDIO 0) - list(REMOVE_ITEM raylib_sources ${CMAKE_CURRENT_SOURCE_DIR}/raudio.c) - set(sources ${raylib_sources}) -endif() - -### Config options ### -# Translate the config options to what raylib wants -if(${PLATFORM} MATCHES "Desktop") - set(PLATFORM_CPP "PLATFORM_DESKTOP") - - if(APPLE) - # Need to force OpenGL 3.3 on OS X - # See: https://github.com/raysan5/raylib/issues/341 - set(GRAPHICS "GRAPHICS_API_OPENGL_33") - find_library(OPENGL_LIBRARY OpenGL) - set(LIBS_PRIVATE ${OPENGL_LIBRARY}) - link_libraries("${LIBS_PRIVATE}") - if (NOT CMAKE_SYSTEM STRLESS "Darwin-18.0.0") - add_definitions(-DGL_SILENCE_DEPRECATION) - MESSAGE(AUTHOR_WARNING "OpenGL is deprecated starting with macOS 10.14 (Mojave)!") - endif() - elseif(WIN32) - add_definitions(-D_CRT_SECURE_NO_WARNINGS) - set(LIBS_PRIVATE ${LIBS_PRIVATE} winmm) - else() - find_library(pthread NAMES pthread) - find_package(OpenGL QUIET) - if ("${OPENGL_LIBRARIES}" STREQUAL "") - set(OPENGL_LIBRARIES "GL") - endif() - - if ("${CMAKE_SYSTEM_NAME}" MATCHES "(Net|Open)BSD") - find_library(OSS_LIBRARY ossaudio) - endif() - - set(LIBS_PRIVATE m pthread ${OPENGL_LIBRARIES} ${OSS_LIBRARY}) - endif() - -elseif(${PLATFORM} MATCHES "Web") - set(PLATFORM_CPP "PLATFORM_WEB") - set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") - - set(CMAKE_C_FLAGS "-s USE_GLFW=3 -s ASSERTIONS=1 --profiling") - - # Change the name of the output library - -elseif(${PLATFORM} MATCHES "Android") - set(PLATFORM_CPP "PLATFORM_ANDROID") - set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") - include(AddIfFlagCompiles) - add_if_flag_compiles(-ffunction-sections CMAKE_C_FLAGS) - add_if_flag_compiles(-funwind-tables CMAKE_C_FLAGS) - add_if_flag_compiles(-fstack-protector-strong CMAKE_C_FLAGS) - set(CMAKE_POSITION_INDEPENDENT_CODE ON) - add_if_flag_compiles(-Wa,--noexecstack CMAKE_C_FLAGS) - add_if_flag_compiles(-no-canonical-prefixes CMAKE_C_FLAGS) - add_definitions(-DANDROID -D__ANDROID_API__=21) - include_directories(external/android/native_app_glue) - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs,libatomic.a -Wl,--build-id -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings -uANativeActivity_onCreate") - - find_library(OPENGL_LIBRARY OpenGL) - set(LIBS_PRIVATE m log android EGL GLESv2 OpenSLES atomic c) - -elseif(${PLATFORM} MATCHES "Raspberry Pi") - set(PLATFORM_CPP "PLATFORM_RPI") - set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") - - add_definitions(-D_DEFAULT_SOURCE) - - find_library(GLESV2 brcmGLESv2 HINTS /opt/vc/lib) - find_library(EGL brcmEGL HINTS /opt/vc/lib) - find_library(BCMHOST bcm_host HINTS /opt/vc/lib) - include_directories(/opt/vc/include /opt/vc/include/interface/vmcs_host/linux /opt/vc/include/interface/vcos/pthreads) - link_directories(/opt/vc/lib) - set(LIBS_PRIVATE ${GLESV2} ${EGL} ${BCMHOST} pthread rt m dl) - - elseif(${PLATFORM} MATCHES "DRM") - set(PLATFORM_CPP "PLATFORM_DRM") - set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") - - add_definitions(-D_DEFAULT_SOURCE) - add_definitions(-DEGL_NO_X11) - add_definitions(-DPLATFORM_DRM) - - find_library(GLESV2 GLESv2) - find_library(EGL EGL) - find_library(DRM drm) - find_library(GBM gbm) - - include_directories(/usr/include/libdrm) - set(LIBS_PRIVATE ${GLESV2} ${EGL} ${DRM} ${GBM} pthread m dl) - -endif() - -if (${OPENGL_VERSION}) - set(${SUGGESTED_GRAPHICS} "${GRAPHICS}") - if (${OPENGL_VERSION} MATCHES "3.3") - set(GRAPHICS "GRAPHICS_API_OPENGL_33") - elseif (${OPENGL_VERSION} MATCHES "2.1") - set(GRAPHICS "GRAPHICS_API_OPENGL_21") - elseif (${OPENGL_VERSION} MATCHES "1.1") - set(GRAPHICS "GRAPHICS_API_OPENGL_11") - elseif (${OPENGL_VERSION} MATCHES "ES 2.0") - set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") - endif() - if ("${SUGGESTED_GRAPHICS}" AND NOT "${SUGGESTED_GRAPHICS}" STREQUAL "${GRAPHICS}") - message(WARNING "You are overriding the suggested GRAPHICS=${SUGGESTED_GRAPHICS} with ${GRAPHICS}! This may fail") - endif() -endif() - -if(NOT GRAPHICS) - set(GRAPHICS "GRAPHICS_API_OPENGL_33") -endif() - -include_directories(${OPENGL_INCLUDE_DIR} ${OPENAL_INCLUDE_DIR}) set(LIBS_PRIVATE ${LIBS_PRIVATE} ${OPENAL_LIBRARY}) -include(LibraryPathToLinkerFlags) -library_path_to_linker_flags(__PKG_CONFIG_LIBS_PRIVATE "${LIBS_PRIVATE}") -if(STATIC) - MESSAGE(STATUS "Building raylib static library") - if(${PLATFORM} MATCHES "Web") - set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") - endif() - - add_library(raylib_static STATIC ${sources}) - - target_compile_definitions(raylib_static - PUBLIC ${PLATFORM_CPP} - PUBLIC PLATFORM=${PLATFORM_CPP} - PUBLIC ${GRAPHICS} - PUBLIC GRAPHICS=${GRAPHICS} - ) - - target_link_libraries(raylib_static - PUBLIC ${LIBS_PRIVATE} - ) - - set(PKG_CONFIG_LIBS_PRIVATE ${__PKG_CONFIG_LIBS_PRIVATE} ${GLFW_PKG_LIBS}) - string (REPLACE ";" " " PKG_CONFIG_LIBS_PRIVATE "${PKG_CONFIG_LIBS_PRIVATE}") - if (${PLATFORM} MATCHES "Desktop") - target_link_libraries(raylib_static PRIVATE glfw ${GLFW_LIBRARIES} ${LIBS_PRIVATE}) - endif() - - if (WITH_PIC) - set_property(TARGET raylib_static PROPERTY POSITION_INDEPENDENT_CODE ON) - endif() - set_target_properties(raylib_static PROPERTIES PUBLIC_HEADER "raylib.h") - if(NOT MSVC) # Keep lib*.(a|dll) name, but avoid *.lib files overwriting each other on Windows - set_target_properties(raylib_static PROPERTIES OUTPUT_NAME raylib) - endif() - install( - TARGETS raylib_static - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - ) - set_target_properties(raylib_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}") - - add_test("pkg-config--static" ${PROJECT_SOURCE_DIR}/../cmake/test-pkgconfig.sh --static) -endif(STATIC) +if (STATIC) + MESSAGE(STATUS "Building raylib static library") + + add_library(raylib STATIC ${raylib_sources} ${raylib_public_headers}) + add_library(raylib_static ALIAS raylib) + + add_test("pkg-config--static" ${PROJECT_SOURCE_DIR}/../cmake/test-pkgconfig.sh --static) +endif (STATIC) -if(SHARED) - MESSAGE(STATUS "Building raylib shared library") - add_library(raylib SHARED ${sources}) +if (SHARED) + MESSAGE(STATUS "Building raylib shared library") + add_library(raylib SHARED ${raylib_sources} ${raylib_public_headers}) + + if (MSVC) + target_compile_definitions(raylib + PRIVATE $ + INTERFACE $ + ) + endif () + + add_test("pkg-config" ${PROJECT_SOURCE_DIR}/../cmake/test-pkgconfig.sh) +endif () - target_compile_definitions(raylib - PUBLIC ${PLATFORM_CPP} - PUBLIC ${GRAPHICS} - ) +# Setting target properties +set_target_properties(raylib PROPERTIES + PUBLIC_HEADER "${raylib_public_headers}" + VERSION ${PROJECT_VERSION} + SOVERSION ${API_VERSION} + ) - if(MSVC) - target_compile_definitions(raylib - PRIVATE $ - INTERFACE $ - ) - endif() +if (WITH_PIC OR SHARED) + set_property(TARGET raylib PROPERTY POSITION_INDEPENDENT_CODE ON) +endif () - set(PKG_CONFIG_LIBS_EXTRA "") - - set_property(TARGET raylib PROPERTY POSITION_INDEPENDENT_CODE ON) - set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_LIBDIR}") - set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - set(CMAKE_MACOSX_RPATH ON) - - target_link_libraries(raylib ${LIBS_PRIVATE}) - if (${PLATFORM} MATCHES "Desktop") +# Linking libraries +target_link_libraries(raylib "${LIBS_PRIVATE}") +if (${PLATFORM} MATCHES "Desktop") target_link_libraries(raylib glfw) - endif() - set_target_properties(raylib PROPERTIES - PUBLIC_HEADER "raylib.h" - VERSION ${PROJECT_VERSION} - SOVERSION ${API_VERSION} - ) +endif () - if (WIN32) - install( - TARGETS raylib - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" - PUBLIC_HEADER DESTINATION "include" - ) - else() - install( - TARGETS raylib - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - ) - endif() - set_target_properties(raylib PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}") +# Adding compile definitions +target_compile_definitions(raylib + PUBLIC "${PLATFORM_CPP}" + PUBLIC "${GRAPHICS}" + ) - add_test("pkg-config" ${PROJECT_SOURCE_DIR}/../cmake/test-pkgconfig.sh) -else(SHARED) - add_library(raylib ALIAS raylib_static) -endif(SHARED) +# Registering include directories +target_include_directories(raylib + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/external + ${CMAKE_BINARY_DIR} # For cmake/config.h + ${OPENGL_INCLUDE_DIR} + ${OPENAL_INCLUDE_DIR} + ) -if (NOT DEFINED PKG_CONFIG_LIBS_EXTRA) - set(PKG_CONFIG_LIBS_EXTRA "${PKG_CONFIG_LIBS_PRIVATE}") -endif() +# Copy the header files to the build directory for convenience +file(COPY ${raylib_public_headers} DESTINATION "include") -join_paths(libdir_for_pc_file "\${exec_prefix}" "${CMAKE_INSTALL_LIBDIR}") -join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") - -configure_file(../raylib.pc.in raylib.pc @ONLY) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/raylib.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") -configure_file(../cmake/raylib-config-version.cmake raylib-config-version.cmake @ONLY) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/raylib-config-version.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/raylib") -install(FILES ${PROJECT_SOURCE_DIR}/../cmake/raylib-config.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/raylib") - -# populates raylib_{FOUND, INCLUDE_DIRS, LIBRARIES, LDFLAGS, DEFINITIONS} -include(PopulateConfigVariablesLocally) -populate_config_variables_locally(raylib) - -# Copy the header files to the build directory -file(COPY "raylib.h" DESTINATION ".") -file(COPY "rlgl.h" DESTINATION ".") -file(COPY "physac.h" DESTINATION ".") -file(COPY "raymath.h" DESTINATION ".") -file(COPY "raudio.h" DESTINATION ".") - -# Also install them -install(FILES "raylib.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -install(FILES "rlgl.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -install(FILES "physac.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -install(FILES "raymath.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -install(FILES "raudio.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") +include(InstallConfigurations) # Print the flags for the user if (DEFINED CMAKE_BUILD_TYPE) - message(STATUS "Generated build type: ${CMAKE_BUILD_TYPE}") -else() - message(STATUS "Generated config types: ${CMAKE_CONFIGURATION_TYPES}") -endif() + message(STATUS "Generated build type: ${CMAKE_BUILD_TYPE}") +else () + message(STATUS "Generated config types: ${CMAKE_CONFIGURATION_TYPES}") +endif () + message(STATUS "Compiling with the flags:") message(STATUS " PLATFORM=" ${PLATFORM_CPP}) message(STATUS " GRAPHICS=" ${GRAPHICS}) -# Packaging -SET(CPACK_PACKAGE_NAME "raylib") -SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Simple and easy-to-use library to enjoy videogames programming") -SET(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") -SET(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") -SET(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") -SET(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") -SET(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/../README.md") -SET(CPACK_RESOURCE_FILE_WELCOME "${PROJECT_SOURCE_DIR}/../README.md") -SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/../LICENSE") -SET(CPACK_PACKAGE_FILE_NAME "raylib-${PROJECT_VERSION}$ENV{RAYLIB_PACKAGE_SUFFIX}") -SET(CPACK_GENERATOR "ZIP;TGZ") # Remove this, if you want the NSIS installer on Windows -include(CPack) +include(PackConfigurations) enable_testing() diff --git a/src/config.h b/src/config.h index f84aa0170..dbe77cd9f 100644 --- a/src/config.h +++ b/src/config.h @@ -29,7 +29,7 @@ // Edit to control what features Makefile'd raylib is compiled with #if defined(RAYLIB_CMAKE) - // Edit CMakeOptions.txt for CMake instead + // Edit cmake/RaylibOptions.cmake for CMake instead #include "cmake/config.h" #else From 8d3381b490c856de560875c820b24c0785c56d3a Mon Sep 17 00:00:00 2001 From: Dmitry Matveyev Date: Thu, 14 Jan 2021 22:09:34 +0600 Subject: [PATCH 14/23] Add NimraylibNow! wrapper for Nim to bindings [ci skip] (#1532) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index c2be20838..dc3b61248 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -33,6 +33,7 @@ Here it is a list with the ones I'm aware of: | raylib-nim | 2.0 | [Nim](https://nim-lang.org/) | https://github.com/Skrylar/raylib-nim | | raylib-Forever | 3.1-dev | [Nim](https://nim-lang.org/) | https://github.com/Guevara-chan/Raylib-Forever | | nim-raylib | 3.1-dev | [Nim](https://nim-lang.org/) | https://github.com/tomc1998/nim-raylib | +| NimraylibNow! | 3.5-dev | [Nim](https://nim-lang.org/) | https://github.com/greenfork/nimraylib_now | | raylib-haskell | 2.0 | [Haskell](https://www.haskell.org/) | https://github.com/DevJac/raylib-haskell | | raylib-cr | 2.5-dev | [Crystal](https://crystal-lang.org/) | https://github.com/AregevDev/raylib-cr | | cray | 1.8 | [Crystal](https://crystal-lang.org/) | https://gitlab.com/Zatherz/cray | From a3c56d9052a008c843b4021f9b641cdec0e235e3 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 14 Jan 2021 19:53:58 +0100 Subject: [PATCH 15/23] go-raylib updated to 3.5 --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index dc3b61248..9f43546ec 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -19,7 +19,7 @@ Here it is a list with the ones I'm aware of: | raylib-go | 3.0 | [Go](https://golang.org/) | https://github.com/gen2brain/raylib-go | | raylib-goplus | 2.6-dev | [Go](https://golang.org/) | https://github.com/Lachee/raylib-goplus | | ray-go | 2.6-dev | [Go](https://golang.org/) | https://github.com/hecate-tech/ray-go | -| go-raylib | 3.1-dev | [Go](https://golang.org/) | https://github.com/chunqian/go-raylib | +| go-raylib | 3.5 | [Go](https://golang.org/) | https://github.com/chunqian/go-raylib | | raylib-rs | 3.0 | [Rust](https://www.rust-lang.org/) | https://github.com/deltaphc/raylib-rs | | raylib-lua | 1.7 | [Lua](http://www.lua.org/) | https://github.com/raysan5/raylib-lua | | raylib-lua-ffi | 2.0 | [Lua](http://www.lua.org/) | https://github.com/raysan5/raylib/issues/693 | From 186e52c4d8665cd4edefc74d85c11a993ada05e7 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 14 Jan 2021 20:42:01 +0100 Subject: [PATCH 16/23] REVIEWED: DecompressData() Corrected bug! --- src/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core.c b/src/core.c index ec962d8b8..15aab363a 100644 --- a/src/core.c +++ b/src/core.c @@ -2585,7 +2585,7 @@ unsigned char *DecompressData(unsigned char *compData, int compDataLength, int * #if defined(SUPPORT_COMPRESSION_API) // Decompress data from a valid DEFLATE stream - data = RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 0); + data = RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1); int length = sinflate(data, compData, compDataLength); RL_REALLOC(data, length); *dataLength = length; From b7f275efb3a0c5ef18e4bfcede6f30b979059e59 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 15 Jan 2021 00:20:23 +0100 Subject: [PATCH 17/23] Review warning --- src/raudio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index fdf9883e3..f4ae9efaa 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1966,13 +1966,13 @@ static int SaveWAV(Wave wave, const char *fileName) format.sampleRate = wave.sampleRate; format.bitsPerSample = wave.sampleSize; - unsigned char *fileData = NULL; + void *fileData = NULL; size_t fileDataSize = 0; success = drwav_init_memory_write(&wav, &fileData, &fileDataSize, &format, NULL); if (success) success = (int)drwav_write_pcm_frames(&wav, wave.sampleCount/wave.channels, wave.data); drwav_result result = drwav_uninit(&wav); - if (result == DRWAV_SUCCESS) success = SaveFileData(fileName, fileData, (unsigned int)fileDataSize); + if (result == DRWAV_SUCCESS) success = SaveFileData(fileName, (unsigned char *)fileData, (unsigned int)fileDataSize); drwav_free(fileData, NULL); From eb7820b2b0832b2e624709cfae1a61606833ef62 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 15 Jan 2021 00:20:35 +0100 Subject: [PATCH 18/23] Review comment --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index a28711174..24992a4f6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -831,7 +831,7 @@ typedef enum { BLEND_MULTIPLIED, // Blend textures multiplying colors BLEND_ADD_COLORS, // Blend textures adding colors (alternative) BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) - BLEND_CUSTOM // Belnd textures using custom src/dst factors (use SetBlendModeCustom()) + BLEND_CUSTOM // Belnd textures using custom src/dst factors (use rlSetBlendMode()) } BlendMode; // Gestures type From 1866be047578fbe38e3e857ae047ce84d92870ee Mon Sep 17 00:00:00 2001 From: Gil Barbosa Reis Date: Sat, 16 Jan 2021 06:33:13 -0300 Subject: [PATCH 19/23] Fix absolute path handling in GetFileName and GetDirectoryPath (#1534) (#1535) --- src/core.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/core.c b/src/core.c index 15aab363a..3cac4796a 100644 --- a/src/core.c +++ b/src/core.c @@ -2353,7 +2353,7 @@ const char *GetFileName(const char *filePath) const char *fileName = NULL; if (filePath != NULL) fileName = strprbrk(filePath, "\\/"); - if (!fileName || (fileName == filePath)) return filePath; + if (!fileName) return filePath; return fileName + 1; } @@ -2399,9 +2399,9 @@ const char *GetDirectoryPath(const char *filePath) static char dirPath[MAX_FILEPATH_LENGTH]; memset(dirPath, 0, MAX_FILEPATH_LENGTH); - // In case provided path does not contains a root drive letter (C:\, D:\), + // In case provided path does not contain a root drive letter (C:\, D:\) nor leading path separator (\, /), // we add the current directory path to dirPath - if (filePath[1] != ':') + if (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/') { // For security, we set starting path to current directory, // obtained path will be concated to this @@ -2412,9 +2412,18 @@ const char *GetDirectoryPath(const char *filePath) lastSlash = strprbrk(filePath, "\\/"); if (lastSlash) { - // NOTE: Be careful, strncpy() is not safe, it does not care about '\0' - memcpy(dirPath + ((filePath[1] != ':')? 2 : 0), filePath, strlen(filePath) - (strlen(lastSlash) - 1)); - dirPath[strlen(filePath) - strlen(lastSlash) + ((filePath[1] != ':')? 2 : 0)] = '\0'; // Add '\0' manually + if (lastSlash == filePath) + { + // The last and only slash is the leading one: path is in a root directory + dirPath[0] = filePath[0]; + dirPath[1] = '\0'; + } + else + { + // NOTE: Be careful, strncpy() is not safe, it does not care about '\0' + memcpy(dirPath + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0), filePath, strlen(filePath) - (strlen(lastSlash) - 1)); + dirPath[strlen(filePath) - strlen(lastSlash) + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0)] = '\0'; // Add '\0' manually + } } return dirPath; From 6cc27e979769affcc9d094143c03efd5872e70b9 Mon Sep 17 00:00:00 2001 From: hristo Date: Sat, 16 Jan 2021 15:04:01 +0200 Subject: [PATCH 20/23] Fix cmake build error dirent (#1536) * Better ignore support for idea projects. Added a wildcard at the end because different configurations would have a diffeerent build directory. * Removed external from being a relative include directory for target raylib. Fixes #1533 --- .gitignore | 2 +- src/CMakeLists.txt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 2a23619ef..d51b5bcd3 100644 --- a/.gitignore +++ b/.gitignore @@ -82,7 +82,7 @@ DerivedData/ # Jetbrains project .idea/ -cmake-build-debug/ +cmake-build-*/ # CMake stuff CMakeCache.txt diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b94ac1498..3319ef03d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -105,7 +105,6 @@ target_include_directories(raylib $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/external ${CMAKE_BINARY_DIR} # For cmake/config.h ${OPENGL_INCLUDE_DIR} ${OPENAL_INCLUDE_DIR} From f21aa0352bc677a4e801588c7129c9d9bc085aa2 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 16 Jan 2021 19:01:28 +0000 Subject: [PATCH 21/23] Update BINDINGS.md (#1538) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 9f43546ec..29f5baa07 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -46,7 +46,7 @@ Here it is a list with the ones I'm aware of: | raylib-ruby | 2.6 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/a0/raylib-ruby | | raylib-mruby | 2.5-dev | [mruby](https://github.com/mruby/mruby) | https://github.com/lihaochen910/raylib-mruby | | raylib-py | 2.0 | [Python](https://www.python.org/) | https://github.com/overdev/raylib-py | -| raylib-python-cffi | 3.1-dev | [Python](https://www.python.org/) | https://github.com/electronstudio/raylib-python-cffi | +| raylib-python-cffi | 3.5 | [Python](https://www.python.org/) | https://github.com/electronstudio/raylib-python-cffi | | raylib-py-ctbg | 2.6 | [Python](https://www.python.org/) | https://github.com/overdev/raylib-py-ctbg | | jaylib | 3.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | https://github.com/electronstudio/jaylib/ | | raylib-java | 2.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | https://github.com/XoanaIO/raylib-java | From 3e3e41eaba17366cf3502687835a53d51ef03d99 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 17 Jan 2021 03:31:56 -0500 Subject: [PATCH 22/23] Update node-raylib to 3.5 (#1539) [`node-raylib`](https://github.com/robloach/node-raylib) is now on raylib 3.5.0 --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 29f5baa07..e23dbb919 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -51,7 +51,7 @@ Here it is a list with the ones I'm aware of: | jaylib | 3.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | https://github.com/electronstudio/jaylib/ | | raylib-java | 2.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | https://github.com/XoanaIO/raylib-java | | clj-raylib | 3.0 | [Clojure](https://clojure.org/) | https://github.com/lsevero/clj-raylib | -| node-raylib | 3.0 | [Node.js](https://nodejs.org/en/) | https://github.com/RobLoach/node-raylib | +| node-raylib | 3.5 | [Node.js](https://nodejs.org/en/) | https://github.com/RobLoach/node-raylib | | QuickJS-raylib | 3.0 | [QuickJS](https://bellard.org/quickjs/) | https://github.com/sntg-p/QuickJS-raylib | | raylib-js | 2.6 | [JavaScript](https://en.wikipedia.org/wiki/JavaScript) | https://github.com/RobLoach/raylib-js | | raylib-chaiscript | 2.6 | [ChaiScript](http://chaiscript.com/) | https://github.com/RobLoach/raylib-chaiscript | From 5e6eb0b847d5c27a76e03c60d3db6d10d8788456 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 19 Jan 2021 12:36:30 +0100 Subject: [PATCH 23/23] Update raylib to v3.5 :P --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index e23dbb919..2d1975c47 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -6,7 +6,7 @@ Here it is a list with the ones I'm aware of: | name | raylib version | language | repo | |:------------------:|:-------------: | :--------:|----------------------------------------------------------------------| -| raylib | **3.1-dev** | [C](https://en.wikipedia.org/wiki/C_(programming_language)) | https://github.com/raysan5/raylib | +| raylib | **3.5** | [C](https://en.wikipedia.org/wiki/C_(programming_language)) | https://github.com/raysan5/raylib | | raylib-cpp | 3.5 | [C++](https://en.wikipedia.org/wiki/C%2B%2B) | https://github.com/robloach/raylib-cpp | | Raylib-cs | 3.5 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | https://github.com/ChrisDill/Raylib-cs | | raylib-cppsharp | 2.5 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | https://github.com/phxvyper/raylib-cppsharp |