From 8505032307711eb3ed6da510e5bbde904638ca54 Mon Sep 17 00:00:00 2001 From: versustunez Date: Thu, 30 Dec 2021 18:06:04 +0100 Subject: [PATCH] DONT KNOW :D --- .gitignore | 2 + CMakeLists.txt | 10 +- cmake/FindHIDAPI.cmake | 2 +- cmake/FindJACK.cmake | 62 +++- headers/VUtils/Environment.h | 4 +- headers/VUtils/Math.h | 1 + headers/VUtils/Random.h | 23 ++ headers/VUtils/SimplexNoise.h | 55 +++ headers/VulcanoLE/Audio/AudioGrabber.h | 7 +- headers/VulcanoLE/Audio/FFT.h | 2 + headers/VulcanoLE/Audio/Filter.h | 30 ++ headers/VulcanoLE/Audio/Types.h | 1 - headers/VulcanoLE/Colors/ColorHelper.h | 9 +- headers/VulcanoLE/Colors/Type.h | 22 +- headers/VulcanoLE/Keyboards/Vulcan121.h | 9 +- headers/VulcanoLE/Scripts/BassHistory.h | 20 + headers/VulcanoLE/Scripts/Loudness.h | 5 +- headers/VulcanoLE/Scripts/RainbowLine.h | 6 +- headers/VulcanoLE/Scripts/RainbowMap.h | 2 +- headers/VulcanoLE/Scripts/Random.h | 13 +- headers/VulcanoLE/Scripts/Spectral.h | 18 + headers/VulcanoLE/Scripts/Spectrum.h | 30 +- headers/VulcanoLE/Scripts/Strobo.h | 21 ++ headers/VulcanoLE/Scripts/TheUknown.h | 24 ++ headers/VulcanoLE/Visual/VisPlugins.h | 2 +- main.cpp | 4 +- src/VUtils/Math.cpp | 12 + src/VUtils/Random.cpp | 21 ++ src/VUtils/SimplexNoise.cpp | 475 ++++++++++++++++++++++++ src/VulcanoLE/Audio/AudioGrabber.cpp | 36 +- src/VulcanoLE/Audio/Filter.cpp | 71 ++++ src/VulcanoLE/Colors/ColorHelper.cpp | 136 +++++-- src/VulcanoLE/Keyboards/Vulcan121.cpp | 52 +-- src/VulcanoLE/Scripts/BassHistory.cpp | 56 +++ src/VulcanoLE/Scripts/Loudness.cpp | 61 ++- src/VulcanoLE/Scripts/PoliceLike.cpp | 6 +- src/VulcanoLE/Scripts/RainbowLine.cpp | 18 +- src/VulcanoLE/Scripts/RainbowMap.cpp | 5 +- src/VulcanoLE/Scripts/Random.cpp | 93 ++--- src/VulcanoLE/Scripts/Spectral.cpp | 40 ++ src/VulcanoLE/Scripts/Spectrum.cpp | 71 ++-- src/VulcanoLE/Scripts/Strobo.cpp | 44 +++ src/VulcanoLE/Scripts/TheUnknown.cpp | 41 ++ src/VulcanoLE/Visual/VisPlugins.cpp | 13 +- 44 files changed, 1360 insertions(+), 275 deletions(-) create mode 100644 headers/VUtils/Random.h create mode 100644 headers/VUtils/SimplexNoise.h create mode 100644 headers/VulcanoLE/Audio/Filter.h create mode 100644 headers/VulcanoLE/Scripts/BassHistory.h create mode 100644 headers/VulcanoLE/Scripts/Spectral.h create mode 100644 headers/VulcanoLE/Scripts/Strobo.h create mode 100644 headers/VulcanoLE/Scripts/TheUknown.h create mode 100644 src/VUtils/Random.cpp create mode 100644 src/VUtils/SimplexNoise.cpp create mode 100644 src/VulcanoLE/Audio/Filter.cpp create mode 100644 src/VulcanoLE/Scripts/BassHistory.cpp create mode 100644 src/VulcanoLE/Scripts/Spectral.cpp create mode 100644 src/VulcanoLE/Scripts/Strobo.cpp create mode 100644 src/VulcanoLE/Scripts/TheUnknown.cpp diff --git a/.gitignore b/.gitignore index 122520b..8e4b3f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ +.cache/ /build/ +/build-debug/ /helper/ /cmake-build-debug/ .idea/ diff --git a/CMakeLists.txt b/CMakeLists.txt index dc2880b..6d1f6b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,12 +2,13 @@ cmake_minimum_required(VERSION 3.17) project(VulcanoLE) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") +set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +find_package(Threads REQUIRED) find_package(udev REQUIRED) find_package(HIDAPI REQUIRED) pkg_check_modules(LIBUSB REQUIRED libusb-1.0) -find_package(Threads REQUIRED) find_package(JACK REQUIRED) @@ -27,14 +28,21 @@ set(SOURCE_FILES src/VulcanoLE/Scripts/RainbowLine.cpp src/VulcanoLE/Scripts/Random.cpp src/VulcanoLE/Scripts/RainbowMap.cpp + src/VulcanoLE/Scripts/Spectral.cpp + src/VulcanoLE/Scripts/BassHistory.cpp + src/VulcanoLE/Scripts/TheUnknown.cpp + src/VulcanoLE/Scripts/Strobo.cpp + src/VulcanoLE/Audio/Filter.cpp ) set(UTILS_FILES src/VUtils/Logging.cpp src/VUtils/FileHandler.cpp src/VUtils/Pool.cpp src/VUtils/Environment.cpp + src/VUtils/Random.cpp src/VUtils/StringUtils.cpp src/VUtils/Math.cpp + src/VUtils/SimplexNoise.cpp ) include_directories(${CMAKE_SOURCE_DIR}/headers/) add_executable( diff --git a/cmake/FindHIDAPI.cmake b/cmake/FindHIDAPI.cmake index c695e1b..4231281 100644 --- a/cmake/FindHIDAPI.cmake +++ b/cmake/FindHIDAPI.cmake @@ -118,7 +118,7 @@ find_path(HIDAPI_INCLUDE_DIR ${PC_HIDAPI_HIDRAW_INCLUDE_DIRS} ${PC_HIDAPI_LIBUSB_INCLUDE_DIRS}) -find_package(Threads QUIET) +find_package(Threads REQUIRED) ### # Compute the "I don't care which backend" library diff --git a/cmake/FindJACK.cmake b/cmake/FindJACK.cmake index 6748155..d7dab44 100644 --- a/cmake/FindJACK.cmake +++ b/cmake/FindJACK.cmake @@ -1,19 +1,49 @@ -# Copyright (c) 2015 Andrew Kelley -# This file is MIT licensed. -# See http://opensource.org/licenses/MIT - -# JACK_FOUND -# JACK_INCLUDE_DIR -# JACK_LIBRARY - -find_path(JACK_INCLUDE_DIR NAMES jack/jack.h) - -find_library(JACK_LIBRARY NAMES jack) - -include(CheckLibraryExists) -check_library_exists(jack "jack_set_port_rename_callback" "${JACK_LIBRARY}" HAVE_jack_set_port_rename_callback) +# Try to find JACK +# This will define the following variables: +# +# JACK_FOUND - Whether Jack was found. +# JACK_INCLUDE_DIRS - Jack include directories. +# JACK_LIBRARIES - Jack libraries. include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(JACK DEFAULT_MSG JACK_LIBRARY JACK_INCLUDE_DIR HAVE_jack_set_port_rename_callback) -mark_as_advanced(JACK_INCLUDE_DIR JACK_LIBRARY) \ No newline at end of file +if(JACK_LIBRARIES AND JACK_INCLUDE_DIRS) + + # in cache already + set(JACK_FOUND TRUE) + +else() + + find_package(PkgConfig) + if(PKG_CONFIG_FOUND) + pkg_check_modules(_JACK jack) + endif(PKG_CONFIG_FOUND) + + find_path(JACK_INCLUDE_DIR + NAMES + jack/jack.h + PATHS + ${_JACK_INCLUDEDIR} + ) + + find_library(JACK_LIBRARY + NAMES + jack + PATHS + ${_JACK_LIBDIR} + ) + + set(JACK_INCLUDE_DIRS + ${JACK_INCLUDE_DIR} + ) + + set(JACK_LIBRARIES + ${JACK_LIBRARY} + ) + + find_package_handle_standard_args(JACK DEFAULT_MSG JACK_LIBRARIES JACK_INCLUDE_DIRS) + + # show the JACK_INCLUDE_DIRS and JACK_LIBRARIES variables only in the advanced view + mark_as_advanced(JACK_INCLUDE_DIR JACK_LIBRARY JACK_INCLUDE_DIRS JACK_LIBRARIES) + +endif() \ No newline at end of file diff --git a/headers/VUtils/Environment.h b/headers/VUtils/Environment.h index 19fbdeb..1f73d3a 100644 --- a/headers/VUtils/Environment.h +++ b/headers/VUtils/Environment.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace VUtils { class Environment { @@ -20,7 +20,7 @@ namespace VUtils { void set(const char* name, const char *value); void setNumber(const char* name, double value); protected: - std::unordered_map m_env; + std::map m_env; std::string m_prefix = "VENV_"; std::string m_filename; }; diff --git a/headers/VUtils/Math.h b/headers/VUtils/Math.h index 9e48c3c..53ea451 100644 --- a/headers/VUtils/Math.h +++ b/headers/VUtils/Math.h @@ -7,5 +7,6 @@ namespace VUtils { static double bezierBlend(double t); static double easeIn(double ratio); static double map(double x, double in_min, double in_max, double out_min, double out_max); + static double cubicInterpolate (double y0, double y1, double y2, double y3, double factor); }; } \ No newline at end of file diff --git a/headers/VUtils/Random.h b/headers/VUtils/Random.h new file mode 100644 index 0000000..6aab345 --- /dev/null +++ b/headers/VUtils/Random.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace VUtils { + + class Random { + public: + static Random& the() { + static Random _instance; + return _instance; + } + Random(); + static double generate(double min, double max); + double get(double min, double max); + void setDist(double min, double max); + double getFast(); + private: + std::random_device m_rd; + std::mt19937 m_mt; + std::uniform_real_distribution m_dist; + }; +} \ No newline at end of file diff --git a/headers/VUtils/SimplexNoise.h b/headers/VUtils/SimplexNoise.h new file mode 100644 index 0000000..176af50 --- /dev/null +++ b/headers/VUtils/SimplexNoise.h @@ -0,0 +1,55 @@ +/** + * @file SimplexNoise.h + * @brief A Perlin Simplex Noise C++ Implementation (1D, 2D, 3D). + * + * Copyright (c) 2014-2018 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include // size_t + +/** + * @brief A Perlin Simplex Noise C++ Implementation (1D, 2D, 3D, 4D). + */ +class SimplexNoise { +public: + // 1D Perlin simplex noise + static float noise(float x); + // 2D Perlin simplex noise + static float noise(float x, float y); + // 3D Perlin simplex noise + static float noise(float x, float y, float z); + + // Fractal/Fractional Brownian Motion (fBm) noise summation + float fractal(size_t octaves, float x) const; + float fractal(size_t octaves, float x, float y) const; + float fractal(size_t octaves, float x, float y, float z) const; + + /** + * Constructor of to initialize a fractal noise summation + * + * @param[in] frequency Frequency ("width") of the first octave of noise (default to 1.0) + * @param[in] amplitude Amplitude ("height") of the first octave of noise (default to 1.0) + * @param[in] lacunarity Lacunarity specifies the frequency multiplier between successive octaves (default to 2.0). + * @param[in] persistence Persistence is the loss of amplitude between successive octaves (usually 1/lacunarity) + */ + explicit SimplexNoise(float frequency = 1.0f, + float amplitude = 1.0f, + float lacunarity = 2.0f, + float persistence = 0.5f) : + mFrequency(frequency), + mAmplitude(amplitude), + mLacunarity(lacunarity), + mPersistence(persistence) { + } + +private: + // Parameters of Fractional Brownian Motion (fBm) : sum of N "octaves" of noise + float mFrequency; ///< Frequency ("width") of the first octave of noise (default to 1.0) + float mAmplitude; ///< Amplitude ("height") of the first octave of noise (default to 1.0) + float mLacunarity; ///< Lacunarity specifies the frequency multiplier between successive octaves (default to 2.0). + float mPersistence; ///< Persistence is the loss of amplitude between successive octaves (usually 1/lacunarity) +}; \ No newline at end of file diff --git a/headers/VulcanoLE/Audio/AudioGrabber.h b/headers/VulcanoLE/Audio/AudioGrabber.h index eb7299f..94f3382 100644 --- a/headers/VulcanoLE/Audio/AudioGrabber.h +++ b/headers/VulcanoLE/Audio/AudioGrabber.h @@ -7,6 +7,7 @@ #include #include #include +#include class AudioGrabber { public: @@ -14,19 +15,22 @@ public: FFT = 0, RMS = 1, PEAK = 2, - ALL = 3 + FILTER = 3, + ALL = 4 }; AudioGrabber(); ~AudioGrabber(); bool read(stereoSample *buffer, uint32_t bufferSize); static AudioGrabber* createAudioGrabber(); void init(); + Audio::FilterHelper& getFilter(); FFT fft; ReqMode requestMode = ReqMode::FFT; stereoSampleFrame loudness = {0.0, 0.0}; stereoSampleFrame getLoudness(); bool work(); VUtils::Environment *env = nullptr; + double getBass(); private: std::mutex m_mtx; stereoSample *m_buffer{}; @@ -34,4 +38,5 @@ private: void calculatePEAK(stereoSample *pFrame); double m_scale = 1.0; int availableData = 0; + Audio::FilterHelper m_filter; }; diff --git a/headers/VulcanoLE/Audio/FFT.h b/headers/VulcanoLE/Audio/FFT.h index a5b493a..aabc212 100644 --- a/headers/VulcanoLE/Audio/FFT.h +++ b/headers/VulcanoLE/Audio/FFT.h @@ -12,6 +12,7 @@ public: outputSample *getData(); bool prepareInput(stereoSample *buffer, uint32_t sampleSize, double scale); double hannWindow[BUFFER_SIZE]{}; + int size = BUFFER_SIZE; protected: fftw_complex *m_fftwInputLeft{}; fftw_complex *m_fftwInputRight{}; @@ -25,4 +26,5 @@ protected: static double valueToAmp(double value); double times; static double limit; + }; diff --git a/headers/VulcanoLE/Audio/Filter.h b/headers/VulcanoLE/Audio/Filter.h new file mode 100644 index 0000000..07ee538 --- /dev/null +++ b/headers/VulcanoLE/Audio/Filter.h @@ -0,0 +1,30 @@ +#include "Types.h" +#include + +#define FILTER_LIMIT 0.25 +namespace Audio { + class Filter { + public: + Filter(); + float process(float input, float cut, float); + + protected: + float output = 0.0; + int m_counter = 0; + float m_feedback[4][16]{}; + float m_filterOutput[4]{}; + }; + + struct FilterHelper { + Filter filter_right{}; + Filter filter_left{}; + float cutoff{}; + float scale{1.0}; + std::vector output; + stereoSampleFrame work(stereoSample *buffer, int size); + protected: + double m_limit = FILTER_LIMIT; + int overshot_times = 0; + stereoSampleFrame doLimit(double, double); + }; +} \ No newline at end of file diff --git a/headers/VulcanoLE/Audio/Types.h b/headers/VulcanoLE/Audio/Types.h index f16ee65..d72a5aa 100644 --- a/headers/VulcanoLE/Audio/Types.h +++ b/headers/VulcanoLE/Audio/Types.h @@ -1,5 +1,4 @@ #pragma once -#define FFT_SIZE 512 #define BUFFER_SIZE 1024 struct stereoSampleFrame { diff --git a/headers/VulcanoLE/Colors/ColorHelper.h b/headers/VulcanoLE/Colors/ColorHelper.h index 08277aa..149bde4 100644 --- a/headers/VulcanoLE/Colors/ColorHelper.h +++ b/headers/VulcanoLE/Colors/ColorHelper.h @@ -2,7 +2,8 @@ #include namespace Color { - struct Generator { - static rgba rgbFromRatio(double ratio, int16_t alpha); - }; -} \ No newline at end of file +struct Generator { + static rgba rgbFromRatio(double ratio, int16_t alpha); + static rgb rgbFromHSV(hsv in); +}; +} // namespace Color diff --git a/headers/VulcanoLE/Colors/Type.h b/headers/VulcanoLE/Colors/Type.h index 9f688c6..6bd3504 100644 --- a/headers/VulcanoLE/Colors/Type.h +++ b/headers/VulcanoLE/Colors/Type.h @@ -3,8 +3,20 @@ #include typedef struct rgba_type { - int16_t r{}; - int16_t g{}; - int16_t b{}; - int16_t a = 255; -} rgba; \ No newline at end of file + int16_t r = 0; + int16_t g = 0; + int16_t b = 0; + int16_t a = 0; +} rgba; + +typedef struct { + double r; // a fraction between 0 and 1 + double g; // a fraction between 0 and 1 + double b; // a fraction between 0 and 1 +} rgb; + +typedef struct { + double h; // angle in degrees + double s; // a fraction between 0 and 1 + double v; // a fraction between 0 and 1 +} hsv; diff --git a/headers/VulcanoLE/Keyboards/Vulcan121.h b/headers/VulcanoLE/Keyboards/Vulcan121.h index f5737bf..84589ea 100644 --- a/headers/VulcanoLE/Keyboards/Vulcan121.h +++ b/headers/VulcanoLE/Keyboards/Vulcan121.h @@ -20,13 +20,12 @@ class Vulcan121 { public: explicit Vulcan121(HIDHelper *helper); ~Vulcan121(); - int sendLedMap(led_map *src, bool deleteMap = false); + int sendLedMap(led_map& src); int sendToLEDs(rgba rgb); bool sendInitSequence(); bool queryCtrlReport(unsigned char id); bool sendCtrlReport(unsigned char id); bool waitForCtrlDev(); - static led_map *createEmptyLEDMap(); struct DATA { int num_rows = NUM_ROWS; int num_cols = NUM_COLS; @@ -39,8 +38,10 @@ public: int getIndex(int row, int col); // PLEASE MAKE SURE YOU KNOW THE LIMITS! int getIndexNoCheck(int row, int col); - static void fadeOutMap(led_map* map, double factor); - void fadeOutMapColumn(led_map* map, double factor); + static void fadeOutMap(led_map& map, double factor); + static void setColor(led_map& map, rgba color); + static void setColorNoBrightness(led_map& map, rgba color); + void fadeOutMapColumn(led_map& map, double factor); protected: void setupMap(); // we need some mapping feature! rows and cols dont have the same amount of keys. so the struct needs diff --git a/headers/VulcanoLE/Scripts/BassHistory.h b/headers/VulcanoLE/Scripts/BassHistory.h new file mode 100644 index 0000000..90651d4 --- /dev/null +++ b/headers/VulcanoLE/Scripts/BassHistory.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +namespace VIZ { + struct BassHistory : public VIZ { + BassHistory(AudioGrabber *pGrabber, Vulcan121 *pVulcan121); + ~BassHistory() override = default; + void onSetup() override; + void onTick(float delta) override; + const char *name() override; + std::string m_name = "Bass History"; + led_map map{}; + std::vector m_history; + void pushNew(double val); + }; +} + diff --git a/headers/VulcanoLE/Scripts/Loudness.h b/headers/VulcanoLE/Scripts/Loudness.h index f0b4c42..184cdce 100644 --- a/headers/VulcanoLE/Scripts/Loudness.h +++ b/headers/VulcanoLE/Scripts/Loudness.h @@ -13,8 +13,11 @@ namespace VIZ { void onTick(float delta) override; void setForChannel(float value, int channel); void drawFrame(int toRow); + void drawSetup(float val); const char *name() override; std::string m_name = "Loudness Meter"; + bool isSetup = true; + double frameWidth = 0; protected: rgba colours[3] = { { 0, 0, 255, 80 }, @@ -22,7 +25,7 @@ namespace VIZ { { 255, 0, 0, 40 } }; double tailFactor = 0; - led_map *data = Vulcan121::createEmptyLEDMap(); + led_map data{}; }; } diff --git a/headers/VulcanoLE/Scripts/RainbowLine.h b/headers/VulcanoLE/Scripts/RainbowLine.h index 7744528..ffea094 100644 --- a/headers/VulcanoLE/Scripts/RainbowLine.h +++ b/headers/VulcanoLE/Scripts/RainbowLine.h @@ -5,13 +5,13 @@ namespace VIZ { class RainbowLine : public VIZ { int currentColumn = 0; - double deltaNeeded = 100000.0; + double deltaNeeded = 0.0; double deltaElapsed = 0; rgba *colours = nullptr; int maxCols = 0; double lastValue = 0; double decayValue = 0; - double tailFactor = 0.3; + double tailFactor = 0.5; public: RainbowLine(AudioGrabber *pGrabber, Vulcan121 *vulcan); ~RainbowLine() override; @@ -20,7 +20,7 @@ namespace VIZ { void calcNextDelta(double ratio); const char *name() override; std::string m_name = "Rainbow Line"; - led_map *data = Vulcan121::createEmptyLEDMap(); + led_map data{}; bool firstUnder = true; double deltaMove(double val); void moveLine(double val, double d); diff --git a/headers/VulcanoLE/Scripts/RainbowMap.h b/headers/VulcanoLE/Scripts/RainbowMap.h index 55c8b10..f813593 100644 --- a/headers/VulcanoLE/Scripts/RainbowMap.h +++ b/headers/VulcanoLE/Scripts/RainbowMap.h @@ -19,7 +19,7 @@ namespace VIZ { void calcNextDelta(double ratio); const char *name() override; std::string m_name = "Rainbow Map"; - led_map *data = Vulcan121::createEmptyLEDMap(); + led_map data{}; bool firstUnder = true; double deltaMove(double val); void moveRainbow(double d); diff --git a/headers/VulcanoLE/Scripts/Random.h b/headers/VulcanoLE/Scripts/Random.h index 6bcd73a..f2c003a 100644 --- a/headers/VulcanoLE/Scripts/Random.h +++ b/headers/VulcanoLE/Scripts/Random.h @@ -4,23 +4,18 @@ #include #include #include +#include "VUtils/Random.h" namespace VIZ { class Random : public VIZ { protected: - led_map *map = nullptr; - bool isReverse{ false }; - bool emptyTicks{ false }; + led_map map{}; + VUtils::Random m_random; + double m_angle{0.0}; public: Random(AudioGrabber *pGrabber, Vulcan121 *vulcan); - ~Random() override; void onSetup() override; void onTick(float delta) override; const char *name() override; - float deltaElapsed; - float deltaNeeded{ 80000 }; - int row; - int columnCount{0}; // for last selected row all previous draw full - int ticks; }; } \ No newline at end of file diff --git a/headers/VulcanoLE/Scripts/Spectral.h b/headers/VulcanoLE/Scripts/Spectral.h new file mode 100644 index 0000000..8f5a2b7 --- /dev/null +++ b/headers/VulcanoLE/Scripts/Spectral.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +namespace VIZ { + struct Spectral : public VIZ { + Spectral(AudioGrabber *pGrabber, Vulcan121 *pVulcan121); + ~Spectral() override = default; + void onSetup() override; + void onTick(float delta) override; + const char *name() override; + std::string m_name = "SpectralO"; + led_map map{}; + }; +} + diff --git a/headers/VulcanoLE/Scripts/Spectrum.h b/headers/VulcanoLE/Scripts/Spectrum.h index d5c0165..7b41175 100644 --- a/headers/VulcanoLE/Scripts/Spectrum.h +++ b/headers/VulcanoLE/Scripts/Spectrum.h @@ -1,22 +1,20 @@ #pragma once -#include #include +#include namespace VIZ { - struct Spectrum : public VIZ { - Spectrum(AudioGrabber *pGrabber, Vulcan121 *pVulcan121); - ~Spectrum() override = default; - void onSetup() override; - void onTick(float delta) override; - double lastVal = 0; - const char *name() override; - std::string m_name = "Spectrum One"; - rgba colors[3] = { - { 0, 10, 180, 0 }, - { 0, 180, 0, 0 }, - { 150, 0, 0, 0 } - }; - }; -} +struct Spectrum : public VIZ { +public: + static constexpr int maxColors = 3; + Spectrum(AudioGrabber *pGrabber, Vulcan121 *pVulcan121); + ~Spectrum() override = default; + void onSetup() override; + void onTick(float delta) override; + const char *name() override; +protected: + std::string m_name = "Spectrum One"; + double m_angle = 0.0; +}; +} // namespace VIZ diff --git a/headers/VulcanoLE/Scripts/Strobo.h b/headers/VulcanoLE/Scripts/Strobo.h new file mode 100644 index 0000000..5ca3988 --- /dev/null +++ b/headers/VulcanoLE/Scripts/Strobo.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace VIZ { +struct Strobo : public VIZ { +public: + Strobo(AudioGrabber *pGrabber, Vulcan121 *pVulcan121); + ~Strobo() override = default; + void onSetup() override; + void onTick(float delta) override; + const char *name() override; + +protected: + std::string m_name = "Strobo"; + double m_sinPoint; + bool m_isStanding; + rgba m_color{}; +}; +} // namespace VIZ diff --git a/headers/VulcanoLE/Scripts/TheUknown.h b/headers/VulcanoLE/Scripts/TheUknown.h new file mode 100644 index 0000000..0df2bbf --- /dev/null +++ b/headers/VulcanoLE/Scripts/TheUknown.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include +#include "VUtils/Random.h" + +namespace VIZ { + class TheUnknown : public VIZ { + protected: + VUtils::Random m_random{}; + led_map map{}; + public: + TheUnknown(AudioGrabber *pGrabber, Vulcan121 *vulcan); + void onSetup() override; + void onTick(float delta) override; + const char *name() override; + + double m_keyOffset[NUM_KEYS]{}; + double m_keyHeightMap[NUM_KEYS]{}; + double m_angle = 0.0001; + }; +} \ No newline at end of file diff --git a/headers/VulcanoLE/Visual/VisPlugins.h b/headers/VulcanoLE/Visual/VisPlugins.h index 0ed5c02..8672d44 100644 --- a/headers/VulcanoLE/Visual/VisPlugins.h +++ b/headers/VulcanoLE/Visual/VisPlugins.h @@ -3,7 +3,7 @@ #include #include -#define VIZSIZE 6 +#define VIZSIZE 10 using micro = std::chrono::duration; using ms = std::chrono::duration; diff --git a/main.cpp b/main.cpp index 9a7fa1f..4aeca52 100644 --- a/main.cpp +++ b/main.cpp @@ -37,7 +37,7 @@ int main(int argc, char **argv) { if (helper.openDevices() < 0) { ERR("Unable to find Keyboard!") exit(0); - }; + } usleep(10000); auto runner = VisAudioRunner::create(); runner->env = &config; @@ -46,7 +46,7 @@ int main(int argc, char **argv) { runner->plugins->setCurrentMode(config.getAsInt("visual_mode", 1)); while (shouldRun) { int mode; - LOGWN("Enter Visual Mode: %d-%d < 0 = EXIT:\t", 1, VIZSIZE) + LOGWN("Enter Visual Mode: %d-%d < 0 = EXIT: \n> ", 1, VIZSIZE) std::cin >> mode; if (std::cin.fail()) { ERR("ERROR -- You did not enter an integer") diff --git a/src/VUtils/Math.cpp b/src/VUtils/Math.cpp index 1c20530..fd7011c 100644 --- a/src/VUtils/Math.cpp +++ b/src/VUtils/Math.cpp @@ -17,4 +17,16 @@ namespace VUtils { double Math::map(double x, double in_min, double in_max, double out_min, double out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } + double Math::cubicInterpolate (double y0, double y1, double y2, double y3, double factor) + { + double a0, a1, a2, a3, mu2; + + mu2 = factor * factor; + a0 = y3 - y2 - y0 + y1; + a1 = y0 - y1 - a0; + a2 = y2 - y0; + a3 = y1; + + return (a0 * factor * mu2 + a1 * mu2 + a2 * factor + a3); + } } \ No newline at end of file diff --git a/src/VUtils/Random.cpp b/src/VUtils/Random.cpp new file mode 100644 index 0000000..ffbf3cc --- /dev/null +++ b/src/VUtils/Random.cpp @@ -0,0 +1,21 @@ +#include + +namespace VUtils { + Random::Random() : m_mt{ m_rd() } { + } + + double Random::generate(double min, double max) { + return Random::the().get(min, max); + } + + double Random::get(double min, double max) { + std::uniform_real_distribution dist(min, max); + return dist(m_mt); + } + void Random::setDist(double min, double max) { + m_dist = std::uniform_real_distribution(min, max); + } + double Random::getFast() { + return m_dist(m_mt); + } +} \ No newline at end of file diff --git a/src/VUtils/SimplexNoise.cpp b/src/VUtils/SimplexNoise.cpp new file mode 100644 index 0000000..dcfa6bd --- /dev/null +++ b/src/VUtils/SimplexNoise.cpp @@ -0,0 +1,475 @@ +/** + * @file SimplexNoise.cpp + * @brief A Perlin Simplex Noise C++ Implementation (1D, 2D, 3D). + * + * Copyright (c) 2014-2018 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * This C++ implementation is based on the speed-improved Java version 2012-03-09 + * by Stefan Gustavson (original Java source code in the public domain). + * http://webstaff.itn.liu.se/~stegu/simplexnoise/SimplexNoise.java: + * - Based on example code by Stefan Gustavson (stegu@itn.liu.se). + * - Optimisations by Peter Eastman (peastman@drizzle.stanford.edu). + * - Better rank ordering method by Stefan Gustavson in 2012. + * + * This implementation is "Simplex Noise" as presented by + * Ken Perlin at a relatively obscure and not often cited course + * session "Real-Time Shading" at Siggraph 2001 (before real + * time shading actually took on), under the title "hardware noise". + * The 3D function is numerically equivalent to his Java reference + * code available in the PDF course notes, although I re-implemented + * it from scratch to get more readable code. The 1D, 2D and 4D cases + * were implemented from scratch by me from Ken Perlin's text. + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ + +#include + +#include // int32_t/uint8_t + +/** + * Computes the largest integer value not greater than the float one + * + * This method is faster than using (int32_t)std::floor(fp). + * + * I measured it to be approximately twice as fast: + * float: ~18.4ns instead of ~39.6ns on an AMD APU), + * double: ~20.6ns instead of ~36.6ns on an AMD APU), + * Reference: http://www.codeproject.com/Tips/700780/Fast-floor-ceiling-functions + * + * @param[in] fp float input value + * + * @return largest integer value not greater than fp + */ +static inline int32_t fastfloor(float fp) { + int32_t i = static_cast(fp); + return (fp < i) ? (i - 1) : (i); +} + +/** + * Permutation table. This is just a random jumble of all numbers 0-255. + * + * This produce a repeatable pattern of 256, but Ken Perlin stated + * that it is not a problem for graphic texture as the noise features disappear + * at a distance far enough to be able to see a repeatable pattern of 256. + * + * This needs to be exactly the same for all instances on all platforms, + * so it's easiest to just keep it as static explicit data. + * This also removes the need for any initialisation of this class. + * + * Note that making this an uint32_t[] instead of a uint8_t[] might make the + * code run faster on platforms with a high penalty for unaligned single + * byte addressing. Intel x86 is generally single-byte-friendly, but + * some other CPUs are faster with 4-aligned reads. + * However, a char[] is smaller, which avoids cache trashing, and that + * is probably the most important aspect on most architectures. + * This array is accessed a *lot* by the noise functions. + * A vector-valued noise over 3D accesses it 96 times, and a + * float-valued 4D noise 64 times. We want this to fit in the cache! + */ +static const uint8_t perm[256] = { + 151, 160, 137, 91, 90, 15, + 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, + 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, + 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, + 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, + 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, + 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, + 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, + 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, + 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, + 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, + 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, + 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 +}; + +/** + * Helper function to hash an integer using the above permutation table + * + * This inline function costs around 1ns, and is called N+1 times for a noise of N dimension. + * + * Using a real hash function would be better to improve the "repeatability of 256" of the above permutation table, + * but fast integer Hash functions uses more time and have bad random properties. + * + * @param[in] i Integer value to hash + * + * @return 8-bits hashed value + */ +static inline uint8_t hash(int32_t i) { + return perm[static_cast(i)]; +} + +/* NOTE Gradient table to test if lookup-table are more efficient than calculs +static const float gradients1D[16] = { + -8.f, -7.f, -6.f, -5.f, -4.f, -3.f, -2.f, -1.f, + 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f +}; +*/ + +/** + * Helper function to compute gradients-dot-residual vectors (1D) + * + * @note that these generate gradients of more than unit length. To make + * a close match with the value range of classic Perlin noise, the final + * noise values need to be rescaled to fit nicely within [-1,1]. + * (The simplex noise functions as such also have different scaling.) + * Note also that these noise functions are the most practical and useful + * signed version of Perlin noise. + * + * @param[in] hash hash value + * @param[in] x distance to the corner + * + * @return gradient value + */ +static float grad(int32_t hash, float x) { + const int32_t h = hash & 0x0F; // Convert low 4 bits of hash code + float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0 + if ((h & 8) != 0) grad = -grad; // Set a random sign for the gradient +// float grad = gradients1D[h]; // NOTE : Test of Gradient look-up table instead of the above + return (grad * x); // Multiply the gradient with the distance +} + +/** + * Helper functions to compute gradients-dot-residual vectors (2D) + * + * @param[in] hash hash value + * @param[in] x x coord of the distance to the corner + * @param[in] y y coord of the distance to the corner + * + * @return gradient value + */ +static float grad(int32_t hash, float x, float y) { + const int32_t h = hash & 0x3F; // Convert low 3 bits of hash code + const float u = h < 4 ? x : y; // into 8 simple gradient directions, + const float v = h < 4 ? y : x; + return ((h & 1) ? -u : u) + ((h & 2) ? -2.0f * v : 2.0f * v); // and compute the dot product with (x,y). +} + +/** + * Helper functions to compute gradients-dot-residual vectors (3D) + * + * @param[in] hash hash value + * @param[in] x x coord of the distance to the corner + * @param[in] y y coord of the distance to the corner + * @param[in] z z coord of the distance to the corner + * + * @return gradient value + */ +static float grad(int32_t hash, float x, float y, float z) { + int h = hash & 15; // Convert low 4 bits of hash code into 12 simple + float u = h < 8 ? x : y; // gradient directions, and compute dot product. + float v = h < 4 ? y : h == 12 || h == 14 ? x : z; // Fix repeats at h = 12 to 15 + return ((h & 1) ? -u : u) + ((h & 2) ? -v : v); +} + +/** + * 1D Perlin simplex noise + * + * Takes around 74ns on an AMD APU. + * + * @param[in] x float coordinate + * + * @return Noise value in the range[-1; 1], value of 0 on all integer coordinates. + */ +float SimplexNoise::noise(float x) { + float n0, n1; // Noise contributions from the two "corners" + + // No need to skew the input space in 1D + + // Corners coordinates (nearest integer values): + int32_t i0 = fastfloor(x); + int32_t i1 = i0 + 1; + // Distances to corners (between 0 and 1): + float x0 = x - i0; + float x1 = x0 - 1.0f; + + // Calculate the contribution from the first corner + float t0 = 1.0f - x0*x0; +// if(t0 < 0.0f) t0 = 0.0f; // not possible + t0 *= t0; + n0 = t0 * t0 * grad(hash(i0), x0); + + // Calculate the contribution from the second corner + float t1 = 1.0f - x1*x1; +// if(t1 < 0.0f) t1 = 0.0f; // not possible + t1 *= t1; + n1 = t1 * t1 * grad(hash(i1), x1); + + // The maximum value of this noise is 8*(3/4)^4 = 2.53125 + // A factor of 0.395 scales to fit exactly within [-1,1] + return 0.395f * (n0 + n1); +} + +/** + * 2D Perlin simplex noise + * + * Takes around 150ns on an AMD APU. + * + * @param[in] x float coordinate + * @param[in] y float coordinate + * + * @return Noise value in the range[-1; 1], value of 0 on all integer coordinates. + */ +float SimplexNoise::noise(float x, float y) { + float n0, n1, n2; // Noise contributions from the three corners + + // Skewing/Unskewing factors for 2D + static const float F2 = 0.366025403f; // F2 = (sqrt(3) - 1) / 2 + static const float G2 = 0.211324865f; // G2 = (3 - sqrt(3)) / 6 = F2 / (1 + 2 * K) + + // Skew the input space to determine which simplex cell we're in + const float s = (x + y) * F2; // Hairy factor for 2D + const float xs = x + s; + const float ys = y + s; + const int32_t i = fastfloor(xs); + const int32_t j = fastfloor(ys); + + // Unskew the cell origin back to (x,y) space + const float t = static_cast(i + j) * G2; + const float X0 = i - t; + const float Y0 = j - t; + const float x0 = x - X0; // The x,y distances from the cell origin + const float y0 = y - Y0; + + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + int32_t i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords + if (x0 > y0) { // lower triangle, XY order: (0,0)->(1,0)->(1,1) + i1 = 1; + j1 = 0; + } else { // upper triangle, YX order: (0,0)->(0,1)->(1,1) + i1 = 0; + j1 = 1; + } + + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + + const float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + const float y1 = y0 - j1 + G2; + const float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords + const float y2 = y0 - 1.0f + 2.0f * G2; + + // Work out the hashed gradient indices of the three simplex corners + const int gi0 = hash(i + hash(j)); + const int gi1 = hash(i + i1 + hash(j + j1)); + const int gi2 = hash(i + 1 + hash(j + 1)); + + // Calculate the contribution from the first corner + float t0 = 0.5f - x0*x0 - y0*y0; + if (t0 < 0.0f) { + n0 = 0.0f; + } else { + t0 *= t0; + n0 = t0 * t0 * grad(gi0, x0, y0); + } + + // Calculate the contribution from the second corner + float t1 = 0.5f - x1*x1 - y1*y1; + if (t1 < 0.0f) { + n1 = 0.0f; + } else { + t1 *= t1; + n1 = t1 * t1 * grad(gi1, x1, y1); + } + + // Calculate the contribution from the third corner + float t2 = 0.5f - x2*x2 - y2*y2; + if (t2 < 0.0f) { + n2 = 0.0f; + } else { + t2 *= t2; + n2 = t2 * t2 * grad(gi2, x2, y2); + } + + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 45.23065f * (n0 + n1 + n2); +} + + +/** + * 3D Perlin simplex noise + * + * @param[in] x float coordinate + * @param[in] y float coordinate + * @param[in] z float coordinate + * + * @return Noise value in the range[-1; 1], value of 0 on all integer coordinates. + */ +float SimplexNoise::noise(float x, float y, float z) { + float n0, n1, n2, n3; // Noise contributions from the four corners + + // Skewing/Unskewing factors for 3D + static const float F3 = 1.0f / 3.0f; + static const float G3 = 1.0f / 6.0f; + + // Skew the input space to determine which simplex cell we're in + float s = (x + y + z) * F3; // Very nice and simple skew factor for 3D + int i = fastfloor(x + s); + int j = fastfloor(y + s); + int k = fastfloor(z + s); + float t = (i + j + k) * G3; + float X0 = i - t; // Unskew the cell origin back to (x,y,z) space + float Y0 = j - t; + float Z0 = k - t; + float x0 = x - X0; // The x,y,z distances from the cell origin + float y0 = y - Y0; + float z0 = z - Z0; + + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + // Determine which simplex we are in. + int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + if (x0 >= y0) { + if (y0 >= z0) { + i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; // X Y Z order + } else if (x0 >= z0) { + i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; // X Z Y order + } else { + i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; // Z X Y order + } + } else { // x0 #include #include +#include "VUtils/Math.h" AudioGrabber::AudioGrabber() = default; @@ -27,27 +28,28 @@ void AudioGrabber::init() { m_buffer = static_cast(calloc(BUFFER_SIZE, sizeof(stereoSample))); if (env != nullptr) m_scale = env->getAsDouble("audio_scale", 1.0); + m_filter.scale = 1.0; DBG("SET Audio Scale: %.3f", m_scale) loudness = { 0.0, 0.0 }; } void AudioGrabber::calculateRMS(stereoSample *pFrame) { - float squareL = 0, meanL; - float squareR = 0, meanR; + double squareL = 0, meanL; + double squareR = 0, meanR; for (int i = 0; i < availableData; i++) { - squareL += std::pow(pFrame[0].l * m_scale, 2); - squareR += std::pow(pFrame[0].r * m_scale, 2); + squareL += std::pow(pFrame[i].l * m_scale, 2); + squareR += std::pow(pFrame[i].r * m_scale, 2); } - meanL = (squareL / (float) (BUFFER_SIZE)); - meanR = (squareR / (float) (BUFFER_SIZE)); - loudness = { std::sqrt(meanL), std::sqrt(meanR) }; + meanL = (squareL / (float) (availableData)); + meanR = (squareR / (float) (availableData)); + loudness = { (float) std::sqrt(meanL), (float) std::sqrt(meanR) }; } void AudioGrabber::calculatePEAK(stereoSample *pFrame) { stereoSampleFrame max = { 0, 0 }; for (int i = 0; i < availableData; i++) { - float left = std::abs(pFrame[0].l * m_scale); - float right = std::abs(pFrame[0].r * m_scale); + auto left = (float) std::abs(pFrame[i].l * m_scale); + auto right = (float) std::abs(pFrame[i].r * m_scale); if (left > max.l) max.l = left; if (right > max.r) @@ -74,6 +76,9 @@ bool AudioGrabber::work() { case ReqMode::PEAK: calculatePEAK(m_buffer); break; + case ReqMode::FILTER: + loudness = m_filter.work(m_buffer, availableData); + break; default: { calculateRMS(m_buffer); calculatePEAK(m_buffer); @@ -85,3 +90,16 @@ bool AudioGrabber::work() { return false; } } +Audio::FilterHelper &AudioGrabber::getFilter() { + return m_filter; +} +double AudioGrabber::getBass() { + auto fftData = fft.getData(); + auto val = 0.0; + for (int i = 1; i < 4; ++i) { + auto avg = (fftData->leftChannel[i] + fftData->rightChannel[i]) / 2.0; + if (avg > val) + val = avg; + } + return VUtils::Math::clamp(val, 1, 255); +} diff --git a/src/VulcanoLE/Audio/Filter.cpp b/src/VulcanoLE/Audio/Filter.cpp new file mode 100644 index 0000000..6a47fe1 --- /dev/null +++ b/src/VulcanoLE/Audio/Filter.cpp @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include + +namespace Audio { + + Filter::Filter() { + for (int i = 0; i < 3; ++i) { + m_filterOutput[i] = 0.0f; + for (int j = 0; j < 16; ++j) { + m_feedback[i][j] = 0.0f; + } + } + } + float Filter::process(float input, float cut, float) { + cut *= cut; + output = input; + if (m_counter >= 8) + m_counter = 0; + for (int i = 0; i < 4; ++i) { + output = output * cut + m_feedback[i][m_counter + 7] * (1.f - cut); + m_feedback[i][m_counter] = output; + m_feedback[i][m_counter + 8] = output; + m_filterOutput[i] = output; + } + m_counter++; + return m_filterOutput[3]; + } + stereoSampleFrame FilterHelper::work(stereoSample *buffer, int size) { + output.clear(); + output.resize(size); + double peakL = 0; + double peakR = 0; + for (int i = 0; i < size; ++i) { + output[i].r = filter_right.process(buffer[i].r, cutoff, 0); + output[i].l = filter_left.process(buffer[i].l, cutoff, 0); + double l = std::abs(output[i].l); + double r = std::abs(output[i].r); + if (l > peakL) peakL = l; + if (r > peakR) peakR = r; + } + return doLimit(peakL, peakR); + } + stereoSampleFrame FilterHelper::doLimit(double left, double right) { + if (left > m_limit) { + m_limit = left + 0.05; + DBG("Overshot! New Limit: %.2f", m_limit) + } + if (right > m_limit) { + m_limit = right + 0.05; + DBG("Overshot! New Limit: %.2f", m_limit) + } + + m_limit = std::clamp(m_limit, 0.0, 1.0); + if (left < 0.00005 && right < 0.00005) { + // reset limit :D + overshot_times++; + if (overshot_times > 10000 && m_limit != FILTER_LIMIT) { + DBG("OVER 10000 SILENCE SIGNALS... RESET TO INITIAL LIMIT") + m_limit = FILTER_LIMIT; + overshot_times = 0; + } + } + return { + (float) VUtils::Math::map(left, 0, m_limit, 0, 255), + (float) VUtils::Math::map(right, 0, m_limit, 0, 255), + }; + } +} \ No newline at end of file diff --git a/src/VulcanoLE/Colors/ColorHelper.cpp b/src/VulcanoLE/Colors/ColorHelper.cpp index 17dc6a5..afc952a 100644 --- a/src/VulcanoLE/Colors/ColorHelper.cpp +++ b/src/VulcanoLE/Colors/ColorHelper.cpp @@ -1,44 +1,102 @@ #include namespace Color { - rgba Generator::rgbFromRatio(double ratio, int16_t alpha = 255) { - int normalized = int(ratio * 256 * 6); - int x = normalized % 256; +rgba Generator::rgbFromRatio(double ratio, int16_t alpha = 255) { + int normalized = int(ratio * 256 * 6); + int x = normalized % 256; - int16_t red = 0, green = 0, blue = 0; - switch (normalized / 256) { - case 0: - red = 255; - green = x; - blue = 0; - break; - case 1: - red = 255 - x; - green = 255; - blue = 0; - break; - case 2: - red = 0; - green = 255; - blue = x; - break; - case 3: - red = 0; - green = 255 - x; - blue = 255; - break; - case 4: - red = x; - green = 0; - blue = 255; - break; - case 5: - red = 255; - green = 0; - blue = 255 - x; - break; - } + int16_t red = 0, green = 0, blue = 0; + switch (normalized / 256) { + case 0: + red = 255; + green = x; + blue = 0; + break; + case 1: + red = 255 - x; + green = 255; + blue = 0; + break; + case 2: + red = 0; + green = 255; + blue = x; + break; + case 3: + red = 0; + green = 255 - x; + blue = 255; + break; + case 4: + red = x; + green = 0; + blue = 255; + break; + case 5: + red = 255; + green = 0; + blue = 255 - x; + break; + } - return { red, green, blue, alpha }; - } -} \ No newline at end of file + return {red, green, blue, alpha}; +} + +rgb Generator::rgbFromHSV(hsv in) { + double hh, p, q, t, ff; + long i; + rgb out; + + if (in.s <= 0.0) { // < is bogus, just shuts up warnings + out.r = in.v; + out.g = in.v; + out.b = in.v; + return out; + } + hh = in.h; + if (hh >= 360.0) + hh = 0.0; + hh /= 60.0; + i = (long)hh; + ff = hh - i; + p = in.v * (1.0 - in.s); + q = in.v * (1.0 - (in.s * ff)); + t = in.v * (1.0 - (in.s * (1.0 - ff))); + + switch (i) { + case 0: + out.r = in.v; + out.g = t; + out.b = p; + break; + case 1: + out.r = q; + out.g = in.v; + out.b = p; + break; + case 2: + out.r = p; + out.g = in.v; + out.b = t; + break; + + case 3: + out.r = p; + out.g = q; + out.b = in.v; + break; + case 4: + out.r = t; + out.g = p; + out.b = in.v; + break; + case 5: + default: + out.r = in.v; + out.g = p; + out.b = q; + break; + } + return out; +} +} // namespace Color diff --git a/src/VulcanoLE/Keyboards/Vulcan121.cpp b/src/VulcanoLE/Keyboards/Vulcan121.cpp index 3597060..7c0dce3 100644 --- a/src/VulcanoLE/Keyboards/Vulcan121.cpp +++ b/src/VulcanoLE/Keyboards/Vulcan121.cpp @@ -7,10 +7,10 @@ Vulcan121::Vulcan121(HIDHelper *helper) : m_helper(helper) { - for (auto &item : keyMapRow) { + for (auto &item: keyMapRow) { item = new keys; } - for (auto &item : keyMapCol) { + for (auto &item: keyMapCol) { item = new keys; } @@ -30,7 +30,7 @@ Vulcan121::~Vulcan121() { // Colors are in blocks of 12 keys (2 columns). Color parts are sorted by color e.g. the red // values for all 12 keys are first then come the green values etc. -int Vulcan121::sendLedMap(led_map *src, bool deleteMap) { +int Vulcan121::sendLedMap(led_map &src) { int i, k; rgba rgb; unsigned char ledBuffer[448]{}; @@ -41,7 +41,7 @@ int Vulcan121::sendLedMap(led_map *src, bool deleteMap) { ledBuffer[3] = 0xb4; for (k = 0; k < NUM_KEYS; k++) { // Prepare Color - auto use = m_fixed[k] ? *(m_fixed[k]) : src ? src->key[k] : m_colorOff; + auto use = m_fixed[k] ? *(m_fixed[k]) : src.key[k]; rgb.a = (int16_t) VUtils::Math::clamp(use.a, 0, 255); double factor = rgb.a / 255.0; rgb.r = (int16_t) VUtils::Math::clamp(std::round(use.r * factor), 0, 255); @@ -61,34 +61,26 @@ int Vulcan121::sendLedMap(led_map *src, bool deleteMap) { memcpy(&buffer[1], &ledBuffer[index], 64); if (hid_write(m_helper->m_ledDevice, buffer, 65) != 65) { ERR("Write failed") - if (deleteMap) - delete src; return 0; } } - if (deleteMap) - delete src; return 1; } int Vulcan121::sendToLEDs(rgba rgb) { - auto *map = new led_map(); - for (auto &i : map->key) { + auto map = led_map(); + for (auto &i: map.key) { i = rgb; } - int st = sendLedMap(map, true); + int st = sendLedMap(map); return st; } -led_map *Vulcan121::createEmptyLEDMap() { - return new led_map(); -} - bool Vulcan121::sendInitSequence() { LOG("Sending device init sequence...") unsigned char a[9] = { 0x15, 0x05, 0x07, 0x0a, 0x0b, 0x06, 0x09, 0x0d, 0x13 }; if (!queryCtrlReport(0x0f)) return false; - for (auto i : a) { + for (auto i: a) { if (!sendCtrlReport(i) || !waitForCtrlDev()) { return false; } @@ -327,25 +319,41 @@ const keys *Vulcan121::getRow(int row) { return keyMapRow[row]; } -void Vulcan121::fadeOutMap(led_map *map, double factor = 0.3) { - for (auto &i : map->key) { +void Vulcan121::fadeOutMap(led_map &map, double factor = 0.3) { + for (auto &i: map.key) { i.a *= factor; } } // Column FadeOut! Check if previous Col is brighter! -void Vulcan121::fadeOutMapColumn(led_map *map, double factor) { +void Vulcan121::fadeOutMapColumn(led_map &map, double factor) { for (int i = 0; i < NUM_COLS; ++i) { // prev check if not 0 auto *columnA = getColumn(i); int16_t colorB = 0; - if (i > 0 && map->key[columnA->index[0]].a > 0.1) { + if (i > 0 && map.key[columnA->index[0]].a > 0.1) { auto columnB = getColumn(i - 1); - colorB = map->key[columnB->index[0]].a; + colorB = map.key[columnB->index[0]].a; } for (int j = 0; j < columnA->count; ++j) { - auto &colorA = map->key[columnA->index[j]]; + auto &colorA = map.key[columnA->index[j]]; colorA.a = colorB != 0 && colorB > colorA.a ? colorB * 1.3 : colorA.a * factor; } } } + +void Vulcan121::setColor(led_map &map, rgba color) { + for (auto &i: map.key) { + i.r = color.r; + i.g = color.g; + i.b = color.b; + i.a = color.a; + } +} +void Vulcan121::setColorNoBrightness(led_map &map, rgba color) { + for (auto &i: map.key) { + i.r = color.r; + i.g = color.g; + i.b = color.b; + } +} diff --git a/src/VulcanoLE/Scripts/BassHistory.cpp b/src/VulcanoLE/Scripts/BassHistory.cpp new file mode 100644 index 0000000..5aac576 --- /dev/null +++ b/src/VulcanoLE/Scripts/BassHistory.cpp @@ -0,0 +1,56 @@ +#include +#include + +namespace VIZ { + + BassHistory::BassHistory(AudioGrabber *pGrabber, Vulcan121 *pVulcan121) : VIZ(pGrabber, pVulcan121){ + for (auto &item : map.key) { + item.a = 0; + } + m_history.resize(keyboardData.num_cols); + } + + void BassHistory::onSetup() { + keyboard->sendToLEDs({ 0, 0, 0, 0 }); + grabber->requestMode = AudioGrabber::ReqMode::FFT; + } + + void BassHistory::onTick(float delta) { + auto data = grabber->fft.getData()->leftChannel; + auto val = 0.0; + for (int i = 1; i < 4; ++i) { + if (data[i] > val) { + val = data[i]; + } + } + pushNew(val); + for (int i = 0; i < keyboardData.num_cols; ++i) { + auto col = keyboard->getColumn(i); + for (int j = 0; j < col->count; j++) { + auto& color = map.key[col->index[j]]; + color.a = m_history[i]; + auto newColor = Color::Generator::rgbFromHSV({ + m_history[i] / 255 * 360, + 1, + 1 + }); + color.r = int16_t(newColor.r * 255); + color.g = int16_t(newColor.g * 255); + color.b = int16_t(newColor.b * 255); + } + } + keyboard->sendLedMap(map); + } + + const char *BassHistory::name() { + return m_name.c_str(); + } + + void BassHistory::pushNew(double val) { + for (int i = 1; i < m_history.size(); ++i) { + m_history[i - 1] = m_history[i]; + } + m_history[m_history.size()-1] = val; + } + +} \ No newline at end of file diff --git a/src/VulcanoLE/Scripts/Loudness.cpp b/src/VulcanoLE/Scripts/Loudness.cpp index 387898f..425f92a 100644 --- a/src/VulcanoLE/Scripts/Loudness.cpp +++ b/src/VulcanoLE/Scripts/Loudness.cpp @@ -4,19 +4,46 @@ namespace VIZ { Loudness::Loudness(AudioGrabber *pGrabber, Vulcan121 *pVulcan121) : VIZ(pGrabber, pVulcan121) {} void Loudness::onSetup() { - keyboard->sendToLEDs({ 0, 0, 0, 0 }); - grabber->requestMode = AudioGrabber::ReqMode::PEAK; - usleep(100000); + if (isSetup) { + double i = 1; + bool wasFull = false; + for (;;) { + if (frameWidth >= 1.0) frameWidth = 1; + i += wasFull ? -.7 : .5; + if (i >= 100) wasFull = true; + drawSetup(i / 100.0f); + if (wasFull && i >= 100) { + for (int j = 0; j < 5; ++j) { + usleep(100000); + drawSetup(0); + usleep(100000); + drawSetup(1); + } + } + if (wasFull && i <= 0) { + frameWidth = 1; + break; + } + } + usleep(200000); + isSetup = false; + } + + grabber->requestMode = AudioGrabber::ReqMode::RMS; } void Loudness::onTick(float delta) { - Vulcan121::fadeOutMap(data, tailFactor); + if (isSetup) { + return; + } stereoSample val = grabber->getLoudness(); val.l = val.l > 1.0f ? 1.0f : val.l; val.r = val.r > 1.0f ? 1.0f : val.r; setForChannel(val.l, 0); setForChannel(val.r, 1); - keyboard->sendLedMap(data, false); + drawFrame(0); + drawFrame(5); + keyboard->sendLedMap(data); } const char *Loudness::name() { @@ -27,24 +54,34 @@ namespace VIZ { // because we have stereo we need to move on the rows int offset = channel == 0 ? 1 : 3; int until = channel == 0 ? 3 : 5; - colours[channel].a = value * 128.0; + auto color = colours[channel]; + color.a = value * 255.0; for (int i = offset; i < until; ++i) { auto row = keyboard->getRow(i); int to = row->count * value; - for (int j = 0; j < to; ++j) { + for (int j = 0; j < row->count; ++j) { auto index = row->index[j]; - data[0].key[index] = colours[channel]; + if (j < to) + data.key[index] = color; + else + data.key[index].a = 0; } } - drawFrame(0); - drawFrame(5); } void Loudness::drawFrame(int toRow) { auto row = keyboard->getRow(toRow); - for (int j = 0; j < row->count; ++j) { + for (int j = 0; j < row->count * frameWidth; ++j) { auto index = row->index[j]; - data[0].key[index] = colours[2]; + data.key[index] = colours[2]; } } + void Loudness::drawSetup(float val) { + setForChannel(val, 0); + setForChannel(val, 1); + frameWidth = val; + drawFrame(0); + drawFrame(5); + keyboard->sendLedMap(data); + } } \ No newline at end of file diff --git a/src/VulcanoLE/Scripts/PoliceLike.cpp b/src/VulcanoLE/Scripts/PoliceLike.cpp index dbe5a94..0bc82b1 100644 --- a/src/VulcanoLE/Scripts/PoliceLike.cpp +++ b/src/VulcanoLE/Scripts/PoliceLike.cpp @@ -12,7 +12,7 @@ namespace VIZ { } void PoliceLike::onTick(float delta) { - auto map = Vulcan121::createEmptyLEDMap(); + led_map map{}; auto data = grabber->fft.getData()->leftChannel; auto val = 0.0; for (int i = 1; i < 3; ++i) { @@ -29,10 +29,10 @@ namespace VIZ { for (int i = 0; i < max; ++i) { auto col = keyboard->getColumn(i + colOff); for (int j = 0; j < col->count; ++j) { - map[0].key[col->index[j]] = colors[colorInd]; + map.key[col->index[j]] = colors[colorInd]; } } - keyboard->sendLedMap(map, true); + keyboard->sendLedMap(map); } void PoliceLike::switchOnPeak(double peak) { diff --git a/src/VulcanoLE/Scripts/RainbowLine.cpp b/src/VulcanoLE/Scripts/RainbowLine.cpp index 1ccfcca..10d0102 100644 --- a/src/VulcanoLE/Scripts/RainbowLine.cpp +++ b/src/VulcanoLE/Scripts/RainbowLine.cpp @@ -3,7 +3,7 @@ #include #include -#define MAX_DELTA 808000 +#define MAX_DELTA 0.808 #define MAX_PEAK 170 namespace VIZ { @@ -17,7 +17,6 @@ namespace VIZ { } RainbowLine::~RainbowLine() { delete[] colours; - delete data; } void RainbowLine::onSetup() { currentColumn = 0; @@ -25,18 +24,11 @@ namespace VIZ { tailFactor = VUtils::Math::clamp(grabber->env->getAsDouble("rainbow_tail_factor", 0.3), 0.0, 0.9); keyboard->sendToLEDs({ 0, 0, 0, 0 }); grabber->requestMode = AudioGrabber::ReqMode::FFT; - usleep(100000); } void RainbowLine::onTick(float delta) { deltaElapsed += delta; - auto fftData = grabber->fft.getData()->leftChannel; - auto val = 0.0; - for (int i = 1; i < 4; ++i) { - auto avg = fftData[i]; - if (avg > val) - val = avg; - } + auto val = grabber->getBass(); val = VUtils::Math::clamp(val, 0, 255); double avg = deltaMove(val); if (deltaElapsed >= deltaNeeded) { @@ -49,7 +41,7 @@ namespace VIZ { moveLine(val, tailFactor); lastValue = avg; // we clean up the map self later! :) - keyboard->sendLedMap(data, false); + keyboard->sendLedMap(data); } double RainbowLine::deltaMove(double val) { @@ -63,7 +55,7 @@ namespace VIZ { firstUnder = true; } if (avg < 10) { - deltaNeeded = 1000000000; + deltaNeeded = 10; } if (decayValue >= 0) decayValue -= 10; @@ -88,7 +80,7 @@ namespace VIZ { auto column = keyboard->getColumn(col); for (int i = 0; i < column->count; ++i) { auto index = column->index[i]; - data[0].key[index] = colours[currentColumn]; + data.key[index] = colours[currentColumn]; } } } \ No newline at end of file diff --git a/src/VulcanoLE/Scripts/RainbowMap.cpp b/src/VulcanoLE/Scripts/RainbowMap.cpp index 8488d01..2a14d47 100644 --- a/src/VulcanoLE/Scripts/RainbowMap.cpp +++ b/src/VulcanoLE/Scripts/RainbowMap.cpp @@ -17,7 +17,6 @@ namespace VIZ { } RainbowMap::~RainbowMap() { delete[] colours; - delete data; } void RainbowMap::onSetup() { currentColumn = 0; @@ -48,7 +47,7 @@ namespace VIZ { moveRainbow(avg); lastValue = avg; // we clean up the map self later! :) - keyboard->sendLedMap(data, false); + keyboard->sendLedMap(data); } double RainbowMap::deltaMove(double val) { @@ -101,7 +100,7 @@ namespace VIZ { } for (int i = 0; i < column->count; ++i) { auto index = column->index[i]; - data[0].key[index] = colours[colIndex]; + data.key[index] = colours[colIndex]; } } } \ No newline at end of file diff --git a/src/VulcanoLE/Scripts/Random.cpp b/src/VulcanoLE/Scripts/Random.cpp index e867bfd..21bdea8 100644 --- a/src/VulcanoLE/Scripts/Random.cpp +++ b/src/VulcanoLE/Scripts/Random.cpp @@ -1,83 +1,44 @@ #include -#include -#include +#include +#include namespace VIZ { Random::Random(AudioGrabber *pGrabber, Vulcan121 *vulcan) : VIZ(pGrabber, vulcan) { + m_random.setDist(0, keyboardData.num_keys); } void Random::onSetup() { keyboard->sendToLEDs({ 0, 0, 0, 0 }); - if (map == nullptr) { - map = Vulcan121::createEmptyLEDMap(); - for (auto &color : map->key) { - color.r = 255; - color.b = 155; - color.a = 0; - } - } + Vulcan121::setColor(map, {255,155,0,255}); grabber->requestMode = AudioGrabber::ReqMode::FFT; - usleep(100000); } - void Random::onTick(float delta) { - deltaElapsed += delta; - auto fftData = grabber->fft.getData()->leftChannel; - auto val = 0.0; - for (int i = 1; i < 4; ++i) { - auto avg = fftData[i]; - if (avg > val) - val = avg; - } - val = VUtils::Math::clamp(val, 1,255); - if (deltaElapsed >= deltaNeeded) { - deltaElapsed -= deltaNeeded; - if (emptyTicks && ticks < 10) { - ticks++; - } else { - ticks = 0; - emptyTicks = false; - auto *kRow = keyboard->getRow(row); - columnCount += isReverse ? -1 : 1; - if (!isReverse && columnCount > kRow->count) { - row++; - if (row >= keyboardData.num_rows) { - emptyTicks = true; - isReverse = true; - row = keyboardData.num_rows-1; - } else { - columnCount = 0; - } - } else if (isReverse && columnCount < 0) { - row--; - if (row < 0) { - emptyTicks = true; - isReverse = false; - row = 0; - } else { - columnCount = kRow->count; - } + void Random::onTick(float /*delta*/) { + double val = grabber->getBass(); + double energy = val / 255.0; + uint16_t half = val / 2; + Vulcan121::fadeOutMap(map, (energy * 0.55) + 0.25); + m_angle += energy; + rgb nColor = Color::Generator::rgbFromHSV({ m_angle, 1.0, 1.0 }); + if (m_angle > 360) m_angle -= 360; + auto times = std::lround(energy * 4.5); + auto factor = 1.0; + // search for a less < halfPeak + for (int i = 0; i < times; i++) { + for (int j = 0; j < 3; j++) { + auto key = static_cast(m_random.getFast()); + auto &item = map.key[key]; + if (item.a < half) { + item.r = int16_t(nColor.r * 255); + item.g = int16_t(nColor.g * 255); + item.b = int16_t(nColor.b * 255); + item.a = static_cast(val * factor); + factor -= 0.2; + break; } } } - for (auto &color : map->key) - color.a = 0; - for (int i = 0; i <= row; ++i) { - auto *kRow = keyboard->getRow(i); - if (kRow) { - for (int j = 0; j < kRow->count; ++j) { - auto intVal = (int16_t) val; - if (i == row && j > columnCount) - intVal = 0; - map->key[kRow->index[j]].a = intVal; - } - } - } - keyboard->sendLedMap(map, false); + keyboard->sendLedMap(map); } const char *Random::name() { return "Random"; } - - Random::~Random() { - delete map; - } } \ No newline at end of file diff --git a/src/VulcanoLE/Scripts/Spectral.cpp b/src/VulcanoLE/Scripts/Spectral.cpp new file mode 100644 index 0000000..e2d2ad2 --- /dev/null +++ b/src/VulcanoLE/Scripts/Spectral.cpp @@ -0,0 +1,40 @@ +#include +#include + +namespace VIZ { + +Spectral::Spectral(AudioGrabber *pGrabber, Vulcan121 *pVulcan121) + : VIZ(pGrabber, pVulcan121) { + + double ri = 0; + for (int i = 1; i < keyboardData.num_rows; ++i) { + auto *row = keyboard->getRow(i); + double r = ri / row->count; + for (int j = 0; j < row->count; j++) { + map.key[row->index[j]] = Color::Generator::rgbFromRatio(r, 255); + } + ri++; + } +} + +void Spectral::onSetup() { + keyboard->sendToLEDs({0, 0, 0, 0}); + grabber->requestMode = AudioGrabber::ReqMode::FFT; +} + +void Spectral::onTick(float delta) { + auto *data = grabber->fft.getData(); + auto *channel = data->leftChannel; + for (int i = 0; i < keyboardData.num_cols; ++i) { + auto *col = keyboard->getColumn(i); + auto ratio = (channel[i + 1] / 128.0 * -1 + 1) * keyboardData.num_rows; + for (int j = 0; j < col->count; j++) { + map.key[col->index[j]].a = j < ratio ? 0 : channel[j]; + } + } + keyboard->sendLedMap(map); +} + +const char *Spectral::name() { return m_name.c_str(); } + +} // namespace VIZ diff --git a/src/VulcanoLE/Scripts/Spectrum.cpp b/src/VulcanoLE/Scripts/Spectrum.cpp index 81439db..845f4b6 100644 --- a/src/VulcanoLE/Scripts/Spectrum.cpp +++ b/src/VulcanoLE/Scripts/Spectrum.cpp @@ -1,43 +1,40 @@ +#include +#include +#include #include namespace VIZ { - Spectrum::Spectrum(AudioGrabber *pGrabber, Vulcan121 *pVulcan121) : VIZ(pGrabber, pVulcan121) {} +Spectrum::Spectrum(AudioGrabber *pGrabber, Vulcan121 *pVulcan121) + : VIZ(pGrabber, pVulcan121) {} - void Spectrum::onSetup() { - keyboard->sendToLEDs({ 0, 0, 0, 0 }); - grabber->requestMode = AudioGrabber::ReqMode::FFT; - usleep(100000); - } - void Spectrum::onTick(float delta) { - auto map = Vulcan121::createEmptyLEDMap(); - auto data = grabber->fft.getData()->leftChannel; - // find largest bass ~43hz (44100 / FFT_SIZE) range - auto val = 0.0; - for (int i = 1; i < 4; ++i) { - if (data[i] > val) { - val = data[i]; - } - } - double newVal = (val + lastVal) / 2.0; - lastVal = val; - int split = keyboardData.num_keys / 3; - int x = 0; - int colorInd = 0; - colors[0].a = newVal; - colors[1].a = newVal; - colors[2].a = newVal; - for (int key = 0; key < keyboardData.num_keys; ++key) { - map[0].key[key] = colors[colorInd]; - x++; - if (x > split) { - colorInd++; - x = 0; - } - } - keyboard->sendLedMap(map, true); +void Spectrum::onSetup() { + keyboard->sendToLEDs({0, 0, 0, 0}); + grabber->requestMode = AudioGrabber::ReqMode::FFT; +} +void Spectrum::onTick(float delta) { + led_map map{}; + auto *data = grabber->fft.getData()->leftChannel; + auto val = 0.0; + for (int i = 0; i < 3; ++i) { + if (data[i] > val) { + val = data[i]; } + } + double normalized = val / 255.0; + if (m_angle > 360.0) + m_angle -= 360.0; + m_angle += (normalized * normalized * normalized) * 5.0; + rgb nColor = + Color::Generator::rgbFromHSV({m_angle, 1.0, normalized * normalized}); + int16_t r = nColor.r * 255.0; + int16_t g = nColor.g * 255.0; + int16_t b = nColor.b * 255.0; + rgba color = {r, g, b, 255}; + for (int key = 0; key < keyboardData.num_keys; ++key) { + map.key[key] = color; + } + keyboard->sendLedMap(map); +} - const char *Spectrum::name() { - return m_name.c_str(); - } -} \ No newline at end of file +const char *Spectrum::name() { return m_name.c_str(); } +} // namespace VIZ diff --git a/src/VulcanoLE/Scripts/Strobo.cpp b/src/VulcanoLE/Scripts/Strobo.cpp new file mode 100644 index 0000000..05962d1 --- /dev/null +++ b/src/VulcanoLE/Scripts/Strobo.cpp @@ -0,0 +1,44 @@ +#include +#include +#include + +namespace VIZ { +Strobo::Strobo(AudioGrabber *pGrabber, Vulcan121 *pVulcan121) + : VIZ(pGrabber, pVulcan121) {} + +void Strobo::onSetup() { + keyboard->sendToLEDs({0, 0, 0, 0}); + grabber->requestMode = AudioGrabber::ReqMode::FFT; + int16_t r = (int16_t)grabber->env->getAsInt("strobo.color.r", 25); + int16_t g = (int16_t)grabber->env->getAsInt("strobo.color.g", 0); + int16_t b = (int16_t)grabber->env->getAsInt("strobo.color.b", 150); + m_color = {r, g, b, 255}; + m_isStanding = false; +} +void Strobo::onTick(float delta) { + led_map map{}; + auto bass = grabber->getBass(); + auto val = bass / 255.0; + m_isStanding = bass < 5; + + if (m_isStanding) { + m_color.a += 5; + if (m_color.a > 255) { + m_color.a = 255; + } + } else { + m_sinPoint += val * val; + if (m_sinPoint > 360) + m_sinPoint -= 360; + auto v = (std::sin(m_sinPoint) + 1) * 0.5; + m_color.a = (v * v) * 255; + } + + for (int key = 0; key < keyboardData.num_keys; ++key) { + map.key[key] = m_color; + } + keyboard->sendLedMap(map); +} + +const char *Strobo::name() { return m_name.c_str(); } +} // namespace VIZ diff --git a/src/VulcanoLE/Scripts/TheUnknown.cpp b/src/VulcanoLE/Scripts/TheUnknown.cpp new file mode 100644 index 0000000..87d0dc9 --- /dev/null +++ b/src/VulcanoLE/Scripts/TheUnknown.cpp @@ -0,0 +1,41 @@ +#include +#include + +namespace VIZ { + TheUnknown::TheUnknown(AudioGrabber *pGrabber, Vulcan121 *vulcan) : VIZ(pGrabber, vulcan) { + m_random.setDist(0, 128); + for (int i = 0; i < NUM_KEYS; i++) { + m_keyHeightMap[i] = std::abs(SimplexNoise::noise(i + 1.0f)) * 0.1; + m_keyOffset[i] = m_random.getFast(); + } + m_random.setDist(0.005, 0.01); + } + void TheUnknown::onSetup() { + keyboard->sendToLEDs({ 0, 0, 0, 0 }); + Vulcan121::setColor(map, { 202, 35, 13, 255 }); + grabber->requestMode = AudioGrabber::ReqMode::PEAK; + } + void TheUnknown::onTick(float delta) { + auto loudness = grabber->getLoudness(); + auto factor = (((loudness.r + loudness.l) / 2.0) * 0.9) + 0.05; + for (size_t i = 0; i < keyboardData.num_keys; i++) { + auto &color = map.key[i]; + auto noise = SimplexNoise::noise(m_keyOffset[i]) * factor; + noise = std::abs(noise) + 0.02; + auto newAlpha = std::clamp((noise + m_keyHeightMap[i]) * 255, 0.0, 255.0); + color.a = (color.a + newAlpha) / 2; + + auto offset = std::sin(m_angle) * m_random.getFast(); + m_keyOffset[i] += (delta * (1.5 * factor)) + offset; + m_angle += 0.0005; + if (m_keyOffset[i] > 20000000) + m_keyOffset[i] = 0.0; + if (m_angle > 1.0) + m_angle -= 1; + } + keyboard->sendLedMap(map); + } + const char *TheUnknown::name() { + return "TheUnknown"; + } +} \ No newline at end of file diff --git a/src/VulcanoLE/Visual/VisPlugins.cpp b/src/VulcanoLE/Visual/VisPlugins.cpp index 102c5ed..7fa1858 100644 --- a/src/VulcanoLE/Visual/VisPlugins.cpp +++ b/src/VulcanoLE/Visual/VisPlugins.cpp @@ -6,6 +6,10 @@ #include #include #include +#include +#include +#include +#include namespace VIZ { void VisPlugins::init(HIDHelper *hidHelper, AudioGrabber *audioGrabber) { @@ -17,6 +21,10 @@ namespace VIZ { viz[3] = new RainbowLine(grabber, keyboard); viz[4] = new Random(grabber, keyboard); viz[5] = new RainbowMap(grabber, keyboard); + viz[6] = new Spectral(grabber, keyboard); + viz[7] = new BassHistory(grabber, keyboard); + viz[8] = new TheUnknown(grabber, keyboard); + viz[9] = new Strobo(grabber, keyboard); currentVis = viz[mode]; } @@ -33,8 +41,8 @@ namespace VIZ { void VisPlugins::onTick() { std::lock_guard lockGuard(guard); auto stop = std::chrono::high_resolution_clock::now(); - auto delta = std::chrono::duration_cast(stop - start).count(); - currentVis->onTick(delta); + auto delta = std::chrono::duration_cast(stop - start).count() / 1000000.0; + currentVis->onTick((float) delta); frames++; #ifdef DEBUG auto fps = std::chrono::duration_cast(stop - frameStart).count(); @@ -44,7 +52,6 @@ namespace VIZ { frames = 0; } #endif - // DBG("Time Needed %f ms", delta / 1000) start = stop; }