DONT KNOW :D
This commit is contained in:
parent
4152ee0413
commit
8505032307
44 changed files with 1360 additions and 275 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,4 +1,6 @@
|
||||||
|
.cache/
|
||||||
/build/
|
/build/
|
||||||
|
/build-debug/
|
||||||
/helper/
|
/helper/
|
||||||
/cmake-build-debug/
|
/cmake-build-debug/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
|
@ -2,12 +2,13 @@ cmake_minimum_required(VERSION 3.17)
|
||||||
project(VulcanoLE)
|
project(VulcanoLE)
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
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")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
find_package(udev REQUIRED)
|
find_package(udev REQUIRED)
|
||||||
find_package(HIDAPI REQUIRED)
|
find_package(HIDAPI REQUIRED)
|
||||||
pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
|
pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
|
||||||
find_package(Threads REQUIRED)
|
|
||||||
|
|
||||||
find_package(JACK REQUIRED)
|
find_package(JACK REQUIRED)
|
||||||
|
|
||||||
|
@ -27,14 +28,21 @@ set(SOURCE_FILES
|
||||||
src/VulcanoLE/Scripts/RainbowLine.cpp
|
src/VulcanoLE/Scripts/RainbowLine.cpp
|
||||||
src/VulcanoLE/Scripts/Random.cpp
|
src/VulcanoLE/Scripts/Random.cpp
|
||||||
src/VulcanoLE/Scripts/RainbowMap.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
|
set(UTILS_FILES
|
||||||
src/VUtils/Logging.cpp
|
src/VUtils/Logging.cpp
|
||||||
src/VUtils/FileHandler.cpp
|
src/VUtils/FileHandler.cpp
|
||||||
src/VUtils/Pool.cpp
|
src/VUtils/Pool.cpp
|
||||||
src/VUtils/Environment.cpp
|
src/VUtils/Environment.cpp
|
||||||
|
src/VUtils/Random.cpp
|
||||||
src/VUtils/StringUtils.cpp
|
src/VUtils/StringUtils.cpp
|
||||||
src/VUtils/Math.cpp
|
src/VUtils/Math.cpp
|
||||||
|
src/VUtils/SimplexNoise.cpp
|
||||||
)
|
)
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/headers/)
|
include_directories(${CMAKE_SOURCE_DIR}/headers/)
|
||||||
add_executable(
|
add_executable(
|
||||||
|
|
|
@ -118,7 +118,7 @@ find_path(HIDAPI_INCLUDE_DIR
|
||||||
${PC_HIDAPI_HIDRAW_INCLUDE_DIRS}
|
${PC_HIDAPI_HIDRAW_INCLUDE_DIRS}
|
||||||
${PC_HIDAPI_LIBUSB_INCLUDE_DIRS})
|
${PC_HIDAPI_LIBUSB_INCLUDE_DIRS})
|
||||||
|
|
||||||
find_package(Threads QUIET)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
###
|
###
|
||||||
# Compute the "I don't care which backend" library
|
# Compute the "I don't care which backend" library
|
||||||
|
|
|
@ -1,19 +1,49 @@
|
||||||
# Copyright (c) 2015 Andrew Kelley
|
# Try to find JACK
|
||||||
# This file is MIT licensed.
|
# This will define the following variables:
|
||||||
# See http://opensource.org/licenses/MIT
|
#
|
||||||
|
# JACK_FOUND - Whether Jack was found.
|
||||||
# JACK_FOUND
|
# JACK_INCLUDE_DIRS - Jack include directories.
|
||||||
# JACK_INCLUDE_DIR
|
# JACK_LIBRARIES - Jack libraries.
|
||||||
# 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)
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
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)
|
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()
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <map>
|
||||||
|
|
||||||
namespace VUtils {
|
namespace VUtils {
|
||||||
class Environment {
|
class Environment {
|
||||||
|
@ -20,7 +20,7 @@ namespace VUtils {
|
||||||
void set(const char* name, const char *value);
|
void set(const char* name, const char *value);
|
||||||
void setNumber(const char* name, double value);
|
void setNumber(const char* name, double value);
|
||||||
protected:
|
protected:
|
||||||
std::unordered_map<std::string, std::string> m_env;
|
std::map<std::string, std::string> m_env;
|
||||||
std::string m_prefix = "VENV_";
|
std::string m_prefix = "VENV_";
|
||||||
std::string m_filename;
|
std::string m_filename;
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,5 +7,6 @@ namespace VUtils {
|
||||||
static double bezierBlend(double t);
|
static double bezierBlend(double t);
|
||||||
static double easeIn(double ratio);
|
static double easeIn(double ratio);
|
||||||
static double map(double x, double in_min, double in_max, double out_min, double out_max);
|
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);
|
||||||
};
|
};
|
||||||
}
|
}
|
23
headers/VUtils/Random.h
Normal file
23
headers/VUtils/Random.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
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<double> m_dist;
|
||||||
|
};
|
||||||
|
}
|
55
headers/VUtils/SimplexNoise.h
Normal file
55
headers/VUtils/SimplexNoise.h
Normal file
|
@ -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 <cstddef> // 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)
|
||||||
|
};
|
|
@ -7,6 +7,7 @@
|
||||||
#include <VUtils/Environment.h>
|
#include <VUtils/Environment.h>
|
||||||
#include <VulcanoLE/Audio/FFT.h>
|
#include <VulcanoLE/Audio/FFT.h>
|
||||||
#include <VulcanoLE/Audio/Types.h>
|
#include <VulcanoLE/Audio/Types.h>
|
||||||
|
#include <VulcanoLE/Audio/Filter.h>
|
||||||
|
|
||||||
class AudioGrabber {
|
class AudioGrabber {
|
||||||
public:
|
public:
|
||||||
|
@ -14,19 +15,22 @@ public:
|
||||||
FFT = 0,
|
FFT = 0,
|
||||||
RMS = 1,
|
RMS = 1,
|
||||||
PEAK = 2,
|
PEAK = 2,
|
||||||
ALL = 3
|
FILTER = 3,
|
||||||
|
ALL = 4
|
||||||
};
|
};
|
||||||
AudioGrabber();
|
AudioGrabber();
|
||||||
~AudioGrabber();
|
~AudioGrabber();
|
||||||
bool read(stereoSample *buffer, uint32_t bufferSize);
|
bool read(stereoSample *buffer, uint32_t bufferSize);
|
||||||
static AudioGrabber* createAudioGrabber();
|
static AudioGrabber* createAudioGrabber();
|
||||||
void init();
|
void init();
|
||||||
|
Audio::FilterHelper& getFilter();
|
||||||
FFT fft;
|
FFT fft;
|
||||||
ReqMode requestMode = ReqMode::FFT;
|
ReqMode requestMode = ReqMode::FFT;
|
||||||
stereoSampleFrame loudness = {0.0, 0.0};
|
stereoSampleFrame loudness = {0.0, 0.0};
|
||||||
stereoSampleFrame getLoudness();
|
stereoSampleFrame getLoudness();
|
||||||
bool work();
|
bool work();
|
||||||
VUtils::Environment *env = nullptr;
|
VUtils::Environment *env = nullptr;
|
||||||
|
double getBass();
|
||||||
private:
|
private:
|
||||||
std::mutex m_mtx;
|
std::mutex m_mtx;
|
||||||
stereoSample *m_buffer{};
|
stereoSample *m_buffer{};
|
||||||
|
@ -34,4 +38,5 @@ private:
|
||||||
void calculatePEAK(stereoSample *pFrame);
|
void calculatePEAK(stereoSample *pFrame);
|
||||||
double m_scale = 1.0;
|
double m_scale = 1.0;
|
||||||
int availableData = 0;
|
int availableData = 0;
|
||||||
|
Audio::FilterHelper m_filter;
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,6 +12,7 @@ public:
|
||||||
outputSample *getData();
|
outputSample *getData();
|
||||||
bool prepareInput(stereoSample *buffer, uint32_t sampleSize, double scale);
|
bool prepareInput(stereoSample *buffer, uint32_t sampleSize, double scale);
|
||||||
double hannWindow[BUFFER_SIZE]{};
|
double hannWindow[BUFFER_SIZE]{};
|
||||||
|
int size = BUFFER_SIZE;
|
||||||
protected:
|
protected:
|
||||||
fftw_complex *m_fftwInputLeft{};
|
fftw_complex *m_fftwInputLeft{};
|
||||||
fftw_complex *m_fftwInputRight{};
|
fftw_complex *m_fftwInputRight{};
|
||||||
|
@ -25,4 +26,5 @@ protected:
|
||||||
static double valueToAmp(double value);
|
static double valueToAmp(double value);
|
||||||
double times;
|
double times;
|
||||||
static double limit;
|
static double limit;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
30
headers/VulcanoLE/Audio/Filter.h
Normal file
30
headers/VulcanoLE/Audio/Filter.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#include "Types.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<stereoSample> output;
|
||||||
|
stereoSampleFrame work(stereoSample *buffer, int size);
|
||||||
|
protected:
|
||||||
|
double m_limit = FILTER_LIMIT;
|
||||||
|
int overshot_times = 0;
|
||||||
|
stereoSampleFrame doLimit(double, double);
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#define FFT_SIZE 512
|
|
||||||
#define BUFFER_SIZE 1024
|
#define BUFFER_SIZE 1024
|
||||||
|
|
||||||
struct stereoSampleFrame {
|
struct stereoSampleFrame {
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
#include <VulcanoLE/Colors/Type.h>
|
#include <VulcanoLE/Colors/Type.h>
|
||||||
|
|
||||||
namespace Color {
|
namespace Color {
|
||||||
struct Generator {
|
struct Generator {
|
||||||
static rgba rgbFromRatio(double ratio, int16_t alpha);
|
static rgba rgbFromRatio(double ratio, int16_t alpha);
|
||||||
};
|
static rgb rgbFromHSV(hsv in);
|
||||||
}
|
};
|
||||||
|
} // namespace Color
|
||||||
|
|
|
@ -3,8 +3,20 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
typedef struct rgba_type {
|
typedef struct rgba_type {
|
||||||
int16_t r{};
|
int16_t r = 0;
|
||||||
int16_t g{};
|
int16_t g = 0;
|
||||||
int16_t b{};
|
int16_t b = 0;
|
||||||
int16_t a = 255;
|
int16_t a = 0;
|
||||||
} rgba;
|
} 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;
|
||||||
|
|
|
@ -20,13 +20,12 @@ class Vulcan121 {
|
||||||
public:
|
public:
|
||||||
explicit Vulcan121(HIDHelper *helper);
|
explicit Vulcan121(HIDHelper *helper);
|
||||||
~Vulcan121();
|
~Vulcan121();
|
||||||
int sendLedMap(led_map *src, bool deleteMap = false);
|
int sendLedMap(led_map& src);
|
||||||
int sendToLEDs(rgba rgb);
|
int sendToLEDs(rgba rgb);
|
||||||
bool sendInitSequence();
|
bool sendInitSequence();
|
||||||
bool queryCtrlReport(unsigned char id);
|
bool queryCtrlReport(unsigned char id);
|
||||||
bool sendCtrlReport(unsigned char id);
|
bool sendCtrlReport(unsigned char id);
|
||||||
bool waitForCtrlDev();
|
bool waitForCtrlDev();
|
||||||
static led_map *createEmptyLEDMap();
|
|
||||||
struct DATA {
|
struct DATA {
|
||||||
int num_rows = NUM_ROWS;
|
int num_rows = NUM_ROWS;
|
||||||
int num_cols = NUM_COLS;
|
int num_cols = NUM_COLS;
|
||||||
|
@ -39,8 +38,10 @@ public:
|
||||||
int getIndex(int row, int col);
|
int getIndex(int row, int col);
|
||||||
// PLEASE MAKE SURE YOU KNOW THE LIMITS!
|
// PLEASE MAKE SURE YOU KNOW THE LIMITS!
|
||||||
int getIndexNoCheck(int row, int col);
|
int getIndexNoCheck(int row, int col);
|
||||||
static void fadeOutMap(led_map* map, double factor);
|
static void fadeOutMap(led_map& map, double factor);
|
||||||
void fadeOutMapColumn(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:
|
protected:
|
||||||
void setupMap();
|
void setupMap();
|
||||||
// we need some mapping feature! rows and cols dont have the same amount of keys. so the struct needs
|
// we need some mapping feature! rows and cols dont have the same amount of keys. so the struct needs
|
||||||
|
|
20
headers/VulcanoLE/Scripts/BassHistory.h
Normal file
20
headers/VulcanoLE/Scripts/BassHistory.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <VulcanoLE/Visual/VIZ.h>
|
||||||
|
#include <VulcanoLE/Audio/AudioGrabber.h>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
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<double> m_history;
|
||||||
|
void pushNew(double val);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -13,8 +13,11 @@ namespace VIZ {
|
||||||
void onTick(float delta) override;
|
void onTick(float delta) override;
|
||||||
void setForChannel(float value, int channel);
|
void setForChannel(float value, int channel);
|
||||||
void drawFrame(int toRow);
|
void drawFrame(int toRow);
|
||||||
|
void drawSetup(float val);
|
||||||
const char *name() override;
|
const char *name() override;
|
||||||
std::string m_name = "Loudness Meter";
|
std::string m_name = "Loudness Meter";
|
||||||
|
bool isSetup = true;
|
||||||
|
double frameWidth = 0;
|
||||||
protected:
|
protected:
|
||||||
rgba colours[3] = {
|
rgba colours[3] = {
|
||||||
{ 0, 0, 255, 80 },
|
{ 0, 0, 255, 80 },
|
||||||
|
@ -22,7 +25,7 @@ namespace VIZ {
|
||||||
{ 255, 0, 0, 40 }
|
{ 255, 0, 0, 40 }
|
||||||
};
|
};
|
||||||
double tailFactor = 0;
|
double tailFactor = 0;
|
||||||
led_map *data = Vulcan121::createEmptyLEDMap();
|
led_map data{};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
namespace VIZ {
|
namespace VIZ {
|
||||||
class RainbowLine : public VIZ {
|
class RainbowLine : public VIZ {
|
||||||
int currentColumn = 0;
|
int currentColumn = 0;
|
||||||
double deltaNeeded = 100000.0;
|
double deltaNeeded = 0.0;
|
||||||
double deltaElapsed = 0;
|
double deltaElapsed = 0;
|
||||||
rgba *colours = nullptr;
|
rgba *colours = nullptr;
|
||||||
int maxCols = 0;
|
int maxCols = 0;
|
||||||
double lastValue = 0;
|
double lastValue = 0;
|
||||||
double decayValue = 0;
|
double decayValue = 0;
|
||||||
double tailFactor = 0.3;
|
double tailFactor = 0.5;
|
||||||
public:
|
public:
|
||||||
RainbowLine(AudioGrabber *pGrabber, Vulcan121 *vulcan);
|
RainbowLine(AudioGrabber *pGrabber, Vulcan121 *vulcan);
|
||||||
~RainbowLine() override;
|
~RainbowLine() override;
|
||||||
|
@ -20,7 +20,7 @@ namespace VIZ {
|
||||||
void calcNextDelta(double ratio);
|
void calcNextDelta(double ratio);
|
||||||
const char *name() override;
|
const char *name() override;
|
||||||
std::string m_name = "Rainbow Line";
|
std::string m_name = "Rainbow Line";
|
||||||
led_map *data = Vulcan121::createEmptyLEDMap();
|
led_map data{};
|
||||||
bool firstUnder = true;
|
bool firstUnder = true;
|
||||||
double deltaMove(double val);
|
double deltaMove(double val);
|
||||||
void moveLine(double val, double d);
|
void moveLine(double val, double d);
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace VIZ {
|
||||||
void calcNextDelta(double ratio);
|
void calcNextDelta(double ratio);
|
||||||
const char *name() override;
|
const char *name() override;
|
||||||
std::string m_name = "Rainbow Map";
|
std::string m_name = "Rainbow Map";
|
||||||
led_map *data = Vulcan121::createEmptyLEDMap();
|
led_map data{};
|
||||||
bool firstUnder = true;
|
bool firstUnder = true;
|
||||||
double deltaMove(double val);
|
double deltaMove(double val);
|
||||||
void moveRainbow(double d);
|
void moveRainbow(double d);
|
||||||
|
|
|
@ -4,23 +4,18 @@
|
||||||
#include <VulcanoLE/Keyboards/Vulcan121.h>
|
#include <VulcanoLE/Keyboards/Vulcan121.h>
|
||||||
#include <VulcanoLE/Visual/VIZ.h>
|
#include <VulcanoLE/Visual/VIZ.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "VUtils/Random.h"
|
||||||
|
|
||||||
namespace VIZ {
|
namespace VIZ {
|
||||||
class Random : public VIZ {
|
class Random : public VIZ {
|
||||||
protected:
|
protected:
|
||||||
led_map *map = nullptr;
|
led_map map{};
|
||||||
bool isReverse{ false };
|
VUtils::Random m_random;
|
||||||
bool emptyTicks{ false };
|
double m_angle{0.0};
|
||||||
public:
|
public:
|
||||||
Random(AudioGrabber *pGrabber, Vulcan121 *vulcan);
|
Random(AudioGrabber *pGrabber, Vulcan121 *vulcan);
|
||||||
~Random() override;
|
|
||||||
void onSetup() override;
|
void onSetup() override;
|
||||||
void onTick(float delta) override;
|
void onTick(float delta) override;
|
||||||
const char *name() 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;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
18
headers/VulcanoLE/Scripts/Spectral.h
Normal file
18
headers/VulcanoLE/Scripts/Spectral.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <VulcanoLE/Visual/VIZ.h>
|
||||||
|
#include <VulcanoLE/Audio/AudioGrabber.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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{};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <VulcanoLE/Visual/VIZ.h>
|
|
||||||
#include <VulcanoLE/Audio/AudioGrabber.h>
|
#include <VulcanoLE/Audio/AudioGrabber.h>
|
||||||
|
#include <VulcanoLE/Visual/VIZ.h>
|
||||||
|
|
||||||
namespace VIZ {
|
namespace VIZ {
|
||||||
struct Spectrum : public VIZ {
|
struct Spectrum : public VIZ {
|
||||||
|
public:
|
||||||
|
static constexpr int maxColors = 3;
|
||||||
Spectrum(AudioGrabber *pGrabber, Vulcan121 *pVulcan121);
|
Spectrum(AudioGrabber *pGrabber, Vulcan121 *pVulcan121);
|
||||||
~Spectrum() override = default;
|
~Spectrum() override = default;
|
||||||
void onSetup() override;
|
void onSetup() override;
|
||||||
void onTick(float delta) override;
|
void onTick(float delta) override;
|
||||||
double lastVal = 0;
|
|
||||||
const char *name() override;
|
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 }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string m_name = "Spectrum One";
|
||||||
|
double m_angle = 0.0;
|
||||||
|
};
|
||||||
|
} // namespace VIZ
|
||||||
|
|
21
headers/VulcanoLE/Scripts/Strobo.h
Normal file
21
headers/VulcanoLE/Scripts/Strobo.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <VulcanoLE/Audio/AudioGrabber.h>
|
||||||
|
#include <VulcanoLE/Visual/VIZ.h>
|
||||||
|
|
||||||
|
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
|
24
headers/VulcanoLE/Scripts/TheUknown.h
Normal file
24
headers/VulcanoLE/Scripts/TheUknown.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <VulcanoLE/Audio/AudioGrabber.h>
|
||||||
|
#include <VulcanoLE/Keyboards/Vulcan121.h>
|
||||||
|
#include <VulcanoLE/Visual/VIZ.h>
|
||||||
|
#include <vector>
|
||||||
|
#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;
|
||||||
|
};
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <VulcanoLE/Scripts/Spectrum.h>
|
#include <VulcanoLE/Scripts/Spectrum.h>
|
||||||
|
|
||||||
#define VIZSIZE 6
|
#define VIZSIZE 10
|
||||||
|
|
||||||
using micro = std::chrono::duration<double, std::micro>;
|
using micro = std::chrono::duration<double, std::micro>;
|
||||||
using ms = std::chrono::duration<double, std::milli>;
|
using ms = std::chrono::duration<double, std::milli>;
|
||||||
|
|
4
main.cpp
4
main.cpp
|
@ -37,7 +37,7 @@ int main(int argc, char **argv) {
|
||||||
if (helper.openDevices() < 0) {
|
if (helper.openDevices() < 0) {
|
||||||
ERR("Unable to find Keyboard!")
|
ERR("Unable to find Keyboard!")
|
||||||
exit(0);
|
exit(0);
|
||||||
};
|
}
|
||||||
usleep(10000);
|
usleep(10000);
|
||||||
auto runner = VisAudioRunner::create();
|
auto runner = VisAudioRunner::create();
|
||||||
runner->env = &config;
|
runner->env = &config;
|
||||||
|
@ -46,7 +46,7 @@ int main(int argc, char **argv) {
|
||||||
runner->plugins->setCurrentMode(config.getAsInt("visual_mode", 1));
|
runner->plugins->setCurrentMode(config.getAsInt("visual_mode", 1));
|
||||||
while (shouldRun) {
|
while (shouldRun) {
|
||||||
int mode;
|
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;
|
std::cin >> mode;
|
||||||
if (std::cin.fail()) {
|
if (std::cin.fail()) {
|
||||||
ERR("ERROR -- You did not enter an integer")
|
ERR("ERROR -- You did not enter an integer")
|
||||||
|
|
|
@ -17,4 +17,16 @@ namespace VUtils {
|
||||||
double Math::map(double x, double in_min, double in_max, double out_min, double out_max) {
|
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;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
21
src/VUtils/Random.cpp
Normal file
21
src/VUtils/Random.cpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#include <VUtils/Random.h>
|
||||||
|
|
||||||
|
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<double> 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);
|
||||||
|
}
|
||||||
|
}
|
475
src/VUtils/SimplexNoise.cpp
Normal file
475
src/VUtils/SimplexNoise.cpp
Normal file
|
@ -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 <VUtils/SimplexNoise.h>
|
||||||
|
|
||||||
|
#include <cstdint> // 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<int32_t>(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<uint8_t>(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<float>(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<y0
|
||||||
|
if (y0 < z0) {
|
||||||
|
i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; // Z Y X order
|
||||||
|
} else if (x0 < z0) {
|
||||||
|
i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; // Y Z X order
|
||||||
|
} else {
|
||||||
|
i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; // Y X Z order
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
|
||||||
|
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
|
||||||
|
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
|
||||||
|
// c = 1/6.
|
||||||
|
float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
|
||||||
|
float y1 = y0 - j1 + G3;
|
||||||
|
float z1 = z0 - k1 + G3;
|
||||||
|
float x2 = x0 - i2 + 2.0f * G3; // Offsets for third corner in (x,y,z) coords
|
||||||
|
float y2 = y0 - j2 + 2.0f * G3;
|
||||||
|
float z2 = z0 - k2 + 2.0f * G3;
|
||||||
|
float x3 = x0 - 1.0f + 3.0f * G3; // Offsets for last corner in (x,y,z) coords
|
||||||
|
float y3 = y0 - 1.0f + 3.0f * G3;
|
||||||
|
float z3 = z0 - 1.0f + 3.0f * G3;
|
||||||
|
|
||||||
|
// Work out the hashed gradient indices of the four simplex corners
|
||||||
|
int gi0 = hash(i + hash(j + hash(k)));
|
||||||
|
int gi1 = hash(i + i1 + hash(j + j1 + hash(k + k1)));
|
||||||
|
int gi2 = hash(i + i2 + hash(j + j2 + hash(k + k2)));
|
||||||
|
int gi3 = hash(i + 1 + hash(j + 1 + hash(k + 1)));
|
||||||
|
|
||||||
|
// Calculate the contribution from the four corners
|
||||||
|
float t0 = 0.6f - x0*x0 - y0*y0 - z0*z0;
|
||||||
|
if (t0 < 0) {
|
||||||
|
n0 = 0.0;
|
||||||
|
} else {
|
||||||
|
t0 *= t0;
|
||||||
|
n0 = t0 * t0 * grad(gi0, x0, y0, z0);
|
||||||
|
}
|
||||||
|
float t1 = 0.6f - x1*x1 - y1*y1 - z1*z1;
|
||||||
|
if (t1 < 0) {
|
||||||
|
n1 = 0.0;
|
||||||
|
} else {
|
||||||
|
t1 *= t1;
|
||||||
|
n1 = t1 * t1 * grad(gi1, x1, y1, z1);
|
||||||
|
}
|
||||||
|
float t2 = 0.6f - x2*x2 - y2*y2 - z2*z2;
|
||||||
|
if (t2 < 0) {
|
||||||
|
n2 = 0.0;
|
||||||
|
} else {
|
||||||
|
t2 *= t2;
|
||||||
|
n2 = t2 * t2 * grad(gi2, x2, y2, z2);
|
||||||
|
}
|
||||||
|
float t3 = 0.6f - x3*x3 - y3*y3 - z3*z3;
|
||||||
|
if (t3 < 0) {
|
||||||
|
n3 = 0.0;
|
||||||
|
} else {
|
||||||
|
t3 *= t3;
|
||||||
|
n3 = t3 * t3 * grad(gi3, x3, y3, z3);
|
||||||
|
}
|
||||||
|
// Add contributions from each corner to get the final noise value.
|
||||||
|
// The result is scaled to stay just inside [-1,1]
|
||||||
|
return 32.0f*(n0 + n1 + n2 + n3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fractal/Fractional Brownian Motion (fBm) summation of 1D Perlin Simplex noise
|
||||||
|
*
|
||||||
|
* @param[in] octaves number of fraction of noise to sum
|
||||||
|
* @param[in] x float coordinate
|
||||||
|
*
|
||||||
|
* @return Noise value in the range[-1; 1], value of 0 on all integer coordinates.
|
||||||
|
*/
|
||||||
|
float SimplexNoise::fractal(size_t octaves, float x) const {
|
||||||
|
float output = 0.f;
|
||||||
|
float denom = 0.f;
|
||||||
|
float frequency = mFrequency;
|
||||||
|
float amplitude = mAmplitude;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < octaves; i++) {
|
||||||
|
output += (amplitude * noise(x * frequency));
|
||||||
|
denom += amplitude;
|
||||||
|
|
||||||
|
frequency *= mLacunarity;
|
||||||
|
amplitude *= mPersistence;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (output / denom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fractal/Fractional Brownian Motion (fBm) summation of 2D Perlin Simplex noise
|
||||||
|
*
|
||||||
|
* @param[in] octaves number of fraction of noise to sum
|
||||||
|
* @param[in] x x float coordinate
|
||||||
|
* @param[in] y y float coordinate
|
||||||
|
*
|
||||||
|
* @return Noise value in the range[-1; 1], value of 0 on all integer coordinates.
|
||||||
|
*/
|
||||||
|
float SimplexNoise::fractal(size_t octaves, float x, float y) const {
|
||||||
|
float output = 0.f;
|
||||||
|
float denom = 0.f;
|
||||||
|
float frequency = mFrequency;
|
||||||
|
float amplitude = mAmplitude;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < octaves; i++) {
|
||||||
|
output += (amplitude * noise(x * frequency, y * frequency));
|
||||||
|
denom += amplitude;
|
||||||
|
|
||||||
|
frequency *= mLacunarity;
|
||||||
|
amplitude *= mPersistence;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (output / denom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fractal/Fractional Brownian Motion (fBm) summation of 3D Perlin Simplex noise
|
||||||
|
*
|
||||||
|
* @param[in] octaves number of fraction of noise to sum
|
||||||
|
* @param[in] x x float coordinate
|
||||||
|
* @param[in] y y float coordinate
|
||||||
|
* @param[in] z z float coordinate
|
||||||
|
*
|
||||||
|
* @return Noise value in the range[-1; 1], value of 0 on all integer coordinates.
|
||||||
|
*/
|
||||||
|
float SimplexNoise::fractal(size_t octaves, float x, float y, float z) const {
|
||||||
|
float output = 0.f;
|
||||||
|
float denom = 0.f;
|
||||||
|
float frequency = mFrequency;
|
||||||
|
float amplitude = mAmplitude;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < octaves; i++) {
|
||||||
|
output += (amplitude * noise(x * frequency, y * frequency, z * frequency));
|
||||||
|
denom += amplitude;
|
||||||
|
|
||||||
|
frequency *= mLacunarity;
|
||||||
|
amplitude *= mPersistence;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (output / denom);
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
#include <VulcanoLE/Audio/AudioGrabber.h>
|
#include <VulcanoLE/Audio/AudioGrabber.h>
|
||||||
#include <VUtils/Logging.h>
|
#include <VUtils/Logging.h>
|
||||||
#include <VulcanoLE/Audio/JackClient.h>
|
#include <VulcanoLE/Audio/JackClient.h>
|
||||||
|
#include "VUtils/Math.h"
|
||||||
|
|
||||||
|
|
||||||
AudioGrabber::AudioGrabber() = default;
|
AudioGrabber::AudioGrabber() = default;
|
||||||
|
@ -27,27 +28,28 @@ void AudioGrabber::init() {
|
||||||
m_buffer = static_cast<stereoSample *>(calloc(BUFFER_SIZE, sizeof(stereoSample)));
|
m_buffer = static_cast<stereoSample *>(calloc(BUFFER_SIZE, sizeof(stereoSample)));
|
||||||
if (env != nullptr)
|
if (env != nullptr)
|
||||||
m_scale = env->getAsDouble("audio_scale", 1.0);
|
m_scale = env->getAsDouble("audio_scale", 1.0);
|
||||||
|
m_filter.scale = 1.0;
|
||||||
DBG("SET Audio Scale: %.3f", m_scale)
|
DBG("SET Audio Scale: %.3f", m_scale)
|
||||||
loudness = { 0.0, 0.0 };
|
loudness = { 0.0, 0.0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGrabber::calculateRMS(stereoSample *pFrame) {
|
void AudioGrabber::calculateRMS(stereoSample *pFrame) {
|
||||||
float squareL = 0, meanL;
|
double squareL = 0, meanL;
|
||||||
float squareR = 0, meanR;
|
double squareR = 0, meanR;
|
||||||
for (int i = 0; i < availableData; i++) {
|
for (int i = 0; i < availableData; i++) {
|
||||||
squareL += std::pow(pFrame[0].l * m_scale, 2);
|
squareL += std::pow(pFrame[i].l * m_scale, 2);
|
||||||
squareR += std::pow(pFrame[0].r * m_scale, 2);
|
squareR += std::pow(pFrame[i].r * m_scale, 2);
|
||||||
}
|
}
|
||||||
meanL = (squareL / (float) (BUFFER_SIZE));
|
meanL = (squareL / (float) (availableData));
|
||||||
meanR = (squareR / (float) (BUFFER_SIZE));
|
meanR = (squareR / (float) (availableData));
|
||||||
loudness = { std::sqrt(meanL), std::sqrt(meanR) };
|
loudness = { (float) std::sqrt(meanL), (float) std::sqrt(meanR) };
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGrabber::calculatePEAK(stereoSample *pFrame) {
|
void AudioGrabber::calculatePEAK(stereoSample *pFrame) {
|
||||||
stereoSampleFrame max = { 0, 0 };
|
stereoSampleFrame max = { 0, 0 };
|
||||||
for (int i = 0; i < availableData; i++) {
|
for (int i = 0; i < availableData; i++) {
|
||||||
float left = std::abs(pFrame[0].l * m_scale);
|
auto left = (float) std::abs(pFrame[i].l * m_scale);
|
||||||
float right = std::abs(pFrame[0].r * m_scale);
|
auto right = (float) std::abs(pFrame[i].r * m_scale);
|
||||||
if (left > max.l)
|
if (left > max.l)
|
||||||
max.l = left;
|
max.l = left;
|
||||||
if (right > max.r)
|
if (right > max.r)
|
||||||
|
@ -74,6 +76,9 @@ bool AudioGrabber::work() {
|
||||||
case ReqMode::PEAK:
|
case ReqMode::PEAK:
|
||||||
calculatePEAK(m_buffer);
|
calculatePEAK(m_buffer);
|
||||||
break;
|
break;
|
||||||
|
case ReqMode::FILTER:
|
||||||
|
loudness = m_filter.work(m_buffer, availableData);
|
||||||
|
break;
|
||||||
default: {
|
default: {
|
||||||
calculateRMS(m_buffer);
|
calculateRMS(m_buffer);
|
||||||
calculatePEAK(m_buffer);
|
calculatePEAK(m_buffer);
|
||||||
|
@ -85,3 +90,16 @@ bool AudioGrabber::work() {
|
||||||
return false;
|
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);
|
||||||
|
}
|
||||||
|
|
71
src/VulcanoLE/Audio/Filter.cpp
Normal file
71
src/VulcanoLE/Audio/Filter.cpp
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#include <VulcanoLE/Audio/Filter.h>
|
||||||
|
#include <VulcanoLE/Audio/Types.h>
|
||||||
|
#include <valarray>
|
||||||
|
#include <VUtils/Math.h>
|
||||||
|
#include <VUtils/Logging.h>
|
||||||
|
|
||||||
|
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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
#include <VulcanoLE/Colors/ColorHelper.h>
|
#include <VulcanoLE/Colors/ColorHelper.h>
|
||||||
|
|
||||||
namespace Color {
|
namespace Color {
|
||||||
rgba Generator::rgbFromRatio(double ratio, int16_t alpha = 255) {
|
rgba Generator::rgbFromRatio(double ratio, int16_t alpha = 255) {
|
||||||
int normalized = int(ratio * 256 * 6);
|
int normalized = int(ratio * 256 * 6);
|
||||||
int x = normalized % 256;
|
int x = normalized % 256;
|
||||||
|
|
||||||
|
@ -39,6 +39,64 @@ namespace Color {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { red, green, blue, alpha };
|
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
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
|
|
||||||
Vulcan121::Vulcan121(HIDHelper *helper)
|
Vulcan121::Vulcan121(HIDHelper *helper)
|
||||||
: m_helper(helper) {
|
: m_helper(helper) {
|
||||||
for (auto &item : keyMapRow) {
|
for (auto &item: keyMapRow) {
|
||||||
item = new keys;
|
item = new keys;
|
||||||
}
|
}
|
||||||
for (auto &item : keyMapCol) {
|
for (auto &item: keyMapCol) {
|
||||||
item = new keys;
|
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
|
// 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.
|
// 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;
|
int i, k;
|
||||||
rgba rgb;
|
rgba rgb;
|
||||||
unsigned char ledBuffer[448]{};
|
unsigned char ledBuffer[448]{};
|
||||||
|
@ -41,7 +41,7 @@ int Vulcan121::sendLedMap(led_map *src, bool deleteMap) {
|
||||||
ledBuffer[3] = 0xb4;
|
ledBuffer[3] = 0xb4;
|
||||||
for (k = 0; k < NUM_KEYS; k++) {
|
for (k = 0; k < NUM_KEYS; k++) {
|
||||||
// Prepare Color
|
// 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);
|
rgb.a = (int16_t) VUtils::Math::clamp(use.a, 0, 255);
|
||||||
double factor = rgb.a / 255.0;
|
double factor = rgb.a / 255.0;
|
||||||
rgb.r = (int16_t) VUtils::Math::clamp(std::round(use.r * factor), 0, 255);
|
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);
|
memcpy(&buffer[1], &ledBuffer[index], 64);
|
||||||
if (hid_write(m_helper->m_ledDevice, buffer, 65) != 65) {
|
if (hid_write(m_helper->m_ledDevice, buffer, 65) != 65) {
|
||||||
ERR("Write failed")
|
ERR("Write failed")
|
||||||
if (deleteMap)
|
|
||||||
delete src;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (deleteMap)
|
|
||||||
delete src;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int Vulcan121::sendToLEDs(rgba rgb) {
|
int Vulcan121::sendToLEDs(rgba rgb) {
|
||||||
auto *map = new led_map();
|
auto map = led_map();
|
||||||
for (auto &i : map->key) {
|
for (auto &i: map.key) {
|
||||||
i = rgb;
|
i = rgb;
|
||||||
}
|
}
|
||||||
int st = sendLedMap(map, true);
|
int st = sendLedMap(map);
|
||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
|
||||||
led_map *Vulcan121::createEmptyLEDMap() {
|
|
||||||
return new led_map();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Vulcan121::sendInitSequence() {
|
bool Vulcan121::sendInitSequence() {
|
||||||
LOG("Sending device init sequence...")
|
LOG("Sending device init sequence...")
|
||||||
unsigned char a[9] = { 0x15, 0x05, 0x07, 0x0a, 0x0b, 0x06, 0x09, 0x0d, 0x13 };
|
unsigned char a[9] = { 0x15, 0x05, 0x07, 0x0a, 0x0b, 0x06, 0x09, 0x0d, 0x13 };
|
||||||
if (!queryCtrlReport(0x0f))
|
if (!queryCtrlReport(0x0f))
|
||||||
return false;
|
return false;
|
||||||
for (auto i : a) {
|
for (auto i: a) {
|
||||||
if (!sendCtrlReport(i) || !waitForCtrlDev()) {
|
if (!sendCtrlReport(i) || !waitForCtrlDev()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -327,25 +319,41 @@ const keys *Vulcan121::getRow(int row) {
|
||||||
return keyMapRow[row];
|
return keyMapRow[row];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vulcan121::fadeOutMap(led_map *map, double factor = 0.3) {
|
void Vulcan121::fadeOutMap(led_map &map, double factor = 0.3) {
|
||||||
for (auto &i : map->key) {
|
for (auto &i: map.key) {
|
||||||
i.a *= factor;
|
i.a *= factor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Column FadeOut! Check if previous Col is brighter!
|
// 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) {
|
for (int i = 0; i < NUM_COLS; ++i) {
|
||||||
// prev check if not 0
|
// prev check if not 0
|
||||||
auto *columnA = getColumn(i);
|
auto *columnA = getColumn(i);
|
||||||
int16_t colorB = 0;
|
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);
|
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) {
|
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;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
56
src/VulcanoLE/Scripts/BassHistory.cpp
Normal file
56
src/VulcanoLE/Scripts/BassHistory.cpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include <VulcanoLE/Scripts/BassHistory.h>
|
||||||
|
#include <VulcanoLE/Colors/ColorHelper.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,19 +4,46 @@ namespace VIZ {
|
||||||
Loudness::Loudness(AudioGrabber *pGrabber, Vulcan121 *pVulcan121) : VIZ(pGrabber, pVulcan121) {}
|
Loudness::Loudness(AudioGrabber *pGrabber, Vulcan121 *pVulcan121) : VIZ(pGrabber, pVulcan121) {}
|
||||||
|
|
||||||
void Loudness::onSetup() {
|
void Loudness::onSetup() {
|
||||||
keyboard->sendToLEDs({ 0, 0, 0, 0 });
|
if (isSetup) {
|
||||||
grabber->requestMode = AudioGrabber::ReqMode::PEAK;
|
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);
|
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) {
|
void Loudness::onTick(float delta) {
|
||||||
Vulcan121::fadeOutMap(data, tailFactor);
|
if (isSetup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
stereoSample val = grabber->getLoudness();
|
stereoSample val = grabber->getLoudness();
|
||||||
val.l = val.l > 1.0f ? 1.0f : val.l;
|
val.l = val.l > 1.0f ? 1.0f : val.l;
|
||||||
val.r = val.r > 1.0f ? 1.0f : val.r;
|
val.r = val.r > 1.0f ? 1.0f : val.r;
|
||||||
setForChannel(val.l, 0);
|
setForChannel(val.l, 0);
|
||||||
setForChannel(val.r, 1);
|
setForChannel(val.r, 1);
|
||||||
keyboard->sendLedMap(data, false);
|
drawFrame(0);
|
||||||
|
drawFrame(5);
|
||||||
|
keyboard->sendLedMap(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *Loudness::name() {
|
const char *Loudness::name() {
|
||||||
|
@ -27,24 +54,34 @@ namespace VIZ {
|
||||||
// because we have stereo we need to move on the rows
|
// because we have stereo we need to move on the rows
|
||||||
int offset = channel == 0 ? 1 : 3;
|
int offset = channel == 0 ? 1 : 3;
|
||||||
int until = channel == 0 ? 3 : 5;
|
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) {
|
for (int i = offset; i < until; ++i) {
|
||||||
auto row = keyboard->getRow(i);
|
auto row = keyboard->getRow(i);
|
||||||
int to = row->count * value;
|
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];
|
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) {
|
void Loudness::drawFrame(int toRow) {
|
||||||
auto row = keyboard->getRow(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];
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ namespace VIZ {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PoliceLike::onTick(float delta) {
|
void PoliceLike::onTick(float delta) {
|
||||||
auto map = Vulcan121::createEmptyLEDMap();
|
led_map map{};
|
||||||
auto data = grabber->fft.getData()->leftChannel;
|
auto data = grabber->fft.getData()->leftChannel;
|
||||||
auto val = 0.0;
|
auto val = 0.0;
|
||||||
for (int i = 1; i < 3; ++i) {
|
for (int i = 1; i < 3; ++i) {
|
||||||
|
@ -29,10 +29,10 @@ namespace VIZ {
|
||||||
for (int i = 0; i < max; ++i) {
|
for (int i = 0; i < max; ++i) {
|
||||||
auto col = keyboard->getColumn(i + colOff);
|
auto col = keyboard->getColumn(i + colOff);
|
||||||
for (int j = 0; j < col->count; ++j) {
|
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) {
|
void PoliceLike::switchOnPeak(double peak) {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include <VUtils/Logging.h>
|
#include <VUtils/Logging.h>
|
||||||
#include <VUtils/Math.h>
|
#include <VUtils/Math.h>
|
||||||
|
|
||||||
#define MAX_DELTA 808000
|
#define MAX_DELTA 0.808
|
||||||
#define MAX_PEAK 170
|
#define MAX_PEAK 170
|
||||||
|
|
||||||
namespace VIZ {
|
namespace VIZ {
|
||||||
|
@ -17,7 +17,6 @@ namespace VIZ {
|
||||||
}
|
}
|
||||||
RainbowLine::~RainbowLine() {
|
RainbowLine::~RainbowLine() {
|
||||||
delete[] colours;
|
delete[] colours;
|
||||||
delete data;
|
|
||||||
}
|
}
|
||||||
void RainbowLine::onSetup() {
|
void RainbowLine::onSetup() {
|
||||||
currentColumn = 0;
|
currentColumn = 0;
|
||||||
|
@ -25,18 +24,11 @@ namespace VIZ {
|
||||||
tailFactor = VUtils::Math::clamp(grabber->env->getAsDouble("rainbow_tail_factor", 0.3), 0.0, 0.9);
|
tailFactor = VUtils::Math::clamp(grabber->env->getAsDouble("rainbow_tail_factor", 0.3), 0.0, 0.9);
|
||||||
keyboard->sendToLEDs({ 0, 0, 0, 0 });
|
keyboard->sendToLEDs({ 0, 0, 0, 0 });
|
||||||
grabber->requestMode = AudioGrabber::ReqMode::FFT;
|
grabber->requestMode = AudioGrabber::ReqMode::FFT;
|
||||||
usleep(100000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RainbowLine::onTick(float delta) {
|
void RainbowLine::onTick(float delta) {
|
||||||
deltaElapsed += delta;
|
deltaElapsed += delta;
|
||||||
auto fftData = grabber->fft.getData()->leftChannel;
|
auto val = grabber->getBass();
|
||||||
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, 0, 255);
|
val = VUtils::Math::clamp(val, 0, 255);
|
||||||
double avg = deltaMove(val);
|
double avg = deltaMove(val);
|
||||||
if (deltaElapsed >= deltaNeeded) {
|
if (deltaElapsed >= deltaNeeded) {
|
||||||
|
@ -49,7 +41,7 @@ namespace VIZ {
|
||||||
moveLine(val, tailFactor);
|
moveLine(val, tailFactor);
|
||||||
lastValue = avg;
|
lastValue = avg;
|
||||||
// we clean up the map self later! :)
|
// we clean up the map self later! :)
|
||||||
keyboard->sendLedMap(data, false);
|
keyboard->sendLedMap(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
double RainbowLine::deltaMove(double val) {
|
double RainbowLine::deltaMove(double val) {
|
||||||
|
@ -63,7 +55,7 @@ namespace VIZ {
|
||||||
firstUnder = true;
|
firstUnder = true;
|
||||||
}
|
}
|
||||||
if (avg < 10) {
|
if (avg < 10) {
|
||||||
deltaNeeded = 1000000000;
|
deltaNeeded = 10;
|
||||||
}
|
}
|
||||||
if (decayValue >= 0)
|
if (decayValue >= 0)
|
||||||
decayValue -= 10;
|
decayValue -= 10;
|
||||||
|
@ -88,7 +80,7 @@ namespace VIZ {
|
||||||
auto column = keyboard->getColumn(col);
|
auto column = keyboard->getColumn(col);
|
||||||
for (int i = 0; i < column->count; ++i) {
|
for (int i = 0; i < column->count; ++i) {
|
||||||
auto index = column->index[i];
|
auto index = column->index[i];
|
||||||
data[0].key[index] = colours[currentColumn];
|
data.key[index] = colours[currentColumn];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,7 +17,6 @@ namespace VIZ {
|
||||||
}
|
}
|
||||||
RainbowMap::~RainbowMap() {
|
RainbowMap::~RainbowMap() {
|
||||||
delete[] colours;
|
delete[] colours;
|
||||||
delete data;
|
|
||||||
}
|
}
|
||||||
void RainbowMap::onSetup() {
|
void RainbowMap::onSetup() {
|
||||||
currentColumn = 0;
|
currentColumn = 0;
|
||||||
|
@ -48,7 +47,7 @@ namespace VIZ {
|
||||||
moveRainbow(avg);
|
moveRainbow(avg);
|
||||||
lastValue = avg;
|
lastValue = avg;
|
||||||
// we clean up the map self later! :)
|
// we clean up the map self later! :)
|
||||||
keyboard->sendLedMap(data, false);
|
keyboard->sendLedMap(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
double RainbowMap::deltaMove(double val) {
|
double RainbowMap::deltaMove(double val) {
|
||||||
|
@ -101,7 +100,7 @@ namespace VIZ {
|
||||||
}
|
}
|
||||||
for (int i = 0; i < column->count; ++i) {
|
for (int i = 0; i < column->count; ++i) {
|
||||||
auto index = column->index[i];
|
auto index = column->index[i];
|
||||||
data[0].key[index] = colours[colIndex];
|
data.key[index] = colours[colIndex];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,83 +1,44 @@
|
||||||
#include <VulcanoLE/Scripts/Random.h>
|
#include <VulcanoLE/Scripts/Random.h>
|
||||||
#include <VUtils/Logging.h>
|
#include <VulcanoLE/Colors/ColorHelper.h>
|
||||||
#include <VUtils/Math.h>
|
#include <cmath>
|
||||||
|
|
||||||
namespace VIZ {
|
namespace VIZ {
|
||||||
Random::Random(AudioGrabber *pGrabber, Vulcan121 *vulcan) : VIZ(pGrabber, vulcan) {
|
Random::Random(AudioGrabber *pGrabber, Vulcan121 *vulcan) : VIZ(pGrabber, vulcan) {
|
||||||
|
m_random.setDist(0, keyboardData.num_keys);
|
||||||
}
|
}
|
||||||
void Random::onSetup() {
|
void Random::onSetup() {
|
||||||
keyboard->sendToLEDs({ 0, 0, 0, 0 });
|
keyboard->sendToLEDs({ 0, 0, 0, 0 });
|
||||||
if (map == nullptr) {
|
Vulcan121::setColor(map, {255,155,0,255});
|
||||||
map = Vulcan121::createEmptyLEDMap();
|
|
||||||
for (auto &color : map->key) {
|
|
||||||
color.r = 255;
|
|
||||||
color.b = 155;
|
|
||||||
color.a = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
grabber->requestMode = AudioGrabber::ReqMode::FFT;
|
grabber->requestMode = AudioGrabber::ReqMode::FFT;
|
||||||
usleep(100000);
|
|
||||||
}
|
}
|
||||||
void Random::onTick(float delta) {
|
void Random::onTick(float /*delta*/) {
|
||||||
deltaElapsed += delta;
|
double val = grabber->getBass();
|
||||||
auto fftData = grabber->fft.getData()->leftChannel;
|
double energy = val / 255.0;
|
||||||
auto val = 0.0;
|
uint16_t half = val / 2;
|
||||||
for (int i = 1; i < 4; ++i) {
|
Vulcan121::fadeOutMap(map, (energy * 0.55) + 0.25);
|
||||||
auto avg = fftData[i];
|
m_angle += energy;
|
||||||
if (avg > val)
|
rgb nColor = Color::Generator::rgbFromHSV({ m_angle, 1.0, 1.0 });
|
||||||
val = avg;
|
if (m_angle > 360) m_angle -= 360;
|
||||||
}
|
auto times = std::lround(energy * 4.5);
|
||||||
val = VUtils::Math::clamp(val, 1,255);
|
auto factor = 1.0;
|
||||||
if (deltaElapsed >= deltaNeeded) {
|
// search for a less < halfPeak
|
||||||
deltaElapsed -= deltaNeeded;
|
for (int i = 0; i < times; i++) {
|
||||||
if (emptyTicks && ticks < 10) {
|
for (int j = 0; j < 3; j++) {
|
||||||
ticks++;
|
auto key = static_cast<int>(m_random.getFast());
|
||||||
} else {
|
auto &item = map.key[key];
|
||||||
ticks = 0;
|
if (item.a < half) {
|
||||||
emptyTicks = false;
|
item.r = int16_t(nColor.r * 255);
|
||||||
auto *kRow = keyboard->getRow(row);
|
item.g = int16_t(nColor.g * 255);
|
||||||
columnCount += isReverse ? -1 : 1;
|
item.b = int16_t(nColor.b * 255);
|
||||||
if (!isReverse && columnCount > kRow->count) {
|
item.a = static_cast<int16_t>(val * factor);
|
||||||
row++;
|
factor -= 0.2;
|
||||||
if (row >= keyboardData.num_rows) {
|
break;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
keyboard->sendLedMap(map);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
const char *Random::name() {
|
const char *Random::name() {
|
||||||
return "Random";
|
return "Random";
|
||||||
}
|
}
|
||||||
|
|
||||||
Random::~Random() {
|
|
||||||
delete map;
|
|
||||||
}
|
|
||||||
}
|
}
|
40
src/VulcanoLE/Scripts/Spectral.cpp
Normal file
40
src/VulcanoLE/Scripts/Spectral.cpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#include <VulcanoLE/Colors/ColorHelper.h>
|
||||||
|
#include <VulcanoLE/Scripts/Spectral.h>
|
||||||
|
|
||||||
|
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
|
|
@ -1,43 +1,40 @@
|
||||||
|
#include <VUtils/Logging.h>
|
||||||
|
#include <VUtils/Math.h>
|
||||||
|
#include <VulcanoLE/Colors/ColorHelper.h>
|
||||||
#include <VulcanoLE/Scripts/Spectrum.h>
|
#include <VulcanoLE/Scripts/Spectrum.h>
|
||||||
|
|
||||||
namespace VIZ {
|
namespace VIZ {
|
||||||
Spectrum::Spectrum(AudioGrabber *pGrabber, Vulcan121 *pVulcan121) : VIZ(pGrabber, pVulcan121) {}
|
Spectrum::Spectrum(AudioGrabber *pGrabber, Vulcan121 *pVulcan121)
|
||||||
|
: VIZ(pGrabber, pVulcan121) {}
|
||||||
|
|
||||||
void Spectrum::onSetup() {
|
void Spectrum::onSetup() {
|
||||||
keyboard->sendToLEDs({ 0, 0, 0, 0 });
|
keyboard->sendToLEDs({0, 0, 0, 0});
|
||||||
grabber->requestMode = AudioGrabber::ReqMode::FFT;
|
grabber->requestMode = AudioGrabber::ReqMode::FFT;
|
||||||
usleep(100000);
|
}
|
||||||
}
|
void Spectrum::onTick(float delta) {
|
||||||
void Spectrum::onTick(float delta) {
|
led_map map{};
|
||||||
auto map = Vulcan121::createEmptyLEDMap();
|
auto *data = grabber->fft.getData()->leftChannel;
|
||||||
auto data = grabber->fft.getData()->leftChannel;
|
|
||||||
// find largest bass ~43hz (44100 / FFT_SIZE) range
|
|
||||||
auto val = 0.0;
|
auto val = 0.0;
|
||||||
for (int i = 1; i < 4; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
if (data[i] > val) {
|
if (data[i] > val) {
|
||||||
val = data[i];
|
val = data[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
double newVal = (val + lastVal) / 2.0;
|
double normalized = val / 255.0;
|
||||||
lastVal = val;
|
if (m_angle > 360.0)
|
||||||
int split = keyboardData.num_keys / 3;
|
m_angle -= 360.0;
|
||||||
int x = 0;
|
m_angle += (normalized * normalized * normalized) * 5.0;
|
||||||
int colorInd = 0;
|
rgb nColor =
|
||||||
colors[0].a = newVal;
|
Color::Generator::rgbFromHSV({m_angle, 1.0, normalized * normalized});
|
||||||
colors[1].a = newVal;
|
int16_t r = nColor.r * 255.0;
|
||||||
colors[2].a = newVal;
|
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) {
|
for (int key = 0; key < keyboardData.num_keys; ++key) {
|
||||||
map[0].key[key] = colors[colorInd];
|
map.key[key] = color;
|
||||||
x++;
|
|
||||||
if (x > split) {
|
|
||||||
colorInd++;
|
|
||||||
x = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyboard->sendLedMap(map, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *Spectrum::name() {
|
|
||||||
return m_name.c_str();
|
|
||||||
}
|
}
|
||||||
|
keyboard->sendLedMap(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *Spectrum::name() { return m_name.c_str(); }
|
||||||
|
} // namespace VIZ
|
||||||
|
|
44
src/VulcanoLE/Scripts/Strobo.cpp
Normal file
44
src/VulcanoLE/Scripts/Strobo.cpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#include <VUtils/Random.h>
|
||||||
|
#include <VulcanoLE/Colors/ColorHelper.h>
|
||||||
|
#include <VulcanoLE/Scripts/Strobo.h>
|
||||||
|
|
||||||
|
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
|
41
src/VulcanoLE/Scripts/TheUnknown.cpp
Normal file
41
src/VulcanoLE/Scripts/TheUnknown.cpp
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#include <VulcanoLE/Scripts/TheUknown.h>
|
||||||
|
#include <VUtils/SimplexNoise.h>
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,10 @@
|
||||||
#include <VulcanoLE/Scripts/RainbowLine.h>
|
#include <VulcanoLE/Scripts/RainbowLine.h>
|
||||||
#include <VulcanoLE/Scripts/Random.h>
|
#include <VulcanoLE/Scripts/Random.h>
|
||||||
#include <VulcanoLE/Scripts/RainbowMap.h>
|
#include <VulcanoLE/Scripts/RainbowMap.h>
|
||||||
|
#include <VulcanoLE/Scripts/Spectral.h>
|
||||||
|
#include <VulcanoLE/Scripts/BassHistory.h>
|
||||||
|
#include <VulcanoLE/Scripts/TheUknown.h>
|
||||||
|
#include <VulcanoLE/Scripts/Strobo.h>
|
||||||
|
|
||||||
namespace VIZ {
|
namespace VIZ {
|
||||||
void VisPlugins::init(HIDHelper *hidHelper, AudioGrabber *audioGrabber) {
|
void VisPlugins::init(HIDHelper *hidHelper, AudioGrabber *audioGrabber) {
|
||||||
|
@ -17,6 +21,10 @@ namespace VIZ {
|
||||||
viz[3] = new RainbowLine(grabber, keyboard);
|
viz[3] = new RainbowLine(grabber, keyboard);
|
||||||
viz[4] = new Random(grabber, keyboard);
|
viz[4] = new Random(grabber, keyboard);
|
||||||
viz[5] = new RainbowMap(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];
|
currentVis = viz[mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,8 +41,8 @@ namespace VIZ {
|
||||||
void VisPlugins::onTick() {
|
void VisPlugins::onTick() {
|
||||||
std::lock_guard<std::mutex> lockGuard(guard);
|
std::lock_guard<std::mutex> lockGuard(guard);
|
||||||
auto stop = std::chrono::high_resolution_clock::now();
|
auto stop = std::chrono::high_resolution_clock::now();
|
||||||
auto delta = std::chrono::duration_cast<micro>(stop - start).count();
|
auto delta = std::chrono::duration_cast<micro>(stop - start).count() / 1000000.0;
|
||||||
currentVis->onTick(delta);
|
currentVis->onTick((float) delta);
|
||||||
frames++;
|
frames++;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
auto fps = std::chrono::duration_cast<ms>(stop - frameStart).count();
|
auto fps = std::chrono::duration_cast<ms>(stop - frameStart).count();
|
||||||
|
@ -44,7 +52,6 @@ namespace VIZ {
|
||||||
frames = 0;
|
frames = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// DBG("Time Needed %f ms", delta / 1000)
|
|
||||||
start = stop;
|
start = stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue