From 4bce0f27e6265fdcdae7dbb01ca112527c6acd70 Mon Sep 17 00:00:00 2001 From: Tornike Goshadze Date: Sun, 3 Jan 2021 13:20:02 -0800 Subject: [PATCH 01/43] Update Raylib-cs bindings version (#1508) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index e03697b0e..c2be20838 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -8,7 +8,7 @@ Here it is a list with the ones I'm aware of: |:------------------:|:-------------: | :--------:|----------------------------------------------------------------------| | raylib | **3.1-dev** | [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.0 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | https://github.com/ChrisDill/Raylib-cs | +| 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 | | raylib-boo | 3.5 | [Boo](http://boo-language.github.io/) | https://github.com/Rabios/raylib-boo | | RaylibFS | 2.5 | [F#](https://fsharp.org/) | https://github.com/dallinbeutler/RaylibFS | From 551597d5793532b19ed71808803a0d6a6e1178b0 Mon Sep 17 00:00:00 2001 From: hristo Date: Sun, 3 Jan 2021 23:43:09 +0200 Subject: [PATCH 02/43] Removed a repeating allocation of memory (#1507) Resolves #1495 This line allocated some memory that was already allocated in the beginning of the function and was essentially creating a leak. --- src/models.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models.c b/src/models.c index b014352df..60e08171c 100644 --- a/src/models.c +++ b/src/models.c @@ -1486,7 +1486,6 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) mesh.vertices = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float)); mesh.texcoords = (float *)RL_MALLOC(plane->ntriangles*3*2*sizeof(float)); mesh.normals = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float)); - mesh.vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); mesh.vertexCount = plane->ntriangles*3; mesh.triangleCount = plane->ntriangles; From 5d4aada52649e0a43ba6eb446b205a68cd0831f6 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 6 Jan 2021 04:21:58 -0800 Subject: [PATCH 03/43] Don't create an ortho matrix when the viewport is 0 in any axis. (#1504) * Don't create an ortho matrix when the viewport is 0 in any axis. Not all compilers divide by 0 and return inf, some segfault. The matrix is not used by anything when minimized, so it just needs to not be called. * Better fix that always ensures the rlgl matrix is always valid * Better fix that always ensures the rlgl matrix is always valid --- src/rlgl.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index 3d327a6f9..bc851668a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1100,6 +1100,12 @@ 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) + { + *RLGL.State.currentMatrix = MatrixIdentity(); + return; + } + Matrix matOrtho = MatrixOrtho(left, right, bottom, top, znear, zfar); *RLGL.State.currentMatrix = MatrixMultiply(*RLGL.State.currentMatrix, matOrtho); From 7bd33e4406adc31b0c72fd771733c24397b783b5 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 6 Jan 2021 13:26:55 +0100 Subject: [PATCH 04/43] Review rlOrtho() to avoid return in the middle of the function I usually try to avoid any return in the middle of functions, I try to keep them always at the end of the functions. --- src/rlgl.h | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index bc851668a..63b4fa7f1 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1100,15 +1100,12 @@ 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) && ((bottom - top) > 0)) { - *RLGL.State.currentMatrix = MatrixIdentity(); - return; + Matrix matOrtho = MatrixOrtho(left, right, bottom, top, znear, zfar); + *RLGL.State.currentMatrix = MatrixMultiply(*RLGL.State.currentMatrix, matOrtho); } - - Matrix matOrtho = MatrixOrtho(left, right, bottom, top, znear, zfar); - - *RLGL.State.currentMatrix = MatrixMultiply(*RLGL.State.currentMatrix, matOrtho); + else *RLGL.State.currentMatrix = MatrixIdentity(); } #endif From a6cd6eedbe4666318d1e9b7a291cbfaba9410b26 Mon Sep 17 00:00:00 2001 From: Victor Gallet Date: Wed, 6 Jan 2021 13:27:32 +0100 Subject: [PATCH 05/43] Remove unused condition in 'GenerateMipmaps' function for GRAPHICS_API_OPENGL_11 (#1496) --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 63b4fa7f1..572e86c47 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4791,8 +4791,8 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) // Count mipmap levels required while ((width != 1) && (height != 1)) { - if (width != 1) width /= 2; - if (height != 1) height /= 2; + width /= 2; + height /= 2; TRACELOGD("TEXTURE: Next mipmap size: %i x %i", width, height); 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 06/43] 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 07/43] 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 08/43] 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 09/43] 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 10/43] 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 11/43] 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 12/43] 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 13/43] 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 14/43] 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 15/43] 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 16/43] 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 17/43] 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 18/43] 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 19/43] 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 20/43] 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 21/43] 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 22/43] 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 23/43] 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 24/43] 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 25/43] 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 26/43] 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 27/43] 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 28/43] 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 | From 407c014eb4b12d8658b85c41d3f4615a4535a1f2 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 20 Jan 2021 17:06:30 +0100 Subject: [PATCH 29/43] Update Makefile --- templates/advance_game/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/advance_game/Makefile b/templates/advance_game/Makefile index 7097e2c2c..65c14cdff 100644 --- a/templates/advance_game/Makefile +++ b/templates/advance_game/Makefile @@ -237,7 +237,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # -O2 # optimization level 2, if used, also set --memory-init-file 0 # -s USE_GLFW=3 # Use glfw3 library (context/input management) # -s ALLOW_MEMORY_GROWTH=1 # to allow memory resizing -> WARNING: Audio buffers could FAIL! - # -s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) + # -s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) (67108864 = 64MB) # -s USE_PTHREADS=1 # multithreading support # -s WASM=0 # disable Web Assembly, emitted by default # -s ASYNCIFY # lets synchronous C/C++ code interact with asynchronous JS From 01b7509a396f4c060bb256ce2c5908d4ed9758ad Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 20 Jan 2021 17:06:53 +0100 Subject: [PATCH 30/43] Review screen capture / gif recording #1540 --- src/core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core.c b/src/core.c index 15aab363a..d7bff21fe 100644 --- a/src/core.c +++ b/src/core.c @@ -4606,6 +4606,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // NOTE: Before closing window, while loop must be left! } +#if defined(SUPPORT_SCREEN_CAPTURE) else if (key == GLFW_KEY_F12 && action == GLFW_PRESS) { #if defined(SUPPORT_GIF_RECORDING) @@ -4648,14 +4649,13 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i } } else -#endif // SUPPORT_GIF_RECORDING -#if defined(SUPPORT_SCREEN_CAPTURE) +#endif // SUPPORT_GIF_RECORDING { TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); screenshotCounter++; } -#endif // SUPPORT_SCREEN_CAPTURE } +#endif // SUPPORT_SCREEN_CAPTURE else { // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1 @@ -5978,6 +5978,7 @@ void UWPKeyDownEvent(int key, bool down, bool controlKey) // Time to close the window. CORE.Window.shouldClose = true; } +#if defined(SUPPORT_SCREEN_CAPTURE) else if (key == KEY_F12 && down) { #if defined(SUPPORT_GIF_RECORDING) @@ -6011,14 +6012,13 @@ void UWPKeyDownEvent(int key, bool down, bool controlKey) } } else -#endif // SUPPORT_GIF_RECORDING -#if defined(SUPPORT_SCREEN_CAPTURE) +#endif // SUPPORT_GIF_RECORDING { TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); screenshotCounter++; } -#endif // SUPPORT_SCREEN_CAPTURE } +#endif // SUPPORT_SCREEN_CAPTURE else { CORE.Input.Keyboard.currentKeyState[key] = down; From b845f3886a24db69c25fd685d99cad00200e79f1 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 20 Jan 2021 17:18:45 +0100 Subject: [PATCH 31/43] Minor tweak to Makefile --- projects/VSCode/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile index b15eb7f6f..5e09d668e 100644 --- a/projects/VSCode/Makefile +++ b/projects/VSCode/Makefile @@ -25,8 +25,7 @@ # Define required raylib variables PROJECT_NAME ?= game -RAYLIB_VERSION ?= 3.0.0 -RAYLIB_API_VERSION ?= 300 +RAYLIB_VERSION ?= 3.5.0 RAYLIB_PATH ?= ..\.. # Define compiler path on Windows From 677f420bf00d346b8b5a84ac17beaf70111dbf2b Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 20 Jan 2021 20:55:12 +0100 Subject: [PATCH 32/43] REVIEWED: physac module and examples #1525 --- examples/physics/physics_demo.c | 34 +- examples/physics/physics_friction.c | 21 +- examples/physics/physics_movement.c | 21 +- examples/physics/physics_restitution.c | 22 +- examples/physics/physics_shatter.c | 38 +- src/physac.h | 1065 +++++++++++------------- 6 files changed, 535 insertions(+), 666 deletions(-) diff --git a/examples/physics/physics_demo.c b/examples/physics/physics_demo.c index 421c82a90..9763bf990 100644 --- a/examples/physics/physics_demo.c +++ b/examples/physics/physics_demo.c @@ -1,24 +1,19 @@ /******************************************************************************************* * -* Physac - Physics demo +* raylib [physac] example - physics demo * -* NOTE 1: Physac requires multi-threading, when InitPhysics() a second thread is created to manage physics calculations. -* NOTE 2: Physac requires static C library linkage to avoid dependency on MinGW DLL (-static -lpthread) +* This example has been created using raylib 1.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Use the following line to compile: +* This example uses physac 1.1 (https://github.com/raysan5/raylib/blob/master/src/physac.h) * -* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static / -* -lraylib -lpthread -lglfw3 -lopengl32 -lgdi32 -lopenal32 -lwinmm / -* -std=c99 -Wl,--subsystem,windows -Wl,-allow-multiple-definition -* -* Copyright (c) 2016-2018 Victor Fisac +* Copyright (c) 2016-2021 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ #include "raylib.h" #define PHYSAC_IMPLEMENTATION -#define PHYSAC_NO_THREADS #include "physac.h" int main(void) @@ -29,12 +24,11 @@ int main(void) const int screenHeight = 450; SetConfigFlags(FLAG_MSAA_4X_HINT); - InitWindow(screenWidth, screenHeight, "Physac [raylib] - Physics demo"); + InitWindow(screenWidth, screenHeight, "raylib [physac] example - physics demo"); // Physac logo drawing position int logoX = screenWidth - MeasureText("Physac", 30) - 10; int logoY = 15; - bool needsReset = false; // Initialize physics and default physics bodies InitPhysics(); @@ -55,25 +49,17 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - // Delay initialization of variables due to physics reset async - RunPhysicsStep(); + UpdatePhysics(); // Update physics system - if (needsReset) + if (IsKeyPressed('R')) // Reset physics system { + ResetPhysics(); + floor = CreatePhysicsBodyRectangle((Vector2){ screenWidth/2, screenHeight }, 500, 100, 10); floor->enabled = false; circle = CreatePhysicsBodyCircle((Vector2){ screenWidth/2, screenHeight/2 }, 45, 10); circle->enabled = false; - - needsReset = false; - } - - // Reset physics input - if (IsKeyPressed('R')) - { - ResetPhysics(); - needsReset = true; } // Physics body creation inputs diff --git a/examples/physics/physics_friction.c b/examples/physics/physics_friction.c index 0acf88aea..b91950391 100644 --- a/examples/physics/physics_friction.c +++ b/examples/physics/physics_friction.c @@ -1,24 +1,19 @@ /******************************************************************************************* * -* Physac - Physics friction +* raylib [physac] example - physics friction * -* NOTE 1: Physac requires multi-threading, when InitPhysics() a second thread is created to manage physics calculations. -* NOTE 2: Physac requires static C library linkage to avoid dependency on MinGW DLL (-static -lpthread) +* This example has been created using raylib 1.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Use the following line to compile: +* This example uses physac 1.1 (https://github.com/raysan5/raylib/blob/master/src/physac.h) * -* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static / -* -lraylib -lpthread -lglfw3 -lopengl32 -lgdi32 -lopenal32 -lwinmm / -* -std=c99 -Wl,--subsystem,windows -Wl,-allow-multiple-definition -* -* Copyright (c) 2016-2018 Victor Fisac +* Copyright (c) 2016-2021 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ #include "raylib.h" #define PHYSAC_IMPLEMENTATION -#define PHYSAC_NO_THREADS #include "physac.h" int main(void) @@ -29,7 +24,7 @@ int main(void) const int screenHeight = 450; SetConfigFlags(FLAG_MSAA_4X_HINT); - InitWindow(screenWidth, screenHeight, "Physac [raylib] - Physics friction"); + InitWindow(screenWidth, screenHeight, "raylib [physac] example - physics friction"); // Physac logo drawing position int logoX = screenWidth - MeasureText("Physac", 30) - 10; @@ -73,9 +68,9 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - RunPhysicsStep(); + UpdatePhysics(); // Update physics system - if (IsKeyPressed('R')) // Reset physics input + if (IsKeyPressed('R')) // Reset physics system { // Reset dynamic physics bodies position, velocity and rotation bodyA->position = (Vector2){ 35, screenHeight*0.6f }; diff --git a/examples/physics/physics_movement.c b/examples/physics/physics_movement.c index d946811d1..73a1f9277 100644 --- a/examples/physics/physics_movement.c +++ b/examples/physics/physics_movement.c @@ -1,24 +1,19 @@ /******************************************************************************************* * -* Physac - Physics movement +* raylib [physac] example - physics movement * -* NOTE 1: Physac requires multi-threading, when InitPhysics() a second thread is created to manage physics calculations. -* NOTE 2: Physac requires static C library linkage to avoid dependency on MinGW DLL (-static -lpthread) +* This example has been created using raylib 1.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Use the following line to compile: +* This example uses physac 1.1 (https://github.com/raysan5/raylib/blob/master/src/physac.h) * -* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static / -* -lraylib -lpthread -lglfw3 -lopengl32 -lgdi32 -lopenal32 -lwinmm / -* -std=c99 -Wl,--subsystem,windows -Wl,-allow-multiple-definition -* -* Copyright (c) 2016-2018 Victor Fisac +* Copyright (c) 2016-2021 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ #include "raylib.h" #define PHYSAC_IMPLEMENTATION -#define PHYSAC_NO_THREADS #include "physac.h" #define VELOCITY 0.5f @@ -31,7 +26,7 @@ int main(void) const int screenHeight = 450; SetConfigFlags(FLAG_MSAA_4X_HINT); - InitWindow(screenWidth, screenHeight, "Physac [raylib] - Physics movement"); + InitWindow(screenWidth, screenHeight, "raylib [physac] example - physics movement"); // Physac logo drawing position int logoX = screenWidth - MeasureText("Physac", 30) - 10; @@ -66,9 +61,9 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - RunPhysicsStep(); + UpdatePhysics(); // Update physics system - if (IsKeyPressed('R')) // Reset physics input + if (IsKeyPressed('R')) // Reset physics input { // Reset movement physics body position, velocity and rotation body->position = (Vector2){ screenWidth/2, screenHeight/2 }; diff --git a/examples/physics/physics_restitution.c b/examples/physics/physics_restitution.c index 12a2a9220..787db98d9 100644 --- a/examples/physics/physics_restitution.c +++ b/examples/physics/physics_restitution.c @@ -1,24 +1,19 @@ /******************************************************************************************* * -* Physac - Physics restitution +* raylib [physac] example - physics restitution * -* NOTE 1: Physac requires multi-threading, when InitPhysics() a second thread is created to manage physics calculations. -* NOTE 2: Physac requires static C library linkage to avoid dependency on MinGW DLL (-static -lpthread) +* This example has been created using raylib 1.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Use the following line to compile: +* This example uses physac 1.1 (https://github.com/raysan5/raylib/blob/master/src/physac.h) * -* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static / -* -lraylib -lpthread -lglfw3 -lopengl32 -lgdi32 -lopenal32 -lwinmm / -* -std=c99 -Wl,--subsystem,windows -Wl,-allow-multiple-definition -* -* Copyright (c) 2016-2018 Victor Fisac +* Copyright (c) 2016-2021 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ #include "raylib.h" #define PHYSAC_IMPLEMENTATION -#define PHYSAC_NO_THREADS #include "physac.h" int main(void) @@ -29,7 +24,7 @@ int main(void) const int screenHeight = 450; SetConfigFlags(FLAG_MSAA_4X_HINT); - InitWindow(screenWidth, screenHeight, "Physac [raylib] - Physics restitution"); + InitWindow(screenWidth, screenHeight, "raylib [physac] example - physics restitution"); // Physac logo drawing position int logoX = screenWidth - MeasureText("Physac", 30) - 10; @@ -62,9 +57,9 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - RunPhysicsStep(); + UpdatePhysics(); // Update physics system - if (IsKeyPressed('R')) // Reset physics input + if (IsKeyPressed('R')) // Reset physics input { // Reset circles physics bodies position and velocity circleA->position = (Vector2){ screenWidth*0.25f, screenHeight/2 }; @@ -124,6 +119,7 @@ int main(void) DestroyPhysicsBody(circleB); DestroyPhysicsBody(circleC); DestroyPhysicsBody(floor); + ClosePhysics(); // Unitialize physics CloseWindow(); // Close window and OpenGL context diff --git a/examples/physics/physics_shatter.c b/examples/physics/physics_shatter.c index cbc65221d..46876ff7c 100644 --- a/examples/physics/physics_shatter.c +++ b/examples/physics/physics_shatter.c @@ -1,24 +1,19 @@ /******************************************************************************************* * -* Physac - Body shatter +* raylib [physac] example - physics shatter * -* NOTE 1: Physac requires multi-threading, when InitPhysics() a second thread is created to manage physics calculations. -* NOTE 2: Physac requires static C library linkage to avoid dependency on MinGW DLL (-static -lpthread) +* This example has been created using raylib 1.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Use the following line to compile: +* This example uses physac 1.1 (https://github.com/raysan5/raylib/blob/master/src/physac.h) * -* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static / -* -lraylib -lpthread -lglfw3 -lopengl32 -lgdi32 -lopenal32 -lwinmm / -* -std=c99 -Wl,--subsystem,windows -Wl,-allow-multiple-definition -* -* Copyright (c) 2016-2018 Victor Fisac +* Copyright (c) 2016-2021 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ #include "raylib.h" #define PHYSAC_IMPLEMENTATION -#define PHYSAC_NO_THREADS #include "physac.h" int main(void) @@ -29,12 +24,11 @@ int main(void) const int screenHeight = 450; SetConfigFlags(FLAG_MSAA_4X_HINT); - InitWindow(screenWidth, screenHeight, "Physac [raylib] - Body shatter"); + InitWindow(screenWidth, screenHeight, "raylib [physac] example - physics shatter"); // Physac logo drawing position int logoX = screenWidth - MeasureText("Physac", 30) - 10; int logoY = 15; - bool needsReset = false; // Initialize physics and default physics bodies InitPhysics(); @@ -49,31 +43,23 @@ int main(void) // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - RunPhysicsStep(); - //---------------------------------------------------------------------------------- - // Delay initialization of variables due to physics reset asynchronous - if (needsReset) - { - // Create random polygon physics body to shatter - CreatePhysicsBodyPolygon((Vector2){ screenWidth/2, screenHeight/2 }, GetRandomValue(80, 200), GetRandomValue(3, 8), 10); - needsReset = false; - } - - if (IsKeyPressed('R')) // Reset physics input + UpdatePhysics(); // Update physics system + + if (IsKeyPressed('R')) // Reset physics input { ResetPhysics(); - needsReset = true; + + CreatePhysicsBodyPolygon((Vector2){ screenWidth/2, screenHeight/2 }, GetRandomValue(80, 200), GetRandomValue(3, 8), 10); } if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) // Physics shatter input { - // Note: some values need to be stored in variables due to asynchronous changes during main thread int count = GetPhysicsBodiesCount(); for (int i = count - 1; i >= 0; i--) { PhysicsBody currentBody = GetPhysicsBody(i); + if (currentBody != NULL) PhysicsShatter(currentBody, GetMousePosition(), 10/currentBody->inverseMass); } } diff --git a/src/physac.h b/src/physac.h index c3898d2fc..695e399d4 100644 --- a/src/physac.h +++ b/src/physac.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* Physac v1.0 - 2D Physics library for videogames +* Physac v1.1 - 2D Physics library for videogames * * DESCRIPTION: * @@ -20,38 +20,44 @@ * The generated implementation will stay private inside implementation file and all * internal symbols and functions will only be visible inside that file. * -* #define PHYSAC_NO_THREADS -* The generated implementation won't include pthread library and user must create a secondary thread to call PhysicsThread(). -* It is so important that the thread where PhysicsThread() is called must not have v-sync or any other CPU limitation. -* -* #define PHYSAC_STANDALONE -* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined -* internally in the library and input management and drawing functions must be provided by -* the user (check library implementation for further details). -* * #define PHYSAC_DEBUG -* Traces log messages when creating and destroying physics bodies and detects errors in physics -* calculations and reference exceptions; it is useful for debug purposes +* Show debug traces log messages about physic bodies creation/destruction, physic system errors, +* some calculations results and NULL reference exceptions +* +* #define PHYSAC_DEFINE_VECTOR2_TYPE +* Forces library to define struct Vector2 data type (float x; float y) +* +* #define PHYSAC_AVOID_TIMMING_SYSTEM +* Disables internal timming system, used by UpdatePhysics() to launch timmed physic steps, +* it allows just running UpdatePhysics() automatically on a separate thread at a desired time step. +* In case physics steps update needs to be controlled by user with a custom timming mechanism, +* just define this flag and the internal timming mechanism will be avoided, in that case, +* timming libraries are neither required by the module. * * #define PHYSAC_MALLOC() +* #define PHYSAC_CALLOC() * #define PHYSAC_FREE() * You can define your own malloc/free implementation replacing stdlib.h malloc()/free() functions. * Otherwise it will include stdlib.h and use the C standard library malloc()/free() function. * +* COMPILATION: * -* NOTE 1: Physac requires multi-threading, when InitPhysics() a second thread is created to manage physics calculations. -* NOTE 2: Physac requires static C library linkage to avoid dependency on MinGW DLL (-static -lpthread) +* Use the following code to compile with GCC: +* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static -lraylib -lopengl32 -lgdi32 -lwinmm -std=c99 * -* Use the following code to compile: -* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static -lraylib -lpthread -lopengl32 -lgdi32 -lwinmm -std=c99 -* -* VERY THANKS TO: -* Ramon Santamaria (github: @raysan5) +* VERSIONS HISTORY: +* 1.1 (20-Jan-2021) @raysan5: Library general revision +* Removed threading system (up to the user) +* Support MSVC C++ compilation using CLITERAL() +* Review DEBUG mechanism for TRACELOG() and all TRACELOG() messages +* Review internal variables/functions naming for consistency +* Allow option to avoid internal timming system, to allow app manage the steps +* 1.0 (12-Jun-2017) First release of the library * * * LICENSE: zlib/libpng * -* Copyright (c) 2016-2018 Victor Fisac (github: @victorfisac) +* Copyright (c) 2016-2021 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -87,6 +93,9 @@ #ifndef PHYSAC_MALLOC #define PHYSAC_MALLOC(size) malloc(size) #endif +#ifndef PHYSAC_CALLOC + #define PHYSAC_CALLOC(size, n) calloc(size, n) +#endif #ifndef PHYSAC_FREE #define PHYSAC_FREE(ptr) free(ptr) #endif @@ -94,10 +103,10 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define PHYSAC_MAX_BODIES 64 -#define PHYSAC_MAX_MANIFOLDS 4096 -#define PHYSAC_MAX_VERTICES 24 -#define PHYSAC_CIRCLE_VERTICES 24 +#define PHYSAC_MAX_BODIES 64 // Maximum number of physic bodies supported +#define PHYSAC_MAX_MANIFOLDS 4096 // Maximum number of physic bodies interactions (64x64) +#define PHYSAC_MAX_VERTICES 24 // Maximum number of vertex for polygons shapes +#define PHYSAC_DEFAULT_CIRCLE_VERTICES 24 // Default number of vertices for circle shapes #define PHYSAC_COLLISION_ITERATIONS 100 #define PHYSAC_PENETRATION_ALLOWANCE 0.05f @@ -106,34 +115,28 @@ #define PHYSAC_PI 3.14159265358979323846 #define PHYSAC_DEG2RAD (PHYSAC_PI/180.0f) -//---------------------------------------------------------------------------------- -// Types and Structures Definition -// NOTE: Below types are required for PHYSAC_STANDALONE usage -//---------------------------------------------------------------------------------- -#if defined(PHYSAC_STANDALONE) - // Boolean type - #if defined(__STDC__) && __STDC_VERSION__ >= 199901L - #include - #elif !defined(__cplusplus) && !defined(bool) - typedef enum { false, true } bool; - #endif - - // Vector2 type - typedef struct Vector2 { - float x; - float y; - } Vector2; -#endif - //---------------------------------------------------------------------------------- // Data Types Structure Definition //---------------------------------------------------------------------------------- +#if defined(__STDC__) && __STDC_VERSION__ >= 199901L + #include +#elif !defined(__cplusplus) && !defined(bool) + typedef enum { false, true } bool; +#endif -typedef enum PhysicsShapeType { PHYSICS_CIRCLE, PHYSICS_POLYGON } PhysicsShapeType; +typedef enum PhysicsShapeType { PHYSICS_CIRCLE = 0, PHYSICS_POLYGON } PhysicsShapeType; // Previously defined to be used in PhysicsShape struct as circular dependencies typedef struct PhysicsBodyData *PhysicsBody; +#if defined(PHYSAC_DEFINE_VECTOR2_TYPE) +// Vector2 type +typedef struct Vector2 { + float x; + float y; +} Vector2; +#endif + // Matrix2x2 type (used for polygon shape rotation matrix) typedef struct Matrix2x2 { float m00; @@ -142,22 +145,22 @@ typedef struct Matrix2x2 { 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 PhysicsVertexData { + unsigned int vertexCount; // Vertex count (positions and normals) + Vector2 positions[PHYSAC_MAX_VERTICES]; // Vertex positions vectors + Vector2 normals[PHYSAC_MAX_VERTICES]; // Vertex normals vectors +} PhysicsVertexData; 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) + PhysicsShapeType type; // Shape type (circle or polygon) + PhysicsBody body; // Shape physics body data pointer + PhysicsVertexData vertexData; // Shape vertices data (used for polygon shapes) + float radius; // 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 + unsigned int id; // 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 @@ -175,11 +178,11 @@ typedef struct PhysicsBodyData { 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) + PhysicsShape shape; // Physics body shape information (type, radius, vertices, transform) } PhysicsBodyData; typedef struct PhysicsManifoldData { - unsigned int id; // Reference unique identifier + unsigned int id; // Unique identifier PhysicsBody bodyA; // Manifold first physics body reference PhysicsBody bodyB; // Manifold second physics body reference float penetration; // Depth of penetration from collision @@ -198,26 +201,32 @@ extern "C" { // Prevents name mangling of fun //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- -PHYSACDEF void InitPhysics(void); // Initializes physics values, pointers and creates physics loop thread -PHYSACDEF void RunPhysicsStep(void); // Run physics step, to be used if PHYSICS_NO_THREADS is set in your main loop +// Physics system management +PHYSACDEF void InitPhysics(void); // Initializes physics system +PHYSACDEF void UpdatePhysics(void); // Update physics system +PHYSACDEF void ResetPhysics(void); // Reset physics system (global variables) +PHYSACDEF void ClosePhysics(void); // Close physics system and unload used memory PHYSACDEF void SetPhysicsTimeStep(double delta); // Sets physics fixed time step in milliseconds. 1.666666 by default -PHYSACDEF bool IsPhysicsEnabled(void); // Returns true if physics thread is currently enabled PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force + +// Physic body creation/destroy PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new circle physics body with generic parameters PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density); // Creates a new rectangle physics body with generic parameters PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density); // Creates a new polygon physics body with generic parameters +PHYSACDEF void DestroyPhysicsBody(PhysicsBody body); // Destroy a physics body + +// Physic body forces PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force); // Adds a force to a physics body PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount); // Adds an angular force to a physics body PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force); // Shatters a polygon shape physics body to little physics bodies with explosion force -PHYSACDEF int GetPhysicsBodiesCount(void); // Returns the current amount of created physics bodies +PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Sets physics body shape transform based on radians parameter + +// Query physics info PHYSACDEF PhysicsBody GetPhysicsBody(int index); // Returns a physics body of the bodies pool at a specific index +PHYSACDEF int GetPhysicsBodiesCount(void); // Returns the current amount of created physics bodies PHYSACDEF int GetPhysicsShapeType(int index); // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) PHYSACDEF int GetPhysicsShapeVerticesCount(int index); // Returns the amount of vertices of a physics body shape PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex); // Returns transformed position of a body shape (body position + vertex transformed position) -PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Sets physics body shape transform based on radians parameter -PHYSACDEF void DestroyPhysicsBody(PhysicsBody body); // Unitializes and destroy a physics body -PHYSACDEF void ResetPhysics(void); // Destroys created physics bodies and manifolds and resets global values -PHYSACDEF void ClosePhysics(void); // Unitializes physics pointers and closes physics loop thread #if defined(__cplusplus) } @@ -233,151 +242,138 @@ PHYSACDEF void ClosePhysics(void); #if defined(PHYSAC_IMPLEMENTATION) -#if !defined(PHYSAC_NO_THREADS) - #include // Required for: pthread_t, pthread_create() -#endif - -#if defined(PHYSAC_DEBUG) - -#endif - // Support TRACELOG macros #if defined(PHYSAC_DEBUG) #include // Required for: printf() #define TRACELOG(...) printf(__VA_ARGS__) #else - #define TRACELOG(...) (void)0 + #define TRACELOG(...) (void)0; #endif -#include // Required for: malloc(), free(), srand(), rand() +#include // Required for: malloc(), calloc(), free() #include // Required for: cosf(), sinf(), fabs(), sqrtf() -#if !defined(PHYSAC_STANDALONE) - #include "raymath.h" // Required for: Vector2Add(), Vector2Subtract() +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) + // Time management functionality + #include // Required for: time(), clock_gettime() + #if defined(_WIN32) + // Functions required to query time on Windows + int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount); + int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency); + #elif defined(__linux__) + #if _POSIX_C_SOURCE < 199309L + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. + #endif + #include // Required for: timespec + #elif defined(__APPLE__) // macOS also defines __MACH__ + #include // Required for: mach_absolute_time() + #endif #endif -// Time management functionality -#include // Required for: time(), clock_gettime() -#if defined(_WIN32) - // Functions required to query time on Windows - int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount); - int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency); -#elif defined(__linux__) - #if _POSIX_C_SOURCE < 199309L - #undef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. - #endif - #include // Required for: timespec -#elif defined(__APPLE__) // macOS also defines __MACH__ - #include // Required for: mach_absolute_time() +// NOTE: MSVC C++ compiler does not support compound literals (C99 feature) +// Plain structures in C++ (without constructors) can be initialized from { } initializers. +#if defined(__cplusplus) + #define CLITERAL(type) type +#else + #define CLITERAL(type) (type) #endif //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define min(a,b) (((a)<(b))?(a):(b)) -#define max(a,b) (((a)>(b))?(a):(b)) -#define PHYSAC_FLT_MAX 3.402823466e+38f -#define PHYSAC_EPSILON 0.000001f -#define PHYSAC_K 1.0f/3.0f -#define PHYSAC_VECTOR_ZERO (Vector2){ 0.0f, 0.0f } +#define PHYSAC_MIN(a,b) (((a)<(b))?(a):(b)) +#define PHYSAC_MAX(a,b) (((a)>(b))?(a):(b)) +#define PHYSAC_FLT_MAX 3.402823466e+38f +#define PHYSAC_EPSILON 0.000001f +#define PHYSAC_K 1.0f/3.0f +#define PHYSAC_VECTOR_ZERO CLITERAL(Vector2){ 0.0f, 0.0f } //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -#if !defined(PHYSAC_NO_THREADS) -static pthread_t physicsThreadId; // Physics thread id -#endif -static unsigned int usedMemory = 0; // Total allocated dynamic memory -static bool physicsThreadEnabled = false; // Physics thread enabled state -static double baseTime = 0.0; // Offset time for MONOTONIC clock -static double startTime = 0.0; // Start time in milliseconds -static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time used for physics steps, in milliseconds -static double currentTime = 0.0; // Current time in milliseconds -static unsigned long long int frequency = 0; // Hi-res clock frequency +static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time in milliseconds used for physics steps -static double accumulator = 0.0; // Physics time step delta time accumulator -static unsigned int stepsCount = 0; // Total physics steps processed -static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) +// Time measure variables +static double baseClockTicks = 0.0; // Offset clock ticks for MONOTONIC clock +static unsigned long long int frequency = 0; // Hi-res clock frequency +static double startTime = 0.0; // Start time in milliseconds +static double currentTime = 0.0; // Current time in milliseconds +#endif + +// Physics system configuration static PhysicsBody bodies[PHYSAC_MAX_BODIES]; // Physics bodies pointers array static unsigned int physicsBodiesCount = 0; // Physics world current bodies counter static PhysicsManifold contacts[PHYSAC_MAX_MANIFOLDS]; // Physics bodies pointers array static unsigned int physicsManifoldsCount = 0; // Physics world current manifolds counter +static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force + +// Utilities variables +static unsigned int usedMemory = 0; // Total allocated dynamic memory + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) +// Timming measure functions +static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer +static unsigned long long int GetClockTicks(void); // Get hi-res MONOTONIC time measure in mseconds +static double GetCurrentTime(void); // Get current time measure in milliseconds +#endif + +static void UpdatePhysicsStep(void); // Update physics step (dynamics, collisions and position corrections) + static int FindAvailableBodyIndex(); // Finds a valid index for a new physics body initialization -static PolygonData CreateRandomPolygon(float radius, int sides); // Creates a random polygon shape with max vertex distance from polygon pivot -static PolygonData CreateRectanglePolygon(Vector2 pos, Vector2 size); // Creates a rectangle polygon shape based on a min and max positions -static void *PhysicsLoop(void *arg); // Physics loop thread function -static void PhysicsStep(void); // Physics steps calculations (dynamics, collisions and position corrections) static int FindAvailableManifoldIndex(); // Finds a valid index for a new manifold initialization +static PhysicsVertexData CreateDefaultPolygon(float radius, int sides); // Creates a random polygon shape with max vertex distance from polygon pivot +static PhysicsVertexData CreateRectanglePolygon(Vector2 pos, Vector2 size); // Creates a rectangle polygon shape based on a min and max positions + +static void InitializePhysicsManifolds(PhysicsManifold manifold); // Initializes physics manifolds to solve collisions static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b); // Creates a new physics manifold to solve collision static void DestroyPhysicsManifold(PhysicsManifold manifold); // Unitializes and destroys a physics manifold + static void SolvePhysicsManifold(PhysicsManifold manifold); // Solves a created physics manifold between two physics bodies static void SolveCircleToCircle(PhysicsManifold manifold); // Solves collision between two circle shape physics bodies static void SolveCircleToPolygon(PhysicsManifold manifold); // Solves collision between a circle to a polygon shape physics bodies static void SolvePolygonToCircle(PhysicsManifold manifold); // Solves collision between a polygon to a circle shape physics bodies static void SolvePolygonToPolygon(PhysicsManifold manifold); // Solves collision between two polygons shape physics bodies static void IntegratePhysicsForces(PhysicsBody body); // Integrates physics forces into velocity -static void InitializePhysicsManifolds(PhysicsManifold manifold); // Initializes physics manifolds to solve collisions -static void IntegratePhysicsImpulses(PhysicsManifold manifold); // Integrates physics collisions impulses to solve collisions static void IntegratePhysicsVelocity(PhysicsBody body); // Integrates physics velocity into position and forces +static void IntegratePhysicsImpulses(PhysicsManifold manifold); // Integrates physics collisions impulses to solve collisions static void CorrectPhysicsPositions(PhysicsManifold manifold); // Corrects physics bodies positions based on manifolds collision information -static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB); // Finds polygon shapes axis least penetration static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index); // Finds two polygon shapes incident face -static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB); // Calculates clipping based on a normal and two faces -static bool BiasGreaterThan(float valueA, float valueB); // Check if values are between bias range -static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points +static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB); // Finds polygon shapes axis least penetration -static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer -static unsigned long long int GetTimeCount(void); // Get hi-res MONOTONIC time measure in mseconds -static double GetCurrentTime(void); // Get current time measure in milliseconds - -// Math functions -static Vector2 MathCross(float value, Vector2 vector); // Returns the cross product of a vector and a value -static float MathCrossVector2(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors -static float MathLenSqr(Vector2 vector); // Returns the len square root of a vector -static float MathDot(Vector2 v1, Vector2 v2); // Returns the dot product of two vectors -static inline float DistSqr(Vector2 v1, Vector2 v2); // Returns the square root of distance between two vectors -static void MathNormalize(Vector2 *vector); // Returns the normalized values of a vector -#if defined(PHYSAC_STANDALONE) -static Vector2 Vector2Add(Vector2 v1, Vector2 v2); // Returns the sum of two given vectors -static Vector2 Vector2Subtract(Vector2 v1, Vector2 v2); // Returns the subtract of two given vectors -#endif - -static Matrix2x2 Mat2Radians(float radians); // Creates a matrix 2x2 from a given radians value -static void Mat2Set(Matrix2x2 *matrix, float radians); // Set values from radians to a created matrix 2x2 -static inline Matrix2x2 Mat2Transpose(Matrix2x2 matrix); // Returns the transpose of a given matrix 2x2 -static inline Vector2 Mat2MultiplyVector2(Matrix2x2 matrix, Vector2 vector); // Multiplies a vector by a matrix 2x2 +// Math required functions +static Vector2 MathVector2Product(Vector2 vector, float value); // Returns the product of a vector and a value +static float MathVector2CrossProduct(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors +static float MathVector2SqrLen(Vector2 vector); // Returns the len square root of a vector +static float MathVector2DotProduct(Vector2 v1, Vector2 v2); // Returns the dot product of two vectors +static inline float MathVector2SqrDistance(Vector2 v1, Vector2 v2); // Returns the square root of distance between two vectors +static void MathVector2Normalize(Vector2 *vector); // Returns the normalized values of a vector +static Vector2 MathVector2Add(Vector2 v1, Vector2 v2); // Returns the sum of two given vectors +static Vector2 MathVector2Subtract(Vector2 v1, Vector2 v2); // Returns the subtract of two given vectors +static Matrix2x2 MathMatFromRadians(float radians); // Returns a matrix 2x2 from a given radians value +static inline Matrix2x2 MathMatTranspose(Matrix2x2 matrix); // Returns the transpose of a given matrix 2x2 +static inline Vector2 MathMatVector2Product(Matrix2x2 matrix, Vector2 vector); // Returns product between matrix 2x2 and vector +static int MathVector2Clip(Vector2 normal, Vector2 *faceA, Vector2 *faceB, float clip); // Returns clipping value based on a normal and two faces +static Vector2 MathTriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- + // Initializes physics values, pointers and creates physics loop thread PHYSACDEF void InitPhysics(void) { - #if !defined(PHYSAC_NO_THREADS) - // NOTE: if defined, user will need to create a thread for PhysicsThread function manually - // Create physics thread using POSIXS thread libraries - pthread_create(&physicsThreadId, NULL, &PhysicsLoop, NULL); - #endif - +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) // Initialize high resolution timer InitTimer(); +#endif - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] physics module initialized successfully\n"); - #endif - - accumulator = 0.0; -} - -// Returns true if physics thread is currently enabled -PHYSACDEF bool IsPhysicsEnabled(void) -{ - return physicsThreadEnabled; + TRACELOG("[PHYSAC] Physics module initialized successfully\n"); } // Sets physics global gravity force @@ -390,47 +386,42 @@ PHYSACDEF void SetPhysicsGravity(float x, float y) // Creates a new circle physics body with generic parameters PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density) { - PhysicsBody newBody = CreatePhysicsBodyPolygon(pos, radius, PHYSAC_CIRCLE_VERTICES, density); - return newBody; + PhysicsBody body = CreatePhysicsBodyPolygon(pos, radius, PHYSAC_DEFAULT_CIRCLE_VERTICES, density); + return body; } // Creates a new rectangle physics body with generic parameters PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density) { - PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); + // NOTE: Make sure body data is initialized to 0 + PhysicsBody body = (PhysicsBody)PHYSAC_CALLOC(sizeof(PhysicsBodyData), 1); usedMemory += sizeof(PhysicsBodyData); - int newId = FindAvailableBodyIndex(); - if (newId != -1) + int id = FindAvailableBodyIndex(); + if (id != -1) { // Initialize new body with generic values - newBody->id = newId; - newBody->enabled = true; - newBody->position = pos; - newBody->velocity = (Vector2){ 0.0f, 0.0f }; - newBody->force = (Vector2){ 0.0f, 0.0f }; - newBody->angularVelocity = 0.0f; - newBody->torque = 0.0f; - newBody->orient = 0.0f; - newBody->shape.type = PHYSICS_POLYGON; - newBody->shape.body = newBody; - newBody->shape.radius = 0.0f; - newBody->shape.transform = Mat2Radians(0.0f); - newBody->shape.vertexData = CreateRectanglePolygon(pos, (Vector2){ width, height }); + body->id = id; + body->enabled = true; + body->position = pos; + body->shape.type = PHYSICS_POLYGON; + body->shape.body = body; + body->shape.transform = MathMatFromRadians(0.0f); + body->shape.vertexData = CreateRectanglePolygon(pos, CLITERAL(Vector2){ width, height }); // Calculate centroid and moment of inertia Vector2 center = { 0.0f, 0.0f }; float area = 0.0f; float inertia = 0.0f; - for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) + for (int i = 0; i < body->shape.vertexData.vertexCount; i++) { // Triangle vertices, third vertex implied as (0, 0) - Vector2 p1 = newBody->shape.vertexData.positions[i]; - int nextIndex = (((i + 1) < newBody->shape.vertexData.vertexCount) ? (i + 1) : 0); - Vector2 p2 = newBody->shape.vertexData.positions[nextIndex]; + Vector2 p1 = body->shape.vertexData.positions[i]; + int nextIndex = (((i + 1) < body->shape.vertexData.vertexCount) ? (i + 1) : 0); + Vector2 p2 = body->shape.vertexData.positions[nextIndex]; - float D = MathCrossVector2(p1, p2); + float D = MathVector2CrossProduct(p1, p2); float triangleArea = D/2; area += triangleArea; @@ -449,74 +440,70 @@ PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space) // Note: this is not really necessary - for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) + for (int i = 0; i < body->shape.vertexData.vertexCount; i++) { - newBody->shape.vertexData.positions[i].x -= center.x; - newBody->shape.vertexData.positions[i].y -= center.y; + body->shape.vertexData.positions[i].x -= center.x; + body->shape.vertexData.positions[i].y -= center.y; } - newBody->mass = density*area; - newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); - newBody->inertia = density*inertia; - newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); - newBody->staticFriction = 0.4f; - newBody->dynamicFriction = 0.2f; - newBody->restitution = 0.0f; - newBody->useGravity = true; - newBody->isGrounded = false; - newBody->freezeOrient = false; + body->mass = density*area; + body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f); + body->inertia = density*inertia; + body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f); + body->staticFriction = 0.4f; + body->dynamicFriction = 0.2f; + body->restitution = 0.0f; + body->useGravity = true; + body->isGrounded = false; + body->freezeOrient = false; // Add new body to bodies pointers array and update bodies count - bodies[physicsBodiesCount] = newBody; + bodies[physicsBodiesCount] = body; physicsBodiesCount++; - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] created polygon physics body id %i\n", newBody->id); - #endif + TRACELOG("[PHYSAC] Physic body created successfully (id: %i)\n", body->id); } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] new physics body creation failed because there is any available id to use\n"); - #endif + else TRACELOG("[PHYSAC] Physic body could not be created, PHYSAC_MAX_BODIES reached\n"); - return newBody; + return body; } // Creates a new polygon physics body with generic parameters PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density) { - PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); + PhysicsBody body = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); usedMemory += sizeof(PhysicsBodyData); - int newId = FindAvailableBodyIndex(); - if (newId != -1) + int id = FindAvailableBodyIndex(); + if (id != -1) { // Initialize new body with generic values - newBody->id = newId; - newBody->enabled = true; - newBody->position = pos; - newBody->velocity = PHYSAC_VECTOR_ZERO; - newBody->force = PHYSAC_VECTOR_ZERO; - newBody->angularVelocity = 0.0f; - newBody->torque = 0.0f; - newBody->orient = 0.0f; - newBody->shape.type = PHYSICS_POLYGON; - newBody->shape.body = newBody; - newBody->shape.transform = Mat2Radians(0.0f); - newBody->shape.vertexData = CreateRandomPolygon(radius, sides); + body->id = id; + body->enabled = true; + body->position = pos; + body->velocity = PHYSAC_VECTOR_ZERO; + body->force = PHYSAC_VECTOR_ZERO; + body->angularVelocity = 0.0f; + body->torque = 0.0f; + body->orient = 0.0f; + body->shape.type = PHYSICS_POLYGON; + body->shape.body = body; + body->shape.transform = MathMatFromRadians(0.0f); + body->shape.vertexData = CreateDefaultPolygon(radius, sides); // Calculate centroid and moment of inertia Vector2 center = { 0.0f, 0.0f }; float area = 0.0f; float inertia = 0.0f; - for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) + for (int i = 0; i < body->shape.vertexData.vertexCount; i++) { // Triangle vertices, third vertex implied as (0, 0) - Vector2 position1 = newBody->shape.vertexData.positions[i]; - int nextIndex = (((i + 1) < newBody->shape.vertexData.vertexCount) ? (i + 1) : 0); - Vector2 position2 = newBody->shape.vertexData.positions[nextIndex]; + Vector2 position1 = body->shape.vertexData.positions[i]; + int nextIndex = (((i + 1) < body->shape.vertexData.vertexCount) ? (i + 1) : 0); + Vector2 position2 = body->shape.vertexData.positions[nextIndex]; - float cross = MathCrossVector2(position1, position2); + float cross = MathVector2CrossProduct(position1, position2); float triangleArea = cross/2; area += triangleArea; @@ -535,42 +522,38 @@ PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int si // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space) // Note: this is not really necessary - for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) + for (int i = 0; i < body->shape.vertexData.vertexCount; i++) { - newBody->shape.vertexData.positions[i].x -= center.x; - newBody->shape.vertexData.positions[i].y -= center.y; + body->shape.vertexData.positions[i].x -= center.x; + body->shape.vertexData.positions[i].y -= center.y; } - newBody->mass = density*area; - newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); - newBody->inertia = density*inertia; - newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); - newBody->staticFriction = 0.4f; - newBody->dynamicFriction = 0.2f; - newBody->restitution = 0.0f; - newBody->useGravity = true; - newBody->isGrounded = false; - newBody->freezeOrient = false; + body->mass = density*area; + body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f); + body->inertia = density*inertia; + body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f); + body->staticFriction = 0.4f; + body->dynamicFriction = 0.2f; + body->restitution = 0.0f; + body->useGravity = true; + body->isGrounded = false; + body->freezeOrient = false; // Add new body to bodies pointers array and update bodies count - bodies[physicsBodiesCount] = newBody; + bodies[physicsBodiesCount] = body; physicsBodiesCount++; - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] created polygon physics body id %i\n", newBody->id); - #endif + TRACELOG("[PHYSAC] Physic body created successfully (id: %i)\n", body->id); } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] new physics body creation failed because there is any available id to use\n"); - #endif + else TRACELOG("[PHYSAC] Physics body could not be created, PHYSAC_MAX_BODIES reached\n"); - return newBody; + return body; } // Adds a force to a physics body PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force) { - if (body != NULL) body->force = Vector2Add(body->force, force); + if (body != NULL) body->force = MathVector2Add(body->force, force); } // Adds an angular force to a physics body @@ -586,15 +569,15 @@ PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) { if (body->shape.type == PHYSICS_POLYGON) { - PolygonData vertexData = body->shape.vertexData; + PhysicsVertexData vertexData = body->shape.vertexData; bool collision = false; for (int i = 0; i < vertexData.vertexCount; i++) { Vector2 positionA = body->position; - Vector2 positionB = Mat2MultiplyVector2(body->shape.transform, Vector2Add(body->position, vertexData.positions[i])); + Vector2 positionB = MathMatVector2Product(body->shape.transform, MathVector2Add(body->position, vertexData.positions[i])); int nextIndex = (((i + 1) < vertexData.vertexCount) ? (i + 1) : 0); - Vector2 positionC = Mat2MultiplyVector2(body->shape.transform, Vector2Add(body->position, vertexData.positions[nextIndex])); + Vector2 positionC = MathMatVector2Product(body->shape.transform, MathVector2Add(body->position, vertexData.positions[nextIndex])); // Check collision between each triangle float alpha = ((positionB.y - positionC.y)*(position.x - positionC.x) + (positionC.x - positionB.x)*(position.y - positionC.y))/ @@ -626,54 +609,54 @@ PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) for (int i = 0; i < count; i++) { int nextIndex = (((i + 1) < count) ? (i + 1) : 0); - Vector2 center = TriangleBarycenter(vertices[i], vertices[nextIndex], PHYSAC_VECTOR_ZERO); - center = Vector2Add(bodyPos, center); - Vector2 offset = Vector2Subtract(center, bodyPos); + Vector2 center = MathTriangleBarycenter(vertices[i], vertices[nextIndex], PHYSAC_VECTOR_ZERO); + center = MathVector2Add(bodyPos, center); + Vector2 offset = MathVector2Subtract(center, bodyPos); - PhysicsBody newBody = CreatePhysicsBodyPolygon(center, 10, 3, 10); // Create polygon physics body with relevant values + PhysicsBody body = CreatePhysicsBodyPolygon(center, 10, 3, 10); // Create polygon physics body with relevant values - PolygonData newData = { 0 }; - newData.vertexCount = 3; + PhysicsVertexData vertexData = { 0 }; + vertexData.vertexCount = 3; - newData.positions[0] = Vector2Subtract(vertices[i], offset); - newData.positions[1] = Vector2Subtract(vertices[nextIndex], offset); - newData.positions[2] = Vector2Subtract(position, center); + vertexData.positions[0] = MathVector2Subtract(vertices[i], offset); + vertexData.positions[1] = MathVector2Subtract(vertices[nextIndex], offset); + vertexData.positions[2] = MathVector2Subtract(position, center); // Separate vertices to avoid unnecessary physics collisions - newData.positions[0].x *= 0.95f; - newData.positions[0].y *= 0.95f; - newData.positions[1].x *= 0.95f; - newData.positions[1].y *= 0.95f; - newData.positions[2].x *= 0.95f; - newData.positions[2].y *= 0.95f; + vertexData.positions[0].x *= 0.95f; + vertexData.positions[0].y *= 0.95f; + vertexData.positions[1].x *= 0.95f; + vertexData.positions[1].y *= 0.95f; + vertexData.positions[2].x *= 0.95f; + vertexData.positions[2].y *= 0.95f; // Calculate polygon faces normals - for (int j = 0; j < newData.vertexCount; j++) + for (int j = 0; j < vertexData.vertexCount; j++) { - int nextVertex = (((j + 1) < newData.vertexCount) ? (j + 1) : 0); - Vector2 face = Vector2Subtract(newData.positions[nextVertex], newData.positions[j]); + int nextVertex = (((j + 1) < vertexData.vertexCount) ? (j + 1) : 0); + Vector2 face = MathVector2Subtract(vertexData.positions[nextVertex], vertexData.positions[j]); - newData.normals[j] = (Vector2){ face.y, -face.x }; - MathNormalize(&newData.normals[j]); + vertexData.normals[j] = CLITERAL(Vector2){ face.y, -face.x }; + MathVector2Normalize(&vertexData.normals[j]); } // Apply computed vertex data to new physics body shape - newBody->shape.vertexData = newData; - newBody->shape.transform = trans; + body->shape.vertexData = vertexData; + body->shape.transform = trans; // Calculate centroid and moment of inertia center = PHYSAC_VECTOR_ZERO; float area = 0.0f; float inertia = 0.0f; - for (int j = 0; j < newBody->shape.vertexData.vertexCount; j++) + for (int j = 0; j < body->shape.vertexData.vertexCount; j++) { // Triangle vertices, third vertex implied as (0, 0) - Vector2 p1 = newBody->shape.vertexData.positions[j]; - int nextVertex = (((j + 1) < newBody->shape.vertexData.vertexCount) ? (j + 1) : 0); - Vector2 p2 = newBody->shape.vertexData.positions[nextVertex]; + Vector2 p1 = body->shape.vertexData.positions[j]; + int nextVertex = (((j + 1) < body->shape.vertexData.vertexCount) ? (j + 1) : 0); + Vector2 p2 = body->shape.vertexData.positions[nextVertex]; - float D = MathCrossVector2(p1, p2); + float D = MathVector2CrossProduct(p1, p2); float triangleArea = D/2; area += triangleArea; @@ -690,32 +673,30 @@ PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) center.x *= 1.0f/area; center.y *= 1.0f/area; - newBody->mass = area; - newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); - newBody->inertia = inertia; - newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); + body->mass = area; + body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f); + body->inertia = inertia; + body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f); // Calculate explosion force direction - Vector2 pointA = newBody->position; - Vector2 pointB = Vector2Subtract(newData.positions[1], newData.positions[0]); + Vector2 pointA = body->position; + Vector2 pointB = MathVector2Subtract(vertexData.positions[1], vertexData.positions[0]); pointB.x /= 2.0f; pointB.y /= 2.0f; - Vector2 forceDirection = Vector2Subtract(Vector2Add(pointA, Vector2Add(newData.positions[0], pointB)), newBody->position); - MathNormalize(&forceDirection); + Vector2 forceDirection = MathVector2Subtract(MathVector2Add(pointA, MathVector2Add(vertexData.positions[0], pointB)), body->position); + MathVector2Normalize(&forceDirection); forceDirection.x *= force; forceDirection.y *= force; // Apply force to new physics body - PhysicsAddForce(newBody, forceDirection); + PhysicsAddForce(body, forceDirection); } PHYSAC_FREE(vertices); } } } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] error when trying to shatter a null reference physics body"); - #endif + else TRACELOG("[PHYSAC] WARNING: PhysicsShatter: NULL physic body\n"); } // Returns the current amount of created physics bodies @@ -733,16 +714,9 @@ PHYSACDEF PhysicsBody GetPhysicsBody(int index) { body = bodies[index]; - if (body == NULL) - { - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] error when trying to get a null reference physics body"); - #endif - } + if (body == NULL) TRACELOG("[PHYSAC] WARNING: GetPhysicsBody: NULL physic body\n"); } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] physics body index is out of bounds"); - #endif + else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n"); return body; } @@ -757,13 +731,9 @@ PHYSACDEF int GetPhysicsShapeType(int index) PhysicsBody body = bodies[index]; if (body != NULL) result = body->shape.type; - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] error when trying to get a null reference physics body"); - #endif + else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeType: NULL physic body\n"); } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] physics body index is out of bounds"); - #endif + else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n"); return result; } @@ -781,18 +751,14 @@ PHYSACDEF int GetPhysicsShapeVerticesCount(int index) { switch (body->shape.type) { - case PHYSICS_CIRCLE: result = PHYSAC_CIRCLE_VERTICES; break; + case PHYSICS_CIRCLE: result = PHYSAC_DEFAULT_CIRCLE_VERTICES; break; case PHYSICS_POLYGON: result = body->shape.vertexData.vertexCount; break; default: break; } } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] error when trying to get a null reference physics body"); - #endif + else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeVerticesCount: NULL physic body\n"); } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] physics body index is out of bounds"); - #endif + else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n"); return result; } @@ -808,20 +774,18 @@ PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex) { case PHYSICS_CIRCLE: { - position.x = body->position.x + cosf(360.0f/PHYSAC_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; - position.y = body->position.y + sinf(360.0f/PHYSAC_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; + position.x = body->position.x + cosf(360.0f/PHYSAC_DEFAULT_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; + position.y = body->position.y + sinf(360.0f/PHYSAC_DEFAULT_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; } break; case PHYSICS_POLYGON: { - PolygonData vertexData = body->shape.vertexData; - position = Vector2Add(body->position, Mat2MultiplyVector2(body->shape.transform, vertexData.positions[vertex])); + PhysicsVertexData vertexData = body->shape.vertexData; + position = MathVector2Add(body->position, MathMatVector2Product(body->shape.transform, vertexData.positions[vertex])); } break; default: break; } } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] error when trying to get a null reference physics body"); - #endif + else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeVertex: NULL physic body\n"); return position; } @@ -833,7 +797,7 @@ PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians) { body->orient = radians; - if (body->shape.type == PHYSICS_POLYGON) body->shape.transform = Mat2Radians(radians); + if (body->shape.type == PHYSICS_POLYGON) body->shape.transform = MathMatFromRadians(radians); } } @@ -856,9 +820,7 @@ PHYSACDEF void DestroyPhysicsBody(PhysicsBody body) if (index == -1) { - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] Not possible to find body id %i in pointers array\n", id); - #endif + TRACELOG("[PHYSAC] WARNING: Requested body (id: %i) can not be found\n", id); return; // Prevent access to index -1 } @@ -876,13 +838,9 @@ PHYSACDEF void DestroyPhysicsBody(PhysicsBody body) // Update physics bodies count physicsBodiesCount--; - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] destroyed physics body id %i\n", id); - #endif + TRACELOG("[PHYSAC] Physic body destroyed successfully (id: %i)\n", id); } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] error trying to destroy a null referenced body\n"); - #endif + else TRACELOG("[PHYSAC] WARNING: DestroyPhysicsBody: NULL physic body\n"); } // Destroys created physics bodies and manifolds and resets global values @@ -918,32 +876,28 @@ PHYSACDEF void ResetPhysics(void) physicsManifoldsCount = 0; - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] physics module reset successfully\n"); - #endif + TRACELOG("[PHYSAC] Physics module reseted successfully\n"); } // Unitializes physics pointers and exits physics loop thread PHYSACDEF void ClosePhysics(void) { - // Exit physics loop thread - physicsThreadEnabled = false; - - #if !defined(PHYSAC_NO_THREADS) - pthread_join(physicsThreadId, NULL); - #endif - // Unitialize physics manifolds dynamic memory allocations for (int i = physicsManifoldsCount - 1; i >= 0; i--) DestroyPhysicsManifold(contacts[i]); // Unitialize physics bodies dynamic memory allocations for (int i = physicsBodiesCount - 1; i >= 0; i--) DestroyPhysicsBody(bodies[i]); - #if defined(PHYSAC_DEBUG) - if (physicsBodiesCount > 0 || usedMemory != 0) TRACELOG("[PHYSAC] physics module closed with %i still allocated bodies [MEMORY: %i bytes]\n", physicsBodiesCount, usedMemory); - else if (physicsManifoldsCount > 0 || usedMemory != 0) TRACELOG("[PHYSAC] physics module closed with %i still allocated manifolds [MEMORY: %i bytes]\n", physicsManifoldsCount, usedMemory); - else TRACELOG("[PHYSAC] physics module closed successfully\n"); - #endif + // Trace log info + if ((physicsBodiesCount > 0) || (usedMemory != 0)) + { + TRACELOG("[PHYSAC] WARNING: Physics module closed with unallocated bodies (BODIES: %i, MEMORY: %i bytes)\n", physicsBodiesCount, usedMemory); + } + else if ((physicsManifoldsCount > 0) || (usedMemory != 0)) + { + TRACELOG("[PHYSAC] WARNING: Pysics module closed with unallocated manifolds (MANIFOLDS: %i, MEMORY: %i bytes)\n", physicsManifoldsCount, usedMemory); + } + else TRACELOG("[PHYSAC] Physics module closed successfully\n"); } //---------------------------------------------------------------------------------- @@ -978,10 +932,10 @@ static int FindAvailableBodyIndex() return index; } -// Creates a random polygon shape with max vertex distance from polygon pivot -static PolygonData CreateRandomPolygon(float radius, int sides) +// Creates a default polygon shape with max vertex distance from polygon pivot +static PhysicsVertexData CreateDefaultPolygon(float radius, int sides) { - PolygonData data = { 0 }; + PhysicsVertexData data = { 0 }; data.vertexCount = sides; // Calculate polygon vertices positions @@ -995,67 +949,43 @@ static PolygonData CreateRandomPolygon(float radius, int sides) for (int i = 0; i < data.vertexCount; i++) { int nextIndex = (((i + 1) < sides) ? (i + 1) : 0); - Vector2 face = Vector2Subtract(data.positions[nextIndex], data.positions[i]); + Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]); - data.normals[i] = (Vector2){ face.y, -face.x }; - MathNormalize(&data.normals[i]); + data.normals[i] = CLITERAL(Vector2){ face.y, -face.x }; + MathVector2Normalize(&data.normals[i]); } return data; } // Creates a rectangle polygon shape based on a min and max positions -static PolygonData CreateRectanglePolygon(Vector2 pos, Vector2 size) +static PhysicsVertexData CreateRectanglePolygon(Vector2 pos, Vector2 size) { - PolygonData data = { 0 }; + PhysicsVertexData data = { 0 }; data.vertexCount = 4; // Calculate polygon vertices positions - data.positions[0] = (Vector2){ pos.x + size.x/2, pos.y - size.y/2 }; - data.positions[1] = (Vector2){ pos.x + size.x/2, pos.y + size.y/2 }; - data.positions[2] = (Vector2){ pos.x - size.x/2, pos.y + size.y/2 }; - data.positions[3] = (Vector2){ pos.x - size.x/2, pos.y - size.y/2 }; + data.positions[0] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y - size.y/2 }; + data.positions[1] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y + size.y/2 }; + data.positions[2] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y + size.y/2 }; + data.positions[3] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y - size.y/2 }; // Calculate polygon faces normals for (int i = 0; i < data.vertexCount; i++) { int nextIndex = (((i + 1) < data.vertexCount) ? (i + 1) : 0); - Vector2 face = Vector2Subtract(data.positions[nextIndex], data.positions[i]); + Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]); - data.normals[i] = (Vector2){ face.y, -face.x }; - MathNormalize(&data.normals[i]); + data.normals[i] = CLITERAL(Vector2){ face.y, -face.x }; + MathVector2Normalize(&data.normals[i]); } return data; } -// Physics loop thread function -static void *PhysicsLoop(void *arg) +// Update physics step (dynamics, collisions and position corrections) +void UpdatePhysicsStep(void) { -#if !defined(PHYSAC_NO_THREADS) - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] physics thread created successfully\n"); - #endif - - // Initialize physics loop thread values - physicsThreadEnabled = true; - - // Physics update loop - while (physicsThreadEnabled) - { - RunPhysicsStep(); - } -#endif - - return NULL; -} - -// Physics steps calculations (dynamics, collisions and position corrections) -static void PhysicsStep(void) -{ - // Update current steps count - stepsCount++; - // Clear previous generated collisions information for (int i = physicsManifoldsCount - 1; i >= 0; i--) { @@ -1091,15 +1021,15 @@ static void PhysicsStep(void) if (manifold->contactsCount > 0) { // Create a new manifold with same information as previously solved manifold and add it to the manifolds pool last slot - PhysicsManifold newManifold = CreatePhysicsManifold(bodyA, bodyB); - newManifold->penetration = manifold->penetration; - newManifold->normal = manifold->normal; - newManifold->contacts[0] = manifold->contacts[0]; - newManifold->contacts[1] = manifold->contacts[1]; - newManifold->contactsCount = manifold->contactsCount; - newManifold->restitution = manifold->restitution; - newManifold->dynamicFriction = manifold->dynamicFriction; - newManifold->staticFriction = manifold->staticFriction; + PhysicsManifold manifold = CreatePhysicsManifold(bodyA, bodyB); + manifold->penetration = manifold->penetration; + manifold->normal = manifold->normal; + manifold->contacts[0] = manifold->contacts[0]; + manifold->contacts[1] = manifold->contacts[1]; + manifold->contactsCount = manifold->contactsCount; + manifold->restitution = manifold->restitution; + manifold->dynamicFriction = manifold->dynamicFriction; + manifold->staticFriction = manifold->staticFriction; } } } @@ -1156,31 +1086,34 @@ static void PhysicsStep(void) } } -// Wrapper to ensure PhysicsStep is run with at a fixed time step -PHYSACDEF void RunPhysicsStep(void) +// Update physics system +// Physics steps are launched at a fixed time step if enabled +PHYSACDEF void UpdatePhysics(void) { - // Calculate current time +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) + static double deltaTimeAccumulator = 0.0; + + // Calculate current time (ms) currentTime = GetCurrentTime(); - // Calculate current delta time + // Calculate current delta time (ms) const double delta = currentTime - startTime; // Store the time elapsed since the last frame began - accumulator += delta; + deltaTimeAccumulator += delta; // Fixed time stepping loop - while (accumulator >= deltaTime) + while (deltaTimeAccumulator >= deltaTime) { -#ifdef PHYSAC_DEBUG - //TRACELOG("currentTime %f, startTime %f, accumulator-pre %f, accumulator-post %f, delta %f, deltaTime %f\n", - // currentTime, startTime, accumulator, accumulator-deltaTime, delta, deltaTime); -#endif - PhysicsStep(); - accumulator -= deltaTime; + UpdatePhysicsStep(); + deltaTimeAccumulator -= deltaTime; } // Record the starting of this frame startTime = currentTime; +#else + UpdatePhysicsStep(); +#endif } PHYSACDEF void SetPhysicsTimeStep(double delta) @@ -1220,34 +1153,32 @@ static int FindAvailableManifoldIndex() // Creates a new physics manifold to solve collision static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b) { - PhysicsManifold newManifold = (PhysicsManifold)PHYSAC_MALLOC(sizeof(PhysicsManifoldData)); + PhysicsManifold manifold = (PhysicsManifold)PHYSAC_MALLOC(sizeof(PhysicsManifoldData)); usedMemory += sizeof(PhysicsManifoldData); - int newId = FindAvailableManifoldIndex(); - if (newId != -1) + int id = FindAvailableManifoldIndex(); + if (id != -1) { // Initialize new manifold with generic values - newManifold->id = newId; - newManifold->bodyA = a; - newManifold->bodyB = b; - newManifold->penetration = 0; - newManifold->normal = PHYSAC_VECTOR_ZERO; - newManifold->contacts[0] = PHYSAC_VECTOR_ZERO; - newManifold->contacts[1] = PHYSAC_VECTOR_ZERO; - newManifold->contactsCount = 0; - newManifold->restitution = 0.0f; - newManifold->dynamicFriction = 0.0f; - newManifold->staticFriction = 0.0f; + manifold->id = id; + manifold->bodyA = a; + manifold->bodyB = b; + manifold->penetration = 0; + manifold->normal = PHYSAC_VECTOR_ZERO; + manifold->contacts[0] = PHYSAC_VECTOR_ZERO; + manifold->contacts[1] = PHYSAC_VECTOR_ZERO; + manifold->contactsCount = 0; + manifold->restitution = 0.0f; + manifold->dynamicFriction = 0.0f; + manifold->staticFriction = 0.0f; // Add new body to bodies pointers array and update bodies count - contacts[physicsManifoldsCount] = newManifold; + contacts[physicsManifoldsCount] = manifold; physicsManifoldsCount++; } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] new physics manifold creation failed because there is any available id to use\n"); - #endif + else TRACELOG("[PHYSAC] Physic manifold could not be created, PHYSAC_MAX_MANIFOLDS reached\n"); - return newManifold; + return manifold; } // Unitializes and destroys a physics manifold @@ -1267,13 +1198,7 @@ static void DestroyPhysicsManifold(PhysicsManifold manifold) } } - if (index == -1) - { - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] Not possible to manifold id %i in pointers array\n", id); - #endif - return; // Prevent access to index -1 - } + if (index == -1) return; // Prevent access to index -1 // Free manifold allocated memory PHYSAC_FREE(manifold); @@ -1289,9 +1214,7 @@ static void DestroyPhysicsManifold(PhysicsManifold manifold) // Update physics manifolds count physicsManifoldsCount--; } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] error trying to destroy a null referenced manifold\n"); - #endif + else TRACELOG("[PHYSAC] WARNING: DestroyPhysicsManifold: NULL physic manifold\n"); } // Solves a created physics manifold between two physics bodies @@ -1333,9 +1256,9 @@ static void SolveCircleToCircle(PhysicsManifold manifold) if ((bodyA == NULL) || (bodyB == NULL)) return; // Calculate translational vector, which is normal - Vector2 normal = Vector2Subtract(bodyB->position, bodyA->position); + Vector2 normal = MathVector2Subtract(bodyB->position, bodyA->position); - float distSqr = MathLenSqr(normal); + float distSqr = MathVector2SqrLen(normal); float radius = bodyA->shape.radius + bodyB->shape.radius; // Check if circles are not in contact @@ -1351,14 +1274,14 @@ static void SolveCircleToCircle(PhysicsManifold manifold) if (distance == 0.0f) { manifold->penetration = bodyA->shape.radius; - manifold->normal = (Vector2){ 1.0f, 0.0f }; + manifold->normal = CLITERAL(Vector2){ 1.0f, 0.0f }; manifold->contacts[0] = bodyA->position; } else { manifold->penetration = radius - distance; - manifold->normal = (Vector2){ normal.x/distance, normal.y/distance }; // Faster than using MathNormalize() due to sqrt is already performed - manifold->contacts[0] = (Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; + manifold->normal = CLITERAL(Vector2){ normal.x/distance, normal.y/distance }; // Faster than using MathVector2Normalize() due to sqrt is already performed + manifold->contacts[0] = CLITERAL(Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; } // Update physics body grounded state if normal direction is down @@ -1377,17 +1300,17 @@ static void SolveCircleToPolygon(PhysicsManifold manifold) // Transform circle center to polygon transform space Vector2 center = bodyA->position; - center = Mat2MultiplyVector2(Mat2Transpose(bodyB->shape.transform), Vector2Subtract(center, bodyB->position)); + center = MathMatVector2Product(MathMatTranspose(bodyB->shape.transform), MathVector2Subtract(center, bodyB->position)); // Find edge with minimum penetration // It is the same concept as using support points in SolvePolygonToPolygon float separation = -PHYSAC_FLT_MAX; int faceNormal = 0; - PolygonData vertexData = bodyB->shape.vertexData; + PhysicsVertexData vertexData = bodyB->shape.vertexData; for (int i = 0; i < vertexData.vertexCount; i++) { - float currentSeparation = MathDot(vertexData.normals[i], Vector2Subtract(center, vertexData.positions[i])); + float currentSeparation = MathVector2DotProduct(vertexData.normals[i], MathVector2Subtract(center, vertexData.positions[i])); if (currentSeparation > bodyA->shape.radius) return; @@ -1407,53 +1330,53 @@ static void SolveCircleToPolygon(PhysicsManifold manifold) if (separation < PHYSAC_EPSILON) { manifold->contactsCount = 1; - Vector2 normal = Mat2MultiplyVector2(bodyB->shape.transform, vertexData.normals[faceNormal]); - manifold->normal = (Vector2){ -normal.x, -normal.y }; - manifold->contacts[0] = (Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; + Vector2 normal = MathMatVector2Product(bodyB->shape.transform, vertexData.normals[faceNormal]); + manifold->normal = CLITERAL(Vector2){ -normal.x, -normal.y }; + manifold->contacts[0] = CLITERAL(Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; manifold->penetration = bodyA->shape.radius; return; } // Determine which voronoi region of the edge center of circle lies within - float dot1 = MathDot(Vector2Subtract(center, v1), Vector2Subtract(v2, v1)); - float dot2 = MathDot(Vector2Subtract(center, v2), Vector2Subtract(v1, v2)); + float dot1 = MathVector2DotProduct(MathVector2Subtract(center, v1), MathVector2Subtract(v2, v1)); + float dot2 = MathVector2DotProduct(MathVector2Subtract(center, v2), MathVector2Subtract(v1, v2)); manifold->penetration = bodyA->shape.radius - separation; if (dot1 <= 0.0f) // Closest to v1 { - if (DistSqr(center, v1) > bodyA->shape.radius*bodyA->shape.radius) return; + if (MathVector2SqrDistance(center, v1) > bodyA->shape.radius*bodyA->shape.radius) return; manifold->contactsCount = 1; - Vector2 normal = Vector2Subtract(v1, center); - normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); - MathNormalize(&normal); + Vector2 normal = MathVector2Subtract(v1, center); + normal = MathMatVector2Product(bodyB->shape.transform, normal); + MathVector2Normalize(&normal); manifold->normal = normal; - v1 = Mat2MultiplyVector2(bodyB->shape.transform, v1); - v1 = Vector2Add(v1, bodyB->position); + v1 = MathMatVector2Product(bodyB->shape.transform, v1); + v1 = MathVector2Add(v1, bodyB->position); manifold->contacts[0] = v1; } else if (dot2 <= 0.0f) // Closest to v2 { - if (DistSqr(center, v2) > bodyA->shape.radius*bodyA->shape.radius) return; + if (MathVector2SqrDistance(center, v2) > bodyA->shape.radius*bodyA->shape.radius) return; manifold->contactsCount = 1; - Vector2 normal = Vector2Subtract(v2, center); - v2 = Mat2MultiplyVector2(bodyB->shape.transform, v2); - v2 = Vector2Add(v2, bodyB->position); + Vector2 normal = MathVector2Subtract(v2, center); + v2 = MathMatVector2Product(bodyB->shape.transform, v2); + v2 = MathVector2Add(v2, bodyB->position); manifold->contacts[0] = v2; - normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); - MathNormalize(&normal); + normal = MathMatVector2Product(bodyB->shape.transform, normal); + MathVector2Normalize(&normal); manifold->normal = normal; } else // Closest to face { Vector2 normal = vertexData.normals[faceNormal]; - if (MathDot(Vector2Subtract(center, v1), normal) > bodyA->shape.radius) return; + if (MathVector2DotProduct(MathVector2Subtract(center, v1), normal) > bodyA->shape.radius) return; - normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); - manifold->normal = (Vector2){ -normal.x, -normal.y }; - manifold->contacts[0] = (Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; + normal = MathMatVector2Product(bodyB->shape.transform, normal); + manifold->normal = CLITERAL(Vector2){ -normal.x, -normal.y }; + manifold->contacts[0] = CLITERAL(Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; manifold->contactsCount = 1; } } @@ -1500,7 +1423,8 @@ static void SolvePolygonToPolygon(PhysicsManifold manifold) PhysicsShape incPoly; // Incident // Determine which shape contains reference face - if (BiasGreaterThan(penetrationA, penetrationB)) + // Checking bias range for penetration + if (penetrationA >= (penetrationB*0.95f + penetrationA*0.01f)) { refPoly = bodyA; incPoly = bodyB; @@ -1519,37 +1443,37 @@ static void SolvePolygonToPolygon(PhysicsManifold manifold) FindIncidentFace(&incidentFace[0], &incidentFace[1], refPoly, incPoly, referenceIndex); // Setup reference face vertices - PolygonData refData = refPoly.vertexData; + PhysicsVertexData refData = refPoly.vertexData; Vector2 v1 = refData.positions[referenceIndex]; referenceIndex = (((referenceIndex + 1) < refData.vertexCount) ? (referenceIndex + 1) : 0); Vector2 v2 = refData.positions[referenceIndex]; // Transform vertices to world space - v1 = Mat2MultiplyVector2(refPoly.transform, v1); - v1 = Vector2Add(v1, refPoly.body->position); - v2 = Mat2MultiplyVector2(refPoly.transform, v2); - v2 = Vector2Add(v2, refPoly.body->position); + v1 = MathMatVector2Product(refPoly.transform, v1); + v1 = MathVector2Add(v1, refPoly.body->position); + v2 = MathMatVector2Product(refPoly.transform, v2); + v2 = MathVector2Add(v2, refPoly.body->position); // Calculate reference face side normal in world space - Vector2 sidePlaneNormal = Vector2Subtract(v2, v1); - MathNormalize(&sidePlaneNormal); + Vector2 sidePlaneNormal = MathVector2Subtract(v2, v1); + MathVector2Normalize(&sidePlaneNormal); // Orthogonalize Vector2 refFaceNormal = { sidePlaneNormal.y, -sidePlaneNormal.x }; - float refC = MathDot(refFaceNormal, v1); - float negSide = MathDot(sidePlaneNormal, v1)*-1; - float posSide = MathDot(sidePlaneNormal, v2); + float refC = MathVector2DotProduct(refFaceNormal, v1); + float negSide = MathVector2DotProduct(sidePlaneNormal, v1)*-1; + float posSide = MathVector2DotProduct(sidePlaneNormal, v2); - // Clip incident face to reference face side planes (due to floating point error, possible to not have required points - if (Clip((Vector2){ -sidePlaneNormal.x, -sidePlaneNormal.y }, negSide, &incidentFace[0], &incidentFace[1]) < 2) return; - if (Clip(sidePlaneNormal, posSide, &incidentFace[0], &incidentFace[1]) < 2) return; + // MathVector2Clip incident face to reference face side planes (due to floating point error, possible to not have required points + if (MathVector2Clip(CLITERAL(Vector2){ -sidePlaneNormal.x, -sidePlaneNormal.y }, &incidentFace[0], &incidentFace[1], negSide) < 2) return; + if (MathVector2Clip(sidePlaneNormal, &incidentFace[0], &incidentFace[1], posSide) < 2) return; // Flip normal if required - manifold->normal = (flip ? (Vector2){ -refFaceNormal.x, -refFaceNormal.y } : refFaceNormal); + manifold->normal = (flip ? CLITERAL(Vector2){ -refFaceNormal.x, -refFaceNormal.y } : refFaceNormal); // Keep points behind reference face - int currentPoint = 0; // Clipped points behind reference face - float separation = MathDot(refFaceNormal, incidentFace[0]) - refC; + int currentPoint = 0; // MathVector2Clipped points behind reference face + float separation = MathVector2DotProduct(refFaceNormal, incidentFace[0]) - refC; if (separation <= 0.0f) { manifold->contacts[currentPoint] = incidentFace[0]; @@ -1558,7 +1482,7 @@ static void SolvePolygonToPolygon(PhysicsManifold manifold) } else manifold->penetration = 0.0f; - separation = MathDot(refFaceNormal, incidentFace[1]) - refC; + separation = MathVector2DotProduct(refFaceNormal, incidentFace[1]) - refC; if (separation <= 0.0f) { @@ -1606,11 +1530,11 @@ static void InitializePhysicsManifolds(PhysicsManifold manifold) for (int i = 0; i < manifold->contactsCount; i++) { // Caculate radius from center of mass to contact - Vector2 radiusA = Vector2Subtract(manifold->contacts[i], bodyA->position); - Vector2 radiusB = Vector2Subtract(manifold->contacts[i], bodyB->position); + Vector2 radiusA = MathVector2Subtract(manifold->contacts[i], bodyA->position); + Vector2 radiusB = MathVector2Subtract(manifold->contacts[i], bodyB->position); - Vector2 crossA = MathCross(bodyA->angularVelocity, radiusA); - Vector2 crossB = MathCross(bodyB->angularVelocity, radiusB); + Vector2 crossA = MathVector2Product(radiusA, bodyA->angularVelocity); + Vector2 crossB = MathVector2Product(radiusB, bodyB->angularVelocity); Vector2 radiusV = { 0.0f, 0.0f }; radiusV.x = bodyB->velocity.x + crossB.x - bodyA->velocity.x - crossA.x; @@ -1618,7 +1542,7 @@ static void InitializePhysicsManifolds(PhysicsManifold manifold) // Determine if we should perform a resting collision or not; // The idea is if the only thing moving this object is gravity, then the collision should be performed without any restitution - if (MathLenSqr(radiusV) < (MathLenSqr((Vector2){ gravityForce.x*deltaTime/1000, gravityForce.y*deltaTime/1000 }) + PHYSAC_EPSILON)) manifold->restitution = 0; + if (MathVector2SqrLen(radiusV) < (MathVector2SqrLen(CLITERAL(Vector2){ gravityForce.x*deltaTime/1000, gravityForce.y*deltaTime/1000 }) + PHYSAC_EPSILON)) manifold->restitution = 0; } } @@ -1641,22 +1565,22 @@ static void IntegratePhysicsImpulses(PhysicsManifold manifold) for (int i = 0; i < manifold->contactsCount; i++) { // Calculate radius from center of mass to contact - Vector2 radiusA = Vector2Subtract(manifold->contacts[i], bodyA->position); - Vector2 radiusB = Vector2Subtract(manifold->contacts[i], bodyB->position); + Vector2 radiusA = MathVector2Subtract(manifold->contacts[i], bodyA->position); + Vector2 radiusB = MathVector2Subtract(manifold->contacts[i], bodyB->position); // Calculate relative velocity Vector2 radiusV = { 0.0f, 0.0f }; - radiusV.x = bodyB->velocity.x + MathCross(bodyB->angularVelocity, radiusB).x - bodyA->velocity.x - MathCross(bodyA->angularVelocity, radiusA).x; - radiusV.y = bodyB->velocity.y + MathCross(bodyB->angularVelocity, radiusB).y - bodyA->velocity.y - MathCross(bodyA->angularVelocity, radiusA).y; + radiusV.x = bodyB->velocity.x + MathVector2Product(radiusB, bodyB->angularVelocity).x - bodyA->velocity.x - MathVector2Product(radiusA, bodyA->angularVelocity).x; + radiusV.y = bodyB->velocity.y + MathVector2Product(radiusB, bodyB->angularVelocity).y - bodyA->velocity.y - MathVector2Product(radiusA, bodyA->angularVelocity).y; // Relative velocity along the normal - float contactVelocity = MathDot(radiusV, manifold->normal); + float contactVelocity = MathVector2DotProduct(radiusV, manifold->normal); // Do not resolve if velocities are separating if (contactVelocity > 0.0f) return; - float raCrossN = MathCrossVector2(radiusA, manifold->normal); - float rbCrossN = MathCrossVector2(radiusB, manifold->normal); + float raCrossN = MathVector2CrossProduct(radiusA, manifold->normal); + float rbCrossN = MathVector2CrossProduct(radiusB, manifold->normal); float inverseMassSum = bodyA->inverseMass + bodyB->inverseMass + (raCrossN*raCrossN)*bodyA->inverseInertia + (rbCrossN*rbCrossN)*bodyB->inverseInertia; @@ -1672,25 +1596,25 @@ static void IntegratePhysicsImpulses(PhysicsManifold manifold) { bodyA->velocity.x += bodyA->inverseMass*(-impulseV.x); bodyA->velocity.y += bodyA->inverseMass*(-impulseV.y); - if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathCrossVector2(radiusA, (Vector2){ -impulseV.x, -impulseV.y }); + if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathVector2CrossProduct(radiusA, CLITERAL(Vector2){ -impulseV.x, -impulseV.y }); } if (bodyB->enabled) { bodyB->velocity.x += bodyB->inverseMass*(impulseV.x); bodyB->velocity.y += bodyB->inverseMass*(impulseV.y); - if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathCrossVector2(radiusB, impulseV); + if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathVector2CrossProduct(radiusB, impulseV); } // Apply friction impulse to each physics body - radiusV.x = bodyB->velocity.x + MathCross(bodyB->angularVelocity, radiusB).x - bodyA->velocity.x - MathCross(bodyA->angularVelocity, radiusA).x; - radiusV.y = bodyB->velocity.y + MathCross(bodyB->angularVelocity, radiusB).y - bodyA->velocity.y - MathCross(bodyA->angularVelocity, radiusA).y; + radiusV.x = bodyB->velocity.x + MathVector2Product(radiusB, bodyB->angularVelocity).x - bodyA->velocity.x - MathVector2Product(radiusA, bodyA->angularVelocity).x; + radiusV.y = bodyB->velocity.y + MathVector2Product(radiusB, bodyB->angularVelocity).y - bodyA->velocity.y - MathVector2Product(radiusA, bodyA->angularVelocity).y; - Vector2 tangent = { radiusV.x - (manifold->normal.x*MathDot(radiusV, manifold->normal)), radiusV.y - (manifold->normal.y*MathDot(radiusV, manifold->normal)) }; - MathNormalize(&tangent); + Vector2 tangent = { radiusV.x - (manifold->normal.x*MathVector2DotProduct(radiusV, manifold->normal)), radiusV.y - (manifold->normal.y*MathVector2DotProduct(radiusV, manifold->normal)) }; + MathVector2Normalize(&tangent); // Calculate impulse tangent magnitude - float impulseTangent = -MathDot(radiusV, tangent); + float impulseTangent = -MathVector2DotProduct(radiusV, tangent); impulseTangent /= inverseMassSum; impulseTangent /= (float)manifold->contactsCount; @@ -1701,8 +1625,8 @@ static void IntegratePhysicsImpulses(PhysicsManifold manifold) // Apply coulumb's law Vector2 tangentImpulse = { 0.0f, 0.0f }; - if (absImpulseTangent < impulse*manifold->staticFriction) tangentImpulse = (Vector2){ tangent.x*impulseTangent, tangent.y*impulseTangent }; - else tangentImpulse = (Vector2){ tangent.x*-impulse*manifold->dynamicFriction, tangent.y*-impulse*manifold->dynamicFriction }; + if (absImpulseTangent < impulse*manifold->staticFriction) tangentImpulse = CLITERAL(Vector2){ tangent.x*impulseTangent, tangent.y*impulseTangent }; + else tangentImpulse = CLITERAL(Vector2){ tangent.x*-impulse*manifold->dynamicFriction, tangent.y*-impulse*manifold->dynamicFriction }; // Apply friction impulse if (bodyA->enabled) @@ -1710,7 +1634,7 @@ static void IntegratePhysicsImpulses(PhysicsManifold manifold) bodyA->velocity.x += bodyA->inverseMass*(-tangentImpulse.x); bodyA->velocity.y += bodyA->inverseMass*(-tangentImpulse.y); - if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathCrossVector2(radiusA, (Vector2){ -tangentImpulse.x, -tangentImpulse.y }); + if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathVector2CrossProduct(radiusA, CLITERAL(Vector2){ -tangentImpulse.x, -tangentImpulse.y }); } if (bodyB->enabled) @@ -1718,7 +1642,7 @@ static void IntegratePhysicsImpulses(PhysicsManifold manifold) bodyB->velocity.x += bodyB->inverseMass*(tangentImpulse.x); bodyB->velocity.y += bodyB->inverseMass*(tangentImpulse.y); - if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathCrossVector2(radiusB, tangentImpulse); + if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathVector2CrossProduct(radiusB, tangentImpulse); } } } @@ -1732,7 +1656,7 @@ static void IntegratePhysicsVelocity(PhysicsBody body) body->position.y += body->velocity.y*deltaTime; if (!body->freezeOrient) body->orient += body->angularVelocity*deltaTime; - Mat2Set(&body->shape.transform, body->orient); + body->shape.transform = MathMatFromRadians(body->orient); IntegratePhysicsForces(body); } @@ -1746,8 +1670,8 @@ static void CorrectPhysicsPositions(PhysicsManifold manifold) if ((bodyA == NULL) || (bodyB == NULL)) return; Vector2 correction = { 0.0f, 0.0f }; - correction.x = (max(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.x*PHYSAC_PENETRATION_CORRECTION; - correction.y = (max(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.y*PHYSAC_PENETRATION_CORRECTION; + correction.x = (PHYSAC_MAX(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.x*PHYSAC_PENETRATION_CORRECTION; + correction.y = (PHYSAC_MAX(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.y*PHYSAC_PENETRATION_CORRECTION; if (bodyA->enabled) { @@ -1767,12 +1691,12 @@ static Vector2 GetSupport(PhysicsShape shape, Vector2 dir) { float bestProjection = -PHYSAC_FLT_MAX; Vector2 bestVertex = { 0.0f, 0.0f }; - PolygonData data = shape.vertexData; + PhysicsVertexData data = shape.vertexData; for (int i = 0; i < data.vertexCount; i++) { Vector2 vertex = data.positions[i]; - float projection = MathDot(vertex, dir); + float projection = MathVector2DotProduct(vertex, dir); if (projection > bestProjection) { @@ -1790,31 +1714,31 @@ static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, Physi float bestDistance = -PHYSAC_FLT_MAX; int bestIndex = 0; - PolygonData dataA = shapeA.vertexData; - //PolygonData dataB = shapeB.vertexData; + PhysicsVertexData dataA = shapeA.vertexData; + //PhysicsVertexData dataB = shapeB.vertexData; for (int i = 0; i < dataA.vertexCount; i++) { // Retrieve a face normal from A shape Vector2 normal = dataA.normals[i]; - Vector2 transNormal = Mat2MultiplyVector2(shapeA.transform, normal); + Vector2 transNormal = MathMatVector2Product(shapeA.transform, normal); // Transform face normal into B shape's model space - Matrix2x2 buT = Mat2Transpose(shapeB.transform); - normal = Mat2MultiplyVector2(buT, transNormal); + Matrix2x2 buT = MathMatTranspose(shapeB.transform); + normal = MathMatVector2Product(buT, transNormal); // Retrieve support point from B shape along -n - Vector2 support = GetSupport(shapeB, (Vector2){ -normal.x, -normal.y }); + Vector2 support = GetSupport(shapeB, CLITERAL(Vector2){ -normal.x, -normal.y }); // Retrieve vertex on face from A shape, transform into B shape's model space Vector2 vertex = dataA.positions[i]; - vertex = Mat2MultiplyVector2(shapeA.transform, vertex); - vertex = Vector2Add(vertex, shapeA.body->position); - vertex = Vector2Subtract(vertex, shapeB.body->position); - vertex = Mat2MultiplyVector2(buT, vertex); + vertex = MathMatVector2Product(shapeA.transform, vertex); + vertex = MathVector2Add(vertex, shapeA.body->position); + vertex = MathVector2Subtract(vertex, shapeB.body->position); + vertex = MathMatVector2Product(buT, vertex); // Compute penetration distance in B shape's model space - float distance = MathDot(normal, Vector2Subtract(support, vertex)); + float distance = MathVector2DotProduct(normal, MathVector2Subtract(support, vertex)); // Store greatest distance if (distance > bestDistance) @@ -1831,14 +1755,14 @@ static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, Physi // Finds two polygon shapes incident face static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index) { - PolygonData refData = ref.vertexData; - PolygonData incData = inc.vertexData; + PhysicsVertexData refData = ref.vertexData; + PhysicsVertexData incData = inc.vertexData; Vector2 referenceNormal = refData.normals[index]; // Calculate normal in incident's frame of reference - referenceNormal = Mat2MultiplyVector2(ref.transform, referenceNormal); // To world space - referenceNormal = Mat2MultiplyVector2(Mat2Transpose(inc.transform), referenceNormal); // To incident's model space + referenceNormal = MathMatVector2Product(ref.transform, referenceNormal); // To world space + referenceNormal = MathMatVector2Product(MathMatTranspose(inc.transform), referenceNormal); // To incident's model space // Find most anti-normal face on polygon int incidentFace = 0; @@ -1846,7 +1770,7 @@ static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, Physics for (int i = 0; i < incData.vertexCount; i++) { - float dot = MathDot(referenceNormal, incData.normals[i]); + float dot = MathVector2DotProduct(referenceNormal, incData.normals[i]); if (dot < minDot) { @@ -1856,22 +1780,22 @@ static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, Physics } // Assign face vertices for incident face - *v0 = Mat2MultiplyVector2(inc.transform, incData.positions[incidentFace]); - *v0 = Vector2Add(*v0, inc.body->position); + *v0 = MathMatVector2Product(inc.transform, incData.positions[incidentFace]); + *v0 = MathVector2Add(*v0, inc.body->position); incidentFace = (((incidentFace + 1) < incData.vertexCount) ? (incidentFace + 1) : 0); - *v1 = Mat2MultiplyVector2(inc.transform, incData.positions[incidentFace]); - *v1 = Vector2Add(*v1, inc.body->position); + *v1 = MathMatVector2Product(inc.transform, incData.positions[incidentFace]); + *v1 = MathVector2Add(*v1, inc.body->position); } -// Calculates clipping based on a normal and two faces -static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB) +// Returns clipping value based on a normal and two faces +static int MathVector2Clip(Vector2 normal, Vector2 *faceA, Vector2 *faceB, float clip) { int sp = 0; Vector2 out[2] = { *faceA, *faceB }; // Retrieve distances from each endpoint to the line - float distanceA = MathDot(normal, *faceA) - clip; - float distanceB = MathDot(normal, *faceB) - clip; + float distanceA = MathVector2DotProduct(normal, *faceA) - clip; + float distanceB = MathVector2DotProduct(normal, *faceB) - clip; // If negative (behind plane) if (distanceA <= 0.0f) out[sp++] = *faceA; @@ -1883,10 +1807,10 @@ static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB) // Push intersection point float alpha = distanceA/(distanceA - distanceB); out[sp] = *faceA; - Vector2 delta = Vector2Subtract(*faceB, *faceA); + Vector2 delta = MathVector2Subtract(*faceB, *faceA); delta.x *= alpha; delta.y *= alpha; - out[sp] = Vector2Add(out[sp], delta); + out[sp] = MathVector2Add(out[sp], delta); sp++; } @@ -1897,14 +1821,8 @@ static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB) return sp; } -// Check if values are between bias range -static bool BiasGreaterThan(float valueA, float valueB) -{ - return (valueA >= (valueB*0.95f + valueA*0.01f)); -} - // Returns the barycenter of a triangle given by 3 points -static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3) +static Vector2 MathTriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3) { Vector2 result = { 0.0f, 0.0f }; @@ -1914,11 +1832,10 @@ static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3) return result; } +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) // Initializes hi-resolution MONOTONIC timer static void InitTimer(void) { - srand(time(NULL)); // Initialize random seed - #if defined(_WIN32) QueryPerformanceFrequency((unsigned long long int *) &frequency); #endif @@ -1934,12 +1851,12 @@ static void InitTimer(void) frequency = (timebase.denom*1e9)/timebase.numer; #endif - baseTime = GetTimeCount(); // Get MONOTONIC clock time offset - startTime = GetCurrentTime(); // Get current time + baseClockTicks = GetClockTicks(); // Get MONOTONIC clock time offset + startTime = GetCurrentTime(); // Get current time in milliseconds } -// Get hi-res MONOTONIC time measure in seconds -static unsigned long long int GetTimeCount(void) +// Get hi-res MONOTONIC time measure in clock ticks +static unsigned long long int GetClockTicks(void) { unsigned long long int value = 0; @@ -1963,42 +1880,45 @@ static unsigned long long int GetTimeCount(void) // Get current time in milliseconds static double GetCurrentTime(void) { - return (double)(GetTimeCount() - baseTime)/frequency*1000; + return (double)(GetClockTicks() - baseClockTicks)/frequency*1000; } +#endif // !PHYSAC_AVOID_TIMMING_SYSTEM + // Returns the cross product of a vector and a value -static inline Vector2 MathCross(float value, Vector2 vector) +static inline Vector2 MathVector2Product(Vector2 vector, float value) { - return (Vector2){ -value*vector.y, value*vector.x }; + Vector2 result = { -value*vector.y, value*vector.x }; + return result; } // Returns the cross product of two vectors -static inline float MathCrossVector2(Vector2 v1, Vector2 v2) +static inline float MathVector2CrossProduct(Vector2 v1, Vector2 v2) { return (v1.x*v2.y - v1.y*v2.x); } // Returns the len square root of a vector -static inline float MathLenSqr(Vector2 vector) +static inline float MathVector2SqrLen(Vector2 vector) { return (vector.x*vector.x + vector.y*vector.y); } // Returns the dot product of two vectors -static inline float MathDot(Vector2 v1, Vector2 v2) +static inline float MathVector2DotProduct(Vector2 v1, Vector2 v2) { return (v1.x*v2.x + v1.y*v2.y); } // Returns the square root of distance between two vectors -static inline float DistSqr(Vector2 v1, Vector2 v2) +static inline float MathVector2SqrDistance(Vector2 v1, Vector2 v2) { - Vector2 dir = Vector2Subtract(v1, v2); - return MathDot(dir, dir); + Vector2 dir = MathVector2Subtract(v1, v2); + return MathVector2DotProduct(dir, dir); } // Returns the normalized values of a vector -static void MathNormalize(Vector2 *vector) +static void MathVector2Normalize(Vector2 *vector) { float length, ilength; @@ -2013,51 +1933,42 @@ static void MathNormalize(Vector2 *vector) vector->y *= ilength; } -#if defined(PHYSAC_STANDALONE) // Returns the sum of two given vectors -static inline Vector2 Vector2Add(Vector2 v1, Vector2 v2) +static inline Vector2 MathVector2Add(Vector2 v1, Vector2 v2) { - return (Vector2){ v1.x + v2.x, v1.y + v2.y }; + Vector2 result = { v1.x + v2.x, v1.y + v2.y }; + return result; } // Returns the subtract of two given vectors -static inline Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) +static inline Vector2 MathVector2Subtract(Vector2 v1, Vector2 v2) { - return (Vector2){ v1.x - v2.x, v1.y - v2.y }; + Vector2 result = { v1.x - v2.x, v1.y - v2.y }; + return result; } -#endif // Creates a matrix 2x2 from a given radians value -static Matrix2x2 Mat2Radians(float radians) -{ - float c = cosf(radians); - float s = sinf(radians); - - return (Matrix2x2){ c, -s, s, c }; -} - -// Set values from radians to a created matrix 2x2 -static void Mat2Set(Matrix2x2 *matrix, float radians) +static Matrix2x2 MathMatFromRadians(float radians) { float cos = cosf(radians); float sin = sinf(radians); - matrix->m00 = cos; - matrix->m01 = -sin; - matrix->m10 = sin; - matrix->m11 = cos; + Matrix2x2 result = { cos, -sin, sin, cos }; + return result; } // Returns the transpose of a given matrix 2x2 -static inline Matrix2x2 Mat2Transpose(Matrix2x2 matrix) +static inline Matrix2x2 MathMatTranspose(Matrix2x2 matrix) { - return (Matrix2x2){ matrix.m00, matrix.m10, matrix.m01, matrix.m11 }; + Matrix2x2 result = { matrix.m00, matrix.m10, matrix.m01, matrix.m11 }; + return result; } // Multiplies a vector by a matrix 2x2 -static inline Vector2 Mat2MultiplyVector2(Matrix2x2 matrix, Vector2 vector) +static inline Vector2 MathMatVector2Product(Matrix2x2 matrix, Vector2 vector) { - return (Vector2){ matrix.m00*vector.x + matrix.m01*vector.y, matrix.m10*vector.x + matrix.m11*vector.y }; + Vector2 result = { matrix.m00*vector.x + matrix.m01*vector.y, matrix.m10*vector.x + matrix.m11*vector.y }; + return result; } #endif // PHYSAC_IMPLEMENTATION From 18ab694f703ee4b185935b5bdd5a533ea05933d2 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 21 Jan 2021 14:39:03 +0100 Subject: [PATCH 33/43] ADDED: SetGamepadMappings() #1506 --- src/core.c | 8 ++++++++ src/raylib.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/core.c b/src/core.c index 4162e8936..5549fc9d2 100644 --- a/src/core.c +++ b/src/core.c @@ -2976,6 +2976,14 @@ int GetGamepadButtonPressed(void) return CORE.Input.Gamepad.lastButtonPressed; } +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ +#if defined(PLATFORM_DESKTOP) + return glfwUpdateGamepadMappings(mappings); +#endif +} + // Detect if a mouse button has been pressed once bool IsMouseButtonPressed(int button) { diff --git a/src/raylib.h b/src/raylib.h index 24992a4f6..4e72fb6b8 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1035,6 +1035,7 @@ RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Detect if a gam RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed RLAPI int GetGamepadAxisCount(int gamepad); // Return gamepad axis count for a gamepad RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Return axis movement value for a gamepad axis +RLAPI int SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB) // Input-related functions: mouse RLAPI bool IsMouseButtonPressed(int button); // Detect if a mouse button has been pressed once From 05dfbf3cd42e63f56d978cafec63f22fd7538e9f Mon Sep 17 00:00:00 2001 From: hristo Date: Fri, 22 Jan 2021 01:07:22 +0200 Subject: [PATCH 34/43] Remove STATIC and SHARED variables. (#1542) As described in the official documentation https://cmake.org/cmake/help/v3.0/variable/BUILD_SHARED_LIBS.html this flag is global by default and controls if the library will be built as a shared or a static library allowing us to define only one call to the add_library function (without specifying its type). It is also added as an option to be visible in CMake GUI applications. --- CMakeOptions.txt | 3 +-- cmake/BuildOptions.cmake | 21 --------------------- cmake/GlfwImport.cmake | 7 ++++++- cmake/InstallConfigurations.cmake | 4 ++-- src/CMakeLists.txt | 20 +++++++------------- 5 files changed, 16 insertions(+), 39 deletions(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index f39d17a1e..7ab43de25 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -14,8 +14,7 @@ option(ENABLE_MSAN "Enable MemorySanitizer (MSan) for debugging (not recommended # 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) -option(STATIC "Build raylib as a static library" ON) +option(BUILD_SHARED_LIBS "Build raylib as a shared library" OFF) option(MACOS_FATLIB "Build fat library for both i386 and x86_64 on macOS" OFF) option(USE_AUDIO "Build raylib with audio module" ON) diff --git a/cmake/BuildOptions.cmake b/cmake/BuildOptions.cmake index 00586c23c..0fce64292 100644 --- a/cmake/BuildOptions.cmake +++ b/cmake/BuildOptions.cmake @@ -1,24 +1,3 @@ -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) diff --git a/cmake/GlfwImport.cmake b/cmake/GlfwImport.cmake index 12dbfeb9f..5692e44f2 100644 --- a/cmake/GlfwImport.cmake +++ b/cmake/GlfwImport.cmake @@ -16,10 +16,15 @@ if(NOT glfw3_FOUND AND NOT USE_EXTERNAL_GLFW STREQUAL "ON" AND "${PLATFORM}" MAT 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) + set(WAS_SHARED ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS OFF CACHE BOOL " " FORCE) + add_subdirectory(external/glfw) + + set(BUILD_SHARED_LIBS WAS_SHARED CACHE BOOL " " FORCE) + unset(WAS_SHARED) list(APPEND raylib_sources $) include_directories(BEFORE SYSTEM external/glfw/include) diff --git a/cmake/InstallConfigurations.cmake b/cmake/InstallConfigurations.cmake index a26fedf55..6a89ad55c 100644 --- a/cmake/InstallConfigurations.cmake +++ b/cmake/InstallConfigurations.cmake @@ -7,12 +7,12 @@ install( ) # PKG_CONFIG_LIBS_PRIVATE is used in raylib.pc.in -if (STATIC) +if (NOT BUILD_SHARED_LIBS) 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) +elseif (BUILD_SHARED_LIBS) set(PKG_CONFIG_LIBS_EXTRA "") endif () diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3319ef03d..f6416dfe7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,29 +51,23 @@ include(LibraryConfigurations) set(LIBS_PRIVATE ${LIBS_PRIVATE} ${OPENAL_LIBRARY}) -if (STATIC) +add_library(raylib ${raylib_sources} ${raylib_public_headers}) + +if (NOT BUILD_SHARED_LIBS) 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) +else() 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 () +endif() # Setting target properties set_target_properties(raylib PROPERTIES @@ -82,7 +76,7 @@ set_target_properties(raylib PROPERTIES SOVERSION ${API_VERSION} ) -if (WITH_PIC OR SHARED) +if (WITH_PIC OR BUILD_SHARED_LIBS) set_property(TARGET raylib PROPERTY POSITION_INDEPENDENT_CODE ON) endif () From f2c0981c572f9ea2a93d32eee36a9447bdd549e0 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Jan 2021 11:57:18 +0100 Subject: [PATCH 35/43] Review typo --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 4e72fb6b8..1bc1fe8b5 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -113,7 +113,7 @@ #define RL_FREE(ptr) free(ptr) #endif -// NOTE: MSC C++ compiler does not support compound literals (C99 feature) +// NOTE: MSVC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized from { } initializers. #if defined(__cplusplus) #define CLITERAL(type) type From 721768bdb02dbbe6875dd46af065298faa74f9c1 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Jan 2021 11:57:57 +0100 Subject: [PATCH 36/43] Remove automatic pointer lock on mouse click #1513 --- src/core.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/core.c b/src/core.c index 5549fc9d2..3b31f0dea 100644 --- a/src/core.c +++ b/src/core.c @@ -1738,6 +1738,9 @@ void EnableCursor(void) #if defined(PLATFORM_DESKTOP) glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); #endif +#if defined(PLATFORM_WEB) + emscripten_exit_pointerlock(); +#endif #if defined(PLATFORM_UWP) UWPGetMouseUnlockFunc()(); #endif @@ -1750,6 +1753,9 @@ void DisableCursor(void) #if defined(PLATFORM_DESKTOP) glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); #endif +#if defined(PLATFORM_WEB) + emscripten_request_pointerlock("#canvas", 1); +#endif #if defined(PLATFORM_UWP) UWPGetMouseLockFunc()(); #endif @@ -5092,17 +5098,7 @@ static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent // Lock mouse pointer when click on screen if (eventType == EMSCRIPTEN_EVENT_CLICK) { - EmscriptenPointerlockChangeEvent plce; - emscripten_get_pointerlock_status(&plce); - - int result = emscripten_request_pointerlock("#canvas", 1); // TODO: It does not work! - - // result -> EMSCRIPTEN_RESULT_DEFERRED - // The requested operation cannot be completed now for web security reasons, - // and has been deferred for completion in the next event handler. --> but it never happens! - - //if (!plce.isActive) emscripten_request_pointerlock(0, 1); - //else emscripten_exit_pointerlock(); + // TODO: Manage mouse events if required (note that GLFW JS wrapper manages it now) } return 0; From f4f208c4aeab5ee4c7e441714fdf6938dca3c24d Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Jan 2021 12:16:19 +0100 Subject: [PATCH 37/43] ADDED: UploadMesh() #1529 Upload mesh data to GPU and get VAO/VBO identifiers --- src/models.c | 6 ++++++ src/raylib.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/models.c b/src/models.c index 60e08171c..98aa2eff2 100644 --- a/src/models.c +++ b/src/models.c @@ -849,6 +849,12 @@ Mesh *LoadMeshes(const char *fileName, int *meshCount) return meshes; } +// Upload mesh vertex data to GPU +void UploadMesh(Mesh *mesh) +{ + rlLoadMesh(&mesh, false); // Static mesh by default +} + // Unload mesh from memory (RAM and/or VRAM) void UnloadMesh(Mesh mesh) { diff --git a/src/raylib.h b/src/raylib.h index 1bc1fe8b5..a2e5c63b6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1339,6 +1339,7 @@ RLAPI void UnloadModelKeepMeshes(Model model); // Mesh loading/unloading functions RLAPI Mesh *LoadMeshes(const char *fileName, int *meshCount); // Load meshes from model file +RLAPI void UploadMesh(Mesh *mesh); // Upload mesh vertex data to GPU (VRAM) RLAPI void UnloadMesh(Mesh mesh); // Unload mesh from memory (RAM and/or VRAM) RLAPI bool ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file, returns true on success From a0d2b64747ceb8e25fe0a313b0e8efaa33f7876b Mon Sep 17 00:00:00 2001 From: Nikolas Date: Fri, 22 Jan 2021 23:43:06 +0100 Subject: [PATCH 38/43] Fix issue when trying to build raylib statically (#1544) --- cmake/GlfwImport.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/GlfwImport.cmake b/cmake/GlfwImport.cmake index 5692e44f2..b740884f7 100644 --- a/cmake/GlfwImport.cmake +++ b/cmake/GlfwImport.cmake @@ -23,7 +23,7 @@ if(NOT glfw3_FOUND AND NOT USE_EXTERNAL_GLFW STREQUAL "ON" AND "${PLATFORM}" MAT add_subdirectory(external/glfw) - set(BUILD_SHARED_LIBS WAS_SHARED CACHE BOOL " " FORCE) + set(BUILD_SHARED_LIBS ${WAS_SHARED} CACHE BOOL " " FORCE) unset(WAS_SHARED) list(APPEND raylib_sources $) @@ -31,4 +31,4 @@ if(NOT glfw3_FOUND AND NOT USE_EXTERNAL_GLFW STREQUAL "ON" AND "${PLATFORM}" MAT else() MESSAGE(STATUS "Using external GLFW") set(GLFW_PKG_DEPS glfw3) -endif() \ No newline at end of file +endif() From 1d23e1569219f22376144999de07900c7409d13f Mon Sep 17 00:00:00 2001 From: hristo Date: Sun, 24 Jan 2021 22:09:01 +0200 Subject: [PATCH 39/43] Added a windows and linux simple compile with cmake (#1543) * Added a windows and linux simple compile with cmake Will expand on the file but it will be nice to know if they are some extremely breaking changes in the future. cmake.yml will run two task one for linux build and one for windows build essentially checking that both are working. We can add all platforms later. * Renaming the task to "CMake Builds" and removing the test step. Commented out the test step as I don't yet know what the two bash files are doing but they are failing the build. --- .github/workflows/cmake.yml | 93 +++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 .github/workflows/cmake.yml diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 000000000..11aabbb04 --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,93 @@ +name: CMake Builds + +on: + push: + pull_request: + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build_windows: + name: Windows Build + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: powershell + working-directory: ${{github.workspace}}/build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: cmake $env:GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$env:BUILD_TYPE -DPLATFORM=Desktop + + - name: Build + working-directory: ${{github.workspace}}/build + shell: powershell + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config $env:BUILD_TYPE + +# - name: Test +# working-directory: ${{github.workspace}}/build +# shell: powershell +# # Execute tests defined by the CMake configuration. +# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail +# run: ctest -C $env:BUILD_TYPE + + build_linux: + name: Linux Build + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Setup Environment + run: | + sudo apt-get update -qq + sudo apt-get install gcc-multilib + sudo apt-get install -y --no-install-recommends libglfw3 libglfw3-dev libx11-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libxext-dev libxfixes-dev + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash + working-directory: ${{github.workspace}}/build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DPLATFORM=Desktop + + - name: Build + working-directory: ${{github.workspace}}/build + shell: bash + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config $BUILD_TYPE + +# - name: Test +# working-directory: ${{github.workspace}}/build +# shell: bash +# # Execute tests defined by the CMake configuration. +# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail +# run: ctest -C $BUILD_TYPE \ No newline at end of file From 4bf7b00013f7efe222d02171e176bb827a90fad9 Mon Sep 17 00:00:00 2001 From: hristo Date: Mon, 25 Jan 2021 11:44:30 +0200 Subject: [PATCH 40/43] Removing test file. (#1545) This test file is just testing compilation with the library works correctly but is no longer needed because: - it is not cross platform - it taps into the CTest system which is better suited for real unit/integration tests - it can be incorporated into the pipeline of github actions instead in the future --- .github/workflows/cmake.yml | 24 ++++++++++++------------ cmake/test-pkgconfig.sh | 21 --------------------- src/CMakeLists.txt | 3 --- 3 files changed, 12 insertions(+), 36 deletions(-) delete mode 100755 cmake/test-pkgconfig.sh diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 11aabbb04..52fbdd80c 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -41,12 +41,12 @@ jobs: # Execute the build. You can specify a specific target with "--target " run: cmake --build . --config $env:BUILD_TYPE -# - name: Test -# working-directory: ${{github.workspace}}/build -# shell: powershell -# # Execute tests defined by the CMake configuration. -# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail -# run: ctest -C $env:BUILD_TYPE + - name: Test + working-directory: ${{github.workspace}}/build + shell: powershell + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C $env:BUILD_TYPE build_linux: name: Linux Build @@ -85,9 +85,9 @@ jobs: # Execute the build. You can specify a specific target with "--target " run: cmake --build . --config $BUILD_TYPE -# - name: Test -# working-directory: ${{github.workspace}}/build -# shell: bash -# # Execute tests defined by the CMake configuration. -# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail -# run: ctest -C $BUILD_TYPE \ No newline at end of file + - name: Test + working-directory: ${{github.workspace}}/build + shell: bash + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C $BUILD_TYPE \ No newline at end of file diff --git a/cmake/test-pkgconfig.sh b/cmake/test-pkgconfig.sh deleted file mode 100755 index ccbdfb652..000000000 --- a/cmake/test-pkgconfig.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# Test if including/linking/running an installed raylib works - -set -x -export LD_RUN_PATH=/usr/local/lib - -CFLAGS="-Wall -Wextra -Werror $CFLAGS" -if [ "$ARCH" = "i386" ]; then -CFLAGS="-m32 $CLFAGS" -fi - -cat << EOF | ${CC:-cc} -otest -xc - $(pkg-config --libs --cflags $@ raylib.pc) $CFLAGS && exec ./test -#include -#include - -int main(void) -{ - int num = GetRandomValue(42, 1337); - return 42 <= num && num <= 1337 ? EXIT_SUCCESS : EXIT_FAILURE; -} -EOF diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f6416dfe7..a11f4efbe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -56,7 +56,6 @@ add_library(raylib ${raylib_sources} ${raylib_public_headers}) if (NOT BUILD_SHARED_LIBS) MESSAGE(STATUS "Building raylib static library") add_library(raylib_static ALIAS raylib) - add_test("pkg-config--static" ${PROJECT_SOURCE_DIR}/../cmake/test-pkgconfig.sh --static) else() MESSAGE(STATUS "Building raylib shared library") if (MSVC) @@ -65,8 +64,6 @@ else() INTERFACE $ ) endif () - - add_test("pkg-config" ${PROJECT_SOURCE_DIR}/../cmake/test-pkgconfig.sh) endif() # Setting target properties From f3ce3a6f741fdf02eb602e2d7df7b14951f4aa65 Mon Sep 17 00:00:00 2001 From: hristo Date: Mon, 25 Jan 2021 11:47:53 +0200 Subject: [PATCH 41/43] Removing config.h.in file (#1546) CMake relied on this file for configurations and also was interfering in the regular config.h by having a separate definition if building with CMake. This was not entirely correct so instead we will define compile time definitions separately through CMake (CompileDefinitions.cmake) and also will use the provided EXTERNAL_CONFIG_FLAGS that I found that will not use config.h in through the build process. I also introduced a new compiler option (CUSTOMIZE_BUILD) that when OFF will use the default config.h and when ON will show other options for redefining your own options. Fixed an error in rlgl.h where if you have both RLGL_STANDALONE and SUPPORT_VR_SIMULATOR you get a compile time error. --- CMakeOptions.txt | 92 +++++++++++++------------ cmake/CompileDefinitions.cmake | 109 ++++++++++++++++++++++++++++++ cmake/LibraryConfigurations.cmake | 5 -- src/CMakeLists.txt | 7 +- src/config.h | 9 --- src/config.h.in | 91 ------------------------- src/rlgl.h | 2 +- 7 files changed, 158 insertions(+), 157 deletions(-) create mode 100644 cmake/CompileDefinitions.cmake delete mode 100644 src/config.h.in diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 7ab43de25..d60ca2b79 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -8,6 +8,7 @@ enum_option(OPENGL_VERSION "OFF;3.3;2.1;1.1;ES 2.0" "Force a specific OpenGL Ver # Configuration options option(BUILD_EXAMPLES "Build the examples." ${RAYLIB_IS_MAIN}) +option(CUSTOMIZE_BUILD "Show options for customizing your Raylib library build." OFF) 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) @@ -16,7 +17,7 @@ option(ENABLE_MSAN "Enable MemorySanitizer (MSan) for debugging (not recommended option(WITH_PIC "Compile static library as position-independent code" OFF) option(BUILD_SHARED_LIBS "Build raylib as a shared library" OFF) option(MACOS_FATLIB "Build fat library for both i386 and x86_64 on macOS" OFF) -option(USE_AUDIO "Build raylib with audio module" ON) +cmake_dependent_option(USE_AUDIO "Build raylib with audio module" ON CUSTOMIZE_BUILD ON) enum_option(USE_EXTERNAL_GLFW "OFF;IF_POSSIBLE;ON" "Link raylib against system GLFW instead of embedded one") if(UNIX AND NOT APPLE) @@ -27,61 +28,62 @@ option(INCLUDE_EVERYTHING "Include everything disabled by default (for CI usage" set(OFF ${INCLUDE_EVERYTHING} CACHE INTERNAL "Replace any OFF by default with \${OFF} to have it covered by this option") # core.c -option(SUPPORT_CAMERA_SYSTEM "Provide camera module (camera.h) with multiple predefined cameras: free, 1st/3rd person, orbital" ON) -option(SUPPORT_GESTURES_SYSTEM "Gestures module is included (gestures.h) to support gestures detection: tap, hold, swipe, drag" ON) -option(SUPPORT_MOUSE_GESTURES "Mouse gestures are directly mapped like touches and processed by gestures system" ON) -option(SUPPORT_SSH_KEYBOARD_RPI "Reconfigure standard input to receive key inputs, works with SSH connection" OFF) -option(SUPPORT_DEFAULT_FONT "Default font is loaded on window initialization to be available for the user to render simple text. If enabled, uses external module functions to load default raylib font (module: text)" ON) -option(SUPPORT_SCREEN_CAPTURE "Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()" ON) -option(SUPPORT_GIF_RECORDING "Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()" ON) -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) +cmake_dependent_option(SUPPORT_CAMERA_SYSTEM "Provide camera module (camera.h) with multiple predefined cameras: free, 1st/3rd person, orbital" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_GESTURES_SYSTEM "Gestures module is included (gestures.h) to support gestures detection: tap, hold, swipe, drag" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_MOUSE_GESTURES "Mouse gestures are directly mapped like touches and processed by gestures system" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_SSH_KEYBOARD_RPI "Reconfigure standard input to receive key inputs, works with SSH connection" OFF CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_DEFAULT_FONT "Default font is loaded on window initialization to be available for the user to render simple text. If enabled, uses external module functions to load default raylib font (module: text)" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_SCREEN_CAPTURE "Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_GIF_RECORDING "Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_BUSY_WAIT_LOOP "Use busy wait loop for timing sync instead of a high-resolution timer" OFF CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_EVENTS_WAITING "Wait for events passively (sleeping while no events) instead of polling them actively every frame" OFF CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_HIGH_DPI "Support high DPI displays" OFF CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_DATA_STORAGE "Support for persistent data storage" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_COMPRESSION_API "Support for compression API" ON CUSTOMIZE_BUILD ON) # rlgl.h -option(SUPPORT_VR_SIMULATOR "Support VR simulation functionality (stereo rendering)" ON) +cmake_dependent_option(SUPPORT_VR_SIMULATOR "Support VR simulation functionality (stereo rendering)" ON CUSTOMIZE_BUILD ON) # shapes.c -option(SUPPORT_FONT_TEXTURE "Draw rectangle shapes using font texture white character instead of default white texture. Allows drawing rectangles and text with a single draw call, very useful for GUI systems!" ON) -option(SUPPORT_QUADS_DRAW_MODE "Use QUADS instead of TRIANGLES for drawing when possible. Some lines-based shapes could still use lines" ON) +cmake_dependent_option(SUPPORT_FONT_TEXTURE "Draw rectangle shapes using font texture white character instead of default white texture. Allows drawing rectangles and text with a single draw call, very useful for GUI systems!" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_QUADS_DRAW_MODE "Use QUADS instead of TRIANGLES for drawing when possible. Some lines-based shapes could still use lines" ON CUSTOMIZE_BUILD ON) # textures.c -option(SUPPORT_IMAGE_EXPORT "Support image exporting to file" ON) -option(SUPPORT_IMAGE_GENERATION "Support procedural image generation functionality (gradient, spot, perlin-noise, cellular)" ON) -option(SUPPORT_IMAGE_MANIPULATION "Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT()" ON) -option(SUPPORT_FILEFORMAT_PNG "Support loading PNG as textures" ON) -option(SUPPORT_FILEFORMAT_DDS "Support loading DDS as textures" ON) -option(SUPPORT_FILEFORMAT_HDR "Support loading HDR as textures" ON) -option(SUPPORT_FILEFORMAT_KTX "Support loading KTX as textures" ON) -option(SUPPORT_FILEFORMAT_ASTC "Support loading ASTC as textures" ON) -option(SUPPORT_FILEFORMAT_BMP "Support loading BMP as textures" ${OFF}) -option(SUPPORT_FILEFORMAT_TGA "Support loading TGA as textures" ${OFF}) -option(SUPPORT_FILEFORMAT_JPG "Support loading JPG as textures" ${OFF}) -option(SUPPORT_FILEFORMAT_GIF "Support loading GIF as textures" ${OFF}) -option(SUPPORT_FILEFORMAT_PSD "Support loading PSD as textures" ${OFF}) -option(SUPPORT_FILEFORMAT_PKM "Support loading PKM as textures" ${OFF}) -option(SUPPORT_FILEFORMAT_PVR "Support loading PVR as textures" ${OFF}) +cmake_dependent_option(SUPPORT_IMAGE_EXPORT "Support image exporting to file" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_IMAGE_GENERATION "Support procedural image generation functionality (gradient, spot, perlin-noise, cellular)" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_IMAGE_MANIPULATION "Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT()" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_PNG "Support loading PNG as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_DDS "Support loading DDS as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_HDR "Support loading HDR as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_KTX "Support loading KTX as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_ASTC "Support loading ASTC as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_BMP "Support loading BMP as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_TGA "Support loading TGA as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_JPG "Support loading JPG as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_GIF "Support loading GIF as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_PSD "Support loading PSD as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_PKM "Support loading PKM as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_PVR "Support loading PVR as textures" ${OFF} CUSTOMIZE_BUILD OFF) # text.c -option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON) -option(SUPPORT_FILEFORMAT_TTF "Support loading font in TTF/OTF format" ON) -option(SUPPORT_TEXT_MANIPULATION "Support text manipulation functions" ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_TTF "Support loading font in TTF/OTF format" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_TEXT_MANIPULATION "Support text manipulation functions" ON CUSTOMIZE_BUILD ON) # models.c -option(SUPPORT_MESH_GENERATION "Support procedural mesh generation functions, uses external par_shapes.h library. NOTE: Some generated meshes DO NOT include generated texture coordinates" ON) -option(SUPPORT_FILEFORMAT_OBJ "Support loading OBJ file format" ON) -option(SUPPORT_FILEFORMAT_MTL "Support loading MTL file format" ON) -option(SUPPORT_FILEFORMAT_IQM "Support loading IQM file format" ON) -option(SUPPORT_FILEFORMAT_GLTF "Support loading GLTF file format" ON) +cmake_dependent_option(SUPPORT_MESH_GENERATION "Support procedural mesh generation functions, uses external par_shapes.h library. NOTE: Some generated meshes DO NOT include generated texture coordinates" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_OBJ "Support loading OBJ file format" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_MTL "Support loading MTL file format" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_IQM "Support loading IQM file format" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_GLTF "Support loading GLTF file format" ON CUSTOMIZE_BUILD ON) # raudio.c -option(SUPPORT_FILEFORMAT_WAV "Support loading WAV for sound" ON) -option(SUPPORT_FILEFORMAT_OGG "Support loading OGG for sound" ON) -option(SUPPORT_FILEFORMAT_XM "Support loading XM for sound" ON) -option(SUPPORT_FILEFORMAT_MOD "Support loading MOD for sound" ON) -option(SUPPORT_FILEFORMAT_MP3 "Support loading MP3 for sound" ON) -option(SUPPORT_FILEFORMAT_FLAC "Support loading FLAC for sound" ${OFF}) +cmake_dependent_option(SUPPORT_FILEFORMAT_WAV "Support loading WAV for sound" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_OGG "Support loading OGG for sound" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_XM "Support loading XM for sound" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_MOD "Support loading MOD for sound" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_MP3 "Support loading MP3 for sound" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_FLAC "Support loading FLAC for sound" ${OFF} CUSTOMIZE_BUILD OFF) # utils.c -option(SUPPORT_TRACELOG "Show TraceLog() output messages. NOTE: By default LOG_DEBUG traces not shown" ON) +cmake_dependent_option(SUPPORT_TRACELOG "Show TraceLog() output messages. NOTE: By default LOG_DEBUG traces not shown" ON CUSTOMIZE_BUILD ON) diff --git a/cmake/CompileDefinitions.cmake b/cmake/CompileDefinitions.cmake new file mode 100644 index 000000000..b07503c16 --- /dev/null +++ b/cmake/CompileDefinitions.cmake @@ -0,0 +1,109 @@ +# Adding compile definitions +target_compile_definitions("raylib" PUBLIC "${PLATFORM_CPP}") +target_compile_definitions("raylib" PUBLIC "${GRAPHICS}") + +function(define_if target variable) + if (${${variable}}) + target_compile_definitions(${target} PUBLIC "${variable}") + endif () +endfunction() + +if (${CUSTOMIZE_BUILD}) + target_compile_definitions("raylib" PUBLIC EXTERNAL_CONFIG_FLAGS) + define_if("raylib" SUPPORT_CAMERA_SYSTEM) + define_if("raylib" SUPPORT_GESTURES_SYSTEM) + define_if("raylib" SUPPORT_MOUSE_GESTURES) + define_if("raylib" SUPPORT_SSH_KEYBOARD_RPI) + define_if("raylib" SUPPORT_BUSY_WAIT_LOOP) + define_if("raylib" SUPPORT_EVENTS_WAITING) + define_if("raylib" SUPPORT_SCREEN_CAPTURE) + define_if("raylib" SUPPORT_GIF_RECORDING) + define_if("raylib" SUPPORT_HIGH_DPI) + define_if("raylib" SUPPORT_COMPRESSION_API) + define_if("raylib" SUPPORT_DATA_STORAGE) + define_if("raylib" SUPPORT_VR_SIMULATOR) + define_if("raylib" SUPPORT_FONT_TEXTURE) + define_if("raylib" SUPPORT_QUADS_DRAW_MODE) + define_if("raylib" SUPPORT_FILEFORMAT_PNG) + define_if("raylib" SUPPORT_FILEFORMAT_DDS) + define_if("raylib" SUPPORT_FILEFORMAT_HDR) + define_if("raylib" SUPPORT_FILEFORMAT_KTX) + define_if("raylib" SUPPORT_FILEFORMAT_ASTC) + define_if("raylib" SUPPORT_FILEFORMAT_BMP) + define_if("raylib" SUPPORT_FILEFORMAT_TGA) + define_if("raylib" SUPPORT_FILEFORMAT_JPG) + define_if("raylib" SUPPORT_FILEFORMAT_GIF) + define_if("raylib" SUPPORT_FILEFORMAT_PSD) + define_if("raylib" SUPPORT_FILEFORMAT_PKM) + define_if("raylib" SUPPORT_FILEFORMAT_PVR) + define_if("raylib" ORT_IMAGE_EXPORT) + define_if("raylib" SUPPORT_IMAGE_MANIPULATION) + define_if("raylib" SUPPORT_IMAGE_GENERATION) + define_if("raylib" SUPPORT_DEFAULT_FONT) + define_if("raylib" SUPPORT_FILEFORMAT_FNT) + define_if("raylib" SUPPORT_FILEFORMAT_TTF) + define_if("raylib" SUPPORT_TEXT_MANIPULATION) + define_if("raylib" SUPPORT_FILEFORMAT_OBJ) + define_if("raylib" SUPPORT_FILEFORMAT_MTL) + define_if("raylib" SUPPORT_FILEFORMAT_IQM) + define_if("raylib" SUPPORT_FILEFORMAT_GLTF) + define_if("raylib" SUPPORT_MESH_GENERATION) + define_if("raylib" SUPPORT_FILEFORMAT_WAV) + define_if("raylib" SUPPORT_FILEFORMAT_OGG) + define_if("raylib" SUPPORT_FILEFORMAT_XM) + define_if("raylib" SUPPORT_FILEFORMAT_MOD) + define_if("raylib" SUPPORT_FILEFORMAT_FLAC) + define_if("raylib" SUPPORT_FILEFORMAT_MP3) + define_if("raylib" SUPPORT_TRACELOG) + define_if("raylib" SUPPORT_COMPRESSION_API) + + + if (UNIX AND NOT APPLE) + target_compile_definitions("raylib" PUBLIC "MAX_FILEPATH_LENGTH=4096") + else () + target_compile_definitions("raylib" PUBLIC "MAX_FILEPATH_LENGTH=512") + endif () + + target_compile_definitions("raylib" PUBLIC "MAX_GAMEPADS=4") + target_compile_definitions("raylib" PUBLIC "MAX_GAMEPAD_AXIS=8") + target_compile_definitions("raylib" PUBLIC "MAX_GAMEPAD_BUTTONS=32") + target_compile_definitions("raylib" PUBLIC "MAX_TOUCH_POINTS=10") + target_compile_definitions("raylib" PUBLIC "MAX_KEY_PRESSED_QUEUE=16") + + target_compile_definitions("raylib" PUBLIC "STORAGE_DATA_FILE=\"storage.data\"") + target_compile_definitions("raylib" PUBLIC "MAX_KEY_PRESSED_QUEUE=16") + target_compile_definitions("raylib" PUBLIC "MAX_DECOMPRESSION_SIZE=64") + + if (${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_33" OR ${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_11") + target_compile_definitions("raylib" PUBLIC "DEFAULT_BATCH_BUFFER_ELEMENTS=8192") + elseif (${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_ES2") + target_compile_definitions("raylib" PUBLIC "DEFAULT_BATCH_BUFFER_ELEMENTS=2048") + endif () + + target_compile_definitions("raylib" PUBLIC "DEFAULT_BATCH_DRAWCALLS=256") + target_compile_definitions("raylib" PUBLIC "MAX_MATRIX_STACK_SIZE=32") + target_compile_definitions("raylib" PUBLIC "MAX_SHADER_LOCATIONS=32") + target_compile_definitions("raylib" PUBLIC "MAX_MATERIAL_MAPS=12") + target_compile_definitions("raylib" PUBLIC "RL_CULL_DISTANCE_NEAR=0.01") + target_compile_definitions("raylib" PUBLIC "RL_CULL_DISTANCE_FAR=1000.0") + + target_compile_definitions("raylib" PUBLIC "DEFAULT_SHADER_ATTRIB_NAME_POSITION=\"vertexPosition\"") + target_compile_definitions("raylib" PUBLIC "DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD=\"vertexTexCoord\"") + target_compile_definitions("raylib" PUBLIC "DEFAULT_SHADER_ATTRIB_NAME_NORMAL=\"vertexNormal\"") + target_compile_definitions("raylib" PUBLIC "DEFAULT_SHADER_ATTRIB_NAME_COLOR=\"vertexColor\"") + target_compile_definitions("raylib" PUBLIC "DEFAULT_SHADER_ATTRIB_NAME_TANGENT=\"vertexTangent\"") + target_compile_definitions("raylib" PUBLIC "DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2=\"vertexTexCoord2\"") + + target_compile_definitions("raylib" PUBLIC "MAX_TEXT_BUFFER_LENGTH=1024") + target_compile_definitions("raylib" PUBLIC "MAX_TEXT_UNICODE_CHARS=512") + target_compile_definitions("raylib" PUBLIC "MAX_TEXTSPLIT_COUNT=128") + + target_compile_definitions("raylib" PUBLIC "AUDIO_DEVICE_FORMAT=ma_format_f32") + target_compile_definitions("raylib" PUBLIC "AUDIO_DEVICE_CHANNELS=2") + target_compile_definitions("raylib" PUBLIC "AUDIO_DEVICE_SAMPLE_RATE=44100") + target_compile_definitions("raylib" PUBLIC "DEFAULT_AUDIO_BUFFER_SIZE=4096") + + target_compile_definitions("raylib" PUBLIC "MAX_TRACELOG_MSG_LENGTH=128") + target_compile_definitions("raylib" PUBLIC "MAX_UWP_MESSAGES=512") +endif () + diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index 8cca24649..cf5e85f8e 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -1,8 +1,3 @@ - -### 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") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a11f4efbe..bcef058bb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -83,11 +83,7 @@ if (${PLATFORM} MATCHES "Desktop") target_link_libraries(raylib glfw) endif () -# Adding compile definitions -target_compile_definitions(raylib - PUBLIC "${PLATFORM_CPP}" - PUBLIC "${GRAPHICS}" - ) +include(CompileDefinitions) # Registering include directories target_include_directories(raylib @@ -96,7 +92,6 @@ target_include_directories(raylib $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_BINARY_DIR} # For cmake/config.h ${OPENGL_INCLUDE_DIR} ${OPENAL_INCLUDE_DIR} ) diff --git a/src/config.h b/src/config.h index dbe77cd9f..f73804223 100644 --- a/src/config.h +++ b/src/config.h @@ -27,12 +27,6 @@ #define RAYLIB_VERSION "3.5" -// Edit to control what features Makefile'd raylib is compiled with -#if defined(RAYLIB_CMAKE) - // Edit cmake/RaylibOptions.cmake for CMake instead - #include "cmake/config.h" -#else - //------------------------------------------------------------------------------------ // Module: core - Configuration Flags //------------------------------------------------------------------------------------ @@ -217,6 +211,3 @@ //------------------------------------------------------------------------------------ #define MAX_TRACELOG_MSG_LENGTH 128 // Max length of one trace-log message #define MAX_UWP_MESSAGES 512 // Max UWP messages to process - - -#endif //defined(RAYLIB_CMAKE) diff --git a/src/config.h.in b/src/config.h.in deleted file mode 100644 index 4cb217b5c..000000000 --- a/src/config.h.in +++ /dev/null @@ -1,91 +0,0 @@ -// config.h.in - -// core.c -// Camera module is included (camera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital -#cmakedefine SUPPORT_CAMERA_SYSTEM 1 -// Gestures module is included (gestures.h) to support gestures detection: tap, hold, swipe, drag -#cmakedefine SUPPORT_GESTURES_SYSTEM 1 -// Mouse gestures are directly mapped like touches and processed by gestures system. -#cmakedefine SUPPORT_MOUSE_GESTURES 1 -// Reconfigure standard input to receive key inputs, works with SSH connection. -#cmakedefine SUPPORT_SSH_KEYBOARD_RPI 1 -// Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used -#cmakedefine SUPPORT_BUSY_WAIT_LOOP 1 -// Wait for events passively (sleeping while no events) instead of polling them actively every frame -#cmakedefine SUPPORT_EVENTS_WAITING 1 -// Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() -#cmakedefine SUPPORT_SCREEN_CAPTURE 1 -// Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() -#cmakedefine SUPPORT_GIF_RECORDING 1 -// Support high DPI displays -#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) -#cmakedefine SUPPORT_VR_SIMULATOR 1 - -// shapes.c -// Draw rectangle shapes using font texture white character instead of default white texture -#cmakedefine SUPPORT_FONT_TEXTURE 1 -// Use QUADS instead of TRIANGLES for drawing when possible -// Some lines-based shapes could still use lines -#cmakedefine SUPPORT_QUADS_DRAW_MODE 1 - -// textures.c -// Selecte desired fileformats to be supported for image data loading. -#cmakedefine SUPPORT_FILEFORMAT_PNG 1 -#cmakedefine SUPPORT_FILEFORMAT_DDS 1 -#cmakedefine SUPPORT_FILEFORMAT_HDR 1 -#cmakedefine SUPPORT_FILEFORMAT_KTX 1 -#cmakedefine SUPPORT_FILEFORMAT_ASTC 1 -#cmakedefine SUPPORT_FILEFORMAT_BMP 1 -#cmakedefine SUPPORT_FILEFORMAT_TGA 1 -#cmakedefine SUPPORT_FILEFORMAT_JPG 1 -#cmakedefine SUPPORT_FILEFORMAT_GIF 1 -#cmakedefine SUPPORT_FILEFORMAT_PSD 1 -#cmakedefine SUPPORT_FILEFORMAT_PKM 1 -#cmakedefine SUPPORT_FILEFORMAT_PVR 1 -// Support image export functionality (.png, .bmp, .tga, .jpg) -#define SUPPORT_IMAGE_EXPORT 1 -// Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT() -#cmakedefine SUPPORT_IMAGE_MANIPULATION 1 -// Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) -#cmakedefine SUPPORT_IMAGE_GENERATION 1 - -// text.c -// Default font is loaded on window initialization to be available for the user to render simple text. NOTE: If enabled, uses external module functions to load default raylib font (module: text) -#cmakedefine SUPPORT_DEFAULT_FONT 1 -// Selected desired fileformats to be supported for loading. -#cmakedefine SUPPORT_FILEFORMAT_FNT 1 -#cmakedefine SUPPORT_FILEFORMAT_TTF 1 -// Support text management functions -// If not defined, still some functions are supported: TextLength(), TextFormat() -#cmakedefine SUPPORT_TEXT_MANIPULATION 1 - -// models.c -// Selected desired fileformats to be supported for loading. -#cmakedefine SUPPORT_FILEFORMAT_OBJ 1 -#cmakedefine SUPPORT_FILEFORMAT_MTL 1 -#cmakedefine SUPPORT_FILEFORMAT_IQM 1 -#cmakedefine SUPPORT_FILEFORMAT_GLTF 1 -// Support procedural mesh generation functions, uses external par_shapes.h library -// NOTE: Some generated meshes DO NOT include generated texture coordinates -#cmakedefine SUPPORT_MESH_GENERATION 1 - -// raudio.c -// Desired fileformats to be supported for loading. -#cmakedefine SUPPORT_FILEFORMAT_WAV 1 -#cmakedefine SUPPORT_FILEFORMAT_OGG 1 -#cmakedefine SUPPORT_FILEFORMAT_XM 1 -#cmakedefine SUPPORT_FILEFORMAT_MOD 1 -#cmakedefine SUPPORT_FILEFORMAT_FLAC 1 -#cmakedefine SUPPORT_FILEFORMAT_MP3 1 - -// utils.c -// Show TraceLog() output messages. NOTE: By default LOG_DEBUG traces not shown -#cmakedefine SUPPORT_TRACELOG 1 - diff --git a/src/rlgl.h b/src/rlgl.h index d8e3de9d6..11f511cca 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -840,7 +840,7 @@ typedef struct RenderBatch { float currentDepth; // Current depth value for next draw } RenderBatch; -#if defined(SUPPORT_VR_SIMULATOR) +#if defined(SUPPORT_VR_SIMULATOR) && !defined(RLGL_STANDALONE) // VR Stereo rendering configuration for simulator typedef struct VrStereoConfig { Shader distortionShader; // VR stereo rendering distortion shader From 65b299c6cfdfd85a79833641c96b41babd50c872 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 25 Jan 2021 17:53:04 +0100 Subject: [PATCH 42/43] Replace tabs by 4 spaces --- .../models/resources/shaders/glsl330/brdf.fs | 26 ++-- .../resources/shaders/glsl330/prefilter.fs | 26 ++-- .../resources/shaders/glsl330/skybox.fs | 2 +- .../resources/shaders/glsl100/bloom.fs | 2 +- .../resources/shaders/glsl100/dream_vision.fs | 2 +- .../resources/shaders/glsl100/raymarching.fs | 112 +++++++++--------- .../resources/shaders/glsl100/sobel.fs | 38 +++--- .../resources/shaders/glsl100/spotlight.fs | 88 +++++++------- .../shaders/resources/shaders/glsl100/wave.fs | 18 +-- .../resources/shaders/glsl120/bloom.fs | 2 +- .../resources/shaders/glsl120/dream_vision.fs | 2 +- .../resources/shaders/glsl120/sobel.fs | 38 +++--- .../resources/shaders/glsl330/bloom.fs | 2 +- .../resources/shaders/glsl330/dream_vision.fs | 2 +- .../resources/shaders/glsl330/eratosthenes.fs | 6 +- .../resources/shaders/glsl330/raymarching.fs | 112 +++++++++--------- .../resources/shaders/glsl330/reload.fs | 26 ++-- .../resources/shaders/glsl330/sobel.fs | 38 +++--- .../resources/shaders/glsl330/spotlight.fs | 58 ++++----- .../shaders/resources/shaders/glsl330/wave.fs | 18 +-- 20 files changed, 309 insertions(+), 309 deletions(-) diff --git a/examples/models/resources/shaders/glsl330/brdf.fs b/examples/models/resources/shaders/glsl330/brdf.fs index d04bc6618..f2774f81c 100644 --- a/examples/models/resources/shaders/glsl330/brdf.fs +++ b/examples/models/resources/shaders/glsl330/brdf.fs @@ -41,27 +41,27 @@ float RadicalInverseVdC(uint bits) // Compute Hammersley coordinates vec2 Hammersley(uint i, uint N) { - return vec2(float(i)/float(N), RadicalInverseVdC(i)); + return vec2(float(i)/float(N), RadicalInverseVdC(i)); } // Integrate number of importance samples for (roughness and NoV) vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) { - float a = roughness*roughness; - float phi = 2.0 * PI * Xi.x; - float cosTheta = sqrt((1.0 - Xi.y)/(1.0 + (a*a - 1.0)*Xi.y)); - float sinTheta = sqrt(1.0 - cosTheta*cosTheta); + float a = roughness*roughness; + float phi = 2.0 * PI * Xi.x; + float cosTheta = sqrt((1.0 - Xi.y)/(1.0 + (a*a - 1.0)*Xi.y)); + float sinTheta = sqrt(1.0 - cosTheta*cosTheta); - // Transform from spherical coordinates to cartesian coordinates (halfway vector) - vec3 H = vec3(cos(phi)*sinTheta, sin(phi)*sinTheta, cosTheta); + // Transform from spherical coordinates to cartesian coordinates (halfway vector) + vec3 H = vec3(cos(phi)*sinTheta, sin(phi)*sinTheta, cosTheta); - // Transform from tangent space H vector to world space sample vector - vec3 up = ((abs(N.z) < 0.999) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0)); - vec3 tangent = normalize(cross(up, N)); - vec3 bitangent = cross(N, tangent); - vec3 sampleVec = tangent*H.x + bitangent*H.y + N*H.z; + // Transform from tangent space H vector to world space sample vector + vec3 up = ((abs(N.z) < 0.999) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0)); + vec3 tangent = normalize(cross(up, N)); + vec3 bitangent = cross(N, tangent); + vec3 sampleVec = tangent*H.x + bitangent*H.y + N*H.z; - return normalize(sampleVec); + return normalize(sampleVec); } float GeometrySchlickGGX(float NdotV, float roughness) diff --git a/examples/models/resources/shaders/glsl330/prefilter.fs b/examples/models/resources/shaders/glsl330/prefilter.fs index 941ea86c0..82f0f7710 100644 --- a/examples/models/resources/shaders/glsl330/prefilter.fs +++ b/examples/models/resources/shaders/glsl330/prefilter.fs @@ -54,26 +54,26 @@ float RadicalInverse_VdC(uint bits) vec2 Hammersley(uint i, uint N) { - return vec2(float(i)/float(N), RadicalInverse_VdC(i)); + return vec2(float(i)/float(N), RadicalInverse_VdC(i)); } vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) { - float a = roughness*roughness; - float phi = 2.0*PI*Xi.x; - float cosTheta = sqrt((1.0 - Xi.y)/(1.0 + (a*a - 1.0)*Xi.y)); - float sinTheta = sqrt(1.0 - cosTheta*cosTheta); + float a = roughness*roughness; + float phi = 2.0*PI*Xi.x; + float cosTheta = sqrt((1.0 - Xi.y)/(1.0 + (a*a - 1.0)*Xi.y)); + float sinTheta = sqrt(1.0 - cosTheta*cosTheta); - // Transform from spherical coordinates to cartesian coordinates (halfway vector) - vec3 H = vec3(cos(phi)*sinTheta, sin(phi)*sinTheta, cosTheta); + // Transform from spherical coordinates to cartesian coordinates (halfway vector) + vec3 H = vec3(cos(phi)*sinTheta, sin(phi)*sinTheta, cosTheta); - // Transform from tangent space H vector to world space sample vector - vec3 up = ((abs(N.z) < 0.999) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0)); - vec3 tangent = normalize(cross(up, N)); - vec3 bitangent = cross(N, tangent); - vec3 sampleVec = tangent*H.x + bitangent*H.y + N*H.z; + // Transform from tangent space H vector to world space sample vector + vec3 up = ((abs(N.z) < 0.999) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0)); + vec3 tangent = normalize(cross(up, N)); + vec3 bitangent = cross(N, tangent); + vec3 sampleVec = tangent*H.x + bitangent*H.y + N*H.z; - return normalize(sampleVec); + return normalize(sampleVec); } void main() diff --git a/examples/models/resources/shaders/glsl330/skybox.fs b/examples/models/resources/shaders/glsl330/skybox.fs index c7a17883e..dd8078e09 100644 --- a/examples/models/resources/shaders/glsl330/skybox.fs +++ b/examples/models/resources/shaders/glsl330/skybox.fs @@ -4,7 +4,7 @@ * * Copyright (c) 2017 Victor Fisac * -* 19-Jun-2020 - modified by Giuseppe Mastrangelo (@peppemas) - VFlip Support +* 19-Jun-2020 - modified by Giuseppe Mastrangelo (@peppemas) - VFlip Support * **********************************************************************************************/ diff --git a/examples/shaders/resources/shaders/glsl100/bloom.fs b/examples/shaders/resources/shaders/glsl100/bloom.fs index a8e1d20f7..673e0116a 100644 --- a/examples/shaders/resources/shaders/glsl100/bloom.fs +++ b/examples/shaders/resources/shaders/glsl100/bloom.fs @@ -14,7 +14,7 @@ uniform vec4 colDiffuse; const vec2 size = vec2(800, 450); // render size const float samples = 5.0; // pixels per axis; higher = bigger glow, worse performance -const float quality = 2.5; // lower = smaller glow, better quality +const float quality = 2.5; // lower = smaller glow, better quality void main() { diff --git a/examples/shaders/resources/shaders/glsl100/dream_vision.fs b/examples/shaders/resources/shaders/glsl100/dream_vision.fs index fa9c5b771..7014b5920 100644 --- a/examples/shaders/resources/shaders/glsl100/dream_vision.fs +++ b/examples/shaders/resources/shaders/glsl100/dream_vision.fs @@ -34,4 +34,4 @@ void main() color = color/9.5; gl_FragColor = color; -} \ No newline at end of file +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl100/raymarching.fs b/examples/shaders/resources/shaders/glsl100/raymarching.fs index c823e01eb..7535086b5 100644 --- a/examples/shaders/resources/shaders/glsl100/raymarching.fs +++ b/examples/shaders/resources/shaders/glsl100/raymarching.fs @@ -42,7 +42,7 @@ uniform vec2 resolution; float sdPlane( vec3 p ) { - return p.y; + return p.y; } float sdSphere( vec3 p, float s ) @@ -85,9 +85,9 @@ float sdHexPrism( vec3 p, vec2 h ) float sdCapsule( vec3 p, vec3 a, vec3 b, float r ) { - vec3 pa = p-a, ba = b-a; - float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); - return length( pa - ba*h ) - r; + vec3 pa = p-a, ba = b-a; + float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); + return length( pa - ba*h ) - r; } float sdEquilateralTriangle( in vec2 p ) @@ -154,19 +154,19 @@ float sdPryamid4(vec3 p, vec3 h ) // h = { cos a, sin a, height } float length2( vec2 p ) { - return sqrt( p.x*p.x + p.y*p.y ); + return sqrt( p.x*p.x + p.y*p.y ); } float length6( vec2 p ) { - p = p*p*p; p = p*p; - return pow( p.x + p.y, 1.0/6.0 ); + p = p*p*p; p = p*p; + return pow( p.x + p.y, 1.0/6.0 ); } float length8( vec2 p ) { - p = p*p; p = p*p; p = p*p; - return pow( p.x + p.y, 1.0/8.0 ); + p = p*p; p = p*p; p = p*p; + return pow( p.x + p.y, 1.0/8.0 ); } float sdTorus82( vec3 p, vec2 t ) @@ -195,7 +195,7 @@ float opS( float d1, float d2 ) vec2 opU( vec2 d1, vec2 d2 ) { - return (d1.xtmax ) break; t += res.x; - m = res.y; + m = res.y; } if( t>tmax ) m=-1.0; @@ -271,11 +271,11 @@ vec2 castRay( in vec3 ro, in vec3 rd ) float calcSoftshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax ) { - float res = 1.0; + float res = 1.0; float t = mint; for( int i=0; i<16; i++ ) { - float h = map( ro + rd*t ).x; + float h = map( ro + rd*t ).x; res = min( res, 8.0*h/t ); t += clamp( h, 0.02, 0.10 ); if( h<0.001 || t>tmax ) break; @@ -287,22 +287,22 @@ vec3 calcNormal( in vec3 pos ) { vec2 e = vec2(1.0,-1.0)*0.5773*0.0005; return normalize( e.xyy*map( pos + e.xyy ).x + - e.yyx*map( pos + e.yyx ).x + - e.yxy*map( pos + e.yxy ).x + - e.xxx*map( pos + e.xxx ).x ); + e.yyx*map( pos + e.yyx ).x + + e.yxy*map( pos + e.yxy ).x + + e.xxx*map( pos + e.xxx ).x ); /* - vec3 eps = vec3( 0.0005, 0.0, 0.0 ); - vec3 nor = vec3( - map(pos+eps.xyy).x - map(pos-eps.xyy).x, - map(pos+eps.yxy).x - map(pos-eps.yxy).x, - map(pos+eps.yyx).x - map(pos-eps.yyx).x ); - return normalize(nor); - */ + vec3 eps = vec3( 0.0005, 0.0, 0.0 ); + vec3 nor = vec3( + map(pos+eps.xyy).x - map(pos-eps.xyy).x, + map(pos+eps.yxy).x - map(pos-eps.yxy).x, + map(pos+eps.yyx).x - map(pos-eps.yyx).x ); + return normalize(nor); + */ } float calcAO( in vec3 pos, in vec3 nor ) { - float occ = 0.0; + float occ = 0.0; float sca = 1.0; for( int i=0; i<5; i++ ) { @@ -331,7 +331,7 @@ vec3 render( in vec3 ro, in vec3 rd ) vec3 col = vec3(0.7, 0.9, 1.0) +rd.y*0.8; vec2 res = castRay(ro,rd); float t = res.x; - float m = res.y; + float m = res.y; if( m>-0.5 ) { vec3 pos = ro + t*rd; @@ -339,7 +339,7 @@ vec3 render( in vec3 ro, in vec3 rd ) vec3 ref = reflect( rd, nor ); // material - col = 0.45 + 0.35*sin( vec3(0.05,0.08,0.10)*(m-1.0) ); + col = 0.45 + 0.35*sin( vec3(0.05,0.08,0.10)*(m-1.0) ); if( m<1.5 ) { @@ -349,9 +349,9 @@ vec3 render( in vec3 ro, in vec3 rd ) // lighting float occ = calcAO( pos, nor ); - vec3 lig = normalize( vec3(cos(-0.4 * runTime), sin(0.7 * runTime), -0.6) ); + vec3 lig = normalize( vec3(cos(-0.4 * runTime), sin(0.7 * runTime), -0.6) ); vec3 hal = normalize( lig-rd ); - float amb = clamp( 0.5+0.5*nor.y, 0.0, 1.0 ); + float amb = clamp( 0.5+0.5*nor.y, 0.0, 1.0 ); float dif = clamp( dot( nor, lig ), 0.0, 1.0 ); float bac = clamp( dot( nor, normalize(vec3(-lig.x,0.0,-lig.z))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0); float dom = smoothstep( -0.1, 0.1, ref.y ); @@ -360,31 +360,31 @@ vec3 render( in vec3 ro, in vec3 rd ) dif *= calcSoftshadow( pos, lig, 0.02, 2.5 ); dom *= calcSoftshadow( pos, ref, 0.02, 2.5 ); - float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0)* + float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0)* dif * (0.04 + 0.96*pow( clamp(1.0+dot(hal,rd),0.0,1.0), 5.0 )); - vec3 lin = vec3(0.0); + vec3 lin = vec3(0.0); lin += 1.30*dif*vec3(1.00,0.80,0.55); lin += 0.40*amb*vec3(0.40,0.60,1.00)*occ; lin += 0.50*dom*vec3(0.40,0.60,1.00)*occ; lin += 0.50*bac*vec3(0.25,0.25,0.25)*occ; lin += 0.25*fre*vec3(1.00,1.00,1.00)*occ; - col = col*lin; - col += 10.00*spe*vec3(1.00,0.90,0.70); + col = col*lin; + col += 10.00*spe*vec3(1.00,0.90,0.70); - col = mix( col, vec3(0.8,0.9,1.0), 1.0-exp( -0.0002*t*t*t ) ); + col = mix( col, vec3(0.8,0.9,1.0), 1.0-exp( -0.0002*t*t*t ) ); } - return vec3( clamp(col,0.0,1.0) ); + return vec3( clamp(col,0.0,1.0) ); } mat3 setCamera( in vec3 ro, in vec3 ta, float cr ) { vec3 cw = normalize(ta-ro); - vec3 cp = vec3(sin(cr), cos(cr),0.0); - vec3 cu = normalize( cross(cw,cp) ); - vec3 cv = normalize( cross(cu,cw) ); + vec3 cp = vec3(sin(cr), cos(cr),0.0); + vec3 cu = normalize( cross(cw,cp) ); + vec3 cv = normalize( cross(cu,cw) ); return mat3( cu, cv, cw ); } @@ -402,7 +402,7 @@ void main() vec2 p = (-resolution.xy + 2.0*gl_FragCoord.xy)/resolution.y; #endif - // RAY: Camera is provided from raylib + // RAY: Camera is provided from raylib //vec3 ro = vec3( -0.5+3.5*cos(0.1*time + 6.0*mo.x), 1.0 + 2.0*mo.y, 0.5 + 4.0*sin(0.1*time + 6.0*mo.x) ); vec3 ro = viewEye; @@ -413,10 +413,10 @@ void main() // ray direction vec3 rd = ca * normalize( vec3(p.xy,2.0) ); - // render + // render vec3 col = render( ro, rd ); - // gamma + // gamma col = pow( col, vec3(0.4545) ); tot += col; diff --git a/examples/shaders/resources/shaders/glsl100/sobel.fs b/examples/shaders/resources/shaders/glsl100/sobel.fs index 745562ad0..3e5c22dda 100644 --- a/examples/shaders/resources/shaders/glsl100/sobel.fs +++ b/examples/shaders/resources/shaders/glsl100/sobel.fs @@ -15,26 +15,26 @@ vec2 resolution = vec2(800.0, 450.0); void main() { - float x = 1.0/resolution.x; - float y = 1.0/resolution.y; + float x = 1.0/resolution.x; + float y = 1.0/resolution.y; - vec4 horizEdge = vec4(0.0); - horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; - horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))*2.0; - horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; - horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; - horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))*2.0; - horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; + vec4 horizEdge = vec4(0.0); + horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; + horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))*2.0; + horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; + horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; + horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))*2.0; + horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; - vec4 vertEdge = vec4(0.0); - vertEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; - vertEdge -= texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y - y))*2.0; - vertEdge -= texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; - vertEdge += texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; - vertEdge += texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y + y))*2.0; - vertEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; + vec4 vertEdge = vec4(0.0); + vertEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; + vertEdge -= texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y - y))*2.0; + vertEdge -= texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; + vertEdge += texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; + vertEdge += texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y + y))*2.0; + vertEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; - vec3 edge = sqrt((horizEdge.rgb*horizEdge.rgb) + (vertEdge.rgb*vertEdge.rgb)); - - gl_FragColor = vec4(edge, texture2D(texture0, fragTexCoord).a); + vec3 edge = sqrt((horizEdge.rgb*horizEdge.rgb) + (vertEdge.rgb*vertEdge.rgb)); + + gl_FragColor = vec4(edge, texture2D(texture0, fragTexCoord).a); } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl100/spotlight.fs b/examples/shaders/resources/shaders/glsl100/spotlight.fs index c1202610e..c8d830381 100644 --- a/examples/shaders/resources/shaders/glsl100/spotlight.fs +++ b/examples/shaders/resources/shaders/glsl100/spotlight.fs @@ -5,9 +5,9 @@ precision mediump float; #define MAX_SPOTS 3 struct Spot { - vec2 pos; // window coords of spot - float inner; // inner fully transparent centre radius - float radius; // alpha fades out to this radius + vec2 pos; // window coords of spot + float inner; // inner fully transparent centre radius + float radius; // alpha fades out to this radius }; uniform Spot spots[MAX_SPOTS]; // Spotlight positions array @@ -15,63 +15,63 @@ uniform float screenWidth; // Width of the screen void main() { - float alpha = 1.0; + float alpha = 1.0; - // Get the position of the current fragment (screen coordinates!) - vec2 pos = vec2(gl_FragCoord.x, gl_FragCoord.y); - - // Find out which spotlight is nearest - float d = 65000.0; // some high value - int fi = -1; // found index + // Get the position of the current fragment (screen coordinates!) + vec2 pos = vec2(gl_FragCoord.x, gl_FragCoord.y); + + // Find out which spotlight is nearest + float d = 65000.0; // some high value + int fi = -1; // found index for (int i = 0; i < MAX_SPOTS; i++) { - for (int j = 0; j < MAX_SPOTS; j++) - { - float dj = distance(pos, spots[j].pos) - spots[j].radius + spots[i].radius; + for (int j = 0; j < MAX_SPOTS; j++) + { + float dj = distance(pos, spots[j].pos) - spots[j].radius + spots[i].radius; - if (d > dj) - { - d = dj; - fi = i; - } - } + if (d > dj) + { + d = dj; + fi = i; + } + } } // d now equals distance to nearest spot... // allowing for the different radii of all spotlights if (fi == 0) { - if (d > spots[0].radius) alpha = 1.0; - else - { - if (d < spots[0].inner) alpha = 0.0; - else alpha = (d - spots[0].inner)/(spots[0].radius - spots[0].inner); - } - } + if (d > spots[0].radius) alpha = 1.0; + else + { + if (d < spots[0].inner) alpha = 0.0; + else alpha = (d - spots[0].inner)/(spots[0].radius - spots[0].inner); + } + } else if (fi == 1) { - if (d > spots[1].radius) alpha = 1.0; - else - { - if (d < spots[1].inner) alpha = 0.0; - else alpha = (d - spots[1].inner)/(spots[1].radius - spots[1].inner); - } - } + if (d > spots[1].radius) alpha = 1.0; + else + { + if (d < spots[1].inner) alpha = 0.0; + else alpha = (d - spots[1].inner)/(spots[1].radius - spots[1].inner); + } + } else if (fi == 2) { - if (d > spots[2].radius) alpha = 1.0; - else - { - if (d < spots[2].inner) alpha = 0.0; - else alpha = (d - spots[2].inner)/(spots[2].radius - spots[2].inner); - } - } - - // Right hand side of screen is dimly lit, + if (d > spots[2].radius) alpha = 1.0; + else + { + if (d < spots[2].inner) alpha = 0.0; + else alpha = (d - spots[2].inner)/(spots[2].radius - spots[2].inner); + } + } + + // Right hand side of screen is dimly lit, // could make the threshold value user definable - if ((pos.x > screenWidth/2.0) && (alpha > 0.9)) alpha = 0.9; + if ((pos.x > screenWidth/2.0) && (alpha > 0.9)) alpha = 0.9; - // could make the black out colour user definable... + // could make the black out colour user definable... gl_FragColor = vec4(0, 0, 0, alpha); } diff --git a/examples/shaders/resources/shaders/glsl100/wave.fs b/examples/shaders/resources/shaders/glsl100/wave.fs index bcc156cc6..50c4e02f3 100644 --- a/examples/shaders/resources/shaders/glsl100/wave.fs +++ b/examples/shaders/resources/shaders/glsl100/wave.fs @@ -22,15 +22,15 @@ uniform float speedX; uniform float speedY; void main() { - float pixelWidth = 1.0 / size.x; - float pixelHeight = 1.0 / size.y; - float aspect = pixelHeight / pixelWidth; - float boxLeft = 0.0; - float boxTop = 0.0; + float pixelWidth = 1.0 / size.x; + float pixelHeight = 1.0 / size.y; + float aspect = pixelHeight / pixelWidth; + float boxLeft = 0.0; + float boxTop = 0.0; - vec2 p = fragTexCoord; - p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (secondes * speedX)) * ampX * pixelWidth; - p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (secondes * speedY)) * ampY * pixelHeight; + vec2 p = fragTexCoord; + p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (secondes * speedX)) * ampX * pixelWidth; + p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (secondes * speedY)) * ampY * pixelHeight; - gl_FragColor = texture2D(texture0, p)*colDiffuse*fragColor; + gl_FragColor = texture2D(texture0, p)*colDiffuse*fragColor; } diff --git a/examples/shaders/resources/shaders/glsl120/bloom.fs b/examples/shaders/resources/shaders/glsl120/bloom.fs index c28836b0d..f97c18ed7 100644 --- a/examples/shaders/resources/shaders/glsl120/bloom.fs +++ b/examples/shaders/resources/shaders/glsl120/bloom.fs @@ -12,7 +12,7 @@ uniform vec4 colDiffuse; const vec2 size = vec2(800, 450); // render size const float samples = 5.0; // pixels per axis; higher = bigger glow, worse performance -const float quality = 2.5; // lower = smaller glow, better quality +const float quality = 2.5; // lower = smaller glow, better quality void main() { diff --git a/examples/shaders/resources/shaders/glsl120/dream_vision.fs b/examples/shaders/resources/shaders/glsl120/dream_vision.fs index 4ca2a8634..cb97b2bd0 100644 --- a/examples/shaders/resources/shaders/glsl120/dream_vision.fs +++ b/examples/shaders/resources/shaders/glsl120/dream_vision.fs @@ -32,4 +32,4 @@ void main() color = color/9.5; gl_FragColor = color; -} \ No newline at end of file +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl120/sobel.fs b/examples/shaders/resources/shaders/glsl120/sobel.fs index a3f3f2bf7..73e72703a 100644 --- a/examples/shaders/resources/shaders/glsl120/sobel.fs +++ b/examples/shaders/resources/shaders/glsl120/sobel.fs @@ -13,26 +13,26 @@ vec2 resolution = vec2(800.0, 450.0); void main() { - float x = 1.0/resolution.x; - float y = 1.0/resolution.y; + float x = 1.0/resolution.x; + float y = 1.0/resolution.y; - vec4 horizEdge = vec4(0.0); - horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; - horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))*2.0; - horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; - horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; - horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))*2.0; - horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; + vec4 horizEdge = vec4(0.0); + horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; + horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))*2.0; + horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; + horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; + horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))*2.0; + horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; - vec4 vertEdge = vec4(0.0); - vertEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; - vertEdge -= texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y - y))*2.0; - vertEdge -= texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; - vertEdge += texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; - vertEdge += texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y + y))*2.0; - vertEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; + vec4 vertEdge = vec4(0.0); + vertEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; + vertEdge -= texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y - y))*2.0; + vertEdge -= texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; + vertEdge += texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; + vertEdge += texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y + y))*2.0; + vertEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; - vec3 edge = sqrt((horizEdge.rgb*horizEdge.rgb) + (vertEdge.rgb*vertEdge.rgb)); - - gl_FragColor = vec4(edge, texture2D(texture0, fragTexCoord).a); + vec3 edge = sqrt((horizEdge.rgb*horizEdge.rgb) + (vertEdge.rgb*vertEdge.rgb)); + + gl_FragColor = vec4(edge, texture2D(texture0, fragTexCoord).a); } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/bloom.fs b/examples/shaders/resources/shaders/glsl330/bloom.fs index 333d5b059..549cde8f8 100644 --- a/examples/shaders/resources/shaders/glsl330/bloom.fs +++ b/examples/shaders/resources/shaders/glsl330/bloom.fs @@ -15,7 +15,7 @@ out vec4 finalColor; const vec2 size = vec2(800, 450); // render size const float samples = 5.0; // pixels per axis; higher = bigger glow, worse performance -const float quality = 2.5; // lower = smaller glow, better quality +const float quality = 2.5; // lower = smaller glow, better quality void main() { diff --git a/examples/shaders/resources/shaders/glsl330/dream_vision.fs b/examples/shaders/resources/shaders/glsl330/dream_vision.fs index 031158628..31d3fd2f3 100644 --- a/examples/shaders/resources/shaders/glsl330/dream_vision.fs +++ b/examples/shaders/resources/shaders/glsl330/dream_vision.fs @@ -31,4 +31,4 @@ void main() color = color/9.5; fragColor = color; -} \ No newline at end of file +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/eratosthenes.fs b/examples/shaders/resources/shaders/glsl330/eratosthenes.fs index 92d2d2571..14711222b 100644 --- a/examples/shaders/resources/shaders/glsl330/eratosthenes.fs +++ b/examples/shaders/resources/shaders/glsl330/eratosthenes.fs @@ -38,9 +38,9 @@ vec4 Colorizer(float counter, float maxSize) void main() { - vec4 color = vec4(1.0); - float scale = 1000.0; // Makes 100x100 square grid. Change this variable to make a smaller or larger grid. - int value = int(scale*floor(fragTexCoord.y*scale)+floor(fragTexCoord.x*scale)); // Group pixels into boxes representing integer values + vec4 color = vec4(1.0); + float scale = 1000.0; // Makes 100x100 square grid. Change this variable to make a smaller or larger grid. + int value = int(scale*floor(fragTexCoord.y*scale)+floor(fragTexCoord.x*scale)); // Group pixels into boxes representing integer values if ((value == 0) || (value == 1) || (value == 2)) finalColor = vec4(1.0); else diff --git a/examples/shaders/resources/shaders/glsl330/raymarching.fs b/examples/shaders/resources/shaders/glsl330/raymarching.fs index 3cec58a2b..5b0caa652 100644 --- a/examples/shaders/resources/shaders/glsl330/raymarching.fs +++ b/examples/shaders/resources/shaders/glsl330/raymarching.fs @@ -43,7 +43,7 @@ uniform vec2 resolution; float sdPlane( vec3 p ) { - return p.y; + return p.y; } float sdSphere( vec3 p, float s ) @@ -86,9 +86,9 @@ float sdHexPrism( vec3 p, vec2 h ) float sdCapsule( vec3 p, vec3 a, vec3 b, float r ) { - vec3 pa = p-a, ba = b-a; - float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); - return length( pa - ba*h ) - r; + vec3 pa = p-a, ba = b-a; + float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); + return length( pa - ba*h ) - r; } float sdEquilateralTriangle( in vec2 p ) @@ -155,19 +155,19 @@ float sdPryamid4(vec3 p, vec3 h ) // h = { cos a, sin a, height } float length2( vec2 p ) { - return sqrt( p.x*p.x + p.y*p.y ); + return sqrt( p.x*p.x + p.y*p.y ); } float length6( vec2 p ) { - p = p*p*p; p = p*p; - return pow( p.x + p.y, 1.0/6.0 ); + p = p*p*p; p = p*p; + return pow( p.x + p.y, 1.0/6.0 ); } float length8( vec2 p ) { - p = p*p; p = p*p; p = p*p; - return pow( p.x + p.y, 1.0/8.0 ); + p = p*p; p = p*p; p = p*p; + return pow( p.x + p.y, 1.0/8.0 ); } float sdTorus82( vec3 p, vec2 t ) @@ -196,7 +196,7 @@ float opS( float d1, float d2 ) vec2 opU( vec2 d1, vec2 d2 ) { - return (d1.xtmax ) break; t += res.x; - m = res.y; + m = res.y; } if( t>tmax ) m=-1.0; @@ -272,11 +272,11 @@ vec2 castRay( in vec3 ro, in vec3 rd ) float calcSoftshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax ) { - float res = 1.0; + float res = 1.0; float t = mint; for( int i=0; i<16; i++ ) { - float h = map( ro + rd*t ).x; + float h = map( ro + rd*t ).x; res = min( res, 8.0*h/t ); t += clamp( h, 0.02, 0.10 ); if( h<0.001 || t>tmax ) break; @@ -288,22 +288,22 @@ vec3 calcNormal( in vec3 pos ) { vec2 e = vec2(1.0,-1.0)*0.5773*0.0005; return normalize( e.xyy*map( pos + e.xyy ).x + - e.yyx*map( pos + e.yyx ).x + - e.yxy*map( pos + e.yxy ).x + - e.xxx*map( pos + e.xxx ).x ); + e.yyx*map( pos + e.yyx ).x + + e.yxy*map( pos + e.yxy ).x + + e.xxx*map( pos + e.xxx ).x ); /* - vec3 eps = vec3( 0.0005, 0.0, 0.0 ); - vec3 nor = vec3( - map(pos+eps.xyy).x - map(pos-eps.xyy).x, - map(pos+eps.yxy).x - map(pos-eps.yxy).x, - map(pos+eps.yyx).x - map(pos-eps.yyx).x ); - return normalize(nor); - */ + vec3 eps = vec3( 0.0005, 0.0, 0.0 ); + vec3 nor = vec3( + map(pos+eps.xyy).x - map(pos-eps.xyy).x, + map(pos+eps.yxy).x - map(pos-eps.yxy).x, + map(pos+eps.yyx).x - map(pos-eps.yyx).x ); + return normalize(nor); + */ } float calcAO( in vec3 pos, in vec3 nor ) { - float occ = 0.0; + float occ = 0.0; float sca = 1.0; for( int i=0; i<5; i++ ) { @@ -332,7 +332,7 @@ vec3 render( in vec3 ro, in vec3 rd ) vec3 col = vec3(0.7, 0.9, 1.0) +rd.y*0.8; vec2 res = castRay(ro,rd); float t = res.x; - float m = res.y; + float m = res.y; if( m>-0.5 ) { vec3 pos = ro + t*rd; @@ -340,7 +340,7 @@ vec3 render( in vec3 ro, in vec3 rd ) vec3 ref = reflect( rd, nor ); // material - col = 0.45 + 0.35*sin( vec3(0.05,0.08,0.10)*(m-1.0) ); + col = 0.45 + 0.35*sin( vec3(0.05,0.08,0.10)*(m-1.0) ); if( m<1.5 ) { @@ -350,9 +350,9 @@ vec3 render( in vec3 ro, in vec3 rd ) // lighting float occ = calcAO( pos, nor ); - vec3 lig = normalize( vec3(cos(-0.4 * runTime), sin(0.7 * runTime), -0.6) ); + vec3 lig = normalize( vec3(cos(-0.4 * runTime), sin(0.7 * runTime), -0.6) ); vec3 hal = normalize( lig-rd ); - float amb = clamp( 0.5+0.5*nor.y, 0.0, 1.0 ); + float amb = clamp( 0.5+0.5*nor.y, 0.0, 1.0 ); float dif = clamp( dot( nor, lig ), 0.0, 1.0 ); float bac = clamp( dot( nor, normalize(vec3(-lig.x,0.0,-lig.z))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0); float dom = smoothstep( -0.1, 0.1, ref.y ); @@ -361,31 +361,31 @@ vec3 render( in vec3 ro, in vec3 rd ) dif *= calcSoftshadow( pos, lig, 0.02, 2.5 ); dom *= calcSoftshadow( pos, ref, 0.02, 2.5 ); - float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0)* + float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0)* dif * (0.04 + 0.96*pow( clamp(1.0+dot(hal,rd),0.0,1.0), 5.0 )); - vec3 lin = vec3(0.0); + vec3 lin = vec3(0.0); lin += 1.30*dif*vec3(1.00,0.80,0.55); lin += 0.40*amb*vec3(0.40,0.60,1.00)*occ; lin += 0.50*dom*vec3(0.40,0.60,1.00)*occ; lin += 0.50*bac*vec3(0.25,0.25,0.25)*occ; lin += 0.25*fre*vec3(1.00,1.00,1.00)*occ; - col = col*lin; - col += 10.00*spe*vec3(1.00,0.90,0.70); + col = col*lin; + col += 10.00*spe*vec3(1.00,0.90,0.70); - col = mix( col, vec3(0.8,0.9,1.0), 1.0-exp( -0.0002*t*t*t ) ); + col = mix( col, vec3(0.8,0.9,1.0), 1.0-exp( -0.0002*t*t*t ) ); } - return vec3( clamp(col,0.0,1.0) ); + return vec3( clamp(col,0.0,1.0) ); } mat3 setCamera( in vec3 ro, in vec3 ta, float cr ) { vec3 cw = normalize(ta-ro); - vec3 cp = vec3(sin(cr), cos(cr),0.0); - vec3 cu = normalize( cross(cw,cp) ); - vec3 cv = normalize( cross(cu,cw) ); + vec3 cp = vec3(sin(cr), cos(cr),0.0); + vec3 cu = normalize( cross(cw,cp) ); + vec3 cv = normalize( cross(cu,cw) ); return mat3( cu, cv, cw ); } @@ -403,7 +403,7 @@ void main() vec2 p = (-resolution.xy + 2.0*gl_FragCoord.xy)/resolution.y; #endif - // RAY: Camera is provided from raylib + // RAY: Camera is provided from raylib //vec3 ro = vec3( -0.5+3.5*cos(0.1*time + 6.0*mo.x), 1.0 + 2.0*mo.y, 0.5 + 4.0*sin(0.1*time + 6.0*mo.x) ); vec3 ro = viewEye; @@ -414,10 +414,10 @@ void main() // ray direction vec3 rd = ca * normalize( vec3(p.xy,2.0) ); - // render + // render vec3 col = render( ro, rd ); - // gamma + // gamma col = pow( col, vec3(0.4545) ); tot += col; diff --git a/examples/shaders/resources/shaders/glsl330/reload.fs b/examples/shaders/resources/shaders/glsl330/reload.fs index 9891a4b5f..21ce18e02 100644 --- a/examples/shaders/resources/shaders/glsl330/reload.fs +++ b/examples/shaders/resources/shaders/glsl330/reload.fs @@ -15,26 +15,26 @@ uniform float time; // Total run time (in secods) // Draw circle vec4 DrawCircle(vec2 fragCoord, vec2 position, float radius, vec3 color) { - float d = length(position - fragCoord) - radius; - float t = clamp(d, 0.0, 1.0); - return vec4(color, 1.0 - t); + float d = length(position - fragCoord) - radius; + float t = clamp(d, 0.0, 1.0); + return vec4(color, 1.0 - t); } void main() { - vec2 fragCoord = gl_FragCoord.xy; - vec2 position = vec2(mouse.x, resolution.y - mouse.y); - float radius = 40.0; + vec2 fragCoord = gl_FragCoord.xy; + vec2 position = vec2(mouse.x, resolution.y - mouse.y); + float radius = 40.0; // Draw background layer vec4 colorA = vec4(0.2,0.2,0.8, 1.0); vec4 colorB = vec4(1.0,0.7,0.2, 1.0); - vec4 layer1 = mix(colorA, colorB, abs(sin(time*0.1))); + vec4 layer1 = mix(colorA, colorB, abs(sin(time*0.1))); - // Draw circle layer - vec3 color = vec3(0.9, 0.16, 0.21); - vec4 layer2 = DrawCircle(fragCoord, position, radius, color); - - // Blend the two layers - finalColor = mix(layer1, layer2, layer2.a); + // Draw circle layer + vec3 color = vec3(0.9, 0.16, 0.21); + vec4 layer2 = DrawCircle(fragCoord, position, radius, color); + + // Blend the two layers + finalColor = mix(layer1, layer2, layer2.a); } diff --git a/examples/shaders/resources/shaders/glsl330/sobel.fs b/examples/shaders/resources/shaders/glsl330/sobel.fs index a1430cdd0..f317ae328 100644 --- a/examples/shaders/resources/shaders/glsl330/sobel.fs +++ b/examples/shaders/resources/shaders/glsl330/sobel.fs @@ -16,26 +16,26 @@ uniform vec2 resolution = vec2(800, 450); void main() { - float x = 1.0/resolution.x; - float y = 1.0/resolution.y; + float x = 1.0/resolution.x; + float y = 1.0/resolution.y; - vec4 horizEdge = vec4(0.0); - horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; - horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))*2.0; - horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; - horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; - horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))*2.0; - horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; + vec4 horizEdge = vec4(0.0); + horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; + horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))*2.0; + horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; + horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; + horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))*2.0; + horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; - vec4 vertEdge = vec4(0.0); - vertEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; - vertEdge -= texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y - y))*2.0; - vertEdge -= texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; - vertEdge += texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; - vertEdge += texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y + y))*2.0; - vertEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; + vec4 vertEdge = vec4(0.0); + vertEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; + vertEdge -= texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y - y))*2.0; + vertEdge -= texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; + vertEdge += texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; + vertEdge += texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y + y))*2.0; + vertEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; - vec3 edge = sqrt((horizEdge.rgb*horizEdge.rgb) + (vertEdge.rgb*vertEdge.rgb)); - - finalColor = vec4(edge, texture2D(texture0, fragTexCoord).a); + vec3 edge = sqrt((horizEdge.rgb*horizEdge.rgb) + (vertEdge.rgb*vertEdge.rgb)); + + finalColor = vec4(edge, texture2D(texture0, fragTexCoord).a); } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/spotlight.fs b/examples/shaders/resources/shaders/glsl330/spotlight.fs index 97a4377b4..c573be8f3 100644 --- a/examples/shaders/resources/shaders/glsl330/spotlight.fs +++ b/examples/shaders/resources/shaders/glsl330/spotlight.fs @@ -12,9 +12,9 @@ out vec4 finalColor; #define MAX_SPOTS 3 struct Spot { - vec2 pos; // window coords of spot - float inner; // inner fully transparent centre radius - float radius; // alpha fades out to this radius + vec2 pos; // window coords of spot + float inner; // inner fully transparent centre radius + float radius; // alpha fades out to this radius }; uniform Spot spots[MAX_SPOTS]; // Spotlight positions array @@ -22,44 +22,44 @@ uniform float screenWidth; // Width of the screen void main() { - float alpha = 1.0; + float alpha = 1.0; - // Get the position of the current fragment (screen coordinates!) - vec2 pos = vec2(gl_FragCoord.x, gl_FragCoord.y); - - // Find out which spotlight is nearest - float d = 65000; // some high value - int fi = -1; // found index + // Get the position of the current fragment (screen coordinates!) + vec2 pos = vec2(gl_FragCoord.x, gl_FragCoord.y); + + // Find out which spotlight is nearest + float d = 65000; // some high value + int fi = -1; // found index for (int i = 0; i < MAX_SPOTS; i++) { - for (int j = 0; j < MAX_SPOTS; j++) - { - float dj = distance(pos, spots[j].pos) - spots[j].radius + spots[i].radius; + for (int j = 0; j < MAX_SPOTS; j++) + { + float dj = distance(pos, spots[j].pos) - spots[j].radius + spots[i].radius; - if (d > dj) - { - d = dj; - fi = i; - } - } + if (d > dj) + { + d = dj; + fi = i; + } + } } // d now equals distance to nearest spot... // allowing for the different radii of all spotlights if (fi != -1) { - if (d > spots[fi].radius) alpha = 1.0; - else - { - if (d < spots[fi].inner) alpha = 0.0; - else alpha = (d - spots[fi].inner) / (spots[fi].radius - spots[fi].inner); - } - } - - // Right hand side of screen is dimly lit, + if (d > spots[fi].radius) alpha = 1.0; + else + { + if (d < spots[fi].inner) alpha = 0.0; + else alpha = (d - spots[fi].inner) / (spots[fi].radius - spots[fi].inner); + } + } + + // Right hand side of screen is dimly lit, // could make the threshold value user definable - if ((pos.x > screenWidth/2.0) && (alpha > 0.9)) alpha = 0.9; + if ((pos.x > screenWidth/2.0) && (alpha > 0.9)) alpha = 0.9; finalColor = vec4(0, 0, 0, alpha); } diff --git a/examples/shaders/resources/shaders/glsl330/wave.fs b/examples/shaders/resources/shaders/glsl330/wave.fs index f139f3957..43efee234 100644 --- a/examples/shaders/resources/shaders/glsl330/wave.fs +++ b/examples/shaders/resources/shaders/glsl330/wave.fs @@ -23,15 +23,15 @@ uniform float speedX; uniform float speedY; void main() { - float pixelWidth = 1.0 / size.x; - float pixelHeight = 1.0 / size.y; - float aspect = pixelHeight / pixelWidth; - float boxLeft = 0.0; - float boxTop = 0.0; + float pixelWidth = 1.0 / size.x; + float pixelHeight = 1.0 / size.y; + float aspect = pixelHeight / pixelWidth; + float boxLeft = 0.0; + float boxTop = 0.0; - vec2 p = fragTexCoord; - p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (secondes * speedX)) * ampX * pixelWidth; - p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (secondes * speedY)) * ampY * pixelHeight; + vec2 p = fragTexCoord; + p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (secondes * speedX)) * ampX * pixelWidth; + p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (secondes * speedY)) * ampY * pixelHeight; - finalColor = texture(texture0, p)*colDiffuse*fragColor; + finalColor = texture(texture0, p)*colDiffuse*fragColor; } From 88a6f16c9a552ebb8c39fff57cf16cfce7c88913 Mon Sep 17 00:00:00 2001 From: hristo Date: Tue, 26 Jan 2021 15:34:27 +0200 Subject: [PATCH 43/43] Documentation cmake (#1549) * Documenting the compiler flags * Moved some android compiler flags and added documentation on them too. * Some more restructuring. Removed unnecessary comments that were self described by the code. Added some more explanations around certain parts of CMake and especially around compiler flags. --- CMakeLists.txt | 6 +- cmake/CompilerFlags.cmake | 76 ++++++++--- cmake/LibraryConfigurations.cmake | 51 ++++---- examples/CMakeLists.txt | 208 ++++++++++++++++-------------- src/CMakeLists.txt | 23 ++-- 5 files changed, 217 insertions(+), 147 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c5d2113c..5fe826dff 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,9 +2,11 @@ cmake_minimum_required(VERSION 3.0) project(raylib) # Directory for easier includes +# Anywhere you see include(...) you can check /cmake for that file 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. +# RAYLIB_IS_MAIN determines whether the project is being used from root +# or if it is added as a dependency (through add_subdirectory for example). if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") set(RAYLIB_IS_MAIN TRUE) else() @@ -17,7 +19,7 @@ include(CompilerFlags) # Registers build options that are exposed to cmake include(CMakeOptions.txt) -# Checks a few environment and compiler configurations +# Enforces a few environment and compiler configurations include(BuildOptions) # Main sources directory (the second parameter sets the output directory name to raylib) diff --git a/cmake/CompilerFlags.cmake b/cmake/CompilerFlags.cmake index 41fee6922..970462c8b 100644 --- a/cmake/CompilerFlags.cmake +++ b/cmake/CompilerFlags.cmake @@ -1,28 +1,52 @@ include(AddIfFlagCompiles) +# Makes +/- operations on void pointers be considered an error +# https://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html add_if_flag_compiles(-Werror=pointer-arith CMAKE_C_FLAGS) + +# Generates error whenever a function is used before being declared +# https://gcc.gnu.org/onlinedocs/gcc-4.0.1/gcc/Warning-Options.html add_if_flag_compiles(-Werror=implicit-function-declaration CMAKE_C_FLAGS) -# src/external/jar_xm.h does shady stuff + +# Allows some casting of pointers without generating a warning 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) + # MSAN and ASAN both work on memory - ASAN does more things MESSAGE(WARNING "Compiling with both AddressSanitizer and MemorySanitizer is not recommended") endif() -add_definitions("-DRAYLIB_CMAKE=1") +if (ENABLE_ASAN) + + # If enabled it would generate errors/warnings for all kinds of memory errors + # (like returning a stack variable by reference) + # https://clang.llvm.org/docs/AddressSanitizer.html + + 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) + + # If enabled this will generate errors for undefined behavior points + # (like adding +1 to the maximum int value) + # https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html + + 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) + + # If enabled this will generate warnings for places where uninitialized memory is used + # https://clang.llvm.org/docs/MemorySanitizer.html + + 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(CMAKE_VERSION VERSION_LESS "3.1") if(CMAKE_C_COMPILER_ID STREQUAL "GNU") @@ -31,3 +55,25 @@ if(CMAKE_VERSION VERSION_LESS "3.1") else() set (CMAKE_C_STANDARD 99) endif() + +if(${PLATFORM} MATCHES "Android") + + # If enabled will remove dead code during the linking process + # https://gcc.gnu.org/onlinedocs/gnat_ugn/Compilation-options.html + add_if_flag_compiles(-ffunction-sections CMAKE_C_FLAGS) + + # If enabled will generate some exception data (usually disabled for C programs) + # https://gcc.gnu.org/onlinedocs/gcc-4.2.4/gcc/Code-Gen-Options.html + add_if_flag_compiles(-funwind-tables CMAKE_C_FLAGS) + + # If enabled adds stack protection guards around functions that allocate memory + # https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_cjh1548250046139.htm + add_if_flag_compiles(-fstack-protector-strong CMAKE_C_FLAGS) + + # Marks that the library will not be compiled with an executable stack + add_if_flag_compiles(-Wa,--noexecstack CMAKE_C_FLAGS) + + # Do not expand symbolic links or resolve paths like "/./" or "/../", etc. + # https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html + add_if_flag_compiles(-no-canonical-prefixes CMAKE_C_FLAGS) +endif() diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index cf5e85f8e..1b1961b9b 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -1,7 +1,7 @@ -if(${PLATFORM} MATCHES "Desktop") +if (${PLATFORM} MATCHES "Desktop") set(PLATFORM_CPP "PLATFORM_DESKTOP") - if(APPLE) + 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") @@ -11,40 +11,35 @@ if(${PLATFORM} MATCHES "Desktop") 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) + endif () + elseif (WIN32) add_definitions(-D_CRT_SECURE_NO_WARNINGS) set(LIBS_PRIVATE ${LIBS_PRIVATE} winmm) - else() + else () find_library(pthread NAMES pthread) find_package(OpenGL QUIET) if ("${OPENGL_LIBRARIES}" STREQUAL "") set(OPENGL_LIBRARIES "GL") - endif() + endif () if ("${CMAKE_SYSTEM_NAME}" MATCHES "(Net|Open)BSD") find_library(OSS_LIBRARY ossaudio) - endif() + endif () set(LIBS_PRIVATE m pthread ${OPENGL_LIBRARIES} ${OSS_LIBRARY}) - endif() + endif () -elseif(${PLATFORM} MATCHES "Web") +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_C_FLAGS "${CMAKE_C_FLAGS} -s USE_GLFW=3 -s ASSERTIONS=1 --profiling") set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") -elseif(${PLATFORM} MATCHES "Android") +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") @@ -52,7 +47,7 @@ elseif(${PLATFORM} MATCHES "Android") find_library(OPENGL_LIBRARY OpenGL) set(LIBS_PRIVATE m log android EGL GLESv2 OpenSLES atomic c) -elseif(${PLATFORM} MATCHES "Raspberry Pi") +elseif (${PLATFORM} MATCHES "Raspberry Pi") set(PLATFORM_CPP "PLATFORM_RPI") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") @@ -65,7 +60,7 @@ elseif(${PLATFORM} MATCHES "Raspberry Pi") link_directories(/opt/vc/lib) set(LIBS_PRIVATE ${GLESV2} ${EGL} ${BCMHOST} pthread rt m dl) -elseif(${PLATFORM} MATCHES "DRM") +elseif (${PLATFORM} MATCHES "DRM") set(PLATFORM_CPP "PLATFORM_DRM") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") @@ -81,7 +76,7 @@ elseif(${PLATFORM} MATCHES "DRM") include_directories(/usr/include/libdrm) set(LIBS_PRIVATE ${GLESV2} ${EGL} ${DRM} ${GBM} pthread m dl) -endif() +endif () if (${OPENGL_VERSION}) set(${SUGGESTED_GRAPHICS} "${GRAPHICS}") @@ -93,12 +88,18 @@ if (${OPENGL_VERSION}) set(GRAPHICS "GRAPHICS_API_OPENGL_11") elseif (${OPENGL_VERSION} MATCHES "ES 2.0") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") - endif() + 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() + endif () +endif () -if(NOT GRAPHICS) +if (NOT GRAPHICS) set(GRAPHICS "GRAPHICS_API_OPENGL_33") -endif() \ No newline at end of file +endif () + +set(LIBS_PRIVATE ${LIBS_PRIVATE} ${OPENAL_LIBRARY}) + +if (${PLATFORM} MATCHES "Desktop") + set(LIBS_PRIVATE ${LIBS_PRIVATE} glfw) +endif () \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e3ef3a0fc..ad391edd4 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,122 +1,138 @@ # Setup the project and settings project(examples) -# Get the sources together -set(example_dirs audio core models others shaders shapes text textures) +# Directories that contain examples +set(example_dirs + audio + core + models + others + shaders + shapes + text + textures + ) + +# Next few lines will check for existence of symbols or header files +# They are needed for the physac example and threads examples set(CMAKE_REQUIRED_DEFINITIONS -D_POSIX_C_SOURCE=199309L) - include(CheckSymbolExists) - check_symbol_exists(CLOCK_MONOTONIC time.h HAVE_CLOCK_MONOTONIC) - check_symbol_exists(QueryPerformanceCounter windows.h HAVE_QPC) +include(CheckSymbolExists) +check_symbol_exists(CLOCK_MONOTONIC time.h HAVE_CLOCK_MONOTONIC) +check_symbol_exists(QueryPerformanceCounter windows.h HAVE_QPC) set(CMAKE_REQUIRED_DEFINITIONS) -if(HAVE_QPC OR HAVE_CLOCK_MONOTONIC) - set(example_dirs ${example_dirs} physac) -endif() -set(example_sources) -set(example_resources) -foreach(example_dir ${example_dirs}) - # Get the .c files - file(GLOB sources ${example_dir}/*.c) - list(APPEND example_sources ${sources}) - - # Any any resources - file(GLOB resources ${example_dir}/resources/*) - list(APPEND example_resources ${resources}) -endforeach() - -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() +if (HAVE_QPC OR HAVE_CLOCK_MONOTONIC) + set(example_dirs ${example_dirs} physac) +endif () include(CheckIncludeFile) CHECK_INCLUDE_FILE("stdatomic.h" HAVE_STDATOMIC_H) set(CMAKE_THREAD_PREFER_PTHREAD TRUE) find_package(Threads) if (CMAKE_USE_PTHREADS_INIT AND HAVE_STDATOMIC_H) - add_if_flag_compiles("-std=c11" CMAKE_C_FLAGS) - if(THREADS_HAVE_PTHREAD_ARG) - add_if_flag_compiles("-pthread" CMAKE_C_FLAGS) - endif() - if(CMAKE_THREAD_LIBS_INIT) - link_libraries("${CMAKE_THREAD_LIBS_INIT}") - endif() -else() - # Items requiring pthreads - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_loading_thread.c) -endif() + add_if_flag_compiles("-std=c11" CMAKE_C_FLAGS) + if (THREADS_HAVE_PTHREAD_ARG) + add_if_flag_compiles("-pthread" CMAKE_C_FLAGS) + endif () + if (CMAKE_THREAD_LIBS_INIT) + link_libraries("${CMAKE_THREAD_LIBS_INIT}") + endif () +endif () +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 () -if(${PLATFORM} MATCHES "Android") - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/rlgl_standalone.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/standard_lighting.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_3d_picking.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_vr_simulator.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_3d_camera_free.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_3d_camera_first_person.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_world_screen.c) +# Collect all source files and resource files +# into a CMake variable +set(example_sources) +set(example_resources) +foreach (example_dir ${example_dirs}) + # Get the .c files + file(GLOB sources ${example_dir}/*.c) + list(APPEND example_sources ${sources}) + + # Any any resources + file(GLOB resources ${example_dir}/resources/*) + list(APPEND example_resources ${resources}) +endforeach () - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_mesh_picking.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_material_pbr.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_cubicmap.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_skybox.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_mesh_picking.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_mesh_generation.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_heightmap.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_billboard.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_rlgl_solar_system.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_rlgl_full_solar_system.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_solar_system.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_obj_viewer.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_animation.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_first_person_maze.c) +if(NOT CMAKE_USE_PTHREADS_INIT OR NOT HAVE_STDATOMIC_H) + # Items requiring pthreads + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_loading_thread.c) +endif () - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_custom_uniform.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_model_shader.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_postprocessing.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_raymarching.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_palette_switch.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_basic_lighting.c) +if (${PLATFORM} MATCHES "Android") + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/rlgl_standalone.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/standard_lighting.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_3d_picking.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_vr_simulator.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_3d_camera_free.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_3d_camera_first_person.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/core/core_world_screen.c) + + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_mesh_picking.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_material_pbr.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_cubicmap.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_skybox.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_mesh_picking.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_mesh_generation.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_heightmap.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_billboard.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_rlgl_solar_system.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_rlgl_full_solar_system.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_solar_system.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_obj_viewer.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_animation.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_first_person_maze.c) + + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_custom_uniform.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_model_shader.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_postprocessing.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_raymarching.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_palette_switch.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_basic_lighting.c) -elseif(${PLATFORM} MATCHES "Web") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY") - # Since WASM is used, ALLOW_MEMORY_GROWTH has no extra overheads - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s ALLOW_MEMORY_GROWTH=1 --no-heap-copy") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --shell-file ${CMAKE_SOURCE_DIR}/src/shell.html") - set(CMAKE_EXECUTABLE_SUFFIX ".html") - - # Remove the -rdynamic flag because otherwise emscripten - # does not generate HTML+JS+WASM files, only a non-working - # and fat HTML - string(REPLACE "-rdynamic" "" CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}") -endif() +elseif (${PLATFORM} MATCHES "Web") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY") + # Since WASM is used, ALLOW_MEMORY_GROWTH has no extra overheads + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s ALLOW_MEMORY_GROWTH=1 --no-heap-copy") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --shell-file ${CMAKE_SOURCE_DIR}/src/shell.html") + set(CMAKE_EXECUTABLE_SUFFIX ".html") + + # Remove the -rdynamic flag because otherwise emscripten + # does not generate HTML+JS+WASM files, only a non-working + # and fat HTML + string(REPLACE "-rdynamic" "" CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}") +endif () include_directories(BEFORE SYSTEM others/external/include) if (NOT TARGET raylib) - find_package(raylib 2.0 REQUIRED) -endif() + find_package(raylib 2.0 REQUIRED) +endif () # Do each example -foreach(example_source ${example_sources}) - # Create the basename for the example - get_filename_component(example_name ${example_source} NAME) - string(REPLACE ".c" "" example_name ${example_name}) - - # Setup the example - add_executable(${example_name} ${example_source}) - - target_link_libraries(${example_name} raylib) - - string(REGEX MATCH ".*/.*/" resources_dir ${example_source}) - string(APPEND resources_dir "resources") - - if(${PLATFORM} MATCHES "Web" AND EXISTS ${resources_dir}) - # The local resources path needs to be mapped to /resources virtual path - string(APPEND resources_dir "@resources") - set_target_properties(${example_name} PROPERTIES LINK_FLAGS "--preload-file ${resources_dir}") - endif() -endforeach() +foreach (example_source ${example_sources}) + # Create the basename for the example + get_filename_component(example_name ${example_source} NAME) + string(REPLACE ".c" "" example_name ${example_name}) + + # Setup the example + add_executable(${example_name} ${example_source}) + + target_link_libraries(${example_name} raylib) + + string(REGEX MATCH ".*/.*/" resources_dir ${example_source}) + string(APPEND resources_dir "resources") + + if (${PLATFORM} MATCHES "Web" AND EXISTS ${resources_dir}) + # The local resources path needs to be mapped to /resources virtual path + string(APPEND resources_dir "@resources") + set_target_properties(${example_name} PROPERTIES LINK_FLAGS "--preload-file ${resources_dir}") + endif () +endforeach () # 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 bcef058bb..d1d80652b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,6 +10,8 @@ include(JoinPaths) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) if(RAYLIB_IS_MAIN) set(default_build_type Debug) + else() + message(WARNING "Default build type is not set (CMAKE_BUILD_TYPE)") endif() message(STATUS "Setting build type to '${default_build_type}' as none was specified.") @@ -18,7 +20,7 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() -# Get the sources together +# Used as public API to be included into other projects set(raylib_public_headers raylib.h rlgl.h @@ -27,6 +29,7 @@ set(raylib_public_headers raudio.h ) +# Sources to be compiled set(raylib_sources core.c models.c @@ -36,7 +39,7 @@ set(raylib_sources utils.c ) -# cmake/GlfwImport.cmake handles the details around the inclusion of glfw +# /cmake/GlfwImport.cmake handles the details around the inclusion of glfw include(GlfwImport) @@ -47,10 +50,11 @@ else () MESSAGE(STATUS "Audio Backend: None (-DUSE_AUDIO=OFF)") endif () +# Sets additional platform options and link libraries for each platform +# also selects the proper graphics API and version for that platform +# Produces a variable LIBS_PRIVATE that will be used later include(LibraryConfigurations) -set(LIBS_PRIVATE ${LIBS_PRIVATE} ${OPENAL_LIBRARY}) - add_library(raylib ${raylib_sources} ${raylib_public_headers}) if (NOT BUILD_SHARED_LIBS) @@ -66,7 +70,6 @@ else() endif () endif() -# Setting target properties set_target_properties(raylib PROPERTIES PUBLIC_HEADER "${raylib_public_headers}" VERSION ${PROJECT_VERSION} @@ -77,12 +80,11 @@ if (WITH_PIC OR BUILD_SHARED_LIBS) set_property(TARGET raylib PROPERTY POSITION_INDEPENDENT_CODE ON) endif () -# Linking libraries target_link_libraries(raylib "${LIBS_PRIVATE}") -if (${PLATFORM} MATCHES "Desktop") - target_link_libraries(raylib glfw) -endif () +# Sets some compile time definitions for the pre-processor +# If CUSTOMIZE_BUILD option is on you will not use config.h by default +# and you will be able to select more build options include(CompileDefinitions) # Registering include directories @@ -99,6 +101,8 @@ target_include_directories(raylib # Copy the header files to the build directory for convenience file(COPY ${raylib_public_headers} DESTINATION "include") +# Includes information on how the library will be installed on the system +# when cmake --install is run include(InstallConfigurations) # Print the flags for the user @@ -112,6 +116,7 @@ message(STATUS "Compiling with the flags:") message(STATUS " PLATFORM=" ${PLATFORM_CPP}) message(STATUS " GRAPHICS=" ${GRAPHICS}) +# Options if you want to create an installer using CPack include(PackConfigurations) enable_testing()