From 4b2323c4d6ec97b51ff45c1994d01526420c7c66 Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Fri, 15 May 2026 02:52:27 +0200 Subject: [PATCH 01/13] Update Angle from version 3712 to 7258 (includes our modified fork) --- depends/windows/angle/0001-fix-uwp.patch | 263 ----------------- depends/windows/angle/CMakeLists.txt | 359 ----------------------- depends/windows/angle/angle.sha256 | 2 +- depends/windows/angle/angle.txt | 2 +- depends/windows/angle/flags.txt | 1 + 5 files changed, 3 insertions(+), 624 deletions(-) delete mode 100644 depends/windows/angle/0001-fix-uwp.patch delete mode 100644 depends/windows/angle/CMakeLists.txt create mode 100644 depends/windows/angle/flags.txt diff --git a/depends/windows/angle/0001-fix-uwp.patch b/depends/windows/angle/0001-fix-uwp.patch deleted file mode 100644 index 7f5bc5f..0000000 --- a/depends/windows/angle/0001-fix-uwp.patch +++ /dev/null @@ -1,263 +0,0 @@ -diff --git a/commit.h b/commit.h -new file mode 100644 -index 0000000..654e023 ---- /dev/null -+++ b/commit.h -@@ -0,0 +1,3 @@ -+#define ANGLE_COMMIT_HASH "invalid-hash" -+#define ANGLE_COMMIT_HASH_SIZE 12 -+#define ANGLE_COMMIT_DATE "invalid-date" -diff --git a/src/common/system_utils_win.cpp b/src/common/system_utils_win.cpp -index 7db4485..04b6a85 100644 ---- a/src/common/system_utils_win.cpp -+++ b/src/common/system_utils_win.cpp -@@ -37,6 +37,7 @@ const char *GetSharedLibraryExtension() - - Optional GetCWD() - { -+#ifndef ANGLE_ENABLE_WINDOWS_STORE - std::array pathBuf; - DWORD result = GetCurrentDirectoryA(static_cast(pathBuf.size()), pathBuf.data()); - if (result == 0) -@@ -44,11 +45,18 @@ Optional GetCWD() - return Optional::Invalid(); - } - return std::string(pathBuf.data()); -+#else -+ return std::string(); -+#endif - } - - bool SetCWD(const char *dirName) - { -+#ifndef ANGLE_ENABLE_WINDOWS_STORE - return (SetCurrentDirectoryA(dirName) == TRUE); -+#else -+ return false; -+#endif - } - - bool UnsetEnvironmentVar(const char *variableName) -@@ -86,12 +94,14 @@ class Win32Library : public Library - public: - Win32Library(const char *libraryName) - { -+#ifndef ANGLE_ENABLE_WINDOWS_STORE - char buffer[MAX_PATH]; - int ret = snprintf(buffer, MAX_PATH, "%s.%s", libraryName, GetSharedLibraryExtension()); - if (ret > 0 && ret < MAX_PATH) - { - mModule = LoadLibraryA(buffer); - } -+#endif - } - - ~Win32Library() override -diff --git a/src/libANGLE/renderer/d3d/RendererD3D.cpp b/src/libANGLE/renderer/d3d/RendererD3D.cpp -index 2a8d022..36f1f25 100644 ---- a/src/libANGLE/renderer/d3d/RendererD3D.cpp -+++ b/src/libANGLE/renderer/d3d/RendererD3D.cpp -@@ -229,7 +229,9 @@ GLenum DefaultGLErrorCode(HRESULT hr) - { - switch (hr) - { -+#ifdef ANGLE_ENABLE_D3D9 - case D3DERR_OUTOFVIDEOMEMORY: -+#endif - case E_OUTOFMEMORY: - return GL_OUT_OF_MEMORY; - default: -diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp -index 9b67ac4..20584e3 100644 ---- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp -+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp -@@ -1192,11 +1192,13 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions - // All D3D feature levels support robust resource init - outExtensions->robustResourceInitialization = true; - -+#ifndef ANGLE_ENABLE_WINDOWS_STORE - // Compositor Native Window capabilies require WinVer >= 1803 - if (CompositorNativeWindow11::IsSupportedWinRelease()) - { - outExtensions->windowsUIComposition = true; - } -+#endif - } - - angle::Result Renderer11::flush(Context11 *context11) -@@ -1270,7 +1272,11 @@ NativeWindowD3D *Renderer11::createNativeWindow(EGLNativeWindowType window, - const egl::Config *config, - const egl::AttributeMap &attribs) const - { -- auto useWinUiComp = window != nullptr && !NativeWindow11Win32::IsValidNativeWindow(window); -+#ifdef ANGLE_ENABLE_WINDOWS_STORE -+ (void)attribs; -+ return new NativeWindow11WinRT(window, config->alphaSize > 0); -+#else -+ auto useWinUiComp = window != nullptr && !isValidNativeWindow(window); - - if (useWinUiComp) - { -@@ -1278,15 +1284,11 @@ NativeWindowD3D *Renderer11::createNativeWindow(EGLNativeWindowType window, - } - else - { --#ifdef ANGLE_ENABLE_WINDOWS_STORE -- UNUSED_VARIABLE(attribs); -- return new NativeWindow11WinRT(window, config->alphaSize > 0); --#else - return new NativeWindow11Win32( - window, config->alphaSize > 0, - attribs.get(EGL_DIRECT_COMPOSITION_ANGLE, EGL_FALSE) == EGL_TRUE); --#endif - } -+#endif - } - - egl::Error Renderer11::getD3DTextureInfo(const egl::Config *configuration, -diff --git a/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp b/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp -index cb2f279..99b615c 100644 ---- a/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp -+++ b/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp -@@ -145,6 +145,7 @@ HRESULT CoreWindowNativeWindow::createSwapChain(ID3D11Device *device, - unsigned int width, - unsigned int height, - bool containsAlpha, -+ unsigned int samples, - IDXGISwapChain1 **swapChain) - { - if (device == nullptr || factory == nullptr || swapChain == nullptr || width == 0 || -@@ -158,7 +159,7 @@ HRESULT CoreWindowNativeWindow::createSwapChain(ID3D11Device *device, - swapChainDesc.Height = height; - swapChainDesc.Format = format; - swapChainDesc.Stereo = FALSE; -- swapChainDesc.SampleDesc.Count = 1; -+ swapChainDesc.SampleDesc.Count = samples; - swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.BufferUsage = - DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER; -@@ -213,10 +214,10 @@ HRESULT GetCoreWindowSizeInPixels(const ComPtr displayProperties; -+ ComPtr displayProperties; - - if (SUCCEEDED(GetActivationFactory( -- HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayProperties).Get(), -+ HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayInformation).Get(), - displayProperties.GetAddressOf()))) - { - float dpi = 96.0f; -diff --git a/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.h b/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.h -index ae57cfb..3262d0a 100644 ---- a/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.h -+++ b/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.h -@@ -35,7 +35,8 @@ class CoreWindowNativeWindow : public InspectableNativeWindow, - DXGI_FORMAT format, - unsigned int width, - unsigned int height, - bool containsAlpha, -+ unsigned int samples, - IDXGISwapChain1 **swapChain) override; - - protected: -@@ -49,7 +50,7 @@ class CoreWindowNativeWindow : public InspectableNativeWindow, - ComPtr> mPropertyMap; - }; - --[uuid(7F924F66 - EBAE - 40E5 - A10B - B8F35E245190)] class CoreWindowSizeChangedHandler -+[uuid(7F924F66-EBAE-40E5-A10B-B8F35E245190)] class CoreWindowSizeChangedHandler - : public Microsoft::WRL::RuntimeClass< - Microsoft::WRL::RuntimeClassFlags, - IWindowSizeChangedEventHandler> -diff --git a/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.h b/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.h -index 708e8a2..9bfa4c4 100644 ---- a/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.h -+++ b/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.h -@@ -53,6 +53,7 @@ class InspectableNativeWindow - unsigned int width, - unsigned int height, - bool containsAlpha, -+ unsigned int samples, - IDXGISwapChain1 **swapChain) = 0; - - bool getClientRect(RECT *rect) -diff --git a/src/libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.cpp b/src/libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.cpp -index 8972ca2..571955d 100644 ---- a/src/libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.cpp -+++ b/src/libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.cpp -@@ -88,6 +88,7 @@ HRESULT NativeWindow11WinRT::createSwapChain(ID3D11Device *device, - DXGI_FORMAT format, - UINT width, - UINT height, -+ UINT samples, - IDXGISwapChain **swapChain) - { - if (mImpl) -@@ -95,7 +96,7 @@ HRESULT NativeWindow11WinRT::createSwapChain(ID3D11Device *device, - IDXGIFactory2 *factory2 = d3d11::DynamicCastComObject(factory); - IDXGISwapChain1 *swapChain1 = nullptr; - HRESULT result = -- mImpl->createSwapChain(device, factory2, format, width, height, mHasAlpha, &swapChain1); -+ mImpl->createSwapChain(device, factory2, format, width, height, mHasAlpha, samples, &swapChain1); - SafeRelease(factory2); - *swapChain = static_cast(swapChain1); - return result; -diff --git a/src/libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.h b/src/libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.h -index eac5b21..92c3121 100644 ---- a/src/libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.h -+++ b/src/libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.h -@@ -34,6 +34,7 @@ class NativeWindow11WinRT : public NativeWindow11 - DXGI_FORMAT format, - UINT width, - UINT height, -+ UINT samples, - IDXGISwapChain **swapChain) override; - - void commitChange() override; -diff --git a/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.cpp b/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.cpp -index af0beb6..2fc32f1 100644 ---- a/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.cpp -+++ b/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.cpp -@@ -247,6 +247,7 @@ HRESULT SwapChainPanelNativeWindow::createSwapChain(ID3D11Device *device, - unsigned int width, - unsigned int height, - bool containsAlpha, -+ unsigned int samples, - IDXGISwapChain1 **swapChain) - { - if (device == nullptr || factory == nullptr || swapChain == nullptr || width == 0 || -@@ -260,7 +261,7 @@ HRESULT SwapChainPanelNativeWindow::createSwapChain(ID3D11Device *device, - swapChainDesc.Height = height; - swapChainDesc.Format = format; - swapChainDesc.Stereo = FALSE; -- swapChainDesc.SampleDesc.Count = 1; -+ swapChainDesc.SampleDesc.Count = samples; - swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.BufferUsage = - DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER; -diff --git a/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.h b/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.h -index 09642ee..7fc13da 100644 ---- a/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.h -+++ b/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.h -@@ -28,6 +28,7 @@ class SwapChainPanelNativeWindow : public InspectableNativeWindow, - unsigned int width, - unsigned int height, - bool containsAlpha, -+ unsigned int samples, - IDXGISwapChain1 **swapChain) override; - - protected: -@@ -43,7 +44,7 @@ class SwapChainPanelNativeWindow : public InspectableNativeWindow, - ComPtr mSwapChain; - }; - --[uuid(8ACBD974 - 8187 - 4508 - AD80 - AEC77F93CF36)] class SwapChainPanelSizeChangedHandler -+[uuid(8ACBD974-8187-4508-AD80-AEC77F93CF36)] class SwapChainPanelSizeChangedHandler - : public Microsoft::WRL::RuntimeClass< - Microsoft::WRL::RuntimeClassFlags, - ABI::Windows::UI::Xaml::ISizeChangedEventHandler> --- -2.20.1.windows.1 - diff --git a/depends/windows/angle/CMakeLists.txt b/depends/windows/angle/CMakeLists.txt deleted file mode 100644 index 26f13dd..0000000 --- a/depends/windows/angle/CMakeLists.txt +++ /dev/null @@ -1,359 +0,0 @@ -cmake_minimum_required(VERSION 3.8) -project(angle) - -message(STATUS "Used CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") -message(STATUS "Used CMAKE_SYSTEM_VERSION: ${CMAKE_SYSTEM_VERSION}") -message(STATUS "Used CMAKE_HOST_SYSTEM_VERSION: ${CMAKE_HOST_SYSTEM_VERSION}") - -if(CMAKE_SYSTEM_VERSION VERSION_LESS 10.0.16299.0) - message(FATAL_ERROR "Minimal Windows system version must be: 10.0.16299.0") -endif() - -# Force package name to kodi, e.g. BUILD_KODI_ADDON not defined on addon depends build! -if(NOT PACKAGE_NAME) - set(PACKAGE_NAME "kodi") -endif() - -if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - cmake_policy(SET CMP0076 NEW) -endif() - -option(ANGLE_ENABLE_NULL "Enable null renderer" OFF) -# windows options -option(ANGLE_ENABLE_D3D9 "Enable support D3D9 renderer (Windows desktop only)" OFF) -option(ANGLE_ENABLE_WGL "Enable support OpenGL renderer on Windows" OFF) - -if(CMAKE_SIZEOF_VOID_P EQUAL 4) - add_definitions(-DANGLE_IS_32_BIT_CPU) -elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) - add_definitions(-DANGLE_IS_64_BIT_CPU) -else() - message(FATAL_ERROR "Unknown current CPU bit size: ${CMAKE_SIZEOF_VOID_P}") -endif() - -if(WIN32 AND NOT WINDOWS_STORE) - set(WINDOWS_DESKTOP TRUE) -else() - set(WINDOWS_DESKTOP FALSE) -endif() -if (WINDOWS_DESKTOP OR WINDOWS_STORE) - set(IS_WIN 1) - set(WINDOWS_ANY TRUE) -else() - set(WINDOWS_ANY FALSE) -endif() - -if(UNIX AND NOT APPLE) - set(LINUX TRUE) -else() - set(LINUX FALSE) -endif() - -if(CMAKE_BUILD_TYPE MATCHES DEBUG) - set(IS_DEBUG TRUE) -else() - set(IS_DEBUG FALSE) -endif() - -if(WINDOWS_ANY) - add_compile_options(/d2guard4 /Wv:18 /guard:cf) -else() - add_compile_options(-std=c++17 -fPIC) -endif() - -if(APPLE) - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework Cocoa -framework OpenGL -framework IOKit -framework CoreFoundation -framework IOSurface -framework QuartzCore -framework CoreGraphics") -endif() - -set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") -set(CMAKE_STATIC_LIBRARY_PREFIX "") -set(CMAKE_DEBUG_POSTFIX "_dbg") - -# if BUILD_SHARED_LIBS is undefined comes it to a cmake build error, set it as default to 0. -if(NOT DEFINED BUILD_SHARED_LIBS) - set(BUILD_SHARED_LIBS 0) -endif() - -add_definitions( - -D_CRT_SECURE_NO_DEPRECATE - -D_SCL_SECURE_NO_WARNINGS - -D_HAS_EXCEPTIONS=0 - -DNOMINMAX - -DANGLE_STANDALONE_BUILD) -if(IS_DEBUG) - add_definitions(-DANGLE_ENABLE_DEBUG_ANNOTATIONS) -endif() -if(DCHECK_ALWAYS_ON) - add_definitions(-DANGLE_ENABLE_RELEASE_ASSERTS) -endif() - -# Shared library function visibility -if(IS_WIN) - set(ANGLE_GL_VISIBILITY_CONFIG GL_APICALL= - GL_API=) -else() - set(ANGLE_GL_VISIBILITY_CONFIG GL_APICALL=__attribute__((visibility(\"default\"))) - GL_API=__attribute__((visibility(\"default\")))) -endif() - -# This config controls export definitions on ANGLE API calls. -set(ANGLE_STATIC_DEFINES ANGLE_EXPORT= - ANGLE_UTIL_EXPORT= - EGLAPI= - GL_APICALL= - GL_API=) - -set(GL_PROTOTYPES GL_GLES_PROTOTYPES=1 - EGL_EGL_PROTOTYPES=1 - GL_GLEXT_PROTOTYPES - EGL_EGLEXT_PROTOTYPES - HAS_ANGLE) - -########## -# Generate required commit.h file -set(COMMIT_H - "#define ANGLE_COMMIT_HASH \"invalid-hash\"\n" - "#define ANGLE_COMMIT_HASH_SIZE 12\n" - "#define ANGLE_COMMIT_DATE \"invalid-date\"\n") -file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/include/id/commit.h ${COMMIT_H}) -include_directories(include ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/src - ${CMAKE_CURRENT_SOURCE_DIR}/src/common/third_party/base - ${CMAKE_CURRENT_BINARY_DIR}/include) - -########## -# Load angle source list by translating *.gni's to cmake format -set(GNI_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/compiler.gni - ${CMAKE_CURRENT_SOURCE_DIR}/src/libGLESv2.gni - ${CMAKE_CURRENT_SOURCE_DIR}/src/libANGLE/renderer/d3d/d3d11/d3d11_blit_shaders_autogen.gni - ${CMAKE_CURRENT_SOURCE_DIR}/util/util.gni) - -set(ANGLE_SOURCE_FILES "") -foreach(file ${GNI_FILES}) - get_filename_component(fullpath "${file}" ABSOLUTE) - get_filename_component(file "${file}" NAME_WE) - - file(READ ${fullpath} CONTENT_GNI) - string(REGEX REPLACE "([0-9a-z_]+)([^a-z]+)([*^\=)])" "set(\\1 XXXYYY \\1 ZZZ" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE "is_win" "IS_WIN" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE "is_linux" "IS_LINUX" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE "is_android" "IS_ANDROID" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE "is_fuchsia" "IS_FUCHSIA" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE "is_mac" "IS_MAC" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE "||" "OR" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE "}" "endif()" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE " {" "" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE "\"," "" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE "\"" "" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE " [" "" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE "]" ")" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE " ZZZ" "}" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE "YYY " "{" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE "XXX" "$" CONTENT_GNI ${CONTENT_GNI}) - if(CMAKE_VERSION VERSION_LESS 3.13) - string(REPLACE " src/" " \${CMAKE_CURRENT_SOURCE_DIR}/src/" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE " include/" " \${CMAKE_CURRENT_SOURCE_DIR}/include/" CONTENT_GNI ${CONTENT_GNI}) - string(REPLACE " util/" " \${CMAKE_CURRENT_SOURCE_DIR}/util/" CONTENT_GNI ${CONTENT_GNI}) - endif() - file(WRITE ${CMAKE_BINARY_DIR}/angleSourceList-${file}.cmake ${CONTENT_GNI}) - list(APPEND ANGLE_SOURCE_FILES ${CMAKE_BINARY_DIR}/angleSourceList-${file}.cmake) -endforeach() - -foreach(file ${ANGLE_SOURCE_FILES}) - include(${file}) -endforeach() - -########## -# angle::angle_system_utils -add_library(angle_system_utils OBJECT ${angle_system_utils_sources}) -add_library(angle::system_utils ALIAS angle_system_utils) - -########## -# angle::xxhash -add_library(angle_xxhash OBJECT ${xxhash_sources}) -add_library(angle::xxhash ALIAS angle_xxhash) - -########## -# angle::common -add_library(angle_common OBJECT ${libangle_common_sources} - ${libangle_includes} - ${CMAKE_SOURCE_DIR}/src/common/version.h) -add_library(angle::common ALIAS angle_common) - -########## -# angle::image_util -add_library(angle_image_util OBJECT ${libangle_image_util_sources}) -add_library(angle::image_util ALIAS angle_image_util) - -########## -# angle::preprocessor -add_library(angle_preprocessor OBJECT ${angle_preprocessor_sources}) -add_library(angle::preprocessor ALIAS angle_preprocessor) - -########## -# angle::translator -add_library(angle_translator OBJECT ${angle_translator_sources} - ${angle_translator_essl_sources} - ${angle_translator_glsl_sources} - ${angle_translator_hlsl_sources}) -target_compile_definitions(angle_translator PUBLIC ANGLE_ENABLE_ESSL - ANGLE_ENABLE_GLSL - ANGLE_ENABLE_HLSL) -add_library(angle::translator ALIAS angle_translator) - -########## -# libANGLE - -## OpenGL Renderer -if((WINDOWS_DESKTOP AND ANGLE_ENABLE_WGL) OR LINUX OR APPLE) - add_library(angle_renderer_opengl INTERFACE) - if(WINDOWS_DESKTOP) - target_sources(angle_renderer_opengl INTERFACE $ - $) - elseif(APPLE) - target_sources(angle_renderer_opengl INTERFACE $ - $) - elseif(LINUX) - target_sources(angle_renderer_opengl INTERFACE $ - $) - endif() - - target_compile_definitions(angle_renderer_opengl INTERFACE ANGLE_ENABLE_OPENGL) - add_library(angle::renderer::opengl ALIAS angle_renderer_opengl) -endif() - -# D3D Renderers -if(WINDOWS_ANY) - ## WinRT D3D Renderer - if(WINDOWS_STORE) - add_library(angle_renderer_winrt INTERFACE) - target_sources(angle_renderer_winrt INTERFACE $) - target_link_libraries(angle_renderer_winrt INTERFACE d3dcompiler.lib) - add_library(angle::renderer::winrt ALIAS angle_renderer_winrt) - endif() - - ## Win32/d3d9 D3D Renderer - if(WINDOWS_DESKTOP AND ANGLE_ENABLE_D3D9) - add_library(angle_renderer_win32 INTERFACE) - target_sources(angle_renderer_win32 INTERFACE $ - $) - target_compile_definitions(angle_renderer_win32 INTERFACE ANGLE_ENABLE_D3D9) - target_link_libraries(angle_renderer_win32 INTERFACE d3d9.lib) - add_library(angle::renderer::win32 ALIAS angle_renderer_win32) - list(APPEND ANGLE_DEPEND_LIBRARIES d3d9.lib) - endif() - - ## D3D11 Base renderer - add_library(angle_renderer_d3d INTERFACE) - target_sources(angle_renderer_d3d INTERFACE $ - $ - $) - if(WINDOWS_DESKTOP) - target_sources(angle_renderer_d3d INTERFACE $) - endif() - target_compile_definitions(angle_renderer_d3d INTERFACE - ANGLE_ENABLE_D3D11 - "ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES={ \"d3dcompiler_47.dll\", \"d3dcompiler_46.dll\", \"d3dcompiler_43.dll\" }" - ) - target_link_libraries(angle_renderer_d3d INTERFACE d3d11.lib dxguid.lib) - add_library(angle::renderer::d3d ALIAS angle_renderer_d3d) - list(APPEND ANGLE_DEPEND_LIBRARIES d3d11.lib dxguid.lib) -endif() - -## Core libANGLE library -if(LINUX OR APPLE) - set(LIBANGLE_RENDERER_PLATFORM - $ - ) -elseif(WINDOWS_STORE) - set(LIBANGLE_RENDERER_PLATFORM - $ - $ - ) -elseif(WINDOWS_DESKTOP) - set(LIBANGLE_RENDERER_PLATFORM - $ - $<$:$> - $<$:$> - gdi32 - ) -else() - set(LIBANGLE_RENDERER_PLATFORM ) -endif() - -if(ANGLE_ENABLE_NULL) - add_library(angle_renderer_null INTERFACE) - target_sources(angle_renderer_null INTERFACE $ - $) - add_library(angle::renderer::null ALIAS angle_renderer_null) - list(APPEND LIBANGLE_RENDERER_PLATFORM $) -endif() - -add_library(libANGLE INTERFACE) -target_sources(libANGLE INTERFACE $ - $ - $ - $ - $ - $ - $) -target_link_libraries(libANGLE INTERFACE ${LIBANGLE_RENDERER_PLATFORM}) -target_include_directories(libANGLE INTERFACE $) -target_compile_definitions(libANGLE INTERFACE LIBANGLE_IMPLEMENTATION - $<$:ANGLE_ENABLE_NULL> - $<$:ANGLE_USE_X11>) -add_library(angle::libANGLE ALIAS libANGLE) - -########## -# libGLESv2 -add_library(libGLESv2 ${libglesv2_sources} - ${libangle_includes} - ${CMAKE_CURRENT_SOURCE_DIR}/src/libGLESv2/libGLESv2_autogen.def) -target_link_libraries(libGLESv2 PRIVATE $ - ${ANGLE_DEPEND_LIBRARIES}) -if(BUILD_SHARED_LIBS) - target_compile_definitions(libGLESv2 PRIVATE LIBGLESV2_IMPLEMENTATION - ${GL_PROTOTYPES} - ${ANGLE_GL_VISIBILITY_CONFIG}) -else() - target_compile_definitions(libGLESv2 PRIVATE LIBGLESV2_IMPLEMENTATION - ${GL_PROTOTYPES} - PUBLIC ${ANGLE_STATIC_DEFINES}) -endif() -target_include_directories(libGLESv2 PUBLIC $) -target_compile_definitions(libGLESv2 PUBLIC $) - -########## -# libEGL -add_library(libEGL ${libegl_sources} - ${libangle_includes} - $<$:${CMAKE_CURRENT_SOURCE_DIR}/src/libEGL/libEGL.def>) -target_link_libraries(libEGL PUBLIC libGLESv2) -target_include_directories(libEGL PUBLIC $) - -########## -# Angle installation -set_target_properties(libGLESv2 PROPERTIES PREFIX "") -set_target_properties(libEGL PROPERTIES PREFIX "") - -install(TARGETS libGLESv2 - libEGL - EXPORT ANGLEExport - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib) - -install(EXPORT ANGLEExport - FILE ${PACKAGE_NAME}-angle-config.cmake - NAMESPACE ${PACKAGE_NAME}::angle:: - DESTINATION share/${PACKAGE_NAME}-angle) - -if(NOT DISABLE_INSTALL_HEADERS) - install(DIRECTORY include/ - DESTINATION include - FILES_MATCHING - PATTERN "*.h" - PATTERN "*.inc" - PATTERN "GLSLANG" EXCLUDE - PATTERN "export.h" EXCLUDE) -endif() diff --git a/depends/windows/angle/angle.sha256 b/depends/windows/angle/angle.sha256 index 31d00b2..23d520d 100644 --- a/depends/windows/angle/angle.sha256 +++ b/depends/windows/angle/angle.sha256 @@ -1 +1 @@ -28836135bd7549c01d7d07ae5a0a0956096f2c63355cf243ef306b2213bd91ae +19d148fd319ce85f84ddebb3fc4c42990b1f228d75cdcd54e30a2390bae3247e diff --git a/depends/windows/angle/angle.txt b/depends/windows/angle/angle.txt index e9c9ff3..fcadb55 100644 --- a/depends/windows/angle/angle.txt +++ b/depends/windows/angle/angle.txt @@ -1 +1 @@ -angle https://github.com/google/angle/archive/chromium/3712.tar.gz +angle https://github.com/AlwinEsch/angle-kodi/archive/kodi/7258.tar.gz diff --git a/depends/windows/angle/flags.txt b/depends/windows/angle/flags.txt new file mode 100644 index 0000000..a90abc9 --- /dev/null +++ b/depends/windows/angle/flags.txt @@ -0,0 +1 @@ +-DANGLE_BUILD_SHARED_LIBS=FALSE -DANGLE_UNOFFICIAL_INSTALL=FALSE -DANGLE_UNOFFICIAL_NAME=kodi \ No newline at end of file From 2946537f63927cc06a4c4b3bc7e1a954b9018886 Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Fri, 15 May 2026 03:53:25 +0200 Subject: [PATCH 02/13] split add-on and fische sources with move fische to own folder As the fische is a independent code where not access Kodi's API can it be in own folder to have more clean and prepared for further changes. Further becomes it easier to take the code by someone to places outside of Kodi. --- CMakeLists.txt | 42 +++++++++++++++--------------- src/{ => fische}/analyst.cpp | 0 src/{ => fische}/analyst.h | 0 src/{ => fische}/audiobuffer.cpp | 0 src/{ => fische}/audiobuffer.h | 0 src/{ => fische}/blurengine.cpp | 0 src/{ => fische}/blurengine.h | 0 src/{ => fische}/cpudetect.cpp | 0 src/{ => fische}/fische.cpp | 0 src/{ => fische}/fische.h | 0 src/{ => fische}/fische_internal.h | 0 src/{ => fische}/screenbuffer.cpp | 0 src/{ => fische}/screenbuffer.h | 0 src/{ => fische}/vector.cpp | 0 src/{ => fische}/vector.h | 0 src/{ => fische}/vectorfield.cpp | 0 src/{ => fische}/vectorfield.h | 0 src/{ => fische}/wavepainter.cpp | 0 src/{ => fische}/wavepainter.h | 0 src/fishbmc_addon.cpp | 2 +- src/fishbmc_addon.h | 2 +- 21 files changed, 23 insertions(+), 23 deletions(-) rename src/{ => fische}/analyst.cpp (100%) rename src/{ => fische}/analyst.h (100%) rename src/{ => fische}/audiobuffer.cpp (100%) rename src/{ => fische}/audiobuffer.h (100%) rename src/{ => fische}/blurengine.cpp (100%) rename src/{ => fische}/blurengine.h (100%) rename src/{ => fische}/cpudetect.cpp (100%) rename src/{ => fische}/fische.cpp (100%) rename src/{ => fische}/fische.h (100%) rename src/{ => fische}/fische_internal.h (100%) rename src/{ => fische}/screenbuffer.cpp (100%) rename src/{ => fische}/screenbuffer.h (100%) rename src/{ => fische}/vector.cpp (100%) rename src/{ => fische}/vector.h (100%) rename src/{ => fische}/vectorfield.cpp (100%) rename src/{ => fische}/vectorfield.h (100%) rename src/{ => fische}/wavepainter.cpp (100%) rename src/{ => fische}/wavepainter.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 556b2a8..9970785 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,27 +23,27 @@ include_directories(${includes} ${GLM_INCLUDE_DIR} ${KODI_INCLUDE_DIR}/..) # Hack way with "/..", need bigger Kodi cmake rework to match right include ways (becomes done in future) -set(FISH_SOURCES src/analyst.cpp - src/audiobuffer.cpp - src/blurengine.cpp - src/cpudetect.cpp - src/fische.cpp - src/fishbmc_addon.cpp - src/screenbuffer.cpp - src/vector.cpp - src/vectorfield.cpp - src/wavepainter.cpp) - -set(FISH_HEADERS src/analyst.h - src/audiobuffer.h - src/blurengine.h - src/fische.h - src/fishbmc_addon.h - src/fische_internal.h - src/screenbuffer.h - src/vectorfield.h - src/vector.h - src/wavepainter.h) +set(FISH_SOURCES src/fishbmc_addon.cpp + src/fische/analyst.cpp + src/fische/audiobuffer.cpp + src/fische/blurengine.cpp + src/fische/cpudetect.cpp + src/fische/fische.cpp + src/fische/screenbuffer.cpp + src/fische/vector.cpp + src/fische/vectorfield.cpp + src/fische/wavepainter.cpp) + +set(FISH_HEADERS src/fishbmc_addon.h + src/fische/analyst.h + src/fische/audiobuffer.h + src/fische/blurengine.h + src/fische/fische.h + src/fische/fische_internal.h + src/fische/screenbuffer.h + src/fische/vectorfield.h + src/fische/vector.h + src/fische/wavepainter.h) build_addon(visualization.fishbmc FISH DEPLIBS) diff --git a/src/analyst.cpp b/src/fische/analyst.cpp similarity index 100% rename from src/analyst.cpp rename to src/fische/analyst.cpp diff --git a/src/analyst.h b/src/fische/analyst.h similarity index 100% rename from src/analyst.h rename to src/fische/analyst.h diff --git a/src/audiobuffer.cpp b/src/fische/audiobuffer.cpp similarity index 100% rename from src/audiobuffer.cpp rename to src/fische/audiobuffer.cpp diff --git a/src/audiobuffer.h b/src/fische/audiobuffer.h similarity index 100% rename from src/audiobuffer.h rename to src/fische/audiobuffer.h diff --git a/src/blurengine.cpp b/src/fische/blurengine.cpp similarity index 100% rename from src/blurengine.cpp rename to src/fische/blurengine.cpp diff --git a/src/blurengine.h b/src/fische/blurengine.h similarity index 100% rename from src/blurengine.h rename to src/fische/blurengine.h diff --git a/src/cpudetect.cpp b/src/fische/cpudetect.cpp similarity index 100% rename from src/cpudetect.cpp rename to src/fische/cpudetect.cpp diff --git a/src/fische.cpp b/src/fische/fische.cpp similarity index 100% rename from src/fische.cpp rename to src/fische/fische.cpp diff --git a/src/fische.h b/src/fische/fische.h similarity index 100% rename from src/fische.h rename to src/fische/fische.h diff --git a/src/fische_internal.h b/src/fische/fische_internal.h similarity index 100% rename from src/fische_internal.h rename to src/fische/fische_internal.h diff --git a/src/screenbuffer.cpp b/src/fische/screenbuffer.cpp similarity index 100% rename from src/screenbuffer.cpp rename to src/fische/screenbuffer.cpp diff --git a/src/screenbuffer.h b/src/fische/screenbuffer.h similarity index 100% rename from src/screenbuffer.h rename to src/fische/screenbuffer.h diff --git a/src/vector.cpp b/src/fische/vector.cpp similarity index 100% rename from src/vector.cpp rename to src/fische/vector.cpp diff --git a/src/vector.h b/src/fische/vector.h similarity index 100% rename from src/vector.h rename to src/fische/vector.h diff --git a/src/vectorfield.cpp b/src/fische/vectorfield.cpp similarity index 100% rename from src/vectorfield.cpp rename to src/fische/vectorfield.cpp diff --git a/src/vectorfield.h b/src/fische/vectorfield.h similarity index 100% rename from src/vectorfield.h rename to src/fische/vectorfield.h diff --git a/src/wavepainter.cpp b/src/fische/wavepainter.cpp similarity index 100% rename from src/wavepainter.cpp rename to src/fische/wavepainter.cpp diff --git a/src/wavepainter.h b/src/fische/wavepainter.h similarity index 100% rename from src/wavepainter.h rename to src/fische/wavepainter.h diff --git a/src/fishbmc_addon.cpp b/src/fishbmc_addon.cpp index 8eddb8e..f01a3b5 100644 --- a/src/fishbmc_addon.cpp +++ b/src/fishbmc_addon.cpp @@ -8,7 +8,7 @@ #include "fishbmc_addon.h" -#include "fische_internal.h" +#include "fische/fische_internal.h" #include #include diff --git a/src/fishbmc_addon.h b/src/fishbmc_addon.h index 767bfc6..59374f8 100644 --- a/src/fishbmc_addon.h +++ b/src/fishbmc_addon.h @@ -8,7 +8,7 @@ #pragma once -#include "fische.h" +#include "fische/fische.h" #include #include From 4ecd331eb7a23a8f8463121bb4a226e265c11b83 Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Fri, 15 May 2026 03:59:48 +0200 Subject: [PATCH 03/13] update copyright year to 2026 --- debian/copyright | 2 +- src/fische/analyst.cpp | 2 +- src/fische/analyst.h | 2 +- src/fische/audiobuffer.cpp | 2 +- src/fische/audiobuffer.h | 2 +- src/fische/blurengine.cpp | 2 +- src/fische/blurengine.h | 2 +- src/fische/cpudetect.cpp | 2 +- src/fische/fische.cpp | 2 +- src/fische/fische.h | 2 +- src/fische/fische_internal.h | 2 +- src/fische/screenbuffer.cpp | 2 +- src/fische/screenbuffer.h | 2 +- src/fische/vector.cpp | 2 +- src/fische/vector.h | 2 +- src/fische/vectorfield.cpp | 2 +- src/fische/vectorfield.h | 2 +- src/fische/wavepainter.cpp | 2 +- src/fische/wavepainter.h | 2 +- src/fishbmc_addon.cpp | 2 +- src/fishbmc_addon.h | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/debian/copyright b/debian/copyright index 801b24d..9157092 100644 --- a/debian/copyright +++ b/debian/copyright @@ -2,7 +2,7 @@ Format: http://dep.debian.net/deps/dep5 Upstream-Name: visualization.fishbmc Files: * -Copyright: 2005-2022 Team Kodi +Copyright: 2005-2026 Team Kodi License: GPL-2+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/fische/analyst.cpp b/src/fische/analyst.cpp index c26590d..ea5b174 100644 --- a/src/fische/analyst.cpp +++ b/src/fische/analyst.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/analyst.h b/src/fische/analyst.h index 395c5e3..d1ae560 100644 --- a/src/fische/analyst.h +++ b/src/fische/analyst.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/audiobuffer.cpp b/src/fische/audiobuffer.cpp index 9725772..5e9ac50 100644 --- a/src/fische/audiobuffer.cpp +++ b/src/fische/audiobuffer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/audiobuffer.h b/src/fische/audiobuffer.h index b349d86..fea2bbf 100644 --- a/src/fische/audiobuffer.h +++ b/src/fische/audiobuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/blurengine.cpp b/src/fische/blurengine.cpp index 20bfb5b..9cc2c86 100644 --- a/src/fische/blurengine.cpp +++ b/src/fische/blurengine.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/blurengine.h b/src/fische/blurengine.h index b803e03..cb0d6bf 100644 --- a/src/fische/blurengine.h +++ b/src/fische/blurengine.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/cpudetect.cpp b/src/fische/cpudetect.cpp index 48cb7d2..d5bcb37 100644 --- a/src/fische/cpudetect.cpp +++ b/src/fische/cpudetect.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/fische.cpp b/src/fische/fische.cpp index 522643b..366fa3a 100644 --- a/src/fische/fische.cpp +++ b/src/fische/fische.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/fische.h b/src/fische/fische.h index 5979da2..c9e6c11 100644 --- a/src/fische/fische.h +++ b/src/fische/fische.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/fische_internal.h b/src/fische/fische_internal.h index 8b59a96..83a102f 100644 --- a/src/fische/fische_internal.h +++ b/src/fische/fische_internal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/screenbuffer.cpp b/src/fische/screenbuffer.cpp index 9afee42..580c422 100644 --- a/src/fische/screenbuffer.cpp +++ b/src/fische/screenbuffer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/screenbuffer.h b/src/fische/screenbuffer.h index 025643b..044a0b6 100644 --- a/src/fische/screenbuffer.h +++ b/src/fische/screenbuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/vector.cpp b/src/fische/vector.cpp index e9a7e7b..0000564 100644 --- a/src/fische/vector.cpp +++ b/src/fische/vector.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/vector.h b/src/fische/vector.h index 9fad448..6b0de56 100644 --- a/src/fische/vector.h +++ b/src/fische/vector.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/vectorfield.cpp b/src/fische/vectorfield.cpp index de4795e..708525a 100644 --- a/src/fische/vectorfield.cpp +++ b/src/fische/vectorfield.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/vectorfield.h b/src/fische/vectorfield.h index 60d3fe5..b423e1d 100644 --- a/src/fische/vectorfield.h +++ b/src/fische/vectorfield.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/wavepainter.cpp b/src/fische/wavepainter.cpp index a36b01a..20c55e5 100644 --- a/src/fische/wavepainter.cpp +++ b/src/fische/wavepainter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fische/wavepainter.h b/src/fische/wavepainter.h index 8a5c861..d7533e6 100644 --- a/src/fische/wavepainter.h +++ b/src/fische/wavepainter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fishbmc_addon.cpp b/src/fishbmc_addon.cpp index f01a3b5..b16e427 100644 --- a/src/fishbmc_addon.cpp +++ b/src/fishbmc_addon.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/fishbmc_addon.h b/src/fishbmc_addon.h index 59374f8..ef4a32f 100644 --- a/src/fishbmc_addon.h +++ b/src/fishbmc_addon.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) * Copyright (C) 2012 Marcel Ebmer * * SPDX-License-Identifier: GPL-2.0-or-later From d520628c06c684b8767efeb6f7563a40d469860b Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Fri, 15 May 2026 04:10:39 +0200 Subject: [PATCH 04/13] use cpudetect as "C" code only As this goes with ASM deep in hardware, makes it sense for me to leave as "C" and not as "C++". From my side hold the code there as small as possible. Further is it independent to all other places. --- CMakeLists.txt | 3 ++- src/fische/{cpudetect.cpp => cpudetect.c} | 3 +-- src/fische/cpudetect.h | 16 ++++++++++++++++ src/fische/fische.cpp | 1 + src/fische/fische_internal.h | 3 --- 5 files changed, 20 insertions(+), 6 deletions(-) rename src/fische/{cpudetect.cpp => cpudetect.c} (98%) create mode 100644 src/fische/cpudetect.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9970785..64b3468 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ set(FISH_SOURCES src/fishbmc_addon.cpp src/fische/analyst.cpp src/fische/audiobuffer.cpp src/fische/blurengine.cpp - src/fische/cpudetect.cpp + src/fische/cpudetect.c src/fische/fische.cpp src/fische/screenbuffer.cpp src/fische/vector.cpp @@ -38,6 +38,7 @@ set(FISH_HEADERS src/fishbmc_addon.h src/fische/analyst.h src/fische/audiobuffer.h src/fische/blurengine.h + src/fische/cpudetect.h src/fische/fische.h src/fische/fische_internal.h src/fische/screenbuffer.h diff --git a/src/fische/cpudetect.cpp b/src/fische/cpudetect.c similarity index 98% rename from src/fische/cpudetect.cpp rename to src/fische/cpudetect.c index d5bcb37..e8728dd 100644 --- a/src/fische/cpudetect.cpp +++ b/src/fische/cpudetect.c @@ -6,8 +6,7 @@ * See LICENSE.md for more information. */ -#include "fische_internal.h" - +#include #include #if defined(__x86_64__) diff --git a/src/fische/cpudetect.h b/src/fische/cpudetect.h new file mode 100644 index 0000000..89dc30c --- /dev/null +++ b/src/fische/cpudetect.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) + * Copyright (C) 2012 Marcel Ebmer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#ifndef CPUDETECT_H +#define CPUDETECT_H + +#include + +extern "C" uint_fast8_t _fische__cpu_detect_(); + +#endif /* CPUDETECT_H */ diff --git a/src/fische/fische.cpp b/src/fische/fische.cpp index 366fa3a..adc0519 100644 --- a/src/fische/fische.cpp +++ b/src/fische/fische.cpp @@ -6,6 +6,7 @@ * See LICENSE.md for more information. */ +#include "cpudetect.h" #include "fische_internal.h" #include diff --git a/src/fische/fische_internal.h b/src/fische/fische_internal.h index 83a102f..68394d5 100644 --- a/src/fische/fische_internal.h +++ b/src/fische/fische_internal.h @@ -27,9 +27,6 @@ #define FISCHE_PRIVATE(P) ((struct _fische__internal_*)P->fische->priv) -uint_fast8_t _fische__cpu_detect_(); - - struct _fische__internal_ { struct fische__screenbuffer* screenbuffer; From 542d80b09a5814cd0db5a5c727e4833dd0617e97 Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Fri, 15 May 2026 04:19:58 +0200 Subject: [PATCH 05/13] rename some fische header to have ".hpp" as end This done to have better usable in coming changes. As the fische code by add-on was on begin defined as a "C" code only and over the year some "C++" parts implemented to them. About this and to become a usable "C" ABI support back to them are the headers renamed and to identify in furture which relates to "C" and whicht to "C++". --- CMakeLists.txt | 14 +++++++------- src/fische/{analyst.h => analyst.hpp} | 0 src/fische/{audiobuffer.h => audiobuffer.hpp} | 0 src/fische/{blurengine.h => blurengine.hpp} | 0 src/fische/fische_internal.h | 14 +++++++------- src/fische/{screenbuffer.h => screenbuffer.hpp} | 0 src/fische/vector.cpp | 2 +- src/fische/{vector.h => vector.hpp} | 0 src/fische/{vectorfield.h => vectorfield.hpp} | 0 src/fische/{wavepainter.h => wavepainter.hpp} | 0 10 files changed, 15 insertions(+), 15 deletions(-) rename src/fische/{analyst.h => analyst.hpp} (100%) rename src/fische/{audiobuffer.h => audiobuffer.hpp} (100%) rename src/fische/{blurengine.h => blurengine.hpp} (100%) rename src/fische/{screenbuffer.h => screenbuffer.hpp} (100%) rename src/fische/{vector.h => vector.hpp} (100%) rename src/fische/{vectorfield.h => vectorfield.hpp} (100%) rename src/fische/{wavepainter.h => wavepainter.hpp} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64b3468..392241b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,16 +35,16 @@ set(FISH_SOURCES src/fishbmc_addon.cpp src/fische/wavepainter.cpp) set(FISH_HEADERS src/fishbmc_addon.h - src/fische/analyst.h - src/fische/audiobuffer.h - src/fische/blurengine.h + src/fische/analyst.hpp + src/fische/audiobuffer.hpp + src/fische/blurengine.hpp src/fische/cpudetect.h src/fische/fische.h src/fische/fische_internal.h - src/fische/screenbuffer.h - src/fische/vectorfield.h - src/fische/vector.h - src/fische/wavepainter.h) + src/fische/screenbuffer.hpp + src/fische/vectorfield.hpp + src/fische/vector.hpp + src/fische/wavepainter.hpp) build_addon(visualization.fishbmc FISH DEPLIBS) diff --git a/src/fische/analyst.h b/src/fische/analyst.hpp similarity index 100% rename from src/fische/analyst.h rename to src/fische/analyst.hpp diff --git a/src/fische/audiobuffer.h b/src/fische/audiobuffer.hpp similarity index 100% rename from src/fische/audiobuffer.h rename to src/fische/audiobuffer.hpp diff --git a/src/fische/blurengine.h b/src/fische/blurengine.hpp similarity index 100% rename from src/fische/blurengine.h rename to src/fische/blurengine.hpp diff --git a/src/fische/fische_internal.h b/src/fische/fische_internal.h index 68394d5..6df798c 100644 --- a/src/fische/fische_internal.h +++ b/src/fische/fische_internal.h @@ -9,14 +9,14 @@ #ifndef FISCHE_INTERNAL_H #define FISCHE_INTERNAL_H -#include "analyst.h" -#include "audiobuffer.h" -#include "blurengine.h" +#include "analyst.hpp" +#include "audiobuffer.hpp" +#include "blurengine.hpp" #include "fische.h" -#include "screenbuffer.h" -#include "vector.h" -#include "vectorfield.h" -#include "wavepainter.h" +#include "screenbuffer.hpp" +#include "vector.hpp" +#include "vectorfield.hpp" +#include "wavepainter.hpp" #ifdef WIN32 #ifndef M_PI diff --git a/src/fische/screenbuffer.h b/src/fische/screenbuffer.hpp similarity index 100% rename from src/fische/screenbuffer.h rename to src/fische/screenbuffer.hpp diff --git a/src/fische/vector.cpp b/src/fische/vector.cpp index 0000564..a187276 100644 --- a/src/fische/vector.cpp +++ b/src/fische/vector.cpp @@ -6,7 +6,7 @@ * See LICENSE.md for more information. */ -#include "vector.h" +#include "vector.hpp" double fische__vector_length(fische__vector* self) { diff --git a/src/fische/vector.h b/src/fische/vector.hpp similarity index 100% rename from src/fische/vector.h rename to src/fische/vector.hpp diff --git a/src/fische/vectorfield.h b/src/fische/vectorfield.hpp similarity index 100% rename from src/fische/vectorfield.h rename to src/fische/vectorfield.hpp diff --git a/src/fische/wavepainter.h b/src/fische/wavepainter.hpp similarity index 100% rename from src/fische/wavepainter.h rename to src/fische/wavepainter.hpp From 127296f1f01072da5d52f4e018400d989dadd019 Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Fri, 15 May 2026 04:28:52 +0200 Subject: [PATCH 06/13] use "#pragma once" in fische headers where use C++ code into them To makes it a bit cleaner and not have the #ifdef and #endif. --- src/fische/analyst.hpp | 5 +---- src/fische/audiobuffer.hpp | 5 +---- src/fische/blurengine.hpp | 5 +---- src/fische/screenbuffer.hpp | 5 +---- src/fische/vector.hpp | 5 +---- src/fische/vectorfield.hpp | 5 +---- src/fische/wavepainter.hpp | 5 +---- 7 files changed, 7 insertions(+), 28 deletions(-) diff --git a/src/fische/analyst.hpp b/src/fische/analyst.hpp index d1ae560..c6eb4dc 100644 --- a/src/fische/analyst.hpp +++ b/src/fische/analyst.hpp @@ -6,8 +6,7 @@ * See LICENSE.md for more information. */ -#ifndef ANALYST_H -#define ANALYST_H +#pragma once #include @@ -44,5 +43,3 @@ struct fische__analyst struct _fische__analyst_* priv; }; - -#endif diff --git a/src/fische/audiobuffer.hpp b/src/fische/audiobuffer.hpp index fea2bbf..2b2965d 100644 --- a/src/fische/audiobuffer.hpp +++ b/src/fische/audiobuffer.hpp @@ -6,8 +6,7 @@ * See LICENSE.md for more information. */ -#ifndef AUDIOBUFFER_H -#define AUDIOBUFFER_H +#pragma once #include @@ -49,5 +48,3 @@ struct fische__audiobuffer struct _fische__audiobuffer_* priv; }; - -#endif diff --git a/src/fische/blurengine.hpp b/src/fische/blurengine.hpp index cb0d6bf..7f91f88 100644 --- a/src/fische/blurengine.hpp +++ b/src/fische/blurengine.hpp @@ -6,8 +6,7 @@ * See LICENSE.md for more information. */ -#ifndef BLURENGINE_H -#define BLURENGINE_H +#pragma once #include #include @@ -56,5 +55,3 @@ struct fische__blurengine { struct _fische__blurengine_* priv; }; - -#endif diff --git a/src/fische/screenbuffer.hpp b/src/fische/screenbuffer.hpp index 044a0b6..22e3c76 100644 --- a/src/fische/screenbuffer.hpp +++ b/src/fische/screenbuffer.hpp @@ -6,8 +6,7 @@ * See LICENSE.md for more information. */ -#ifndef SCREENBUFFER_H -#define SCREENBUFFER_H +#pragma once #include @@ -49,5 +48,3 @@ struct fische__screenbuffer struct _fische__screenbuffer_* priv; }; - -#endif diff --git a/src/fische/vector.hpp b/src/fische/vector.hpp index 6b0de56..be2e5a2 100644 --- a/src/fische/vector.hpp +++ b/src/fische/vector.hpp @@ -6,8 +6,7 @@ * See LICENSE.md for more information. */ -#ifndef F_VECTOR_H -#define F_VECTOR_H +#pragma once #include #include @@ -43,5 +42,3 @@ fische__vector fische__vector_intersect_border(fische__vector* self, uint_fast16_t width, uint_fast16_t height, int_fast8_t direction); - -#endif diff --git a/src/fische/vectorfield.hpp b/src/fische/vectorfield.hpp index b423e1d..d5cf454 100644 --- a/src/fische/vectorfield.hpp +++ b/src/fische/vectorfield.hpp @@ -6,8 +6,7 @@ * See LICENSE.md for more information. */ -#ifndef VECTORFIELD_H -#define VECTORFIELD_H +#pragma once #include @@ -47,5 +46,3 @@ struct fische__vectorfield struct _fische__vectorfield_* priv; }; - -#endif diff --git a/src/fische/wavepainter.hpp b/src/fische/wavepainter.hpp index d7533e6..bdf239b 100644 --- a/src/fische/wavepainter.hpp +++ b/src/fische/wavepainter.hpp @@ -6,8 +6,7 @@ * See LICENSE.md for more information. */ -#ifndef WAVEPAINTER_H -#define WAVEPAINTER_H +#pragma once #include @@ -47,5 +46,3 @@ struct fische__wavepainter { struct _fische__wavepainter_* priv; }; - -#endif From dbf4119649073aadad9442438024262f40fdad9c Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Mon, 18 May 2026 13:14:06 +0200 Subject: [PATCH 07/13] increase cmake_minimum version to 3.18 Relates to: ``` CMake Deprecation Warning at CMakeLists.txt:2 (cmake_policy): Compatibility with CMake < 3.10 will be removed from a future version of CMake. ``` And to have equal to Kodi's used version. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 392241b..fe7369c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.18) project(visualization.fishbmc) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}) From b19c43041982b75d48a41ce85e1d95a3e300e03b Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Fri, 15 May 2026 04:52:31 +0200 Subject: [PATCH 08/13] rework fish to become mostly "C++" based Some parts in mathematic processes are leaved on the old "C" way. Other parts are in C++ now, reason why done is to have more OS independent and more clean in Code. Seen that fische in his Version 3 was based complete upon C++, but done here from scratch without code take from his old version. See here version 3: https://sources.debian.org/src/fische/3.2.2-8/src And here version 4: https://github.com/maysl/fische --- CMakeLists.txt | 4 +- src/fische/analyst.cpp | 172 +++++-------- src/fische/analyst.hpp | 73 +++--- src/fische/audiobuffer.cpp | 147 +++++------ src/fische/audiobuffer.hpp | 68 ++--- src/fische/blurengine.cpp | 205 +++++++-------- src/fische/blurengine.hpp | 77 +++--- src/fische/fische.cpp | 465 ++++++++++++++++++++--------------- src/fische/fische.h | 134 ++++------ src/fische/fische.hpp | 117 +++++++++ src/fische/fische_internal.h | 43 ---- src/fische/screenbuffer.cpp | 161 +++++------- src/fische/screenbuffer.hpp | 70 +++--- src/fische/vector.cpp | 63 ++--- src/fische/vector.hpp | 51 ++-- src/fische/vectorfield.cpp | 412 ++++++++++++++----------------- src/fische/vectorfield.hpp | 71 +++--- src/fische/wavepainter.cpp | 307 +++++++++++------------ src/fische/wavepainter.hpp | 65 ++--- src/fishbmc_addon.cpp | 129 ++++------ src/fishbmc_addon.h | 17 +- 21 files changed, 1391 insertions(+), 1460 deletions(-) create mode 100644 src/fische/fische.hpp delete mode 100644 src/fische/fische_internal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fe7369c..f9053c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,10 +40,10 @@ set(FISH_HEADERS src/fishbmc_addon.h src/fische/blurengine.hpp src/fische/cpudetect.h src/fische/fische.h - src/fische/fische_internal.h + src/fische/fische.hpp src/fische/screenbuffer.hpp - src/fische/vectorfield.hpp src/fische/vector.hpp + src/fische/vectorfield.hpp src/fische/wavepainter.hpp) build_addon(visualization.fishbmc FISH DEPLIBS) diff --git a/src/fische/analyst.cpp b/src/fische/analyst.cpp index ea5b174..c027126 100644 --- a/src/fische/analyst.cpp +++ b/src/fische/analyst.cpp @@ -6,42 +6,39 @@ * See LICENSE.md for more information. */ -#include "fische_internal.h" +#include "analyst.hpp" -#include -#include +#include "fische.hpp" -#ifdef DEBUG -#include -#endif +#include +#include -enum +namespace fische { - _FISCHE__WAITING_, - _FISCHE__MAYBEWAITING_, - _FISCHE__BEAT_ -}; -int _fische__compare_int_fast16_t_(void const* value1, void const* value2) +CAnalyst::CAnalyst(const CFische* parent) : m_fische(parent) +{ +} + +int CAnalyst::CompareInt(void const* value1, void const* value2) { return (*(int_fast16_t*)value1 - *(int_fast16_t*)value2); } -double _fische__guess_frames_per_beat_(uint_fast16_t* beat_gap_history) +double CAnalyst::GuessFramesPerBeat() { - uint_fast16_t gap_history_sorted[30]; + uint_fast16_t gap_history_sorted[BEAT_GAP_HISTORY_SIZE]; - memcpy(gap_history_sorted, beat_gap_history, 30 * sizeof(uint_fast16_t)); - qsort(gap_history_sorted, 30, sizeof(uint_fast16_t), _fische__compare_int_fast16_t_); + memcpy(gap_history_sorted, m_beat_gap_history.data(), sizeof(m_beat_gap_history)); + qsort(gap_history_sorted, BEAT_GAP_HISTORY_SIZE, sizeof(uint_fast16_t), CompareInt); uint_fast16_t guess = gap_history_sorted[14]; - double result = 0; + double result = 0.0; int count = 0; - uint_fast8_t i; uint16_t value; - for (i = 0; i < 30; ++i) + for (size_t i = 0; i < 30; ++i) { value = gap_history_sorted[i] - guess; if (abs(value) <= 2) @@ -54,12 +51,11 @@ double _fische__guess_frames_per_beat_(uint_fast16_t* beat_gap_history) return result / count; } -double _fische__get_audio_level_(double* data, uint_fast32_t data_size) +double CAnalyst::GetAudioLevel(const double* data, size_t data_size) { - double E = 0; + double E = 0.0; - uint_fast32_t i; - for (i = 0; i < data_size; ++i) + for (size_t i = 0; i < data_size; ++i) { E += fabs(*(data + i)); } @@ -71,142 +67,104 @@ double _fische__get_audio_level_(double* data, uint_fast32_t data_size) return log10(E) * 10; } -struct fische__analyst* fische__analyst_new(struct fische* parent) -{ - - struct fische__analyst* retval = - static_cast(malloc(sizeof(struct fische__analyst))); - retval->priv = static_cast<_fische__analyst_*>(malloc(sizeof(struct _fische__analyst_))); - - struct _fische__analyst_* P = retval->priv; - - P->fische = parent; - P->bghist_head = 0; - P->intensity_moving_avg = 0; - P->intensity_std_dev = 0; - P->last_beat_frame = 0; - P->moving_avg_03 = 0; - P->moving_avg_30 = 0; - P->state = _FISCHE__WAITING_; - P->std_dev = 0; - - P->beat_gap_history = static_cast(malloc(30 * sizeof(uint_fast16_t))); - memset(P->beat_gap_history, '\0', 30 * sizeof(uint_fast16_t)); - - retval->frames_per_beat = 0; - retval->relative_energy = 1; - - return retval; -} - -void fische__analyst_free(struct fische__analyst* self) -{ - if (!self) - return; - - free(self->priv->beat_gap_history); - free(self->priv); - free(self); -} - -int_fast8_t fische__analyst_analyse(struct fische__analyst* self, double* data, uint_fast16_t size) +int_fast8_t CAnalyst::Analyse(const double* data, size_t size) { if (!size) return -1; - struct _fische__analyst_* P = self->priv; + const double dezibel{GetAudioLevel(data, size * 2)}; - double dezibel = _fische__get_audio_level_(data, size * 2); - - if (P->moving_avg_30 == 0) - P->moving_avg_30 = dezibel; + if (m_moving_avg_30 == 0) + m_moving_avg_30 = dezibel; else - P->moving_avg_30 = P->moving_avg_30 * 0.9667 + dezibel * 0.0333; + m_moving_avg_30 = m_moving_avg_30 * 0.9667 + dezibel * 0.0333; - P->std_dev = P->std_dev * 0.9667 + fabs(dezibel - P->moving_avg_30) * 0.0333; + m_std_dev = m_std_dev * 0.9667 + fabs(dezibel - m_moving_avg_30) * 0.0333; - uint_fast32_t frameno = P->fische->frame_counter; - if ((frameno - P->last_beat_frame) > 90) + const uint_fast32_t frameno = m_fische->GetFrameCounter(); + if ((frameno - m_last_beat_frame) > 90) { - self->frames_per_beat = 0; - memset(P->beat_gap_history, '\0', 30 * sizeof(uint_fast16_t)); - P->bghist_head = 0; + m_frames_per_beat = 0; + m_beat_gap_history.fill('\0'); + m_bghist_head = 0; } - self->relative_energy = P->moving_avg_03 / P->moving_avg_30; + m_relative_energy = m_moving_avg_03 / m_moving_avg_30; - double relative_intensity = 0; + double relative_intensity = 0.0; double new_frames_per_beat; - switch (P->state) + switch (m_state) { - case _FISCHE__WAITING_: + case WAITING: // don't bother if intensity too low - if (dezibel < P->moving_avg_30 + P->std_dev) + if (dezibel < m_moving_avg_30 + m_std_dev) break; // initialisation fallbacks - if (P->std_dev == 0) - relative_intensity = 1; // avoid div by 0 + if (m_std_dev == 0) + relative_intensity = 1.0; // avoid div by 0 else - relative_intensity = (dezibel - P->moving_avg_30) / P->std_dev; + relative_intensity = (dezibel - m_moving_avg_30) / m_std_dev; - if (P->intensity_moving_avg == 0) - P->intensity_moving_avg = relative_intensity; // initial assignment + if (m_intensity_moving_avg == 0) + m_intensity_moving_avg = relative_intensity; // initial assignment else - P->intensity_moving_avg = P->intensity_moving_avg * 0.95 + relative_intensity * 0.05; + m_intensity_moving_avg = m_intensity_moving_avg * 0.95 + relative_intensity * 0.05; // update intensity standard deviation - P->intensity_std_dev = - P->intensity_std_dev * 0.95 + fabs(P->intensity_moving_avg - relative_intensity) * 0.05; + m_intensity_std_dev = + m_intensity_std_dev * 0.95 + fabs(m_intensity_moving_avg - relative_intensity) * 0.05; // we DO have a beat - P->state = _FISCHE__BEAT_; + m_state = FISCHE_BEAT; // update beat gap history - P->beat_gap_history[P->bghist_head++] = frameno - P->last_beat_frame; - if (P->bghist_head == 30) - P->bghist_head = 0; + m_beat_gap_history[m_bghist_head++] = frameno - m_last_beat_frame; + if (m_bghist_head == BEAT_GAP_HISTORY_SIZE) + m_bghist_head = 0; // remember this as the last beat - P->last_beat_frame = frameno; + m_last_beat_frame = frameno; // reset the short-term moving average - P->moving_avg_03 = dezibel; + m_moving_avg_03 = dezibel; // try a guess at the tempo - new_frames_per_beat = _fische__guess_frames_per_beat_(P->beat_gap_history); - if ((self->frames_per_beat) && (self->frames_per_beat / new_frames_per_beat < 1.2) && - (new_frames_per_beat / self->frames_per_beat < 1.2)) - self->frames_per_beat = (self->frames_per_beat * 2 + new_frames_per_beat) / 3; + new_frames_per_beat = GuessFramesPerBeat(); + if ((m_frames_per_beat) && (m_frames_per_beat / new_frames_per_beat < 1.2) && + (new_frames_per_beat / m_frames_per_beat < 1.2)) + m_frames_per_beat = (m_frames_per_beat * 2.0 + new_frames_per_beat) / 3.0; else - self->frames_per_beat = new_frames_per_beat; + m_frames_per_beat = new_frames_per_beat; // return based on relative beat intensity - if (relative_intensity > P->intensity_moving_avg + 3 * P->intensity_std_dev) + if (relative_intensity > m_intensity_moving_avg + 3 * m_intensity_std_dev) return 4; - if (relative_intensity > P->intensity_moving_avg + 2 * P->intensity_std_dev) + if (relative_intensity > m_intensity_moving_avg + 2 * m_intensity_std_dev) return 3; - if (relative_intensity > P->intensity_moving_avg + 1 * P->intensity_std_dev) + if (relative_intensity > m_intensity_moving_avg + 1 * m_intensity_std_dev) return 2; return 1; - case _FISCHE__BEAT_: - case _FISCHE__MAYBEWAITING_: + case FISCHE_BEAT: + case MAYBEWAITING: // update short term moving average - P->moving_avg_03 = P->moving_avg_03 * 0.6667 + dezibel * 0.3333; + m_moving_avg_03 = m_moving_avg_03 * 0.6667 + dezibel * 0.3333; // needs to be low enough twice to exit BEAT state - if (P->moving_avg_03 < P->moving_avg_30 + P->std_dev) + if (m_moving_avg_03 < m_moving_avg_30 + m_std_dev) { - P->state = P->state == _FISCHE__MAYBEWAITING_ ? _FISCHE__WAITING_ : _FISCHE__MAYBEWAITING_; + m_state = m_state == MAYBEWAITING ? WAITING : MAYBEWAITING; return 0; } } // report level too low - if (dezibel < -45) + if (dezibel < -45.0) return -1; return 0; } + +} // namespace fische diff --git a/src/fische/analyst.hpp b/src/fische/analyst.hpp index c6eb4dc..2a4b0a5 100644 --- a/src/fische/analyst.hpp +++ b/src/fische/analyst.hpp @@ -8,38 +8,53 @@ #pragma once -#include +#include +#include +#include -struct fische; -struct _fische__analyst_; -struct fische__analyst; - - -struct fische__analyst* fische__analyst_new(struct fische* parent); -void fische__analyst_free(struct fische__analyst* self); - -int_fast8_t fische__analyst_analyse(struct fische__analyst* self, double* data, uint_fast16_t size); +namespace fische +{ +class CFische; -struct _fische__analyst_ +class CAnalyst { - uint_fast8_t state; - double moving_avg_30; - double moving_avg_03; - double std_dev; - double intensity_moving_avg; - double intensity_std_dev; - uint_fast32_t last_beat_frame; - uint_fast16_t* beat_gap_history; - uint_fast8_t bghist_head; - - struct fische* fische; +public: + CAnalyst(const CFische* parent); + ~CAnalyst() = default; + + int_fast8_t Analyse(const double* data, size_t size); + inline double GetRelativeEnergy() const { return m_relative_energy; } + inline double GetFramesPerBeat() const { return m_frames_per_beat; } + +private: + const CFische* m_fische; + + enum STATE + { + WAITING, + MAYBEWAITING, + FISCHE_BEAT + }; + + static const uint_fast8_t BEAT_GAP_HISTORY_SIZE{30}; + + static int CompareInt(void const* value1, void const* value2); + double GuessFramesPerBeat(); + double GetAudioLevel(const double* data, size_t data_size); + + double m_relative_energy{1.0}; + double m_frames_per_beat{0.0}; + + STATE m_state{WAITING}; + double m_moving_avg_30{0.0}; + double m_moving_avg_03{0.0}; + double m_std_dev{0.0}; + double m_intensity_moving_avg{0.0}; + double m_intensity_std_dev{0.0}; + uint_fast32_t m_last_beat_frame{0}; + std::array m_beat_gap_history{'\0'}; + uint_fast8_t m_bghist_head{0}; }; -struct fische__analyst -{ - double relative_energy; - double frames_per_beat; - - struct _fische__analyst_* priv; -}; +} // namespace fische diff --git a/src/fische/audiobuffer.cpp b/src/fische/audiobuffer.cpp index 5e9ac50..4525724 100644 --- a/src/fische/audiobuffer.cpp +++ b/src/fische/audiobuffer.cpp @@ -6,66 +6,36 @@ * See LICENSE.md for more information. */ -#include "fische_internal.h" +#include "audiobuffer.hpp" #include -#include -#include +#include +#include +#include #include -#ifdef DEBUG -#include -#endif - -struct fische__audiobuffer* fische__audiobuffer_new(struct fische* parent) +namespace fische { - struct fische__audiobuffer* retval = - static_cast(malloc(sizeof(struct fische__audiobuffer))); - retval->priv = static_cast<_fische__audiobuffer_*>(malloc(sizeof(struct _fische__audiobuffer_))); - struct _fische__audiobuffer_* P = retval->priv; - - P->fische = parent; - P->buffer = 0; - P->buffer_size = 0; - P->format = parent->audio_format; - P->is_locked = 0; - P->puts = 0; - P->gets = 0; - P->last_get = 0; - - retval->front_sample_count = 0; - retval->front_samples = 0; - retval->back_sample_count = 0; - retval->back_samples = 0; - - return retval; +CAudioBuffer::CAudioBuffer(FISCHE_AUDIOFORMAT format) : m_format(format) +{ } -void fische__audiobuffer_free(struct fische__audiobuffer* self) +CAudioBuffer::~CAudioBuffer() { - if (!self) - return; + Lock(); - fische__audiobuffer_lock(self); - - free(self->priv->buffer); - free(self->priv); - free(self); + free(m_buffer); } -void fische__audiobuffer_insert(struct fische__audiobuffer* self, - const void* data, - uint_fast32_t size) +void CAudioBuffer::Insert(const void* data, size_t size) { - struct _fische__audiobuffer_* P = self->priv; - - if (P->buffer_size > 44100) + if (m_buffer_size > 44100) return; uint_fast8_t width = 1; - switch (P->format) + switch (m_format) { case FISCHE_AUDIOFORMAT_DOUBLE: width = 8; @@ -80,113 +50,110 @@ void fische__audiobuffer_insert(struct fische__audiobuffer* self, width = 2; } - uint_fast32_t old_bufsize = P->buffer_size; - P->buffer_size += size / width; - P->buffer = static_cast(realloc(P->buffer, P->buffer_size * sizeof(double))); + const size_t old_bufsize = m_buffer_size; + m_buffer_size += size / width; + m_buffer = static_cast(realloc(m_buffer, m_buffer_size * sizeof(double))); - uint_fast32_t i; - for (i = 0; i < size / width; ++i) + for (size_t i = 0; i < size / width; ++i) { - double* dest = (P->buffer + old_bufsize + i); + double* dest = (m_buffer + old_bufsize + i); - switch (P->format) + switch (m_format) { case FISCHE_AUDIOFORMAT_FLOAT: - *dest = *((float*)data + i); + *dest = *(static_cast(data) + i); break; case FISCHE_AUDIOFORMAT_DOUBLE: - *dest = *((double*)data + i); + *dest = *(static_cast(data) + i); break; case FISCHE_AUDIOFORMAT_S32: - *dest = *((int32_t*)data + i); + *dest = *(static_cast(data) + i); *dest /= INT32_MAX; break; case FISCHE_AUDIOFORMAT_U32: - *dest = *((uint32_t*)data + i); + *dest = *(static_cast(data) + i); *dest -= INT32_MAX; *dest /= INT32_MAX; break; case FISCHE_AUDIOFORMAT_S16: - *dest = *((int16_t*)data + i); + *dest = *(static_cast(data) + i); *dest /= INT16_MAX; break; case FISCHE_AUDIOFORMAT_U16: - *dest = *((uint16_t*)data + i); + *dest = *(static_cast(data) + i); *dest -= INT16_MAX; *dest /= INT16_MAX; break; case FISCHE_AUDIOFORMAT_S8: - *dest = *((int8_t*)data + i); + *dest = *(static_cast(data) + i); *dest /= INT8_MAX; break; case FISCHE_AUDIOFORMAT_U8: - *dest = *((uint8_t*)data + i); + *dest = *(static_cast(data) + i); *dest /= INT8_MAX; *dest /= INT8_MAX; break; } } - ++P->puts; + ++m_puts; } -void fische__audiobuffer_get(struct fische__audiobuffer* self) +void CAudioBuffer::Get() { - struct _fische__audiobuffer_* P = self->priv; - - if (P->buffer_size == 0) + if (m_buffer_size == 0) return; - double* new_start = P->buffer + P->last_get * 2; - P->buffer_size -= P->last_get * 2; + double* new_start = m_buffer + m_last_get * 2; + m_buffer_size -= m_last_get * 2; // pop used data off front - memmove(P->buffer, new_start, P->buffer_size * sizeof(double)); - P->buffer = static_cast(realloc(P->buffer, P->buffer_size * sizeof(double))); + memmove(m_buffer, new_start, m_buffer_size * sizeof(double)); + m_buffer = static_cast(realloc(m_buffer, m_buffer_size * sizeof(double))); - if (!P->puts) + if (!m_puts) return; // fallback for first get - if (P->gets == 0) + if (m_gets == 0) { - P->gets = 3; - P->puts = 1; + m_gets = 3; + m_puts = 1; } // get/put ratio - double d_ratio = ((double)P->gets) / P->puts; - uint_fast8_t ratio = ceil(d_ratio); + double d_ratio = static_cast(m_gets) / m_puts; + uint_fast8_t ratio = static_cast(ceil(d_ratio)); // how many samples to return - uint_fast32_t n_samples = P->buffer_size / 2 / ratio; + size_t n_samples = m_buffer_size / 2 / ratio; // set return data size and remember - self->front_sample_count = n_samples; - self->back_sample_count = n_samples; - P->last_get = n_samples; + m_front_sample_count = n_samples; + m_back_sample_count = n_samples; + m_last_get = n_samples; // set export buffer - self->front_samples = P->buffer; - self->back_samples = P->buffer + P->buffer_size - n_samples * 2; + m_front_samples = m_buffer; + m_back_samples = m_buffer + m_buffer_size - n_samples * 2; // increment get counter - ++P->gets; + ++m_gets; } -void fische__audiobuffer_lock(struct fische__audiobuffer* self) +void CAudioBuffer::Lock() { -#ifdef __GNUC__ - while (!__sync_bool_compare_and_swap(&self->priv->is_locked, 0, 1)) - std::this_thread::sleep_for(std::chrono::microseconds(1)); -#else - while (self->priv->is_locked) + bool expected = false; + while (m_is_locked.compare_exchange_strong(expected, true) == false) + { + expected = false; std::this_thread::sleep_for(std::chrono::microseconds(1)); - self->priv->is_locked = 1; -#endif + } } -void fische__audiobuffer_unlock(struct fische__audiobuffer* self) +void CAudioBuffer::Unlock() { - self->priv->is_locked = 0; + m_is_locked = false; } + +} // namespace fische diff --git a/src/fische/audiobuffer.hpp b/src/fische/audiobuffer.hpp index 2b2965d..aba3a12 100644 --- a/src/fische/audiobuffer.hpp +++ b/src/fische/audiobuffer.hpp @@ -8,43 +8,49 @@ #pragma once -#include +#include "fische.h" -struct fische; -struct _fische__audiobuffer_; -struct fische__audiobuffer; +#include +#include +#include +namespace fische +{ -struct fische__audiobuffer* fische__audiobuffer_new(struct fische* parent); -void fische__audiobuffer_free(struct fische__audiobuffer* self); +class CFische; -void fische__audiobuffer_insert(struct fische__audiobuffer* self, - const void* data, - uint_fast32_t size); -void fische__audiobuffer_lock(struct fische__audiobuffer* self); -void fische__audiobuffer_unlock(struct fische__audiobuffer* self); -void fische__audiobuffer_get(struct fische__audiobuffer* self); +class CAudioBuffer +{ +public: + CAudioBuffer(FISCHE_AUDIOFORMAT format); + ~CAudioBuffer(); + void Insert(const void* data, size_t size); + void Lock(); + void Unlock(); + void Get(); -struct _fische__audiobuffer_ -{ - double* buffer; - uint_fast32_t buffer_size; - uint_fast8_t format; - uint_fast8_t is_locked; - uint_fast32_t puts; - uint_fast32_t gets; - uint_fast32_t last_get; - - struct fische* fische; -}; + inline const double* FrontSamples() const { return m_front_samples; } + inline size_t FrontSampleCount() const { return m_front_sample_count; } -struct fische__audiobuffer -{ - double* front_samples; - uint_fast16_t front_sample_count; - double* back_samples; - uint_fast16_t back_sample_count; + inline const double* BackSamples() const { return m_back_samples; } + inline size_t BackSampleCount() const { return m_back_sample_count; } - struct _fische__audiobuffer_* priv; +private: + const FISCHE_AUDIOFORMAT m_format; + + double* m_front_samples{nullptr}; + size_t m_front_sample_count{0}; + + double* m_back_samples{nullptr}; + size_t m_back_sample_count{0}; + + double* m_buffer{nullptr}; + size_t m_buffer_size{0}; + std::atomic m_is_locked{false}; + uint_fast32_t m_puts{0}; + uint_fast32_t m_gets{0}; + size_t m_last_get{0}; }; + +} // namespace fische diff --git a/src/fische/blurengine.cpp b/src/fische/blurengine.cpp index 9cc2c86..ffbc265 100644 --- a/src/fische/blurengine.cpp +++ b/src/fische/blurengine.cpp @@ -6,62 +6,126 @@ * See LICENSE.md for more information. */ -#include "fische_internal.h" +#include "blurengine.hpp" + +#include "fische.hpp" +#include "screenbuffer.hpp" #include -#include -#ifdef DEBUG -#include -#endif +namespace fische +{ + +CBlurEngine::CBlurEngine(const CFische* parent) + : m_fische(parent), + m_width(parent->GetWidth()), + m_height(parent->GetHeight()), + m_threads(parent->GetUsedCPUs()) +{ + m_sourcebuffer = m_fische->GetScreenbuffer()->Pixels(); + m_destinationbuffer = new uint32_t[m_width * m_height](); + + for (uint_fast8_t i = 0; i < m_threads; ++i) + { + m_worker[i].vectors = nullptr; + m_worker[i].y_start = (i * m_height) / m_threads; + m_worker[i].y_end = ((i + 1) * m_height) / m_threads; + m_worker[i].kill = false; + m_worker[i].work = false; + m_worker[i].thread = new std::thread(&CBlurEngine::ThreadWorker, this, &m_worker[i]); + } +} + +CBlurEngine::~CBlurEngine() +{ + for (uint_fast8_t i = 0; i < m_threads; ++i) + { + m_worker[i].kill = true; + m_worker[i].thread->join(); + delete m_worker[i].thread; + } + + delete[] m_destinationbuffer; +} + +void CBlurEngine::Blur(const uint16_t* vectors) +{ + for (uint_fast8_t i = 0; i < m_threads; ++i) + { + m_worker[i].vectors = vectors; + m_worker[i].work = true; + } +} + +void CBlurEngine::SwapBuffers() +{ + // wait for all workers to finish + bool work = true; + + while (work) + { + work = false; + for (uint_fast8_t i = 0; i < m_threads; ++i) + { + work |= m_worker[i].work; + } + + if (work) + std::this_thread::sleep_for(std::chrono::microseconds(1)); + } -void blur_worker(struct _fische__blurworker_* params) + uint32_t* t = m_destinationbuffer; + m_destinationbuffer = m_sourcebuffer; + m_sourcebuffer = t; + m_fische->GetScreenbuffer()->SetPixels(t); +} + +void CBlurEngine::ThreadWorker(_blurworker_* params) { - uint_fast16_t const width = params->width; - uint_fast16_t const width_x2 = 2 * width; - uint_fast16_t const y_start = params->y_start; - uint_fast16_t const y_end = params->y_end; + const uint32_t width_x2 = 2 * m_width; + const uint32_t y_start = params->y_start; + const uint32_t y_end = params->y_end; uint32_t source_component[4]; - uint_fast16_t const two_lines = 2 * width; - uint_fast16_t const one_line = width; - uint_fast16_t const two_columns = 2; + const uint32_t two_lines = 2 * m_width; + const uint32_t one_line = m_width; + const uint32_t two_columns = 2; - uint_fast16_t x, y; + uint32_t x, y; int_fast8_t vector_x, vector_y; while (!params->kill) { - if (!params->work) { std::this_thread::sleep_for(std::chrono::microseconds(1)); continue; } - uint32_t* source = params->source; + uint32_t* source = m_sourcebuffer; uint32_t* source_pixel; - uint32_t* destination = params->destination; - uint32_t* destination_pixel = destination + y_start * width; + uint32_t* destination_pixel = m_destinationbuffer + y_start * m_width; - int8_t* vectors = (int8_t*)params->vectors; - int8_t* vector_pointer = vectors + y_start * width_x2; + const uint8_t* vectors = reinterpret_cast(params->vectors); + const uint8_t* vector_pointer = vectors + y_start * width_x2; // vertical loop for (y = y_start; y < y_end; y++) { // horizontal loop - for (x = 0; x < width; x++) + for (x = 0; x < m_width; x++) { + if (params->kill) + return; // read the motion vector (actually its opposite) vector_x = *(vector_pointer + 0); vector_y = *(vector_pointer + 1); // point to the pixel at [present + motion vector] - source_pixel = source + (y + vector_y) * width + x + vector_x; + source_pixel = source + (y + vector_y) * m_width + x + vector_x; // read the pixels at [source + (2,1)] [source + (-2,1)] [source + (0,-2)] // shift them right by 2 and remove the bits that overflow each byte @@ -81,101 +145,8 @@ void blur_worker(struct _fische__blurworker_* params) } // mark work as done - params->work = 0; + params->work = false; } - - return; } -struct fische__blurengine* fische__blurengine_new(struct fische* parent) -{ - - struct fische__blurengine* retval = - static_cast(malloc(sizeof(struct fische__blurengine))); - retval->priv = static_cast<_fische__blurengine_*>(malloc(sizeof(struct _fische__blurengine_))); - struct _fische__blurengine_* P = retval->priv; - - P->fische = parent; - P->width = parent->width; - P->height = parent->height; - P->threads = parent->used_cpus; - P->sourcebuffer = FISCHE_PRIVATE(P)->screenbuffer->pixels; - P->destinationbuffer = static_cast(malloc(P->width * P->height * sizeof(uint32_t))); - - uint_fast8_t i; - for (i = 0; i < P->threads; ++i) - { - P->worker[i].source = P->sourcebuffer; - P->worker[i].destination = P->destinationbuffer; - P->worker[i].vectors = 0; - P->worker[i].width = P->width; - P->worker[i].y_start = (i * P->height) / P->threads; - P->worker[i].y_end = ((i + 1) * P->height) / P->threads; - P->worker[i].kill = 0; - P->worker[i].work = 0; - - P->worker[i].thread = new std::thread(blur_worker, &P->worker[i]); - } - - return retval; -} - -void fische__blurengine_free(struct fische__blurengine* self) -{ - if (!self) - return; - - struct _fische__blurengine_* P = self->priv; - - uint_fast8_t i; - for (i = 0; i < P->threads; ++i) - { - P->worker[i].kill = 1; - P->worker[i].thread->join(); - delete P->worker[i].thread; - P->worker[i].thread = nullptr; - } - - free(self->priv->destinationbuffer); - free(self->priv); - free(self); -} - -void fische__blurengine_blur(struct fische__blurengine* self, uint16_t* vectors) -{ - struct _fische__blurengine_* P = self->priv; - uint_fast8_t i; - for (i = 0; i < P->threads; ++i) - { - P->worker[i].source = P->sourcebuffer; - P->worker[i].destination = P->destinationbuffer; - P->worker[i].vectors = vectors; - P->worker[i].work = 1; - } -} - -void fische__blurengine_swapbuffers(struct fische__blurengine* self) -{ - struct _fische__blurengine_* P = self->priv; - - // wait for all workers to finish - uint_fast8_t work = 1; - while (work) - { - - work = 0; - uint_fast8_t i; - for (i = 0; i < P->threads; ++i) - { - work += P->worker[i].work; - } - - if (work) - std::this_thread::sleep_for(std::chrono::microseconds(1)); - } - - uint32_t* t = P->destinationbuffer; - P->destinationbuffer = P->sourcebuffer; - P->sourcebuffer = t; - FISCHE_PRIVATE(P)->screenbuffer->pixels = t; -} +} // namespace fische diff --git a/src/fische/blurengine.hpp b/src/fische/blurengine.hpp index 7f91f88..a54b1fe 100644 --- a/src/fische/blurengine.hpp +++ b/src/fische/blurengine.hpp @@ -8,50 +8,47 @@ #pragma once -#include +#include +#include +#include #include -struct fische; - -struct _fische__blurworker_; -struct _fische__blurengine_; -struct fische__blurengine; - - -struct fische__blurengine* fische__blurengine_new(struct fische* parent); -void fische__blurengine_free(struct fische__blurengine* self); - -void fische__blurengine_blur(struct fische__blurengine* self, uint16_t* vectors); -void fische__blurengine_swapbuffers(struct fische__blurengine* self); - - -struct _fische__blurworker_ +namespace fische { - std::thread* thread; - uint32_t* source; - uint32_t* destination; - uint_fast16_t width; - uint_fast16_t y_start; - uint_fast16_t y_end; - uint16_t* vectors; - uint_fast8_t work; - uint_fast8_t kill; -}; - -struct _fische__blurengine_ -{ - int_fast16_t width; - int_fast16_t height; - uint_fast8_t threads; - uint32_t* sourcebuffer; - uint32_t* destinationbuffer; - struct _fische__blurworker_ worker[8]; +class CFische; - struct fische* fische; -}; - -struct fische__blurengine +class CBlurEngine { - struct _fische__blurengine_* priv; +public: + CBlurEngine(const CFische* parent); + ~CBlurEngine(); + + void Blur(const uint16_t* vectors); + void SwapBuffers(); + +private: + const CFische* m_fische; + const uint_fast16_t m_width; + const uint_fast16_t m_height; + const uint_fast8_t m_threads; + + uint32_t* m_sourcebuffer; + uint32_t* m_destinationbuffer; + + struct _blurworker_ + { + std::thread* thread; + uint_fast16_t y_start; + uint_fast16_t y_end; + const uint16_t* vectors; + std::atomic work; + std::atomic kill; + }; + + void ThreadWorker(_blurworker_* params); + + std::array<_blurworker_, 8> m_worker; }; + +} // namespace fische diff --git a/src/fische/fische.cpp b/src/fische/fische.cpp index adc0519..a644322 100644 --- a/src/fische/fische.cpp +++ b/src/fische/fische.cpp @@ -6,308 +6,377 @@ * See LICENSE.md for more information. */ +#include "fische.hpp" + +#include "analyst.hpp" +#include "audiobuffer.hpp" +#include "blurengine.hpp" #include "cpudetect.h" -#include "fische_internal.h" +#include "screenbuffer.hpp" +#include "vector.hpp" +#include "vectorfield.hpp" +#include "wavepainter.hpp" #include -#include +#include +#include #include -#ifdef DEBUG -#include -#endif - -void create_vectors(fische* F) +namespace fische { - struct _fische__internal_* P = static_cast<_fische__internal_*>(F->priv); - P->vectorfield = fische__vectorfield_new(F, &P->init_progress, &P->init_cancel); - return; -} -void indicate_busy(fische* F) +CFische::CFische() : m_init_progress(0), m_init_cancel(false) { - struct _fische__internal_* P = static_cast<_fische__internal_*>(F->priv); - struct fische__screenbuffer* sbuf = P->screenbuffer; - - fische__point center; - center.x = sbuf->priv->width / 2; - center.y = sbuf->priv->height / 2; - double dim = (center.x > center.y) ? center.y / 2 : center.x / 2; - - double last = -1; - - while ((P->init_progress < 1) && (!P->init_cancel)) - { - - if ((P->init_progress < 0) || (P->init_progress == last)) - { - std::this_thread::sleep_for(std::chrono::microseconds(10000)); - continue; - } - - last = P->init_progress; - double angle = P->init_progress * -2 * 3.1415 + 3.0415; - - fische__vector c1; - c1.x = sin(angle) * dim; - c1.y = cos(angle) * dim; - - fische__vector c2; - c2.x = sin(angle + 0.1) * dim; - c2.y = cos(angle + 0.1) * dim; - - fische__vector e1 = fische__vector_single(&c1); - fische__vector_mul(&e1, dim / 2); - fische__vector e2 = fische__vector_single(&c2); - fische__vector_mul(&e2, dim / 2); - - fische__vector c3 = c2; - fische__vector_sub(&c3, &e2); - fische__vector c4 = c1; - fische__vector_sub(&c4, &e1); - - fische__vector_mul(&c1, F->scale); - fische__vector_mul(&c2, F->scale); - fische__vector_mul(&c3, F->scale); - fische__vector_mul(&c4, F->scale); - - fische__vector_add(&c1, ¢er); - fische__vector_add(&c2, ¢er); - fische__vector_add(&c3, ¢er); - fische__vector_add(&c4, ¢er); - - fische__screenbuffer_lock(sbuf); - fische__screenbuffer_line(sbuf, c1.x, c1.y, c2.x, c2.y, 0xffffffff); - fische__screenbuffer_line(sbuf, c2.x, c2.y, c3.x, c3.y, 0xffffffff); - fische__screenbuffer_line(sbuf, c3.x, c3.y, c4.x, c4.y, 0xffffffff); - fische__screenbuffer_line(sbuf, c4.x, c4.y, c1.x, c1.y, 0xffffffff); - fische__screenbuffer_unlock(sbuf); - } - - return; + used_cpus = _fische__cpu_detect_(); + if (used_cpus > 8) + used_cpus = 8; + + frame_counter = 0; + audio_format = FISCHE_AUDIOFORMAT_FLOAT; + pixel_format = FISCHE_PIXELFORMAT_0xAABBGGRR; + width = 512; + height = 256; + vector_store_load_usage = 0; + read_vectors = nullptr; + write_vectors = nullptr; + on_beat = nullptr; + nervous_mode = 0; + blur_mode = FISCHE_BLUR_SLICK; + line_style = FISCHE_LINESTYLE_ALPHA_SIMULATION; + scale = 1; + amplification = 0; + error_text = "no error"; } -struct fische* fische_new() +CFische::~CFische() { - struct fische* retval = static_cast(malloc(sizeof(struct fische))); - - retval->used_cpus = _fische__cpu_detect_(); - if (retval->used_cpus > 8) - retval->used_cpus = 8; - - retval->frame_counter = 0; - retval->audio_format = FISCHE_AUDIOFORMAT_FLOAT; - retval->pixel_format = FISCHE_PIXELFORMAT_0xAABBGGRR; - retval->width = 512; - retval->height = 256; - retval->read_vectors = 0; - retval->write_vectors = 0; - retval->on_beat = 0; - retval->nervous_mode = 0; - retval->blur_mode = FISCHE_BLUR_SLICK; - retval->line_style = FISCHE_LINESTYLE_ALPHA_SIMULATION; - retval->scale = 1; - retval->amplification = 0; - retval->priv = 0; - retval->error_text = "no error"; - - return retval; + // tell init threads to quit + m_init_cancel = true; + + // wait for init threads to quit + while (m_init_progress < 1) + std::this_thread::sleep_for(std::chrono::microseconds(10)); + + m_audiobuffer.reset(); + m_blurengine.reset(); + m_vectorfield.reset(); + m_wavepainter.reset(); + m_screenbuffer.reset(); + m_analyst.reset(); } -int fische_start(struct fische* handle) +bool CFische::Start() { // plausibility checks - if ((handle->used_cpus > 8) || (handle->used_cpus < 1)) + if ((used_cpus > 8) || (used_cpus < 1)) { - handle->error_text = "CPU count out of range (1 <= used_cpus <= 8)"; - return 1; + error_text = "CPU count out of range (1 <= used_cpus <= 8)"; + return false; } - if (handle->audio_format >= _FISCHE__AUDIOFORMAT_LAST_) + if (audio_format >= _FISCHE__AUDIOFORMAT_LAST_) { - handle->error_text = "audio format invalid"; - return 1; + error_text = "audio format invalid"; + return false; } - if (handle->line_style >= _FISCHE__LINESTYLE_LAST_) + if (line_style >= _FISCHE__LINESTYLE_LAST_) { - handle->error_text = "line style invalid"; - return 1; + error_text = "line style invalid"; + return false; } - if (handle->frame_counter != 0) + if (frame_counter != 0) { - handle->error_text = "frame counter garbled"; - return 1; + error_text = "frame counter garbled"; + return false; } - if ((handle->amplification < -10) || (handle->amplification > 10)) + if ((amplification < -10) || (amplification > 10)) { - handle->error_text = "amplification value out of range (-10 <= amplification <= 10)"; - return 1; + error_text = "amplification value out of range (-10 <= amplification <= 10)"; + return false; } - if ((handle->height < 16) || (handle->height > 2048)) + if ((height < 16) || (height > 2048)) { - handle->error_text = "height value out of range (16 <= height <= 2048)"; - return 1; + error_text = "height value out of range (16 <= height <= 2048)"; + return false; } - if ((handle->width < 16) || (handle->width > 2048)) + if ((width < 16) || (width > 2048)) { - handle->error_text = "width value out of range (16 <= width <= 2048)"; - return 1; + error_text = "width value out of range (16 <= width <= 2048)"; + return false; } - if (handle->width % 4 != 0) + if (width % 4 != 0) { - handle->error_text = "width value invalid (must be a multiple of four)"; - return 1; + error_text = "width value invalid (must be a multiple of four)"; + return false; } - if (handle->pixel_format >= _FISCHE__PIXELFORMAT_LAST_) + if (pixel_format >= _FISCHE__PIXELFORMAT_LAST_) { - handle->error_text = "pixel format invalid"; - return 1; + error_text = "pixel format invalid"; + return false; } - if ((handle->scale < 0.5) || (handle->scale > 2)) + if ((scale < 0.5) || (scale > 2)) { - handle->error_text = "scale value out of range (0.5 <= scale <= 2.0)"; - return 1; + error_text = "scale value out of range (0.5 <= scale <= 2.0)"; + return false; } - if (handle->blur_mode >= _FISCHE__BLUR_LAST_) + if (blur_mode >= _FISCHE__BLUR_LAST_) { - handle->error_text = "blur option invalid"; - return 1; + error_text = "blur option invalid"; + return false; } - // initialize private struct - handle->priv = malloc(sizeof(struct _fische__internal_)); - memset(handle->priv, '\0', sizeof(struct _fische__internal_)); - struct _fische__internal_* P = static_cast<_fische__internal_*>(handle->priv); - - P->init_progress = -1; + m_init_progress = -1; - P->analyst = fische__analyst_new(handle); - P->screenbuffer = fische__screenbuffer_new(handle); - P->wavepainter = fische__wavepainter_new(handle); - P->blurengine = fische__blurengine_new(handle); - P->audiobuffer = fische__audiobuffer_new(handle); + m_analyst = std::make_unique(this); + m_screenbuffer = std::make_unique(this); + m_wavepainter = std::make_unique(this); + m_blurengine = std::make_unique(this); + m_audiobuffer = std::make_unique(audio_format); // start vector creation and busy indicator threads - std::thread(create_vectors, handle).detach(); - std::thread(indicate_busy, handle).detach(); + std::thread(&CFische::ThreadCreateVectors, this).detach(); + std::thread(&CFische::ThreadIndicateBusy, this).detach(); - return 0; + return true; } -uint32_t* fische_render(struct fische* handle) +uint32_t* CFische::Render() { - struct _fische__internal_* P = static_cast<_fische__internal_*>(handle->priv); - // only if init completed - if (P->init_progress >= 1) + if (m_init_progress >= 1) { - // analyse sound data - fische__audiobuffer_lock(P->audiobuffer); - fische__audiobuffer_get(P->audiobuffer); - int_fast8_t analysis = fische__analyst_analyse(P->analyst, P->audiobuffer->back_samples, - P->audiobuffer->back_sample_count); + m_audiobuffer->Lock(); + m_audiobuffer->Get(); + int_fast8_t analysis = + m_analyst->Analyse(m_audiobuffer->BackSamples(), m_audiobuffer->BackSampleCount()); // act accordingly - if (handle->nervous_mode) + if (nervous_mode) { if (analysis >= 2) - fische__wavepainter_change_shape(P->wavepainter); + m_wavepainter->ChangeShape(); if (analysis >= 1) - fische__vectorfield_change(P->vectorfield); + m_vectorfield->Change(); } else { if (analysis >= 1) - fische__wavepainter_change_shape(P->wavepainter); + m_wavepainter->ChangeShape(); if (analysis >= 2) - fische__vectorfield_change(P->vectorfield); + m_vectorfield->Change(); } if (analysis >= 3) { - fische__wavepainter_beat(P->wavepainter, P->analyst->frames_per_beat); + m_wavepainter->Beat(m_analyst->GetFramesPerBeat()); } if (analysis >= 4) { - if (handle->on_beat) - handle->on_beat(handle->handler, P->analyst->frames_per_beat); + OnBeat(m_analyst->GetFramesPerBeat()); } - P->audio_valid = analysis >= 0 ? 1 : 0; - - fische__wavepainter_change_color(P->wavepainter, P->analyst->frames_per_beat, - P->analyst->relative_energy); + m_audio_valid = analysis >= 0; + m_wavepainter->ChangeColor(m_analyst->GetFramesPerBeat(), m_analyst->GetRelativeEnergy()); // wait for blurring to be finished // and swap buffers - fische__screenbuffer_lock(P->screenbuffer); - fische__blurengine_swapbuffers(P->blurengine); - fische__screenbuffer_unlock(P->screenbuffer); + m_screenbuffer->Lock(); + m_blurengine->SwapBuffers(); + m_screenbuffer->Unlock(); // draw waves - if (P->audio_valid) - fische__wavepainter_paint(P->wavepainter, P->audiobuffer->front_samples, - P->audiobuffer->front_sample_count); + if (m_audio_valid) + m_wavepainter->Paint(m_audiobuffer->FrontSamples(), m_audiobuffer->FrontSampleCount()); // start blurring for the next frame - fische__blurengine_blur(P->blurengine, P->vectorfield->field); + m_blurengine->Blur(m_vectorfield->Field()); - fische__audiobuffer_unlock(P->audiobuffer); + m_audiobuffer->Unlock(); } - handle->frame_counter++; + frame_counter++; - return P->screenbuffer->pixels; + return m_screenbuffer->Pixels(); } -void fische_free(struct fische* handle) +void CFische::AudioData(const void* data, size_t data_size) { - if (!handle) + if (m_audiobuffer == nullptr) return; - struct _fische__internal_* P = static_cast<_fische__internal_*>(handle->priv); + m_audiobuffer->Lock(); + m_audiobuffer->Insert(data, data_size); + m_audiobuffer->Unlock(); +} - if (handle->priv) +void CFische::ThreadCreateVectors() +{ + m_vectorfield = std::make_unique(this, m_init_progress, m_init_cancel); +} + +void CFische::ThreadIndicateBusy() +{ + fische::point center; + center.x = m_screenbuffer->Width() / 2; + center.y = m_screenbuffer->Height() / 2; + double dim = (center.x > center.y) ? center.y / 2 : center.x / 2; + + double last = -1; + + while ((m_init_progress < 1) && (!m_init_cancel)) { - // tell init threads to quit - P->init_cancel = 1; - // wait for init threads to quit - while (P->init_progress < 1) - std::this_thread::sleep_for(std::chrono::microseconds(10)); + if ((m_init_progress < 0) || (m_init_progress == last)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + continue; + } + + last = m_init_progress; + double angle = m_init_progress * -2 * 3.1415 + 3.0415; - fische__audiobuffer_free(P->audiobuffer); - fische__blurengine_free(P->blurengine); - fische__vectorfield_free(P->vectorfield); - fische__wavepainter_free(P->wavepainter); - fische__screenbuffer_free(P->screenbuffer); - fische__analyst_free(P->analyst); + fische::vector c1; + c1.x = sin(angle) * dim; + c1.y = cos(angle) * dim; - free(handle->priv); + fische::vector c2; + c2.x = sin(angle + 0.1) * dim; + c2.y = cos(angle + 0.1) * dim; + + fische::vector e1 = fische::vector_single(&c1); + fische::vector_mul(&e1, dim / 2); + fische::vector e2 = fische::vector_single(&c2); + fische::vector_mul(&e2, dim / 2); + + fische::vector c3 = c2; + fische::vector_sub(&c3, &e2); + fische::vector c4 = c1; + fische::vector_sub(&c4, &e1); + + fische::vector_mul(&c1, scale); + fische::vector_mul(&c2, scale); + fische::vector_mul(&c3, scale); + fische::vector_mul(&c4, scale); + + fische::vector_add(&c1, ¢er); + fische::vector_add(&c2, ¢er); + fische::vector_add(&c3, ¢er); + fische::vector_add(&c4, ¢er); + + m_screenbuffer->Lock(); + m_screenbuffer->Line(c1.x, c1.y, c2.x, c2.y, 0xffffffff); + m_screenbuffer->Line(c2.x, c2.y, c3.x, c3.y, 0xffffffff); + m_screenbuffer->Line(c3.x, c3.y, c4.x, c4.y, 0xffffffff); + m_screenbuffer->Line(c4.x, c4.y, c1.x, c1.y, 0xffffffff); + m_screenbuffer->Unlock(); } +} - free(handle); +bool CFische::SetWidthHeight(uint16_t width, uint16_t height) +{ + if ((width < 16) || (width > 2048) || (height < 16) || (height > 2048)) + { + error_text = "width and height out of range (16 <= width, height <= 2048)"; + return false; + } + + this->width = width; + this->height = height; + return true; } -void fische_audiodata(struct fische* handle, const void* data, size_t data_size) +bool CFische::SetUsedCPUs(uint8_t used_cpus) { - struct _fische__internal_* P = static_cast<_fische__internal_*>(handle->priv); + if ((used_cpus < 1) || (used_cpus > 8)) + { + error_text = "CPU count out of range (1 <= used_cpus <= 8)"; + return false; + } + this->used_cpus = used_cpus; + return true; +} - if (NULL == P->audiobuffer) - return; +bool CFische::SetNervousMode(bool nervous_mode) +{ + this->nervous_mode = nervous_mode ? 1 : 0; + return true; +} + +bool CFische::SetAudioFormat(FISCHE_AUDIOFORMAT audio_format) +{ + if (audio_format >= _FISCHE__AUDIOFORMAT_LAST_) + { + error_text = "audio format invalid"; + return false; + } + this->audio_format = audio_format; + return true; +} + +bool CFische::SetPixelFormat(FISCHE_PIXELFORMAT pixel_format) +{ + if (pixel_format >= _FISCHE__PIXELFORMAT_LAST_) + { + error_text = "pixel format invalid"; + return false; + } + this->pixel_format = pixel_format; + return true; +} - fische__audiobuffer_lock(P->audiobuffer); - fische__audiobuffer_insert(P->audiobuffer, data, data_size); - fische__audiobuffer_unlock(P->audiobuffer); +bool CFische::SetBlurMode(FISCHE_BLUR blur_mode) +{ + if (blur_mode >= _FISCHE__BLUR_LAST_) + { + error_text = "blur option invalid"; + return false; + } + this->blur_mode = blur_mode; + return true; } + +bool CFische::SetLineStyle(FISCHE_LINESTYLE line_style) +{ + if (line_style >= _FISCHE__LINESTYLE_LAST_) + { + error_text = "line style invalid"; + return false; + } + this->line_style = line_style; + return true; +} + +bool CFische::SetScale(double scale) +{ + if ((scale < 0.5) || (scale > 2)) + { + error_text = "scale value out of range (0.5 <= scale <= 2.0)"; + return false; + } + this->scale = scale; + return true; +} + +bool CFische::SetAmplification(double amplification) +{ + if ((amplification < -10) || (amplification > 10)) + { + error_text = "amplification value out of range (-10 <= amplification <= 10)"; + return false; + } + this->amplification = amplification; + return true; +} + +} // namespace fische diff --git a/src/fische/fische.h b/src/fische/fische.h index c9e6c11..a824e6a 100644 --- a/src/fische/fische.h +++ b/src/fische/fische.h @@ -9,12 +9,55 @@ #ifndef FISCHE_H #define FISCHE_H -/* int types */ #include -/* size_t */ #include -typedef struct fische +#ifdef WIN32 +#define rand_r(_seed) (_seed == _seed ? rand() : rand()) +#endif + +/* audio sample formats */ +typedef enum FISCHE_AUDIOFORMAT +{ + FISCHE_AUDIOFORMAT_U8, + FISCHE_AUDIOFORMAT_S8, + FISCHE_AUDIOFORMAT_U16, + FISCHE_AUDIOFORMAT_S16, + FISCHE_AUDIOFORMAT_U32, + FISCHE_AUDIOFORMAT_S32, + FISCHE_AUDIOFORMAT_FLOAT, + FISCHE_AUDIOFORMAT_DOUBLE, + _FISCHE__AUDIOFORMAT_LAST_ +} FISCHE_AUDIOFORMAT; + +/* pixel formats */ +typedef enum FISCHE_PIXELFORMAT +{ + FISCHE_PIXELFORMAT_0xRRGGBBAA, + FISCHE_PIXELFORMAT_0xAABBGGRR, + FISCHE_PIXELFORMAT_0xAARRGGBB, + FISCHE_PIXELFORMAT_0xBBGGRRAA, + _FISCHE__PIXELFORMAT_LAST_ +} FISCHE_PIXELFORMAT; + +/* blur style */ +typedef enum FISCHE_BLUR +{ + FISCHE_BLUR_SLICK, + FISCHE_BLUR_FUZZY, + _FISCHE__BLUR_LAST_ +} FISCHE_BLUR; + +/* line style */ +typedef enum FISCHE_LINESTYLE +{ + FISCHE_LINESTYLE_THIN, + FISCHE_LINESTYLE_THICK, + FISCHE_LINESTYLE_ALPHA_SIMULATION, + _FISCHE__LINESTYLE_LAST_ +} FISCHE_LINESTYLE; + +typedef struct FISCHE { /* 16 <= width <= 2048 @@ -39,21 +82,21 @@ typedef struct fische /* see below (audio format enum) * DEFAULT: FISCHE_AUDIOFORMAT_FLOAT * constant after fische_start() */ - uint8_t audio_format; + FISCHE_AUDIOFORMAT audio_format; /* see below (pixel format enum) * DEFAULT: FISCHE_PIXELFORMAT_0xAABBGGRR * constant after fische_start() */ - uint8_t pixel_format; + FISCHE_PIXELFORMAT pixel_format; /* see below (blur mode enum) * DEFAULT: FISCHE_BLUR_SLICK * constant after fische_start() */ - uint8_t blur_mode; + FISCHE_BLUR blur_mode; /* see below (line style enum) * DEFAULT: FISCHE_LINESTYLE_ALPHA_SIMULATION */ - uint8_t line_style; + FISCHE_LINESTYLE line_style; /* 0.5 <= scale <= 2.0 * DEFAULT: 1.0 @@ -64,6 +107,10 @@ typedef struct fische * DEFAULT: 0 */ double amplification; + /* true (!=0) or false (0) + * DEFAULT: 0 */ + uint8_t vector_store_load_usage; + /* if non-NULL, * fische calls this to read vector fields from an external source * takes a void** for data placement @@ -87,79 +134,6 @@ typedef struct fische /* read only */ const char* error_text; - - void* priv; - } FISCHE; - -#ifdef __cplusplus -extern "C" -{ -#endif - - - /* creates a new FISCHE object - * and initialzes it with default values */ - FISCHE* fische_new(); - - /* starts FISCHE */ - int fische_start(FISCHE* handle); - - /* makes the next frame available */ - uint32_t* fische_render(FISCHE* handle); - - /* destructs the FISCHE object */ - void fische_free(FISCHE* handle); - - /* inserts audio data */ - void fische_audiodata(FISCHE* handle, const void* data, size_t data_size); - - -#ifdef __cplusplus -} -#endif - - -/* audio sample formats */ -enum -{ - FISCHE_AUDIOFORMAT_U8, - FISCHE_AUDIOFORMAT_S8, - FISCHE_AUDIOFORMAT_U16, - FISCHE_AUDIOFORMAT_S16, - FISCHE_AUDIOFORMAT_U32, - FISCHE_AUDIOFORMAT_S32, - FISCHE_AUDIOFORMAT_FLOAT, - FISCHE_AUDIOFORMAT_DOUBLE, - _FISCHE__AUDIOFORMAT_LAST_ -}; - -/* pixel formats */ -enum -{ - FISCHE_PIXELFORMAT_0xRRGGBBAA, - FISCHE_PIXELFORMAT_0xAABBGGRR, - FISCHE_PIXELFORMAT_0xAARRGGBB, - FISCHE_PIXELFORMAT_0xBBGGRRAA, - _FISCHE__PIXELFORMAT_LAST_ -}; - -/* blur style */ -enum -{ - FISCHE_BLUR_SLICK, - FISCHE_BLUR_FUZZY, - _FISCHE__BLUR_LAST_ -}; - -/* line style */ -enum -{ - FISCHE_LINESTYLE_THIN, - FISCHE_LINESTYLE_THICK, - FISCHE_LINESTYLE_ALPHA_SIMULATION, - _FISCHE__LINESTYLE_LAST_ -}; - #endif diff --git a/src/fische/fische.hpp b/src/fische/fische.hpp new file mode 100644 index 0000000..0a0cc2c --- /dev/null +++ b/src/fische/fische.hpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) + * Copyright (C) 2012 Marcel Ebmer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#pragma once + +#include "fische.h" + +#include +#include +#include + +namespace fische +{ + +class CScreenBuffer; +class CWavePainter; +class CAnalyst; +class CBlurEngine; +class CVectorField; +class CAudioBuffer; + +class CFische : public FISCHE +{ +public: + /* creates a new FISCHE object + * and initialzes it with default values */ + CFische(); + ~CFische(); + + /* starts FISCHE */ + bool Start(); + + /* makes the next frame available */ + uint32_t* Render(); + + /* inserts audio data */ + void AudioData(const void* data, size_t data_size); + + bool UseVectorStoreLoadUsage() const { return vector_store_load_usage; } + void SetVectorStoreLoadUsage(bool store_and_load) + { + vector_store_load_usage = store_and_load ? 1 : 0; + } + + /* if non-NULL, + * fische calls this to read vector fields from an external source + * takes a void** for data placement + * returns the number of bytes read */ + virtual size_t ReadVectors(void** data) + { + if (read_vectors) + return read_vectors(handler, data); + return 0; + } + + /* if non-NULL, + * fische calls this to write vector field data to an external sink + * takes a void* and the number of bytes to be written */ + virtual void WriteVectors(const void* data, size_t bytes) + { + if (write_vectors && vector_store_load_usage) + write_vectors(handler, data, bytes); + } + + /* if non-NULL, + * fische calls this on major beats that are not handled internally + * takes frames per beat */ + virtual void OnBeat(double frames_per_beat) + { + if (on_beat && vector_store_load_usage) + on_beat(handler, frames_per_beat); + } + + inline uint32_t GetFrameCounter() const { return frame_counter; } + inline uint16_t GetWidth() const { return width; } + inline uint16_t GetHeight() const { return height; } + inline uint8_t GetUsedCPUs() const { return used_cpus; } + inline double GetScale() const { return scale; } + inline FISCHE_PIXELFORMAT GetPixelFormat() const { return pixel_format; } + inline FISCHE_BLUR GetBlurMode() const { return blur_mode; } + inline FISCHE_LINESTYLE GetLineStyle() const { return line_style; } + inline double GetAmplification() const { return amplification; } + CScreenBuffer* GetScreenbuffer() const { return m_screenbuffer.get(); } + + bool SetWidthHeight(uint16_t width, uint16_t height); + bool SetUsedCPUs(uint8_t used_cpus); + bool SetNervousMode(bool nervous_mode); + bool SetAudioFormat(FISCHE_AUDIOFORMAT audio_format); + bool SetPixelFormat(FISCHE_PIXELFORMAT pixel_format); + bool SetBlurMode(FISCHE_BLUR blur_mode); + bool SetLineStyle(FISCHE_LINESTYLE line_style); + bool SetScale(double scale); + bool SetAmplification(double amplification); + + std::string GetErrorText() const { return error_text; } + +private: + void ThreadCreateVectors(); + void ThreadIndicateBusy(); + + std::unique_ptr m_screenbuffer; + std::unique_ptr m_wavepainter; + std::unique_ptr m_analyst; + std::unique_ptr m_blurengine; + std::unique_ptr m_vectorfield; + std::unique_ptr m_audiobuffer; + double m_init_progress; + bool m_init_cancel; + bool m_audio_valid{false}; +}; + +} // namespace fische diff --git a/src/fische/fische_internal.h b/src/fische/fische_internal.h deleted file mode 100644 index 6df798c..0000000 --- a/src/fische/fische_internal.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) - * Copyright (C) 2012 Marcel Ebmer - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSE.md for more information. - */ - -#ifndef FISCHE_INTERNAL_H -#define FISCHE_INTERNAL_H - -#include "analyst.hpp" -#include "audiobuffer.hpp" -#include "blurengine.hpp" -#include "fische.h" -#include "screenbuffer.hpp" -#include "vector.hpp" -#include "vectorfield.hpp" -#include "wavepainter.hpp" - -#ifdef WIN32 -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif -#define rand_r(_seed) (_seed == _seed ? rand() : rand()) -#endif - -#define FISCHE_PRIVATE(P) ((struct _fische__internal_*)P->fische->priv) - -struct _fische__internal_ -{ - struct fische__screenbuffer* screenbuffer; - struct fische__wavepainter* wavepainter; - struct fische__analyst* analyst; - struct fische__blurengine* blurengine; - struct fische__vectorfield* vectorfield; - struct fische__audiobuffer* audiobuffer; - double init_progress; - uint_fast8_t init_cancel; - uint_fast8_t audio_valid; -}; - -#endif diff --git a/src/fische/screenbuffer.cpp b/src/fische/screenbuffer.cpp index 580c422..9836b80 100644 --- a/src/fische/screenbuffer.cpp +++ b/src/fische/screenbuffer.cpp @@ -6,103 +6,78 @@ * See LICENSE.md for more information. */ -#include "fische_internal.h" +#include "screenbuffer.hpp" + +#include "fische.hpp" #include -#include -#include -#include +#include +#include +#include #include -struct fische__screenbuffer* fische__screenbuffer_new(struct fische* parent) +namespace fische { - struct fische__screenbuffer* retval = - static_cast(malloc(sizeof(struct fische__screenbuffer))); - retval->priv = - static_cast<_fische__screenbuffer_*>(malloc(sizeof(struct _fische__screenbuffer_))); - struct _fische__screenbuffer_* P = retval->priv; - P->fische = parent; - P->width = parent->width; - P->height = parent->height; - P->is_locked = 0; +CScreenBuffer::CScreenBuffer(const CFische* parent) + : m_fische(parent), m_width(parent->GetWidth()), m_height(parent->GetHeight()) - retval->pixels = static_cast(malloc(P->width * P->height * sizeof(uint32_t))); - memset(retval->pixels, '\0', P->width * P->height * sizeof(uint32_t)); +{ + m_pixels = new uint32_t[m_width * m_height](); - switch (parent->pixel_format) + switch (parent->GetPixelFormat()) { - case FISCHE_PIXELFORMAT_0xAABBGGRR: - P->alpha_shift = 24; - P->blue_shift = 16; - P->green_shift = 8; - P->red_shift = 0; + m_alpha_shift = 24; + m_blue_shift = 16; + m_green_shift = 8; + m_red_shift = 0; break; - case FISCHE_PIXELFORMAT_0xAARRGGBB: - P->alpha_shift = 24; - P->blue_shift = 0; - P->green_shift = 8; - P->red_shift = 16; + m_alpha_shift = 24; + m_blue_shift = 0; + m_green_shift = 8; + m_red_shift = 16; break; - case FISCHE_PIXELFORMAT_0xBBGGRRAA: - P->alpha_shift = 0; - P->blue_shift = 24; - P->green_shift = 16; - P->red_shift = 8; + m_alpha_shift = 0; + m_blue_shift = 24; + m_green_shift = 16; + m_red_shift = 8; break; - case FISCHE_PIXELFORMAT_0xRRGGBBAA: - P->alpha_shift = 0; - P->blue_shift = 8; - P->green_shift = 16; - P->red_shift = 24; + m_alpha_shift = 0; + m_blue_shift = 8; + m_green_shift = 16; + m_red_shift = 24; break; } - - return retval; } -void fische__screenbuffer_free(struct fische__screenbuffer* self) +CScreenBuffer::~CScreenBuffer() { - if (!self) - return; + Lock(); - fische__screenbuffer_lock(self); - - free(self->priv); - free(self->pixels); - free(self); + delete[] m_pixels; } -void fische__screenbuffer_lock(struct fische__screenbuffer* self) +void CScreenBuffer::Lock() { -#ifdef __GNUC__ - while (!__sync_bool_compare_and_swap(&self->priv->is_locked, 0, 1)) - std::this_thread::sleep_for(std::chrono::microseconds(1)); -#else - while (self->priv->is_locked) + bool expected = false; + while (m_is_locked.compare_exchange_strong(expected, true) == false) + { + expected = false; std::this_thread::sleep_for(std::chrono::microseconds(1)); - self->priv->is_locked = 1; -#endif + } } -void fische__screenbuffer_unlock(struct fische__screenbuffer* self) +void CScreenBuffer::Unlock() { - self->priv->is_locked = 0; + m_is_locked = false; } -void fische__screenbuffer_line(struct fische__screenbuffer* self, - int_fast16_t x1, - int_fast16_t y1, - int_fast16_t x2, - int_fast16_t y2, - uint32_t color) +void CScreenBuffer::Line(double x1, double y1, double x2, double y2, uint32_t color) { - struct _fische__screenbuffer_* P = self->priv; - double diff_x = (x1 > x2) ? (x1 - x2) : (x2 - x1); double diff_y = (y1 > y2) ? (y1 - y2) : (y2 - y1); double dir_x = (x2 < x1) ? -1 : 1; @@ -113,70 +88,64 @@ void fische__screenbuffer_line(struct fische__screenbuffer* self, uint32_t half_alpha_mask; - if (P->fische->line_style == FISCHE_LINESTYLE_ALPHA_SIMULATION) - half_alpha_mask = (0x7f << P->red_shift) + (0x7f << P->green_shift) + (0x7f << P->blue_shift) + - (0x7f << P->alpha_shift); + if (m_fische->GetLineStyle() == FISCHE_LINESTYLE_ALPHA_SIMULATION) + half_alpha_mask = (0x7f << m_red_shift) + (0x7f << m_green_shift) + (0x7f << m_blue_shift) + + (0x7f << m_alpha_shift); else - half_alpha_mask = (0xff << P->red_shift) + (0xff << P->green_shift) + (0xff << P->blue_shift) + - (0x7f << P->alpha_shift); + half_alpha_mask = (0xff << m_red_shift) + (0xff << m_green_shift) + (0xff << m_blue_shift) + + (0x7f << m_alpha_shift); if (diff_x > diff_y) { - - int_fast16_t x; - for (x = x1; x * dir_x <= x2 * dir_x; x += dir_x) + for (int_fast16_t x = int_fast16_t(x1); x * dir_x <= x2 * dir_x; x += int_fast16_t(dir_x)) { + int_fast16_t y = int_fast16_t(y1 + diff_y / diff_x * dir_y * abs(x - x1) + 0.5); - int_fast16_t y = (y1 + diff_y / diff_x * dir_y * abs(x - x1) + 0.5); - - if ((x < 0) || (x >= P->width) || (y < 0) || (y >= P->height)) + if ((x < 0) || (x >= m_width) || (y < 0) || (y >= m_height)) continue; - if (P->fische->line_style != FISCHE_LINESTYLE_THIN) + if (m_fische->GetLineStyle() != FISCHE_LINESTYLE_THIN) { y++; - if (!(y < 0) && !(y >= P->height)) - *(self->pixels + y * P->width + x) = color & half_alpha_mask; - + if (!(y < 0) && !(y >= m_height)) + *(m_pixels + y * m_width + x) = color & half_alpha_mask; y -= 2; - if ((y < 0) || (y >= P->height)) + if ((y < 0) || (y >= m_height)) continue; - *(self->pixels + y * P->width + x) = color & half_alpha_mask; - + *(m_pixels + y * m_width + x) = color & half_alpha_mask; y++; } - *(self->pixels + y * P->width + x) = color; + *(m_pixels + y * m_width + x) = color; } } else { - - int_fast16_t y; - for (y = y1; y * dir_y <= y2 * dir_y; y += dir_y) + for (int_fast16_t y = int_fast16_t(y1); y * dir_y <= y2 * dir_y; y += int_fast16_t(dir_y)) { + int_fast16_t x = int_fast16_t(x1 + diff_x / diff_y * dir_x * abs(y - y1) + 0.5); - int_fast16_t x = (x1 + diff_x / diff_y * dir_x * abs(y - y1) + 0.5); - - if ((x < 0) || (x >= P->width) || (y < 0) || (y >= P->height)) + if ((x < 0) || (x >= m_width) || (y < 0) || (y >= m_height)) continue; - if (P->fische->line_style != FISCHE_LINESTYLE_THIN) + if (m_fische->GetLineStyle() != FISCHE_LINESTYLE_THIN) { x++; - if (!(x < 0) && !(x >= P->width)) - *(self->pixels + y * P->width + x) = color & half_alpha_mask; + if (!(x < 0) && !(x >= m_width)) + *(m_pixels + y * m_width + x) = color & half_alpha_mask; x -= 2; - if ((x < 0) || (x >= P->width)) + if ((x < 0) || (x >= m_width)) continue; - *(self->pixels + y * P->width + x) = color & half_alpha_mask; + *(m_pixels + y * m_width + x) = color & half_alpha_mask; x++; } - *(self->pixels + y * P->width + x) = color; + *(m_pixels + y * m_width + x) = color; } } } + +} // namespace fische diff --git a/src/fische/screenbuffer.hpp b/src/fische/screenbuffer.hpp index 22e3c76..f4a657b 100644 --- a/src/fische/screenbuffer.hpp +++ b/src/fische/screenbuffer.hpp @@ -8,43 +8,45 @@ #pragma once -#include +#include +#include -struct fische; -struct _fische__screenbuffer; -struct fische__screenbuffer; - - -struct fische__screenbuffer* fische__screenbuffer_new(struct fische* parent); -void fische__screenbuffer_free(struct fische__screenbuffer* self); - -void fische__screenbuffer_lock(struct fische__screenbuffer* self); -void fische__screenbuffer_unlock(struct fische__screenbuffer* self); - -void fische__screenbuffer_line(struct fische__screenbuffer* self, - int_fast16_t x1, - int_fast16_t y1, - int_fast16_t x2, - int_fast16_t y2, - uint32_t color); +namespace fische +{ +class CFische; -struct _fische__screenbuffer_ +class CScreenBuffer { - uint_fast8_t is_locked; - int_fast16_t width; - int_fast16_t height; - uint_fast8_t red_shift; - uint_fast8_t blue_shift; - uint_fast8_t green_shift; - uint_fast8_t alpha_shift; - - struct fische* fische; +public: + CScreenBuffer(const CFische* parent); + ~CScreenBuffer(); + + void Lock(); + void Unlock(); + + void Line(double x1, double y1, double x2, double y2, uint32_t color); + + inline uint32_t* Pixels() { return m_pixels; } + inline void SetPixels(uint32_t* pixels) { m_pixels = pixels; } + inline int_fast16_t Width() const { return m_width; } + inline int_fast16_t Height() const { return m_height; } + inline uint_fast8_t RedShift() const { return m_red_shift; } + inline uint_fast8_t BlueShift() const { return m_blue_shift; } + inline uint_fast8_t GreenShift() const { return m_green_shift; } + inline uint_fast8_t AlphaShift() const { return m_alpha_shift; } + +private: + const CFische* m_fische; + const int_fast16_t m_width; + const int_fast16_t m_height; + + std::atomic m_is_locked{false}; + uint32_t* m_pixels; + uint_fast8_t m_red_shift; + uint_fast8_t m_blue_shift; + uint_fast8_t m_green_shift; + uint_fast8_t m_alpha_shift; }; -struct fische__screenbuffer -{ - uint32_t* pixels; - - struct _fische__screenbuffer_* priv; -}; +} // namespace fische diff --git a/src/fische/vector.cpp b/src/fische/vector.cpp index a187276..6f9166c 100644 --- a/src/fische/vector.cpp +++ b/src/fische/vector.cpp @@ -8,31 +8,36 @@ #include "vector.hpp" -double fische__vector_length(fische__vector* self) +#include + +namespace fische +{ + +double vector_length(vector* self) { return sqrt(pow(self->x, 2) + pow(self->y, 2)); } -fische__vector fische__vector_normal(fische__vector* self) +vector vector_normal(vector* self) { - fische__vector r; + vector r; r.x = self->y; r.y = -self->x; return r; } -fische__vector fische__vector_single(fische__vector* self) +vector vector_single(vector* self) { - double l = fische__vector_length(self); - fische__vector r; + double l = vector_length(self); + vector r; r.x = self->x / l; r.y = self->y / l; return r; } -double fische__vector_angle(fische__vector* self) +double vector_angle(vector* self) { - fische__vector su = fische__vector_single(self); + vector su = vector_single(self); double a = acos(su.x); if (self->y > 0) return a; @@ -41,7 +46,7 @@ double fische__vector_angle(fische__vector* self) } // conversion to 2x int8 -uint16_t fische__vector_to_uint16(fische__vector* self) +uint16_t vector_to_uint16(vector* self) { if (self->x < -127) self->x = -127; @@ -52,61 +57,61 @@ uint16_t fische__vector_to_uint16(fische__vector* self) if (self->y > 127) self->y = 127; - int8_t ix = (self->x < 0) ? self->x - 0.5 : self->x + 0.5; - int8_t iy = (self->y < 0) ? self->y - 0.5 : self->y + 0.5; + int8_t ix = static_cast((self->x < 0) ? self->x - 0.5 : self->x + 0.5); + int8_t iy = static_cast((self->y < 0) ? self->y - 0.5 : self->y + 0.5); uint16_t retval = (uint8_t)ix; retval |= ((uint8_t)iy) << 8; return retval; } -fische__vector fische__vector_from_uint16(uint16_t val) +vector vector_from_uint16(uint16_t val) { int8_t ix = val & 0xff; int8_t iy = val >> 8; - fische__vector r; + vector r; r.x = ix; r.y = iy; return r; } -void fische__vector_add(fische__vector* self, fische__vector* other) +void vector_add(vector* self, vector* other) { self->x += other->x; self->y += other->y; } -void fische__vector_sub(fische__vector* self, fische__vector* other) +void vector_sub(vector* self, vector* other) { self->x -= other->x; self->y -= other->y; } -void fische__vector_mul(fische__vector* self, double val) +void vector_mul(vector* self, double val) { self->x *= val; self->y *= val; } -void fische__vector_div(fische__vector* self, double val) +void vector_div(vector* self, double val) { self->x /= val; self->y /= val; } -fische__vector fische__vector_intersect_border(fische__vector* self, - fische__vector* normal_vec, - uint_fast16_t width, - uint_fast16_t height, - int_fast8_t direction) +vector vector_intersect_border(vector* self, + vector* normal_vec, + uint_fast16_t width, + uint_fast16_t height, + int_fast8_t direction) { width--; height--; - fische__vector nvec = *normal_vec; - if (direction == _FISCHE__VECTOR_RIGHT_) + vector nvec = *normal_vec; + if (direction == _VECTOR_RIGHT_) { - fische__vector_mul(&nvec, -1); + vector_mul(&nvec, -1); } double t1, t2, t3, t4; @@ -143,20 +148,22 @@ fische__vector fische__vector_intersect_border(fische__vector* self, double min_t = (a < b) ? a : b; - int_fast16_t ret_x = self->x + nvec.x * min_t; + int_fast16_t ret_x = static_cast(self->x + nvec.x * min_t); while (ret_x < 0) ret_x++; while ((unsigned)ret_x > width) ret_x--; - int_fast16_t ret_y = self->y + nvec.y * min_t; + int_fast16_t ret_y = static_cast(self->y + nvec.y * min_t); while (ret_y < 0) ret_y++; while ((unsigned)ret_y > height) ret_y--; - fische__vector r; + vector r; r.x = ret_x; r.y = ret_y; return r; } + +} // namespace fische diff --git a/src/fische/vector.hpp b/src/fische/vector.hpp index be2e5a2..a2be93b 100644 --- a/src/fische/vector.hpp +++ b/src/fische/vector.hpp @@ -8,37 +8,42 @@ #pragma once -#include -#include +#include -struct _fische__vector_ +namespace fische +{ + +struct _vector_ { double x; double y; }; -typedef struct _fische__vector_ fische__vector; -typedef struct _fische__vector_ fische__point; +typedef struct _vector_ vector; +typedef struct _vector_ point; enum { - _FISCHE__VECTOR_LEFT_, - _FISCHE__VECTOR_RIGHT_ + _VECTOR_LEFT_, + _VECTOR_RIGHT_ }; -double fische__vector_length(fische__vector* self); -fische__vector fische__vector_normal(fische__vector* self); -fische__vector fische__vector_single(fische__vector* self); -double fische__vector_angle(fische__vector* self); -uint16_t fische__vector_to_uint16(fische__vector* self); -fische__vector fische__vector_from_uint16(uint16_t val); -void fische__vector_add(fische__vector* self, fische__vector* other); -void fische__vector_sub(fische__vector* self, fische__vector* other); -void fische__vector_mul(fische__vector* self, double val); -void fische__vector_div(fische__vector* self, double val); - -fische__vector fische__vector_intersect_border(fische__vector* self, - fische__vector* normal_vec, - uint_fast16_t width, - uint_fast16_t height, - int_fast8_t direction); +double vector_length(vector* self); +vector vector_normal(vector* self); +vector vector_single(vector* self); +double vector_angle(vector* self); +uint16_t vector_to_uint16(vector* self); +vector vector_from_uint16(uint16_t val); +void vector_add(vector* self, vector* other); +void vector_sub(vector* self, vector* other); +void vector_mul(vector* self, double val); +void vector_div(vector* self, double val); + +vector vector_intersect_border(vector* self, + vector* normal_vec, + uint_fast16_t width, + uint_fast16_t height, + int_fast8_t direction); + + +} // namespace fische diff --git a/src/fische/vectorfield.cpp b/src/fische/vectorfield.cpp index 708525a..0eff33a 100644 --- a/src/fische/vectorfield.cpp +++ b/src/fische/vectorfield.cpp @@ -6,144 +6,220 @@ * See LICENSE.md for more information. */ -#include "fische_internal.h" +#define _USE_MATH_DEFINES -#include -#include -#include +#include "vectorfield.hpp" + +#include "fische.hpp" -#ifdef DEBUG -#include -#endif +#include +#include #define N_FIELDS 20 +#define MAX_THREADS 8 + +namespace fische +{ + +uint32_t CVectorField::m_rand_seed; + +CVectorField::CVectorField(CFische* parent, double& progress, bool& cancel) + : m_fische(parent), + m_width(parent->GetWidth()), + m_height(parent->GetHeight()), + m_center_x(m_width / 2), + m_center_y(m_height / 2), + m_dimension(m_width < m_height ? uint_fast16_t(m_width * m_fische->GetScale()) + : uint_fast16_t(m_height * m_fische->GetScale())), + m_threads(parent->GetUsedCPUs() >= MAX_THREADS ? MAX_THREADS : parent->GetUsedCPUs()), + m_fieldsize(m_width * m_height * sizeof(uint16_t)) +{ + const bool useStoredVectors = m_fische->UseVectorStoreLoadUsage(); + + m_rand_seed = static_cast(time(nullptr)); + progress = 0.0; + + // if we have stored fields, load them + if (useStoredVectors) + { + size_t bytes = m_fische->ReadVectors((void**)(&m_fields)); + if (bytes) + { + progress = 1.0; + m_n_fields = bytes / m_fieldsize; + m_field = m_fields; + return; + } + } + + // if not, recalculate everything + // NOTE: Leave by `malloc` as the memory can used from "C" code where makes `free(...)`! + m_fields = static_cast(malloc(N_FIELDS * m_fieldsize)); + m_n_fields = N_FIELDS; + + for (uint_fast8_t i = 0; i < N_FIELDS; ++i) + { + if (cancel) + { + m_cancelled = true; + break; + } + + FillField(i); + progress = (i + 1); + progress /= N_FIELDS; + } -struct field_param + // If we use stored vectors and was not present then store it now + if (useStoredVectors) + m_fische->WriteVectors(m_fields, m_n_fields * m_fieldsize); + + progress = 1.0; + + m_field = m_fields; +} + +CVectorField::~CVectorField() { - uint16_t* data; - uint_fast8_t number; - uint_fast16_t start_y; - uint_fast16_t end_y; - struct _fische__vectorfield_* vecfield; -}; + free(m_fields); +} -unsigned int rand_seed; +void CVectorField::Change() +{ + uint16_t* n = m_field; + while (n == m_field) + { + m_field = m_fields + uint16_t(rand()) % m_n_fields * m_width * m_height; + } +} -static inline void _fische__vectorfield_randomize_(fische__vector* vec) +inline void CVectorField::Randomize(fische::vector* vec) { - vec->x += rand_r(&rand_seed) % 3; + vec->x += rand_r(&m_rand_seed) % 3; vec->x -= 1; - vec->y += rand_r(&rand_seed) % 3; + vec->y += rand_r(&m_rand_seed) % 3; vec->y -= 1; } -static inline void _fische__vectorfield_validate_(struct _fische__vectorfield_* P, - fische__vector* vec, - double x, - double y) +inline void CVectorField::Validate(fische::vector* vec, double x, double y) { while (x + vec->x < 2) vec->x += 1; - while (x + vec->x > P->width - 3) + while (x + vec->x > m_width - 3) vec->x -= 1; while (y + vec->y < 2) vec->y += 1; - while (y + vec->y > P->height - 2) + while (y + vec->y > m_height - 2) vec->y -= 1; } -void _fische__fill_thread_(struct field_param* params) +void CVectorField::FillField(uint_fast8_t fieldno) { - uint16_t* field = params->data; - int fieldno = params->number; - struct _fische__vectorfield_* P = params->vecfield; + uint16_t* field = m_fields + fieldno * m_fieldsize / 2; - uint_fast16_t y; - for (y = params->start_y; y < params->end_y; y++) + // threads maximum is 8 + std::thread vec_threads[MAX_THREADS]; + + for (uint_fast8_t i = 0; i < m_threads; ++i) { + const uint_fast16_t start_y = (i * m_height) / m_threads; + const uint_fast16_t end_y = ((i + 1) * m_height) / m_threads; - uint_fast16_t x; - for (x = 0; x < P->width; x++) - { + vec_threads[i] = std::thread(&CVectorField::FillThread, this, field, fieldno, start_y, end_y); + } + + for (uint_fast8_t i = 0; i < m_threads; ++i) + { + vec_threads[i].join(); + } +} - uint16_t* vector = field + x + y * P->width; +void CVectorField::FillThread(uint16_t* field, + uint_fast8_t fieldno, + uint_fast16_t start_y, + uint_fast16_t end_y) +{ + for (uint_fast16_t y = start_y; y < end_y; y++) + { + for (uint_fast16_t x = 0; x < m_width; x++) + { + uint16_t* vector = field + x + y * m_width; // distance and direction relative to center - fische__vector rvec; + fische::vector rvec; rvec.x = x; - rvec.x -= P->center_x; + rvec.x -= m_center_x; rvec.y = y; - rvec.y -= P->center_y; + rvec.y -= m_center_y; - fische__vector e = fische__vector_single(&rvec); - fische__vector n = fische__vector_normal(&e); + fische::vector e = fische::vector_single(&rvec); + fische::vector n = fische::vector_normal(&e); - double r = fische__vector_length(&rvec) / P->dimension; + double r = fische::vector_length(&rvec) / m_dimension; // distance and direction relative to left co-center - fische__vector rvec_left; - rvec_left.x = (double)x - P->center_x + P->width / 3 * P->fische->scale; - rvec_left.y = (double)y - P->center_y; + fische::vector rvec_left; + rvec_left.x = (double)x - m_center_x + m_width / 3 * m_fische->GetScale(); + rvec_left.y = (double)y - m_center_y; - fische__vector e_left = fische__vector_single(&rvec_left); - fische__vector n_left = fische__vector_normal(&e_left); + fische::vector e_left = fische::vector_single(&rvec_left); + fische::vector n_left = fische::vector_normal(&e_left); - double r_left = fische__vector_length(&rvec_left) / P->dimension; + double r_left = fische::vector_length(&rvec_left) / m_dimension; // distance and direction relative to right co-center - fische__vector rvec_right; - rvec_right.x = (double)x - P->center_x - P->width / 3 * P->fische->scale; - rvec_right.y = (double)y - P->center_y; + fische::vector rvec_right; + rvec_right.x = (double)x - m_center_x - m_width / 3 * m_fische->GetScale(); + rvec_right.y = (double)y - m_center_y; - fische__vector e_right = fische__vector_single(&rvec_right); - fische__vector n_right = fische__vector_normal(&e_right); + fische::vector e_right = fische::vector_single(&rvec_right); + fische::vector n_right = fische::vector_normal(&e_right); - double r_right = fische__vector_length(&rvec_right) / P->dimension; + double r_right = fische::vector_length(&rvec_right) / m_dimension; - double speed = P->dimension / 45; + double speed = m_dimension / 45; // correction factors ensure consistent average speeds with all field types // double const corr[] = {0.77, 0.92, 1.72, 2.06, 1.45, 1.45, 1.73, 1.18, 3.24, 2.76, 0.82, 1.21, 1.73, 3.55, 0.47, 0.66, 0.96, 0.97, 1.00, 1.00}; double const corr[] = {0.83, 0.83, 1.56, 1.56, 1.08, 3.54, 1.56, 1.00, 4.47, 2.77, 0.74, 1.01, 1.56, 3.12, 0.67, 0.67, 0.83, 2.43, 1.21, 0.77}; - fische__vector v; + fische::vector v; switch (fieldno) { - case 0: // linear vectors showing away from a horizontal mirror axis v.x = 0; - v.y = (y < P->center_y) ? speed * corr[fieldno] : -speed * corr[fieldno]; + v.y = (y < m_center_y) ? speed * corr[fieldno] : -speed * corr[fieldno]; break; case 1: // linear vectors showing away from a vertical mirror axis - v.x = (x < P->center_x) ? speed * corr[fieldno] : -speed * corr[fieldno]; + v.x = (x < m_center_x) ? speed * corr[fieldno] : -speed * corr[fieldno]; v.y = 0; break; case 2: // radial vectors showing away from the center v = e; - fische__vector_mul(&v, -r * speed * corr[fieldno]); + fische::vector_mul(&v, -r * speed * corr[fieldno]); break; case 3: // tangential vectors (right) v = n; - fische__vector_mul(&v, r * speed * corr[fieldno]); + fische::vector_mul(&v, r * speed * corr[fieldno]); break; case 4: { // tangential-radial vectors (left) - fische__vector _v1 = n; - fische__vector_mul(&_v1, -r * speed * corr[fieldno]); + fische::vector _v1 = n; + fische::vector_mul(&_v1, -r * speed * corr[fieldno]); v = e; - fische__vector_mul(&v, -r * speed * corr[fieldno]); - fische__vector_add(&v, &_v1); + fische::vector_mul(&v, -r * speed * corr[fieldno]); + fische::vector_add(&v, &_v1); break; } @@ -152,7 +228,7 @@ void _fische__fill_thread_(struct field_param* params) // tree rings double dv = cos(M_PI * 24 * r); v = e; - fische__vector_mul(&v, speed * 0.33 * corr[fieldno] * dv); + fische::vector_mul(&v, speed * 0.33 * corr[fieldno] * dv); break; } @@ -161,15 +237,15 @@ void _fische__fill_thread_(struct field_param* params) // hyperbolic vectors v.x = e.y; v.y = e.x; - fische__vector_mul(&v, -r * speed * corr[fieldno]); + fische::vector_mul(&v, -r * speed * corr[fieldno]); break; } case 7: // purely random - v.x = rand_r(&rand_seed) % (int_fast32_t)(2 * speed * corr[fieldno] + 1) - + v.x = rand_r(&m_rand_seed) % (int_fast32_t)(2 * speed * corr[fieldno] + 1) - (speed * corr[fieldno]); - v.y = rand_r(&rand_seed) % (int_fast32_t)(2 * speed * corr[fieldno] + 1) - + v.y = rand_r(&m_rand_seed) % (int_fast32_t)(2 * speed * corr[fieldno] + 1) - (speed * corr[fieldno]); break; @@ -178,7 +254,7 @@ void _fische__fill_thread_(struct field_param* params) // sphere double dv = cos(M_PI * r); v = e; - fische__vector_mul(&v, -r * dv * speed * corr[fieldno]); + fische::vector_mul(&v, -r * dv * speed * corr[fieldno]); break; } @@ -187,19 +263,19 @@ void _fische__fill_thread_(struct field_param* params) // sine distortion double dv = sin(M_PI * 8 * r); v = e; - fische__vector_mul(&v, -r * dv * speed * corr[fieldno]); + fische::vector_mul(&v, -r * dv * speed * corr[fieldno]); break; } case 10: { // black hole - fische__vector _v1 = n; - fische__vector_mul(&_v1, speed * corr[fieldno]); + fische::vector _v1 = n; + fische::vector_mul(&_v1, speed * corr[fieldno]); v = e; - fische__vector_mul(&v, r * speed * corr[fieldno]); - fische__vector_add(&v, &_v1); - if (r * P->dimension < 10) + fische::vector_mul(&v, r * speed * corr[fieldno]); + fische::vector_add(&v, &_v1); + if (r * m_dimension < 10) { v.x = 0; v.y = 0; @@ -213,7 +289,7 @@ void _fische__fill_thread_(struct field_param* params) double dim = pow(11 * M_PI, 2); double _r = r * dim; v = e; - fische__vector_mul(&v, + fische::vector_mul(&v, -speed * corr[fieldno] * sqrt(1.04 - pow(cos(sqrt(_r)), 2) + 0.25 * pow(sin(sqrt(_r)), 2))); break; @@ -223,9 +299,9 @@ void _fische__fill_thread_(struct field_param* params) // spinning CD v = n; if (fabs(r - 0.25) < 0.15) - fische__vector_mul(&v, r * speed * corr[fieldno]); + fische::vector_mul(&v, r * speed * corr[fieldno]); else - fische__vector_mul(&v, -r * speed * corr[fieldno]); + fische::vector_mul(&v, -r * speed * corr[fieldno]); break; case 13: @@ -235,17 +311,17 @@ void _fische__fill_thread_(struct field_param* params) if (r < rt * 1.2) { v = n; - fische__vector_mul(&v, -r * speed * corr[fieldno]); + fische::vector_mul(&v, -r * speed * corr[fieldno]); } else if (r_left < rt) { v = n_left; - fische__vector_mul(&v, r_left * speed * corr[fieldno]); + fische::vector_mul(&v, r_left * speed * corr[fieldno]); } else if (r_right < rt) { v = n_right; - fische__vector_mul(&v, r_right * speed * corr[fieldno]); + fische::vector_mul(&v, r_right * speed * corr[fieldno]); } else { @@ -258,28 +334,28 @@ void _fische__fill_thread_(struct field_param* params) case 14: { // 3-centered fields - radial - fische__vector _v1 = e_left; - fische__vector_mul(&_v1, (2 - r_left) * speed * corr[fieldno]); - fische__vector _v2 = e_right; - fische__vector_mul(&_v2, (2 - r_right) * speed * corr[fieldno]); + fische::vector _v1 = e_left; + fische::vector_mul(&_v1, (2 - r_left) * speed * corr[fieldno]); + fische::vector _v2 = e_right; + fische::vector_mul(&_v2, (2 - r_right) * speed * corr[fieldno]); v = e; - fische__vector_mul(&v, (2 - r) * -speed * corr[fieldno]); - fische__vector_add(&v, &_v1); - fische__vector_add(&v, &_v2); + fische::vector_mul(&v, (2 - r) * -speed * corr[fieldno]); + fische::vector_add(&v, &_v1); + fische::vector_add(&v, &_v2); break; } case 15: { // 3-centered fields - tangential - fische__vector _v1 = n_left; - fische__vector_mul(&_v1, (2 - r_left) * -speed * corr[fieldno]); - fische__vector _v2 = n_right; - fische__vector_mul(&_v2, (2 - r_right) * -speed * corr[fieldno]); + fische::vector _v1 = n_left; + fische::vector_mul(&_v1, (2 - r_left) * -speed * corr[fieldno]); + fische::vector _v2 = n_right; + fische::vector_mul(&_v2, (2 - r_right) * -speed * corr[fieldno]); v = n; - fische__vector_mul(&v, (2 - r) * speed * corr[fieldno]); - fische__vector_add(&v, &_v1); - fische__vector_add(&v, &_v2); + fische::vector_mul(&v, (2 - r) * speed * corr[fieldno]); + fische::vector_add(&v, &_v1); + fische::vector_add(&v, &_v2); break; } @@ -287,11 +363,11 @@ void _fische__fill_thread_(struct field_param* params) { // lenses effect double _r = r * 8 * M_PI; - fische__vector _v1 = e; - fische__vector_mul(&_v1, sin(_r) * -speed * corr[fieldno]); + fische::vector _v1 = e; + fische::vector_mul(&_v1, sin(_r) * -speed * corr[fieldno]); v = n; - fische__vector_mul(&v, sin(8 * fische__vector_angle(&e)) * -speed * corr[fieldno]); - fische__vector_add(&v, &_v1); + fische::vector_mul(&v, sin(8 * fische::vector_angle(&e)) * -speed * corr[fieldno]); + fische::vector_add(&v, &_v1); break; } @@ -299,12 +375,12 @@ void _fische__fill_thread_(struct field_param* params) { // lenses effect double _r = r * 24 * M_PI; - fische__vector _v1 = e; - fische__vector_mul(&_v1, sin(_r) * -speed * 0.33 * corr[fieldno]); + fische::vector _v1 = e; + fische::vector_mul(&_v1, sin(_r) * -speed * 0.33 * corr[fieldno]); v = n; - fische__vector_mul(&v, - sin(24 * fische__vector_angle(&e)) * -speed * 0.33 * corr[fieldno]); - fische__vector_add(&v, &_v1); + fische::vector_mul(&v, + sin(24 * fische::vector_angle(&e)) * -speed * 0.33 * corr[fieldno]); + fische::vector_add(&v, &_v1); break; } @@ -312,8 +388,8 @@ void _fische__fill_thread_(struct field_param* params) { // fan 1 v = e; - double angle = fische__vector_angle(&e); - fische__vector_mul(&v, -speed * corr[fieldno] * sin(8 * angle)); + double angle = fische::vector_angle(&e); + fische::vector_mul(&v, -speed * corr[fieldno] * sin(8 * angle)); break; } @@ -321,8 +397,8 @@ void _fische__fill_thread_(struct field_param* params) { // fan 1 v = e; - double angle = fische__vector_angle(&e); - fische__vector_mul(&v, -speed * corr[fieldno] * (1.1 + sin(8 * angle))); + double angle = fische::vector_angle(&e); + fische::vector_mul(&v, -speed * corr[fieldno] * (1.1 + sin(8 * angle))); break; } @@ -331,126 +407,14 @@ void _fische__fill_thread_(struct field_param* params) return; } - if (P->fische->blur_mode == FISCHE_BLUR_FUZZY) - _fische__vectorfield_randomize_(&v); + if (m_fische->GetBlurMode() == FISCHE_BLUR_FUZZY) + Randomize(&v); - _fische__vectorfield_validate_(P, &v, x, y); - *vector = fische__vector_to_uint16(&v); - } - } - return; -} - -void _fische__fill_field_(struct _fische__vectorfield_* P, uint_fast8_t fieldno) -{ - uint16_t* field = P->fields + fieldno * P->fieldsize / 2; - - // threads maximum is 8 - std::thread vec_threads[8]; - struct field_param params[8]; - - uint_fast8_t i; - for (i = 0; i < P->threads; ++i) - { - params[i].data = field; - params[i].number = fieldno; - params[i].start_y = (i * P->height) / P->threads; - params[i].end_y = ((i + 1) * P->height) / P->threads; - params[i].vecfield = P; - - vec_threads[i] = std::thread(_fische__fill_thread_, ¶ms[i]); - } - - for (i = 0; i < P->threads; ++i) - { - vec_threads[i].join(); - } -} - -struct fische__vectorfield* fische__vectorfield_new(struct fische* parent, - double* progress, - uint_fast8_t* cancel) -{ - - struct fische__vectorfield* retval = - static_cast(malloc(sizeof(struct fische__vectorfield))); - retval->priv = static_cast<_fische__vectorfield_*>(malloc(sizeof(struct _fische__vectorfield_))); - struct _fische__vectorfield_* P = retval->priv; - - rand_seed = time(NULL); - *progress = 0; + Validate(&v, x, y); - P->fische = parent; - P->width = parent->width; - P->height = parent->height; - P->center_x = P->width / 2; - P->center_y = P->height / 2; - P->dimension = P->width < P->height ? P->width * P->fische->scale : P->height * P->fische->scale; - P->fieldsize = P->width * P->height * sizeof(int16_t); - P->threads = parent->used_cpus; - P->cancelled = 0; - - // if we have stored fields, load them - if (parent->read_vectors) - { - size_t bytes = parent->read_vectors(parent->handler, (void**)(&P->fields)); - if (bytes) - { - *progress = 1; - P->n_fields = bytes / P->fieldsize; - retval->field = P->fields; - return retval; + *vector = fische::vector_to_uint16(&v); } } - - // if not, recalculate everything - P->fields = static_cast(malloc(N_FIELDS * P->fieldsize)); - P->n_fields = N_FIELDS; - - uint_fast8_t i; - for (i = 0; i < N_FIELDS; ++i) - { - if (*cancel) - { - P->cancelled = 1; - break; - } - _fische__fill_field_(P, i); - *progress = (i + 1); - *progress /= N_FIELDS; - } - - *progress = 1; - - retval->field = P->fields; - - return retval; } -void fische__vectorfield_free(struct fische__vectorfield* self) -{ - if (!self) - return; - - struct _fische__vectorfield_* P = self->priv; - - if (!P->cancelled && P->fische->write_vectors) - { - P->fische->write_vectors(P->fische->handler, P->fields, P->n_fields * P->fieldsize); - } - - free(self->priv->fields); - free(self->priv); - free(self); -} - -void fische__vectorfield_change(struct fische__vectorfield* self) -{ - struct _fische__vectorfield_* P = self->priv; - - uint16_t* n = self->field; - while (n == self->field) - { - self->field = P->fields + rand() % P->n_fields * P->width * P->height; - } -} +} // namespace fische diff --git a/src/fische/vectorfield.hpp b/src/fische/vectorfield.hpp index d5cf454..2739e3d 100644 --- a/src/fische/vectorfield.hpp +++ b/src/fische/vectorfield.hpp @@ -8,41 +8,50 @@ #pragma once -#include +#include "vector.hpp" -struct fische; -struct _fische__vectorfield_; -struct fische__vectorfield; +#include +#include - -struct fische__vectorfield* fische__vectorfield_new(struct fische* parent, - double* progress, - uint_fast8_t* cancel); -void fische__vectorfield_free(struct fische__vectorfield* self); - -void fische__vectorfield_change(struct fische__vectorfield* self); - - -struct _fische__vectorfield_ +namespace fische { - uint16_t* fields; - uint_fast32_t fieldsize; - uint_fast16_t width; - uint_fast16_t height; - uint_fast16_t dimension; - uint_fast16_t center_x; - uint_fast16_t center_y; - uint_fast8_t threads; - uint_fast8_t n_fields; - uint_fast8_t cancelled; - - struct fische* fische; -}; +class CFische; -struct fische__vectorfield +class CVectorField { - uint16_t* field; - - struct _fische__vectorfield_* priv; +public: + CVectorField(CFische* parent, double& progress, bool& cancel); + ~CVectorField(); + + void Change(); + inline const uint16_t* Field() const { return m_field; }; + +private: + inline void Randomize(fische::vector* vec); + inline void Validate(fische::vector* vec, double x, double y); + void FillField(uint_fast8_t fieldno); + void FillThread(uint16_t* field, + uint_fast8_t fieldno, + uint_fast16_t start_y, + uint_fast16_t end_y); + + uint16_t* m_field{nullptr}; + + CFische* const m_fische; + const uint_fast16_t m_width; + const uint_fast16_t m_height; + const uint_fast16_t m_center_x; + const uint_fast16_t m_center_y; + const uint_fast16_t m_dimension; + const uint_fast8_t m_threads; + const size_t m_fieldsize; + + uint16_t* m_fields{nullptr}; + size_t m_n_fields; + bool m_cancelled{false}; + + static uint32_t m_rand_seed; }; + +} // namespace fische diff --git a/src/fische/wavepainter.cpp b/src/fische/wavepainter.cpp index 20c55e5..9bc9a17 100644 --- a/src/fische/wavepainter.cpp +++ b/src/fische/wavepainter.cpp @@ -6,180 +6,156 @@ * See LICENSE.md for more information. */ -#include "fische_internal.h" +#define _USE_MATH_DEFINES -#include -#include +#include "wavepainter.hpp" -#ifdef DEBUG -#include -#endif +#include "fische.hpp" +#include "screenbuffer.hpp" +#include "vector.hpp" -struct fische__wavepainter* fische__wavepainter_new(struct fische* parent) -{ - - struct fische__wavepainter* retval = - static_cast(malloc(sizeof(struct fische__wavepainter))); - retval->priv = static_cast<_fische__wavepainter_*>(malloc(sizeof(struct _fische__wavepainter_))); - struct _fische__wavepainter_* P = retval->priv; - - P->fische = parent; - - uint32_t full_alpha = 0xff << FISCHE_PRIVATE(P)->screenbuffer->priv->alpha_shift; - - P->width = parent->width; - P->height = parent->height; - P->angle = 0; - P->center_x = P->width / 2; - P->center_y = P->height / 2; - P->color_1 = (rand() % 0xffffffff) | full_alpha; - P->color_2 = (~P->color_1) | full_alpha; - P->direction = 1; - P->is_rotating = 0; - P->n_shapes = 2; - P->rotation_increment = 0; - P->shape = 0; - - return retval; -} +#include +#include -void fische__wavepainter_free(struct fische__wavepainter* self) +namespace fische { - if (!self) - return; - free(self->priv); - free(self); +CWavePainter::CWavePainter(const CFische* parent) + : m_fische(parent), + m_width(parent->GetWidth()), + m_height(parent->GetHeight()), + m_center_x(parent->GetWidth() / 2), + m_center_y(parent->GetHeight() / 2), + m_sbuf(m_fische->GetScreenbuffer()) +{ + const uint32_t full_alpha = 0xff << m_sbuf->AlphaShift(); + m_color_1 = (rand() % 0xffffffff) | full_alpha; + m_color_2 = (~m_color_1) | full_alpha; } -void fische__wavepainter_paint(struct fische__wavepainter* self, double* data, uint_fast16_t size) +void CWavePainter::Paint(const double* data, size_t size) { if (!size) return; - struct _fische__wavepainter_* P = self->priv; - // rotation - if (P->is_rotating) + if (m_is_rotating) { - P->angle += P->rotation_increment; - if ((P->angle > 2 * M_PI) || (P->angle < -2 * M_PI)) + m_angle += m_rotation_increment; + if ((m_angle > 2 * M_PI) || (m_angle < -2 * M_PI)) { - P->angle = 0; - P->is_rotating = 0; + m_angle = 0; + m_is_rotating = false; } } // only init fische scale once static double f_scale = 0; if (f_scale == 0) - f_scale = P->fische->scale; + f_scale = m_fische->GetScale(); // necessary parameters - double dim = (P->height < P->width) ? P->height : P->width; + double dim = (m_height < m_width) ? m_height : m_width; dim *= f_scale; - double factor = pow(10, P->fische->amplification / 10); + double factor = pow(10, m_fische->GetAmplification() / 10); double scale = 6 / dim / factor; // alpha saturation fix - struct fische__screenbuffer* sbuf = FISCHE_PRIVATE(P)->screenbuffer; - fische__screenbuffer_line(sbuf, 0, 0, P->width - 1, 0, 0); - fische__screenbuffer_line(sbuf, P->width - 1, 0, P->width - 1, P->height - 1, 0); - fische__screenbuffer_line(sbuf, P->width - 1, P->height - 1, 0, P->height - 1, 0); - fische__screenbuffer_line(sbuf, 0, P->height - 1, 0, 0, 0); + m_sbuf->Line(0, 0, m_width - 1, 0, 0); + m_sbuf->Line(m_width - 1, 0, m_width - 1, m_height - 1, 0); + m_sbuf->Line(m_width - 1, m_height - 1, 0, m_height - 1, 0); + m_sbuf->Line(0, m_height - 1, 0, 0, 0); - switch (P->shape) + switch (m_shape) { - case 0: { - fische__point center; - center.x = P->center_x; - center.y = P->center_y; + fische::point center; + center.x = m_center_x; + center.y = m_center_y; // base will be the middle of a line, // normally horizontal (angle = 0), but could be rotating - fische__point base1; - base1.x = center.x + (dim / 6) * sin(P->angle); - base1.y = center.y + (dim / 6) * cos(P->angle); + fische::point base1; + base1.x = center.x + (dim / 6) * sin(m_angle); + base1.y = center.y + (dim / 6) * cos(m_angle); - fische__point base2; - base2.x = P->width / 2 - (dim / 6) * sin(P->angle); - base2.y = P->height / 2 - (dim / 6) * cos(P->angle); + fische::point base2; + base2.x = m_width / 2 - (dim / 6) * sin(m_angle); + base2.y = m_height / 2 - (dim / 6) * cos(m_angle); // create vectors perpendicular to the line center->base - fische__vector _nvec1 = base1; - fische__vector_sub(&_nvec1, ¢er); - fische__vector nvec1 = fische__vector_normal(&_nvec1); + fische::vector _nvec1 = base1; + fische::vector_sub(&_nvec1, ¢er); + fische::vector nvec1 = fische::vector_normal(&_nvec1); - fische__vector _nvec2 = base2; - fische__vector_sub(&_nvec2, ¢er); - fische__vector nvec2 = fische__vector_normal(&_nvec2); + fische::vector _nvec2 = base2; + fische::vector_sub(&_nvec2, ¢er); + fische::vector nvec2 = fische::vector_normal(&_nvec2); // find the points where the line would exit the screen - fische__point start1 = fische__vector_intersect_border(&base1, &nvec1, P->width, P->height, - _FISCHE__VECTOR_LEFT_); - fische__point end1 = fische__vector_intersect_border(&base1, &nvec1, P->width, P->height, - _FISCHE__VECTOR_RIGHT_); + fische::point start1 = + fische::vector_intersect_border(&base1, &nvec1, m_width, m_height, fische::_VECTOR_LEFT_); + fische::point end1 = + fische::vector_intersect_border(&base1, &nvec1, m_width, m_height, fische::_VECTOR_RIGHT_); - fische__point start2 = fische__vector_intersect_border(&base2, &nvec2, P->width, P->height, - _FISCHE__VECTOR_LEFT_); - fische__point end2 = fische__vector_intersect_border(&base2, &nvec2, P->width, P->height, - _FISCHE__VECTOR_RIGHT_); + fische::point start2 = + fische::vector_intersect_border(&base2, &nvec2, m_width, m_height, fische::_VECTOR_LEFT_); + fische::point end2 = + fische::vector_intersect_border(&base2, &nvec2, m_width, m_height, fische::_VECTOR_RIGHT_); // determine the direction and length (i.e. vector) // of the increment between two sound samples - fische__vector v1 = end1; - fische__vector_sub(&v1, &start1); - fische__vector_div(&v1, size); + fische::vector v1 = end1; + fische::vector_sub(&v1, &start1); + fische::vector_div(&v1, static_cast(size)); - fische__vector v2 = end2; - fische__vector_sub(&v2, &start2); - fische__vector_div(&v2, size); + fische::vector v2 = end2; + fische::vector_sub(&v2, &start2); + fische::vector_div(&v2, static_cast(size)); // determine the normal vectors // for calculating the sound sample offset (amplitude) - fische__vector _n1 = fische__vector_normal(&v1); - fische__vector n1 = fische__vector_single(&_n1); - fische__vector _n2 = fische__vector_normal(&v2); - fische__vector n2 = fische__vector_single(&_n2); + fische::vector _n1 = fische::vector_normal(&v1); + fische::vector n1 = fische::vector_single(&_n1); + fische::vector _n2 = fische::vector_normal(&v2); + fische::vector n2 = fische::vector_single(&_n2); // draw both lines - fische__point base_p1 = start1; - fische__point base_p2 = start2; + fische::point base_p1 = start1; + fische::point base_p2 = start2; - uint_fast16_t i; - for (i = 0; i < size - 1; i++) + for (size_t i = 0; i < size - 1; i++) { - fische__point pt11 = base_p1; - fische__vector offset11 = n1; - fische__vector_mul(&offset11, (*(data + 2 * i))); - fische__vector_div(&offset11, scale); - fische__vector_add(&pt11, &offset11); - - fische__point pt21 = base_p2; - fische__vector offset21 = n2; - fische__vector_mul(&offset21, (*(data + 1 + 2 * i))); - fische__vector_div(&offset21, scale); - fische__vector_add(&pt21, &offset21); - - fische__vector_add(&base_p1, &v1); - fische__vector_add(&base_p2, &v2); - - fische__point pt12 = base_p1; - fische__vector offset12 = n1; - fische__vector_mul(&offset12, (*(data + 2 * (i + 1)))); - fische__vector_div(&offset12, scale); - fische__vector_add(&pt12, &offset12); - - fische__point pt22 = base_p2; - fische__vector offset22 = n2; - fische__vector_mul(&offset22, (*(data + 1 + 2 * (i + 1)))); - fische__vector_div(&offset22, scale); - fische__vector_add(&pt22, &offset22); - - fische__screenbuffer_line(sbuf, pt11.x, pt11.y, pt12.x, pt12.y, P->color_1); - fische__screenbuffer_line(sbuf, pt21.x, pt21.y, pt22.x, pt22.y, P->color_2); + fische::point pt11 = base_p1; + fische::vector offset11 = n1; + fische::vector_mul(&offset11, (*(data + 2 * i))); + fische::vector_div(&offset11, scale); + fische::vector_add(&pt11, &offset11); + + fische::point pt21 = base_p2; + fische::vector offset21 = n2; + fische::vector_mul(&offset21, (*(data + 1 + 2 * i))); + fische::vector_div(&offset21, scale); + fische::vector_add(&pt21, &offset21); + + fische::vector_add(&base_p1, &v1); + fische::vector_add(&base_p2, &v2); + + fische::point pt12 = base_p1; + fische::vector offset12 = n1; + fische::vector_mul(&offset12, (*(data + 2 * (i + 1)))); + fische::vector_div(&offset12, scale); + fische::vector_add(&pt12, &offset12); + + fische::point pt22 = base_p2; + fische::vector offset22 = n2; + fische::vector_mul(&offset22, (*(data + 1 + 2 * (i + 1)))); + fische::vector_div(&offset22, scale); + fische::vector_add(&pt22, &offset22); + + m_sbuf->Line(pt11.x, pt11.y, pt12.x, pt12.y, m_color_1); + m_sbuf->Line(pt21.x, pt21.y, pt22.x, pt22.y, m_color_2); } return; } @@ -187,29 +163,27 @@ void fische__wavepainter_paint(struct fische__wavepainter* self, double* data, u // circular shape case 1: { - double f = cos(M_PI / 3 + 2 * P->angle) + 0.5; + double f = cos(M_PI / 3 + 2 * m_angle) + 0.5; double e = 1; - uint_fast16_t i; - for (i = 0; i < size - 1; i++) + for (size_t i = 0; i < size - 1; i++) { - - double incr = i; + double incr = static_cast(i); // calculate angles for this and the next sound sample - double phi1 = M_PI * (0.25 + incr / size) + P->angle; + double phi1 = M_PI * (0.25 + incr / size) + m_angle; double phi2 = phi1 + M_PI / size; // calculate the corresponding radius double r1 = dim / 4 + *(data + 2 * i) / scale; double r2 = dim / 4 + *(data + 2 * (i + 1)) / scale; - uint_fast16_t x1 = floor((P->center_x + f * r1 * sin(phi1)) + 0.5); - uint_fast16_t x2 = floor((P->center_x + f * r2 * sin(phi2)) + 0.5); - uint_fast16_t y1 = floor((P->center_y + e * r1 * cos(phi1)) + 0.5); - uint_fast16_t y2 = floor((P->center_y + e * r2 * cos(phi2)) + 0.5); + double x1 = floor((m_center_x + f * r1 * sin(phi1)) + 0.5); + double x2 = floor((m_center_x + f * r2 * sin(phi2)) + 0.5); + double y1 = floor((m_center_y + e * r1 * cos(phi1)) + 0.5); + double y2 = floor((m_center_y + e * r2 * cos(phi2)) + 0.5); - fische__screenbuffer_line(sbuf, x1, y1, x2, y2, P->color_1); + m_sbuf->Line(x1, y1, x2, y2, m_color_1); // the second line will be exactly on the // opposite side of the circle @@ -219,45 +193,40 @@ void fische__wavepainter_paint(struct fische__wavepainter* self, double* data, u r1 = dim / 4 + *(data + 1 + 2 * i) / scale; r2 = dim / 4 + *(data + 1 + 2 * (i + 1)) / scale; - x1 = floor((P->center_x + f * r1 * sin(phi1)) + 0.5); - x2 = floor((P->center_x + f * r2 * sin(phi2)) + 0.5); - y1 = floor((P->center_y + e * r1 * cos(phi1)) + 0.5); - y2 = floor((P->center_y + e * r2 * cos(phi2)) + 0.5); + x1 = floor((m_center_x + f * r1 * sin(phi1)) + 0.5); + x2 = floor((m_center_x + f * r2 * sin(phi2)) + 0.5); + y1 = floor((m_center_y + e * r1 * cos(phi1)) + 0.5); + y2 = floor((m_center_y + e * r2 * cos(phi2)) + 0.5); - fische__screenbuffer_line(sbuf, x1, y1, x2, y2, P->color_2); + m_sbuf->Line(x1, y1, x2, y2, m_color_2); } return; } } } -void fische__wavepainter_beat(struct fische__wavepainter* self, double frames_per_beat) +void CWavePainter::Beat(double frames_per_beat) { - struct _fische__wavepainter_* P = self->priv; - if (!P->is_rotating) + if (!m_is_rotating) { if (frames_per_beat != 0) { - P->direction = 1 - 2 * (rand() % 2); - P->rotation_increment = M_PI / frames_per_beat / 2 * P->direction; - P->angle = 0; - P->is_rotating = 1; + m_direction = 1 - 2 * (rand() % 2); + m_rotation_increment = M_PI / frames_per_beat / 2 * m_direction; + m_angle = 0; + m_is_rotating = true; } } } -void fische__wavepainter_change_color(struct fische__wavepainter* self, - double frames_per_beat, - double energy) +void CWavePainter::ChangeColor(double frames_per_beat, double energy) { - struct _fische__wavepainter_* P = self->priv; - - uint32_t full_alpha = 0xff << FISCHE_PRIVATE(P)->screenbuffer->priv->alpha_shift; + uint32_t full_alpha = 0xff << m_sbuf->AlphaShift(); if (!frames_per_beat && !energy) { - P->color_1 = (rand() % 0xffffffff) | full_alpha; - P->color_2 = (~P->color_1) | full_alpha; + m_color_1 = (rand() % 0xffffffff) | full_alpha; + m_color_2 = (~m_color_1) | full_alpha; } if (!frames_per_beat) @@ -306,26 +275,26 @@ void fische__wavepainter_change_color(struct fische__wavepainter* self, b = x; } - uint32_t red = floor(r * 255 + 0.5); - uint32_t green = floor(b * 255 + 0.5); - uint32_t blue = floor(g * 255 + 0.5); + uint32_t red = static_cast(floor(r * 255 + 0.5)); + uint32_t green = static_cast(floor(b * 255 + 0.5)); + uint32_t blue = static_cast(floor(g * 255 + 0.5)); - P->color_1 = (blue << FISCHE_PRIVATE(P)->screenbuffer->priv->blue_shift) + - (green << FISCHE_PRIVATE(P)->screenbuffer->priv->green_shift) + - (red << FISCHE_PRIVATE(P)->screenbuffer->priv->red_shift) + - (0xff << FISCHE_PRIVATE(P)->screenbuffer->priv->alpha_shift); + m_color_1 = (blue << m_sbuf->BlueShift()) + + (green << m_sbuf->GreenShift()) + + (red << m_sbuf->RedShift()) + + (0xff << m_sbuf->AlphaShift()); - P->color_2 = (~P->color_1) | full_alpha; + m_color_2 = (~m_color_1) | full_alpha; } -void fische__wavepainter_change_shape(struct fische__wavepainter* self) +void CWavePainter::ChangeShape() { - struct _fische__wavepainter_* P = self->priv; - - if (P->is_rotating) + if (m_is_rotating) return; - int_fast8_t n = P->shape; - while (n == P->shape) - n = rand() % P->n_shapes; - P->shape = n; + int_fast8_t n = m_shape; + while (n == m_shape) + n = rand() % m_n_shapes; + m_shape = n; } + +} // namespace fische diff --git a/src/fische/wavepainter.hpp b/src/fische/wavepainter.hpp index bdf239b..2514270 100644 --- a/src/fische/wavepainter.hpp +++ b/src/fische/wavepainter.hpp @@ -8,41 +8,42 @@ #pragma once -#include +#include +#include -struct fische; -struct _fische__wavepainter_; -struct fische__wavepainter; - - -struct fische__wavepainter* fische__wavepainter_new(struct fische* parent); -void fische__wavepainter_free(struct fische__wavepainter* self); - -void fische__wavepainter_paint(struct fische__wavepainter* self, double* data, uint_fast16_t size); -void fische__wavepainter_beat(struct fische__wavepainter* self, double bpm); -void fische__wavepainter_change_color(struct fische__wavepainter* self, double bpm, double energy); -void fische__wavepainter_change_shape(struct fische__wavepainter* self); +namespace fische +{ +class CFische; +class CScreenBuffer; -struct _fische__wavepainter_ +class CWavePainter { - uint_fast16_t width; - uint_fast16_t height; - uint_fast16_t center_x; - uint_fast16_t center_y; - int_fast8_t direction; - uint_fast8_t shape; - uint_fast8_t n_shapes; - uint32_t color_1; - uint32_t color_2; - double angle; - uint_fast8_t is_rotating; - double rotation_increment; - - struct fische* fische; +public: + CWavePainter(const CFische* parent); + ~CWavePainter() = default; + + void Paint(const double* data, size_t size); + void Beat(double bpm); + void ChangeColor(double bpm, double energy); + void ChangeShape(); + +private: + const CFische* m_fische; + const uint_fast16_t m_width; + const uint_fast16_t m_height; + const uint_fast16_t m_center_x; + const uint_fast16_t m_center_y; + CScreenBuffer* m_sbuf; + + uint32_t m_color_1; + uint32_t m_color_2; + int_fast8_t m_direction{1}; + uint_fast8_t m_shape{0}; + uint_fast8_t m_n_shapes{2}; + double m_angle{0.0}; + bool m_is_rotating{false}; + double m_rotation_increment{0.0}; }; -struct fische__wavepainter -{ - struct _fische__wavepainter_* priv; -}; +} // namespace fische diff --git a/src/fishbmc_addon.cpp b/src/fishbmc_addon.cpp index b16e427..fb487af 100644 --- a/src/fishbmc_addon.cpp +++ b/src/fishbmc_addon.cpp @@ -6,9 +6,9 @@ * See LICENSE.md for more information. */ -#include "fishbmc_addon.h" +#define _USE_MATH_DEFINES -#include "fische/fische_internal.h" +#include "fishbmc_addon.h" #include #include @@ -21,16 +21,10 @@ CVisualizationFishBMC::CVisualizationFishBMC() { - m_fische = fische_new(); - m_fische->on_beat = &on_beat; - m_fische->pixel_format = FISCHE_PIXELFORMAT_0xAABBGGRR; - m_fische->line_style = FISCHE_LINESTYLE_THICK; - m_aspect = double(Width()) / double(Height()); + m_aspect = float(Width()) / float(Height()); m_texleft = (2 - m_aspect) / 4; m_texright = 1 - m_texleft; m_filemode = kodi::addon::GetSettingBoolean("filemode"); - m_fische->nervous_mode = kodi::addon::GetSettingBoolean("nervous") ? 1 : 0; - m_fische->handler = this; int detail = kodi::addon::GetSettingInt("detail"); m_size = 128; @@ -51,12 +45,12 @@ CVisualizationFishBMC::CVisualizationFishBMC() // screen bottom right: (1, 1) // screen depth clipping: 3 to 15 m_projMatrix = glm::frustum(-1.0f, 1.0f, 1.0f, -1.0f, 3.0f, 15.0f); -} -CVisualizationFishBMC::~CVisualizationFishBMC() -{ - fische_free(m_fische); - m_fische = nullptr; + CFische::SetAudioFormat(FISCHE_AUDIOFORMAT_FLOAT); + CFische::SetPixelFormat(FISCHE_PIXELFORMAT_0xAABBGGRR); + CFische::SetLineStyle(FISCHE_LINESTYLE_THICK); + CFische::SetNervousMode(kodi::addon::GetSettingBoolean("nervous") ? 1 : 0); + CFische::SetVectorStoreLoadUsage(m_filemode); } bool CVisualizationFishBMC::Start(int channels, @@ -66,29 +60,22 @@ bool CVisualizationFishBMC::Start(int channels, { m_errorstate = false; - m_fische->audio_format = FISCHE_AUDIOFORMAT_FLOAT; + CFische::SetAudioFormat(FISCHE_AUDIOFORMAT_FLOAT); + CFische::SetWidthHeight(m_size * 2, m_size); - m_fische->height = m_size; - m_fische->width = 2 * m_size; - - if (m_filemode) - { - m_fische->read_vectors = &read_vectors; - m_fische->write_vectors = &write_vectors; - } - else + if (!m_filemode) { - delete_vectors(); + DeleteVectors(); } - if (fische_start(m_fische) != 0) + if (!CFische::Start()) { - std::cerr << "fische failed to start" << std::endl; + kodi::Log(ADDON_LOG_ERROR, "fische failed to start: %s", CFische::GetErrorText().c_str()); m_errorstate = true; return false; } - uint32_t* pixels = fische_render(m_fische); + uint32_t* pixels = CFische::Render(); if (!m_shaderLoaded) { @@ -112,7 +99,7 @@ bool CVisualizationFishBMC::Start(int channels, glBindTexture(GL_TEXTURE_2D, m_texture); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_fische->width, m_fische->height, 0, GL_RGBA, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, CFische::GetWidth(), CFische::GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); m_isrotating = false; @@ -147,7 +134,7 @@ void CVisualizationFishBMC::AudioData(const float* pAudioData, size_t iAudioData if (!m_startOK) return; - fische_audiodata(m_fische, pAudioData, iAudioDataLength * 4); + CFische::AudioData(pAudioData, iAudioDataLength * 4); } void CVisualizationFishBMC::Render() @@ -160,9 +147,9 @@ void CVisualizationFishBMC::Render() // check if this frame is to be skipped if (++frame % m_framedivisor == 0) { - uint32_t* pixels = fische_render(m_fische); + uint32_t* pixels = CFische::Render(); glBindTexture(GL_TEXTURE_2D, m_texture); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_fische->width, m_fische->height, GL_RGBA, + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, CFische::GetWidth(), CFische::GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, pixels); if (m_isrotating) m_angle += m_angleincrement; @@ -173,7 +160,7 @@ void CVisualizationFishBMC::Render() { if (m_angle - m_lastangle > 180) { - m_lastangle = m_lastangle ? 0 : 180; + m_lastangle = m_lastangle ? 0.0f : 180.0f; m_angle = m_lastangle; m_isrotating = false; } @@ -181,7 +168,7 @@ void CVisualizationFishBMC::Render() // how many quads will there be? int n_Y = 8; - int n_X = (m_aspect * 8 + 0.5); + int n_X = int(m_aspect * 8.0f + 0.5f); // one-time initialization of rotation axis array if (m_axis.empty()) @@ -197,21 +184,21 @@ void CVisualizationFishBMC::Render() // loop over and draw all quads int quad_count = 0; - double quad_width = 4.0 / n_X; - double quad_height = 4.0 / n_Y; - double tex_width = (m_texright - m_texleft); + float quad_width = 4.0f / n_X; + float quad_height = 4.0f / n_Y; + float tex_width = (m_texright - m_texleft); - for (double X = 0; X < n_X; X += 1) + for (float X = 0; X < n_X; X += 1) { - for (double Y = 0; Y < n_Y; Y += 1) + for (float Y = 0; Y < n_Y; Y += 1) { - double center_x = -2 + (X + 0.5) * 4 / n_X; - double center_y = -2 + (Y + 0.5) * 4 / n_Y; - double tex_left = m_texleft + tex_width * X / n_X; - double tex_right = m_texleft + tex_width * (X + 1) / n_X; - double tex_top = Y / n_Y; - double tex_bottom = (Y + 1) / n_Y; - double angle = (m_angle - m_lastangle) * 4 - (X + Y * n_X) / (n_X * n_Y) * 360; + float center_x = -2 + (X + 0.5f) * 4 / n_X; + float center_y = -2 + (Y + 0.5f) * 4 / n_Y; + float tex_left = m_texleft + tex_width * X / n_X; + float tex_right = m_texleft + tex_width * (X + 1) / n_X; + float tex_top = Y / n_Y; + float tex_bottom = (Y + 1) / n_Y; + float angle = (m_angle - m_lastangle) * 4 - (X + Y * n_X) / (n_X * n_Y) * 360; if (angle < 0) angle = 0; if (angle > 360) @@ -232,7 +219,7 @@ ADDON_STATUS CVisualizationFishBMC::SetSetting(const std::string& settingName, return ADDON_STATUS_UNKNOWN; if (settingName == "nervous") - m_fische->nervous_mode = settingValue.GetBoolean() ? 1 : 0; + CFische::SetNervousMode(settingValue.GetBoolean()); else if (settingName == "filemode") m_filemode = settingValue.GetBoolean(); else if (settingName == "detail") @@ -274,6 +261,17 @@ bool CVisualizationFishBMC::OnEnabled() return true; } +void CVisualizationFishBMC::OnBeat(double frames_per_beat) +{ + if (!m_isrotating) + { + m_isrotating = true; + if (frames_per_beat < 1) + frames_per_beat = 12.0; + m_angleincrement = float(180.0f / 4.0f / frames_per_beat); + } +} + // OpenGL: paint a textured quad void CVisualizationFishBMC::textured_quad(float center_x, float center_y, @@ -286,7 +284,7 @@ void CVisualizationFishBMC::textured_quad(float center_x, float tex_top, float tex_bottom) { - float scale = 1 - sin(angle / 360 * M_PI) / 3; + float scale = 1 - sinf(angle / 360.0f * float(M_PI)) / 3; glm::mat4 modelMatrixOld = m_modelMatrix; m_modelMatrix = glm::translate(m_modelMatrix, glm::vec3(center_x, center_y, 0)); @@ -369,33 +367,13 @@ void CVisualizationFishBMC::finish_render() glDisableVertexAttribArray(m_aVertexLoc); } -void CVisualizationFishBMC::on_beat(void* handler, double frames_per_beat) +void CVisualizationFishBMC::WriteVectors(const void* data, size_t bytes) { - if (!handler) - return; - - CVisualizationFishBMC* thisClass = static_cast(handler); - if (!thisClass->m_isrotating) - { - thisClass->m_isrotating = true; - if (frames_per_beat < 1) - frames_per_beat = 12; - thisClass->m_angleincrement = 180 / 4 / frames_per_beat; - } -} - -void CVisualizationFishBMC::write_vectors(void* handler, const void* data, size_t bytes) -{ - if (!handler) - return; - - CVisualizationFishBMC* thisClass = static_cast(handler); - std::string dirname = kodi::addon::GetUserPath("data"); kodi::vfs::CreateDirectory(dirname); std::ostringstream filename; - filename << dirname << "/" << thisClass->m_fische->height; + filename << dirname << "/" << CFische::GetHeight(); // open the file std::fstream vectorsfile(filename.str().c_str(), std::fstream::out | std::fstream::binary); @@ -407,18 +385,13 @@ void CVisualizationFishBMC::write_vectors(void* handler, const void* data, size_ vectorsfile.close(); } -size_t CVisualizationFishBMC::read_vectors(void* handler, void** data) +size_t CVisualizationFishBMC::ReadVectors(void** data) { - if (!handler) - return 0; - - CVisualizationFishBMC* thisClass = static_cast(handler); - std::string dirname = kodi::addon::GetUserPath("data"); kodi::vfs::CreateDirectory(dirname); std::ostringstream filename; - filename << dirname << "/" << thisClass->m_fische->height; + filename << dirname << "/" << CFische::GetHeight(); // open the file std::fstream vectorsfile(filename.str().c_str(), std::fstream::in); @@ -436,7 +409,7 @@ size_t CVisualizationFishBMC::read_vectors(void* handler, void** data) return n; } -void CVisualizationFishBMC::delete_vectors() +void CVisualizationFishBMC::DeleteVectors() { std::string dirname = kodi::addon::GetUserPath("data"); kodi::vfs::CreateDirectory(dirname); diff --git a/src/fishbmc_addon.h b/src/fishbmc_addon.h index ef4a32f..c860a5e 100644 --- a/src/fishbmc_addon.h +++ b/src/fishbmc_addon.h @@ -8,7 +8,7 @@ #pragma once -#include "fische/fische.h" +#include "fische/fische.hpp" #include #include @@ -33,11 +33,12 @@ struct sCoord class ATTR_DLL_LOCAL CVisualizationFishBMC : public kodi::addon::CAddonBase, public kodi::addon::CInstanceVisualization, - public kodi::gui::gl::CShaderProgram + public kodi::gui::gl::CShaderProgram, + public fische::CFische { public: CVisualizationFishBMC(); - ~CVisualizationFishBMC() override; + ~CVisualizationFishBMC() override = default; bool Start(int channels, int samplesPerSec, @@ -52,6 +53,10 @@ class ATTR_DLL_LOCAL CVisualizationFishBMC : public kodi::addon::CAddonBase, void OnCompiledAndLinked() override; bool OnEnabled() override; + void WriteVectors(const void* data, size_t bytes) override; + size_t ReadVectors(void** data) override; + void OnBeat(double frames_per_beat) override; + private: void start_render(); void finish_render(); @@ -65,10 +70,7 @@ class ATTR_DLL_LOCAL CVisualizationFishBMC : public kodi::addon::CAddonBase, float tex_right, float tex_top, float tex_bottom); - static void on_beat(void* handler, double frames_per_beat); - static void write_vectors(void* handler, const void* data, size_t bytes); - static size_t read_vectors(void* handler, void** data); - void delete_vectors(); + void DeleteVectors(); bool m_startOK = false; bool m_shaderLoaded = false; @@ -89,7 +91,6 @@ class ATTR_DLL_LOCAL CVisualizationFishBMC : public kodi::addon::CAddonBase, GLuint m_indexVBO = 0; GLuint m_texture = 0; - FISCHE* m_fische = nullptr; float m_aspect; bool m_isrotating; float m_angle; From de22559d24869f35033285b30b2d60b16edb93dc Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Fri, 15 May 2026 05:07:54 +0200 Subject: [PATCH 09/13] bring back a "C" based ABI to use for fische Normally not needed on Kodi, but thought to have it as lib usable in "C" too. Tested temporary by set add-on code to use as "C" of it and has worked. --- CMakeLists.txt | 1 + src/fische/fische.h | 25 +++++++++++++++ src/fische/fische_c.cpp | 71 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 src/fische/fische_c.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f9053c7..c7b05cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ set(FISH_SOURCES src/fishbmc_addon.cpp src/fische/audiobuffer.cpp src/fische/blurengine.cpp src/fische/cpudetect.c + src/fische/fische_c.cpp src/fische/fische.cpp src/fische/screenbuffer.cpp src/fische/vector.cpp diff --git a/src/fische/fische.h b/src/fische/fische.h index a824e6a..1714067 100644 --- a/src/fische/fische.h +++ b/src/fische/fische.h @@ -136,4 +136,29 @@ typedef struct FISCHE const char* error_text; } FISCHE; +#ifdef __cplusplus +extern "C" +{ +#endif + + /* creates a new FISCHE object + * and initialzes it with default values */ + FISCHE* fische_new(); + + /* destructs the FISCHE object */ + void fische_free(FISCHE* handle); + + /* starts FISCHE */ + int fische_start(FISCHE* handle); + + /* makes the next frame available */ + uint32_t* fische_render(FISCHE* handle); + + /* inserts audio data */ + void fische_audiodata(FISCHE* handle, const void* data, size_t data_size); + +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/fische/fische_c.cpp b/src/fische/fische_c.cpp new file mode 100644 index 0000000..670d2ef --- /dev/null +++ b/src/fische/fische_c.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2005-2026 Team Kodi (https://kodi.tv) + * Copyright (C) 2012 Marcel Ebmer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#include "fische.hpp" + +using namespace fische; + +/* creates a new FISCHE object + * and initialzes it with default values + */ +FISCHE* fische_new() +{ + CFische* fische = new CFische; + FISCHE* cfische = fische; + cfische->handler = fische; + + return cfische; +} + +/* destructs the FISCHE object */ +void fische_free(FISCHE* handle) +{ + if (!handle) + return; + CFische* fische = static_cast(handle->handler); + if (!fische) + return; + + delete fische; +} + +/* starts FISCHE */ +int fische_start(FISCHE* handle) +{ + if (!handle) + return -1; + CFische* fische = static_cast(handle->handler); + if (!fische) + return -1; + + return fische->Start(); +} + +/* makes the next frame available */ +uint32_t* fische_render(FISCHE* handle) +{ + if (!handle) + return 0; + CFische* fische = static_cast(handle->handler); + if (!fische) + return 0; + + return fische->Render(); +} + +/* inserts audio data */ +void fische_audiodata(FISCHE* handle, const void* data, size_t data_size) +{ + if (!handle) + return; + CFische* fische = static_cast(handle->handler); + if (!fische) + return; + + fische->AudioData(data, data_size); +} From f6fb447ce9d7a59b2d84ffafc690bee401e8a1f9 Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Fri, 15 May 2026 05:32:12 +0200 Subject: [PATCH 10/13] fix always present Windows crash by use of stored vectorfiles For a for me unknown reason was by this "std::fstream vectorsfile(filename.str().c_str(), std::fstream::in);" something partly wrong readed where creates after few seconds to one minute by playback a crash of Kodi. Due a strange reason this comes only on Windows. By fault search I was not come on begin to them, thought on begin maybe relates to threads and why rewritten the code to find why it crash. With use of our Kodi filesystem API has it worked correct. --- src/fishbmc_addon.cpp | 73 ++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/src/fishbmc_addon.cpp b/src/fishbmc_addon.cpp index fb487af..9700235 100644 --- a/src/fishbmc_addon.cpp +++ b/src/fishbmc_addon.cpp @@ -369,56 +369,77 @@ void CVisualizationFishBMC::finish_render() void CVisualizationFishBMC::WriteVectors(const void* data, size_t bytes) { - std::string dirname = kodi::addon::GetUserPath("data"); - kodi::vfs::CreateDirectory(dirname); + if (!data) + return; + + kodi::vfs::CreateDirectory(kodi::addon::GetUserPath("data")); + + const std::string filename = + kodi::addon::GetUserPath("data/vector-" + std::to_string(CFische::GetHeight()) + "px"); - std::ostringstream filename; - filename << dirname << "/" << CFische::GetHeight(); + kodi::vfs::CFile vectorsfile; // open the file - std::fstream vectorsfile(filename.str().c_str(), std::fstream::out | std::fstream::binary); - if (!vectorsfile.good()) + if (!vectorsfile.OpenFileForWrite(filename, true)) + { + kodi::Log(ADDON_LOG_ERROR, "File write \"%s\": Failed to open file", filename.c_str()); return; + } // write it - vectorsfile.write(reinterpret_cast(data), bytes); - vectorsfile.close(); + ssize_t size = vectorsfile.Write(data, bytes); + if (size < 0 || bytes != size) + { + kodi::Log(ADDON_LOG_ERROR, "File write \"%s\": Size mismatch of writed file", filename.c_str()); + return; + } } size_t CVisualizationFishBMC::ReadVectors(void** data) { - std::string dirname = kodi::addon::GetUserPath("data"); - kodi::vfs::CreateDirectory(dirname); + const std::string filename = + kodi::addon::GetUserPath("data/vector-" + std::to_string(CFische::GetHeight()) + "px"); + if (!kodi::vfs::FileExists(filename)) + return 0; - std::ostringstream filename; - filename << dirname << "/" << CFische::GetHeight(); + kodi::vfs::CFile vectorsfile; // open the file - std::fstream vectorsfile(filename.str().c_str(), std::fstream::in); - if (!vectorsfile.good()) + if (!vectorsfile.OpenFile(filename)) + { + kodi::Log(ADDON_LOG_ERROR, "File read \"%s\": Failed to open file", filename); return 0; + } - vectorsfile.seekg(0, std::ios::end); - size_t n = vectorsfile.tellg(); - vectorsfile.seekg(0, std::ios::beg); + ssize_t size = vectorsfile.GetLength(); + if (size <= 0) + { + kodi::Log(ADDON_LOG_ERROR, "File read \"%s\": Unable to get size of file", filename); + return 0; + } - *data = malloc(n); - vectorsfile.read(reinterpret_cast(*data), n); - vectorsfile.close(); + *data = malloc(size_t(size)); + ssize_t sizeRead = vectorsfile.Read(*data, size_t(size)); + if (sizeRead < 0 || sizeRead != size) + { + kodi::Log(ADDON_LOG_ERROR, "File read \"%s\": Size mismatch of readed file", filename); + free(*data); + return 0; + } - return n; + return sizeRead; } void CVisualizationFishBMC::DeleteVectors() { - std::string dirname = kodi::addon::GetUserPath("data"); - kodi::vfs::CreateDirectory(dirname); + const std::string dirname = kodi::addon::GetUserPath("data"); + if (!kodi::vfs::DirectoryExists(dirname)) + return; for (int i = 64; i <= 2048; i *= 2) { - std::ostringstream filename; - filename << dirname << "/" << i; - kodi::vfs::DeleteFile(filename.str()); + const std::string filename = dirname + "/vector-" + std::to_string(i) + "px"; + kodi::vfs::DeleteFile(filename); } } From 548c8998f8fef7b8cf560ef95e6b5f6d958b2848 Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Fri, 15 May 2026 05:16:59 +0200 Subject: [PATCH 11/13] bring in the needed changes about comming API change This the reason why started on add-on and to have usable on coming API change of Kodi. --- src/fishbmc_addon.cpp | 7 ++----- src/fishbmc_addon.h | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/fishbmc_addon.cpp b/src/fishbmc_addon.cpp index 9700235..bc31d04 100644 --- a/src/fishbmc_addon.cpp +++ b/src/fishbmc_addon.cpp @@ -53,10 +53,7 @@ CVisualizationFishBMC::CVisualizationFishBMC() CFische::SetVectorStoreLoadUsage(m_filemode); } -bool CVisualizationFishBMC::Start(int channels, - int samplesPerSec, - int bitsPerSample, - const std::string& songName) +bool CVisualizationFishBMC::AudioStart(int channels, int samplesPerSec, int bitsPerSample) { m_errorstate = false; @@ -110,7 +107,7 @@ bool CVisualizationFishBMC::Start(int channels, return true; } -void CVisualizationFishBMC::Stop() +void CVisualizationFishBMC::AudioStop() { if (!m_startOK) return; diff --git a/src/fishbmc_addon.h b/src/fishbmc_addon.h index c860a5e..3b44f1f 100644 --- a/src/fishbmc_addon.h +++ b/src/fishbmc_addon.h @@ -40,11 +40,8 @@ class ATTR_DLL_LOCAL CVisualizationFishBMC : public kodi::addon::CAddonBase, CVisualizationFishBMC(); ~CVisualizationFishBMC() override = default; - bool Start(int channels, - int samplesPerSec, - int bitsPerSample, - const std::string& songName) override; - void Stop() override; + bool AudioStart(int channels, int samplesPerSec, int bitsPerSample) override; + void AudioStop() override; void Render() override; void AudioData(const float* audioData, size_t audioDataLength) override; ADDON_STATUS SetSetting(const std::string& settingName, From 676503af8e4a2a7f0461700091cbdb478119ce7a Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Tue, 19 May 2026 23:21:22 +0200 Subject: [PATCH 12/13] Add some checks to prevent possible crash conditions Wanted by our AI --- CMakeLists.txt | 2 + src/fische/analyst.cpp | 3 + src/fische/fische.cpp | 7 +- src/fische/fische.hpp | 1 + src/fische/vector.cpp | 5 + src/fische/vectorfield.cpp | 39 ++- src/fische/vectorfield.hpp | 2 + src/fishbmc_addon.cpp | 58 ++-- src/md5.cpp | 345 +++++++++++++++++++ src/md5.h | 14 + visualization.fishbmc/resources/settings.xml | 8 + 11 files changed, 453 insertions(+), 31 deletions(-) create mode 100644 src/md5.cpp create mode 100644 src/md5.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c7b05cd..35893da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ include_directories(${includes} ${KODI_INCLUDE_DIR}/..) # Hack way with "/..", need bigger Kodi cmake rework to match right include ways (becomes done in future) set(FISH_SOURCES src/fishbmc_addon.cpp + src/md5.cpp src/fische/analyst.cpp src/fische/audiobuffer.cpp src/fische/blurengine.cpp @@ -36,6 +37,7 @@ set(FISH_SOURCES src/fishbmc_addon.cpp src/fische/wavepainter.cpp) set(FISH_HEADERS src/fishbmc_addon.h + src/md5.h src/fische/analyst.hpp src/fische/audiobuffer.hpp src/fische/blurengine.hpp diff --git a/src/fische/analyst.cpp b/src/fische/analyst.cpp index c027126..e62c222 100644 --- a/src/fische/analyst.cpp +++ b/src/fische/analyst.cpp @@ -48,6 +48,9 @@ double CAnalyst::GuessFramesPerBeat() } } + if (count == 0) + return 0.0; + return result / count; } diff --git a/src/fische/fische.cpp b/src/fische/fische.cpp index a644322..f9a2c63 100644 --- a/src/fische/fische.cpp +++ b/src/fische/fische.cpp @@ -152,7 +152,7 @@ bool CFische::Start() uint32_t* CFische::Render() { // only if init completed - if (m_init_progress >= 1) + if (m_init_progress >= 1 && m_vectorfield) { // analyse sound data m_audiobuffer->Lock(); @@ -283,6 +283,11 @@ void CFische::ThreadIndicateBusy() } } +size_t CFische::GetMaxVectorsSize() const +{ + return 2048 * 2048 * sizeof(uint16_t) * VECTOR_N_FIELDS; +} + bool CFische::SetWidthHeight(uint16_t width, uint16_t height) { if ((width < 16) || (width > 2048) || (height < 16) || (height > 2048)) diff --git a/src/fische/fische.hpp b/src/fische/fische.hpp index 0a0cc2c..e09173b 100644 --- a/src/fische/fische.hpp +++ b/src/fische/fische.hpp @@ -86,6 +86,7 @@ class CFische : public FISCHE inline FISCHE_LINESTYLE GetLineStyle() const { return line_style; } inline double GetAmplification() const { return amplification; } CScreenBuffer* GetScreenbuffer() const { return m_screenbuffer.get(); } + size_t GetMaxVectorsSize() const; bool SetWidthHeight(uint16_t width, uint16_t height); bool SetUsedCPUs(uint8_t used_cpus); diff --git a/src/fische/vector.cpp b/src/fische/vector.cpp index 6f9166c..133482f 100644 --- a/src/fische/vector.cpp +++ b/src/fische/vector.cpp @@ -29,6 +29,11 @@ vector vector_normal(vector* self) vector vector_single(vector* self) { double l = vector_length(self); + if (l < 1e-9) + { + vector r = {0.0, 0.0}; + return r; + } vector r; r.x = self->x / l; r.y = self->y / l; diff --git a/src/fische/vectorfield.cpp b/src/fische/vectorfield.cpp index 0eff33a..92f3842 100644 --- a/src/fische/vectorfield.cpp +++ b/src/fische/vectorfield.cpp @@ -15,7 +15,6 @@ #include #include -#define N_FIELDS 20 #define MAX_THREADS 8 namespace fische @@ -43,21 +42,35 @@ CVectorField::CVectorField(CFische* parent, double& progress, bool& cancel) if (useStoredVectors) { size_t bytes = m_fische->ReadVectors((void**)(&m_fields)); - if (bytes) + if (bytes >= m_fieldsize && bytes % m_fieldsize == 0) { progress = 1.0; m_n_fields = bytes / m_fieldsize; m_field = m_fields; return; } + else if (bytes > 0) + { + // Invalid data size, free and regenerate + free(m_fields); + m_fields = nullptr; + } } // if not, recalculate everything // NOTE: Leave by `malloc` as the memory can used from "C" code where makes `free(...)`! - m_fields = static_cast(malloc(N_FIELDS * m_fieldsize)); - m_n_fields = N_FIELDS; + m_fields = static_cast(malloc(VECTOR_N_FIELDS * m_fieldsize)); + if (m_fields == nullptr) + { + progress = 0.0; + cancel = true; + m_cancelled = true; + return; + } - for (uint_fast8_t i = 0; i < N_FIELDS; ++i) + m_n_fields = VECTOR_N_FIELDS; + + for (uint_fast8_t i = 0; i < VECTOR_N_FIELDS; ++i) { if (cancel) { @@ -67,11 +80,11 @@ CVectorField::CVectorField(CFische* parent, double& progress, bool& cancel) FillField(i); progress = (i + 1); - progress /= N_FIELDS; + progress /= VECTOR_N_FIELDS; } // If we use stored vectors and was not present then store it now - if (useStoredVectors) + if (useStoredVectors && !m_cancelled) m_fische->WriteVectors(m_fields, m_n_fields * m_fieldsize); progress = 1.0; @@ -86,10 +99,13 @@ CVectorField::~CVectorField() void CVectorField::Change() { + if (!m_fields) + return; + uint16_t* n = m_field; while (n == m_field) { - m_field = m_fields + uint16_t(rand()) % m_n_fields * m_width * m_height; + m_field = m_fields + (rand_r(&m_rand_seed) % m_n_fields) * m_width * m_height; } } @@ -110,13 +126,16 @@ inline void CVectorField::Validate(fische::vector* vec, double x, double y) vec->x -= 1; while (y + vec->y < 2) vec->y += 1; - while (y + vec->y > m_height - 2) + while (y + vec->y > m_height - 3) vec->y -= 1; } void CVectorField::FillField(uint_fast8_t fieldno) { - uint16_t* field = m_fields + fieldno * m_fieldsize / 2; + if (!m_fields) + return; + + uint16_t* field = m_fields + fieldno * m_fieldsize / sizeof(uint16_t); // threads maximum is 8 std::thread vec_threads[MAX_THREADS]; diff --git a/src/fische/vectorfield.hpp b/src/fische/vectorfield.hpp index 2739e3d..96e910c 100644 --- a/src/fische/vectorfield.hpp +++ b/src/fische/vectorfield.hpp @@ -13,6 +13,8 @@ #include #include +#define VECTOR_N_FIELDS 20 + namespace fische { diff --git a/src/fishbmc_addon.cpp b/src/fishbmc_addon.cpp index bc31d04..b3df0eb 100644 --- a/src/fishbmc_addon.cpp +++ b/src/fishbmc_addon.cpp @@ -10,6 +10,8 @@ #include "fishbmc_addon.h" +#include "md5.h" + #include #include #include @@ -390,41 +392,57 @@ void CVisualizationFishBMC::WriteVectors(const void* data, size_t bytes) kodi::Log(ADDON_LOG_ERROR, "File write \"%s\": Size mismatch of writed file", filename.c_str()); return; } + + const std::string hash = hashing::md5::hash_as_hex(data, bytes); + kodi::addon::SetSettingString("file_md5", hash); } size_t CVisualizationFishBMC::ReadVectors(void** data) { + const std::string hashWanted = kodi::addon::GetSettingString("file_md5"); const std::string filename = kodi::addon::GetUserPath("data/vector-" + std::to_string(CFische::GetHeight()) + "px"); - if (!kodi::vfs::FileExists(filename)) + if (hashWanted.empty() || !kodi::vfs::FileExists(filename)) return 0; kodi::vfs::CFile vectorsfile; - - // open the file - if (!vectorsfile.OpenFile(filename)) + try { - kodi::Log(ADDON_LOG_ERROR, "File read \"%s\": Failed to open file", filename); - return 0; - } + // open the file + if (!vectorsfile.OpenFile(filename)) + throw std::string("Failed to open file"); - ssize_t size = vectorsfile.GetLength(); - if (size <= 0) - { - kodi::Log(ADDON_LOG_ERROR, "File read \"%s\": Unable to get size of file", filename); - return 0; - } + ssize_t size = vectorsfile.GetLength(); + if (size <= 0 || size > static_cast(CFische::GetMaxVectorsSize())) + throw std::string("Unable to get correct size of file"); - *data = malloc(size_t(size)); - ssize_t sizeRead = vectorsfile.Read(*data, size_t(size)); - if (sizeRead < 0 || sizeRead != size) + *data = malloc(size_t(size)); + if (*data == nullptr) + throw std::string("Failed to allocate memory"); + + ssize_t sizeRead = vectorsfile.Read(*data, size_t(size)); + if (sizeRead < 0 || sizeRead != size) + { + free(*data); + *data = nullptr; + throw std::string("Size mismatch of readed file"); + } + + const std::string hashGenerated = hashing::md5::hash_as_hex(*data, size); + if (hashGenerated != hashWanted) + { + free(*data); + *data = nullptr; + throw std::string("hash not match readed file"); + } + + return sizeRead; + } + catch (const std::string& error_text) { - kodi::Log(ADDON_LOG_ERROR, "File read \"%s\": Size mismatch of readed file", filename); - free(*data); + kodi::Log(ADDON_LOG_ERROR, "File read \"%s\": %s", filename.c_str(), error_text.c_str()); return 0; } - - return sizeRead; } void CVisualizationFishBMC::DeleteVectors() diff --git a/src/md5.cpp b/src/md5.cpp new file mode 100644 index 0000000..b2963a7 --- /dev/null +++ b/src/md5.cpp @@ -0,0 +1,345 @@ +/*****************************************************************//** + * Kodi note: + * File taken from: + * https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/hashing/md5.cpp + *********************************************************************/ + +#include "md5.h" + +/** + * @file + * @author [tGautot](https://github.com/tGautot) + * @brief Simple C++ implementation of the [MD5 Hashing + * Algorithm](https://en.wikipedia.org/wiki/MD5) + * @details + * The [MD5 Algorithm](https://en.wikipedia.org/wiki/MD5) is a + * hashing algorithm which was designed in 1991 by [Ronal + * Rivest](https://en.wikipedia.org/wiki/Ron_Rivest). + * + * MD5 is one of the most used hashing algorithm there is. Some of its + * use cases are: + * 1. Providing checksum for downloaded software + * 2. Store salted password + * + * However MD5 has be know to be cryptographically weak for quite some + * time, yet it is still widely used. This weakness was exploited by the + * [Flame Malware](https://en.wikipedia.org/wiki/Flame_(malware)) in 2012 + * + * ### Algorithm + * First of all, all values are expected to be in [little endian] + * (https://en.wikipedia.org/wiki/Endianness). This is especially important + * when using part of the bytestring as an integer. + * + * The first step of the algorithm is to pad the message for its length to + * be a multiple of 64 (bytes). This is done by first adding 0x80 (10000000) + * and then only zeroes until the last 8 bytes must be filled, where then the + * 64 bit size of the input will be added + * + * Once this is done, the algo breaks down this padded message + * into 64 bytes chunks. Each chunk is used for one *round*, a round + * breaks the chunk into 16 blocks of 4 bytes. During these rounds + * the algorithm will update its 128 bit state (represented by 4 ints: A,B,C,D) + * For more precisions on these operations please see the [Wikipedia + * aritcle](https://en.wikipedia.org/wiki/MD5#Algorithm). + * The signature given by MD5 is its 128 bit state once all rounds are done. + * @note This is a simple implementation for a byte string but + * some implmenetations can work on bytestream, messages of unknown length. + */ + +#include /// Used for std::copy +#include /// Used for std::array +#include /// Used for assert +#include +#include /// Used for std::memcopy +#include /// Used for IO operations +#include /// Used for strings +#include /// Used for std::vector + +/** + * @namespace hashing + * @brief Hashing algorithms + */ +namespace hashing +{ +/** + * @namespace MD5 + * @brief Functions for the [MD5](https://en.wikipedia.org/wiki/MD5) algorithm + * implementation + */ +namespace md5 +{ + +/** + * @brief Rotates the bits of a 32-bit unsigned integer + * @param n Integer to rotate + * @param rotate How many bits for the rotation + * @return uint32_t The rotated integer + */ +uint32_t leftRotate32bits(uint32_t n, std::size_t rotate) +{ + return (n << rotate) | (n >> (32 - rotate)); +} + +/** + * @brief Checks whether integers are stored as big endian or not + * @note Taken from [this](https://stackoverflow.com/a/1001373) StackOverflow + * post + * @return true IF integers are detected to work as big-endian + * @return false IF integers are detected to work as little-endian + */ +bool isBigEndian() +{ + union + { + uint32_t i; + std::array c; + } bint = {0x01020304}; + + return bint.c[0] == 1; +} + +/** + * @brief Sets 32-bit integer to little-endian if needed + * @param n Number to set to little-endian (uint32_t) + * @return uint32_t param n with binary representation as little-endian + */ +uint32_t toLittleEndian32(uint32_t n) +{ + if (!isBigEndian()) + { + return ((n << 24) & 0xFF000000) | ((n << 8) & 0x00FF0000) | ((n >> 8) & 0x0000FF00) | + ((n >> 24) & 0x000000FF); + } + // Machine works on little endian, no need to change anything + return n; +} + +/** + * @brief Sets 64-bit integer to little-endian if needed + * @param n Number to set to little-endian (uint64_t) + * @return uint64_t param n with binary representation as little-endian + */ +uint64_t toLittleEndian64(uint64_t n) +{ + if (!isBigEndian()) + { + return ((n << 56) & 0xFF00000000000000) | ((n << 40) & 0x00FF000000000000) | + ((n << 24) & 0x0000FF0000000000) | ((n << 8) & 0x000000FF00000000) | + ((n >> 8) & 0x00000000FF000000) | ((n >> 24) & 0x0000000000FF0000) | + ((n >> 40) & 0x000000000000FF00) | ((n >> 56) & 0x00000000000000FF); + ; + } + // Machine works on little endian, no need to change anything + return n; +} + +/** + * @brief Transforms the 128-bit MD5 signature into a 32 char hex string + * @param sig The MD5 signature (Expected 16 bytes) + * @return std::string The hex signature + */ +std::string sig2hex(void* sig) +{ + const char* hexChars = "0123456789abcdef"; + auto* intsig = static_cast(sig); + std::string hex = ""; + for (uint8_t i = 0; i < 16; i++) + { + hex.push_back(hexChars[(intsig[i] >> 4) & 0xF]); + hex.push_back(hexChars[(intsig[i]) & 0xF]); + } + return hex; +} + +/** + * @brief The MD5 algorithm itself, taking in a bytestring + * @param input_bs The bytestring to hash + * @param input_size The size (in BYTES) of the input + * @return void* Pointer to the 128-bit signature + */ +void* hash_bs(const void* input_bs, uint64_t input_size) +{ + auto* input = static_cast(input_bs); + + // Step 0: Initial Data (Those are decided in the MD5 protocol) + // s is the shift used in the leftrotate each round + std::array s = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; + // K is pseudo-random values used each round + // The values can be obtained by the following python code: + + /** + * @brief Values of K are pseudo-random and used to "salt" each round + * The values can be obtained by the following python code + * @code{.py} + * from math import floor, sin + * + * for i in range(64): + * print(floor(2**32 * abs(sin(i+1)))) + * @endcode + */ + std::array K = { + 3614090360, 3905402710, 606105819, 3250441966, 4118548399, 1200080426, 2821735955, + 4249261313, 1770035416, 2336552879, 4294925233, 2304563134, 1804603682, 4254626195, + 2792965006, 1236535329, 4129170786, 3225465664, 643717713, 3921069994, 3593408605, + 38016083, 3634488961, 3889429448, 568446438, 3275163606, 4107603335, 1163531501, + 2850285829, 4243563512, 1735328473, 2368359562, 4294588738, 2272392833, 1839030562, + 4259657740, 2763975236, 1272893353, 4139469664, 3200236656, 681279174, 3936430074, + 3572445317, 76029189, 3654602809, 3873151461, 530742520, 3299628645, 4096336452, + 1126891415, 2878612391, 4237533241, 1700485571, 2399980690, 4293915773, 2240044497, + 1873313359, 4264355552, 2734768916, 1309151649, 4149444226, 3174756917, 718787259, + 3951481745}; + + // The initial 128-bit state + uint32_t a0 = 0x67452301, A = 0; + uint32_t b0 = 0xefcdab89, B = 0; + uint32_t c0 = 0x98badcfe, C = 0; + uint32_t d0 = 0x10325476, D = 0; + + // Step 1: Processing the bytestring + + // First compute the size the padded message will have + // so it is possible to allocate the right amount of memory + uint64_t padded_message_size = 0; + if (input_size % 64 < 56) + { + padded_message_size = input_size + 64 - (input_size % 64); + } + else + { + padded_message_size = input_size + 128 - (input_size % 64); + } + + std::vector padded_message(padded_message_size); + + // Beginning of the padded message is the original message + std::copy(input, input + input_size, padded_message.begin()); + + // Afterwards comes a single 1 bit and then only zeroes + padded_message[input_size] = 1 << 7; // 10000000 + for (uint64_t i = input_size; i % 64 != 56; i++) + { + if (i == input_size) + { + continue; // pass first iteration + } + padded_message[i] = 0; + } + + // We then have to add the 64-bit size of the message at the end + // When there is a conversion from int to bytestring or vice-versa + // We always need to make sure it is little endian + uint64_t input_bitsize_le = toLittleEndian64(input_size * 8); + for (uint8_t i = 0; i < 8; i++) + { + padded_message[padded_message_size - 8 + i] = (input_bitsize_le >> (56 - 8 * i)) & 0xFF; + } + + // Already allocate memory for blocks + std::array blocks{}; + + // Rounds + for (uint64_t chunk = 0; chunk * 64 < padded_message_size; chunk++) + { + // First, build the 16 32-bits blocks from the chunk + for (uint8_t bid = 0; bid < 16; bid++) + { + blocks[bid] = 0; + + // Having to build a 32-bit word from 4-bit words + // Add each and shift them to the left + for (uint8_t cid = 0; cid < 4; cid++) + { + blocks[bid] = (blocks[bid] << 8) + padded_message[chunk * 64 + bid * 4 + cid]; + } + } + + A = a0; + B = b0; + C = c0; + D = d0; + + // Main "hashing" loop + for (uint8_t i = 0; i < 64; i++) + { + uint32_t F = 0, g = 0; + if (i < 16) + { + F = (B & C) | ((~B) & D); + g = i; + } + else if (i < 32) + { + F = (D & B) | ((~D) & C); + g = (5 * i + 1) % 16; + } + else if (i < 48) + { + F = B ^ C ^ D; + g = (3 * i + 5) % 16; + } + else + { + F = C ^ (B | (~D)); + g = (7 * i) % 16; + } + + // Update the accumulators + F += A + K[i] + toLittleEndian32(blocks[g]); + + A = D; + D = C; + C = B; + B += leftRotate32bits(F, s[i]); + } + // Update the state with this chunk's hash + a0 += A; + b0 += B; + c0 += C; + d0 += D; + } + + // Build signature from state + // Note, any type could be used for the signature + // uint8_t was used to make the 16 bytes obvious + // The sig needs to be little endian + auto* sig = new uint8_t[16]; + for (uint8_t i = 0; i < 4; i++) + { + sig[i] = (a0 >> (8 * i)) & 0xFF; + sig[i + 4] = (b0 >> (8 * i)) & 0xFF; + sig[i + 8] = (c0 >> (8 * i)) & 0xFF; + sig[i + 12] = (d0 >> (8 * i)) & 0xFF; + } + + return sig; +} + +/** + * @brief Converts the string to bytestring and calls the main algorithm + * @param message Plain character message to hash + * @return void* Pointer to the MD5 signature + */ +void* hash(const std::string& message) +{ + return hash_bs(&message[0], message.size()); +} + +/** + * @brief Own for Kodi created function to get direct a hash hexadecimal string. + */ +std::string hash_as_hex(const void* input_bs, uint64_t input_size) +{ + void* hash = hash_bs(input_bs, input_size); + if (hash == nullptr) + return ""; + + const std::string ret = sig2hex(hash); + delete[] static_cast(hash); + return ret; +} + +} // namespace md5 +} // namespace hashing diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 0000000..457083f --- /dev/null +++ b/src/md5.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace hashing +{ +namespace md5 +{ + +std::string hash_as_hex(const void* input_bs, uint64_t input_size); + +} // namespace md5 +} // namespace hashing diff --git a/visualization.fishbmc/resources/settings.xml b/visualization.fishbmc/resources/settings.xml index 2f49d32..2bd7184 100644 --- a/visualization.fishbmc/resources/settings.xml +++ b/visualization.fishbmc/resources/settings.xml @@ -35,6 +35,14 @@ true + + false + + true + + + + From c28ff0c65d071abec5d7444b3544f976b0abeebe Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Fri, 15 May 2026 05:18:56 +0200 Subject: [PATCH 13/13] increase add-on version to 22.1.0 --- visualization.fishbmc/addon.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visualization.fishbmc/addon.xml.in b/visualization.fishbmc/addon.xml.in index e4a48b0..71d35dc 100644 --- a/visualization.fishbmc/addon.xml.in +++ b/visualization.fishbmc/addon.xml.in @@ -1,7 +1,7 @@ @ADDON_DEPENDS@