diff options
| author | Sebastiano Tronto <sebastiano@tronto.net> | 2025-04-21 11:09:56 +0200 |
|---|---|---|
| committer | Sebastiano Tronto <sebastiano@tronto.net> | 2025-04-21 11:09:56 +0200 |
| commit | 123144c93bfc77883c8fb517828b47bbe13b8671 (patch) | |
| tree | 762739afedb5f3f168051367515cb9b9a06ec68d /raylib/src | |
| download | minesweeper-master.tar.gz minesweeper-master.zip | |
Diffstat (limited to 'raylib/src')
160 files changed, 316972 insertions, 0 deletions
diff --git a/raylib/src/CMakeLists.txt b/raylib/src/CMakeLists.txt new file mode 100644 index 0000000..9735e26 --- /dev/null +++ b/raylib/src/CMakeLists.txt | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | # Setup the project and settings | ||
| 2 | project(raylib C) | ||
| 3 | set(PROJECT_VERSION 5.5.0) | ||
| 4 | set(API_VERSION 550) | ||
| 5 | |||
| 6 | include(GNUInstallDirs) | ||
| 7 | include(JoinPaths) | ||
| 8 | |||
| 9 | # Sets build type if not set by now | ||
| 10 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) | ||
| 11 | if(RAYLIB_IS_MAIN) | ||
| 12 | set(default_build_type Debug) | ||
| 13 | else() | ||
| 14 | message(WARNING "Default build type is not set (CMAKE_BUILD_TYPE)") | ||
| 15 | endif() | ||
| 16 | |||
| 17 | message(STATUS "Setting build type to '${default_build_type}' as none was specified.") | ||
| 18 | |||
| 19 | set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) | ||
| 20 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") | ||
| 21 | endif() | ||
| 22 | |||
| 23 | # Used as public API to be included into other projects | ||
| 24 | set(raylib_public_headers | ||
| 25 | raylib.h | ||
| 26 | rlgl.h | ||
| 27 | raymath.h | ||
| 28 | ) | ||
| 29 | |||
| 30 | # Sources to be compiled | ||
| 31 | set(raylib_sources | ||
| 32 | raudio.c | ||
| 33 | rcore.c | ||
| 34 | rmodels.c | ||
| 35 | rshapes.c | ||
| 36 | rtext.c | ||
| 37 | rtextures.c | ||
| 38 | utils.c | ||
| 39 | ) | ||
| 40 | |||
| 41 | # <root>/cmake/GlfwImport.cmake handles the details around the inclusion of glfw | ||
| 42 | if (NOT ${PLATFORM} MATCHES "Web") | ||
| 43 | include(GlfwImport) | ||
| 44 | endif () | ||
| 45 | |||
| 46 | # Sets additional platform options and link libraries for each platform | ||
| 47 | # also selects the proper graphics API and version for that platform | ||
| 48 | # Produces a variable LIBS_PRIVATE that will be used later | ||
| 49 | include(LibraryConfigurations) | ||
| 50 | |||
| 51 | if (SUPPORT_MODULE_RAUDIO) | ||
| 52 | MESSAGE(STATUS "Audio Backend: miniaudio") | ||
| 53 | else () | ||
| 54 | MESSAGE(STATUS "Audio Backend: None (-DCUSTOMIZE_BUILD=ON -DSUPPORT_MODULE_RAUDIO=OFF)") | ||
| 55 | endif () | ||
| 56 | |||
| 57 | add_library(raylib ${raylib_sources} ${raylib_public_headers}) | ||
| 58 | |||
| 59 | if (NOT BUILD_SHARED_LIBS) | ||
| 60 | MESSAGE(STATUS "Building raylib static library") | ||
| 61 | add_library(raylib_static ALIAS raylib) | ||
| 62 | else() | ||
| 63 | MESSAGE(STATUS "Building raylib shared library") | ||
| 64 | target_compile_definitions(raylib | ||
| 65 | PRIVATE $<BUILD_INTERFACE:BUILD_LIBTYPE_SHARED> | ||
| 66 | INTERFACE $<INSTALL_INTERFACE:USE_LIBTYPE_SHARED> | ||
| 67 | ) | ||
| 68 | endif() | ||
| 69 | |||
| 70 | if (${PLATFORM} MATCHES "Web") | ||
| 71 | target_link_options(raylib PUBLIC "-sUSE_GLFW=3") | ||
| 72 | if(${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_ES3") | ||
| 73 | target_link_options(raylib PUBLIC "-sMIN_WEBGL_VERSION=2") | ||
| 74 | target_link_options(raylib PUBLIC "-sMAX_WEBGL_VERSION=2") | ||
| 75 | endif() | ||
| 76 | endif() | ||
| 77 | |||
| 78 | set_target_properties(raylib PROPERTIES | ||
| 79 | PUBLIC_HEADER "${raylib_public_headers}" | ||
| 80 | VERSION ${PROJECT_VERSION} | ||
| 81 | SOVERSION ${API_VERSION} | ||
| 82 | ) | ||
| 83 | |||
| 84 | if (WITH_PIC OR BUILD_SHARED_LIBS) | ||
| 85 | set_property(TARGET raylib PROPERTY POSITION_INDEPENDENT_CODE ON) | ||
| 86 | endif () | ||
| 87 | |||
| 88 | if (BUILD_SHARED_LIBS) | ||
| 89 | # Hide raylib's symbols by default so RLAPI can expose them | ||
| 90 | set_property(TARGET raylib PROPERTY C_VISIBILITY_PRESET hidden) | ||
| 91 | endif () | ||
| 92 | |||
| 93 | target_link_libraries(raylib "${LIBS_PRIVATE}") | ||
| 94 | |||
| 95 | # Sets some compile time definitions for the pre-processor | ||
| 96 | # If CUSTOMIZE_BUILD option is on you will not use config.h by default | ||
| 97 | # and you will be able to select more build options | ||
| 98 | include(CompileDefinitions) | ||
| 99 | |||
| 100 | # Registering include directories | ||
| 101 | target_include_directories(raylib | ||
| 102 | PUBLIC | ||
| 103 | $<INSTALL_INTERFACE:include> | ||
| 104 | $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> | ||
| 105 | PRIVATE | ||
| 106 | ${CMAKE_CURRENT_SOURCE_DIR} | ||
| 107 | ${OPENGL_INCLUDE_DIR} | ||
| 108 | ${OPENAL_INCLUDE_DIR} | ||
| 109 | ) | ||
| 110 | |||
| 111 | # Copy the header files to the build directory for convenience | ||
| 112 | file(COPY ${raylib_public_headers} DESTINATION "include") | ||
| 113 | |||
| 114 | # Includes information on how the library will be installed on the system | ||
| 115 | # when cmake --install is run | ||
| 116 | include(InstallConfigurations) | ||
| 117 | |||
| 118 | # Print the flags for the user | ||
| 119 | if (DEFINED CMAKE_BUILD_TYPE) | ||
| 120 | message(STATUS "Generated build type: ${CMAKE_BUILD_TYPE}") | ||
| 121 | else () | ||
| 122 | message(STATUS "Generated config types: ${CMAKE_CONFIGURATION_TYPES}") | ||
| 123 | endif () | ||
| 124 | |||
| 125 | message(STATUS "Compiling with the flags:") | ||
| 126 | message(STATUS " PLATFORM=" ${PLATFORM_CPP}) | ||
| 127 | message(STATUS " GRAPHICS=" ${GRAPHICS}) | ||
| 128 | |||
| 129 | # Options if you want to create an installer using CPack | ||
| 130 | include(PackConfigurations) | ||
| 131 | |||
| 132 | enable_testing() | ||
diff --git a/raylib/src/LICENSE b/raylib/src/LICENSE new file mode 100644 index 0000000..e96f876 --- /dev/null +++ b/raylib/src/LICENSE | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) | ||
| 2 | |||
| 3 | This software is provided "as-is", without any express or implied warranty. In no event | ||
| 4 | will the authors be held liable for any damages arising from the use of this software. | ||
| 5 | |||
| 6 | Permission is granted to anyone to use this software for any purpose, including commercial | ||
| 7 | applications, and to alter it and redistribute it freely, subject to the following restrictions: | ||
| 8 | |||
| 9 | 1. The origin of this software must not be misrepresented; you must not claim that you | ||
| 10 | wrote the original software. If you use this software in a product, an acknowledgment | ||
| 11 | in the product documentation would be appreciated but is not required. | ||
| 12 | |||
| 13 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented | ||
| 14 | as being the original software. | ||
| 15 | |||
| 16 | 3. This notice may not be removed or altered from any source distribution. | ||
diff --git a/raylib/src/Makefile b/raylib/src/Makefile new file mode 100644 index 0000000..7dde52f --- /dev/null +++ b/raylib/src/Makefile | |||
| @@ -0,0 +1,873 @@ | |||
| 1 | #****************************************************************************** | ||
| 2 | # | ||
| 3 | # raylib makefile | ||
| 4 | # | ||
| 5 | # This file supports building raylib library for the following platforms: | ||
| 6 | # | ||
| 7 | # > PLATFORM_DESKTOP | ||
| 8 | # - Defaults to PLATFORM_DESKTOP_GLFW | ||
| 9 | # > PLATFORM_DESKTOP_GLFW (GLFW backend): | ||
| 10 | # - Windows (Win32, Win64) | ||
| 11 | # - Linux (X11/Wayland desktop mode) | ||
| 12 | # - macOS/OSX (x64, arm64) | ||
| 13 | # - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) | ||
| 14 | # > PLATFORM_DESKTOP_SDL (SDL backend): | ||
| 15 | # - Windows (Win32, Win64) | ||
| 16 | # - Linux (X11/Wayland desktop mode) | ||
| 17 | # - Others (not tested) | ||
| 18 | # > PLATFORM_DESKTOP_RGFW (RGFW backend): | ||
| 19 | # - Windows (Win32, Win64) | ||
| 20 | # - Linux (X11 desktop mode) | ||
| 21 | # - macOS/OSX (x64, arm64 (not tested)) | ||
| 22 | # - Others (not tested) | ||
| 23 | # > PLATFORM_WEB: | ||
| 24 | # - HTML5 (WebAssembly) | ||
| 25 | # > PLATFORM_DRM: | ||
| 26 | # - Raspberry Pi 0-5 (DRM/KMS) | ||
| 27 | # - Linux DRM subsystem (KMS mode) | ||
| 28 | # > PLATFORM_ANDROID: | ||
| 29 | # - Android (ARM, ARM64) | ||
| 30 | # | ||
| 31 | # Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. | ||
| 32 | # Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. | ||
| 33 | # | ||
| 34 | # Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) | ||
| 35 | # | ||
| 36 | # This software is provided "as-is", without any express or implied warranty. In no event | ||
| 37 | # will the authors be held liable for any damages arising from the use of this software. | ||
| 38 | # | ||
| 39 | # Permission is granted to anyone to use this software for any purpose, including commercial | ||
| 40 | # applications, and to alter it and redistribute it freely, subject to the following restrictions: | ||
| 41 | # | ||
| 42 | # 1. The origin of this software must not be misrepresented; you must not claim that you | ||
| 43 | # wrote the original software. If you use this software in a product, an acknowledgment | ||
| 44 | # in the product documentation would be appreciated but is not required. | ||
| 45 | # | ||
| 46 | # 2. Altered source versions must be plainly marked as such, and must not be misrepresented | ||
| 47 | # as being the original software. | ||
| 48 | # | ||
| 49 | # 3. This notice may not be removed or altered from any source distribution. | ||
| 50 | # | ||
| 51 | #************************************************************************************************** | ||
| 52 | |||
| 53 | # NOTE: Highly recommended to read the raylib Wiki to know how to compile raylib for different platforms | ||
| 54 | # https://github.com/raysan5/raylib/wiki | ||
| 55 | |||
| 56 | .PHONY: all clean install uninstall | ||
| 57 | |||
| 58 | # Define required environment variables | ||
| 59 | #------------------------------------------------------------------------------------------------ | ||
| 60 | # Define target platform | ||
| 61 | PLATFORM ?= PLATFORM_DESKTOP | ||
| 62 | |||
| 63 | ifeq ($(PLATFORM), PLATFORM_DESKTOP) | ||
| 64 | TARGET_PLATFORM = PLATFORM_DESKTOP_GLFW | ||
| 65 | else | ||
| 66 | TARGET_PLATFORM = $(PLATFORM) | ||
| 67 | endif | ||
| 68 | |||
| 69 | # Define required raylib variables | ||
| 70 | RAYLIB_VERSION = 5.5.0 | ||
| 71 | RAYLIB_API_VERSION = 550 | ||
| 72 | |||
| 73 | # Define raylib source code path | ||
| 74 | RAYLIB_SRC_PATH ?= ../src | ||
| 75 | |||
| 76 | # Define output directory for compiled library, defaults to src directory | ||
| 77 | # NOTE: If externally provided, make sure directory exists | ||
| 78 | RAYLIB_RELEASE_PATH ?= $(RAYLIB_SRC_PATH) | ||
| 79 | |||
| 80 | # Library type used for raylib: STATIC (.a) or SHARED (.so/.dll) | ||
| 81 | RAYLIB_LIBTYPE ?= STATIC | ||
| 82 | |||
| 83 | # Build mode for library: DEBUG or RELEASE | ||
| 84 | RAYLIB_BUILD_MODE ?= RELEASE | ||
| 85 | |||
| 86 | # Build output name for the library | ||
| 87 | RAYLIB_LIB_NAME ?= raylib | ||
| 88 | |||
| 89 | # Define resource file for DLL properties | ||
| 90 | RAYLIB_RES_FILE ?= ./raylib.dll.rc.data | ||
| 91 | |||
| 92 | # Define external config flags | ||
| 93 | # NOTE: It will override config.h flags with the provided ones, | ||
| 94 | # if NONE, default config.h flags are used | ||
| 95 | RAYLIB_CONFIG_FLAGS ?= NONE | ||
| 96 | |||
| 97 | # To define additional cflags: Use make CUSTOM_CFLAGS="" | ||
| 98 | |||
| 99 | # Include raylib modules on compilation | ||
| 100 | # NOTE: Some programs like tools could not require those modules | ||
| 101 | RAYLIB_MODULE_AUDIO ?= TRUE | ||
| 102 | RAYLIB_MODULE_MODELS ?= TRUE | ||
| 103 | RAYLIB_MODULE_RAYGUI ?= FALSE | ||
| 104 | |||
| 105 | # NOTE: Additional libraries have been moved to their own repos: | ||
| 106 | # raygui: https://github.com/raysan5/raygui | ||
| 107 | RAYLIB_MODULE_RAYGUI_PATH ?= $(RAYLIB_SRC_PATH)/../../raygui/src | ||
| 108 | |||
| 109 | # Use external GLFW library instead of rglfw module | ||
| 110 | USE_EXTERNAL_GLFW ?= FALSE | ||
| 111 | |||
| 112 | # Enable support for X11 by default on Linux when using GLFW | ||
| 113 | # NOTE: Wayland is disabled by default, only enable if you are sure | ||
| 114 | GLFW_LINUX_ENABLE_WAYLAND ?= FALSE | ||
| 115 | GLFW_LINUX_ENABLE_X11 ?= TRUE | ||
| 116 | |||
| 117 | # PLATFORM_DESKTOP_SDL: It requires SDL library to be provided externally | ||
| 118 | # WARNING: Library is not included in raylib, it MUST be configured by users | ||
| 119 | SDL_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2/include | ||
| 120 | SDL_LIBRARY_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2/lib | ||
| 121 | SDL_LIBRARIES ?= -lSDL2 -lSDL2main | ||
| 122 | |||
| 123 | |||
| 124 | # Determine if the file has root access (only required to install raylib) | ||
| 125 | # "whoami" prints the name of the user that calls him (so, if it is the root user, "whoami" prints "root") | ||
| 126 | ROOT = $(shell whoami) | ||
| 127 | |||
| 128 | # By default we suppose we are working on Windows | ||
| 129 | HOST_PLATFORM_OS ?= WINDOWS | ||
| 130 | PLATFORM_OS ?= WINDOWS | ||
| 131 | |||
| 132 | # Determine PLATFORM_OS when required | ||
| 133 | ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW PLATFORM_DESKTOP_SDL PLATFORM_DESKTOP_RGFW PLATFORM_WEB PLATFORM_ANDROID)) | ||
| 134 | # No uname.exe on MinGW!, but OS=Windows_NT on Windows! | ||
| 135 | # ifeq ($(UNAME),Msys) -> Windows | ||
| 136 | ifeq ($(OS),Windows_NT) | ||
| 137 | PLATFORM_OS = WINDOWS | ||
| 138 | ifndef PLATFORM_SHELL | ||
| 139 | PLATFORM_SHELL = cmd | ||
| 140 | endif | ||
| 141 | else | ||
| 142 | UNAMEOS = $(shell uname) | ||
| 143 | ifeq ($(UNAMEOS),Linux) | ||
| 144 | PLATFORM_OS = LINUX | ||
| 145 | endif | ||
| 146 | ifeq ($(UNAMEOS),FreeBSD) | ||
| 147 | PLATFORM_OS = BSD | ||
| 148 | endif | ||
| 149 | ifeq ($(UNAMEOS),OpenBSD) | ||
| 150 | PLATFORM_OS = BSD | ||
| 151 | endif | ||
| 152 | ifeq ($(UNAMEOS),NetBSD) | ||
| 153 | PLATFORM_OS = BSD | ||
| 154 | endif | ||
| 155 | ifeq ($(UNAMEOS),DragonFly) | ||
| 156 | PLATFORM_OS = BSD | ||
| 157 | endif | ||
| 158 | ifeq ($(UNAMEOS),Darwin) | ||
| 159 | PLATFORM_OS = OSX | ||
| 160 | endif | ||
| 161 | ifndef PLATFORM_SHELL | ||
| 162 | PLATFORM_SHELL = sh | ||
| 163 | endif | ||
| 164 | endif | ||
| 165 | endif | ||
| 166 | ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) | ||
| 167 | UNAMEOS = $(shell uname) | ||
| 168 | ifeq ($(UNAMEOS),Linux) | ||
| 169 | PLATFORM_OS = LINUX | ||
| 170 | endif | ||
| 171 | ifndef PLATFORM_SHELL | ||
| 172 | PLATFORM_SHELL = sh | ||
| 173 | endif | ||
| 174 | endif | ||
| 175 | ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) | ||
| 176 | ifeq ($(PLATFORM_OS),LINUX) | ||
| 177 | ifndef PLATFORM_SHELL | ||
| 178 | PLATFORM_SHELL = sh | ||
| 179 | endif | ||
| 180 | endif | ||
| 181 | endif | ||
| 182 | |||
| 183 | ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) | ||
| 184 | ifeq ($(PLATFORM_OS), WINDOWS) | ||
| 185 | # Emscripten required variables | ||
| 186 | EMSDK_PATH ?= C:/raylib/emsdk | ||
| 187 | EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten | ||
| 188 | CLANG_PATH := $(EMSDK_PATH)/upstream/bin | ||
| 189 | PYTHON_PATH := $(EMSDK_PATH)/python/3.9.2-nuget_64bit | ||
| 190 | NODE_PATH := $(EMSDK_PATH)/node/20.18.0_64bit/bin | ||
| 191 | export PATH := $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:/raylib/MinGW/bin;$(PATH) | ||
| 192 | endif | ||
| 193 | endif | ||
| 194 | |||
| 195 | ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) | ||
| 196 | # Android architecture | ||
| 197 | # Starting at 2019 using arm64 is mandatory for published apps, | ||
| 198 | # Starting on August 2020, minimum required target API is Android 10 (API level 29) | ||
| 199 | ANDROID_ARCH ?= arm64 | ||
| 200 | ANDROID_API_VERSION ?= 29 | ||
| 201 | |||
| 202 | # Android required path variables | ||
| 203 | # NOTE: Starting with Android NDK r21, no more toolchain generation is required, NDK is the toolchain on itself | ||
| 204 | ifeq ($(OS),Windows_NT) | ||
| 205 | ANDROID_NDK ?= C:/android-ndk | ||
| 206 | ANDROID_TOOLCHAIN = $(ANDROID_NDK)/toolchains/llvm/prebuilt/windows-x86_64 | ||
| 207 | else | ||
| 208 | ANDROID_NDK ?= /usr/lib/android/ndk | ||
| 209 | ifeq ($(PLATFORM_OS), OSX) | ||
| 210 | ANDROID_TOOLCHAIN = $(ANDROID_NDK)/toolchains/llvm/prebuilt/darwin-x86_64 | ||
| 211 | else | ||
| 212 | ANDROID_TOOLCHAIN = $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64 | ||
| 213 | endif | ||
| 214 | endif | ||
| 215 | |||
| 216 | # NOTE: Sysroot can also be reference from $(ANDROID_NDK)/sysroot | ||
| 217 | ANDROID_SYSROOT ?= $(ANDROID_TOOLCHAIN)/sysroot | ||
| 218 | |||
| 219 | ifeq ($(ANDROID_ARCH),arm) | ||
| 220 | ANDROID_COMPILER_ARCH = armv7a | ||
| 221 | endif | ||
| 222 | ifeq ($(ANDROID_ARCH),arm64) | ||
| 223 | ANDROID_COMPILER_ARCH = aarch64 | ||
| 224 | endif | ||
| 225 | ifeq ($(ANDROID_ARCH),x86) | ||
| 226 | ANDROID_COMPILER_ARCH = i686 | ||
| 227 | endif | ||
| 228 | ifeq ($(ANDROID_ARCH),x86_64) | ||
| 229 | ANDROID_COMPILER_ARCH = x86_64 | ||
| 230 | endif | ||
| 231 | |||
| 232 | endif | ||
| 233 | |||
| 234 | # Define raylib graphics api depending on selected platform | ||
| 235 | # NOTE: By default use OpenGL 3.3 on desktop platforms | ||
| 236 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) | ||
| 237 | GRAPHICS ?= GRAPHICS_API_OPENGL_33 | ||
| 238 | #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 | ||
| 239 | #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 | ||
| 240 | #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 | ||
| 241 | #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) | ||
| 242 | endif | ||
| 243 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) | ||
| 244 | GRAPHICS ?= GRAPHICS_API_OPENGL_33 | ||
| 245 | endif | ||
| 246 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) | ||
| 247 | GRAPHICS ?= GRAPHICS_API_OPENGL_33 | ||
| 248 | #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 | ||
| 249 | #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 | ||
| 250 | #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 | ||
| 251 | #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) | ||
| 252 | endif | ||
| 253 | ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) | ||
| 254 | # On DRM OpenGL ES 2.0 must be used | ||
| 255 | GRAPHICS = GRAPHICS_API_OPENGL_ES2 | ||
| 256 | endif | ||
| 257 | ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) | ||
| 258 | # On HTML5 OpenGL ES 2.0 is used, emscripten translates it to WebGL 1.0 | ||
| 259 | GRAPHICS = GRAPHICS_API_OPENGL_ES2 | ||
| 260 | #GRAPHICS = GRAPHICS_API_OPENGL_ES3 | ||
| 261 | endif | ||
| 262 | ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) | ||
| 263 | # By default use OpenGL ES 2.0 on Android | ||
| 264 | GRAPHICS = GRAPHICS_API_OPENGL_ES2 | ||
| 265 | endif | ||
| 266 | |||
| 267 | # Define default C compiler and archiver to pack library: CC, AR | ||
| 268 | #------------------------------------------------------------------------------------------------ | ||
| 269 | CC = gcc | ||
| 270 | AR = ar | ||
| 271 | |||
| 272 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) | ||
| 273 | ifeq ($(PLATFORM_OS),OSX) | ||
| 274 | # OSX default compiler | ||
| 275 | CC = clang | ||
| 276 | GLFW_OSX = -x objective-c | ||
| 277 | endif | ||
| 278 | ifeq ($(PLATFORM_OS),BSD) | ||
| 279 | # FreeBSD, OpenBSD, NetBSD, DragonFly default compiler | ||
| 280 | CC = clang | ||
| 281 | endif | ||
| 282 | endif | ||
| 283 | ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) | ||
| 284 | ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) | ||
| 285 | # Define RPI cross-compiler | ||
| 286 | #CC = armv6j-hardfloat-linux-gnueabi-gcc | ||
| 287 | CC = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-gcc | ||
| 288 | AR = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-ar | ||
| 289 | endif | ||
| 290 | endif | ||
| 291 | ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) | ||
| 292 | # HTML5 emscripten compiler | ||
| 293 | CC = emcc | ||
| 294 | AR = emar | ||
| 295 | endif | ||
| 296 | ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) | ||
| 297 | # Android toolchain (must be provided for desired architecture and compiler) | ||
| 298 | ifeq ($(ANDROID_ARCH),arm) | ||
| 299 | CC = $(ANDROID_TOOLCHAIN)/bin/$(ANDROID_COMPILER_ARCH)-linux-androideabi$(ANDROID_API_VERSION)-clang | ||
| 300 | endif | ||
| 301 | ifeq ($(ANDROID_ARCH),arm64) | ||
| 302 | CC = $(ANDROID_TOOLCHAIN)/bin/$(ANDROID_COMPILER_ARCH)-linux-android$(ANDROID_API_VERSION)-clang | ||
| 303 | endif | ||
| 304 | ifeq ($(ANDROID_ARCH),x86) | ||
| 305 | CC = $(ANDROID_TOOLCHAIN)/bin/$(ANDROID_COMPILER_ARCH)-linux-android$(ANDROID_API_VERSION)-clang | ||
| 306 | endif | ||
| 307 | ifeq ($(ANDROID_ARCH),x86_64) | ||
| 308 | CC = $(ANDROID_TOOLCHAIN)/bin/$(ANDROID_COMPILER_ARCH)-linux-android$(ANDROID_API_VERSION)-clang | ||
| 309 | endif | ||
| 310 | # It seems from Android NDK r22 onwards we need to use llvm-ar | ||
| 311 | AR = $(ANDROID_TOOLCHAIN)/bin/llvm-ar | ||
| 312 | endif | ||
| 313 | |||
| 314 | # Define compiler flags: CFLAGS | ||
| 315 | #------------------------------------------------------------------------------------------------ | ||
| 316 | # -O1 defines optimization level | ||
| 317 | # -g include debug information on compilation | ||
| 318 | # -s strip unnecessary data from build --> linker | ||
| 319 | # -Wall turns on most, but not all, compiler warnings | ||
| 320 | # -std=c99 defines C language mode (standard C from 1999 revision) | ||
| 321 | # -std=gnu99 defines C language mode (GNU C from 1999 revision) | ||
| 322 | # -Wno-missing-braces ignore invalid warning (GCC bug 53119) | ||
| 323 | # -Wno-unused-value ignore unused return values of some functions (i.e. fread()) | ||
| 324 | # -D_DEFAULT_SOURCE use with -std=c99 on Linux and PLATFORM_WEB, required for timespec | ||
| 325 | # -D_GNU_SOURCE access to lots of nonstandard GNU/Linux extension functions | ||
| 326 | # -Werror=pointer-arith catch unportable code that does direct arithmetic on void pointers | ||
| 327 | # -fno-strict-aliasing jar_xm.h does shady stuff (breaks strict aliasing) | ||
| 328 | CFLAGS = -Wall -D_GNU_SOURCE -D$(TARGET_PLATFORM) -D$(GRAPHICS) -Wno-missing-braces -Werror=pointer-arith -fno-strict-aliasing | ||
| 329 | |||
| 330 | ifneq ($(RAYLIB_CONFIG_FLAGS), NONE) | ||
| 331 | CFLAGS += -DEXTERNAL_CONFIG_FLAGS $(RAYLIB_CONFIG_FLAGS) | ||
| 332 | endif | ||
| 333 | |||
| 334 | ifeq ($(TARGET_PLATFORM), PLATFORM_WEB) | ||
| 335 | # NOTE: When using multi-threading in the user code, it requires -pthread enabled | ||
| 336 | CFLAGS += -std=gnu99 | ||
| 337 | else | ||
| 338 | CFLAGS += -std=c99 | ||
| 339 | endif | ||
| 340 | |||
| 341 | ifeq ($(PLATFORM_OS), LINUX) | ||
| 342 | CFLAGS += -fPIC | ||
| 343 | endif | ||
| 344 | |||
| 345 | ifeq ($(RAYLIB_BUILD_MODE),DEBUG) | ||
| 346 | CFLAGS += -g -D_DEBUG | ||
| 347 | endif | ||
| 348 | |||
| 349 | ifeq ($(RAYLIB_BUILD_MODE),RELEASE) | ||
| 350 | ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) | ||
| 351 | CFLAGS += -Os | ||
| 352 | endif | ||
| 353 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) | ||
| 354 | CFLAGS += -O1 | ||
| 355 | endif | ||
| 356 | ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) | ||
| 357 | CFLAGS += -O2 | ||
| 358 | endif | ||
| 359 | endif | ||
| 360 | |||
| 361 | # Additional flags for compiler (if desired) | ||
| 362 | # -Wextra enables some extra warning flags that are not enabled by -Wall | ||
| 363 | # -Wmissing-prototypes warn if a global function is defined without a previous prototype declaration | ||
| 364 | # -Wstrict-prototypes warn if a function is declared or defined without specifying the argument types | ||
| 365 | # -Werror=implicit-function-declaration catch function calls without prior declaration | ||
| 366 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) | ||
| 367 | CFLAGS += -Werror=implicit-function-declaration | ||
| 368 | endif | ||
| 369 | ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) | ||
| 370 | # -Os # size optimization | ||
| 371 | # -O2 # optimization level 2, if used, also set --memory-init-file 0 | ||
| 372 | # -sUSE_GLFW=3 # Use glfw3 library (context/input management) -> Only for linker! | ||
| 373 | # -sALLOW_MEMORY_GROWTH=1 # to allow memory resizing -> WARNING: Audio buffers could FAIL! | ||
| 374 | # -sTOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) | ||
| 375 | # -sUSE_PTHREADS=1 # multithreading support | ||
| 376 | # -sFORCE_FILESYSTEM=1 # force filesystem to load/save files data | ||
| 377 | # -sASSERTIONS=1 # enable runtime checks for common memory allocation errors (-O1 and above turn it off) | ||
| 378 | # -sGL_ENABLE_GET_PROC_ADDRESS # enable using the *glGetProcAddress() family of functions, required for extensions loading | ||
| 379 | # --profiling # include information for code profiling | ||
| 380 | # --memory-init-file 0 # to avoid an external memory initialization code file (.mem) | ||
| 381 | # --preload-file resources # specify a resources folder for data compilation | ||
| 382 | ifeq ($(RAYLIB_BUILD_MODE),DEBUG) | ||
| 383 | CFLAGS += -sASSERTIONS=1 --profiling | ||
| 384 | endif | ||
| 385 | #CFLAGS += -sGL_ENABLE_GET_PROC_ADDRESS | ||
| 386 | endif | ||
| 387 | ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) | ||
| 388 | # Compiler flags for arquitecture | ||
| 389 | ifeq ($(ANDROID_ARCH),arm) | ||
| 390 | CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 | ||
| 391 | endif | ||
| 392 | ifeq ($(ANDROID_ARCH),arm64) | ||
| 393 | CFLAGS += -target aarch64 -mfix-cortex-a53-835769 | ||
| 394 | endif | ||
| 395 | ifeq ($(ANDROID_ARCH),x86) | ||
| 396 | CFLAGS += -march=i686 | ||
| 397 | endif | ||
| 398 | ifeq ($(ANDROID_ARCH),x86_64) | ||
| 399 | CFLAGS += -march=x86-64 | ||
| 400 | endif | ||
| 401 | # Compilation functions attributes options | ||
| 402 | CFLAGS += -ffunction-sections -funwind-tables -fstack-protector-strong -fPIE -fPIC | ||
| 403 | # Compiler options for the linker | ||
| 404 | # -Werror=format-security | ||
| 405 | CFLAGS += -Wa,--noexecstack -Wformat -no-canonical-prefixes | ||
| 406 | # Preprocessor macro definitions | ||
| 407 | CFLAGS += -D__ANDROID__ -DPLATFORM_ANDROID -D__ANDROID_API__=$(ANDROID_API_VERSION) | ||
| 408 | endif | ||
| 409 | |||
| 410 | # Define required compilation flags for raylib SHARED lib | ||
| 411 | ifeq ($(RAYLIB_LIBTYPE),SHARED) | ||
| 412 | # make sure code is compiled as position independent | ||
| 413 | # BE CAREFUL: It seems that for gcc -fpic is not the same as -fPIC | ||
| 414 | # MinGW32 just doesn't need -fPIC, it shows warnings | ||
| 415 | CFLAGS += -fPIC -DBUILD_LIBTYPE_SHARED | ||
| 416 | |||
| 417 | # hide all symbols by default, so RLAPI can expose them | ||
| 418 | ifeq ($(PLATFORM_OS),$(filter $(PLATFORM_OS), LINUX BSD OSX)) | ||
| 419 | CFLAGS += -fvisibility=hidden | ||
| 420 | endif | ||
| 421 | endif | ||
| 422 | |||
| 423 | ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) | ||
| 424 | # without EGL_NO_X11 eglplatform.h tears Xlib.h in which tears X.h in | ||
| 425 | # which contains a conflicting type Font | ||
| 426 | CFLAGS += -DEGL_NO_X11 | ||
| 427 | CFLAGS += -Werror=implicit-function-declaration | ||
| 428 | endif | ||
| 429 | # Use Wayland display on Linux desktop | ||
| 430 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) | ||
| 431 | ifeq ($(PLATFORM_OS), LINUX) | ||
| 432 | ifeq ($(GLFW_LINUX_ENABLE_X11),TRUE) | ||
| 433 | CFLAGS += -D_GLFW_X11 | ||
| 434 | endif | ||
| 435 | ifeq ($(GLFW_LINUX_ENABLE_WAYLAND),TRUE) | ||
| 436 | CFLAGS += -D_GLFW_WAYLAND | ||
| 437 | LDFLAGS += $(shell pkg-config wayland-client wayland-cursor wayland-egl xkbcommon --libs) | ||
| 438 | |||
| 439 | WL_PROTOCOLS_DIR := external/glfw/deps/wayland | ||
| 440 | |||
| 441 | wl_generate = \ | ||
| 442 | $(eval protocol=$(1)) \ | ||
| 443 | $(eval basename=$(2)) \ | ||
| 444 | $(shell wayland-scanner client-header $(protocol) $(RAYLIB_SRC_PATH)/$(basename).h) \ | ||
| 445 | $(shell wayland-scanner private-code $(protocol) $(RAYLIB_SRC_PATH)/$(basename)-code.h) | ||
| 446 | |||
| 447 | $(call wl_generate, $(WL_PROTOCOLS_DIR)/wayland.xml, wayland-client-protocol) | ||
| 448 | $(call wl_generate, $(WL_PROTOCOLS_DIR)/xdg-shell.xml, xdg-shell-client-protocol) | ||
| 449 | $(call wl_generate, $(WL_PROTOCOLS_DIR)/xdg-decoration-unstable-v1.xml, xdg-decoration-unstable-v1-client-protocol) | ||
| 450 | $(call wl_generate, $(WL_PROTOCOLS_DIR)/viewporter.xml, viewporter-client-protocol) | ||
| 451 | $(call wl_generate, $(WL_PROTOCOLS_DIR)/relative-pointer-unstable-v1.xml, relative-pointer-unstable-v1-client-protocol) | ||
| 452 | $(call wl_generate, $(WL_PROTOCOLS_DIR)/pointer-constraints-unstable-v1.xml, pointer-constraints-unstable-v1-client-protocol) | ||
| 453 | $(call wl_generate, $(WL_PROTOCOLS_DIR)/fractional-scale-v1.xml, fractional-scale-v1-client-protocol) | ||
| 454 | $(call wl_generate, $(WL_PROTOCOLS_DIR)/xdg-activation-v1.xml, xdg-activation-v1-client-protocol) | ||
| 455 | $(call wl_generate, $(WL_PROTOCOLS_DIR)/idle-inhibit-unstable-v1.xml, idle-inhibit-unstable-v1-client-protocol) | ||
| 456 | endif | ||
| 457 | endif | ||
| 458 | endif | ||
| 459 | |||
| 460 | CFLAGS += $(CUSTOM_CFLAGS) | ||
| 461 | |||
| 462 | # Define include paths for required headers: INCLUDE_PATHS | ||
| 463 | # NOTE: Several external required libraries (stb and others) | ||
| 464 | #------------------------------------------------------------------------------------------------ | ||
| 465 | INCLUDE_PATHS = -I. $(EXTRA_INCLUDE_PATHS) | ||
| 466 | |||
| 467 | # Define additional directories containing required header files | ||
| 468 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) | ||
| 469 | INCLUDE_PATHS += -Iexternal/glfw/include | ||
| 470 | ifeq ($(PLATFORM_OS),BSD) | ||
| 471 | INCLUDE_PATHS += -I/usr/local/include -I/usr/pkg/include -I/usr/X11R7/include | ||
| 472 | endif | ||
| 473 | endif | ||
| 474 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) | ||
| 475 | INCLUDE_PATHS += -I$(SDL_INCLUDE_PATH) | ||
| 476 | endif | ||
| 477 | ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) | ||
| 478 | INCLUDE_PATHS += -Iexternal/glfw/include | ||
| 479 | endif | ||
| 480 | ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) | ||
| 481 | INCLUDE_PATHS += -I/usr/include/libdrm | ||
| 482 | ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) | ||
| 483 | INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/usr/include | ||
| 484 | INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include | ||
| 485 | endif | ||
| 486 | endif | ||
| 487 | ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) | ||
| 488 | NATIVE_APP_GLUE = $(ANDROID_NDK)/sources/android/native_app_glue | ||
| 489 | # Include android_native_app_glue.h | ||
| 490 | INCLUDE_PATHS += -I$(NATIVE_APP_GLUE) | ||
| 491 | |||
| 492 | # Android required libraries | ||
| 493 | INCLUDE_PATHS += -I$(ANDROID_SYSROOT)/usr/include | ||
| 494 | ifeq ($(ANDROID_ARCH),arm) | ||
| 495 | INCLUDE_PATHS += -I$(ANDROID_SYSROOT)/usr/include/arm-linux-androideabi | ||
| 496 | endif | ||
| 497 | ifeq ($(ANDROID_ARCH),arm64) | ||
| 498 | INCLUDE_PATHS += -I$(ANDROID_SYSROOT)/usr/include/aarch64-linux-android | ||
| 499 | endif | ||
| 500 | ifeq ($(ANDROID_ARCH),x86) | ||
| 501 | INCLUDE_PATHS += -I$(ANDROID_SYSROOT)/usr/include/i686-linux-android | ||
| 502 | endif | ||
| 503 | ifeq ($(ANDROID_ARCH),x86_64) | ||
| 504 | INCLUDE_PATHS += -I$(ANDROID_SYSROOT)/usr/include/x86_64-linux-android | ||
| 505 | endif | ||
| 506 | endif | ||
| 507 | |||
| 508 | # Define library paths containing required libs: LDFLAGS | ||
| 509 | # NOTE: This is only required for dynamic library generation | ||
| 510 | #------------------------------------------------------------------------------------------------ | ||
| 511 | LDFLAGS = $(CUSTOM_LDFLAGS) -L. -L$(RAYLIB_RELEASE_PATH) | ||
| 512 | |||
| 513 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) | ||
| 514 | ifeq ($(PLATFORM_OS),WINDOWS) | ||
| 515 | ifneq ($(CC), tcc) | ||
| 516 | LDFLAGS += -Wl,--out-implib,$(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME)dll.a | ||
| 517 | endif | ||
| 518 | endif | ||
| 519 | ifeq ($(PLATFORM_OS),OSX) | ||
| 520 | LDFLAGS += -compatibility_version $(RAYLIB_API_VERSION) -current_version $(RAYLIB_VERSION) | ||
| 521 | endif | ||
| 522 | ifeq ($(PLATFORM_OS),LINUX) | ||
| 523 | LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) | ||
| 524 | endif | ||
| 525 | ifeq ($(PLATFORM_OS),BSD) | ||
| 526 | LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so -Lsrc -L/usr/local/lib -L/usr/pkg/lib -Wl,-R/usr/pkg/lib | ||
| 527 | endif | ||
| 528 | endif | ||
| 529 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) | ||
| 530 | LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) | ||
| 531 | LDFLAGS += -L$(SDL_LIBRARY_PATH) | ||
| 532 | endif | ||
| 533 | ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) | ||
| 534 | LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) | ||
| 535 | ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) | ||
| 536 | LDFLAGS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -L$(RPI_TOOLCHAIN_SYSROOT)/usr/lib | ||
| 537 | endif | ||
| 538 | endif | ||
| 539 | ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) | ||
| 540 | LDFLAGS += -Wl,-soname,libraylib.$(RAYLIB_API_VERSION).so -Wl,--exclude-libs,libatomic.a | ||
| 541 | LDFLAGS += -Wl,--build-id -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings | ||
| 542 | # Force linking of library module to define symbol | ||
| 543 | LDFLAGS += -u ANativeActivity_onCreate | ||
| 544 | # Library paths containing required libs | ||
| 545 | LDFLAGS += -Lsrc | ||
| 546 | # Avoid unresolved symbol pointing to external main() | ||
| 547 | LDFLAGS += -Wl,-undefined,dynamic_lookup | ||
| 548 | endif | ||
| 549 | |||
| 550 | # Define libraries required on linking: LDLIBS | ||
| 551 | # NOTE: This is only required for dynamic library generation | ||
| 552 | #------------------------------------------------------------------------------------------------ | ||
| 553 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) | ||
| 554 | ifeq ($(PLATFORM_OS),WINDOWS) | ||
| 555 | ifeq ($(CC), tcc) | ||
| 556 | LDLIBS = -lopengl32 -lgdi32 -lwinmm -lshell32 | ||
| 557 | else | ||
| 558 | LDLIBS = -static-libgcc -lopengl32 -lgdi32 -lwinmm | ||
| 559 | endif | ||
| 560 | endif | ||
| 561 | ifeq ($(PLATFORM_OS),LINUX) | ||
| 562 | LDLIBS = -lGL -lc -lm -lpthread -ldl -lrt | ||
| 563 | ifeq ($(GLFW_LINUX_ENABLE_X11),TRUE) | ||
| 564 | LDLIBS += -lX11 | ||
| 565 | endif | ||
| 566 | # TODO: On ARM 32bit arch, miniaudio requires atomics library | ||
| 567 | #LDLIBS += -latomic | ||
| 568 | endif | ||
| 569 | ifeq ($(PLATFORM_OS),OSX) | ||
| 570 | LDLIBS = -framework OpenGL -framework Cocoa -framework IOKit -framework CoreAudio -framework CoreVideo | ||
| 571 | endif | ||
| 572 | ifeq ($(PLATFORM_OS),BSD) | ||
| 573 | LDLIBS = -lGL -lpthread | ||
| 574 | endif | ||
| 575 | ifeq ($(USE_EXTERNAL_GLFW),TRUE) | ||
| 576 | # Check the version name. If GLFW3 was built manually, it may have produced | ||
| 577 | # a static library known as libglfw3.a. In that case, the name should be -lglfw3 | ||
| 578 | LDLIBS = -lglfw | ||
| 579 | endif | ||
| 580 | endif | ||
| 581 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) | ||
| 582 | ifeq ($(PLATFORM_OS),WINDOWS) | ||
| 583 | LDLIBS = -static-libgcc -lopengl32 -lgdi32 | ||
| 584 | endif | ||
| 585 | ifeq ($(PLATFORM_OS),LINUX) | ||
| 586 | LDLIBS = -lGL -lc -lm -lpthread -ldl -lrt | ||
| 587 | ifeq ($(USE_WAYLAND_DISPLAY),FALSE) | ||
| 588 | LDLIBS += -lX11 | ||
| 589 | endif | ||
| 590 | endif | ||
| 591 | LDLIBS += $(SDL_LIBRARIES) | ||
| 592 | endif | ||
| 593 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) | ||
| 594 | ifeq ($(PLATFORM_OS),WINDOWS) | ||
| 595 | # Libraries for Windows desktop compilation | ||
| 596 | LDLIBS = -lgdi32 -lwinmm -lopengl32 | ||
| 597 | endif | ||
| 598 | ifeq ($(PLATFORM_OS),LINUX) | ||
| 599 | # Libraries for Debian GNU/Linux desktop compipling | ||
| 600 | # NOTE: Required packages: libegl1-mesa-dev | ||
| 601 | LDLIBS = -lGL -lX11 -lXrandr -lXinerama -lXi -lXcursor -lm -lpthread -ldl -lrt | ||
| 602 | |||
| 603 | # Explicit link to libc | ||
| 604 | ifeq ($(RAYLIB_LIBTYPE),SHARED) | ||
| 605 | LDLIBS += -lc | ||
| 606 | endif | ||
| 607 | |||
| 608 | # NOTE: On ARM 32bit arch, miniaudio requires atomics library | ||
| 609 | LDLIBS += -latomic | ||
| 610 | endif | ||
| 611 | ifeq ($(PLATFORM_OS),OSX) | ||
| 612 | # Libraries for Debian MacOS desktop compiling | ||
| 613 | # NOTE: Required packages: libegl1-mesa-dev | ||
| 614 | LDLIBS += -lm -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo | ||
| 615 | endif | ||
| 616 | endif | ||
| 617 | ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) | ||
| 618 | LDLIBS = -lGLESv2 -lEGL -ldrm -lgbm -lpthread -lrt -lm -ldl | ||
| 619 | ifeq ($(RAYLIB_MODULE_AUDIO),TRUE) | ||
| 620 | LDLIBS += -latomic | ||
| 621 | endif | ||
| 622 | endif | ||
| 623 | ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) | ||
| 624 | LDLIBS = -llog -landroid -lEGL -lGLESv2 -lOpenSLES -lc -lm | ||
| 625 | endif | ||
| 626 | |||
| 627 | # Define source code object files required | ||
| 628 | #------------------------------------------------------------------------------------------------ | ||
| 629 | OBJS = rcore.o \ | ||
| 630 | rshapes.o \ | ||
| 631 | rtextures.o \ | ||
| 632 | rtext.o \ | ||
| 633 | utils.o | ||
| 634 | |||
| 635 | ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) | ||
| 636 | ifeq ($(USE_EXTERNAL_GLFW),FALSE) | ||
| 637 | OBJS += rglfw.o | ||
| 638 | endif | ||
| 639 | endif | ||
| 640 | ifeq ($(RAYLIB_MODULE_MODELS),TRUE) | ||
| 641 | OBJS += rmodels.o | ||
| 642 | endif | ||
| 643 | ifeq ($(RAYLIB_MODULE_AUDIO),TRUE) | ||
| 644 | OBJS += raudio.o | ||
| 645 | endif | ||
| 646 | ifeq ($(RAYLIB_MODULE_RAYGUI),TRUE) | ||
| 647 | OBJS += raygui.o | ||
| 648 | endif | ||
| 649 | |||
| 650 | ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) | ||
| 651 | OBJS += android_native_app_glue.o | ||
| 652 | endif | ||
| 653 | |||
| 654 | # Define processes to execute | ||
| 655 | #------------------------------------------------------------------------------------------------ | ||
| 656 | # Default target entry | ||
| 657 | all: raylib | ||
| 658 | |||
| 659 | # Compile raylib library | ||
| 660 | # NOTE: Release directory is created if not exist | ||
| 661 | raylib: $(OBJS) | ||
| 662 | ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) | ||
| 663 | # Compile raylib libray for web | ||
| 664 | #$(CC) $(OBJS) -r -o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc | ||
| 665 | $(AR) rcs $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(OBJS) | ||
| 666 | @echo "raylib library generated (lib$(RAYLIB_LIB_NAME).a)!" | ||
| 667 | else | ||
| 668 | ifeq ($(RAYLIB_LIBTYPE),SHARED) | ||
| 669 | ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW PLATFORM_DESKTOP_SDL PLATFORM_DESKTOP_RGFW)) | ||
| 670 | ifeq ($(PLATFORM_OS),WINDOWS) | ||
| 671 | # NOTE: Linking with provided resource file | ||
| 672 | $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/$(RAYLIB_LIB_NAME).dll $(OBJS) $(RAYLIB_RES_FILE) $(LDFLAGS) $(LDLIBS) | ||
| 673 | @echo "raylib dynamic library ($(RAYLIB_LIB_NAME).dll) and import library (lib$(RAYLIB_LIB_NAME)dll.a) generated!" | ||
| 674 | endif | ||
| 675 | ifeq ($(PLATFORM_OS),LINUX) | ||
| 676 | # Compile raylib shared library version $(RAYLIB_VERSION). | ||
| 677 | # WARNING: you should type "make clean" before doing this target | ||
| 678 | $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION) $(OBJS) $(LDFLAGS) $(LDLIBS) | ||
| 679 | @echo "raylib shared library generated (lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION)) in $(RAYLIB_RELEASE_PATH)!" | ||
| 680 | cd $(RAYLIB_RELEASE_PATH) && ln -fsv lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION) lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) | ||
| 681 | cd $(RAYLIB_RELEASE_PATH) && ln -fsv lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) lib$(RAYLIB_LIB_NAME).so | ||
| 682 | endif | ||
| 683 | ifeq ($(PLATFORM_OS),OSX) | ||
| 684 | $(CC) -dynamiclib -o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).dylib $(OBJS) $(LDFLAGS) $(LDLIBS) | ||
| 685 | install_name_tool -id "@rpath/lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).dylib" $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).dylib | ||
| 686 | @echo "raylib shared library generated (lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).dylib)!" | ||
| 687 | cd $(RAYLIB_RELEASE_PATH) && ln -fs lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).dylib lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).dylib | ||
| 688 | cd $(RAYLIB_RELEASE_PATH) && ln -fs lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).dylib lib$(RAYLIB_LIB_NAME).dylib | ||
| 689 | endif | ||
| 690 | ifeq ($(PLATFORM_OS),BSD) | ||
| 691 | # WARNING: you should type "gmake clean" before doing this target | ||
| 692 | $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so $(OBJS) $(LDFLAGS) $(LDLIBS) | ||
| 693 | @echo "raylib shared library generated (lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so)!" | ||
| 694 | cd $(RAYLIB_RELEASE_PATH) && ln -fs lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so | ||
| 695 | cd $(RAYLIB_RELEASE_PATH) && ln -fs lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so lib$(RAYLIB_LIB_NAME).so | ||
| 696 | endif | ||
| 697 | endif | ||
| 698 | ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) | ||
| 699 | # Compile raylib shared library version $(RAYLIB_VERSION). | ||
| 700 | # WARNING: you should type "make clean" before doing this target | ||
| 701 | $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION) $(OBJS) $(LDFLAGS) $(LDLIBS) | ||
| 702 | @echo "raylib shared library generated (lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION)) in $(RAYLIB_RELEASE_PATH)!" | ||
| 703 | cd $(RAYLIB_RELEASE_PATH) && ln -fsv lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION) lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) | ||
| 704 | cd $(RAYLIB_RELEASE_PATH) && ln -fsv lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) lib$(RAYLIB_LIB_NAME).so | ||
| 705 | endif | ||
| 706 | ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) | ||
| 707 | $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so $(OBJS) $(LDFLAGS) $(LDLIBS) | ||
| 708 | @echo "raylib shared library generated (lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so)!" | ||
| 709 | # WARNING: symbolic links creation on Windows should be done using mklink command, no ln available | ||
| 710 | ifeq ($(HOST_PLATFORM_OS),LINUX) | ||
| 711 | cd $(RAYLIB_RELEASE_PATH) && ln -fs lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so | ||
| 712 | cd $(RAYLIB_RELEASE_PATH) && ln -fs lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so lib$(RAYLIB_LIB_NAME).so | ||
| 713 | endif | ||
| 714 | endif | ||
| 715 | else | ||
| 716 | # Compile raylib static library version $(RAYLIB_VERSION) | ||
| 717 | # WARNING: You should type "make clean" before doing this target. | ||
| 718 | $(AR) rcs $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(OBJS) | ||
| 719 | @echo "raylib static library generated (lib$(RAYLIB_LIB_NAME).a) in $(RAYLIB_RELEASE_PATH)!" | ||
| 720 | endif | ||
| 721 | endif | ||
| 722 | |||
| 723 | # Compile all modules with their prerequisites | ||
| 724 | |||
| 725 | # Prerequisites of core module | ||
| 726 | rcore.o : platforms/*.c | ||
| 727 | |||
| 728 | # Compile core module | ||
| 729 | rcore.o : rcore.c raylib.h rlgl.h utils.h raymath.h rcamera.h rgestures.h | ||
| 730 | $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) | ||
| 731 | |||
| 732 | # Compile rglfw module | ||
| 733 | rglfw.o : rglfw.c | ||
| 734 | $(CC) $(GLFW_OSX) -c $< $(CFLAGS) $(INCLUDE_PATHS) | ||
| 735 | |||
| 736 | # Compile shapes module | ||
| 737 | rshapes.o : rshapes.c raylib.h rlgl.h | ||
| 738 | $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) | ||
| 739 | |||
| 740 | # Compile textures module | ||
| 741 | rtextures.o : rtextures.c raylib.h rlgl.h utils.h | ||
| 742 | $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) | ||
| 743 | |||
| 744 | # Compile text module | ||
| 745 | rtext.o : rtext.c raylib.h utils.h | ||
| 746 | $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) | ||
| 747 | |||
| 748 | # Compile utils module | ||
| 749 | utils.o : utils.c utils.h | ||
| 750 | $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) | ||
| 751 | |||
| 752 | # Compile models module | ||
| 753 | rmodels.o : rmodels.c raylib.h rlgl.h raymath.h | ||
| 754 | $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) | ||
| 755 | |||
| 756 | # Compile audio module | ||
| 757 | raudio.o : raudio.c raylib.h | ||
| 758 | $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) | ||
| 759 | |||
| 760 | # Compile raygui module | ||
| 761 | # NOTE: raygui header should be distributed with raylib.h | ||
| 762 | raygui.o : raygui.c | ||
| 763 | $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) | ||
| 764 | raygui.c: | ||
| 765 | ifeq ($(PLATFORM_SHELL), cmd) | ||
| 766 | @echo #define RAYGUI_IMPLEMENTATION > raygui.c | ||
| 767 | @echo #include "$(RAYLIB_MODULE_RAYGUI_PATH)/raygui.h" >> raygui.c | ||
| 768 | else | ||
| 769 | @echo "#define RAYGUI_IMPLEMENTATION" > raygui.c | ||
| 770 | @echo "#include \"$(RAYLIB_MODULE_RAYGUI_PATH)/raygui.h\"" >> raygui.c | ||
| 771 | endif | ||
| 772 | |||
| 773 | # Compile android_native_app_glue module | ||
| 774 | android_native_app_glue.o : $(NATIVE_APP_GLUE)/android_native_app_glue.c | ||
| 775 | $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) | ||
| 776 | |||
| 777 | # Install generated and needed files to desired directories. | ||
| 778 | # On GNU/Linux and BSDs, there are some standard directories that contain extra | ||
| 779 | # libraries and header files. These directories (often /usr/local/lib and | ||
| 780 | # /usr/local/include) are for libraries that are installed manually | ||
| 781 | # (without a package manager). We'll use /usr/local/lib/raysan5 and /usr/local/include/raysan5 | ||
| 782 | # for our -L and -I specification to simplify management of the raylib source package. | ||
| 783 | # Customize these locations if you like but don't forget to pass them to make | ||
| 784 | # for compilation and enable runtime linking with -rpath, LD_LIBRARY_PATH, or ldconfig. | ||
| 785 | # HINT: Add -L$(RAYLIB_INSTALL_PATH) -I$(RAYLIB_H_INSTALL_PATH) to your own makefiles. | ||
| 786 | # See below and ../examples/Makefile for more information. | ||
| 787 | |||
| 788 | # RAYLIB_INSTALL_PATH should be the desired full path to libraylib. No relative paths. | ||
| 789 | DESTDIR ?= /usr/local | ||
| 790 | RAYLIB_INSTALL_PATH ?= $(DESTDIR)/lib | ||
| 791 | # RAYLIB_H_INSTALL_PATH locates the installed raylib header and associated source files. | ||
| 792 | RAYLIB_H_INSTALL_PATH ?= $(DESTDIR)/include | ||
| 793 | |||
| 794 | install : | ||
| 795 | ifeq ($(ROOT),root) | ||
| 796 | ifeq ($(PLATFORM_OS),LINUX) | ||
| 797 | # Attention! You are root, writing files to $(RAYLIB_INSTALL_PATH) | ||
| 798 | # and $(RAYLIB_H_INSTALL_PATH). Consult this Makefile for more information. | ||
| 799 | # Prepare the environment as needed. | ||
| 800 | mkdir --parents --verbose $(RAYLIB_INSTALL_PATH) | ||
| 801 | mkdir --parents --verbose $(RAYLIB_H_INSTALL_PATH) | ||
| 802 | ifeq ($(RAYLIB_LIBTYPE),SHARED) | ||
| 803 | # Installing raylib to $(RAYLIB_INSTALL_PATH). | ||
| 804 | cp --update --verbose $(RAYLIB_RELEASE_PATH)/libraylib.so.$(RAYLIB_VERSION) $(RAYLIB_INSTALL_PATH)/lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION) | ||
| 805 | cd $(RAYLIB_INSTALL_PATH); ln -fsv lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION) lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) | ||
| 806 | cd $(RAYLIB_INSTALL_PATH); ln -fsv lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) lib$(RAYLIB_LIB_NAME).so | ||
| 807 | # Uncomment to update the runtime linker cache with RAYLIB_INSTALL_PATH. | ||
| 808 | # Not necessary if later embedding RPATH in your executable. See examples/Makefile. | ||
| 809 | ldconfig $(RAYLIB_INSTALL_PATH) | ||
| 810 | else | ||
| 811 | # Installing raylib to $(RAYLIB_INSTALL_PATH). | ||
| 812 | cp --update --verbose $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_INSTALL_PATH)/lib$(RAYLIB_LIB_NAME).a | ||
| 813 | endif | ||
| 814 | # Copying raylib development files to $(RAYLIB_H_INSTALL_PATH). | ||
| 815 | cp --update raylib.h $(RAYLIB_H_INSTALL_PATH)/raylib.h | ||
| 816 | cp --update raymath.h $(RAYLIB_H_INSTALL_PATH)/raymath.h | ||
| 817 | cp --update rlgl.h $(RAYLIB_H_INSTALL_PATH)/rlgl.h | ||
| 818 | @echo "raylib development files installed/updated!" | ||
| 819 | else | ||
| 820 | @echo "This function currently works on GNU/Linux systems. Add yours today (^;" | ||
| 821 | endif | ||
| 822 | else | ||
| 823 | @echo "Error: Root permissions needed for installation. Try sudo make install" | ||
| 824 | endif | ||
| 825 | |||
| 826 | # Remove raylib dev files installed on the system | ||
| 827 | # NOTE: see 'install' target. | ||
| 828 | uninstall : | ||
| 829 | ifeq ($(ROOT),root) | ||
| 830 | # WARNING: You are root, about to delete items from $(RAYLIB_INSTALL_PATH). | ||
| 831 | # and $(RAYLIB_H_INSTALL_PATH). Please confirm each item. | ||
| 832 | ifeq ($(PLATFORM_OS),LINUX) | ||
| 833 | ifeq ($(RAYLIB_LIBTYPE),SHARED) | ||
| 834 | rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so | ||
| 835 | rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_API_VERSION) | ||
| 836 | rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_VERSION) | ||
| 837 | # Uncomment to clean up the runtime linker cache. See install target. | ||
| 838 | ldconfig | ||
| 839 | else | ||
| 840 | rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.a | ||
| 841 | endif | ||
| 842 | rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/raylib.h | ||
| 843 | rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/raymath.h | ||
| 844 | rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/rlgl.h | ||
| 845 | @echo "raylib development files removed!" | ||
| 846 | else | ||
| 847 | @echo "This function currently works on GNU/Linux systems. Add yours today (^;" | ||
| 848 | endif | ||
| 849 | else | ||
| 850 | @echo "Error: Root permissions needed for uninstallation. Try sudo make uninstall" | ||
| 851 | endif | ||
| 852 | |||
| 853 | .PHONY: clean_shell_cmd clean_shell_sh | ||
| 854 | |||
| 855 | # Clean everything | ||
| 856 | clean: clean_shell_$(PLATFORM_SHELL) | ||
| 857 | @echo "removed all generated files!" | ||
| 858 | |||
| 859 | clean_shell_sh: | ||
| 860 | rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c $(RAYLIB_RELEASE_PATH)/*-protocol.h $(RAYLIB_RELEASE_PATH)/*-protocol-code.h | ||
| 861 | ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) | ||
| 862 | rm -fv $(NATIVE_APP_GLUE)/android_native_app_glue.o | ||
| 863 | endif | ||
| 864 | |||
| 865 | # Set specific target variable | ||
| 866 | clean_shell_cmd: SHELL=cmd | ||
| 867 | clean_shell_cmd: | ||
| 868 | del *.o /s | ||
| 869 | cd $(RAYLIB_RELEASE_PATH) & \ | ||
| 870 | del lib$(RAYLIB_LIB_NAME).a /s & \ | ||
| 871 | del lib$(RAYLIB_LIB_NAME)dll.a /s & \ | ||
| 872 | del $(RAYLIB_LIB_NAME).dll /s & \ | ||
| 873 | del raygui.c /s & \ | ||
diff --git a/raylib/src/config.h b/raylib/src/config.h new file mode 100644 index 0000000..e3749c5 --- /dev/null +++ b/raylib/src/config.h | |||
| @@ -0,0 +1,303 @@ | |||
| 1 | /********************************************************************************************** | ||
| 2 | * | ||
| 3 | * raylib configuration flags | ||
| 4 | * | ||
| 5 | * This file defines all the configuration flags for the different raylib modules | ||
| 6 | * | ||
| 7 | * LICENSE: zlib/libpng | ||
| 8 | * | ||
| 9 | * Copyright (c) 2018-2024 Ahmad Fatoum & Ramon Santamaria (@raysan5) | ||
| 10 | * | ||
| 11 | * This software is provided "as-is", without any express or implied warranty. In no event | ||
| 12 | * will the authors be held liable for any damages arising from the use of this software. | ||
| 13 | * | ||
| 14 | * Permission is granted to anyone to use this software for any purpose, including commercial | ||
| 15 | * applications, and to alter it and redistribute it freely, subject to the following restrictions: | ||
| 16 | * | ||
| 17 | * 1. The origin of this software must not be misrepresented; you must not claim that you | ||
| 18 | * wrote the original software. If you use this software in a product, an acknowledgment | ||
| 19 | * in the product documentation would be appreciated but is not required. | ||
| 20 | * | ||
| 21 | * 2. Altered source versions must be plainly marked as such, and must not be misrepresented | ||
| 22 | * as being the original software. | ||
| 23 | * | ||
| 24 | * 3. This notice may not be removed or altered from any source distribution. | ||
| 25 | * | ||
| 26 | **********************************************************************************************/ | ||
| 27 | |||
| 28 | #ifndef CONFIG_H | ||
| 29 | #define CONFIG_H | ||
| 30 | |||
| 31 | //------------------------------------------------------------------------------------ | ||
| 32 | // Module selection - Some modules could be avoided | ||
| 33 | // Mandatory modules: rcore, rlgl, utils | ||
| 34 | //------------------------------------------------------------------------------------ | ||
| 35 | #define SUPPORT_MODULE_RSHAPES 1 | ||
| 36 | #define SUPPORT_MODULE_RTEXTURES 1 | ||
| 37 | #define SUPPORT_MODULE_RTEXT 1 // WARNING: It requires SUPPORT_MODULE_RTEXTURES to load sprite font textures | ||
| 38 | #define SUPPORT_MODULE_RMODELS 1 | ||
| 39 | #define SUPPORT_MODULE_RAUDIO 1 | ||
| 40 | |||
| 41 | //------------------------------------------------------------------------------------ | ||
| 42 | // Module: rcore - Configuration Flags | ||
| 43 | //------------------------------------------------------------------------------------ | ||
| 44 | // Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital | ||
| 45 | #define SUPPORT_CAMERA_SYSTEM 1 | ||
| 46 | // Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag | ||
| 47 | #define SUPPORT_GESTURES_SYSTEM 1 | ||
| 48 | // Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64 | ||
| 49 | #define SUPPORT_RPRAND_GENERATOR 1 | ||
| 50 | // Mouse gestures are directly mapped like touches and processed by gestures system | ||
| 51 | #define SUPPORT_MOUSE_GESTURES 1 | ||
| 52 | // Reconfigure standard input to receive key inputs, works with SSH connection. | ||
| 53 | #define SUPPORT_SSH_KEYBOARD_RPI 1 | ||
| 54 | // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. | ||
| 55 | // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. | ||
| 56 | #define SUPPORT_WINMM_HIGHRES_TIMER 1 | ||
| 57 | // Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used | ||
| 58 | //#define SUPPORT_BUSY_WAIT_LOOP 1 | ||
| 59 | // Use a partial-busy wait loop, in this case frame sleeps for most of the time, but then runs a busy loop at the end for accuracy | ||
| 60 | #define SUPPORT_PARTIALBUSY_WAIT_LOOP 1 | ||
| 61 | // Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() | ||
| 62 | #define SUPPORT_SCREEN_CAPTURE 1 | ||
| 63 | // Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() | ||
| 64 | #define SUPPORT_GIF_RECORDING 1 | ||
| 65 | // Support CompressData() and DecompressData() functions | ||
| 66 | #define SUPPORT_COMPRESSION_API 1 | ||
| 67 | // Support automatic generated events, loading and recording of those events when required | ||
| 68 | #define SUPPORT_AUTOMATION_EVENTS 1 | ||
| 69 | // Support custom frame control, only for advanced users | ||
| 70 | // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() | ||
| 71 | // Enabling this flag allows manual control of the frame processes, use at your own risk | ||
| 72 | //#define SUPPORT_CUSTOM_FRAME_CONTROL 1 | ||
| 73 | |||
| 74 | |||
| 75 | // rcore: Configuration values | ||
| 76 | //------------------------------------------------------------------------------------ | ||
| 77 | #define MAX_FILEPATH_CAPACITY 8192 // Maximum file paths capacity | ||
| 78 | #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) | ||
| 79 | |||
| 80 | #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported | ||
| 81 | #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported | ||
| 82 | #define MAX_GAMEPADS 4 // Maximum number of gamepads supported | ||
| 83 | #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad) | ||
| 84 | #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) | ||
| 85 | #define MAX_GAMEPAD_VIBRATION_TIME 2.0f // Maximum vibration time in seconds | ||
| 86 | #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported | ||
| 87 | #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue | ||
| 88 | #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue | ||
| 89 | |||
| 90 | #define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB | ||
| 91 | |||
| 92 | #define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record | ||
| 93 | |||
| 94 | //------------------------------------------------------------------------------------ | ||
| 95 | // Module: rlgl - Configuration values | ||
| 96 | //------------------------------------------------------------------------------------ | ||
| 97 | |||
| 98 | // Enable OpenGL Debug Context (only available on OpenGL 4.3) | ||
| 99 | //#define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT 1 | ||
| 100 | |||
| 101 | // Show OpenGL extensions and capabilities detailed logs on init | ||
| 102 | //#define RLGL_SHOW_GL_DETAILS_INFO 1 | ||
| 103 | |||
| 104 | #define RL_SUPPORT_MESH_GPU_SKINNING 1 // GPU skinning, comment if your GPU does not support more than 8 VBOs | ||
| 105 | |||
| 106 | //#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 4096 // Default internal render batch elements limits | ||
| 107 | #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) | ||
| 108 | #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) | ||
| 109 | #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) | ||
| 110 | |||
| 111 | #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack | ||
| 112 | |||
| 113 | #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported | ||
| 114 | |||
| 115 | #define RL_CULL_DISTANCE_NEAR 0.01 // Default projection matrix near cull distance | ||
| 116 | #define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance | ||
| 117 | |||
| 118 | // Default shader vertex attribute locations | ||
| 119 | #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0 | ||
| 120 | #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD 1 | ||
| 121 | #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL 2 | ||
| 122 | #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR 3 | ||
| 123 | #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT 4 | ||
| 124 | #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5 | ||
| 125 | #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6 | ||
| 126 | #if defined(RL_SUPPORT_MESH_GPU_SKINNING) | ||
| 127 | #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS 7 | ||
| 128 | #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8 | ||
| 129 | #endif | ||
| 130 | |||
| 131 | // Default shader vertex attribute names to set location points | ||
| 132 | // NOTE: When a new shader is loaded, the following locations are tried to be set for convenience | ||
| 133 | #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION | ||
| 134 | #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD | ||
| 135 | #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL | ||
| 136 | #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR | ||
| 137 | #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT | ||
| 138 | #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 | ||
| 139 | |||
| 140 | #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix | ||
| 141 | #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix | ||
| 142 | #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix | ||
| 143 | #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix | ||
| 144 | #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) | ||
| 145 | #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) | ||
| 146 | #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) | ||
| 147 | #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) | ||
| 148 | #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) | ||
| 149 | |||
| 150 | |||
| 151 | //------------------------------------------------------------------------------------ | ||
| 152 | // Module: rshapes - Configuration Flags | ||
| 153 | //------------------------------------------------------------------------------------ | ||
| 154 | // Use QUADS instead of TRIANGLES for drawing when possible | ||
| 155 | // Some lines-based shapes could still use lines | ||
| 156 | #define SUPPORT_QUADS_DRAW_MODE 1 | ||
| 157 | |||
| 158 | // rshapes: Configuration values | ||
| 159 | //------------------------------------------------------------------------------------ | ||
| 160 | #define SPLINE_SEGMENT_DIVISIONS 24 // Spline segments subdivisions | ||
| 161 | |||
| 162 | |||
| 163 | //------------------------------------------------------------------------------------ | ||
| 164 | // Module: rtextures - Configuration Flags | ||
| 165 | //------------------------------------------------------------------------------------ | ||
| 166 | // Selecte desired fileformats to be supported for image data loading | ||
| 167 | #define SUPPORT_FILEFORMAT_PNG 1 | ||
| 168 | //#define SUPPORT_FILEFORMAT_BMP 1 | ||
| 169 | //#define SUPPORT_FILEFORMAT_TGA 1 | ||
| 170 | //#define SUPPORT_FILEFORMAT_JPG 1 | ||
| 171 | #define SUPPORT_FILEFORMAT_GIF 1 | ||
| 172 | #define SUPPORT_FILEFORMAT_QOI 1 | ||
| 173 | //#define SUPPORT_FILEFORMAT_PSD 1 | ||
| 174 | #define SUPPORT_FILEFORMAT_DDS 1 | ||
| 175 | //#define SUPPORT_FILEFORMAT_HDR 1 | ||
| 176 | //#define SUPPORT_FILEFORMAT_PIC 1 | ||
| 177 | //#define SUPPORT_FILEFORMAT_KTX 1 | ||
| 178 | //#define SUPPORT_FILEFORMAT_ASTC 1 | ||
| 179 | //#define SUPPORT_FILEFORMAT_PKM 1 | ||
| 180 | //#define SUPPORT_FILEFORMAT_PVR 1 | ||
| 181 | |||
| 182 | // Support image export functionality (.png, .bmp, .tga, .jpg, .qoi) | ||
| 183 | #define SUPPORT_IMAGE_EXPORT 1 | ||
| 184 | // Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) | ||
| 185 | #define SUPPORT_IMAGE_GENERATION 1 | ||
| 186 | // Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... | ||
| 187 | // If not defined, still some functions are supported: ImageFormat(), ImageCrop(), ImageToPOT() | ||
| 188 | #define SUPPORT_IMAGE_MANIPULATION 1 | ||
| 189 | |||
| 190 | |||
| 191 | //------------------------------------------------------------------------------------ | ||
| 192 | // Module: rtext - Configuration Flags | ||
| 193 | //------------------------------------------------------------------------------------ | ||
| 194 | // Default font is loaded on window initialization to be available for the user to render simple text | ||
| 195 | // NOTE: If enabled, uses external module functions to load default raylib font | ||
| 196 | #define SUPPORT_DEFAULT_FONT 1 | ||
| 197 | // Selected desired font fileformats to be supported for loading | ||
| 198 | #define SUPPORT_FILEFORMAT_TTF 1 | ||
| 199 | #define SUPPORT_FILEFORMAT_FNT 1 | ||
| 200 | //#define SUPPORT_FILEFORMAT_BDF 1 | ||
| 201 | |||
| 202 | // Support text management functions | ||
| 203 | // If not defined, still some functions are supported: TextLength(), TextFormat() | ||
| 204 | #define SUPPORT_TEXT_MANIPULATION 1 | ||
| 205 | |||
| 206 | // On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle | ||
| 207 | // at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow | ||
| 208 | // drawing text and shapes with a single draw call [SetShapesTexture()]. | ||
| 209 | #define SUPPORT_FONT_ATLAS_WHITE_REC 1 | ||
| 210 | |||
| 211 | // rtext: Configuration values | ||
| 212 | //------------------------------------------------------------------------------------ | ||
| 213 | #define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions: | ||
| 214 | // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit() | ||
| 215 | #define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit() | ||
| 216 | |||
| 217 | |||
| 218 | //------------------------------------------------------------------------------------ | ||
| 219 | // Module: rmodels - Configuration Flags | ||
| 220 | //------------------------------------------------------------------------------------ | ||
| 221 | // Selected desired model fileformats to be supported for loading | ||
| 222 | #define SUPPORT_FILEFORMAT_OBJ 1 | ||
| 223 | #define SUPPORT_FILEFORMAT_MTL 1 | ||
| 224 | #define SUPPORT_FILEFORMAT_IQM 1 | ||
| 225 | #define SUPPORT_FILEFORMAT_GLTF 1 | ||
| 226 | #define SUPPORT_FILEFORMAT_VOX 1 | ||
| 227 | #define SUPPORT_FILEFORMAT_M3D 1 | ||
| 228 | // Support procedural mesh generation functions, uses external par_shapes.h library | ||
| 229 | // NOTE: Some generated meshes DO NOT include generated texture coordinates | ||
| 230 | #define SUPPORT_MESH_GENERATION 1 | ||
| 231 | |||
| 232 | // rmodels: Configuration values | ||
| 233 | //------------------------------------------------------------------------------------ | ||
| 234 | #define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported | ||
| 235 | |||
| 236 | #ifdef RL_SUPPORT_MESH_GPU_SKINNING | ||
| 237 | #define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh | ||
| 238 | #else | ||
| 239 | #define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh | ||
| 240 | #endif | ||
| 241 | |||
| 242 | //------------------------------------------------------------------------------------ | ||
| 243 | // Module: raudio - Configuration Flags | ||
| 244 | //------------------------------------------------------------------------------------ | ||
| 245 | // Desired audio fileformats to be supported for loading | ||
| 246 | #define SUPPORT_FILEFORMAT_WAV 1 | ||
| 247 | #define SUPPORT_FILEFORMAT_OGG 1 | ||
| 248 | #define SUPPORT_FILEFORMAT_MP3 1 | ||
| 249 | #define SUPPORT_FILEFORMAT_QOA 1 | ||
| 250 | //#define SUPPORT_FILEFORMAT_FLAC 1 | ||
| 251 | #define SUPPORT_FILEFORMAT_XM 1 | ||
| 252 | #define SUPPORT_FILEFORMAT_MOD 1 | ||
| 253 | |||
| 254 | // raudio: Configuration values | ||
| 255 | //------------------------------------------------------------------------------------ | ||
| 256 | #define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (miniaudio: float-32bit) | ||
| 257 | #define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo | ||
| 258 | #define AUDIO_DEVICE_SAMPLE_RATE 0 // Device sample rate (device default) | ||
| 259 | |||
| 260 | #define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Maximum number of audio pool channels | ||
| 261 | |||
| 262 | //------------------------------------------------------------------------------------ | ||
| 263 | // Module: utils - Configuration Flags | ||
| 264 | //------------------------------------------------------------------------------------ | ||
| 265 | // Standard file io library (stdio.h) included | ||
| 266 | #define SUPPORT_STANDARD_FILEIO 1 | ||
| 267 | // Show TRACELOG() output messages | ||
| 268 | // NOTE: By default LOG_DEBUG traces not shown | ||
| 269 | #define SUPPORT_TRACELOG 1 | ||
| 270 | //#define SUPPORT_TRACELOG_DEBUG 1 | ||
| 271 | |||
| 272 | // utils: Configuration values | ||
| 273 | //------------------------------------------------------------------------------------ | ||
| 274 | #define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message | ||
| 275 | |||
| 276 | |||
| 277 | // Enable partial support for clipboard image, only working on SDL3 or | ||
| 278 | // being on both Windows OS + GLFW or Windows OS + RGFW | ||
| 279 | #define SUPPORT_CLIPBOARD_IMAGE 1 | ||
| 280 | |||
| 281 | #if defined(SUPPORT_CLIPBOARD_IMAGE) | ||
| 282 | #ifndef STBI_REQUIRED | ||
| 283 | #define STBI_REQUIRED | ||
| 284 | #endif | ||
| 285 | |||
| 286 | #ifndef SUPPORT_FILEFORMAT_BMP // For clipboard image on Windows | ||
| 287 | #define SUPPORT_FILEFORMAT_BMP 1 | ||
| 288 | #endif | ||
| 289 | |||
| 290 | #ifndef SUPPORT_FILEFORMAT_PNG // Wayland uses png for prints, at least it was on 22 LTS ubuntu | ||
| 291 | #define SUPPORT_FILEFORMAT_PNG 1 | ||
| 292 | #endif | ||
| 293 | |||
| 294 | #ifndef SUPPORT_FILEFORMAT_JPG | ||
| 295 | #define SUPPORT_FILEFORMAT_JPG 1 | ||
| 296 | #endif | ||
| 297 | |||
| 298 | #ifndef SUPPORT_MODULE_RTEXTURES | ||
| 299 | #define SUPPORT_MODULE_RTEXTURES 1 | ||
| 300 | #endif | ||
| 301 | #endif | ||
| 302 | |||
| 303 | #endif // CONFIG_H | ||
diff --git a/raylib/src/external/RGFW.h b/raylib/src/external/RGFW.h new file mode 100644 index 0000000..978536f --- /dev/null +++ b/raylib/src/external/RGFW.h | |||
| @@ -0,0 +1,9015 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2023-24 ColleagueRiley | ||
| 3 | * | ||
| 4 | * libpng license | ||
| 5 | * | ||
| 6 | * This software is provided 'as-is', without any express or implied | ||
| 7 | * warranty. In no event will the authors be held liable for any damages | ||
| 8 | * arising from the use of this software. | ||
| 9 | * | ||
| 10 | * Permission is granted to anyone to use this software for any purpose, | ||
| 11 | * including commercial applications, and to alter it and redistribute it | ||
| 12 | * freely, subject to the following restrictions: | ||
| 13 | * | ||
| 14 | * 1. The origin of this software must not be misrepresented; you must not | ||
| 15 | * claim that you wrote the original software. If you use this software | ||
| 16 | * in a product, an acknowledgment in the product documentation would be | ||
| 17 | * appreciated but is not required. | ||
| 18 | * 2. Altered source versions must be plainly marked as such, and must not be | ||
| 19 | * misrepresented as being the original software. | ||
| 20 | * 3. This notice may not be removed or altered from any source distribution. | ||
| 21 | * | ||
| 22 | * | ||
| 23 | */ | ||
| 24 | |||
| 25 | /* | ||
| 26 | (MAKE SURE RGFW_IMPLEMENTATION is in exactly one header or you use -D RGFW_IMPLEMENTATION) | ||
| 27 | #define RGFW_IMPLEMENTATION - makes it so source code is included with header | ||
| 28 | */ | ||
| 29 | |||
| 30 | /* | ||
| 31 | #define RGFW_IMPLEMENTATION - (required) makes it so the source code is included | ||
| 32 | #define RGFW_PRINT_ERRORS - (optional) makes it so RGFW prints errors when they're found | ||
| 33 | #define RGFW_OSMESA - (optional) use OSmesa as backend (instead of system's opengl api + regular opengl) | ||
| 34 | #define RGFW_BUFFER - (optional) just draw directly to (RGFW) window pixel buffer that is drawn to screen (the buffer is in the RGBA format) | ||
| 35 | #define RGFW_EGL - (optional) use EGL for loading an OpenGL context (instead of the system's opengl api) | ||
| 36 | #define RGFW_OPENGL_ES1 - (optional) use EGL to load and use Opengl ES (version 1) for backend rendering (instead of the system's opengl api) | ||
| 37 | This version doesn't work for desktops (I'm pretty sure) | ||
| 38 | #define RGFW_OPENGL_ES2 - (optional) use OpenGL ES (version 2) | ||
| 39 | #define RGFW_OPENGL_ES3 - (optional) use OpenGL ES (version 3) | ||
| 40 | #define RGFW_DIRECTX - (optional) use directX for the rendering backend (rather than opengl) (windows only, defaults to opengl for unix) | ||
| 41 | #define RGFW_WEBGPU - (optional) use webGPU for rendering (Web ONLY) | ||
| 42 | #define RGFW_NO_API - (optional) don't use any rendering API (no opengl, no vulkan, no directX) | ||
| 43 | |||
| 44 | #define RGFW_LINK_EGL (optional) (windows only) if EGL is being used, if EGL functions should be defined dymanically (using GetProcAddress) | ||
| 45 | #define RGFW_LINK_OSMESA (optional) (windows only) if EGL is being used, if OS Mesa functions should be defined dymanically (using GetProcAddress) | ||
| 46 | |||
| 47 | #define RGFW_X11 (optional) (unix only) if X11 should be used. This option is turned on by default by unix systems except for MacOS | ||
| 48 | #define RGFW_WGL_LOAD (optional) (windows only) if WGL should be loaded dynamically during runtime | ||
| 49 | #define RGFW_NO_X11_CURSOR (optional) (unix only) don't use XCursor | ||
| 50 | #define RGFW_NO_X11_CURSOR_PRELOAD (optional) (unix only) Use XCursor, but don't link it in code, (you'll have to link it with -lXcursor) | ||
| 51 | |||
| 52 | #define RGFW_NO_DPI - Do not include calculate DPI (no XRM nor libShcore included) | ||
| 53 | |||
| 54 | #define RGFW_ALLOC_DROPFILES (optional) if room should be allocating for drop files (by default it's global data) | ||
| 55 | #define RGFW_MALLOC x - choose what function to use to allocate, by default the standard malloc is used | ||
| 56 | #define RGFW_CALLOC x - choose what function to use to allocate (calloc), by default the standard calloc is used | ||
| 57 | #define RGFW_FREE x - choose what function to use to allocated memory, by default the standard free is used | ||
| 58 | |||
| 59 | #define RGFW_EXPORT - Use when building RGFW | ||
| 60 | #define RGFW_IMPORT - Use when linking with RGFW (not as a single-header) | ||
| 61 | |||
| 62 | #define RGFW_STD_INT - force the use stdint.h (for systems that might not have stdint.h (msvc)) | ||
| 63 | */ | ||
| 64 | |||
| 65 | /* | ||
| 66 | Credits : | ||
| 67 | EimaMei/Sacode : Much of the code for creating windows using winapi, Wrote the Silicon library, helped with MacOS Support, siliapp.h -> referencing | ||
| 68 | |||
| 69 | stb - This project is heavily inspired by the stb single header files | ||
| 70 | |||
| 71 | GLFW: | ||
| 72 | certain parts of winapi and X11 are very poorly documented, | ||
| 73 | GLFW's source code was referenced and used throughout the project (used code is marked in some way), | ||
| 74 | this mainly includes, code for drag and drops, code for setting the icon to a bitmap and the code for managing the clipboard for X11 (as these parts are not documented very well) | ||
| 75 | |||
| 76 | GLFW Copyright, https::/github.com/GLFW/GLFW | ||
| 77 | |||
| 78 | Copyright (c) 2002-2006 Marcus Geelnard | ||
| 79 | Copyright (c) 2006-2019 Camilla Löwy | ||
| 80 | |||
| 81 | contributors : (feel free to put yourself here if you contribute) | ||
| 82 | krisvers -> code review | ||
| 83 | EimaMei (SaCode) -> code review | ||
| 84 | Code-Nycticebus -> bug fixes | ||
| 85 | Rob Rohan -> X11 bugs and missing features, MacOS/Cocoa fixing memory issues/bugs | ||
| 86 | AICDG (@THISISAGOODNAME) -> vulkan support (example) | ||
| 87 | @Easymode -> support, testing/debugging, bug fixes and reviews | ||
| 88 | Joshua Rowe (omnisci3nce) - bug fix, review (macOS) | ||
| 89 | @lesleyrs -> bug fix, review (OpenGL) | ||
| 90 | Nick Porcino (meshula) - testing, organization, review (MacOS, examples) | ||
| 91 | */ | ||
| 92 | |||
| 93 | #if _MSC_VER | ||
| 94 | #pragma comment(lib, "gdi32") | ||
| 95 | #pragma comment(lib, "shell32") | ||
| 96 | #pragma comment(lib, "opengl32") | ||
| 97 | #pragma comment(lib, "winmm") | ||
| 98 | #pragma comment(lib, "user32") | ||
| 99 | #endif | ||
| 100 | |||
| 101 | #ifndef RGFW_MALLOC | ||
| 102 | #include <stdlib.h> | ||
| 103 | |||
| 104 | #ifndef __USE_POSIX199309 | ||
| 105 | #define __USE_POSIX199309 | ||
| 106 | #endif | ||
| 107 | |||
| 108 | #include <time.h> | ||
| 109 | #define RGFW_MALLOC malloc | ||
| 110 | #define RGFW_CALLOC calloc | ||
| 111 | #define RGFW_FREE free | ||
| 112 | #endif | ||
| 113 | |||
| 114 | #if !_MSC_VER | ||
| 115 | #ifndef inline | ||
| 116 | #ifndef __APPLE__ | ||
| 117 | #define inline __inline | ||
| 118 | #endif | ||
| 119 | #endif | ||
| 120 | #endif | ||
| 121 | |||
| 122 | #ifdef RGFW_WIN95 /* for windows 95 testing (not that it really works) */ | ||
| 123 | #define RGFW_NO_MONITOR | ||
| 124 | #define RGFW_NO_PASSTHROUGH | ||
| 125 | #endif | ||
| 126 | |||
| 127 | #if defined(RGFW_EXPORT) || defined(RGFW_IMPORT) | ||
| 128 | #if defined(_WIN32) | ||
| 129 | #if defined(__TINYC__) && (defined(RGFW_EXPORT) || defined(RGFW_IMPORT)) | ||
| 130 | #define __declspec(x) __attribute__((x)) | ||
| 131 | #endif | ||
| 132 | |||
| 133 | #if defined(RGFW_EXPORT) | ||
| 134 | #define RGFWDEF __declspec(dllexport) | ||
| 135 | #else | ||
| 136 | #define RGFWDEF __declspec(dllimport) | ||
| 137 | #endif | ||
| 138 | #else | ||
| 139 | #if defined(RGFW_EXPORT) | ||
| 140 | #define RGFWDEF __attribute__((visibility("default"))) | ||
| 141 | #endif | ||
| 142 | #endif | ||
| 143 | #endif | ||
| 144 | |||
| 145 | #ifndef RGFWDEF | ||
| 146 | #ifdef __clang__ | ||
| 147 | #define RGFWDEF static inline | ||
| 148 | #else | ||
| 149 | #define RGFWDEF inline | ||
| 150 | #endif | ||
| 151 | #endif | ||
| 152 | |||
| 153 | #ifndef RGFW_ENUM | ||
| 154 | #define RGFW_ENUM(type, name) type name; enum | ||
| 155 | #endif | ||
| 156 | |||
| 157 | #ifndef RGFW_UNUSED | ||
| 158 | #define RGFW_UNUSED(x) (void)(x); | ||
| 159 | #endif | ||
| 160 | |||
| 161 | #if defined(__cplusplus) && !defined(__EMSCRIPTEN__) | ||
| 162 | extern "C" { | ||
| 163 | #endif | ||
| 164 | |||
| 165 | /* makes sure the header file part is only defined once by default */ | ||
| 166 | #ifndef RGFW_HEADER | ||
| 167 | |||
| 168 | #define RGFW_HEADER | ||
| 169 | |||
| 170 | #if !defined(u8) | ||
| 171 | #if ((defined(_MSC_VER) || defined(__SYMBIAN32__)) && !defined(RGFW_STD_INT)) /* MSVC might not have stdint.h */ | ||
| 172 | typedef unsigned char u8; | ||
| 173 | typedef signed char i8; | ||
| 174 | typedef unsigned short u16; | ||
| 175 | typedef signed short i16; | ||
| 176 | typedef unsigned int u32; | ||
| 177 | typedef signed int i32; | ||
| 178 | typedef unsigned long u64; | ||
| 179 | typedef signed long i64; | ||
| 180 | #else /* use stdint standard types instead of c ""standard"" types */ | ||
| 181 | #include <stdint.h> | ||
| 182 | |||
| 183 | typedef uint8_t u8; | ||
| 184 | typedef int8_t i8; | ||
| 185 | typedef uint16_t u16; | ||
| 186 | typedef int16_t i16; | ||
| 187 | typedef uint32_t u32; | ||
| 188 | typedef int32_t i32; | ||
| 189 | typedef uint64_t u64; | ||
| 190 | typedef int64_t i64; | ||
| 191 | #endif | ||
| 192 | #endif | ||
| 193 | |||
| 194 | #if !defined(b8) /* RGFW bool type */ | ||
| 195 | typedef u8 b8; | ||
| 196 | typedef u32 b32; | ||
| 197 | #endif | ||
| 198 | |||
| 199 | #define RGFW_TRUE (!(0)) | ||
| 200 | #define RGFW_FALSE 0 | ||
| 201 | |||
| 202 | /* thse OS macros looks better & are standardized */ | ||
| 203 | /* plus it helps with cross-compiling */ | ||
| 204 | |||
| 205 | #ifdef __EMSCRIPTEN__ | ||
| 206 | #define RGFW_WEBASM | ||
| 207 | |||
| 208 | #if !defined(RGFW_NO_API) && !defined(RGFW_WEBGPU) | ||
| 209 | #define RGFW_OPENGL | ||
| 210 | #endif | ||
| 211 | |||
| 212 | #ifdef RGFW_EGL | ||
| 213 | #undef RGFW_EGL | ||
| 214 | #endif | ||
| 215 | |||
| 216 | #include <emscripten/html5.h> | ||
| 217 | #include <emscripten/key_codes.h> | ||
| 218 | |||
| 219 | #ifdef RGFW_WEBGPU | ||
| 220 | #include <emscripten/html5_webgpu.h> | ||
| 221 | #endif | ||
| 222 | #endif | ||
| 223 | |||
| 224 | #if defined(RGFW_X11) && defined(__APPLE__) | ||
| 225 | #define RGFW_MACOS_X11 | ||
| 226 | #undef __APPLE__ | ||
| 227 | #endif | ||
| 228 | |||
| 229 | #if defined(_WIN32) && !defined(RGFW_X11) && !defined(RGFW_WEBASM) /* (if you're using X11 on windows some how) */ | ||
| 230 | #define RGFW_WINDOWS | ||
| 231 | |||
| 232 | /* make sure the correct architecture is defined */ | ||
| 233 | #if defined(_WIN64) | ||
| 234 | #define _AMD64_ | ||
| 235 | #undef _X86_ | ||
| 236 | #else | ||
| 237 | #undef _AMD64_ | ||
| 238 | #ifndef _X86_ | ||
| 239 | #define _X86_ | ||
| 240 | #endif | ||
| 241 | #endif | ||
| 242 | |||
| 243 | #ifndef RGFW_NO_XINPUT | ||
| 244 | #ifdef __MINGW32__ /* try to find the right header */ | ||
| 245 | #include <xinput.h> | ||
| 246 | #else | ||
| 247 | #include <XInput.h> | ||
| 248 | #endif | ||
| 249 | #endif | ||
| 250 | |||
| 251 | #if defined(RGFW_DIRECTX) | ||
| 252 | #include <d3d11.h> | ||
| 253 | #include <dxgi.h> | ||
| 254 | #include <dxgi.h> | ||
| 255 | #include <d3dcompiler.h> | ||
| 256 | |||
| 257 | #ifndef __cplusplus | ||
| 258 | #define __uuidof(T) IID_##T | ||
| 259 | #endif | ||
| 260 | #endif | ||
| 261 | |||
| 262 | #elif defined(RGFW_WAYLAND) | ||
| 263 | #if !defined(RGFW_NO_API) && (!defined(RGFW_BUFFER) || defined(RGFW_OPENGL)) | ||
| 264 | #define RGFW_EGL | ||
| 265 | #define RGFW_OPENGL | ||
| 266 | #include <wayland-egl.h> | ||
| 267 | #endif | ||
| 268 | |||
| 269 | #include <wayland-client.h> | ||
| 270 | #elif (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11)) && !defined(RGFW_WEBASM) | ||
| 271 | #define RGFW_MACOS_X11 | ||
| 272 | #define RGFW_X11 | ||
| 273 | #include <X11/Xlib.h> | ||
| 274 | #elif defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11) && !defined(RGFW_WEBASM) | ||
| 275 | #define RGFW_MACOS | ||
| 276 | #endif | ||
| 277 | |||
| 278 | #if (defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)) && !defined(RGFW_EGL) | ||
| 279 | #define RGFW_EGL | ||
| 280 | #endif | ||
| 281 | |||
| 282 | #if !defined(RGFW_OSMESA) && !defined(RGFW_EGL) && !defined(RGFW_OPENGL) && !defined(RGFW_DIRECTX) && !defined(RGFW_BUFFER) && !defined(RGFW_NO_API) | ||
| 283 | #define RGFW_OPENGL | ||
| 284 | #endif | ||
| 285 | |||
| 286 | #ifdef RGFW_EGL | ||
| 287 | #include <EGL/egl.h> | ||
| 288 | #elif defined(RGFW_OSMESA) | ||
| 289 | #ifndef __APPLE__ | ||
| 290 | #include <GL/osmesa.h> | ||
| 291 | #else | ||
| 292 | #include <OpenGL/osmesa.h> | ||
| 293 | #endif | ||
| 294 | #endif | ||
| 295 | |||
| 296 | #if defined(RGFW_OPENGL) && defined(RGFW_X11) | ||
| 297 | #ifndef GLX_MESA_swap_control | ||
| 298 | #define GLX_MESA_swap_control | ||
| 299 | #endif | ||
| 300 | #include <GL/glx.h> /* GLX defs, xlib.h, gl.h */ | ||
| 301 | #endif | ||
| 302 | |||
| 303 | #ifndef RGFW_ALPHA | ||
| 304 | #define RGFW_ALPHA 128 /* alpha value for RGFW_TRANSPARENT_WINDOW (WINAPI ONLY, macOS + linux don't need this) */ | ||
| 305 | #endif | ||
| 306 | |||
| 307 | /*! Optional arguments for making a windows */ | ||
| 308 | #define RGFW_TRANSPARENT_WINDOW (1L<<9) /*!< the window is transparent (only properly works on X11 and MacOS, although it's although for windows) */ | ||
| 309 | #define RGFW_NO_BORDER (1L<<3) /*!< the window doesn't have border */ | ||
| 310 | #define RGFW_NO_RESIZE (1L<<4) /*!< the window cannot be resized by the user */ | ||
| 311 | #define RGFW_ALLOW_DND (1L<<5) /*!< the window supports drag and drop*/ | ||
| 312 | #define RGFW_HIDE_MOUSE (1L<<6) /*! the window should hide the mouse or not (can be toggled later on) using `RGFW_window_mouseShow*/ | ||
| 313 | #define RGFW_FULLSCREEN (1L<<8) /* the window is fullscreen by default or not */ | ||
| 314 | #define RGFW_CENTER (1L<<10) /*! center the window on the screen */ | ||
| 315 | #define RGFW_OPENGL_SOFTWARE (1L<<11) /*! use OpenGL software rendering */ | ||
| 316 | #define RGFW_COCOA_MOVE_TO_RESOURCE_DIR (1L << 12) /* (cocoa only), move to resource folder */ | ||
| 317 | #define RGFW_SCALE_TO_MONITOR (1L << 13) /* scale the window to the screen */ | ||
| 318 | #define RGFW_NO_INIT_API (1L << 2) /* DO not init an API (mostly for bindings, you should use `#define RGFW_NO_API` in C */ | ||
| 319 | |||
| 320 | #define RGFW_NO_GPU_RENDER (1L<<14) /* don't render (using the GPU based API)*/ | ||
| 321 | #define RGFW_NO_CPU_RENDER (1L<<15) /* don't render (using the CPU based buffer rendering)*/ | ||
| 322 | #define RGFW_WINDOW_HIDE (1L << 16)/* the window is hidden */ | ||
| 323 | |||
| 324 | typedef RGFW_ENUM(u8, RGFW_event_types) { | ||
| 325 | /*! event codes */ | ||
| 326 | RGFW_keyPressed = 1, /* a key has been pressed */ | ||
| 327 | RGFW_keyReleased, /*!< a key has been released*/ | ||
| 328 | /*! key event note | ||
| 329 | the code of the key pressed is stored in | ||
| 330 | RGFW_Event.keyCode | ||
| 331 | !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! | ||
| 332 | |||
| 333 | while a string version is stored in | ||
| 334 | RGFW_Event.KeyString | ||
| 335 | |||
| 336 | RGFW_Event.lockState holds the current lockState | ||
| 337 | this means if CapsLock, NumLock are active or not | ||
| 338 | */ | ||
| 339 | RGFW_mouseButtonPressed, /*!< a mouse button has been pressed (left,middle,right)*/ | ||
| 340 | RGFW_mouseButtonReleased, /*!< a mouse button has been released (left,middle,right)*/ | ||
| 341 | RGFW_mousePosChanged, /*!< the position of the mouse has been changed*/ | ||
| 342 | /*! mouse event note | ||
| 343 | the x and y of the mouse can be found in the vector, RGFW_Event.point | ||
| 344 | |||
| 345 | RGFW_Event.button holds which mouse button was pressed | ||
| 346 | */ | ||
| 347 | RGFW_jsButtonPressed, /*!< a joystick button was pressed */ | ||
| 348 | RGFW_jsButtonReleased, /*!< a joystick button was released */ | ||
| 349 | RGFW_jsAxisMove, /*!< an axis of a joystick was moved*/ | ||
| 350 | /*! joystick event note | ||
| 351 | RGFW_Event.joystick holds which joystick was altered, if any | ||
| 352 | RGFW_Event.button holds which joystick button was pressed | ||
| 353 | |||
| 354 | RGFW_Event.axis holds the data of all the axis | ||
| 355 | RGFW_Event.axisCount says how many axis there are | ||
| 356 | */ | ||
| 357 | RGFW_windowMoved, /*!< the window was moved (by the user) */ | ||
| 358 | RGFW_windowResized, /*!< the window was resized (by the user), [on webASM this means the browser was resized] */ | ||
| 359 | RGFW_focusIn, /*!< window is in focus now */ | ||
| 360 | RGFW_focusOut, /*!< window is out of focus now */ | ||
| 361 | RGFW_mouseEnter, /* mouse entered the window */ | ||
| 362 | RGFW_mouseLeave, /* mouse left the window */ | ||
| 363 | RGFW_windowRefresh, /* The window content needs to be refreshed */ | ||
| 364 | |||
| 365 | /* attribs change event note | ||
| 366 | The event data is sent straight to the window structure | ||
| 367 | with win->r.x, win->r.y, win->r.w and win->r.h | ||
| 368 | */ | ||
| 369 | RGFW_quit, /*!< the user clicked the quit button*/ | ||
| 370 | RGFW_dnd, /*!< a file has been dropped into the window*/ | ||
| 371 | RGFW_dnd_init /*!< the start of a dnd event, when the place where the file drop is known */ | ||
| 372 | /* dnd data note | ||
| 373 | The x and y coords of the drop are stored in the vector RGFW_Event.point | ||
| 374 | |||
| 375 | RGFW_Event.droppedFilesCount holds how many files were dropped | ||
| 376 | |||
| 377 | This is also the size of the array which stores all the dropped file string, | ||
| 378 | RGFW_Event.droppedFiles | ||
| 379 | */ | ||
| 380 | }; | ||
| 381 | |||
| 382 | /*! mouse button codes (RGFW_Event.button) */ | ||
| 383 | #define RGFW_mouseLeft 1 /*!< left mouse button is pressed*/ | ||
| 384 | #define RGFW_mouseMiddle 2 /*!< mouse-wheel-button is pressed*/ | ||
| 385 | #define RGFW_mouseRight 3 /*!< right mouse button is pressed*/ | ||
| 386 | #define RGFW_mouseScrollUp 4 /*!< mouse wheel is scrolling up*/ | ||
| 387 | #define RGFW_mouseScrollDown 5 /*!< mouse wheel is scrolling down*/ | ||
| 388 | |||
| 389 | #ifndef RGFW_MAX_PATH | ||
| 390 | #define RGFW_MAX_PATH 260 /* max length of a path (for dnd) */ | ||
| 391 | #endif | ||
| 392 | #ifndef RGFW_MAX_DROPS | ||
| 393 | #define RGFW_MAX_DROPS 260 /* max items you can drop at once */ | ||
| 394 | #endif | ||
| 395 | |||
| 396 | |||
| 397 | /* for RGFW_Event.lockstate */ | ||
| 398 | #define RGFW_CAPSLOCK (1L << 1) | ||
| 399 | #define RGFW_NUMLOCK (1L << 2) | ||
| 400 | |||
| 401 | /*! joystick button codes (based on xbox/playstation), you may need to change these values per controller */ | ||
| 402 | #ifndef RGFW_joystick_codes | ||
| 403 | typedef RGFW_ENUM(u8, RGFW_joystick_codes) { | ||
| 404 | RGFW_JS_A = 0, /*!< or PS X button */ | ||
| 405 | RGFW_JS_B = 1, /*!< or PS circle button */ | ||
| 406 | RGFW_JS_Y = 2, /*!< or PS triangle button */ | ||
| 407 | RGFW_JS_X = 3, /*!< or PS square button */ | ||
| 408 | RGFW_JS_START = 9, /*!< start button */ | ||
| 409 | RGFW_JS_SELECT = 8, /*!< select button */ | ||
| 410 | RGFW_JS_HOME = 10, /*!< home button */ | ||
| 411 | RGFW_JS_UP = 13, /*!< dpad up */ | ||
| 412 | RGFW_JS_DOWN = 14, /*!< dpad down*/ | ||
| 413 | RGFW_JS_LEFT = 15, /*!< dpad left */ | ||
| 414 | RGFW_JS_RIGHT = 16, /*!< dpad right */ | ||
| 415 | RGFW_JS_L1 = 4, /*!< left bump */ | ||
| 416 | RGFW_JS_L2 = 5, /*!< left trigger*/ | ||
| 417 | RGFW_JS_R1 = 6, /*!< right bumper */ | ||
| 418 | RGFW_JS_R2 = 7, /*!< right trigger */ | ||
| 419 | }; | ||
| 420 | #endif | ||
| 421 | |||
| 422 | /*! basic vector type, if there's not already a point/vector type of choice */ | ||
| 423 | #ifndef RGFW_point | ||
| 424 | typedef struct { i32 x, y; } RGFW_point; | ||
| 425 | #endif | ||
| 426 | |||
| 427 | /*! basic rect type, if there's not already a rect type of choice */ | ||
| 428 | #ifndef RGFW_rect | ||
| 429 | typedef struct { i32 x, y, w, h; } RGFW_rect; | ||
| 430 | #endif | ||
| 431 | |||
| 432 | /*! basic area type, if there's not already a area type of choice */ | ||
| 433 | #ifndef RGFW_area | ||
| 434 | typedef struct { u32 w, h; } RGFW_area; | ||
| 435 | #endif | ||
| 436 | |||
| 437 | #ifndef __cplusplus | ||
| 438 | #define RGFW_POINT(x, y) (RGFW_point){(i32)(x), (i32)(y)} | ||
| 439 | #define RGFW_RECT(x, y, w, h) (RGFW_rect){(i32)(x), (i32)(y), (i32)(w), (i32)(h)} | ||
| 440 | #define RGFW_AREA(w, h) (RGFW_area){(u32)(w), (u32)(h)} | ||
| 441 | #else | ||
| 442 | #define RGFW_POINT(x, y) {(i32)(x), (i32)(y)} | ||
| 443 | #define RGFW_RECT(x, y, w, h) {(i32)(x), (i32)(y), (i32)(w), (i32)(h)} | ||
| 444 | #define RGFW_AREA(w, h) {(u32)(w), (u32)(h)} | ||
| 445 | #endif | ||
| 446 | |||
| 447 | #ifndef RGFW_NO_MONITOR | ||
| 448 | /*! structure for monitor data */ | ||
| 449 | typedef struct RGFW_monitor { | ||
| 450 | char name[128]; /*!< monitor name */ | ||
| 451 | RGFW_rect rect; /*!< monitor Workarea */ | ||
| 452 | float scaleX, scaleY; /*!< monitor content scale*/ | ||
| 453 | float physW, physH; /*!< monitor physical size */ | ||
| 454 | } RGFW_monitor; | ||
| 455 | |||
| 456 | /* | ||
| 457 | NOTE : Monitor functions should be ran only as many times as needed (not in a loop) | ||
| 458 | */ | ||
| 459 | |||
| 460 | /*! get an array of all the monitors (max 6) */ | ||
| 461 | RGFWDEF RGFW_monitor* RGFW_getMonitors(void); | ||
| 462 | /*! get the primary monitor */ | ||
| 463 | RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void); | ||
| 464 | #endif | ||
| 465 | |||
| 466 | /* NOTE: some parts of the data can represent different things based on the event (read comments in RGFW_Event struct) */ | ||
| 467 | /*! Event structure for checking/getting events */ | ||
| 468 | typedef struct RGFW_Event { | ||
| 469 | char keyName[16]; /*!< key name of event*/ | ||
| 470 | |||
| 471 | /*! drag and drop data */ | ||
| 472 | /* 260 max paths with a max length of 260 */ | ||
| 473 | #ifdef RGFW_ALLOC_DROPFILES | ||
| 474 | char** droppedFiles; | ||
| 475 | #else | ||
| 476 | char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH]; /*!< dropped files*/ | ||
| 477 | #endif | ||
| 478 | u32 droppedFilesCount; /*!< house many files were dropped */ | ||
| 479 | |||
| 480 | u32 type; /*!< which event has been sent?*/ | ||
| 481 | RGFW_point point; /*!< mouse x, y of event (or drop point) */ | ||
| 482 | |||
| 483 | u8 keyCode; /*!< keycode of event !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */ | ||
| 484 | |||
| 485 | b8 repeat; /*!< key press event repeated (the key is being held) */ | ||
| 486 | b8 inFocus; /*!< if the window is in focus or not (this is always true for MacOS windows due to the api being weird) */ | ||
| 487 | |||
| 488 | u8 lockState; | ||
| 489 | |||
| 490 | u8 button; /* !< which mouse button was pressed */ | ||
| 491 | double scroll; /*!< the raw mouse scroll value */ | ||
| 492 | |||
| 493 | u16 joystick; /*! which joystick this event applies to (if applicable to any) */ | ||
| 494 | u8 axisesCount; /*!< number of axises */ | ||
| 495 | RGFW_point axis[2]; /*!< x, y of axises (-100 to 100) */ | ||
| 496 | |||
| 497 | u64 frameTime, frameTime2; /*!< this is used for counting the fps */ | ||
| 498 | } RGFW_Event; | ||
| 499 | |||
| 500 | /*! source data for the window (used by the APIs) */ | ||
| 501 | typedef struct RGFW_window_src { | ||
| 502 | #ifdef RGFW_WINDOWS | ||
| 503 | HWND window; /*!< source window */ | ||
| 504 | HDC hdc; /*!< source HDC */ | ||
| 505 | u32 hOffset; /*!< height offset for window */ | ||
| 506 | #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) | ||
| 507 | HGLRC ctx; /*!< source graphics context */ | ||
| 508 | #elif defined(RGFW_OSMESA) | ||
| 509 | OSMesaContext ctx; | ||
| 510 | #elif defined(RGFW_DIRECTX) | ||
| 511 | IDXGISwapChain* swapchain; | ||
| 512 | ID3D11RenderTargetView* renderTargetView; | ||
| 513 | ID3D11DepthStencilView* pDepthStencilView; | ||
| 514 | #elif defined(RGFW_EGL) | ||
| 515 | EGLSurface EGL_surface; | ||
| 516 | EGLDisplay EGL_display; | ||
| 517 | EGLContext EGL_context; | ||
| 518 | #endif | ||
| 519 | |||
| 520 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 521 | HDC hdcMem; | ||
| 522 | HBITMAP bitmap; | ||
| 523 | #endif | ||
| 524 | RGFW_area maxSize, minSize; /*!< for setting max/min resize (RGFW_WINDOWS) */ | ||
| 525 | #elif defined(RGFW_X11) | ||
| 526 | Display* display; /*!< source display */ | ||
| 527 | Window window; /*!< source window */ | ||
| 528 | #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) | ||
| 529 | GLXContext ctx; /*!< source graphics context */ | ||
| 530 | #elif defined(RGFW_OSMESA) | ||
| 531 | OSMesaContext ctx; | ||
| 532 | #elif defined(RGFW_EGL) | ||
| 533 | EGLSurface EGL_surface; | ||
| 534 | EGLDisplay EGL_display; | ||
| 535 | EGLContext EGL_context; | ||
| 536 | #endif | ||
| 537 | |||
| 538 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 539 | XImage* bitmap; | ||
| 540 | GC gc; | ||
| 541 | #endif | ||
| 542 | #elif defined(RGFW_WAYLAND) | ||
| 543 | struct wl_display* display; | ||
| 544 | struct wl_surface* surface; | ||
| 545 | struct wl_buffer* wl_buffer; | ||
| 546 | struct wl_keyboard* keyboard; | ||
| 547 | |||
| 548 | struct xdg_surface* xdg_surface; | ||
| 549 | struct xdg_toplevel* xdg_toplevel; | ||
| 550 | struct zxdg_toplevel_decoration_v1* decoration; | ||
| 551 | RGFW_Event events[20]; | ||
| 552 | i32 eventLen; | ||
| 553 | size_t eventIndex; | ||
| 554 | #if defined(RGFW_EGL) | ||
| 555 | struct wl_egl_window* window; | ||
| 556 | EGLSurface EGL_surface; | ||
| 557 | EGLDisplay EGL_display; | ||
| 558 | EGLContext EGL_context; | ||
| 559 | #elif defined(RGFW_OSMESA) | ||
| 560 | OSMesaContext ctx; | ||
| 561 | #endif | ||
| 562 | #elif defined(RGFW_MACOS) | ||
| 563 | u32 display; | ||
| 564 | void* displayLink; | ||
| 565 | void* window; | ||
| 566 | b8 dndPassed; | ||
| 567 | #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) | ||
| 568 | void* ctx; /*!< source graphics context */ | ||
| 569 | #elif defined(RGFW_OSMESA) | ||
| 570 | OSMesaContext ctx; | ||
| 571 | #elif defined(RGFW_EGL) | ||
| 572 | EGLSurface EGL_surface; | ||
| 573 | EGLDisplay EGL_display; | ||
| 574 | EGLContext EGL_context; | ||
| 575 | #endif | ||
| 576 | |||
| 577 | void* view; /*apple viewpoint thingy*/ | ||
| 578 | |||
| 579 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 580 | void* bitmap; /*!< API's bitmap for storing or managing */ | ||
| 581 | void* image; | ||
| 582 | #endif | ||
| 583 | #elif defined(RGFW_WEBASM) | ||
| 584 | #ifdef RGFW_WEBGPU | ||
| 585 | WGPUInstance ctx; | ||
| 586 | WGPUDevice device; | ||
| 587 | WGPUQueue queue; | ||
| 588 | #else | ||
| 589 | EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx; | ||
| 590 | #endif | ||
| 591 | #endif | ||
| 592 | } RGFW_window_src; | ||
| 593 | |||
| 594 | |||
| 595 | |||
| 596 | typedef struct RGFW_window { | ||
| 597 | RGFW_window_src src; /*!< src window data */ | ||
| 598 | |||
| 599 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 600 | u8* buffer; /*!< buffer for non-GPU systems (OSMesa, basic software rendering) */ | ||
| 601 | /* when rendering using RGFW_BUFFER, the buffer is in the RGBA format */ | ||
| 602 | #endif | ||
| 603 | void* userPtr; /* ptr for usr data */ | ||
| 604 | |||
| 605 | RGFW_Event event; /*!< current event */ | ||
| 606 | |||
| 607 | RGFW_rect r; /*!< the x, y, w and h of the struct */ | ||
| 608 | |||
| 609 | RGFW_point _lastMousePoint; /*!< last cusor point (for raw mouse data) */ | ||
| 610 | |||
| 611 | u32 _winArgs; /*!< windows args (for RGFW to check) */ | ||
| 612 | } RGFW_window; /*!< Window structure for managing the window */ | ||
| 613 | |||
| 614 | #if defined(RGFW_X11) || defined(RGFW_MACOS) | ||
| 615 | typedef u64 RGFW_thread; /*!< thread type unix */ | ||
| 616 | #else | ||
| 617 | typedef void* RGFW_thread; /*!< thread type for window */ | ||
| 618 | #endif | ||
| 619 | |||
| 620 | /** * @defgroup Window_management | ||
| 621 | * @{ */ | ||
| 622 | |||
| 623 | |||
| 624 | /*! | ||
| 625 | * the class name for X11 and WinAPI. apps with the same class will be grouped by the WM | ||
| 626 | * by default the class name will == the root window's name | ||
| 627 | */ | ||
| 628 | RGFWDEF void RGFW_setClassName(char* name); | ||
| 629 | |||
| 630 | /*! this has to be set before createWindow is called, else the fulscreen size is used */ | ||
| 631 | RGFWDEF void RGFW_setBufferSize(RGFW_area size); /*!< the buffer cannot be resized (by RGFW) */ | ||
| 632 | |||
| 633 | RGFWDEF RGFW_window* RGFW_createWindow( | ||
| 634 | const char* name, /* name of the window */ | ||
| 635 | RGFW_rect rect, /* rect of window */ | ||
| 636 | u16 args /* extra arguments (NULL / (u16)0 means no args used)*/ | ||
| 637 | ); /*!< function to create a window struct */ | ||
| 638 | |||
| 639 | /*! get the size of the screen to an area struct */ | ||
| 640 | RGFWDEF RGFW_area RGFW_getScreenSize(void); | ||
| 641 | |||
| 642 | /*! | ||
| 643 | this function checks an *individual* event (and updates window structure attributes) | ||
| 644 | this means, using this function without a while loop may cause event lag | ||
| 645 | |||
| 646 | ex. | ||
| 647 | |||
| 648 | while (RGFW_window_checkEvent(win) != NULL) [this keeps checking events until it reaches the last one] | ||
| 649 | |||
| 650 | this function is optional if you choose to use event callbacks, | ||
| 651 | although you still need some way to tell RGFW to process events eg. `RGFW_window_checkEvents` | ||
| 652 | */ | ||
| 653 | |||
| 654 | RGFWDEF RGFW_Event* RGFW_window_checkEvent(RGFW_window* win); /*!< check current event (returns a pointer to win->event or NULL if there is no event)*/ | ||
| 655 | |||
| 656 | /*! | ||
| 657 | for RGFW_window_eventWait and RGFW_window_checkEvents | ||
| 658 | waitMS -> Allows th e function to keep checking for events even after `RGFW_window_checkEvent == NULL` | ||
| 659 | if waitMS == 0, the loop will not wait for events | ||
| 660 | if waitMS == a positive integer, the loop will wait that many miliseconds after there are no more events until it returns | ||
| 661 | if waitMS == a negative integer, the loop will not return until it gets another event | ||
| 662 | */ | ||
| 663 | typedef RGFW_ENUM(i32, RGFW_eventWait) { | ||
| 664 | RGFW_NEXT = -1, | ||
| 665 | RGFW_NO_WAIT = 0 | ||
| 666 | }; | ||
| 667 | |||
| 668 | /*! sleep until RGFW gets an event or the timer ends (defined by OS) */ | ||
| 669 | RGFWDEF void RGFW_window_eventWait(RGFW_window* win, i32 waitMS); | ||
| 670 | |||
| 671 | /*! | ||
| 672 | check all the events until there are none left, | ||
| 673 | this should only be used if you're using callbacks only | ||
| 674 | */ | ||
| 675 | RGFWDEF void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS); | ||
| 676 | |||
| 677 | /*! | ||
| 678 | Tell RGFW_window_eventWait to stop waiting, to be ran from another thread | ||
| 679 | */ | ||
| 680 | RGFWDEF void RGFW_stopCheckEvents(void); | ||
| 681 | |||
| 682 | /*! window managment functions*/ | ||
| 683 | RGFWDEF void RGFW_window_close(RGFW_window* win); /*!< close the window and free leftover data */ | ||
| 684 | |||
| 685 | /*! moves window to a given point */ | ||
| 686 | RGFWDEF void RGFW_window_move(RGFW_window* win, | ||
| 687 | RGFW_point v/*!< new pos*/ | ||
| 688 | ); | ||
| 689 | |||
| 690 | #ifndef RGFW_NO_MONITOR | ||
| 691 | /*! move to a specific monitor */ | ||
| 692 | RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m /* monitor */); | ||
| 693 | #endif | ||
| 694 | |||
| 695 | /*! resize window to a current size/area */ | ||
| 696 | RGFWDEF void RGFW_window_resize(RGFW_window* win, /*!< source window */ | ||
| 697 | RGFW_area a/*!< new size*/ | ||
| 698 | ); | ||
| 699 | |||
| 700 | /*! set the minimum size a user can shrink a window to a given size/area */ | ||
| 701 | RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a); | ||
| 702 | /*! set the minimum size a user can extend a window to a given size/area */ | ||
| 703 | RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a); | ||
| 704 | |||
| 705 | RGFWDEF void RGFW_window_maximize(RGFW_window* win); /*!< maximize the window size */ | ||
| 706 | RGFWDEF void RGFW_window_minimize(RGFW_window* win); /*!< minimize the window (in taskbar (per OS))*/ | ||
| 707 | RGFWDEF void RGFW_window_restore(RGFW_window* win); /*!< restore the window from minimized (per OS)*/ | ||
| 708 | |||
| 709 | /*! if the window should have a border or not (borderless) based on bool value of `border` */ | ||
| 710 | RGFWDEF void RGFW_window_setBorder(RGFW_window* win, b8 border); | ||
| 711 | |||
| 712 | /*! turn on / off dnd (RGFW_ALLOW_DND stil must be passed to the window)*/ | ||
| 713 | RGFWDEF void RGFW_window_setDND(RGFW_window* win, b8 allow); | ||
| 714 | |||
| 715 | #ifndef RGFW_NO_PASSTHROUGH | ||
| 716 | /*!! turn on / off mouse passthrough */ | ||
| 717 | RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough); | ||
| 718 | #endif | ||
| 719 | |||
| 720 | /*! rename window to a given string */ | ||
| 721 | RGFWDEF void RGFW_window_setName(RGFW_window* win, | ||
| 722 | char* name | ||
| 723 | ); | ||
| 724 | |||
| 725 | RGFWDEF void RGFW_window_setIcon(RGFW_window* win, /*!< source window */ | ||
| 726 | u8* icon /*!< icon bitmap */, | ||
| 727 | RGFW_area a /*!< width and height of the bitmap*/, | ||
| 728 | i32 channels /*!< how many channels the bitmap has (rgb : 3, rgba : 4) */ | ||
| 729 | ); /*!< image resized by default */ | ||
| 730 | |||
| 731 | /*!< sets mouse to bitmap (very simular to RGFW_window_setIcon), image NOT resized by default*/ | ||
| 732 | RGFWDEF void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels); | ||
| 733 | |||
| 734 | /*!< sets the mouse to a standard API cursor (based on RGFW_MOUSE, as seen at the end of the RGFW_HEADER part of this file) */ | ||
| 735 | RGFWDEF void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse); | ||
| 736 | |||
| 737 | RGFWDEF void RGFW_window_setMouseDefault(RGFW_window* win); /*!< sets the mouse to the default mouse icon */ | ||
| 738 | /* | ||
| 739 | Locks cursor at the center of the window | ||
| 740 | win->event.point become raw mouse movement data | ||
| 741 | |||
| 742 | this is useful for a 3D camera | ||
| 743 | */ | ||
| 744 | RGFWDEF void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area); | ||
| 745 | /*! stop holding the mouse and let it move freely */ | ||
| 746 | RGFWDEF void RGFW_window_mouseUnhold(RGFW_window* win); | ||
| 747 | |||
| 748 | /*! hide the window */ | ||
| 749 | RGFWDEF void RGFW_window_hide(RGFW_window* win); | ||
| 750 | /*! show the window */ | ||
| 751 | RGFWDEF void RGFW_window_show(RGFW_window* win); | ||
| 752 | |||
| 753 | /* | ||
| 754 | makes it so `RGFW_window_shouldClose` returns true | ||
| 755 | by setting the window event.type to RGFW_quit | ||
| 756 | */ | ||
| 757 | RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win); | ||
| 758 | |||
| 759 | /*! where the mouse is on the screen */ | ||
| 760 | RGFWDEF RGFW_point RGFW_getGlobalMousePoint(void); | ||
| 761 | |||
| 762 | /*! where the mouse is on the window */ | ||
| 763 | RGFWDEF RGFW_point RGFW_window_getMousePoint(RGFW_window* win); | ||
| 764 | |||
| 765 | /*! show the mouse or hide the mouse*/ | ||
| 766 | RGFWDEF void RGFW_window_showMouse(RGFW_window* win, i8 show); | ||
| 767 | /*! move the mouse to a set x, y pos*/ | ||
| 768 | RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v); | ||
| 769 | |||
| 770 | /*! if the window should close (RGFW_close was sent or escape was pressed) */ | ||
| 771 | RGFWDEF b8 RGFW_window_shouldClose(RGFW_window* win); | ||
| 772 | /*! if window is fullscreen'd */ | ||
| 773 | RGFWDEF b8 RGFW_window_isFullscreen(RGFW_window* win); | ||
| 774 | /*! if window is hidden */ | ||
| 775 | RGFWDEF b8 RGFW_window_isHidden(RGFW_window* win); | ||
| 776 | /*! if window is minimized */ | ||
| 777 | RGFWDEF b8 RGFW_window_isMinimized(RGFW_window* win); | ||
| 778 | /*! if window is maximized */ | ||
| 779 | RGFWDEF b8 RGFW_window_isMaximized(RGFW_window* win); | ||
| 780 | |||
| 781 | /** @} */ | ||
| 782 | |||
| 783 | /** * @defgroup Monitor | ||
| 784 | * @{ */ | ||
| 785 | |||
| 786 | #ifndef RGFW_NO_MONITOR | ||
| 787 | /* | ||
| 788 | scale the window to the monitor, | ||
| 789 | this is run by default if the user uses the arg `RGFW_SCALE_TO_MONITOR` during window creation | ||
| 790 | */ | ||
| 791 | RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win); | ||
| 792 | /*! get the struct of the window's monitor */ | ||
| 793 | RGFWDEF RGFW_monitor RGFW_window_getMonitor(RGFW_window* win); | ||
| 794 | #endif | ||
| 795 | |||
| 796 | /** @} */ | ||
| 797 | |||
| 798 | /** * @defgroup Input | ||
| 799 | * @{ */ | ||
| 800 | |||
| 801 | /*error handling*/ | ||
| 802 | RGFWDEF b8 RGFW_Error(void); /*!< returns true if an error has occurred (doesn't print errors itself) */ | ||
| 803 | |||
| 804 | /*! returns true if the key should be shifted */ | ||
| 805 | RGFWDEF b8 RGFW_shouldShift(u32 keycode, u8 lockState); | ||
| 806 | |||
| 807 | /*! get char from RGFW keycode (using a LUT), uses shift'd version if shift = true */ | ||
| 808 | RGFWDEF char RGFW_keyCodeToChar(u32 keycode, b8 shift); | ||
| 809 | /*! get char from RGFW keycode (using a LUT), uses lockState for shouldShift) */ | ||
| 810 | RGFWDEF char RGFW_keyCodeToCharAuto(u32 keycode, u8 lockState); | ||
| 811 | |||
| 812 | /*! if window == NULL, it checks if the key is pressed globally. Otherwise, it checks only if the key is pressed while the window in focus.*/ | ||
| 813 | RGFWDEF b8 RGFW_isPressed(RGFW_window* win, u8 key); /*!< if key is pressed (key code)*/ | ||
| 814 | |||
| 815 | RGFWDEF b8 RGFW_wasPressed(RGFW_window* win, u8 key); /*!< if key was pressed (checks previous state only) (key code)*/ | ||
| 816 | |||
| 817 | RGFWDEF b8 RGFW_isHeld(RGFW_window* win, u8 key); /*!< if key is held (key code)*/ | ||
| 818 | RGFWDEF b8 RGFW_isReleased(RGFW_window* win, u8 key); /*!< if key is released (key code)*/ | ||
| 819 | |||
| 820 | /* if a key is pressed and then released, pretty much the same as RGFW_isReleased */ | ||
| 821 | RGFWDEF b8 RGFW_isClicked(RGFW_window* win, u8 key /*!< key code*/); | ||
| 822 | |||
| 823 | /*! if a mouse button is pressed */ | ||
| 824 | RGFWDEF b8 RGFW_isMousePressed(RGFW_window* win, u8 button /*!< mouse button code */ ); | ||
| 825 | /*! if a mouse button is held */ | ||
| 826 | RGFWDEF b8 RGFW_isMouseHeld(RGFW_window* win, u8 button /*!< mouse button code */ ); | ||
| 827 | /*! if a mouse button was released */ | ||
| 828 | RGFWDEF b8 RGFW_isMouseReleased(RGFW_window* win, u8 button /*!< mouse button code */ ); | ||
| 829 | /*! if a mouse button was pressed (checks previous state only) */ | ||
| 830 | RGFWDEF b8 RGFW_wasMousePressed(RGFW_window* win, u8 button /*!< mouse button code */ ); | ||
| 831 | /** @} */ | ||
| 832 | |||
| 833 | /** * @defgroup Clipboard | ||
| 834 | * @{ */ | ||
| 835 | RGFWDEF char* RGFW_readClipboard(size_t* size); /*!< read clipboard data */ | ||
| 836 | RGFWDEF void RGFW_clipboardFree(char* str); /*!< the string returned from RGFW_readClipboard must be freed */ | ||
| 837 | |||
| 838 | RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); /*!< write text to the clipboard */ | ||
| 839 | /** @} */ | ||
| 840 | |||
| 841 | /** | ||
| 842 | |||
| 843 | |||
| 844 | Event callbacks, | ||
| 845 | these are completely optional, you can use the normal | ||
| 846 | RGFW_checkEvent() method if you prefer that | ||
| 847 | |||
| 848 | * @defgroup Callbacks | ||
| 849 | * @{ | ||
| 850 | */ | ||
| 851 | |||
| 852 | /*! RGFW_windowMoved, the window and its new rect value */ | ||
| 853 | typedef void (* RGFW_windowmovefunc)(RGFW_window* win, RGFW_rect r); | ||
| 854 | /*! RGFW_windowResized, the window and its new rect value */ | ||
| 855 | typedef void (* RGFW_windowresizefunc)(RGFW_window* win, RGFW_rect r); | ||
| 856 | /*! RGFW_quit, the window that was closed */ | ||
| 857 | typedef void (* RGFW_windowquitfunc)(RGFW_window* win); | ||
| 858 | /*! RGFW_focusIn / RGFW_focusOut, the window who's focus has changed and if its inFocus */ | ||
| 859 | typedef void (* RGFW_focusfunc)(RGFW_window* win, b8 inFocus); | ||
| 860 | /*! RGFW_mouseEnter / RGFW_mouseLeave, the window that changed, the point of the mouse (enter only) and if the mouse has entered */ | ||
| 861 | typedef void (* RGFW_mouseNotifyfunc)(RGFW_window* win, RGFW_point point, b8 status); | ||
| 862 | /*! RGFW_mousePosChanged, the window that the move happened on and the new point of the mouse */ | ||
| 863 | typedef void (* RGFW_mouseposfunc)(RGFW_window* win, RGFW_point point); | ||
| 864 | /*! RGFW_dnd_init, the window, the point of the drop on the windows */ | ||
| 865 | typedef void (* RGFW_dndInitfunc)(RGFW_window* win, RGFW_point point); | ||
| 866 | /*! RGFW_windowRefresh, the window that needs to be refreshed */ | ||
| 867 | typedef void (* RGFW_windowrefreshfunc)(RGFW_window* win); | ||
| 868 | /*! RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the keycode, the string version, the state of mod keys, if it was a press (else it's a release) */ | ||
| 869 | typedef void (* RGFW_keyfunc)(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed); | ||
| 870 | /*! RGFW_mouseButtonPressed / RGFW_mouseButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */ | ||
| 871 | typedef void (* RGFW_mousebuttonfunc)(RGFW_window* win, u8 button, double scroll, b8 pressed); | ||
| 872 | /*! RGFW_jsButtonPressed / RGFW_jsButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */ | ||
| 873 | typedef void (* RGFW_jsButtonfunc)(RGFW_window* win, u16 joystick, u8 button, b8 pressed); | ||
| 874 | /*! RGFW_jsAxisMove, the window that got the event, the joystick in question, the axis values and the amount of axises */ | ||
| 875 | typedef void (* RGFW_jsAxisfunc)(RGFW_window* win, u16 joystick, RGFW_point axis[2], u8 axisesCount); | ||
| 876 | |||
| 877 | |||
| 878 | /*! RGFW_dnd, the window that had the drop, the drop data and the amount files dropped returns previous callback function (if it was set) */ | ||
| 879 | #ifdef RGFW_ALLOC_DROPFILES | ||
| 880 | typedef void (* RGFW_dndfunc)(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount); | ||
| 881 | #else | ||
| 882 | typedef void (* RGFW_dndfunc)(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount); | ||
| 883 | #endif | ||
| 884 | /*! set callback for a window move event returns previous callback function (if it was set) */ | ||
| 885 | RGFWDEF RGFW_windowmovefunc RGFW_setWindowMoveCallback(RGFW_windowmovefunc func); | ||
| 886 | /*! set callback for a window resize event returns previous callback function (if it was set) */ | ||
| 887 | RGFWDEF RGFW_windowresizefunc RGFW_setWindowResizeCallback(RGFW_windowresizefunc func); | ||
| 888 | /*! set callback for a window quit event returns previous callback function (if it was set) */ | ||
| 889 | RGFWDEF RGFW_windowquitfunc RGFW_setWindowQuitCallback(RGFW_windowquitfunc func); | ||
| 890 | /*! set callback for a mouse move event returns previous callback function (if it was set) */ | ||
| 891 | RGFWDEF RGFW_mouseposfunc RGFW_setMousePosCallback(RGFW_mouseposfunc func); | ||
| 892 | /*! set callback for a window refresh event returns previous callback function (if it was set) */ | ||
| 893 | RGFWDEF RGFW_windowrefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func); | ||
| 894 | /*! set callback for a window focus change event returns previous callback function (if it was set) */ | ||
| 895 | RGFWDEF RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func); | ||
| 896 | /*! set callback for a mouse notify event returns previous callback function (if it was set) */ | ||
| 897 | RGFWDEF RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func); | ||
| 898 | /*! set callback for a drop event event returns previous callback function (if it was set) */ | ||
| 899 | RGFWDEF RGFW_dndfunc RGFW_setDndCallback(RGFW_dndfunc func); | ||
| 900 | /*! set callback for a start of a drop event returns previous callback function (if it was set) */ | ||
| 901 | RGFWDEF RGFW_dndInitfunc RGFW_setDndInitCallback(RGFW_dndInitfunc func); | ||
| 902 | /*! set callback for a key (press / release ) event returns previous callback function (if it was set) */ | ||
| 903 | RGFWDEF RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func); | ||
| 904 | /*! set callback for a mouse button (press / release ) event returns previous callback function (if it was set) */ | ||
| 905 | RGFWDEF RGFW_mousebuttonfunc RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func); | ||
| 906 | /*! set callback for a controller button (press / release ) event returns previous callback function (if it was set) */ | ||
| 907 | RGFWDEF RGFW_jsButtonfunc RGFW_setjsButtonCallback(RGFW_jsButtonfunc func); | ||
| 908 | /*! set callback for a joystick axis mov event returns previous callback function (if it was set) */ | ||
| 909 | RGFWDEF RGFW_jsAxisfunc RGFW_setjsAxisCallback(RGFW_jsAxisfunc func); | ||
| 910 | |||
| 911 | /** @} */ | ||
| 912 | |||
| 913 | /** * @defgroup Threads | ||
| 914 | * @{ */ | ||
| 915 | |||
| 916 | #ifndef RGFW_NO_THREADS | ||
| 917 | /*! threading functions*/ | ||
| 918 | |||
| 919 | /*! NOTE! (for X11/linux) : if you define a window in a thread, it must be run after the original thread's window is created or else there will be a memory error */ | ||
| 920 | /* | ||
| 921 | I'd suggest you use sili's threading functions instead | ||
| 922 | if you're going to use sili | ||
| 923 | which is a good idea generally | ||
| 924 | */ | ||
| 925 | |||
| 926 | #if defined(__unix__) || defined(__APPLE__) || defined(RGFW_WEBASM) | ||
| 927 | typedef void* (* RGFW_threadFunc_ptr)(void*); | ||
| 928 | #else | ||
| 929 | typedef DWORD (__stdcall *RGFW_threadFunc_ptr) (LPVOID lpThreadParameter); | ||
| 930 | #endif | ||
| 931 | |||
| 932 | RGFWDEF RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args); /*!< create a thread*/ | ||
| 933 | RGFWDEF void RGFW_cancelThread(RGFW_thread thread); /*!< cancels a thread*/ | ||
| 934 | RGFWDEF void RGFW_joinThread(RGFW_thread thread); /*!< join thread to current thread */ | ||
| 935 | RGFWDEF void RGFW_setThreadPriority(RGFW_thread thread, u8 priority); /*!< sets the priority priority */ | ||
| 936 | #endif | ||
| 937 | |||
| 938 | /** @} */ | ||
| 939 | |||
| 940 | /** * @defgroup joystick | ||
| 941 | * @{ */ | ||
| 942 | |||
| 943 | /*! joystick count starts at 0*/ | ||
| 944 | /*!< register joystick to window based on a number (the number is based on when it was connected eg. /dev/js0)*/ | ||
| 945 | RGFWDEF u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber); | ||
| 946 | RGFWDEF u16 RGFW_registerJoystickF(RGFW_window* win, char* file); | ||
| 947 | |||
| 948 | RGFWDEF u32 RGFW_isPressedJS(RGFW_window* win, u16 controller, u8 button); | ||
| 949 | |||
| 950 | /** @} */ | ||
| 951 | |||
| 952 | /** * @defgroup graphics_API | ||
| 953 | * @{ */ | ||
| 954 | |||
| 955 | /*!< make the window the current opengl drawing context | ||
| 956 | |||
| 957 | NOTE: | ||
| 958 | if you want to switch the graphics context's thread, | ||
| 959 | you have to run RGFW_window_makeCurrent(NULL); on the old thread | ||
| 960 | then RGFW_window_makeCurrent(valid_window) on the new thread | ||
| 961 | */ | ||
| 962 | RGFWDEF void RGFW_window_makeCurrent(RGFW_window* win); | ||
| 963 | |||
| 964 | /*< updates fps / sets fps to cap (must by ran manually by the user at the end of a frame), returns current fps */ | ||
| 965 | RGFWDEF u32 RGFW_window_checkFPS(RGFW_window* win, u32 fpsCap); | ||
| 966 | |||
| 967 | /* supports openGL, directX, OSMesa, EGL and software rendering */ | ||
| 968 | RGFWDEF void RGFW_window_swapBuffers(RGFW_window* win); /*!< swap the rendering buffer */ | ||
| 969 | RGFWDEF void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval); | ||
| 970 | |||
| 971 | RGFWDEF void RGFW_window_setGPURender(RGFW_window* win, i8 set); | ||
| 972 | RGFWDEF void RGFW_window_setCPURender(RGFW_window* win, i8 set); | ||
| 973 | |||
| 974 | /*! native API functions */ | ||
| 975 | #if defined(RGFW_OPENGL) || defined(RGFW_EGL) | ||
| 976 | /*! OpenGL init hints */ | ||
| 977 | RGFWDEF void RGFW_setGLStencil(i32 stencil); /*!< set stencil buffer bit size (8 by default) */ | ||
| 978 | RGFWDEF void RGFW_setGLSamples(i32 samples); /*!< set number of sampiling buffers (4 by default) */ | ||
| 979 | RGFWDEF void RGFW_setGLStereo(i32 stereo); /*!< use GL_STEREO (GL_FALSE by default) */ | ||
| 980 | RGFWDEF void RGFW_setGLAuxBuffers(i32 auxBuffers); /*!< number of aux buffers (0 by default) */ | ||
| 981 | |||
| 982 | /*! which profile to use for the opengl verion */ | ||
| 983 | typedef RGFW_ENUM(u8, RGFW_GL_profile) { RGFW_GL_CORE = 0, RGFW_GL_COMPATIBILITY }; | ||
| 984 | /*! Set OpenGL version hint (core or compatibility profile)*/ | ||
| 985 | RGFWDEF void RGFW_setGLVersion(RGFW_GL_profile profile, i32 major, i32 minor); | ||
| 986 | RGFWDEF void RGFW_setDoubleBuffer(b8 useDoubleBuffer); | ||
| 987 | RGFWDEF void* RGFW_getProcAddress(const char* procname); /*!< get native opengl proc address */ | ||
| 988 | RGFWDEF void RGFW_window_makeCurrent_OpenGL(RGFW_window* win); /*!< to be called by RGFW_window_makeCurrent */ | ||
| 989 | #elif defined(RGFW_DIRECTX) | ||
| 990 | typedef struct { | ||
| 991 | IDXGIFactory* pFactory; | ||
| 992 | IDXGIAdapter* pAdapter; | ||
| 993 | ID3D11Device* pDevice; | ||
| 994 | ID3D11DeviceContext* pDeviceContext; | ||
| 995 | } RGFW_directXinfo; | ||
| 996 | |||
| 997 | /* | ||
| 998 | RGFW stores a global instance of RGFW_directXinfo, | ||
| 999 | you can use this function to get a pointer the instance | ||
| 1000 | */ | ||
| 1001 | RGFWDEF RGFW_directXinfo* RGFW_getDirectXInfo(void); | ||
| 1002 | #endif | ||
| 1003 | |||
| 1004 | /** @} */ | ||
| 1005 | |||
| 1006 | /** * @defgroup Supporting | ||
| 1007 | * @{ */ | ||
| 1008 | RGFWDEF u64 RGFW_getTime(void); /*!< get time in seconds */ | ||
| 1009 | RGFWDEF u64 RGFW_getTimeNS(void); /*!< get time in nanoseconds */ | ||
| 1010 | RGFWDEF void RGFW_sleep(u64 milisecond); /*!< sleep for a set time */ | ||
| 1011 | |||
| 1012 | /*! | ||
| 1013 | key codes and mouse icon enums | ||
| 1014 | */ | ||
| 1015 | |||
| 1016 | typedef RGFW_ENUM(u8, RGFW_Key) { | ||
| 1017 | RGFW_KEY_NULL = 0, | ||
| 1018 | RGFW_Escape, | ||
| 1019 | RGFW_F1, | ||
| 1020 | RGFW_F2, | ||
| 1021 | RGFW_F3, | ||
| 1022 | RGFW_F4, | ||
| 1023 | RGFW_F5, | ||
| 1024 | RGFW_F6, | ||
| 1025 | RGFW_F7, | ||
| 1026 | RGFW_F8, | ||
| 1027 | RGFW_F9, | ||
| 1028 | RGFW_F10, | ||
| 1029 | RGFW_F11, | ||
| 1030 | RGFW_F12, | ||
| 1031 | |||
| 1032 | RGFW_Backtick, | ||
| 1033 | |||
| 1034 | RGFW_0, | ||
| 1035 | RGFW_1, | ||
| 1036 | RGFW_2, | ||
| 1037 | RGFW_3, | ||
| 1038 | RGFW_4, | ||
| 1039 | RGFW_5, | ||
| 1040 | RGFW_6, | ||
| 1041 | RGFW_7, | ||
| 1042 | RGFW_8, | ||
| 1043 | RGFW_9, | ||
| 1044 | |||
| 1045 | RGFW_Minus, | ||
| 1046 | RGFW_Equals, | ||
| 1047 | RGFW_BackSpace, | ||
| 1048 | RGFW_Tab, | ||
| 1049 | RGFW_CapsLock, | ||
| 1050 | RGFW_ShiftL, | ||
| 1051 | RGFW_ControlL, | ||
| 1052 | RGFW_AltL, | ||
| 1053 | RGFW_SuperL, | ||
| 1054 | RGFW_ShiftR, | ||
| 1055 | RGFW_ControlR, | ||
| 1056 | RGFW_AltR, | ||
| 1057 | RGFW_SuperR, | ||
| 1058 | RGFW_Space, | ||
| 1059 | |||
| 1060 | RGFW_a, | ||
| 1061 | RGFW_b, | ||
| 1062 | RGFW_c, | ||
| 1063 | RGFW_d, | ||
| 1064 | RGFW_e, | ||
| 1065 | RGFW_f, | ||
| 1066 | RGFW_g, | ||
| 1067 | RGFW_h, | ||
| 1068 | RGFW_i, | ||
| 1069 | RGFW_j, | ||
| 1070 | RGFW_k, | ||
| 1071 | RGFW_l, | ||
| 1072 | RGFW_m, | ||
| 1073 | RGFW_n, | ||
| 1074 | RGFW_o, | ||
| 1075 | RGFW_p, | ||
| 1076 | RGFW_q, | ||
| 1077 | RGFW_r, | ||
| 1078 | RGFW_s, | ||
| 1079 | RGFW_t, | ||
| 1080 | RGFW_u, | ||
| 1081 | RGFW_v, | ||
| 1082 | RGFW_w, | ||
| 1083 | RGFW_x, | ||
| 1084 | RGFW_y, | ||
| 1085 | RGFW_z, | ||
| 1086 | |||
| 1087 | RGFW_Period, | ||
| 1088 | RGFW_Comma, | ||
| 1089 | RGFW_Slash, | ||
| 1090 | RGFW_Bracket, | ||
| 1091 | RGFW_CloseBracket, | ||
| 1092 | RGFW_Semicolon, | ||
| 1093 | RGFW_Return, | ||
| 1094 | RGFW_Quote, | ||
| 1095 | RGFW_BackSlash, | ||
| 1096 | |||
| 1097 | RGFW_Up, | ||
| 1098 | RGFW_Down, | ||
| 1099 | RGFW_Left, | ||
| 1100 | RGFW_Right, | ||
| 1101 | |||
| 1102 | RGFW_Delete, | ||
| 1103 | RGFW_Insert, | ||
| 1104 | RGFW_End, | ||
| 1105 | RGFW_Home, | ||
| 1106 | RGFW_PageUp, | ||
| 1107 | RGFW_PageDown, | ||
| 1108 | |||
| 1109 | RGFW_Numlock, | ||
| 1110 | RGFW_KP_Slash, | ||
| 1111 | RGFW_Multiply, | ||
| 1112 | RGFW_KP_Minus, | ||
| 1113 | RGFW_KP_1, | ||
| 1114 | RGFW_KP_2, | ||
| 1115 | RGFW_KP_3, | ||
| 1116 | RGFW_KP_4, | ||
| 1117 | RGFW_KP_5, | ||
| 1118 | RGFW_KP_6, | ||
| 1119 | RGFW_KP_7, | ||
| 1120 | RGFW_KP_8, | ||
| 1121 | RGFW_KP_9, | ||
| 1122 | RGFW_KP_0, | ||
| 1123 | RGFW_KP_Period, | ||
| 1124 | RGFW_KP_Return, | ||
| 1125 | |||
| 1126 | final_key, | ||
| 1127 | }; | ||
| 1128 | |||
| 1129 | |||
| 1130 | typedef RGFW_ENUM(u8, RGFW_mouseIcons) { | ||
| 1131 | RGFW_MOUSE_NORMAL = 0, | ||
| 1132 | RGFW_MOUSE_ARROW, | ||
| 1133 | RGFW_MOUSE_IBEAM, | ||
| 1134 | RGFW_MOUSE_CROSSHAIR, | ||
| 1135 | RGFW_MOUSE_POINTING_HAND, | ||
| 1136 | RGFW_MOUSE_RESIZE_EW, | ||
| 1137 | RGFW_MOUSE_RESIZE_NS, | ||
| 1138 | RGFW_MOUSE_RESIZE_NWSE, | ||
| 1139 | RGFW_MOUSE_RESIZE_NESW, | ||
| 1140 | RGFW_MOUSE_RESIZE_ALL, | ||
| 1141 | RGFW_MOUSE_NOT_ALLOWED, | ||
| 1142 | }; | ||
| 1143 | |||
| 1144 | /** @} */ | ||
| 1145 | |||
| 1146 | #endif /* RGFW_HEADER */ | ||
| 1147 | |||
| 1148 | /* | ||
| 1149 | Example to get you started : | ||
| 1150 | |||
| 1151 | linux : gcc main.c -lX11 -lXcursor -lGL | ||
| 1152 | windows : gcc main.c -lopengl32 -lshell32 -lgdi32 | ||
| 1153 | macos : gcc main.c -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo | ||
| 1154 | |||
| 1155 | #define RGFW_IMPLEMENTATION | ||
| 1156 | #include "RGFW.h" | ||
| 1157 | |||
| 1158 | u8 icon[4 * 3 * 3] = {0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF}; | ||
| 1159 | |||
| 1160 | int main() { | ||
| 1161 | RGFW_window* win = RGFW_createWindow("name", RGFW_RECT(500, 500, 500, 500), (u64)0); | ||
| 1162 | |||
| 1163 | RGFW_window_setIcon(win, icon, RGFW_AREA(3, 3), 4); | ||
| 1164 | |||
| 1165 | for (;;) { | ||
| 1166 | RGFW_window_checkEvent(win); // NOTE: checking events outside of a while loop may cause input lag | ||
| 1167 | if (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_Escape)) | ||
| 1168 | break; | ||
| 1169 | |||
| 1170 | RGFW_window_swapBuffers(win); | ||
| 1171 | |||
| 1172 | glClearColor(0xFF, 0XFF, 0xFF, 0xFF); | ||
| 1173 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | RGFW_window_close(win); | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | compiling : | ||
| 1180 | |||
| 1181 | if you wish to compile the library all you have to do is create a new file with this in it | ||
| 1182 | |||
| 1183 | rgfw.c | ||
| 1184 | #define RGFW_IMPLEMENTATION | ||
| 1185 | #include "RGFW.h" | ||
| 1186 | |||
| 1187 | then you can use gcc (or whatever compile you wish to use) to compile the library into object file | ||
| 1188 | |||
| 1189 | ex. gcc -c RGFW.c -fPIC | ||
| 1190 | |||
| 1191 | after you compile the library into an object file, you can also turn the object file into an static or shared library | ||
| 1192 | |||
| 1193 | (commands ar and gcc can be replaced with whatever equivalent your system uses) | ||
| 1194 | static : ar rcs RGFW.a RGFW.o | ||
| 1195 | shared : | ||
| 1196 | windows: | ||
| 1197 | gcc -shared RGFW.o -lwinmm -lopengl32 -lshell32 -lgdi32 -o RGFW.dll | ||
| 1198 | linux: | ||
| 1199 | gcc -shared RGFW.o -lX11 -lXcursor -lGL -lXrandr -o RGFW.so | ||
| 1200 | macos: | ||
| 1201 | gcc -shared RGFW.o -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo | ||
| 1202 | */ | ||
| 1203 | |||
| 1204 | #ifdef RGFW_X11 | ||
| 1205 | #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) l | ||
| 1206 | #elif defined(RGFW_WINDOWS) | ||
| 1207 | #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) w | ||
| 1208 | #elif defined(RGFW_MACOS) | ||
| 1209 | #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) m | ||
| 1210 | #elif defined(RGFW_WEBASM) | ||
| 1211 | #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) h | ||
| 1212 | #elif defined(RGFW_WAYLAND) | ||
| 1213 | #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) ww | ||
| 1214 | #endif | ||
| 1215 | |||
| 1216 | |||
| 1217 | #ifdef RGFW_IMPLEMENTATION | ||
| 1218 | |||
| 1219 | #include <stdio.h> | ||
| 1220 | #include <string.h> | ||
| 1221 | #include <math.h> | ||
| 1222 | #include <assert.h> | ||
| 1223 | |||
| 1224 | /* | ||
| 1225 | RGFW_IMPLEMENTATION starts with generic RGFW defines | ||
| 1226 | |||
| 1227 | This is the start of keycode data | ||
| 1228 | |||
| 1229 | Why not use macros instead of the numbers itself? | ||
| 1230 | Windows -> Not all virtual keys are macros (VK_0 - VK_1, VK_a - VK_z) | ||
| 1231 | Linux -> Only symcodes are values, (XK_0 - XK_1, XK_a - XK_z) are larger than 0xFF00, I can't find any way to work with them without making the array an unreasonable size | ||
| 1232 | MacOS -> windows and linux already don't have keycodes as macros, so there's no point | ||
| 1233 | */ | ||
| 1234 | |||
| 1235 | |||
| 1236 | |||
| 1237 | /* | ||
| 1238 | the c++ compiler doesn't support setting up an array like, | ||
| 1239 | we'll have to do it during runtime using a function & this messy setup | ||
| 1240 | */ | ||
| 1241 | #ifndef __cplusplus | ||
| 1242 | #define RGFW_NEXT , | ||
| 1243 | #define RGFW_MAP | ||
| 1244 | #else | ||
| 1245 | #define RGFW_NEXT ; | ||
| 1246 | #define RGFW_MAP RGFW_keycodes | ||
| 1247 | #endif | ||
| 1248 | |||
| 1249 | #ifdef RGFW_WAYLAND | ||
| 1250 | #include <linux/input-event-codes.h> | ||
| 1251 | #endif | ||
| 1252 | |||
| 1253 | u8 RGFW_keycodes [RGFW_OS_BASED_VALUE(136, 337, 128, DOM_VK_WIN_OEM_CLEAR + 1, 130)] = { | ||
| 1254 | #ifdef __cplusplus | ||
| 1255 | 0 | ||
| 1256 | }; | ||
| 1257 | void RGFW_init_keys(void) { | ||
| 1258 | #endif | ||
| 1259 | RGFW_MAP [RGFW_OS_BASED_VALUE(49, 192, 50, DOM_VK_BACK_QUOTE, KEY_GRAVE)] = RGFW_Backtick RGFW_NEXT | ||
| 1260 | |||
| 1261 | RGFW_MAP [RGFW_OS_BASED_VALUE(19, 0x30, 29, DOM_VK_0, KEY_0)] = RGFW_0 RGFW_NEXT | ||
| 1262 | RGFW_MAP [RGFW_OS_BASED_VALUE(10, 0x31, 18, DOM_VK_1, KEY_1)] = RGFW_1 RGFW_NEXT | ||
| 1263 | RGFW_MAP [RGFW_OS_BASED_VALUE(11, 0x32, 19, DOM_VK_2, KEY_2)] = RGFW_2 RGFW_NEXT | ||
| 1264 | RGFW_MAP [RGFW_OS_BASED_VALUE(12, 0x33, 20, DOM_VK_3, KEY_3)] = RGFW_3 RGFW_NEXT | ||
| 1265 | RGFW_MAP [RGFW_OS_BASED_VALUE(13, 0x34, 21, DOM_VK_4, KEY_4)] = RGFW_4 RGFW_NEXT | ||
| 1266 | RGFW_MAP [RGFW_OS_BASED_VALUE(14, 0x35, 23, DOM_VK_5, KEY_5)] = RGFW_5 RGFW_NEXT | ||
| 1267 | RGFW_MAP [RGFW_OS_BASED_VALUE(15, 0x36, 22, DOM_VK_6, KEY_6)] = RGFW_6 RGFW_NEXT | ||
| 1268 | RGFW_MAP [RGFW_OS_BASED_VALUE(16, 0x37, 26, DOM_VK_7, KEY_7)] = RGFW_7 RGFW_NEXT | ||
| 1269 | RGFW_MAP [RGFW_OS_BASED_VALUE(17, 0x38, 28, DOM_VK_8, KEY_8)] = RGFW_8 RGFW_NEXT | ||
| 1270 | RGFW_MAP [RGFW_OS_BASED_VALUE(18, 0x39, 25, DOM_VK_9, KEY_9)] = RGFW_9, | ||
| 1271 | |||
| 1272 | RGFW_MAP [RGFW_OS_BASED_VALUE(65, 0x20, 49, DOM_VK_SPACE, KEY_SPACE)] = RGFW_Space, | ||
| 1273 | |||
| 1274 | RGFW_MAP [RGFW_OS_BASED_VALUE(38, 0x41, 0, DOM_VK_A, KEY_A)] = RGFW_a RGFW_NEXT | ||
| 1275 | RGFW_MAP [RGFW_OS_BASED_VALUE(56, 0x42, 11, DOM_VK_B, KEY_B)] = RGFW_b RGFW_NEXT | ||
| 1276 | RGFW_MAP [RGFW_OS_BASED_VALUE(54, 0x43, 8, DOM_VK_C, KEY_C)] = RGFW_c RGFW_NEXT | ||
| 1277 | RGFW_MAP [RGFW_OS_BASED_VALUE(40, 0x44, 2, DOM_VK_D, KEY_D)] = RGFW_d RGFW_NEXT | ||
| 1278 | RGFW_MAP [RGFW_OS_BASED_VALUE(26, 0x45, 14, DOM_VK_E, KEY_E)] = RGFW_e RGFW_NEXT | ||
| 1279 | RGFW_MAP [RGFW_OS_BASED_VALUE(41, 0x46, 3, DOM_VK_F, KEY_F)] = RGFW_f RGFW_NEXT | ||
| 1280 | RGFW_MAP [RGFW_OS_BASED_VALUE(42, 0x47, 5, DOM_VK_G, KEY_G)] = RGFW_g RGFW_NEXT | ||
| 1281 | RGFW_MAP [RGFW_OS_BASED_VALUE(43, 0x48, 4, DOM_VK_H, KEY_H)] = RGFW_h RGFW_NEXT | ||
| 1282 | RGFW_MAP [RGFW_OS_BASED_VALUE(31, 0x49, 34, DOM_VK_I, KEY_I)] = RGFW_i RGFW_NEXT | ||
| 1283 | RGFW_MAP [RGFW_OS_BASED_VALUE(44, 0x4A, 38, DOM_VK_J, KEY_J)] = RGFW_j RGFW_NEXT | ||
| 1284 | RGFW_MAP [RGFW_OS_BASED_VALUE(45, 0x4B, 40, DOM_VK_K, KEY_K)] = RGFW_k RGFW_NEXT | ||
| 1285 | RGFW_MAP [RGFW_OS_BASED_VALUE(46, 0x4C, 37, DOM_VK_L, KEY_L)] = RGFW_l RGFW_NEXT | ||
| 1286 | RGFW_MAP [RGFW_OS_BASED_VALUE(58, 0x4D, 46, DOM_VK_M, KEY_M)] = RGFW_m RGFW_NEXT | ||
| 1287 | RGFW_MAP [RGFW_OS_BASED_VALUE(57, 0x4E, 45, DOM_VK_N, KEY_N)] = RGFW_n RGFW_NEXT | ||
| 1288 | RGFW_MAP [RGFW_OS_BASED_VALUE(32, 0x4F, 31, DOM_VK_O, KEY_O)] = RGFW_o RGFW_NEXT | ||
| 1289 | RGFW_MAP [RGFW_OS_BASED_VALUE(33, 0x50, 35, DOM_VK_P, KEY_P)] = RGFW_p RGFW_NEXT | ||
| 1290 | RGFW_MAP [RGFW_OS_BASED_VALUE(24, 0x51, 12, DOM_VK_Q, KEY_Q)] = RGFW_q RGFW_NEXT | ||
| 1291 | RGFW_MAP [RGFW_OS_BASED_VALUE(27, 0x52, 15, DOM_VK_R, KEY_R)] = RGFW_r RGFW_NEXT | ||
| 1292 | RGFW_MAP [RGFW_OS_BASED_VALUE(39, 0x53, 1, DOM_VK_S, KEY_S)] = RGFW_s RGFW_NEXT | ||
| 1293 | RGFW_MAP [RGFW_OS_BASED_VALUE(28, 0x54, 17, DOM_VK_T, KEY_T)] = RGFW_t RGFW_NEXT | ||
| 1294 | RGFW_MAP [RGFW_OS_BASED_VALUE(30, 0x55, 32, DOM_VK_U, KEY_U)] = RGFW_u RGFW_NEXT | ||
| 1295 | RGFW_MAP [RGFW_OS_BASED_VALUE(55, 0x56, 9, DOM_VK_V, KEY_V)] = RGFW_v RGFW_NEXT | ||
| 1296 | RGFW_MAP [RGFW_OS_BASED_VALUE(25, 0x57, 13, DOM_VK_W, KEY_W)] = RGFW_w RGFW_NEXT | ||
| 1297 | RGFW_MAP [RGFW_OS_BASED_VALUE(53, 0x58, 7, DOM_VK_X, KEY_X)] = RGFW_x RGFW_NEXT | ||
| 1298 | RGFW_MAP [RGFW_OS_BASED_VALUE(29, 0x59, 16, DOM_VK_Y, KEY_Y)] = RGFW_y RGFW_NEXT | ||
| 1299 | RGFW_MAP [RGFW_OS_BASED_VALUE(52, 0x5A, 6, DOM_VK_Z, KEY_Z)] = RGFW_z, | ||
| 1300 | |||
| 1301 | RGFW_MAP [RGFW_OS_BASED_VALUE(60, 190, 47, DOM_VK_PERIOD, KEY_DOT)] = RGFW_Period RGFW_NEXT | ||
| 1302 | RGFW_MAP [RGFW_OS_BASED_VALUE(59, 188, 43, DOM_VK_COMMA, KEY_COMMA)] = RGFW_Comma RGFW_NEXT | ||
| 1303 | RGFW_MAP [RGFW_OS_BASED_VALUE(61, 191, 44, DOM_VK_SLASH, KEY_SLASH)] = RGFW_Slash RGFW_NEXT | ||
| 1304 | RGFW_MAP [RGFW_OS_BASED_VALUE(34, 219, 33, DOM_VK_OPEN_BRACKET, KEY_LEFTBRACE)] = RGFW_Bracket RGFW_NEXT | ||
| 1305 | RGFW_MAP [RGFW_OS_BASED_VALUE(35, 221, 30, DOM_VK_CLOSE_BRACKET, KEY_RIGHTBRACE)] = RGFW_CloseBracket RGFW_NEXT | ||
| 1306 | RGFW_MAP [RGFW_OS_BASED_VALUE(47, 186, 41, DOM_VK_SEMICOLON, KEY_SEMICOLON)] = RGFW_Semicolon RGFW_NEXT | ||
| 1307 | RGFW_MAP [RGFW_OS_BASED_VALUE(48, 222, 39, DOM_VK_QUOTE, KEY_APOSTROPHE)] = RGFW_Quote RGFW_NEXT | ||
| 1308 | RGFW_MAP [RGFW_OS_BASED_VALUE(51, 322, 42, DOM_VK_BACK_SLASH, KEY_BACKSLASH)] = RGFW_BackSlash, | ||
| 1309 | |||
| 1310 | RGFW_MAP [RGFW_OS_BASED_VALUE(36, 0x0D, 36, DOM_VK_RETURN, KEY_ENTER)] = RGFW_Return RGFW_NEXT | ||
| 1311 | RGFW_MAP [RGFW_OS_BASED_VALUE(119, 0x2E, 118, DOM_VK_DELETE, KEY_DELETE)] = RGFW_Delete RGFW_NEXT | ||
| 1312 | RGFW_MAP [RGFW_OS_BASED_VALUE(77, 0x90, 72, DOM_VK_NUM_LOCK, KEY_NUMLOCK)] = RGFW_Numlock RGFW_NEXT | ||
| 1313 | RGFW_MAP [RGFW_OS_BASED_VALUE(106, 0x6F, 82, DOM_VK_DIVIDE, KEY_KPSLASH)] = RGFW_KP_Slash RGFW_NEXT | ||
| 1314 | RGFW_MAP [RGFW_OS_BASED_VALUE(63, 0x6A, 76, DOM_VK_MULTIPLY, KEY_KPASTERISK)] = RGFW_Multiply RGFW_NEXT | ||
| 1315 | RGFW_MAP [RGFW_OS_BASED_VALUE(82, 0x6D, 67, DOM_VK_SUBTRACT, KEY_KPMINUS)] = RGFW_KP_Minus RGFW_NEXT | ||
| 1316 | RGFW_MAP [RGFW_OS_BASED_VALUE(87, 0x61, 84, DOM_VK_NUMPAD1, KEY_KP1)] = RGFW_KP_1 RGFW_NEXT | ||
| 1317 | RGFW_MAP [RGFW_OS_BASED_VALUE(88, 0x62, 85, DOM_VK_NUMPAD2, KEY_KP2)] = RGFW_KP_2 RGFW_NEXT | ||
| 1318 | RGFW_MAP [RGFW_OS_BASED_VALUE(89, 0x63, 86, DOM_VK_NUMPAD3, KEY_KP3)] = RGFW_KP_3 RGFW_NEXT | ||
| 1319 | RGFW_MAP [RGFW_OS_BASED_VALUE(83, 0x64, 87, DOM_VK_NUMPAD4, KEY_KP4)] = RGFW_KP_4 RGFW_NEXT | ||
| 1320 | RGFW_MAP [RGFW_OS_BASED_VALUE(84, 0x65, 88, DOM_VK_NUMPAD5, KEY_KP5)] = RGFW_KP_5 RGFW_NEXT | ||
| 1321 | RGFW_MAP [RGFW_OS_BASED_VALUE(85, 0x66, 89, DOM_VK_NUMPAD6, KEY_KP6)] = RGFW_KP_6 RGFW_NEXT | ||
| 1322 | RGFW_MAP [RGFW_OS_BASED_VALUE(79, 0x67, 90, DOM_VK_NUMPAD7, KEY_KP7)] = RGFW_KP_7 RGFW_NEXT | ||
| 1323 | RGFW_MAP [RGFW_OS_BASED_VALUE(80, 0x68, 92, DOM_VK_NUMPAD8, KEY_KP8)] = RGFW_KP_8 RGFW_NEXT | ||
| 1324 | RGFW_MAP [RGFW_OS_BASED_VALUE(81, 0x69, 93, DOM_VK_NUMPAD9, KEY_KP9)] = RGFW_KP_9 RGFW_NEXT | ||
| 1325 | RGFW_MAP [RGFW_OS_BASED_VALUE(90, 0x60, 83, DOM_VK_NUMPAD0, KEY_KP0)] = RGFW_KP_0 RGFW_NEXT | ||
| 1326 | RGFW_MAP [RGFW_OS_BASED_VALUE(91, 0x6E, 65, DOM_VK_DECIMAL, KEY_KPDOT)] = RGFW_KP_Period RGFW_NEXT | ||
| 1327 | RGFW_MAP [RGFW_OS_BASED_VALUE(104, 0x92, 77, 0, KEY_KPENTER)] = RGFW_KP_Return, | ||
| 1328 | |||
| 1329 | RGFW_MAP [RGFW_OS_BASED_VALUE(20, 189, 27, DOM_VK_HYPHEN_MINUS, KEY_MINUS)] = RGFW_Minus RGFW_NEXT | ||
| 1330 | RGFW_MAP [RGFW_OS_BASED_VALUE(21, 187, 24, DOM_VK_EQUALS, KEY_EQUAL)] = RGFW_Equals RGFW_NEXT | ||
| 1331 | RGFW_MAP [RGFW_OS_BASED_VALUE(22, 8, 51, DOM_VK_BACK_SPACE, KEY_BACKSPACE)] = RGFW_BackSpace RGFW_NEXT | ||
| 1332 | RGFW_MAP [RGFW_OS_BASED_VALUE(23, 0x09, 48, DOM_VK_TAB, KEY_TAB)] = RGFW_Tab RGFW_NEXT | ||
| 1333 | RGFW_MAP [RGFW_OS_BASED_VALUE(66, 20, 57, DOM_VK_CAPS_LOCK, KEY_CAPSLOCK)] = RGFW_CapsLock RGFW_NEXT | ||
| 1334 | RGFW_MAP [RGFW_OS_BASED_VALUE(50, 0x10, 56, DOM_VK_SHIFT, KEY_LEFTSHIFT)] = RGFW_ShiftL RGFW_NEXT | ||
| 1335 | RGFW_MAP [RGFW_OS_BASED_VALUE(37, 0x11, 59, DOM_VK_CONTROL, KEY_LEFTCTRL)] = RGFW_ControlL RGFW_NEXT | ||
| 1336 | RGFW_MAP [RGFW_OS_BASED_VALUE(64,0x12, 58, DOM_VK_ALT, KEY_LEFTALT)] = RGFW_AltL RGFW_NEXT | ||
| 1337 | RGFW_MAP [RGFW_OS_BASED_VALUE(133, 0x5B, 55, DOM_VK_WIN, KEY_LEFTMETA)] = RGFW_SuperL, | ||
| 1338 | |||
| 1339 | #if !defined(RGFW_WINDOWS) && !defined(RGFW_MACOS) && !defined(RGFW_WEBASM) | ||
| 1340 | RGFW_MAP [RGFW_OS_BASED_VALUE(105, 0x11, 59, 0, KEY_RIGHTCTRL)] = RGFW_ControlR RGFW_NEXT | ||
| 1341 | RGFW_MAP [RGFW_OS_BASED_VALUE(135, 0xA4, 55, 0, KEY_RIGHTMETA)] = RGFW_SuperR, | ||
| 1342 | RGFW_MAP [RGFW_OS_BASED_VALUE(62, 0x5C, 56, 0, KEY_RIGHTSHIFT)] = RGFW_ShiftR RGFW_NEXT | ||
| 1343 | RGFW_MAP [RGFW_OS_BASED_VALUE(108, 165, 58, 0, KEY_RIGHTALT)] = RGFW_AltR, | ||
| 1344 | #endif | ||
| 1345 | |||
| 1346 | RGFW_MAP [RGFW_OS_BASED_VALUE(67, 0x70, 127, DOM_VK_F1, KEY_F1)] = RGFW_F1 RGFW_NEXT | ||
| 1347 | RGFW_MAP [RGFW_OS_BASED_VALUE(68, 0x71, 121, DOM_VK_F2, KEY_F2)] = RGFW_F2 RGFW_NEXT | ||
| 1348 | RGFW_MAP [RGFW_OS_BASED_VALUE(69, 0x72, 100, DOM_VK_F3, KEY_F3)] = RGFW_F3 RGFW_NEXT | ||
| 1349 | RGFW_MAP [RGFW_OS_BASED_VALUE(70, 0x73, 119, DOM_VK_F4, KEY_F4)] = RGFW_F4 RGFW_NEXT | ||
| 1350 | RGFW_MAP [RGFW_OS_BASED_VALUE(71, 0x74, 97, DOM_VK_F5, KEY_F5)] = RGFW_F5 RGFW_NEXT | ||
| 1351 | RGFW_MAP [RGFW_OS_BASED_VALUE(72, 0x75, 98, DOM_VK_F6, KEY_F6)] = RGFW_F6 RGFW_NEXT | ||
| 1352 | RGFW_MAP [RGFW_OS_BASED_VALUE(73, 0x76, 99, DOM_VK_F7, KEY_F7)] = RGFW_F7 RGFW_NEXT | ||
| 1353 | RGFW_MAP [RGFW_OS_BASED_VALUE(74, 0x77, 101, DOM_VK_F8, KEY_F8)] = RGFW_F8 RGFW_NEXT | ||
| 1354 | RGFW_MAP [RGFW_OS_BASED_VALUE(75, 0x78, 102, DOM_VK_F9, KEY_F9)] = RGFW_F9 RGFW_NEXT | ||
| 1355 | RGFW_MAP [RGFW_OS_BASED_VALUE(76, 0x79, 110, DOM_VK_F10, KEY_F10)] = RGFW_F10 RGFW_NEXT | ||
| 1356 | RGFW_MAP [RGFW_OS_BASED_VALUE(95, 0x7A, 104, DOM_VK_F11, KEY_F11)] = RGFW_F11 RGFW_NEXT | ||
| 1357 | RGFW_MAP [RGFW_OS_BASED_VALUE(96, 0x7B, 112, DOM_VK_F12, KEY_F12)] = RGFW_F12 RGFW_NEXT | ||
| 1358 | RGFW_MAP [RGFW_OS_BASED_VALUE(111, 0x26, 126, DOM_VK_UP, KEY_UP)] = RGFW_Up RGFW_NEXT | ||
| 1359 | RGFW_MAP [RGFW_OS_BASED_VALUE(116, 0x28, 125, DOM_VK_DOWN, KEY_DOWN)] = RGFW_Down RGFW_NEXT | ||
| 1360 | RGFW_MAP [RGFW_OS_BASED_VALUE(113, 0x25, 123, DOM_VK_LEFT, KEY_LEFT)] = RGFW_Left RGFW_NEXT | ||
| 1361 | RGFW_MAP [RGFW_OS_BASED_VALUE(114, 0x27, 124, DOM_VK_RIGHT, KEY_RIGHT)] = RGFW_Right RGFW_NEXT | ||
| 1362 | RGFW_MAP [RGFW_OS_BASED_VALUE(118, 0x2D, 115, DOM_VK_INSERT, KEY_INSERT)] = RGFW_Insert RGFW_NEXT | ||
| 1363 | RGFW_MAP [RGFW_OS_BASED_VALUE(115, 0x23, 120, DOM_VK_END, KEY_END)] = RGFW_End RGFW_NEXT | ||
| 1364 | RGFW_MAP [RGFW_OS_BASED_VALUE(112, 336, 117, DOM_VK_PAGE_UP, KEY_PAGEUP)] = RGFW_PageUp RGFW_NEXT | ||
| 1365 | RGFW_MAP [RGFW_OS_BASED_VALUE(117, 325, 122, DOM_VK_PAGE_DOWN, KEY_PAGEDOWN)] = RGFW_PageDown RGFW_NEXT | ||
| 1366 | RGFW_MAP [RGFW_OS_BASED_VALUE(9, 0x1B, 53, DOM_VK_ESCAPE, KEY_ESC)] = RGFW_Escape RGFW_NEXT | ||
| 1367 | RGFW_MAP [RGFW_OS_BASED_VALUE(110, 0x24, 116, DOM_VK_HOME, KEY_HOME)] = RGFW_Home RGFW_NEXT | ||
| 1368 | #ifndef __cplusplus | ||
| 1369 | }; | ||
| 1370 | #else | ||
| 1371 | } | ||
| 1372 | #endif | ||
| 1373 | |||
| 1374 | #undef RGFW_NEXT | ||
| 1375 | #undef RGFW_MAP | ||
| 1376 | |||
| 1377 | typedef struct { | ||
| 1378 | b8 current : 1; | ||
| 1379 | b8 prev : 1; | ||
| 1380 | } RGFW_keyState; | ||
| 1381 | |||
| 1382 | RGFW_keyState RGFW_keyboard[final_key] = { {0, 0} }; | ||
| 1383 | |||
| 1384 | RGFWDEF u32 RGFW_apiKeyCodeToRGFW(u32 keycode); | ||
| 1385 | |||
| 1386 | u32 RGFW_apiKeyCodeToRGFW(u32 keycode) { | ||
| 1387 | #ifdef __cplusplus | ||
| 1388 | if (RGFW_OS_BASED_VALUE(49, 192, 50, DOM_VK_BACK_QUOTE, KEY_GRAVE) != RGFW_Backtick) { | ||
| 1389 | RGFW_init_keys(); | ||
| 1390 | } | ||
| 1391 | #endif | ||
| 1392 | |||
| 1393 | /* make sure the key isn't out of bounds */ | ||
| 1394 | if (keycode > sizeof(RGFW_keycodes) / sizeof(u8)) | ||
| 1395 | return 0; | ||
| 1396 | |||
| 1397 | return RGFW_keycodes[keycode]; | ||
| 1398 | } | ||
| 1399 | |||
| 1400 | RGFWDEF void RGFW_resetKey(void); | ||
| 1401 | void RGFW_resetKey(void) { | ||
| 1402 | size_t len = final_key; /*!< last_key == length */ | ||
| 1403 | |||
| 1404 | size_t i; /*!< reset each previous state */ | ||
| 1405 | for (i = 0; i < len; i++) | ||
| 1406 | RGFW_keyboard[i].prev = 0; | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | b8 RGFW_shouldShift(u32 keycode, u8 lockState) { | ||
| 1410 | #define RGFW_xor(x, y) (( (x) && (!(y)) ) || ((y) && (!(x)) )) | ||
| 1411 | b8 caps4caps = (lockState & RGFW_CAPSLOCK) && ((keycode >= RGFW_a) && (keycode <= RGFW_z)); | ||
| 1412 | b8 shouldShift = RGFW_xor((RGFW_isPressed(NULL, RGFW_ShiftL) || RGFW_isPressed(NULL, RGFW_ShiftR)), caps4caps); | ||
| 1413 | #undef RGFW_xor | ||
| 1414 | |||
| 1415 | return shouldShift; | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | char RGFW_keyCodeToChar(u32 keycode, b8 shift) { | ||
| 1419 | static const char map[] = { | ||
| 1420 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '`', '0', '1', '2', '3', '4', '5', '6', '7', '8', | ||
| 1421 | '9', '-', '=', 0, '\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', | ||
| 1422 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '.', ',', '/', '[', ']', ';', '\n', '\'', '\\', | ||
| 1423 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', '*', '-', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '\n' | ||
| 1424 | }; | ||
| 1425 | |||
| 1426 | static const char mapCaps[] = { | ||
| 1427 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '~', ')', '!', '@', '#', '$', '%', '^', '&', '*', | ||
| 1428 | '(', '_', '+', 0, '0', 0, 0, 0, 0, 0, 0, 0, 0, 0, ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', | ||
| 1429 | 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', | ||
| 1430 | 'X', 'Y', 'Z', '>', '<', '?', '{', '}', ':', '\n', '"', '|', | ||
| 1431 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '?', '*', '-', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | ||
| 1432 | }; | ||
| 1433 | |||
| 1434 | if (shift == RGFW_FALSE) | ||
| 1435 | return map[keycode]; | ||
| 1436 | return mapCaps[keycode]; | ||
| 1437 | } | ||
| 1438 | |||
| 1439 | char RGFW_keyCodeToCharAuto(u32 keycode, u8 lockState) { return RGFW_keyCodeToChar(keycode, RGFW_shouldShift(keycode, lockState)); } | ||
| 1440 | |||
| 1441 | /* | ||
| 1442 | this is the end of keycode data | ||
| 1443 | */ | ||
| 1444 | |||
| 1445 | /* joystick data */ | ||
| 1446 | u8 RGFW_jsPressed[4][16]; /*!< if a key is currently pressed or not (per joystick) */ | ||
| 1447 | |||
| 1448 | i32 RGFW_joysticks[4]; /*!< limit of 4 joysticks at a time */ | ||
| 1449 | u16 RGFW_joystickCount; /*!< the actual amount of joysticks */ | ||
| 1450 | |||
| 1451 | /* | ||
| 1452 | event callback defines start here | ||
| 1453 | */ | ||
| 1454 | |||
| 1455 | |||
| 1456 | /* | ||
| 1457 | These exist to avoid the | ||
| 1458 | if (func == NULL) check | ||
| 1459 | for (allegedly) better performance | ||
| 1460 | */ | ||
| 1461 | void RGFW_windowmovefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } | ||
| 1462 | void RGFW_windowresizefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } | ||
| 1463 | void RGFW_windowquitfuncEMPTY(RGFW_window* win) { RGFW_UNUSED(win); } | ||
| 1464 | void RGFW_focusfuncEMPTY(RGFW_window* win, b8 inFocus) {RGFW_UNUSED(win); RGFW_UNUSED(inFocus);} | ||
| 1465 | void RGFW_mouseNotifyfuncEMPTY(RGFW_window* win, RGFW_point point, b8 status) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(status);} | ||
| 1466 | void RGFW_mouseposfuncEMPTY(RGFW_window* win, RGFW_point point) {RGFW_UNUSED(win); RGFW_UNUSED(point);} | ||
| 1467 | void RGFW_dndInitfuncEMPTY(RGFW_window* win, RGFW_point point) {RGFW_UNUSED(win); RGFW_UNUSED(point);} | ||
| 1468 | void RGFW_windowrefreshfuncEMPTY(RGFW_window* win) {RGFW_UNUSED(win); } | ||
| 1469 | void RGFW_keyfuncEMPTY(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed) {RGFW_UNUSED(win); RGFW_UNUSED(keycode); RGFW_UNUSED(keyName); RGFW_UNUSED(lockState); RGFW_UNUSED(pressed);} | ||
| 1470 | void RGFW_mousebuttonfuncEMPTY(RGFW_window* win, u8 button, double scroll, b8 pressed) {RGFW_UNUSED(win); RGFW_UNUSED(button); RGFW_UNUSED(scroll); RGFW_UNUSED(pressed);} | ||
| 1471 | void RGFW_jsButtonfuncEMPTY(RGFW_window* win, u16 joystick, u8 button, b8 pressed){RGFW_UNUSED(win); RGFW_UNUSED(joystick); RGFW_UNUSED(button); RGFW_UNUSED(pressed); } | ||
| 1472 | void RGFW_jsAxisfuncEMPTY(RGFW_window* win, u16 joystick, RGFW_point axis[2], u8 axisesCount){RGFW_UNUSED(win); RGFW_UNUSED(joystick); RGFW_UNUSED(axis); RGFW_UNUSED(axisesCount); } | ||
| 1473 | |||
| 1474 | #ifdef RGFW_ALLOC_DROPFILES | ||
| 1475 | void RGFW_dndfuncEMPTY(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);} | ||
| 1476 | #else | ||
| 1477 | void RGFW_dndfuncEMPTY(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);} | ||
| 1478 | #endif | ||
| 1479 | |||
| 1480 | RGFW_windowmovefunc RGFW_windowMoveCallback = RGFW_windowmovefuncEMPTY; | ||
| 1481 | RGFW_windowresizefunc RGFW_windowResizeCallback = RGFW_windowresizefuncEMPTY; | ||
| 1482 | RGFW_windowquitfunc RGFW_windowQuitCallback = RGFW_windowquitfuncEMPTY; | ||
| 1483 | RGFW_mouseposfunc RGFW_mousePosCallback = RGFW_mouseposfuncEMPTY; | ||
| 1484 | RGFW_windowrefreshfunc RGFW_windowRefreshCallback = RGFW_windowrefreshfuncEMPTY; | ||
| 1485 | RGFW_focusfunc RGFW_focusCallback = RGFW_focusfuncEMPTY; | ||
| 1486 | RGFW_mouseNotifyfunc RGFW_mouseNotifyCallBack = RGFW_mouseNotifyfuncEMPTY; | ||
| 1487 | RGFW_dndfunc RGFW_dndCallback = RGFW_dndfuncEMPTY; | ||
| 1488 | RGFW_dndInitfunc RGFW_dndInitCallback = RGFW_dndInitfuncEMPTY; | ||
| 1489 | RGFW_keyfunc RGFW_keyCallback = RGFW_keyfuncEMPTY; | ||
| 1490 | RGFW_mousebuttonfunc RGFW_mouseButtonCallback = RGFW_mousebuttonfuncEMPTY; | ||
| 1491 | RGFW_jsButtonfunc RGFW_jsButtonCallback = RGFW_jsButtonfuncEMPTY; | ||
| 1492 | RGFW_jsAxisfunc RGFW_jsAxisCallback = RGFW_jsAxisfuncEMPTY; | ||
| 1493 | |||
| 1494 | void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS) { | ||
| 1495 | RGFW_window_eventWait(win, waitMS); | ||
| 1496 | |||
| 1497 | while (RGFW_window_checkEvent(win) != NULL && RGFW_window_shouldClose(win) == 0) { | ||
| 1498 | if (win->event.type == RGFW_quit) return; | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | #ifdef RGFW_WEBASM /* webasm needs to run the sleep function for asyncify */ | ||
| 1502 | RGFW_sleep(0); | ||
| 1503 | #endif | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | RGFW_windowmovefunc RGFW_setWindowMoveCallback(RGFW_windowmovefunc func) { | ||
| 1507 | RGFW_windowmovefunc prev = (RGFW_windowMoveCallback == RGFW_windowmovefuncEMPTY) ? NULL : RGFW_windowMoveCallback; | ||
| 1508 | RGFW_windowMoveCallback = func; | ||
| 1509 | return prev; | ||
| 1510 | } | ||
| 1511 | RGFW_windowresizefunc RGFW_setWindowResizeCallback(RGFW_windowresizefunc func) { | ||
| 1512 | RGFW_windowresizefunc prev = (RGFW_windowResizeCallback == RGFW_windowresizefuncEMPTY) ? NULL : RGFW_windowResizeCallback; | ||
| 1513 | RGFW_windowResizeCallback = func; | ||
| 1514 | return prev; | ||
| 1515 | } | ||
| 1516 | RGFW_windowquitfunc RGFW_setWindowQuitCallback(RGFW_windowquitfunc func) { | ||
| 1517 | RGFW_windowquitfunc prev = (RGFW_windowQuitCallback == RGFW_windowquitfuncEMPTY) ? NULL : RGFW_windowQuitCallback; | ||
| 1518 | RGFW_windowQuitCallback = func; | ||
| 1519 | return prev; | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | RGFW_mouseposfunc RGFW_setMousePosCallback(RGFW_mouseposfunc func) { | ||
| 1523 | RGFW_mouseposfunc prev = (RGFW_mousePosCallback == RGFW_mouseposfuncEMPTY) ? NULL : RGFW_mousePosCallback; | ||
| 1524 | RGFW_mousePosCallback = func; | ||
| 1525 | return prev; | ||
| 1526 | } | ||
| 1527 | RGFW_windowrefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func) { | ||
| 1528 | RGFW_windowrefreshfunc prev = (RGFW_windowRefreshCallback == RGFW_windowrefreshfuncEMPTY) ? NULL : RGFW_windowRefreshCallback; | ||
| 1529 | RGFW_windowRefreshCallback = func; | ||
| 1530 | return prev; | ||
| 1531 | } | ||
| 1532 | RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func) { | ||
| 1533 | RGFW_focusfunc prev = (RGFW_focusCallback == RGFW_focusfuncEMPTY) ? NULL : RGFW_focusCallback; | ||
| 1534 | RGFW_focusCallback = func; | ||
| 1535 | return prev; | ||
| 1536 | } | ||
| 1537 | |||
| 1538 | RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func) { | ||
| 1539 | RGFW_mouseNotifyfunc prev = (RGFW_mouseNotifyCallBack == RGFW_mouseNotifyfuncEMPTY) ? NULL : RGFW_mouseNotifyCallBack; | ||
| 1540 | RGFW_mouseNotifyCallBack = func; | ||
| 1541 | return prev; | ||
| 1542 | } | ||
| 1543 | RGFW_dndfunc RGFW_setDndCallback(RGFW_dndfunc func) { | ||
| 1544 | RGFW_dndfunc prev = (RGFW_dndCallback == RGFW_dndfuncEMPTY) ? NULL : RGFW_dndCallback; | ||
| 1545 | RGFW_dndCallback = func; | ||
| 1546 | return prev; | ||
| 1547 | } | ||
| 1548 | RGFW_dndInitfunc RGFW_setDndInitCallback(RGFW_dndInitfunc func) { | ||
| 1549 | RGFW_dndInitfunc prev = (RGFW_dndInitCallback == RGFW_dndInitfuncEMPTY) ? NULL : RGFW_dndInitCallback; | ||
| 1550 | RGFW_dndInitCallback = func; | ||
| 1551 | return prev; | ||
| 1552 | } | ||
| 1553 | RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func) { | ||
| 1554 | RGFW_keyfunc prev = (RGFW_keyCallback == RGFW_keyfuncEMPTY) ? NULL : RGFW_keyCallback; | ||
| 1555 | RGFW_keyCallback = func; | ||
| 1556 | return prev; | ||
| 1557 | } | ||
| 1558 | RGFW_mousebuttonfunc RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func) { | ||
| 1559 | RGFW_mousebuttonfunc prev = (RGFW_mouseButtonCallback == RGFW_mousebuttonfuncEMPTY) ? NULL : RGFW_mouseButtonCallback; | ||
| 1560 | RGFW_mouseButtonCallback = func; | ||
| 1561 | return prev; | ||
| 1562 | } | ||
| 1563 | RGFW_jsButtonfunc RGFW_setjsButtonCallback(RGFW_jsButtonfunc func) { | ||
| 1564 | RGFW_jsButtonfunc prev = (RGFW_jsButtonCallback == RGFW_jsButtonfuncEMPTY) ? NULL : RGFW_jsButtonCallback; | ||
| 1565 | RGFW_jsButtonCallback = func; | ||
| 1566 | return prev; | ||
| 1567 | } | ||
| 1568 | RGFW_jsAxisfunc RGFW_setjsAxisCallback(RGFW_jsAxisfunc func) { | ||
| 1569 | RGFW_jsAxisfunc prev = (RGFW_jsAxisCallback == RGFW_jsAxisfuncEMPTY) ? NULL : RGFW_jsAxisCallback; | ||
| 1570 | RGFW_jsAxisCallback = func; | ||
| 1571 | return prev; | ||
| 1572 | } | ||
| 1573 | /* | ||
| 1574 | no more event call back defines | ||
| 1575 | */ | ||
| 1576 | |||
| 1577 | #define RGFW_ASSERT(check, str) {\ | ||
| 1578 | if (!(check)) { \ | ||
| 1579 | printf(str); \ | ||
| 1580 | assert(check); \ | ||
| 1581 | } \ | ||
| 1582 | } | ||
| 1583 | |||
| 1584 | b8 RGFW_error = 0; | ||
| 1585 | b8 RGFW_Error(void) { return RGFW_error; } | ||
| 1586 | |||
| 1587 | #define SET_ATTRIB(a, v) { \ | ||
| 1588 | assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ | ||
| 1589 | attribs[index++] = a; \ | ||
| 1590 | attribs[index++] = v; \ | ||
| 1591 | } | ||
| 1592 | |||
| 1593 | RGFW_area RGFW_bufferSize = {0, 0}; | ||
| 1594 | void RGFW_setBufferSize(RGFW_area size) { | ||
| 1595 | RGFW_bufferSize = size; | ||
| 1596 | } | ||
| 1597 | |||
| 1598 | |||
| 1599 | RGFWDEF RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args); | ||
| 1600 | |||
| 1601 | /* do a basic initialization for RGFW_window, this is to standard it for each OS */ | ||
| 1602 | RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args) { | ||
| 1603 | RGFW_window* win = (RGFW_window*) RGFW_MALLOC(sizeof(RGFW_window)); /*!< make a new RGFW struct */ | ||
| 1604 | |||
| 1605 | /* clear out dnd info */ | ||
| 1606 | #ifdef RGFW_ALLOC_DROPFILES | ||
| 1607 | win->event.droppedFiles = (char**) RGFW_MALLOC(sizeof(char*) * RGFW_MAX_DROPS); | ||
| 1608 | u32 i; | ||
| 1609 | for (i = 0; i < RGFW_MAX_DROPS; i++) | ||
| 1610 | win->event.droppedFiles[i] = (char*) RGFW_CALLOC(RGFW_MAX_PATH, sizeof(char)); | ||
| 1611 | #endif | ||
| 1612 | |||
| 1613 | /* X11 requires us to have a display to get the screen size */ | ||
| 1614 | #ifndef RGFW_X11 | ||
| 1615 | RGFW_area screenR = RGFW_getScreenSize(); | ||
| 1616 | #else | ||
| 1617 | win->src.display = XOpenDisplay(NULL); | ||
| 1618 | assert(win->src.display != NULL); | ||
| 1619 | |||
| 1620 | Screen* scrn = DefaultScreenOfDisplay((Display*)win->src.display); | ||
| 1621 | RGFW_area screenR = RGFW_AREA((u32)scrn->width, (u32)scrn->height); | ||
| 1622 | #endif | ||
| 1623 | |||
| 1624 | /* rect based the requested args */ | ||
| 1625 | if (args & RGFW_FULLSCREEN) | ||
| 1626 | rect = RGFW_RECT(0, 0, screenR.w, screenR.h); | ||
| 1627 | |||
| 1628 | /* set and init the new window's data */ | ||
| 1629 | win->r = rect; | ||
| 1630 | win->event.inFocus = 1; | ||
| 1631 | win->event.droppedFilesCount = 0; | ||
| 1632 | RGFW_joystickCount = 0; | ||
| 1633 | win->_winArgs = 0; | ||
| 1634 | win->event.lockState = 0; | ||
| 1635 | |||
| 1636 | return win; | ||
| 1637 | } | ||
| 1638 | |||
| 1639 | #ifndef RGFW_NO_MONITOR | ||
| 1640 | void RGFW_window_scaleToMonitor(RGFW_window* win) { | ||
| 1641 | RGFW_monitor monitor = RGFW_window_getMonitor(win); | ||
| 1642 | |||
| 1643 | RGFW_window_resize(win, RGFW_AREA((u32)(monitor.scaleX * (float)win->r.w), (u32)(monitor.scaleX * (float)win->r.h))); | ||
| 1644 | } | ||
| 1645 | #endif | ||
| 1646 | |||
| 1647 | RGFW_window* RGFW_root = NULL; | ||
| 1648 | |||
| 1649 | |||
| 1650 | #define RGFW_HOLD_MOUSE (1L<<2) /*!< hold the moues still */ | ||
| 1651 | #define RGFW_MOUSE_LEFT (1L<<3) /* if mouse left the window */ | ||
| 1652 | |||
| 1653 | #ifdef RGFW_MACOS | ||
| 1654 | RGFWDEF void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer); | ||
| 1655 | RGFWDEF void* RGFW_cocoaGetLayer(void); | ||
| 1656 | #endif | ||
| 1657 | |||
| 1658 | char* RGFW_className = NULL; | ||
| 1659 | void RGFW_setClassName(char* name) { | ||
| 1660 | RGFW_className = name; | ||
| 1661 | } | ||
| 1662 | |||
| 1663 | void RGFW_clipboardFree(char* str) { RGFW_FREE(str); } | ||
| 1664 | |||
| 1665 | RGFW_keyState RGFW_mouseButtons[5] = { {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} }; | ||
| 1666 | |||
| 1667 | b8 RGFW_isMousePressed(RGFW_window* win, u8 button) { | ||
| 1668 | assert(win != NULL); | ||
| 1669 | return RGFW_mouseButtons[button].current && (win != NULL) && win->event.inFocus; | ||
| 1670 | } | ||
| 1671 | b8 RGFW_wasMousePressed(RGFW_window* win, u8 button) { | ||
| 1672 | assert(win != NULL); | ||
| 1673 | return RGFW_mouseButtons[button].prev && (win != NULL) && win->event.inFocus; | ||
| 1674 | } | ||
| 1675 | b8 RGFW_isMouseHeld(RGFW_window* win, u8 button) { | ||
| 1676 | return (RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button)); | ||
| 1677 | } | ||
| 1678 | b8 RGFW_isMouseReleased(RGFW_window* win, u8 button) { | ||
| 1679 | return (!RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button)); | ||
| 1680 | } | ||
| 1681 | |||
| 1682 | b8 RGFW_isPressed(RGFW_window* win, u8 key) { | ||
| 1683 | return RGFW_keyboard[key].current && (win == NULL || win->event.inFocus); | ||
| 1684 | } | ||
| 1685 | |||
| 1686 | b8 RGFW_wasPressed(RGFW_window* win, u8 key) { | ||
| 1687 | return RGFW_keyboard[key].prev && (win == NULL || win->event.inFocus); | ||
| 1688 | } | ||
| 1689 | |||
| 1690 | b8 RGFW_isHeld(RGFW_window* win, u8 key) { | ||
| 1691 | return (RGFW_isPressed(win, key) && RGFW_wasPressed(win, key)); | ||
| 1692 | } | ||
| 1693 | |||
| 1694 | b8 RGFW_isClicked(RGFW_window* win, u8 key) { | ||
| 1695 | return (RGFW_wasPressed(win, key) && !RGFW_isPressed(win, key)); | ||
| 1696 | } | ||
| 1697 | |||
| 1698 | b8 RGFW_isReleased(RGFW_window* win, u8 key) { | ||
| 1699 | return (!RGFW_isPressed(win, key) && RGFW_wasPressed(win, key)); | ||
| 1700 | } | ||
| 1701 | |||
| 1702 | #if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) /* defines for directX context*/ | ||
| 1703 | RGFW_directXinfo RGFW_dxInfo; | ||
| 1704 | RGFW_directXinfo* RGFW_getDirectXInfo(void) { return &RGFW_dxInfo; } | ||
| 1705 | #endif | ||
| 1706 | |||
| 1707 | void RGFW_window_makeCurrent(RGFW_window* win) { | ||
| 1708 | #if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) | ||
| 1709 | if (win == NULL) | ||
| 1710 | RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, NULL, NULL); | ||
| 1711 | else | ||
| 1712 | RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, NULL); | ||
| 1713 | #elif defined(RGFW_OPENGL) | ||
| 1714 | RGFW_window_makeCurrent_OpenGL(win); | ||
| 1715 | #else | ||
| 1716 | RGFW_UNUSED(win) | ||
| 1717 | #endif | ||
| 1718 | } | ||
| 1719 | |||
| 1720 | void RGFW_window_setGPURender(RGFW_window* win, i8 set) { | ||
| 1721 | if (!set && !(win->_winArgs & RGFW_NO_GPU_RENDER)) | ||
| 1722 | win->_winArgs |= RGFW_NO_GPU_RENDER; | ||
| 1723 | |||
| 1724 | else if (set && win->_winArgs & RGFW_NO_GPU_RENDER) | ||
| 1725 | win->_winArgs ^= RGFW_NO_GPU_RENDER; | ||
| 1726 | } | ||
| 1727 | |||
| 1728 | void RGFW_window_setCPURender(RGFW_window* win, i8 set) { | ||
| 1729 | if (!set && !(win->_winArgs & RGFW_NO_CPU_RENDER)) | ||
| 1730 | win->_winArgs |= RGFW_NO_CPU_RENDER; | ||
| 1731 | |||
| 1732 | else if (set && win->_winArgs & RGFW_NO_CPU_RENDER) | ||
| 1733 | win->_winArgs ^= RGFW_NO_CPU_RENDER; | ||
| 1734 | } | ||
| 1735 | |||
| 1736 | void RGFW_window_maximize(RGFW_window* win) { | ||
| 1737 | assert(win != NULL); | ||
| 1738 | |||
| 1739 | RGFW_area screen = RGFW_getScreenSize(); | ||
| 1740 | |||
| 1741 | RGFW_window_move(win, RGFW_POINT(0, 0)); | ||
| 1742 | RGFW_window_resize(win, screen); | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | b8 RGFW_window_shouldClose(RGFW_window* win) { | ||
| 1746 | assert(win != NULL); | ||
| 1747 | return (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_Escape)); | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | void RGFW_window_setShouldClose(RGFW_window* win) { win->event.type = RGFW_quit; RGFW_windowQuitCallback(win); } | ||
| 1751 | |||
| 1752 | #ifndef RGFW_NO_MONITOR | ||
| 1753 | void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) { | ||
| 1754 | RGFW_window_move(win, RGFW_POINT(m.rect.x + win->r.x, m.rect.y + win->r.y)); | ||
| 1755 | } | ||
| 1756 | #endif | ||
| 1757 | |||
| 1758 | RGFWDEF void RGFW_captureCursor(RGFW_window* win, RGFW_rect); | ||
| 1759 | RGFWDEF void RGFW_releaseCursor(RGFW_window* win); | ||
| 1760 | |||
| 1761 | void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area) { | ||
| 1762 | if ((win->_winArgs & RGFW_HOLD_MOUSE)) | ||
| 1763 | return; | ||
| 1764 | |||
| 1765 | |||
| 1766 | if (!area.w && !area.h) | ||
| 1767 | area = RGFW_AREA(win->r.w / 2, win->r.h / 2); | ||
| 1768 | |||
| 1769 | win->_winArgs |= RGFW_HOLD_MOUSE; | ||
| 1770 | RGFW_captureCursor(win, win->r); | ||
| 1771 | RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); | ||
| 1772 | } | ||
| 1773 | |||
| 1774 | void RGFW_window_mouseUnhold(RGFW_window* win) { | ||
| 1775 | if ((win->_winArgs & RGFW_HOLD_MOUSE)) { | ||
| 1776 | win->_winArgs ^= RGFW_HOLD_MOUSE; | ||
| 1777 | |||
| 1778 | RGFW_releaseCursor(win); | ||
| 1779 | } | ||
| 1780 | } | ||
| 1781 | |||
| 1782 | u32 RGFW_window_checkFPS(RGFW_window* win, u32 fpsCap) { | ||
| 1783 | u64 deltaTime = RGFW_getTimeNS() - win->event.frameTime; | ||
| 1784 | |||
| 1785 | u32 output_fps = 0; | ||
| 1786 | u64 fps = round(1e+9 / deltaTime); | ||
| 1787 | output_fps= fps; | ||
| 1788 | |||
| 1789 | if (fpsCap && fps > fpsCap) { | ||
| 1790 | u64 frameTimeNS = 1e+9 / fpsCap; | ||
| 1791 | u64 sleepTimeMS = (frameTimeNS - deltaTime) / 1e6; | ||
| 1792 | |||
| 1793 | if (sleepTimeMS > 0) { | ||
| 1794 | RGFW_sleep(sleepTimeMS); | ||
| 1795 | win->event.frameTime = 0; | ||
| 1796 | } | ||
| 1797 | } | ||
| 1798 | |||
| 1799 | win->event.frameTime = RGFW_getTimeNS(); | ||
| 1800 | |||
| 1801 | if (fpsCap == 0) | ||
| 1802 | return (u32) output_fps; | ||
| 1803 | |||
| 1804 | deltaTime = RGFW_getTimeNS() - win->event.frameTime2; | ||
| 1805 | output_fps = round(1e+9 / deltaTime); | ||
| 1806 | win->event.frameTime2 = RGFW_getTimeNS(); | ||
| 1807 | |||
| 1808 | return output_fps; | ||
| 1809 | } | ||
| 1810 | |||
| 1811 | u32 RGFW_isPressedJS(RGFW_window* win, u16 c, u8 button) { | ||
| 1812 | RGFW_UNUSED(win); | ||
| 1813 | return RGFW_jsPressed[c][button]; | ||
| 1814 | } | ||
| 1815 | |||
| 1816 | #if defined(RGFW_X11) || defined(RGFW_WINDOWS) | ||
| 1817 | void RGFW_window_showMouse(RGFW_window* win, i8 show) { | ||
| 1818 | static u8 RGFW_blk[] = { 0, 0, 0, 0 }; | ||
| 1819 | if (show == 0) | ||
| 1820 | RGFW_window_setMouse(win, RGFW_blk, RGFW_AREA(1, 1), 4); | ||
| 1821 | else | ||
| 1822 | RGFW_window_setMouseDefault(win); | ||
| 1823 | } | ||
| 1824 | #endif | ||
| 1825 | |||
| 1826 | RGFWDEF void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock); | ||
| 1827 | void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { | ||
| 1828 | if (capital && !(win->event.lockState & RGFW_CAPSLOCK)) | ||
| 1829 | win->event.lockState |= RGFW_CAPSLOCK; | ||
| 1830 | else if (!capital && (win->event.lockState & RGFW_CAPSLOCK)) | ||
| 1831 | win->event.lockState ^= RGFW_CAPSLOCK; | ||
| 1832 | |||
| 1833 | if (numlock && !(win->event.lockState & RGFW_NUMLOCK)) | ||
| 1834 | win->event.lockState |= RGFW_NUMLOCK; | ||
| 1835 | else if (!numlock && (win->event.lockState & RGFW_NUMLOCK)) | ||
| 1836 | win->event.lockState ^= RGFW_NUMLOCK; | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | #if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WEBASM) || defined(RGFW_WAYLAND) | ||
| 1840 | struct timespec; | ||
| 1841 | |||
| 1842 | int nanosleep(const struct timespec* duration, struct timespec* rem); | ||
| 1843 | int clock_gettime(clockid_t clk_id, struct timespec* tp); | ||
| 1844 | int setenv(const char *name, const char *value, int overwrite); | ||
| 1845 | |||
| 1846 | void RGFW_window_setDND(RGFW_window* win, b8 allow) { | ||
| 1847 | if (allow && !(win->_winArgs & RGFW_ALLOW_DND)) | ||
| 1848 | win->_winArgs |= RGFW_ALLOW_DND; | ||
| 1849 | |||
| 1850 | else if (!allow && (win->_winArgs & RGFW_ALLOW_DND)) | ||
| 1851 | win->_winArgs ^= RGFW_ALLOW_DND; | ||
| 1852 | } | ||
| 1853 | #endif | ||
| 1854 | |||
| 1855 | /* | ||
| 1856 | graphics API specific code (end of generic code) | ||
| 1857 | starts here | ||
| 1858 | */ | ||
| 1859 | |||
| 1860 | |||
| 1861 | /* | ||
| 1862 | OpenGL defines start here (Normal, EGL, OSMesa) | ||
| 1863 | */ | ||
| 1864 | |||
| 1865 | #if defined(RGFW_OPENGL) || defined(RGFW_EGL) || defined(RGFW_OSMESA) | ||
| 1866 | #ifdef RGFW_WINDOWS | ||
| 1867 | #define WIN32_LEAN_AND_MEAN | ||
| 1868 | #define OEMRESOURCE | ||
| 1869 | #include <windows.h> | ||
| 1870 | #endif | ||
| 1871 | |||
| 1872 | #if !defined(__APPLE__) && !defined(RGFW_NO_GL_HEADER) | ||
| 1873 | #include <GL/gl.h> | ||
| 1874 | #elif defined(__APPLE__) | ||
| 1875 | #ifndef GL_SILENCE_DEPRECATION | ||
| 1876 | #define GL_SILENCE_DEPRECATION | ||
| 1877 | #endif | ||
| 1878 | #include <OpenGL/gl.h> | ||
| 1879 | #include <OpenGL/OpenGL.h> | ||
| 1880 | #endif | ||
| 1881 | |||
| 1882 | /* EGL, normal OpenGL only */ | ||
| 1883 | #if !defined(RGFW_OSMESA) | ||
| 1884 | i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0; | ||
| 1885 | b8 RGFW_profile = RGFW_GL_CORE; | ||
| 1886 | |||
| 1887 | #ifndef RGFW_EGL | ||
| 1888 | i32 RGFW_STENCIL = 8, RGFW_SAMPLES = 4, RGFW_STEREO = 0, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1; | ||
| 1889 | #else | ||
| 1890 | i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = 0, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1; | ||
| 1891 | #endif | ||
| 1892 | |||
| 1893 | |||
| 1894 | void RGFW_setGLStencil(i32 stencil) { RGFW_STENCIL = stencil; } | ||
| 1895 | void RGFW_setGLSamples(i32 samples) { RGFW_SAMPLES = samples; } | ||
| 1896 | void RGFW_setGLStereo(i32 stereo) { RGFW_STEREO = stereo; } | ||
| 1897 | void RGFW_setGLAuxBuffers(i32 auxBuffers) { RGFW_AUX_BUFFERS = auxBuffers; } | ||
| 1898 | void RGFW_setDoubleBuffer(b8 useDoubleBuffer) { RGFW_DOUBLE_BUFFER = useDoubleBuffer; } | ||
| 1899 | |||
| 1900 | void RGFW_setGLVersion(b8 profile, i32 major, i32 minor) { | ||
| 1901 | RGFW_profile = profile; | ||
| 1902 | RGFW_majorVersion = major; | ||
| 1903 | RGFW_minorVersion = minor; | ||
| 1904 | } | ||
| 1905 | |||
| 1906 | /* OPENGL normal only (no EGL / OSMesa) */ | ||
| 1907 | #ifndef RGFW_EGL | ||
| 1908 | |||
| 1909 | #define RGFW_GL_RENDER_TYPE RGFW_OS_BASED_VALUE(GLX_X_VISUAL_TYPE, 0x2003, 73, 0, 0) | ||
| 1910 | #define RGFW_GL_ALPHA_SIZE RGFW_OS_BASED_VALUE(GLX_ALPHA_SIZE, 0x201b, 11, 0, 0) | ||
| 1911 | #define RGFW_GL_DEPTH_SIZE RGFW_OS_BASED_VALUE(GLX_DEPTH_SIZE, 0x2022, 12, 0, 0) | ||
| 1912 | #define RGFW_GL_DOUBLEBUFFER RGFW_OS_BASED_VALUE(GLX_DOUBLEBUFFER, 0x2011, 5, 0, 0) | ||
| 1913 | #define RGFW_GL_STENCIL_SIZE RGFW_OS_BASED_VALUE(GLX_STENCIL_SIZE, 0x2023, 13, 0, 0) | ||
| 1914 | #define RGFW_GL_SAMPLES RGFW_OS_BASED_VALUE(GLX_SAMPLES, 0x2042, 55, 0, 0) | ||
| 1915 | #define RGFW_GL_STEREO RGFW_OS_BASED_VALUE(GLX_STEREO, 0x2012, 6, 0, 0) | ||
| 1916 | #define RGFW_GL_AUX_BUFFERS RGFW_OS_BASED_VALUE(GLX_AUX_BUFFERS, 0x2024, 7, 0, 0) | ||
| 1917 | |||
| 1918 | #if defined(RGFW_X11) || defined(RGFW_WINDOWS) | ||
| 1919 | #define RGFW_GL_DRAW RGFW_OS_BASED_VALUE(GLX_X_RENDERABLE, 0x2001, 0, 0, 0) | ||
| 1920 | #define RGFW_GL_DRAW_TYPE RGFW_OS_BASED_VALUE(GLX_RENDER_TYPE, 0x2013, 0, 0, 0) | ||
| 1921 | #define RGFW_GL_FULL_FORMAT RGFW_OS_BASED_VALUE(GLX_TRUE_COLOR, 0x2027, 0, 0, 0) | ||
| 1922 | #define RGFW_GL_RED_SIZE RGFW_OS_BASED_VALUE(GLX_RED_SIZE, 0x2015, 0, 0, 0) | ||
| 1923 | #define RGFW_GL_GREEN_SIZE RGFW_OS_BASED_VALUE(GLX_GREEN_SIZE, 0x2017, 0, 0, 0) | ||
| 1924 | #define RGFW_GL_BLUE_SIZE RGFW_OS_BASED_VALUE(GLX_BLUE_SIZE, 0x2019, 0, 0, 0) | ||
| 1925 | #define RGFW_GL_USE_RGBA RGFW_OS_BASED_VALUE(GLX_RGBA_BIT, 0x202B, 0, 0, 0) | ||
| 1926 | #endif | ||
| 1927 | |||
| 1928 | #ifdef RGFW_WINDOWS | ||
| 1929 | #define WGL_SUPPORT_OPENGL_ARB 0x2010 | ||
| 1930 | #define WGL_COLOR_BITS_ARB 0x2014 | ||
| 1931 | #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 | ||
| 1932 | #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 | ||
| 1933 | #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 | ||
| 1934 | #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 | ||
| 1935 | #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 | ||
| 1936 | #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 | ||
| 1937 | #define WGL_SAMPLE_BUFFERS_ARB 0x2041 | ||
| 1938 | #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 | ||
| 1939 | #define WGL_PIXEL_TYPE_ARB 0x2013 | ||
| 1940 | #define WGL_TYPE_RGBA_ARB 0x202B | ||
| 1941 | |||
| 1942 | #define WGL_TRANSPARENT_ARB 0x200A | ||
| 1943 | #endif | ||
| 1944 | |||
| 1945 | /* The window'ing api needs to know how to render the data we (or opengl) give it | ||
| 1946 | MacOS and Windows do this using a structure called a "pixel format" | ||
| 1947 | X11 calls it a "Visual" | ||
| 1948 | This function returns the attributes for the format we want */ | ||
| 1949 | static u32* RGFW_initFormatAttribs(u32 useSoftware) { | ||
| 1950 | RGFW_UNUSED(useSoftware); | ||
| 1951 | static u32 attribs[] = { | ||
| 1952 | #if defined(RGFW_X11) || defined(RGFW_WINDOWS) | ||
| 1953 | RGFW_GL_RENDER_TYPE, | ||
| 1954 | RGFW_GL_FULL_FORMAT, | ||
| 1955 | #endif | ||
| 1956 | RGFW_GL_ALPHA_SIZE , 8, | ||
| 1957 | RGFW_GL_DEPTH_SIZE , 24, | ||
| 1958 | #if defined(RGFW_X11) || defined(RGFW_WINDOWS) | ||
| 1959 | RGFW_GL_DRAW, 1, | ||
| 1960 | RGFW_GL_RED_SIZE , 8, | ||
| 1961 | RGFW_GL_GREEN_SIZE , 8, | ||
| 1962 | RGFW_GL_BLUE_SIZE , 8, | ||
| 1963 | RGFW_GL_DRAW_TYPE , RGFW_GL_USE_RGBA, | ||
| 1964 | #endif | ||
| 1965 | |||
| 1966 | #ifdef RGFW_X11 | ||
| 1967 | GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, | ||
| 1968 | #endif | ||
| 1969 | |||
| 1970 | #ifdef RGFW_MACOS | ||
| 1971 | 72, | ||
| 1972 | 8, 24, | ||
| 1973 | #endif | ||
| 1974 | |||
| 1975 | #ifdef RGFW_WINDOWS | ||
| 1976 | WGL_SUPPORT_OPENGL_ARB, 1, | ||
| 1977 | WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, | ||
| 1978 | WGL_COLOR_BITS_ARB, 32, | ||
| 1979 | #endif | ||
| 1980 | |||
| 1981 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | ||
| 1982 | }; | ||
| 1983 | |||
| 1984 | size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13; | ||
| 1985 | |||
| 1986 | #define RGFW_GL_ADD_ATTRIB(attrib, attVal) \ | ||
| 1987 | if (attVal) { \ | ||
| 1988 | attribs[index] = attrib;\ | ||
| 1989 | attribs[index + 1] = attVal;\ | ||
| 1990 | index += 2;\ | ||
| 1991 | } | ||
| 1992 | |||
| 1993 | RGFW_GL_ADD_ATTRIB(RGFW_GL_DOUBLEBUFFER, 1); | ||
| 1994 | |||
| 1995 | RGFW_GL_ADD_ATTRIB(RGFW_GL_STENCIL_SIZE, RGFW_STENCIL); | ||
| 1996 | RGFW_GL_ADD_ATTRIB(RGFW_GL_STEREO, RGFW_STEREO); | ||
| 1997 | RGFW_GL_ADD_ATTRIB(RGFW_GL_AUX_BUFFERS, RGFW_AUX_BUFFERS); | ||
| 1998 | |||
| 1999 | #ifndef RGFW_X11 | ||
| 2000 | RGFW_GL_ADD_ATTRIB(RGFW_GL_SAMPLES, RGFW_SAMPLES); | ||
| 2001 | #endif | ||
| 2002 | |||
| 2003 | #ifdef RGFW_MACOS | ||
| 2004 | if (useSoftware) { | ||
| 2005 | RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID); | ||
| 2006 | } else { | ||
| 2007 | attribs[index] = RGFW_GL_RENDER_TYPE; | ||
| 2008 | index += 1; | ||
| 2009 | } | ||
| 2010 | #endif | ||
| 2011 | |||
| 2012 | #ifdef RGFW_MACOS | ||
| 2013 | /* macOS has the surface attribs and the opengl attribs connected for some reason | ||
| 2014 | maybe this is to give macOS more control to limit openGL/the opengl version? */ | ||
| 2015 | |||
| 2016 | attribs[index] = 99; | ||
| 2017 | attribs[index + 1] = 0x1000; | ||
| 2018 | |||
| 2019 | if (RGFW_majorVersion >= 4 || RGFW_majorVersion >= 3) { | ||
| 2020 | attribs[index + 1] = (u32) ((RGFW_majorVersion >= 4) ? 0x4100 : 0x3200); | ||
| 2021 | } | ||
| 2022 | #endif | ||
| 2023 | |||
| 2024 | RGFW_GL_ADD_ATTRIB(0, 0); | ||
| 2025 | |||
| 2026 | return attribs; | ||
| 2027 | } | ||
| 2028 | |||
| 2029 | /* EGL only (no OSMesa nor normal OPENGL) */ | ||
| 2030 | #elif defined(RGFW_EGL) | ||
| 2031 | |||
| 2032 | #include <EGL/egl.h> | ||
| 2033 | |||
| 2034 | #if defined(RGFW_LINK_EGL) | ||
| 2035 | typedef EGLBoolean(EGLAPIENTRY* PFN_eglInitialize)(EGLDisplay, EGLint*, EGLint*); | ||
| 2036 | |||
| 2037 | PFNEGLINITIALIZEPROC eglInitializeSource; | ||
| 2038 | PFNEGLGETCONFIGSPROC eglGetConfigsSource; | ||
| 2039 | PFNEGLCHOOSECONFIGPROC eglChooseConfigSource; | ||
| 2040 | PFNEGLCREATEWINDOWSURFACEPROC eglCreateWindowSurfaceSource; | ||
| 2041 | PFNEGLCREATECONTEXTPROC eglCreateContextSource; | ||
| 2042 | PFNEGLMAKECURRENTPROC eglMakeCurrentSource; | ||
| 2043 | PFNEGLGETDISPLAYPROC eglGetDisplaySource; | ||
| 2044 | PFNEGLSWAPBUFFERSPROC eglSwapBuffersSource; | ||
| 2045 | PFNEGLSWAPINTERVALPROC eglSwapIntervalSource; | ||
| 2046 | PFNEGLBINDAPIPROC eglBindAPISource; | ||
| 2047 | PFNEGLDESTROYCONTEXTPROC eglDestroyContextSource; | ||
| 2048 | PFNEGLTERMINATEPROC eglTerminateSource; | ||
| 2049 | PFNEGLDESTROYSURFACEPROC eglDestroySurfaceSource; | ||
| 2050 | |||
| 2051 | #define eglInitialize eglInitializeSource | ||
| 2052 | #define eglGetConfigs eglGetConfigsSource | ||
| 2053 | #define eglChooseConfig eglChooseConfigSource | ||
| 2054 | #define eglCreateWindowSurface eglCreateWindowSurfaceSource | ||
| 2055 | #define eglCreateContext eglCreateContextSource | ||
| 2056 | #define eglMakeCurrent eglMakeCurrentSource | ||
| 2057 | #define eglGetDisplay eglGetDisplaySource | ||
| 2058 | #define eglSwapBuffers eglSwapBuffersSource | ||
| 2059 | #define eglSwapInterval eglSwapIntervalSource | ||
| 2060 | #define eglBindAPI eglBindAPISource | ||
| 2061 | #define eglDestroyContext eglDestroyContextSource | ||
| 2062 | #define eglTerminate eglTerminateSource | ||
| 2063 | #define eglDestroySurface eglDestroySurfaceSource; | ||
| 2064 | #endif | ||
| 2065 | |||
| 2066 | |||
| 2067 | #define EGL_SURFACE_MAJOR_VERSION_KHR 0x3098 | ||
| 2068 | #define EGL_SURFACE_MINOR_VERSION_KHR 0x30fb | ||
| 2069 | |||
| 2070 | #ifndef RGFW_GL_ADD_ATTRIB | ||
| 2071 | #define RGFW_GL_ADD_ATTRIB(attrib, attVal) \ | ||
| 2072 | if (attVal) { \ | ||
| 2073 | attribs[index] = attrib;\ | ||
| 2074 | attribs[index + 1] = attVal;\ | ||
| 2075 | index += 2;\ | ||
| 2076 | } | ||
| 2077 | #endif | ||
| 2078 | |||
| 2079 | |||
| 2080 | void RGFW_createOpenGLContext(RGFW_window* win) { | ||
| 2081 | #if defined(RGFW_LINK_EGL) | ||
| 2082 | eglInitializeSource = (PFNEGLINITIALIZEPROC) eglGetProcAddress("eglInitialize"); | ||
| 2083 | eglGetConfigsSource = (PFNEGLGETCONFIGSPROC) eglGetProcAddress("eglGetConfigs"); | ||
| 2084 | eglChooseConfigSource = (PFNEGLCHOOSECONFIGPROC) eglGetProcAddress("eglChooseConfig"); | ||
| 2085 | eglCreateWindowSurfaceSource = (PFNEGLCREATEWINDOWSURFACEPROC) eglGetProcAddress("eglCreateWindowSurface"); | ||
| 2086 | eglCreateContextSource = (PFNEGLCREATECONTEXTPROC) eglGetProcAddress("eglCreateContext"); | ||
| 2087 | eglMakeCurrentSource = (PFNEGLMAKECURRENTPROC) eglGetProcAddress("eglMakeCurrent"); | ||
| 2088 | eglGetDisplaySource = (PFNEGLGETDISPLAYPROC) eglGetProcAddress("eglGetDisplay"); | ||
| 2089 | eglSwapBuffersSource = (PFNEGLSWAPBUFFERSPROC) eglGetProcAddress("eglSwapBuffers"); | ||
| 2090 | eglSwapIntervalSource = (PFNEGLSWAPINTERVALPROC) eglGetProcAddress("eglSwapInterval"); | ||
| 2091 | eglBindAPISource = (PFNEGLBINDAPIPROC) eglGetProcAddress("eglBindAPI"); | ||
| 2092 | eglDestroyContextSource = (PFNEGLDESTROYCONTEXTPROC) eglGetProcAddress("eglDestroyContext"); | ||
| 2093 | eglTerminateSource = (PFNEGLTERMINATEPROC) eglGetProcAddress("eglTerminate"); | ||
| 2094 | eglDestroySurfaceSource = (PFNEGLDESTROYSURFACEPROC) eglGetProcAddress("eglDestroySurface"); | ||
| 2095 | #endif /* RGFW_LINK_EGL */ | ||
| 2096 | |||
| 2097 | #ifdef RGFW_WINDOWS | ||
| 2098 | win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.hdc); | ||
| 2099 | #elif defined(RGFW_MACOS) | ||
| 2100 | win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType)0); | ||
| 2101 | #else | ||
| 2102 | win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display); | ||
| 2103 | #endif | ||
| 2104 | |||
| 2105 | EGLint major, minor; | ||
| 2106 | |||
| 2107 | eglInitialize(win->src.EGL_display, &major, &minor); | ||
| 2108 | |||
| 2109 | #ifndef EGL_OPENGL_ES1_BIT | ||
| 2110 | #define EGL_OPENGL_ES1_BIT 0x1 | ||
| 2111 | #endif | ||
| 2112 | |||
| 2113 | EGLint egl_config[] = { | ||
| 2114 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT, | ||
| 2115 | EGL_RENDERABLE_TYPE, | ||
| 2116 | #ifdef RGFW_OPENGL_ES1 | ||
| 2117 | EGL_OPENGL_ES1_BIT, | ||
| 2118 | #elif defined(RGFW_OPENGL_ES3) | ||
| 2119 | EGL_OPENGL_ES3_BIT, | ||
| 2120 | #elif defined(RGFW_OPENGL_ES2) | ||
| 2121 | EGL_OPENGL_ES2_BIT, | ||
| 2122 | #else | ||
| 2123 | EGL_OPENGL_BIT, | ||
| 2124 | #endif | ||
| 2125 | EGL_NONE, EGL_NONE | ||
| 2126 | }; | ||
| 2127 | |||
| 2128 | EGLConfig config; | ||
| 2129 | EGLint numConfigs; | ||
| 2130 | eglChooseConfig(win->src.EGL_display, egl_config, &config, 1, &numConfigs); | ||
| 2131 | |||
| 2132 | #if defined(RGFW_MACOS) | ||
| 2133 | void* layer = RGFW_cocoaGetLayer(); | ||
| 2134 | |||
| 2135 | RGFW_window_cocoaSetLayer(win, layer); | ||
| 2136 | |||
| 2137 | win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) layer, NULL); | ||
| 2138 | #else | ||
| 2139 | win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); | ||
| 2140 | #endif | ||
| 2141 | |||
| 2142 | EGLint attribs[] = { | ||
| 2143 | EGL_CONTEXT_CLIENT_VERSION, | ||
| 2144 | #ifdef RGFW_OPENGL_ES1 | ||
| 2145 | 1, | ||
| 2146 | #else | ||
| 2147 | 2, | ||
| 2148 | #endif | ||
| 2149 | EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE | ||
| 2150 | }; | ||
| 2151 | |||
| 2152 | size_t index = 4; | ||
| 2153 | RGFW_GL_ADD_ATTRIB(EGL_STENCIL_SIZE, RGFW_STENCIL); | ||
| 2154 | RGFW_GL_ADD_ATTRIB(EGL_SAMPLES, RGFW_SAMPLES); | ||
| 2155 | |||
| 2156 | if (RGFW_DOUBLE_BUFFER) | ||
| 2157 | RGFW_GL_ADD_ATTRIB(EGL_RENDER_BUFFER, EGL_BACK_BUFFER); | ||
| 2158 | |||
| 2159 | if (RGFW_majorVersion) { | ||
| 2160 | attribs[1] = RGFW_majorVersion; | ||
| 2161 | |||
| 2162 | RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MAJOR_VERSION, RGFW_majorVersion); | ||
| 2163 | RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MINOR_VERSION, RGFW_minorVersion); | ||
| 2164 | |||
| 2165 | if (RGFW_profile == RGFW_GL_CORE) { | ||
| 2166 | RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT); | ||
| 2167 | } | ||
| 2168 | else { | ||
| 2169 | RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT); | ||
| 2170 | } | ||
| 2171 | |||
| 2172 | } | ||
| 2173 | |||
| 2174 | #if defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3) | ||
| 2175 | eglBindAPI(EGL_OPENGL_ES_API); | ||
| 2176 | #else | ||
| 2177 | eglBindAPI(EGL_OPENGL_API); | ||
| 2178 | #endif | ||
| 2179 | |||
| 2180 | win->src.EGL_context = eglCreateContext(win->src.EGL_display, config, EGL_NO_CONTEXT, attribs); | ||
| 2181 | |||
| 2182 | if (win->src.EGL_context == NULL) | ||
| 2183 | fprintf(stderr, "failed to create an EGL opengl context\n"); | ||
| 2184 | |||
| 2185 | eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context); | ||
| 2186 | eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); | ||
| 2187 | } | ||
| 2188 | |||
| 2189 | void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { | ||
| 2190 | eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context); | ||
| 2191 | } | ||
| 2192 | |||
| 2193 | #ifdef RGFW_APPLE | ||
| 2194 | void* RGFWnsglFramework = NULL; | ||
| 2195 | #elif defined(RGFW_WINDOWS) | ||
| 2196 | static HMODULE wglinstance = NULL; | ||
| 2197 | #endif | ||
| 2198 | |||
| 2199 | void* RGFW_getProcAddress(const char* procname) { | ||
| 2200 | #if defined(RGFW_WINDOWS) | ||
| 2201 | void* proc = (void*) GetProcAddress(wglinstance, procname); | ||
| 2202 | |||
| 2203 | if (proc) | ||
| 2204 | return proc; | ||
| 2205 | #endif | ||
| 2206 | |||
| 2207 | return (void*) eglGetProcAddress(procname); | ||
| 2208 | } | ||
| 2209 | |||
| 2210 | void RGFW_closeEGL(RGFW_window* win) { | ||
| 2211 | eglDestroySurface(win->src.EGL_display, win->src.EGL_surface); | ||
| 2212 | eglDestroyContext(win->src.EGL_display, win->src.EGL_context); | ||
| 2213 | |||
| 2214 | eglTerminate(win->src.EGL_display); | ||
| 2215 | } | ||
| 2216 | |||
| 2217 | void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { | ||
| 2218 | assert(win != NULL); | ||
| 2219 | |||
| 2220 | eglSwapInterval(win->src.EGL_display, swapInterval); | ||
| 2221 | |||
| 2222 | } | ||
| 2223 | #endif /* RGFW_EGL */ | ||
| 2224 | |||
| 2225 | /* | ||
| 2226 | end of RGFW_EGL defines | ||
| 2227 | */ | ||
| 2228 | |||
| 2229 | /* OPENGL Normal / EGL defines only (no OS MESA) Ends here */ | ||
| 2230 | |||
| 2231 | #elif defined(RGFW_OSMESA) /* OSmesa only */ | ||
| 2232 | RGFWDEF void RGFW_OSMesa_reorganize(void); | ||
| 2233 | |||
| 2234 | /* reorganize buffer for osmesa */ | ||
| 2235 | void RGFW_OSMesa_reorganize(void) { | ||
| 2236 | u8* row = (u8*) RGFW_MALLOC(win->r.w * 3); | ||
| 2237 | |||
| 2238 | i32 half_height = win->r.h / 2; | ||
| 2239 | i32 stride = win->r.w * 3; | ||
| 2240 | |||
| 2241 | i32 y; | ||
| 2242 | for (y = 0; y < half_height; ++y) { | ||
| 2243 | i32 top_offset = y * stride; | ||
| 2244 | i32 bottom_offset = (win->r.h - y - 1) * stride; | ||
| 2245 | memcpy(row, win->buffer + top_offset, stride); | ||
| 2246 | memcpy(win->buffer + top_offset, win->buffer + bottom_offset, stride); | ||
| 2247 | memcpy(win->buffer + bottom_offset, row, stride); | ||
| 2248 | } | ||
| 2249 | |||
| 2250 | RGFW_FREE(row); | ||
| 2251 | } | ||
| 2252 | #endif /* RGFW_OSMesa */ | ||
| 2253 | |||
| 2254 | #endif /* RGFW_GL (OpenGL, EGL, OSMesa )*/ | ||
| 2255 | |||
| 2256 | /* | ||
| 2257 | This is where OS specific stuff starts | ||
| 2258 | */ | ||
| 2259 | |||
| 2260 | |||
| 2261 | #if defined(RGFW_WAYLAND) || defined(RGFW_X11) | ||
| 2262 | int RGFW_eventWait_forceStop[] = {0, 0, 0}; /* for wait events */ | ||
| 2263 | |||
| 2264 | #ifdef __linux__ | ||
| 2265 | #include <linux/joystick.h> | ||
| 2266 | #include <fcntl.h> | ||
| 2267 | #include <unistd.h> | ||
| 2268 | |||
| 2269 | RGFW_Event* RGFW_linux_updateJoystick(RGFW_window* win) { | ||
| 2270 | static int xAxis = 0, yAxis = 0; | ||
| 2271 | u8 i; | ||
| 2272 | for (i = 0; i < RGFW_joystickCount; i++) { | ||
| 2273 | struct js_event e; | ||
| 2274 | |||
| 2275 | |||
| 2276 | if (RGFW_joysticks[i] == 0) | ||
| 2277 | continue; | ||
| 2278 | |||
| 2279 | i32 flags = fcntl(RGFW_joysticks[i], F_GETFL, 0); | ||
| 2280 | fcntl(RGFW_joysticks[i], F_SETFL, flags | O_NONBLOCK); | ||
| 2281 | |||
| 2282 | ssize_t bytes; | ||
| 2283 | while ((bytes = read(RGFW_joysticks[i], &e, sizeof(e))) > 0) { | ||
| 2284 | switch (e.type) { | ||
| 2285 | case JS_EVENT_BUTTON: | ||
| 2286 | win->event.type = e.value ? RGFW_jsButtonPressed : RGFW_jsButtonReleased; | ||
| 2287 | win->event.button = e.number; | ||
| 2288 | RGFW_jsPressed[i][e.number] = e.value; | ||
| 2289 | RGFW_jsButtonCallback(win, i, e.number, e.value); | ||
| 2290 | return &win->event; | ||
| 2291 | case JS_EVENT_AXIS: | ||
| 2292 | ioctl(RGFW_joysticks[i], JSIOCGAXES, &win->event.axisesCount); | ||
| 2293 | |||
| 2294 | if ((e.number == 0 || e.number % 2) && e.number != 1) | ||
| 2295 | xAxis = e.value; | ||
| 2296 | else | ||
| 2297 | yAxis = e.value; | ||
| 2298 | |||
| 2299 | win->event.axis[e.number / 2].x = xAxis; | ||
| 2300 | win->event.axis[e.number / 2].y = yAxis; | ||
| 2301 | win->event.type = RGFW_jsAxisMove; | ||
| 2302 | win->event.joystick = i; | ||
| 2303 | RGFW_jsAxisCallback(win, i, win->event.axis, win->event.axisesCount); | ||
| 2304 | return &win->event; | ||
| 2305 | |||
| 2306 | default: break; | ||
| 2307 | } | ||
| 2308 | } | ||
| 2309 | } | ||
| 2310 | |||
| 2311 | return NULL; | ||
| 2312 | } | ||
| 2313 | |||
| 2314 | #endif | ||
| 2315 | #endif | ||
| 2316 | |||
| 2317 | /* | ||
| 2318 | |||
| 2319 | |||
| 2320 | Start of Linux / Unix defines | ||
| 2321 | |||
| 2322 | |||
| 2323 | */ | ||
| 2324 | |||
| 2325 | #ifdef RGFW_X11 | ||
| 2326 | #ifndef RGFW_NO_X11_CURSOR | ||
| 2327 | #include <X11/Xcursor/Xcursor.h> | ||
| 2328 | #endif | ||
| 2329 | #include <dlfcn.h> | ||
| 2330 | |||
| 2331 | #ifndef RGFW_NO_DPI | ||
| 2332 | #include <X11/extensions/Xrandr.h> | ||
| 2333 | #include <X11/Xresource.h> | ||
| 2334 | #endif | ||
| 2335 | |||
| 2336 | #include <X11/Xutil.h> | ||
| 2337 | #include <X11/Xatom.h> | ||
| 2338 | #include <X11/keysymdef.h> | ||
| 2339 | #include <unistd.h> | ||
| 2340 | |||
| 2341 | #include <X11/XKBlib.h> /* for converting keycode to string */ | ||
| 2342 | #include <X11/cursorfont.h> /* for hiding */ | ||
| 2343 | #include <X11/extensions/shapeconst.h> | ||
| 2344 | #include <X11/extensions/shape.h> | ||
| 2345 | #include <X11/extensions/XInput2.h> | ||
| 2346 | |||
| 2347 | #include <limits.h> /* for data limits (mainly used in drag and drop functions) */ | ||
| 2348 | #include <poll.h> | ||
| 2349 | |||
| 2350 | |||
| 2351 | #ifdef __linux__ | ||
| 2352 | #include <linux/joystick.h> | ||
| 2353 | #endif | ||
| 2354 | |||
| 2355 | u8 RGFW_mouseIconSrc[] = { XC_arrow, XC_left_ptr, XC_xterm, XC_crosshair, XC_hand2, XC_sb_h_double_arrow, XC_sb_v_double_arrow, XC_bottom_left_corner, XC_bottom_right_corner, XC_fleur, XC_X_cursor}; | ||
| 2356 | /*atoms needed for drag and drop*/ | ||
| 2357 | Atom XdndAware, XdndTypeList, XdndSelection, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished, XdndActionCopy, XtextPlain, XtextUriList; | ||
| 2358 | |||
| 2359 | Atom wm_delete_window = 0; | ||
| 2360 | |||
| 2361 | #if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) | ||
| 2362 | typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int); | ||
| 2363 | typedef void (*PFN_XcursorImageDestroy)(XcursorImage*); | ||
| 2364 | typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*); | ||
| 2365 | #endif | ||
| 2366 | #ifdef RGFW_OPENGL | ||
| 2367 | typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); | ||
| 2368 | #endif | ||
| 2369 | |||
| 2370 | #if !defined(RGFW_NO_X11_XI_PRELOAD) | ||
| 2371 | typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); | ||
| 2372 | PFN_XISelectEvents XISelectEventsSrc = NULL; | ||
| 2373 | #define XISelectEvents XISelectEventsSrc | ||
| 2374 | |||
| 2375 | void* X11Xihandle = NULL; | ||
| 2376 | #endif | ||
| 2377 | |||
| 2378 | #if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) | ||
| 2379 | PFN_XcursorImageLoadCursor XcursorImageLoadCursorSrc = NULL; | ||
| 2380 | PFN_XcursorImageCreate XcursorImageCreateSrc = NULL; | ||
| 2381 | PFN_XcursorImageDestroy XcursorImageDestroySrc = NULL; | ||
| 2382 | |||
| 2383 | #define XcursorImageLoadCursor XcursorImageLoadCursorSrc | ||
| 2384 | #define XcursorImageCreate XcursorImageCreateSrc | ||
| 2385 | #define XcursorImageDestroy XcursorImageDestroySrc | ||
| 2386 | |||
| 2387 | void* X11Cursorhandle = NULL; | ||
| 2388 | #endif | ||
| 2389 | |||
| 2390 | u32 RGFW_windowsOpen = 0; | ||
| 2391 | |||
| 2392 | #ifdef RGFW_OPENGL | ||
| 2393 | void* RGFW_getProcAddress(const char* procname) { return (void*) glXGetProcAddress((GLubyte*) procname); } | ||
| 2394 | #endif | ||
| 2395 | |||
| 2396 | RGFWDEF void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi); | ||
| 2397 | void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi) { | ||
| 2398 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 2399 | if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) | ||
| 2400 | RGFW_bufferSize = RGFW_getScreenSize(); | ||
| 2401 | |||
| 2402 | win->buffer = (u8*)RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); | ||
| 2403 | |||
| 2404 | #ifdef RGFW_OSMESA | ||
| 2405 | win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); | ||
| 2406 | OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); | ||
| 2407 | #endif | ||
| 2408 | |||
| 2409 | win->src.bitmap = XCreateImage( | ||
| 2410 | win->src.display, XDefaultVisual(win->src.display, vi->screen), | ||
| 2411 | vi->depth, | ||
| 2412 | ZPixmap, 0, NULL, RGFW_bufferSize.w, RGFW_bufferSize.h, | ||
| 2413 | 32, 0 | ||
| 2414 | ); | ||
| 2415 | |||
| 2416 | win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL); | ||
| 2417 | |||
| 2418 | #else | ||
| 2419 | RGFW_UNUSED(win); /*!< if buffer rendering is not being used */ | ||
| 2420 | RGFW_UNUSED(vi) | ||
| 2421 | #endif | ||
| 2422 | } | ||
| 2423 | |||
| 2424 | |||
| 2425 | |||
| 2426 | void RGFW_window_setBorder(RGFW_window* win, u8 border) { | ||
| 2427 | static Atom _MOTIF_WM_HINTS = 0; | ||
| 2428 | if (_MOTIF_WM_HINTS == 0 ) | ||
| 2429 | _MOTIF_WM_HINTS = XInternAtom(win->src.display, "_MOTIF_WM_HINTS", False); | ||
| 2430 | |||
| 2431 | struct __x11WindowHints { | ||
| 2432 | unsigned long flags, functions, decorations, status; | ||
| 2433 | long input_mode; | ||
| 2434 | } hints; | ||
| 2435 | hints.flags = (1L << 1); | ||
| 2436 | hints.decorations = border; | ||
| 2437 | |||
| 2438 | XChangeProperty( | ||
| 2439 | win->src.display, win->src.window, | ||
| 2440 | _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, | ||
| 2441 | 32, PropModeReplace, (u8*)&hints, 5 | ||
| 2442 | ); | ||
| 2443 | } | ||
| 2444 | |||
| 2445 | void RGFW_releaseCursor(RGFW_window* win) { | ||
| 2446 | XUngrabPointer(win->src.display, CurrentTime); | ||
| 2447 | |||
| 2448 | /* disable raw input */ | ||
| 2449 | unsigned char mask[] = { 0 }; | ||
| 2450 | XIEventMask em; | ||
| 2451 | em.deviceid = XIAllMasterDevices; | ||
| 2452 | em.mask_len = sizeof(mask); | ||
| 2453 | em.mask = mask; | ||
| 2454 | |||
| 2455 | XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); | ||
| 2456 | } | ||
| 2457 | |||
| 2458 | void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { | ||
| 2459 | /* enable raw input */ | ||
| 2460 | unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; | ||
| 2461 | XISetMask(mask, XI_RawMotion); | ||
| 2462 | |||
| 2463 | XIEventMask em; | ||
| 2464 | em.deviceid = XIAllMasterDevices; | ||
| 2465 | em.mask_len = sizeof(mask); | ||
| 2466 | em.mask = mask; | ||
| 2467 | |||
| 2468 | XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); | ||
| 2469 | |||
| 2470 | XGrabPointer(win->src.display, win->src.window, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); | ||
| 2471 | |||
| 2472 | RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (i32)(r.w / 2), win->r.y + (i32)(r.h / 2))); | ||
| 2473 | } | ||
| 2474 | |||
| 2475 | RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { | ||
| 2476 | #if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) | ||
| 2477 | if (X11Cursorhandle == NULL) { | ||
| 2478 | #if defined(__CYGWIN__) | ||
| 2479 | X11Cursorhandle = dlopen("libXcursor-1.so", RTLD_LAZY | RTLD_LOCAL); | ||
| 2480 | #elif defined(__OpenBSD__) || defined(__NetBSD__) | ||
| 2481 | X11Cursorhandle = dlopen("libXcursor.so", RTLD_LAZY | RTLD_LOCAL); | ||
| 2482 | #else | ||
| 2483 | X11Cursorhandle = dlopen("libXcursor.so.1", RTLD_LAZY | RTLD_LOCAL); | ||
| 2484 | #endif | ||
| 2485 | |||
| 2486 | XcursorImageCreateSrc = (PFN_XcursorImageCreate) dlsym(X11Cursorhandle, "XcursorImageCreate"); | ||
| 2487 | XcursorImageDestroySrc = (PFN_XcursorImageDestroy) dlsym(X11Cursorhandle, "XcursorImageDestroy"); | ||
| 2488 | XcursorImageLoadCursorSrc = (PFN_XcursorImageLoadCursor) dlsym(X11Cursorhandle, "XcursorImageLoadCursor"); | ||
| 2489 | } | ||
| 2490 | #endif | ||
| 2491 | |||
| 2492 | #if !defined(RGFW_NO_X11_XI_PRELOAD) | ||
| 2493 | if (X11Xihandle == NULL) { | ||
| 2494 | #if defined(__CYGWIN__) | ||
| 2495 | X11Xihandle = dlopen("libXi-6.so", RTLD_LAZY | RTLD_LOCAL); | ||
| 2496 | #elif defined(__OpenBSD__) || defined(__NetBSD__) | ||
| 2497 | X11Xihandle = dlopen("libXi.so", RTLD_LAZY | RTLD_LOCAL); | ||
| 2498 | #else | ||
| 2499 | X11Xihandle = dlopen("libXi.so.6", RTLD_LAZY | RTLD_LOCAL); | ||
| 2500 | #endif | ||
| 2501 | |||
| 2502 | XISelectEventsSrc = (PFN_XISelectEvents) dlsym(X11Xihandle, "XISelectEvents"); | ||
| 2503 | } | ||
| 2504 | #endif | ||
| 2505 | |||
| 2506 | XInitThreads(); /*!< init X11 threading*/ | ||
| 2507 | |||
| 2508 | if (args & RGFW_OPENGL_SOFTWARE) | ||
| 2509 | setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); | ||
| 2510 | |||
| 2511 | RGFW_window* win = RGFW_window_basic_init(rect, args); | ||
| 2512 | |||
| 2513 | u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /*!< X11 events accepted*/ | ||
| 2514 | |||
| 2515 | #ifdef RGFW_OPENGL | ||
| 2516 | u32* visual_attribs = RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE); | ||
| 2517 | i32 fbcount; | ||
| 2518 | GLXFBConfig* fbc = glXChooseFBConfig((Display*) win->src.display, DefaultScreen(win->src.display), (i32*) visual_attribs, &fbcount); | ||
| 2519 | |||
| 2520 | i32 best_fbc = -1; | ||
| 2521 | |||
| 2522 | if (fbcount == 0) { | ||
| 2523 | printf("Failed to find any valid GLX visual configs\n"); | ||
| 2524 | return NULL; | ||
| 2525 | } | ||
| 2526 | |||
| 2527 | u32 i; | ||
| 2528 | for (i = 0; i < (u32)fbcount; i++) { | ||
| 2529 | XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) win->src.display, fbc[i]); | ||
| 2530 | if (vi == NULL) | ||
| 2531 | continue; | ||
| 2532 | |||
| 2533 | XFree(vi); | ||
| 2534 | |||
| 2535 | i32 samp_buf, samples; | ||
| 2536 | glXGetFBConfigAttrib((Display*) win->src.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); | ||
| 2537 | glXGetFBConfigAttrib((Display*) win->src.display, fbc[i], GLX_SAMPLES, &samples); | ||
| 2538 | |||
| 2539 | if ((!(args & RGFW_TRANSPARENT_WINDOW) || vi->depth == 32) && | ||
| 2540 | (best_fbc < 0 || samp_buf) && (samples == RGFW_SAMPLES || best_fbc == -1)) { | ||
| 2541 | best_fbc = i; | ||
| 2542 | } | ||
| 2543 | } | ||
| 2544 | |||
| 2545 | if (best_fbc == -1) { | ||
| 2546 | printf("Failed to get a valid GLX visual\n"); | ||
| 2547 | return NULL; | ||
| 2548 | } | ||
| 2549 | |||
| 2550 | GLXFBConfig bestFbc = fbc[best_fbc]; | ||
| 2551 | |||
| 2552 | /* Get a visual */ | ||
| 2553 | XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) win->src.display, bestFbc); | ||
| 2554 | |||
| 2555 | XFree(fbc); | ||
| 2556 | #else | ||
| 2557 | XVisualInfo viNorm; | ||
| 2558 | |||
| 2559 | viNorm.visual = DefaultVisual((Display*) win->src.display, DefaultScreen((Display*) win->src.display)); | ||
| 2560 | |||
| 2561 | viNorm.depth = 0; | ||
| 2562 | XVisualInfo* vi = &viNorm; | ||
| 2563 | |||
| 2564 | XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /*!< for RGBA backgrounds*/ | ||
| 2565 | #endif | ||
| 2566 | /* make X window attrubutes*/ | ||
| 2567 | XSetWindowAttributes swa; | ||
| 2568 | Colormap cmap; | ||
| 2569 | |||
| 2570 | swa.colormap = cmap = XCreateColormap((Display*) win->src.display, | ||
| 2571 | DefaultRootWindow(win->src.display), | ||
| 2572 | vi->visual, AllocNone); | ||
| 2573 | |||
| 2574 | swa.background_pixmap = None; | ||
| 2575 | swa.border_pixel = 0; | ||
| 2576 | swa.event_mask = event_mask; | ||
| 2577 | |||
| 2578 | swa.background_pixel = 0; | ||
| 2579 | |||
| 2580 | /* create the window*/ | ||
| 2581 | win->src.window = XCreateWindow((Display*) win->src.display, DefaultRootWindow((Display*) win->src.display), win->r.x, win->r.y, win->r.w, win->r.h, | ||
| 2582 | 0, vi->depth, InputOutput, vi->visual, | ||
| 2583 | CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa); | ||
| 2584 | |||
| 2585 | XFreeColors((Display*) win->src.display, cmap, NULL, 0, 0); | ||
| 2586 | |||
| 2587 | #ifdef RGFW_OPENGL | ||
| 2588 | XFree(vi); | ||
| 2589 | #endif | ||
| 2590 | |||
| 2591 | // In your .desktop app, if you set the property | ||
| 2592 | // StartupWMClass=RGFW that will assoicate the launcher icon | ||
| 2593 | // with your application - robrohan | ||
| 2594 | |||
| 2595 | if (RGFW_className == NULL) | ||
| 2596 | RGFW_className = (char*)name; | ||
| 2597 | |||
| 2598 | XClassHint *hint = XAllocClassHint(); | ||
| 2599 | assert(hint != NULL); | ||
| 2600 | hint->res_class = (char*)RGFW_className; | ||
| 2601 | hint->res_name = (char*)name; // just use the window name as the app name | ||
| 2602 | XSetClassHint((Display*) win->src.display, win->src.window, hint); | ||
| 2603 | XFree(hint); | ||
| 2604 | |||
| 2605 | if ((args & RGFW_NO_INIT_API) == 0) { | ||
| 2606 | #ifdef RGFW_OPENGL /* This is the second part of setting up opengl. This is where we ask OpenGL for a specific version. */ | ||
| 2607 | i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 }; | ||
| 2608 | context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB; | ||
| 2609 | if (RGFW_profile == RGFW_GL_CORE) | ||
| 2610 | context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; | ||
| 2611 | else | ||
| 2612 | context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; | ||
| 2613 | |||
| 2614 | if (RGFW_majorVersion || RGFW_minorVersion) { | ||
| 2615 | context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB; | ||
| 2616 | context_attribs[3] = RGFW_majorVersion; | ||
| 2617 | context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB; | ||
| 2618 | context_attribs[5] = RGFW_minorVersion; | ||
| 2619 | } | ||
| 2620 | |||
| 2621 | glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; | ||
| 2622 | glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) | ||
| 2623 | glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB"); | ||
| 2624 | |||
| 2625 | GLXContext ctx = NULL; | ||
| 2626 | |||
| 2627 | if (RGFW_root != NULL) | ||
| 2628 | ctx = RGFW_root->src.ctx; | ||
| 2629 | |||
| 2630 | win->src.ctx = glXCreateContextAttribsARB((Display*) win->src.display, bestFbc, ctx, True, context_attribs); | ||
| 2631 | #endif | ||
| 2632 | if (RGFW_root == NULL) | ||
| 2633 | RGFW_root = win; | ||
| 2634 | |||
| 2635 | RGFW_init_buffer(win, vi); | ||
| 2636 | } | ||
| 2637 | |||
| 2638 | |||
| 2639 | #ifndef RGFW_NO_MONITOR | ||
| 2640 | if (args & RGFW_SCALE_TO_MONITOR) | ||
| 2641 | RGFW_window_scaleToMonitor(win); | ||
| 2642 | #endif | ||
| 2643 | |||
| 2644 | if (args & RGFW_CENTER) { | ||
| 2645 | RGFW_area screenR = RGFW_getScreenSize(); | ||
| 2646 | RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); | ||
| 2647 | } | ||
| 2648 | |||
| 2649 | if (args & RGFW_NO_RESIZE) { /* make it so the user can't resize the window*/ | ||
| 2650 | XSizeHints* sh = XAllocSizeHints(); | ||
| 2651 | sh->flags = (1L << 4) | (1L << 5); | ||
| 2652 | sh->min_width = sh->max_width = win->r.w; | ||
| 2653 | sh->min_height = sh->max_height = win->r.h; | ||
| 2654 | |||
| 2655 | XSetWMSizeHints((Display*) win->src.display, (Drawable) win->src.window, sh, XA_WM_NORMAL_HINTS); | ||
| 2656 | XFree(sh); | ||
| 2657 | } | ||
| 2658 | |||
| 2659 | if (args & RGFW_NO_BORDER) { | ||
| 2660 | RGFW_window_setBorder(win, 0); | ||
| 2661 | } | ||
| 2662 | |||
| 2663 | XSelectInput((Display*) win->src.display, (Drawable) win->src.window, event_mask); /*!< tell X11 what events we want*/ | ||
| 2664 | |||
| 2665 | /* make it so the user can't close the window until the program does*/ | ||
| 2666 | if (wm_delete_window == 0) | ||
| 2667 | wm_delete_window = XInternAtom((Display*) win->src.display, "WM_DELETE_WINDOW", False); | ||
| 2668 | |||
| 2669 | XSetWMProtocols((Display*) win->src.display, (Drawable) win->src.window, &wm_delete_window, 1); | ||
| 2670 | |||
| 2671 | /* connect the context to the window*/ | ||
| 2672 | #ifdef RGFW_OPENGL | ||
| 2673 | if ((args & RGFW_NO_INIT_API) == 0) | ||
| 2674 | glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); | ||
| 2675 | #endif | ||
| 2676 | |||
| 2677 | /* set the background*/ | ||
| 2678 | XStoreName((Display*) win->src.display, (Drawable) win->src.window, name); /*!< set the name*/ | ||
| 2679 | |||
| 2680 | XMapWindow((Display*) win->src.display, (Drawable) win->src.window); /* draw the window*/ | ||
| 2681 | XMoveWindow((Display*) win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /*!< move the window to it's proper cords*/ | ||
| 2682 | |||
| 2683 | if (args & RGFW_ALLOW_DND) { /* init drag and drop atoms and turn on drag and drop for this window */ | ||
| 2684 | win->_winArgs |= RGFW_ALLOW_DND; | ||
| 2685 | |||
| 2686 | XdndTypeList = XInternAtom((Display*) win->src.display, "XdndTypeList", False); | ||
| 2687 | XdndSelection = XInternAtom((Display*) win->src.display, "XdndSelection", False); | ||
| 2688 | |||
| 2689 | /* client messages */ | ||
| 2690 | XdndEnter = XInternAtom((Display*) win->src.display, "XdndEnter", False); | ||
| 2691 | XdndPosition = XInternAtom((Display*) win->src.display, "XdndPosition", False); | ||
| 2692 | XdndStatus = XInternAtom((Display*) win->src.display, "XdndStatus", False); | ||
| 2693 | XdndLeave = XInternAtom((Display*) win->src.display, "XdndLeave", False); | ||
| 2694 | XdndDrop = XInternAtom((Display*) win->src.display, "XdndDrop", False); | ||
| 2695 | XdndFinished = XInternAtom((Display*) win->src.display, "XdndFinished", False); | ||
| 2696 | |||
| 2697 | /* actions */ | ||
| 2698 | XdndActionCopy = XInternAtom((Display*) win->src.display, "XdndActionCopy", False); | ||
| 2699 | |||
| 2700 | XtextUriList = XInternAtom((Display*) win->src.display, "text/uri-list", False); | ||
| 2701 | XtextPlain = XInternAtom((Display*) win->src.display, "text/plain", False); | ||
| 2702 | |||
| 2703 | XdndAware = XInternAtom((Display*) win->src.display, "XdndAware", False); | ||
| 2704 | const u8 version = 5; | ||
| 2705 | |||
| 2706 | XChangeProperty((Display*) win->src.display, (Window) win->src.window, | ||
| 2707 | XdndAware, 4, 32, | ||
| 2708 | PropModeReplace, &version, 1); /*!< turns on drag and drop */ | ||
| 2709 | } | ||
| 2710 | |||
| 2711 | #ifdef RGFW_EGL | ||
| 2712 | if ((args & RGFW_NO_INIT_API) == 0) | ||
| 2713 | RGFW_createOpenGLContext(win); | ||
| 2714 | #endif | ||
| 2715 | |||
| 2716 | RGFW_window_setMouseDefault(win); | ||
| 2717 | |||
| 2718 | RGFW_windowsOpen++; | ||
| 2719 | |||
| 2720 | return win; /*return newly created window*/ | ||
| 2721 | } | ||
| 2722 | |||
| 2723 | RGFW_area RGFW_getScreenSize(void) { | ||
| 2724 | assert(RGFW_root != NULL); | ||
| 2725 | |||
| 2726 | Screen* scrn = DefaultScreenOfDisplay((Display*) RGFW_root->src.display); | ||
| 2727 | return RGFW_AREA(scrn->width, scrn->height); | ||
| 2728 | } | ||
| 2729 | |||
| 2730 | RGFW_point RGFW_getGlobalMousePoint(void) { | ||
| 2731 | assert(RGFW_root != NULL); | ||
| 2732 | |||
| 2733 | RGFW_point RGFWMouse; | ||
| 2734 | |||
| 2735 | i32 x, y; | ||
| 2736 | u32 z; | ||
| 2737 | Window window1, window2; | ||
| 2738 | XQueryPointer((Display*) RGFW_root->src.display, XDefaultRootWindow((Display*) RGFW_root->src.display), &window1, &window2, &RGFWMouse.x, &RGFWMouse.y, &x, &y, &z); | ||
| 2739 | |||
| 2740 | return RGFWMouse; | ||
| 2741 | } | ||
| 2742 | |||
| 2743 | RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { | ||
| 2744 | assert(win != NULL); | ||
| 2745 | |||
| 2746 | RGFW_point RGFWMouse; | ||
| 2747 | |||
| 2748 | i32 x, y; | ||
| 2749 | u32 z; | ||
| 2750 | Window window1, window2; | ||
| 2751 | XQueryPointer((Display*) win->src.display, win->src.window, &window1, &window2, &x, &y, &RGFWMouse.x, &RGFWMouse.y, &z); | ||
| 2752 | |||
| 2753 | return RGFWMouse; | ||
| 2754 | } | ||
| 2755 | |||
| 2756 | int xAxis = 0, yAxis = 0; | ||
| 2757 | |||
| 2758 | RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { | ||
| 2759 | assert(win != NULL); | ||
| 2760 | |||
| 2761 | static struct { | ||
| 2762 | long source, version; | ||
| 2763 | i32 format; | ||
| 2764 | } xdnd; | ||
| 2765 | |||
| 2766 | if (win->event.type == 0) | ||
| 2767 | RGFW_resetKey(); | ||
| 2768 | |||
| 2769 | if (win->event.type == RGFW_quit) { | ||
| 2770 | return NULL; | ||
| 2771 | } | ||
| 2772 | |||
| 2773 | win->event.type = 0; | ||
| 2774 | |||
| 2775 | #ifdef __linux__ | ||
| 2776 | RGFW_Event* event = RGFW_linux_updateJoystick(win); | ||
| 2777 | if (event != NULL) | ||
| 2778 | return event; | ||
| 2779 | #endif | ||
| 2780 | |||
| 2781 | XPending(win->src.display); | ||
| 2782 | |||
| 2783 | XEvent E; /*!< raw X11 event */ | ||
| 2784 | |||
| 2785 | /* if there is no unread qued events, get a new one */ | ||
| 2786 | if ((QLength(win->src.display) || XEventsQueued((Display*) win->src.display, QueuedAlready) + XEventsQueued((Display*) win->src.display, QueuedAfterReading)) | ||
| 2787 | && win->event.type != RGFW_quit | ||
| 2788 | ) | ||
| 2789 | XNextEvent((Display*) win->src.display, &E); | ||
| 2790 | else { | ||
| 2791 | return NULL; | ||
| 2792 | } | ||
| 2793 | |||
| 2794 | u32 i; | ||
| 2795 | win->event.type = 0; | ||
| 2796 | |||
| 2797 | |||
| 2798 | switch (E.type) { | ||
| 2799 | case KeyPress: | ||
| 2800 | case KeyRelease: { | ||
| 2801 | win->event.repeat = RGFW_FALSE; | ||
| 2802 | /* check if it's a real key release */ | ||
| 2803 | if (E.type == KeyRelease && XEventsQueued((Display*) win->src.display, QueuedAfterReading)) { /* get next event if there is one*/ | ||
| 2804 | XEvent NE; | ||
| 2805 | XPeekEvent((Display*) win->src.display, &NE); | ||
| 2806 | |||
| 2807 | if (E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) /* check if the current and next are both the same*/ | ||
| 2808 | win->event.repeat = RGFW_TRUE; | ||
| 2809 | } | ||
| 2810 | |||
| 2811 | /* set event key data */ | ||
| 2812 | KeySym sym = (KeySym)XkbKeycodeToKeysym((Display*) win->src.display, E.xkey.keycode, 0, E.xkey.state & ShiftMask ? 1 : 0); | ||
| 2813 | win->event.keyCode = RGFW_apiKeyCodeToRGFW(E.xkey.keycode); | ||
| 2814 | |||
| 2815 | char* str = (char*)XKeysymToString(sym); | ||
| 2816 | if (str != NULL) | ||
| 2817 | strncpy(win->event.keyName, str, 16); | ||
| 2818 | |||
| 2819 | win->event.keyName[15] = '\0'; | ||
| 2820 | |||
| 2821 | RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode); | ||
| 2822 | |||
| 2823 | /* get keystate data */ | ||
| 2824 | win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased; | ||
| 2825 | |||
| 2826 | XKeyboardState keystate; | ||
| 2827 | XGetKeyboardControl((Display*) win->src.display, &keystate); | ||
| 2828 | |||
| 2829 | RGFW_updateLockState(win, (keystate.led_mask & 1), (keystate.led_mask & 2)); | ||
| 2830 | RGFW_keyboard[win->event.keyCode].current = (E.type == KeyPress); | ||
| 2831 | RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, (E.type == KeyPress)); | ||
| 2832 | break; | ||
| 2833 | } | ||
| 2834 | case ButtonPress: | ||
| 2835 | case ButtonRelease: | ||
| 2836 | win->event.type = RGFW_mouseButtonPressed + (E.type == ButtonRelease); // the events match | ||
| 2837 | |||
| 2838 | switch(win->event.button) { | ||
| 2839 | case RGFW_mouseScrollUp: | ||
| 2840 | win->event.scroll = 1; | ||
| 2841 | break; | ||
| 2842 | case RGFW_mouseScrollDown: | ||
| 2843 | win->event.scroll = -1; | ||
| 2844 | break; | ||
| 2845 | default: break; | ||
| 2846 | } | ||
| 2847 | |||
| 2848 | win->event.button = E.xbutton.button; | ||
| 2849 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 2850 | |||
| 2851 | if (win->event.repeat == RGFW_FALSE) | ||
| 2852 | win->event.repeat = RGFW_isPressed(win, win->event.keyCode); | ||
| 2853 | |||
| 2854 | RGFW_mouseButtons[win->event.button].current = (E.type == ButtonPress); | ||
| 2855 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, (E.type == ButtonPress)); | ||
| 2856 | break; | ||
| 2857 | |||
| 2858 | case MotionNotify: | ||
| 2859 | win->event.point.x = E.xmotion.x; | ||
| 2860 | win->event.point.y = E.xmotion.y; | ||
| 2861 | |||
| 2862 | if ((win->_winArgs & RGFW_HOLD_MOUSE)) { | ||
| 2863 | win->event.point.y = E.xmotion.y; | ||
| 2864 | |||
| 2865 | win->event.point.x = win->_lastMousePoint.x - abs(win->event.point.x); | ||
| 2866 | win->event.point.y = win->_lastMousePoint.y - abs(win->event.point.y); | ||
| 2867 | } | ||
| 2868 | |||
| 2869 | win->_lastMousePoint = RGFW_POINT(E.xmotion.x, E.xmotion.y); | ||
| 2870 | |||
| 2871 | win->event.type = RGFW_mousePosChanged; | ||
| 2872 | RGFW_mousePosCallback(win, win->event.point); | ||
| 2873 | break; | ||
| 2874 | |||
| 2875 | case GenericEvent: { | ||
| 2876 | /* MotionNotify is used for mouse events if the mouse isn't held */ | ||
| 2877 | if (!(win->_winArgs & RGFW_HOLD_MOUSE)) { | ||
| 2878 | XFreeEventData(win->src.display, &E.xcookie); | ||
| 2879 | break; | ||
| 2880 | } | ||
| 2881 | |||
| 2882 | XGetEventData(win->src.display, &E.xcookie); | ||
| 2883 | if (E.xcookie.evtype == XI_RawMotion) { | ||
| 2884 | XIRawEvent *raw = (XIRawEvent *)E.xcookie.data; | ||
| 2885 | if (raw->valuators.mask_len == 0) { | ||
| 2886 | XFreeEventData(win->src.display, &E.xcookie); | ||
| 2887 | break; | ||
| 2888 | } | ||
| 2889 | |||
| 2890 | double deltaX = 0.0f; | ||
| 2891 | double deltaY = 0.0f; | ||
| 2892 | |||
| 2893 | /* check if relative motion data exists where we think it does */ | ||
| 2894 | if (XIMaskIsSet(raw->valuators.mask, 0) != 0) | ||
| 2895 | deltaX += raw->raw_values[0]; | ||
| 2896 | if (XIMaskIsSet(raw->valuators.mask, 1) != 0) | ||
| 2897 | deltaY += raw->raw_values[1]; | ||
| 2898 | |||
| 2899 | win->event.point = RGFW_POINT((i32)deltaX, (i32)deltaY); | ||
| 2900 | |||
| 2901 | RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); | ||
| 2902 | |||
| 2903 | win->event.type = RGFW_mousePosChanged; | ||
| 2904 | RGFW_mousePosCallback(win, win->event.point); | ||
| 2905 | } | ||
| 2906 | |||
| 2907 | XFreeEventData(win->src.display, &E.xcookie); | ||
| 2908 | break; | ||
| 2909 | } | ||
| 2910 | |||
| 2911 | case Expose: | ||
| 2912 | win->event.type = RGFW_windowRefresh; | ||
| 2913 | RGFW_windowRefreshCallback(win); | ||
| 2914 | break; | ||
| 2915 | |||
| 2916 | case ClientMessage: | ||
| 2917 | /* if the client closed the window*/ | ||
| 2918 | if (E.xclient.data.l[0] == (i64) wm_delete_window) { | ||
| 2919 | win->event.type = RGFW_quit; | ||
| 2920 | RGFW_windowQuitCallback(win); | ||
| 2921 | break; | ||
| 2922 | } | ||
| 2923 | |||
| 2924 | /* reset DND values */ | ||
| 2925 | if (win->event.droppedFilesCount) { | ||
| 2926 | for (i = 0; i < win->event.droppedFilesCount; i++) | ||
| 2927 | win->event.droppedFiles[i][0] = '\0'; | ||
| 2928 | } | ||
| 2929 | |||
| 2930 | win->event.droppedFilesCount = 0; | ||
| 2931 | |||
| 2932 | if ((win->_winArgs & RGFW_ALLOW_DND) == 0) | ||
| 2933 | break; | ||
| 2934 | |||
| 2935 | XEvent reply = { ClientMessage }; | ||
| 2936 | reply.xclient.window = xdnd.source; | ||
| 2937 | reply.xclient.format = 32; | ||
| 2938 | reply.xclient.data.l[0] = (long) win->src.window; | ||
| 2939 | reply.xclient.data.l[1] = 0; | ||
| 2940 | reply.xclient.data.l[2] = None; | ||
| 2941 | |||
| 2942 | if (E.xclient.message_type == XdndEnter) { | ||
| 2943 | unsigned long count; | ||
| 2944 | Atom* formats; | ||
| 2945 | Atom real_formats[6]; | ||
| 2946 | |||
| 2947 | Bool list = E.xclient.data.l[1] & 1; | ||
| 2948 | |||
| 2949 | xdnd.source = E.xclient.data.l[0]; | ||
| 2950 | xdnd.version = E.xclient.data.l[1] >> 24; | ||
| 2951 | xdnd.format = None; | ||
| 2952 | |||
| 2953 | if (xdnd.version > 5) | ||
| 2954 | break; | ||
| 2955 | |||
| 2956 | if (list) { | ||
| 2957 | Atom actualType; | ||
| 2958 | i32 actualFormat; | ||
| 2959 | unsigned long bytesAfter; | ||
| 2960 | |||
| 2961 | XGetWindowProperty((Display*) win->src.display, | ||
| 2962 | xdnd.source, | ||
| 2963 | XdndTypeList, | ||
| 2964 | 0, | ||
| 2965 | LONG_MAX, | ||
| 2966 | False, | ||
| 2967 | 4, | ||
| 2968 | &actualType, | ||
| 2969 | &actualFormat, | ||
| 2970 | &count, | ||
| 2971 | &bytesAfter, | ||
| 2972 | (u8**) &formats); | ||
| 2973 | } else { | ||
| 2974 | count = 0; | ||
| 2975 | |||
| 2976 | if (E.xclient.data.l[2] != None) | ||
| 2977 | real_formats[count++] = E.xclient.data.l[2]; | ||
| 2978 | if (E.xclient.data.l[3] != None) | ||
| 2979 | real_formats[count++] = E.xclient.data.l[3]; | ||
| 2980 | if (E.xclient.data.l[4] != None) | ||
| 2981 | real_formats[count++] = E.xclient.data.l[4]; | ||
| 2982 | |||
| 2983 | formats = real_formats; | ||
| 2984 | } | ||
| 2985 | |||
| 2986 | unsigned long i; | ||
| 2987 | for (i = 0; i < count; i++) { | ||
| 2988 | if (formats[i] == XtextUriList || formats[i] == XtextPlain) { | ||
| 2989 | xdnd.format = formats[i]; | ||
| 2990 | break; | ||
| 2991 | } | ||
| 2992 | } | ||
| 2993 | |||
| 2994 | if (list) { | ||
| 2995 | XFree(formats); | ||
| 2996 | } | ||
| 2997 | |||
| 2998 | break; | ||
| 2999 | } | ||
| 3000 | if (E.xclient.message_type == XdndPosition) { | ||
| 3001 | const i32 xabs = (E.xclient.data.l[2] >> 16) & 0xffff; | ||
| 3002 | const i32 yabs = (E.xclient.data.l[2]) & 0xffff; | ||
| 3003 | Window dummy; | ||
| 3004 | i32 xpos, ypos; | ||
| 3005 | |||
| 3006 | if (xdnd.version > 5) | ||
| 3007 | break; | ||
| 3008 | |||
| 3009 | XTranslateCoordinates((Display*) win->src.display, | ||
| 3010 | XDefaultRootWindow((Display*) win->src.display), | ||
| 3011 | (Window) win->src.window, | ||
| 3012 | xabs, yabs, | ||
| 3013 | &xpos, &ypos, | ||
| 3014 | &dummy); | ||
| 3015 | |||
| 3016 | win->event.point.x = xpos; | ||
| 3017 | win->event.point.y = ypos; | ||
| 3018 | |||
| 3019 | reply.xclient.window = xdnd.source; | ||
| 3020 | reply.xclient.message_type = XdndStatus; | ||
| 3021 | |||
| 3022 | if (xdnd.format) { | ||
| 3023 | reply.xclient.data.l[1] = 1; | ||
| 3024 | if (xdnd.version >= 2) | ||
| 3025 | reply.xclient.data.l[4] = XdndActionCopy; | ||
| 3026 | } | ||
| 3027 | |||
| 3028 | XSendEvent((Display*) win->src.display, xdnd.source, False, NoEventMask, &reply); | ||
| 3029 | XFlush((Display*) win->src.display); | ||
| 3030 | break; | ||
| 3031 | } | ||
| 3032 | |||
| 3033 | if (E.xclient.message_type != XdndDrop) | ||
| 3034 | break; | ||
| 3035 | |||
| 3036 | if (xdnd.version > 5) | ||
| 3037 | break; | ||
| 3038 | |||
| 3039 | win->event.type = RGFW_dnd_init; | ||
| 3040 | |||
| 3041 | if (xdnd.format) { | ||
| 3042 | Time time = CurrentTime; | ||
| 3043 | |||
| 3044 | if (xdnd.version >= 1) | ||
| 3045 | time = E.xclient.data.l[2]; | ||
| 3046 | |||
| 3047 | XConvertSelection((Display*) win->src.display, | ||
| 3048 | XdndSelection, | ||
| 3049 | xdnd.format, | ||
| 3050 | XdndSelection, | ||
| 3051 | (Window) win->src.window, | ||
| 3052 | time); | ||
| 3053 | } else if (xdnd.version >= 2) { | ||
| 3054 | XEvent reply = { ClientMessage }; | ||
| 3055 | |||
| 3056 | XSendEvent((Display*) win->src.display, xdnd.source, | ||
| 3057 | False, NoEventMask, &reply); | ||
| 3058 | XFlush((Display*) win->src.display); | ||
| 3059 | } | ||
| 3060 | |||
| 3061 | RGFW_dndInitCallback(win, win->event.point); | ||
| 3062 | break; | ||
| 3063 | case SelectionNotify: { | ||
| 3064 | /* this is only for checking for xdnd drops */ | ||
| 3065 | if (E.xselection.property != XdndSelection || !(win->_winArgs | RGFW_ALLOW_DND)) | ||
| 3066 | break; | ||
| 3067 | |||
| 3068 | char* data; | ||
| 3069 | unsigned long result; | ||
| 3070 | |||
| 3071 | Atom actualType; | ||
| 3072 | i32 actualFormat; | ||
| 3073 | unsigned long bytesAfter; | ||
| 3074 | |||
| 3075 | XGetWindowProperty((Display*) win->src.display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data); | ||
| 3076 | |||
| 3077 | if (result == 0) | ||
| 3078 | break; | ||
| 3079 | |||
| 3080 | /* | ||
| 3081 | SOURCED FROM GLFW _glfwParseUriList | ||
| 3082 | Copyright (c) 2002-2006 Marcus Geelnard | ||
| 3083 | Copyright (c) 2006-2019 Camilla Löwy | ||
| 3084 | */ | ||
| 3085 | |||
| 3086 | const char* prefix = (const char*)"file://"; | ||
| 3087 | |||
| 3088 | char* line; | ||
| 3089 | |||
| 3090 | win->event.droppedFilesCount = 0; | ||
| 3091 | |||
| 3092 | win->event.type = RGFW_dnd; | ||
| 3093 | |||
| 3094 | while ((line = strtok(data, "\r\n"))) { | ||
| 3095 | char path[RGFW_MAX_PATH]; | ||
| 3096 | |||
| 3097 | data = NULL; | ||
| 3098 | |||
| 3099 | if (line[0] == '#') | ||
| 3100 | continue; | ||
| 3101 | |||
| 3102 | char* l; | ||
| 3103 | for (l = line; 1; l++) { | ||
| 3104 | if ((l - line) > 7) | ||
| 3105 | break; | ||
| 3106 | else if (*l != prefix[(l - line)]) | ||
| 3107 | break; | ||
| 3108 | else if (*l == '\0' && prefix[(l - line)] == '\0') { | ||
| 3109 | line += 7; | ||
| 3110 | while (*line != '/') | ||
| 3111 | line++; | ||
| 3112 | break; | ||
| 3113 | } else if (*l == '\0') | ||
| 3114 | break; | ||
| 3115 | } | ||
| 3116 | |||
| 3117 | win->event.droppedFilesCount++; | ||
| 3118 | |||
| 3119 | size_t index = 0; | ||
| 3120 | while (*line) { | ||
| 3121 | if (line[0] == '%' && line[1] && line[2]) { | ||
| 3122 | const char digits[3] = { line[1], line[2], '\0' }; | ||
| 3123 | path[index] = (char) strtol(digits, NULL, 16); | ||
| 3124 | line += 2; | ||
| 3125 | } else | ||
| 3126 | path[index] = *line; | ||
| 3127 | |||
| 3128 | index++; | ||
| 3129 | line++; | ||
| 3130 | } | ||
| 3131 | path[index] = '\0'; | ||
| 3132 | strncpy(win->event.droppedFiles[win->event.droppedFilesCount - 1], path, index + 1); | ||
| 3133 | } | ||
| 3134 | |||
| 3135 | if (data) | ||
| 3136 | XFree(data); | ||
| 3137 | |||
| 3138 | if (xdnd.version >= 2) { | ||
| 3139 | reply.xclient.message_type = XdndFinished; | ||
| 3140 | reply.xclient.data.l[1] = result; | ||
| 3141 | reply.xclient.data.l[2] = XdndActionCopy; | ||
| 3142 | |||
| 3143 | XSendEvent((Display*) win->src.display, xdnd.source, False, NoEventMask, &reply); | ||
| 3144 | XFlush((Display*) win->src.display); | ||
| 3145 | } | ||
| 3146 | |||
| 3147 | RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); | ||
| 3148 | break; | ||
| 3149 | } | ||
| 3150 | case FocusIn: | ||
| 3151 | win->event.inFocus = 1; | ||
| 3152 | win->event.type = RGFW_focusIn; | ||
| 3153 | RGFW_focusCallback(win, 1); | ||
| 3154 | break; | ||
| 3155 | |||
| 3156 | break; | ||
| 3157 | case FocusOut: | ||
| 3158 | win->event.inFocus = 0; | ||
| 3159 | win->event.type = RGFW_focusOut; | ||
| 3160 | RGFW_focusCallback(win, 0); | ||
| 3161 | break; | ||
| 3162 | |||
| 3163 | case EnterNotify: { | ||
| 3164 | win->event.type = RGFW_mouseEnter; | ||
| 3165 | win->event.point.x = E.xcrossing.x; | ||
| 3166 | win->event.point.y = E.xcrossing.y; | ||
| 3167 | RGFW_mouseNotifyCallBack(win, win->event.point, 1); | ||
| 3168 | break; | ||
| 3169 | } | ||
| 3170 | |||
| 3171 | case LeaveNotify: { | ||
| 3172 | win->event.type = RGFW_mouseLeave; | ||
| 3173 | RGFW_mouseNotifyCallBack(win, win->event.point, 0); | ||
| 3174 | break; | ||
| 3175 | } | ||
| 3176 | |||
| 3177 | case ConfigureNotify: { | ||
| 3178 | /* detect resize */ | ||
| 3179 | if (E.xconfigure.width != win->r.w || E.xconfigure.height != win->r.h) { | ||
| 3180 | win->event.type = RGFW_windowResized; | ||
| 3181 | win->r = RGFW_RECT(win->r.x, win->r.y, E.xconfigure.width, E.xconfigure.height); | ||
| 3182 | RGFW_windowResizeCallback(win, win->r); | ||
| 3183 | break; | ||
| 3184 | } | ||
| 3185 | |||
| 3186 | /* detect move */ | ||
| 3187 | if (E.xconfigure.x != win->r.x || E.xconfigure.y != win->r.y) { | ||
| 3188 | win->event.type = RGFW_windowMoved; | ||
| 3189 | win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->r.w, win->r.h); | ||
| 3190 | RGFW_windowMoveCallback(win, win->r); | ||
| 3191 | break; | ||
| 3192 | } | ||
| 3193 | |||
| 3194 | break; | ||
| 3195 | } | ||
| 3196 | default: { | ||
| 3197 | break; | ||
| 3198 | } | ||
| 3199 | } | ||
| 3200 | |||
| 3201 | XFlush((Display*) win->src.display); | ||
| 3202 | |||
| 3203 | if (win->event.type) | ||
| 3204 | return &win->event; | ||
| 3205 | else | ||
| 3206 | return NULL; | ||
| 3207 | } | ||
| 3208 | |||
| 3209 | void RGFW_window_move(RGFW_window* win, RGFW_point v) { | ||
| 3210 | assert(win != NULL); | ||
| 3211 | win->r.x = v.x; | ||
| 3212 | win->r.y = v.y; | ||
| 3213 | |||
| 3214 | XMoveWindow((Display*) win->src.display, (Window) win->src.window, v.x, v.y); | ||
| 3215 | } | ||
| 3216 | |||
| 3217 | |||
| 3218 | void RGFW_window_resize(RGFW_window* win, RGFW_area a) { | ||
| 3219 | assert(win != NULL); | ||
| 3220 | win->r.w = a.w; | ||
| 3221 | win->r.h = a.h; | ||
| 3222 | |||
| 3223 | XResizeWindow((Display*) win->src.display, (Window) win->src.window, a.w, a.h); | ||
| 3224 | } | ||
| 3225 | |||
| 3226 | void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { | ||
| 3227 | assert(win != NULL); | ||
| 3228 | |||
| 3229 | if (a.w == 0 && a.h == 0) | ||
| 3230 | return; | ||
| 3231 | |||
| 3232 | XSizeHints hints; | ||
| 3233 | long flags; | ||
| 3234 | |||
| 3235 | XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags); | ||
| 3236 | |||
| 3237 | hints.flags |= PMinSize; | ||
| 3238 | |||
| 3239 | hints.min_width = a.w; | ||
| 3240 | hints.min_height = a.h; | ||
| 3241 | |||
| 3242 | XSetWMNormalHints(win->src.display, (Window) win->src.window, &hints); | ||
| 3243 | } | ||
| 3244 | |||
| 3245 | void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { | ||
| 3246 | assert(win != NULL); | ||
| 3247 | |||
| 3248 | if (a.w == 0 && a.h == 0) | ||
| 3249 | return; | ||
| 3250 | |||
| 3251 | XSizeHints hints; | ||
| 3252 | long flags; | ||
| 3253 | |||
| 3254 | XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags); | ||
| 3255 | |||
| 3256 | hints.flags |= PMaxSize; | ||
| 3257 | |||
| 3258 | hints.max_width = a.w; | ||
| 3259 | hints.max_height = a.h; | ||
| 3260 | |||
| 3261 | XSetWMNormalHints(win->src.display, (Window) win->src.window, &hints); | ||
| 3262 | } | ||
| 3263 | |||
| 3264 | |||
| 3265 | void RGFW_window_minimize(RGFW_window* win) { | ||
| 3266 | assert(win != NULL); | ||
| 3267 | |||
| 3268 | XIconifyWindow(win->src.display, (Window) win->src.window, DefaultScreen(win->src.display)); | ||
| 3269 | XFlush(win->src.display); | ||
| 3270 | } | ||
| 3271 | |||
| 3272 | void RGFW_window_restore(RGFW_window* win) { | ||
| 3273 | assert(win != NULL); | ||
| 3274 | |||
| 3275 | XMapWindow(win->src.display, (Window) win->src.window); | ||
| 3276 | XFlush(win->src.display); | ||
| 3277 | } | ||
| 3278 | |||
| 3279 | void RGFW_window_setName(RGFW_window* win, char* name) { | ||
| 3280 | assert(win != NULL); | ||
| 3281 | |||
| 3282 | XStoreName((Display*) win->src.display, (Window) win->src.window, name); | ||
| 3283 | } | ||
| 3284 | |||
| 3285 | void* RGFW_libxshape = NULL; | ||
| 3286 | |||
| 3287 | #ifndef RGFW_NO_PASSTHROUGH | ||
| 3288 | void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { | ||
| 3289 | assert(win != NULL); | ||
| 3290 | |||
| 3291 | #if defined(__CYGWIN__) | ||
| 3292 | RGFW_libxshape = dlopen("libXext-6.so", RTLD_LAZY | RTLD_LOCAL); | ||
| 3293 | #elif defined(__OpenBSD__) || defined(__NetBSD__) | ||
| 3294 | RGFW_libxshape = dlopen("libXext.so", RTLD_LAZY | RTLD_LOCAL); | ||
| 3295 | #else | ||
| 3296 | RGFW_libxshape = dlopen("libXext.so.6", RTLD_LAZY | RTLD_LOCAL); | ||
| 3297 | #endif | ||
| 3298 | |||
| 3299 | typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int); | ||
| 3300 | static PFN_XShapeCombineMask XShapeCombineMask; | ||
| 3301 | |||
| 3302 | typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int); | ||
| 3303 | static PFN_XShapeCombineRegion XShapeCombineRegion; | ||
| 3304 | |||
| 3305 | if (XShapeCombineMask != NULL) | ||
| 3306 | XShapeCombineMask = (PFN_XShapeCombineMask) dlsym(RGFW_libxshape, "XShapeCombineMask"); | ||
| 3307 | |||
| 3308 | if (XShapeCombineRegion != NULL) | ||
| 3309 | XShapeCombineRegion = (PFN_XShapeCombineRegion) dlsym(RGFW_libxshape, "XShapeCombineMask"); | ||
| 3310 | |||
| 3311 | if (passthrough) { | ||
| 3312 | Region region = XCreateRegion(); | ||
| 3313 | XShapeCombineRegion(win->src.display, win->src.window, ShapeInput, 0, 0, region, ShapeSet); | ||
| 3314 | XDestroyRegion(region); | ||
| 3315 | |||
| 3316 | return; | ||
| 3317 | } | ||
| 3318 | |||
| 3319 | XShapeCombineMask(win->src.display, win->src.window, ShapeInput, 0, 0, None, ShapeSet); | ||
| 3320 | } | ||
| 3321 | #endif | ||
| 3322 | |||
| 3323 | /* | ||
| 3324 | the majority function is sourced from GLFW | ||
| 3325 | */ | ||
| 3326 | |||
| 3327 | void RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) { | ||
| 3328 | assert(win != NULL); | ||
| 3329 | |||
| 3330 | i32 longCount = 2 + a.w * a.h; | ||
| 3331 | |||
| 3332 | u64* X11Icon = (u64*) RGFW_MALLOC(longCount * sizeof(u64)); | ||
| 3333 | u64* target = X11Icon; | ||
| 3334 | |||
| 3335 | *target++ = a.w; | ||
| 3336 | *target++ = a.h; | ||
| 3337 | |||
| 3338 | u32 i; | ||
| 3339 | |||
| 3340 | for (i = 0; i < a.w * a.h; i++) { | ||
| 3341 | if (channels == 3) | ||
| 3342 | *target++ = ((icon[i * 3 + 0]) << 16) | | ||
| 3343 | ((icon[i * 3 + 1]) << 8) | | ||
| 3344 | ((icon[i * 3 + 2]) << 0) | | ||
| 3345 | (0xFF << 24); | ||
| 3346 | |||
| 3347 | else if (channels == 4) | ||
| 3348 | *target++ = ((icon[i * 4 + 0]) << 16) | | ||
| 3349 | ((icon[i * 4 + 1]) << 8) | | ||
| 3350 | ((icon[i * 4 + 2]) << 0) | | ||
| 3351 | ((icon[i * 4 + 3]) << 24); | ||
| 3352 | } | ||
| 3353 | |||
| 3354 | static Atom NET_WM_ICON = 0; | ||
| 3355 | if (NET_WM_ICON == 0) | ||
| 3356 | NET_WM_ICON = XInternAtom((Display*) win->src.display, "_NET_WM_ICON", False); | ||
| 3357 | |||
| 3358 | XChangeProperty((Display*) win->src.display, (Window) win->src.window, | ||
| 3359 | NET_WM_ICON, | ||
| 3360 | 6, 32, | ||
| 3361 | PropModeReplace, | ||
| 3362 | (u8*) X11Icon, | ||
| 3363 | longCount); | ||
| 3364 | |||
| 3365 | RGFW_FREE(X11Icon); | ||
| 3366 | |||
| 3367 | XFlush((Display*) win->src.display); | ||
| 3368 | } | ||
| 3369 | |||
| 3370 | void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { | ||
| 3371 | assert(win != NULL); | ||
| 3372 | |||
| 3373 | #ifndef RGFW_NO_X11_CURSOR | ||
| 3374 | XcursorImage* native = XcursorImageCreate(a.w, a.h); | ||
| 3375 | native->xhot = 0; | ||
| 3376 | native->yhot = 0; | ||
| 3377 | |||
| 3378 | u8* source = (u8*) image; | ||
| 3379 | XcursorPixel* target = native->pixels; | ||
| 3380 | |||
| 3381 | u32 i; | ||
| 3382 | for (i = 0; i < a.w * a.h; i++, target++, source += 4) { | ||
| 3383 | u8 alpha = 0xFF; | ||
| 3384 | if (channels == 4) | ||
| 3385 | alpha = source[3]; | ||
| 3386 | |||
| 3387 | *target = (alpha << 24) | (((source[0] * alpha) / 255) << 16) | (((source[1] * alpha) / 255) << 8) | (((source[2] * alpha) / 255) << 0); | ||
| 3388 | } | ||
| 3389 | |||
| 3390 | Cursor cursor = XcursorImageLoadCursor((Display*) win->src.display, native); | ||
| 3391 | XDefineCursor((Display*) win->src.display, (Window) win->src.window, (Cursor) cursor); | ||
| 3392 | |||
| 3393 | XFreeCursor((Display*) win->src.display, (Cursor) cursor); | ||
| 3394 | XcursorImageDestroy(native); | ||
| 3395 | #else | ||
| 3396 | RGFW_UNUSED(image) RGFW_UNUSED(a.w) RGFW_UNUSED(channels) | ||
| 3397 | #endif | ||
| 3398 | } | ||
| 3399 | |||
| 3400 | void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { | ||
| 3401 | assert(win != NULL); | ||
| 3402 | |||
| 3403 | XEvent event; | ||
| 3404 | XQueryPointer(win->src.display, DefaultRootWindow(win->src.display), | ||
| 3405 | &event.xbutton.root, &event.xbutton.window, | ||
| 3406 | &event.xbutton.x_root, &event.xbutton.y_root, | ||
| 3407 | &event.xbutton.x, &event.xbutton.y, | ||
| 3408 | &event.xbutton.state); | ||
| 3409 | |||
| 3410 | if (event.xbutton.x == v.x && event.xbutton.y == v.y) | ||
| 3411 | return; | ||
| 3412 | |||
| 3413 | XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) v.x - win->r.x, (int) v.y - win->r.y); | ||
| 3414 | } | ||
| 3415 | |||
| 3416 | RGFWDEF void RGFW_window_disableMouse(RGFW_window* win) { | ||
| 3417 | RGFW_UNUSED(win); | ||
| 3418 | } | ||
| 3419 | |||
| 3420 | void RGFW_window_setMouseDefault(RGFW_window* win) { | ||
| 3421 | RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW); | ||
| 3422 | } | ||
| 3423 | |||
| 3424 | void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { | ||
| 3425 | assert(win != NULL); | ||
| 3426 | |||
| 3427 | if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u8))) | ||
| 3428 | return; | ||
| 3429 | |||
| 3430 | mouse = RGFW_mouseIconSrc[mouse]; | ||
| 3431 | |||
| 3432 | Cursor cursor = XCreateFontCursor((Display*) win->src.display, mouse); | ||
| 3433 | XDefineCursor((Display*) win->src.display, (Window) win->src.window, (Cursor) cursor); | ||
| 3434 | |||
| 3435 | XFreeCursor((Display*) win->src.display, (Cursor) cursor); | ||
| 3436 | } | ||
| 3437 | |||
| 3438 | void RGFW_window_hide(RGFW_window* win) { | ||
| 3439 | XMapWindow(win->src.display, win->src.window); | ||
| 3440 | } | ||
| 3441 | |||
| 3442 | void RGFW_window_show(RGFW_window* win) { | ||
| 3443 | XUnmapWindow(win->src.display, win->src.window); | ||
| 3444 | } | ||
| 3445 | |||
| 3446 | /* | ||
| 3447 | the majority function is sourced from GLFW | ||
| 3448 | */ | ||
| 3449 | char* RGFW_readClipboard(size_t* size) { | ||
| 3450 | static Atom UTF8 = 0; | ||
| 3451 | if (UTF8 == 0) | ||
| 3452 | UTF8 = XInternAtom(RGFW_root->src.display, "UTF8_STRING", True); | ||
| 3453 | |||
| 3454 | XEvent event; | ||
| 3455 | int format; | ||
| 3456 | unsigned long N, sizeN; | ||
| 3457 | char* data, * s = NULL; | ||
| 3458 | Atom target; | ||
| 3459 | Atom CLIPBOARD = 0, XSEL_DATA = 0; | ||
| 3460 | |||
| 3461 | if (CLIPBOARD == 0) { | ||
| 3462 | CLIPBOARD = XInternAtom(RGFW_root->src.display, "CLIPBOARD", 0); | ||
| 3463 | XSEL_DATA = XInternAtom(RGFW_root->src.display, "XSEL_DATA", 0); | ||
| 3464 | } | ||
| 3465 | |||
| 3466 | XConvertSelection(RGFW_root->src.display, CLIPBOARD, UTF8, XSEL_DATA, RGFW_root->src.window, CurrentTime); | ||
| 3467 | XSync(RGFW_root->src.display, 0); | ||
| 3468 | XNextEvent(RGFW_root->src.display, &event); | ||
| 3469 | |||
| 3470 | if (event.type != SelectionNotify || event.xselection.selection != CLIPBOARD || event.xselection.property == 0) | ||
| 3471 | return NULL; | ||
| 3472 | |||
| 3473 | XGetWindowProperty(event.xselection.display, event.xselection.requestor, | ||
| 3474 | event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target, | ||
| 3475 | &format, &sizeN, &N, (unsigned char**) &data); | ||
| 3476 | |||
| 3477 | if (target == UTF8 || target == XA_STRING) { | ||
| 3478 | s = (char*)RGFW_MALLOC(sizeof(char) * sizeN); | ||
| 3479 | strncpy(s, data, sizeN); | ||
| 3480 | s[sizeN] = '\0'; | ||
| 3481 | XFree(data); | ||
| 3482 | } | ||
| 3483 | |||
| 3484 | XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); | ||
| 3485 | |||
| 3486 | if (s != NULL && size != NULL) | ||
| 3487 | *size = sizeN; | ||
| 3488 | |||
| 3489 | return s; | ||
| 3490 | } | ||
| 3491 | |||
| 3492 | /* | ||
| 3493 | almost all of this function is sourced from GLFW | ||
| 3494 | */ | ||
| 3495 | void RGFW_writeClipboard(const char* text, u32 textLen) { | ||
| 3496 | static Atom CLIPBOARD = 0, | ||
| 3497 | UTF8_STRING = 0, | ||
| 3498 | SAVE_TARGETS = 0, | ||
| 3499 | TARGETS = 0, | ||
| 3500 | MULTIPLE = 0, | ||
| 3501 | ATOM_PAIR = 0, | ||
| 3502 | CLIPBOARD_MANAGER = 0; | ||
| 3503 | |||
| 3504 | if (CLIPBOARD == 0) { | ||
| 3505 | CLIPBOARD = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD", False); | ||
| 3506 | UTF8_STRING = XInternAtom((Display*) RGFW_root->src.display, "UTF8_STRING", False); | ||
| 3507 | SAVE_TARGETS = XInternAtom((Display*) RGFW_root->src.display, "SAVE_TARGETS", False); | ||
| 3508 | TARGETS = XInternAtom((Display*) RGFW_root->src.display, "TARGETS", False); | ||
| 3509 | MULTIPLE = XInternAtom((Display*) RGFW_root->src.display, "MULTIPLE", False); | ||
| 3510 | ATOM_PAIR = XInternAtom((Display*) RGFW_root->src.display, "ATOM_PAIR", False); | ||
| 3511 | CLIPBOARD_MANAGER = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD_MANAGER", False); | ||
| 3512 | } | ||
| 3513 | |||
| 3514 | XSetSelectionOwner((Display*) RGFW_root->src.display, CLIPBOARD, (Window) RGFW_root->src.window, CurrentTime); | ||
| 3515 | |||
| 3516 | XConvertSelection((Display*) RGFW_root->src.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, (Window) RGFW_root->src.window, CurrentTime); | ||
| 3517 | for (;;) { | ||
| 3518 | XEvent event; | ||
| 3519 | |||
| 3520 | XNextEvent((Display*) RGFW_root->src.display, &event); | ||
| 3521 | if (event.type != SelectionRequest) { | ||
| 3522 | break; | ||
| 3523 | } | ||
| 3524 | |||
| 3525 | const XSelectionRequestEvent* request = &event.xselectionrequest; | ||
| 3526 | |||
| 3527 | XEvent reply = { SelectionNotify }; | ||
| 3528 | reply.xselection.property = 0; | ||
| 3529 | |||
| 3530 | if (request->target == TARGETS) { | ||
| 3531 | const Atom targets[] = { TARGETS, | ||
| 3532 | MULTIPLE, | ||
| 3533 | UTF8_STRING, | ||
| 3534 | XA_STRING }; | ||
| 3535 | |||
| 3536 | XChangeProperty((Display*) RGFW_root->src.display, | ||
| 3537 | request->requestor, | ||
| 3538 | request->property, | ||
| 3539 | 4, | ||
| 3540 | 32, | ||
| 3541 | PropModeReplace, | ||
| 3542 | (u8*) targets, | ||
| 3543 | sizeof(targets) / sizeof(targets[0])); | ||
| 3544 | |||
| 3545 | reply.xselection.property = request->property; | ||
| 3546 | } | ||
| 3547 | |||
| 3548 | if (request->target == MULTIPLE) { | ||
| 3549 | Atom* targets = NULL; | ||
| 3550 | |||
| 3551 | Atom actualType = 0; | ||
| 3552 | int actualFormat = 0; | ||
| 3553 | unsigned long count = 0, bytesAfter = 0; | ||
| 3554 | |||
| 3555 | XGetWindowProperty((Display*) RGFW_root->src.display, request->requestor, request->property, 0, LONG_MAX, False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets); | ||
| 3556 | |||
| 3557 | unsigned long i; | ||
| 3558 | for (i = 0; i < (u32)count; i += 2) { | ||
| 3559 | if (targets[i] == UTF8_STRING || targets[i] == XA_STRING) { | ||
| 3560 | XChangeProperty((Display*) RGFW_root->src.display, | ||
| 3561 | request->requestor, | ||
| 3562 | targets[i + 1], | ||
| 3563 | targets[i], | ||
| 3564 | 8, | ||
| 3565 | PropModeReplace, | ||
| 3566 | (u8*) text, | ||
| 3567 | textLen); | ||
| 3568 | XFlush(RGFW_root->src.display); | ||
| 3569 | } else { | ||
| 3570 | targets[i + 1] = None; | ||
| 3571 | } | ||
| 3572 | } | ||
| 3573 | |||
| 3574 | XChangeProperty((Display*) RGFW_root->src.display, | ||
| 3575 | request->requestor, | ||
| 3576 | request->property, | ||
| 3577 | ATOM_PAIR, | ||
| 3578 | 32, | ||
| 3579 | PropModeReplace, | ||
| 3580 | (u8*) targets, | ||
| 3581 | count); | ||
| 3582 | |||
| 3583 | XFlush(RGFW_root->src.display); | ||
| 3584 | XFree(targets); | ||
| 3585 | |||
| 3586 | reply.xselection.property = request->property; | ||
| 3587 | } | ||
| 3588 | |||
| 3589 | reply.xselection.display = request->display; | ||
| 3590 | reply.xselection.requestor = request->requestor; | ||
| 3591 | reply.xselection.selection = request->selection; | ||
| 3592 | reply.xselection.target = request->target; | ||
| 3593 | reply.xselection.time = request->time; | ||
| 3594 | |||
| 3595 | XSendEvent((Display*) RGFW_root->src.display, request->requestor, False, 0, &reply); | ||
| 3596 | XFlush(RGFW_root->src.display); | ||
| 3597 | } | ||
| 3598 | } | ||
| 3599 | |||
| 3600 | u8 RGFW_window_isFullscreen(RGFW_window* win) { | ||
| 3601 | assert(win != NULL); | ||
| 3602 | |||
| 3603 | XWindowAttributes windowAttributes; | ||
| 3604 | XGetWindowAttributes(win->src.display, (Window) win->src.window, &windowAttributes); | ||
| 3605 | |||
| 3606 | /* check if the window is visable */ | ||
| 3607 | if (windowAttributes.map_state != IsViewable) | ||
| 3608 | return 0; | ||
| 3609 | |||
| 3610 | /* check if the window covers the full screen */ | ||
| 3611 | return (windowAttributes.x == 0 && windowAttributes.y == 0 && | ||
| 3612 | windowAttributes.width == XDisplayWidth(win->src.display, DefaultScreen(win->src.display)) && | ||
| 3613 | windowAttributes.height == XDisplayHeight(win->src.display, DefaultScreen(win->src.display))); | ||
| 3614 | } | ||
| 3615 | |||
| 3616 | u8 RGFW_window_isHidden(RGFW_window* win) { | ||
| 3617 | assert(win != NULL); | ||
| 3618 | |||
| 3619 | XWindowAttributes windowAttributes; | ||
| 3620 | XGetWindowAttributes(win->src.display, (Window) win->src.window, &windowAttributes); | ||
| 3621 | |||
| 3622 | return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win)); | ||
| 3623 | } | ||
| 3624 | |||
| 3625 | u8 RGFW_window_isMinimized(RGFW_window* win) { | ||
| 3626 | assert(win != NULL); | ||
| 3627 | |||
| 3628 | static Atom prop = 0; | ||
| 3629 | if (prop == 0) | ||
| 3630 | prop = XInternAtom(win->src.display, "WM_STATE", False); | ||
| 3631 | |||
| 3632 | Atom actual_type; | ||
| 3633 | i32 actual_format; | ||
| 3634 | unsigned long nitems, bytes_after; | ||
| 3635 | unsigned char* prop_data; | ||
| 3636 | |||
| 3637 | i16 status = XGetWindowProperty(win->src.display, (Window) win->src.window, prop, 0, 2, False, | ||
| 3638 | AnyPropertyType, &actual_type, &actual_format, | ||
| 3639 | &nitems, &bytes_after, &prop_data); | ||
| 3640 | |||
| 3641 | if (status == Success && nitems >= 1 && *((int*) prop_data) == IconicState) { | ||
| 3642 | XFree(prop_data); | ||
| 3643 | return 1; | ||
| 3644 | } | ||
| 3645 | |||
| 3646 | if (prop_data != NULL) | ||
| 3647 | XFree(prop_data); | ||
| 3648 | |||
| 3649 | return 0; | ||
| 3650 | } | ||
| 3651 | |||
| 3652 | u8 RGFW_window_isMaximized(RGFW_window* win) { | ||
| 3653 | assert(win != NULL); | ||
| 3654 | |||
| 3655 | static Atom net_wm_state = 0; | ||
| 3656 | static Atom net_wm_state_maximized_horz = 0; | ||
| 3657 | static Atom net_wm_state_maximized_vert = 0; | ||
| 3658 | |||
| 3659 | if (net_wm_state == 0) { | ||
| 3660 | net_wm_state = XInternAtom(win->src.display, "_NET_WM_STATE", False); | ||
| 3661 | net_wm_state_maximized_vert = XInternAtom(win->src.display, "_NET_WM_STATE_MAXIMIZED_VERT", False); | ||
| 3662 | net_wm_state_maximized_horz = XInternAtom(win->src.display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); | ||
| 3663 | } | ||
| 3664 | |||
| 3665 | Atom actual_type; | ||
| 3666 | i32 actual_format; | ||
| 3667 | unsigned long nitems, bytes_after; | ||
| 3668 | unsigned char* prop_data; | ||
| 3669 | |||
| 3670 | i16 status = XGetWindowProperty(win->src.display, (Window) win->src.window, net_wm_state, 0, 1024, False, | ||
| 3671 | XA_ATOM, &actual_type, &actual_format, | ||
| 3672 | &nitems, &bytes_after, &prop_data); | ||
| 3673 | |||
| 3674 | if (status != Success) { | ||
| 3675 | if (prop_data != NULL) | ||
| 3676 | XFree(prop_data); | ||
| 3677 | |||
| 3678 | return 0; | ||
| 3679 | } | ||
| 3680 | |||
| 3681 | Atom* atoms = (Atom*) prop_data; | ||
| 3682 | u64 i; | ||
| 3683 | for (i = 0; i < nitems; ++i) { | ||
| 3684 | if (atoms[i] == net_wm_state_maximized_horz || | ||
| 3685 | atoms[i] == net_wm_state_maximized_vert) { | ||
| 3686 | XFree(prop_data); | ||
| 3687 | return 1; | ||
| 3688 | } | ||
| 3689 | } | ||
| 3690 | |||
| 3691 | return 0; | ||
| 3692 | } | ||
| 3693 | |||
| 3694 | static void XGetSystemContentScale(Display* display, float* xscale, float* yscale) { | ||
| 3695 | float xdpi = 96.f, ydpi = 96.f; | ||
| 3696 | |||
| 3697 | #ifndef RGFW_NO_DPI | ||
| 3698 | char* rms = XResourceManagerString(display); | ||
| 3699 | XrmDatabase db = NULL; | ||
| 3700 | |||
| 3701 | if (rms && db) | ||
| 3702 | db = XrmGetStringDatabase(rms); | ||
| 3703 | |||
| 3704 | if (db == 0) { | ||
| 3705 | *xscale = xdpi / 96.f; | ||
| 3706 | *yscale = ydpi / 96.f; | ||
| 3707 | return; | ||
| 3708 | } | ||
| 3709 | |||
| 3710 | XrmValue value; | ||
| 3711 | char* type = NULL; | ||
| 3712 | |||
| 3713 | if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && strncmp(type, "String", 7) == 0) | ||
| 3714 | xdpi = ydpi = atof(value.addr); | ||
| 3715 | XrmDestroyDatabase(db); | ||
| 3716 | #endif | ||
| 3717 | |||
| 3718 | * xscale = xdpi / 96.f; | ||
| 3719 | *yscale = ydpi / 96.f; | ||
| 3720 | } | ||
| 3721 | |||
| 3722 | RGFW_monitor RGFW_XCreateMonitor(i32 screen) { | ||
| 3723 | RGFW_monitor monitor; | ||
| 3724 | |||
| 3725 | Display* display = XOpenDisplay(NULL); | ||
| 3726 | |||
| 3727 | RGFW_area size = RGFW_getScreenSize(); | ||
| 3728 | |||
| 3729 | monitor.rect = RGFW_RECT(0, 0, size.w, size.h); | ||
| 3730 | monitor.physW = DisplayWidthMM(display, screen); | ||
| 3731 | monitor.physH = DisplayHeightMM(display, screen); | ||
| 3732 | |||
| 3733 | XGetSystemContentScale(display, &monitor.scaleX, &monitor.scaleY); | ||
| 3734 | XRRScreenResources* sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen)); | ||
| 3735 | |||
| 3736 | XRRCrtcInfo* ci = NULL; | ||
| 3737 | int crtc = screen; | ||
| 3738 | |||
| 3739 | if (sr->ncrtc > crtc) { | ||
| 3740 | ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]); | ||
| 3741 | } | ||
| 3742 | |||
| 3743 | if (ci == NULL) { | ||
| 3744 | float dpi_width = round((double)monitor.rect.w/(((double)monitor.physW)/25.4)); | ||
| 3745 | float dpi_height = round((double)monitor.rect.h/(((double)monitor.physH)/25.4)); | ||
| 3746 | |||
| 3747 | monitor.scaleX = (float) (dpi_width) / (float) 96; | ||
| 3748 | monitor.scaleY = (float) (dpi_height) / (float) 96; | ||
| 3749 | XRRFreeScreenResources(sr); | ||
| 3750 | XCloseDisplay(display); | ||
| 3751 | return monitor; | ||
| 3752 | } | ||
| 3753 | |||
| 3754 | XRROutputInfo* info = XRRGetOutputInfo (display, sr, sr->outputs[screen]); | ||
| 3755 | monitor.physW = info->mm_width; | ||
| 3756 | monitor.physH = info->mm_height; | ||
| 3757 | |||
| 3758 | monitor.rect.x = ci->x; | ||
| 3759 | monitor.rect.y = ci->y; | ||
| 3760 | monitor.rect.w = ci->width; | ||
| 3761 | monitor.rect.h = ci->height; | ||
| 3762 | |||
| 3763 | float dpi_width = round((double)monitor.rect.w/(((double)monitor.physW)/25.4)); | ||
| 3764 | float dpi_height = round((double)monitor.rect.h/(((double)monitor.physH)/25.4)); | ||
| 3765 | |||
| 3766 | monitor.scaleX = (float) (dpi_width) / (float) 96; | ||
| 3767 | monitor.scaleY = (float) (dpi_height) / (float) 96; | ||
| 3768 | |||
| 3769 | if (monitor.scaleX > 1 && monitor.scaleX < 1.1) | ||
| 3770 | monitor.scaleX = 1; | ||
| 3771 | |||
| 3772 | if (monitor.scaleY > 1 && monitor.scaleY < 1.1) | ||
| 3773 | monitor.scaleY = 1; | ||
| 3774 | |||
| 3775 | XRRFreeCrtcInfo(ci); | ||
| 3776 | XRRFreeScreenResources(sr); | ||
| 3777 | |||
| 3778 | XCloseDisplay(display); | ||
| 3779 | |||
| 3780 | return monitor; | ||
| 3781 | } | ||
| 3782 | |||
| 3783 | RGFW_monitor RGFW_monitors[6]; | ||
| 3784 | RGFW_monitor* RGFW_getMonitors(void) { | ||
| 3785 | size_t i; | ||
| 3786 | for (i = 0; i < (size_t)ScreenCount(RGFW_root->src.display) && i < 6; i++) | ||
| 3787 | RGFW_monitors[i] = RGFW_XCreateMonitor(i); | ||
| 3788 | |||
| 3789 | return RGFW_monitors; | ||
| 3790 | } | ||
| 3791 | |||
| 3792 | RGFW_monitor RGFW_getPrimaryMonitor(void) { | ||
| 3793 | assert(RGFW_root != NULL); | ||
| 3794 | |||
| 3795 | i32 primary = -1; | ||
| 3796 | Window root = DefaultRootWindow(RGFW_root->src.display); | ||
| 3797 | XRRScreenResources* res = XRRGetScreenResources(RGFW_root->src.display, root); | ||
| 3798 | |||
| 3799 | for (int i = 0; i < res->noutput; i++) { | ||
| 3800 | XRROutputInfo* output_info = XRRGetOutputInfo(RGFW_root->src.display, res, res->outputs[i]); | ||
| 3801 | if (output_info->connection == RR_Connected && output_info->crtc) { | ||
| 3802 | XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(RGFW_root->src.display, res, output_info->crtc); | ||
| 3803 | if (crtc_info->mode != None && crtc_info->x == 0 && crtc_info->y == 0) { | ||
| 3804 | primary = i; | ||
| 3805 | XRRFreeCrtcInfo(crtc_info); | ||
| 3806 | XRRFreeOutputInfo(output_info); | ||
| 3807 | break; | ||
| 3808 | } | ||
| 3809 | XRRFreeCrtcInfo(crtc_info); | ||
| 3810 | } | ||
| 3811 | XRRFreeOutputInfo(output_info); | ||
| 3812 | } | ||
| 3813 | |||
| 3814 | XRRFreeScreenResources(res); | ||
| 3815 | |||
| 3816 | return RGFW_XCreateMonitor(primary); | ||
| 3817 | } | ||
| 3818 | |||
| 3819 | RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { | ||
| 3820 | return RGFW_XCreateMonitor(DefaultScreen(win->src.display)); | ||
| 3821 | } | ||
| 3822 | |||
| 3823 | #ifdef RGFW_OPENGL | ||
| 3824 | void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { | ||
| 3825 | if (win == NULL) | ||
| 3826 | glXMakeCurrent((Display*) NULL, (Drawable)NULL, (GLXContext) NULL); | ||
| 3827 | else | ||
| 3828 | glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); | ||
| 3829 | } | ||
| 3830 | #endif | ||
| 3831 | |||
| 3832 | |||
| 3833 | void RGFW_window_swapBuffers(RGFW_window* win) { | ||
| 3834 | assert(win != NULL); | ||
| 3835 | |||
| 3836 | /* clear the window*/ | ||
| 3837 | if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) { | ||
| 3838 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 3839 | #ifdef RGFW_OSMESA | ||
| 3840 | RGFW_OSMesa_reorganize(); | ||
| 3841 | #endif | ||
| 3842 | RGFW_area area = RGFW_bufferSize; | ||
| 3843 | |||
| 3844 | #ifndef RGFW_X11_DONT_CONVERT_BGR | ||
| 3845 | win->src.bitmap->data = (char*) win->buffer; | ||
| 3846 | u32 x, y; | ||
| 3847 | for (y = 0; y < (u32)win->r.h; y++) { | ||
| 3848 | for (x = 0; x < (u32)win->r.w; x++) { | ||
| 3849 | u32 index = (y * 4 * area.w) + x * 4; | ||
| 3850 | |||
| 3851 | u8 red = win->src.bitmap->data[index]; | ||
| 3852 | win->src.bitmap->data[index] = win->buffer[index + 2]; | ||
| 3853 | win->src.bitmap->data[index + 2] = red; | ||
| 3854 | |||
| 3855 | } | ||
| 3856 | } | ||
| 3857 | #endif | ||
| 3858 | XPutImage(win->src.display, (Window) win->src.window, win->src.gc, win->src.bitmap, 0, 0, 0, 0, RGFW_bufferSize.w, RGFW_bufferSize.h); | ||
| 3859 | #endif | ||
| 3860 | } | ||
| 3861 | |||
| 3862 | if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) { | ||
| 3863 | #ifdef RGFW_EGL | ||
| 3864 | eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); | ||
| 3865 | #elif defined(RGFW_OPENGL) | ||
| 3866 | glXSwapBuffers((Display*) win->src.display, (Window) win->src.window); | ||
| 3867 | #endif | ||
| 3868 | } | ||
| 3869 | } | ||
| 3870 | |||
| 3871 | #if !defined(RGFW_EGL) | ||
| 3872 | void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { | ||
| 3873 | assert(win != NULL); | ||
| 3874 | |||
| 3875 | #if defined(RGFW_OPENGL) | ||
| 3876 | ((PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"))((Display*) win->src.display, (Window) win->src.window, swapInterval); | ||
| 3877 | #else | ||
| 3878 | RGFW_UNUSED(swapInterval); | ||
| 3879 | #endif | ||
| 3880 | } | ||
| 3881 | #endif | ||
| 3882 | |||
| 3883 | |||
| 3884 | void RGFW_window_close(RGFW_window* win) { | ||
| 3885 | /* ungrab pointer if it was grabbed */ | ||
| 3886 | if (win->_winArgs & RGFW_HOLD_MOUSE) | ||
| 3887 | XUngrabPointer(win->src.display, CurrentTime); | ||
| 3888 | |||
| 3889 | assert(win != NULL); | ||
| 3890 | #ifdef RGFW_EGL | ||
| 3891 | RGFW_closeEGL(win); | ||
| 3892 | #endif | ||
| 3893 | |||
| 3894 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 3895 | if (win->buffer != NULL) { | ||
| 3896 | XDestroyImage((XImage*) win->src.bitmap); | ||
| 3897 | XFreeGC(win->src.display, win->src.gc); | ||
| 3898 | } | ||
| 3899 | #endif | ||
| 3900 | |||
| 3901 | if ((Display*) win->src.display) { | ||
| 3902 | #ifdef RGFW_OPENGL | ||
| 3903 | glXDestroyContext((Display*) win->src.display, win->src.ctx); | ||
| 3904 | #endif | ||
| 3905 | |||
| 3906 | if (win == RGFW_root) | ||
| 3907 | RGFW_root = NULL; | ||
| 3908 | |||
| 3909 | if ((Drawable) win->src.window) | ||
| 3910 | XDestroyWindow((Display*) win->src.display, (Drawable) win->src.window); /*!< close the window*/ | ||
| 3911 | |||
| 3912 | XCloseDisplay((Display*) win->src.display); /*!< kill the display*/ | ||
| 3913 | } | ||
| 3914 | |||
| 3915 | #ifdef RGFW_ALLOC_DROPFILES | ||
| 3916 | { | ||
| 3917 | u32 i; | ||
| 3918 | for (i = 0; i < RGFW_MAX_DROPS; i++) | ||
| 3919 | RGFW_FREE(win->event.droppedFiles[i]); | ||
| 3920 | |||
| 3921 | |||
| 3922 | RGFW_FREE(win->event.droppedFiles); | ||
| 3923 | } | ||
| 3924 | #endif | ||
| 3925 | |||
| 3926 | RGFW_windowsOpen--; | ||
| 3927 | #if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR) | ||
| 3928 | if (X11Cursorhandle != NULL && RGFW_windowsOpen <= 0) { | ||
| 3929 | dlclose(X11Cursorhandle); | ||
| 3930 | |||
| 3931 | X11Cursorhandle = NULL; | ||
| 3932 | } | ||
| 3933 | #endif | ||
| 3934 | #if !defined(RGFW_NO_X11_XI_PRELOAD) | ||
| 3935 | if (X11Xihandle != NULL && RGFW_windowsOpen <= 0) { | ||
| 3936 | dlclose(X11Xihandle); | ||
| 3937 | |||
| 3938 | X11Xihandle = NULL; | ||
| 3939 | } | ||
| 3940 | #endif | ||
| 3941 | |||
| 3942 | if (RGFW_libxshape != NULL && RGFW_windowsOpen <= 0) { | ||
| 3943 | dlclose(RGFW_libxshape); | ||
| 3944 | RGFW_libxshape = NULL; | ||
| 3945 | } | ||
| 3946 | |||
| 3947 | if (RGFW_windowsOpen <= 0) { | ||
| 3948 | if (RGFW_eventWait_forceStop[0] || RGFW_eventWait_forceStop[1]){ | ||
| 3949 | close(RGFW_eventWait_forceStop[0]); | ||
| 3950 | close(RGFW_eventWait_forceStop[1]); | ||
| 3951 | } | ||
| 3952 | |||
| 3953 | u8 i; | ||
| 3954 | for (i = 0; i < RGFW_joystickCount; i++) | ||
| 3955 | close(RGFW_joysticks[i]); | ||
| 3956 | } | ||
| 3957 | |||
| 3958 | /* set cleared display / window to NULL for error checking */ | ||
| 3959 | win->src.display = (Display*) 0; | ||
| 3960 | win->src.window = (Window) 0; | ||
| 3961 | |||
| 3962 | RGFW_FREE(win); /*!< free collected window data */ | ||
| 3963 | } | ||
| 3964 | |||
| 3965 | |||
| 3966 | /* | ||
| 3967 | End of X11 linux / unix defines | ||
| 3968 | */ | ||
| 3969 | |||
| 3970 | #endif /* RGFW_X11 */ | ||
| 3971 | |||
| 3972 | |||
| 3973 | /* wayland or X11 defines*/ | ||
| 3974 | #if defined(RGFW_WAYLAND) || defined(RGFW_X11) | ||
| 3975 | #include <fcntl.h> | ||
| 3976 | #include <poll.h> | ||
| 3977 | #include <unistd.h> | ||
| 3978 | u16 RGFW_registerJoystickF(RGFW_window* win, char* file) { | ||
| 3979 | assert(win != NULL); | ||
| 3980 | |||
| 3981 | #ifdef __linux__ | ||
| 3982 | |||
| 3983 | i32 js = open(file, O_RDONLY); | ||
| 3984 | |||
| 3985 | if (js && RGFW_joystickCount < 4) { | ||
| 3986 | RGFW_joystickCount++; | ||
| 3987 | |||
| 3988 | RGFW_joysticks[RGFW_joystickCount - 1] = open(file, O_RDONLY); | ||
| 3989 | |||
| 3990 | u8 i; | ||
| 3991 | for (i = 0; i < 16; i++) | ||
| 3992 | RGFW_jsPressed[RGFW_joystickCount - 1][i] = 0; | ||
| 3993 | |||
| 3994 | } | ||
| 3995 | |||
| 3996 | else { | ||
| 3997 | #ifdef RGFW_PRINT_ERRORS | ||
| 3998 | RGFW_error = 1; | ||
| 3999 | fprintf(stderr, "Error RGFW_registerJoystickF : Cannot open file %s\n", file); | ||
| 4000 | #endif | ||
| 4001 | } | ||
| 4002 | |||
| 4003 | return RGFW_joystickCount - 1; | ||
| 4004 | #endif | ||
| 4005 | } | ||
| 4006 | |||
| 4007 | u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) { | ||
| 4008 | assert(win != NULL); | ||
| 4009 | |||
| 4010 | #ifdef __linux__ | ||
| 4011 | char file[15]; | ||
| 4012 | sprintf(file, "/dev/input/js%i", jsNumber); | ||
| 4013 | |||
| 4014 | return RGFW_registerJoystickF(win, file); | ||
| 4015 | #endif | ||
| 4016 | } | ||
| 4017 | |||
| 4018 | void RGFW_stopCheckEvents(void) { | ||
| 4019 | RGFW_eventWait_forceStop[2] = 1; | ||
| 4020 | while (1) { | ||
| 4021 | const char byte = 0; | ||
| 4022 | const ssize_t result = write(RGFW_eventWait_forceStop[1], &byte, 1); | ||
| 4023 | if (result == 1 || result == -1) | ||
| 4024 | break; | ||
| 4025 | } | ||
| 4026 | } | ||
| 4027 | |||
| 4028 | void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { | ||
| 4029 | if (waitMS == 0) | ||
| 4030 | return; | ||
| 4031 | |||
| 4032 | u8 i; | ||
| 4033 | |||
| 4034 | if (RGFW_eventWait_forceStop[0] == 0 || RGFW_eventWait_forceStop[1] == 0) { | ||
| 4035 | if (pipe(RGFW_eventWait_forceStop) != -1) { | ||
| 4036 | fcntl(RGFW_eventWait_forceStop[0], F_GETFL, 0); | ||
| 4037 | fcntl(RGFW_eventWait_forceStop[0], F_GETFD, 0); | ||
| 4038 | fcntl(RGFW_eventWait_forceStop[1], F_GETFL, 0); | ||
| 4039 | fcntl(RGFW_eventWait_forceStop[1], F_GETFD, 0); | ||
| 4040 | } | ||
| 4041 | } | ||
| 4042 | |||
| 4043 | struct pollfd fds[] = { | ||
| 4044 | #ifdef RGFW_WAYLAND | ||
| 4045 | { wl_display_get_fd(win->src.display), POLLIN, 0 }, | ||
| 4046 | #else | ||
| 4047 | { ConnectionNumber(win->src.display), POLLIN, 0 }, | ||
| 4048 | #endif | ||
| 4049 | { RGFW_eventWait_forceStop[0], POLLIN, 0 }, | ||
| 4050 | #ifdef __linux__ /* blank space for 4 joystick files*/ | ||
| 4051 | { -1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0} | ||
| 4052 | #endif | ||
| 4053 | }; | ||
| 4054 | |||
| 4055 | u8 index = 2; | ||
| 4056 | |||
| 4057 | #if defined(__linux__) | ||
| 4058 | for (i = 0; i < RGFW_joystickCount; i++) { | ||
| 4059 | if (RGFW_joysticks[i] == 0) | ||
| 4060 | continue; | ||
| 4061 | |||
| 4062 | fds[index].fd = RGFW_joysticks[i]; | ||
| 4063 | index++; | ||
| 4064 | } | ||
| 4065 | #endif | ||
| 4066 | |||
| 4067 | |||
| 4068 | u64 start = RGFW_getTimeNS(); | ||
| 4069 | |||
| 4070 | #ifdef RGFW_WAYLAND | ||
| 4071 | while (wl_display_dispatch(win->src.display) <= 0 && waitMS >= -1) { | ||
| 4072 | #else | ||
| 4073 | while (XPending(win->src.display) == 0 && waitMS >= -1) { | ||
| 4074 | #endif | ||
| 4075 | if (poll(fds, index, waitMS) <= 0) | ||
| 4076 | break; | ||
| 4077 | |||
| 4078 | if (waitMS > 0) { | ||
| 4079 | waitMS -= (RGFW_getTimeNS() - start) / 1e+6; | ||
| 4080 | } | ||
| 4081 | } | ||
| 4082 | |||
| 4083 | /* drain any data in the stop request */ | ||
| 4084 | if (RGFW_eventWait_forceStop[2]) { | ||
| 4085 | char data[64]; | ||
| 4086 | (void)!read(RGFW_eventWait_forceStop[0], data, sizeof(data)); | ||
| 4087 | |||
| 4088 | RGFW_eventWait_forceStop[2] = 0; | ||
| 4089 | } | ||
| 4090 | } | ||
| 4091 | |||
| 4092 | u64 RGFW_getTimeNS(void) { | ||
| 4093 | struct timespec ts = { 0 }; | ||
| 4094 | clock_gettime(1, &ts); | ||
| 4095 | unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; | ||
| 4096 | |||
| 4097 | return nanoSeconds; | ||
| 4098 | } | ||
| 4099 | |||
| 4100 | u64 RGFW_getTime(void) { | ||
| 4101 | struct timespec ts = { 0 }; | ||
| 4102 | clock_gettime(1, &ts); | ||
| 4103 | unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; | ||
| 4104 | |||
| 4105 | return (double)(nanoSeconds) * 1e-9; | ||
| 4106 | } | ||
| 4107 | #endif /* end of wayland or X11 time defines*/ | ||
| 4108 | |||
| 4109 | |||
| 4110 | /* | ||
| 4111 | |||
| 4112 | Start of Wayland defines | ||
| 4113 | |||
| 4114 | |||
| 4115 | */ | ||
| 4116 | |||
| 4117 | #ifdef RGFW_WAYLAND | ||
| 4118 | /* | ||
| 4119 | Wayland TODO: | ||
| 4120 | - fix RGFW_keyPressed lock state | ||
| 4121 | |||
| 4122 | RGFW_windowMoved, the window was moved (by the user) | ||
| 4123 | RGFW_windowResized the window was resized (by the user), [on webASM this means the browser was resized] | ||
| 4124 | RGFW_windowRefresh The window content needs to be refreshed | ||
| 4125 | |||
| 4126 | RGFW_dnd a file has been dropped into the window | ||
| 4127 | RGFW_dnd_init | ||
| 4128 | |||
| 4129 | - window args: | ||
| 4130 | #define RGFW_NO_RESIZE the window cannot be resized by the user | ||
| 4131 | #define RGFW_ALLOW_DND the window supports drag and drop | ||
| 4132 | #define RGFW_SCALE_TO_MONITOR scale the window to the screen | ||
| 4133 | |||
| 4134 | - other missing functions functions ("TODO wayland") (~30 functions) | ||
| 4135 | - fix buffer rendering weird behavior | ||
| 4136 | */ | ||
| 4137 | #include <errno.h> | ||
| 4138 | #include <unistd.h> | ||
| 4139 | #include <sys/mman.h> | ||
| 4140 | #include <xkbcommon/xkbcommon.h> | ||
| 4141 | #include <xkbcommon/xkbcommon-keysyms.h> | ||
| 4142 | #include <dirent.h> | ||
| 4143 | #include <linux/kd.h> | ||
| 4144 | #include <wayland-cursor.h> | ||
| 4145 | |||
| 4146 | RGFW_window* RGFW_key_win = NULL; | ||
| 4147 | |||
| 4148 | void RGFW_eventPipe_push(RGFW_window* win, RGFW_Event event) { | ||
| 4149 | if (win == NULL) { | ||
| 4150 | win = RGFW_key_win; | ||
| 4151 | |||
| 4152 | if (win == NULL) return; | ||
| 4153 | } | ||
| 4154 | |||
| 4155 | if (win->src.eventLen >= (i32)(sizeof(win->src.events) / sizeof(win->src.events[0]))) | ||
| 4156 | return; | ||
| 4157 | |||
| 4158 | win->src.events[win->src.eventLen] = event; | ||
| 4159 | win->src.eventLen += 1; | ||
| 4160 | } | ||
| 4161 | |||
| 4162 | RGFW_Event RGFW_eventPipe_pop(RGFW_window* win) { | ||
| 4163 | RGFW_Event ev; | ||
| 4164 | ev.type = 0; | ||
| 4165 | |||
| 4166 | if (win->src.eventLen > -1) | ||
| 4167 | win->src.eventLen -= 1; | ||
| 4168 | |||
| 4169 | if (win->src.eventLen >= 0) | ||
| 4170 | ev = win->src.events[win->src.eventLen]; | ||
| 4171 | |||
| 4172 | return ev; | ||
| 4173 | } | ||
| 4174 | |||
| 4175 | /* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */ | ||
| 4176 | #include "xdg-shell.h" | ||
| 4177 | #include "xdg-decoration-unstable-v1.h" | ||
| 4178 | |||
| 4179 | struct xdg_wm_base *xdg_wm_base; | ||
| 4180 | struct wl_compositor* RGFW_compositor = NULL; | ||
| 4181 | struct wl_shm* shm = NULL; | ||
| 4182 | struct wl_shell* RGFW_shell = NULL; | ||
| 4183 | static struct wl_seat *seat = NULL; | ||
| 4184 | static struct xkb_context *xkb_context; | ||
| 4185 | static struct xkb_keymap *keymap = NULL; | ||
| 4186 | static struct xkb_state *xkb_state = NULL; | ||
| 4187 | enum zxdg_toplevel_decoration_v1_mode client_preferred_mode, RGFW_current_mode; | ||
| 4188 | static struct zxdg_decoration_manager_v1 *decoration_manager = NULL; | ||
| 4189 | |||
| 4190 | struct wl_cursor_theme* RGFW_wl_cursor_theme = NULL; | ||
| 4191 | struct wl_surface* RGFW_cursor_surface = NULL; | ||
| 4192 | struct wl_cursor_image* RGFW_cursor_image = NULL; | ||
| 4193 | |||
| 4194 | static void xdg_wm_base_ping_handler(void *data, | ||
| 4195 | struct xdg_wm_base *wm_base, uint32_t serial) | ||
| 4196 | { | ||
| 4197 | RGFW_UNUSED(data); | ||
| 4198 | xdg_wm_base_pong(wm_base, serial); | ||
| 4199 | } | ||
| 4200 | |||
| 4201 | static const struct xdg_wm_base_listener xdg_wm_base_listener = { | ||
| 4202 | .ping = xdg_wm_base_ping_handler, | ||
| 4203 | }; | ||
| 4204 | |||
| 4205 | b8 RGFW_wl_configured = 0; | ||
| 4206 | |||
| 4207 | static void xdg_surface_configure_handler(void *data, | ||
| 4208 | struct xdg_surface *xdg_surface, uint32_t serial) | ||
| 4209 | { | ||
| 4210 | RGFW_UNUSED(data); | ||
| 4211 | xdg_surface_ack_configure(xdg_surface, serial); | ||
| 4212 | #ifdef RGFW_DEBUG | ||
| 4213 | printf("Surface configured\n"); | ||
| 4214 | #endif | ||
| 4215 | RGFW_wl_configured = 1; | ||
| 4216 | } | ||
| 4217 | |||
| 4218 | static const struct xdg_surface_listener xdg_surface_listener = { | ||
| 4219 | .configure = xdg_surface_configure_handler, | ||
| 4220 | }; | ||
| 4221 | |||
| 4222 | static void xdg_toplevel_configure_handler(void *data, | ||
| 4223 | struct xdg_toplevel *toplevel, int32_t width, int32_t height, | ||
| 4224 | struct wl_array *states) | ||
| 4225 | { | ||
| 4226 | RGFW_UNUSED(data); RGFW_UNUSED(toplevel); RGFW_UNUSED(states) | ||
| 4227 | fprintf(stderr, "XDG toplevel configure: %dx%d\n", width, height); | ||
| 4228 | } | ||
| 4229 | |||
| 4230 | static void xdg_toplevel_close_handler(void *data, | ||
| 4231 | struct xdg_toplevel *toplevel) | ||
| 4232 | { | ||
| 4233 | RGFW_UNUSED(data); | ||
| 4234 | RGFW_window* win = (RGFW_window*)xdg_toplevel_get_user_data(toplevel); | ||
| 4235 | if (win == NULL) | ||
| 4236 | win = RGFW_key_win; | ||
| 4237 | |||
| 4238 | RGFW_Event ev; | ||
| 4239 | ev.type = RGFW_quit; | ||
| 4240 | |||
| 4241 | RGFW_eventPipe_push(win, ev); | ||
| 4242 | |||
| 4243 | RGFW_windowQuitCallback(win); | ||
| 4244 | } | ||
| 4245 | |||
| 4246 | static void shm_format_handler(void *data, | ||
| 4247 | struct wl_shm *shm, uint32_t format) | ||
| 4248 | { | ||
| 4249 | RGFW_UNUSED(data); RGFW_UNUSED(shm); | ||
| 4250 | fprintf(stderr, "Format %d\n", format); | ||
| 4251 | } | ||
| 4252 | |||
| 4253 | static const struct wl_shm_listener shm_listener = { | ||
| 4254 | .format = shm_format_handler, | ||
| 4255 | }; | ||
| 4256 | |||
| 4257 | static const struct xdg_toplevel_listener xdg_toplevel_listener = { | ||
| 4258 | .configure = xdg_toplevel_configure_handler, | ||
| 4259 | .close = xdg_toplevel_close_handler, | ||
| 4260 | }; | ||
| 4261 | |||
| 4262 | RGFW_window* RGFW_mouse_win = NULL; | ||
| 4263 | |||
| 4264 | static void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { | ||
| 4265 | RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface_x); RGFW_UNUSED(surface_y); | ||
| 4266 | RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); | ||
| 4267 | RGFW_mouse_win = win; | ||
| 4268 | |||
| 4269 | RGFW_Event ev; | ||
| 4270 | ev.type = RGFW_mouseEnter; | ||
| 4271 | ev.point = win->event.point; | ||
| 4272 | |||
| 4273 | RGFW_eventPipe_push(win, ev); | ||
| 4274 | |||
| 4275 | RGFW_mouseNotifyCallBack(win, win->event.point, RGFW_TRUE); | ||
| 4276 | } | ||
| 4277 | static void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { | ||
| 4278 | RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface); | ||
| 4279 | RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); | ||
| 4280 | if (RGFW_mouse_win == win) | ||
| 4281 | RGFW_mouse_win = NULL; | ||
| 4282 | |||
| 4283 | RGFW_Event ev; | ||
| 4284 | ev.type = RGFW_mouseLeave; | ||
| 4285 | ev.point = win->event.point; | ||
| 4286 | RGFW_eventPipe_push(win, ev); | ||
| 4287 | |||
| 4288 | RGFW_mouseNotifyCallBack(win, win->event.point, RGFW_FALSE); | ||
| 4289 | } | ||
| 4290 | static void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) { | ||
| 4291 | RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(x); RGFW_UNUSED(y); | ||
| 4292 | |||
| 4293 | assert(RGFW_mouse_win != NULL); | ||
| 4294 | |||
| 4295 | RGFW_Event ev; | ||
| 4296 | ev.type = RGFW_mousePosChanged; | ||
| 4297 | ev.point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)); | ||
| 4298 | RGFW_eventPipe_push(RGFW_mouse_win, ev); | ||
| 4299 | |||
| 4300 | RGFW_mousePosCallback(RGFW_mouse_win, RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y))); | ||
| 4301 | } | ||
| 4302 | static void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { | ||
| 4303 | RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial); | ||
| 4304 | assert(RGFW_mouse_win != NULL); | ||
| 4305 | |||
| 4306 | u32 b = (button - 0x110) + 1; | ||
| 4307 | |||
| 4308 | /* flip right and middle button codes */ | ||
| 4309 | if (b == 2) b = 3; | ||
| 4310 | else if (b == 3) b = 2; | ||
| 4311 | |||
| 4312 | RGFW_mouseButtons[b].prev = RGFW_mouseButtons[b].current; | ||
| 4313 | RGFW_mouseButtons[b].current = state; | ||
| 4314 | |||
| 4315 | RGFW_Event ev; | ||
| 4316 | ev.type = RGFW_mouseButtonPressed + state; | ||
| 4317 | ev.button = b; | ||
| 4318 | RGFW_eventPipe_push(RGFW_mouse_win, ev); | ||
| 4319 | |||
| 4320 | RGFW_mouseButtonCallback(RGFW_mouse_win, b, 0, state); | ||
| 4321 | } | ||
| 4322 | static void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { | ||
| 4323 | RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(axis); | ||
| 4324 | assert(RGFW_mouse_win != NULL); | ||
| 4325 | |||
| 4326 | double scroll = wl_fixed_to_double(value); | ||
| 4327 | |||
| 4328 | RGFW_Event ev; | ||
| 4329 | ev.type = RGFW_mouseButtonPressed; | ||
| 4330 | ev.button = RGFW_mouseScrollUp + (scroll < 0); | ||
| 4331 | RGFW_eventPipe_push(RGFW_mouse_win, ev); | ||
| 4332 | |||
| 4333 | RGFW_mouseButtonCallback(RGFW_mouse_win, RGFW_mouseScrollUp + (scroll < 0), scroll, 1); | ||
| 4334 | } | ||
| 4335 | |||
| 4336 | void RGFW_doNothing(void) { } | ||
| 4337 | static struct wl_pointer_listener pointer_listener = (struct wl_pointer_listener){&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing}; | ||
| 4338 | |||
| 4339 | static void keyboard_keymap (void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) { | ||
| 4340 | RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(format); | ||
| 4341 | |||
| 4342 | char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0); | ||
| 4343 | xkb_keymap_unref (keymap); | ||
| 4344 | keymap = xkb_keymap_new_from_string (xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); | ||
| 4345 | |||
| 4346 | munmap (keymap_string, size); | ||
| 4347 | close (fd); | ||
| 4348 | xkb_state_unref (xkb_state); | ||
| 4349 | xkb_state = xkb_state_new (keymap); | ||
| 4350 | } | ||
| 4351 | static void keyboard_enter (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { | ||
| 4352 | RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(keys); | ||
| 4353 | |||
| 4354 | RGFW_key_win = (RGFW_window*)wl_surface_get_user_data(surface); | ||
| 4355 | |||
| 4356 | RGFW_Event ev; | ||
| 4357 | ev.type = RGFW_focusIn; | ||
| 4358 | ev.inFocus = RGFW_TRUE; | ||
| 4359 | RGFW_key_win->event.inFocus = RGFW_TRUE; | ||
| 4360 | |||
| 4361 | RGFW_eventPipe_push((RGFW_window*)RGFW_mouse_win, ev); | ||
| 4362 | |||
| 4363 | RGFW_focusCallback(RGFW_key_win, RGFW_TRUE); | ||
| 4364 | } | ||
| 4365 | static void keyboard_leave (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { | ||
| 4366 | RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); | ||
| 4367 | |||
| 4368 | RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); | ||
| 4369 | if (RGFW_key_win == win) | ||
| 4370 | RGFW_key_win = NULL; | ||
| 4371 | |||
| 4372 | RGFW_Event ev; | ||
| 4373 | ev.type = RGFW_focusOut; | ||
| 4374 | ev.inFocus = RGFW_FALSE; | ||
| 4375 | win->event.inFocus = RGFW_FALSE; | ||
| 4376 | RGFW_eventPipe_push(win, ev); | ||
| 4377 | |||
| 4378 | RGFW_focusCallback(win, RGFW_FALSE); | ||
| 4379 | } | ||
| 4380 | static void keyboard_key (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { | ||
| 4381 | RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); | ||
| 4382 | |||
| 4383 | assert(RGFW_key_win != NULL); | ||
| 4384 | |||
| 4385 | xkb_keysym_t keysym = xkb_state_key_get_one_sym (xkb_state, key+8); | ||
| 4386 | char name[16]; | ||
| 4387 | xkb_keysym_get_name(keysym, name, 16); | ||
| 4388 | |||
| 4389 | u32 RGFW_key = RGFW_apiKeyCodeToRGFW(key); | ||
| 4390 | RGFW_keyboard[RGFW_key].prev = RGFW_keyboard[RGFW_key].current; | ||
| 4391 | RGFW_keyboard[RGFW_key].current = state; | ||
| 4392 | RGFW_Event ev; | ||
| 4393 | ev.type = RGFW_keyPressed + state; | ||
| 4394 | ev.keyCode = RGFW_key; | ||
| 4395 | strcpy(ev.keyName, name); | ||
| 4396 | ev.repeat = RGFW_isHeld(RGFW_key_win, RGFW_key); | ||
| 4397 | RGFW_eventPipe_push(RGFW_key_win, ev); | ||
| 4398 | |||
| 4399 | RGFW_updateLockState(RGFW_key_win, xkb_keymap_mod_get_index(keymap, "Lock"), xkb_keymap_mod_get_index(keymap, "Mod2")); | ||
| 4400 | |||
| 4401 | RGFW_keyCallback(RGFW_key_win, RGFW_key, name, RGFW_key_win->event.lockState, state); | ||
| 4402 | } | ||
| 4403 | static void keyboard_modifiers (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { | ||
| 4404 | RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); | ||
| 4405 | xkb_state_update_mask (xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); | ||
| 4406 | } | ||
| 4407 | static struct wl_keyboard_listener keyboard_listener = {&keyboard_keymap, &keyboard_enter, &keyboard_leave, &keyboard_key, &keyboard_modifiers, (void*)&RGFW_doNothing}; | ||
| 4408 | |||
| 4409 | static void seat_capabilities (void *data, struct wl_seat *seat, uint32_t capabilities) { | ||
| 4410 | RGFW_UNUSED(data); | ||
| 4411 | |||
| 4412 | if (capabilities & WL_SEAT_CAPABILITY_POINTER) { | ||
| 4413 | struct wl_pointer *pointer = wl_seat_get_pointer (seat); | ||
| 4414 | wl_pointer_add_listener (pointer, &pointer_listener, NULL); | ||
| 4415 | } | ||
| 4416 | if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { | ||
| 4417 | struct wl_keyboard *keyboard = wl_seat_get_keyboard (seat); | ||
| 4418 | wl_keyboard_add_listener (keyboard, &keyboard_listener, NULL); | ||
| 4419 | } | ||
| 4420 | } | ||
| 4421 | static struct wl_seat_listener seat_listener = {&seat_capabilities, (void*)&RGFW_doNothing}; | ||
| 4422 | |||
| 4423 | static void wl_global_registry_handler(void *data, | ||
| 4424 | struct wl_registry *registry, uint32_t id, const char *interface, | ||
| 4425 | uint32_t version) | ||
| 4426 | { | ||
| 4427 | RGFW_UNUSED(data); RGFW_UNUSED(version); | ||
| 4428 | |||
| 4429 | if (strcmp(interface, "wl_compositor") == 0) { | ||
| 4430 | RGFW_compositor = wl_registry_bind(registry, | ||
| 4431 | id, &wl_compositor_interface, 4); | ||
| 4432 | } else if (strcmp(interface, "xdg_wm_base") == 0) { | ||
| 4433 | xdg_wm_base = wl_registry_bind(registry, | ||
| 4434 | id, &xdg_wm_base_interface, 1); | ||
| 4435 | } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { | ||
| 4436 | decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1); | ||
| 4437 | } else if (strcmp(interface, "wl_shm") == 0) { | ||
| 4438 | shm = wl_registry_bind(registry, | ||
| 4439 | id, &wl_shm_interface, 1); | ||
| 4440 | wl_shm_add_listener(shm, &shm_listener, NULL); | ||
| 4441 | } else if (strcmp(interface,"wl_seat") == 0) { | ||
| 4442 | seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); | ||
| 4443 | wl_seat_add_listener(seat, &seat_listener, NULL); | ||
| 4444 | } | ||
| 4445 | |||
| 4446 | else { | ||
| 4447 | #ifdef RGFW_DEBUG | ||
| 4448 | printf("did not register %s\n", interface); | ||
| 4449 | return; | ||
| 4450 | #endif | ||
| 4451 | } | ||
| 4452 | |||
| 4453 | #ifdef RGFW_DEBUG | ||
| 4454 | printf("registered %s\n", interface); | ||
| 4455 | #endif | ||
| 4456 | } | ||
| 4457 | |||
| 4458 | static void wl_global_registry_remove(void *data, struct wl_registry *registry, uint32_t name) { RGFW_UNUSED(data); RGFW_UNUSED(registry); RGFW_UNUSED(name); } | ||
| 4459 | static const struct wl_registry_listener registry_listener = { | ||
| 4460 | .global = wl_global_registry_handler, | ||
| 4461 | .global_remove = wl_global_registry_remove, | ||
| 4462 | }; | ||
| 4463 | |||
| 4464 | static const char *get_mode_name(enum zxdg_toplevel_decoration_v1_mode mode) { | ||
| 4465 | switch (mode) { | ||
| 4466 | case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: | ||
| 4467 | return "client-side decorations"; | ||
| 4468 | case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: | ||
| 4469 | return "server-side decorations"; | ||
| 4470 | } | ||
| 4471 | abort(); | ||
| 4472 | } | ||
| 4473 | |||
| 4474 | |||
| 4475 | static void decoration_handle_configure(void *data, | ||
| 4476 | struct zxdg_toplevel_decoration_v1 *decoration, | ||
| 4477 | enum zxdg_toplevel_decoration_v1_mode mode) { | ||
| 4478 | RGFW_UNUSED(data); RGFW_UNUSED(decoration); | ||
| 4479 | printf("Using %s\n", get_mode_name(mode)); | ||
| 4480 | RGFW_current_mode = mode; | ||
| 4481 | } | ||
| 4482 | |||
| 4483 | static const struct zxdg_toplevel_decoration_v1_listener decoration_listener = { | ||
| 4484 | .configure = decoration_handle_configure, | ||
| 4485 | }; | ||
| 4486 | |||
| 4487 | static void randname(char *buf) { | ||
| 4488 | struct timespec ts; | ||
| 4489 | clock_gettime(CLOCK_REALTIME, &ts); | ||
| 4490 | long r = ts.tv_nsec; | ||
| 4491 | for (int i = 0; i < 6; ++i) { | ||
| 4492 | buf[i] = 'A'+(r&15)+(r&16)*2; | ||
| 4493 | r >>= 5; | ||
| 4494 | } | ||
| 4495 | } | ||
| 4496 | |||
| 4497 | static int anonymous_shm_open(void) { | ||
| 4498 | char name[] = "/RGFW-wayland-XXXXXX"; | ||
| 4499 | int retries = 100; | ||
| 4500 | |||
| 4501 | do { | ||
| 4502 | randname(name + strlen(name) - 6); | ||
| 4503 | |||
| 4504 | --retries; | ||
| 4505 | // shm_open guarantees that O_CLOEXEC is set | ||
| 4506 | int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); | ||
| 4507 | if (fd >= 0) { | ||
| 4508 | shm_unlink(name); | ||
| 4509 | return fd; | ||
| 4510 | } | ||
| 4511 | } while (retries > 0 && errno == EEXIST); | ||
| 4512 | |||
| 4513 | return -1; | ||
| 4514 | } | ||
| 4515 | |||
| 4516 | int create_shm_file(off_t size) { | ||
| 4517 | int fd = anonymous_shm_open(); | ||
| 4518 | if (fd < 0) { | ||
| 4519 | return fd; | ||
| 4520 | } | ||
| 4521 | |||
| 4522 | if (ftruncate(fd, size) < 0) { | ||
| 4523 | close(fd); | ||
| 4524 | return -1; | ||
| 4525 | } | ||
| 4526 | |||
| 4527 | return fd; | ||
| 4528 | } | ||
| 4529 | |||
| 4530 | static void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { | ||
| 4531 | #ifdef RGFW_BUFFER | ||
| 4532 | RGFW_window* win = (RGFW_window*)data; | ||
| 4533 | if ((win->_winArgs & RGFW_NO_CPU_RENDER)) | ||
| 4534 | return; | ||
| 4535 | |||
| 4536 | #ifndef RGFW_X11_DONT_CONVERT_BGR | ||
| 4537 | u32 x, y; | ||
| 4538 | for (y = 0; y < (u32)win->r.h; y++) { | ||
| 4539 | for (x = 0; x < (u32)win->r.w; x++) { | ||
| 4540 | u32 index = (y * 4 * win->r.w) + x * 4; | ||
| 4541 | |||
| 4542 | u8 red = win->buffer[index]; | ||
| 4543 | win->buffer[index] = win->buffer[index + 2]; | ||
| 4544 | win->buffer[index + 2] = red; | ||
| 4545 | |||
| 4546 | } | ||
| 4547 | } | ||
| 4548 | #endif | ||
| 4549 | |||
| 4550 | wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0); | ||
| 4551 | wl_surface_damage_buffer(win->src.surface, 0, 0, win->r.w, win->r.h); | ||
| 4552 | wl_surface_commit(win->src.surface); | ||
| 4553 | #endif | ||
| 4554 | } | ||
| 4555 | |||
| 4556 | static const struct wl_callback_listener wl_surface_frame_listener = { | ||
| 4557 | .done = wl_surface_frame_done, | ||
| 4558 | }; | ||
| 4559 | |||
| 4560 | |||
| 4561 | /* normal wayland RGFW stuff */ | ||
| 4562 | |||
| 4563 | RGFW_area RGFW_getScreenSize(void) { | ||
| 4564 | RGFW_area area = {}; | ||
| 4565 | |||
| 4566 | if (RGFW_root != NULL) | ||
| 4567 | /* this isn't right but it's here for buffers */ | ||
| 4568 | area = RGFW_AREA(RGFW_root->r.w, RGFW_root->r.h); | ||
| 4569 | |||
| 4570 | /* TODO wayland */ | ||
| 4571 | return area; | ||
| 4572 | } | ||
| 4573 | |||
| 4574 | void RGFW_releaseCursor(RGFW_window* win) { | ||
| 4575 | RGFW_UNUSED(win); | ||
| 4576 | } | ||
| 4577 | |||
| 4578 | void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { | ||
| 4579 | RGFW_UNUSED(win); RGFW_UNUSED(r); | ||
| 4580 | |||
| 4581 | /* TODO wayland */ | ||
| 4582 | } | ||
| 4583 | |||
| 4584 | |||
| 4585 | RGFWDEF void RGFW_init_buffer(RGFW_window* win); | ||
| 4586 | void RGFW_init_buffer(RGFW_window* win) { | ||
| 4587 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 4588 | size_t size = win->r.w * win->r.h * 4; | ||
| 4589 | int fd = create_shm_file(size); | ||
| 4590 | if (fd < 0) { | ||
| 4591 | fprintf(stderr, "Failed to create a buffer. size: %ld\n", size); | ||
| 4592 | exit(1); | ||
| 4593 | } | ||
| 4594 | |||
| 4595 | win->buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | ||
| 4596 | if (win->buffer == MAP_FAILED) { | ||
| 4597 | fprintf(stderr, "mmap failed!\n"); | ||
| 4598 | close(fd); | ||
| 4599 | exit(1); | ||
| 4600 | } | ||
| 4601 | |||
| 4602 | struct wl_shm_pool* pool = wl_shm_create_pool(shm, fd, size); | ||
| 4603 | win->src.wl_buffer = wl_shm_pool_create_buffer(pool, 0, win->r.w, win->r.h, win->r.w * 4, | ||
| 4604 | WL_SHM_FORMAT_ARGB8888); | ||
| 4605 | wl_shm_pool_destroy(pool); | ||
| 4606 | |||
| 4607 | close(fd); | ||
| 4608 | |||
| 4609 | wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0); | ||
| 4610 | wl_surface_commit(win->src.surface); | ||
| 4611 | |||
| 4612 | u8 color[] = {0x00, 0x00, 0x00, 0xFF}; | ||
| 4613 | |||
| 4614 | size_t i; | ||
| 4615 | for (i = 0; i < size; i += 4) { | ||
| 4616 | memcpy(&win->buffer[i], color, 4); | ||
| 4617 | } | ||
| 4618 | |||
| 4619 | #if defined(RGFW_OSMESA) | ||
| 4620 | win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); | ||
| 4621 | OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); | ||
| 4622 | #endif | ||
| 4623 | #else | ||
| 4624 | RGFW_UNUSED(win); | ||
| 4625 | #endif | ||
| 4626 | } | ||
| 4627 | |||
| 4628 | |||
| 4629 | RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { | ||
| 4630 | RGFW_window* win = RGFW_window_basic_init(rect, args); | ||
| 4631 | |||
| 4632 | fprintf(stderr, "Warning: RGFW Wayland support is experimental\n"); | ||
| 4633 | |||
| 4634 | win->src.display = wl_display_connect(NULL); | ||
| 4635 | if (win->src.display == NULL) { | ||
| 4636 | #ifdef RGFW_DEBUG | ||
| 4637 | fprintf(stderr, "Failed to load Wayland display\n"); | ||
| 4638 | #endif | ||
| 4639 | return NULL; | ||
| 4640 | } | ||
| 4641 | |||
| 4642 | struct wl_registry *registry = wl_display_get_registry(win->src.display); | ||
| 4643 | wl_registry_add_listener(registry, ®istry_listener, NULL); | ||
| 4644 | |||
| 4645 | wl_display_dispatch(win->src.display); | ||
| 4646 | wl_display_roundtrip(win->src.display); | ||
| 4647 | |||
| 4648 | if (RGFW_compositor == NULL) { | ||
| 4649 | #ifdef RGFW_DEBUG | ||
| 4650 | fprintf(stderr, "Can't find compositor.\n"); | ||
| 4651 | #endif | ||
| 4652 | |||
| 4653 | return NULL; | ||
| 4654 | } | ||
| 4655 | |||
| 4656 | if (RGFW_wl_cursor_theme == NULL) { | ||
| 4657 | RGFW_wl_cursor_theme = wl_cursor_theme_load(NULL, 24, shm); | ||
| 4658 | RGFW_cursor_surface = wl_compositor_create_surface(RGFW_compositor); | ||
| 4659 | |||
| 4660 | struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, "left_ptr"); | ||
| 4661 | RGFW_cursor_image = cursor->images[0]; | ||
| 4662 | struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image); | ||
| 4663 | |||
| 4664 | wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0); | ||
| 4665 | wl_surface_commit(RGFW_cursor_surface); | ||
| 4666 | } | ||
| 4667 | |||
| 4668 | if (RGFW_root == NULL) | ||
| 4669 | xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL); | ||
| 4670 | |||
| 4671 | xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | ||
| 4672 | |||
| 4673 | win->src.surface = wl_compositor_create_surface(RGFW_compositor); | ||
| 4674 | wl_surface_set_user_data(win->src.surface, win); | ||
| 4675 | |||
| 4676 | win->src.xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, win->src.surface); | ||
| 4677 | xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, NULL); | ||
| 4678 | |||
| 4679 | xdg_wm_base_set_user_data(xdg_wm_base, win); | ||
| 4680 | |||
| 4681 | win->src.xdg_toplevel = xdg_surface_get_toplevel(win->src.xdg_surface); | ||
| 4682 | xdg_toplevel_set_user_data(win->src.xdg_toplevel, win); | ||
| 4683 | xdg_toplevel_set_title(win->src.xdg_toplevel, name); | ||
| 4684 | xdg_toplevel_add_listener(win->src.xdg_toplevel, &xdg_toplevel_listener, NULL); | ||
| 4685 | |||
| 4686 | xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h); | ||
| 4687 | |||
| 4688 | if (!(args & RGFW_NO_BORDER)) { | ||
| 4689 | win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( | ||
| 4690 | decoration_manager, win->src.xdg_toplevel); | ||
| 4691 | } | ||
| 4692 | |||
| 4693 | if (args & RGFW_CENTER) { | ||
| 4694 | RGFW_area screenR = RGFW_getScreenSize(); | ||
| 4695 | RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); | ||
| 4696 | } | ||
| 4697 | |||
| 4698 | if (args & RGFW_OPENGL_SOFTWARE) | ||
| 4699 | setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); | ||
| 4700 | |||
| 4701 | wl_display_roundtrip(win->src.display); | ||
| 4702 | |||
| 4703 | wl_surface_commit(win->src.surface); | ||
| 4704 | |||
| 4705 | /* wait for the surface to be configured */ | ||
| 4706 | while (wl_display_dispatch(win->src.display) != -1 && !RGFW_wl_configured) { } | ||
| 4707 | |||
| 4708 | |||
| 4709 | #ifdef RGFW_OPENGL | ||
| 4710 | if ((args & RGFW_NO_INIT_API) == 0) { | ||
| 4711 | win->src.window = wl_egl_window_create(win->src.surface, win->r.w, win->r.h); | ||
| 4712 | RGFW_createOpenGLContext(win); | ||
| 4713 | } | ||
| 4714 | #endif | ||
| 4715 | |||
| 4716 | RGFW_init_buffer(win); | ||
| 4717 | |||
| 4718 | struct wl_callback* callback = wl_surface_frame(win->src.surface); | ||
| 4719 | wl_callback_add_listener(callback, &wl_surface_frame_listener, win); | ||
| 4720 | wl_surface_commit(win->src.surface); | ||
| 4721 | |||
| 4722 | if (args & RGFW_HIDE_MOUSE) { | ||
| 4723 | RGFW_window_showMouse(win, 0); | ||
| 4724 | } | ||
| 4725 | |||
| 4726 | if (RGFW_root == NULL) { | ||
| 4727 | RGFW_root = win; | ||
| 4728 | } | ||
| 4729 | |||
| 4730 | win->src.eventIndex = 0; | ||
| 4731 | win->src.eventLen = 0; | ||
| 4732 | |||
| 4733 | return win; | ||
| 4734 | } | ||
| 4735 | |||
| 4736 | RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { | ||
| 4737 | if (win->_winArgs & RGFW_WINDOW_HIDE) | ||
| 4738 | return NULL; | ||
| 4739 | |||
| 4740 | if (win->src.eventIndex == 0) { | ||
| 4741 | if (wl_display_roundtrip(win->src.display) == -1) { | ||
| 4742 | return NULL; | ||
| 4743 | } | ||
| 4744 | RGFW_resetKey(); | ||
| 4745 | } | ||
| 4746 | |||
| 4747 | #ifdef __linux__ | ||
| 4748 | RGFW_Event* event = RGFW_linux_updateJoystick(win); | ||
| 4749 | if (event != NULL) | ||
| 4750 | return event; | ||
| 4751 | #endif | ||
| 4752 | |||
| 4753 | if (win->src.eventLen == 0) { | ||
| 4754 | return NULL; | ||
| 4755 | } | ||
| 4756 | |||
| 4757 | RGFW_Event ev = RGFW_eventPipe_pop(win); | ||
| 4758 | |||
| 4759 | if (ev.type == 0 || win->event.type == RGFW_quit) { | ||
| 4760 | return NULL; | ||
| 4761 | } | ||
| 4762 | |||
| 4763 | ev.frameTime = win->event.frameTime; | ||
| 4764 | ev.frameTime2 = win->event.frameTime2; | ||
| 4765 | ev.inFocus = win->event.inFocus; | ||
| 4766 | win->event = ev; | ||
| 4767 | |||
| 4768 | return &win->event; | ||
| 4769 | } | ||
| 4770 | |||
| 4771 | |||
| 4772 | void RGFW_window_resize(RGFW_window* win, RGFW_area a) { | ||
| 4773 | RGFW_UNUSED(win); RGFW_UNUSED(a); | ||
| 4774 | |||
| 4775 | /* TODO wayland */ | ||
| 4776 | } | ||
| 4777 | |||
| 4778 | void RGFW_window_move(RGFW_window* win, RGFW_point v) { | ||
| 4779 | RGFW_UNUSED(win); RGFW_UNUSED(v); | ||
| 4780 | |||
| 4781 | /* TODO wayland */ | ||
| 4782 | } | ||
| 4783 | |||
| 4784 | void RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) { | ||
| 4785 | RGFW_UNUSED(win); RGFW_UNUSED(src); RGFW_UNUSED(a); RGFW_UNUSED(channels) | ||
| 4786 | /* TODO wayland */ | ||
| 4787 | } | ||
| 4788 | |||
| 4789 | void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { | ||
| 4790 | RGFW_UNUSED(win); RGFW_UNUSED(v); | ||
| 4791 | |||
| 4792 | /* TODO wayland */ | ||
| 4793 | } | ||
| 4794 | |||
| 4795 | void RGFW_window_showMouse(RGFW_window* win, i8 show) { | ||
| 4796 | RGFW_UNUSED(win); | ||
| 4797 | |||
| 4798 | if (show) { | ||
| 4799 | |||
| 4800 | } | ||
| 4801 | else { | ||
| 4802 | |||
| 4803 | } | ||
| 4804 | |||
| 4805 | /* TODO wayland */ | ||
| 4806 | } | ||
| 4807 | |||
| 4808 | b8 RGFW_window_isMaximized(RGFW_window* win) { | ||
| 4809 | RGFW_UNUSED(win); | ||
| 4810 | /* TODO wayland */ | ||
| 4811 | return 0; | ||
| 4812 | } | ||
| 4813 | |||
| 4814 | b8 RGFW_window_isMinimized(RGFW_window* win) { | ||
| 4815 | RGFW_UNUSED(win); | ||
| 4816 | /* TODO wayland */ | ||
| 4817 | return 0; | ||
| 4818 | } | ||
| 4819 | |||
| 4820 | b8 RGFW_window_isHidden(RGFW_window* win) { | ||
| 4821 | RGFW_UNUSED(win); | ||
| 4822 | /* TODO wayland */ | ||
| 4823 | return 0; | ||
| 4824 | } | ||
| 4825 | |||
| 4826 | b8 RGFW_window_isFullscreen(RGFW_window* win) { | ||
| 4827 | RGFW_UNUSED(win); | ||
| 4828 | /* TODO wayland */ | ||
| 4829 | return 0; | ||
| 4830 | } | ||
| 4831 | |||
| 4832 | RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { | ||
| 4833 | RGFW_UNUSED(win); | ||
| 4834 | /* TODO wayland */ | ||
| 4835 | return RGFW_POINT(0, 0); | ||
| 4836 | } | ||
| 4837 | |||
| 4838 | RGFW_point RGFW_getGlobalMousePoint(void) { | ||
| 4839 | /* TODO wayland */ | ||
| 4840 | return RGFW_POINT(0, 0); | ||
| 4841 | } | ||
| 4842 | |||
| 4843 | void RGFW_window_show(RGFW_window* win) { | ||
| 4844 | //wl_surface_attach(win->src.surface, win->rc., 0, 0); | ||
| 4845 | wl_surface_commit(win->src.surface); | ||
| 4846 | |||
| 4847 | if (win->_winArgs & RGFW_WINDOW_HIDE) | ||
| 4848 | win->_winArgs ^= RGFW_WINDOW_HIDE; | ||
| 4849 | } | ||
| 4850 | |||
| 4851 | void RGFW_window_hide(RGFW_window* win) { | ||
| 4852 | wl_surface_attach(win->src.surface, NULL, 0, 0); | ||
| 4853 | wl_surface_commit(win->src.surface); | ||
| 4854 | win->_winArgs |= RGFW_WINDOW_HIDE; | ||
| 4855 | } | ||
| 4856 | |||
| 4857 | void RGFW_window_setMouseDefault(RGFW_window* win) { | ||
| 4858 | RGFW_UNUSED(win); | ||
| 4859 | |||
| 4860 | RGFW_window_setMouseStandard(win, RGFW_MOUSE_NORMAL); | ||
| 4861 | } | ||
| 4862 | |||
| 4863 | void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { | ||
| 4864 | RGFW_UNUSED(win); | ||
| 4865 | |||
| 4866 | static const char* iconStrings[] = { "left_ptr", "left_ptr", "text", "cross", "pointer", "e-resize", "n-resize", "nw-resize", "ne-resize", "all-resize", "not-allowed" }; | ||
| 4867 | |||
| 4868 | struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]); | ||
| 4869 | RGFW_cursor_image = cursor->images[0]; | ||
| 4870 | struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image); | ||
| 4871 | |||
| 4872 | wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0); | ||
| 4873 | wl_surface_commit(RGFW_cursor_surface); | ||
| 4874 | } | ||
| 4875 | |||
| 4876 | void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { | ||
| 4877 | RGFW_UNUSED(win); RGFW_UNUSED(image); RGFW_UNUSED(a); RGFW_UNUSED(channels) | ||
| 4878 | //struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]); | ||
| 4879 | //RGFW_cursor_image = image; | ||
| 4880 | struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image); | ||
| 4881 | |||
| 4882 | wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0); | ||
| 4883 | wl_surface_commit(RGFW_cursor_surface); | ||
| 4884 | } | ||
| 4885 | |||
| 4886 | void RGFW_window_setName(RGFW_window* win, char* name) { | ||
| 4887 | xdg_toplevel_set_title(win->src.xdg_toplevel, name); | ||
| 4888 | } | ||
| 4889 | |||
| 4890 | void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { | ||
| 4891 | RGFW_UNUSED(win); RGFW_UNUSED(passthrough); | ||
| 4892 | |||
| 4893 | /* TODO wayland */ | ||
| 4894 | } | ||
| 4895 | |||
| 4896 | void RGFW_window_setBorder(RGFW_window* win, b8 border) { | ||
| 4897 | RGFW_UNUSED(win); RGFW_UNUSED(border); | ||
| 4898 | |||
| 4899 | /* TODO wayland */ | ||
| 4900 | } | ||
| 4901 | |||
| 4902 | void RGFW_window_restore(RGFW_window* win) { | ||
| 4903 | RGFW_UNUSED(win); | ||
| 4904 | |||
| 4905 | /* TODO wayland */ | ||
| 4906 | } | ||
| 4907 | |||
| 4908 | void RGFW_window_minimize(RGFW_window* win) { | ||
| 4909 | RGFW_UNUSED(win); | ||
| 4910 | |||
| 4911 | /* TODO wayland */ | ||
| 4912 | } | ||
| 4913 | |||
| 4914 | void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { | ||
| 4915 | RGFW_UNUSED(win); RGFW_UNUSED(a); | ||
| 4916 | |||
| 4917 | /* TODO wayland */ | ||
| 4918 | } | ||
| 4919 | |||
| 4920 | void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { | ||
| 4921 | RGFW_UNUSED(win); RGFW_UNUSED(a); | ||
| 4922 | |||
| 4923 | /* TODO wayland */ | ||
| 4924 | } | ||
| 4925 | |||
| 4926 | RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { | ||
| 4927 | RGFW_monitor m = {}; | ||
| 4928 | RGFW_UNUSED(win); | ||
| 4929 | RGFW_UNUSED(m); | ||
| 4930 | /* TODO wayland */ | ||
| 4931 | |||
| 4932 | return m; | ||
| 4933 | } | ||
| 4934 | |||
| 4935 | |||
| 4936 | #ifndef RGFW_EGL | ||
| 4937 | void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); } | ||
| 4938 | #endif | ||
| 4939 | |||
| 4940 | void RGFW_window_swapBuffers(RGFW_window* win) { | ||
| 4941 | assert(win != NULL); | ||
| 4942 | |||
| 4943 | /* clear the window*/ | ||
| 4944 | #ifdef RGFW_BUFFER | ||
| 4945 | wl_surface_frame_done(win, NULL, 0); | ||
| 4946 | if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) | ||
| 4947 | #endif | ||
| 4948 | { | ||
| 4949 | #ifdef RGFW_OPENGL | ||
| 4950 | eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); | ||
| 4951 | #endif | ||
| 4952 | } | ||
| 4953 | |||
| 4954 | wl_display_flush(win->src.display); | ||
| 4955 | } | ||
| 4956 | |||
| 4957 | void RGFW_window_close(RGFW_window* win) { | ||
| 4958 | #ifdef RGFW_EGL | ||
| 4959 | RGFW_closeEGL(win); | ||
| 4960 | #endif | ||
| 4961 | |||
| 4962 | if (RGFW_root == win) { | ||
| 4963 | RGFW_root = NULL; | ||
| 4964 | } | ||
| 4965 | |||
| 4966 | xdg_toplevel_destroy(win->src.xdg_toplevel); | ||
| 4967 | xdg_surface_destroy(win->src.xdg_surface); | ||
| 4968 | wl_surface_destroy(win->src.surface); | ||
| 4969 | |||
| 4970 | #ifdef RGFW_BUFFER | ||
| 4971 | wl_buffer_destroy(win->src.wl_buffer); | ||
| 4972 | #endif | ||
| 4973 | |||
| 4974 | wl_display_disconnect(win->src.display); | ||
| 4975 | RGFW_FREE(win); | ||
| 4976 | } | ||
| 4977 | |||
| 4978 | RGFW_monitor RGFW_getPrimaryMonitor(void) { | ||
| 4979 | /* TODO wayland */ | ||
| 4980 | |||
| 4981 | return (RGFW_monitor){}; | ||
| 4982 | } | ||
| 4983 | |||
| 4984 | RGFW_monitor* RGFW_getMonitors(void) { | ||
| 4985 | /* TODO wayland */ | ||
| 4986 | |||
| 4987 | return NULL; | ||
| 4988 | } | ||
| 4989 | |||
| 4990 | void RGFW_writeClipboard(const char* text, u32 textLen) { | ||
| 4991 | RGFW_UNUSED(text); RGFW_UNUSED(textLen); | ||
| 4992 | |||
| 4993 | /* TODO wayland */ | ||
| 4994 | } | ||
| 4995 | |||
| 4996 | char* RGFW_readClipboard(size_t* size) { | ||
| 4997 | RGFW_UNUSED(size); | ||
| 4998 | |||
| 4999 | /* TODO wayland */ | ||
| 5000 | |||
| 5001 | return NULL; | ||
| 5002 | } | ||
| 5003 | #endif /* RGFW_WAYLAND */ | ||
| 5004 | |||
| 5005 | /* | ||
| 5006 | End of Wayland defines | ||
| 5007 | */ | ||
| 5008 | |||
| 5009 | |||
| 5010 | /* | ||
| 5011 | |||
| 5012 | Start of Windows defines | ||
| 5013 | |||
| 5014 | |||
| 5015 | */ | ||
| 5016 | |||
| 5017 | #ifdef RGFW_WINDOWS | ||
| 5018 | #define WIN32_LEAN_AND_MEAN | ||
| 5019 | #define OEMRESOURCE | ||
| 5020 | #include <windows.h> | ||
| 5021 | |||
| 5022 | #include <processthreadsapi.h> | ||
| 5023 | #include <wchar.h> | ||
| 5024 | #include <locale.h> | ||
| 5025 | #include <windowsx.h> | ||
| 5026 | #include <shellapi.h> | ||
| 5027 | #include <shellscalingapi.h> | ||
| 5028 | |||
| 5029 | #include <winuser.h> | ||
| 5030 | |||
| 5031 | __declspec(dllimport) int __stdcall WideCharToMultiByte( UINT CodePage, DWORD dwFlags, const WCHAR* lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar); | ||
| 5032 | |||
| 5033 | #ifndef RGFW_NO_XINPUT | ||
| 5034 | typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*); | ||
| 5035 | PFN_XInputGetState XInputGetStateSRC = NULL; | ||
| 5036 | #define XInputGetState XInputGetStateSRC | ||
| 5037 | |||
| 5038 | typedef DWORD (WINAPI * PFN_XInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE); | ||
| 5039 | PFN_XInputGetKeystroke XInputGetKeystrokeSRC = NULL; | ||
| 5040 | #define XInputGetKeystroke XInputGetKeystrokeSRC | ||
| 5041 | |||
| 5042 | static HMODULE RGFW_XInput_dll = NULL; | ||
| 5043 | #endif | ||
| 5044 | |||
| 5045 | u32 RGFW_mouseIconSrc[] = {OCR_NORMAL, OCR_NORMAL, OCR_IBEAM, OCR_CROSS, OCR_HAND, OCR_SIZEWE, OCR_SIZENS, OCR_SIZENWSE, OCR_SIZENESW, OCR_SIZEALL, OCR_NO}; | ||
| 5046 | |||
| 5047 | char* createUTF8FromWideStringWin32(const WCHAR* source); | ||
| 5048 | |||
| 5049 | #define GL_FRONT 0x0404 | ||
| 5050 | #define GL_BACK 0x0405 | ||
| 5051 | #define GL_LEFT 0x0406 | ||
| 5052 | #define GL_RIGHT 0x0407 | ||
| 5053 | |||
| 5054 | #if defined(RGFW_OSMESA) && defined(RGFW_LINK_OSMESA) | ||
| 5055 | |||
| 5056 | typedef void (GLAPIENTRY* PFN_OSMesaDestroyContext)(OSMesaContext); | ||
| 5057 | typedef i32(GLAPIENTRY* PFN_OSMesaMakeCurrent)(OSMesaContext, void*, int, int, int); | ||
| 5058 | typedef OSMesaContext(GLAPIENTRY* PFN_OSMesaCreateContext)(GLenum, OSMesaContext); | ||
| 5059 | |||
| 5060 | PFN_OSMesaMakeCurrent OSMesaMakeCurrentSource; | ||
| 5061 | PFN_OSMesaCreateContext OSMesaCreateContextSource; | ||
| 5062 | PFN_OSMesaDestroyContext OSMesaDestroyContextSource; | ||
| 5063 | |||
| 5064 | #define OSMesaCreateContext OSMesaCreateContextSource | ||
| 5065 | #define OSMesaMakeCurrent OSMesaMakeCurrentSource | ||
| 5066 | #define OSMesaDestroyContext OSMesaDestroyContextSource | ||
| 5067 | #endif | ||
| 5068 | |||
| 5069 | typedef int (*PFN_wglGetSwapIntervalEXT)(void); | ||
| 5070 | PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL; | ||
| 5071 | #define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc | ||
| 5072 | |||
| 5073 | |||
| 5074 | void* RGFWjoystickApi = NULL; | ||
| 5075 | |||
| 5076 | /* these two wgl functions need to be preloaded */ | ||
| 5077 | typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList); | ||
| 5078 | PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; | ||
| 5079 | |||
| 5080 | /* defines for creating ARB attributes */ | ||
| 5081 | #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 | ||
| 5082 | #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 | ||
| 5083 | #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 | ||
| 5084 | #define WGL_DRAW_TO_WINDOW_ARB 0x2001 | ||
| 5085 | #define WGL_ACCELERATION_ARB 0x2003 | ||
| 5086 | #define WGL_NO_ACCELERATION_ARB 0x2025 | ||
| 5087 | #define WGL_DOUBLE_BUFFER_ARB 0x2011 | ||
| 5088 | #define WGL_COLOR_BITS_ARB 0x2014 | ||
| 5089 | #define WGL_RED_BITS_ARB 0x2015 | ||
| 5090 | #define WGL_RED_SHIFT_ARB 0x2016 | ||
| 5091 | #define WGL_GREEN_BITS_ARB 0x2017 | ||
| 5092 | #define WGL_GREEN_SHIFT_ARB 0x2018 | ||
| 5093 | #define WGL_BLUE_BITS_ARB 0x2019 | ||
| 5094 | #define WGL_BLUE_SHIFT_ARB 0x201a | ||
| 5095 | #define WGL_ALPHA_BITS_ARB 0x201b | ||
| 5096 | #define WGL_ALPHA_SHIFT_ARB 0x201c | ||
| 5097 | #define WGL_ACCUM_BITS_ARB 0x201d | ||
| 5098 | #define WGL_ACCUM_RED_BITS_ARB 0x201e | ||
| 5099 | #define WGL_ACCUM_GREEN_BITS_ARB 0x201f | ||
| 5100 | #define WGL_ACCUM_BLUE_BITS_ARB 0x2020 | ||
| 5101 | #define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 | ||
| 5102 | #define WGL_DEPTH_BITS_ARB 0x2022 | ||
| 5103 | #define WGL_AUX_BUFFERS_ARB 0x2024 | ||
| 5104 | #define WGL_STEREO_ARB 0x2012 | ||
| 5105 | #define WGL_DEPTH_BITS_ARB 0x2022 | ||
| 5106 | #define WGL_STENCIL_BITS_ARB 0x2023 | ||
| 5107 | #define WGL_FULL_ACCELERATION_ARB 0x2027 | ||
| 5108 | #define WGL_CONTEXT_FLAGS_ARB 0x2094 | ||
| 5109 | #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 | ||
| 5110 | #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 | ||
| 5111 | #define WGL_SAMPLE_BUFFERS_ARB 0x2041 | ||
| 5112 | #define WGL_SAMPLES_ARB 0x2042 | ||
| 5113 | #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 | ||
| 5114 | |||
| 5115 | #ifndef RGFW_EGL | ||
| 5116 | static HMODULE wglinstance = NULL; | ||
| 5117 | #endif | ||
| 5118 | |||
| 5119 | #ifdef RGFW_WGL_LOAD | ||
| 5120 | typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC); | ||
| 5121 | typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC); | ||
| 5122 | typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR); | ||
| 5123 | typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC); | ||
| 5124 | typedef HDC(WINAPI* PFN_wglGetCurrentDC)(); | ||
| 5125 | typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(); | ||
| 5126 | |||
| 5127 | PFN_wglCreateContext wglCreateContextSRC; | ||
| 5128 | PFN_wglDeleteContext wglDeleteContextSRC; | ||
| 5129 | PFN_wglGetProcAddress wglGetProcAddressSRC; | ||
| 5130 | PFN_wglMakeCurrent wglMakeCurrentSRC; | ||
| 5131 | PFN_wglGetCurrentDC wglGetCurrentDCSRC; | ||
| 5132 | PFN_wglGetCurrentContext wglGetCurrentContextSRC; | ||
| 5133 | |||
| 5134 | #define wglCreateContext wglCreateContextSRC | ||
| 5135 | #define wglDeleteContext wglDeleteContextSRC | ||
| 5136 | #define wglGetProcAddress wglGetProcAddressSRC | ||
| 5137 | #define wglMakeCurrent wglMakeCurrentSRC | ||
| 5138 | |||
| 5139 | #define wglGetCurrentDC wglGetCurrentDCSRC | ||
| 5140 | #define wglGetCurrentContext wglGetCurrentContextSRC | ||
| 5141 | #endif | ||
| 5142 | |||
| 5143 | #ifdef RGFW_OPENGL | ||
| 5144 | void* RGFW_getProcAddress(const char* procname) { | ||
| 5145 | void* proc = (void*) wglGetProcAddress(procname); | ||
| 5146 | if (proc) | ||
| 5147 | return proc; | ||
| 5148 | |||
| 5149 | return (void*) GetProcAddress(wglinstance, procname); | ||
| 5150 | } | ||
| 5151 | |||
| 5152 | typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats); | ||
| 5153 | static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL; | ||
| 5154 | #endif | ||
| 5155 | |||
| 5156 | RGFW_window RGFW_eventWindow; | ||
| 5157 | |||
| 5158 | LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { | ||
| 5159 | switch (message) { | ||
| 5160 | case WM_MOVE: | ||
| 5161 | RGFW_eventWindow.r.x = LOWORD(lParam); | ||
| 5162 | RGFW_eventWindow.r.y = HIWORD(lParam); | ||
| 5163 | RGFW_eventWindow.src.window = hWnd; | ||
| 5164 | return DefWindowProcA(hWnd, message, wParam, lParam); | ||
| 5165 | case WM_SIZE: | ||
| 5166 | RGFW_eventWindow.r.w = LOWORD(lParam); | ||
| 5167 | RGFW_eventWindow.r.h = HIWORD(lParam); | ||
| 5168 | RGFW_eventWindow.src.window = hWnd; | ||
| 5169 | return DefWindowProcA(hWnd, message, wParam, lParam); // Call DefWindowProc after handling | ||
| 5170 | default: | ||
| 5171 | return DefWindowProcA(hWnd, message, wParam, lParam); | ||
| 5172 | } | ||
| 5173 | } | ||
| 5174 | |||
| 5175 | #ifndef RGFW_NO_DPI | ||
| 5176 | static HMODULE RGFW_Shcore_dll = NULL; | ||
| 5177 | typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); | ||
| 5178 | PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL; | ||
| 5179 | #define GetDpiForMonitor GetDpiForMonitorSRC | ||
| 5180 | #endif | ||
| 5181 | |||
| 5182 | __declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod); | ||
| 5183 | |||
| 5184 | #ifndef RGFW_NO_XINPUT | ||
| 5185 | void RGFW_loadXInput(void) { | ||
| 5186 | u32 i; | ||
| 5187 | static const char* names[] = { | ||
| 5188 | "xinput1_4.dll", | ||
| 5189 | "xinput1_3.dll", | ||
| 5190 | "xinput9_1_0.dll", | ||
| 5191 | "xinput1_2.dll", | ||
| 5192 | "xinput1_1.dll" | ||
| 5193 | }; | ||
| 5194 | |||
| 5195 | for (i = 0; i < sizeof(names) / sizeof(const char*); i++) { | ||
| 5196 | RGFW_XInput_dll = LoadLibraryA(names[i]); | ||
| 5197 | |||
| 5198 | if (RGFW_XInput_dll) { | ||
| 5199 | XInputGetStateSRC = (PFN_XInputGetState)(void*)GetProcAddress(RGFW_XInput_dll, "XInputGetState"); | ||
| 5200 | |||
| 5201 | if (XInputGetStateSRC == NULL) | ||
| 5202 | printf("Failed to load XInputGetState"); | ||
| 5203 | } | ||
| 5204 | } | ||
| 5205 | } | ||
| 5206 | #endif | ||
| 5207 | |||
| 5208 | RGFWDEF void RGFW_init_buffer(RGFW_window* win); | ||
| 5209 | void RGFW_init_buffer(RGFW_window* win) { | ||
| 5210 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 5211 | if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) | ||
| 5212 | RGFW_bufferSize = RGFW_getScreenSize(); | ||
| 5213 | |||
| 5214 | BITMAPV5HEADER bi = { 0 }; | ||
| 5215 | ZeroMemory(&bi, sizeof(bi)); | ||
| 5216 | bi.bV5Size = sizeof(bi); | ||
| 5217 | bi.bV5Width = RGFW_bufferSize.w; | ||
| 5218 | bi.bV5Height = -((LONG) RGFW_bufferSize.h); | ||
| 5219 | bi.bV5Planes = 1; | ||
| 5220 | bi.bV5BitCount = 32; | ||
| 5221 | bi.bV5Compression = BI_BITFIELDS; | ||
| 5222 | bi.bV5BlueMask = 0x00ff0000; | ||
| 5223 | bi.bV5GreenMask = 0x0000ff00; | ||
| 5224 | bi.bV5RedMask = 0x000000ff; | ||
| 5225 | bi.bV5AlphaMask = 0xff000000; | ||
| 5226 | |||
| 5227 | win->src.bitmap = CreateDIBSection(win->src.hdc, | ||
| 5228 | (BITMAPINFO*) &bi, | ||
| 5229 | DIB_RGB_COLORS, | ||
| 5230 | (void**) &win->buffer, | ||
| 5231 | NULL, | ||
| 5232 | (DWORD) 0); | ||
| 5233 | |||
| 5234 | win->src.hdcMem = CreateCompatibleDC(win->src.hdc); | ||
| 5235 | |||
| 5236 | #if defined(RGFW_OSMESA) | ||
| 5237 | win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); | ||
| 5238 | OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); | ||
| 5239 | #endif | ||
| 5240 | #else | ||
| 5241 | RGFW_UNUSED(win); /*!< if buffer rendering is not being used */ | ||
| 5242 | #endif | ||
| 5243 | } | ||
| 5244 | |||
| 5245 | void RGFW_window_setDND(RGFW_window* win, b8 allow) { | ||
| 5246 | DragAcceptFiles(win->src.window, allow); | ||
| 5247 | } | ||
| 5248 | |||
| 5249 | void RGFW_releaseCursor(RGFW_window* win) { | ||
| 5250 | RGFW_UNUSED(win); | ||
| 5251 | ClipCursor(NULL); | ||
| 5252 | const RAWINPUTDEVICE id = { 0x01, 0x02, RIDEV_REMOVE, NULL }; | ||
| 5253 | RegisterRawInputDevices(&id, 1, sizeof(id)); | ||
| 5254 | } | ||
| 5255 | |||
| 5256 | void RGFW_captureCursor(RGFW_window* win, RGFW_rect rect) { | ||
| 5257 | RGFW_UNUSED(win); RGFW_UNUSED(rect); | ||
| 5258 | |||
| 5259 | RECT clipRect; | ||
| 5260 | GetClientRect(win->src.window, &clipRect); | ||
| 5261 | ClientToScreen(win->src.window, (POINT*) &clipRect.left); | ||
| 5262 | ClientToScreen(win->src.window, (POINT*) &clipRect.right); | ||
| 5263 | ClipCursor(&clipRect); | ||
| 5264 | |||
| 5265 | const RAWINPUTDEVICE id = { 0x01, 0x02, 0, win->src.window }; | ||
| 5266 | RegisterRawInputDevices(&id, 1, sizeof(id)); | ||
| 5267 | } | ||
| 5268 | |||
| 5269 | RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { | ||
| 5270 | #ifndef RGFW_NO_XINPUT | ||
| 5271 | if (RGFW_XInput_dll == NULL) | ||
| 5272 | RGFW_loadXInput(); | ||
| 5273 | #endif | ||
| 5274 | |||
| 5275 | #ifndef RGFW_NO_DPI | ||
| 5276 | if (RGFW_Shcore_dll == NULL) { | ||
| 5277 | RGFW_Shcore_dll = LoadLibraryA("shcore.dll"); | ||
| 5278 | GetDpiForMonitorSRC = (PFN_GetDpiForMonitor)(void*)GetProcAddress(RGFW_Shcore_dll, "GetDpiForMonitor"); | ||
| 5279 | SetProcessDPIAware(); | ||
| 5280 | } | ||
| 5281 | #endif | ||
| 5282 | |||
| 5283 | if (wglinstance == NULL) { | ||
| 5284 | wglinstance = LoadLibraryA("opengl32.dll"); | ||
| 5285 | #ifdef RGFW_WGL_LOAD | ||
| 5286 | wglCreateContextSRC = (PFN_wglCreateContext) GetProcAddress(wglinstance, "wglCreateContext"); | ||
| 5287 | wglDeleteContextSRC = (PFN_wglDeleteContext) GetProcAddress(wglinstance, "wglDeleteContext"); | ||
| 5288 | wglGetProcAddressSRC = (PFN_wglGetProcAddress) GetProcAddress(wglinstance, "wglGetProcAddress"); | ||
| 5289 | wglMakeCurrentSRC = (PFN_wglMakeCurrent) GetProcAddress(wglinstance, "wglMakeCurrent"); | ||
| 5290 | wglGetCurrentDCSRC = (PFN_wglGetCurrentDC) GetProcAddress(wglinstance, "wglGetCurrentDC"); | ||
| 5291 | wglGetCurrentContextSRC = (PFN_wglGetCurrentContext) GetProcAddress(wglinstance, "wglGetCurrentContext"); | ||
| 5292 | #endif | ||
| 5293 | } | ||
| 5294 | |||
| 5295 | if (name[0] == 0) name = (char*) " "; | ||
| 5296 | |||
| 5297 | RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1); | ||
| 5298 | RGFW_eventWindow.src.window = NULL; | ||
| 5299 | |||
| 5300 | RGFW_window* win = RGFW_window_basic_init(rect, args); | ||
| 5301 | |||
| 5302 | win->src.maxSize = RGFW_AREA(0, 0); | ||
| 5303 | win->src.minSize = RGFW_AREA(0, 0); | ||
| 5304 | |||
| 5305 | |||
| 5306 | HINSTANCE inh = GetModuleHandleA(NULL); | ||
| 5307 | |||
| 5308 | #ifndef __cplusplus | ||
| 5309 | WNDCLASSA Class = { 0 }; /*!< Setup the Window class. */ | ||
| 5310 | #else | ||
| 5311 | WNDCLASSA Class = { }; | ||
| 5312 | #endif | ||
| 5313 | |||
| 5314 | if (RGFW_className == NULL) | ||
| 5315 | RGFW_className = (char*)name; | ||
| 5316 | |||
| 5317 | Class.lpszClassName = RGFW_className; | ||
| 5318 | Class.hInstance = inh; | ||
| 5319 | Class.hCursor = LoadCursor(NULL, IDC_ARROW); | ||
| 5320 | Class.lpfnWndProc = WndProc; | ||
| 5321 | |||
| 5322 | RegisterClassA(&Class); | ||
| 5323 | |||
| 5324 | DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; | ||
| 5325 | |||
| 5326 | RECT windowRect, clientRect; | ||
| 5327 | |||
| 5328 | if (!(args & RGFW_NO_BORDER)) { | ||
| 5329 | window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX; | ||
| 5330 | |||
| 5331 | if (!(args & RGFW_NO_RESIZE)) | ||
| 5332 | window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME; | ||
| 5333 | } else | ||
| 5334 | window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX; | ||
| 5335 | |||
| 5336 | HWND dummyWin = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h, 0, 0, inh, 0); | ||
| 5337 | |||
| 5338 | GetWindowRect(dummyWin, &windowRect); | ||
| 5339 | GetClientRect(dummyWin, &clientRect); | ||
| 5340 | |||
| 5341 | win->src.hOffset = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top); | ||
| 5342 | win->src.window = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h + win->src.hOffset, 0, 0, inh, 0); | ||
| 5343 | |||
| 5344 | if (args & RGFW_ALLOW_DND) { | ||
| 5345 | win->_winArgs |= RGFW_ALLOW_DND; | ||
| 5346 | RGFW_window_setDND(win, 1); | ||
| 5347 | } | ||
| 5348 | win->src.hdc = GetDC(win->src.window); | ||
| 5349 | |||
| 5350 | if ((args & RGFW_NO_INIT_API) == 0) { | ||
| 5351 | #ifdef RGFW_DIRECTX | ||
| 5352 | assert(FAILED(CreateDXGIFactory(&__uuidof(IDXGIFactory), (void**) &RGFW_dxInfo.pFactory)) == 0); | ||
| 5353 | |||
| 5354 | if (FAILED(RGFW_dxInfo.pFactory->lpVtbl->EnumAdapters(RGFW_dxInfo.pFactory, 0, &RGFW_dxInfo.pAdapter))) { | ||
| 5355 | fprintf(stderr, "Failed to enumerate DXGI adapters\n"); | ||
| 5356 | RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory); | ||
| 5357 | return NULL; | ||
| 5358 | } | ||
| 5359 | |||
| 5360 | D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 }; | ||
| 5361 | |||
| 5362 | if (FAILED(D3D11CreateDevice(RGFW_dxInfo.pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, featureLevels, 1, D3D11_SDK_VERSION, &RGFW_dxInfo.pDevice, NULL, &RGFW_dxInfo.pDeviceContext))) { | ||
| 5363 | fprintf(stderr, "Failed to create Direct3D device\n"); | ||
| 5364 | RGFW_dxInfo.pAdapter->lpVtbl->Release(RGFW_dxInfo.pAdapter); | ||
| 5365 | RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory); | ||
| 5366 | return NULL; | ||
| 5367 | } | ||
| 5368 | |||
| 5369 | DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 }; | ||
| 5370 | swapChainDesc.BufferCount = 1; | ||
| 5371 | swapChainDesc.BufferDesc.Width = win->r.w; | ||
| 5372 | swapChainDesc.BufferDesc.Height = win->r.h; | ||
| 5373 | swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; | ||
| 5374 | swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; | ||
| 5375 | swapChainDesc.OutputWindow = win->src.window; | ||
| 5376 | swapChainDesc.SampleDesc.Count = 1; | ||
| 5377 | swapChainDesc.SampleDesc.Quality = 0; | ||
| 5378 | swapChainDesc.Windowed = TRUE; | ||
| 5379 | RGFW_dxInfo.pFactory->lpVtbl->CreateSwapChain(RGFW_dxInfo.pFactory, (IUnknown*) RGFW_dxInfo.pDevice, &swapChainDesc, &win->src.swapchain); | ||
| 5380 | |||
| 5381 | ID3D11Texture2D* pBackBuffer; | ||
| 5382 | win->src.swapchain->lpVtbl->GetBuffer(win->src.swapchain, 0, &__uuidof(ID3D11Texture2D), (LPVOID*) &pBackBuffer); | ||
| 5383 | RGFW_dxInfo.pDevice->lpVtbl->CreateRenderTargetView(RGFW_dxInfo.pDevice, (ID3D11Resource*) pBackBuffer, NULL, &win->src.renderTargetView); | ||
| 5384 | pBackBuffer->lpVtbl->Release(pBackBuffer); | ||
| 5385 | |||
| 5386 | D3D11_TEXTURE2D_DESC depthStencilDesc = { 0 }; | ||
| 5387 | depthStencilDesc.Width = win->r.w; | ||
| 5388 | depthStencilDesc.Height = win->r.h; | ||
| 5389 | depthStencilDesc.MipLevels = 1; | ||
| 5390 | depthStencilDesc.ArraySize = 1; | ||
| 5391 | depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; | ||
| 5392 | depthStencilDesc.SampleDesc.Count = 1; | ||
| 5393 | depthStencilDesc.SampleDesc.Quality = 0; | ||
| 5394 | depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; | ||
| 5395 | depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; | ||
| 5396 | |||
| 5397 | ID3D11Texture2D* pDepthStencilTexture = NULL; | ||
| 5398 | RGFW_dxInfo.pDevice->lpVtbl->CreateTexture2D(RGFW_dxInfo.pDevice, &depthStencilDesc, NULL, &pDepthStencilTexture); | ||
| 5399 | |||
| 5400 | D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc = { 0 }; | ||
| 5401 | depthStencilViewDesc.Format = depthStencilDesc.Format; | ||
| 5402 | depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; | ||
| 5403 | depthStencilViewDesc.Texture2D.MipSlice = 0; | ||
| 5404 | |||
| 5405 | RGFW_dxInfo.pDevice->lpVtbl->CreateDepthStencilView(RGFW_dxInfo.pDevice, (ID3D11Resource*) pDepthStencilTexture, &depthStencilViewDesc, &win->src.pDepthStencilView); | ||
| 5406 | |||
| 5407 | pDepthStencilTexture->lpVtbl->Release(pDepthStencilTexture); | ||
| 5408 | |||
| 5409 | RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, win->src.pDepthStencilView); | ||
| 5410 | #endif | ||
| 5411 | |||
| 5412 | #ifdef RGFW_OPENGL | ||
| 5413 | HDC dummy_dc = GetDC(dummyWin); | ||
| 5414 | |||
| 5415 | u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; | ||
| 5416 | |||
| 5417 | //if (RGFW_DOUBLE_BUFFER) | ||
| 5418 | pfd_flags |= PFD_DOUBLEBUFFER; | ||
| 5419 | |||
| 5420 | PIXELFORMATDESCRIPTOR pfd = { | ||
| 5421 | sizeof(pfd), | ||
| 5422 | 1, /* version */ | ||
| 5423 | pfd_flags, | ||
| 5424 | PFD_TYPE_RGBA, /* ipixel type */ | ||
| 5425 | 24, /* color bits */ | ||
| 5426 | 0, 0, 0, 0, 0, 0, | ||
| 5427 | 8, /* alpha bits */ | ||
| 5428 | 0, 0, 0, 0, 0, 0, | ||
| 5429 | 32, /* depth bits */ | ||
| 5430 | 8, /* stencil bits */ | ||
| 5431 | 0, | ||
| 5432 | PFD_MAIN_PLANE, /* Layer type */ | ||
| 5433 | 0, 0, 0, 0 | ||
| 5434 | }; | ||
| 5435 | |||
| 5436 | int pixel_format = ChoosePixelFormat(dummy_dc, &pfd); | ||
| 5437 | SetPixelFormat(dummy_dc, pixel_format, &pfd); | ||
| 5438 | |||
| 5439 | HGLRC dummy_context = wglCreateContext(dummy_dc); | ||
| 5440 | wglMakeCurrent(dummy_dc, dummy_context); | ||
| 5441 | |||
| 5442 | if (wglChoosePixelFormatARB == NULL) { | ||
| 5443 | wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress("wglCreateContextAttribsARB"); | ||
| 5444 | wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress("wglChoosePixelFormatARB"); | ||
| 5445 | } | ||
| 5446 | |||
| 5447 | wglMakeCurrent(dummy_dc, 0); | ||
| 5448 | wglDeleteContext(dummy_context); | ||
| 5449 | ReleaseDC(dummyWin, dummy_dc); | ||
| 5450 | |||
| 5451 | /* try to create the pixel format we want for opengl and then try to create an opengl context for the specified version */ | ||
| 5452 | if (wglCreateContextAttribsARB != NULL) { | ||
| 5453 | PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 24, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | ||
| 5454 | |||
| 5455 | if (args & RGFW_OPENGL_SOFTWARE) | ||
| 5456 | pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED; | ||
| 5457 | |||
| 5458 | if (wglChoosePixelFormatARB != NULL) { | ||
| 5459 | i32* pixel_format_attribs = (i32*)RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE); | ||
| 5460 | |||
| 5461 | int pixel_format; | ||
| 5462 | UINT num_formats; | ||
| 5463 | wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &pixel_format, &num_formats); | ||
| 5464 | if (!num_formats) { | ||
| 5465 | printf("Failed to create a pixel format for WGL.\n"); | ||
| 5466 | } | ||
| 5467 | |||
| 5468 | DescribePixelFormat(win->src.hdc, pixel_format, sizeof(pfd), &pfd); | ||
| 5469 | if (!SetPixelFormat(win->src.hdc, pixel_format, &pfd)) { | ||
| 5470 | printf("Failed to set the WGL pixel format.\n"); | ||
| 5471 | } | ||
| 5472 | } | ||
| 5473 | |||
| 5474 | /* create opengl/WGL context for the specified version */ | ||
| 5475 | u32 index = 0; | ||
| 5476 | i32 attribs[40]; | ||
| 5477 | |||
| 5478 | if (RGFW_profile == RGFW_GL_CORE) { | ||
| 5479 | SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB); | ||
| 5480 | } | ||
| 5481 | else { | ||
| 5482 | SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); | ||
| 5483 | } | ||
| 5484 | |||
| 5485 | if (RGFW_majorVersion || RGFW_minorVersion) { | ||
| 5486 | SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion); | ||
| 5487 | SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion); | ||
| 5488 | } | ||
| 5489 | |||
| 5490 | SET_ATTRIB(0, 0); | ||
| 5491 | |||
| 5492 | win->src.ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs); | ||
| 5493 | } else { /* fall back to a default context (probably opengl 2 or something) */ | ||
| 5494 | fprintf(stderr, "Failed to create an accelerated OpenGL Context\n"); | ||
| 5495 | |||
| 5496 | int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd); | ||
| 5497 | SetPixelFormat(win->src.hdc, pixel_format, &pfd); | ||
| 5498 | |||
| 5499 | win->src.ctx = wglCreateContext(win->src.hdc); | ||
| 5500 | } | ||
| 5501 | |||
| 5502 | wglMakeCurrent(win->src.hdc, win->src.ctx); | ||
| 5503 | #endif | ||
| 5504 | } | ||
| 5505 | |||
| 5506 | #ifdef RGFW_OSMESA | ||
| 5507 | #ifdef RGFW_LINK_OSM ESA | ||
| 5508 | OSMesaMakeCurrentSource = (PFN_OSMesaMakeCurrent) GetProcAddress(win->src.hdc, "OSMesaMakeCurrent"); | ||
| 5509 | OSMesaCreateContextSource = (PFN_OSMesaCreateContext) GetProcAddress(win->src.hdc, "OSMesaCreateContext"); | ||
| 5510 | OSMesaDestroyContextSource = (PFN_OSMesaDestroyContext) GetProcAddress(win->src.hdc, "OSMesaDestroyContext"); | ||
| 5511 | #endif | ||
| 5512 | #endif | ||
| 5513 | |||
| 5514 | #ifdef RGFW_OPENGL | ||
| 5515 | if ((args & RGFW_NO_INIT_API) == 0) { | ||
| 5516 | ReleaseDC(win->src.window, win->src.hdc); | ||
| 5517 | win->src.hdc = GetDC(win->src.window); | ||
| 5518 | wglMakeCurrent(win->src.hdc, win->src.ctx); | ||
| 5519 | } | ||
| 5520 | #endif | ||
| 5521 | |||
| 5522 | DestroyWindow(dummyWin); | ||
| 5523 | RGFW_init_buffer(win); | ||
| 5524 | |||
| 5525 | |||
| 5526 | #ifndef RGFW_NO_MONITOR | ||
| 5527 | if (args & RGFW_SCALE_TO_MONITOR) | ||
| 5528 | RGFW_window_scaleToMonitor(win); | ||
| 5529 | #endif | ||
| 5530 | |||
| 5531 | if (args & RGFW_CENTER) { | ||
| 5532 | RGFW_area screenR = RGFW_getScreenSize(); | ||
| 5533 | RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); | ||
| 5534 | } | ||
| 5535 | |||
| 5536 | #ifdef RGFW_EGL | ||
| 5537 | if ((args & RGFW_NO_INIT_API) == 0) | ||
| 5538 | RGFW_createOpenGLContext(win); | ||
| 5539 | #endif | ||
| 5540 | |||
| 5541 | if (args & RGFW_HIDE_MOUSE) | ||
| 5542 | RGFW_window_showMouse(win, 0); | ||
| 5543 | |||
| 5544 | if (args & RGFW_TRANSPARENT_WINDOW) { | ||
| 5545 | SetWindowLong(win->src.window, GWL_EXSTYLE, GetWindowLong(win->src.window, GWL_EXSTYLE) | WS_EX_LAYERED); | ||
| 5546 | SetLayeredWindowAttributes(win->src.window, RGB(255, 255, 255), RGFW_ALPHA, LWA_ALPHA); | ||
| 5547 | } | ||
| 5548 | |||
| 5549 | ShowWindow(win->src.window, SW_SHOWNORMAL); | ||
| 5550 | |||
| 5551 | if (RGFW_root == NULL) | ||
| 5552 | RGFW_root = win; | ||
| 5553 | |||
| 5554 | #ifdef RGFW_OPENGL | ||
| 5555 | else | ||
| 5556 | wglShareLists(RGFW_root->src.ctx, win->src.ctx); | ||
| 5557 | #endif | ||
| 5558 | |||
| 5559 | return win; | ||
| 5560 | } | ||
| 5561 | |||
| 5562 | void RGFW_window_setBorder(RGFW_window* win, u8 border) { | ||
| 5563 | DWORD style = GetWindowLong(win->src.window, GWL_STYLE); | ||
| 5564 | |||
| 5565 | if (border == 0) { | ||
| 5566 | SetWindowLong(win->src.window, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW); | ||
| 5567 | SetWindowPos( | ||
| 5568 | win->src.window, HWND_TOP, 0, 0, 0, 0, | ||
| 5569 | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | ||
| 5570 | ); | ||
| 5571 | } | ||
| 5572 | else { | ||
| 5573 | SetWindowLong(win->src.window, GWL_STYLE, style | WS_OVERLAPPEDWINDOW); | ||
| 5574 | SetWindowPos( | ||
| 5575 | win->src.window, HWND_TOP, 0, 0, 0, 0, | ||
| 5576 | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | ||
| 5577 | ); | ||
| 5578 | } | ||
| 5579 | } | ||
| 5580 | |||
| 5581 | |||
| 5582 | RGFW_area RGFW_getScreenSize(void) { | ||
| 5583 | return RGFW_AREA(GetDeviceCaps(GetDC(NULL), HORZRES), GetDeviceCaps(GetDC(NULL), VERTRES)); | ||
| 5584 | } | ||
| 5585 | |||
| 5586 | RGFW_point RGFW_getGlobalMousePoint(void) { | ||
| 5587 | POINT p; | ||
| 5588 | GetCursorPos(&p); | ||
| 5589 | |||
| 5590 | return RGFW_POINT(p.x, p.y); | ||
| 5591 | } | ||
| 5592 | |||
| 5593 | RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { | ||
| 5594 | POINT p; | ||
| 5595 | GetCursorPos(&p); | ||
| 5596 | ScreenToClient(win->src.window, &p); | ||
| 5597 | |||
| 5598 | return RGFW_POINT(p.x, p.y); | ||
| 5599 | } | ||
| 5600 | |||
| 5601 | void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { | ||
| 5602 | assert(win != NULL); | ||
| 5603 | win->src.minSize = a; | ||
| 5604 | } | ||
| 5605 | |||
| 5606 | void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { | ||
| 5607 | assert(win != NULL); | ||
| 5608 | win->src.maxSize = a; | ||
| 5609 | } | ||
| 5610 | |||
| 5611 | |||
| 5612 | void RGFW_window_minimize(RGFW_window* win) { | ||
| 5613 | assert(win != NULL); | ||
| 5614 | |||
| 5615 | ShowWindow(win->src.window, SW_MINIMIZE); | ||
| 5616 | } | ||
| 5617 | |||
| 5618 | void RGFW_window_restore(RGFW_window* win) { | ||
| 5619 | assert(win != NULL); | ||
| 5620 | |||
| 5621 | ShowWindow(win->src.window, SW_RESTORE); | ||
| 5622 | } | ||
| 5623 | |||
| 5624 | |||
| 5625 | u8 RGFW_xinput2RGFW[] = { | ||
| 5626 | RGFW_JS_A, /* or PS X button */ | ||
| 5627 | RGFW_JS_B, /* or PS circle button */ | ||
| 5628 | RGFW_JS_X, /* or PS square button */ | ||
| 5629 | RGFW_JS_Y, /* or PS triangle button */ | ||
| 5630 | RGFW_JS_R1, /* right bumper */ | ||
| 5631 | RGFW_JS_L1, /* left bump */ | ||
| 5632 | RGFW_JS_L2, /* left trigger*/ | ||
| 5633 | RGFW_JS_R2, /* right trigger */ | ||
| 5634 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 5635 | RGFW_JS_UP, /* dpad up */ | ||
| 5636 | RGFW_JS_DOWN, /* dpad down*/ | ||
| 5637 | RGFW_JS_LEFT, /* dpad left */ | ||
| 5638 | RGFW_JS_RIGHT, /* dpad right */ | ||
| 5639 | RGFW_JS_START, /* start button */ | ||
| 5640 | RGFW_JS_SELECT/* select button */ | ||
| 5641 | }; | ||
| 5642 | |||
| 5643 | static i32 RGFW_checkXInput(RGFW_window* win, RGFW_Event* e) { | ||
| 5644 | RGFW_UNUSED(win) | ||
| 5645 | |||
| 5646 | size_t i; | ||
| 5647 | for (i = 0; i < 4; i++) { | ||
| 5648 | XINPUT_KEYSTROKE keystroke; | ||
| 5649 | |||
| 5650 | if (XInputGetKeystroke == NULL) | ||
| 5651 | return 0; | ||
| 5652 | |||
| 5653 | DWORD result = XInputGetKeystroke((DWORD)i, 0, &keystroke); | ||
| 5654 | |||
| 5655 | if ((keystroke.Flags & XINPUT_KEYSTROKE_REPEAT) == 0 && result != ERROR_EMPTY) { | ||
| 5656 | if (result != ERROR_SUCCESS) | ||
| 5657 | return 0; | ||
| 5658 | |||
| 5659 | if (keystroke.VirtualKey > VK_PAD_BACK) | ||
| 5660 | continue; | ||
| 5661 | |||
| 5662 | // RGFW_jsButtonPressed + 1 = RGFW_jsButtonReleased | ||
| 5663 | e->type = RGFW_jsButtonPressed + !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN); | ||
| 5664 | e->button = RGFW_xinput2RGFW[keystroke.VirtualKey - 0x5800]; | ||
| 5665 | RGFW_jsPressed[i][e->button] = !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN); | ||
| 5666 | |||
| 5667 | return 1; | ||
| 5668 | } | ||
| 5669 | |||
| 5670 | XINPUT_STATE state; | ||
| 5671 | if (XInputGetState == NULL || | ||
| 5672 | XInputGetState((DWORD) i, &state) == ERROR_DEVICE_NOT_CONNECTED | ||
| 5673 | ) | ||
| 5674 | return 0; | ||
| 5675 | #define INPUT_DEADZONE ( 0.24f * (float)(0x7FFF) ) // Default to 24% of the +/- 32767 range. This is a reasonable default value but can be altered if needed. | ||
| 5676 | |||
| 5677 | if ((state.Gamepad.sThumbLX < INPUT_DEADZONE && | ||
| 5678 | state.Gamepad.sThumbLX > -INPUT_DEADZONE) && | ||
| 5679 | (state.Gamepad.sThumbLY < INPUT_DEADZONE && | ||
| 5680 | state.Gamepad.sThumbLY > -INPUT_DEADZONE)) | ||
| 5681 | { | ||
| 5682 | state.Gamepad.sThumbLX = 0; | ||
| 5683 | state.Gamepad.sThumbLY = 0; | ||
| 5684 | } | ||
| 5685 | |||
| 5686 | if ((state.Gamepad.sThumbRX < INPUT_DEADZONE && | ||
| 5687 | state.Gamepad.sThumbRX > -INPUT_DEADZONE) && | ||
| 5688 | (state.Gamepad.sThumbRY < INPUT_DEADZONE && | ||
| 5689 | state.Gamepad.sThumbRY > -INPUT_DEADZONE)) | ||
| 5690 | { | ||
| 5691 | state.Gamepad.sThumbRX = 0; | ||
| 5692 | state.Gamepad.sThumbRY = 0; | ||
| 5693 | } | ||
| 5694 | |||
| 5695 | e->axisesCount = 2; | ||
| 5696 | RGFW_point axis1 = RGFW_POINT(state.Gamepad.sThumbLX, state.Gamepad.sThumbLY); | ||
| 5697 | RGFW_point axis2 = RGFW_POINT(state.Gamepad.sThumbRX, state.Gamepad.sThumbRY); | ||
| 5698 | |||
| 5699 | if (axis1.x != e->axis[0].x || axis1.y != e->axis[0].y || axis2.x != e->axis[1].x || axis2.y != e->axis[1].y) { | ||
| 5700 | e->type = RGFW_jsAxisMove; | ||
| 5701 | |||
| 5702 | e->axis[0] = axis1; | ||
| 5703 | e->axis[1] = axis2; | ||
| 5704 | |||
| 5705 | return 1; | ||
| 5706 | } | ||
| 5707 | |||
| 5708 | e->axis[0] = axis1; | ||
| 5709 | e->axis[1] = axis2; | ||
| 5710 | } | ||
| 5711 | |||
| 5712 | return 0; | ||
| 5713 | } | ||
| 5714 | |||
| 5715 | void RGFW_stopCheckEvents(void) { | ||
| 5716 | PostMessageW(RGFW_root->src.window, WM_NULL, 0, 0); | ||
| 5717 | } | ||
| 5718 | |||
| 5719 | void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { | ||
| 5720 | RGFW_UNUSED(win); | ||
| 5721 | |||
| 5722 | MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (waitMS * 1e3), QS_ALLINPUT); | ||
| 5723 | } | ||
| 5724 | |||
| 5725 | RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { | ||
| 5726 | assert(win != NULL); | ||
| 5727 | |||
| 5728 | if (win->event.type == RGFW_quit) { | ||
| 5729 | return NULL; | ||
| 5730 | } | ||
| 5731 | |||
| 5732 | MSG msg; | ||
| 5733 | |||
| 5734 | if (RGFW_eventWindow.src.window == win->src.window) { | ||
| 5735 | if (RGFW_eventWindow.r.x != -1) { | ||
| 5736 | win->r.x = RGFW_eventWindow.r.x; | ||
| 5737 | win->r.y = RGFW_eventWindow.r.y; | ||
| 5738 | win->event.type = RGFW_windowMoved; | ||
| 5739 | RGFW_windowMoveCallback(win, win->r); | ||
| 5740 | } | ||
| 5741 | |||
| 5742 | if (RGFW_eventWindow.r.w != -1) { | ||
| 5743 | win->r.w = RGFW_eventWindow.r.w; | ||
| 5744 | win->r.h = RGFW_eventWindow.r.h; | ||
| 5745 | win->event.type = RGFW_windowResized; | ||
| 5746 | RGFW_windowResizeCallback(win, win->r); | ||
| 5747 | } | ||
| 5748 | |||
| 5749 | RGFW_eventWindow.src.window = NULL; | ||
| 5750 | RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1); | ||
| 5751 | |||
| 5752 | return &win->event; | ||
| 5753 | } | ||
| 5754 | |||
| 5755 | |||
| 5756 | static HDROP drop; | ||
| 5757 | |||
| 5758 | if (win->event.type == RGFW_dnd_init) { | ||
| 5759 | if (win->event.droppedFilesCount) { | ||
| 5760 | u32 i; | ||
| 5761 | for (i = 0; i < win->event.droppedFilesCount; i++) | ||
| 5762 | win->event.droppedFiles[i][0] = '\0'; | ||
| 5763 | } | ||
| 5764 | |||
| 5765 | win->event.droppedFilesCount = 0; | ||
| 5766 | win->event.droppedFilesCount = DragQueryFileW(drop, 0xffffffff, NULL, 0); | ||
| 5767 | //win->event.droppedFiles = (char**)RGFW_CALLOC(win->event.droppedFilesCount, sizeof(char*)); | ||
| 5768 | |||
| 5769 | u32 i; | ||
| 5770 | for (i = 0; i < win->event.droppedFilesCount; i++) { | ||
| 5771 | const UINT length = DragQueryFileW(drop, i, NULL, 0); | ||
| 5772 | WCHAR* buffer = (WCHAR*) RGFW_CALLOC((size_t) length + 1, sizeof(WCHAR)); | ||
| 5773 | |||
| 5774 | DragQueryFileW(drop, i, buffer, length + 1); | ||
| 5775 | strncpy(win->event.droppedFiles[i], createUTF8FromWideStringWin32(buffer), RGFW_MAX_PATH); | ||
| 5776 | win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0'; | ||
| 5777 | RGFW_FREE(buffer); | ||
| 5778 | } | ||
| 5779 | |||
| 5780 | DragFinish(drop); | ||
| 5781 | RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); | ||
| 5782 | |||
| 5783 | win->event.type = RGFW_dnd; | ||
| 5784 | return &win->event; | ||
| 5785 | } | ||
| 5786 | |||
| 5787 | win->event.inFocus = (GetForegroundWindow() == win->src.window); | ||
| 5788 | |||
| 5789 | if (RGFW_checkXInput(win, &win->event)) | ||
| 5790 | return &win->event; | ||
| 5791 | |||
| 5792 | static BYTE keyboardState[256]; | ||
| 5793 | |||
| 5794 | if (PeekMessageA(&msg, win->src.window, 0u, 0u, PM_REMOVE)) { | ||
| 5795 | switch (msg.message) { | ||
| 5796 | case WM_CLOSE: | ||
| 5797 | case WM_QUIT: | ||
| 5798 | RGFW_windowQuitCallback(win); | ||
| 5799 | win->event.type = RGFW_quit; | ||
| 5800 | break; | ||
| 5801 | |||
| 5802 | case WM_ACTIVATE: | ||
| 5803 | win->event.inFocus = (LOWORD(msg.wParam) == WA_INACTIVE); | ||
| 5804 | |||
| 5805 | if (win->event.inFocus) { | ||
| 5806 | win->event.type = RGFW_focusIn; | ||
| 5807 | RGFW_focusCallback(win, 1); | ||
| 5808 | } | ||
| 5809 | else { | ||
| 5810 | win->event.type = RGFW_focusOut; | ||
| 5811 | RGFW_focusCallback(win, 0); | ||
| 5812 | } | ||
| 5813 | |||
| 5814 | break; | ||
| 5815 | |||
| 5816 | case WM_PAINT: | ||
| 5817 | win->event.type = RGFW_windowRefresh; | ||
| 5818 | RGFW_windowRefreshCallback(win); | ||
| 5819 | break; | ||
| 5820 | |||
| 5821 | case WM_MOUSELEAVE: | ||
| 5822 | win->event.type = RGFW_mouseLeave; | ||
| 5823 | win->_winArgs |= RGFW_MOUSE_LEFT; | ||
| 5824 | RGFW_mouseNotifyCallBack(win, win->event.point, 0); | ||
| 5825 | break; | ||
| 5826 | |||
| 5827 | case WM_KEYUP: { | ||
| 5828 | win->event.keyCode = RGFW_apiKeyCodeToRGFW((u32) msg.wParam); | ||
| 5829 | |||
| 5830 | RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode); | ||
| 5831 | |||
| 5832 | static char keyName[16]; | ||
| 5833 | |||
| 5834 | { | ||
| 5835 | GetKeyNameTextA((LONG) msg.lParam, keyName, 16); | ||
| 5836 | |||
| 5837 | if ((!(GetKeyState(VK_CAPITAL) & 0x0001) && !(GetKeyState(VK_SHIFT) & 0x8000)) || | ||
| 5838 | ((GetKeyState(VK_CAPITAL) & 0x0001) && (GetKeyState(VK_SHIFT) & 0x8000))) { | ||
| 5839 | CharLowerBuffA(keyName, 16); | ||
| 5840 | } | ||
| 5841 | } | ||
| 5842 | |||
| 5843 | RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001)); | ||
| 5844 | |||
| 5845 | strncpy(win->event.keyName, keyName, 16); | ||
| 5846 | |||
| 5847 | if (RGFW_isPressed(win, RGFW_ShiftL)) { | ||
| 5848 | ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR), | ||
| 5849 | keyboardState, (LPWORD) win->event.keyName, 0); | ||
| 5850 | } | ||
| 5851 | |||
| 5852 | win->event.type = RGFW_keyReleased; | ||
| 5853 | RGFW_keyboard[win->event.keyCode].current = 0; | ||
| 5854 | RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 0); | ||
| 5855 | break; | ||
| 5856 | } | ||
| 5857 | case WM_KEYDOWN: { | ||
| 5858 | win->event.keyCode = RGFW_apiKeyCodeToRGFW((u32) msg.wParam); | ||
| 5859 | |||
| 5860 | RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode); | ||
| 5861 | |||
| 5862 | static char keyName[16]; | ||
| 5863 | |||
| 5864 | { | ||
| 5865 | GetKeyNameTextA((LONG) msg.lParam, keyName, 16); | ||
| 5866 | |||
| 5867 | if ((!(GetKeyState(VK_CAPITAL) & 0x0001) && !(GetKeyState(VK_SHIFT) & 0x8000)) || | ||
| 5868 | ((GetKeyState(VK_CAPITAL) & 0x0001) && (GetKeyState(VK_SHIFT) & 0x8000))) { | ||
| 5869 | CharLowerBuffA(keyName, 16); | ||
| 5870 | } | ||
| 5871 | } | ||
| 5872 | |||
| 5873 | RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001)); | ||
| 5874 | |||
| 5875 | strncpy(win->event.keyName, keyName, 16); | ||
| 5876 | |||
| 5877 | if (RGFW_isPressed(win, RGFW_ShiftL) & 0x8000) { | ||
| 5878 | ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR), | ||
| 5879 | keyboardState, (LPWORD) win->event.keyName, 0); | ||
| 5880 | } | ||
| 5881 | |||
| 5882 | win->event.type = RGFW_keyPressed; | ||
| 5883 | win->event.repeat = RGFW_isPressed(win, win->event.keyCode); | ||
| 5884 | RGFW_keyboard[win->event.keyCode].current = 1; | ||
| 5885 | RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1); | ||
| 5886 | break; | ||
| 5887 | } | ||
| 5888 | |||
| 5889 | case WM_MOUSEMOVE: | ||
| 5890 | if ((win->_winArgs & RGFW_HOLD_MOUSE)) | ||
| 5891 | break; | ||
| 5892 | |||
| 5893 | win->event.type = RGFW_mousePosChanged; | ||
| 5894 | |||
| 5895 | win->event.point.x = GET_X_LPARAM(msg.lParam); | ||
| 5896 | win->event.point.y = GET_Y_LPARAM(msg.lParam); | ||
| 5897 | |||
| 5898 | RGFW_mousePosCallback(win, win->event.point); | ||
| 5899 | |||
| 5900 | if (win->_winArgs & RGFW_MOUSE_LEFT) { | ||
| 5901 | win->_winArgs ^= RGFW_MOUSE_LEFT; | ||
| 5902 | win->event.type = RGFW_mouseEnter; | ||
| 5903 | RGFW_mouseNotifyCallBack(win, win->event.point, 1); | ||
| 5904 | } | ||
| 5905 | |||
| 5906 | break; | ||
| 5907 | |||
| 5908 | case WM_INPUT: { | ||
| 5909 | if (!(win->_winArgs & RGFW_HOLD_MOUSE)) | ||
| 5910 | break; | ||
| 5911 | |||
| 5912 | unsigned size = sizeof(RAWINPUT); | ||
| 5913 | static RAWINPUT raw[sizeof(RAWINPUT)]; | ||
| 5914 | GetRawInputData((HRAWINPUT)msg.lParam, RID_INPUT, raw, &size, sizeof(RAWINPUTHEADER)); | ||
| 5915 | |||
| 5916 | if (raw->header.dwType != RIM_TYPEMOUSE || (raw->data.mouse.lLastX == 0 && raw->data.mouse.lLastY == 0) ) | ||
| 5917 | break; | ||
| 5918 | |||
| 5919 | win->event.type = RGFW_mousePosChanged; | ||
| 5920 | win->event.point.x = raw->data.mouse.lLastX; | ||
| 5921 | win->event.point.y = raw->data.mouse.lLastY; | ||
| 5922 | break; | ||
| 5923 | } | ||
| 5924 | |||
| 5925 | case WM_LBUTTONDOWN: | ||
| 5926 | win->event.button = RGFW_mouseLeft; | ||
| 5927 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 5928 | RGFW_mouseButtons[win->event.button].current = 1; | ||
| 5929 | win->event.type = RGFW_mouseButtonPressed; | ||
| 5930 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); | ||
| 5931 | break; | ||
| 5932 | case WM_RBUTTONDOWN: | ||
| 5933 | win->event.button = RGFW_mouseRight; | ||
| 5934 | win->event.type = RGFW_mouseButtonPressed; | ||
| 5935 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 5936 | RGFW_mouseButtons[win->event.button].current = 1; | ||
| 5937 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); | ||
| 5938 | break; | ||
| 5939 | case WM_MBUTTONDOWN: | ||
| 5940 | win->event.button = RGFW_mouseMiddle; | ||
| 5941 | win->event.type = RGFW_mouseButtonPressed; | ||
| 5942 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 5943 | RGFW_mouseButtons[win->event.button].current = 1; | ||
| 5944 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); | ||
| 5945 | break; | ||
| 5946 | |||
| 5947 | case WM_MOUSEWHEEL: | ||
| 5948 | if (msg.wParam > 0) | ||
| 5949 | win->event.button = RGFW_mouseScrollUp; | ||
| 5950 | else | ||
| 5951 | win->event.button = RGFW_mouseScrollDown; | ||
| 5952 | |||
| 5953 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 5954 | RGFW_mouseButtons[win->event.button].current = 1; | ||
| 5955 | |||
| 5956 | win->event.scroll = (SHORT) HIWORD(msg.wParam) / (double) WHEEL_DELTA; | ||
| 5957 | |||
| 5958 | win->event.type = RGFW_mouseButtonPressed; | ||
| 5959 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); | ||
| 5960 | break; | ||
| 5961 | |||
| 5962 | case WM_LBUTTONUP: | ||
| 5963 | |||
| 5964 | win->event.button = RGFW_mouseLeft; | ||
| 5965 | win->event.type = RGFW_mouseButtonReleased; | ||
| 5966 | |||
| 5967 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 5968 | RGFW_mouseButtons[win->event.button].current = 0; | ||
| 5969 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); | ||
| 5970 | break; | ||
| 5971 | case WM_RBUTTONUP: | ||
| 5972 | win->event.button = RGFW_mouseRight; | ||
| 5973 | win->event.type = RGFW_mouseButtonReleased; | ||
| 5974 | |||
| 5975 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 5976 | RGFW_mouseButtons[win->event.button].current = 0; | ||
| 5977 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); | ||
| 5978 | break; | ||
| 5979 | case WM_MBUTTONUP: | ||
| 5980 | win->event.button = RGFW_mouseMiddle; | ||
| 5981 | win->event.type = RGFW_mouseButtonReleased; | ||
| 5982 | |||
| 5983 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 5984 | RGFW_mouseButtons[win->event.button].current = 0; | ||
| 5985 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); | ||
| 5986 | break; | ||
| 5987 | |||
| 5988 | /* | ||
| 5989 | much of this event is source from glfw | ||
| 5990 | */ | ||
| 5991 | case WM_DROPFILES: { | ||
| 5992 | win->event.type = RGFW_dnd_init; | ||
| 5993 | |||
| 5994 | drop = (HDROP) msg.wParam; | ||
| 5995 | POINT pt; | ||
| 5996 | |||
| 5997 | /* Move the mouse to the position of the drop */ | ||
| 5998 | DragQueryPoint(drop, &pt); | ||
| 5999 | |||
| 6000 | win->event.point.x = pt.x; | ||
| 6001 | win->event.point.y = pt.y; | ||
| 6002 | |||
| 6003 | RGFW_dndInitCallback(win, win->event.point); | ||
| 6004 | } | ||
| 6005 | break; | ||
| 6006 | case WM_GETMINMAXINFO: | ||
| 6007 | { | ||
| 6008 | if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0) | ||
| 6009 | break; | ||
| 6010 | |||
| 6011 | MINMAXINFO* mmi = (MINMAXINFO*) msg.lParam; | ||
| 6012 | mmi->ptMinTrackSize.x = win->src.minSize.w; | ||
| 6013 | mmi->ptMinTrackSize.y = win->src.minSize.h; | ||
| 6014 | mmi->ptMaxTrackSize.x = win->src.maxSize.w; | ||
| 6015 | mmi->ptMaxTrackSize.y = win->src.maxSize.h; | ||
| 6016 | return 0; | ||
| 6017 | } | ||
| 6018 | default: | ||
| 6019 | win->event.type = 0; | ||
| 6020 | break; | ||
| 6021 | } | ||
| 6022 | |||
| 6023 | TranslateMessage(&msg); | ||
| 6024 | DispatchMessageA(&msg); | ||
| 6025 | } | ||
| 6026 | |||
| 6027 | else | ||
| 6028 | win->event.type = 0; | ||
| 6029 | |||
| 6030 | if (!IsWindow(win->src.window)) { | ||
| 6031 | win->event.type = RGFW_quit; | ||
| 6032 | RGFW_windowQuitCallback(win); | ||
| 6033 | } | ||
| 6034 | |||
| 6035 | if (win->event.type) | ||
| 6036 | return &win->event; | ||
| 6037 | else | ||
| 6038 | return NULL; | ||
| 6039 | } | ||
| 6040 | |||
| 6041 | u8 RGFW_window_isFullscreen(RGFW_window* win) { | ||
| 6042 | assert(win != NULL); | ||
| 6043 | |||
| 6044 | #ifndef __cplusplus | ||
| 6045 | WINDOWPLACEMENT placement = { 0 }; | ||
| 6046 | #else | ||
| 6047 | WINDOWPLACEMENT placement = { }; | ||
| 6048 | #endif | ||
| 6049 | GetWindowPlacement(win->src.window, &placement); | ||
| 6050 | return placement.showCmd == SW_SHOWMAXIMIZED; | ||
| 6051 | } | ||
| 6052 | |||
| 6053 | u8 RGFW_window_isHidden(RGFW_window* win) { | ||
| 6054 | assert(win != NULL); | ||
| 6055 | |||
| 6056 | return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win); | ||
| 6057 | } | ||
| 6058 | |||
| 6059 | u8 RGFW_window_isMinimized(RGFW_window* win) { | ||
| 6060 | assert(win != NULL); | ||
| 6061 | |||
| 6062 | #ifndef __cplusplus | ||
| 6063 | WINDOWPLACEMENT placement = { 0 }; | ||
| 6064 | #else | ||
| 6065 | WINDOWPLACEMENT placement = { }; | ||
| 6066 | #endif | ||
| 6067 | GetWindowPlacement(win->src.window, &placement); | ||
| 6068 | return placement.showCmd == SW_SHOWMINIMIZED; | ||
| 6069 | } | ||
| 6070 | |||
| 6071 | u8 RGFW_window_isMaximized(RGFW_window* win) { | ||
| 6072 | assert(win != NULL); | ||
| 6073 | |||
| 6074 | #ifndef __cplusplus | ||
| 6075 | WINDOWPLACEMENT placement = { 0 }; | ||
| 6076 | #else | ||
| 6077 | WINDOWPLACEMENT placement = { }; | ||
| 6078 | #endif | ||
| 6079 | GetWindowPlacement(win->src.window, &placement); | ||
| 6080 | return placement.showCmd == SW_SHOWMAXIMIZED; | ||
| 6081 | } | ||
| 6082 | |||
| 6083 | typedef struct { int iIndex; HMONITOR hMonitor; } RGFW_mInfo; | ||
| 6084 | BOOL CALLBACK GetMonitorByHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { | ||
| 6085 | RGFW_UNUSED(hdcMonitor) | ||
| 6086 | RGFW_UNUSED(lprcMonitor) | ||
| 6087 | |||
| 6088 | RGFW_mInfo* info = (RGFW_mInfo*) dwData; | ||
| 6089 | if (info->hMonitor == hMonitor) | ||
| 6090 | return FALSE; | ||
| 6091 | |||
| 6092 | info->iIndex++; | ||
| 6093 | return TRUE; | ||
| 6094 | } | ||
| 6095 | |||
| 6096 | #ifndef RGFW_NO_MONITOR | ||
| 6097 | RGFW_monitor win32CreateMonitor(HMONITOR src) { | ||
| 6098 | RGFW_monitor monitor; | ||
| 6099 | MONITORINFO monitorInfo; | ||
| 6100 | |||
| 6101 | monitorInfo.cbSize = sizeof(MONITORINFO); | ||
| 6102 | GetMonitorInfoA(src, &monitorInfo); | ||
| 6103 | |||
| 6104 | RGFW_mInfo info; | ||
| 6105 | info.iIndex = 0; | ||
| 6106 | info.hMonitor = src; | ||
| 6107 | |||
| 6108 | /* get the monitor's index */ | ||
| 6109 | if (EnumDisplayMonitors(NULL, NULL, GetMonitorByHandle, (LPARAM) &info)) { | ||
| 6110 | DISPLAY_DEVICEA dd; | ||
| 6111 | dd.cb = sizeof(dd); | ||
| 6112 | |||
| 6113 | /* loop through the devices until you find a device with the monitor's index */ | ||
| 6114 | size_t deviceIndex; | ||
| 6115 | for (deviceIndex = 0; EnumDisplayDevicesA(0, (DWORD) deviceIndex, &dd, 0); deviceIndex++) { | ||
| 6116 | char* deviceName = dd.DeviceName; | ||
| 6117 | if (EnumDisplayDevicesA(deviceName, info.iIndex, &dd, 0)) { | ||
| 6118 | strncpy(monitor.name, dd.DeviceString, 128); /*!< copy the monitor's name */ | ||
| 6119 | break; | ||
| 6120 | } | ||
| 6121 | } | ||
| 6122 | } | ||
| 6123 | |||
| 6124 | monitor.rect.x = monitorInfo.rcWork.left; | ||
| 6125 | monitor.rect.y = monitorInfo.rcWork.top; | ||
| 6126 | monitor.rect.w = monitorInfo.rcWork.right - monitorInfo.rcWork.left; | ||
| 6127 | monitor.rect.h = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; | ||
| 6128 | |||
| 6129 | #ifndef RGFW_NO_DPI | ||
| 6130 | #ifndef USER_DEFAULT_SCREEN_DPI | ||
| 6131 | #define USER_DEFAULT_SCREEN_DPI 96 | ||
| 6132 | #endif | ||
| 6133 | |||
| 6134 | if (GetDpiForMonitor != NULL) { | ||
| 6135 | u32 x, y; | ||
| 6136 | GetDpiForMonitor(src, MDT_EFFECTIVE_DPI, &x, &y); | ||
| 6137 | |||
| 6138 | monitor.scaleX = (float) (x) / (float) USER_DEFAULT_SCREEN_DPI; | ||
| 6139 | monitor.scaleY = (float) (y) / (float) USER_DEFAULT_SCREEN_DPI; | ||
| 6140 | } | ||
| 6141 | #endif | ||
| 6142 | |||
| 6143 | HDC hdc = GetDC(NULL); | ||
| 6144 | /* get pixels per inch */ | ||
| 6145 | i32 ppiX = GetDeviceCaps(hdc, LOGPIXELSX); | ||
| 6146 | i32 ppiY = GetDeviceCaps(hdc, LOGPIXELSY); | ||
| 6147 | ReleaseDC(NULL, hdc); | ||
| 6148 | |||
| 6149 | /* Calculate physical height in inches */ | ||
| 6150 | monitor.physW = GetSystemMetrics(SM_CYSCREEN) / (float) ppiX; | ||
| 6151 | monitor.physH = GetSystemMetrics(SM_CXSCREEN) / (float) ppiY; | ||
| 6152 | |||
| 6153 | return monitor; | ||
| 6154 | } | ||
| 6155 | #endif /* RGFW_NO_MONITOR */ | ||
| 6156 | |||
| 6157 | |||
| 6158 | #ifndef RGFW_NO_MONITOR | ||
| 6159 | RGFW_monitor RGFW_monitors[6]; | ||
| 6160 | BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { | ||
| 6161 | RGFW_UNUSED(hdcMonitor) | ||
| 6162 | RGFW_UNUSED(lprcMonitor) | ||
| 6163 | |||
| 6164 | RGFW_mInfo* info = (RGFW_mInfo*) dwData; | ||
| 6165 | |||
| 6166 | if (info->iIndex >= 6) | ||
| 6167 | return FALSE; | ||
| 6168 | |||
| 6169 | RGFW_monitors[info->iIndex] = win32CreateMonitor(hMonitor); | ||
| 6170 | info->iIndex++; | ||
| 6171 | |||
| 6172 | return TRUE; | ||
| 6173 | } | ||
| 6174 | |||
| 6175 | RGFW_monitor RGFW_getPrimaryMonitor(void) { | ||
| 6176 | #ifdef __cplusplus | ||
| 6177 | return win32CreateMonitor(MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); | ||
| 6178 | #else | ||
| 6179 | return win32CreateMonitor(MonitorFromPoint((POINT) { 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); | ||
| 6180 | #endif | ||
| 6181 | } | ||
| 6182 | |||
| 6183 | RGFW_monitor* RGFW_getMonitors(void) { | ||
| 6184 | RGFW_mInfo info; | ||
| 6185 | info.iIndex = 0; | ||
| 6186 | while (EnumDisplayMonitors(NULL, NULL, GetMonitorHandle, (LPARAM) &info)); | ||
| 6187 | |||
| 6188 | return RGFW_monitors; | ||
| 6189 | } | ||
| 6190 | |||
| 6191 | RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { | ||
| 6192 | HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY); | ||
| 6193 | return win32CreateMonitor(src); | ||
| 6194 | } | ||
| 6195 | #endif | ||
| 6196 | |||
| 6197 | HICON RGFW_loadHandleImage(RGFW_window* win, u8* src, RGFW_area a, BOOL icon) { | ||
| 6198 | assert(win != NULL); | ||
| 6199 | |||
| 6200 | u32 i; | ||
| 6201 | HDC dc; | ||
| 6202 | HICON handle; | ||
| 6203 | HBITMAP color, mask; | ||
| 6204 | BITMAPV5HEADER bi; | ||
| 6205 | ICONINFO ii; | ||
| 6206 | u8* target = NULL; | ||
| 6207 | u8* source = src; | ||
| 6208 | |||
| 6209 | ZeroMemory(&bi, sizeof(bi)); | ||
| 6210 | bi.bV5Size = sizeof(bi); | ||
| 6211 | bi.bV5Width = a.w; | ||
| 6212 | bi.bV5Height = -((LONG) a.h); | ||
| 6213 | bi.bV5Planes = 1; | ||
| 6214 | bi.bV5BitCount = 32; | ||
| 6215 | bi.bV5Compression = BI_BITFIELDS; | ||
| 6216 | bi.bV5RedMask = 0x00ff0000; | ||
| 6217 | bi.bV5GreenMask = 0x0000ff00; | ||
| 6218 | bi.bV5BlueMask = 0x000000ff; | ||
| 6219 | bi.bV5AlphaMask = 0xff000000; | ||
| 6220 | |||
| 6221 | dc = GetDC(NULL); | ||
| 6222 | color = CreateDIBSection(dc, | ||
| 6223 | (BITMAPINFO*) &bi, | ||
| 6224 | DIB_RGB_COLORS, | ||
| 6225 | (void**) &target, | ||
| 6226 | NULL, | ||
| 6227 | (DWORD) 0); | ||
| 6228 | ReleaseDC(NULL, dc); | ||
| 6229 | |||
| 6230 | mask = CreateBitmap(a.w, a.h, 1, 1, NULL); | ||
| 6231 | |||
| 6232 | for (i = 0; i < a.w * a.h; i++) { | ||
| 6233 | target[0] = source[2]; | ||
| 6234 | target[1] = source[1]; | ||
| 6235 | target[2] = source[0]; | ||
| 6236 | target[3] = source[3]; | ||
| 6237 | target += 4; | ||
| 6238 | source += 4; | ||
| 6239 | } | ||
| 6240 | |||
| 6241 | ZeroMemory(&ii, sizeof(ii)); | ||
| 6242 | ii.fIcon = icon; | ||
| 6243 | ii.xHotspot = 0; | ||
| 6244 | ii.yHotspot = 0; | ||
| 6245 | ii.hbmMask = mask; | ||
| 6246 | ii.hbmColor = color; | ||
| 6247 | |||
| 6248 | handle = CreateIconIndirect(&ii); | ||
| 6249 | |||
| 6250 | DeleteObject(color); | ||
| 6251 | DeleteObject(mask); | ||
| 6252 | |||
| 6253 | return handle; | ||
| 6254 | } | ||
| 6255 | |||
| 6256 | void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { | ||
| 6257 | assert(win != NULL); | ||
| 6258 | RGFW_UNUSED(channels) | ||
| 6259 | |||
| 6260 | HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(win, image, a, FALSE); | ||
| 6261 | SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) cursor); | ||
| 6262 | SetCursor(cursor); | ||
| 6263 | DestroyCursor(cursor); | ||
| 6264 | } | ||
| 6265 | |||
| 6266 | void RGFW_window_setMouseDefault(RGFW_window* win) { | ||
| 6267 | RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW); | ||
| 6268 | } | ||
| 6269 | |||
| 6270 | void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { | ||
| 6271 | assert(win != NULL); | ||
| 6272 | |||
| 6273 | if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u32))) | ||
| 6274 | return; | ||
| 6275 | |||
| 6276 | char* icon = MAKEINTRESOURCEA(RGFW_mouseIconSrc[mouse]); | ||
| 6277 | |||
| 6278 | SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon)); | ||
| 6279 | SetCursor(LoadCursorA(NULL, icon)); | ||
| 6280 | } | ||
| 6281 | |||
| 6282 | void RGFW_window_hide(RGFW_window* win) { | ||
| 6283 | ShowWindow(win->src.window, SW_HIDE); | ||
| 6284 | } | ||
| 6285 | |||
| 6286 | void RGFW_window_show(RGFW_window* win) { | ||
| 6287 | ShowWindow(win->src.window, SW_RESTORE); | ||
| 6288 | } | ||
| 6289 | |||
| 6290 | void RGFW_window_close(RGFW_window* win) { | ||
| 6291 | assert(win != NULL); | ||
| 6292 | |||
| 6293 | #ifdef RGFW_EGL | ||
| 6294 | RGFW_closeEGL(win); | ||
| 6295 | #endif | ||
| 6296 | |||
| 6297 | if (win == RGFW_root) { | ||
| 6298 | #ifdef RGFW_DIRECTX | ||
| 6299 | RGFW_dxInfo.pDeviceContext->lpVtbl->Release(RGFW_dxInfo.pDeviceContext); | ||
| 6300 | RGFW_dxInfo.pDevice->lpVtbl->Release(RGFW_dxInfo.pDevice); | ||
| 6301 | RGFW_dxInfo.pAdapter->lpVtbl->Release(RGFW_dxInfo.pAdapter); | ||
| 6302 | RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory); | ||
| 6303 | #endif | ||
| 6304 | |||
| 6305 | if (RGFW_XInput_dll != NULL) { | ||
| 6306 | FreeLibrary(RGFW_XInput_dll); | ||
| 6307 | RGFW_XInput_dll = NULL; | ||
| 6308 | } | ||
| 6309 | |||
| 6310 | #ifndef RGFW_NO_DPI | ||
| 6311 | if (RGFW_Shcore_dll != NULL) { | ||
| 6312 | FreeLibrary(RGFW_Shcore_dll); | ||
| 6313 | RGFW_Shcore_dll = NULL; | ||
| 6314 | } | ||
| 6315 | #endif | ||
| 6316 | |||
| 6317 | if (wglinstance != NULL) { | ||
| 6318 | FreeLibrary(wglinstance); | ||
| 6319 | wglinstance = NULL; | ||
| 6320 | } | ||
| 6321 | |||
| 6322 | RGFW_root = NULL; | ||
| 6323 | } | ||
| 6324 | |||
| 6325 | #ifdef RGFW_DIRECTX | ||
| 6326 | win->src.swapchain->lpVtbl->Release(win->src.swapchain); | ||
| 6327 | win->src.renderTargetView->lpVtbl->Release(win->src.renderTargetView); | ||
| 6328 | win->src.pDepthStencilView->lpVtbl->Release(win->src.pDepthStencilView); | ||
| 6329 | #endif | ||
| 6330 | |||
| 6331 | #ifdef RGFW_BUFFER | ||
| 6332 | DeleteDC(win->src.hdcMem); | ||
| 6333 | DeleteObject(win->src.bitmap); | ||
| 6334 | #endif | ||
| 6335 | |||
| 6336 | #ifdef RGFW_OPENGL | ||
| 6337 | wglDeleteContext((HGLRC) win->src.ctx); /*!< delete opengl context */ | ||
| 6338 | #endif | ||
| 6339 | DeleteDC(win->src.hdc); /*!< delete device context */ | ||
| 6340 | DestroyWindow(win->src.window); /*!< delete window */ | ||
| 6341 | |||
| 6342 | #if defined(RGFW_OSMESA) | ||
| 6343 | if (win->buffer != NULL) | ||
| 6344 | RGFW_FREE(win->buffer); | ||
| 6345 | #endif | ||
| 6346 | |||
| 6347 | #ifdef RGFW_ALLOC_DROPFILES | ||
| 6348 | { | ||
| 6349 | u32 i; | ||
| 6350 | for (i = 0; i < RGFW_MAX_DROPS; i++) | ||
| 6351 | RGFW_FREE(win->event.droppedFiles[i]); | ||
| 6352 | |||
| 6353 | |||
| 6354 | RGFW_FREE(win->event.droppedFiles); | ||
| 6355 | } | ||
| 6356 | #endif | ||
| 6357 | |||
| 6358 | RGFW_FREE(win); | ||
| 6359 | } | ||
| 6360 | |||
| 6361 | void RGFW_window_move(RGFW_window* win, RGFW_point v) { | ||
| 6362 | assert(win != NULL); | ||
| 6363 | |||
| 6364 | win->r.x = v.x; | ||
| 6365 | win->r.y = v.y; | ||
| 6366 | SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, 0, 0, SWP_NOSIZE); | ||
| 6367 | } | ||
| 6368 | |||
| 6369 | void RGFW_window_resize(RGFW_window* win, RGFW_area a) { | ||
| 6370 | assert(win != NULL); | ||
| 6371 | |||
| 6372 | win->r.w = a.w; | ||
| 6373 | win->r.h = a.h; | ||
| 6374 | SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->r.w, win->r.h + win->src.hOffset, SWP_NOMOVE); | ||
| 6375 | } | ||
| 6376 | |||
| 6377 | |||
| 6378 | void RGFW_window_setName(RGFW_window* win, char* name) { | ||
| 6379 | assert(win != NULL); | ||
| 6380 | |||
| 6381 | SetWindowTextA(win->src.window, name); | ||
| 6382 | } | ||
| 6383 | |||
| 6384 | /* sourced from GLFW */ | ||
| 6385 | #ifndef RGFW_NO_PASSTHROUGH | ||
| 6386 | void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { | ||
| 6387 | assert(win != NULL); | ||
| 6388 | |||
| 6389 | COLORREF key = 0; | ||
| 6390 | BYTE alpha = 0; | ||
| 6391 | DWORD flags = 0; | ||
| 6392 | DWORD exStyle = GetWindowLongW(win->src.window, GWL_EXSTYLE); | ||
| 6393 | |||
| 6394 | if (exStyle & WS_EX_LAYERED) | ||
| 6395 | GetLayeredWindowAttributes(win->src.window, &key, &alpha, &flags); | ||
| 6396 | |||
| 6397 | if (passthrough) | ||
| 6398 | exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED); | ||
| 6399 | else | ||
| 6400 | { | ||
| 6401 | exStyle &= ~WS_EX_TRANSPARENT; | ||
| 6402 | // NOTE: Window opacity also needs the layered window style so do not | ||
| 6403 | // remove it if the window is alpha blended | ||
| 6404 | if (exStyle & WS_EX_LAYERED) | ||
| 6405 | { | ||
| 6406 | if (!(flags & LWA_ALPHA)) | ||
| 6407 | exStyle &= ~WS_EX_LAYERED; | ||
| 6408 | } | ||
| 6409 | } | ||
| 6410 | |||
| 6411 | SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle); | ||
| 6412 | |||
| 6413 | if (passthrough) { | ||
| 6414 | SetLayeredWindowAttributes(win->src.window, key, alpha, flags); | ||
| 6415 | } | ||
| 6416 | } | ||
| 6417 | #endif | ||
| 6418 | |||
| 6419 | /* much of this function is sourced from GLFW */ | ||
| 6420 | void RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) { | ||
| 6421 | assert(win != NULL); | ||
| 6422 | #ifndef RGFW_WIN95 | ||
| 6423 | RGFW_UNUSED(channels) | ||
| 6424 | |||
| 6425 | HICON handle = RGFW_loadHandleImage(win, src, a, TRUE); | ||
| 6426 | |||
| 6427 | SetClassLongPtrA(win->src.window, GCLP_HICON, (LPARAM) handle); | ||
| 6428 | |||
| 6429 | DestroyIcon(handle); | ||
| 6430 | #else | ||
| 6431 | RGFW_UNUSED(src) | ||
| 6432 | RGFW_UNUSED(a) | ||
| 6433 | RGFW_UNUSED(channels) | ||
| 6434 | #endif | ||
| 6435 | } | ||
| 6436 | |||
| 6437 | char* RGFW_readClipboard(size_t* size) { | ||
| 6438 | /* Open the clipboard */ | ||
| 6439 | if (OpenClipboard(NULL) == 0) | ||
| 6440 | return (char*) ""; | ||
| 6441 | |||
| 6442 | /* Get the clipboard data as a Unicode string */ | ||
| 6443 | HANDLE hData = GetClipboardData(CF_UNICODETEXT); | ||
| 6444 | if (hData == NULL) { | ||
| 6445 | CloseClipboard(); | ||
| 6446 | return (char*) ""; | ||
| 6447 | } | ||
| 6448 | |||
| 6449 | wchar_t* wstr = (wchar_t*) GlobalLock(hData); | ||
| 6450 | |||
| 6451 | char* text; | ||
| 6452 | |||
| 6453 | { | ||
| 6454 | setlocale(LC_ALL, "en_US.UTF-8"); | ||
| 6455 | |||
| 6456 | size_t textLen = wcstombs(NULL, wstr, 0); | ||
| 6457 | if (textLen == 0) | ||
| 6458 | return (char*) ""; | ||
| 6459 | |||
| 6460 | text = (char*) RGFW_MALLOC((textLen * sizeof(char)) + 1); | ||
| 6461 | |||
| 6462 | wcstombs(text, wstr, (textLen) +1); | ||
| 6463 | |||
| 6464 | if (size != NULL) | ||
| 6465 | *size = textLen + 1; | ||
| 6466 | |||
| 6467 | text[textLen] = '\0'; | ||
| 6468 | } | ||
| 6469 | |||
| 6470 | /* Release the clipboard data */ | ||
| 6471 | GlobalUnlock(hData); | ||
| 6472 | CloseClipboard(); | ||
| 6473 | |||
| 6474 | return text; | ||
| 6475 | } | ||
| 6476 | |||
| 6477 | void RGFW_writeClipboard(const char* text, u32 textLen) { | ||
| 6478 | HANDLE object; | ||
| 6479 | WCHAR* buffer; | ||
| 6480 | |||
| 6481 | object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR)); | ||
| 6482 | if (!object) | ||
| 6483 | return; | ||
| 6484 | |||
| 6485 | buffer = (WCHAR*) GlobalLock(object); | ||
| 6486 | if (!buffer) { | ||
| 6487 | GlobalFree(object); | ||
| 6488 | return; | ||
| 6489 | } | ||
| 6490 | |||
| 6491 | MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, textLen); | ||
| 6492 | GlobalUnlock(object); | ||
| 6493 | |||
| 6494 | if (!OpenClipboard(RGFW_root->src.window)) { | ||
| 6495 | GlobalFree(object); | ||
| 6496 | return; | ||
| 6497 | } | ||
| 6498 | |||
| 6499 | EmptyClipboard(); | ||
| 6500 | SetClipboardData(CF_UNICODETEXT, object); | ||
| 6501 | CloseClipboard(); | ||
| 6502 | } | ||
| 6503 | |||
| 6504 | u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) { | ||
| 6505 | assert(win != NULL); | ||
| 6506 | |||
| 6507 | RGFW_UNUSED(jsNumber) | ||
| 6508 | |||
| 6509 | return RGFW_registerJoystickF(win, (char*) ""); | ||
| 6510 | } | ||
| 6511 | |||
| 6512 | u16 RGFW_registerJoystickF(RGFW_window* win, char* file) { | ||
| 6513 | assert(win != NULL); | ||
| 6514 | RGFW_UNUSED(file) | ||
| 6515 | |||
| 6516 | return RGFW_joystickCount - 1; | ||
| 6517 | } | ||
| 6518 | |||
| 6519 | void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) { | ||
| 6520 | assert(win != NULL); | ||
| 6521 | |||
| 6522 | SetCursorPos(p.x, p.y); | ||
| 6523 | } | ||
| 6524 | |||
| 6525 | #ifdef RGFW_OPENGL | ||
| 6526 | void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { | ||
| 6527 | if (win == NULL) | ||
| 6528 | wglMakeCurrent(NULL, NULL); | ||
| 6529 | else | ||
| 6530 | wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx); | ||
| 6531 | } | ||
| 6532 | #endif | ||
| 6533 | |||
| 6534 | #ifndef RGFW_EGL | ||
| 6535 | void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { | ||
| 6536 | assert(win != NULL); | ||
| 6537 | |||
| 6538 | #if defined(RGFW_OPENGL) | ||
| 6539 | typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval); | ||
| 6540 | static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; | ||
| 6541 | static void* loadSwapFunc = (void*) 1; | ||
| 6542 | |||
| 6543 | if (loadSwapFunc == NULL) { | ||
| 6544 | fprintf(stderr, "wglSwapIntervalEXT not supported\n"); | ||
| 6545 | return; | ||
| 6546 | } | ||
| 6547 | |||
| 6548 | if (wglSwapIntervalEXT == NULL) { | ||
| 6549 | loadSwapFunc = (void*) wglGetProcAddress("wglSwapIntervalEXT"); | ||
| 6550 | wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) loadSwapFunc; | ||
| 6551 | } | ||
| 6552 | |||
| 6553 | if (wglSwapIntervalEXT(swapInterval) == FALSE) | ||
| 6554 | fprintf(stderr, "Failed to set swap interval\n"); | ||
| 6555 | #else | ||
| 6556 | RGFW_UNUSED(swapInterval); | ||
| 6557 | #endif | ||
| 6558 | |||
| 6559 | } | ||
| 6560 | #endif | ||
| 6561 | |||
| 6562 | void RGFW_window_swapBuffers(RGFW_window* win) { | ||
| 6563 | //assert(win != NULL); | ||
| 6564 | /* clear the window*/ | ||
| 6565 | |||
| 6566 | if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) { | ||
| 6567 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 6568 | #ifdef RGFW_OSMESA | ||
| 6569 | RGFW_OSMesa_reorganize(); | ||
| 6570 | #endif | ||
| 6571 | |||
| 6572 | HGDIOBJ oldbmp = SelectObject(win->src.hdcMem, win->src.bitmap); | ||
| 6573 | BitBlt(win->src.hdc, 0, 0, win->r.w, win->r.h, win->src.hdcMem, 0, 0, SRCCOPY); | ||
| 6574 | SelectObject(win->src.hdcMem, oldbmp); | ||
| 6575 | #endif | ||
| 6576 | } | ||
| 6577 | |||
| 6578 | if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) { | ||
| 6579 | #ifdef RGFW_EGL | ||
| 6580 | eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); | ||
| 6581 | #elif defined(RGFW_OPENGL) | ||
| 6582 | SwapBuffers(win->src.hdc); | ||
| 6583 | #endif | ||
| 6584 | |||
| 6585 | #if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) | ||
| 6586 | win->src.swapchain->lpVtbl->Present(win->src.swapchain, 0, 0); | ||
| 6587 | #endif | ||
| 6588 | } | ||
| 6589 | } | ||
| 6590 | |||
| 6591 | char* createUTF8FromWideStringWin32(const WCHAR* source) { | ||
| 6592 | char* target; | ||
| 6593 | i32 size; | ||
| 6594 | |||
| 6595 | size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); | ||
| 6596 | if (!size) { | ||
| 6597 | return NULL; | ||
| 6598 | } | ||
| 6599 | |||
| 6600 | target = (char*) RGFW_CALLOC(size, 1); | ||
| 6601 | |||
| 6602 | if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) { | ||
| 6603 | RGFW_FREE(target); | ||
| 6604 | return NULL; | ||
| 6605 | } | ||
| 6606 | |||
| 6607 | return target; | ||
| 6608 | } | ||
| 6609 | |||
| 6610 | static inline LARGE_INTEGER RGFW_win32_initTimer(void) { | ||
| 6611 | static LARGE_INTEGER frequency = {{0, 0}}; | ||
| 6612 | if (frequency.QuadPart == 0) { | ||
| 6613 | timeBeginPeriod(1); | ||
| 6614 | QueryPerformanceFrequency(&frequency); | ||
| 6615 | } | ||
| 6616 | |||
| 6617 | return frequency; | ||
| 6618 | } | ||
| 6619 | |||
| 6620 | u64 RGFW_getTimeNS(void) { | ||
| 6621 | LARGE_INTEGER frequency = RGFW_win32_initTimer(); | ||
| 6622 | |||
| 6623 | LARGE_INTEGER counter; | ||
| 6624 | QueryPerformanceCounter(&counter); | ||
| 6625 | |||
| 6626 | return (u64) ((counter.QuadPart * 1e9) / frequency.QuadPart); | ||
| 6627 | } | ||
| 6628 | |||
| 6629 | u64 RGFW_getTime(void) { | ||
| 6630 | LARGE_INTEGER frequency = RGFW_win32_initTimer(); | ||
| 6631 | |||
| 6632 | LARGE_INTEGER counter; | ||
| 6633 | QueryPerformanceCounter(&counter); | ||
| 6634 | return (u64) (counter.QuadPart / (double) frequency.QuadPart); | ||
| 6635 | } | ||
| 6636 | |||
| 6637 | void RGFW_sleep(u64 ms) { | ||
| 6638 | Sleep(ms); | ||
| 6639 | } | ||
| 6640 | |||
| 6641 | #ifndef RGFW_NO_THREADS | ||
| 6642 | RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { return CreateThread(NULL, 0, ptr, args, 0, NULL); } | ||
| 6643 | void RGFW_cancelThread(RGFW_thread thread) { CloseHandle((HANDLE) thread); } | ||
| 6644 | void RGFW_joinThread(RGFW_thread thread) { WaitForSingleObject((HANDLE) thread, INFINITE); } | ||
| 6645 | void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { SetThreadPriority((HANDLE) thread, priority); } | ||
| 6646 | #endif | ||
| 6647 | #endif /* RGFW_WINDOWS */ | ||
| 6648 | |||
| 6649 | /* | ||
| 6650 | End of Windows defines | ||
| 6651 | */ | ||
| 6652 | |||
| 6653 | |||
| 6654 | |||
| 6655 | /* | ||
| 6656 | |||
| 6657 | Start of MacOS defines | ||
| 6658 | |||
| 6659 | |||
| 6660 | */ | ||
| 6661 | |||
| 6662 | #if defined(RGFW_MACOS) | ||
| 6663 | /* | ||
| 6664 | based on silicon.h | ||
| 6665 | start of cocoa wrapper | ||
| 6666 | */ | ||
| 6667 | |||
| 6668 | #include <CoreVideo/CVDisplayLink.h> | ||
| 6669 | #include <ApplicationServices/ApplicationServices.h> | ||
| 6670 | #include <objc/runtime.h> | ||
| 6671 | #include <objc/message.h> | ||
| 6672 | #include <mach/mach_time.h> | ||
| 6673 | |||
| 6674 | typedef CGRect NSRect; | ||
| 6675 | typedef CGPoint NSPoint; | ||
| 6676 | typedef CGSize NSSize; | ||
| 6677 | |||
| 6678 | typedef void NSBitmapImageRep; | ||
| 6679 | typedef void NSCursor; | ||
| 6680 | typedef void NSDraggingInfo; | ||
| 6681 | typedef void NSWindow; | ||
| 6682 | typedef void NSApplication; | ||
| 6683 | typedef void NSScreen; | ||
| 6684 | typedef void NSEvent; | ||
| 6685 | typedef void NSString; | ||
| 6686 | typedef void NSOpenGLContext; | ||
| 6687 | typedef void NSPasteboard; | ||
| 6688 | typedef void NSColor; | ||
| 6689 | typedef void NSArray; | ||
| 6690 | typedef void NSImageRep; | ||
| 6691 | typedef void NSImage; | ||
| 6692 | typedef void NSOpenGLView; | ||
| 6693 | |||
| 6694 | |||
| 6695 | typedef const char* NSPasteboardType; | ||
| 6696 | typedef unsigned long NSUInteger; | ||
| 6697 | typedef long NSInteger; | ||
| 6698 | typedef NSInteger NSModalResponse; | ||
| 6699 | |||
| 6700 | #ifdef __arm64__ | ||
| 6701 | /* ARM just uses objc_msgSend */ | ||
| 6702 | #define abi_objc_msgSend_stret objc_msgSend | ||
| 6703 | #define abi_objc_msgSend_fpret objc_msgSend | ||
| 6704 | #else /* __i386__ */ | ||
| 6705 | /* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */ | ||
| 6706 | #define abi_objc_msgSend_stret objc_msgSend_stret | ||
| 6707 | #define abi_objc_msgSend_fpret objc_msgSend_fpret | ||
| 6708 | #endif | ||
| 6709 | |||
| 6710 | #define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc")) | ||
| 6711 | #define objc_msgSend_bool ((BOOL (*)(id, SEL))objc_msgSend) | ||
| 6712 | #define objc_msgSend_void ((void (*)(id, SEL))objc_msgSend) | ||
| 6713 | #define objc_msgSend_void_id ((void (*)(id, SEL, id))objc_msgSend) | ||
| 6714 | #define objc_msgSend_uint ((NSUInteger (*)(id, SEL))objc_msgSend) | ||
| 6715 | #define objc_msgSend_void_bool ((void (*)(id, SEL, BOOL))objc_msgSend) | ||
| 6716 | #define objc_msgSend_bool_void ((BOOL (*)(id, SEL))objc_msgSend) | ||
| 6717 | #define objc_msgSend_void_SEL ((void (*)(id, SEL, SEL))objc_msgSend) | ||
| 6718 | #define objc_msgSend_id ((id (*)(id, SEL))objc_msgSend) | ||
| 6719 | #define objc_msgSend_id_id ((id (*)(id, SEL, id))objc_msgSend) | ||
| 6720 | #define objc_msgSend_id_bool ((BOOL (*)(id, SEL, id))objc_msgSend) | ||
| 6721 | #define objc_msgSend_int ((id (*)(id, SEL, int))objc_msgSend) | ||
| 6722 | #define objc_msgSend_arr ((id (*)(id, SEL, int))objc_msgSend) | ||
| 6723 | #define objc_msgSend_ptr ((id (*)(id, SEL, void*))objc_msgSend) | ||
| 6724 | #define objc_msgSend_class ((id (*)(Class, SEL))objc_msgSend) | ||
| 6725 | #define objc_msgSend_class_char ((id (*)(Class, SEL, char*))objc_msgSend) | ||
| 6726 | |||
| 6727 | NSApplication* NSApp = NULL; | ||
| 6728 | |||
| 6729 | void NSRelease(id obj) { | ||
| 6730 | objc_msgSend_void(obj, sel_registerName("release")); | ||
| 6731 | } | ||
| 6732 | |||
| 6733 | #define release NSRelease | ||
| 6734 | |||
| 6735 | NSString* NSString_stringWithUTF8String(const char* str) { | ||
| 6736 | return ((id(*)(id, SEL, const char*))objc_msgSend) | ||
| 6737 | ((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), str); | ||
| 6738 | } | ||
| 6739 | |||
| 6740 | const char* NSString_to_char(NSString* str) { | ||
| 6741 | return ((const char* (*)(id, SEL)) objc_msgSend) (str, sel_registerName("UTF8String")); | ||
| 6742 | } | ||
| 6743 | |||
| 6744 | void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function) { | ||
| 6745 | Class selected_class; | ||
| 6746 | |||
| 6747 | if (strcmp(class_name, "NSView") == 0) { | ||
| 6748 | selected_class = objc_getClass("ViewClass"); | ||
| 6749 | } else if (strcmp(class_name, "NSWindow") == 0) { | ||
| 6750 | selected_class = objc_getClass("WindowClass"); | ||
| 6751 | } else { | ||
| 6752 | selected_class = objc_getClass(class_name); | ||
| 6753 | } | ||
| 6754 | |||
| 6755 | class_addMethod(selected_class, sel_registerName(register_name), (IMP) function, 0); | ||
| 6756 | } | ||
| 6757 | |||
| 6758 | /* Header for the array. */ | ||
| 6759 | typedef struct siArrayHeader { | ||
| 6760 | size_t count; | ||
| 6761 | /* TODO(EimaMei): Add a `type_width` later on. */ | ||
| 6762 | } siArrayHeader; | ||
| 6763 | |||
| 6764 | /* Gets the header of the siArray. */ | ||
| 6765 | #define SI_ARRAY_HEADER(s) ((siArrayHeader*)s - 1) | ||
| 6766 | |||
| 6767 | void* si_array_init_reserve(size_t sizeof_element, size_t count) { | ||
| 6768 | siArrayHeader* ptr = malloc(sizeof(siArrayHeader) + (sizeof_element * count)); | ||
| 6769 | void* array = ptr + sizeof(siArrayHeader); | ||
| 6770 | |||
| 6771 | siArrayHeader* header = SI_ARRAY_HEADER(array); | ||
| 6772 | header->count = count; | ||
| 6773 | |||
| 6774 | return array; | ||
| 6775 | } | ||
| 6776 | |||
| 6777 | #define si_array_len(array) (SI_ARRAY_HEADER(array)->count) | ||
| 6778 | #define si_func_to_SEL(class_name, function) si_impl_func_to_SEL_with_name(class_name, #function":", function) | ||
| 6779 | /* Creates an Objective-C method (SEL) from a regular C function with the option to set the register name.*/ | ||
| 6780 | #define si_func_to_SEL_with_name(class_name, register_name, function) si_impl_func_to_SEL_with_name(class_name, register_name":", function) | ||
| 6781 | |||
| 6782 | unsigned char* NSBitmapImageRep_bitmapData(NSBitmapImageRep* imageRep) { | ||
| 6783 | return ((unsigned char* (*)(id, SEL))objc_msgSend) | ||
| 6784 | (imageRep, sel_registerName("bitmapData")); | ||
| 6785 | } | ||
| 6786 | |||
| 6787 | #define NS_ENUM(type, name) type name; enum | ||
| 6788 | |||
| 6789 | typedef NS_ENUM(NSUInteger, NSBitmapFormat) { | ||
| 6790 | NSBitmapFormatAlphaFirst = 1 << 0, // 0 means is alpha last (RGBA, CMYKA, etc.) | ||
| 6791 | NSBitmapFormatAlphaNonpremultiplied = 1 << 1, // 0 means is premultiplied | ||
| 6792 | NSBitmapFormatFloatingPointSamples = 1 << 2, // 0 is integer | ||
| 6793 | |||
| 6794 | NSBitmapFormatSixteenBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 8), | ||
| 6795 | NSBitmapFormatThirtyTwoBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 9), | ||
| 6796 | NSBitmapFormatSixteenBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 10), | ||
| 6797 | NSBitmapFormatThirtyTwoBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 11) | ||
| 6798 | }; | ||
| 6799 | |||
| 6800 | NSBitmapImageRep* NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) { | ||
| 6801 | void* func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:"); | ||
| 6802 | |||
| 6803 | return (NSBitmapImageRep*) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, const char*, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend) | ||
| 6804 | (NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits); | ||
| 6805 | } | ||
| 6806 | |||
| 6807 | NSColor* NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) { | ||
| 6808 | void* nsclass = objc_getClass("NSColor"); | ||
| 6809 | void* func = sel_registerName("colorWithSRGBRed:green:blue:alpha:"); | ||
| 6810 | return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend) | ||
| 6811 | (nsclass, func, red, green, blue, alpha); | ||
| 6812 | } | ||
| 6813 | |||
| 6814 | NSCursor* NSCursor_initWithImage(NSImage* newImage, NSPoint aPoint) { | ||
| 6815 | void* func = sel_registerName("initWithImage:hotSpot:"); | ||
| 6816 | void* nsclass = objc_getClass("NSCursor"); | ||
| 6817 | |||
| 6818 | return (NSCursor*) ((id(*)(id, SEL, id, NSPoint))objc_msgSend) | ||
| 6819 | (NSAlloc(nsclass), func, newImage, aPoint); | ||
| 6820 | } | ||
| 6821 | |||
| 6822 | void NSImage_addRepresentation(NSImage* image, NSImageRep* imageRep) { | ||
| 6823 | void* func = sel_registerName("addRepresentation:"); | ||
| 6824 | objc_msgSend_void_id(image, func, imageRep); | ||
| 6825 | } | ||
| 6826 | |||
| 6827 | NSImage* NSImage_initWithSize(NSSize size) { | ||
| 6828 | void* func = sel_registerName("initWithSize:"); | ||
| 6829 | return ((id(*)(id, SEL, NSSize))objc_msgSend) | ||
| 6830 | (NSAlloc((id)objc_getClass("NSImage")), func, size); | ||
| 6831 | } | ||
| 6832 | #define NS_OPENGL_ENUM_DEPRECATED(minVers, maxVers) API_AVAILABLE(macos(minVers)) | ||
| 6833 | typedef NS_ENUM(NSInteger, NSOpenGLContextParameter) { | ||
| 6834 | NSOpenGLContextParameterSwapInterval NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */ | ||
| 6835 | NSOpenGLContextParametectxaceOrder NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */ | ||
| 6836 | NSOpenGLContextParametectxaceOpacity NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */ | ||
| 6837 | NSOpenGLContextParametectxaceBackingSize NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 304, /* 2 params. Width/height of surface backing size */ | ||
| 6838 | NSOpenGLContextParameterReclaimResources NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 308, /* 0 params. */ | ||
| 6839 | NSOpenGLContextParameterCurrentRendererID NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 309, /* 1 param. Retrieves the current renderer ID */ | ||
| 6840 | NSOpenGLContextParameterGPUVertexProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 310, /* 1 param. Currently processing vertices with GPU (get) */ | ||
| 6841 | NSOpenGLContextParameterGPUFragmentProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 311, /* 1 param. Currently processing fragments with GPU (get) */ | ||
| 6842 | NSOpenGLContextParameterHasDrawable NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 314, /* 1 param. Boolean returned if drawable is attached */ | ||
| 6843 | NSOpenGLContextParameterMPSwapsInFlight NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 315, /* 1 param. Max number of swaps queued by the MP GL engine */ | ||
| 6844 | |||
| 6845 | NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params. Set or get the swap rectangle {x, y, w, h} */ | ||
| 6846 | NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */ | ||
| 6847 | NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */ | ||
| 6848 | NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */ | ||
| 6849 | NSOpenGLContextParametectxaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param. Surface volatile state */ | ||
| 6850 | }; | ||
| 6851 | |||
| 6852 | |||
| 6853 | void NSOpenGLContext_setValues(NSOpenGLContext* context, const int* vals, NSOpenGLContextParameter param) { | ||
| 6854 | void* func = sel_registerName("setValues:forParameter:"); | ||
| 6855 | ((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend) | ||
| 6856 | (context, func, vals, param); | ||
| 6857 | } | ||
| 6858 | |||
| 6859 | void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs) { | ||
| 6860 | void* func = sel_registerName("initWithAttributes:"); | ||
| 6861 | return (void*) ((id(*)(id, SEL, const uint32_t*))objc_msgSend) | ||
| 6862 | (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), func, attribs); | ||
| 6863 | } | ||
| 6864 | |||
| 6865 | NSOpenGLView* NSOpenGLView_initWithFrame(NSRect frameRect, uint32_t* format) { | ||
| 6866 | void* func = sel_registerName("initWithFrame:pixelFormat:"); | ||
| 6867 | return (NSOpenGLView*) ((id(*)(id, SEL, NSRect, uint32_t*))objc_msgSend) | ||
| 6868 | (NSAlloc((id)objc_getClass("NSOpenGLView")), func, frameRect, format); | ||
| 6869 | } | ||
| 6870 | |||
| 6871 | void NSCursor_performSelector(NSCursor* cursor, void* selector) { | ||
| 6872 | void* func = sel_registerName("performSelector:"); | ||
| 6873 | objc_msgSend_void_SEL(cursor, func, selector); | ||
| 6874 | } | ||
| 6875 | |||
| 6876 | NSPasteboard* NSPasteboard_generalPasteboard(void) { | ||
| 6877 | return (NSPasteboard*) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard")); | ||
| 6878 | } | ||
| 6879 | |||
| 6880 | NSString** cstrToNSStringArray(char** strs, size_t len) { | ||
| 6881 | static NSString* nstrs[6]; | ||
| 6882 | size_t i; | ||
| 6883 | for (i = 0; i < len; i++) | ||
| 6884 | nstrs[i] = NSString_stringWithUTF8String(strs[i]); | ||
| 6885 | |||
| 6886 | return nstrs; | ||
| 6887 | } | ||
| 6888 | |||
| 6889 | const char* NSPasteboard_stringForType(NSPasteboard* pasteboard, NSPasteboardType dataType) { | ||
| 6890 | void* func = sel_registerName("stringForType:"); | ||
| 6891 | return (const char*) NSString_to_char(((id(*)(id, SEL, const char*))objc_msgSend)(pasteboard, func, NSString_stringWithUTF8String(dataType))); | ||
| 6892 | } | ||
| 6893 | |||
| 6894 | NSArray* c_array_to_NSArray(void* array, size_t len) { | ||
| 6895 | SEL func = sel_registerName("initWithObjects:count:"); | ||
| 6896 | void* nsclass = objc_getClass("NSArray"); | ||
| 6897 | return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend) | ||
| 6898 | (NSAlloc(nsclass), func, array, len); | ||
| 6899 | } | ||
| 6900 | |||
| 6901 | void NSregisterForDraggedTypes(void* view, NSPasteboardType* newTypes, size_t len) { | ||
| 6902 | NSString** ntypes = cstrToNSStringArray((char**)newTypes, len); | ||
| 6903 | |||
| 6904 | NSArray* array = c_array_to_NSArray(ntypes, len); | ||
| 6905 | objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array); | ||
| 6906 | NSRelease(array); | ||
| 6907 | } | ||
| 6908 | |||
| 6909 | NSInteger NSPasteBoard_declareTypes(NSPasteboard* pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) { | ||
| 6910 | NSString** ntypes = cstrToNSStringArray((char**)newTypes, len); | ||
| 6911 | |||
| 6912 | void* func = sel_registerName("declareTypes:owner:"); | ||
| 6913 | |||
| 6914 | NSArray* array = c_array_to_NSArray(ntypes, len); | ||
| 6915 | |||
| 6916 | NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend) | ||
| 6917 | (pasteboard, func, array, owner); | ||
| 6918 | NSRelease(array); | ||
| 6919 | |||
| 6920 | return output; | ||
| 6921 | } | ||
| 6922 | |||
| 6923 | bool NSPasteBoard_setString(NSPasteboard* pasteboard, const char* stringToWrite, NSPasteboardType dataType) { | ||
| 6924 | void* func = sel_registerName("setString:forType:"); | ||
| 6925 | return ((bool (*)(id, SEL, id, NSPasteboardType))objc_msgSend) | ||
| 6926 | (pasteboard, func, NSString_stringWithUTF8String(stringToWrite), NSString_stringWithUTF8String(dataType)); | ||
| 6927 | } | ||
| 6928 | |||
| 6929 | void NSRetain(id obj) { objc_msgSend_void(obj, sel_registerName("retain")); } | ||
| 6930 | |||
| 6931 | typedef enum NSApplicationActivationPolicy { | ||
| 6932 | NSApplicationActivationPolicyRegular, | ||
| 6933 | NSApplicationActivationPolicyAccessory, | ||
| 6934 | NSApplicationActivationPolicyProhibited | ||
| 6935 | } NSApplicationActivationPolicy; | ||
| 6936 | |||
| 6937 | typedef NS_ENUM(u32, NSBackingStoreType) { | ||
| 6938 | NSBackingStoreRetained = 0, | ||
| 6939 | NSBackingStoreNonretained = 1, | ||
| 6940 | NSBackingStoreBuffered = 2 | ||
| 6941 | }; | ||
| 6942 | |||
| 6943 | typedef NS_ENUM(u32, NSWindowStyleMask) { | ||
| 6944 | NSWindowStyleMaskBorderless = 0, | ||
| 6945 | NSWindowStyleMaskTitled = 1 << 0, | ||
| 6946 | NSWindowStyleMaskClosable = 1 << 1, | ||
| 6947 | NSWindowStyleMaskMiniaturizable = 1 << 2, | ||
| 6948 | NSWindowStyleMaskResizable = 1 << 3, | ||
| 6949 | NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */ | ||
| 6950 | NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12, | ||
| 6951 | NSWindowStyleMaskFullScreen = 1 << 14, | ||
| 6952 | NSWindowStyleMaskFullSizeContentView = 1 << 15, | ||
| 6953 | NSWindowStyleMaskUtilityWindow = 1 << 4, | ||
| 6954 | NSWindowStyleMaskDocModalWindow = 1 << 6, | ||
| 6955 | NSWindowStyleMaskNonactivatingPanel = 1 << 7, | ||
| 6956 | NSWindowStyleMaskHUDWindow = 1 << 13 | ||
| 6957 | }; | ||
| 6958 | |||
| 6959 | NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; // Replaces NSStringPboardType | ||
| 6960 | |||
| 6961 | |||
| 6962 | typedef NS_ENUM(i32, NSDragOperation) { | ||
| 6963 | NSDragOperationNone = 0, | ||
| 6964 | NSDragOperationCopy = 1, | ||
| 6965 | NSDragOperationLink = 2, | ||
| 6966 | NSDragOperationGeneric = 4, | ||
| 6967 | NSDragOperationPrivate = 8, | ||
| 6968 | NSDragOperationMove = 16, | ||
| 6969 | NSDragOperationDelete = 32, | ||
| 6970 | NSDragOperationEvery = ULONG_MAX, | ||
| 6971 | |||
| 6972 | //NSDragOperationAll_Obsolete API_DEPRECATED("", macos(10.0,10.10)) = 15, // Use NSDragOperationEvery | ||
| 6973 | //NSDragOperationAll API_DEPRECATED("", macos(10.0,10.10)) = NSDragOperationAll_Obsolete, // Use NSDragOperationEvery | ||
| 6974 | }; | ||
| 6975 | |||
| 6976 | void* NSArray_objectAtIndex(NSArray* array, NSUInteger index) { | ||
| 6977 | void* func = sel_registerName("objectAtIndex:"); | ||
| 6978 | return ((id(*)(id, SEL, NSUInteger))objc_msgSend)(array, func, index); | ||
| 6979 | } | ||
| 6980 | |||
| 6981 | const char** NSPasteboard_readObjectsForClasses(NSPasteboard* pasteboard, Class* classArray, size_t len, void* options) { | ||
| 6982 | void* func = sel_registerName("readObjectsForClasses:options:"); | ||
| 6983 | |||
| 6984 | NSArray* array = c_array_to_NSArray(classArray, len); | ||
| 6985 | |||
| 6986 | NSArray* output = (NSArray*) ((id(*)(id, SEL, id, void*))objc_msgSend) | ||
| 6987 | (pasteboard, func, array, options); | ||
| 6988 | |||
| 6989 | NSRelease(array); | ||
| 6990 | NSUInteger count = ((NSUInteger(*)(id, SEL))objc_msgSend)(output, sel_registerName("count")); | ||
| 6991 | |||
| 6992 | const char** res = si_array_init_reserve(sizeof(const char*), count); | ||
| 6993 | |||
| 6994 | void* path_func = sel_registerName("path"); | ||
| 6995 | |||
| 6996 | for (NSUInteger i = 0; i < count; i++) { | ||
| 6997 | void* url = NSArray_objectAtIndex(output, i); | ||
| 6998 | NSString* url_str = ((id(*)(id, SEL))objc_msgSend)(url, path_func); | ||
| 6999 | res[i] = NSString_to_char(url_str); | ||
| 7000 | } | ||
| 7001 | |||
| 7002 | return res; | ||
| 7003 | } | ||
| 7004 | |||
| 7005 | void* NSWindow_contentView(NSWindow* window) { | ||
| 7006 | void* func = sel_registerName("contentView"); | ||
| 7007 | return objc_msgSend_id(window, func); | ||
| 7008 | } | ||
| 7009 | |||
| 7010 | /* | ||
| 7011 | End of cocoa wrapper | ||
| 7012 | */ | ||
| 7013 | |||
| 7014 | char* RGFW_mouseIconSrc[] = {"arrowCursor", "arrowCursor", "IBeamCursor", "crosshairCursor", "pointingHandCursor", "resizeLeftRightCursor", "resizeUpDownCursor", "_windowResizeNorthWestSouthEastCursor", "_windowResizeNorthEastSouthWestCursor", "closedHandCursor", "operationNotAllowedCursor"}; | ||
| 7015 | |||
| 7016 | void* RGFWnsglFramework = NULL; | ||
| 7017 | |||
| 7018 | #ifdef RGFW_OPENGL | ||
| 7019 | void* RGFW_getProcAddress(const char* procname) { | ||
| 7020 | if (RGFWnsglFramework == NULL) | ||
| 7021 | RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); | ||
| 7022 | |||
| 7023 | CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII); | ||
| 7024 | |||
| 7025 | void* symbol = CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName); | ||
| 7026 | |||
| 7027 | CFRelease(symbolName); | ||
| 7028 | |||
| 7029 | return symbol; | ||
| 7030 | } | ||
| 7031 | #endif | ||
| 7032 | |||
| 7033 | CVReturn displayCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { | ||
| 7034 | RGFW_UNUSED(displayLink) RGFW_UNUSED(inNow) RGFW_UNUSED(inOutputTime) RGFW_UNUSED(flagsIn) RGFW_UNUSED(flagsOut) RGFW_UNUSED(displayLinkContext) | ||
| 7035 | return kCVReturnSuccess; | ||
| 7036 | } | ||
| 7037 | |||
| 7038 | id NSWindow_delegate(RGFW_window* win) { | ||
| 7039 | return (id) objc_msgSend_id(win->src.window, sel_registerName("delegate")); | ||
| 7040 | } | ||
| 7041 | |||
| 7042 | u32 RGFW_OnClose(void* self) { | ||
| 7043 | RGFW_window* win = NULL; | ||
| 7044 | object_getInstanceVariable(self, "RGFW_window", (void*)&win); | ||
| 7045 | if (win == NULL) | ||
| 7046 | return true; | ||
| 7047 | |||
| 7048 | win->event.type = RGFW_quit; | ||
| 7049 | RGFW_windowQuitCallback(win); | ||
| 7050 | |||
| 7051 | return true; | ||
| 7052 | } | ||
| 7053 | |||
| 7054 | /* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */ | ||
| 7055 | bool acceptsFirstResponder(void) { return true; } | ||
| 7056 | bool performKeyEquivalent(NSEvent* event) { RGFW_UNUSED(event); return true; } | ||
| 7057 | |||
| 7058 | NSDragOperation draggingEntered(id self, SEL sel, id sender) { | ||
| 7059 | RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); | ||
| 7060 | |||
| 7061 | return NSDragOperationCopy; | ||
| 7062 | } | ||
| 7063 | NSDragOperation draggingUpdated(id self, SEL sel, id sender) { | ||
| 7064 | RGFW_UNUSED(sel); | ||
| 7065 | |||
| 7066 | RGFW_window* win = NULL; | ||
| 7067 | object_getInstanceVariable(self, "RGFW_window", (void*)&win); | ||
| 7068 | if (win == NULL) | ||
| 7069 | return 0; | ||
| 7070 | |||
| 7071 | if (!(win->_winArgs & RGFW_ALLOW_DND)) { | ||
| 7072 | return 0; | ||
| 7073 | } | ||
| 7074 | |||
| 7075 | win->event.type = RGFW_dnd_init; | ||
| 7076 | win->src.dndPassed = 0; | ||
| 7077 | |||
| 7078 | NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); | ||
| 7079 | |||
| 7080 | win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)); | ||
| 7081 | RGFW_dndInitCallback(win, win->event.point); | ||
| 7082 | |||
| 7083 | return NSDragOperationCopy; | ||
| 7084 | } | ||
| 7085 | bool prepareForDragOperation(id self) { | ||
| 7086 | RGFW_window* win = NULL; | ||
| 7087 | object_getInstanceVariable(self, "RGFW_window", (void*)&win); | ||
| 7088 | if (win == NULL) | ||
| 7089 | return true; | ||
| 7090 | |||
| 7091 | if (!(win->_winArgs & RGFW_ALLOW_DND)) { | ||
| 7092 | return false; | ||
| 7093 | } | ||
| 7094 | |||
| 7095 | return true; | ||
| 7096 | } | ||
| 7097 | |||
| 7098 | void RGFW__osxDraggingEnded(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); return; } | ||
| 7099 | |||
| 7100 | /* NOTE(EimaMei): Usually, you never need 'id self, SEL cmd' for C -> Obj-C methods. This isn't the case. */ | ||
| 7101 | bool performDragOperation(id self, SEL sel, id sender) { | ||
| 7102 | RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); | ||
| 7103 | |||
| 7104 | RGFW_window* win = NULL; | ||
| 7105 | object_getInstanceVariable(self, "RGFW_window", (void*)&win); | ||
| 7106 | |||
| 7107 | if (win == NULL) | ||
| 7108 | return false; | ||
| 7109 | |||
| 7110 | // NSPasteboard* pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard")); | ||
| 7111 | |||
| 7112 | ///////////////////////////// | ||
| 7113 | id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard")); | ||
| 7114 | |||
| 7115 | // Get the types of data available on the pasteboard | ||
| 7116 | id types = objc_msgSend_id(pasteBoard, sel_registerName("types")); | ||
| 7117 | |||
| 7118 | // Get the string type for file URLs | ||
| 7119 | id fileURLsType = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "NSFilenamesPboardType"); | ||
| 7120 | |||
| 7121 | // Check if the pasteboard contains file URLs | ||
| 7122 | if (objc_msgSend_id_bool(types, sel_registerName("containsObject:"), fileURLsType) == 0) { | ||
| 7123 | #ifdef RGFW_DEBUG | ||
| 7124 | printf("No files found on the pasteboard.\n"); | ||
| 7125 | #endif | ||
| 7126 | |||
| 7127 | return 0; | ||
| 7128 | } | ||
| 7129 | |||
| 7130 | id fileURLs = objc_msgSend_id_id(pasteBoard, sel_registerName("propertyListForType:"), fileURLsType); | ||
| 7131 | int count = ((int (*)(id, SEL))objc_msgSend)(fileURLs, sel_registerName("count")); | ||
| 7132 | |||
| 7133 | if (count == 0) | ||
| 7134 | return 0; | ||
| 7135 | |||
| 7136 | for (int i = 0; i < count; i++) { | ||
| 7137 | id fileURL = objc_msgSend_arr(fileURLs, sel_registerName("objectAtIndex:"), i); | ||
| 7138 | const char *filePath = ((const char* (*)(id, SEL))objc_msgSend)(fileURL, sel_registerName("UTF8String")); | ||
| 7139 | strncpy(win->event.droppedFiles[i], filePath, RGFW_MAX_PATH); | ||
| 7140 | win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0'; | ||
| 7141 | } | ||
| 7142 | win->event.droppedFilesCount = count; | ||
| 7143 | |||
| 7144 | win->event.type = RGFW_dnd; | ||
| 7145 | win->src.dndPassed = 0; | ||
| 7146 | |||
| 7147 | NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); | ||
| 7148 | win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)); | ||
| 7149 | |||
| 7150 | RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); | ||
| 7151 | |||
| 7152 | return false; | ||
| 7153 | } | ||
| 7154 | |||
| 7155 | static void NSMoveToResourceDir(void) { | ||
| 7156 | /* sourced from glfw */ | ||
| 7157 | char resourcesPath[255]; | ||
| 7158 | |||
| 7159 | CFBundleRef bundle = CFBundleGetMainBundle(); | ||
| 7160 | if (!bundle) | ||
| 7161 | return; | ||
| 7162 | |||
| 7163 | CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); | ||
| 7164 | CFStringRef last = CFURLCopyLastPathComponent(resourcesURL); | ||
| 7165 | |||
| 7166 | if ( | ||
| 7167 | CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo || | ||
| 7168 | CFURLGetFileSystemRepresentation(resourcesURL, true, (u8*) resourcesPath, 255) == 0 | ||
| 7169 | ) { | ||
| 7170 | CFRelease(last); | ||
| 7171 | CFRelease(resourcesURL); | ||
| 7172 | return; | ||
| 7173 | } | ||
| 7174 | |||
| 7175 | CFRelease(last); | ||
| 7176 | CFRelease(resourcesURL); | ||
| 7177 | |||
| 7178 | chdir(resourcesPath); | ||
| 7179 | } | ||
| 7180 | |||
| 7181 | |||
| 7182 | NSSize RGFW__osxWindowResize(void* self, SEL sel, NSSize frameSize) { | ||
| 7183 | RGFW_UNUSED(sel); | ||
| 7184 | |||
| 7185 | RGFW_window* win = NULL; | ||
| 7186 | object_getInstanceVariable(self, "RGFW_window", (void*)&win); | ||
| 7187 | if (win == NULL) | ||
| 7188 | return frameSize; | ||
| 7189 | |||
| 7190 | win->r.w = frameSize.width; | ||
| 7191 | win->r.h = frameSize.height; | ||
| 7192 | win->event.type = RGFW_windowResized; | ||
| 7193 | RGFW_windowResizeCallback(win, win->r); | ||
| 7194 | return frameSize; | ||
| 7195 | } | ||
| 7196 | |||
| 7197 | void RGFW__osxWindowMove(void* self, SEL sel) { | ||
| 7198 | RGFW_UNUSED(sel); | ||
| 7199 | |||
| 7200 | RGFW_window* win = NULL; | ||
| 7201 | object_getInstanceVariable(self, "RGFW_window", (void*)&win); | ||
| 7202 | if (win == NULL) | ||
| 7203 | return; | ||
| 7204 | |||
| 7205 | NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)(win->src.window, sel_registerName("frame")); | ||
| 7206 | win->r.x = (i32) frame.origin.x; | ||
| 7207 | win->r.y = (i32) frame.origin.y; | ||
| 7208 | |||
| 7209 | win->event.type = RGFW_windowMoved; | ||
| 7210 | RGFW_windowMoveCallback(win, win->r); | ||
| 7211 | } | ||
| 7212 | |||
| 7213 | void RGFW__osxUpdateLayer(void* self, SEL sel) { | ||
| 7214 | RGFW_UNUSED(sel); | ||
| 7215 | |||
| 7216 | RGFW_window* win = NULL; | ||
| 7217 | object_getInstanceVariable(self, "RGFW_window", (void*)&win); | ||
| 7218 | if (win == NULL) | ||
| 7219 | return; | ||
| 7220 | |||
| 7221 | win->event.type = RGFW_windowRefresh; | ||
| 7222 | RGFW_windowRefreshCallback(win); | ||
| 7223 | } | ||
| 7224 | |||
| 7225 | RGFWDEF void RGFW_init_buffer(RGFW_window* win); | ||
| 7226 | void RGFW_init_buffer(RGFW_window* win) { | ||
| 7227 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 7228 | if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) | ||
| 7229 | RGFW_bufferSize = RGFW_getScreenSize(); | ||
| 7230 | |||
| 7231 | win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); | ||
| 7232 | |||
| 7233 | #ifdef RGFW_OSMESA | ||
| 7234 | win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); | ||
| 7235 | OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); | ||
| 7236 | #endif | ||
| 7237 | #else | ||
| 7238 | RGFW_UNUSED(win); /*!< if buffer rendering is not being used */ | ||
| 7239 | #endif | ||
| 7240 | } | ||
| 7241 | |||
| 7242 | |||
| 7243 | void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer) { | ||
| 7244 | objc_msgSend_void_id(win->src.view, sel_registerName("setLayer"), layer); | ||
| 7245 | } | ||
| 7246 | |||
| 7247 | void* RGFW_cocoaGetLayer(void) { | ||
| 7248 | return objc_msgSend_class(objc_getClass("CAMetalLayer"), sel_registerName("layer")); | ||
| 7249 | } | ||
| 7250 | |||
| 7251 | |||
| 7252 | NSPasteboardType const NSPasteboardTypeURL = "public.url"; | ||
| 7253 | NSPasteboardType const NSPasteboardTypeFileURL = "public.file-url"; | ||
| 7254 | |||
| 7255 | RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { | ||
| 7256 | static u8 RGFW_loaded = 0; | ||
| 7257 | |||
| 7258 | /* NOTE(EimaMei): Why does Apple hate good code? Like wtf, who thought of methods being a great idea??? | ||
| 7259 | Imagine a universe, where MacOS had a proper system API (we would probably have like 20% better performance). | ||
| 7260 | */ | ||
| 7261 | si_func_to_SEL_with_name("NSObject", "windowShouldClose", RGFW_OnClose); | ||
| 7262 | |||
| 7263 | /* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */ | ||
| 7264 | si_func_to_SEL("NSWindow", acceptsFirstResponder); | ||
| 7265 | si_func_to_SEL("NSWindow", performKeyEquivalent); | ||
| 7266 | |||
| 7267 | // RR Create an autorelease pool | ||
| 7268 | id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); | ||
| 7269 | pool = objc_msgSend_id(pool, sel_registerName("init")); | ||
| 7270 | |||
| 7271 | if (NSApp == NULL) { | ||
| 7272 | NSApp = objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication")); | ||
| 7273 | |||
| 7274 | ((void (*)(id, SEL, NSUInteger))objc_msgSend) | ||
| 7275 | (NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular); | ||
| 7276 | } | ||
| 7277 | |||
| 7278 | RGFW_window* win = RGFW_window_basic_init(rect, args); | ||
| 7279 | |||
| 7280 | RGFW_window_setMouseDefault(win); | ||
| 7281 | |||
| 7282 | NSRect windowRect; | ||
| 7283 | windowRect.origin.x = win->r.x; | ||
| 7284 | windowRect.origin.y = win->r.y; | ||
| 7285 | windowRect.size.width = win->r.w; | ||
| 7286 | windowRect.size.height = win->r.h; | ||
| 7287 | |||
| 7288 | NSBackingStoreType macArgs = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled; | ||
| 7289 | |||
| 7290 | if (!(args & RGFW_NO_RESIZE)) | ||
| 7291 | macArgs |= NSWindowStyleMaskResizable; | ||
| 7292 | if (!(args & RGFW_NO_BORDER)) | ||
| 7293 | macArgs |= NSWindowStyleMaskTitled; | ||
| 7294 | else | ||
| 7295 | macArgs = NSWindowStyleMaskBorderless; | ||
| 7296 | { | ||
| 7297 | void* nsclass = objc_getClass("NSWindow"); | ||
| 7298 | void* func = sel_registerName("initWithContentRect:styleMask:backing:defer:"); | ||
| 7299 | |||
| 7300 | win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend) | ||
| 7301 | (NSAlloc(nsclass), func, windowRect, macArgs, macArgs, false); | ||
| 7302 | } | ||
| 7303 | |||
| 7304 | NSString* str = NSString_stringWithUTF8String(name); | ||
| 7305 | objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str); | ||
| 7306 | |||
| 7307 | #ifdef RGFW_EGL | ||
| 7308 | if ((args & RGFW_NO_INIT_API) == 0) | ||
| 7309 | RGFW_createOpenGLContext(win); | ||
| 7310 | #endif | ||
| 7311 | |||
| 7312 | #ifdef RGFW_OPENGL | ||
| 7313 | if ((args & RGFW_NO_INIT_API) == 0) { | ||
| 7314 | void* attrs = RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE); | ||
| 7315 | void* format = NSOpenGLPixelFormat_initWithAttributes(attrs); | ||
| 7316 | |||
| 7317 | if (format == NULL) { | ||
| 7318 | printf("Failed to load pixel format for OpenGL\n"); | ||
| 7319 | |||
| 7320 | void* attrs = RGFW_initFormatAttribs(1); | ||
| 7321 | format = NSOpenGLPixelFormat_initWithAttributes(attrs); | ||
| 7322 | if (format == NULL) | ||
| 7323 | printf("and loading software rendering OpenGL failed\n"); | ||
| 7324 | else | ||
| 7325 | printf("Switching to software rendering\n"); | ||
| 7326 | } | ||
| 7327 | |||
| 7328 | /* the pixel format can be passed directly to opengl context creation to create a context | ||
| 7329 | this is because the format also includes information about the opengl version (which may be a bad thing) */ | ||
| 7330 | win->src.view = NSOpenGLView_initWithFrame((NSRect){{0, 0}, {win->r.w, win->r.h}}, format); | ||
| 7331 | objc_msgSend_void(win->src.view, sel_registerName("prepareOpenGL")); | ||
| 7332 | win->src.ctx = objc_msgSend_id(win->src.view, sel_registerName("openGLContext")); | ||
| 7333 | } else | ||
| 7334 | #endif | ||
| 7335 | { | ||
| 7336 | NSRect contentRect = (NSRect){{0, 0}, {win->r.w, win->r.h}}; | ||
| 7337 | win->src.view = ((id(*)(id, SEL, NSRect))objc_msgSend) | ||
| 7338 | (NSAlloc((id)objc_getClass("NSView")), sel_registerName("initWithFrame:"), | ||
| 7339 | contentRect); | ||
| 7340 | } | ||
| 7341 | |||
| 7342 | void* contentView = NSWindow_contentView(win->src.window); | ||
| 7343 | objc_msgSend_void_bool(contentView, sel_registerName("setWantsLayer:"), true); | ||
| 7344 | |||
| 7345 | objc_msgSend_void_id(win->src.window, sel_registerName("setContentView:"), win->src.view); | ||
| 7346 | |||
| 7347 | #ifdef RGFW_OPENGL | ||
| 7348 | if ((args & RGFW_NO_INIT_API) == 0) | ||
| 7349 | objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext")); | ||
| 7350 | #endif | ||
| 7351 | if (args & RGFW_TRANSPARENT_WINDOW) { | ||
| 7352 | #ifdef RGFW_OPENGL | ||
| 7353 | if ((args & RGFW_NO_INIT_API) == 0) { | ||
| 7354 | i32 opacity = 0; | ||
| 7355 | #define NSOpenGLCPSurfaceOpacity 236 | ||
| 7356 | NSOpenGLContext_setValues(win->src.ctx, &opacity, NSOpenGLCPSurfaceOpacity); | ||
| 7357 | } | ||
| 7358 | #endif | ||
| 7359 | |||
| 7360 | objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false); | ||
| 7361 | |||
| 7362 | objc_msgSend_void_id(win->src.window, sel_registerName("setBackgroundColor:"), | ||
| 7363 | NSColor_colorWithSRGB(0, 0, 0, 0)); | ||
| 7364 | } | ||
| 7365 | |||
| 7366 | win->src.display = CGMainDisplayID(); | ||
| 7367 | CVDisplayLinkCreateWithCGDisplay(win->src.display, (CVDisplayLinkRef*)&win->src.displayLink); | ||
| 7368 | CVDisplayLinkSetOutputCallback(win->src.displayLink, displayCallback, win); | ||
| 7369 | CVDisplayLinkStart(win->src.displayLink); | ||
| 7370 | |||
| 7371 | RGFW_init_buffer(win); | ||
| 7372 | |||
| 7373 | #ifndef RGFW_NO_MONITOR | ||
| 7374 | if (args & RGFW_SCALE_TO_MONITOR) | ||
| 7375 | RGFW_window_scaleToMonitor(win); | ||
| 7376 | #endif | ||
| 7377 | |||
| 7378 | if (args & RGFW_CENTER) { | ||
| 7379 | RGFW_area screenR = RGFW_getScreenSize(); | ||
| 7380 | RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); | ||
| 7381 | } | ||
| 7382 | |||
| 7383 | if (args & RGFW_HIDE_MOUSE) | ||
| 7384 | RGFW_window_showMouse(win, 0); | ||
| 7385 | |||
| 7386 | if (args & RGFW_COCOA_MOVE_TO_RESOURCE_DIR) | ||
| 7387 | NSMoveToResourceDir(); | ||
| 7388 | |||
| 7389 | Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0); | ||
| 7390 | |||
| 7391 | class_addIvar( | ||
| 7392 | delegateClass, "RGFW_window", | ||
| 7393 | sizeof(RGFW_window*), rint(log2(sizeof(RGFW_window*))), | ||
| 7394 | "L" | ||
| 7395 | ); | ||
| 7396 | |||
| 7397 | class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) RGFW__osxWindowResize, "{NSSize=ff}@:{NSSize=ff}"); | ||
| 7398 | class_addMethod(delegateClass, sel_registerName("updateLayer:"), (IMP) RGFW__osxUpdateLayer, ""); | ||
| 7399 | class_addMethod(delegateClass, sel_registerName("windowWillMove:"), (IMP) RGFW__osxWindowMove, ""); | ||
| 7400 | class_addMethod(delegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, ""); | ||
| 7401 | class_addMethod(delegateClass, sel_registerName("draggingEntered:"), (IMP)draggingEntered, "l@:@"); | ||
| 7402 | class_addMethod(delegateClass, sel_registerName("draggingUpdated:"), (IMP)draggingUpdated, "l@:@"); | ||
| 7403 | class_addMethod(delegateClass, sel_registerName("draggingExited:"), (IMP)RGFW__osxDraggingEnded, "v@:@"); | ||
| 7404 | class_addMethod(delegateClass, sel_registerName("draggingEnded:"), (IMP)RGFW__osxDraggingEnded, "v@:@"); | ||
| 7405 | class_addMethod(delegateClass, sel_registerName("prepareForDragOperation:"), (IMP)prepareForDragOperation, "B@:@"); | ||
| 7406 | class_addMethod(delegateClass, sel_registerName("performDragOperation:"), (IMP)performDragOperation, "B@:@"); | ||
| 7407 | |||
| 7408 | id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init")); | ||
| 7409 | |||
| 7410 | object_setInstanceVariable(delegate, "RGFW_window", win); | ||
| 7411 | |||
| 7412 | objc_msgSend_void_id(win->src.window, sel_registerName("setDelegate:"), delegate); | ||
| 7413 | |||
| 7414 | if (args & RGFW_ALLOW_DND) { | ||
| 7415 | win->_winArgs |= RGFW_ALLOW_DND; | ||
| 7416 | |||
| 7417 | NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString}; | ||
| 7418 | NSregisterForDraggedTypes(win->src.window, types, 3); | ||
| 7419 | } | ||
| 7420 | |||
| 7421 | // Show the window | ||
| 7422 | objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true); | ||
| 7423 | ((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL); | ||
| 7424 | objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true); | ||
| 7425 | |||
| 7426 | if (!RGFW_loaded) { | ||
| 7427 | objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow")); | ||
| 7428 | |||
| 7429 | RGFW_loaded = 1; | ||
| 7430 | } | ||
| 7431 | |||
| 7432 | objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow")); | ||
| 7433 | |||
| 7434 | objc_msgSend_void(NSApp, sel_registerName("finishLaunching")); | ||
| 7435 | |||
| 7436 | if (RGFW_root == NULL) | ||
| 7437 | RGFW_root = win; | ||
| 7438 | |||
| 7439 | NSRetain(win->src.window); | ||
| 7440 | NSRetain(NSApp); | ||
| 7441 | |||
| 7442 | return win; | ||
| 7443 | } | ||
| 7444 | |||
| 7445 | void RGFW_window_setBorder(RGFW_window* win, u8 border) { | ||
| 7446 | NSBackingStoreType storeType = NSWindowStyleMaskBorderless; | ||
| 7447 | if (!border) { | ||
| 7448 | storeType = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; | ||
| 7449 | } | ||
| 7450 | if (!(win->_winArgs & RGFW_NO_RESIZE)) { | ||
| 7451 | storeType |= NSWindowStyleMaskResizable; | ||
| 7452 | } | ||
| 7453 | |||
| 7454 | ((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)(win->src.window, sel_registerName("setStyleMask:"), storeType); | ||
| 7455 | |||
| 7456 | objc_msgSend_void_bool(win->src.window, sel_registerName("setHasShadow:"), border); | ||
| 7457 | } | ||
| 7458 | |||
| 7459 | RGFW_area RGFW_getScreenSize(void) { | ||
| 7460 | static CGDirectDisplayID display = 0; | ||
| 7461 | |||
| 7462 | if (display == 0) | ||
| 7463 | display = CGMainDisplayID(); | ||
| 7464 | |||
| 7465 | return RGFW_AREA(CGDisplayPixelsWide(display), CGDisplayPixelsHigh(display)); | ||
| 7466 | } | ||
| 7467 | |||
| 7468 | RGFW_point RGFW_getGlobalMousePoint(void) { | ||
| 7469 | assert(RGFW_root != NULL); | ||
| 7470 | |||
| 7471 | CGEventRef e = CGEventCreate(NULL); | ||
| 7472 | CGPoint point = CGEventGetLocation(e); | ||
| 7473 | CFRelease(e); | ||
| 7474 | |||
| 7475 | return RGFW_POINT((u32) point.x, (u32) point.y); /*!< the point is loaded during event checks */ | ||
| 7476 | } | ||
| 7477 | |||
| 7478 | RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { | ||
| 7479 | NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(win->src.window, sel_registerName("mouseLocationOutsideOfEventStream")); | ||
| 7480 | |||
| 7481 | return RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)); | ||
| 7482 | } | ||
| 7483 | |||
| 7484 | u32 RGFW_keysPressed[10]; /*10 keys at a time*/ | ||
| 7485 | typedef NS_ENUM(u32, NSEventType) { /* various types of events */ | ||
| 7486 | NSEventTypeLeftMouseDown = 1, | ||
| 7487 | NSEventTypeLeftMouseUp = 2, | ||
| 7488 | NSEventTypeRightMouseDown = 3, | ||
| 7489 | NSEventTypeRightMouseUp = 4, | ||
| 7490 | NSEventTypeMouseMoved = 5, | ||
| 7491 | NSEventTypeLeftMouseDragged = 6, | ||
| 7492 | NSEventTypeRightMouseDragged = 7, | ||
| 7493 | NSEventTypeMouseEntered = 8, | ||
| 7494 | NSEventTypeMouseExited = 9, | ||
| 7495 | NSEventTypeKeyDown = 10, | ||
| 7496 | NSEventTypeKeyUp = 11, | ||
| 7497 | NSEventTypeFlagsChanged = 12, | ||
| 7498 | NSEventTypeAppKitDefined = 13, | ||
| 7499 | NSEventTypeSystemDefined = 14, | ||
| 7500 | NSEventTypeApplicationDefined = 15, | ||
| 7501 | NSEventTypePeriodic = 16, | ||
| 7502 | NSEventTypeCursorUpdate = 17, | ||
| 7503 | NSEventTypeScrollWheel = 22, | ||
| 7504 | NSEventTypeTabletPoint = 23, | ||
| 7505 | NSEventTypeTabletProximity = 24, | ||
| 7506 | NSEventTypeOtherMouseDown = 25, | ||
| 7507 | NSEventTypeOtherMouseUp = 26, | ||
| 7508 | NSEventTypeOtherMouseDragged = 27, | ||
| 7509 | /* The following event types are available on some hardware on 10.5.2 and later */ | ||
| 7510 | NSEventTypeGesture API_AVAILABLE(macos(10.5)) = 29, | ||
| 7511 | NSEventTypeMagnify API_AVAILABLE(macos(10.5)) = 30, | ||
| 7512 | NSEventTypeSwipe API_AVAILABLE(macos(10.5)) = 31, | ||
| 7513 | NSEventTypeRotate API_AVAILABLE(macos(10.5)) = 18, | ||
| 7514 | NSEventTypeBeginGesture API_AVAILABLE(macos(10.5)) = 19, | ||
| 7515 | NSEventTypeEndGesture API_AVAILABLE(macos(10.5)) = 20, | ||
| 7516 | |||
| 7517 | NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32, | ||
| 7518 | NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33, | ||
| 7519 | |||
| 7520 | NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34, | ||
| 7521 | NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37, | ||
| 7522 | |||
| 7523 | NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38, | ||
| 7524 | }; | ||
| 7525 | |||
| 7526 | typedef NS_ENUM(unsigned long long, NSEventMask) { /* masks for the types of events */ | ||
| 7527 | NSEventMaskLeftMouseDown = 1ULL << NSEventTypeLeftMouseDown, | ||
| 7528 | NSEventMaskLeftMouseUp = 1ULL << NSEventTypeLeftMouseUp, | ||
| 7529 | NSEventMaskRightMouseDown = 1ULL << NSEventTypeRightMouseDown, | ||
| 7530 | NSEventMaskRightMouseUp = 1ULL << NSEventTypeRightMouseUp, | ||
| 7531 | NSEventMaskMouseMoved = 1ULL << NSEventTypeMouseMoved, | ||
| 7532 | NSEventMaskLeftMouseDragged = 1ULL << NSEventTypeLeftMouseDragged, | ||
| 7533 | NSEventMaskRightMouseDragged = 1ULL << NSEventTypeRightMouseDragged, | ||
| 7534 | NSEventMaskMouseEntered = 1ULL << NSEventTypeMouseEntered, | ||
| 7535 | NSEventMaskMouseExited = 1ULL << NSEventTypeMouseExited, | ||
| 7536 | NSEventMaskKeyDown = 1ULL << NSEventTypeKeyDown, | ||
| 7537 | NSEventMaskKeyUp = 1ULL << NSEventTypeKeyUp, | ||
| 7538 | NSEventMaskFlagsChanged = 1ULL << NSEventTypeFlagsChanged, | ||
| 7539 | NSEventMaskAppKitDefined = 1ULL << NSEventTypeAppKitDefined, | ||
| 7540 | NSEventMaskSystemDefined = 1ULL << NSEventTypeSystemDefined, | ||
| 7541 | NSEventMaskApplicationDefined = 1ULL << NSEventTypeApplicationDefined, | ||
| 7542 | NSEventMaskPeriodic = 1ULL << NSEventTypePeriodic, | ||
| 7543 | NSEventMaskCursorUpdate = 1ULL << NSEventTypeCursorUpdate, | ||
| 7544 | NSEventMaskScrollWheel = 1ULL << NSEventTypeScrollWheel, | ||
| 7545 | NSEventMaskTabletPoint = 1ULL << NSEventTypeTabletPoint, | ||
| 7546 | NSEventMaskTabletProximity = 1ULL << NSEventTypeTabletProximity, | ||
| 7547 | NSEventMaskOtherMouseDown = 1ULL << NSEventTypeOtherMouseDown, | ||
| 7548 | NSEventMaskOtherMouseUp = 1ULL << NSEventTypeOtherMouseUp, | ||
| 7549 | NSEventMaskOtherMouseDragged = 1ULL << NSEventTypeOtherMouseDragged, | ||
| 7550 | /* The following event masks are available on some hardware on 10.5.2 and later */ | ||
| 7551 | NSEventMaskGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeGesture, | ||
| 7552 | NSEventMaskMagnify API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeMagnify, | ||
| 7553 | NSEventMaskSwipe API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeSwipe, | ||
| 7554 | NSEventMaskRotate API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeRotate, | ||
| 7555 | NSEventMaskBeginGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeBeginGesture, | ||
| 7556 | NSEventMaskEndGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeEndGesture, | ||
| 7557 | |||
| 7558 | /* Note: You can only use these event masks on 64 bit. In other words, you cannot setup a local, nor global, event monitor for these event types on 32 bit. Also, you cannot search the event queue for them (nextEventMatchingMask:...) on 32 bit. | ||
| 7559 | */ | ||
| 7560 | NSEventMaskSmartMagnify API_AVAILABLE(macos(10.8)) = 1ULL << NSEventTypeSmartMagnify, | ||
| 7561 | NSEventMaskPressure API_AVAILABLE(macos(10.10.3)) = 1ULL << NSEventTypePressure, | ||
| 7562 | NSEventMaskDirectTouch API_AVAILABLE(macos(10.12.2)) = 1ULL << NSEventTypeDirectTouch, | ||
| 7563 | |||
| 7564 | NSEventMaskChangeMode API_AVAILABLE(macos(10.15)) = 1ULL << NSEventTypeChangeMode, | ||
| 7565 | |||
| 7566 | NSEventMaskAny = ULONG_MAX, | ||
| 7567 | |||
| 7568 | }; | ||
| 7569 | |||
| 7570 | typedef enum NSEventModifierFlags { | ||
| 7571 | NSEventModifierFlagCapsLock = 1 << 16, | ||
| 7572 | NSEventModifierFlagShift = 1 << 17, | ||
| 7573 | NSEventModifierFlagControl = 1 << 18, | ||
| 7574 | NSEventModifierFlagOption = 1 << 19, | ||
| 7575 | NSEventModifierFlagCommand = 1 << 20, | ||
| 7576 | NSEventModifierFlagNumericPad = 1 << 21 | ||
| 7577 | } NSEventModifierFlags; | ||
| 7578 | |||
| 7579 | void RGFW_stopCheckEvents(void) { | ||
| 7580 | id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); | ||
| 7581 | eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); | ||
| 7582 | |||
| 7583 | NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventType, NSPoint, NSEventModifierFlags, void*, NSInteger, void**, short, NSInteger, NSInteger))objc_msgSend) | ||
| 7584 | (NSApp, sel_registerName("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"), | ||
| 7585 | NSEventTypeApplicationDefined, (NSPoint){0, 0}, 0, 0, 0, NULL, 0, 0, 0); | ||
| 7586 | |||
| 7587 | ((void (*)(id, SEL, id, bool))objc_msgSend) | ||
| 7588 | (NSApp, sel_registerName("postEvent:atStart:"), e, 1); | ||
| 7589 | |||
| 7590 | objc_msgSend_bool_void(eventPool, sel_registerName("drain")); | ||
| 7591 | } | ||
| 7592 | |||
| 7593 | void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { | ||
| 7594 | RGFW_UNUSED(win); | ||
| 7595 | |||
| 7596 | id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); | ||
| 7597 | eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); | ||
| 7598 | |||
| 7599 | void* date = (void*) ((id(*)(Class, SEL, double))objc_msgSend) | ||
| 7600 | (objc_getClass("NSDate"), sel_registerName("dateWithTimeIntervalSinceNow:"), waitMS); | ||
| 7601 | |||
| 7602 | NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend) | ||
| 7603 | (NSApp, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), | ||
| 7604 | ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); | ||
| 7605 | |||
| 7606 | |||
| 7607 | if (e) { | ||
| 7608 | objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); | ||
| 7609 | } | ||
| 7610 | |||
| 7611 | objc_msgSend_bool_void(eventPool, sel_registerName("drain")); | ||
| 7612 | } | ||
| 7613 | |||
| 7614 | RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { | ||
| 7615 | assert(win != NULL); | ||
| 7616 | |||
| 7617 | if (win->event.type == RGFW_quit) | ||
| 7618 | return NULL; | ||
| 7619 | |||
| 7620 | if ((win->event.type == RGFW_dnd || win->event.type == RGFW_dnd_init) && win->src.dndPassed == 0) { | ||
| 7621 | win->src.dndPassed = 1; | ||
| 7622 | return &win->event; | ||
| 7623 | } | ||
| 7624 | |||
| 7625 | id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); | ||
| 7626 | eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); | ||
| 7627 | |||
| 7628 | static void* eventFunc = NULL; | ||
| 7629 | if (eventFunc == NULL) | ||
| 7630 | eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"); | ||
| 7631 | |||
| 7632 | if ((win->event.type == RGFW_windowMoved || win->event.type == RGFW_windowResized || win->event.type == RGFW_windowRefresh) && win->event.keyCode != 120) { | ||
| 7633 | win->event.keyCode = 120; | ||
| 7634 | objc_msgSend_bool_void(eventPool, sel_registerName("drain")); | ||
| 7635 | return &win->event; | ||
| 7636 | } | ||
| 7637 | |||
| 7638 | void* date = NULL; | ||
| 7639 | |||
| 7640 | NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend) | ||
| 7641 | (NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); | ||
| 7642 | |||
| 7643 | if (e == NULL) { | ||
| 7644 | objc_msgSend_bool_void(eventPool, sel_registerName("drain")); | ||
| 7645 | return NULL; | ||
| 7646 | } | ||
| 7647 | |||
| 7648 | if (objc_msgSend_id(e, sel_registerName("window")) != win->src.window) { | ||
| 7649 | ((void (*)(id, SEL, id, bool))objc_msgSend) | ||
| 7650 | (NSApp, sel_registerName("postEvent:atStart:"), e, 0); | ||
| 7651 | |||
| 7652 | objc_msgSend_bool_void(eventPool, sel_registerName("drain")); | ||
| 7653 | return NULL; | ||
| 7654 | } | ||
| 7655 | |||
| 7656 | if (win->event.droppedFilesCount) { | ||
| 7657 | u32 i; | ||
| 7658 | for (i = 0; i < win->event.droppedFilesCount; i++) | ||
| 7659 | win->event.droppedFiles[i][0] = '\0'; | ||
| 7660 | } | ||
| 7661 | |||
| 7662 | win->event.droppedFilesCount = 0; | ||
| 7663 | win->event.type = 0; | ||
| 7664 | |||
| 7665 | switch (objc_msgSend_uint(e, sel_registerName("type"))) { | ||
| 7666 | case NSEventTypeMouseEntered: { | ||
| 7667 | win->event.type = RGFW_mouseEnter; | ||
| 7668 | NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow")); | ||
| 7669 | |||
| 7670 | win->event.point = RGFW_POINT((i32) p.x, (i32) (win->r.h - p.y)); | ||
| 7671 | RGFW_mouseNotifyCallBack(win, win->event.point, 1); | ||
| 7672 | break; | ||
| 7673 | } | ||
| 7674 | |||
| 7675 | case NSEventTypeMouseExited: | ||
| 7676 | win->event.type = RGFW_mouseLeave; | ||
| 7677 | RGFW_mouseNotifyCallBack(win, win->event.point, 0); | ||
| 7678 | break; | ||
| 7679 | |||
| 7680 | case NSEventTypeKeyDown: { | ||
| 7681 | u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode")); | ||
| 7682 | win->event.keyCode = RGFW_apiKeyCodeToRGFW(key); | ||
| 7683 | RGFW_keyboard[win->event.keyCode].prev = RGFW_keyboard[win->event.keyCode].current; | ||
| 7684 | |||
| 7685 | win->event.type = RGFW_keyPressed; | ||
| 7686 | char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters"))); | ||
| 7687 | strncpy(win->event.keyName, str, 16); | ||
| 7688 | win->event.repeat = RGFW_isPressed(win, win->event.keyCode); | ||
| 7689 | RGFW_keyboard[win->event.keyCode].current = 1; | ||
| 7690 | |||
| 7691 | RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1); | ||
| 7692 | break; | ||
| 7693 | } | ||
| 7694 | |||
| 7695 | case NSEventTypeKeyUp: { | ||
| 7696 | u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode")); | ||
| 7697 | win->event.keyCode = RGFW_apiKeyCodeToRGFW(key);; | ||
| 7698 | |||
| 7699 | RGFW_keyboard[win->event.keyCode].prev = RGFW_keyboard[win->event.keyCode].current; | ||
| 7700 | |||
| 7701 | win->event.type = RGFW_keyReleased; | ||
| 7702 | char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters"))); | ||
| 7703 | strncpy(win->event.keyName, str, 16); | ||
| 7704 | |||
| 7705 | RGFW_keyboard[win->event.keyCode].current = 0; | ||
| 7706 | RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 0); | ||
| 7707 | break; | ||
| 7708 | } | ||
| 7709 | |||
| 7710 | case NSEventTypeFlagsChanged: { | ||
| 7711 | u32 flags = objc_msgSend_uint(e, sel_registerName("modifierFlags")); | ||
| 7712 | RGFW_updateLockState(win, ((u32)(flags & NSEventModifierFlagCapsLock) % 255), ((flags & NSEventModifierFlagNumericPad) % 255)); | ||
| 7713 | |||
| 7714 | u8 i; | ||
| 7715 | for (i = 0; i < 9; i++) | ||
| 7716 | RGFW_keyboard[i + RGFW_CapsLock].prev = 0; | ||
| 7717 | |||
| 7718 | for (i = 0; i < 5; i++) { | ||
| 7719 | u32 shift = (1 << (i + 16)); | ||
| 7720 | u32 key = i + RGFW_CapsLock; | ||
| 7721 | |||
| 7722 | if ((flags & shift) && !RGFW_wasPressed(win, key)) { | ||
| 7723 | RGFW_keyboard[key].current = 1; | ||
| 7724 | |||
| 7725 | if (key != RGFW_CapsLock) | ||
| 7726 | RGFW_keyboard[key+ 4].current = 1; | ||
| 7727 | |||
| 7728 | win->event.type = RGFW_keyPressed; | ||
| 7729 | win->event.keyCode = key; | ||
| 7730 | break; | ||
| 7731 | } | ||
| 7732 | |||
| 7733 | if (!(flags & shift) && RGFW_wasPressed(win, key)) { | ||
| 7734 | RGFW_keyboard[key].current = 0; | ||
| 7735 | |||
| 7736 | if (key != RGFW_CapsLock) | ||
| 7737 | RGFW_keyboard[key + 4].current = 0; | ||
| 7738 | |||
| 7739 | win->event.type = RGFW_keyReleased; | ||
| 7740 | win->event.keyCode = key; | ||
| 7741 | break; | ||
| 7742 | } | ||
| 7743 | } | ||
| 7744 | |||
| 7745 | RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, win->event.type == RGFW_keyPressed); | ||
| 7746 | |||
| 7747 | break; | ||
| 7748 | } | ||
| 7749 | case NSEventTypeLeftMouseDragged: | ||
| 7750 | case NSEventTypeOtherMouseDragged: | ||
| 7751 | case NSEventTypeRightMouseDragged: | ||
| 7752 | case NSEventTypeMouseMoved: | ||
| 7753 | win->event.type = RGFW_mousePosChanged; | ||
| 7754 | NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow")); | ||
| 7755 | win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)); | ||
| 7756 | |||
| 7757 | if ((win->_winArgs & RGFW_HOLD_MOUSE)) { | ||
| 7758 | p.x = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaX")); | ||
| 7759 | p.y = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY")); | ||
| 7760 | |||
| 7761 | win->event.point = RGFW_POINT((i32)p.x, (i32)p.y); | ||
| 7762 | } | ||
| 7763 | |||
| 7764 | RGFW_mousePosCallback(win, win->event.point); | ||
| 7765 | break; | ||
| 7766 | |||
| 7767 | case NSEventTypeLeftMouseDown: | ||
| 7768 | win->event.button = RGFW_mouseLeft; | ||
| 7769 | win->event.type = RGFW_mouseButtonPressed; | ||
| 7770 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 7771 | RGFW_mouseButtons[win->event.button].current = 1; | ||
| 7772 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); | ||
| 7773 | break; | ||
| 7774 | |||
| 7775 | case NSEventTypeOtherMouseDown: | ||
| 7776 | win->event.button = RGFW_mouseMiddle; | ||
| 7777 | win->event.type = RGFW_mouseButtonPressed; | ||
| 7778 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 7779 | RGFW_mouseButtons[win->event.button].current = 1; | ||
| 7780 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); | ||
| 7781 | break; | ||
| 7782 | |||
| 7783 | case NSEventTypeRightMouseDown: | ||
| 7784 | win->event.button = RGFW_mouseRight; | ||
| 7785 | win->event.type = RGFW_mouseButtonPressed; | ||
| 7786 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 7787 | RGFW_mouseButtons[win->event.button].current = 1; | ||
| 7788 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); | ||
| 7789 | break; | ||
| 7790 | |||
| 7791 | case NSEventTypeLeftMouseUp: | ||
| 7792 | win->event.button = RGFW_mouseLeft; | ||
| 7793 | win->event.type = RGFW_mouseButtonReleased; | ||
| 7794 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 7795 | RGFW_mouseButtons[win->event.button].current = 0; | ||
| 7796 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); | ||
| 7797 | break; | ||
| 7798 | |||
| 7799 | case NSEventTypeOtherMouseUp: | ||
| 7800 | win->event.button = RGFW_mouseMiddle; | ||
| 7801 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 7802 | RGFW_mouseButtons[win->event.button].current = 0; | ||
| 7803 | win->event.type = RGFW_mouseButtonReleased; | ||
| 7804 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); | ||
| 7805 | break; | ||
| 7806 | |||
| 7807 | case NSEventTypeRightMouseUp: | ||
| 7808 | win->event.button = RGFW_mouseRight; | ||
| 7809 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 7810 | RGFW_mouseButtons[win->event.button].current = 0; | ||
| 7811 | win->event.type = RGFW_mouseButtonReleased; | ||
| 7812 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); | ||
| 7813 | break; | ||
| 7814 | |||
| 7815 | case NSEventTypeScrollWheel: { | ||
| 7816 | double deltaY = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY")); | ||
| 7817 | |||
| 7818 | if (deltaY > 0) { | ||
| 7819 | win->event.button = RGFW_mouseScrollUp; | ||
| 7820 | } | ||
| 7821 | else if (deltaY < 0) { | ||
| 7822 | win->event.button = RGFW_mouseScrollDown; | ||
| 7823 | } | ||
| 7824 | |||
| 7825 | RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; | ||
| 7826 | RGFW_mouseButtons[win->event.button].current = 1; | ||
| 7827 | |||
| 7828 | win->event.scroll = deltaY; | ||
| 7829 | |||
| 7830 | win->event.type = RGFW_mouseButtonPressed; | ||
| 7831 | RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); | ||
| 7832 | break; | ||
| 7833 | } | ||
| 7834 | |||
| 7835 | default: | ||
| 7836 | break; | ||
| 7837 | } | ||
| 7838 | |||
| 7839 | objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); | ||
| 7840 | ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows")); | ||
| 7841 | |||
| 7842 | objc_msgSend_bool_void(eventPool, sel_registerName("drain")); | ||
| 7843 | return &win->event; | ||
| 7844 | } | ||
| 7845 | |||
| 7846 | |||
| 7847 | void RGFW_window_move(RGFW_window* win, RGFW_point v) { | ||
| 7848 | assert(win != NULL); | ||
| 7849 | |||
| 7850 | win->r.x = v.x; | ||
| 7851 | win->r.y = v.y; | ||
| 7852 | ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) | ||
| 7853 | (win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true); | ||
| 7854 | } | ||
| 7855 | |||
| 7856 | void RGFW_window_resize(RGFW_window* win, RGFW_area a) { | ||
| 7857 | assert(win != NULL); | ||
| 7858 | |||
| 7859 | win->r.w = a.w; | ||
| 7860 | win->r.h = a.h; | ||
| 7861 | ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) | ||
| 7862 | (win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true); | ||
| 7863 | } | ||
| 7864 | |||
| 7865 | void RGFW_window_minimize(RGFW_window* win) { | ||
| 7866 | assert(win != NULL); | ||
| 7867 | |||
| 7868 | objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL); | ||
| 7869 | } | ||
| 7870 | |||
| 7871 | void RGFW_window_restore(RGFW_window* win) { | ||
| 7872 | assert(win != NULL); | ||
| 7873 | |||
| 7874 | objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL); | ||
| 7875 | } | ||
| 7876 | |||
| 7877 | void RGFW_window_setName(RGFW_window* win, char* name) { | ||
| 7878 | assert(win != NULL); | ||
| 7879 | |||
| 7880 | NSString* str = NSString_stringWithUTF8String(name); | ||
| 7881 | objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str); | ||
| 7882 | } | ||
| 7883 | |||
| 7884 | #ifndef RGFW_NO_PASSTHROUGH | ||
| 7885 | void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { | ||
| 7886 | objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough); | ||
| 7887 | } | ||
| 7888 | #endif | ||
| 7889 | |||
| 7890 | void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { | ||
| 7891 | if (a.w == 0 && a.h == 0) | ||
| 7892 | return; | ||
| 7893 | |||
| 7894 | ((void (*)(id, SEL, NSSize))objc_msgSend) | ||
| 7895 | (win->src.window, sel_registerName("setMinSize:"), (NSSize){a.w, a.h}); | ||
| 7896 | } | ||
| 7897 | |||
| 7898 | void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { | ||
| 7899 | if (a.w == 0 && a.h == 0) | ||
| 7900 | return; | ||
| 7901 | |||
| 7902 | ((void (*)(id, SEL, NSSize))objc_msgSend) | ||
| 7903 | (win->src.window, sel_registerName("setMaxSize:"), (NSSize){a.w, a.h}); | ||
| 7904 | } | ||
| 7905 | |||
| 7906 | void RGFW_window_setIcon(RGFW_window* win, u8* data, RGFW_area area, i32 channels) { | ||
| 7907 | assert(win != NULL); | ||
| 7908 | |||
| 7909 | /* code by EimaMei */ | ||
| 7910 | // Make a bitmap representation, then copy the loaded image into it. | ||
| 7911 | void* representation = NSBitmapImageRep_initWithBitmapData(NULL, area.w, area.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, area.w * channels, 8 * channels); | ||
| 7912 | memcpy(NSBitmapImageRep_bitmapData(representation), data, area.w * area.h * channels); | ||
| 7913 | |||
| 7914 | // Add ze representation. | ||
| 7915 | void* dock_image = NSImage_initWithSize((NSSize){area.w, area.h}); | ||
| 7916 | NSImage_addRepresentation(dock_image, (void*) representation); | ||
| 7917 | |||
| 7918 | // Finally, set the dock image to it. | ||
| 7919 | objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), dock_image); | ||
| 7920 | // Free the garbage. | ||
| 7921 | release(dock_image); | ||
| 7922 | release(representation); | ||
| 7923 | } | ||
| 7924 | |||
| 7925 | NSCursor* NSCursor_arrowStr(char* str) { | ||
| 7926 | void* nclass = objc_getClass("NSCursor"); | ||
| 7927 | void* func = sel_registerName(str); | ||
| 7928 | return (NSCursor*) objc_msgSend_id(nclass, func); | ||
| 7929 | } | ||
| 7930 | |||
| 7931 | void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { | ||
| 7932 | assert(win != NULL); | ||
| 7933 | |||
| 7934 | if (image == NULL) { | ||
| 7935 | objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set")); | ||
| 7936 | return; | ||
| 7937 | } | ||
| 7938 | |||
| 7939 | /* NOTE(EimaMei): Code by yours truly. */ | ||
| 7940 | // Make a bitmap representation, then copy the loaded image into it. | ||
| 7941 | void* representation = NSBitmapImageRep_initWithBitmapData(NULL, a.w, a.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, a.w * channels, 8 * channels); | ||
| 7942 | memcpy(NSBitmapImageRep_bitmapData(representation), image, a.w * a.h * channels); | ||
| 7943 | |||
| 7944 | // Add ze representation. | ||
| 7945 | void* cursor_image = NSImage_initWithSize((NSSize){a.w, a.h}); | ||
| 7946 | NSImage_addRepresentation(cursor_image, representation); | ||
| 7947 | |||
| 7948 | // Finally, set the cursor image. | ||
| 7949 | void* cursor = NSCursor_initWithImage(cursor_image, (NSPoint){0.0, 0.0}); | ||
| 7950 | |||
| 7951 | objc_msgSend_void(cursor, sel_registerName("set")); | ||
| 7952 | |||
| 7953 | // Free the garbage. | ||
| 7954 | release(cursor_image); | ||
| 7955 | release(representation); | ||
| 7956 | } | ||
| 7957 | |||
| 7958 | void RGFW_window_setMouseDefault(RGFW_window* win) { | ||
| 7959 | RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW); | ||
| 7960 | } | ||
| 7961 | |||
| 7962 | void RGFW_window_showMouse(RGFW_window* win, i8 show) { | ||
| 7963 | RGFW_UNUSED(win); | ||
| 7964 | |||
| 7965 | if (show) { | ||
| 7966 | CGDisplayShowCursor(kCGDirectMainDisplay); | ||
| 7967 | } | ||
| 7968 | else { | ||
| 7969 | CGDisplayHideCursor(kCGDirectMainDisplay); | ||
| 7970 | } | ||
| 7971 | } | ||
| 7972 | |||
| 7973 | void RGFW_window_setMouseStandard(RGFW_window* win, u8 stdMouses) { | ||
| 7974 | if (stdMouses > ((sizeof(RGFW_mouseIconSrc)) / (sizeof(char*)))) | ||
| 7975 | return; | ||
| 7976 | |||
| 7977 | char* mouseStr = RGFW_mouseIconSrc[stdMouses]; | ||
| 7978 | void* mouse = NSCursor_arrowStr(mouseStr); | ||
| 7979 | |||
| 7980 | if (mouse == NULL) | ||
| 7981 | return; | ||
| 7982 | |||
| 7983 | RGFW_UNUSED(win); | ||
| 7984 | CGDisplayShowCursor(kCGDirectMainDisplay); | ||
| 7985 | objc_msgSend_void(mouse, sel_registerName("set")); | ||
| 7986 | } | ||
| 7987 | |||
| 7988 | void RGFW_releaseCursor(RGFW_window* win) { | ||
| 7989 | RGFW_UNUSED(win); | ||
| 7990 | CGAssociateMouseAndMouseCursorPosition(1); | ||
| 7991 | } | ||
| 7992 | |||
| 7993 | void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { | ||
| 7994 | RGFW_UNUSED(win) | ||
| 7995 | |||
| 7996 | CGWarpMouseCursorPosition(CGPointMake(r.x + (r.w / 2), r.y + (r.h / 2))); | ||
| 7997 | CGAssociateMouseAndMouseCursorPosition(0); | ||
| 7998 | } | ||
| 7999 | |||
| 8000 | void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { | ||
| 8001 | RGFW_UNUSED(win); | ||
| 8002 | |||
| 8003 | CGWarpMouseCursorPosition(CGPointMake(v.x, v.y)); | ||
| 8004 | } | ||
| 8005 | |||
| 8006 | |||
| 8007 | void RGFW_window_hide(RGFW_window* win) { | ||
| 8008 | objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), false); | ||
| 8009 | } | ||
| 8010 | |||
| 8011 | void RGFW_window_show(RGFW_window* win) { | ||
| 8012 | ((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL); | ||
| 8013 | objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true); | ||
| 8014 | } | ||
| 8015 | |||
| 8016 | u8 RGFW_window_isFullscreen(RGFW_window* win) { | ||
| 8017 | assert(win != NULL); | ||
| 8018 | |||
| 8019 | NSWindowStyleMask mask = (NSWindowStyleMask) objc_msgSend_uint(win->src.window, sel_registerName("styleMask")); | ||
| 8020 | return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen; | ||
| 8021 | } | ||
| 8022 | |||
| 8023 | u8 RGFW_window_isHidden(RGFW_window* win) { | ||
| 8024 | assert(win != NULL); | ||
| 8025 | |||
| 8026 | bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible")); | ||
| 8027 | return visible == NO && !RGFW_window_isMinimized(win); | ||
| 8028 | } | ||
| 8029 | |||
| 8030 | u8 RGFW_window_isMinimized(RGFW_window* win) { | ||
| 8031 | assert(win != NULL); | ||
| 8032 | |||
| 8033 | return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES; | ||
| 8034 | } | ||
| 8035 | |||
| 8036 | u8 RGFW_window_isMaximized(RGFW_window* win) { | ||
| 8037 | assert(win != NULL); | ||
| 8038 | |||
| 8039 | return objc_msgSend_bool(win->src.window, sel_registerName("isZoomed")); | ||
| 8040 | } | ||
| 8041 | |||
| 8042 | static RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display) { | ||
| 8043 | RGFW_monitor monitor; | ||
| 8044 | |||
| 8045 | CGRect bounds = CGDisplayBounds(display); | ||
| 8046 | monitor.rect = RGFW_RECT((int) bounds.origin.x, (int) bounds.origin.y, (int) bounds.size.width, (int) bounds.size.height); | ||
| 8047 | |||
| 8048 | CGSize screenSizeMM = CGDisplayScreenSize(display); | ||
| 8049 | monitor.physW = screenSizeMM.width; | ||
| 8050 | monitor.physH = screenSizeMM.height; | ||
| 8051 | |||
| 8052 | monitor.scaleX = ((monitor.rect.w / (screenSizeMM.width / 25.4)) / 96) + 0.25; | ||
| 8053 | monitor.scaleY = ((monitor.rect.h / (screenSizeMM.height / 25.4)) / 96) + 0.25; | ||
| 8054 | |||
| 8055 | return monitor; | ||
| 8056 | } | ||
| 8057 | |||
| 8058 | |||
| 8059 | static RGFW_monitor RGFW_monitors[7]; | ||
| 8060 | |||
| 8061 | RGFW_monitor* RGFW_getMonitors(void) { | ||
| 8062 | static CGDirectDisplayID displays[7]; | ||
| 8063 | u32 count; | ||
| 8064 | |||
| 8065 | if (CGGetActiveDisplayList(6, displays, &count) != kCGErrorSuccess) | ||
| 8066 | return NULL; | ||
| 8067 | |||
| 8068 | for (u32 i = 0; i < count; i++) | ||
| 8069 | RGFW_monitors[i] = RGFW_NSCreateMonitor(displays[i]); | ||
| 8070 | |||
| 8071 | return RGFW_monitors; | ||
| 8072 | } | ||
| 8073 | |||
| 8074 | RGFW_monitor RGFW_getPrimaryMonitor(void) { | ||
| 8075 | CGDirectDisplayID primary = CGMainDisplayID(); | ||
| 8076 | return RGFW_NSCreateMonitor(primary); | ||
| 8077 | } | ||
| 8078 | |||
| 8079 | RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { | ||
| 8080 | return RGFW_NSCreateMonitor(win->src.display); | ||
| 8081 | } | ||
| 8082 | |||
| 8083 | char* RGFW_readClipboard(size_t* size) { | ||
| 8084 | char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString); | ||
| 8085 | |||
| 8086 | size_t clip_len = 1; | ||
| 8087 | |||
| 8088 | if (clip != NULL) { | ||
| 8089 | clip_len = strlen(clip) + 1; | ||
| 8090 | } | ||
| 8091 | |||
| 8092 | char* str = (char*)RGFW_MALLOC(sizeof(char) * clip_len); | ||
| 8093 | |||
| 8094 | if (clip != NULL) { | ||
| 8095 | strncpy(str, clip, clip_len); | ||
| 8096 | } | ||
| 8097 | |||
| 8098 | str[clip_len] = '\0'; | ||
| 8099 | |||
| 8100 | if (size != NULL) | ||
| 8101 | *size = clip_len; | ||
| 8102 | return str; | ||
| 8103 | } | ||
| 8104 | |||
| 8105 | void RGFW_writeClipboard(const char* text, u32 textLen) { | ||
| 8106 | RGFW_UNUSED(textLen); | ||
| 8107 | |||
| 8108 | NSPasteboardType array[] = { NSPasteboardTypeString, NULL }; | ||
| 8109 | NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL); | ||
| 8110 | |||
| 8111 | NSPasteBoard_setString(NSPasteboard_generalPasteboard(), text, NSPasteboardTypeString); | ||
| 8112 | } | ||
| 8113 | |||
| 8114 | u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) { | ||
| 8115 | RGFW_UNUSED(jsNumber); | ||
| 8116 | |||
| 8117 | assert(win != NULL); | ||
| 8118 | |||
| 8119 | return RGFW_registerJoystickF(win, (char*) ""); | ||
| 8120 | } | ||
| 8121 | |||
| 8122 | u16 RGFW_registerJoystickF(RGFW_window* win, char* file) { | ||
| 8123 | RGFW_UNUSED(file); | ||
| 8124 | |||
| 8125 | assert(win != NULL); | ||
| 8126 | |||
| 8127 | return RGFW_joystickCount - 1; | ||
| 8128 | } | ||
| 8129 | |||
| 8130 | #ifdef RGFW_OPENGL | ||
| 8131 | void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { | ||
| 8132 | assert(win != NULL); | ||
| 8133 | objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext")); | ||
| 8134 | } | ||
| 8135 | #endif | ||
| 8136 | |||
| 8137 | #if !defined(RGFW_EGL) | ||
| 8138 | void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { | ||
| 8139 | assert(win != NULL); | ||
| 8140 | #if defined(RGFW_OPENGL) | ||
| 8141 | |||
| 8142 | NSOpenGLContext_setValues(win->src.ctx, &swapInterval, 222); | ||
| 8143 | #else | ||
| 8144 | RGFW_UNUSED(swapInterval); | ||
| 8145 | #endif | ||
| 8146 | } | ||
| 8147 | #endif | ||
| 8148 | |||
| 8149 | // Function to create a CGImageRef from an array of bytes | ||
| 8150 | CGImageRef createImageFromBytes(unsigned char *buffer, int width, int height) | ||
| 8151 | { | ||
| 8152 | // Define color space | ||
| 8153 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); | ||
| 8154 | // Create bitmap context | ||
| 8155 | CGContextRef context = CGBitmapContextCreate( | ||
| 8156 | buffer, | ||
| 8157 | width, height, | ||
| 8158 | 8, | ||
| 8159 | RGFW_bufferSize.w * 4, | ||
| 8160 | colorSpace, | ||
| 8161 | kCGImageAlphaPremultipliedLast); | ||
| 8162 | // Create image from bitmap context | ||
| 8163 | CGImageRef image = CGBitmapContextCreateImage(context); | ||
| 8164 | // Release the color space and context | ||
| 8165 | CGColorSpaceRelease(colorSpace); | ||
| 8166 | CGContextRelease(context); | ||
| 8167 | |||
| 8168 | return image; | ||
| 8169 | } | ||
| 8170 | |||
| 8171 | void RGFW_window_swapBuffers(RGFW_window* win) { | ||
| 8172 | assert(win != NULL); | ||
| 8173 | /* clear the window*/ | ||
| 8174 | |||
| 8175 | if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) { | ||
| 8176 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 8177 | #ifdef RGFW_OSMESA | ||
| 8178 | RGFW_OSMesa_reorganize(); | ||
| 8179 | #endif | ||
| 8180 | |||
| 8181 | void* view = NSWindow_contentView(win->src.window); | ||
| 8182 | void* layer = objc_msgSend_id(view, sel_registerName("layer")); | ||
| 8183 | |||
| 8184 | ((void(*)(id, SEL, NSRect))objc_msgSend)(layer, | ||
| 8185 | sel_registerName("setFrame:"), | ||
| 8186 | (NSRect){{0, 0}, {win->r.w, win->r.h}}); | ||
| 8187 | |||
| 8188 | CGImageRef image = createImageFromBytes(win->buffer, win->r.w, win->r.h); | ||
| 8189 | // Get the current graphics context | ||
| 8190 | id graphicsContext = objc_msgSend_class(objc_getClass("NSGraphicsContext"), sel_registerName("currentContext")); | ||
| 8191 | // Get the CGContext from the current NSGraphicsContext | ||
| 8192 | id cgContext = objc_msgSend_id(graphicsContext, sel_registerName("graphicsPort")); | ||
| 8193 | // Draw the image in the context | ||
| 8194 | NSRect bounds = (NSRect){{0,0}, {win->r.w, win->r.h}}; | ||
| 8195 | CGContextDrawImage((void*)cgContext, *(CGRect*)&bounds, image); | ||
| 8196 | // Flush the graphics context to ensure the drawing is displayed | ||
| 8197 | objc_msgSend_id(graphicsContext, sel_registerName("flushGraphics")); | ||
| 8198 | |||
| 8199 | objc_msgSend_void_id(layer, sel_registerName("setContents:"), (id)image); | ||
| 8200 | objc_msgSend_id(layer, sel_registerName("setNeedsDisplay")); | ||
| 8201 | |||
| 8202 | CGImageRelease(image); | ||
| 8203 | #endif | ||
| 8204 | } | ||
| 8205 | |||
| 8206 | if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) { | ||
| 8207 | #ifdef RGFW_EGL | ||
| 8208 | eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); | ||
| 8209 | #elif defined(RGFW_OPENGL) | ||
| 8210 | objc_msgSend_void(win->src.ctx, sel_registerName("flushBuffer")); | ||
| 8211 | #endif | ||
| 8212 | } | ||
| 8213 | } | ||
| 8214 | |||
| 8215 | void RGFW_window_close(RGFW_window* win) { | ||
| 8216 | assert(win != NULL); | ||
| 8217 | release(win->src.view); | ||
| 8218 | |||
| 8219 | #ifdef RGFW_ALLOC_DROPFILES | ||
| 8220 | { | ||
| 8221 | u32 i; | ||
| 8222 | for (i = 0; i < RGFW_MAX_DROPS; i++) | ||
| 8223 | RGFW_FREE(win->event.droppedFiles[i]); | ||
| 8224 | |||
| 8225 | |||
| 8226 | RGFW_FREE(win->event.droppedFiles); | ||
| 8227 | } | ||
| 8228 | #endif | ||
| 8229 | |||
| 8230 | #ifdef RGFW_BUFFER | ||
| 8231 | release(win->src.bitmap); | ||
| 8232 | release(win->src.image); | ||
| 8233 | #endif | ||
| 8234 | |||
| 8235 | CVDisplayLinkStop(win->src.displayLink); | ||
| 8236 | CVDisplayLinkRelease(win->src.displayLink); | ||
| 8237 | |||
| 8238 | RGFW_FREE(win); | ||
| 8239 | } | ||
| 8240 | |||
| 8241 | u64 RGFW_getTimeNS(void) { | ||
| 8242 | static mach_timebase_info_data_t timebase_info; | ||
| 8243 | if (timebase_info.denom == 0) { | ||
| 8244 | mach_timebase_info(&timebase_info); | ||
| 8245 | } | ||
| 8246 | return mach_absolute_time() * timebase_info.numer / timebase_info.denom; | ||
| 8247 | } | ||
| 8248 | |||
| 8249 | u64 RGFW_getTime(void) { | ||
| 8250 | static mach_timebase_info_data_t timebase_info; | ||
| 8251 | if (timebase_info.denom == 0) { | ||
| 8252 | mach_timebase_info(&timebase_info); | ||
| 8253 | } | ||
| 8254 | return (double) mach_absolute_time() * (double) timebase_info.numer / ((double) timebase_info.denom * 1e9); | ||
| 8255 | } | ||
| 8256 | #endif /* RGFW_MACOS */ | ||
| 8257 | |||
| 8258 | /* | ||
| 8259 | End of MaOS defines | ||
| 8260 | */ | ||
| 8261 | |||
| 8262 | /* | ||
| 8263 | WEBASM defines | ||
| 8264 | */ | ||
| 8265 | |||
| 8266 | #ifdef RGFW_WEBASM | ||
| 8267 | RGFW_Event RGFW_events[20]; | ||
| 8268 | size_t RGFW_eventLen = 0; | ||
| 8269 | |||
| 8270 | EM_BOOL Emscripten_on_keydown(int eventType, const EmscriptenKeyboardEvent* e, void* userData) { | ||
| 8271 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); | ||
| 8272 | |||
| 8273 | RGFW_events[RGFW_eventLen].type = RGFW_keyPressed; | ||
| 8274 | memcpy(RGFW_events[RGFW_eventLen].keyName, e->key, 16); | ||
| 8275 | RGFW_events[RGFW_eventLen].keyCode = RGFW_apiKeyCodeToRGFW(e->keyCode); | ||
| 8276 | RGFW_events[RGFW_eventLen].lockState = 0; | ||
| 8277 | RGFW_eventLen++; | ||
| 8278 | |||
| 8279 | RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].prev = RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current; | ||
| 8280 | RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current = 1; | ||
| 8281 | RGFW_keyCallback(RGFW_root, RGFW_apiKeyCodeToRGFW(e->keyCode), RGFW_events[RGFW_eventLen].keyName, 0, 1); | ||
| 8282 | |||
| 8283 | return EM_TRUE; | ||
| 8284 | } | ||
| 8285 | |||
| 8286 | EM_BOOL Emscripten_on_keyup(int eventType, const EmscriptenKeyboardEvent* e, void* userData) { | ||
| 8287 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); | ||
| 8288 | |||
| 8289 | RGFW_events[RGFW_eventLen].type = RGFW_keyReleased; | ||
| 8290 | memcpy(RGFW_events[RGFW_eventLen].keyName, e->key, 16); | ||
| 8291 | RGFW_events[RGFW_eventLen].keyCode = RGFW_apiKeyCodeToRGFW(e->keyCode); | ||
| 8292 | RGFW_events[RGFW_eventLen].lockState = 0; | ||
| 8293 | RGFW_eventLen++; | ||
| 8294 | |||
| 8295 | RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].prev = RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current; | ||
| 8296 | RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current = 0; | ||
| 8297 | |||
| 8298 | RGFW_keyCallback(RGFW_root, RGFW_apiKeyCodeToRGFW(e->keyCode), RGFW_events[RGFW_eventLen].keyName, 0, 0); | ||
| 8299 | |||
| 8300 | return EM_TRUE; | ||
| 8301 | } | ||
| 8302 | |||
| 8303 | EM_BOOL Emscripten_on_resize(int eventType, const EmscriptenUiEvent* e, void* userData) { | ||
| 8304 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); | ||
| 8305 | |||
| 8306 | RGFW_events[RGFW_eventLen].type = RGFW_windowResized; | ||
| 8307 | RGFW_eventLen++; | ||
| 8308 | |||
| 8309 | RGFW_windowResizeCallback(RGFW_root, RGFW_RECT(0, 0, e->windowInnerWidth, e->windowInnerHeight)); | ||
| 8310 | return EM_TRUE; | ||
| 8311 | } | ||
| 8312 | |||
| 8313 | EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent* e, void* userData) { | ||
| 8314 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); | ||
| 8315 | |||
| 8316 | RGFW_events[RGFW_eventLen].type = RGFW_windowResized; | ||
| 8317 | RGFW_eventLen++; | ||
| 8318 | |||
| 8319 | RGFW_root->r = RGFW_RECT(0, 0, e->elementWidth, e->elementHeight); | ||
| 8320 | RGFW_windowResizeCallback(RGFW_root, RGFW_root->r); | ||
| 8321 | return EM_TRUE; | ||
| 8322 | } | ||
| 8323 | |||
| 8324 | EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* e, void* userData) { | ||
| 8325 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); | ||
| 8326 | |||
| 8327 | RGFW_events[RGFW_eventLen].type = RGFW_focusIn; | ||
| 8328 | RGFW_eventLen++; | ||
| 8329 | |||
| 8330 | RGFW_root->event.inFocus = 1; | ||
| 8331 | RGFW_focusCallback(RGFW_root, 1); | ||
| 8332 | return EM_TRUE; | ||
| 8333 | } | ||
| 8334 | |||
| 8335 | EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* e, void* userData) { | ||
| 8336 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); | ||
| 8337 | |||
| 8338 | RGFW_events[RGFW_eventLen].type = RGFW_focusOut; | ||
| 8339 | RGFW_eventLen++; | ||
| 8340 | |||
| 8341 | RGFW_root->event.inFocus = 0; | ||
| 8342 | RGFW_focusCallback(RGFW_root, 0); | ||
| 8343 | return EM_TRUE; | ||
| 8344 | } | ||
| 8345 | |||
| 8346 | EM_BOOL Emscripten_on_mousemove(int eventType, const EmscriptenMouseEvent* e, void* userData) { | ||
| 8347 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); | ||
| 8348 | |||
| 8349 | RGFW_events[RGFW_eventLen].type = RGFW_mousePosChanged; | ||
| 8350 | |||
| 8351 | if ((RGFW_root->_winArgs & RGFW_HOLD_MOUSE)) { | ||
| 8352 | RGFW_point p = RGFW_POINT(e->movementX, e->movementY); | ||
| 8353 | RGFW_events[RGFW_eventLen].point = p; | ||
| 8354 | } | ||
| 8355 | else | ||
| 8356 | RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->targetX, e->targetY); | ||
| 8357 | RGFW_eventLen++; | ||
| 8358 | |||
| 8359 | RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point); | ||
| 8360 | return EM_TRUE; | ||
| 8361 | } | ||
| 8362 | |||
| 8363 | EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* e, void* userData) { | ||
| 8364 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); | ||
| 8365 | |||
| 8366 | RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed; | ||
| 8367 | RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->targetX, e->targetY); | ||
| 8368 | RGFW_events[RGFW_eventLen].button = e->button + 1; | ||
| 8369 | RGFW_events[RGFW_eventLen].scroll = 0; | ||
| 8370 | |||
| 8371 | RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; | ||
| 8372 | RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1; | ||
| 8373 | |||
| 8374 | RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1); | ||
| 8375 | RGFW_eventLen++; | ||
| 8376 | |||
| 8377 | return EM_TRUE; | ||
| 8378 | } | ||
| 8379 | |||
| 8380 | EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* e, void* userData) { | ||
| 8381 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); | ||
| 8382 | |||
| 8383 | RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonReleased; | ||
| 8384 | RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->targetX, e->targetY); | ||
| 8385 | RGFW_events[RGFW_eventLen].button = e->button + 1; | ||
| 8386 | RGFW_events[RGFW_eventLen].scroll = 0; | ||
| 8387 | |||
| 8388 | RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; | ||
| 8389 | RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 0; | ||
| 8390 | |||
| 8391 | RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 0); | ||
| 8392 | RGFW_eventLen++; | ||
| 8393 | return EM_TRUE; | ||
| 8394 | } | ||
| 8395 | |||
| 8396 | EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* e, void* userData) { | ||
| 8397 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); | ||
| 8398 | |||
| 8399 | RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed; | ||
| 8400 | RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->mouse.targetX, e->mouse.targetY); | ||
| 8401 | RGFW_events[RGFW_eventLen].button = RGFW_mouseScrollUp + (e->deltaY < 0); | ||
| 8402 | RGFW_events[RGFW_eventLen].scroll = e->deltaY; | ||
| 8403 | |||
| 8404 | RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; | ||
| 8405 | RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1; | ||
| 8406 | |||
| 8407 | RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1); | ||
| 8408 | RGFW_eventLen++; | ||
| 8409 | |||
| 8410 | return EM_TRUE; | ||
| 8411 | } | ||
| 8412 | |||
| 8413 | EM_BOOL Emscripten_on_touchstart(int eventType, const EmscriptenTouchEvent* e, void* userData) { | ||
| 8414 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); | ||
| 8415 | |||
| 8416 | size_t i; | ||
| 8417 | for (i = 0; i < (size_t)e->numTouches; i++) { | ||
| 8418 | RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed; | ||
| 8419 | RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); | ||
| 8420 | RGFW_events[RGFW_eventLen].button = 1; | ||
| 8421 | RGFW_events[RGFW_eventLen].scroll = 0; | ||
| 8422 | |||
| 8423 | |||
| 8424 | RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; | ||
| 8425 | RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1; | ||
| 8426 | |||
| 8427 | RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point); | ||
| 8428 | |||
| 8429 | RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1); | ||
| 8430 | RGFW_eventLen++; | ||
| 8431 | } | ||
| 8432 | |||
| 8433 | return EM_TRUE; | ||
| 8434 | } | ||
| 8435 | EM_BOOL Emscripten_on_touchmove(int eventType, const EmscriptenTouchEvent* e, void* userData) { | ||
| 8436 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); | ||
| 8437 | |||
| 8438 | size_t i; | ||
| 8439 | for (i = 0; i < (size_t)e->numTouches; i++) { | ||
| 8440 | RGFW_events[RGFW_eventLen].type = RGFW_mousePosChanged; | ||
| 8441 | RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); | ||
| 8442 | |||
| 8443 | RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point); | ||
| 8444 | RGFW_eventLen++; | ||
| 8445 | } | ||
| 8446 | return EM_TRUE; | ||
| 8447 | } | ||
| 8448 | |||
| 8449 | EM_BOOL Emscripten_on_touchend(int eventType, const EmscriptenTouchEvent* e, void* userData) { | ||
| 8450 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); | ||
| 8451 | |||
| 8452 | size_t i; | ||
| 8453 | for (i = 0; i < (size_t)e->numTouches; i++) { | ||
| 8454 | RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonReleased; | ||
| 8455 | RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); | ||
| 8456 | RGFW_events[RGFW_eventLen].button = 1; | ||
| 8457 | RGFW_events[RGFW_eventLen].scroll = 0; | ||
| 8458 | |||
| 8459 | RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; | ||
| 8460 | RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 0; | ||
| 8461 | |||
| 8462 | RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 0); | ||
| 8463 | RGFW_eventLen++; | ||
| 8464 | } | ||
| 8465 | return EM_TRUE; | ||
| 8466 | } | ||
| 8467 | |||
| 8468 | EM_BOOL Emscripten_on_touchcancel(int eventType, const EmscriptenTouchEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); return EM_TRUE; } | ||
| 8469 | |||
| 8470 | EM_BOOL Emscripten_on_gamepad(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) { | ||
| 8471 | RGFW_UNUSED(eventType); RGFW_UNUSED(userData); | ||
| 8472 | |||
| 8473 | if (gamepadEvent->index >= 4) | ||
| 8474 | return 0; | ||
| 8475 | |||
| 8476 | RGFW_joysticks[gamepadEvent->index] = gamepadEvent->connected; | ||
| 8477 | |||
| 8478 | return 1; // The event was consumed by the callback handler | ||
| 8479 | } | ||
| 8480 | |||
| 8481 | void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) { | ||
| 8482 | if (!(RGFW_root->_winArgs & RGFW_ALLOW_DND)) | ||
| 8483 | return; | ||
| 8484 | |||
| 8485 | RGFW_events[RGFW_eventLen].droppedFilesCount = count; | ||
| 8486 | RGFW_dndCallback(RGFW_root, RGFW_events[RGFW_eventLen].droppedFiles, count); | ||
| 8487 | RGFW_eventLen++; | ||
| 8488 | } | ||
| 8489 | |||
| 8490 | b8 RGFW_stopCheckEvents_bool = RGFW_FALSE; | ||
| 8491 | void RGFW_stopCheckEvents(void) { | ||
| 8492 | RGFW_stopCheckEvents_bool = RGFW_TRUE; | ||
| 8493 | } | ||
| 8494 | |||
| 8495 | void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { | ||
| 8496 | RGFW_UNUSED(win); | ||
| 8497 | |||
| 8498 | if (waitMS == 0) | ||
| 8499 | return; | ||
| 8500 | |||
| 8501 | u32 start = (u32)(((u64)RGFW_getTimeNS()) / 1e+6); | ||
| 8502 | |||
| 8503 | while ((RGFW_eventLen == 0) && RGFW_stopCheckEvents_bool == RGFW_FALSE && | ||
| 8504 | (waitMS < 0 || (RGFW_getTimeNS() / 1e+6) - start < waitMS) | ||
| 8505 | ) { | ||
| 8506 | emscripten_sleep(0); | ||
| 8507 | } | ||
| 8508 | |||
| 8509 | RGFW_stopCheckEvents_bool = RGFW_FALSE; | ||
| 8510 | } | ||
| 8511 | |||
| 8512 | RGFWDEF void RGFW_init_buffer(RGFW_window* win); | ||
| 8513 | void RGFW_init_buffer(RGFW_window* win) { | ||
| 8514 | #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) | ||
| 8515 | if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) | ||
| 8516 | RGFW_bufferSize = RGFW_getScreenSize(); | ||
| 8517 | |||
| 8518 | win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); | ||
| 8519 | #ifdef RGFW_OSMESA | ||
| 8520 | win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); | ||
| 8521 | OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); | ||
| 8522 | #endif | ||
| 8523 | #else | ||
| 8524 | RGFW_UNUSED(win); /*!< if buffer rendering is not being used */ | ||
| 8525 | #endif | ||
| 8526 | } | ||
| 8527 | |||
| 8528 | void EMSCRIPTEN_KEEPALIVE RGFW_makeSetValue(size_t index, char* file) { | ||
| 8529 | /* This seems like a terrible idea, don't replicate this unless you hate yourself or the OS */ | ||
| 8530 | /* TODO: find a better way to do this, | ||
| 8531 | strcpy doesn't seem to work, maybe because of asyncio | ||
| 8532 | */ | ||
| 8533 | |||
| 8534 | RGFW_events[RGFW_eventLen].type = RGFW_dnd; | ||
| 8535 | char** arr = (char**)&RGFW_events[RGFW_eventLen].droppedFiles[index]; | ||
| 8536 | *arr = file; | ||
| 8537 | } | ||
| 8538 | |||
| 8539 | #include <sys/stat.h> | ||
| 8540 | #include <sys/types.h> | ||
| 8541 | #include <errno.h> | ||
| 8542 | |||
| 8543 | void EMSCRIPTEN_KEEPALIVE RGFW_mkdir(char* name) { mkdir(name, 0755); } | ||
| 8544 | |||
| 8545 | void EMSCRIPTEN_KEEPALIVE RGFW_writeFile(const char *path, const char *data, size_t len) { | ||
| 8546 | FILE* file = fopen(path, "w+"); | ||
| 8547 | if (file == NULL) | ||
| 8548 | return; | ||
| 8549 | |||
| 8550 | fwrite(data, sizeof(char), len, file); | ||
| 8551 | fclose(file); | ||
| 8552 | } | ||
| 8553 | |||
| 8554 | RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { | ||
| 8555 | RGFW_UNUSED(name) | ||
| 8556 | |||
| 8557 | RGFW_UNUSED(RGFW_initFormatAttribs); | ||
| 8558 | |||
| 8559 | RGFW_window* win = RGFW_window_basic_init(rect, args); | ||
| 8560 | |||
| 8561 | #ifndef RGFW_WEBGPU | ||
| 8562 | EmscriptenWebGLContextAttributes attrs; | ||
| 8563 | attrs.alpha = EM_TRUE; | ||
| 8564 | attrs.depth = EM_TRUE; | ||
| 8565 | attrs.alpha = EM_TRUE; | ||
| 8566 | attrs.stencil = RGFW_STENCIL; | ||
| 8567 | attrs.antialias = RGFW_SAMPLES; | ||
| 8568 | attrs.premultipliedAlpha = EM_TRUE; | ||
| 8569 | attrs.preserveDrawingBuffer = EM_FALSE; | ||
| 8570 | |||
| 8571 | if (RGFW_DOUBLE_BUFFER == 0) | ||
| 8572 | attrs.renderViaOffscreenBackBuffer = 0; | ||
| 8573 | else | ||
| 8574 | attrs.renderViaOffscreenBackBuffer = RGFW_AUX_BUFFERS; | ||
| 8575 | |||
| 8576 | attrs.failIfMajorPerformanceCaveat = EM_FALSE; | ||
| 8577 | attrs.majorVersion = (RGFW_majorVersion == 0) ? 1 : RGFW_majorVersion; | ||
| 8578 | attrs.minorVersion = RGFW_minorVersion; | ||
| 8579 | |||
| 8580 | attrs.enableExtensionsByDefault = EM_TRUE; | ||
| 8581 | attrs.explicitSwapControl = EM_TRUE; | ||
| 8582 | |||
| 8583 | emscripten_webgl_init_context_attributes(&attrs); | ||
| 8584 | win->src.ctx = emscripten_webgl_create_context("#canvas", &attrs); | ||
| 8585 | emscripten_webgl_make_context_current(win->src.ctx); | ||
| 8586 | |||
| 8587 | #ifdef LEGACY_GL_EMULATION | ||
| 8588 | EM_ASM("Module.useWebGL = true; GLImmediate.init();"); | ||
| 8589 | #endif | ||
| 8590 | #else | ||
| 8591 | win->src.ctx = wgpuCreateInstance(NULL); | ||
| 8592 | win->src.device = emscripten_webgpu_get_device(); | ||
| 8593 | win->src.queue = wgpuDeviceGetQueue(win->src.device); | ||
| 8594 | #endif | ||
| 8595 | |||
| 8596 | emscripten_set_canvas_element_size("#canvas", rect.w, rect.h); | ||
| 8597 | emscripten_set_window_title(name); | ||
| 8598 | |||
| 8599 | /* load callbacks */ | ||
| 8600 | emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_keydown); | ||
| 8601 | emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_keyup); | ||
| 8602 | emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_resize); | ||
| 8603 | emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, EM_FALSE, Emscripten_on_fullscreenchange); | ||
| 8604 | emscripten_set_mousemove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousemove); | ||
| 8605 | emscripten_set_touchstart_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchstart); | ||
| 8606 | emscripten_set_touchend_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchend); | ||
| 8607 | emscripten_set_touchmove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchmove); | ||
| 8608 | emscripten_set_touchcancel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchcancel); | ||
| 8609 | emscripten_set_mousedown_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousedown); | ||
| 8610 | emscripten_set_mouseup_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mouseup); | ||
| 8611 | emscripten_set_wheel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_wheel); | ||
| 8612 | emscripten_set_focusin_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusin); | ||
| 8613 | emscripten_set_focusout_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusout); | ||
| 8614 | emscripten_set_gamepadconnected_callback(NULL, 1, Emscripten_on_gamepad); | ||
| 8615 | emscripten_set_gamepaddisconnected_callback(NULL, 1, Emscripten_on_gamepad); | ||
| 8616 | |||
| 8617 | if (args & RGFW_ALLOW_DND) { | ||
| 8618 | win->_winArgs |= RGFW_ALLOW_DND; | ||
| 8619 | } | ||
| 8620 | |||
| 8621 | EM_ASM({ | ||
| 8622 | var canvas = document.getElementById('canvas'); | ||
| 8623 | canvas.addEventListener('drop', function(e) { | ||
| 8624 | e.preventDefault(); | ||
| 8625 | if (e.dataTransfer.file < 0) | ||
| 8626 | return; | ||
| 8627 | |||
| 8628 | var filenamesArray = []; | ||
| 8629 | var count = e.dataTransfer.files.length; | ||
| 8630 | |||
| 8631 | /* Read and save the files to emscripten's files */ | ||
| 8632 | var drop_dir = '.rgfw_dropped_files'; | ||
| 8633 | Module._RGFW_mkdir(drop_dir); | ||
| 8634 | |||
| 8635 | for (var i = 0; i < count; i++) { | ||
| 8636 | var file = e.dataTransfer.files[i]; | ||
| 8637 | |||
| 8638 | var path = '/' + drop_dir + '/' + file.name.replace("//", '_'); | ||
| 8639 | var reader = new FileReader(); | ||
| 8640 | |||
| 8641 | reader.onloadend = (e) => { | ||
| 8642 | if (reader.readyState != 2) { | ||
| 8643 | out('failed to read dropped file: '+file.name+': '+reader.error); | ||
| 8644 | } | ||
| 8645 | else { | ||
| 8646 | var data = e.target.result; | ||
| 8647 | |||
| 8648 | _RGFW_writeFile(path, new Uint8Array(data), file.size); | ||
| 8649 | } | ||
| 8650 | }; | ||
| 8651 | |||
| 8652 | reader.readAsArrayBuffer(file); | ||
| 8653 | // This works weird on modern opengl | ||
| 8654 | var filename = stringToNewUTF8(path); | ||
| 8655 | |||
| 8656 | filenamesArray.push(filename); | ||
| 8657 | |||
| 8658 | Module._RGFW_makeSetValue(i, filename); | ||
| 8659 | } | ||
| 8660 | |||
| 8661 | Module._Emscripten_onDrop(count); | ||
| 8662 | |||
| 8663 | for (var i = 0; i < count; ++i) { | ||
| 8664 | _free(filenamesArray[i]); | ||
| 8665 | } | ||
| 8666 | }, true); | ||
| 8667 | |||
| 8668 | canvas.addEventListener('dragover', function(e) { e.preventDefault(); return false; }, true); | ||
| 8669 | }); | ||
| 8670 | |||
| 8671 | RGFW_init_buffer(win); | ||
| 8672 | glViewport(0, 0, rect.w, rect.h); | ||
| 8673 | |||
| 8674 | RGFW_root = win; | ||
| 8675 | |||
| 8676 | if (args & RGFW_HIDE_MOUSE) { | ||
| 8677 | RGFW_window_showMouse(win, 0); | ||
| 8678 | } | ||
| 8679 | |||
| 8680 | if (args & RGFW_FULLSCREEN) { | ||
| 8681 | RGFW_window_resize(win, RGFW_getScreenSize()); | ||
| 8682 | } | ||
| 8683 | |||
| 8684 | return win; | ||
| 8685 | } | ||
| 8686 | |||
| 8687 | RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { | ||
| 8688 | static u8 index = 0; | ||
| 8689 | |||
| 8690 | if (index == 0) | ||
| 8691 | RGFW_resetKey(); | ||
| 8692 | |||
| 8693 | /* check gamepads */ | ||
| 8694 | for (int i = 0; (i < emscripten_get_num_gamepads()) && (i < 4); i++) { | ||
| 8695 | if (RGFW_joysticks[i] == 0) | ||
| 8696 | continue;; | ||
| 8697 | |||
| 8698 | EmscriptenGamepadEvent gamepadState; | ||
| 8699 | |||
| 8700 | if (emscripten_get_gamepad_status(i, &gamepadState) != EMSCRIPTEN_RESULT_SUCCESS) | ||
| 8701 | break; | ||
| 8702 | |||
| 8703 | // Register buttons data for every connected gamepad | ||
| 8704 | for (int j = 0; (j < gamepadState.numButtons) && (j < 16); j++) { | ||
| 8705 | u32 map[] = { | ||
| 8706 | RGFW_JS_A, RGFW_JS_X, RGFW_JS_B, RGFW_JS_Y, | ||
| 8707 | RGFW_JS_L1, RGFW_JS_R1, RGFW_JS_L2, RGFW_JS_R2, | ||
| 8708 | RGFW_JS_SELECT, RGFW_JS_START, | ||
| 8709 | 0, 0, | ||
| 8710 | RGFW_JS_UP, RGFW_JS_DOWN, RGFW_JS_LEFT, RGFW_JS_RIGHT | ||
| 8711 | }; | ||
| 8712 | |||
| 8713 | u32 button = map[j]; | ||
| 8714 | if (RGFW_jsPressed[i][button] != gamepadState.digitalButton[j]) { | ||
| 8715 | win->event.type = RGFW_jsButtonPressed; | ||
| 8716 | win->event.joystick = i; | ||
| 8717 | win->event.button = map[j]; | ||
| 8718 | return &win->event; | ||
| 8719 | } | ||
| 8720 | |||
| 8721 | RGFW_jsPressed[i][button] = gamepadState.digitalButton[j]; | ||
| 8722 | } | ||
| 8723 | |||
| 8724 | for (int j = 0; (j < gamepadState.numAxes) && (j < 4); j += 2) { | ||
| 8725 | win->event.axisesCount = gamepadState.numAxes; | ||
| 8726 | if (win->event.axis[j].x != gamepadState.axis[j] || | ||
| 8727 | win->event.axis[j].y != gamepadState.axis[j + 1] | ||
| 8728 | ) { | ||
| 8729 | win->event.axis[j].x = gamepadState.axis[j]; | ||
| 8730 | win->event.axis[j].y = gamepadState.axis[j + 1]; | ||
| 8731 | win->event.type = RGFW_jsAxisMove; | ||
| 8732 | win->event.joystick = i; | ||
| 8733 | return &win->event; | ||
| 8734 | } | ||
| 8735 | } | ||
| 8736 | } | ||
| 8737 | |||
| 8738 | /* check queued events */ | ||
| 8739 | if (RGFW_eventLen == 0) | ||
| 8740 | return NULL; | ||
| 8741 | |||
| 8742 | RGFW_events[index].frameTime = win->event.frameTime; | ||
| 8743 | RGFW_events[index].frameTime2 = win->event.frameTime2; | ||
| 8744 | RGFW_events[index].inFocus = win->event.inFocus; | ||
| 8745 | |||
| 8746 | win->event = RGFW_events[index]; | ||
| 8747 | |||
| 8748 | RGFW_eventLen--; | ||
| 8749 | |||
| 8750 | if (RGFW_eventLen) | ||
| 8751 | index++; | ||
| 8752 | else | ||
| 8753 | index = 0; | ||
| 8754 | |||
| 8755 | return &win->event; | ||
| 8756 | } | ||
| 8757 | |||
| 8758 | void RGFW_window_resize(RGFW_window* win, RGFW_area a) { | ||
| 8759 | RGFW_UNUSED(win) | ||
| 8760 | emscripten_set_canvas_element_size("#canvas", a.w, a.h); | ||
| 8761 | } | ||
| 8762 | |||
| 8763 | /* NOTE: I don't know if this is possible */ | ||
| 8764 | void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win); RGFW_UNUSED(v); } | ||
| 8765 | /* this one might be possible but it looks iffy */ | ||
| 8766 | void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { RGFW_UNUSED(win); RGFW_UNUSED(channels) RGFW_UNUSED(a) RGFW_UNUSED(image) } | ||
| 8767 | |||
| 8768 | const char RGFW_CURSORS[11][12] = { | ||
| 8769 | "default", | ||
| 8770 | "default", | ||
| 8771 | "text", | ||
| 8772 | "crosshair", | ||
| 8773 | "pointer", | ||
| 8774 | "ew-resize", | ||
| 8775 | "ns-resize", | ||
| 8776 | "nwse-resize", | ||
| 8777 | "nesw-resize", | ||
| 8778 | "move", | ||
| 8779 | "not-allowed" | ||
| 8780 | }; | ||
| 8781 | |||
| 8782 | void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { | ||
| 8783 | RGFW_UNUSED(win) | ||
| 8784 | EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, RGFW_CURSORS[mouse]); | ||
| 8785 | } | ||
| 8786 | |||
| 8787 | void RGFW_window_setMouseDefault(RGFW_window* win) { | ||
| 8788 | RGFW_window_setMouseStandard(win, RGFW_MOUSE_NORMAL); | ||
| 8789 | } | ||
| 8790 | |||
| 8791 | void RGFW_window_showMouse(RGFW_window* win, i8 show) { | ||
| 8792 | if (show) | ||
| 8793 | RGFW_window_setMouseDefault(win); | ||
| 8794 | else | ||
| 8795 | EM_ASM(document.getElementById('canvas').style.cursor = 'none';); | ||
| 8796 | } | ||
| 8797 | |||
| 8798 | RGFW_point RGFW_getGlobalMousePoint(void) { | ||
| 8799 | RGFW_point point; | ||
| 8800 | point.x = EM_ASM_INT({ | ||
| 8801 | return window.mouseX || 0; | ||
| 8802 | }); | ||
| 8803 | point.y = EM_ASM_INT({ | ||
| 8804 | return window.mouseY || 0; | ||
| 8805 | }); | ||
| 8806 | return point; | ||
| 8807 | } | ||
| 8808 | |||
| 8809 | RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { | ||
| 8810 | RGFW_UNUSED(win); | ||
| 8811 | |||
| 8812 | EmscriptenMouseEvent mouseEvent; | ||
| 8813 | emscripten_get_mouse_status(&mouseEvent); | ||
| 8814 | return RGFW_POINT( mouseEvent.targetX, mouseEvent.targetY); | ||
| 8815 | } | ||
| 8816 | |||
| 8817 | void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { | ||
| 8818 | RGFW_UNUSED(win); | ||
| 8819 | |||
| 8820 | EM_ASM_({ | ||
| 8821 | var canvas = document.getElementById('canvas'); | ||
| 8822 | if ($0) { | ||
| 8823 | canvas.style.pointerEvents = 'none'; | ||
| 8824 | } else { | ||
| 8825 | canvas.style.pointerEvents = 'auto'; | ||
| 8826 | } | ||
| 8827 | }, passthrough); | ||
| 8828 | } | ||
| 8829 | |||
| 8830 | void RGFW_writeClipboard(const char* text, u32 textLen) { | ||
| 8831 | RGFW_UNUSED(textLen) | ||
| 8832 | EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text); | ||
| 8833 | } | ||
| 8834 | |||
| 8835 | |||
| 8836 | char* RGFW_readClipboard(size_t* size) { | ||
| 8837 | /* | ||
| 8838 | placeholder code for later | ||
| 8839 | I'm not sure if this is possible do the the async stuff | ||
| 8840 | */ | ||
| 8841 | |||
| 8842 | if (size != NULL) | ||
| 8843 | *size = 0; | ||
| 8844 | |||
| 8845 | char* str = (char*)malloc(1); | ||
| 8846 | str[0] = '\0'; | ||
| 8847 | |||
| 8848 | return str; | ||
| 8849 | } | ||
| 8850 | |||
| 8851 | void RGFW_window_swapBuffers(RGFW_window* win) { | ||
| 8852 | RGFW_UNUSED(win); | ||
| 8853 | |||
| 8854 | #ifdef RGFW_BUFFER | ||
| 8855 | if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) { | ||
| 8856 | glEnable(GL_TEXTURE_2D); | ||
| 8857 | |||
| 8858 | GLuint texture; | ||
| 8859 | glGenTextures(1,&texture); | ||
| 8860 | |||
| 8861 | glBindTexture(GL_TEXTURE_2D,texture); | ||
| 8862 | |||
| 8863 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
| 8864 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
| 8865 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
| 8866 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
| 8867 | |||
| 8868 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, RGFW_bufferSize.w, RGFW_bufferSize.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, win->buffer); | ||
| 8869 | |||
| 8870 | float ratioX = ((float)win->r.w / (float)RGFW_bufferSize.w); | ||
| 8871 | float ratioY = ((float)win->r.h / (float)RGFW_bufferSize.h); | ||
| 8872 | |||
| 8873 | // Set up the viewport | ||
| 8874 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 8875 | |||
| 8876 | glBegin(GL_TRIANGLES); | ||
| 8877 | glTexCoord2f(0, ratioY); glColor3f(1, 1, 1); glVertex2f(-1, -1); | ||
| 8878 | glTexCoord2f(0, 0); glColor3f(1, 1, 1); glVertex2f(-1, 1); | ||
| 8879 | glTexCoord2f(ratioX, ratioY); glColor3f(1, 1, 1); glVertex2f(1, -1); | ||
| 8880 | |||
| 8881 | glTexCoord2f(ratioX, 0); glColor3f(1, 1, 1); glVertex2f(1, 1); | ||
| 8882 | glTexCoord2f(ratioX, ratioY); glColor3f(1, 1, 1); glVertex2f(1, -1); | ||
| 8883 | glTexCoord2f(0, 0); glColor3f(1, 1, 1); glVertex2f(-1, 1); | ||
| 8884 | glEnd(); | ||
| 8885 | |||
| 8886 | glDeleteTextures(1, &texture); | ||
| 8887 | } | ||
| 8888 | #endif | ||
| 8889 | |||
| 8890 | #ifndef RGFW_WEBGPU | ||
| 8891 | emscripten_webgl_commit_frame(); | ||
| 8892 | #endif | ||
| 8893 | emscripten_sleep(0); | ||
| 8894 | } | ||
| 8895 | |||
| 8896 | |||
| 8897 | void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { | ||
| 8898 | #ifndef RGFW_WEBGPU | ||
| 8899 | if (win == NULL) | ||
| 8900 | emscripten_webgl_make_context_current(0); | ||
| 8901 | else | ||
| 8902 | emscripten_webgl_make_context_current(win->src.ctx); | ||
| 8903 | #endif | ||
| 8904 | } | ||
| 8905 | |||
| 8906 | #ifndef RGFW_EGL | ||
| 8907 | void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); } | ||
| 8908 | #endif | ||
| 8909 | |||
| 8910 | void RGFW_window_close(RGFW_window* win) { | ||
| 8911 | #ifndef RGFW_WEBGPU | ||
| 8912 | emscripten_webgl_destroy_context(win->src.ctx); | ||
| 8913 | #endif | ||
| 8914 | |||
| 8915 | free(win); | ||
| 8916 | } | ||
| 8917 | |||
| 8918 | int RGFW_innerWidth(void) { return EM_ASM_INT({ return window.innerWidth; }); } | ||
| 8919 | int RGFW_innerHeight(void) { return EM_ASM_INT({ return window.innerHeight; }); } | ||
| 8920 | |||
| 8921 | RGFW_area RGFW_getScreenSize(void) { | ||
| 8922 | return RGFW_AREA(RGFW_innerWidth(), RGFW_innerHeight()); | ||
| 8923 | } | ||
| 8924 | |||
| 8925 | void* RGFW_getProcAddress(const char* procname) { | ||
| 8926 | return emscripten_webgl_get_proc_address(procname); | ||
| 8927 | } | ||
| 8928 | |||
| 8929 | void RGFW_sleep(u64 milisecond) { | ||
| 8930 | emscripten_sleep(milisecond); | ||
| 8931 | } | ||
| 8932 | |||
| 8933 | u64 RGFW_getTimeNS(void) { | ||
| 8934 | return emscripten_get_now() * 1e+6; | ||
| 8935 | } | ||
| 8936 | |||
| 8937 | u64 RGFW_getTime(void) { | ||
| 8938 | return emscripten_get_now() * 1000; | ||
| 8939 | } | ||
| 8940 | |||
| 8941 | void RGFW_releaseCursor(RGFW_window* win) { | ||
| 8942 | RGFW_UNUSED(win); | ||
| 8943 | emscripten_exit_pointerlock(); | ||
| 8944 | } | ||
| 8945 | |||
| 8946 | void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { | ||
| 8947 | RGFW_UNUSED(win); RGFW_UNUSED(r); | ||
| 8948 | |||
| 8949 | emscripten_request_pointerlock("#canvas", 1); | ||
| 8950 | } | ||
| 8951 | |||
| 8952 | |||
| 8953 | void RGFW_window_setName(RGFW_window* win, char* name) { | ||
| 8954 | RGFW_UNUSED(win); | ||
| 8955 | emscripten_set_window_title(name); | ||
| 8956 | } | ||
| 8957 | |||
| 8958 | /* unsupported functions */ | ||
| 8959 | RGFW_monitor* RGFW_getMonitors(void) { return NULL; } | ||
| 8960 | RGFW_monitor RGFW_getPrimaryMonitor(void) { return (RGFW_monitor){}; } | ||
| 8961 | void RGFW_window_move(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win) RGFW_UNUSED(v) } | ||
| 8962 | void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win) RGFW_UNUSED(a) } | ||
| 8963 | void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win) RGFW_UNUSED(a) } | ||
| 8964 | void RGFW_window_minimize(RGFW_window* win) { RGFW_UNUSED(win)} | ||
| 8965 | void RGFW_window_restore(RGFW_window* win) { RGFW_UNUSED(win) } | ||
| 8966 | void RGFW_window_setBorder(RGFW_window* win, b8 border) { RGFW_UNUSED(win) RGFW_UNUSED(border) } | ||
| 8967 | void RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) { RGFW_UNUSED(win) RGFW_UNUSED(icon) RGFW_UNUSED(a) RGFW_UNUSED(channels) } | ||
| 8968 | void RGFW_window_hide(RGFW_window* win) { RGFW_UNUSED(win) } | ||
| 8969 | void RGFW_window_show(RGFW_window* win) {RGFW_UNUSED(win) } | ||
| 8970 | b8 RGFW_window_isHidden(RGFW_window* win) { RGFW_UNUSED(win) return 0; } | ||
| 8971 | b8 RGFW_window_isMinimized(RGFW_window* win) { RGFW_UNUSED(win) return 0; } | ||
| 8972 | b8 RGFW_window_isMaximized(RGFW_window* win) { RGFW_UNUSED(win) return 0; } | ||
| 8973 | RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { RGFW_UNUSED(win) return (RGFW_monitor){}; } | ||
| 8974 | |||
| 8975 | #endif | ||
| 8976 | |||
| 8977 | /* end of web asm defines */ | ||
| 8978 | |||
| 8979 | /* unix (macOS, linux, web asm) only stuff */ | ||
| 8980 | #if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WEBASM) || defined(RGFW_WAYLAND) | ||
| 8981 | /* unix threading */ | ||
| 8982 | #ifndef RGFW_NO_THREADS | ||
| 8983 | #include <pthread.h> | ||
| 8984 | |||
| 8985 | RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { | ||
| 8986 | RGFW_UNUSED(args); | ||
| 8987 | |||
| 8988 | RGFW_thread t; | ||
| 8989 | pthread_create((pthread_t*) &t, NULL, *ptr, NULL); | ||
| 8990 | return t; | ||
| 8991 | } | ||
| 8992 | void RGFW_cancelThread(RGFW_thread thread) { pthread_cancel((pthread_t) thread); } | ||
| 8993 | void RGFW_joinThread(RGFW_thread thread) { pthread_join((pthread_t) thread, NULL); } | ||
| 8994 | #ifdef __linux__ | ||
| 8995 | void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio((pthread_t)thread, priority); } | ||
| 8996 | #endif | ||
| 8997 | #endif | ||
| 8998 | |||
| 8999 | #ifndef RGFW_WEBASM | ||
| 9000 | /* unix sleep */ | ||
| 9001 | void RGFW_sleep(u64 ms) { | ||
| 9002 | struct timespec time; | ||
| 9003 | time.tv_sec = 0; | ||
| 9004 | time.tv_nsec = ms * 1e+6; | ||
| 9005 | |||
| 9006 | nanosleep(&time, NULL); | ||
| 9007 | } | ||
| 9008 | #endif | ||
| 9009 | |||
| 9010 | #endif /* end of unix / mac stuff*/ | ||
| 9011 | #endif /*RGFW_IMPLEMENTATION*/ | ||
| 9012 | |||
| 9013 | #if defined(__cplusplus) && !defined(__EMSCRIPTEN__) | ||
| 9014 | } | ||
| 9015 | #endif | ||
diff --git a/raylib/src/external/cgltf.h b/raylib/src/external/cgltf.h new file mode 100644 index 0000000..36fd644 --- /dev/null +++ b/raylib/src/external/cgltf.h | |||
| @@ -0,0 +1,7081 @@ | |||
| 1 | /** | ||
| 2 | * cgltf - a single-file glTF 2.0 parser written in C99. | ||
| 3 | * | ||
| 4 | * Version: 1.14 | ||
| 5 | * | ||
| 6 | * Website: https://github.com/jkuhlmann/cgltf | ||
| 7 | * | ||
| 8 | * Distributed under the MIT License, see notice at the end of this file. | ||
| 9 | * | ||
| 10 | * Building: | ||
| 11 | * Include this file where you need the struct and function | ||
| 12 | * declarations. Have exactly one source file where you define | ||
| 13 | * `CGLTF_IMPLEMENTATION` before including this file to get the | ||
| 14 | * function definitions. | ||
| 15 | * | ||
| 16 | * Reference: | ||
| 17 | * `cgltf_result cgltf_parse(const cgltf_options*, const void*, | ||
| 18 | * cgltf_size, cgltf_data**)` parses both glTF and GLB data. If | ||
| 19 | * this function returns `cgltf_result_success`, you have to call | ||
| 20 | * `cgltf_free()` on the created `cgltf_data*` variable. | ||
| 21 | * Note that contents of external files for buffers and images are not | ||
| 22 | * automatically loaded. You'll need to read these files yourself using | ||
| 23 | * URIs in the `cgltf_data` structure. | ||
| 24 | * | ||
| 25 | * `cgltf_options` is the struct passed to `cgltf_parse()` to control | ||
| 26 | * parts of the parsing process. You can use it to force the file type | ||
| 27 | * and provide memory allocation as well as file operation callbacks. | ||
| 28 | * Should be zero-initialized to trigger default behavior. | ||
| 29 | * | ||
| 30 | * `cgltf_data` is the struct allocated and filled by `cgltf_parse()`. | ||
| 31 | * It generally mirrors the glTF format as described by the spec (see | ||
| 32 | * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0). | ||
| 33 | * | ||
| 34 | * `void cgltf_free(cgltf_data*)` frees the allocated `cgltf_data` | ||
| 35 | * variable. | ||
| 36 | * | ||
| 37 | * `cgltf_result cgltf_load_buffers(const cgltf_options*, cgltf_data*, | ||
| 38 | * const char* gltf_path)` can be optionally called to open and read buffer | ||
| 39 | * files using the `FILE*` APIs. The `gltf_path` argument is the path to | ||
| 40 | * the original glTF file, which allows the parser to resolve the path to | ||
| 41 | * buffer files. | ||
| 42 | * | ||
| 43 | * `cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, | ||
| 44 | * cgltf_size size, const char* base64, void** out_data)` decodes | ||
| 45 | * base64-encoded data content. Used internally by `cgltf_load_buffers()`. | ||
| 46 | * This is useful when decoding data URIs in images. | ||
| 47 | * | ||
| 48 | * `cgltf_result cgltf_parse_file(const cgltf_options* options, const | ||
| 49 | * char* path, cgltf_data** out_data)` can be used to open the given | ||
| 50 | * file using `FILE*` APIs and parse the data using `cgltf_parse()`. | ||
| 51 | * | ||
| 52 | * `cgltf_result cgltf_validate(cgltf_data*)` can be used to do additional | ||
| 53 | * checks to make sure the parsed glTF data is valid. | ||
| 54 | * | ||
| 55 | * `cgltf_node_transform_local` converts the translation / rotation / scale properties of a node | ||
| 56 | * into a mat4. | ||
| 57 | * | ||
| 58 | * `cgltf_node_transform_world` calls `cgltf_node_transform_local` on every ancestor in order | ||
| 59 | * to compute the root-to-node transformation. | ||
| 60 | * | ||
| 61 | * `cgltf_accessor_unpack_floats` reads in the data from an accessor, applies sparse data (if any), | ||
| 62 | * and converts them to floating point. Assumes that `cgltf_load_buffers` has already been called. | ||
| 63 | * By passing null for the output pointer, users can find out how many floats are required in the | ||
| 64 | * output buffer. | ||
| 65 | * | ||
| 66 | * `cgltf_accessor_unpack_indices` reads in the index data from an accessor. Assumes that | ||
| 67 | * `cgltf_load_buffers` has already been called. By passing null for the output pointer, users can | ||
| 68 | * find out how many indices are required in the output buffer. Returns 0 if the accessor is | ||
| 69 | * sparse or if the output component size is less than the accessor's component size. | ||
| 70 | * | ||
| 71 | * `cgltf_num_components` is a tiny utility that tells you the dimensionality of | ||
| 72 | * a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate | ||
| 73 | * the necessary amount of memory. `cgltf_component_size` and `cgltf_calc_size` exist for | ||
| 74 | * similar purposes. | ||
| 75 | * | ||
| 76 | * `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to | ||
| 77 | * floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element | ||
| 78 | * size is the number of floats in the output buffer, which should be in the range [1, 16]. Returns | ||
| 79 | * false if the passed-in element_size is too small, or if the accessor is sparse. | ||
| 80 | * | ||
| 81 | * `cgltf_accessor_read_uint` is similar to its floating-point counterpart, but limited to reading | ||
| 82 | * vector types and does not support matrix types. The passed-in element size is the number of uints | ||
| 83 | * in the output buffer, which should be in the range [1, 4]. Returns false if the passed-in | ||
| 84 | * element_size is too small, or if the accessor is sparse. | ||
| 85 | * | ||
| 86 | * `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t | ||
| 87 | * and only works with single-component data types. | ||
| 88 | * | ||
| 89 | * `cgltf_copy_extras_json` allows users to retrieve the "extras" data that can be attached to many | ||
| 90 | * glTF objects (which can be arbitrary JSON data). This is a legacy function, consider using | ||
| 91 | * cgltf_extras::data directly instead. You can parse this data using your own JSON parser | ||
| 92 | * or, if you've included the cgltf implementation using the integrated JSMN JSON parser. | ||
| 93 | */ | ||
| 94 | #ifndef CGLTF_H_INCLUDED__ | ||
| 95 | #define CGLTF_H_INCLUDED__ | ||
| 96 | |||
| 97 | #include <stddef.h> | ||
| 98 | #include <stdint.h> /* For uint8_t, uint32_t */ | ||
| 99 | |||
| 100 | #ifdef __cplusplus | ||
| 101 | extern "C" { | ||
| 102 | #endif | ||
| 103 | |||
| 104 | typedef size_t cgltf_size; | ||
| 105 | typedef long long int cgltf_ssize; | ||
| 106 | typedef float cgltf_float; | ||
| 107 | typedef int cgltf_int; | ||
| 108 | typedef unsigned int cgltf_uint; | ||
| 109 | typedef int cgltf_bool; | ||
| 110 | |||
| 111 | typedef enum cgltf_file_type | ||
| 112 | { | ||
| 113 | cgltf_file_type_invalid, | ||
| 114 | cgltf_file_type_gltf, | ||
| 115 | cgltf_file_type_glb, | ||
| 116 | cgltf_file_type_max_enum | ||
| 117 | } cgltf_file_type; | ||
| 118 | |||
| 119 | typedef enum cgltf_result | ||
| 120 | { | ||
| 121 | cgltf_result_success, | ||
| 122 | cgltf_result_data_too_short, | ||
| 123 | cgltf_result_unknown_format, | ||
| 124 | cgltf_result_invalid_json, | ||
| 125 | cgltf_result_invalid_gltf, | ||
| 126 | cgltf_result_invalid_options, | ||
| 127 | cgltf_result_file_not_found, | ||
| 128 | cgltf_result_io_error, | ||
| 129 | cgltf_result_out_of_memory, | ||
| 130 | cgltf_result_legacy_gltf, | ||
| 131 | cgltf_result_max_enum | ||
| 132 | } cgltf_result; | ||
| 133 | |||
| 134 | typedef struct cgltf_memory_options | ||
| 135 | { | ||
| 136 | void* (*alloc_func)(void* user, cgltf_size size); | ||
| 137 | void (*free_func) (void* user, void* ptr); | ||
| 138 | void* user_data; | ||
| 139 | } cgltf_memory_options; | ||
| 140 | |||
| 141 | typedef struct cgltf_file_options | ||
| 142 | { | ||
| 143 | cgltf_result(*read)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data); | ||
| 144 | void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data); | ||
| 145 | void* user_data; | ||
| 146 | } cgltf_file_options; | ||
| 147 | |||
| 148 | typedef struct cgltf_options | ||
| 149 | { | ||
| 150 | cgltf_file_type type; /* invalid == auto detect */ | ||
| 151 | cgltf_size json_token_count; /* 0 == auto */ | ||
| 152 | cgltf_memory_options memory; | ||
| 153 | cgltf_file_options file; | ||
| 154 | } cgltf_options; | ||
| 155 | |||
| 156 | typedef enum cgltf_buffer_view_type | ||
| 157 | { | ||
| 158 | cgltf_buffer_view_type_invalid, | ||
| 159 | cgltf_buffer_view_type_indices, | ||
| 160 | cgltf_buffer_view_type_vertices, | ||
| 161 | cgltf_buffer_view_type_max_enum | ||
| 162 | } cgltf_buffer_view_type; | ||
| 163 | |||
| 164 | typedef enum cgltf_attribute_type | ||
| 165 | { | ||
| 166 | cgltf_attribute_type_invalid, | ||
| 167 | cgltf_attribute_type_position, | ||
| 168 | cgltf_attribute_type_normal, | ||
| 169 | cgltf_attribute_type_tangent, | ||
| 170 | cgltf_attribute_type_texcoord, | ||
| 171 | cgltf_attribute_type_color, | ||
| 172 | cgltf_attribute_type_joints, | ||
| 173 | cgltf_attribute_type_weights, | ||
| 174 | cgltf_attribute_type_custom, | ||
| 175 | cgltf_attribute_type_max_enum | ||
| 176 | } cgltf_attribute_type; | ||
| 177 | |||
| 178 | typedef enum cgltf_component_type | ||
| 179 | { | ||
| 180 | cgltf_component_type_invalid, | ||
| 181 | cgltf_component_type_r_8, /* BYTE */ | ||
| 182 | cgltf_component_type_r_8u, /* UNSIGNED_BYTE */ | ||
| 183 | cgltf_component_type_r_16, /* SHORT */ | ||
| 184 | cgltf_component_type_r_16u, /* UNSIGNED_SHORT */ | ||
| 185 | cgltf_component_type_r_32u, /* UNSIGNED_INT */ | ||
| 186 | cgltf_component_type_r_32f, /* FLOAT */ | ||
| 187 | cgltf_component_type_max_enum | ||
| 188 | } cgltf_component_type; | ||
| 189 | |||
| 190 | typedef enum cgltf_type | ||
| 191 | { | ||
| 192 | cgltf_type_invalid, | ||
| 193 | cgltf_type_scalar, | ||
| 194 | cgltf_type_vec2, | ||
| 195 | cgltf_type_vec3, | ||
| 196 | cgltf_type_vec4, | ||
| 197 | cgltf_type_mat2, | ||
| 198 | cgltf_type_mat3, | ||
| 199 | cgltf_type_mat4, | ||
| 200 | cgltf_type_max_enum | ||
| 201 | } cgltf_type; | ||
| 202 | |||
| 203 | typedef enum cgltf_primitive_type | ||
| 204 | { | ||
| 205 | cgltf_primitive_type_invalid, | ||
| 206 | cgltf_primitive_type_points, | ||
| 207 | cgltf_primitive_type_lines, | ||
| 208 | cgltf_primitive_type_line_loop, | ||
| 209 | cgltf_primitive_type_line_strip, | ||
| 210 | cgltf_primitive_type_triangles, | ||
| 211 | cgltf_primitive_type_triangle_strip, | ||
| 212 | cgltf_primitive_type_triangle_fan, | ||
| 213 | cgltf_primitive_type_max_enum | ||
| 214 | } cgltf_primitive_type; | ||
| 215 | |||
| 216 | typedef enum cgltf_alpha_mode | ||
| 217 | { | ||
| 218 | cgltf_alpha_mode_opaque, | ||
| 219 | cgltf_alpha_mode_mask, | ||
| 220 | cgltf_alpha_mode_blend, | ||
| 221 | cgltf_alpha_mode_max_enum | ||
| 222 | } cgltf_alpha_mode; | ||
| 223 | |||
| 224 | typedef enum cgltf_animation_path_type { | ||
| 225 | cgltf_animation_path_type_invalid, | ||
| 226 | cgltf_animation_path_type_translation, | ||
| 227 | cgltf_animation_path_type_rotation, | ||
| 228 | cgltf_animation_path_type_scale, | ||
| 229 | cgltf_animation_path_type_weights, | ||
| 230 | cgltf_animation_path_type_max_enum | ||
| 231 | } cgltf_animation_path_type; | ||
| 232 | |||
| 233 | typedef enum cgltf_interpolation_type { | ||
| 234 | cgltf_interpolation_type_linear, | ||
| 235 | cgltf_interpolation_type_step, | ||
| 236 | cgltf_interpolation_type_cubic_spline, | ||
| 237 | cgltf_interpolation_type_max_enum | ||
| 238 | } cgltf_interpolation_type; | ||
| 239 | |||
| 240 | typedef enum cgltf_camera_type { | ||
| 241 | cgltf_camera_type_invalid, | ||
| 242 | cgltf_camera_type_perspective, | ||
| 243 | cgltf_camera_type_orthographic, | ||
| 244 | cgltf_camera_type_max_enum | ||
| 245 | } cgltf_camera_type; | ||
| 246 | |||
| 247 | typedef enum cgltf_light_type { | ||
| 248 | cgltf_light_type_invalid, | ||
| 249 | cgltf_light_type_directional, | ||
| 250 | cgltf_light_type_point, | ||
| 251 | cgltf_light_type_spot, | ||
| 252 | cgltf_light_type_max_enum | ||
| 253 | } cgltf_light_type; | ||
| 254 | |||
| 255 | typedef enum cgltf_data_free_method { | ||
| 256 | cgltf_data_free_method_none, | ||
| 257 | cgltf_data_free_method_file_release, | ||
| 258 | cgltf_data_free_method_memory_free, | ||
| 259 | cgltf_data_free_method_max_enum | ||
| 260 | } cgltf_data_free_method; | ||
| 261 | |||
| 262 | typedef struct cgltf_extras { | ||
| 263 | cgltf_size start_offset; /* this field is deprecated and will be removed in the future; use data instead */ | ||
| 264 | cgltf_size end_offset; /* this field is deprecated and will be removed in the future; use data instead */ | ||
| 265 | |||
| 266 | char* data; | ||
| 267 | } cgltf_extras; | ||
| 268 | |||
| 269 | typedef struct cgltf_extension { | ||
| 270 | char* name; | ||
| 271 | char* data; | ||
| 272 | } cgltf_extension; | ||
| 273 | |||
| 274 | typedef struct cgltf_buffer | ||
| 275 | { | ||
| 276 | char* name; | ||
| 277 | cgltf_size size; | ||
| 278 | char* uri; | ||
| 279 | void* data; /* loaded by cgltf_load_buffers */ | ||
| 280 | cgltf_data_free_method data_free_method; | ||
| 281 | cgltf_extras extras; | ||
| 282 | cgltf_size extensions_count; | ||
| 283 | cgltf_extension* extensions; | ||
| 284 | } cgltf_buffer; | ||
| 285 | |||
| 286 | typedef enum cgltf_meshopt_compression_mode { | ||
| 287 | cgltf_meshopt_compression_mode_invalid, | ||
| 288 | cgltf_meshopt_compression_mode_attributes, | ||
| 289 | cgltf_meshopt_compression_mode_triangles, | ||
| 290 | cgltf_meshopt_compression_mode_indices, | ||
| 291 | cgltf_meshopt_compression_mode_max_enum | ||
| 292 | } cgltf_meshopt_compression_mode; | ||
| 293 | |||
| 294 | typedef enum cgltf_meshopt_compression_filter { | ||
| 295 | cgltf_meshopt_compression_filter_none, | ||
| 296 | cgltf_meshopt_compression_filter_octahedral, | ||
| 297 | cgltf_meshopt_compression_filter_quaternion, | ||
| 298 | cgltf_meshopt_compression_filter_exponential, | ||
| 299 | cgltf_meshopt_compression_filter_max_enum | ||
| 300 | } cgltf_meshopt_compression_filter; | ||
| 301 | |||
| 302 | typedef struct cgltf_meshopt_compression | ||
| 303 | { | ||
| 304 | cgltf_buffer* buffer; | ||
| 305 | cgltf_size offset; | ||
| 306 | cgltf_size size; | ||
| 307 | cgltf_size stride; | ||
| 308 | cgltf_size count; | ||
| 309 | cgltf_meshopt_compression_mode mode; | ||
| 310 | cgltf_meshopt_compression_filter filter; | ||
| 311 | } cgltf_meshopt_compression; | ||
| 312 | |||
| 313 | typedef struct cgltf_buffer_view | ||
| 314 | { | ||
| 315 | char *name; | ||
| 316 | cgltf_buffer* buffer; | ||
| 317 | cgltf_size offset; | ||
| 318 | cgltf_size size; | ||
| 319 | cgltf_size stride; /* 0 == automatically determined by accessor */ | ||
| 320 | cgltf_buffer_view_type type; | ||
| 321 | void* data; /* overrides buffer->data if present, filled by extensions */ | ||
| 322 | cgltf_bool has_meshopt_compression; | ||
| 323 | cgltf_meshopt_compression meshopt_compression; | ||
| 324 | cgltf_extras extras; | ||
| 325 | cgltf_size extensions_count; | ||
| 326 | cgltf_extension* extensions; | ||
| 327 | } cgltf_buffer_view; | ||
| 328 | |||
| 329 | typedef struct cgltf_accessor_sparse | ||
| 330 | { | ||
| 331 | cgltf_size count; | ||
| 332 | cgltf_buffer_view* indices_buffer_view; | ||
| 333 | cgltf_size indices_byte_offset; | ||
| 334 | cgltf_component_type indices_component_type; | ||
| 335 | cgltf_buffer_view* values_buffer_view; | ||
| 336 | cgltf_size values_byte_offset; | ||
| 337 | } cgltf_accessor_sparse; | ||
| 338 | |||
| 339 | typedef struct cgltf_accessor | ||
| 340 | { | ||
| 341 | char* name; | ||
| 342 | cgltf_component_type component_type; | ||
| 343 | cgltf_bool normalized; | ||
| 344 | cgltf_type type; | ||
| 345 | cgltf_size offset; | ||
| 346 | cgltf_size count; | ||
| 347 | cgltf_size stride; | ||
| 348 | cgltf_buffer_view* buffer_view; | ||
| 349 | cgltf_bool has_min; | ||
| 350 | cgltf_float min[16]; | ||
| 351 | cgltf_bool has_max; | ||
| 352 | cgltf_float max[16]; | ||
| 353 | cgltf_bool is_sparse; | ||
| 354 | cgltf_accessor_sparse sparse; | ||
| 355 | cgltf_extras extras; | ||
| 356 | cgltf_size extensions_count; | ||
| 357 | cgltf_extension* extensions; | ||
| 358 | } cgltf_accessor; | ||
| 359 | |||
| 360 | typedef struct cgltf_attribute | ||
| 361 | { | ||
| 362 | char* name; | ||
| 363 | cgltf_attribute_type type; | ||
| 364 | cgltf_int index; | ||
| 365 | cgltf_accessor* data; | ||
| 366 | } cgltf_attribute; | ||
| 367 | |||
| 368 | typedef struct cgltf_image | ||
| 369 | { | ||
| 370 | char* name; | ||
| 371 | char* uri; | ||
| 372 | cgltf_buffer_view* buffer_view; | ||
| 373 | char* mime_type; | ||
| 374 | cgltf_extras extras; | ||
| 375 | cgltf_size extensions_count; | ||
| 376 | cgltf_extension* extensions; | ||
| 377 | } cgltf_image; | ||
| 378 | |||
| 379 | typedef struct cgltf_sampler | ||
| 380 | { | ||
| 381 | char* name; | ||
| 382 | cgltf_int mag_filter; | ||
| 383 | cgltf_int min_filter; | ||
| 384 | cgltf_int wrap_s; | ||
| 385 | cgltf_int wrap_t; | ||
| 386 | cgltf_extras extras; | ||
| 387 | cgltf_size extensions_count; | ||
| 388 | cgltf_extension* extensions; | ||
| 389 | } cgltf_sampler; | ||
| 390 | |||
| 391 | typedef struct cgltf_texture | ||
| 392 | { | ||
| 393 | char* name; | ||
| 394 | cgltf_image* image; | ||
| 395 | cgltf_sampler* sampler; | ||
| 396 | cgltf_bool has_basisu; | ||
| 397 | cgltf_image* basisu_image; | ||
| 398 | cgltf_bool has_webp; | ||
| 399 | cgltf_image* webp_image; | ||
| 400 | cgltf_extras extras; | ||
| 401 | cgltf_size extensions_count; | ||
| 402 | cgltf_extension* extensions; | ||
| 403 | } cgltf_texture; | ||
| 404 | |||
| 405 | typedef struct cgltf_texture_transform | ||
| 406 | { | ||
| 407 | cgltf_float offset[2]; | ||
| 408 | cgltf_float rotation; | ||
| 409 | cgltf_float scale[2]; | ||
| 410 | cgltf_bool has_texcoord; | ||
| 411 | cgltf_int texcoord; | ||
| 412 | } cgltf_texture_transform; | ||
| 413 | |||
| 414 | typedef struct cgltf_texture_view | ||
| 415 | { | ||
| 416 | cgltf_texture* texture; | ||
| 417 | cgltf_int texcoord; | ||
| 418 | cgltf_float scale; /* equivalent to strength for occlusion_texture */ | ||
| 419 | cgltf_bool has_transform; | ||
| 420 | cgltf_texture_transform transform; | ||
| 421 | } cgltf_texture_view; | ||
| 422 | |||
| 423 | typedef struct cgltf_pbr_metallic_roughness | ||
| 424 | { | ||
| 425 | cgltf_texture_view base_color_texture; | ||
| 426 | cgltf_texture_view metallic_roughness_texture; | ||
| 427 | |||
| 428 | cgltf_float base_color_factor[4]; | ||
| 429 | cgltf_float metallic_factor; | ||
| 430 | cgltf_float roughness_factor; | ||
| 431 | } cgltf_pbr_metallic_roughness; | ||
| 432 | |||
| 433 | typedef struct cgltf_pbr_specular_glossiness | ||
| 434 | { | ||
| 435 | cgltf_texture_view diffuse_texture; | ||
| 436 | cgltf_texture_view specular_glossiness_texture; | ||
| 437 | |||
| 438 | cgltf_float diffuse_factor[4]; | ||
| 439 | cgltf_float specular_factor[3]; | ||
| 440 | cgltf_float glossiness_factor; | ||
| 441 | } cgltf_pbr_specular_glossiness; | ||
| 442 | |||
| 443 | typedef struct cgltf_clearcoat | ||
| 444 | { | ||
| 445 | cgltf_texture_view clearcoat_texture; | ||
| 446 | cgltf_texture_view clearcoat_roughness_texture; | ||
| 447 | cgltf_texture_view clearcoat_normal_texture; | ||
| 448 | |||
| 449 | cgltf_float clearcoat_factor; | ||
| 450 | cgltf_float clearcoat_roughness_factor; | ||
| 451 | } cgltf_clearcoat; | ||
| 452 | |||
| 453 | typedef struct cgltf_transmission | ||
| 454 | { | ||
| 455 | cgltf_texture_view transmission_texture; | ||
| 456 | cgltf_float transmission_factor; | ||
| 457 | } cgltf_transmission; | ||
| 458 | |||
| 459 | typedef struct cgltf_ior | ||
| 460 | { | ||
| 461 | cgltf_float ior; | ||
| 462 | } cgltf_ior; | ||
| 463 | |||
| 464 | typedef struct cgltf_specular | ||
| 465 | { | ||
| 466 | cgltf_texture_view specular_texture; | ||
| 467 | cgltf_texture_view specular_color_texture; | ||
| 468 | cgltf_float specular_color_factor[3]; | ||
| 469 | cgltf_float specular_factor; | ||
| 470 | } cgltf_specular; | ||
| 471 | |||
| 472 | typedef struct cgltf_volume | ||
| 473 | { | ||
| 474 | cgltf_texture_view thickness_texture; | ||
| 475 | cgltf_float thickness_factor; | ||
| 476 | cgltf_float attenuation_color[3]; | ||
| 477 | cgltf_float attenuation_distance; | ||
| 478 | } cgltf_volume; | ||
| 479 | |||
| 480 | typedef struct cgltf_sheen | ||
| 481 | { | ||
| 482 | cgltf_texture_view sheen_color_texture; | ||
| 483 | cgltf_float sheen_color_factor[3]; | ||
| 484 | cgltf_texture_view sheen_roughness_texture; | ||
| 485 | cgltf_float sheen_roughness_factor; | ||
| 486 | } cgltf_sheen; | ||
| 487 | |||
| 488 | typedef struct cgltf_emissive_strength | ||
| 489 | { | ||
| 490 | cgltf_float emissive_strength; | ||
| 491 | } cgltf_emissive_strength; | ||
| 492 | |||
| 493 | typedef struct cgltf_iridescence | ||
| 494 | { | ||
| 495 | cgltf_float iridescence_factor; | ||
| 496 | cgltf_texture_view iridescence_texture; | ||
| 497 | cgltf_float iridescence_ior; | ||
| 498 | cgltf_float iridescence_thickness_min; | ||
| 499 | cgltf_float iridescence_thickness_max; | ||
| 500 | cgltf_texture_view iridescence_thickness_texture; | ||
| 501 | } cgltf_iridescence; | ||
| 502 | |||
| 503 | typedef struct cgltf_anisotropy | ||
| 504 | { | ||
| 505 | cgltf_float anisotropy_strength; | ||
| 506 | cgltf_float anisotropy_rotation; | ||
| 507 | cgltf_texture_view anisotropy_texture; | ||
| 508 | } cgltf_anisotropy; | ||
| 509 | |||
| 510 | typedef struct cgltf_dispersion | ||
| 511 | { | ||
| 512 | cgltf_float dispersion; | ||
| 513 | } cgltf_dispersion; | ||
| 514 | |||
| 515 | typedef struct cgltf_material | ||
| 516 | { | ||
| 517 | char* name; | ||
| 518 | cgltf_bool has_pbr_metallic_roughness; | ||
| 519 | cgltf_bool has_pbr_specular_glossiness; | ||
| 520 | cgltf_bool has_clearcoat; | ||
| 521 | cgltf_bool has_transmission; | ||
| 522 | cgltf_bool has_volume; | ||
| 523 | cgltf_bool has_ior; | ||
| 524 | cgltf_bool has_specular; | ||
| 525 | cgltf_bool has_sheen; | ||
| 526 | cgltf_bool has_emissive_strength; | ||
| 527 | cgltf_bool has_iridescence; | ||
| 528 | cgltf_bool has_anisotropy; | ||
| 529 | cgltf_bool has_dispersion; | ||
| 530 | cgltf_pbr_metallic_roughness pbr_metallic_roughness; | ||
| 531 | cgltf_pbr_specular_glossiness pbr_specular_glossiness; | ||
| 532 | cgltf_clearcoat clearcoat; | ||
| 533 | cgltf_ior ior; | ||
| 534 | cgltf_specular specular; | ||
| 535 | cgltf_sheen sheen; | ||
| 536 | cgltf_transmission transmission; | ||
| 537 | cgltf_volume volume; | ||
| 538 | cgltf_emissive_strength emissive_strength; | ||
| 539 | cgltf_iridescence iridescence; | ||
| 540 | cgltf_anisotropy anisotropy; | ||
| 541 | cgltf_dispersion dispersion; | ||
| 542 | cgltf_texture_view normal_texture; | ||
| 543 | cgltf_texture_view occlusion_texture; | ||
| 544 | cgltf_texture_view emissive_texture; | ||
| 545 | cgltf_float emissive_factor[3]; | ||
| 546 | cgltf_alpha_mode alpha_mode; | ||
| 547 | cgltf_float alpha_cutoff; | ||
| 548 | cgltf_bool double_sided; | ||
| 549 | cgltf_bool unlit; | ||
| 550 | cgltf_extras extras; | ||
| 551 | cgltf_size extensions_count; | ||
| 552 | cgltf_extension* extensions; | ||
| 553 | } cgltf_material; | ||
| 554 | |||
| 555 | typedef struct cgltf_material_mapping | ||
| 556 | { | ||
| 557 | cgltf_size variant; | ||
| 558 | cgltf_material* material; | ||
| 559 | cgltf_extras extras; | ||
| 560 | } cgltf_material_mapping; | ||
| 561 | |||
| 562 | typedef struct cgltf_morph_target { | ||
| 563 | cgltf_attribute* attributes; | ||
| 564 | cgltf_size attributes_count; | ||
| 565 | } cgltf_morph_target; | ||
| 566 | |||
| 567 | typedef struct cgltf_draco_mesh_compression { | ||
| 568 | cgltf_buffer_view* buffer_view; | ||
| 569 | cgltf_attribute* attributes; | ||
| 570 | cgltf_size attributes_count; | ||
| 571 | } cgltf_draco_mesh_compression; | ||
| 572 | |||
| 573 | typedef struct cgltf_mesh_gpu_instancing { | ||
| 574 | cgltf_attribute* attributes; | ||
| 575 | cgltf_size attributes_count; | ||
| 576 | } cgltf_mesh_gpu_instancing; | ||
| 577 | |||
| 578 | typedef struct cgltf_primitive { | ||
| 579 | cgltf_primitive_type type; | ||
| 580 | cgltf_accessor* indices; | ||
| 581 | cgltf_material* material; | ||
| 582 | cgltf_attribute* attributes; | ||
| 583 | cgltf_size attributes_count; | ||
| 584 | cgltf_morph_target* targets; | ||
| 585 | cgltf_size targets_count; | ||
| 586 | cgltf_extras extras; | ||
| 587 | cgltf_bool has_draco_mesh_compression; | ||
| 588 | cgltf_draco_mesh_compression draco_mesh_compression; | ||
| 589 | cgltf_material_mapping* mappings; | ||
| 590 | cgltf_size mappings_count; | ||
| 591 | cgltf_size extensions_count; | ||
| 592 | cgltf_extension* extensions; | ||
| 593 | } cgltf_primitive; | ||
| 594 | |||
| 595 | typedef struct cgltf_mesh { | ||
| 596 | char* name; | ||
| 597 | cgltf_primitive* primitives; | ||
| 598 | cgltf_size primitives_count; | ||
| 599 | cgltf_float* weights; | ||
| 600 | cgltf_size weights_count; | ||
| 601 | char** target_names; | ||
| 602 | cgltf_size target_names_count; | ||
| 603 | cgltf_extras extras; | ||
| 604 | cgltf_size extensions_count; | ||
| 605 | cgltf_extension* extensions; | ||
| 606 | } cgltf_mesh; | ||
| 607 | |||
| 608 | typedef struct cgltf_node cgltf_node; | ||
| 609 | |||
| 610 | typedef struct cgltf_skin { | ||
| 611 | char* name; | ||
| 612 | cgltf_node** joints; | ||
| 613 | cgltf_size joints_count; | ||
| 614 | cgltf_node* skeleton; | ||
| 615 | cgltf_accessor* inverse_bind_matrices; | ||
| 616 | cgltf_extras extras; | ||
| 617 | cgltf_size extensions_count; | ||
| 618 | cgltf_extension* extensions; | ||
| 619 | } cgltf_skin; | ||
| 620 | |||
| 621 | typedef struct cgltf_camera_perspective { | ||
| 622 | cgltf_bool has_aspect_ratio; | ||
| 623 | cgltf_float aspect_ratio; | ||
| 624 | cgltf_float yfov; | ||
| 625 | cgltf_bool has_zfar; | ||
| 626 | cgltf_float zfar; | ||
| 627 | cgltf_float znear; | ||
| 628 | cgltf_extras extras; | ||
| 629 | } cgltf_camera_perspective; | ||
| 630 | |||
| 631 | typedef struct cgltf_camera_orthographic { | ||
| 632 | cgltf_float xmag; | ||
| 633 | cgltf_float ymag; | ||
| 634 | cgltf_float zfar; | ||
| 635 | cgltf_float znear; | ||
| 636 | cgltf_extras extras; | ||
| 637 | } cgltf_camera_orthographic; | ||
| 638 | |||
| 639 | typedef struct cgltf_camera { | ||
| 640 | char* name; | ||
| 641 | cgltf_camera_type type; | ||
| 642 | union { | ||
| 643 | cgltf_camera_perspective perspective; | ||
| 644 | cgltf_camera_orthographic orthographic; | ||
| 645 | } data; | ||
| 646 | cgltf_extras extras; | ||
| 647 | cgltf_size extensions_count; | ||
| 648 | cgltf_extension* extensions; | ||
| 649 | } cgltf_camera; | ||
| 650 | |||
| 651 | typedef struct cgltf_light { | ||
| 652 | char* name; | ||
| 653 | cgltf_float color[3]; | ||
| 654 | cgltf_float intensity; | ||
| 655 | cgltf_light_type type; | ||
| 656 | cgltf_float range; | ||
| 657 | cgltf_float spot_inner_cone_angle; | ||
| 658 | cgltf_float spot_outer_cone_angle; | ||
| 659 | cgltf_extras extras; | ||
| 660 | } cgltf_light; | ||
| 661 | |||
| 662 | struct cgltf_node { | ||
| 663 | char* name; | ||
| 664 | cgltf_node* parent; | ||
| 665 | cgltf_node** children; | ||
| 666 | cgltf_size children_count; | ||
| 667 | cgltf_skin* skin; | ||
| 668 | cgltf_mesh* mesh; | ||
| 669 | cgltf_camera* camera; | ||
| 670 | cgltf_light* light; | ||
| 671 | cgltf_float* weights; | ||
| 672 | cgltf_size weights_count; | ||
| 673 | cgltf_bool has_translation; | ||
| 674 | cgltf_bool has_rotation; | ||
| 675 | cgltf_bool has_scale; | ||
| 676 | cgltf_bool has_matrix; | ||
| 677 | cgltf_float translation[3]; | ||
| 678 | cgltf_float rotation[4]; | ||
| 679 | cgltf_float scale[3]; | ||
| 680 | cgltf_float matrix[16]; | ||
| 681 | cgltf_extras extras; | ||
| 682 | cgltf_bool has_mesh_gpu_instancing; | ||
| 683 | cgltf_mesh_gpu_instancing mesh_gpu_instancing; | ||
| 684 | cgltf_size extensions_count; | ||
| 685 | cgltf_extension* extensions; | ||
| 686 | }; | ||
| 687 | |||
| 688 | typedef struct cgltf_scene { | ||
| 689 | char* name; | ||
| 690 | cgltf_node** nodes; | ||
| 691 | cgltf_size nodes_count; | ||
| 692 | cgltf_extras extras; | ||
| 693 | cgltf_size extensions_count; | ||
| 694 | cgltf_extension* extensions; | ||
| 695 | } cgltf_scene; | ||
| 696 | |||
| 697 | typedef struct cgltf_animation_sampler { | ||
| 698 | cgltf_accessor* input; | ||
| 699 | cgltf_accessor* output; | ||
| 700 | cgltf_interpolation_type interpolation; | ||
| 701 | cgltf_extras extras; | ||
| 702 | cgltf_size extensions_count; | ||
| 703 | cgltf_extension* extensions; | ||
| 704 | } cgltf_animation_sampler; | ||
| 705 | |||
| 706 | typedef struct cgltf_animation_channel { | ||
| 707 | cgltf_animation_sampler* sampler; | ||
| 708 | cgltf_node* target_node; | ||
| 709 | cgltf_animation_path_type target_path; | ||
| 710 | cgltf_extras extras; | ||
| 711 | cgltf_size extensions_count; | ||
| 712 | cgltf_extension* extensions; | ||
| 713 | } cgltf_animation_channel; | ||
| 714 | |||
| 715 | typedef struct cgltf_animation { | ||
| 716 | char* name; | ||
| 717 | cgltf_animation_sampler* samplers; | ||
| 718 | cgltf_size samplers_count; | ||
| 719 | cgltf_animation_channel* channels; | ||
| 720 | cgltf_size channels_count; | ||
| 721 | cgltf_extras extras; | ||
| 722 | cgltf_size extensions_count; | ||
| 723 | cgltf_extension* extensions; | ||
| 724 | } cgltf_animation; | ||
| 725 | |||
| 726 | typedef struct cgltf_material_variant | ||
| 727 | { | ||
| 728 | char* name; | ||
| 729 | cgltf_extras extras; | ||
| 730 | } cgltf_material_variant; | ||
| 731 | |||
| 732 | typedef struct cgltf_asset { | ||
| 733 | char* copyright; | ||
| 734 | char* generator; | ||
| 735 | char* version; | ||
| 736 | char* min_version; | ||
| 737 | cgltf_extras extras; | ||
| 738 | cgltf_size extensions_count; | ||
| 739 | cgltf_extension* extensions; | ||
| 740 | } cgltf_asset; | ||
| 741 | |||
| 742 | typedef struct cgltf_data | ||
| 743 | { | ||
| 744 | cgltf_file_type file_type; | ||
| 745 | void* file_data; | ||
| 746 | |||
| 747 | cgltf_asset asset; | ||
| 748 | |||
| 749 | cgltf_mesh* meshes; | ||
| 750 | cgltf_size meshes_count; | ||
| 751 | |||
| 752 | cgltf_material* materials; | ||
| 753 | cgltf_size materials_count; | ||
| 754 | |||
| 755 | cgltf_accessor* accessors; | ||
| 756 | cgltf_size accessors_count; | ||
| 757 | |||
| 758 | cgltf_buffer_view* buffer_views; | ||
| 759 | cgltf_size buffer_views_count; | ||
| 760 | |||
| 761 | cgltf_buffer* buffers; | ||
| 762 | cgltf_size buffers_count; | ||
| 763 | |||
| 764 | cgltf_image* images; | ||
| 765 | cgltf_size images_count; | ||
| 766 | |||
| 767 | cgltf_texture* textures; | ||
| 768 | cgltf_size textures_count; | ||
| 769 | |||
| 770 | cgltf_sampler* samplers; | ||
| 771 | cgltf_size samplers_count; | ||
| 772 | |||
| 773 | cgltf_skin* skins; | ||
| 774 | cgltf_size skins_count; | ||
| 775 | |||
| 776 | cgltf_camera* cameras; | ||
| 777 | cgltf_size cameras_count; | ||
| 778 | |||
| 779 | cgltf_light* lights; | ||
| 780 | cgltf_size lights_count; | ||
| 781 | |||
| 782 | cgltf_node* nodes; | ||
| 783 | cgltf_size nodes_count; | ||
| 784 | |||
| 785 | cgltf_scene* scenes; | ||
| 786 | cgltf_size scenes_count; | ||
| 787 | |||
| 788 | cgltf_scene* scene; | ||
| 789 | |||
| 790 | cgltf_animation* animations; | ||
| 791 | cgltf_size animations_count; | ||
| 792 | |||
| 793 | cgltf_material_variant* variants; | ||
| 794 | cgltf_size variants_count; | ||
| 795 | |||
| 796 | cgltf_extras extras; | ||
| 797 | |||
| 798 | cgltf_size data_extensions_count; | ||
| 799 | cgltf_extension* data_extensions; | ||
| 800 | |||
| 801 | char** extensions_used; | ||
| 802 | cgltf_size extensions_used_count; | ||
| 803 | |||
| 804 | char** extensions_required; | ||
| 805 | cgltf_size extensions_required_count; | ||
| 806 | |||
| 807 | const char* json; | ||
| 808 | cgltf_size json_size; | ||
| 809 | |||
| 810 | const void* bin; | ||
| 811 | cgltf_size bin_size; | ||
| 812 | |||
| 813 | cgltf_memory_options memory; | ||
| 814 | cgltf_file_options file; | ||
| 815 | } cgltf_data; | ||
| 816 | |||
| 817 | cgltf_result cgltf_parse( | ||
| 818 | const cgltf_options* options, | ||
| 819 | const void* data, | ||
| 820 | cgltf_size size, | ||
| 821 | cgltf_data** out_data); | ||
| 822 | |||
| 823 | cgltf_result cgltf_parse_file( | ||
| 824 | const cgltf_options* options, | ||
| 825 | const char* path, | ||
| 826 | cgltf_data** out_data); | ||
| 827 | |||
| 828 | cgltf_result cgltf_load_buffers( | ||
| 829 | const cgltf_options* options, | ||
| 830 | cgltf_data* data, | ||
| 831 | const char* gltf_path); | ||
| 832 | |||
| 833 | cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data); | ||
| 834 | |||
| 835 | cgltf_size cgltf_decode_string(char* string); | ||
| 836 | cgltf_size cgltf_decode_uri(char* uri); | ||
| 837 | |||
| 838 | cgltf_result cgltf_validate(cgltf_data* data); | ||
| 839 | |||
| 840 | void cgltf_free(cgltf_data* data); | ||
| 841 | |||
| 842 | void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix); | ||
| 843 | void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix); | ||
| 844 | |||
| 845 | const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view); | ||
| 846 | |||
| 847 | cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size); | ||
| 848 | cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size); | ||
| 849 | cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); | ||
| 850 | |||
| 851 | cgltf_size cgltf_num_components(cgltf_type type); | ||
| 852 | cgltf_size cgltf_component_size(cgltf_component_type component_type); | ||
| 853 | cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type); | ||
| 854 | |||
| 855 | cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count); | ||
| 856 | cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* out, cgltf_size out_component_size, cgltf_size index_count); | ||
| 857 | |||
| 858 | /* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */ | ||
| 859 | cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size); | ||
| 860 | |||
| 861 | cgltf_size cgltf_mesh_index(const cgltf_data* data, const cgltf_mesh* object); | ||
| 862 | cgltf_size cgltf_material_index(const cgltf_data* data, const cgltf_material* object); | ||
| 863 | cgltf_size cgltf_accessor_index(const cgltf_data* data, const cgltf_accessor* object); | ||
| 864 | cgltf_size cgltf_buffer_view_index(const cgltf_data* data, const cgltf_buffer_view* object); | ||
| 865 | cgltf_size cgltf_buffer_index(const cgltf_data* data, const cgltf_buffer* object); | ||
| 866 | cgltf_size cgltf_image_index(const cgltf_data* data, const cgltf_image* object); | ||
| 867 | cgltf_size cgltf_texture_index(const cgltf_data* data, const cgltf_texture* object); | ||
| 868 | cgltf_size cgltf_sampler_index(const cgltf_data* data, const cgltf_sampler* object); | ||
| 869 | cgltf_size cgltf_skin_index(const cgltf_data* data, const cgltf_skin* object); | ||
| 870 | cgltf_size cgltf_camera_index(const cgltf_data* data, const cgltf_camera* object); | ||
| 871 | cgltf_size cgltf_light_index(const cgltf_data* data, const cgltf_light* object); | ||
| 872 | cgltf_size cgltf_node_index(const cgltf_data* data, const cgltf_node* object); | ||
| 873 | cgltf_size cgltf_scene_index(const cgltf_data* data, const cgltf_scene* object); | ||
| 874 | cgltf_size cgltf_animation_index(const cgltf_data* data, const cgltf_animation* object); | ||
| 875 | cgltf_size cgltf_animation_sampler_index(const cgltf_animation* animation, const cgltf_animation_sampler* object); | ||
| 876 | cgltf_size cgltf_animation_channel_index(const cgltf_animation* animation, const cgltf_animation_channel* object); | ||
| 877 | |||
| 878 | #ifdef __cplusplus | ||
| 879 | } | ||
| 880 | #endif | ||
| 881 | |||
| 882 | #endif /* #ifndef CGLTF_H_INCLUDED__ */ | ||
| 883 | |||
| 884 | /* | ||
| 885 | * | ||
| 886 | * Stop now, if you are only interested in the API. | ||
| 887 | * Below, you find the implementation. | ||
| 888 | * | ||
| 889 | */ | ||
| 890 | |||
| 891 | #if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__) | ||
| 892 | /* This makes MSVC/CLion intellisense work. */ | ||
| 893 | #define CGLTF_IMPLEMENTATION | ||
| 894 | #endif | ||
| 895 | |||
| 896 | #ifdef CGLTF_IMPLEMENTATION | ||
| 897 | |||
| 898 | #include <assert.h> /* For assert */ | ||
| 899 | #include <string.h> /* For strncpy */ | ||
| 900 | #include <stdio.h> /* For fopen */ | ||
| 901 | #include <limits.h> /* For UINT_MAX etc */ | ||
| 902 | #include <float.h> /* For FLT_MAX */ | ||
| 903 | |||
| 904 | #if !defined(CGLTF_MALLOC) || !defined(CGLTF_FREE) || !defined(CGLTF_ATOI) || !defined(CGLTF_ATOF) || !defined(CGLTF_ATOLL) | ||
| 905 | #include <stdlib.h> /* For malloc, free, atoi, atof */ | ||
| 906 | #endif | ||
| 907 | |||
| 908 | /* JSMN_PARENT_LINKS is necessary to make parsing large structures linear in input size */ | ||
| 909 | #define JSMN_PARENT_LINKS | ||
| 910 | |||
| 911 | /* JSMN_STRICT is necessary to reject invalid JSON documents */ | ||
| 912 | #define JSMN_STRICT | ||
| 913 | |||
| 914 | /* | ||
| 915 | * -- jsmn.h start -- | ||
| 916 | * Source: https://github.com/zserge/jsmn | ||
| 917 | * License: MIT | ||
| 918 | */ | ||
| 919 | typedef enum { | ||
| 920 | JSMN_UNDEFINED = 0, | ||
| 921 | JSMN_OBJECT = 1, | ||
| 922 | JSMN_ARRAY = 2, | ||
| 923 | JSMN_STRING = 3, | ||
| 924 | JSMN_PRIMITIVE = 4 | ||
| 925 | } jsmntype_t; | ||
| 926 | enum jsmnerr { | ||
| 927 | /* Not enough tokens were provided */ | ||
| 928 | JSMN_ERROR_NOMEM = -1, | ||
| 929 | /* Invalid character inside JSON string */ | ||
| 930 | JSMN_ERROR_INVAL = -2, | ||
| 931 | /* The string is not a full JSON packet, more bytes expected */ | ||
| 932 | JSMN_ERROR_PART = -3 | ||
| 933 | }; | ||
| 934 | typedef struct { | ||
| 935 | jsmntype_t type; | ||
| 936 | ptrdiff_t start; | ||
| 937 | ptrdiff_t end; | ||
| 938 | int size; | ||
| 939 | #ifdef JSMN_PARENT_LINKS | ||
| 940 | int parent; | ||
| 941 | #endif | ||
| 942 | } jsmntok_t; | ||
| 943 | typedef struct { | ||
| 944 | size_t pos; /* offset in the JSON string */ | ||
| 945 | unsigned int toknext; /* next token to allocate */ | ||
| 946 | int toksuper; /* superior token node, e.g parent object or array */ | ||
| 947 | } jsmn_parser; | ||
| 948 | static void jsmn_init(jsmn_parser *parser); | ||
| 949 | static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens); | ||
| 950 | /* | ||
| 951 | * -- jsmn.h end -- | ||
| 952 | */ | ||
| 953 | |||
| 954 | |||
| 955 | #ifndef CGLTF_CONSTS | ||
| 956 | #define GlbHeaderSize 12 | ||
| 957 | #define GlbChunkHeaderSize 8 | ||
| 958 | static const uint32_t GlbVersion = 2; | ||
| 959 | static const uint32_t GlbMagic = 0x46546C67; | ||
| 960 | static const uint32_t GlbMagicJsonChunk = 0x4E4F534A; | ||
| 961 | static const uint32_t GlbMagicBinChunk = 0x004E4942; | ||
| 962 | #define CGLTF_CONSTS | ||
| 963 | #endif | ||
| 964 | |||
| 965 | #ifndef CGLTF_MALLOC | ||
| 966 | #define CGLTF_MALLOC(size) malloc(size) | ||
| 967 | #endif | ||
| 968 | #ifndef CGLTF_FREE | ||
| 969 | #define CGLTF_FREE(ptr) free(ptr) | ||
| 970 | #endif | ||
| 971 | #ifndef CGLTF_ATOI | ||
| 972 | #define CGLTF_ATOI(str) atoi(str) | ||
| 973 | #endif | ||
| 974 | #ifndef CGLTF_ATOF | ||
| 975 | #define CGLTF_ATOF(str) atof(str) | ||
| 976 | #endif | ||
| 977 | #ifndef CGLTF_ATOLL | ||
| 978 | #define CGLTF_ATOLL(str) atoll(str) | ||
| 979 | #endif | ||
| 980 | #ifndef CGLTF_VALIDATE_ENABLE_ASSERTS | ||
| 981 | #define CGLTF_VALIDATE_ENABLE_ASSERTS 0 | ||
| 982 | #endif | ||
| 983 | |||
| 984 | static void* cgltf_default_alloc(void* user, cgltf_size size) | ||
| 985 | { | ||
| 986 | (void)user; | ||
| 987 | return CGLTF_MALLOC(size); | ||
| 988 | } | ||
| 989 | |||
| 990 | static void cgltf_default_free(void* user, void* ptr) | ||
| 991 | { | ||
| 992 | (void)user; | ||
| 993 | CGLTF_FREE(ptr); | ||
| 994 | } | ||
| 995 | |||
| 996 | static void* cgltf_calloc(cgltf_options* options, size_t element_size, cgltf_size count) | ||
| 997 | { | ||
| 998 | if (SIZE_MAX / element_size < count) | ||
| 999 | { | ||
| 1000 | return NULL; | ||
| 1001 | } | ||
| 1002 | void* result = options->memory.alloc_func(options->memory.user_data, element_size * count); | ||
| 1003 | if (!result) | ||
| 1004 | { | ||
| 1005 | return NULL; | ||
| 1006 | } | ||
| 1007 | memset(result, 0, element_size * count); | ||
| 1008 | return result; | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | static cgltf_result cgltf_default_file_read(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data) | ||
| 1012 | { | ||
| 1013 | (void)file_options; | ||
| 1014 | void* (*memory_alloc)(void*, cgltf_size) = memory_options->alloc_func ? memory_options->alloc_func : &cgltf_default_alloc; | ||
| 1015 | void (*memory_free)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free; | ||
| 1016 | |||
| 1017 | FILE* file = fopen(path, "rb"); | ||
| 1018 | if (!file) | ||
| 1019 | { | ||
| 1020 | return cgltf_result_file_not_found; | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | cgltf_size file_size = size ? *size : 0; | ||
| 1024 | |||
| 1025 | if (file_size == 0) | ||
| 1026 | { | ||
| 1027 | fseek(file, 0, SEEK_END); | ||
| 1028 | |||
| 1029 | #ifdef _MSC_VER | ||
| 1030 | __int64 length = _ftelli64(file); | ||
| 1031 | #else | ||
| 1032 | long length = ftell(file); | ||
| 1033 | #endif | ||
| 1034 | |||
| 1035 | if (length < 0) | ||
| 1036 | { | ||
| 1037 | fclose(file); | ||
| 1038 | return cgltf_result_io_error; | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | fseek(file, 0, SEEK_SET); | ||
| 1042 | file_size = (cgltf_size)length; | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | char* file_data = (char*)memory_alloc(memory_options->user_data, file_size); | ||
| 1046 | if (!file_data) | ||
| 1047 | { | ||
| 1048 | fclose(file); | ||
| 1049 | return cgltf_result_out_of_memory; | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | cgltf_size read_size = fread(file_data, 1, file_size, file); | ||
| 1053 | |||
| 1054 | fclose(file); | ||
| 1055 | |||
| 1056 | if (read_size != file_size) | ||
| 1057 | { | ||
| 1058 | memory_free(memory_options->user_data, file_data); | ||
| 1059 | return cgltf_result_io_error; | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | if (size) | ||
| 1063 | { | ||
| 1064 | *size = file_size; | ||
| 1065 | } | ||
| 1066 | if (data) | ||
| 1067 | { | ||
| 1068 | *data = file_data; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | return cgltf_result_success; | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data) | ||
| 1075 | { | ||
| 1076 | (void)file_options; | ||
| 1077 | void (*memfree)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free; | ||
| 1078 | memfree(memory_options->user_data, data); | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | static cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data); | ||
| 1082 | |||
| 1083 | cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_size size, cgltf_data** out_data) | ||
| 1084 | { | ||
| 1085 | if (size < GlbHeaderSize) | ||
| 1086 | { | ||
| 1087 | return cgltf_result_data_too_short; | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | if (options == NULL) | ||
| 1091 | { | ||
| 1092 | return cgltf_result_invalid_options; | ||
| 1093 | } | ||
| 1094 | |||
| 1095 | cgltf_options fixed_options = *options; | ||
| 1096 | if (fixed_options.memory.alloc_func == NULL) | ||
| 1097 | { | ||
| 1098 | fixed_options.memory.alloc_func = &cgltf_default_alloc; | ||
| 1099 | } | ||
| 1100 | if (fixed_options.memory.free_func == NULL) | ||
| 1101 | { | ||
| 1102 | fixed_options.memory.free_func = &cgltf_default_free; | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | uint32_t tmp; | ||
| 1106 | // Magic | ||
| 1107 | memcpy(&tmp, data, 4); | ||
| 1108 | if (tmp != GlbMagic) | ||
| 1109 | { | ||
| 1110 | if (fixed_options.type == cgltf_file_type_invalid) | ||
| 1111 | { | ||
| 1112 | fixed_options.type = cgltf_file_type_gltf; | ||
| 1113 | } | ||
| 1114 | else if (fixed_options.type == cgltf_file_type_glb) | ||
| 1115 | { | ||
| 1116 | return cgltf_result_unknown_format; | ||
| 1117 | } | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | if (fixed_options.type == cgltf_file_type_gltf) | ||
| 1121 | { | ||
| 1122 | cgltf_result json_result = cgltf_parse_json(&fixed_options, (const uint8_t*)data, size, out_data); | ||
| 1123 | if (json_result != cgltf_result_success) | ||
| 1124 | { | ||
| 1125 | return json_result; | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | (*out_data)->file_type = cgltf_file_type_gltf; | ||
| 1129 | |||
| 1130 | return cgltf_result_success; | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | const uint8_t* ptr = (const uint8_t*)data; | ||
| 1134 | // Version | ||
| 1135 | memcpy(&tmp, ptr + 4, 4); | ||
| 1136 | uint32_t version = tmp; | ||
| 1137 | if (version != GlbVersion) | ||
| 1138 | { | ||
| 1139 | return version < GlbVersion ? cgltf_result_legacy_gltf : cgltf_result_unknown_format; | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | // Total length | ||
| 1143 | memcpy(&tmp, ptr + 8, 4); | ||
| 1144 | if (tmp > size) | ||
| 1145 | { | ||
| 1146 | return cgltf_result_data_too_short; | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | const uint8_t* json_chunk = ptr + GlbHeaderSize; | ||
| 1150 | |||
| 1151 | if (GlbHeaderSize + GlbChunkHeaderSize > size) | ||
| 1152 | { | ||
| 1153 | return cgltf_result_data_too_short; | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | // JSON chunk: length | ||
| 1157 | uint32_t json_length; | ||
| 1158 | memcpy(&json_length, json_chunk, 4); | ||
| 1159 | if (json_length > size - GlbHeaderSize - GlbChunkHeaderSize) | ||
| 1160 | { | ||
| 1161 | return cgltf_result_data_too_short; | ||
| 1162 | } | ||
| 1163 | |||
| 1164 | // JSON chunk: magic | ||
| 1165 | memcpy(&tmp, json_chunk + 4, 4); | ||
| 1166 | if (tmp != GlbMagicJsonChunk) | ||
| 1167 | { | ||
| 1168 | return cgltf_result_unknown_format; | ||
| 1169 | } | ||
| 1170 | |||
| 1171 | json_chunk += GlbChunkHeaderSize; | ||
| 1172 | |||
| 1173 | const void* bin = NULL; | ||
| 1174 | cgltf_size bin_size = 0; | ||
| 1175 | |||
| 1176 | if (GlbChunkHeaderSize <= size - GlbHeaderSize - GlbChunkHeaderSize - json_length) | ||
| 1177 | { | ||
| 1178 | // We can read another chunk | ||
| 1179 | const uint8_t* bin_chunk = json_chunk + json_length; | ||
| 1180 | |||
| 1181 | // Bin chunk: length | ||
| 1182 | uint32_t bin_length; | ||
| 1183 | memcpy(&bin_length, bin_chunk, 4); | ||
| 1184 | if (bin_length > size - GlbHeaderSize - GlbChunkHeaderSize - json_length - GlbChunkHeaderSize) | ||
| 1185 | { | ||
| 1186 | return cgltf_result_data_too_short; | ||
| 1187 | } | ||
| 1188 | |||
| 1189 | // Bin chunk: magic | ||
| 1190 | memcpy(&tmp, bin_chunk + 4, 4); | ||
| 1191 | if (tmp != GlbMagicBinChunk) | ||
| 1192 | { | ||
| 1193 | return cgltf_result_unknown_format; | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | bin_chunk += GlbChunkHeaderSize; | ||
| 1197 | |||
| 1198 | bin = bin_chunk; | ||
| 1199 | bin_size = bin_length; | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | cgltf_result json_result = cgltf_parse_json(&fixed_options, json_chunk, json_length, out_data); | ||
| 1203 | if (json_result != cgltf_result_success) | ||
| 1204 | { | ||
| 1205 | return json_result; | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | (*out_data)->file_type = cgltf_file_type_glb; | ||
| 1209 | (*out_data)->bin = bin; | ||
| 1210 | (*out_data)->bin_size = bin_size; | ||
| 1211 | |||
| 1212 | return cgltf_result_success; | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cgltf_data** out_data) | ||
| 1216 | { | ||
| 1217 | if (options == NULL) | ||
| 1218 | { | ||
| 1219 | return cgltf_result_invalid_options; | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; | ||
| 1223 | void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = options->file.release ? options->file.release : cgltf_default_file_release; | ||
| 1224 | |||
| 1225 | void* file_data = NULL; | ||
| 1226 | cgltf_size file_size = 0; | ||
| 1227 | cgltf_result result = file_read(&options->memory, &options->file, path, &file_size, &file_data); | ||
| 1228 | if (result != cgltf_result_success) | ||
| 1229 | { | ||
| 1230 | return result; | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | result = cgltf_parse(options, file_data, file_size, out_data); | ||
| 1234 | |||
| 1235 | if (result != cgltf_result_success) | ||
| 1236 | { | ||
| 1237 | file_release(&options->memory, &options->file, file_data); | ||
| 1238 | return result; | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | (*out_data)->file_data = file_data; | ||
| 1242 | |||
| 1243 | return cgltf_result_success; | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | static void cgltf_combine_paths(char* path, const char* base, const char* uri) | ||
| 1247 | { | ||
| 1248 | const char* s0 = strrchr(base, '/'); | ||
| 1249 | const char* s1 = strrchr(base, '\\'); | ||
| 1250 | const char* slash = s0 ? (s1 && s1 > s0 ? s1 : s0) : s1; | ||
| 1251 | |||
| 1252 | if (slash) | ||
| 1253 | { | ||
| 1254 | size_t prefix = slash - base + 1; | ||
| 1255 | |||
| 1256 | strncpy(path, base, prefix); | ||
| 1257 | strcpy(path + prefix, uri); | ||
| 1258 | } | ||
| 1259 | else | ||
| 1260 | { | ||
| 1261 | strcpy(path, uri); | ||
| 1262 | } | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | static cgltf_result cgltf_load_buffer_file(const cgltf_options* options, cgltf_size size, const char* uri, const char* gltf_path, void** out_data) | ||
| 1266 | { | ||
| 1267 | void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc_func ? options->memory.alloc_func : &cgltf_default_alloc; | ||
| 1268 | void (*memory_free)(void*, void*) = options->memory.free_func ? options->memory.free_func : &cgltf_default_free; | ||
| 1269 | cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; | ||
| 1270 | |||
| 1271 | char* path = (char*)memory_alloc(options->memory.user_data, strlen(uri) + strlen(gltf_path) + 1); | ||
| 1272 | if (!path) | ||
| 1273 | { | ||
| 1274 | return cgltf_result_out_of_memory; | ||
| 1275 | } | ||
| 1276 | |||
| 1277 | cgltf_combine_paths(path, gltf_path, uri); | ||
| 1278 | |||
| 1279 | // after combining, the tail of the resulting path is a uri; decode_uri converts it into path | ||
| 1280 | cgltf_decode_uri(path + strlen(path) - strlen(uri)); | ||
| 1281 | |||
| 1282 | void* file_data = NULL; | ||
| 1283 | cgltf_result result = file_read(&options->memory, &options->file, path, &size, &file_data); | ||
| 1284 | |||
| 1285 | memory_free(options->memory.user_data, path); | ||
| 1286 | |||
| 1287 | *out_data = (result == cgltf_result_success) ? file_data : NULL; | ||
| 1288 | |||
| 1289 | return result; | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data) | ||
| 1293 | { | ||
| 1294 | void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc_func ? options->memory.alloc_func : &cgltf_default_alloc; | ||
| 1295 | void (*memory_free)(void*, void*) = options->memory.free_func ? options->memory.free_func : &cgltf_default_free; | ||
| 1296 | |||
| 1297 | unsigned char* data = (unsigned char*)memory_alloc(options->memory.user_data, size); | ||
| 1298 | if (!data) | ||
| 1299 | { | ||
| 1300 | return cgltf_result_out_of_memory; | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | unsigned int buffer = 0; | ||
| 1304 | unsigned int buffer_bits = 0; | ||
| 1305 | |||
| 1306 | for (cgltf_size i = 0; i < size; ++i) | ||
| 1307 | { | ||
| 1308 | while (buffer_bits < 8) | ||
| 1309 | { | ||
| 1310 | char ch = *base64++; | ||
| 1311 | |||
| 1312 | int index = | ||
| 1313 | (unsigned)(ch - 'A') < 26 ? (ch - 'A') : | ||
| 1314 | (unsigned)(ch - 'a') < 26 ? (ch - 'a') + 26 : | ||
| 1315 | (unsigned)(ch - '0') < 10 ? (ch - '0') + 52 : | ||
| 1316 | ch == '+' ? 62 : | ||
| 1317 | ch == '/' ? 63 : | ||
| 1318 | -1; | ||
| 1319 | |||
| 1320 | if (index < 0) | ||
| 1321 | { | ||
| 1322 | memory_free(options->memory.user_data, data); | ||
| 1323 | return cgltf_result_io_error; | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | buffer = (buffer << 6) | index; | ||
| 1327 | buffer_bits += 6; | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | data[i] = (unsigned char)(buffer >> (buffer_bits - 8)); | ||
| 1331 | buffer_bits -= 8; | ||
| 1332 | } | ||
| 1333 | |||
| 1334 | *out_data = data; | ||
| 1335 | |||
| 1336 | return cgltf_result_success; | ||
| 1337 | } | ||
| 1338 | |||
| 1339 | static int cgltf_unhex(char ch) | ||
| 1340 | { | ||
| 1341 | return | ||
| 1342 | (unsigned)(ch - '0') < 10 ? (ch - '0') : | ||
| 1343 | (unsigned)(ch - 'A') < 6 ? (ch - 'A') + 10 : | ||
| 1344 | (unsigned)(ch - 'a') < 6 ? (ch - 'a') + 10 : | ||
| 1345 | -1; | ||
| 1346 | } | ||
| 1347 | |||
| 1348 | cgltf_size cgltf_decode_string(char* string) | ||
| 1349 | { | ||
| 1350 | char* read = string + strcspn(string, "\\"); | ||
| 1351 | if (*read == 0) | ||
| 1352 | { | ||
| 1353 | return read - string; | ||
| 1354 | } | ||
| 1355 | char* write = string; | ||
| 1356 | char* last = string; | ||
| 1357 | |||
| 1358 | for (;;) | ||
| 1359 | { | ||
| 1360 | // Copy characters since last escaped sequence | ||
| 1361 | cgltf_size written = read - last; | ||
| 1362 | memmove(write, last, written); | ||
| 1363 | write += written; | ||
| 1364 | |||
| 1365 | if (*read++ == 0) | ||
| 1366 | { | ||
| 1367 | break; | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | // jsmn already checked that all escape sequences are valid | ||
| 1371 | switch (*read++) | ||
| 1372 | { | ||
| 1373 | case '\"': *write++ = '\"'; break; | ||
| 1374 | case '/': *write++ = '/'; break; | ||
| 1375 | case '\\': *write++ = '\\'; break; | ||
| 1376 | case 'b': *write++ = '\b'; break; | ||
| 1377 | case 'f': *write++ = '\f'; break; | ||
| 1378 | case 'r': *write++ = '\r'; break; | ||
| 1379 | case 'n': *write++ = '\n'; break; | ||
| 1380 | case 't': *write++ = '\t'; break; | ||
| 1381 | case 'u': | ||
| 1382 | { | ||
| 1383 | // UCS-2 codepoint \uXXXX to UTF-8 | ||
| 1384 | int character = 0; | ||
| 1385 | for (cgltf_size i = 0; i < 4; ++i) | ||
| 1386 | { | ||
| 1387 | character = (character << 4) + cgltf_unhex(*read++); | ||
| 1388 | } | ||
| 1389 | |||
| 1390 | if (character <= 0x7F) | ||
| 1391 | { | ||
| 1392 | *write++ = character & 0xFF; | ||
| 1393 | } | ||
| 1394 | else if (character <= 0x7FF) | ||
| 1395 | { | ||
| 1396 | *write++ = 0xC0 | ((character >> 6) & 0xFF); | ||
| 1397 | *write++ = 0x80 | (character & 0x3F); | ||
| 1398 | } | ||
| 1399 | else | ||
| 1400 | { | ||
| 1401 | *write++ = 0xE0 | ((character >> 12) & 0xFF); | ||
| 1402 | *write++ = 0x80 | ((character >> 6) & 0x3F); | ||
| 1403 | *write++ = 0x80 | (character & 0x3F); | ||
| 1404 | } | ||
| 1405 | break; | ||
| 1406 | } | ||
| 1407 | default: | ||
| 1408 | break; | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | last = read; | ||
| 1412 | read += strcspn(read, "\\"); | ||
| 1413 | } | ||
| 1414 | |||
| 1415 | *write = 0; | ||
| 1416 | return write - string; | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | cgltf_size cgltf_decode_uri(char* uri) | ||
| 1420 | { | ||
| 1421 | char* write = uri; | ||
| 1422 | char* i = uri; | ||
| 1423 | |||
| 1424 | while (*i) | ||
| 1425 | { | ||
| 1426 | if (*i == '%') | ||
| 1427 | { | ||
| 1428 | int ch1 = cgltf_unhex(i[1]); | ||
| 1429 | |||
| 1430 | if (ch1 >= 0) | ||
| 1431 | { | ||
| 1432 | int ch2 = cgltf_unhex(i[2]); | ||
| 1433 | |||
| 1434 | if (ch2 >= 0) | ||
| 1435 | { | ||
| 1436 | *write++ = (char)(ch1 * 16 + ch2); | ||
| 1437 | i += 3; | ||
| 1438 | continue; | ||
| 1439 | } | ||
| 1440 | } | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | *write++ = *i++; | ||
| 1444 | } | ||
| 1445 | |||
| 1446 | *write = 0; | ||
| 1447 | return write - uri; | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, const char* gltf_path) | ||
| 1451 | { | ||
| 1452 | if (options == NULL) | ||
| 1453 | { | ||
| 1454 | return cgltf_result_invalid_options; | ||
| 1455 | } | ||
| 1456 | |||
| 1457 | if (data->buffers_count && data->buffers[0].data == NULL && data->buffers[0].uri == NULL && data->bin) | ||
| 1458 | { | ||
| 1459 | if (data->bin_size < data->buffers[0].size) | ||
| 1460 | { | ||
| 1461 | return cgltf_result_data_too_short; | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | data->buffers[0].data = (void*)data->bin; | ||
| 1465 | data->buffers[0].data_free_method = cgltf_data_free_method_none; | ||
| 1466 | } | ||
| 1467 | |||
| 1468 | for (cgltf_size i = 0; i < data->buffers_count; ++i) | ||
| 1469 | { | ||
| 1470 | if (data->buffers[i].data) | ||
| 1471 | { | ||
| 1472 | continue; | ||
| 1473 | } | ||
| 1474 | |||
| 1475 | const char* uri = data->buffers[i].uri; | ||
| 1476 | |||
| 1477 | if (uri == NULL) | ||
| 1478 | { | ||
| 1479 | continue; | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | if (strncmp(uri, "data:", 5) == 0) | ||
| 1483 | { | ||
| 1484 | const char* comma = strchr(uri, ','); | ||
| 1485 | |||
| 1486 | if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0) | ||
| 1487 | { | ||
| 1488 | cgltf_result res = cgltf_load_buffer_base64(options, data->buffers[i].size, comma + 1, &data->buffers[i].data); | ||
| 1489 | data->buffers[i].data_free_method = cgltf_data_free_method_memory_free; | ||
| 1490 | |||
| 1491 | if (res != cgltf_result_success) | ||
| 1492 | { | ||
| 1493 | return res; | ||
| 1494 | } | ||
| 1495 | } | ||
| 1496 | else | ||
| 1497 | { | ||
| 1498 | return cgltf_result_unknown_format; | ||
| 1499 | } | ||
| 1500 | } | ||
| 1501 | else if (strstr(uri, "://") == NULL && gltf_path) | ||
| 1502 | { | ||
| 1503 | cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data); | ||
| 1504 | data->buffers[i].data_free_method = cgltf_data_free_method_file_release; | ||
| 1505 | |||
| 1506 | if (res != cgltf_result_success) | ||
| 1507 | { | ||
| 1508 | return res; | ||
| 1509 | } | ||
| 1510 | } | ||
| 1511 | else | ||
| 1512 | { | ||
| 1513 | return cgltf_result_unknown_format; | ||
| 1514 | } | ||
| 1515 | } | ||
| 1516 | |||
| 1517 | return cgltf_result_success; | ||
| 1518 | } | ||
| 1519 | |||
| 1520 | static cgltf_size cgltf_calc_index_bound(cgltf_buffer_view* buffer_view, cgltf_size offset, cgltf_component_type component_type, cgltf_size count) | ||
| 1521 | { | ||
| 1522 | char* data = (char*)buffer_view->buffer->data + offset + buffer_view->offset; | ||
| 1523 | cgltf_size bound = 0; | ||
| 1524 | |||
| 1525 | switch (component_type) | ||
| 1526 | { | ||
| 1527 | case cgltf_component_type_r_8u: | ||
| 1528 | for (size_t i = 0; i < count; ++i) | ||
| 1529 | { | ||
| 1530 | cgltf_size v = ((unsigned char*)data)[i]; | ||
| 1531 | bound = bound > v ? bound : v; | ||
| 1532 | } | ||
| 1533 | break; | ||
| 1534 | |||
| 1535 | case cgltf_component_type_r_16u: | ||
| 1536 | for (size_t i = 0; i < count; ++i) | ||
| 1537 | { | ||
| 1538 | cgltf_size v = ((unsigned short*)data)[i]; | ||
| 1539 | bound = bound > v ? bound : v; | ||
| 1540 | } | ||
| 1541 | break; | ||
| 1542 | |||
| 1543 | case cgltf_component_type_r_32u: | ||
| 1544 | for (size_t i = 0; i < count; ++i) | ||
| 1545 | { | ||
| 1546 | cgltf_size v = ((unsigned int*)data)[i]; | ||
| 1547 | bound = bound > v ? bound : v; | ||
| 1548 | } | ||
| 1549 | break; | ||
| 1550 | |||
| 1551 | default: | ||
| 1552 | ; | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | return bound; | ||
| 1556 | } | ||
| 1557 | |||
| 1558 | #if CGLTF_VALIDATE_ENABLE_ASSERTS | ||
| 1559 | #define CGLTF_ASSERT_IF(cond, result) assert(!(cond)); if (cond) return result; | ||
| 1560 | #else | ||
| 1561 | #define CGLTF_ASSERT_IF(cond, result) if (cond) return result; | ||
| 1562 | #endif | ||
| 1563 | |||
| 1564 | cgltf_result cgltf_validate(cgltf_data* data) | ||
| 1565 | { | ||
| 1566 | for (cgltf_size i = 0; i < data->accessors_count; ++i) | ||
| 1567 | { | ||
| 1568 | cgltf_accessor* accessor = &data->accessors[i]; | ||
| 1569 | |||
| 1570 | CGLTF_ASSERT_IF(data->accessors[i].component_type == cgltf_component_type_invalid, cgltf_result_invalid_gltf); | ||
| 1571 | CGLTF_ASSERT_IF(data->accessors[i].type == cgltf_type_invalid, cgltf_result_invalid_gltf); | ||
| 1572 | |||
| 1573 | cgltf_size element_size = cgltf_calc_size(accessor->type, accessor->component_type); | ||
| 1574 | |||
| 1575 | if (accessor->buffer_view) | ||
| 1576 | { | ||
| 1577 | cgltf_size req_size = accessor->offset + accessor->stride * (accessor->count - 1) + element_size; | ||
| 1578 | |||
| 1579 | CGLTF_ASSERT_IF(accessor->buffer_view->size < req_size, cgltf_result_data_too_short); | ||
| 1580 | } | ||
| 1581 | |||
| 1582 | if (accessor->is_sparse) | ||
| 1583 | { | ||
| 1584 | cgltf_accessor_sparse* sparse = &accessor->sparse; | ||
| 1585 | |||
| 1586 | cgltf_size indices_component_size = cgltf_component_size(sparse->indices_component_type); | ||
| 1587 | cgltf_size indices_req_size = sparse->indices_byte_offset + indices_component_size * sparse->count; | ||
| 1588 | cgltf_size values_req_size = sparse->values_byte_offset + element_size * sparse->count; | ||
| 1589 | |||
| 1590 | CGLTF_ASSERT_IF(sparse->indices_buffer_view->size < indices_req_size || | ||
| 1591 | sparse->values_buffer_view->size < values_req_size, cgltf_result_data_too_short); | ||
| 1592 | |||
| 1593 | CGLTF_ASSERT_IF(sparse->indices_component_type != cgltf_component_type_r_8u && | ||
| 1594 | sparse->indices_component_type != cgltf_component_type_r_16u && | ||
| 1595 | sparse->indices_component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); | ||
| 1596 | |||
| 1597 | if (sparse->indices_buffer_view->buffer->data) | ||
| 1598 | { | ||
| 1599 | cgltf_size index_bound = cgltf_calc_index_bound(sparse->indices_buffer_view, sparse->indices_byte_offset, sparse->indices_component_type, sparse->count); | ||
| 1600 | |||
| 1601 | CGLTF_ASSERT_IF(index_bound >= accessor->count, cgltf_result_data_too_short); | ||
| 1602 | } | ||
| 1603 | } | ||
| 1604 | } | ||
| 1605 | |||
| 1606 | for (cgltf_size i = 0; i < data->buffer_views_count; ++i) | ||
| 1607 | { | ||
| 1608 | cgltf_size req_size = data->buffer_views[i].offset + data->buffer_views[i].size; | ||
| 1609 | |||
| 1610 | CGLTF_ASSERT_IF(data->buffer_views[i].buffer && data->buffer_views[i].buffer->size < req_size, cgltf_result_data_too_short); | ||
| 1611 | |||
| 1612 | if (data->buffer_views[i].has_meshopt_compression) | ||
| 1613 | { | ||
| 1614 | cgltf_meshopt_compression* mc = &data->buffer_views[i].meshopt_compression; | ||
| 1615 | |||
| 1616 | CGLTF_ASSERT_IF(mc->buffer == NULL || mc->buffer->size < mc->offset + mc->size, cgltf_result_data_too_short); | ||
| 1617 | |||
| 1618 | CGLTF_ASSERT_IF(data->buffer_views[i].stride && mc->stride != data->buffer_views[i].stride, cgltf_result_invalid_gltf); | ||
| 1619 | |||
| 1620 | CGLTF_ASSERT_IF(data->buffer_views[i].size != mc->stride * mc->count, cgltf_result_invalid_gltf); | ||
| 1621 | |||
| 1622 | CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_invalid, cgltf_result_invalid_gltf); | ||
| 1623 | |||
| 1624 | CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_attributes && !(mc->stride % 4 == 0 && mc->stride <= 256), cgltf_result_invalid_gltf); | ||
| 1625 | |||
| 1626 | CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_triangles && mc->count % 3 != 0, cgltf_result_invalid_gltf); | ||
| 1627 | |||
| 1628 | CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->stride != 2 && mc->stride != 4, cgltf_result_invalid_gltf); | ||
| 1629 | |||
| 1630 | CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->filter != cgltf_meshopt_compression_filter_none, cgltf_result_invalid_gltf); | ||
| 1631 | |||
| 1632 | CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_octahedral && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf); | ||
| 1633 | |||
| 1634 | CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_quaternion && mc->stride != 8, cgltf_result_invalid_gltf); | ||
| 1635 | } | ||
| 1636 | } | ||
| 1637 | |||
| 1638 | for (cgltf_size i = 0; i < data->meshes_count; ++i) | ||
| 1639 | { | ||
| 1640 | if (data->meshes[i].weights) | ||
| 1641 | { | ||
| 1642 | CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].weights_count, cgltf_result_invalid_gltf); | ||
| 1643 | } | ||
| 1644 | |||
| 1645 | if (data->meshes[i].target_names) | ||
| 1646 | { | ||
| 1647 | CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].target_names_count, cgltf_result_invalid_gltf); | ||
| 1648 | } | ||
| 1649 | |||
| 1650 | for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) | ||
| 1651 | { | ||
| 1652 | CGLTF_ASSERT_IF(data->meshes[i].primitives[j].type == cgltf_primitive_type_invalid, cgltf_result_invalid_gltf); | ||
| 1653 | CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets_count != data->meshes[i].primitives[0].targets_count, cgltf_result_invalid_gltf); | ||
| 1654 | |||
| 1655 | CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes_count == 0, cgltf_result_invalid_gltf); | ||
| 1656 | |||
| 1657 | cgltf_accessor* first = data->meshes[i].primitives[j].attributes[0].data; | ||
| 1658 | |||
| 1659 | CGLTF_ASSERT_IF(first->count == 0, cgltf_result_invalid_gltf); | ||
| 1660 | |||
| 1661 | for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) | ||
| 1662 | { | ||
| 1663 | CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes[k].data->count != first->count, cgltf_result_invalid_gltf); | ||
| 1664 | } | ||
| 1665 | |||
| 1666 | for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) | ||
| 1667 | { | ||
| 1668 | for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) | ||
| 1669 | { | ||
| 1670 | CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets[k].attributes[m].data->count != first->count, cgltf_result_invalid_gltf); | ||
| 1671 | } | ||
| 1672 | } | ||
| 1673 | |||
| 1674 | cgltf_accessor* indices = data->meshes[i].primitives[j].indices; | ||
| 1675 | |||
| 1676 | CGLTF_ASSERT_IF(indices && | ||
| 1677 | indices->component_type != cgltf_component_type_r_8u && | ||
| 1678 | indices->component_type != cgltf_component_type_r_16u && | ||
| 1679 | indices->component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); | ||
| 1680 | |||
| 1681 | CGLTF_ASSERT_IF(indices && indices->type != cgltf_type_scalar, cgltf_result_invalid_gltf); | ||
| 1682 | CGLTF_ASSERT_IF(indices && indices->stride != cgltf_component_size(indices->component_type), cgltf_result_invalid_gltf); | ||
| 1683 | |||
| 1684 | if (indices && indices->buffer_view && indices->buffer_view->buffer->data) | ||
| 1685 | { | ||
| 1686 | cgltf_size index_bound = cgltf_calc_index_bound(indices->buffer_view, indices->offset, indices->component_type, indices->count); | ||
| 1687 | |||
| 1688 | CGLTF_ASSERT_IF(index_bound >= first->count, cgltf_result_data_too_short); | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) | ||
| 1692 | { | ||
| 1693 | CGLTF_ASSERT_IF(data->meshes[i].primitives[j].mappings[k].variant >= data->variants_count, cgltf_result_invalid_gltf); | ||
| 1694 | } | ||
| 1695 | } | ||
| 1696 | } | ||
| 1697 | |||
| 1698 | for (cgltf_size i = 0; i < data->nodes_count; ++i) | ||
| 1699 | { | ||
| 1700 | if (data->nodes[i].weights && data->nodes[i].mesh) | ||
| 1701 | { | ||
| 1702 | CGLTF_ASSERT_IF(data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf); | ||
| 1703 | } | ||
| 1704 | |||
| 1705 | if (data->nodes[i].has_mesh_gpu_instancing) | ||
| 1706 | { | ||
| 1707 | CGLTF_ASSERT_IF(data->nodes[i].mesh == NULL, cgltf_result_invalid_gltf); | ||
| 1708 | CGLTF_ASSERT_IF(data->nodes[i].mesh_gpu_instancing.attributes_count == 0, cgltf_result_invalid_gltf); | ||
| 1709 | |||
| 1710 | cgltf_accessor* first = data->nodes[i].mesh_gpu_instancing.attributes[0].data; | ||
| 1711 | |||
| 1712 | for (cgltf_size k = 0; k < data->nodes[i].mesh_gpu_instancing.attributes_count; ++k) | ||
| 1713 | { | ||
| 1714 | CGLTF_ASSERT_IF(data->nodes[i].mesh_gpu_instancing.attributes[k].data->count != first->count, cgltf_result_invalid_gltf); | ||
| 1715 | } | ||
| 1716 | } | ||
| 1717 | } | ||
| 1718 | |||
| 1719 | for (cgltf_size i = 0; i < data->nodes_count; ++i) | ||
| 1720 | { | ||
| 1721 | cgltf_node* p1 = data->nodes[i].parent; | ||
| 1722 | cgltf_node* p2 = p1 ? p1->parent : NULL; | ||
| 1723 | |||
| 1724 | while (p1 && p2) | ||
| 1725 | { | ||
| 1726 | CGLTF_ASSERT_IF(p1 == p2, cgltf_result_invalid_gltf); | ||
| 1727 | |||
| 1728 | p1 = p1->parent; | ||
| 1729 | p2 = p2->parent ? p2->parent->parent : NULL; | ||
| 1730 | } | ||
| 1731 | } | ||
| 1732 | |||
| 1733 | for (cgltf_size i = 0; i < data->scenes_count; ++i) | ||
| 1734 | { | ||
| 1735 | for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j) | ||
| 1736 | { | ||
| 1737 | CGLTF_ASSERT_IF(data->scenes[i].nodes[j]->parent, cgltf_result_invalid_gltf); | ||
| 1738 | } | ||
| 1739 | } | ||
| 1740 | |||
| 1741 | for (cgltf_size i = 0; i < data->animations_count; ++i) | ||
| 1742 | { | ||
| 1743 | for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) | ||
| 1744 | { | ||
| 1745 | cgltf_animation_channel* channel = &data->animations[i].channels[j]; | ||
| 1746 | |||
| 1747 | if (!channel->target_node) | ||
| 1748 | { | ||
| 1749 | continue; | ||
| 1750 | } | ||
| 1751 | |||
| 1752 | cgltf_size components = 1; | ||
| 1753 | |||
| 1754 | if (channel->target_path == cgltf_animation_path_type_weights) | ||
| 1755 | { | ||
| 1756 | CGLTF_ASSERT_IF(!channel->target_node->mesh || !channel->target_node->mesh->primitives_count, cgltf_result_invalid_gltf); | ||
| 1757 | |||
| 1758 | components = channel->target_node->mesh->primitives[0].targets_count; | ||
| 1759 | } | ||
| 1760 | |||
| 1761 | cgltf_size values = channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline ? 3 : 1; | ||
| 1762 | |||
| 1763 | CGLTF_ASSERT_IF(channel->sampler->input->count * components * values != channel->sampler->output->count, cgltf_result_invalid_gltf); | ||
| 1764 | } | ||
| 1765 | } | ||
| 1766 | |||
| 1767 | for (cgltf_size i = 0; i < data->variants_count; ++i) | ||
| 1768 | { | ||
| 1769 | CGLTF_ASSERT_IF(!data->variants[i].name, cgltf_result_invalid_gltf); | ||
| 1770 | } | ||
| 1771 | |||
| 1772 | return cgltf_result_success; | ||
| 1773 | } | ||
| 1774 | |||
| 1775 | cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size) | ||
| 1776 | { | ||
| 1777 | cgltf_size json_size = extras->end_offset - extras->start_offset; | ||
| 1778 | |||
| 1779 | if (!dest) | ||
| 1780 | { | ||
| 1781 | if (dest_size) | ||
| 1782 | { | ||
| 1783 | *dest_size = json_size + 1; | ||
| 1784 | return cgltf_result_success; | ||
| 1785 | } | ||
| 1786 | return cgltf_result_invalid_options; | ||
| 1787 | } | ||
| 1788 | |||
| 1789 | if (*dest_size + 1 < json_size) | ||
| 1790 | { | ||
| 1791 | strncpy(dest, data->json + extras->start_offset, *dest_size - 1); | ||
| 1792 | dest[*dest_size - 1] = 0; | ||
| 1793 | } | ||
| 1794 | else | ||
| 1795 | { | ||
| 1796 | strncpy(dest, data->json + extras->start_offset, json_size); | ||
| 1797 | dest[json_size] = 0; | ||
| 1798 | } | ||
| 1799 | |||
| 1800 | return cgltf_result_success; | ||
| 1801 | } | ||
| 1802 | |||
| 1803 | static void cgltf_free_extras(cgltf_data* data, cgltf_extras* extras) | ||
| 1804 | { | ||
| 1805 | data->memory.free_func(data->memory.user_data, extras->data); | ||
| 1806 | } | ||
| 1807 | |||
| 1808 | static void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count) | ||
| 1809 | { | ||
| 1810 | for (cgltf_size i = 0; i < extensions_count; ++i) | ||
| 1811 | { | ||
| 1812 | data->memory.free_func(data->memory.user_data, extensions[i].name); | ||
| 1813 | data->memory.free_func(data->memory.user_data, extensions[i].data); | ||
| 1814 | } | ||
| 1815 | data->memory.free_func(data->memory.user_data, extensions); | ||
| 1816 | } | ||
| 1817 | |||
| 1818 | void cgltf_free(cgltf_data* data) | ||
| 1819 | { | ||
| 1820 | if (!data) | ||
| 1821 | { | ||
| 1822 | return; | ||
| 1823 | } | ||
| 1824 | |||
| 1825 | void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = data->file.release ? data->file.release : cgltf_default_file_release; | ||
| 1826 | |||
| 1827 | data->memory.free_func(data->memory.user_data, data->asset.copyright); | ||
| 1828 | data->memory.free_func(data->memory.user_data, data->asset.generator); | ||
| 1829 | data->memory.free_func(data->memory.user_data, data->asset.version); | ||
| 1830 | data->memory.free_func(data->memory.user_data, data->asset.min_version); | ||
| 1831 | |||
| 1832 | cgltf_free_extensions(data, data->asset.extensions, data->asset.extensions_count); | ||
| 1833 | cgltf_free_extras(data, &data->asset.extras); | ||
| 1834 | |||
| 1835 | for (cgltf_size i = 0; i < data->accessors_count; ++i) | ||
| 1836 | { | ||
| 1837 | data->memory.free_func(data->memory.user_data, data->accessors[i].name); | ||
| 1838 | |||
| 1839 | cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count); | ||
| 1840 | cgltf_free_extras(data, &data->accessors[i].extras); | ||
| 1841 | } | ||
| 1842 | data->memory.free_func(data->memory.user_data, data->accessors); | ||
| 1843 | |||
| 1844 | for (cgltf_size i = 0; i < data->buffer_views_count; ++i) | ||
| 1845 | { | ||
| 1846 | data->memory.free_func(data->memory.user_data, data->buffer_views[i].name); | ||
| 1847 | data->memory.free_func(data->memory.user_data, data->buffer_views[i].data); | ||
| 1848 | |||
| 1849 | cgltf_free_extensions(data, data->buffer_views[i].extensions, data->buffer_views[i].extensions_count); | ||
| 1850 | cgltf_free_extras(data, &data->buffer_views[i].extras); | ||
| 1851 | } | ||
| 1852 | data->memory.free_func(data->memory.user_data, data->buffer_views); | ||
| 1853 | |||
| 1854 | for (cgltf_size i = 0; i < data->buffers_count; ++i) | ||
| 1855 | { | ||
| 1856 | data->memory.free_func(data->memory.user_data, data->buffers[i].name); | ||
| 1857 | |||
| 1858 | if (data->buffers[i].data_free_method == cgltf_data_free_method_file_release) | ||
| 1859 | { | ||
| 1860 | file_release(&data->memory, &data->file, data->buffers[i].data); | ||
| 1861 | } | ||
| 1862 | else if (data->buffers[i].data_free_method == cgltf_data_free_method_memory_free) | ||
| 1863 | { | ||
| 1864 | data->memory.free_func(data->memory.user_data, data->buffers[i].data); | ||
| 1865 | } | ||
| 1866 | |||
| 1867 | data->memory.free_func(data->memory.user_data, data->buffers[i].uri); | ||
| 1868 | |||
| 1869 | cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count); | ||
| 1870 | cgltf_free_extras(data, &data->buffers[i].extras); | ||
| 1871 | } | ||
| 1872 | data->memory.free_func(data->memory.user_data, data->buffers); | ||
| 1873 | |||
| 1874 | for (cgltf_size i = 0; i < data->meshes_count; ++i) | ||
| 1875 | { | ||
| 1876 | data->memory.free_func(data->memory.user_data, data->meshes[i].name); | ||
| 1877 | |||
| 1878 | for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) | ||
| 1879 | { | ||
| 1880 | for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) | ||
| 1881 | { | ||
| 1882 | data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].attributes[k].name); | ||
| 1883 | } | ||
| 1884 | |||
| 1885 | data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].attributes); | ||
| 1886 | |||
| 1887 | for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) | ||
| 1888 | { | ||
| 1889 | for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) | ||
| 1890 | { | ||
| 1891 | data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes[m].name); | ||
| 1892 | } | ||
| 1893 | |||
| 1894 | data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes); | ||
| 1895 | } | ||
| 1896 | |||
| 1897 | data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets); | ||
| 1898 | |||
| 1899 | if (data->meshes[i].primitives[j].has_draco_mesh_compression) | ||
| 1900 | { | ||
| 1901 | for (cgltf_size k = 0; k < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++k) | ||
| 1902 | { | ||
| 1903 | data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes[k].name); | ||
| 1904 | } | ||
| 1905 | |||
| 1906 | data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes); | ||
| 1907 | } | ||
| 1908 | |||
| 1909 | for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) | ||
| 1910 | { | ||
| 1911 | cgltf_free_extras(data, &data->meshes[i].primitives[j].mappings[k].extras); | ||
| 1912 | } | ||
| 1913 | |||
| 1914 | data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].mappings); | ||
| 1915 | |||
| 1916 | cgltf_free_extensions(data, data->meshes[i].primitives[j].extensions, data->meshes[i].primitives[j].extensions_count); | ||
| 1917 | cgltf_free_extras(data, &data->meshes[i].primitives[j].extras); | ||
| 1918 | } | ||
| 1919 | |||
| 1920 | data->memory.free_func(data->memory.user_data, data->meshes[i].primitives); | ||
| 1921 | data->memory.free_func(data->memory.user_data, data->meshes[i].weights); | ||
| 1922 | |||
| 1923 | for (cgltf_size j = 0; j < data->meshes[i].target_names_count; ++j) | ||
| 1924 | { | ||
| 1925 | data->memory.free_func(data->memory.user_data, data->meshes[i].target_names[j]); | ||
| 1926 | } | ||
| 1927 | |||
| 1928 | cgltf_free_extensions(data, data->meshes[i].extensions, data->meshes[i].extensions_count); | ||
| 1929 | cgltf_free_extras(data, &data->meshes[i].extras); | ||
| 1930 | |||
| 1931 | data->memory.free_func(data->memory.user_data, data->meshes[i].target_names); | ||
| 1932 | } | ||
| 1933 | |||
| 1934 | data->memory.free_func(data->memory.user_data, data->meshes); | ||
| 1935 | |||
| 1936 | for (cgltf_size i = 0; i < data->materials_count; ++i) | ||
| 1937 | { | ||
| 1938 | data->memory.free_func(data->memory.user_data, data->materials[i].name); | ||
| 1939 | |||
| 1940 | cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count); | ||
| 1941 | cgltf_free_extras(data, &data->materials[i].extras); | ||
| 1942 | } | ||
| 1943 | |||
| 1944 | data->memory.free_func(data->memory.user_data, data->materials); | ||
| 1945 | |||
| 1946 | for (cgltf_size i = 0; i < data->images_count; ++i) | ||
| 1947 | { | ||
| 1948 | data->memory.free_func(data->memory.user_data, data->images[i].name); | ||
| 1949 | data->memory.free_func(data->memory.user_data, data->images[i].uri); | ||
| 1950 | data->memory.free_func(data->memory.user_data, data->images[i].mime_type); | ||
| 1951 | |||
| 1952 | cgltf_free_extensions(data, data->images[i].extensions, data->images[i].extensions_count); | ||
| 1953 | cgltf_free_extras(data, &data->images[i].extras); | ||
| 1954 | } | ||
| 1955 | |||
| 1956 | data->memory.free_func(data->memory.user_data, data->images); | ||
| 1957 | |||
| 1958 | for (cgltf_size i = 0; i < data->textures_count; ++i) | ||
| 1959 | { | ||
| 1960 | data->memory.free_func(data->memory.user_data, data->textures[i].name); | ||
| 1961 | |||
| 1962 | cgltf_free_extensions(data, data->textures[i].extensions, data->textures[i].extensions_count); | ||
| 1963 | cgltf_free_extras(data, &data->textures[i].extras); | ||
| 1964 | } | ||
| 1965 | |||
| 1966 | data->memory.free_func(data->memory.user_data, data->textures); | ||
| 1967 | |||
| 1968 | for (cgltf_size i = 0; i < data->samplers_count; ++i) | ||
| 1969 | { | ||
| 1970 | data->memory.free_func(data->memory.user_data, data->samplers[i].name); | ||
| 1971 | |||
| 1972 | cgltf_free_extensions(data, data->samplers[i].extensions, data->samplers[i].extensions_count); | ||
| 1973 | cgltf_free_extras(data, &data->samplers[i].extras); | ||
| 1974 | } | ||
| 1975 | |||
| 1976 | data->memory.free_func(data->memory.user_data, data->samplers); | ||
| 1977 | |||
| 1978 | for (cgltf_size i = 0; i < data->skins_count; ++i) | ||
| 1979 | { | ||
| 1980 | data->memory.free_func(data->memory.user_data, data->skins[i].name); | ||
| 1981 | data->memory.free_func(data->memory.user_data, data->skins[i].joints); | ||
| 1982 | |||
| 1983 | cgltf_free_extensions(data, data->skins[i].extensions, data->skins[i].extensions_count); | ||
| 1984 | cgltf_free_extras(data, &data->skins[i].extras); | ||
| 1985 | } | ||
| 1986 | |||
| 1987 | data->memory.free_func(data->memory.user_data, data->skins); | ||
| 1988 | |||
| 1989 | for (cgltf_size i = 0; i < data->cameras_count; ++i) | ||
| 1990 | { | ||
| 1991 | data->memory.free_func(data->memory.user_data, data->cameras[i].name); | ||
| 1992 | |||
| 1993 | if (data->cameras[i].type == cgltf_camera_type_perspective) | ||
| 1994 | { | ||
| 1995 | cgltf_free_extras(data, &data->cameras[i].data.perspective.extras); | ||
| 1996 | } | ||
| 1997 | else if (data->cameras[i].type == cgltf_camera_type_orthographic) | ||
| 1998 | { | ||
| 1999 | cgltf_free_extras(data, &data->cameras[i].data.orthographic.extras); | ||
| 2000 | } | ||
| 2001 | |||
| 2002 | cgltf_free_extensions(data, data->cameras[i].extensions, data->cameras[i].extensions_count); | ||
| 2003 | cgltf_free_extras(data, &data->cameras[i].extras); | ||
| 2004 | } | ||
| 2005 | |||
| 2006 | data->memory.free_func(data->memory.user_data, data->cameras); | ||
| 2007 | |||
| 2008 | for (cgltf_size i = 0; i < data->lights_count; ++i) | ||
| 2009 | { | ||
| 2010 | data->memory.free_func(data->memory.user_data, data->lights[i].name); | ||
| 2011 | |||
| 2012 | cgltf_free_extras(data, &data->lights[i].extras); | ||
| 2013 | } | ||
| 2014 | |||
| 2015 | data->memory.free_func(data->memory.user_data, data->lights); | ||
| 2016 | |||
| 2017 | for (cgltf_size i = 0; i < data->nodes_count; ++i) | ||
| 2018 | { | ||
| 2019 | data->memory.free_func(data->memory.user_data, data->nodes[i].name); | ||
| 2020 | data->memory.free_func(data->memory.user_data, data->nodes[i].children); | ||
| 2021 | data->memory.free_func(data->memory.user_data, data->nodes[i].weights); | ||
| 2022 | |||
| 2023 | if (data->nodes[i].has_mesh_gpu_instancing) | ||
| 2024 | { | ||
| 2025 | for (cgltf_size j = 0; j < data->nodes[i].mesh_gpu_instancing.attributes_count; ++j) | ||
| 2026 | { | ||
| 2027 | data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes[j].name); | ||
| 2028 | } | ||
| 2029 | |||
| 2030 | data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes); | ||
| 2031 | } | ||
| 2032 | |||
| 2033 | cgltf_free_extensions(data, data->nodes[i].extensions, data->nodes[i].extensions_count); | ||
| 2034 | cgltf_free_extras(data, &data->nodes[i].extras); | ||
| 2035 | } | ||
| 2036 | |||
| 2037 | data->memory.free_func(data->memory.user_data, data->nodes); | ||
| 2038 | |||
| 2039 | for (cgltf_size i = 0; i < data->scenes_count; ++i) | ||
| 2040 | { | ||
| 2041 | data->memory.free_func(data->memory.user_data, data->scenes[i].name); | ||
| 2042 | data->memory.free_func(data->memory.user_data, data->scenes[i].nodes); | ||
| 2043 | |||
| 2044 | cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count); | ||
| 2045 | cgltf_free_extras(data, &data->scenes[i].extras); | ||
| 2046 | } | ||
| 2047 | |||
| 2048 | data->memory.free_func(data->memory.user_data, data->scenes); | ||
| 2049 | |||
| 2050 | for (cgltf_size i = 0; i < data->animations_count; ++i) | ||
| 2051 | { | ||
| 2052 | data->memory.free_func(data->memory.user_data, data->animations[i].name); | ||
| 2053 | for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) | ||
| 2054 | { | ||
| 2055 | cgltf_free_extensions(data, data->animations[i].samplers[j].extensions, data->animations[i].samplers[j].extensions_count); | ||
| 2056 | cgltf_free_extras(data, &data->animations[i].samplers[j].extras); | ||
| 2057 | } | ||
| 2058 | data->memory.free_func(data->memory.user_data, data->animations[i].samplers); | ||
| 2059 | |||
| 2060 | for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) | ||
| 2061 | { | ||
| 2062 | cgltf_free_extensions(data, data->animations[i].channels[j].extensions, data->animations[i].channels[j].extensions_count); | ||
| 2063 | cgltf_free_extras(data, &data->animations[i].channels[j].extras); | ||
| 2064 | } | ||
| 2065 | data->memory.free_func(data->memory.user_data, data->animations[i].channels); | ||
| 2066 | |||
| 2067 | cgltf_free_extensions(data, data->animations[i].extensions, data->animations[i].extensions_count); | ||
| 2068 | cgltf_free_extras(data, &data->animations[i].extras); | ||
| 2069 | } | ||
| 2070 | |||
| 2071 | data->memory.free_func(data->memory.user_data, data->animations); | ||
| 2072 | |||
| 2073 | for (cgltf_size i = 0; i < data->variants_count; ++i) | ||
| 2074 | { | ||
| 2075 | data->memory.free_func(data->memory.user_data, data->variants[i].name); | ||
| 2076 | |||
| 2077 | cgltf_free_extras(data, &data->variants[i].extras); | ||
| 2078 | } | ||
| 2079 | |||
| 2080 | data->memory.free_func(data->memory.user_data, data->variants); | ||
| 2081 | |||
| 2082 | cgltf_free_extensions(data, data->data_extensions, data->data_extensions_count); | ||
| 2083 | cgltf_free_extras(data, &data->extras); | ||
| 2084 | |||
| 2085 | for (cgltf_size i = 0; i < data->extensions_used_count; ++i) | ||
| 2086 | { | ||
| 2087 | data->memory.free_func(data->memory.user_data, data->extensions_used[i]); | ||
| 2088 | } | ||
| 2089 | |||
| 2090 | data->memory.free_func(data->memory.user_data, data->extensions_used); | ||
| 2091 | |||
| 2092 | for (cgltf_size i = 0; i < data->extensions_required_count; ++i) | ||
| 2093 | { | ||
| 2094 | data->memory.free_func(data->memory.user_data, data->extensions_required[i]); | ||
| 2095 | } | ||
| 2096 | |||
| 2097 | data->memory.free_func(data->memory.user_data, data->extensions_required); | ||
| 2098 | |||
| 2099 | file_release(&data->memory, &data->file, data->file_data); | ||
| 2100 | |||
| 2101 | data->memory.free_func(data->memory.user_data, data); | ||
| 2102 | } | ||
| 2103 | |||
| 2104 | void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix) | ||
| 2105 | { | ||
| 2106 | cgltf_float* lm = out_matrix; | ||
| 2107 | |||
| 2108 | if (node->has_matrix) | ||
| 2109 | { | ||
| 2110 | memcpy(lm, node->matrix, sizeof(float) * 16); | ||
| 2111 | } | ||
| 2112 | else | ||
| 2113 | { | ||
| 2114 | float tx = node->translation[0]; | ||
| 2115 | float ty = node->translation[1]; | ||
| 2116 | float tz = node->translation[2]; | ||
| 2117 | |||
| 2118 | float qx = node->rotation[0]; | ||
| 2119 | float qy = node->rotation[1]; | ||
| 2120 | float qz = node->rotation[2]; | ||
| 2121 | float qw = node->rotation[3]; | ||
| 2122 | |||
| 2123 | float sx = node->scale[0]; | ||
| 2124 | float sy = node->scale[1]; | ||
| 2125 | float sz = node->scale[2]; | ||
| 2126 | |||
| 2127 | lm[0] = (1 - 2 * qy*qy - 2 * qz*qz) * sx; | ||
| 2128 | lm[1] = (2 * qx*qy + 2 * qz*qw) * sx; | ||
| 2129 | lm[2] = (2 * qx*qz - 2 * qy*qw) * sx; | ||
| 2130 | lm[3] = 0.f; | ||
| 2131 | |||
| 2132 | lm[4] = (2 * qx*qy - 2 * qz*qw) * sy; | ||
| 2133 | lm[5] = (1 - 2 * qx*qx - 2 * qz*qz) * sy; | ||
| 2134 | lm[6] = (2 * qy*qz + 2 * qx*qw) * sy; | ||
| 2135 | lm[7] = 0.f; | ||
| 2136 | |||
| 2137 | lm[8] = (2 * qx*qz + 2 * qy*qw) * sz; | ||
| 2138 | lm[9] = (2 * qy*qz - 2 * qx*qw) * sz; | ||
| 2139 | lm[10] = (1 - 2 * qx*qx - 2 * qy*qy) * sz; | ||
| 2140 | lm[11] = 0.f; | ||
| 2141 | |||
| 2142 | lm[12] = tx; | ||
| 2143 | lm[13] = ty; | ||
| 2144 | lm[14] = tz; | ||
| 2145 | lm[15] = 1.f; | ||
| 2146 | } | ||
| 2147 | } | ||
| 2148 | |||
| 2149 | void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix) | ||
| 2150 | { | ||
| 2151 | cgltf_float* lm = out_matrix; | ||
| 2152 | cgltf_node_transform_local(node, lm); | ||
| 2153 | |||
| 2154 | const cgltf_node* parent = node->parent; | ||
| 2155 | |||
| 2156 | while (parent) | ||
| 2157 | { | ||
| 2158 | float pm[16]; | ||
| 2159 | cgltf_node_transform_local(parent, pm); | ||
| 2160 | |||
| 2161 | for (int i = 0; i < 4; ++i) | ||
| 2162 | { | ||
| 2163 | float l0 = lm[i * 4 + 0]; | ||
| 2164 | float l1 = lm[i * 4 + 1]; | ||
| 2165 | float l2 = lm[i * 4 + 2]; | ||
| 2166 | |||
| 2167 | float r0 = l0 * pm[0] + l1 * pm[4] + l2 * pm[8]; | ||
| 2168 | float r1 = l0 * pm[1] + l1 * pm[5] + l2 * pm[9]; | ||
| 2169 | float r2 = l0 * pm[2] + l1 * pm[6] + l2 * pm[10]; | ||
| 2170 | |||
| 2171 | lm[i * 4 + 0] = r0; | ||
| 2172 | lm[i * 4 + 1] = r1; | ||
| 2173 | lm[i * 4 + 2] = r2; | ||
| 2174 | } | ||
| 2175 | |||
| 2176 | lm[12] += pm[12]; | ||
| 2177 | lm[13] += pm[13]; | ||
| 2178 | lm[14] += pm[14]; | ||
| 2179 | |||
| 2180 | parent = parent->parent; | ||
| 2181 | } | ||
| 2182 | } | ||
| 2183 | |||
| 2184 | static cgltf_ssize cgltf_component_read_integer(const void* in, cgltf_component_type component_type) | ||
| 2185 | { | ||
| 2186 | switch (component_type) | ||
| 2187 | { | ||
| 2188 | case cgltf_component_type_r_16: | ||
| 2189 | return *((const int16_t*) in); | ||
| 2190 | case cgltf_component_type_r_16u: | ||
| 2191 | return *((const uint16_t*) in); | ||
| 2192 | case cgltf_component_type_r_32u: | ||
| 2193 | return *((const uint32_t*) in); | ||
| 2194 | case cgltf_component_type_r_8: | ||
| 2195 | return *((const int8_t*) in); | ||
| 2196 | case cgltf_component_type_r_8u: | ||
| 2197 | return *((const uint8_t*) in); | ||
| 2198 | default: | ||
| 2199 | return 0; | ||
| 2200 | } | ||
| 2201 | } | ||
| 2202 | |||
| 2203 | static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_type component_type) | ||
| 2204 | { | ||
| 2205 | switch (component_type) | ||
| 2206 | { | ||
| 2207 | case cgltf_component_type_r_16u: | ||
| 2208 | return *((const uint16_t*) in); | ||
| 2209 | case cgltf_component_type_r_32u: | ||
| 2210 | return *((const uint32_t*) in); | ||
| 2211 | case cgltf_component_type_r_8u: | ||
| 2212 | return *((const uint8_t*) in); | ||
| 2213 | default: | ||
| 2214 | return 0; | ||
| 2215 | } | ||
| 2216 | } | ||
| 2217 | |||
| 2218 | static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_type component_type, cgltf_bool normalized) | ||
| 2219 | { | ||
| 2220 | if (component_type == cgltf_component_type_r_32f) | ||
| 2221 | { | ||
| 2222 | return *((const float*) in); | ||
| 2223 | } | ||
| 2224 | |||
| 2225 | if (normalized) | ||
| 2226 | { | ||
| 2227 | switch (component_type) | ||
| 2228 | { | ||
| 2229 | // note: glTF spec doesn't currently define normalized conversions for 32-bit integers | ||
| 2230 | case cgltf_component_type_r_16: | ||
| 2231 | return *((const int16_t*) in) / (cgltf_float)32767; | ||
| 2232 | case cgltf_component_type_r_16u: | ||
| 2233 | return *((const uint16_t*) in) / (cgltf_float)65535; | ||
| 2234 | case cgltf_component_type_r_8: | ||
| 2235 | return *((const int8_t*) in) / (cgltf_float)127; | ||
| 2236 | case cgltf_component_type_r_8u: | ||
| 2237 | return *((const uint8_t*) in) / (cgltf_float)255; | ||
| 2238 | default: | ||
| 2239 | return 0; | ||
| 2240 | } | ||
| 2241 | } | ||
| 2242 | |||
| 2243 | return (cgltf_float)cgltf_component_read_integer(in, component_type); | ||
| 2244 | } | ||
| 2245 | |||
| 2246 | static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size) | ||
| 2247 | { | ||
| 2248 | cgltf_size num_components = cgltf_num_components(type); | ||
| 2249 | |||
| 2250 | if (element_size < num_components) { | ||
| 2251 | return 0; | ||
| 2252 | } | ||
| 2253 | |||
| 2254 | // There are three special cases for component extraction, see #data-alignment in the 2.0 spec. | ||
| 2255 | |||
| 2256 | cgltf_size component_size = cgltf_component_size(component_type); | ||
| 2257 | |||
| 2258 | if (type == cgltf_type_mat2 && component_size == 1) | ||
| 2259 | { | ||
| 2260 | out[0] = cgltf_component_read_float(element, component_type, normalized); | ||
| 2261 | out[1] = cgltf_component_read_float(element + 1, component_type, normalized); | ||
| 2262 | out[2] = cgltf_component_read_float(element + 4, component_type, normalized); | ||
| 2263 | out[3] = cgltf_component_read_float(element + 5, component_type, normalized); | ||
| 2264 | return 1; | ||
| 2265 | } | ||
| 2266 | |||
| 2267 | if (type == cgltf_type_mat3 && component_size == 1) | ||
| 2268 | { | ||
| 2269 | out[0] = cgltf_component_read_float(element, component_type, normalized); | ||
| 2270 | out[1] = cgltf_component_read_float(element + 1, component_type, normalized); | ||
| 2271 | out[2] = cgltf_component_read_float(element + 2, component_type, normalized); | ||
| 2272 | out[3] = cgltf_component_read_float(element + 4, component_type, normalized); | ||
| 2273 | out[4] = cgltf_component_read_float(element + 5, component_type, normalized); | ||
| 2274 | out[5] = cgltf_component_read_float(element + 6, component_type, normalized); | ||
| 2275 | out[6] = cgltf_component_read_float(element + 8, component_type, normalized); | ||
| 2276 | out[7] = cgltf_component_read_float(element + 9, component_type, normalized); | ||
| 2277 | out[8] = cgltf_component_read_float(element + 10, component_type, normalized); | ||
| 2278 | return 1; | ||
| 2279 | } | ||
| 2280 | |||
| 2281 | if (type == cgltf_type_mat3 && component_size == 2) | ||
| 2282 | { | ||
| 2283 | out[0] = cgltf_component_read_float(element, component_type, normalized); | ||
| 2284 | out[1] = cgltf_component_read_float(element + 2, component_type, normalized); | ||
| 2285 | out[2] = cgltf_component_read_float(element + 4, component_type, normalized); | ||
| 2286 | out[3] = cgltf_component_read_float(element + 8, component_type, normalized); | ||
| 2287 | out[4] = cgltf_component_read_float(element + 10, component_type, normalized); | ||
| 2288 | out[5] = cgltf_component_read_float(element + 12, component_type, normalized); | ||
| 2289 | out[6] = cgltf_component_read_float(element + 16, component_type, normalized); | ||
| 2290 | out[7] = cgltf_component_read_float(element + 18, component_type, normalized); | ||
| 2291 | out[8] = cgltf_component_read_float(element + 20, component_type, normalized); | ||
| 2292 | return 1; | ||
| 2293 | } | ||
| 2294 | |||
| 2295 | for (cgltf_size i = 0; i < num_components; ++i) | ||
| 2296 | { | ||
| 2297 | out[i] = cgltf_component_read_float(element + component_size * i, component_type, normalized); | ||
| 2298 | } | ||
| 2299 | return 1; | ||
| 2300 | } | ||
| 2301 | |||
| 2302 | const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view) | ||
| 2303 | { | ||
| 2304 | if (view->data) | ||
| 2305 | return (const uint8_t*)view->data; | ||
| 2306 | |||
| 2307 | if (!view->buffer->data) | ||
| 2308 | return NULL; | ||
| 2309 | |||
| 2310 | const uint8_t* result = (const uint8_t*)view->buffer->data; | ||
| 2311 | result += view->offset; | ||
| 2312 | return result; | ||
| 2313 | } | ||
| 2314 | |||
| 2315 | cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size) | ||
| 2316 | { | ||
| 2317 | if (accessor->is_sparse) | ||
| 2318 | { | ||
| 2319 | return 0; | ||
| 2320 | } | ||
| 2321 | if (accessor->buffer_view == NULL) | ||
| 2322 | { | ||
| 2323 | memset(out, 0, element_size * sizeof(cgltf_float)); | ||
| 2324 | return 1; | ||
| 2325 | } | ||
| 2326 | const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); | ||
| 2327 | if (element == NULL) | ||
| 2328 | { | ||
| 2329 | return 0; | ||
| 2330 | } | ||
| 2331 | element += accessor->offset + accessor->stride * index; | ||
| 2332 | return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size); | ||
| 2333 | } | ||
| 2334 | |||
| 2335 | cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count) | ||
| 2336 | { | ||
| 2337 | cgltf_size floats_per_element = cgltf_num_components(accessor->type); | ||
| 2338 | cgltf_size available_floats = accessor->count * floats_per_element; | ||
| 2339 | if (out == NULL) | ||
| 2340 | { | ||
| 2341 | return available_floats; | ||
| 2342 | } | ||
| 2343 | |||
| 2344 | float_count = available_floats < float_count ? available_floats : float_count; | ||
| 2345 | cgltf_size element_count = float_count / floats_per_element; | ||
| 2346 | |||
| 2347 | // First pass: convert each element in the base accessor. | ||
| 2348 | if (accessor->buffer_view == NULL) | ||
| 2349 | { | ||
| 2350 | memset(out, 0, element_count * floats_per_element * sizeof(cgltf_float)); | ||
| 2351 | } | ||
| 2352 | else | ||
| 2353 | { | ||
| 2354 | const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); | ||
| 2355 | if (element == NULL) | ||
| 2356 | { | ||
| 2357 | return 0; | ||
| 2358 | } | ||
| 2359 | element += accessor->offset; | ||
| 2360 | |||
| 2361 | if (accessor->component_type == cgltf_component_type_r_32f && accessor->stride == floats_per_element * sizeof(cgltf_float)) | ||
| 2362 | { | ||
| 2363 | memcpy(out, element, element_count * floats_per_element * sizeof(cgltf_float)); | ||
| 2364 | } | ||
| 2365 | else | ||
| 2366 | { | ||
| 2367 | cgltf_float* dest = out; | ||
| 2368 | |||
| 2369 | for (cgltf_size index = 0; index < element_count; index++, dest += floats_per_element, element += accessor->stride) | ||
| 2370 | { | ||
| 2371 | if (!cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, dest, floats_per_element)) | ||
| 2372 | { | ||
| 2373 | return 0; | ||
| 2374 | } | ||
| 2375 | } | ||
| 2376 | } | ||
| 2377 | } | ||
| 2378 | |||
| 2379 | // Second pass: write out each element in the sparse accessor. | ||
| 2380 | if (accessor->is_sparse) | ||
| 2381 | { | ||
| 2382 | const cgltf_accessor_sparse* sparse = &accessor->sparse; | ||
| 2383 | |||
| 2384 | const uint8_t* index_data = cgltf_buffer_view_data(sparse->indices_buffer_view); | ||
| 2385 | const uint8_t* reader_head = cgltf_buffer_view_data(sparse->values_buffer_view); | ||
| 2386 | |||
| 2387 | if (index_data == NULL || reader_head == NULL) | ||
| 2388 | { | ||
| 2389 | return 0; | ||
| 2390 | } | ||
| 2391 | |||
| 2392 | index_data += sparse->indices_byte_offset; | ||
| 2393 | reader_head += sparse->values_byte_offset; | ||
| 2394 | |||
| 2395 | cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type); | ||
| 2396 | for (cgltf_size reader_index = 0; reader_index < sparse->count; reader_index++, index_data += index_stride, reader_head += accessor->stride) | ||
| 2397 | { | ||
| 2398 | size_t writer_index = cgltf_component_read_index(index_data, sparse->indices_component_type); | ||
| 2399 | float* writer_head = out + writer_index * floats_per_element; | ||
| 2400 | |||
| 2401 | if (!cgltf_element_read_float(reader_head, accessor->type, accessor->component_type, accessor->normalized, writer_head, floats_per_element)) | ||
| 2402 | { | ||
| 2403 | return 0; | ||
| 2404 | } | ||
| 2405 | } | ||
| 2406 | } | ||
| 2407 | |||
| 2408 | return element_count * floats_per_element; | ||
| 2409 | } | ||
| 2410 | |||
| 2411 | static cgltf_uint cgltf_component_read_uint(const void* in, cgltf_component_type component_type) | ||
| 2412 | { | ||
| 2413 | switch (component_type) | ||
| 2414 | { | ||
| 2415 | case cgltf_component_type_r_8: | ||
| 2416 | return *((const int8_t*) in); | ||
| 2417 | |||
| 2418 | case cgltf_component_type_r_8u: | ||
| 2419 | return *((const uint8_t*) in); | ||
| 2420 | |||
| 2421 | case cgltf_component_type_r_16: | ||
| 2422 | return *((const int16_t*) in); | ||
| 2423 | |||
| 2424 | case cgltf_component_type_r_16u: | ||
| 2425 | return *((const uint16_t*) in); | ||
| 2426 | |||
| 2427 | case cgltf_component_type_r_32u: | ||
| 2428 | return *((const uint32_t*) in); | ||
| 2429 | |||
| 2430 | default: | ||
| 2431 | return 0; | ||
| 2432 | } | ||
| 2433 | } | ||
| 2434 | |||
| 2435 | static cgltf_bool cgltf_element_read_uint(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_uint* out, cgltf_size element_size) | ||
| 2436 | { | ||
| 2437 | cgltf_size num_components = cgltf_num_components(type); | ||
| 2438 | |||
| 2439 | if (element_size < num_components) | ||
| 2440 | { | ||
| 2441 | return 0; | ||
| 2442 | } | ||
| 2443 | |||
| 2444 | // Reading integer matrices is not a valid use case | ||
| 2445 | if (type == cgltf_type_mat2 || type == cgltf_type_mat3 || type == cgltf_type_mat4) | ||
| 2446 | { | ||
| 2447 | return 0; | ||
| 2448 | } | ||
| 2449 | |||
| 2450 | cgltf_size component_size = cgltf_component_size(component_type); | ||
| 2451 | |||
| 2452 | for (cgltf_size i = 0; i < num_components; ++i) | ||
| 2453 | { | ||
| 2454 | out[i] = cgltf_component_read_uint(element + component_size * i, component_type); | ||
| 2455 | } | ||
| 2456 | return 1; | ||
| 2457 | } | ||
| 2458 | |||
| 2459 | cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size) | ||
| 2460 | { | ||
| 2461 | if (accessor->is_sparse) | ||
| 2462 | { | ||
| 2463 | return 0; | ||
| 2464 | } | ||
| 2465 | if (accessor->buffer_view == NULL) | ||
| 2466 | { | ||
| 2467 | memset(out, 0, element_size * sizeof( cgltf_uint )); | ||
| 2468 | return 1; | ||
| 2469 | } | ||
| 2470 | const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); | ||
| 2471 | if (element == NULL) | ||
| 2472 | { | ||
| 2473 | return 0; | ||
| 2474 | } | ||
| 2475 | element += accessor->offset + accessor->stride * index; | ||
| 2476 | return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size); | ||
| 2477 | } | ||
| 2478 | |||
| 2479 | cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index) | ||
| 2480 | { | ||
| 2481 | if (accessor->is_sparse) | ||
| 2482 | { | ||
| 2483 | return 0; // This is an error case, but we can't communicate the error with existing interface. | ||
| 2484 | } | ||
| 2485 | if (accessor->buffer_view == NULL) | ||
| 2486 | { | ||
| 2487 | return 0; | ||
| 2488 | } | ||
| 2489 | const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); | ||
| 2490 | if (element == NULL) | ||
| 2491 | { | ||
| 2492 | return 0; // This is an error case, but we can't communicate the error with existing interface. | ||
| 2493 | } | ||
| 2494 | element += accessor->offset + accessor->stride * index; | ||
| 2495 | return cgltf_component_read_index(element, accessor->component_type); | ||
| 2496 | } | ||
| 2497 | |||
| 2498 | cgltf_size cgltf_mesh_index(const cgltf_data* data, const cgltf_mesh* object) | ||
| 2499 | { | ||
| 2500 | assert(object && (cgltf_size)(object - data->meshes) < data->meshes_count); | ||
| 2501 | return (cgltf_size)(object - data->meshes); | ||
| 2502 | } | ||
| 2503 | |||
| 2504 | cgltf_size cgltf_material_index(const cgltf_data* data, const cgltf_material* object) | ||
| 2505 | { | ||
| 2506 | assert(object && (cgltf_size)(object - data->materials) < data->materials_count); | ||
| 2507 | return (cgltf_size)(object - data->materials); | ||
| 2508 | } | ||
| 2509 | |||
| 2510 | cgltf_size cgltf_accessor_index(const cgltf_data* data, const cgltf_accessor* object) | ||
| 2511 | { | ||
| 2512 | assert(object && (cgltf_size)(object - data->accessors) < data->accessors_count); | ||
| 2513 | return (cgltf_size)(object - data->accessors); | ||
| 2514 | } | ||
| 2515 | |||
| 2516 | cgltf_size cgltf_buffer_view_index(const cgltf_data* data, const cgltf_buffer_view* object) | ||
| 2517 | { | ||
| 2518 | assert(object && (cgltf_size)(object - data->buffer_views) < data->buffer_views_count); | ||
| 2519 | return (cgltf_size)(object - data->buffer_views); | ||
| 2520 | } | ||
| 2521 | |||
| 2522 | cgltf_size cgltf_buffer_index(const cgltf_data* data, const cgltf_buffer* object) | ||
| 2523 | { | ||
| 2524 | assert(object && (cgltf_size)(object - data->buffers) < data->buffers_count); | ||
| 2525 | return (cgltf_size)(object - data->buffers); | ||
| 2526 | } | ||
| 2527 | |||
| 2528 | cgltf_size cgltf_image_index(const cgltf_data* data, const cgltf_image* object) | ||
| 2529 | { | ||
| 2530 | assert(object && (cgltf_size)(object - data->images) < data->images_count); | ||
| 2531 | return (cgltf_size)(object - data->images); | ||
| 2532 | } | ||
| 2533 | |||
| 2534 | cgltf_size cgltf_texture_index(const cgltf_data* data, const cgltf_texture* object) | ||
| 2535 | { | ||
| 2536 | assert(object && (cgltf_size)(object - data->textures) < data->textures_count); | ||
| 2537 | return (cgltf_size)(object - data->textures); | ||
| 2538 | } | ||
| 2539 | |||
| 2540 | cgltf_size cgltf_sampler_index(const cgltf_data* data, const cgltf_sampler* object) | ||
| 2541 | { | ||
| 2542 | assert(object && (cgltf_size)(object - data->samplers) < data->samplers_count); | ||
| 2543 | return (cgltf_size)(object - data->samplers); | ||
| 2544 | } | ||
| 2545 | |||
| 2546 | cgltf_size cgltf_skin_index(const cgltf_data* data, const cgltf_skin* object) | ||
| 2547 | { | ||
| 2548 | assert(object && (cgltf_size)(object - data->skins) < data->skins_count); | ||
| 2549 | return (cgltf_size)(object - data->skins); | ||
| 2550 | } | ||
| 2551 | |||
| 2552 | cgltf_size cgltf_camera_index(const cgltf_data* data, const cgltf_camera* object) | ||
| 2553 | { | ||
| 2554 | assert(object && (cgltf_size)(object - data->cameras) < data->cameras_count); | ||
| 2555 | return (cgltf_size)(object - data->cameras); | ||
| 2556 | } | ||
| 2557 | |||
| 2558 | cgltf_size cgltf_light_index(const cgltf_data* data, const cgltf_light* object) | ||
| 2559 | { | ||
| 2560 | assert(object && (cgltf_size)(object - data->lights) < data->lights_count); | ||
| 2561 | return (cgltf_size)(object - data->lights); | ||
| 2562 | } | ||
| 2563 | |||
| 2564 | cgltf_size cgltf_node_index(const cgltf_data* data, const cgltf_node* object) | ||
| 2565 | { | ||
| 2566 | assert(object && (cgltf_size)(object - data->nodes) < data->nodes_count); | ||
| 2567 | return (cgltf_size)(object - data->nodes); | ||
| 2568 | } | ||
| 2569 | |||
| 2570 | cgltf_size cgltf_scene_index(const cgltf_data* data, const cgltf_scene* object) | ||
| 2571 | { | ||
| 2572 | assert(object && (cgltf_size)(object - data->scenes) < data->scenes_count); | ||
| 2573 | return (cgltf_size)(object - data->scenes); | ||
| 2574 | } | ||
| 2575 | |||
| 2576 | cgltf_size cgltf_animation_index(const cgltf_data* data, const cgltf_animation* object) | ||
| 2577 | { | ||
| 2578 | assert(object && (cgltf_size)(object - data->animations) < data->animations_count); | ||
| 2579 | return (cgltf_size)(object - data->animations); | ||
| 2580 | } | ||
| 2581 | |||
| 2582 | cgltf_size cgltf_animation_sampler_index(const cgltf_animation* animation, const cgltf_animation_sampler* object) | ||
| 2583 | { | ||
| 2584 | assert(object && (cgltf_size)(object - animation->samplers) < animation->samplers_count); | ||
| 2585 | return (cgltf_size)(object - animation->samplers); | ||
| 2586 | } | ||
| 2587 | |||
| 2588 | cgltf_size cgltf_animation_channel_index(const cgltf_animation* animation, const cgltf_animation_channel* object) | ||
| 2589 | { | ||
| 2590 | assert(object && (cgltf_size)(object - animation->channels) < animation->channels_count); | ||
| 2591 | return (cgltf_size)(object - animation->channels); | ||
| 2592 | } | ||
| 2593 | |||
| 2594 | cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* out, cgltf_size out_component_size, cgltf_size index_count) | ||
| 2595 | { | ||
| 2596 | if (out == NULL) | ||
| 2597 | { | ||
| 2598 | return accessor->count; | ||
| 2599 | } | ||
| 2600 | |||
| 2601 | index_count = accessor->count < index_count ? accessor->count : index_count; | ||
| 2602 | cgltf_size index_component_size = cgltf_component_size(accessor->component_type); | ||
| 2603 | |||
| 2604 | if (accessor->is_sparse) | ||
| 2605 | { | ||
| 2606 | return 0; | ||
| 2607 | } | ||
| 2608 | if (accessor->buffer_view == NULL) | ||
| 2609 | { | ||
| 2610 | return 0; | ||
| 2611 | } | ||
| 2612 | if (index_component_size > out_component_size) | ||
| 2613 | { | ||
| 2614 | return 0; | ||
| 2615 | } | ||
| 2616 | const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); | ||
| 2617 | if (element == NULL) | ||
| 2618 | { | ||
| 2619 | return 0; | ||
| 2620 | } | ||
| 2621 | element += accessor->offset; | ||
| 2622 | |||
| 2623 | if (index_component_size == out_component_size && accessor->stride == out_component_size) | ||
| 2624 | { | ||
| 2625 | memcpy(out, element, index_count * index_component_size); | ||
| 2626 | return index_count; | ||
| 2627 | } | ||
| 2628 | |||
| 2629 | // The component size of the output array is larger than the component size of the index data, so index data will be padded. | ||
| 2630 | switch (out_component_size) | ||
| 2631 | { | ||
| 2632 | case 2: | ||
| 2633 | for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride) | ||
| 2634 | { | ||
| 2635 | ((uint16_t*)out)[index] = (uint16_t)cgltf_component_read_index(element, accessor->component_type); | ||
| 2636 | } | ||
| 2637 | break; | ||
| 2638 | case 4: | ||
| 2639 | for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride) | ||
| 2640 | { | ||
| 2641 | ((uint32_t*)out)[index] = (uint32_t)cgltf_component_read_index(element, accessor->component_type); | ||
| 2642 | } | ||
| 2643 | break; | ||
| 2644 | default: | ||
| 2645 | break; | ||
| 2646 | } | ||
| 2647 | |||
| 2648 | return index_count; | ||
| 2649 | } | ||
| 2650 | |||
| 2651 | #define CGLTF_ERROR_JSON -1 | ||
| 2652 | #define CGLTF_ERROR_NOMEM -2 | ||
| 2653 | #define CGLTF_ERROR_LEGACY -3 | ||
| 2654 | |||
| 2655 | #define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; } | ||
| 2656 | #define CGLTF_CHECK_TOKTYPE_RET(tok_, type_, ret_) if ((tok_).type != (type_)) { return ret_; } | ||
| 2657 | #define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */ | ||
| 2658 | |||
| 2659 | #define CGLTF_PTRINDEX(type, idx) (type*)((cgltf_size)idx + 1) | ||
| 2660 | #define CGLTF_PTRFIXUP(var, data, size) if (var) { if ((cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; } | ||
| 2661 | #define CGLTF_PTRFIXUP_REQ(var, data, size) if (!var || (cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; | ||
| 2662 | |||
| 2663 | static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, const char* str) | ||
| 2664 | { | ||
| 2665 | CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING); | ||
| 2666 | size_t const str_len = strlen(str); | ||
| 2667 | size_t const name_length = (size_t)(tok->end - tok->start); | ||
| 2668 | return (str_len == name_length) ? strncmp((const char*)json_chunk + tok->start, str, str_len) : 128; | ||
| 2669 | } | ||
| 2670 | |||
| 2671 | static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk) | ||
| 2672 | { | ||
| 2673 | CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); | ||
| 2674 | char tmp[128]; | ||
| 2675 | int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); | ||
| 2676 | strncpy(tmp, (const char*)json_chunk + tok->start, size); | ||
| 2677 | tmp[size] = 0; | ||
| 2678 | return CGLTF_ATOI(tmp); | ||
| 2679 | } | ||
| 2680 | |||
| 2681 | static cgltf_size cgltf_json_to_size(jsmntok_t const* tok, const uint8_t* json_chunk) | ||
| 2682 | { | ||
| 2683 | CGLTF_CHECK_TOKTYPE_RET(*tok, JSMN_PRIMITIVE, 0); | ||
| 2684 | char tmp[128]; | ||
| 2685 | int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); | ||
| 2686 | strncpy(tmp, (const char*)json_chunk + tok->start, size); | ||
| 2687 | tmp[size] = 0; | ||
| 2688 | long long res = CGLTF_ATOLL(tmp); | ||
| 2689 | return res < 0 ? 0 : (cgltf_size)res; | ||
| 2690 | } | ||
| 2691 | |||
| 2692 | static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk) | ||
| 2693 | { | ||
| 2694 | CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); | ||
| 2695 | char tmp[128]; | ||
| 2696 | int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); | ||
| 2697 | strncpy(tmp, (const char*)json_chunk + tok->start, size); | ||
| 2698 | tmp[size] = 0; | ||
| 2699 | return (cgltf_float)CGLTF_ATOF(tmp); | ||
| 2700 | } | ||
| 2701 | |||
| 2702 | static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk) | ||
| 2703 | { | ||
| 2704 | int size = (int)(tok->end - tok->start); | ||
| 2705 | return size == 4 && memcmp(json_chunk + tok->start, "true", 4) == 0; | ||
| 2706 | } | ||
| 2707 | |||
| 2708 | static int cgltf_skip_json(jsmntok_t const* tokens, int i) | ||
| 2709 | { | ||
| 2710 | int end = i + 1; | ||
| 2711 | |||
| 2712 | while (i < end) | ||
| 2713 | { | ||
| 2714 | switch (tokens[i].type) | ||
| 2715 | { | ||
| 2716 | case JSMN_OBJECT: | ||
| 2717 | end += tokens[i].size * 2; | ||
| 2718 | break; | ||
| 2719 | |||
| 2720 | case JSMN_ARRAY: | ||
| 2721 | end += tokens[i].size; | ||
| 2722 | break; | ||
| 2723 | |||
| 2724 | case JSMN_PRIMITIVE: | ||
| 2725 | case JSMN_STRING: | ||
| 2726 | break; | ||
| 2727 | |||
| 2728 | default: | ||
| 2729 | return -1; | ||
| 2730 | } | ||
| 2731 | |||
| 2732 | i++; | ||
| 2733 | } | ||
| 2734 | |||
| 2735 | return i; | ||
| 2736 | } | ||
| 2737 | |||
| 2738 | static void cgltf_fill_float_array(float* out_array, int size, float value) | ||
| 2739 | { | ||
| 2740 | for (int j = 0; j < size; ++j) | ||
| 2741 | { | ||
| 2742 | out_array[j] = value; | ||
| 2743 | } | ||
| 2744 | } | ||
| 2745 | |||
| 2746 | static int cgltf_parse_json_float_array(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, float* out_array, int size) | ||
| 2747 | { | ||
| 2748 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); | ||
| 2749 | if (tokens[i].size != size) | ||
| 2750 | { | ||
| 2751 | return CGLTF_ERROR_JSON; | ||
| 2752 | } | ||
| 2753 | ++i; | ||
| 2754 | for (int j = 0; j < size; ++j) | ||
| 2755 | { | ||
| 2756 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); | ||
| 2757 | out_array[j] = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 2758 | ++i; | ||
| 2759 | } | ||
| 2760 | return i; | ||
| 2761 | } | ||
| 2762 | |||
| 2763 | static int cgltf_parse_json_string(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char** out_string) | ||
| 2764 | { | ||
| 2765 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); | ||
| 2766 | if (*out_string) | ||
| 2767 | { | ||
| 2768 | return CGLTF_ERROR_JSON; | ||
| 2769 | } | ||
| 2770 | int size = (int)(tokens[i].end - tokens[i].start); | ||
| 2771 | char* result = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); | ||
| 2772 | if (!result) | ||
| 2773 | { | ||
| 2774 | return CGLTF_ERROR_NOMEM; | ||
| 2775 | } | ||
| 2776 | strncpy(result, (const char*)json_chunk + tokens[i].start, size); | ||
| 2777 | result[size] = 0; | ||
| 2778 | *out_string = result; | ||
| 2779 | return i + 1; | ||
| 2780 | } | ||
| 2781 | |||
| 2782 | static int cgltf_parse_json_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, size_t element_size, void** out_array, cgltf_size* out_size) | ||
| 2783 | { | ||
| 2784 | (void)json_chunk; | ||
| 2785 | if (tokens[i].type != JSMN_ARRAY) | ||
| 2786 | { | ||
| 2787 | return tokens[i].type == JSMN_OBJECT ? CGLTF_ERROR_LEGACY : CGLTF_ERROR_JSON; | ||
| 2788 | } | ||
| 2789 | if (*out_array) | ||
| 2790 | { | ||
| 2791 | return CGLTF_ERROR_JSON; | ||
| 2792 | } | ||
| 2793 | int size = tokens[i].size; | ||
| 2794 | void* result = cgltf_calloc(options, element_size, size); | ||
| 2795 | if (!result) | ||
| 2796 | { | ||
| 2797 | return CGLTF_ERROR_NOMEM; | ||
| 2798 | } | ||
| 2799 | *out_array = result; | ||
| 2800 | *out_size = size; | ||
| 2801 | return i + 1; | ||
| 2802 | } | ||
| 2803 | |||
| 2804 | static int cgltf_parse_json_string_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char*** out_array, cgltf_size* out_size) | ||
| 2805 | { | ||
| 2806 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); | ||
| 2807 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(char*), (void**)out_array, out_size); | ||
| 2808 | if (i < 0) | ||
| 2809 | { | ||
| 2810 | return i; | ||
| 2811 | } | ||
| 2812 | |||
| 2813 | for (cgltf_size j = 0; j < *out_size; ++j) | ||
| 2814 | { | ||
| 2815 | i = cgltf_parse_json_string(options, tokens, i, json_chunk, j + (*out_array)); | ||
| 2816 | if (i < 0) | ||
| 2817 | { | ||
| 2818 | return i; | ||
| 2819 | } | ||
| 2820 | } | ||
| 2821 | return i; | ||
| 2822 | } | ||
| 2823 | |||
| 2824 | static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* out_type, int* out_index) | ||
| 2825 | { | ||
| 2826 | if (*name == '_') | ||
| 2827 | { | ||
| 2828 | *out_type = cgltf_attribute_type_custom; | ||
| 2829 | return; | ||
| 2830 | } | ||
| 2831 | |||
| 2832 | const char* us = strchr(name, '_'); | ||
| 2833 | size_t len = us ? (size_t)(us - name) : strlen(name); | ||
| 2834 | |||
| 2835 | if (len == 8 && strncmp(name, "POSITION", 8) == 0) | ||
| 2836 | { | ||
| 2837 | *out_type = cgltf_attribute_type_position; | ||
| 2838 | } | ||
| 2839 | else if (len == 6 && strncmp(name, "NORMAL", 6) == 0) | ||
| 2840 | { | ||
| 2841 | *out_type = cgltf_attribute_type_normal; | ||
| 2842 | } | ||
| 2843 | else if (len == 7 && strncmp(name, "TANGENT", 7) == 0) | ||
| 2844 | { | ||
| 2845 | *out_type = cgltf_attribute_type_tangent; | ||
| 2846 | } | ||
| 2847 | else if (len == 8 && strncmp(name, "TEXCOORD", 8) == 0) | ||
| 2848 | { | ||
| 2849 | *out_type = cgltf_attribute_type_texcoord; | ||
| 2850 | } | ||
| 2851 | else if (len == 5 && strncmp(name, "COLOR", 5) == 0) | ||
| 2852 | { | ||
| 2853 | *out_type = cgltf_attribute_type_color; | ||
| 2854 | } | ||
| 2855 | else if (len == 6 && strncmp(name, "JOINTS", 6) == 0) | ||
| 2856 | { | ||
| 2857 | *out_type = cgltf_attribute_type_joints; | ||
| 2858 | } | ||
| 2859 | else if (len == 7 && strncmp(name, "WEIGHTS", 7) == 0) | ||
| 2860 | { | ||
| 2861 | *out_type = cgltf_attribute_type_weights; | ||
| 2862 | } | ||
| 2863 | else | ||
| 2864 | { | ||
| 2865 | *out_type = cgltf_attribute_type_invalid; | ||
| 2866 | } | ||
| 2867 | |||
| 2868 | if (us && *out_type != cgltf_attribute_type_invalid) | ||
| 2869 | { | ||
| 2870 | *out_index = CGLTF_ATOI(us + 1); | ||
| 2871 | if (*out_index < 0) | ||
| 2872 | { | ||
| 2873 | *out_type = cgltf_attribute_type_invalid; | ||
| 2874 | *out_index = 0; | ||
| 2875 | } | ||
| 2876 | } | ||
| 2877 | } | ||
| 2878 | |||
| 2879 | static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_attribute** out_attributes, cgltf_size* out_attributes_count) | ||
| 2880 | { | ||
| 2881 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 2882 | |||
| 2883 | if (*out_attributes) | ||
| 2884 | { | ||
| 2885 | return CGLTF_ERROR_JSON; | ||
| 2886 | } | ||
| 2887 | |||
| 2888 | *out_attributes_count = tokens[i].size; | ||
| 2889 | *out_attributes = (cgltf_attribute*)cgltf_calloc(options, sizeof(cgltf_attribute), *out_attributes_count); | ||
| 2890 | ++i; | ||
| 2891 | |||
| 2892 | if (!*out_attributes) | ||
| 2893 | { | ||
| 2894 | return CGLTF_ERROR_NOMEM; | ||
| 2895 | } | ||
| 2896 | |||
| 2897 | for (cgltf_size j = 0; j < *out_attributes_count; ++j) | ||
| 2898 | { | ||
| 2899 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 2900 | |||
| 2901 | i = cgltf_parse_json_string(options, tokens, i, json_chunk, &(*out_attributes)[j].name); | ||
| 2902 | if (i < 0) | ||
| 2903 | { | ||
| 2904 | return CGLTF_ERROR_JSON; | ||
| 2905 | } | ||
| 2906 | |||
| 2907 | cgltf_parse_attribute_type((*out_attributes)[j].name, &(*out_attributes)[j].type, &(*out_attributes)[j].index); | ||
| 2908 | |||
| 2909 | (*out_attributes)[j].data = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 2910 | ++i; | ||
| 2911 | } | ||
| 2912 | |||
| 2913 | return i; | ||
| 2914 | } | ||
| 2915 | |||
| 2916 | static int cgltf_parse_json_extras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras) | ||
| 2917 | { | ||
| 2918 | if (out_extras->data) | ||
| 2919 | { | ||
| 2920 | return CGLTF_ERROR_JSON; | ||
| 2921 | } | ||
| 2922 | |||
| 2923 | /* fill deprecated fields for now, this will be removed in the future */ | ||
| 2924 | out_extras->start_offset = tokens[i].start; | ||
| 2925 | out_extras->end_offset = tokens[i].end; | ||
| 2926 | |||
| 2927 | size_t start = tokens[i].start; | ||
| 2928 | size_t size = tokens[i].end - start; | ||
| 2929 | out_extras->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); | ||
| 2930 | if (!out_extras->data) | ||
| 2931 | { | ||
| 2932 | return CGLTF_ERROR_NOMEM; | ||
| 2933 | } | ||
| 2934 | strncpy(out_extras->data, (const char*)json_chunk + start, size); | ||
| 2935 | out_extras->data[size] = '\0'; | ||
| 2936 | |||
| 2937 | i = cgltf_skip_json(tokens, i); | ||
| 2938 | return i; | ||
| 2939 | } | ||
| 2940 | |||
| 2941 | static int cgltf_parse_json_unprocessed_extension(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extension* out_extension) | ||
| 2942 | { | ||
| 2943 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); | ||
| 2944 | CGLTF_CHECK_TOKTYPE(tokens[i+1], JSMN_OBJECT); | ||
| 2945 | if (out_extension->name) | ||
| 2946 | { | ||
| 2947 | return CGLTF_ERROR_JSON; | ||
| 2948 | } | ||
| 2949 | |||
| 2950 | cgltf_size name_length = tokens[i].end - tokens[i].start; | ||
| 2951 | out_extension->name = (char*)options->memory.alloc_func(options->memory.user_data, name_length + 1); | ||
| 2952 | if (!out_extension->name) | ||
| 2953 | { | ||
| 2954 | return CGLTF_ERROR_NOMEM; | ||
| 2955 | } | ||
| 2956 | strncpy(out_extension->name, (const char*)json_chunk + tokens[i].start, name_length); | ||
| 2957 | out_extension->name[name_length] = 0; | ||
| 2958 | i++; | ||
| 2959 | |||
| 2960 | size_t start = tokens[i].start; | ||
| 2961 | size_t size = tokens[i].end - start; | ||
| 2962 | out_extension->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); | ||
| 2963 | if (!out_extension->data) | ||
| 2964 | { | ||
| 2965 | return CGLTF_ERROR_NOMEM; | ||
| 2966 | } | ||
| 2967 | strncpy(out_extension->data, (const char*)json_chunk + start, size); | ||
| 2968 | out_extension->data[size] = '\0'; | ||
| 2969 | |||
| 2970 | i = cgltf_skip_json(tokens, i); | ||
| 2971 | |||
| 2972 | return i; | ||
| 2973 | } | ||
| 2974 | |||
| 2975 | static int cgltf_parse_json_unprocessed_extensions(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size* out_extensions_count, cgltf_extension** out_extensions) | ||
| 2976 | { | ||
| 2977 | ++i; | ||
| 2978 | |||
| 2979 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 2980 | if(*out_extensions) | ||
| 2981 | { | ||
| 2982 | return CGLTF_ERROR_JSON; | ||
| 2983 | } | ||
| 2984 | |||
| 2985 | int extensions_size = tokens[i].size; | ||
| 2986 | *out_extensions_count = 0; | ||
| 2987 | *out_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); | ||
| 2988 | |||
| 2989 | if (!*out_extensions) | ||
| 2990 | { | ||
| 2991 | return CGLTF_ERROR_NOMEM; | ||
| 2992 | } | ||
| 2993 | |||
| 2994 | ++i; | ||
| 2995 | |||
| 2996 | for (int j = 0; j < extensions_size; ++j) | ||
| 2997 | { | ||
| 2998 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 2999 | |||
| 3000 | cgltf_size extension_index = (*out_extensions_count)++; | ||
| 3001 | cgltf_extension* extension = &((*out_extensions)[extension_index]); | ||
| 3002 | i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, extension); | ||
| 3003 | |||
| 3004 | if (i < 0) | ||
| 3005 | { | ||
| 3006 | return i; | ||
| 3007 | } | ||
| 3008 | } | ||
| 3009 | return i; | ||
| 3010 | } | ||
| 3011 | |||
| 3012 | static int cgltf_parse_json_draco_mesh_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_draco_mesh_compression* out_draco_mesh_compression) | ||
| 3013 | { | ||
| 3014 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3015 | |||
| 3016 | int size = tokens[i].size; | ||
| 3017 | ++i; | ||
| 3018 | |||
| 3019 | for (int j = 0; j < size; ++j) | ||
| 3020 | { | ||
| 3021 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3022 | |||
| 3023 | if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0) | ||
| 3024 | { | ||
| 3025 | i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_draco_mesh_compression->attributes, &out_draco_mesh_compression->attributes_count); | ||
| 3026 | } | ||
| 3027 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferView") == 0) | ||
| 3028 | { | ||
| 3029 | ++i; | ||
| 3030 | out_draco_mesh_compression->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 3031 | ++i; | ||
| 3032 | } | ||
| 3033 | else | ||
| 3034 | { | ||
| 3035 | i = cgltf_skip_json(tokens, i+1); | ||
| 3036 | } | ||
| 3037 | |||
| 3038 | if (i < 0) | ||
| 3039 | { | ||
| 3040 | return i; | ||
| 3041 | } | ||
| 3042 | } | ||
| 3043 | |||
| 3044 | return i; | ||
| 3045 | } | ||
| 3046 | |||
| 3047 | static int cgltf_parse_json_mesh_gpu_instancing(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh_gpu_instancing* out_mesh_gpu_instancing) | ||
| 3048 | { | ||
| 3049 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3050 | |||
| 3051 | int size = tokens[i].size; | ||
| 3052 | ++i; | ||
| 3053 | |||
| 3054 | for (int j = 0; j < size; ++j) | ||
| 3055 | { | ||
| 3056 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3057 | |||
| 3058 | if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0) | ||
| 3059 | { | ||
| 3060 | i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_mesh_gpu_instancing->attributes, &out_mesh_gpu_instancing->attributes_count); | ||
| 3061 | } | ||
| 3062 | else | ||
| 3063 | { | ||
| 3064 | i = cgltf_skip_json(tokens, i+1); | ||
| 3065 | } | ||
| 3066 | |||
| 3067 | if (i < 0) | ||
| 3068 | { | ||
| 3069 | return i; | ||
| 3070 | } | ||
| 3071 | } | ||
| 3072 | |||
| 3073 | return i; | ||
| 3074 | } | ||
| 3075 | |||
| 3076 | static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_mapping* out_mappings, cgltf_size* offset) | ||
| 3077 | { | ||
| 3078 | (void)options; | ||
| 3079 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); | ||
| 3080 | |||
| 3081 | int size = tokens[i].size; | ||
| 3082 | ++i; | ||
| 3083 | |||
| 3084 | for (int j = 0; j < size; ++j) | ||
| 3085 | { | ||
| 3086 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3087 | |||
| 3088 | int obj_size = tokens[i].size; | ||
| 3089 | ++i; | ||
| 3090 | |||
| 3091 | int material = -1; | ||
| 3092 | int variants_tok = -1; | ||
| 3093 | int extras_tok = -1; | ||
| 3094 | |||
| 3095 | for (int k = 0; k < obj_size; ++k) | ||
| 3096 | { | ||
| 3097 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3098 | |||
| 3099 | if (cgltf_json_strcmp(tokens + i, json_chunk, "material") == 0) | ||
| 3100 | { | ||
| 3101 | ++i; | ||
| 3102 | material = cgltf_json_to_int(tokens + i, json_chunk); | ||
| 3103 | ++i; | ||
| 3104 | } | ||
| 3105 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0) | ||
| 3106 | { | ||
| 3107 | variants_tok = i+1; | ||
| 3108 | CGLTF_CHECK_TOKTYPE(tokens[variants_tok], JSMN_ARRAY); | ||
| 3109 | |||
| 3110 | i = cgltf_skip_json(tokens, i+1); | ||
| 3111 | } | ||
| 3112 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 3113 | { | ||
| 3114 | extras_tok = i + 1; | ||
| 3115 | i = cgltf_skip_json(tokens, extras_tok); | ||
| 3116 | } | ||
| 3117 | else | ||
| 3118 | { | ||
| 3119 | i = cgltf_skip_json(tokens, i+1); | ||
| 3120 | } | ||
| 3121 | |||
| 3122 | if (i < 0) | ||
| 3123 | { | ||
| 3124 | return i; | ||
| 3125 | } | ||
| 3126 | } | ||
| 3127 | |||
| 3128 | if (material < 0 || variants_tok < 0) | ||
| 3129 | { | ||
| 3130 | return CGLTF_ERROR_JSON; | ||
| 3131 | } | ||
| 3132 | |||
| 3133 | if (out_mappings) | ||
| 3134 | { | ||
| 3135 | for (int k = 0; k < tokens[variants_tok].size; ++k) | ||
| 3136 | { | ||
| 3137 | int variant = cgltf_json_to_int(&tokens[variants_tok + 1 + k], json_chunk); | ||
| 3138 | if (variant < 0) | ||
| 3139 | return variant; | ||
| 3140 | |||
| 3141 | out_mappings[*offset].material = CGLTF_PTRINDEX(cgltf_material, material); | ||
| 3142 | out_mappings[*offset].variant = variant; | ||
| 3143 | |||
| 3144 | if (extras_tok >= 0) | ||
| 3145 | { | ||
| 3146 | int e = cgltf_parse_json_extras(options, tokens, extras_tok, json_chunk, &out_mappings[*offset].extras); | ||
| 3147 | if (e < 0) | ||
| 3148 | return e; | ||
| 3149 | } | ||
| 3150 | |||
| 3151 | (*offset)++; | ||
| 3152 | } | ||
| 3153 | } | ||
| 3154 | else | ||
| 3155 | { | ||
| 3156 | (*offset) += tokens[variants_tok].size; | ||
| 3157 | } | ||
| 3158 | } | ||
| 3159 | |||
| 3160 | return i; | ||
| 3161 | } | ||
| 3162 | |||
| 3163 | static int cgltf_parse_json_material_mappings(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) | ||
| 3164 | { | ||
| 3165 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3166 | |||
| 3167 | int size = tokens[i].size; | ||
| 3168 | ++i; | ||
| 3169 | |||
| 3170 | for (int j = 0; j < size; ++j) | ||
| 3171 | { | ||
| 3172 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3173 | |||
| 3174 | if (cgltf_json_strcmp(tokens + i, json_chunk, "mappings") == 0) | ||
| 3175 | { | ||
| 3176 | if (out_prim->mappings) | ||
| 3177 | { | ||
| 3178 | return CGLTF_ERROR_JSON; | ||
| 3179 | } | ||
| 3180 | |||
| 3181 | cgltf_size mappings_offset = 0; | ||
| 3182 | int k = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, NULL, &mappings_offset); | ||
| 3183 | if (k < 0) | ||
| 3184 | { | ||
| 3185 | return k; | ||
| 3186 | } | ||
| 3187 | |||
| 3188 | out_prim->mappings_count = mappings_offset; | ||
| 3189 | out_prim->mappings = (cgltf_material_mapping*)cgltf_calloc(options, sizeof(cgltf_material_mapping), out_prim->mappings_count); | ||
| 3190 | |||
| 3191 | mappings_offset = 0; | ||
| 3192 | i = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, out_prim->mappings, &mappings_offset); | ||
| 3193 | } | ||
| 3194 | else | ||
| 3195 | { | ||
| 3196 | i = cgltf_skip_json(tokens, i+1); | ||
| 3197 | } | ||
| 3198 | |||
| 3199 | if (i < 0) | ||
| 3200 | { | ||
| 3201 | return i; | ||
| 3202 | } | ||
| 3203 | } | ||
| 3204 | |||
| 3205 | return i; | ||
| 3206 | } | ||
| 3207 | |||
| 3208 | static cgltf_primitive_type cgltf_json_to_primitive_type(jsmntok_t const* tok, const uint8_t* json_chunk) | ||
| 3209 | { | ||
| 3210 | int type = cgltf_json_to_int(tok, json_chunk); | ||
| 3211 | |||
| 3212 | switch (type) | ||
| 3213 | { | ||
| 3214 | case 0: | ||
| 3215 | return cgltf_primitive_type_points; | ||
| 3216 | case 1: | ||
| 3217 | return cgltf_primitive_type_lines; | ||
| 3218 | case 2: | ||
| 3219 | return cgltf_primitive_type_line_loop; | ||
| 3220 | case 3: | ||
| 3221 | return cgltf_primitive_type_line_strip; | ||
| 3222 | case 4: | ||
| 3223 | return cgltf_primitive_type_triangles; | ||
| 3224 | case 5: | ||
| 3225 | return cgltf_primitive_type_triangle_strip; | ||
| 3226 | case 6: | ||
| 3227 | return cgltf_primitive_type_triangle_fan; | ||
| 3228 | default: | ||
| 3229 | return cgltf_primitive_type_invalid; | ||
| 3230 | } | ||
| 3231 | } | ||
| 3232 | |||
| 3233 | static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) | ||
| 3234 | { | ||
| 3235 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3236 | |||
| 3237 | out_prim->type = cgltf_primitive_type_triangles; | ||
| 3238 | |||
| 3239 | int size = tokens[i].size; | ||
| 3240 | ++i; | ||
| 3241 | |||
| 3242 | for (int j = 0; j < size; ++j) | ||
| 3243 | { | ||
| 3244 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3245 | |||
| 3246 | if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) | ||
| 3247 | { | ||
| 3248 | ++i; | ||
| 3249 | out_prim->type = cgltf_json_to_primitive_type(tokens+i, json_chunk); | ||
| 3250 | ++i; | ||
| 3251 | } | ||
| 3252 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) | ||
| 3253 | { | ||
| 3254 | ++i; | ||
| 3255 | out_prim->indices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 3256 | ++i; | ||
| 3257 | } | ||
| 3258 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "material") == 0) | ||
| 3259 | { | ||
| 3260 | ++i; | ||
| 3261 | out_prim->material = CGLTF_PTRINDEX(cgltf_material, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 3262 | ++i; | ||
| 3263 | } | ||
| 3264 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "attributes") == 0) | ||
| 3265 | { | ||
| 3266 | i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_prim->attributes, &out_prim->attributes_count); | ||
| 3267 | } | ||
| 3268 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "targets") == 0) | ||
| 3269 | { | ||
| 3270 | i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_morph_target), (void**)&out_prim->targets, &out_prim->targets_count); | ||
| 3271 | if (i < 0) | ||
| 3272 | { | ||
| 3273 | return i; | ||
| 3274 | } | ||
| 3275 | |||
| 3276 | for (cgltf_size k = 0; k < out_prim->targets_count; ++k) | ||
| 3277 | { | ||
| 3278 | i = cgltf_parse_json_attribute_list(options, tokens, i, json_chunk, &out_prim->targets[k].attributes, &out_prim->targets[k].attributes_count); | ||
| 3279 | if (i < 0) | ||
| 3280 | { | ||
| 3281 | return i; | ||
| 3282 | } | ||
| 3283 | } | ||
| 3284 | } | ||
| 3285 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 3286 | { | ||
| 3287 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_prim->extras); | ||
| 3288 | } | ||
| 3289 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 3290 | { | ||
| 3291 | ++i; | ||
| 3292 | |||
| 3293 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3294 | if(out_prim->extensions) | ||
| 3295 | { | ||
| 3296 | return CGLTF_ERROR_JSON; | ||
| 3297 | } | ||
| 3298 | |||
| 3299 | int extensions_size = tokens[i].size; | ||
| 3300 | out_prim->extensions_count = 0; | ||
| 3301 | out_prim->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); | ||
| 3302 | |||
| 3303 | if (!out_prim->extensions) | ||
| 3304 | { | ||
| 3305 | return CGLTF_ERROR_NOMEM; | ||
| 3306 | } | ||
| 3307 | |||
| 3308 | ++i; | ||
| 3309 | for (int k = 0; k < extensions_size; ++k) | ||
| 3310 | { | ||
| 3311 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3312 | |||
| 3313 | if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_draco_mesh_compression") == 0) | ||
| 3314 | { | ||
| 3315 | out_prim->has_draco_mesh_compression = 1; | ||
| 3316 | i = cgltf_parse_json_draco_mesh_compression(options, tokens, i + 1, json_chunk, &out_prim->draco_mesh_compression); | ||
| 3317 | } | ||
| 3318 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0) | ||
| 3319 | { | ||
| 3320 | i = cgltf_parse_json_material_mappings(options, tokens, i + 1, json_chunk, out_prim); | ||
| 3321 | } | ||
| 3322 | else | ||
| 3323 | { | ||
| 3324 | i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_prim->extensions[out_prim->extensions_count++])); | ||
| 3325 | } | ||
| 3326 | |||
| 3327 | if (i < 0) | ||
| 3328 | { | ||
| 3329 | return i; | ||
| 3330 | } | ||
| 3331 | } | ||
| 3332 | } | ||
| 3333 | else | ||
| 3334 | { | ||
| 3335 | i = cgltf_skip_json(tokens, i+1); | ||
| 3336 | } | ||
| 3337 | |||
| 3338 | if (i < 0) | ||
| 3339 | { | ||
| 3340 | return i; | ||
| 3341 | } | ||
| 3342 | } | ||
| 3343 | |||
| 3344 | return i; | ||
| 3345 | } | ||
| 3346 | |||
| 3347 | static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh* out_mesh) | ||
| 3348 | { | ||
| 3349 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3350 | |||
| 3351 | int size = tokens[i].size; | ||
| 3352 | ++i; | ||
| 3353 | |||
| 3354 | for (int j = 0; j < size; ++j) | ||
| 3355 | { | ||
| 3356 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3357 | |||
| 3358 | if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) | ||
| 3359 | { | ||
| 3360 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_mesh->name); | ||
| 3361 | } | ||
| 3362 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "primitives") == 0) | ||
| 3363 | { | ||
| 3364 | i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_primitive), (void**)&out_mesh->primitives, &out_mesh->primitives_count); | ||
| 3365 | if (i < 0) | ||
| 3366 | { | ||
| 3367 | return i; | ||
| 3368 | } | ||
| 3369 | |||
| 3370 | for (cgltf_size prim_index = 0; prim_index < out_mesh->primitives_count; ++prim_index) | ||
| 3371 | { | ||
| 3372 | i = cgltf_parse_json_primitive(options, tokens, i, json_chunk, &out_mesh->primitives[prim_index]); | ||
| 3373 | if (i < 0) | ||
| 3374 | { | ||
| 3375 | return i; | ||
| 3376 | } | ||
| 3377 | } | ||
| 3378 | } | ||
| 3379 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0) | ||
| 3380 | { | ||
| 3381 | i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_mesh->weights, &out_mesh->weights_count); | ||
| 3382 | if (i < 0) | ||
| 3383 | { | ||
| 3384 | return i; | ||
| 3385 | } | ||
| 3386 | |||
| 3387 | i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_mesh->weights, (int)out_mesh->weights_count); | ||
| 3388 | } | ||
| 3389 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 3390 | { | ||
| 3391 | ++i; | ||
| 3392 | |||
| 3393 | out_mesh->extras.start_offset = tokens[i].start; | ||
| 3394 | out_mesh->extras.end_offset = tokens[i].end; | ||
| 3395 | |||
| 3396 | if (tokens[i].type == JSMN_OBJECT) | ||
| 3397 | { | ||
| 3398 | int extras_size = tokens[i].size; | ||
| 3399 | ++i; | ||
| 3400 | |||
| 3401 | for (int k = 0; k < extras_size; ++k) | ||
| 3402 | { | ||
| 3403 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3404 | |||
| 3405 | if (cgltf_json_strcmp(tokens+i, json_chunk, "targetNames") == 0 && tokens[i+1].type == JSMN_ARRAY) | ||
| 3406 | { | ||
| 3407 | i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_mesh->target_names, &out_mesh->target_names_count); | ||
| 3408 | } | ||
| 3409 | else | ||
| 3410 | { | ||
| 3411 | i = cgltf_skip_json(tokens, i+1); | ||
| 3412 | } | ||
| 3413 | |||
| 3414 | if (i < 0) | ||
| 3415 | { | ||
| 3416 | return i; | ||
| 3417 | } | ||
| 3418 | } | ||
| 3419 | } | ||
| 3420 | else | ||
| 3421 | { | ||
| 3422 | i = cgltf_skip_json(tokens, i); | ||
| 3423 | } | ||
| 3424 | } | ||
| 3425 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 3426 | { | ||
| 3427 | i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_mesh->extensions_count, &out_mesh->extensions); | ||
| 3428 | } | ||
| 3429 | else | ||
| 3430 | { | ||
| 3431 | i = cgltf_skip_json(tokens, i+1); | ||
| 3432 | } | ||
| 3433 | |||
| 3434 | if (i < 0) | ||
| 3435 | { | ||
| 3436 | return i; | ||
| 3437 | } | ||
| 3438 | } | ||
| 3439 | |||
| 3440 | return i; | ||
| 3441 | } | ||
| 3442 | |||
| 3443 | static int cgltf_parse_json_meshes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 3444 | { | ||
| 3445 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_mesh), (void**)&out_data->meshes, &out_data->meshes_count); | ||
| 3446 | if (i < 0) | ||
| 3447 | { | ||
| 3448 | return i; | ||
| 3449 | } | ||
| 3450 | |||
| 3451 | for (cgltf_size j = 0; j < out_data->meshes_count; ++j) | ||
| 3452 | { | ||
| 3453 | i = cgltf_parse_json_mesh(options, tokens, i, json_chunk, &out_data->meshes[j]); | ||
| 3454 | if (i < 0) | ||
| 3455 | { | ||
| 3456 | return i; | ||
| 3457 | } | ||
| 3458 | } | ||
| 3459 | return i; | ||
| 3460 | } | ||
| 3461 | |||
| 3462 | static cgltf_component_type cgltf_json_to_component_type(jsmntok_t const* tok, const uint8_t* json_chunk) | ||
| 3463 | { | ||
| 3464 | int type = cgltf_json_to_int(tok, json_chunk); | ||
| 3465 | |||
| 3466 | switch (type) | ||
| 3467 | { | ||
| 3468 | case 5120: | ||
| 3469 | return cgltf_component_type_r_8; | ||
| 3470 | case 5121: | ||
| 3471 | return cgltf_component_type_r_8u; | ||
| 3472 | case 5122: | ||
| 3473 | return cgltf_component_type_r_16; | ||
| 3474 | case 5123: | ||
| 3475 | return cgltf_component_type_r_16u; | ||
| 3476 | case 5125: | ||
| 3477 | return cgltf_component_type_r_32u; | ||
| 3478 | case 5126: | ||
| 3479 | return cgltf_component_type_r_32f; | ||
| 3480 | default: | ||
| 3481 | return cgltf_component_type_invalid; | ||
| 3482 | } | ||
| 3483 | } | ||
| 3484 | |||
| 3485 | static int cgltf_parse_json_accessor_sparse(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor_sparse* out_sparse) | ||
| 3486 | { | ||
| 3487 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3488 | |||
| 3489 | int size = tokens[i].size; | ||
| 3490 | ++i; | ||
| 3491 | |||
| 3492 | for (int j = 0; j < size; ++j) | ||
| 3493 | { | ||
| 3494 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3495 | |||
| 3496 | if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) | ||
| 3497 | { | ||
| 3498 | ++i; | ||
| 3499 | out_sparse->count = cgltf_json_to_size(tokens + i, json_chunk); | ||
| 3500 | ++i; | ||
| 3501 | } | ||
| 3502 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) | ||
| 3503 | { | ||
| 3504 | ++i; | ||
| 3505 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3506 | |||
| 3507 | int indices_size = tokens[i].size; | ||
| 3508 | ++i; | ||
| 3509 | |||
| 3510 | for (int k = 0; k < indices_size; ++k) | ||
| 3511 | { | ||
| 3512 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3513 | |||
| 3514 | if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) | ||
| 3515 | { | ||
| 3516 | ++i; | ||
| 3517 | out_sparse->indices_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 3518 | ++i; | ||
| 3519 | } | ||
| 3520 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) | ||
| 3521 | { | ||
| 3522 | ++i; | ||
| 3523 | out_sparse->indices_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); | ||
| 3524 | ++i; | ||
| 3525 | } | ||
| 3526 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) | ||
| 3527 | { | ||
| 3528 | ++i; | ||
| 3529 | out_sparse->indices_component_type = cgltf_json_to_component_type(tokens + i, json_chunk); | ||
| 3530 | ++i; | ||
| 3531 | } | ||
| 3532 | else | ||
| 3533 | { | ||
| 3534 | i = cgltf_skip_json(tokens, i+1); | ||
| 3535 | } | ||
| 3536 | |||
| 3537 | if (i < 0) | ||
| 3538 | { | ||
| 3539 | return i; | ||
| 3540 | } | ||
| 3541 | } | ||
| 3542 | } | ||
| 3543 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "values") == 0) | ||
| 3544 | { | ||
| 3545 | ++i; | ||
| 3546 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3547 | |||
| 3548 | int values_size = tokens[i].size; | ||
| 3549 | ++i; | ||
| 3550 | |||
| 3551 | for (int k = 0; k < values_size; ++k) | ||
| 3552 | { | ||
| 3553 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3554 | |||
| 3555 | if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) | ||
| 3556 | { | ||
| 3557 | ++i; | ||
| 3558 | out_sparse->values_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 3559 | ++i; | ||
| 3560 | } | ||
| 3561 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) | ||
| 3562 | { | ||
| 3563 | ++i; | ||
| 3564 | out_sparse->values_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); | ||
| 3565 | ++i; | ||
| 3566 | } | ||
| 3567 | else | ||
| 3568 | { | ||
| 3569 | i = cgltf_skip_json(tokens, i+1); | ||
| 3570 | } | ||
| 3571 | |||
| 3572 | if (i < 0) | ||
| 3573 | { | ||
| 3574 | return i; | ||
| 3575 | } | ||
| 3576 | } | ||
| 3577 | } | ||
| 3578 | else | ||
| 3579 | { | ||
| 3580 | i = cgltf_skip_json(tokens, i+1); | ||
| 3581 | } | ||
| 3582 | |||
| 3583 | if (i < 0) | ||
| 3584 | { | ||
| 3585 | return i; | ||
| 3586 | } | ||
| 3587 | } | ||
| 3588 | |||
| 3589 | return i; | ||
| 3590 | } | ||
| 3591 | |||
| 3592 | static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor* out_accessor) | ||
| 3593 | { | ||
| 3594 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3595 | |||
| 3596 | int size = tokens[i].size; | ||
| 3597 | ++i; | ||
| 3598 | |||
| 3599 | for (int j = 0; j < size; ++j) | ||
| 3600 | { | ||
| 3601 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3602 | |||
| 3603 | if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) | ||
| 3604 | { | ||
| 3605 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_accessor->name); | ||
| 3606 | } | ||
| 3607 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) | ||
| 3608 | { | ||
| 3609 | ++i; | ||
| 3610 | out_accessor->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 3611 | ++i; | ||
| 3612 | } | ||
| 3613 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) | ||
| 3614 | { | ||
| 3615 | ++i; | ||
| 3616 | out_accessor->offset = | ||
| 3617 | cgltf_json_to_size(tokens+i, json_chunk); | ||
| 3618 | ++i; | ||
| 3619 | } | ||
| 3620 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) | ||
| 3621 | { | ||
| 3622 | ++i; | ||
| 3623 | out_accessor->component_type = cgltf_json_to_component_type(tokens + i, json_chunk); | ||
| 3624 | ++i; | ||
| 3625 | } | ||
| 3626 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "normalized") == 0) | ||
| 3627 | { | ||
| 3628 | ++i; | ||
| 3629 | out_accessor->normalized = cgltf_json_to_bool(tokens+i, json_chunk); | ||
| 3630 | ++i; | ||
| 3631 | } | ||
| 3632 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) | ||
| 3633 | { | ||
| 3634 | ++i; | ||
| 3635 | out_accessor->count = cgltf_json_to_size(tokens+i, json_chunk); | ||
| 3636 | ++i; | ||
| 3637 | } | ||
| 3638 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) | ||
| 3639 | { | ||
| 3640 | ++i; | ||
| 3641 | if (cgltf_json_strcmp(tokens+i, json_chunk, "SCALAR") == 0) | ||
| 3642 | { | ||
| 3643 | out_accessor->type = cgltf_type_scalar; | ||
| 3644 | } | ||
| 3645 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC2") == 0) | ||
| 3646 | { | ||
| 3647 | out_accessor->type = cgltf_type_vec2; | ||
| 3648 | } | ||
| 3649 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC3") == 0) | ||
| 3650 | { | ||
| 3651 | out_accessor->type = cgltf_type_vec3; | ||
| 3652 | } | ||
| 3653 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC4") == 0) | ||
| 3654 | { | ||
| 3655 | out_accessor->type = cgltf_type_vec4; | ||
| 3656 | } | ||
| 3657 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT2") == 0) | ||
| 3658 | { | ||
| 3659 | out_accessor->type = cgltf_type_mat2; | ||
| 3660 | } | ||
| 3661 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT3") == 0) | ||
| 3662 | { | ||
| 3663 | out_accessor->type = cgltf_type_mat3; | ||
| 3664 | } | ||
| 3665 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT4") == 0) | ||
| 3666 | { | ||
| 3667 | out_accessor->type = cgltf_type_mat4; | ||
| 3668 | } | ||
| 3669 | ++i; | ||
| 3670 | } | ||
| 3671 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "min") == 0) | ||
| 3672 | { | ||
| 3673 | ++i; | ||
| 3674 | out_accessor->has_min = 1; | ||
| 3675 | // note: we can't parse the precise number of elements since type may not have been computed yet | ||
| 3676 | int min_size = tokens[i].size > 16 ? 16 : tokens[i].size; | ||
| 3677 | i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->min, min_size); | ||
| 3678 | } | ||
| 3679 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "max") == 0) | ||
| 3680 | { | ||
| 3681 | ++i; | ||
| 3682 | out_accessor->has_max = 1; | ||
| 3683 | // note: we can't parse the precise number of elements since type may not have been computed yet | ||
| 3684 | int max_size = tokens[i].size > 16 ? 16 : tokens[i].size; | ||
| 3685 | i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->max, max_size); | ||
| 3686 | } | ||
| 3687 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "sparse") == 0) | ||
| 3688 | { | ||
| 3689 | out_accessor->is_sparse = 1; | ||
| 3690 | i = cgltf_parse_json_accessor_sparse(tokens, i + 1, json_chunk, &out_accessor->sparse); | ||
| 3691 | } | ||
| 3692 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 3693 | { | ||
| 3694 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_accessor->extras); | ||
| 3695 | } | ||
| 3696 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 3697 | { | ||
| 3698 | i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_accessor->extensions_count, &out_accessor->extensions); | ||
| 3699 | } | ||
| 3700 | else | ||
| 3701 | { | ||
| 3702 | i = cgltf_skip_json(tokens, i+1); | ||
| 3703 | } | ||
| 3704 | |||
| 3705 | if (i < 0) | ||
| 3706 | { | ||
| 3707 | return i; | ||
| 3708 | } | ||
| 3709 | } | ||
| 3710 | |||
| 3711 | return i; | ||
| 3712 | } | ||
| 3713 | |||
| 3714 | static int cgltf_parse_json_texture_transform(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_transform* out_texture_transform) | ||
| 3715 | { | ||
| 3716 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3717 | |||
| 3718 | int size = tokens[i].size; | ||
| 3719 | ++i; | ||
| 3720 | |||
| 3721 | for (int j = 0; j < size; ++j) | ||
| 3722 | { | ||
| 3723 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3724 | |||
| 3725 | if (cgltf_json_strcmp(tokens + i, json_chunk, "offset") == 0) | ||
| 3726 | { | ||
| 3727 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->offset, 2); | ||
| 3728 | } | ||
| 3729 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "rotation") == 0) | ||
| 3730 | { | ||
| 3731 | ++i; | ||
| 3732 | out_texture_transform->rotation = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 3733 | ++i; | ||
| 3734 | } | ||
| 3735 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) | ||
| 3736 | { | ||
| 3737 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->scale, 2); | ||
| 3738 | } | ||
| 3739 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) | ||
| 3740 | { | ||
| 3741 | ++i; | ||
| 3742 | out_texture_transform->has_texcoord = 1; | ||
| 3743 | out_texture_transform->texcoord = cgltf_json_to_int(tokens + i, json_chunk); | ||
| 3744 | ++i; | ||
| 3745 | } | ||
| 3746 | else | ||
| 3747 | { | ||
| 3748 | i = cgltf_skip_json(tokens, i + 1); | ||
| 3749 | } | ||
| 3750 | |||
| 3751 | if (i < 0) | ||
| 3752 | { | ||
| 3753 | return i; | ||
| 3754 | } | ||
| 3755 | } | ||
| 3756 | |||
| 3757 | return i; | ||
| 3758 | } | ||
| 3759 | |||
| 3760 | static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_view* out_texture_view) | ||
| 3761 | { | ||
| 3762 | (void)options; | ||
| 3763 | |||
| 3764 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3765 | |||
| 3766 | out_texture_view->scale = 1.0f; | ||
| 3767 | cgltf_fill_float_array(out_texture_view->transform.scale, 2, 1.0f); | ||
| 3768 | |||
| 3769 | int size = tokens[i].size; | ||
| 3770 | ++i; | ||
| 3771 | |||
| 3772 | for (int j = 0; j < size; ++j) | ||
| 3773 | { | ||
| 3774 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3775 | |||
| 3776 | if (cgltf_json_strcmp(tokens + i, json_chunk, "index") == 0) | ||
| 3777 | { | ||
| 3778 | ++i; | ||
| 3779 | out_texture_view->texture = CGLTF_PTRINDEX(cgltf_texture, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 3780 | ++i; | ||
| 3781 | } | ||
| 3782 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) | ||
| 3783 | { | ||
| 3784 | ++i; | ||
| 3785 | out_texture_view->texcoord = cgltf_json_to_int(tokens + i, json_chunk); | ||
| 3786 | ++i; | ||
| 3787 | } | ||
| 3788 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) | ||
| 3789 | { | ||
| 3790 | ++i; | ||
| 3791 | out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 3792 | ++i; | ||
| 3793 | } | ||
| 3794 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "strength") == 0) | ||
| 3795 | { | ||
| 3796 | ++i; | ||
| 3797 | out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 3798 | ++i; | ||
| 3799 | } | ||
| 3800 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 3801 | { | ||
| 3802 | ++i; | ||
| 3803 | |||
| 3804 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3805 | int extensions_size = tokens[i].size; | ||
| 3806 | |||
| 3807 | ++i; | ||
| 3808 | |||
| 3809 | for (int k = 0; k < extensions_size; ++k) | ||
| 3810 | { | ||
| 3811 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3812 | |||
| 3813 | if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_texture_transform") == 0) | ||
| 3814 | { | ||
| 3815 | out_texture_view->has_transform = 1; | ||
| 3816 | i = cgltf_parse_json_texture_transform(tokens, i + 1, json_chunk, &out_texture_view->transform); | ||
| 3817 | } | ||
| 3818 | else | ||
| 3819 | { | ||
| 3820 | i = cgltf_skip_json(tokens, i + 1); | ||
| 3821 | } | ||
| 3822 | |||
| 3823 | if (i < 0) | ||
| 3824 | { | ||
| 3825 | return i; | ||
| 3826 | } | ||
| 3827 | } | ||
| 3828 | } | ||
| 3829 | else | ||
| 3830 | { | ||
| 3831 | i = cgltf_skip_json(tokens, i + 1); | ||
| 3832 | } | ||
| 3833 | |||
| 3834 | if (i < 0) | ||
| 3835 | { | ||
| 3836 | return i; | ||
| 3837 | } | ||
| 3838 | } | ||
| 3839 | |||
| 3840 | return i; | ||
| 3841 | } | ||
| 3842 | |||
| 3843 | static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_metallic_roughness* out_pbr) | ||
| 3844 | { | ||
| 3845 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3846 | |||
| 3847 | int size = tokens[i].size; | ||
| 3848 | ++i; | ||
| 3849 | |||
| 3850 | for (int j = 0; j < size; ++j) | ||
| 3851 | { | ||
| 3852 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3853 | |||
| 3854 | if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0) | ||
| 3855 | { | ||
| 3856 | ++i; | ||
| 3857 | out_pbr->metallic_factor = | ||
| 3858 | cgltf_json_to_float(tokens + i, json_chunk); | ||
| 3859 | ++i; | ||
| 3860 | } | ||
| 3861 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0) | ||
| 3862 | { | ||
| 3863 | ++i; | ||
| 3864 | out_pbr->roughness_factor = | ||
| 3865 | cgltf_json_to_float(tokens+i, json_chunk); | ||
| 3866 | ++i; | ||
| 3867 | } | ||
| 3868 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorFactor") == 0) | ||
| 3869 | { | ||
| 3870 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->base_color_factor, 4); | ||
| 3871 | } | ||
| 3872 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0) | ||
| 3873 | { | ||
| 3874 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->base_color_texture); | ||
| 3875 | } | ||
| 3876 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0) | ||
| 3877 | { | ||
| 3878 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->metallic_roughness_texture); | ||
| 3879 | } | ||
| 3880 | else | ||
| 3881 | { | ||
| 3882 | i = cgltf_skip_json(tokens, i+1); | ||
| 3883 | } | ||
| 3884 | |||
| 3885 | if (i < 0) | ||
| 3886 | { | ||
| 3887 | return i; | ||
| 3888 | } | ||
| 3889 | } | ||
| 3890 | |||
| 3891 | return i; | ||
| 3892 | } | ||
| 3893 | |||
| 3894 | static int cgltf_parse_json_pbr_specular_glossiness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_specular_glossiness* out_pbr) | ||
| 3895 | { | ||
| 3896 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3897 | int size = tokens[i].size; | ||
| 3898 | ++i; | ||
| 3899 | |||
| 3900 | for (int j = 0; j < size; ++j) | ||
| 3901 | { | ||
| 3902 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3903 | |||
| 3904 | if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseFactor") == 0) | ||
| 3905 | { | ||
| 3906 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->diffuse_factor, 4); | ||
| 3907 | } | ||
| 3908 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) | ||
| 3909 | { | ||
| 3910 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->specular_factor, 3); | ||
| 3911 | } | ||
| 3912 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "glossinessFactor") == 0) | ||
| 3913 | { | ||
| 3914 | ++i; | ||
| 3915 | out_pbr->glossiness_factor = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 3916 | ++i; | ||
| 3917 | } | ||
| 3918 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseTexture") == 0) | ||
| 3919 | { | ||
| 3920 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->diffuse_texture); | ||
| 3921 | } | ||
| 3922 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularGlossinessTexture") == 0) | ||
| 3923 | { | ||
| 3924 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->specular_glossiness_texture); | ||
| 3925 | } | ||
| 3926 | else | ||
| 3927 | { | ||
| 3928 | i = cgltf_skip_json(tokens, i+1); | ||
| 3929 | } | ||
| 3930 | |||
| 3931 | if (i < 0) | ||
| 3932 | { | ||
| 3933 | return i; | ||
| 3934 | } | ||
| 3935 | } | ||
| 3936 | |||
| 3937 | return i; | ||
| 3938 | } | ||
| 3939 | |||
| 3940 | static int cgltf_parse_json_clearcoat(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_clearcoat* out_clearcoat) | ||
| 3941 | { | ||
| 3942 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3943 | int size = tokens[i].size; | ||
| 3944 | ++i; | ||
| 3945 | |||
| 3946 | for (int j = 0; j < size; ++j) | ||
| 3947 | { | ||
| 3948 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 3949 | |||
| 3950 | if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatFactor") == 0) | ||
| 3951 | { | ||
| 3952 | ++i; | ||
| 3953 | out_clearcoat->clearcoat_factor = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 3954 | ++i; | ||
| 3955 | } | ||
| 3956 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessFactor") == 0) | ||
| 3957 | { | ||
| 3958 | ++i; | ||
| 3959 | out_clearcoat->clearcoat_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 3960 | ++i; | ||
| 3961 | } | ||
| 3962 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatTexture") == 0) | ||
| 3963 | { | ||
| 3964 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_texture); | ||
| 3965 | } | ||
| 3966 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessTexture") == 0) | ||
| 3967 | { | ||
| 3968 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_roughness_texture); | ||
| 3969 | } | ||
| 3970 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatNormalTexture") == 0) | ||
| 3971 | { | ||
| 3972 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_normal_texture); | ||
| 3973 | } | ||
| 3974 | else | ||
| 3975 | { | ||
| 3976 | i = cgltf_skip_json(tokens, i+1); | ||
| 3977 | } | ||
| 3978 | |||
| 3979 | if (i < 0) | ||
| 3980 | { | ||
| 3981 | return i; | ||
| 3982 | } | ||
| 3983 | } | ||
| 3984 | |||
| 3985 | return i; | ||
| 3986 | } | ||
| 3987 | |||
| 3988 | static int cgltf_parse_json_ior(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_ior* out_ior) | ||
| 3989 | { | ||
| 3990 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 3991 | int size = tokens[i].size; | ||
| 3992 | ++i; | ||
| 3993 | |||
| 3994 | // Default values | ||
| 3995 | out_ior->ior = 1.5f; | ||
| 3996 | |||
| 3997 | for (int j = 0; j < size; ++j) | ||
| 3998 | { | ||
| 3999 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4000 | |||
| 4001 | if (cgltf_json_strcmp(tokens+i, json_chunk, "ior") == 0) | ||
| 4002 | { | ||
| 4003 | ++i; | ||
| 4004 | out_ior->ior = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4005 | ++i; | ||
| 4006 | } | ||
| 4007 | else | ||
| 4008 | { | ||
| 4009 | i = cgltf_skip_json(tokens, i+1); | ||
| 4010 | } | ||
| 4011 | |||
| 4012 | if (i < 0) | ||
| 4013 | { | ||
| 4014 | return i; | ||
| 4015 | } | ||
| 4016 | } | ||
| 4017 | |||
| 4018 | return i; | ||
| 4019 | } | ||
| 4020 | |||
| 4021 | static int cgltf_parse_json_specular(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_specular* out_specular) | ||
| 4022 | { | ||
| 4023 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4024 | int size = tokens[i].size; | ||
| 4025 | ++i; | ||
| 4026 | |||
| 4027 | // Default values | ||
| 4028 | out_specular->specular_factor = 1.0f; | ||
| 4029 | cgltf_fill_float_array(out_specular->specular_color_factor, 3, 1.0f); | ||
| 4030 | |||
| 4031 | for (int j = 0; j < size; ++j) | ||
| 4032 | { | ||
| 4033 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4034 | |||
| 4035 | if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) | ||
| 4036 | { | ||
| 4037 | ++i; | ||
| 4038 | out_specular->specular_factor = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4039 | ++i; | ||
| 4040 | } | ||
| 4041 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularColorFactor") == 0) | ||
| 4042 | { | ||
| 4043 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_specular->specular_color_factor, 3); | ||
| 4044 | } | ||
| 4045 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularTexture") == 0) | ||
| 4046 | { | ||
| 4047 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_texture); | ||
| 4048 | } | ||
| 4049 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "specularColorTexture") == 0) | ||
| 4050 | { | ||
| 4051 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_color_texture); | ||
| 4052 | } | ||
| 4053 | else | ||
| 4054 | { | ||
| 4055 | i = cgltf_skip_json(tokens, i+1); | ||
| 4056 | } | ||
| 4057 | |||
| 4058 | if (i < 0) | ||
| 4059 | { | ||
| 4060 | return i; | ||
| 4061 | } | ||
| 4062 | } | ||
| 4063 | |||
| 4064 | return i; | ||
| 4065 | } | ||
| 4066 | |||
| 4067 | static int cgltf_parse_json_transmission(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_transmission* out_transmission) | ||
| 4068 | { | ||
| 4069 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4070 | int size = tokens[i].size; | ||
| 4071 | ++i; | ||
| 4072 | |||
| 4073 | for (int j = 0; j < size; ++j) | ||
| 4074 | { | ||
| 4075 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4076 | |||
| 4077 | if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionFactor") == 0) | ||
| 4078 | { | ||
| 4079 | ++i; | ||
| 4080 | out_transmission->transmission_factor = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4081 | ++i; | ||
| 4082 | } | ||
| 4083 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionTexture") == 0) | ||
| 4084 | { | ||
| 4085 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_transmission->transmission_texture); | ||
| 4086 | } | ||
| 4087 | else | ||
| 4088 | { | ||
| 4089 | i = cgltf_skip_json(tokens, i+1); | ||
| 4090 | } | ||
| 4091 | |||
| 4092 | if (i < 0) | ||
| 4093 | { | ||
| 4094 | return i; | ||
| 4095 | } | ||
| 4096 | } | ||
| 4097 | |||
| 4098 | return i; | ||
| 4099 | } | ||
| 4100 | |||
| 4101 | static int cgltf_parse_json_volume(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_volume* out_volume) | ||
| 4102 | { | ||
| 4103 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4104 | int size = tokens[i].size; | ||
| 4105 | ++i; | ||
| 4106 | |||
| 4107 | for (int j = 0; j < size; ++j) | ||
| 4108 | { | ||
| 4109 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4110 | |||
| 4111 | if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessFactor") == 0) | ||
| 4112 | { | ||
| 4113 | ++i; | ||
| 4114 | out_volume->thickness_factor = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4115 | ++i; | ||
| 4116 | } | ||
| 4117 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessTexture") == 0) | ||
| 4118 | { | ||
| 4119 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_volume->thickness_texture); | ||
| 4120 | } | ||
| 4121 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationColor") == 0) | ||
| 4122 | { | ||
| 4123 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_volume->attenuation_color, 3); | ||
| 4124 | } | ||
| 4125 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationDistance") == 0) | ||
| 4126 | { | ||
| 4127 | ++i; | ||
| 4128 | out_volume->attenuation_distance = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4129 | ++i; | ||
| 4130 | } | ||
| 4131 | else | ||
| 4132 | { | ||
| 4133 | i = cgltf_skip_json(tokens, i + 1); | ||
| 4134 | } | ||
| 4135 | |||
| 4136 | if (i < 0) | ||
| 4137 | { | ||
| 4138 | return i; | ||
| 4139 | } | ||
| 4140 | } | ||
| 4141 | |||
| 4142 | return i; | ||
| 4143 | } | ||
| 4144 | |||
| 4145 | static int cgltf_parse_json_sheen(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sheen* out_sheen) | ||
| 4146 | { | ||
| 4147 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4148 | int size = tokens[i].size; | ||
| 4149 | ++i; | ||
| 4150 | |||
| 4151 | for (int j = 0; j < size; ++j) | ||
| 4152 | { | ||
| 4153 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4154 | |||
| 4155 | if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorFactor") == 0) | ||
| 4156 | { | ||
| 4157 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_sheen->sheen_color_factor, 3); | ||
| 4158 | } | ||
| 4159 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorTexture") == 0) | ||
| 4160 | { | ||
| 4161 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_color_texture); | ||
| 4162 | } | ||
| 4163 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessFactor") == 0) | ||
| 4164 | { | ||
| 4165 | ++i; | ||
| 4166 | out_sheen->sheen_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4167 | ++i; | ||
| 4168 | } | ||
| 4169 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessTexture") == 0) | ||
| 4170 | { | ||
| 4171 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_roughness_texture); | ||
| 4172 | } | ||
| 4173 | else | ||
| 4174 | { | ||
| 4175 | i = cgltf_skip_json(tokens, i+1); | ||
| 4176 | } | ||
| 4177 | |||
| 4178 | if (i < 0) | ||
| 4179 | { | ||
| 4180 | return i; | ||
| 4181 | } | ||
| 4182 | } | ||
| 4183 | |||
| 4184 | return i; | ||
| 4185 | } | ||
| 4186 | |||
| 4187 | static int cgltf_parse_json_emissive_strength(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_emissive_strength* out_emissive_strength) | ||
| 4188 | { | ||
| 4189 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4190 | int size = tokens[i].size; | ||
| 4191 | ++i; | ||
| 4192 | |||
| 4193 | // Default | ||
| 4194 | out_emissive_strength->emissive_strength = 1.f; | ||
| 4195 | |||
| 4196 | for (int j = 0; j < size; ++j) | ||
| 4197 | { | ||
| 4198 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4199 | |||
| 4200 | if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveStrength") == 0) | ||
| 4201 | { | ||
| 4202 | ++i; | ||
| 4203 | out_emissive_strength->emissive_strength = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4204 | ++i; | ||
| 4205 | } | ||
| 4206 | else | ||
| 4207 | { | ||
| 4208 | i = cgltf_skip_json(tokens, i + 1); | ||
| 4209 | } | ||
| 4210 | |||
| 4211 | if (i < 0) | ||
| 4212 | { | ||
| 4213 | return i; | ||
| 4214 | } | ||
| 4215 | } | ||
| 4216 | |||
| 4217 | return i; | ||
| 4218 | } | ||
| 4219 | |||
| 4220 | static int cgltf_parse_json_iridescence(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_iridescence* out_iridescence) | ||
| 4221 | { | ||
| 4222 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4223 | int size = tokens[i].size; | ||
| 4224 | ++i; | ||
| 4225 | |||
| 4226 | // Default | ||
| 4227 | out_iridescence->iridescence_ior = 1.3f; | ||
| 4228 | out_iridescence->iridescence_thickness_min = 100.f; | ||
| 4229 | out_iridescence->iridescence_thickness_max = 400.f; | ||
| 4230 | |||
| 4231 | for (int j = 0; j < size; ++j) | ||
| 4232 | { | ||
| 4233 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4234 | |||
| 4235 | if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceFactor") == 0) | ||
| 4236 | { | ||
| 4237 | ++i; | ||
| 4238 | out_iridescence->iridescence_factor = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4239 | ++i; | ||
| 4240 | } | ||
| 4241 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceTexture") == 0) | ||
| 4242 | { | ||
| 4243 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_iridescence->iridescence_texture); | ||
| 4244 | } | ||
| 4245 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceIor") == 0) | ||
| 4246 | { | ||
| 4247 | ++i; | ||
| 4248 | out_iridescence->iridescence_ior = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4249 | ++i; | ||
| 4250 | } | ||
| 4251 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessMinimum") == 0) | ||
| 4252 | { | ||
| 4253 | ++i; | ||
| 4254 | out_iridescence->iridescence_thickness_min = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4255 | ++i; | ||
| 4256 | } | ||
| 4257 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessMaximum") == 0) | ||
| 4258 | { | ||
| 4259 | ++i; | ||
| 4260 | out_iridescence->iridescence_thickness_max = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4261 | ++i; | ||
| 4262 | } | ||
| 4263 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessTexture") == 0) | ||
| 4264 | { | ||
| 4265 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_iridescence->iridescence_thickness_texture); | ||
| 4266 | } | ||
| 4267 | else | ||
| 4268 | { | ||
| 4269 | i = cgltf_skip_json(tokens, i + 1); | ||
| 4270 | } | ||
| 4271 | |||
| 4272 | if (i < 0) | ||
| 4273 | { | ||
| 4274 | return i; | ||
| 4275 | } | ||
| 4276 | } | ||
| 4277 | |||
| 4278 | return i; | ||
| 4279 | } | ||
| 4280 | |||
| 4281 | static int cgltf_parse_json_anisotropy(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_anisotropy* out_anisotropy) | ||
| 4282 | { | ||
| 4283 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4284 | int size = tokens[i].size; | ||
| 4285 | ++i; | ||
| 4286 | |||
| 4287 | |||
| 4288 | for (int j = 0; j < size; ++j) | ||
| 4289 | { | ||
| 4290 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4291 | |||
| 4292 | if (cgltf_json_strcmp(tokens + i, json_chunk, "anisotropyStrength") == 0) | ||
| 4293 | { | ||
| 4294 | ++i; | ||
| 4295 | out_anisotropy->anisotropy_strength = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4296 | ++i; | ||
| 4297 | } | ||
| 4298 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "anisotropyRotation") == 0) | ||
| 4299 | { | ||
| 4300 | ++i; | ||
| 4301 | out_anisotropy->anisotropy_rotation = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4302 | ++i; | ||
| 4303 | } | ||
| 4304 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "anisotropyTexture") == 0) | ||
| 4305 | { | ||
| 4306 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_anisotropy->anisotropy_texture); | ||
| 4307 | } | ||
| 4308 | else | ||
| 4309 | { | ||
| 4310 | i = cgltf_skip_json(tokens, i + 1); | ||
| 4311 | } | ||
| 4312 | |||
| 4313 | if (i < 0) | ||
| 4314 | { | ||
| 4315 | return i; | ||
| 4316 | } | ||
| 4317 | } | ||
| 4318 | |||
| 4319 | return i; | ||
| 4320 | } | ||
| 4321 | |||
| 4322 | static int cgltf_parse_json_dispersion(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_dispersion* out_dispersion) | ||
| 4323 | { | ||
| 4324 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4325 | int size = tokens[i].size; | ||
| 4326 | ++i; | ||
| 4327 | |||
| 4328 | |||
| 4329 | for (int j = 0; j < size; ++j) | ||
| 4330 | { | ||
| 4331 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4332 | |||
| 4333 | if (cgltf_json_strcmp(tokens + i, json_chunk, "dispersion") == 0) | ||
| 4334 | { | ||
| 4335 | ++i; | ||
| 4336 | out_dispersion->dispersion = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4337 | ++i; | ||
| 4338 | } | ||
| 4339 | else | ||
| 4340 | { | ||
| 4341 | i = cgltf_skip_json(tokens, i + 1); | ||
| 4342 | } | ||
| 4343 | |||
| 4344 | if (i < 0) | ||
| 4345 | { | ||
| 4346 | return i; | ||
| 4347 | } | ||
| 4348 | } | ||
| 4349 | |||
| 4350 | return i; | ||
| 4351 | } | ||
| 4352 | |||
| 4353 | static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_image* out_image) | ||
| 4354 | { | ||
| 4355 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4356 | |||
| 4357 | int size = tokens[i].size; | ||
| 4358 | ++i; | ||
| 4359 | |||
| 4360 | for (int j = 0; j < size; ++j) | ||
| 4361 | { | ||
| 4362 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4363 | |||
| 4364 | if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) | ||
| 4365 | { | ||
| 4366 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->uri); | ||
| 4367 | } | ||
| 4368 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) | ||
| 4369 | { | ||
| 4370 | ++i; | ||
| 4371 | out_image->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 4372 | ++i; | ||
| 4373 | } | ||
| 4374 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "mimeType") == 0) | ||
| 4375 | { | ||
| 4376 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->mime_type); | ||
| 4377 | } | ||
| 4378 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) | ||
| 4379 | { | ||
| 4380 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->name); | ||
| 4381 | } | ||
| 4382 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 4383 | { | ||
| 4384 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_image->extras); | ||
| 4385 | } | ||
| 4386 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 4387 | { | ||
| 4388 | i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_image->extensions_count, &out_image->extensions); | ||
| 4389 | } | ||
| 4390 | else | ||
| 4391 | { | ||
| 4392 | i = cgltf_skip_json(tokens, i + 1); | ||
| 4393 | } | ||
| 4394 | |||
| 4395 | if (i < 0) | ||
| 4396 | { | ||
| 4397 | return i; | ||
| 4398 | } | ||
| 4399 | } | ||
| 4400 | |||
| 4401 | return i; | ||
| 4402 | } | ||
| 4403 | |||
| 4404 | static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sampler* out_sampler) | ||
| 4405 | { | ||
| 4406 | (void)options; | ||
| 4407 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4408 | |||
| 4409 | out_sampler->wrap_s = 10497; | ||
| 4410 | out_sampler->wrap_t = 10497; | ||
| 4411 | |||
| 4412 | int size = tokens[i].size; | ||
| 4413 | ++i; | ||
| 4414 | |||
| 4415 | for (int j = 0; j < size; ++j) | ||
| 4416 | { | ||
| 4417 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4418 | |||
| 4419 | if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) | ||
| 4420 | { | ||
| 4421 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_sampler->name); | ||
| 4422 | } | ||
| 4423 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "magFilter") == 0) | ||
| 4424 | { | ||
| 4425 | ++i; | ||
| 4426 | out_sampler->mag_filter | ||
| 4427 | = cgltf_json_to_int(tokens + i, json_chunk); | ||
| 4428 | ++i; | ||
| 4429 | } | ||
| 4430 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0) | ||
| 4431 | { | ||
| 4432 | ++i; | ||
| 4433 | out_sampler->min_filter | ||
| 4434 | = cgltf_json_to_int(tokens + i, json_chunk); | ||
| 4435 | ++i; | ||
| 4436 | } | ||
| 4437 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0) | ||
| 4438 | { | ||
| 4439 | ++i; | ||
| 4440 | out_sampler->wrap_s | ||
| 4441 | = cgltf_json_to_int(tokens + i, json_chunk); | ||
| 4442 | ++i; | ||
| 4443 | } | ||
| 4444 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) | ||
| 4445 | { | ||
| 4446 | ++i; | ||
| 4447 | out_sampler->wrap_t | ||
| 4448 | = cgltf_json_to_int(tokens + i, json_chunk); | ||
| 4449 | ++i; | ||
| 4450 | } | ||
| 4451 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 4452 | { | ||
| 4453 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras); | ||
| 4454 | } | ||
| 4455 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 4456 | { | ||
| 4457 | i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions); | ||
| 4458 | } | ||
| 4459 | else | ||
| 4460 | { | ||
| 4461 | i = cgltf_skip_json(tokens, i + 1); | ||
| 4462 | } | ||
| 4463 | |||
| 4464 | if (i < 0) | ||
| 4465 | { | ||
| 4466 | return i; | ||
| 4467 | } | ||
| 4468 | } | ||
| 4469 | |||
| 4470 | return i; | ||
| 4471 | } | ||
| 4472 | |||
| 4473 | static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture* out_texture) | ||
| 4474 | { | ||
| 4475 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4476 | |||
| 4477 | int size = tokens[i].size; | ||
| 4478 | ++i; | ||
| 4479 | |||
| 4480 | for (int j = 0; j < size; ++j) | ||
| 4481 | { | ||
| 4482 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4483 | |||
| 4484 | if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) | ||
| 4485 | { | ||
| 4486 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_texture->name); | ||
| 4487 | } | ||
| 4488 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "sampler") == 0) | ||
| 4489 | { | ||
| 4490 | ++i; | ||
| 4491 | out_texture->sampler = CGLTF_PTRINDEX(cgltf_sampler, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 4492 | ++i; | ||
| 4493 | } | ||
| 4494 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) | ||
| 4495 | { | ||
| 4496 | ++i; | ||
| 4497 | out_texture->image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 4498 | ++i; | ||
| 4499 | } | ||
| 4500 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 4501 | { | ||
| 4502 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture->extras); | ||
| 4503 | } | ||
| 4504 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 4505 | { | ||
| 4506 | ++i; | ||
| 4507 | |||
| 4508 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4509 | if (out_texture->extensions) | ||
| 4510 | { | ||
| 4511 | return CGLTF_ERROR_JSON; | ||
| 4512 | } | ||
| 4513 | |||
| 4514 | int extensions_size = tokens[i].size; | ||
| 4515 | ++i; | ||
| 4516 | out_texture->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); | ||
| 4517 | out_texture->extensions_count = 0; | ||
| 4518 | |||
| 4519 | if (!out_texture->extensions) | ||
| 4520 | { | ||
| 4521 | return CGLTF_ERROR_NOMEM; | ||
| 4522 | } | ||
| 4523 | |||
| 4524 | for (int k = 0; k < extensions_size; ++k) | ||
| 4525 | { | ||
| 4526 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4527 | |||
| 4528 | if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_texture_basisu") == 0) | ||
| 4529 | { | ||
| 4530 | out_texture->has_basisu = 1; | ||
| 4531 | ++i; | ||
| 4532 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4533 | int num_properties = tokens[i].size; | ||
| 4534 | ++i; | ||
| 4535 | |||
| 4536 | for (int t = 0; t < num_properties; ++t) | ||
| 4537 | { | ||
| 4538 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4539 | |||
| 4540 | if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) | ||
| 4541 | { | ||
| 4542 | ++i; | ||
| 4543 | out_texture->basisu_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 4544 | ++i; | ||
| 4545 | } | ||
| 4546 | else | ||
| 4547 | { | ||
| 4548 | i = cgltf_skip_json(tokens, i + 1); | ||
| 4549 | } | ||
| 4550 | if (i < 0) | ||
| 4551 | { | ||
| 4552 | return i; | ||
| 4553 | } | ||
| 4554 | } | ||
| 4555 | } | ||
| 4556 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "EXT_texture_webp") == 0) | ||
| 4557 | { | ||
| 4558 | out_texture->has_webp = 1; | ||
| 4559 | ++i; | ||
| 4560 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4561 | int num_properties = tokens[i].size; | ||
| 4562 | ++i; | ||
| 4563 | |||
| 4564 | for (int t = 0; t < num_properties; ++t) | ||
| 4565 | { | ||
| 4566 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4567 | |||
| 4568 | if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) | ||
| 4569 | { | ||
| 4570 | ++i; | ||
| 4571 | out_texture->webp_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 4572 | ++i; | ||
| 4573 | } | ||
| 4574 | else | ||
| 4575 | { | ||
| 4576 | i = cgltf_skip_json(tokens, i + 1); | ||
| 4577 | } | ||
| 4578 | if (i < 0) | ||
| 4579 | { | ||
| 4580 | return i; | ||
| 4581 | } | ||
| 4582 | } | ||
| 4583 | } | ||
| 4584 | else | ||
| 4585 | { | ||
| 4586 | i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture->extensions[out_texture->extensions_count++])); | ||
| 4587 | } | ||
| 4588 | |||
| 4589 | if (i < 0) | ||
| 4590 | { | ||
| 4591 | return i; | ||
| 4592 | } | ||
| 4593 | } | ||
| 4594 | } | ||
| 4595 | else | ||
| 4596 | { | ||
| 4597 | i = cgltf_skip_json(tokens, i + 1); | ||
| 4598 | } | ||
| 4599 | |||
| 4600 | if (i < 0) | ||
| 4601 | { | ||
| 4602 | return i; | ||
| 4603 | } | ||
| 4604 | } | ||
| 4605 | |||
| 4606 | return i; | ||
| 4607 | } | ||
| 4608 | |||
| 4609 | static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material* out_material) | ||
| 4610 | { | ||
| 4611 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4612 | |||
| 4613 | cgltf_fill_float_array(out_material->pbr_metallic_roughness.base_color_factor, 4, 1.0f); | ||
| 4614 | out_material->pbr_metallic_roughness.metallic_factor = 1.0f; | ||
| 4615 | out_material->pbr_metallic_roughness.roughness_factor = 1.0f; | ||
| 4616 | |||
| 4617 | cgltf_fill_float_array(out_material->pbr_specular_glossiness.diffuse_factor, 4, 1.0f); | ||
| 4618 | cgltf_fill_float_array(out_material->pbr_specular_glossiness.specular_factor, 3, 1.0f); | ||
| 4619 | out_material->pbr_specular_glossiness.glossiness_factor = 1.0f; | ||
| 4620 | |||
| 4621 | cgltf_fill_float_array(out_material->volume.attenuation_color, 3, 1.0f); | ||
| 4622 | out_material->volume.attenuation_distance = FLT_MAX; | ||
| 4623 | |||
| 4624 | out_material->alpha_cutoff = 0.5f; | ||
| 4625 | |||
| 4626 | int size = tokens[i].size; | ||
| 4627 | ++i; | ||
| 4628 | |||
| 4629 | for (int j = 0; j < size; ++j) | ||
| 4630 | { | ||
| 4631 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4632 | |||
| 4633 | if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) | ||
| 4634 | { | ||
| 4635 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_material->name); | ||
| 4636 | } | ||
| 4637 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "pbrMetallicRoughness") == 0) | ||
| 4638 | { | ||
| 4639 | out_material->has_pbr_metallic_roughness = 1; | ||
| 4640 | i = cgltf_parse_json_pbr_metallic_roughness(options, tokens, i + 1, json_chunk, &out_material->pbr_metallic_roughness); | ||
| 4641 | } | ||
| 4642 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "emissiveFactor") == 0) | ||
| 4643 | { | ||
| 4644 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_material->emissive_factor, 3); | ||
| 4645 | } | ||
| 4646 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "normalTexture") == 0) | ||
| 4647 | { | ||
| 4648 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, | ||
| 4649 | &out_material->normal_texture); | ||
| 4650 | } | ||
| 4651 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "occlusionTexture") == 0) | ||
| 4652 | { | ||
| 4653 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, | ||
| 4654 | &out_material->occlusion_texture); | ||
| 4655 | } | ||
| 4656 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveTexture") == 0) | ||
| 4657 | { | ||
| 4658 | i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, | ||
| 4659 | &out_material->emissive_texture); | ||
| 4660 | } | ||
| 4661 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaMode") == 0) | ||
| 4662 | { | ||
| 4663 | ++i; | ||
| 4664 | if (cgltf_json_strcmp(tokens + i, json_chunk, "OPAQUE") == 0) | ||
| 4665 | { | ||
| 4666 | out_material->alpha_mode = cgltf_alpha_mode_opaque; | ||
| 4667 | } | ||
| 4668 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "MASK") == 0) | ||
| 4669 | { | ||
| 4670 | out_material->alpha_mode = cgltf_alpha_mode_mask; | ||
| 4671 | } | ||
| 4672 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "BLEND") == 0) | ||
| 4673 | { | ||
| 4674 | out_material->alpha_mode = cgltf_alpha_mode_blend; | ||
| 4675 | } | ||
| 4676 | ++i; | ||
| 4677 | } | ||
| 4678 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaCutoff") == 0) | ||
| 4679 | { | ||
| 4680 | ++i; | ||
| 4681 | out_material->alpha_cutoff = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 4682 | ++i; | ||
| 4683 | } | ||
| 4684 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "doubleSided") == 0) | ||
| 4685 | { | ||
| 4686 | ++i; | ||
| 4687 | out_material->double_sided = | ||
| 4688 | cgltf_json_to_bool(tokens + i, json_chunk); | ||
| 4689 | ++i; | ||
| 4690 | } | ||
| 4691 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 4692 | { | ||
| 4693 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_material->extras); | ||
| 4694 | } | ||
| 4695 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 4696 | { | ||
| 4697 | ++i; | ||
| 4698 | |||
| 4699 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4700 | if(out_material->extensions) | ||
| 4701 | { | ||
| 4702 | return CGLTF_ERROR_JSON; | ||
| 4703 | } | ||
| 4704 | |||
| 4705 | int extensions_size = tokens[i].size; | ||
| 4706 | ++i; | ||
| 4707 | out_material->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); | ||
| 4708 | out_material->extensions_count= 0; | ||
| 4709 | |||
| 4710 | if (!out_material->extensions) | ||
| 4711 | { | ||
| 4712 | return CGLTF_ERROR_NOMEM; | ||
| 4713 | } | ||
| 4714 | |||
| 4715 | for (int k = 0; k < extensions_size; ++k) | ||
| 4716 | { | ||
| 4717 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4718 | |||
| 4719 | if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_pbrSpecularGlossiness") == 0) | ||
| 4720 | { | ||
| 4721 | out_material->has_pbr_specular_glossiness = 1; | ||
| 4722 | i = cgltf_parse_json_pbr_specular_glossiness(options, tokens, i + 1, json_chunk, &out_material->pbr_specular_glossiness); | ||
| 4723 | } | ||
| 4724 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_unlit") == 0) | ||
| 4725 | { | ||
| 4726 | out_material->unlit = 1; | ||
| 4727 | i = cgltf_skip_json(tokens, i+1); | ||
| 4728 | } | ||
| 4729 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_clearcoat") == 0) | ||
| 4730 | { | ||
| 4731 | out_material->has_clearcoat = 1; | ||
| 4732 | i = cgltf_parse_json_clearcoat(options, tokens, i + 1, json_chunk, &out_material->clearcoat); | ||
| 4733 | } | ||
| 4734 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_ior") == 0) | ||
| 4735 | { | ||
| 4736 | out_material->has_ior = 1; | ||
| 4737 | i = cgltf_parse_json_ior(tokens, i + 1, json_chunk, &out_material->ior); | ||
| 4738 | } | ||
| 4739 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_specular") == 0) | ||
| 4740 | { | ||
| 4741 | out_material->has_specular = 1; | ||
| 4742 | i = cgltf_parse_json_specular(options, tokens, i + 1, json_chunk, &out_material->specular); | ||
| 4743 | } | ||
| 4744 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_transmission") == 0) | ||
| 4745 | { | ||
| 4746 | out_material->has_transmission = 1; | ||
| 4747 | i = cgltf_parse_json_transmission(options, tokens, i + 1, json_chunk, &out_material->transmission); | ||
| 4748 | } | ||
| 4749 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_volume") == 0) | ||
| 4750 | { | ||
| 4751 | out_material->has_volume = 1; | ||
| 4752 | i = cgltf_parse_json_volume(options, tokens, i + 1, json_chunk, &out_material->volume); | ||
| 4753 | } | ||
| 4754 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_sheen") == 0) | ||
| 4755 | { | ||
| 4756 | out_material->has_sheen = 1; | ||
| 4757 | i = cgltf_parse_json_sheen(options, tokens, i + 1, json_chunk, &out_material->sheen); | ||
| 4758 | } | ||
| 4759 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_emissive_strength") == 0) | ||
| 4760 | { | ||
| 4761 | out_material->has_emissive_strength = 1; | ||
| 4762 | i = cgltf_parse_json_emissive_strength(tokens, i + 1, json_chunk, &out_material->emissive_strength); | ||
| 4763 | } | ||
| 4764 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_iridescence") == 0) | ||
| 4765 | { | ||
| 4766 | out_material->has_iridescence = 1; | ||
| 4767 | i = cgltf_parse_json_iridescence(options, tokens, i + 1, json_chunk, &out_material->iridescence); | ||
| 4768 | } | ||
| 4769 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_anisotropy") == 0) | ||
| 4770 | { | ||
| 4771 | out_material->has_anisotropy = 1; | ||
| 4772 | i = cgltf_parse_json_anisotropy(options, tokens, i + 1, json_chunk, &out_material->anisotropy); | ||
| 4773 | } | ||
| 4774 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_dispersion") == 0) | ||
| 4775 | { | ||
| 4776 | out_material->has_dispersion = 1; | ||
| 4777 | i = cgltf_parse_json_dispersion(tokens, i + 1, json_chunk, &out_material->dispersion); | ||
| 4778 | } | ||
| 4779 | else | ||
| 4780 | { | ||
| 4781 | i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_material->extensions[out_material->extensions_count++])); | ||
| 4782 | } | ||
| 4783 | |||
| 4784 | if (i < 0) | ||
| 4785 | { | ||
| 4786 | return i; | ||
| 4787 | } | ||
| 4788 | } | ||
| 4789 | } | ||
| 4790 | else | ||
| 4791 | { | ||
| 4792 | i = cgltf_skip_json(tokens, i+1); | ||
| 4793 | } | ||
| 4794 | |||
| 4795 | if (i < 0) | ||
| 4796 | { | ||
| 4797 | return i; | ||
| 4798 | } | ||
| 4799 | } | ||
| 4800 | |||
| 4801 | return i; | ||
| 4802 | } | ||
| 4803 | |||
| 4804 | static int cgltf_parse_json_accessors(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 4805 | { | ||
| 4806 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_accessor), (void**)&out_data->accessors, &out_data->accessors_count); | ||
| 4807 | if (i < 0) | ||
| 4808 | { | ||
| 4809 | return i; | ||
| 4810 | } | ||
| 4811 | |||
| 4812 | for (cgltf_size j = 0; j < out_data->accessors_count; ++j) | ||
| 4813 | { | ||
| 4814 | i = cgltf_parse_json_accessor(options, tokens, i, json_chunk, &out_data->accessors[j]); | ||
| 4815 | if (i < 0) | ||
| 4816 | { | ||
| 4817 | return i; | ||
| 4818 | } | ||
| 4819 | } | ||
| 4820 | return i; | ||
| 4821 | } | ||
| 4822 | |||
| 4823 | static int cgltf_parse_json_materials(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 4824 | { | ||
| 4825 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material), (void**)&out_data->materials, &out_data->materials_count); | ||
| 4826 | if (i < 0) | ||
| 4827 | { | ||
| 4828 | return i; | ||
| 4829 | } | ||
| 4830 | |||
| 4831 | for (cgltf_size j = 0; j < out_data->materials_count; ++j) | ||
| 4832 | { | ||
| 4833 | i = cgltf_parse_json_material(options, tokens, i, json_chunk, &out_data->materials[j]); | ||
| 4834 | if (i < 0) | ||
| 4835 | { | ||
| 4836 | return i; | ||
| 4837 | } | ||
| 4838 | } | ||
| 4839 | return i; | ||
| 4840 | } | ||
| 4841 | |||
| 4842 | static int cgltf_parse_json_images(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 4843 | { | ||
| 4844 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_image), (void**)&out_data->images, &out_data->images_count); | ||
| 4845 | if (i < 0) | ||
| 4846 | { | ||
| 4847 | return i; | ||
| 4848 | } | ||
| 4849 | |||
| 4850 | for (cgltf_size j = 0; j < out_data->images_count; ++j) | ||
| 4851 | { | ||
| 4852 | i = cgltf_parse_json_image(options, tokens, i, json_chunk, &out_data->images[j]); | ||
| 4853 | if (i < 0) | ||
| 4854 | { | ||
| 4855 | return i; | ||
| 4856 | } | ||
| 4857 | } | ||
| 4858 | return i; | ||
| 4859 | } | ||
| 4860 | |||
| 4861 | static int cgltf_parse_json_textures(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 4862 | { | ||
| 4863 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_texture), (void**)&out_data->textures, &out_data->textures_count); | ||
| 4864 | if (i < 0) | ||
| 4865 | { | ||
| 4866 | return i; | ||
| 4867 | } | ||
| 4868 | |||
| 4869 | for (cgltf_size j = 0; j < out_data->textures_count; ++j) | ||
| 4870 | { | ||
| 4871 | i = cgltf_parse_json_texture(options, tokens, i, json_chunk, &out_data->textures[j]); | ||
| 4872 | if (i < 0) | ||
| 4873 | { | ||
| 4874 | return i; | ||
| 4875 | } | ||
| 4876 | } | ||
| 4877 | return i; | ||
| 4878 | } | ||
| 4879 | |||
| 4880 | static int cgltf_parse_json_samplers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 4881 | { | ||
| 4882 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_sampler), (void**)&out_data->samplers, &out_data->samplers_count); | ||
| 4883 | if (i < 0) | ||
| 4884 | { | ||
| 4885 | return i; | ||
| 4886 | } | ||
| 4887 | |||
| 4888 | for (cgltf_size j = 0; j < out_data->samplers_count; ++j) | ||
| 4889 | { | ||
| 4890 | i = cgltf_parse_json_sampler(options, tokens, i, json_chunk, &out_data->samplers[j]); | ||
| 4891 | if (i < 0) | ||
| 4892 | { | ||
| 4893 | return i; | ||
| 4894 | } | ||
| 4895 | } | ||
| 4896 | return i; | ||
| 4897 | } | ||
| 4898 | |||
| 4899 | static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_meshopt_compression* out_meshopt_compression) | ||
| 4900 | { | ||
| 4901 | (void)options; | ||
| 4902 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4903 | |||
| 4904 | int size = tokens[i].size; | ||
| 4905 | ++i; | ||
| 4906 | |||
| 4907 | for (int j = 0; j < size; ++j) | ||
| 4908 | { | ||
| 4909 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 4910 | |||
| 4911 | if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) | ||
| 4912 | { | ||
| 4913 | ++i; | ||
| 4914 | out_meshopt_compression->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 4915 | ++i; | ||
| 4916 | } | ||
| 4917 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) | ||
| 4918 | { | ||
| 4919 | ++i; | ||
| 4920 | out_meshopt_compression->offset = cgltf_json_to_size(tokens+i, json_chunk); | ||
| 4921 | ++i; | ||
| 4922 | } | ||
| 4923 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) | ||
| 4924 | { | ||
| 4925 | ++i; | ||
| 4926 | out_meshopt_compression->size = cgltf_json_to_size(tokens+i, json_chunk); | ||
| 4927 | ++i; | ||
| 4928 | } | ||
| 4929 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) | ||
| 4930 | { | ||
| 4931 | ++i; | ||
| 4932 | out_meshopt_compression->stride = cgltf_json_to_size(tokens+i, json_chunk); | ||
| 4933 | ++i; | ||
| 4934 | } | ||
| 4935 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) | ||
| 4936 | { | ||
| 4937 | ++i; | ||
| 4938 | out_meshopt_compression->count = cgltf_json_to_size(tokens+i, json_chunk); | ||
| 4939 | ++i; | ||
| 4940 | } | ||
| 4941 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) | ||
| 4942 | { | ||
| 4943 | ++i; | ||
| 4944 | if (cgltf_json_strcmp(tokens+i, json_chunk, "ATTRIBUTES") == 0) | ||
| 4945 | { | ||
| 4946 | out_meshopt_compression->mode = cgltf_meshopt_compression_mode_attributes; | ||
| 4947 | } | ||
| 4948 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "TRIANGLES") == 0) | ||
| 4949 | { | ||
| 4950 | out_meshopt_compression->mode = cgltf_meshopt_compression_mode_triangles; | ||
| 4951 | } | ||
| 4952 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "INDICES") == 0) | ||
| 4953 | { | ||
| 4954 | out_meshopt_compression->mode = cgltf_meshopt_compression_mode_indices; | ||
| 4955 | } | ||
| 4956 | ++i; | ||
| 4957 | } | ||
| 4958 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "filter") == 0) | ||
| 4959 | { | ||
| 4960 | ++i; | ||
| 4961 | if (cgltf_json_strcmp(tokens+i, json_chunk, "NONE") == 0) | ||
| 4962 | { | ||
| 4963 | out_meshopt_compression->filter = cgltf_meshopt_compression_filter_none; | ||
| 4964 | } | ||
| 4965 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "OCTAHEDRAL") == 0) | ||
| 4966 | { | ||
| 4967 | out_meshopt_compression->filter = cgltf_meshopt_compression_filter_octahedral; | ||
| 4968 | } | ||
| 4969 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "QUATERNION") == 0) | ||
| 4970 | { | ||
| 4971 | out_meshopt_compression->filter = cgltf_meshopt_compression_filter_quaternion; | ||
| 4972 | } | ||
| 4973 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "EXPONENTIAL") == 0) | ||
| 4974 | { | ||
| 4975 | out_meshopt_compression->filter = cgltf_meshopt_compression_filter_exponential; | ||
| 4976 | } | ||
| 4977 | ++i; | ||
| 4978 | } | ||
| 4979 | else | ||
| 4980 | { | ||
| 4981 | i = cgltf_skip_json(tokens, i+1); | ||
| 4982 | } | ||
| 4983 | |||
| 4984 | if (i < 0) | ||
| 4985 | { | ||
| 4986 | return i; | ||
| 4987 | } | ||
| 4988 | } | ||
| 4989 | |||
| 4990 | return i; | ||
| 4991 | } | ||
| 4992 | |||
| 4993 | static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer_view* out_buffer_view) | ||
| 4994 | { | ||
| 4995 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 4996 | |||
| 4997 | int size = tokens[i].size; | ||
| 4998 | ++i; | ||
| 4999 | |||
| 5000 | for (int j = 0; j < size; ++j) | ||
| 5001 | { | ||
| 5002 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5003 | |||
| 5004 | if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) | ||
| 5005 | { | ||
| 5006 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer_view->name); | ||
| 5007 | } | ||
| 5008 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) | ||
| 5009 | { | ||
| 5010 | ++i; | ||
| 5011 | out_buffer_view->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5012 | ++i; | ||
| 5013 | } | ||
| 5014 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) | ||
| 5015 | { | ||
| 5016 | ++i; | ||
| 5017 | out_buffer_view->offset = | ||
| 5018 | cgltf_json_to_size(tokens+i, json_chunk); | ||
| 5019 | ++i; | ||
| 5020 | } | ||
| 5021 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) | ||
| 5022 | { | ||
| 5023 | ++i; | ||
| 5024 | out_buffer_view->size = | ||
| 5025 | cgltf_json_to_size(tokens+i, json_chunk); | ||
| 5026 | ++i; | ||
| 5027 | } | ||
| 5028 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) | ||
| 5029 | { | ||
| 5030 | ++i; | ||
| 5031 | out_buffer_view->stride = | ||
| 5032 | cgltf_json_to_size(tokens+i, json_chunk); | ||
| 5033 | ++i; | ||
| 5034 | } | ||
| 5035 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) | ||
| 5036 | { | ||
| 5037 | ++i; | ||
| 5038 | int type = cgltf_json_to_int(tokens+i, json_chunk); | ||
| 5039 | switch (type) | ||
| 5040 | { | ||
| 5041 | case 34962: | ||
| 5042 | type = cgltf_buffer_view_type_vertices; | ||
| 5043 | break; | ||
| 5044 | case 34963: | ||
| 5045 | type = cgltf_buffer_view_type_indices; | ||
| 5046 | break; | ||
| 5047 | default: | ||
| 5048 | type = cgltf_buffer_view_type_invalid; | ||
| 5049 | break; | ||
| 5050 | } | ||
| 5051 | out_buffer_view->type = (cgltf_buffer_view_type)type; | ||
| 5052 | ++i; | ||
| 5053 | } | ||
| 5054 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 5055 | { | ||
| 5056 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer_view->extras); | ||
| 5057 | } | ||
| 5058 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 5059 | { | ||
| 5060 | ++i; | ||
| 5061 | |||
| 5062 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5063 | if(out_buffer_view->extensions) | ||
| 5064 | { | ||
| 5065 | return CGLTF_ERROR_JSON; | ||
| 5066 | } | ||
| 5067 | |||
| 5068 | int extensions_size = tokens[i].size; | ||
| 5069 | out_buffer_view->extensions_count = 0; | ||
| 5070 | out_buffer_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); | ||
| 5071 | |||
| 5072 | if (!out_buffer_view->extensions) | ||
| 5073 | { | ||
| 5074 | return CGLTF_ERROR_NOMEM; | ||
| 5075 | } | ||
| 5076 | |||
| 5077 | ++i; | ||
| 5078 | for (int k = 0; k < extensions_size; ++k) | ||
| 5079 | { | ||
| 5080 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5081 | |||
| 5082 | if (cgltf_json_strcmp(tokens+i, json_chunk, "EXT_meshopt_compression") == 0) | ||
| 5083 | { | ||
| 5084 | out_buffer_view->has_meshopt_compression = 1; | ||
| 5085 | i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression); | ||
| 5086 | } | ||
| 5087 | else | ||
| 5088 | { | ||
| 5089 | i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_buffer_view->extensions[out_buffer_view->extensions_count++])); | ||
| 5090 | } | ||
| 5091 | |||
| 5092 | if (i < 0) | ||
| 5093 | { | ||
| 5094 | return i; | ||
| 5095 | } | ||
| 5096 | } | ||
| 5097 | } | ||
| 5098 | else | ||
| 5099 | { | ||
| 5100 | i = cgltf_skip_json(tokens, i+1); | ||
| 5101 | } | ||
| 5102 | |||
| 5103 | if (i < 0) | ||
| 5104 | { | ||
| 5105 | return i; | ||
| 5106 | } | ||
| 5107 | } | ||
| 5108 | |||
| 5109 | return i; | ||
| 5110 | } | ||
| 5111 | |||
| 5112 | static int cgltf_parse_json_buffer_views(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 5113 | { | ||
| 5114 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer_view), (void**)&out_data->buffer_views, &out_data->buffer_views_count); | ||
| 5115 | if (i < 0) | ||
| 5116 | { | ||
| 5117 | return i; | ||
| 5118 | } | ||
| 5119 | |||
| 5120 | for (cgltf_size j = 0; j < out_data->buffer_views_count; ++j) | ||
| 5121 | { | ||
| 5122 | i = cgltf_parse_json_buffer_view(options, tokens, i, json_chunk, &out_data->buffer_views[j]); | ||
| 5123 | if (i < 0) | ||
| 5124 | { | ||
| 5125 | return i; | ||
| 5126 | } | ||
| 5127 | } | ||
| 5128 | return i; | ||
| 5129 | } | ||
| 5130 | |||
| 5131 | static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer* out_buffer) | ||
| 5132 | { | ||
| 5133 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5134 | |||
| 5135 | int size = tokens[i].size; | ||
| 5136 | ++i; | ||
| 5137 | |||
| 5138 | for (int j = 0; j < size; ++j) | ||
| 5139 | { | ||
| 5140 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5141 | |||
| 5142 | if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) | ||
| 5143 | { | ||
| 5144 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->name); | ||
| 5145 | } | ||
| 5146 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) | ||
| 5147 | { | ||
| 5148 | ++i; | ||
| 5149 | out_buffer->size = | ||
| 5150 | cgltf_json_to_size(tokens+i, json_chunk); | ||
| 5151 | ++i; | ||
| 5152 | } | ||
| 5153 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0) | ||
| 5154 | { | ||
| 5155 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->uri); | ||
| 5156 | } | ||
| 5157 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 5158 | { | ||
| 5159 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer->extras); | ||
| 5160 | } | ||
| 5161 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 5162 | { | ||
| 5163 | i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_buffer->extensions_count, &out_buffer->extensions); | ||
| 5164 | } | ||
| 5165 | else | ||
| 5166 | { | ||
| 5167 | i = cgltf_skip_json(tokens, i+1); | ||
| 5168 | } | ||
| 5169 | |||
| 5170 | if (i < 0) | ||
| 5171 | { | ||
| 5172 | return i; | ||
| 5173 | } | ||
| 5174 | } | ||
| 5175 | |||
| 5176 | return i; | ||
| 5177 | } | ||
| 5178 | |||
| 5179 | static int cgltf_parse_json_buffers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 5180 | { | ||
| 5181 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer), (void**)&out_data->buffers, &out_data->buffers_count); | ||
| 5182 | if (i < 0) | ||
| 5183 | { | ||
| 5184 | return i; | ||
| 5185 | } | ||
| 5186 | |||
| 5187 | for (cgltf_size j = 0; j < out_data->buffers_count; ++j) | ||
| 5188 | { | ||
| 5189 | i = cgltf_parse_json_buffer(options, tokens, i, json_chunk, &out_data->buffers[j]); | ||
| 5190 | if (i < 0) | ||
| 5191 | { | ||
| 5192 | return i; | ||
| 5193 | } | ||
| 5194 | } | ||
| 5195 | return i; | ||
| 5196 | } | ||
| 5197 | |||
| 5198 | static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_skin* out_skin) | ||
| 5199 | { | ||
| 5200 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5201 | |||
| 5202 | int size = tokens[i].size; | ||
| 5203 | ++i; | ||
| 5204 | |||
| 5205 | for (int j = 0; j < size; ++j) | ||
| 5206 | { | ||
| 5207 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5208 | |||
| 5209 | if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) | ||
| 5210 | { | ||
| 5211 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_skin->name); | ||
| 5212 | } | ||
| 5213 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "joints") == 0) | ||
| 5214 | { | ||
| 5215 | i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_skin->joints, &out_skin->joints_count); | ||
| 5216 | if (i < 0) | ||
| 5217 | { | ||
| 5218 | return i; | ||
| 5219 | } | ||
| 5220 | |||
| 5221 | for (cgltf_size k = 0; k < out_skin->joints_count; ++k) | ||
| 5222 | { | ||
| 5223 | out_skin->joints[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5224 | ++i; | ||
| 5225 | } | ||
| 5226 | } | ||
| 5227 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "skeleton") == 0) | ||
| 5228 | { | ||
| 5229 | ++i; | ||
| 5230 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); | ||
| 5231 | out_skin->skeleton = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5232 | ++i; | ||
| 5233 | } | ||
| 5234 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "inverseBindMatrices") == 0) | ||
| 5235 | { | ||
| 5236 | ++i; | ||
| 5237 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); | ||
| 5238 | out_skin->inverse_bind_matrices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5239 | ++i; | ||
| 5240 | } | ||
| 5241 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 5242 | { | ||
| 5243 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_skin->extras); | ||
| 5244 | } | ||
| 5245 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 5246 | { | ||
| 5247 | i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_skin->extensions_count, &out_skin->extensions); | ||
| 5248 | } | ||
| 5249 | else | ||
| 5250 | { | ||
| 5251 | i = cgltf_skip_json(tokens, i+1); | ||
| 5252 | } | ||
| 5253 | |||
| 5254 | if (i < 0) | ||
| 5255 | { | ||
| 5256 | return i; | ||
| 5257 | } | ||
| 5258 | } | ||
| 5259 | |||
| 5260 | return i; | ||
| 5261 | } | ||
| 5262 | |||
| 5263 | static int cgltf_parse_json_skins(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 5264 | { | ||
| 5265 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_skin), (void**)&out_data->skins, &out_data->skins_count); | ||
| 5266 | if (i < 0) | ||
| 5267 | { | ||
| 5268 | return i; | ||
| 5269 | } | ||
| 5270 | |||
| 5271 | for (cgltf_size j = 0; j < out_data->skins_count; ++j) | ||
| 5272 | { | ||
| 5273 | i = cgltf_parse_json_skin(options, tokens, i, json_chunk, &out_data->skins[j]); | ||
| 5274 | if (i < 0) | ||
| 5275 | { | ||
| 5276 | return i; | ||
| 5277 | } | ||
| 5278 | } | ||
| 5279 | return i; | ||
| 5280 | } | ||
| 5281 | |||
| 5282 | static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_camera* out_camera) | ||
| 5283 | { | ||
| 5284 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5285 | |||
| 5286 | int size = tokens[i].size; | ||
| 5287 | ++i; | ||
| 5288 | |||
| 5289 | for (int j = 0; j < size; ++j) | ||
| 5290 | { | ||
| 5291 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5292 | |||
| 5293 | if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) | ||
| 5294 | { | ||
| 5295 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name); | ||
| 5296 | } | ||
| 5297 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0) | ||
| 5298 | { | ||
| 5299 | ++i; | ||
| 5300 | |||
| 5301 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5302 | |||
| 5303 | int data_size = tokens[i].size; | ||
| 5304 | ++i; | ||
| 5305 | |||
| 5306 | if (out_camera->type != cgltf_camera_type_invalid) | ||
| 5307 | { | ||
| 5308 | return CGLTF_ERROR_JSON; | ||
| 5309 | } | ||
| 5310 | |||
| 5311 | out_camera->type = cgltf_camera_type_perspective; | ||
| 5312 | |||
| 5313 | for (int k = 0; k < data_size; ++k) | ||
| 5314 | { | ||
| 5315 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5316 | |||
| 5317 | if (cgltf_json_strcmp(tokens+i, json_chunk, "aspectRatio") == 0) | ||
| 5318 | { | ||
| 5319 | ++i; | ||
| 5320 | out_camera->data.perspective.has_aspect_ratio = 1; | ||
| 5321 | out_camera->data.perspective.aspect_ratio = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 5322 | ++i; | ||
| 5323 | } | ||
| 5324 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "yfov") == 0) | ||
| 5325 | { | ||
| 5326 | ++i; | ||
| 5327 | out_camera->data.perspective.yfov = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 5328 | ++i; | ||
| 5329 | } | ||
| 5330 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0) | ||
| 5331 | { | ||
| 5332 | ++i; | ||
| 5333 | out_camera->data.perspective.has_zfar = 1; | ||
| 5334 | out_camera->data.perspective.zfar = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 5335 | ++i; | ||
| 5336 | } | ||
| 5337 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0) | ||
| 5338 | { | ||
| 5339 | ++i; | ||
| 5340 | out_camera->data.perspective.znear = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 5341 | ++i; | ||
| 5342 | } | ||
| 5343 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 5344 | { | ||
| 5345 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.perspective.extras); | ||
| 5346 | } | ||
| 5347 | else | ||
| 5348 | { | ||
| 5349 | i = cgltf_skip_json(tokens, i+1); | ||
| 5350 | } | ||
| 5351 | |||
| 5352 | if (i < 0) | ||
| 5353 | { | ||
| 5354 | return i; | ||
| 5355 | } | ||
| 5356 | } | ||
| 5357 | } | ||
| 5358 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "orthographic") == 0) | ||
| 5359 | { | ||
| 5360 | ++i; | ||
| 5361 | |||
| 5362 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5363 | |||
| 5364 | int data_size = tokens[i].size; | ||
| 5365 | ++i; | ||
| 5366 | |||
| 5367 | if (out_camera->type != cgltf_camera_type_invalid) | ||
| 5368 | { | ||
| 5369 | return CGLTF_ERROR_JSON; | ||
| 5370 | } | ||
| 5371 | |||
| 5372 | out_camera->type = cgltf_camera_type_orthographic; | ||
| 5373 | |||
| 5374 | for (int k = 0; k < data_size; ++k) | ||
| 5375 | { | ||
| 5376 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5377 | |||
| 5378 | if (cgltf_json_strcmp(tokens+i, json_chunk, "xmag") == 0) | ||
| 5379 | { | ||
| 5380 | ++i; | ||
| 5381 | out_camera->data.orthographic.xmag = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 5382 | ++i; | ||
| 5383 | } | ||
| 5384 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "ymag") == 0) | ||
| 5385 | { | ||
| 5386 | ++i; | ||
| 5387 | out_camera->data.orthographic.ymag = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 5388 | ++i; | ||
| 5389 | } | ||
| 5390 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0) | ||
| 5391 | { | ||
| 5392 | ++i; | ||
| 5393 | out_camera->data.orthographic.zfar = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 5394 | ++i; | ||
| 5395 | } | ||
| 5396 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0) | ||
| 5397 | { | ||
| 5398 | ++i; | ||
| 5399 | out_camera->data.orthographic.znear = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 5400 | ++i; | ||
| 5401 | } | ||
| 5402 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 5403 | { | ||
| 5404 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras); | ||
| 5405 | } | ||
| 5406 | else | ||
| 5407 | { | ||
| 5408 | i = cgltf_skip_json(tokens, i+1); | ||
| 5409 | } | ||
| 5410 | |||
| 5411 | if (i < 0) | ||
| 5412 | { | ||
| 5413 | return i; | ||
| 5414 | } | ||
| 5415 | } | ||
| 5416 | } | ||
| 5417 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 5418 | { | ||
| 5419 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->extras); | ||
| 5420 | } | ||
| 5421 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 5422 | { | ||
| 5423 | i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_camera->extensions_count, &out_camera->extensions); | ||
| 5424 | } | ||
| 5425 | else | ||
| 5426 | { | ||
| 5427 | i = cgltf_skip_json(tokens, i+1); | ||
| 5428 | } | ||
| 5429 | |||
| 5430 | if (i < 0) | ||
| 5431 | { | ||
| 5432 | return i; | ||
| 5433 | } | ||
| 5434 | } | ||
| 5435 | |||
| 5436 | return i; | ||
| 5437 | } | ||
| 5438 | |||
| 5439 | static int cgltf_parse_json_cameras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 5440 | { | ||
| 5441 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_camera), (void**)&out_data->cameras, &out_data->cameras_count); | ||
| 5442 | if (i < 0) | ||
| 5443 | { | ||
| 5444 | return i; | ||
| 5445 | } | ||
| 5446 | |||
| 5447 | for (cgltf_size j = 0; j < out_data->cameras_count; ++j) | ||
| 5448 | { | ||
| 5449 | i = cgltf_parse_json_camera(options, tokens, i, json_chunk, &out_data->cameras[j]); | ||
| 5450 | if (i < 0) | ||
| 5451 | { | ||
| 5452 | return i; | ||
| 5453 | } | ||
| 5454 | } | ||
| 5455 | return i; | ||
| 5456 | } | ||
| 5457 | |||
| 5458 | static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_light* out_light) | ||
| 5459 | { | ||
| 5460 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5461 | |||
| 5462 | out_light->color[0] = 1.f; | ||
| 5463 | out_light->color[1] = 1.f; | ||
| 5464 | out_light->color[2] = 1.f; | ||
| 5465 | out_light->intensity = 1.f; | ||
| 5466 | |||
| 5467 | out_light->spot_inner_cone_angle = 0.f; | ||
| 5468 | out_light->spot_outer_cone_angle = 3.1415926535f / 4.0f; | ||
| 5469 | |||
| 5470 | int size = tokens[i].size; | ||
| 5471 | ++i; | ||
| 5472 | |||
| 5473 | for (int j = 0; j < size; ++j) | ||
| 5474 | { | ||
| 5475 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5476 | |||
| 5477 | if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) | ||
| 5478 | { | ||
| 5479 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_light->name); | ||
| 5480 | } | ||
| 5481 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "color") == 0) | ||
| 5482 | { | ||
| 5483 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_light->color, 3); | ||
| 5484 | } | ||
| 5485 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "intensity") == 0) | ||
| 5486 | { | ||
| 5487 | ++i; | ||
| 5488 | out_light->intensity = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 5489 | ++i; | ||
| 5490 | } | ||
| 5491 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) | ||
| 5492 | { | ||
| 5493 | ++i; | ||
| 5494 | if (cgltf_json_strcmp(tokens + i, json_chunk, "directional") == 0) | ||
| 5495 | { | ||
| 5496 | out_light->type = cgltf_light_type_directional; | ||
| 5497 | } | ||
| 5498 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "point") == 0) | ||
| 5499 | { | ||
| 5500 | out_light->type = cgltf_light_type_point; | ||
| 5501 | } | ||
| 5502 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "spot") == 0) | ||
| 5503 | { | ||
| 5504 | out_light->type = cgltf_light_type_spot; | ||
| 5505 | } | ||
| 5506 | ++i; | ||
| 5507 | } | ||
| 5508 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "range") == 0) | ||
| 5509 | { | ||
| 5510 | ++i; | ||
| 5511 | out_light->range = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 5512 | ++i; | ||
| 5513 | } | ||
| 5514 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "spot") == 0) | ||
| 5515 | { | ||
| 5516 | ++i; | ||
| 5517 | |||
| 5518 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5519 | |||
| 5520 | int data_size = tokens[i].size; | ||
| 5521 | ++i; | ||
| 5522 | |||
| 5523 | for (int k = 0; k < data_size; ++k) | ||
| 5524 | { | ||
| 5525 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5526 | |||
| 5527 | if (cgltf_json_strcmp(tokens+i, json_chunk, "innerConeAngle") == 0) | ||
| 5528 | { | ||
| 5529 | ++i; | ||
| 5530 | out_light->spot_inner_cone_angle = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 5531 | ++i; | ||
| 5532 | } | ||
| 5533 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "outerConeAngle") == 0) | ||
| 5534 | { | ||
| 5535 | ++i; | ||
| 5536 | out_light->spot_outer_cone_angle = cgltf_json_to_float(tokens + i, json_chunk); | ||
| 5537 | ++i; | ||
| 5538 | } | ||
| 5539 | else | ||
| 5540 | { | ||
| 5541 | i = cgltf_skip_json(tokens, i+1); | ||
| 5542 | } | ||
| 5543 | |||
| 5544 | if (i < 0) | ||
| 5545 | { | ||
| 5546 | return i; | ||
| 5547 | } | ||
| 5548 | } | ||
| 5549 | } | ||
| 5550 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 5551 | { | ||
| 5552 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_light->extras); | ||
| 5553 | } | ||
| 5554 | else | ||
| 5555 | { | ||
| 5556 | i = cgltf_skip_json(tokens, i+1); | ||
| 5557 | } | ||
| 5558 | |||
| 5559 | if (i < 0) | ||
| 5560 | { | ||
| 5561 | return i; | ||
| 5562 | } | ||
| 5563 | } | ||
| 5564 | |||
| 5565 | return i; | ||
| 5566 | } | ||
| 5567 | |||
| 5568 | static int cgltf_parse_json_lights(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 5569 | { | ||
| 5570 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_light), (void**)&out_data->lights, &out_data->lights_count); | ||
| 5571 | if (i < 0) | ||
| 5572 | { | ||
| 5573 | return i; | ||
| 5574 | } | ||
| 5575 | |||
| 5576 | for (cgltf_size j = 0; j < out_data->lights_count; ++j) | ||
| 5577 | { | ||
| 5578 | i = cgltf_parse_json_light(options, tokens, i, json_chunk, &out_data->lights[j]); | ||
| 5579 | if (i < 0) | ||
| 5580 | { | ||
| 5581 | return i; | ||
| 5582 | } | ||
| 5583 | } | ||
| 5584 | return i; | ||
| 5585 | } | ||
| 5586 | |||
| 5587 | static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_node* out_node) | ||
| 5588 | { | ||
| 5589 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5590 | |||
| 5591 | out_node->rotation[3] = 1.0f; | ||
| 5592 | out_node->scale[0] = 1.0f; | ||
| 5593 | out_node->scale[1] = 1.0f; | ||
| 5594 | out_node->scale[2] = 1.0f; | ||
| 5595 | out_node->matrix[0] = 1.0f; | ||
| 5596 | out_node->matrix[5] = 1.0f; | ||
| 5597 | out_node->matrix[10] = 1.0f; | ||
| 5598 | out_node->matrix[15] = 1.0f; | ||
| 5599 | |||
| 5600 | int size = tokens[i].size; | ||
| 5601 | ++i; | ||
| 5602 | |||
| 5603 | for (int j = 0; j < size; ++j) | ||
| 5604 | { | ||
| 5605 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5606 | |||
| 5607 | if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) | ||
| 5608 | { | ||
| 5609 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_node->name); | ||
| 5610 | } | ||
| 5611 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "children") == 0) | ||
| 5612 | { | ||
| 5613 | i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_node->children, &out_node->children_count); | ||
| 5614 | if (i < 0) | ||
| 5615 | { | ||
| 5616 | return i; | ||
| 5617 | } | ||
| 5618 | |||
| 5619 | for (cgltf_size k = 0; k < out_node->children_count; ++k) | ||
| 5620 | { | ||
| 5621 | out_node->children[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5622 | ++i; | ||
| 5623 | } | ||
| 5624 | } | ||
| 5625 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "mesh") == 0) | ||
| 5626 | { | ||
| 5627 | ++i; | ||
| 5628 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); | ||
| 5629 | out_node->mesh = CGLTF_PTRINDEX(cgltf_mesh, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5630 | ++i; | ||
| 5631 | } | ||
| 5632 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "skin") == 0) | ||
| 5633 | { | ||
| 5634 | ++i; | ||
| 5635 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); | ||
| 5636 | out_node->skin = CGLTF_PTRINDEX(cgltf_skin, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5637 | ++i; | ||
| 5638 | } | ||
| 5639 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "camera") == 0) | ||
| 5640 | { | ||
| 5641 | ++i; | ||
| 5642 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); | ||
| 5643 | out_node->camera = CGLTF_PTRINDEX(cgltf_camera, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5644 | ++i; | ||
| 5645 | } | ||
| 5646 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0) | ||
| 5647 | { | ||
| 5648 | out_node->has_translation = 1; | ||
| 5649 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->translation, 3); | ||
| 5650 | } | ||
| 5651 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0) | ||
| 5652 | { | ||
| 5653 | out_node->has_rotation = 1; | ||
| 5654 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->rotation, 4); | ||
| 5655 | } | ||
| 5656 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0) | ||
| 5657 | { | ||
| 5658 | out_node->has_scale = 1; | ||
| 5659 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->scale, 3); | ||
| 5660 | } | ||
| 5661 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "matrix") == 0) | ||
| 5662 | { | ||
| 5663 | out_node->has_matrix = 1; | ||
| 5664 | i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->matrix, 16); | ||
| 5665 | } | ||
| 5666 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0) | ||
| 5667 | { | ||
| 5668 | i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_node->weights, &out_node->weights_count); | ||
| 5669 | if (i < 0) | ||
| 5670 | { | ||
| 5671 | return i; | ||
| 5672 | } | ||
| 5673 | |||
| 5674 | i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_node->weights, (int)out_node->weights_count); | ||
| 5675 | } | ||
| 5676 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 5677 | { | ||
| 5678 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_node->extras); | ||
| 5679 | } | ||
| 5680 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 5681 | { | ||
| 5682 | ++i; | ||
| 5683 | |||
| 5684 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5685 | if(out_node->extensions) | ||
| 5686 | { | ||
| 5687 | return CGLTF_ERROR_JSON; | ||
| 5688 | } | ||
| 5689 | |||
| 5690 | int extensions_size = tokens[i].size; | ||
| 5691 | out_node->extensions_count= 0; | ||
| 5692 | out_node->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); | ||
| 5693 | |||
| 5694 | if (!out_node->extensions) | ||
| 5695 | { | ||
| 5696 | return CGLTF_ERROR_NOMEM; | ||
| 5697 | } | ||
| 5698 | |||
| 5699 | ++i; | ||
| 5700 | |||
| 5701 | for (int k = 0; k < extensions_size; ++k) | ||
| 5702 | { | ||
| 5703 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5704 | |||
| 5705 | if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0) | ||
| 5706 | { | ||
| 5707 | ++i; | ||
| 5708 | |||
| 5709 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5710 | |||
| 5711 | int data_size = tokens[i].size; | ||
| 5712 | ++i; | ||
| 5713 | |||
| 5714 | for (int m = 0; m < data_size; ++m) | ||
| 5715 | { | ||
| 5716 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5717 | |||
| 5718 | if (cgltf_json_strcmp(tokens + i, json_chunk, "light") == 0) | ||
| 5719 | { | ||
| 5720 | ++i; | ||
| 5721 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); | ||
| 5722 | out_node->light = CGLTF_PTRINDEX(cgltf_light, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5723 | ++i; | ||
| 5724 | } | ||
| 5725 | else | ||
| 5726 | { | ||
| 5727 | i = cgltf_skip_json(tokens, i + 1); | ||
| 5728 | } | ||
| 5729 | |||
| 5730 | if (i < 0) | ||
| 5731 | { | ||
| 5732 | return i; | ||
| 5733 | } | ||
| 5734 | } | ||
| 5735 | } | ||
| 5736 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "EXT_mesh_gpu_instancing") == 0) | ||
| 5737 | { | ||
| 5738 | out_node->has_mesh_gpu_instancing = 1; | ||
| 5739 | i = cgltf_parse_json_mesh_gpu_instancing(options, tokens, i + 1, json_chunk, &out_node->mesh_gpu_instancing); | ||
| 5740 | } | ||
| 5741 | else | ||
| 5742 | { | ||
| 5743 | i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_node->extensions[out_node->extensions_count++])); | ||
| 5744 | } | ||
| 5745 | |||
| 5746 | if (i < 0) | ||
| 5747 | { | ||
| 5748 | return i; | ||
| 5749 | } | ||
| 5750 | } | ||
| 5751 | } | ||
| 5752 | else | ||
| 5753 | { | ||
| 5754 | i = cgltf_skip_json(tokens, i+1); | ||
| 5755 | } | ||
| 5756 | |||
| 5757 | if (i < 0) | ||
| 5758 | { | ||
| 5759 | return i; | ||
| 5760 | } | ||
| 5761 | } | ||
| 5762 | |||
| 5763 | return i; | ||
| 5764 | } | ||
| 5765 | |||
| 5766 | static int cgltf_parse_json_nodes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 5767 | { | ||
| 5768 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_node), (void**)&out_data->nodes, &out_data->nodes_count); | ||
| 5769 | if (i < 0) | ||
| 5770 | { | ||
| 5771 | return i; | ||
| 5772 | } | ||
| 5773 | |||
| 5774 | for (cgltf_size j = 0; j < out_data->nodes_count; ++j) | ||
| 5775 | { | ||
| 5776 | i = cgltf_parse_json_node(options, tokens, i, json_chunk, &out_data->nodes[j]); | ||
| 5777 | if (i < 0) | ||
| 5778 | { | ||
| 5779 | return i; | ||
| 5780 | } | ||
| 5781 | } | ||
| 5782 | return i; | ||
| 5783 | } | ||
| 5784 | |||
| 5785 | static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_scene* out_scene) | ||
| 5786 | { | ||
| 5787 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5788 | |||
| 5789 | int size = tokens[i].size; | ||
| 5790 | ++i; | ||
| 5791 | |||
| 5792 | for (int j = 0; j < size; ++j) | ||
| 5793 | { | ||
| 5794 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5795 | |||
| 5796 | if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) | ||
| 5797 | { | ||
| 5798 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_scene->name); | ||
| 5799 | } | ||
| 5800 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "nodes") == 0) | ||
| 5801 | { | ||
| 5802 | i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_scene->nodes, &out_scene->nodes_count); | ||
| 5803 | if (i < 0) | ||
| 5804 | { | ||
| 5805 | return i; | ||
| 5806 | } | ||
| 5807 | |||
| 5808 | for (cgltf_size k = 0; k < out_scene->nodes_count; ++k) | ||
| 5809 | { | ||
| 5810 | out_scene->nodes[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5811 | ++i; | ||
| 5812 | } | ||
| 5813 | } | ||
| 5814 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 5815 | { | ||
| 5816 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_scene->extras); | ||
| 5817 | } | ||
| 5818 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 5819 | { | ||
| 5820 | i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_scene->extensions_count, &out_scene->extensions); | ||
| 5821 | } | ||
| 5822 | else | ||
| 5823 | { | ||
| 5824 | i = cgltf_skip_json(tokens, i+1); | ||
| 5825 | } | ||
| 5826 | |||
| 5827 | if (i < 0) | ||
| 5828 | { | ||
| 5829 | return i; | ||
| 5830 | } | ||
| 5831 | } | ||
| 5832 | |||
| 5833 | return i; | ||
| 5834 | } | ||
| 5835 | |||
| 5836 | static int cgltf_parse_json_scenes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 5837 | { | ||
| 5838 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_scene), (void**)&out_data->scenes, &out_data->scenes_count); | ||
| 5839 | if (i < 0) | ||
| 5840 | { | ||
| 5841 | return i; | ||
| 5842 | } | ||
| 5843 | |||
| 5844 | for (cgltf_size j = 0; j < out_data->scenes_count; ++j) | ||
| 5845 | { | ||
| 5846 | i = cgltf_parse_json_scene(options, tokens, i, json_chunk, &out_data->scenes[j]); | ||
| 5847 | if (i < 0) | ||
| 5848 | { | ||
| 5849 | return i; | ||
| 5850 | } | ||
| 5851 | } | ||
| 5852 | return i; | ||
| 5853 | } | ||
| 5854 | |||
| 5855 | static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_sampler* out_sampler) | ||
| 5856 | { | ||
| 5857 | (void)options; | ||
| 5858 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5859 | |||
| 5860 | int size = tokens[i].size; | ||
| 5861 | ++i; | ||
| 5862 | |||
| 5863 | for (int j = 0; j < size; ++j) | ||
| 5864 | { | ||
| 5865 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5866 | |||
| 5867 | if (cgltf_json_strcmp(tokens+i, json_chunk, "input") == 0) | ||
| 5868 | { | ||
| 5869 | ++i; | ||
| 5870 | out_sampler->input = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5871 | ++i; | ||
| 5872 | } | ||
| 5873 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "output") == 0) | ||
| 5874 | { | ||
| 5875 | ++i; | ||
| 5876 | out_sampler->output = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5877 | ++i; | ||
| 5878 | } | ||
| 5879 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "interpolation") == 0) | ||
| 5880 | { | ||
| 5881 | ++i; | ||
| 5882 | if (cgltf_json_strcmp(tokens + i, json_chunk, "LINEAR") == 0) | ||
| 5883 | { | ||
| 5884 | out_sampler->interpolation = cgltf_interpolation_type_linear; | ||
| 5885 | } | ||
| 5886 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "STEP") == 0) | ||
| 5887 | { | ||
| 5888 | out_sampler->interpolation = cgltf_interpolation_type_step; | ||
| 5889 | } | ||
| 5890 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "CUBICSPLINE") == 0) | ||
| 5891 | { | ||
| 5892 | out_sampler->interpolation = cgltf_interpolation_type_cubic_spline; | ||
| 5893 | } | ||
| 5894 | ++i; | ||
| 5895 | } | ||
| 5896 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 5897 | { | ||
| 5898 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras); | ||
| 5899 | } | ||
| 5900 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 5901 | { | ||
| 5902 | i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions); | ||
| 5903 | } | ||
| 5904 | else | ||
| 5905 | { | ||
| 5906 | i = cgltf_skip_json(tokens, i+1); | ||
| 5907 | } | ||
| 5908 | |||
| 5909 | if (i < 0) | ||
| 5910 | { | ||
| 5911 | return i; | ||
| 5912 | } | ||
| 5913 | } | ||
| 5914 | |||
| 5915 | return i; | ||
| 5916 | } | ||
| 5917 | |||
| 5918 | static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_channel* out_channel) | ||
| 5919 | { | ||
| 5920 | (void)options; | ||
| 5921 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5922 | |||
| 5923 | int size = tokens[i].size; | ||
| 5924 | ++i; | ||
| 5925 | |||
| 5926 | for (int j = 0; j < size; ++j) | ||
| 5927 | { | ||
| 5928 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5929 | |||
| 5930 | if (cgltf_json_strcmp(tokens+i, json_chunk, "sampler") == 0) | ||
| 5931 | { | ||
| 5932 | ++i; | ||
| 5933 | out_channel->sampler = CGLTF_PTRINDEX(cgltf_animation_sampler, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5934 | ++i; | ||
| 5935 | } | ||
| 5936 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) | ||
| 5937 | { | ||
| 5938 | ++i; | ||
| 5939 | |||
| 5940 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 5941 | |||
| 5942 | int target_size = tokens[i].size; | ||
| 5943 | ++i; | ||
| 5944 | |||
| 5945 | for (int k = 0; k < target_size; ++k) | ||
| 5946 | { | ||
| 5947 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 5948 | |||
| 5949 | if (cgltf_json_strcmp(tokens+i, json_chunk, "node") == 0) | ||
| 5950 | { | ||
| 5951 | ++i; | ||
| 5952 | out_channel->target_node = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 5953 | ++i; | ||
| 5954 | } | ||
| 5955 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "path") == 0) | ||
| 5956 | { | ||
| 5957 | ++i; | ||
| 5958 | if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0) | ||
| 5959 | { | ||
| 5960 | out_channel->target_path = cgltf_animation_path_type_translation; | ||
| 5961 | } | ||
| 5962 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0) | ||
| 5963 | { | ||
| 5964 | out_channel->target_path = cgltf_animation_path_type_rotation; | ||
| 5965 | } | ||
| 5966 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0) | ||
| 5967 | { | ||
| 5968 | out_channel->target_path = cgltf_animation_path_type_scale; | ||
| 5969 | } | ||
| 5970 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "weights") == 0) | ||
| 5971 | { | ||
| 5972 | out_channel->target_path = cgltf_animation_path_type_weights; | ||
| 5973 | } | ||
| 5974 | ++i; | ||
| 5975 | } | ||
| 5976 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 5977 | { | ||
| 5978 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_channel->extras); | ||
| 5979 | } | ||
| 5980 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 5981 | { | ||
| 5982 | i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_channel->extensions_count, &out_channel->extensions); | ||
| 5983 | } | ||
| 5984 | else | ||
| 5985 | { | ||
| 5986 | i = cgltf_skip_json(tokens, i+1); | ||
| 5987 | } | ||
| 5988 | |||
| 5989 | if (i < 0) | ||
| 5990 | { | ||
| 5991 | return i; | ||
| 5992 | } | ||
| 5993 | } | ||
| 5994 | } | ||
| 5995 | else | ||
| 5996 | { | ||
| 5997 | i = cgltf_skip_json(tokens, i+1); | ||
| 5998 | } | ||
| 5999 | |||
| 6000 | if (i < 0) | ||
| 6001 | { | ||
| 6002 | return i; | ||
| 6003 | } | ||
| 6004 | } | ||
| 6005 | |||
| 6006 | return i; | ||
| 6007 | } | ||
| 6008 | |||
| 6009 | static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation* out_animation) | ||
| 6010 | { | ||
| 6011 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 6012 | |||
| 6013 | int size = tokens[i].size; | ||
| 6014 | ++i; | ||
| 6015 | |||
| 6016 | for (int j = 0; j < size; ++j) | ||
| 6017 | { | ||
| 6018 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 6019 | |||
| 6020 | if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) | ||
| 6021 | { | ||
| 6022 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_animation->name); | ||
| 6023 | } | ||
| 6024 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "samplers") == 0) | ||
| 6025 | { | ||
| 6026 | i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_sampler), (void**)&out_animation->samplers, &out_animation->samplers_count); | ||
| 6027 | if (i < 0) | ||
| 6028 | { | ||
| 6029 | return i; | ||
| 6030 | } | ||
| 6031 | |||
| 6032 | for (cgltf_size k = 0; k < out_animation->samplers_count; ++k) | ||
| 6033 | { | ||
| 6034 | i = cgltf_parse_json_animation_sampler(options, tokens, i, json_chunk, &out_animation->samplers[k]); | ||
| 6035 | if (i < 0) | ||
| 6036 | { | ||
| 6037 | return i; | ||
| 6038 | } | ||
| 6039 | } | ||
| 6040 | } | ||
| 6041 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "channels") == 0) | ||
| 6042 | { | ||
| 6043 | i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_channel), (void**)&out_animation->channels, &out_animation->channels_count); | ||
| 6044 | if (i < 0) | ||
| 6045 | { | ||
| 6046 | return i; | ||
| 6047 | } | ||
| 6048 | |||
| 6049 | for (cgltf_size k = 0; k < out_animation->channels_count; ++k) | ||
| 6050 | { | ||
| 6051 | i = cgltf_parse_json_animation_channel(options, tokens, i, json_chunk, &out_animation->channels[k]); | ||
| 6052 | if (i < 0) | ||
| 6053 | { | ||
| 6054 | return i; | ||
| 6055 | } | ||
| 6056 | } | ||
| 6057 | } | ||
| 6058 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 6059 | { | ||
| 6060 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_animation->extras); | ||
| 6061 | } | ||
| 6062 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 6063 | { | ||
| 6064 | i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_animation->extensions_count, &out_animation->extensions); | ||
| 6065 | } | ||
| 6066 | else | ||
| 6067 | { | ||
| 6068 | i = cgltf_skip_json(tokens, i+1); | ||
| 6069 | } | ||
| 6070 | |||
| 6071 | if (i < 0) | ||
| 6072 | { | ||
| 6073 | return i; | ||
| 6074 | } | ||
| 6075 | } | ||
| 6076 | |||
| 6077 | return i; | ||
| 6078 | } | ||
| 6079 | |||
| 6080 | static int cgltf_parse_json_animations(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 6081 | { | ||
| 6082 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_animation), (void**)&out_data->animations, &out_data->animations_count); | ||
| 6083 | if (i < 0) | ||
| 6084 | { | ||
| 6085 | return i; | ||
| 6086 | } | ||
| 6087 | |||
| 6088 | for (cgltf_size j = 0; j < out_data->animations_count; ++j) | ||
| 6089 | { | ||
| 6090 | i = cgltf_parse_json_animation(options, tokens, i, json_chunk, &out_data->animations[j]); | ||
| 6091 | if (i < 0) | ||
| 6092 | { | ||
| 6093 | return i; | ||
| 6094 | } | ||
| 6095 | } | ||
| 6096 | return i; | ||
| 6097 | } | ||
| 6098 | |||
| 6099 | static int cgltf_parse_json_variant(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_variant* out_variant) | ||
| 6100 | { | ||
| 6101 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 6102 | |||
| 6103 | int size = tokens[i].size; | ||
| 6104 | ++i; | ||
| 6105 | |||
| 6106 | for (int j = 0; j < size; ++j) | ||
| 6107 | { | ||
| 6108 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 6109 | |||
| 6110 | if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) | ||
| 6111 | { | ||
| 6112 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_variant->name); | ||
| 6113 | } | ||
| 6114 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 6115 | { | ||
| 6116 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_variant->extras); | ||
| 6117 | } | ||
| 6118 | else | ||
| 6119 | { | ||
| 6120 | i = cgltf_skip_json(tokens, i+1); | ||
| 6121 | } | ||
| 6122 | |||
| 6123 | if (i < 0) | ||
| 6124 | { | ||
| 6125 | return i; | ||
| 6126 | } | ||
| 6127 | } | ||
| 6128 | |||
| 6129 | return i; | ||
| 6130 | } | ||
| 6131 | |||
| 6132 | static int cgltf_parse_json_variants(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 6133 | { | ||
| 6134 | i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material_variant), (void**)&out_data->variants, &out_data->variants_count); | ||
| 6135 | if (i < 0) | ||
| 6136 | { | ||
| 6137 | return i; | ||
| 6138 | } | ||
| 6139 | |||
| 6140 | for (cgltf_size j = 0; j < out_data->variants_count; ++j) | ||
| 6141 | { | ||
| 6142 | i = cgltf_parse_json_variant(options, tokens, i, json_chunk, &out_data->variants[j]); | ||
| 6143 | if (i < 0) | ||
| 6144 | { | ||
| 6145 | return i; | ||
| 6146 | } | ||
| 6147 | } | ||
| 6148 | return i; | ||
| 6149 | } | ||
| 6150 | |||
| 6151 | static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_asset* out_asset) | ||
| 6152 | { | ||
| 6153 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 6154 | |||
| 6155 | int size = tokens[i].size; | ||
| 6156 | ++i; | ||
| 6157 | |||
| 6158 | for (int j = 0; j < size; ++j) | ||
| 6159 | { | ||
| 6160 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 6161 | |||
| 6162 | if (cgltf_json_strcmp(tokens+i, json_chunk, "copyright") == 0) | ||
| 6163 | { | ||
| 6164 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->copyright); | ||
| 6165 | } | ||
| 6166 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "generator") == 0) | ||
| 6167 | { | ||
| 6168 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->generator); | ||
| 6169 | } | ||
| 6170 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "version") == 0) | ||
| 6171 | { | ||
| 6172 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->version); | ||
| 6173 | } | ||
| 6174 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "minVersion") == 0) | ||
| 6175 | { | ||
| 6176 | i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->min_version); | ||
| 6177 | } | ||
| 6178 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) | ||
| 6179 | { | ||
| 6180 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_asset->extras); | ||
| 6181 | } | ||
| 6182 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 6183 | { | ||
| 6184 | i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_asset->extensions_count, &out_asset->extensions); | ||
| 6185 | } | ||
| 6186 | else | ||
| 6187 | { | ||
| 6188 | i = cgltf_skip_json(tokens, i+1); | ||
| 6189 | } | ||
| 6190 | |||
| 6191 | if (i < 0) | ||
| 6192 | { | ||
| 6193 | return i; | ||
| 6194 | } | ||
| 6195 | } | ||
| 6196 | |||
| 6197 | if (out_asset->version && CGLTF_ATOF(out_asset->version) < 2) | ||
| 6198 | { | ||
| 6199 | return CGLTF_ERROR_LEGACY; | ||
| 6200 | } | ||
| 6201 | |||
| 6202 | return i; | ||
| 6203 | } | ||
| 6204 | |||
| 6205 | cgltf_size cgltf_num_components(cgltf_type type) { | ||
| 6206 | switch (type) | ||
| 6207 | { | ||
| 6208 | case cgltf_type_vec2: | ||
| 6209 | return 2; | ||
| 6210 | case cgltf_type_vec3: | ||
| 6211 | return 3; | ||
| 6212 | case cgltf_type_vec4: | ||
| 6213 | return 4; | ||
| 6214 | case cgltf_type_mat2: | ||
| 6215 | return 4; | ||
| 6216 | case cgltf_type_mat3: | ||
| 6217 | return 9; | ||
| 6218 | case cgltf_type_mat4: | ||
| 6219 | return 16; | ||
| 6220 | case cgltf_type_invalid: | ||
| 6221 | case cgltf_type_scalar: | ||
| 6222 | default: | ||
| 6223 | return 1; | ||
| 6224 | } | ||
| 6225 | } | ||
| 6226 | |||
| 6227 | cgltf_size cgltf_component_size(cgltf_component_type component_type) { | ||
| 6228 | switch (component_type) | ||
| 6229 | { | ||
| 6230 | case cgltf_component_type_r_8: | ||
| 6231 | case cgltf_component_type_r_8u: | ||
| 6232 | return 1; | ||
| 6233 | case cgltf_component_type_r_16: | ||
| 6234 | case cgltf_component_type_r_16u: | ||
| 6235 | return 2; | ||
| 6236 | case cgltf_component_type_r_32u: | ||
| 6237 | case cgltf_component_type_r_32f: | ||
| 6238 | return 4; | ||
| 6239 | case cgltf_component_type_invalid: | ||
| 6240 | default: | ||
| 6241 | return 0; | ||
| 6242 | } | ||
| 6243 | } | ||
| 6244 | |||
| 6245 | cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) | ||
| 6246 | { | ||
| 6247 | cgltf_size component_size = cgltf_component_size(component_type); | ||
| 6248 | if (type == cgltf_type_mat2 && component_size == 1) | ||
| 6249 | { | ||
| 6250 | return 8 * component_size; | ||
| 6251 | } | ||
| 6252 | else if (type == cgltf_type_mat3 && (component_size == 1 || component_size == 2)) | ||
| 6253 | { | ||
| 6254 | return 12 * component_size; | ||
| 6255 | } | ||
| 6256 | return component_size * cgltf_num_components(type); | ||
| 6257 | } | ||
| 6258 | |||
| 6259 | static int cgltf_fixup_pointers(cgltf_data* out_data); | ||
| 6260 | |||
| 6261 | static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) | ||
| 6262 | { | ||
| 6263 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 6264 | |||
| 6265 | int size = tokens[i].size; | ||
| 6266 | ++i; | ||
| 6267 | |||
| 6268 | for (int j = 0; j < size; ++j) | ||
| 6269 | { | ||
| 6270 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 6271 | |||
| 6272 | if (cgltf_json_strcmp(tokens + i, json_chunk, "asset") == 0) | ||
| 6273 | { | ||
| 6274 | i = cgltf_parse_json_asset(options, tokens, i + 1, json_chunk, &out_data->asset); | ||
| 6275 | } | ||
| 6276 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "meshes") == 0) | ||
| 6277 | { | ||
| 6278 | i = cgltf_parse_json_meshes(options, tokens, i + 1, json_chunk, out_data); | ||
| 6279 | } | ||
| 6280 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "accessors") == 0) | ||
| 6281 | { | ||
| 6282 | i = cgltf_parse_json_accessors(options, tokens, i + 1, json_chunk, out_data); | ||
| 6283 | } | ||
| 6284 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferViews") == 0) | ||
| 6285 | { | ||
| 6286 | i = cgltf_parse_json_buffer_views(options, tokens, i + 1, json_chunk, out_data); | ||
| 6287 | } | ||
| 6288 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "buffers") == 0) | ||
| 6289 | { | ||
| 6290 | i = cgltf_parse_json_buffers(options, tokens, i + 1, json_chunk, out_data); | ||
| 6291 | } | ||
| 6292 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "materials") == 0) | ||
| 6293 | { | ||
| 6294 | i = cgltf_parse_json_materials(options, tokens, i + 1, json_chunk, out_data); | ||
| 6295 | } | ||
| 6296 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "images") == 0) | ||
| 6297 | { | ||
| 6298 | i = cgltf_parse_json_images(options, tokens, i + 1, json_chunk, out_data); | ||
| 6299 | } | ||
| 6300 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "textures") == 0) | ||
| 6301 | { | ||
| 6302 | i = cgltf_parse_json_textures(options, tokens, i + 1, json_chunk, out_data); | ||
| 6303 | } | ||
| 6304 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "samplers") == 0) | ||
| 6305 | { | ||
| 6306 | i = cgltf_parse_json_samplers(options, tokens, i + 1, json_chunk, out_data); | ||
| 6307 | } | ||
| 6308 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "skins") == 0) | ||
| 6309 | { | ||
| 6310 | i = cgltf_parse_json_skins(options, tokens, i + 1, json_chunk, out_data); | ||
| 6311 | } | ||
| 6312 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "cameras") == 0) | ||
| 6313 | { | ||
| 6314 | i = cgltf_parse_json_cameras(options, tokens, i + 1, json_chunk, out_data); | ||
| 6315 | } | ||
| 6316 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "nodes") == 0) | ||
| 6317 | { | ||
| 6318 | i = cgltf_parse_json_nodes(options, tokens, i + 1, json_chunk, out_data); | ||
| 6319 | } | ||
| 6320 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "scenes") == 0) | ||
| 6321 | { | ||
| 6322 | i = cgltf_parse_json_scenes(options, tokens, i + 1, json_chunk, out_data); | ||
| 6323 | } | ||
| 6324 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "scene") == 0) | ||
| 6325 | { | ||
| 6326 | ++i; | ||
| 6327 | out_data->scene = CGLTF_PTRINDEX(cgltf_scene, cgltf_json_to_int(tokens + i, json_chunk)); | ||
| 6328 | ++i; | ||
| 6329 | } | ||
| 6330 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "animations") == 0) | ||
| 6331 | { | ||
| 6332 | i = cgltf_parse_json_animations(options, tokens, i + 1, json_chunk, out_data); | ||
| 6333 | } | ||
| 6334 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0) | ||
| 6335 | { | ||
| 6336 | i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_data->extras); | ||
| 6337 | } | ||
| 6338 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) | ||
| 6339 | { | ||
| 6340 | ++i; | ||
| 6341 | |||
| 6342 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 6343 | if(out_data->data_extensions) | ||
| 6344 | { | ||
| 6345 | return CGLTF_ERROR_JSON; | ||
| 6346 | } | ||
| 6347 | |||
| 6348 | int extensions_size = tokens[i].size; | ||
| 6349 | out_data->data_extensions_count = 0; | ||
| 6350 | out_data->data_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); | ||
| 6351 | |||
| 6352 | if (!out_data->data_extensions) | ||
| 6353 | { | ||
| 6354 | return CGLTF_ERROR_NOMEM; | ||
| 6355 | } | ||
| 6356 | |||
| 6357 | ++i; | ||
| 6358 | |||
| 6359 | for (int k = 0; k < extensions_size; ++k) | ||
| 6360 | { | ||
| 6361 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 6362 | |||
| 6363 | if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0) | ||
| 6364 | { | ||
| 6365 | ++i; | ||
| 6366 | |||
| 6367 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 6368 | |||
| 6369 | int data_size = tokens[i].size; | ||
| 6370 | ++i; | ||
| 6371 | |||
| 6372 | for (int m = 0; m < data_size; ++m) | ||
| 6373 | { | ||
| 6374 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 6375 | |||
| 6376 | if (cgltf_json_strcmp(tokens + i, json_chunk, "lights") == 0) | ||
| 6377 | { | ||
| 6378 | i = cgltf_parse_json_lights(options, tokens, i + 1, json_chunk, out_data); | ||
| 6379 | } | ||
| 6380 | else | ||
| 6381 | { | ||
| 6382 | i = cgltf_skip_json(tokens, i + 1); | ||
| 6383 | } | ||
| 6384 | |||
| 6385 | if (i < 0) | ||
| 6386 | { | ||
| 6387 | return i; | ||
| 6388 | } | ||
| 6389 | } | ||
| 6390 | } | ||
| 6391 | else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0) | ||
| 6392 | { | ||
| 6393 | ++i; | ||
| 6394 | |||
| 6395 | CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); | ||
| 6396 | |||
| 6397 | int data_size = tokens[i].size; | ||
| 6398 | ++i; | ||
| 6399 | |||
| 6400 | for (int m = 0; m < data_size; ++m) | ||
| 6401 | { | ||
| 6402 | CGLTF_CHECK_KEY(tokens[i]); | ||
| 6403 | |||
| 6404 | if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0) | ||
| 6405 | { | ||
| 6406 | i = cgltf_parse_json_variants(options, tokens, i + 1, json_chunk, out_data); | ||
| 6407 | } | ||
| 6408 | else | ||
| 6409 | { | ||
| 6410 | i = cgltf_skip_json(tokens, i + 1); | ||
| 6411 | } | ||
| 6412 | |||
| 6413 | if (i < 0) | ||
| 6414 | { | ||
| 6415 | return i; | ||
| 6416 | } | ||
| 6417 | } | ||
| 6418 | } | ||
| 6419 | else | ||
| 6420 | { | ||
| 6421 | i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_data->data_extensions[out_data->data_extensions_count++])); | ||
| 6422 | } | ||
| 6423 | |||
| 6424 | if (i < 0) | ||
| 6425 | { | ||
| 6426 | return i; | ||
| 6427 | } | ||
| 6428 | } | ||
| 6429 | } | ||
| 6430 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsUsed") == 0) | ||
| 6431 | { | ||
| 6432 | i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_used, &out_data->extensions_used_count); | ||
| 6433 | } | ||
| 6434 | else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsRequired") == 0) | ||
| 6435 | { | ||
| 6436 | i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_required, &out_data->extensions_required_count); | ||
| 6437 | } | ||
| 6438 | else | ||
| 6439 | { | ||
| 6440 | i = cgltf_skip_json(tokens, i + 1); | ||
| 6441 | } | ||
| 6442 | |||
| 6443 | if (i < 0) | ||
| 6444 | { | ||
| 6445 | return i; | ||
| 6446 | } | ||
| 6447 | } | ||
| 6448 | |||
| 6449 | return i; | ||
| 6450 | } | ||
| 6451 | |||
| 6452 | cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data) | ||
| 6453 | { | ||
| 6454 | jsmn_parser parser = { 0, 0, 0 }; | ||
| 6455 | |||
| 6456 | if (options->json_token_count == 0) | ||
| 6457 | { | ||
| 6458 | int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, NULL, 0); | ||
| 6459 | |||
| 6460 | if (token_count <= 0) | ||
| 6461 | { | ||
| 6462 | return cgltf_result_invalid_json; | ||
| 6463 | } | ||
| 6464 | |||
| 6465 | options->json_token_count = token_count; | ||
| 6466 | } | ||
| 6467 | |||
| 6468 | jsmntok_t* tokens = (jsmntok_t*)options->memory.alloc_func(options->memory.user_data, sizeof(jsmntok_t) * (options->json_token_count + 1)); | ||
| 6469 | |||
| 6470 | if (!tokens) | ||
| 6471 | { | ||
| 6472 | return cgltf_result_out_of_memory; | ||
| 6473 | } | ||
| 6474 | |||
| 6475 | jsmn_init(&parser); | ||
| 6476 | |||
| 6477 | int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, tokens, options->json_token_count); | ||
| 6478 | |||
| 6479 | if (token_count <= 0) | ||
| 6480 | { | ||
| 6481 | options->memory.free_func(options->memory.user_data, tokens); | ||
| 6482 | return cgltf_result_invalid_json; | ||
| 6483 | } | ||
| 6484 | |||
| 6485 | // this makes sure that we always have an UNDEFINED token at the end of the stream | ||
| 6486 | // for invalid JSON inputs this makes sure we don't perform out of bound reads of token data | ||
| 6487 | tokens[token_count].type = JSMN_UNDEFINED; | ||
| 6488 | |||
| 6489 | cgltf_data* data = (cgltf_data*)options->memory.alloc_func(options->memory.user_data, sizeof(cgltf_data)); | ||
| 6490 | |||
| 6491 | if (!data) | ||
| 6492 | { | ||
| 6493 | options->memory.free_func(options->memory.user_data, tokens); | ||
| 6494 | return cgltf_result_out_of_memory; | ||
| 6495 | } | ||
| 6496 | |||
| 6497 | memset(data, 0, sizeof(cgltf_data)); | ||
| 6498 | data->memory = options->memory; | ||
| 6499 | data->file = options->file; | ||
| 6500 | |||
| 6501 | int i = cgltf_parse_json_root(options, tokens, 0, json_chunk, data); | ||
| 6502 | |||
| 6503 | options->memory.free_func(options->memory.user_data, tokens); | ||
| 6504 | |||
| 6505 | if (i < 0) | ||
| 6506 | { | ||
| 6507 | cgltf_free(data); | ||
| 6508 | |||
| 6509 | switch (i) | ||
| 6510 | { | ||
| 6511 | case CGLTF_ERROR_NOMEM: return cgltf_result_out_of_memory; | ||
| 6512 | case CGLTF_ERROR_LEGACY: return cgltf_result_legacy_gltf; | ||
| 6513 | default: return cgltf_result_invalid_gltf; | ||
| 6514 | } | ||
| 6515 | } | ||
| 6516 | |||
| 6517 | if (cgltf_fixup_pointers(data) < 0) | ||
| 6518 | { | ||
| 6519 | cgltf_free(data); | ||
| 6520 | return cgltf_result_invalid_gltf; | ||
| 6521 | } | ||
| 6522 | |||
| 6523 | data->json = (const char*)json_chunk; | ||
| 6524 | data->json_size = size; | ||
| 6525 | |||
| 6526 | *out_data = data; | ||
| 6527 | |||
| 6528 | return cgltf_result_success; | ||
| 6529 | } | ||
| 6530 | |||
| 6531 | static int cgltf_fixup_pointers(cgltf_data* data) | ||
| 6532 | { | ||
| 6533 | for (cgltf_size i = 0; i < data->meshes_count; ++i) | ||
| 6534 | { | ||
| 6535 | for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) | ||
| 6536 | { | ||
| 6537 | CGLTF_PTRFIXUP(data->meshes[i].primitives[j].indices, data->accessors, data->accessors_count); | ||
| 6538 | CGLTF_PTRFIXUP(data->meshes[i].primitives[j].material, data->materials, data->materials_count); | ||
| 6539 | |||
| 6540 | for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) | ||
| 6541 | { | ||
| 6542 | CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].attributes[k].data, data->accessors, data->accessors_count); | ||
| 6543 | } | ||
| 6544 | |||
| 6545 | for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) | ||
| 6546 | { | ||
| 6547 | for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) | ||
| 6548 | { | ||
| 6549 | CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].targets[k].attributes[m].data, data->accessors, data->accessors_count); | ||
| 6550 | } | ||
| 6551 | } | ||
| 6552 | |||
| 6553 | if (data->meshes[i].primitives[j].has_draco_mesh_compression) | ||
| 6554 | { | ||
| 6555 | CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.buffer_view, data->buffer_views, data->buffer_views_count); | ||
| 6556 | for (cgltf_size m = 0; m < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++m) | ||
| 6557 | { | ||
| 6558 | CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.attributes[m].data, data->accessors, data->accessors_count); | ||
| 6559 | } | ||
| 6560 | } | ||
| 6561 | |||
| 6562 | for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) | ||
| 6563 | { | ||
| 6564 | CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].mappings[k].material, data->materials, data->materials_count); | ||
| 6565 | } | ||
| 6566 | } | ||
| 6567 | } | ||
| 6568 | |||
| 6569 | for (cgltf_size i = 0; i < data->accessors_count; ++i) | ||
| 6570 | { | ||
| 6571 | CGLTF_PTRFIXUP(data->accessors[i].buffer_view, data->buffer_views, data->buffer_views_count); | ||
| 6572 | |||
| 6573 | if (data->accessors[i].is_sparse) | ||
| 6574 | { | ||
| 6575 | CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.indices_buffer_view, data->buffer_views, data->buffer_views_count); | ||
| 6576 | CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.values_buffer_view, data->buffer_views, data->buffer_views_count); | ||
| 6577 | } | ||
| 6578 | |||
| 6579 | if (data->accessors[i].buffer_view) | ||
| 6580 | { | ||
| 6581 | data->accessors[i].stride = data->accessors[i].buffer_view->stride; | ||
| 6582 | } | ||
| 6583 | |||
| 6584 | if (data->accessors[i].stride == 0) | ||
| 6585 | { | ||
| 6586 | data->accessors[i].stride = cgltf_calc_size(data->accessors[i].type, data->accessors[i].component_type); | ||
| 6587 | } | ||
| 6588 | } | ||
| 6589 | |||
| 6590 | for (cgltf_size i = 0; i < data->textures_count; ++i) | ||
| 6591 | { | ||
| 6592 | CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count); | ||
| 6593 | CGLTF_PTRFIXUP(data->textures[i].basisu_image, data->images, data->images_count); | ||
| 6594 | CGLTF_PTRFIXUP(data->textures[i].webp_image, data->images, data->images_count); | ||
| 6595 | CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count); | ||
| 6596 | } | ||
| 6597 | |||
| 6598 | for (cgltf_size i = 0; i < data->images_count; ++i) | ||
| 6599 | { | ||
| 6600 | CGLTF_PTRFIXUP(data->images[i].buffer_view, data->buffer_views, data->buffer_views_count); | ||
| 6601 | } | ||
| 6602 | |||
| 6603 | for (cgltf_size i = 0; i < data->materials_count; ++i) | ||
| 6604 | { | ||
| 6605 | CGLTF_PTRFIXUP(data->materials[i].normal_texture.texture, data->textures, data->textures_count); | ||
| 6606 | CGLTF_PTRFIXUP(data->materials[i].emissive_texture.texture, data->textures, data->textures_count); | ||
| 6607 | CGLTF_PTRFIXUP(data->materials[i].occlusion_texture.texture, data->textures, data->textures_count); | ||
| 6608 | |||
| 6609 | CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.base_color_texture.texture, data->textures, data->textures_count); | ||
| 6610 | CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture, data->textures, data->textures_count); | ||
| 6611 | |||
| 6612 | CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.diffuse_texture.texture, data->textures, data->textures_count); | ||
| 6613 | CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.texture, data->textures, data->textures_count); | ||
| 6614 | |||
| 6615 | CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_texture.texture, data->textures, data->textures_count); | ||
| 6616 | CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_roughness_texture.texture, data->textures, data->textures_count); | ||
| 6617 | CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_normal_texture.texture, data->textures, data->textures_count); | ||
| 6618 | |||
| 6619 | CGLTF_PTRFIXUP(data->materials[i].specular.specular_texture.texture, data->textures, data->textures_count); | ||
| 6620 | CGLTF_PTRFIXUP(data->materials[i].specular.specular_color_texture.texture, data->textures, data->textures_count); | ||
| 6621 | |||
| 6622 | CGLTF_PTRFIXUP(data->materials[i].transmission.transmission_texture.texture, data->textures, data->textures_count); | ||
| 6623 | |||
| 6624 | CGLTF_PTRFIXUP(data->materials[i].volume.thickness_texture.texture, data->textures, data->textures_count); | ||
| 6625 | |||
| 6626 | CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_color_texture.texture, data->textures, data->textures_count); | ||
| 6627 | CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_roughness_texture.texture, data->textures, data->textures_count); | ||
| 6628 | |||
| 6629 | CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_texture.texture, data->textures, data->textures_count); | ||
| 6630 | CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_thickness_texture.texture, data->textures, data->textures_count); | ||
| 6631 | |||
| 6632 | CGLTF_PTRFIXUP(data->materials[i].anisotropy.anisotropy_texture.texture, data->textures, data->textures_count); | ||
| 6633 | } | ||
| 6634 | |||
| 6635 | for (cgltf_size i = 0; i < data->buffer_views_count; ++i) | ||
| 6636 | { | ||
| 6637 | CGLTF_PTRFIXUP_REQ(data->buffer_views[i].buffer, data->buffers, data->buffers_count); | ||
| 6638 | |||
| 6639 | if (data->buffer_views[i].has_meshopt_compression) | ||
| 6640 | { | ||
| 6641 | CGLTF_PTRFIXUP_REQ(data->buffer_views[i].meshopt_compression.buffer, data->buffers, data->buffers_count); | ||
| 6642 | } | ||
| 6643 | } | ||
| 6644 | |||
| 6645 | for (cgltf_size i = 0; i < data->skins_count; ++i) | ||
| 6646 | { | ||
| 6647 | for (cgltf_size j = 0; j < data->skins[i].joints_count; ++j) | ||
| 6648 | { | ||
| 6649 | CGLTF_PTRFIXUP_REQ(data->skins[i].joints[j], data->nodes, data->nodes_count); | ||
| 6650 | } | ||
| 6651 | |||
| 6652 | CGLTF_PTRFIXUP(data->skins[i].skeleton, data->nodes, data->nodes_count); | ||
| 6653 | CGLTF_PTRFIXUP(data->skins[i].inverse_bind_matrices, data->accessors, data->accessors_count); | ||
| 6654 | } | ||
| 6655 | |||
| 6656 | for (cgltf_size i = 0; i < data->nodes_count; ++i) | ||
| 6657 | { | ||
| 6658 | for (cgltf_size j = 0; j < data->nodes[i].children_count; ++j) | ||
| 6659 | { | ||
| 6660 | CGLTF_PTRFIXUP_REQ(data->nodes[i].children[j], data->nodes, data->nodes_count); | ||
| 6661 | |||
| 6662 | if (data->nodes[i].children[j]->parent) | ||
| 6663 | { | ||
| 6664 | return CGLTF_ERROR_JSON; | ||
| 6665 | } | ||
| 6666 | |||
| 6667 | data->nodes[i].children[j]->parent = &data->nodes[i]; | ||
| 6668 | } | ||
| 6669 | |||
| 6670 | CGLTF_PTRFIXUP(data->nodes[i].mesh, data->meshes, data->meshes_count); | ||
| 6671 | CGLTF_PTRFIXUP(data->nodes[i].skin, data->skins, data->skins_count); | ||
| 6672 | CGLTF_PTRFIXUP(data->nodes[i].camera, data->cameras, data->cameras_count); | ||
| 6673 | CGLTF_PTRFIXUP(data->nodes[i].light, data->lights, data->lights_count); | ||
| 6674 | |||
| 6675 | if (data->nodes[i].has_mesh_gpu_instancing) | ||
| 6676 | { | ||
| 6677 | for (cgltf_size m = 0; m < data->nodes[i].mesh_gpu_instancing.attributes_count; ++m) | ||
| 6678 | { | ||
| 6679 | CGLTF_PTRFIXUP_REQ(data->nodes[i].mesh_gpu_instancing.attributes[m].data, data->accessors, data->accessors_count); | ||
| 6680 | } | ||
| 6681 | } | ||
| 6682 | } | ||
| 6683 | |||
| 6684 | for (cgltf_size i = 0; i < data->scenes_count; ++i) | ||
| 6685 | { | ||
| 6686 | for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j) | ||
| 6687 | { | ||
| 6688 | CGLTF_PTRFIXUP_REQ(data->scenes[i].nodes[j], data->nodes, data->nodes_count); | ||
| 6689 | |||
| 6690 | if (data->scenes[i].nodes[j]->parent) | ||
| 6691 | { | ||
| 6692 | return CGLTF_ERROR_JSON; | ||
| 6693 | } | ||
| 6694 | } | ||
| 6695 | } | ||
| 6696 | |||
| 6697 | CGLTF_PTRFIXUP(data->scene, data->scenes, data->scenes_count); | ||
| 6698 | |||
| 6699 | for (cgltf_size i = 0; i < data->animations_count; ++i) | ||
| 6700 | { | ||
| 6701 | for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) | ||
| 6702 | { | ||
| 6703 | CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].input, data->accessors, data->accessors_count); | ||
| 6704 | CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].output, data->accessors, data->accessors_count); | ||
| 6705 | } | ||
| 6706 | |||
| 6707 | for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) | ||
| 6708 | { | ||
| 6709 | CGLTF_PTRFIXUP_REQ(data->animations[i].channels[j].sampler, data->animations[i].samplers, data->animations[i].samplers_count); | ||
| 6710 | CGLTF_PTRFIXUP(data->animations[i].channels[j].target_node, data->nodes, data->nodes_count); | ||
| 6711 | } | ||
| 6712 | } | ||
| 6713 | |||
| 6714 | return 0; | ||
| 6715 | } | ||
| 6716 | |||
| 6717 | /* | ||
| 6718 | * -- jsmn.c start -- | ||
| 6719 | * Source: https://github.com/zserge/jsmn | ||
| 6720 | * License: MIT | ||
| 6721 | * | ||
| 6722 | * Copyright (c) 2010 Serge A. Zaitsev | ||
| 6723 | |||
| 6724 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 6725 | * of this software and associated documentation files (the "Software"), to deal | ||
| 6726 | * in the Software without restriction, including without limitation the rights | ||
| 6727 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 6728 | * copies of the Software, and to permit persons to whom the Software is | ||
| 6729 | * furnished to do so, subject to the following conditions: | ||
| 6730 | |||
| 6731 | * The above copyright notice and this permission notice shall be included in | ||
| 6732 | * all copies or substantial portions of the Software. | ||
| 6733 | |||
| 6734 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 6735 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 6736 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 6737 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 6738 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 6739 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 6740 | * THE SOFTWARE. | ||
| 6741 | */ | ||
| 6742 | |||
| 6743 | /** | ||
| 6744 | * Allocates a fresh unused token from the token pull. | ||
| 6745 | */ | ||
| 6746 | static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, | ||
| 6747 | jsmntok_t *tokens, size_t num_tokens) { | ||
| 6748 | jsmntok_t *tok; | ||
| 6749 | if (parser->toknext >= num_tokens) { | ||
| 6750 | return NULL; | ||
| 6751 | } | ||
| 6752 | tok = &tokens[parser->toknext++]; | ||
| 6753 | tok->start = tok->end = -1; | ||
| 6754 | tok->size = 0; | ||
| 6755 | #ifdef JSMN_PARENT_LINKS | ||
| 6756 | tok->parent = -1; | ||
| 6757 | #endif | ||
| 6758 | return tok; | ||
| 6759 | } | ||
| 6760 | |||
| 6761 | /** | ||
| 6762 | * Fills token type and boundaries. | ||
| 6763 | */ | ||
| 6764 | static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, | ||
| 6765 | ptrdiff_t start, ptrdiff_t end) { | ||
| 6766 | token->type = type; | ||
| 6767 | token->start = start; | ||
| 6768 | token->end = end; | ||
| 6769 | token->size = 0; | ||
| 6770 | } | ||
| 6771 | |||
| 6772 | /** | ||
| 6773 | * Fills next available token with JSON primitive. | ||
| 6774 | */ | ||
| 6775 | static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, | ||
| 6776 | size_t len, jsmntok_t *tokens, size_t num_tokens) { | ||
| 6777 | jsmntok_t *token; | ||
| 6778 | ptrdiff_t start; | ||
| 6779 | |||
| 6780 | start = parser->pos; | ||
| 6781 | |||
| 6782 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { | ||
| 6783 | switch (js[parser->pos]) { | ||
| 6784 | #ifndef JSMN_STRICT | ||
| 6785 | /* In strict mode primitive must be followed by "," or "}" or "]" */ | ||
| 6786 | case ':': | ||
| 6787 | #endif | ||
| 6788 | case '\t' : case '\r' : case '\n' : case ' ' : | ||
| 6789 | case ',' : case ']' : case '}' : | ||
| 6790 | goto found; | ||
| 6791 | } | ||
| 6792 | if (js[parser->pos] < 32 || js[parser->pos] >= 127) { | ||
| 6793 | parser->pos = start; | ||
| 6794 | return JSMN_ERROR_INVAL; | ||
| 6795 | } | ||
| 6796 | } | ||
| 6797 | #ifdef JSMN_STRICT | ||
| 6798 | /* In strict mode primitive must be followed by a comma/object/array */ | ||
| 6799 | parser->pos = start; | ||
| 6800 | return JSMN_ERROR_PART; | ||
| 6801 | #endif | ||
| 6802 | |||
| 6803 | found: | ||
| 6804 | if (tokens == NULL) { | ||
| 6805 | parser->pos--; | ||
| 6806 | return 0; | ||
| 6807 | } | ||
| 6808 | token = jsmn_alloc_token(parser, tokens, num_tokens); | ||
| 6809 | if (token == NULL) { | ||
| 6810 | parser->pos = start; | ||
| 6811 | return JSMN_ERROR_NOMEM; | ||
| 6812 | } | ||
| 6813 | jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); | ||
| 6814 | #ifdef JSMN_PARENT_LINKS | ||
| 6815 | token->parent = parser->toksuper; | ||
| 6816 | #endif | ||
| 6817 | parser->pos--; | ||
| 6818 | return 0; | ||
| 6819 | } | ||
| 6820 | |||
| 6821 | /** | ||
| 6822 | * Fills next token with JSON string. | ||
| 6823 | */ | ||
| 6824 | static int jsmn_parse_string(jsmn_parser *parser, const char *js, | ||
| 6825 | size_t len, jsmntok_t *tokens, size_t num_tokens) { | ||
| 6826 | jsmntok_t *token; | ||
| 6827 | |||
| 6828 | ptrdiff_t start = parser->pos; | ||
| 6829 | |||
| 6830 | parser->pos++; | ||
| 6831 | |||
| 6832 | /* Skip starting quote */ | ||
| 6833 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { | ||
| 6834 | char c = js[parser->pos]; | ||
| 6835 | |||
| 6836 | /* Quote: end of string */ | ||
| 6837 | if (c == '\"') { | ||
| 6838 | if (tokens == NULL) { | ||
| 6839 | return 0; | ||
| 6840 | } | ||
| 6841 | token = jsmn_alloc_token(parser, tokens, num_tokens); | ||
| 6842 | if (token == NULL) { | ||
| 6843 | parser->pos = start; | ||
| 6844 | return JSMN_ERROR_NOMEM; | ||
| 6845 | } | ||
| 6846 | jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); | ||
| 6847 | #ifdef JSMN_PARENT_LINKS | ||
| 6848 | token->parent = parser->toksuper; | ||
| 6849 | #endif | ||
| 6850 | return 0; | ||
| 6851 | } | ||
| 6852 | |||
| 6853 | /* Backslash: Quoted symbol expected */ | ||
| 6854 | if (c == '\\' && parser->pos + 1 < len) { | ||
| 6855 | int i; | ||
| 6856 | parser->pos++; | ||
| 6857 | switch (js[parser->pos]) { | ||
| 6858 | /* Allowed escaped symbols */ | ||
| 6859 | case '\"': case '/' : case '\\' : case 'b' : | ||
| 6860 | case 'f' : case 'r' : case 'n' : case 't' : | ||
| 6861 | break; | ||
| 6862 | /* Allows escaped symbol \uXXXX */ | ||
| 6863 | case 'u': | ||
| 6864 | parser->pos++; | ||
| 6865 | for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { | ||
| 6866 | /* If it isn't a hex character we have an error */ | ||
| 6867 | if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ | ||
| 6868 | (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ | ||
| 6869 | (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ | ||
| 6870 | parser->pos = start; | ||
| 6871 | return JSMN_ERROR_INVAL; | ||
| 6872 | } | ||
| 6873 | parser->pos++; | ||
| 6874 | } | ||
| 6875 | parser->pos--; | ||
| 6876 | break; | ||
| 6877 | /* Unexpected symbol */ | ||
| 6878 | default: | ||
| 6879 | parser->pos = start; | ||
| 6880 | return JSMN_ERROR_INVAL; | ||
| 6881 | } | ||
| 6882 | } | ||
| 6883 | } | ||
| 6884 | parser->pos = start; | ||
| 6885 | return JSMN_ERROR_PART; | ||
| 6886 | } | ||
| 6887 | |||
| 6888 | /** | ||
| 6889 | * Parse JSON string and fill tokens. | ||
| 6890 | */ | ||
| 6891 | static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, | ||
| 6892 | jsmntok_t *tokens, size_t num_tokens) { | ||
| 6893 | int r; | ||
| 6894 | int i; | ||
| 6895 | jsmntok_t *token; | ||
| 6896 | int count = parser->toknext; | ||
| 6897 | |||
| 6898 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { | ||
| 6899 | char c; | ||
| 6900 | jsmntype_t type; | ||
| 6901 | |||
| 6902 | c = js[parser->pos]; | ||
| 6903 | switch (c) { | ||
| 6904 | case '{': case '[': | ||
| 6905 | count++; | ||
| 6906 | if (tokens == NULL) { | ||
| 6907 | break; | ||
| 6908 | } | ||
| 6909 | token = jsmn_alloc_token(parser, tokens, num_tokens); | ||
| 6910 | if (token == NULL) | ||
| 6911 | return JSMN_ERROR_NOMEM; | ||
| 6912 | if (parser->toksuper != -1) { | ||
| 6913 | tokens[parser->toksuper].size++; | ||
| 6914 | #ifdef JSMN_PARENT_LINKS | ||
| 6915 | token->parent = parser->toksuper; | ||
| 6916 | #endif | ||
| 6917 | } | ||
| 6918 | token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); | ||
| 6919 | token->start = parser->pos; | ||
| 6920 | parser->toksuper = parser->toknext - 1; | ||
| 6921 | break; | ||
| 6922 | case '}': case ']': | ||
| 6923 | if (tokens == NULL) | ||
| 6924 | break; | ||
| 6925 | type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); | ||
| 6926 | #ifdef JSMN_PARENT_LINKS | ||
| 6927 | if (parser->toknext < 1) { | ||
| 6928 | return JSMN_ERROR_INVAL; | ||
| 6929 | } | ||
| 6930 | token = &tokens[parser->toknext - 1]; | ||
| 6931 | for (;;) { | ||
| 6932 | if (token->start != -1 && token->end == -1) { | ||
| 6933 | if (token->type != type) { | ||
| 6934 | return JSMN_ERROR_INVAL; | ||
| 6935 | } | ||
| 6936 | token->end = parser->pos + 1; | ||
| 6937 | parser->toksuper = token->parent; | ||
| 6938 | break; | ||
| 6939 | } | ||
| 6940 | if (token->parent == -1) { | ||
| 6941 | if(token->type != type || parser->toksuper == -1) { | ||
| 6942 | return JSMN_ERROR_INVAL; | ||
| 6943 | } | ||
| 6944 | break; | ||
| 6945 | } | ||
| 6946 | token = &tokens[token->parent]; | ||
| 6947 | } | ||
| 6948 | #else | ||
| 6949 | for (i = parser->toknext - 1; i >= 0; i--) { | ||
| 6950 | token = &tokens[i]; | ||
| 6951 | if (token->start != -1 && token->end == -1) { | ||
| 6952 | if (token->type != type) { | ||
| 6953 | return JSMN_ERROR_INVAL; | ||
| 6954 | } | ||
| 6955 | parser->toksuper = -1; | ||
| 6956 | token->end = parser->pos + 1; | ||
| 6957 | break; | ||
| 6958 | } | ||
| 6959 | } | ||
| 6960 | /* Error if unmatched closing bracket */ | ||
| 6961 | if (i == -1) return JSMN_ERROR_INVAL; | ||
| 6962 | for (; i >= 0; i--) { | ||
| 6963 | token = &tokens[i]; | ||
| 6964 | if (token->start != -1 && token->end == -1) { | ||
| 6965 | parser->toksuper = i; | ||
| 6966 | break; | ||
| 6967 | } | ||
| 6968 | } | ||
| 6969 | #endif | ||
| 6970 | break; | ||
| 6971 | case '\"': | ||
| 6972 | r = jsmn_parse_string(parser, js, len, tokens, num_tokens); | ||
| 6973 | if (r < 0) return r; | ||
| 6974 | count++; | ||
| 6975 | if (parser->toksuper != -1 && tokens != NULL) | ||
| 6976 | tokens[parser->toksuper].size++; | ||
| 6977 | break; | ||
| 6978 | case '\t' : case '\r' : case '\n' : case ' ': | ||
| 6979 | break; | ||
| 6980 | case ':': | ||
| 6981 | parser->toksuper = parser->toknext - 1; | ||
| 6982 | break; | ||
| 6983 | case ',': | ||
| 6984 | if (tokens != NULL && parser->toksuper != -1 && | ||
| 6985 | tokens[parser->toksuper].type != JSMN_ARRAY && | ||
| 6986 | tokens[parser->toksuper].type != JSMN_OBJECT) { | ||
| 6987 | #ifdef JSMN_PARENT_LINKS | ||
| 6988 | parser->toksuper = tokens[parser->toksuper].parent; | ||
| 6989 | #else | ||
| 6990 | for (i = parser->toknext - 1; i >= 0; i--) { | ||
| 6991 | if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { | ||
| 6992 | if (tokens[i].start != -1 && tokens[i].end == -1) { | ||
| 6993 | parser->toksuper = i; | ||
| 6994 | break; | ||
| 6995 | } | ||
| 6996 | } | ||
| 6997 | } | ||
| 6998 | #endif | ||
| 6999 | } | ||
| 7000 | break; | ||
| 7001 | #ifdef JSMN_STRICT | ||
| 7002 | /* In strict mode primitives are: numbers and booleans */ | ||
| 7003 | case '-': case '0': case '1' : case '2': case '3' : case '4': | ||
| 7004 | case '5': case '6': case '7' : case '8': case '9': | ||
| 7005 | case 't': case 'f': case 'n' : | ||
| 7006 | /* And they must not be keys of the object */ | ||
| 7007 | if (tokens != NULL && parser->toksuper != -1) { | ||
| 7008 | jsmntok_t *t = &tokens[parser->toksuper]; | ||
| 7009 | if (t->type == JSMN_OBJECT || | ||
| 7010 | (t->type == JSMN_STRING && t->size != 0)) { | ||
| 7011 | return JSMN_ERROR_INVAL; | ||
| 7012 | } | ||
| 7013 | } | ||
| 7014 | #else | ||
| 7015 | /* In non-strict mode every unquoted value is a primitive */ | ||
| 7016 | default: | ||
| 7017 | #endif | ||
| 7018 | r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); | ||
| 7019 | if (r < 0) return r; | ||
| 7020 | count++; | ||
| 7021 | if (parser->toksuper != -1 && tokens != NULL) | ||
| 7022 | tokens[parser->toksuper].size++; | ||
| 7023 | break; | ||
| 7024 | |||
| 7025 | #ifdef JSMN_STRICT | ||
| 7026 | /* Unexpected char in strict mode */ | ||
| 7027 | default: | ||
| 7028 | return JSMN_ERROR_INVAL; | ||
| 7029 | #endif | ||
| 7030 | } | ||
| 7031 | } | ||
| 7032 | |||
| 7033 | if (tokens != NULL) { | ||
| 7034 | for (i = parser->toknext - 1; i >= 0; i--) { | ||
| 7035 | /* Unmatched opened object or array */ | ||
| 7036 | if (tokens[i].start != -1 && tokens[i].end == -1) { | ||
| 7037 | return JSMN_ERROR_PART; | ||
| 7038 | } | ||
| 7039 | } | ||
| 7040 | } | ||
| 7041 | |||
| 7042 | return count; | ||
| 7043 | } | ||
| 7044 | |||
| 7045 | /** | ||
| 7046 | * Creates a new parser based over a given buffer with an array of tokens | ||
| 7047 | * available. | ||
| 7048 | */ | ||
| 7049 | static void jsmn_init(jsmn_parser *parser) { | ||
| 7050 | parser->pos = 0; | ||
| 7051 | parser->toknext = 0; | ||
| 7052 | parser->toksuper = -1; | ||
| 7053 | } | ||
| 7054 | /* | ||
| 7055 | * -- jsmn.c end -- | ||
| 7056 | */ | ||
| 7057 | |||
| 7058 | #endif /* #ifdef CGLTF_IMPLEMENTATION */ | ||
| 7059 | |||
| 7060 | /* cgltf is distributed under MIT license: | ||
| 7061 | * | ||
| 7062 | * Copyright (c) 2018-2021 Johannes Kuhlmann | ||
| 7063 | |||
| 7064 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 7065 | * of this software and associated documentation files (the "Software"), to deal | ||
| 7066 | * in the Software without restriction, including without limitation the rights | ||
| 7067 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 7068 | * copies of the Software, and to permit persons to whom the Software is | ||
| 7069 | * furnished to do so, subject to the following conditions: | ||
| 7070 | |||
| 7071 | * The above copyright notice and this permission notice shall be included in all | ||
| 7072 | * copies or substantial portions of the Software. | ||
| 7073 | |||
| 7074 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 7075 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 7076 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 7077 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 7078 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 7079 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 7080 | * SOFTWARE. | ||
| 7081 | */ | ||
diff --git a/raylib/src/external/dirent.h b/raylib/src/external/dirent.h new file mode 100644 index 0000000..c611376 --- /dev/null +++ b/raylib/src/external/dirent.h | |||
| @@ -0,0 +1,183 @@ | |||
| 1 | /**************************************************************************** | ||
| 2 | |||
| 3 | Declaration of POSIX directory browsing functions and types for Win32. | ||
| 4 | |||
| 5 | Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) | ||
| 6 | History: Created March 1997. Updated June 2003. | ||
| 7 | Reviewed by Ramon Santamaria for raylib on January 2020. | ||
| 8 | |||
| 9 | Copyright Kevlin Henney, 1997, 2003. All rights reserved. | ||
| 10 | |||
| 11 | Permission to use, copy, modify, and distribute this software and its | ||
| 12 | documentation for any purpose is hereby granted without fee, provided | ||
| 13 | that this copyright and permissions notice appear in all copies and | ||
| 14 | derivatives. | ||
| 15 | |||
| 16 | This software is supplied "as is" without express or implied warranty. | ||
| 17 | |||
| 18 | But that said, if there are any problems please get in touch. | ||
| 19 | |||
| 20 | ****************************************************************************/ | ||
| 21 | |||
| 22 | #ifndef DIRENT_H | ||
| 23 | #define DIRENT_H | ||
| 24 | |||
| 25 | // Allow custom memory allocators | ||
| 26 | #ifndef DIRENT_MALLOC | ||
| 27 | #define DIRENT_MALLOC(sz) malloc(sz) | ||
| 28 | #endif | ||
| 29 | #ifndef DIRENT_FREE | ||
| 30 | #define DIRENT_FREE(p) free(p) | ||
| 31 | #endif | ||
| 32 | |||
| 33 | //---------------------------------------------------------------------------------- | ||
| 34 | // Types and Structures Definition | ||
| 35 | //---------------------------------------------------------------------------------- | ||
| 36 | |||
| 37 | // Fordward declaration of DIR, implementation below | ||
| 38 | typedef struct DIR DIR; | ||
| 39 | |||
| 40 | struct dirent { | ||
| 41 | char *d_name; | ||
| 42 | }; | ||
| 43 | |||
| 44 | #ifdef __cplusplus | ||
| 45 | extern "C" { | ||
| 46 | #endif | ||
| 47 | |||
| 48 | //------------------------------------------------------------------------------------ | ||
| 49 | // Functions Declaration | ||
| 50 | //------------------------------------------------------------------------------------ | ||
| 51 | DIR *opendir(const char *name); | ||
| 52 | int closedir(DIR *dir); | ||
| 53 | struct dirent *readdir(DIR *dir); | ||
| 54 | void rewinddir(DIR *dir); | ||
| 55 | |||
| 56 | #ifdef __cplusplus | ||
| 57 | } | ||
| 58 | #endif | ||
| 59 | |||
| 60 | #endif // DIRENT_H | ||
| 61 | |||
| 62 | /**************************************************************************** | ||
| 63 | |||
| 64 | Implementation of POSIX directory browsing functions and types for Win32. | ||
| 65 | |||
| 66 | Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) | ||
| 67 | History: Created March 1997. Updated June 2003. | ||
| 68 | Reviewed by Ramon Santamaria for raylib on January 2020. | ||
| 69 | |||
| 70 | Copyright Kevlin Henney, 1997, 2003. All rights reserved. | ||
| 71 | |||
| 72 | Permission to use, copy, modify, and distribute this software and its | ||
| 73 | documentation for any purpose is hereby granted without fee, provided | ||
| 74 | that this copyright and permissions notice appear in all copies and | ||
| 75 | derivatives. | ||
| 76 | |||
| 77 | This software is supplied "as is" without express or implied warranty. | ||
| 78 | |||
| 79 | But that said, if there are any problems please get in touch. | ||
| 80 | |||
| 81 | ****************************************************************************/ | ||
| 82 | |||
| 83 | #include <io.h> // _findfirst and _findnext set errno iff they return -1 | ||
| 84 | #include <stdlib.h> | ||
| 85 | #include <string.h> | ||
| 86 | #include <errno.h> | ||
| 87 | |||
| 88 | //---------------------------------------------------------------------------------- | ||
| 89 | // Types and Structures Definition | ||
| 90 | //---------------------------------------------------------------------------------- | ||
| 91 | typedef ptrdiff_t handle_type; // C99's intptr_t not sufficiently portable | ||
| 92 | |||
| 93 | struct DIR { | ||
| 94 | handle_type handle; // -1 for failed rewind | ||
| 95 | struct _finddata_t info; | ||
| 96 | struct dirent result; // d_name null iff first time | ||
| 97 | char *name; // null-terminated char string | ||
| 98 | }; | ||
| 99 | |||
| 100 | DIR *opendir(const char *name) | ||
| 101 | { | ||
| 102 | DIR *dir = 0; | ||
| 103 | |||
| 104 | if (name && name[0]) | ||
| 105 | { | ||
| 106 | size_t base_length = strlen(name); | ||
| 107 | |||
| 108 | // Search pattern must end with suitable wildcard | ||
| 109 | const char *all = strchr("/\\", name[base_length - 1]) ? "*" : "/*"; | ||
| 110 | |||
| 111 | if ((dir = (DIR *)DIRENT_MALLOC(sizeof *dir)) != 0 && | ||
| 112 | (dir->name = (char *)DIRENT_MALLOC(base_length + strlen(all) + 1)) != 0) | ||
| 113 | { | ||
| 114 | strcat(strcpy(dir->name, name), all); | ||
| 115 | |||
| 116 | if ((dir->handle = (handle_type) _findfirst(dir->name, &dir->info)) != -1) | ||
| 117 | { | ||
| 118 | dir->result.d_name = 0; | ||
| 119 | } | ||
| 120 | else // rollback | ||
| 121 | { | ||
| 122 | DIRENT_FREE(dir->name); | ||
| 123 | DIRENT_FREE(dir); | ||
| 124 | dir = 0; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | else // rollback | ||
| 128 | { | ||
| 129 | DIRENT_FREE(dir); | ||
| 130 | dir = 0; | ||
| 131 | errno = ENOMEM; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | else errno = EINVAL; | ||
| 135 | |||
| 136 | return dir; | ||
| 137 | } | ||
| 138 | |||
| 139 | int closedir(DIR *dir) | ||
| 140 | { | ||
| 141 | int result = -1; | ||
| 142 | |||
| 143 | if (dir) | ||
| 144 | { | ||
| 145 | if (dir->handle != -1) result = _findclose(dir->handle); | ||
| 146 | |||
| 147 | DIRENT_FREE(dir->name); | ||
| 148 | DIRENT_FREE(dir); | ||
| 149 | } | ||
| 150 | |||
| 151 | // NOTE: All errors ampped to EBADF | ||
| 152 | if (result == -1) errno = EBADF; | ||
| 153 | |||
| 154 | return result; | ||
| 155 | } | ||
| 156 | |||
| 157 | struct dirent *readdir(DIR *dir) | ||
| 158 | { | ||
| 159 | struct dirent *result = 0; | ||
| 160 | |||
| 161 | if (dir && dir->handle != -1) | ||
| 162 | { | ||
| 163 | if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) | ||
| 164 | { | ||
| 165 | result = &dir->result; | ||
| 166 | result->d_name = dir->info.name; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | else errno = EBADF; | ||
| 170 | |||
| 171 | return result; | ||
| 172 | } | ||
| 173 | |||
| 174 | void rewinddir(DIR *dir) | ||
| 175 | { | ||
| 176 | if (dir && dir->handle != -1) | ||
| 177 | { | ||
| 178 | _findclose(dir->handle); | ||
| 179 | dir->handle = (handle_type) _findfirst(dir->name, &dir->info); | ||
| 180 | dir->result.d_name = 0; | ||
| 181 | } | ||
| 182 | else errno = EBADF; | ||
| 183 | } | ||
diff --git a/raylib/src/external/dr_flac.h b/raylib/src/external/dr_flac.h new file mode 100644 index 0000000..14324cf --- /dev/null +++ b/raylib/src/external/dr_flac.h | |||
| @@ -0,0 +1,12536 @@ | |||
| 1 | /* | ||
| 2 | FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. | ||
| 3 | dr_flac - v0.12.42 - 2023-11-02 | ||
| 4 | |||
| 5 | David Reid - mackron@gmail.com | ||
| 6 | |||
| 7 | GitHub: https://github.com/mackron/dr_libs | ||
| 8 | */ | ||
| 9 | |||
| 10 | /* | ||
| 11 | RELEASE NOTES - v0.12.0 | ||
| 12 | ======================= | ||
| 13 | Version 0.12.0 has breaking API changes including changes to the existing API and the removal of deprecated APIs. | ||
| 14 | |||
| 15 | |||
| 16 | Improved Client-Defined Memory Allocation | ||
| 17 | ----------------------------------------- | ||
| 18 | The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The | ||
| 19 | existing system of DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE are still in place and will be used by default when no custom | ||
| 20 | allocation callbacks are specified. | ||
| 21 | |||
| 22 | To use the new system, you pass in a pointer to a drflac_allocation_callbacks object to drflac_open() and family, like this: | ||
| 23 | |||
| 24 | void* my_malloc(size_t sz, void* pUserData) | ||
| 25 | { | ||
| 26 | return malloc(sz); | ||
| 27 | } | ||
| 28 | void* my_realloc(void* p, size_t sz, void* pUserData) | ||
| 29 | { | ||
| 30 | return realloc(p, sz); | ||
| 31 | } | ||
| 32 | void my_free(void* p, void* pUserData) | ||
| 33 | { | ||
| 34 | free(p); | ||
| 35 | } | ||
| 36 | |||
| 37 | ... | ||
| 38 | |||
| 39 | drflac_allocation_callbacks allocationCallbacks; | ||
| 40 | allocationCallbacks.pUserData = &myData; | ||
| 41 | allocationCallbacks.onMalloc = my_malloc; | ||
| 42 | allocationCallbacks.onRealloc = my_realloc; | ||
| 43 | allocationCallbacks.onFree = my_free; | ||
| 44 | drflac* pFlac = drflac_open_file("my_file.flac", &allocationCallbacks); | ||
| 45 | |||
| 46 | The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines. | ||
| 47 | |||
| 48 | Passing in null for the allocation callbacks object will cause dr_flac to use defaults which is the same as DRFLAC_MALLOC, | ||
| 49 | DRFLAC_REALLOC and DRFLAC_FREE and the equivalent of how it worked in previous versions. | ||
| 50 | |||
| 51 | Every API that opens a drflac object now takes this extra parameter. These include the following: | ||
| 52 | |||
| 53 | drflac_open() | ||
| 54 | drflac_open_relaxed() | ||
| 55 | drflac_open_with_metadata() | ||
| 56 | drflac_open_with_metadata_relaxed() | ||
| 57 | drflac_open_file() | ||
| 58 | drflac_open_file_with_metadata() | ||
| 59 | drflac_open_memory() | ||
| 60 | drflac_open_memory_with_metadata() | ||
| 61 | drflac_open_and_read_pcm_frames_s32() | ||
| 62 | drflac_open_and_read_pcm_frames_s16() | ||
| 63 | drflac_open_and_read_pcm_frames_f32() | ||
| 64 | drflac_open_file_and_read_pcm_frames_s32() | ||
| 65 | drflac_open_file_and_read_pcm_frames_s16() | ||
| 66 | drflac_open_file_and_read_pcm_frames_f32() | ||
| 67 | drflac_open_memory_and_read_pcm_frames_s32() | ||
| 68 | drflac_open_memory_and_read_pcm_frames_s16() | ||
| 69 | drflac_open_memory_and_read_pcm_frames_f32() | ||
| 70 | |||
| 71 | |||
| 72 | |||
| 73 | Optimizations | ||
| 74 | ------------- | ||
| 75 | Seeking performance has been greatly improved. A new binary search based seeking algorithm has been introduced which significantly | ||
| 76 | improves performance over the brute force method which was used when no seek table was present. Seek table based seeking also takes | ||
| 77 | advantage of the new binary search seeking system to further improve performance there as well. Note that this depends on CRC which | ||
| 78 | means it will be disabled when DR_FLAC_NO_CRC is used. | ||
| 79 | |||
| 80 | The SSE4.1 pipeline has been cleaned up and optimized. You should see some improvements with decoding speed of 24-bit files in | ||
| 81 | particular. 16-bit streams should also see some improvement. | ||
| 82 | |||
| 83 | drflac_read_pcm_frames_s16() has been optimized. Previously this sat on top of drflac_read_pcm_frames_s32() and performed it's s32 | ||
| 84 | to s16 conversion in a second pass. This is now all done in a single pass. This includes SSE2 and ARM NEON optimized paths. | ||
| 85 | |||
| 86 | A minor optimization has been implemented for drflac_read_pcm_frames_s32(). This will now use an SSE2 optimized pipeline for stereo | ||
| 87 | channel reconstruction which is the last part of the decoding process. | ||
| 88 | |||
| 89 | The ARM build has seen a few improvements. The CLZ (count leading zeroes) and REV (byte swap) instructions are now used when | ||
| 90 | compiling with GCC and Clang which is achieved using inline assembly. The CLZ instruction requires ARM architecture version 5 at | ||
| 91 | compile time and the REV instruction requires ARM architecture version 6. | ||
| 92 | |||
| 93 | An ARM NEON optimized pipeline has been implemented. To enable this you'll need to add -mfpu=neon to the command line when compiling. | ||
| 94 | |||
| 95 | |||
| 96 | Removed APIs | ||
| 97 | ------------ | ||
| 98 | The following APIs were deprecated in version 0.11.0 and have been completely removed in version 0.12.0: | ||
| 99 | |||
| 100 | drflac_read_s32() -> drflac_read_pcm_frames_s32() | ||
| 101 | drflac_read_s16() -> drflac_read_pcm_frames_s16() | ||
| 102 | drflac_read_f32() -> drflac_read_pcm_frames_f32() | ||
| 103 | drflac_seek_to_sample() -> drflac_seek_to_pcm_frame() | ||
| 104 | drflac_open_and_decode_s32() -> drflac_open_and_read_pcm_frames_s32() | ||
| 105 | drflac_open_and_decode_s16() -> drflac_open_and_read_pcm_frames_s16() | ||
| 106 | drflac_open_and_decode_f32() -> drflac_open_and_read_pcm_frames_f32() | ||
| 107 | drflac_open_and_decode_file_s32() -> drflac_open_file_and_read_pcm_frames_s32() | ||
| 108 | drflac_open_and_decode_file_s16() -> drflac_open_file_and_read_pcm_frames_s16() | ||
| 109 | drflac_open_and_decode_file_f32() -> drflac_open_file_and_read_pcm_frames_f32() | ||
| 110 | drflac_open_and_decode_memory_s32() -> drflac_open_memory_and_read_pcm_frames_s32() | ||
| 111 | drflac_open_and_decode_memory_s16() -> drflac_open_memory_and_read_pcm_frames_s16() | ||
| 112 | drflac_open_and_decode_memory_f32() -> drflac_open_memroy_and_read_pcm_frames_f32() | ||
| 113 | |||
| 114 | Prior versions of dr_flac operated on a per-sample basis whereas now it operates on PCM frames. The removed APIs all relate | ||
| 115 | to the old per-sample APIs. You now need to use the "pcm_frame" versions. | ||
| 116 | */ | ||
| 117 | |||
| 118 | |||
| 119 | /* | ||
| 120 | Introduction | ||
| 121 | ============ | ||
| 122 | dr_flac is a single file library. To use it, do something like the following in one .c file. | ||
| 123 | |||
| 124 | ```c | ||
| 125 | #define DR_FLAC_IMPLEMENTATION | ||
| 126 | #include "dr_flac.h" | ||
| 127 | ``` | ||
| 128 | |||
| 129 | You can then #include this file in other parts of the program as you would with any other header file. To decode audio data, do something like the following: | ||
| 130 | |||
| 131 | ```c | ||
| 132 | drflac* pFlac = drflac_open_file("MySong.flac", NULL); | ||
| 133 | if (pFlac == NULL) { | ||
| 134 | // Failed to open FLAC file | ||
| 135 | } | ||
| 136 | |||
| 137 | drflac_int32* pSamples = malloc(pFlac->totalPCMFrameCount * pFlac->channels * sizeof(drflac_int32)); | ||
| 138 | drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_pcm_frames_s32(pFlac, pFlac->totalPCMFrameCount, pSamples); | ||
| 139 | ``` | ||
| 140 | |||
| 141 | The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of channels and the bits per sample, | ||
| 142 | should be directly accessible - just make sure you don't change their values. Samples are always output as interleaved signed 32-bit PCM. In the example above | ||
| 143 | a native FLAC stream was opened, however dr_flac has seamless support for Ogg encapsulated FLAC streams as well. | ||
| 144 | |||
| 145 | You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and the decoder will give you as many | ||
| 146 | samples as it can, up to the amount requested. Later on when you need the next batch of samples, just call it again. Example: | ||
| 147 | |||
| 148 | ```c | ||
| 149 | while (drflac_read_pcm_frames_s32(pFlac, chunkSizeInPCMFrames, pChunkSamples) > 0) { | ||
| 150 | do_something(); | ||
| 151 | } | ||
| 152 | ``` | ||
| 153 | |||
| 154 | You can seek to a specific PCM frame with `drflac_seek_to_pcm_frame()`. | ||
| 155 | |||
| 156 | If you just want to quickly decode an entire FLAC file in one go you can do something like this: | ||
| 157 | |||
| 158 | ```c | ||
| 159 | unsigned int channels; | ||
| 160 | unsigned int sampleRate; | ||
| 161 | drflac_uint64 totalPCMFrameCount; | ||
| 162 | drflac_int32* pSampleData = drflac_open_file_and_read_pcm_frames_s32("MySong.flac", &channels, &sampleRate, &totalPCMFrameCount, NULL); | ||
| 163 | if (pSampleData == NULL) { | ||
| 164 | // Failed to open and decode FLAC file. | ||
| 165 | } | ||
| 166 | |||
| 167 | ... | ||
| 168 | |||
| 169 | drflac_free(pSampleData, NULL); | ||
| 170 | ``` | ||
| 171 | |||
| 172 | You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs respectively, but note that these | ||
| 173 | should be considered lossy. | ||
| 174 | |||
| 175 | |||
| 176 | If you need access to metadata (album art, etc.), use `drflac_open_with_metadata()`, `drflac_open_file_with_metdata()` or `drflac_open_memory_with_metadata()`. | ||
| 177 | The rationale for keeping these APIs separate is that they're slightly slower than the normal versions and also just a little bit harder to use. dr_flac | ||
| 178 | reports metadata to the application through the use of a callback, and every metadata block is reported before `drflac_open_with_metdata()` returns. | ||
| 179 | |||
| 180 | The main opening APIs (`drflac_open()`, etc.) will fail if the header is not present. The presents a problem in certain scenarios such as broadcast style | ||
| 181 | streams or internet radio where the header may not be present because the user has started playback mid-stream. To handle this, use the relaxed APIs: | ||
| 182 | |||
| 183 | `drflac_open_relaxed()` | ||
| 184 | `drflac_open_with_metadata_relaxed()` | ||
| 185 | |||
| 186 | It is not recommended to use these APIs for file based streams because a missing header would usually indicate a corrupt or perverse file. In addition, these | ||
| 187 | APIs can take a long time to initialize because they may need to spend a lot of time finding the first frame. | ||
| 188 | |||
| 189 | |||
| 190 | |||
| 191 | Build Options | ||
| 192 | ============= | ||
| 193 | #define these options before including this file. | ||
| 194 | |||
| 195 | #define DR_FLAC_NO_STDIO | ||
| 196 | Disable `drflac_open_file()` and family. | ||
| 197 | |||
| 198 | #define DR_FLAC_NO_OGG | ||
| 199 | Disables support for Ogg/FLAC streams. | ||
| 200 | |||
| 201 | #define DR_FLAC_BUFFER_SIZE <number> | ||
| 202 | Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls back to the client for more data. | ||
| 203 | Larger values means more memory, but better performance. My tests show diminishing returns after about 4KB (which is the default). Consider reducing this if | ||
| 204 | you have a very efficient implementation of onRead(), or increase it if it's very inefficient. Must be a multiple of 8. | ||
| 205 | |||
| 206 | #define DR_FLAC_NO_CRC | ||
| 207 | Disables CRC checks. This will offer a performance boost when CRC is unnecessary. This will disable binary search seeking. When seeking, the seek table will | ||
| 208 | be used if available. Otherwise the seek will be performed using brute force. | ||
| 209 | |||
| 210 | #define DR_FLAC_NO_SIMD | ||
| 211 | Disables SIMD optimizations (SSE on x86/x64 architectures, NEON on ARM architectures). Use this if you are having compatibility issues with your compiler. | ||
| 212 | |||
| 213 | #define DR_FLAC_NO_WCHAR | ||
| 214 | Disables all functions ending with `_w`. Use this if your compiler does not provide wchar.h. Not required if DR_FLAC_NO_STDIO is also defined. | ||
| 215 | |||
| 216 | |||
| 217 | |||
| 218 | Notes | ||
| 219 | ===== | ||
| 220 | - dr_flac does not support changing the sample rate nor channel count mid stream. | ||
| 221 | - dr_flac is not thread-safe, but its APIs can be called from any thread so long as you do your own synchronization. | ||
| 222 | - When using Ogg encapsulation, a corrupted metadata block will result in `drflac_open_with_metadata()` and `drflac_open()` returning inconsistent samples due | ||
| 223 | to differences in corrupted stream recorvery logic between the two APIs. | ||
| 224 | */ | ||
| 225 | |||
| 226 | #ifndef dr_flac_h | ||
| 227 | #define dr_flac_h | ||
| 228 | |||
| 229 | #ifdef __cplusplus | ||
| 230 | extern "C" { | ||
| 231 | #endif | ||
| 232 | |||
| 233 | #define DRFLAC_STRINGIFY(x) #x | ||
| 234 | #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) | ||
| 235 | |||
| 236 | #define DRFLAC_VERSION_MAJOR 0 | ||
| 237 | #define DRFLAC_VERSION_MINOR 12 | ||
| 238 | #define DRFLAC_VERSION_REVISION 42 | ||
| 239 | #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) | ||
| 240 | |||
| 241 | #include <stddef.h> /* For size_t. */ | ||
| 242 | |||
| 243 | /* Sized Types */ | ||
| 244 | typedef signed char drflac_int8; | ||
| 245 | typedef unsigned char drflac_uint8; | ||
| 246 | typedef signed short drflac_int16; | ||
| 247 | typedef unsigned short drflac_uint16; | ||
| 248 | typedef signed int drflac_int32; | ||
| 249 | typedef unsigned int drflac_uint32; | ||
| 250 | #if defined(_MSC_VER) && !defined(__clang__) | ||
| 251 | typedef signed __int64 drflac_int64; | ||
| 252 | typedef unsigned __int64 drflac_uint64; | ||
| 253 | #else | ||
| 254 | #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) | ||
| 255 | #pragma GCC diagnostic push | ||
| 256 | #pragma GCC diagnostic ignored "-Wlong-long" | ||
| 257 | #if defined(__clang__) | ||
| 258 | #pragma GCC diagnostic ignored "-Wc++11-long-long" | ||
| 259 | #endif | ||
| 260 | #endif | ||
| 261 | typedef signed long long drflac_int64; | ||
| 262 | typedef unsigned long long drflac_uint64; | ||
| 263 | #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) | ||
| 264 | #pragma GCC diagnostic pop | ||
| 265 | #endif | ||
| 266 | #endif | ||
| 267 | #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) | ||
| 268 | typedef drflac_uint64 drflac_uintptr; | ||
| 269 | #else | ||
| 270 | typedef drflac_uint32 drflac_uintptr; | ||
| 271 | #endif | ||
| 272 | typedef drflac_uint8 drflac_bool8; | ||
| 273 | typedef drflac_uint32 drflac_bool32; | ||
| 274 | #define DRFLAC_TRUE 1 | ||
| 275 | #define DRFLAC_FALSE 0 | ||
| 276 | /* End Sized Types */ | ||
| 277 | |||
| 278 | /* Decorations */ | ||
| 279 | #if !defined(DRFLAC_API) | ||
| 280 | #if defined(DRFLAC_DLL) | ||
| 281 | #if defined(_WIN32) | ||
| 282 | #define DRFLAC_DLL_IMPORT __declspec(dllimport) | ||
| 283 | #define DRFLAC_DLL_EXPORT __declspec(dllexport) | ||
| 284 | #define DRFLAC_DLL_PRIVATE static | ||
| 285 | #else | ||
| 286 | #if defined(__GNUC__) && __GNUC__ >= 4 | ||
| 287 | #define DRFLAC_DLL_IMPORT __attribute__((visibility("default"))) | ||
| 288 | #define DRFLAC_DLL_EXPORT __attribute__((visibility("default"))) | ||
| 289 | #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden"))) | ||
| 290 | #else | ||
| 291 | #define DRFLAC_DLL_IMPORT | ||
| 292 | #define DRFLAC_DLL_EXPORT | ||
| 293 | #define DRFLAC_DLL_PRIVATE static | ||
| 294 | #endif | ||
| 295 | #endif | ||
| 296 | |||
| 297 | #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) | ||
| 298 | #define DRFLAC_API DRFLAC_DLL_EXPORT | ||
| 299 | #else | ||
| 300 | #define DRFLAC_API DRFLAC_DLL_IMPORT | ||
| 301 | #endif | ||
| 302 | #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE | ||
| 303 | #else | ||
| 304 | #define DRFLAC_API extern | ||
| 305 | #define DRFLAC_PRIVATE static | ||
| 306 | #endif | ||
| 307 | #endif | ||
| 308 | /* End Decorations */ | ||
| 309 | |||
| 310 | #if defined(_MSC_VER) && _MSC_VER >= 1700 /* Visual Studio 2012 */ | ||
| 311 | #define DRFLAC_DEPRECATED __declspec(deprecated) | ||
| 312 | #elif (defined(__GNUC__) && __GNUC__ >= 4) /* GCC 4 */ | ||
| 313 | #define DRFLAC_DEPRECATED __attribute__((deprecated)) | ||
| 314 | #elif defined(__has_feature) /* Clang */ | ||
| 315 | #if __has_feature(attribute_deprecated) | ||
| 316 | #define DRFLAC_DEPRECATED __attribute__((deprecated)) | ||
| 317 | #else | ||
| 318 | #define DRFLAC_DEPRECATED | ||
| 319 | #endif | ||
| 320 | #else | ||
| 321 | #define DRFLAC_DEPRECATED | ||
| 322 | #endif | ||
| 323 | |||
| 324 | DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision); | ||
| 325 | DRFLAC_API const char* drflac_version_string(void); | ||
| 326 | |||
| 327 | /* Allocation Callbacks */ | ||
| 328 | typedef struct | ||
| 329 | { | ||
| 330 | void* pUserData; | ||
| 331 | void* (* onMalloc)(size_t sz, void* pUserData); | ||
| 332 | void* (* onRealloc)(void* p, size_t sz, void* pUserData); | ||
| 333 | void (* onFree)(void* p, void* pUserData); | ||
| 334 | } drflac_allocation_callbacks; | ||
| 335 | /* End Allocation Callbacks */ | ||
| 336 | |||
| 337 | /* | ||
| 338 | As data is read from the client it is placed into an internal buffer for fast access. This controls the size of that buffer. Larger values means more speed, | ||
| 339 | but also more memory. In my testing there is diminishing returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8. | ||
| 340 | */ | ||
| 341 | #ifndef DR_FLAC_BUFFER_SIZE | ||
| 342 | #define DR_FLAC_BUFFER_SIZE 4096 | ||
| 343 | #endif | ||
| 344 | |||
| 345 | |||
| 346 | /* Architecture Detection */ | ||
| 347 | #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) | ||
| 348 | #define DRFLAC_64BIT | ||
| 349 | #endif | ||
| 350 | |||
| 351 | #if defined(__x86_64__) || defined(_M_X64) | ||
| 352 | #define DRFLAC_X64 | ||
| 353 | #elif defined(__i386) || defined(_M_IX86) | ||
| 354 | #define DRFLAC_X86 | ||
| 355 | #elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) | ||
| 356 | #define DRFLAC_ARM | ||
| 357 | #endif | ||
| 358 | /* End Architecture Detection */ | ||
| 359 | |||
| 360 | |||
| 361 | #ifdef DRFLAC_64BIT | ||
| 362 | typedef drflac_uint64 drflac_cache_t; | ||
| 363 | #else | ||
| 364 | typedef drflac_uint32 drflac_cache_t; | ||
| 365 | #endif | ||
| 366 | |||
| 367 | /* The various metadata block types. */ | ||
| 368 | #define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 | ||
| 369 | #define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1 | ||
| 370 | #define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2 | ||
| 371 | #define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 | ||
| 372 | #define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 | ||
| 373 | #define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5 | ||
| 374 | #define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6 | ||
| 375 | #define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127 | ||
| 376 | |||
| 377 | /* The various picture types specified in the PICTURE block. */ | ||
| 378 | #define DRFLAC_PICTURE_TYPE_OTHER 0 | ||
| 379 | #define DRFLAC_PICTURE_TYPE_FILE_ICON 1 | ||
| 380 | #define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 | ||
| 381 | #define DRFLAC_PICTURE_TYPE_COVER_FRONT 3 | ||
| 382 | #define DRFLAC_PICTURE_TYPE_COVER_BACK 4 | ||
| 383 | #define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5 | ||
| 384 | #define DRFLAC_PICTURE_TYPE_MEDIA 6 | ||
| 385 | #define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7 | ||
| 386 | #define DRFLAC_PICTURE_TYPE_ARTIST 8 | ||
| 387 | #define DRFLAC_PICTURE_TYPE_CONDUCTOR 9 | ||
| 388 | #define DRFLAC_PICTURE_TYPE_BAND 10 | ||
| 389 | #define DRFLAC_PICTURE_TYPE_COMPOSER 11 | ||
| 390 | #define DRFLAC_PICTURE_TYPE_LYRICIST 12 | ||
| 391 | #define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13 | ||
| 392 | #define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14 | ||
| 393 | #define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 | ||
| 394 | #define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 | ||
| 395 | #define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 | ||
| 396 | #define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18 | ||
| 397 | #define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 | ||
| 398 | #define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 | ||
| 399 | |||
| 400 | typedef enum | ||
| 401 | { | ||
| 402 | drflac_container_native, | ||
| 403 | drflac_container_ogg, | ||
| 404 | drflac_container_unknown | ||
| 405 | } drflac_container; | ||
| 406 | |||
| 407 | typedef enum | ||
| 408 | { | ||
| 409 | drflac_seek_origin_start, | ||
| 410 | drflac_seek_origin_current | ||
| 411 | } drflac_seek_origin; | ||
| 412 | |||
| 413 | /* The order of members in this structure is important because we map this directly to the raw data within the SEEKTABLE metadata block. */ | ||
| 414 | typedef struct | ||
| 415 | { | ||
| 416 | drflac_uint64 firstPCMFrame; | ||
| 417 | drflac_uint64 flacFrameOffset; /* The offset from the first byte of the header of the first frame. */ | ||
| 418 | drflac_uint16 pcmFrameCount; | ||
| 419 | } drflac_seekpoint; | ||
| 420 | |||
| 421 | typedef struct | ||
| 422 | { | ||
| 423 | drflac_uint16 minBlockSizeInPCMFrames; | ||
| 424 | drflac_uint16 maxBlockSizeInPCMFrames; | ||
| 425 | drflac_uint32 minFrameSizeInPCMFrames; | ||
| 426 | drflac_uint32 maxFrameSizeInPCMFrames; | ||
| 427 | drflac_uint32 sampleRate; | ||
| 428 | drflac_uint8 channels; | ||
| 429 | drflac_uint8 bitsPerSample; | ||
| 430 | drflac_uint64 totalPCMFrameCount; | ||
| 431 | drflac_uint8 md5[16]; | ||
| 432 | } drflac_streaminfo; | ||
| 433 | |||
| 434 | typedef struct | ||
| 435 | { | ||
| 436 | /* | ||
| 437 | The metadata type. Use this to know how to interpret the data below. Will be set to one of the | ||
| 438 | DRFLAC_METADATA_BLOCK_TYPE_* tokens. | ||
| 439 | */ | ||
| 440 | drflac_uint32 type; | ||
| 441 | |||
| 442 | /* | ||
| 443 | A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to | ||
| 444 | not modify the contents of this buffer. Use the structures below for more meaningful and structured | ||
| 445 | information about the metadata. It's possible for this to be null. | ||
| 446 | */ | ||
| 447 | const void* pRawData; | ||
| 448 | |||
| 449 | /* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */ | ||
| 450 | drflac_uint32 rawDataSize; | ||
| 451 | |||
| 452 | union | ||
| 453 | { | ||
| 454 | drflac_streaminfo streaminfo; | ||
| 455 | |||
| 456 | struct | ||
| 457 | { | ||
| 458 | int unused; | ||
| 459 | } padding; | ||
| 460 | |||
| 461 | struct | ||
| 462 | { | ||
| 463 | drflac_uint32 id; | ||
| 464 | const void* pData; | ||
| 465 | drflac_uint32 dataSize; | ||
| 466 | } application; | ||
| 467 | |||
| 468 | struct | ||
| 469 | { | ||
| 470 | drflac_uint32 seekpointCount; | ||
| 471 | const drflac_seekpoint* pSeekpoints; | ||
| 472 | } seektable; | ||
| 473 | |||
| 474 | struct | ||
| 475 | { | ||
| 476 | drflac_uint32 vendorLength; | ||
| 477 | const char* vendor; | ||
| 478 | drflac_uint32 commentCount; | ||
| 479 | const void* pComments; | ||
| 480 | } vorbis_comment; | ||
| 481 | |||
| 482 | struct | ||
| 483 | { | ||
| 484 | char catalog[128]; | ||
| 485 | drflac_uint64 leadInSampleCount; | ||
| 486 | drflac_bool32 isCD; | ||
| 487 | drflac_uint8 trackCount; | ||
| 488 | const void* pTrackData; | ||
| 489 | } cuesheet; | ||
| 490 | |||
| 491 | struct | ||
| 492 | { | ||
| 493 | drflac_uint32 type; | ||
| 494 | drflac_uint32 mimeLength; | ||
| 495 | const char* mime; | ||
| 496 | drflac_uint32 descriptionLength; | ||
| 497 | const char* description; | ||
| 498 | drflac_uint32 width; | ||
| 499 | drflac_uint32 height; | ||
| 500 | drflac_uint32 colorDepth; | ||
| 501 | drflac_uint32 indexColorCount; | ||
| 502 | drflac_uint32 pictureDataSize; | ||
| 503 | const drflac_uint8* pPictureData; | ||
| 504 | } picture; | ||
| 505 | } data; | ||
| 506 | } drflac_metadata; | ||
| 507 | |||
| 508 | |||
| 509 | /* | ||
| 510 | Callback for when data needs to be read from the client. | ||
| 511 | |||
| 512 | |||
| 513 | Parameters | ||
| 514 | ---------- | ||
| 515 | pUserData (in) | ||
| 516 | The user data that was passed to drflac_open() and family. | ||
| 517 | |||
| 518 | pBufferOut (out) | ||
| 519 | The output buffer. | ||
| 520 | |||
| 521 | bytesToRead (in) | ||
| 522 | The number of bytes to read. | ||
| 523 | |||
| 524 | |||
| 525 | Return Value | ||
| 526 | ------------ | ||
| 527 | The number of bytes actually read. | ||
| 528 | |||
| 529 | |||
| 530 | Remarks | ||
| 531 | ------- | ||
| 532 | A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until either the entire bytesToRead is filled or | ||
| 533 | you have reached the end of the stream. | ||
| 534 | */ | ||
| 535 | typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); | ||
| 536 | |||
| 537 | /* | ||
| 538 | Callback for when data needs to be seeked. | ||
| 539 | |||
| 540 | |||
| 541 | Parameters | ||
| 542 | ---------- | ||
| 543 | pUserData (in) | ||
| 544 | The user data that was passed to drflac_open() and family. | ||
| 545 | |||
| 546 | offset (in) | ||
| 547 | The number of bytes to move, relative to the origin. Will never be negative. | ||
| 548 | |||
| 549 | origin (in) | ||
| 550 | The origin of the seek - the current position or the start of the stream. | ||
| 551 | |||
| 552 | |||
| 553 | Return Value | ||
| 554 | ------------ | ||
| 555 | Whether or not the seek was successful. | ||
| 556 | |||
| 557 | |||
| 558 | Remarks | ||
| 559 | ------- | ||
| 560 | The offset will never be negative. Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be | ||
| 561 | either drflac_seek_origin_start or drflac_seek_origin_current. | ||
| 562 | |||
| 563 | When seeking to a PCM frame using drflac_seek_to_pcm_frame(), dr_flac may call this with an offset beyond the end of the FLAC stream. This needs to be detected | ||
| 564 | and handled by returning DRFLAC_FALSE. | ||
| 565 | */ | ||
| 566 | typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); | ||
| 567 | |||
| 568 | /* | ||
| 569 | Callback for when a metadata block is read. | ||
| 570 | |||
| 571 | |||
| 572 | Parameters | ||
| 573 | ---------- | ||
| 574 | pUserData (in) | ||
| 575 | The user data that was passed to drflac_open() and family. | ||
| 576 | |||
| 577 | pMetadata (in) | ||
| 578 | A pointer to a structure containing the data of the metadata block. | ||
| 579 | |||
| 580 | |||
| 581 | Remarks | ||
| 582 | ------- | ||
| 583 | Use pMetadata->type to determine which metadata block is being handled and how to read the data. This | ||
| 584 | will be set to one of the DRFLAC_METADATA_BLOCK_TYPE_* tokens. | ||
| 585 | */ | ||
| 586 | typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); | ||
| 587 | |||
| 588 | |||
| 589 | /* Structure for internal use. Only used for decoders opened with drflac_open_memory. */ | ||
| 590 | typedef struct | ||
| 591 | { | ||
| 592 | const drflac_uint8* data; | ||
| 593 | size_t dataSize; | ||
| 594 | size_t currentReadPos; | ||
| 595 | } drflac__memory_stream; | ||
| 596 | |||
| 597 | /* Structure for internal use. Used for bit streaming. */ | ||
| 598 | typedef struct | ||
| 599 | { | ||
| 600 | /* The function to call when more data needs to be read. */ | ||
| 601 | drflac_read_proc onRead; | ||
| 602 | |||
| 603 | /* The function to call when the current read position needs to be moved. */ | ||
| 604 | drflac_seek_proc onSeek; | ||
| 605 | |||
| 606 | /* The user data to pass around to onRead and onSeek. */ | ||
| 607 | void* pUserData; | ||
| 608 | |||
| 609 | |||
| 610 | /* | ||
| 611 | The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the | ||
| 612 | stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether | ||
| 613 | or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t). | ||
| 614 | */ | ||
| 615 | size_t unalignedByteCount; | ||
| 616 | |||
| 617 | /* The content of the unaligned bytes. */ | ||
| 618 | drflac_cache_t unalignedCache; | ||
| 619 | |||
| 620 | /* The index of the next valid cache line in the "L2" cache. */ | ||
| 621 | drflac_uint32 nextL2Line; | ||
| 622 | |||
| 623 | /* The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining. */ | ||
| 624 | drflac_uint32 consumedBits; | ||
| 625 | |||
| 626 | /* | ||
| 627 | The cached data which was most recently read from the client. There are two levels of cache. Data flows as such: | ||
| 628 | Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions. | ||
| 629 | */ | ||
| 630 | drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; | ||
| 631 | drflac_cache_t cache; | ||
| 632 | |||
| 633 | /* | ||
| 634 | CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this | ||
| 635 | is reset to 0 at the beginning of each frame. | ||
| 636 | */ | ||
| 637 | drflac_uint16 crc16; | ||
| 638 | drflac_cache_t crc16Cache; /* A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded. */ | ||
| 639 | drflac_uint32 crc16CacheIgnoredBytes; /* The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache. */ | ||
| 640 | } drflac_bs; | ||
| 641 | |||
| 642 | typedef struct | ||
| 643 | { | ||
| 644 | /* The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC. */ | ||
| 645 | drflac_uint8 subframeType; | ||
| 646 | |||
| 647 | /* The number of wasted bits per sample as specified by the sub-frame header. */ | ||
| 648 | drflac_uint8 wastedBitsPerSample; | ||
| 649 | |||
| 650 | /* The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC. */ | ||
| 651 | drflac_uint8 lpcOrder; | ||
| 652 | |||
| 653 | /* A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. */ | ||
| 654 | drflac_int32* pSamplesS32; | ||
| 655 | } drflac_subframe; | ||
| 656 | |||
| 657 | typedef struct | ||
| 658 | { | ||
| 659 | /* | ||
| 660 | If the stream uses variable block sizes, this will be set to the index of the first PCM frame. If fixed block sizes are used, this will | ||
| 661 | always be set to 0. This is 64-bit because the decoded PCM frame number will be 36 bits. | ||
| 662 | */ | ||
| 663 | drflac_uint64 pcmFrameNumber; | ||
| 664 | |||
| 665 | /* | ||
| 666 | If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0. This | ||
| 667 | is 32-bit because in fixed block sizes, the maximum frame number will be 31 bits. | ||
| 668 | */ | ||
| 669 | drflac_uint32 flacFrameNumber; | ||
| 670 | |||
| 671 | /* The sample rate of this frame. */ | ||
| 672 | drflac_uint32 sampleRate; | ||
| 673 | |||
| 674 | /* The number of PCM frames in each sub-frame within this frame. */ | ||
| 675 | drflac_uint16 blockSizeInPCMFrames; | ||
| 676 | |||
| 677 | /* | ||
| 678 | The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this | ||
| 679 | will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE. | ||
| 680 | */ | ||
| 681 | drflac_uint8 channelAssignment; | ||
| 682 | |||
| 683 | /* The number of bits per sample within this frame. */ | ||
| 684 | drflac_uint8 bitsPerSample; | ||
| 685 | |||
| 686 | /* The frame's CRC. */ | ||
| 687 | drflac_uint8 crc8; | ||
| 688 | } drflac_frame_header; | ||
| 689 | |||
| 690 | typedef struct | ||
| 691 | { | ||
| 692 | /* The header. */ | ||
| 693 | drflac_frame_header header; | ||
| 694 | |||
| 695 | /* | ||
| 696 | The number of PCM frames left to be read in this FLAC frame. This is initially set to the block size. As PCM frames are read, | ||
| 697 | this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame. | ||
| 698 | */ | ||
| 699 | drflac_uint32 pcmFramesRemaining; | ||
| 700 | |||
| 701 | /* The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels. */ | ||
| 702 | drflac_subframe subframes[8]; | ||
| 703 | } drflac_frame; | ||
| 704 | |||
| 705 | typedef struct | ||
| 706 | { | ||
| 707 | /* The function to call when a metadata block is read. */ | ||
| 708 | drflac_meta_proc onMeta; | ||
| 709 | |||
| 710 | /* The user data posted to the metadata callback function. */ | ||
| 711 | void* pUserDataMD; | ||
| 712 | |||
| 713 | /* Memory allocation callbacks. */ | ||
| 714 | drflac_allocation_callbacks allocationCallbacks; | ||
| 715 | |||
| 716 | |||
| 717 | /* The sample rate. Will be set to something like 44100. */ | ||
| 718 | drflac_uint32 sampleRate; | ||
| 719 | |||
| 720 | /* | ||
| 721 | The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the | ||
| 722 | value specified in the STREAMINFO block. | ||
| 723 | */ | ||
| 724 | drflac_uint8 channels; | ||
| 725 | |||
| 726 | /* The bits per sample. Will be set to something like 16, 24, etc. */ | ||
| 727 | drflac_uint8 bitsPerSample; | ||
| 728 | |||
| 729 | /* The maximum block size, in samples. This number represents the number of samples in each channel (not combined). */ | ||
| 730 | drflac_uint16 maxBlockSizeInPCMFrames; | ||
| 731 | |||
| 732 | /* | ||
| 733 | The total number of PCM Frames making up the stream. Can be 0 in which case it's still a valid stream, but just means | ||
| 734 | the total PCM frame count is unknown. Likely the case with streams like internet radio. | ||
| 735 | */ | ||
| 736 | drflac_uint64 totalPCMFrameCount; | ||
| 737 | |||
| 738 | |||
| 739 | /* The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. */ | ||
| 740 | drflac_container container; | ||
| 741 | |||
| 742 | /* The number of seekpoints in the seektable. */ | ||
| 743 | drflac_uint32 seekpointCount; | ||
| 744 | |||
| 745 | |||
| 746 | /* Information about the frame the decoder is currently sitting on. */ | ||
| 747 | drflac_frame currentFLACFrame; | ||
| 748 | |||
| 749 | |||
| 750 | /* The index of the PCM frame the decoder is currently sitting on. This is only used for seeking. */ | ||
| 751 | drflac_uint64 currentPCMFrame; | ||
| 752 | |||
| 753 | /* The position of the first FLAC frame in the stream. This is only ever used for seeking. */ | ||
| 754 | drflac_uint64 firstFLACFramePosInBytes; | ||
| 755 | |||
| 756 | |||
| 757 | /* A hack to avoid a malloc() when opening a decoder with drflac_open_memory(). */ | ||
| 758 | drflac__memory_stream memoryStream; | ||
| 759 | |||
| 760 | |||
| 761 | /* A pointer to the decoded sample data. This is an offset of pExtraData. */ | ||
| 762 | drflac_int32* pDecodedSamples; | ||
| 763 | |||
| 764 | /* A pointer to the seek table. This is an offset of pExtraData, or NULL if there is no seek table. */ | ||
| 765 | drflac_seekpoint* pSeekpoints; | ||
| 766 | |||
| 767 | /* Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData. */ | ||
| 768 | void* _oggbs; | ||
| 769 | |||
| 770 | /* Internal use only. Used for profiling and testing different seeking modes. */ | ||
| 771 | drflac_bool32 _noSeekTableSeek : 1; | ||
| 772 | drflac_bool32 _noBinarySearchSeek : 1; | ||
| 773 | drflac_bool32 _noBruteForceSeek : 1; | ||
| 774 | |||
| 775 | /* The bit streamer. The raw FLAC data is fed through this object. */ | ||
| 776 | drflac_bs bs; | ||
| 777 | |||
| 778 | /* Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs. */ | ||
| 779 | drflac_uint8 pExtraData[1]; | ||
| 780 | } drflac; | ||
| 781 | |||
| 782 | |||
| 783 | /* | ||
| 784 | Opens a FLAC decoder. | ||
| 785 | |||
| 786 | |||
| 787 | Parameters | ||
| 788 | ---------- | ||
| 789 | onRead (in) | ||
| 790 | The function to call when data needs to be read from the client. | ||
| 791 | |||
| 792 | onSeek (in) | ||
| 793 | The function to call when the read position of the client data needs to move. | ||
| 794 | |||
| 795 | pUserData (in, optional) | ||
| 796 | A pointer to application defined data that will be passed to onRead and onSeek. | ||
| 797 | |||
| 798 | pAllocationCallbacks (in, optional) | ||
| 799 | A pointer to application defined callbacks for managing memory allocations. | ||
| 800 | |||
| 801 | |||
| 802 | Return Value | ||
| 803 | ------------ | ||
| 804 | Returns a pointer to an object representing the decoder. | ||
| 805 | |||
| 806 | |||
| 807 | Remarks | ||
| 808 | ------- | ||
| 809 | Close the decoder with `drflac_close()`. | ||
| 810 | |||
| 811 | `pAllocationCallbacks` can be NULL in which case it will use `DRFLAC_MALLOC`, `DRFLAC_REALLOC` and `DRFLAC_FREE`. | ||
| 812 | |||
| 813 | This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated FLAC, both of which should work seamlessly | ||
| 814 | without any manual intervention. Ogg encapsulation also works with multiplexed streams which basically means it can play FLAC encoded audio tracks in videos. | ||
| 815 | |||
| 816 | This is the lowest level function for opening a FLAC stream. You can also use `drflac_open_file()` and `drflac_open_memory()` to open the stream from a file or | ||
| 817 | from a block of memory respectively. | ||
| 818 | |||
| 819 | The STREAMINFO block must be present for this to succeed. Use `drflac_open_relaxed()` to open a FLAC stream where the header may not be present. | ||
| 820 | |||
| 821 | Use `drflac_open_with_metadata()` if you need access to metadata. | ||
| 822 | |||
| 823 | |||
| 824 | Seek Also | ||
| 825 | --------- | ||
| 826 | drflac_open_file() | ||
| 827 | drflac_open_memory() | ||
| 828 | drflac_open_with_metadata() | ||
| 829 | drflac_close() | ||
| 830 | */ | ||
| 831 | DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 832 | |||
| 833 | /* | ||
| 834 | Opens a FLAC stream with relaxed validation of the header block. | ||
| 835 | |||
| 836 | |||
| 837 | Parameters | ||
| 838 | ---------- | ||
| 839 | onRead (in) | ||
| 840 | The function to call when data needs to be read from the client. | ||
| 841 | |||
| 842 | onSeek (in) | ||
| 843 | The function to call when the read position of the client data needs to move. | ||
| 844 | |||
| 845 | container (in) | ||
| 846 | Whether or not the FLAC stream is encapsulated using standard FLAC encapsulation or Ogg encapsulation. | ||
| 847 | |||
| 848 | pUserData (in, optional) | ||
| 849 | A pointer to application defined data that will be passed to onRead and onSeek. | ||
| 850 | |||
| 851 | pAllocationCallbacks (in, optional) | ||
| 852 | A pointer to application defined callbacks for managing memory allocations. | ||
| 853 | |||
| 854 | |||
| 855 | Return Value | ||
| 856 | ------------ | ||
| 857 | A pointer to an object representing the decoder. | ||
| 858 | |||
| 859 | |||
| 860 | Remarks | ||
| 861 | ------- | ||
| 862 | The same as drflac_open(), except attempts to open the stream even when a header block is not present. | ||
| 863 | |||
| 864 | Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do not set this to `drflac_container_unknown` | ||
| 865 | as that is for internal use only. | ||
| 866 | |||
| 867 | Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never found it will continue forever. To abort, | ||
| 868 | force your `onRead` callback to return 0, which dr_flac will use as an indicator that the end of the stream was found. | ||
| 869 | |||
| 870 | Use `drflac_open_with_metadata_relaxed()` if you need access to metadata. | ||
| 871 | */ | ||
| 872 | DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 873 | |||
| 874 | /* | ||
| 875 | Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.). | ||
| 876 | |||
| 877 | |||
| 878 | Parameters | ||
| 879 | ---------- | ||
| 880 | onRead (in) | ||
| 881 | The function to call when data needs to be read from the client. | ||
| 882 | |||
| 883 | onSeek (in) | ||
| 884 | The function to call when the read position of the client data needs to move. | ||
| 885 | |||
| 886 | onMeta (in) | ||
| 887 | The function to call for every metadata block. | ||
| 888 | |||
| 889 | pUserData (in, optional) | ||
| 890 | A pointer to application defined data that will be passed to onRead, onSeek and onMeta. | ||
| 891 | |||
| 892 | pAllocationCallbacks (in, optional) | ||
| 893 | A pointer to application defined callbacks for managing memory allocations. | ||
| 894 | |||
| 895 | |||
| 896 | Return Value | ||
| 897 | ------------ | ||
| 898 | A pointer to an object representing the decoder. | ||
| 899 | |||
| 900 | |||
| 901 | Remarks | ||
| 902 | ------- | ||
| 903 | Close the decoder with `drflac_close()`. | ||
| 904 | |||
| 905 | `pAllocationCallbacks` can be NULL in which case it will use `DRFLAC_MALLOC`, `DRFLAC_REALLOC` and `DRFLAC_FREE`. | ||
| 906 | |||
| 907 | This is slower than `drflac_open()`, so avoid this one if you don't need metadata. Internally, this will allocate and free memory on the heap for every | ||
| 908 | metadata block except for STREAMINFO and PADDING blocks. | ||
| 909 | |||
| 910 | The caller is notified of the metadata via the `onMeta` callback. All metadata blocks will be handled before the function returns. This callback takes a | ||
| 911 | pointer to a `drflac_metadata` object which is a union containing the data of all relevant metadata blocks. Use the `type` member to discriminate against | ||
| 912 | the different metadata types. | ||
| 913 | |||
| 914 | The STREAMINFO block must be present for this to succeed. Use `drflac_open_with_metadata_relaxed()` to open a FLAC stream where the header may not be present. | ||
| 915 | |||
| 916 | Note that this will behave inconsistently with `drflac_open()` if the stream is an Ogg encapsulated stream and a metadata block is corrupted. This is due to | ||
| 917 | the way the Ogg stream recovers from corrupted pages. When `drflac_open_with_metadata()` is being used, the open routine will try to read the contents of the | ||
| 918 | metadata block, whereas `drflac_open()` will simply seek past it (for the sake of efficiency). This inconsistency can result in different samples being | ||
| 919 | returned depending on whether or not the stream is being opened with metadata. | ||
| 920 | |||
| 921 | |||
| 922 | Seek Also | ||
| 923 | --------- | ||
| 924 | drflac_open_file_with_metadata() | ||
| 925 | drflac_open_memory_with_metadata() | ||
| 926 | drflac_open() | ||
| 927 | drflac_close() | ||
| 928 | */ | ||
| 929 | DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 930 | |||
| 931 | /* | ||
| 932 | The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present. | ||
| 933 | |||
| 934 | See Also | ||
| 935 | -------- | ||
| 936 | drflac_open_with_metadata() | ||
| 937 | drflac_open_relaxed() | ||
| 938 | */ | ||
| 939 | DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 940 | |||
| 941 | /* | ||
| 942 | Closes the given FLAC decoder. | ||
| 943 | |||
| 944 | |||
| 945 | Parameters | ||
| 946 | ---------- | ||
| 947 | pFlac (in) | ||
| 948 | The decoder to close. | ||
| 949 | |||
| 950 | |||
| 951 | Remarks | ||
| 952 | ------- | ||
| 953 | This will destroy the decoder object. | ||
| 954 | |||
| 955 | |||
| 956 | See Also | ||
| 957 | -------- | ||
| 958 | drflac_open() | ||
| 959 | drflac_open_with_metadata() | ||
| 960 | drflac_open_file() | ||
| 961 | drflac_open_file_w() | ||
| 962 | drflac_open_file_with_metadata() | ||
| 963 | drflac_open_file_with_metadata_w() | ||
| 964 | drflac_open_memory() | ||
| 965 | drflac_open_memory_with_metadata() | ||
| 966 | */ | ||
| 967 | DRFLAC_API void drflac_close(drflac* pFlac); | ||
| 968 | |||
| 969 | |||
| 970 | /* | ||
| 971 | Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM. | ||
| 972 | |||
| 973 | |||
| 974 | Parameters | ||
| 975 | ---------- | ||
| 976 | pFlac (in) | ||
| 977 | The decoder. | ||
| 978 | |||
| 979 | framesToRead (in) | ||
| 980 | The number of PCM frames to read. | ||
| 981 | |||
| 982 | pBufferOut (out, optional) | ||
| 983 | A pointer to the buffer that will receive the decoded samples. | ||
| 984 | |||
| 985 | |||
| 986 | Return Value | ||
| 987 | ------------ | ||
| 988 | Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. | ||
| 989 | |||
| 990 | |||
| 991 | Remarks | ||
| 992 | ------- | ||
| 993 | pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. | ||
| 994 | */ | ||
| 995 | DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut); | ||
| 996 | |||
| 997 | |||
| 998 | /* | ||
| 999 | Reads sample data from the given FLAC decoder, output as interleaved signed 16-bit PCM. | ||
| 1000 | |||
| 1001 | |||
| 1002 | Parameters | ||
| 1003 | ---------- | ||
| 1004 | pFlac (in) | ||
| 1005 | The decoder. | ||
| 1006 | |||
| 1007 | framesToRead (in) | ||
| 1008 | The number of PCM frames to read. | ||
| 1009 | |||
| 1010 | pBufferOut (out, optional) | ||
| 1011 | A pointer to the buffer that will receive the decoded samples. | ||
| 1012 | |||
| 1013 | |||
| 1014 | Return Value | ||
| 1015 | ------------ | ||
| 1016 | Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. | ||
| 1017 | |||
| 1018 | |||
| 1019 | Remarks | ||
| 1020 | ------- | ||
| 1021 | pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. | ||
| 1022 | |||
| 1023 | Note that this is lossy for streams where the bits per sample is larger than 16. | ||
| 1024 | */ | ||
| 1025 | DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut); | ||
| 1026 | |||
| 1027 | /* | ||
| 1028 | Reads sample data from the given FLAC decoder, output as interleaved 32-bit floating point PCM. | ||
| 1029 | |||
| 1030 | |||
| 1031 | Parameters | ||
| 1032 | ---------- | ||
| 1033 | pFlac (in) | ||
| 1034 | The decoder. | ||
| 1035 | |||
| 1036 | framesToRead (in) | ||
| 1037 | The number of PCM frames to read. | ||
| 1038 | |||
| 1039 | pBufferOut (out, optional) | ||
| 1040 | A pointer to the buffer that will receive the decoded samples. | ||
| 1041 | |||
| 1042 | |||
| 1043 | Return Value | ||
| 1044 | ------------ | ||
| 1045 | Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. | ||
| 1046 | |||
| 1047 | |||
| 1048 | Remarks | ||
| 1049 | ------- | ||
| 1050 | pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. | ||
| 1051 | |||
| 1052 | Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly represent every possible number. | ||
| 1053 | */ | ||
| 1054 | DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut); | ||
| 1055 | |||
| 1056 | /* | ||
| 1057 | Seeks to the PCM frame at the given index. | ||
| 1058 | |||
| 1059 | |||
| 1060 | Parameters | ||
| 1061 | ---------- | ||
| 1062 | pFlac (in) | ||
| 1063 | The decoder. | ||
| 1064 | |||
| 1065 | pcmFrameIndex (in) | ||
| 1066 | The index of the PCM frame to seek to. See notes below. | ||
| 1067 | |||
| 1068 | |||
| 1069 | Return Value | ||
| 1070 | ------------- | ||
| 1071 | `DRFLAC_TRUE` if successful; `DRFLAC_FALSE` otherwise. | ||
| 1072 | */ | ||
| 1073 | DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex); | ||
| 1074 | |||
| 1075 | |||
| 1076 | |||
| 1077 | #ifndef DR_FLAC_NO_STDIO | ||
| 1078 | /* | ||
| 1079 | Opens a FLAC decoder from the file at the given path. | ||
| 1080 | |||
| 1081 | |||
| 1082 | Parameters | ||
| 1083 | ---------- | ||
| 1084 | pFileName (in) | ||
| 1085 | The path of the file to open, either absolute or relative to the current directory. | ||
| 1086 | |||
| 1087 | pAllocationCallbacks (in, optional) | ||
| 1088 | A pointer to application defined callbacks for managing memory allocations. | ||
| 1089 | |||
| 1090 | |||
| 1091 | Return Value | ||
| 1092 | ------------ | ||
| 1093 | A pointer to an object representing the decoder. | ||
| 1094 | |||
| 1095 | |||
| 1096 | Remarks | ||
| 1097 | ------- | ||
| 1098 | Close the decoder with drflac_close(). | ||
| 1099 | |||
| 1100 | |||
| 1101 | Remarks | ||
| 1102 | ------- | ||
| 1103 | This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the number of files a process can have open | ||
| 1104 | at any given time, so keep this mind if you have many decoders open at the same time. | ||
| 1105 | |||
| 1106 | |||
| 1107 | See Also | ||
| 1108 | -------- | ||
| 1109 | drflac_open_file_with_metadata() | ||
| 1110 | drflac_open() | ||
| 1111 | drflac_close() | ||
| 1112 | */ | ||
| 1113 | DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1114 | DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1115 | |||
| 1116 | /* | ||
| 1117 | Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.) | ||
| 1118 | |||
| 1119 | |||
| 1120 | Parameters | ||
| 1121 | ---------- | ||
| 1122 | pFileName (in) | ||
| 1123 | The path of the file to open, either absolute or relative to the current directory. | ||
| 1124 | |||
| 1125 | pAllocationCallbacks (in, optional) | ||
| 1126 | A pointer to application defined callbacks for managing memory allocations. | ||
| 1127 | |||
| 1128 | onMeta (in) | ||
| 1129 | The callback to fire for each metadata block. | ||
| 1130 | |||
| 1131 | pUserData (in) | ||
| 1132 | A pointer to the user data to pass to the metadata callback. | ||
| 1133 | |||
| 1134 | pAllocationCallbacks (in) | ||
| 1135 | A pointer to application defined callbacks for managing memory allocations. | ||
| 1136 | |||
| 1137 | |||
| 1138 | Remarks | ||
| 1139 | ------- | ||
| 1140 | Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. | ||
| 1141 | |||
| 1142 | |||
| 1143 | See Also | ||
| 1144 | -------- | ||
| 1145 | drflac_open_with_metadata() | ||
| 1146 | drflac_open() | ||
| 1147 | drflac_close() | ||
| 1148 | */ | ||
| 1149 | DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1150 | DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1151 | #endif | ||
| 1152 | |||
| 1153 | /* | ||
| 1154 | Opens a FLAC decoder from a pre-allocated block of memory | ||
| 1155 | |||
| 1156 | |||
| 1157 | Parameters | ||
| 1158 | ---------- | ||
| 1159 | pData (in) | ||
| 1160 | A pointer to the raw encoded FLAC data. | ||
| 1161 | |||
| 1162 | dataSize (in) | ||
| 1163 | The size in bytes of `data`. | ||
| 1164 | |||
| 1165 | pAllocationCallbacks (in) | ||
| 1166 | A pointer to application defined callbacks for managing memory allocations. | ||
| 1167 | |||
| 1168 | |||
| 1169 | Return Value | ||
| 1170 | ------------ | ||
| 1171 | A pointer to an object representing the decoder. | ||
| 1172 | |||
| 1173 | |||
| 1174 | Remarks | ||
| 1175 | ------- | ||
| 1176 | This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for the lifetime of the decoder. | ||
| 1177 | |||
| 1178 | |||
| 1179 | See Also | ||
| 1180 | -------- | ||
| 1181 | drflac_open() | ||
| 1182 | drflac_close() | ||
| 1183 | */ | ||
| 1184 | DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1185 | |||
| 1186 | /* | ||
| 1187 | Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.) | ||
| 1188 | |||
| 1189 | |||
| 1190 | Parameters | ||
| 1191 | ---------- | ||
| 1192 | pData (in) | ||
| 1193 | A pointer to the raw encoded FLAC data. | ||
| 1194 | |||
| 1195 | dataSize (in) | ||
| 1196 | The size in bytes of `data`. | ||
| 1197 | |||
| 1198 | onMeta (in) | ||
| 1199 | The callback to fire for each metadata block. | ||
| 1200 | |||
| 1201 | pUserData (in) | ||
| 1202 | A pointer to the user data to pass to the metadata callback. | ||
| 1203 | |||
| 1204 | pAllocationCallbacks (in) | ||
| 1205 | A pointer to application defined callbacks for managing memory allocations. | ||
| 1206 | |||
| 1207 | |||
| 1208 | Remarks | ||
| 1209 | ------- | ||
| 1210 | Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. | ||
| 1211 | |||
| 1212 | |||
| 1213 | See Also | ||
| 1214 | ------- | ||
| 1215 | drflac_open_with_metadata() | ||
| 1216 | drflac_open() | ||
| 1217 | drflac_close() | ||
| 1218 | */ | ||
| 1219 | DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1220 | |||
| 1221 | |||
| 1222 | |||
| 1223 | /* High Level APIs */ | ||
| 1224 | |||
| 1225 | /* | ||
| 1226 | Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a | ||
| 1227 | pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with drflac_free(). | ||
| 1228 | |||
| 1229 | You can pass in custom memory allocation callbacks via the pAllocationCallbacks parameter. This can be NULL in which | ||
| 1230 | case it will use DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE. | ||
| 1231 | |||
| 1232 | Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously | ||
| 1233 | read samples into a dynamically sized buffer on the heap until no samples are left. | ||
| 1234 | |||
| 1235 | Do not call this function on a broadcast type of stream (like internet radio streams and whatnot). | ||
| 1236 | */ | ||
| 1237 | DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1238 | |||
| 1239 | /* Same as drflac_open_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ | ||
| 1240 | DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1241 | |||
| 1242 | /* Same as drflac_open_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ | ||
| 1243 | DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1244 | |||
| 1245 | #ifndef DR_FLAC_NO_STDIO | ||
| 1246 | /* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a file. */ | ||
| 1247 | DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1248 | |||
| 1249 | /* Same as drflac_open_file_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ | ||
| 1250 | DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1251 | |||
| 1252 | /* Same as drflac_open_file_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ | ||
| 1253 | DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1254 | #endif | ||
| 1255 | |||
| 1256 | /* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a block of memory. */ | ||
| 1257 | DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1258 | |||
| 1259 | /* Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ | ||
| 1260 | DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1261 | |||
| 1262 | /* Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ | ||
| 1263 | DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1264 | |||
| 1265 | /* | ||
| 1266 | Frees memory that was allocated internally by dr_flac. | ||
| 1267 | |||
| 1268 | Set pAllocationCallbacks to the same object that was passed to drflac_open_*_and_read_pcm_frames_*(). If you originally passed in NULL, pass in NULL for this. | ||
| 1269 | */ | ||
| 1270 | DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks); | ||
| 1271 | |||
| 1272 | |||
| 1273 | /* Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block. */ | ||
| 1274 | typedef struct | ||
| 1275 | { | ||
| 1276 | drflac_uint32 countRemaining; | ||
| 1277 | const char* pRunningData; | ||
| 1278 | } drflac_vorbis_comment_iterator; | ||
| 1279 | |||
| 1280 | /* | ||
| 1281 | Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT | ||
| 1282 | metadata block. | ||
| 1283 | */ | ||
| 1284 | DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments); | ||
| 1285 | |||
| 1286 | /* | ||
| 1287 | Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The | ||
| 1288 | returned string is NOT null terminated. | ||
| 1289 | */ | ||
| 1290 | DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); | ||
| 1291 | |||
| 1292 | |||
| 1293 | /* Structure representing an iterator for cuesheet tracks in a CUESHEET metadata block. */ | ||
| 1294 | typedef struct | ||
| 1295 | { | ||
| 1296 | drflac_uint32 countRemaining; | ||
| 1297 | const char* pRunningData; | ||
| 1298 | } drflac_cuesheet_track_iterator; | ||
| 1299 | |||
| 1300 | /* The order of members here is important because we map this directly to the raw data within the CUESHEET metadata block. */ | ||
| 1301 | typedef struct | ||
| 1302 | { | ||
| 1303 | drflac_uint64 offset; | ||
| 1304 | drflac_uint8 index; | ||
| 1305 | drflac_uint8 reserved[3]; | ||
| 1306 | } drflac_cuesheet_track_index; | ||
| 1307 | |||
| 1308 | typedef struct | ||
| 1309 | { | ||
| 1310 | drflac_uint64 offset; | ||
| 1311 | drflac_uint8 trackNumber; | ||
| 1312 | char ISRC[12]; | ||
| 1313 | drflac_bool8 isAudio; | ||
| 1314 | drflac_bool8 preEmphasis; | ||
| 1315 | drflac_uint8 indexCount; | ||
| 1316 | const drflac_cuesheet_track_index* pIndexPoints; | ||
| 1317 | } drflac_cuesheet_track; | ||
| 1318 | |||
| 1319 | /* | ||
| 1320 | Initializes a cuesheet track iterator. This can be used for iterating over the cuesheet tracks in a CUESHEET metadata | ||
| 1321 | block. | ||
| 1322 | */ | ||
| 1323 | DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData); | ||
| 1324 | |||
| 1325 | /* Goes to the next cuesheet track in the given iterator. If DRFLAC_FALSE is returned it means there are no more comments. */ | ||
| 1326 | DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack); | ||
| 1327 | |||
| 1328 | |||
| 1329 | #ifdef __cplusplus | ||
| 1330 | } | ||
| 1331 | #endif | ||
| 1332 | #endif /* dr_flac_h */ | ||
| 1333 | |||
| 1334 | |||
| 1335 | /************************************************************************************************************************************************************ | ||
| 1336 | ************************************************************************************************************************************************************ | ||
| 1337 | |||
| 1338 | IMPLEMENTATION | ||
| 1339 | |||
| 1340 | ************************************************************************************************************************************************************ | ||
| 1341 | ************************************************************************************************************************************************************/ | ||
| 1342 | #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) | ||
| 1343 | #ifndef dr_flac_c | ||
| 1344 | #define dr_flac_c | ||
| 1345 | |||
| 1346 | /* Disable some annoying warnings. */ | ||
| 1347 | #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) | ||
| 1348 | #pragma GCC diagnostic push | ||
| 1349 | #if __GNUC__ >= 7 | ||
| 1350 | #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" | ||
| 1351 | #endif | ||
| 1352 | #endif | ||
| 1353 | |||
| 1354 | #ifdef __linux__ | ||
| 1355 | #ifndef _BSD_SOURCE | ||
| 1356 | #define _BSD_SOURCE | ||
| 1357 | #endif | ||
| 1358 | #ifndef _DEFAULT_SOURCE | ||
| 1359 | #define _DEFAULT_SOURCE | ||
| 1360 | #endif | ||
| 1361 | #ifndef __USE_BSD | ||
| 1362 | #define __USE_BSD | ||
| 1363 | #endif | ||
| 1364 | #include <endian.h> | ||
| 1365 | #endif | ||
| 1366 | |||
| 1367 | #include <stdlib.h> | ||
| 1368 | #include <string.h> | ||
| 1369 | |||
| 1370 | /* Inline */ | ||
| 1371 | #ifdef _MSC_VER | ||
| 1372 | #define DRFLAC_INLINE __forceinline | ||
| 1373 | #elif defined(__GNUC__) | ||
| 1374 | /* | ||
| 1375 | I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when | ||
| 1376 | the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some | ||
| 1377 | case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the | ||
| 1378 | command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue | ||
| 1379 | I am using "__inline__" only when we're compiling in strict ANSI mode. | ||
| 1380 | */ | ||
| 1381 | #if defined(__STRICT_ANSI__) | ||
| 1382 | #define DRFLAC_GNUC_INLINE_HINT __inline__ | ||
| 1383 | #else | ||
| 1384 | #define DRFLAC_GNUC_INLINE_HINT inline | ||
| 1385 | #endif | ||
| 1386 | |||
| 1387 | #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) | ||
| 1388 | #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline)) | ||
| 1389 | #else | ||
| 1390 | #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT | ||
| 1391 | #endif | ||
| 1392 | #elif defined(__WATCOMC__) | ||
| 1393 | #define DRFLAC_INLINE __inline | ||
| 1394 | #else | ||
| 1395 | #define DRFLAC_INLINE | ||
| 1396 | #endif | ||
| 1397 | /* End Inline */ | ||
| 1398 | |||
| 1399 | /* | ||
| 1400 | Intrinsics Support | ||
| 1401 | |||
| 1402 | There's a bug in GCC 4.2.x which results in an incorrect compilation error when using _mm_slli_epi32() where it complains with | ||
| 1403 | |||
| 1404 | "error: shift must be an immediate" | ||
| 1405 | |||
| 1406 | Unfortuantely dr_flac depends on this for a few things so we're just going to disable SSE on GCC 4.2 and below. | ||
| 1407 | */ | ||
| 1408 | #if !defined(DR_FLAC_NO_SIMD) | ||
| 1409 | #if defined(DRFLAC_X64) || defined(DRFLAC_X86) | ||
| 1410 | #if defined(_MSC_VER) && !defined(__clang__) | ||
| 1411 | /* MSVC. */ | ||
| 1412 | #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2) /* 2005 */ | ||
| 1413 | #define DRFLAC_SUPPORT_SSE2 | ||
| 1414 | #endif | ||
| 1415 | #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41) /* 2010 */ | ||
| 1416 | #define DRFLAC_SUPPORT_SSE41 | ||
| 1417 | #endif | ||
| 1418 | #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) | ||
| 1419 | /* Assume GNUC-style. */ | ||
| 1420 | #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2) | ||
| 1421 | #define DRFLAC_SUPPORT_SSE2 | ||
| 1422 | #endif | ||
| 1423 | #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41) | ||
| 1424 | #define DRFLAC_SUPPORT_SSE41 | ||
| 1425 | #endif | ||
| 1426 | #endif | ||
| 1427 | |||
| 1428 | /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ | ||
| 1429 | #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) | ||
| 1430 | #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include(<emmintrin.h>) | ||
| 1431 | #define DRFLAC_SUPPORT_SSE2 | ||
| 1432 | #endif | ||
| 1433 | #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include(<smmintrin.h>) | ||
| 1434 | #define DRFLAC_SUPPORT_SSE41 | ||
| 1435 | #endif | ||
| 1436 | #endif | ||
| 1437 | |||
| 1438 | #if defined(DRFLAC_SUPPORT_SSE41) | ||
| 1439 | #include <smmintrin.h> | ||
| 1440 | #elif defined(DRFLAC_SUPPORT_SSE2) | ||
| 1441 | #include <emmintrin.h> | ||
| 1442 | #endif | ||
| 1443 | #endif | ||
| 1444 | |||
| 1445 | #if defined(DRFLAC_ARM) | ||
| 1446 | #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) | ||
| 1447 | #define DRFLAC_SUPPORT_NEON | ||
| 1448 | #include <arm_neon.h> | ||
| 1449 | #endif | ||
| 1450 | #endif | ||
| 1451 | #endif | ||
| 1452 | |||
| 1453 | /* Compile-time CPU feature support. */ | ||
| 1454 | #if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) | ||
| 1455 | #if defined(_MSC_VER) && !defined(__clang__) | ||
| 1456 | #if _MSC_VER >= 1400 | ||
| 1457 | #include <intrin.h> | ||
| 1458 | static void drflac__cpuid(int info[4], int fid) | ||
| 1459 | { | ||
| 1460 | __cpuid(info, fid); | ||
| 1461 | } | ||
| 1462 | #else | ||
| 1463 | #define DRFLAC_NO_CPUID | ||
| 1464 | #endif | ||
| 1465 | #else | ||
| 1466 | #if defined(__GNUC__) || defined(__clang__) | ||
| 1467 | static void drflac__cpuid(int info[4], int fid) | ||
| 1468 | { | ||
| 1469 | /* | ||
| 1470 | It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the | ||
| 1471 | specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for | ||
| 1472 | supporting different assembly dialects. | ||
| 1473 | |||
| 1474 | What's basically happening is that we're saving and restoring the ebx register manually. | ||
| 1475 | */ | ||
| 1476 | #if defined(DRFLAC_X86) && defined(__PIC__) | ||
| 1477 | __asm__ __volatile__ ( | ||
| 1478 | "xchg{l} {%%}ebx, %k1;" | ||
| 1479 | "cpuid;" | ||
| 1480 | "xchg{l} {%%}ebx, %k1;" | ||
| 1481 | : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) | ||
| 1482 | ); | ||
| 1483 | #else | ||
| 1484 | __asm__ __volatile__ ( | ||
| 1485 | "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) | ||
| 1486 | ); | ||
| 1487 | #endif | ||
| 1488 | } | ||
| 1489 | #else | ||
| 1490 | #define DRFLAC_NO_CPUID | ||
| 1491 | #endif | ||
| 1492 | #endif | ||
| 1493 | #else | ||
| 1494 | #define DRFLAC_NO_CPUID | ||
| 1495 | #endif | ||
| 1496 | |||
| 1497 | static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void) | ||
| 1498 | { | ||
| 1499 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 1500 | #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2) | ||
| 1501 | #if defined(DRFLAC_X64) | ||
| 1502 | return DRFLAC_TRUE; /* 64-bit targets always support SSE2. */ | ||
| 1503 | #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) | ||
| 1504 | return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ | ||
| 1505 | #else | ||
| 1506 | #if defined(DRFLAC_NO_CPUID) | ||
| 1507 | return DRFLAC_FALSE; | ||
| 1508 | #else | ||
| 1509 | int info[4]; | ||
| 1510 | drflac__cpuid(info, 1); | ||
| 1511 | return (info[3] & (1 << 26)) != 0; | ||
| 1512 | #endif | ||
| 1513 | #endif | ||
| 1514 | #else | ||
| 1515 | return DRFLAC_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ | ||
| 1516 | #endif | ||
| 1517 | #else | ||
| 1518 | return DRFLAC_FALSE; /* No compiler support. */ | ||
| 1519 | #endif | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) | ||
| 1523 | { | ||
| 1524 | #if defined(DRFLAC_SUPPORT_SSE41) | ||
| 1525 | #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) | ||
| 1526 | #if defined(__SSE4_1__) || defined(__AVX__) | ||
| 1527 | return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE41 code we can assume support. */ | ||
| 1528 | #else | ||
| 1529 | #if defined(DRFLAC_NO_CPUID) | ||
| 1530 | return DRFLAC_FALSE; | ||
| 1531 | #else | ||
| 1532 | int info[4]; | ||
| 1533 | drflac__cpuid(info, 1); | ||
| 1534 | return (info[2] & (1 << 19)) != 0; | ||
| 1535 | #endif | ||
| 1536 | #endif | ||
| 1537 | #else | ||
| 1538 | return DRFLAC_FALSE; /* SSE41 is only supported on x86 and x64 architectures. */ | ||
| 1539 | #endif | ||
| 1540 | #else | ||
| 1541 | return DRFLAC_FALSE; /* No compiler support. */ | ||
| 1542 | #endif | ||
| 1543 | } | ||
| 1544 | |||
| 1545 | |||
| 1546 | #if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__) | ||
| 1547 | #define DRFLAC_HAS_LZCNT_INTRINSIC | ||
| 1548 | #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) | ||
| 1549 | #define DRFLAC_HAS_LZCNT_INTRINSIC | ||
| 1550 | #elif defined(__clang__) | ||
| 1551 | #if defined(__has_builtin) | ||
| 1552 | #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) | ||
| 1553 | #define DRFLAC_HAS_LZCNT_INTRINSIC | ||
| 1554 | #endif | ||
| 1555 | #endif | ||
| 1556 | #endif | ||
| 1557 | |||
| 1558 | #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) | ||
| 1559 | #define DRFLAC_HAS_BYTESWAP16_INTRINSIC | ||
| 1560 | #define DRFLAC_HAS_BYTESWAP32_INTRINSIC | ||
| 1561 | #define DRFLAC_HAS_BYTESWAP64_INTRINSIC | ||
| 1562 | #elif defined(__clang__) | ||
| 1563 | #if defined(__has_builtin) | ||
| 1564 | #if __has_builtin(__builtin_bswap16) | ||
| 1565 | #define DRFLAC_HAS_BYTESWAP16_INTRINSIC | ||
| 1566 | #endif | ||
| 1567 | #if __has_builtin(__builtin_bswap32) | ||
| 1568 | #define DRFLAC_HAS_BYTESWAP32_INTRINSIC | ||
| 1569 | #endif | ||
| 1570 | #if __has_builtin(__builtin_bswap64) | ||
| 1571 | #define DRFLAC_HAS_BYTESWAP64_INTRINSIC | ||
| 1572 | #endif | ||
| 1573 | #endif | ||
| 1574 | #elif defined(__GNUC__) | ||
| 1575 | #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) | ||
| 1576 | #define DRFLAC_HAS_BYTESWAP32_INTRINSIC | ||
| 1577 | #define DRFLAC_HAS_BYTESWAP64_INTRINSIC | ||
| 1578 | #endif | ||
| 1579 | #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) | ||
| 1580 | #define DRFLAC_HAS_BYTESWAP16_INTRINSIC | ||
| 1581 | #endif | ||
| 1582 | #elif defined(__WATCOMC__) && defined(__386__) | ||
| 1583 | #define DRFLAC_HAS_BYTESWAP16_INTRINSIC | ||
| 1584 | #define DRFLAC_HAS_BYTESWAP32_INTRINSIC | ||
| 1585 | #define DRFLAC_HAS_BYTESWAP64_INTRINSIC | ||
| 1586 | extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16); | ||
| 1587 | extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32); | ||
| 1588 | extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64); | ||
| 1589 | #pragma aux _watcom_bswap16 = \ | ||
| 1590 | "xchg al, ah" \ | ||
| 1591 | parm [ax] \ | ||
| 1592 | value [ax] \ | ||
| 1593 | modify nomemory; | ||
| 1594 | #pragma aux _watcom_bswap32 = \ | ||
| 1595 | "bswap eax" \ | ||
| 1596 | parm [eax] \ | ||
| 1597 | value [eax] \ | ||
| 1598 | modify nomemory; | ||
| 1599 | #pragma aux _watcom_bswap64 = \ | ||
| 1600 | "bswap eax" \ | ||
| 1601 | "bswap edx" \ | ||
| 1602 | "xchg eax,edx" \ | ||
| 1603 | parm [eax edx] \ | ||
| 1604 | value [eax edx] \ | ||
| 1605 | modify nomemory; | ||
| 1606 | #endif | ||
| 1607 | |||
| 1608 | |||
| 1609 | /* Standard library stuff. */ | ||
| 1610 | #ifndef DRFLAC_ASSERT | ||
| 1611 | #include <assert.h> | ||
| 1612 | #define DRFLAC_ASSERT(expression) assert(expression) | ||
| 1613 | #endif | ||
| 1614 | #ifndef DRFLAC_MALLOC | ||
| 1615 | #define DRFLAC_MALLOC(sz) malloc((sz)) | ||
| 1616 | #endif | ||
| 1617 | #ifndef DRFLAC_REALLOC | ||
| 1618 | #define DRFLAC_REALLOC(p, sz) realloc((p), (sz)) | ||
| 1619 | #endif | ||
| 1620 | #ifndef DRFLAC_FREE | ||
| 1621 | #define DRFLAC_FREE(p) free((p)) | ||
| 1622 | #endif | ||
| 1623 | #ifndef DRFLAC_COPY_MEMORY | ||
| 1624 | #define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) | ||
| 1625 | #endif | ||
| 1626 | #ifndef DRFLAC_ZERO_MEMORY | ||
| 1627 | #define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) | ||
| 1628 | #endif | ||
| 1629 | #ifndef DRFLAC_ZERO_OBJECT | ||
| 1630 | #define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p))) | ||
| 1631 | #endif | ||
| 1632 | |||
| 1633 | #define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */ | ||
| 1634 | |||
| 1635 | /* Result Codes */ | ||
| 1636 | typedef drflac_int32 drflac_result; | ||
| 1637 | #define DRFLAC_SUCCESS 0 | ||
| 1638 | #define DRFLAC_ERROR -1 /* A generic error. */ | ||
| 1639 | #define DRFLAC_INVALID_ARGS -2 | ||
| 1640 | #define DRFLAC_INVALID_OPERATION -3 | ||
| 1641 | #define DRFLAC_OUT_OF_MEMORY -4 | ||
| 1642 | #define DRFLAC_OUT_OF_RANGE -5 | ||
| 1643 | #define DRFLAC_ACCESS_DENIED -6 | ||
| 1644 | #define DRFLAC_DOES_NOT_EXIST -7 | ||
| 1645 | #define DRFLAC_ALREADY_EXISTS -8 | ||
| 1646 | #define DRFLAC_TOO_MANY_OPEN_FILES -9 | ||
| 1647 | #define DRFLAC_INVALID_FILE -10 | ||
| 1648 | #define DRFLAC_TOO_BIG -11 | ||
| 1649 | #define DRFLAC_PATH_TOO_LONG -12 | ||
| 1650 | #define DRFLAC_NAME_TOO_LONG -13 | ||
| 1651 | #define DRFLAC_NOT_DIRECTORY -14 | ||
| 1652 | #define DRFLAC_IS_DIRECTORY -15 | ||
| 1653 | #define DRFLAC_DIRECTORY_NOT_EMPTY -16 | ||
| 1654 | #define DRFLAC_END_OF_FILE -17 | ||
| 1655 | #define DRFLAC_NO_SPACE -18 | ||
| 1656 | #define DRFLAC_BUSY -19 | ||
| 1657 | #define DRFLAC_IO_ERROR -20 | ||
| 1658 | #define DRFLAC_INTERRUPT -21 | ||
| 1659 | #define DRFLAC_UNAVAILABLE -22 | ||
| 1660 | #define DRFLAC_ALREADY_IN_USE -23 | ||
| 1661 | #define DRFLAC_BAD_ADDRESS -24 | ||
| 1662 | #define DRFLAC_BAD_SEEK -25 | ||
| 1663 | #define DRFLAC_BAD_PIPE -26 | ||
| 1664 | #define DRFLAC_DEADLOCK -27 | ||
| 1665 | #define DRFLAC_TOO_MANY_LINKS -28 | ||
| 1666 | #define DRFLAC_NOT_IMPLEMENTED -29 | ||
| 1667 | #define DRFLAC_NO_MESSAGE -30 | ||
| 1668 | #define DRFLAC_BAD_MESSAGE -31 | ||
| 1669 | #define DRFLAC_NO_DATA_AVAILABLE -32 | ||
| 1670 | #define DRFLAC_INVALID_DATA -33 | ||
| 1671 | #define DRFLAC_TIMEOUT -34 | ||
| 1672 | #define DRFLAC_NO_NETWORK -35 | ||
| 1673 | #define DRFLAC_NOT_UNIQUE -36 | ||
| 1674 | #define DRFLAC_NOT_SOCKET -37 | ||
| 1675 | #define DRFLAC_NO_ADDRESS -38 | ||
| 1676 | #define DRFLAC_BAD_PROTOCOL -39 | ||
| 1677 | #define DRFLAC_PROTOCOL_UNAVAILABLE -40 | ||
| 1678 | #define DRFLAC_PROTOCOL_NOT_SUPPORTED -41 | ||
| 1679 | #define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42 | ||
| 1680 | #define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43 | ||
| 1681 | #define DRFLAC_SOCKET_NOT_SUPPORTED -44 | ||
| 1682 | #define DRFLAC_CONNECTION_RESET -45 | ||
| 1683 | #define DRFLAC_ALREADY_CONNECTED -46 | ||
| 1684 | #define DRFLAC_NOT_CONNECTED -47 | ||
| 1685 | #define DRFLAC_CONNECTION_REFUSED -48 | ||
| 1686 | #define DRFLAC_NO_HOST -49 | ||
| 1687 | #define DRFLAC_IN_PROGRESS -50 | ||
| 1688 | #define DRFLAC_CANCELLED -51 | ||
| 1689 | #define DRFLAC_MEMORY_ALREADY_MAPPED -52 | ||
| 1690 | #define DRFLAC_AT_END -53 | ||
| 1691 | |||
| 1692 | #define DRFLAC_CRC_MISMATCH -100 | ||
| 1693 | /* End Result Codes */ | ||
| 1694 | |||
| 1695 | |||
| 1696 | #define DRFLAC_SUBFRAME_CONSTANT 0 | ||
| 1697 | #define DRFLAC_SUBFRAME_VERBATIM 1 | ||
| 1698 | #define DRFLAC_SUBFRAME_FIXED 8 | ||
| 1699 | #define DRFLAC_SUBFRAME_LPC 32 | ||
| 1700 | #define DRFLAC_SUBFRAME_RESERVED 255 | ||
| 1701 | |||
| 1702 | #define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 | ||
| 1703 | #define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 | ||
| 1704 | |||
| 1705 | #define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 | ||
| 1706 | #define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 | ||
| 1707 | #define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 | ||
| 1708 | #define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 | ||
| 1709 | |||
| 1710 | #define DRFLAC_SEEKPOINT_SIZE_IN_BYTES 18 | ||
| 1711 | #define DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 | ||
| 1712 | #define DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 | ||
| 1713 | |||
| 1714 | #define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) | ||
| 1715 | |||
| 1716 | |||
| 1717 | DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision) | ||
| 1718 | { | ||
| 1719 | if (pMajor) { | ||
| 1720 | *pMajor = DRFLAC_VERSION_MAJOR; | ||
| 1721 | } | ||
| 1722 | |||
| 1723 | if (pMinor) { | ||
| 1724 | *pMinor = DRFLAC_VERSION_MINOR; | ||
| 1725 | } | ||
| 1726 | |||
| 1727 | if (pRevision) { | ||
| 1728 | *pRevision = DRFLAC_VERSION_REVISION; | ||
| 1729 | } | ||
| 1730 | } | ||
| 1731 | |||
| 1732 | DRFLAC_API const char* drflac_version_string(void) | ||
| 1733 | { | ||
| 1734 | return DRFLAC_VERSION_STRING; | ||
| 1735 | } | ||
| 1736 | |||
| 1737 | |||
| 1738 | /* CPU caps. */ | ||
| 1739 | #if defined(__has_feature) | ||
| 1740 | #if __has_feature(thread_sanitizer) | ||
| 1741 | #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) | ||
| 1742 | #else | ||
| 1743 | #define DRFLAC_NO_THREAD_SANITIZE | ||
| 1744 | #endif | ||
| 1745 | #else | ||
| 1746 | #define DRFLAC_NO_THREAD_SANITIZE | ||
| 1747 | #endif | ||
| 1748 | |||
| 1749 | #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) | ||
| 1750 | static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; | ||
| 1751 | #endif | ||
| 1752 | |||
| 1753 | #ifndef DRFLAC_NO_CPUID | ||
| 1754 | static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE; | ||
| 1755 | static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE; | ||
| 1756 | |||
| 1757 | /* | ||
| 1758 | I've had a bug report that Clang's ThreadSanitizer presents a warning in this function. Having reviewed this, this does | ||
| 1759 | actually make sense. However, since CPU caps should never differ for a running process, I don't think the trade off of | ||
| 1760 | complicating internal API's by passing around CPU caps versus just disabling the warnings is worthwhile. I'm therefore | ||
| 1761 | just going to disable these warnings. This is disabled via the DRFLAC_NO_THREAD_SANITIZE attribute. | ||
| 1762 | */ | ||
| 1763 | DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) | ||
| 1764 | { | ||
| 1765 | static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE; | ||
| 1766 | |||
| 1767 | if (!isCPUCapsInitialized) { | ||
| 1768 | /* LZCNT */ | ||
| 1769 | #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) | ||
| 1770 | int info[4] = {0}; | ||
| 1771 | drflac__cpuid(info, 0x80000001); | ||
| 1772 | drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; | ||
| 1773 | #endif | ||
| 1774 | |||
| 1775 | /* SSE2 */ | ||
| 1776 | drflac__gIsSSE2Supported = drflac_has_sse2(); | ||
| 1777 | |||
| 1778 | /* SSE4.1 */ | ||
| 1779 | drflac__gIsSSE41Supported = drflac_has_sse41(); | ||
| 1780 | |||
| 1781 | /* Initialized. */ | ||
| 1782 | isCPUCapsInitialized = DRFLAC_TRUE; | ||
| 1783 | } | ||
| 1784 | } | ||
| 1785 | #else | ||
| 1786 | static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE; | ||
| 1787 | |||
| 1788 | static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void) | ||
| 1789 | { | ||
| 1790 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 1791 | #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON) | ||
| 1792 | #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) | ||
| 1793 | return DRFLAC_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ | ||
| 1794 | #else | ||
| 1795 | /* TODO: Runtime check. */ | ||
| 1796 | return DRFLAC_FALSE; | ||
| 1797 | #endif | ||
| 1798 | #else | ||
| 1799 | return DRFLAC_FALSE; /* NEON is only supported on ARM architectures. */ | ||
| 1800 | #endif | ||
| 1801 | #else | ||
| 1802 | return DRFLAC_FALSE; /* No compiler support. */ | ||
| 1803 | #endif | ||
| 1804 | } | ||
| 1805 | |||
| 1806 | DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) | ||
| 1807 | { | ||
| 1808 | drflac__gIsNEONSupported = drflac__has_neon(); | ||
| 1809 | |||
| 1810 | #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) | ||
| 1811 | drflac__gIsLZCNTSupported = DRFLAC_TRUE; | ||
| 1812 | #endif | ||
| 1813 | } | ||
| 1814 | #endif | ||
| 1815 | |||
| 1816 | |||
| 1817 | /* Endian Management */ | ||
| 1818 | static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void) | ||
| 1819 | { | ||
| 1820 | #if defined(DRFLAC_X86) || defined(DRFLAC_X64) | ||
| 1821 | return DRFLAC_TRUE; | ||
| 1822 | #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN | ||
| 1823 | return DRFLAC_TRUE; | ||
| 1824 | #else | ||
| 1825 | int n = 1; | ||
| 1826 | return (*(char*)&n) == 1; | ||
| 1827 | #endif | ||
| 1828 | } | ||
| 1829 | |||
| 1830 | static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) | ||
| 1831 | { | ||
| 1832 | #ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC | ||
| 1833 | #if defined(_MSC_VER) && !defined(__clang__) | ||
| 1834 | return _byteswap_ushort(n); | ||
| 1835 | #elif defined(__GNUC__) || defined(__clang__) | ||
| 1836 | return __builtin_bswap16(n); | ||
| 1837 | #elif defined(__WATCOMC__) && defined(__386__) | ||
| 1838 | return _watcom_bswap16(n); | ||
| 1839 | #else | ||
| 1840 | #error "This compiler does not support the byte swap intrinsic." | ||
| 1841 | #endif | ||
| 1842 | #else | ||
| 1843 | return ((n & 0xFF00) >> 8) | | ||
| 1844 | ((n & 0x00FF) << 8); | ||
| 1845 | #endif | ||
| 1846 | } | ||
| 1847 | |||
| 1848 | static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) | ||
| 1849 | { | ||
| 1850 | #ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC | ||
| 1851 | #if defined(_MSC_VER) && !defined(__clang__) | ||
| 1852 | return _byteswap_ulong(n); | ||
| 1853 | #elif defined(__GNUC__) || defined(__clang__) | ||
| 1854 | #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(DRFLAC_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ | ||
| 1855 | /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ | ||
| 1856 | drflac_uint32 r; | ||
| 1857 | __asm__ __volatile__ ( | ||
| 1858 | #if defined(DRFLAC_64BIT) | ||
| 1859 | "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ | ||
| 1860 | #else | ||
| 1861 | "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) | ||
| 1862 | #endif | ||
| 1863 | ); | ||
| 1864 | return r; | ||
| 1865 | #else | ||
| 1866 | return __builtin_bswap32(n); | ||
| 1867 | #endif | ||
| 1868 | #elif defined(__WATCOMC__) && defined(__386__) | ||
| 1869 | return _watcom_bswap32(n); | ||
| 1870 | #else | ||
| 1871 | #error "This compiler does not support the byte swap intrinsic." | ||
| 1872 | #endif | ||
| 1873 | #else | ||
| 1874 | return ((n & 0xFF000000) >> 24) | | ||
| 1875 | ((n & 0x00FF0000) >> 8) | | ||
| 1876 | ((n & 0x0000FF00) << 8) | | ||
| 1877 | ((n & 0x000000FF) << 24); | ||
| 1878 | #endif | ||
| 1879 | } | ||
| 1880 | |||
| 1881 | static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) | ||
| 1882 | { | ||
| 1883 | #ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC | ||
| 1884 | #if defined(_MSC_VER) && !defined(__clang__) | ||
| 1885 | return _byteswap_uint64(n); | ||
| 1886 | #elif defined(__GNUC__) || defined(__clang__) | ||
| 1887 | return __builtin_bswap64(n); | ||
| 1888 | #elif defined(__WATCOMC__) && defined(__386__) | ||
| 1889 | return _watcom_bswap64(n); | ||
| 1890 | #else | ||
| 1891 | #error "This compiler does not support the byte swap intrinsic." | ||
| 1892 | #endif | ||
| 1893 | #else | ||
| 1894 | /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */ | ||
| 1895 | return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) | | ||
| 1896 | ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) | | ||
| 1897 | ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) | | ||
| 1898 | ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) | | ||
| 1899 | ((n & ((drflac_uint64)0xFF000000 )) << 8) | | ||
| 1900 | ((n & ((drflac_uint64)0x00FF0000 )) << 24) | | ||
| 1901 | ((n & ((drflac_uint64)0x0000FF00 )) << 40) | | ||
| 1902 | ((n & ((drflac_uint64)0x000000FF )) << 56); | ||
| 1903 | #endif | ||
| 1904 | } | ||
| 1905 | |||
| 1906 | |||
| 1907 | static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) | ||
| 1908 | { | ||
| 1909 | if (drflac__is_little_endian()) { | ||
| 1910 | return drflac__swap_endian_uint16(n); | ||
| 1911 | } | ||
| 1912 | |||
| 1913 | return n; | ||
| 1914 | } | ||
| 1915 | |||
| 1916 | static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) | ||
| 1917 | { | ||
| 1918 | if (drflac__is_little_endian()) { | ||
| 1919 | return drflac__swap_endian_uint32(n); | ||
| 1920 | } | ||
| 1921 | |||
| 1922 | return n; | ||
| 1923 | } | ||
| 1924 | |||
| 1925 | static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData) | ||
| 1926 | { | ||
| 1927 | const drflac_uint8* pNum = (drflac_uint8*)pData; | ||
| 1928 | return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); | ||
| 1929 | } | ||
| 1930 | |||
| 1931 | static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) | ||
| 1932 | { | ||
| 1933 | if (drflac__is_little_endian()) { | ||
| 1934 | return drflac__swap_endian_uint64(n); | ||
| 1935 | } | ||
| 1936 | |||
| 1937 | return n; | ||
| 1938 | } | ||
| 1939 | |||
| 1940 | |||
| 1941 | static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) | ||
| 1942 | { | ||
| 1943 | if (!drflac__is_little_endian()) { | ||
| 1944 | return drflac__swap_endian_uint32(n); | ||
| 1945 | } | ||
| 1946 | |||
| 1947 | return n; | ||
| 1948 | } | ||
| 1949 | |||
| 1950 | static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData) | ||
| 1951 | { | ||
| 1952 | const drflac_uint8* pNum = (drflac_uint8*)pData; | ||
| 1953 | return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; | ||
| 1954 | } | ||
| 1955 | |||
| 1956 | |||
| 1957 | static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) | ||
| 1958 | { | ||
| 1959 | drflac_uint32 result = 0; | ||
| 1960 | result |= (n & 0x7F000000) >> 3; | ||
| 1961 | result |= (n & 0x007F0000) >> 2; | ||
| 1962 | result |= (n & 0x00007F00) >> 1; | ||
| 1963 | result |= (n & 0x0000007F) >> 0; | ||
| 1964 | |||
| 1965 | return result; | ||
| 1966 | } | ||
| 1967 | |||
| 1968 | |||
| 1969 | |||
| 1970 | /* The CRC code below is based on this document: http://zlib.net/crc_v3.txt */ | ||
| 1971 | static drflac_uint8 drflac__crc8_table[] = { | ||
| 1972 | 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, | ||
| 1973 | 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, | ||
| 1974 | 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, | ||
| 1975 | 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, | ||
| 1976 | 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, | ||
| 1977 | 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, | ||
| 1978 | 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, | ||
| 1979 | 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, | ||
| 1980 | 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, | ||
| 1981 | 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, | ||
| 1982 | 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, | ||
| 1983 | 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, | ||
| 1984 | 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, | ||
| 1985 | 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, | ||
| 1986 | 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, | ||
| 1987 | 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 | ||
| 1988 | }; | ||
| 1989 | |||
| 1990 | static drflac_uint16 drflac__crc16_table[] = { | ||
| 1991 | 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, | ||
| 1992 | 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, | ||
| 1993 | 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, | ||
| 1994 | 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, | ||
| 1995 | 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, | ||
| 1996 | 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, | ||
| 1997 | 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, | ||
| 1998 | 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, | ||
| 1999 | 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, | ||
| 2000 | 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, | ||
| 2001 | 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, | ||
| 2002 | 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, | ||
| 2003 | 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, | ||
| 2004 | 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, | ||
| 2005 | 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, | ||
| 2006 | 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, | ||
| 2007 | 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, | ||
| 2008 | 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, | ||
| 2009 | 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, | ||
| 2010 | 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, | ||
| 2011 | 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, | ||
| 2012 | 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, | ||
| 2013 | 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, | ||
| 2014 | 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, | ||
| 2015 | 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, | ||
| 2016 | 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, | ||
| 2017 | 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, | ||
| 2018 | 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, | ||
| 2019 | 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, | ||
| 2020 | 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, | ||
| 2021 | 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, | ||
| 2022 | 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 | ||
| 2023 | }; | ||
| 2024 | |||
| 2025 | static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data) | ||
| 2026 | { | ||
| 2027 | return drflac__crc8_table[crc ^ data]; | ||
| 2028 | } | ||
| 2029 | |||
| 2030 | static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) | ||
| 2031 | { | ||
| 2032 | #ifdef DR_FLAC_NO_CRC | ||
| 2033 | (void)crc; | ||
| 2034 | (void)data; | ||
| 2035 | (void)count; | ||
| 2036 | return 0; | ||
| 2037 | #else | ||
| 2038 | #if 0 | ||
| 2039 | /* REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);") */ | ||
| 2040 | drflac_uint8 p = 0x07; | ||
| 2041 | for (int i = count-1; i >= 0; --i) { | ||
| 2042 | drflac_uint8 bit = (data & (1 << i)) >> i; | ||
| 2043 | if (crc & 0x80) { | ||
| 2044 | crc = ((crc << 1) | bit) ^ p; | ||
| 2045 | } else { | ||
| 2046 | crc = ((crc << 1) | bit); | ||
| 2047 | } | ||
| 2048 | } | ||
| 2049 | return crc; | ||
| 2050 | #else | ||
| 2051 | drflac_uint32 wholeBytes; | ||
| 2052 | drflac_uint32 leftoverBits; | ||
| 2053 | drflac_uint64 leftoverDataMask; | ||
| 2054 | |||
| 2055 | static drflac_uint64 leftoverDataMaskTable[8] = { | ||
| 2056 | 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F | ||
| 2057 | }; | ||
| 2058 | |||
| 2059 | DRFLAC_ASSERT(count <= 32); | ||
| 2060 | |||
| 2061 | wholeBytes = count >> 3; | ||
| 2062 | leftoverBits = count - (wholeBytes*8); | ||
| 2063 | leftoverDataMask = leftoverDataMaskTable[leftoverBits]; | ||
| 2064 | |||
| 2065 | switch (wholeBytes) { | ||
| 2066 | case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); | ||
| 2067 | case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); | ||
| 2068 | case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); | ||
| 2069 | case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); | ||
| 2070 | case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); | ||
| 2071 | } | ||
| 2072 | return crc; | ||
| 2073 | #endif | ||
| 2074 | #endif | ||
| 2075 | } | ||
| 2076 | |||
| 2077 | static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data) | ||
| 2078 | { | ||
| 2079 | return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data]; | ||
| 2080 | } | ||
| 2081 | |||
| 2082 | static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data) | ||
| 2083 | { | ||
| 2084 | #ifdef DRFLAC_64BIT | ||
| 2085 | crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); | ||
| 2086 | crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); | ||
| 2087 | crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); | ||
| 2088 | crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); | ||
| 2089 | #endif | ||
| 2090 | crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); | ||
| 2091 | crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); | ||
| 2092 | crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); | ||
| 2093 | crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); | ||
| 2094 | |||
| 2095 | return crc; | ||
| 2096 | } | ||
| 2097 | |||
| 2098 | static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount) | ||
| 2099 | { | ||
| 2100 | switch (byteCount) | ||
| 2101 | { | ||
| 2102 | #ifdef DRFLAC_64BIT | ||
| 2103 | case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); | ||
| 2104 | case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); | ||
| 2105 | case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); | ||
| 2106 | case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); | ||
| 2107 | #endif | ||
| 2108 | case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); | ||
| 2109 | case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); | ||
| 2110 | case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); | ||
| 2111 | case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); | ||
| 2112 | } | ||
| 2113 | |||
| 2114 | return crc; | ||
| 2115 | } | ||
| 2116 | |||
| 2117 | #if 0 | ||
| 2118 | static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) | ||
| 2119 | { | ||
| 2120 | #ifdef DR_FLAC_NO_CRC | ||
| 2121 | (void)crc; | ||
| 2122 | (void)data; | ||
| 2123 | (void)count; | ||
| 2124 | return 0; | ||
| 2125 | #else | ||
| 2126 | #if 0 | ||
| 2127 | /* REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);") */ | ||
| 2128 | drflac_uint16 p = 0x8005; | ||
| 2129 | for (int i = count-1; i >= 0; --i) { | ||
| 2130 | drflac_uint16 bit = (data & (1ULL << i)) >> i; | ||
| 2131 | if (r & 0x8000) { | ||
| 2132 | r = ((r << 1) | bit) ^ p; | ||
| 2133 | } else { | ||
| 2134 | r = ((r << 1) | bit); | ||
| 2135 | } | ||
| 2136 | } | ||
| 2137 | |||
| 2138 | return crc; | ||
| 2139 | #else | ||
| 2140 | drflac_uint32 wholeBytes; | ||
| 2141 | drflac_uint32 leftoverBits; | ||
| 2142 | drflac_uint64 leftoverDataMask; | ||
| 2143 | |||
| 2144 | static drflac_uint64 leftoverDataMaskTable[8] = { | ||
| 2145 | 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F | ||
| 2146 | }; | ||
| 2147 | |||
| 2148 | DRFLAC_ASSERT(count <= 64); | ||
| 2149 | |||
| 2150 | wholeBytes = count >> 3; | ||
| 2151 | leftoverBits = count & 7; | ||
| 2152 | leftoverDataMask = leftoverDataMaskTable[leftoverBits]; | ||
| 2153 | |||
| 2154 | switch (wholeBytes) { | ||
| 2155 | default: | ||
| 2156 | case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); | ||
| 2157 | case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); | ||
| 2158 | case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); | ||
| 2159 | case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); | ||
| 2160 | case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; | ||
| 2161 | } | ||
| 2162 | return crc; | ||
| 2163 | #endif | ||
| 2164 | #endif | ||
| 2165 | } | ||
| 2166 | |||
| 2167 | static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) | ||
| 2168 | { | ||
| 2169 | #ifdef DR_FLAC_NO_CRC | ||
| 2170 | (void)crc; | ||
| 2171 | (void)data; | ||
| 2172 | (void)count; | ||
| 2173 | return 0; | ||
| 2174 | #else | ||
| 2175 | drflac_uint32 wholeBytes; | ||
| 2176 | drflac_uint32 leftoverBits; | ||
| 2177 | drflac_uint64 leftoverDataMask; | ||
| 2178 | |||
| 2179 | static drflac_uint64 leftoverDataMaskTable[8] = { | ||
| 2180 | 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F | ||
| 2181 | }; | ||
| 2182 | |||
| 2183 | DRFLAC_ASSERT(count <= 64); | ||
| 2184 | |||
| 2185 | wholeBytes = count >> 3; | ||
| 2186 | leftoverBits = count & 7; | ||
| 2187 | leftoverDataMask = leftoverDataMaskTable[leftoverBits]; | ||
| 2188 | |||
| 2189 | switch (wholeBytes) { | ||
| 2190 | default: | ||
| 2191 | case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */ | ||
| 2192 | case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); | ||
| 2193 | case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); | ||
| 2194 | case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); | ||
| 2195 | case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); | ||
| 2196 | case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); | ||
| 2197 | case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); | ||
| 2198 | case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); | ||
| 2199 | case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; | ||
| 2200 | } | ||
| 2201 | return crc; | ||
| 2202 | #endif | ||
| 2203 | } | ||
| 2204 | |||
| 2205 | |||
| 2206 | static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count) | ||
| 2207 | { | ||
| 2208 | #ifdef DRFLAC_64BIT | ||
| 2209 | return drflac_crc16__64bit(crc, data, count); | ||
| 2210 | #else | ||
| 2211 | return drflac_crc16__32bit(crc, data, count); | ||
| 2212 | #endif | ||
| 2213 | } | ||
| 2214 | #endif | ||
| 2215 | |||
| 2216 | |||
| 2217 | #ifdef DRFLAC_64BIT | ||
| 2218 | #define drflac__be2host__cache_line drflac__be2host_64 | ||
| 2219 | #else | ||
| 2220 | #define drflac__be2host__cache_line drflac__be2host_32 | ||
| 2221 | #endif | ||
| 2222 | |||
| 2223 | /* | ||
| 2224 | BIT READING ATTEMPT #2 | ||
| 2225 | |||
| 2226 | This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting | ||
| 2227 | on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache | ||
| 2228 | is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an | ||
| 2229 | array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data | ||
| 2230 | from onRead() is read into. | ||
| 2231 | */ | ||
| 2232 | #define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) | ||
| 2233 | #define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) | ||
| 2234 | #define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) | ||
| 2235 | #define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount))) | ||
| 2236 | #define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) | ||
| 2237 | #define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) | ||
| 2238 | #define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) | ||
| 2239 | #define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1))) | ||
| 2240 | #define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) | ||
| 2241 | #define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) | ||
| 2242 | #define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) | ||
| 2243 | |||
| 2244 | |||
| 2245 | #ifndef DR_FLAC_NO_CRC | ||
| 2246 | static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs) | ||
| 2247 | { | ||
| 2248 | bs->crc16 = 0; | ||
| 2249 | bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; | ||
| 2250 | } | ||
| 2251 | |||
| 2252 | static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) | ||
| 2253 | { | ||
| 2254 | if (bs->crc16CacheIgnoredBytes == 0) { | ||
| 2255 | bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache); | ||
| 2256 | } else { | ||
| 2257 | bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); | ||
| 2258 | bs->crc16CacheIgnoredBytes = 0; | ||
| 2259 | } | ||
| 2260 | } | ||
| 2261 | |||
| 2262 | static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) | ||
| 2263 | { | ||
| 2264 | /* We should never be flushing in a situation where we are not aligned on a byte boundary. */ | ||
| 2265 | DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); | ||
| 2266 | |||
| 2267 | /* | ||
| 2268 | The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined | ||
| 2269 | by the number of bits that have been consumed. | ||
| 2270 | */ | ||
| 2271 | if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { | ||
| 2272 | drflac__update_crc16(bs); | ||
| 2273 | } else { | ||
| 2274 | /* We only accumulate the consumed bits. */ | ||
| 2275 | bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); | ||
| 2276 | |||
| 2277 | /* | ||
| 2278 | The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated | ||
| 2279 | so we can handle that later. | ||
| 2280 | */ | ||
| 2281 | bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; | ||
| 2282 | } | ||
| 2283 | |||
| 2284 | return bs->crc16; | ||
| 2285 | } | ||
| 2286 | #endif | ||
| 2287 | |||
| 2288 | static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) | ||
| 2289 | { | ||
| 2290 | size_t bytesRead; | ||
| 2291 | size_t alignedL1LineCount; | ||
| 2292 | |||
| 2293 | /* Fast path. Try loading straight from L2. */ | ||
| 2294 | if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { | ||
| 2295 | bs->cache = bs->cacheL2[bs->nextL2Line++]; | ||
| 2296 | return DRFLAC_TRUE; | ||
| 2297 | } | ||
| 2298 | |||
| 2299 | /* | ||
| 2300 | If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's | ||
| 2301 | any left. | ||
| 2302 | */ | ||
| 2303 | if (bs->unalignedByteCount > 0) { | ||
| 2304 | return DRFLAC_FALSE; /* If we have any unaligned bytes it means there's no more aligned bytes left in the client. */ | ||
| 2305 | } | ||
| 2306 | |||
| 2307 | bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); | ||
| 2308 | |||
| 2309 | bs->nextL2Line = 0; | ||
| 2310 | if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { | ||
| 2311 | bs->cache = bs->cacheL2[bs->nextL2Line++]; | ||
| 2312 | return DRFLAC_TRUE; | ||
| 2313 | } | ||
| 2314 | |||
| 2315 | |||
| 2316 | /* | ||
| 2317 | If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably | ||
| 2318 | means we've just reached the end of the file. We need to move the valid data down to the end of the buffer | ||
| 2319 | and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to | ||
| 2320 | the size of the L1 so we'll need to seek backwards by any misaligned bytes. | ||
| 2321 | */ | ||
| 2322 | alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); | ||
| 2323 | |||
| 2324 | /* We need to keep track of any unaligned bytes for later use. */ | ||
| 2325 | bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); | ||
| 2326 | if (bs->unalignedByteCount > 0) { | ||
| 2327 | bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; | ||
| 2328 | } | ||
| 2329 | |||
| 2330 | if (alignedL1LineCount > 0) { | ||
| 2331 | size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; | ||
| 2332 | size_t i; | ||
| 2333 | for (i = alignedL1LineCount; i > 0; --i) { | ||
| 2334 | bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; | ||
| 2335 | } | ||
| 2336 | |||
| 2337 | bs->nextL2Line = (drflac_uint32)offset; | ||
| 2338 | bs->cache = bs->cacheL2[bs->nextL2Line++]; | ||
| 2339 | return DRFLAC_TRUE; | ||
| 2340 | } else { | ||
| 2341 | /* If we get into this branch it means we weren't able to load any L1-aligned data. */ | ||
| 2342 | bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); | ||
| 2343 | return DRFLAC_FALSE; | ||
| 2344 | } | ||
| 2345 | } | ||
| 2346 | |||
| 2347 | static drflac_bool32 drflac__reload_cache(drflac_bs* bs) | ||
| 2348 | { | ||
| 2349 | size_t bytesRead; | ||
| 2350 | |||
| 2351 | #ifndef DR_FLAC_NO_CRC | ||
| 2352 | drflac__update_crc16(bs); | ||
| 2353 | #endif | ||
| 2354 | |||
| 2355 | /* Fast path. Try just moving the next value in the L2 cache to the L1 cache. */ | ||
| 2356 | if (drflac__reload_l1_cache_from_l2(bs)) { | ||
| 2357 | bs->cache = drflac__be2host__cache_line(bs->cache); | ||
| 2358 | bs->consumedBits = 0; | ||
| 2359 | #ifndef DR_FLAC_NO_CRC | ||
| 2360 | bs->crc16Cache = bs->cache; | ||
| 2361 | #endif | ||
| 2362 | return DRFLAC_TRUE; | ||
| 2363 | } | ||
| 2364 | |||
| 2365 | /* Slow path. */ | ||
| 2366 | |||
| 2367 | /* | ||
| 2368 | If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last | ||
| 2369 | few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the | ||
| 2370 | data from the unaligned cache. | ||
| 2371 | */ | ||
| 2372 | bytesRead = bs->unalignedByteCount; | ||
| 2373 | if (bytesRead == 0) { | ||
| 2374 | bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); /* <-- The stream has been exhausted, so marked the bits as consumed. */ | ||
| 2375 | return DRFLAC_FALSE; | ||
| 2376 | } | ||
| 2377 | |||
| 2378 | DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); | ||
| 2379 | bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; | ||
| 2380 | |||
| 2381 | bs->cache = drflac__be2host__cache_line(bs->unalignedCache); | ||
| 2382 | bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs)); /* <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property. */ | ||
| 2383 | bs->unalignedByteCount = 0; /* <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes. */ | ||
| 2384 | |||
| 2385 | #ifndef DR_FLAC_NO_CRC | ||
| 2386 | bs->crc16Cache = bs->cache >> bs->consumedBits; | ||
| 2387 | bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; | ||
| 2388 | #endif | ||
| 2389 | return DRFLAC_TRUE; | ||
| 2390 | } | ||
| 2391 | |||
| 2392 | static void drflac__reset_cache(drflac_bs* bs) | ||
| 2393 | { | ||
| 2394 | bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); /* <-- This clears the L2 cache. */ | ||
| 2395 | bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); /* <-- This clears the L1 cache. */ | ||
| 2396 | bs->cache = 0; | ||
| 2397 | bs->unalignedByteCount = 0; /* <-- This clears the trailing unaligned bytes. */ | ||
| 2398 | bs->unalignedCache = 0; | ||
| 2399 | |||
| 2400 | #ifndef DR_FLAC_NO_CRC | ||
| 2401 | bs->crc16Cache = 0; | ||
| 2402 | bs->crc16CacheIgnoredBytes = 0; | ||
| 2403 | #endif | ||
| 2404 | } | ||
| 2405 | |||
| 2406 | |||
| 2407 | static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut) | ||
| 2408 | { | ||
| 2409 | DRFLAC_ASSERT(bs != NULL); | ||
| 2410 | DRFLAC_ASSERT(pResultOut != NULL); | ||
| 2411 | DRFLAC_ASSERT(bitCount > 0); | ||
| 2412 | DRFLAC_ASSERT(bitCount <= 32); | ||
| 2413 | |||
| 2414 | if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { | ||
| 2415 | if (!drflac__reload_cache(bs)) { | ||
| 2416 | return DRFLAC_FALSE; | ||
| 2417 | } | ||
| 2418 | } | ||
| 2419 | |||
| 2420 | if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { | ||
| 2421 | /* | ||
| 2422 | If we want to load all 32-bits from a 32-bit cache we need to do it slightly differently because we can't do | ||
| 2423 | a 32-bit shift on a 32-bit integer. This will never be the case on 64-bit caches, so we can have a slightly | ||
| 2424 | more optimal solution for this. | ||
| 2425 | */ | ||
| 2426 | #ifdef DRFLAC_64BIT | ||
| 2427 | *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); | ||
| 2428 | bs->consumedBits += bitCount; | ||
| 2429 | bs->cache <<= bitCount; | ||
| 2430 | #else | ||
| 2431 | if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { | ||
| 2432 | *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); | ||
| 2433 | bs->consumedBits += bitCount; | ||
| 2434 | bs->cache <<= bitCount; | ||
| 2435 | } else { | ||
| 2436 | /* Cannot shift by 32-bits, so need to do it differently. */ | ||
| 2437 | *pResultOut = (drflac_uint32)bs->cache; | ||
| 2438 | bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); | ||
| 2439 | bs->cache = 0; | ||
| 2440 | } | ||
| 2441 | #endif | ||
| 2442 | |||
| 2443 | return DRFLAC_TRUE; | ||
| 2444 | } else { | ||
| 2445 | /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ | ||
| 2446 | drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); | ||
| 2447 | drflac_uint32 bitCountLo = bitCount - bitCountHi; | ||
| 2448 | drflac_uint32 resultHi; | ||
| 2449 | |||
| 2450 | DRFLAC_ASSERT(bitCountHi > 0); | ||
| 2451 | DRFLAC_ASSERT(bitCountHi < 32); | ||
| 2452 | resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); | ||
| 2453 | |||
| 2454 | if (!drflac__reload_cache(bs)) { | ||
| 2455 | return DRFLAC_FALSE; | ||
| 2456 | } | ||
| 2457 | if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { | ||
| 2458 | /* This happens when we get to end of stream */ | ||
| 2459 | return DRFLAC_FALSE; | ||
| 2460 | } | ||
| 2461 | |||
| 2462 | *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); | ||
| 2463 | bs->consumedBits += bitCountLo; | ||
| 2464 | bs->cache <<= bitCountLo; | ||
| 2465 | return DRFLAC_TRUE; | ||
| 2466 | } | ||
| 2467 | } | ||
| 2468 | |||
| 2469 | static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) | ||
| 2470 | { | ||
| 2471 | drflac_uint32 result; | ||
| 2472 | |||
| 2473 | DRFLAC_ASSERT(bs != NULL); | ||
| 2474 | DRFLAC_ASSERT(pResult != NULL); | ||
| 2475 | DRFLAC_ASSERT(bitCount > 0); | ||
| 2476 | DRFLAC_ASSERT(bitCount <= 32); | ||
| 2477 | |||
| 2478 | if (!drflac__read_uint32(bs, bitCount, &result)) { | ||
| 2479 | return DRFLAC_FALSE; | ||
| 2480 | } | ||
| 2481 | |||
| 2482 | /* Do not attempt to shift by 32 as it's undefined. */ | ||
| 2483 | if (bitCount < 32) { | ||
| 2484 | drflac_uint32 signbit; | ||
| 2485 | signbit = ((result >> (bitCount-1)) & 0x01); | ||
| 2486 | result |= (~signbit + 1) << bitCount; | ||
| 2487 | } | ||
| 2488 | |||
| 2489 | *pResult = (drflac_int32)result; | ||
| 2490 | return DRFLAC_TRUE; | ||
| 2491 | } | ||
| 2492 | |||
| 2493 | #ifdef DRFLAC_64BIT | ||
| 2494 | static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) | ||
| 2495 | { | ||
| 2496 | drflac_uint32 resultHi; | ||
| 2497 | drflac_uint32 resultLo; | ||
| 2498 | |||
| 2499 | DRFLAC_ASSERT(bitCount <= 64); | ||
| 2500 | DRFLAC_ASSERT(bitCount > 32); | ||
| 2501 | |||
| 2502 | if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { | ||
| 2503 | return DRFLAC_FALSE; | ||
| 2504 | } | ||
| 2505 | |||
| 2506 | if (!drflac__read_uint32(bs, 32, &resultLo)) { | ||
| 2507 | return DRFLAC_FALSE; | ||
| 2508 | } | ||
| 2509 | |||
| 2510 | *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); | ||
| 2511 | return DRFLAC_TRUE; | ||
| 2512 | } | ||
| 2513 | #endif | ||
| 2514 | |||
| 2515 | /* Function below is unused, but leaving it here in case I need to quickly add it again. */ | ||
| 2516 | #if 0 | ||
| 2517 | static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) | ||
| 2518 | { | ||
| 2519 | drflac_uint64 result; | ||
| 2520 | drflac_uint64 signbit; | ||
| 2521 | |||
| 2522 | DRFLAC_ASSERT(bitCount <= 64); | ||
| 2523 | |||
| 2524 | if (!drflac__read_uint64(bs, bitCount, &result)) { | ||
| 2525 | return DRFLAC_FALSE; | ||
| 2526 | } | ||
| 2527 | |||
| 2528 | signbit = ((result >> (bitCount-1)) & 0x01); | ||
| 2529 | result |= (~signbit + 1) << bitCount; | ||
| 2530 | |||
| 2531 | *pResultOut = (drflac_int64)result; | ||
| 2532 | return DRFLAC_TRUE; | ||
| 2533 | } | ||
| 2534 | #endif | ||
| 2535 | |||
| 2536 | static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) | ||
| 2537 | { | ||
| 2538 | drflac_uint32 result; | ||
| 2539 | |||
| 2540 | DRFLAC_ASSERT(bs != NULL); | ||
| 2541 | DRFLAC_ASSERT(pResult != NULL); | ||
| 2542 | DRFLAC_ASSERT(bitCount > 0); | ||
| 2543 | DRFLAC_ASSERT(bitCount <= 16); | ||
| 2544 | |||
| 2545 | if (!drflac__read_uint32(bs, bitCount, &result)) { | ||
| 2546 | return DRFLAC_FALSE; | ||
| 2547 | } | ||
| 2548 | |||
| 2549 | *pResult = (drflac_uint16)result; | ||
| 2550 | return DRFLAC_TRUE; | ||
| 2551 | } | ||
| 2552 | |||
| 2553 | #if 0 | ||
| 2554 | static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) | ||
| 2555 | { | ||
| 2556 | drflac_int32 result; | ||
| 2557 | |||
| 2558 | DRFLAC_ASSERT(bs != NULL); | ||
| 2559 | DRFLAC_ASSERT(pResult != NULL); | ||
| 2560 | DRFLAC_ASSERT(bitCount > 0); | ||
| 2561 | DRFLAC_ASSERT(bitCount <= 16); | ||
| 2562 | |||
| 2563 | if (!drflac__read_int32(bs, bitCount, &result)) { | ||
| 2564 | return DRFLAC_FALSE; | ||
| 2565 | } | ||
| 2566 | |||
| 2567 | *pResult = (drflac_int16)result; | ||
| 2568 | return DRFLAC_TRUE; | ||
| 2569 | } | ||
| 2570 | #endif | ||
| 2571 | |||
| 2572 | static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) | ||
| 2573 | { | ||
| 2574 | drflac_uint32 result; | ||
| 2575 | |||
| 2576 | DRFLAC_ASSERT(bs != NULL); | ||
| 2577 | DRFLAC_ASSERT(pResult != NULL); | ||
| 2578 | DRFLAC_ASSERT(bitCount > 0); | ||
| 2579 | DRFLAC_ASSERT(bitCount <= 8); | ||
| 2580 | |||
| 2581 | if (!drflac__read_uint32(bs, bitCount, &result)) { | ||
| 2582 | return DRFLAC_FALSE; | ||
| 2583 | } | ||
| 2584 | |||
| 2585 | *pResult = (drflac_uint8)result; | ||
| 2586 | return DRFLAC_TRUE; | ||
| 2587 | } | ||
| 2588 | |||
| 2589 | static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) | ||
| 2590 | { | ||
| 2591 | drflac_int32 result; | ||
| 2592 | |||
| 2593 | DRFLAC_ASSERT(bs != NULL); | ||
| 2594 | DRFLAC_ASSERT(pResult != NULL); | ||
| 2595 | DRFLAC_ASSERT(bitCount > 0); | ||
| 2596 | DRFLAC_ASSERT(bitCount <= 8); | ||
| 2597 | |||
| 2598 | if (!drflac__read_int32(bs, bitCount, &result)) { | ||
| 2599 | return DRFLAC_FALSE; | ||
| 2600 | } | ||
| 2601 | |||
| 2602 | *pResult = (drflac_int8)result; | ||
| 2603 | return DRFLAC_TRUE; | ||
| 2604 | } | ||
| 2605 | |||
| 2606 | |||
| 2607 | static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) | ||
| 2608 | { | ||
| 2609 | if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { | ||
| 2610 | bs->consumedBits += (drflac_uint32)bitsToSeek; | ||
| 2611 | bs->cache <<= bitsToSeek; | ||
| 2612 | return DRFLAC_TRUE; | ||
| 2613 | } else { | ||
| 2614 | /* It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here. */ | ||
| 2615 | bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); | ||
| 2616 | bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); | ||
| 2617 | bs->cache = 0; | ||
| 2618 | |||
| 2619 | /* Simple case. Seek in groups of the same number as bits that fit within a cache line. */ | ||
| 2620 | #ifdef DRFLAC_64BIT | ||
| 2621 | while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { | ||
| 2622 | drflac_uint64 bin; | ||
| 2623 | if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { | ||
| 2624 | return DRFLAC_FALSE; | ||
| 2625 | } | ||
| 2626 | bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); | ||
| 2627 | } | ||
| 2628 | #else | ||
| 2629 | while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { | ||
| 2630 | drflac_uint32 bin; | ||
| 2631 | if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { | ||
| 2632 | return DRFLAC_FALSE; | ||
| 2633 | } | ||
| 2634 | bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); | ||
| 2635 | } | ||
| 2636 | #endif | ||
| 2637 | |||
| 2638 | /* Whole leftover bytes. */ | ||
| 2639 | while (bitsToSeek >= 8) { | ||
| 2640 | drflac_uint8 bin; | ||
| 2641 | if (!drflac__read_uint8(bs, 8, &bin)) { | ||
| 2642 | return DRFLAC_FALSE; | ||
| 2643 | } | ||
| 2644 | bitsToSeek -= 8; | ||
| 2645 | } | ||
| 2646 | |||
| 2647 | /* Leftover bits. */ | ||
| 2648 | if (bitsToSeek > 0) { | ||
| 2649 | drflac_uint8 bin; | ||
| 2650 | if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { | ||
| 2651 | return DRFLAC_FALSE; | ||
| 2652 | } | ||
| 2653 | bitsToSeek = 0; /* <-- Necessary for the assert below. */ | ||
| 2654 | } | ||
| 2655 | |||
| 2656 | DRFLAC_ASSERT(bitsToSeek == 0); | ||
| 2657 | return DRFLAC_TRUE; | ||
| 2658 | } | ||
| 2659 | } | ||
| 2660 | |||
| 2661 | |||
| 2662 | /* This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16. */ | ||
| 2663 | static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) | ||
| 2664 | { | ||
| 2665 | DRFLAC_ASSERT(bs != NULL); | ||
| 2666 | |||
| 2667 | /* | ||
| 2668 | The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first | ||
| 2669 | thing to do is align to the next byte. | ||
| 2670 | */ | ||
| 2671 | if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { | ||
| 2672 | return DRFLAC_FALSE; | ||
| 2673 | } | ||
| 2674 | |||
| 2675 | for (;;) { | ||
| 2676 | drflac_uint8 hi; | ||
| 2677 | |||
| 2678 | #ifndef DR_FLAC_NO_CRC | ||
| 2679 | drflac__reset_crc16(bs); | ||
| 2680 | #endif | ||
| 2681 | |||
| 2682 | if (!drflac__read_uint8(bs, 8, &hi)) { | ||
| 2683 | return DRFLAC_FALSE; | ||
| 2684 | } | ||
| 2685 | |||
| 2686 | if (hi == 0xFF) { | ||
| 2687 | drflac_uint8 lo; | ||
| 2688 | if (!drflac__read_uint8(bs, 6, &lo)) { | ||
| 2689 | return DRFLAC_FALSE; | ||
| 2690 | } | ||
| 2691 | |||
| 2692 | if (lo == 0x3E) { | ||
| 2693 | return DRFLAC_TRUE; | ||
| 2694 | } else { | ||
| 2695 | if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { | ||
| 2696 | return DRFLAC_FALSE; | ||
| 2697 | } | ||
| 2698 | } | ||
| 2699 | } | ||
| 2700 | } | ||
| 2701 | |||
| 2702 | /* Should never get here. */ | ||
| 2703 | /*return DRFLAC_FALSE;*/ | ||
| 2704 | } | ||
| 2705 | |||
| 2706 | |||
| 2707 | #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) | ||
| 2708 | #define DRFLAC_IMPLEMENT_CLZ_LZCNT | ||
| 2709 | #endif | ||
| 2710 | #if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__) | ||
| 2711 | #define DRFLAC_IMPLEMENT_CLZ_MSVC | ||
| 2712 | #endif | ||
| 2713 | #if defined(__WATCOMC__) && defined(__386__) | ||
| 2714 | #define DRFLAC_IMPLEMENT_CLZ_WATCOM | ||
| 2715 | #endif | ||
| 2716 | #ifdef __MRC__ | ||
| 2717 | #include <intrinsics.h> | ||
| 2718 | #define DRFLAC_IMPLEMENT_CLZ_MRC | ||
| 2719 | #endif | ||
| 2720 | |||
| 2721 | static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) | ||
| 2722 | { | ||
| 2723 | drflac_uint32 n; | ||
| 2724 | static drflac_uint32 clz_table_4[] = { | ||
| 2725 | 0, | ||
| 2726 | 4, | ||
| 2727 | 3, 3, | ||
| 2728 | 2, 2, 2, 2, | ||
| 2729 | 1, 1, 1, 1, 1, 1, 1, 1 | ||
| 2730 | }; | ||
| 2731 | |||
| 2732 | if (x == 0) { | ||
| 2733 | return sizeof(x)*8; | ||
| 2734 | } | ||
| 2735 | |||
| 2736 | n = clz_table_4[x >> (sizeof(x)*8 - 4)]; | ||
| 2737 | if (n == 0) { | ||
| 2738 | #ifdef DRFLAC_64BIT | ||
| 2739 | if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } | ||
| 2740 | if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } | ||
| 2741 | if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } | ||
| 2742 | if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } | ||
| 2743 | #else | ||
| 2744 | if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } | ||
| 2745 | if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } | ||
| 2746 | if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } | ||
| 2747 | #endif | ||
| 2748 | n += clz_table_4[x >> (sizeof(x)*8 - 4)]; | ||
| 2749 | } | ||
| 2750 | |||
| 2751 | return n - 1; | ||
| 2752 | } | ||
| 2753 | |||
| 2754 | #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT | ||
| 2755 | static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) | ||
| 2756 | { | ||
| 2757 | /* Fast compile time check for ARM. */ | ||
| 2758 | #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) | ||
| 2759 | return DRFLAC_TRUE; | ||
| 2760 | #elif defined(__MRC__) | ||
| 2761 | return DRFLAC_TRUE; | ||
| 2762 | #else | ||
| 2763 | /* If the compiler itself does not support the intrinsic then we'll need to return false. */ | ||
| 2764 | #ifdef DRFLAC_HAS_LZCNT_INTRINSIC | ||
| 2765 | return drflac__gIsLZCNTSupported; | ||
| 2766 | #else | ||
| 2767 | return DRFLAC_FALSE; | ||
| 2768 | #endif | ||
| 2769 | #endif | ||
| 2770 | } | ||
| 2771 | |||
| 2772 | static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) | ||
| 2773 | { | ||
| 2774 | /* | ||
| 2775 | It's critical for competitive decoding performance that this function be highly optimal. With MSVC we can use the __lzcnt64() and __lzcnt() intrinsics | ||
| 2776 | to achieve good performance, however on GCC and Clang it's a little bit more annoying. The __builtin_clzl() and __builtin_clzll() intrinsics leave | ||
| 2777 | it undefined as to the return value when `x` is 0. We need this to be well defined as returning 32 or 64, depending on whether or not it's a 32- or | ||
| 2778 | 64-bit build. To work around this we would need to add a conditional to check for the x = 0 case, but this creates unnecessary inefficiency. To work | ||
| 2779 | around this problem I have written some inline assembly to emit the LZCNT (x86) or CLZ (ARM) instruction directly which removes the need to include | ||
| 2780 | the conditional. This has worked well in the past, but for some reason Clang's MSVC compatible driver, clang-cl, does not seem to be handling this | ||
| 2781 | in the same way as the normal Clang driver. It seems that `clang-cl` is just outputting the wrong results sometimes, maybe due to some register | ||
| 2782 | getting clobbered? | ||
| 2783 | |||
| 2784 | I'm not sure if this is a bug with dr_flac's inlined assembly (most likely), a bug in `clang-cl` or just a misunderstanding on my part with inline | ||
| 2785 | assembly rules for `clang-cl`. If somebody can identify an error in dr_flac's inlined assembly I'm happy to get that fixed. | ||
| 2786 | |||
| 2787 | Fortunately there is an easy workaround for this. Clang implements MSVC-specific intrinsics for compatibility. It also defines _MSC_VER for extra | ||
| 2788 | compatibility. We can therefore just check for _MSC_VER and use the MSVC intrinsic which, fortunately for us, Clang supports. It would still be nice | ||
| 2789 | to know how to fix the inlined assembly for correctness sake, however. | ||
| 2790 | */ | ||
| 2791 | |||
| 2792 | #if defined(_MSC_VER) /*&& !defined(__clang__)*/ /* <-- Intentionally wanting Clang to use the MSVC __lzcnt64/__lzcnt intrinsics due to above ^. */ | ||
| 2793 | #ifdef DRFLAC_64BIT | ||
| 2794 | return (drflac_uint32)__lzcnt64(x); | ||
| 2795 | #else | ||
| 2796 | return (drflac_uint32)__lzcnt(x); | ||
| 2797 | #endif | ||
| 2798 | #else | ||
| 2799 | #if defined(__GNUC__) || defined(__clang__) | ||
| 2800 | #if defined(DRFLAC_X64) | ||
| 2801 | { | ||
| 2802 | drflac_uint64 r; | ||
| 2803 | __asm__ __volatile__ ( | ||
| 2804 | "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" | ||
| 2805 | ); | ||
| 2806 | |||
| 2807 | return (drflac_uint32)r; | ||
| 2808 | } | ||
| 2809 | #elif defined(DRFLAC_X86) | ||
| 2810 | { | ||
| 2811 | drflac_uint32 r; | ||
| 2812 | __asm__ __volatile__ ( | ||
| 2813 | "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" | ||
| 2814 | ); | ||
| 2815 | |||
| 2816 | return r; | ||
| 2817 | } | ||
| 2818 | #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */ | ||
| 2819 | { | ||
| 2820 | unsigned int r; | ||
| 2821 | __asm__ __volatile__ ( | ||
| 2822 | #if defined(DRFLAC_64BIT) | ||
| 2823 | "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ | ||
| 2824 | #else | ||
| 2825 | "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) | ||
| 2826 | #endif | ||
| 2827 | ); | ||
| 2828 | |||
| 2829 | return r; | ||
| 2830 | } | ||
| 2831 | #else | ||
| 2832 | if (x == 0) { | ||
| 2833 | return sizeof(x)*8; | ||
| 2834 | } | ||
| 2835 | #ifdef DRFLAC_64BIT | ||
| 2836 | return (drflac_uint32)__builtin_clzll((drflac_uint64)x); | ||
| 2837 | #else | ||
| 2838 | return (drflac_uint32)__builtin_clzl((drflac_uint32)x); | ||
| 2839 | #endif | ||
| 2840 | #endif | ||
| 2841 | #else | ||
| 2842 | /* Unsupported compiler. */ | ||
| 2843 | #error "This compiler does not support the lzcnt intrinsic." | ||
| 2844 | #endif | ||
| 2845 | #endif | ||
| 2846 | } | ||
| 2847 | #endif | ||
| 2848 | |||
| 2849 | #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC | ||
| 2850 | #include <intrin.h> /* For BitScanReverse(). */ | ||
| 2851 | |||
| 2852 | static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) | ||
| 2853 | { | ||
| 2854 | drflac_uint32 n; | ||
| 2855 | |||
| 2856 | if (x == 0) { | ||
| 2857 | return sizeof(x)*8; | ||
| 2858 | } | ||
| 2859 | |||
| 2860 | #ifdef DRFLAC_64BIT | ||
| 2861 | _BitScanReverse64((unsigned long*)&n, x); | ||
| 2862 | #else | ||
| 2863 | _BitScanReverse((unsigned long*)&n, x); | ||
| 2864 | #endif | ||
| 2865 | return sizeof(x)*8 - n - 1; | ||
| 2866 | } | ||
| 2867 | #endif | ||
| 2868 | |||
| 2869 | #ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM | ||
| 2870 | static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); | ||
| 2871 | #ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT | ||
| 2872 | /* Use the LZCNT instruction (only available on some processors since the 2010s). */ | ||
| 2873 | #pragma aux drflac__clz_watcom_lzcnt = \ | ||
| 2874 | "db 0F3h, 0Fh, 0BDh, 0C0h" /* lzcnt eax, eax */ \ | ||
| 2875 | parm [eax] \ | ||
| 2876 | value [eax] \ | ||
| 2877 | modify nomemory; | ||
| 2878 | #else | ||
| 2879 | /* Use the 386+-compatible implementation. */ | ||
| 2880 | #pragma aux drflac__clz_watcom = \ | ||
| 2881 | "bsr eax, eax" \ | ||
| 2882 | "xor eax, 31" \ | ||
| 2883 | parm [eax] nomemory \ | ||
| 2884 | value [eax] \ | ||
| 2885 | modify exact [eax] nomemory; | ||
| 2886 | #endif | ||
| 2887 | #endif | ||
| 2888 | |||
| 2889 | static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) | ||
| 2890 | { | ||
| 2891 | #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT | ||
| 2892 | if (drflac__is_lzcnt_supported()) { | ||
| 2893 | return drflac__clz_lzcnt(x); | ||
| 2894 | } else | ||
| 2895 | #endif | ||
| 2896 | { | ||
| 2897 | #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC | ||
| 2898 | return drflac__clz_msvc(x); | ||
| 2899 | #elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) | ||
| 2900 | return drflac__clz_watcom_lzcnt(x); | ||
| 2901 | #elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM) | ||
| 2902 | return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x); | ||
| 2903 | #elif defined(__MRC__) | ||
| 2904 | return __cntlzw(x); | ||
| 2905 | #else | ||
| 2906 | return drflac__clz_software(x); | ||
| 2907 | #endif | ||
| 2908 | } | ||
| 2909 | } | ||
| 2910 | |||
| 2911 | |||
| 2912 | static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) | ||
| 2913 | { | ||
| 2914 | drflac_uint32 zeroCounter = 0; | ||
| 2915 | drflac_uint32 setBitOffsetPlus1; | ||
| 2916 | |||
| 2917 | while (bs->cache == 0) { | ||
| 2918 | zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); | ||
| 2919 | if (!drflac__reload_cache(bs)) { | ||
| 2920 | return DRFLAC_FALSE; | ||
| 2921 | } | ||
| 2922 | } | ||
| 2923 | |||
| 2924 | if (bs->cache == 1) { | ||
| 2925 | /* Not catching this would lead to undefined behaviour: a shift of a 32-bit number by 32 or more is undefined */ | ||
| 2926 | *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1; | ||
| 2927 | if (!drflac__reload_cache(bs)) { | ||
| 2928 | return DRFLAC_FALSE; | ||
| 2929 | } | ||
| 2930 | |||
| 2931 | return DRFLAC_TRUE; | ||
| 2932 | } | ||
| 2933 | |||
| 2934 | setBitOffsetPlus1 = drflac__clz(bs->cache); | ||
| 2935 | setBitOffsetPlus1 += 1; | ||
| 2936 | |||
| 2937 | if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { | ||
| 2938 | /* This happens when we get to end of stream */ | ||
| 2939 | return DRFLAC_FALSE; | ||
| 2940 | } | ||
| 2941 | |||
| 2942 | bs->consumedBits += setBitOffsetPlus1; | ||
| 2943 | bs->cache <<= setBitOffsetPlus1; | ||
| 2944 | |||
| 2945 | *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; | ||
| 2946 | return DRFLAC_TRUE; | ||
| 2947 | } | ||
| 2948 | |||
| 2949 | |||
| 2950 | |||
| 2951 | static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart) | ||
| 2952 | { | ||
| 2953 | DRFLAC_ASSERT(bs != NULL); | ||
| 2954 | DRFLAC_ASSERT(offsetFromStart > 0); | ||
| 2955 | |||
| 2956 | /* | ||
| 2957 | Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which | ||
| 2958 | is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit. | ||
| 2959 | To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder. | ||
| 2960 | */ | ||
| 2961 | if (offsetFromStart > 0x7FFFFFFF) { | ||
| 2962 | drflac_uint64 bytesRemaining = offsetFromStart; | ||
| 2963 | if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { | ||
| 2964 | return DRFLAC_FALSE; | ||
| 2965 | } | ||
| 2966 | bytesRemaining -= 0x7FFFFFFF; | ||
| 2967 | |||
| 2968 | while (bytesRemaining > 0x7FFFFFFF) { | ||
| 2969 | if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { | ||
| 2970 | return DRFLAC_FALSE; | ||
| 2971 | } | ||
| 2972 | bytesRemaining -= 0x7FFFFFFF; | ||
| 2973 | } | ||
| 2974 | |||
| 2975 | if (bytesRemaining > 0) { | ||
| 2976 | if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { | ||
| 2977 | return DRFLAC_FALSE; | ||
| 2978 | } | ||
| 2979 | } | ||
| 2980 | } else { | ||
| 2981 | if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { | ||
| 2982 | return DRFLAC_FALSE; | ||
| 2983 | } | ||
| 2984 | } | ||
| 2985 | |||
| 2986 | /* The cache should be reset to force a reload of fresh data from the client. */ | ||
| 2987 | drflac__reset_cache(bs); | ||
| 2988 | return DRFLAC_TRUE; | ||
| 2989 | } | ||
| 2990 | |||
| 2991 | |||
| 2992 | static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) | ||
| 2993 | { | ||
| 2994 | drflac_uint8 crc; | ||
| 2995 | drflac_uint64 result; | ||
| 2996 | drflac_uint8 utf8[7] = {0}; | ||
| 2997 | int byteCount; | ||
| 2998 | int i; | ||
| 2999 | |||
| 3000 | DRFLAC_ASSERT(bs != NULL); | ||
| 3001 | DRFLAC_ASSERT(pNumberOut != NULL); | ||
| 3002 | DRFLAC_ASSERT(pCRCOut != NULL); | ||
| 3003 | |||
| 3004 | crc = *pCRCOut; | ||
| 3005 | |||
| 3006 | if (!drflac__read_uint8(bs, 8, utf8)) { | ||
| 3007 | *pNumberOut = 0; | ||
| 3008 | return DRFLAC_AT_END; | ||
| 3009 | } | ||
| 3010 | crc = drflac_crc8(crc, utf8[0], 8); | ||
| 3011 | |||
| 3012 | if ((utf8[0] & 0x80) == 0) { | ||
| 3013 | *pNumberOut = utf8[0]; | ||
| 3014 | *pCRCOut = crc; | ||
| 3015 | return DRFLAC_SUCCESS; | ||
| 3016 | } | ||
| 3017 | |||
| 3018 | /*byteCount = 1;*/ | ||
| 3019 | if ((utf8[0] & 0xE0) == 0xC0) { | ||
| 3020 | byteCount = 2; | ||
| 3021 | } else if ((utf8[0] & 0xF0) == 0xE0) { | ||
| 3022 | byteCount = 3; | ||
| 3023 | } else if ((utf8[0] & 0xF8) == 0xF0) { | ||
| 3024 | byteCount = 4; | ||
| 3025 | } else if ((utf8[0] & 0xFC) == 0xF8) { | ||
| 3026 | byteCount = 5; | ||
| 3027 | } else if ((utf8[0] & 0xFE) == 0xFC) { | ||
| 3028 | byteCount = 6; | ||
| 3029 | } else if ((utf8[0] & 0xFF) == 0xFE) { | ||
| 3030 | byteCount = 7; | ||
| 3031 | } else { | ||
| 3032 | *pNumberOut = 0; | ||
| 3033 | return DRFLAC_CRC_MISMATCH; /* Bad UTF-8 encoding. */ | ||
| 3034 | } | ||
| 3035 | |||
| 3036 | /* Read extra bytes. */ | ||
| 3037 | DRFLAC_ASSERT(byteCount > 1); | ||
| 3038 | |||
| 3039 | result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); | ||
| 3040 | for (i = 1; i < byteCount; ++i) { | ||
| 3041 | if (!drflac__read_uint8(bs, 8, utf8 + i)) { | ||
| 3042 | *pNumberOut = 0; | ||
| 3043 | return DRFLAC_AT_END; | ||
| 3044 | } | ||
| 3045 | crc = drflac_crc8(crc, utf8[i], 8); | ||
| 3046 | |||
| 3047 | result = (result << 6) | (utf8[i] & 0x3F); | ||
| 3048 | } | ||
| 3049 | |||
| 3050 | *pNumberOut = result; | ||
| 3051 | *pCRCOut = crc; | ||
| 3052 | return DRFLAC_SUCCESS; | ||
| 3053 | } | ||
| 3054 | |||
| 3055 | |||
| 3056 | static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) | ||
| 3057 | { | ||
| 3058 | #if 1 /* Needs optimizing. */ | ||
| 3059 | drflac_uint32 result = 0; | ||
| 3060 | while (x > 0) { | ||
| 3061 | result += 1; | ||
| 3062 | x >>= 1; | ||
| 3063 | } | ||
| 3064 | |||
| 3065 | return result; | ||
| 3066 | #endif | ||
| 3067 | } | ||
| 3068 | |||
| 3069 | static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision) | ||
| 3070 | { | ||
| 3071 | /* https://web.archive.org/web/20220205005724/https://github.com/ietf-wg-cellar/flac-specification/blob/37a49aa48ba4ba12e8757badfc59c0df35435fec/rfc_backmatter.md */ | ||
| 3072 | return bitsPerSample + precision + drflac__ilog2_u32(order) > 32; | ||
| 3073 | } | ||
| 3074 | |||
| 3075 | |||
| 3076 | /* | ||
| 3077 | The next two functions are responsible for calculating the prediction. | ||
| 3078 | |||
| 3079 | When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's | ||
| 3080 | safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16. | ||
| 3081 | */ | ||
| 3082 | #if defined(__clang__) | ||
| 3083 | __attribute__((no_sanitize("signed-integer-overflow"))) | ||
| 3084 | #endif | ||
| 3085 | static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) | ||
| 3086 | { | ||
| 3087 | drflac_int32 prediction = 0; | ||
| 3088 | |||
| 3089 | DRFLAC_ASSERT(order <= 32); | ||
| 3090 | |||
| 3091 | /* 32-bit version. */ | ||
| 3092 | |||
| 3093 | /* VC++ optimizes this to a single jmp. I've not yet verified this for other compilers. */ | ||
| 3094 | switch (order) | ||
| 3095 | { | ||
| 3096 | case 32: prediction += coefficients[31] * pDecodedSamples[-32]; | ||
| 3097 | case 31: prediction += coefficients[30] * pDecodedSamples[-31]; | ||
| 3098 | case 30: prediction += coefficients[29] * pDecodedSamples[-30]; | ||
| 3099 | case 29: prediction += coefficients[28] * pDecodedSamples[-29]; | ||
| 3100 | case 28: prediction += coefficients[27] * pDecodedSamples[-28]; | ||
| 3101 | case 27: prediction += coefficients[26] * pDecodedSamples[-27]; | ||
| 3102 | case 26: prediction += coefficients[25] * pDecodedSamples[-26]; | ||
| 3103 | case 25: prediction += coefficients[24] * pDecodedSamples[-25]; | ||
| 3104 | case 24: prediction += coefficients[23] * pDecodedSamples[-24]; | ||
| 3105 | case 23: prediction += coefficients[22] * pDecodedSamples[-23]; | ||
| 3106 | case 22: prediction += coefficients[21] * pDecodedSamples[-22]; | ||
| 3107 | case 21: prediction += coefficients[20] * pDecodedSamples[-21]; | ||
| 3108 | case 20: prediction += coefficients[19] * pDecodedSamples[-20]; | ||
| 3109 | case 19: prediction += coefficients[18] * pDecodedSamples[-19]; | ||
| 3110 | case 18: prediction += coefficients[17] * pDecodedSamples[-18]; | ||
| 3111 | case 17: prediction += coefficients[16] * pDecodedSamples[-17]; | ||
| 3112 | case 16: prediction += coefficients[15] * pDecodedSamples[-16]; | ||
| 3113 | case 15: prediction += coefficients[14] * pDecodedSamples[-15]; | ||
| 3114 | case 14: prediction += coefficients[13] * pDecodedSamples[-14]; | ||
| 3115 | case 13: prediction += coefficients[12] * pDecodedSamples[-13]; | ||
| 3116 | case 12: prediction += coefficients[11] * pDecodedSamples[-12]; | ||
| 3117 | case 11: prediction += coefficients[10] * pDecodedSamples[-11]; | ||
| 3118 | case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; | ||
| 3119 | case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; | ||
| 3120 | case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; | ||
| 3121 | case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; | ||
| 3122 | case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; | ||
| 3123 | case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; | ||
| 3124 | case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; | ||
| 3125 | case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; | ||
| 3126 | case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; | ||
| 3127 | case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; | ||
| 3128 | } | ||
| 3129 | |||
| 3130 | return (drflac_int32)(prediction >> shift); | ||
| 3131 | } | ||
| 3132 | |||
| 3133 | static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) | ||
| 3134 | { | ||
| 3135 | drflac_int64 prediction; | ||
| 3136 | |||
| 3137 | DRFLAC_ASSERT(order <= 32); | ||
| 3138 | |||
| 3139 | /* 64-bit version. */ | ||
| 3140 | |||
| 3141 | /* This method is faster on the 32-bit build when compiling with VC++. See note below. */ | ||
| 3142 | #ifndef DRFLAC_64BIT | ||
| 3143 | if (order == 8) | ||
| 3144 | { | ||
| 3145 | prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; | ||
| 3146 | prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; | ||
| 3147 | prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; | ||
| 3148 | prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; | ||
| 3149 | prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; | ||
| 3150 | prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; | ||
| 3151 | prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; | ||
| 3152 | prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; | ||
| 3153 | } | ||
| 3154 | else if (order == 7) | ||
| 3155 | { | ||
| 3156 | prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; | ||
| 3157 | prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; | ||
| 3158 | prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; | ||
| 3159 | prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; | ||
| 3160 | prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; | ||
| 3161 | prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; | ||
| 3162 | prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; | ||
| 3163 | } | ||
| 3164 | else if (order == 3) | ||
| 3165 | { | ||
| 3166 | prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; | ||
| 3167 | prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; | ||
| 3168 | prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; | ||
| 3169 | } | ||
| 3170 | else if (order == 6) | ||
| 3171 | { | ||
| 3172 | prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; | ||
| 3173 | prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; | ||
| 3174 | prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; | ||
| 3175 | prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; | ||
| 3176 | prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; | ||
| 3177 | prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; | ||
| 3178 | } | ||
| 3179 | else if (order == 5) | ||
| 3180 | { | ||
| 3181 | prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; | ||
| 3182 | prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; | ||
| 3183 | prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; | ||
| 3184 | prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; | ||
| 3185 | prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; | ||
| 3186 | } | ||
| 3187 | else if (order == 4) | ||
| 3188 | { | ||
| 3189 | prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; | ||
| 3190 | prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; | ||
| 3191 | prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; | ||
| 3192 | prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; | ||
| 3193 | } | ||
| 3194 | else if (order == 12) | ||
| 3195 | { | ||
| 3196 | prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; | ||
| 3197 | prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; | ||
| 3198 | prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; | ||
| 3199 | prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; | ||
| 3200 | prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; | ||
| 3201 | prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; | ||
| 3202 | prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; | ||
| 3203 | prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; | ||
| 3204 | prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; | ||
| 3205 | prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; | ||
| 3206 | prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; | ||
| 3207 | prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; | ||
| 3208 | } | ||
| 3209 | else if (order == 2) | ||
| 3210 | { | ||
| 3211 | prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; | ||
| 3212 | prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; | ||
| 3213 | } | ||
| 3214 | else if (order == 1) | ||
| 3215 | { | ||
| 3216 | prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; | ||
| 3217 | } | ||
| 3218 | else if (order == 10) | ||
| 3219 | { | ||
| 3220 | prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; | ||
| 3221 | prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; | ||
| 3222 | prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; | ||
| 3223 | prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; | ||
| 3224 | prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; | ||
| 3225 | prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; | ||
| 3226 | prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; | ||
| 3227 | prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; | ||
| 3228 | prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; | ||
| 3229 | prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; | ||
| 3230 | } | ||
| 3231 | else if (order == 9) | ||
| 3232 | { | ||
| 3233 | prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; | ||
| 3234 | prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; | ||
| 3235 | prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; | ||
| 3236 | prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; | ||
| 3237 | prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; | ||
| 3238 | prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; | ||
| 3239 | prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; | ||
| 3240 | prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; | ||
| 3241 | prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; | ||
| 3242 | } | ||
| 3243 | else if (order == 11) | ||
| 3244 | { | ||
| 3245 | prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; | ||
| 3246 | prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; | ||
| 3247 | prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; | ||
| 3248 | prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; | ||
| 3249 | prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; | ||
| 3250 | prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; | ||
| 3251 | prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; | ||
| 3252 | prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; | ||
| 3253 | prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; | ||
| 3254 | prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; | ||
| 3255 | prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; | ||
| 3256 | } | ||
| 3257 | else | ||
| 3258 | { | ||
| 3259 | int j; | ||
| 3260 | |||
| 3261 | prediction = 0; | ||
| 3262 | for (j = 0; j < (int)order; ++j) { | ||
| 3263 | prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; | ||
| 3264 | } | ||
| 3265 | } | ||
| 3266 | #endif | ||
| 3267 | |||
| 3268 | /* | ||
| 3269 | VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some | ||
| 3270 | reason. The ugly version above is faster so we'll just switch between the two depending on the target platform. | ||
| 3271 | */ | ||
| 3272 | #ifdef DRFLAC_64BIT | ||
| 3273 | prediction = 0; | ||
| 3274 | switch (order) | ||
| 3275 | { | ||
| 3276 | case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32]; | ||
| 3277 | case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31]; | ||
| 3278 | case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30]; | ||
| 3279 | case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29]; | ||
| 3280 | case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28]; | ||
| 3281 | case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27]; | ||
| 3282 | case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26]; | ||
| 3283 | case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25]; | ||
| 3284 | case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24]; | ||
| 3285 | case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23]; | ||
| 3286 | case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22]; | ||
| 3287 | case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21]; | ||
| 3288 | case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20]; | ||
| 3289 | case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19]; | ||
| 3290 | case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18]; | ||
| 3291 | case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17]; | ||
| 3292 | case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16]; | ||
| 3293 | case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15]; | ||
| 3294 | case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14]; | ||
| 3295 | case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13]; | ||
| 3296 | case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; | ||
| 3297 | case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; | ||
| 3298 | case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10]; | ||
| 3299 | case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9]; | ||
| 3300 | case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8]; | ||
| 3301 | case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7]; | ||
| 3302 | case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6]; | ||
| 3303 | case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5]; | ||
| 3304 | case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4]; | ||
| 3305 | case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3]; | ||
| 3306 | case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2]; | ||
| 3307 | case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1]; | ||
| 3308 | } | ||
| 3309 | #endif | ||
| 3310 | |||
| 3311 | return (drflac_int32)(prediction >> shift); | ||
| 3312 | } | ||
| 3313 | |||
| 3314 | |||
| 3315 | #if 0 | ||
| 3316 | /* | ||
| 3317 | Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the | ||
| 3318 | sake of readability and should only be used as a reference. | ||
| 3319 | */ | ||
| 3320 | static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) | ||
| 3321 | { | ||
| 3322 | drflac_uint32 i; | ||
| 3323 | |||
| 3324 | DRFLAC_ASSERT(bs != NULL); | ||
| 3325 | DRFLAC_ASSERT(pSamplesOut != NULL); | ||
| 3326 | |||
| 3327 | for (i = 0; i < count; ++i) { | ||
| 3328 | drflac_uint32 zeroCounter = 0; | ||
| 3329 | for (;;) { | ||
| 3330 | drflac_uint8 bit; | ||
| 3331 | if (!drflac__read_uint8(bs, 1, &bit)) { | ||
| 3332 | return DRFLAC_FALSE; | ||
| 3333 | } | ||
| 3334 | |||
| 3335 | if (bit == 0) { | ||
| 3336 | zeroCounter += 1; | ||
| 3337 | } else { | ||
| 3338 | break; | ||
| 3339 | } | ||
| 3340 | } | ||
| 3341 | |||
| 3342 | drflac_uint32 decodedRice; | ||
| 3343 | if (riceParam > 0) { | ||
| 3344 | if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { | ||
| 3345 | return DRFLAC_FALSE; | ||
| 3346 | } | ||
| 3347 | } else { | ||
| 3348 | decodedRice = 0; | ||
| 3349 | } | ||
| 3350 | |||
| 3351 | decodedRice |= (zeroCounter << riceParam); | ||
| 3352 | if ((decodedRice & 0x01)) { | ||
| 3353 | decodedRice = ~(decodedRice >> 1); | ||
| 3354 | } else { | ||
| 3355 | decodedRice = (decodedRice >> 1); | ||
| 3356 | } | ||
| 3357 | |||
| 3358 | |||
| 3359 | if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { | ||
| 3360 | pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); | ||
| 3361 | } else { | ||
| 3362 | pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); | ||
| 3363 | } | ||
| 3364 | } | ||
| 3365 | |||
| 3366 | return DRFLAC_TRUE; | ||
| 3367 | } | ||
| 3368 | #endif | ||
| 3369 | |||
| 3370 | #if 0 | ||
| 3371 | static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) | ||
| 3372 | { | ||
| 3373 | drflac_uint32 zeroCounter = 0; | ||
| 3374 | drflac_uint32 decodedRice; | ||
| 3375 | |||
| 3376 | for (;;) { | ||
| 3377 | drflac_uint8 bit; | ||
| 3378 | if (!drflac__read_uint8(bs, 1, &bit)) { | ||
| 3379 | return DRFLAC_FALSE; | ||
| 3380 | } | ||
| 3381 | |||
| 3382 | if (bit == 0) { | ||
| 3383 | zeroCounter += 1; | ||
| 3384 | } else { | ||
| 3385 | break; | ||
| 3386 | } | ||
| 3387 | } | ||
| 3388 | |||
| 3389 | if (riceParam > 0) { | ||
| 3390 | if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { | ||
| 3391 | return DRFLAC_FALSE; | ||
| 3392 | } | ||
| 3393 | } else { | ||
| 3394 | decodedRice = 0; | ||
| 3395 | } | ||
| 3396 | |||
| 3397 | *pZeroCounterOut = zeroCounter; | ||
| 3398 | *pRiceParamPartOut = decodedRice; | ||
| 3399 | return DRFLAC_TRUE; | ||
| 3400 | } | ||
| 3401 | #endif | ||
| 3402 | |||
| 3403 | #if 0 | ||
| 3404 | static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) | ||
| 3405 | { | ||
| 3406 | drflac_cache_t riceParamMask; | ||
| 3407 | drflac_uint32 zeroCounter; | ||
| 3408 | drflac_uint32 setBitOffsetPlus1; | ||
| 3409 | drflac_uint32 riceParamPart; | ||
| 3410 | drflac_uint32 riceLength; | ||
| 3411 | |||
| 3412 | DRFLAC_ASSERT(riceParam > 0); /* <-- riceParam should never be 0. drflac__read_rice_parts__param_equals_zero() should be used instead for this case. */ | ||
| 3413 | |||
| 3414 | riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); | ||
| 3415 | |||
| 3416 | zeroCounter = 0; | ||
| 3417 | while (bs->cache == 0) { | ||
| 3418 | zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); | ||
| 3419 | if (!drflac__reload_cache(bs)) { | ||
| 3420 | return DRFLAC_FALSE; | ||
| 3421 | } | ||
| 3422 | } | ||
| 3423 | |||
| 3424 | setBitOffsetPlus1 = drflac__clz(bs->cache); | ||
| 3425 | zeroCounter += setBitOffsetPlus1; | ||
| 3426 | setBitOffsetPlus1 += 1; | ||
| 3427 | |||
| 3428 | riceLength = setBitOffsetPlus1 + riceParam; | ||
| 3429 | if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { | ||
| 3430 | riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); | ||
| 3431 | |||
| 3432 | bs->consumedBits += riceLength; | ||
| 3433 | bs->cache <<= riceLength; | ||
| 3434 | } else { | ||
| 3435 | drflac_uint32 bitCountLo; | ||
| 3436 | drflac_cache_t resultHi; | ||
| 3437 | |||
| 3438 | bs->consumedBits += riceLength; | ||
| 3439 | bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); /* <-- Equivalent to "if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { bs->cache <<= setBitOffsetPlus1; }" */ | ||
| 3440 | |||
| 3441 | /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ | ||
| 3442 | bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); | ||
| 3443 | resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); /* <-- Use DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE() if ever this function allows riceParam=0. */ | ||
| 3444 | |||
| 3445 | if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { | ||
| 3446 | #ifndef DR_FLAC_NO_CRC | ||
| 3447 | drflac__update_crc16(bs); | ||
| 3448 | #endif | ||
| 3449 | bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); | ||
| 3450 | bs->consumedBits = 0; | ||
| 3451 | #ifndef DR_FLAC_NO_CRC | ||
| 3452 | bs->crc16Cache = bs->cache; | ||
| 3453 | #endif | ||
| 3454 | } else { | ||
| 3455 | /* Slow path. We need to fetch more data from the client. */ | ||
| 3456 | if (!drflac__reload_cache(bs)) { | ||
| 3457 | return DRFLAC_FALSE; | ||
| 3458 | } | ||
| 3459 | if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { | ||
| 3460 | /* This happens when we get to end of stream */ | ||
| 3461 | return DRFLAC_FALSE; | ||
| 3462 | } | ||
| 3463 | } | ||
| 3464 | |||
| 3465 | riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); | ||
| 3466 | |||
| 3467 | bs->consumedBits += bitCountLo; | ||
| 3468 | bs->cache <<= bitCountLo; | ||
| 3469 | } | ||
| 3470 | |||
| 3471 | pZeroCounterOut[0] = zeroCounter; | ||
| 3472 | pRiceParamPartOut[0] = riceParamPart; | ||
| 3473 | |||
| 3474 | return DRFLAC_TRUE; | ||
| 3475 | } | ||
| 3476 | #endif | ||
| 3477 | |||
| 3478 | static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) | ||
| 3479 | { | ||
| 3480 | drflac_uint32 riceParamPlus1 = riceParam + 1; | ||
| 3481 | /*drflac_cache_t riceParamPlus1Mask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParamPlus1);*/ | ||
| 3482 | drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); | ||
| 3483 | drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; | ||
| 3484 | |||
| 3485 | /* | ||
| 3486 | The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have | ||
| 3487 | no idea how this will work in practice... | ||
| 3488 | */ | ||
| 3489 | drflac_cache_t bs_cache = bs->cache; | ||
| 3490 | drflac_uint32 bs_consumedBits = bs->consumedBits; | ||
| 3491 | |||
| 3492 | /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */ | ||
| 3493 | drflac_uint32 lzcount = drflac__clz(bs_cache); | ||
| 3494 | if (lzcount < sizeof(bs_cache)*8) { | ||
| 3495 | pZeroCounterOut[0] = lzcount; | ||
| 3496 | |||
| 3497 | /* | ||
| 3498 | It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting | ||
| 3499 | this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled | ||
| 3500 | outside of this function at a higher level. | ||
| 3501 | */ | ||
| 3502 | extract_rice_param_part: | ||
| 3503 | bs_cache <<= lzcount; | ||
| 3504 | bs_consumedBits += lzcount; | ||
| 3505 | |||
| 3506 | if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { | ||
| 3507 | /* Getting here means the rice parameter part is wholly contained within the current cache line. */ | ||
| 3508 | pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); | ||
| 3509 | bs_cache <<= riceParamPlus1; | ||
| 3510 | bs_consumedBits += riceParamPlus1; | ||
| 3511 | } else { | ||
| 3512 | drflac_uint32 riceParamPartHi; | ||
| 3513 | drflac_uint32 riceParamPartLo; | ||
| 3514 | drflac_uint32 riceParamPartLoBitCount; | ||
| 3515 | |||
| 3516 | /* | ||
| 3517 | Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache | ||
| 3518 | line, reload the cache, and then combine it with the head of the next cache line. | ||
| 3519 | */ | ||
| 3520 | |||
| 3521 | /* Grab the high part of the rice parameter part. */ | ||
| 3522 | riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); | ||
| 3523 | |||
| 3524 | /* Before reloading the cache we need to grab the size in bits of the low part. */ | ||
| 3525 | riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; | ||
| 3526 | DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); | ||
| 3527 | |||
| 3528 | /* Now reload the cache. */ | ||
| 3529 | if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { | ||
| 3530 | #ifndef DR_FLAC_NO_CRC | ||
| 3531 | drflac__update_crc16(bs); | ||
| 3532 | #endif | ||
| 3533 | bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); | ||
| 3534 | bs_consumedBits = riceParamPartLoBitCount; | ||
| 3535 | #ifndef DR_FLAC_NO_CRC | ||
| 3536 | bs->crc16Cache = bs_cache; | ||
| 3537 | #endif | ||
| 3538 | } else { | ||
| 3539 | /* Slow path. We need to fetch more data from the client. */ | ||
| 3540 | if (!drflac__reload_cache(bs)) { | ||
| 3541 | return DRFLAC_FALSE; | ||
| 3542 | } | ||
| 3543 | if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { | ||
| 3544 | /* This happens when we get to end of stream */ | ||
| 3545 | return DRFLAC_FALSE; | ||
| 3546 | } | ||
| 3547 | |||
| 3548 | bs_cache = bs->cache; | ||
| 3549 | bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; | ||
| 3550 | } | ||
| 3551 | |||
| 3552 | /* We should now have enough information to construct the rice parameter part. */ | ||
| 3553 | riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); | ||
| 3554 | pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; | ||
| 3555 | |||
| 3556 | bs_cache <<= riceParamPartLoBitCount; | ||
| 3557 | } | ||
| 3558 | } else { | ||
| 3559 | /* | ||
| 3560 | Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call | ||
| 3561 | to drflac__clz() and we need to reload the cache. | ||
| 3562 | */ | ||
| 3563 | drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); | ||
| 3564 | for (;;) { | ||
| 3565 | if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { | ||
| 3566 | #ifndef DR_FLAC_NO_CRC | ||
| 3567 | drflac__update_crc16(bs); | ||
| 3568 | #endif | ||
| 3569 | bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); | ||
| 3570 | bs_consumedBits = 0; | ||
| 3571 | #ifndef DR_FLAC_NO_CRC | ||
| 3572 | bs->crc16Cache = bs_cache; | ||
| 3573 | #endif | ||
| 3574 | } else { | ||
| 3575 | /* Slow path. We need to fetch more data from the client. */ | ||
| 3576 | if (!drflac__reload_cache(bs)) { | ||
| 3577 | return DRFLAC_FALSE; | ||
| 3578 | } | ||
| 3579 | |||
| 3580 | bs_cache = bs->cache; | ||
| 3581 | bs_consumedBits = bs->consumedBits; | ||
| 3582 | } | ||
| 3583 | |||
| 3584 | lzcount = drflac__clz(bs_cache); | ||
| 3585 | zeroCounter += lzcount; | ||
| 3586 | |||
| 3587 | if (lzcount < sizeof(bs_cache)*8) { | ||
| 3588 | break; | ||
| 3589 | } | ||
| 3590 | } | ||
| 3591 | |||
| 3592 | pZeroCounterOut[0] = zeroCounter; | ||
| 3593 | goto extract_rice_param_part; | ||
| 3594 | } | ||
| 3595 | |||
| 3596 | /* Make sure the cache is restored at the end of it all. */ | ||
| 3597 | bs->cache = bs_cache; | ||
| 3598 | bs->consumedBits = bs_consumedBits; | ||
| 3599 | |||
| 3600 | return DRFLAC_TRUE; | ||
| 3601 | } | ||
| 3602 | |||
| 3603 | static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam) | ||
| 3604 | { | ||
| 3605 | drflac_uint32 riceParamPlus1 = riceParam + 1; | ||
| 3606 | drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; | ||
| 3607 | |||
| 3608 | /* | ||
| 3609 | The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have | ||
| 3610 | no idea how this will work in practice... | ||
| 3611 | */ | ||
| 3612 | drflac_cache_t bs_cache = bs->cache; | ||
| 3613 | drflac_uint32 bs_consumedBits = bs->consumedBits; | ||
| 3614 | |||
| 3615 | /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */ | ||
| 3616 | drflac_uint32 lzcount = drflac__clz(bs_cache); | ||
| 3617 | if (lzcount < sizeof(bs_cache)*8) { | ||
| 3618 | /* | ||
| 3619 | It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting | ||
| 3620 | this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled | ||
| 3621 | outside of this function at a higher level. | ||
| 3622 | */ | ||
| 3623 | extract_rice_param_part: | ||
| 3624 | bs_cache <<= lzcount; | ||
| 3625 | bs_consumedBits += lzcount; | ||
| 3626 | |||
| 3627 | if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { | ||
| 3628 | /* Getting here means the rice parameter part is wholly contained within the current cache line. */ | ||
| 3629 | bs_cache <<= riceParamPlus1; | ||
| 3630 | bs_consumedBits += riceParamPlus1; | ||
| 3631 | } else { | ||
| 3632 | /* | ||
| 3633 | Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache | ||
| 3634 | line, reload the cache, and then combine it with the head of the next cache line. | ||
| 3635 | */ | ||
| 3636 | |||
| 3637 | /* Before reloading the cache we need to grab the size in bits of the low part. */ | ||
| 3638 | drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; | ||
| 3639 | DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); | ||
| 3640 | |||
| 3641 | /* Now reload the cache. */ | ||
| 3642 | if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { | ||
| 3643 | #ifndef DR_FLAC_NO_CRC | ||
| 3644 | drflac__update_crc16(bs); | ||
| 3645 | #endif | ||
| 3646 | bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); | ||
| 3647 | bs_consumedBits = riceParamPartLoBitCount; | ||
| 3648 | #ifndef DR_FLAC_NO_CRC | ||
| 3649 | bs->crc16Cache = bs_cache; | ||
| 3650 | #endif | ||
| 3651 | } else { | ||
| 3652 | /* Slow path. We need to fetch more data from the client. */ | ||
| 3653 | if (!drflac__reload_cache(bs)) { | ||
| 3654 | return DRFLAC_FALSE; | ||
| 3655 | } | ||
| 3656 | |||
| 3657 | if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { | ||
| 3658 | /* This happens when we get to end of stream */ | ||
| 3659 | return DRFLAC_FALSE; | ||
| 3660 | } | ||
| 3661 | |||
| 3662 | bs_cache = bs->cache; | ||
| 3663 | bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; | ||
| 3664 | } | ||
| 3665 | |||
| 3666 | bs_cache <<= riceParamPartLoBitCount; | ||
| 3667 | } | ||
| 3668 | } else { | ||
| 3669 | /* | ||
| 3670 | Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call | ||
| 3671 | to drflac__clz() and we need to reload the cache. | ||
| 3672 | */ | ||
| 3673 | for (;;) { | ||
| 3674 | if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { | ||
| 3675 | #ifndef DR_FLAC_NO_CRC | ||
| 3676 | drflac__update_crc16(bs); | ||
| 3677 | #endif | ||
| 3678 | bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); | ||
| 3679 | bs_consumedBits = 0; | ||
| 3680 | #ifndef DR_FLAC_NO_CRC | ||
| 3681 | bs->crc16Cache = bs_cache; | ||
| 3682 | #endif | ||
| 3683 | } else { | ||
| 3684 | /* Slow path. We need to fetch more data from the client. */ | ||
| 3685 | if (!drflac__reload_cache(bs)) { | ||
| 3686 | return DRFLAC_FALSE; | ||
| 3687 | } | ||
| 3688 | |||
| 3689 | bs_cache = bs->cache; | ||
| 3690 | bs_consumedBits = bs->consumedBits; | ||
| 3691 | } | ||
| 3692 | |||
| 3693 | lzcount = drflac__clz(bs_cache); | ||
| 3694 | if (lzcount < sizeof(bs_cache)*8) { | ||
| 3695 | break; | ||
| 3696 | } | ||
| 3697 | } | ||
| 3698 | |||
| 3699 | goto extract_rice_param_part; | ||
| 3700 | } | ||
| 3701 | |||
| 3702 | /* Make sure the cache is restored at the end of it all. */ | ||
| 3703 | bs->cache = bs_cache; | ||
| 3704 | bs->consumedBits = bs_consumedBits; | ||
| 3705 | |||
| 3706 | return DRFLAC_TRUE; | ||
| 3707 | } | ||
| 3708 | |||
| 3709 | |||
| 3710 | static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) | ||
| 3711 | { | ||
| 3712 | drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; | ||
| 3713 | drflac_uint32 zeroCountPart0; | ||
| 3714 | drflac_uint32 riceParamPart0; | ||
| 3715 | drflac_uint32 riceParamMask; | ||
| 3716 | drflac_uint32 i; | ||
| 3717 | |||
| 3718 | DRFLAC_ASSERT(bs != NULL); | ||
| 3719 | DRFLAC_ASSERT(pSamplesOut != NULL); | ||
| 3720 | |||
| 3721 | (void)bitsPerSample; | ||
| 3722 | (void)order; | ||
| 3723 | (void)shift; | ||
| 3724 | (void)coefficients; | ||
| 3725 | |||
| 3726 | riceParamMask = (drflac_uint32)~((~0UL) << riceParam); | ||
| 3727 | |||
| 3728 | i = 0; | ||
| 3729 | while (i < count) { | ||
| 3730 | /* Rice extraction. */ | ||
| 3731 | if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { | ||
| 3732 | return DRFLAC_FALSE; | ||
| 3733 | } | ||
| 3734 | |||
| 3735 | /* Rice reconstruction. */ | ||
| 3736 | riceParamPart0 &= riceParamMask; | ||
| 3737 | riceParamPart0 |= (zeroCountPart0 << riceParam); | ||
| 3738 | riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; | ||
| 3739 | |||
| 3740 | pSamplesOut[i] = riceParamPart0; | ||
| 3741 | |||
| 3742 | i += 1; | ||
| 3743 | } | ||
| 3744 | |||
| 3745 | return DRFLAC_TRUE; | ||
| 3746 | } | ||
| 3747 | |||
| 3748 | static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) | ||
| 3749 | { | ||
| 3750 | drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; | ||
| 3751 | drflac_uint32 zeroCountPart0 = 0; | ||
| 3752 | drflac_uint32 zeroCountPart1 = 0; | ||
| 3753 | drflac_uint32 zeroCountPart2 = 0; | ||
| 3754 | drflac_uint32 zeroCountPart3 = 0; | ||
| 3755 | drflac_uint32 riceParamPart0 = 0; | ||
| 3756 | drflac_uint32 riceParamPart1 = 0; | ||
| 3757 | drflac_uint32 riceParamPart2 = 0; | ||
| 3758 | drflac_uint32 riceParamPart3 = 0; | ||
| 3759 | drflac_uint32 riceParamMask; | ||
| 3760 | const drflac_int32* pSamplesOutEnd; | ||
| 3761 | drflac_uint32 i; | ||
| 3762 | |||
| 3763 | DRFLAC_ASSERT(bs != NULL); | ||
| 3764 | DRFLAC_ASSERT(pSamplesOut != NULL); | ||
| 3765 | |||
| 3766 | if (lpcOrder == 0) { | ||
| 3767 | return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); | ||
| 3768 | } | ||
| 3769 | |||
| 3770 | riceParamMask = (drflac_uint32)~((~0UL) << riceParam); | ||
| 3771 | pSamplesOutEnd = pSamplesOut + (count & ~3); | ||
| 3772 | |||
| 3773 | if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { | ||
| 3774 | while (pSamplesOut < pSamplesOutEnd) { | ||
| 3775 | /* | ||
| 3776 | Rice extraction. It's faster to do this one at a time against local variables than it is to use the x4 version | ||
| 3777 | against an array. Not sure why, but perhaps it's making more efficient use of registers? | ||
| 3778 | */ | ||
| 3779 | if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || | ||
| 3780 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || | ||
| 3781 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || | ||
| 3782 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { | ||
| 3783 | return DRFLAC_FALSE; | ||
| 3784 | } | ||
| 3785 | |||
| 3786 | riceParamPart0 &= riceParamMask; | ||
| 3787 | riceParamPart1 &= riceParamMask; | ||
| 3788 | riceParamPart2 &= riceParamMask; | ||
| 3789 | riceParamPart3 &= riceParamMask; | ||
| 3790 | |||
| 3791 | riceParamPart0 |= (zeroCountPart0 << riceParam); | ||
| 3792 | riceParamPart1 |= (zeroCountPart1 << riceParam); | ||
| 3793 | riceParamPart2 |= (zeroCountPart2 << riceParam); | ||
| 3794 | riceParamPart3 |= (zeroCountPart3 << riceParam); | ||
| 3795 | |||
| 3796 | riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; | ||
| 3797 | riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; | ||
| 3798 | riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; | ||
| 3799 | riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; | ||
| 3800 | |||
| 3801 | pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); | ||
| 3802 | pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); | ||
| 3803 | pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); | ||
| 3804 | pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); | ||
| 3805 | |||
| 3806 | pSamplesOut += 4; | ||
| 3807 | } | ||
| 3808 | } else { | ||
| 3809 | while (pSamplesOut < pSamplesOutEnd) { | ||
| 3810 | if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || | ||
| 3811 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || | ||
| 3812 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || | ||
| 3813 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { | ||
| 3814 | return DRFLAC_FALSE; | ||
| 3815 | } | ||
| 3816 | |||
| 3817 | riceParamPart0 &= riceParamMask; | ||
| 3818 | riceParamPart1 &= riceParamMask; | ||
| 3819 | riceParamPart2 &= riceParamMask; | ||
| 3820 | riceParamPart3 &= riceParamMask; | ||
| 3821 | |||
| 3822 | riceParamPart0 |= (zeroCountPart0 << riceParam); | ||
| 3823 | riceParamPart1 |= (zeroCountPart1 << riceParam); | ||
| 3824 | riceParamPart2 |= (zeroCountPart2 << riceParam); | ||
| 3825 | riceParamPart3 |= (zeroCountPart3 << riceParam); | ||
| 3826 | |||
| 3827 | riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; | ||
| 3828 | riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; | ||
| 3829 | riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; | ||
| 3830 | riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; | ||
| 3831 | |||
| 3832 | pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); | ||
| 3833 | pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); | ||
| 3834 | pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); | ||
| 3835 | pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); | ||
| 3836 | |||
| 3837 | pSamplesOut += 4; | ||
| 3838 | } | ||
| 3839 | } | ||
| 3840 | |||
| 3841 | i = (count & ~3); | ||
| 3842 | while (i < count) { | ||
| 3843 | /* Rice extraction. */ | ||
| 3844 | if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { | ||
| 3845 | return DRFLAC_FALSE; | ||
| 3846 | } | ||
| 3847 | |||
| 3848 | /* Rice reconstruction. */ | ||
| 3849 | riceParamPart0 &= riceParamMask; | ||
| 3850 | riceParamPart0 |= (zeroCountPart0 << riceParam); | ||
| 3851 | riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; | ||
| 3852 | /*riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1);*/ | ||
| 3853 | |||
| 3854 | /* Sample reconstruction. */ | ||
| 3855 | if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { | ||
| 3856 | pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); | ||
| 3857 | } else { | ||
| 3858 | pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); | ||
| 3859 | } | ||
| 3860 | |||
| 3861 | i += 1; | ||
| 3862 | pSamplesOut += 1; | ||
| 3863 | } | ||
| 3864 | |||
| 3865 | return DRFLAC_TRUE; | ||
| 3866 | } | ||
| 3867 | |||
| 3868 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 3869 | static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b) | ||
| 3870 | { | ||
| 3871 | __m128i r; | ||
| 3872 | |||
| 3873 | /* Pack. */ | ||
| 3874 | r = _mm_packs_epi32(a, b); | ||
| 3875 | |||
| 3876 | /* a3a2 a1a0 b3b2 b1b0 -> a3a2 b3b2 a1a0 b1b0 */ | ||
| 3877 | r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); | ||
| 3878 | |||
| 3879 | /* a3a2 b3b2 a1a0 b1b0 -> a3b3 a2b2 a1b1 a0b0 */ | ||
| 3880 | r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); | ||
| 3881 | r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); | ||
| 3882 | |||
| 3883 | return r; | ||
| 3884 | } | ||
| 3885 | #endif | ||
| 3886 | |||
| 3887 | #if defined(DRFLAC_SUPPORT_SSE41) | ||
| 3888 | static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a) | ||
| 3889 | { | ||
| 3890 | return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); | ||
| 3891 | } | ||
| 3892 | |||
| 3893 | static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x) | ||
| 3894 | { | ||
| 3895 | __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); | ||
| 3896 | __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); | ||
| 3897 | return _mm_add_epi32(x64, x32); | ||
| 3898 | } | ||
| 3899 | |||
| 3900 | static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x) | ||
| 3901 | { | ||
| 3902 | return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); | ||
| 3903 | } | ||
| 3904 | |||
| 3905 | static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count) | ||
| 3906 | { | ||
| 3907 | /* | ||
| 3908 | To simplify this we are assuming count < 32. This restriction allows us to work on a low side and a high side. The low side | ||
| 3909 | is shifted with zero bits, whereas the right side is shifted with sign bits. | ||
| 3910 | */ | ||
| 3911 | __m128i lo = _mm_srli_epi64(x, count); | ||
| 3912 | __m128i hi = _mm_srai_epi32(x, count); | ||
| 3913 | |||
| 3914 | hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); /* The high part needs to have the low part cleared. */ | ||
| 3915 | |||
| 3916 | return _mm_or_si128(lo, hi); | ||
| 3917 | } | ||
| 3918 | |||
| 3919 | static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) | ||
| 3920 | { | ||
| 3921 | int i; | ||
| 3922 | drflac_uint32 riceParamMask; | ||
| 3923 | drflac_int32* pDecodedSamples = pSamplesOut; | ||
| 3924 | drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); | ||
| 3925 | drflac_uint32 zeroCountParts0 = 0; | ||
| 3926 | drflac_uint32 zeroCountParts1 = 0; | ||
| 3927 | drflac_uint32 zeroCountParts2 = 0; | ||
| 3928 | drflac_uint32 zeroCountParts3 = 0; | ||
| 3929 | drflac_uint32 riceParamParts0 = 0; | ||
| 3930 | drflac_uint32 riceParamParts1 = 0; | ||
| 3931 | drflac_uint32 riceParamParts2 = 0; | ||
| 3932 | drflac_uint32 riceParamParts3 = 0; | ||
| 3933 | __m128i coefficients128_0; | ||
| 3934 | __m128i coefficients128_4; | ||
| 3935 | __m128i coefficients128_8; | ||
| 3936 | __m128i samples128_0; | ||
| 3937 | __m128i samples128_4; | ||
| 3938 | __m128i samples128_8; | ||
| 3939 | __m128i riceParamMask128; | ||
| 3940 | |||
| 3941 | const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; | ||
| 3942 | |||
| 3943 | riceParamMask = (drflac_uint32)~((~0UL) << riceParam); | ||
| 3944 | riceParamMask128 = _mm_set1_epi32(riceParamMask); | ||
| 3945 | |||
| 3946 | /* Pre-load. */ | ||
| 3947 | coefficients128_0 = _mm_setzero_si128(); | ||
| 3948 | coefficients128_4 = _mm_setzero_si128(); | ||
| 3949 | coefficients128_8 = _mm_setzero_si128(); | ||
| 3950 | |||
| 3951 | samples128_0 = _mm_setzero_si128(); | ||
| 3952 | samples128_4 = _mm_setzero_si128(); | ||
| 3953 | samples128_8 = _mm_setzero_si128(); | ||
| 3954 | |||
| 3955 | /* | ||
| 3956 | Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than | ||
| 3957 | what's available in the input buffers. It would be convenient to use a fall-through switch to do this, but this results | ||
| 3958 | in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted | ||
| 3959 | so I think there's opportunity for this to be simplified. | ||
| 3960 | */ | ||
| 3961 | #if 1 | ||
| 3962 | { | ||
| 3963 | int runningOrder = order; | ||
| 3964 | |||
| 3965 | /* 0 - 3. */ | ||
| 3966 | if (runningOrder >= 4) { | ||
| 3967 | coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); | ||
| 3968 | samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); | ||
| 3969 | runningOrder -= 4; | ||
| 3970 | } else { | ||
| 3971 | switch (runningOrder) { | ||
| 3972 | case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; | ||
| 3973 | case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; | ||
| 3974 | case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; | ||
| 3975 | } | ||
| 3976 | runningOrder = 0; | ||
| 3977 | } | ||
| 3978 | |||
| 3979 | /* 4 - 7 */ | ||
| 3980 | if (runningOrder >= 4) { | ||
| 3981 | coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); | ||
| 3982 | samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); | ||
| 3983 | runningOrder -= 4; | ||
| 3984 | } else { | ||
| 3985 | switch (runningOrder) { | ||
| 3986 | case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; | ||
| 3987 | case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; | ||
| 3988 | case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; | ||
| 3989 | } | ||
| 3990 | runningOrder = 0; | ||
| 3991 | } | ||
| 3992 | |||
| 3993 | /* 8 - 11 */ | ||
| 3994 | if (runningOrder == 4) { | ||
| 3995 | coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); | ||
| 3996 | samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); | ||
| 3997 | runningOrder -= 4; | ||
| 3998 | } else { | ||
| 3999 | switch (runningOrder) { | ||
| 4000 | case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; | ||
| 4001 | case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; | ||
| 4002 | case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; | ||
| 4003 | } | ||
| 4004 | runningOrder = 0; | ||
| 4005 | } | ||
| 4006 | |||
| 4007 | /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ | ||
| 4008 | coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); | ||
| 4009 | coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); | ||
| 4010 | coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); | ||
| 4011 | } | ||
| 4012 | #else | ||
| 4013 | /* This causes strict-aliasing warnings with GCC. */ | ||
| 4014 | switch (order) | ||
| 4015 | { | ||
| 4016 | case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; | ||
| 4017 | case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; | ||
| 4018 | case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; | ||
| 4019 | case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; | ||
| 4020 | case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; | ||
| 4021 | case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; | ||
| 4022 | case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; | ||
| 4023 | case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; | ||
| 4024 | case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; | ||
| 4025 | case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; | ||
| 4026 | case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; | ||
| 4027 | case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; | ||
| 4028 | } | ||
| 4029 | #endif | ||
| 4030 | |||
| 4031 | /* For this version we are doing one sample at a time. */ | ||
| 4032 | while (pDecodedSamples < pDecodedSamplesEnd) { | ||
| 4033 | __m128i prediction128; | ||
| 4034 | __m128i zeroCountPart128; | ||
| 4035 | __m128i riceParamPart128; | ||
| 4036 | |||
| 4037 | if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || | ||
| 4038 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || | ||
| 4039 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || | ||
| 4040 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { | ||
| 4041 | return DRFLAC_FALSE; | ||
| 4042 | } | ||
| 4043 | |||
| 4044 | zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); | ||
| 4045 | riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); | ||
| 4046 | |||
| 4047 | riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); | ||
| 4048 | riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); | ||
| 4049 | riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); /* <-- SSE2 compatible */ | ||
| 4050 | /*riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_mullo_epi32(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01)), _mm_set1_epi32(0xFFFFFFFF)));*/ /* <-- Only supported from SSE4.1 and is slower in my testing... */ | ||
| 4051 | |||
| 4052 | if (order <= 4) { | ||
| 4053 | for (i = 0; i < 4; i += 1) { | ||
| 4054 | prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); | ||
| 4055 | |||
| 4056 | /* Horizontal add and shift. */ | ||
| 4057 | prediction128 = drflac__mm_hadd_epi32(prediction128); | ||
| 4058 | prediction128 = _mm_srai_epi32(prediction128, shift); | ||
| 4059 | prediction128 = _mm_add_epi32(riceParamPart128, prediction128); | ||
| 4060 | |||
| 4061 | samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); | ||
| 4062 | riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); | ||
| 4063 | } | ||
| 4064 | } else if (order <= 8) { | ||
| 4065 | for (i = 0; i < 4; i += 1) { | ||
| 4066 | prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); | ||
| 4067 | prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); | ||
| 4068 | |||
| 4069 | /* Horizontal add and shift. */ | ||
| 4070 | prediction128 = drflac__mm_hadd_epi32(prediction128); | ||
| 4071 | prediction128 = _mm_srai_epi32(prediction128, shift); | ||
| 4072 | prediction128 = _mm_add_epi32(riceParamPart128, prediction128); | ||
| 4073 | |||
| 4074 | samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); | ||
| 4075 | samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); | ||
| 4076 | riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); | ||
| 4077 | } | ||
| 4078 | } else { | ||
| 4079 | for (i = 0; i < 4; i += 1) { | ||
| 4080 | prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); | ||
| 4081 | prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); | ||
| 4082 | prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); | ||
| 4083 | |||
| 4084 | /* Horizontal add and shift. */ | ||
| 4085 | prediction128 = drflac__mm_hadd_epi32(prediction128); | ||
| 4086 | prediction128 = _mm_srai_epi32(prediction128, shift); | ||
| 4087 | prediction128 = _mm_add_epi32(riceParamPart128, prediction128); | ||
| 4088 | |||
| 4089 | samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); | ||
| 4090 | samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); | ||
| 4091 | samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); | ||
| 4092 | riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); | ||
| 4093 | } | ||
| 4094 | } | ||
| 4095 | |||
| 4096 | /* We store samples in groups of 4. */ | ||
| 4097 | _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); | ||
| 4098 | pDecodedSamples += 4; | ||
| 4099 | } | ||
| 4100 | |||
| 4101 | /* Make sure we process the last few samples. */ | ||
| 4102 | i = (count & ~3); | ||
| 4103 | while (i < (int)count) { | ||
| 4104 | /* Rice extraction. */ | ||
| 4105 | if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { | ||
| 4106 | return DRFLAC_FALSE; | ||
| 4107 | } | ||
| 4108 | |||
| 4109 | /* Rice reconstruction. */ | ||
| 4110 | riceParamParts0 &= riceParamMask; | ||
| 4111 | riceParamParts0 |= (zeroCountParts0 << riceParam); | ||
| 4112 | riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; | ||
| 4113 | |||
| 4114 | /* Sample reconstruction. */ | ||
| 4115 | pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); | ||
| 4116 | |||
| 4117 | i += 1; | ||
| 4118 | pDecodedSamples += 1; | ||
| 4119 | } | ||
| 4120 | |||
| 4121 | return DRFLAC_TRUE; | ||
| 4122 | } | ||
| 4123 | |||
| 4124 | static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) | ||
| 4125 | { | ||
| 4126 | int i; | ||
| 4127 | drflac_uint32 riceParamMask; | ||
| 4128 | drflac_int32* pDecodedSamples = pSamplesOut; | ||
| 4129 | drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); | ||
| 4130 | drflac_uint32 zeroCountParts0 = 0; | ||
| 4131 | drflac_uint32 zeroCountParts1 = 0; | ||
| 4132 | drflac_uint32 zeroCountParts2 = 0; | ||
| 4133 | drflac_uint32 zeroCountParts3 = 0; | ||
| 4134 | drflac_uint32 riceParamParts0 = 0; | ||
| 4135 | drflac_uint32 riceParamParts1 = 0; | ||
| 4136 | drflac_uint32 riceParamParts2 = 0; | ||
| 4137 | drflac_uint32 riceParamParts3 = 0; | ||
| 4138 | __m128i coefficients128_0; | ||
| 4139 | __m128i coefficients128_4; | ||
| 4140 | __m128i coefficients128_8; | ||
| 4141 | __m128i samples128_0; | ||
| 4142 | __m128i samples128_4; | ||
| 4143 | __m128i samples128_8; | ||
| 4144 | __m128i prediction128; | ||
| 4145 | __m128i riceParamMask128; | ||
| 4146 | |||
| 4147 | const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; | ||
| 4148 | |||
| 4149 | DRFLAC_ASSERT(order <= 12); | ||
| 4150 | |||
| 4151 | riceParamMask = (drflac_uint32)~((~0UL) << riceParam); | ||
| 4152 | riceParamMask128 = _mm_set1_epi32(riceParamMask); | ||
| 4153 | |||
| 4154 | prediction128 = _mm_setzero_si128(); | ||
| 4155 | |||
| 4156 | /* Pre-load. */ | ||
| 4157 | coefficients128_0 = _mm_setzero_si128(); | ||
| 4158 | coefficients128_4 = _mm_setzero_si128(); | ||
| 4159 | coefficients128_8 = _mm_setzero_si128(); | ||
| 4160 | |||
| 4161 | samples128_0 = _mm_setzero_si128(); | ||
| 4162 | samples128_4 = _mm_setzero_si128(); | ||
| 4163 | samples128_8 = _mm_setzero_si128(); | ||
| 4164 | |||
| 4165 | #if 1 | ||
| 4166 | { | ||
| 4167 | int runningOrder = order; | ||
| 4168 | |||
| 4169 | /* 0 - 3. */ | ||
| 4170 | if (runningOrder >= 4) { | ||
| 4171 | coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); | ||
| 4172 | samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); | ||
| 4173 | runningOrder -= 4; | ||
| 4174 | } else { | ||
| 4175 | switch (runningOrder) { | ||
| 4176 | case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; | ||
| 4177 | case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; | ||
| 4178 | case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; | ||
| 4179 | } | ||
| 4180 | runningOrder = 0; | ||
| 4181 | } | ||
| 4182 | |||
| 4183 | /* 4 - 7 */ | ||
| 4184 | if (runningOrder >= 4) { | ||
| 4185 | coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); | ||
| 4186 | samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); | ||
| 4187 | runningOrder -= 4; | ||
| 4188 | } else { | ||
| 4189 | switch (runningOrder) { | ||
| 4190 | case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; | ||
| 4191 | case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; | ||
| 4192 | case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; | ||
| 4193 | } | ||
| 4194 | runningOrder = 0; | ||
| 4195 | } | ||
| 4196 | |||
| 4197 | /* 8 - 11 */ | ||
| 4198 | if (runningOrder == 4) { | ||
| 4199 | coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); | ||
| 4200 | samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); | ||
| 4201 | runningOrder -= 4; | ||
| 4202 | } else { | ||
| 4203 | switch (runningOrder) { | ||
| 4204 | case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; | ||
| 4205 | case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; | ||
| 4206 | case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; | ||
| 4207 | } | ||
| 4208 | runningOrder = 0; | ||
| 4209 | } | ||
| 4210 | |||
| 4211 | /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ | ||
| 4212 | coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); | ||
| 4213 | coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); | ||
| 4214 | coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); | ||
| 4215 | } | ||
| 4216 | #else | ||
| 4217 | switch (order) | ||
| 4218 | { | ||
| 4219 | case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; | ||
| 4220 | case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; | ||
| 4221 | case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; | ||
| 4222 | case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; | ||
| 4223 | case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; | ||
| 4224 | case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; | ||
| 4225 | case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; | ||
| 4226 | case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; | ||
| 4227 | case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; | ||
| 4228 | case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; | ||
| 4229 | case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; | ||
| 4230 | case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; | ||
| 4231 | } | ||
| 4232 | #endif | ||
| 4233 | |||
| 4234 | /* For this version we are doing one sample at a time. */ | ||
| 4235 | while (pDecodedSamples < pDecodedSamplesEnd) { | ||
| 4236 | __m128i zeroCountPart128; | ||
| 4237 | __m128i riceParamPart128; | ||
| 4238 | |||
| 4239 | if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || | ||
| 4240 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || | ||
| 4241 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || | ||
| 4242 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { | ||
| 4243 | return DRFLAC_FALSE; | ||
| 4244 | } | ||
| 4245 | |||
| 4246 | zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); | ||
| 4247 | riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); | ||
| 4248 | |||
| 4249 | riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); | ||
| 4250 | riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); | ||
| 4251 | riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); | ||
| 4252 | |||
| 4253 | for (i = 0; i < 4; i += 1) { | ||
| 4254 | prediction128 = _mm_xor_si128(prediction128, prediction128); /* Reset to 0. */ | ||
| 4255 | |||
| 4256 | switch (order) | ||
| 4257 | { | ||
| 4258 | case 12: | ||
| 4259 | case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); | ||
| 4260 | case 10: | ||
| 4261 | case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); | ||
| 4262 | case 8: | ||
| 4263 | case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); | ||
| 4264 | case 6: | ||
| 4265 | case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); | ||
| 4266 | case 4: | ||
| 4267 | case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); | ||
| 4268 | case 2: | ||
| 4269 | case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); | ||
| 4270 | } | ||
| 4271 | |||
| 4272 | /* Horizontal add and shift. */ | ||
| 4273 | prediction128 = drflac__mm_hadd_epi64(prediction128); | ||
| 4274 | prediction128 = drflac__mm_srai_epi64(prediction128, shift); | ||
| 4275 | prediction128 = _mm_add_epi32(riceParamPart128, prediction128); | ||
| 4276 | |||
| 4277 | /* Our value should be sitting in prediction128[0]. We need to combine this with our SSE samples. */ | ||
| 4278 | samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); | ||
| 4279 | samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); | ||
| 4280 | samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); | ||
| 4281 | |||
| 4282 | /* Slide our rice parameter down so that the value in position 0 contains the next one to process. */ | ||
| 4283 | riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); | ||
| 4284 | } | ||
| 4285 | |||
| 4286 | /* We store samples in groups of 4. */ | ||
| 4287 | _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); | ||
| 4288 | pDecodedSamples += 4; | ||
| 4289 | } | ||
| 4290 | |||
| 4291 | /* Make sure we process the last few samples. */ | ||
| 4292 | i = (count & ~3); | ||
| 4293 | while (i < (int)count) { | ||
| 4294 | /* Rice extraction. */ | ||
| 4295 | if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { | ||
| 4296 | return DRFLAC_FALSE; | ||
| 4297 | } | ||
| 4298 | |||
| 4299 | /* Rice reconstruction. */ | ||
| 4300 | riceParamParts0 &= riceParamMask; | ||
| 4301 | riceParamParts0 |= (zeroCountParts0 << riceParam); | ||
| 4302 | riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; | ||
| 4303 | |||
| 4304 | /* Sample reconstruction. */ | ||
| 4305 | pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); | ||
| 4306 | |||
| 4307 | i += 1; | ||
| 4308 | pDecodedSamples += 1; | ||
| 4309 | } | ||
| 4310 | |||
| 4311 | return DRFLAC_TRUE; | ||
| 4312 | } | ||
| 4313 | |||
| 4314 | static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) | ||
| 4315 | { | ||
| 4316 | DRFLAC_ASSERT(bs != NULL); | ||
| 4317 | DRFLAC_ASSERT(pSamplesOut != NULL); | ||
| 4318 | |||
| 4319 | /* In my testing the order is rarely > 12, so in this case I'm going to simplify the SSE implementation by only handling order <= 12. */ | ||
| 4320 | if (lpcOrder > 0 && lpcOrder <= 12) { | ||
| 4321 | if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { | ||
| 4322 | return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); | ||
| 4323 | } else { | ||
| 4324 | return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); | ||
| 4325 | } | ||
| 4326 | } else { | ||
| 4327 | return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); | ||
| 4328 | } | ||
| 4329 | } | ||
| 4330 | #endif | ||
| 4331 | |||
| 4332 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 4333 | static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x) | ||
| 4334 | { | ||
| 4335 | vst1q_s32(p+0, x.val[0]); | ||
| 4336 | vst1q_s32(p+4, x.val[1]); | ||
| 4337 | } | ||
| 4338 | |||
| 4339 | static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x) | ||
| 4340 | { | ||
| 4341 | vst1q_u32(p+0, x.val[0]); | ||
| 4342 | vst1q_u32(p+4, x.val[1]); | ||
| 4343 | } | ||
| 4344 | |||
| 4345 | static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x) | ||
| 4346 | { | ||
| 4347 | vst1q_f32(p+0, x.val[0]); | ||
| 4348 | vst1q_f32(p+4, x.val[1]); | ||
| 4349 | } | ||
| 4350 | |||
| 4351 | static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x) | ||
| 4352 | { | ||
| 4353 | vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); | ||
| 4354 | } | ||
| 4355 | |||
| 4356 | static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x) | ||
| 4357 | { | ||
| 4358 | vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); | ||
| 4359 | } | ||
| 4360 | |||
| 4361 | static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0) | ||
| 4362 | { | ||
| 4363 | drflac_int32 x[4]; | ||
| 4364 | x[3] = x3; | ||
| 4365 | x[2] = x2; | ||
| 4366 | x[1] = x1; | ||
| 4367 | x[0] = x0; | ||
| 4368 | return vld1q_s32(x); | ||
| 4369 | } | ||
| 4370 | |||
| 4371 | static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b) | ||
| 4372 | { | ||
| 4373 | /* Equivalent to SSE's _mm_alignr_epi8(a, b, 4) */ | ||
| 4374 | |||
| 4375 | /* Reference */ | ||
| 4376 | /*return drflac__vdupq_n_s32x4( | ||
| 4377 | vgetq_lane_s32(a, 0), | ||
| 4378 | vgetq_lane_s32(b, 3), | ||
| 4379 | vgetq_lane_s32(b, 2), | ||
| 4380 | vgetq_lane_s32(b, 1) | ||
| 4381 | );*/ | ||
| 4382 | |||
| 4383 | return vextq_s32(b, a, 1); | ||
| 4384 | } | ||
| 4385 | |||
| 4386 | static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) | ||
| 4387 | { | ||
| 4388 | /* Equivalent to SSE's _mm_alignr_epi8(a, b, 4) */ | ||
| 4389 | |||
| 4390 | /* Reference */ | ||
| 4391 | /*return drflac__vdupq_n_s32x4( | ||
| 4392 | vgetq_lane_s32(a, 0), | ||
| 4393 | vgetq_lane_s32(b, 3), | ||
| 4394 | vgetq_lane_s32(b, 2), | ||
| 4395 | vgetq_lane_s32(b, 1) | ||
| 4396 | );*/ | ||
| 4397 | |||
| 4398 | return vextq_u32(b, a, 1); | ||
| 4399 | } | ||
| 4400 | |||
| 4401 | static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x) | ||
| 4402 | { | ||
| 4403 | /* The sum must end up in position 0. */ | ||
| 4404 | |||
| 4405 | /* Reference */ | ||
| 4406 | /*return vdupq_n_s32( | ||
| 4407 | vgetq_lane_s32(x, 3) + | ||
| 4408 | vgetq_lane_s32(x, 2) + | ||
| 4409 | vgetq_lane_s32(x, 1) + | ||
| 4410 | vgetq_lane_s32(x, 0) | ||
| 4411 | );*/ | ||
| 4412 | |||
| 4413 | int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); | ||
| 4414 | return vpadd_s32(r, r); | ||
| 4415 | } | ||
| 4416 | |||
| 4417 | static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x) | ||
| 4418 | { | ||
| 4419 | return vadd_s64(vget_high_s64(x), vget_low_s64(x)); | ||
| 4420 | } | ||
| 4421 | |||
| 4422 | static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x) | ||
| 4423 | { | ||
| 4424 | /* Reference */ | ||
| 4425 | /*return drflac__vdupq_n_s32x4( | ||
| 4426 | vgetq_lane_s32(x, 0), | ||
| 4427 | vgetq_lane_s32(x, 1), | ||
| 4428 | vgetq_lane_s32(x, 2), | ||
| 4429 | vgetq_lane_s32(x, 3) | ||
| 4430 | );*/ | ||
| 4431 | |||
| 4432 | return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); | ||
| 4433 | } | ||
| 4434 | |||
| 4435 | static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x) | ||
| 4436 | { | ||
| 4437 | return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); | ||
| 4438 | } | ||
| 4439 | |||
| 4440 | static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x) | ||
| 4441 | { | ||
| 4442 | return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); | ||
| 4443 | } | ||
| 4444 | |||
| 4445 | static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) | ||
| 4446 | { | ||
| 4447 | int i; | ||
| 4448 | drflac_uint32 riceParamMask; | ||
| 4449 | drflac_int32* pDecodedSamples = pSamplesOut; | ||
| 4450 | drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); | ||
| 4451 | drflac_uint32 zeroCountParts[4]; | ||
| 4452 | drflac_uint32 riceParamParts[4]; | ||
| 4453 | int32x4_t coefficients128_0; | ||
| 4454 | int32x4_t coefficients128_4; | ||
| 4455 | int32x4_t coefficients128_8; | ||
| 4456 | int32x4_t samples128_0; | ||
| 4457 | int32x4_t samples128_4; | ||
| 4458 | int32x4_t samples128_8; | ||
| 4459 | uint32x4_t riceParamMask128; | ||
| 4460 | int32x4_t riceParam128; | ||
| 4461 | int32x2_t shift64; | ||
| 4462 | uint32x4_t one128; | ||
| 4463 | |||
| 4464 | const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; | ||
| 4465 | |||
| 4466 | riceParamMask = (drflac_uint32)~((~0UL) << riceParam); | ||
| 4467 | riceParamMask128 = vdupq_n_u32(riceParamMask); | ||
| 4468 | |||
| 4469 | riceParam128 = vdupq_n_s32(riceParam); | ||
| 4470 | shift64 = vdup_n_s32(-shift); /* Negate the shift because we'll be doing a variable shift using vshlq_s32(). */ | ||
| 4471 | one128 = vdupq_n_u32(1); | ||
| 4472 | |||
| 4473 | /* | ||
| 4474 | Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than | ||
| 4475 | what's available in the input buffers. It would be conenient to use a fall-through switch to do this, but this results | ||
| 4476 | in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted | ||
| 4477 | so I think there's opportunity for this to be simplified. | ||
| 4478 | */ | ||
| 4479 | { | ||
| 4480 | int runningOrder = order; | ||
| 4481 | drflac_int32 tempC[4] = {0, 0, 0, 0}; | ||
| 4482 | drflac_int32 tempS[4] = {0, 0, 0, 0}; | ||
| 4483 | |||
| 4484 | /* 0 - 3. */ | ||
| 4485 | if (runningOrder >= 4) { | ||
| 4486 | coefficients128_0 = vld1q_s32(coefficients + 0); | ||
| 4487 | samples128_0 = vld1q_s32(pSamplesOut - 4); | ||
| 4488 | runningOrder -= 4; | ||
| 4489 | } else { | ||
| 4490 | switch (runningOrder) { | ||
| 4491 | case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; /* fallthrough */ | ||
| 4492 | case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; /* fallthrough */ | ||
| 4493 | case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; /* fallthrough */ | ||
| 4494 | } | ||
| 4495 | |||
| 4496 | coefficients128_0 = vld1q_s32(tempC); | ||
| 4497 | samples128_0 = vld1q_s32(tempS); | ||
| 4498 | runningOrder = 0; | ||
| 4499 | } | ||
| 4500 | |||
| 4501 | /* 4 - 7 */ | ||
| 4502 | if (runningOrder >= 4) { | ||
| 4503 | coefficients128_4 = vld1q_s32(coefficients + 4); | ||
| 4504 | samples128_4 = vld1q_s32(pSamplesOut - 8); | ||
| 4505 | runningOrder -= 4; | ||
| 4506 | } else { | ||
| 4507 | switch (runningOrder) { | ||
| 4508 | case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; /* fallthrough */ | ||
| 4509 | case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; /* fallthrough */ | ||
| 4510 | case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; /* fallthrough */ | ||
| 4511 | } | ||
| 4512 | |||
| 4513 | coefficients128_4 = vld1q_s32(tempC); | ||
| 4514 | samples128_4 = vld1q_s32(tempS); | ||
| 4515 | runningOrder = 0; | ||
| 4516 | } | ||
| 4517 | |||
| 4518 | /* 8 - 11 */ | ||
| 4519 | if (runningOrder == 4) { | ||
| 4520 | coefficients128_8 = vld1q_s32(coefficients + 8); | ||
| 4521 | samples128_8 = vld1q_s32(pSamplesOut - 12); | ||
| 4522 | runningOrder -= 4; | ||
| 4523 | } else { | ||
| 4524 | switch (runningOrder) { | ||
| 4525 | case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; /* fallthrough */ | ||
| 4526 | case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; /* fallthrough */ | ||
| 4527 | case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; /* fallthrough */ | ||
| 4528 | } | ||
| 4529 | |||
| 4530 | coefficients128_8 = vld1q_s32(tempC); | ||
| 4531 | samples128_8 = vld1q_s32(tempS); | ||
| 4532 | runningOrder = 0; | ||
| 4533 | } | ||
| 4534 | |||
| 4535 | /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ | ||
| 4536 | coefficients128_0 = drflac__vrevq_s32(coefficients128_0); | ||
| 4537 | coefficients128_4 = drflac__vrevq_s32(coefficients128_4); | ||
| 4538 | coefficients128_8 = drflac__vrevq_s32(coefficients128_8); | ||
| 4539 | } | ||
| 4540 | |||
| 4541 | /* For this version we are doing one sample at a time. */ | ||
| 4542 | while (pDecodedSamples < pDecodedSamplesEnd) { | ||
| 4543 | int32x4_t prediction128; | ||
| 4544 | int32x2_t prediction64; | ||
| 4545 | uint32x4_t zeroCountPart128; | ||
| 4546 | uint32x4_t riceParamPart128; | ||
| 4547 | |||
| 4548 | if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || | ||
| 4549 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || | ||
| 4550 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || | ||
| 4551 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { | ||
| 4552 | return DRFLAC_FALSE; | ||
| 4553 | } | ||
| 4554 | |||
| 4555 | zeroCountPart128 = vld1q_u32(zeroCountParts); | ||
| 4556 | riceParamPart128 = vld1q_u32(riceParamParts); | ||
| 4557 | |||
| 4558 | riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); | ||
| 4559 | riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); | ||
| 4560 | riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); | ||
| 4561 | |||
| 4562 | if (order <= 4) { | ||
| 4563 | for (i = 0; i < 4; i += 1) { | ||
| 4564 | prediction128 = vmulq_s32(coefficients128_0, samples128_0); | ||
| 4565 | |||
| 4566 | /* Horizontal add and shift. */ | ||
| 4567 | prediction64 = drflac__vhaddq_s32(prediction128); | ||
| 4568 | prediction64 = vshl_s32(prediction64, shift64); | ||
| 4569 | prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); | ||
| 4570 | |||
| 4571 | samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); | ||
| 4572 | riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); | ||
| 4573 | } | ||
| 4574 | } else if (order <= 8) { | ||
| 4575 | for (i = 0; i < 4; i += 1) { | ||
| 4576 | prediction128 = vmulq_s32(coefficients128_4, samples128_4); | ||
| 4577 | prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); | ||
| 4578 | |||
| 4579 | /* Horizontal add and shift. */ | ||
| 4580 | prediction64 = drflac__vhaddq_s32(prediction128); | ||
| 4581 | prediction64 = vshl_s32(prediction64, shift64); | ||
| 4582 | prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); | ||
| 4583 | |||
| 4584 | samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); | ||
| 4585 | samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); | ||
| 4586 | riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); | ||
| 4587 | } | ||
| 4588 | } else { | ||
| 4589 | for (i = 0; i < 4; i += 1) { | ||
| 4590 | prediction128 = vmulq_s32(coefficients128_8, samples128_8); | ||
| 4591 | prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); | ||
| 4592 | prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); | ||
| 4593 | |||
| 4594 | /* Horizontal add and shift. */ | ||
| 4595 | prediction64 = drflac__vhaddq_s32(prediction128); | ||
| 4596 | prediction64 = vshl_s32(prediction64, shift64); | ||
| 4597 | prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); | ||
| 4598 | |||
| 4599 | samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); | ||
| 4600 | samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); | ||
| 4601 | samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); | ||
| 4602 | riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); | ||
| 4603 | } | ||
| 4604 | } | ||
| 4605 | |||
| 4606 | /* We store samples in groups of 4. */ | ||
| 4607 | vst1q_s32(pDecodedSamples, samples128_0); | ||
| 4608 | pDecodedSamples += 4; | ||
| 4609 | } | ||
| 4610 | |||
| 4611 | /* Make sure we process the last few samples. */ | ||
| 4612 | i = (count & ~3); | ||
| 4613 | while (i < (int)count) { | ||
| 4614 | /* Rice extraction. */ | ||
| 4615 | if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { | ||
| 4616 | return DRFLAC_FALSE; | ||
| 4617 | } | ||
| 4618 | |||
| 4619 | /* Rice reconstruction. */ | ||
| 4620 | riceParamParts[0] &= riceParamMask; | ||
| 4621 | riceParamParts[0] |= (zeroCountParts[0] << riceParam); | ||
| 4622 | riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; | ||
| 4623 | |||
| 4624 | /* Sample reconstruction. */ | ||
| 4625 | pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); | ||
| 4626 | |||
| 4627 | i += 1; | ||
| 4628 | pDecodedSamples += 1; | ||
| 4629 | } | ||
| 4630 | |||
| 4631 | return DRFLAC_TRUE; | ||
| 4632 | } | ||
| 4633 | |||
| 4634 | static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) | ||
| 4635 | { | ||
| 4636 | int i; | ||
| 4637 | drflac_uint32 riceParamMask; | ||
| 4638 | drflac_int32* pDecodedSamples = pSamplesOut; | ||
| 4639 | drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); | ||
| 4640 | drflac_uint32 zeroCountParts[4]; | ||
| 4641 | drflac_uint32 riceParamParts[4]; | ||
| 4642 | int32x4_t coefficients128_0; | ||
| 4643 | int32x4_t coefficients128_4; | ||
| 4644 | int32x4_t coefficients128_8; | ||
| 4645 | int32x4_t samples128_0; | ||
| 4646 | int32x4_t samples128_4; | ||
| 4647 | int32x4_t samples128_8; | ||
| 4648 | uint32x4_t riceParamMask128; | ||
| 4649 | int32x4_t riceParam128; | ||
| 4650 | int64x1_t shift64; | ||
| 4651 | uint32x4_t one128; | ||
| 4652 | int64x2_t prediction128 = { 0 }; | ||
| 4653 | uint32x4_t zeroCountPart128; | ||
| 4654 | uint32x4_t riceParamPart128; | ||
| 4655 | |||
| 4656 | const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; | ||
| 4657 | |||
| 4658 | riceParamMask = (drflac_uint32)~((~0UL) << riceParam); | ||
| 4659 | riceParamMask128 = vdupq_n_u32(riceParamMask); | ||
| 4660 | |||
| 4661 | riceParam128 = vdupq_n_s32(riceParam); | ||
| 4662 | shift64 = vdup_n_s64(-shift); /* Negate the shift because we'll be doing a variable shift using vshlq_s32(). */ | ||
| 4663 | one128 = vdupq_n_u32(1); | ||
| 4664 | |||
| 4665 | /* | ||
| 4666 | Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than | ||
| 4667 | what's available in the input buffers. It would be convenient to use a fall-through switch to do this, but this results | ||
| 4668 | in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted | ||
| 4669 | so I think there's opportunity for this to be simplified. | ||
| 4670 | */ | ||
| 4671 | { | ||
| 4672 | int runningOrder = order; | ||
| 4673 | drflac_int32 tempC[4] = {0, 0, 0, 0}; | ||
| 4674 | drflac_int32 tempS[4] = {0, 0, 0, 0}; | ||
| 4675 | |||
| 4676 | /* 0 - 3. */ | ||
| 4677 | if (runningOrder >= 4) { | ||
| 4678 | coefficients128_0 = vld1q_s32(coefficients + 0); | ||
| 4679 | samples128_0 = vld1q_s32(pSamplesOut - 4); | ||
| 4680 | runningOrder -= 4; | ||
| 4681 | } else { | ||
| 4682 | switch (runningOrder) { | ||
| 4683 | case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; /* fallthrough */ | ||
| 4684 | case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; /* fallthrough */ | ||
| 4685 | case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; /* fallthrough */ | ||
| 4686 | } | ||
| 4687 | |||
| 4688 | coefficients128_0 = vld1q_s32(tempC); | ||
| 4689 | samples128_0 = vld1q_s32(tempS); | ||
| 4690 | runningOrder = 0; | ||
| 4691 | } | ||
| 4692 | |||
| 4693 | /* 4 - 7 */ | ||
| 4694 | if (runningOrder >= 4) { | ||
| 4695 | coefficients128_4 = vld1q_s32(coefficients + 4); | ||
| 4696 | samples128_4 = vld1q_s32(pSamplesOut - 8); | ||
| 4697 | runningOrder -= 4; | ||
| 4698 | } else { | ||
| 4699 | switch (runningOrder) { | ||
| 4700 | case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; /* fallthrough */ | ||
| 4701 | case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; /* fallthrough */ | ||
| 4702 | case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; /* fallthrough */ | ||
| 4703 | } | ||
| 4704 | |||
| 4705 | coefficients128_4 = vld1q_s32(tempC); | ||
| 4706 | samples128_4 = vld1q_s32(tempS); | ||
| 4707 | runningOrder = 0; | ||
| 4708 | } | ||
| 4709 | |||
| 4710 | /* 8 - 11 */ | ||
| 4711 | if (runningOrder == 4) { | ||
| 4712 | coefficients128_8 = vld1q_s32(coefficients + 8); | ||
| 4713 | samples128_8 = vld1q_s32(pSamplesOut - 12); | ||
| 4714 | runningOrder -= 4; | ||
| 4715 | } else { | ||
| 4716 | switch (runningOrder) { | ||
| 4717 | case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; /* fallthrough */ | ||
| 4718 | case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; /* fallthrough */ | ||
| 4719 | case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; /* fallthrough */ | ||
| 4720 | } | ||
| 4721 | |||
| 4722 | coefficients128_8 = vld1q_s32(tempC); | ||
| 4723 | samples128_8 = vld1q_s32(tempS); | ||
| 4724 | runningOrder = 0; | ||
| 4725 | } | ||
| 4726 | |||
| 4727 | /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ | ||
| 4728 | coefficients128_0 = drflac__vrevq_s32(coefficients128_0); | ||
| 4729 | coefficients128_4 = drflac__vrevq_s32(coefficients128_4); | ||
| 4730 | coefficients128_8 = drflac__vrevq_s32(coefficients128_8); | ||
| 4731 | } | ||
| 4732 | |||
| 4733 | /* For this version we are doing one sample at a time. */ | ||
| 4734 | while (pDecodedSamples < pDecodedSamplesEnd) { | ||
| 4735 | if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || | ||
| 4736 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || | ||
| 4737 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || | ||
| 4738 | !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { | ||
| 4739 | return DRFLAC_FALSE; | ||
| 4740 | } | ||
| 4741 | |||
| 4742 | zeroCountPart128 = vld1q_u32(zeroCountParts); | ||
| 4743 | riceParamPart128 = vld1q_u32(riceParamParts); | ||
| 4744 | |||
| 4745 | riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); | ||
| 4746 | riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); | ||
| 4747 | riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); | ||
| 4748 | |||
| 4749 | for (i = 0; i < 4; i += 1) { | ||
| 4750 | int64x1_t prediction64; | ||
| 4751 | |||
| 4752 | prediction128 = veorq_s64(prediction128, prediction128); /* Reset to 0. */ | ||
| 4753 | switch (order) | ||
| 4754 | { | ||
| 4755 | case 12: | ||
| 4756 | case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); | ||
| 4757 | case 10: | ||
| 4758 | case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); | ||
| 4759 | case 8: | ||
| 4760 | case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); | ||
| 4761 | case 6: | ||
| 4762 | case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); | ||
| 4763 | case 4: | ||
| 4764 | case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); | ||
| 4765 | case 2: | ||
| 4766 | case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); | ||
| 4767 | } | ||
| 4768 | |||
| 4769 | /* Horizontal add and shift. */ | ||
| 4770 | prediction64 = drflac__vhaddq_s64(prediction128); | ||
| 4771 | prediction64 = vshl_s64(prediction64, shift64); | ||
| 4772 | prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); | ||
| 4773 | |||
| 4774 | /* Our value should be sitting in prediction64[0]. We need to combine this with our SSE samples. */ | ||
| 4775 | samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); | ||
| 4776 | samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); | ||
| 4777 | samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); | ||
| 4778 | |||
| 4779 | /* Slide our rice parameter down so that the value in position 0 contains the next one to process. */ | ||
| 4780 | riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); | ||
| 4781 | } | ||
| 4782 | |||
| 4783 | /* We store samples in groups of 4. */ | ||
| 4784 | vst1q_s32(pDecodedSamples, samples128_0); | ||
| 4785 | pDecodedSamples += 4; | ||
| 4786 | } | ||
| 4787 | |||
| 4788 | /* Make sure we process the last few samples. */ | ||
| 4789 | i = (count & ~3); | ||
| 4790 | while (i < (int)count) { | ||
| 4791 | /* Rice extraction. */ | ||
| 4792 | if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { | ||
| 4793 | return DRFLAC_FALSE; | ||
| 4794 | } | ||
| 4795 | |||
| 4796 | /* Rice reconstruction. */ | ||
| 4797 | riceParamParts[0] &= riceParamMask; | ||
| 4798 | riceParamParts[0] |= (zeroCountParts[0] << riceParam); | ||
| 4799 | riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; | ||
| 4800 | |||
| 4801 | /* Sample reconstruction. */ | ||
| 4802 | pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); | ||
| 4803 | |||
| 4804 | i += 1; | ||
| 4805 | pDecodedSamples += 1; | ||
| 4806 | } | ||
| 4807 | |||
| 4808 | return DRFLAC_TRUE; | ||
| 4809 | } | ||
| 4810 | |||
| 4811 | static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) | ||
| 4812 | { | ||
| 4813 | DRFLAC_ASSERT(bs != NULL); | ||
| 4814 | DRFLAC_ASSERT(pSamplesOut != NULL); | ||
| 4815 | |||
| 4816 | /* In my testing the order is rarely > 12, so in this case I'm going to simplify the NEON implementation by only handling order <= 12. */ | ||
| 4817 | if (lpcOrder > 0 && lpcOrder <= 12) { | ||
| 4818 | if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { | ||
| 4819 | return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); | ||
| 4820 | } else { | ||
| 4821 | return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); | ||
| 4822 | } | ||
| 4823 | } else { | ||
| 4824 | return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); | ||
| 4825 | } | ||
| 4826 | } | ||
| 4827 | #endif | ||
| 4828 | |||
| 4829 | static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) | ||
| 4830 | { | ||
| 4831 | #if defined(DRFLAC_SUPPORT_SSE41) | ||
| 4832 | if (drflac__gIsSSE41Supported) { | ||
| 4833 | return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); | ||
| 4834 | } else | ||
| 4835 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 4836 | if (drflac__gIsNEONSupported) { | ||
| 4837 | return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); | ||
| 4838 | } else | ||
| 4839 | #endif | ||
| 4840 | { | ||
| 4841 | /* Scalar fallback. */ | ||
| 4842 | #if 0 | ||
| 4843 | return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); | ||
| 4844 | #else | ||
| 4845 | return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); | ||
| 4846 | #endif | ||
| 4847 | } | ||
| 4848 | } | ||
| 4849 | |||
| 4850 | /* Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. */ | ||
| 4851 | static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) | ||
| 4852 | { | ||
| 4853 | drflac_uint32 i; | ||
| 4854 | |||
| 4855 | DRFLAC_ASSERT(bs != NULL); | ||
| 4856 | |||
| 4857 | for (i = 0; i < count; ++i) { | ||
| 4858 | if (!drflac__seek_rice_parts(bs, riceParam)) { | ||
| 4859 | return DRFLAC_FALSE; | ||
| 4860 | } | ||
| 4861 | } | ||
| 4862 | |||
| 4863 | return DRFLAC_TRUE; | ||
| 4864 | } | ||
| 4865 | |||
| 4866 | #if defined(__clang__) | ||
| 4867 | __attribute__((no_sanitize("signed-integer-overflow"))) | ||
| 4868 | #endif | ||
| 4869 | static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) | ||
| 4870 | { | ||
| 4871 | drflac_uint32 i; | ||
| 4872 | |||
| 4873 | DRFLAC_ASSERT(bs != NULL); | ||
| 4874 | DRFLAC_ASSERT(unencodedBitsPerSample <= 31); /* <-- unencodedBitsPerSample is a 5 bit number, so cannot exceed 31. */ | ||
| 4875 | DRFLAC_ASSERT(pSamplesOut != NULL); | ||
| 4876 | |||
| 4877 | for (i = 0; i < count; ++i) { | ||
| 4878 | if (unencodedBitsPerSample > 0) { | ||
| 4879 | if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { | ||
| 4880 | return DRFLAC_FALSE; | ||
| 4881 | } | ||
| 4882 | } else { | ||
| 4883 | pSamplesOut[i] = 0; | ||
| 4884 | } | ||
| 4885 | |||
| 4886 | if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { | ||
| 4887 | pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); | ||
| 4888 | } else { | ||
| 4889 | pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); | ||
| 4890 | } | ||
| 4891 | } | ||
| 4892 | |||
| 4893 | return DRFLAC_TRUE; | ||
| 4894 | } | ||
| 4895 | |||
| 4896 | |||
| 4897 | /* | ||
| 4898 | Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called | ||
| 4899 | when the decoder is sitting at the very start of the RESIDUAL block. The first <order> residuals will be ignored. The | ||
| 4900 | <blockSize> and <order> parameters are used to determine how many residual values need to be decoded. | ||
| 4901 | */ | ||
| 4902 | static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) | ||
| 4903 | { | ||
| 4904 | drflac_uint8 residualMethod; | ||
| 4905 | drflac_uint8 partitionOrder; | ||
| 4906 | drflac_uint32 samplesInPartition; | ||
| 4907 | drflac_uint32 partitionsRemaining; | ||
| 4908 | |||
| 4909 | DRFLAC_ASSERT(bs != NULL); | ||
| 4910 | DRFLAC_ASSERT(blockSize != 0); | ||
| 4911 | DRFLAC_ASSERT(pDecodedSamples != NULL); /* <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode? */ | ||
| 4912 | |||
| 4913 | if (!drflac__read_uint8(bs, 2, &residualMethod)) { | ||
| 4914 | return DRFLAC_FALSE; | ||
| 4915 | } | ||
| 4916 | |||
| 4917 | if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { | ||
| 4918 | return DRFLAC_FALSE; /* Unknown or unsupported residual coding method. */ | ||
| 4919 | } | ||
| 4920 | |||
| 4921 | /* Ignore the first <order> values. */ | ||
| 4922 | pDecodedSamples += lpcOrder; | ||
| 4923 | |||
| 4924 | if (!drflac__read_uint8(bs, 4, &partitionOrder)) { | ||
| 4925 | return DRFLAC_FALSE; | ||
| 4926 | } | ||
| 4927 | |||
| 4928 | /* | ||
| 4929 | From the FLAC spec: | ||
| 4930 | The Rice partition order in a Rice-coded residual section must be less than or equal to 8. | ||
| 4931 | */ | ||
| 4932 | if (partitionOrder > 8) { | ||
| 4933 | return DRFLAC_FALSE; | ||
| 4934 | } | ||
| 4935 | |||
| 4936 | /* Validation check. */ | ||
| 4937 | if ((blockSize / (1 << partitionOrder)) < lpcOrder) { | ||
| 4938 | return DRFLAC_FALSE; | ||
| 4939 | } | ||
| 4940 | |||
| 4941 | samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; | ||
| 4942 | partitionsRemaining = (1 << partitionOrder); | ||
| 4943 | for (;;) { | ||
| 4944 | drflac_uint8 riceParam = 0; | ||
| 4945 | if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { | ||
| 4946 | if (!drflac__read_uint8(bs, 4, &riceParam)) { | ||
| 4947 | return DRFLAC_FALSE; | ||
| 4948 | } | ||
| 4949 | if (riceParam == 15) { | ||
| 4950 | riceParam = 0xFF; | ||
| 4951 | } | ||
| 4952 | } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { | ||
| 4953 | if (!drflac__read_uint8(bs, 5, &riceParam)) { | ||
| 4954 | return DRFLAC_FALSE; | ||
| 4955 | } | ||
| 4956 | if (riceParam == 31) { | ||
| 4957 | riceParam = 0xFF; | ||
| 4958 | } | ||
| 4959 | } | ||
| 4960 | |||
| 4961 | if (riceParam != 0xFF) { | ||
| 4962 | if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { | ||
| 4963 | return DRFLAC_FALSE; | ||
| 4964 | } | ||
| 4965 | } else { | ||
| 4966 | drflac_uint8 unencodedBitsPerSample = 0; | ||
| 4967 | if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { | ||
| 4968 | return DRFLAC_FALSE; | ||
| 4969 | } | ||
| 4970 | |||
| 4971 | if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { | ||
| 4972 | return DRFLAC_FALSE; | ||
| 4973 | } | ||
| 4974 | } | ||
| 4975 | |||
| 4976 | pDecodedSamples += samplesInPartition; | ||
| 4977 | |||
| 4978 | if (partitionsRemaining == 1) { | ||
| 4979 | break; | ||
| 4980 | } | ||
| 4981 | |||
| 4982 | partitionsRemaining -= 1; | ||
| 4983 | |||
| 4984 | if (partitionOrder != 0) { | ||
| 4985 | samplesInPartition = blockSize / (1 << partitionOrder); | ||
| 4986 | } | ||
| 4987 | } | ||
| 4988 | |||
| 4989 | return DRFLAC_TRUE; | ||
| 4990 | } | ||
| 4991 | |||
| 4992 | /* | ||
| 4993 | Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called | ||
| 4994 | when the decoder is sitting at the very start of the RESIDUAL block. The first <order> residuals will be set to 0. The | ||
| 4995 | <blockSize> and <order> parameters are used to determine how many residual values need to be decoded. | ||
| 4996 | */ | ||
| 4997 | static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) | ||
| 4998 | { | ||
| 4999 | drflac_uint8 residualMethod; | ||
| 5000 | drflac_uint8 partitionOrder; | ||
| 5001 | drflac_uint32 samplesInPartition; | ||
| 5002 | drflac_uint32 partitionsRemaining; | ||
| 5003 | |||
| 5004 | DRFLAC_ASSERT(bs != NULL); | ||
| 5005 | DRFLAC_ASSERT(blockSize != 0); | ||
| 5006 | |||
| 5007 | if (!drflac__read_uint8(bs, 2, &residualMethod)) { | ||
| 5008 | return DRFLAC_FALSE; | ||
| 5009 | } | ||
| 5010 | |||
| 5011 | if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { | ||
| 5012 | return DRFLAC_FALSE; /* Unknown or unsupported residual coding method. */ | ||
| 5013 | } | ||
| 5014 | |||
| 5015 | if (!drflac__read_uint8(bs, 4, &partitionOrder)) { | ||
| 5016 | return DRFLAC_FALSE; | ||
| 5017 | } | ||
| 5018 | |||
| 5019 | /* | ||
| 5020 | From the FLAC spec: | ||
| 5021 | The Rice partition order in a Rice-coded residual section must be less than or equal to 8. | ||
| 5022 | */ | ||
| 5023 | if (partitionOrder > 8) { | ||
| 5024 | return DRFLAC_FALSE; | ||
| 5025 | } | ||
| 5026 | |||
| 5027 | /* Validation check. */ | ||
| 5028 | if ((blockSize / (1 << partitionOrder)) <= order) { | ||
| 5029 | return DRFLAC_FALSE; | ||
| 5030 | } | ||
| 5031 | |||
| 5032 | samplesInPartition = (blockSize / (1 << partitionOrder)) - order; | ||
| 5033 | partitionsRemaining = (1 << partitionOrder); | ||
| 5034 | for (;;) | ||
| 5035 | { | ||
| 5036 | drflac_uint8 riceParam = 0; | ||
| 5037 | if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { | ||
| 5038 | if (!drflac__read_uint8(bs, 4, &riceParam)) { | ||
| 5039 | return DRFLAC_FALSE; | ||
| 5040 | } | ||
| 5041 | if (riceParam == 15) { | ||
| 5042 | riceParam = 0xFF; | ||
| 5043 | } | ||
| 5044 | } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { | ||
| 5045 | if (!drflac__read_uint8(bs, 5, &riceParam)) { | ||
| 5046 | return DRFLAC_FALSE; | ||
| 5047 | } | ||
| 5048 | if (riceParam == 31) { | ||
| 5049 | riceParam = 0xFF; | ||
| 5050 | } | ||
| 5051 | } | ||
| 5052 | |||
| 5053 | if (riceParam != 0xFF) { | ||
| 5054 | if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { | ||
| 5055 | return DRFLAC_FALSE; | ||
| 5056 | } | ||
| 5057 | } else { | ||
| 5058 | drflac_uint8 unencodedBitsPerSample = 0; | ||
| 5059 | if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { | ||
| 5060 | return DRFLAC_FALSE; | ||
| 5061 | } | ||
| 5062 | |||
| 5063 | if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { | ||
| 5064 | return DRFLAC_FALSE; | ||
| 5065 | } | ||
| 5066 | } | ||
| 5067 | |||
| 5068 | |||
| 5069 | if (partitionsRemaining == 1) { | ||
| 5070 | break; | ||
| 5071 | } | ||
| 5072 | |||
| 5073 | partitionsRemaining -= 1; | ||
| 5074 | samplesInPartition = blockSize / (1 << partitionOrder); | ||
| 5075 | } | ||
| 5076 | |||
| 5077 | return DRFLAC_TRUE; | ||
| 5078 | } | ||
| 5079 | |||
| 5080 | |||
| 5081 | static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) | ||
| 5082 | { | ||
| 5083 | drflac_uint32 i; | ||
| 5084 | |||
| 5085 | /* Only a single sample needs to be decoded here. */ | ||
| 5086 | drflac_int32 sample; | ||
| 5087 | if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { | ||
| 5088 | return DRFLAC_FALSE; | ||
| 5089 | } | ||
| 5090 | |||
| 5091 | /* | ||
| 5092 | We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely) | ||
| 5093 | we'll want to look at a more efficient way. | ||
| 5094 | */ | ||
| 5095 | for (i = 0; i < blockSize; ++i) { | ||
| 5096 | pDecodedSamples[i] = sample; | ||
| 5097 | } | ||
| 5098 | |||
| 5099 | return DRFLAC_TRUE; | ||
| 5100 | } | ||
| 5101 | |||
| 5102 | static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) | ||
| 5103 | { | ||
| 5104 | drflac_uint32 i; | ||
| 5105 | |||
| 5106 | for (i = 0; i < blockSize; ++i) { | ||
| 5107 | drflac_int32 sample; | ||
| 5108 | if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { | ||
| 5109 | return DRFLAC_FALSE; | ||
| 5110 | } | ||
| 5111 | |||
| 5112 | pDecodedSamples[i] = sample; | ||
| 5113 | } | ||
| 5114 | |||
| 5115 | return DRFLAC_TRUE; | ||
| 5116 | } | ||
| 5117 | |||
| 5118 | static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) | ||
| 5119 | { | ||
| 5120 | drflac_uint32 i; | ||
| 5121 | |||
| 5122 | static drflac_int32 lpcCoefficientsTable[5][4] = { | ||
| 5123 | {0, 0, 0, 0}, | ||
| 5124 | {1, 0, 0, 0}, | ||
| 5125 | {2, -1, 0, 0}, | ||
| 5126 | {3, -3, 1, 0}, | ||
| 5127 | {4, -6, 4, -1} | ||
| 5128 | }; | ||
| 5129 | |||
| 5130 | /* Warm up samples and coefficients. */ | ||
| 5131 | for (i = 0; i < lpcOrder; ++i) { | ||
| 5132 | drflac_int32 sample; | ||
| 5133 | if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { | ||
| 5134 | return DRFLAC_FALSE; | ||
| 5135 | } | ||
| 5136 | |||
| 5137 | pDecodedSamples[i] = sample; | ||
| 5138 | } | ||
| 5139 | |||
| 5140 | if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { | ||
| 5141 | return DRFLAC_FALSE; | ||
| 5142 | } | ||
| 5143 | |||
| 5144 | return DRFLAC_TRUE; | ||
| 5145 | } | ||
| 5146 | |||
| 5147 | static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) | ||
| 5148 | { | ||
| 5149 | drflac_uint8 i; | ||
| 5150 | drflac_uint8 lpcPrecision; | ||
| 5151 | drflac_int8 lpcShift; | ||
| 5152 | drflac_int32 coefficients[32]; | ||
| 5153 | |||
| 5154 | /* Warm up samples. */ | ||
| 5155 | for (i = 0; i < lpcOrder; ++i) { | ||
| 5156 | drflac_int32 sample; | ||
| 5157 | if (!drflac__read_int32(bs, bitsPerSample, &sample)) { | ||
| 5158 | return DRFLAC_FALSE; | ||
| 5159 | } | ||
| 5160 | |||
| 5161 | pDecodedSamples[i] = sample; | ||
| 5162 | } | ||
| 5163 | |||
| 5164 | if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { | ||
| 5165 | return DRFLAC_FALSE; | ||
| 5166 | } | ||
| 5167 | if (lpcPrecision == 15) { | ||
| 5168 | return DRFLAC_FALSE; /* Invalid. */ | ||
| 5169 | } | ||
| 5170 | lpcPrecision += 1; | ||
| 5171 | |||
| 5172 | if (!drflac__read_int8(bs, 5, &lpcShift)) { | ||
| 5173 | return DRFLAC_FALSE; | ||
| 5174 | } | ||
| 5175 | |||
| 5176 | /* | ||
| 5177 | From the FLAC specification: | ||
| 5178 | |||
| 5179 | Quantized linear predictor coefficient shift needed in bits (NOTE: this number is signed two's-complement) | ||
| 5180 | |||
| 5181 | Emphasis on the "signed two's-complement". In practice there does not seem to be any encoders nor decoders supporting negative shifts. For now dr_flac is | ||
| 5182 | not going to support negative shifts as I don't have any reference files. However, when a reference file comes through I will consider adding support. | ||
| 5183 | */ | ||
| 5184 | if (lpcShift < 0) { | ||
| 5185 | return DRFLAC_FALSE; | ||
| 5186 | } | ||
| 5187 | |||
| 5188 | DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); | ||
| 5189 | for (i = 0; i < lpcOrder; ++i) { | ||
| 5190 | if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { | ||
| 5191 | return DRFLAC_FALSE; | ||
| 5192 | } | ||
| 5193 | } | ||
| 5194 | |||
| 5195 | if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { | ||
| 5196 | return DRFLAC_FALSE; | ||
| 5197 | } | ||
| 5198 | |||
| 5199 | return DRFLAC_TRUE; | ||
| 5200 | } | ||
| 5201 | |||
| 5202 | |||
| 5203 | static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) | ||
| 5204 | { | ||
| 5205 | const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; | ||
| 5206 | const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; /* -1 = reserved. */ | ||
| 5207 | |||
| 5208 | DRFLAC_ASSERT(bs != NULL); | ||
| 5209 | DRFLAC_ASSERT(header != NULL); | ||
| 5210 | |||
| 5211 | /* Keep looping until we find a valid sync code. */ | ||
| 5212 | for (;;) { | ||
| 5213 | drflac_uint8 crc8 = 0xCE; /* 0xCE = drflac_crc8(0, 0x3FFE, 14); */ | ||
| 5214 | drflac_uint8 reserved = 0; | ||
| 5215 | drflac_uint8 blockingStrategy = 0; | ||
| 5216 | drflac_uint8 blockSize = 0; | ||
| 5217 | drflac_uint8 sampleRate = 0; | ||
| 5218 | drflac_uint8 channelAssignment = 0; | ||
| 5219 | drflac_uint8 bitsPerSample = 0; | ||
| 5220 | drflac_bool32 isVariableBlockSize; | ||
| 5221 | |||
| 5222 | if (!drflac__find_and_seek_to_next_sync_code(bs)) { | ||
| 5223 | return DRFLAC_FALSE; | ||
| 5224 | } | ||
| 5225 | |||
| 5226 | if (!drflac__read_uint8(bs, 1, &reserved)) { | ||
| 5227 | return DRFLAC_FALSE; | ||
| 5228 | } | ||
| 5229 | if (reserved == 1) { | ||
| 5230 | continue; | ||
| 5231 | } | ||
| 5232 | crc8 = drflac_crc8(crc8, reserved, 1); | ||
| 5233 | |||
| 5234 | if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { | ||
| 5235 | return DRFLAC_FALSE; | ||
| 5236 | } | ||
| 5237 | crc8 = drflac_crc8(crc8, blockingStrategy, 1); | ||
| 5238 | |||
| 5239 | if (!drflac__read_uint8(bs, 4, &blockSize)) { | ||
| 5240 | return DRFLAC_FALSE; | ||
| 5241 | } | ||
| 5242 | if (blockSize == 0) { | ||
| 5243 | continue; | ||
| 5244 | } | ||
| 5245 | crc8 = drflac_crc8(crc8, blockSize, 4); | ||
| 5246 | |||
| 5247 | if (!drflac__read_uint8(bs, 4, &sampleRate)) { | ||
| 5248 | return DRFLAC_FALSE; | ||
| 5249 | } | ||
| 5250 | crc8 = drflac_crc8(crc8, sampleRate, 4); | ||
| 5251 | |||
| 5252 | if (!drflac__read_uint8(bs, 4, &channelAssignment)) { | ||
| 5253 | return DRFLAC_FALSE; | ||
| 5254 | } | ||
| 5255 | if (channelAssignment > 10) { | ||
| 5256 | continue; | ||
| 5257 | } | ||
| 5258 | crc8 = drflac_crc8(crc8, channelAssignment, 4); | ||
| 5259 | |||
| 5260 | if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { | ||
| 5261 | return DRFLAC_FALSE; | ||
| 5262 | } | ||
| 5263 | if (bitsPerSample == 3 || bitsPerSample == 7) { | ||
| 5264 | continue; | ||
| 5265 | } | ||
| 5266 | crc8 = drflac_crc8(crc8, bitsPerSample, 3); | ||
| 5267 | |||
| 5268 | |||
| 5269 | if (!drflac__read_uint8(bs, 1, &reserved)) { | ||
| 5270 | return DRFLAC_FALSE; | ||
| 5271 | } | ||
| 5272 | if (reserved == 1) { | ||
| 5273 | continue; | ||
| 5274 | } | ||
| 5275 | crc8 = drflac_crc8(crc8, reserved, 1); | ||
| 5276 | |||
| 5277 | |||
| 5278 | isVariableBlockSize = blockingStrategy == 1; | ||
| 5279 | if (isVariableBlockSize) { | ||
| 5280 | drflac_uint64 pcmFrameNumber; | ||
| 5281 | drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); | ||
| 5282 | if (result != DRFLAC_SUCCESS) { | ||
| 5283 | if (result == DRFLAC_AT_END) { | ||
| 5284 | return DRFLAC_FALSE; | ||
| 5285 | } else { | ||
| 5286 | continue; | ||
| 5287 | } | ||
| 5288 | } | ||
| 5289 | header->flacFrameNumber = 0; | ||
| 5290 | header->pcmFrameNumber = pcmFrameNumber; | ||
| 5291 | } else { | ||
| 5292 | drflac_uint64 flacFrameNumber = 0; | ||
| 5293 | drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); | ||
| 5294 | if (result != DRFLAC_SUCCESS) { | ||
| 5295 | if (result == DRFLAC_AT_END) { | ||
| 5296 | return DRFLAC_FALSE; | ||
| 5297 | } else { | ||
| 5298 | continue; | ||
| 5299 | } | ||
| 5300 | } | ||
| 5301 | header->flacFrameNumber = (drflac_uint32)flacFrameNumber; /* <-- Safe cast. */ | ||
| 5302 | header->pcmFrameNumber = 0; | ||
| 5303 | } | ||
| 5304 | |||
| 5305 | |||
| 5306 | DRFLAC_ASSERT(blockSize > 0); | ||
| 5307 | if (blockSize == 1) { | ||
| 5308 | header->blockSizeInPCMFrames = 192; | ||
| 5309 | } else if (blockSize <= 5) { | ||
| 5310 | DRFLAC_ASSERT(blockSize >= 2); | ||
| 5311 | header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); | ||
| 5312 | } else if (blockSize == 6) { | ||
| 5313 | if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { | ||
| 5314 | return DRFLAC_FALSE; | ||
| 5315 | } | ||
| 5316 | crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8); | ||
| 5317 | header->blockSizeInPCMFrames += 1; | ||
| 5318 | } else if (blockSize == 7) { | ||
| 5319 | if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { | ||
| 5320 | return DRFLAC_FALSE; | ||
| 5321 | } | ||
| 5322 | crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16); | ||
| 5323 | if (header->blockSizeInPCMFrames == 0xFFFF) { | ||
| 5324 | return DRFLAC_FALSE; /* Frame is too big. This is the size of the frame minus 1. The STREAMINFO block defines the max block size which is 16-bits. Adding one will make it 17 bits and therefore too big. */ | ||
| 5325 | } | ||
| 5326 | header->blockSizeInPCMFrames += 1; | ||
| 5327 | } else { | ||
| 5328 | DRFLAC_ASSERT(blockSize >= 8); | ||
| 5329 | header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); | ||
| 5330 | } | ||
| 5331 | |||
| 5332 | |||
| 5333 | if (sampleRate <= 11) { | ||
| 5334 | header->sampleRate = sampleRateTable[sampleRate]; | ||
| 5335 | } else if (sampleRate == 12) { | ||
| 5336 | if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { | ||
| 5337 | return DRFLAC_FALSE; | ||
| 5338 | } | ||
| 5339 | crc8 = drflac_crc8(crc8, header->sampleRate, 8); | ||
| 5340 | header->sampleRate *= 1000; | ||
| 5341 | } else if (sampleRate == 13) { | ||
| 5342 | if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { | ||
| 5343 | return DRFLAC_FALSE; | ||
| 5344 | } | ||
| 5345 | crc8 = drflac_crc8(crc8, header->sampleRate, 16); | ||
| 5346 | } else if (sampleRate == 14) { | ||
| 5347 | if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { | ||
| 5348 | return DRFLAC_FALSE; | ||
| 5349 | } | ||
| 5350 | crc8 = drflac_crc8(crc8, header->sampleRate, 16); | ||
| 5351 | header->sampleRate *= 10; | ||
| 5352 | } else { | ||
| 5353 | continue; /* Invalid. Assume an invalid block. */ | ||
| 5354 | } | ||
| 5355 | |||
| 5356 | |||
| 5357 | header->channelAssignment = channelAssignment; | ||
| 5358 | |||
| 5359 | header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; | ||
| 5360 | if (header->bitsPerSample == 0) { | ||
| 5361 | header->bitsPerSample = streaminfoBitsPerSample; | ||
| 5362 | } | ||
| 5363 | |||
| 5364 | if (header->bitsPerSample != streaminfoBitsPerSample) { | ||
| 5365 | /* If this subframe has a different bitsPerSample then streaminfo or the first frame, reject it */ | ||
| 5366 | return DRFLAC_FALSE; | ||
| 5367 | } | ||
| 5368 | |||
| 5369 | if (!drflac__read_uint8(bs, 8, &header->crc8)) { | ||
| 5370 | return DRFLAC_FALSE; | ||
| 5371 | } | ||
| 5372 | |||
| 5373 | #ifndef DR_FLAC_NO_CRC | ||
| 5374 | if (header->crc8 != crc8) { | ||
| 5375 | continue; /* CRC mismatch. Loop back to the top and find the next sync code. */ | ||
| 5376 | } | ||
| 5377 | #endif | ||
| 5378 | return DRFLAC_TRUE; | ||
| 5379 | } | ||
| 5380 | } | ||
| 5381 | |||
| 5382 | static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) | ||
| 5383 | { | ||
| 5384 | drflac_uint8 header; | ||
| 5385 | int type; | ||
| 5386 | |||
| 5387 | if (!drflac__read_uint8(bs, 8, &header)) { | ||
| 5388 | return DRFLAC_FALSE; | ||
| 5389 | } | ||
| 5390 | |||
| 5391 | /* First bit should always be 0. */ | ||
| 5392 | if ((header & 0x80) != 0) { | ||
| 5393 | return DRFLAC_FALSE; | ||
| 5394 | } | ||
| 5395 | |||
| 5396 | type = (header & 0x7E) >> 1; | ||
| 5397 | if (type == 0) { | ||
| 5398 | pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; | ||
| 5399 | } else if (type == 1) { | ||
| 5400 | pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; | ||
| 5401 | } else { | ||
| 5402 | if ((type & 0x20) != 0) { | ||
| 5403 | pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; | ||
| 5404 | pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1; | ||
| 5405 | } else if ((type & 0x08) != 0) { | ||
| 5406 | pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; | ||
| 5407 | pSubframe->lpcOrder = (drflac_uint8)(type & 0x07); | ||
| 5408 | if (pSubframe->lpcOrder > 4) { | ||
| 5409 | pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; | ||
| 5410 | pSubframe->lpcOrder = 0; | ||
| 5411 | } | ||
| 5412 | } else { | ||
| 5413 | pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; | ||
| 5414 | } | ||
| 5415 | } | ||
| 5416 | |||
| 5417 | if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { | ||
| 5418 | return DRFLAC_FALSE; | ||
| 5419 | } | ||
| 5420 | |||
| 5421 | /* Wasted bits per sample. */ | ||
| 5422 | pSubframe->wastedBitsPerSample = 0; | ||
| 5423 | if ((header & 0x01) == 1) { | ||
| 5424 | unsigned int wastedBitsPerSample; | ||
| 5425 | if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { | ||
| 5426 | return DRFLAC_FALSE; | ||
| 5427 | } | ||
| 5428 | pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1; | ||
| 5429 | } | ||
| 5430 | |||
| 5431 | return DRFLAC_TRUE; | ||
| 5432 | } | ||
| 5433 | |||
| 5434 | static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) | ||
| 5435 | { | ||
| 5436 | drflac_subframe* pSubframe; | ||
| 5437 | drflac_uint32 subframeBitsPerSample; | ||
| 5438 | |||
| 5439 | DRFLAC_ASSERT(bs != NULL); | ||
| 5440 | DRFLAC_ASSERT(frame != NULL); | ||
| 5441 | |||
| 5442 | pSubframe = frame->subframes + subframeIndex; | ||
| 5443 | if (!drflac__read_subframe_header(bs, pSubframe)) { | ||
| 5444 | return DRFLAC_FALSE; | ||
| 5445 | } | ||
| 5446 | |||
| 5447 | /* Side channels require an extra bit per sample. Took a while to figure that one out... */ | ||
| 5448 | subframeBitsPerSample = frame->header.bitsPerSample; | ||
| 5449 | if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { | ||
| 5450 | subframeBitsPerSample += 1; | ||
| 5451 | } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { | ||
| 5452 | subframeBitsPerSample += 1; | ||
| 5453 | } | ||
| 5454 | |||
| 5455 | if (subframeBitsPerSample > 32) { | ||
| 5456 | /* libFLAC and ffmpeg reject 33-bit subframes as well */ | ||
| 5457 | return DRFLAC_FALSE; | ||
| 5458 | } | ||
| 5459 | |||
| 5460 | /* Need to handle wasted bits per sample. */ | ||
| 5461 | if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { | ||
| 5462 | return DRFLAC_FALSE; | ||
| 5463 | } | ||
| 5464 | subframeBitsPerSample -= pSubframe->wastedBitsPerSample; | ||
| 5465 | |||
| 5466 | pSubframe->pSamplesS32 = pDecodedSamplesOut; | ||
| 5467 | |||
| 5468 | switch (pSubframe->subframeType) | ||
| 5469 | { | ||
| 5470 | case DRFLAC_SUBFRAME_CONSTANT: | ||
| 5471 | { | ||
| 5472 | drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); | ||
| 5473 | } break; | ||
| 5474 | |||
| 5475 | case DRFLAC_SUBFRAME_VERBATIM: | ||
| 5476 | { | ||
| 5477 | drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); | ||
| 5478 | } break; | ||
| 5479 | |||
| 5480 | case DRFLAC_SUBFRAME_FIXED: | ||
| 5481 | { | ||
| 5482 | drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); | ||
| 5483 | } break; | ||
| 5484 | |||
| 5485 | case DRFLAC_SUBFRAME_LPC: | ||
| 5486 | { | ||
| 5487 | drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); | ||
| 5488 | } break; | ||
| 5489 | |||
| 5490 | default: return DRFLAC_FALSE; | ||
| 5491 | } | ||
| 5492 | |||
| 5493 | return DRFLAC_TRUE; | ||
| 5494 | } | ||
| 5495 | |||
| 5496 | static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) | ||
| 5497 | { | ||
| 5498 | drflac_subframe* pSubframe; | ||
| 5499 | drflac_uint32 subframeBitsPerSample; | ||
| 5500 | |||
| 5501 | DRFLAC_ASSERT(bs != NULL); | ||
| 5502 | DRFLAC_ASSERT(frame != NULL); | ||
| 5503 | |||
| 5504 | pSubframe = frame->subframes + subframeIndex; | ||
| 5505 | if (!drflac__read_subframe_header(bs, pSubframe)) { | ||
| 5506 | return DRFLAC_FALSE; | ||
| 5507 | } | ||
| 5508 | |||
| 5509 | /* Side channels require an extra bit per sample. Took a while to figure that one out... */ | ||
| 5510 | subframeBitsPerSample = frame->header.bitsPerSample; | ||
| 5511 | if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { | ||
| 5512 | subframeBitsPerSample += 1; | ||
| 5513 | } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { | ||
| 5514 | subframeBitsPerSample += 1; | ||
| 5515 | } | ||
| 5516 | |||
| 5517 | /* Need to handle wasted bits per sample. */ | ||
| 5518 | if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { | ||
| 5519 | return DRFLAC_FALSE; | ||
| 5520 | } | ||
| 5521 | subframeBitsPerSample -= pSubframe->wastedBitsPerSample; | ||
| 5522 | |||
| 5523 | pSubframe->pSamplesS32 = NULL; | ||
| 5524 | |||
| 5525 | switch (pSubframe->subframeType) | ||
| 5526 | { | ||
| 5527 | case DRFLAC_SUBFRAME_CONSTANT: | ||
| 5528 | { | ||
| 5529 | if (!drflac__seek_bits(bs, subframeBitsPerSample)) { | ||
| 5530 | return DRFLAC_FALSE; | ||
| 5531 | } | ||
| 5532 | } break; | ||
| 5533 | |||
| 5534 | case DRFLAC_SUBFRAME_VERBATIM: | ||
| 5535 | { | ||
| 5536 | unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; | ||
| 5537 | if (!drflac__seek_bits(bs, bitsToSeek)) { | ||
| 5538 | return DRFLAC_FALSE; | ||
| 5539 | } | ||
| 5540 | } break; | ||
| 5541 | |||
| 5542 | case DRFLAC_SUBFRAME_FIXED: | ||
| 5543 | { | ||
| 5544 | unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; | ||
| 5545 | if (!drflac__seek_bits(bs, bitsToSeek)) { | ||
| 5546 | return DRFLAC_FALSE; | ||
| 5547 | } | ||
| 5548 | |||
| 5549 | if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { | ||
| 5550 | return DRFLAC_FALSE; | ||
| 5551 | } | ||
| 5552 | } break; | ||
| 5553 | |||
| 5554 | case DRFLAC_SUBFRAME_LPC: | ||
| 5555 | { | ||
| 5556 | drflac_uint8 lpcPrecision; | ||
| 5557 | |||
| 5558 | unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; | ||
| 5559 | if (!drflac__seek_bits(bs, bitsToSeek)) { | ||
| 5560 | return DRFLAC_FALSE; | ||
| 5561 | } | ||
| 5562 | |||
| 5563 | if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { | ||
| 5564 | return DRFLAC_FALSE; | ||
| 5565 | } | ||
| 5566 | if (lpcPrecision == 15) { | ||
| 5567 | return DRFLAC_FALSE; /* Invalid. */ | ||
| 5568 | } | ||
| 5569 | lpcPrecision += 1; | ||
| 5570 | |||
| 5571 | |||
| 5572 | bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; /* +5 for shift. */ | ||
| 5573 | if (!drflac__seek_bits(bs, bitsToSeek)) { | ||
| 5574 | return DRFLAC_FALSE; | ||
| 5575 | } | ||
| 5576 | |||
| 5577 | if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { | ||
| 5578 | return DRFLAC_FALSE; | ||
| 5579 | } | ||
| 5580 | } break; | ||
| 5581 | |||
| 5582 | default: return DRFLAC_FALSE; | ||
| 5583 | } | ||
| 5584 | |||
| 5585 | return DRFLAC_TRUE; | ||
| 5586 | } | ||
| 5587 | |||
| 5588 | |||
| 5589 | static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) | ||
| 5590 | { | ||
| 5591 | drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; | ||
| 5592 | |||
| 5593 | DRFLAC_ASSERT(channelAssignment <= 10); | ||
| 5594 | return lookup[channelAssignment]; | ||
| 5595 | } | ||
| 5596 | |||
| 5597 | static drflac_result drflac__decode_flac_frame(drflac* pFlac) | ||
| 5598 | { | ||
| 5599 | int channelCount; | ||
| 5600 | int i; | ||
| 5601 | drflac_uint8 paddingSizeInBits; | ||
| 5602 | drflac_uint16 desiredCRC16; | ||
| 5603 | #ifndef DR_FLAC_NO_CRC | ||
| 5604 | drflac_uint16 actualCRC16; | ||
| 5605 | #endif | ||
| 5606 | |||
| 5607 | /* This function should be called while the stream is sitting on the first byte after the frame header. */ | ||
| 5608 | DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); | ||
| 5609 | |||
| 5610 | /* The frame block size must never be larger than the maximum block size defined by the FLAC stream. */ | ||
| 5611 | if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { | ||
| 5612 | return DRFLAC_ERROR; | ||
| 5613 | } | ||
| 5614 | |||
| 5615 | /* The number of channels in the frame must match the channel count from the STREAMINFO block. */ | ||
| 5616 | channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); | ||
| 5617 | if (channelCount != (int)pFlac->channels) { | ||
| 5618 | return DRFLAC_ERROR; | ||
| 5619 | } | ||
| 5620 | |||
| 5621 | for (i = 0; i < channelCount; ++i) { | ||
| 5622 | if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { | ||
| 5623 | return DRFLAC_ERROR; | ||
| 5624 | } | ||
| 5625 | } | ||
| 5626 | |||
| 5627 | paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); | ||
| 5628 | if (paddingSizeInBits > 0) { | ||
| 5629 | drflac_uint8 padding = 0; | ||
| 5630 | if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { | ||
| 5631 | return DRFLAC_AT_END; | ||
| 5632 | } | ||
| 5633 | } | ||
| 5634 | |||
| 5635 | #ifndef DR_FLAC_NO_CRC | ||
| 5636 | actualCRC16 = drflac__flush_crc16(&pFlac->bs); | ||
| 5637 | #endif | ||
| 5638 | if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { | ||
| 5639 | return DRFLAC_AT_END; | ||
| 5640 | } | ||
| 5641 | |||
| 5642 | #ifndef DR_FLAC_NO_CRC | ||
| 5643 | if (actualCRC16 != desiredCRC16) { | ||
| 5644 | return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */ | ||
| 5645 | } | ||
| 5646 | #endif | ||
| 5647 | |||
| 5648 | pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; | ||
| 5649 | |||
| 5650 | return DRFLAC_SUCCESS; | ||
| 5651 | } | ||
| 5652 | |||
| 5653 | static drflac_result drflac__seek_flac_frame(drflac* pFlac) | ||
| 5654 | { | ||
| 5655 | int channelCount; | ||
| 5656 | int i; | ||
| 5657 | drflac_uint16 desiredCRC16; | ||
| 5658 | #ifndef DR_FLAC_NO_CRC | ||
| 5659 | drflac_uint16 actualCRC16; | ||
| 5660 | #endif | ||
| 5661 | |||
| 5662 | channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); | ||
| 5663 | for (i = 0; i < channelCount; ++i) { | ||
| 5664 | if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { | ||
| 5665 | return DRFLAC_ERROR; | ||
| 5666 | } | ||
| 5667 | } | ||
| 5668 | |||
| 5669 | /* Padding. */ | ||
| 5670 | if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { | ||
| 5671 | return DRFLAC_ERROR; | ||
| 5672 | } | ||
| 5673 | |||
| 5674 | /* CRC. */ | ||
| 5675 | #ifndef DR_FLAC_NO_CRC | ||
| 5676 | actualCRC16 = drflac__flush_crc16(&pFlac->bs); | ||
| 5677 | #endif | ||
| 5678 | if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { | ||
| 5679 | return DRFLAC_AT_END; | ||
| 5680 | } | ||
| 5681 | |||
| 5682 | #ifndef DR_FLAC_NO_CRC | ||
| 5683 | if (actualCRC16 != desiredCRC16) { | ||
| 5684 | return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */ | ||
| 5685 | } | ||
| 5686 | #endif | ||
| 5687 | |||
| 5688 | return DRFLAC_SUCCESS; | ||
| 5689 | } | ||
| 5690 | |||
| 5691 | static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac) | ||
| 5692 | { | ||
| 5693 | DRFLAC_ASSERT(pFlac != NULL); | ||
| 5694 | |||
| 5695 | for (;;) { | ||
| 5696 | drflac_result result; | ||
| 5697 | |||
| 5698 | if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { | ||
| 5699 | return DRFLAC_FALSE; | ||
| 5700 | } | ||
| 5701 | |||
| 5702 | result = drflac__decode_flac_frame(pFlac); | ||
| 5703 | if (result != DRFLAC_SUCCESS) { | ||
| 5704 | if (result == DRFLAC_CRC_MISMATCH) { | ||
| 5705 | continue; /* CRC mismatch. Skip to the next frame. */ | ||
| 5706 | } else { | ||
| 5707 | return DRFLAC_FALSE; | ||
| 5708 | } | ||
| 5709 | } | ||
| 5710 | |||
| 5711 | return DRFLAC_TRUE; | ||
| 5712 | } | ||
| 5713 | } | ||
| 5714 | |||
| 5715 | static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame) | ||
| 5716 | { | ||
| 5717 | drflac_uint64 firstPCMFrame; | ||
| 5718 | drflac_uint64 lastPCMFrame; | ||
| 5719 | |||
| 5720 | DRFLAC_ASSERT(pFlac != NULL); | ||
| 5721 | |||
| 5722 | firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; | ||
| 5723 | if (firstPCMFrame == 0) { | ||
| 5724 | firstPCMFrame = ((drflac_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; | ||
| 5725 | } | ||
| 5726 | |||
| 5727 | lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; | ||
| 5728 | if (lastPCMFrame > 0) { | ||
| 5729 | lastPCMFrame -= 1; /* Needs to be zero based. */ | ||
| 5730 | } | ||
| 5731 | |||
| 5732 | if (pFirstPCMFrame) { | ||
| 5733 | *pFirstPCMFrame = firstPCMFrame; | ||
| 5734 | } | ||
| 5735 | if (pLastPCMFrame) { | ||
| 5736 | *pLastPCMFrame = lastPCMFrame; | ||
| 5737 | } | ||
| 5738 | } | ||
| 5739 | |||
| 5740 | static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) | ||
| 5741 | { | ||
| 5742 | drflac_bool32 result; | ||
| 5743 | |||
| 5744 | DRFLAC_ASSERT(pFlac != NULL); | ||
| 5745 | |||
| 5746 | result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); | ||
| 5747 | |||
| 5748 | DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); | ||
| 5749 | pFlac->currentPCMFrame = 0; | ||
| 5750 | |||
| 5751 | return result; | ||
| 5752 | } | ||
| 5753 | |||
| 5754 | static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac) | ||
| 5755 | { | ||
| 5756 | /* This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section. */ | ||
| 5757 | DRFLAC_ASSERT(pFlac != NULL); | ||
| 5758 | return drflac__seek_flac_frame(pFlac); | ||
| 5759 | } | ||
| 5760 | |||
| 5761 | |||
| 5762 | static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek) | ||
| 5763 | { | ||
| 5764 | drflac_uint64 pcmFramesRead = 0; | ||
| 5765 | while (pcmFramesToSeek > 0) { | ||
| 5766 | if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { | ||
| 5767 | if (!drflac__read_and_decode_next_flac_frame(pFlac)) { | ||
| 5768 | break; /* Couldn't read the next frame, so just break from the loop and return. */ | ||
| 5769 | } | ||
| 5770 | } else { | ||
| 5771 | if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { | ||
| 5772 | pcmFramesRead += pcmFramesToSeek; | ||
| 5773 | pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek; /* <-- Safe cast. Will always be < currentFrame.pcmFramesRemaining < 65536. */ | ||
| 5774 | pcmFramesToSeek = 0; | ||
| 5775 | } else { | ||
| 5776 | pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; | ||
| 5777 | pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; | ||
| 5778 | pFlac->currentFLACFrame.pcmFramesRemaining = 0; | ||
| 5779 | } | ||
| 5780 | } | ||
| 5781 | } | ||
| 5782 | |||
| 5783 | pFlac->currentPCMFrame += pcmFramesRead; | ||
| 5784 | return pcmFramesRead; | ||
| 5785 | } | ||
| 5786 | |||
| 5787 | |||
| 5788 | static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex) | ||
| 5789 | { | ||
| 5790 | drflac_bool32 isMidFrame = DRFLAC_FALSE; | ||
| 5791 | drflac_uint64 runningPCMFrameCount; | ||
| 5792 | |||
| 5793 | DRFLAC_ASSERT(pFlac != NULL); | ||
| 5794 | |||
| 5795 | /* If we are seeking forward we start from the current position. Otherwise we need to start all the way from the start of the file. */ | ||
| 5796 | if (pcmFrameIndex >= pFlac->currentPCMFrame) { | ||
| 5797 | /* Seeking forward. Need to seek from the current position. */ | ||
| 5798 | runningPCMFrameCount = pFlac->currentPCMFrame; | ||
| 5799 | |||
| 5800 | /* The frame header for the first frame may not yet have been read. We need to do that if necessary. */ | ||
| 5801 | if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { | ||
| 5802 | if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { | ||
| 5803 | return DRFLAC_FALSE; | ||
| 5804 | } | ||
| 5805 | } else { | ||
| 5806 | isMidFrame = DRFLAC_TRUE; | ||
| 5807 | } | ||
| 5808 | } else { | ||
| 5809 | /* Seeking backwards. Need to seek from the start of the file. */ | ||
| 5810 | runningPCMFrameCount = 0; | ||
| 5811 | |||
| 5812 | /* Move back to the start. */ | ||
| 5813 | if (!drflac__seek_to_first_frame(pFlac)) { | ||
| 5814 | return DRFLAC_FALSE; | ||
| 5815 | } | ||
| 5816 | |||
| 5817 | /* Decode the first frame in preparation for sample-exact seeking below. */ | ||
| 5818 | if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { | ||
| 5819 | return DRFLAC_FALSE; | ||
| 5820 | } | ||
| 5821 | } | ||
| 5822 | |||
| 5823 | /* | ||
| 5824 | We need to as quickly as possible find the frame that contains the target sample. To do this, we iterate over each frame and inspect its | ||
| 5825 | header. If based on the header we can determine that the frame contains the sample, we do a full decode of that frame. | ||
| 5826 | */ | ||
| 5827 | for (;;) { | ||
| 5828 | drflac_uint64 pcmFrameCountInThisFLACFrame; | ||
| 5829 | drflac_uint64 firstPCMFrameInFLACFrame = 0; | ||
| 5830 | drflac_uint64 lastPCMFrameInFLACFrame = 0; | ||
| 5831 | |||
| 5832 | drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); | ||
| 5833 | |||
| 5834 | pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; | ||
| 5835 | if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { | ||
| 5836 | /* | ||
| 5837 | The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend | ||
| 5838 | it never existed and keep iterating. | ||
| 5839 | */ | ||
| 5840 | drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; | ||
| 5841 | |||
| 5842 | if (!isMidFrame) { | ||
| 5843 | drflac_result result = drflac__decode_flac_frame(pFlac); | ||
| 5844 | if (result == DRFLAC_SUCCESS) { | ||
| 5845 | /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ | ||
| 5846 | return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */ | ||
| 5847 | } else { | ||
| 5848 | if (result == DRFLAC_CRC_MISMATCH) { | ||
| 5849 | goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ | ||
| 5850 | } else { | ||
| 5851 | return DRFLAC_FALSE; | ||
| 5852 | } | ||
| 5853 | } | ||
| 5854 | } else { | ||
| 5855 | /* We started seeking mid-frame which means we need to skip the frame decoding part. */ | ||
| 5856 | return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; | ||
| 5857 | } | ||
| 5858 | } else { | ||
| 5859 | /* | ||
| 5860 | It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this | ||
| 5861 | frame never existed and leave the running sample count untouched. | ||
| 5862 | */ | ||
| 5863 | if (!isMidFrame) { | ||
| 5864 | drflac_result result = drflac__seek_to_next_flac_frame(pFlac); | ||
| 5865 | if (result == DRFLAC_SUCCESS) { | ||
| 5866 | runningPCMFrameCount += pcmFrameCountInThisFLACFrame; | ||
| 5867 | } else { | ||
| 5868 | if (result == DRFLAC_CRC_MISMATCH) { | ||
| 5869 | goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ | ||
| 5870 | } else { | ||
| 5871 | return DRFLAC_FALSE; | ||
| 5872 | } | ||
| 5873 | } | ||
| 5874 | } else { | ||
| 5875 | /* | ||
| 5876 | We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with | ||
| 5877 | drflac__seek_to_next_flac_frame() which only works if the decoder is sitting on the byte just after the frame header. | ||
| 5878 | */ | ||
| 5879 | runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; | ||
| 5880 | pFlac->currentFLACFrame.pcmFramesRemaining = 0; | ||
| 5881 | isMidFrame = DRFLAC_FALSE; | ||
| 5882 | } | ||
| 5883 | |||
| 5884 | /* If we are seeking to the end of the file and we've just hit it, we're done. */ | ||
| 5885 | if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { | ||
| 5886 | return DRFLAC_TRUE; | ||
| 5887 | } | ||
| 5888 | } | ||
| 5889 | |||
| 5890 | next_iteration: | ||
| 5891 | /* Grab the next frame in preparation for the next iteration. */ | ||
| 5892 | if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { | ||
| 5893 | return DRFLAC_FALSE; | ||
| 5894 | } | ||
| 5895 | } | ||
| 5896 | } | ||
| 5897 | |||
| 5898 | |||
| 5899 | #if !defined(DR_FLAC_NO_CRC) | ||
| 5900 | /* | ||
| 5901 | We use an average compression ratio to determine our approximate start location. FLAC files are generally about 50%-70% the size of their | ||
| 5902 | uncompressed counterparts so we'll use this as a basis. I'm going to split the middle and use a factor of 0.6 to determine the starting | ||
| 5903 | location. | ||
| 5904 | */ | ||
| 5905 | #define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f | ||
| 5906 | |||
| 5907 | static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset) | ||
| 5908 | { | ||
| 5909 | DRFLAC_ASSERT(pFlac != NULL); | ||
| 5910 | DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); | ||
| 5911 | DRFLAC_ASSERT(targetByte >= rangeLo); | ||
| 5912 | DRFLAC_ASSERT(targetByte <= rangeHi); | ||
| 5913 | |||
| 5914 | *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; | ||
| 5915 | |||
| 5916 | for (;;) { | ||
| 5917 | /* After rangeLo == rangeHi == targetByte fails, we need to break out. */ | ||
| 5918 | drflac_uint64 lastTargetByte = targetByte; | ||
| 5919 | |||
| 5920 | /* When seeking to a byte, failure probably means we've attempted to seek beyond the end of the stream. To counter this we just halve it each attempt. */ | ||
| 5921 | if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) { | ||
| 5922 | /* If we couldn't even seek to the first byte in the stream we have a problem. Just abandon the whole thing. */ | ||
| 5923 | if (targetByte == 0) { | ||
| 5924 | drflac__seek_to_first_frame(pFlac); /* Try to recover. */ | ||
| 5925 | return DRFLAC_FALSE; | ||
| 5926 | } | ||
| 5927 | |||
| 5928 | /* Halve the byte location and continue. */ | ||
| 5929 | targetByte = rangeLo + ((rangeHi - rangeLo)/2); | ||
| 5930 | rangeHi = targetByte; | ||
| 5931 | } else { | ||
| 5932 | /* Getting here should mean that we have seeked to an appropriate byte. */ | ||
| 5933 | |||
| 5934 | /* Clear the details of the FLAC frame so we don't misreport data. */ | ||
| 5935 | DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); | ||
| 5936 | |||
| 5937 | /* | ||
| 5938 | Now seek to the next FLAC frame. We need to decode the entire frame (not just the header) because it's possible for the header to incorrectly pass the | ||
| 5939 | CRC check and return bad data. We need to decode the entire frame to be more certain. Although this seems unlikely, this has happened to me in testing | ||
| 5940 | so it needs to stay this way for now. | ||
| 5941 | */ | ||
| 5942 | #if 1 | ||
| 5943 | if (!drflac__read_and_decode_next_flac_frame(pFlac)) { | ||
| 5944 | /* Halve the byte location and continue. */ | ||
| 5945 | targetByte = rangeLo + ((rangeHi - rangeLo)/2); | ||
| 5946 | rangeHi = targetByte; | ||
| 5947 | } else { | ||
| 5948 | break; | ||
| 5949 | } | ||
| 5950 | #else | ||
| 5951 | if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { | ||
| 5952 | /* Halve the byte location and continue. */ | ||
| 5953 | targetByte = rangeLo + ((rangeHi - rangeLo)/2); | ||
| 5954 | rangeHi = targetByte; | ||
| 5955 | } else { | ||
| 5956 | break; | ||
| 5957 | } | ||
| 5958 | #endif | ||
| 5959 | } | ||
| 5960 | |||
| 5961 | /* We already tried this byte and there are no more to try, break out. */ | ||
| 5962 | if(targetByte == lastTargetByte) { | ||
| 5963 | return DRFLAC_FALSE; | ||
| 5964 | } | ||
| 5965 | } | ||
| 5966 | |||
| 5967 | /* The current PCM frame needs to be updated based on the frame we just seeked to. */ | ||
| 5968 | drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); | ||
| 5969 | |||
| 5970 | DRFLAC_ASSERT(targetByte <= rangeHi); | ||
| 5971 | |||
| 5972 | *pLastSuccessfulSeekOffset = targetByte; | ||
| 5973 | return DRFLAC_TRUE; | ||
| 5974 | } | ||
| 5975 | |||
| 5976 | static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset) | ||
| 5977 | { | ||
| 5978 | /* This section of code would be used if we were only decoding the FLAC frame header when calling drflac__seek_to_approximate_flac_frame_to_byte(). */ | ||
| 5979 | #if 0 | ||
| 5980 | if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) { | ||
| 5981 | /* We failed to decode this frame which may be due to it being corrupt. We'll just use the next valid FLAC frame. */ | ||
| 5982 | if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) { | ||
| 5983 | return DRFLAC_FALSE; | ||
| 5984 | } | ||
| 5985 | } | ||
| 5986 | #endif | ||
| 5987 | |||
| 5988 | return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset; | ||
| 5989 | } | ||
| 5990 | |||
| 5991 | |||
| 5992 | static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi) | ||
| 5993 | { | ||
| 5994 | /* This assumes pFlac->currentPCMFrame is sitting on byteRangeLo upon entry. */ | ||
| 5995 | |||
| 5996 | drflac_uint64 targetByte; | ||
| 5997 | drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; | ||
| 5998 | drflac_uint64 pcmRangeHi = 0; | ||
| 5999 | drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1; | ||
| 6000 | drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; | ||
| 6001 | drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; | ||
| 6002 | |||
| 6003 | targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); | ||
| 6004 | if (targetByte > byteRangeHi) { | ||
| 6005 | targetByte = byteRangeHi; | ||
| 6006 | } | ||
| 6007 | |||
| 6008 | for (;;) { | ||
| 6009 | if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { | ||
| 6010 | /* We found a FLAC frame. We need to check if it contains the sample we're looking for. */ | ||
| 6011 | drflac_uint64 newPCMRangeLo; | ||
| 6012 | drflac_uint64 newPCMRangeHi; | ||
| 6013 | drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); | ||
| 6014 | |||
| 6015 | /* If we selected the same frame, it means we should be pretty close. Just decode the rest. */ | ||
| 6016 | if (pcmRangeLo == newPCMRangeLo) { | ||
| 6017 | if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { | ||
| 6018 | break; /* Failed to seek to closest frame. */ | ||
| 6019 | } | ||
| 6020 | |||
| 6021 | if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { | ||
| 6022 | return DRFLAC_TRUE; | ||
| 6023 | } else { | ||
| 6024 | break; /* Failed to seek forward. */ | ||
| 6025 | } | ||
| 6026 | } | ||
| 6027 | |||
| 6028 | pcmRangeLo = newPCMRangeLo; | ||
| 6029 | pcmRangeHi = newPCMRangeHi; | ||
| 6030 | |||
| 6031 | if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { | ||
| 6032 | /* The target PCM frame is in this FLAC frame. */ | ||
| 6033 | if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { | ||
| 6034 | return DRFLAC_TRUE; | ||
| 6035 | } else { | ||
| 6036 | break; /* Failed to seek to FLAC frame. */ | ||
| 6037 | } | ||
| 6038 | } else { | ||
| 6039 | const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); | ||
| 6040 | |||
| 6041 | if (pcmRangeLo > pcmFrameIndex) { | ||
| 6042 | /* We seeked too far forward. We need to move our target byte backward and try again. */ | ||
| 6043 | byteRangeHi = lastSuccessfulSeekOffset; | ||
| 6044 | if (byteRangeLo > byteRangeHi) { | ||
| 6045 | byteRangeLo = byteRangeHi; | ||
| 6046 | } | ||
| 6047 | |||
| 6048 | targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); | ||
| 6049 | if (targetByte < byteRangeLo) { | ||
| 6050 | targetByte = byteRangeLo; | ||
| 6051 | } | ||
| 6052 | } else /*if (pcmRangeHi < pcmFrameIndex)*/ { | ||
| 6053 | /* We didn't seek far enough. We need to move our target byte forward and try again. */ | ||
| 6054 | |||
| 6055 | /* If we're close enough we can just seek forward. */ | ||
| 6056 | if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { | ||
| 6057 | if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { | ||
| 6058 | return DRFLAC_TRUE; | ||
| 6059 | } else { | ||
| 6060 | break; /* Failed to seek to FLAC frame. */ | ||
| 6061 | } | ||
| 6062 | } else { | ||
| 6063 | byteRangeLo = lastSuccessfulSeekOffset; | ||
| 6064 | if (byteRangeHi < byteRangeLo) { | ||
| 6065 | byteRangeHi = byteRangeLo; | ||
| 6066 | } | ||
| 6067 | |||
| 6068 | targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); | ||
| 6069 | if (targetByte > byteRangeHi) { | ||
| 6070 | targetByte = byteRangeHi; | ||
| 6071 | } | ||
| 6072 | |||
| 6073 | if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { | ||
| 6074 | closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; | ||
| 6075 | } | ||
| 6076 | } | ||
| 6077 | } | ||
| 6078 | } | ||
| 6079 | } else { | ||
| 6080 | /* Getting here is really bad. We just recover as best we can, but moving to the first frame in the stream, and then abort. */ | ||
| 6081 | break; | ||
| 6082 | } | ||
| 6083 | } | ||
| 6084 | |||
| 6085 | drflac__seek_to_first_frame(pFlac); /* <-- Try to recover. */ | ||
| 6086 | return DRFLAC_FALSE; | ||
| 6087 | } | ||
| 6088 | |||
| 6089 | static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex) | ||
| 6090 | { | ||
| 6091 | drflac_uint64 byteRangeLo; | ||
| 6092 | drflac_uint64 byteRangeHi; | ||
| 6093 | drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; | ||
| 6094 | |||
| 6095 | /* Our algorithm currently assumes the FLAC stream is currently sitting at the start. */ | ||
| 6096 | if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) { | ||
| 6097 | return DRFLAC_FALSE; | ||
| 6098 | } | ||
| 6099 | |||
| 6100 | /* If we're close enough to the start, just move to the start and seek forward. */ | ||
| 6101 | if (pcmFrameIndex < seekForwardThreshold) { | ||
| 6102 | return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; | ||
| 6103 | } | ||
| 6104 | |||
| 6105 | /* | ||
| 6106 | Our starting byte range is the byte position of the first FLAC frame and the approximate end of the file as if it were completely uncompressed. This ensures | ||
| 6107 | the entire file is included, even though most of the time it'll exceed the end of the actual stream. This is OK as the frame searching logic will handle it. | ||
| 6108 | */ | ||
| 6109 | byteRangeLo = pFlac->firstFLACFramePosInBytes; | ||
| 6110 | byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); | ||
| 6111 | |||
| 6112 | return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); | ||
| 6113 | } | ||
| 6114 | #endif /* !DR_FLAC_NO_CRC */ | ||
| 6115 | |||
| 6116 | static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex) | ||
| 6117 | { | ||
| 6118 | drflac_uint32 iClosestSeekpoint = 0; | ||
| 6119 | drflac_bool32 isMidFrame = DRFLAC_FALSE; | ||
| 6120 | drflac_uint64 runningPCMFrameCount; | ||
| 6121 | drflac_uint32 iSeekpoint; | ||
| 6122 | |||
| 6123 | |||
| 6124 | DRFLAC_ASSERT(pFlac != NULL); | ||
| 6125 | |||
| 6126 | if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { | ||
| 6127 | return DRFLAC_FALSE; | ||
| 6128 | } | ||
| 6129 | |||
| 6130 | /* Do not use the seektable if pcmFramIndex is not coverd by it. */ | ||
| 6131 | if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { | ||
| 6132 | return DRFLAC_FALSE; | ||
| 6133 | } | ||
| 6134 | |||
| 6135 | for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { | ||
| 6136 | if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { | ||
| 6137 | break; | ||
| 6138 | } | ||
| 6139 | |||
| 6140 | iClosestSeekpoint = iSeekpoint; | ||
| 6141 | } | ||
| 6142 | |||
| 6143 | /* There's been cases where the seek table contains only zeros. We need to do some basic validation on the closest seekpoint. */ | ||
| 6144 | if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { | ||
| 6145 | return DRFLAC_FALSE; | ||
| 6146 | } | ||
| 6147 | if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { | ||
| 6148 | return DRFLAC_FALSE; | ||
| 6149 | } | ||
| 6150 | |||
| 6151 | #if !defined(DR_FLAC_NO_CRC) | ||
| 6152 | /* At this point we should know the closest seek point. We can use a binary search for this. We need to know the total sample count for this. */ | ||
| 6153 | if (pFlac->totalPCMFrameCount > 0) { | ||
| 6154 | drflac_uint64 byteRangeLo; | ||
| 6155 | drflac_uint64 byteRangeHi; | ||
| 6156 | |||
| 6157 | byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); | ||
| 6158 | byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; | ||
| 6159 | |||
| 6160 | /* | ||
| 6161 | If our closest seek point is not the last one, we only need to search between it and the next one. The section below calculates an appropriate starting | ||
| 6162 | value for byteRangeHi which will clamp it appropriately. | ||
| 6163 | |||
| 6164 | Note that the next seekpoint must have an offset greater than the closest seekpoint because otherwise our binary search algorithm will break down. There | ||
| 6165 | have been cases where a seektable consists of seek points where every byte offset is set to 0 which causes problems. If this happens we need to abort. | ||
| 6166 | */ | ||
| 6167 | if (iClosestSeekpoint < pFlac->seekpointCount-1) { | ||
| 6168 | drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1; | ||
| 6169 | |||
| 6170 | /* Basic validation on the seekpoints to ensure they're usable. */ | ||
| 6171 | if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { | ||
| 6172 | return DRFLAC_FALSE; /* The next seekpoint doesn't look right. The seek table cannot be trusted from here. Abort. */ | ||
| 6173 | } | ||
| 6174 | |||
| 6175 | if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { /* Make sure it's not a placeholder seekpoint. */ | ||
| 6176 | byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; /* byteRangeHi must be zero based. */ | ||
| 6177 | } | ||
| 6178 | } | ||
| 6179 | |||
| 6180 | if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { | ||
| 6181 | if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { | ||
| 6182 | drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); | ||
| 6183 | |||
| 6184 | if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { | ||
| 6185 | return DRFLAC_TRUE; | ||
| 6186 | } | ||
| 6187 | } | ||
| 6188 | } | ||
| 6189 | } | ||
| 6190 | #endif /* !DR_FLAC_NO_CRC */ | ||
| 6191 | |||
| 6192 | /* Getting here means we need to use a slower algorithm because the binary search method failed or cannot be used. */ | ||
| 6193 | |||
| 6194 | /* | ||
| 6195 | If we are seeking forward and the closest seekpoint is _before_ the current sample, we just seek forward from where we are. Otherwise we start seeking | ||
| 6196 | from the seekpoint's first sample. | ||
| 6197 | */ | ||
| 6198 | if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { | ||
| 6199 | /* Optimized case. Just seek forward from where we are. */ | ||
| 6200 | runningPCMFrameCount = pFlac->currentPCMFrame; | ||
| 6201 | |||
| 6202 | /* The frame header for the first frame may not yet have been read. We need to do that if necessary. */ | ||
| 6203 | if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { | ||
| 6204 | if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { | ||
| 6205 | return DRFLAC_FALSE; | ||
| 6206 | } | ||
| 6207 | } else { | ||
| 6208 | isMidFrame = DRFLAC_TRUE; | ||
| 6209 | } | ||
| 6210 | } else { | ||
| 6211 | /* Slower case. Seek to the start of the seekpoint and then seek forward from there. */ | ||
| 6212 | runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; | ||
| 6213 | |||
| 6214 | if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { | ||
| 6215 | return DRFLAC_FALSE; | ||
| 6216 | } | ||
| 6217 | |||
| 6218 | /* Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below. */ | ||
| 6219 | if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { | ||
| 6220 | return DRFLAC_FALSE; | ||
| 6221 | } | ||
| 6222 | } | ||
| 6223 | |||
| 6224 | for (;;) { | ||
| 6225 | drflac_uint64 pcmFrameCountInThisFLACFrame; | ||
| 6226 | drflac_uint64 firstPCMFrameInFLACFrame = 0; | ||
| 6227 | drflac_uint64 lastPCMFrameInFLACFrame = 0; | ||
| 6228 | |||
| 6229 | drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); | ||
| 6230 | |||
| 6231 | pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; | ||
| 6232 | if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { | ||
| 6233 | /* | ||
| 6234 | The sample should be in this frame. We need to fully decode it, but if it's an invalid frame (a CRC mismatch) we need to pretend | ||
| 6235 | it never existed and keep iterating. | ||
| 6236 | */ | ||
| 6237 | drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; | ||
| 6238 | |||
| 6239 | if (!isMidFrame) { | ||
| 6240 | drflac_result result = drflac__decode_flac_frame(pFlac); | ||
| 6241 | if (result == DRFLAC_SUCCESS) { | ||
| 6242 | /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ | ||
| 6243 | return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */ | ||
| 6244 | } else { | ||
| 6245 | if (result == DRFLAC_CRC_MISMATCH) { | ||
| 6246 | goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ | ||
| 6247 | } else { | ||
| 6248 | return DRFLAC_FALSE; | ||
| 6249 | } | ||
| 6250 | } | ||
| 6251 | } else { | ||
| 6252 | /* We started seeking mid-frame which means we need to skip the frame decoding part. */ | ||
| 6253 | return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; | ||
| 6254 | } | ||
| 6255 | } else { | ||
| 6256 | /* | ||
| 6257 | It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this | ||
| 6258 | frame never existed and leave the running sample count untouched. | ||
| 6259 | */ | ||
| 6260 | if (!isMidFrame) { | ||
| 6261 | drflac_result result = drflac__seek_to_next_flac_frame(pFlac); | ||
| 6262 | if (result == DRFLAC_SUCCESS) { | ||
| 6263 | runningPCMFrameCount += pcmFrameCountInThisFLACFrame; | ||
| 6264 | } else { | ||
| 6265 | if (result == DRFLAC_CRC_MISMATCH) { | ||
| 6266 | goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ | ||
| 6267 | } else { | ||
| 6268 | return DRFLAC_FALSE; | ||
| 6269 | } | ||
| 6270 | } | ||
| 6271 | } else { | ||
| 6272 | /* | ||
| 6273 | We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with | ||
| 6274 | drflac__seek_to_next_flac_frame() which only works if the decoder is sitting on the byte just after the frame header. | ||
| 6275 | */ | ||
| 6276 | runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; | ||
| 6277 | pFlac->currentFLACFrame.pcmFramesRemaining = 0; | ||
| 6278 | isMidFrame = DRFLAC_FALSE; | ||
| 6279 | } | ||
| 6280 | |||
| 6281 | /* If we are seeking to the end of the file and we've just hit it, we're done. */ | ||
| 6282 | if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { | ||
| 6283 | return DRFLAC_TRUE; | ||
| 6284 | } | ||
| 6285 | } | ||
| 6286 | |||
| 6287 | next_iteration: | ||
| 6288 | /* Grab the next frame in preparation for the next iteration. */ | ||
| 6289 | if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { | ||
| 6290 | return DRFLAC_FALSE; | ||
| 6291 | } | ||
| 6292 | } | ||
| 6293 | } | ||
| 6294 | |||
| 6295 | |||
| 6296 | #ifndef DR_FLAC_NO_OGG | ||
| 6297 | typedef struct | ||
| 6298 | { | ||
| 6299 | drflac_uint8 capturePattern[4]; /* Should be "OggS" */ | ||
| 6300 | drflac_uint8 structureVersion; /* Always 0. */ | ||
| 6301 | drflac_uint8 headerType; | ||
| 6302 | drflac_uint64 granulePosition; | ||
| 6303 | drflac_uint32 serialNumber; | ||
| 6304 | drflac_uint32 sequenceNumber; | ||
| 6305 | drflac_uint32 checksum; | ||
| 6306 | drflac_uint8 segmentCount; | ||
| 6307 | drflac_uint8 segmentTable[255]; | ||
| 6308 | } drflac_ogg_page_header; | ||
| 6309 | #endif | ||
| 6310 | |||
| 6311 | typedef struct | ||
| 6312 | { | ||
| 6313 | drflac_read_proc onRead; | ||
| 6314 | drflac_seek_proc onSeek; | ||
| 6315 | drflac_meta_proc onMeta; | ||
| 6316 | drflac_container container; | ||
| 6317 | void* pUserData; | ||
| 6318 | void* pUserDataMD; | ||
| 6319 | drflac_uint32 sampleRate; | ||
| 6320 | drflac_uint8 channels; | ||
| 6321 | drflac_uint8 bitsPerSample; | ||
| 6322 | drflac_uint64 totalPCMFrameCount; | ||
| 6323 | drflac_uint16 maxBlockSizeInPCMFrames; | ||
| 6324 | drflac_uint64 runningFilePos; | ||
| 6325 | drflac_bool32 hasStreamInfoBlock; | ||
| 6326 | drflac_bool32 hasMetadataBlocks; | ||
| 6327 | drflac_bs bs; /* <-- A bit streamer is required for loading data during initialization. */ | ||
| 6328 | drflac_frame_header firstFrameHeader; /* <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block. */ | ||
| 6329 | |||
| 6330 | #ifndef DR_FLAC_NO_OGG | ||
| 6331 | drflac_uint32 oggSerial; | ||
| 6332 | drflac_uint64 oggFirstBytePos; | ||
| 6333 | drflac_ogg_page_header oggBosHeader; | ||
| 6334 | #endif | ||
| 6335 | } drflac_init_info; | ||
| 6336 | |||
| 6337 | static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) | ||
| 6338 | { | ||
| 6339 | blockHeader = drflac__be2host_32(blockHeader); | ||
| 6340 | *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31); | ||
| 6341 | *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24); | ||
| 6342 | *blockSize = (blockHeader & 0x00FFFFFFUL); | ||
| 6343 | } | ||
| 6344 | |||
| 6345 | static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) | ||
| 6346 | { | ||
| 6347 | drflac_uint32 blockHeader; | ||
| 6348 | |||
| 6349 | *blockSize = 0; | ||
| 6350 | if (onRead(pUserData, &blockHeader, 4) != 4) { | ||
| 6351 | return DRFLAC_FALSE; | ||
| 6352 | } | ||
| 6353 | |||
| 6354 | drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); | ||
| 6355 | return DRFLAC_TRUE; | ||
| 6356 | } | ||
| 6357 | |||
| 6358 | static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) | ||
| 6359 | { | ||
| 6360 | drflac_uint32 blockSizes; | ||
| 6361 | drflac_uint64 frameSizes = 0; | ||
| 6362 | drflac_uint64 importantProps; | ||
| 6363 | drflac_uint8 md5[16]; | ||
| 6364 | |||
| 6365 | /* min/max block size. */ | ||
| 6366 | if (onRead(pUserData, &blockSizes, 4) != 4) { | ||
| 6367 | return DRFLAC_FALSE; | ||
| 6368 | } | ||
| 6369 | |||
| 6370 | /* min/max frame size. */ | ||
| 6371 | if (onRead(pUserData, &frameSizes, 6) != 6) { | ||
| 6372 | return DRFLAC_FALSE; | ||
| 6373 | } | ||
| 6374 | |||
| 6375 | /* Sample rate, channels, bits per sample and total sample count. */ | ||
| 6376 | if (onRead(pUserData, &importantProps, 8) != 8) { | ||
| 6377 | return DRFLAC_FALSE; | ||
| 6378 | } | ||
| 6379 | |||
| 6380 | /* MD5 */ | ||
| 6381 | if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { | ||
| 6382 | return DRFLAC_FALSE; | ||
| 6383 | } | ||
| 6384 | |||
| 6385 | blockSizes = drflac__be2host_32(blockSizes); | ||
| 6386 | frameSizes = drflac__be2host_64(frameSizes); | ||
| 6387 | importantProps = drflac__be2host_64(importantProps); | ||
| 6388 | |||
| 6389 | pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16); | ||
| 6390 | pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF); | ||
| 6391 | pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40); | ||
| 6392 | pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16); | ||
| 6393 | pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44); | ||
| 6394 | pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1; | ||
| 6395 | pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1; | ||
| 6396 | pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); | ||
| 6397 | DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); | ||
| 6398 | |||
| 6399 | return DRFLAC_TRUE; | ||
| 6400 | } | ||
| 6401 | |||
| 6402 | |||
| 6403 | static void* drflac__malloc_default(size_t sz, void* pUserData) | ||
| 6404 | { | ||
| 6405 | (void)pUserData; | ||
| 6406 | return DRFLAC_MALLOC(sz); | ||
| 6407 | } | ||
| 6408 | |||
| 6409 | static void* drflac__realloc_default(void* p, size_t sz, void* pUserData) | ||
| 6410 | { | ||
| 6411 | (void)pUserData; | ||
| 6412 | return DRFLAC_REALLOC(p, sz); | ||
| 6413 | } | ||
| 6414 | |||
| 6415 | static void drflac__free_default(void* p, void* pUserData) | ||
| 6416 | { | ||
| 6417 | (void)pUserData; | ||
| 6418 | DRFLAC_FREE(p); | ||
| 6419 | } | ||
| 6420 | |||
| 6421 | |||
| 6422 | static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 6423 | { | ||
| 6424 | if (pAllocationCallbacks == NULL) { | ||
| 6425 | return NULL; | ||
| 6426 | } | ||
| 6427 | |||
| 6428 | if (pAllocationCallbacks->onMalloc != NULL) { | ||
| 6429 | return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); | ||
| 6430 | } | ||
| 6431 | |||
| 6432 | /* Try using realloc(). */ | ||
| 6433 | if (pAllocationCallbacks->onRealloc != NULL) { | ||
| 6434 | return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); | ||
| 6435 | } | ||
| 6436 | |||
| 6437 | return NULL; | ||
| 6438 | } | ||
| 6439 | |||
| 6440 | static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 6441 | { | ||
| 6442 | if (pAllocationCallbacks == NULL) { | ||
| 6443 | return NULL; | ||
| 6444 | } | ||
| 6445 | |||
| 6446 | if (pAllocationCallbacks->onRealloc != NULL) { | ||
| 6447 | return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); | ||
| 6448 | } | ||
| 6449 | |||
| 6450 | /* Try emulating realloc() in terms of malloc()/free(). */ | ||
| 6451 | if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { | ||
| 6452 | void* p2; | ||
| 6453 | |||
| 6454 | p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); | ||
| 6455 | if (p2 == NULL) { | ||
| 6456 | return NULL; | ||
| 6457 | } | ||
| 6458 | |||
| 6459 | if (p != NULL) { | ||
| 6460 | DRFLAC_COPY_MEMORY(p2, p, szOld); | ||
| 6461 | pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); | ||
| 6462 | } | ||
| 6463 | |||
| 6464 | return p2; | ||
| 6465 | } | ||
| 6466 | |||
| 6467 | return NULL; | ||
| 6468 | } | ||
| 6469 | |||
| 6470 | static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 6471 | { | ||
| 6472 | if (p == NULL || pAllocationCallbacks == NULL) { | ||
| 6473 | return; | ||
| 6474 | } | ||
| 6475 | |||
| 6476 | if (pAllocationCallbacks->onFree != NULL) { | ||
| 6477 | pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); | ||
| 6478 | } | ||
| 6479 | } | ||
| 6480 | |||
| 6481 | |||
| 6482 | static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 6483 | { | ||
| 6484 | /* | ||
| 6485 | We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that | ||
| 6486 | we'll be sitting on byte 42. | ||
| 6487 | */ | ||
| 6488 | drflac_uint64 runningFilePos = 42; | ||
| 6489 | drflac_uint64 seektablePos = 0; | ||
| 6490 | drflac_uint32 seektableSize = 0; | ||
| 6491 | |||
| 6492 | for (;;) { | ||
| 6493 | drflac_metadata metadata; | ||
| 6494 | drflac_uint8 isLastBlock = 0; | ||
| 6495 | drflac_uint8 blockType = 0; | ||
| 6496 | drflac_uint32 blockSize; | ||
| 6497 | if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) { | ||
| 6498 | return DRFLAC_FALSE; | ||
| 6499 | } | ||
| 6500 | runningFilePos += 4; | ||
| 6501 | |||
| 6502 | metadata.type = blockType; | ||
| 6503 | metadata.pRawData = NULL; | ||
| 6504 | metadata.rawDataSize = 0; | ||
| 6505 | |||
| 6506 | switch (blockType) | ||
| 6507 | { | ||
| 6508 | case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: | ||
| 6509 | { | ||
| 6510 | if (blockSize < 4) { | ||
| 6511 | return DRFLAC_FALSE; | ||
| 6512 | } | ||
| 6513 | |||
| 6514 | if (onMeta) { | ||
| 6515 | void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); | ||
| 6516 | if (pRawData == NULL) { | ||
| 6517 | return DRFLAC_FALSE; | ||
| 6518 | } | ||
| 6519 | |||
| 6520 | if (onRead(pUserData, pRawData, blockSize) != blockSize) { | ||
| 6521 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6522 | return DRFLAC_FALSE; | ||
| 6523 | } | ||
| 6524 | |||
| 6525 | metadata.pRawData = pRawData; | ||
| 6526 | metadata.rawDataSize = blockSize; | ||
| 6527 | metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); | ||
| 6528 | metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); | ||
| 6529 | metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); | ||
| 6530 | onMeta(pUserDataMD, &metadata); | ||
| 6531 | |||
| 6532 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6533 | } | ||
| 6534 | } break; | ||
| 6535 | |||
| 6536 | case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: | ||
| 6537 | { | ||
| 6538 | seektablePos = runningFilePos; | ||
| 6539 | seektableSize = blockSize; | ||
| 6540 | |||
| 6541 | if (onMeta) { | ||
| 6542 | drflac_uint32 seekpointCount; | ||
| 6543 | drflac_uint32 iSeekpoint; | ||
| 6544 | void* pRawData; | ||
| 6545 | |||
| 6546 | seekpointCount = blockSize/DRFLAC_SEEKPOINT_SIZE_IN_BYTES; | ||
| 6547 | |||
| 6548 | pRawData = drflac__malloc_from_callbacks(seekpointCount * sizeof(drflac_seekpoint), pAllocationCallbacks); | ||
| 6549 | if (pRawData == NULL) { | ||
| 6550 | return DRFLAC_FALSE; | ||
| 6551 | } | ||
| 6552 | |||
| 6553 | /* We need to read seekpoint by seekpoint and do some processing. */ | ||
| 6554 | for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { | ||
| 6555 | drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; | ||
| 6556 | |||
| 6557 | if (onRead(pUserData, pSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) != DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { | ||
| 6558 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6559 | return DRFLAC_FALSE; | ||
| 6560 | } | ||
| 6561 | |||
| 6562 | /* Endian swap. */ | ||
| 6563 | pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame); | ||
| 6564 | pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset); | ||
| 6565 | pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount); | ||
| 6566 | } | ||
| 6567 | |||
| 6568 | metadata.pRawData = pRawData; | ||
| 6569 | metadata.rawDataSize = blockSize; | ||
| 6570 | metadata.data.seektable.seekpointCount = seekpointCount; | ||
| 6571 | metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; | ||
| 6572 | |||
| 6573 | onMeta(pUserDataMD, &metadata); | ||
| 6574 | |||
| 6575 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6576 | } | ||
| 6577 | } break; | ||
| 6578 | |||
| 6579 | case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: | ||
| 6580 | { | ||
| 6581 | if (blockSize < 8) { | ||
| 6582 | return DRFLAC_FALSE; | ||
| 6583 | } | ||
| 6584 | |||
| 6585 | if (onMeta) { | ||
| 6586 | void* pRawData; | ||
| 6587 | const char* pRunningData; | ||
| 6588 | const char* pRunningDataEnd; | ||
| 6589 | drflac_uint32 i; | ||
| 6590 | |||
| 6591 | pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); | ||
| 6592 | if (pRawData == NULL) { | ||
| 6593 | return DRFLAC_FALSE; | ||
| 6594 | } | ||
| 6595 | |||
| 6596 | if (onRead(pUserData, pRawData, blockSize) != blockSize) { | ||
| 6597 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6598 | return DRFLAC_FALSE; | ||
| 6599 | } | ||
| 6600 | |||
| 6601 | metadata.pRawData = pRawData; | ||
| 6602 | metadata.rawDataSize = blockSize; | ||
| 6603 | |||
| 6604 | pRunningData = (const char*)pRawData; | ||
| 6605 | pRunningDataEnd = (const char*)pRawData + blockSize; | ||
| 6606 | |||
| 6607 | metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; | ||
| 6608 | |||
| 6609 | /* Need space for the rest of the block */ | ||
| 6610 | if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ | ||
| 6611 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6612 | return DRFLAC_FALSE; | ||
| 6613 | } | ||
| 6614 | metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; | ||
| 6615 | metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; | ||
| 6616 | |||
| 6617 | /* Need space for 'commentCount' comments after the block, which at minimum is a drflac_uint32 per comment */ | ||
| 6618 | if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { /* <-- Note the order of operations to avoid overflow to a valid value */ | ||
| 6619 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6620 | return DRFLAC_FALSE; | ||
| 6621 | } | ||
| 6622 | metadata.data.vorbis_comment.pComments = pRunningData; | ||
| 6623 | |||
| 6624 | /* Check that the comments section is valid before passing it to the callback */ | ||
| 6625 | for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { | ||
| 6626 | drflac_uint32 commentLength; | ||
| 6627 | |||
| 6628 | if (pRunningDataEnd - pRunningData < 4) { | ||
| 6629 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6630 | return DRFLAC_FALSE; | ||
| 6631 | } | ||
| 6632 | |||
| 6633 | commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; | ||
| 6634 | if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ | ||
| 6635 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6636 | return DRFLAC_FALSE; | ||
| 6637 | } | ||
| 6638 | pRunningData += commentLength; | ||
| 6639 | } | ||
| 6640 | |||
| 6641 | onMeta(pUserDataMD, &metadata); | ||
| 6642 | |||
| 6643 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6644 | } | ||
| 6645 | } break; | ||
| 6646 | |||
| 6647 | case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: | ||
| 6648 | { | ||
| 6649 | if (blockSize < 396) { | ||
| 6650 | return DRFLAC_FALSE; | ||
| 6651 | } | ||
| 6652 | |||
| 6653 | if (onMeta) { | ||
| 6654 | void* pRawData; | ||
| 6655 | const char* pRunningData; | ||
| 6656 | const char* pRunningDataEnd; | ||
| 6657 | size_t bufferSize; | ||
| 6658 | drflac_uint8 iTrack; | ||
| 6659 | drflac_uint8 iIndex; | ||
| 6660 | void* pTrackData; | ||
| 6661 | |||
| 6662 | /* | ||
| 6663 | This needs to be loaded in two passes. The first pass is used to calculate the size of the memory allocation | ||
| 6664 | we need for storing the necessary data. The second pass will fill that buffer with usable data. | ||
| 6665 | */ | ||
| 6666 | pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); | ||
| 6667 | if (pRawData == NULL) { | ||
| 6668 | return DRFLAC_FALSE; | ||
| 6669 | } | ||
| 6670 | |||
| 6671 | if (onRead(pUserData, pRawData, blockSize) != blockSize) { | ||
| 6672 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6673 | return DRFLAC_FALSE; | ||
| 6674 | } | ||
| 6675 | |||
| 6676 | metadata.pRawData = pRawData; | ||
| 6677 | metadata.rawDataSize = blockSize; | ||
| 6678 | |||
| 6679 | pRunningData = (const char*)pRawData; | ||
| 6680 | pRunningDataEnd = (const char*)pRawData + blockSize; | ||
| 6681 | |||
| 6682 | DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; | ||
| 6683 | metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8; | ||
| 6684 | metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; | ||
| 6685 | metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; | ||
| 6686 | metadata.data.cuesheet.pTrackData = NULL; /* Will be filled later. */ | ||
| 6687 | |||
| 6688 | /* Pass 1: Calculate the size of the buffer for the track data. */ | ||
| 6689 | { | ||
| 6690 | const char* pRunningDataSaved = pRunningData; /* Will be restored at the end in preparation for the second pass. */ | ||
| 6691 | |||
| 6692 | bufferSize = metadata.data.cuesheet.trackCount * DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES; | ||
| 6693 | |||
| 6694 | for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { | ||
| 6695 | drflac_uint8 indexCount; | ||
| 6696 | drflac_uint32 indexPointSize; | ||
| 6697 | |||
| 6698 | if (pRunningDataEnd - pRunningData < DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { | ||
| 6699 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6700 | return DRFLAC_FALSE; | ||
| 6701 | } | ||
| 6702 | |||
| 6703 | /* Skip to the index point count */ | ||
| 6704 | pRunningData += 35; | ||
| 6705 | |||
| 6706 | indexCount = pRunningData[0]; | ||
| 6707 | pRunningData += 1; | ||
| 6708 | |||
| 6709 | bufferSize += indexCount * sizeof(drflac_cuesheet_track_index); | ||
| 6710 | |||
| 6711 | /* Quick validation check. */ | ||
| 6712 | indexPointSize = indexCount * DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; | ||
| 6713 | if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { | ||
| 6714 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6715 | return DRFLAC_FALSE; | ||
| 6716 | } | ||
| 6717 | |||
| 6718 | pRunningData += indexPointSize; | ||
| 6719 | } | ||
| 6720 | |||
| 6721 | pRunningData = pRunningDataSaved; | ||
| 6722 | } | ||
| 6723 | |||
| 6724 | /* Pass 2: Allocate a buffer and fill the data. Validation was done in the step above so can be skipped. */ | ||
| 6725 | { | ||
| 6726 | char* pRunningTrackData; | ||
| 6727 | |||
| 6728 | pTrackData = drflac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); | ||
| 6729 | if (pTrackData == NULL) { | ||
| 6730 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6731 | return DRFLAC_FALSE; | ||
| 6732 | } | ||
| 6733 | |||
| 6734 | pRunningTrackData = (char*)pTrackData; | ||
| 6735 | |||
| 6736 | for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { | ||
| 6737 | drflac_uint8 indexCount; | ||
| 6738 | |||
| 6739 | DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES); | ||
| 6740 | pRunningData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; /* Skip forward, but not beyond the last byte in the CUESHEET_TRACK block which is the index count. */ | ||
| 6741 | pRunningTrackData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; | ||
| 6742 | |||
| 6743 | /* Grab the index count for the next part. */ | ||
| 6744 | indexCount = pRunningData[0]; | ||
| 6745 | pRunningData += 1; | ||
| 6746 | pRunningTrackData += 1; | ||
| 6747 | |||
| 6748 | /* Extract each track index. */ | ||
| 6749 | for (iIndex = 0; iIndex < indexCount; ++iIndex) { | ||
| 6750 | drflac_cuesheet_track_index* pTrackIndex = (drflac_cuesheet_track_index*)pRunningTrackData; | ||
| 6751 | |||
| 6752 | DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); | ||
| 6753 | pRunningData += DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; | ||
| 6754 | pRunningTrackData += sizeof(drflac_cuesheet_track_index); | ||
| 6755 | |||
| 6756 | pTrackIndex->offset = drflac__be2host_64(pTrackIndex->offset); | ||
| 6757 | } | ||
| 6758 | } | ||
| 6759 | |||
| 6760 | metadata.data.cuesheet.pTrackData = pTrackData; | ||
| 6761 | } | ||
| 6762 | |||
| 6763 | /* The original data is no longer needed. */ | ||
| 6764 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6765 | pRawData = NULL; | ||
| 6766 | |||
| 6767 | onMeta(pUserDataMD, &metadata); | ||
| 6768 | |||
| 6769 | drflac__free_from_callbacks(pTrackData, pAllocationCallbacks); | ||
| 6770 | pTrackData = NULL; | ||
| 6771 | } | ||
| 6772 | } break; | ||
| 6773 | |||
| 6774 | case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: | ||
| 6775 | { | ||
| 6776 | if (blockSize < 32) { | ||
| 6777 | return DRFLAC_FALSE; | ||
| 6778 | } | ||
| 6779 | |||
| 6780 | if (onMeta) { | ||
| 6781 | void* pRawData; | ||
| 6782 | const char* pRunningData; | ||
| 6783 | const char* pRunningDataEnd; | ||
| 6784 | |||
| 6785 | pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); | ||
| 6786 | if (pRawData == NULL) { | ||
| 6787 | return DRFLAC_FALSE; | ||
| 6788 | } | ||
| 6789 | |||
| 6790 | if (onRead(pUserData, pRawData, blockSize) != blockSize) { | ||
| 6791 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6792 | return DRFLAC_FALSE; | ||
| 6793 | } | ||
| 6794 | |||
| 6795 | metadata.pRawData = pRawData; | ||
| 6796 | metadata.rawDataSize = blockSize; | ||
| 6797 | |||
| 6798 | pRunningData = (const char*)pRawData; | ||
| 6799 | pRunningDataEnd = (const char*)pRawData + blockSize; | ||
| 6800 | |||
| 6801 | metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; | ||
| 6802 | metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; | ||
| 6803 | |||
| 6804 | /* Need space for the rest of the block */ | ||
| 6805 | if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ | ||
| 6806 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6807 | return DRFLAC_FALSE; | ||
| 6808 | } | ||
| 6809 | metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; | ||
| 6810 | metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; | ||
| 6811 | |||
| 6812 | /* Need space for the rest of the block */ | ||
| 6813 | if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ | ||
| 6814 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6815 | return DRFLAC_FALSE; | ||
| 6816 | } | ||
| 6817 | metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; | ||
| 6818 | metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; | ||
| 6819 | metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; | ||
| 6820 | metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; | ||
| 6821 | metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; | ||
| 6822 | metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; | ||
| 6823 | metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; | ||
| 6824 | |||
| 6825 | /* Need space for the picture after the block */ | ||
| 6826 | if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { /* <-- Note the order of operations to avoid overflow to a valid value */ | ||
| 6827 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6828 | return DRFLAC_FALSE; | ||
| 6829 | } | ||
| 6830 | |||
| 6831 | onMeta(pUserDataMD, &metadata); | ||
| 6832 | |||
| 6833 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6834 | } | ||
| 6835 | } break; | ||
| 6836 | |||
| 6837 | case DRFLAC_METADATA_BLOCK_TYPE_PADDING: | ||
| 6838 | { | ||
| 6839 | if (onMeta) { | ||
| 6840 | metadata.data.padding.unused = 0; | ||
| 6841 | |||
| 6842 | /* Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. */ | ||
| 6843 | if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { | ||
| 6844 | isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */ | ||
| 6845 | } else { | ||
| 6846 | onMeta(pUserDataMD, &metadata); | ||
| 6847 | } | ||
| 6848 | } | ||
| 6849 | } break; | ||
| 6850 | |||
| 6851 | case DRFLAC_METADATA_BLOCK_TYPE_INVALID: | ||
| 6852 | { | ||
| 6853 | /* Invalid chunk. Just skip over this one. */ | ||
| 6854 | if (onMeta) { | ||
| 6855 | if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { | ||
| 6856 | isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */ | ||
| 6857 | } | ||
| 6858 | } | ||
| 6859 | } break; | ||
| 6860 | |||
| 6861 | default: | ||
| 6862 | { | ||
| 6863 | /* | ||
| 6864 | It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we | ||
| 6865 | can at the very least report the chunk to the application and let it look at the raw data. | ||
| 6866 | */ | ||
| 6867 | if (onMeta) { | ||
| 6868 | void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); | ||
| 6869 | if (pRawData == NULL) { | ||
| 6870 | return DRFLAC_FALSE; | ||
| 6871 | } | ||
| 6872 | |||
| 6873 | if (onRead(pUserData, pRawData, blockSize) != blockSize) { | ||
| 6874 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6875 | return DRFLAC_FALSE; | ||
| 6876 | } | ||
| 6877 | |||
| 6878 | metadata.pRawData = pRawData; | ||
| 6879 | metadata.rawDataSize = blockSize; | ||
| 6880 | onMeta(pUserDataMD, &metadata); | ||
| 6881 | |||
| 6882 | drflac__free_from_callbacks(pRawData, pAllocationCallbacks); | ||
| 6883 | } | ||
| 6884 | } break; | ||
| 6885 | } | ||
| 6886 | |||
| 6887 | /* If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. */ | ||
| 6888 | if (onMeta == NULL && blockSize > 0) { | ||
| 6889 | if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { | ||
| 6890 | isLastBlock = DRFLAC_TRUE; | ||
| 6891 | } | ||
| 6892 | } | ||
| 6893 | |||
| 6894 | runningFilePos += blockSize; | ||
| 6895 | if (isLastBlock) { | ||
| 6896 | break; | ||
| 6897 | } | ||
| 6898 | } | ||
| 6899 | |||
| 6900 | *pSeektablePos = seektablePos; | ||
| 6901 | *pSeekpointCount = seektableSize / DRFLAC_SEEKPOINT_SIZE_IN_BYTES; | ||
| 6902 | *pFirstFramePos = runningFilePos; | ||
| 6903 | |||
| 6904 | return DRFLAC_TRUE; | ||
| 6905 | } | ||
| 6906 | |||
| 6907 | static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) | ||
| 6908 | { | ||
| 6909 | /* Pre Condition: The bit stream should be sitting just past the 4-byte id header. */ | ||
| 6910 | |||
| 6911 | drflac_uint8 isLastBlock; | ||
| 6912 | drflac_uint8 blockType; | ||
| 6913 | drflac_uint32 blockSize; | ||
| 6914 | |||
| 6915 | (void)onSeek; | ||
| 6916 | |||
| 6917 | pInit->container = drflac_container_native; | ||
| 6918 | |||
| 6919 | /* The first metadata block should be the STREAMINFO block. */ | ||
| 6920 | if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { | ||
| 6921 | return DRFLAC_FALSE; | ||
| 6922 | } | ||
| 6923 | |||
| 6924 | if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { | ||
| 6925 | if (!relaxed) { | ||
| 6926 | /* We're opening in strict mode and the first block is not the STREAMINFO block. Error. */ | ||
| 6927 | return DRFLAC_FALSE; | ||
| 6928 | } else { | ||
| 6929 | /* | ||
| 6930 | Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined | ||
| 6931 | for that frame. | ||
| 6932 | */ | ||
| 6933 | pInit->hasStreamInfoBlock = DRFLAC_FALSE; | ||
| 6934 | pInit->hasMetadataBlocks = DRFLAC_FALSE; | ||
| 6935 | |||
| 6936 | if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { | ||
| 6937 | return DRFLAC_FALSE; /* Couldn't find a frame. */ | ||
| 6938 | } | ||
| 6939 | |||
| 6940 | if (pInit->firstFrameHeader.bitsPerSample == 0) { | ||
| 6941 | return DRFLAC_FALSE; /* Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist. */ | ||
| 6942 | } | ||
| 6943 | |||
| 6944 | pInit->sampleRate = pInit->firstFrameHeader.sampleRate; | ||
| 6945 | pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); | ||
| 6946 | pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; | ||
| 6947 | pInit->maxBlockSizeInPCMFrames = 65535; /* <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo */ | ||
| 6948 | return DRFLAC_TRUE; | ||
| 6949 | } | ||
| 6950 | } else { | ||
| 6951 | drflac_streaminfo streaminfo; | ||
| 6952 | if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { | ||
| 6953 | return DRFLAC_FALSE; | ||
| 6954 | } | ||
| 6955 | |||
| 6956 | pInit->hasStreamInfoBlock = DRFLAC_TRUE; | ||
| 6957 | pInit->sampleRate = streaminfo.sampleRate; | ||
| 6958 | pInit->channels = streaminfo.channels; | ||
| 6959 | pInit->bitsPerSample = streaminfo.bitsPerSample; | ||
| 6960 | pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; | ||
| 6961 | pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; /* Don't care about the min block size - only the max (used for determining the size of the memory allocation). */ | ||
| 6962 | pInit->hasMetadataBlocks = !isLastBlock; | ||
| 6963 | |||
| 6964 | if (onMeta) { | ||
| 6965 | drflac_metadata metadata; | ||
| 6966 | metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; | ||
| 6967 | metadata.pRawData = NULL; | ||
| 6968 | metadata.rawDataSize = 0; | ||
| 6969 | metadata.data.streaminfo = streaminfo; | ||
| 6970 | onMeta(pUserDataMD, &metadata); | ||
| 6971 | } | ||
| 6972 | |||
| 6973 | return DRFLAC_TRUE; | ||
| 6974 | } | ||
| 6975 | } | ||
| 6976 | |||
| 6977 | #ifndef DR_FLAC_NO_OGG | ||
| 6978 | #define DRFLAC_OGG_MAX_PAGE_SIZE 65307 | ||
| 6979 | #define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 /* CRC-32 of "OggS". */ | ||
| 6980 | |||
| 6981 | typedef enum | ||
| 6982 | { | ||
| 6983 | drflac_ogg_recover_on_crc_mismatch, | ||
| 6984 | drflac_ogg_fail_on_crc_mismatch | ||
| 6985 | } drflac_ogg_crc_mismatch_recovery; | ||
| 6986 | |||
| 6987 | #ifndef DR_FLAC_NO_CRC | ||
| 6988 | static drflac_uint32 drflac__crc32_table[] = { | ||
| 6989 | 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, | ||
| 6990 | 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, | ||
| 6991 | 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, | ||
| 6992 | 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, | ||
| 6993 | 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, | ||
| 6994 | 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, | ||
| 6995 | 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, | ||
| 6996 | 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, | ||
| 6997 | 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, | ||
| 6998 | 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, | ||
| 6999 | 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, | ||
| 7000 | 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, | ||
| 7001 | 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, | ||
| 7002 | 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, | ||
| 7003 | 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, | ||
| 7004 | 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, | ||
| 7005 | 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, | ||
| 7006 | 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, | ||
| 7007 | 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, | ||
| 7008 | 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, | ||
| 7009 | 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, | ||
| 7010 | 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, | ||
| 7011 | 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, | ||
| 7012 | 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, | ||
| 7013 | 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, | ||
| 7014 | 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, | ||
| 7015 | 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, | ||
| 7016 | 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, | ||
| 7017 | 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, | ||
| 7018 | 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, | ||
| 7019 | 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, | ||
| 7020 | 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, | ||
| 7021 | 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, | ||
| 7022 | 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, | ||
| 7023 | 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, | ||
| 7024 | 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, | ||
| 7025 | 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, | ||
| 7026 | 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, | ||
| 7027 | 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, | ||
| 7028 | 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, | ||
| 7029 | 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, | ||
| 7030 | 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, | ||
| 7031 | 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, | ||
| 7032 | 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, | ||
| 7033 | 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, | ||
| 7034 | 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, | ||
| 7035 | 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, | ||
| 7036 | 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, | ||
| 7037 | 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, | ||
| 7038 | 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, | ||
| 7039 | 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, | ||
| 7040 | 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, | ||
| 7041 | 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, | ||
| 7042 | 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, | ||
| 7043 | 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, | ||
| 7044 | 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, | ||
| 7045 | 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, | ||
| 7046 | 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, | ||
| 7047 | 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, | ||
| 7048 | 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, | ||
| 7049 | 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, | ||
| 7050 | 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, | ||
| 7051 | 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, | ||
| 7052 | 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L | ||
| 7053 | }; | ||
| 7054 | #endif | ||
| 7055 | |||
| 7056 | static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data) | ||
| 7057 | { | ||
| 7058 | #ifndef DR_FLAC_NO_CRC | ||
| 7059 | return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data]; | ||
| 7060 | #else | ||
| 7061 | (void)data; | ||
| 7062 | return crc32; | ||
| 7063 | #endif | ||
| 7064 | } | ||
| 7065 | |||
| 7066 | #if 0 | ||
| 7067 | static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data) | ||
| 7068 | { | ||
| 7069 | crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF)); | ||
| 7070 | crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF)); | ||
| 7071 | crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF)); | ||
| 7072 | crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF)); | ||
| 7073 | return crc32; | ||
| 7074 | } | ||
| 7075 | |||
| 7076 | static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data) | ||
| 7077 | { | ||
| 7078 | crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF)); | ||
| 7079 | crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF)); | ||
| 7080 | return crc32; | ||
| 7081 | } | ||
| 7082 | #endif | ||
| 7083 | |||
| 7084 | static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) | ||
| 7085 | { | ||
| 7086 | /* This can be optimized. */ | ||
| 7087 | drflac_uint32 i; | ||
| 7088 | for (i = 0; i < dataSize; ++i) { | ||
| 7089 | crc32 = drflac_crc32_byte(crc32, pData[i]); | ||
| 7090 | } | ||
| 7091 | return crc32; | ||
| 7092 | } | ||
| 7093 | |||
| 7094 | |||
| 7095 | static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4]) | ||
| 7096 | { | ||
| 7097 | return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; | ||
| 7098 | } | ||
| 7099 | |||
| 7100 | static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) | ||
| 7101 | { | ||
| 7102 | return 27 + pHeader->segmentCount; | ||
| 7103 | } | ||
| 7104 | |||
| 7105 | static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) | ||
| 7106 | { | ||
| 7107 | drflac_uint32 pageBodySize = 0; | ||
| 7108 | int i; | ||
| 7109 | |||
| 7110 | for (i = 0; i < pHeader->segmentCount; ++i) { | ||
| 7111 | pageBodySize += pHeader->segmentTable[i]; | ||
| 7112 | } | ||
| 7113 | |||
| 7114 | return pageBodySize; | ||
| 7115 | } | ||
| 7116 | |||
| 7117 | static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) | ||
| 7118 | { | ||
| 7119 | drflac_uint8 data[23]; | ||
| 7120 | drflac_uint32 i; | ||
| 7121 | |||
| 7122 | DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); | ||
| 7123 | |||
| 7124 | if (onRead(pUserData, data, 23) != 23) { | ||
| 7125 | return DRFLAC_AT_END; | ||
| 7126 | } | ||
| 7127 | *pBytesRead += 23; | ||
| 7128 | |||
| 7129 | /* | ||
| 7130 | It's not actually used, but set the capture pattern to 'OggS' for completeness. Not doing this will cause static analysers to complain about | ||
| 7131 | us trying to access uninitialized data. We could alternatively just comment out this member of the drflac_ogg_page_header structure, but I | ||
| 7132 | like to have it map to the structure of the underlying data. | ||
| 7133 | */ | ||
| 7134 | pHeader->capturePattern[0] = 'O'; | ||
| 7135 | pHeader->capturePattern[1] = 'g'; | ||
| 7136 | pHeader->capturePattern[2] = 'g'; | ||
| 7137 | pHeader->capturePattern[3] = 'S'; | ||
| 7138 | |||
| 7139 | pHeader->structureVersion = data[0]; | ||
| 7140 | pHeader->headerType = data[1]; | ||
| 7141 | DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); | ||
| 7142 | DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); | ||
| 7143 | DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); | ||
| 7144 | DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); | ||
| 7145 | pHeader->segmentCount = data[22]; | ||
| 7146 | |||
| 7147 | /* Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0. */ | ||
| 7148 | data[18] = 0; | ||
| 7149 | data[19] = 0; | ||
| 7150 | data[20] = 0; | ||
| 7151 | data[21] = 0; | ||
| 7152 | |||
| 7153 | for (i = 0; i < 23; ++i) { | ||
| 7154 | *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); | ||
| 7155 | } | ||
| 7156 | |||
| 7157 | |||
| 7158 | if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { | ||
| 7159 | return DRFLAC_AT_END; | ||
| 7160 | } | ||
| 7161 | *pBytesRead += pHeader->segmentCount; | ||
| 7162 | |||
| 7163 | for (i = 0; i < pHeader->segmentCount; ++i) { | ||
| 7164 | *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); | ||
| 7165 | } | ||
| 7166 | |||
| 7167 | return DRFLAC_SUCCESS; | ||
| 7168 | } | ||
| 7169 | |||
| 7170 | static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) | ||
| 7171 | { | ||
| 7172 | drflac_uint8 id[4]; | ||
| 7173 | |||
| 7174 | *pBytesRead = 0; | ||
| 7175 | |||
| 7176 | if (onRead(pUserData, id, 4) != 4) { | ||
| 7177 | return DRFLAC_AT_END; | ||
| 7178 | } | ||
| 7179 | *pBytesRead += 4; | ||
| 7180 | |||
| 7181 | /* We need to read byte-by-byte until we find the OggS capture pattern. */ | ||
| 7182 | for (;;) { | ||
| 7183 | if (drflac_ogg__is_capture_pattern(id)) { | ||
| 7184 | drflac_result result; | ||
| 7185 | |||
| 7186 | *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; | ||
| 7187 | |||
| 7188 | result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); | ||
| 7189 | if (result == DRFLAC_SUCCESS) { | ||
| 7190 | return DRFLAC_SUCCESS; | ||
| 7191 | } else { | ||
| 7192 | if (result == DRFLAC_CRC_MISMATCH) { | ||
| 7193 | continue; | ||
| 7194 | } else { | ||
| 7195 | return result; | ||
| 7196 | } | ||
| 7197 | } | ||
| 7198 | } else { | ||
| 7199 | /* The first 4 bytes did not equal the capture pattern. Read the next byte and try again. */ | ||
| 7200 | id[0] = id[1]; | ||
| 7201 | id[1] = id[2]; | ||
| 7202 | id[2] = id[3]; | ||
| 7203 | if (onRead(pUserData, &id[3], 1) != 1) { | ||
| 7204 | return DRFLAC_AT_END; | ||
| 7205 | } | ||
| 7206 | *pBytesRead += 1; | ||
| 7207 | } | ||
| 7208 | } | ||
| 7209 | } | ||
| 7210 | |||
| 7211 | |||
| 7212 | /* | ||
| 7213 | The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works | ||
| 7214 | in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is designed | ||
| 7215 | in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type | ||
| 7216 | dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from | ||
| 7217 | the physical Ogg bitstream are converted and delivered in native FLAC format. | ||
| 7218 | */ | ||
| 7219 | typedef struct | ||
| 7220 | { | ||
| 7221 | drflac_read_proc onRead; /* The original onRead callback from drflac_open() and family. */ | ||
| 7222 | drflac_seek_proc onSeek; /* The original onSeek callback from drflac_open() and family. */ | ||
| 7223 | void* pUserData; /* The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. */ | ||
| 7224 | drflac_uint64 currentBytePos; /* The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. */ | ||
| 7225 | drflac_uint64 firstBytePos; /* The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. */ | ||
| 7226 | drflac_uint32 serialNumber; /* The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization. */ | ||
| 7227 | drflac_ogg_page_header bosPageHeader; /* Used for seeking. */ | ||
| 7228 | drflac_ogg_page_header currentPageHeader; | ||
| 7229 | drflac_uint32 bytesRemainingInPage; | ||
| 7230 | drflac_uint32 pageDataSize; | ||
| 7231 | drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; | ||
| 7232 | } drflac_oggbs; /* oggbs = Ogg Bitstream */ | ||
| 7233 | |||
| 7234 | static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) | ||
| 7235 | { | ||
| 7236 | size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); | ||
| 7237 | oggbs->currentBytePos += bytesActuallyRead; | ||
| 7238 | |||
| 7239 | return bytesActuallyRead; | ||
| 7240 | } | ||
| 7241 | |||
| 7242 | static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) | ||
| 7243 | { | ||
| 7244 | if (origin == drflac_seek_origin_start) { | ||
| 7245 | if (offset <= 0x7FFFFFFF) { | ||
| 7246 | if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { | ||
| 7247 | return DRFLAC_FALSE; | ||
| 7248 | } | ||
| 7249 | oggbs->currentBytePos = offset; | ||
| 7250 | |||
| 7251 | return DRFLAC_TRUE; | ||
| 7252 | } else { | ||
| 7253 | if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { | ||
| 7254 | return DRFLAC_FALSE; | ||
| 7255 | } | ||
| 7256 | oggbs->currentBytePos = offset; | ||
| 7257 | |||
| 7258 | return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); | ||
| 7259 | } | ||
| 7260 | } else { | ||
| 7261 | while (offset > 0x7FFFFFFF) { | ||
| 7262 | if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { | ||
| 7263 | return DRFLAC_FALSE; | ||
| 7264 | } | ||
| 7265 | oggbs->currentBytePos += 0x7FFFFFFF; | ||
| 7266 | offset -= 0x7FFFFFFF; | ||
| 7267 | } | ||
| 7268 | |||
| 7269 | if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { /* <-- Safe cast thanks to the loop above. */ | ||
| 7270 | return DRFLAC_FALSE; | ||
| 7271 | } | ||
| 7272 | oggbs->currentBytePos += offset; | ||
| 7273 | |||
| 7274 | return DRFLAC_TRUE; | ||
| 7275 | } | ||
| 7276 | } | ||
| 7277 | |||
| 7278 | static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod) | ||
| 7279 | { | ||
| 7280 | drflac_ogg_page_header header; | ||
| 7281 | for (;;) { | ||
| 7282 | drflac_uint32 crc32 = 0; | ||
| 7283 | drflac_uint32 bytesRead; | ||
| 7284 | drflac_uint32 pageBodySize; | ||
| 7285 | #ifndef DR_FLAC_NO_CRC | ||
| 7286 | drflac_uint32 actualCRC32; | ||
| 7287 | #endif | ||
| 7288 | |||
| 7289 | if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { | ||
| 7290 | return DRFLAC_FALSE; | ||
| 7291 | } | ||
| 7292 | oggbs->currentBytePos += bytesRead; | ||
| 7293 | |||
| 7294 | pageBodySize = drflac_ogg__get_page_body_size(&header); | ||
| 7295 | if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { | ||
| 7296 | continue; /* Invalid page size. Assume it's corrupted and just move to the next page. */ | ||
| 7297 | } | ||
| 7298 | |||
| 7299 | if (header.serialNumber != oggbs->serialNumber) { | ||
| 7300 | /* It's not a FLAC page. Skip it. */ | ||
| 7301 | if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { | ||
| 7302 | return DRFLAC_FALSE; | ||
| 7303 | } | ||
| 7304 | continue; | ||
| 7305 | } | ||
| 7306 | |||
| 7307 | |||
| 7308 | /* We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page. */ | ||
| 7309 | if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { | ||
| 7310 | return DRFLAC_FALSE; | ||
| 7311 | } | ||
| 7312 | oggbs->pageDataSize = pageBodySize; | ||
| 7313 | |||
| 7314 | #ifndef DR_FLAC_NO_CRC | ||
| 7315 | actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); | ||
| 7316 | if (actualCRC32 != header.checksum) { | ||
| 7317 | if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { | ||
| 7318 | continue; /* CRC mismatch. Skip this page. */ | ||
| 7319 | } else { | ||
| 7320 | /* | ||
| 7321 | Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we | ||
| 7322 | go to the next valid page to ensure we're in a good state, but return false to let the caller know that the | ||
| 7323 | seek did not fully complete. | ||
| 7324 | */ | ||
| 7325 | drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); | ||
| 7326 | return DRFLAC_FALSE; | ||
| 7327 | } | ||
| 7328 | } | ||
| 7329 | #else | ||
| 7330 | (void)recoveryMethod; /* <-- Silence a warning. */ | ||
| 7331 | #endif | ||
| 7332 | |||
| 7333 | oggbs->currentPageHeader = header; | ||
| 7334 | oggbs->bytesRemainingInPage = pageBodySize; | ||
| 7335 | return DRFLAC_TRUE; | ||
| 7336 | } | ||
| 7337 | } | ||
| 7338 | |||
| 7339 | /* Function below is unused at the moment, but I might be re-adding it later. */ | ||
| 7340 | #if 0 | ||
| 7341 | static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) | ||
| 7342 | { | ||
| 7343 | drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; | ||
| 7344 | drflac_uint8 iSeg = 0; | ||
| 7345 | drflac_uint32 iByte = 0; | ||
| 7346 | while (iByte < bytesConsumedInPage) { | ||
| 7347 | drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; | ||
| 7348 | if (iByte + segmentSize > bytesConsumedInPage) { | ||
| 7349 | break; | ||
| 7350 | } else { | ||
| 7351 | iSeg += 1; | ||
| 7352 | iByte += segmentSize; | ||
| 7353 | } | ||
| 7354 | } | ||
| 7355 | |||
| 7356 | *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); | ||
| 7357 | return iSeg; | ||
| 7358 | } | ||
| 7359 | |||
| 7360 | static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) | ||
| 7361 | { | ||
| 7362 | /* The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page. */ | ||
| 7363 | for (;;) { | ||
| 7364 | drflac_bool32 atEndOfPage = DRFLAC_FALSE; | ||
| 7365 | |||
| 7366 | drflac_uint8 bytesRemainingInSeg; | ||
| 7367 | drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); | ||
| 7368 | |||
| 7369 | drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; | ||
| 7370 | for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { | ||
| 7371 | drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; | ||
| 7372 | if (segmentSize < 255) { | ||
| 7373 | if (iSeg == oggbs->currentPageHeader.segmentCount-1) { | ||
| 7374 | atEndOfPage = DRFLAC_TRUE; | ||
| 7375 | } | ||
| 7376 | |||
| 7377 | break; | ||
| 7378 | } | ||
| 7379 | |||
| 7380 | bytesToEndOfPacketOrPage += segmentSize; | ||
| 7381 | } | ||
| 7382 | |||
| 7383 | /* | ||
| 7384 | At this point we will have found either the packet or the end of the page. If were at the end of the page we'll | ||
| 7385 | want to load the next page and keep searching for the end of the packet. | ||
| 7386 | */ | ||
| 7387 | drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); | ||
| 7388 | oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; | ||
| 7389 | |||
| 7390 | if (atEndOfPage) { | ||
| 7391 | /* | ||
| 7392 | We're potentially at the next packet, but we need to check the next page first to be sure because the packet may | ||
| 7393 | straddle pages. | ||
| 7394 | */ | ||
| 7395 | if (!drflac_oggbs__goto_next_page(oggbs)) { | ||
| 7396 | return DRFLAC_FALSE; | ||
| 7397 | } | ||
| 7398 | |||
| 7399 | /* If it's a fresh packet it most likely means we're at the next packet. */ | ||
| 7400 | if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { | ||
| 7401 | return DRFLAC_TRUE; | ||
| 7402 | } | ||
| 7403 | } else { | ||
| 7404 | /* We're at the next packet. */ | ||
| 7405 | return DRFLAC_TRUE; | ||
| 7406 | } | ||
| 7407 | } | ||
| 7408 | } | ||
| 7409 | |||
| 7410 | static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) | ||
| 7411 | { | ||
| 7412 | /* The bitstream should be sitting on the first byte just after the header of the frame. */ | ||
| 7413 | |||
| 7414 | /* What we're actually doing here is seeking to the start of the next packet. */ | ||
| 7415 | return drflac_oggbs__seek_to_next_packet(oggbs); | ||
| 7416 | } | ||
| 7417 | #endif | ||
| 7418 | |||
| 7419 | static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) | ||
| 7420 | { | ||
| 7421 | drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; | ||
| 7422 | drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; | ||
| 7423 | size_t bytesRead = 0; | ||
| 7424 | |||
| 7425 | DRFLAC_ASSERT(oggbs != NULL); | ||
| 7426 | DRFLAC_ASSERT(pRunningBufferOut != NULL); | ||
| 7427 | |||
| 7428 | /* Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one. */ | ||
| 7429 | while (bytesRead < bytesToRead) { | ||
| 7430 | size_t bytesRemainingToRead = bytesToRead - bytesRead; | ||
| 7431 | |||
| 7432 | if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { | ||
| 7433 | DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); | ||
| 7434 | bytesRead += bytesRemainingToRead; | ||
| 7435 | oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; | ||
| 7436 | break; | ||
| 7437 | } | ||
| 7438 | |||
| 7439 | /* If we get here it means some of the requested data is contained in the next pages. */ | ||
| 7440 | if (oggbs->bytesRemainingInPage > 0) { | ||
| 7441 | DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); | ||
| 7442 | bytesRead += oggbs->bytesRemainingInPage; | ||
| 7443 | pRunningBufferOut += oggbs->bytesRemainingInPage; | ||
| 7444 | oggbs->bytesRemainingInPage = 0; | ||
| 7445 | } | ||
| 7446 | |||
| 7447 | DRFLAC_ASSERT(bytesRemainingToRead > 0); | ||
| 7448 | if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { | ||
| 7449 | break; /* Failed to go to the next page. Might have simply hit the end of the stream. */ | ||
| 7450 | } | ||
| 7451 | } | ||
| 7452 | |||
| 7453 | return bytesRead; | ||
| 7454 | } | ||
| 7455 | |||
| 7456 | static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) | ||
| 7457 | { | ||
| 7458 | drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; | ||
| 7459 | int bytesSeeked = 0; | ||
| 7460 | |||
| 7461 | DRFLAC_ASSERT(oggbs != NULL); | ||
| 7462 | DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ | ||
| 7463 | |||
| 7464 | /* Seeking is always forward which makes things a lot simpler. */ | ||
| 7465 | if (origin == drflac_seek_origin_start) { | ||
| 7466 | if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { | ||
| 7467 | return DRFLAC_FALSE; | ||
| 7468 | } | ||
| 7469 | |||
| 7470 | if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { | ||
| 7471 | return DRFLAC_FALSE; | ||
| 7472 | } | ||
| 7473 | |||
| 7474 | return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); | ||
| 7475 | } | ||
| 7476 | |||
| 7477 | DRFLAC_ASSERT(origin == drflac_seek_origin_current); | ||
| 7478 | |||
| 7479 | while (bytesSeeked < offset) { | ||
| 7480 | int bytesRemainingToSeek = offset - bytesSeeked; | ||
| 7481 | DRFLAC_ASSERT(bytesRemainingToSeek >= 0); | ||
| 7482 | |||
| 7483 | if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { | ||
| 7484 | bytesSeeked += bytesRemainingToSeek; | ||
| 7485 | (void)bytesSeeked; /* <-- Silence a dead store warning emitted by Clang Static Analyzer. */ | ||
| 7486 | oggbs->bytesRemainingInPage -= bytesRemainingToSeek; | ||
| 7487 | break; | ||
| 7488 | } | ||
| 7489 | |||
| 7490 | /* If we get here it means some of the requested data is contained in the next pages. */ | ||
| 7491 | if (oggbs->bytesRemainingInPage > 0) { | ||
| 7492 | bytesSeeked += (int)oggbs->bytesRemainingInPage; | ||
| 7493 | oggbs->bytesRemainingInPage = 0; | ||
| 7494 | } | ||
| 7495 | |||
| 7496 | DRFLAC_ASSERT(bytesRemainingToSeek > 0); | ||
| 7497 | if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { | ||
| 7498 | /* Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. */ | ||
| 7499 | return DRFLAC_FALSE; | ||
| 7500 | } | ||
| 7501 | } | ||
| 7502 | |||
| 7503 | return DRFLAC_TRUE; | ||
| 7504 | } | ||
| 7505 | |||
| 7506 | |||
| 7507 | static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) | ||
| 7508 | { | ||
| 7509 | drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; | ||
| 7510 | drflac_uint64 originalBytePos; | ||
| 7511 | drflac_uint64 runningGranulePosition; | ||
| 7512 | drflac_uint64 runningFrameBytePos; | ||
| 7513 | drflac_uint64 runningPCMFrameCount; | ||
| 7514 | |||
| 7515 | DRFLAC_ASSERT(oggbs != NULL); | ||
| 7516 | |||
| 7517 | originalBytePos = oggbs->currentBytePos; /* For recovery. Points to the OggS identifier. */ | ||
| 7518 | |||
| 7519 | /* First seek to the first frame. */ | ||
| 7520 | if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { | ||
| 7521 | return DRFLAC_FALSE; | ||
| 7522 | } | ||
| 7523 | oggbs->bytesRemainingInPage = 0; | ||
| 7524 | |||
| 7525 | runningGranulePosition = 0; | ||
| 7526 | for (;;) { | ||
| 7527 | if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { | ||
| 7528 | drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); | ||
| 7529 | return DRFLAC_FALSE; /* Never did find that sample... */ | ||
| 7530 | } | ||
| 7531 | |||
| 7532 | runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; | ||
| 7533 | if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { | ||
| 7534 | break; /* The sample is somewhere in the previous page. */ | ||
| 7535 | } | ||
| 7536 | |||
| 7537 | /* | ||
| 7538 | At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we | ||
| 7539 | disregard any pages that do not begin a fresh packet. | ||
| 7540 | */ | ||
| 7541 | if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { /* <-- Is it a fresh page? */ | ||
| 7542 | if (oggbs->currentPageHeader.segmentTable[0] >= 2) { | ||
| 7543 | drflac_uint8 firstBytesInPage[2]; | ||
| 7544 | firstBytesInPage[0] = oggbs->pageData[0]; | ||
| 7545 | firstBytesInPage[1] = oggbs->pageData[1]; | ||
| 7546 | |||
| 7547 | if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { /* <-- Does the page begin with a frame's sync code? */ | ||
| 7548 | runningGranulePosition = oggbs->currentPageHeader.granulePosition; | ||
| 7549 | } | ||
| 7550 | |||
| 7551 | continue; | ||
| 7552 | } | ||
| 7553 | } | ||
| 7554 | } | ||
| 7555 | |||
| 7556 | /* | ||
| 7557 | We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the | ||
| 7558 | start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of | ||
| 7559 | a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until | ||
| 7560 | we find the one containing the target sample. | ||
| 7561 | */ | ||
| 7562 | if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { | ||
| 7563 | return DRFLAC_FALSE; | ||
| 7564 | } | ||
| 7565 | if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { | ||
| 7566 | return DRFLAC_FALSE; | ||
| 7567 | } | ||
| 7568 | |||
| 7569 | /* | ||
| 7570 | At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep | ||
| 7571 | looping over these frames until we find the one containing the sample we're after. | ||
| 7572 | */ | ||
| 7573 | runningPCMFrameCount = runningGranulePosition; | ||
| 7574 | for (;;) { | ||
| 7575 | /* | ||
| 7576 | There are two ways to find the sample and seek past irrelevant frames: | ||
| 7577 | 1) Use the native FLAC decoder. | ||
| 7578 | 2) Use Ogg's framing system. | ||
| 7579 | |||
| 7580 | Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to | ||
| 7581 | do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code | ||
| 7582 | duplication for the decoding of frame headers. | ||
| 7583 | |||
| 7584 | Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg | ||
| 7585 | bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the | ||
| 7586 | standard drflac__*() APIs because that will read in extra data for its own internal caching which in turn breaks | ||
| 7587 | the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read | ||
| 7588 | using the native FLAC decoding APIs, such as drflac__read_next_flac_frame_header(), need to be re-implemented so as to | ||
| 7589 | avoid the use of the drflac_bs object. | ||
| 7590 | |||
| 7591 | Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: | ||
| 7592 | 1) Seeking is already partially accelerated using Ogg's paging system in the code block above. | ||
| 7593 | 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. | ||
| 7594 | 3) Simplicity. | ||
| 7595 | */ | ||
| 7596 | drflac_uint64 firstPCMFrameInFLACFrame = 0; | ||
| 7597 | drflac_uint64 lastPCMFrameInFLACFrame = 0; | ||
| 7598 | drflac_uint64 pcmFrameCountInThisFrame; | ||
| 7599 | |||
| 7600 | if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { | ||
| 7601 | return DRFLAC_FALSE; | ||
| 7602 | } | ||
| 7603 | |||
| 7604 | drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); | ||
| 7605 | |||
| 7606 | pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; | ||
| 7607 | |||
| 7608 | /* If we are seeking to the end of the file and we've just hit it, we're done. */ | ||
| 7609 | if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { | ||
| 7610 | drflac_result result = drflac__decode_flac_frame(pFlac); | ||
| 7611 | if (result == DRFLAC_SUCCESS) { | ||
| 7612 | pFlac->currentPCMFrame = pcmFrameIndex; | ||
| 7613 | pFlac->currentFLACFrame.pcmFramesRemaining = 0; | ||
| 7614 | return DRFLAC_TRUE; | ||
| 7615 | } else { | ||
| 7616 | return DRFLAC_FALSE; | ||
| 7617 | } | ||
| 7618 | } | ||
| 7619 | |||
| 7620 | if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { | ||
| 7621 | /* | ||
| 7622 | The sample should be in this FLAC frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend | ||
| 7623 | it never existed and keep iterating. | ||
| 7624 | */ | ||
| 7625 | drflac_result result = drflac__decode_flac_frame(pFlac); | ||
| 7626 | if (result == DRFLAC_SUCCESS) { | ||
| 7627 | /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ | ||
| 7628 | drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); /* <-- Safe cast because the maximum number of samples in a frame is 65535. */ | ||
| 7629 | if (pcmFramesToDecode == 0) { | ||
| 7630 | return DRFLAC_TRUE; | ||
| 7631 | } | ||
| 7632 | |||
| 7633 | pFlac->currentPCMFrame = runningPCMFrameCount; | ||
| 7634 | |||
| 7635 | return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */ | ||
| 7636 | } else { | ||
| 7637 | if (result == DRFLAC_CRC_MISMATCH) { | ||
| 7638 | continue; /* CRC mismatch. Pretend this frame never existed. */ | ||
| 7639 | } else { | ||
| 7640 | return DRFLAC_FALSE; | ||
| 7641 | } | ||
| 7642 | } | ||
| 7643 | } else { | ||
| 7644 | /* | ||
| 7645 | It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this | ||
| 7646 | frame never existed and leave the running sample count untouched. | ||
| 7647 | */ | ||
| 7648 | drflac_result result = drflac__seek_to_next_flac_frame(pFlac); | ||
| 7649 | if (result == DRFLAC_SUCCESS) { | ||
| 7650 | runningPCMFrameCount += pcmFrameCountInThisFrame; | ||
| 7651 | } else { | ||
| 7652 | if (result == DRFLAC_CRC_MISMATCH) { | ||
| 7653 | continue; /* CRC mismatch. Pretend this frame never existed. */ | ||
| 7654 | } else { | ||
| 7655 | return DRFLAC_FALSE; | ||
| 7656 | } | ||
| 7657 | } | ||
| 7658 | } | ||
| 7659 | } | ||
| 7660 | } | ||
| 7661 | |||
| 7662 | |||
| 7663 | |||
| 7664 | static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) | ||
| 7665 | { | ||
| 7666 | drflac_ogg_page_header header; | ||
| 7667 | drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; | ||
| 7668 | drflac_uint32 bytesRead = 0; | ||
| 7669 | |||
| 7670 | /* Pre Condition: The bit stream should be sitting just past the 4-byte OggS capture pattern. */ | ||
| 7671 | (void)relaxed; | ||
| 7672 | |||
| 7673 | pInit->container = drflac_container_ogg; | ||
| 7674 | pInit->oggFirstBytePos = 0; | ||
| 7675 | |||
| 7676 | /* | ||
| 7677 | We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the | ||
| 7678 | stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if | ||
| 7679 | any match the FLAC specification. Important to keep in mind that the stream may be multiplexed. | ||
| 7680 | */ | ||
| 7681 | if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { | ||
| 7682 | return DRFLAC_FALSE; | ||
| 7683 | } | ||
| 7684 | pInit->runningFilePos += bytesRead; | ||
| 7685 | |||
| 7686 | for (;;) { | ||
| 7687 | int pageBodySize; | ||
| 7688 | |||
| 7689 | /* Break if we're past the beginning of stream page. */ | ||
| 7690 | if ((header.headerType & 0x02) == 0) { | ||
| 7691 | return DRFLAC_FALSE; | ||
| 7692 | } | ||
| 7693 | |||
| 7694 | /* Check if it's a FLAC header. */ | ||
| 7695 | pageBodySize = drflac_ogg__get_page_body_size(&header); | ||
| 7696 | if (pageBodySize == 51) { /* 51 = the lacing value of the FLAC header packet. */ | ||
| 7697 | /* It could be a FLAC page... */ | ||
| 7698 | drflac_uint32 bytesRemainingInPage = pageBodySize; | ||
| 7699 | drflac_uint8 packetType; | ||
| 7700 | |||
| 7701 | if (onRead(pUserData, &packetType, 1) != 1) { | ||
| 7702 | return DRFLAC_FALSE; | ||
| 7703 | } | ||
| 7704 | |||
| 7705 | bytesRemainingInPage -= 1; | ||
| 7706 | if (packetType == 0x7F) { | ||
| 7707 | /* Increasingly more likely to be a FLAC page... */ | ||
| 7708 | drflac_uint8 sig[4]; | ||
| 7709 | if (onRead(pUserData, sig, 4) != 4) { | ||
| 7710 | return DRFLAC_FALSE; | ||
| 7711 | } | ||
| 7712 | |||
| 7713 | bytesRemainingInPage -= 4; | ||
| 7714 | if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { | ||
| 7715 | /* Almost certainly a FLAC page... */ | ||
| 7716 | drflac_uint8 mappingVersion[2]; | ||
| 7717 | if (onRead(pUserData, mappingVersion, 2) != 2) { | ||
| 7718 | return DRFLAC_FALSE; | ||
| 7719 | } | ||
| 7720 | |||
| 7721 | if (mappingVersion[0] != 1) { | ||
| 7722 | return DRFLAC_FALSE; /* Only supporting version 1.x of the Ogg mapping. */ | ||
| 7723 | } | ||
| 7724 | |||
| 7725 | /* | ||
| 7726 | The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to | ||
| 7727 | be handling it in a generic way based on the serial number and packet types. | ||
| 7728 | */ | ||
| 7729 | if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { | ||
| 7730 | return DRFLAC_FALSE; | ||
| 7731 | } | ||
| 7732 | |||
| 7733 | /* Expecting the native FLAC signature "fLaC". */ | ||
| 7734 | if (onRead(pUserData, sig, 4) != 4) { | ||
| 7735 | return DRFLAC_FALSE; | ||
| 7736 | } | ||
| 7737 | |||
| 7738 | if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { | ||
| 7739 | /* The remaining data in the page should be the STREAMINFO block. */ | ||
| 7740 | drflac_streaminfo streaminfo; | ||
| 7741 | drflac_uint8 isLastBlock; | ||
| 7742 | drflac_uint8 blockType; | ||
| 7743 | drflac_uint32 blockSize; | ||
| 7744 | if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { | ||
| 7745 | return DRFLAC_FALSE; | ||
| 7746 | } | ||
| 7747 | |||
| 7748 | if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { | ||
| 7749 | return DRFLAC_FALSE; /* Invalid block type. First block must be the STREAMINFO block. */ | ||
| 7750 | } | ||
| 7751 | |||
| 7752 | if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { | ||
| 7753 | /* Success! */ | ||
| 7754 | pInit->hasStreamInfoBlock = DRFLAC_TRUE; | ||
| 7755 | pInit->sampleRate = streaminfo.sampleRate; | ||
| 7756 | pInit->channels = streaminfo.channels; | ||
| 7757 | pInit->bitsPerSample = streaminfo.bitsPerSample; | ||
| 7758 | pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; | ||
| 7759 | pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; | ||
| 7760 | pInit->hasMetadataBlocks = !isLastBlock; | ||
| 7761 | |||
| 7762 | if (onMeta) { | ||
| 7763 | drflac_metadata metadata; | ||
| 7764 | metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; | ||
| 7765 | metadata.pRawData = NULL; | ||
| 7766 | metadata.rawDataSize = 0; | ||
| 7767 | metadata.data.streaminfo = streaminfo; | ||
| 7768 | onMeta(pUserDataMD, &metadata); | ||
| 7769 | } | ||
| 7770 | |||
| 7771 | pInit->runningFilePos += pageBodySize; | ||
| 7772 | pInit->oggFirstBytePos = pInit->runningFilePos - 79; /* Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page. */ | ||
| 7773 | pInit->oggSerial = header.serialNumber; | ||
| 7774 | pInit->oggBosHeader = header; | ||
| 7775 | break; | ||
| 7776 | } else { | ||
| 7777 | /* Failed to read STREAMINFO block. Aww, so close... */ | ||
| 7778 | return DRFLAC_FALSE; | ||
| 7779 | } | ||
| 7780 | } else { | ||
| 7781 | /* Invalid file. */ | ||
| 7782 | return DRFLAC_FALSE; | ||
| 7783 | } | ||
| 7784 | } else { | ||
| 7785 | /* Not a FLAC header. Skip it. */ | ||
| 7786 | if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { | ||
| 7787 | return DRFLAC_FALSE; | ||
| 7788 | } | ||
| 7789 | } | ||
| 7790 | } else { | ||
| 7791 | /* Not a FLAC header. Seek past the entire page and move on to the next. */ | ||
| 7792 | if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { | ||
| 7793 | return DRFLAC_FALSE; | ||
| 7794 | } | ||
| 7795 | } | ||
| 7796 | } else { | ||
| 7797 | if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { | ||
| 7798 | return DRFLAC_FALSE; | ||
| 7799 | } | ||
| 7800 | } | ||
| 7801 | |||
| 7802 | pInit->runningFilePos += pageBodySize; | ||
| 7803 | |||
| 7804 | |||
| 7805 | /* Read the header of the next page. */ | ||
| 7806 | if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { | ||
| 7807 | return DRFLAC_FALSE; | ||
| 7808 | } | ||
| 7809 | pInit->runningFilePos += bytesRead; | ||
| 7810 | } | ||
| 7811 | |||
| 7812 | /* | ||
| 7813 | If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next | ||
| 7814 | packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialization phase for Ogg is to create the | ||
| 7815 | Ogg bistream object. | ||
| 7816 | */ | ||
| 7817 | pInit->hasMetadataBlocks = DRFLAC_TRUE; /* <-- Always have at least VORBIS_COMMENT metadata block. */ | ||
| 7818 | return DRFLAC_TRUE; | ||
| 7819 | } | ||
| 7820 | #endif | ||
| 7821 | |||
| 7822 | static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) | ||
| 7823 | { | ||
| 7824 | drflac_bool32 relaxed; | ||
| 7825 | drflac_uint8 id[4]; | ||
| 7826 | |||
| 7827 | if (pInit == NULL || onRead == NULL || onSeek == NULL) { | ||
| 7828 | return DRFLAC_FALSE; | ||
| 7829 | } | ||
| 7830 | |||
| 7831 | DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); | ||
| 7832 | pInit->onRead = onRead; | ||
| 7833 | pInit->onSeek = onSeek; | ||
| 7834 | pInit->onMeta = onMeta; | ||
| 7835 | pInit->container = container; | ||
| 7836 | pInit->pUserData = pUserData; | ||
| 7837 | pInit->pUserDataMD = pUserDataMD; | ||
| 7838 | |||
| 7839 | pInit->bs.onRead = onRead; | ||
| 7840 | pInit->bs.onSeek = onSeek; | ||
| 7841 | pInit->bs.pUserData = pUserData; | ||
| 7842 | drflac__reset_cache(&pInit->bs); | ||
| 7843 | |||
| 7844 | |||
| 7845 | /* If the container is explicitly defined then we can try opening in relaxed mode. */ | ||
| 7846 | relaxed = container != drflac_container_unknown; | ||
| 7847 | |||
| 7848 | /* Skip over any ID3 tags. */ | ||
| 7849 | for (;;) { | ||
| 7850 | if (onRead(pUserData, id, 4) != 4) { | ||
| 7851 | return DRFLAC_FALSE; /* Ran out of data. */ | ||
| 7852 | } | ||
| 7853 | pInit->runningFilePos += 4; | ||
| 7854 | |||
| 7855 | if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { | ||
| 7856 | drflac_uint8 header[6]; | ||
| 7857 | drflac_uint8 flags; | ||
| 7858 | drflac_uint32 headerSize; | ||
| 7859 | |||
| 7860 | if (onRead(pUserData, header, 6) != 6) { | ||
| 7861 | return DRFLAC_FALSE; /* Ran out of data. */ | ||
| 7862 | } | ||
| 7863 | pInit->runningFilePos += 6; | ||
| 7864 | |||
| 7865 | flags = header[1]; | ||
| 7866 | |||
| 7867 | DRFLAC_COPY_MEMORY(&headerSize, header+2, 4); | ||
| 7868 | headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); | ||
| 7869 | if (flags & 0x10) { | ||
| 7870 | headerSize += 10; | ||
| 7871 | } | ||
| 7872 | |||
| 7873 | if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { | ||
| 7874 | return DRFLAC_FALSE; /* Failed to seek past the tag. */ | ||
| 7875 | } | ||
| 7876 | pInit->runningFilePos += headerSize; | ||
| 7877 | } else { | ||
| 7878 | break; | ||
| 7879 | } | ||
| 7880 | } | ||
| 7881 | |||
| 7882 | if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { | ||
| 7883 | return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); | ||
| 7884 | } | ||
| 7885 | #ifndef DR_FLAC_NO_OGG | ||
| 7886 | if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { | ||
| 7887 | return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); | ||
| 7888 | } | ||
| 7889 | #endif | ||
| 7890 | |||
| 7891 | /* If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable. */ | ||
| 7892 | if (relaxed) { | ||
| 7893 | if (container == drflac_container_native) { | ||
| 7894 | return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); | ||
| 7895 | } | ||
| 7896 | #ifndef DR_FLAC_NO_OGG | ||
| 7897 | if (container == drflac_container_ogg) { | ||
| 7898 | return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); | ||
| 7899 | } | ||
| 7900 | #endif | ||
| 7901 | } | ||
| 7902 | |||
| 7903 | /* Unsupported container. */ | ||
| 7904 | return DRFLAC_FALSE; | ||
| 7905 | } | ||
| 7906 | |||
| 7907 | static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) | ||
| 7908 | { | ||
| 7909 | DRFLAC_ASSERT(pFlac != NULL); | ||
| 7910 | DRFLAC_ASSERT(pInit != NULL); | ||
| 7911 | |||
| 7912 | DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); | ||
| 7913 | pFlac->bs = pInit->bs; | ||
| 7914 | pFlac->onMeta = pInit->onMeta; | ||
| 7915 | pFlac->pUserDataMD = pInit->pUserDataMD; | ||
| 7916 | pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; | ||
| 7917 | pFlac->sampleRate = pInit->sampleRate; | ||
| 7918 | pFlac->channels = (drflac_uint8)pInit->channels; | ||
| 7919 | pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample; | ||
| 7920 | pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; | ||
| 7921 | pFlac->container = pInit->container; | ||
| 7922 | } | ||
| 7923 | |||
| 7924 | |||
| 7925 | static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 7926 | { | ||
| 7927 | drflac_init_info init; | ||
| 7928 | drflac_uint32 allocationSize; | ||
| 7929 | drflac_uint32 wholeSIMDVectorCountPerChannel; | ||
| 7930 | drflac_uint32 decodedSamplesAllocationSize; | ||
| 7931 | #ifndef DR_FLAC_NO_OGG | ||
| 7932 | drflac_oggbs* pOggbs = NULL; | ||
| 7933 | #endif | ||
| 7934 | drflac_uint64 firstFramePos; | ||
| 7935 | drflac_uint64 seektablePos; | ||
| 7936 | drflac_uint32 seekpointCount; | ||
| 7937 | drflac_allocation_callbacks allocationCallbacks; | ||
| 7938 | drflac* pFlac; | ||
| 7939 | |||
| 7940 | /* CPU support first. */ | ||
| 7941 | drflac__init_cpu_caps(); | ||
| 7942 | |||
| 7943 | if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { | ||
| 7944 | return NULL; | ||
| 7945 | } | ||
| 7946 | |||
| 7947 | if (pAllocationCallbacks != NULL) { | ||
| 7948 | allocationCallbacks = *pAllocationCallbacks; | ||
| 7949 | if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { | ||
| 7950 | return NULL; /* Invalid allocation callbacks. */ | ||
| 7951 | } | ||
| 7952 | } else { | ||
| 7953 | allocationCallbacks.pUserData = NULL; | ||
| 7954 | allocationCallbacks.onMalloc = drflac__malloc_default; | ||
| 7955 | allocationCallbacks.onRealloc = drflac__realloc_default; | ||
| 7956 | allocationCallbacks.onFree = drflac__free_default; | ||
| 7957 | } | ||
| 7958 | |||
| 7959 | |||
| 7960 | /* | ||
| 7961 | The size of the allocation for the drflac object needs to be large enough to fit the following: | ||
| 7962 | 1) The main members of the drflac structure | ||
| 7963 | 2) A block of memory large enough to store the decoded samples of the largest frame in the stream | ||
| 7964 | 3) If the container is Ogg, a drflac_oggbs object | ||
| 7965 | |||
| 7966 | The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration | ||
| 7967 | the different SIMD instruction sets. | ||
| 7968 | */ | ||
| 7969 | allocationSize = sizeof(drflac); | ||
| 7970 | |||
| 7971 | /* | ||
| 7972 | The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector | ||
| 7973 | we are supporting. | ||
| 7974 | */ | ||
| 7975 | if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { | ||
| 7976 | wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); | ||
| 7977 | } else { | ||
| 7978 | wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; | ||
| 7979 | } | ||
| 7980 | |||
| 7981 | decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; | ||
| 7982 | |||
| 7983 | allocationSize += decodedSamplesAllocationSize; | ||
| 7984 | allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; /* Allocate extra bytes to ensure we have enough for alignment. */ | ||
| 7985 | |||
| 7986 | #ifndef DR_FLAC_NO_OGG | ||
| 7987 | /* There's additional data required for Ogg streams. */ | ||
| 7988 | if (init.container == drflac_container_ogg) { | ||
| 7989 | allocationSize += sizeof(drflac_oggbs); | ||
| 7990 | |||
| 7991 | pOggbs = (drflac_oggbs*)drflac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); | ||
| 7992 | if (pOggbs == NULL) { | ||
| 7993 | return NULL; /*DRFLAC_OUT_OF_MEMORY;*/ | ||
| 7994 | } | ||
| 7995 | |||
| 7996 | DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); | ||
| 7997 | pOggbs->onRead = onRead; | ||
| 7998 | pOggbs->onSeek = onSeek; | ||
| 7999 | pOggbs->pUserData = pUserData; | ||
| 8000 | pOggbs->currentBytePos = init.oggFirstBytePos; | ||
| 8001 | pOggbs->firstBytePos = init.oggFirstBytePos; | ||
| 8002 | pOggbs->serialNumber = init.oggSerial; | ||
| 8003 | pOggbs->bosPageHeader = init.oggBosHeader; | ||
| 8004 | pOggbs->bytesRemainingInPage = 0; | ||
| 8005 | } | ||
| 8006 | #endif | ||
| 8007 | |||
| 8008 | /* | ||
| 8009 | This part is a bit awkward. We need to load the seektable so that it can be referenced in-memory, but I want the drflac object to | ||
| 8010 | consist of only a single heap allocation. To this, the size of the seek table needs to be known, which we determine when reading | ||
| 8011 | and decoding the metadata. | ||
| 8012 | */ | ||
| 8013 | firstFramePos = 42; /* <-- We know we are at byte 42 at this point. */ | ||
| 8014 | seektablePos = 0; | ||
| 8015 | seekpointCount = 0; | ||
| 8016 | if (init.hasMetadataBlocks) { | ||
| 8017 | drflac_read_proc onReadOverride = onRead; | ||
| 8018 | drflac_seek_proc onSeekOverride = onSeek; | ||
| 8019 | void* pUserDataOverride = pUserData; | ||
| 8020 | |||
| 8021 | #ifndef DR_FLAC_NO_OGG | ||
| 8022 | if (init.container == drflac_container_ogg) { | ||
| 8023 | onReadOverride = drflac__on_read_ogg; | ||
| 8024 | onSeekOverride = drflac__on_seek_ogg; | ||
| 8025 | pUserDataOverride = (void*)pOggbs; | ||
| 8026 | } | ||
| 8027 | #endif | ||
| 8028 | |||
| 8029 | if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { | ||
| 8030 | #ifndef DR_FLAC_NO_OGG | ||
| 8031 | drflac__free_from_callbacks(pOggbs, &allocationCallbacks); | ||
| 8032 | #endif | ||
| 8033 | return NULL; | ||
| 8034 | } | ||
| 8035 | |||
| 8036 | allocationSize += seekpointCount * sizeof(drflac_seekpoint); | ||
| 8037 | } | ||
| 8038 | |||
| 8039 | |||
| 8040 | pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks); | ||
| 8041 | if (pFlac == NULL) { | ||
| 8042 | #ifndef DR_FLAC_NO_OGG | ||
| 8043 | drflac__free_from_callbacks(pOggbs, &allocationCallbacks); | ||
| 8044 | #endif | ||
| 8045 | return NULL; | ||
| 8046 | } | ||
| 8047 | |||
| 8048 | drflac__init_from_info(pFlac, &init); | ||
| 8049 | pFlac->allocationCallbacks = allocationCallbacks; | ||
| 8050 | pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); | ||
| 8051 | |||
| 8052 | #ifndef DR_FLAC_NO_OGG | ||
| 8053 | if (init.container == drflac_container_ogg) { | ||
| 8054 | drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(drflac_seekpoint))); | ||
| 8055 | DRFLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); | ||
| 8056 | |||
| 8057 | /* At this point the pOggbs object has been handed over to pInternalOggbs and can be freed. */ | ||
| 8058 | drflac__free_from_callbacks(pOggbs, &allocationCallbacks); | ||
| 8059 | pOggbs = NULL; | ||
| 8060 | |||
| 8061 | /* The Ogg bistream needs to be layered on top of the original bitstream. */ | ||
| 8062 | pFlac->bs.onRead = drflac__on_read_ogg; | ||
| 8063 | pFlac->bs.onSeek = drflac__on_seek_ogg; | ||
| 8064 | pFlac->bs.pUserData = (void*)pInternalOggbs; | ||
| 8065 | pFlac->_oggbs = (void*)pInternalOggbs; | ||
| 8066 | } | ||
| 8067 | #endif | ||
| 8068 | |||
| 8069 | pFlac->firstFLACFramePosInBytes = firstFramePos; | ||
| 8070 | |||
| 8071 | /* NOTE: Seektables are not currently compatible with Ogg encapsulation (Ogg has its own accelerated seeking system). I may change this later, so I'm leaving this here for now. */ | ||
| 8072 | #ifndef DR_FLAC_NO_OGG | ||
| 8073 | if (init.container == drflac_container_ogg) | ||
| 8074 | { | ||
| 8075 | pFlac->pSeekpoints = NULL; | ||
| 8076 | pFlac->seekpointCount = 0; | ||
| 8077 | } | ||
| 8078 | else | ||
| 8079 | #endif | ||
| 8080 | { | ||
| 8081 | /* If we have a seektable we need to load it now, making sure we move back to where we were previously. */ | ||
| 8082 | if (seektablePos != 0) { | ||
| 8083 | pFlac->seekpointCount = seekpointCount; | ||
| 8084 | pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); | ||
| 8085 | |||
| 8086 | DRFLAC_ASSERT(pFlac->bs.onSeek != NULL); | ||
| 8087 | DRFLAC_ASSERT(pFlac->bs.onRead != NULL); | ||
| 8088 | |||
| 8089 | /* Seek to the seektable, then just read directly into our seektable buffer. */ | ||
| 8090 | if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { | ||
| 8091 | drflac_uint32 iSeekpoint; | ||
| 8092 | |||
| 8093 | for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { | ||
| 8094 | if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) == DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { | ||
| 8095 | /* Endian swap. */ | ||
| 8096 | pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); | ||
| 8097 | pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); | ||
| 8098 | pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); | ||
| 8099 | } else { | ||
| 8100 | /* Failed to read the seektable. Pretend we don't have one. */ | ||
| 8101 | pFlac->pSeekpoints = NULL; | ||
| 8102 | pFlac->seekpointCount = 0; | ||
| 8103 | break; | ||
| 8104 | } | ||
| 8105 | } | ||
| 8106 | |||
| 8107 | /* We need to seek back to where we were. If this fails it's a critical error. */ | ||
| 8108 | if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) { | ||
| 8109 | drflac__free_from_callbacks(pFlac, &allocationCallbacks); | ||
| 8110 | return NULL; | ||
| 8111 | } | ||
| 8112 | } else { | ||
| 8113 | /* Failed to seek to the seektable. Ominous sign, but for now we can just pretend we don't have one. */ | ||
| 8114 | pFlac->pSeekpoints = NULL; | ||
| 8115 | pFlac->seekpointCount = 0; | ||
| 8116 | } | ||
| 8117 | } | ||
| 8118 | } | ||
| 8119 | |||
| 8120 | |||
| 8121 | /* | ||
| 8122 | If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode | ||
| 8123 | the first frame. | ||
| 8124 | */ | ||
| 8125 | if (!init.hasStreamInfoBlock) { | ||
| 8126 | pFlac->currentFLACFrame.header = init.firstFrameHeader; | ||
| 8127 | for (;;) { | ||
| 8128 | drflac_result result = drflac__decode_flac_frame(pFlac); | ||
| 8129 | if (result == DRFLAC_SUCCESS) { | ||
| 8130 | break; | ||
| 8131 | } else { | ||
| 8132 | if (result == DRFLAC_CRC_MISMATCH) { | ||
| 8133 | if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { | ||
| 8134 | drflac__free_from_callbacks(pFlac, &allocationCallbacks); | ||
| 8135 | return NULL; | ||
| 8136 | } | ||
| 8137 | continue; | ||
| 8138 | } else { | ||
| 8139 | drflac__free_from_callbacks(pFlac, &allocationCallbacks); | ||
| 8140 | return NULL; | ||
| 8141 | } | ||
| 8142 | } | ||
| 8143 | } | ||
| 8144 | } | ||
| 8145 | |||
| 8146 | return pFlac; | ||
| 8147 | } | ||
| 8148 | |||
| 8149 | |||
| 8150 | |||
| 8151 | #ifndef DR_FLAC_NO_STDIO | ||
| 8152 | #include <stdio.h> | ||
| 8153 | #ifndef DR_FLAC_NO_WCHAR | ||
| 8154 | #include <wchar.h> /* For wcslen(), wcsrtombs() */ | ||
| 8155 | #endif | ||
| 8156 | |||
| 8157 | /* Errno */ | ||
| 8158 | /* drflac_result_from_errno() is only used for fopen() and wfopen() so putting it inside DR_WAV_NO_STDIO for now. If something else needs this later we can move it out. */ | ||
| 8159 | #include <errno.h> | ||
| 8160 | static drflac_result drflac_result_from_errno(int e) | ||
| 8161 | { | ||
| 8162 | switch (e) | ||
| 8163 | { | ||
| 8164 | case 0: return DRFLAC_SUCCESS; | ||
| 8165 | #ifdef EPERM | ||
| 8166 | case EPERM: return DRFLAC_INVALID_OPERATION; | ||
| 8167 | #endif | ||
| 8168 | #ifdef ENOENT | ||
| 8169 | case ENOENT: return DRFLAC_DOES_NOT_EXIST; | ||
| 8170 | #endif | ||
| 8171 | #ifdef ESRCH | ||
| 8172 | case ESRCH: return DRFLAC_DOES_NOT_EXIST; | ||
| 8173 | #endif | ||
| 8174 | #ifdef EINTR | ||
| 8175 | case EINTR: return DRFLAC_INTERRUPT; | ||
| 8176 | #endif | ||
| 8177 | #ifdef EIO | ||
| 8178 | case EIO: return DRFLAC_IO_ERROR; | ||
| 8179 | #endif | ||
| 8180 | #ifdef ENXIO | ||
| 8181 | case ENXIO: return DRFLAC_DOES_NOT_EXIST; | ||
| 8182 | #endif | ||
| 8183 | #ifdef E2BIG | ||
| 8184 | case E2BIG: return DRFLAC_INVALID_ARGS; | ||
| 8185 | #endif | ||
| 8186 | #ifdef ENOEXEC | ||
| 8187 | case ENOEXEC: return DRFLAC_INVALID_FILE; | ||
| 8188 | #endif | ||
| 8189 | #ifdef EBADF | ||
| 8190 | case EBADF: return DRFLAC_INVALID_FILE; | ||
| 8191 | #endif | ||
| 8192 | #ifdef ECHILD | ||
| 8193 | case ECHILD: return DRFLAC_ERROR; | ||
| 8194 | #endif | ||
| 8195 | #ifdef EAGAIN | ||
| 8196 | case EAGAIN: return DRFLAC_UNAVAILABLE; | ||
| 8197 | #endif | ||
| 8198 | #ifdef ENOMEM | ||
| 8199 | case ENOMEM: return DRFLAC_OUT_OF_MEMORY; | ||
| 8200 | #endif | ||
| 8201 | #ifdef EACCES | ||
| 8202 | case EACCES: return DRFLAC_ACCESS_DENIED; | ||
| 8203 | #endif | ||
| 8204 | #ifdef EFAULT | ||
| 8205 | case EFAULT: return DRFLAC_BAD_ADDRESS; | ||
| 8206 | #endif | ||
| 8207 | #ifdef ENOTBLK | ||
| 8208 | case ENOTBLK: return DRFLAC_ERROR; | ||
| 8209 | #endif | ||
| 8210 | #ifdef EBUSY | ||
| 8211 | case EBUSY: return DRFLAC_BUSY; | ||
| 8212 | #endif | ||
| 8213 | #ifdef EEXIST | ||
| 8214 | case EEXIST: return DRFLAC_ALREADY_EXISTS; | ||
| 8215 | #endif | ||
| 8216 | #ifdef EXDEV | ||
| 8217 | case EXDEV: return DRFLAC_ERROR; | ||
| 8218 | #endif | ||
| 8219 | #ifdef ENODEV | ||
| 8220 | case ENODEV: return DRFLAC_DOES_NOT_EXIST; | ||
| 8221 | #endif | ||
| 8222 | #ifdef ENOTDIR | ||
| 8223 | case ENOTDIR: return DRFLAC_NOT_DIRECTORY; | ||
| 8224 | #endif | ||
| 8225 | #ifdef EISDIR | ||
| 8226 | case EISDIR: return DRFLAC_IS_DIRECTORY; | ||
| 8227 | #endif | ||
| 8228 | #ifdef EINVAL | ||
| 8229 | case EINVAL: return DRFLAC_INVALID_ARGS; | ||
| 8230 | #endif | ||
| 8231 | #ifdef ENFILE | ||
| 8232 | case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES; | ||
| 8233 | #endif | ||
| 8234 | #ifdef EMFILE | ||
| 8235 | case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES; | ||
| 8236 | #endif | ||
| 8237 | #ifdef ENOTTY | ||
| 8238 | case ENOTTY: return DRFLAC_INVALID_OPERATION; | ||
| 8239 | #endif | ||
| 8240 | #ifdef ETXTBSY | ||
| 8241 | case ETXTBSY: return DRFLAC_BUSY; | ||
| 8242 | #endif | ||
| 8243 | #ifdef EFBIG | ||
| 8244 | case EFBIG: return DRFLAC_TOO_BIG; | ||
| 8245 | #endif | ||
| 8246 | #ifdef ENOSPC | ||
| 8247 | case ENOSPC: return DRFLAC_NO_SPACE; | ||
| 8248 | #endif | ||
| 8249 | #ifdef ESPIPE | ||
| 8250 | case ESPIPE: return DRFLAC_BAD_SEEK; | ||
| 8251 | #endif | ||
| 8252 | #ifdef EROFS | ||
| 8253 | case EROFS: return DRFLAC_ACCESS_DENIED; | ||
| 8254 | #endif | ||
| 8255 | #ifdef EMLINK | ||
| 8256 | case EMLINK: return DRFLAC_TOO_MANY_LINKS; | ||
| 8257 | #endif | ||
| 8258 | #ifdef EPIPE | ||
| 8259 | case EPIPE: return DRFLAC_BAD_PIPE; | ||
| 8260 | #endif | ||
| 8261 | #ifdef EDOM | ||
| 8262 | case EDOM: return DRFLAC_OUT_OF_RANGE; | ||
| 8263 | #endif | ||
| 8264 | #ifdef ERANGE | ||
| 8265 | case ERANGE: return DRFLAC_OUT_OF_RANGE; | ||
| 8266 | #endif | ||
| 8267 | #ifdef EDEADLK | ||
| 8268 | case EDEADLK: return DRFLAC_DEADLOCK; | ||
| 8269 | #endif | ||
| 8270 | #ifdef ENAMETOOLONG | ||
| 8271 | case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG; | ||
| 8272 | #endif | ||
| 8273 | #ifdef ENOLCK | ||
| 8274 | case ENOLCK: return DRFLAC_ERROR; | ||
| 8275 | #endif | ||
| 8276 | #ifdef ENOSYS | ||
| 8277 | case ENOSYS: return DRFLAC_NOT_IMPLEMENTED; | ||
| 8278 | #endif | ||
| 8279 | #ifdef ENOTEMPTY | ||
| 8280 | case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY; | ||
| 8281 | #endif | ||
| 8282 | #ifdef ELOOP | ||
| 8283 | case ELOOP: return DRFLAC_TOO_MANY_LINKS; | ||
| 8284 | #endif | ||
| 8285 | #ifdef ENOMSG | ||
| 8286 | case ENOMSG: return DRFLAC_NO_MESSAGE; | ||
| 8287 | #endif | ||
| 8288 | #ifdef EIDRM | ||
| 8289 | case EIDRM: return DRFLAC_ERROR; | ||
| 8290 | #endif | ||
| 8291 | #ifdef ECHRNG | ||
| 8292 | case ECHRNG: return DRFLAC_ERROR; | ||
| 8293 | #endif | ||
| 8294 | #ifdef EL2NSYNC | ||
| 8295 | case EL2NSYNC: return DRFLAC_ERROR; | ||
| 8296 | #endif | ||
| 8297 | #ifdef EL3HLT | ||
| 8298 | case EL3HLT: return DRFLAC_ERROR; | ||
| 8299 | #endif | ||
| 8300 | #ifdef EL3RST | ||
| 8301 | case EL3RST: return DRFLAC_ERROR; | ||
| 8302 | #endif | ||
| 8303 | #ifdef ELNRNG | ||
| 8304 | case ELNRNG: return DRFLAC_OUT_OF_RANGE; | ||
| 8305 | #endif | ||
| 8306 | #ifdef EUNATCH | ||
| 8307 | case EUNATCH: return DRFLAC_ERROR; | ||
| 8308 | #endif | ||
| 8309 | #ifdef ENOCSI | ||
| 8310 | case ENOCSI: return DRFLAC_ERROR; | ||
| 8311 | #endif | ||
| 8312 | #ifdef EL2HLT | ||
| 8313 | case EL2HLT: return DRFLAC_ERROR; | ||
| 8314 | #endif | ||
| 8315 | #ifdef EBADE | ||
| 8316 | case EBADE: return DRFLAC_ERROR; | ||
| 8317 | #endif | ||
| 8318 | #ifdef EBADR | ||
| 8319 | case EBADR: return DRFLAC_ERROR; | ||
| 8320 | #endif | ||
| 8321 | #ifdef EXFULL | ||
| 8322 | case EXFULL: return DRFLAC_ERROR; | ||
| 8323 | #endif | ||
| 8324 | #ifdef ENOANO | ||
| 8325 | case ENOANO: return DRFLAC_ERROR; | ||
| 8326 | #endif | ||
| 8327 | #ifdef EBADRQC | ||
| 8328 | case EBADRQC: return DRFLAC_ERROR; | ||
| 8329 | #endif | ||
| 8330 | #ifdef EBADSLT | ||
| 8331 | case EBADSLT: return DRFLAC_ERROR; | ||
| 8332 | #endif | ||
| 8333 | #ifdef EBFONT | ||
| 8334 | case EBFONT: return DRFLAC_INVALID_FILE; | ||
| 8335 | #endif | ||
| 8336 | #ifdef ENOSTR | ||
| 8337 | case ENOSTR: return DRFLAC_ERROR; | ||
| 8338 | #endif | ||
| 8339 | #ifdef ENODATA | ||
| 8340 | case ENODATA: return DRFLAC_NO_DATA_AVAILABLE; | ||
| 8341 | #endif | ||
| 8342 | #ifdef ETIME | ||
| 8343 | case ETIME: return DRFLAC_TIMEOUT; | ||
| 8344 | #endif | ||
| 8345 | #ifdef ENOSR | ||
| 8346 | case ENOSR: return DRFLAC_NO_DATA_AVAILABLE; | ||
| 8347 | #endif | ||
| 8348 | #ifdef ENONET | ||
| 8349 | case ENONET: return DRFLAC_NO_NETWORK; | ||
| 8350 | #endif | ||
| 8351 | #ifdef ENOPKG | ||
| 8352 | case ENOPKG: return DRFLAC_ERROR; | ||
| 8353 | #endif | ||
| 8354 | #ifdef EREMOTE | ||
| 8355 | case EREMOTE: return DRFLAC_ERROR; | ||
| 8356 | #endif | ||
| 8357 | #ifdef ENOLINK | ||
| 8358 | case ENOLINK: return DRFLAC_ERROR; | ||
| 8359 | #endif | ||
| 8360 | #ifdef EADV | ||
| 8361 | case EADV: return DRFLAC_ERROR; | ||
| 8362 | #endif | ||
| 8363 | #ifdef ESRMNT | ||
| 8364 | case ESRMNT: return DRFLAC_ERROR; | ||
| 8365 | #endif | ||
| 8366 | #ifdef ECOMM | ||
| 8367 | case ECOMM: return DRFLAC_ERROR; | ||
| 8368 | #endif | ||
| 8369 | #ifdef EPROTO | ||
| 8370 | case EPROTO: return DRFLAC_ERROR; | ||
| 8371 | #endif | ||
| 8372 | #ifdef EMULTIHOP | ||
| 8373 | case EMULTIHOP: return DRFLAC_ERROR; | ||
| 8374 | #endif | ||
| 8375 | #ifdef EDOTDOT | ||
| 8376 | case EDOTDOT: return DRFLAC_ERROR; | ||
| 8377 | #endif | ||
| 8378 | #ifdef EBADMSG | ||
| 8379 | case EBADMSG: return DRFLAC_BAD_MESSAGE; | ||
| 8380 | #endif | ||
| 8381 | #ifdef EOVERFLOW | ||
| 8382 | case EOVERFLOW: return DRFLAC_TOO_BIG; | ||
| 8383 | #endif | ||
| 8384 | #ifdef ENOTUNIQ | ||
| 8385 | case ENOTUNIQ: return DRFLAC_NOT_UNIQUE; | ||
| 8386 | #endif | ||
| 8387 | #ifdef EBADFD | ||
| 8388 | case EBADFD: return DRFLAC_ERROR; | ||
| 8389 | #endif | ||
| 8390 | #ifdef EREMCHG | ||
| 8391 | case EREMCHG: return DRFLAC_ERROR; | ||
| 8392 | #endif | ||
| 8393 | #ifdef ELIBACC | ||
| 8394 | case ELIBACC: return DRFLAC_ACCESS_DENIED; | ||
| 8395 | #endif | ||
| 8396 | #ifdef ELIBBAD | ||
| 8397 | case ELIBBAD: return DRFLAC_INVALID_FILE; | ||
| 8398 | #endif | ||
| 8399 | #ifdef ELIBSCN | ||
| 8400 | case ELIBSCN: return DRFLAC_INVALID_FILE; | ||
| 8401 | #endif | ||
| 8402 | #ifdef ELIBMAX | ||
| 8403 | case ELIBMAX: return DRFLAC_ERROR; | ||
| 8404 | #endif | ||
| 8405 | #ifdef ELIBEXEC | ||
| 8406 | case ELIBEXEC: return DRFLAC_ERROR; | ||
| 8407 | #endif | ||
| 8408 | #ifdef EILSEQ | ||
| 8409 | case EILSEQ: return DRFLAC_INVALID_DATA; | ||
| 8410 | #endif | ||
| 8411 | #ifdef ERESTART | ||
| 8412 | case ERESTART: return DRFLAC_ERROR; | ||
| 8413 | #endif | ||
| 8414 | #ifdef ESTRPIPE | ||
| 8415 | case ESTRPIPE: return DRFLAC_ERROR; | ||
| 8416 | #endif | ||
| 8417 | #ifdef EUSERS | ||
| 8418 | case EUSERS: return DRFLAC_ERROR; | ||
| 8419 | #endif | ||
| 8420 | #ifdef ENOTSOCK | ||
| 8421 | case ENOTSOCK: return DRFLAC_NOT_SOCKET; | ||
| 8422 | #endif | ||
| 8423 | #ifdef EDESTADDRREQ | ||
| 8424 | case EDESTADDRREQ: return DRFLAC_NO_ADDRESS; | ||
| 8425 | #endif | ||
| 8426 | #ifdef EMSGSIZE | ||
| 8427 | case EMSGSIZE: return DRFLAC_TOO_BIG; | ||
| 8428 | #endif | ||
| 8429 | #ifdef EPROTOTYPE | ||
| 8430 | case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL; | ||
| 8431 | #endif | ||
| 8432 | #ifdef ENOPROTOOPT | ||
| 8433 | case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE; | ||
| 8434 | #endif | ||
| 8435 | #ifdef EPROTONOSUPPORT | ||
| 8436 | case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED; | ||
| 8437 | #endif | ||
| 8438 | #ifdef ESOCKTNOSUPPORT | ||
| 8439 | case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED; | ||
| 8440 | #endif | ||
| 8441 | #ifdef EOPNOTSUPP | ||
| 8442 | case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION; | ||
| 8443 | #endif | ||
| 8444 | #ifdef EPFNOSUPPORT | ||
| 8445 | case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED; | ||
| 8446 | #endif | ||
| 8447 | #ifdef EAFNOSUPPORT | ||
| 8448 | case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED; | ||
| 8449 | #endif | ||
| 8450 | #ifdef EADDRINUSE | ||
| 8451 | case EADDRINUSE: return DRFLAC_ALREADY_IN_USE; | ||
| 8452 | #endif | ||
| 8453 | #ifdef EADDRNOTAVAIL | ||
| 8454 | case EADDRNOTAVAIL: return DRFLAC_ERROR; | ||
| 8455 | #endif | ||
| 8456 | #ifdef ENETDOWN | ||
| 8457 | case ENETDOWN: return DRFLAC_NO_NETWORK; | ||
| 8458 | #endif | ||
| 8459 | #ifdef ENETUNREACH | ||
| 8460 | case ENETUNREACH: return DRFLAC_NO_NETWORK; | ||
| 8461 | #endif | ||
| 8462 | #ifdef ENETRESET | ||
| 8463 | case ENETRESET: return DRFLAC_NO_NETWORK; | ||
| 8464 | #endif | ||
| 8465 | #ifdef ECONNABORTED | ||
| 8466 | case ECONNABORTED: return DRFLAC_NO_NETWORK; | ||
| 8467 | #endif | ||
| 8468 | #ifdef ECONNRESET | ||
| 8469 | case ECONNRESET: return DRFLAC_CONNECTION_RESET; | ||
| 8470 | #endif | ||
| 8471 | #ifdef ENOBUFS | ||
| 8472 | case ENOBUFS: return DRFLAC_NO_SPACE; | ||
| 8473 | #endif | ||
| 8474 | #ifdef EISCONN | ||
| 8475 | case EISCONN: return DRFLAC_ALREADY_CONNECTED; | ||
| 8476 | #endif | ||
| 8477 | #ifdef ENOTCONN | ||
| 8478 | case ENOTCONN: return DRFLAC_NOT_CONNECTED; | ||
| 8479 | #endif | ||
| 8480 | #ifdef ESHUTDOWN | ||
| 8481 | case ESHUTDOWN: return DRFLAC_ERROR; | ||
| 8482 | #endif | ||
| 8483 | #ifdef ETOOMANYREFS | ||
| 8484 | case ETOOMANYREFS: return DRFLAC_ERROR; | ||
| 8485 | #endif | ||
| 8486 | #ifdef ETIMEDOUT | ||
| 8487 | case ETIMEDOUT: return DRFLAC_TIMEOUT; | ||
| 8488 | #endif | ||
| 8489 | #ifdef ECONNREFUSED | ||
| 8490 | case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED; | ||
| 8491 | #endif | ||
| 8492 | #ifdef EHOSTDOWN | ||
| 8493 | case EHOSTDOWN: return DRFLAC_NO_HOST; | ||
| 8494 | #endif | ||
| 8495 | #ifdef EHOSTUNREACH | ||
| 8496 | case EHOSTUNREACH: return DRFLAC_NO_HOST; | ||
| 8497 | #endif | ||
| 8498 | #ifdef EALREADY | ||
| 8499 | case EALREADY: return DRFLAC_IN_PROGRESS; | ||
| 8500 | #endif | ||
| 8501 | #ifdef EINPROGRESS | ||
| 8502 | case EINPROGRESS: return DRFLAC_IN_PROGRESS; | ||
| 8503 | #endif | ||
| 8504 | #ifdef ESTALE | ||
| 8505 | case ESTALE: return DRFLAC_INVALID_FILE; | ||
| 8506 | #endif | ||
| 8507 | #ifdef EUCLEAN | ||
| 8508 | case EUCLEAN: return DRFLAC_ERROR; | ||
| 8509 | #endif | ||
| 8510 | #ifdef ENOTNAM | ||
| 8511 | case ENOTNAM: return DRFLAC_ERROR; | ||
| 8512 | #endif | ||
| 8513 | #ifdef ENAVAIL | ||
| 8514 | case ENAVAIL: return DRFLAC_ERROR; | ||
| 8515 | #endif | ||
| 8516 | #ifdef EISNAM | ||
| 8517 | case EISNAM: return DRFLAC_ERROR; | ||
| 8518 | #endif | ||
| 8519 | #ifdef EREMOTEIO | ||
| 8520 | case EREMOTEIO: return DRFLAC_IO_ERROR; | ||
| 8521 | #endif | ||
| 8522 | #ifdef EDQUOT | ||
| 8523 | case EDQUOT: return DRFLAC_NO_SPACE; | ||
| 8524 | #endif | ||
| 8525 | #ifdef ENOMEDIUM | ||
| 8526 | case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST; | ||
| 8527 | #endif | ||
| 8528 | #ifdef EMEDIUMTYPE | ||
| 8529 | case EMEDIUMTYPE: return DRFLAC_ERROR; | ||
| 8530 | #endif | ||
| 8531 | #ifdef ECANCELED | ||
| 8532 | case ECANCELED: return DRFLAC_CANCELLED; | ||
| 8533 | #endif | ||
| 8534 | #ifdef ENOKEY | ||
| 8535 | case ENOKEY: return DRFLAC_ERROR; | ||
| 8536 | #endif | ||
| 8537 | #ifdef EKEYEXPIRED | ||
| 8538 | case EKEYEXPIRED: return DRFLAC_ERROR; | ||
| 8539 | #endif | ||
| 8540 | #ifdef EKEYREVOKED | ||
| 8541 | case EKEYREVOKED: return DRFLAC_ERROR; | ||
| 8542 | #endif | ||
| 8543 | #ifdef EKEYREJECTED | ||
| 8544 | case EKEYREJECTED: return DRFLAC_ERROR; | ||
| 8545 | #endif | ||
| 8546 | #ifdef EOWNERDEAD | ||
| 8547 | case EOWNERDEAD: return DRFLAC_ERROR; | ||
| 8548 | #endif | ||
| 8549 | #ifdef ENOTRECOVERABLE | ||
| 8550 | case ENOTRECOVERABLE: return DRFLAC_ERROR; | ||
| 8551 | #endif | ||
| 8552 | #ifdef ERFKILL | ||
| 8553 | case ERFKILL: return DRFLAC_ERROR; | ||
| 8554 | #endif | ||
| 8555 | #ifdef EHWPOISON | ||
| 8556 | case EHWPOISON: return DRFLAC_ERROR; | ||
| 8557 | #endif | ||
| 8558 | default: return DRFLAC_ERROR; | ||
| 8559 | } | ||
| 8560 | } | ||
| 8561 | /* End Errno */ | ||
| 8562 | |||
| 8563 | /* fopen */ | ||
| 8564 | static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) | ||
| 8565 | { | ||
| 8566 | #if defined(_MSC_VER) && _MSC_VER >= 1400 | ||
| 8567 | errno_t err; | ||
| 8568 | #endif | ||
| 8569 | |||
| 8570 | if (ppFile != NULL) { | ||
| 8571 | *ppFile = NULL; /* Safety. */ | ||
| 8572 | } | ||
| 8573 | |||
| 8574 | if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { | ||
| 8575 | return DRFLAC_INVALID_ARGS; | ||
| 8576 | } | ||
| 8577 | |||
| 8578 | #if defined(_MSC_VER) && _MSC_VER >= 1400 | ||
| 8579 | err = fopen_s(ppFile, pFilePath, pOpenMode); | ||
| 8580 | if (err != 0) { | ||
| 8581 | return drflac_result_from_errno(err); | ||
| 8582 | } | ||
| 8583 | #else | ||
| 8584 | #if defined(_WIN32) || defined(__APPLE__) | ||
| 8585 | *ppFile = fopen(pFilePath, pOpenMode); | ||
| 8586 | #else | ||
| 8587 | #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) | ||
| 8588 | *ppFile = fopen64(pFilePath, pOpenMode); | ||
| 8589 | #else | ||
| 8590 | *ppFile = fopen(pFilePath, pOpenMode); | ||
| 8591 | #endif | ||
| 8592 | #endif | ||
| 8593 | if (*ppFile == NULL) { | ||
| 8594 | drflac_result result = drflac_result_from_errno(errno); | ||
| 8595 | if (result == DRFLAC_SUCCESS) { | ||
| 8596 | result = DRFLAC_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ | ||
| 8597 | } | ||
| 8598 | |||
| 8599 | return result; | ||
| 8600 | } | ||
| 8601 | #endif | ||
| 8602 | |||
| 8603 | return DRFLAC_SUCCESS; | ||
| 8604 | } | ||
| 8605 | |||
| 8606 | /* | ||
| 8607 | _wfopen() isn't always available in all compilation environments. | ||
| 8608 | |||
| 8609 | * Windows only. | ||
| 8610 | * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). | ||
| 8611 | * MinGW-64 (both 32- and 64-bit) seems to support it. | ||
| 8612 | * MinGW wraps it in !defined(__STRICT_ANSI__). | ||
| 8613 | * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). | ||
| 8614 | |||
| 8615 | This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() | ||
| 8616 | fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. | ||
| 8617 | */ | ||
| 8618 | #if defined(_WIN32) | ||
| 8619 | #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) | ||
| 8620 | #define DRFLAC_HAS_WFOPEN | ||
| 8621 | #endif | ||
| 8622 | #endif | ||
| 8623 | |||
| 8624 | #ifndef DR_FLAC_NO_WCHAR | ||
| 8625 | static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 8626 | { | ||
| 8627 | if (ppFile != NULL) { | ||
| 8628 | *ppFile = NULL; /* Safety. */ | ||
| 8629 | } | ||
| 8630 | |||
| 8631 | if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { | ||
| 8632 | return DRFLAC_INVALID_ARGS; | ||
| 8633 | } | ||
| 8634 | |||
| 8635 | #if defined(DRFLAC_HAS_WFOPEN) | ||
| 8636 | { | ||
| 8637 | /* Use _wfopen() on Windows. */ | ||
| 8638 | #if defined(_MSC_VER) && _MSC_VER >= 1400 | ||
| 8639 | errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); | ||
| 8640 | if (err != 0) { | ||
| 8641 | return drflac_result_from_errno(err); | ||
| 8642 | } | ||
| 8643 | #else | ||
| 8644 | *ppFile = _wfopen(pFilePath, pOpenMode); | ||
| 8645 | if (*ppFile == NULL) { | ||
| 8646 | return drflac_result_from_errno(errno); | ||
| 8647 | } | ||
| 8648 | #endif | ||
| 8649 | (void)pAllocationCallbacks; | ||
| 8650 | } | ||
| 8651 | #else | ||
| 8652 | /* | ||
| 8653 | Use fopen() on anything other than Windows. Requires a conversion. This is annoying because | ||
| 8654 | fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note | ||
| 8655 | that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for | ||
| 8656 | maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler | ||
| 8657 | error I'll look into improving compatibility. | ||
| 8658 | */ | ||
| 8659 | |||
| 8660 | /* | ||
| 8661 | Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just | ||
| 8662 | need to abort with an error. If you encounter a compiler lacking such support, add it to this list | ||
| 8663 | and submit a bug report and it'll be added to the library upstream. | ||
| 8664 | */ | ||
| 8665 | #if defined(__DJGPP__) | ||
| 8666 | { | ||
| 8667 | /* Nothing to do here. This will fall through to the error check below. */ | ||
| 8668 | } | ||
| 8669 | #else | ||
| 8670 | { | ||
| 8671 | mbstate_t mbs; | ||
| 8672 | size_t lenMB; | ||
| 8673 | const wchar_t* pFilePathTemp = pFilePath; | ||
| 8674 | char* pFilePathMB = NULL; | ||
| 8675 | char pOpenModeMB[32] = {0}; | ||
| 8676 | |||
| 8677 | /* Get the length first. */ | ||
| 8678 | DRFLAC_ZERO_OBJECT(&mbs); | ||
| 8679 | lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); | ||
| 8680 | if (lenMB == (size_t)-1) { | ||
| 8681 | return drflac_result_from_errno(errno); | ||
| 8682 | } | ||
| 8683 | |||
| 8684 | pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); | ||
| 8685 | if (pFilePathMB == NULL) { | ||
| 8686 | return DRFLAC_OUT_OF_MEMORY; | ||
| 8687 | } | ||
| 8688 | |||
| 8689 | pFilePathTemp = pFilePath; | ||
| 8690 | DRFLAC_ZERO_OBJECT(&mbs); | ||
| 8691 | wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); | ||
| 8692 | |||
| 8693 | /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ | ||
| 8694 | { | ||
| 8695 | size_t i = 0; | ||
| 8696 | for (;;) { | ||
| 8697 | if (pOpenMode[i] == 0) { | ||
| 8698 | pOpenModeMB[i] = '\0'; | ||
| 8699 | break; | ||
| 8700 | } | ||
| 8701 | |||
| 8702 | pOpenModeMB[i] = (char)pOpenMode[i]; | ||
| 8703 | i += 1; | ||
| 8704 | } | ||
| 8705 | } | ||
| 8706 | |||
| 8707 | *ppFile = fopen(pFilePathMB, pOpenModeMB); | ||
| 8708 | |||
| 8709 | drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); | ||
| 8710 | } | ||
| 8711 | #endif | ||
| 8712 | |||
| 8713 | if (*ppFile == NULL) { | ||
| 8714 | return DRFLAC_ERROR; | ||
| 8715 | } | ||
| 8716 | #endif | ||
| 8717 | |||
| 8718 | return DRFLAC_SUCCESS; | ||
| 8719 | } | ||
| 8720 | #endif | ||
| 8721 | /* End fopen */ | ||
| 8722 | |||
| 8723 | static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) | ||
| 8724 | { | ||
| 8725 | return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); | ||
| 8726 | } | ||
| 8727 | |||
| 8728 | static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) | ||
| 8729 | { | ||
| 8730 | DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ | ||
| 8731 | |||
| 8732 | return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; | ||
| 8733 | } | ||
| 8734 | |||
| 8735 | |||
| 8736 | DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 8737 | { | ||
| 8738 | drflac* pFlac; | ||
| 8739 | FILE* pFile; | ||
| 8740 | |||
| 8741 | if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { | ||
| 8742 | return NULL; | ||
| 8743 | } | ||
| 8744 | |||
| 8745 | pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); | ||
| 8746 | if (pFlac == NULL) { | ||
| 8747 | fclose(pFile); | ||
| 8748 | return NULL; | ||
| 8749 | } | ||
| 8750 | |||
| 8751 | return pFlac; | ||
| 8752 | } | ||
| 8753 | |||
| 8754 | #ifndef DR_FLAC_NO_WCHAR | ||
| 8755 | DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 8756 | { | ||
| 8757 | drflac* pFlac; | ||
| 8758 | FILE* pFile; | ||
| 8759 | |||
| 8760 | if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { | ||
| 8761 | return NULL; | ||
| 8762 | } | ||
| 8763 | |||
| 8764 | pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); | ||
| 8765 | if (pFlac == NULL) { | ||
| 8766 | fclose(pFile); | ||
| 8767 | return NULL; | ||
| 8768 | } | ||
| 8769 | |||
| 8770 | return pFlac; | ||
| 8771 | } | ||
| 8772 | #endif | ||
| 8773 | |||
| 8774 | DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 8775 | { | ||
| 8776 | drflac* pFlac; | ||
| 8777 | FILE* pFile; | ||
| 8778 | |||
| 8779 | if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { | ||
| 8780 | return NULL; | ||
| 8781 | } | ||
| 8782 | |||
| 8783 | pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); | ||
| 8784 | if (pFlac == NULL) { | ||
| 8785 | fclose(pFile); | ||
| 8786 | return pFlac; | ||
| 8787 | } | ||
| 8788 | |||
| 8789 | return pFlac; | ||
| 8790 | } | ||
| 8791 | |||
| 8792 | #ifndef DR_FLAC_NO_WCHAR | ||
| 8793 | DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 8794 | { | ||
| 8795 | drflac* pFlac; | ||
| 8796 | FILE* pFile; | ||
| 8797 | |||
| 8798 | if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { | ||
| 8799 | return NULL; | ||
| 8800 | } | ||
| 8801 | |||
| 8802 | pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); | ||
| 8803 | if (pFlac == NULL) { | ||
| 8804 | fclose(pFile); | ||
| 8805 | return pFlac; | ||
| 8806 | } | ||
| 8807 | |||
| 8808 | return pFlac; | ||
| 8809 | } | ||
| 8810 | #endif | ||
| 8811 | #endif /* DR_FLAC_NO_STDIO */ | ||
| 8812 | |||
| 8813 | static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) | ||
| 8814 | { | ||
| 8815 | drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; | ||
| 8816 | size_t bytesRemaining; | ||
| 8817 | |||
| 8818 | DRFLAC_ASSERT(memoryStream != NULL); | ||
| 8819 | DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); | ||
| 8820 | |||
| 8821 | bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; | ||
| 8822 | if (bytesToRead > bytesRemaining) { | ||
| 8823 | bytesToRead = bytesRemaining; | ||
| 8824 | } | ||
| 8825 | |||
| 8826 | if (bytesToRead > 0) { | ||
| 8827 | DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); | ||
| 8828 | memoryStream->currentReadPos += bytesToRead; | ||
| 8829 | } | ||
| 8830 | |||
| 8831 | return bytesToRead; | ||
| 8832 | } | ||
| 8833 | |||
| 8834 | static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) | ||
| 8835 | { | ||
| 8836 | drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; | ||
| 8837 | |||
| 8838 | DRFLAC_ASSERT(memoryStream != NULL); | ||
| 8839 | DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ | ||
| 8840 | |||
| 8841 | if (offset > (drflac_int64)memoryStream->dataSize) { | ||
| 8842 | return DRFLAC_FALSE; | ||
| 8843 | } | ||
| 8844 | |||
| 8845 | if (origin == drflac_seek_origin_current) { | ||
| 8846 | if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { | ||
| 8847 | memoryStream->currentReadPos += offset; | ||
| 8848 | } else { | ||
| 8849 | return DRFLAC_FALSE; /* Trying to seek too far forward. */ | ||
| 8850 | } | ||
| 8851 | } else { | ||
| 8852 | if ((drflac_uint32)offset <= memoryStream->dataSize) { | ||
| 8853 | memoryStream->currentReadPos = offset; | ||
| 8854 | } else { | ||
| 8855 | return DRFLAC_FALSE; /* Trying to seek too far forward. */ | ||
| 8856 | } | ||
| 8857 | } | ||
| 8858 | |||
| 8859 | return DRFLAC_TRUE; | ||
| 8860 | } | ||
| 8861 | |||
| 8862 | DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 8863 | { | ||
| 8864 | drflac__memory_stream memoryStream; | ||
| 8865 | drflac* pFlac; | ||
| 8866 | |||
| 8867 | memoryStream.data = (const drflac_uint8*)pData; | ||
| 8868 | memoryStream.dataSize = dataSize; | ||
| 8869 | memoryStream.currentReadPos = 0; | ||
| 8870 | pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks); | ||
| 8871 | if (pFlac == NULL) { | ||
| 8872 | return NULL; | ||
| 8873 | } | ||
| 8874 | |||
| 8875 | pFlac->memoryStream = memoryStream; | ||
| 8876 | |||
| 8877 | /* This is an awful hack... */ | ||
| 8878 | #ifndef DR_FLAC_NO_OGG | ||
| 8879 | if (pFlac->container == drflac_container_ogg) | ||
| 8880 | { | ||
| 8881 | drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; | ||
| 8882 | oggbs->pUserData = &pFlac->memoryStream; | ||
| 8883 | } | ||
| 8884 | else | ||
| 8885 | #endif | ||
| 8886 | { | ||
| 8887 | pFlac->bs.pUserData = &pFlac->memoryStream; | ||
| 8888 | } | ||
| 8889 | |||
| 8890 | return pFlac; | ||
| 8891 | } | ||
| 8892 | |||
| 8893 | DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 8894 | { | ||
| 8895 | drflac__memory_stream memoryStream; | ||
| 8896 | drflac* pFlac; | ||
| 8897 | |||
| 8898 | memoryStream.data = (const drflac_uint8*)pData; | ||
| 8899 | memoryStream.dataSize = dataSize; | ||
| 8900 | memoryStream.currentReadPos = 0; | ||
| 8901 | pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); | ||
| 8902 | if (pFlac == NULL) { | ||
| 8903 | return NULL; | ||
| 8904 | } | ||
| 8905 | |||
| 8906 | pFlac->memoryStream = memoryStream; | ||
| 8907 | |||
| 8908 | /* This is an awful hack... */ | ||
| 8909 | #ifndef DR_FLAC_NO_OGG | ||
| 8910 | if (pFlac->container == drflac_container_ogg) | ||
| 8911 | { | ||
| 8912 | drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; | ||
| 8913 | oggbs->pUserData = &pFlac->memoryStream; | ||
| 8914 | } | ||
| 8915 | else | ||
| 8916 | #endif | ||
| 8917 | { | ||
| 8918 | pFlac->bs.pUserData = &pFlac->memoryStream; | ||
| 8919 | } | ||
| 8920 | |||
| 8921 | return pFlac; | ||
| 8922 | } | ||
| 8923 | |||
| 8924 | |||
| 8925 | |||
| 8926 | DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 8927 | { | ||
| 8928 | return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); | ||
| 8929 | } | ||
| 8930 | DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 8931 | { | ||
| 8932 | return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); | ||
| 8933 | } | ||
| 8934 | |||
| 8935 | DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 8936 | { | ||
| 8937 | return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); | ||
| 8938 | } | ||
| 8939 | DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 8940 | { | ||
| 8941 | return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); | ||
| 8942 | } | ||
| 8943 | |||
| 8944 | DRFLAC_API void drflac_close(drflac* pFlac) | ||
| 8945 | { | ||
| 8946 | if (pFlac == NULL) { | ||
| 8947 | return; | ||
| 8948 | } | ||
| 8949 | |||
| 8950 | #ifndef DR_FLAC_NO_STDIO | ||
| 8951 | /* | ||
| 8952 | If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file() | ||
| 8953 | was used by looking at the callbacks. | ||
| 8954 | */ | ||
| 8955 | if (pFlac->bs.onRead == drflac__on_read_stdio) { | ||
| 8956 | fclose((FILE*)pFlac->bs.pUserData); | ||
| 8957 | } | ||
| 8958 | |||
| 8959 | #ifndef DR_FLAC_NO_OGG | ||
| 8960 | /* Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained. */ | ||
| 8961 | if (pFlac->container == drflac_container_ogg) { | ||
| 8962 | drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; | ||
| 8963 | DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg); | ||
| 8964 | |||
| 8965 | if (oggbs->onRead == drflac__on_read_stdio) { | ||
| 8966 | fclose((FILE*)oggbs->pUserData); | ||
| 8967 | } | ||
| 8968 | } | ||
| 8969 | #endif | ||
| 8970 | #endif | ||
| 8971 | |||
| 8972 | drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); | ||
| 8973 | } | ||
| 8974 | |||
| 8975 | |||
| 8976 | #if 0 | ||
| 8977 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 8978 | { | ||
| 8979 | drflac_uint64 i; | ||
| 8980 | for (i = 0; i < frameCount; ++i) { | ||
| 8981 | drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 8982 | drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 8983 | drflac_uint32 right = left - side; | ||
| 8984 | |||
| 8985 | pOutputSamples[i*2+0] = (drflac_int32)left; | ||
| 8986 | pOutputSamples[i*2+1] = (drflac_int32)right; | ||
| 8987 | } | ||
| 8988 | } | ||
| 8989 | #endif | ||
| 8990 | |||
| 8991 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 8992 | { | ||
| 8993 | drflac_uint64 i; | ||
| 8994 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 8995 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 8996 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 8997 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 8998 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 8999 | |||
| 9000 | for (i = 0; i < frameCount4; ++i) { | ||
| 9001 | drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; | ||
| 9002 | drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; | ||
| 9003 | drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; | ||
| 9004 | drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; | ||
| 9005 | |||
| 9006 | drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; | ||
| 9007 | drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; | ||
| 9008 | drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; | ||
| 9009 | drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; | ||
| 9010 | |||
| 9011 | drflac_uint32 right0 = left0 - side0; | ||
| 9012 | drflac_uint32 right1 = left1 - side1; | ||
| 9013 | drflac_uint32 right2 = left2 - side2; | ||
| 9014 | drflac_uint32 right3 = left3 - side3; | ||
| 9015 | |||
| 9016 | pOutputSamples[i*8+0] = (drflac_int32)left0; | ||
| 9017 | pOutputSamples[i*8+1] = (drflac_int32)right0; | ||
| 9018 | pOutputSamples[i*8+2] = (drflac_int32)left1; | ||
| 9019 | pOutputSamples[i*8+3] = (drflac_int32)right1; | ||
| 9020 | pOutputSamples[i*8+4] = (drflac_int32)left2; | ||
| 9021 | pOutputSamples[i*8+5] = (drflac_int32)right2; | ||
| 9022 | pOutputSamples[i*8+6] = (drflac_int32)left3; | ||
| 9023 | pOutputSamples[i*8+7] = (drflac_int32)right3; | ||
| 9024 | } | ||
| 9025 | |||
| 9026 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9027 | drflac_uint32 left = pInputSamples0U32[i] << shift0; | ||
| 9028 | drflac_uint32 side = pInputSamples1U32[i] << shift1; | ||
| 9029 | drflac_uint32 right = left - side; | ||
| 9030 | |||
| 9031 | pOutputSamples[i*2+0] = (drflac_int32)left; | ||
| 9032 | pOutputSamples[i*2+1] = (drflac_int32)right; | ||
| 9033 | } | ||
| 9034 | } | ||
| 9035 | |||
| 9036 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 9037 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9038 | { | ||
| 9039 | drflac_uint64 i; | ||
| 9040 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9041 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9042 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9043 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9044 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9045 | |||
| 9046 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 9047 | |||
| 9048 | for (i = 0; i < frameCount4; ++i) { | ||
| 9049 | __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); | ||
| 9050 | __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); | ||
| 9051 | __m128i right = _mm_sub_epi32(left, side); | ||
| 9052 | |||
| 9053 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); | ||
| 9054 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); | ||
| 9055 | } | ||
| 9056 | |||
| 9057 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9058 | drflac_uint32 left = pInputSamples0U32[i] << shift0; | ||
| 9059 | drflac_uint32 side = pInputSamples1U32[i] << shift1; | ||
| 9060 | drflac_uint32 right = left - side; | ||
| 9061 | |||
| 9062 | pOutputSamples[i*2+0] = (drflac_int32)left; | ||
| 9063 | pOutputSamples[i*2+1] = (drflac_int32)right; | ||
| 9064 | } | ||
| 9065 | } | ||
| 9066 | #endif | ||
| 9067 | |||
| 9068 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 9069 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9070 | { | ||
| 9071 | drflac_uint64 i; | ||
| 9072 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9073 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9074 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9075 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9076 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9077 | int32x4_t shift0_4; | ||
| 9078 | int32x4_t shift1_4; | ||
| 9079 | |||
| 9080 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 9081 | |||
| 9082 | shift0_4 = vdupq_n_s32(shift0); | ||
| 9083 | shift1_4 = vdupq_n_s32(shift1); | ||
| 9084 | |||
| 9085 | for (i = 0; i < frameCount4; ++i) { | ||
| 9086 | uint32x4_t left; | ||
| 9087 | uint32x4_t side; | ||
| 9088 | uint32x4_t right; | ||
| 9089 | |||
| 9090 | left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); | ||
| 9091 | side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); | ||
| 9092 | right = vsubq_u32(left, side); | ||
| 9093 | |||
| 9094 | drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); | ||
| 9095 | } | ||
| 9096 | |||
| 9097 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9098 | drflac_uint32 left = pInputSamples0U32[i] << shift0; | ||
| 9099 | drflac_uint32 side = pInputSamples1U32[i] << shift1; | ||
| 9100 | drflac_uint32 right = left - side; | ||
| 9101 | |||
| 9102 | pOutputSamples[i*2+0] = (drflac_int32)left; | ||
| 9103 | pOutputSamples[i*2+1] = (drflac_int32)right; | ||
| 9104 | } | ||
| 9105 | } | ||
| 9106 | #endif | ||
| 9107 | |||
| 9108 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9109 | { | ||
| 9110 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 9111 | if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { | ||
| 9112 | drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9113 | } else | ||
| 9114 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 9115 | if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { | ||
| 9116 | drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9117 | } else | ||
| 9118 | #endif | ||
| 9119 | { | ||
| 9120 | /* Scalar fallback. */ | ||
| 9121 | #if 0 | ||
| 9122 | drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9123 | #else | ||
| 9124 | drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9125 | #endif | ||
| 9126 | } | ||
| 9127 | } | ||
| 9128 | |||
| 9129 | |||
| 9130 | #if 0 | ||
| 9131 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9132 | { | ||
| 9133 | drflac_uint64 i; | ||
| 9134 | for (i = 0; i < frameCount; ++i) { | ||
| 9135 | drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 9136 | drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 9137 | drflac_uint32 left = right + side; | ||
| 9138 | |||
| 9139 | pOutputSamples[i*2+0] = (drflac_int32)left; | ||
| 9140 | pOutputSamples[i*2+1] = (drflac_int32)right; | ||
| 9141 | } | ||
| 9142 | } | ||
| 9143 | #endif | ||
| 9144 | |||
| 9145 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9146 | { | ||
| 9147 | drflac_uint64 i; | ||
| 9148 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9149 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9150 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9151 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9152 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9153 | |||
| 9154 | for (i = 0; i < frameCount4; ++i) { | ||
| 9155 | drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; | ||
| 9156 | drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; | ||
| 9157 | drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; | ||
| 9158 | drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; | ||
| 9159 | |||
| 9160 | drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; | ||
| 9161 | drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; | ||
| 9162 | drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; | ||
| 9163 | drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; | ||
| 9164 | |||
| 9165 | drflac_uint32 left0 = right0 + side0; | ||
| 9166 | drflac_uint32 left1 = right1 + side1; | ||
| 9167 | drflac_uint32 left2 = right2 + side2; | ||
| 9168 | drflac_uint32 left3 = right3 + side3; | ||
| 9169 | |||
| 9170 | pOutputSamples[i*8+0] = (drflac_int32)left0; | ||
| 9171 | pOutputSamples[i*8+1] = (drflac_int32)right0; | ||
| 9172 | pOutputSamples[i*8+2] = (drflac_int32)left1; | ||
| 9173 | pOutputSamples[i*8+3] = (drflac_int32)right1; | ||
| 9174 | pOutputSamples[i*8+4] = (drflac_int32)left2; | ||
| 9175 | pOutputSamples[i*8+5] = (drflac_int32)right2; | ||
| 9176 | pOutputSamples[i*8+6] = (drflac_int32)left3; | ||
| 9177 | pOutputSamples[i*8+7] = (drflac_int32)right3; | ||
| 9178 | } | ||
| 9179 | |||
| 9180 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9181 | drflac_uint32 side = pInputSamples0U32[i] << shift0; | ||
| 9182 | drflac_uint32 right = pInputSamples1U32[i] << shift1; | ||
| 9183 | drflac_uint32 left = right + side; | ||
| 9184 | |||
| 9185 | pOutputSamples[i*2+0] = (drflac_int32)left; | ||
| 9186 | pOutputSamples[i*2+1] = (drflac_int32)right; | ||
| 9187 | } | ||
| 9188 | } | ||
| 9189 | |||
| 9190 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 9191 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9192 | { | ||
| 9193 | drflac_uint64 i; | ||
| 9194 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9195 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9196 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9197 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9198 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9199 | |||
| 9200 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 9201 | |||
| 9202 | for (i = 0; i < frameCount4; ++i) { | ||
| 9203 | __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); | ||
| 9204 | __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); | ||
| 9205 | __m128i left = _mm_add_epi32(right, side); | ||
| 9206 | |||
| 9207 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); | ||
| 9208 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); | ||
| 9209 | } | ||
| 9210 | |||
| 9211 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9212 | drflac_uint32 side = pInputSamples0U32[i] << shift0; | ||
| 9213 | drflac_uint32 right = pInputSamples1U32[i] << shift1; | ||
| 9214 | drflac_uint32 left = right + side; | ||
| 9215 | |||
| 9216 | pOutputSamples[i*2+0] = (drflac_int32)left; | ||
| 9217 | pOutputSamples[i*2+1] = (drflac_int32)right; | ||
| 9218 | } | ||
| 9219 | } | ||
| 9220 | #endif | ||
| 9221 | |||
| 9222 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 9223 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9224 | { | ||
| 9225 | drflac_uint64 i; | ||
| 9226 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9227 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9228 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9229 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9230 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9231 | int32x4_t shift0_4; | ||
| 9232 | int32x4_t shift1_4; | ||
| 9233 | |||
| 9234 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 9235 | |||
| 9236 | shift0_4 = vdupq_n_s32(shift0); | ||
| 9237 | shift1_4 = vdupq_n_s32(shift1); | ||
| 9238 | |||
| 9239 | for (i = 0; i < frameCount4; ++i) { | ||
| 9240 | uint32x4_t side; | ||
| 9241 | uint32x4_t right; | ||
| 9242 | uint32x4_t left; | ||
| 9243 | |||
| 9244 | side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); | ||
| 9245 | right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); | ||
| 9246 | left = vaddq_u32(right, side); | ||
| 9247 | |||
| 9248 | drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); | ||
| 9249 | } | ||
| 9250 | |||
| 9251 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9252 | drflac_uint32 side = pInputSamples0U32[i] << shift0; | ||
| 9253 | drflac_uint32 right = pInputSamples1U32[i] << shift1; | ||
| 9254 | drflac_uint32 left = right + side; | ||
| 9255 | |||
| 9256 | pOutputSamples[i*2+0] = (drflac_int32)left; | ||
| 9257 | pOutputSamples[i*2+1] = (drflac_int32)right; | ||
| 9258 | } | ||
| 9259 | } | ||
| 9260 | #endif | ||
| 9261 | |||
| 9262 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9263 | { | ||
| 9264 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 9265 | if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { | ||
| 9266 | drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9267 | } else | ||
| 9268 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 9269 | if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { | ||
| 9270 | drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9271 | } else | ||
| 9272 | #endif | ||
| 9273 | { | ||
| 9274 | /* Scalar fallback. */ | ||
| 9275 | #if 0 | ||
| 9276 | drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9277 | #else | ||
| 9278 | drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9279 | #endif | ||
| 9280 | } | ||
| 9281 | } | ||
| 9282 | |||
| 9283 | |||
| 9284 | #if 0 | ||
| 9285 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9286 | { | ||
| 9287 | for (drflac_uint64 i = 0; i < frameCount; ++i) { | ||
| 9288 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9289 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9290 | |||
| 9291 | mid = (mid << 1) | (side & 0x01); | ||
| 9292 | |||
| 9293 | pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); | ||
| 9294 | pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); | ||
| 9295 | } | ||
| 9296 | } | ||
| 9297 | #endif | ||
| 9298 | |||
| 9299 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9300 | { | ||
| 9301 | drflac_uint64 i; | ||
| 9302 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9303 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9304 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9305 | drflac_int32 shift = unusedBitsPerSample; | ||
| 9306 | |||
| 9307 | if (shift > 0) { | ||
| 9308 | shift -= 1; | ||
| 9309 | for (i = 0; i < frameCount4; ++i) { | ||
| 9310 | drflac_uint32 temp0L; | ||
| 9311 | drflac_uint32 temp1L; | ||
| 9312 | drflac_uint32 temp2L; | ||
| 9313 | drflac_uint32 temp3L; | ||
| 9314 | drflac_uint32 temp0R; | ||
| 9315 | drflac_uint32 temp1R; | ||
| 9316 | drflac_uint32 temp2R; | ||
| 9317 | drflac_uint32 temp3R; | ||
| 9318 | |||
| 9319 | drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9320 | drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9321 | drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9322 | drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9323 | |||
| 9324 | drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9325 | drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9326 | drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9327 | drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9328 | |||
| 9329 | mid0 = (mid0 << 1) | (side0 & 0x01); | ||
| 9330 | mid1 = (mid1 << 1) | (side1 & 0x01); | ||
| 9331 | mid2 = (mid2 << 1) | (side2 & 0x01); | ||
| 9332 | mid3 = (mid3 << 1) | (side3 & 0x01); | ||
| 9333 | |||
| 9334 | temp0L = (mid0 + side0) << shift; | ||
| 9335 | temp1L = (mid1 + side1) << shift; | ||
| 9336 | temp2L = (mid2 + side2) << shift; | ||
| 9337 | temp3L = (mid3 + side3) << shift; | ||
| 9338 | |||
| 9339 | temp0R = (mid0 - side0) << shift; | ||
| 9340 | temp1R = (mid1 - side1) << shift; | ||
| 9341 | temp2R = (mid2 - side2) << shift; | ||
| 9342 | temp3R = (mid3 - side3) << shift; | ||
| 9343 | |||
| 9344 | pOutputSamples[i*8+0] = (drflac_int32)temp0L; | ||
| 9345 | pOutputSamples[i*8+1] = (drflac_int32)temp0R; | ||
| 9346 | pOutputSamples[i*8+2] = (drflac_int32)temp1L; | ||
| 9347 | pOutputSamples[i*8+3] = (drflac_int32)temp1R; | ||
| 9348 | pOutputSamples[i*8+4] = (drflac_int32)temp2L; | ||
| 9349 | pOutputSamples[i*8+5] = (drflac_int32)temp2R; | ||
| 9350 | pOutputSamples[i*8+6] = (drflac_int32)temp3L; | ||
| 9351 | pOutputSamples[i*8+7] = (drflac_int32)temp3R; | ||
| 9352 | } | ||
| 9353 | } else { | ||
| 9354 | for (i = 0; i < frameCount4; ++i) { | ||
| 9355 | drflac_uint32 temp0L; | ||
| 9356 | drflac_uint32 temp1L; | ||
| 9357 | drflac_uint32 temp2L; | ||
| 9358 | drflac_uint32 temp3L; | ||
| 9359 | drflac_uint32 temp0R; | ||
| 9360 | drflac_uint32 temp1R; | ||
| 9361 | drflac_uint32 temp2R; | ||
| 9362 | drflac_uint32 temp3R; | ||
| 9363 | |||
| 9364 | drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9365 | drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9366 | drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9367 | drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9368 | |||
| 9369 | drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9370 | drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9371 | drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9372 | drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9373 | |||
| 9374 | mid0 = (mid0 << 1) | (side0 & 0x01); | ||
| 9375 | mid1 = (mid1 << 1) | (side1 & 0x01); | ||
| 9376 | mid2 = (mid2 << 1) | (side2 & 0x01); | ||
| 9377 | mid3 = (mid3 << 1) | (side3 & 0x01); | ||
| 9378 | |||
| 9379 | temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); | ||
| 9380 | temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); | ||
| 9381 | temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); | ||
| 9382 | temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); | ||
| 9383 | |||
| 9384 | temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); | ||
| 9385 | temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); | ||
| 9386 | temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); | ||
| 9387 | temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); | ||
| 9388 | |||
| 9389 | pOutputSamples[i*8+0] = (drflac_int32)temp0L; | ||
| 9390 | pOutputSamples[i*8+1] = (drflac_int32)temp0R; | ||
| 9391 | pOutputSamples[i*8+2] = (drflac_int32)temp1L; | ||
| 9392 | pOutputSamples[i*8+3] = (drflac_int32)temp1R; | ||
| 9393 | pOutputSamples[i*8+4] = (drflac_int32)temp2L; | ||
| 9394 | pOutputSamples[i*8+5] = (drflac_int32)temp2R; | ||
| 9395 | pOutputSamples[i*8+6] = (drflac_int32)temp3L; | ||
| 9396 | pOutputSamples[i*8+7] = (drflac_int32)temp3R; | ||
| 9397 | } | ||
| 9398 | } | ||
| 9399 | |||
| 9400 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9401 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9402 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9403 | |||
| 9404 | mid = (mid << 1) | (side & 0x01); | ||
| 9405 | |||
| 9406 | pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); | ||
| 9407 | pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); | ||
| 9408 | } | ||
| 9409 | } | ||
| 9410 | |||
| 9411 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 9412 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9413 | { | ||
| 9414 | drflac_uint64 i; | ||
| 9415 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9416 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9417 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9418 | drflac_int32 shift = unusedBitsPerSample; | ||
| 9419 | |||
| 9420 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 9421 | |||
| 9422 | if (shift == 0) { | ||
| 9423 | for (i = 0; i < frameCount4; ++i) { | ||
| 9424 | __m128i mid; | ||
| 9425 | __m128i side; | ||
| 9426 | __m128i left; | ||
| 9427 | __m128i right; | ||
| 9428 | |||
| 9429 | mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 9430 | side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 9431 | |||
| 9432 | mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); | ||
| 9433 | |||
| 9434 | left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); | ||
| 9435 | right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); | ||
| 9436 | |||
| 9437 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); | ||
| 9438 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); | ||
| 9439 | } | ||
| 9440 | |||
| 9441 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9442 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9443 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9444 | |||
| 9445 | mid = (mid << 1) | (side & 0x01); | ||
| 9446 | |||
| 9447 | pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; | ||
| 9448 | pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; | ||
| 9449 | } | ||
| 9450 | } else { | ||
| 9451 | shift -= 1; | ||
| 9452 | for (i = 0; i < frameCount4; ++i) { | ||
| 9453 | __m128i mid; | ||
| 9454 | __m128i side; | ||
| 9455 | __m128i left; | ||
| 9456 | __m128i right; | ||
| 9457 | |||
| 9458 | mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 9459 | side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 9460 | |||
| 9461 | mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); | ||
| 9462 | |||
| 9463 | left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); | ||
| 9464 | right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); | ||
| 9465 | |||
| 9466 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); | ||
| 9467 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); | ||
| 9468 | } | ||
| 9469 | |||
| 9470 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9471 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9472 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9473 | |||
| 9474 | mid = (mid << 1) | (side & 0x01); | ||
| 9475 | |||
| 9476 | pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); | ||
| 9477 | pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); | ||
| 9478 | } | ||
| 9479 | } | ||
| 9480 | } | ||
| 9481 | #endif | ||
| 9482 | |||
| 9483 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 9484 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9485 | { | ||
| 9486 | drflac_uint64 i; | ||
| 9487 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9488 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9489 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9490 | drflac_int32 shift = unusedBitsPerSample; | ||
| 9491 | int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */ | ||
| 9492 | int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */ | ||
| 9493 | uint32x4_t one4; | ||
| 9494 | |||
| 9495 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 9496 | |||
| 9497 | wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 9498 | wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 9499 | one4 = vdupq_n_u32(1); | ||
| 9500 | |||
| 9501 | if (shift == 0) { | ||
| 9502 | for (i = 0; i < frameCount4; ++i) { | ||
| 9503 | uint32x4_t mid; | ||
| 9504 | uint32x4_t side; | ||
| 9505 | int32x4_t left; | ||
| 9506 | int32x4_t right; | ||
| 9507 | |||
| 9508 | mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); | ||
| 9509 | side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); | ||
| 9510 | |||
| 9511 | mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); | ||
| 9512 | |||
| 9513 | left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); | ||
| 9514 | right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); | ||
| 9515 | |||
| 9516 | drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); | ||
| 9517 | } | ||
| 9518 | |||
| 9519 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9520 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9521 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9522 | |||
| 9523 | mid = (mid << 1) | (side & 0x01); | ||
| 9524 | |||
| 9525 | pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; | ||
| 9526 | pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; | ||
| 9527 | } | ||
| 9528 | } else { | ||
| 9529 | int32x4_t shift4; | ||
| 9530 | |||
| 9531 | shift -= 1; | ||
| 9532 | shift4 = vdupq_n_s32(shift); | ||
| 9533 | |||
| 9534 | for (i = 0; i < frameCount4; ++i) { | ||
| 9535 | uint32x4_t mid; | ||
| 9536 | uint32x4_t side; | ||
| 9537 | int32x4_t left; | ||
| 9538 | int32x4_t right; | ||
| 9539 | |||
| 9540 | mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); | ||
| 9541 | side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); | ||
| 9542 | |||
| 9543 | mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); | ||
| 9544 | |||
| 9545 | left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); | ||
| 9546 | right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); | ||
| 9547 | |||
| 9548 | drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); | ||
| 9549 | } | ||
| 9550 | |||
| 9551 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9552 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9553 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9554 | |||
| 9555 | mid = (mid << 1) | (side & 0x01); | ||
| 9556 | |||
| 9557 | pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); | ||
| 9558 | pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); | ||
| 9559 | } | ||
| 9560 | } | ||
| 9561 | } | ||
| 9562 | #endif | ||
| 9563 | |||
| 9564 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9565 | { | ||
| 9566 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 9567 | if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { | ||
| 9568 | drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9569 | } else | ||
| 9570 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 9571 | if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { | ||
| 9572 | drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9573 | } else | ||
| 9574 | #endif | ||
| 9575 | { | ||
| 9576 | /* Scalar fallback. */ | ||
| 9577 | #if 0 | ||
| 9578 | drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9579 | #else | ||
| 9580 | drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9581 | #endif | ||
| 9582 | } | ||
| 9583 | } | ||
| 9584 | |||
| 9585 | |||
| 9586 | #if 0 | ||
| 9587 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9588 | { | ||
| 9589 | for (drflac_uint64 i = 0; i < frameCount; ++i) { | ||
| 9590 | pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); | ||
| 9591 | pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); | ||
| 9592 | } | ||
| 9593 | } | ||
| 9594 | #endif | ||
| 9595 | |||
| 9596 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9597 | { | ||
| 9598 | drflac_uint64 i; | ||
| 9599 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9600 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9601 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9602 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9603 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9604 | |||
| 9605 | for (i = 0; i < frameCount4; ++i) { | ||
| 9606 | drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; | ||
| 9607 | drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; | ||
| 9608 | drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; | ||
| 9609 | drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; | ||
| 9610 | |||
| 9611 | drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; | ||
| 9612 | drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; | ||
| 9613 | drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; | ||
| 9614 | drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; | ||
| 9615 | |||
| 9616 | pOutputSamples[i*8+0] = (drflac_int32)tempL0; | ||
| 9617 | pOutputSamples[i*8+1] = (drflac_int32)tempR0; | ||
| 9618 | pOutputSamples[i*8+2] = (drflac_int32)tempL1; | ||
| 9619 | pOutputSamples[i*8+3] = (drflac_int32)tempR1; | ||
| 9620 | pOutputSamples[i*8+4] = (drflac_int32)tempL2; | ||
| 9621 | pOutputSamples[i*8+5] = (drflac_int32)tempR2; | ||
| 9622 | pOutputSamples[i*8+6] = (drflac_int32)tempL3; | ||
| 9623 | pOutputSamples[i*8+7] = (drflac_int32)tempR3; | ||
| 9624 | } | ||
| 9625 | |||
| 9626 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9627 | pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); | ||
| 9628 | pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); | ||
| 9629 | } | ||
| 9630 | } | ||
| 9631 | |||
| 9632 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 9633 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9634 | { | ||
| 9635 | drflac_uint64 i; | ||
| 9636 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9637 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9638 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9639 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9640 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9641 | |||
| 9642 | for (i = 0; i < frameCount4; ++i) { | ||
| 9643 | __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); | ||
| 9644 | __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); | ||
| 9645 | |||
| 9646 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); | ||
| 9647 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); | ||
| 9648 | } | ||
| 9649 | |||
| 9650 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9651 | pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); | ||
| 9652 | pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); | ||
| 9653 | } | ||
| 9654 | } | ||
| 9655 | #endif | ||
| 9656 | |||
| 9657 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 9658 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9659 | { | ||
| 9660 | drflac_uint64 i; | ||
| 9661 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9662 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9663 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9664 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9665 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9666 | |||
| 9667 | int32x4_t shift4_0 = vdupq_n_s32(shift0); | ||
| 9668 | int32x4_t shift4_1 = vdupq_n_s32(shift1); | ||
| 9669 | |||
| 9670 | for (i = 0; i < frameCount4; ++i) { | ||
| 9671 | int32x4_t left; | ||
| 9672 | int32x4_t right; | ||
| 9673 | |||
| 9674 | left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); | ||
| 9675 | right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); | ||
| 9676 | |||
| 9677 | drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); | ||
| 9678 | } | ||
| 9679 | |||
| 9680 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9681 | pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); | ||
| 9682 | pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); | ||
| 9683 | } | ||
| 9684 | } | ||
| 9685 | #endif | ||
| 9686 | |||
| 9687 | static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) | ||
| 9688 | { | ||
| 9689 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 9690 | if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { | ||
| 9691 | drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9692 | } else | ||
| 9693 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 9694 | if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { | ||
| 9695 | drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9696 | } else | ||
| 9697 | #endif | ||
| 9698 | { | ||
| 9699 | /* Scalar fallback. */ | ||
| 9700 | #if 0 | ||
| 9701 | drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9702 | #else | ||
| 9703 | drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9704 | #endif | ||
| 9705 | } | ||
| 9706 | } | ||
| 9707 | |||
| 9708 | |||
| 9709 | DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut) | ||
| 9710 | { | ||
| 9711 | drflac_uint64 framesRead; | ||
| 9712 | drflac_uint32 unusedBitsPerSample; | ||
| 9713 | |||
| 9714 | if (pFlac == NULL || framesToRead == 0) { | ||
| 9715 | return 0; | ||
| 9716 | } | ||
| 9717 | |||
| 9718 | if (pBufferOut == NULL) { | ||
| 9719 | return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); | ||
| 9720 | } | ||
| 9721 | |||
| 9722 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); | ||
| 9723 | unusedBitsPerSample = 32 - pFlac->bitsPerSample; | ||
| 9724 | |||
| 9725 | framesRead = 0; | ||
| 9726 | while (framesToRead > 0) { | ||
| 9727 | /* If we've run out of samples in this frame, go to the next. */ | ||
| 9728 | if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { | ||
| 9729 | if (!drflac__read_and_decode_next_flac_frame(pFlac)) { | ||
| 9730 | break; /* Couldn't read the next frame, so just break from the loop and return. */ | ||
| 9731 | } | ||
| 9732 | } else { | ||
| 9733 | unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); | ||
| 9734 | drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; | ||
| 9735 | drflac_uint64 frameCountThisIteration = framesToRead; | ||
| 9736 | |||
| 9737 | if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { | ||
| 9738 | frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; | ||
| 9739 | } | ||
| 9740 | |||
| 9741 | if (channelCount == 2) { | ||
| 9742 | const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; | ||
| 9743 | const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; | ||
| 9744 | |||
| 9745 | switch (pFlac->currentFLACFrame.header.channelAssignment) | ||
| 9746 | { | ||
| 9747 | case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: | ||
| 9748 | { | ||
| 9749 | drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); | ||
| 9750 | } break; | ||
| 9751 | |||
| 9752 | case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: | ||
| 9753 | { | ||
| 9754 | drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); | ||
| 9755 | } break; | ||
| 9756 | |||
| 9757 | case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: | ||
| 9758 | { | ||
| 9759 | drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); | ||
| 9760 | } break; | ||
| 9761 | |||
| 9762 | case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: | ||
| 9763 | default: | ||
| 9764 | { | ||
| 9765 | drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); | ||
| 9766 | } break; | ||
| 9767 | } | ||
| 9768 | } else { | ||
| 9769 | /* Generic interleaving. */ | ||
| 9770 | drflac_uint64 i; | ||
| 9771 | for (i = 0; i < frameCountThisIteration; ++i) { | ||
| 9772 | unsigned int j; | ||
| 9773 | for (j = 0; j < channelCount; ++j) { | ||
| 9774 | pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); | ||
| 9775 | } | ||
| 9776 | } | ||
| 9777 | } | ||
| 9778 | |||
| 9779 | framesRead += frameCountThisIteration; | ||
| 9780 | pBufferOut += frameCountThisIteration * channelCount; | ||
| 9781 | framesToRead -= frameCountThisIteration; | ||
| 9782 | pFlac->currentPCMFrame += frameCountThisIteration; | ||
| 9783 | pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; | ||
| 9784 | } | ||
| 9785 | } | ||
| 9786 | |||
| 9787 | return framesRead; | ||
| 9788 | } | ||
| 9789 | |||
| 9790 | |||
| 9791 | #if 0 | ||
| 9792 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 9793 | { | ||
| 9794 | drflac_uint64 i; | ||
| 9795 | for (i = 0; i < frameCount; ++i) { | ||
| 9796 | drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 9797 | drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 9798 | drflac_uint32 right = left - side; | ||
| 9799 | |||
| 9800 | left >>= 16; | ||
| 9801 | right >>= 16; | ||
| 9802 | |||
| 9803 | pOutputSamples[i*2+0] = (drflac_int16)left; | ||
| 9804 | pOutputSamples[i*2+1] = (drflac_int16)right; | ||
| 9805 | } | ||
| 9806 | } | ||
| 9807 | #endif | ||
| 9808 | |||
| 9809 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 9810 | { | ||
| 9811 | drflac_uint64 i; | ||
| 9812 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9813 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9814 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9815 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9816 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9817 | |||
| 9818 | for (i = 0; i < frameCount4; ++i) { | ||
| 9819 | drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; | ||
| 9820 | drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; | ||
| 9821 | drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; | ||
| 9822 | drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; | ||
| 9823 | |||
| 9824 | drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; | ||
| 9825 | drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; | ||
| 9826 | drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; | ||
| 9827 | drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; | ||
| 9828 | |||
| 9829 | drflac_uint32 right0 = left0 - side0; | ||
| 9830 | drflac_uint32 right1 = left1 - side1; | ||
| 9831 | drflac_uint32 right2 = left2 - side2; | ||
| 9832 | drflac_uint32 right3 = left3 - side3; | ||
| 9833 | |||
| 9834 | left0 >>= 16; | ||
| 9835 | left1 >>= 16; | ||
| 9836 | left2 >>= 16; | ||
| 9837 | left3 >>= 16; | ||
| 9838 | |||
| 9839 | right0 >>= 16; | ||
| 9840 | right1 >>= 16; | ||
| 9841 | right2 >>= 16; | ||
| 9842 | right3 >>= 16; | ||
| 9843 | |||
| 9844 | pOutputSamples[i*8+0] = (drflac_int16)left0; | ||
| 9845 | pOutputSamples[i*8+1] = (drflac_int16)right0; | ||
| 9846 | pOutputSamples[i*8+2] = (drflac_int16)left1; | ||
| 9847 | pOutputSamples[i*8+3] = (drflac_int16)right1; | ||
| 9848 | pOutputSamples[i*8+4] = (drflac_int16)left2; | ||
| 9849 | pOutputSamples[i*8+5] = (drflac_int16)right2; | ||
| 9850 | pOutputSamples[i*8+6] = (drflac_int16)left3; | ||
| 9851 | pOutputSamples[i*8+7] = (drflac_int16)right3; | ||
| 9852 | } | ||
| 9853 | |||
| 9854 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9855 | drflac_uint32 left = pInputSamples0U32[i] << shift0; | ||
| 9856 | drflac_uint32 side = pInputSamples1U32[i] << shift1; | ||
| 9857 | drflac_uint32 right = left - side; | ||
| 9858 | |||
| 9859 | left >>= 16; | ||
| 9860 | right >>= 16; | ||
| 9861 | |||
| 9862 | pOutputSamples[i*2+0] = (drflac_int16)left; | ||
| 9863 | pOutputSamples[i*2+1] = (drflac_int16)right; | ||
| 9864 | } | ||
| 9865 | } | ||
| 9866 | |||
| 9867 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 9868 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 9869 | { | ||
| 9870 | drflac_uint64 i; | ||
| 9871 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9872 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9873 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9874 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9875 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9876 | |||
| 9877 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 9878 | |||
| 9879 | for (i = 0; i < frameCount4; ++i) { | ||
| 9880 | __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); | ||
| 9881 | __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); | ||
| 9882 | __m128i right = _mm_sub_epi32(left, side); | ||
| 9883 | |||
| 9884 | left = _mm_srai_epi32(left, 16); | ||
| 9885 | right = _mm_srai_epi32(right, 16); | ||
| 9886 | |||
| 9887 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); | ||
| 9888 | } | ||
| 9889 | |||
| 9890 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9891 | drflac_uint32 left = pInputSamples0U32[i] << shift0; | ||
| 9892 | drflac_uint32 side = pInputSamples1U32[i] << shift1; | ||
| 9893 | drflac_uint32 right = left - side; | ||
| 9894 | |||
| 9895 | left >>= 16; | ||
| 9896 | right >>= 16; | ||
| 9897 | |||
| 9898 | pOutputSamples[i*2+0] = (drflac_int16)left; | ||
| 9899 | pOutputSamples[i*2+1] = (drflac_int16)right; | ||
| 9900 | } | ||
| 9901 | } | ||
| 9902 | #endif | ||
| 9903 | |||
| 9904 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 9905 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 9906 | { | ||
| 9907 | drflac_uint64 i; | ||
| 9908 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9909 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9910 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9911 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9912 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9913 | int32x4_t shift0_4; | ||
| 9914 | int32x4_t shift1_4; | ||
| 9915 | |||
| 9916 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 9917 | |||
| 9918 | shift0_4 = vdupq_n_s32(shift0); | ||
| 9919 | shift1_4 = vdupq_n_s32(shift1); | ||
| 9920 | |||
| 9921 | for (i = 0; i < frameCount4; ++i) { | ||
| 9922 | uint32x4_t left; | ||
| 9923 | uint32x4_t side; | ||
| 9924 | uint32x4_t right; | ||
| 9925 | |||
| 9926 | left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); | ||
| 9927 | side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); | ||
| 9928 | right = vsubq_u32(left, side); | ||
| 9929 | |||
| 9930 | left = vshrq_n_u32(left, 16); | ||
| 9931 | right = vshrq_n_u32(right, 16); | ||
| 9932 | |||
| 9933 | drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); | ||
| 9934 | } | ||
| 9935 | |||
| 9936 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 9937 | drflac_uint32 left = pInputSamples0U32[i] << shift0; | ||
| 9938 | drflac_uint32 side = pInputSamples1U32[i] << shift1; | ||
| 9939 | drflac_uint32 right = left - side; | ||
| 9940 | |||
| 9941 | left >>= 16; | ||
| 9942 | right >>= 16; | ||
| 9943 | |||
| 9944 | pOutputSamples[i*2+0] = (drflac_int16)left; | ||
| 9945 | pOutputSamples[i*2+1] = (drflac_int16)right; | ||
| 9946 | } | ||
| 9947 | } | ||
| 9948 | #endif | ||
| 9949 | |||
| 9950 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 9951 | { | ||
| 9952 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 9953 | if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { | ||
| 9954 | drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9955 | } else | ||
| 9956 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 9957 | if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { | ||
| 9958 | drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9959 | } else | ||
| 9960 | #endif | ||
| 9961 | { | ||
| 9962 | /* Scalar fallback. */ | ||
| 9963 | #if 0 | ||
| 9964 | drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9965 | #else | ||
| 9966 | drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 9967 | #endif | ||
| 9968 | } | ||
| 9969 | } | ||
| 9970 | |||
| 9971 | |||
| 9972 | #if 0 | ||
| 9973 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 9974 | { | ||
| 9975 | drflac_uint64 i; | ||
| 9976 | for (i = 0; i < frameCount; ++i) { | ||
| 9977 | drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 9978 | drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 9979 | drflac_uint32 left = right + side; | ||
| 9980 | |||
| 9981 | left >>= 16; | ||
| 9982 | right >>= 16; | ||
| 9983 | |||
| 9984 | pOutputSamples[i*2+0] = (drflac_int16)left; | ||
| 9985 | pOutputSamples[i*2+1] = (drflac_int16)right; | ||
| 9986 | } | ||
| 9987 | } | ||
| 9988 | #endif | ||
| 9989 | |||
| 9990 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 9991 | { | ||
| 9992 | drflac_uint64 i; | ||
| 9993 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 9994 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 9995 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 9996 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 9997 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 9998 | |||
| 9999 | for (i = 0; i < frameCount4; ++i) { | ||
| 10000 | drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; | ||
| 10001 | drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; | ||
| 10002 | drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; | ||
| 10003 | drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; | ||
| 10004 | |||
| 10005 | drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; | ||
| 10006 | drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; | ||
| 10007 | drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; | ||
| 10008 | drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; | ||
| 10009 | |||
| 10010 | drflac_uint32 left0 = right0 + side0; | ||
| 10011 | drflac_uint32 left1 = right1 + side1; | ||
| 10012 | drflac_uint32 left2 = right2 + side2; | ||
| 10013 | drflac_uint32 left3 = right3 + side3; | ||
| 10014 | |||
| 10015 | left0 >>= 16; | ||
| 10016 | left1 >>= 16; | ||
| 10017 | left2 >>= 16; | ||
| 10018 | left3 >>= 16; | ||
| 10019 | |||
| 10020 | right0 >>= 16; | ||
| 10021 | right1 >>= 16; | ||
| 10022 | right2 >>= 16; | ||
| 10023 | right3 >>= 16; | ||
| 10024 | |||
| 10025 | pOutputSamples[i*8+0] = (drflac_int16)left0; | ||
| 10026 | pOutputSamples[i*8+1] = (drflac_int16)right0; | ||
| 10027 | pOutputSamples[i*8+2] = (drflac_int16)left1; | ||
| 10028 | pOutputSamples[i*8+3] = (drflac_int16)right1; | ||
| 10029 | pOutputSamples[i*8+4] = (drflac_int16)left2; | ||
| 10030 | pOutputSamples[i*8+5] = (drflac_int16)right2; | ||
| 10031 | pOutputSamples[i*8+6] = (drflac_int16)left3; | ||
| 10032 | pOutputSamples[i*8+7] = (drflac_int16)right3; | ||
| 10033 | } | ||
| 10034 | |||
| 10035 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10036 | drflac_uint32 side = pInputSamples0U32[i] << shift0; | ||
| 10037 | drflac_uint32 right = pInputSamples1U32[i] << shift1; | ||
| 10038 | drflac_uint32 left = right + side; | ||
| 10039 | |||
| 10040 | left >>= 16; | ||
| 10041 | right >>= 16; | ||
| 10042 | |||
| 10043 | pOutputSamples[i*2+0] = (drflac_int16)left; | ||
| 10044 | pOutputSamples[i*2+1] = (drflac_int16)right; | ||
| 10045 | } | ||
| 10046 | } | ||
| 10047 | |||
| 10048 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 10049 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10050 | { | ||
| 10051 | drflac_uint64 i; | ||
| 10052 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10053 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10054 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10055 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10056 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10057 | |||
| 10058 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 10059 | |||
| 10060 | for (i = 0; i < frameCount4; ++i) { | ||
| 10061 | __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); | ||
| 10062 | __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); | ||
| 10063 | __m128i left = _mm_add_epi32(right, side); | ||
| 10064 | |||
| 10065 | left = _mm_srai_epi32(left, 16); | ||
| 10066 | right = _mm_srai_epi32(right, 16); | ||
| 10067 | |||
| 10068 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); | ||
| 10069 | } | ||
| 10070 | |||
| 10071 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10072 | drflac_uint32 side = pInputSamples0U32[i] << shift0; | ||
| 10073 | drflac_uint32 right = pInputSamples1U32[i] << shift1; | ||
| 10074 | drflac_uint32 left = right + side; | ||
| 10075 | |||
| 10076 | left >>= 16; | ||
| 10077 | right >>= 16; | ||
| 10078 | |||
| 10079 | pOutputSamples[i*2+0] = (drflac_int16)left; | ||
| 10080 | pOutputSamples[i*2+1] = (drflac_int16)right; | ||
| 10081 | } | ||
| 10082 | } | ||
| 10083 | #endif | ||
| 10084 | |||
| 10085 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 10086 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10087 | { | ||
| 10088 | drflac_uint64 i; | ||
| 10089 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10090 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10091 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10092 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10093 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10094 | int32x4_t shift0_4; | ||
| 10095 | int32x4_t shift1_4; | ||
| 10096 | |||
| 10097 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 10098 | |||
| 10099 | shift0_4 = vdupq_n_s32(shift0); | ||
| 10100 | shift1_4 = vdupq_n_s32(shift1); | ||
| 10101 | |||
| 10102 | for (i = 0; i < frameCount4; ++i) { | ||
| 10103 | uint32x4_t side; | ||
| 10104 | uint32x4_t right; | ||
| 10105 | uint32x4_t left; | ||
| 10106 | |||
| 10107 | side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); | ||
| 10108 | right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); | ||
| 10109 | left = vaddq_u32(right, side); | ||
| 10110 | |||
| 10111 | left = vshrq_n_u32(left, 16); | ||
| 10112 | right = vshrq_n_u32(right, 16); | ||
| 10113 | |||
| 10114 | drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); | ||
| 10115 | } | ||
| 10116 | |||
| 10117 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10118 | drflac_uint32 side = pInputSamples0U32[i] << shift0; | ||
| 10119 | drflac_uint32 right = pInputSamples1U32[i] << shift1; | ||
| 10120 | drflac_uint32 left = right + side; | ||
| 10121 | |||
| 10122 | left >>= 16; | ||
| 10123 | right >>= 16; | ||
| 10124 | |||
| 10125 | pOutputSamples[i*2+0] = (drflac_int16)left; | ||
| 10126 | pOutputSamples[i*2+1] = (drflac_int16)right; | ||
| 10127 | } | ||
| 10128 | } | ||
| 10129 | #endif | ||
| 10130 | |||
| 10131 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10132 | { | ||
| 10133 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 10134 | if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { | ||
| 10135 | drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10136 | } else | ||
| 10137 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 10138 | if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { | ||
| 10139 | drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10140 | } else | ||
| 10141 | #endif | ||
| 10142 | { | ||
| 10143 | /* Scalar fallback. */ | ||
| 10144 | #if 0 | ||
| 10145 | drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10146 | #else | ||
| 10147 | drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10148 | #endif | ||
| 10149 | } | ||
| 10150 | } | ||
| 10151 | |||
| 10152 | |||
| 10153 | #if 0 | ||
| 10154 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10155 | { | ||
| 10156 | for (drflac_uint64 i = 0; i < frameCount; ++i) { | ||
| 10157 | drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10158 | drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10159 | |||
| 10160 | mid = (mid << 1) | (side & 0x01); | ||
| 10161 | |||
| 10162 | pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); | ||
| 10163 | pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); | ||
| 10164 | } | ||
| 10165 | } | ||
| 10166 | #endif | ||
| 10167 | |||
| 10168 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10169 | { | ||
| 10170 | drflac_uint64 i; | ||
| 10171 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10172 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10173 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10174 | drflac_uint32 shift = unusedBitsPerSample; | ||
| 10175 | |||
| 10176 | if (shift > 0) { | ||
| 10177 | shift -= 1; | ||
| 10178 | for (i = 0; i < frameCount4; ++i) { | ||
| 10179 | drflac_uint32 temp0L; | ||
| 10180 | drflac_uint32 temp1L; | ||
| 10181 | drflac_uint32 temp2L; | ||
| 10182 | drflac_uint32 temp3L; | ||
| 10183 | drflac_uint32 temp0R; | ||
| 10184 | drflac_uint32 temp1R; | ||
| 10185 | drflac_uint32 temp2R; | ||
| 10186 | drflac_uint32 temp3R; | ||
| 10187 | |||
| 10188 | drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10189 | drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10190 | drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10191 | drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10192 | |||
| 10193 | drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10194 | drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10195 | drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10196 | drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10197 | |||
| 10198 | mid0 = (mid0 << 1) | (side0 & 0x01); | ||
| 10199 | mid1 = (mid1 << 1) | (side1 & 0x01); | ||
| 10200 | mid2 = (mid2 << 1) | (side2 & 0x01); | ||
| 10201 | mid3 = (mid3 << 1) | (side3 & 0x01); | ||
| 10202 | |||
| 10203 | temp0L = (mid0 + side0) << shift; | ||
| 10204 | temp1L = (mid1 + side1) << shift; | ||
| 10205 | temp2L = (mid2 + side2) << shift; | ||
| 10206 | temp3L = (mid3 + side3) << shift; | ||
| 10207 | |||
| 10208 | temp0R = (mid0 - side0) << shift; | ||
| 10209 | temp1R = (mid1 - side1) << shift; | ||
| 10210 | temp2R = (mid2 - side2) << shift; | ||
| 10211 | temp3R = (mid3 - side3) << shift; | ||
| 10212 | |||
| 10213 | temp0L >>= 16; | ||
| 10214 | temp1L >>= 16; | ||
| 10215 | temp2L >>= 16; | ||
| 10216 | temp3L >>= 16; | ||
| 10217 | |||
| 10218 | temp0R >>= 16; | ||
| 10219 | temp1R >>= 16; | ||
| 10220 | temp2R >>= 16; | ||
| 10221 | temp3R >>= 16; | ||
| 10222 | |||
| 10223 | pOutputSamples[i*8+0] = (drflac_int16)temp0L; | ||
| 10224 | pOutputSamples[i*8+1] = (drflac_int16)temp0R; | ||
| 10225 | pOutputSamples[i*8+2] = (drflac_int16)temp1L; | ||
| 10226 | pOutputSamples[i*8+3] = (drflac_int16)temp1R; | ||
| 10227 | pOutputSamples[i*8+4] = (drflac_int16)temp2L; | ||
| 10228 | pOutputSamples[i*8+5] = (drflac_int16)temp2R; | ||
| 10229 | pOutputSamples[i*8+6] = (drflac_int16)temp3L; | ||
| 10230 | pOutputSamples[i*8+7] = (drflac_int16)temp3R; | ||
| 10231 | } | ||
| 10232 | } else { | ||
| 10233 | for (i = 0; i < frameCount4; ++i) { | ||
| 10234 | drflac_uint32 temp0L; | ||
| 10235 | drflac_uint32 temp1L; | ||
| 10236 | drflac_uint32 temp2L; | ||
| 10237 | drflac_uint32 temp3L; | ||
| 10238 | drflac_uint32 temp0R; | ||
| 10239 | drflac_uint32 temp1R; | ||
| 10240 | drflac_uint32 temp2R; | ||
| 10241 | drflac_uint32 temp3R; | ||
| 10242 | |||
| 10243 | drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10244 | drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10245 | drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10246 | drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10247 | |||
| 10248 | drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10249 | drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10250 | drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10251 | drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10252 | |||
| 10253 | mid0 = (mid0 << 1) | (side0 & 0x01); | ||
| 10254 | mid1 = (mid1 << 1) | (side1 & 0x01); | ||
| 10255 | mid2 = (mid2 << 1) | (side2 & 0x01); | ||
| 10256 | mid3 = (mid3 << 1) | (side3 & 0x01); | ||
| 10257 | |||
| 10258 | temp0L = ((drflac_int32)(mid0 + side0) >> 1); | ||
| 10259 | temp1L = ((drflac_int32)(mid1 + side1) >> 1); | ||
| 10260 | temp2L = ((drflac_int32)(mid2 + side2) >> 1); | ||
| 10261 | temp3L = ((drflac_int32)(mid3 + side3) >> 1); | ||
| 10262 | |||
| 10263 | temp0R = ((drflac_int32)(mid0 - side0) >> 1); | ||
| 10264 | temp1R = ((drflac_int32)(mid1 - side1) >> 1); | ||
| 10265 | temp2R = ((drflac_int32)(mid2 - side2) >> 1); | ||
| 10266 | temp3R = ((drflac_int32)(mid3 - side3) >> 1); | ||
| 10267 | |||
| 10268 | temp0L >>= 16; | ||
| 10269 | temp1L >>= 16; | ||
| 10270 | temp2L >>= 16; | ||
| 10271 | temp3L >>= 16; | ||
| 10272 | |||
| 10273 | temp0R >>= 16; | ||
| 10274 | temp1R >>= 16; | ||
| 10275 | temp2R >>= 16; | ||
| 10276 | temp3R >>= 16; | ||
| 10277 | |||
| 10278 | pOutputSamples[i*8+0] = (drflac_int16)temp0L; | ||
| 10279 | pOutputSamples[i*8+1] = (drflac_int16)temp0R; | ||
| 10280 | pOutputSamples[i*8+2] = (drflac_int16)temp1L; | ||
| 10281 | pOutputSamples[i*8+3] = (drflac_int16)temp1R; | ||
| 10282 | pOutputSamples[i*8+4] = (drflac_int16)temp2L; | ||
| 10283 | pOutputSamples[i*8+5] = (drflac_int16)temp2R; | ||
| 10284 | pOutputSamples[i*8+6] = (drflac_int16)temp3L; | ||
| 10285 | pOutputSamples[i*8+7] = (drflac_int16)temp3R; | ||
| 10286 | } | ||
| 10287 | } | ||
| 10288 | |||
| 10289 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10290 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10291 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10292 | |||
| 10293 | mid = (mid << 1) | (side & 0x01); | ||
| 10294 | |||
| 10295 | pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); | ||
| 10296 | pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); | ||
| 10297 | } | ||
| 10298 | } | ||
| 10299 | |||
| 10300 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 10301 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10302 | { | ||
| 10303 | drflac_uint64 i; | ||
| 10304 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10305 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10306 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10307 | drflac_uint32 shift = unusedBitsPerSample; | ||
| 10308 | |||
| 10309 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 10310 | |||
| 10311 | if (shift == 0) { | ||
| 10312 | for (i = 0; i < frameCount4; ++i) { | ||
| 10313 | __m128i mid; | ||
| 10314 | __m128i side; | ||
| 10315 | __m128i left; | ||
| 10316 | __m128i right; | ||
| 10317 | |||
| 10318 | mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 10319 | side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 10320 | |||
| 10321 | mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); | ||
| 10322 | |||
| 10323 | left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); | ||
| 10324 | right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); | ||
| 10325 | |||
| 10326 | left = _mm_srai_epi32(left, 16); | ||
| 10327 | right = _mm_srai_epi32(right, 16); | ||
| 10328 | |||
| 10329 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); | ||
| 10330 | } | ||
| 10331 | |||
| 10332 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10333 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10334 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10335 | |||
| 10336 | mid = (mid << 1) | (side & 0x01); | ||
| 10337 | |||
| 10338 | pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); | ||
| 10339 | pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); | ||
| 10340 | } | ||
| 10341 | } else { | ||
| 10342 | shift -= 1; | ||
| 10343 | for (i = 0; i < frameCount4; ++i) { | ||
| 10344 | __m128i mid; | ||
| 10345 | __m128i side; | ||
| 10346 | __m128i left; | ||
| 10347 | __m128i right; | ||
| 10348 | |||
| 10349 | mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 10350 | side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 10351 | |||
| 10352 | mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); | ||
| 10353 | |||
| 10354 | left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); | ||
| 10355 | right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); | ||
| 10356 | |||
| 10357 | left = _mm_srai_epi32(left, 16); | ||
| 10358 | right = _mm_srai_epi32(right, 16); | ||
| 10359 | |||
| 10360 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); | ||
| 10361 | } | ||
| 10362 | |||
| 10363 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10364 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10365 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10366 | |||
| 10367 | mid = (mid << 1) | (side & 0x01); | ||
| 10368 | |||
| 10369 | pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); | ||
| 10370 | pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); | ||
| 10371 | } | ||
| 10372 | } | ||
| 10373 | } | ||
| 10374 | #endif | ||
| 10375 | |||
| 10376 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 10377 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10378 | { | ||
| 10379 | drflac_uint64 i; | ||
| 10380 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10381 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10382 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10383 | drflac_uint32 shift = unusedBitsPerSample; | ||
| 10384 | int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */ | ||
| 10385 | int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */ | ||
| 10386 | |||
| 10387 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 10388 | |||
| 10389 | wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 10390 | wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 10391 | |||
| 10392 | if (shift == 0) { | ||
| 10393 | for (i = 0; i < frameCount4; ++i) { | ||
| 10394 | uint32x4_t mid; | ||
| 10395 | uint32x4_t side; | ||
| 10396 | int32x4_t left; | ||
| 10397 | int32x4_t right; | ||
| 10398 | |||
| 10399 | mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); | ||
| 10400 | side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); | ||
| 10401 | |||
| 10402 | mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); | ||
| 10403 | |||
| 10404 | left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); | ||
| 10405 | right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); | ||
| 10406 | |||
| 10407 | left = vshrq_n_s32(left, 16); | ||
| 10408 | right = vshrq_n_s32(right, 16); | ||
| 10409 | |||
| 10410 | drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); | ||
| 10411 | } | ||
| 10412 | |||
| 10413 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10414 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10415 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10416 | |||
| 10417 | mid = (mid << 1) | (side & 0x01); | ||
| 10418 | |||
| 10419 | pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); | ||
| 10420 | pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); | ||
| 10421 | } | ||
| 10422 | } else { | ||
| 10423 | int32x4_t shift4; | ||
| 10424 | |||
| 10425 | shift -= 1; | ||
| 10426 | shift4 = vdupq_n_s32(shift); | ||
| 10427 | |||
| 10428 | for (i = 0; i < frameCount4; ++i) { | ||
| 10429 | uint32x4_t mid; | ||
| 10430 | uint32x4_t side; | ||
| 10431 | int32x4_t left; | ||
| 10432 | int32x4_t right; | ||
| 10433 | |||
| 10434 | mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); | ||
| 10435 | side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); | ||
| 10436 | |||
| 10437 | mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); | ||
| 10438 | |||
| 10439 | left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); | ||
| 10440 | right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); | ||
| 10441 | |||
| 10442 | left = vshrq_n_s32(left, 16); | ||
| 10443 | right = vshrq_n_s32(right, 16); | ||
| 10444 | |||
| 10445 | drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); | ||
| 10446 | } | ||
| 10447 | |||
| 10448 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10449 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10450 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10451 | |||
| 10452 | mid = (mid << 1) | (side & 0x01); | ||
| 10453 | |||
| 10454 | pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); | ||
| 10455 | pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); | ||
| 10456 | } | ||
| 10457 | } | ||
| 10458 | } | ||
| 10459 | #endif | ||
| 10460 | |||
| 10461 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10462 | { | ||
| 10463 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 10464 | if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { | ||
| 10465 | drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10466 | } else | ||
| 10467 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 10468 | if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { | ||
| 10469 | drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10470 | } else | ||
| 10471 | #endif | ||
| 10472 | { | ||
| 10473 | /* Scalar fallback. */ | ||
| 10474 | #if 0 | ||
| 10475 | drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10476 | #else | ||
| 10477 | drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10478 | #endif | ||
| 10479 | } | ||
| 10480 | } | ||
| 10481 | |||
| 10482 | |||
| 10483 | #if 0 | ||
| 10484 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10485 | { | ||
| 10486 | for (drflac_uint64 i = 0; i < frameCount; ++i) { | ||
| 10487 | pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); | ||
| 10488 | pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); | ||
| 10489 | } | ||
| 10490 | } | ||
| 10491 | #endif | ||
| 10492 | |||
| 10493 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10494 | { | ||
| 10495 | drflac_uint64 i; | ||
| 10496 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10497 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10498 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10499 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10500 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10501 | |||
| 10502 | for (i = 0; i < frameCount4; ++i) { | ||
| 10503 | drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; | ||
| 10504 | drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; | ||
| 10505 | drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; | ||
| 10506 | drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; | ||
| 10507 | |||
| 10508 | drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; | ||
| 10509 | drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; | ||
| 10510 | drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; | ||
| 10511 | drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; | ||
| 10512 | |||
| 10513 | tempL0 >>= 16; | ||
| 10514 | tempL1 >>= 16; | ||
| 10515 | tempL2 >>= 16; | ||
| 10516 | tempL3 >>= 16; | ||
| 10517 | |||
| 10518 | tempR0 >>= 16; | ||
| 10519 | tempR1 >>= 16; | ||
| 10520 | tempR2 >>= 16; | ||
| 10521 | tempR3 >>= 16; | ||
| 10522 | |||
| 10523 | pOutputSamples[i*8+0] = (drflac_int16)tempL0; | ||
| 10524 | pOutputSamples[i*8+1] = (drflac_int16)tempR0; | ||
| 10525 | pOutputSamples[i*8+2] = (drflac_int16)tempL1; | ||
| 10526 | pOutputSamples[i*8+3] = (drflac_int16)tempR1; | ||
| 10527 | pOutputSamples[i*8+4] = (drflac_int16)tempL2; | ||
| 10528 | pOutputSamples[i*8+5] = (drflac_int16)tempR2; | ||
| 10529 | pOutputSamples[i*8+6] = (drflac_int16)tempL3; | ||
| 10530 | pOutputSamples[i*8+7] = (drflac_int16)tempR3; | ||
| 10531 | } | ||
| 10532 | |||
| 10533 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10534 | pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); | ||
| 10535 | pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); | ||
| 10536 | } | ||
| 10537 | } | ||
| 10538 | |||
| 10539 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 10540 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10541 | { | ||
| 10542 | drflac_uint64 i; | ||
| 10543 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10544 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10545 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10546 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10547 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10548 | |||
| 10549 | for (i = 0; i < frameCount4; ++i) { | ||
| 10550 | __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); | ||
| 10551 | __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); | ||
| 10552 | |||
| 10553 | left = _mm_srai_epi32(left, 16); | ||
| 10554 | right = _mm_srai_epi32(right, 16); | ||
| 10555 | |||
| 10556 | /* At this point we have results. We can now pack and interleave these into a single __m128i object and then store the in the output buffer. */ | ||
| 10557 | _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); | ||
| 10558 | } | ||
| 10559 | |||
| 10560 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10561 | pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); | ||
| 10562 | pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); | ||
| 10563 | } | ||
| 10564 | } | ||
| 10565 | #endif | ||
| 10566 | |||
| 10567 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 10568 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10569 | { | ||
| 10570 | drflac_uint64 i; | ||
| 10571 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10572 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10573 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10574 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10575 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10576 | |||
| 10577 | int32x4_t shift0_4 = vdupq_n_s32(shift0); | ||
| 10578 | int32x4_t shift1_4 = vdupq_n_s32(shift1); | ||
| 10579 | |||
| 10580 | for (i = 0; i < frameCount4; ++i) { | ||
| 10581 | int32x4_t left; | ||
| 10582 | int32x4_t right; | ||
| 10583 | |||
| 10584 | left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); | ||
| 10585 | right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); | ||
| 10586 | |||
| 10587 | left = vshrq_n_s32(left, 16); | ||
| 10588 | right = vshrq_n_s32(right, 16); | ||
| 10589 | |||
| 10590 | drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); | ||
| 10591 | } | ||
| 10592 | |||
| 10593 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10594 | pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); | ||
| 10595 | pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); | ||
| 10596 | } | ||
| 10597 | } | ||
| 10598 | #endif | ||
| 10599 | |||
| 10600 | static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) | ||
| 10601 | { | ||
| 10602 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 10603 | if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { | ||
| 10604 | drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10605 | } else | ||
| 10606 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 10607 | if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { | ||
| 10608 | drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10609 | } else | ||
| 10610 | #endif | ||
| 10611 | { | ||
| 10612 | /* Scalar fallback. */ | ||
| 10613 | #if 0 | ||
| 10614 | drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10615 | #else | ||
| 10616 | drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10617 | #endif | ||
| 10618 | } | ||
| 10619 | } | ||
| 10620 | |||
| 10621 | DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut) | ||
| 10622 | { | ||
| 10623 | drflac_uint64 framesRead; | ||
| 10624 | drflac_uint32 unusedBitsPerSample; | ||
| 10625 | |||
| 10626 | if (pFlac == NULL || framesToRead == 0) { | ||
| 10627 | return 0; | ||
| 10628 | } | ||
| 10629 | |||
| 10630 | if (pBufferOut == NULL) { | ||
| 10631 | return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); | ||
| 10632 | } | ||
| 10633 | |||
| 10634 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); | ||
| 10635 | unusedBitsPerSample = 32 - pFlac->bitsPerSample; | ||
| 10636 | |||
| 10637 | framesRead = 0; | ||
| 10638 | while (framesToRead > 0) { | ||
| 10639 | /* If we've run out of samples in this frame, go to the next. */ | ||
| 10640 | if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { | ||
| 10641 | if (!drflac__read_and_decode_next_flac_frame(pFlac)) { | ||
| 10642 | break; /* Couldn't read the next frame, so just break from the loop and return. */ | ||
| 10643 | } | ||
| 10644 | } else { | ||
| 10645 | unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); | ||
| 10646 | drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; | ||
| 10647 | drflac_uint64 frameCountThisIteration = framesToRead; | ||
| 10648 | |||
| 10649 | if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { | ||
| 10650 | frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; | ||
| 10651 | } | ||
| 10652 | |||
| 10653 | if (channelCount == 2) { | ||
| 10654 | const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; | ||
| 10655 | const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; | ||
| 10656 | |||
| 10657 | switch (pFlac->currentFLACFrame.header.channelAssignment) | ||
| 10658 | { | ||
| 10659 | case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: | ||
| 10660 | { | ||
| 10661 | drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); | ||
| 10662 | } break; | ||
| 10663 | |||
| 10664 | case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: | ||
| 10665 | { | ||
| 10666 | drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); | ||
| 10667 | } break; | ||
| 10668 | |||
| 10669 | case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: | ||
| 10670 | { | ||
| 10671 | drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); | ||
| 10672 | } break; | ||
| 10673 | |||
| 10674 | case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: | ||
| 10675 | default: | ||
| 10676 | { | ||
| 10677 | drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); | ||
| 10678 | } break; | ||
| 10679 | } | ||
| 10680 | } else { | ||
| 10681 | /* Generic interleaving. */ | ||
| 10682 | drflac_uint64 i; | ||
| 10683 | for (i = 0; i < frameCountThisIteration; ++i) { | ||
| 10684 | unsigned int j; | ||
| 10685 | for (j = 0; j < channelCount; ++j) { | ||
| 10686 | drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); | ||
| 10687 | pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16); | ||
| 10688 | } | ||
| 10689 | } | ||
| 10690 | } | ||
| 10691 | |||
| 10692 | framesRead += frameCountThisIteration; | ||
| 10693 | pBufferOut += frameCountThisIteration * channelCount; | ||
| 10694 | framesToRead -= frameCountThisIteration; | ||
| 10695 | pFlac->currentPCMFrame += frameCountThisIteration; | ||
| 10696 | pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; | ||
| 10697 | } | ||
| 10698 | } | ||
| 10699 | |||
| 10700 | return framesRead; | ||
| 10701 | } | ||
| 10702 | |||
| 10703 | |||
| 10704 | #if 0 | ||
| 10705 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 10706 | { | ||
| 10707 | drflac_uint64 i; | ||
| 10708 | for (i = 0; i < frameCount; ++i) { | ||
| 10709 | drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 10710 | drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 10711 | drflac_uint32 right = left - side; | ||
| 10712 | |||
| 10713 | pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); | ||
| 10714 | pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); | ||
| 10715 | } | ||
| 10716 | } | ||
| 10717 | #endif | ||
| 10718 | |||
| 10719 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 10720 | { | ||
| 10721 | drflac_uint64 i; | ||
| 10722 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10723 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10724 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10725 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10726 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10727 | |||
| 10728 | float factor = 1 / 2147483648.0; | ||
| 10729 | |||
| 10730 | for (i = 0; i < frameCount4; ++i) { | ||
| 10731 | drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; | ||
| 10732 | drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; | ||
| 10733 | drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; | ||
| 10734 | drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; | ||
| 10735 | |||
| 10736 | drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; | ||
| 10737 | drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; | ||
| 10738 | drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; | ||
| 10739 | drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; | ||
| 10740 | |||
| 10741 | drflac_uint32 right0 = left0 - side0; | ||
| 10742 | drflac_uint32 right1 = left1 - side1; | ||
| 10743 | drflac_uint32 right2 = left2 - side2; | ||
| 10744 | drflac_uint32 right3 = left3 - side3; | ||
| 10745 | |||
| 10746 | pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; | ||
| 10747 | pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; | ||
| 10748 | pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; | ||
| 10749 | pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; | ||
| 10750 | pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; | ||
| 10751 | pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; | ||
| 10752 | pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; | ||
| 10753 | pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; | ||
| 10754 | } | ||
| 10755 | |||
| 10756 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10757 | drflac_uint32 left = pInputSamples0U32[i] << shift0; | ||
| 10758 | drflac_uint32 side = pInputSamples1U32[i] << shift1; | ||
| 10759 | drflac_uint32 right = left - side; | ||
| 10760 | |||
| 10761 | pOutputSamples[i*2+0] = (drflac_int32)left * factor; | ||
| 10762 | pOutputSamples[i*2+1] = (drflac_int32)right * factor; | ||
| 10763 | } | ||
| 10764 | } | ||
| 10765 | |||
| 10766 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 10767 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 10768 | { | ||
| 10769 | drflac_uint64 i; | ||
| 10770 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10771 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10772 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10773 | drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; | ||
| 10774 | drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; | ||
| 10775 | __m128 factor; | ||
| 10776 | |||
| 10777 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 10778 | |||
| 10779 | factor = _mm_set1_ps(1.0f / 8388608.0f); | ||
| 10780 | |||
| 10781 | for (i = 0; i < frameCount4; ++i) { | ||
| 10782 | __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); | ||
| 10783 | __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); | ||
| 10784 | __m128i right = _mm_sub_epi32(left, side); | ||
| 10785 | __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); | ||
| 10786 | __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); | ||
| 10787 | |||
| 10788 | _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); | ||
| 10789 | _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); | ||
| 10790 | } | ||
| 10791 | |||
| 10792 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10793 | drflac_uint32 left = pInputSamples0U32[i] << shift0; | ||
| 10794 | drflac_uint32 side = pInputSamples1U32[i] << shift1; | ||
| 10795 | drflac_uint32 right = left - side; | ||
| 10796 | |||
| 10797 | pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; | ||
| 10798 | pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; | ||
| 10799 | } | ||
| 10800 | } | ||
| 10801 | #endif | ||
| 10802 | |||
| 10803 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 10804 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 10805 | { | ||
| 10806 | drflac_uint64 i; | ||
| 10807 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10808 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10809 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10810 | drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; | ||
| 10811 | drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; | ||
| 10812 | float32x4_t factor4; | ||
| 10813 | int32x4_t shift0_4; | ||
| 10814 | int32x4_t shift1_4; | ||
| 10815 | |||
| 10816 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 10817 | |||
| 10818 | factor4 = vdupq_n_f32(1.0f / 8388608.0f); | ||
| 10819 | shift0_4 = vdupq_n_s32(shift0); | ||
| 10820 | shift1_4 = vdupq_n_s32(shift1); | ||
| 10821 | |||
| 10822 | for (i = 0; i < frameCount4; ++i) { | ||
| 10823 | uint32x4_t left; | ||
| 10824 | uint32x4_t side; | ||
| 10825 | uint32x4_t right; | ||
| 10826 | float32x4_t leftf; | ||
| 10827 | float32x4_t rightf; | ||
| 10828 | |||
| 10829 | left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); | ||
| 10830 | side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); | ||
| 10831 | right = vsubq_u32(left, side); | ||
| 10832 | leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); | ||
| 10833 | rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); | ||
| 10834 | |||
| 10835 | drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); | ||
| 10836 | } | ||
| 10837 | |||
| 10838 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10839 | drflac_uint32 left = pInputSamples0U32[i] << shift0; | ||
| 10840 | drflac_uint32 side = pInputSamples1U32[i] << shift1; | ||
| 10841 | drflac_uint32 right = left - side; | ||
| 10842 | |||
| 10843 | pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; | ||
| 10844 | pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; | ||
| 10845 | } | ||
| 10846 | } | ||
| 10847 | #endif | ||
| 10848 | |||
| 10849 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 10850 | { | ||
| 10851 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 10852 | if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { | ||
| 10853 | drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10854 | } else | ||
| 10855 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 10856 | if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { | ||
| 10857 | drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10858 | } else | ||
| 10859 | #endif | ||
| 10860 | { | ||
| 10861 | /* Scalar fallback. */ | ||
| 10862 | #if 0 | ||
| 10863 | drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10864 | #else | ||
| 10865 | drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 10866 | #endif | ||
| 10867 | } | ||
| 10868 | } | ||
| 10869 | |||
| 10870 | |||
| 10871 | #if 0 | ||
| 10872 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 10873 | { | ||
| 10874 | drflac_uint64 i; | ||
| 10875 | for (i = 0; i < frameCount; ++i) { | ||
| 10876 | drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 10877 | drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 10878 | drflac_uint32 left = right + side; | ||
| 10879 | |||
| 10880 | pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); | ||
| 10881 | pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); | ||
| 10882 | } | ||
| 10883 | } | ||
| 10884 | #endif | ||
| 10885 | |||
| 10886 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 10887 | { | ||
| 10888 | drflac_uint64 i; | ||
| 10889 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10890 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10891 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10892 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 10893 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 10894 | float factor = 1 / 2147483648.0; | ||
| 10895 | |||
| 10896 | for (i = 0; i < frameCount4; ++i) { | ||
| 10897 | drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; | ||
| 10898 | drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; | ||
| 10899 | drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; | ||
| 10900 | drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; | ||
| 10901 | |||
| 10902 | drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; | ||
| 10903 | drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; | ||
| 10904 | drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; | ||
| 10905 | drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; | ||
| 10906 | |||
| 10907 | drflac_uint32 left0 = right0 + side0; | ||
| 10908 | drflac_uint32 left1 = right1 + side1; | ||
| 10909 | drflac_uint32 left2 = right2 + side2; | ||
| 10910 | drflac_uint32 left3 = right3 + side3; | ||
| 10911 | |||
| 10912 | pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; | ||
| 10913 | pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; | ||
| 10914 | pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; | ||
| 10915 | pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; | ||
| 10916 | pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; | ||
| 10917 | pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; | ||
| 10918 | pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; | ||
| 10919 | pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; | ||
| 10920 | } | ||
| 10921 | |||
| 10922 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10923 | drflac_uint32 side = pInputSamples0U32[i] << shift0; | ||
| 10924 | drflac_uint32 right = pInputSamples1U32[i] << shift1; | ||
| 10925 | drflac_uint32 left = right + side; | ||
| 10926 | |||
| 10927 | pOutputSamples[i*2+0] = (drflac_int32)left * factor; | ||
| 10928 | pOutputSamples[i*2+1] = (drflac_int32)right * factor; | ||
| 10929 | } | ||
| 10930 | } | ||
| 10931 | |||
| 10932 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 10933 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 10934 | { | ||
| 10935 | drflac_uint64 i; | ||
| 10936 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10937 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10938 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10939 | drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; | ||
| 10940 | drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; | ||
| 10941 | __m128 factor; | ||
| 10942 | |||
| 10943 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 10944 | |||
| 10945 | factor = _mm_set1_ps(1.0f / 8388608.0f); | ||
| 10946 | |||
| 10947 | for (i = 0; i < frameCount4; ++i) { | ||
| 10948 | __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); | ||
| 10949 | __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); | ||
| 10950 | __m128i left = _mm_add_epi32(right, side); | ||
| 10951 | __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); | ||
| 10952 | __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); | ||
| 10953 | |||
| 10954 | _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); | ||
| 10955 | _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); | ||
| 10956 | } | ||
| 10957 | |||
| 10958 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 10959 | drflac_uint32 side = pInputSamples0U32[i] << shift0; | ||
| 10960 | drflac_uint32 right = pInputSamples1U32[i] << shift1; | ||
| 10961 | drflac_uint32 left = right + side; | ||
| 10962 | |||
| 10963 | pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; | ||
| 10964 | pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; | ||
| 10965 | } | ||
| 10966 | } | ||
| 10967 | #endif | ||
| 10968 | |||
| 10969 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 10970 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 10971 | { | ||
| 10972 | drflac_uint64 i; | ||
| 10973 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 10974 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 10975 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 10976 | drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; | ||
| 10977 | drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; | ||
| 10978 | float32x4_t factor4; | ||
| 10979 | int32x4_t shift0_4; | ||
| 10980 | int32x4_t shift1_4; | ||
| 10981 | |||
| 10982 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 10983 | |||
| 10984 | factor4 = vdupq_n_f32(1.0f / 8388608.0f); | ||
| 10985 | shift0_4 = vdupq_n_s32(shift0); | ||
| 10986 | shift1_4 = vdupq_n_s32(shift1); | ||
| 10987 | |||
| 10988 | for (i = 0; i < frameCount4; ++i) { | ||
| 10989 | uint32x4_t side; | ||
| 10990 | uint32x4_t right; | ||
| 10991 | uint32x4_t left; | ||
| 10992 | float32x4_t leftf; | ||
| 10993 | float32x4_t rightf; | ||
| 10994 | |||
| 10995 | side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); | ||
| 10996 | right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); | ||
| 10997 | left = vaddq_u32(right, side); | ||
| 10998 | leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); | ||
| 10999 | rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); | ||
| 11000 | |||
| 11001 | drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); | ||
| 11002 | } | ||
| 11003 | |||
| 11004 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 11005 | drflac_uint32 side = pInputSamples0U32[i] << shift0; | ||
| 11006 | drflac_uint32 right = pInputSamples1U32[i] << shift1; | ||
| 11007 | drflac_uint32 left = right + side; | ||
| 11008 | |||
| 11009 | pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; | ||
| 11010 | pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; | ||
| 11011 | } | ||
| 11012 | } | ||
| 11013 | #endif | ||
| 11014 | |||
| 11015 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 11016 | { | ||
| 11017 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 11018 | if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { | ||
| 11019 | drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 11020 | } else | ||
| 11021 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 11022 | if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { | ||
| 11023 | drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 11024 | } else | ||
| 11025 | #endif | ||
| 11026 | { | ||
| 11027 | /* Scalar fallback. */ | ||
| 11028 | #if 0 | ||
| 11029 | drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 11030 | #else | ||
| 11031 | drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 11032 | #endif | ||
| 11033 | } | ||
| 11034 | } | ||
| 11035 | |||
| 11036 | |||
| 11037 | #if 0 | ||
| 11038 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 11039 | { | ||
| 11040 | for (drflac_uint64 i = 0; i < frameCount; ++i) { | ||
| 11041 | drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11042 | drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11043 | |||
| 11044 | mid = (mid << 1) | (side & 0x01); | ||
| 11045 | |||
| 11046 | pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); | ||
| 11047 | pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); | ||
| 11048 | } | ||
| 11049 | } | ||
| 11050 | #endif | ||
| 11051 | |||
| 11052 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 11053 | { | ||
| 11054 | drflac_uint64 i; | ||
| 11055 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 11056 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 11057 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 11058 | drflac_uint32 shift = unusedBitsPerSample; | ||
| 11059 | float factor = 1 / 2147483648.0; | ||
| 11060 | |||
| 11061 | if (shift > 0) { | ||
| 11062 | shift -= 1; | ||
| 11063 | for (i = 0; i < frameCount4; ++i) { | ||
| 11064 | drflac_uint32 temp0L; | ||
| 11065 | drflac_uint32 temp1L; | ||
| 11066 | drflac_uint32 temp2L; | ||
| 11067 | drflac_uint32 temp3L; | ||
| 11068 | drflac_uint32 temp0R; | ||
| 11069 | drflac_uint32 temp1R; | ||
| 11070 | drflac_uint32 temp2R; | ||
| 11071 | drflac_uint32 temp3R; | ||
| 11072 | |||
| 11073 | drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11074 | drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11075 | drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11076 | drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11077 | |||
| 11078 | drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11079 | drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11080 | drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11081 | drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11082 | |||
| 11083 | mid0 = (mid0 << 1) | (side0 & 0x01); | ||
| 11084 | mid1 = (mid1 << 1) | (side1 & 0x01); | ||
| 11085 | mid2 = (mid2 << 1) | (side2 & 0x01); | ||
| 11086 | mid3 = (mid3 << 1) | (side3 & 0x01); | ||
| 11087 | |||
| 11088 | temp0L = (mid0 + side0) << shift; | ||
| 11089 | temp1L = (mid1 + side1) << shift; | ||
| 11090 | temp2L = (mid2 + side2) << shift; | ||
| 11091 | temp3L = (mid3 + side3) << shift; | ||
| 11092 | |||
| 11093 | temp0R = (mid0 - side0) << shift; | ||
| 11094 | temp1R = (mid1 - side1) << shift; | ||
| 11095 | temp2R = (mid2 - side2) << shift; | ||
| 11096 | temp3R = (mid3 - side3) << shift; | ||
| 11097 | |||
| 11098 | pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; | ||
| 11099 | pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; | ||
| 11100 | pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; | ||
| 11101 | pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; | ||
| 11102 | pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; | ||
| 11103 | pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; | ||
| 11104 | pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; | ||
| 11105 | pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; | ||
| 11106 | } | ||
| 11107 | } else { | ||
| 11108 | for (i = 0; i < frameCount4; ++i) { | ||
| 11109 | drflac_uint32 temp0L; | ||
| 11110 | drflac_uint32 temp1L; | ||
| 11111 | drflac_uint32 temp2L; | ||
| 11112 | drflac_uint32 temp3L; | ||
| 11113 | drflac_uint32 temp0R; | ||
| 11114 | drflac_uint32 temp1R; | ||
| 11115 | drflac_uint32 temp2R; | ||
| 11116 | drflac_uint32 temp3R; | ||
| 11117 | |||
| 11118 | drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11119 | drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11120 | drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11121 | drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11122 | |||
| 11123 | drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11124 | drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11125 | drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11126 | drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11127 | |||
| 11128 | mid0 = (mid0 << 1) | (side0 & 0x01); | ||
| 11129 | mid1 = (mid1 << 1) | (side1 & 0x01); | ||
| 11130 | mid2 = (mid2 << 1) | (side2 & 0x01); | ||
| 11131 | mid3 = (mid3 << 1) | (side3 & 0x01); | ||
| 11132 | |||
| 11133 | temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); | ||
| 11134 | temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); | ||
| 11135 | temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); | ||
| 11136 | temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); | ||
| 11137 | |||
| 11138 | temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); | ||
| 11139 | temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); | ||
| 11140 | temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); | ||
| 11141 | temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); | ||
| 11142 | |||
| 11143 | pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; | ||
| 11144 | pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; | ||
| 11145 | pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; | ||
| 11146 | pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; | ||
| 11147 | pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; | ||
| 11148 | pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; | ||
| 11149 | pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; | ||
| 11150 | pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; | ||
| 11151 | } | ||
| 11152 | } | ||
| 11153 | |||
| 11154 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 11155 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11156 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11157 | |||
| 11158 | mid = (mid << 1) | (side & 0x01); | ||
| 11159 | |||
| 11160 | pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; | ||
| 11161 | pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; | ||
| 11162 | } | ||
| 11163 | } | ||
| 11164 | |||
| 11165 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 11166 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 11167 | { | ||
| 11168 | drflac_uint64 i; | ||
| 11169 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 11170 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 11171 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 11172 | drflac_uint32 shift = unusedBitsPerSample - 8; | ||
| 11173 | float factor; | ||
| 11174 | __m128 factor128; | ||
| 11175 | |||
| 11176 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 11177 | |||
| 11178 | factor = 1.0f / 8388608.0f; | ||
| 11179 | factor128 = _mm_set1_ps(factor); | ||
| 11180 | |||
| 11181 | if (shift == 0) { | ||
| 11182 | for (i = 0; i < frameCount4; ++i) { | ||
| 11183 | __m128i mid; | ||
| 11184 | __m128i side; | ||
| 11185 | __m128i tempL; | ||
| 11186 | __m128i tempR; | ||
| 11187 | __m128 leftf; | ||
| 11188 | __m128 rightf; | ||
| 11189 | |||
| 11190 | mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 11191 | side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 11192 | |||
| 11193 | mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); | ||
| 11194 | |||
| 11195 | tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); | ||
| 11196 | tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); | ||
| 11197 | |||
| 11198 | leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); | ||
| 11199 | rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); | ||
| 11200 | |||
| 11201 | _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); | ||
| 11202 | _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); | ||
| 11203 | } | ||
| 11204 | |||
| 11205 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 11206 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11207 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11208 | |||
| 11209 | mid = (mid << 1) | (side & 0x01); | ||
| 11210 | |||
| 11211 | pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; | ||
| 11212 | pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; | ||
| 11213 | } | ||
| 11214 | } else { | ||
| 11215 | shift -= 1; | ||
| 11216 | for (i = 0; i < frameCount4; ++i) { | ||
| 11217 | __m128i mid; | ||
| 11218 | __m128i side; | ||
| 11219 | __m128i tempL; | ||
| 11220 | __m128i tempR; | ||
| 11221 | __m128 leftf; | ||
| 11222 | __m128 rightf; | ||
| 11223 | |||
| 11224 | mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 11225 | side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 11226 | |||
| 11227 | mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); | ||
| 11228 | |||
| 11229 | tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); | ||
| 11230 | tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); | ||
| 11231 | |||
| 11232 | leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); | ||
| 11233 | rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); | ||
| 11234 | |||
| 11235 | _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); | ||
| 11236 | _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); | ||
| 11237 | } | ||
| 11238 | |||
| 11239 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 11240 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11241 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11242 | |||
| 11243 | mid = (mid << 1) | (side & 0x01); | ||
| 11244 | |||
| 11245 | pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; | ||
| 11246 | pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; | ||
| 11247 | } | ||
| 11248 | } | ||
| 11249 | } | ||
| 11250 | #endif | ||
| 11251 | |||
| 11252 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 11253 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 11254 | { | ||
| 11255 | drflac_uint64 i; | ||
| 11256 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 11257 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 11258 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 11259 | drflac_uint32 shift = unusedBitsPerSample - 8; | ||
| 11260 | float factor; | ||
| 11261 | float32x4_t factor4; | ||
| 11262 | int32x4_t shift4; | ||
| 11263 | int32x4_t wbps0_4; /* Wasted Bits Per Sample */ | ||
| 11264 | int32x4_t wbps1_4; /* Wasted Bits Per Sample */ | ||
| 11265 | |||
| 11266 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); | ||
| 11267 | |||
| 11268 | factor = 1.0f / 8388608.0f; | ||
| 11269 | factor4 = vdupq_n_f32(factor); | ||
| 11270 | wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); | ||
| 11271 | wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); | ||
| 11272 | |||
| 11273 | if (shift == 0) { | ||
| 11274 | for (i = 0; i < frameCount4; ++i) { | ||
| 11275 | int32x4_t lefti; | ||
| 11276 | int32x4_t righti; | ||
| 11277 | float32x4_t leftf; | ||
| 11278 | float32x4_t rightf; | ||
| 11279 | |||
| 11280 | uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); | ||
| 11281 | uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); | ||
| 11282 | |||
| 11283 | mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); | ||
| 11284 | |||
| 11285 | lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); | ||
| 11286 | righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); | ||
| 11287 | |||
| 11288 | leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); | ||
| 11289 | rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); | ||
| 11290 | |||
| 11291 | drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); | ||
| 11292 | } | ||
| 11293 | |||
| 11294 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 11295 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11296 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11297 | |||
| 11298 | mid = (mid << 1) | (side & 0x01); | ||
| 11299 | |||
| 11300 | pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; | ||
| 11301 | pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; | ||
| 11302 | } | ||
| 11303 | } else { | ||
| 11304 | shift -= 1; | ||
| 11305 | shift4 = vdupq_n_s32(shift); | ||
| 11306 | for (i = 0; i < frameCount4; ++i) { | ||
| 11307 | uint32x4_t mid; | ||
| 11308 | uint32x4_t side; | ||
| 11309 | int32x4_t lefti; | ||
| 11310 | int32x4_t righti; | ||
| 11311 | float32x4_t leftf; | ||
| 11312 | float32x4_t rightf; | ||
| 11313 | |||
| 11314 | mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); | ||
| 11315 | side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); | ||
| 11316 | |||
| 11317 | mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); | ||
| 11318 | |||
| 11319 | lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); | ||
| 11320 | righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); | ||
| 11321 | |||
| 11322 | leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); | ||
| 11323 | rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); | ||
| 11324 | |||
| 11325 | drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); | ||
| 11326 | } | ||
| 11327 | |||
| 11328 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 11329 | drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11330 | drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11331 | |||
| 11332 | mid = (mid << 1) | (side & 0x01); | ||
| 11333 | |||
| 11334 | pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; | ||
| 11335 | pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; | ||
| 11336 | } | ||
| 11337 | } | ||
| 11338 | } | ||
| 11339 | #endif | ||
| 11340 | |||
| 11341 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 11342 | { | ||
| 11343 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 11344 | if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { | ||
| 11345 | drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 11346 | } else | ||
| 11347 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 11348 | if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { | ||
| 11349 | drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 11350 | } else | ||
| 11351 | #endif | ||
| 11352 | { | ||
| 11353 | /* Scalar fallback. */ | ||
| 11354 | #if 0 | ||
| 11355 | drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 11356 | #else | ||
| 11357 | drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 11358 | #endif | ||
| 11359 | } | ||
| 11360 | } | ||
| 11361 | |||
| 11362 | #if 0 | ||
| 11363 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 11364 | { | ||
| 11365 | for (drflac_uint64 i = 0; i < frameCount; ++i) { | ||
| 11366 | pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); | ||
| 11367 | pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); | ||
| 11368 | } | ||
| 11369 | } | ||
| 11370 | #endif | ||
| 11371 | |||
| 11372 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 11373 | { | ||
| 11374 | drflac_uint64 i; | ||
| 11375 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 11376 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 11377 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 11378 | drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; | ||
| 11379 | drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; | ||
| 11380 | float factor = 1 / 2147483648.0; | ||
| 11381 | |||
| 11382 | for (i = 0; i < frameCount4; ++i) { | ||
| 11383 | drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; | ||
| 11384 | drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; | ||
| 11385 | drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; | ||
| 11386 | drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; | ||
| 11387 | |||
| 11388 | drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; | ||
| 11389 | drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; | ||
| 11390 | drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; | ||
| 11391 | drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; | ||
| 11392 | |||
| 11393 | pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor; | ||
| 11394 | pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor; | ||
| 11395 | pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor; | ||
| 11396 | pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor; | ||
| 11397 | pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor; | ||
| 11398 | pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor; | ||
| 11399 | pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor; | ||
| 11400 | pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor; | ||
| 11401 | } | ||
| 11402 | |||
| 11403 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 11404 | pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; | ||
| 11405 | pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; | ||
| 11406 | } | ||
| 11407 | } | ||
| 11408 | |||
| 11409 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 11410 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 11411 | { | ||
| 11412 | drflac_uint64 i; | ||
| 11413 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 11414 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 11415 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 11416 | drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; | ||
| 11417 | drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; | ||
| 11418 | |||
| 11419 | float factor = 1.0f / 8388608.0f; | ||
| 11420 | __m128 factor128 = _mm_set1_ps(factor); | ||
| 11421 | |||
| 11422 | for (i = 0; i < frameCount4; ++i) { | ||
| 11423 | __m128i lefti; | ||
| 11424 | __m128i righti; | ||
| 11425 | __m128 leftf; | ||
| 11426 | __m128 rightf; | ||
| 11427 | |||
| 11428 | lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); | ||
| 11429 | righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); | ||
| 11430 | |||
| 11431 | leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); | ||
| 11432 | rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); | ||
| 11433 | |||
| 11434 | _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); | ||
| 11435 | _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); | ||
| 11436 | } | ||
| 11437 | |||
| 11438 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 11439 | pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; | ||
| 11440 | pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; | ||
| 11441 | } | ||
| 11442 | } | ||
| 11443 | #endif | ||
| 11444 | |||
| 11445 | #if defined(DRFLAC_SUPPORT_NEON) | ||
| 11446 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 11447 | { | ||
| 11448 | drflac_uint64 i; | ||
| 11449 | drflac_uint64 frameCount4 = frameCount >> 2; | ||
| 11450 | const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; | ||
| 11451 | const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; | ||
| 11452 | drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; | ||
| 11453 | drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; | ||
| 11454 | |||
| 11455 | float factor = 1.0f / 8388608.0f; | ||
| 11456 | float32x4_t factor4 = vdupq_n_f32(factor); | ||
| 11457 | int32x4_t shift0_4 = vdupq_n_s32(shift0); | ||
| 11458 | int32x4_t shift1_4 = vdupq_n_s32(shift1); | ||
| 11459 | |||
| 11460 | for (i = 0; i < frameCount4; ++i) { | ||
| 11461 | int32x4_t lefti; | ||
| 11462 | int32x4_t righti; | ||
| 11463 | float32x4_t leftf; | ||
| 11464 | float32x4_t rightf; | ||
| 11465 | |||
| 11466 | lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); | ||
| 11467 | righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); | ||
| 11468 | |||
| 11469 | leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); | ||
| 11470 | rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); | ||
| 11471 | |||
| 11472 | drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); | ||
| 11473 | } | ||
| 11474 | |||
| 11475 | for (i = (frameCount4 << 2); i < frameCount; ++i) { | ||
| 11476 | pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; | ||
| 11477 | pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; | ||
| 11478 | } | ||
| 11479 | } | ||
| 11480 | #endif | ||
| 11481 | |||
| 11482 | static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) | ||
| 11483 | { | ||
| 11484 | #if defined(DRFLAC_SUPPORT_SSE2) | ||
| 11485 | if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { | ||
| 11486 | drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 11487 | } else | ||
| 11488 | #elif defined(DRFLAC_SUPPORT_NEON) | ||
| 11489 | if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { | ||
| 11490 | drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 11491 | } else | ||
| 11492 | #endif | ||
| 11493 | { | ||
| 11494 | /* Scalar fallback. */ | ||
| 11495 | #if 0 | ||
| 11496 | drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 11497 | #else | ||
| 11498 | drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); | ||
| 11499 | #endif | ||
| 11500 | } | ||
| 11501 | } | ||
| 11502 | |||
| 11503 | DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut) | ||
| 11504 | { | ||
| 11505 | drflac_uint64 framesRead; | ||
| 11506 | drflac_uint32 unusedBitsPerSample; | ||
| 11507 | |||
| 11508 | if (pFlac == NULL || framesToRead == 0) { | ||
| 11509 | return 0; | ||
| 11510 | } | ||
| 11511 | |||
| 11512 | if (pBufferOut == NULL) { | ||
| 11513 | return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); | ||
| 11514 | } | ||
| 11515 | |||
| 11516 | DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); | ||
| 11517 | unusedBitsPerSample = 32 - pFlac->bitsPerSample; | ||
| 11518 | |||
| 11519 | framesRead = 0; | ||
| 11520 | while (framesToRead > 0) { | ||
| 11521 | /* If we've run out of samples in this frame, go to the next. */ | ||
| 11522 | if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { | ||
| 11523 | if (!drflac__read_and_decode_next_flac_frame(pFlac)) { | ||
| 11524 | break; /* Couldn't read the next frame, so just break from the loop and return. */ | ||
| 11525 | } | ||
| 11526 | } else { | ||
| 11527 | unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); | ||
| 11528 | drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; | ||
| 11529 | drflac_uint64 frameCountThisIteration = framesToRead; | ||
| 11530 | |||
| 11531 | if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { | ||
| 11532 | frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; | ||
| 11533 | } | ||
| 11534 | |||
| 11535 | if (channelCount == 2) { | ||
| 11536 | const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; | ||
| 11537 | const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; | ||
| 11538 | |||
| 11539 | switch (pFlac->currentFLACFrame.header.channelAssignment) | ||
| 11540 | { | ||
| 11541 | case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: | ||
| 11542 | { | ||
| 11543 | drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); | ||
| 11544 | } break; | ||
| 11545 | |||
| 11546 | case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: | ||
| 11547 | { | ||
| 11548 | drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); | ||
| 11549 | } break; | ||
| 11550 | |||
| 11551 | case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: | ||
| 11552 | { | ||
| 11553 | drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); | ||
| 11554 | } break; | ||
| 11555 | |||
| 11556 | case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: | ||
| 11557 | default: | ||
| 11558 | { | ||
| 11559 | drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); | ||
| 11560 | } break; | ||
| 11561 | } | ||
| 11562 | } else { | ||
| 11563 | /* Generic interleaving. */ | ||
| 11564 | drflac_uint64 i; | ||
| 11565 | for (i = 0; i < frameCountThisIteration; ++i) { | ||
| 11566 | unsigned int j; | ||
| 11567 | for (j = 0; j < channelCount; ++j) { | ||
| 11568 | drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); | ||
| 11569 | pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); | ||
| 11570 | } | ||
| 11571 | } | ||
| 11572 | } | ||
| 11573 | |||
| 11574 | framesRead += frameCountThisIteration; | ||
| 11575 | pBufferOut += frameCountThisIteration * channelCount; | ||
| 11576 | framesToRead -= frameCountThisIteration; | ||
| 11577 | pFlac->currentPCMFrame += frameCountThisIteration; | ||
| 11578 | pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; | ||
| 11579 | } | ||
| 11580 | } | ||
| 11581 | |||
| 11582 | return framesRead; | ||
| 11583 | } | ||
| 11584 | |||
| 11585 | |||
| 11586 | DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) | ||
| 11587 | { | ||
| 11588 | if (pFlac == NULL) { | ||
| 11589 | return DRFLAC_FALSE; | ||
| 11590 | } | ||
| 11591 | |||
| 11592 | /* Don't do anything if we're already on the seek point. */ | ||
| 11593 | if (pFlac->currentPCMFrame == pcmFrameIndex) { | ||
| 11594 | return DRFLAC_TRUE; | ||
| 11595 | } | ||
| 11596 | |||
| 11597 | /* | ||
| 11598 | If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present | ||
| 11599 | when the decoder was opened. | ||
| 11600 | */ | ||
| 11601 | if (pFlac->firstFLACFramePosInBytes == 0) { | ||
| 11602 | return DRFLAC_FALSE; | ||
| 11603 | } | ||
| 11604 | |||
| 11605 | if (pcmFrameIndex == 0) { | ||
| 11606 | pFlac->currentPCMFrame = 0; | ||
| 11607 | return drflac__seek_to_first_frame(pFlac); | ||
| 11608 | } else { | ||
| 11609 | drflac_bool32 wasSuccessful = DRFLAC_FALSE; | ||
| 11610 | drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame; | ||
| 11611 | |||
| 11612 | /* Clamp the sample to the end. */ | ||
| 11613 | if (pcmFrameIndex > pFlac->totalPCMFrameCount) { | ||
| 11614 | pcmFrameIndex = pFlac->totalPCMFrameCount; | ||
| 11615 | } | ||
| 11616 | |||
| 11617 | /* If the target sample and the current sample are in the same frame we just move the position forward. */ | ||
| 11618 | if (pcmFrameIndex > pFlac->currentPCMFrame) { | ||
| 11619 | /* Forward. */ | ||
| 11620 | drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); | ||
| 11621 | if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { | ||
| 11622 | pFlac->currentFLACFrame.pcmFramesRemaining -= offset; | ||
| 11623 | pFlac->currentPCMFrame = pcmFrameIndex; | ||
| 11624 | return DRFLAC_TRUE; | ||
| 11625 | } | ||
| 11626 | } else { | ||
| 11627 | /* Backward. */ | ||
| 11628 | drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); | ||
| 11629 | drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; | ||
| 11630 | drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; | ||
| 11631 | if (currentFLACFramePCMFramesConsumed > offsetAbs) { | ||
| 11632 | pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; | ||
| 11633 | pFlac->currentPCMFrame = pcmFrameIndex; | ||
| 11634 | return DRFLAC_TRUE; | ||
| 11635 | } | ||
| 11636 | } | ||
| 11637 | |||
| 11638 | /* | ||
| 11639 | Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so | ||
| 11640 | we'll instead use Ogg's natural seeking facility. | ||
| 11641 | */ | ||
| 11642 | #ifndef DR_FLAC_NO_OGG | ||
| 11643 | if (pFlac->container == drflac_container_ogg) | ||
| 11644 | { | ||
| 11645 | wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); | ||
| 11646 | } | ||
| 11647 | else | ||
| 11648 | #endif | ||
| 11649 | { | ||
| 11650 | /* First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. */ | ||
| 11651 | if (/*!wasSuccessful && */!pFlac->_noSeekTableSeek) { | ||
| 11652 | wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); | ||
| 11653 | } | ||
| 11654 | |||
| 11655 | #if !defined(DR_FLAC_NO_CRC) | ||
| 11656 | /* Fall back to binary search if seek table seeking fails. This requires the length of the stream to be known. */ | ||
| 11657 | if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { | ||
| 11658 | wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); | ||
| 11659 | } | ||
| 11660 | #endif | ||
| 11661 | |||
| 11662 | /* Fall back to brute force if all else fails. */ | ||
| 11663 | if (!wasSuccessful && !pFlac->_noBruteForceSeek) { | ||
| 11664 | wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); | ||
| 11665 | } | ||
| 11666 | } | ||
| 11667 | |||
| 11668 | if (wasSuccessful) { | ||
| 11669 | pFlac->currentPCMFrame = pcmFrameIndex; | ||
| 11670 | } else { | ||
| 11671 | /* Seek failed. Try putting the decoder back to it's original state. */ | ||
| 11672 | if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) { | ||
| 11673 | /* Failed to seek back to the original PCM frame. Fall back to 0. */ | ||
| 11674 | drflac_seek_to_pcm_frame(pFlac, 0); | ||
| 11675 | } | ||
| 11676 | } | ||
| 11677 | |||
| 11678 | return wasSuccessful; | ||
| 11679 | } | ||
| 11680 | } | ||
| 11681 | |||
| 11682 | |||
| 11683 | |||
| 11684 | /* High Level APIs */ | ||
| 11685 | |||
| 11686 | /* SIZE_MAX */ | ||
| 11687 | #if defined(SIZE_MAX) | ||
| 11688 | #define DRFLAC_SIZE_MAX SIZE_MAX | ||
| 11689 | #else | ||
| 11690 | #if defined(DRFLAC_64BIT) | ||
| 11691 | #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF) | ||
| 11692 | #else | ||
| 11693 | #define DRFLAC_SIZE_MAX 0xFFFFFFFF | ||
| 11694 | #endif | ||
| 11695 | #endif | ||
| 11696 | /* End SIZE_MAX */ | ||
| 11697 | |||
| 11698 | |||
| 11699 | /* Using a macro as the definition of the drflac__full_decode_and_close_*() API family. Sue me. */ | ||
| 11700 | #define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ | ||
| 11701 | static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\ | ||
| 11702 | { \ | ||
| 11703 | type* pSampleData = NULL; \ | ||
| 11704 | drflac_uint64 totalPCMFrameCount; \ | ||
| 11705 | \ | ||
| 11706 | DRFLAC_ASSERT(pFlac != NULL); \ | ||
| 11707 | \ | ||
| 11708 | totalPCMFrameCount = pFlac->totalPCMFrameCount; \ | ||
| 11709 | \ | ||
| 11710 | if (totalPCMFrameCount == 0) { \ | ||
| 11711 | type buffer[4096]; \ | ||
| 11712 | drflac_uint64 pcmFramesRead; \ | ||
| 11713 | size_t sampleDataBufferSize = sizeof(buffer); \ | ||
| 11714 | \ | ||
| 11715 | pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ | ||
| 11716 | if (pSampleData == NULL) { \ | ||
| 11717 | goto on_error; \ | ||
| 11718 | } \ | ||
| 11719 | \ | ||
| 11720 | while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ | ||
| 11721 | if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ | ||
| 11722 | type* pNewSampleData; \ | ||
| 11723 | size_t newSampleDataBufferSize; \ | ||
| 11724 | \ | ||
| 11725 | newSampleDataBufferSize = sampleDataBufferSize * 2; \ | ||
| 11726 | pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ | ||
| 11727 | if (pNewSampleData == NULL) { \ | ||
| 11728 | drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ | ||
| 11729 | goto on_error; \ | ||
| 11730 | } \ | ||
| 11731 | \ | ||
| 11732 | sampleDataBufferSize = newSampleDataBufferSize; \ | ||
| 11733 | pSampleData = pNewSampleData; \ | ||
| 11734 | } \ | ||
| 11735 | \ | ||
| 11736 | DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ | ||
| 11737 | totalPCMFrameCount += pcmFramesRead; \ | ||
| 11738 | } \ | ||
| 11739 | \ | ||
| 11740 | /* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \ | ||
| 11741 | protect those ears from random noise! */ \ | ||
| 11742 | DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ | ||
| 11743 | } else { \ | ||
| 11744 | drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ | ||
| 11745 | if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \ | ||
| 11746 | goto on_error; /* The decoded data is too big. */ \ | ||
| 11747 | } \ | ||
| 11748 | \ | ||
| 11749 | pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); /* <-- Safe cast as per the check above. */ \ | ||
| 11750 | if (pSampleData == NULL) { \ | ||
| 11751 | goto on_error; \ | ||
| 11752 | } \ | ||
| 11753 | \ | ||
| 11754 | totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ | ||
| 11755 | } \ | ||
| 11756 | \ | ||
| 11757 | if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ | ||
| 11758 | if (channelsOut) *channelsOut = pFlac->channels; \ | ||
| 11759 | if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ | ||
| 11760 | \ | ||
| 11761 | drflac_close(pFlac); \ | ||
| 11762 | return pSampleData; \ | ||
| 11763 | \ | ||
| 11764 | on_error: \ | ||
| 11765 | drflac_close(pFlac); \ | ||
| 11766 | return NULL; \ | ||
| 11767 | } | ||
| 11768 | |||
| 11769 | DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32) | ||
| 11770 | DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16) | ||
| 11771 | DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) | ||
| 11772 | |||
| 11773 | DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 11774 | { | ||
| 11775 | drflac* pFlac; | ||
| 11776 | |||
| 11777 | if (channelsOut) { | ||
| 11778 | *channelsOut = 0; | ||
| 11779 | } | ||
| 11780 | if (sampleRateOut) { | ||
| 11781 | *sampleRateOut = 0; | ||
| 11782 | } | ||
| 11783 | if (totalPCMFrameCountOut) { | ||
| 11784 | *totalPCMFrameCountOut = 0; | ||
| 11785 | } | ||
| 11786 | |||
| 11787 | pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); | ||
| 11788 | if (pFlac == NULL) { | ||
| 11789 | return NULL; | ||
| 11790 | } | ||
| 11791 | |||
| 11792 | return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); | ||
| 11793 | } | ||
| 11794 | |||
| 11795 | DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 11796 | { | ||
| 11797 | drflac* pFlac; | ||
| 11798 | |||
| 11799 | if (channelsOut) { | ||
| 11800 | *channelsOut = 0; | ||
| 11801 | } | ||
| 11802 | if (sampleRateOut) { | ||
| 11803 | *sampleRateOut = 0; | ||
| 11804 | } | ||
| 11805 | if (totalPCMFrameCountOut) { | ||
| 11806 | *totalPCMFrameCountOut = 0; | ||
| 11807 | } | ||
| 11808 | |||
| 11809 | pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); | ||
| 11810 | if (pFlac == NULL) { | ||
| 11811 | return NULL; | ||
| 11812 | } | ||
| 11813 | |||
| 11814 | return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); | ||
| 11815 | } | ||
| 11816 | |||
| 11817 | DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 11818 | { | ||
| 11819 | drflac* pFlac; | ||
| 11820 | |||
| 11821 | if (channelsOut) { | ||
| 11822 | *channelsOut = 0; | ||
| 11823 | } | ||
| 11824 | if (sampleRateOut) { | ||
| 11825 | *sampleRateOut = 0; | ||
| 11826 | } | ||
| 11827 | if (totalPCMFrameCountOut) { | ||
| 11828 | *totalPCMFrameCountOut = 0; | ||
| 11829 | } | ||
| 11830 | |||
| 11831 | pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); | ||
| 11832 | if (pFlac == NULL) { | ||
| 11833 | return NULL; | ||
| 11834 | } | ||
| 11835 | |||
| 11836 | return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); | ||
| 11837 | } | ||
| 11838 | |||
| 11839 | #ifndef DR_FLAC_NO_STDIO | ||
| 11840 | DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 11841 | { | ||
| 11842 | drflac* pFlac; | ||
| 11843 | |||
| 11844 | if (sampleRate) { | ||
| 11845 | *sampleRate = 0; | ||
| 11846 | } | ||
| 11847 | if (channels) { | ||
| 11848 | *channels = 0; | ||
| 11849 | } | ||
| 11850 | if (totalPCMFrameCount) { | ||
| 11851 | *totalPCMFrameCount = 0; | ||
| 11852 | } | ||
| 11853 | |||
| 11854 | pFlac = drflac_open_file(filename, pAllocationCallbacks); | ||
| 11855 | if (pFlac == NULL) { | ||
| 11856 | return NULL; | ||
| 11857 | } | ||
| 11858 | |||
| 11859 | return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); | ||
| 11860 | } | ||
| 11861 | |||
| 11862 | DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 11863 | { | ||
| 11864 | drflac* pFlac; | ||
| 11865 | |||
| 11866 | if (sampleRate) { | ||
| 11867 | *sampleRate = 0; | ||
| 11868 | } | ||
| 11869 | if (channels) { | ||
| 11870 | *channels = 0; | ||
| 11871 | } | ||
| 11872 | if (totalPCMFrameCount) { | ||
| 11873 | *totalPCMFrameCount = 0; | ||
| 11874 | } | ||
| 11875 | |||
| 11876 | pFlac = drflac_open_file(filename, pAllocationCallbacks); | ||
| 11877 | if (pFlac == NULL) { | ||
| 11878 | return NULL; | ||
| 11879 | } | ||
| 11880 | |||
| 11881 | return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); | ||
| 11882 | } | ||
| 11883 | |||
| 11884 | DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 11885 | { | ||
| 11886 | drflac* pFlac; | ||
| 11887 | |||
| 11888 | if (sampleRate) { | ||
| 11889 | *sampleRate = 0; | ||
| 11890 | } | ||
| 11891 | if (channels) { | ||
| 11892 | *channels = 0; | ||
| 11893 | } | ||
| 11894 | if (totalPCMFrameCount) { | ||
| 11895 | *totalPCMFrameCount = 0; | ||
| 11896 | } | ||
| 11897 | |||
| 11898 | pFlac = drflac_open_file(filename, pAllocationCallbacks); | ||
| 11899 | if (pFlac == NULL) { | ||
| 11900 | return NULL; | ||
| 11901 | } | ||
| 11902 | |||
| 11903 | return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); | ||
| 11904 | } | ||
| 11905 | #endif | ||
| 11906 | |||
| 11907 | DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 11908 | { | ||
| 11909 | drflac* pFlac; | ||
| 11910 | |||
| 11911 | if (sampleRate) { | ||
| 11912 | *sampleRate = 0; | ||
| 11913 | } | ||
| 11914 | if (channels) { | ||
| 11915 | *channels = 0; | ||
| 11916 | } | ||
| 11917 | if (totalPCMFrameCount) { | ||
| 11918 | *totalPCMFrameCount = 0; | ||
| 11919 | } | ||
| 11920 | |||
| 11921 | pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); | ||
| 11922 | if (pFlac == NULL) { | ||
| 11923 | return NULL; | ||
| 11924 | } | ||
| 11925 | |||
| 11926 | return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); | ||
| 11927 | } | ||
| 11928 | |||
| 11929 | DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 11930 | { | ||
| 11931 | drflac* pFlac; | ||
| 11932 | |||
| 11933 | if (sampleRate) { | ||
| 11934 | *sampleRate = 0; | ||
| 11935 | } | ||
| 11936 | if (channels) { | ||
| 11937 | *channels = 0; | ||
| 11938 | } | ||
| 11939 | if (totalPCMFrameCount) { | ||
| 11940 | *totalPCMFrameCount = 0; | ||
| 11941 | } | ||
| 11942 | |||
| 11943 | pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); | ||
| 11944 | if (pFlac == NULL) { | ||
| 11945 | return NULL; | ||
| 11946 | } | ||
| 11947 | |||
| 11948 | return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); | ||
| 11949 | } | ||
| 11950 | |||
| 11951 | DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 11952 | { | ||
| 11953 | drflac* pFlac; | ||
| 11954 | |||
| 11955 | if (sampleRate) { | ||
| 11956 | *sampleRate = 0; | ||
| 11957 | } | ||
| 11958 | if (channels) { | ||
| 11959 | *channels = 0; | ||
| 11960 | } | ||
| 11961 | if (totalPCMFrameCount) { | ||
| 11962 | *totalPCMFrameCount = 0; | ||
| 11963 | } | ||
| 11964 | |||
| 11965 | pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); | ||
| 11966 | if (pFlac == NULL) { | ||
| 11967 | return NULL; | ||
| 11968 | } | ||
| 11969 | |||
| 11970 | return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); | ||
| 11971 | } | ||
| 11972 | |||
| 11973 | |||
| 11974 | DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) | ||
| 11975 | { | ||
| 11976 | if (pAllocationCallbacks != NULL) { | ||
| 11977 | drflac__free_from_callbacks(p, pAllocationCallbacks); | ||
| 11978 | } else { | ||
| 11979 | drflac__free_default(p, NULL); | ||
| 11980 | } | ||
| 11981 | } | ||
| 11982 | |||
| 11983 | |||
| 11984 | |||
| 11985 | |||
| 11986 | DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments) | ||
| 11987 | { | ||
| 11988 | if (pIter == NULL) { | ||
| 11989 | return; | ||
| 11990 | } | ||
| 11991 | |||
| 11992 | pIter->countRemaining = commentCount; | ||
| 11993 | pIter->pRunningData = (const char*)pComments; | ||
| 11994 | } | ||
| 11995 | |||
| 11996 | DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) | ||
| 11997 | { | ||
| 11998 | drflac_int32 length; | ||
| 11999 | const char* pComment; | ||
| 12000 | |||
| 12001 | /* Safety. */ | ||
| 12002 | if (pCommentLengthOut) { | ||
| 12003 | *pCommentLengthOut = 0; | ||
| 12004 | } | ||
| 12005 | |||
| 12006 | if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { | ||
| 12007 | return NULL; | ||
| 12008 | } | ||
| 12009 | |||
| 12010 | length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData); | ||
| 12011 | pIter->pRunningData += 4; | ||
| 12012 | |||
| 12013 | pComment = pIter->pRunningData; | ||
| 12014 | pIter->pRunningData += length; | ||
| 12015 | pIter->countRemaining -= 1; | ||
| 12016 | |||
| 12017 | if (pCommentLengthOut) { | ||
| 12018 | *pCommentLengthOut = length; | ||
| 12019 | } | ||
| 12020 | |||
| 12021 | return pComment; | ||
| 12022 | } | ||
| 12023 | |||
| 12024 | |||
| 12025 | |||
| 12026 | |||
| 12027 | DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData) | ||
| 12028 | { | ||
| 12029 | if (pIter == NULL) { | ||
| 12030 | return; | ||
| 12031 | } | ||
| 12032 | |||
| 12033 | pIter->countRemaining = trackCount; | ||
| 12034 | pIter->pRunningData = (const char*)pTrackData; | ||
| 12035 | } | ||
| 12036 | |||
| 12037 | DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack) | ||
| 12038 | { | ||
| 12039 | drflac_cuesheet_track cuesheetTrack; | ||
| 12040 | const char* pRunningData; | ||
| 12041 | drflac_uint64 offsetHi; | ||
| 12042 | drflac_uint64 offsetLo; | ||
| 12043 | |||
| 12044 | if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { | ||
| 12045 | return DRFLAC_FALSE; | ||
| 12046 | } | ||
| 12047 | |||
| 12048 | pRunningData = pIter->pRunningData; | ||
| 12049 | |||
| 12050 | offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; | ||
| 12051 | offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; | ||
| 12052 | cuesheetTrack.offset = offsetLo | (offsetHi << 32); | ||
| 12053 | cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; | ||
| 12054 | DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; | ||
| 12055 | cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; | ||
| 12056 | cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; | ||
| 12057 | cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; | ||
| 12058 | cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index); | ||
| 12059 | |||
| 12060 | pIter->pRunningData = pRunningData; | ||
| 12061 | pIter->countRemaining -= 1; | ||
| 12062 | |||
| 12063 | if (pCuesheetTrack) { | ||
| 12064 | *pCuesheetTrack = cuesheetTrack; | ||
| 12065 | } | ||
| 12066 | |||
| 12067 | return DRFLAC_TRUE; | ||
| 12068 | } | ||
| 12069 | |||
| 12070 | #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) | ||
| 12071 | #pragma GCC diagnostic pop | ||
| 12072 | #endif | ||
| 12073 | #endif /* dr_flac_c */ | ||
| 12074 | #endif /* DR_FLAC_IMPLEMENTATION */ | ||
| 12075 | |||
| 12076 | |||
| 12077 | /* | ||
| 12078 | REVISION HISTORY | ||
| 12079 | ================ | ||
| 12080 | v0.12.42 - 2023-11-02 | ||
| 12081 | - Fix build for ARMv6-M. | ||
| 12082 | - Fix a compilation warning with GCC. | ||
| 12083 | |||
| 12084 | v0.12.41 - 2023-06-17 | ||
| 12085 | - Fix an incorrect date in revision history. No functional change. | ||
| 12086 | |||
| 12087 | v0.12.40 - 2023-05-22 | ||
| 12088 | - Minor code restructure. No functional change. | ||
| 12089 | |||
| 12090 | v0.12.39 - 2022-09-17 | ||
| 12091 | - Fix compilation with DJGPP. | ||
| 12092 | - Fix compilation error with Visual Studio 2019 and the ARM build. | ||
| 12093 | - Fix an error with SSE 4.1 detection. | ||
| 12094 | - Add support for disabling wchar_t with DR_WAV_NO_WCHAR. | ||
| 12095 | - Improve compatibility with compilers which lack support for explicit struct packing. | ||
| 12096 | - Improve compatibility with low-end and embedded hardware by reducing the amount of stack | ||
| 12097 | allocation when loading an Ogg encapsulated file. | ||
| 12098 | |||
| 12099 | v0.12.38 - 2022-04-10 | ||
| 12100 | - Fix compilation error on older versions of GCC. | ||
| 12101 | |||
| 12102 | v0.12.37 - 2022-02-12 | ||
| 12103 | - Improve ARM detection. | ||
| 12104 | |||
| 12105 | v0.12.36 - 2022-02-07 | ||
| 12106 | - Fix a compilation error with the ARM build. | ||
| 12107 | |||
| 12108 | v0.12.35 - 2022-02-06 | ||
| 12109 | - Fix a bug due to underestimating the amount of precision required for the prediction stage. | ||
| 12110 | - Fix some bugs found from fuzz testing. | ||
| 12111 | |||
| 12112 | v0.12.34 - 2022-01-07 | ||
| 12113 | - Fix some misalignment bugs when reading metadata. | ||
| 12114 | |||
| 12115 | v0.12.33 - 2021-12-22 | ||
| 12116 | - Fix a bug with seeking when the seek table does not start at PCM frame 0. | ||
| 12117 | |||
| 12118 | v0.12.32 - 2021-12-11 | ||
| 12119 | - Fix a warning with Clang. | ||
| 12120 | |||
| 12121 | v0.12.31 - 2021-08-16 | ||
| 12122 | - Silence some warnings. | ||
| 12123 | |||
| 12124 | v0.12.30 - 2021-07-31 | ||
| 12125 | - Fix platform detection for ARM64. | ||
| 12126 | |||
| 12127 | v0.12.29 - 2021-04-02 | ||
| 12128 | - Fix a bug where the running PCM frame index is set to an invalid value when over-seeking. | ||
| 12129 | - Fix a decoding error due to an incorrect validation check. | ||
| 12130 | |||
| 12131 | v0.12.28 - 2021-02-21 | ||
| 12132 | - Fix a warning due to referencing _MSC_VER when it is undefined. | ||
| 12133 | |||
| 12134 | v0.12.27 - 2021-01-31 | ||
| 12135 | - Fix a static analysis warning. | ||
| 12136 | |||
| 12137 | v0.12.26 - 2021-01-17 | ||
| 12138 | - Fix a compilation warning due to _BSD_SOURCE being deprecated. | ||
| 12139 | |||
| 12140 | v0.12.25 - 2020-12-26 | ||
| 12141 | - Update documentation. | ||
| 12142 | |||
| 12143 | v0.12.24 - 2020-11-29 | ||
| 12144 | - Fix ARM64/NEON detection when compiling with MSVC. | ||
| 12145 | |||
| 12146 | v0.12.23 - 2020-11-21 | ||
| 12147 | - Fix compilation with OpenWatcom. | ||
| 12148 | |||
| 12149 | v0.12.22 - 2020-11-01 | ||
| 12150 | - Fix an error with the previous release. | ||
| 12151 | |||
| 12152 | v0.12.21 - 2020-11-01 | ||
| 12153 | - Fix a possible deadlock when seeking. | ||
| 12154 | - Improve compiler support for older versions of GCC. | ||
| 12155 | |||
| 12156 | v0.12.20 - 2020-09-08 | ||
| 12157 | - Fix a compilation error on older compilers. | ||
| 12158 | |||
| 12159 | v0.12.19 - 2020-08-30 | ||
| 12160 | - Fix a bug due to an undefined 32-bit shift. | ||
| 12161 | |||
| 12162 | v0.12.18 - 2020-08-14 | ||
| 12163 | - Fix a crash when compiling with clang-cl. | ||
| 12164 | |||
| 12165 | v0.12.17 - 2020-08-02 | ||
| 12166 | - Simplify sized types. | ||
| 12167 | |||
| 12168 | v0.12.16 - 2020-07-25 | ||
| 12169 | - Fix a compilation warning. | ||
| 12170 | |||
| 12171 | v0.12.15 - 2020-07-06 | ||
| 12172 | - Check for negative LPC shifts and return an error. | ||
| 12173 | |||
| 12174 | v0.12.14 - 2020-06-23 | ||
| 12175 | - Add include guard for the implementation section. | ||
| 12176 | |||
| 12177 | v0.12.13 - 2020-05-16 | ||
| 12178 | - Add compile-time and run-time version querying. | ||
| 12179 | - DRFLAC_VERSION_MINOR | ||
| 12180 | - DRFLAC_VERSION_MAJOR | ||
| 12181 | - DRFLAC_VERSION_REVISION | ||
| 12182 | - DRFLAC_VERSION_STRING | ||
| 12183 | - drflac_version() | ||
| 12184 | - drflac_version_string() | ||
| 12185 | |||
| 12186 | v0.12.12 - 2020-04-30 | ||
| 12187 | - Fix compilation errors with VC6. | ||
| 12188 | |||
| 12189 | v0.12.11 - 2020-04-19 | ||
| 12190 | - Fix some pedantic warnings. | ||
| 12191 | - Fix some undefined behaviour warnings. | ||
| 12192 | |||
| 12193 | v0.12.10 - 2020-04-10 | ||
| 12194 | - Fix some bugs when trying to seek with an invalid seek table. | ||
| 12195 | |||
| 12196 | v0.12.9 - 2020-04-05 | ||
| 12197 | - Fix warnings. | ||
| 12198 | |||
| 12199 | v0.12.8 - 2020-04-04 | ||
| 12200 | - Add drflac_open_file_w() and drflac_open_file_with_metadata_w(). | ||
| 12201 | - Fix some static analysis warnings. | ||
| 12202 | - Minor documentation updates. | ||
| 12203 | |||
| 12204 | v0.12.7 - 2020-03-14 | ||
| 12205 | - Fix compilation errors with VC6. | ||
| 12206 | |||
| 12207 | v0.12.6 - 2020-03-07 | ||
| 12208 | - Fix compilation error with Visual Studio .NET 2003. | ||
| 12209 | |||
| 12210 | v0.12.5 - 2020-01-30 | ||
| 12211 | - Silence some static analysis warnings. | ||
| 12212 | |||
| 12213 | v0.12.4 - 2020-01-29 | ||
| 12214 | - Silence some static analysis warnings. | ||
| 12215 | |||
| 12216 | v0.12.3 - 2019-12-02 | ||
| 12217 | - Fix some warnings when compiling with GCC and the -Og flag. | ||
| 12218 | - Fix a crash in out-of-memory situations. | ||
| 12219 | - Fix potential integer overflow bug. | ||
| 12220 | - Fix some static analysis warnings. | ||
| 12221 | - Fix a possible crash when using custom memory allocators without a custom realloc() implementation. | ||
| 12222 | - Fix a bug with binary search seeking where the bits per sample is not a multiple of 8. | ||
| 12223 | |||
| 12224 | v0.12.2 - 2019-10-07 | ||
| 12225 | - Internal code clean up. | ||
| 12226 | |||
| 12227 | v0.12.1 - 2019-09-29 | ||
| 12228 | - Fix some Clang Static Analyzer warnings. | ||
| 12229 | - Fix an unused variable warning. | ||
| 12230 | |||
| 12231 | v0.12.0 - 2019-09-23 | ||
| 12232 | - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation | ||
| 12233 | routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs: | ||
| 12234 | - drflac_open() | ||
| 12235 | - drflac_open_relaxed() | ||
| 12236 | - drflac_open_with_metadata() | ||
| 12237 | - drflac_open_with_metadata_relaxed() | ||
| 12238 | - drflac_open_file() | ||
| 12239 | - drflac_open_file_with_metadata() | ||
| 12240 | - drflac_open_memory() | ||
| 12241 | - drflac_open_memory_with_metadata() | ||
| 12242 | - drflac_open_and_read_pcm_frames_s32() | ||
| 12243 | - drflac_open_and_read_pcm_frames_s16() | ||
| 12244 | - drflac_open_and_read_pcm_frames_f32() | ||
| 12245 | - drflac_open_file_and_read_pcm_frames_s32() | ||
| 12246 | - drflac_open_file_and_read_pcm_frames_s16() | ||
| 12247 | - drflac_open_file_and_read_pcm_frames_f32() | ||
| 12248 | - drflac_open_memory_and_read_pcm_frames_s32() | ||
| 12249 | - drflac_open_memory_and_read_pcm_frames_s16() | ||
| 12250 | - drflac_open_memory_and_read_pcm_frames_f32() | ||
| 12251 | Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use | ||
| 12252 | DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE. | ||
| 12253 | - Remove deprecated APIs: | ||
| 12254 | - drflac_read_s32() | ||
| 12255 | - drflac_read_s16() | ||
| 12256 | - drflac_read_f32() | ||
| 12257 | - drflac_seek_to_sample() | ||
| 12258 | - drflac_open_and_decode_s32() | ||
| 12259 | - drflac_open_and_decode_s16() | ||
| 12260 | - drflac_open_and_decode_f32() | ||
| 12261 | - drflac_open_and_decode_file_s32() | ||
| 12262 | - drflac_open_and_decode_file_s16() | ||
| 12263 | - drflac_open_and_decode_file_f32() | ||
| 12264 | - drflac_open_and_decode_memory_s32() | ||
| 12265 | - drflac_open_and_decode_memory_s16() | ||
| 12266 | - drflac_open_and_decode_memory_f32() | ||
| 12267 | - Remove drflac.totalSampleCount which is now replaced with drflac.totalPCMFrameCount. You can emulate drflac.totalSampleCount | ||
| 12268 | by doing pFlac->totalPCMFrameCount*pFlac->channels. | ||
| 12269 | - Rename drflac.currentFrame to drflac.currentFLACFrame to remove ambiguity with PCM frames. | ||
| 12270 | - Fix errors when seeking to the end of a stream. | ||
| 12271 | - Optimizations to seeking. | ||
| 12272 | - SSE improvements and optimizations. | ||
| 12273 | - ARM NEON optimizations. | ||
| 12274 | - Optimizations to drflac_read_pcm_frames_s16(). | ||
| 12275 | - Optimizations to drflac_read_pcm_frames_s32(). | ||
| 12276 | |||
| 12277 | v0.11.10 - 2019-06-26 | ||
| 12278 | - Fix a compiler error. | ||
| 12279 | |||
| 12280 | v0.11.9 - 2019-06-16 | ||
| 12281 | - Silence some ThreadSanitizer warnings. | ||
| 12282 | |||
| 12283 | v0.11.8 - 2019-05-21 | ||
| 12284 | - Fix warnings. | ||
| 12285 | |||
| 12286 | v0.11.7 - 2019-05-06 | ||
| 12287 | - C89 fixes. | ||
| 12288 | |||
| 12289 | v0.11.6 - 2019-05-05 | ||
| 12290 | - Add support for C89. | ||
| 12291 | - Fix a compiler warning when CRC is disabled. | ||
| 12292 | - Change license to choice of public domain or MIT-0. | ||
| 12293 | |||
| 12294 | v0.11.5 - 2019-04-19 | ||
| 12295 | - Fix a compiler error with GCC. | ||
| 12296 | |||
| 12297 | v0.11.4 - 2019-04-17 | ||
| 12298 | - Fix some warnings with GCC when compiling with -std=c99. | ||
| 12299 | |||
| 12300 | v0.11.3 - 2019-04-07 | ||
| 12301 | - Silence warnings with GCC. | ||
| 12302 | |||
| 12303 | v0.11.2 - 2019-03-10 | ||
| 12304 | - Fix a warning. | ||
| 12305 | |||
| 12306 | v0.11.1 - 2019-02-17 | ||
| 12307 | - Fix a potential bug with seeking. | ||
| 12308 | |||
| 12309 | v0.11.0 - 2018-12-16 | ||
| 12310 | - API CHANGE: Deprecated drflac_read_s32(), drflac_read_s16() and drflac_read_f32() and replaced them with | ||
| 12311 | drflac_read_pcm_frames_s32(), drflac_read_pcm_frames_s16() and drflac_read_pcm_frames_f32(). The new APIs take | ||
| 12312 | and return PCM frame counts instead of sample counts. To upgrade you will need to change the input count by | ||
| 12313 | dividing it by the channel count, and then do the same with the return value. | ||
| 12314 | - API_CHANGE: Deprecated drflac_seek_to_sample() and replaced with drflac_seek_to_pcm_frame(). Same rules as | ||
| 12315 | the changes to drflac_read_*() apply. | ||
| 12316 | - API CHANGE: Deprecated drflac_open_and_decode_*() and replaced with drflac_open_*_and_read_*(). Same rules as | ||
| 12317 | the changes to drflac_read_*() apply. | ||
| 12318 | - Optimizations. | ||
| 12319 | |||
| 12320 | v0.10.0 - 2018-09-11 | ||
| 12321 | - Remove the DR_FLAC_NO_WIN32_IO option and the Win32 file IO functionality. If you need to use Win32 file IO you | ||
| 12322 | need to do it yourself via the callback API. | ||
| 12323 | - Fix the clang build. | ||
| 12324 | - Fix undefined behavior. | ||
| 12325 | - Fix errors with CUESHEET metdata blocks. | ||
| 12326 | - Add an API for iterating over each cuesheet track in the CUESHEET metadata block. This works the same way as the | ||
| 12327 | Vorbis comment API. | ||
| 12328 | - Other miscellaneous bug fixes, mostly relating to invalid FLAC streams. | ||
| 12329 | - Minor optimizations. | ||
| 12330 | |||
| 12331 | v0.9.11 - 2018-08-29 | ||
| 12332 | - Fix a bug with sample reconstruction. | ||
| 12333 | |||
| 12334 | v0.9.10 - 2018-08-07 | ||
| 12335 | - Improve 64-bit detection. | ||
| 12336 | |||
| 12337 | v0.9.9 - 2018-08-05 | ||
| 12338 | - Fix C++ build on older versions of GCC. | ||
| 12339 | |||
| 12340 | v0.9.8 - 2018-07-24 | ||
| 12341 | - Fix compilation errors. | ||
| 12342 | |||
| 12343 | v0.9.7 - 2018-07-05 | ||
| 12344 | - Fix a warning. | ||
| 12345 | |||
| 12346 | v0.9.6 - 2018-06-29 | ||
| 12347 | - Fix some typos. | ||
| 12348 | |||
| 12349 | v0.9.5 - 2018-06-23 | ||
| 12350 | - Fix some warnings. | ||
| 12351 | |||
| 12352 | v0.9.4 - 2018-06-14 | ||
| 12353 | - Optimizations to seeking. | ||
| 12354 | - Clean up. | ||
| 12355 | |||
| 12356 | v0.9.3 - 2018-05-22 | ||
| 12357 | - Bug fix. | ||
| 12358 | |||
| 12359 | v0.9.2 - 2018-05-12 | ||
| 12360 | - Fix a compilation error due to a missing break statement. | ||
| 12361 | |||
| 12362 | v0.9.1 - 2018-04-29 | ||
| 12363 | - Fix compilation error with Clang. | ||
| 12364 | |||
| 12365 | v0.9 - 2018-04-24 | ||
| 12366 | - Fix Clang build. | ||
| 12367 | - Start using major.minor.revision versioning. | ||
| 12368 | |||
| 12369 | v0.8g - 2018-04-19 | ||
| 12370 | - Fix build on non-x86/x64 architectures. | ||
| 12371 | |||
| 12372 | v0.8f - 2018-02-02 | ||
| 12373 | - Stop pretending to support changing rate/channels mid stream. | ||
| 12374 | |||
| 12375 | v0.8e - 2018-02-01 | ||
| 12376 | - Fix a crash when the block size of a frame is larger than the maximum block size defined by the FLAC stream. | ||
| 12377 | - Fix a crash the the Rice partition order is invalid. | ||
| 12378 | |||
| 12379 | v0.8d - 2017-09-22 | ||
| 12380 | - Add support for decoding streams with ID3 tags. ID3 tags are just skipped. | ||
| 12381 | |||
| 12382 | v0.8c - 2017-09-07 | ||
| 12383 | - Fix warning on non-x86/x64 architectures. | ||
| 12384 | |||
| 12385 | v0.8b - 2017-08-19 | ||
| 12386 | - Fix build on non-x86/x64 architectures. | ||
| 12387 | |||
| 12388 | v0.8a - 2017-08-13 | ||
| 12389 | - A small optimization for the Clang build. | ||
| 12390 | |||
| 12391 | v0.8 - 2017-08-12 | ||
| 12392 | - API CHANGE: Rename dr_* types to drflac_*. | ||
| 12393 | - Optimizations. This brings dr_flac back to about the same class of efficiency as the reference implementation. | ||
| 12394 | - Add support for custom implementations of malloc(), realloc(), etc. | ||
| 12395 | - Add CRC checking to Ogg encapsulated streams. | ||
| 12396 | - Fix VC++ 6 build. This is only for the C++ compiler. The C compiler is not currently supported. | ||
| 12397 | - Bug fixes. | ||
| 12398 | |||
| 12399 | v0.7 - 2017-07-23 | ||
| 12400 | - Add support for opening a stream without a header block. To do this, use drflac_open_relaxed() / drflac_open_with_metadata_relaxed(). | ||
| 12401 | |||
| 12402 | v0.6 - 2017-07-22 | ||
| 12403 | - Add support for recovering from invalid frames. With this change, dr_flac will simply skip over invalid frames as if they | ||
| 12404 | never existed. Frames are checked against their sync code, the CRC-8 of the frame header and the CRC-16 of the whole frame. | ||
| 12405 | |||
| 12406 | v0.5 - 2017-07-16 | ||
| 12407 | - Fix typos. | ||
| 12408 | - Change drflac_bool* types to unsigned. | ||
| 12409 | - Add CRC checking. This makes dr_flac slower, but can be disabled with #define DR_FLAC_NO_CRC. | ||
| 12410 | |||
| 12411 | v0.4f - 2017-03-10 | ||
| 12412 | - Fix a couple of bugs with the bitstreaming code. | ||
| 12413 | |||
| 12414 | v0.4e - 2017-02-17 | ||
| 12415 | - Fix some warnings. | ||
| 12416 | |||
| 12417 | v0.4d - 2016-12-26 | ||
| 12418 | - Add support for 32-bit floating-point PCM decoding. | ||
| 12419 | - Use drflac_int* and drflac_uint* sized types to improve compiler support. | ||
| 12420 | - Minor improvements to documentation. | ||
| 12421 | |||
| 12422 | v0.4c - 2016-12-26 | ||
| 12423 | - Add support for signed 16-bit integer PCM decoding. | ||
| 12424 | |||
| 12425 | v0.4b - 2016-10-23 | ||
| 12426 | - A minor change to drflac_bool8 and drflac_bool32 types. | ||
| 12427 | |||
| 12428 | v0.4a - 2016-10-11 | ||
| 12429 | - Rename drBool32 to drflac_bool32 for styling consistency. | ||
| 12430 | |||
| 12431 | v0.4 - 2016-09-29 | ||
| 12432 | - API/ABI CHANGE: Use fixed size 32-bit booleans instead of the built-in bool type. | ||
| 12433 | - API CHANGE: Rename drflac_open_and_decode*() to drflac_open_and_decode*_s32(). | ||
| 12434 | - API CHANGE: Swap the order of "channels" and "sampleRate" parameters in drflac_open_and_decode*(). Rationale for this is to | ||
| 12435 | keep it consistent with drflac_audio. | ||
| 12436 | |||
| 12437 | v0.3f - 2016-09-21 | ||
| 12438 | - Fix a warning with GCC. | ||
| 12439 | |||
| 12440 | v0.3e - 2016-09-18 | ||
| 12441 | - Fixed a bug where GCC 4.3+ was not getting properly identified. | ||
| 12442 | - Fixed a few typos. | ||
| 12443 | - Changed date formats to ISO 8601 (YYYY-MM-DD). | ||
| 12444 | |||
| 12445 | v0.3d - 2016-06-11 | ||
| 12446 | - Minor clean up. | ||
| 12447 | |||
| 12448 | v0.3c - 2016-05-28 | ||
| 12449 | - Fixed compilation error. | ||
| 12450 | |||
| 12451 | v0.3b - 2016-05-16 | ||
| 12452 | - Fixed Linux/GCC build. | ||
| 12453 | - Updated documentation. | ||
| 12454 | |||
| 12455 | v0.3a - 2016-05-15 | ||
| 12456 | - Minor fixes to documentation. | ||
| 12457 | |||
| 12458 | v0.3 - 2016-05-11 | ||
| 12459 | - Optimizations. Now at about parity with the reference implementation on 32-bit builds. | ||
| 12460 | - Lots of clean up. | ||
| 12461 | |||
| 12462 | v0.2b - 2016-05-10 | ||
| 12463 | - Bug fixes. | ||
| 12464 | |||
| 12465 | v0.2a - 2016-05-10 | ||
| 12466 | - Made drflac_open_and_decode() more robust. | ||
| 12467 | - Removed an unused debugging variable | ||
| 12468 | |||
| 12469 | v0.2 - 2016-05-09 | ||
| 12470 | - Added support for Ogg encapsulation. | ||
| 12471 | - API CHANGE. Have the onSeek callback take a third argument which specifies whether or not the seek | ||
| 12472 | should be relative to the start or the current position. Also changes the seeking rules such that | ||
| 12473 | seeking offsets will never be negative. | ||
| 12474 | - Have drflac_open_and_decode() fail gracefully if the stream has an unknown total sample count. | ||
| 12475 | |||
| 12476 | v0.1b - 2016-05-07 | ||
| 12477 | - Properly close the file handle in drflac_open_file() and family when the decoder fails to initialize. | ||
| 12478 | - Removed a stale comment. | ||
| 12479 | |||
| 12480 | v0.1a - 2016-05-05 | ||
| 12481 | - Minor formatting changes. | ||
| 12482 | - Fixed a warning on the GCC build. | ||
| 12483 | |||
| 12484 | v0.1 - 2016-05-03 | ||
| 12485 | - Initial versioned release. | ||
| 12486 | */ | ||
| 12487 | |||
| 12488 | /* | ||
| 12489 | This software is available as a choice of the following licenses. Choose | ||
| 12490 | whichever you prefer. | ||
| 12491 | |||
| 12492 | =============================================================================== | ||
| 12493 | ALTERNATIVE 1 - Public Domain (www.unlicense.org) | ||
| 12494 | =============================================================================== | ||
| 12495 | This is free and unencumbered software released into the public domain. | ||
| 12496 | |||
| 12497 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this | ||
| 12498 | software, either in source code form or as a compiled binary, for any purpose, | ||
| 12499 | commercial or non-commercial, and by any means. | ||
| 12500 | |||
| 12501 | In jurisdictions that recognize copyright laws, the author or authors of this | ||
| 12502 | software dedicate any and all copyright interest in the software to the public | ||
| 12503 | domain. We make this dedication for the benefit of the public at large and to | ||
| 12504 | the detriment of our heirs and successors. We intend this dedication to be an | ||
| 12505 | overt act of relinquishment in perpetuity of all present and future rights to | ||
| 12506 | this software under copyright law. | ||
| 12507 | |||
| 12508 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 12509 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 12510 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 12511 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 12512 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
| 12513 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 12514 | |||
| 12515 | For more information, please refer to <http://unlicense.org/> | ||
| 12516 | |||
| 12517 | =============================================================================== | ||
| 12518 | ALTERNATIVE 2 - MIT No Attribution | ||
| 12519 | =============================================================================== | ||
| 12520 | Copyright 2023 David Reid | ||
| 12521 | |||
| 12522 | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| 12523 | this software and associated documentation files (the "Software"), to deal in | ||
| 12524 | the Software without restriction, including without limitation the rights to | ||
| 12525 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
| 12526 | of the Software, and to permit persons to whom the Software is furnished to do | ||
| 12527 | so. | ||
| 12528 | |||
| 12529 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 12530 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 12531 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 12532 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 12533 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 12534 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 12535 | SOFTWARE. | ||
| 12536 | */ | ||
diff --git a/raylib/src/external/dr_mp3.h b/raylib/src/external/dr_mp3.h new file mode 100644 index 0000000..e1a66d9 --- /dev/null +++ b/raylib/src/external/dr_mp3.h | |||
| @@ -0,0 +1,4837 @@ | |||
| 1 | /* | ||
| 2 | MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. | ||
| 3 | dr_mp3 - v0.6.39 - 2024-02-27 | ||
| 4 | |||
| 5 | David Reid - mackron@gmail.com | ||
| 6 | |||
| 7 | GitHub: https://github.com/mackron/dr_libs | ||
| 8 | |||
| 9 | Based on minimp3 (https://github.com/lieff/minimp3) which is where the real work was done. See the bottom of this file for differences between minimp3 and dr_mp3. | ||
| 10 | */ | ||
| 11 | |||
| 12 | /* | ||
| 13 | RELEASE NOTES - VERSION 0.6 | ||
| 14 | =========================== | ||
| 15 | Version 0.6 includes breaking changes with the configuration of decoders. The ability to customize the number of output channels and the sample rate has been | ||
| 16 | removed. You must now use the channel count and sample rate reported by the MP3 stream itself, and all channel and sample rate conversion must be done | ||
| 17 | yourself. | ||
| 18 | |||
| 19 | |||
| 20 | Changes to Initialization | ||
| 21 | ------------------------- | ||
| 22 | Previously, `drmp3_init()`, etc. took a pointer to a `drmp3_config` object that allowed you to customize the output channels and sample rate. This has been | ||
| 23 | removed. If you need the old behaviour you will need to convert the data yourself or just not upgrade. The following APIs have changed. | ||
| 24 | |||
| 25 | `drmp3_init()` | ||
| 26 | `drmp3_init_memory()` | ||
| 27 | `drmp3_init_file()` | ||
| 28 | |||
| 29 | |||
| 30 | Miscellaneous Changes | ||
| 31 | --------------------- | ||
| 32 | Support for loading a file from a `wchar_t` string has been added via the `drmp3_init_file_w()` API. | ||
| 33 | */ | ||
| 34 | |||
| 35 | /* | ||
| 36 | Introducation | ||
| 37 | ============= | ||
| 38 | dr_mp3 is a single file library. To use it, do something like the following in one .c file. | ||
| 39 | |||
| 40 | ```c | ||
| 41 | #define DR_MP3_IMPLEMENTATION | ||
| 42 | #include "dr_mp3.h" | ||
| 43 | ``` | ||
| 44 | |||
| 45 | You can then #include this file in other parts of the program as you would with any other header file. To decode audio data, do something like the following: | ||
| 46 | |||
| 47 | ```c | ||
| 48 | drmp3 mp3; | ||
| 49 | if (!drmp3_init_file(&mp3, "MySong.mp3", NULL)) { | ||
| 50 | // Failed to open file | ||
| 51 | } | ||
| 52 | |||
| 53 | ... | ||
| 54 | |||
| 55 | drmp3_uint64 framesRead = drmp3_read_pcm_frames_f32(pMP3, framesToRead, pFrames); | ||
| 56 | ``` | ||
| 57 | |||
| 58 | The drmp3 object is transparent so you can get access to the channel count and sample rate like so: | ||
| 59 | |||
| 60 | ``` | ||
| 61 | drmp3_uint32 channels = mp3.channels; | ||
| 62 | drmp3_uint32 sampleRate = mp3.sampleRate; | ||
| 63 | ``` | ||
| 64 | |||
| 65 | The example above initializes a decoder from a file, but you can also initialize it from a block of memory and read and seek callbacks with | ||
| 66 | `drmp3_init_memory()` and `drmp3_init()` respectively. | ||
| 67 | |||
| 68 | You do not need to do any annoying memory management when reading PCM frames - this is all managed internally. You can request any number of PCM frames in each | ||
| 69 | call to `drmp3_read_pcm_frames_f32()` and it will return as many PCM frames as it can, up to the requested amount. | ||
| 70 | |||
| 71 | You can also decode an entire file in one go with `drmp3_open_and_read_pcm_frames_f32()`, `drmp3_open_memory_and_read_pcm_frames_f32()` and | ||
| 72 | `drmp3_open_file_and_read_pcm_frames_f32()`. | ||
| 73 | |||
| 74 | |||
| 75 | Build Options | ||
| 76 | ============= | ||
| 77 | #define these options before including this file. | ||
| 78 | |||
| 79 | #define DR_MP3_NO_STDIO | ||
| 80 | Disable drmp3_init_file(), etc. | ||
| 81 | |||
| 82 | #define DR_MP3_NO_SIMD | ||
| 83 | Disable SIMD optimizations. | ||
| 84 | */ | ||
| 85 | |||
| 86 | #ifndef dr_mp3_h | ||
| 87 | #define dr_mp3_h | ||
| 88 | |||
| 89 | #ifdef __cplusplus | ||
| 90 | extern "C" { | ||
| 91 | #endif | ||
| 92 | |||
| 93 | #define DRMP3_STRINGIFY(x) #x | ||
| 94 | #define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) | ||
| 95 | |||
| 96 | #define DRMP3_VERSION_MAJOR 0 | ||
| 97 | #define DRMP3_VERSION_MINOR 6 | ||
| 98 | #define DRMP3_VERSION_REVISION 39 | ||
| 99 | #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) | ||
| 100 | |||
| 101 | #include <stddef.h> /* For size_t. */ | ||
| 102 | |||
| 103 | /* Sized Types */ | ||
| 104 | typedef signed char drmp3_int8; | ||
| 105 | typedef unsigned char drmp3_uint8; | ||
| 106 | typedef signed short drmp3_int16; | ||
| 107 | typedef unsigned short drmp3_uint16; | ||
| 108 | typedef signed int drmp3_int32; | ||
| 109 | typedef unsigned int drmp3_uint32; | ||
| 110 | #if defined(_MSC_VER) && !defined(__clang__) | ||
| 111 | typedef signed __int64 drmp3_int64; | ||
| 112 | typedef unsigned __int64 drmp3_uint64; | ||
| 113 | #else | ||
| 114 | #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) | ||
| 115 | #pragma GCC diagnostic push | ||
| 116 | #pragma GCC diagnostic ignored "-Wlong-long" | ||
| 117 | #if defined(__clang__) | ||
| 118 | #pragma GCC diagnostic ignored "-Wc++11-long-long" | ||
| 119 | #endif | ||
| 120 | #endif | ||
| 121 | typedef signed long long drmp3_int64; | ||
| 122 | typedef unsigned long long drmp3_uint64; | ||
| 123 | #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) | ||
| 124 | #pragma GCC diagnostic pop | ||
| 125 | #endif | ||
| 126 | #endif | ||
| 127 | #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) | ||
| 128 | typedef drmp3_uint64 drmp3_uintptr; | ||
| 129 | #else | ||
| 130 | typedef drmp3_uint32 drmp3_uintptr; | ||
| 131 | #endif | ||
| 132 | typedef drmp3_uint8 drmp3_bool8; | ||
| 133 | typedef drmp3_uint32 drmp3_bool32; | ||
| 134 | #define DRMP3_TRUE 1 | ||
| 135 | #define DRMP3_FALSE 0 | ||
| 136 | /* End Sized Types */ | ||
| 137 | |||
| 138 | /* Decorations */ | ||
| 139 | #if !defined(DRMP3_API) | ||
| 140 | #if defined(DRMP3_DLL) | ||
| 141 | #if defined(_WIN32) | ||
| 142 | #define DRMP3_DLL_IMPORT __declspec(dllimport) | ||
| 143 | #define DRMP3_DLL_EXPORT __declspec(dllexport) | ||
| 144 | #define DRMP3_DLL_PRIVATE static | ||
| 145 | #else | ||
| 146 | #if defined(__GNUC__) && __GNUC__ >= 4 | ||
| 147 | #define DRMP3_DLL_IMPORT __attribute__((visibility("default"))) | ||
| 148 | #define DRMP3_DLL_EXPORT __attribute__((visibility("default"))) | ||
| 149 | #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden"))) | ||
| 150 | #else | ||
| 151 | #define DRMP3_DLL_IMPORT | ||
| 152 | #define DRMP3_DLL_EXPORT | ||
| 153 | #define DRMP3_DLL_PRIVATE static | ||
| 154 | #endif | ||
| 155 | #endif | ||
| 156 | |||
| 157 | #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) | ||
| 158 | #define DRMP3_API DRMP3_DLL_EXPORT | ||
| 159 | #else | ||
| 160 | #define DRMP3_API DRMP3_DLL_IMPORT | ||
| 161 | #endif | ||
| 162 | #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE | ||
| 163 | #else | ||
| 164 | #define DRMP3_API extern | ||
| 165 | #define DRMP3_PRIVATE static | ||
| 166 | #endif | ||
| 167 | #endif | ||
| 168 | /* End Decorations */ | ||
| 169 | |||
| 170 | /* Result Codes */ | ||
| 171 | typedef drmp3_int32 drmp3_result; | ||
| 172 | #define DRMP3_SUCCESS 0 | ||
| 173 | #define DRMP3_ERROR -1 /* A generic error. */ | ||
| 174 | #define DRMP3_INVALID_ARGS -2 | ||
| 175 | #define DRMP3_INVALID_OPERATION -3 | ||
| 176 | #define DRMP3_OUT_OF_MEMORY -4 | ||
| 177 | #define DRMP3_OUT_OF_RANGE -5 | ||
| 178 | #define DRMP3_ACCESS_DENIED -6 | ||
| 179 | #define DRMP3_DOES_NOT_EXIST -7 | ||
| 180 | #define DRMP3_ALREADY_EXISTS -8 | ||
| 181 | #define DRMP3_TOO_MANY_OPEN_FILES -9 | ||
| 182 | #define DRMP3_INVALID_FILE -10 | ||
| 183 | #define DRMP3_TOO_BIG -11 | ||
| 184 | #define DRMP3_PATH_TOO_LONG -12 | ||
| 185 | #define DRMP3_NAME_TOO_LONG -13 | ||
| 186 | #define DRMP3_NOT_DIRECTORY -14 | ||
| 187 | #define DRMP3_IS_DIRECTORY -15 | ||
| 188 | #define DRMP3_DIRECTORY_NOT_EMPTY -16 | ||
| 189 | #define DRMP3_END_OF_FILE -17 | ||
| 190 | #define DRMP3_NO_SPACE -18 | ||
| 191 | #define DRMP3_BUSY -19 | ||
| 192 | #define DRMP3_IO_ERROR -20 | ||
| 193 | #define DRMP3_INTERRUPT -21 | ||
| 194 | #define DRMP3_UNAVAILABLE -22 | ||
| 195 | #define DRMP3_ALREADY_IN_USE -23 | ||
| 196 | #define DRMP3_BAD_ADDRESS -24 | ||
| 197 | #define DRMP3_BAD_SEEK -25 | ||
| 198 | #define DRMP3_BAD_PIPE -26 | ||
| 199 | #define DRMP3_DEADLOCK -27 | ||
| 200 | #define DRMP3_TOO_MANY_LINKS -28 | ||
| 201 | #define DRMP3_NOT_IMPLEMENTED -29 | ||
| 202 | #define DRMP3_NO_MESSAGE -30 | ||
| 203 | #define DRMP3_BAD_MESSAGE -31 | ||
| 204 | #define DRMP3_NO_DATA_AVAILABLE -32 | ||
| 205 | #define DRMP3_INVALID_DATA -33 | ||
| 206 | #define DRMP3_TIMEOUT -34 | ||
| 207 | #define DRMP3_NO_NETWORK -35 | ||
| 208 | #define DRMP3_NOT_UNIQUE -36 | ||
| 209 | #define DRMP3_NOT_SOCKET -37 | ||
| 210 | #define DRMP3_NO_ADDRESS -38 | ||
| 211 | #define DRMP3_BAD_PROTOCOL -39 | ||
| 212 | #define DRMP3_PROTOCOL_UNAVAILABLE -40 | ||
| 213 | #define DRMP3_PROTOCOL_NOT_SUPPORTED -41 | ||
| 214 | #define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42 | ||
| 215 | #define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43 | ||
| 216 | #define DRMP3_SOCKET_NOT_SUPPORTED -44 | ||
| 217 | #define DRMP3_CONNECTION_RESET -45 | ||
| 218 | #define DRMP3_ALREADY_CONNECTED -46 | ||
| 219 | #define DRMP3_NOT_CONNECTED -47 | ||
| 220 | #define DRMP3_CONNECTION_REFUSED -48 | ||
| 221 | #define DRMP3_NO_HOST -49 | ||
| 222 | #define DRMP3_IN_PROGRESS -50 | ||
| 223 | #define DRMP3_CANCELLED -51 | ||
| 224 | #define DRMP3_MEMORY_ALREADY_MAPPED -52 | ||
| 225 | #define DRMP3_AT_END -53 | ||
| 226 | /* End Result Codes */ | ||
| 227 | |||
| 228 | #define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 | ||
| 229 | #define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) | ||
| 230 | |||
| 231 | /* Inline */ | ||
| 232 | #ifdef _MSC_VER | ||
| 233 | #define DRMP3_INLINE __forceinline | ||
| 234 | #elif defined(__GNUC__) | ||
| 235 | /* | ||
| 236 | I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when | ||
| 237 | the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some | ||
| 238 | case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the | ||
| 239 | command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue | ||
| 240 | I am using "__inline__" only when we're compiling in strict ANSI mode. | ||
| 241 | */ | ||
| 242 | #if defined(__STRICT_ANSI__) | ||
| 243 | #define DRMP3_GNUC_INLINE_HINT __inline__ | ||
| 244 | #else | ||
| 245 | #define DRMP3_GNUC_INLINE_HINT inline | ||
| 246 | #endif | ||
| 247 | |||
| 248 | #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) | ||
| 249 | #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline)) | ||
| 250 | #else | ||
| 251 | #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT | ||
| 252 | #endif | ||
| 253 | #elif defined(__WATCOMC__) | ||
| 254 | #define DRMP3_INLINE __inline | ||
| 255 | #else | ||
| 256 | #define DRMP3_INLINE | ||
| 257 | #endif | ||
| 258 | /* End Inline */ | ||
| 259 | |||
| 260 | |||
| 261 | DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision); | ||
| 262 | DRMP3_API const char* drmp3_version_string(void); | ||
| 263 | |||
| 264 | |||
| 265 | /* Allocation Callbacks */ | ||
| 266 | typedef struct | ||
| 267 | { | ||
| 268 | void* pUserData; | ||
| 269 | void* (* onMalloc)(size_t sz, void* pUserData); | ||
| 270 | void* (* onRealloc)(void* p, size_t sz, void* pUserData); | ||
| 271 | void (* onFree)(void* p, void* pUserData); | ||
| 272 | } drmp3_allocation_callbacks; | ||
| 273 | /* End Allocation Callbacks */ | ||
| 274 | |||
| 275 | |||
| 276 | /* | ||
| 277 | Low Level Push API | ||
| 278 | ================== | ||
| 279 | */ | ||
| 280 | typedef struct | ||
| 281 | { | ||
| 282 | int frame_bytes, channels, hz, layer, bitrate_kbps; | ||
| 283 | } drmp3dec_frame_info; | ||
| 284 | |||
| 285 | typedef struct | ||
| 286 | { | ||
| 287 | float mdct_overlap[2][9*32], qmf_state[15*2*32]; | ||
| 288 | int reserv, free_format_bytes; | ||
| 289 | drmp3_uint8 header[4], reserv_buf[511]; | ||
| 290 | } drmp3dec; | ||
| 291 | |||
| 292 | /* Initializes a low level decoder. */ | ||
| 293 | DRMP3_API void drmp3dec_init(drmp3dec *dec); | ||
| 294 | |||
| 295 | /* Reads a frame from a low level decoder. */ | ||
| 296 | DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info); | ||
| 297 | |||
| 298 | /* Helper for converting between f32 and s16. */ | ||
| 299 | DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples); | ||
| 300 | |||
| 301 | |||
| 302 | |||
| 303 | /* | ||
| 304 | Main API (Pull API) | ||
| 305 | =================== | ||
| 306 | */ | ||
| 307 | typedef enum | ||
| 308 | { | ||
| 309 | drmp3_seek_origin_start, | ||
| 310 | drmp3_seek_origin_current | ||
| 311 | } drmp3_seek_origin; | ||
| 312 | |||
| 313 | typedef struct | ||
| 314 | { | ||
| 315 | drmp3_uint64 seekPosInBytes; /* Points to the first byte of an MP3 frame. */ | ||
| 316 | drmp3_uint64 pcmFrameIndex; /* The index of the PCM frame this seek point targets. */ | ||
| 317 | drmp3_uint16 mp3FramesToDiscard; /* The number of whole MP3 frames to be discarded before pcmFramesToDiscard. */ | ||
| 318 | drmp3_uint16 pcmFramesToDiscard; /* The number of leading samples to read and discard. These are discarded after mp3FramesToDiscard. */ | ||
| 319 | } drmp3_seek_point; | ||
| 320 | |||
| 321 | /* | ||
| 322 | Callback for when data is read. Return value is the number of bytes actually read. | ||
| 323 | |||
| 324 | pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family. | ||
| 325 | pBufferOut [out] The output buffer. | ||
| 326 | bytesToRead [in] The number of bytes to read. | ||
| 327 | |||
| 328 | Returns the number of bytes actually read. | ||
| 329 | |||
| 330 | A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until | ||
| 331 | either the entire bytesToRead is filled or you have reached the end of the stream. | ||
| 332 | */ | ||
| 333 | typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); | ||
| 334 | |||
| 335 | /* | ||
| 336 | Callback for when data needs to be seeked. | ||
| 337 | |||
| 338 | pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family. | ||
| 339 | offset [in] The number of bytes to move, relative to the origin. Will never be negative. | ||
| 340 | origin [in] The origin of the seek - the current position or the start of the stream. | ||
| 341 | |||
| 342 | Returns whether or not the seek was successful. | ||
| 343 | |||
| 344 | Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which | ||
| 345 | will be either drmp3_seek_origin_start or drmp3_seek_origin_current. | ||
| 346 | */ | ||
| 347 | typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin); | ||
| 348 | |||
| 349 | typedef struct | ||
| 350 | { | ||
| 351 | drmp3_uint32 channels; | ||
| 352 | drmp3_uint32 sampleRate; | ||
| 353 | } drmp3_config; | ||
| 354 | |||
| 355 | typedef struct | ||
| 356 | { | ||
| 357 | drmp3dec decoder; | ||
| 358 | drmp3_uint32 channels; | ||
| 359 | drmp3_uint32 sampleRate; | ||
| 360 | drmp3_read_proc onRead; | ||
| 361 | drmp3_seek_proc onSeek; | ||
| 362 | void* pUserData; | ||
| 363 | drmp3_allocation_callbacks allocationCallbacks; | ||
| 364 | drmp3_uint32 mp3FrameChannels; /* The number of channels in the currently loaded MP3 frame. Internal use only. */ | ||
| 365 | drmp3_uint32 mp3FrameSampleRate; /* The sample rate of the currently loaded MP3 frame. Internal use only. */ | ||
| 366 | drmp3_uint32 pcmFramesConsumedInMP3Frame; | ||
| 367 | drmp3_uint32 pcmFramesRemainingInMP3Frame; | ||
| 368 | drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; /* <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT. */ | ||
| 369 | drmp3_uint64 currentPCMFrame; /* The current PCM frame, globally, based on the output sample rate. Mainly used for seeking. */ | ||
| 370 | drmp3_uint64 streamCursor; /* The current byte the decoder is sitting on in the raw stream. */ | ||
| 371 | drmp3_seek_point* pSeekPoints; /* NULL by default. Set with drmp3_bind_seek_table(). Memory is owned by the client. dr_mp3 will never attempt to free this pointer. */ | ||
| 372 | drmp3_uint32 seekPointCount; /* The number of items in pSeekPoints. When set to 0 assumes to no seek table. Defaults to zero. */ | ||
| 373 | size_t dataSize; | ||
| 374 | size_t dataCapacity; | ||
| 375 | size_t dataConsumed; | ||
| 376 | drmp3_uint8* pData; | ||
| 377 | drmp3_bool32 atEnd : 1; | ||
| 378 | struct | ||
| 379 | { | ||
| 380 | const drmp3_uint8* pData; | ||
| 381 | size_t dataSize; | ||
| 382 | size_t currentReadPos; | ||
| 383 | } memory; /* Only used for decoders that were opened against a block of memory. */ | ||
| 384 | } drmp3; | ||
| 385 | |||
| 386 | /* | ||
| 387 | Initializes an MP3 decoder. | ||
| 388 | |||
| 389 | onRead [in] The function to call when data needs to be read from the client. | ||
| 390 | onSeek [in] The function to call when the read position of the client data needs to move. | ||
| 391 | pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. | ||
| 392 | |||
| 393 | Returns true if successful; false otherwise. | ||
| 394 | |||
| 395 | Close the loader with drmp3_uninit(). | ||
| 396 | |||
| 397 | See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit() | ||
| 398 | */ | ||
| 399 | DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks); | ||
| 400 | |||
| 401 | /* | ||
| 402 | Initializes an MP3 decoder from a block of memory. | ||
| 403 | |||
| 404 | This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for | ||
| 405 | the lifetime of the drmp3 object. | ||
| 406 | |||
| 407 | The buffer should contain the contents of the entire MP3 file. | ||
| 408 | */ | ||
| 409 | DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks); | ||
| 410 | |||
| 411 | #ifndef DR_MP3_NO_STDIO | ||
| 412 | /* | ||
| 413 | Initializes an MP3 decoder from a file. | ||
| 414 | |||
| 415 | This holds the internal FILE object until drmp3_uninit() is called. Keep this in mind if you're caching drmp3 | ||
| 416 | objects because the operating system may restrict the number of file handles an application can have open at | ||
| 417 | any given time. | ||
| 418 | */ | ||
| 419 | DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); | ||
| 420 | DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); | ||
| 421 | #endif | ||
| 422 | |||
| 423 | /* | ||
| 424 | Uninitializes an MP3 decoder. | ||
| 425 | */ | ||
| 426 | DRMP3_API void drmp3_uninit(drmp3* pMP3); | ||
| 427 | |||
| 428 | /* | ||
| 429 | Reads PCM frames as interleaved 32-bit IEEE floating point PCM. | ||
| 430 | |||
| 431 | Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames. | ||
| 432 | */ | ||
| 433 | DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut); | ||
| 434 | |||
| 435 | /* | ||
| 436 | Reads PCM frames as interleaved signed 16-bit integer PCM. | ||
| 437 | |||
| 438 | Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames. | ||
| 439 | */ | ||
| 440 | DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut); | ||
| 441 | |||
| 442 | /* | ||
| 443 | Seeks to a specific frame. | ||
| 444 | |||
| 445 | Note that this is _not_ an MP3 frame, but rather a PCM frame. | ||
| 446 | */ | ||
| 447 | DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex); | ||
| 448 | |||
| 449 | /* | ||
| 450 | Calculates the total number of PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet | ||
| 451 | radio. Runs in linear time. Returns 0 on error. | ||
| 452 | */ | ||
| 453 | DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3); | ||
| 454 | |||
| 455 | /* | ||
| 456 | Calculates the total number of MP3 frames in the MP3 stream. Cannot be used for infinite streams such as internet | ||
| 457 | radio. Runs in linear time. Returns 0 on error. | ||
| 458 | */ | ||
| 459 | DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3); | ||
| 460 | |||
| 461 | /* | ||
| 462 | Calculates the total number of MP3 and PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet | ||
| 463 | radio. Runs in linear time. Returns 0 on error. | ||
| 464 | |||
| 465 | This is equivalent to calling drmp3_get_mp3_frame_count() and drmp3_get_pcm_frame_count() except that it's more efficient. | ||
| 466 | */ | ||
| 467 | DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount); | ||
| 468 | |||
| 469 | /* | ||
| 470 | Calculates the seekpoints based on PCM frames. This is slow. | ||
| 471 | |||
| 472 | pSeekpoint count is a pointer to a uint32 containing the seekpoint count. On input it contains the desired count. | ||
| 473 | On output it contains the actual count. The reason for this design is that the client may request too many | ||
| 474 | seekpoints, in which case dr_mp3 will return a corrected count. | ||
| 475 | |||
| 476 | Note that seektable seeking is not quite sample exact when the MP3 stream contains inconsistent sample rates. | ||
| 477 | */ | ||
| 478 | DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints); | ||
| 479 | |||
| 480 | /* | ||
| 481 | Binds a seek table to the decoder. | ||
| 482 | |||
| 483 | This does _not_ make a copy of pSeekPoints - it only references it. It is up to the application to ensure this | ||
| 484 | remains valid while it is bound to the decoder. | ||
| 485 | |||
| 486 | Use drmp3_calculate_seek_points() to calculate the seek points. | ||
| 487 | */ | ||
| 488 | DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints); | ||
| 489 | |||
| 490 | |||
| 491 | /* | ||
| 492 | Opens an decodes an entire MP3 stream as a single operation. | ||
| 493 | |||
| 494 | On output pConfig will receive the channel count and sample rate of the stream. | ||
| 495 | |||
| 496 | Free the returned pointer with drmp3_free(). | ||
| 497 | */ | ||
| 498 | DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); | ||
| 499 | DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); | ||
| 500 | |||
| 501 | DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); | ||
| 502 | DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); | ||
| 503 | |||
| 504 | #ifndef DR_MP3_NO_STDIO | ||
| 505 | DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); | ||
| 506 | DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); | ||
| 507 | #endif | ||
| 508 | |||
| 509 | /* | ||
| 510 | Allocates a block of memory on the heap. | ||
| 511 | */ | ||
| 512 | DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks); | ||
| 513 | |||
| 514 | /* | ||
| 515 | Frees any memory that was allocated by a public drmp3 API. | ||
| 516 | */ | ||
| 517 | DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks); | ||
| 518 | |||
| 519 | #ifdef __cplusplus | ||
| 520 | } | ||
| 521 | #endif | ||
| 522 | #endif /* dr_mp3_h */ | ||
| 523 | |||
| 524 | |||
| 525 | /************************************************************************************************************************************************************ | ||
| 526 | ************************************************************************************************************************************************************ | ||
| 527 | |||
| 528 | IMPLEMENTATION | ||
| 529 | |||
| 530 | ************************************************************************************************************************************************************ | ||
| 531 | ************************************************************************************************************************************************************/ | ||
| 532 | #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) | ||
| 533 | #ifndef dr_mp3_c | ||
| 534 | #define dr_mp3_c | ||
| 535 | |||
| 536 | #include <stdlib.h> | ||
| 537 | #include <string.h> | ||
| 538 | #include <limits.h> /* For INT_MAX */ | ||
| 539 | |||
| 540 | DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision) | ||
| 541 | { | ||
| 542 | if (pMajor) { | ||
| 543 | *pMajor = DRMP3_VERSION_MAJOR; | ||
| 544 | } | ||
| 545 | |||
| 546 | if (pMinor) { | ||
| 547 | *pMinor = DRMP3_VERSION_MINOR; | ||
| 548 | } | ||
| 549 | |||
| 550 | if (pRevision) { | ||
| 551 | *pRevision = DRMP3_VERSION_REVISION; | ||
| 552 | } | ||
| 553 | } | ||
| 554 | |||
| 555 | DRMP3_API const char* drmp3_version_string(void) | ||
| 556 | { | ||
| 557 | return DRMP3_VERSION_STRING; | ||
| 558 | } | ||
| 559 | |||
| 560 | /* Disable SIMD when compiling with TCC for now. */ | ||
| 561 | #if defined(__TINYC__) | ||
| 562 | #define DR_MP3_NO_SIMD | ||
| 563 | #endif | ||
| 564 | |||
| 565 | #define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) | ||
| 566 | |||
| 567 | #define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ | ||
| 568 | #ifndef DRMP3_MAX_FRAME_SYNC_MATCHES | ||
| 569 | #define DRMP3_MAX_FRAME_SYNC_MATCHES 10 | ||
| 570 | #endif | ||
| 571 | |||
| 572 | #define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ | ||
| 573 | |||
| 574 | #define DRMP3_MAX_BITRESERVOIR_BYTES 511 | ||
| 575 | #define DRMP3_SHORT_BLOCK_TYPE 2 | ||
| 576 | #define DRMP3_STOP_BLOCK_TYPE 3 | ||
| 577 | #define DRMP3_MODE_MONO 3 | ||
| 578 | #define DRMP3_MODE_JOINT_STEREO 1 | ||
| 579 | #define DRMP3_HDR_SIZE 4 | ||
| 580 | #define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) | ||
| 581 | #define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) | ||
| 582 | #define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) | ||
| 583 | #define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1)) | ||
| 584 | #define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) | ||
| 585 | #define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) | ||
| 586 | #define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) | ||
| 587 | #define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) | ||
| 588 | #define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) | ||
| 589 | #define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) | ||
| 590 | #define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) | ||
| 591 | #define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) | ||
| 592 | #define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) | ||
| 593 | #define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) | ||
| 594 | #define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) | ||
| 595 | #define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) | ||
| 596 | #define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) | ||
| 597 | |||
| 598 | #define DRMP3_BITS_DEQUANTIZER_OUT -1 | ||
| 599 | #define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210) | ||
| 600 | #define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3) | ||
| 601 | |||
| 602 | #define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) | ||
| 603 | #define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) | ||
| 604 | |||
| 605 | #if !defined(DR_MP3_NO_SIMD) | ||
| 606 | |||
| 607 | #if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) | ||
| 608 | /* x64 always have SSE2, arm64 always have neon, no need for generic code */ | ||
| 609 | #define DR_MP3_ONLY_SIMD | ||
| 610 | #endif | ||
| 611 | |||
| 612 | #if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) | ||
| 613 | #if defined(_MSC_VER) | ||
| 614 | #include <intrin.h> | ||
| 615 | #endif | ||
| 616 | #include <emmintrin.h> | ||
| 617 | #define DRMP3_HAVE_SSE 1 | ||
| 618 | #define DRMP3_HAVE_SIMD 1 | ||
| 619 | #define DRMP3_VSTORE _mm_storeu_ps | ||
| 620 | #define DRMP3_VLD _mm_loadu_ps | ||
| 621 | #define DRMP3_VSET _mm_set1_ps | ||
| 622 | #define DRMP3_VADD _mm_add_ps | ||
| 623 | #define DRMP3_VSUB _mm_sub_ps | ||
| 624 | #define DRMP3_VMUL _mm_mul_ps | ||
| 625 | #define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) | ||
| 626 | #define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) | ||
| 627 | #define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) | ||
| 628 | #define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) | ||
| 629 | typedef __m128 drmp3_f4; | ||
| 630 | #if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) | ||
| 631 | #define drmp3_cpuid __cpuid | ||
| 632 | #else | ||
| 633 | static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) | ||
| 634 | { | ||
| 635 | #if defined(__PIC__) | ||
| 636 | __asm__ __volatile__( | ||
| 637 | #if defined(__x86_64__) | ||
| 638 | "push %%rbx\n" | ||
| 639 | "cpuid\n" | ||
| 640 | "xchgl %%ebx, %1\n" | ||
| 641 | "pop %%rbx\n" | ||
| 642 | #else | ||
| 643 | "xchgl %%ebx, %1\n" | ||
| 644 | "cpuid\n" | ||
| 645 | "xchgl %%ebx, %1\n" | ||
| 646 | #endif | ||
| 647 | : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) | ||
| 648 | : "a" (InfoType)); | ||
| 649 | #else | ||
| 650 | __asm__ __volatile__( | ||
| 651 | "cpuid" | ||
| 652 | : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) | ||
| 653 | : "a" (InfoType)); | ||
| 654 | #endif | ||
| 655 | } | ||
| 656 | #endif | ||
| 657 | static int drmp3_have_simd(void) | ||
| 658 | { | ||
| 659 | #ifdef DR_MP3_ONLY_SIMD | ||
| 660 | return 1; | ||
| 661 | #else | ||
| 662 | static int g_have_simd; | ||
| 663 | int CPUInfo[4]; | ||
| 664 | #ifdef MINIMP3_TEST | ||
| 665 | static int g_counter; | ||
| 666 | if (g_counter++ > 100) | ||
| 667 | return 0; | ||
| 668 | #endif | ||
| 669 | if (g_have_simd) | ||
| 670 | goto end; | ||
| 671 | drmp3_cpuid(CPUInfo, 0); | ||
| 672 | if (CPUInfo[0] > 0) | ||
| 673 | { | ||
| 674 | drmp3_cpuid(CPUInfo, 1); | ||
| 675 | g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; /* SSE2 */ | ||
| 676 | return g_have_simd - 1; | ||
| 677 | } | ||
| 678 | |||
| 679 | end: | ||
| 680 | return g_have_simd - 1; | ||
| 681 | #endif | ||
| 682 | } | ||
| 683 | #elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) | ||
| 684 | #include <arm_neon.h> | ||
| 685 | #define DRMP3_HAVE_SSE 0 | ||
| 686 | #define DRMP3_HAVE_SIMD 1 | ||
| 687 | #define DRMP3_VSTORE vst1q_f32 | ||
| 688 | #define DRMP3_VLD vld1q_f32 | ||
| 689 | #define DRMP3_VSET vmovq_n_f32 | ||
| 690 | #define DRMP3_VADD vaddq_f32 | ||
| 691 | #define DRMP3_VSUB vsubq_f32 | ||
| 692 | #define DRMP3_VMUL vmulq_f32 | ||
| 693 | #define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y) | ||
| 694 | #define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y) | ||
| 695 | #define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) | ||
| 696 | #define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) | ||
| 697 | typedef float32x4_t drmp3_f4; | ||
| 698 | static int drmp3_have_simd(void) | ||
| 699 | { /* TODO: detect neon for !DR_MP3_ONLY_SIMD */ | ||
| 700 | return 1; | ||
| 701 | } | ||
| 702 | #else | ||
| 703 | #define DRMP3_HAVE_SSE 0 | ||
| 704 | #define DRMP3_HAVE_SIMD 0 | ||
| 705 | #ifdef DR_MP3_ONLY_SIMD | ||
| 706 | #error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled | ||
| 707 | #endif | ||
| 708 | #endif | ||
| 709 | |||
| 710 | #else | ||
| 711 | |||
| 712 | #define DRMP3_HAVE_SIMD 0 | ||
| 713 | |||
| 714 | #endif | ||
| 715 | |||
| 716 | #if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(__ARM_ARCH_6M__) | ||
| 717 | #define DRMP3_HAVE_ARMV6 1 | ||
| 718 | static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a) | ||
| 719 | { | ||
| 720 | drmp3_int32 x = 0; | ||
| 721 | __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); | ||
| 722 | return x; | ||
| 723 | } | ||
| 724 | #else | ||
| 725 | #define DRMP3_HAVE_ARMV6 0 | ||
| 726 | #endif | ||
| 727 | |||
| 728 | |||
| 729 | /* Standard library stuff. */ | ||
| 730 | #ifndef DRMP3_ASSERT | ||
| 731 | #include <assert.h> | ||
| 732 | #define DRMP3_ASSERT(expression) assert(expression) | ||
| 733 | #endif | ||
| 734 | #ifndef DRMP3_COPY_MEMORY | ||
| 735 | #define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) | ||
| 736 | #endif | ||
| 737 | #ifndef DRMP3_MOVE_MEMORY | ||
| 738 | #define DRMP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) | ||
| 739 | #endif | ||
| 740 | #ifndef DRMP3_ZERO_MEMORY | ||
| 741 | #define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) | ||
| 742 | #endif | ||
| 743 | #define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p))) | ||
| 744 | #ifndef DRMP3_MALLOC | ||
| 745 | #define DRMP3_MALLOC(sz) malloc((sz)) | ||
| 746 | #endif | ||
| 747 | #ifndef DRMP3_REALLOC | ||
| 748 | #define DRMP3_REALLOC(p, sz) realloc((p), (sz)) | ||
| 749 | #endif | ||
| 750 | #ifndef DRMP3_FREE | ||
| 751 | #define DRMP3_FREE(p) free((p)) | ||
| 752 | #endif | ||
| 753 | |||
| 754 | typedef struct | ||
| 755 | { | ||
| 756 | const drmp3_uint8 *buf; | ||
| 757 | int pos, limit; | ||
| 758 | } drmp3_bs; | ||
| 759 | |||
| 760 | typedef struct | ||
| 761 | { | ||
| 762 | float scf[3*64]; | ||
| 763 | drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; | ||
| 764 | } drmp3_L12_scale_info; | ||
| 765 | |||
| 766 | typedef struct | ||
| 767 | { | ||
| 768 | drmp3_uint8 tab_offset, code_tab_width, band_count; | ||
| 769 | } drmp3_L12_subband_alloc; | ||
| 770 | |||
| 771 | typedef struct | ||
| 772 | { | ||
| 773 | const drmp3_uint8 *sfbtab; | ||
| 774 | drmp3_uint16 part_23_length, big_values, scalefac_compress; | ||
| 775 | drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; | ||
| 776 | drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; | ||
| 777 | drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; | ||
| 778 | } drmp3_L3_gr_info; | ||
| 779 | |||
| 780 | typedef struct | ||
| 781 | { | ||
| 782 | drmp3_bs bs; | ||
| 783 | drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; | ||
| 784 | drmp3_L3_gr_info gr_info[4]; | ||
| 785 | float grbuf[2][576], scf[40], syn[18 + 15][2*32]; | ||
| 786 | drmp3_uint8 ist_pos[2][39]; | ||
| 787 | } drmp3dec_scratch; | ||
| 788 | |||
| 789 | static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) | ||
| 790 | { | ||
| 791 | bs->buf = data; | ||
| 792 | bs->pos = 0; | ||
| 793 | bs->limit = bytes*8; | ||
| 794 | } | ||
| 795 | |||
| 796 | static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n) | ||
| 797 | { | ||
| 798 | drmp3_uint32 next, cache = 0, s = bs->pos & 7; | ||
| 799 | int shl = n + s; | ||
| 800 | const drmp3_uint8 *p = bs->buf + (bs->pos >> 3); | ||
| 801 | if ((bs->pos += n) > bs->limit) | ||
| 802 | return 0; | ||
| 803 | next = *p++ & (255 >> s); | ||
| 804 | while ((shl -= 8) > 0) | ||
| 805 | { | ||
| 806 | cache |= next << shl; | ||
| 807 | next = *p++; | ||
| 808 | } | ||
| 809 | return cache | (next >> -shl); | ||
| 810 | } | ||
| 811 | |||
| 812 | static int drmp3_hdr_valid(const drmp3_uint8 *h) | ||
| 813 | { | ||
| 814 | return h[0] == 0xff && | ||
| 815 | ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && | ||
| 816 | (DRMP3_HDR_GET_LAYER(h) != 0) && | ||
| 817 | (DRMP3_HDR_GET_BITRATE(h) != 15) && | ||
| 818 | (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3); | ||
| 819 | } | ||
| 820 | |||
| 821 | static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2) | ||
| 822 | { | ||
| 823 | return drmp3_hdr_valid(h2) && | ||
| 824 | ((h1[1] ^ h2[1]) & 0xFE) == 0 && | ||
| 825 | ((h1[2] ^ h2[2]) & 0x0C) == 0 && | ||
| 826 | !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2)); | ||
| 827 | } | ||
| 828 | |||
| 829 | static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h) | ||
| 830 | { | ||
| 831 | static const drmp3_uint8 halfrate[2][3][15] = { | ||
| 832 | { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, | ||
| 833 | { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, | ||
| 834 | }; | ||
| 835 | return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)]; | ||
| 836 | } | ||
| 837 | |||
| 838 | static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h) | ||
| 839 | { | ||
| 840 | static const unsigned g_hz[3] = { 44100, 48000, 32000 }; | ||
| 841 | return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h); | ||
| 842 | } | ||
| 843 | |||
| 844 | static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h) | ||
| 845 | { | ||
| 846 | return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h)); | ||
| 847 | } | ||
| 848 | |||
| 849 | static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size) | ||
| 850 | { | ||
| 851 | int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h); | ||
| 852 | if (DRMP3_HDR_IS_LAYER_1(h)) | ||
| 853 | { | ||
| 854 | frame_bytes &= ~3; /* slot align */ | ||
| 855 | } | ||
| 856 | return frame_bytes ? frame_bytes : free_format_size; | ||
| 857 | } | ||
| 858 | |||
| 859 | static int drmp3_hdr_padding(const drmp3_uint8 *h) | ||
| 860 | { | ||
| 861 | return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; | ||
| 862 | } | ||
| 863 | |||
| 864 | #ifndef DR_MP3_ONLY_MP3 | ||
| 865 | static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci) | ||
| 866 | { | ||
| 867 | const drmp3_L12_subband_alloc *alloc; | ||
| 868 | int mode = DRMP3_HDR_GET_STEREO_MODE(hdr); | ||
| 869 | int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; | ||
| 870 | |||
| 871 | if (DRMP3_HDR_IS_LAYER_1(hdr)) | ||
| 872 | { | ||
| 873 | static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; | ||
| 874 | alloc = g_alloc_L1; | ||
| 875 | nbands = 32; | ||
| 876 | } else if (!DRMP3_HDR_TEST_MPEG1(hdr)) | ||
| 877 | { | ||
| 878 | static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; | ||
| 879 | alloc = g_alloc_L2M2; | ||
| 880 | nbands = 30; | ||
| 881 | } else | ||
| 882 | { | ||
| 883 | static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; | ||
| 884 | int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr); | ||
| 885 | unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO); | ||
| 886 | if (!kbps) /* free-format */ | ||
| 887 | { | ||
| 888 | kbps = 192; | ||
| 889 | } | ||
| 890 | |||
| 891 | alloc = g_alloc_L2M1; | ||
| 892 | nbands = 27; | ||
| 893 | if (kbps < 56) | ||
| 894 | { | ||
| 895 | static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; | ||
| 896 | alloc = g_alloc_L2M1_lowrate; | ||
| 897 | nbands = sample_rate_idx == 2 ? 12 : 8; | ||
| 898 | } else if (kbps >= 96 && sample_rate_idx != 1) | ||
| 899 | { | ||
| 900 | nbands = 30; | ||
| 901 | } | ||
| 902 | } | ||
| 903 | |||
| 904 | sci->total_bands = (drmp3_uint8)nbands; | ||
| 905 | sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands); | ||
| 906 | |||
| 907 | return alloc; | ||
| 908 | } | ||
| 909 | |||
| 910 | static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf) | ||
| 911 | { | ||
| 912 | static const float g_deq_L12[18*3] = { | ||
| 913 | #define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x | ||
| 914 | DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9) | ||
| 915 | }; | ||
| 916 | int i, m; | ||
| 917 | for (i = 0; i < bands; i++) | ||
| 918 | { | ||
| 919 | float s = 0; | ||
| 920 | int ba = *pba++; | ||
| 921 | int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; | ||
| 922 | for (m = 4; m; m >>= 1) | ||
| 923 | { | ||
| 924 | if (mask & m) | ||
| 925 | { | ||
| 926 | int b = drmp3_bs_get_bits(bs, 6); | ||
| 927 | s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); | ||
| 928 | } | ||
| 929 | *scf++ = s; | ||
| 930 | } | ||
| 931 | } | ||
| 932 | } | ||
| 933 | |||
| 934 | static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci) | ||
| 935 | { | ||
| 936 | static const drmp3_uint8 g_bitalloc_code_tab[] = { | ||
| 937 | 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, | ||
| 938 | 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, | ||
| 939 | 0,17,18, 3,19,4,5,16, | ||
| 940 | 0,17,18,16, | ||
| 941 | 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, | ||
| 942 | 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, | ||
| 943 | 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 | ||
| 944 | }; | ||
| 945 | const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci); | ||
| 946 | |||
| 947 | int i, k = 0, ba_bits = 0; | ||
| 948 | const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab; | ||
| 949 | |||
| 950 | for (i = 0; i < sci->total_bands; i++) | ||
| 951 | { | ||
| 952 | drmp3_uint8 ba; | ||
| 953 | if (i == k) | ||
| 954 | { | ||
| 955 | k += subband_alloc->band_count; | ||
| 956 | ba_bits = subband_alloc->code_tab_width; | ||
| 957 | ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; | ||
| 958 | subband_alloc++; | ||
| 959 | } | ||
| 960 | ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; | ||
| 961 | sci->bitalloc[2*i] = ba; | ||
| 962 | if (i < sci->stereo_bands) | ||
| 963 | { | ||
| 964 | ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; | ||
| 965 | } | ||
| 966 | sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; | ||
| 967 | } | ||
| 968 | |||
| 969 | for (i = 0; i < 2*sci->total_bands; i++) | ||
| 970 | { | ||
| 971 | sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6); | ||
| 972 | } | ||
| 973 | |||
| 974 | drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); | ||
| 975 | |||
| 976 | for (i = sci->stereo_bands; i < sci->total_bands; i++) | ||
| 977 | { | ||
| 978 | sci->bitalloc[2*i + 1] = 0; | ||
| 979 | } | ||
| 980 | } | ||
| 981 | |||
| 982 | static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size) | ||
| 983 | { | ||
| 984 | int i, j, k, choff = 576; | ||
| 985 | for (j = 0; j < 4; j++) | ||
| 986 | { | ||
| 987 | float *dst = grbuf + group_size*j; | ||
| 988 | for (i = 0; i < 2*sci->total_bands; i++) | ||
| 989 | { | ||
| 990 | int ba = sci->bitalloc[i]; | ||
| 991 | if (ba != 0) | ||
| 992 | { | ||
| 993 | if (ba < 17) | ||
| 994 | { | ||
| 995 | int half = (1 << (ba - 1)) - 1; | ||
| 996 | for (k = 0; k < group_size; k++) | ||
| 997 | { | ||
| 998 | dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half); | ||
| 999 | } | ||
| 1000 | } else | ||
| 1001 | { | ||
| 1002 | unsigned mod = (2 << (ba - 17)) + 1; /* 3, 5, 9 */ | ||
| 1003 | unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); /* 5, 7, 10 */ | ||
| 1004 | for (k = 0; k < group_size; k++, code /= mod) | ||
| 1005 | { | ||
| 1006 | dst[k] = (float)((int)(code % mod - mod/2)); | ||
| 1007 | } | ||
| 1008 | } | ||
| 1009 | } | ||
| 1010 | dst += choff; | ||
| 1011 | choff = 18 - choff; | ||
| 1012 | } | ||
| 1013 | } | ||
| 1014 | return group_size*4; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst) | ||
| 1018 | { | ||
| 1019 | int i, k; | ||
| 1020 | DRMP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); | ||
| 1021 | for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) | ||
| 1022 | { | ||
| 1023 | for (k = 0; k < 12; k++) | ||
| 1024 | { | ||
| 1025 | dst[k + 0] *= scf[0]; | ||
| 1026 | dst[k + 576] *= scf[3]; | ||
| 1027 | } | ||
| 1028 | } | ||
| 1029 | } | ||
| 1030 | #endif | ||
| 1031 | |||
| 1032 | static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) | ||
| 1033 | { | ||
| 1034 | static const drmp3_uint8 g_scf_long[8][23] = { | ||
| 1035 | { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, | ||
| 1036 | { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, | ||
| 1037 | { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, | ||
| 1038 | { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, | ||
| 1039 | { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, | ||
| 1040 | { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, | ||
| 1041 | { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, | ||
| 1042 | { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } | ||
| 1043 | }; | ||
| 1044 | static const drmp3_uint8 g_scf_short[8][40] = { | ||
| 1045 | { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, | ||
| 1046 | { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, | ||
| 1047 | { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, | ||
| 1048 | { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, | ||
| 1049 | { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, | ||
| 1050 | { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, | ||
| 1051 | { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, | ||
| 1052 | { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } | ||
| 1053 | }; | ||
| 1054 | static const drmp3_uint8 g_scf_mixed[8][40] = { | ||
| 1055 | { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, | ||
| 1056 | { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, | ||
| 1057 | { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, | ||
| 1058 | { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, | ||
| 1059 | { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, | ||
| 1060 | { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, | ||
| 1061 | { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, | ||
| 1062 | { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } | ||
| 1063 | }; | ||
| 1064 | |||
| 1065 | unsigned tables, scfsi = 0; | ||
| 1066 | int main_data_begin, part_23_sum = 0; | ||
| 1067 | int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; | ||
| 1068 | int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); | ||
| 1069 | |||
| 1070 | if (DRMP3_HDR_TEST_MPEG1(hdr)) | ||
| 1071 | { | ||
| 1072 | gr_count *= 2; | ||
| 1073 | main_data_begin = drmp3_bs_get_bits(bs, 9); | ||
| 1074 | scfsi = drmp3_bs_get_bits(bs, 7 + gr_count); | ||
| 1075 | } else | ||
| 1076 | { | ||
| 1077 | main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; | ||
| 1078 | } | ||
| 1079 | |||
| 1080 | do | ||
| 1081 | { | ||
| 1082 | if (DRMP3_HDR_IS_MONO(hdr)) | ||
| 1083 | { | ||
| 1084 | scfsi <<= 4; | ||
| 1085 | } | ||
| 1086 | gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12); | ||
| 1087 | part_23_sum += gr->part_23_length; | ||
| 1088 | gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9); | ||
| 1089 | if (gr->big_values > 288) | ||
| 1090 | { | ||
| 1091 | return -1; | ||
| 1092 | } | ||
| 1093 | gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8); | ||
| 1094 | gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); | ||
| 1095 | gr->sfbtab = g_scf_long[sr_idx]; | ||
| 1096 | gr->n_long_sfb = 22; | ||
| 1097 | gr->n_short_sfb = 0; | ||
| 1098 | if (drmp3_bs_get_bits(bs, 1)) | ||
| 1099 | { | ||
| 1100 | gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2); | ||
| 1101 | if (!gr->block_type) | ||
| 1102 | { | ||
| 1103 | return -1; | ||
| 1104 | } | ||
| 1105 | gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); | ||
| 1106 | gr->region_count[0] = 7; | ||
| 1107 | gr->region_count[1] = 255; | ||
| 1108 | if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE) | ||
| 1109 | { | ||
| 1110 | scfsi &= 0x0F0F; | ||
| 1111 | if (!gr->mixed_block_flag) | ||
| 1112 | { | ||
| 1113 | gr->region_count[0] = 8; | ||
| 1114 | gr->sfbtab = g_scf_short[sr_idx]; | ||
| 1115 | gr->n_long_sfb = 0; | ||
| 1116 | gr->n_short_sfb = 39; | ||
| 1117 | } else | ||
| 1118 | { | ||
| 1119 | gr->sfbtab = g_scf_mixed[sr_idx]; | ||
| 1120 | gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; | ||
| 1121 | gr->n_short_sfb = 30; | ||
| 1122 | } | ||
| 1123 | } | ||
| 1124 | tables = drmp3_bs_get_bits(bs, 10); | ||
| 1125 | tables <<= 5; | ||
| 1126 | gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); | ||
| 1127 | gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); | ||
| 1128 | gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); | ||
| 1129 | } else | ||
| 1130 | { | ||
| 1131 | gr->block_type = 0; | ||
| 1132 | gr->mixed_block_flag = 0; | ||
| 1133 | tables = drmp3_bs_get_bits(bs, 15); | ||
| 1134 | gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4); | ||
| 1135 | gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); | ||
| 1136 | gr->region_count[2] = 255; | ||
| 1137 | } | ||
| 1138 | gr->table_select[0] = (drmp3_uint8)(tables >> 10); | ||
| 1139 | gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31); | ||
| 1140 | gr->table_select[2] = (drmp3_uint8)((tables) & 31); | ||
| 1141 | gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); | ||
| 1142 | gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); | ||
| 1143 | gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); | ||
| 1144 | gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15); | ||
| 1145 | scfsi <<= 4; | ||
| 1146 | gr++; | ||
| 1147 | } while(--gr_count); | ||
| 1148 | |||
| 1149 | if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) | ||
| 1150 | { | ||
| 1151 | return -1; | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | return main_data_begin; | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi) | ||
| 1158 | { | ||
| 1159 | int i, k; | ||
| 1160 | for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) | ||
| 1161 | { | ||
| 1162 | int cnt = scf_count[i]; | ||
| 1163 | if (scfsi & 8) | ||
| 1164 | { | ||
| 1165 | DRMP3_COPY_MEMORY(scf, ist_pos, cnt); | ||
| 1166 | } else | ||
| 1167 | { | ||
| 1168 | int bits = scf_size[i]; | ||
| 1169 | if (!bits) | ||
| 1170 | { | ||
| 1171 | DRMP3_ZERO_MEMORY(scf, cnt); | ||
| 1172 | DRMP3_ZERO_MEMORY(ist_pos, cnt); | ||
| 1173 | } else | ||
| 1174 | { | ||
| 1175 | int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; | ||
| 1176 | for (k = 0; k < cnt; k++) | ||
| 1177 | { | ||
| 1178 | int s = drmp3_bs_get_bits(bitbuf, bits); | ||
| 1179 | ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s); | ||
| 1180 | scf[k] = (drmp3_uint8)s; | ||
| 1181 | } | ||
| 1182 | } | ||
| 1183 | } | ||
| 1184 | ist_pos += cnt; | ||
| 1185 | scf += cnt; | ||
| 1186 | } | ||
| 1187 | scf[0] = scf[1] = scf[2] = 0; | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | static float drmp3_L3_ldexp_q2(float y, int exp_q2) | ||
| 1191 | { | ||
| 1192 | static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; | ||
| 1193 | int e; | ||
| 1194 | do | ||
| 1195 | { | ||
| 1196 | e = DRMP3_MIN(30*4, exp_q2); | ||
| 1197 | y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); | ||
| 1198 | } while ((exp_q2 -= e) > 0); | ||
| 1199 | return y; | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch) | ||
| 1203 | { | ||
| 1204 | static const drmp3_uint8 g_scf_partitions[3][28] = { | ||
| 1205 | { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, | ||
| 1206 | { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, | ||
| 1207 | { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } | ||
| 1208 | }; | ||
| 1209 | const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; | ||
| 1210 | drmp3_uint8 scf_size[4], iscf[40]; | ||
| 1211 | int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; | ||
| 1212 | float gain; | ||
| 1213 | |||
| 1214 | if (DRMP3_HDR_TEST_MPEG1(hdr)) | ||
| 1215 | { | ||
| 1216 | static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; | ||
| 1217 | int part = g_scfc_decode[gr->scalefac_compress]; | ||
| 1218 | scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2); | ||
| 1219 | scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3); | ||
| 1220 | } else | ||
| 1221 | { | ||
| 1222 | static const drmp3_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; | ||
| 1223 | int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch; | ||
| 1224 | sfc = gr->scalefac_compress >> ist; | ||
| 1225 | for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) | ||
| 1226 | { | ||
| 1227 | for (modprod = 1, i = 3; i >= 0; i--) | ||
| 1228 | { | ||
| 1229 | scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]); | ||
| 1230 | modprod *= g_mod[k + i]; | ||
| 1231 | } | ||
| 1232 | } | ||
| 1233 | scf_partition += k; | ||
| 1234 | scfsi = -16; | ||
| 1235 | } | ||
| 1236 | drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); | ||
| 1237 | |||
| 1238 | if (gr->n_short_sfb) | ||
| 1239 | { | ||
| 1240 | int sh = 3 - scf_shift; | ||
| 1241 | for (i = 0; i < gr->n_short_sfb; i += 3) | ||
| 1242 | { | ||
| 1243 | iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); | ||
| 1244 | iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); | ||
| 1245 | iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); | ||
| 1246 | } | ||
| 1247 | } else if (gr->preflag) | ||
| 1248 | { | ||
| 1249 | static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; | ||
| 1250 | for (i = 0; i < 10; i++) | ||
| 1251 | { | ||
| 1252 | iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]); | ||
| 1253 | } | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); | ||
| 1257 | gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp); | ||
| 1258 | for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) | ||
| 1259 | { | ||
| 1260 | scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); | ||
| 1261 | } | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | static const float g_drmp3_pow43[129 + 16] = { | ||
| 1265 | 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, | ||
| 1266 | 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f | ||
| 1267 | }; | ||
| 1268 | |||
| 1269 | static float drmp3_L3_pow_43(int x) | ||
| 1270 | { | ||
| 1271 | float frac; | ||
| 1272 | int sign, mult = 256; | ||
| 1273 | |||
| 1274 | if (x < 129) | ||
| 1275 | { | ||
| 1276 | return g_drmp3_pow43[16 + x]; | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | if (x < 1024) | ||
| 1280 | { | ||
| 1281 | mult = 16; | ||
| 1282 | x <<= 3; | ||
| 1283 | } | ||
| 1284 | |||
| 1285 | sign = 2*x & 64; | ||
| 1286 | frac = (float)((x & 63) - sign) / ((x & ~63) + sign); | ||
| 1287 | return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) | ||
| 1291 | { | ||
| 1292 | static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 1293 | 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, | ||
| 1294 | -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, | ||
| 1295 | -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, | ||
| 1296 | -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, | ||
| 1297 | -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, | ||
| 1298 | -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, | ||
| 1299 | -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, | ||
| 1300 | -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, | ||
| 1301 | -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, | ||
| 1302 | -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, | ||
| 1303 | -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, | ||
| 1304 | -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, | ||
| 1305 | -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, | ||
| 1306 | -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, | ||
| 1307 | -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; | ||
| 1308 | static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; | ||
| 1309 | static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; | ||
| 1310 | static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; | ||
| 1311 | static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; | ||
| 1312 | |||
| 1313 | #define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) | ||
| 1314 | #define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } | ||
| 1315 | #define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } | ||
| 1316 | #define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) | ||
| 1317 | |||
| 1318 | float one = 0.0f; | ||
| 1319 | int ireg = 0, big_val_cnt = gr_info->big_values; | ||
| 1320 | const drmp3_uint8 *sfb = gr_info->sfbtab; | ||
| 1321 | const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8; | ||
| 1322 | drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); | ||
| 1323 | int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; | ||
| 1324 | bs_next_ptr += 4; | ||
| 1325 | |||
| 1326 | while (big_val_cnt > 0) | ||
| 1327 | { | ||
| 1328 | int tab_num = gr_info->table_select[ireg]; | ||
| 1329 | int sfb_cnt = gr_info->region_count[ireg++]; | ||
| 1330 | const drmp3_int16 *codebook = tabs + tabindex[tab_num]; | ||
| 1331 | int linbits = g_linbits[tab_num]; | ||
| 1332 | if (linbits) | ||
| 1333 | { | ||
| 1334 | do | ||
| 1335 | { | ||
| 1336 | np = *sfb++ / 2; | ||
| 1337 | pairs_to_decode = DRMP3_MIN(big_val_cnt, np); | ||
| 1338 | one = *scf++; | ||
| 1339 | do | ||
| 1340 | { | ||
| 1341 | int j, w = 5; | ||
| 1342 | int leaf = codebook[DRMP3_PEEK_BITS(w)]; | ||
| 1343 | while (leaf < 0) | ||
| 1344 | { | ||
| 1345 | DRMP3_FLUSH_BITS(w); | ||
| 1346 | w = leaf & 7; | ||
| 1347 | leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; | ||
| 1348 | } | ||
| 1349 | DRMP3_FLUSH_BITS(leaf >> 8); | ||
| 1350 | |||
| 1351 | for (j = 0; j < 2; j++, dst++, leaf >>= 4) | ||
| 1352 | { | ||
| 1353 | int lsb = leaf & 0x0F; | ||
| 1354 | if (lsb == 15) | ||
| 1355 | { | ||
| 1356 | lsb += DRMP3_PEEK_BITS(linbits); | ||
| 1357 | DRMP3_FLUSH_BITS(linbits); | ||
| 1358 | DRMP3_CHECK_BITS; | ||
| 1359 | *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1); | ||
| 1360 | } else | ||
| 1361 | { | ||
| 1362 | *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; | ||
| 1363 | } | ||
| 1364 | DRMP3_FLUSH_BITS(lsb ? 1 : 0); | ||
| 1365 | } | ||
| 1366 | DRMP3_CHECK_BITS; | ||
| 1367 | } while (--pairs_to_decode); | ||
| 1368 | } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); | ||
| 1369 | } else | ||
| 1370 | { | ||
| 1371 | do | ||
| 1372 | { | ||
| 1373 | np = *sfb++ / 2; | ||
| 1374 | pairs_to_decode = DRMP3_MIN(big_val_cnt, np); | ||
| 1375 | one = *scf++; | ||
| 1376 | do | ||
| 1377 | { | ||
| 1378 | int j, w = 5; | ||
| 1379 | int leaf = codebook[DRMP3_PEEK_BITS(w)]; | ||
| 1380 | while (leaf < 0) | ||
| 1381 | { | ||
| 1382 | DRMP3_FLUSH_BITS(w); | ||
| 1383 | w = leaf & 7; | ||
| 1384 | leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; | ||
| 1385 | } | ||
| 1386 | DRMP3_FLUSH_BITS(leaf >> 8); | ||
| 1387 | |||
| 1388 | for (j = 0; j < 2; j++, dst++, leaf >>= 4) | ||
| 1389 | { | ||
| 1390 | int lsb = leaf & 0x0F; | ||
| 1391 | *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; | ||
| 1392 | DRMP3_FLUSH_BITS(lsb ? 1 : 0); | ||
| 1393 | } | ||
| 1394 | DRMP3_CHECK_BITS; | ||
| 1395 | } while (--pairs_to_decode); | ||
| 1396 | } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); | ||
| 1397 | } | ||
| 1398 | } | ||
| 1399 | |||
| 1400 | for (np = 1 - big_val_cnt;; dst += 4) | ||
| 1401 | { | ||
| 1402 | const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; | ||
| 1403 | int leaf = codebook_count1[DRMP3_PEEK_BITS(4)]; | ||
| 1404 | if (!(leaf & 8)) | ||
| 1405 | { | ||
| 1406 | leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; | ||
| 1407 | } | ||
| 1408 | DRMP3_FLUSH_BITS(leaf & 7); | ||
| 1409 | if (DRMP3_BSPOS > layer3gr_limit) | ||
| 1410 | { | ||
| 1411 | break; | ||
| 1412 | } | ||
| 1413 | #define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } | ||
| 1414 | #define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) } | ||
| 1415 | DRMP3_RELOAD_SCALEFACTOR; | ||
| 1416 | DRMP3_DEQ_COUNT1(0); | ||
| 1417 | DRMP3_DEQ_COUNT1(1); | ||
| 1418 | DRMP3_RELOAD_SCALEFACTOR; | ||
| 1419 | DRMP3_DEQ_COUNT1(2); | ||
| 1420 | DRMP3_DEQ_COUNT1(3); | ||
| 1421 | DRMP3_CHECK_BITS; | ||
| 1422 | } | ||
| 1423 | |||
| 1424 | bs->pos = layer3gr_limit; | ||
| 1425 | } | ||
| 1426 | |||
| 1427 | static void drmp3_L3_midside_stereo(float *left, int n) | ||
| 1428 | { | ||
| 1429 | int i = 0; | ||
| 1430 | float *right = left + 576; | ||
| 1431 | #if DRMP3_HAVE_SIMD | ||
| 1432 | if (drmp3_have_simd()) | ||
| 1433 | { | ||
| 1434 | for (; i < n - 3; i += 4) | ||
| 1435 | { | ||
| 1436 | drmp3_f4 vl = DRMP3_VLD(left + i); | ||
| 1437 | drmp3_f4 vr = DRMP3_VLD(right + i); | ||
| 1438 | DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr)); | ||
| 1439 | DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr)); | ||
| 1440 | } | ||
| 1441 | #ifdef __GNUC__ | ||
| 1442 | /* Workaround for spurious -Waggressive-loop-optimizations warning from gcc. | ||
| 1443 | * For more info see: https://github.com/lieff/minimp3/issues/88 | ||
| 1444 | */ | ||
| 1445 | if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) | ||
| 1446 | return; | ||
| 1447 | #endif | ||
| 1448 | } | ||
| 1449 | #endif | ||
| 1450 | for (; i < n; i++) | ||
| 1451 | { | ||
| 1452 | float a = left[i]; | ||
| 1453 | float b = right[i]; | ||
| 1454 | left[i] = a + b; | ||
| 1455 | right[i] = a - b; | ||
| 1456 | } | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) | ||
| 1460 | { | ||
| 1461 | int i; | ||
| 1462 | for (i = 0; i < n; i++) | ||
| 1463 | { | ||
| 1464 | left[i + 576] = left[i]*kr; | ||
| 1465 | left[i] = left[i]*kl; | ||
| 1466 | } | ||
| 1467 | } | ||
| 1468 | |||
| 1469 | static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3]) | ||
| 1470 | { | ||
| 1471 | int i, k; | ||
| 1472 | |||
| 1473 | max_band[0] = max_band[1] = max_band[2] = -1; | ||
| 1474 | |||
| 1475 | for (i = 0; i < nbands; i++) | ||
| 1476 | { | ||
| 1477 | for (k = 0; k < sfb[i]; k += 2) | ||
| 1478 | { | ||
| 1479 | if (right[k] != 0 || right[k + 1] != 0) | ||
| 1480 | { | ||
| 1481 | max_band[i % 3] = i; | ||
| 1482 | break; | ||
| 1483 | } | ||
| 1484 | } | ||
| 1485 | right += sfb[i]; | ||
| 1486 | } | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh) | ||
| 1490 | { | ||
| 1491 | static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; | ||
| 1492 | unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; | ||
| 1493 | |||
| 1494 | for (i = 0; sfb[i]; i++) | ||
| 1495 | { | ||
| 1496 | unsigned ipos = ist_pos[i]; | ||
| 1497 | if ((int)i > max_band[i % 3] && ipos < max_pos) | ||
| 1498 | { | ||
| 1499 | float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; | ||
| 1500 | if (DRMP3_HDR_TEST_MPEG1(hdr)) | ||
| 1501 | { | ||
| 1502 | kl = g_pan[2*ipos]; | ||
| 1503 | kr = g_pan[2*ipos + 1]; | ||
| 1504 | } else | ||
| 1505 | { | ||
| 1506 | kl = 1; | ||
| 1507 | kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); | ||
| 1508 | if (ipos & 1) | ||
| 1509 | { | ||
| 1510 | kl = kr; | ||
| 1511 | kr = 1; | ||
| 1512 | } | ||
| 1513 | } | ||
| 1514 | drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); | ||
| 1515 | } else if (DRMP3_HDR_TEST_MS_STEREO(hdr)) | ||
| 1516 | { | ||
| 1517 | drmp3_L3_midside_stereo(left, sfb[i]); | ||
| 1518 | } | ||
| 1519 | left += sfb[i]; | ||
| 1520 | } | ||
| 1521 | } | ||
| 1522 | |||
| 1523 | static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) | ||
| 1524 | { | ||
| 1525 | int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; | ||
| 1526 | int i, max_blocks = gr->n_short_sfb ? 3 : 1; | ||
| 1527 | |||
| 1528 | drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); | ||
| 1529 | if (gr->n_long_sfb) | ||
| 1530 | { | ||
| 1531 | max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]); | ||
| 1532 | } | ||
| 1533 | for (i = 0; i < max_blocks; i++) | ||
| 1534 | { | ||
| 1535 | int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; | ||
| 1536 | int itop = n_sfb - max_blocks + i; | ||
| 1537 | int prev = itop - max_blocks; | ||
| 1538 | ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); | ||
| 1539 | } | ||
| 1540 | drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb) | ||
| 1544 | { | ||
| 1545 | int i, len; | ||
| 1546 | float *src = grbuf, *dst = scratch; | ||
| 1547 | |||
| 1548 | for (;0 != (len = *sfb); sfb += 3, src += 2*len) | ||
| 1549 | { | ||
| 1550 | for (i = 0; i < len; i++, src++) | ||
| 1551 | { | ||
| 1552 | *dst++ = src[0*len]; | ||
| 1553 | *dst++ = src[1*len]; | ||
| 1554 | *dst++ = src[2*len]; | ||
| 1555 | } | ||
| 1556 | } | ||
| 1557 | DRMP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); | ||
| 1558 | } | ||
| 1559 | |||
| 1560 | static void drmp3_L3_antialias(float *grbuf, int nbands) | ||
| 1561 | { | ||
| 1562 | static const float g_aa[2][8] = { | ||
| 1563 | {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, | ||
| 1564 | {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} | ||
| 1565 | }; | ||
| 1566 | |||
| 1567 | for (; nbands > 0; nbands--, grbuf += 18) | ||
| 1568 | { | ||
| 1569 | int i = 0; | ||
| 1570 | #if DRMP3_HAVE_SIMD | ||
| 1571 | if (drmp3_have_simd()) for (; i < 8; i += 4) | ||
| 1572 | { | ||
| 1573 | drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i); | ||
| 1574 | drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i); | ||
| 1575 | drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i); | ||
| 1576 | drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i); | ||
| 1577 | vd = DRMP3_VREV(vd); | ||
| 1578 | DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1))); | ||
| 1579 | vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0)); | ||
| 1580 | DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd)); | ||
| 1581 | } | ||
| 1582 | #endif | ||
| 1583 | #ifndef DR_MP3_ONLY_SIMD | ||
| 1584 | for(; i < 8; i++) | ||
| 1585 | { | ||
| 1586 | float u = grbuf[18 + i]; | ||
| 1587 | float d = grbuf[17 - i]; | ||
| 1588 | grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; | ||
| 1589 | grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; | ||
| 1590 | } | ||
| 1591 | #endif | ||
| 1592 | } | ||
| 1593 | } | ||
| 1594 | |||
| 1595 | static void drmp3_L3_dct3_9(float *y) | ||
| 1596 | { | ||
| 1597 | float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; | ||
| 1598 | |||
| 1599 | s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; | ||
| 1600 | t0 = s0 + s6*0.5f; | ||
| 1601 | s0 -= s6; | ||
| 1602 | t4 = (s4 + s2)*0.93969262f; | ||
| 1603 | t2 = (s8 + s2)*0.76604444f; | ||
| 1604 | s6 = (s4 - s8)*0.17364818f; | ||
| 1605 | s4 += s8 - s2; | ||
| 1606 | |||
| 1607 | s2 = s0 - s4*0.5f; | ||
| 1608 | y[4] = s4 + s0; | ||
| 1609 | s8 = t0 - t2 + s6; | ||
| 1610 | s0 = t0 - t4 + t2; | ||
| 1611 | s4 = t0 + t4 - s6; | ||
| 1612 | |||
| 1613 | s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; | ||
| 1614 | |||
| 1615 | s3 *= 0.86602540f; | ||
| 1616 | t0 = (s5 + s1)*0.98480775f; | ||
| 1617 | t4 = (s5 - s7)*0.34202014f; | ||
| 1618 | t2 = (s1 + s7)*0.64278761f; | ||
| 1619 | s1 = (s1 - s5 - s7)*0.86602540f; | ||
| 1620 | |||
| 1621 | s5 = t0 - s3 - t2; | ||
| 1622 | s7 = t4 - s3 - t0; | ||
| 1623 | s3 = t4 + s3 - t2; | ||
| 1624 | |||
| 1625 | y[0] = s4 - s7; | ||
| 1626 | y[1] = s2 + s1; | ||
| 1627 | y[2] = s0 - s3; | ||
| 1628 | y[3] = s8 + s5; | ||
| 1629 | y[5] = s8 - s5; | ||
| 1630 | y[6] = s0 + s3; | ||
| 1631 | y[7] = s2 - s1; | ||
| 1632 | y[8] = s4 + s7; | ||
| 1633 | } | ||
| 1634 | |||
| 1635 | static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) | ||
| 1636 | { | ||
| 1637 | int i, j; | ||
| 1638 | static const float g_twid9[18] = { | ||
| 1639 | 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f | ||
| 1640 | }; | ||
| 1641 | |||
| 1642 | for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) | ||
| 1643 | { | ||
| 1644 | float co[9], si[9]; | ||
| 1645 | co[0] = -grbuf[0]; | ||
| 1646 | si[0] = grbuf[17]; | ||
| 1647 | for (i = 0; i < 4; i++) | ||
| 1648 | { | ||
| 1649 | si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; | ||
| 1650 | co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; | ||
| 1651 | si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; | ||
| 1652 | co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); | ||
| 1653 | } | ||
| 1654 | drmp3_L3_dct3_9(co); | ||
| 1655 | drmp3_L3_dct3_9(si); | ||
| 1656 | |||
| 1657 | si[1] = -si[1]; | ||
| 1658 | si[3] = -si[3]; | ||
| 1659 | si[5] = -si[5]; | ||
| 1660 | si[7] = -si[7]; | ||
| 1661 | |||
| 1662 | i = 0; | ||
| 1663 | |||
| 1664 | #if DRMP3_HAVE_SIMD | ||
| 1665 | if (drmp3_have_simd()) for (; i < 8; i += 4) | ||
| 1666 | { | ||
| 1667 | drmp3_f4 vovl = DRMP3_VLD(overlap + i); | ||
| 1668 | drmp3_f4 vc = DRMP3_VLD(co + i); | ||
| 1669 | drmp3_f4 vs = DRMP3_VLD(si + i); | ||
| 1670 | drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i); | ||
| 1671 | drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i); | ||
| 1672 | drmp3_f4 vw0 = DRMP3_VLD(window + i); | ||
| 1673 | drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i); | ||
| 1674 | drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0)); | ||
| 1675 | DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1))); | ||
| 1676 | DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1))); | ||
| 1677 | vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0)); | ||
| 1678 | DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum)); | ||
| 1679 | } | ||
| 1680 | #endif | ||
| 1681 | for (; i < 9; i++) | ||
| 1682 | { | ||
| 1683 | float ovl = overlap[i]; | ||
| 1684 | float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; | ||
| 1685 | overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; | ||
| 1686 | grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; | ||
| 1687 | grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; | ||
| 1688 | } | ||
| 1689 | } | ||
| 1690 | } | ||
| 1691 | |||
| 1692 | static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst) | ||
| 1693 | { | ||
| 1694 | float m1 = x1*0.86602540f; | ||
| 1695 | float a1 = x0 - x2*0.5f; | ||
| 1696 | dst[1] = x0 + x2; | ||
| 1697 | dst[0] = a1 + m1; | ||
| 1698 | dst[2] = a1 - m1; | ||
| 1699 | } | ||
| 1700 | |||
| 1701 | static void drmp3_L3_imdct12(float *x, float *dst, float *overlap) | ||
| 1702 | { | ||
| 1703 | static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; | ||
| 1704 | float co[3], si[3]; | ||
| 1705 | int i; | ||
| 1706 | |||
| 1707 | drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); | ||
| 1708 | drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); | ||
| 1709 | si[1] = -si[1]; | ||
| 1710 | |||
| 1711 | for (i = 0; i < 3; i++) | ||
| 1712 | { | ||
| 1713 | float ovl = overlap[i]; | ||
| 1714 | float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; | ||
| 1715 | overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; | ||
| 1716 | dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; | ||
| 1717 | dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; | ||
| 1718 | } | ||
| 1719 | } | ||
| 1720 | |||
| 1721 | static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) | ||
| 1722 | { | ||
| 1723 | for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) | ||
| 1724 | { | ||
| 1725 | float tmp[18]; | ||
| 1726 | DRMP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); | ||
| 1727 | DRMP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); | ||
| 1728 | drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); | ||
| 1729 | drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); | ||
| 1730 | drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6); | ||
| 1731 | } | ||
| 1732 | } | ||
| 1733 | |||
| 1734 | static void drmp3_L3_change_sign(float *grbuf) | ||
| 1735 | { | ||
| 1736 | int b, i; | ||
| 1737 | for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) | ||
| 1738 | for (i = 1; i < 18; i += 2) | ||
| 1739 | grbuf[i] = -grbuf[i]; | ||
| 1740 | } | ||
| 1741 | |||
| 1742 | static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) | ||
| 1743 | { | ||
| 1744 | static const float g_mdct_window[2][18] = { | ||
| 1745 | { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, | ||
| 1746 | { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } | ||
| 1747 | }; | ||
| 1748 | if (n_long_bands) | ||
| 1749 | { | ||
| 1750 | drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); | ||
| 1751 | grbuf += 18*n_long_bands; | ||
| 1752 | overlap += 9*n_long_bands; | ||
| 1753 | } | ||
| 1754 | if (block_type == DRMP3_SHORT_BLOCK_TYPE) | ||
| 1755 | drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); | ||
| 1756 | else | ||
| 1757 | drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands); | ||
| 1758 | } | ||
| 1759 | |||
| 1760 | static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s) | ||
| 1761 | { | ||
| 1762 | int pos = (s->bs.pos + 7)/8u; | ||
| 1763 | int remains = s->bs.limit/8u - pos; | ||
| 1764 | if (remains > DRMP3_MAX_BITRESERVOIR_BYTES) | ||
| 1765 | { | ||
| 1766 | pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES; | ||
| 1767 | remains = DRMP3_MAX_BITRESERVOIR_BYTES; | ||
| 1768 | } | ||
| 1769 | if (remains > 0) | ||
| 1770 | { | ||
| 1771 | DRMP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); | ||
| 1772 | } | ||
| 1773 | h->reserv = remains; | ||
| 1774 | } | ||
| 1775 | |||
| 1776 | static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin) | ||
| 1777 | { | ||
| 1778 | int frame_bytes = (bs->limit - bs->pos)/8; | ||
| 1779 | int bytes_have = DRMP3_MIN(h->reserv, main_data_begin); | ||
| 1780 | DRMP3_COPY_MEMORY(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin)); | ||
| 1781 | DRMP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); | ||
| 1782 | drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); | ||
| 1783 | return h->reserv >= main_data_begin; | ||
| 1784 | } | ||
| 1785 | |||
| 1786 | static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch) | ||
| 1787 | { | ||
| 1788 | int ch; | ||
| 1789 | |||
| 1790 | for (ch = 0; ch < nch; ch++) | ||
| 1791 | { | ||
| 1792 | int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; | ||
| 1793 | drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); | ||
| 1794 | drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); | ||
| 1795 | } | ||
| 1796 | |||
| 1797 | if (DRMP3_HDR_TEST_I_STEREO(h->header)) | ||
| 1798 | { | ||
| 1799 | drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); | ||
| 1800 | } else if (DRMP3_HDR_IS_MS_STEREO(h->header)) | ||
| 1801 | { | ||
| 1802 | drmp3_L3_midside_stereo(s->grbuf[0], 576); | ||
| 1803 | } | ||
| 1804 | |||
| 1805 | for (ch = 0; ch < nch; ch++, gr_info++) | ||
| 1806 | { | ||
| 1807 | int aa_bands = 31; | ||
| 1808 | int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); | ||
| 1809 | |||
| 1810 | if (gr_info->n_short_sfb) | ||
| 1811 | { | ||
| 1812 | aa_bands = n_long_bands - 1; | ||
| 1813 | drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); | ||
| 1814 | } | ||
| 1815 | |||
| 1816 | drmp3_L3_antialias(s->grbuf[ch], aa_bands); | ||
| 1817 | drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); | ||
| 1818 | drmp3_L3_change_sign(s->grbuf[ch]); | ||
| 1819 | } | ||
| 1820 | } | ||
| 1821 | |||
| 1822 | static void drmp3d_DCT_II(float *grbuf, int n) | ||
| 1823 | { | ||
| 1824 | static const float g_sec[24] = { | ||
| 1825 | 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f | ||
| 1826 | }; | ||
| 1827 | int i, k = 0; | ||
| 1828 | #if DRMP3_HAVE_SIMD | ||
| 1829 | if (drmp3_have_simd()) for (; k < n; k += 4) | ||
| 1830 | { | ||
| 1831 | drmp3_f4 t[4][8], *x; | ||
| 1832 | float *y = grbuf + k; | ||
| 1833 | |||
| 1834 | for (x = t[0], i = 0; i < 8; i++, x++) | ||
| 1835 | { | ||
| 1836 | drmp3_f4 x0 = DRMP3_VLD(&y[i*18]); | ||
| 1837 | drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]); | ||
| 1838 | drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]); | ||
| 1839 | drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]); | ||
| 1840 | drmp3_f4 t0 = DRMP3_VADD(x0, x3); | ||
| 1841 | drmp3_f4 t1 = DRMP3_VADD(x1, x2); | ||
| 1842 | drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]); | ||
| 1843 | drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]); | ||
| 1844 | x[0] = DRMP3_VADD(t0, t1); | ||
| 1845 | x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]); | ||
| 1846 | x[16] = DRMP3_VADD(t3, t2); | ||
| 1847 | x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]); | ||
| 1848 | } | ||
| 1849 | for (x = t[0], i = 0; i < 4; i++, x += 8) | ||
| 1850 | { | ||
| 1851 | drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; | ||
| 1852 | xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7); | ||
| 1853 | x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6); | ||
| 1854 | x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5); | ||
| 1855 | x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4); | ||
| 1856 | x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3); | ||
| 1857 | x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2); | ||
| 1858 | x[0] = DRMP3_VADD(x0, x1); | ||
| 1859 | x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f); | ||
| 1860 | x5 = DRMP3_VADD(x5, x6); | ||
| 1861 | x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f); | ||
| 1862 | x7 = DRMP3_VADD(x7, xt); | ||
| 1863 | x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f); | ||
| 1864 | x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); /* rotate by PI/8 */ | ||
| 1865 | x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f)); | ||
| 1866 | x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); | ||
| 1867 | x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6); | ||
| 1868 | x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f); | ||
| 1869 | x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f); | ||
| 1870 | x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f); | ||
| 1871 | x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f); | ||
| 1872 | x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f); | ||
| 1873 | x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f); | ||
| 1874 | } | ||
| 1875 | |||
| 1876 | if (k > n - 3) | ||
| 1877 | { | ||
| 1878 | #if DRMP3_HAVE_SSE | ||
| 1879 | #define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) | ||
| 1880 | #else | ||
| 1881 | #define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) | ||
| 1882 | #endif | ||
| 1883 | for (i = 0; i < 7; i++, y += 4*18) | ||
| 1884 | { | ||
| 1885 | drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); | ||
| 1886 | DRMP3_VSAVE2(0, t[0][i]); | ||
| 1887 | DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s)); | ||
| 1888 | DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1])); | ||
| 1889 | DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s)); | ||
| 1890 | } | ||
| 1891 | DRMP3_VSAVE2(0, t[0][7]); | ||
| 1892 | DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7])); | ||
| 1893 | DRMP3_VSAVE2(2, t[1][7]); | ||
| 1894 | DRMP3_VSAVE2(3, t[3][7]); | ||
| 1895 | } else | ||
| 1896 | { | ||
| 1897 | #define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v) | ||
| 1898 | for (i = 0; i < 7; i++, y += 4*18) | ||
| 1899 | { | ||
| 1900 | drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); | ||
| 1901 | DRMP3_VSAVE4(0, t[0][i]); | ||
| 1902 | DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s)); | ||
| 1903 | DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1])); | ||
| 1904 | DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s)); | ||
| 1905 | } | ||
| 1906 | DRMP3_VSAVE4(0, t[0][7]); | ||
| 1907 | DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7])); | ||
| 1908 | DRMP3_VSAVE4(2, t[1][7]); | ||
| 1909 | DRMP3_VSAVE4(3, t[3][7]); | ||
| 1910 | } | ||
| 1911 | } else | ||
| 1912 | #endif | ||
| 1913 | #ifdef DR_MP3_ONLY_SIMD | ||
| 1914 | {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */ | ||
| 1915 | #else | ||
| 1916 | for (; k < n; k++) | ||
| 1917 | { | ||
| 1918 | float t[4][8], *x, *y = grbuf + k; | ||
| 1919 | |||
| 1920 | for (x = t[0], i = 0; i < 8; i++, x++) | ||
| 1921 | { | ||
| 1922 | float x0 = y[i*18]; | ||
| 1923 | float x1 = y[(15 - i)*18]; | ||
| 1924 | float x2 = y[(16 + i)*18]; | ||
| 1925 | float x3 = y[(31 - i)*18]; | ||
| 1926 | float t0 = x0 + x3; | ||
| 1927 | float t1 = x1 + x2; | ||
| 1928 | float t2 = (x1 - x2)*g_sec[3*i + 0]; | ||
| 1929 | float t3 = (x0 - x3)*g_sec[3*i + 1]; | ||
| 1930 | x[0] = t0 + t1; | ||
| 1931 | x[8] = (t0 - t1)*g_sec[3*i + 2]; | ||
| 1932 | x[16] = t3 + t2; | ||
| 1933 | x[24] = (t3 - t2)*g_sec[3*i + 2]; | ||
| 1934 | } | ||
| 1935 | for (x = t[0], i = 0; i < 4; i++, x += 8) | ||
| 1936 | { | ||
| 1937 | float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; | ||
| 1938 | xt = x0 - x7; x0 += x7; | ||
| 1939 | x7 = x1 - x6; x1 += x6; | ||
| 1940 | x6 = x2 - x5; x2 += x5; | ||
| 1941 | x5 = x3 - x4; x3 += x4; | ||
| 1942 | x4 = x0 - x3; x0 += x3; | ||
| 1943 | x3 = x1 - x2; x1 += x2; | ||
| 1944 | x[0] = x0 + x1; | ||
| 1945 | x[4] = (x0 - x1)*0.70710677f; | ||
| 1946 | x5 = x5 + x6; | ||
| 1947 | x6 = (x6 + x7)*0.70710677f; | ||
| 1948 | x7 = x7 + xt; | ||
| 1949 | x3 = (x3 + x4)*0.70710677f; | ||
| 1950 | x5 -= x7*0.198912367f; /* rotate by PI/8 */ | ||
| 1951 | x7 += x5*0.382683432f; | ||
| 1952 | x5 -= x7*0.198912367f; | ||
| 1953 | x0 = xt - x6; xt += x6; | ||
| 1954 | x[1] = (xt + x7)*0.50979561f; | ||
| 1955 | x[2] = (x4 + x3)*0.54119611f; | ||
| 1956 | x[3] = (x0 - x5)*0.60134488f; | ||
| 1957 | x[5] = (x0 + x5)*0.89997619f; | ||
| 1958 | x[6] = (x4 - x3)*1.30656302f; | ||
| 1959 | x[7] = (xt - x7)*2.56291556f; | ||
| 1960 | |||
| 1961 | } | ||
| 1962 | for (i = 0; i < 7; i++, y += 4*18) | ||
| 1963 | { | ||
| 1964 | y[0*18] = t[0][i]; | ||
| 1965 | y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; | ||
| 1966 | y[2*18] = t[1][i] + t[1][i + 1]; | ||
| 1967 | y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; | ||
| 1968 | } | ||
| 1969 | y[0*18] = t[0][7]; | ||
| 1970 | y[1*18] = t[2][7] + t[3][7]; | ||
| 1971 | y[2*18] = t[1][7]; | ||
| 1972 | y[3*18] = t[3][7]; | ||
| 1973 | } | ||
| 1974 | #endif | ||
| 1975 | } | ||
| 1976 | |||
| 1977 | #ifndef DR_MP3_FLOAT_OUTPUT | ||
| 1978 | typedef drmp3_int16 drmp3d_sample_t; | ||
| 1979 | |||
| 1980 | static drmp3_int16 drmp3d_scale_pcm(float sample) | ||
| 1981 | { | ||
| 1982 | drmp3_int16 s; | ||
| 1983 | #if DRMP3_HAVE_ARMV6 | ||
| 1984 | drmp3_int32 s32 = (drmp3_int32)(sample + .5f); | ||
| 1985 | s32 -= (s32 < 0); | ||
| 1986 | s = (drmp3_int16)drmp3_clip_int16_arm(s32); | ||
| 1987 | #else | ||
| 1988 | if (sample >= 32766.5f) return (drmp3_int16) 32767; | ||
| 1989 | if (sample <= -32767.5f) return (drmp3_int16)-32768; | ||
| 1990 | s = (drmp3_int16)(sample + .5f); | ||
| 1991 | s -= (s < 0); /* away from zero, to be compliant */ | ||
| 1992 | #endif | ||
| 1993 | return s; | ||
| 1994 | } | ||
| 1995 | #else | ||
| 1996 | typedef float drmp3d_sample_t; | ||
| 1997 | |||
| 1998 | static float drmp3d_scale_pcm(float sample) | ||
| 1999 | { | ||
| 2000 | return sample*(1.f/32768.f); | ||
| 2001 | } | ||
| 2002 | #endif | ||
| 2003 | |||
| 2004 | static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) | ||
| 2005 | { | ||
| 2006 | float a; | ||
| 2007 | a = (z[14*64] - z[ 0]) * 29; | ||
| 2008 | a += (z[ 1*64] + z[13*64]) * 213; | ||
| 2009 | a += (z[12*64] - z[ 2*64]) * 459; | ||
| 2010 | a += (z[ 3*64] + z[11*64]) * 2037; | ||
| 2011 | a += (z[10*64] - z[ 4*64]) * 5153; | ||
| 2012 | a += (z[ 5*64] + z[ 9*64]) * 6574; | ||
| 2013 | a += (z[ 8*64] - z[ 6*64]) * 37489; | ||
| 2014 | a += z[ 7*64] * 75038; | ||
| 2015 | pcm[0] = drmp3d_scale_pcm(a); | ||
| 2016 | |||
| 2017 | z += 2; | ||
| 2018 | a = z[14*64] * 104; | ||
| 2019 | a += z[12*64] * 1567; | ||
| 2020 | a += z[10*64] * 9727; | ||
| 2021 | a += z[ 8*64] * 64019; | ||
| 2022 | a += z[ 6*64] * -9975; | ||
| 2023 | a += z[ 4*64] * -45; | ||
| 2024 | a += z[ 2*64] * 146; | ||
| 2025 | a += z[ 0*64] * -5; | ||
| 2026 | pcm[16*nch] = drmp3d_scale_pcm(a); | ||
| 2027 | } | ||
| 2028 | |||
| 2029 | static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) | ||
| 2030 | { | ||
| 2031 | int i; | ||
| 2032 | float *xr = xl + 576*(nch - 1); | ||
| 2033 | drmp3d_sample_t *dstr = dstl + (nch - 1); | ||
| 2034 | |||
| 2035 | static const float g_win[] = { | ||
| 2036 | -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, | ||
| 2037 | -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, | ||
| 2038 | -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, | ||
| 2039 | -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, | ||
| 2040 | -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, | ||
| 2041 | -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, | ||
| 2042 | -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, | ||
| 2043 | -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, | ||
| 2044 | -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, | ||
| 2045 | -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, | ||
| 2046 | -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, | ||
| 2047 | -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, | ||
| 2048 | -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, | ||
| 2049 | -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, | ||
| 2050 | -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 | ||
| 2051 | }; | ||
| 2052 | float *zlin = lins + 15*64; | ||
| 2053 | const float *w = g_win; | ||
| 2054 | |||
| 2055 | zlin[4*15] = xl[18*16]; | ||
| 2056 | zlin[4*15 + 1] = xr[18*16]; | ||
| 2057 | zlin[4*15 + 2] = xl[0]; | ||
| 2058 | zlin[4*15 + 3] = xr[0]; | ||
| 2059 | |||
| 2060 | zlin[4*31] = xl[1 + 18*16]; | ||
| 2061 | zlin[4*31 + 1] = xr[1 + 18*16]; | ||
| 2062 | zlin[4*31 + 2] = xl[1]; | ||
| 2063 | zlin[4*31 + 3] = xr[1]; | ||
| 2064 | |||
| 2065 | drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1); | ||
| 2066 | drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); | ||
| 2067 | drmp3d_synth_pair(dstl, nch, lins + 4*15); | ||
| 2068 | drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); | ||
| 2069 | |||
| 2070 | #if DRMP3_HAVE_SIMD | ||
| 2071 | if (drmp3_have_simd()) for (i = 14; i >= 0; i--) | ||
| 2072 | { | ||
| 2073 | #define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]); | ||
| 2074 | #define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); } | ||
| 2075 | #define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); } | ||
| 2076 | #define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); } | ||
| 2077 | drmp3_f4 a, b; | ||
| 2078 | zlin[4*i] = xl[18*(31 - i)]; | ||
| 2079 | zlin[4*i + 1] = xr[18*(31 - i)]; | ||
| 2080 | zlin[4*i + 2] = xl[1 + 18*(31 - i)]; | ||
| 2081 | zlin[4*i + 3] = xr[1 + 18*(31 - i)]; | ||
| 2082 | zlin[4*i + 64] = xl[1 + 18*(1 + i)]; | ||
| 2083 | zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; | ||
| 2084 | zlin[4*i - 64 + 2] = xl[18*(1 + i)]; | ||
| 2085 | zlin[4*i - 64 + 3] = xr[18*(1 + i)]; | ||
| 2086 | |||
| 2087 | DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) | ||
| 2088 | |||
| 2089 | { | ||
| 2090 | #ifndef DR_MP3_FLOAT_OUTPUT | ||
| 2091 | #if DRMP3_HAVE_SSE | ||
| 2092 | static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; | ||
| 2093 | static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; | ||
| 2094 | __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), | ||
| 2095 | _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); | ||
| 2096 | dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); | ||
| 2097 | dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); | ||
| 2098 | dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); | ||
| 2099 | dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); | ||
| 2100 | dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); | ||
| 2101 | dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); | ||
| 2102 | dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); | ||
| 2103 | dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); | ||
| 2104 | #else | ||
| 2105 | int16x4_t pcma, pcmb; | ||
| 2106 | a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); | ||
| 2107 | b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); | ||
| 2108 | pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); | ||
| 2109 | pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); | ||
| 2110 | vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); | ||
| 2111 | vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); | ||
| 2112 | vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); | ||
| 2113 | vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); | ||
| 2114 | vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); | ||
| 2115 | vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); | ||
| 2116 | vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); | ||
| 2117 | vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); | ||
| 2118 | #endif | ||
| 2119 | #else | ||
| 2120 | #if DRMP3_HAVE_SSE | ||
| 2121 | static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; | ||
| 2122 | #else | ||
| 2123 | const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); | ||
| 2124 | #endif | ||
| 2125 | a = DRMP3_VMUL(a, g_scale); | ||
| 2126 | b = DRMP3_VMUL(b, g_scale); | ||
| 2127 | #if DRMP3_HAVE_SSE | ||
| 2128 | _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); | ||
| 2129 | _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); | ||
| 2130 | _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); | ||
| 2131 | _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); | ||
| 2132 | _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); | ||
| 2133 | _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); | ||
| 2134 | _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); | ||
| 2135 | _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); | ||
| 2136 | #else | ||
| 2137 | vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); | ||
| 2138 | vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); | ||
| 2139 | vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); | ||
| 2140 | vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); | ||
| 2141 | vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); | ||
| 2142 | vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); | ||
| 2143 | vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); | ||
| 2144 | vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); | ||
| 2145 | #endif | ||
| 2146 | #endif /* DR_MP3_FLOAT_OUTPUT */ | ||
| 2147 | } | ||
| 2148 | } else | ||
| 2149 | #endif | ||
| 2150 | #ifdef DR_MP3_ONLY_SIMD | ||
| 2151 | {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */ | ||
| 2152 | #else | ||
| 2153 | for (i = 14; i >= 0; i--) | ||
| 2154 | { | ||
| 2155 | #define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; | ||
| 2156 | #define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } | ||
| 2157 | #define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } | ||
| 2158 | #define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } | ||
| 2159 | float a[4], b[4]; | ||
| 2160 | |||
| 2161 | zlin[4*i] = xl[18*(31 - i)]; | ||
| 2162 | zlin[4*i + 1] = xr[18*(31 - i)]; | ||
| 2163 | zlin[4*i + 2] = xl[1 + 18*(31 - i)]; | ||
| 2164 | zlin[4*i + 3] = xr[1 + 18*(31 - i)]; | ||
| 2165 | zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; | ||
| 2166 | zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; | ||
| 2167 | zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; | ||
| 2168 | zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; | ||
| 2169 | |||
| 2170 | DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7) | ||
| 2171 | |||
| 2172 | dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]); | ||
| 2173 | dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]); | ||
| 2174 | dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]); | ||
| 2175 | dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]); | ||
| 2176 | dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]); | ||
| 2177 | dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]); | ||
| 2178 | dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]); | ||
| 2179 | dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]); | ||
| 2180 | } | ||
| 2181 | #endif | ||
| 2182 | } | ||
| 2183 | |||
| 2184 | static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins) | ||
| 2185 | { | ||
| 2186 | int i; | ||
| 2187 | for (i = 0; i < nch; i++) | ||
| 2188 | { | ||
| 2189 | drmp3d_DCT_II(grbuf + 576*i, nbands); | ||
| 2190 | } | ||
| 2191 | |||
| 2192 | DRMP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); | ||
| 2193 | |||
| 2194 | for (i = 0; i < nbands; i += 2) | ||
| 2195 | { | ||
| 2196 | drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); | ||
| 2197 | } | ||
| 2198 | #ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL | ||
| 2199 | if (nch == 1) | ||
| 2200 | { | ||
| 2201 | for (i = 0; i < 15*64; i += 2) | ||
| 2202 | { | ||
| 2203 | qmf_state[i] = lins[nbands*64 + i]; | ||
| 2204 | } | ||
| 2205 | } else | ||
| 2206 | #endif | ||
| 2207 | { | ||
| 2208 | DRMP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); | ||
| 2209 | } | ||
| 2210 | } | ||
| 2211 | |||
| 2212 | static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes) | ||
| 2213 | { | ||
| 2214 | int i, nmatch; | ||
| 2215 | for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++) | ||
| 2216 | { | ||
| 2217 | i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i); | ||
| 2218 | if (i + DRMP3_HDR_SIZE > mp3_bytes) | ||
| 2219 | return nmatch > 0; | ||
| 2220 | if (!drmp3_hdr_compare(hdr, hdr + i)) | ||
| 2221 | return 0; | ||
| 2222 | } | ||
| 2223 | return 1; | ||
| 2224 | } | ||
| 2225 | |||
| 2226 | static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) | ||
| 2227 | { | ||
| 2228 | int i, k; | ||
| 2229 | for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++) | ||
| 2230 | { | ||
| 2231 | if (drmp3_hdr_valid(mp3)) | ||
| 2232 | { | ||
| 2233 | int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes); | ||
| 2234 | int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3); | ||
| 2235 | |||
| 2236 | for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++) | ||
| 2237 | { | ||
| 2238 | if (drmp3_hdr_compare(mp3, mp3 + k)) | ||
| 2239 | { | ||
| 2240 | int fb = k - drmp3_hdr_padding(mp3); | ||
| 2241 | int nextfb = fb + drmp3_hdr_padding(mp3 + k); | ||
| 2242 | if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb)) | ||
| 2243 | continue; | ||
| 2244 | frame_and_padding = k; | ||
| 2245 | frame_bytes = fb; | ||
| 2246 | *free_format_bytes = fb; | ||
| 2247 | } | ||
| 2248 | } | ||
| 2249 | |||
| 2250 | if ((frame_bytes && i + frame_and_padding <= mp3_bytes && | ||
| 2251 | drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || | ||
| 2252 | (!i && frame_and_padding == mp3_bytes)) | ||
| 2253 | { | ||
| 2254 | *ptr_frame_bytes = frame_and_padding; | ||
| 2255 | return i; | ||
| 2256 | } | ||
| 2257 | *free_format_bytes = 0; | ||
| 2258 | } | ||
| 2259 | } | ||
| 2260 | *ptr_frame_bytes = 0; | ||
| 2261 | return mp3_bytes; | ||
| 2262 | } | ||
| 2263 | |||
| 2264 | DRMP3_API void drmp3dec_init(drmp3dec *dec) | ||
| 2265 | { | ||
| 2266 | dec->header[0] = 0; | ||
| 2267 | } | ||
| 2268 | |||
| 2269 | DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info) | ||
| 2270 | { | ||
| 2271 | int i = 0, igr, frame_size = 0, success = 1; | ||
| 2272 | const drmp3_uint8 *hdr; | ||
| 2273 | drmp3_bs bs_frame[1]; | ||
| 2274 | drmp3dec_scratch scratch; | ||
| 2275 | |||
| 2276 | if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) | ||
| 2277 | { | ||
| 2278 | frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3); | ||
| 2279 | if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size))) | ||
| 2280 | { | ||
| 2281 | frame_size = 0; | ||
| 2282 | } | ||
| 2283 | } | ||
| 2284 | if (!frame_size) | ||
| 2285 | { | ||
| 2286 | DRMP3_ZERO_MEMORY(dec, sizeof(drmp3dec)); | ||
| 2287 | i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); | ||
| 2288 | if (!frame_size || i + frame_size > mp3_bytes) | ||
| 2289 | { | ||
| 2290 | info->frame_bytes = i; | ||
| 2291 | return 0; | ||
| 2292 | } | ||
| 2293 | } | ||
| 2294 | |||
| 2295 | hdr = mp3 + i; | ||
| 2296 | DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE); | ||
| 2297 | info->frame_bytes = i + frame_size; | ||
| 2298 | info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; | ||
| 2299 | info->hz = drmp3_hdr_sample_rate_hz(hdr); | ||
| 2300 | info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); | ||
| 2301 | info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); | ||
| 2302 | |||
| 2303 | drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); | ||
| 2304 | if (DRMP3_HDR_IS_CRC(hdr)) | ||
| 2305 | { | ||
| 2306 | drmp3_bs_get_bits(bs_frame, 16); | ||
| 2307 | } | ||
| 2308 | |||
| 2309 | if (info->layer == 3) | ||
| 2310 | { | ||
| 2311 | int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); | ||
| 2312 | if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) | ||
| 2313 | { | ||
| 2314 | drmp3dec_init(dec); | ||
| 2315 | return 0; | ||
| 2316 | } | ||
| 2317 | success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); | ||
| 2318 | if (success && pcm != NULL) | ||
| 2319 | { | ||
| 2320 | for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels)) | ||
| 2321 | { | ||
| 2322 | DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); | ||
| 2323 | drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); | ||
| 2324 | drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); | ||
| 2325 | } | ||
| 2326 | } | ||
| 2327 | drmp3_L3_save_reservoir(dec, &scratch); | ||
| 2328 | } else | ||
| 2329 | { | ||
| 2330 | #ifdef DR_MP3_ONLY_MP3 | ||
| 2331 | return 0; | ||
| 2332 | #else | ||
| 2333 | drmp3_L12_scale_info sci[1]; | ||
| 2334 | |||
| 2335 | if (pcm == NULL) { | ||
| 2336 | return drmp3_hdr_frame_samples(hdr); | ||
| 2337 | } | ||
| 2338 | |||
| 2339 | drmp3_L12_read_scale_info(hdr, bs_frame, sci); | ||
| 2340 | |||
| 2341 | DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); | ||
| 2342 | for (i = 0, igr = 0; igr < 3; igr++) | ||
| 2343 | { | ||
| 2344 | if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) | ||
| 2345 | { | ||
| 2346 | i = 0; | ||
| 2347 | drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); | ||
| 2348 | drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); | ||
| 2349 | DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); | ||
| 2350 | pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); | ||
| 2351 | } | ||
| 2352 | if (bs_frame->pos > bs_frame->limit) | ||
| 2353 | { | ||
| 2354 | drmp3dec_init(dec); | ||
| 2355 | return 0; | ||
| 2356 | } | ||
| 2357 | } | ||
| 2358 | #endif | ||
| 2359 | } | ||
| 2360 | |||
| 2361 | return success*drmp3_hdr_frame_samples(dec->header); | ||
| 2362 | } | ||
| 2363 | |||
| 2364 | DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples) | ||
| 2365 | { | ||
| 2366 | size_t i = 0; | ||
| 2367 | #if DRMP3_HAVE_SIMD | ||
| 2368 | size_t aligned_count = num_samples & ~7; | ||
| 2369 | for(; i < aligned_count; i+=8) | ||
| 2370 | { | ||
| 2371 | drmp3_f4 scale = DRMP3_VSET(32768.0f); | ||
| 2372 | drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale); | ||
| 2373 | drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale); | ||
| 2374 | #if DRMP3_HAVE_SSE | ||
| 2375 | drmp3_f4 s16max = DRMP3_VSET( 32767.0f); | ||
| 2376 | drmp3_f4 s16min = DRMP3_VSET(-32768.0f); | ||
| 2377 | __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), | ||
| 2378 | _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); | ||
| 2379 | out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); | ||
| 2380 | out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); | ||
| 2381 | out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); | ||
| 2382 | out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); | ||
| 2383 | out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); | ||
| 2384 | out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); | ||
| 2385 | out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); | ||
| 2386 | out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); | ||
| 2387 | #else | ||
| 2388 | int16x4_t pcma, pcmb; | ||
| 2389 | a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); | ||
| 2390 | b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); | ||
| 2391 | pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); | ||
| 2392 | pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); | ||
| 2393 | vst1_lane_s16(out+i , pcma, 0); | ||
| 2394 | vst1_lane_s16(out+i+1, pcma, 1); | ||
| 2395 | vst1_lane_s16(out+i+2, pcma, 2); | ||
| 2396 | vst1_lane_s16(out+i+3, pcma, 3); | ||
| 2397 | vst1_lane_s16(out+i+4, pcmb, 0); | ||
| 2398 | vst1_lane_s16(out+i+5, pcmb, 1); | ||
| 2399 | vst1_lane_s16(out+i+6, pcmb, 2); | ||
| 2400 | vst1_lane_s16(out+i+7, pcmb, 3); | ||
| 2401 | #endif | ||
| 2402 | } | ||
| 2403 | #endif | ||
| 2404 | for(; i < num_samples; i++) | ||
| 2405 | { | ||
| 2406 | float sample = in[i] * 32768.0f; | ||
| 2407 | if (sample >= 32766.5f) | ||
| 2408 | out[i] = (drmp3_int16) 32767; | ||
| 2409 | else if (sample <= -32767.5f) | ||
| 2410 | out[i] = (drmp3_int16)-32768; | ||
| 2411 | else | ||
| 2412 | { | ||
| 2413 | short s = (drmp3_int16)(sample + .5f); | ||
| 2414 | s -= (s < 0); /* away from zero, to be compliant */ | ||
| 2415 | out[i] = s; | ||
| 2416 | } | ||
| 2417 | } | ||
| 2418 | } | ||
| 2419 | |||
| 2420 | |||
| 2421 | |||
| 2422 | /************************************************************************************************************************************************************ | ||
| 2423 | |||
| 2424 | Main Public API | ||
| 2425 | |||
| 2426 | ************************************************************************************************************************************************************/ | ||
| 2427 | /* SIZE_MAX */ | ||
| 2428 | #if defined(SIZE_MAX) | ||
| 2429 | #define DRMP3_SIZE_MAX SIZE_MAX | ||
| 2430 | #else | ||
| 2431 | #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) | ||
| 2432 | #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF) | ||
| 2433 | #else | ||
| 2434 | #define DRMP3_SIZE_MAX 0xFFFFFFFF | ||
| 2435 | #endif | ||
| 2436 | #endif | ||
| 2437 | /* End SIZE_MAX */ | ||
| 2438 | |||
| 2439 | /* Options. */ | ||
| 2440 | #ifndef DRMP3_SEEK_LEADING_MP3_FRAMES | ||
| 2441 | #define DRMP3_SEEK_LEADING_MP3_FRAMES 2 | ||
| 2442 | #endif | ||
| 2443 | |||
| 2444 | #define DRMP3_MIN_DATA_CHUNK_SIZE 16384 | ||
| 2445 | |||
| 2446 | /* The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends at least 16K, but in an attempt to reduce data movement I'm making this slightly larger. */ | ||
| 2447 | #ifndef DRMP3_DATA_CHUNK_SIZE | ||
| 2448 | #define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE*4) | ||
| 2449 | #endif | ||
| 2450 | |||
| 2451 | |||
| 2452 | #define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) | ||
| 2453 | #define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi))) | ||
| 2454 | |||
| 2455 | #ifndef DRMP3_PI_D | ||
| 2456 | #define DRMP3_PI_D 3.14159265358979323846264 | ||
| 2457 | #endif | ||
| 2458 | |||
| 2459 | #define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2 | ||
| 2460 | |||
| 2461 | static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a) | ||
| 2462 | { | ||
| 2463 | return x*(1-a) + y*a; | ||
| 2464 | } | ||
| 2465 | static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a) | ||
| 2466 | { | ||
| 2467 | float r0 = (y - x); | ||
| 2468 | float r1 = r0*a; | ||
| 2469 | return x + r1; | ||
| 2470 | /*return x + (y - x)*a;*/ | ||
| 2471 | } | ||
| 2472 | |||
| 2473 | |||
| 2474 | /* | ||
| 2475 | Greatest common factor using Euclid's algorithm iteratively. | ||
| 2476 | */ | ||
| 2477 | static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b) | ||
| 2478 | { | ||
| 2479 | for (;;) { | ||
| 2480 | if (b == 0) { | ||
| 2481 | break; | ||
| 2482 | } else { | ||
| 2483 | drmp3_uint32 t = a; | ||
| 2484 | a = b; | ||
| 2485 | b = t % a; | ||
| 2486 | } | ||
| 2487 | } | ||
| 2488 | |||
| 2489 | return a; | ||
| 2490 | } | ||
| 2491 | |||
| 2492 | |||
| 2493 | static void* drmp3__malloc_default(size_t sz, void* pUserData) | ||
| 2494 | { | ||
| 2495 | (void)pUserData; | ||
| 2496 | return DRMP3_MALLOC(sz); | ||
| 2497 | } | ||
| 2498 | |||
| 2499 | static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData) | ||
| 2500 | { | ||
| 2501 | (void)pUserData; | ||
| 2502 | return DRMP3_REALLOC(p, sz); | ||
| 2503 | } | ||
| 2504 | |||
| 2505 | static void drmp3__free_default(void* p, void* pUserData) | ||
| 2506 | { | ||
| 2507 | (void)pUserData; | ||
| 2508 | DRMP3_FREE(p); | ||
| 2509 | } | ||
| 2510 | |||
| 2511 | |||
| 2512 | static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 2513 | { | ||
| 2514 | if (pAllocationCallbacks == NULL) { | ||
| 2515 | return NULL; | ||
| 2516 | } | ||
| 2517 | |||
| 2518 | if (pAllocationCallbacks->onMalloc != NULL) { | ||
| 2519 | return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); | ||
| 2520 | } | ||
| 2521 | |||
| 2522 | /* Try using realloc(). */ | ||
| 2523 | if (pAllocationCallbacks->onRealloc != NULL) { | ||
| 2524 | return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); | ||
| 2525 | } | ||
| 2526 | |||
| 2527 | return NULL; | ||
| 2528 | } | ||
| 2529 | |||
| 2530 | static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 2531 | { | ||
| 2532 | if (pAllocationCallbacks == NULL) { | ||
| 2533 | return NULL; | ||
| 2534 | } | ||
| 2535 | |||
| 2536 | if (pAllocationCallbacks->onRealloc != NULL) { | ||
| 2537 | return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); | ||
| 2538 | } | ||
| 2539 | |||
| 2540 | /* Try emulating realloc() in terms of malloc()/free(). */ | ||
| 2541 | if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { | ||
| 2542 | void* p2; | ||
| 2543 | |||
| 2544 | p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); | ||
| 2545 | if (p2 == NULL) { | ||
| 2546 | return NULL; | ||
| 2547 | } | ||
| 2548 | |||
| 2549 | if (p != NULL) { | ||
| 2550 | DRMP3_COPY_MEMORY(p2, p, szOld); | ||
| 2551 | pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); | ||
| 2552 | } | ||
| 2553 | |||
| 2554 | return p2; | ||
| 2555 | } | ||
| 2556 | |||
| 2557 | return NULL; | ||
| 2558 | } | ||
| 2559 | |||
| 2560 | static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 2561 | { | ||
| 2562 | if (p == NULL || pAllocationCallbacks == NULL) { | ||
| 2563 | return; | ||
| 2564 | } | ||
| 2565 | |||
| 2566 | if (pAllocationCallbacks->onFree != NULL) { | ||
| 2567 | pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); | ||
| 2568 | } | ||
| 2569 | } | ||
| 2570 | |||
| 2571 | |||
| 2572 | static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 2573 | { | ||
| 2574 | if (pAllocationCallbacks != NULL) { | ||
| 2575 | /* Copy. */ | ||
| 2576 | return *pAllocationCallbacks; | ||
| 2577 | } else { | ||
| 2578 | /* Defaults. */ | ||
| 2579 | drmp3_allocation_callbacks allocationCallbacks; | ||
| 2580 | allocationCallbacks.pUserData = NULL; | ||
| 2581 | allocationCallbacks.onMalloc = drmp3__malloc_default; | ||
| 2582 | allocationCallbacks.onRealloc = drmp3__realloc_default; | ||
| 2583 | allocationCallbacks.onFree = drmp3__free_default; | ||
| 2584 | return allocationCallbacks; | ||
| 2585 | } | ||
| 2586 | } | ||
| 2587 | |||
| 2588 | |||
| 2589 | |||
| 2590 | static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) | ||
| 2591 | { | ||
| 2592 | size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); | ||
| 2593 | pMP3->streamCursor += bytesRead; | ||
| 2594 | return bytesRead; | ||
| 2595 | } | ||
| 2596 | |||
| 2597 | static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin) | ||
| 2598 | { | ||
| 2599 | DRMP3_ASSERT(offset >= 0); | ||
| 2600 | |||
| 2601 | if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { | ||
| 2602 | return DRMP3_FALSE; | ||
| 2603 | } | ||
| 2604 | |||
| 2605 | if (origin == drmp3_seek_origin_start) { | ||
| 2606 | pMP3->streamCursor = (drmp3_uint64)offset; | ||
| 2607 | } else { | ||
| 2608 | pMP3->streamCursor += offset; | ||
| 2609 | } | ||
| 2610 | |||
| 2611 | return DRMP3_TRUE; | ||
| 2612 | } | ||
| 2613 | |||
| 2614 | static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin) | ||
| 2615 | { | ||
| 2616 | if (offset <= 0x7FFFFFFF) { | ||
| 2617 | return drmp3__on_seek(pMP3, (int)offset, origin); | ||
| 2618 | } | ||
| 2619 | |||
| 2620 | |||
| 2621 | /* Getting here "offset" is too large for a 32-bit integer. We just keep seeking forward until we hit the offset. */ | ||
| 2622 | if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) { | ||
| 2623 | return DRMP3_FALSE; | ||
| 2624 | } | ||
| 2625 | |||
| 2626 | offset -= 0x7FFFFFFF; | ||
| 2627 | while (offset > 0) { | ||
| 2628 | if (offset <= 0x7FFFFFFF) { | ||
| 2629 | if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) { | ||
| 2630 | return DRMP3_FALSE; | ||
| 2631 | } | ||
| 2632 | offset = 0; | ||
| 2633 | } else { | ||
| 2634 | if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) { | ||
| 2635 | return DRMP3_FALSE; | ||
| 2636 | } | ||
| 2637 | offset -= 0x7FFFFFFF; | ||
| 2638 | } | ||
| 2639 | } | ||
| 2640 | |||
| 2641 | return DRMP3_TRUE; | ||
| 2642 | } | ||
| 2643 | |||
| 2644 | |||
| 2645 | static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) | ||
| 2646 | { | ||
| 2647 | drmp3_uint32 pcmFramesRead = 0; | ||
| 2648 | |||
| 2649 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 2650 | DRMP3_ASSERT(pMP3->onRead != NULL); | ||
| 2651 | |||
| 2652 | if (pMP3->atEnd) { | ||
| 2653 | return 0; | ||
| 2654 | } | ||
| 2655 | |||
| 2656 | for (;;) { | ||
| 2657 | drmp3dec_frame_info info; | ||
| 2658 | |||
| 2659 | /* minimp3 recommends doing data submission in chunks of at least 16K. If we don't have at least 16K bytes available, get more. */ | ||
| 2660 | if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) { | ||
| 2661 | size_t bytesRead; | ||
| 2662 | |||
| 2663 | /* First we need to move the data down. */ | ||
| 2664 | if (pMP3->pData != NULL) { | ||
| 2665 | DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); | ||
| 2666 | } | ||
| 2667 | |||
| 2668 | pMP3->dataConsumed = 0; | ||
| 2669 | |||
| 2670 | if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) { | ||
| 2671 | drmp3_uint8* pNewData; | ||
| 2672 | size_t newDataCap; | ||
| 2673 | |||
| 2674 | newDataCap = DRMP3_DATA_CHUNK_SIZE; | ||
| 2675 | |||
| 2676 | pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); | ||
| 2677 | if (pNewData == NULL) { | ||
| 2678 | return 0; /* Out of memory. */ | ||
| 2679 | } | ||
| 2680 | |||
| 2681 | pMP3->pData = pNewData; | ||
| 2682 | pMP3->dataCapacity = newDataCap; | ||
| 2683 | } | ||
| 2684 | |||
| 2685 | bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); | ||
| 2686 | if (bytesRead == 0) { | ||
| 2687 | if (pMP3->dataSize == 0) { | ||
| 2688 | pMP3->atEnd = DRMP3_TRUE; | ||
| 2689 | return 0; /* No data. */ | ||
| 2690 | } | ||
| 2691 | } | ||
| 2692 | |||
| 2693 | pMP3->dataSize += bytesRead; | ||
| 2694 | } | ||
| 2695 | |||
| 2696 | if (pMP3->dataSize > INT_MAX) { | ||
| 2697 | pMP3->atEnd = DRMP3_TRUE; | ||
| 2698 | return 0; /* File too big. */ | ||
| 2699 | } | ||
| 2700 | |||
| 2701 | DRMP3_ASSERT(pMP3->pData != NULL); | ||
| 2702 | DRMP3_ASSERT(pMP3->dataCapacity > 0); | ||
| 2703 | |||
| 2704 | /* Do a runtime check here to try silencing a false-positive from clang-analyzer. */ | ||
| 2705 | if (pMP3->pData == NULL) { | ||
| 2706 | return 0; | ||
| 2707 | } | ||
| 2708 | |||
| 2709 | pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); /* <-- Safe size_t -> int conversion thanks to the check above. */ | ||
| 2710 | |||
| 2711 | /* Consume the data. */ | ||
| 2712 | if (info.frame_bytes > 0) { | ||
| 2713 | pMP3->dataConsumed += (size_t)info.frame_bytes; | ||
| 2714 | pMP3->dataSize -= (size_t)info.frame_bytes; | ||
| 2715 | } | ||
| 2716 | |||
| 2717 | /* pcmFramesRead will be equal to 0 if decoding failed. If it is zero and info.frame_bytes > 0 then we have successfully decoded the frame. */ | ||
| 2718 | if (pcmFramesRead > 0) { | ||
| 2719 | pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); | ||
| 2720 | pMP3->pcmFramesConsumedInMP3Frame = 0; | ||
| 2721 | pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; | ||
| 2722 | pMP3->mp3FrameChannels = info.channels; | ||
| 2723 | pMP3->mp3FrameSampleRate = info.hz; | ||
| 2724 | break; | ||
| 2725 | } else if (info.frame_bytes == 0) { | ||
| 2726 | /* Need more data. minimp3 recommends doing data submission in 16K chunks. */ | ||
| 2727 | size_t bytesRead; | ||
| 2728 | |||
| 2729 | /* First we need to move the data down. */ | ||
| 2730 | DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); | ||
| 2731 | pMP3->dataConsumed = 0; | ||
| 2732 | |||
| 2733 | if (pMP3->dataCapacity == pMP3->dataSize) { | ||
| 2734 | /* No room. Expand. */ | ||
| 2735 | drmp3_uint8* pNewData; | ||
| 2736 | size_t newDataCap; | ||
| 2737 | |||
| 2738 | newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE; | ||
| 2739 | |||
| 2740 | pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); | ||
| 2741 | if (pNewData == NULL) { | ||
| 2742 | return 0; /* Out of memory. */ | ||
| 2743 | } | ||
| 2744 | |||
| 2745 | pMP3->pData = pNewData; | ||
| 2746 | pMP3->dataCapacity = newDataCap; | ||
| 2747 | } | ||
| 2748 | |||
| 2749 | /* Fill in a chunk. */ | ||
| 2750 | bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); | ||
| 2751 | if (bytesRead == 0) { | ||
| 2752 | pMP3->atEnd = DRMP3_TRUE; | ||
| 2753 | return 0; /* Error reading more data. */ | ||
| 2754 | } | ||
| 2755 | |||
| 2756 | pMP3->dataSize += bytesRead; | ||
| 2757 | } | ||
| 2758 | }; | ||
| 2759 | |||
| 2760 | return pcmFramesRead; | ||
| 2761 | } | ||
| 2762 | |||
| 2763 | static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) | ||
| 2764 | { | ||
| 2765 | drmp3_uint32 pcmFramesRead = 0; | ||
| 2766 | drmp3dec_frame_info info; | ||
| 2767 | |||
| 2768 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 2769 | DRMP3_ASSERT(pMP3->memory.pData != NULL); | ||
| 2770 | |||
| 2771 | if (pMP3->atEnd) { | ||
| 2772 | return 0; | ||
| 2773 | } | ||
| 2774 | |||
| 2775 | for (;;) { | ||
| 2776 | pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); | ||
| 2777 | if (pcmFramesRead > 0) { | ||
| 2778 | pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); | ||
| 2779 | pMP3->pcmFramesConsumedInMP3Frame = 0; | ||
| 2780 | pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; | ||
| 2781 | pMP3->mp3FrameChannels = info.channels; | ||
| 2782 | pMP3->mp3FrameSampleRate = info.hz; | ||
| 2783 | break; | ||
| 2784 | } else if (info.frame_bytes > 0) { | ||
| 2785 | /* No frames were read, but it looks like we skipped past one. Read the next MP3 frame. */ | ||
| 2786 | pMP3->memory.currentReadPos += (size_t)info.frame_bytes; | ||
| 2787 | } else { | ||
| 2788 | /* Nothing at all was read. Abort. */ | ||
| 2789 | break; | ||
| 2790 | } | ||
| 2791 | } | ||
| 2792 | |||
| 2793 | /* Consume the data. */ | ||
| 2794 | pMP3->memory.currentReadPos += (size_t)info.frame_bytes; | ||
| 2795 | |||
| 2796 | return pcmFramesRead; | ||
| 2797 | } | ||
| 2798 | |||
| 2799 | static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) | ||
| 2800 | { | ||
| 2801 | if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { | ||
| 2802 | return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); | ||
| 2803 | } else { | ||
| 2804 | return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); | ||
| 2805 | } | ||
| 2806 | } | ||
| 2807 | |||
| 2808 | static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3) | ||
| 2809 | { | ||
| 2810 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 2811 | return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames); | ||
| 2812 | } | ||
| 2813 | |||
| 2814 | #if 0 | ||
| 2815 | static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) | ||
| 2816 | { | ||
| 2817 | drmp3_uint32 pcmFrameCount; | ||
| 2818 | |||
| 2819 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 2820 | |||
| 2821 | pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL); | ||
| 2822 | if (pcmFrameCount == 0) { | ||
| 2823 | return 0; | ||
| 2824 | } | ||
| 2825 | |||
| 2826 | /* We have essentially just skipped past the frame, so just set the remaining samples to 0. */ | ||
| 2827 | pMP3->currentPCMFrame += pcmFrameCount; | ||
| 2828 | pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; | ||
| 2829 | pMP3->pcmFramesRemainingInMP3Frame = 0; | ||
| 2830 | |||
| 2831 | return pcmFrameCount; | ||
| 2832 | } | ||
| 2833 | #endif | ||
| 2834 | |||
| 2835 | static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 2836 | { | ||
| 2837 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 2838 | DRMP3_ASSERT(onRead != NULL); | ||
| 2839 | |||
| 2840 | /* This function assumes the output object has already been reset to 0. Do not do that here, otherwise things will break. */ | ||
| 2841 | drmp3dec_init(&pMP3->decoder); | ||
| 2842 | |||
| 2843 | pMP3->onRead = onRead; | ||
| 2844 | pMP3->onSeek = onSeek; | ||
| 2845 | pMP3->pUserData = pUserData; | ||
| 2846 | pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); | ||
| 2847 | |||
| 2848 | if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { | ||
| 2849 | return DRMP3_FALSE; /* Invalid allocation callbacks. */ | ||
| 2850 | } | ||
| 2851 | |||
| 2852 | /* Decode the first frame to confirm that it is indeed a valid MP3 stream. */ | ||
| 2853 | if (drmp3_decode_next_frame(pMP3) == 0) { | ||
| 2854 | drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); /* The call above may have allocated memory. Need to make sure it's freed before aborting. */ | ||
| 2855 | return DRMP3_FALSE; /* Not a valid MP3 stream. */ | ||
| 2856 | } | ||
| 2857 | |||
| 2858 | pMP3->channels = pMP3->mp3FrameChannels; | ||
| 2859 | pMP3->sampleRate = pMP3->mp3FrameSampleRate; | ||
| 2860 | |||
| 2861 | return DRMP3_TRUE; | ||
| 2862 | } | ||
| 2863 | |||
| 2864 | DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 2865 | { | ||
| 2866 | if (pMP3 == NULL || onRead == NULL) { | ||
| 2867 | return DRMP3_FALSE; | ||
| 2868 | } | ||
| 2869 | |||
| 2870 | DRMP3_ZERO_OBJECT(pMP3); | ||
| 2871 | return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); | ||
| 2872 | } | ||
| 2873 | |||
| 2874 | |||
| 2875 | static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) | ||
| 2876 | { | ||
| 2877 | drmp3* pMP3 = (drmp3*)pUserData; | ||
| 2878 | size_t bytesRemaining; | ||
| 2879 | |||
| 2880 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 2881 | DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); | ||
| 2882 | |||
| 2883 | bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; | ||
| 2884 | if (bytesToRead > bytesRemaining) { | ||
| 2885 | bytesToRead = bytesRemaining; | ||
| 2886 | } | ||
| 2887 | |||
| 2888 | if (bytesToRead > 0) { | ||
| 2889 | DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); | ||
| 2890 | pMP3->memory.currentReadPos += bytesToRead; | ||
| 2891 | } | ||
| 2892 | |||
| 2893 | return bytesToRead; | ||
| 2894 | } | ||
| 2895 | |||
| 2896 | static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) | ||
| 2897 | { | ||
| 2898 | drmp3* pMP3 = (drmp3*)pUserData; | ||
| 2899 | |||
| 2900 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 2901 | |||
| 2902 | if (origin == drmp3_seek_origin_current) { | ||
| 2903 | if (byteOffset > 0) { | ||
| 2904 | if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { | ||
| 2905 | byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); /* Trying to seek too far forward. */ | ||
| 2906 | } | ||
| 2907 | } else { | ||
| 2908 | if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { | ||
| 2909 | byteOffset = -(int)pMP3->memory.currentReadPos; /* Trying to seek too far backwards. */ | ||
| 2910 | } | ||
| 2911 | } | ||
| 2912 | |||
| 2913 | /* This will never underflow thanks to the clamps above. */ | ||
| 2914 | pMP3->memory.currentReadPos += byteOffset; | ||
| 2915 | } else { | ||
| 2916 | if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) { | ||
| 2917 | pMP3->memory.currentReadPos = byteOffset; | ||
| 2918 | } else { | ||
| 2919 | pMP3->memory.currentReadPos = pMP3->memory.dataSize; /* Trying to seek too far forward. */ | ||
| 2920 | } | ||
| 2921 | } | ||
| 2922 | |||
| 2923 | return DRMP3_TRUE; | ||
| 2924 | } | ||
| 2925 | |||
| 2926 | DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 2927 | { | ||
| 2928 | if (pMP3 == NULL) { | ||
| 2929 | return DRMP3_FALSE; | ||
| 2930 | } | ||
| 2931 | |||
| 2932 | DRMP3_ZERO_OBJECT(pMP3); | ||
| 2933 | |||
| 2934 | if (pData == NULL || dataSize == 0) { | ||
| 2935 | return DRMP3_FALSE; | ||
| 2936 | } | ||
| 2937 | |||
| 2938 | pMP3->memory.pData = (const drmp3_uint8*)pData; | ||
| 2939 | pMP3->memory.dataSize = dataSize; | ||
| 2940 | pMP3->memory.currentReadPos = 0; | ||
| 2941 | |||
| 2942 | return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks); | ||
| 2943 | } | ||
| 2944 | |||
| 2945 | |||
| 2946 | #ifndef DR_MP3_NO_STDIO | ||
| 2947 | #include <stdio.h> | ||
| 2948 | #include <wchar.h> /* For wcslen(), wcsrtombs() */ | ||
| 2949 | |||
| 2950 | /* Errno */ | ||
| 2951 | /* drmp3_result_from_errno() is only used inside DR_MP3_NO_STDIO for now. Move this out if it's ever used elsewhere. */ | ||
| 2952 | #include <errno.h> | ||
| 2953 | static drmp3_result drmp3_result_from_errno(int e) | ||
| 2954 | { | ||
| 2955 | switch (e) | ||
| 2956 | { | ||
| 2957 | case 0: return DRMP3_SUCCESS; | ||
| 2958 | #ifdef EPERM | ||
| 2959 | case EPERM: return DRMP3_INVALID_OPERATION; | ||
| 2960 | #endif | ||
| 2961 | #ifdef ENOENT | ||
| 2962 | case ENOENT: return DRMP3_DOES_NOT_EXIST; | ||
| 2963 | #endif | ||
| 2964 | #ifdef ESRCH | ||
| 2965 | case ESRCH: return DRMP3_DOES_NOT_EXIST; | ||
| 2966 | #endif | ||
| 2967 | #ifdef EINTR | ||
| 2968 | case EINTR: return DRMP3_INTERRUPT; | ||
| 2969 | #endif | ||
| 2970 | #ifdef EIO | ||
| 2971 | case EIO: return DRMP3_IO_ERROR; | ||
| 2972 | #endif | ||
| 2973 | #ifdef ENXIO | ||
| 2974 | case ENXIO: return DRMP3_DOES_NOT_EXIST; | ||
| 2975 | #endif | ||
| 2976 | #ifdef E2BIG | ||
| 2977 | case E2BIG: return DRMP3_INVALID_ARGS; | ||
| 2978 | #endif | ||
| 2979 | #ifdef ENOEXEC | ||
| 2980 | case ENOEXEC: return DRMP3_INVALID_FILE; | ||
| 2981 | #endif | ||
| 2982 | #ifdef EBADF | ||
| 2983 | case EBADF: return DRMP3_INVALID_FILE; | ||
| 2984 | #endif | ||
| 2985 | #ifdef ECHILD | ||
| 2986 | case ECHILD: return DRMP3_ERROR; | ||
| 2987 | #endif | ||
| 2988 | #ifdef EAGAIN | ||
| 2989 | case EAGAIN: return DRMP3_UNAVAILABLE; | ||
| 2990 | #endif | ||
| 2991 | #ifdef ENOMEM | ||
| 2992 | case ENOMEM: return DRMP3_OUT_OF_MEMORY; | ||
| 2993 | #endif | ||
| 2994 | #ifdef EACCES | ||
| 2995 | case EACCES: return DRMP3_ACCESS_DENIED; | ||
| 2996 | #endif | ||
| 2997 | #ifdef EFAULT | ||
| 2998 | case EFAULT: return DRMP3_BAD_ADDRESS; | ||
| 2999 | #endif | ||
| 3000 | #ifdef ENOTBLK | ||
| 3001 | case ENOTBLK: return DRMP3_ERROR; | ||
| 3002 | #endif | ||
| 3003 | #ifdef EBUSY | ||
| 3004 | case EBUSY: return DRMP3_BUSY; | ||
| 3005 | #endif | ||
| 3006 | #ifdef EEXIST | ||
| 3007 | case EEXIST: return DRMP3_ALREADY_EXISTS; | ||
| 3008 | #endif | ||
| 3009 | #ifdef EXDEV | ||
| 3010 | case EXDEV: return DRMP3_ERROR; | ||
| 3011 | #endif | ||
| 3012 | #ifdef ENODEV | ||
| 3013 | case ENODEV: return DRMP3_DOES_NOT_EXIST; | ||
| 3014 | #endif | ||
| 3015 | #ifdef ENOTDIR | ||
| 3016 | case ENOTDIR: return DRMP3_NOT_DIRECTORY; | ||
| 3017 | #endif | ||
| 3018 | #ifdef EISDIR | ||
| 3019 | case EISDIR: return DRMP3_IS_DIRECTORY; | ||
| 3020 | #endif | ||
| 3021 | #ifdef EINVAL | ||
| 3022 | case EINVAL: return DRMP3_INVALID_ARGS; | ||
| 3023 | #endif | ||
| 3024 | #ifdef ENFILE | ||
| 3025 | case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES; | ||
| 3026 | #endif | ||
| 3027 | #ifdef EMFILE | ||
| 3028 | case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES; | ||
| 3029 | #endif | ||
| 3030 | #ifdef ENOTTY | ||
| 3031 | case ENOTTY: return DRMP3_INVALID_OPERATION; | ||
| 3032 | #endif | ||
| 3033 | #ifdef ETXTBSY | ||
| 3034 | case ETXTBSY: return DRMP3_BUSY; | ||
| 3035 | #endif | ||
| 3036 | #ifdef EFBIG | ||
| 3037 | case EFBIG: return DRMP3_TOO_BIG; | ||
| 3038 | #endif | ||
| 3039 | #ifdef ENOSPC | ||
| 3040 | case ENOSPC: return DRMP3_NO_SPACE; | ||
| 3041 | #endif | ||
| 3042 | #ifdef ESPIPE | ||
| 3043 | case ESPIPE: return DRMP3_BAD_SEEK; | ||
| 3044 | #endif | ||
| 3045 | #ifdef EROFS | ||
| 3046 | case EROFS: return DRMP3_ACCESS_DENIED; | ||
| 3047 | #endif | ||
| 3048 | #ifdef EMLINK | ||
| 3049 | case EMLINK: return DRMP3_TOO_MANY_LINKS; | ||
| 3050 | #endif | ||
| 3051 | #ifdef EPIPE | ||
| 3052 | case EPIPE: return DRMP3_BAD_PIPE; | ||
| 3053 | #endif | ||
| 3054 | #ifdef EDOM | ||
| 3055 | case EDOM: return DRMP3_OUT_OF_RANGE; | ||
| 3056 | #endif | ||
| 3057 | #ifdef ERANGE | ||
| 3058 | case ERANGE: return DRMP3_OUT_OF_RANGE; | ||
| 3059 | #endif | ||
| 3060 | #ifdef EDEADLK | ||
| 3061 | case EDEADLK: return DRMP3_DEADLOCK; | ||
| 3062 | #endif | ||
| 3063 | #ifdef ENAMETOOLONG | ||
| 3064 | case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG; | ||
| 3065 | #endif | ||
| 3066 | #ifdef ENOLCK | ||
| 3067 | case ENOLCK: return DRMP3_ERROR; | ||
| 3068 | #endif | ||
| 3069 | #ifdef ENOSYS | ||
| 3070 | case ENOSYS: return DRMP3_NOT_IMPLEMENTED; | ||
| 3071 | #endif | ||
| 3072 | #ifdef ENOTEMPTY | ||
| 3073 | case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY; | ||
| 3074 | #endif | ||
| 3075 | #ifdef ELOOP | ||
| 3076 | case ELOOP: return DRMP3_TOO_MANY_LINKS; | ||
| 3077 | #endif | ||
| 3078 | #ifdef ENOMSG | ||
| 3079 | case ENOMSG: return DRMP3_NO_MESSAGE; | ||
| 3080 | #endif | ||
| 3081 | #ifdef EIDRM | ||
| 3082 | case EIDRM: return DRMP3_ERROR; | ||
| 3083 | #endif | ||
| 3084 | #ifdef ECHRNG | ||
| 3085 | case ECHRNG: return DRMP3_ERROR; | ||
| 3086 | #endif | ||
| 3087 | #ifdef EL2NSYNC | ||
| 3088 | case EL2NSYNC: return DRMP3_ERROR; | ||
| 3089 | #endif | ||
| 3090 | #ifdef EL3HLT | ||
| 3091 | case EL3HLT: return DRMP3_ERROR; | ||
| 3092 | #endif | ||
| 3093 | #ifdef EL3RST | ||
| 3094 | case EL3RST: return DRMP3_ERROR; | ||
| 3095 | #endif | ||
| 3096 | #ifdef ELNRNG | ||
| 3097 | case ELNRNG: return DRMP3_OUT_OF_RANGE; | ||
| 3098 | #endif | ||
| 3099 | #ifdef EUNATCH | ||
| 3100 | case EUNATCH: return DRMP3_ERROR; | ||
| 3101 | #endif | ||
| 3102 | #ifdef ENOCSI | ||
| 3103 | case ENOCSI: return DRMP3_ERROR; | ||
| 3104 | #endif | ||
| 3105 | #ifdef EL2HLT | ||
| 3106 | case EL2HLT: return DRMP3_ERROR; | ||
| 3107 | #endif | ||
| 3108 | #ifdef EBADE | ||
| 3109 | case EBADE: return DRMP3_ERROR; | ||
| 3110 | #endif | ||
| 3111 | #ifdef EBADR | ||
| 3112 | case EBADR: return DRMP3_ERROR; | ||
| 3113 | #endif | ||
| 3114 | #ifdef EXFULL | ||
| 3115 | case EXFULL: return DRMP3_ERROR; | ||
| 3116 | #endif | ||
| 3117 | #ifdef ENOANO | ||
| 3118 | case ENOANO: return DRMP3_ERROR; | ||
| 3119 | #endif | ||
| 3120 | #ifdef EBADRQC | ||
| 3121 | case EBADRQC: return DRMP3_ERROR; | ||
| 3122 | #endif | ||
| 3123 | #ifdef EBADSLT | ||
| 3124 | case EBADSLT: return DRMP3_ERROR; | ||
| 3125 | #endif | ||
| 3126 | #ifdef EBFONT | ||
| 3127 | case EBFONT: return DRMP3_INVALID_FILE; | ||
| 3128 | #endif | ||
| 3129 | #ifdef ENOSTR | ||
| 3130 | case ENOSTR: return DRMP3_ERROR; | ||
| 3131 | #endif | ||
| 3132 | #ifdef ENODATA | ||
| 3133 | case ENODATA: return DRMP3_NO_DATA_AVAILABLE; | ||
| 3134 | #endif | ||
| 3135 | #ifdef ETIME | ||
| 3136 | case ETIME: return DRMP3_TIMEOUT; | ||
| 3137 | #endif | ||
| 3138 | #ifdef ENOSR | ||
| 3139 | case ENOSR: return DRMP3_NO_DATA_AVAILABLE; | ||
| 3140 | #endif | ||
| 3141 | #ifdef ENONET | ||
| 3142 | case ENONET: return DRMP3_NO_NETWORK; | ||
| 3143 | #endif | ||
| 3144 | #ifdef ENOPKG | ||
| 3145 | case ENOPKG: return DRMP3_ERROR; | ||
| 3146 | #endif | ||
| 3147 | #ifdef EREMOTE | ||
| 3148 | case EREMOTE: return DRMP3_ERROR; | ||
| 3149 | #endif | ||
| 3150 | #ifdef ENOLINK | ||
| 3151 | case ENOLINK: return DRMP3_ERROR; | ||
| 3152 | #endif | ||
| 3153 | #ifdef EADV | ||
| 3154 | case EADV: return DRMP3_ERROR; | ||
| 3155 | #endif | ||
| 3156 | #ifdef ESRMNT | ||
| 3157 | case ESRMNT: return DRMP3_ERROR; | ||
| 3158 | #endif | ||
| 3159 | #ifdef ECOMM | ||
| 3160 | case ECOMM: return DRMP3_ERROR; | ||
| 3161 | #endif | ||
| 3162 | #ifdef EPROTO | ||
| 3163 | case EPROTO: return DRMP3_ERROR; | ||
| 3164 | #endif | ||
| 3165 | #ifdef EMULTIHOP | ||
| 3166 | case EMULTIHOP: return DRMP3_ERROR; | ||
| 3167 | #endif | ||
| 3168 | #ifdef EDOTDOT | ||
| 3169 | case EDOTDOT: return DRMP3_ERROR; | ||
| 3170 | #endif | ||
| 3171 | #ifdef EBADMSG | ||
| 3172 | case EBADMSG: return DRMP3_BAD_MESSAGE; | ||
| 3173 | #endif | ||
| 3174 | #ifdef EOVERFLOW | ||
| 3175 | case EOVERFLOW: return DRMP3_TOO_BIG; | ||
| 3176 | #endif | ||
| 3177 | #ifdef ENOTUNIQ | ||
| 3178 | case ENOTUNIQ: return DRMP3_NOT_UNIQUE; | ||
| 3179 | #endif | ||
| 3180 | #ifdef EBADFD | ||
| 3181 | case EBADFD: return DRMP3_ERROR; | ||
| 3182 | #endif | ||
| 3183 | #ifdef EREMCHG | ||
| 3184 | case EREMCHG: return DRMP3_ERROR; | ||
| 3185 | #endif | ||
| 3186 | #ifdef ELIBACC | ||
| 3187 | case ELIBACC: return DRMP3_ACCESS_DENIED; | ||
| 3188 | #endif | ||
| 3189 | #ifdef ELIBBAD | ||
| 3190 | case ELIBBAD: return DRMP3_INVALID_FILE; | ||
| 3191 | #endif | ||
| 3192 | #ifdef ELIBSCN | ||
| 3193 | case ELIBSCN: return DRMP3_INVALID_FILE; | ||
| 3194 | #endif | ||
| 3195 | #ifdef ELIBMAX | ||
| 3196 | case ELIBMAX: return DRMP3_ERROR; | ||
| 3197 | #endif | ||
| 3198 | #ifdef ELIBEXEC | ||
| 3199 | case ELIBEXEC: return DRMP3_ERROR; | ||
| 3200 | #endif | ||
| 3201 | #ifdef EILSEQ | ||
| 3202 | case EILSEQ: return DRMP3_INVALID_DATA; | ||
| 3203 | #endif | ||
| 3204 | #ifdef ERESTART | ||
| 3205 | case ERESTART: return DRMP3_ERROR; | ||
| 3206 | #endif | ||
| 3207 | #ifdef ESTRPIPE | ||
| 3208 | case ESTRPIPE: return DRMP3_ERROR; | ||
| 3209 | #endif | ||
| 3210 | #ifdef EUSERS | ||
| 3211 | case EUSERS: return DRMP3_ERROR; | ||
| 3212 | #endif | ||
| 3213 | #ifdef ENOTSOCK | ||
| 3214 | case ENOTSOCK: return DRMP3_NOT_SOCKET; | ||
| 3215 | #endif | ||
| 3216 | #ifdef EDESTADDRREQ | ||
| 3217 | case EDESTADDRREQ: return DRMP3_NO_ADDRESS; | ||
| 3218 | #endif | ||
| 3219 | #ifdef EMSGSIZE | ||
| 3220 | case EMSGSIZE: return DRMP3_TOO_BIG; | ||
| 3221 | #endif | ||
| 3222 | #ifdef EPROTOTYPE | ||
| 3223 | case EPROTOTYPE: return DRMP3_BAD_PROTOCOL; | ||
| 3224 | #endif | ||
| 3225 | #ifdef ENOPROTOOPT | ||
| 3226 | case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE; | ||
| 3227 | #endif | ||
| 3228 | #ifdef EPROTONOSUPPORT | ||
| 3229 | case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED; | ||
| 3230 | #endif | ||
| 3231 | #ifdef ESOCKTNOSUPPORT | ||
| 3232 | case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED; | ||
| 3233 | #endif | ||
| 3234 | #ifdef EOPNOTSUPP | ||
| 3235 | case EOPNOTSUPP: return DRMP3_INVALID_OPERATION; | ||
| 3236 | #endif | ||
| 3237 | #ifdef EPFNOSUPPORT | ||
| 3238 | case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED; | ||
| 3239 | #endif | ||
| 3240 | #ifdef EAFNOSUPPORT | ||
| 3241 | case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED; | ||
| 3242 | #endif | ||
| 3243 | #ifdef EADDRINUSE | ||
| 3244 | case EADDRINUSE: return DRMP3_ALREADY_IN_USE; | ||
| 3245 | #endif | ||
| 3246 | #ifdef EADDRNOTAVAIL | ||
| 3247 | case EADDRNOTAVAIL: return DRMP3_ERROR; | ||
| 3248 | #endif | ||
| 3249 | #ifdef ENETDOWN | ||
| 3250 | case ENETDOWN: return DRMP3_NO_NETWORK; | ||
| 3251 | #endif | ||
| 3252 | #ifdef ENETUNREACH | ||
| 3253 | case ENETUNREACH: return DRMP3_NO_NETWORK; | ||
| 3254 | #endif | ||
| 3255 | #ifdef ENETRESET | ||
| 3256 | case ENETRESET: return DRMP3_NO_NETWORK; | ||
| 3257 | #endif | ||
| 3258 | #ifdef ECONNABORTED | ||
| 3259 | case ECONNABORTED: return DRMP3_NO_NETWORK; | ||
| 3260 | #endif | ||
| 3261 | #ifdef ECONNRESET | ||
| 3262 | case ECONNRESET: return DRMP3_CONNECTION_RESET; | ||
| 3263 | #endif | ||
| 3264 | #ifdef ENOBUFS | ||
| 3265 | case ENOBUFS: return DRMP3_NO_SPACE; | ||
| 3266 | #endif | ||
| 3267 | #ifdef EISCONN | ||
| 3268 | case EISCONN: return DRMP3_ALREADY_CONNECTED; | ||
| 3269 | #endif | ||
| 3270 | #ifdef ENOTCONN | ||
| 3271 | case ENOTCONN: return DRMP3_NOT_CONNECTED; | ||
| 3272 | #endif | ||
| 3273 | #ifdef ESHUTDOWN | ||
| 3274 | case ESHUTDOWN: return DRMP3_ERROR; | ||
| 3275 | #endif | ||
| 3276 | #ifdef ETOOMANYREFS | ||
| 3277 | case ETOOMANYREFS: return DRMP3_ERROR; | ||
| 3278 | #endif | ||
| 3279 | #ifdef ETIMEDOUT | ||
| 3280 | case ETIMEDOUT: return DRMP3_TIMEOUT; | ||
| 3281 | #endif | ||
| 3282 | #ifdef ECONNREFUSED | ||
| 3283 | case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED; | ||
| 3284 | #endif | ||
| 3285 | #ifdef EHOSTDOWN | ||
| 3286 | case EHOSTDOWN: return DRMP3_NO_HOST; | ||
| 3287 | #endif | ||
| 3288 | #ifdef EHOSTUNREACH | ||
| 3289 | case EHOSTUNREACH: return DRMP3_NO_HOST; | ||
| 3290 | #endif | ||
| 3291 | #ifdef EALREADY | ||
| 3292 | case EALREADY: return DRMP3_IN_PROGRESS; | ||
| 3293 | #endif | ||
| 3294 | #ifdef EINPROGRESS | ||
| 3295 | case EINPROGRESS: return DRMP3_IN_PROGRESS; | ||
| 3296 | #endif | ||
| 3297 | #ifdef ESTALE | ||
| 3298 | case ESTALE: return DRMP3_INVALID_FILE; | ||
| 3299 | #endif | ||
| 3300 | #ifdef EUCLEAN | ||
| 3301 | case EUCLEAN: return DRMP3_ERROR; | ||
| 3302 | #endif | ||
| 3303 | #ifdef ENOTNAM | ||
| 3304 | case ENOTNAM: return DRMP3_ERROR; | ||
| 3305 | #endif | ||
| 3306 | #ifdef ENAVAIL | ||
| 3307 | case ENAVAIL: return DRMP3_ERROR; | ||
| 3308 | #endif | ||
| 3309 | #ifdef EISNAM | ||
| 3310 | case EISNAM: return DRMP3_ERROR; | ||
| 3311 | #endif | ||
| 3312 | #ifdef EREMOTEIO | ||
| 3313 | case EREMOTEIO: return DRMP3_IO_ERROR; | ||
| 3314 | #endif | ||
| 3315 | #ifdef EDQUOT | ||
| 3316 | case EDQUOT: return DRMP3_NO_SPACE; | ||
| 3317 | #endif | ||
| 3318 | #ifdef ENOMEDIUM | ||
| 3319 | case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST; | ||
| 3320 | #endif | ||
| 3321 | #ifdef EMEDIUMTYPE | ||
| 3322 | case EMEDIUMTYPE: return DRMP3_ERROR; | ||
| 3323 | #endif | ||
| 3324 | #ifdef ECANCELED | ||
| 3325 | case ECANCELED: return DRMP3_CANCELLED; | ||
| 3326 | #endif | ||
| 3327 | #ifdef ENOKEY | ||
| 3328 | case ENOKEY: return DRMP3_ERROR; | ||
| 3329 | #endif | ||
| 3330 | #ifdef EKEYEXPIRED | ||
| 3331 | case EKEYEXPIRED: return DRMP3_ERROR; | ||
| 3332 | #endif | ||
| 3333 | #ifdef EKEYREVOKED | ||
| 3334 | case EKEYREVOKED: return DRMP3_ERROR; | ||
| 3335 | #endif | ||
| 3336 | #ifdef EKEYREJECTED | ||
| 3337 | case EKEYREJECTED: return DRMP3_ERROR; | ||
| 3338 | #endif | ||
| 3339 | #ifdef EOWNERDEAD | ||
| 3340 | case EOWNERDEAD: return DRMP3_ERROR; | ||
| 3341 | #endif | ||
| 3342 | #ifdef ENOTRECOVERABLE | ||
| 3343 | case ENOTRECOVERABLE: return DRMP3_ERROR; | ||
| 3344 | #endif | ||
| 3345 | #ifdef ERFKILL | ||
| 3346 | case ERFKILL: return DRMP3_ERROR; | ||
| 3347 | #endif | ||
| 3348 | #ifdef EHWPOISON | ||
| 3349 | case EHWPOISON: return DRMP3_ERROR; | ||
| 3350 | #endif | ||
| 3351 | default: return DRMP3_ERROR; | ||
| 3352 | } | ||
| 3353 | } | ||
| 3354 | /* End Errno */ | ||
| 3355 | |||
| 3356 | /* fopen */ | ||
| 3357 | static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) | ||
| 3358 | { | ||
| 3359 | #if defined(_MSC_VER) && _MSC_VER >= 1400 | ||
| 3360 | errno_t err; | ||
| 3361 | #endif | ||
| 3362 | |||
| 3363 | if (ppFile != NULL) { | ||
| 3364 | *ppFile = NULL; /* Safety. */ | ||
| 3365 | } | ||
| 3366 | |||
| 3367 | if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { | ||
| 3368 | return DRMP3_INVALID_ARGS; | ||
| 3369 | } | ||
| 3370 | |||
| 3371 | #if defined(_MSC_VER) && _MSC_VER >= 1400 | ||
| 3372 | err = fopen_s(ppFile, pFilePath, pOpenMode); | ||
| 3373 | if (err != 0) { | ||
| 3374 | return drmp3_result_from_errno(err); | ||
| 3375 | } | ||
| 3376 | #else | ||
| 3377 | #if defined(_WIN32) || defined(__APPLE__) | ||
| 3378 | *ppFile = fopen(pFilePath, pOpenMode); | ||
| 3379 | #else | ||
| 3380 | #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) | ||
| 3381 | *ppFile = fopen64(pFilePath, pOpenMode); | ||
| 3382 | #else | ||
| 3383 | *ppFile = fopen(pFilePath, pOpenMode); | ||
| 3384 | #endif | ||
| 3385 | #endif | ||
| 3386 | if (*ppFile == NULL) { | ||
| 3387 | drmp3_result result = drmp3_result_from_errno(errno); | ||
| 3388 | if (result == DRMP3_SUCCESS) { | ||
| 3389 | result = DRMP3_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ | ||
| 3390 | } | ||
| 3391 | |||
| 3392 | return result; | ||
| 3393 | } | ||
| 3394 | #endif | ||
| 3395 | |||
| 3396 | return DRMP3_SUCCESS; | ||
| 3397 | } | ||
| 3398 | |||
| 3399 | /* | ||
| 3400 | _wfopen() isn't always available in all compilation environments. | ||
| 3401 | |||
| 3402 | * Windows only. | ||
| 3403 | * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). | ||
| 3404 | * MinGW-64 (both 32- and 64-bit) seems to support it. | ||
| 3405 | * MinGW wraps it in !defined(__STRICT_ANSI__). | ||
| 3406 | * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). | ||
| 3407 | |||
| 3408 | This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() | ||
| 3409 | fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. | ||
| 3410 | */ | ||
| 3411 | #if defined(_WIN32) | ||
| 3412 | #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) | ||
| 3413 | #define DRMP3_HAS_WFOPEN | ||
| 3414 | #endif | ||
| 3415 | #endif | ||
| 3416 | |||
| 3417 | static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 3418 | { | ||
| 3419 | if (ppFile != NULL) { | ||
| 3420 | *ppFile = NULL; /* Safety. */ | ||
| 3421 | } | ||
| 3422 | |||
| 3423 | if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { | ||
| 3424 | return DRMP3_INVALID_ARGS; | ||
| 3425 | } | ||
| 3426 | |||
| 3427 | #if defined(DRMP3_HAS_WFOPEN) | ||
| 3428 | { | ||
| 3429 | /* Use _wfopen() on Windows. */ | ||
| 3430 | #if defined(_MSC_VER) && _MSC_VER >= 1400 | ||
| 3431 | errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); | ||
| 3432 | if (err != 0) { | ||
| 3433 | return drmp3_result_from_errno(err); | ||
| 3434 | } | ||
| 3435 | #else | ||
| 3436 | *ppFile = _wfopen(pFilePath, pOpenMode); | ||
| 3437 | if (*ppFile == NULL) { | ||
| 3438 | return drmp3_result_from_errno(errno); | ||
| 3439 | } | ||
| 3440 | #endif | ||
| 3441 | (void)pAllocationCallbacks; | ||
| 3442 | } | ||
| 3443 | #else | ||
| 3444 | /* | ||
| 3445 | Use fopen() on anything other than Windows. Requires a conversion. This is annoying because | ||
| 3446 | fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note | ||
| 3447 | that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for | ||
| 3448 | maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler | ||
| 3449 | error I'll look into improving compatibility. | ||
| 3450 | */ | ||
| 3451 | |||
| 3452 | /* | ||
| 3453 | Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just | ||
| 3454 | need to abort with an error. If you encounter a compiler lacking such support, add it to this list | ||
| 3455 | and submit a bug report and it'll be added to the library upstream. | ||
| 3456 | */ | ||
| 3457 | #if defined(__DJGPP__) | ||
| 3458 | { | ||
| 3459 | /* Nothing to do here. This will fall through to the error check below. */ | ||
| 3460 | } | ||
| 3461 | #else | ||
| 3462 | { | ||
| 3463 | mbstate_t mbs; | ||
| 3464 | size_t lenMB; | ||
| 3465 | const wchar_t* pFilePathTemp = pFilePath; | ||
| 3466 | char* pFilePathMB = NULL; | ||
| 3467 | char pOpenModeMB[32] = {0}; | ||
| 3468 | |||
| 3469 | /* Get the length first. */ | ||
| 3470 | DRMP3_ZERO_OBJECT(&mbs); | ||
| 3471 | lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); | ||
| 3472 | if (lenMB == (size_t)-1) { | ||
| 3473 | return drmp3_result_from_errno(errno); | ||
| 3474 | } | ||
| 3475 | |||
| 3476 | pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); | ||
| 3477 | if (pFilePathMB == NULL) { | ||
| 3478 | return DRMP3_OUT_OF_MEMORY; | ||
| 3479 | } | ||
| 3480 | |||
| 3481 | pFilePathTemp = pFilePath; | ||
| 3482 | DRMP3_ZERO_OBJECT(&mbs); | ||
| 3483 | wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); | ||
| 3484 | |||
| 3485 | /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ | ||
| 3486 | { | ||
| 3487 | size_t i = 0; | ||
| 3488 | for (;;) { | ||
| 3489 | if (pOpenMode[i] == 0) { | ||
| 3490 | pOpenModeMB[i] = '\0'; | ||
| 3491 | break; | ||
| 3492 | } | ||
| 3493 | |||
| 3494 | pOpenModeMB[i] = (char)pOpenMode[i]; | ||
| 3495 | i += 1; | ||
| 3496 | } | ||
| 3497 | } | ||
| 3498 | |||
| 3499 | *ppFile = fopen(pFilePathMB, pOpenModeMB); | ||
| 3500 | |||
| 3501 | drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); | ||
| 3502 | } | ||
| 3503 | #endif | ||
| 3504 | |||
| 3505 | if (*ppFile == NULL) { | ||
| 3506 | return DRMP3_ERROR; | ||
| 3507 | } | ||
| 3508 | #endif | ||
| 3509 | |||
| 3510 | return DRMP3_SUCCESS; | ||
| 3511 | } | ||
| 3512 | /* End fopen */ | ||
| 3513 | |||
| 3514 | |||
| 3515 | static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) | ||
| 3516 | { | ||
| 3517 | return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); | ||
| 3518 | } | ||
| 3519 | |||
| 3520 | static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin) | ||
| 3521 | { | ||
| 3522 | return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; | ||
| 3523 | } | ||
| 3524 | |||
| 3525 | DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 3526 | { | ||
| 3527 | drmp3_bool32 result; | ||
| 3528 | FILE* pFile; | ||
| 3529 | |||
| 3530 | if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) { | ||
| 3531 | return DRMP3_FALSE; | ||
| 3532 | } | ||
| 3533 | |||
| 3534 | result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); | ||
| 3535 | if (result != DRMP3_TRUE) { | ||
| 3536 | fclose(pFile); | ||
| 3537 | return result; | ||
| 3538 | } | ||
| 3539 | |||
| 3540 | return DRMP3_TRUE; | ||
| 3541 | } | ||
| 3542 | |||
| 3543 | DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 3544 | { | ||
| 3545 | drmp3_bool32 result; | ||
| 3546 | FILE* pFile; | ||
| 3547 | |||
| 3548 | if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) { | ||
| 3549 | return DRMP3_FALSE; | ||
| 3550 | } | ||
| 3551 | |||
| 3552 | result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); | ||
| 3553 | if (result != DRMP3_TRUE) { | ||
| 3554 | fclose(pFile); | ||
| 3555 | return result; | ||
| 3556 | } | ||
| 3557 | |||
| 3558 | return DRMP3_TRUE; | ||
| 3559 | } | ||
| 3560 | #endif | ||
| 3561 | |||
| 3562 | DRMP3_API void drmp3_uninit(drmp3* pMP3) | ||
| 3563 | { | ||
| 3564 | if (pMP3 == NULL) { | ||
| 3565 | return; | ||
| 3566 | } | ||
| 3567 | |||
| 3568 | #ifndef DR_MP3_NO_STDIO | ||
| 3569 | if (pMP3->onRead == drmp3__on_read_stdio) { | ||
| 3570 | FILE* pFile = (FILE*)pMP3->pUserData; | ||
| 3571 | if (pFile != NULL) { | ||
| 3572 | fclose(pFile); | ||
| 3573 | pMP3->pUserData = NULL; /* Make sure the file handle is cleared to NULL to we don't attempt to close it a second time. */ | ||
| 3574 | } | ||
| 3575 | } | ||
| 3576 | #endif | ||
| 3577 | |||
| 3578 | drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); | ||
| 3579 | } | ||
| 3580 | |||
| 3581 | #if defined(DR_MP3_FLOAT_OUTPUT) | ||
| 3582 | static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount) | ||
| 3583 | { | ||
| 3584 | drmp3_uint64 i; | ||
| 3585 | drmp3_uint64 i4; | ||
| 3586 | drmp3_uint64 sampleCount4; | ||
| 3587 | |||
| 3588 | /* Unrolled. */ | ||
| 3589 | i = 0; | ||
| 3590 | sampleCount4 = sampleCount >> 2; | ||
| 3591 | for (i4 = 0; i4 < sampleCount4; i4 += 1) { | ||
| 3592 | float x0 = src[i+0]; | ||
| 3593 | float x1 = src[i+1]; | ||
| 3594 | float x2 = src[i+2]; | ||
| 3595 | float x3 = src[i+3]; | ||
| 3596 | |||
| 3597 | x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); | ||
| 3598 | x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); | ||
| 3599 | x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); | ||
| 3600 | x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); | ||
| 3601 | |||
| 3602 | x0 = x0 * 32767.0f; | ||
| 3603 | x1 = x1 * 32767.0f; | ||
| 3604 | x2 = x2 * 32767.0f; | ||
| 3605 | x3 = x3 * 32767.0f; | ||
| 3606 | |||
| 3607 | dst[i+0] = (drmp3_int16)x0; | ||
| 3608 | dst[i+1] = (drmp3_int16)x1; | ||
| 3609 | dst[i+2] = (drmp3_int16)x2; | ||
| 3610 | dst[i+3] = (drmp3_int16)x3; | ||
| 3611 | |||
| 3612 | i += 4; | ||
| 3613 | } | ||
| 3614 | |||
| 3615 | /* Leftover. */ | ||
| 3616 | for (; i < sampleCount; i += 1) { | ||
| 3617 | float x = src[i]; | ||
| 3618 | x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ | ||
| 3619 | x = x * 32767.0f; /* -1..1 to -32767..32767 */ | ||
| 3620 | |||
| 3621 | dst[i] = (drmp3_int16)x; | ||
| 3622 | } | ||
| 3623 | } | ||
| 3624 | #endif | ||
| 3625 | |||
| 3626 | #if !defined(DR_MP3_FLOAT_OUTPUT) | ||
| 3627 | static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount) | ||
| 3628 | { | ||
| 3629 | drmp3_uint64 i; | ||
| 3630 | for (i = 0; i < sampleCount; i += 1) { | ||
| 3631 | float x = (float)src[i]; | ||
| 3632 | x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ | ||
| 3633 | dst[i] = x; | ||
| 3634 | } | ||
| 3635 | } | ||
| 3636 | #endif | ||
| 3637 | |||
| 3638 | |||
| 3639 | static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut) | ||
| 3640 | { | ||
| 3641 | drmp3_uint64 totalFramesRead = 0; | ||
| 3642 | |||
| 3643 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 3644 | DRMP3_ASSERT(pMP3->onRead != NULL); | ||
| 3645 | |||
| 3646 | while (framesToRead > 0) { | ||
| 3647 | drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); | ||
| 3648 | if (pBufferOut != NULL) { | ||
| 3649 | #if defined(DR_MP3_FLOAT_OUTPUT) | ||
| 3650 | /* f32 */ | ||
| 3651 | float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); | ||
| 3652 | float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); | ||
| 3653 | DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); | ||
| 3654 | #else | ||
| 3655 | /* s16 */ | ||
| 3656 | drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels); | ||
| 3657 | drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); | ||
| 3658 | DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels); | ||
| 3659 | #endif | ||
| 3660 | } | ||
| 3661 | |||
| 3662 | pMP3->currentPCMFrame += framesToConsume; | ||
| 3663 | pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; | ||
| 3664 | pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; | ||
| 3665 | totalFramesRead += framesToConsume; | ||
| 3666 | framesToRead -= framesToConsume; | ||
| 3667 | |||
| 3668 | if (framesToRead == 0) { | ||
| 3669 | break; | ||
| 3670 | } | ||
| 3671 | |||
| 3672 | DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); | ||
| 3673 | |||
| 3674 | /* | ||
| 3675 | At this point we have exhausted our in-memory buffer so we need to re-fill. Note that the sample rate may have changed | ||
| 3676 | at this point which means we'll also need to update our sample rate conversion pipeline. | ||
| 3677 | */ | ||
| 3678 | if (drmp3_decode_next_frame(pMP3) == 0) { | ||
| 3679 | break; | ||
| 3680 | } | ||
| 3681 | } | ||
| 3682 | |||
| 3683 | return totalFramesRead; | ||
| 3684 | } | ||
| 3685 | |||
| 3686 | |||
| 3687 | DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut) | ||
| 3688 | { | ||
| 3689 | if (pMP3 == NULL || pMP3->onRead == NULL) { | ||
| 3690 | return 0; | ||
| 3691 | } | ||
| 3692 | |||
| 3693 | #if defined(DR_MP3_FLOAT_OUTPUT) | ||
| 3694 | /* Fast path. No conversion required. */ | ||
| 3695 | return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); | ||
| 3696 | #else | ||
| 3697 | /* Slow path. Convert from s16 to f32. */ | ||
| 3698 | { | ||
| 3699 | drmp3_int16 pTempS16[8192]; | ||
| 3700 | drmp3_uint64 totalPCMFramesRead = 0; | ||
| 3701 | |||
| 3702 | while (totalPCMFramesRead < framesToRead) { | ||
| 3703 | drmp3_uint64 framesJustRead; | ||
| 3704 | drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; | ||
| 3705 | drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels; | ||
| 3706 | if (framesToReadNow > framesRemaining) { | ||
| 3707 | framesToReadNow = framesRemaining; | ||
| 3708 | } | ||
| 3709 | |||
| 3710 | framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); | ||
| 3711 | if (framesJustRead == 0) { | ||
| 3712 | break; | ||
| 3713 | } | ||
| 3714 | |||
| 3715 | drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); | ||
| 3716 | totalPCMFramesRead += framesJustRead; | ||
| 3717 | } | ||
| 3718 | |||
| 3719 | return totalPCMFramesRead; | ||
| 3720 | } | ||
| 3721 | #endif | ||
| 3722 | } | ||
| 3723 | |||
| 3724 | DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut) | ||
| 3725 | { | ||
| 3726 | if (pMP3 == NULL || pMP3->onRead == NULL) { | ||
| 3727 | return 0; | ||
| 3728 | } | ||
| 3729 | |||
| 3730 | #if !defined(DR_MP3_FLOAT_OUTPUT) | ||
| 3731 | /* Fast path. No conversion required. */ | ||
| 3732 | return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); | ||
| 3733 | #else | ||
| 3734 | /* Slow path. Convert from f32 to s16. */ | ||
| 3735 | { | ||
| 3736 | float pTempF32[4096]; | ||
| 3737 | drmp3_uint64 totalPCMFramesRead = 0; | ||
| 3738 | |||
| 3739 | while (totalPCMFramesRead < framesToRead) { | ||
| 3740 | drmp3_uint64 framesJustRead; | ||
| 3741 | drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; | ||
| 3742 | drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels; | ||
| 3743 | if (framesToReadNow > framesRemaining) { | ||
| 3744 | framesToReadNow = framesRemaining; | ||
| 3745 | } | ||
| 3746 | |||
| 3747 | framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); | ||
| 3748 | if (framesJustRead == 0) { | ||
| 3749 | break; | ||
| 3750 | } | ||
| 3751 | |||
| 3752 | drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); | ||
| 3753 | totalPCMFramesRead += framesJustRead; | ||
| 3754 | } | ||
| 3755 | |||
| 3756 | return totalPCMFramesRead; | ||
| 3757 | } | ||
| 3758 | #endif | ||
| 3759 | } | ||
| 3760 | |||
| 3761 | static void drmp3_reset(drmp3* pMP3) | ||
| 3762 | { | ||
| 3763 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 3764 | |||
| 3765 | pMP3->pcmFramesConsumedInMP3Frame = 0; | ||
| 3766 | pMP3->pcmFramesRemainingInMP3Frame = 0; | ||
| 3767 | pMP3->currentPCMFrame = 0; | ||
| 3768 | pMP3->dataSize = 0; | ||
| 3769 | pMP3->atEnd = DRMP3_FALSE; | ||
| 3770 | drmp3dec_init(&pMP3->decoder); | ||
| 3771 | } | ||
| 3772 | |||
| 3773 | static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) | ||
| 3774 | { | ||
| 3775 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 3776 | DRMP3_ASSERT(pMP3->onSeek != NULL); | ||
| 3777 | |||
| 3778 | /* Seek to the start of the stream to begin with. */ | ||
| 3779 | if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) { | ||
| 3780 | return DRMP3_FALSE; | ||
| 3781 | } | ||
| 3782 | |||
| 3783 | /* Clear any cached data. */ | ||
| 3784 | drmp3_reset(pMP3); | ||
| 3785 | return DRMP3_TRUE; | ||
| 3786 | } | ||
| 3787 | |||
| 3788 | |||
| 3789 | static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset) | ||
| 3790 | { | ||
| 3791 | drmp3_uint64 framesRead; | ||
| 3792 | |||
| 3793 | /* | ||
| 3794 | Just using a dumb read-and-discard for now. What would be nice is to parse only the header of the MP3 frame, and then skip over leading | ||
| 3795 | frames without spending the time doing a full decode. I cannot see an easy way to do this in minimp3, however, so it may involve some | ||
| 3796 | kind of manual processing. | ||
| 3797 | */ | ||
| 3798 | #if defined(DR_MP3_FLOAT_OUTPUT) | ||
| 3799 | framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); | ||
| 3800 | #else | ||
| 3801 | framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); | ||
| 3802 | #endif | ||
| 3803 | if (framesRead != frameOffset) { | ||
| 3804 | return DRMP3_FALSE; | ||
| 3805 | } | ||
| 3806 | |||
| 3807 | return DRMP3_TRUE; | ||
| 3808 | } | ||
| 3809 | |||
| 3810 | static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex) | ||
| 3811 | { | ||
| 3812 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 3813 | |||
| 3814 | if (frameIndex == pMP3->currentPCMFrame) { | ||
| 3815 | return DRMP3_TRUE; | ||
| 3816 | } | ||
| 3817 | |||
| 3818 | /* | ||
| 3819 | If we're moving foward we just read from where we're at. Otherwise we need to move back to the start of | ||
| 3820 | the stream and read from the beginning. | ||
| 3821 | */ | ||
| 3822 | if (frameIndex < pMP3->currentPCMFrame) { | ||
| 3823 | /* Moving backward. Move to the start of the stream and then move forward. */ | ||
| 3824 | if (!drmp3_seek_to_start_of_stream(pMP3)) { | ||
| 3825 | return DRMP3_FALSE; | ||
| 3826 | } | ||
| 3827 | } | ||
| 3828 | |||
| 3829 | DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); | ||
| 3830 | return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); | ||
| 3831 | } | ||
| 3832 | |||
| 3833 | static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex) | ||
| 3834 | { | ||
| 3835 | drmp3_uint32 iSeekPoint; | ||
| 3836 | |||
| 3837 | DRMP3_ASSERT(pSeekPointIndex != NULL); | ||
| 3838 | |||
| 3839 | *pSeekPointIndex = 0; | ||
| 3840 | |||
| 3841 | if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { | ||
| 3842 | return DRMP3_FALSE; | ||
| 3843 | } | ||
| 3844 | |||
| 3845 | /* Linear search for simplicity to begin with while I'm getting this thing working. Once it's all working change this to a binary search. */ | ||
| 3846 | for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { | ||
| 3847 | if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { | ||
| 3848 | break; /* Found it. */ | ||
| 3849 | } | ||
| 3850 | |||
| 3851 | *pSeekPointIndex = iSeekPoint; | ||
| 3852 | } | ||
| 3853 | |||
| 3854 | return DRMP3_TRUE; | ||
| 3855 | } | ||
| 3856 | |||
| 3857 | static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex) | ||
| 3858 | { | ||
| 3859 | drmp3_seek_point seekPoint; | ||
| 3860 | drmp3_uint32 priorSeekPointIndex; | ||
| 3861 | drmp3_uint16 iMP3Frame; | ||
| 3862 | drmp3_uint64 leftoverFrames; | ||
| 3863 | |||
| 3864 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 3865 | DRMP3_ASSERT(pMP3->pSeekPoints != NULL); | ||
| 3866 | DRMP3_ASSERT(pMP3->seekPointCount > 0); | ||
| 3867 | |||
| 3868 | /* If there is no prior seekpoint it means the target PCM frame comes before the first seek point. Just assume a seekpoint at the start of the file in this case. */ | ||
| 3869 | if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { | ||
| 3870 | seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; | ||
| 3871 | } else { | ||
| 3872 | seekPoint.seekPosInBytes = 0; | ||
| 3873 | seekPoint.pcmFrameIndex = 0; | ||
| 3874 | seekPoint.mp3FramesToDiscard = 0; | ||
| 3875 | seekPoint.pcmFramesToDiscard = 0; | ||
| 3876 | } | ||
| 3877 | |||
| 3878 | /* First thing to do is seek to the first byte of the relevant MP3 frame. */ | ||
| 3879 | if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) { | ||
| 3880 | return DRMP3_FALSE; /* Failed to seek. */ | ||
| 3881 | } | ||
| 3882 | |||
| 3883 | /* Clear any cached data. */ | ||
| 3884 | drmp3_reset(pMP3); | ||
| 3885 | |||
| 3886 | /* Whole MP3 frames need to be discarded first. */ | ||
| 3887 | for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { | ||
| 3888 | drmp3_uint32 pcmFramesRead; | ||
| 3889 | drmp3d_sample_t* pPCMFrames; | ||
| 3890 | |||
| 3891 | /* Pass in non-null for the last frame because we want to ensure the sample rate converter is preloaded correctly. */ | ||
| 3892 | pPCMFrames = NULL; | ||
| 3893 | if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { | ||
| 3894 | pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames; | ||
| 3895 | } | ||
| 3896 | |||
| 3897 | /* We first need to decode the next frame. */ | ||
| 3898 | pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames); | ||
| 3899 | if (pcmFramesRead == 0) { | ||
| 3900 | return DRMP3_FALSE; | ||
| 3901 | } | ||
| 3902 | } | ||
| 3903 | |||
| 3904 | /* We seeked to an MP3 frame in the raw stream so we need to make sure the current PCM frame is set correctly. */ | ||
| 3905 | pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; | ||
| 3906 | |||
| 3907 | /* | ||
| 3908 | Now at this point we can follow the same process as the brute force technique where we just skip over unnecessary MP3 frames and then | ||
| 3909 | read-and-discard at least 2 whole MP3 frames. | ||
| 3910 | */ | ||
| 3911 | leftoverFrames = frameIndex - pMP3->currentPCMFrame; | ||
| 3912 | return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); | ||
| 3913 | } | ||
| 3914 | |||
| 3915 | DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) | ||
| 3916 | { | ||
| 3917 | if (pMP3 == NULL || pMP3->onSeek == NULL) { | ||
| 3918 | return DRMP3_FALSE; | ||
| 3919 | } | ||
| 3920 | |||
| 3921 | if (frameIndex == 0) { | ||
| 3922 | return drmp3_seek_to_start_of_stream(pMP3); | ||
| 3923 | } | ||
| 3924 | |||
| 3925 | /* Use the seek table if we have one. */ | ||
| 3926 | if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { | ||
| 3927 | return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); | ||
| 3928 | } else { | ||
| 3929 | return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); | ||
| 3930 | } | ||
| 3931 | } | ||
| 3932 | |||
| 3933 | DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount) | ||
| 3934 | { | ||
| 3935 | drmp3_uint64 currentPCMFrame; | ||
| 3936 | drmp3_uint64 totalPCMFrameCount; | ||
| 3937 | drmp3_uint64 totalMP3FrameCount; | ||
| 3938 | |||
| 3939 | if (pMP3 == NULL) { | ||
| 3940 | return DRMP3_FALSE; | ||
| 3941 | } | ||
| 3942 | |||
| 3943 | /* | ||
| 3944 | The way this works is we move back to the start of the stream, iterate over each MP3 frame and calculate the frame count based | ||
| 3945 | on our output sample rate, the seek back to the PCM frame we were sitting on before calling this function. | ||
| 3946 | */ | ||
| 3947 | |||
| 3948 | /* The stream must support seeking for this to work. */ | ||
| 3949 | if (pMP3->onSeek == NULL) { | ||
| 3950 | return DRMP3_FALSE; | ||
| 3951 | } | ||
| 3952 | |||
| 3953 | /* We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later. */ | ||
| 3954 | currentPCMFrame = pMP3->currentPCMFrame; | ||
| 3955 | |||
| 3956 | if (!drmp3_seek_to_start_of_stream(pMP3)) { | ||
| 3957 | return DRMP3_FALSE; | ||
| 3958 | } | ||
| 3959 | |||
| 3960 | totalPCMFrameCount = 0; | ||
| 3961 | totalMP3FrameCount = 0; | ||
| 3962 | |||
| 3963 | for (;;) { | ||
| 3964 | drmp3_uint32 pcmFramesInCurrentMP3Frame; | ||
| 3965 | |||
| 3966 | pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL); | ||
| 3967 | if (pcmFramesInCurrentMP3Frame == 0) { | ||
| 3968 | break; | ||
| 3969 | } | ||
| 3970 | |||
| 3971 | totalPCMFrameCount += pcmFramesInCurrentMP3Frame; | ||
| 3972 | totalMP3FrameCount += 1; | ||
| 3973 | } | ||
| 3974 | |||
| 3975 | /* Finally, we need to seek back to where we were. */ | ||
| 3976 | if (!drmp3_seek_to_start_of_stream(pMP3)) { | ||
| 3977 | return DRMP3_FALSE; | ||
| 3978 | } | ||
| 3979 | |||
| 3980 | if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { | ||
| 3981 | return DRMP3_FALSE; | ||
| 3982 | } | ||
| 3983 | |||
| 3984 | if (pMP3FrameCount != NULL) { | ||
| 3985 | *pMP3FrameCount = totalMP3FrameCount; | ||
| 3986 | } | ||
| 3987 | if (pPCMFrameCount != NULL) { | ||
| 3988 | *pPCMFrameCount = totalPCMFrameCount; | ||
| 3989 | } | ||
| 3990 | |||
| 3991 | return DRMP3_TRUE; | ||
| 3992 | } | ||
| 3993 | |||
| 3994 | DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) | ||
| 3995 | { | ||
| 3996 | drmp3_uint64 totalPCMFrameCount; | ||
| 3997 | if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { | ||
| 3998 | return 0; | ||
| 3999 | } | ||
| 4000 | |||
| 4001 | return totalPCMFrameCount; | ||
| 4002 | } | ||
| 4003 | |||
| 4004 | DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) | ||
| 4005 | { | ||
| 4006 | drmp3_uint64 totalMP3FrameCount; | ||
| 4007 | if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { | ||
| 4008 | return 0; | ||
| 4009 | } | ||
| 4010 | |||
| 4011 | return totalMP3FrameCount; | ||
| 4012 | } | ||
| 4013 | |||
| 4014 | static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) | ||
| 4015 | { | ||
| 4016 | float srcRatio; | ||
| 4017 | float pcmFrameCountOutF; | ||
| 4018 | drmp3_uint32 pcmFrameCountOut; | ||
| 4019 | |||
| 4020 | srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; | ||
| 4021 | DRMP3_ASSERT(srcRatio > 0); | ||
| 4022 | |||
| 4023 | pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); | ||
| 4024 | pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF; | ||
| 4025 | *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; | ||
| 4026 | *pRunningPCMFrameCount += pcmFrameCountOut; | ||
| 4027 | } | ||
| 4028 | |||
| 4029 | typedef struct | ||
| 4030 | { | ||
| 4031 | drmp3_uint64 bytePos; | ||
| 4032 | drmp3_uint64 pcmFrameIndex; /* <-- After sample rate conversion. */ | ||
| 4033 | } drmp3__seeking_mp3_frame_info; | ||
| 4034 | |||
| 4035 | DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints) | ||
| 4036 | { | ||
| 4037 | drmp3_uint32 seekPointCount; | ||
| 4038 | drmp3_uint64 currentPCMFrame; | ||
| 4039 | drmp3_uint64 totalMP3FrameCount; | ||
| 4040 | drmp3_uint64 totalPCMFrameCount; | ||
| 4041 | |||
| 4042 | if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { | ||
| 4043 | return DRMP3_FALSE; /* Invalid args. */ | ||
| 4044 | } | ||
| 4045 | |||
| 4046 | seekPointCount = *pSeekPointCount; | ||
| 4047 | if (seekPointCount == 0) { | ||
| 4048 | return DRMP3_FALSE; /* The client has requested no seek points. Consider this to be invalid arguments since the client has probably not intended this. */ | ||
| 4049 | } | ||
| 4050 | |||
| 4051 | /* We'll need to seek back to the current sample after calculating the seekpoints so we need to go ahead and grab the current location at the top. */ | ||
| 4052 | currentPCMFrame = pMP3->currentPCMFrame; | ||
| 4053 | |||
| 4054 | /* We never do more than the total number of MP3 frames and we limit it to 32-bits. */ | ||
| 4055 | if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { | ||
| 4056 | return DRMP3_FALSE; | ||
| 4057 | } | ||
| 4058 | |||
| 4059 | /* If there's less than DRMP3_SEEK_LEADING_MP3_FRAMES+1 frames we just report 1 seek point which will be the very start of the stream. */ | ||
| 4060 | if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) { | ||
| 4061 | seekPointCount = 1; | ||
| 4062 | pSeekPoints[0].seekPosInBytes = 0; | ||
| 4063 | pSeekPoints[0].pcmFrameIndex = 0; | ||
| 4064 | pSeekPoints[0].mp3FramesToDiscard = 0; | ||
| 4065 | pSeekPoints[0].pcmFramesToDiscard = 0; | ||
| 4066 | } else { | ||
| 4067 | drmp3_uint64 pcmFramesBetweenSeekPoints; | ||
| 4068 | drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1]; | ||
| 4069 | drmp3_uint64 runningPCMFrameCount = 0; | ||
| 4070 | float runningPCMFrameCountFractionalPart = 0; | ||
| 4071 | drmp3_uint64 nextTargetPCMFrame; | ||
| 4072 | drmp3_uint32 iMP3Frame; | ||
| 4073 | drmp3_uint32 iSeekPoint; | ||
| 4074 | |||
| 4075 | if (seekPointCount > totalMP3FrameCount-1) { | ||
| 4076 | seekPointCount = (drmp3_uint32)totalMP3FrameCount-1; | ||
| 4077 | } | ||
| 4078 | |||
| 4079 | pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); | ||
| 4080 | |||
| 4081 | /* | ||
| 4082 | Here is where we actually calculate the seek points. We need to start by moving the start of the stream. We then enumerate over each | ||
| 4083 | MP3 frame. | ||
| 4084 | */ | ||
| 4085 | if (!drmp3_seek_to_start_of_stream(pMP3)) { | ||
| 4086 | return DRMP3_FALSE; | ||
| 4087 | } | ||
| 4088 | |||
| 4089 | /* | ||
| 4090 | We need to cache the byte positions of the previous MP3 frames. As a new MP3 frame is iterated, we cycle the byte positions in this | ||
| 4091 | array. The value in the first item in this array is the byte position that will be reported in the next seek point. | ||
| 4092 | */ | ||
| 4093 | |||
| 4094 | /* We need to initialize the array of MP3 byte positions for the leading MP3 frames. */ | ||
| 4095 | for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { | ||
| 4096 | drmp3_uint32 pcmFramesInCurrentMP3FrameIn; | ||
| 4097 | |||
| 4098 | /* The byte position of the next frame will be the stream's cursor position, minus whatever is sitting in the buffer. */ | ||
| 4099 | DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); | ||
| 4100 | mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; | ||
| 4101 | mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; | ||
| 4102 | |||
| 4103 | /* We need to get information about this frame so we can know how many samples it contained. */ | ||
| 4104 | pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); | ||
| 4105 | if (pcmFramesInCurrentMP3FrameIn == 0) { | ||
| 4106 | return DRMP3_FALSE; /* This should never happen. */ | ||
| 4107 | } | ||
| 4108 | |||
| 4109 | drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); | ||
| 4110 | } | ||
| 4111 | |||
| 4112 | /* | ||
| 4113 | At this point we will have extracted the byte positions of the leading MP3 frames. We can now start iterating over each seek point and | ||
| 4114 | calculate them. | ||
| 4115 | */ | ||
| 4116 | nextTargetPCMFrame = 0; | ||
| 4117 | for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { | ||
| 4118 | nextTargetPCMFrame += pcmFramesBetweenSeekPoints; | ||
| 4119 | |||
| 4120 | for (;;) { | ||
| 4121 | if (nextTargetPCMFrame < runningPCMFrameCount) { | ||
| 4122 | /* The next seek point is in the current MP3 frame. */ | ||
| 4123 | pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; | ||
| 4124 | pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; | ||
| 4125 | pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; | ||
| 4126 | pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); | ||
| 4127 | break; | ||
| 4128 | } else { | ||
| 4129 | size_t i; | ||
| 4130 | drmp3_uint32 pcmFramesInCurrentMP3FrameIn; | ||
| 4131 | |||
| 4132 | /* | ||
| 4133 | The next seek point is not in the current MP3 frame, so continue on to the next one. The first thing to do is cycle the cached | ||
| 4134 | MP3 frame info. | ||
| 4135 | */ | ||
| 4136 | for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) { | ||
| 4137 | mp3FrameInfo[i] = mp3FrameInfo[i+1]; | ||
| 4138 | } | ||
| 4139 | |||
| 4140 | /* Cache previous MP3 frame info. */ | ||
| 4141 | mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; | ||
| 4142 | mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; | ||
| 4143 | |||
| 4144 | /* | ||
| 4145 | Go to the next MP3 frame. This shouldn't ever fail, but just in case it does we just set the seek point and break. If it happens, it | ||
| 4146 | should only ever do it for the last seek point. | ||
| 4147 | */ | ||
| 4148 | pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); | ||
| 4149 | if (pcmFramesInCurrentMP3FrameIn == 0) { | ||
| 4150 | pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; | ||
| 4151 | pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; | ||
| 4152 | pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; | ||
| 4153 | pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); | ||
| 4154 | break; | ||
| 4155 | } | ||
| 4156 | |||
| 4157 | drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); | ||
| 4158 | } | ||
| 4159 | } | ||
| 4160 | } | ||
| 4161 | |||
| 4162 | /* Finally, we need to seek back to where we were. */ | ||
| 4163 | if (!drmp3_seek_to_start_of_stream(pMP3)) { | ||
| 4164 | return DRMP3_FALSE; | ||
| 4165 | } | ||
| 4166 | if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { | ||
| 4167 | return DRMP3_FALSE; | ||
| 4168 | } | ||
| 4169 | } | ||
| 4170 | |||
| 4171 | *pSeekPointCount = seekPointCount; | ||
| 4172 | return DRMP3_TRUE; | ||
| 4173 | } | ||
| 4174 | |||
| 4175 | DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints) | ||
| 4176 | { | ||
| 4177 | if (pMP3 == NULL) { | ||
| 4178 | return DRMP3_FALSE; | ||
| 4179 | } | ||
| 4180 | |||
| 4181 | if (seekPointCount == 0 || pSeekPoints == NULL) { | ||
| 4182 | /* Unbinding. */ | ||
| 4183 | pMP3->seekPointCount = 0; | ||
| 4184 | pMP3->pSeekPoints = NULL; | ||
| 4185 | } else { | ||
| 4186 | /* Binding. */ | ||
| 4187 | pMP3->seekPointCount = seekPointCount; | ||
| 4188 | pMP3->pSeekPoints = pSeekPoints; | ||
| 4189 | } | ||
| 4190 | |||
| 4191 | return DRMP3_TRUE; | ||
| 4192 | } | ||
| 4193 | |||
| 4194 | |||
| 4195 | static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) | ||
| 4196 | { | ||
| 4197 | drmp3_uint64 totalFramesRead = 0; | ||
| 4198 | drmp3_uint64 framesCapacity = 0; | ||
| 4199 | float* pFrames = NULL; | ||
| 4200 | float temp[4096]; | ||
| 4201 | |||
| 4202 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 4203 | |||
| 4204 | for (;;) { | ||
| 4205 | drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; | ||
| 4206 | drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); | ||
| 4207 | if (framesJustRead == 0) { | ||
| 4208 | break; | ||
| 4209 | } | ||
| 4210 | |||
| 4211 | /* Reallocate the output buffer if there's not enough room. */ | ||
| 4212 | if (framesCapacity < totalFramesRead + framesJustRead) { | ||
| 4213 | drmp3_uint64 oldFramesBufferSize; | ||
| 4214 | drmp3_uint64 newFramesBufferSize; | ||
| 4215 | drmp3_uint64 newFramesCap; | ||
| 4216 | float* pNewFrames; | ||
| 4217 | |||
| 4218 | newFramesCap = framesCapacity * 2; | ||
| 4219 | if (newFramesCap < totalFramesRead + framesJustRead) { | ||
| 4220 | newFramesCap = totalFramesRead + framesJustRead; | ||
| 4221 | } | ||
| 4222 | |||
| 4223 | oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); | ||
| 4224 | newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); | ||
| 4225 | if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { | ||
| 4226 | break; | ||
| 4227 | } | ||
| 4228 | |||
| 4229 | pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); | ||
| 4230 | if (pNewFrames == NULL) { | ||
| 4231 | drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); | ||
| 4232 | break; | ||
| 4233 | } | ||
| 4234 | |||
| 4235 | pFrames = pNewFrames; | ||
| 4236 | framesCapacity = newFramesCap; | ||
| 4237 | } | ||
| 4238 | |||
| 4239 | DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); | ||
| 4240 | totalFramesRead += framesJustRead; | ||
| 4241 | |||
| 4242 | /* If the number of frames we asked for is less that what we actually read it means we've reached the end. */ | ||
| 4243 | if (framesJustRead != framesToReadRightNow) { | ||
| 4244 | break; | ||
| 4245 | } | ||
| 4246 | } | ||
| 4247 | |||
| 4248 | if (pConfig != NULL) { | ||
| 4249 | pConfig->channels = pMP3->channels; | ||
| 4250 | pConfig->sampleRate = pMP3->sampleRate; | ||
| 4251 | } | ||
| 4252 | |||
| 4253 | drmp3_uninit(pMP3); | ||
| 4254 | |||
| 4255 | if (pTotalFrameCount) { | ||
| 4256 | *pTotalFrameCount = totalFramesRead; | ||
| 4257 | } | ||
| 4258 | |||
| 4259 | return pFrames; | ||
| 4260 | } | ||
| 4261 | |||
| 4262 | static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) | ||
| 4263 | { | ||
| 4264 | drmp3_uint64 totalFramesRead = 0; | ||
| 4265 | drmp3_uint64 framesCapacity = 0; | ||
| 4266 | drmp3_int16* pFrames = NULL; | ||
| 4267 | drmp3_int16 temp[4096]; | ||
| 4268 | |||
| 4269 | DRMP3_ASSERT(pMP3 != NULL); | ||
| 4270 | |||
| 4271 | for (;;) { | ||
| 4272 | drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; | ||
| 4273 | drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); | ||
| 4274 | if (framesJustRead == 0) { | ||
| 4275 | break; | ||
| 4276 | } | ||
| 4277 | |||
| 4278 | /* Reallocate the output buffer if there's not enough room. */ | ||
| 4279 | if (framesCapacity < totalFramesRead + framesJustRead) { | ||
| 4280 | drmp3_uint64 newFramesBufferSize; | ||
| 4281 | drmp3_uint64 oldFramesBufferSize; | ||
| 4282 | drmp3_uint64 newFramesCap; | ||
| 4283 | drmp3_int16* pNewFrames; | ||
| 4284 | |||
| 4285 | newFramesCap = framesCapacity * 2; | ||
| 4286 | if (newFramesCap < totalFramesRead + framesJustRead) { | ||
| 4287 | newFramesCap = totalFramesRead + framesJustRead; | ||
| 4288 | } | ||
| 4289 | |||
| 4290 | oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16); | ||
| 4291 | newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16); | ||
| 4292 | if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { | ||
| 4293 | break; | ||
| 4294 | } | ||
| 4295 | |||
| 4296 | pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); | ||
| 4297 | if (pNewFrames == NULL) { | ||
| 4298 | drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); | ||
| 4299 | break; | ||
| 4300 | } | ||
| 4301 | |||
| 4302 | pFrames = pNewFrames; | ||
| 4303 | framesCapacity = newFramesCap; | ||
| 4304 | } | ||
| 4305 | |||
| 4306 | DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16))); | ||
| 4307 | totalFramesRead += framesJustRead; | ||
| 4308 | |||
| 4309 | /* If the number of frames we asked for is less that what we actually read it means we've reached the end. */ | ||
| 4310 | if (framesJustRead != framesToReadRightNow) { | ||
| 4311 | break; | ||
| 4312 | } | ||
| 4313 | } | ||
| 4314 | |||
| 4315 | if (pConfig != NULL) { | ||
| 4316 | pConfig->channels = pMP3->channels; | ||
| 4317 | pConfig->sampleRate = pMP3->sampleRate; | ||
| 4318 | } | ||
| 4319 | |||
| 4320 | drmp3_uninit(pMP3); | ||
| 4321 | |||
| 4322 | if (pTotalFrameCount) { | ||
| 4323 | *pTotalFrameCount = totalFramesRead; | ||
| 4324 | } | ||
| 4325 | |||
| 4326 | return pFrames; | ||
| 4327 | } | ||
| 4328 | |||
| 4329 | |||
| 4330 | DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 4331 | { | ||
| 4332 | drmp3 mp3; | ||
| 4333 | if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { | ||
| 4334 | return NULL; | ||
| 4335 | } | ||
| 4336 | |||
| 4337 | return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); | ||
| 4338 | } | ||
| 4339 | |||
| 4340 | DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 4341 | { | ||
| 4342 | drmp3 mp3; | ||
| 4343 | if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { | ||
| 4344 | return NULL; | ||
| 4345 | } | ||
| 4346 | |||
| 4347 | return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); | ||
| 4348 | } | ||
| 4349 | |||
| 4350 | |||
| 4351 | DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 4352 | { | ||
| 4353 | drmp3 mp3; | ||
| 4354 | if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { | ||
| 4355 | return NULL; | ||
| 4356 | } | ||
| 4357 | |||
| 4358 | return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); | ||
| 4359 | } | ||
| 4360 | |||
| 4361 | DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 4362 | { | ||
| 4363 | drmp3 mp3; | ||
| 4364 | if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { | ||
| 4365 | return NULL; | ||
| 4366 | } | ||
| 4367 | |||
| 4368 | return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); | ||
| 4369 | } | ||
| 4370 | |||
| 4371 | |||
| 4372 | #ifndef DR_MP3_NO_STDIO | ||
| 4373 | DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 4374 | { | ||
| 4375 | drmp3 mp3; | ||
| 4376 | if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { | ||
| 4377 | return NULL; | ||
| 4378 | } | ||
| 4379 | |||
| 4380 | return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); | ||
| 4381 | } | ||
| 4382 | |||
| 4383 | DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 4384 | { | ||
| 4385 | drmp3 mp3; | ||
| 4386 | if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { | ||
| 4387 | return NULL; | ||
| 4388 | } | ||
| 4389 | |||
| 4390 | return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); | ||
| 4391 | } | ||
| 4392 | #endif | ||
| 4393 | |||
| 4394 | DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 4395 | { | ||
| 4396 | if (pAllocationCallbacks != NULL) { | ||
| 4397 | return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks); | ||
| 4398 | } else { | ||
| 4399 | return drmp3__malloc_default(sz, NULL); | ||
| 4400 | } | ||
| 4401 | } | ||
| 4402 | |||
| 4403 | DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) | ||
| 4404 | { | ||
| 4405 | if (pAllocationCallbacks != NULL) { | ||
| 4406 | drmp3__free_from_callbacks(p, pAllocationCallbacks); | ||
| 4407 | } else { | ||
| 4408 | drmp3__free_default(p, NULL); | ||
| 4409 | } | ||
| 4410 | } | ||
| 4411 | |||
| 4412 | #endif /* dr_mp3_c */ | ||
| 4413 | #endif /*DR_MP3_IMPLEMENTATION*/ | ||
| 4414 | |||
| 4415 | /* | ||
| 4416 | DIFFERENCES BETWEEN minimp3 AND dr_mp3 | ||
| 4417 | ====================================== | ||
| 4418 | - First, keep in mind that minimp3 (https://github.com/lieff/minimp3) is where all the real work was done. All of the | ||
| 4419 | code relating to the actual decoding remains mostly unmodified, apart from some namespacing changes. | ||
| 4420 | - dr_mp3 adds a pulling style API which allows you to deliver raw data via callbacks. So, rather than pushing data | ||
| 4421 | to the decoder, the decoder _pulls_ data from your callbacks. | ||
| 4422 | - In addition to callbacks, a decoder can be initialized from a block of memory and a file. | ||
| 4423 | - The dr_mp3 pull API reads PCM frames rather than whole MP3 frames. | ||
| 4424 | - dr_mp3 adds convenience APIs for opening and decoding entire files in one go. | ||
| 4425 | - dr_mp3 is fully namespaced, including the implementation section, which is more suitable when compiling projects | ||
| 4426 | as a single translation unit (aka unity builds). At the time of writing this, a unity build is not possible when | ||
| 4427 | using minimp3 in conjunction with stb_vorbis. dr_mp3 addresses this. | ||
| 4428 | */ | ||
| 4429 | |||
| 4430 | /* | ||
| 4431 | RELEASE NOTES - v0.5.0 | ||
| 4432 | ======================= | ||
| 4433 | Version 0.5.0 has breaking API changes. | ||
| 4434 | |||
| 4435 | Improved Client-Defined Memory Allocation | ||
| 4436 | ----------------------------------------- | ||
| 4437 | The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The | ||
| 4438 | existing system of DRMP3_MALLOC, DRMP3_REALLOC and DRMP3_FREE are still in place and will be used by default when no custom | ||
| 4439 | allocation callbacks are specified. | ||
| 4440 | |||
| 4441 | To use the new system, you pass in a pointer to a drmp3_allocation_callbacks object to drmp3_init() and family, like this: | ||
| 4442 | |||
| 4443 | void* my_malloc(size_t sz, void* pUserData) | ||
| 4444 | { | ||
| 4445 | return malloc(sz); | ||
| 4446 | } | ||
| 4447 | void* my_realloc(void* p, size_t sz, void* pUserData) | ||
| 4448 | { | ||
| 4449 | return realloc(p, sz); | ||
| 4450 | } | ||
| 4451 | void my_free(void* p, void* pUserData) | ||
| 4452 | { | ||
| 4453 | free(p); | ||
| 4454 | } | ||
| 4455 | |||
| 4456 | ... | ||
| 4457 | |||
| 4458 | drmp3_allocation_callbacks allocationCallbacks; | ||
| 4459 | allocationCallbacks.pUserData = &myData; | ||
| 4460 | allocationCallbacks.onMalloc = my_malloc; | ||
| 4461 | allocationCallbacks.onRealloc = my_realloc; | ||
| 4462 | allocationCallbacks.onFree = my_free; | ||
| 4463 | drmp3_init_file(&mp3, "my_file.mp3", NULL, &allocationCallbacks); | ||
| 4464 | |||
| 4465 | The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines. | ||
| 4466 | |||
| 4467 | Passing in null for the allocation callbacks object will cause dr_mp3 to use defaults which is the same as DRMP3_MALLOC, | ||
| 4468 | DRMP3_REALLOC and DRMP3_FREE and the equivalent of how it worked in previous versions. | ||
| 4469 | |||
| 4470 | Every API that opens a drmp3 object now takes this extra parameter. These include the following: | ||
| 4471 | |||
| 4472 | drmp3_init() | ||
| 4473 | drmp3_init_file() | ||
| 4474 | drmp3_init_memory() | ||
| 4475 | drmp3_open_and_read_pcm_frames_f32() | ||
| 4476 | drmp3_open_and_read_pcm_frames_s16() | ||
| 4477 | drmp3_open_memory_and_read_pcm_frames_f32() | ||
| 4478 | drmp3_open_memory_and_read_pcm_frames_s16() | ||
| 4479 | drmp3_open_file_and_read_pcm_frames_f32() | ||
| 4480 | drmp3_open_file_and_read_pcm_frames_s16() | ||
| 4481 | |||
| 4482 | Renamed APIs | ||
| 4483 | ------------ | ||
| 4484 | The following APIs have been renamed for consistency with other dr_* libraries and to make it clear that they return PCM frame | ||
| 4485 | counts rather than sample counts. | ||
| 4486 | |||
| 4487 | drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32() | ||
| 4488 | drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16() | ||
| 4489 | drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32() | ||
| 4490 | drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16() | ||
| 4491 | drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32() | ||
| 4492 | drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16() | ||
| 4493 | */ | ||
| 4494 | |||
| 4495 | /* | ||
| 4496 | REVISION HISTORY | ||
| 4497 | ================ | ||
| 4498 | v0.6.39 - 2024-02-27 | ||
| 4499 | - Fix a Wdouble-promotion warning. | ||
| 4500 | |||
| 4501 | v0.6.38 - 2023-11-02 | ||
| 4502 | - Fix build for ARMv6-M. | ||
| 4503 | |||
| 4504 | v0.6.37 - 2023-07-07 | ||
| 4505 | - Silence a static analysis warning. | ||
| 4506 | |||
| 4507 | v0.6.36 - 2023-06-17 | ||
| 4508 | - Fix an incorrect date in revision history. No functional change. | ||
| 4509 | |||
| 4510 | v0.6.35 - 2023-05-22 | ||
| 4511 | - Minor code restructure. No functional change. | ||
| 4512 | |||
| 4513 | v0.6.34 - 2022-09-17 | ||
| 4514 | - Fix compilation with DJGPP. | ||
| 4515 | - Fix compilation when compiling with x86 with no SSE2. | ||
| 4516 | - Remove an unnecessary variable from the drmp3 structure. | ||
| 4517 | |||
| 4518 | v0.6.33 - 2022-04-10 | ||
| 4519 | - Fix compilation error with the MSVC ARM64 build. | ||
| 4520 | - Fix compilation error on older versions of GCC. | ||
| 4521 | - Remove some unused functions. | ||
| 4522 | |||
| 4523 | v0.6.32 - 2021-12-11 | ||
| 4524 | - Fix a warning with Clang. | ||
| 4525 | |||
| 4526 | v0.6.31 - 2021-08-22 | ||
| 4527 | - Fix a bug when loading from memory. | ||
| 4528 | |||
| 4529 | v0.6.30 - 2021-08-16 | ||
| 4530 | - Silence some warnings. | ||
| 4531 | - Replace memory operations with DRMP3_* macros. | ||
| 4532 | |||
| 4533 | v0.6.29 - 2021-08-08 | ||
| 4534 | - Bring up to date with minimp3. | ||
| 4535 | |||
| 4536 | v0.6.28 - 2021-07-31 | ||
| 4537 | - Fix platform detection for ARM64. | ||
| 4538 | - Fix a compilation error with C89. | ||
| 4539 | |||
| 4540 | v0.6.27 - 2021-02-21 | ||
| 4541 | - Fix a warning due to referencing _MSC_VER when it is undefined. | ||
| 4542 | |||
| 4543 | v0.6.26 - 2021-01-31 | ||
| 4544 | - Bring up to date with minimp3. | ||
| 4545 | |||
| 4546 | v0.6.25 - 2020-12-26 | ||
| 4547 | - Remove DRMP3_DEFAULT_CHANNELS and DRMP3_DEFAULT_SAMPLE_RATE which are leftovers from some removed APIs. | ||
| 4548 | |||
| 4549 | v0.6.24 - 2020-12-07 | ||
| 4550 | - Fix a typo in version date for 0.6.23. | ||
| 4551 | |||
| 4552 | v0.6.23 - 2020-12-03 | ||
| 4553 | - Fix an error where a file can be closed twice when initialization of the decoder fails. | ||
| 4554 | |||
| 4555 | v0.6.22 - 2020-12-02 | ||
| 4556 | - Fix an error where it's possible for a file handle to be left open when initialization of the decoder fails. | ||
| 4557 | |||
| 4558 | v0.6.21 - 2020-11-28 | ||
| 4559 | - Bring up to date with minimp3. | ||
| 4560 | |||
| 4561 | v0.6.20 - 2020-11-21 | ||
| 4562 | - Fix compilation with OpenWatcom. | ||
| 4563 | |||
| 4564 | v0.6.19 - 2020-11-13 | ||
| 4565 | - Minor code clean up. | ||
| 4566 | |||
| 4567 | v0.6.18 - 2020-11-01 | ||
| 4568 | - Improve compiler support for older versions of GCC. | ||
| 4569 | |||
| 4570 | v0.6.17 - 2020-09-28 | ||
| 4571 | - Bring up to date with minimp3. | ||
| 4572 | |||
| 4573 | v0.6.16 - 2020-08-02 | ||
| 4574 | - Simplify sized types. | ||
| 4575 | |||
| 4576 | v0.6.15 - 2020-07-25 | ||
| 4577 | - Fix a compilation warning. | ||
| 4578 | |||
| 4579 | v0.6.14 - 2020-07-23 | ||
| 4580 | - Fix undefined behaviour with memmove(). | ||
| 4581 | |||
| 4582 | v0.6.13 - 2020-07-06 | ||
| 4583 | - Fix a bug when converting from s16 to f32 in drmp3_read_pcm_frames_f32(). | ||
| 4584 | |||
| 4585 | v0.6.12 - 2020-06-23 | ||
| 4586 | - Add include guard for the implementation section. | ||
| 4587 | |||
| 4588 | v0.6.11 - 2020-05-26 | ||
| 4589 | - Fix use of uninitialized variable error. | ||
| 4590 | |||
| 4591 | v0.6.10 - 2020-05-16 | ||
| 4592 | - Add compile-time and run-time version querying. | ||
| 4593 | - DRMP3_VERSION_MINOR | ||
| 4594 | - DRMP3_VERSION_MAJOR | ||
| 4595 | - DRMP3_VERSION_REVISION | ||
| 4596 | - DRMP3_VERSION_STRING | ||
| 4597 | - drmp3_version() | ||
| 4598 | - drmp3_version_string() | ||
| 4599 | |||
| 4600 | v0.6.9 - 2020-04-30 | ||
| 4601 | - Change the `pcm` parameter of drmp3dec_decode_frame() to a `const drmp3_uint8*` for consistency with internal APIs. | ||
| 4602 | |||
| 4603 | v0.6.8 - 2020-04-26 | ||
| 4604 | - Optimizations to decoding when initializing from memory. | ||
| 4605 | |||
| 4606 | v0.6.7 - 2020-04-25 | ||
| 4607 | - Fix a compilation error with DR_MP3_NO_STDIO | ||
| 4608 | - Optimization to decoding by reducing some data movement. | ||
| 4609 | |||
| 4610 | v0.6.6 - 2020-04-23 | ||
| 4611 | - Fix a minor bug with the running PCM frame counter. | ||
| 4612 | |||
| 4613 | v0.6.5 - 2020-04-19 | ||
| 4614 | - Fix compilation error on ARM builds. | ||
| 4615 | |||
| 4616 | v0.6.4 - 2020-04-19 | ||
| 4617 | - Bring up to date with changes to minimp3. | ||
| 4618 | |||
| 4619 | v0.6.3 - 2020-04-13 | ||
| 4620 | - Fix some pedantic warnings. | ||
| 4621 | |||
| 4622 | v0.6.2 - 2020-04-10 | ||
| 4623 | - Fix a crash in drmp3_open_*_and_read_pcm_frames_*() if the output config object is NULL. | ||
| 4624 | |||
| 4625 | v0.6.1 - 2020-04-05 | ||
| 4626 | - Fix warnings. | ||
| 4627 | |||
| 4628 | v0.6.0 - 2020-04-04 | ||
| 4629 | - API CHANGE: Remove the pConfig parameter from the following APIs: | ||
| 4630 | - drmp3_init() | ||
| 4631 | - drmp3_init_memory() | ||
| 4632 | - drmp3_init_file() | ||
| 4633 | - Add drmp3_init_file_w() for opening a file from a wchar_t encoded path. | ||
| 4634 | |||
| 4635 | v0.5.6 - 2020-02-12 | ||
| 4636 | - Bring up to date with minimp3. | ||
| 4637 | |||
| 4638 | v0.5.5 - 2020-01-29 | ||
| 4639 | - Fix a memory allocation bug in high level s16 decoding APIs. | ||
| 4640 | |||
| 4641 | v0.5.4 - 2019-12-02 | ||
| 4642 | - Fix a possible null pointer dereference when using custom memory allocators for realloc(). | ||
| 4643 | |||
| 4644 | v0.5.3 - 2019-11-14 | ||
| 4645 | - Fix typos in documentation. | ||
| 4646 | |||
| 4647 | v0.5.2 - 2019-11-02 | ||
| 4648 | - Bring up to date with minimp3. | ||
| 4649 | |||
| 4650 | v0.5.1 - 2019-10-08 | ||
| 4651 | - Fix a warning with GCC. | ||
| 4652 | |||
| 4653 | v0.5.0 - 2019-10-07 | ||
| 4654 | - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation | ||
| 4655 | routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs: | ||
| 4656 | - drmp3_init() | ||
| 4657 | - drmp3_init_file() | ||
| 4658 | - drmp3_init_memory() | ||
| 4659 | - drmp3_open_and_read_pcm_frames_f32() | ||
| 4660 | - drmp3_open_and_read_pcm_frames_s16() | ||
| 4661 | - drmp3_open_memory_and_read_pcm_frames_f32() | ||
| 4662 | - drmp3_open_memory_and_read_pcm_frames_s16() | ||
| 4663 | - drmp3_open_file_and_read_pcm_frames_f32() | ||
| 4664 | - drmp3_open_file_and_read_pcm_frames_s16() | ||
| 4665 | - API CHANGE: Renamed the following APIs: | ||
| 4666 | - drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32() | ||
| 4667 | - drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16() | ||
| 4668 | - drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32() | ||
| 4669 | - drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16() | ||
| 4670 | - drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32() | ||
| 4671 | - drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16() | ||
| 4672 | |||
| 4673 | v0.4.7 - 2019-07-28 | ||
| 4674 | - Fix a compiler error. | ||
| 4675 | |||
| 4676 | v0.4.6 - 2019-06-14 | ||
| 4677 | - Fix a compiler error. | ||
| 4678 | |||
| 4679 | v0.4.5 - 2019-06-06 | ||
| 4680 | - Bring up to date with minimp3. | ||
| 4681 | |||
| 4682 | v0.4.4 - 2019-05-06 | ||
| 4683 | - Fixes to the VC6 build. | ||
| 4684 | |||
| 4685 | v0.4.3 - 2019-05-05 | ||
| 4686 | - Use the channel count and/or sample rate of the first MP3 frame instead of DRMP3_DEFAULT_CHANNELS and | ||
| 4687 | DRMP3_DEFAULT_SAMPLE_RATE when they are set to 0. To use the old behaviour, just set the relevant property to | ||
| 4688 | DRMP3_DEFAULT_CHANNELS or DRMP3_DEFAULT_SAMPLE_RATE. | ||
| 4689 | - Add s16 reading APIs | ||
| 4690 | - drmp3_read_pcm_frames_s16 | ||
| 4691 | - drmp3_open_memory_and_read_pcm_frames_s16 | ||
| 4692 | - drmp3_open_and_read_pcm_frames_s16 | ||
| 4693 | - drmp3_open_file_and_read_pcm_frames_s16 | ||
| 4694 | - Add drmp3_get_mp3_and_pcm_frame_count() to the public header section. | ||
| 4695 | - Add support for C89. | ||
| 4696 | - Change license to choice of public domain or MIT-0. | ||
| 4697 | |||
| 4698 | v0.4.2 - 2019-02-21 | ||
| 4699 | - Fix a warning. | ||
| 4700 | |||
| 4701 | v0.4.1 - 2018-12-30 | ||
| 4702 | - Fix a warning. | ||
| 4703 | |||
| 4704 | v0.4.0 - 2018-12-16 | ||
| 4705 | - API CHANGE: Rename some APIs: | ||
| 4706 | - drmp3_read_f32 -> to drmp3_read_pcm_frames_f32 | ||
| 4707 | - drmp3_seek_to_frame -> drmp3_seek_to_pcm_frame | ||
| 4708 | - drmp3_open_and_decode_f32 -> drmp3_open_and_read_pcm_frames_f32 | ||
| 4709 | - drmp3_open_and_decode_memory_f32 -> drmp3_open_memory_and_read_pcm_frames_f32 | ||
| 4710 | - drmp3_open_and_decode_file_f32 -> drmp3_open_file_and_read_pcm_frames_f32 | ||
| 4711 | - Add drmp3_get_pcm_frame_count(). | ||
| 4712 | - Add drmp3_get_mp3_frame_count(). | ||
| 4713 | - Improve seeking performance. | ||
| 4714 | |||
| 4715 | v0.3.2 - 2018-09-11 | ||
| 4716 | - Fix a couple of memory leaks. | ||
| 4717 | - Bring up to date with minimp3. | ||
| 4718 | |||
| 4719 | v0.3.1 - 2018-08-25 | ||
| 4720 | - Fix C++ build. | ||
| 4721 | |||
| 4722 | v0.3.0 - 2018-08-25 | ||
| 4723 | - Bring up to date with minimp3. This has a minor API change: the "pcm" parameter of drmp3dec_decode_frame() has | ||
| 4724 | been changed from short* to void* because it can now output both s16 and f32 samples, depending on whether or | ||
| 4725 | not the DR_MP3_FLOAT_OUTPUT option is set. | ||
| 4726 | |||
| 4727 | v0.2.11 - 2018-08-08 | ||
| 4728 | - Fix a bug where the last part of a file is not read. | ||
| 4729 | |||
| 4730 | v0.2.10 - 2018-08-07 | ||
| 4731 | - Improve 64-bit detection. | ||
| 4732 | |||
| 4733 | v0.2.9 - 2018-08-05 | ||
| 4734 | - Fix C++ build on older versions of GCC. | ||
| 4735 | - Bring up to date with minimp3. | ||
| 4736 | |||
| 4737 | v0.2.8 - 2018-08-02 | ||
| 4738 | - Fix compilation errors with older versions of GCC. | ||
| 4739 | |||
| 4740 | v0.2.7 - 2018-07-13 | ||
| 4741 | - Bring up to date with minimp3. | ||
| 4742 | |||
| 4743 | v0.2.6 - 2018-07-12 | ||
| 4744 | - Bring up to date with minimp3. | ||
| 4745 | |||
| 4746 | v0.2.5 - 2018-06-22 | ||
| 4747 | - Bring up to date with minimp3. | ||
| 4748 | |||
| 4749 | v0.2.4 - 2018-05-12 | ||
| 4750 | - Bring up to date with minimp3. | ||
| 4751 | |||
| 4752 | v0.2.3 - 2018-04-29 | ||
| 4753 | - Fix TCC build. | ||
| 4754 | |||
| 4755 | v0.2.2 - 2018-04-28 | ||
| 4756 | - Fix bug when opening a decoder from memory. | ||
| 4757 | |||
| 4758 | v0.2.1 - 2018-04-27 | ||
| 4759 | - Efficiency improvements when the decoder reaches the end of the stream. | ||
| 4760 | |||
| 4761 | v0.2 - 2018-04-21 | ||
| 4762 | - Bring up to date with minimp3. | ||
| 4763 | - Start using major.minor.revision versioning. | ||
| 4764 | |||
| 4765 | v0.1d - 2018-03-30 | ||
| 4766 | - Bring up to date with minimp3. | ||
| 4767 | |||
| 4768 | v0.1c - 2018-03-11 | ||
| 4769 | - Fix C++ build error. | ||
| 4770 | |||
| 4771 | v0.1b - 2018-03-07 | ||
| 4772 | - Bring up to date with minimp3. | ||
| 4773 | |||
| 4774 | v0.1a - 2018-02-28 | ||
| 4775 | - Fix compilation error on GCC/Clang. | ||
| 4776 | - Fix some warnings. | ||
| 4777 | |||
| 4778 | v0.1 - 2018-02-xx | ||
| 4779 | - Initial versioned release. | ||
| 4780 | */ | ||
| 4781 | |||
| 4782 | /* | ||
| 4783 | This software is available as a choice of the following licenses. Choose | ||
| 4784 | whichever you prefer. | ||
| 4785 | |||
| 4786 | =============================================================================== | ||
| 4787 | ALTERNATIVE 1 - Public Domain (www.unlicense.org) | ||
| 4788 | =============================================================================== | ||
| 4789 | This is free and unencumbered software released into the public domain. | ||
| 4790 | |||
| 4791 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this | ||
| 4792 | software, either in source code form or as a compiled binary, for any purpose, | ||
| 4793 | commercial or non-commercial, and by any means. | ||
| 4794 | |||
| 4795 | In jurisdictions that recognize copyright laws, the author or authors of this | ||
| 4796 | software dedicate any and all copyright interest in the software to the public | ||
| 4797 | domain. We make this dedication for the benefit of the public at large and to | ||
| 4798 | the detriment of our heirs and successors. We intend this dedication to be an | ||
| 4799 | overt act of relinquishment in perpetuity of all present and future rights to | ||
| 4800 | this software under copyright law. | ||
| 4801 | |||
| 4802 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 4803 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 4804 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 4805 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 4806 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
| 4807 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 4808 | |||
| 4809 | For more information, please refer to <http://unlicense.org/> | ||
| 4810 | |||
| 4811 | =============================================================================== | ||
| 4812 | ALTERNATIVE 2 - MIT No Attribution | ||
| 4813 | =============================================================================== | ||
| 4814 | Copyright 2023 David Reid | ||
| 4815 | |||
| 4816 | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| 4817 | this software and associated documentation files (the "Software"), to deal in | ||
| 4818 | the Software without restriction, including without limitation the rights to | ||
| 4819 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
| 4820 | of the Software, and to permit persons to whom the Software is furnished to do | ||
| 4821 | so. | ||
| 4822 | |||
| 4823 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 4824 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 4825 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 4826 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 4827 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 4828 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 4829 | SOFTWARE. | ||
| 4830 | */ | ||
| 4831 | |||
| 4832 | /* | ||
| 4833 | https://github.com/lieff/minimp3 | ||
| 4834 | To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. | ||
| 4835 | This software is distributed without any warranty. | ||
| 4836 | See <http://creativecommons.org/publicdomain/zero/1.0/>. | ||
| 4837 | */ | ||
diff --git a/raylib/src/external/dr_wav.h b/raylib/src/external/dr_wav.h new file mode 100644 index 0000000..a8207ab --- /dev/null +++ b/raylib/src/external/dr_wav.h | |||
| @@ -0,0 +1,8815 @@ | |||
| 1 | /* | ||
| 2 | WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file. | ||
| 3 | dr_wav - v0.13.16 - 2024-02-27 | ||
| 4 | |||
| 5 | David Reid - mackron@gmail.com | ||
| 6 | |||
| 7 | GitHub: https://github.com/mackron/dr_libs | ||
| 8 | */ | ||
| 9 | |||
| 10 | /* | ||
| 11 | Introduction | ||
| 12 | ============ | ||
| 13 | This is a single file library. To use it, do something like the following in one .c file. | ||
| 14 | |||
| 15 | ```c | ||
| 16 | #define DR_WAV_IMPLEMENTATION | ||
| 17 | #include "dr_wav.h" | ||
| 18 | ``` | ||
| 19 | |||
| 20 | You can then #include this file in other parts of the program as you would with any other header file. Do something like the following to read audio data: | ||
| 21 | |||
| 22 | ```c | ||
| 23 | drwav wav; | ||
| 24 | if (!drwav_init_file(&wav, "my_song.wav", NULL)) { | ||
| 25 | // Error opening WAV file. | ||
| 26 | } | ||
| 27 | |||
| 28 | drwav_int32* pDecodedInterleavedPCMFrames = malloc(wav.totalPCMFrameCount * wav.channels * sizeof(drwav_int32)); | ||
| 29 | size_t numberOfSamplesActuallyDecoded = drwav_read_pcm_frames_s32(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames); | ||
| 30 | |||
| 31 | ... | ||
| 32 | |||
| 33 | drwav_uninit(&wav); | ||
| 34 | ``` | ||
| 35 | |||
| 36 | If you just want to quickly open and read the audio data in a single operation you can do something like this: | ||
| 37 | |||
| 38 | ```c | ||
| 39 | unsigned int channels; | ||
| 40 | unsigned int sampleRate; | ||
| 41 | drwav_uint64 totalPCMFrameCount; | ||
| 42 | float* pSampleData = drwav_open_file_and_read_pcm_frames_f32("my_song.wav", &channels, &sampleRate, &totalPCMFrameCount, NULL); | ||
| 43 | if (pSampleData == NULL) { | ||
| 44 | // Error opening and reading WAV file. | ||
| 45 | } | ||
| 46 | |||
| 47 | ... | ||
| 48 | |||
| 49 | drwav_free(pSampleData, NULL); | ||
| 50 | ``` | ||
| 51 | |||
| 52 | The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in this case), but you can still output the | ||
| 53 | audio data in its internal format (see notes below for supported formats): | ||
| 54 | |||
| 55 | ```c | ||
| 56 | size_t framesRead = drwav_read_pcm_frames(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames); | ||
| 57 | ``` | ||
| 58 | |||
| 59 | You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for a particular data format: | ||
| 60 | |||
| 61 | ```c | ||
| 62 | size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer); | ||
| 63 | ``` | ||
| 64 | |||
| 65 | dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at `drwav_init_write()`, | ||
| 66 | `drwav_init_file_write()`, etc. Use `drwav_write_pcm_frames()` to write samples, or `drwav_write_raw()` to write raw data in the "data" chunk. | ||
| 67 | |||
| 68 | ```c | ||
| 69 | drwav_data_format format; | ||
| 70 | format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64. | ||
| 71 | format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes. | ||
| 72 | format.channels = 2; | ||
| 73 | format.sampleRate = 44100; | ||
| 74 | format.bitsPerSample = 16; | ||
| 75 | drwav_init_file_write(&wav, "data/recording.wav", &format, NULL); | ||
| 76 | |||
| 77 | ... | ||
| 78 | |||
| 79 | drwav_uint64 framesWritten = drwav_write_pcm_frames(pWav, frameCount, pSamples); | ||
| 80 | ``` | ||
| 81 | |||
| 82 | Note that writing to AIFF or RIFX is not supported. | ||
| 83 | |||
| 84 | dr_wav has support for decoding from a number of different encapsulation formats. See below for details. | ||
| 85 | |||
| 86 | |||
| 87 | Build Options | ||
| 88 | ============= | ||
| 89 | #define these options before including this file. | ||
| 90 | |||
| 91 | #define DR_WAV_NO_CONVERSION_API | ||
| 92 | Disables conversion APIs such as `drwav_read_pcm_frames_f32()` and `drwav_s16_to_f32()`. | ||
| 93 | |||
| 94 | #define DR_WAV_NO_STDIO | ||
| 95 | Disables APIs that initialize a decoder from a file such as `drwav_init_file()`, `drwav_init_file_write()`, etc. | ||
| 96 | |||
| 97 | #define DR_WAV_NO_WCHAR | ||
| 98 | Disables all functions ending with `_w`. Use this if your compiler does not provide wchar.h. Not required if DR_WAV_NO_STDIO is also defined. | ||
| 99 | |||
| 100 | |||
| 101 | Supported Encapsulations | ||
| 102 | ======================== | ||
| 103 | - RIFF (Regular WAV) | ||
| 104 | - RIFX (Big-Endian) | ||
| 105 | - AIFF (Does not currently support ADPCM) | ||
| 106 | - RF64 | ||
| 107 | - W64 | ||
| 108 | |||
| 109 | Note that AIFF and RIFX do not support write mode, nor do they support reading of metadata. | ||
| 110 | |||
| 111 | |||
| 112 | Supported Encodings | ||
| 113 | =================== | ||
| 114 | - Unsigned 8-bit PCM | ||
| 115 | - Signed 12-bit PCM | ||
| 116 | - Signed 16-bit PCM | ||
| 117 | - Signed 24-bit PCM | ||
| 118 | - Signed 32-bit PCM | ||
| 119 | - IEEE 32-bit floating point | ||
| 120 | - IEEE 64-bit floating point | ||
| 121 | - A-law and u-law | ||
| 122 | - Microsoft ADPCM | ||
| 123 | - IMA ADPCM (DVI, format code 0x11) | ||
| 124 | |||
| 125 | 8-bit PCM encodings are always assumed to be unsigned. Signed 8-bit encoding can only be read with `drwav_read_raw()`. | ||
| 126 | |||
| 127 | Note that ADPCM is not currently supported with AIFF. Contributions welcome. | ||
| 128 | |||
| 129 | |||
| 130 | Notes | ||
| 131 | ===== | ||
| 132 | - Samples are always interleaved. | ||
| 133 | - The default read function does not do any data conversion. Use `drwav_read_pcm_frames_f32()`, `drwav_read_pcm_frames_s32()` and `drwav_read_pcm_frames_s16()` | ||
| 134 | to read and convert audio data to 32-bit floating point, signed 32-bit integer and signed 16-bit integer samples respectively. | ||
| 135 | - dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format. | ||
| 136 | */ | ||
| 137 | |||
| 138 | #ifndef dr_wav_h | ||
| 139 | #define dr_wav_h | ||
| 140 | |||
| 141 | #ifdef __cplusplus | ||
| 142 | extern "C" { | ||
| 143 | #endif | ||
| 144 | |||
| 145 | #define DRWAV_STRINGIFY(x) #x | ||
| 146 | #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) | ||
| 147 | |||
| 148 | #define DRWAV_VERSION_MAJOR 0 | ||
| 149 | #define DRWAV_VERSION_MINOR 13 | ||
| 150 | #define DRWAV_VERSION_REVISION 16 | ||
| 151 | #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) | ||
| 152 | |||
| 153 | #include <stddef.h> /* For size_t. */ | ||
| 154 | |||
| 155 | /* Sized Types */ | ||
| 156 | typedef signed char drwav_int8; | ||
| 157 | typedef unsigned char drwav_uint8; | ||
| 158 | typedef signed short drwav_int16; | ||
| 159 | typedef unsigned short drwav_uint16; | ||
| 160 | typedef signed int drwav_int32; | ||
| 161 | typedef unsigned int drwav_uint32; | ||
| 162 | #if defined(_MSC_VER) && !defined(__clang__) | ||
| 163 | typedef signed __int64 drwav_int64; | ||
| 164 | typedef unsigned __int64 drwav_uint64; | ||
| 165 | #else | ||
| 166 | #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) | ||
| 167 | #pragma GCC diagnostic push | ||
| 168 | #pragma GCC diagnostic ignored "-Wlong-long" | ||
| 169 | #if defined(__clang__) | ||
| 170 | #pragma GCC diagnostic ignored "-Wc++11-long-long" | ||
| 171 | #endif | ||
| 172 | #endif | ||
| 173 | typedef signed long long drwav_int64; | ||
| 174 | typedef unsigned long long drwav_uint64; | ||
| 175 | #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) | ||
| 176 | #pragma GCC diagnostic pop | ||
| 177 | #endif | ||
| 178 | #endif | ||
| 179 | #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) | ||
| 180 | typedef drwav_uint64 drwav_uintptr; | ||
| 181 | #else | ||
| 182 | typedef drwav_uint32 drwav_uintptr; | ||
| 183 | #endif | ||
| 184 | typedef drwav_uint8 drwav_bool8; | ||
| 185 | typedef drwav_uint32 drwav_bool32; | ||
| 186 | #define DRWAV_TRUE 1 | ||
| 187 | #define DRWAV_FALSE 0 | ||
| 188 | /* End Sized Types */ | ||
| 189 | |||
| 190 | /* Decorations */ | ||
| 191 | #if !defined(DRWAV_API) | ||
| 192 | #if defined(DRWAV_DLL) | ||
| 193 | #if defined(_WIN32) | ||
| 194 | #define DRWAV_DLL_IMPORT __declspec(dllimport) | ||
| 195 | #define DRWAV_DLL_EXPORT __declspec(dllexport) | ||
| 196 | #define DRWAV_DLL_PRIVATE static | ||
| 197 | #else | ||
| 198 | #if defined(__GNUC__) && __GNUC__ >= 4 | ||
| 199 | #define DRWAV_DLL_IMPORT __attribute__((visibility("default"))) | ||
| 200 | #define DRWAV_DLL_EXPORT __attribute__((visibility("default"))) | ||
| 201 | #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden"))) | ||
| 202 | #else | ||
| 203 | #define DRWAV_DLL_IMPORT | ||
| 204 | #define DRWAV_DLL_EXPORT | ||
| 205 | #define DRWAV_DLL_PRIVATE static | ||
| 206 | #endif | ||
| 207 | #endif | ||
| 208 | |||
| 209 | #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION) | ||
| 210 | #define DRWAV_API DRWAV_DLL_EXPORT | ||
| 211 | #else | ||
| 212 | #define DRWAV_API DRWAV_DLL_IMPORT | ||
| 213 | #endif | ||
| 214 | #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE | ||
| 215 | #else | ||
| 216 | #define DRWAV_API extern | ||
| 217 | #define DRWAV_PRIVATE static | ||
| 218 | #endif | ||
| 219 | #endif | ||
| 220 | /* End Decorations */ | ||
| 221 | |||
| 222 | /* Result Codes */ | ||
| 223 | typedef drwav_int32 drwav_result; | ||
| 224 | #define DRWAV_SUCCESS 0 | ||
| 225 | #define DRWAV_ERROR -1 /* A generic error. */ | ||
| 226 | #define DRWAV_INVALID_ARGS -2 | ||
| 227 | #define DRWAV_INVALID_OPERATION -3 | ||
| 228 | #define DRWAV_OUT_OF_MEMORY -4 | ||
| 229 | #define DRWAV_OUT_OF_RANGE -5 | ||
| 230 | #define DRWAV_ACCESS_DENIED -6 | ||
| 231 | #define DRWAV_DOES_NOT_EXIST -7 | ||
| 232 | #define DRWAV_ALREADY_EXISTS -8 | ||
| 233 | #define DRWAV_TOO_MANY_OPEN_FILES -9 | ||
| 234 | #define DRWAV_INVALID_FILE -10 | ||
| 235 | #define DRWAV_TOO_BIG -11 | ||
| 236 | #define DRWAV_PATH_TOO_LONG -12 | ||
| 237 | #define DRWAV_NAME_TOO_LONG -13 | ||
| 238 | #define DRWAV_NOT_DIRECTORY -14 | ||
| 239 | #define DRWAV_IS_DIRECTORY -15 | ||
| 240 | #define DRWAV_DIRECTORY_NOT_EMPTY -16 | ||
| 241 | #define DRWAV_END_OF_FILE -17 | ||
| 242 | #define DRWAV_NO_SPACE -18 | ||
| 243 | #define DRWAV_BUSY -19 | ||
| 244 | #define DRWAV_IO_ERROR -20 | ||
| 245 | #define DRWAV_INTERRUPT -21 | ||
| 246 | #define DRWAV_UNAVAILABLE -22 | ||
| 247 | #define DRWAV_ALREADY_IN_USE -23 | ||
| 248 | #define DRWAV_BAD_ADDRESS -24 | ||
| 249 | #define DRWAV_BAD_SEEK -25 | ||
| 250 | #define DRWAV_BAD_PIPE -26 | ||
| 251 | #define DRWAV_DEADLOCK -27 | ||
| 252 | #define DRWAV_TOO_MANY_LINKS -28 | ||
| 253 | #define DRWAV_NOT_IMPLEMENTED -29 | ||
| 254 | #define DRWAV_NO_MESSAGE -30 | ||
| 255 | #define DRWAV_BAD_MESSAGE -31 | ||
| 256 | #define DRWAV_NO_DATA_AVAILABLE -32 | ||
| 257 | #define DRWAV_INVALID_DATA -33 | ||
| 258 | #define DRWAV_TIMEOUT -34 | ||
| 259 | #define DRWAV_NO_NETWORK -35 | ||
| 260 | #define DRWAV_NOT_UNIQUE -36 | ||
| 261 | #define DRWAV_NOT_SOCKET -37 | ||
| 262 | #define DRWAV_NO_ADDRESS -38 | ||
| 263 | #define DRWAV_BAD_PROTOCOL -39 | ||
| 264 | #define DRWAV_PROTOCOL_UNAVAILABLE -40 | ||
| 265 | #define DRWAV_PROTOCOL_NOT_SUPPORTED -41 | ||
| 266 | #define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42 | ||
| 267 | #define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43 | ||
| 268 | #define DRWAV_SOCKET_NOT_SUPPORTED -44 | ||
| 269 | #define DRWAV_CONNECTION_RESET -45 | ||
| 270 | #define DRWAV_ALREADY_CONNECTED -46 | ||
| 271 | #define DRWAV_NOT_CONNECTED -47 | ||
| 272 | #define DRWAV_CONNECTION_REFUSED -48 | ||
| 273 | #define DRWAV_NO_HOST -49 | ||
| 274 | #define DRWAV_IN_PROGRESS -50 | ||
| 275 | #define DRWAV_CANCELLED -51 | ||
| 276 | #define DRWAV_MEMORY_ALREADY_MAPPED -52 | ||
| 277 | #define DRWAV_AT_END -53 | ||
| 278 | /* End Result Codes */ | ||
| 279 | |||
| 280 | /* Common data formats. */ | ||
| 281 | #define DR_WAVE_FORMAT_PCM 0x1 | ||
| 282 | #define DR_WAVE_FORMAT_ADPCM 0x2 | ||
| 283 | #define DR_WAVE_FORMAT_IEEE_FLOAT 0x3 | ||
| 284 | #define DR_WAVE_FORMAT_ALAW 0x6 | ||
| 285 | #define DR_WAVE_FORMAT_MULAW 0x7 | ||
| 286 | #define DR_WAVE_FORMAT_DVI_ADPCM 0x11 | ||
| 287 | #define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE | ||
| 288 | |||
| 289 | /* Flags to pass into drwav_init_ex(), etc. */ | ||
| 290 | #define DRWAV_SEQUENTIAL 0x00000001 | ||
| 291 | #define DRWAV_WITH_METADATA 0x00000002 | ||
| 292 | |||
| 293 | DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision); | ||
| 294 | DRWAV_API const char* drwav_version_string(void); | ||
| 295 | |||
| 296 | /* Allocation Callbacks */ | ||
| 297 | typedef struct | ||
| 298 | { | ||
| 299 | void* pUserData; | ||
| 300 | void* (* onMalloc)(size_t sz, void* pUserData); | ||
| 301 | void* (* onRealloc)(void* p, size_t sz, void* pUserData); | ||
| 302 | void (* onFree)(void* p, void* pUserData); | ||
| 303 | } drwav_allocation_callbacks; | ||
| 304 | /* End Allocation Callbacks */ | ||
| 305 | |||
| 306 | typedef enum | ||
| 307 | { | ||
| 308 | drwav_seek_origin_start, | ||
| 309 | drwav_seek_origin_current | ||
| 310 | } drwav_seek_origin; | ||
| 311 | |||
| 312 | typedef enum | ||
| 313 | { | ||
| 314 | drwav_container_riff, | ||
| 315 | drwav_container_rifx, | ||
| 316 | drwav_container_w64, | ||
| 317 | drwav_container_rf64, | ||
| 318 | drwav_container_aiff | ||
| 319 | } drwav_container; | ||
| 320 | |||
| 321 | typedef struct | ||
| 322 | { | ||
| 323 | union | ||
| 324 | { | ||
| 325 | drwav_uint8 fourcc[4]; | ||
| 326 | drwav_uint8 guid[16]; | ||
| 327 | } id; | ||
| 328 | |||
| 329 | /* The size in bytes of the chunk. */ | ||
| 330 | drwav_uint64 sizeInBytes; | ||
| 331 | |||
| 332 | /* | ||
| 333 | RIFF = 2 byte alignment. | ||
| 334 | W64 = 8 byte alignment. | ||
| 335 | */ | ||
| 336 | unsigned int paddingSize; | ||
| 337 | } drwav_chunk_header; | ||
| 338 | |||
| 339 | typedef struct | ||
| 340 | { | ||
| 341 | /* | ||
| 342 | The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications | ||
| 343 | that require support for data formats not natively supported by dr_wav. | ||
| 344 | */ | ||
| 345 | drwav_uint16 formatTag; | ||
| 346 | |||
| 347 | /* The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc. */ | ||
| 348 | drwav_uint16 channels; | ||
| 349 | |||
| 350 | /* The sample rate. Usually set to something like 44100. */ | ||
| 351 | drwav_uint32 sampleRate; | ||
| 352 | |||
| 353 | /* Average bytes per second. You probably don't need this, but it's left here for informational purposes. */ | ||
| 354 | drwav_uint32 avgBytesPerSec; | ||
| 355 | |||
| 356 | /* Block align. This is equal to the number of channels * bytes per sample. */ | ||
| 357 | drwav_uint16 blockAlign; | ||
| 358 | |||
| 359 | /* Bits per sample. */ | ||
| 360 | drwav_uint16 bitsPerSample; | ||
| 361 | |||
| 362 | /* The size of the extended data. Only used internally for validation, but left here for informational purposes. */ | ||
| 363 | drwav_uint16 extendedSize; | ||
| 364 | |||
| 365 | /* | ||
| 366 | The number of valid bits per sample. When <formatTag> is equal to WAVE_FORMAT_EXTENSIBLE, <bitsPerSample> | ||
| 367 | is always rounded up to the nearest multiple of 8. This variable contains information about exactly how | ||
| 368 | many bits are valid per sample. Mainly used for informational purposes. | ||
| 369 | */ | ||
| 370 | drwav_uint16 validBitsPerSample; | ||
| 371 | |||
| 372 | /* The channel mask. Not used at the moment. */ | ||
| 373 | drwav_uint32 channelMask; | ||
| 374 | |||
| 375 | /* The sub-format, exactly as specified by the wave file. */ | ||
| 376 | drwav_uint8 subFormat[16]; | ||
| 377 | } drwav_fmt; | ||
| 378 | |||
| 379 | DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT); | ||
| 380 | |||
| 381 | |||
| 382 | /* | ||
| 383 | Callback for when data is read. Return value is the number of bytes actually read. | ||
| 384 | |||
| 385 | pUserData [in] The user data that was passed to drwav_init() and family. | ||
| 386 | pBufferOut [out] The output buffer. | ||
| 387 | bytesToRead [in] The number of bytes to read. | ||
| 388 | |||
| 389 | Returns the number of bytes actually read. | ||
| 390 | |||
| 391 | A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until | ||
| 392 | either the entire bytesToRead is filled or you have reached the end of the stream. | ||
| 393 | */ | ||
| 394 | typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); | ||
| 395 | |||
| 396 | /* | ||
| 397 | Callback for when data is written. Returns value is the number of bytes actually written. | ||
| 398 | |||
| 399 | pUserData [in] The user data that was passed to drwav_init_write() and family. | ||
| 400 | pData [out] A pointer to the data to write. | ||
| 401 | bytesToWrite [in] The number of bytes to write. | ||
| 402 | |||
| 403 | Returns the number of bytes actually written. | ||
| 404 | |||
| 405 | If the return value differs from bytesToWrite, it indicates an error. | ||
| 406 | */ | ||
| 407 | typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); | ||
| 408 | |||
| 409 | /* | ||
| 410 | Callback for when data needs to be seeked. | ||
| 411 | |||
| 412 | pUserData [in] The user data that was passed to drwav_init() and family. | ||
| 413 | offset [in] The number of bytes to move, relative to the origin. Will never be negative. | ||
| 414 | origin [in] The origin of the seek - the current position or the start of the stream. | ||
| 415 | |||
| 416 | Returns whether or not the seek was successful. | ||
| 417 | |||
| 418 | Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either drwav_seek_origin_start or | ||
| 419 | drwav_seek_origin_current. | ||
| 420 | */ | ||
| 421 | typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); | ||
| 422 | |||
| 423 | /* | ||
| 424 | Callback for when drwav_init_ex() finds a chunk. | ||
| 425 | |||
| 426 | pChunkUserData [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex() and family. | ||
| 427 | onRead [in] A pointer to the function to call when reading. | ||
| 428 | onSeek [in] A pointer to the function to call when seeking. | ||
| 429 | pReadSeekUserData [in] The user data that was passed to the pReadSeekUserData parameter of drwav_init_ex() and family. | ||
| 430 | pChunkHeader [in] A pointer to an object containing basic header information about the chunk. Use this to identify the chunk. | ||
| 431 | container [in] Whether or not the WAV file is a RIFF or Wave64 container. If you're unsure of the difference, assume RIFF. | ||
| 432 | pFMT [in] A pointer to the object containing the contents of the "fmt" chunk. | ||
| 433 | |||
| 434 | Returns the number of bytes read + seeked. | ||
| 435 | |||
| 436 | To read data from the chunk, call onRead(), passing in pReadSeekUserData as the first parameter. Do the same for seeking with onSeek(). The return value must | ||
| 437 | be the total number of bytes you have read _plus_ seeked. | ||
| 438 | |||
| 439 | Use the `container` argument to discriminate the fields in `pChunkHeader->id`. If the container is `drwav_container_riff` or `drwav_container_rf64` you should | ||
| 440 | use `id.fourcc`, otherwise you should use `id.guid`. | ||
| 441 | |||
| 442 | The `pFMT` parameter can be used to determine the data format of the wave file. Use `drwav_fmt_get_format()` to get the sample format, which will be one of the | ||
| 443 | `DR_WAVE_FORMAT_*` identifiers. | ||
| 444 | |||
| 445 | The read pointer will be sitting on the first byte after the chunk's header. You must not attempt to read beyond the boundary of the chunk. | ||
| 446 | */ | ||
| 447 | typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT); | ||
| 448 | |||
| 449 | |||
| 450 | /* Structure for internal use. Only used for loaders opened with drwav_init_memory(). */ | ||
| 451 | typedef struct | ||
| 452 | { | ||
| 453 | const drwav_uint8* data; | ||
| 454 | size_t dataSize; | ||
| 455 | size_t currentReadPos; | ||
| 456 | } drwav__memory_stream; | ||
| 457 | |||
| 458 | /* Structure for internal use. Only used for writers opened with drwav_init_memory_write(). */ | ||
| 459 | typedef struct | ||
| 460 | { | ||
| 461 | void** ppData; | ||
| 462 | size_t* pDataSize; | ||
| 463 | size_t dataSize; | ||
| 464 | size_t dataCapacity; | ||
| 465 | size_t currentWritePos; | ||
| 466 | } drwav__memory_stream_write; | ||
| 467 | |||
| 468 | typedef struct | ||
| 469 | { | ||
| 470 | drwav_container container; /* RIFF, W64. */ | ||
| 471 | drwav_uint32 format; /* DR_WAVE_FORMAT_* */ | ||
| 472 | drwav_uint32 channels; | ||
| 473 | drwav_uint32 sampleRate; | ||
| 474 | drwav_uint32 bitsPerSample; | ||
| 475 | } drwav_data_format; | ||
| 476 | |||
| 477 | typedef enum | ||
| 478 | { | ||
| 479 | drwav_metadata_type_none = 0, | ||
| 480 | |||
| 481 | /* | ||
| 482 | Unknown simply means a chunk that drwav does not handle specifically. You can still ask to | ||
| 483 | receive these chunks as metadata objects. It is then up to you to interpret the chunk's data. | ||
| 484 | You can also write unknown metadata to a wav file. Be careful writing unknown chunks if you | ||
| 485 | have also edited the audio data. The unknown chunks could represent offsets/sizes that no | ||
| 486 | longer correctly correspond to the audio data. | ||
| 487 | */ | ||
| 488 | drwav_metadata_type_unknown = 1 << 0, | ||
| 489 | |||
| 490 | /* Only 1 of each of these metadata items are allowed in a wav file. */ | ||
| 491 | drwav_metadata_type_smpl = 1 << 1, | ||
| 492 | drwav_metadata_type_inst = 1 << 2, | ||
| 493 | drwav_metadata_type_cue = 1 << 3, | ||
| 494 | drwav_metadata_type_acid = 1 << 4, | ||
| 495 | drwav_metadata_type_bext = 1 << 5, | ||
| 496 | |||
| 497 | /* | ||
| 498 | Wav files often have a LIST chunk. This is a chunk that contains a set of subchunks. For this | ||
| 499 | higher-level metadata API, we don't make a distinction between a regular chunk and a LIST | ||
| 500 | subchunk. Instead, they are all just 'metadata' items. | ||
| 501 | |||
| 502 | There can be multiple of these metadata items in a wav file. | ||
| 503 | */ | ||
| 504 | drwav_metadata_type_list_label = 1 << 6, | ||
| 505 | drwav_metadata_type_list_note = 1 << 7, | ||
| 506 | drwav_metadata_type_list_labelled_cue_region = 1 << 8, | ||
| 507 | |||
| 508 | drwav_metadata_type_list_info_software = 1 << 9, | ||
| 509 | drwav_metadata_type_list_info_copyright = 1 << 10, | ||
| 510 | drwav_metadata_type_list_info_title = 1 << 11, | ||
| 511 | drwav_metadata_type_list_info_artist = 1 << 12, | ||
| 512 | drwav_metadata_type_list_info_comment = 1 << 13, | ||
| 513 | drwav_metadata_type_list_info_date = 1 << 14, | ||
| 514 | drwav_metadata_type_list_info_genre = 1 << 15, | ||
| 515 | drwav_metadata_type_list_info_album = 1 << 16, | ||
| 516 | drwav_metadata_type_list_info_tracknumber = 1 << 17, | ||
| 517 | |||
| 518 | /* Other type constants for convenience. */ | ||
| 519 | drwav_metadata_type_list_all_info_strings = drwav_metadata_type_list_info_software | ||
| 520 | | drwav_metadata_type_list_info_copyright | ||
| 521 | | drwav_metadata_type_list_info_title | ||
| 522 | | drwav_metadata_type_list_info_artist | ||
| 523 | | drwav_metadata_type_list_info_comment | ||
| 524 | | drwav_metadata_type_list_info_date | ||
| 525 | | drwav_metadata_type_list_info_genre | ||
| 526 | | drwav_metadata_type_list_info_album | ||
| 527 | | drwav_metadata_type_list_info_tracknumber, | ||
| 528 | |||
| 529 | drwav_metadata_type_list_all_adtl = drwav_metadata_type_list_label | ||
| 530 | | drwav_metadata_type_list_note | ||
| 531 | | drwav_metadata_type_list_labelled_cue_region, | ||
| 532 | |||
| 533 | drwav_metadata_type_all = -2, /*0xFFFFFFFF & ~drwav_metadata_type_unknown,*/ | ||
| 534 | drwav_metadata_type_all_including_unknown = -1 /*0xFFFFFFFF,*/ | ||
| 535 | } drwav_metadata_type; | ||
| 536 | |||
| 537 | /* | ||
| 538 | Sampler Metadata | ||
| 539 | |||
| 540 | The sampler chunk contains information about how a sound should be played in the context of a whole | ||
| 541 | audio production, and when used in a sampler. See https://en.wikipedia.org/wiki/Sample-based_synthesis. | ||
| 542 | */ | ||
| 543 | typedef enum | ||
| 544 | { | ||
| 545 | drwav_smpl_loop_type_forward = 0, | ||
| 546 | drwav_smpl_loop_type_pingpong = 1, | ||
| 547 | drwav_smpl_loop_type_backward = 2 | ||
| 548 | } drwav_smpl_loop_type; | ||
| 549 | |||
| 550 | typedef struct | ||
| 551 | { | ||
| 552 | /* The ID of the associated cue point, see drwav_cue and drwav_cue_point. As with all cue point IDs, this can correspond to a label chunk to give this loop a name, see drwav_list_label_or_note. */ | ||
| 553 | drwav_uint32 cuePointId; | ||
| 554 | |||
| 555 | /* See drwav_smpl_loop_type. */ | ||
| 556 | drwav_uint32 type; | ||
| 557 | |||
| 558 | /* The byte offset of the first sample to be played in the loop. */ | ||
| 559 | drwav_uint32 firstSampleByteOffset; | ||
| 560 | |||
| 561 | /* The byte offset into the audio data of the last sample to be played in the loop. */ | ||
| 562 | drwav_uint32 lastSampleByteOffset; | ||
| 563 | |||
| 564 | /* A value to represent that playback should occur at a point between samples. This value ranges from 0 to UINT32_MAX. Where a value of 0 means no fraction, and a value of (UINT32_MAX / 2) would mean half a sample. */ | ||
| 565 | drwav_uint32 sampleFraction; | ||
| 566 | |||
| 567 | /* Number of times to play the loop. 0 means loop infinitely. */ | ||
| 568 | drwav_uint32 playCount; | ||
| 569 | } drwav_smpl_loop; | ||
| 570 | |||
| 571 | typedef struct | ||
| 572 | { | ||
| 573 | /* IDs for a particular MIDI manufacturer. 0 if not used. */ | ||
| 574 | drwav_uint32 manufacturerId; | ||
| 575 | drwav_uint32 productId; | ||
| 576 | |||
| 577 | /* The period of 1 sample in nanoseconds. */ | ||
| 578 | drwav_uint32 samplePeriodNanoseconds; | ||
| 579 | |||
| 580 | /* The MIDI root note of this file. 0 to 127. */ | ||
| 581 | drwav_uint32 midiUnityNote; | ||
| 582 | |||
| 583 | /* The fraction of a semitone up from the given MIDI note. This is a value from 0 to UINT32_MAX, where 0 means no change and (UINT32_MAX / 2) is half a semitone (AKA 50 cents). */ | ||
| 584 | drwav_uint32 midiPitchFraction; | ||
| 585 | |||
| 586 | /* Data relating to SMPTE standards which are used for syncing audio and video. 0 if not used. */ | ||
| 587 | drwav_uint32 smpteFormat; | ||
| 588 | drwav_uint32 smpteOffset; | ||
| 589 | |||
| 590 | /* drwav_smpl_loop loops. */ | ||
| 591 | drwav_uint32 sampleLoopCount; | ||
| 592 | |||
| 593 | /* Optional sampler-specific data. */ | ||
| 594 | drwav_uint32 samplerSpecificDataSizeInBytes; | ||
| 595 | |||
| 596 | drwav_smpl_loop* pLoops; | ||
| 597 | drwav_uint8* pSamplerSpecificData; | ||
| 598 | } drwav_smpl; | ||
| 599 | |||
| 600 | /* | ||
| 601 | Instrument Metadata | ||
| 602 | |||
| 603 | The inst metadata contains data about how a sound should be played as part of an instrument. This | ||
| 604 | commonly read by samplers. See https://en.wikipedia.org/wiki/Sample-based_synthesis. | ||
| 605 | */ | ||
| 606 | typedef struct | ||
| 607 | { | ||
| 608 | drwav_int8 midiUnityNote; /* The root note of the audio as a MIDI note number. 0 to 127. */ | ||
| 609 | drwav_int8 fineTuneCents; /* -50 to +50 */ | ||
| 610 | drwav_int8 gainDecibels; /* -64 to +64 */ | ||
| 611 | drwav_int8 lowNote; /* 0 to 127 */ | ||
| 612 | drwav_int8 highNote; /* 0 to 127 */ | ||
| 613 | drwav_int8 lowVelocity; /* 1 to 127 */ | ||
| 614 | drwav_int8 highVelocity; /* 1 to 127 */ | ||
| 615 | } drwav_inst; | ||
| 616 | |||
| 617 | /* | ||
| 618 | Cue Metadata | ||
| 619 | |||
| 620 | Cue points are markers at specific points in the audio. They often come with an associated piece of | ||
| 621 | drwav_list_label_or_note metadata which contains the text for the marker. | ||
| 622 | */ | ||
| 623 | typedef struct | ||
| 624 | { | ||
| 625 | /* Unique identification value. */ | ||
| 626 | drwav_uint32 id; | ||
| 627 | |||
| 628 | /* Set to 0. This is only relevant if there is a 'playlist' chunk - which is not supported by dr_wav. */ | ||
| 629 | drwav_uint32 playOrderPosition; | ||
| 630 | |||
| 631 | /* Should always be "data". This represents the fourcc value of the chunk that this cue point corresponds to. dr_wav only supports a single data chunk so this should always be "data". */ | ||
| 632 | drwav_uint8 dataChunkId[4]; | ||
| 633 | |||
| 634 | /* Set to 0. This is only relevant if there is a wave list chunk. dr_wav, like lots of readers/writers, do not support this. */ | ||
| 635 | drwav_uint32 chunkStart; | ||
| 636 | |||
| 637 | /* Set to 0 for uncompressed formats. Else the last byte in compressed wave data where decompression can begin to find the value of the corresponding sample value. */ | ||
| 638 | drwav_uint32 blockStart; | ||
| 639 | |||
| 640 | /* For uncompressed formats this is the byte offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */ | ||
| 641 | drwav_uint32 sampleByteOffset; | ||
| 642 | } drwav_cue_point; | ||
| 643 | |||
| 644 | typedef struct | ||
| 645 | { | ||
| 646 | drwav_uint32 cuePointCount; | ||
| 647 | drwav_cue_point *pCuePoints; | ||
| 648 | } drwav_cue; | ||
| 649 | |||
| 650 | /* | ||
| 651 | Acid Metadata | ||
| 652 | |||
| 653 | This chunk contains some information about the time signature and the tempo of the audio. | ||
| 654 | */ | ||
| 655 | typedef enum | ||
| 656 | { | ||
| 657 | drwav_acid_flag_one_shot = 1, /* If this is not set, then it is a loop instead of a one-shot. */ | ||
| 658 | drwav_acid_flag_root_note_set = 2, | ||
| 659 | drwav_acid_flag_stretch = 4, | ||
| 660 | drwav_acid_flag_disk_based = 8, | ||
| 661 | drwav_acid_flag_acidizer = 16 /* Not sure what this means. */ | ||
| 662 | } drwav_acid_flag; | ||
| 663 | |||
| 664 | typedef struct | ||
| 665 | { | ||
| 666 | /* A bit-field, see drwav_acid_flag. */ | ||
| 667 | drwav_uint32 flags; | ||
| 668 | |||
| 669 | /* Valid if flags contains drwav_acid_flag_root_note_set. It represents the MIDI root note the file - a value from 0 to 127. */ | ||
| 670 | drwav_uint16 midiUnityNote; | ||
| 671 | |||
| 672 | /* Reserved values that should probably be ignored. reserved1 seems to often be 128 and reserved2 is 0. */ | ||
| 673 | drwav_uint16 reserved1; | ||
| 674 | float reserved2; | ||
| 675 | |||
| 676 | /* Number of beats. */ | ||
| 677 | drwav_uint32 numBeats; | ||
| 678 | |||
| 679 | /* The time signature of the audio. */ | ||
| 680 | drwav_uint16 meterDenominator; | ||
| 681 | drwav_uint16 meterNumerator; | ||
| 682 | |||
| 683 | /* Beats per minute of the track. Setting a value of 0 suggests that there is no tempo. */ | ||
| 684 | float tempo; | ||
| 685 | } drwav_acid; | ||
| 686 | |||
| 687 | /* | ||
| 688 | Cue Label or Note metadata | ||
| 689 | |||
| 690 | These are 2 different types of metadata, but they have the exact same format. Labels tend to be the | ||
| 691 | more common and represent a short name for a cue point. Notes might be used to represent a longer | ||
| 692 | comment. | ||
| 693 | */ | ||
| 694 | typedef struct | ||
| 695 | { | ||
| 696 | /* The ID of a cue point that this label or note corresponds to. */ | ||
| 697 | drwav_uint32 cuePointId; | ||
| 698 | |||
| 699 | /* Size of the string not including any null terminator. */ | ||
| 700 | drwav_uint32 stringLength; | ||
| 701 | |||
| 702 | /* The string. The *init_with_metadata functions null terminate this for convenience. */ | ||
| 703 | char* pString; | ||
| 704 | } drwav_list_label_or_note; | ||
| 705 | |||
| 706 | /* | ||
| 707 | BEXT metadata, also known as Broadcast Wave Format (BWF) | ||
| 708 | |||
| 709 | This metadata adds some extra description to an audio file. You must check the version field to | ||
| 710 | determine if the UMID or the loudness fields are valid. | ||
| 711 | */ | ||
| 712 | typedef struct | ||
| 713 | { | ||
| 714 | /* | ||
| 715 | These top 3 fields, and the umid field are actually defined in the standard as a statically | ||
| 716 | sized buffers. In order to reduce the size of this struct (and therefore the union in the | ||
| 717 | metadata struct), we instead store these as pointers. | ||
| 718 | */ | ||
| 719 | char* pDescription; /* Can be NULL or a null-terminated string, must be <= 256 characters. */ | ||
| 720 | char* pOriginatorName; /* Can be NULL or a null-terminated string, must be <= 32 characters. */ | ||
| 721 | char* pOriginatorReference; /* Can be NULL or a null-terminated string, must be <= 32 characters. */ | ||
| 722 | char pOriginationDate[10]; /* ASCII "yyyy:mm:dd". */ | ||
| 723 | char pOriginationTime[8]; /* ASCII "hh:mm:ss". */ | ||
| 724 | drwav_uint64 timeReference; /* First sample count since midnight. */ | ||
| 725 | drwav_uint16 version; /* Version of the BWF, check this to see if the fields below are valid. */ | ||
| 726 | |||
| 727 | /* | ||
| 728 | Unrestricted ASCII characters containing a collection of strings terminated by CR/LF. Each | ||
| 729 | string shall contain a description of a coding process applied to the audio data. | ||
| 730 | */ | ||
| 731 | char* pCodingHistory; | ||
| 732 | drwav_uint32 codingHistorySize; | ||
| 733 | |||
| 734 | /* Fields below this point are only valid if the version is 1 or above. */ | ||
| 735 | drwav_uint8* pUMID; /* Exactly 64 bytes of SMPTE UMID */ | ||
| 736 | |||
| 737 | /* Fields below this point are only valid if the version is 2 or above. */ | ||
| 738 | drwav_uint16 loudnessValue; /* Integrated Loudness Value of the file in LUFS (multiplied by 100). */ | ||
| 739 | drwav_uint16 loudnessRange; /* Loudness Range of the file in LU (multiplied by 100). */ | ||
| 740 | drwav_uint16 maxTruePeakLevel; /* Maximum True Peak Level of the file expressed as dBTP (multiplied by 100). */ | ||
| 741 | drwav_uint16 maxMomentaryLoudness; /* Highest value of the Momentary Loudness Level of the file in LUFS (multiplied by 100). */ | ||
| 742 | drwav_uint16 maxShortTermLoudness; /* Highest value of the Short-Term Loudness Level of the file in LUFS (multiplied by 100). */ | ||
| 743 | } drwav_bext; | ||
| 744 | |||
| 745 | /* | ||
| 746 | Info Text Metadata | ||
| 747 | |||
| 748 | There a many different types of information text that can be saved in this format. This is where | ||
| 749 | things like the album name, the artists, the year it was produced, etc are saved. See | ||
| 750 | drwav_metadata_type for the full list of types that dr_wav supports. | ||
| 751 | */ | ||
| 752 | typedef struct | ||
| 753 | { | ||
| 754 | /* Size of the string not including any null terminator. */ | ||
| 755 | drwav_uint32 stringLength; | ||
| 756 | |||
| 757 | /* The string. The *init_with_metadata functions null terminate this for convenience. */ | ||
| 758 | char* pString; | ||
| 759 | } drwav_list_info_text; | ||
| 760 | |||
| 761 | /* | ||
| 762 | Labelled Cue Region Metadata | ||
| 763 | |||
| 764 | The labelled cue region metadata is used to associate some region of audio with text. The region | ||
| 765 | starts at a cue point, and extends for the given number of samples. | ||
| 766 | */ | ||
| 767 | typedef struct | ||
| 768 | { | ||
| 769 | /* The ID of a cue point that this object corresponds to. */ | ||
| 770 | drwav_uint32 cuePointId; | ||
| 771 | |||
| 772 | /* The number of samples from the cue point forwards that should be considered this region */ | ||
| 773 | drwav_uint32 sampleLength; | ||
| 774 | |||
| 775 | /* Four characters used to say what the purpose of this region is. */ | ||
| 776 | drwav_uint8 purposeId[4]; | ||
| 777 | |||
| 778 | /* Unsure of the exact meanings of these. It appears to be acceptable to set them all to 0. */ | ||
| 779 | drwav_uint16 country; | ||
| 780 | drwav_uint16 language; | ||
| 781 | drwav_uint16 dialect; | ||
| 782 | drwav_uint16 codePage; | ||
| 783 | |||
| 784 | /* Size of the string not including any null terminator. */ | ||
| 785 | drwav_uint32 stringLength; | ||
| 786 | |||
| 787 | /* The string. The *init_with_metadata functions null terminate this for convenience. */ | ||
| 788 | char* pString; | ||
| 789 | } drwav_list_labelled_cue_region; | ||
| 790 | |||
| 791 | /* | ||
| 792 | Unknown Metadata | ||
| 793 | |||
| 794 | This chunk just represents a type of chunk that dr_wav does not understand. | ||
| 795 | |||
| 796 | Unknown metadata has a location attached to it. This is because wav files can have a LIST chunk | ||
| 797 | that contains subchunks. These LIST chunks can be one of two types. An adtl list, or an INFO | ||
| 798 | list. This enum is used to specify the location of a chunk that dr_wav currently doesn't support. | ||
| 799 | */ | ||
| 800 | typedef enum | ||
| 801 | { | ||
| 802 | drwav_metadata_location_invalid, | ||
| 803 | drwav_metadata_location_top_level, | ||
| 804 | drwav_metadata_location_inside_info_list, | ||
| 805 | drwav_metadata_location_inside_adtl_list | ||
| 806 | } drwav_metadata_location; | ||
| 807 | |||
| 808 | typedef struct | ||
| 809 | { | ||
| 810 | drwav_uint8 id[4]; | ||
| 811 | drwav_metadata_location chunkLocation; | ||
| 812 | drwav_uint32 dataSizeInBytes; | ||
| 813 | drwav_uint8* pData; | ||
| 814 | } drwav_unknown_metadata; | ||
| 815 | |||
| 816 | /* | ||
| 817 | Metadata is saved as a union of all the supported types. | ||
| 818 | */ | ||
| 819 | typedef struct | ||
| 820 | { | ||
| 821 | /* Determines which item in the union is valid. */ | ||
| 822 | drwav_metadata_type type; | ||
| 823 | |||
| 824 | union | ||
| 825 | { | ||
| 826 | drwav_cue cue; | ||
| 827 | drwav_smpl smpl; | ||
| 828 | drwav_acid acid; | ||
| 829 | drwav_inst inst; | ||
| 830 | drwav_bext bext; | ||
| 831 | drwav_list_label_or_note labelOrNote; /* List label or list note. */ | ||
| 832 | drwav_list_labelled_cue_region labelledCueRegion; | ||
| 833 | drwav_list_info_text infoText; /* Any of the list info types. */ | ||
| 834 | drwav_unknown_metadata unknown; | ||
| 835 | } data; | ||
| 836 | } drwav_metadata; | ||
| 837 | |||
| 838 | typedef struct | ||
| 839 | { | ||
| 840 | /* A pointer to the function to call when more data is needed. */ | ||
| 841 | drwav_read_proc onRead; | ||
| 842 | |||
| 843 | /* A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode. */ | ||
| 844 | drwav_write_proc onWrite; | ||
| 845 | |||
| 846 | /* A pointer to the function to call when the wav file needs to be seeked. */ | ||
| 847 | drwav_seek_proc onSeek; | ||
| 848 | |||
| 849 | /* The user data to pass to callbacks. */ | ||
| 850 | void* pUserData; | ||
| 851 | |||
| 852 | /* Allocation callbacks. */ | ||
| 853 | drwav_allocation_callbacks allocationCallbacks; | ||
| 854 | |||
| 855 | |||
| 856 | /* Whether or not the WAV file is formatted as a standard RIFF file or W64. */ | ||
| 857 | drwav_container container; | ||
| 858 | |||
| 859 | |||
| 860 | /* Structure containing format information exactly as specified by the wav file. */ | ||
| 861 | drwav_fmt fmt; | ||
| 862 | |||
| 863 | /* The sample rate. Will be set to something like 44100. */ | ||
| 864 | drwav_uint32 sampleRate; | ||
| 865 | |||
| 866 | /* The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. */ | ||
| 867 | drwav_uint16 channels; | ||
| 868 | |||
| 869 | /* The bits per sample. Will be set to something like 16, 24, etc. */ | ||
| 870 | drwav_uint16 bitsPerSample; | ||
| 871 | |||
| 872 | /* Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE). */ | ||
| 873 | drwav_uint16 translatedFormatTag; | ||
| 874 | |||
| 875 | /* The total number of PCM frames making up the audio data. */ | ||
| 876 | drwav_uint64 totalPCMFrameCount; | ||
| 877 | |||
| 878 | |||
| 879 | /* The size in bytes of the data chunk. */ | ||
| 880 | drwav_uint64 dataChunkDataSize; | ||
| 881 | |||
| 882 | /* The position in the stream of the first data byte of the data chunk. This is used for seeking. */ | ||
| 883 | drwav_uint64 dataChunkDataPos; | ||
| 884 | |||
| 885 | /* The number of bytes remaining in the data chunk. */ | ||
| 886 | drwav_uint64 bytesRemaining; | ||
| 887 | |||
| 888 | /* The current read position in PCM frames. */ | ||
| 889 | drwav_uint64 readCursorInPCMFrames; | ||
| 890 | |||
| 891 | |||
| 892 | /* | ||
| 893 | Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always | ||
| 894 | set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation. | ||
| 895 | */ | ||
| 896 | drwav_uint64 dataChunkDataSizeTargetWrite; | ||
| 897 | |||
| 898 | /* Keeps track of whether or not the wav writer was initialized in sequential mode. */ | ||
| 899 | drwav_bool32 isSequentialWrite; | ||
| 900 | |||
| 901 | |||
| 902 | /* A array of metadata. This is valid after the *init_with_metadata call returns. It will be valid until drwav_uninit() is called. You can take ownership of this data with drwav_take_ownership_of_metadata(). */ | ||
| 903 | drwav_metadata* pMetadata; | ||
| 904 | drwav_uint32 metadataCount; | ||
| 905 | |||
| 906 | |||
| 907 | /* A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_init_memory(). */ | ||
| 908 | drwav__memory_stream memoryStream; | ||
| 909 | drwav__memory_stream_write memoryStreamWrite; | ||
| 910 | |||
| 911 | |||
| 912 | /* Microsoft ADPCM specific data. */ | ||
| 913 | struct | ||
| 914 | { | ||
| 915 | drwav_uint32 bytesRemainingInBlock; | ||
| 916 | drwav_uint16 predictor[2]; | ||
| 917 | drwav_int32 delta[2]; | ||
| 918 | drwav_int32 cachedFrames[4]; /* Samples are stored in this cache during decoding. */ | ||
| 919 | drwav_uint32 cachedFrameCount; | ||
| 920 | drwav_int32 prevFrames[2][2]; /* The previous 2 samples for each channel (2 channels at most). */ | ||
| 921 | } msadpcm; | ||
| 922 | |||
| 923 | /* IMA ADPCM specific data. */ | ||
| 924 | struct | ||
| 925 | { | ||
| 926 | drwav_uint32 bytesRemainingInBlock; | ||
| 927 | drwav_int32 predictor[2]; | ||
| 928 | drwav_int32 stepIndex[2]; | ||
| 929 | drwav_int32 cachedFrames[16]; /* Samples are stored in this cache during decoding. */ | ||
| 930 | drwav_uint32 cachedFrameCount; | ||
| 931 | } ima; | ||
| 932 | |||
| 933 | /* AIFF specific data. */ | ||
| 934 | struct | ||
| 935 | { | ||
| 936 | drwav_bool8 isLE; /* Will be set to true if the audio data is little-endian encoded. */ | ||
| 937 | drwav_bool8 isUnsigned; /* Only used for 8-bit samples. When set to true, will be treated as unsigned. */ | ||
| 938 | } aiff; | ||
| 939 | } drwav; | ||
| 940 | |||
| 941 | |||
| 942 | /* | ||
| 943 | Initializes a pre-allocated drwav object for reading. | ||
| 944 | |||
| 945 | pWav [out] A pointer to the drwav object being initialized. | ||
| 946 | onRead [in] The function to call when data needs to be read from the client. | ||
| 947 | onSeek [in] The function to call when the read position of the client data needs to move. | ||
| 948 | onChunk [in, optional] The function to call when a chunk is enumerated at initialized time. | ||
| 949 | pUserData, pReadSeekUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. | ||
| 950 | pChunkUserData [in, optional] A pointer to application defined data that will be passed to onChunk. | ||
| 951 | flags [in, optional] A set of flags for controlling how things are loaded. | ||
| 952 | |||
| 953 | Returns true if successful; false otherwise. | ||
| 954 | |||
| 955 | Close the loader with drwav_uninit(). | ||
| 956 | |||
| 957 | This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory() | ||
| 958 | to open the stream from a file or from a block of memory respectively. | ||
| 959 | |||
| 960 | Possible values for flags: | ||
| 961 | DRWAV_SEQUENTIAL: Never perform a backwards seek while loading. This disables the chunk callback and will cause this function | ||
| 962 | to return as soon as the data chunk is found. Any chunks after the data chunk will be ignored. | ||
| 963 | |||
| 964 | drwav_init() is equivalent to "drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);". | ||
| 965 | |||
| 966 | The onChunk callback is not called for the WAVE or FMT chunks. The contents of the FMT chunk can be read from pWav->fmt | ||
| 967 | after the function returns. | ||
| 968 | |||
| 969 | See also: drwav_init_file(), drwav_init_memory(), drwav_uninit() | ||
| 970 | */ | ||
| 971 | DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 972 | DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 973 | DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 974 | |||
| 975 | /* | ||
| 976 | Initializes a pre-allocated drwav object for writing. | ||
| 977 | |||
| 978 | onWrite [in] The function to call when data needs to be written. | ||
| 979 | onSeek [in] The function to call when the write position needs to move. | ||
| 980 | pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek. | ||
| 981 | metadata, numMetadata [in, optional] An array of metadata objects that should be written to the file. The array is not edited. You are responsible for this metadata memory and it must maintain valid until drwav_uninit() is called. | ||
| 982 | |||
| 983 | Returns true if successful; false otherwise. | ||
| 984 | |||
| 985 | Close the writer with drwav_uninit(). | ||
| 986 | |||
| 987 | This is the lowest level function for initializing a WAV file. You can also use drwav_init_file_write() and drwav_init_memory_write() | ||
| 988 | to open the stream from a file or from a block of memory respectively. | ||
| 989 | |||
| 990 | If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform | ||
| 991 | a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek. | ||
| 992 | |||
| 993 | See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit() | ||
| 994 | */ | ||
| 995 | DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 996 | DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 997 | DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 998 | DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount); | ||
| 999 | |||
| 1000 | /* | ||
| 1001 | Utility function to determine the target size of the entire data to be written (including all headers and chunks). | ||
| 1002 | |||
| 1003 | Returns the target size in bytes. | ||
| 1004 | |||
| 1005 | The metadata argument can be NULL meaning no metadata exists. | ||
| 1006 | |||
| 1007 | Useful if the application needs to know the size to allocate. | ||
| 1008 | |||
| 1009 | Only writing to the RIFF chunk and one data chunk is currently supported. | ||
| 1010 | |||
| 1011 | See also: drwav_init_write(), drwav_init_file_write(), drwav_init_memory_write() | ||
| 1012 | */ | ||
| 1013 | DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount); | ||
| 1014 | |||
| 1015 | /* | ||
| 1016 | Take ownership of the metadata objects that were allocated via one of the init_with_metadata() function calls. The init_with_metdata functions perform a single heap allocation for this metadata. | ||
| 1017 | |||
| 1018 | Useful if you want the data to persist beyond the lifetime of the drwav object. | ||
| 1019 | |||
| 1020 | You must free the data returned from this function using drwav_free(). | ||
| 1021 | */ | ||
| 1022 | DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav); | ||
| 1023 | |||
| 1024 | /* | ||
| 1025 | Uninitializes the given drwav object. | ||
| 1026 | |||
| 1027 | Use this only for objects initialized with drwav_init*() functions (drwav_init(), drwav_init_ex(), drwav_init_write(), drwav_init_write_sequential()). | ||
| 1028 | */ | ||
| 1029 | DRWAV_API drwav_result drwav_uninit(drwav* pWav); | ||
| 1030 | |||
| 1031 | |||
| 1032 | /* | ||
| 1033 | Reads raw audio data. | ||
| 1034 | |||
| 1035 | This is the lowest level function for reading audio data. It simply reads the given number of | ||
| 1036 | bytes of the raw internal sample data. | ||
| 1037 | |||
| 1038 | Consider using drwav_read_pcm_frames_s16(), drwav_read_pcm_frames_s32() or drwav_read_pcm_frames_f32() for | ||
| 1039 | reading sample data in a consistent format. | ||
| 1040 | |||
| 1041 | pBufferOut can be NULL in which case a seek will be performed. | ||
| 1042 | |||
| 1043 | Returns the number of bytes actually read. | ||
| 1044 | */ | ||
| 1045 | DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut); | ||
| 1046 | |||
| 1047 | /* | ||
| 1048 | Reads up to the specified number of PCM frames from the WAV file. | ||
| 1049 | |||
| 1050 | The output data will be in the file's internal format, converted to native-endian byte order. Use | ||
| 1051 | drwav_read_pcm_frames_s16/f32/s32() to read data in a specific format. | ||
| 1052 | |||
| 1053 | If the return value is less than <framesToRead> it means the end of the file has been reached or | ||
| 1054 | you have requested more PCM frames than can possibly fit in the output buffer. | ||
| 1055 | |||
| 1056 | This function will only work when sample data is of a fixed size and uncompressed. If you are | ||
| 1057 | using a compressed format consider using drwav_read_raw() or drwav_read_pcm_frames_s16/s32/f32(). | ||
| 1058 | |||
| 1059 | pBufferOut can be NULL in which case a seek will be performed. | ||
| 1060 | */ | ||
| 1061 | DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); | ||
| 1062 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); | ||
| 1063 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); | ||
| 1064 | |||
| 1065 | /* | ||
| 1066 | Seeks to the given PCM frame. | ||
| 1067 | |||
| 1068 | Returns true if successful; false otherwise. | ||
| 1069 | */ | ||
| 1070 | DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex); | ||
| 1071 | |||
| 1072 | /* | ||
| 1073 | Retrieves the current read position in pcm frames. | ||
| 1074 | */ | ||
| 1075 | DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor); | ||
| 1076 | |||
| 1077 | /* | ||
| 1078 | Retrieves the length of the file. | ||
| 1079 | */ | ||
| 1080 | DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength); | ||
| 1081 | |||
| 1082 | |||
| 1083 | /* | ||
| 1084 | Writes raw audio data. | ||
| 1085 | |||
| 1086 | Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error. | ||
| 1087 | */ | ||
| 1088 | DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData); | ||
| 1089 | |||
| 1090 | /* | ||
| 1091 | Writes PCM frames. | ||
| 1092 | |||
| 1093 | Returns the number of PCM frames written. | ||
| 1094 | |||
| 1095 | Input samples need to be in native-endian byte order. On big-endian architectures the input data will be converted to | ||
| 1096 | little-endian. Use drwav_write_raw() to write raw audio data without performing any conversion. | ||
| 1097 | */ | ||
| 1098 | DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); | ||
| 1099 | DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); | ||
| 1100 | DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); | ||
| 1101 | |||
| 1102 | /* Conversion Utilities */ | ||
| 1103 | #ifndef DR_WAV_NO_CONVERSION_API | ||
| 1104 | |||
| 1105 | /* | ||
| 1106 | Reads a chunk of audio data and converts it to signed 16-bit PCM samples. | ||
| 1107 | |||
| 1108 | pBufferOut can be NULL in which case a seek will be performed. | ||
| 1109 | |||
| 1110 | Returns the number of PCM frames actually read. | ||
| 1111 | |||
| 1112 | If the return value is less than <framesToRead> it means the end of the file has been reached. | ||
| 1113 | */ | ||
| 1114 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); | ||
| 1115 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); | ||
| 1116 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); | ||
| 1117 | |||
| 1118 | /* Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples. */ | ||
| 1119 | DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); | ||
| 1120 | |||
| 1121 | /* Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples. */ | ||
| 1122 | DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); | ||
| 1123 | |||
| 1124 | /* Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples. */ | ||
| 1125 | DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount); | ||
| 1126 | |||
| 1127 | /* Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples. */ | ||
| 1128 | DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount); | ||
| 1129 | |||
| 1130 | /* Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples. */ | ||
| 1131 | DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount); | ||
| 1132 | |||
| 1133 | /* Low-level function for converting A-law samples to signed 16-bit PCM samples. */ | ||
| 1134 | DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); | ||
| 1135 | |||
| 1136 | /* Low-level function for converting u-law samples to signed 16-bit PCM samples. */ | ||
| 1137 | DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); | ||
| 1138 | |||
| 1139 | |||
| 1140 | /* | ||
| 1141 | Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples. | ||
| 1142 | |||
| 1143 | pBufferOut can be NULL in which case a seek will be performed. | ||
| 1144 | |||
| 1145 | Returns the number of PCM frames actually read. | ||
| 1146 | |||
| 1147 | If the return value is less than <framesToRead> it means the end of the file has been reached. | ||
| 1148 | */ | ||
| 1149 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); | ||
| 1150 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); | ||
| 1151 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); | ||
| 1152 | |||
| 1153 | /* Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples. */ | ||
| 1154 | DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); | ||
| 1155 | |||
| 1156 | /* Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples. */ | ||
| 1157 | DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount); | ||
| 1158 | |||
| 1159 | /* Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples. */ | ||
| 1160 | DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); | ||
| 1161 | |||
| 1162 | /* Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples. */ | ||
| 1163 | DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount); | ||
| 1164 | |||
| 1165 | /* Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples. */ | ||
| 1166 | DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); | ||
| 1167 | |||
| 1168 | /* Low-level function for converting A-law samples to IEEE 32-bit floating point samples. */ | ||
| 1169 | DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); | ||
| 1170 | |||
| 1171 | /* Low-level function for converting u-law samples to IEEE 32-bit floating point samples. */ | ||
| 1172 | DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); | ||
| 1173 | |||
| 1174 | |||
| 1175 | /* | ||
| 1176 | Reads a chunk of audio data and converts it to signed 32-bit PCM samples. | ||
| 1177 | |||
| 1178 | pBufferOut can be NULL in which case a seek will be performed. | ||
| 1179 | |||
| 1180 | Returns the number of PCM frames actually read. | ||
| 1181 | |||
| 1182 | If the return value is less than <framesToRead> it means the end of the file has been reached. | ||
| 1183 | */ | ||
| 1184 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); | ||
| 1185 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); | ||
| 1186 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); | ||
| 1187 | |||
| 1188 | /* Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples. */ | ||
| 1189 | DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); | ||
| 1190 | |||
| 1191 | /* Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples. */ | ||
| 1192 | DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount); | ||
| 1193 | |||
| 1194 | /* Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples. */ | ||
| 1195 | DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); | ||
| 1196 | |||
| 1197 | /* Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples. */ | ||
| 1198 | DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount); | ||
| 1199 | |||
| 1200 | /* Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples. */ | ||
| 1201 | DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount); | ||
| 1202 | |||
| 1203 | /* Low-level function for converting A-law samples to signed 32-bit PCM samples. */ | ||
| 1204 | DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); | ||
| 1205 | |||
| 1206 | /* Low-level function for converting u-law samples to signed 32-bit PCM samples. */ | ||
| 1207 | DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); | ||
| 1208 | |||
| 1209 | #endif /* DR_WAV_NO_CONVERSION_API */ | ||
| 1210 | |||
| 1211 | |||
| 1212 | /* High-Level Convenience Helpers */ | ||
| 1213 | |||
| 1214 | #ifndef DR_WAV_NO_STDIO | ||
| 1215 | /* | ||
| 1216 | Helper for initializing a wave file for reading using stdio. | ||
| 1217 | |||
| 1218 | This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav | ||
| 1219 | objects because the operating system may restrict the number of file handles an application can have open at | ||
| 1220 | any given time. | ||
| 1221 | */ | ||
| 1222 | DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1223 | DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1224 | DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1225 | DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1226 | DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1227 | DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1228 | |||
| 1229 | |||
| 1230 | /* | ||
| 1231 | Helper for initializing a wave file for writing using stdio. | ||
| 1232 | |||
| 1233 | This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav | ||
| 1234 | objects because the operating system may restrict the number of file handles an application can have open at | ||
| 1235 | any given time. | ||
| 1236 | */ | ||
| 1237 | DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1238 | DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1239 | DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1240 | DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1241 | DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1242 | DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1243 | #endif /* DR_WAV_NO_STDIO */ | ||
| 1244 | |||
| 1245 | /* | ||
| 1246 | Helper for initializing a loader from a pre-allocated memory buffer. | ||
| 1247 | |||
| 1248 | This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for | ||
| 1249 | the lifetime of the drwav object. | ||
| 1250 | |||
| 1251 | The buffer should contain the contents of the entire wave file, not just the sample data. | ||
| 1252 | */ | ||
| 1253 | DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1254 | DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1255 | DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1256 | |||
| 1257 | /* | ||
| 1258 | Helper for initializing a writer which outputs data to a memory buffer. | ||
| 1259 | |||
| 1260 | dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free(). | ||
| 1261 | |||
| 1262 | The buffer will remain allocated even after drwav_uninit() is called. The buffer should not be considered valid | ||
| 1263 | until after drwav_uninit() has been called. | ||
| 1264 | */ | ||
| 1265 | DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1266 | DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1267 | DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1268 | |||
| 1269 | |||
| 1270 | #ifndef DR_WAV_NO_CONVERSION_API | ||
| 1271 | /* | ||
| 1272 | Opens and reads an entire wav file in a single operation. | ||
| 1273 | |||
| 1274 | The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. | ||
| 1275 | */ | ||
| 1276 | DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1277 | DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1278 | DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1279 | #ifndef DR_WAV_NO_STDIO | ||
| 1280 | /* | ||
| 1281 | Opens and decodes an entire wav file in a single operation. | ||
| 1282 | |||
| 1283 | The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. | ||
| 1284 | */ | ||
| 1285 | DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1286 | DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1287 | DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1288 | DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1289 | DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1290 | DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1291 | #endif | ||
| 1292 | /* | ||
| 1293 | Opens and decodes an entire wav file from a block of memory in a single operation. | ||
| 1294 | |||
| 1295 | The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. | ||
| 1296 | */ | ||
| 1297 | DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1298 | DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1299 | DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1300 | #endif | ||
| 1301 | |||
| 1302 | /* Frees data that was allocated internally by dr_wav. */ | ||
| 1303 | DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks); | ||
| 1304 | |||
| 1305 | /* Converts bytes from a wav stream to a sized type of native endian. */ | ||
| 1306 | DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data); | ||
| 1307 | DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data); | ||
| 1308 | DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data); | ||
| 1309 | DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data); | ||
| 1310 | DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data); | ||
| 1311 | DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data); | ||
| 1312 | DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data); | ||
| 1313 | |||
| 1314 | /* Compares a GUID for the purpose of checking the type of a Wave64 chunk. */ | ||
| 1315 | DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]); | ||
| 1316 | |||
| 1317 | /* Compares a four-character-code for the purpose of checking the type of a RIFF chunk. */ | ||
| 1318 | DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); | ||
| 1319 | |||
| 1320 | #ifdef __cplusplus | ||
| 1321 | } | ||
| 1322 | #endif | ||
| 1323 | #endif /* dr_wav_h */ | ||
| 1324 | |||
| 1325 | |||
| 1326 | /************************************************************************************************************************************************************ | ||
| 1327 | ************************************************************************************************************************************************************ | ||
| 1328 | |||
| 1329 | IMPLEMENTATION | ||
| 1330 | |||
| 1331 | ************************************************************************************************************************************************************ | ||
| 1332 | ************************************************************************************************************************************************************/ | ||
| 1333 | #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION) | ||
| 1334 | #ifndef dr_wav_c | ||
| 1335 | #define dr_wav_c | ||
| 1336 | |||
| 1337 | #ifdef __MRC__ | ||
| 1338 | /* MrC currently doesn't compile dr_wav correctly with any optimizations enabled. */ | ||
| 1339 | #pragma options opt off | ||
| 1340 | #endif | ||
| 1341 | |||
| 1342 | #include <stdlib.h> | ||
| 1343 | #include <string.h> | ||
| 1344 | #include <limits.h> /* For INT_MAX */ | ||
| 1345 | |||
| 1346 | #ifndef DR_WAV_NO_STDIO | ||
| 1347 | #include <stdio.h> | ||
| 1348 | #ifndef DR_WAV_NO_WCHAR | ||
| 1349 | #include <wchar.h> | ||
| 1350 | #endif | ||
| 1351 | #endif | ||
| 1352 | |||
| 1353 | /* Standard library stuff. */ | ||
| 1354 | #ifndef DRWAV_ASSERT | ||
| 1355 | #include <assert.h> | ||
| 1356 | #define DRWAV_ASSERT(expression) assert(expression) | ||
| 1357 | #endif | ||
| 1358 | #ifndef DRWAV_MALLOC | ||
| 1359 | #define DRWAV_MALLOC(sz) malloc((sz)) | ||
| 1360 | #endif | ||
| 1361 | #ifndef DRWAV_REALLOC | ||
| 1362 | #define DRWAV_REALLOC(p, sz) realloc((p), (sz)) | ||
| 1363 | #endif | ||
| 1364 | #ifndef DRWAV_FREE | ||
| 1365 | #define DRWAV_FREE(p) free((p)) | ||
| 1366 | #endif | ||
| 1367 | #ifndef DRWAV_COPY_MEMORY | ||
| 1368 | #define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) | ||
| 1369 | #endif | ||
| 1370 | #ifndef DRWAV_ZERO_MEMORY | ||
| 1371 | #define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) | ||
| 1372 | #endif | ||
| 1373 | #ifndef DRWAV_ZERO_OBJECT | ||
| 1374 | #define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p)) | ||
| 1375 | #endif | ||
| 1376 | |||
| 1377 | #define drwav_countof(x) (sizeof(x) / sizeof(x[0])) | ||
| 1378 | #define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) | ||
| 1379 | #define drwav_min(a, b) (((a) < (b)) ? (a) : (b)) | ||
| 1380 | #define drwav_max(a, b) (((a) > (b)) ? (a) : (b)) | ||
| 1381 | #define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x)))) | ||
| 1382 | #define drwav_offset_ptr(p, offset) (((drwav_uint8*)(p)) + (offset)) | ||
| 1383 | |||
| 1384 | #define DRWAV_MAX_SIMD_VECTOR_SIZE 32 | ||
| 1385 | |||
| 1386 | /* Architecture Detection */ | ||
| 1387 | #if defined(__x86_64__) || defined(_M_X64) | ||
| 1388 | #define DRWAV_X64 | ||
| 1389 | #elif defined(__i386) || defined(_M_IX86) | ||
| 1390 | #define DRWAV_X86 | ||
| 1391 | #elif defined(__arm__) || defined(_M_ARM) | ||
| 1392 | #define DRWAV_ARM | ||
| 1393 | #endif | ||
| 1394 | /* End Architecture Detection */ | ||
| 1395 | |||
| 1396 | /* Inline */ | ||
| 1397 | #ifdef _MSC_VER | ||
| 1398 | #define DRWAV_INLINE __forceinline | ||
| 1399 | #elif defined(__GNUC__) | ||
| 1400 | /* | ||
| 1401 | I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when | ||
| 1402 | the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some | ||
| 1403 | case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the | ||
| 1404 | command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue | ||
| 1405 | I am using "__inline__" only when we're compiling in strict ANSI mode. | ||
| 1406 | */ | ||
| 1407 | #if defined(__STRICT_ANSI__) | ||
| 1408 | #define DRWAV_GNUC_INLINE_HINT __inline__ | ||
| 1409 | #else | ||
| 1410 | #define DRWAV_GNUC_INLINE_HINT inline | ||
| 1411 | #endif | ||
| 1412 | |||
| 1413 | #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) | ||
| 1414 | #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline)) | ||
| 1415 | #else | ||
| 1416 | #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT | ||
| 1417 | #endif | ||
| 1418 | #elif defined(__WATCOMC__) | ||
| 1419 | #define DRWAV_INLINE __inline | ||
| 1420 | #else | ||
| 1421 | #define DRWAV_INLINE | ||
| 1422 | #endif | ||
| 1423 | /* End Inline */ | ||
| 1424 | |||
| 1425 | /* SIZE_MAX */ | ||
| 1426 | #if defined(SIZE_MAX) | ||
| 1427 | #define DRWAV_SIZE_MAX SIZE_MAX | ||
| 1428 | #else | ||
| 1429 | #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) | ||
| 1430 | #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF) | ||
| 1431 | #else | ||
| 1432 | #define DRWAV_SIZE_MAX 0xFFFFFFFF | ||
| 1433 | #endif | ||
| 1434 | #endif | ||
| 1435 | /* End SIZE_MAX */ | ||
| 1436 | |||
| 1437 | /* Weird bit manipulation is for C89 compatibility (no direct support for 64-bit integers). */ | ||
| 1438 | #define DRWAV_INT64_MIN ((drwav_int64) ((drwav_uint64)0x80000000 << 32)) | ||
| 1439 | #define DRWAV_INT64_MAX ((drwav_int64)(((drwav_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF)) | ||
| 1440 | |||
| 1441 | #if defined(_MSC_VER) && _MSC_VER >= 1400 | ||
| 1442 | #define DRWAV_HAS_BYTESWAP16_INTRINSIC | ||
| 1443 | #define DRWAV_HAS_BYTESWAP32_INTRINSIC | ||
| 1444 | #define DRWAV_HAS_BYTESWAP64_INTRINSIC | ||
| 1445 | #elif defined(__clang__) | ||
| 1446 | #if defined(__has_builtin) | ||
| 1447 | #if __has_builtin(__builtin_bswap16) | ||
| 1448 | #define DRWAV_HAS_BYTESWAP16_INTRINSIC | ||
| 1449 | #endif | ||
| 1450 | #if __has_builtin(__builtin_bswap32) | ||
| 1451 | #define DRWAV_HAS_BYTESWAP32_INTRINSIC | ||
| 1452 | #endif | ||
| 1453 | #if __has_builtin(__builtin_bswap64) | ||
| 1454 | #define DRWAV_HAS_BYTESWAP64_INTRINSIC | ||
| 1455 | #endif | ||
| 1456 | #endif | ||
| 1457 | #elif defined(__GNUC__) | ||
| 1458 | #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) | ||
| 1459 | #define DRWAV_HAS_BYTESWAP32_INTRINSIC | ||
| 1460 | #define DRWAV_HAS_BYTESWAP64_INTRINSIC | ||
| 1461 | #endif | ||
| 1462 | #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) | ||
| 1463 | #define DRWAV_HAS_BYTESWAP16_INTRINSIC | ||
| 1464 | #endif | ||
| 1465 | #endif | ||
| 1466 | |||
| 1467 | DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision) | ||
| 1468 | { | ||
| 1469 | if (pMajor) { | ||
| 1470 | *pMajor = DRWAV_VERSION_MAJOR; | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | if (pMinor) { | ||
| 1474 | *pMinor = DRWAV_VERSION_MINOR; | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | if (pRevision) { | ||
| 1478 | *pRevision = DRWAV_VERSION_REVISION; | ||
| 1479 | } | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | DRWAV_API const char* drwav_version_string(void) | ||
| 1483 | { | ||
| 1484 | return DRWAV_VERSION_STRING; | ||
| 1485 | } | ||
| 1486 | |||
| 1487 | /* | ||
| 1488 | These limits are used for basic validation when initializing the decoder. If you exceed these limits, first of all: what on Earth are | ||
| 1489 | you doing?! (Let me know, I'd be curious!) Second, you can adjust these by #define-ing them before the dr_wav implementation. | ||
| 1490 | */ | ||
| 1491 | #ifndef DRWAV_MAX_SAMPLE_RATE | ||
| 1492 | #define DRWAV_MAX_SAMPLE_RATE 384000 | ||
| 1493 | #endif | ||
| 1494 | #ifndef DRWAV_MAX_CHANNELS | ||
| 1495 | #define DRWAV_MAX_CHANNELS 256 | ||
| 1496 | #endif | ||
| 1497 | #ifndef DRWAV_MAX_BITS_PER_SAMPLE | ||
| 1498 | #define DRWAV_MAX_BITS_PER_SAMPLE 64 | ||
| 1499 | #endif | ||
| 1500 | |||
| 1501 | static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; /* 66666972-912E-11CF-A5D6-28DB04C10000 */ | ||
| 1502 | static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */ | ||
| 1503 | /*static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};*/ /* 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A */ | ||
| 1504 | static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */ | ||
| 1505 | static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 74636166-ACF3-11D3-8CD1-00C04F8EDB8A */ | ||
| 1506 | static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */ | ||
| 1507 | /*static const drwav_uint8 drwavGUID_W64_SMPL[16] = {0x73,0x6D,0x70,0x6C, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};*/ /* 6C706D73-ACF3-11D3-8CD1-00C04F8EDB8A */ | ||
| 1508 | |||
| 1509 | |||
| 1510 | static DRWAV_INLINE int drwav__is_little_endian(void) | ||
| 1511 | { | ||
| 1512 | #if defined(DRWAV_X86) || defined(DRWAV_X64) | ||
| 1513 | return DRWAV_TRUE; | ||
| 1514 | #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN | ||
| 1515 | return DRWAV_TRUE; | ||
| 1516 | #else | ||
| 1517 | int n = 1; | ||
| 1518 | return (*(char*)&n) == 1; | ||
| 1519 | #endif | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | |||
| 1523 | static DRWAV_INLINE void drwav_bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid) | ||
| 1524 | { | ||
| 1525 | int i; | ||
| 1526 | for (i = 0; i < 16; ++i) { | ||
| 1527 | guid[i] = data[i]; | ||
| 1528 | } | ||
| 1529 | } | ||
| 1530 | |||
| 1531 | |||
| 1532 | static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n) | ||
| 1533 | { | ||
| 1534 | #ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC | ||
| 1535 | #if defined(_MSC_VER) | ||
| 1536 | return _byteswap_ushort(n); | ||
| 1537 | #elif defined(__GNUC__) || defined(__clang__) | ||
| 1538 | return __builtin_bswap16(n); | ||
| 1539 | #else | ||
| 1540 | #error "This compiler does not support the byte swap intrinsic." | ||
| 1541 | #endif | ||
| 1542 | #else | ||
| 1543 | return ((n & 0xFF00) >> 8) | | ||
| 1544 | ((n & 0x00FF) << 8); | ||
| 1545 | #endif | ||
| 1546 | } | ||
| 1547 | |||
| 1548 | static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n) | ||
| 1549 | { | ||
| 1550 | #ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC | ||
| 1551 | #if defined(_MSC_VER) | ||
| 1552 | return _byteswap_ulong(n); | ||
| 1553 | #elif defined(__GNUC__) || defined(__clang__) | ||
| 1554 | #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ | ||
| 1555 | /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ | ||
| 1556 | drwav_uint32 r; | ||
| 1557 | __asm__ __volatile__ ( | ||
| 1558 | #if defined(DRWAV_64BIT) | ||
| 1559 | "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ | ||
| 1560 | #else | ||
| 1561 | "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) | ||
| 1562 | #endif | ||
| 1563 | ); | ||
| 1564 | return r; | ||
| 1565 | #else | ||
| 1566 | return __builtin_bswap32(n); | ||
| 1567 | #endif | ||
| 1568 | #else | ||
| 1569 | #error "This compiler does not support the byte swap intrinsic." | ||
| 1570 | #endif | ||
| 1571 | #else | ||
| 1572 | return ((n & 0xFF000000) >> 24) | | ||
| 1573 | ((n & 0x00FF0000) >> 8) | | ||
| 1574 | ((n & 0x0000FF00) << 8) | | ||
| 1575 | ((n & 0x000000FF) << 24); | ||
| 1576 | #endif | ||
| 1577 | } | ||
| 1578 | |||
| 1579 | static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n) | ||
| 1580 | { | ||
| 1581 | #ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC | ||
| 1582 | #if defined(_MSC_VER) | ||
| 1583 | return _byteswap_uint64(n); | ||
| 1584 | #elif defined(__GNUC__) || defined(__clang__) | ||
| 1585 | return __builtin_bswap64(n); | ||
| 1586 | #else | ||
| 1587 | #error "This compiler does not support the byte swap intrinsic." | ||
| 1588 | #endif | ||
| 1589 | #else | ||
| 1590 | /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */ | ||
| 1591 | return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) | | ||
| 1592 | ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) | | ||
| 1593 | ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) | | ||
| 1594 | ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) | | ||
| 1595 | ((n & ((drwav_uint64)0xFF000000 )) << 8) | | ||
| 1596 | ((n & ((drwav_uint64)0x00FF0000 )) << 24) | | ||
| 1597 | ((n & ((drwav_uint64)0x0000FF00 )) << 40) | | ||
| 1598 | ((n & ((drwav_uint64)0x000000FF )) << 56); | ||
| 1599 | #endif | ||
| 1600 | } | ||
| 1601 | |||
| 1602 | |||
| 1603 | static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n) | ||
| 1604 | { | ||
| 1605 | return (drwav_int16)drwav__bswap16((drwav_uint16)n); | ||
| 1606 | } | ||
| 1607 | |||
| 1608 | static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount) | ||
| 1609 | { | ||
| 1610 | drwav_uint64 iSample; | ||
| 1611 | for (iSample = 0; iSample < sampleCount; iSample += 1) { | ||
| 1612 | pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]); | ||
| 1613 | } | ||
| 1614 | } | ||
| 1615 | |||
| 1616 | |||
| 1617 | static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p) | ||
| 1618 | { | ||
| 1619 | drwav_uint8 t; | ||
| 1620 | t = p[0]; | ||
| 1621 | p[0] = p[2]; | ||
| 1622 | p[2] = t; | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount) | ||
| 1626 | { | ||
| 1627 | drwav_uint64 iSample; | ||
| 1628 | for (iSample = 0; iSample < sampleCount; iSample += 1) { | ||
| 1629 | drwav_uint8* pSample = pSamples + (iSample*3); | ||
| 1630 | drwav__bswap_s24(pSample); | ||
| 1631 | } | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | |||
| 1635 | static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n) | ||
| 1636 | { | ||
| 1637 | return (drwav_int32)drwav__bswap32((drwav_uint32)n); | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount) | ||
| 1641 | { | ||
| 1642 | drwav_uint64 iSample; | ||
| 1643 | for (iSample = 0; iSample < sampleCount; iSample += 1) { | ||
| 1644 | pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]); | ||
| 1645 | } | ||
| 1646 | } | ||
| 1647 | |||
| 1648 | |||
| 1649 | static DRWAV_INLINE drwav_int64 drwav__bswap_s64(drwav_int64 n) | ||
| 1650 | { | ||
| 1651 | return (drwav_int64)drwav__bswap64((drwav_uint64)n); | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | static DRWAV_INLINE void drwav__bswap_samples_s64(drwav_int64* pSamples, drwav_uint64 sampleCount) | ||
| 1655 | { | ||
| 1656 | drwav_uint64 iSample; | ||
| 1657 | for (iSample = 0; iSample < sampleCount; iSample += 1) { | ||
| 1658 | pSamples[iSample] = drwav__bswap_s64(pSamples[iSample]); | ||
| 1659 | } | ||
| 1660 | } | ||
| 1661 | |||
| 1662 | |||
| 1663 | static DRWAV_INLINE float drwav__bswap_f32(float n) | ||
| 1664 | { | ||
| 1665 | union { | ||
| 1666 | drwav_uint32 i; | ||
| 1667 | float f; | ||
| 1668 | } x; | ||
| 1669 | x.f = n; | ||
| 1670 | x.i = drwav__bswap32(x.i); | ||
| 1671 | |||
| 1672 | return x.f; | ||
| 1673 | } | ||
| 1674 | |||
| 1675 | static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount) | ||
| 1676 | { | ||
| 1677 | drwav_uint64 iSample; | ||
| 1678 | for (iSample = 0; iSample < sampleCount; iSample += 1) { | ||
| 1679 | pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]); | ||
| 1680 | } | ||
| 1681 | } | ||
| 1682 | |||
| 1683 | |||
| 1684 | static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) | ||
| 1685 | { | ||
| 1686 | switch (bytesPerSample) | ||
| 1687 | { | ||
| 1688 | case 1: | ||
| 1689 | { | ||
| 1690 | /* No-op. */ | ||
| 1691 | } break; | ||
| 1692 | case 2: | ||
| 1693 | { | ||
| 1694 | drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); | ||
| 1695 | } break; | ||
| 1696 | case 3: | ||
| 1697 | { | ||
| 1698 | drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount); | ||
| 1699 | } break; | ||
| 1700 | case 4: | ||
| 1701 | { | ||
| 1702 | drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount); | ||
| 1703 | } break; | ||
| 1704 | case 8: | ||
| 1705 | { | ||
| 1706 | drwav__bswap_samples_s64((drwav_int64*)pSamples, sampleCount); | ||
| 1707 | } break; | ||
| 1708 | default: | ||
| 1709 | { | ||
| 1710 | /* Unsupported format. */ | ||
| 1711 | DRWAV_ASSERT(DRWAV_FALSE); | ||
| 1712 | } break; | ||
| 1713 | } | ||
| 1714 | } | ||
| 1715 | |||
| 1716 | |||
| 1717 | |||
| 1718 | DRWAV_PRIVATE DRWAV_INLINE drwav_bool32 drwav_is_container_be(drwav_container container) | ||
| 1719 | { | ||
| 1720 | if (container == drwav_container_rifx || container == drwav_container_aiff) { | ||
| 1721 | return DRWAV_TRUE; | ||
| 1722 | } else { | ||
| 1723 | return DRWAV_FALSE; | ||
| 1724 | } | ||
| 1725 | } | ||
| 1726 | |||
| 1727 | |||
| 1728 | DRWAV_PRIVATE DRWAV_INLINE drwav_uint16 drwav_bytes_to_u16_le(const drwav_uint8* data) | ||
| 1729 | { | ||
| 1730 | return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8); | ||
| 1731 | } | ||
| 1732 | |||
| 1733 | DRWAV_PRIVATE DRWAV_INLINE drwav_uint16 drwav_bytes_to_u16_be(const drwav_uint8* data) | ||
| 1734 | { | ||
| 1735 | return ((drwav_uint16)data[1] << 0) | ((drwav_uint16)data[0] << 8); | ||
| 1736 | } | ||
| 1737 | |||
| 1738 | DRWAV_PRIVATE DRWAV_INLINE drwav_uint16 drwav_bytes_to_u16_ex(const drwav_uint8* data, drwav_container container) | ||
| 1739 | { | ||
| 1740 | if (drwav_is_container_be(container)) { | ||
| 1741 | return drwav_bytes_to_u16_be(data); | ||
| 1742 | } else { | ||
| 1743 | return drwav_bytes_to_u16_le(data); | ||
| 1744 | } | ||
| 1745 | } | ||
| 1746 | |||
| 1747 | |||
| 1748 | DRWAV_PRIVATE DRWAV_INLINE drwav_uint32 drwav_bytes_to_u32_le(const drwav_uint8* data) | ||
| 1749 | { | ||
| 1750 | return ((drwav_uint32)data[0] << 0) | ((drwav_uint32)data[1] << 8) | ((drwav_uint32)data[2] << 16) | ((drwav_uint32)data[3] << 24); | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | DRWAV_PRIVATE DRWAV_INLINE drwav_uint32 drwav_bytes_to_u32_be(const drwav_uint8* data) | ||
| 1754 | { | ||
| 1755 | return ((drwav_uint32)data[3] << 0) | ((drwav_uint32)data[2] << 8) | ((drwav_uint32)data[1] << 16) | ((drwav_uint32)data[0] << 24); | ||
| 1756 | } | ||
| 1757 | |||
| 1758 | DRWAV_PRIVATE DRWAV_INLINE drwav_uint32 drwav_bytes_to_u32_ex(const drwav_uint8* data, drwav_container container) | ||
| 1759 | { | ||
| 1760 | if (drwav_is_container_be(container)) { | ||
| 1761 | return drwav_bytes_to_u32_be(data); | ||
| 1762 | } else { | ||
| 1763 | return drwav_bytes_to_u32_le(data); | ||
| 1764 | } | ||
| 1765 | } | ||
| 1766 | |||
| 1767 | |||
| 1768 | |||
| 1769 | DRWAV_PRIVATE drwav_int64 drwav_aiff_extented_to_s64(const drwav_uint8* data) | ||
| 1770 | { | ||
| 1771 | drwav_uint32 exponent = ((drwav_uint32)data[0] << 8) | data[1]; | ||
| 1772 | drwav_uint64 hi = ((drwav_uint64)data[2] << 24) | ((drwav_uint64)data[3] << 16) | ((drwav_uint64)data[4] << 8) | ((drwav_uint64)data[5] << 0); | ||
| 1773 | drwav_uint64 lo = ((drwav_uint64)data[6] << 24) | ((drwav_uint64)data[7] << 16) | ((drwav_uint64)data[8] << 8) | ((drwav_uint64)data[9] << 0); | ||
| 1774 | drwav_uint64 significand = (hi << 32) | lo; | ||
| 1775 | int sign = exponent >> 15; | ||
| 1776 | |||
| 1777 | /* Remove sign bit. */ | ||
| 1778 | exponent &= 0x7FFF; | ||
| 1779 | |||
| 1780 | /* Special cases. */ | ||
| 1781 | if (exponent == 0 && significand == 0) { | ||
| 1782 | return 0; | ||
| 1783 | } else if (exponent == 0x7FFF) { | ||
| 1784 | return sign ? DRWAV_INT64_MIN : DRWAV_INT64_MAX; /* Infinite. */ | ||
| 1785 | } | ||
| 1786 | |||
| 1787 | exponent -= 16383; | ||
| 1788 | |||
| 1789 | if (exponent > 63) { | ||
| 1790 | return sign ? DRWAV_INT64_MIN : DRWAV_INT64_MAX; /* Too big for a 64-bit integer. */ | ||
| 1791 | } else if (exponent < 1) { | ||
| 1792 | return 0; /* Number is less than 1, so rounds down to 0. */ | ||
| 1793 | } | ||
| 1794 | |||
| 1795 | significand >>= (63 - exponent); | ||
| 1796 | |||
| 1797 | if (sign) { | ||
| 1798 | return -(drwav_int64)significand; | ||
| 1799 | } else { | ||
| 1800 | return (drwav_int64)significand; | ||
| 1801 | } | ||
| 1802 | } | ||
| 1803 | |||
| 1804 | |||
| 1805 | DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData) | ||
| 1806 | { | ||
| 1807 | (void)pUserData; | ||
| 1808 | return DRWAV_MALLOC(sz); | ||
| 1809 | } | ||
| 1810 | |||
| 1811 | DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData) | ||
| 1812 | { | ||
| 1813 | (void)pUserData; | ||
| 1814 | return DRWAV_REALLOC(p, sz); | ||
| 1815 | } | ||
| 1816 | |||
| 1817 | DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData) | ||
| 1818 | { | ||
| 1819 | (void)pUserData; | ||
| 1820 | DRWAV_FREE(p); | ||
| 1821 | } | ||
| 1822 | |||
| 1823 | |||
| 1824 | DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 1825 | { | ||
| 1826 | if (pAllocationCallbacks == NULL) { | ||
| 1827 | return NULL; | ||
| 1828 | } | ||
| 1829 | |||
| 1830 | if (pAllocationCallbacks->onMalloc != NULL) { | ||
| 1831 | return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | /* Try using realloc(). */ | ||
| 1835 | if (pAllocationCallbacks->onRealloc != NULL) { | ||
| 1836 | return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | return NULL; | ||
| 1840 | } | ||
| 1841 | |||
| 1842 | DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 1843 | { | ||
| 1844 | if (pAllocationCallbacks == NULL) { | ||
| 1845 | return NULL; | ||
| 1846 | } | ||
| 1847 | |||
| 1848 | if (pAllocationCallbacks->onRealloc != NULL) { | ||
| 1849 | return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); | ||
| 1850 | } | ||
| 1851 | |||
| 1852 | /* Try emulating realloc() in terms of malloc()/free(). */ | ||
| 1853 | if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { | ||
| 1854 | void* p2; | ||
| 1855 | |||
| 1856 | p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); | ||
| 1857 | if (p2 == NULL) { | ||
| 1858 | return NULL; | ||
| 1859 | } | ||
| 1860 | |||
| 1861 | if (p != NULL) { | ||
| 1862 | DRWAV_COPY_MEMORY(p2, p, szOld); | ||
| 1863 | pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); | ||
| 1864 | } | ||
| 1865 | |||
| 1866 | return p2; | ||
| 1867 | } | ||
| 1868 | |||
| 1869 | return NULL; | ||
| 1870 | } | ||
| 1871 | |||
| 1872 | DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 1873 | { | ||
| 1874 | if (p == NULL || pAllocationCallbacks == NULL) { | ||
| 1875 | return; | ||
| 1876 | } | ||
| 1877 | |||
| 1878 | if (pAllocationCallbacks->onFree != NULL) { | ||
| 1879 | pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); | ||
| 1880 | } | ||
| 1881 | } | ||
| 1882 | |||
| 1883 | |||
| 1884 | DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 1885 | { | ||
| 1886 | if (pAllocationCallbacks != NULL) { | ||
| 1887 | /* Copy. */ | ||
| 1888 | return *pAllocationCallbacks; | ||
| 1889 | } else { | ||
| 1890 | /* Defaults. */ | ||
| 1891 | drwav_allocation_callbacks allocationCallbacks; | ||
| 1892 | allocationCallbacks.pUserData = NULL; | ||
| 1893 | allocationCallbacks.onMalloc = drwav__malloc_default; | ||
| 1894 | allocationCallbacks.onRealloc = drwav__realloc_default; | ||
| 1895 | allocationCallbacks.onFree = drwav__free_default; | ||
| 1896 | return allocationCallbacks; | ||
| 1897 | } | ||
| 1898 | } | ||
| 1899 | |||
| 1900 | |||
| 1901 | static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag) | ||
| 1902 | { | ||
| 1903 | return | ||
| 1904 | formatTag == DR_WAVE_FORMAT_ADPCM || | ||
| 1905 | formatTag == DR_WAVE_FORMAT_DVI_ADPCM; | ||
| 1906 | } | ||
| 1907 | |||
| 1908 | DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize) | ||
| 1909 | { | ||
| 1910 | return (unsigned int)(chunkSize % 2); | ||
| 1911 | } | ||
| 1912 | |||
| 1913 | DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize) | ||
| 1914 | { | ||
| 1915 | return (unsigned int)(chunkSize % 8); | ||
| 1916 | } | ||
| 1917 | |||
| 1918 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); | ||
| 1919 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); | ||
| 1920 | DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); | ||
| 1921 | |||
| 1922 | DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut) | ||
| 1923 | { | ||
| 1924 | if (container == drwav_container_riff || container == drwav_container_rifx || container == drwav_container_rf64 || container == drwav_container_aiff) { | ||
| 1925 | drwav_uint8 sizeInBytes[4]; | ||
| 1926 | |||
| 1927 | if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { | ||
| 1928 | return DRWAV_AT_END; | ||
| 1929 | } | ||
| 1930 | |||
| 1931 | if (onRead(pUserData, sizeInBytes, 4) != 4) { | ||
| 1932 | return DRWAV_INVALID_FILE; | ||
| 1933 | } | ||
| 1934 | |||
| 1935 | pHeaderOut->sizeInBytes = drwav_bytes_to_u32_ex(sizeInBytes, container); | ||
| 1936 | pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); | ||
| 1937 | |||
| 1938 | *pRunningBytesReadOut += 8; | ||
| 1939 | } else if (container == drwav_container_w64) { | ||
| 1940 | drwav_uint8 sizeInBytes[8]; | ||
| 1941 | |||
| 1942 | if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { | ||
| 1943 | return DRWAV_AT_END; | ||
| 1944 | } | ||
| 1945 | |||
| 1946 | if (onRead(pUserData, sizeInBytes, 8) != 8) { | ||
| 1947 | return DRWAV_INVALID_FILE; | ||
| 1948 | } | ||
| 1949 | |||
| 1950 | pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24; /* <-- Subtract 24 because w64 includes the size of the header. */ | ||
| 1951 | pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); | ||
| 1952 | *pRunningBytesReadOut += 24; | ||
| 1953 | } else { | ||
| 1954 | return DRWAV_INVALID_FILE; | ||
| 1955 | } | ||
| 1956 | |||
| 1957 | return DRWAV_SUCCESS; | ||
| 1958 | } | ||
| 1959 | |||
| 1960 | DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) | ||
| 1961 | { | ||
| 1962 | drwav_uint64 bytesRemainingToSeek = offset; | ||
| 1963 | while (bytesRemainingToSeek > 0) { | ||
| 1964 | if (bytesRemainingToSeek > 0x7FFFFFFF) { | ||
| 1965 | if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { | ||
| 1966 | return DRWAV_FALSE; | ||
| 1967 | } | ||
| 1968 | bytesRemainingToSeek -= 0x7FFFFFFF; | ||
| 1969 | } else { | ||
| 1970 | if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) { | ||
| 1971 | return DRWAV_FALSE; | ||
| 1972 | } | ||
| 1973 | bytesRemainingToSeek = 0; | ||
| 1974 | } | ||
| 1975 | } | ||
| 1976 | |||
| 1977 | return DRWAV_TRUE; | ||
| 1978 | } | ||
| 1979 | |||
| 1980 | DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) | ||
| 1981 | { | ||
| 1982 | if (offset <= 0x7FFFFFFF) { | ||
| 1983 | return onSeek(pUserData, (int)offset, drwav_seek_origin_start); | ||
| 1984 | } | ||
| 1985 | |||
| 1986 | /* Larger than 32-bit seek. */ | ||
| 1987 | if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) { | ||
| 1988 | return DRWAV_FALSE; | ||
| 1989 | } | ||
| 1990 | offset -= 0x7FFFFFFF; | ||
| 1991 | |||
| 1992 | for (;;) { | ||
| 1993 | if (offset <= 0x7FFFFFFF) { | ||
| 1994 | return onSeek(pUserData, (int)offset, drwav_seek_origin_current); | ||
| 1995 | } | ||
| 1996 | |||
| 1997 | if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { | ||
| 1998 | return DRWAV_FALSE; | ||
| 1999 | } | ||
| 2000 | offset -= 0x7FFFFFFF; | ||
| 2001 | } | ||
| 2002 | |||
| 2003 | /* Should never get here. */ | ||
| 2004 | /*return DRWAV_TRUE; */ | ||
| 2005 | } | ||
| 2006 | |||
| 2007 | |||
| 2008 | |||
| 2009 | DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) | ||
| 2010 | { | ||
| 2011 | size_t bytesRead; | ||
| 2012 | |||
| 2013 | DRWAV_ASSERT(onRead != NULL); | ||
| 2014 | DRWAV_ASSERT(pCursor != NULL); | ||
| 2015 | |||
| 2016 | bytesRead = onRead(pUserData, pBufferOut, bytesToRead); | ||
| 2017 | *pCursor += bytesRead; | ||
| 2018 | return bytesRead; | ||
| 2019 | } | ||
| 2020 | |||
| 2021 | #if 0 | ||
| 2022 | DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor) | ||
| 2023 | { | ||
| 2024 | DRWAV_ASSERT(onSeek != NULL); | ||
| 2025 | DRWAV_ASSERT(pCursor != NULL); | ||
| 2026 | |||
| 2027 | if (!onSeek(pUserData, offset, origin)) { | ||
| 2028 | return DRWAV_FALSE; | ||
| 2029 | } | ||
| 2030 | |||
| 2031 | if (origin == drwav_seek_origin_start) { | ||
| 2032 | *pCursor = offset; | ||
| 2033 | } else { | ||
| 2034 | *pCursor += offset; | ||
| 2035 | } | ||
| 2036 | |||
| 2037 | return DRWAV_TRUE; | ||
| 2038 | } | ||
| 2039 | #endif | ||
| 2040 | |||
| 2041 | |||
| 2042 | #define DRWAV_SMPL_BYTES 36 | ||
| 2043 | #define DRWAV_SMPL_LOOP_BYTES 24 | ||
| 2044 | #define DRWAV_INST_BYTES 7 | ||
| 2045 | #define DRWAV_ACID_BYTES 24 | ||
| 2046 | #define DRWAV_CUE_BYTES 4 | ||
| 2047 | #define DRWAV_BEXT_BYTES 602 | ||
| 2048 | #define DRWAV_BEXT_DESCRIPTION_BYTES 256 | ||
| 2049 | #define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32 | ||
| 2050 | #define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32 | ||
| 2051 | #define DRWAV_BEXT_RESERVED_BYTES 180 | ||
| 2052 | #define DRWAV_BEXT_UMID_BYTES 64 | ||
| 2053 | #define DRWAV_CUE_POINT_BYTES 24 | ||
| 2054 | #define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4 | ||
| 2055 | #define DRWAV_LIST_LABELLED_TEXT_BYTES 20 | ||
| 2056 | |||
| 2057 | #define DRWAV_METADATA_ALIGNMENT 8 | ||
| 2058 | |||
| 2059 | typedef enum | ||
| 2060 | { | ||
| 2061 | drwav__metadata_parser_stage_count, | ||
| 2062 | drwav__metadata_parser_stage_read | ||
| 2063 | } drwav__metadata_parser_stage; | ||
| 2064 | |||
| 2065 | typedef struct | ||
| 2066 | { | ||
| 2067 | drwav_read_proc onRead; | ||
| 2068 | drwav_seek_proc onSeek; | ||
| 2069 | void *pReadSeekUserData; | ||
| 2070 | drwav__metadata_parser_stage stage; | ||
| 2071 | drwav_metadata *pMetadata; | ||
| 2072 | drwav_uint32 metadataCount; | ||
| 2073 | drwav_uint8 *pData; | ||
| 2074 | drwav_uint8 *pDataCursor; | ||
| 2075 | drwav_uint64 metadataCursor; | ||
| 2076 | drwav_uint64 extraCapacity; | ||
| 2077 | } drwav__metadata_parser; | ||
| 2078 | |||
| 2079 | DRWAV_PRIVATE size_t drwav__metadata_memory_capacity(drwav__metadata_parser* pParser) | ||
| 2080 | { | ||
| 2081 | drwav_uint64 cap = sizeof(drwav_metadata) * (drwav_uint64)pParser->metadataCount + pParser->extraCapacity; | ||
| 2082 | if (cap > DRWAV_SIZE_MAX) { | ||
| 2083 | return 0; /* Too big. */ | ||
| 2084 | } | ||
| 2085 | |||
| 2086 | return (size_t)cap; /* Safe cast thanks to the check above. */ | ||
| 2087 | } | ||
| 2088 | |||
| 2089 | DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pParser, size_t size, size_t align) | ||
| 2090 | { | ||
| 2091 | drwav_uint8* pResult; | ||
| 2092 | |||
| 2093 | if (align) { | ||
| 2094 | drwav_uintptr modulo = (drwav_uintptr)pParser->pDataCursor % align; | ||
| 2095 | if (modulo != 0) { | ||
| 2096 | pParser->pDataCursor += align - modulo; | ||
| 2097 | } | ||
| 2098 | } | ||
| 2099 | |||
| 2100 | pResult = pParser->pDataCursor; | ||
| 2101 | |||
| 2102 | /* | ||
| 2103 | Getting to the point where this function is called means there should always be memory | ||
| 2104 | available. Out of memory checks should have been done at an earlier stage. | ||
| 2105 | */ | ||
| 2106 | DRWAV_ASSERT((pResult + size) <= (pParser->pData + drwav__metadata_memory_capacity(pParser))); | ||
| 2107 | |||
| 2108 | pParser->pDataCursor += size; | ||
| 2109 | return pResult; | ||
| 2110 | } | ||
| 2111 | |||
| 2112 | DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser* pParser, size_t bytes, size_t align) | ||
| 2113 | { | ||
| 2114 | size_t extra = bytes + (align ? (align - 1) : 0); | ||
| 2115 | pParser->extraCapacity += extra; | ||
| 2116 | } | ||
| 2117 | |||
| 2118 | DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 2119 | { | ||
| 2120 | if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { | ||
| 2121 | pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); | ||
| 2122 | |||
| 2123 | pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); | ||
| 2124 | pParser->pDataCursor = pParser->pData; | ||
| 2125 | |||
| 2126 | if (pParser->pData == NULL) { | ||
| 2127 | return DRWAV_OUT_OF_MEMORY; | ||
| 2128 | } | ||
| 2129 | |||
| 2130 | /* | ||
| 2131 | We don't need to worry about specifying an alignment here because malloc always returns something | ||
| 2132 | of suitable alignment. This also means pParser->pMetadata is all that we need to store in order | ||
| 2133 | for us to free when we are done. | ||
| 2134 | */ | ||
| 2135 | pParser->pMetadata = (drwav_metadata*)drwav__metadata_get_memory(pParser, sizeof(drwav_metadata) * pParser->metadataCount, 1); | ||
| 2136 | pParser->metadataCursor = 0; | ||
| 2137 | } | ||
| 2138 | |||
| 2139 | return DRWAV_SUCCESS; | ||
| 2140 | } | ||
| 2141 | |||
| 2142 | DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) | ||
| 2143 | { | ||
| 2144 | if (pCursor != NULL) { | ||
| 2145 | return drwav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); | ||
| 2146 | } else { | ||
| 2147 | return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); | ||
| 2148 | } | ||
| 2149 | } | ||
| 2150 | |||
| 2151 | DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) | ||
| 2152 | { | ||
| 2153 | drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES]; | ||
| 2154 | drwav_uint64 totalBytesRead = 0; | ||
| 2155 | size_t bytesJustRead; | ||
| 2156 | |||
| 2157 | if (pMetadata == NULL) { | ||
| 2158 | return 0; | ||
| 2159 | } | ||
| 2160 | |||
| 2161 | bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); | ||
| 2162 | |||
| 2163 | DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); | ||
| 2164 | DRWAV_ASSERT(pChunkHeader != NULL); | ||
| 2165 | |||
| 2166 | if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { | ||
| 2167 | drwav_uint32 iSampleLoop; | ||
| 2168 | |||
| 2169 | pMetadata->type = drwav_metadata_type_smpl; | ||
| 2170 | pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); | ||
| 2171 | pMetadata->data.smpl.productId = drwav_bytes_to_u32(smplHeaderData + 4); | ||
| 2172 | pMetadata->data.smpl.samplePeriodNanoseconds = drwav_bytes_to_u32(smplHeaderData + 8); | ||
| 2173 | pMetadata->data.smpl.midiUnityNote = drwav_bytes_to_u32(smplHeaderData + 12); | ||
| 2174 | pMetadata->data.smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData + 16); | ||
| 2175 | pMetadata->data.smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData + 20); | ||
| 2176 | pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24); | ||
| 2177 | pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28); | ||
| 2178 | pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32); | ||
| 2179 | |||
| 2180 | /* | ||
| 2181 | The loop count needs to be validated against the size of the chunk for safety so we don't | ||
| 2182 | attempt to read over the boundary of the chunk. | ||
| 2183 | */ | ||
| 2184 | if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) { | ||
| 2185 | pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT); | ||
| 2186 | |||
| 2187 | for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { | ||
| 2188 | drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES]; | ||
| 2189 | bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); | ||
| 2190 | |||
| 2191 | if (bytesJustRead == sizeof(smplLoopData)) { | ||
| 2192 | pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0); | ||
| 2193 | pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4); | ||
| 2194 | pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8); | ||
| 2195 | pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12); | ||
| 2196 | pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16); | ||
| 2197 | pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20); | ||
| 2198 | } else { | ||
| 2199 | break; | ||
| 2200 | } | ||
| 2201 | } | ||
| 2202 | |||
| 2203 | if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { | ||
| 2204 | pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); | ||
| 2205 | DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); | ||
| 2206 | |||
| 2207 | drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); | ||
| 2208 | } | ||
| 2209 | } | ||
| 2210 | } | ||
| 2211 | |||
| 2212 | return totalBytesRead; | ||
| 2213 | } | ||
| 2214 | |||
| 2215 | DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) | ||
| 2216 | { | ||
| 2217 | drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES]; | ||
| 2218 | drwav_uint64 totalBytesRead = 0; | ||
| 2219 | size_t bytesJustRead; | ||
| 2220 | |||
| 2221 | if (pMetadata == NULL) { | ||
| 2222 | return 0; | ||
| 2223 | } | ||
| 2224 | |||
| 2225 | bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); | ||
| 2226 | |||
| 2227 | DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); | ||
| 2228 | |||
| 2229 | if (bytesJustRead == sizeof(cueHeaderSectionData)) { | ||
| 2230 | pMetadata->type = drwav_metadata_type_cue; | ||
| 2231 | pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData); | ||
| 2232 | |||
| 2233 | /* | ||
| 2234 | We need to validate the cue point count against the size of the chunk so we don't read | ||
| 2235 | beyond the chunk. | ||
| 2236 | */ | ||
| 2237 | if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES) { | ||
| 2238 | pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT); | ||
| 2239 | DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); | ||
| 2240 | |||
| 2241 | if (pMetadata->data.cue.cuePointCount > 0) { | ||
| 2242 | drwav_uint32 iCuePoint; | ||
| 2243 | |||
| 2244 | for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { | ||
| 2245 | drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES]; | ||
| 2246 | bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); | ||
| 2247 | |||
| 2248 | if (bytesJustRead == sizeof(cuePointData)) { | ||
| 2249 | pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0); | ||
| 2250 | pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4); | ||
| 2251 | pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; | ||
| 2252 | pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; | ||
| 2253 | pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; | ||
| 2254 | pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; | ||
| 2255 | pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12); | ||
| 2256 | pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16); | ||
| 2257 | pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20); | ||
| 2258 | } else { | ||
| 2259 | break; | ||
| 2260 | } | ||
| 2261 | } | ||
| 2262 | } | ||
| 2263 | } | ||
| 2264 | } | ||
| 2265 | |||
| 2266 | return totalBytesRead; | ||
| 2267 | } | ||
| 2268 | |||
| 2269 | DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) | ||
| 2270 | { | ||
| 2271 | drwav_uint8 instData[DRWAV_INST_BYTES]; | ||
| 2272 | drwav_uint64 bytesRead; | ||
| 2273 | |||
| 2274 | if (pMetadata == NULL) { | ||
| 2275 | return 0; | ||
| 2276 | } | ||
| 2277 | |||
| 2278 | bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); | ||
| 2279 | |||
| 2280 | DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); | ||
| 2281 | |||
| 2282 | if (bytesRead == sizeof(instData)) { | ||
| 2283 | pMetadata->type = drwav_metadata_type_inst; | ||
| 2284 | pMetadata->data.inst.midiUnityNote = (drwav_int8)instData[0]; | ||
| 2285 | pMetadata->data.inst.fineTuneCents = (drwav_int8)instData[1]; | ||
| 2286 | pMetadata->data.inst.gainDecibels = (drwav_int8)instData[2]; | ||
| 2287 | pMetadata->data.inst.lowNote = (drwav_int8)instData[3]; | ||
| 2288 | pMetadata->data.inst.highNote = (drwav_int8)instData[4]; | ||
| 2289 | pMetadata->data.inst.lowVelocity = (drwav_int8)instData[5]; | ||
| 2290 | pMetadata->data.inst.highVelocity = (drwav_int8)instData[6]; | ||
| 2291 | } | ||
| 2292 | |||
| 2293 | return bytesRead; | ||
| 2294 | } | ||
| 2295 | |||
| 2296 | DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) | ||
| 2297 | { | ||
| 2298 | drwav_uint8 acidData[DRWAV_ACID_BYTES]; | ||
| 2299 | drwav_uint64 bytesRead; | ||
| 2300 | |||
| 2301 | if (pMetadata == NULL) { | ||
| 2302 | return 0; | ||
| 2303 | } | ||
| 2304 | |||
| 2305 | bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); | ||
| 2306 | |||
| 2307 | DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); | ||
| 2308 | |||
| 2309 | if (bytesRead == sizeof(acidData)) { | ||
| 2310 | pMetadata->type = drwav_metadata_type_acid; | ||
| 2311 | pMetadata->data.acid.flags = drwav_bytes_to_u32(acidData + 0); | ||
| 2312 | pMetadata->data.acid.midiUnityNote = drwav_bytes_to_u16(acidData + 4); | ||
| 2313 | pMetadata->data.acid.reserved1 = drwav_bytes_to_u16(acidData + 6); | ||
| 2314 | pMetadata->data.acid.reserved2 = drwav_bytes_to_f32(acidData + 8); | ||
| 2315 | pMetadata->data.acid.numBeats = drwav_bytes_to_u32(acidData + 12); | ||
| 2316 | pMetadata->data.acid.meterDenominator = drwav_bytes_to_u16(acidData + 16); | ||
| 2317 | pMetadata->data.acid.meterNumerator = drwav_bytes_to_u16(acidData + 18); | ||
| 2318 | pMetadata->data.acid.tempo = drwav_bytes_to_f32(acidData + 20); | ||
| 2319 | } | ||
| 2320 | |||
| 2321 | return bytesRead; | ||
| 2322 | } | ||
| 2323 | |||
| 2324 | DRWAV_PRIVATE size_t drwav__strlen(const char* str) | ||
| 2325 | { | ||
| 2326 | size_t result = 0; | ||
| 2327 | |||
| 2328 | while (*str++) { | ||
| 2329 | result += 1; | ||
| 2330 | } | ||
| 2331 | |||
| 2332 | return result; | ||
| 2333 | } | ||
| 2334 | |||
| 2335 | DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead) | ||
| 2336 | { | ||
| 2337 | size_t result = 0; | ||
| 2338 | |||
| 2339 | while (*str++ && result < maxToRead) { | ||
| 2340 | result += 1; | ||
| 2341 | } | ||
| 2342 | |||
| 2343 | return result; | ||
| 2344 | } | ||
| 2345 | |||
| 2346 | DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, const char* str, size_t maxToRead) | ||
| 2347 | { | ||
| 2348 | size_t len = drwav__strlen_clamped(str, maxToRead); | ||
| 2349 | |||
| 2350 | if (len) { | ||
| 2351 | char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1); | ||
| 2352 | DRWAV_ASSERT(result != NULL); | ||
| 2353 | |||
| 2354 | DRWAV_COPY_MEMORY(result, str, len); | ||
| 2355 | result[len] = '\0'; | ||
| 2356 | |||
| 2357 | return result; | ||
| 2358 | } else { | ||
| 2359 | return NULL; | ||
| 2360 | } | ||
| 2361 | } | ||
| 2362 | |||
| 2363 | typedef struct | ||
| 2364 | { | ||
| 2365 | const void* pBuffer; | ||
| 2366 | size_t sizeInBytes; | ||
| 2367 | size_t cursor; | ||
| 2368 | } drwav_buffer_reader; | ||
| 2369 | |||
| 2370 | DRWAV_PRIVATE drwav_result drwav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, drwav_buffer_reader* pReader) | ||
| 2371 | { | ||
| 2372 | DRWAV_ASSERT(pBuffer != NULL); | ||
| 2373 | DRWAV_ASSERT(pReader != NULL); | ||
| 2374 | |||
| 2375 | DRWAV_ZERO_OBJECT(pReader); | ||
| 2376 | |||
| 2377 | pReader->pBuffer = pBuffer; | ||
| 2378 | pReader->sizeInBytes = sizeInBytes; | ||
| 2379 | pReader->cursor = 0; | ||
| 2380 | |||
| 2381 | return DRWAV_SUCCESS; | ||
| 2382 | } | ||
| 2383 | |||
| 2384 | DRWAV_PRIVATE const void* drwav_buffer_reader_ptr(const drwav_buffer_reader* pReader) | ||
| 2385 | { | ||
| 2386 | DRWAV_ASSERT(pReader != NULL); | ||
| 2387 | |||
| 2388 | return drwav_offset_ptr(pReader->pBuffer, pReader->cursor); | ||
| 2389 | } | ||
| 2390 | |||
| 2391 | DRWAV_PRIVATE drwav_result drwav_buffer_reader_seek(drwav_buffer_reader* pReader, size_t bytesToSeek) | ||
| 2392 | { | ||
| 2393 | DRWAV_ASSERT(pReader != NULL); | ||
| 2394 | |||
| 2395 | if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { | ||
| 2396 | return DRWAV_BAD_SEEK; /* Seeking too far forward. */ | ||
| 2397 | } | ||
| 2398 | |||
| 2399 | pReader->cursor += bytesToSeek; | ||
| 2400 | |||
| 2401 | return DRWAV_SUCCESS; | ||
| 2402 | } | ||
| 2403 | |||
| 2404 | DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) | ||
| 2405 | { | ||
| 2406 | drwav_result result = DRWAV_SUCCESS; | ||
| 2407 | size_t bytesRemaining; | ||
| 2408 | |||
| 2409 | DRWAV_ASSERT(pReader != NULL); | ||
| 2410 | |||
| 2411 | if (pBytesRead != NULL) { | ||
| 2412 | *pBytesRead = 0; | ||
| 2413 | } | ||
| 2414 | |||
| 2415 | bytesRemaining = (pReader->sizeInBytes - pReader->cursor); | ||
| 2416 | if (bytesToRead > bytesRemaining) { | ||
| 2417 | bytesToRead = bytesRemaining; | ||
| 2418 | } | ||
| 2419 | |||
| 2420 | if (pDst == NULL) { | ||
| 2421 | /* Seek. */ | ||
| 2422 | result = drwav_buffer_reader_seek(pReader, bytesToRead); | ||
| 2423 | } else { | ||
| 2424 | /* Read. */ | ||
| 2425 | DRWAV_COPY_MEMORY(pDst, drwav_buffer_reader_ptr(pReader), bytesToRead); | ||
| 2426 | pReader->cursor += bytesToRead; | ||
| 2427 | } | ||
| 2428 | |||
| 2429 | DRWAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); | ||
| 2430 | |||
| 2431 | if (result == DRWAV_SUCCESS) { | ||
| 2432 | if (pBytesRead != NULL) { | ||
| 2433 | *pBytesRead = bytesToRead; | ||
| 2434 | } | ||
| 2435 | } | ||
| 2436 | |||
| 2437 | return DRWAV_SUCCESS; | ||
| 2438 | } | ||
| 2439 | |||
| 2440 | DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u16(drwav_buffer_reader* pReader, drwav_uint16* pDst) | ||
| 2441 | { | ||
| 2442 | drwav_result result; | ||
| 2443 | size_t bytesRead; | ||
| 2444 | drwav_uint8 data[2]; | ||
| 2445 | |||
| 2446 | DRWAV_ASSERT(pReader != NULL); | ||
| 2447 | DRWAV_ASSERT(pDst != NULL); | ||
| 2448 | |||
| 2449 | *pDst = 0; /* Safety. */ | ||
| 2450 | |||
| 2451 | result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); | ||
| 2452 | if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { | ||
| 2453 | return result; | ||
| 2454 | } | ||
| 2455 | |||
| 2456 | *pDst = drwav_bytes_to_u16(data); | ||
| 2457 | |||
| 2458 | return DRWAV_SUCCESS; | ||
| 2459 | } | ||
| 2460 | |||
| 2461 | DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u32(drwav_buffer_reader* pReader, drwav_uint32* pDst) | ||
| 2462 | { | ||
| 2463 | drwav_result result; | ||
| 2464 | size_t bytesRead; | ||
| 2465 | drwav_uint8 data[4]; | ||
| 2466 | |||
| 2467 | DRWAV_ASSERT(pReader != NULL); | ||
| 2468 | DRWAV_ASSERT(pDst != NULL); | ||
| 2469 | |||
| 2470 | *pDst = 0; /* Safety. */ | ||
| 2471 | |||
| 2472 | result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); | ||
| 2473 | if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { | ||
| 2474 | return result; | ||
| 2475 | } | ||
| 2476 | |||
| 2477 | *pDst = drwav_bytes_to_u32(data); | ||
| 2478 | |||
| 2479 | return DRWAV_SUCCESS; | ||
| 2480 | } | ||
| 2481 | |||
| 2482 | |||
| 2483 | |||
| 2484 | DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) | ||
| 2485 | { | ||
| 2486 | drwav_uint8 bextData[DRWAV_BEXT_BYTES]; | ||
| 2487 | size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); | ||
| 2488 | |||
| 2489 | DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); | ||
| 2490 | |||
| 2491 | if (bytesRead == sizeof(bextData)) { | ||
| 2492 | drwav_buffer_reader reader; | ||
| 2493 | drwav_uint32 timeReferenceLow; | ||
| 2494 | drwav_uint32 timeReferenceHigh; | ||
| 2495 | size_t extraBytes; | ||
| 2496 | |||
| 2497 | pMetadata->type = drwav_metadata_type_bext; | ||
| 2498 | |||
| 2499 | if (drwav_buffer_reader_init(bextData, bytesRead, &reader) == DRWAV_SUCCESS) { | ||
| 2500 | pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_DESCRIPTION_BYTES); | ||
| 2501 | drwav_buffer_reader_seek(&reader, DRWAV_BEXT_DESCRIPTION_BYTES); | ||
| 2502 | |||
| 2503 | pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_NAME_BYTES); | ||
| 2504 | drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); | ||
| 2505 | |||
| 2506 | pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_REF_BYTES); | ||
| 2507 | drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_REF_BYTES); | ||
| 2508 | |||
| 2509 | drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); | ||
| 2510 | drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); | ||
| 2511 | |||
| 2512 | drwav_buffer_reader_read_u32(&reader, &timeReferenceLow); | ||
| 2513 | drwav_buffer_reader_read_u32(&reader, &timeReferenceHigh); | ||
| 2514 | pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow; | ||
| 2515 | |||
| 2516 | drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); | ||
| 2517 | |||
| 2518 | pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1); | ||
| 2519 | drwav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES, NULL); | ||
| 2520 | |||
| 2521 | drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); | ||
| 2522 | drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); | ||
| 2523 | drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); | ||
| 2524 | drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); | ||
| 2525 | drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); | ||
| 2526 | |||
| 2527 | DRWAV_ASSERT((drwav_offset_ptr(drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_RESERVED_BYTES)) == (bextData + DRWAV_BEXT_BYTES)); | ||
| 2528 | |||
| 2529 | extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES); | ||
| 2530 | if (extraBytes > 0) { | ||
| 2531 | pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1); | ||
| 2532 | DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); | ||
| 2533 | |||
| 2534 | bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); | ||
| 2535 | pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory); | ||
| 2536 | } else { | ||
| 2537 | pMetadata->data.bext.pCodingHistory = NULL; | ||
| 2538 | pMetadata->data.bext.codingHistorySize = 0; | ||
| 2539 | } | ||
| 2540 | } | ||
| 2541 | } | ||
| 2542 | |||
| 2543 | return bytesRead; | ||
| 2544 | } | ||
| 2545 | |||
| 2546 | DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize, drwav_metadata_type type) | ||
| 2547 | { | ||
| 2548 | drwav_uint8 cueIDBuffer[DRWAV_LIST_LABEL_OR_NOTE_BYTES]; | ||
| 2549 | drwav_uint64 totalBytesRead = 0; | ||
| 2550 | size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); | ||
| 2551 | |||
| 2552 | DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); | ||
| 2553 | |||
| 2554 | if (bytesJustRead == sizeof(cueIDBuffer)) { | ||
| 2555 | drwav_uint32 sizeIncludingNullTerminator; | ||
| 2556 | |||
| 2557 | pMetadata->type = type; | ||
| 2558 | pMetadata->data.labelOrNote.cuePointId = drwav_bytes_to_u32(cueIDBuffer); | ||
| 2559 | |||
| 2560 | sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; | ||
| 2561 | if (sizeIncludingNullTerminator > 0) { | ||
| 2562 | pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; | ||
| 2563 | pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); | ||
| 2564 | DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); | ||
| 2565 | |||
| 2566 | drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); | ||
| 2567 | } else { | ||
| 2568 | pMetadata->data.labelOrNote.stringLength = 0; | ||
| 2569 | pMetadata->data.labelOrNote.pString = NULL; | ||
| 2570 | } | ||
| 2571 | } | ||
| 2572 | |||
| 2573 | return totalBytesRead; | ||
| 2574 | } | ||
| 2575 | |||
| 2576 | DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) | ||
| 2577 | { | ||
| 2578 | drwav_uint8 buffer[DRWAV_LIST_LABELLED_TEXT_BYTES]; | ||
| 2579 | drwav_uint64 totalBytesRead = 0; | ||
| 2580 | size_t bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); | ||
| 2581 | |||
| 2582 | DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); | ||
| 2583 | |||
| 2584 | if (bytesJustRead == sizeof(buffer)) { | ||
| 2585 | drwav_uint32 sizeIncludingNullTerminator; | ||
| 2586 | |||
| 2587 | pMetadata->type = drwav_metadata_type_list_labelled_cue_region; | ||
| 2588 | pMetadata->data.labelledCueRegion.cuePointId = drwav_bytes_to_u32(buffer + 0); | ||
| 2589 | pMetadata->data.labelledCueRegion.sampleLength = drwav_bytes_to_u32(buffer + 4); | ||
| 2590 | pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; | ||
| 2591 | pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; | ||
| 2592 | pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; | ||
| 2593 | pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; | ||
| 2594 | pMetadata->data.labelledCueRegion.country = drwav_bytes_to_u16(buffer + 12); | ||
| 2595 | pMetadata->data.labelledCueRegion.language = drwav_bytes_to_u16(buffer + 14); | ||
| 2596 | pMetadata->data.labelledCueRegion.dialect = drwav_bytes_to_u16(buffer + 16); | ||
| 2597 | pMetadata->data.labelledCueRegion.codePage = drwav_bytes_to_u16(buffer + 18); | ||
| 2598 | |||
| 2599 | sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABELLED_TEXT_BYTES; | ||
| 2600 | if (sizeIncludingNullTerminator > 0) { | ||
| 2601 | pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; | ||
| 2602 | pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); | ||
| 2603 | DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); | ||
| 2604 | |||
| 2605 | drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); | ||
| 2606 | } else { | ||
| 2607 | pMetadata->data.labelledCueRegion.stringLength = 0; | ||
| 2608 | pMetadata->data.labelledCueRegion.pString = NULL; | ||
| 2609 | } | ||
| 2610 | } | ||
| 2611 | |||
| 2612 | return totalBytesRead; | ||
| 2613 | } | ||
| 2614 | |||
| 2615 | DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metadata_parser* pParser, drwav_uint64 chunkSize, drwav_metadata_type type) | ||
| 2616 | { | ||
| 2617 | drwav_uint64 bytesRead = 0; | ||
| 2618 | drwav_uint32 stringSizeWithNullTerminator = (drwav_uint32)chunkSize; | ||
| 2619 | |||
| 2620 | if (pParser->stage == drwav__metadata_parser_stage_count) { | ||
| 2621 | pParser->metadataCount += 1; | ||
| 2622 | drwav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); | ||
| 2623 | } else { | ||
| 2624 | drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; | ||
| 2625 | pMetadata->type = type; | ||
| 2626 | if (stringSizeWithNullTerminator > 0) { | ||
| 2627 | pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; | ||
| 2628 | pMetadata->data.infoText.pString = (char*)drwav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); | ||
| 2629 | DRWAV_ASSERT(pMetadata->data.infoText.pString != NULL); | ||
| 2630 | |||
| 2631 | bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); | ||
| 2632 | if (bytesRead == chunkSize) { | ||
| 2633 | pParser->metadataCursor += 1; | ||
| 2634 | } else { | ||
| 2635 | /* Failed to parse. */ | ||
| 2636 | } | ||
| 2637 | } else { | ||
| 2638 | pMetadata->data.infoText.stringLength = 0; | ||
| 2639 | pMetadata->data.infoText.pString = NULL; | ||
| 2640 | pParser->metadataCursor += 1; | ||
| 2641 | } | ||
| 2642 | } | ||
| 2643 | |||
| 2644 | return bytesRead; | ||
| 2645 | } | ||
| 2646 | |||
| 2647 | DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata_parser* pParser, const drwav_uint8* pChunkId, drwav_uint64 chunkSize, drwav_metadata_location location) | ||
| 2648 | { | ||
| 2649 | drwav_uint64 bytesRead = 0; | ||
| 2650 | |||
| 2651 | if (location == drwav_metadata_location_invalid) { | ||
| 2652 | return 0; | ||
| 2653 | } | ||
| 2654 | |||
| 2655 | if (drwav_fourcc_equal(pChunkId, "data") || drwav_fourcc_equal(pChunkId, "fmt ") || drwav_fourcc_equal(pChunkId, "fact")) { | ||
| 2656 | return 0; | ||
| 2657 | } | ||
| 2658 | |||
| 2659 | if (pParser->stage == drwav__metadata_parser_stage_count) { | ||
| 2660 | pParser->metadataCount += 1; | ||
| 2661 | drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); | ||
| 2662 | } else { | ||
| 2663 | drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; | ||
| 2664 | pMetadata->type = drwav_metadata_type_unknown; | ||
| 2665 | pMetadata->data.unknown.chunkLocation = location; | ||
| 2666 | pMetadata->data.unknown.id[0] = pChunkId[0]; | ||
| 2667 | pMetadata->data.unknown.id[1] = pChunkId[1]; | ||
| 2668 | pMetadata->data.unknown.id[2] = pChunkId[2]; | ||
| 2669 | pMetadata->data.unknown.id[3] = pChunkId[3]; | ||
| 2670 | pMetadata->data.unknown.dataSizeInBytes = (drwav_uint32)chunkSize; | ||
| 2671 | pMetadata->data.unknown.pData = (drwav_uint8 *)drwav__metadata_get_memory(pParser, (size_t)chunkSize, 1); | ||
| 2672 | DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); | ||
| 2673 | |||
| 2674 | bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); | ||
| 2675 | if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { | ||
| 2676 | pParser->metadataCursor += 1; | ||
| 2677 | } else { | ||
| 2678 | /* Failed to read. */ | ||
| 2679 | } | ||
| 2680 | } | ||
| 2681 | |||
| 2682 | return bytesRead; | ||
| 2683 | } | ||
| 2684 | |||
| 2685 | DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_metadata_type allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID) | ||
| 2686 | { | ||
| 2687 | return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID); | ||
| 2688 | } | ||
| 2689 | |||
| 2690 | DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata_type allowedMetadataTypes) | ||
| 2691 | { | ||
| 2692 | const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc; | ||
| 2693 | drwav_uint64 bytesRead = 0; | ||
| 2694 | |||
| 2695 | if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_smpl, "smpl")) { | ||
| 2696 | if (pChunkHeader->sizeInBytes >= DRWAV_SMPL_BYTES) { | ||
| 2697 | if (pParser->stage == drwav__metadata_parser_stage_count) { | ||
| 2698 | drwav_uint8 buffer[4]; | ||
| 2699 | size_t bytesJustRead; | ||
| 2700 | |||
| 2701 | if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) { | ||
| 2702 | return bytesRead; | ||
| 2703 | } | ||
| 2704 | bytesRead += 28; | ||
| 2705 | |||
| 2706 | bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); | ||
| 2707 | if (bytesJustRead == sizeof(buffer)) { | ||
| 2708 | drwav_uint32 loopCount = drwav_bytes_to_u32(buffer); | ||
| 2709 | drwav_uint64 calculatedLoopCount; | ||
| 2710 | |||
| 2711 | /* The loop count must be validated against the size of the chunk. */ | ||
| 2712 | calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES; | ||
| 2713 | if (calculatedLoopCount == loopCount) { | ||
| 2714 | bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); | ||
| 2715 | if (bytesJustRead == sizeof(buffer)) { | ||
| 2716 | drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer); | ||
| 2717 | |||
| 2718 | pParser->metadataCount += 1; | ||
| 2719 | drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT); | ||
| 2720 | drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); | ||
| 2721 | } | ||
| 2722 | } else { | ||
| 2723 | /* Loop count in header does not match the size of the chunk. */ | ||
| 2724 | } | ||
| 2725 | } | ||
| 2726 | } else { | ||
| 2727 | bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); | ||
| 2728 | if (bytesRead == pChunkHeader->sizeInBytes) { | ||
| 2729 | pParser->metadataCursor += 1; | ||
| 2730 | } else { | ||
| 2731 | /* Failed to parse. */ | ||
| 2732 | } | ||
| 2733 | } | ||
| 2734 | } else { | ||
| 2735 | /* Incorrectly formed chunk. */ | ||
| 2736 | } | ||
| 2737 | } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_inst, "inst")) { | ||
| 2738 | if (pChunkHeader->sizeInBytes == DRWAV_INST_BYTES) { | ||
| 2739 | if (pParser->stage == drwav__metadata_parser_stage_count) { | ||
| 2740 | pParser->metadataCount += 1; | ||
| 2741 | } else { | ||
| 2742 | bytesRead = drwav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); | ||
| 2743 | if (bytesRead == pChunkHeader->sizeInBytes) { | ||
| 2744 | pParser->metadataCursor += 1; | ||
| 2745 | } else { | ||
| 2746 | /* Failed to parse. */ | ||
| 2747 | } | ||
| 2748 | } | ||
| 2749 | } else { | ||
| 2750 | /* Incorrectly formed chunk. */ | ||
| 2751 | } | ||
| 2752 | } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_acid, "acid")) { | ||
| 2753 | if (pChunkHeader->sizeInBytes == DRWAV_ACID_BYTES) { | ||
| 2754 | if (pParser->stage == drwav__metadata_parser_stage_count) { | ||
| 2755 | pParser->metadataCount += 1; | ||
| 2756 | } else { | ||
| 2757 | bytesRead = drwav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); | ||
| 2758 | if (bytesRead == pChunkHeader->sizeInBytes) { | ||
| 2759 | pParser->metadataCursor += 1; | ||
| 2760 | } else { | ||
| 2761 | /* Failed to parse. */ | ||
| 2762 | } | ||
| 2763 | } | ||
| 2764 | } else { | ||
| 2765 | /* Incorrectly formed chunk. */ | ||
| 2766 | } | ||
| 2767 | } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_cue, "cue ")) { | ||
| 2768 | if (pChunkHeader->sizeInBytes >= DRWAV_CUE_BYTES) { | ||
| 2769 | if (pParser->stage == drwav__metadata_parser_stage_count) { | ||
| 2770 | size_t cueCount; | ||
| 2771 | |||
| 2772 | pParser->metadataCount += 1; | ||
| 2773 | cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES; | ||
| 2774 | drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT); | ||
| 2775 | } else { | ||
| 2776 | bytesRead = drwav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); | ||
| 2777 | if (bytesRead == pChunkHeader->sizeInBytes) { | ||
| 2778 | pParser->metadataCursor += 1; | ||
| 2779 | } else { | ||
| 2780 | /* Failed to parse. */ | ||
| 2781 | } | ||
| 2782 | } | ||
| 2783 | } else { | ||
| 2784 | /* Incorrectly formed chunk. */ | ||
| 2785 | } | ||
| 2786 | } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_bext, "bext")) { | ||
| 2787 | if (pChunkHeader->sizeInBytes >= DRWAV_BEXT_BYTES) { | ||
| 2788 | if (pParser->stage == drwav__metadata_parser_stage_count) { | ||
| 2789 | /* The description field is the largest one in a bext chunk, so that is the max size of this temporary buffer. */ | ||
| 2790 | char buffer[DRWAV_BEXT_DESCRIPTION_BYTES + 1]; | ||
| 2791 | size_t allocSizeNeeded = DRWAV_BEXT_UMID_BYTES; /* We know we will need SMPTE umid size. */ | ||
| 2792 | size_t bytesJustRead; | ||
| 2793 | |||
| 2794 | buffer[DRWAV_BEXT_DESCRIPTION_BYTES] = '\0'; | ||
| 2795 | bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_DESCRIPTION_BYTES, &bytesRead); | ||
| 2796 | if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) { | ||
| 2797 | return bytesRead; | ||
| 2798 | } | ||
| 2799 | allocSizeNeeded += drwav__strlen(buffer) + 1; | ||
| 2800 | |||
| 2801 | buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; | ||
| 2802 | bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); | ||
| 2803 | if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) { | ||
| 2804 | return bytesRead; | ||
| 2805 | } | ||
| 2806 | allocSizeNeeded += drwav__strlen(buffer) + 1; | ||
| 2807 | |||
| 2808 | buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; | ||
| 2809 | bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); | ||
| 2810 | if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) { | ||
| 2811 | return bytesRead; | ||
| 2812 | } | ||
| 2813 | allocSizeNeeded += drwav__strlen(buffer) + 1; | ||
| 2814 | allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; /* Coding history. */ | ||
| 2815 | |||
| 2816 | drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); | ||
| 2817 | |||
| 2818 | pParser->metadataCount += 1; | ||
| 2819 | } else { | ||
| 2820 | bytesRead = drwav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); | ||
| 2821 | if (bytesRead == pChunkHeader->sizeInBytes) { | ||
| 2822 | pParser->metadataCursor += 1; | ||
| 2823 | } else { | ||
| 2824 | /* Failed to parse. */ | ||
| 2825 | } | ||
| 2826 | } | ||
| 2827 | } else { | ||
| 2828 | /* Incorrectly formed chunk. */ | ||
| 2829 | } | ||
| 2830 | } else if (drwav_fourcc_equal(pChunkID, "LIST") || drwav_fourcc_equal(pChunkID, "list")) { | ||
| 2831 | drwav_metadata_location listType = drwav_metadata_location_invalid; | ||
| 2832 | while (bytesRead < pChunkHeader->sizeInBytes) { | ||
| 2833 | drwav_uint8 subchunkId[4]; | ||
| 2834 | drwav_uint8 subchunkSizeBuffer[4]; | ||
| 2835 | drwav_uint64 subchunkDataSize; | ||
| 2836 | drwav_uint64 subchunkBytesRead = 0; | ||
| 2837 | drwav_uint64 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); | ||
| 2838 | if (bytesJustRead != sizeof(subchunkId)) { | ||
| 2839 | break; | ||
| 2840 | } | ||
| 2841 | |||
| 2842 | /* | ||
| 2843 | The first thing in a list chunk should be "adtl" or "INFO". | ||
| 2844 | |||
| 2845 | - adtl means this list is a Associated Data List Chunk and will contain labels, notes | ||
| 2846 | or labelled cue regions. | ||
| 2847 | - INFO means this list is an Info List Chunk containing info text chunks such as IPRD | ||
| 2848 | which would specifies the album of this wav file. | ||
| 2849 | |||
| 2850 | No data follows the adtl or INFO id so we just make note of what type this list is and | ||
| 2851 | continue. | ||
| 2852 | */ | ||
| 2853 | if (drwav_fourcc_equal(subchunkId, "adtl")) { | ||
| 2854 | listType = drwav_metadata_location_inside_adtl_list; | ||
| 2855 | continue; | ||
| 2856 | } else if (drwav_fourcc_equal(subchunkId, "INFO")) { | ||
| 2857 | listType = drwav_metadata_location_inside_info_list; | ||
| 2858 | continue; | ||
| 2859 | } | ||
| 2860 | |||
| 2861 | bytesJustRead = drwav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); | ||
| 2862 | if (bytesJustRead != sizeof(subchunkSizeBuffer)) { | ||
| 2863 | break; | ||
| 2864 | } | ||
| 2865 | subchunkDataSize = drwav_bytes_to_u32(subchunkSizeBuffer); | ||
| 2866 | |||
| 2867 | if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_label, "labl") || drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_note, "note")) { | ||
| 2868 | if (subchunkDataSize >= DRWAV_LIST_LABEL_OR_NOTE_BYTES) { | ||
| 2869 | drwav_uint64 stringSizeWithNullTerm = subchunkDataSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; | ||
| 2870 | if (pParser->stage == drwav__metadata_parser_stage_count) { | ||
| 2871 | pParser->metadataCount += 1; | ||
| 2872 | drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); | ||
| 2873 | } else { | ||
| 2874 | subchunkBytesRead = drwav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, drwav_fourcc_equal(subchunkId, "labl") ? drwav_metadata_type_list_label : drwav_metadata_type_list_note); | ||
| 2875 | if (subchunkBytesRead == subchunkDataSize) { | ||
| 2876 | pParser->metadataCursor += 1; | ||
| 2877 | } else { | ||
| 2878 | /* Failed to parse. */ | ||
| 2879 | } | ||
| 2880 | } | ||
| 2881 | } else { | ||
| 2882 | /* Incorrectly formed chunk. */ | ||
| 2883 | } | ||
| 2884 | } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_labelled_cue_region, "ltxt")) { | ||
| 2885 | if (subchunkDataSize >= DRWAV_LIST_LABELLED_TEXT_BYTES) { | ||
| 2886 | drwav_uint64 stringSizeWithNullTerminator = subchunkDataSize - DRWAV_LIST_LABELLED_TEXT_BYTES; | ||
| 2887 | if (pParser->stage == drwav__metadata_parser_stage_count) { | ||
| 2888 | pParser->metadataCount += 1; | ||
| 2889 | drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); | ||
| 2890 | } else { | ||
| 2891 | subchunkBytesRead = drwav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); | ||
| 2892 | if (subchunkBytesRead == subchunkDataSize) { | ||
| 2893 | pParser->metadataCursor += 1; | ||
| 2894 | } else { | ||
| 2895 | /* Failed to parse. */ | ||
| 2896 | } | ||
| 2897 | } | ||
| 2898 | } else { | ||
| 2899 | /* Incorrectly formed chunk. */ | ||
| 2900 | } | ||
| 2901 | } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_software, "ISFT")) { | ||
| 2902 | subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_software); | ||
| 2903 | } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_copyright, "ICOP")) { | ||
| 2904 | subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_copyright); | ||
| 2905 | } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_title, "INAM")) { | ||
| 2906 | subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_title); | ||
| 2907 | } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_artist, "IART")) { | ||
| 2908 | subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_artist); | ||
| 2909 | } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_comment, "ICMT")) { | ||
| 2910 | subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_comment); | ||
| 2911 | } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_date, "ICRD")) { | ||
| 2912 | subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_date); | ||
| 2913 | } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_genre, "IGNR")) { | ||
| 2914 | subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_genre); | ||
| 2915 | } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_album, "IPRD")) { | ||
| 2916 | subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album); | ||
| 2917 | } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) { | ||
| 2918 | subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber); | ||
| 2919 | } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { | ||
| 2920 | subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); | ||
| 2921 | } | ||
| 2922 | |||
| 2923 | bytesRead += subchunkBytesRead; | ||
| 2924 | DRWAV_ASSERT(subchunkBytesRead <= subchunkDataSize); | ||
| 2925 | |||
| 2926 | if (subchunkBytesRead < subchunkDataSize) { | ||
| 2927 | drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; | ||
| 2928 | |||
| 2929 | if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) { | ||
| 2930 | break; | ||
| 2931 | } | ||
| 2932 | bytesRead += bytesToSeek; | ||
| 2933 | } | ||
| 2934 | |||
| 2935 | if ((subchunkDataSize % 2) == 1) { | ||
| 2936 | if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) { | ||
| 2937 | break; | ||
| 2938 | } | ||
| 2939 | bytesRead += 1; | ||
| 2940 | } | ||
| 2941 | } | ||
| 2942 | } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { | ||
| 2943 | bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level); | ||
| 2944 | } | ||
| 2945 | |||
| 2946 | return bytesRead; | ||
| 2947 | } | ||
| 2948 | |||
| 2949 | |||
| 2950 | DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav) | ||
| 2951 | { | ||
| 2952 | drwav_uint32 bytesPerFrame; | ||
| 2953 | |||
| 2954 | /* | ||
| 2955 | The bytes per frame is a bit ambiguous. It can be either be based on the bits per sample, or the block align. The way I'm doing it here | ||
| 2956 | is that if the bits per sample is a multiple of 8, use floor(bitsPerSample*channels/8), otherwise fall back to the block align. | ||
| 2957 | */ | ||
| 2958 | if ((pWav->bitsPerSample & 0x7) == 0) { | ||
| 2959 | /* Bits per sample is a multiple of 8. */ | ||
| 2960 | bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; | ||
| 2961 | } else { | ||
| 2962 | bytesPerFrame = pWav->fmt.blockAlign; | ||
| 2963 | } | ||
| 2964 | |||
| 2965 | /* Validation for known formats. a-law and mu-law should be 1 byte per channel. If it's not, it's not decodable. */ | ||
| 2966 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { | ||
| 2967 | if (bytesPerFrame != pWav->fmt.channels) { | ||
| 2968 | return 0; /* Invalid file. */ | ||
| 2969 | } | ||
| 2970 | } | ||
| 2971 | |||
| 2972 | return bytesPerFrame; | ||
| 2973 | } | ||
| 2974 | |||
| 2975 | DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT) | ||
| 2976 | { | ||
| 2977 | if (pFMT == NULL) { | ||
| 2978 | return 0; | ||
| 2979 | } | ||
| 2980 | |||
| 2981 | if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) { | ||
| 2982 | return pFMT->formatTag; | ||
| 2983 | } else { | ||
| 2984 | return drwav_bytes_to_u16(pFMT->subFormat); /* Only the first two bytes are required. */ | ||
| 2985 | } | ||
| 2986 | } | ||
| 2987 | |||
| 2988 | DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 2989 | { | ||
| 2990 | if (pWav == NULL || onRead == NULL || onSeek == NULL) { | ||
| 2991 | return DRWAV_FALSE; | ||
| 2992 | } | ||
| 2993 | |||
| 2994 | DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); | ||
| 2995 | pWav->onRead = onRead; | ||
| 2996 | pWav->onSeek = onSeek; | ||
| 2997 | pWav->pUserData = pReadSeekUserData; | ||
| 2998 | pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); | ||
| 2999 | |||
| 3000 | if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { | ||
| 3001 | return DRWAV_FALSE; /* Invalid allocation callbacks. */ | ||
| 3002 | } | ||
| 3003 | |||
| 3004 | return DRWAV_TRUE; | ||
| 3005 | } | ||
| 3006 | |||
| 3007 | DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) | ||
| 3008 | { | ||
| 3009 | /* This function assumes drwav_preinit() has been called beforehand. */ | ||
| 3010 | drwav_result result; | ||
| 3011 | drwav_uint64 cursor; /* <-- Keeps track of the byte position so we can seek to specific locations. */ | ||
| 3012 | drwav_bool32 sequential; | ||
| 3013 | drwav_uint8 riff[4]; | ||
| 3014 | drwav_fmt fmt; | ||
| 3015 | unsigned short translatedFormatTag; | ||
| 3016 | drwav_uint64 dataChunkSize = 0; /* <-- Important! Don't explicitly set this to 0 anywhere else. Calculation of the size of the data chunk is performed in different paths depending on the container. */ | ||
| 3017 | drwav_uint64 sampleCountFromFactChunk = 0; /* Same as dataChunkSize - make sure this is the only place this is initialized to 0. */ | ||
| 3018 | drwav_uint64 metadataStartPos; | ||
| 3019 | drwav__metadata_parser metadataParser; | ||
| 3020 | drwav_bool8 isProcessingMetadata = DRWAV_FALSE; | ||
| 3021 | drwav_bool8 foundChunk_fmt = DRWAV_FALSE; | ||
| 3022 | drwav_bool8 foundChunk_data = DRWAV_FALSE; | ||
| 3023 | drwav_bool8 isAIFCFormType = DRWAV_FALSE; /* Only used with AIFF. */ | ||
| 3024 | drwav_uint64 aiffFrameCount = 0; | ||
| 3025 | |||
| 3026 | cursor = 0; | ||
| 3027 | sequential = (flags & DRWAV_SEQUENTIAL) != 0; | ||
| 3028 | DRWAV_ZERO_OBJECT(&fmt); | ||
| 3029 | |||
| 3030 | /* The first 4 bytes should be the RIFF identifier. */ | ||
| 3031 | if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { | ||
| 3032 | return DRWAV_FALSE; | ||
| 3033 | } | ||
| 3034 | |||
| 3035 | /* | ||
| 3036 | The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for | ||
| 3037 | w64 it will start with "riff". | ||
| 3038 | */ | ||
| 3039 | if (drwav_fourcc_equal(riff, "RIFF")) { | ||
| 3040 | pWav->container = drwav_container_riff; | ||
| 3041 | } else if (drwav_fourcc_equal(riff, "RIFX")) { | ||
| 3042 | pWav->container = drwav_container_rifx; | ||
| 3043 | } else if (drwav_fourcc_equal(riff, "riff")) { | ||
| 3044 | int i; | ||
| 3045 | drwav_uint8 riff2[12]; | ||
| 3046 | |||
| 3047 | pWav->container = drwav_container_w64; | ||
| 3048 | |||
| 3049 | /* Check the rest of the GUID for validity. */ | ||
| 3050 | if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { | ||
| 3051 | return DRWAV_FALSE; | ||
| 3052 | } | ||
| 3053 | |||
| 3054 | for (i = 0; i < 12; ++i) { | ||
| 3055 | if (riff2[i] != drwavGUID_W64_RIFF[i+4]) { | ||
| 3056 | return DRWAV_FALSE; | ||
| 3057 | } | ||
| 3058 | } | ||
| 3059 | } else if (drwav_fourcc_equal(riff, "RF64")) { | ||
| 3060 | pWav->container = drwav_container_rf64; | ||
| 3061 | } else if (drwav_fourcc_equal(riff, "FORM")) { | ||
| 3062 | pWav->container = drwav_container_aiff; | ||
| 3063 | } else { | ||
| 3064 | return DRWAV_FALSE; /* Unknown or unsupported container. */ | ||
| 3065 | } | ||
| 3066 | |||
| 3067 | |||
| 3068 | if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) { | ||
| 3069 | drwav_uint8 chunkSizeBytes[4]; | ||
| 3070 | drwav_uint8 wave[4]; | ||
| 3071 | |||
| 3072 | if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { | ||
| 3073 | return DRWAV_FALSE; | ||
| 3074 | } | ||
| 3075 | |||
| 3076 | if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx) { | ||
| 3077 | if (drwav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) { | ||
| 3078 | /* | ||
| 3079 | I've had a report of a WAV file failing to load when the size of the WAVE chunk is not encoded | ||
| 3080 | and is instead just set to 0. I'm going to relax the validation here to allow these files to | ||
| 3081 | load. Considering the chunk size isn't actually used this should be safe. With this change my | ||
| 3082 | test suite still passes. | ||
| 3083 | */ | ||
| 3084 | /*return DRWAV_FALSE;*/ /* Chunk size should always be at least 36 bytes. */ | ||
| 3085 | } | ||
| 3086 | } else if (pWav->container == drwav_container_rf64) { | ||
| 3087 | if (drwav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) { | ||
| 3088 | return DRWAV_FALSE; /* Chunk size should always be set to -1/0xFFFFFFFF for RF64. The actual size is retrieved later. */ | ||
| 3089 | } | ||
| 3090 | } else { | ||
| 3091 | return DRWAV_FALSE; /* Should never hit this. */ | ||
| 3092 | } | ||
| 3093 | |||
| 3094 | if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { | ||
| 3095 | return DRWAV_FALSE; | ||
| 3096 | } | ||
| 3097 | |||
| 3098 | if (!drwav_fourcc_equal(wave, "WAVE")) { | ||
| 3099 | return DRWAV_FALSE; /* Expecting "WAVE". */ | ||
| 3100 | } | ||
| 3101 | } else if (pWav->container == drwav_container_w64) { | ||
| 3102 | drwav_uint8 chunkSizeBytes[8]; | ||
| 3103 | drwav_uint8 wave[16]; | ||
| 3104 | |||
| 3105 | if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { | ||
| 3106 | return DRWAV_FALSE; | ||
| 3107 | } | ||
| 3108 | |||
| 3109 | if (drwav_bytes_to_u64(chunkSizeBytes) < 80) { | ||
| 3110 | return DRWAV_FALSE; | ||
| 3111 | } | ||
| 3112 | |||
| 3113 | if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { | ||
| 3114 | return DRWAV_FALSE; | ||
| 3115 | } | ||
| 3116 | |||
| 3117 | if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) { | ||
| 3118 | return DRWAV_FALSE; | ||
| 3119 | } | ||
| 3120 | } else if (pWav->container == drwav_container_aiff) { | ||
| 3121 | drwav_uint8 chunkSizeBytes[4]; | ||
| 3122 | drwav_uint8 aiff[4]; | ||
| 3123 | |||
| 3124 | if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { | ||
| 3125 | return DRWAV_FALSE; | ||
| 3126 | } | ||
| 3127 | |||
| 3128 | if (drwav_bytes_to_u32_be(chunkSizeBytes) < 18) { | ||
| 3129 | return DRWAV_FALSE; | ||
| 3130 | } | ||
| 3131 | |||
| 3132 | if (drwav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) { | ||
| 3133 | return DRWAV_FALSE; | ||
| 3134 | } | ||
| 3135 | |||
| 3136 | if (drwav_fourcc_equal(aiff, "AIFF")) { | ||
| 3137 | isAIFCFormType = DRWAV_FALSE; | ||
| 3138 | } else if (drwav_fourcc_equal(aiff, "AIFC")) { | ||
| 3139 | isAIFCFormType = DRWAV_TRUE; | ||
| 3140 | } else { | ||
| 3141 | return DRWAV_FALSE; /* Expecting "AIFF" or "AIFC". */ | ||
| 3142 | } | ||
| 3143 | } else { | ||
| 3144 | return DRWAV_FALSE; | ||
| 3145 | } | ||
| 3146 | |||
| 3147 | |||
| 3148 | /* For RF64, the "ds64" chunk must come next, before the "fmt " chunk. */ | ||
| 3149 | if (pWav->container == drwav_container_rf64) { | ||
| 3150 | drwav_uint8 sizeBytes[8]; | ||
| 3151 | drwav_uint64 bytesRemainingInChunk; | ||
| 3152 | drwav_chunk_header header; | ||
| 3153 | result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); | ||
| 3154 | if (result != DRWAV_SUCCESS) { | ||
| 3155 | return DRWAV_FALSE; | ||
| 3156 | } | ||
| 3157 | |||
| 3158 | if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) { | ||
| 3159 | return DRWAV_FALSE; /* Expecting "ds64". */ | ||
| 3160 | } | ||
| 3161 | |||
| 3162 | bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; | ||
| 3163 | |||
| 3164 | /* We don't care about the size of the RIFF chunk - skip it. */ | ||
| 3165 | if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { | ||
| 3166 | return DRWAV_FALSE; | ||
| 3167 | } | ||
| 3168 | bytesRemainingInChunk -= 8; | ||
| 3169 | cursor += 8; | ||
| 3170 | |||
| 3171 | |||
| 3172 | /* Next 8 bytes is the size of the "data" chunk. */ | ||
| 3173 | if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { | ||
| 3174 | return DRWAV_FALSE; | ||
| 3175 | } | ||
| 3176 | bytesRemainingInChunk -= 8; | ||
| 3177 | dataChunkSize = drwav_bytes_to_u64(sizeBytes); | ||
| 3178 | |||
| 3179 | |||
| 3180 | /* Next 8 bytes is the same count which we would usually derived from the FACT chunk if it was available. */ | ||
| 3181 | if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { | ||
| 3182 | return DRWAV_FALSE; | ||
| 3183 | } | ||
| 3184 | bytesRemainingInChunk -= 8; | ||
| 3185 | sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes); | ||
| 3186 | |||
| 3187 | |||
| 3188 | /* Skip over everything else. */ | ||
| 3189 | if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { | ||
| 3190 | return DRWAV_FALSE; | ||
| 3191 | } | ||
| 3192 | cursor += bytesRemainingInChunk; | ||
| 3193 | } | ||
| 3194 | |||
| 3195 | |||
| 3196 | metadataStartPos = cursor; | ||
| 3197 | |||
| 3198 | /* | ||
| 3199 | Whether or not we are processing metadata controls how we load. We can load more efficiently when | ||
| 3200 | metadata is not being processed, but we also cannot process metadata for Wave64 because I have not | ||
| 3201 | been able to test it. If someone is able to test this and provide a patch I'm happy to enable it. | ||
| 3202 | |||
| 3203 | Seqential mode cannot support metadata because it involves seeking backwards. | ||
| 3204 | */ | ||
| 3205 | isProcessingMetadata = !sequential && ((flags & DRWAV_WITH_METADATA) != 0); | ||
| 3206 | |||
| 3207 | /* Don't allow processing of metadata with untested containers. */ | ||
| 3208 | if (pWav->container != drwav_container_riff && pWav->container != drwav_container_rf64) { | ||
| 3209 | isProcessingMetadata = DRWAV_FALSE; | ||
| 3210 | } | ||
| 3211 | |||
| 3212 | DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); | ||
| 3213 | if (isProcessingMetadata) { | ||
| 3214 | metadataParser.onRead = pWav->onRead; | ||
| 3215 | metadataParser.onSeek = pWav->onSeek; | ||
| 3216 | metadataParser.pReadSeekUserData = pWav->pUserData; | ||
| 3217 | metadataParser.stage = drwav__metadata_parser_stage_count; | ||
| 3218 | } | ||
| 3219 | |||
| 3220 | |||
| 3221 | /* | ||
| 3222 | From here on out, chunks might be in any order. In order to robustly handle metadata we'll need | ||
| 3223 | to loop through every chunk and handle them as we find them. In sequential mode we need to get | ||
| 3224 | out of the loop as soon as we find the data chunk because we won't be able to seek back. | ||
| 3225 | */ | ||
| 3226 | for (;;) { /* For each chunk... */ | ||
| 3227 | drwav_chunk_header header; | ||
| 3228 | drwav_uint64 chunkSize; | ||
| 3229 | |||
| 3230 | result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); | ||
| 3231 | if (result != DRWAV_SUCCESS) { | ||
| 3232 | break; | ||
| 3233 | } | ||
| 3234 | |||
| 3235 | chunkSize = header.sizeInBytes; | ||
| 3236 | |||
| 3237 | |||
| 3238 | /* | ||
| 3239 | Always tell the caller about this chunk. We cannot do this in sequential mode because the | ||
| 3240 | callback is allowed to read from the file, in which case we'll need to rewind. | ||
| 3241 | */ | ||
| 3242 | if (!sequential && onChunk != NULL) { | ||
| 3243 | drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); | ||
| 3244 | |||
| 3245 | /* | ||
| 3246 | dr_wav may need to read the contents of the chunk, so we now need to seek back to the position before | ||
| 3247 | we called the callback. | ||
| 3248 | */ | ||
| 3249 | if (callbackBytesRead > 0) { | ||
| 3250 | if (drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == DRWAV_FALSE) { | ||
| 3251 | return DRWAV_FALSE; | ||
| 3252 | } | ||
| 3253 | } | ||
| 3254 | } | ||
| 3255 | |||
| 3256 | |||
| 3257 | /* Explicitly handle known chunks first. */ | ||
| 3258 | |||
| 3259 | /* "fmt " */ | ||
| 3260 | if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "fmt ")) || | ||
| 3261 | ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT))) { | ||
| 3262 | drwav_uint8 fmtData[16]; | ||
| 3263 | |||
| 3264 | foundChunk_fmt = DRWAV_TRUE; | ||
| 3265 | |||
| 3266 | if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) { | ||
| 3267 | return DRWAV_FALSE; | ||
| 3268 | } | ||
| 3269 | cursor += sizeof(fmtData); | ||
| 3270 | |||
| 3271 | fmt.formatTag = drwav_bytes_to_u16_ex(fmtData + 0, pWav->container); | ||
| 3272 | fmt.channels = drwav_bytes_to_u16_ex(fmtData + 2, pWav->container); | ||
| 3273 | fmt.sampleRate = drwav_bytes_to_u32_ex(fmtData + 4, pWav->container); | ||
| 3274 | fmt.avgBytesPerSec = drwav_bytes_to_u32_ex(fmtData + 8, pWav->container); | ||
| 3275 | fmt.blockAlign = drwav_bytes_to_u16_ex(fmtData + 12, pWav->container); | ||
| 3276 | fmt.bitsPerSample = drwav_bytes_to_u16_ex(fmtData + 14, pWav->container); | ||
| 3277 | |||
| 3278 | fmt.extendedSize = 0; | ||
| 3279 | fmt.validBitsPerSample = 0; | ||
| 3280 | fmt.channelMask = 0; | ||
| 3281 | DRWAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat)); | ||
| 3282 | |||
| 3283 | if (header.sizeInBytes > 16) { | ||
| 3284 | drwav_uint8 fmt_cbSize[2]; | ||
| 3285 | int bytesReadSoFar = 0; | ||
| 3286 | |||
| 3287 | if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { | ||
| 3288 | return DRWAV_FALSE; /* Expecting more data. */ | ||
| 3289 | } | ||
| 3290 | cursor += sizeof(fmt_cbSize); | ||
| 3291 | |||
| 3292 | bytesReadSoFar = 18; | ||
| 3293 | |||
| 3294 | fmt.extendedSize = drwav_bytes_to_u16_ex(fmt_cbSize, pWav->container); | ||
| 3295 | if (fmt.extendedSize > 0) { | ||
| 3296 | /* Simple validation. */ | ||
| 3297 | if (fmt.formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { | ||
| 3298 | if (fmt.extendedSize != 22) { | ||
| 3299 | return DRWAV_FALSE; | ||
| 3300 | } | ||
| 3301 | } | ||
| 3302 | |||
| 3303 | if (fmt.formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { | ||
| 3304 | drwav_uint8 fmtext[22]; | ||
| 3305 | |||
| 3306 | if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) { | ||
| 3307 | return DRWAV_FALSE; /* Expecting more data. */ | ||
| 3308 | } | ||
| 3309 | |||
| 3310 | fmt.validBitsPerSample = drwav_bytes_to_u16_ex(fmtext + 0, pWav->container); | ||
| 3311 | fmt.channelMask = drwav_bytes_to_u32_ex(fmtext + 2, pWav->container); | ||
| 3312 | drwav_bytes_to_guid(fmtext + 6, fmt.subFormat); | ||
| 3313 | } else { | ||
| 3314 | if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, drwav_seek_origin_current) == DRWAV_FALSE) { | ||
| 3315 | return DRWAV_FALSE; | ||
| 3316 | } | ||
| 3317 | } | ||
| 3318 | cursor += fmt.extendedSize; | ||
| 3319 | |||
| 3320 | bytesReadSoFar += fmt.extendedSize; | ||
| 3321 | } | ||
| 3322 | |||
| 3323 | /* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */ | ||
| 3324 | if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current) == DRWAV_FALSE) { | ||
| 3325 | return DRWAV_FALSE; | ||
| 3326 | } | ||
| 3327 | cursor += (header.sizeInBytes - bytesReadSoFar); | ||
| 3328 | } | ||
| 3329 | |||
| 3330 | if (header.paddingSize > 0) { | ||
| 3331 | if (drwav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == DRWAV_FALSE) { | ||
| 3332 | break; | ||
| 3333 | } | ||
| 3334 | cursor += header.paddingSize; | ||
| 3335 | } | ||
| 3336 | |||
| 3337 | /* Go to the next chunk. Don't include this chunk in metadata. */ | ||
| 3338 | continue; | ||
| 3339 | } | ||
| 3340 | |||
| 3341 | /* "data" */ | ||
| 3342 | if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "data")) || | ||
| 3343 | ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA))) { | ||
| 3344 | foundChunk_data = DRWAV_TRUE; | ||
| 3345 | |||
| 3346 | pWav->dataChunkDataPos = cursor; | ||
| 3347 | |||
| 3348 | if (pWav->container != drwav_container_rf64) { /* The data chunk size for RF64 will always be set to 0xFFFFFFFF here. It was set to it's true value earlier. */ | ||
| 3349 | dataChunkSize = chunkSize; | ||
| 3350 | } | ||
| 3351 | |||
| 3352 | /* If we're running in sequential mode, or we're not reading metadata, we have enough now that we can get out of the loop. */ | ||
| 3353 | if (sequential || !isProcessingMetadata) { | ||
| 3354 | break; /* No need to keep reading beyond the data chunk. */ | ||
| 3355 | } else { | ||
| 3356 | chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */ | ||
| 3357 | if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) { | ||
| 3358 | break; | ||
| 3359 | } | ||
| 3360 | cursor += chunkSize; | ||
| 3361 | |||
| 3362 | continue; /* There may be some more metadata to read. */ | ||
| 3363 | } | ||
| 3364 | } | ||
| 3365 | |||
| 3366 | /* "fact". This is optional. Can use this to get the sample count which is useful for compressed formats. For RF64 we retrieved the sample count from the ds64 chunk earlier. */ | ||
| 3367 | if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "fact")) || | ||
| 3368 | ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT))) { | ||
| 3369 | if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx) { | ||
| 3370 | drwav_uint8 sampleCount[4]; | ||
| 3371 | if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { | ||
| 3372 | return DRWAV_FALSE; | ||
| 3373 | } | ||
| 3374 | |||
| 3375 | chunkSize -= 4; | ||
| 3376 | |||
| 3377 | /* | ||
| 3378 | The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this | ||
| 3379 | for Microsoft ADPCM formats. | ||
| 3380 | */ | ||
| 3381 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { | ||
| 3382 | sampleCountFromFactChunk = drwav_bytes_to_u32_ex(sampleCount, pWav->container); | ||
| 3383 | } else { | ||
| 3384 | sampleCountFromFactChunk = 0; | ||
| 3385 | } | ||
| 3386 | } else if (pWav->container == drwav_container_w64) { | ||
| 3387 | if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { | ||
| 3388 | return DRWAV_FALSE; | ||
| 3389 | } | ||
| 3390 | |||
| 3391 | chunkSize -= 8; | ||
| 3392 | } else if (pWav->container == drwav_container_rf64) { | ||
| 3393 | /* We retrieved the sample count from the ds64 chunk earlier so no need to do that here. */ | ||
| 3394 | } | ||
| 3395 | |||
| 3396 | /* Seek to the next chunk in preparation for the next iteration. */ | ||
| 3397 | chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */ | ||
| 3398 | if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) { | ||
| 3399 | break; | ||
| 3400 | } | ||
| 3401 | cursor += chunkSize; | ||
| 3402 | |||
| 3403 | continue; | ||
| 3404 | } | ||
| 3405 | |||
| 3406 | |||
| 3407 | /* "COMM". AIFF/AIFC only. */ | ||
| 3408 | if (pWav->container == drwav_container_aiff && drwav_fourcc_equal(header.id.fourcc, "COMM")) { | ||
| 3409 | drwav_uint8 commData[24]; | ||
| 3410 | drwav_uint32 commDataBytesToRead; | ||
| 3411 | drwav_uint16 channels; | ||
| 3412 | drwav_uint32 frameCount; | ||
| 3413 | drwav_uint16 sampleSizeInBits; | ||
| 3414 | drwav_int64 sampleRate; | ||
| 3415 | drwav_uint16 compressionFormat; | ||
| 3416 | |||
| 3417 | foundChunk_fmt = DRWAV_TRUE; | ||
| 3418 | |||
| 3419 | if (isAIFCFormType) { | ||
| 3420 | commDataBytesToRead = 24; | ||
| 3421 | if (header.sizeInBytes < commDataBytesToRead) { | ||
| 3422 | return DRWAV_FALSE; /* Invalid COMM chunk. */ | ||
| 3423 | } | ||
| 3424 | } else { | ||
| 3425 | commDataBytesToRead = 18; | ||
| 3426 | if (header.sizeInBytes != commDataBytesToRead) { | ||
| 3427 | return DRWAV_FALSE; /* INVALID COMM chunk. */ | ||
| 3428 | } | ||
| 3429 | } | ||
| 3430 | |||
| 3431 | if (drwav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) { | ||
| 3432 | return DRWAV_FALSE; | ||
| 3433 | } | ||
| 3434 | |||
| 3435 | |||
| 3436 | channels = drwav_bytes_to_u16_ex (commData + 0, pWav->container); | ||
| 3437 | frameCount = drwav_bytes_to_u32_ex (commData + 2, pWav->container); | ||
| 3438 | sampleSizeInBits = drwav_bytes_to_u16_ex (commData + 6, pWav->container); | ||
| 3439 | sampleRate = drwav_aiff_extented_to_s64(commData + 8); | ||
| 3440 | |||
| 3441 | if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) { | ||
| 3442 | return DRWAV_FALSE; /* Invalid sample rate. */ | ||
| 3443 | } | ||
| 3444 | |||
| 3445 | if (isAIFCFormType) { | ||
| 3446 | const drwav_uint8* type = commData + 18; | ||
| 3447 | |||
| 3448 | if (drwav_fourcc_equal(type, "NONE")) { | ||
| 3449 | compressionFormat = DR_WAVE_FORMAT_PCM; /* PCM, big-endian. */ | ||
| 3450 | } else if (drwav_fourcc_equal(type, "raw ")) { | ||
| 3451 | compressionFormat = DR_WAVE_FORMAT_PCM; | ||
| 3452 | |||
| 3453 | /* In my testing, it looks like when the "raw " compression type is used, 8-bit samples should be considered unsigned. */ | ||
| 3454 | if (sampleSizeInBits == 8) { | ||
| 3455 | pWav->aiff.isUnsigned = DRWAV_TRUE; | ||
| 3456 | } | ||
| 3457 | } else if (drwav_fourcc_equal(type, "sowt")) { | ||
| 3458 | compressionFormat = DR_WAVE_FORMAT_PCM; /* PCM, little-endian. */ | ||
| 3459 | pWav->aiff.isLE = DRWAV_TRUE; | ||
| 3460 | } else if (drwav_fourcc_equal(type, "fl32") || drwav_fourcc_equal(type, "fl64") || drwav_fourcc_equal(type, "FL32") || drwav_fourcc_equal(type, "FL64")) { | ||
| 3461 | compressionFormat = DR_WAVE_FORMAT_IEEE_FLOAT; | ||
| 3462 | } else if (drwav_fourcc_equal(type, "alaw") || drwav_fourcc_equal(type, "ALAW")) { | ||
| 3463 | compressionFormat = DR_WAVE_FORMAT_ALAW; | ||
| 3464 | } else if (drwav_fourcc_equal(type, "ulaw") || drwav_fourcc_equal(type, "ULAW")) { | ||
| 3465 | compressionFormat = DR_WAVE_FORMAT_MULAW; | ||
| 3466 | } else if (drwav_fourcc_equal(type, "ima4")) { | ||
| 3467 | compressionFormat = DR_WAVE_FORMAT_DVI_ADPCM; | ||
| 3468 | sampleSizeInBits = 4; | ||
| 3469 | |||
| 3470 | /* | ||
| 3471 | I haven't been able to figure out how to get correct decoding for IMA ADPCM. Until this is figured out | ||
| 3472 | we'll need to abort when we encounter such an encoding. Advice welcome! | ||
| 3473 | */ | ||
| 3474 | return DRWAV_FALSE; | ||
| 3475 | } else { | ||
| 3476 | return DRWAV_FALSE; /* Unknown or unsupported compression format. Need to abort. */ | ||
| 3477 | } | ||
| 3478 | } else { | ||
| 3479 | compressionFormat = DR_WAVE_FORMAT_PCM; /* It's a standard AIFF form which is always compressed. */ | ||
| 3480 | } | ||
| 3481 | |||
| 3482 | /* With AIFF we want to use the explicitly defined frame count rather than deriving it from the size of the chunk. */ | ||
| 3483 | aiffFrameCount = frameCount; | ||
| 3484 | |||
| 3485 | /* We should now have enough information to fill out our fmt structure. */ | ||
| 3486 | fmt.formatTag = compressionFormat; | ||
| 3487 | fmt.channels = channels; | ||
| 3488 | fmt.sampleRate = (drwav_uint32)sampleRate; | ||
| 3489 | fmt.bitsPerSample = sampleSizeInBits; | ||
| 3490 | fmt.blockAlign = (drwav_uint16)(fmt.channels * fmt.bitsPerSample / 8); | ||
| 3491 | fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate; | ||
| 3492 | |||
| 3493 | if (fmt.blockAlign == 0 && compressionFormat == DR_WAVE_FORMAT_DVI_ADPCM) { | ||
| 3494 | fmt.blockAlign = 34 * fmt.channels; | ||
| 3495 | } | ||
| 3496 | |||
| 3497 | /* | ||
| 3498 | Weird one. I've seen some alaw and ulaw encoded files that for some reason set the bits per sample to 16 when | ||
| 3499 | it should be 8. To get this working I need to explicitly check for this and change it. | ||
| 3500 | */ | ||
| 3501 | if (compressionFormat == DR_WAVE_FORMAT_ALAW || compressionFormat == DR_WAVE_FORMAT_MULAW) { | ||
| 3502 | if (fmt.bitsPerSample > 8) { | ||
| 3503 | fmt.bitsPerSample = 8; | ||
| 3504 | fmt.blockAlign = fmt.channels; | ||
| 3505 | } | ||
| 3506 | } | ||
| 3507 | |||
| 3508 | /* In AIFF, samples are padded to 8 byte boundaries. We need to round up our bits per sample here. */ | ||
| 3509 | fmt.bitsPerSample += (fmt.bitsPerSample & 7); | ||
| 3510 | |||
| 3511 | |||
| 3512 | /* If the form type is AIFC there will be some additional data in the chunk. We need to seek past it. */ | ||
| 3513 | if (isAIFCFormType) { | ||
| 3514 | if (drwav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == DRWAV_FALSE) { | ||
| 3515 | return DRWAV_FALSE; | ||
| 3516 | } | ||
| 3517 | cursor += (chunkSize - commDataBytesToRead); | ||
| 3518 | } | ||
| 3519 | |||
| 3520 | /* Don't fall through or else we'll end up treating this chunk as metadata which is incorrect. */ | ||
| 3521 | continue; | ||
| 3522 | } | ||
| 3523 | |||
| 3524 | |||
| 3525 | /* "SSND". AIFF/AIFC only. This is the AIFF equivalent of the "data" chunk. */ | ||
| 3526 | if (pWav->container == drwav_container_aiff && drwav_fourcc_equal(header.id.fourcc, "SSND")) { | ||
| 3527 | drwav_uint8 offsetAndBlockSizeData[8]; | ||
| 3528 | drwav_uint32 offset; | ||
| 3529 | |||
| 3530 | foundChunk_data = DRWAV_TRUE; | ||
| 3531 | |||
| 3532 | if (drwav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) { | ||
| 3533 | return DRWAV_FALSE; | ||
| 3534 | } | ||
| 3535 | |||
| 3536 | /* We need to seek forward by the offset. */ | ||
| 3537 | offset = drwav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); | ||
| 3538 | if (drwav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == DRWAV_FALSE) { | ||
| 3539 | return DRWAV_FALSE; | ||
| 3540 | } | ||
| 3541 | cursor += offset; | ||
| 3542 | |||
| 3543 | pWav->dataChunkDataPos = cursor; | ||
| 3544 | dataChunkSize = chunkSize; | ||
| 3545 | |||
| 3546 | /* If we're running in sequential mode, or we're not reading metadata, we have enough now that we can get out of the loop. */ | ||
| 3547 | if (sequential || !isProcessingMetadata) { | ||
| 3548 | break; /* No need to keep reading beyond the data chunk. */ | ||
| 3549 | } else { | ||
| 3550 | if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) { | ||
| 3551 | break; | ||
| 3552 | } | ||
| 3553 | cursor += chunkSize; | ||
| 3554 | |||
| 3555 | continue; /* There may be some more metadata to read. */ | ||
| 3556 | } | ||
| 3557 | } | ||
| 3558 | |||
| 3559 | |||
| 3560 | |||
| 3561 | /* Getting here means it's not a chunk that we care about internally, but might need to be handled as metadata by the caller. */ | ||
| 3562 | if (isProcessingMetadata) { | ||
| 3563 | drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown); | ||
| 3564 | |||
| 3565 | /* Go back to the start of the chunk so we can normalize the position of the cursor. */ | ||
| 3566 | if (drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == DRWAV_FALSE) { | ||
| 3567 | break; /* Failed to seek. Can't reliable read the remaining chunks. Get out. */ | ||
| 3568 | } | ||
| 3569 | } | ||
| 3570 | |||
| 3571 | |||
| 3572 | /* Make sure we skip past the content of this chunk before we go to the next one. */ | ||
| 3573 | chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */ | ||
| 3574 | if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) { | ||
| 3575 | break; | ||
| 3576 | } | ||
| 3577 | cursor += chunkSize; | ||
| 3578 | } | ||
| 3579 | |||
| 3580 | /* There's some mandatory chunks that must exist. If they were not found in the iteration above we must abort. */ | ||
| 3581 | if (!foundChunk_fmt || !foundChunk_data) { | ||
| 3582 | return DRWAV_FALSE; | ||
| 3583 | } | ||
| 3584 | |||
| 3585 | /* Basic validation. */ | ||
| 3586 | if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE ) || | ||
| 3587 | (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS ) || | ||
| 3588 | (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) || | ||
| 3589 | fmt.blockAlign == 0) { | ||
| 3590 | return DRWAV_FALSE; /* Probably an invalid WAV file. */ | ||
| 3591 | } | ||
| 3592 | |||
| 3593 | /* Translate the internal format. */ | ||
| 3594 | translatedFormatTag = fmt.formatTag; | ||
| 3595 | if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) { | ||
| 3596 | translatedFormatTag = drwav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container); | ||
| 3597 | } | ||
| 3598 | |||
| 3599 | /* We may have moved passed the data chunk. If so we need to move back. If running in sequential mode we can assume we are already sitting on the data chunk. */ | ||
| 3600 | if (!sequential) { | ||
| 3601 | if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { | ||
| 3602 | return DRWAV_FALSE; | ||
| 3603 | } | ||
| 3604 | cursor = pWav->dataChunkDataPos; | ||
| 3605 | } | ||
| 3606 | |||
| 3607 | |||
| 3608 | /* | ||
| 3609 | At this point we should have done the initial parsing of each of our chunks, but we now need to | ||
| 3610 | do a second pass to extract the actual contents of the metadata (the first pass just calculated | ||
| 3611 | the length of the memory allocation). | ||
| 3612 | |||
| 3613 | We only do this if we've actually got metadata to parse. | ||
| 3614 | */ | ||
| 3615 | if (isProcessingMetadata && metadataParser.metadataCount > 0) { | ||
| 3616 | if (drwav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == DRWAV_FALSE) { | ||
| 3617 | return DRWAV_FALSE; | ||
| 3618 | } | ||
| 3619 | |||
| 3620 | result = drwav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); | ||
| 3621 | if (result != DRWAV_SUCCESS) { | ||
| 3622 | return DRWAV_FALSE; | ||
| 3623 | } | ||
| 3624 | |||
| 3625 | metadataParser.stage = drwav__metadata_parser_stage_read; | ||
| 3626 | |||
| 3627 | for (;;) { | ||
| 3628 | drwav_chunk_header header; | ||
| 3629 | drwav_uint64 metadataBytesRead; | ||
| 3630 | |||
| 3631 | result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); | ||
| 3632 | if (result != DRWAV_SUCCESS) { | ||
| 3633 | break; | ||
| 3634 | } | ||
| 3635 | |||
| 3636 | metadataBytesRead = drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown); | ||
| 3637 | |||
| 3638 | /* Move to the end of the chunk so we can keep iterating. */ | ||
| 3639 | if (drwav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == DRWAV_FALSE) { | ||
| 3640 | drwav_free(metadataParser.pMetadata, &pWav->allocationCallbacks); | ||
| 3641 | return DRWAV_FALSE; | ||
| 3642 | } | ||
| 3643 | } | ||
| 3644 | |||
| 3645 | /* Getting here means we're finished parsing the metadata. */ | ||
| 3646 | pWav->pMetadata = metadataParser.pMetadata; | ||
| 3647 | pWav->metadataCount = metadataParser.metadataCount; | ||
| 3648 | } | ||
| 3649 | |||
| 3650 | |||
| 3651 | /* At this point we should be sitting on the first byte of the raw audio data. */ | ||
| 3652 | |||
| 3653 | /* | ||
| 3654 | I've seen a WAV file in the wild where a RIFF-ecapsulated file has the size of it's "RIFF" and | ||
| 3655 | "data" chunks set to 0xFFFFFFFF when the file is definitely not that big. In this case we're | ||
| 3656 | going to have to calculate the size by reading and discarding bytes, and then seeking back. We | ||
| 3657 | cannot do this in sequential mode. We just assume that the rest of the file is audio data. | ||
| 3658 | */ | ||
| 3659 | if (dataChunkSize == 0xFFFFFFFF && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx) && pWav->isSequentialWrite == DRWAV_FALSE) { | ||
| 3660 | dataChunkSize = 0; | ||
| 3661 | |||
| 3662 | for (;;) { | ||
| 3663 | drwav_uint8 temp[4096]; | ||
| 3664 | size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp)); | ||
| 3665 | dataChunkSize += bytesRead; | ||
| 3666 | |||
| 3667 | if (bytesRead < sizeof(temp)) { | ||
| 3668 | break; | ||
| 3669 | } | ||
| 3670 | } | ||
| 3671 | } | ||
| 3672 | |||
| 3673 | if (drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == DRWAV_FALSE) { | ||
| 3674 | drwav_free(pWav->pMetadata, &pWav->allocationCallbacks); | ||
| 3675 | return DRWAV_FALSE; | ||
| 3676 | } | ||
| 3677 | |||
| 3678 | |||
| 3679 | pWav->fmt = fmt; | ||
| 3680 | pWav->sampleRate = fmt.sampleRate; | ||
| 3681 | pWav->channels = fmt.channels; | ||
| 3682 | pWav->bitsPerSample = fmt.bitsPerSample; | ||
| 3683 | pWav->bytesRemaining = dataChunkSize; | ||
| 3684 | pWav->translatedFormatTag = translatedFormatTag; | ||
| 3685 | pWav->dataChunkDataSize = dataChunkSize; | ||
| 3686 | |||
| 3687 | if (sampleCountFromFactChunk != 0) { | ||
| 3688 | pWav->totalPCMFrameCount = sampleCountFromFactChunk; | ||
| 3689 | } else if (aiffFrameCount != 0) { | ||
| 3690 | pWav->totalPCMFrameCount = aiffFrameCount; | ||
| 3691 | } else { | ||
| 3692 | drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 3693 | if (bytesPerFrame == 0) { | ||
| 3694 | drwav_free(pWav->pMetadata, &pWav->allocationCallbacks); | ||
| 3695 | return DRWAV_FALSE; /* Invalid file. */ | ||
| 3696 | } | ||
| 3697 | |||
| 3698 | pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; | ||
| 3699 | |||
| 3700 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { | ||
| 3701 | drwav_uint64 totalBlockHeaderSizeInBytes; | ||
| 3702 | drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; | ||
| 3703 | |||
| 3704 | /* Make sure any trailing partial block is accounted for. */ | ||
| 3705 | if ((blockCount * fmt.blockAlign) < dataChunkSize) { | ||
| 3706 | blockCount += 1; | ||
| 3707 | } | ||
| 3708 | |||
| 3709 | /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */ | ||
| 3710 | totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); | ||
| 3711 | pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; | ||
| 3712 | } | ||
| 3713 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { | ||
| 3714 | drwav_uint64 totalBlockHeaderSizeInBytes; | ||
| 3715 | drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; | ||
| 3716 | |||
| 3717 | /* Make sure any trailing partial block is accounted for. */ | ||
| 3718 | if ((blockCount * fmt.blockAlign) < dataChunkSize) { | ||
| 3719 | blockCount += 1; | ||
| 3720 | } | ||
| 3721 | |||
| 3722 | /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */ | ||
| 3723 | totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); | ||
| 3724 | pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; | ||
| 3725 | |||
| 3726 | /* The header includes a decoded sample for each channel which acts as the initial predictor sample. */ | ||
| 3727 | pWav->totalPCMFrameCount += blockCount; | ||
| 3728 | } | ||
| 3729 | } | ||
| 3730 | |||
| 3731 | /* Some formats only support a certain number of channels. */ | ||
| 3732 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { | ||
| 3733 | if (pWav->channels > 2) { | ||
| 3734 | drwav_free(pWav->pMetadata, &pWav->allocationCallbacks); | ||
| 3735 | return DRWAV_FALSE; | ||
| 3736 | } | ||
| 3737 | } | ||
| 3738 | |||
| 3739 | /* The number of bytes per frame must be known. If not, it's an invalid file and not decodable. */ | ||
| 3740 | if (drwav_get_bytes_per_pcm_frame(pWav) == 0) { | ||
| 3741 | drwav_free(pWav->pMetadata, &pWav->allocationCallbacks); | ||
| 3742 | return DRWAV_FALSE; | ||
| 3743 | } | ||
| 3744 | |||
| 3745 | #ifdef DR_WAV_LIBSNDFILE_COMPAT | ||
| 3746 | /* | ||
| 3747 | I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website), | ||
| 3748 | it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count | ||
| 3749 | from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct | ||
| 3750 | way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should | ||
| 3751 | always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my | ||
| 3752 | correctness tests against libsndfile, and is disabled by default. | ||
| 3753 | */ | ||
| 3754 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { | ||
| 3755 | drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; | ||
| 3756 | pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; /* x2 because two samples per byte. */ | ||
| 3757 | } | ||
| 3758 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { | ||
| 3759 | drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; | ||
| 3760 | pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; | ||
| 3761 | } | ||
| 3762 | #endif | ||
| 3763 | |||
| 3764 | return DRWAV_TRUE; | ||
| 3765 | } | ||
| 3766 | |||
| 3767 | DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 3768 | { | ||
| 3769 | return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); | ||
| 3770 | } | ||
| 3771 | |||
| 3772 | DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 3773 | { | ||
| 3774 | if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { | ||
| 3775 | return DRWAV_FALSE; | ||
| 3776 | } | ||
| 3777 | |||
| 3778 | return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); | ||
| 3779 | } | ||
| 3780 | |||
| 3781 | DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 3782 | { | ||
| 3783 | if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { | ||
| 3784 | return DRWAV_FALSE; | ||
| 3785 | } | ||
| 3786 | |||
| 3787 | return drwav_init__internal(pWav, NULL, NULL, flags | DRWAV_WITH_METADATA); | ||
| 3788 | } | ||
| 3789 | |||
| 3790 | DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav) | ||
| 3791 | { | ||
| 3792 | drwav_metadata *result = pWav->pMetadata; | ||
| 3793 | |||
| 3794 | pWav->pMetadata = NULL; | ||
| 3795 | pWav->metadataCount = 0; | ||
| 3796 | |||
| 3797 | return result; | ||
| 3798 | } | ||
| 3799 | |||
| 3800 | |||
| 3801 | DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize) | ||
| 3802 | { | ||
| 3803 | DRWAV_ASSERT(pWav != NULL); | ||
| 3804 | DRWAV_ASSERT(pWav->onWrite != NULL); | ||
| 3805 | |||
| 3806 | /* Generic write. Assumes no byte reordering required. */ | ||
| 3807 | return pWav->onWrite(pWav->pUserData, pData, dataSize); | ||
| 3808 | } | ||
| 3809 | |||
| 3810 | DRWAV_PRIVATE size_t drwav__write_byte(drwav* pWav, drwav_uint8 byte) | ||
| 3811 | { | ||
| 3812 | DRWAV_ASSERT(pWav != NULL); | ||
| 3813 | DRWAV_ASSERT(pWav->onWrite != NULL); | ||
| 3814 | |||
| 3815 | return pWav->onWrite(pWav->pUserData, &byte, 1); | ||
| 3816 | } | ||
| 3817 | |||
| 3818 | DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value) | ||
| 3819 | { | ||
| 3820 | DRWAV_ASSERT(pWav != NULL); | ||
| 3821 | DRWAV_ASSERT(pWav->onWrite != NULL); | ||
| 3822 | |||
| 3823 | if (!drwav__is_little_endian()) { | ||
| 3824 | value = drwav__bswap16(value); | ||
| 3825 | } | ||
| 3826 | |||
| 3827 | return drwav__write(pWav, &value, 2); | ||
| 3828 | } | ||
| 3829 | |||
| 3830 | DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value) | ||
| 3831 | { | ||
| 3832 | DRWAV_ASSERT(pWav != NULL); | ||
| 3833 | DRWAV_ASSERT(pWav->onWrite != NULL); | ||
| 3834 | |||
| 3835 | if (!drwav__is_little_endian()) { | ||
| 3836 | value = drwav__bswap32(value); | ||
| 3837 | } | ||
| 3838 | |||
| 3839 | return drwav__write(pWav, &value, 4); | ||
| 3840 | } | ||
| 3841 | |||
| 3842 | DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value) | ||
| 3843 | { | ||
| 3844 | DRWAV_ASSERT(pWav != NULL); | ||
| 3845 | DRWAV_ASSERT(pWav->onWrite != NULL); | ||
| 3846 | |||
| 3847 | if (!drwav__is_little_endian()) { | ||
| 3848 | value = drwav__bswap64(value); | ||
| 3849 | } | ||
| 3850 | |||
| 3851 | return drwav__write(pWav, &value, 8); | ||
| 3852 | } | ||
| 3853 | |||
| 3854 | DRWAV_PRIVATE size_t drwav__write_f32ne_to_le(drwav* pWav, float value) | ||
| 3855 | { | ||
| 3856 | union { | ||
| 3857 | drwav_uint32 u32; | ||
| 3858 | float f32; | ||
| 3859 | } u; | ||
| 3860 | |||
| 3861 | DRWAV_ASSERT(pWav != NULL); | ||
| 3862 | DRWAV_ASSERT(pWav->onWrite != NULL); | ||
| 3863 | |||
| 3864 | u.f32 = value; | ||
| 3865 | |||
| 3866 | if (!drwav__is_little_endian()) { | ||
| 3867 | u.u32 = drwav__bswap32(u.u32); | ||
| 3868 | } | ||
| 3869 | |||
| 3870 | return drwav__write(pWav, &u.u32, 4); | ||
| 3871 | } | ||
| 3872 | |||
| 3873 | DRWAV_PRIVATE size_t drwav__write_or_count(drwav* pWav, const void* pData, size_t dataSize) | ||
| 3874 | { | ||
| 3875 | if (pWav == NULL) { | ||
| 3876 | return dataSize; | ||
| 3877 | } | ||
| 3878 | |||
| 3879 | return drwav__write(pWav, pData, dataSize); | ||
| 3880 | } | ||
| 3881 | |||
| 3882 | DRWAV_PRIVATE size_t drwav__write_or_count_byte(drwav* pWav, drwav_uint8 byte) | ||
| 3883 | { | ||
| 3884 | if (pWav == NULL) { | ||
| 3885 | return 1; | ||
| 3886 | } | ||
| 3887 | |||
| 3888 | return drwav__write_byte(pWav, byte); | ||
| 3889 | } | ||
| 3890 | |||
| 3891 | DRWAV_PRIVATE size_t drwav__write_or_count_u16ne_to_le(drwav* pWav, drwav_uint16 value) | ||
| 3892 | { | ||
| 3893 | if (pWav == NULL) { | ||
| 3894 | return 2; | ||
| 3895 | } | ||
| 3896 | |||
| 3897 | return drwav__write_u16ne_to_le(pWav, value); | ||
| 3898 | } | ||
| 3899 | |||
| 3900 | DRWAV_PRIVATE size_t drwav__write_or_count_u32ne_to_le(drwav* pWav, drwav_uint32 value) | ||
| 3901 | { | ||
| 3902 | if (pWav == NULL) { | ||
| 3903 | return 4; | ||
| 3904 | } | ||
| 3905 | |||
| 3906 | return drwav__write_u32ne_to_le(pWav, value); | ||
| 3907 | } | ||
| 3908 | |||
| 3909 | #if 0 /* Unused for now. */ | ||
| 3910 | DRWAV_PRIVATE size_t drwav__write_or_count_u64ne_to_le(drwav* pWav, drwav_uint64 value) | ||
| 3911 | { | ||
| 3912 | if (pWav == NULL) { | ||
| 3913 | return 8; | ||
| 3914 | } | ||
| 3915 | |||
| 3916 | return drwav__write_u64ne_to_le(pWav, value); | ||
| 3917 | } | ||
| 3918 | #endif | ||
| 3919 | |||
| 3920 | DRWAV_PRIVATE size_t drwav__write_or_count_f32ne_to_le(drwav* pWav, float value) | ||
| 3921 | { | ||
| 3922 | if (pWav == NULL) { | ||
| 3923 | return 4; | ||
| 3924 | } | ||
| 3925 | |||
| 3926 | return drwav__write_f32ne_to_le(pWav, value); | ||
| 3927 | } | ||
| 3928 | |||
| 3929 | DRWAV_PRIVATE size_t drwav__write_or_count_string_to_fixed_size_buf(drwav* pWav, char* str, size_t bufFixedSize) | ||
| 3930 | { | ||
| 3931 | size_t len; | ||
| 3932 | |||
| 3933 | if (pWav == NULL) { | ||
| 3934 | return bufFixedSize; | ||
| 3935 | } | ||
| 3936 | |||
| 3937 | len = drwav__strlen_clamped(str, bufFixedSize); | ||
| 3938 | drwav__write_or_count(pWav, str, len); | ||
| 3939 | |||
| 3940 | if (len < bufFixedSize) { | ||
| 3941 | size_t i; | ||
| 3942 | for (i = 0; i < bufFixedSize - len; ++i) { | ||
| 3943 | drwav__write_byte(pWav, 0); | ||
| 3944 | } | ||
| 3945 | } | ||
| 3946 | |||
| 3947 | return bufFixedSize; | ||
| 3948 | } | ||
| 3949 | |||
| 3950 | |||
| 3951 | /* pWav can be NULL meaning just count the bytes that would be written. */ | ||
| 3952 | DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* pMetadatas, drwav_uint32 metadataCount) | ||
| 3953 | { | ||
| 3954 | size_t bytesWritten = 0; | ||
| 3955 | drwav_bool32 hasListAdtl = DRWAV_FALSE; | ||
| 3956 | drwav_bool32 hasListInfo = DRWAV_FALSE; | ||
| 3957 | drwav_uint32 iMetadata; | ||
| 3958 | |||
| 3959 | if (pMetadatas == NULL || metadataCount == 0) { | ||
| 3960 | return 0; | ||
| 3961 | } | ||
| 3962 | |||
| 3963 | for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { | ||
| 3964 | drwav_metadata* pMetadata = &pMetadatas[iMetadata]; | ||
| 3965 | drwav_uint32 chunkSize = 0; | ||
| 3966 | |||
| 3967 | if ((pMetadata->type & drwav_metadata_type_list_all_info_strings) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list)) { | ||
| 3968 | hasListInfo = DRWAV_TRUE; | ||
| 3969 | } | ||
| 3970 | |||
| 3971 | if ((pMetadata->type & drwav_metadata_type_list_all_adtl) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list)) { | ||
| 3972 | hasListAdtl = DRWAV_TRUE; | ||
| 3973 | } | ||
| 3974 | |||
| 3975 | switch (pMetadata->type) { | ||
| 3976 | case drwav_metadata_type_smpl: | ||
| 3977 | { | ||
| 3978 | drwav_uint32 iLoop; | ||
| 3979 | |||
| 3980 | chunkSize = DRWAV_SMPL_BYTES + DRWAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; | ||
| 3981 | |||
| 3982 | bytesWritten += drwav__write_or_count(pWav, "smpl", 4); | ||
| 3983 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); | ||
| 3984 | |||
| 3985 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); | ||
| 3986 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); | ||
| 3987 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); | ||
| 3988 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); | ||
| 3989 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); | ||
| 3990 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); | ||
| 3991 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); | ||
| 3992 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); | ||
| 3993 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); | ||
| 3994 | |||
| 3995 | for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { | ||
| 3996 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); | ||
| 3997 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); | ||
| 3998 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); | ||
| 3999 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); | ||
| 4000 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); | ||
| 4001 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); | ||
| 4002 | } | ||
| 4003 | |||
| 4004 | if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { | ||
| 4005 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); | ||
| 4006 | } | ||
| 4007 | } break; | ||
| 4008 | |||
| 4009 | case drwav_metadata_type_inst: | ||
| 4010 | { | ||
| 4011 | chunkSize = DRWAV_INST_BYTES; | ||
| 4012 | |||
| 4013 | bytesWritten += drwav__write_or_count(pWav, "inst", 4); | ||
| 4014 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); | ||
| 4015 | bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); | ||
| 4016 | bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); | ||
| 4017 | bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); | ||
| 4018 | bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); | ||
| 4019 | bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); | ||
| 4020 | bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); | ||
| 4021 | bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); | ||
| 4022 | } break; | ||
| 4023 | |||
| 4024 | case drwav_metadata_type_cue: | ||
| 4025 | { | ||
| 4026 | drwav_uint32 iCuePoint; | ||
| 4027 | |||
| 4028 | chunkSize = DRWAV_CUE_BYTES + DRWAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; | ||
| 4029 | |||
| 4030 | bytesWritten += drwav__write_or_count(pWav, "cue ", 4); | ||
| 4031 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); | ||
| 4032 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); | ||
| 4033 | for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { | ||
| 4034 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); | ||
| 4035 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); | ||
| 4036 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); | ||
| 4037 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); | ||
| 4038 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); | ||
| 4039 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); | ||
| 4040 | } | ||
| 4041 | } break; | ||
| 4042 | |||
| 4043 | case drwav_metadata_type_acid: | ||
| 4044 | { | ||
| 4045 | chunkSize = DRWAV_ACID_BYTES; | ||
| 4046 | |||
| 4047 | bytesWritten += drwav__write_or_count(pWav, "acid", 4); | ||
| 4048 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); | ||
| 4049 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); | ||
| 4050 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); | ||
| 4051 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); | ||
| 4052 | bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); | ||
| 4053 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); | ||
| 4054 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); | ||
| 4055 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); | ||
| 4056 | bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); | ||
| 4057 | } break; | ||
| 4058 | |||
| 4059 | case drwav_metadata_type_bext: | ||
| 4060 | { | ||
| 4061 | char reservedBuf[DRWAV_BEXT_RESERVED_BYTES]; | ||
| 4062 | drwav_uint32 timeReferenceLow; | ||
| 4063 | drwav_uint32 timeReferenceHigh; | ||
| 4064 | |||
| 4065 | chunkSize = DRWAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; | ||
| 4066 | |||
| 4067 | bytesWritten += drwav__write_or_count(pWav, "bext", 4); | ||
| 4068 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); | ||
| 4069 | |||
| 4070 | bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, DRWAV_BEXT_DESCRIPTION_BYTES); | ||
| 4071 | bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); | ||
| 4072 | bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, DRWAV_BEXT_ORIGINATOR_REF_BYTES); | ||
| 4073 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); | ||
| 4074 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); | ||
| 4075 | |||
| 4076 | timeReferenceLow = (drwav_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); | ||
| 4077 | timeReferenceHigh = (drwav_uint32)(pMetadata->data.bext.timeReference >> 32); | ||
| 4078 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); | ||
| 4079 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); | ||
| 4080 | |||
| 4081 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); | ||
| 4082 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES); | ||
| 4083 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); | ||
| 4084 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); | ||
| 4085 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); | ||
| 4086 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); | ||
| 4087 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); | ||
| 4088 | |||
| 4089 | DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); | ||
| 4090 | bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); | ||
| 4091 | |||
| 4092 | if (pMetadata->data.bext.codingHistorySize > 0) { | ||
| 4093 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); | ||
| 4094 | } | ||
| 4095 | } break; | ||
| 4096 | |||
| 4097 | case drwav_metadata_type_unknown: | ||
| 4098 | { | ||
| 4099 | if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_top_level) { | ||
| 4100 | chunkSize = pMetadata->data.unknown.dataSizeInBytes; | ||
| 4101 | |||
| 4102 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); | ||
| 4103 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); | ||
| 4104 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); | ||
| 4105 | } | ||
| 4106 | } break; | ||
| 4107 | |||
| 4108 | default: break; | ||
| 4109 | } | ||
| 4110 | if ((chunkSize % 2) != 0) { | ||
| 4111 | bytesWritten += drwav__write_or_count_byte(pWav, 0); | ||
| 4112 | } | ||
| 4113 | } | ||
| 4114 | |||
| 4115 | if (hasListInfo) { | ||
| 4116 | drwav_uint32 chunkSize = 4; /* Start with 4 bytes for "INFO". */ | ||
| 4117 | for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { | ||
| 4118 | drwav_metadata* pMetadata = &pMetadatas[iMetadata]; | ||
| 4119 | |||
| 4120 | if ((pMetadata->type & drwav_metadata_type_list_all_info_strings)) { | ||
| 4121 | chunkSize += 8; /* For id and string size. */ | ||
| 4122 | chunkSize += pMetadata->data.infoText.stringLength + 1; /* Include null terminator. */ | ||
| 4123 | } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { | ||
| 4124 | chunkSize += 8; /* For id string size. */ | ||
| 4125 | chunkSize += pMetadata->data.unknown.dataSizeInBytes; | ||
| 4126 | } | ||
| 4127 | |||
| 4128 | if ((chunkSize % 2) != 0) { | ||
| 4129 | chunkSize += 1; | ||
| 4130 | } | ||
| 4131 | } | ||
| 4132 | |||
| 4133 | bytesWritten += drwav__write_or_count(pWav, "LIST", 4); | ||
| 4134 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); | ||
| 4135 | bytesWritten += drwav__write_or_count(pWav, "INFO", 4); | ||
| 4136 | |||
| 4137 | for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { | ||
| 4138 | drwav_metadata* pMetadata = &pMetadatas[iMetadata]; | ||
| 4139 | drwav_uint32 subchunkSize = 0; | ||
| 4140 | |||
| 4141 | if (pMetadata->type & drwav_metadata_type_list_all_info_strings) { | ||
| 4142 | const char* pID = NULL; | ||
| 4143 | |||
| 4144 | switch (pMetadata->type) { | ||
| 4145 | case drwav_metadata_type_list_info_software: pID = "ISFT"; break; | ||
| 4146 | case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break; | ||
| 4147 | case drwav_metadata_type_list_info_title: pID = "INAM"; break; | ||
| 4148 | case drwav_metadata_type_list_info_artist: pID = "IART"; break; | ||
| 4149 | case drwav_metadata_type_list_info_comment: pID = "ICMT"; break; | ||
| 4150 | case drwav_metadata_type_list_info_date: pID = "ICRD"; break; | ||
| 4151 | case drwav_metadata_type_list_info_genre: pID = "IGNR"; break; | ||
| 4152 | case drwav_metadata_type_list_info_album: pID = "IPRD"; break; | ||
| 4153 | case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; | ||
| 4154 | default: break; | ||
| 4155 | } | ||
| 4156 | |||
| 4157 | DRWAV_ASSERT(pID != NULL); | ||
| 4158 | |||
| 4159 | if (pMetadata->data.infoText.stringLength) { | ||
| 4160 | subchunkSize = pMetadata->data.infoText.stringLength + 1; | ||
| 4161 | bytesWritten += drwav__write_or_count(pWav, pID, 4); | ||
| 4162 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); | ||
| 4163 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); | ||
| 4164 | bytesWritten += drwav__write_or_count_byte(pWav, '\0'); | ||
| 4165 | } | ||
| 4166 | } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { | ||
| 4167 | if (pMetadata->data.unknown.dataSizeInBytes) { | ||
| 4168 | subchunkSize = pMetadata->data.unknown.dataSizeInBytes; | ||
| 4169 | |||
| 4170 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); | ||
| 4171 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); | ||
| 4172 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); | ||
| 4173 | } | ||
| 4174 | } | ||
| 4175 | |||
| 4176 | if ((subchunkSize % 2) != 0) { | ||
| 4177 | bytesWritten += drwav__write_or_count_byte(pWav, 0); | ||
| 4178 | } | ||
| 4179 | } | ||
| 4180 | } | ||
| 4181 | |||
| 4182 | if (hasListAdtl) { | ||
| 4183 | drwav_uint32 chunkSize = 4; /* start with 4 bytes for "adtl" */ | ||
| 4184 | |||
| 4185 | for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { | ||
| 4186 | drwav_metadata* pMetadata = &pMetadatas[iMetadata]; | ||
| 4187 | |||
| 4188 | switch (pMetadata->type) | ||
| 4189 | { | ||
| 4190 | case drwav_metadata_type_list_label: | ||
| 4191 | case drwav_metadata_type_list_note: | ||
| 4192 | { | ||
| 4193 | chunkSize += 8; /* for id and chunk size */ | ||
| 4194 | chunkSize += DRWAV_LIST_LABEL_OR_NOTE_BYTES; | ||
| 4195 | |||
| 4196 | if (pMetadata->data.labelOrNote.stringLength > 0) { | ||
| 4197 | chunkSize += pMetadata->data.labelOrNote.stringLength + 1; | ||
| 4198 | } | ||
| 4199 | } break; | ||
| 4200 | |||
| 4201 | case drwav_metadata_type_list_labelled_cue_region: | ||
| 4202 | { | ||
| 4203 | chunkSize += 8; /* for id and chunk size */ | ||
| 4204 | chunkSize += DRWAV_LIST_LABELLED_TEXT_BYTES; | ||
| 4205 | |||
| 4206 | if (pMetadata->data.labelledCueRegion.stringLength > 0) { | ||
| 4207 | chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; | ||
| 4208 | } | ||
| 4209 | } break; | ||
| 4210 | |||
| 4211 | case drwav_metadata_type_unknown: | ||
| 4212 | { | ||
| 4213 | if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { | ||
| 4214 | chunkSize += 8; /* for id and chunk size */ | ||
| 4215 | chunkSize += pMetadata->data.unknown.dataSizeInBytes; | ||
| 4216 | } | ||
| 4217 | } break; | ||
| 4218 | |||
| 4219 | default: break; | ||
| 4220 | } | ||
| 4221 | |||
| 4222 | if ((chunkSize % 2) != 0) { | ||
| 4223 | chunkSize += 1; | ||
| 4224 | } | ||
| 4225 | } | ||
| 4226 | |||
| 4227 | bytesWritten += drwav__write_or_count(pWav, "LIST", 4); | ||
| 4228 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); | ||
| 4229 | bytesWritten += drwav__write_or_count(pWav, "adtl", 4); | ||
| 4230 | |||
| 4231 | for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { | ||
| 4232 | drwav_metadata* pMetadata = &pMetadatas[iMetadata]; | ||
| 4233 | drwav_uint32 subchunkSize = 0; | ||
| 4234 | |||
| 4235 | switch (pMetadata->type) | ||
| 4236 | { | ||
| 4237 | case drwav_metadata_type_list_label: | ||
| 4238 | case drwav_metadata_type_list_note: | ||
| 4239 | { | ||
| 4240 | if (pMetadata->data.labelOrNote.stringLength > 0) { | ||
| 4241 | const char *pID = NULL; | ||
| 4242 | |||
| 4243 | if (pMetadata->type == drwav_metadata_type_list_label) { | ||
| 4244 | pID = "labl"; | ||
| 4245 | } | ||
| 4246 | else if (pMetadata->type == drwav_metadata_type_list_note) { | ||
| 4247 | pID = "note"; | ||
| 4248 | } | ||
| 4249 | |||
| 4250 | DRWAV_ASSERT(pID != NULL); | ||
| 4251 | DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); | ||
| 4252 | |||
| 4253 | subchunkSize = DRWAV_LIST_LABEL_OR_NOTE_BYTES; | ||
| 4254 | |||
| 4255 | bytesWritten += drwav__write_or_count(pWav, pID, 4); | ||
| 4256 | subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; | ||
| 4257 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); | ||
| 4258 | |||
| 4259 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); | ||
| 4260 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); | ||
| 4261 | bytesWritten += drwav__write_or_count_byte(pWav, '\0'); | ||
| 4262 | } | ||
| 4263 | } break; | ||
| 4264 | |||
| 4265 | case drwav_metadata_type_list_labelled_cue_region: | ||
| 4266 | { | ||
| 4267 | subchunkSize = DRWAV_LIST_LABELLED_TEXT_BYTES; | ||
| 4268 | |||
| 4269 | bytesWritten += drwav__write_or_count(pWav, "ltxt", 4); | ||
| 4270 | if (pMetadata->data.labelledCueRegion.stringLength > 0) { | ||
| 4271 | subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; | ||
| 4272 | } | ||
| 4273 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); | ||
| 4274 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); | ||
| 4275 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); | ||
| 4276 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); | ||
| 4277 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); | ||
| 4278 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); | ||
| 4279 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); | ||
| 4280 | bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); | ||
| 4281 | |||
| 4282 | if (pMetadata->data.labelledCueRegion.stringLength > 0) { | ||
| 4283 | DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); | ||
| 4284 | |||
| 4285 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); | ||
| 4286 | bytesWritten += drwav__write_or_count_byte(pWav, '\0'); | ||
| 4287 | } | ||
| 4288 | } break; | ||
| 4289 | |||
| 4290 | case drwav_metadata_type_unknown: | ||
| 4291 | { | ||
| 4292 | if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { | ||
| 4293 | subchunkSize = pMetadata->data.unknown.dataSizeInBytes; | ||
| 4294 | |||
| 4295 | DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); | ||
| 4296 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); | ||
| 4297 | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); | ||
| 4298 | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); | ||
| 4299 | } | ||
| 4300 | } break; | ||
| 4301 | |||
| 4302 | default: break; | ||
| 4303 | } | ||
| 4304 | |||
| 4305 | if ((subchunkSize % 2) != 0) { | ||
| 4306 | bytesWritten += drwav__write_or_count_byte(pWav, 0); | ||
| 4307 | } | ||
| 4308 | } | ||
| 4309 | } | ||
| 4310 | |||
| 4311 | DRWAV_ASSERT((bytesWritten % 2) == 0); | ||
| 4312 | |||
| 4313 | return bytesWritten; | ||
| 4314 | } | ||
| 4315 | |||
| 4316 | DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize, drwav_metadata* pMetadata, drwav_uint32 metadataCount) | ||
| 4317 | { | ||
| 4318 | drwav_uint64 chunkSize = 4 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); /* 4 = "WAVE". 24 = "fmt " chunk. 8 = "data" + u32 data size. */ | ||
| 4319 | if (chunkSize > 0xFFFFFFFFUL) { | ||
| 4320 | chunkSize = 0xFFFFFFFFUL; | ||
| 4321 | } | ||
| 4322 | |||
| 4323 | return (drwav_uint32)chunkSize; /* Safe cast due to the clamp above. */ | ||
| 4324 | } | ||
| 4325 | |||
| 4326 | DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize) | ||
| 4327 | { | ||
| 4328 | if (dataChunkSize <= 0xFFFFFFFFUL) { | ||
| 4329 | return (drwav_uint32)dataChunkSize; | ||
| 4330 | } else { | ||
| 4331 | return 0xFFFFFFFFUL; | ||
| 4332 | } | ||
| 4333 | } | ||
| 4334 | |||
| 4335 | DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize) | ||
| 4336 | { | ||
| 4337 | drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize); | ||
| 4338 | |||
| 4339 | return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; /* +24 because W64 includes the size of the GUID and size fields. */ | ||
| 4340 | } | ||
| 4341 | |||
| 4342 | DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize) | ||
| 4343 | { | ||
| 4344 | return 24 + dataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */ | ||
| 4345 | } | ||
| 4346 | |||
| 4347 | DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize, drwav_metadata *metadata, drwav_uint32 numMetadata) | ||
| 4348 | { | ||
| 4349 | drwav_uint64 chunkSize = 4 + 36 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); /* 4 = "WAVE". 36 = "ds64" chunk. 24 = "fmt " chunk. 8 = "data" + u32 data size. */ | ||
| 4350 | if (chunkSize > 0xFFFFFFFFUL) { | ||
| 4351 | chunkSize = 0xFFFFFFFFUL; | ||
| 4352 | } | ||
| 4353 | |||
| 4354 | return chunkSize; | ||
| 4355 | } | ||
| 4356 | |||
| 4357 | DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize) | ||
| 4358 | { | ||
| 4359 | return dataChunkSize; | ||
| 4360 | } | ||
| 4361 | |||
| 4362 | |||
| 4363 | |||
| 4364 | DRWAV_PRIVATE drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 4365 | { | ||
| 4366 | if (pWav == NULL || onWrite == NULL) { | ||
| 4367 | return DRWAV_FALSE; | ||
| 4368 | } | ||
| 4369 | |||
| 4370 | if (!isSequential && onSeek == NULL) { | ||
| 4371 | return DRWAV_FALSE; /* <-- onSeek is required when in non-sequential mode. */ | ||
| 4372 | } | ||
| 4373 | |||
| 4374 | /* Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this. */ | ||
| 4375 | if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) { | ||
| 4376 | return DRWAV_FALSE; | ||
| 4377 | } | ||
| 4378 | if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) { | ||
| 4379 | return DRWAV_FALSE; | ||
| 4380 | } | ||
| 4381 | |||
| 4382 | DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); | ||
| 4383 | pWav->onWrite = onWrite; | ||
| 4384 | pWav->onSeek = onSeek; | ||
| 4385 | pWav->pUserData = pUserData; | ||
| 4386 | pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); | ||
| 4387 | |||
| 4388 | if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { | ||
| 4389 | return DRWAV_FALSE; /* Invalid allocation callbacks. */ | ||
| 4390 | } | ||
| 4391 | |||
| 4392 | pWav->fmt.formatTag = (drwav_uint16)pFormat->format; | ||
| 4393 | pWav->fmt.channels = (drwav_uint16)pFormat->channels; | ||
| 4394 | pWav->fmt.sampleRate = pFormat->sampleRate; | ||
| 4395 | pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); | ||
| 4396 | pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); | ||
| 4397 | pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; | ||
| 4398 | pWav->fmt.extendedSize = 0; | ||
| 4399 | pWav->isSequentialWrite = isSequential; | ||
| 4400 | |||
| 4401 | return DRWAV_TRUE; | ||
| 4402 | } | ||
| 4403 | |||
| 4404 | |||
| 4405 | DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) | ||
| 4406 | { | ||
| 4407 | /* The function assumes drwav_preinit_write() was called beforehand. */ | ||
| 4408 | |||
| 4409 | size_t runningPos = 0; | ||
| 4410 | drwav_uint64 initialDataChunkSize = 0; | ||
| 4411 | drwav_uint64 chunkSizeFMT; | ||
| 4412 | |||
| 4413 | /* | ||
| 4414 | The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In | ||
| 4415 | sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non- | ||
| 4416 | sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek. | ||
| 4417 | */ | ||
| 4418 | if (pWav->isSequentialWrite) { | ||
| 4419 | initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; | ||
| 4420 | |||
| 4421 | /* | ||
| 4422 | The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64 | ||
| 4423 | so for the sake of simplicity I'm not doing any validation for that. | ||
| 4424 | */ | ||
| 4425 | if (pFormat->container == drwav_container_riff) { | ||
| 4426 | if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { | ||
| 4427 | return DRWAV_FALSE; /* Not enough room to store every sample. */ | ||
| 4428 | } | ||
| 4429 | } | ||
| 4430 | } | ||
| 4431 | |||
| 4432 | pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; | ||
| 4433 | |||
| 4434 | |||
| 4435 | /* "RIFF" chunk. */ | ||
| 4436 | if (pFormat->container == drwav_container_riff) { | ||
| 4437 | drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize; /* +28 = "WAVE" + [sizeof "fmt " chunk] */ | ||
| 4438 | runningPos += drwav__write(pWav, "RIFF", 4); | ||
| 4439 | runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF); | ||
| 4440 | runningPos += drwav__write(pWav, "WAVE", 4); | ||
| 4441 | } else if (pFormat->container == drwav_container_w64) { | ||
| 4442 | drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */ | ||
| 4443 | runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16); | ||
| 4444 | runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF); | ||
| 4445 | runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16); | ||
| 4446 | } else if (pFormat->container == drwav_container_rf64) { | ||
| 4447 | runningPos += drwav__write(pWav, "RF64", 4); | ||
| 4448 | runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); /* Always 0xFFFFFFFF for RF64. Set to a proper value in the "ds64" chunk. */ | ||
| 4449 | runningPos += drwav__write(pWav, "WAVE", 4); | ||
| 4450 | } else { | ||
| 4451 | return DRWAV_FALSE; /* Container not supported for writing. */ | ||
| 4452 | } | ||
| 4453 | |||
| 4454 | |||
| 4455 | /* "ds64" chunk (RF64 only). */ | ||
| 4456 | if (pFormat->container == drwav_container_rf64) { | ||
| 4457 | drwav_uint32 initialds64ChunkSize = 28; /* 28 = [Size of RIFF (8 bytes)] + [Size of DATA (8 bytes)] + [Sample Count (8 bytes)] + [Table Length (4 bytes)]. Table length always set to 0. */ | ||
| 4458 | drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; /* +8 for the ds64 header. */ | ||
| 4459 | |||
| 4460 | runningPos += drwav__write(pWav, "ds64", 4); | ||
| 4461 | runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize); /* Size of ds64. */ | ||
| 4462 | runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize); /* Size of RIFF. Set to true value at the end. */ | ||
| 4463 | runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize); /* Size of DATA. Set to true value at the end. */ | ||
| 4464 | runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount); /* Sample count. */ | ||
| 4465 | runningPos += drwav__write_u32ne_to_le(pWav, 0); /* Table length. Always set to zero in our case since we're not doing any other chunks than "DATA". */ | ||
| 4466 | } | ||
| 4467 | |||
| 4468 | |||
| 4469 | /* "fmt " chunk. */ | ||
| 4470 | if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) { | ||
| 4471 | chunkSizeFMT = 16; | ||
| 4472 | runningPos += drwav__write(pWav, "fmt ", 4); | ||
| 4473 | runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT); | ||
| 4474 | } else if (pFormat->container == drwav_container_w64) { | ||
| 4475 | chunkSizeFMT = 40; | ||
| 4476 | runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16); | ||
| 4477 | runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT); | ||
| 4478 | } | ||
| 4479 | |||
| 4480 | runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); | ||
| 4481 | runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels); | ||
| 4482 | runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); | ||
| 4483 | runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); | ||
| 4484 | runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); | ||
| 4485 | runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); | ||
| 4486 | |||
| 4487 | /* TODO: is a 'fact' chunk required for DR_WAVE_FORMAT_IEEE_FLOAT? */ | ||
| 4488 | |||
| 4489 | if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64)) { | ||
| 4490 | runningPos += drwav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); | ||
| 4491 | } | ||
| 4492 | |||
| 4493 | pWav->dataChunkDataPos = runningPos; | ||
| 4494 | |||
| 4495 | /* "data" chunk. */ | ||
| 4496 | if (pFormat->container == drwav_container_riff) { | ||
| 4497 | drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize; | ||
| 4498 | runningPos += drwav__write(pWav, "data", 4); | ||
| 4499 | runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA); | ||
| 4500 | } else if (pFormat->container == drwav_container_w64) { | ||
| 4501 | drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */ | ||
| 4502 | runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16); | ||
| 4503 | runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA); | ||
| 4504 | } else if (pFormat->container == drwav_container_rf64) { | ||
| 4505 | runningPos += drwav__write(pWav, "data", 4); | ||
| 4506 | runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); /* Always set to 0xFFFFFFFF for RF64. The true size of the data chunk is specified in the ds64 chunk. */ | ||
| 4507 | } | ||
| 4508 | |||
| 4509 | /* Set some properties for the client's convenience. */ | ||
| 4510 | pWav->container = pFormat->container; | ||
| 4511 | pWav->channels = (drwav_uint16)pFormat->channels; | ||
| 4512 | pWav->sampleRate = pFormat->sampleRate; | ||
| 4513 | pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; | ||
| 4514 | pWav->translatedFormatTag = (drwav_uint16)pFormat->format; | ||
| 4515 | pWav->dataChunkDataPos = runningPos; | ||
| 4516 | |||
| 4517 | return DRWAV_TRUE; | ||
| 4518 | } | ||
| 4519 | |||
| 4520 | |||
| 4521 | DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 4522 | { | ||
| 4523 | if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { | ||
| 4524 | return DRWAV_FALSE; | ||
| 4525 | } | ||
| 4526 | |||
| 4527 | return drwav_init_write__internal(pWav, pFormat, 0); /* DRWAV_FALSE = Not Sequential */ | ||
| 4528 | } | ||
| 4529 | |||
| 4530 | DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 4531 | { | ||
| 4532 | if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { | ||
| 4533 | return DRWAV_FALSE; | ||
| 4534 | } | ||
| 4535 | |||
| 4536 | return drwav_init_write__internal(pWav, pFormat, totalSampleCount); /* DRWAV_TRUE = Sequential */ | ||
| 4537 | } | ||
| 4538 | |||
| 4539 | DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 4540 | { | ||
| 4541 | if (pFormat == NULL) { | ||
| 4542 | return DRWAV_FALSE; | ||
| 4543 | } | ||
| 4544 | |||
| 4545 | return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); | ||
| 4546 | } | ||
| 4547 | |||
| 4548 | DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount) | ||
| 4549 | { | ||
| 4550 | if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { | ||
| 4551 | return DRWAV_FALSE; | ||
| 4552 | } | ||
| 4553 | |||
| 4554 | pWav->pMetadata = pMetadata; | ||
| 4555 | pWav->metadataCount = metadataCount; | ||
| 4556 | |||
| 4557 | return drwav_init_write__internal(pWav, pFormat, 0); | ||
| 4558 | } | ||
| 4559 | |||
| 4560 | |||
| 4561 | DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount) | ||
| 4562 | { | ||
| 4563 | /* Casting totalFrameCount to drwav_int64 for VC6 compatibility. No issues in practice because nobody is going to exhaust the whole 63 bits. */ | ||
| 4564 | drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); | ||
| 4565 | drwav_uint64 riffChunkSizeBytes; | ||
| 4566 | drwav_uint64 fileSizeBytes = 0; | ||
| 4567 | |||
| 4568 | if (pFormat->container == drwav_container_riff) { | ||
| 4569 | riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); | ||
| 4570 | fileSizeBytes = (8 + riffChunkSizeBytes); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */ | ||
| 4571 | } else if (pFormat->container == drwav_container_w64) { | ||
| 4572 | riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes); | ||
| 4573 | fileSizeBytes = riffChunkSizeBytes; | ||
| 4574 | } else if (pFormat->container == drwav_container_rf64) { | ||
| 4575 | riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); | ||
| 4576 | fileSizeBytes = (8 + riffChunkSizeBytes); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */ | ||
| 4577 | } | ||
| 4578 | |||
| 4579 | return fileSizeBytes; | ||
| 4580 | } | ||
| 4581 | |||
| 4582 | |||
| 4583 | #ifndef DR_WAV_NO_STDIO | ||
| 4584 | |||
| 4585 | /* Errno */ | ||
| 4586 | /* drwav_result_from_errno() is only used for fopen() and wfopen() so putting it inside DR_WAV_NO_STDIO for now. If something else needs this later we can move it out. */ | ||
| 4587 | #include <errno.h> | ||
| 4588 | DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e) | ||
| 4589 | { | ||
| 4590 | switch (e) | ||
| 4591 | { | ||
| 4592 | case 0: return DRWAV_SUCCESS; | ||
| 4593 | #ifdef EPERM | ||
| 4594 | case EPERM: return DRWAV_INVALID_OPERATION; | ||
| 4595 | #endif | ||
| 4596 | #ifdef ENOENT | ||
| 4597 | case ENOENT: return DRWAV_DOES_NOT_EXIST; | ||
| 4598 | #endif | ||
| 4599 | #ifdef ESRCH | ||
| 4600 | case ESRCH: return DRWAV_DOES_NOT_EXIST; | ||
| 4601 | #endif | ||
| 4602 | #ifdef EINTR | ||
| 4603 | case EINTR: return DRWAV_INTERRUPT; | ||
| 4604 | #endif | ||
| 4605 | #ifdef EIO | ||
| 4606 | case EIO: return DRWAV_IO_ERROR; | ||
| 4607 | #endif | ||
| 4608 | #ifdef ENXIO | ||
| 4609 | case ENXIO: return DRWAV_DOES_NOT_EXIST; | ||
| 4610 | #endif | ||
| 4611 | #ifdef E2BIG | ||
| 4612 | case E2BIG: return DRWAV_INVALID_ARGS; | ||
| 4613 | #endif | ||
| 4614 | #ifdef ENOEXEC | ||
| 4615 | case ENOEXEC: return DRWAV_INVALID_FILE; | ||
| 4616 | #endif | ||
| 4617 | #ifdef EBADF | ||
| 4618 | case EBADF: return DRWAV_INVALID_FILE; | ||
| 4619 | #endif | ||
| 4620 | #ifdef ECHILD | ||
| 4621 | case ECHILD: return DRWAV_ERROR; | ||
| 4622 | #endif | ||
| 4623 | #ifdef EAGAIN | ||
| 4624 | case EAGAIN: return DRWAV_UNAVAILABLE; | ||
| 4625 | #endif | ||
| 4626 | #ifdef ENOMEM | ||
| 4627 | case ENOMEM: return DRWAV_OUT_OF_MEMORY; | ||
| 4628 | #endif | ||
| 4629 | #ifdef EACCES | ||
| 4630 | case EACCES: return DRWAV_ACCESS_DENIED; | ||
| 4631 | #endif | ||
| 4632 | #ifdef EFAULT | ||
| 4633 | case EFAULT: return DRWAV_BAD_ADDRESS; | ||
| 4634 | #endif | ||
| 4635 | #ifdef ENOTBLK | ||
| 4636 | case ENOTBLK: return DRWAV_ERROR; | ||
| 4637 | #endif | ||
| 4638 | #ifdef EBUSY | ||
| 4639 | case EBUSY: return DRWAV_BUSY; | ||
| 4640 | #endif | ||
| 4641 | #ifdef EEXIST | ||
| 4642 | case EEXIST: return DRWAV_ALREADY_EXISTS; | ||
| 4643 | #endif | ||
| 4644 | #ifdef EXDEV | ||
| 4645 | case EXDEV: return DRWAV_ERROR; | ||
| 4646 | #endif | ||
| 4647 | #ifdef ENODEV | ||
| 4648 | case ENODEV: return DRWAV_DOES_NOT_EXIST; | ||
| 4649 | #endif | ||
| 4650 | #ifdef ENOTDIR | ||
| 4651 | case ENOTDIR: return DRWAV_NOT_DIRECTORY; | ||
| 4652 | #endif | ||
| 4653 | #ifdef EISDIR | ||
| 4654 | case EISDIR: return DRWAV_IS_DIRECTORY; | ||
| 4655 | #endif | ||
| 4656 | #ifdef EINVAL | ||
| 4657 | case EINVAL: return DRWAV_INVALID_ARGS; | ||
| 4658 | #endif | ||
| 4659 | #ifdef ENFILE | ||
| 4660 | case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES; | ||
| 4661 | #endif | ||
| 4662 | #ifdef EMFILE | ||
| 4663 | case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES; | ||
| 4664 | #endif | ||
| 4665 | #ifdef ENOTTY | ||
| 4666 | case ENOTTY: return DRWAV_INVALID_OPERATION; | ||
| 4667 | #endif | ||
| 4668 | #ifdef ETXTBSY | ||
| 4669 | case ETXTBSY: return DRWAV_BUSY; | ||
| 4670 | #endif | ||
| 4671 | #ifdef EFBIG | ||
| 4672 | case EFBIG: return DRWAV_TOO_BIG; | ||
| 4673 | #endif | ||
| 4674 | #ifdef ENOSPC | ||
| 4675 | case ENOSPC: return DRWAV_NO_SPACE; | ||
| 4676 | #endif | ||
| 4677 | #ifdef ESPIPE | ||
| 4678 | case ESPIPE: return DRWAV_BAD_SEEK; | ||
| 4679 | #endif | ||
| 4680 | #ifdef EROFS | ||
| 4681 | case EROFS: return DRWAV_ACCESS_DENIED; | ||
| 4682 | #endif | ||
| 4683 | #ifdef EMLINK | ||
| 4684 | case EMLINK: return DRWAV_TOO_MANY_LINKS; | ||
| 4685 | #endif | ||
| 4686 | #ifdef EPIPE | ||
| 4687 | case EPIPE: return DRWAV_BAD_PIPE; | ||
| 4688 | #endif | ||
| 4689 | #ifdef EDOM | ||
| 4690 | case EDOM: return DRWAV_OUT_OF_RANGE; | ||
| 4691 | #endif | ||
| 4692 | #ifdef ERANGE | ||
| 4693 | case ERANGE: return DRWAV_OUT_OF_RANGE; | ||
| 4694 | #endif | ||
| 4695 | #ifdef EDEADLK | ||
| 4696 | case EDEADLK: return DRWAV_DEADLOCK; | ||
| 4697 | #endif | ||
| 4698 | #ifdef ENAMETOOLONG | ||
| 4699 | case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG; | ||
| 4700 | #endif | ||
| 4701 | #ifdef ENOLCK | ||
| 4702 | case ENOLCK: return DRWAV_ERROR; | ||
| 4703 | #endif | ||
| 4704 | #ifdef ENOSYS | ||
| 4705 | case ENOSYS: return DRWAV_NOT_IMPLEMENTED; | ||
| 4706 | #endif | ||
| 4707 | #ifdef ENOTEMPTY | ||
| 4708 | case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY; | ||
| 4709 | #endif | ||
| 4710 | #ifdef ELOOP | ||
| 4711 | case ELOOP: return DRWAV_TOO_MANY_LINKS; | ||
| 4712 | #endif | ||
| 4713 | #ifdef ENOMSG | ||
| 4714 | case ENOMSG: return DRWAV_NO_MESSAGE; | ||
| 4715 | #endif | ||
| 4716 | #ifdef EIDRM | ||
| 4717 | case EIDRM: return DRWAV_ERROR; | ||
| 4718 | #endif | ||
| 4719 | #ifdef ECHRNG | ||
| 4720 | case ECHRNG: return DRWAV_ERROR; | ||
| 4721 | #endif | ||
| 4722 | #ifdef EL2NSYNC | ||
| 4723 | case EL2NSYNC: return DRWAV_ERROR; | ||
| 4724 | #endif | ||
| 4725 | #ifdef EL3HLT | ||
| 4726 | case EL3HLT: return DRWAV_ERROR; | ||
| 4727 | #endif | ||
| 4728 | #ifdef EL3RST | ||
| 4729 | case EL3RST: return DRWAV_ERROR; | ||
| 4730 | #endif | ||
| 4731 | #ifdef ELNRNG | ||
| 4732 | case ELNRNG: return DRWAV_OUT_OF_RANGE; | ||
| 4733 | #endif | ||
| 4734 | #ifdef EUNATCH | ||
| 4735 | case EUNATCH: return DRWAV_ERROR; | ||
| 4736 | #endif | ||
| 4737 | #ifdef ENOCSI | ||
| 4738 | case ENOCSI: return DRWAV_ERROR; | ||
| 4739 | #endif | ||
| 4740 | #ifdef EL2HLT | ||
| 4741 | case EL2HLT: return DRWAV_ERROR; | ||
| 4742 | #endif | ||
| 4743 | #ifdef EBADE | ||
| 4744 | case EBADE: return DRWAV_ERROR; | ||
| 4745 | #endif | ||
| 4746 | #ifdef EBADR | ||
| 4747 | case EBADR: return DRWAV_ERROR; | ||
| 4748 | #endif | ||
| 4749 | #ifdef EXFULL | ||
| 4750 | case EXFULL: return DRWAV_ERROR; | ||
| 4751 | #endif | ||
| 4752 | #ifdef ENOANO | ||
| 4753 | case ENOANO: return DRWAV_ERROR; | ||
| 4754 | #endif | ||
| 4755 | #ifdef EBADRQC | ||
| 4756 | case EBADRQC: return DRWAV_ERROR; | ||
| 4757 | #endif | ||
| 4758 | #ifdef EBADSLT | ||
| 4759 | case EBADSLT: return DRWAV_ERROR; | ||
| 4760 | #endif | ||
| 4761 | #ifdef EBFONT | ||
| 4762 | case EBFONT: return DRWAV_INVALID_FILE; | ||
| 4763 | #endif | ||
| 4764 | #ifdef ENOSTR | ||
| 4765 | case ENOSTR: return DRWAV_ERROR; | ||
| 4766 | #endif | ||
| 4767 | #ifdef ENODATA | ||
| 4768 | case ENODATA: return DRWAV_NO_DATA_AVAILABLE; | ||
| 4769 | #endif | ||
| 4770 | #ifdef ETIME | ||
| 4771 | case ETIME: return DRWAV_TIMEOUT; | ||
| 4772 | #endif | ||
| 4773 | #ifdef ENOSR | ||
| 4774 | case ENOSR: return DRWAV_NO_DATA_AVAILABLE; | ||
| 4775 | #endif | ||
| 4776 | #ifdef ENONET | ||
| 4777 | case ENONET: return DRWAV_NO_NETWORK; | ||
| 4778 | #endif | ||
| 4779 | #ifdef ENOPKG | ||
| 4780 | case ENOPKG: return DRWAV_ERROR; | ||
| 4781 | #endif | ||
| 4782 | #ifdef EREMOTE | ||
| 4783 | case EREMOTE: return DRWAV_ERROR; | ||
| 4784 | #endif | ||
| 4785 | #ifdef ENOLINK | ||
| 4786 | case ENOLINK: return DRWAV_ERROR; | ||
| 4787 | #endif | ||
| 4788 | #ifdef EADV | ||
| 4789 | case EADV: return DRWAV_ERROR; | ||
| 4790 | #endif | ||
| 4791 | #ifdef ESRMNT | ||
| 4792 | case ESRMNT: return DRWAV_ERROR; | ||
| 4793 | #endif | ||
| 4794 | #ifdef ECOMM | ||
| 4795 | case ECOMM: return DRWAV_ERROR; | ||
| 4796 | #endif | ||
| 4797 | #ifdef EPROTO | ||
| 4798 | case EPROTO: return DRWAV_ERROR; | ||
| 4799 | #endif | ||
| 4800 | #ifdef EMULTIHOP | ||
| 4801 | case EMULTIHOP: return DRWAV_ERROR; | ||
| 4802 | #endif | ||
| 4803 | #ifdef EDOTDOT | ||
| 4804 | case EDOTDOT: return DRWAV_ERROR; | ||
| 4805 | #endif | ||
| 4806 | #ifdef EBADMSG | ||
| 4807 | case EBADMSG: return DRWAV_BAD_MESSAGE; | ||
| 4808 | #endif | ||
| 4809 | #ifdef EOVERFLOW | ||
| 4810 | case EOVERFLOW: return DRWAV_TOO_BIG; | ||
| 4811 | #endif | ||
| 4812 | #ifdef ENOTUNIQ | ||
| 4813 | case ENOTUNIQ: return DRWAV_NOT_UNIQUE; | ||
| 4814 | #endif | ||
| 4815 | #ifdef EBADFD | ||
| 4816 | case EBADFD: return DRWAV_ERROR; | ||
| 4817 | #endif | ||
| 4818 | #ifdef EREMCHG | ||
| 4819 | case EREMCHG: return DRWAV_ERROR; | ||
| 4820 | #endif | ||
| 4821 | #ifdef ELIBACC | ||
| 4822 | case ELIBACC: return DRWAV_ACCESS_DENIED; | ||
| 4823 | #endif | ||
| 4824 | #ifdef ELIBBAD | ||
| 4825 | case ELIBBAD: return DRWAV_INVALID_FILE; | ||
| 4826 | #endif | ||
| 4827 | #ifdef ELIBSCN | ||
| 4828 | case ELIBSCN: return DRWAV_INVALID_FILE; | ||
| 4829 | #endif | ||
| 4830 | #ifdef ELIBMAX | ||
| 4831 | case ELIBMAX: return DRWAV_ERROR; | ||
| 4832 | #endif | ||
| 4833 | #ifdef ELIBEXEC | ||
| 4834 | case ELIBEXEC: return DRWAV_ERROR; | ||
| 4835 | #endif | ||
| 4836 | #ifdef EILSEQ | ||
| 4837 | case EILSEQ: return DRWAV_INVALID_DATA; | ||
| 4838 | #endif | ||
| 4839 | #ifdef ERESTART | ||
| 4840 | case ERESTART: return DRWAV_ERROR; | ||
| 4841 | #endif | ||
| 4842 | #ifdef ESTRPIPE | ||
| 4843 | case ESTRPIPE: return DRWAV_ERROR; | ||
| 4844 | #endif | ||
| 4845 | #ifdef EUSERS | ||
| 4846 | case EUSERS: return DRWAV_ERROR; | ||
| 4847 | #endif | ||
| 4848 | #ifdef ENOTSOCK | ||
| 4849 | case ENOTSOCK: return DRWAV_NOT_SOCKET; | ||
| 4850 | #endif | ||
| 4851 | #ifdef EDESTADDRREQ | ||
| 4852 | case EDESTADDRREQ: return DRWAV_NO_ADDRESS; | ||
| 4853 | #endif | ||
| 4854 | #ifdef EMSGSIZE | ||
| 4855 | case EMSGSIZE: return DRWAV_TOO_BIG; | ||
| 4856 | #endif | ||
| 4857 | #ifdef EPROTOTYPE | ||
| 4858 | case EPROTOTYPE: return DRWAV_BAD_PROTOCOL; | ||
| 4859 | #endif | ||
| 4860 | #ifdef ENOPROTOOPT | ||
| 4861 | case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE; | ||
| 4862 | #endif | ||
| 4863 | #ifdef EPROTONOSUPPORT | ||
| 4864 | case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED; | ||
| 4865 | #endif | ||
| 4866 | #ifdef ESOCKTNOSUPPORT | ||
| 4867 | case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED; | ||
| 4868 | #endif | ||
| 4869 | #ifdef EOPNOTSUPP | ||
| 4870 | case EOPNOTSUPP: return DRWAV_INVALID_OPERATION; | ||
| 4871 | #endif | ||
| 4872 | #ifdef EPFNOSUPPORT | ||
| 4873 | case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED; | ||
| 4874 | #endif | ||
| 4875 | #ifdef EAFNOSUPPORT | ||
| 4876 | case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED; | ||
| 4877 | #endif | ||
| 4878 | #ifdef EADDRINUSE | ||
| 4879 | case EADDRINUSE: return DRWAV_ALREADY_IN_USE; | ||
| 4880 | #endif | ||
| 4881 | #ifdef EADDRNOTAVAIL | ||
| 4882 | case EADDRNOTAVAIL: return DRWAV_ERROR; | ||
| 4883 | #endif | ||
| 4884 | #ifdef ENETDOWN | ||
| 4885 | case ENETDOWN: return DRWAV_NO_NETWORK; | ||
| 4886 | #endif | ||
| 4887 | #ifdef ENETUNREACH | ||
| 4888 | case ENETUNREACH: return DRWAV_NO_NETWORK; | ||
| 4889 | #endif | ||
| 4890 | #ifdef ENETRESET | ||
| 4891 | case ENETRESET: return DRWAV_NO_NETWORK; | ||
| 4892 | #endif | ||
| 4893 | #ifdef ECONNABORTED | ||
| 4894 | case ECONNABORTED: return DRWAV_NO_NETWORK; | ||
| 4895 | #endif | ||
| 4896 | #ifdef ECONNRESET | ||
| 4897 | case ECONNRESET: return DRWAV_CONNECTION_RESET; | ||
| 4898 | #endif | ||
| 4899 | #ifdef ENOBUFS | ||
| 4900 | case ENOBUFS: return DRWAV_NO_SPACE; | ||
| 4901 | #endif | ||
| 4902 | #ifdef EISCONN | ||
| 4903 | case EISCONN: return DRWAV_ALREADY_CONNECTED; | ||
| 4904 | #endif | ||
| 4905 | #ifdef ENOTCONN | ||
| 4906 | case ENOTCONN: return DRWAV_NOT_CONNECTED; | ||
| 4907 | #endif | ||
| 4908 | #ifdef ESHUTDOWN | ||
| 4909 | case ESHUTDOWN: return DRWAV_ERROR; | ||
| 4910 | #endif | ||
| 4911 | #ifdef ETOOMANYREFS | ||
| 4912 | case ETOOMANYREFS: return DRWAV_ERROR; | ||
| 4913 | #endif | ||
| 4914 | #ifdef ETIMEDOUT | ||
| 4915 | case ETIMEDOUT: return DRWAV_TIMEOUT; | ||
| 4916 | #endif | ||
| 4917 | #ifdef ECONNREFUSED | ||
| 4918 | case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED; | ||
| 4919 | #endif | ||
| 4920 | #ifdef EHOSTDOWN | ||
| 4921 | case EHOSTDOWN: return DRWAV_NO_HOST; | ||
| 4922 | #endif | ||
| 4923 | #ifdef EHOSTUNREACH | ||
| 4924 | case EHOSTUNREACH: return DRWAV_NO_HOST; | ||
| 4925 | #endif | ||
| 4926 | #ifdef EALREADY | ||
| 4927 | case EALREADY: return DRWAV_IN_PROGRESS; | ||
| 4928 | #endif | ||
| 4929 | #ifdef EINPROGRESS | ||
| 4930 | case EINPROGRESS: return DRWAV_IN_PROGRESS; | ||
| 4931 | #endif | ||
| 4932 | #ifdef ESTALE | ||
| 4933 | case ESTALE: return DRWAV_INVALID_FILE; | ||
| 4934 | #endif | ||
| 4935 | #ifdef EUCLEAN | ||
| 4936 | case EUCLEAN: return DRWAV_ERROR; | ||
| 4937 | #endif | ||
| 4938 | #ifdef ENOTNAM | ||
| 4939 | case ENOTNAM: return DRWAV_ERROR; | ||
| 4940 | #endif | ||
| 4941 | #ifdef ENAVAIL | ||
| 4942 | case ENAVAIL: return DRWAV_ERROR; | ||
| 4943 | #endif | ||
| 4944 | #ifdef EISNAM | ||
| 4945 | case EISNAM: return DRWAV_ERROR; | ||
| 4946 | #endif | ||
| 4947 | #ifdef EREMOTEIO | ||
| 4948 | case EREMOTEIO: return DRWAV_IO_ERROR; | ||
| 4949 | #endif | ||
| 4950 | #ifdef EDQUOT | ||
| 4951 | case EDQUOT: return DRWAV_NO_SPACE; | ||
| 4952 | #endif | ||
| 4953 | #ifdef ENOMEDIUM | ||
| 4954 | case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST; | ||
| 4955 | #endif | ||
| 4956 | #ifdef EMEDIUMTYPE | ||
| 4957 | case EMEDIUMTYPE: return DRWAV_ERROR; | ||
| 4958 | #endif | ||
| 4959 | #ifdef ECANCELED | ||
| 4960 | case ECANCELED: return DRWAV_CANCELLED; | ||
| 4961 | #endif | ||
| 4962 | #ifdef ENOKEY | ||
| 4963 | case ENOKEY: return DRWAV_ERROR; | ||
| 4964 | #endif | ||
| 4965 | #ifdef EKEYEXPIRED | ||
| 4966 | case EKEYEXPIRED: return DRWAV_ERROR; | ||
| 4967 | #endif | ||
| 4968 | #ifdef EKEYREVOKED | ||
| 4969 | case EKEYREVOKED: return DRWAV_ERROR; | ||
| 4970 | #endif | ||
| 4971 | #ifdef EKEYREJECTED | ||
| 4972 | case EKEYREJECTED: return DRWAV_ERROR; | ||
| 4973 | #endif | ||
| 4974 | #ifdef EOWNERDEAD | ||
| 4975 | case EOWNERDEAD: return DRWAV_ERROR; | ||
| 4976 | #endif | ||
| 4977 | #ifdef ENOTRECOVERABLE | ||
| 4978 | case ENOTRECOVERABLE: return DRWAV_ERROR; | ||
| 4979 | #endif | ||
| 4980 | #ifdef ERFKILL | ||
| 4981 | case ERFKILL: return DRWAV_ERROR; | ||
| 4982 | #endif | ||
| 4983 | #ifdef EHWPOISON | ||
| 4984 | case EHWPOISON: return DRWAV_ERROR; | ||
| 4985 | #endif | ||
| 4986 | default: return DRWAV_ERROR; | ||
| 4987 | } | ||
| 4988 | } | ||
| 4989 | /* End Errno */ | ||
| 4990 | |||
| 4991 | /* fopen */ | ||
| 4992 | DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) | ||
| 4993 | { | ||
| 4994 | #if defined(_MSC_VER) && _MSC_VER >= 1400 | ||
| 4995 | errno_t err; | ||
| 4996 | #endif | ||
| 4997 | |||
| 4998 | if (ppFile != NULL) { | ||
| 4999 | *ppFile = NULL; /* Safety. */ | ||
| 5000 | } | ||
| 5001 | |||
| 5002 | if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { | ||
| 5003 | return DRWAV_INVALID_ARGS; | ||
| 5004 | } | ||
| 5005 | |||
| 5006 | #if defined(_MSC_VER) && _MSC_VER >= 1400 | ||
| 5007 | err = fopen_s(ppFile, pFilePath, pOpenMode); | ||
| 5008 | if (err != 0) { | ||
| 5009 | return drwav_result_from_errno(err); | ||
| 5010 | } | ||
| 5011 | #else | ||
| 5012 | #if defined(_WIN32) || defined(__APPLE__) | ||
| 5013 | *ppFile = fopen(pFilePath, pOpenMode); | ||
| 5014 | #else | ||
| 5015 | #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) | ||
| 5016 | *ppFile = fopen64(pFilePath, pOpenMode); | ||
| 5017 | #else | ||
| 5018 | *ppFile = fopen(pFilePath, pOpenMode); | ||
| 5019 | #endif | ||
| 5020 | #endif | ||
| 5021 | if (*ppFile == NULL) { | ||
| 5022 | drwav_result result = drwav_result_from_errno(errno); | ||
| 5023 | if (result == DRWAV_SUCCESS) { | ||
| 5024 | result = DRWAV_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ | ||
| 5025 | } | ||
| 5026 | |||
| 5027 | return result; | ||
| 5028 | } | ||
| 5029 | #endif | ||
| 5030 | |||
| 5031 | return DRWAV_SUCCESS; | ||
| 5032 | } | ||
| 5033 | |||
| 5034 | /* | ||
| 5035 | _wfopen() isn't always available in all compilation environments. | ||
| 5036 | |||
| 5037 | * Windows only. | ||
| 5038 | * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). | ||
| 5039 | * MinGW-64 (both 32- and 64-bit) seems to support it. | ||
| 5040 | * MinGW wraps it in !defined(__STRICT_ANSI__). | ||
| 5041 | * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). | ||
| 5042 | |||
| 5043 | This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() | ||
| 5044 | fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. | ||
| 5045 | */ | ||
| 5046 | #if defined(_WIN32) | ||
| 5047 | #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) | ||
| 5048 | #define DRWAV_HAS_WFOPEN | ||
| 5049 | #endif | ||
| 5050 | #endif | ||
| 5051 | |||
| 5052 | #ifndef DR_WAV_NO_WCHAR | ||
| 5053 | DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5054 | { | ||
| 5055 | if (ppFile != NULL) { | ||
| 5056 | *ppFile = NULL; /* Safety. */ | ||
| 5057 | } | ||
| 5058 | |||
| 5059 | if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { | ||
| 5060 | return DRWAV_INVALID_ARGS; | ||
| 5061 | } | ||
| 5062 | |||
| 5063 | #if defined(DRWAV_HAS_WFOPEN) | ||
| 5064 | { | ||
| 5065 | /* Use _wfopen() on Windows. */ | ||
| 5066 | #if defined(_MSC_VER) && _MSC_VER >= 1400 | ||
| 5067 | errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); | ||
| 5068 | if (err != 0) { | ||
| 5069 | return drwav_result_from_errno(err); | ||
| 5070 | } | ||
| 5071 | #else | ||
| 5072 | *ppFile = _wfopen(pFilePath, pOpenMode); | ||
| 5073 | if (*ppFile == NULL) { | ||
| 5074 | return drwav_result_from_errno(errno); | ||
| 5075 | } | ||
| 5076 | #endif | ||
| 5077 | (void)pAllocationCallbacks; | ||
| 5078 | } | ||
| 5079 | #else | ||
| 5080 | /* | ||
| 5081 | Use fopen() on anything other than Windows. Requires a conversion. This is annoying because | ||
| 5082 | fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note | ||
| 5083 | that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for | ||
| 5084 | maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler | ||
| 5085 | error I'll look into improving compatibility. | ||
| 5086 | */ | ||
| 5087 | |||
| 5088 | /* | ||
| 5089 | Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just | ||
| 5090 | need to abort with an error. If you encounter a compiler lacking such support, add it to this list | ||
| 5091 | and submit a bug report and it'll be added to the library upstream. | ||
| 5092 | */ | ||
| 5093 | #if defined(__DJGPP__) | ||
| 5094 | { | ||
| 5095 | /* Nothing to do here. This will fall through to the error check below. */ | ||
| 5096 | } | ||
| 5097 | #else | ||
| 5098 | { | ||
| 5099 | mbstate_t mbs; | ||
| 5100 | size_t lenMB; | ||
| 5101 | const wchar_t* pFilePathTemp = pFilePath; | ||
| 5102 | char* pFilePathMB = NULL; | ||
| 5103 | char pOpenModeMB[32] = {0}; | ||
| 5104 | |||
| 5105 | /* Get the length first. */ | ||
| 5106 | DRWAV_ZERO_OBJECT(&mbs); | ||
| 5107 | lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); | ||
| 5108 | if (lenMB == (size_t)-1) { | ||
| 5109 | return drwav_result_from_errno(errno); | ||
| 5110 | } | ||
| 5111 | |||
| 5112 | pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); | ||
| 5113 | if (pFilePathMB == NULL) { | ||
| 5114 | return DRWAV_OUT_OF_MEMORY; | ||
| 5115 | } | ||
| 5116 | |||
| 5117 | pFilePathTemp = pFilePath; | ||
| 5118 | DRWAV_ZERO_OBJECT(&mbs); | ||
| 5119 | wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); | ||
| 5120 | |||
| 5121 | /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ | ||
| 5122 | { | ||
| 5123 | size_t i = 0; | ||
| 5124 | for (;;) { | ||
| 5125 | if (pOpenMode[i] == 0) { | ||
| 5126 | pOpenModeMB[i] = '\0'; | ||
| 5127 | break; | ||
| 5128 | } | ||
| 5129 | |||
| 5130 | pOpenModeMB[i] = (char)pOpenMode[i]; | ||
| 5131 | i += 1; | ||
| 5132 | } | ||
| 5133 | } | ||
| 5134 | |||
| 5135 | *ppFile = fopen(pFilePathMB, pOpenModeMB); | ||
| 5136 | |||
| 5137 | drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks); | ||
| 5138 | } | ||
| 5139 | #endif | ||
| 5140 | |||
| 5141 | if (*ppFile == NULL) { | ||
| 5142 | return DRWAV_ERROR; | ||
| 5143 | } | ||
| 5144 | #endif | ||
| 5145 | |||
| 5146 | return DRWAV_SUCCESS; | ||
| 5147 | } | ||
| 5148 | #endif | ||
| 5149 | /* End fopen */ | ||
| 5150 | |||
| 5151 | |||
| 5152 | DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) | ||
| 5153 | { | ||
| 5154 | return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); | ||
| 5155 | } | ||
| 5156 | |||
| 5157 | DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) | ||
| 5158 | { | ||
| 5159 | return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); | ||
| 5160 | } | ||
| 5161 | |||
| 5162 | DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) | ||
| 5163 | { | ||
| 5164 | return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; | ||
| 5165 | } | ||
| 5166 | |||
| 5167 | DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5168 | { | ||
| 5169 | return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); | ||
| 5170 | } | ||
| 5171 | |||
| 5172 | |||
| 5173 | DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5174 | { | ||
| 5175 | drwav_bool32 result; | ||
| 5176 | |||
| 5177 | result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); | ||
| 5178 | if (result != DRWAV_TRUE) { | ||
| 5179 | fclose(pFile); | ||
| 5180 | return result; | ||
| 5181 | } | ||
| 5182 | |||
| 5183 | result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags); | ||
| 5184 | if (result != DRWAV_TRUE) { | ||
| 5185 | fclose(pFile); | ||
| 5186 | return result; | ||
| 5187 | } | ||
| 5188 | |||
| 5189 | return DRWAV_TRUE; | ||
| 5190 | } | ||
| 5191 | |||
| 5192 | DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5193 | { | ||
| 5194 | FILE* pFile; | ||
| 5195 | if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { | ||
| 5196 | return DRWAV_FALSE; | ||
| 5197 | } | ||
| 5198 | |||
| 5199 | /* This takes ownership of the FILE* object. */ | ||
| 5200 | return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); | ||
| 5201 | } | ||
| 5202 | |||
| 5203 | #ifndef DR_WAV_NO_WCHAR | ||
| 5204 | DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5205 | { | ||
| 5206 | return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); | ||
| 5207 | } | ||
| 5208 | |||
| 5209 | DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5210 | { | ||
| 5211 | FILE* pFile; | ||
| 5212 | if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { | ||
| 5213 | return DRWAV_FALSE; | ||
| 5214 | } | ||
| 5215 | |||
| 5216 | /* This takes ownership of the FILE* object. */ | ||
| 5217 | return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); | ||
| 5218 | } | ||
| 5219 | #endif | ||
| 5220 | |||
| 5221 | DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5222 | { | ||
| 5223 | FILE* pFile; | ||
| 5224 | if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { | ||
| 5225 | return DRWAV_FALSE; | ||
| 5226 | } | ||
| 5227 | |||
| 5228 | /* This takes ownership of the FILE* object. */ | ||
| 5229 | return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | DRWAV_WITH_METADATA, pAllocationCallbacks); | ||
| 5230 | } | ||
| 5231 | |||
| 5232 | #ifndef DR_WAV_NO_WCHAR | ||
| 5233 | DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5234 | { | ||
| 5235 | FILE* pFile; | ||
| 5236 | if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { | ||
| 5237 | return DRWAV_FALSE; | ||
| 5238 | } | ||
| 5239 | |||
| 5240 | /* This takes ownership of the FILE* object. */ | ||
| 5241 | return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | DRWAV_WITH_METADATA, pAllocationCallbacks); | ||
| 5242 | } | ||
| 5243 | #endif | ||
| 5244 | |||
| 5245 | |||
| 5246 | DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5247 | { | ||
| 5248 | drwav_bool32 result; | ||
| 5249 | |||
| 5250 | result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); | ||
| 5251 | if (result != DRWAV_TRUE) { | ||
| 5252 | fclose(pFile); | ||
| 5253 | return result; | ||
| 5254 | } | ||
| 5255 | |||
| 5256 | result = drwav_init_write__internal(pWav, pFormat, totalSampleCount); | ||
| 5257 | if (result != DRWAV_TRUE) { | ||
| 5258 | fclose(pFile); | ||
| 5259 | return result; | ||
| 5260 | } | ||
| 5261 | |||
| 5262 | return DRWAV_TRUE; | ||
| 5263 | } | ||
| 5264 | |||
| 5265 | DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5266 | { | ||
| 5267 | FILE* pFile; | ||
| 5268 | if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) { | ||
| 5269 | return DRWAV_FALSE; | ||
| 5270 | } | ||
| 5271 | |||
| 5272 | /* This takes ownership of the FILE* object. */ | ||
| 5273 | return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); | ||
| 5274 | } | ||
| 5275 | |||
| 5276 | #ifndef DR_WAV_NO_WCHAR | ||
| 5277 | DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5278 | { | ||
| 5279 | FILE* pFile; | ||
| 5280 | if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) { | ||
| 5281 | return DRWAV_FALSE; | ||
| 5282 | } | ||
| 5283 | |||
| 5284 | /* This takes ownership of the FILE* object. */ | ||
| 5285 | return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); | ||
| 5286 | } | ||
| 5287 | #endif | ||
| 5288 | |||
| 5289 | DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5290 | { | ||
| 5291 | return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); | ||
| 5292 | } | ||
| 5293 | |||
| 5294 | DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5295 | { | ||
| 5296 | return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); | ||
| 5297 | } | ||
| 5298 | |||
| 5299 | DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5300 | { | ||
| 5301 | if (pFormat == NULL) { | ||
| 5302 | return DRWAV_FALSE; | ||
| 5303 | } | ||
| 5304 | |||
| 5305 | return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); | ||
| 5306 | } | ||
| 5307 | |||
| 5308 | #ifndef DR_WAV_NO_WCHAR | ||
| 5309 | DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5310 | { | ||
| 5311 | return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); | ||
| 5312 | } | ||
| 5313 | |||
| 5314 | DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5315 | { | ||
| 5316 | return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); | ||
| 5317 | } | ||
| 5318 | |||
| 5319 | DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5320 | { | ||
| 5321 | if (pFormat == NULL) { | ||
| 5322 | return DRWAV_FALSE; | ||
| 5323 | } | ||
| 5324 | |||
| 5325 | return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); | ||
| 5326 | } | ||
| 5327 | #endif | ||
| 5328 | #endif /* DR_WAV_NO_STDIO */ | ||
| 5329 | |||
| 5330 | |||
| 5331 | DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) | ||
| 5332 | { | ||
| 5333 | drwav* pWav = (drwav*)pUserData; | ||
| 5334 | size_t bytesRemaining; | ||
| 5335 | |||
| 5336 | DRWAV_ASSERT(pWav != NULL); | ||
| 5337 | DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); | ||
| 5338 | |||
| 5339 | bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; | ||
| 5340 | if (bytesToRead > bytesRemaining) { | ||
| 5341 | bytesToRead = bytesRemaining; | ||
| 5342 | } | ||
| 5343 | |||
| 5344 | if (bytesToRead > 0) { | ||
| 5345 | DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); | ||
| 5346 | pWav->memoryStream.currentReadPos += bytesToRead; | ||
| 5347 | } | ||
| 5348 | |||
| 5349 | return bytesToRead; | ||
| 5350 | } | ||
| 5351 | |||
| 5352 | DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin) | ||
| 5353 | { | ||
| 5354 | drwav* pWav = (drwav*)pUserData; | ||
| 5355 | DRWAV_ASSERT(pWav != NULL); | ||
| 5356 | |||
| 5357 | if (origin == drwav_seek_origin_current) { | ||
| 5358 | if (offset > 0) { | ||
| 5359 | if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { | ||
| 5360 | return DRWAV_FALSE; /* Trying to seek too far forward. */ | ||
| 5361 | } | ||
| 5362 | } else { | ||
| 5363 | if (pWav->memoryStream.currentReadPos < (size_t)-offset) { | ||
| 5364 | return DRWAV_FALSE; /* Trying to seek too far backwards. */ | ||
| 5365 | } | ||
| 5366 | } | ||
| 5367 | |||
| 5368 | /* This will never underflow thanks to the clamps above. */ | ||
| 5369 | pWav->memoryStream.currentReadPos += offset; | ||
| 5370 | } else { | ||
| 5371 | if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) { | ||
| 5372 | pWav->memoryStream.currentReadPos = offset; | ||
| 5373 | } else { | ||
| 5374 | return DRWAV_FALSE; /* Trying to seek too far forward. */ | ||
| 5375 | } | ||
| 5376 | } | ||
| 5377 | |||
| 5378 | return DRWAV_TRUE; | ||
| 5379 | } | ||
| 5380 | |||
| 5381 | DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) | ||
| 5382 | { | ||
| 5383 | drwav* pWav = (drwav*)pUserData; | ||
| 5384 | size_t bytesRemaining; | ||
| 5385 | |||
| 5386 | DRWAV_ASSERT(pWav != NULL); | ||
| 5387 | DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); | ||
| 5388 | |||
| 5389 | bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; | ||
| 5390 | if (bytesRemaining < bytesToWrite) { | ||
| 5391 | /* Need to reallocate. */ | ||
| 5392 | void* pNewData; | ||
| 5393 | size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; | ||
| 5394 | |||
| 5395 | /* If doubling wasn't enough, just make it the minimum required size to write the data. */ | ||
| 5396 | if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { | ||
| 5397 | newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; | ||
| 5398 | } | ||
| 5399 | |||
| 5400 | pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); | ||
| 5401 | if (pNewData == NULL) { | ||
| 5402 | return 0; | ||
| 5403 | } | ||
| 5404 | |||
| 5405 | *pWav->memoryStreamWrite.ppData = pNewData; | ||
| 5406 | pWav->memoryStreamWrite.dataCapacity = newDataCapacity; | ||
| 5407 | } | ||
| 5408 | |||
| 5409 | DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); | ||
| 5410 | |||
| 5411 | pWav->memoryStreamWrite.currentWritePos += bytesToWrite; | ||
| 5412 | if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { | ||
| 5413 | pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; | ||
| 5414 | } | ||
| 5415 | |||
| 5416 | *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; | ||
| 5417 | |||
| 5418 | return bytesToWrite; | ||
| 5419 | } | ||
| 5420 | |||
| 5421 | DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin) | ||
| 5422 | { | ||
| 5423 | drwav* pWav = (drwav*)pUserData; | ||
| 5424 | DRWAV_ASSERT(pWav != NULL); | ||
| 5425 | |||
| 5426 | if (origin == drwav_seek_origin_current) { | ||
| 5427 | if (offset > 0) { | ||
| 5428 | if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { | ||
| 5429 | offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); /* Trying to seek too far forward. */ | ||
| 5430 | } | ||
| 5431 | } else { | ||
| 5432 | if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { | ||
| 5433 | offset = -(int)pWav->memoryStreamWrite.currentWritePos; /* Trying to seek too far backwards. */ | ||
| 5434 | } | ||
| 5435 | } | ||
| 5436 | |||
| 5437 | /* This will never underflow thanks to the clamps above. */ | ||
| 5438 | pWav->memoryStreamWrite.currentWritePos += offset; | ||
| 5439 | } else { | ||
| 5440 | if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) { | ||
| 5441 | pWav->memoryStreamWrite.currentWritePos = offset; | ||
| 5442 | } else { | ||
| 5443 | pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; /* Trying to seek too far forward. */ | ||
| 5444 | } | ||
| 5445 | } | ||
| 5446 | |||
| 5447 | return DRWAV_TRUE; | ||
| 5448 | } | ||
| 5449 | |||
| 5450 | DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5451 | { | ||
| 5452 | return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); | ||
| 5453 | } | ||
| 5454 | |||
| 5455 | DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5456 | { | ||
| 5457 | if (data == NULL || dataSize == 0) { | ||
| 5458 | return DRWAV_FALSE; | ||
| 5459 | } | ||
| 5460 | |||
| 5461 | if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { | ||
| 5462 | return DRWAV_FALSE; | ||
| 5463 | } | ||
| 5464 | |||
| 5465 | pWav->memoryStream.data = (const drwav_uint8*)data; | ||
| 5466 | pWav->memoryStream.dataSize = dataSize; | ||
| 5467 | pWav->memoryStream.currentReadPos = 0; | ||
| 5468 | |||
| 5469 | return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); | ||
| 5470 | } | ||
| 5471 | |||
| 5472 | DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5473 | { | ||
| 5474 | if (data == NULL || dataSize == 0) { | ||
| 5475 | return DRWAV_FALSE; | ||
| 5476 | } | ||
| 5477 | |||
| 5478 | if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { | ||
| 5479 | return DRWAV_FALSE; | ||
| 5480 | } | ||
| 5481 | |||
| 5482 | pWav->memoryStream.data = (const drwav_uint8*)data; | ||
| 5483 | pWav->memoryStream.dataSize = dataSize; | ||
| 5484 | pWav->memoryStream.currentReadPos = 0; | ||
| 5485 | |||
| 5486 | return drwav_init__internal(pWav, NULL, NULL, flags | DRWAV_WITH_METADATA); | ||
| 5487 | } | ||
| 5488 | |||
| 5489 | |||
| 5490 | DRWAV_PRIVATE drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5491 | { | ||
| 5492 | if (ppData == NULL || pDataSize == NULL) { | ||
| 5493 | return DRWAV_FALSE; | ||
| 5494 | } | ||
| 5495 | |||
| 5496 | *ppData = NULL; /* Important because we're using realloc()! */ | ||
| 5497 | *pDataSize = 0; | ||
| 5498 | |||
| 5499 | if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) { | ||
| 5500 | return DRWAV_FALSE; | ||
| 5501 | } | ||
| 5502 | |||
| 5503 | pWav->memoryStreamWrite.ppData = ppData; | ||
| 5504 | pWav->memoryStreamWrite.pDataSize = pDataSize; | ||
| 5505 | pWav->memoryStreamWrite.dataSize = 0; | ||
| 5506 | pWav->memoryStreamWrite.dataCapacity = 0; | ||
| 5507 | pWav->memoryStreamWrite.currentWritePos = 0; | ||
| 5508 | |||
| 5509 | return drwav_init_write__internal(pWav, pFormat, totalSampleCount); | ||
| 5510 | } | ||
| 5511 | |||
| 5512 | DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5513 | { | ||
| 5514 | return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); | ||
| 5515 | } | ||
| 5516 | |||
| 5517 | DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5518 | { | ||
| 5519 | return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); | ||
| 5520 | } | ||
| 5521 | |||
| 5522 | DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 5523 | { | ||
| 5524 | if (pFormat == NULL) { | ||
| 5525 | return DRWAV_FALSE; | ||
| 5526 | } | ||
| 5527 | |||
| 5528 | return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); | ||
| 5529 | } | ||
| 5530 | |||
| 5531 | |||
| 5532 | |||
| 5533 | DRWAV_API drwav_result drwav_uninit(drwav* pWav) | ||
| 5534 | { | ||
| 5535 | drwav_result result = DRWAV_SUCCESS; | ||
| 5536 | |||
| 5537 | if (pWav == NULL) { | ||
| 5538 | return DRWAV_INVALID_ARGS; | ||
| 5539 | } | ||
| 5540 | |||
| 5541 | /* | ||
| 5542 | If the drwav object was opened in write mode we'll need to finalize a few things: | ||
| 5543 | - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers. | ||
| 5544 | - Set the size of the "data" chunk. | ||
| 5545 | */ | ||
| 5546 | if (pWav->onWrite != NULL) { | ||
| 5547 | drwav_uint32 paddingSize = 0; | ||
| 5548 | |||
| 5549 | /* Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding. */ | ||
| 5550 | if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { | ||
| 5551 | paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize); | ||
| 5552 | } else { | ||
| 5553 | paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize); | ||
| 5554 | } | ||
| 5555 | |||
| 5556 | if (paddingSize > 0) { | ||
| 5557 | drwav_uint64 paddingData = 0; | ||
| 5558 | drwav__write(pWav, &paddingData, paddingSize); /* Byte order does not matter for this. */ | ||
| 5559 | } | ||
| 5560 | |||
| 5561 | /* | ||
| 5562 | Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need | ||
| 5563 | to do this when using non-sequential mode. | ||
| 5564 | */ | ||
| 5565 | if (pWav->onSeek && !pWav->isSequentialWrite) { | ||
| 5566 | if (pWav->container == drwav_container_riff) { | ||
| 5567 | /* The "RIFF" chunk size. */ | ||
| 5568 | if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { | ||
| 5569 | drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); | ||
| 5570 | drwav__write_u32ne_to_le(pWav, riffChunkSize); | ||
| 5571 | } | ||
| 5572 | |||
| 5573 | /* The "data" chunk size. */ | ||
| 5574 | if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) { | ||
| 5575 | drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize); | ||
| 5576 | drwav__write_u32ne_to_le(pWav, dataChunkSize); | ||
| 5577 | } | ||
| 5578 | } else if (pWav->container == drwav_container_w64) { | ||
| 5579 | /* The "RIFF" chunk size. */ | ||
| 5580 | if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { | ||
| 5581 | drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize); | ||
| 5582 | drwav__write_u64ne_to_le(pWav, riffChunkSize); | ||
| 5583 | } | ||
| 5584 | |||
| 5585 | /* The "data" chunk size. */ | ||
| 5586 | if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) { | ||
| 5587 | drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize); | ||
| 5588 | drwav__write_u64ne_to_le(pWav, dataChunkSize); | ||
| 5589 | } | ||
| 5590 | } else if (pWav->container == drwav_container_rf64) { | ||
| 5591 | /* We only need to update the ds64 chunk. The "RIFF" and "data" chunks always have their sizes set to 0xFFFFFFFF for RF64. */ | ||
| 5592 | int ds64BodyPos = 12 + 8; | ||
| 5593 | |||
| 5594 | /* The "RIFF" chunk size. */ | ||
| 5595 | if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) { | ||
| 5596 | drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); | ||
| 5597 | drwav__write_u64ne_to_le(pWav, riffChunkSize); | ||
| 5598 | } | ||
| 5599 | |||
| 5600 | /* The "data" chunk size. */ | ||
| 5601 | if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) { | ||
| 5602 | drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize); | ||
| 5603 | drwav__write_u64ne_to_le(pWav, dataChunkSize); | ||
| 5604 | } | ||
| 5605 | } | ||
| 5606 | } | ||
| 5607 | |||
| 5608 | /* Validation for sequential mode. */ | ||
| 5609 | if (pWav->isSequentialWrite) { | ||
| 5610 | if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { | ||
| 5611 | result = DRWAV_INVALID_FILE; | ||
| 5612 | } | ||
| 5613 | } | ||
| 5614 | } else { | ||
| 5615 | drwav_free(pWav->pMetadata, &pWav->allocationCallbacks); | ||
| 5616 | } | ||
| 5617 | |||
| 5618 | #ifndef DR_WAV_NO_STDIO | ||
| 5619 | /* | ||
| 5620 | If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file() | ||
| 5621 | was used by looking at the onRead and onSeek callbacks. | ||
| 5622 | */ | ||
| 5623 | if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) { | ||
| 5624 | fclose((FILE*)pWav->pUserData); | ||
| 5625 | } | ||
| 5626 | #endif | ||
| 5627 | |||
| 5628 | return result; | ||
| 5629 | } | ||
| 5630 | |||
| 5631 | |||
| 5632 | |||
| 5633 | DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut) | ||
| 5634 | { | ||
| 5635 | size_t bytesRead; | ||
| 5636 | drwav_uint32 bytesPerFrame; | ||
| 5637 | |||
| 5638 | if (pWav == NULL || bytesToRead == 0) { | ||
| 5639 | return 0; /* Invalid args. */ | ||
| 5640 | } | ||
| 5641 | |||
| 5642 | if (bytesToRead > pWav->bytesRemaining) { | ||
| 5643 | bytesToRead = (size_t)pWav->bytesRemaining; | ||
| 5644 | } | ||
| 5645 | |||
| 5646 | if (bytesToRead == 0) { | ||
| 5647 | return 0; /* At end. */ | ||
| 5648 | } | ||
| 5649 | |||
| 5650 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 5651 | if (bytesPerFrame == 0) { | ||
| 5652 | return 0; /* Could not determine the bytes per frame. */ | ||
| 5653 | } | ||
| 5654 | |||
| 5655 | if (pBufferOut != NULL) { | ||
| 5656 | bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); | ||
| 5657 | } else { | ||
| 5658 | /* We need to seek. If we fail, we need to read-and-discard to make sure we get a good byte count. */ | ||
| 5659 | bytesRead = 0; | ||
| 5660 | while (bytesRead < bytesToRead) { | ||
| 5661 | size_t bytesToSeek = (bytesToRead - bytesRead); | ||
| 5662 | if (bytesToSeek > 0x7FFFFFFF) { | ||
| 5663 | bytesToSeek = 0x7FFFFFFF; | ||
| 5664 | } | ||
| 5665 | |||
| 5666 | if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) { | ||
| 5667 | break; | ||
| 5668 | } | ||
| 5669 | |||
| 5670 | bytesRead += bytesToSeek; | ||
| 5671 | } | ||
| 5672 | |||
| 5673 | /* When we get here we may need to read-and-discard some data. */ | ||
| 5674 | while (bytesRead < bytesToRead) { | ||
| 5675 | drwav_uint8 buffer[4096]; | ||
| 5676 | size_t bytesSeeked; | ||
| 5677 | size_t bytesToSeek = (bytesToRead - bytesRead); | ||
| 5678 | if (bytesToSeek > sizeof(buffer)) { | ||
| 5679 | bytesToSeek = sizeof(buffer); | ||
| 5680 | } | ||
| 5681 | |||
| 5682 | bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek); | ||
| 5683 | bytesRead += bytesSeeked; | ||
| 5684 | |||
| 5685 | if (bytesSeeked < bytesToSeek) { | ||
| 5686 | break; /* Reached the end. */ | ||
| 5687 | } | ||
| 5688 | } | ||
| 5689 | } | ||
| 5690 | |||
| 5691 | pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame; | ||
| 5692 | |||
| 5693 | pWav->bytesRemaining -= bytesRead; | ||
| 5694 | return bytesRead; | ||
| 5695 | } | ||
| 5696 | |||
| 5697 | |||
| 5698 | |||
| 5699 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) | ||
| 5700 | { | ||
| 5701 | drwav_uint32 bytesPerFrame; | ||
| 5702 | drwav_uint64 bytesToRead; /* Intentionally uint64 instead of size_t so we can do a check that we're not reading too much on 32-bit builds. */ | ||
| 5703 | drwav_uint64 framesRemainingInFile; | ||
| 5704 | |||
| 5705 | if (pWav == NULL || framesToRead == 0) { | ||
| 5706 | return 0; | ||
| 5707 | } | ||
| 5708 | |||
| 5709 | /* Cannot use this function for compressed formats. */ | ||
| 5710 | if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { | ||
| 5711 | return 0; | ||
| 5712 | } | ||
| 5713 | |||
| 5714 | framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames; | ||
| 5715 | if (framesToRead > framesRemainingInFile) { | ||
| 5716 | framesToRead = framesRemainingInFile; | ||
| 5717 | } | ||
| 5718 | |||
| 5719 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 5720 | if (bytesPerFrame == 0) { | ||
| 5721 | return 0; | ||
| 5722 | } | ||
| 5723 | |||
| 5724 | /* Don't try to read more samples than can potentially fit in the output buffer. */ | ||
| 5725 | bytesToRead = framesToRead * bytesPerFrame; | ||
| 5726 | if (bytesToRead > DRWAV_SIZE_MAX) { | ||
| 5727 | bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame; /* Round the number of bytes to read to a clean frame boundary. */ | ||
| 5728 | } | ||
| 5729 | |||
| 5730 | /* | ||
| 5731 | Doing an explicit check here just to make it clear that we don't want to be attempt to read anything if there's no bytes to read. There | ||
| 5732 | *could* be a time where it evaluates to 0 due to overflowing. | ||
| 5733 | */ | ||
| 5734 | if (bytesToRead == 0) { | ||
| 5735 | return 0; | ||
| 5736 | } | ||
| 5737 | |||
| 5738 | return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; | ||
| 5739 | } | ||
| 5740 | |||
| 5741 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) | ||
| 5742 | { | ||
| 5743 | drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); | ||
| 5744 | |||
| 5745 | if (pBufferOut != NULL) { | ||
| 5746 | drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 5747 | if (bytesPerFrame == 0) { | ||
| 5748 | return 0; /* Could not get the bytes per frame which means bytes per sample cannot be determined and we don't know how to byte swap. */ | ||
| 5749 | } | ||
| 5750 | |||
| 5751 | drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels); | ||
| 5752 | } | ||
| 5753 | |||
| 5754 | return framesRead; | ||
| 5755 | } | ||
| 5756 | |||
| 5757 | DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) | ||
| 5758 | { | ||
| 5759 | drwav_uint64 framesRead = 0; | ||
| 5760 | |||
| 5761 | if (drwav_is_container_be(pWav->container)) { | ||
| 5762 | /* | ||
| 5763 | Special case for AIFF. AIFF is a big-endian encoded format, but it supports a format that is | ||
| 5764 | PCM in little-endian encoding. In this case, we fall through this branch and treate it as | ||
| 5765 | little-endian. | ||
| 5766 | */ | ||
| 5767 | if (pWav->container != drwav_container_aiff || pWav->aiff.isLE == DRWAV_FALSE) { | ||
| 5768 | if (drwav__is_little_endian()) { | ||
| 5769 | framesRead = drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); | ||
| 5770 | } else { | ||
| 5771 | framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); | ||
| 5772 | } | ||
| 5773 | |||
| 5774 | goto post_process; | ||
| 5775 | } | ||
| 5776 | } | ||
| 5777 | |||
| 5778 | /* Getting here means the data should be considered little-endian. */ | ||
| 5779 | if (drwav__is_little_endian()) { | ||
| 5780 | framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); | ||
| 5781 | } else { | ||
| 5782 | framesRead = drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); | ||
| 5783 | } | ||
| 5784 | |||
| 5785 | /* | ||
| 5786 | Here is where we check if we need to do a signed/unsigned conversion for AIFF. The reason we need to do this | ||
| 5787 | is because dr_wav always assumes an 8-bit sample is unsigned, whereas AIFF can have signed 8-bit formats. | ||
| 5788 | */ | ||
| 5789 | post_process: | ||
| 5790 | { | ||
| 5791 | if (pWav->container == drwav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == DRWAV_FALSE) { | ||
| 5792 | if (pBufferOut != NULL) { | ||
| 5793 | drwav_uint64 iSample; | ||
| 5794 | |||
| 5795 | for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) { | ||
| 5796 | ((drwav_uint8*)pBufferOut)[iSample] += 128; | ||
| 5797 | } | ||
| 5798 | } | ||
| 5799 | } | ||
| 5800 | } | ||
| 5801 | |||
| 5802 | return framesRead; | ||
| 5803 | } | ||
| 5804 | |||
| 5805 | |||
| 5806 | |||
| 5807 | DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav) | ||
| 5808 | { | ||
| 5809 | if (pWav->onWrite != NULL) { | ||
| 5810 | return DRWAV_FALSE; /* No seeking in write mode. */ | ||
| 5811 | } | ||
| 5812 | |||
| 5813 | if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) { | ||
| 5814 | return DRWAV_FALSE; | ||
| 5815 | } | ||
| 5816 | |||
| 5817 | if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { | ||
| 5818 | /* Cached data needs to be cleared for compressed formats. */ | ||
| 5819 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { | ||
| 5820 | DRWAV_ZERO_OBJECT(&pWav->msadpcm); | ||
| 5821 | } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { | ||
| 5822 | DRWAV_ZERO_OBJECT(&pWav->ima); | ||
| 5823 | } else { | ||
| 5824 | DRWAV_ASSERT(DRWAV_FALSE); /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */ | ||
| 5825 | } | ||
| 5826 | } | ||
| 5827 | |||
| 5828 | pWav->readCursorInPCMFrames = 0; | ||
| 5829 | pWav->bytesRemaining = pWav->dataChunkDataSize; | ||
| 5830 | |||
| 5831 | return DRWAV_TRUE; | ||
| 5832 | } | ||
| 5833 | |||
| 5834 | DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex) | ||
| 5835 | { | ||
| 5836 | /* Seeking should be compatible with wave files > 2GB. */ | ||
| 5837 | |||
| 5838 | if (pWav == NULL || pWav->onSeek == NULL) { | ||
| 5839 | return DRWAV_FALSE; | ||
| 5840 | } | ||
| 5841 | |||
| 5842 | /* No seeking in write mode. */ | ||
| 5843 | if (pWav->onWrite != NULL) { | ||
| 5844 | return DRWAV_FALSE; | ||
| 5845 | } | ||
| 5846 | |||
| 5847 | /* If there are no samples, just return DRWAV_TRUE without doing anything. */ | ||
| 5848 | if (pWav->totalPCMFrameCount == 0) { | ||
| 5849 | return DRWAV_TRUE; | ||
| 5850 | } | ||
| 5851 | |||
| 5852 | /* Make sure the sample is clamped. */ | ||
| 5853 | if (targetFrameIndex > pWav->totalPCMFrameCount) { | ||
| 5854 | targetFrameIndex = pWav->totalPCMFrameCount; | ||
| 5855 | } | ||
| 5856 | |||
| 5857 | /* | ||
| 5858 | For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need | ||
| 5859 | to seek back to the start. | ||
| 5860 | */ | ||
| 5861 | if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { | ||
| 5862 | /* TODO: This can be optimized. */ | ||
| 5863 | |||
| 5864 | /* | ||
| 5865 | If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards, | ||
| 5866 | we first need to seek back to the start and then just do the same thing as a forward seek. | ||
| 5867 | */ | ||
| 5868 | if (targetFrameIndex < pWav->readCursorInPCMFrames) { | ||
| 5869 | if (!drwav_seek_to_first_pcm_frame(pWav)) { | ||
| 5870 | return DRWAV_FALSE; | ||
| 5871 | } | ||
| 5872 | } | ||
| 5873 | |||
| 5874 | if (targetFrameIndex > pWav->readCursorInPCMFrames) { | ||
| 5875 | drwav_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; | ||
| 5876 | |||
| 5877 | drwav_int16 devnull[2048]; | ||
| 5878 | while (offsetInFrames > 0) { | ||
| 5879 | drwav_uint64 framesRead = 0; | ||
| 5880 | drwav_uint64 framesToRead = offsetInFrames; | ||
| 5881 | if (framesToRead > drwav_countof(devnull)/pWav->channels) { | ||
| 5882 | framesToRead = drwav_countof(devnull)/pWav->channels; | ||
| 5883 | } | ||
| 5884 | |||
| 5885 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { | ||
| 5886 | framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); | ||
| 5887 | } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { | ||
| 5888 | framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); | ||
| 5889 | } else { | ||
| 5890 | DRWAV_ASSERT(DRWAV_FALSE); /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */ | ||
| 5891 | } | ||
| 5892 | |||
| 5893 | if (framesRead != framesToRead) { | ||
| 5894 | return DRWAV_FALSE; | ||
| 5895 | } | ||
| 5896 | |||
| 5897 | offsetInFrames -= framesRead; | ||
| 5898 | } | ||
| 5899 | } | ||
| 5900 | } else { | ||
| 5901 | drwav_uint64 totalSizeInBytes; | ||
| 5902 | drwav_uint64 currentBytePos; | ||
| 5903 | drwav_uint64 targetBytePos; | ||
| 5904 | drwav_uint64 offset; | ||
| 5905 | drwav_uint32 bytesPerFrame; | ||
| 5906 | |||
| 5907 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 5908 | if (bytesPerFrame == 0) { | ||
| 5909 | return DRWAV_FALSE; /* Not able to calculate offset. */ | ||
| 5910 | } | ||
| 5911 | |||
| 5912 | totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; | ||
| 5913 | /*DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining);*/ | ||
| 5914 | |||
| 5915 | currentBytePos = totalSizeInBytes - pWav->bytesRemaining; | ||
| 5916 | targetBytePos = targetFrameIndex * bytesPerFrame; | ||
| 5917 | |||
| 5918 | if (currentBytePos < targetBytePos) { | ||
| 5919 | /* Offset forwards. */ | ||
| 5920 | offset = (targetBytePos - currentBytePos); | ||
| 5921 | } else { | ||
| 5922 | /* Offset backwards. */ | ||
| 5923 | if (!drwav_seek_to_first_pcm_frame(pWav)) { | ||
| 5924 | return DRWAV_FALSE; | ||
| 5925 | } | ||
| 5926 | offset = targetBytePos; | ||
| 5927 | } | ||
| 5928 | |||
| 5929 | while (offset > 0) { | ||
| 5930 | int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); | ||
| 5931 | if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) { | ||
| 5932 | return DRWAV_FALSE; | ||
| 5933 | } | ||
| 5934 | |||
| 5935 | pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; | ||
| 5936 | pWav->bytesRemaining -= offset32; | ||
| 5937 | offset -= offset32; | ||
| 5938 | } | ||
| 5939 | } | ||
| 5940 | |||
| 5941 | return DRWAV_TRUE; | ||
| 5942 | } | ||
| 5943 | |||
| 5944 | DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor) | ||
| 5945 | { | ||
| 5946 | if (pCursor == NULL) { | ||
| 5947 | return DRWAV_INVALID_ARGS; | ||
| 5948 | } | ||
| 5949 | |||
| 5950 | *pCursor = 0; /* Safety. */ | ||
| 5951 | |||
| 5952 | if (pWav == NULL) { | ||
| 5953 | return DRWAV_INVALID_ARGS; | ||
| 5954 | } | ||
| 5955 | |||
| 5956 | *pCursor = pWav->readCursorInPCMFrames; | ||
| 5957 | |||
| 5958 | return DRWAV_SUCCESS; | ||
| 5959 | } | ||
| 5960 | |||
| 5961 | DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength) | ||
| 5962 | { | ||
| 5963 | if (pLength == NULL) { | ||
| 5964 | return DRWAV_INVALID_ARGS; | ||
| 5965 | } | ||
| 5966 | |||
| 5967 | *pLength = 0; /* Safety. */ | ||
| 5968 | |||
| 5969 | if (pWav == NULL) { | ||
| 5970 | return DRWAV_INVALID_ARGS; | ||
| 5971 | } | ||
| 5972 | |||
| 5973 | *pLength = pWav->totalPCMFrameCount; | ||
| 5974 | |||
| 5975 | return DRWAV_SUCCESS; | ||
| 5976 | } | ||
| 5977 | |||
| 5978 | |||
| 5979 | DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData) | ||
| 5980 | { | ||
| 5981 | size_t bytesWritten; | ||
| 5982 | |||
| 5983 | if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { | ||
| 5984 | return 0; | ||
| 5985 | } | ||
| 5986 | |||
| 5987 | bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); | ||
| 5988 | pWav->dataChunkDataSize += bytesWritten; | ||
| 5989 | |||
| 5990 | return bytesWritten; | ||
| 5991 | } | ||
| 5992 | |||
| 5993 | DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) | ||
| 5994 | { | ||
| 5995 | drwav_uint64 bytesToWrite; | ||
| 5996 | drwav_uint64 bytesWritten; | ||
| 5997 | const drwav_uint8* pRunningData; | ||
| 5998 | |||
| 5999 | if (pWav == NULL || framesToWrite == 0 || pData == NULL) { | ||
| 6000 | return 0; | ||
| 6001 | } | ||
| 6002 | |||
| 6003 | bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); | ||
| 6004 | if (bytesToWrite > DRWAV_SIZE_MAX) { | ||
| 6005 | return 0; | ||
| 6006 | } | ||
| 6007 | |||
| 6008 | bytesWritten = 0; | ||
| 6009 | pRunningData = (const drwav_uint8*)pData; | ||
| 6010 | |||
| 6011 | while (bytesToWrite > 0) { | ||
| 6012 | size_t bytesJustWritten; | ||
| 6013 | drwav_uint64 bytesToWriteThisIteration; | ||
| 6014 | |||
| 6015 | bytesToWriteThisIteration = bytesToWrite; | ||
| 6016 | DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); /* <-- This is checked above. */ | ||
| 6017 | |||
| 6018 | bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); | ||
| 6019 | if (bytesJustWritten == 0) { | ||
| 6020 | break; | ||
| 6021 | } | ||
| 6022 | |||
| 6023 | bytesToWrite -= bytesJustWritten; | ||
| 6024 | bytesWritten += bytesJustWritten; | ||
| 6025 | pRunningData += bytesJustWritten; | ||
| 6026 | } | ||
| 6027 | |||
| 6028 | return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; | ||
| 6029 | } | ||
| 6030 | |||
| 6031 | DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) | ||
| 6032 | { | ||
| 6033 | drwav_uint64 bytesToWrite; | ||
| 6034 | drwav_uint64 bytesWritten; | ||
| 6035 | drwav_uint32 bytesPerSample; | ||
| 6036 | const drwav_uint8* pRunningData; | ||
| 6037 | |||
| 6038 | if (pWav == NULL || framesToWrite == 0 || pData == NULL) { | ||
| 6039 | return 0; | ||
| 6040 | } | ||
| 6041 | |||
| 6042 | bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); | ||
| 6043 | if (bytesToWrite > DRWAV_SIZE_MAX) { | ||
| 6044 | return 0; | ||
| 6045 | } | ||
| 6046 | |||
| 6047 | bytesWritten = 0; | ||
| 6048 | pRunningData = (const drwav_uint8*)pData; | ||
| 6049 | |||
| 6050 | bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels; | ||
| 6051 | if (bytesPerSample == 0) { | ||
| 6052 | return 0; /* Cannot determine bytes per sample, or bytes per sample is less than one byte. */ | ||
| 6053 | } | ||
| 6054 | |||
| 6055 | while (bytesToWrite > 0) { | ||
| 6056 | drwav_uint8 temp[4096]; | ||
| 6057 | drwav_uint32 sampleCount; | ||
| 6058 | size_t bytesJustWritten; | ||
| 6059 | drwav_uint64 bytesToWriteThisIteration; | ||
| 6060 | |||
| 6061 | bytesToWriteThisIteration = bytesToWrite; | ||
| 6062 | DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); /* <-- This is checked above. */ | ||
| 6063 | |||
| 6064 | /* | ||
| 6065 | WAV files are always little-endian. We need to byte swap on big-endian architectures. Since our input buffer is read-only we need | ||
| 6066 | to use an intermediary buffer for the conversion. | ||
| 6067 | */ | ||
| 6068 | sampleCount = sizeof(temp)/bytesPerSample; | ||
| 6069 | |||
| 6070 | if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) { | ||
| 6071 | bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample; | ||
| 6072 | } | ||
| 6073 | |||
| 6074 | DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); | ||
| 6075 | drwav__bswap_samples(temp, sampleCount, bytesPerSample); | ||
| 6076 | |||
| 6077 | bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); | ||
| 6078 | if (bytesJustWritten == 0) { | ||
| 6079 | break; | ||
| 6080 | } | ||
| 6081 | |||
| 6082 | bytesToWrite -= bytesJustWritten; | ||
| 6083 | bytesWritten += bytesJustWritten; | ||
| 6084 | pRunningData += bytesJustWritten; | ||
| 6085 | } | ||
| 6086 | |||
| 6087 | return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; | ||
| 6088 | } | ||
| 6089 | |||
| 6090 | DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) | ||
| 6091 | { | ||
| 6092 | if (drwav__is_little_endian()) { | ||
| 6093 | return drwav_write_pcm_frames_le(pWav, framesToWrite, pData); | ||
| 6094 | } else { | ||
| 6095 | return drwav_write_pcm_frames_be(pWav, framesToWrite, pData); | ||
| 6096 | } | ||
| 6097 | } | ||
| 6098 | |||
| 6099 | |||
| 6100 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) | ||
| 6101 | { | ||
| 6102 | drwav_uint64 totalFramesRead = 0; | ||
| 6103 | |||
| 6104 | DRWAV_ASSERT(pWav != NULL); | ||
| 6105 | DRWAV_ASSERT(framesToRead > 0); | ||
| 6106 | |||
| 6107 | /* TODO: Lots of room for optimization here. */ | ||
| 6108 | |||
| 6109 | while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { | ||
| 6110 | DRWAV_ASSERT(framesToRead > 0); /* This loop iteration will never get hit with framesToRead == 0 because it's asserted at the top, and we check for 0 inside the loop just below. */ | ||
| 6111 | |||
| 6112 | /* If there are no cached frames we need to load a new block. */ | ||
| 6113 | if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { | ||
| 6114 | if (pWav->channels == 1) { | ||
| 6115 | /* Mono. */ | ||
| 6116 | drwav_uint8 header[7]; | ||
| 6117 | if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { | ||
| 6118 | return totalFramesRead; | ||
| 6119 | } | ||
| 6120 | pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); | ||
| 6121 | |||
| 6122 | pWav->msadpcm.predictor[0] = header[0]; | ||
| 6123 | pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1); | ||
| 6124 | pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3); | ||
| 6125 | pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5); | ||
| 6126 | pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; | ||
| 6127 | pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; | ||
| 6128 | pWav->msadpcm.cachedFrameCount = 2; | ||
| 6129 | } else { | ||
| 6130 | /* Stereo. */ | ||
| 6131 | drwav_uint8 header[14]; | ||
| 6132 | if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { | ||
| 6133 | return totalFramesRead; | ||
| 6134 | } | ||
| 6135 | pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); | ||
| 6136 | |||
| 6137 | pWav->msadpcm.predictor[0] = header[0]; | ||
| 6138 | pWav->msadpcm.predictor[1] = header[1]; | ||
| 6139 | pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2); | ||
| 6140 | pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4); | ||
| 6141 | pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6); | ||
| 6142 | pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8); | ||
| 6143 | pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10); | ||
| 6144 | pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12); | ||
| 6145 | |||
| 6146 | pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; | ||
| 6147 | pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; | ||
| 6148 | pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; | ||
| 6149 | pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; | ||
| 6150 | pWav->msadpcm.cachedFrameCount = 2; | ||
| 6151 | } | ||
| 6152 | } | ||
| 6153 | |||
| 6154 | /* Output anything that's cached. */ | ||
| 6155 | while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { | ||
| 6156 | if (pBufferOut != NULL) { | ||
| 6157 | drwav_uint32 iSample = 0; | ||
| 6158 | for (iSample = 0; iSample < pWav->channels; iSample += 1) { | ||
| 6159 | pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; | ||
| 6160 | } | ||
| 6161 | |||
| 6162 | pBufferOut += pWav->channels; | ||
| 6163 | } | ||
| 6164 | |||
| 6165 | framesToRead -= 1; | ||
| 6166 | totalFramesRead += 1; | ||
| 6167 | pWav->readCursorInPCMFrames += 1; | ||
| 6168 | pWav->msadpcm.cachedFrameCount -= 1; | ||
| 6169 | } | ||
| 6170 | |||
| 6171 | if (framesToRead == 0) { | ||
| 6172 | break; | ||
| 6173 | } | ||
| 6174 | |||
| 6175 | |||
| 6176 | /* | ||
| 6177 | If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next | ||
| 6178 | loop iteration which will trigger the loading of a new block. | ||
| 6179 | */ | ||
| 6180 | if (pWav->msadpcm.cachedFrameCount == 0) { | ||
| 6181 | if (pWav->msadpcm.bytesRemainingInBlock == 0) { | ||
| 6182 | continue; | ||
| 6183 | } else { | ||
| 6184 | static drwav_int32 adaptationTable[] = { | ||
| 6185 | 230, 230, 230, 230, 307, 409, 512, 614, | ||
| 6186 | 768, 614, 512, 409, 307, 230, 230, 230 | ||
| 6187 | }; | ||
| 6188 | static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; | ||
| 6189 | static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; | ||
| 6190 | |||
| 6191 | drwav_uint8 nibbles; | ||
| 6192 | drwav_int32 nibble0; | ||
| 6193 | drwav_int32 nibble1; | ||
| 6194 | |||
| 6195 | if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { | ||
| 6196 | return totalFramesRead; | ||
| 6197 | } | ||
| 6198 | pWav->msadpcm.bytesRemainingInBlock -= 1; | ||
| 6199 | |||
| 6200 | /* TODO: Optimize away these if statements. */ | ||
| 6201 | nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } | ||
| 6202 | nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } | ||
| 6203 | |||
| 6204 | if (pWav->channels == 1) { | ||
| 6205 | /* Mono. */ | ||
| 6206 | drwav_int32 newSample0; | ||
| 6207 | drwav_int32 newSample1; | ||
| 6208 | |||
| 6209 | newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; | ||
| 6210 | newSample0 += nibble0 * pWav->msadpcm.delta[0]; | ||
| 6211 | newSample0 = drwav_clamp(newSample0, -32768, 32767); | ||
| 6212 | |||
| 6213 | pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; | ||
| 6214 | if (pWav->msadpcm.delta[0] < 16) { | ||
| 6215 | pWav->msadpcm.delta[0] = 16; | ||
| 6216 | } | ||
| 6217 | |||
| 6218 | pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; | ||
| 6219 | pWav->msadpcm.prevFrames[0][1] = newSample0; | ||
| 6220 | |||
| 6221 | |||
| 6222 | newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; | ||
| 6223 | newSample1 += nibble1 * pWav->msadpcm.delta[0]; | ||
| 6224 | newSample1 = drwav_clamp(newSample1, -32768, 32767); | ||
| 6225 | |||
| 6226 | pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; | ||
| 6227 | if (pWav->msadpcm.delta[0] < 16) { | ||
| 6228 | pWav->msadpcm.delta[0] = 16; | ||
| 6229 | } | ||
| 6230 | |||
| 6231 | pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; | ||
| 6232 | pWav->msadpcm.prevFrames[0][1] = newSample1; | ||
| 6233 | |||
| 6234 | |||
| 6235 | pWav->msadpcm.cachedFrames[2] = newSample0; | ||
| 6236 | pWav->msadpcm.cachedFrames[3] = newSample1; | ||
| 6237 | pWav->msadpcm.cachedFrameCount = 2; | ||
| 6238 | } else { | ||
| 6239 | /* Stereo. */ | ||
| 6240 | drwav_int32 newSample0; | ||
| 6241 | drwav_int32 newSample1; | ||
| 6242 | |||
| 6243 | /* Left. */ | ||
| 6244 | newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; | ||
| 6245 | newSample0 += nibble0 * pWav->msadpcm.delta[0]; | ||
| 6246 | newSample0 = drwav_clamp(newSample0, -32768, 32767); | ||
| 6247 | |||
| 6248 | pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; | ||
| 6249 | if (pWav->msadpcm.delta[0] < 16) { | ||
| 6250 | pWav->msadpcm.delta[0] = 16; | ||
| 6251 | } | ||
| 6252 | |||
| 6253 | pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; | ||
| 6254 | pWav->msadpcm.prevFrames[0][1] = newSample0; | ||
| 6255 | |||
| 6256 | |||
| 6257 | /* Right. */ | ||
| 6258 | newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; | ||
| 6259 | newSample1 += nibble1 * pWav->msadpcm.delta[1]; | ||
| 6260 | newSample1 = drwav_clamp(newSample1, -32768, 32767); | ||
| 6261 | |||
| 6262 | pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; | ||
| 6263 | if (pWav->msadpcm.delta[1] < 16) { | ||
| 6264 | pWav->msadpcm.delta[1] = 16; | ||
| 6265 | } | ||
| 6266 | |||
| 6267 | pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; | ||
| 6268 | pWav->msadpcm.prevFrames[1][1] = newSample1; | ||
| 6269 | |||
| 6270 | pWav->msadpcm.cachedFrames[2] = newSample0; | ||
| 6271 | pWav->msadpcm.cachedFrames[3] = newSample1; | ||
| 6272 | pWav->msadpcm.cachedFrameCount = 1; | ||
| 6273 | } | ||
| 6274 | } | ||
| 6275 | } | ||
| 6276 | } | ||
| 6277 | |||
| 6278 | return totalFramesRead; | ||
| 6279 | } | ||
| 6280 | |||
| 6281 | |||
| 6282 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) | ||
| 6283 | { | ||
| 6284 | drwav_uint64 totalFramesRead = 0; | ||
| 6285 | drwav_uint32 iChannel; | ||
| 6286 | |||
| 6287 | static drwav_int32 indexTable[16] = { | ||
| 6288 | -1, -1, -1, -1, 2, 4, 6, 8, | ||
| 6289 | -1, -1, -1, -1, 2, 4, 6, 8 | ||
| 6290 | }; | ||
| 6291 | |||
| 6292 | static drwav_int32 stepTable[89] = { | ||
| 6293 | 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, | ||
| 6294 | 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, | ||
| 6295 | 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, | ||
| 6296 | 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, | ||
| 6297 | 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, | ||
| 6298 | 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, | ||
| 6299 | 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, | ||
| 6300 | 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, | ||
| 6301 | 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 | ||
| 6302 | }; | ||
| 6303 | |||
| 6304 | DRWAV_ASSERT(pWav != NULL); | ||
| 6305 | DRWAV_ASSERT(framesToRead > 0); | ||
| 6306 | |||
| 6307 | /* TODO: Lots of room for optimization here. */ | ||
| 6308 | |||
| 6309 | while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { | ||
| 6310 | DRWAV_ASSERT(framesToRead > 0); /* This loop iteration will never get hit with framesToRead == 0 because it's asserted at the top, and we check for 0 inside the loop just below. */ | ||
| 6311 | |||
| 6312 | /* If there are no cached samples we need to load a new block. */ | ||
| 6313 | if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { | ||
| 6314 | if (pWav->channels == 1) { | ||
| 6315 | /* Mono. */ | ||
| 6316 | drwav_uint8 header[4]; | ||
| 6317 | if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { | ||
| 6318 | return totalFramesRead; | ||
| 6319 | } | ||
| 6320 | pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); | ||
| 6321 | |||
| 6322 | if (header[2] >= drwav_countof(stepTable)) { | ||
| 6323 | pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); | ||
| 6324 | pWav->ima.bytesRemainingInBlock = 0; | ||
| 6325 | return totalFramesRead; /* Invalid data. */ | ||
| 6326 | } | ||
| 6327 | |||
| 6328 | pWav->ima.predictor[0] = (drwav_int16)drwav_bytes_to_u16(header + 0); | ||
| 6329 | pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); /* Clamp not necessary because we checked above, but adding here to silence a static analysis warning. */ | ||
| 6330 | pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; | ||
| 6331 | pWav->ima.cachedFrameCount = 1; | ||
| 6332 | } else { | ||
| 6333 | /* Stereo. */ | ||
| 6334 | drwav_uint8 header[8]; | ||
| 6335 | if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { | ||
| 6336 | return totalFramesRead; | ||
| 6337 | } | ||
| 6338 | pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); | ||
| 6339 | |||
| 6340 | if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) { | ||
| 6341 | pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); | ||
| 6342 | pWav->ima.bytesRemainingInBlock = 0; | ||
| 6343 | return totalFramesRead; /* Invalid data. */ | ||
| 6344 | } | ||
| 6345 | |||
| 6346 | pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); | ||
| 6347 | pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); /* Clamp not necessary because we checked above, but adding here to silence a static analysis warning. */ | ||
| 6348 | pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4); | ||
| 6349 | pWav->ima.stepIndex[1] = drwav_clamp(header[6], 0, (drwav_int32)drwav_countof(stepTable)-1); /* Clamp not necessary because we checked above, but adding here to silence a static analysis warning. */ | ||
| 6350 | |||
| 6351 | pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; | ||
| 6352 | pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; | ||
| 6353 | pWav->ima.cachedFrameCount = 1; | ||
| 6354 | } | ||
| 6355 | } | ||
| 6356 | |||
| 6357 | /* Output anything that's cached. */ | ||
| 6358 | while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { | ||
| 6359 | if (pBufferOut != NULL) { | ||
| 6360 | drwav_uint32 iSample; | ||
| 6361 | for (iSample = 0; iSample < pWav->channels; iSample += 1) { | ||
| 6362 | pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; | ||
| 6363 | } | ||
| 6364 | pBufferOut += pWav->channels; | ||
| 6365 | } | ||
| 6366 | |||
| 6367 | framesToRead -= 1; | ||
| 6368 | totalFramesRead += 1; | ||
| 6369 | pWav->readCursorInPCMFrames += 1; | ||
| 6370 | pWav->ima.cachedFrameCount -= 1; | ||
| 6371 | } | ||
| 6372 | |||
| 6373 | if (framesToRead == 0) { | ||
| 6374 | break; | ||
| 6375 | } | ||
| 6376 | |||
| 6377 | /* | ||
| 6378 | If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next | ||
| 6379 | loop iteration which will trigger the loading of a new block. | ||
| 6380 | */ | ||
| 6381 | if (pWav->ima.cachedFrameCount == 0) { | ||
| 6382 | if (pWav->ima.bytesRemainingInBlock == 0) { | ||
| 6383 | continue; | ||
| 6384 | } else { | ||
| 6385 | /* | ||
| 6386 | From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the | ||
| 6387 | left channel, 4 bytes for the right channel. | ||
| 6388 | */ | ||
| 6389 | pWav->ima.cachedFrameCount = 8; | ||
| 6390 | for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { | ||
| 6391 | drwav_uint32 iByte; | ||
| 6392 | drwav_uint8 nibbles[4]; | ||
| 6393 | if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { | ||
| 6394 | pWav->ima.cachedFrameCount = 0; | ||
| 6395 | return totalFramesRead; | ||
| 6396 | } | ||
| 6397 | pWav->ima.bytesRemainingInBlock -= 4; | ||
| 6398 | |||
| 6399 | for (iByte = 0; iByte < 4; ++iByte) { | ||
| 6400 | drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); | ||
| 6401 | drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); | ||
| 6402 | |||
| 6403 | drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; | ||
| 6404 | drwav_int32 predictor = pWav->ima.predictor[iChannel]; | ||
| 6405 | |||
| 6406 | drwav_int32 diff = step >> 3; | ||
| 6407 | if (nibble0 & 1) diff += step >> 2; | ||
| 6408 | if (nibble0 & 2) diff += step >> 1; | ||
| 6409 | if (nibble0 & 4) diff += step; | ||
| 6410 | if (nibble0 & 8) diff = -diff; | ||
| 6411 | |||
| 6412 | predictor = drwav_clamp(predictor + diff, -32768, 32767); | ||
| 6413 | pWav->ima.predictor[iChannel] = predictor; | ||
| 6414 | pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1); | ||
| 6415 | pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; | ||
| 6416 | |||
| 6417 | |||
| 6418 | step = stepTable[pWav->ima.stepIndex[iChannel]]; | ||
| 6419 | predictor = pWav->ima.predictor[iChannel]; | ||
| 6420 | |||
| 6421 | diff = step >> 3; | ||
| 6422 | if (nibble1 & 1) diff += step >> 2; | ||
| 6423 | if (nibble1 & 2) diff += step >> 1; | ||
| 6424 | if (nibble1 & 4) diff += step; | ||
| 6425 | if (nibble1 & 8) diff = -diff; | ||
| 6426 | |||
| 6427 | predictor = drwav_clamp(predictor + diff, -32768, 32767); | ||
| 6428 | pWav->ima.predictor[iChannel] = predictor; | ||
| 6429 | pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1); | ||
| 6430 | pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; | ||
| 6431 | } | ||
| 6432 | } | ||
| 6433 | } | ||
| 6434 | } | ||
| 6435 | } | ||
| 6436 | |||
| 6437 | return totalFramesRead; | ||
| 6438 | } | ||
| 6439 | |||
| 6440 | |||
| 6441 | #ifndef DR_WAV_NO_CONVERSION_API | ||
| 6442 | static unsigned short g_drwavAlawTable[256] = { | ||
| 6443 | 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, | ||
| 6444 | 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, | ||
| 6445 | 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, | ||
| 6446 | 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, | ||
| 6447 | 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, | ||
| 6448 | 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, | ||
| 6449 | 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, | ||
| 6450 | 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, | ||
| 6451 | 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, | ||
| 6452 | 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, | ||
| 6453 | 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, | ||
| 6454 | 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, | ||
| 6455 | 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, | ||
| 6456 | 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, | ||
| 6457 | 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, | ||
| 6458 | 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 | ||
| 6459 | }; | ||
| 6460 | |||
| 6461 | static unsigned short g_drwavMulawTable[256] = { | ||
| 6462 | 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, | ||
| 6463 | 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, | ||
| 6464 | 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, | ||
| 6465 | 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, | ||
| 6466 | 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, | ||
| 6467 | 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, | ||
| 6468 | 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, | ||
| 6469 | 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, | ||
| 6470 | 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, | ||
| 6471 | 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, | ||
| 6472 | 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, | ||
| 6473 | 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, | ||
| 6474 | 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, | ||
| 6475 | 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, | ||
| 6476 | 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, | ||
| 6477 | 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 | ||
| 6478 | }; | ||
| 6479 | |||
| 6480 | static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn) | ||
| 6481 | { | ||
| 6482 | return (short)g_drwavAlawTable[sampleIn]; | ||
| 6483 | } | ||
| 6484 | |||
| 6485 | static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) | ||
| 6486 | { | ||
| 6487 | return (short)g_drwavMulawTable[sampleIn]; | ||
| 6488 | } | ||
| 6489 | |||
| 6490 | |||
| 6491 | |||
| 6492 | DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) | ||
| 6493 | { | ||
| 6494 | size_t i; | ||
| 6495 | |||
| 6496 | /* Special case for 8-bit sample data because it's treated as unsigned. */ | ||
| 6497 | if (bytesPerSample == 1) { | ||
| 6498 | drwav_u8_to_s16(pOut, pIn, totalSampleCount); | ||
| 6499 | return; | ||
| 6500 | } | ||
| 6501 | |||
| 6502 | |||
| 6503 | /* Slightly more optimal implementation for common formats. */ | ||
| 6504 | if (bytesPerSample == 2) { | ||
| 6505 | for (i = 0; i < totalSampleCount; ++i) { | ||
| 6506 | *pOut++ = ((const drwav_int16*)pIn)[i]; | ||
| 6507 | } | ||
| 6508 | return; | ||
| 6509 | } | ||
| 6510 | if (bytesPerSample == 3) { | ||
| 6511 | drwav_s24_to_s16(pOut, pIn, totalSampleCount); | ||
| 6512 | return; | ||
| 6513 | } | ||
| 6514 | if (bytesPerSample == 4) { | ||
| 6515 | drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount); | ||
| 6516 | return; | ||
| 6517 | } | ||
| 6518 | |||
| 6519 | |||
| 6520 | /* Anything more than 64 bits per sample is not supported. */ | ||
| 6521 | if (bytesPerSample > 8) { | ||
| 6522 | DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); | ||
| 6523 | return; | ||
| 6524 | } | ||
| 6525 | |||
| 6526 | |||
| 6527 | /* Generic, slow converter. */ | ||
| 6528 | for (i = 0; i < totalSampleCount; ++i) { | ||
| 6529 | drwav_uint64 sample = 0; | ||
| 6530 | unsigned int shift = (8 - bytesPerSample) * 8; | ||
| 6531 | |||
| 6532 | unsigned int j; | ||
| 6533 | for (j = 0; j < bytesPerSample; j += 1) { | ||
| 6534 | DRWAV_ASSERT(j < 8); | ||
| 6535 | sample |= (drwav_uint64)(pIn[j]) << shift; | ||
| 6536 | shift += 8; | ||
| 6537 | } | ||
| 6538 | |||
| 6539 | pIn += j; | ||
| 6540 | *pOut++ = (drwav_int16)((drwav_int64)sample >> 48); | ||
| 6541 | } | ||
| 6542 | } | ||
| 6543 | |||
| 6544 | DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) | ||
| 6545 | { | ||
| 6546 | if (bytesPerSample == 4) { | ||
| 6547 | drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); | ||
| 6548 | return; | ||
| 6549 | } else if (bytesPerSample == 8) { | ||
| 6550 | drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); | ||
| 6551 | return; | ||
| 6552 | } else { | ||
| 6553 | /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */ | ||
| 6554 | DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); | ||
| 6555 | return; | ||
| 6556 | } | ||
| 6557 | } | ||
| 6558 | |||
| 6559 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) | ||
| 6560 | { | ||
| 6561 | drwav_uint64 totalFramesRead; | ||
| 6562 | drwav_uint8 sampleData[4096] = {0}; | ||
| 6563 | drwav_uint32 bytesPerFrame; | ||
| 6564 | drwav_uint32 bytesPerSample; | ||
| 6565 | drwav_uint64 samplesRead; | ||
| 6566 | |||
| 6567 | /* Fast path. */ | ||
| 6568 | if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { | ||
| 6569 | return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); | ||
| 6570 | } | ||
| 6571 | |||
| 6572 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 6573 | if (bytesPerFrame == 0) { | ||
| 6574 | return 0; | ||
| 6575 | } | ||
| 6576 | |||
| 6577 | bytesPerSample = bytesPerFrame / pWav->channels; | ||
| 6578 | if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { | ||
| 6579 | return 0; /* Only byte-aligned formats are supported. */ | ||
| 6580 | } | ||
| 6581 | |||
| 6582 | totalFramesRead = 0; | ||
| 6583 | |||
| 6584 | while (framesToRead > 0) { | ||
| 6585 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); | ||
| 6586 | drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); | ||
| 6587 | if (framesRead == 0) { | ||
| 6588 | break; | ||
| 6589 | } | ||
| 6590 | |||
| 6591 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 6592 | |||
| 6593 | /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */ | ||
| 6594 | samplesRead = framesRead * pWav->channels; | ||
| 6595 | if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { | ||
| 6596 | DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */ | ||
| 6597 | break; | ||
| 6598 | } | ||
| 6599 | |||
| 6600 | drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); | ||
| 6601 | |||
| 6602 | pBufferOut += samplesRead; | ||
| 6603 | framesToRead -= framesRead; | ||
| 6604 | totalFramesRead += framesRead; | ||
| 6605 | } | ||
| 6606 | |||
| 6607 | return totalFramesRead; | ||
| 6608 | } | ||
| 6609 | |||
| 6610 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) | ||
| 6611 | { | ||
| 6612 | drwav_uint64 totalFramesRead; | ||
| 6613 | drwav_uint8 sampleData[4096] = {0}; | ||
| 6614 | drwav_uint32 bytesPerFrame; | ||
| 6615 | drwav_uint32 bytesPerSample; | ||
| 6616 | drwav_uint64 samplesRead; | ||
| 6617 | |||
| 6618 | if (pBufferOut == NULL) { | ||
| 6619 | return drwav_read_pcm_frames(pWav, framesToRead, NULL); | ||
| 6620 | } | ||
| 6621 | |||
| 6622 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 6623 | if (bytesPerFrame == 0) { | ||
| 6624 | return 0; | ||
| 6625 | } | ||
| 6626 | |||
| 6627 | bytesPerSample = bytesPerFrame / pWav->channels; | ||
| 6628 | if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { | ||
| 6629 | return 0; /* Only byte-aligned formats are supported. */ | ||
| 6630 | } | ||
| 6631 | |||
| 6632 | totalFramesRead = 0; | ||
| 6633 | |||
| 6634 | while (framesToRead > 0) { | ||
| 6635 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); | ||
| 6636 | drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); | ||
| 6637 | if (framesRead == 0) { | ||
| 6638 | break; | ||
| 6639 | } | ||
| 6640 | |||
| 6641 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 6642 | |||
| 6643 | /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */ | ||
| 6644 | samplesRead = framesRead * pWav->channels; | ||
| 6645 | if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { | ||
| 6646 | DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */ | ||
| 6647 | break; | ||
| 6648 | } | ||
| 6649 | |||
| 6650 | drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); /* Safe cast. */ | ||
| 6651 | |||
| 6652 | pBufferOut += samplesRead; | ||
| 6653 | framesToRead -= framesRead; | ||
| 6654 | totalFramesRead += framesRead; | ||
| 6655 | } | ||
| 6656 | |||
| 6657 | return totalFramesRead; | ||
| 6658 | } | ||
| 6659 | |||
| 6660 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) | ||
| 6661 | { | ||
| 6662 | drwav_uint64 totalFramesRead; | ||
| 6663 | drwav_uint8 sampleData[4096] = {0}; | ||
| 6664 | drwav_uint32 bytesPerFrame; | ||
| 6665 | drwav_uint32 bytesPerSample; | ||
| 6666 | drwav_uint64 samplesRead; | ||
| 6667 | |||
| 6668 | if (pBufferOut == NULL) { | ||
| 6669 | return drwav_read_pcm_frames(pWav, framesToRead, NULL); | ||
| 6670 | } | ||
| 6671 | |||
| 6672 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 6673 | if (bytesPerFrame == 0) { | ||
| 6674 | return 0; | ||
| 6675 | } | ||
| 6676 | |||
| 6677 | bytesPerSample = bytesPerFrame / pWav->channels; | ||
| 6678 | if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { | ||
| 6679 | return 0; /* Only byte-aligned formats are supported. */ | ||
| 6680 | } | ||
| 6681 | |||
| 6682 | totalFramesRead = 0; | ||
| 6683 | |||
| 6684 | while (framesToRead > 0) { | ||
| 6685 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); | ||
| 6686 | drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); | ||
| 6687 | if (framesRead == 0) { | ||
| 6688 | break; | ||
| 6689 | } | ||
| 6690 | |||
| 6691 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 6692 | |||
| 6693 | /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */ | ||
| 6694 | samplesRead = framesRead * pWav->channels; | ||
| 6695 | if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { | ||
| 6696 | DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */ | ||
| 6697 | break; | ||
| 6698 | } | ||
| 6699 | |||
| 6700 | drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); | ||
| 6701 | |||
| 6702 | /* | ||
| 6703 | For some reason libsndfile seems to be returning samples of the opposite sign for a-law, but only | ||
| 6704 | with AIFF files. For WAV files it seems to be the same as dr_wav. This is resulting in dr_wav's | ||
| 6705 | automated tests failing. I'm not sure which is correct, but will assume dr_wav. If we're enforcing | ||
| 6706 | libsndfile compatibility we'll swap the signs here. | ||
| 6707 | */ | ||
| 6708 | #ifdef DR_WAV_LIBSNDFILE_COMPAT | ||
| 6709 | { | ||
| 6710 | if (pWav->container == drwav_container_aiff) { | ||
| 6711 | drwav_uint64 iSample; | ||
| 6712 | for (iSample = 0; iSample < samplesRead; iSample += 1) { | ||
| 6713 | pBufferOut[iSample] = -pBufferOut[iSample]; | ||
| 6714 | } | ||
| 6715 | } | ||
| 6716 | } | ||
| 6717 | #endif | ||
| 6718 | |||
| 6719 | pBufferOut += samplesRead; | ||
| 6720 | framesToRead -= framesRead; | ||
| 6721 | totalFramesRead += framesRead; | ||
| 6722 | } | ||
| 6723 | |||
| 6724 | return totalFramesRead; | ||
| 6725 | } | ||
| 6726 | |||
| 6727 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) | ||
| 6728 | { | ||
| 6729 | drwav_uint64 totalFramesRead; | ||
| 6730 | drwav_uint8 sampleData[4096] = {0}; | ||
| 6731 | drwav_uint32 bytesPerFrame; | ||
| 6732 | drwav_uint32 bytesPerSample; | ||
| 6733 | drwav_uint64 samplesRead; | ||
| 6734 | |||
| 6735 | if (pBufferOut == NULL) { | ||
| 6736 | return drwav_read_pcm_frames(pWav, framesToRead, NULL); | ||
| 6737 | } | ||
| 6738 | |||
| 6739 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 6740 | if (bytesPerFrame == 0) { | ||
| 6741 | return 0; | ||
| 6742 | } | ||
| 6743 | |||
| 6744 | bytesPerSample = bytesPerFrame / pWav->channels; | ||
| 6745 | if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { | ||
| 6746 | return 0; /* Only byte-aligned formats are supported. */ | ||
| 6747 | } | ||
| 6748 | |||
| 6749 | totalFramesRead = 0; | ||
| 6750 | |||
| 6751 | while (framesToRead > 0) { | ||
| 6752 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); | ||
| 6753 | drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); | ||
| 6754 | if (framesRead == 0) { | ||
| 6755 | break; | ||
| 6756 | } | ||
| 6757 | |||
| 6758 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 6759 | |||
| 6760 | /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */ | ||
| 6761 | samplesRead = framesRead * pWav->channels; | ||
| 6762 | if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { | ||
| 6763 | DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */ | ||
| 6764 | break; | ||
| 6765 | } | ||
| 6766 | |||
| 6767 | drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); | ||
| 6768 | |||
| 6769 | /* | ||
| 6770 | Just like with alaw, for some reason the signs between libsndfile and dr_wav are opposite. We just need to | ||
| 6771 | swap the sign if we're compiling with libsndfile compatiblity so our automated tests don't fail. | ||
| 6772 | */ | ||
| 6773 | #ifdef DR_WAV_LIBSNDFILE_COMPAT | ||
| 6774 | { | ||
| 6775 | if (pWav->container == drwav_container_aiff) { | ||
| 6776 | drwav_uint64 iSample; | ||
| 6777 | for (iSample = 0; iSample < samplesRead; iSample += 1) { | ||
| 6778 | pBufferOut[iSample] = -pBufferOut[iSample]; | ||
| 6779 | } | ||
| 6780 | } | ||
| 6781 | } | ||
| 6782 | #endif | ||
| 6783 | |||
| 6784 | pBufferOut += samplesRead; | ||
| 6785 | framesToRead -= framesRead; | ||
| 6786 | totalFramesRead += framesRead; | ||
| 6787 | } | ||
| 6788 | |||
| 6789 | return totalFramesRead; | ||
| 6790 | } | ||
| 6791 | |||
| 6792 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) | ||
| 6793 | { | ||
| 6794 | if (pWav == NULL || framesToRead == 0) { | ||
| 6795 | return 0; | ||
| 6796 | } | ||
| 6797 | |||
| 6798 | if (pBufferOut == NULL) { | ||
| 6799 | return drwav_read_pcm_frames(pWav, framesToRead, NULL); | ||
| 6800 | } | ||
| 6801 | |||
| 6802 | /* Don't try to read more samples than can potentially fit in the output buffer. */ | ||
| 6803 | if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) { | ||
| 6804 | framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels; | ||
| 6805 | } | ||
| 6806 | |||
| 6807 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { | ||
| 6808 | return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); | ||
| 6809 | } | ||
| 6810 | |||
| 6811 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { | ||
| 6812 | return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); | ||
| 6813 | } | ||
| 6814 | |||
| 6815 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { | ||
| 6816 | return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); | ||
| 6817 | } | ||
| 6818 | |||
| 6819 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { | ||
| 6820 | return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); | ||
| 6821 | } | ||
| 6822 | |||
| 6823 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { | ||
| 6824 | return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); | ||
| 6825 | } | ||
| 6826 | |||
| 6827 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { | ||
| 6828 | return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); | ||
| 6829 | } | ||
| 6830 | |||
| 6831 | return 0; | ||
| 6832 | } | ||
| 6833 | |||
| 6834 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) | ||
| 6835 | { | ||
| 6836 | drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); | ||
| 6837 | if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { | ||
| 6838 | drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); | ||
| 6839 | } | ||
| 6840 | |||
| 6841 | return framesRead; | ||
| 6842 | } | ||
| 6843 | |||
| 6844 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) | ||
| 6845 | { | ||
| 6846 | drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); | ||
| 6847 | if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { | ||
| 6848 | drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); | ||
| 6849 | } | ||
| 6850 | |||
| 6851 | return framesRead; | ||
| 6852 | } | ||
| 6853 | |||
| 6854 | |||
| 6855 | DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) | ||
| 6856 | { | ||
| 6857 | int r; | ||
| 6858 | size_t i; | ||
| 6859 | for (i = 0; i < sampleCount; ++i) { | ||
| 6860 | int x = pIn[i]; | ||
| 6861 | r = x << 8; | ||
| 6862 | r = r - 32768; | ||
| 6863 | pOut[i] = (short)r; | ||
| 6864 | } | ||
| 6865 | } | ||
| 6866 | |||
| 6867 | DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) | ||
| 6868 | { | ||
| 6869 | int r; | ||
| 6870 | size_t i; | ||
| 6871 | for (i = 0; i < sampleCount; ++i) { | ||
| 6872 | int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8; | ||
| 6873 | r = x >> 8; | ||
| 6874 | pOut[i] = (short)r; | ||
| 6875 | } | ||
| 6876 | } | ||
| 6877 | |||
| 6878 | DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount) | ||
| 6879 | { | ||
| 6880 | int r; | ||
| 6881 | size_t i; | ||
| 6882 | for (i = 0; i < sampleCount; ++i) { | ||
| 6883 | int x = pIn[i]; | ||
| 6884 | r = x >> 16; | ||
| 6885 | pOut[i] = (short)r; | ||
| 6886 | } | ||
| 6887 | } | ||
| 6888 | |||
| 6889 | DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount) | ||
| 6890 | { | ||
| 6891 | int r; | ||
| 6892 | size_t i; | ||
| 6893 | for (i = 0; i < sampleCount; ++i) { | ||
| 6894 | float x = pIn[i]; | ||
| 6895 | float c; | ||
| 6896 | c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); | ||
| 6897 | c = c + 1; | ||
| 6898 | r = (int)(c * 32767.5f); | ||
| 6899 | r = r - 32768; | ||
| 6900 | pOut[i] = (short)r; | ||
| 6901 | } | ||
| 6902 | } | ||
| 6903 | |||
| 6904 | DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount) | ||
| 6905 | { | ||
| 6906 | int r; | ||
| 6907 | size_t i; | ||
| 6908 | for (i = 0; i < sampleCount; ++i) { | ||
| 6909 | double x = pIn[i]; | ||
| 6910 | double c; | ||
| 6911 | c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); | ||
| 6912 | c = c + 1; | ||
| 6913 | r = (int)(c * 32767.5); | ||
| 6914 | r = r - 32768; | ||
| 6915 | pOut[i] = (short)r; | ||
| 6916 | } | ||
| 6917 | } | ||
| 6918 | |||
| 6919 | DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) | ||
| 6920 | { | ||
| 6921 | size_t i; | ||
| 6922 | for (i = 0; i < sampleCount; ++i) { | ||
| 6923 | pOut[i] = drwav__alaw_to_s16(pIn[i]); | ||
| 6924 | } | ||
| 6925 | } | ||
| 6926 | |||
| 6927 | DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) | ||
| 6928 | { | ||
| 6929 | size_t i; | ||
| 6930 | for (i = 0; i < sampleCount; ++i) { | ||
| 6931 | pOut[i] = drwav__mulaw_to_s16(pIn[i]); | ||
| 6932 | } | ||
| 6933 | } | ||
| 6934 | |||
| 6935 | |||
| 6936 | DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) | ||
| 6937 | { | ||
| 6938 | unsigned int i; | ||
| 6939 | |||
| 6940 | /* Special case for 8-bit sample data because it's treated as unsigned. */ | ||
| 6941 | if (bytesPerSample == 1) { | ||
| 6942 | drwav_u8_to_f32(pOut, pIn, sampleCount); | ||
| 6943 | return; | ||
| 6944 | } | ||
| 6945 | |||
| 6946 | /* Slightly more optimal implementation for common formats. */ | ||
| 6947 | if (bytesPerSample == 2) { | ||
| 6948 | drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount); | ||
| 6949 | return; | ||
| 6950 | } | ||
| 6951 | if (bytesPerSample == 3) { | ||
| 6952 | drwav_s24_to_f32(pOut, pIn, sampleCount); | ||
| 6953 | return; | ||
| 6954 | } | ||
| 6955 | if (bytesPerSample == 4) { | ||
| 6956 | drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount); | ||
| 6957 | return; | ||
| 6958 | } | ||
| 6959 | |||
| 6960 | |||
| 6961 | /* Anything more than 64 bits per sample is not supported. */ | ||
| 6962 | if (bytesPerSample > 8) { | ||
| 6963 | DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); | ||
| 6964 | return; | ||
| 6965 | } | ||
| 6966 | |||
| 6967 | |||
| 6968 | /* Generic, slow converter. */ | ||
| 6969 | for (i = 0; i < sampleCount; ++i) { | ||
| 6970 | drwav_uint64 sample = 0; | ||
| 6971 | unsigned int shift = (8 - bytesPerSample) * 8; | ||
| 6972 | |||
| 6973 | unsigned int j; | ||
| 6974 | for (j = 0; j < bytesPerSample; j += 1) { | ||
| 6975 | DRWAV_ASSERT(j < 8); | ||
| 6976 | sample |= (drwav_uint64)(pIn[j]) << shift; | ||
| 6977 | shift += 8; | ||
| 6978 | } | ||
| 6979 | |||
| 6980 | pIn += j; | ||
| 6981 | *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0); | ||
| 6982 | } | ||
| 6983 | } | ||
| 6984 | |||
| 6985 | DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) | ||
| 6986 | { | ||
| 6987 | if (bytesPerSample == 4) { | ||
| 6988 | unsigned int i; | ||
| 6989 | for (i = 0; i < sampleCount; ++i) { | ||
| 6990 | *pOut++ = ((const float*)pIn)[i]; | ||
| 6991 | } | ||
| 6992 | return; | ||
| 6993 | } else if (bytesPerSample == 8) { | ||
| 6994 | drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount); | ||
| 6995 | return; | ||
| 6996 | } else { | ||
| 6997 | /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */ | ||
| 6998 | DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); | ||
| 6999 | return; | ||
| 7000 | } | ||
| 7001 | } | ||
| 7002 | |||
| 7003 | |||
| 7004 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) | ||
| 7005 | { | ||
| 7006 | drwav_uint64 totalFramesRead; | ||
| 7007 | drwav_uint8 sampleData[4096] = {0}; | ||
| 7008 | drwav_uint32 bytesPerFrame; | ||
| 7009 | drwav_uint32 bytesPerSample; | ||
| 7010 | drwav_uint64 samplesRead; | ||
| 7011 | |||
| 7012 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 7013 | if (bytesPerFrame == 0) { | ||
| 7014 | return 0; | ||
| 7015 | } | ||
| 7016 | |||
| 7017 | bytesPerSample = bytesPerFrame / pWav->channels; | ||
| 7018 | if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { | ||
| 7019 | return 0; /* Only byte-aligned formats are supported. */ | ||
| 7020 | } | ||
| 7021 | |||
| 7022 | totalFramesRead = 0; | ||
| 7023 | |||
| 7024 | while (framesToRead > 0) { | ||
| 7025 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); | ||
| 7026 | drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); | ||
| 7027 | if (framesRead == 0) { | ||
| 7028 | break; | ||
| 7029 | } | ||
| 7030 | |||
| 7031 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 7032 | |||
| 7033 | /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */ | ||
| 7034 | samplesRead = framesRead * pWav->channels; | ||
| 7035 | if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { | ||
| 7036 | DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */ | ||
| 7037 | break; | ||
| 7038 | } | ||
| 7039 | |||
| 7040 | drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); | ||
| 7041 | |||
| 7042 | pBufferOut += samplesRead; | ||
| 7043 | framesToRead -= framesRead; | ||
| 7044 | totalFramesRead += framesRead; | ||
| 7045 | } | ||
| 7046 | |||
| 7047 | return totalFramesRead; | ||
| 7048 | } | ||
| 7049 | |||
| 7050 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) | ||
| 7051 | { | ||
| 7052 | /* | ||
| 7053 | We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't | ||
| 7054 | want to duplicate that code. | ||
| 7055 | */ | ||
| 7056 | drwav_uint64 totalFramesRead; | ||
| 7057 | drwav_int16 samples16[2048]; | ||
| 7058 | |||
| 7059 | totalFramesRead = 0; | ||
| 7060 | |||
| 7061 | while (framesToRead > 0) { | ||
| 7062 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); | ||
| 7063 | drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); | ||
| 7064 | if (framesRead == 0) { | ||
| 7065 | break; | ||
| 7066 | } | ||
| 7067 | |||
| 7068 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 7069 | |||
| 7070 | drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */ | ||
| 7071 | |||
| 7072 | pBufferOut += framesRead*pWav->channels; | ||
| 7073 | framesToRead -= framesRead; | ||
| 7074 | totalFramesRead += framesRead; | ||
| 7075 | } | ||
| 7076 | |||
| 7077 | return totalFramesRead; | ||
| 7078 | } | ||
| 7079 | |||
| 7080 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) | ||
| 7081 | { | ||
| 7082 | drwav_uint64 totalFramesRead; | ||
| 7083 | drwav_uint8 sampleData[4096] = {0}; | ||
| 7084 | drwav_uint32 bytesPerFrame; | ||
| 7085 | drwav_uint32 bytesPerSample; | ||
| 7086 | drwav_uint64 samplesRead; | ||
| 7087 | |||
| 7088 | /* Fast path. */ | ||
| 7089 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { | ||
| 7090 | return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); | ||
| 7091 | } | ||
| 7092 | |||
| 7093 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 7094 | if (bytesPerFrame == 0) { | ||
| 7095 | return 0; | ||
| 7096 | } | ||
| 7097 | |||
| 7098 | bytesPerSample = bytesPerFrame / pWav->channels; | ||
| 7099 | if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { | ||
| 7100 | return 0; /* Only byte-aligned formats are supported. */ | ||
| 7101 | } | ||
| 7102 | |||
| 7103 | totalFramesRead = 0; | ||
| 7104 | |||
| 7105 | while (framesToRead > 0) { | ||
| 7106 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); | ||
| 7107 | drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); | ||
| 7108 | if (framesRead == 0) { | ||
| 7109 | break; | ||
| 7110 | } | ||
| 7111 | |||
| 7112 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 7113 | |||
| 7114 | /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */ | ||
| 7115 | samplesRead = framesRead * pWav->channels; | ||
| 7116 | if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { | ||
| 7117 | DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */ | ||
| 7118 | break; | ||
| 7119 | } | ||
| 7120 | |||
| 7121 | drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); | ||
| 7122 | |||
| 7123 | pBufferOut += samplesRead; | ||
| 7124 | framesToRead -= framesRead; | ||
| 7125 | totalFramesRead += framesRead; | ||
| 7126 | } | ||
| 7127 | |||
| 7128 | return totalFramesRead; | ||
| 7129 | } | ||
| 7130 | |||
| 7131 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) | ||
| 7132 | { | ||
| 7133 | drwav_uint64 totalFramesRead; | ||
| 7134 | drwav_uint8 sampleData[4096] = {0}; | ||
| 7135 | drwav_uint32 bytesPerFrame; | ||
| 7136 | drwav_uint32 bytesPerSample; | ||
| 7137 | drwav_uint64 samplesRead; | ||
| 7138 | |||
| 7139 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 7140 | if (bytesPerFrame == 0) { | ||
| 7141 | return 0; | ||
| 7142 | } | ||
| 7143 | |||
| 7144 | bytesPerSample = bytesPerFrame / pWav->channels; | ||
| 7145 | if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { | ||
| 7146 | return 0; /* Only byte-aligned formats are supported. */ | ||
| 7147 | } | ||
| 7148 | |||
| 7149 | totalFramesRead = 0; | ||
| 7150 | |||
| 7151 | while (framesToRead > 0) { | ||
| 7152 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); | ||
| 7153 | drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); | ||
| 7154 | if (framesRead == 0) { | ||
| 7155 | break; | ||
| 7156 | } | ||
| 7157 | |||
| 7158 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 7159 | |||
| 7160 | /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */ | ||
| 7161 | samplesRead = framesRead * pWav->channels; | ||
| 7162 | if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { | ||
| 7163 | DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */ | ||
| 7164 | break; | ||
| 7165 | } | ||
| 7166 | |||
| 7167 | drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); | ||
| 7168 | |||
| 7169 | #ifdef DR_WAV_LIBSNDFILE_COMPAT | ||
| 7170 | { | ||
| 7171 | if (pWav->container == drwav_container_aiff) { | ||
| 7172 | drwav_uint64 iSample; | ||
| 7173 | for (iSample = 0; iSample < samplesRead; iSample += 1) { | ||
| 7174 | pBufferOut[iSample] = -pBufferOut[iSample]; | ||
| 7175 | } | ||
| 7176 | } | ||
| 7177 | } | ||
| 7178 | #endif | ||
| 7179 | |||
| 7180 | pBufferOut += samplesRead; | ||
| 7181 | framesToRead -= framesRead; | ||
| 7182 | totalFramesRead += framesRead; | ||
| 7183 | } | ||
| 7184 | |||
| 7185 | return totalFramesRead; | ||
| 7186 | } | ||
| 7187 | |||
| 7188 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) | ||
| 7189 | { | ||
| 7190 | drwav_uint64 totalFramesRead; | ||
| 7191 | drwav_uint8 sampleData[4096] = {0}; | ||
| 7192 | drwav_uint32 bytesPerFrame; | ||
| 7193 | drwav_uint32 bytesPerSample; | ||
| 7194 | drwav_uint64 samplesRead; | ||
| 7195 | |||
| 7196 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 7197 | if (bytesPerFrame == 0) { | ||
| 7198 | return 0; | ||
| 7199 | } | ||
| 7200 | |||
| 7201 | bytesPerSample = bytesPerFrame / pWav->channels; | ||
| 7202 | if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { | ||
| 7203 | return 0; /* Only byte-aligned formats are supported. */ | ||
| 7204 | } | ||
| 7205 | |||
| 7206 | totalFramesRead = 0; | ||
| 7207 | |||
| 7208 | while (framesToRead > 0) { | ||
| 7209 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); | ||
| 7210 | drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); | ||
| 7211 | if (framesRead == 0) { | ||
| 7212 | break; | ||
| 7213 | } | ||
| 7214 | |||
| 7215 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 7216 | |||
| 7217 | /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */ | ||
| 7218 | samplesRead = framesRead * pWav->channels; | ||
| 7219 | if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { | ||
| 7220 | DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */ | ||
| 7221 | break; | ||
| 7222 | } | ||
| 7223 | |||
| 7224 | drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); | ||
| 7225 | |||
| 7226 | #ifdef DR_WAV_LIBSNDFILE_COMPAT | ||
| 7227 | { | ||
| 7228 | if (pWav->container == drwav_container_aiff) { | ||
| 7229 | drwav_uint64 iSample; | ||
| 7230 | for (iSample = 0; iSample < samplesRead; iSample += 1) { | ||
| 7231 | pBufferOut[iSample] = -pBufferOut[iSample]; | ||
| 7232 | } | ||
| 7233 | } | ||
| 7234 | } | ||
| 7235 | #endif | ||
| 7236 | |||
| 7237 | pBufferOut += samplesRead; | ||
| 7238 | framesToRead -= framesRead; | ||
| 7239 | totalFramesRead += framesRead; | ||
| 7240 | } | ||
| 7241 | |||
| 7242 | return totalFramesRead; | ||
| 7243 | } | ||
| 7244 | |||
| 7245 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) | ||
| 7246 | { | ||
| 7247 | if (pWav == NULL || framesToRead == 0) { | ||
| 7248 | return 0; | ||
| 7249 | } | ||
| 7250 | |||
| 7251 | if (pBufferOut == NULL) { | ||
| 7252 | return drwav_read_pcm_frames(pWav, framesToRead, NULL); | ||
| 7253 | } | ||
| 7254 | |||
| 7255 | /* Don't try to read more samples than can potentially fit in the output buffer. */ | ||
| 7256 | if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) { | ||
| 7257 | framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels; | ||
| 7258 | } | ||
| 7259 | |||
| 7260 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { | ||
| 7261 | return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); | ||
| 7262 | } | ||
| 7263 | |||
| 7264 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { | ||
| 7265 | return drwav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); | ||
| 7266 | } | ||
| 7267 | |||
| 7268 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { | ||
| 7269 | return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); | ||
| 7270 | } | ||
| 7271 | |||
| 7272 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { | ||
| 7273 | return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); | ||
| 7274 | } | ||
| 7275 | |||
| 7276 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { | ||
| 7277 | return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); | ||
| 7278 | } | ||
| 7279 | |||
| 7280 | return 0; | ||
| 7281 | } | ||
| 7282 | |||
| 7283 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) | ||
| 7284 | { | ||
| 7285 | drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); | ||
| 7286 | if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { | ||
| 7287 | drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); | ||
| 7288 | } | ||
| 7289 | |||
| 7290 | return framesRead; | ||
| 7291 | } | ||
| 7292 | |||
| 7293 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) | ||
| 7294 | { | ||
| 7295 | drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); | ||
| 7296 | if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { | ||
| 7297 | drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); | ||
| 7298 | } | ||
| 7299 | |||
| 7300 | return framesRead; | ||
| 7301 | } | ||
| 7302 | |||
| 7303 | |||
| 7304 | DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) | ||
| 7305 | { | ||
| 7306 | size_t i; | ||
| 7307 | |||
| 7308 | if (pOut == NULL || pIn == NULL) { | ||
| 7309 | return; | ||
| 7310 | } | ||
| 7311 | |||
| 7312 | #ifdef DR_WAV_LIBSNDFILE_COMPAT | ||
| 7313 | /* | ||
| 7314 | It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears | ||
| 7315 | libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note | ||
| 7316 | the divisor of 256 vs 255). I use libsndfile as a benchmark for testing, so I'm therefore leaving this block here just for my automated | ||
| 7317 | correctness testing. This is disabled by default. | ||
| 7318 | */ | ||
| 7319 | for (i = 0; i < sampleCount; ++i) { | ||
| 7320 | *pOut++ = (pIn[i] / 256.0f) * 2 - 1; | ||
| 7321 | } | ||
| 7322 | #else | ||
| 7323 | for (i = 0; i < sampleCount; ++i) { | ||
| 7324 | float x = pIn[i]; | ||
| 7325 | x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */ | ||
| 7326 | x = x - 1; /* 0..2 to -1..1 */ | ||
| 7327 | |||
| 7328 | *pOut++ = x; | ||
| 7329 | } | ||
| 7330 | #endif | ||
| 7331 | } | ||
| 7332 | |||
| 7333 | DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount) | ||
| 7334 | { | ||
| 7335 | size_t i; | ||
| 7336 | |||
| 7337 | if (pOut == NULL || pIn == NULL) { | ||
| 7338 | return; | ||
| 7339 | } | ||
| 7340 | |||
| 7341 | for (i = 0; i < sampleCount; ++i) { | ||
| 7342 | *pOut++ = pIn[i] * 0.000030517578125f; | ||
| 7343 | } | ||
| 7344 | } | ||
| 7345 | |||
| 7346 | DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) | ||
| 7347 | { | ||
| 7348 | size_t i; | ||
| 7349 | |||
| 7350 | if (pOut == NULL || pIn == NULL) { | ||
| 7351 | return; | ||
| 7352 | } | ||
| 7353 | |||
| 7354 | for (i = 0; i < sampleCount; ++i) { | ||
| 7355 | double x; | ||
| 7356 | drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8); | ||
| 7357 | drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16); | ||
| 7358 | drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24); | ||
| 7359 | |||
| 7360 | x = (double)((drwav_int32)(a | b | c) >> 8); | ||
| 7361 | *pOut++ = (float)(x * 0.00000011920928955078125); | ||
| 7362 | } | ||
| 7363 | } | ||
| 7364 | |||
| 7365 | DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount) | ||
| 7366 | { | ||
| 7367 | size_t i; | ||
| 7368 | if (pOut == NULL || pIn == NULL) { | ||
| 7369 | return; | ||
| 7370 | } | ||
| 7371 | |||
| 7372 | for (i = 0; i < sampleCount; ++i) { | ||
| 7373 | *pOut++ = (float)(pIn[i] / 2147483648.0); | ||
| 7374 | } | ||
| 7375 | } | ||
| 7376 | |||
| 7377 | DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) | ||
| 7378 | { | ||
| 7379 | size_t i; | ||
| 7380 | |||
| 7381 | if (pOut == NULL || pIn == NULL) { | ||
| 7382 | return; | ||
| 7383 | } | ||
| 7384 | |||
| 7385 | for (i = 0; i < sampleCount; ++i) { | ||
| 7386 | *pOut++ = (float)pIn[i]; | ||
| 7387 | } | ||
| 7388 | } | ||
| 7389 | |||
| 7390 | DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) | ||
| 7391 | { | ||
| 7392 | size_t i; | ||
| 7393 | |||
| 7394 | if (pOut == NULL || pIn == NULL) { | ||
| 7395 | return; | ||
| 7396 | } | ||
| 7397 | |||
| 7398 | for (i = 0; i < sampleCount; ++i) { | ||
| 7399 | *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f; | ||
| 7400 | } | ||
| 7401 | } | ||
| 7402 | |||
| 7403 | DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) | ||
| 7404 | { | ||
| 7405 | size_t i; | ||
| 7406 | |||
| 7407 | if (pOut == NULL || pIn == NULL) { | ||
| 7408 | return; | ||
| 7409 | } | ||
| 7410 | |||
| 7411 | for (i = 0; i < sampleCount; ++i) { | ||
| 7412 | *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f; | ||
| 7413 | } | ||
| 7414 | } | ||
| 7415 | |||
| 7416 | |||
| 7417 | |||
| 7418 | DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) | ||
| 7419 | { | ||
| 7420 | unsigned int i; | ||
| 7421 | |||
| 7422 | /* Special case for 8-bit sample data because it's treated as unsigned. */ | ||
| 7423 | if (bytesPerSample == 1) { | ||
| 7424 | drwav_u8_to_s32(pOut, pIn, totalSampleCount); | ||
| 7425 | return; | ||
| 7426 | } | ||
| 7427 | |||
| 7428 | /* Slightly more optimal implementation for common formats. */ | ||
| 7429 | if (bytesPerSample == 2) { | ||
| 7430 | drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount); | ||
| 7431 | return; | ||
| 7432 | } | ||
| 7433 | if (bytesPerSample == 3) { | ||
| 7434 | drwav_s24_to_s32(pOut, pIn, totalSampleCount); | ||
| 7435 | return; | ||
| 7436 | } | ||
| 7437 | if (bytesPerSample == 4) { | ||
| 7438 | for (i = 0; i < totalSampleCount; ++i) { | ||
| 7439 | *pOut++ = ((const drwav_int32*)pIn)[i]; | ||
| 7440 | } | ||
| 7441 | return; | ||
| 7442 | } | ||
| 7443 | |||
| 7444 | |||
| 7445 | /* Anything more than 64 bits per sample is not supported. */ | ||
| 7446 | if (bytesPerSample > 8) { | ||
| 7447 | DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); | ||
| 7448 | return; | ||
| 7449 | } | ||
| 7450 | |||
| 7451 | |||
| 7452 | /* Generic, slow converter. */ | ||
| 7453 | for (i = 0; i < totalSampleCount; ++i) { | ||
| 7454 | drwav_uint64 sample = 0; | ||
| 7455 | unsigned int shift = (8 - bytesPerSample) * 8; | ||
| 7456 | |||
| 7457 | unsigned int j; | ||
| 7458 | for (j = 0; j < bytesPerSample; j += 1) { | ||
| 7459 | DRWAV_ASSERT(j < 8); | ||
| 7460 | sample |= (drwav_uint64)(pIn[j]) << shift; | ||
| 7461 | shift += 8; | ||
| 7462 | } | ||
| 7463 | |||
| 7464 | pIn += j; | ||
| 7465 | *pOut++ = (drwav_int32)((drwav_int64)sample >> 32); | ||
| 7466 | } | ||
| 7467 | } | ||
| 7468 | |||
| 7469 | DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) | ||
| 7470 | { | ||
| 7471 | if (bytesPerSample == 4) { | ||
| 7472 | drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); | ||
| 7473 | return; | ||
| 7474 | } else if (bytesPerSample == 8) { | ||
| 7475 | drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); | ||
| 7476 | return; | ||
| 7477 | } else { | ||
| 7478 | /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */ | ||
| 7479 | DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); | ||
| 7480 | return; | ||
| 7481 | } | ||
| 7482 | } | ||
| 7483 | |||
| 7484 | |||
| 7485 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) | ||
| 7486 | { | ||
| 7487 | drwav_uint64 totalFramesRead; | ||
| 7488 | drwav_uint8 sampleData[4096] = {0}; | ||
| 7489 | drwav_uint32 bytesPerFrame; | ||
| 7490 | drwav_uint32 bytesPerSample; | ||
| 7491 | drwav_uint64 samplesRead; | ||
| 7492 | |||
| 7493 | /* Fast path. */ | ||
| 7494 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { | ||
| 7495 | return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); | ||
| 7496 | } | ||
| 7497 | |||
| 7498 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 7499 | if (bytesPerFrame == 0) { | ||
| 7500 | return 0; | ||
| 7501 | } | ||
| 7502 | |||
| 7503 | bytesPerSample = bytesPerFrame / pWav->channels; | ||
| 7504 | if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { | ||
| 7505 | return 0; /* Only byte-aligned formats are supported. */ | ||
| 7506 | } | ||
| 7507 | |||
| 7508 | totalFramesRead = 0; | ||
| 7509 | |||
| 7510 | while (framesToRead > 0) { | ||
| 7511 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); | ||
| 7512 | drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); | ||
| 7513 | if (framesRead == 0) { | ||
| 7514 | break; | ||
| 7515 | } | ||
| 7516 | |||
| 7517 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 7518 | |||
| 7519 | /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */ | ||
| 7520 | samplesRead = framesRead * pWav->channels; | ||
| 7521 | if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { | ||
| 7522 | DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */ | ||
| 7523 | break; | ||
| 7524 | } | ||
| 7525 | |||
| 7526 | drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); | ||
| 7527 | |||
| 7528 | pBufferOut += samplesRead; | ||
| 7529 | framesToRead -= framesRead; | ||
| 7530 | totalFramesRead += framesRead; | ||
| 7531 | } | ||
| 7532 | |||
| 7533 | return totalFramesRead; | ||
| 7534 | } | ||
| 7535 | |||
| 7536 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) | ||
| 7537 | { | ||
| 7538 | /* | ||
| 7539 | We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't | ||
| 7540 | want to duplicate that code. | ||
| 7541 | */ | ||
| 7542 | drwav_uint64 totalFramesRead = 0; | ||
| 7543 | drwav_int16 samples16[2048]; | ||
| 7544 | |||
| 7545 | while (framesToRead > 0) { | ||
| 7546 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); | ||
| 7547 | drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); | ||
| 7548 | if (framesRead == 0) { | ||
| 7549 | break; | ||
| 7550 | } | ||
| 7551 | |||
| 7552 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 7553 | |||
| 7554 | drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */ | ||
| 7555 | |||
| 7556 | pBufferOut += framesRead*pWav->channels; | ||
| 7557 | framesToRead -= framesRead; | ||
| 7558 | totalFramesRead += framesRead; | ||
| 7559 | } | ||
| 7560 | |||
| 7561 | return totalFramesRead; | ||
| 7562 | } | ||
| 7563 | |||
| 7564 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) | ||
| 7565 | { | ||
| 7566 | drwav_uint64 totalFramesRead; | ||
| 7567 | drwav_uint8 sampleData[4096] = {0}; | ||
| 7568 | drwav_uint32 bytesPerFrame; | ||
| 7569 | drwav_uint32 bytesPerSample; | ||
| 7570 | drwav_uint64 samplesRead; | ||
| 7571 | |||
| 7572 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 7573 | if (bytesPerFrame == 0) { | ||
| 7574 | return 0; | ||
| 7575 | } | ||
| 7576 | |||
| 7577 | bytesPerSample = bytesPerFrame / pWav->channels; | ||
| 7578 | if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { | ||
| 7579 | return 0; /* Only byte-aligned formats are supported. */ | ||
| 7580 | } | ||
| 7581 | |||
| 7582 | totalFramesRead = 0; | ||
| 7583 | |||
| 7584 | while (framesToRead > 0) { | ||
| 7585 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); | ||
| 7586 | drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); | ||
| 7587 | if (framesRead == 0) { | ||
| 7588 | break; | ||
| 7589 | } | ||
| 7590 | |||
| 7591 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 7592 | |||
| 7593 | /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */ | ||
| 7594 | samplesRead = framesRead * pWav->channels; | ||
| 7595 | if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { | ||
| 7596 | DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */ | ||
| 7597 | break; | ||
| 7598 | } | ||
| 7599 | |||
| 7600 | drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); | ||
| 7601 | |||
| 7602 | pBufferOut += samplesRead; | ||
| 7603 | framesToRead -= framesRead; | ||
| 7604 | totalFramesRead += framesRead; | ||
| 7605 | } | ||
| 7606 | |||
| 7607 | return totalFramesRead; | ||
| 7608 | } | ||
| 7609 | |||
| 7610 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) | ||
| 7611 | { | ||
| 7612 | drwav_uint64 totalFramesRead; | ||
| 7613 | drwav_uint8 sampleData[4096] = {0}; | ||
| 7614 | drwav_uint32 bytesPerFrame; | ||
| 7615 | drwav_uint32 bytesPerSample; | ||
| 7616 | drwav_uint64 samplesRead; | ||
| 7617 | |||
| 7618 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 7619 | if (bytesPerFrame == 0) { | ||
| 7620 | return 0; | ||
| 7621 | } | ||
| 7622 | |||
| 7623 | bytesPerSample = bytesPerFrame / pWav->channels; | ||
| 7624 | if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { | ||
| 7625 | return 0; /* Only byte-aligned formats are supported. */ | ||
| 7626 | } | ||
| 7627 | |||
| 7628 | totalFramesRead = 0; | ||
| 7629 | |||
| 7630 | while (framesToRead > 0) { | ||
| 7631 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); | ||
| 7632 | drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); | ||
| 7633 | if (framesRead == 0) { | ||
| 7634 | break; | ||
| 7635 | } | ||
| 7636 | |||
| 7637 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 7638 | |||
| 7639 | /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */ | ||
| 7640 | samplesRead = framesRead * pWav->channels; | ||
| 7641 | if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { | ||
| 7642 | DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */ | ||
| 7643 | break; | ||
| 7644 | } | ||
| 7645 | |||
| 7646 | drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); | ||
| 7647 | |||
| 7648 | #ifdef DR_WAV_LIBSNDFILE_COMPAT | ||
| 7649 | { | ||
| 7650 | if (pWav->container == drwav_container_aiff) { | ||
| 7651 | drwav_uint64 iSample; | ||
| 7652 | for (iSample = 0; iSample < samplesRead; iSample += 1) { | ||
| 7653 | pBufferOut[iSample] = -pBufferOut[iSample]; | ||
| 7654 | } | ||
| 7655 | } | ||
| 7656 | } | ||
| 7657 | #endif | ||
| 7658 | |||
| 7659 | pBufferOut += samplesRead; | ||
| 7660 | framesToRead -= framesRead; | ||
| 7661 | totalFramesRead += framesRead; | ||
| 7662 | } | ||
| 7663 | |||
| 7664 | return totalFramesRead; | ||
| 7665 | } | ||
| 7666 | |||
| 7667 | DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) | ||
| 7668 | { | ||
| 7669 | drwav_uint64 totalFramesRead; | ||
| 7670 | drwav_uint8 sampleData[4096] = {0}; | ||
| 7671 | drwav_uint32 bytesPerFrame; | ||
| 7672 | drwav_uint32 bytesPerSample; | ||
| 7673 | drwav_uint64 samplesRead; | ||
| 7674 | |||
| 7675 | bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | ||
| 7676 | if (bytesPerFrame == 0) { | ||
| 7677 | return 0; | ||
| 7678 | } | ||
| 7679 | |||
| 7680 | bytesPerSample = bytesPerFrame / pWav->channels; | ||
| 7681 | if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { | ||
| 7682 | return 0; /* Only byte-aligned formats are supported. */ | ||
| 7683 | } | ||
| 7684 | |||
| 7685 | totalFramesRead = 0; | ||
| 7686 | |||
| 7687 | while (framesToRead > 0) { | ||
| 7688 | drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); | ||
| 7689 | drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); | ||
| 7690 | if (framesRead == 0) { | ||
| 7691 | break; | ||
| 7692 | } | ||
| 7693 | |||
| 7694 | DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */ | ||
| 7695 | |||
| 7696 | /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */ | ||
| 7697 | samplesRead = framesRead * pWav->channels; | ||
| 7698 | if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { | ||
| 7699 | DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */ | ||
| 7700 | break; | ||
| 7701 | } | ||
| 7702 | |||
| 7703 | drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); | ||
| 7704 | |||
| 7705 | #ifdef DR_WAV_LIBSNDFILE_COMPAT | ||
| 7706 | { | ||
| 7707 | if (pWav->container == drwav_container_aiff) { | ||
| 7708 | drwav_uint64 iSample; | ||
| 7709 | for (iSample = 0; iSample < samplesRead; iSample += 1) { | ||
| 7710 | pBufferOut[iSample] = -pBufferOut[iSample]; | ||
| 7711 | } | ||
| 7712 | } | ||
| 7713 | } | ||
| 7714 | #endif | ||
| 7715 | |||
| 7716 | pBufferOut += samplesRead; | ||
| 7717 | framesToRead -= framesRead; | ||
| 7718 | totalFramesRead += framesRead; | ||
| 7719 | } | ||
| 7720 | |||
| 7721 | return totalFramesRead; | ||
| 7722 | } | ||
| 7723 | |||
| 7724 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) | ||
| 7725 | { | ||
| 7726 | if (pWav == NULL || framesToRead == 0) { | ||
| 7727 | return 0; | ||
| 7728 | } | ||
| 7729 | |||
| 7730 | if (pBufferOut == NULL) { | ||
| 7731 | return drwav_read_pcm_frames(pWav, framesToRead, NULL); | ||
| 7732 | } | ||
| 7733 | |||
| 7734 | /* Don't try to read more samples than can potentially fit in the output buffer. */ | ||
| 7735 | if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) { | ||
| 7736 | framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels; | ||
| 7737 | } | ||
| 7738 | |||
| 7739 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { | ||
| 7740 | return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); | ||
| 7741 | } | ||
| 7742 | |||
| 7743 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { | ||
| 7744 | return drwav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); | ||
| 7745 | } | ||
| 7746 | |||
| 7747 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { | ||
| 7748 | return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); | ||
| 7749 | } | ||
| 7750 | |||
| 7751 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { | ||
| 7752 | return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); | ||
| 7753 | } | ||
| 7754 | |||
| 7755 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { | ||
| 7756 | return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); | ||
| 7757 | } | ||
| 7758 | |||
| 7759 | return 0; | ||
| 7760 | } | ||
| 7761 | |||
| 7762 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) | ||
| 7763 | { | ||
| 7764 | drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); | ||
| 7765 | if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { | ||
| 7766 | drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); | ||
| 7767 | } | ||
| 7768 | |||
| 7769 | return framesRead; | ||
| 7770 | } | ||
| 7771 | |||
| 7772 | DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) | ||
| 7773 | { | ||
| 7774 | drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); | ||
| 7775 | if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { | ||
| 7776 | drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); | ||
| 7777 | } | ||
| 7778 | |||
| 7779 | return framesRead; | ||
| 7780 | } | ||
| 7781 | |||
| 7782 | |||
| 7783 | DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) | ||
| 7784 | { | ||
| 7785 | size_t i; | ||
| 7786 | |||
| 7787 | if (pOut == NULL || pIn == NULL) { | ||
| 7788 | return; | ||
| 7789 | } | ||
| 7790 | |||
| 7791 | for (i = 0; i < sampleCount; ++i) { | ||
| 7792 | *pOut++ = ((int)pIn[i] - 128) << 24; | ||
| 7793 | } | ||
| 7794 | } | ||
| 7795 | |||
| 7796 | DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount) | ||
| 7797 | { | ||
| 7798 | size_t i; | ||
| 7799 | |||
| 7800 | if (pOut == NULL || pIn == NULL) { | ||
| 7801 | return; | ||
| 7802 | } | ||
| 7803 | |||
| 7804 | for (i = 0; i < sampleCount; ++i) { | ||
| 7805 | *pOut++ = pIn[i] << 16; | ||
| 7806 | } | ||
| 7807 | } | ||
| 7808 | |||
| 7809 | DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) | ||
| 7810 | { | ||
| 7811 | size_t i; | ||
| 7812 | |||
| 7813 | if (pOut == NULL || pIn == NULL) { | ||
| 7814 | return; | ||
| 7815 | } | ||
| 7816 | |||
| 7817 | for (i = 0; i < sampleCount; ++i) { | ||
| 7818 | unsigned int s0 = pIn[i*3 + 0]; | ||
| 7819 | unsigned int s1 = pIn[i*3 + 1]; | ||
| 7820 | unsigned int s2 = pIn[i*3 + 2]; | ||
| 7821 | |||
| 7822 | drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); | ||
| 7823 | *pOut++ = sample32; | ||
| 7824 | } | ||
| 7825 | } | ||
| 7826 | |||
| 7827 | DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount) | ||
| 7828 | { | ||
| 7829 | size_t i; | ||
| 7830 | |||
| 7831 | if (pOut == NULL || pIn == NULL) { | ||
| 7832 | return; | ||
| 7833 | } | ||
| 7834 | |||
| 7835 | for (i = 0; i < sampleCount; ++i) { | ||
| 7836 | *pOut++ = (drwav_int32)(2147483648.0f * pIn[i]); | ||
| 7837 | } | ||
| 7838 | } | ||
| 7839 | |||
| 7840 | DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount) | ||
| 7841 | { | ||
| 7842 | size_t i; | ||
| 7843 | |||
| 7844 | if (pOut == NULL || pIn == NULL) { | ||
| 7845 | return; | ||
| 7846 | } | ||
| 7847 | |||
| 7848 | for (i = 0; i < sampleCount; ++i) { | ||
| 7849 | *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); | ||
| 7850 | } | ||
| 7851 | } | ||
| 7852 | |||
| 7853 | DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) | ||
| 7854 | { | ||
| 7855 | size_t i; | ||
| 7856 | |||
| 7857 | if (pOut == NULL || pIn == NULL) { | ||
| 7858 | return; | ||
| 7859 | } | ||
| 7860 | |||
| 7861 | for (i = 0; i < sampleCount; ++i) { | ||
| 7862 | *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16; | ||
| 7863 | } | ||
| 7864 | } | ||
| 7865 | |||
| 7866 | DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) | ||
| 7867 | { | ||
| 7868 | size_t i; | ||
| 7869 | |||
| 7870 | if (pOut == NULL || pIn == NULL) { | ||
| 7871 | return; | ||
| 7872 | } | ||
| 7873 | |||
| 7874 | for (i= 0; i < sampleCount; ++i) { | ||
| 7875 | *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16; | ||
| 7876 | } | ||
| 7877 | } | ||
| 7878 | |||
| 7879 | |||
| 7880 | |||
| 7881 | DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) | ||
| 7882 | { | ||
| 7883 | drwav_uint64 sampleDataSize; | ||
| 7884 | drwav_int16* pSampleData; | ||
| 7885 | drwav_uint64 framesRead; | ||
| 7886 | |||
| 7887 | DRWAV_ASSERT(pWav != NULL); | ||
| 7888 | |||
| 7889 | sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16); | ||
| 7890 | if (sampleDataSize > DRWAV_SIZE_MAX) { | ||
| 7891 | drwav_uninit(pWav); | ||
| 7892 | return NULL; /* File's too big. */ | ||
| 7893 | } | ||
| 7894 | |||
| 7895 | pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */ | ||
| 7896 | if (pSampleData == NULL) { | ||
| 7897 | drwav_uninit(pWav); | ||
| 7898 | return NULL; /* Failed to allocate memory. */ | ||
| 7899 | } | ||
| 7900 | |||
| 7901 | framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); | ||
| 7902 | if (framesRead != pWav->totalPCMFrameCount) { | ||
| 7903 | drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); | ||
| 7904 | drwav_uninit(pWav); | ||
| 7905 | return NULL; /* There was an error reading the samples. */ | ||
| 7906 | } | ||
| 7907 | |||
| 7908 | drwav_uninit(pWav); | ||
| 7909 | |||
| 7910 | if (sampleRate) { | ||
| 7911 | *sampleRate = pWav->sampleRate; | ||
| 7912 | } | ||
| 7913 | if (channels) { | ||
| 7914 | *channels = pWav->channels; | ||
| 7915 | } | ||
| 7916 | if (totalFrameCount) { | ||
| 7917 | *totalFrameCount = pWav->totalPCMFrameCount; | ||
| 7918 | } | ||
| 7919 | |||
| 7920 | return pSampleData; | ||
| 7921 | } | ||
| 7922 | |||
| 7923 | DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) | ||
| 7924 | { | ||
| 7925 | drwav_uint64 sampleDataSize; | ||
| 7926 | float* pSampleData; | ||
| 7927 | drwav_uint64 framesRead; | ||
| 7928 | |||
| 7929 | DRWAV_ASSERT(pWav != NULL); | ||
| 7930 | |||
| 7931 | sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); | ||
| 7932 | if (sampleDataSize > DRWAV_SIZE_MAX) { | ||
| 7933 | drwav_uninit(pWav); | ||
| 7934 | return NULL; /* File's too big. */ | ||
| 7935 | } | ||
| 7936 | |||
| 7937 | pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */ | ||
| 7938 | if (pSampleData == NULL) { | ||
| 7939 | drwav_uninit(pWav); | ||
| 7940 | return NULL; /* Failed to allocate memory. */ | ||
| 7941 | } | ||
| 7942 | |||
| 7943 | framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); | ||
| 7944 | if (framesRead != pWav->totalPCMFrameCount) { | ||
| 7945 | drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); | ||
| 7946 | drwav_uninit(pWav); | ||
| 7947 | return NULL; /* There was an error reading the samples. */ | ||
| 7948 | } | ||
| 7949 | |||
| 7950 | drwav_uninit(pWav); | ||
| 7951 | |||
| 7952 | if (sampleRate) { | ||
| 7953 | *sampleRate = pWav->sampleRate; | ||
| 7954 | } | ||
| 7955 | if (channels) { | ||
| 7956 | *channels = pWav->channels; | ||
| 7957 | } | ||
| 7958 | if (totalFrameCount) { | ||
| 7959 | *totalFrameCount = pWav->totalPCMFrameCount; | ||
| 7960 | } | ||
| 7961 | |||
| 7962 | return pSampleData; | ||
| 7963 | } | ||
| 7964 | |||
| 7965 | DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) | ||
| 7966 | { | ||
| 7967 | drwav_uint64 sampleDataSize; | ||
| 7968 | drwav_int32* pSampleData; | ||
| 7969 | drwav_uint64 framesRead; | ||
| 7970 | |||
| 7971 | DRWAV_ASSERT(pWav != NULL); | ||
| 7972 | |||
| 7973 | sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32); | ||
| 7974 | if (sampleDataSize > DRWAV_SIZE_MAX) { | ||
| 7975 | drwav_uninit(pWav); | ||
| 7976 | return NULL; /* File's too big. */ | ||
| 7977 | } | ||
| 7978 | |||
| 7979 | pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */ | ||
| 7980 | if (pSampleData == NULL) { | ||
| 7981 | drwav_uninit(pWav); | ||
| 7982 | return NULL; /* Failed to allocate memory. */ | ||
| 7983 | } | ||
| 7984 | |||
| 7985 | framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); | ||
| 7986 | if (framesRead != pWav->totalPCMFrameCount) { | ||
| 7987 | drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); | ||
| 7988 | drwav_uninit(pWav); | ||
| 7989 | return NULL; /* There was an error reading the samples. */ | ||
| 7990 | } | ||
| 7991 | |||
| 7992 | drwav_uninit(pWav); | ||
| 7993 | |||
| 7994 | if (sampleRate) { | ||
| 7995 | *sampleRate = pWav->sampleRate; | ||
| 7996 | } | ||
| 7997 | if (channels) { | ||
| 7998 | *channels = pWav->channels; | ||
| 7999 | } | ||
| 8000 | if (totalFrameCount) { | ||
| 8001 | *totalFrameCount = pWav->totalPCMFrameCount; | ||
| 8002 | } | ||
| 8003 | |||
| 8004 | return pSampleData; | ||
| 8005 | } | ||
| 8006 | |||
| 8007 | |||
| 8008 | |||
| 8009 | DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8010 | { | ||
| 8011 | drwav wav; | ||
| 8012 | |||
| 8013 | if (channelsOut) { | ||
| 8014 | *channelsOut = 0; | ||
| 8015 | } | ||
| 8016 | if (sampleRateOut) { | ||
| 8017 | *sampleRateOut = 0; | ||
| 8018 | } | ||
| 8019 | if (totalFrameCountOut) { | ||
| 8020 | *totalFrameCountOut = 0; | ||
| 8021 | } | ||
| 8022 | |||
| 8023 | if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { | ||
| 8024 | return NULL; | ||
| 8025 | } | ||
| 8026 | |||
| 8027 | return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); | ||
| 8028 | } | ||
| 8029 | |||
| 8030 | DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8031 | { | ||
| 8032 | drwav wav; | ||
| 8033 | |||
| 8034 | if (channelsOut) { | ||
| 8035 | *channelsOut = 0; | ||
| 8036 | } | ||
| 8037 | if (sampleRateOut) { | ||
| 8038 | *sampleRateOut = 0; | ||
| 8039 | } | ||
| 8040 | if (totalFrameCountOut) { | ||
| 8041 | *totalFrameCountOut = 0; | ||
| 8042 | } | ||
| 8043 | |||
| 8044 | if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { | ||
| 8045 | return NULL; | ||
| 8046 | } | ||
| 8047 | |||
| 8048 | return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); | ||
| 8049 | } | ||
| 8050 | |||
| 8051 | DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8052 | { | ||
| 8053 | drwav wav; | ||
| 8054 | |||
| 8055 | if (channelsOut) { | ||
| 8056 | *channelsOut = 0; | ||
| 8057 | } | ||
| 8058 | if (sampleRateOut) { | ||
| 8059 | *sampleRateOut = 0; | ||
| 8060 | } | ||
| 8061 | if (totalFrameCountOut) { | ||
| 8062 | *totalFrameCountOut = 0; | ||
| 8063 | } | ||
| 8064 | |||
| 8065 | if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { | ||
| 8066 | return NULL; | ||
| 8067 | } | ||
| 8068 | |||
| 8069 | return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); | ||
| 8070 | } | ||
| 8071 | |||
| 8072 | #ifndef DR_WAV_NO_STDIO | ||
| 8073 | DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8074 | { | ||
| 8075 | drwav wav; | ||
| 8076 | |||
| 8077 | if (channelsOut) { | ||
| 8078 | *channelsOut = 0; | ||
| 8079 | } | ||
| 8080 | if (sampleRateOut) { | ||
| 8081 | *sampleRateOut = 0; | ||
| 8082 | } | ||
| 8083 | if (totalFrameCountOut) { | ||
| 8084 | *totalFrameCountOut = 0; | ||
| 8085 | } | ||
| 8086 | |||
| 8087 | if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { | ||
| 8088 | return NULL; | ||
| 8089 | } | ||
| 8090 | |||
| 8091 | return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); | ||
| 8092 | } | ||
| 8093 | |||
| 8094 | DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8095 | { | ||
| 8096 | drwav wav; | ||
| 8097 | |||
| 8098 | if (channelsOut) { | ||
| 8099 | *channelsOut = 0; | ||
| 8100 | } | ||
| 8101 | if (sampleRateOut) { | ||
| 8102 | *sampleRateOut = 0; | ||
| 8103 | } | ||
| 8104 | if (totalFrameCountOut) { | ||
| 8105 | *totalFrameCountOut = 0; | ||
| 8106 | } | ||
| 8107 | |||
| 8108 | if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { | ||
| 8109 | return NULL; | ||
| 8110 | } | ||
| 8111 | |||
| 8112 | return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); | ||
| 8113 | } | ||
| 8114 | |||
| 8115 | DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8116 | { | ||
| 8117 | drwav wav; | ||
| 8118 | |||
| 8119 | if (channelsOut) { | ||
| 8120 | *channelsOut = 0; | ||
| 8121 | } | ||
| 8122 | if (sampleRateOut) { | ||
| 8123 | *sampleRateOut = 0; | ||
| 8124 | } | ||
| 8125 | if (totalFrameCountOut) { | ||
| 8126 | *totalFrameCountOut = 0; | ||
| 8127 | } | ||
| 8128 | |||
| 8129 | if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { | ||
| 8130 | return NULL; | ||
| 8131 | } | ||
| 8132 | |||
| 8133 | return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); | ||
| 8134 | } | ||
| 8135 | |||
| 8136 | |||
| 8137 | #ifndef DR_WAV_NO_WCHAR | ||
| 8138 | DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8139 | { | ||
| 8140 | drwav wav; | ||
| 8141 | |||
| 8142 | if (sampleRateOut) { | ||
| 8143 | *sampleRateOut = 0; | ||
| 8144 | } | ||
| 8145 | if (channelsOut) { | ||
| 8146 | *channelsOut = 0; | ||
| 8147 | } | ||
| 8148 | if (totalFrameCountOut) { | ||
| 8149 | *totalFrameCountOut = 0; | ||
| 8150 | } | ||
| 8151 | |||
| 8152 | if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { | ||
| 8153 | return NULL; | ||
| 8154 | } | ||
| 8155 | |||
| 8156 | return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); | ||
| 8157 | } | ||
| 8158 | |||
| 8159 | DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8160 | { | ||
| 8161 | drwav wav; | ||
| 8162 | |||
| 8163 | if (sampleRateOut) { | ||
| 8164 | *sampleRateOut = 0; | ||
| 8165 | } | ||
| 8166 | if (channelsOut) { | ||
| 8167 | *channelsOut = 0; | ||
| 8168 | } | ||
| 8169 | if (totalFrameCountOut) { | ||
| 8170 | *totalFrameCountOut = 0; | ||
| 8171 | } | ||
| 8172 | |||
| 8173 | if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { | ||
| 8174 | return NULL; | ||
| 8175 | } | ||
| 8176 | |||
| 8177 | return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); | ||
| 8178 | } | ||
| 8179 | |||
| 8180 | DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8181 | { | ||
| 8182 | drwav wav; | ||
| 8183 | |||
| 8184 | if (sampleRateOut) { | ||
| 8185 | *sampleRateOut = 0; | ||
| 8186 | } | ||
| 8187 | if (channelsOut) { | ||
| 8188 | *channelsOut = 0; | ||
| 8189 | } | ||
| 8190 | if (totalFrameCountOut) { | ||
| 8191 | *totalFrameCountOut = 0; | ||
| 8192 | } | ||
| 8193 | |||
| 8194 | if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { | ||
| 8195 | return NULL; | ||
| 8196 | } | ||
| 8197 | |||
| 8198 | return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); | ||
| 8199 | } | ||
| 8200 | #endif /* DR_WAV_NO_WCHAR */ | ||
| 8201 | #endif /* DR_WAV_NO_STDIO */ | ||
| 8202 | |||
| 8203 | DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8204 | { | ||
| 8205 | drwav wav; | ||
| 8206 | |||
| 8207 | if (channelsOut) { | ||
| 8208 | *channelsOut = 0; | ||
| 8209 | } | ||
| 8210 | if (sampleRateOut) { | ||
| 8211 | *sampleRateOut = 0; | ||
| 8212 | } | ||
| 8213 | if (totalFrameCountOut) { | ||
| 8214 | *totalFrameCountOut = 0; | ||
| 8215 | } | ||
| 8216 | |||
| 8217 | if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { | ||
| 8218 | return NULL; | ||
| 8219 | } | ||
| 8220 | |||
| 8221 | return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); | ||
| 8222 | } | ||
| 8223 | |||
| 8224 | DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8225 | { | ||
| 8226 | drwav wav; | ||
| 8227 | |||
| 8228 | if (channelsOut) { | ||
| 8229 | *channelsOut = 0; | ||
| 8230 | } | ||
| 8231 | if (sampleRateOut) { | ||
| 8232 | *sampleRateOut = 0; | ||
| 8233 | } | ||
| 8234 | if (totalFrameCountOut) { | ||
| 8235 | *totalFrameCountOut = 0; | ||
| 8236 | } | ||
| 8237 | |||
| 8238 | if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { | ||
| 8239 | return NULL; | ||
| 8240 | } | ||
| 8241 | |||
| 8242 | return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); | ||
| 8243 | } | ||
| 8244 | |||
| 8245 | DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8246 | { | ||
| 8247 | drwav wav; | ||
| 8248 | |||
| 8249 | if (channelsOut) { | ||
| 8250 | *channelsOut = 0; | ||
| 8251 | } | ||
| 8252 | if (sampleRateOut) { | ||
| 8253 | *sampleRateOut = 0; | ||
| 8254 | } | ||
| 8255 | if (totalFrameCountOut) { | ||
| 8256 | *totalFrameCountOut = 0; | ||
| 8257 | } | ||
| 8258 | |||
| 8259 | if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { | ||
| 8260 | return NULL; | ||
| 8261 | } | ||
| 8262 | |||
| 8263 | return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); | ||
| 8264 | } | ||
| 8265 | #endif /* DR_WAV_NO_CONVERSION_API */ | ||
| 8266 | |||
| 8267 | |||
| 8268 | DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) | ||
| 8269 | { | ||
| 8270 | if (pAllocationCallbacks != NULL) { | ||
| 8271 | drwav__free_from_callbacks(p, pAllocationCallbacks); | ||
| 8272 | } else { | ||
| 8273 | drwav__free_default(p, NULL); | ||
| 8274 | } | ||
| 8275 | } | ||
| 8276 | |||
| 8277 | DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data) | ||
| 8278 | { | ||
| 8279 | return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8); | ||
| 8280 | } | ||
| 8281 | |||
| 8282 | DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data) | ||
| 8283 | { | ||
| 8284 | return (drwav_int16)drwav_bytes_to_u16(data); | ||
| 8285 | } | ||
| 8286 | |||
| 8287 | DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data) | ||
| 8288 | { | ||
| 8289 | return drwav_bytes_to_u32_le(data); | ||
| 8290 | } | ||
| 8291 | |||
| 8292 | DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data) | ||
| 8293 | { | ||
| 8294 | union { | ||
| 8295 | drwav_uint32 u32; | ||
| 8296 | float f32; | ||
| 8297 | } value; | ||
| 8298 | |||
| 8299 | value.u32 = drwav_bytes_to_u32(data); | ||
| 8300 | return value.f32; | ||
| 8301 | } | ||
| 8302 | |||
| 8303 | DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data) | ||
| 8304 | { | ||
| 8305 | return (drwav_int32)drwav_bytes_to_u32(data); | ||
| 8306 | } | ||
| 8307 | |||
| 8308 | DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data) | ||
| 8309 | { | ||
| 8310 | return | ||
| 8311 | ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) | | ||
| 8312 | ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56); | ||
| 8313 | } | ||
| 8314 | |||
| 8315 | DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data) | ||
| 8316 | { | ||
| 8317 | return (drwav_int64)drwav_bytes_to_u64(data); | ||
| 8318 | } | ||
| 8319 | |||
| 8320 | |||
| 8321 | DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]) | ||
| 8322 | { | ||
| 8323 | int i; | ||
| 8324 | for (i = 0; i < 16; i += 1) { | ||
| 8325 | if (a[i] != b[i]) { | ||
| 8326 | return DRWAV_FALSE; | ||
| 8327 | } | ||
| 8328 | } | ||
| 8329 | |||
| 8330 | return DRWAV_TRUE; | ||
| 8331 | } | ||
| 8332 | |||
| 8333 | DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) | ||
| 8334 | { | ||
| 8335 | return | ||
| 8336 | a[0] == b[0] && | ||
| 8337 | a[1] == b[1] && | ||
| 8338 | a[2] == b[2] && | ||
| 8339 | a[3] == b[3]; | ||
| 8340 | } | ||
| 8341 | |||
| 8342 | #ifdef __MRC__ | ||
| 8343 | /* Undo the pragma at the beginning of this file. */ | ||
| 8344 | #pragma options opt reset | ||
| 8345 | #endif | ||
| 8346 | |||
| 8347 | #endif /* dr_wav_c */ | ||
| 8348 | #endif /* DR_WAV_IMPLEMENTATION */ | ||
| 8349 | |||
| 8350 | /* | ||
| 8351 | REVISION HISTORY | ||
| 8352 | ================ | ||
| 8353 | v0.13.16 - 2024-02-27 | ||
| 8354 | - Fix a Wdouble-promotion warning. | ||
| 8355 | |||
| 8356 | v0.13.15 - 2024-01-23 | ||
| 8357 | - Relax some unnecessary validation that prevented some files from loading. | ||
| 8358 | |||
| 8359 | v0.13.14 - 2023-12-02 | ||
| 8360 | - Fix a warning about an unused variable. | ||
| 8361 | |||
| 8362 | v0.13.13 - 2023-11-02 | ||
| 8363 | - Fix a warning when compiling with Clang. | ||
| 8364 | |||
| 8365 | v0.13.12 - 2023-08-07 | ||
| 8366 | - Fix a possible crash in drwav_read_pcm_frames(). | ||
| 8367 | |||
| 8368 | v0.13.11 - 2023-07-07 | ||
| 8369 | - AIFF compatibility improvements. | ||
| 8370 | |||
| 8371 | v0.13.10 - 2023-05-29 | ||
| 8372 | - Fix a bug where drwav_init_with_metadata() does not decode any frames after initializtion. | ||
| 8373 | |||
| 8374 | v0.13.9 - 2023-05-22 | ||
| 8375 | - Add support for AIFF decoding (writing and metadata not supported). | ||
| 8376 | - Add support for RIFX decoding (writing and metadata not supported). | ||
| 8377 | - Fix a bug where metadata is not processed if it's located before the "fmt " chunk. | ||
| 8378 | - Add a workaround for a type of malformed WAV file where the size of the "RIFF" and "data" chunks | ||
| 8379 | are incorrectly set to 0xFFFFFFFF. | ||
| 8380 | |||
| 8381 | v0.13.8 - 2023-03-25 | ||
| 8382 | - Fix a possible null pointer dereference. | ||
| 8383 | - Fix a crash when loading files with badly formed metadata. | ||
| 8384 | |||
| 8385 | v0.13.7 - 2022-09-17 | ||
| 8386 | - Fix compilation with DJGPP. | ||
| 8387 | - Add support for disabling wchar_t with DR_WAV_NO_WCHAR. | ||
| 8388 | |||
| 8389 | v0.13.6 - 2022-04-10 | ||
| 8390 | - Fix compilation error on older versions of GCC. | ||
| 8391 | - Remove some dependencies on the standard library. | ||
| 8392 | |||
| 8393 | v0.13.5 - 2022-01-26 | ||
| 8394 | - Fix an error when seeking to the end of the file. | ||
| 8395 | |||
| 8396 | v0.13.4 - 2021-12-08 | ||
| 8397 | - Fix some static analysis warnings. | ||
| 8398 | |||
| 8399 | v0.13.3 - 2021-11-24 | ||
| 8400 | - Fix an incorrect assertion when trying to endian swap 1-byte sample formats. This is now a no-op | ||
| 8401 | rather than a failed assertion. | ||
| 8402 | - Fix a bug with parsing of the bext chunk. | ||
| 8403 | - Fix some static analysis warnings. | ||
| 8404 | |||
| 8405 | v0.13.2 - 2021-10-02 | ||
| 8406 | - Fix a possible buffer overflow when reading from compressed formats. | ||
| 8407 | |||
| 8408 | v0.13.1 - 2021-07-31 | ||
| 8409 | - Fix platform detection for ARM64. | ||
| 8410 | |||
| 8411 | v0.13.0 - 2021-07-01 | ||
| 8412 | - Improve support for reading and writing metadata. Use the `_with_metadata()` APIs to initialize | ||
| 8413 | a WAV decoder and store the metadata within the `drwav` object. Use the `pMetadata` and | ||
| 8414 | `metadataCount` members of the `drwav` object to read the data. The old way of handling metadata | ||
| 8415 | via a callback is still usable and valid. | ||
| 8416 | - API CHANGE: drwav_target_write_size_bytes() now takes extra parameters for calculating the | ||
| 8417 | required write size when writing metadata. | ||
| 8418 | - Add drwav_get_cursor_in_pcm_frames() | ||
| 8419 | - Add drwav_get_length_in_pcm_frames() | ||
| 8420 | - Fix a bug where drwav_read_raw() can call the read callback with a byte count of zero. | ||
| 8421 | |||
| 8422 | v0.12.20 - 2021-06-11 | ||
| 8423 | - Fix some undefined behavior. | ||
| 8424 | |||
| 8425 | v0.12.19 - 2021-02-21 | ||
| 8426 | - Fix a warning due to referencing _MSC_VER when it is undefined. | ||
| 8427 | - Minor improvements to the management of some internal state concerning the data chunk cursor. | ||
| 8428 | |||
| 8429 | v0.12.18 - 2021-01-31 | ||
| 8430 | - Clean up some static analysis warnings. | ||
| 8431 | |||
| 8432 | v0.12.17 - 2021-01-17 | ||
| 8433 | - Minor fix to sample code in documentation. | ||
| 8434 | - Correctly qualify a private API as private rather than public. | ||
| 8435 | - Code cleanup. | ||
| 8436 | |||
| 8437 | v0.12.16 - 2020-12-02 | ||
| 8438 | - Fix a bug when trying to read more bytes than can fit in a size_t. | ||
| 8439 | |||
| 8440 | v0.12.15 - 2020-11-21 | ||
| 8441 | - Fix compilation with OpenWatcom. | ||
| 8442 | |||
| 8443 | v0.12.14 - 2020-11-13 | ||
| 8444 | - Minor code clean up. | ||
| 8445 | |||
| 8446 | v0.12.13 - 2020-11-01 | ||
| 8447 | - Improve compiler support for older versions of GCC. | ||
| 8448 | |||
| 8449 | v0.12.12 - 2020-09-28 | ||
| 8450 | - Add support for RF64. | ||
| 8451 | - Fix a bug in writing mode where the size of the RIFF chunk incorrectly includes the header section. | ||
| 8452 | |||
| 8453 | v0.12.11 - 2020-09-08 | ||
| 8454 | - Fix a compilation error on older compilers. | ||
| 8455 | |||
| 8456 | v0.12.10 - 2020-08-24 | ||
| 8457 | - Fix a bug when seeking with ADPCM formats. | ||
| 8458 | |||
| 8459 | v0.12.9 - 2020-08-02 | ||
| 8460 | - Simplify sized types. | ||
| 8461 | |||
| 8462 | v0.12.8 - 2020-07-25 | ||
| 8463 | - Fix a compilation warning. | ||
| 8464 | |||
| 8465 | v0.12.7 - 2020-07-15 | ||
| 8466 | - Fix some bugs on big-endian architectures. | ||
| 8467 | - Fix an error in s24 to f32 conversion. | ||
| 8468 | |||
| 8469 | v0.12.6 - 2020-06-23 | ||
| 8470 | - Change drwav_read_*() to allow NULL to be passed in as the output buffer which is equivalent to a forward seek. | ||
| 8471 | - Fix a buffer overflow when trying to decode invalid IMA-ADPCM files. | ||
| 8472 | - Add include guard for the implementation section. | ||
| 8473 | |||
| 8474 | v0.12.5 - 2020-05-27 | ||
| 8475 | - Minor documentation fix. | ||
| 8476 | |||
| 8477 | v0.12.4 - 2020-05-16 | ||
| 8478 | - Replace assert() with DRWAV_ASSERT(). | ||
| 8479 | - Add compile-time and run-time version querying. | ||
| 8480 | - DRWAV_VERSION_MINOR | ||
| 8481 | - DRWAV_VERSION_MAJOR | ||
| 8482 | - DRWAV_VERSION_REVISION | ||
| 8483 | - DRWAV_VERSION_STRING | ||
| 8484 | - drwav_version() | ||
| 8485 | - drwav_version_string() | ||
| 8486 | |||
| 8487 | v0.12.3 - 2020-04-30 | ||
| 8488 | - Fix compilation errors with VC6. | ||
| 8489 | |||
| 8490 | v0.12.2 - 2020-04-21 | ||
| 8491 | - Fix a bug where drwav_init_file() does not close the file handle after attempting to load an erroneous file. | ||
| 8492 | |||
| 8493 | v0.12.1 - 2020-04-13 | ||
| 8494 | - Fix some pedantic warnings. | ||
| 8495 | |||
| 8496 | v0.12.0 - 2020-04-04 | ||
| 8497 | - API CHANGE: Add container and format parameters to the chunk callback. | ||
| 8498 | - Minor documentation updates. | ||
| 8499 | |||
| 8500 | v0.11.5 - 2020-03-07 | ||
| 8501 | - Fix compilation error with Visual Studio .NET 2003. | ||
| 8502 | |||
| 8503 | v0.11.4 - 2020-01-29 | ||
| 8504 | - Fix some static analysis warnings. | ||
| 8505 | - Fix a bug when reading f32 samples from an A-law encoded stream. | ||
| 8506 | |||
| 8507 | v0.11.3 - 2020-01-12 | ||
| 8508 | - Minor changes to some f32 format conversion routines. | ||
| 8509 | - Minor bug fix for ADPCM conversion when end of file is reached. | ||
| 8510 | |||
| 8511 | v0.11.2 - 2019-12-02 | ||
| 8512 | - Fix a possible crash when using custom memory allocators without a custom realloc() implementation. | ||
| 8513 | - Fix an integer overflow bug. | ||
| 8514 | - Fix a null pointer dereference bug. | ||
| 8515 | - Add limits to sample rate, channels and bits per sample to tighten up some validation. | ||
| 8516 | |||
| 8517 | v0.11.1 - 2019-10-07 | ||
| 8518 | - Internal code clean up. | ||
| 8519 | |||
| 8520 | v0.11.0 - 2019-10-06 | ||
| 8521 | - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation | ||
| 8522 | routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs: | ||
| 8523 | - drwav_init() | ||
| 8524 | - drwav_init_ex() | ||
| 8525 | - drwav_init_file() | ||
| 8526 | - drwav_init_file_ex() | ||
| 8527 | - drwav_init_file_w() | ||
| 8528 | - drwav_init_file_w_ex() | ||
| 8529 | - drwav_init_memory() | ||
| 8530 | - drwav_init_memory_ex() | ||
| 8531 | - drwav_init_write() | ||
| 8532 | - drwav_init_write_sequential() | ||
| 8533 | - drwav_init_write_sequential_pcm_frames() | ||
| 8534 | - drwav_init_file_write() | ||
| 8535 | - drwav_init_file_write_sequential() | ||
| 8536 | - drwav_init_file_write_sequential_pcm_frames() | ||
| 8537 | - drwav_init_file_write_w() | ||
| 8538 | - drwav_init_file_write_sequential_w() | ||
| 8539 | - drwav_init_file_write_sequential_pcm_frames_w() | ||
| 8540 | - drwav_init_memory_write() | ||
| 8541 | - drwav_init_memory_write_sequential() | ||
| 8542 | - drwav_init_memory_write_sequential_pcm_frames() | ||
| 8543 | - drwav_open_and_read_pcm_frames_s16() | ||
| 8544 | - drwav_open_and_read_pcm_frames_f32() | ||
| 8545 | - drwav_open_and_read_pcm_frames_s32() | ||
| 8546 | - drwav_open_file_and_read_pcm_frames_s16() | ||
| 8547 | - drwav_open_file_and_read_pcm_frames_f32() | ||
| 8548 | - drwav_open_file_and_read_pcm_frames_s32() | ||
| 8549 | - drwav_open_file_and_read_pcm_frames_s16_w() | ||
| 8550 | - drwav_open_file_and_read_pcm_frames_f32_w() | ||
| 8551 | - drwav_open_file_and_read_pcm_frames_s32_w() | ||
| 8552 | - drwav_open_memory_and_read_pcm_frames_s16() | ||
| 8553 | - drwav_open_memory_and_read_pcm_frames_f32() | ||
| 8554 | - drwav_open_memory_and_read_pcm_frames_s32() | ||
| 8555 | Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use | ||
| 8556 | DRWAV_MALLOC, DRWAV_REALLOC and DRWAV_FREE. | ||
| 8557 | - Add support for reading and writing PCM frames in an explicit endianness. New APIs: | ||
| 8558 | - drwav_read_pcm_frames_le() | ||
| 8559 | - drwav_read_pcm_frames_be() | ||
| 8560 | - drwav_read_pcm_frames_s16le() | ||
| 8561 | - drwav_read_pcm_frames_s16be() | ||
| 8562 | - drwav_read_pcm_frames_f32le() | ||
| 8563 | - drwav_read_pcm_frames_f32be() | ||
| 8564 | - drwav_read_pcm_frames_s32le() | ||
| 8565 | - drwav_read_pcm_frames_s32be() | ||
| 8566 | - drwav_write_pcm_frames_le() | ||
| 8567 | - drwav_write_pcm_frames_be() | ||
| 8568 | - Remove deprecated APIs. | ||
| 8569 | - API CHANGE: The following APIs now return native-endian data. Previously they returned little-endian data. | ||
| 8570 | - drwav_read_pcm_frames() | ||
| 8571 | - drwav_read_pcm_frames_s16() | ||
| 8572 | - drwav_read_pcm_frames_s32() | ||
| 8573 | - drwav_read_pcm_frames_f32() | ||
| 8574 | - drwav_open_and_read_pcm_frames_s16() | ||
| 8575 | - drwav_open_and_read_pcm_frames_s32() | ||
| 8576 | - drwav_open_and_read_pcm_frames_f32() | ||
| 8577 | - drwav_open_file_and_read_pcm_frames_s16() | ||
| 8578 | - drwav_open_file_and_read_pcm_frames_s32() | ||
| 8579 | - drwav_open_file_and_read_pcm_frames_f32() | ||
| 8580 | - drwav_open_file_and_read_pcm_frames_s16_w() | ||
| 8581 | - drwav_open_file_and_read_pcm_frames_s32_w() | ||
| 8582 | - drwav_open_file_and_read_pcm_frames_f32_w() | ||
| 8583 | - drwav_open_memory_and_read_pcm_frames_s16() | ||
| 8584 | - drwav_open_memory_and_read_pcm_frames_s32() | ||
| 8585 | - drwav_open_memory_and_read_pcm_frames_f32() | ||
| 8586 | |||
| 8587 | v0.10.1 - 2019-08-31 | ||
| 8588 | - Correctly handle partial trailing ADPCM blocks. | ||
| 8589 | |||
| 8590 | v0.10.0 - 2019-08-04 | ||
| 8591 | - Remove deprecated APIs. | ||
| 8592 | - Add wchar_t variants for file loading APIs: | ||
| 8593 | drwav_init_file_w() | ||
| 8594 | drwav_init_file_ex_w() | ||
| 8595 | drwav_init_file_write_w() | ||
| 8596 | drwav_init_file_write_sequential_w() | ||
| 8597 | - Add drwav_target_write_size_bytes() which calculates the total size in bytes of a WAV file given a format and sample count. | ||
| 8598 | - Add APIs for specifying the PCM frame count instead of the sample count when opening in sequential write mode: | ||
| 8599 | drwav_init_write_sequential_pcm_frames() | ||
| 8600 | drwav_init_file_write_sequential_pcm_frames() | ||
| 8601 | drwav_init_file_write_sequential_pcm_frames_w() | ||
| 8602 | drwav_init_memory_write_sequential_pcm_frames() | ||
| 8603 | - Deprecate drwav_open*() and drwav_close(): | ||
| 8604 | drwav_open() | ||
| 8605 | drwav_open_ex() | ||
| 8606 | drwav_open_write() | ||
| 8607 | drwav_open_write_sequential() | ||
| 8608 | drwav_open_file() | ||
| 8609 | drwav_open_file_ex() | ||
| 8610 | drwav_open_file_write() | ||
| 8611 | drwav_open_file_write_sequential() | ||
| 8612 | drwav_open_memory() | ||
| 8613 | drwav_open_memory_ex() | ||
| 8614 | drwav_open_memory_write() | ||
| 8615 | drwav_open_memory_write_sequential() | ||
| 8616 | drwav_close() | ||
| 8617 | - Minor documentation updates. | ||
| 8618 | |||
| 8619 | v0.9.2 - 2019-05-21 | ||
| 8620 | - Fix warnings. | ||
| 8621 | |||
| 8622 | v0.9.1 - 2019-05-05 | ||
| 8623 | - Add support for C89. | ||
| 8624 | - Change license to choice of public domain or MIT-0. | ||
| 8625 | |||
| 8626 | v0.9.0 - 2018-12-16 | ||
| 8627 | - API CHANGE: Add new reading APIs for reading by PCM frames instead of samples. Old APIs have been deprecated and | ||
| 8628 | will be removed in v0.10.0. Deprecated APIs and their replacements: | ||
| 8629 | drwav_read() -> drwav_read_pcm_frames() | ||
| 8630 | drwav_read_s16() -> drwav_read_pcm_frames_s16() | ||
| 8631 | drwav_read_f32() -> drwav_read_pcm_frames_f32() | ||
| 8632 | drwav_read_s32() -> drwav_read_pcm_frames_s32() | ||
| 8633 | drwav_seek_to_sample() -> drwav_seek_to_pcm_frame() | ||
| 8634 | drwav_write() -> drwav_write_pcm_frames() | ||
| 8635 | drwav_open_and_read_s16() -> drwav_open_and_read_pcm_frames_s16() | ||
| 8636 | drwav_open_and_read_f32() -> drwav_open_and_read_pcm_frames_f32() | ||
| 8637 | drwav_open_and_read_s32() -> drwav_open_and_read_pcm_frames_s32() | ||
| 8638 | drwav_open_file_and_read_s16() -> drwav_open_file_and_read_pcm_frames_s16() | ||
| 8639 | drwav_open_file_and_read_f32() -> drwav_open_file_and_read_pcm_frames_f32() | ||
| 8640 | drwav_open_file_and_read_s32() -> drwav_open_file_and_read_pcm_frames_s32() | ||
| 8641 | drwav_open_memory_and_read_s16() -> drwav_open_memory_and_read_pcm_frames_s16() | ||
| 8642 | drwav_open_memory_and_read_f32() -> drwav_open_memory_and_read_pcm_frames_f32() | ||
| 8643 | drwav_open_memory_and_read_s32() -> drwav_open_memory_and_read_pcm_frames_s32() | ||
| 8644 | drwav::totalSampleCount -> drwav::totalPCMFrameCount | ||
| 8645 | - API CHANGE: Rename drwav_open_and_read_file_*() to drwav_open_file_and_read_*(). | ||
| 8646 | - API CHANGE: Rename drwav_open_and_read_memory_*() to drwav_open_memory_and_read_*(). | ||
| 8647 | - Add built-in support for smpl chunks. | ||
| 8648 | - Add support for firing a callback for each chunk in the file at initialization time. | ||
| 8649 | - This is enabled through the drwav_init_ex(), etc. family of APIs. | ||
| 8650 | - Handle invalid FMT chunks more robustly. | ||
| 8651 | |||
| 8652 | v0.8.5 - 2018-09-11 | ||
| 8653 | - Const correctness. | ||
| 8654 | - Fix a potential stack overflow. | ||
| 8655 | |||
| 8656 | v0.8.4 - 2018-08-07 | ||
| 8657 | - Improve 64-bit detection. | ||
| 8658 | |||
| 8659 | v0.8.3 - 2018-08-05 | ||
| 8660 | - Fix C++ build on older versions of GCC. | ||
| 8661 | |||
| 8662 | v0.8.2 - 2018-08-02 | ||
| 8663 | - Fix some big-endian bugs. | ||
| 8664 | |||
| 8665 | v0.8.1 - 2018-06-29 | ||
| 8666 | - Add support for sequential writing APIs. | ||
| 8667 | - Disable seeking in write mode. | ||
| 8668 | - Fix bugs with Wave64. | ||
| 8669 | - Fix typos. | ||
| 8670 | |||
| 8671 | v0.8 - 2018-04-27 | ||
| 8672 | - Bug fix. | ||
| 8673 | - Start using major.minor.revision versioning. | ||
| 8674 | |||
| 8675 | v0.7f - 2018-02-05 | ||
| 8676 | - Restrict ADPCM formats to a maximum of 2 channels. | ||
| 8677 | |||
| 8678 | v0.7e - 2018-02-02 | ||
| 8679 | - Fix a crash. | ||
| 8680 | |||
| 8681 | v0.7d - 2018-02-01 | ||
| 8682 | - Fix a crash. | ||
| 8683 | |||
| 8684 | v0.7c - 2018-02-01 | ||
| 8685 | - Set drwav.bytesPerSample to 0 for all compressed formats. | ||
| 8686 | - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for | ||
| 8687 | all format conversion reading APIs (*_s16, *_s32, *_f32 APIs). | ||
| 8688 | - Fix some divide-by-zero errors. | ||
| 8689 | |||
| 8690 | v0.7b - 2018-01-22 | ||
| 8691 | - Fix errors with seeking of compressed formats. | ||
| 8692 | - Fix compilation error when DR_WAV_NO_CONVERSION_API | ||
| 8693 | |||
| 8694 | v0.7a - 2017-11-17 | ||
| 8695 | - Fix some GCC warnings. | ||
| 8696 | |||
| 8697 | v0.7 - 2017-11-04 | ||
| 8698 | - Add writing APIs. | ||
| 8699 | |||
| 8700 | v0.6 - 2017-08-16 | ||
| 8701 | - API CHANGE: Rename dr_* types to drwav_*. | ||
| 8702 | - Add support for custom implementations of malloc(), realloc(), etc. | ||
| 8703 | - Add support for Microsoft ADPCM. | ||
| 8704 | - Add support for IMA ADPCM (DVI, format code 0x11). | ||
| 8705 | - Optimizations to drwav_read_s16(). | ||
| 8706 | - Bug fixes. | ||
| 8707 | |||
| 8708 | v0.5g - 2017-07-16 | ||
| 8709 | - Change underlying type for booleans to unsigned. | ||
| 8710 | |||
| 8711 | v0.5f - 2017-04-04 | ||
| 8712 | - Fix a minor bug with drwav_open_and_read_s16() and family. | ||
| 8713 | |||
| 8714 | v0.5e - 2016-12-29 | ||
| 8715 | - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this. | ||
| 8716 | - Minor fixes to documentation. | ||
| 8717 | |||
| 8718 | v0.5d - 2016-12-28 | ||
| 8719 | - Use drwav_int* and drwav_uint* sized types to improve compiler support. | ||
| 8720 | |||
| 8721 | v0.5c - 2016-11-11 | ||
| 8722 | - Properly handle JUNK chunks that come before the FMT chunk. | ||
| 8723 | |||
| 8724 | v0.5b - 2016-10-23 | ||
| 8725 | - A minor change to drwav_bool8 and drwav_bool32 types. | ||
| 8726 | |||
| 8727 | v0.5a - 2016-10-11 | ||
| 8728 | - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering. | ||
| 8729 | - Improve A-law and mu-law efficiency. | ||
| 8730 | |||
| 8731 | v0.5 - 2016-09-29 | ||
| 8732 | - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to | ||
| 8733 | keep it consistent with dr_audio and dr_flac. | ||
| 8734 | |||
| 8735 | v0.4b - 2016-09-18 | ||
| 8736 | - Fixed a typo in documentation. | ||
| 8737 | |||
| 8738 | v0.4a - 2016-09-18 | ||
| 8739 | - Fixed a typo. | ||
| 8740 | - Change date format to ISO 8601 (YYYY-MM-DD) | ||
| 8741 | |||
| 8742 | v0.4 - 2016-07-13 | ||
| 8743 | - API CHANGE. Make onSeek consistent with dr_flac. | ||
| 8744 | - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac. | ||
| 8745 | - Added support for Sony Wave64. | ||
| 8746 | |||
| 8747 | v0.3a - 2016-05-28 | ||
| 8748 | - API CHANGE. Return drwav_bool32 instead of int in onSeek callback. | ||
| 8749 | - Fixed a memory leak. | ||
| 8750 | |||
| 8751 | v0.3 - 2016-05-22 | ||
| 8752 | - Lots of API changes for consistency. | ||
| 8753 | |||
| 8754 | v0.2a - 2016-05-16 | ||
| 8755 | - Fixed Linux/GCC build. | ||
| 8756 | |||
| 8757 | v0.2 - 2016-05-11 | ||
| 8758 | - Added support for reading data as signed 32-bit PCM for consistency with dr_flac. | ||
| 8759 | |||
| 8760 | v0.1a - 2016-05-07 | ||
| 8761 | - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize. | ||
| 8762 | |||
| 8763 | v0.1 - 2016-05-04 | ||
| 8764 | - Initial versioned release. | ||
| 8765 | */ | ||
| 8766 | |||
| 8767 | /* | ||
| 8768 | This software is available as a choice of the following licenses. Choose | ||
| 8769 | whichever you prefer. | ||
| 8770 | |||
| 8771 | =============================================================================== | ||
| 8772 | ALTERNATIVE 1 - Public Domain (www.unlicense.org) | ||
| 8773 | =============================================================================== | ||
| 8774 | This is free and unencumbered software released into the public domain. | ||
| 8775 | |||
| 8776 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this | ||
| 8777 | software, either in source code form or as a compiled binary, for any purpose, | ||
| 8778 | commercial or non-commercial, and by any means. | ||
| 8779 | |||
| 8780 | In jurisdictions that recognize copyright laws, the author or authors of this | ||
| 8781 | software dedicate any and all copyright interest in the software to the public | ||
| 8782 | domain. We make this dedication for the benefit of the public at large and to | ||
| 8783 | the detriment of our heirs and successors. We intend this dedication to be an | ||
| 8784 | overt act of relinquishment in perpetuity of all present and future rights to | ||
| 8785 | this software under copyright law. | ||
| 8786 | |||
| 8787 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 8788 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 8789 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 8790 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 8791 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
| 8792 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 8793 | |||
| 8794 | For more information, please refer to <http://unlicense.org/> | ||
| 8795 | |||
| 8796 | =============================================================================== | ||
| 8797 | ALTERNATIVE 2 - MIT No Attribution | ||
| 8798 | =============================================================================== | ||
| 8799 | Copyright 2023 David Reid | ||
| 8800 | |||
| 8801 | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| 8802 | this software and associated documentation files (the "Software"), to deal in | ||
| 8803 | the Software without restriction, including without limitation the rights to | ||
| 8804 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
| 8805 | of the Software, and to permit persons to whom the Software is furnished to do | ||
| 8806 | so. | ||
| 8807 | |||
| 8808 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 8809 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 8810 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 8811 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 8812 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 8813 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 8814 | SOFTWARE. | ||
| 8815 | */ | ||
diff --git a/raylib/src/external/glad.h b/raylib/src/external/glad.h new file mode 100644 index 0000000..5bb79d4 --- /dev/null +++ b/raylib/src/external/glad.h | |||
| @@ -0,0 +1,8682 @@ | |||
| 1 | /** | ||
| 2 | * Loader generated by glad 2.0.0-beta on Sun Sep 18 18:12:12 2022 | ||
| 3 | * | ||
| 4 | * Generator: C/C++ | ||
| 5 | * Specification: gl | ||
| 6 | * Extensions: 116 | ||
| 7 | * | ||
| 8 | * APIs: | ||
| 9 | * - gl:core=4.3 | ||
| 10 | * | ||
| 11 | * Options: | ||
| 12 | * - ALIAS = False | ||
| 13 | * - DEBUG = False | ||
| 14 | * - HEADER_ONLY = True | ||
| 15 | * - LOADER = False | ||
| 16 | * - MX = False | ||
| 17 | * - MX_GLOBAL = False | ||
| 18 | * - ON_DEMAND = False | ||
| 19 | * | ||
| 20 | * Commandline: | ||
| 21 | * --api='gl:core=4.3' --extensions='GL_ARB_ES2_compatibility,GL_ARB_ES3_1_compatibility,GL_ARB_ES3_2_compatibility,GL_ARB_ES3_compatibility,GL_ARB_blend_func_extended,GL_ARB_buffer_storage,GL_ARB_clear_buffer_object,GL_ARB_clear_texture,GL_ARB_color_buffer_float,GL_ARB_compatibility,GL_ARB_compressed_texture_pixel_storage,GL_ARB_compute_shader,GL_ARB_compute_variable_group_size,GL_ARB_copy_buffer,GL_ARB_copy_image,GL_ARB_debug_output,GL_ARB_depth_buffer_float,GL_ARB_depth_clamp,GL_ARB_depth_texture,GL_ARB_direct_state_access,GL_ARB_draw_buffers,GL_ARB_draw_buffers_blend,GL_ARB_draw_elements_base_vertex,GL_ARB_draw_indirect,GL_ARB_draw_instanced,GL_ARB_enhanced_layouts,GL_ARB_explicit_attrib_location,GL_ARB_explicit_uniform_location,GL_ARB_fragment_coord_conventions,GL_ARB_fragment_layer_viewport,GL_ARB_fragment_program,GL_ARB_fragment_program_shadow,GL_ARB_fragment_shader,GL_ARB_fragment_shader_interlock,GL_ARB_framebuffer_no_attachments,GL_ARB_framebuffer_object,GL_ARB_framebuffer_sRGB,GL_ARB_geometry_shader4,GL_ARB_get_program_binary,GL_ARB_get_texture_sub_image,GL_ARB_gl_spirv,GL_ARB_gpu_shader5,GL_ARB_gpu_shader_fp64,GL_ARB_gpu_shader_int64,GL_ARB_half_float_pixel,GL_ARB_half_float_vertex,GL_ARB_instanced_arrays,GL_ARB_internalformat_query,GL_ARB_internalformat_query2,GL_ARB_map_buffer_range,GL_ARB_multi_bind,GL_ARB_multi_draw_indirect,GL_ARB_multisample,GL_ARB_multitexture,GL_ARB_occlusion_query,GL_ARB_occlusion_query2,GL_ARB_pipeline_statistics_query,GL_ARB_query_buffer_object,GL_ARB_sample_locations,GL_ARB_sample_shading,GL_ARB_seamless_cube_map,GL_ARB_seamless_cubemap_per_texture,GL_ARB_shader_atomic_counter_ops,GL_ARB_shader_atomic_counters,GL_ARB_shader_bit_encoding,GL_ARB_shader_clock,GL_ARB_shader_image_load_store,GL_ARB_shader_image_size,GL_ARB_shader_objects,GL_ARB_shader_storage_buffer_object,GL_ARB_shader_texture_lod,GL_ARB_shading_language_100,GL_ARB_shading_language_420pack,GL_ARB_shading_language_include,GL_ARB_shading_language_packing,GL_ARB_spirv_extensions,GL_ARB_tessellation_shader,GL_ARB_texture_border_clamp,GL_ARB_texture_buffer_object_rgb32,GL_ARB_texture_compression,GL_ARB_texture_cube_map,GL_ARB_texture_cube_map_array,GL_ARB_texture_env_add,GL_ARB_texture_filter_anisotropic,GL_ARB_texture_filter_minmax,GL_ARB_texture_float,GL_ARB_texture_mirror_clamp_to_edge,GL_ARB_texture_mirrored_repeat,GL_ARB_texture_multisample,GL_ARB_texture_non_power_of_two,GL_ARB_texture_rg,GL_ARB_texture_storage,GL_ARB_texture_swizzle,GL_ARB_texture_view,GL_ARB_timer_query,GL_ARB_transpose_matrix,GL_ARB_uniform_buffer_object,GL_ARB_vertex_array_bgra,GL_ARB_vertex_array_object,GL_ARB_vertex_attrib_binding,GL_ARB_vertex_buffer_object,GL_ARB_vertex_program,GL_ARB_vertex_shader,GL_EXT_draw_instanced,GL_EXT_fog_coord,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_EXT_framebuffer_sRGB,GL_EXT_texture_compression_s3tc,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_mirror_clamp,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_OES_compressed_paletted_texture,GL_OES_fixed_point' c --header-only | ||
| 22 | * | ||
| 23 | * Online: | ||
| 24 | * http://glad.sh/#api=gl%3Acore%3D4.3&generator=c&options=HEADER_ONLY | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | |||
| 28 | #ifndef GLAD_GL_H_ | ||
| 29 | #define GLAD_GL_H_ | ||
| 30 | |||
| 31 | #ifdef __clang__ | ||
| 32 | #pragma clang diagnostic push | ||
| 33 | #pragma clang diagnostic ignored "-Wreserved-id-macro" | ||
| 34 | #endif | ||
| 35 | #ifdef __gl_h_ | ||
| 36 | #error OpenGL (gl.h) header already included (API: gl), remove previous include! | ||
| 37 | #endif | ||
| 38 | #define __gl_h_ 1 | ||
| 39 | #ifdef __gl3_h_ | ||
| 40 | #error OpenGL (gl3.h) header already included (API: gl), remove previous include! | ||
| 41 | #endif | ||
| 42 | #define __gl3_h_ 1 | ||
| 43 | #ifdef __glext_h_ | ||
| 44 | #error OpenGL (glext.h) header already included (API: gl), remove previous include! | ||
| 45 | #endif | ||
| 46 | #define __glext_h_ 1 | ||
| 47 | #ifdef __gl3ext_h_ | ||
| 48 | #error OpenGL (gl3ext.h) header already included (API: gl), remove previous include! | ||
| 49 | #endif | ||
| 50 | #define __gl3ext_h_ 1 | ||
| 51 | #ifdef __clang__ | ||
| 52 | #pragma clang diagnostic pop | ||
| 53 | #endif | ||
| 54 | |||
| 55 | #define GLAD_GL | ||
| 56 | #define GLAD_OPTION_GL_HEADER_ONLY | ||
| 57 | |||
| 58 | #ifdef __cplusplus | ||
| 59 | extern "C" { | ||
| 60 | #endif | ||
| 61 | |||
| 62 | #ifndef GLAD_PLATFORM_H_ | ||
| 63 | #define GLAD_PLATFORM_H_ | ||
| 64 | |||
| 65 | #ifndef GLAD_PLATFORM_WIN32 | ||
| 66 | #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__) | ||
| 67 | #define GLAD_PLATFORM_WIN32 1 | ||
| 68 | #else | ||
| 69 | #define GLAD_PLATFORM_WIN32 0 | ||
| 70 | #endif | ||
| 71 | #endif | ||
| 72 | |||
| 73 | #ifndef GLAD_PLATFORM_APPLE | ||
| 74 | #ifdef __APPLE__ | ||
| 75 | #define GLAD_PLATFORM_APPLE 1 | ||
| 76 | #else | ||
| 77 | #define GLAD_PLATFORM_APPLE 0 | ||
| 78 | #endif | ||
| 79 | #endif | ||
| 80 | |||
| 81 | #ifndef GLAD_PLATFORM_EMSCRIPTEN | ||
| 82 | #ifdef __EMSCRIPTEN__ | ||
| 83 | #define GLAD_PLATFORM_EMSCRIPTEN 1 | ||
| 84 | #else | ||
| 85 | #define GLAD_PLATFORM_EMSCRIPTEN 0 | ||
| 86 | #endif | ||
| 87 | #endif | ||
| 88 | |||
| 89 | #ifndef GLAD_PLATFORM_UWP | ||
| 90 | #if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY) | ||
| 91 | #ifdef __has_include | ||
| 92 | #if __has_include(<winapifamily.h>) | ||
| 93 | #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 | ||
| 94 | #endif | ||
| 95 | #elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ | ||
| 96 | #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 | ||
| 97 | #endif | ||
| 98 | #endif | ||
| 99 | |||
| 100 | #ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY | ||
| 101 | #include <winapifamily.h> | ||
| 102 | #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) | ||
| 103 | #define GLAD_PLATFORM_UWP 1 | ||
| 104 | #endif | ||
| 105 | #endif | ||
| 106 | |||
| 107 | #ifndef GLAD_PLATFORM_UWP | ||
| 108 | #define GLAD_PLATFORM_UWP 0 | ||
| 109 | #endif | ||
| 110 | #endif | ||
| 111 | |||
| 112 | #ifdef __GNUC__ | ||
| 113 | #define GLAD_GNUC_EXTENSION __extension__ | ||
| 114 | #else | ||
| 115 | #define GLAD_GNUC_EXTENSION | ||
| 116 | #endif | ||
| 117 | |||
| 118 | #ifndef GLAD_API_CALL | ||
| 119 | #if defined(GLAD_API_CALL_EXPORT) | ||
| 120 | #if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__) | ||
| 121 | #if defined(GLAD_API_CALL_EXPORT_BUILD) | ||
| 122 | #if defined(__GNUC__) | ||
| 123 | #define GLAD_API_CALL __attribute__ ((dllexport)) extern | ||
| 124 | #else | ||
| 125 | #define GLAD_API_CALL __declspec(dllexport) extern | ||
| 126 | #endif | ||
| 127 | #else | ||
| 128 | #if defined(__GNUC__) | ||
| 129 | #define GLAD_API_CALL __attribute__ ((dllimport)) extern | ||
| 130 | #else | ||
| 131 | #define GLAD_API_CALL __declspec(dllimport) extern | ||
| 132 | #endif | ||
| 133 | #endif | ||
| 134 | #elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD) | ||
| 135 | #define GLAD_API_CALL __attribute__ ((visibility ("default"))) extern | ||
| 136 | #else | ||
| 137 | #define GLAD_API_CALL extern | ||
| 138 | #endif | ||
| 139 | #else | ||
| 140 | #define GLAD_API_CALL extern | ||
| 141 | #endif | ||
| 142 | #endif | ||
| 143 | |||
| 144 | #ifdef APIENTRY | ||
| 145 | #define GLAD_API_PTR APIENTRY | ||
| 146 | #elif GLAD_PLATFORM_WIN32 | ||
| 147 | #define GLAD_API_PTR __stdcall | ||
| 148 | #else | ||
| 149 | #define GLAD_API_PTR | ||
| 150 | #endif | ||
| 151 | |||
| 152 | #ifndef GLAPI | ||
| 153 | #define GLAPI GLAD_API_CALL | ||
| 154 | #endif | ||
| 155 | |||
| 156 | #ifndef GLAPIENTRY | ||
| 157 | #define GLAPIENTRY GLAD_API_PTR | ||
| 158 | #endif | ||
| 159 | |||
| 160 | #define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor) | ||
| 161 | #define GLAD_VERSION_MAJOR(version) (version / 10000) | ||
| 162 | #define GLAD_VERSION_MINOR(version) (version % 10000) | ||
| 163 | |||
| 164 | #define GLAD_GENERATOR_VERSION "2.0.0-beta" | ||
| 165 | |||
| 166 | typedef void (*GLADapiproc)(void); | ||
| 167 | |||
| 168 | typedef GLADapiproc (*GLADloadfunc)(const char *name); | ||
| 169 | typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name); | ||
| 170 | |||
| 171 | typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...); | ||
| 172 | typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...); | ||
| 173 | |||
| 174 | #endif /* GLAD_PLATFORM_H_ */ | ||
| 175 | |||
| 176 | #define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 | ||
| 177 | #define GL_ACTIVE_ATTRIBUTES 0x8B89 | ||
| 178 | #define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A | ||
| 179 | #define GL_ACTIVE_PROGRAM 0x8259 | ||
| 180 | #define GL_ACTIVE_RESOURCES 0x92F5 | ||
| 181 | #define GL_ACTIVE_SUBROUTINES 0x8DE5 | ||
| 182 | #define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 | ||
| 183 | #define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 | ||
| 184 | #define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 | ||
| 185 | #define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 | ||
| 186 | #define GL_ACTIVE_TEXTURE 0x84E0 | ||
| 187 | #define GL_ACTIVE_TEXTURE_ARB 0x84E0 | ||
| 188 | #define GL_ACTIVE_UNIFORMS 0x8B86 | ||
| 189 | #define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 | ||
| 190 | #define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 | ||
| 191 | #define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 | ||
| 192 | #define GL_ACTIVE_VARIABLES 0x9305 | ||
| 193 | #define GL_ALIASED_LINE_WIDTH_RANGE 0x846E | ||
| 194 | #define GL_ALL_BARRIER_BITS 0xFFFFFFFF | ||
| 195 | #define GL_ALL_SHADER_BITS 0xFFFFFFFF | ||
| 196 | #define GL_ALPHA 0x1906 | ||
| 197 | #define GL_ALPHA16F_ARB 0x881C | ||
| 198 | #define GL_ALPHA32F_ARB 0x8816 | ||
| 199 | #define GL_ALREADY_SIGNALED 0x911A | ||
| 200 | #define GL_ALWAYS 0x0207 | ||
| 201 | #define GL_AND 0x1501 | ||
| 202 | #define GL_AND_INVERTED 0x1504 | ||
| 203 | #define GL_AND_REVERSE 0x1502 | ||
| 204 | #define GL_ANY_SAMPLES_PASSED 0x8C2F | ||
| 205 | #define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A | ||
| 206 | #define GL_ARRAY_BUFFER 0x8892 | ||
| 207 | #define GL_ARRAY_BUFFER_ARB 0x8892 | ||
| 208 | #define GL_ARRAY_BUFFER_BINDING 0x8894 | ||
| 209 | #define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 | ||
| 210 | #define GL_ARRAY_SIZE 0x92FB | ||
| 211 | #define GL_ARRAY_STRIDE 0x92FE | ||
| 212 | #define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 | ||
| 213 | #define GL_ATOMIC_COUNTER_BUFFER 0x92C0 | ||
| 214 | #define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 | ||
| 215 | #define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 | ||
| 216 | #define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 | ||
| 217 | #define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 | ||
| 218 | #define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 | ||
| 219 | #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED | ||
| 220 | #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB | ||
| 221 | #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA | ||
| 222 | #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 | ||
| 223 | #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 | ||
| 224 | #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 | ||
| 225 | #define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 | ||
| 226 | #define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 | ||
| 227 | #define GL_ATTACHED_SHADERS 0x8B85 | ||
| 228 | #define GL_AUTO_GENERATE_MIPMAP 0x8295 | ||
| 229 | #define GL_BACK 0x0405 | ||
| 230 | #define GL_BACK_LEFT 0x0402 | ||
| 231 | #define GL_BACK_RIGHT 0x0403 | ||
| 232 | #define GL_BGR 0x80E0 | ||
| 233 | #define GL_BGRA 0x80E1 | ||
| 234 | #define GL_BGRA_INTEGER 0x8D9B | ||
| 235 | #define GL_BGR_INTEGER 0x8D9A | ||
| 236 | #define GL_BLEND 0x0BE2 | ||
| 237 | #define GL_BLEND_COLOR 0x8005 | ||
| 238 | #define GL_BLEND_DST 0x0BE0 | ||
| 239 | #define GL_BLEND_DST_ALPHA 0x80CA | ||
| 240 | #define GL_BLEND_DST_RGB 0x80C8 | ||
| 241 | #define GL_BLEND_EQUATION 0x8009 | ||
| 242 | #define GL_BLEND_EQUATION_ALPHA 0x883D | ||
| 243 | #define GL_BLEND_EQUATION_RGB 0x8009 | ||
| 244 | #define GL_BLEND_SRC 0x0BE1 | ||
| 245 | #define GL_BLEND_SRC_ALPHA 0x80CB | ||
| 246 | #define GL_BLEND_SRC_RGB 0x80C9 | ||
| 247 | #define GL_BLOCK_INDEX 0x92FD | ||
| 248 | #define GL_BLUE 0x1905 | ||
| 249 | #define GL_BLUE_INTEGER 0x8D96 | ||
| 250 | #define GL_BOOL 0x8B56 | ||
| 251 | #define GL_BOOL_ARB 0x8B56 | ||
| 252 | #define GL_BOOL_VEC2 0x8B57 | ||
| 253 | #define GL_BOOL_VEC2_ARB 0x8B57 | ||
| 254 | #define GL_BOOL_VEC3 0x8B58 | ||
| 255 | #define GL_BOOL_VEC3_ARB 0x8B58 | ||
| 256 | #define GL_BOOL_VEC4 0x8B59 | ||
| 257 | #define GL_BOOL_VEC4_ARB 0x8B59 | ||
| 258 | #define GL_BUFFER 0x82E0 | ||
| 259 | #define GL_BUFFER_ACCESS 0x88BB | ||
| 260 | #define GL_BUFFER_ACCESS_ARB 0x88BB | ||
| 261 | #define GL_BUFFER_ACCESS_FLAGS 0x911F | ||
| 262 | #define GL_BUFFER_BINDING 0x9302 | ||
| 263 | #define GL_BUFFER_DATA_SIZE 0x9303 | ||
| 264 | #define GL_BUFFER_IMMUTABLE_STORAGE 0x821F | ||
| 265 | #define GL_BUFFER_MAPPED 0x88BC | ||
| 266 | #define GL_BUFFER_MAPPED_ARB 0x88BC | ||
| 267 | #define GL_BUFFER_MAP_LENGTH 0x9120 | ||
| 268 | #define GL_BUFFER_MAP_OFFSET 0x9121 | ||
| 269 | #define GL_BUFFER_MAP_POINTER 0x88BD | ||
| 270 | #define GL_BUFFER_MAP_POINTER_ARB 0x88BD | ||
| 271 | #define GL_BUFFER_SIZE 0x8764 | ||
| 272 | #define GL_BUFFER_SIZE_ARB 0x8764 | ||
| 273 | #define GL_BUFFER_STORAGE_FLAGS 0x8220 | ||
| 274 | #define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 | ||
| 275 | #define GL_BUFFER_USAGE 0x8765 | ||
| 276 | #define GL_BUFFER_USAGE_ARB 0x8765 | ||
| 277 | #define GL_BUFFER_VARIABLE 0x92E5 | ||
| 278 | #define GL_BYTE 0x1400 | ||
| 279 | #define GL_CAVEAT_SUPPORT 0x82B8 | ||
| 280 | #define GL_CCW 0x0901 | ||
| 281 | #define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B | ||
| 282 | #define GL_CLAMP_READ_COLOR 0x891C | ||
| 283 | #define GL_CLAMP_READ_COLOR_ARB 0x891C | ||
| 284 | #define GL_CLAMP_TO_BORDER 0x812D | ||
| 285 | #define GL_CLAMP_TO_BORDER_ARB 0x812D | ||
| 286 | #define GL_CLAMP_TO_EDGE 0x812F | ||
| 287 | #define GL_CLAMP_VERTEX_COLOR_ARB 0x891A | ||
| 288 | #define GL_CLEAR 0x1500 | ||
| 289 | #define GL_CLEAR_BUFFER 0x82B4 | ||
| 290 | #define GL_CLEAR_TEXTURE 0x9365 | ||
| 291 | #define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 | ||
| 292 | #define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 | ||
| 293 | #define GL_CLIENT_STORAGE_BIT 0x0200 | ||
| 294 | #define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6 | ||
| 295 | #define GL_CLIPPING_INPUT_PRIMITIVES_ARB 0x82F6 | ||
| 296 | #define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7 | ||
| 297 | #define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB 0x82F7 | ||
| 298 | #define GL_CLIP_DISTANCE0 0x3000 | ||
| 299 | #define GL_CLIP_DISTANCE1 0x3001 | ||
| 300 | #define GL_CLIP_DISTANCE2 0x3002 | ||
| 301 | #define GL_CLIP_DISTANCE3 0x3003 | ||
| 302 | #define GL_CLIP_DISTANCE4 0x3004 | ||
| 303 | #define GL_CLIP_DISTANCE5 0x3005 | ||
| 304 | #define GL_CLIP_DISTANCE6 0x3006 | ||
| 305 | #define GL_CLIP_DISTANCE7 0x3007 | ||
| 306 | #define GL_COLOR 0x1800 | ||
| 307 | #define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 | ||
| 308 | #define GL_COLOR_ATTACHMENT0 0x8CE0 | ||
| 309 | #define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 | ||
| 310 | #define GL_COLOR_ATTACHMENT1 0x8CE1 | ||
| 311 | #define GL_COLOR_ATTACHMENT10 0x8CEA | ||
| 312 | #define GL_COLOR_ATTACHMENT10_EXT 0x8CEA | ||
| 313 | #define GL_COLOR_ATTACHMENT11 0x8CEB | ||
| 314 | #define GL_COLOR_ATTACHMENT11_EXT 0x8CEB | ||
| 315 | #define GL_COLOR_ATTACHMENT12 0x8CEC | ||
| 316 | #define GL_COLOR_ATTACHMENT12_EXT 0x8CEC | ||
| 317 | #define GL_COLOR_ATTACHMENT13 0x8CED | ||
| 318 | #define GL_COLOR_ATTACHMENT13_EXT 0x8CED | ||
| 319 | #define GL_COLOR_ATTACHMENT14 0x8CEE | ||
| 320 | #define GL_COLOR_ATTACHMENT14_EXT 0x8CEE | ||
| 321 | #define GL_COLOR_ATTACHMENT15 0x8CEF | ||
| 322 | #define GL_COLOR_ATTACHMENT15_EXT 0x8CEF | ||
| 323 | #define GL_COLOR_ATTACHMENT16 0x8CF0 | ||
| 324 | #define GL_COLOR_ATTACHMENT17 0x8CF1 | ||
| 325 | #define GL_COLOR_ATTACHMENT18 0x8CF2 | ||
| 326 | #define GL_COLOR_ATTACHMENT19 0x8CF3 | ||
| 327 | #define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 | ||
| 328 | #define GL_COLOR_ATTACHMENT2 0x8CE2 | ||
| 329 | #define GL_COLOR_ATTACHMENT20 0x8CF4 | ||
| 330 | #define GL_COLOR_ATTACHMENT21 0x8CF5 | ||
| 331 | #define GL_COLOR_ATTACHMENT22 0x8CF6 | ||
| 332 | #define GL_COLOR_ATTACHMENT23 0x8CF7 | ||
| 333 | #define GL_COLOR_ATTACHMENT24 0x8CF8 | ||
| 334 | #define GL_COLOR_ATTACHMENT25 0x8CF9 | ||
| 335 | #define GL_COLOR_ATTACHMENT26 0x8CFA | ||
| 336 | #define GL_COLOR_ATTACHMENT27 0x8CFB | ||
| 337 | #define GL_COLOR_ATTACHMENT28 0x8CFC | ||
| 338 | #define GL_COLOR_ATTACHMENT29 0x8CFD | ||
| 339 | #define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 | ||
| 340 | #define GL_COLOR_ATTACHMENT3 0x8CE3 | ||
| 341 | #define GL_COLOR_ATTACHMENT30 0x8CFE | ||
| 342 | #define GL_COLOR_ATTACHMENT31 0x8CFF | ||
| 343 | #define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 | ||
| 344 | #define GL_COLOR_ATTACHMENT4 0x8CE4 | ||
| 345 | #define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 | ||
| 346 | #define GL_COLOR_ATTACHMENT5 0x8CE5 | ||
| 347 | #define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 | ||
| 348 | #define GL_COLOR_ATTACHMENT6 0x8CE6 | ||
| 349 | #define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 | ||
| 350 | #define GL_COLOR_ATTACHMENT7 0x8CE7 | ||
| 351 | #define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 | ||
| 352 | #define GL_COLOR_ATTACHMENT8 0x8CE8 | ||
| 353 | #define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 | ||
| 354 | #define GL_COLOR_ATTACHMENT9 0x8CE9 | ||
| 355 | #define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 | ||
| 356 | #define GL_COLOR_BUFFER_BIT 0x00004000 | ||
| 357 | #define GL_COLOR_CLEAR_VALUE 0x0C22 | ||
| 358 | #define GL_COLOR_COMPONENTS 0x8283 | ||
| 359 | #define GL_COLOR_ENCODING 0x8296 | ||
| 360 | #define GL_COLOR_LOGIC_OP 0x0BF2 | ||
| 361 | #define GL_COLOR_RENDERABLE 0x8286 | ||
| 362 | #define GL_COLOR_SUM_ARB 0x8458 | ||
| 363 | #define GL_COLOR_WRITEMASK 0x0C23 | ||
| 364 | #define GL_COMMAND_BARRIER_BIT 0x00000040 | ||
| 365 | #define GL_COMPARE_REF_TO_TEXTURE 0x884E | ||
| 366 | #define GL_COMPATIBLE_SUBROUTINES 0x8E4B | ||
| 367 | #define GL_COMPILE_STATUS 0x8B81 | ||
| 368 | #define GL_COMPRESSED_ALPHA_ARB 0x84E9 | ||
| 369 | #define GL_COMPRESSED_INTENSITY_ARB 0x84EC | ||
| 370 | #define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB | ||
| 371 | #define GL_COMPRESSED_LUMINANCE_ARB 0x84EA | ||
| 372 | #define GL_COMPRESSED_R11_EAC 0x9270 | ||
| 373 | #define GL_COMPRESSED_RED 0x8225 | ||
| 374 | #define GL_COMPRESSED_RED_RGTC1 0x8DBB | ||
| 375 | #define GL_COMPRESSED_RG 0x8226 | ||
| 376 | #define GL_COMPRESSED_RG11_EAC 0x9272 | ||
| 377 | #define GL_COMPRESSED_RGB 0x84ED | ||
| 378 | #define GL_COMPRESSED_RGB8_ETC2 0x9274 | ||
| 379 | #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 | ||
| 380 | #define GL_COMPRESSED_RGBA 0x84EE | ||
| 381 | #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 | ||
| 382 | #define GL_COMPRESSED_RGBA_ARB 0x84EE | ||
| 383 | #define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB | ||
| 384 | #define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 | ||
| 385 | #define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 | ||
| 386 | #define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA | ||
| 387 | #define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC | ||
| 388 | #define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD | ||
| 389 | #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 | ||
| 390 | #define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 | ||
| 391 | #define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 | ||
| 392 | #define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 | ||
| 393 | #define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 | ||
| 394 | #define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 | ||
| 395 | #define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 | ||
| 396 | #define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 | ||
| 397 | #define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C | ||
| 398 | #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 | ||
| 399 | #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 | ||
| 400 | #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 | ||
| 401 | #define GL_COMPRESSED_RGB_ARB 0x84ED | ||
| 402 | #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E | ||
| 403 | #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F | ||
| 404 | #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 | ||
| 405 | #define GL_COMPRESSED_RG_RGTC2 0x8DBD | ||
| 406 | #define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 | ||
| 407 | #define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC | ||
| 408 | #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 | ||
| 409 | #define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE | ||
| 410 | #define GL_COMPRESSED_SRGB 0x8C48 | ||
| 411 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB | ||
| 412 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 | ||
| 413 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 | ||
| 414 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA | ||
| 415 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC | ||
| 416 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD | ||
| 417 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 | ||
| 418 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 | ||
| 419 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 | ||
| 420 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 | ||
| 421 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 | ||
| 422 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 | ||
| 423 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 | ||
| 424 | #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 | ||
| 425 | #define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 | ||
| 426 | #define GL_COMPRESSED_SRGB8_ETC2 0x9275 | ||
| 427 | #define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 | ||
| 428 | #define GL_COMPRESSED_SRGB_ALPHA 0x8C49 | ||
| 429 | #define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D | ||
| 430 | #define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 | ||
| 431 | #define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 | ||
| 432 | #define GL_COMPUTE_SHADER 0x91B9 | ||
| 433 | #define GL_COMPUTE_SHADER_BIT 0x00000020 | ||
| 434 | #define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5 | ||
| 435 | #define GL_COMPUTE_SHADER_INVOCATIONS_ARB 0x82F5 | ||
| 436 | #define GL_COMPUTE_SUBROUTINE 0x92ED | ||
| 437 | #define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 | ||
| 438 | #define GL_COMPUTE_TEXTURE 0x82A0 | ||
| 439 | #define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 | ||
| 440 | #define GL_CONDITION_SATISFIED 0x911C | ||
| 441 | #define GL_CONSTANT_ALPHA 0x8003 | ||
| 442 | #define GL_CONSTANT_COLOR 0x8001 | ||
| 443 | #define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 | ||
| 444 | #define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 | ||
| 445 | #define GL_CONTEXT_FLAGS 0x821E | ||
| 446 | #define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 | ||
| 447 | #define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 | ||
| 448 | #define GL_CONTEXT_PROFILE_MASK 0x9126 | ||
| 449 | #define GL_COPY 0x1503 | ||
| 450 | #define GL_COPY_INVERTED 0x150C | ||
| 451 | #define GL_COPY_READ_BUFFER 0x8F36 | ||
| 452 | #define GL_COPY_READ_BUFFER_BINDING 0x8F36 | ||
| 453 | #define GL_COPY_WRITE_BUFFER 0x8F37 | ||
| 454 | #define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 | ||
| 455 | #define GL_CULL_FACE 0x0B44 | ||
| 456 | #define GL_CULL_FACE_MODE 0x0B45 | ||
| 457 | #define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 | ||
| 458 | #define GL_CURRENT_MATRIX_ARB 0x8641 | ||
| 459 | #define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 | ||
| 460 | #define GL_CURRENT_PROGRAM 0x8B8D | ||
| 461 | #define GL_CURRENT_QUERY 0x8865 | ||
| 462 | #define GL_CURRENT_QUERY_ARB 0x8865 | ||
| 463 | #define GL_CURRENT_VERTEX_ATTRIB 0x8626 | ||
| 464 | #define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 | ||
| 465 | #define GL_CW 0x0900 | ||
| 466 | #define GL_DEBUG_CALLBACK_FUNCTION 0x8244 | ||
| 467 | #define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 | ||
| 468 | #define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 | ||
| 469 | #define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 | ||
| 470 | #define GL_DEBUG_GROUP_STACK_DEPTH 0x826D | ||
| 471 | #define GL_DEBUG_LOGGED_MESSAGES 0x9145 | ||
| 472 | #define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 | ||
| 473 | #define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 | ||
| 474 | #define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 | ||
| 475 | #define GL_DEBUG_OUTPUT 0x92E0 | ||
| 476 | #define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 | ||
| 477 | #define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 | ||
| 478 | #define GL_DEBUG_SEVERITY_HIGH 0x9146 | ||
| 479 | #define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 | ||
| 480 | #define GL_DEBUG_SEVERITY_LOW 0x9148 | ||
| 481 | #define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 | ||
| 482 | #define GL_DEBUG_SEVERITY_MEDIUM 0x9147 | ||
| 483 | #define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 | ||
| 484 | #define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B | ||
| 485 | #define GL_DEBUG_SOURCE_API 0x8246 | ||
| 486 | #define GL_DEBUG_SOURCE_API_ARB 0x8246 | ||
| 487 | #define GL_DEBUG_SOURCE_APPLICATION 0x824A | ||
| 488 | #define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A | ||
| 489 | #define GL_DEBUG_SOURCE_OTHER 0x824B | ||
| 490 | #define GL_DEBUG_SOURCE_OTHER_ARB 0x824B | ||
| 491 | #define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 | ||
| 492 | #define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 | ||
| 493 | #define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 | ||
| 494 | #define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 | ||
| 495 | #define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 | ||
| 496 | #define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 | ||
| 497 | #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D | ||
| 498 | #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D | ||
| 499 | #define GL_DEBUG_TYPE_ERROR 0x824C | ||
| 500 | #define GL_DEBUG_TYPE_ERROR_ARB 0x824C | ||
| 501 | #define GL_DEBUG_TYPE_MARKER 0x8268 | ||
| 502 | #define GL_DEBUG_TYPE_OTHER 0x8251 | ||
| 503 | #define GL_DEBUG_TYPE_OTHER_ARB 0x8251 | ||
| 504 | #define GL_DEBUG_TYPE_PERFORMANCE 0x8250 | ||
| 505 | #define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 | ||
| 506 | #define GL_DEBUG_TYPE_POP_GROUP 0x826A | ||
| 507 | #define GL_DEBUG_TYPE_PORTABILITY 0x824F | ||
| 508 | #define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F | ||
| 509 | #define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 | ||
| 510 | #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E | ||
| 511 | #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E | ||
| 512 | #define GL_DECR 0x1E03 | ||
| 513 | #define GL_DECR_WRAP 0x8508 | ||
| 514 | #define GL_DELETE_STATUS 0x8B80 | ||
| 515 | #define GL_DEPTH 0x1801 | ||
| 516 | #define GL_DEPTH24_STENCIL8 0x88F0 | ||
| 517 | #define GL_DEPTH32F_STENCIL8 0x8CAD | ||
| 518 | #define GL_DEPTH_ATTACHMENT 0x8D00 | ||
| 519 | #define GL_DEPTH_ATTACHMENT_EXT 0x8D00 | ||
| 520 | #define GL_DEPTH_BUFFER_BIT 0x00000100 | ||
| 521 | #define GL_DEPTH_CLAMP 0x864F | ||
| 522 | #define GL_DEPTH_CLEAR_VALUE 0x0B73 | ||
| 523 | #define GL_DEPTH_COMPONENT 0x1902 | ||
| 524 | #define GL_DEPTH_COMPONENT16 0x81A5 | ||
| 525 | #define GL_DEPTH_COMPONENT16_ARB 0x81A5 | ||
| 526 | #define GL_DEPTH_COMPONENT24 0x81A6 | ||
| 527 | #define GL_DEPTH_COMPONENT24_ARB 0x81A6 | ||
| 528 | #define GL_DEPTH_COMPONENT32 0x81A7 | ||
| 529 | #define GL_DEPTH_COMPONENT32F 0x8CAC | ||
| 530 | #define GL_DEPTH_COMPONENT32_ARB 0x81A7 | ||
| 531 | #define GL_DEPTH_COMPONENTS 0x8284 | ||
| 532 | #define GL_DEPTH_FUNC 0x0B74 | ||
| 533 | #define GL_DEPTH_RANGE 0x0B70 | ||
| 534 | #define GL_DEPTH_RENDERABLE 0x8287 | ||
| 535 | #define GL_DEPTH_STENCIL 0x84F9 | ||
| 536 | #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A | ||
| 537 | #define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA | ||
| 538 | #define GL_DEPTH_TEST 0x0B71 | ||
| 539 | #define GL_DEPTH_TEXTURE_MODE_ARB 0x884B | ||
| 540 | #define GL_DEPTH_WRITEMASK 0x0B72 | ||
| 541 | #define GL_DISPATCH_INDIRECT_BUFFER 0x90EE | ||
| 542 | #define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF | ||
| 543 | #define GL_DITHER 0x0BD0 | ||
| 544 | #define GL_DONT_CARE 0x1100 | ||
| 545 | #define GL_DOUBLE 0x140A | ||
| 546 | #define GL_DOUBLEBUFFER 0x0C32 | ||
| 547 | #define GL_DOUBLE_MAT2 0x8F46 | ||
| 548 | #define GL_DOUBLE_MAT2x3 0x8F49 | ||
| 549 | #define GL_DOUBLE_MAT2x4 0x8F4A | ||
| 550 | #define GL_DOUBLE_MAT3 0x8F47 | ||
| 551 | #define GL_DOUBLE_MAT3x2 0x8F4B | ||
| 552 | #define GL_DOUBLE_MAT3x4 0x8F4C | ||
| 553 | #define GL_DOUBLE_MAT4 0x8F48 | ||
| 554 | #define GL_DOUBLE_MAT4x2 0x8F4D | ||
| 555 | #define GL_DOUBLE_MAT4x3 0x8F4E | ||
| 556 | #define GL_DOUBLE_VEC2 0x8FFC | ||
| 557 | #define GL_DOUBLE_VEC3 0x8FFD | ||
| 558 | #define GL_DOUBLE_VEC4 0x8FFE | ||
| 559 | #define GL_DRAW_BUFFER 0x0C01 | ||
| 560 | #define GL_DRAW_BUFFER0 0x8825 | ||
| 561 | #define GL_DRAW_BUFFER0_ARB 0x8825 | ||
| 562 | #define GL_DRAW_BUFFER1 0x8826 | ||
| 563 | #define GL_DRAW_BUFFER10 0x882F | ||
| 564 | #define GL_DRAW_BUFFER10_ARB 0x882F | ||
| 565 | #define GL_DRAW_BUFFER11 0x8830 | ||
| 566 | #define GL_DRAW_BUFFER11_ARB 0x8830 | ||
| 567 | #define GL_DRAW_BUFFER12 0x8831 | ||
| 568 | #define GL_DRAW_BUFFER12_ARB 0x8831 | ||
| 569 | #define GL_DRAW_BUFFER13 0x8832 | ||
| 570 | #define GL_DRAW_BUFFER13_ARB 0x8832 | ||
| 571 | #define GL_DRAW_BUFFER14 0x8833 | ||
| 572 | #define GL_DRAW_BUFFER14_ARB 0x8833 | ||
| 573 | #define GL_DRAW_BUFFER15 0x8834 | ||
| 574 | #define GL_DRAW_BUFFER15_ARB 0x8834 | ||
| 575 | #define GL_DRAW_BUFFER1_ARB 0x8826 | ||
| 576 | #define GL_DRAW_BUFFER2 0x8827 | ||
| 577 | #define GL_DRAW_BUFFER2_ARB 0x8827 | ||
| 578 | #define GL_DRAW_BUFFER3 0x8828 | ||
| 579 | #define GL_DRAW_BUFFER3_ARB 0x8828 | ||
| 580 | #define GL_DRAW_BUFFER4 0x8829 | ||
| 581 | #define GL_DRAW_BUFFER4_ARB 0x8829 | ||
| 582 | #define GL_DRAW_BUFFER5 0x882A | ||
| 583 | #define GL_DRAW_BUFFER5_ARB 0x882A | ||
| 584 | #define GL_DRAW_BUFFER6 0x882B | ||
| 585 | #define GL_DRAW_BUFFER6_ARB 0x882B | ||
| 586 | #define GL_DRAW_BUFFER7 0x882C | ||
| 587 | #define GL_DRAW_BUFFER7_ARB 0x882C | ||
| 588 | #define GL_DRAW_BUFFER8 0x882D | ||
| 589 | #define GL_DRAW_BUFFER8_ARB 0x882D | ||
| 590 | #define GL_DRAW_BUFFER9 0x882E | ||
| 591 | #define GL_DRAW_BUFFER9_ARB 0x882E | ||
| 592 | #define GL_DRAW_FRAMEBUFFER 0x8CA9 | ||
| 593 | #define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 | ||
| 594 | #define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 | ||
| 595 | #define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 | ||
| 596 | #define GL_DRAW_INDIRECT_BUFFER 0x8F3F | ||
| 597 | #define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 | ||
| 598 | #define GL_DST_ALPHA 0x0304 | ||
| 599 | #define GL_DST_COLOR 0x0306 | ||
| 600 | #define GL_DYNAMIC_COPY 0x88EA | ||
| 601 | #define GL_DYNAMIC_COPY_ARB 0x88EA | ||
| 602 | #define GL_DYNAMIC_DRAW 0x88E8 | ||
| 603 | #define GL_DYNAMIC_DRAW_ARB 0x88E8 | ||
| 604 | #define GL_DYNAMIC_READ 0x88E9 | ||
| 605 | #define GL_DYNAMIC_READ_ARB 0x88E9 | ||
| 606 | #define GL_DYNAMIC_STORAGE_BIT 0x0100 | ||
| 607 | #define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B | ||
| 608 | #define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 | ||
| 609 | #define GL_ELEMENT_ARRAY_BUFFER 0x8893 | ||
| 610 | #define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 | ||
| 611 | #define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 | ||
| 612 | #define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 | ||
| 613 | #define GL_EQUAL 0x0202 | ||
| 614 | #define GL_EQUIV 0x1509 | ||
| 615 | #define GL_EXTENSIONS 0x1F03 | ||
| 616 | #define GL_FALSE 0 | ||
| 617 | #define GL_FASTEST 0x1101 | ||
| 618 | #define GL_FILL 0x1B02 | ||
| 619 | #define GL_FILTER 0x829A | ||
| 620 | #define GL_FIRST_VERTEX_CONVENTION 0x8E4D | ||
| 621 | #define GL_FIXED 0x140C | ||
| 622 | #define GL_FIXED_OES 0x140C | ||
| 623 | #define GL_FIXED_ONLY 0x891D | ||
| 624 | #define GL_FIXED_ONLY_ARB 0x891D | ||
| 625 | #define GL_FLOAT 0x1406 | ||
| 626 | #define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD | ||
| 627 | #define GL_FLOAT_MAT2 0x8B5A | ||
| 628 | #define GL_FLOAT_MAT2_ARB 0x8B5A | ||
| 629 | #define GL_FLOAT_MAT2x3 0x8B65 | ||
| 630 | #define GL_FLOAT_MAT2x4 0x8B66 | ||
| 631 | #define GL_FLOAT_MAT3 0x8B5B | ||
| 632 | #define GL_FLOAT_MAT3_ARB 0x8B5B | ||
| 633 | #define GL_FLOAT_MAT3x2 0x8B67 | ||
| 634 | #define GL_FLOAT_MAT3x4 0x8B68 | ||
| 635 | #define GL_FLOAT_MAT4 0x8B5C | ||
| 636 | #define GL_FLOAT_MAT4_ARB 0x8B5C | ||
| 637 | #define GL_FLOAT_MAT4x2 0x8B69 | ||
| 638 | #define GL_FLOAT_MAT4x3 0x8B6A | ||
| 639 | #define GL_FLOAT_VEC2 0x8B50 | ||
| 640 | #define GL_FLOAT_VEC2_ARB 0x8B50 | ||
| 641 | #define GL_FLOAT_VEC3 0x8B51 | ||
| 642 | #define GL_FLOAT_VEC3_ARB 0x8B51 | ||
| 643 | #define GL_FLOAT_VEC4 0x8B52 | ||
| 644 | #define GL_FLOAT_VEC4_ARB 0x8B52 | ||
| 645 | #define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D | ||
| 646 | #define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 | ||
| 647 | #define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 | ||
| 648 | #define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 | ||
| 649 | #define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 | ||
| 650 | #define GL_FOG_COORDINATE_EXT 0x8451 | ||
| 651 | #define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 | ||
| 652 | #define GL_FRACTIONAL_EVEN 0x8E7C | ||
| 653 | #define GL_FRACTIONAL_ODD 0x8E7B | ||
| 654 | #define GL_FRAGMENT_DEPTH_EXT 0x8452 | ||
| 655 | #define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D | ||
| 656 | #define GL_FRAGMENT_PROGRAM_ARB 0x8804 | ||
| 657 | #define GL_FRAGMENT_SHADER 0x8B30 | ||
| 658 | #define GL_FRAGMENT_SHADER_ARB 0x8B30 | ||
| 659 | #define GL_FRAGMENT_SHADER_BIT 0x00000002 | ||
| 660 | #define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B | ||
| 661 | #define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B | ||
| 662 | #define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4 | ||
| 663 | #define GL_FRAGMENT_SHADER_INVOCATIONS_ARB 0x82F4 | ||
| 664 | #define GL_FRAGMENT_SUBROUTINE 0x92EC | ||
| 665 | #define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 | ||
| 666 | #define GL_FRAGMENT_TEXTURE 0x829F | ||
| 667 | #define GL_FRAMEBUFFER 0x8D40 | ||
| 668 | #define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 | ||
| 669 | #define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 | ||
| 670 | #define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 | ||
| 671 | #define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 | ||
| 672 | #define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 | ||
| 673 | #define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 | ||
| 674 | #define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 | ||
| 675 | #define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 | ||
| 676 | #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 | ||
| 677 | #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 | ||
| 678 | #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 | ||
| 679 | #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 | ||
| 680 | #define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 | ||
| 681 | #define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 | ||
| 682 | #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 | ||
| 683 | #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 | ||
| 684 | #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 | ||
| 685 | #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 | ||
| 686 | #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 | ||
| 687 | #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 | ||
| 688 | #define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 | ||
| 689 | #define GL_FRAMEBUFFER_BINDING 0x8CA6 | ||
| 690 | #define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 | ||
| 691 | #define GL_FRAMEBUFFER_BLEND 0x828B | ||
| 692 | #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 | ||
| 693 | #define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 | ||
| 694 | #define GL_FRAMEBUFFER_DEFAULT 0x8218 | ||
| 695 | #define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 | ||
| 696 | #define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 | ||
| 697 | #define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 | ||
| 698 | #define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 | ||
| 699 | #define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 | ||
| 700 | #define GL_FRAMEBUFFER_EXT 0x8D40 | ||
| 701 | #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 | ||
| 702 | #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 | ||
| 703 | #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 | ||
| 704 | #define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB | ||
| 705 | #define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB | ||
| 706 | #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA | ||
| 707 | #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 | ||
| 708 | #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 | ||
| 709 | #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 | ||
| 710 | #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 | ||
| 711 | #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 | ||
| 712 | #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 | ||
| 713 | #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 | ||
| 714 | #define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC | ||
| 715 | #define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC | ||
| 716 | #define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_ARB 0x9342 | ||
| 717 | #define GL_FRAMEBUFFER_RENDERABLE 0x8289 | ||
| 718 | #define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A | ||
| 719 | #define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB 0x9343 | ||
| 720 | #define GL_FRAMEBUFFER_SRGB 0x8DB9 | ||
| 721 | #define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA | ||
| 722 | #define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 | ||
| 723 | #define GL_FRAMEBUFFER_UNDEFINED 0x8219 | ||
| 724 | #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD | ||
| 725 | #define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD | ||
| 726 | #define GL_FRONT 0x0404 | ||
| 727 | #define GL_FRONT_AND_BACK 0x0408 | ||
| 728 | #define GL_FRONT_FACE 0x0B46 | ||
| 729 | #define GL_FRONT_LEFT 0x0400 | ||
| 730 | #define GL_FRONT_RIGHT 0x0401 | ||
| 731 | #define GL_FULL_SUPPORT 0x82B7 | ||
| 732 | #define GL_FUNC_ADD 0x8006 | ||
| 733 | #define GL_FUNC_REVERSE_SUBTRACT 0x800B | ||
| 734 | #define GL_FUNC_SUBTRACT 0x800A | ||
| 735 | #define GL_GEOMETRY_INPUT_TYPE 0x8917 | ||
| 736 | #define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB | ||
| 737 | #define GL_GEOMETRY_OUTPUT_TYPE 0x8918 | ||
| 738 | #define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC | ||
| 739 | #define GL_GEOMETRY_SHADER 0x8DD9 | ||
| 740 | #define GL_GEOMETRY_SHADER_ARB 0x8DD9 | ||
| 741 | #define GL_GEOMETRY_SHADER_BIT 0x00000004 | ||
| 742 | #define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F | ||
| 743 | #define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3 | ||
| 744 | #define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB 0x82F3 | ||
| 745 | #define GL_GEOMETRY_SUBROUTINE 0x92EB | ||
| 746 | #define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 | ||
| 747 | #define GL_GEOMETRY_TEXTURE 0x829E | ||
| 748 | #define GL_GEOMETRY_VERTICES_OUT 0x8916 | ||
| 749 | #define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA | ||
| 750 | #define GL_GEQUAL 0x0206 | ||
| 751 | #define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 | ||
| 752 | #define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 | ||
| 753 | #define GL_GREATER 0x0204 | ||
| 754 | #define GL_GREEN 0x1904 | ||
| 755 | #define GL_GREEN_INTEGER 0x8D95 | ||
| 756 | #define GL_HALF_FLOAT 0x140B | ||
| 757 | #define GL_HALF_FLOAT_ARB 0x140B | ||
| 758 | #define GL_HIGH_FLOAT 0x8DF2 | ||
| 759 | #define GL_HIGH_INT 0x8DF5 | ||
| 760 | #define GL_IMAGE_1D 0x904C | ||
| 761 | #define GL_IMAGE_1D_ARRAY 0x9052 | ||
| 762 | #define GL_IMAGE_2D 0x904D | ||
| 763 | #define GL_IMAGE_2D_ARRAY 0x9053 | ||
| 764 | #define GL_IMAGE_2D_MULTISAMPLE 0x9055 | ||
| 765 | #define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 | ||
| 766 | #define GL_IMAGE_2D_RECT 0x904F | ||
| 767 | #define GL_IMAGE_3D 0x904E | ||
| 768 | #define GL_IMAGE_BINDING_ACCESS 0x8F3E | ||
| 769 | #define GL_IMAGE_BINDING_FORMAT 0x906E | ||
| 770 | #define GL_IMAGE_BINDING_LAYER 0x8F3D | ||
| 771 | #define GL_IMAGE_BINDING_LAYERED 0x8F3C | ||
| 772 | #define GL_IMAGE_BINDING_LEVEL 0x8F3B | ||
| 773 | #define GL_IMAGE_BINDING_NAME 0x8F3A | ||
| 774 | #define GL_IMAGE_BUFFER 0x9051 | ||
| 775 | #define GL_IMAGE_CLASS_10_10_10_2 0x82C3 | ||
| 776 | #define GL_IMAGE_CLASS_11_11_10 0x82C2 | ||
| 777 | #define GL_IMAGE_CLASS_1_X_16 0x82BE | ||
| 778 | #define GL_IMAGE_CLASS_1_X_32 0x82BB | ||
| 779 | #define GL_IMAGE_CLASS_1_X_8 0x82C1 | ||
| 780 | #define GL_IMAGE_CLASS_2_X_16 0x82BD | ||
| 781 | #define GL_IMAGE_CLASS_2_X_32 0x82BA | ||
| 782 | #define GL_IMAGE_CLASS_2_X_8 0x82C0 | ||
| 783 | #define GL_IMAGE_CLASS_4_X_16 0x82BC | ||
| 784 | #define GL_IMAGE_CLASS_4_X_32 0x82B9 | ||
| 785 | #define GL_IMAGE_CLASS_4_X_8 0x82BF | ||
| 786 | #define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 | ||
| 787 | #define GL_IMAGE_CUBE 0x9050 | ||
| 788 | #define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 | ||
| 789 | #define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 | ||
| 790 | #define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 | ||
| 791 | #define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 | ||
| 792 | #define GL_IMAGE_PIXEL_FORMAT 0x82A9 | ||
| 793 | #define GL_IMAGE_PIXEL_TYPE 0x82AA | ||
| 794 | #define GL_IMAGE_TEXEL_SIZE 0x82A7 | ||
| 795 | #define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B | ||
| 796 | #define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A | ||
| 797 | #define GL_INCR 0x1E02 | ||
| 798 | #define GL_INCR_WRAP 0x8507 | ||
| 799 | #define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 | ||
| 800 | #define GL_INFO_LOG_LENGTH 0x8B84 | ||
| 801 | #define GL_INT 0x1404 | ||
| 802 | #define GL_INT64_ARB 0x140E | ||
| 803 | #define GL_INT64_VEC2_ARB 0x8FE9 | ||
| 804 | #define GL_INT64_VEC3_ARB 0x8FEA | ||
| 805 | #define GL_INT64_VEC4_ARB 0x8FEB | ||
| 806 | #define GL_INTENSITY16F_ARB 0x881D | ||
| 807 | #define GL_INTENSITY32F_ARB 0x8817 | ||
| 808 | #define GL_INTERLEAVED_ATTRIBS 0x8C8C | ||
| 809 | #define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 | ||
| 810 | #define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B | ||
| 811 | #define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 | ||
| 812 | #define GL_INTERNALFORMAT_BLUE_TYPE 0x827A | ||
| 813 | #define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 | ||
| 814 | #define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C | ||
| 815 | #define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 | ||
| 816 | #define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 | ||
| 817 | #define GL_INTERNALFORMAT_PREFERRED 0x8270 | ||
| 818 | #define GL_INTERNALFORMAT_RED_SIZE 0x8271 | ||
| 819 | #define GL_INTERNALFORMAT_RED_TYPE 0x8278 | ||
| 820 | #define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 | ||
| 821 | #define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 | ||
| 822 | #define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D | ||
| 823 | #define GL_INTERNALFORMAT_SUPPORTED 0x826F | ||
| 824 | #define GL_INT_2_10_10_10_REV 0x8D9F | ||
| 825 | #define GL_INT_IMAGE_1D 0x9057 | ||
| 826 | #define GL_INT_IMAGE_1D_ARRAY 0x905D | ||
| 827 | #define GL_INT_IMAGE_2D 0x9058 | ||
| 828 | #define GL_INT_IMAGE_2D_ARRAY 0x905E | ||
| 829 | #define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 | ||
| 830 | #define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 | ||
| 831 | #define GL_INT_IMAGE_2D_RECT 0x905A | ||
| 832 | #define GL_INT_IMAGE_3D 0x9059 | ||
| 833 | #define GL_INT_IMAGE_BUFFER 0x905C | ||
| 834 | #define GL_INT_IMAGE_CUBE 0x905B | ||
| 835 | #define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F | ||
| 836 | #define GL_INT_SAMPLER_1D 0x8DC9 | ||
| 837 | #define GL_INT_SAMPLER_1D_ARRAY 0x8DCE | ||
| 838 | #define GL_INT_SAMPLER_2D 0x8DCA | ||
| 839 | #define GL_INT_SAMPLER_2D_ARRAY 0x8DCF | ||
| 840 | #define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 | ||
| 841 | #define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C | ||
| 842 | #define GL_INT_SAMPLER_2D_RECT 0x8DCD | ||
| 843 | #define GL_INT_SAMPLER_3D 0x8DCB | ||
| 844 | #define GL_INT_SAMPLER_BUFFER 0x8DD0 | ||
| 845 | #define GL_INT_SAMPLER_CUBE 0x8DCC | ||
| 846 | #define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E | ||
| 847 | #define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E | ||
| 848 | #define GL_INT_VEC2 0x8B53 | ||
| 849 | #define GL_INT_VEC2_ARB 0x8B53 | ||
| 850 | #define GL_INT_VEC3 0x8B54 | ||
| 851 | #define GL_INT_VEC3_ARB 0x8B54 | ||
| 852 | #define GL_INT_VEC4 0x8B55 | ||
| 853 | #define GL_INT_VEC4_ARB 0x8B55 | ||
| 854 | #define GL_INVALID_ENUM 0x0500 | ||
| 855 | #define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 | ||
| 856 | #define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 | ||
| 857 | #define GL_INVALID_INDEX 0xFFFFFFFF | ||
| 858 | #define GL_INVALID_OPERATION 0x0502 | ||
| 859 | #define GL_INVALID_VALUE 0x0501 | ||
| 860 | #define GL_INVERT 0x150A | ||
| 861 | #define GL_ISOLINES 0x8E7A | ||
| 862 | #define GL_IS_PER_PATCH 0x92E7 | ||
| 863 | #define GL_IS_ROW_MAJOR 0x9300 | ||
| 864 | #define GL_KEEP 0x1E00 | ||
| 865 | #define GL_LAST_VERTEX_CONVENTION 0x8E4E | ||
| 866 | #define GL_LAYER_PROVOKING_VERTEX 0x825E | ||
| 867 | #define GL_LEFT 0x0406 | ||
| 868 | #define GL_LEQUAL 0x0203 | ||
| 869 | #define GL_LESS 0x0201 | ||
| 870 | #define GL_LINE 0x1B01 | ||
| 871 | #define GL_LINEAR 0x2601 | ||
| 872 | #define GL_LINEAR_MIPMAP_LINEAR 0x2703 | ||
| 873 | #define GL_LINEAR_MIPMAP_NEAREST 0x2701 | ||
| 874 | #define GL_LINES 0x0001 | ||
| 875 | #define GL_LINES_ADJACENCY 0x000A | ||
| 876 | #define GL_LINES_ADJACENCY_ARB 0x000A | ||
| 877 | #define GL_LINE_LOOP 0x0002 | ||
| 878 | #define GL_LINE_SMOOTH 0x0B20 | ||
| 879 | #define GL_LINE_SMOOTH_HINT 0x0C52 | ||
| 880 | #define GL_LINE_STRIP 0x0003 | ||
| 881 | #define GL_LINE_STRIP_ADJACENCY 0x000B | ||
| 882 | #define GL_LINE_STRIP_ADJACENCY_ARB 0x000B | ||
| 883 | #define GL_LINE_WIDTH 0x0B21 | ||
| 884 | #define GL_LINE_WIDTH_GRANULARITY 0x0B23 | ||
| 885 | #define GL_LINE_WIDTH_RANGE 0x0B22 | ||
| 886 | #define GL_LINK_STATUS 0x8B82 | ||
| 887 | #define GL_LOCATION 0x930E | ||
| 888 | #define GL_LOCATION_COMPONENT 0x934A | ||
| 889 | #define GL_LOCATION_INDEX 0x930F | ||
| 890 | #define GL_LOGIC_OP_MODE 0x0BF0 | ||
| 891 | #define GL_LOWER_LEFT 0x8CA1 | ||
| 892 | #define GL_LOW_FLOAT 0x8DF0 | ||
| 893 | #define GL_LOW_INT 0x8DF3 | ||
| 894 | #define GL_LUMINANCE16F_ARB 0x881E | ||
| 895 | #define GL_LUMINANCE32F_ARB 0x8818 | ||
| 896 | #define GL_LUMINANCE_ALPHA16F_ARB 0x881F | ||
| 897 | #define GL_LUMINANCE_ALPHA32F_ARB 0x8819 | ||
| 898 | #define GL_MAJOR_VERSION 0x821B | ||
| 899 | #define GL_MANUAL_GENERATE_MIPMAP 0x8294 | ||
| 900 | #define GL_MAP_COHERENT_BIT 0x0080 | ||
| 901 | #define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 | ||
| 902 | #define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 | ||
| 903 | #define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 | ||
| 904 | #define GL_MAP_PERSISTENT_BIT 0x0040 | ||
| 905 | #define GL_MAP_READ_BIT 0x0001 | ||
| 906 | #define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 | ||
| 907 | #define GL_MAP_WRITE_BIT 0x0002 | ||
| 908 | #define GL_MATRIX0_ARB 0x88C0 | ||
| 909 | #define GL_MATRIX10_ARB 0x88CA | ||
| 910 | #define GL_MATRIX11_ARB 0x88CB | ||
| 911 | #define GL_MATRIX12_ARB 0x88CC | ||
| 912 | #define GL_MATRIX13_ARB 0x88CD | ||
| 913 | #define GL_MATRIX14_ARB 0x88CE | ||
| 914 | #define GL_MATRIX15_ARB 0x88CF | ||
| 915 | #define GL_MATRIX16_ARB 0x88D0 | ||
| 916 | #define GL_MATRIX17_ARB 0x88D1 | ||
| 917 | #define GL_MATRIX18_ARB 0x88D2 | ||
| 918 | #define GL_MATRIX19_ARB 0x88D3 | ||
| 919 | #define GL_MATRIX1_ARB 0x88C1 | ||
| 920 | #define GL_MATRIX20_ARB 0x88D4 | ||
| 921 | #define GL_MATRIX21_ARB 0x88D5 | ||
| 922 | #define GL_MATRIX22_ARB 0x88D6 | ||
| 923 | #define GL_MATRIX23_ARB 0x88D7 | ||
| 924 | #define GL_MATRIX24_ARB 0x88D8 | ||
| 925 | #define GL_MATRIX25_ARB 0x88D9 | ||
| 926 | #define GL_MATRIX26_ARB 0x88DA | ||
| 927 | #define GL_MATRIX27_ARB 0x88DB | ||
| 928 | #define GL_MATRIX28_ARB 0x88DC | ||
| 929 | #define GL_MATRIX29_ARB 0x88DD | ||
| 930 | #define GL_MATRIX2_ARB 0x88C2 | ||
| 931 | #define GL_MATRIX30_ARB 0x88DE | ||
| 932 | #define GL_MATRIX31_ARB 0x88DF | ||
| 933 | #define GL_MATRIX3_ARB 0x88C3 | ||
| 934 | #define GL_MATRIX4_ARB 0x88C4 | ||
| 935 | #define GL_MATRIX5_ARB 0x88C5 | ||
| 936 | #define GL_MATRIX6_ARB 0x88C6 | ||
| 937 | #define GL_MATRIX7_ARB 0x88C7 | ||
| 938 | #define GL_MATRIX8_ARB 0x88C8 | ||
| 939 | #define GL_MATRIX9_ARB 0x88C9 | ||
| 940 | #define GL_MATRIX_STRIDE 0x92FF | ||
| 941 | #define GL_MAX 0x8008 | ||
| 942 | #define GL_MAX_3D_TEXTURE_SIZE 0x8073 | ||
| 943 | #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF | ||
| 944 | #define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC | ||
| 945 | #define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 | ||
| 946 | #define GL_MAX_CLIP_DISTANCES 0x0D32 | ||
| 947 | #define GL_MAX_COLOR_ATTACHMENTS 0x8CDF | ||
| 948 | #define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF | ||
| 949 | #define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E | ||
