diff --git a/CMakeLists.txt b/CMakeLists.txt index 556b2a8..35893da 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}) @@ -23,27 +23,31 @@ 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/md5.cpp + src/fische/analyst.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 + src/fische/vectorfield.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 + src/fische/cpudetect.h + src/fische/fische.h + src/fische/fische.hpp + src/fische/screenbuffer.hpp + src/fische/vector.hpp + src/fische/vectorfield.hpp + src/fische/wavepainter.hpp) build_addon(visualization.fishbmc FISH DEPLIBS) 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/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 diff --git a/src/analyst.cpp b/src/analyst.cpp deleted file mode 100644 index c26590d..0000000 --- a/src/analyst.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2005-2022 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_internal.h" - -#include -#include - -#ifdef DEBUG -#include -#endif - -enum -{ - _FISCHE__WAITING_, - _FISCHE__MAYBEWAITING_, - _FISCHE__BEAT_ -}; - -int _fische__compare_int_fast16_t_(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) -{ - uint_fast16_t gap_history_sorted[30]; - - 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_); - - uint_fast16_t guess = gap_history_sorted[14]; - - double result = 0; - int count = 0; - - uint_fast8_t i; - uint16_t value; - for (i = 0; i < 30; ++i) - { - value = gap_history_sorted[i] - guess; - if (abs(value) <= 2) - { - result += gap_history_sorted[i]; - ++count; - } - } - - return result / count; -} - -double _fische__get_audio_level_(double* data, uint_fast32_t data_size) -{ - double E = 0; - - uint_fast32_t i; - for (i = 0; i < data_size; ++i) - { - E += fabs(*(data + i)); - } - - if (E <= 0) - E = 1e-9; - E /= 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) -{ - if (!size) - return -1; - - struct _fische__analyst_* P = self->priv; - - double dezibel = _fische__get_audio_level_(data, size * 2); - - if (P->moving_avg_30 == 0) - P->moving_avg_30 = dezibel; - else - P->moving_avg_30 = P->moving_avg_30 * 0.9667 + dezibel * 0.0333; - - P->std_dev = P->std_dev * 0.9667 + fabs(dezibel - P->moving_avg_30) * 0.0333; - - uint_fast32_t frameno = P->fische->frame_counter; - if ((frameno - P->last_beat_frame) > 90) - { - self->frames_per_beat = 0; - memset(P->beat_gap_history, '\0', 30 * sizeof(uint_fast16_t)); - P->bghist_head = 0; - } - - self->relative_energy = P->moving_avg_03 / P->moving_avg_30; - - double relative_intensity = 0; - double new_frames_per_beat; - - switch (P->state) - { - case _FISCHE__WAITING_: - // don't bother if intensity too low - if (dezibel < P->moving_avg_30 + P->std_dev) - break; - - // initialisation fallbacks - if (P->std_dev == 0) - relative_intensity = 1; // avoid div by 0 - else - relative_intensity = (dezibel - P->moving_avg_30) / P->std_dev; - - if (P->intensity_moving_avg == 0) - P->intensity_moving_avg = relative_intensity; // initial assignment - else - P->intensity_moving_avg = P->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; - - // we DO have a beat - P->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; - - // remember this as the last beat - P->last_beat_frame = frameno; - - // reset the short-term moving average - P->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; - else - self->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) - return 4; - if (relative_intensity > P->intensity_moving_avg + 2 * P->intensity_std_dev) - return 3; - if (relative_intensity > P->intensity_moving_avg + 1 * P->intensity_std_dev) - return 2; - - return 1; - - case _FISCHE__BEAT_: - case _FISCHE__MAYBEWAITING_: - // update short term moving average - P->moving_avg_03 = P->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) - { - P->state = P->state == _FISCHE__MAYBEWAITING_ ? _FISCHE__WAITING_ : _FISCHE__MAYBEWAITING_; - return 0; - } - } - - // report level too low - if (dezibel < -45) - return -1; - return 0; -} diff --git a/src/analyst.h b/src/analyst.h deleted file mode 100644 index 395c5e3..0000000 --- a/src/analyst.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2005-2022 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 ANALYST_H -#define ANALYST_H - -#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); - - -struct _fische__analyst_ -{ - 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; -}; - -struct fische__analyst -{ - double relative_energy; - double frames_per_beat; - - struct _fische__analyst_* priv; -}; - -#endif diff --git a/src/audiobuffer.cpp b/src/audiobuffer.cpp deleted file mode 100644 index 9725772..0000000 --- a/src/audiobuffer.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2005-2022 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_internal.h" - -#include -#include -#include -#include - -#ifdef DEBUG -#include -#endif - -struct fische__audiobuffer* fische__audiobuffer_new(struct fische* parent) -{ - - 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; -} - -void fische__audiobuffer_free(struct fische__audiobuffer* self) -{ - if (!self) - return; - - fische__audiobuffer_lock(self); - - free(self->priv->buffer); - free(self->priv); - free(self); -} - -void fische__audiobuffer_insert(struct fische__audiobuffer* self, - const void* data, - uint_fast32_t size) -{ - struct _fische__audiobuffer_* P = self->priv; - - if (P->buffer_size > 44100) - return; - - uint_fast8_t width = 1; - - switch (P->format) - { - case FISCHE_AUDIOFORMAT_DOUBLE: - width = 8; - break; - case FISCHE_AUDIOFORMAT_FLOAT: - case FISCHE_AUDIOFORMAT_S32: - case FISCHE_AUDIOFORMAT_U32: - width = 4; - break; - case FISCHE_AUDIOFORMAT_S16: - case FISCHE_AUDIOFORMAT_U16: - 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))); - - uint_fast32_t i; - for (i = 0; i < size / width; ++i) - { - double* dest = (P->buffer + old_bufsize + i); - - switch (P->format) - { - case FISCHE_AUDIOFORMAT_FLOAT: - *dest = *((float*)data + i); - break; - case FISCHE_AUDIOFORMAT_DOUBLE: - *dest = *((double*)data + i); - break; - case FISCHE_AUDIOFORMAT_S32: - *dest = *((int32_t*)data + i); - *dest /= INT32_MAX; - break; - case FISCHE_AUDIOFORMAT_U32: - *dest = *((uint32_t*)data + i); - *dest -= INT32_MAX; - *dest /= INT32_MAX; - break; - case FISCHE_AUDIOFORMAT_S16: - *dest = *((int16_t*)data + i); - *dest /= INT16_MAX; - break; - case FISCHE_AUDIOFORMAT_U16: - *dest = *((uint16_t*)data + i); - *dest -= INT16_MAX; - *dest /= INT16_MAX; - break; - case FISCHE_AUDIOFORMAT_S8: - *dest = *((int8_t*)data + i); - *dest /= INT8_MAX; - break; - case FISCHE_AUDIOFORMAT_U8: - *dest = *((uint8_t*)data + i); - *dest /= INT8_MAX; - *dest /= INT8_MAX; - break; - } - } - - ++P->puts; -} - -void fische__audiobuffer_get(struct fische__audiobuffer* self) -{ - struct _fische__audiobuffer_* P = self->priv; - - if (P->buffer_size == 0) - return; - - double* new_start = P->buffer + P->last_get * 2; - P->buffer_size -= P->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))); - - if (!P->puts) - return; - - // fallback for first get - if (P->gets == 0) - { - P->gets = 3; - P->puts = 1; - } - - // get/put ratio - double d_ratio = ((double)P->gets) / P->puts; - uint_fast8_t ratio = ceil(d_ratio); - - // how many samples to return - uint_fast32_t n_samples = P->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; - - // set export buffer - self->front_samples = P->buffer; - self->back_samples = P->buffer + P->buffer_size - n_samples * 2; - - // increment get counter - ++P->gets; -} - -void fische__audiobuffer_lock(struct fische__audiobuffer* self) -{ -#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) - std::this_thread::sleep_for(std::chrono::microseconds(1)); - self->priv->is_locked = 1; -#endif -} - -void fische__audiobuffer_unlock(struct fische__audiobuffer* self) -{ - self->priv->is_locked = 0; -} diff --git a/src/audiobuffer.h b/src/audiobuffer.h deleted file mode 100644 index b349d86..0000000 --- a/src/audiobuffer.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2005-2022 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 AUDIOBUFFER_H -#define AUDIOBUFFER_H - -#include - -struct fische; -struct _fische__audiobuffer_; -struct fische__audiobuffer; - - -struct fische__audiobuffer* fische__audiobuffer_new(struct fische* parent); -void fische__audiobuffer_free(struct fische__audiobuffer* self); - -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); - - -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; -}; - -struct fische__audiobuffer -{ - double* front_samples; - uint_fast16_t front_sample_count; - double* back_samples; - uint_fast16_t back_sample_count; - - struct _fische__audiobuffer_* priv; -}; - -#endif diff --git a/src/blurengine.cpp b/src/blurengine.cpp deleted file mode 100644 index 20bfb5b..0000000 --- a/src/blurengine.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2005-2022 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_internal.h" - -#include -#include - -#ifdef DEBUG -#include -#endif - -void blur_worker(struct _fische__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; - - 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; - - uint_fast16_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_pixel; - - uint32_t* destination = params->destination; - uint32_t* destination_pixel = destination + y_start * width; - - int8_t* vectors = (int8_t*)params->vectors; - int8_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++) - { - - // 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; - - // 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 - source_component[0] = (*(source_pixel + one_line - two_columns) >> 2) & 0x3f3f3f3f; - source_component[1] = (*(source_pixel + one_line + two_columns) >> 2) & 0x3f3f3f3f; - source_component[2] = (*(source_pixel - two_lines) >> 2) & 0x3f3f3f3f; - source_component[3] = (*(source_pixel) >> 2) & 0x3f3f3f3f; - - // add those four components and write to the destination - // increment destination pointer - *(destination_pixel++) = - source_component[0] + source_component[1] + source_component[2] + source_component[3]; - - // increment vector source pointer - vector_pointer += 2; - } - } - - // mark work as done - params->work = 0; - } - - 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; -} diff --git a/src/blurengine.h b/src/blurengine.h deleted file mode 100644 index b803e03..0000000 --- a/src/blurengine.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2005-2022 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 BLURENGINE_H -#define BLURENGINE_H - -#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_ -{ - 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]; - - struct fische* fische; -}; - -struct fische__blurengine -{ - struct _fische__blurengine_* priv; -}; - -#endif diff --git a/src/fische.cpp b/src/fische.cpp deleted file mode 100644 index 522643b..0000000 --- a/src/fische.cpp +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright (C) 2005-2022 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_internal.h" - -#include -#include -#include - -#ifdef DEBUG -#include -#endif - -void create_vectors(fische* F) -{ - 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) -{ - 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; -} - -struct fische* fische_new() -{ - 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; -} - -int fische_start(struct fische* handle) -{ - // plausibility checks - if ((handle->used_cpus > 8) || (handle->used_cpus < 1)) - { - handle->error_text = "CPU count out of range (1 <= used_cpus <= 8)"; - return 1; - } - - if (handle->audio_format >= _FISCHE__AUDIOFORMAT_LAST_) - { - handle->error_text = "audio format invalid"; - return 1; - } - - if (handle->line_style >= _FISCHE__LINESTYLE_LAST_) - { - handle->error_text = "line style invalid"; - return 1; - } - - if (handle->frame_counter != 0) - { - handle->error_text = "frame counter garbled"; - return 1; - } - - if ((handle->amplification < -10) || (handle->amplification > 10)) - { - handle->error_text = "amplification value out of range (-10 <= amplification <= 10)"; - return 1; - } - - if ((handle->height < 16) || (handle->height > 2048)) - { - handle->error_text = "height value out of range (16 <= height <= 2048)"; - return 1; - } - - if ((handle->width < 16) || (handle->width > 2048)) - { - handle->error_text = "width value out of range (16 <= width <= 2048)"; - return 1; - } - - if (handle->width % 4 != 0) - { - handle->error_text = "width value invalid (must be a multiple of four)"; - return 1; - } - - if (handle->pixel_format >= _FISCHE__PIXELFORMAT_LAST_) - { - handle->error_text = "pixel format invalid"; - return 1; - } - - if ((handle->scale < 0.5) || (handle->scale > 2)) - { - handle->error_text = "scale value out of range (0.5 <= scale <= 2.0)"; - return 1; - } - - if (handle->blur_mode >= _FISCHE__BLUR_LAST_) - { - handle->error_text = "blur option invalid"; - return 1; - } - - // 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; - - 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); - - // start vector creation and busy indicator threads - std::thread(create_vectors, handle).detach(); - std::thread(indicate_busy, handle).detach(); - - return 0; -} - -uint32_t* fische_render(struct fische* handle) -{ - struct _fische__internal_* P = static_cast<_fische__internal_*>(handle->priv); - - // only if init completed - if (P->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); - - // act accordingly - if (handle->nervous_mode) - { - if (analysis >= 2) - fische__wavepainter_change_shape(P->wavepainter); - if (analysis >= 1) - fische__vectorfield_change(P->vectorfield); - } - else - { - if (analysis >= 1) - fische__wavepainter_change_shape(P->wavepainter); - if (analysis >= 2) - fische__vectorfield_change(P->vectorfield); - } - - if (analysis >= 3) - { - fische__wavepainter_beat(P->wavepainter, P->analyst->frames_per_beat); - } - if (analysis >= 4) - { - if (handle->on_beat) - handle->on_beat(handle->handler, P->analyst->frames_per_beat); - } - - P->audio_valid = analysis >= 0 ? 1 : 0; - - fische__wavepainter_change_color(P->wavepainter, P->analyst->frames_per_beat, - P->analyst->relative_energy); - - - // wait for blurring to be finished - // and swap buffers - fische__screenbuffer_lock(P->screenbuffer); - fische__blurengine_swapbuffers(P->blurengine); - fische__screenbuffer_unlock(P->screenbuffer); - - // draw waves - if (P->audio_valid) - fische__wavepainter_paint(P->wavepainter, P->audiobuffer->front_samples, - P->audiobuffer->front_sample_count); - - // start blurring for the next frame - fische__blurengine_blur(P->blurengine, P->vectorfield->field); - - fische__audiobuffer_unlock(P->audiobuffer); - } - - handle->frame_counter++; - - return P->screenbuffer->pixels; -} - -void fische_free(struct fische* handle) -{ - if (!handle) - return; - - struct _fische__internal_* P = static_cast<_fische__internal_*>(handle->priv); - - if (handle->priv) - { - // 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)); - - 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); - - free(handle->priv); - } - - free(handle); -} - -void fische_audiodata(struct fische* handle, const void* data, size_t data_size) -{ - struct _fische__internal_* P = static_cast<_fische__internal_*>(handle->priv); - - if (NULL == P->audiobuffer) - return; - - fische__audiobuffer_lock(P->audiobuffer); - fische__audiobuffer_insert(P->audiobuffer, data, data_size); - fische__audiobuffer_unlock(P->audiobuffer); -} diff --git a/src/fische/analyst.cpp b/src/fische/analyst.cpp new file mode 100644 index 0000000..e62c222 --- /dev/null +++ b/src/fische/analyst.cpp @@ -0,0 +1,173 @@ +/* + * 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 "analyst.hpp" + +#include "fische.hpp" + +#include +#include + +namespace fische +{ + +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 CAnalyst::GuessFramesPerBeat() +{ + uint_fast16_t gap_history_sorted[BEAT_GAP_HISTORY_SIZE]; + + 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.0; + int count = 0; + + uint16_t value; + for (size_t i = 0; i < 30; ++i) + { + value = gap_history_sorted[i] - guess; + if (abs(value) <= 2) + { + result += gap_history_sorted[i]; + ++count; + } + } + + if (count == 0) + return 0.0; + + return result / count; +} + +double CAnalyst::GetAudioLevel(const double* data, size_t data_size) +{ + double E = 0.0; + + for (size_t i = 0; i < data_size; ++i) + { + E += fabs(*(data + i)); + } + + if (E <= 0) + E = 1e-9; + E /= data_size; + + return log10(E) * 10; +} + +int_fast8_t CAnalyst::Analyse(const double* data, size_t size) +{ + if (!size) + return -1; + + const double dezibel{GetAudioLevel(data, size * 2)}; + + if (m_moving_avg_30 == 0) + m_moving_avg_30 = dezibel; + else + m_moving_avg_30 = m_moving_avg_30 * 0.9667 + dezibel * 0.0333; + + m_std_dev = m_std_dev * 0.9667 + fabs(dezibel - m_moving_avg_30) * 0.0333; + + const uint_fast32_t frameno = m_fische->GetFrameCounter(); + if ((frameno - m_last_beat_frame) > 90) + { + m_frames_per_beat = 0; + m_beat_gap_history.fill('\0'); + m_bghist_head = 0; + } + + m_relative_energy = m_moving_avg_03 / m_moving_avg_30; + + double relative_intensity = 0.0; + double new_frames_per_beat; + + switch (m_state) + { + case WAITING: + // don't bother if intensity too low + if (dezibel < m_moving_avg_30 + m_std_dev) + break; + + // initialisation fallbacks + if (m_std_dev == 0) + relative_intensity = 1.0; // avoid div by 0 + else + relative_intensity = (dezibel - m_moving_avg_30) / m_std_dev; + + if (m_intensity_moving_avg == 0) + m_intensity_moving_avg = relative_intensity; // initial assignment + else + m_intensity_moving_avg = m_intensity_moving_avg * 0.95 + relative_intensity * 0.05; + + // update intensity standard deviation + m_intensity_std_dev = + m_intensity_std_dev * 0.95 + fabs(m_intensity_moving_avg - relative_intensity) * 0.05; + + // we DO have a beat + m_state = FISCHE_BEAT; + + // update beat gap history + 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 + m_last_beat_frame = frameno; + + // reset the short-term moving average + m_moving_avg_03 = dezibel; + + // try a guess at the tempo + 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 + m_frames_per_beat = new_frames_per_beat; + + // return based on relative beat intensity + if (relative_intensity > m_intensity_moving_avg + 3 * m_intensity_std_dev) + return 4; + if (relative_intensity > m_intensity_moving_avg + 2 * m_intensity_std_dev) + return 3; + if (relative_intensity > m_intensity_moving_avg + 1 * m_intensity_std_dev) + return 2; + + return 1; + + case FISCHE_BEAT: + case MAYBEWAITING: + // update short term moving average + m_moving_avg_03 = m_moving_avg_03 * 0.6667 + dezibel * 0.3333; + + // needs to be low enough twice to exit BEAT state + if (m_moving_avg_03 < m_moving_avg_30 + m_std_dev) + { + m_state = m_state == MAYBEWAITING ? WAITING : MAYBEWAITING; + return 0; + } + } + + // report level too low + if (dezibel < -45.0) + return -1; + return 0; +} + +} // namespace fische diff --git a/src/fische/analyst.hpp b/src/fische/analyst.hpp new file mode 100644 index 0000000..2a4b0a5 --- /dev/null +++ b/src/fische/analyst.hpp @@ -0,0 +1,60 @@ +/* + * 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 +#include +#include + +namespace fische +{ + +class CFische; + +class CAnalyst +{ +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}; +}; + +} // namespace fische diff --git a/src/fische/audiobuffer.cpp b/src/fische/audiobuffer.cpp new file mode 100644 index 0000000..4525724 --- /dev/null +++ b/src/fische/audiobuffer.cpp @@ -0,0 +1,159 @@ +/* + * 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 "audiobuffer.hpp" + +#include +#include +#include +#include +#include + +namespace fische +{ + +CAudioBuffer::CAudioBuffer(FISCHE_AUDIOFORMAT format) : m_format(format) +{ +} + +CAudioBuffer::~CAudioBuffer() +{ + Lock(); + + free(m_buffer); +} + +void CAudioBuffer::Insert(const void* data, size_t size) +{ + if (m_buffer_size > 44100) + return; + + uint_fast8_t width = 1; + + switch (m_format) + { + case FISCHE_AUDIOFORMAT_DOUBLE: + width = 8; + break; + case FISCHE_AUDIOFORMAT_FLOAT: + case FISCHE_AUDIOFORMAT_S32: + case FISCHE_AUDIOFORMAT_U32: + width = 4; + break; + case FISCHE_AUDIOFORMAT_S16: + case FISCHE_AUDIOFORMAT_U16: + width = 2; + } + + 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))); + + for (size_t i = 0; i < size / width; ++i) + { + double* dest = (m_buffer + old_bufsize + i); + + switch (m_format) + { + case FISCHE_AUDIOFORMAT_FLOAT: + *dest = *(static_cast(data) + i); + break; + case FISCHE_AUDIOFORMAT_DOUBLE: + *dest = *(static_cast(data) + i); + break; + case FISCHE_AUDIOFORMAT_S32: + *dest = *(static_cast(data) + i); + *dest /= INT32_MAX; + break; + case FISCHE_AUDIOFORMAT_U32: + *dest = *(static_cast(data) + i); + *dest -= INT32_MAX; + *dest /= INT32_MAX; + break; + case FISCHE_AUDIOFORMAT_S16: + *dest = *(static_cast(data) + i); + *dest /= INT16_MAX; + break; + case FISCHE_AUDIOFORMAT_U16: + *dest = *(static_cast(data) + i); + *dest -= INT16_MAX; + *dest /= INT16_MAX; + break; + case FISCHE_AUDIOFORMAT_S8: + *dest = *(static_cast(data) + i); + *dest /= INT8_MAX; + break; + case FISCHE_AUDIOFORMAT_U8: + *dest = *(static_cast(data) + i); + *dest /= INT8_MAX; + *dest /= INT8_MAX; + break; + } + } + + ++m_puts; +} + +void CAudioBuffer::Get() +{ + if (m_buffer_size == 0) + return; + + double* new_start = m_buffer + m_last_get * 2; + m_buffer_size -= m_last_get * 2; + + // pop used data off front + memmove(m_buffer, new_start, m_buffer_size * sizeof(double)); + m_buffer = static_cast(realloc(m_buffer, m_buffer_size * sizeof(double))); + + if (!m_puts) + return; + + // fallback for first get + if (m_gets == 0) + { + m_gets = 3; + m_puts = 1; + } + + // get/put ratio + double d_ratio = static_cast(m_gets) / m_puts; + uint_fast8_t ratio = static_cast(ceil(d_ratio)); + + // how many samples to return + size_t n_samples = m_buffer_size / 2 / ratio; + + // set return data size and remember + m_front_sample_count = n_samples; + m_back_sample_count = n_samples; + m_last_get = n_samples; + + // set export buffer + m_front_samples = m_buffer; + m_back_samples = m_buffer + m_buffer_size - n_samples * 2; + + // increment get counter + ++m_gets; +} + +void CAudioBuffer::Lock() +{ + bool expected = false; + while (m_is_locked.compare_exchange_strong(expected, true) == false) + { + expected = false; + std::this_thread::sleep_for(std::chrono::microseconds(1)); + } +} + +void CAudioBuffer::Unlock() +{ + m_is_locked = false; +} + +} // namespace fische diff --git a/src/fische/audiobuffer.hpp b/src/fische/audiobuffer.hpp new file mode 100644 index 0000000..aba3a12 --- /dev/null +++ b/src/fische/audiobuffer.hpp @@ -0,0 +1,56 @@ +/* + * 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 CFische; + +class CAudioBuffer +{ +public: + CAudioBuffer(FISCHE_AUDIOFORMAT format); + ~CAudioBuffer(); + + void Insert(const void* data, size_t size); + void Lock(); + void Unlock(); + void Get(); + + inline const double* FrontSamples() const { return m_front_samples; } + inline size_t FrontSampleCount() const { return m_front_sample_count; } + + inline const double* BackSamples() const { return m_back_samples; } + inline size_t BackSampleCount() const { return m_back_sample_count; } + +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 new file mode 100644 index 0000000..ffbc265 --- /dev/null +++ b/src/fische/blurengine.cpp @@ -0,0 +1,152 @@ +/* + * 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 "blurengine.hpp" + +#include "fische.hpp" +#include "screenbuffer.hpp" + +#include + +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)); + } + + uint32_t* t = m_destinationbuffer; + m_destinationbuffer = m_sourcebuffer; + m_sourcebuffer = t; + m_fische->GetScreenbuffer()->SetPixels(t); +} + +void CBlurEngine::ThreadWorker(_blurworker_* params) +{ + 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]; + + const uint32_t two_lines = 2 * m_width; + const uint32_t one_line = m_width; + const uint32_t two_columns = 2; + + 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 = m_sourcebuffer; + uint32_t* source_pixel; + + uint32_t* destination_pixel = m_destinationbuffer + y_start * m_width; + + 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 < 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) * 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 + source_component[0] = (*(source_pixel + one_line - two_columns) >> 2) & 0x3f3f3f3f; + source_component[1] = (*(source_pixel + one_line + two_columns) >> 2) & 0x3f3f3f3f; + source_component[2] = (*(source_pixel - two_lines) >> 2) & 0x3f3f3f3f; + source_component[3] = (*(source_pixel) >> 2) & 0x3f3f3f3f; + + // add those four components and write to the destination + // increment destination pointer + *(destination_pixel++) = + source_component[0] + source_component[1] + source_component[2] + source_component[3]; + + // increment vector source pointer + vector_pointer += 2; + } + } + + // mark work as done + params->work = false; + } +} + +} // namespace fische diff --git a/src/fische/blurengine.hpp b/src/fische/blurengine.hpp new file mode 100644 index 0000000..a54b1fe --- /dev/null +++ b/src/fische/blurengine.hpp @@ -0,0 +1,54 @@ +/* + * 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 +#include +#include +#include + +namespace fische +{ + +class CFische; + +class CBlurEngine +{ +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/cpudetect.cpp b/src/fische/cpudetect.c similarity index 95% rename from src/cpudetect.cpp rename to src/fische/cpudetect.c index 48cb7d2..e8728dd 100644 --- a/src/cpudetect.cpp +++ b/src/fische/cpudetect.c @@ -1,13 +1,12 @@ /* - * 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 * 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 new file mode 100644 index 0000000..f9a2c63 --- /dev/null +++ b/src/fische/fische.cpp @@ -0,0 +1,387 @@ +/* + * 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" + +#include "analyst.hpp" +#include "audiobuffer.hpp" +#include "blurengine.hpp" +#include "cpudetect.h" +#include "screenbuffer.hpp" +#include "vector.hpp" +#include "vectorfield.hpp" +#include "wavepainter.hpp" + +#include +#include +#include +#include + +namespace fische +{ + +CFische::CFische() : m_init_progress(0), m_init_cancel(false) +{ + 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"; +} + +CFische::~CFische() +{ + // 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(); +} + +bool CFische::Start() +{ + // plausibility checks + if ((used_cpus > 8) || (used_cpus < 1)) + { + error_text = "CPU count out of range (1 <= used_cpus <= 8)"; + return false; + } + + if (audio_format >= _FISCHE__AUDIOFORMAT_LAST_) + { + error_text = "audio format invalid"; + return false; + } + + if (line_style >= _FISCHE__LINESTYLE_LAST_) + { + error_text = "line style invalid"; + return false; + } + + if (frame_counter != 0) + { + error_text = "frame counter garbled"; + return false; + } + + if ((amplification < -10) || (amplification > 10)) + { + error_text = "amplification value out of range (-10 <= amplification <= 10)"; + return false; + } + + if ((height < 16) || (height > 2048)) + { + error_text = "height value out of range (16 <= height <= 2048)"; + return false; + } + + if ((width < 16) || (width > 2048)) + { + error_text = "width value out of range (16 <= width <= 2048)"; + return false; + } + + if (width % 4 != 0) + { + error_text = "width value invalid (must be a multiple of four)"; + return false; + } + + if (pixel_format >= _FISCHE__PIXELFORMAT_LAST_) + { + error_text = "pixel format invalid"; + return false; + } + + if ((scale < 0.5) || (scale > 2)) + { + error_text = "scale value out of range (0.5 <= scale <= 2.0)"; + return false; + } + + if (blur_mode >= _FISCHE__BLUR_LAST_) + { + error_text = "blur option invalid"; + return false; + } + + m_init_progress = -1; + + 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(&CFische::ThreadCreateVectors, this).detach(); + std::thread(&CFische::ThreadIndicateBusy, this).detach(); + + return true; +} + +uint32_t* CFische::Render() +{ + // only if init completed + if (m_init_progress >= 1 && m_vectorfield) + { + // analyse sound data + m_audiobuffer->Lock(); + m_audiobuffer->Get(); + int_fast8_t analysis = + m_analyst->Analyse(m_audiobuffer->BackSamples(), m_audiobuffer->BackSampleCount()); + + // act accordingly + if (nervous_mode) + { + if (analysis >= 2) + m_wavepainter->ChangeShape(); + if (analysis >= 1) + m_vectorfield->Change(); + } + else + { + if (analysis >= 1) + m_wavepainter->ChangeShape(); + if (analysis >= 2) + m_vectorfield->Change(); + } + + if (analysis >= 3) + { + m_wavepainter->Beat(m_analyst->GetFramesPerBeat()); + } + if (analysis >= 4) + { + OnBeat(m_analyst->GetFramesPerBeat()); + } + + m_audio_valid = analysis >= 0; + + m_wavepainter->ChangeColor(m_analyst->GetFramesPerBeat(), m_analyst->GetRelativeEnergy()); + + // wait for blurring to be finished + // and swap buffers + m_screenbuffer->Lock(); + m_blurengine->SwapBuffers(); + m_screenbuffer->Unlock(); + + // draw waves + if (m_audio_valid) + m_wavepainter->Paint(m_audiobuffer->FrontSamples(), m_audiobuffer->FrontSampleCount()); + + // start blurring for the next frame + m_blurengine->Blur(m_vectorfield->Field()); + + m_audiobuffer->Unlock(); + } + + frame_counter++; + + return m_screenbuffer->Pixels(); +} + +void CFische::AudioData(const void* data, size_t data_size) +{ + if (m_audiobuffer == nullptr) + return; + + m_audiobuffer->Lock(); + m_audiobuffer->Insert(data, data_size); + m_audiobuffer->Unlock(); +} + +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)) + { + + 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::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, 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(); + } +} + +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)) + { + error_text = "width and height out of range (16 <= width, height <= 2048)"; + return false; + } + + this->width = width; + this->height = height; + return true; +} + +bool CFische::SetUsedCPUs(uint8_t used_cpus) +{ + 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; +} + +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; +} + +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.h b/src/fische/fische.h similarity index 83% rename from src/fische.h rename to src/fische/fische.h index 5979da2..1714067 100644 --- a/src/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 @@ -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,31 @@ 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 */ + * 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); - /* 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..e09173b --- /dev/null +++ b/src/fische/fische.hpp @@ -0,0 +1,118 @@ +/* + * 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(); } + size_t GetMaxVectorsSize() const; + + 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_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); +} diff --git a/src/fische/screenbuffer.cpp b/src/fische/screenbuffer.cpp new file mode 100644 index 0000000..9836b80 --- /dev/null +++ b/src/fische/screenbuffer.cpp @@ -0,0 +1,151 @@ +/* + * 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 "screenbuffer.hpp" + +#include "fische.hpp" + +#include +#include +#include +#include +#include + +namespace fische +{ + +CScreenBuffer::CScreenBuffer(const CFische* parent) + : m_fische(parent), m_width(parent->GetWidth()), m_height(parent->GetHeight()) + +{ + m_pixels = new uint32_t[m_width * m_height](); + + switch (parent->GetPixelFormat()) + { + case FISCHE_PIXELFORMAT_0xAABBGGRR: + m_alpha_shift = 24; + m_blue_shift = 16; + m_green_shift = 8; + m_red_shift = 0; + break; + case FISCHE_PIXELFORMAT_0xAARRGGBB: + m_alpha_shift = 24; + m_blue_shift = 0; + m_green_shift = 8; + m_red_shift = 16; + break; + case FISCHE_PIXELFORMAT_0xBBGGRRAA: + m_alpha_shift = 0; + m_blue_shift = 24; + m_green_shift = 16; + m_red_shift = 8; + break; + case FISCHE_PIXELFORMAT_0xRRGGBBAA: + m_alpha_shift = 0; + m_blue_shift = 8; + m_green_shift = 16; + m_red_shift = 24; + break; + } +} + +CScreenBuffer::~CScreenBuffer() +{ + Lock(); + + delete[] m_pixels; +} + +void CScreenBuffer::Lock() +{ + bool expected = false; + while (m_is_locked.compare_exchange_strong(expected, true) == false) + { + expected = false; + std::this_thread::sleep_for(std::chrono::microseconds(1)); + } +} + +void CScreenBuffer::Unlock() +{ + m_is_locked = false; +} + +void CScreenBuffer::Line(double x1, double y1, double x2, double y2, uint32_t color) +{ + 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; + double dir_y = (y2 < y1) ? -1 : 1; + + if (!diff_x && !diff_y) + return; + + uint32_t half_alpha_mask; + + 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 << m_red_shift) + (0xff << m_green_shift) + (0xff << m_blue_shift) + + (0x7f << m_alpha_shift); + + + if (diff_x > diff_y) + { + 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); + + if ((x < 0) || (x >= m_width) || (y < 0) || (y >= m_height)) + continue; + + if (m_fische->GetLineStyle() != FISCHE_LINESTYLE_THIN) + { + y++; + if (!(y < 0) && !(y >= m_height)) + *(m_pixels + y * m_width + x) = color & half_alpha_mask; + y -= 2; + if ((y < 0) || (y >= m_height)) + continue; + *(m_pixels + y * m_width + x) = color & half_alpha_mask; + y++; + } + + *(m_pixels + y * m_width + x) = color; + } + } + else + { + 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); + + if ((x < 0) || (x >= m_width) || (y < 0) || (y >= m_height)) + continue; + + if (m_fische->GetLineStyle() != FISCHE_LINESTYLE_THIN) + { + x++; + if (!(x < 0) && !(x >= m_width)) + *(m_pixels + y * m_width + x) = color & half_alpha_mask; + + x -= 2; + if ((x < 0) || (x >= m_width)) + continue; + *(m_pixels + y * m_width + x) = color & half_alpha_mask; + + x++; + } + + *(m_pixels + y * m_width + x) = color; + } + } +} + +} // namespace fische diff --git a/src/fische/screenbuffer.hpp b/src/fische/screenbuffer.hpp new file mode 100644 index 0000000..f4a657b --- /dev/null +++ b/src/fische/screenbuffer.hpp @@ -0,0 +1,52 @@ +/* + * 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 +#include + +namespace fische +{ + +class CFische; + +class CScreenBuffer +{ +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; +}; + +} // namespace fische diff --git a/src/vector.cpp b/src/fische/vector.cpp similarity index 52% rename from src/vector.cpp rename to src/fische/vector.cpp index e9a7e7b..133482f 100644 --- a/src/vector.cpp +++ b/src/fische/vector.cpp @@ -1,38 +1,48 @@ /* - * 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 * See LICENSE.md for more information. */ -#include "vector.h" +#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); + if (l < 1e-9) + { + vector r = {0.0, 0.0}; + return r; + } + 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 +51,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 +62,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 +153,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 new file mode 100644 index 0000000..a2be93b --- /dev/null +++ b/src/fische/vector.hpp @@ -0,0 +1,49 @@ +/* + * 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 + +namespace fische +{ + +struct _vector_ +{ + double x; + double y; +}; + +typedef struct _vector_ vector; +typedef struct _vector_ point; + +enum +{ + _VECTOR_LEFT_, + _VECTOR_RIGHT_ +}; + +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 new file mode 100644 index 0000000..92f3842 --- /dev/null +++ b/src/fische/vectorfield.cpp @@ -0,0 +1,439 @@ +/* + * 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. + */ + +#define _USE_MATH_DEFINES + +#include "vectorfield.hpp" + +#include "fische.hpp" + +#include +#include + +#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 >= 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(VECTOR_N_FIELDS * m_fieldsize)); + if (m_fields == nullptr) + { + progress = 0.0; + cancel = true; + m_cancelled = true; + return; + } + + m_n_fields = VECTOR_N_FIELDS; + + for (uint_fast8_t i = 0; i < VECTOR_N_FIELDS; ++i) + { + if (cancel) + { + m_cancelled = true; + break; + } + + FillField(i); + progress = (i + 1); + progress /= VECTOR_N_FIELDS; + } + + // If we use stored vectors and was not present then store it now + if (useStoredVectors && !m_cancelled) + m_fische->WriteVectors(m_fields, m_n_fields * m_fieldsize); + + progress = 1.0; + + m_field = m_fields; +} + +CVectorField::~CVectorField() +{ + free(m_fields); +} + +void CVectorField::Change() +{ + if (!m_fields) + return; + + uint16_t* n = m_field; + while (n == m_field) + { + m_field = m_fields + (rand_r(&m_rand_seed) % m_n_fields) * m_width * m_height; + } +} + +inline void CVectorField::Randomize(fische::vector* vec) +{ + vec->x += rand_r(&m_rand_seed) % 3; + vec->x -= 1; + vec->y += rand_r(&m_rand_seed) % 3; + vec->y -= 1; +} + + +inline void CVectorField::Validate(fische::vector* vec, double x, double y) +{ + while (x + vec->x < 2) + vec->x += 1; + while (x + vec->x > m_width - 3) + vec->x -= 1; + while (y + vec->y < 2) + vec->y += 1; + while (y + vec->y > m_height - 3) + vec->y -= 1; +} + +void CVectorField::FillField(uint_fast8_t fieldno) +{ + 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]; + + 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; + + 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(); + } +} + +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; + rvec.x = x; + rvec.x -= m_center_x; + rvec.y = y; + rvec.y -= m_center_y; + + fische::vector e = fische::vector_single(&rvec); + fische::vector n = fische::vector_normal(&e); + + 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 - 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); + + 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 - 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); + + double r_right = fische::vector_length(&rvec_right) / m_dimension; + + 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; + switch (fieldno) + { + case 0: + // linear vectors showing away from a horizontal mirror axis + v.x = 0; + 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 < 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]); + break; + + case 3: + // tangential vectors (right) + v = n; + 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]); + v = e; + fische::vector_mul(&v, -r * speed * corr[fieldno]); + fische::vector_add(&v, &_v1); + break; + } + + case 5: + { + // tree rings + double dv = cos(M_PI * 24 * r); + v = e; + fische::vector_mul(&v, speed * 0.33 * corr[fieldno] * dv); + break; + } + + case 6: + { + // hyperbolic vectors + v.x = e.y; + v.y = e.x; + fische::vector_mul(&v, -r * speed * corr[fieldno]); + break; + } + + case 7: + // purely random + v.x = rand_r(&m_rand_seed) % (int_fast32_t)(2 * speed * corr[fieldno] + 1) - + (speed * corr[fieldno]); + v.y = rand_r(&m_rand_seed) % (int_fast32_t)(2 * speed * corr[fieldno] + 1) - + (speed * corr[fieldno]); + break; + + case 8: + { + // sphere + double dv = cos(M_PI * r); + v = e; + fische::vector_mul(&v, -r * dv * speed * corr[fieldno]); + break; + } + + case 9: + { + // sine distortion + double dv = sin(M_PI * 8 * r); + v = e; + 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]); + v = e; + fische::vector_mul(&v, r * speed * corr[fieldno]); + fische::vector_add(&v, &_v1); + if (r * m_dimension < 10) + { + v.x = 0; + v.y = 0; + } + break; + } + + case 11: + { + // circular waves + double dim = pow(11 * M_PI, 2); + double _r = r * dim; + v = e; + fische::vector_mul(&v, + -speed * corr[fieldno] * + sqrt(1.04 - pow(cos(sqrt(_r)), 2) + 0.25 * pow(sin(sqrt(_r)), 2))); + break; + } + + case 12: + // spinning CD + v = n; + if (fabs(r - 0.25) < 0.15) + fische::vector_mul(&v, r * speed * corr[fieldno]); + else + fische::vector_mul(&v, -r * speed * corr[fieldno]); + break; + + case 13: + { + // three spinning disks + double rt = 0.3; + if (r < rt * 1.2) + { + v = n; + 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]); + } + else if (r_right < rt) + { + v = n_right; + fische::vector_mul(&v, r_right * speed * corr[fieldno]); + } + else + { + v.x = 0; + v.y = 0; + } + break; + } + + 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]); + v = e; + 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]); + v = n; + fische::vector_mul(&v, (2 - r) * speed * corr[fieldno]); + fische::vector_add(&v, &_v1); + fische::vector_add(&v, &_v2); + break; + } + + case 16: + { + // lenses effect + double _r = r * 8 * M_PI; + 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); + break; + } + + case 17: + { + // lenses effect + double _r = r * 24 * M_PI; + 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); + break; + } + + case 18: + { + // fan 1 + v = e; + double angle = fische::vector_angle(&e); + fische::vector_mul(&v, -speed * corr[fieldno] * sin(8 * angle)); + break; + } + + case 19: + { + // fan 1 + v = e; + double angle = fische::vector_angle(&e); + fische::vector_mul(&v, -speed * corr[fieldno] * (1.1 + sin(8 * angle))); + break; + } + + default: + // index too high. return nothing. + return; + } + + if (m_fische->GetBlurMode() == FISCHE_BLUR_FUZZY) + Randomize(&v); + + Validate(&v, x, y); + + *vector = fische::vector_to_uint16(&v); + } + } +} + +} // namespace fische diff --git a/src/fische/vectorfield.hpp b/src/fische/vectorfield.hpp new file mode 100644 index 0000000..96e910c --- /dev/null +++ b/src/fische/vectorfield.hpp @@ -0,0 +1,59 @@ +/* + * 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 "vector.hpp" + +#include +#include + +#define VECTOR_N_FIELDS 20 + +namespace fische +{ + +class CFische; + +class CVectorField +{ +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 new file mode 100644 index 0000000..9bc9a17 --- /dev/null +++ b/src/fische/wavepainter.cpp @@ -0,0 +1,300 @@ +/* + * 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. + */ + +#define _USE_MATH_DEFINES + +#include "wavepainter.hpp" + +#include "fische.hpp" +#include "screenbuffer.hpp" +#include "vector.hpp" + +#include +#include + +namespace fische +{ + +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 CWavePainter::Paint(const double* data, size_t size) +{ + if (!size) + return; + + // rotation + if (m_is_rotating) + { + m_angle += m_rotation_increment; + if ((m_angle > 2 * M_PI) || (m_angle < -2 * M_PI)) + { + m_angle = 0; + m_is_rotating = false; + } + } + + // only init fische scale once + static double f_scale = 0; + if (f_scale == 0) + f_scale = m_fische->GetScale(); + + // necessary parameters + double dim = (m_height < m_width) ? m_height : m_width; + dim *= f_scale; + double factor = pow(10, m_fische->GetAmplification() / 10); + double scale = 6 / dim / factor; + + // alpha saturation fix + 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 (m_shape) + { + case 0: + { + 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(m_angle); + base1.y = center.y + (dim / 6) * cos(m_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 _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, 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, 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, static_cast(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); + + // draw both lines + fische::point base_p1 = start1; + fische::point base_p2 = start2; + + 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); + + 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; + } + + // circular shape + case 1: + { + double f = cos(M_PI / 3 + 2 * m_angle) + 0.5; + double e = 1; + + for (size_t i = 0; i < size - 1; i++) + { + double incr = static_cast(i); + + // calculate angles for this and the next sound sample + 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; + + 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); + + m_sbuf->Line(x1, y1, x2, y2, m_color_1); + + // the second line will be exactly on the + // opposite side of the circle + phi1 += M_PI; + phi2 += M_PI; + + r1 = dim / 4 + *(data + 1 + 2 * i) / scale; + r2 = dim / 4 + *(data + 1 + 2 * (i + 1)) / scale; + + 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); + + m_sbuf->Line(x1, y1, x2, y2, m_color_2); + } + return; + } + } +} + +void CWavePainter::Beat(double frames_per_beat) +{ + if (!m_is_rotating) + { + if (frames_per_beat != 0) + { + 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 CWavePainter::ChangeColor(double frames_per_beat, double energy) +{ + uint32_t full_alpha = 0xff << m_sbuf->AlphaShift(); + + if (!frames_per_beat && !energy) + { + m_color_1 = (rand() % 0xffffffff) | full_alpha; + m_color_2 = (~m_color_1) | full_alpha; + } + + if (!frames_per_beat) + return; + + double hue = frames_per_beat / 2; + while (hue >= 6) + hue -= 6; + + double sv = (energy > 1) ? 1 : pow(energy, 4); + double x = sv * (1 - fabs((int_fast32_t)hue % 2 - 1)); + + double r, g, b; + + switch ((int_fast32_t)hue) + { + case 0: + r = sv; + g = x; + b = 0; + break; + case 1: + r = x; + g = sv; + b = 0; + break; + case 2: + r = 0; + g = sv; + b = x; + break; + case 3: + r = 0; + g = x; + b = sv; + break; + case 4: + r = x; + g = 0; + b = sv; + break; + default: + case 5: + r = sv; + g = 0; + b = x; + } + + 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)); + + m_color_1 = (blue << m_sbuf->BlueShift()) + + (green << m_sbuf->GreenShift()) + + (red << m_sbuf->RedShift()) + + (0xff << m_sbuf->AlphaShift()); + + m_color_2 = (~m_color_1) | full_alpha; +} + +void CWavePainter::ChangeShape() +{ + if (m_is_rotating) + return; + 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 new file mode 100644 index 0000000..2514270 --- /dev/null +++ b/src/fische/wavepainter.hpp @@ -0,0 +1,49 @@ +/* + * 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 +#include + +namespace fische +{ + +class CFische; +class CScreenBuffer; + +class CWavePainter +{ +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}; +}; + +} // namespace fische diff --git a/src/fische_internal.h b/src/fische_internal.h deleted file mode 100644 index 8b59a96..0000000 --- a/src/fische_internal.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2005-2022 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.h" -#include "audiobuffer.h" -#include "blurengine.h" -#include "fische.h" -#include "screenbuffer.h" -#include "vector.h" -#include "vectorfield.h" -#include "wavepainter.h" - -#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) - -uint_fast8_t _fische__cpu_detect_(); - - -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/fishbmc_addon.cpp b/src/fishbmc_addon.cpp index 8eddb8e..b3df0eb 100644 --- a/src/fishbmc_addon.cpp +++ b/src/fishbmc_addon.cpp @@ -1,14 +1,16 @@ /* - * 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 * See LICENSE.md for more information. */ +#define _USE_MATH_DEFINES + #include "fishbmc_addon.h" -#include "fische_internal.h" +#include "md5.h" #include #include @@ -21,16 +23,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,44 +47,34 @@ 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, - int samplesPerSec, - int bitsPerSample, - const std::string& songName) +bool CVisualizationFishBMC::AudioStart(int channels, int samplesPerSec, int bitsPerSample) { 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 +98,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; @@ -123,7 +109,7 @@ bool CVisualizationFishBMC::Start(int channels, return true; } -void CVisualizationFishBMC::Stop() +void CVisualizationFishBMC::AudioStop() { if (!m_startOK) return; @@ -147,7 +133,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 +146,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 +159,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 +167,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 +183,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 +218,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 +260,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 +283,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,83 +366,95 @@ void CVisualizationFishBMC::finish_render() glDisableVertexAttribArray(m_aVertexLoc); } -void CVisualizationFishBMC::on_beat(void* handler, double frames_per_beat) -{ - 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) +void CVisualizationFishBMC::WriteVectors(const void* data, size_t bytes) { - if (!handler) + if (!data) return; - CVisualizationFishBMC* thisClass = static_cast(handler); + kodi::vfs::CreateDirectory(kodi::addon::GetUserPath("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"); - std::ostringstream filename; - filename << dirname << "/" << thisClass->m_fische->height; + 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; + } + + const std::string hash = hashing::md5::hash_as_hex(data, bytes); + kodi::addon::SetSettingString("file_md5", hash); } -size_t CVisualizationFishBMC::read_vectors(void* handler, void** data) +size_t CVisualizationFishBMC::ReadVectors(void** data) { - if (!handler) + 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 (hashWanted.empty() || !kodi::vfs::FileExists(filename)) return 0; - CVisualizationFishBMC* thisClass = static_cast(handler); - - std::string dirname = kodi::addon::GetUserPath("data"); - kodi::vfs::CreateDirectory(dirname); + kodi::vfs::CFile vectorsfile; + try + { + // open the file + if (!vectorsfile.OpenFile(filename)) + throw std::string("Failed to open file"); - std::ostringstream filename; - filename << dirname << "/" << thisClass->m_fische->height; + ssize_t size = vectorsfile.GetLength(); + if (size <= 0 || size > static_cast(CFische::GetMaxVectorsSize())) + throw std::string("Unable to get correct size of file"); - // open the file - std::fstream vectorsfile(filename.str().c_str(), std::fstream::in); - if (!vectorsfile.good()) - return 0; + *data = malloc(size_t(size)); + if (*data == nullptr) + throw std::string("Failed to allocate memory"); - vectorsfile.seekg(0, std::ios::end); - size_t n = vectorsfile.tellg(); - vectorsfile.seekg(0, std::ios::beg); + 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"); + } - *data = malloc(n); - vectorsfile.read(reinterpret_cast(*data), n); - vectorsfile.close(); + 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 n; + return sizeRead; + } + catch (const std::string& error_text) + { + kodi::Log(ADDON_LOG_ERROR, "File read \"%s\": %s", filename.c_str(), error_text.c_str()); + return 0; + } } -void CVisualizationFishBMC::delete_vectors() +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); } } diff --git a/src/fishbmc_addon.h b/src/fishbmc_addon.h index 767bfc6..3b44f1f 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 @@ -8,7 +8,7 @@ #pragma once -#include "fische.h" +#include "fische/fische.hpp" #include #include @@ -33,17 +33,15 @@ 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, - 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, @@ -52,6 +50,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 +67,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 +88,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; 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/src/screenbuffer.cpp b/src/screenbuffer.cpp deleted file mode 100644 index 9afee42..0000000 --- a/src/screenbuffer.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2005-2022 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_internal.h" - -#include -#include -#include -#include -#include - -struct fische__screenbuffer* fische__screenbuffer_new(struct fische* parent) -{ - 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; - - retval->pixels = static_cast(malloc(P->width * P->height * sizeof(uint32_t))); - memset(retval->pixels, '\0', P->width * P->height * sizeof(uint32_t)); - - switch (parent->pixel_format) - { - - case FISCHE_PIXELFORMAT_0xAABBGGRR: - P->alpha_shift = 24; - P->blue_shift = 16; - P->green_shift = 8; - P->red_shift = 0; - break; - - case FISCHE_PIXELFORMAT_0xAARRGGBB: - P->alpha_shift = 24; - P->blue_shift = 0; - P->green_shift = 8; - P->red_shift = 16; - break; - - case FISCHE_PIXELFORMAT_0xBBGGRRAA: - P->alpha_shift = 0; - P->blue_shift = 24; - P->green_shift = 16; - P->red_shift = 8; - break; - - case FISCHE_PIXELFORMAT_0xRRGGBBAA: - P->alpha_shift = 0; - P->blue_shift = 8; - P->green_shift = 16; - P->red_shift = 24; - break; - } - - return retval; -} - -void fische__screenbuffer_free(struct fische__screenbuffer* self) -{ - if (!self) - return; - - fische__screenbuffer_lock(self); - - free(self->priv); - free(self->pixels); - free(self); -} - -void fische__screenbuffer_lock(struct fische__screenbuffer* self) -{ -#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) - std::this_thread::sleep_for(std::chrono::microseconds(1)); - self->priv->is_locked = 1; -#endif -} - -void fische__screenbuffer_unlock(struct fische__screenbuffer* self) -{ - self->priv->is_locked = 0; -} - -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) -{ - 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; - double dir_y = (y2 < y1) ? -1 : 1; - - if (!diff_x && !diff_y) - return; - - 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); - else - half_alpha_mask = (0xff << P->red_shift) + (0xff << P->green_shift) + (0xff << P->blue_shift) + - (0x7f << P->alpha_shift); - - - if (diff_x > diff_y) - { - - int_fast16_t x; - for (x = x1; x * dir_x <= x2 * dir_x; x += dir_x) - { - - 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)) - continue; - - if (P->fische->line_style != FISCHE_LINESTYLE_THIN) - { - y++; - if (!(y < 0) && !(y >= P->height)) - *(self->pixels + y * P->width + x) = color & half_alpha_mask; - - y -= 2; - if ((y < 0) || (y >= P->height)) - continue; - *(self->pixels + y * P->width + x) = color & half_alpha_mask; - - y++; - } - - *(self->pixels + y * P->width + x) = color; - } - } - else - { - - int_fast16_t y; - for (y = y1; y * dir_y <= y2 * dir_y; y += dir_y) - { - - 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)) - continue; - - if (P->fische->line_style != FISCHE_LINESTYLE_THIN) - { - x++; - if (!(x < 0) && !(x >= P->width)) - *(self->pixels + y * P->width + x) = color & half_alpha_mask; - - x -= 2; - if ((x < 0) || (x >= P->width)) - continue; - *(self->pixels + y * P->width + x) = color & half_alpha_mask; - - x++; - } - - *(self->pixels + y * P->width + x) = color; - } - } -} diff --git a/src/screenbuffer.h b/src/screenbuffer.h deleted file mode 100644 index 025643b..0000000 --- a/src/screenbuffer.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2005-2022 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 SCREENBUFFER_H -#define SCREENBUFFER_H - -#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); - - -struct _fische__screenbuffer_ -{ - 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; -}; - -struct fische__screenbuffer -{ - uint32_t* pixels; - - struct _fische__screenbuffer_* priv; -}; - -#endif diff --git a/src/vector.h b/src/vector.h deleted file mode 100644 index 9fad448..0000000 --- a/src/vector.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2005-2022 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 F_VECTOR_H -#define F_VECTOR_H - -#include -#include - -struct _fische__vector_ -{ - double x; - double y; -}; - -typedef struct _fische__vector_ fische__vector; -typedef struct _fische__vector_ fische__point; - -enum -{ - _FISCHE__VECTOR_LEFT_, - _FISCHE__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); - -#endif diff --git a/src/vectorfield.cpp b/src/vectorfield.cpp deleted file mode 100644 index de4795e..0000000 --- a/src/vectorfield.cpp +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright (C) 2005-2022 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_internal.h" - -#include -#include -#include - -#ifdef DEBUG -#include -#endif - -#define N_FIELDS 20 - -struct field_param -{ - uint16_t* data; - uint_fast8_t number; - uint_fast16_t start_y; - uint_fast16_t end_y; - struct _fische__vectorfield_* vecfield; -}; - -unsigned int rand_seed; - -static inline void _fische__vectorfield_randomize_(fische__vector* vec) -{ - vec->x += rand_r(&rand_seed) % 3; - vec->x -= 1; - vec->y += rand_r(&rand_seed) % 3; - vec->y -= 1; -} - - -static inline void _fische__vectorfield_validate_(struct _fische__vectorfield_* P, - fische__vector* vec, - double x, - double y) -{ - while (x + vec->x < 2) - vec->x += 1; - while (x + vec->x > P->width - 3) - vec->x -= 1; - while (y + vec->y < 2) - vec->y += 1; - while (y + vec->y > P->height - 2) - vec->y -= 1; -} - -void _fische__fill_thread_(struct field_param* params) -{ - uint16_t* field = params->data; - int fieldno = params->number; - struct _fische__vectorfield_* P = params->vecfield; - - uint_fast16_t y; - for (y = params->start_y; y < params->end_y; y++) - { - - uint_fast16_t x; - for (x = 0; x < P->width; x++) - { - - uint16_t* vector = field + x + y * P->width; - - // distance and direction relative to center - fische__vector rvec; - rvec.x = x; - rvec.x -= P->center_x; - rvec.y = y; - rvec.y -= P->center_y; - - fische__vector e = fische__vector_single(&rvec); - fische__vector n = fische__vector_normal(&e); - - double r = fische__vector_length(&rvec) / P->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 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; - - // 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 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 speed = P->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; - 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]; - 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.y = 0; - break; - - case 2: - // radial vectors showing away from the center - v = e; - fische__vector_mul(&v, -r * speed * corr[fieldno]); - break; - - case 3: - // tangential vectors (right) - v = n; - 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]); - v = e; - fische__vector_mul(&v, -r * speed * corr[fieldno]); - fische__vector_add(&v, &_v1); - break; - } - - case 5: - { - // tree rings - double dv = cos(M_PI * 24 * r); - v = e; - fische__vector_mul(&v, speed * 0.33 * corr[fieldno] * dv); - break; - } - - case 6: - { - // hyperbolic vectors - v.x = e.y; - v.y = e.x; - 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) - - (speed * corr[fieldno]); - v.y = rand_r(&rand_seed) % (int_fast32_t)(2 * speed * corr[fieldno] + 1) - - (speed * corr[fieldno]); - break; - - case 8: - { - // sphere - double dv = cos(M_PI * r); - v = e; - fische__vector_mul(&v, -r * dv * speed * corr[fieldno]); - break; - } - - case 9: - { - // sine distortion - double dv = sin(M_PI * 8 * r); - v = e; - 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]); - v = e; - fische__vector_mul(&v, r * speed * corr[fieldno]); - fische__vector_add(&v, &_v1); - if (r * P->dimension < 10) - { - v.x = 0; - v.y = 0; - } - break; - } - - case 11: - { - // circular waves - double dim = pow(11 * M_PI, 2); - double _r = r * dim; - v = e; - fische__vector_mul(&v, - -speed * corr[fieldno] * - sqrt(1.04 - pow(cos(sqrt(_r)), 2) + 0.25 * pow(sin(sqrt(_r)), 2))); - break; - } - - case 12: - // spinning CD - v = n; - if (fabs(r - 0.25) < 0.15) - fische__vector_mul(&v, r * speed * corr[fieldno]); - else - fische__vector_mul(&v, -r * speed * corr[fieldno]); - break; - - case 13: - { - // three spinning disks - double rt = 0.3; - if (r < rt * 1.2) - { - v = n; - 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]); - } - else if (r_right < rt) - { - v = n_right; - fische__vector_mul(&v, r_right * speed * corr[fieldno]); - } - else - { - v.x = 0; - v.y = 0; - } - break; - } - - 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]); - v = e; - 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]); - v = n; - fische__vector_mul(&v, (2 - r) * speed * corr[fieldno]); - fische__vector_add(&v, &_v1); - fische__vector_add(&v, &_v2); - break; - } - - case 16: - { - // lenses effect - double _r = r * 8 * M_PI; - 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); - break; - } - - case 17: - { - // lenses effect - double _r = r * 24 * M_PI; - 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); - break; - } - - case 18: - { - // fan 1 - v = e; - double angle = fische__vector_angle(&e); - fische__vector_mul(&v, -speed * corr[fieldno] * sin(8 * angle)); - break; - } - - case 19: - { - // fan 1 - v = e; - double angle = fische__vector_angle(&e); - fische__vector_mul(&v, -speed * corr[fieldno] * (1.1 + sin(8 * angle))); - break; - } - - default: - // index too high. return nothing. - return; - } - - if (P->fische->blur_mode == FISCHE_BLUR_FUZZY) - _fische__vectorfield_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; - - 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; - } - } - - // 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; - } -} diff --git a/src/vectorfield.h b/src/vectorfield.h deleted file mode 100644 index 60d3fe5..0000000 --- a/src/vectorfield.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2005-2022 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 VECTORFIELD_H -#define VECTORFIELD_H - -#include - -struct fische; -struct _fische__vectorfield_; -struct fische__vectorfield; - - -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_ -{ - 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; -}; - - -struct fische__vectorfield -{ - uint16_t* field; - - struct _fische__vectorfield_* priv; -}; - -#endif diff --git a/src/wavepainter.cpp b/src/wavepainter.cpp deleted file mode 100644 index a36b01a..0000000 --- a/src/wavepainter.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (C) 2005-2022 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_internal.h" - -#include -#include - -#ifdef DEBUG -#include -#endif - -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; -} - -void fische__wavepainter_free(struct fische__wavepainter* self) -{ - if (!self) - return; - - free(self->priv); - free(self); -} - -void fische__wavepainter_paint(struct fische__wavepainter* self, double* data, uint_fast16_t size) -{ - if (!size) - return; - - struct _fische__wavepainter_* P = self->priv; - - // rotation - if (P->is_rotating) - { - P->angle += P->rotation_increment; - if ((P->angle > 2 * M_PI) || (P->angle < -2 * M_PI)) - { - P->angle = 0; - P->is_rotating = 0; - } - } - - // only init fische scale once - static double f_scale = 0; - if (f_scale == 0) - f_scale = P->fische->scale; - - // necessary parameters - double dim = (P->height < P->width) ? P->height : P->width; - dim *= f_scale; - double factor = pow(10, P->fische->amplification / 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); - - switch (P->shape) - { - - case 0: - { - fische__point center; - center.x = P->center_x; - center.y = P->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 base2; - base2.x = P->width / 2 - (dim / 6) * sin(P->angle); - base2.y = P->height / 2 - (dim / 6) * cos(P->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 _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 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_); - - // 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 v2 = end2; - fische__vector_sub(&v2, &start2); - fische__vector_div(&v2, 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); - - // draw both lines - fische__point base_p1 = start1; - fische__point base_p2 = start2; - - uint_fast16_t i; - for (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); - } - return; - } - - // circular shape - case 1: - { - double f = cos(M_PI / 3 + 2 * P->angle) + 0.5; - double e = 1; - - uint_fast16_t i; - for (i = 0; i < size - 1; i++) - { - - double incr = i; - - // calculate angles for this and the next sound sample - double phi1 = M_PI * (0.25 + incr / size) + P->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); - - fische__screenbuffer_line(sbuf, x1, y1, x2, y2, P->color_1); - - // the second line will be exactly on the - // opposite side of the circle - phi1 += M_PI; - phi2 += M_PI; - - 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); - - fische__screenbuffer_line(sbuf, x1, y1, x2, y2, P->color_2); - } - return; - } - } -} - -void fische__wavepainter_beat(struct fische__wavepainter* self, double frames_per_beat) -{ - struct _fische__wavepainter_* P = self->priv; - if (!P->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; - } - } -} - -void fische__wavepainter_change_color(struct fische__wavepainter* self, - double frames_per_beat, - double energy) -{ - struct _fische__wavepainter_* P = self->priv; - - uint32_t full_alpha = 0xff << FISCHE_PRIVATE(P)->screenbuffer->priv->alpha_shift; - - if (!frames_per_beat && !energy) - { - P->color_1 = (rand() % 0xffffffff) | full_alpha; - P->color_2 = (~P->color_1) | full_alpha; - } - - if (!frames_per_beat) - return; - - double hue = frames_per_beat / 2; - while (hue >= 6) - hue -= 6; - - double sv = (energy > 1) ? 1 : pow(energy, 4); - double x = sv * (1 - fabs((int_fast32_t)hue % 2 - 1)); - - double r, g, b; - - switch ((int_fast32_t)hue) - { - case 0: - r = sv; - g = x; - b = 0; - break; - case 1: - r = x; - g = sv; - b = 0; - break; - case 2: - r = 0; - g = sv; - b = x; - break; - case 3: - r = 0; - g = x; - b = sv; - break; - case 4: - r = x; - g = 0; - b = sv; - break; - default: - case 5: - r = sv; - g = 0; - 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); - - 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); - - P->color_2 = (~P->color_1) | full_alpha; -} - -void fische__wavepainter_change_shape(struct fische__wavepainter* self) -{ - struct _fische__wavepainter_* P = self->priv; - - if (P->is_rotating) - return; - int_fast8_t n = P->shape; - while (n == P->shape) - n = rand() % P->n_shapes; - P->shape = n; -} diff --git a/src/wavepainter.h b/src/wavepainter.h deleted file mode 100644 index 8a5c861..0000000 --- a/src/wavepainter.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2005-2022 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 WAVEPAINTER_H -#define WAVEPAINTER_H - -#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); - - -struct _fische__wavepainter_ -{ - 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; -}; - -struct fische__wavepainter -{ - struct _fische__wavepainter_* priv; -}; - -#endif 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@ 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 + + + +