WIP
This commit is contained in:
parent
d0e8eb0d5e
commit
4152ee0413
33 changed files with 27356 additions and 233 deletions
|
@ -8,16 +8,14 @@ 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(Threads REQUIRED)
|
||||||
find_library(PULSE_FOUND NAMES pulse)
|
|
||||||
if (PULSE_FOUND)
|
find_package(JACK REQUIRED)
|
||||||
set(DYNAMIC_LIBRARIES ${DYNAMIC_LIBRARIES} pulse pulse-simple)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_ENABLE_PULSE")
|
|
||||||
set(CMAKE_LD_FLAGS "${CMAKE_LD_FLAGS} -D_ENABLE_PULSE")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
src/VulcanoLE/API/HIDHelper.cpp
|
src/VulcanoLE/API/HIDHelper.cpp
|
||||||
|
src/VulcanoLE/API/I2C.cpp
|
||||||
src/VulcanoLE/Keyboards/Vulcan121.cpp
|
src/VulcanoLE/Keyboards/Vulcan121.cpp
|
||||||
|
src/VulcanoLE/Audio/JackClient.cpp
|
||||||
src/VulcanoLE/Audio/AudioGrabber.cpp
|
src/VulcanoLE/Audio/AudioGrabber.cpp
|
||||||
src/VulcanoLE/Audio/FFT.cpp
|
src/VulcanoLE/Audio/FFT.cpp
|
||||||
src/VulcanoLE/Audio/VisAudioRunner.cpp
|
src/VulcanoLE/Audio/VisAudioRunner.cpp
|
||||||
|
@ -27,6 +25,8 @@ set(SOURCE_FILES
|
||||||
src/VulcanoLE/Scripts/Spectrum.cpp
|
src/VulcanoLE/Scripts/Spectrum.cpp
|
||||||
src/VulcanoLE/Scripts/PoliceLike.cpp
|
src/VulcanoLE/Scripts/PoliceLike.cpp
|
||||||
src/VulcanoLE/Scripts/RainbowLine.cpp
|
src/VulcanoLE/Scripts/RainbowLine.cpp
|
||||||
|
src/VulcanoLE/Scripts/Random.cpp
|
||||||
|
src/VulcanoLE/Scripts/RainbowMap.cpp
|
||||||
)
|
)
|
||||||
set(UTILS_FILES
|
set(UTILS_FILES
|
||||||
src/VUtils/Logging.cpp
|
src/VUtils/Logging.cpp
|
||||||
|
@ -41,5 +41,5 @@ add_executable(
|
||||||
VulcanoLE main.cpp
|
VulcanoLE main.cpp
|
||||||
${SOURCE_FILES} ${UTILS_FILES}
|
${SOURCE_FILES} ${UTILS_FILES}
|
||||||
)
|
)
|
||||||
target_link_libraries(VulcanoLE fftw3 evdev hidapi-libusb udev ${CMAKE_DL_LIBS} ${DYNAMIC_LIBRARIES} Threads::Threads m
|
target_link_libraries(VulcanoLE fftw3 evdev hidapi-libusb udev jack ${CMAKE_DL_LIBS} ${DYNAMIC_LIBRARIES} Threads::Threads m
|
||||||
debug tbb)
|
debug tbb)
|
||||||
|
|
19
cmake/FindJACK.cmake
Normal file
19
cmake/FindJACK.cmake
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
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)
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace VUtils {
|
namespace VUtils {
|
||||||
class FileHandler {
|
class FileHandler {
|
||||||
|
@ -17,6 +18,7 @@ namespace VUtils {
|
||||||
static bool createDirectoryIfNotExist(const std::basic_string<char>& fileName);
|
static bool createDirectoryIfNotExist(const std::basic_string<char>& fileName);
|
||||||
static char * getHomeDirectory();
|
static char * getHomeDirectory();
|
||||||
static std::string getFromHomeDir(const std::basic_string<char>& path);
|
static std::string getFromHomeDir(const std::basic_string<char>& path);
|
||||||
|
static std::vector<std::string> readDir(const std::basic_string<char>& path);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,5 +6,6 @@ namespace VUtils {
|
||||||
static double lerp(double a, double b, double f);
|
static double lerp(double a, double b, double f);
|
||||||
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);
|
||||||
};
|
};
|
||||||
}
|
}
|
12
headers/VulcanoLE/API/I2C.h
Normal file
12
headers/VulcanoLE/API/I2C.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct I2CDevice {
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class I2C {
|
||||||
|
public:
|
||||||
|
int readDevices();
|
||||||
|
};
|
|
@ -1,8 +1,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <pulse/error.h>
|
|
||||||
#include <pulse/pulseaudio.h>
|
|
||||||
#include <pulse/simple.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -12,14 +8,6 @@
|
||||||
#include <VulcanoLE/Audio/FFT.h>
|
#include <VulcanoLE/Audio/FFT.h>
|
||||||
#include <VulcanoLE/Audio/Types.h>
|
#include <VulcanoLE/Audio/Types.h>
|
||||||
|
|
||||||
static const char recStreamName[] = "vulcanoLE";
|
|
||||||
static const char recStreamDescription[] = "Keyboard Input Stream";
|
|
||||||
|
|
||||||
static const int32_t sampleRate = 44100;
|
|
||||||
static const int32_t channels = 2;
|
|
||||||
|
|
||||||
static const std::string defaultMonitorPostfix = ".monitor";
|
|
||||||
|
|
||||||
class AudioGrabber {
|
class AudioGrabber {
|
||||||
public:
|
public:
|
||||||
enum class ReqMode {
|
enum class ReqMode {
|
||||||
|
@ -42,17 +30,8 @@ public:
|
||||||
private:
|
private:
|
||||||
std::mutex m_mtx;
|
std::mutex m_mtx;
|
||||||
stereoSample *m_buffer{};
|
stereoSample *m_buffer{};
|
||||||
pa_simple *m_pulseaudioSimple{};
|
|
||||||
pa_mainloop *m_pulseaudioMainloop{};
|
|
||||||
std::string m_PulseaudioDefaultSourceName;
|
|
||||||
void populateDefaultSourceName();
|
|
||||||
bool openPulseaudioSource(uint32_t maxBufferSize);
|
|
||||||
static void pulseaudioContextStateCallback(pa_context *c,
|
|
||||||
void *userdata);
|
|
||||||
static void pulseaudioServerInfoCallback(pa_context *context,
|
|
||||||
const pa_server_info *i,
|
|
||||||
void *userdata);
|
|
||||||
void calculateRMS(stereoSample *pFrame);
|
void calculateRMS(stereoSample *pFrame);
|
||||||
void calculatePEAK(stereoSample *pFrame);
|
void calculatePEAK(stereoSample *pFrame);
|
||||||
double m_scale = 1.0;
|
double m_scale = 1.0;
|
||||||
|
int availableData = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,14 +11,18 @@ public:
|
||||||
void process(stereoSample *frame, double d);
|
void process(stereoSample *frame, double d);
|
||||||
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]{};
|
||||||
protected:
|
protected:
|
||||||
double *m_fftwInputLeft{};
|
fftw_complex *m_fftwInputLeft{};
|
||||||
double *m_fftwInputRight{};
|
fftw_complex *m_fftwInputRight{};
|
||||||
fftw_complex *m_fftwOutputLeft{};
|
fftw_complex *m_fftwOutputLeft{};
|
||||||
fftw_complex *m_fftwOutputRight{};
|
fftw_complex *m_fftwOutputRight{};
|
||||||
outputSample *m_sample = new outputSample(FFT_SIZE);
|
outputSample *m_sample = new outputSample(BUFFER_SIZE);
|
||||||
fftw_plan m_fftwPlanLeft{};
|
fftw_plan m_fftwPlanLeft{};
|
||||||
fftw_plan m_fftwPlanRight{};
|
fftw_plan m_fftwPlanRight{};
|
||||||
std::mutex m_mtx;
|
std::mutex m_mtx;
|
||||||
size_t m_fftwResults;
|
size_t m_fftwResults;
|
||||||
|
static double valueToAmp(double value);
|
||||||
|
double times;
|
||||||
|
static double limit;
|
||||||
};
|
};
|
||||||
|
|
46
headers/VulcanoLE/Audio/JackClient.h
Normal file
46
headers/VulcanoLE/Audio/JackClient.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <VUtils/Environment.h>
|
||||||
|
#include <jack/jack.h>
|
||||||
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
|
#include <VUtils/Logging.h>
|
||||||
|
#include "Types.h"
|
||||||
|
#include "AudioGrabber.h"
|
||||||
|
|
||||||
|
class JackClient {
|
||||||
|
public:
|
||||||
|
static JackClient &get() {
|
||||||
|
static JackClient client;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
void start();
|
||||||
|
static int processSamples(jack_nframes_t frames, void *arg);
|
||||||
|
static void onExit();
|
||||||
|
void connect(const char *port_name, jack_port_t* channel);
|
||||||
|
void fillSamples(stereoSample *pFrame, uint32_t i) {
|
||||||
|
std::lock_guard<std::mutex> lockGuard(guard);
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
pFrame[j].l = samples[j].l;
|
||||||
|
pFrame[j].r = samples[j].r;
|
||||||
|
}
|
||||||
|
m_newDataAvailable = false;
|
||||||
|
}
|
||||||
|
bool isData();
|
||||||
|
std::string getPort(bool isLeft);
|
||||||
|
bool checkPort(const std::string& port);
|
||||||
|
VUtils::Environment* env{};
|
||||||
|
int getFrames();
|
||||||
|
AudioGrabber *grabber;
|
||||||
|
private:
|
||||||
|
std::mutex guard;
|
||||||
|
stereoSample samples[BUFFER_SIZE]{};
|
||||||
|
stereoSample ringBuffer[BUFFER_SIZE]{};
|
||||||
|
int samplesAdded = 0;
|
||||||
|
jack_port_t *input_port_l = nullptr;
|
||||||
|
jack_port_t *input_port_r = nullptr;
|
||||||
|
bool m_newDataAvailable = false;
|
||||||
|
jack_client_t *client = nullptr;
|
||||||
|
jack_options_t options = jack_options_t(JackNoStartServer | JackUseExactName);
|
||||||
|
void addFrames(jack_default_audio_sample_t *pDouble, jack_default_audio_sample_t *pDouble1, jack_nframes_t i);
|
||||||
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#define FFT_SIZE 1024
|
#define FFT_SIZE 512
|
||||||
#define BUFFER_SIZE 2048
|
#define BUFFER_SIZE 1024
|
||||||
|
|
||||||
struct stereoSampleFrame {
|
struct stereoSampleFrame {
|
||||||
float l;
|
float l;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <VulcanoLE/Audio/AudioGrabber.h>
|
#include <VulcanoLE/Audio/AudioGrabber.h>
|
||||||
#include <VulcanoLE/Visual/VisPlugins.h>
|
#include <VulcanoLE/Visual/VisPlugins.h>
|
||||||
#include <VUtils/Environment.h>
|
#include <VUtils/Environment.h>
|
||||||
|
#include <VulcanoLE/API/I2C.h>
|
||||||
|
|
||||||
class VisAudioRunner {
|
class VisAudioRunner {
|
||||||
public:
|
public:
|
||||||
|
@ -16,5 +17,6 @@ public:
|
||||||
VIZ::VisPlugins* plugins;
|
VIZ::VisPlugins* plugins;
|
||||||
bool isActive = true;
|
bool isActive = true;
|
||||||
std::thread thread;
|
std::thread thread;
|
||||||
|
I2C i2c;
|
||||||
VUtils::Environment *env = nullptr;
|
VUtils::Environment *env = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,6 +40,7 @@ public:
|
||||||
// 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);
|
||||||
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
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
namespace VIZ {
|
namespace VIZ {
|
||||||
class PoliceLike : public VIZ {
|
class PoliceLike : public VIZ {
|
||||||
protected:
|
protected:
|
||||||
int decayRate = 10;
|
int decayRate = 5;
|
||||||
double lastPeak = -1;
|
double lastPeak = -1;
|
||||||
double threshold = 15;
|
double threshold = 45;
|
||||||
public:
|
public:
|
||||||
PoliceLike(AudioGrabber *pGrabber, Vulcan121 *pVulcan121);
|
PoliceLike(AudioGrabber *pGrabber, Vulcan121 *pVulcan121);
|
||||||
~PoliceLike() override = default;
|
~PoliceLike() override = default;
|
||||||
|
|
|
@ -8,9 +8,9 @@ namespace VIZ {
|
||||||
double deltaNeeded = 100000.0;
|
double deltaNeeded = 100000.0;
|
||||||
double deltaElapsed = 0;
|
double deltaElapsed = 0;
|
||||||
rgba *colours = nullptr;
|
rgba *colours = nullptr;
|
||||||
|
int maxCols = 0;
|
||||||
double lastValue = 0;
|
double lastValue = 0;
|
||||||
double decayValue = 0;
|
double decayValue = 0;
|
||||||
double ratios[4] = {1.3,1.3,1.3,1.2};
|
|
||||||
double tailFactor = 0.3;
|
double tailFactor = 0.3;
|
||||||
public:
|
public:
|
||||||
RainbowLine(AudioGrabber *pGrabber, Vulcan121 *vulcan);
|
RainbowLine(AudioGrabber *pGrabber, Vulcan121 *vulcan);
|
||||||
|
@ -23,5 +23,7 @@ namespace VIZ {
|
||||||
led_map *data = Vulcan121::createEmptyLEDMap();
|
led_map *data = Vulcan121::createEmptyLEDMap();
|
||||||
bool firstUnder = true;
|
bool firstUnder = true;
|
||||||
double deltaMove(double val);
|
double deltaMove(double val);
|
||||||
|
void moveLine(double val, double d);
|
||||||
|
double factor = 1.0;
|
||||||
};
|
};
|
||||||
}
|
}
|
28
headers/VulcanoLE/Scripts/RainbowMap.h
Normal file
28
headers/VulcanoLE/Scripts/RainbowMap.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <VulcanoLE/Visual/VIZ.h>
|
||||||
|
|
||||||
|
namespace VIZ {
|
||||||
|
class RainbowMap : public VIZ {
|
||||||
|
int currentColumn = 0;
|
||||||
|
double deltaNeeded = 100000.0;
|
||||||
|
double deltaElapsed = 0;
|
||||||
|
rgba *colours = nullptr;
|
||||||
|
int maxCols = 0;
|
||||||
|
double lastValue = 0;
|
||||||
|
double decayValue = 0;
|
||||||
|
public:
|
||||||
|
RainbowMap(AudioGrabber *pGrabber, Vulcan121 *vulcan);
|
||||||
|
~RainbowMap() override;
|
||||||
|
void onSetup() override;
|
||||||
|
void onTick(float delta) override;
|
||||||
|
void calcNextDelta(double ratio);
|
||||||
|
const char *name() override;
|
||||||
|
std::string m_name = "Rainbow Map";
|
||||||
|
led_map *data = Vulcan121::createEmptyLEDMap();
|
||||||
|
bool firstUnder = true;
|
||||||
|
double deltaMove(double val);
|
||||||
|
void moveRainbow(double d);
|
||||||
|
void updateColumn(int column, int colIndex);
|
||||||
|
};
|
||||||
|
}
|
26
headers/VulcanoLE/Scripts/Random.h
Normal file
26
headers/VulcanoLE/Scripts/Random.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <VulcanoLE/Audio/AudioGrabber.h>
|
||||||
|
#include <VulcanoLE/Keyboards/Vulcan121.h>
|
||||||
|
#include <VulcanoLE/Visual/VIZ.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace VIZ {
|
||||||
|
class Random : public VIZ {
|
||||||
|
protected:
|
||||||
|
led_map *map = nullptr;
|
||||||
|
bool isReverse{ false };
|
||||||
|
bool emptyTicks{ false };
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
|
@ -13,9 +13,9 @@ namespace VIZ {
|
||||||
const char *name() override;
|
const char *name() override;
|
||||||
std::string m_name = "Spectrum One";
|
std::string m_name = "Spectrum One";
|
||||||
rgba colors[3] = {
|
rgba colors[3] = {
|
||||||
{ 0, 30, 150, 0 },
|
{ 0, 10, 180, 0 },
|
||||||
{ 150, 150, 0, 0 },
|
{ 0, 180, 0, 0 },
|
||||||
{ 150, 0, 40, 0 }
|
{ 150, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <VulcanoLE/Scripts/Spectrum.h>
|
#include <VulcanoLE/Scripts/Spectrum.h>
|
||||||
|
|
||||||
#define VIZSIZE 4
|
#define VIZSIZE 6
|
||||||
|
|
||||||
using micro = std::chrono::duration<double, std::micro>;
|
using micro = std::chrono::duration<double, std::micro>;
|
||||||
|
using ms = std::chrono::duration<double, std::milli>;
|
||||||
|
|
||||||
namespace VIZ {
|
namespace VIZ {
|
||||||
struct VisPlugins {
|
struct VisPlugins {
|
||||||
|
@ -18,11 +19,14 @@ namespace VIZ {
|
||||||
~VisPlugins();
|
~VisPlugins();
|
||||||
VUtils::Environment *env{};
|
VUtils::Environment *env{};
|
||||||
protected:
|
protected:
|
||||||
|
std::mutex guard;
|
||||||
VIZ *viz[VIZSIZE]{};
|
VIZ *viz[VIZSIZE]{};
|
||||||
VIZ *currentVis{};
|
VIZ *currentVis{};
|
||||||
Vulcan121 *keyboard{};
|
Vulcan121 *keyboard{};
|
||||||
AudioGrabber *grabber{};
|
AudioGrabber *grabber{};
|
||||||
std::chrono::time_point<std::chrono::system_clock, micro> start;
|
std::chrono::time_point<std::chrono::system_clock, micro> start;
|
||||||
|
std::chrono::time_point<std::chrono::system_clock, ms> frameStart;
|
||||||
|
long frames = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26674
headers/sol/sol.hpp
Normal file
26674
headers/sol/sol.hpp
Normal file
File diff suppressed because it is too large
Load diff
2
main.cpp
2
main.cpp
|
@ -5,6 +5,7 @@
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <VulcanoLE/Audio/VisAudioRunner.h>
|
#include <VulcanoLE/Audio/VisAudioRunner.h>
|
||||||
#include <VUtils/Environment.h>
|
#include <VUtils/Environment.h>
|
||||||
|
#include <VulcanoLE/Audio/JackClient.h>
|
||||||
|
|
||||||
bool shouldRun = true;
|
bool shouldRun = true;
|
||||||
void my_handler(int s) {
|
void my_handler(int s) {
|
||||||
|
@ -22,6 +23,7 @@ int main(int argc, char **argv) {
|
||||||
// Load Config...
|
// Load Config...
|
||||||
VUtils::Environment config{VUtils::FileHandler::getFromHomeDir("/.config/VulcanoLE/state.env").c_str()};
|
VUtils::Environment config{VUtils::FileHandler::getFromHomeDir("/.config/VulcanoLE/state.env").c_str()};
|
||||||
config.loadFile();
|
config.loadFile();
|
||||||
|
JackClient::get().env = &config;
|
||||||
LOG(R"(
|
LOG(R"(
|
||||||
|
|
||||||
[===============================================]
|
[===============================================]
|
||||||
|
|
|
@ -88,4 +88,15 @@ namespace VUtils {
|
||||||
std::string FileHandler::getFromHomeDir(const std::basic_string<char> &path) {
|
std::string FileHandler::getFromHomeDir(const std::basic_string<char> &path) {
|
||||||
return std::string(getHomeDirectory() + path);
|
return std::string(getHomeDirectory() + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> FileHandler::readDir(const std::basic_string<char> &path) {
|
||||||
|
std::vector<std::string> res;
|
||||||
|
if (!isDirectory(path)) {
|
||||||
|
ERR("Path: \"%s\" is not a directory", path.c_str());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
for (const auto &entry : std::filesystem::directory_iterator(path))
|
||||||
|
res.push_back(entry.path());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -14,4 +14,7 @@ namespace VUtils {
|
||||||
double Math::easeIn(double ratio) {
|
double Math::easeIn(double ratio) {
|
||||||
return ratio * ratio * ratio;
|
return ratio * ratio * ratio;
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
30
src/VulcanoLE/API/I2C.cpp
Normal file
30
src/VulcanoLE/API/I2C.cpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#include <VulcanoLE/API/I2C.h>
|
||||||
|
#include <VUtils/FileHandler.h>
|
||||||
|
#include <VUtils/Logging.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <linux/i2c-dev.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
// @Todo Implement Reading and creating all busses!
|
||||||
|
int I2C::readDevices() {
|
||||||
|
std::vector<std::string> files = VUtils::FileHandler::readDir("/sys/bus/i2c/devices/");
|
||||||
|
if (files.empty()) {
|
||||||
|
WARN("No I2C Devices found");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
for (auto &file : files) {
|
||||||
|
if (file.find("i2c-") != std::string::npos) {
|
||||||
|
auto device = file + "/name";
|
||||||
|
auto dev = open(device.c_str(), O_RDONLY);
|
||||||
|
if(dev) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ERR("Cannot open device %s", device.c_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -1,131 +1,25 @@
|
||||||
#include <cstring>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <VulcanoLE/Audio/AudioGrabber.h>
|
#include <VulcanoLE/Audio/AudioGrabber.h>
|
||||||
#include <VUtils/Logging.h>
|
#include <VUtils/Logging.h>
|
||||||
|
#include <VulcanoLE/Audio/JackClient.h>
|
||||||
|
|
||||||
|
|
||||||
AudioGrabber::AudioGrabber() = default;
|
AudioGrabber::AudioGrabber() = default;
|
||||||
AudioGrabber::~AudioGrabber() = default;
|
AudioGrabber::~AudioGrabber() = default;
|
||||||
|
|
||||||
bool AudioGrabber::read(stereoSample *buffer, uint32_t bufferSize) {
|
bool AudioGrabber::read(stereoSample *buffer, uint32_t bufferSize) {
|
||||||
auto buffer_size_bytes = static_cast<size_t>(sizeof(stereoSample) * bufferSize);
|
auto &client = JackClient::get();
|
||||||
if (m_pulseaudioSimple == nullptr) {
|
if (!client.isData())
|
||||||
openPulseaudioSource(static_cast<uint32_t>(buffer_size_bytes));
|
return false;
|
||||||
}
|
availableData = client.getFrames();
|
||||||
|
client.fillSamples(buffer, availableData);
|
||||||
if (m_pulseaudioSimple != nullptr) {
|
return true;
|
||||||
memset(buffer, 0, buffer_size_bytes);
|
|
||||||
int32_t error_code;
|
|
||||||
auto return_code = pa_simple_read(m_pulseaudioSimple, buffer,
|
|
||||||
buffer_size_bytes, &error_code);
|
|
||||||
if (return_code < 0) {
|
|
||||||
WARN("Could not finish reading pulse Audio stream buffer\n bytes read: %d buffer\n size: %d", return_code,
|
|
||||||
buffer_size_bytes)
|
|
||||||
// zero out buffer
|
|
||||||
memset(buffer, 0, buffer_size_bytes);
|
|
||||||
pa_simple_free(m_pulseaudioSimple);
|
|
||||||
m_pulseaudioSimple = nullptr;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Success fully read entire buffer
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void AudioGrabber::populateDefaultSourceName() {
|
|
||||||
pa_mainloop_api *mainloop_api;
|
|
||||||
pa_context *pulseaudio_context;
|
|
||||||
m_pulseaudioMainloop = pa_mainloop_new();
|
|
||||||
mainloop_api = pa_mainloop_get_api(m_pulseaudioMainloop);
|
|
||||||
pulseaudio_context = pa_context_new(mainloop_api, "VulcanoLE device list");
|
|
||||||
pa_context_connect(pulseaudio_context, nullptr, PA_CONTEXT_NOFLAGS,
|
|
||||||
nullptr);
|
|
||||||
pa_context_set_state_callback(pulseaudio_context,
|
|
||||||
pulseaudioContextStateCallback,
|
|
||||||
reinterpret_cast<void *>(this));
|
|
||||||
|
|
||||||
int ret;
|
|
||||||
if (pa_mainloop_run(m_pulseaudioMainloop, &ret) < 0) {
|
|
||||||
ERR("Could not open pulseaudio mainloop to find default device name: %d", ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AudioGrabber::openPulseaudioSource(uint32_t maxBufferSize) {
|
|
||||||
int32_t error_code = 0;
|
|
||||||
static const pa_sample_spec sample_spec = { PA_SAMPLE_FLOAT32NE, sampleRate,
|
|
||||||
channels };
|
|
||||||
static const pa_buffer_attr buffer_attr = { maxBufferSize, 0, 0, 0,
|
|
||||||
(maxBufferSize / 2) };
|
|
||||||
populateDefaultSourceName();
|
|
||||||
if (!m_PulseaudioDefaultSourceName.empty()) {
|
|
||||||
m_pulseaudioSimple =
|
|
||||||
pa_simple_new(nullptr, recStreamName, PA_STREAM_RECORD,
|
|
||||||
m_PulseaudioDefaultSourceName.c_str(),
|
|
||||||
recStreamDescription, &sample_spec,
|
|
||||||
nullptr, &buffer_attr, &error_code);
|
|
||||||
}
|
|
||||||
if (m_pulseaudioSimple == nullptr) {
|
|
||||||
m_pulseaudioSimple =
|
|
||||||
pa_simple_new(nullptr, recStreamName, PA_STREAM_RECORD,
|
|
||||||
nullptr, recStreamDescription,
|
|
||||||
&sample_spec, nullptr, &buffer_attr, &error_code);
|
|
||||||
}
|
|
||||||
if (m_pulseaudioSimple == nullptr) {
|
|
||||||
m_pulseaudioSimple =
|
|
||||||
pa_simple_new(nullptr, recStreamName, PA_STREAM_RECORD,
|
|
||||||
"0", recStreamDescription, &sample_spec,
|
|
||||||
nullptr, &buffer_attr, &error_code);
|
|
||||||
}
|
|
||||||
if (m_pulseaudioSimple != nullptr) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ERR("Could not open pulseaudio source: %s", pa_strerror(error_code))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioGrabber::pulseaudioContextStateCallback(pa_context *c, void *userdata) {
|
|
||||||
switch (pa_context_get_state(c)) {
|
|
||||||
case PA_CONTEXT_UNCONNECTED:
|
|
||||||
case PA_CONTEXT_CONNECTING:
|
|
||||||
case PA_CONTEXT_AUTHORIZING:
|
|
||||||
case PA_CONTEXT_SETTING_NAME:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PA_CONTEXT_READY: {
|
|
||||||
pa_operation_unref(pa_context_get_server_info(
|
|
||||||
c, pulseaudioServerInfoCallback, userdata));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case PA_CONTEXT_FAILED:
|
|
||||||
case PA_CONTEXT_TERMINATED:
|
|
||||||
auto *src =
|
|
||||||
reinterpret_cast<AudioGrabber *>(userdata);
|
|
||||||
pa_mainloop_quit(src->m_pulseaudioMainloop, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioGrabber::pulseaudioServerInfoCallback(pa_context *context, const pa_server_info *i, void *userdata) {
|
|
||||||
if (i != nullptr) {
|
|
||||||
auto *src = reinterpret_cast<AudioGrabber *>(userdata);
|
|
||||||
std::string name = i->default_sink_name;
|
|
||||||
name.append(defaultMonitorPostfix);
|
|
||||||
|
|
||||||
src->m_PulseaudioDefaultSourceName = name;
|
|
||||||
|
|
||||||
// stop mainloop after finding default name
|
|
||||||
pa_mainloop_quit(src->m_pulseaudioMainloop, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioGrabber *AudioGrabber::createAudioGrabber() {
|
AudioGrabber *AudioGrabber::createAudioGrabber() {
|
||||||
|
JackClient::get().start();
|
||||||
auto *grabber = new AudioGrabber();
|
auto *grabber = new AudioGrabber();
|
||||||
|
JackClient::get().grabber = grabber;
|
||||||
return grabber;
|
return grabber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +34,7 @@ void AudioGrabber::init() {
|
||||||
void AudioGrabber::calculateRMS(stereoSample *pFrame) {
|
void AudioGrabber::calculateRMS(stereoSample *pFrame) {
|
||||||
float squareL = 0, meanL;
|
float squareL = 0, meanL;
|
||||||
float squareR = 0, meanR;
|
float squareR = 0, meanR;
|
||||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
for (int i = 0; i < availableData; i++) {
|
||||||
squareL += std::pow(pFrame[0].l * m_scale, 2);
|
squareL += std::pow(pFrame[0].l * m_scale, 2);
|
||||||
squareR += std::pow(pFrame[0].r * m_scale, 2);
|
squareR += std::pow(pFrame[0].r * m_scale, 2);
|
||||||
}
|
}
|
||||||
|
@ -151,7 +45,7 @@ void AudioGrabber::calculateRMS(stereoSample *pFrame) {
|
||||||
|
|
||||||
void AudioGrabber::calculatePEAK(stereoSample *pFrame) {
|
void AudioGrabber::calculatePEAK(stereoSample *pFrame) {
|
||||||
stereoSampleFrame max = { 0, 0 };
|
stereoSampleFrame max = { 0, 0 };
|
||||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
for (int i = 0; i < availableData; i++) {
|
||||||
float left = std::abs(pFrame[0].l * m_scale);
|
float left = std::abs(pFrame[0].l * m_scale);
|
||||||
float right = std::abs(pFrame[0].r * m_scale);
|
float right = std::abs(pFrame[0].r * m_scale);
|
||||||
if (left > max.l)
|
if (left > max.l)
|
||||||
|
@ -188,7 +82,6 @@ bool AudioGrabber::work() {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
DBG("Wait for Data")
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,38 @@
|
||||||
#include <VulcanoLE/Audio/FFT.h>
|
#include <VulcanoLE/Audio/FFT.h>
|
||||||
#include <VUtils/Logging.h>
|
#include <VUtils/Logging.h>
|
||||||
|
#include <cmath>
|
||||||
#include <VUtils/Math.h>
|
#include <VUtils/Math.h>
|
||||||
|
|
||||||
|
#define LIMIT 100
|
||||||
|
|
||||||
|
double FFT::limit = LIMIT;
|
||||||
void FFT::process(stereoSample *frame, double scale) {
|
void FFT::process(stereoSample *frame, double scale) {
|
||||||
std::unique_lock<std::mutex> lck(m_mtx);
|
std::unique_lock<std::mutex> lck(m_mtx);
|
||||||
if (prepareInput(frame, FFT_SIZE, scale)) {
|
if (prepareInput(frame, BUFFER_SIZE, scale)) {
|
||||||
for (int i = 0; i < FFT_SIZE; ++i) {
|
for (int i = 0; i < BUFFER_SIZE; ++i) {
|
||||||
m_sample->leftChannel[i] = 0;
|
m_sample->leftChannel[i] = 0;
|
||||||
m_sample->rightChannel[i] = 0;
|
m_sample->rightChannel[i] = 0;
|
||||||
}
|
}
|
||||||
|
// reset limit :D
|
||||||
|
times++;
|
||||||
|
if (times > 100 && limit != LIMIT) {
|
||||||
|
DBG("RESET OVERSHOT TO 100")
|
||||||
|
limit = LIMIT;
|
||||||
|
times = 0;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_fftwPlanRight = fftw_plan_dft_r2c_1d(FFT_SIZE, m_fftwInputRight, m_fftwOutputRight, FFTW_ESTIMATE);
|
m_fftwPlanRight = fftw_plan_dft_1d(BUFFER_SIZE, m_fftwInputRight, m_fftwOutputRight, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
m_fftwPlanLeft = fftw_plan_dft_r2c_1d(FFT_SIZE, m_fftwInputLeft, m_fftwOutputLeft, FFTW_ESTIMATE);
|
m_fftwPlanLeft = fftw_plan_dft_1d(BUFFER_SIZE, m_fftwInputLeft, m_fftwOutputLeft, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
fftw_execute(m_fftwPlanLeft);
|
fftw_execute(m_fftwPlanLeft);
|
||||||
fftw_execute(m_fftwPlanRight);
|
fftw_execute(m_fftwPlanRight);
|
||||||
for (int i = 0; i < m_fftwResults; ++i) {
|
for (int i = 0; i < BUFFER_SIZE; ++i) {
|
||||||
double left = m_fftwOutputLeft[i][0];
|
double left = std::sqrt(
|
||||||
double right = m_fftwOutputRight[i][0];
|
m_fftwOutputLeft[i][0] * m_fftwOutputLeft[i][0] + m_fftwOutputLeft[i][1] * m_fftwOutputLeft[i][1]);
|
||||||
m_sample->leftChannel[i] = left;
|
double right = std::sqrt(
|
||||||
m_sample->rightChannel[i] = right;
|
m_fftwOutputRight[i][0] * m_fftwOutputRight[i][0] + m_fftwOutputRight[i][1] * m_fftwOutputRight[i][1]);
|
||||||
|
m_sample->leftChannel[i] = valueToAmp(left);
|
||||||
|
m_sample->rightChannel[i] = valueToAmp(right);
|
||||||
}
|
}
|
||||||
fftw_destroy_plan(m_fftwPlanRight);
|
fftw_destroy_plan(m_fftwPlanRight);
|
||||||
fftw_destroy_plan(m_fftwPlanLeft);
|
fftw_destroy_plan(m_fftwPlanLeft);
|
||||||
|
@ -34,21 +47,36 @@ outputSample *FFT::getData() {
|
||||||
bool FFT::prepareInput(stereoSample *buffer, uint32_t sampleSize, double scale) {
|
bool FFT::prepareInput(stereoSample *buffer, uint32_t sampleSize, double scale) {
|
||||||
bool is_silent = true;
|
bool is_silent = true;
|
||||||
for (size_t i = 0; i < sampleSize; ++i) {
|
for (size_t i = 0; i < sampleSize; ++i) {
|
||||||
m_fftwInputLeft[i] = VUtils::Math::clamp(buffer[i].r * scale, -1, 1);
|
m_fftwInputLeft[i][0] = VUtils::Math::clamp(buffer[i].r * scale, -1, 1) * hannWindow[i];
|
||||||
m_fftwInputRight[i] = VUtils::Math::clamp(buffer[i].r * scale, -1, 1);
|
m_fftwInputRight[i][0] = VUtils::Math::clamp(buffer[i].r * scale, -1, 1) * hannWindow[i];
|
||||||
if (is_silent && (m_fftwInputLeft[i] != 0 || m_fftwInputRight[i] != 0)) is_silent = false;
|
if (is_silent && (m_fftwInputLeft[i][0] != 0 || m_fftwInputRight[i][0] != 0)) is_silent = false;
|
||||||
}
|
}
|
||||||
return is_silent;
|
return is_silent;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFT::FFT() {
|
FFT::FFT() {
|
||||||
m_fftwResults = (static_cast<size_t>(BUFFER_SIZE) / 2.0) + 1;
|
m_fftwResults = (static_cast<size_t>(BUFFER_SIZE) / 2.0) + 1;
|
||||||
m_fftwInputLeft = static_cast<double *>(fftw_malloc(sizeof(double) * BUFFER_SIZE));
|
m_fftwInputLeft = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex) * BUFFER_SIZE));
|
||||||
m_fftwInputRight = static_cast<double *>(fftw_malloc(sizeof(double) * BUFFER_SIZE));
|
m_fftwInputRight = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex) * BUFFER_SIZE));
|
||||||
m_fftwOutputLeft = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex) * m_fftwResults));
|
m_fftwOutputLeft = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex) * BUFFER_SIZE));
|
||||||
m_fftwOutputRight = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex) * m_fftwResults));
|
m_fftwOutputRight = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex) * BUFFER_SIZE));
|
||||||
|
for (int i = 0; i < BUFFER_SIZE; ++i) {
|
||||||
|
hannWindow[i] = 0.54 - 0.46 * std::cos(2 * M_PI * i / BUFFER_SIZE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
FFT::~FFT() {
|
FFT::~FFT() {
|
||||||
delete m_sample;
|
delete m_sample;
|
||||||
|
fftw_free(m_fftwInputLeft);
|
||||||
|
fftw_free(m_fftwInputRight);
|
||||||
|
fftw_free(m_fftwOutputLeft);
|
||||||
|
fftw_free(m_fftwOutputRight);
|
||||||
|
}
|
||||||
|
double FFT::valueToAmp(double value) {
|
||||||
|
if (value > limit) {
|
||||||
|
double newLimit = std::floor(value) + 11;
|
||||||
|
DBG("OVERSHOT %.2f: %.4f -> %.4f", limit, value, newLimit)
|
||||||
|
limit = newLimit;
|
||||||
|
}
|
||||||
|
return VUtils::Math::clamp(value, 0, limit) / limit * 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
133
src/VulcanoLE/Audio/JackClient.cpp
Normal file
133
src/VulcanoLE/Audio/JackClient.cpp
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
#include <VulcanoLE/Audio/JackClient.h>
|
||||||
|
#include <VUtils/Logging.h>
|
||||||
|
|
||||||
|
int JackClient::processSamples(jack_nframes_t frames, void *arg) {
|
||||||
|
JackClient &client = JackClient::get();
|
||||||
|
jack_default_audio_sample_t *inl;
|
||||||
|
jack_default_audio_sample_t *inr;
|
||||||
|
unsigned int i;
|
||||||
|
if (client.input_port_l == nullptr || client.input_port_r == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
inl = (jack_default_audio_sample_t *) jack_port_get_buffer(client.input_port_l, frames);
|
||||||
|
inr = (jack_default_audio_sample_t *) jack_port_get_buffer(client.input_port_r, frames);
|
||||||
|
client.addFrames(inl, inr, frames);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
void JackClient::onExit() {
|
||||||
|
DBG("SHUTDOWN JACK!")
|
||||||
|
JackClient &jackClient = JackClient::get();
|
||||||
|
const char **all_ports;
|
||||||
|
unsigned int i;
|
||||||
|
if (jackClient.input_port_l != nullptr) {
|
||||||
|
all_ports = jack_port_get_all_connections(jackClient.client, jackClient.input_port_l);
|
||||||
|
for (i = 0; all_ports && all_ports[i]; i++) {
|
||||||
|
jack_disconnect(jackClient.client, all_ports[i], jack_port_name(jackClient.input_port_l));
|
||||||
|
}
|
||||||
|
all_ports = jack_port_get_all_connections(jackClient.client, jackClient.input_port_r);
|
||||||
|
for (i = 0; all_ports && all_ports[i]; i++) {
|
||||||
|
jack_disconnect(jackClient.client, all_ports[i], jack_port_name(jackClient.input_port_r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jack_client_close(jackClient.client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JackClient::connect(const char *port_name, jack_port_t *channel) {
|
||||||
|
jack_port_t *port;
|
||||||
|
port = jack_port_by_name(client, port_name);
|
||||||
|
if (port == nullptr) {
|
||||||
|
ERR("Cannot Find Port: %s", port_name)
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
LOG("Connecting %s to %s", jack_port_name(port), jack_port_name(channel))
|
||||||
|
if (jack_connect(client, jack_port_name(port), jack_port_name(channel))) {
|
||||||
|
ERR("Cannot connect port '%s' to '%s'", jack_port_name(port), jack_port_name(channel))
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JackClient::start() {
|
||||||
|
jack_status_t status;
|
||||||
|
client = jack_client_open("Keyboard Visual", options, &status);
|
||||||
|
if (client == nullptr) {
|
||||||
|
ERR("Failed to start JACK client: %d", status)
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
DBG("Register as %s", jack_get_client_name(client))
|
||||||
|
input_port_l = jack_port_register(client, "left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, BUFFER_SIZE);
|
||||||
|
input_port_r = jack_port_register(client, "right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, BUFFER_SIZE);
|
||||||
|
if (!input_port_l || !input_port_r) {
|
||||||
|
ERR("Cannot Register input port left")
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
atexit(JackClient::onExit);
|
||||||
|
jack_set_process_callback(client, JackClient::processSamples, nullptr);
|
||||||
|
if (jack_activate(client)) {
|
||||||
|
ERR("Cannot Activate Client")
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
auto inputL = env->getEnv("input_l", "");
|
||||||
|
auto inputR = env->getEnv("input_r", "");
|
||||||
|
DBG("Try Using InputL: %s", inputL.c_str())
|
||||||
|
DBG("Try Using InputR: %s", inputR.c_str())
|
||||||
|
if (inputL.empty() || inputR.empty() || !checkPort(inputL) || !checkPort(inputR)) {
|
||||||
|
inputL = getPort(true);
|
||||||
|
inputR = getPort(false);
|
||||||
|
DBG("Picking new InputL: %s", inputL.c_str())
|
||||||
|
DBG("Picking new InputR: %s", inputR.c_str())
|
||||||
|
env->set("input_l", inputL.c_str());
|
||||||
|
env->set("input_r", inputR.c_str());
|
||||||
|
}
|
||||||
|
if (inputL.empty() || inputR.empty()) return;
|
||||||
|
connect(inputL.c_str(), input_port_l);
|
||||||
|
connect(inputR.c_str(), input_port_r);
|
||||||
|
}
|
||||||
|
bool JackClient::isData() {
|
||||||
|
std::lock_guard<std::mutex> lockGuard(guard);
|
||||||
|
return m_newDataAvailable;
|
||||||
|
}
|
||||||
|
std::string JackClient::getPort(bool isLeft) {
|
||||||
|
std::string type = isLeft ? ":monitor_FL" : ":monitor_FR";
|
||||||
|
auto port = jack_get_ports(client, type.c_str(), nullptr,
|
||||||
|
JackPortIsOutput);
|
||||||
|
if (port == nullptr)
|
||||||
|
return "";
|
||||||
|
return std::string(port[0]);
|
||||||
|
}
|
||||||
|
bool JackClient::checkPort(const std::string &port) {
|
||||||
|
return jack_get_ports(client, port.c_str(), nullptr,
|
||||||
|
JackPortIsOutput) != nullptr;
|
||||||
|
}
|
||||||
|
void JackClient::addFrames(jack_default_audio_sample_t *l, jack_default_audio_sample_t *r, jack_nframes_t i) {
|
||||||
|
std::lock_guard<std::mutex> lockGuard(guard);
|
||||||
|
if (!grabber)
|
||||||
|
return;
|
||||||
|
if (grabber->requestMode == AudioGrabber::ReqMode::FFT || grabber->requestMode == AudioGrabber::ReqMode::ALL) {
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
if (samplesAdded >= BUFFER_SIZE) {
|
||||||
|
for (int k = 0; k < BUFFER_SIZE; ++k) {
|
||||||
|
samples[k].l = ringBuffer[k].l;
|
||||||
|
samples[k].r = ringBuffer[k].r;
|
||||||
|
}
|
||||||
|
m_newDataAvailable = true;
|
||||||
|
samplesAdded = 0;
|
||||||
|
}
|
||||||
|
ringBuffer[samplesAdded].l = l[j];
|
||||||
|
ringBuffer[samplesAdded].r = r[j];
|
||||||
|
samplesAdded++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int k = 0; k < i; ++k) {
|
||||||
|
samples[k].l = l[k];
|
||||||
|
samples[k].r = r[k];
|
||||||
|
}
|
||||||
|
m_newDataAvailable = true;
|
||||||
|
samplesAdded = (int) i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int JackClient::getFrames() {
|
||||||
|
std::lock_guard<std::mutex> lockGuard(guard);
|
||||||
|
return grabber->requestMode == AudioGrabber::ReqMode::FFT || grabber->requestMode == AudioGrabber::ReqMode::ALL
|
||||||
|
? BUFFER_SIZE : samplesAdded;
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
#include <VulcanoLE/Audio/VisAudioRunner.h>
|
#include <VulcanoLE/Audio/VisAudioRunner.h>
|
||||||
#include <VUtils/Logging.h>
|
#include <VUtils/Logging.h>
|
||||||
|
|
||||||
VisAudioRunner::VisAudioRunner(AudioGrabber *ag, VIZ::VisPlugins *vp) : grabber(ag), plugins(vp) {}
|
VisAudioRunner::VisAudioRunner(AudioGrabber *ag, VIZ::VisPlugins *vp) : grabber(ag), plugins(vp) {
|
||||||
|
i2c.readDevices();
|
||||||
|
}
|
||||||
|
|
||||||
VisAudioRunner::~VisAudioRunner() {
|
VisAudioRunner::~VisAudioRunner() {
|
||||||
delete plugins;
|
delete plugins;
|
||||||
|
@ -28,7 +30,7 @@ void VisAudioRunner::run() const {
|
||||||
if (grabber->work()) {
|
if (grabber->work()) {
|
||||||
plugins->onTick();
|
plugins->onTick();
|
||||||
} else {
|
} else {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100ul));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
usleep(50000);
|
usleep(50000);
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#include <execution>
|
#include <execution>
|
||||||
#include <VulcanoLE/Keyboards/Vulcan121.h>
|
#include <VulcanoLE/Keyboards/Vulcan121.h>
|
||||||
#include <VUtils/Logging.h>
|
#include <VUtils/Logging.h>
|
||||||
|
#include <VUtils/Math.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
Vulcan121::Vulcan121(HIDHelper *helper)
|
Vulcan121::Vulcan121(HIDHelper *helper)
|
||||||
: m_helper(helper) {
|
: m_helper(helper) {
|
||||||
|
@ -26,58 +28,46 @@ 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, bool deleteMap) {
|
||||||
int i, k;
|
int i, k;
|
||||||
rgba rgb;
|
rgba rgb;
|
||||||
unsigned char hwmap[444]{};
|
unsigned char ledBuffer[448]{};
|
||||||
unsigned char workbuf[65];
|
memset(ledBuffer, 0, sizeof(ledBuffer));
|
||||||
memset(hwmap, 0, sizeof(hwmap));
|
ledBuffer[0] = 0xa1;
|
||||||
|
ledBuffer[1] = 0x01;
|
||||||
|
ledBuffer[2] = 0x01;
|
||||||
|
ledBuffer[3] = 0xb4;
|
||||||
for (k = 0; k < NUM_KEYS; k++) {
|
for (k = 0; k < NUM_KEYS; k++) {
|
||||||
rgb = m_fixed[k] ? *(m_fixed[k]) : (src ? src->key[k] : m_colorOff);
|
// Prepare Color
|
||||||
|
auto use = m_fixed[k] ? *(m_fixed[k]) : src ? src->key[k] : m_colorOff;
|
||||||
rgb.r = (rgb.r > 255) ? 255 : (rgb.r < 0) ? 0 : rgb.r;
|
rgb.a = (int16_t) VUtils::Math::clamp(use.a, 0, 255);
|
||||||
rgb.g = (rgb.g > 255) ? 255 : (rgb.g < 0) ? 0 : rgb.g;
|
|
||||||
rgb.b = (rgb.b > 255) ? 255 : (rgb.b < 0) ? 0 : rgb.b;
|
|
||||||
rgb.a = (rgb.a > 255) ? 255 : (rgb.a < 0) ? 0 : rgb.a;
|
|
||||||
double factor = rgb.a / 255.0;
|
double factor = rgb.a / 255.0;
|
||||||
rgb.r *= factor;
|
rgb.r = (int16_t) VUtils::Math::clamp(std::round(use.r * factor), 0, 255);
|
||||||
rgb.g *= factor;
|
rgb.g = (int16_t) VUtils::Math::clamp(std::round(use.g * factor), 0, 255);
|
||||||
rgb.b *= factor;
|
rgb.b = (int16_t) VUtils::Math::clamp(std::round(use.b * factor), 0, 255);
|
||||||
|
|
||||||
int offset = ((k / 12) * 36) + (k % 12);
|
int offset = ((k / 12) * 36) + (k % 12);
|
||||||
hwmap[offset + 0] = (unsigned char) rgb.r;
|
ledBuffer[offset + 4] = (unsigned char) rgb.r;
|
||||||
hwmap[offset + 12] = (unsigned char) rgb.g;
|
ledBuffer[offset + 16] = (unsigned char) rgb.g;
|
||||||
hwmap[offset + 24] = (unsigned char) rgb.b;
|
ledBuffer[offset + 28] = (unsigned char) rgb.b;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First chunk comes with header
|
unsigned char buffer[65];
|
||||||
workbuf[0] = 0x00;
|
buffer[0] = 0x00;
|
||||||
workbuf[1] = 0xa1;
|
for (i = 0; i < 7; i++) {
|
||||||
workbuf[2] = 0x01;
|
int index = i * 64;
|
||||||
workbuf[3] = 0x01;
|
memcpy(&buffer[1], &ledBuffer[index], 64);
|
||||||
workbuf[4] = 0xb4;
|
if (hid_write(m_helper->m_ledDevice, buffer, 65) != 65) {
|
||||||
memcpy(&workbuf[5], hwmap, 60);
|
ERR("Write failed")
|
||||||
if (hid_write(m_helper->m_ledDevice, workbuf, 65) != 65) {
|
if (deleteMap)
|
||||||
if (deleteMap) {
|
|
||||||
delete src;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Six more chunks
|
|
||||||
for (i = 1; i < 7; i++) {
|
|
||||||
workbuf[0] = 0x00;
|
|
||||||
memcpy(&workbuf[1], &hwmap[(i * 64) - 4], 64);
|
|
||||||
if (hid_write(m_helper->m_ledDevice, workbuf, 65) != 65) {
|
|
||||||
if (deleteMap) {
|
|
||||||
delete src;
|
delete src;
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (deleteMap) {
|
if (deleteMap)
|
||||||
delete src;
|
delete src;
|
||||||
}
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int Vulcan121::sendToLEDs(rgba rgb) {
|
int Vulcan121::sendToLEDs(rgba rgb) {
|
||||||
|
@ -264,6 +254,7 @@ int Vulcan121::getIndex(int row, int col) {
|
||||||
/**
|
/**
|
||||||
* Setup Mapping
|
* Setup Mapping
|
||||||
* They are not magic! look in the layout.md
|
* They are not magic! look in the layout.md
|
||||||
|
* @Fixme: Mapping is broken and not 100% correct for columns!
|
||||||
*/
|
*/
|
||||||
void Vulcan121::setupMap() {
|
void Vulcan121::setupMap() {
|
||||||
// Row Init
|
// Row Init
|
||||||
|
@ -341,3 +332,20 @@ void Vulcan121::fadeOutMap(led_map *map, double factor = 0.3) {
|
||||||
i.a *= factor;
|
i.a *= factor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Column FadeOut! Check if previous Col is brighter!
|
||||||
|
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) {
|
||||||
|
auto columnB = getColumn(i - 1);
|
||||||
|
colorB = map->key[columnB->index[0]].a;
|
||||||
|
}
|
||||||
|
for (int j = 0; j < columnA->count; ++j) {
|
||||||
|
auto &colorA = map->key[columnA->index[j]];
|
||||||
|
colorA.a = colorB != 0 && colorB > colorA.a ? colorB * 1.3 : colorA.a * factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace VIZ {
|
||||||
auto map = Vulcan121::createEmptyLEDMap();
|
auto map = Vulcan121::createEmptyLEDMap();
|
||||||
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 < 4; ++i) {
|
for (int i = 1; i < 3; ++i) {
|
||||||
if (data[i] > val) {
|
if (data[i] > val) {
|
||||||
val = data[i];
|
val = data[i];
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,15 @@
|
||||||
#include <VUtils/Logging.h>
|
#include <VUtils/Logging.h>
|
||||||
#include <VUtils/Math.h>
|
#include <VUtils/Math.h>
|
||||||
|
|
||||||
#define MAX_DELTA 1212000
|
#define MAX_DELTA 808000
|
||||||
#define MAX_PEAK 170
|
#define MAX_PEAK 170
|
||||||
|
|
||||||
namespace VIZ {
|
namespace VIZ {
|
||||||
RainbowLine::RainbowLine(AudioGrabber *pGrabber, Vulcan121 *vulcan) : VIZ(pGrabber, vulcan) {
|
RainbowLine::RainbowLine(AudioGrabber *pGrabber, Vulcan121 *vulcan) : VIZ(pGrabber, vulcan) {
|
||||||
colours = new rgba[keyboardData.num_cols];
|
maxCols = keyboardData.num_cols * 5;
|
||||||
for (int i = 0; i < keyboardData.num_cols; ++i) {
|
colours = new rgba[maxCols];
|
||||||
double ratio = (double) i / keyboardData.num_cols;
|
for (int i = 0; i < maxCols; ++i) {
|
||||||
|
double ratio = (double) i / maxCols;
|
||||||
colours[i] = Color::Generator::rgbFromRatio(ratio, 255);
|
colours[i] = Color::Generator::rgbFromRatio(ratio, 255);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,12 +29,11 @@ namespace VIZ {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RainbowLine::onTick(float delta) {
|
void RainbowLine::onTick(float delta) {
|
||||||
Vulcan121::fadeOutMap(data, tailFactor);
|
|
||||||
deltaElapsed += delta;
|
deltaElapsed += delta;
|
||||||
auto fftData = grabber->fft.getData()->leftChannel;
|
auto fftData = grabber->fft.getData()->leftChannel;
|
||||||
auto val = 0.0;
|
auto val = 0.0;
|
||||||
for (int i = 1; i < 4; ++i) {
|
for (int i = 1; i < 4; ++i) {
|
||||||
auto avg = fftData[i] * ratios[i];
|
auto avg = fftData[i];
|
||||||
if (avg > val)
|
if (avg > val)
|
||||||
val = avg;
|
val = avg;
|
||||||
}
|
}
|
||||||
|
@ -46,19 +46,14 @@ namespace VIZ {
|
||||||
currentColumn = 0;
|
currentColumn = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
colours[currentColumn].a = val;
|
moveLine(val, tailFactor);
|
||||||
auto column = keyboard->getColumn(currentColumn);
|
|
||||||
for (int i = 0; i < column->count; ++i) {
|
|
||||||
auto index = column->index[i];
|
|
||||||
data[0].key[index] = colours[currentColumn];
|
|
||||||
}
|
|
||||||
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, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
double RainbowLine::deltaMove(double val) {
|
double RainbowLine::deltaMove(double val) {
|
||||||
auto avg = (lastValue + val) * 0.5;
|
auto avg = VUtils::Math::clamp((lastValue + val) * 0.5, 0, 200);
|
||||||
if (avg > MAX_PEAK || avg > decayValue) {
|
if (avg > MAX_PEAK || avg > decayValue) {
|
||||||
calcNextDelta(val < MAX_PEAK && firstUnder ? 160 : val);
|
calcNextDelta(val < MAX_PEAK && firstUnder ? 160 : val);
|
||||||
firstUnder = val > MAX_PEAK;
|
firstUnder = val > MAX_PEAK;
|
||||||
|
@ -85,4 +80,15 @@ namespace VIZ {
|
||||||
rRatio = (VUtils::Math::easeIn(rRatio) - 1.0) * -1;
|
rRatio = (VUtils::Math::easeIn(rRatio) - 1.0) * -1;
|
||||||
deltaNeeded = rRatio * MAX_DELTA;
|
deltaNeeded = rRatio * MAX_DELTA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RainbowLine::moveLine(double val, double d) {
|
||||||
|
keyboard->fadeOutMapColumn(data, d);
|
||||||
|
colours[currentColumn].a = val;
|
||||||
|
int col = currentColumn % keyboardData.num_cols;
|
||||||
|
auto column = keyboard->getColumn(col);
|
||||||
|
for (int i = 0; i < column->count; ++i) {
|
||||||
|
auto index = column->index[i];
|
||||||
|
data[0].key[index] = colours[currentColumn];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
107
src/VulcanoLE/Scripts/RainbowMap.cpp
Normal file
107
src/VulcanoLE/Scripts/RainbowMap.cpp
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
#include <VulcanoLE/Scripts/RainbowMap.h>
|
||||||
|
#include <VulcanoLE/Colors/ColorHelper.h>
|
||||||
|
#include <VUtils/Logging.h>
|
||||||
|
#include <VUtils/Math.h>
|
||||||
|
|
||||||
|
#define MAX_DELTA 808000
|
||||||
|
#define MAX_PEAK 170
|
||||||
|
|
||||||
|
namespace VIZ {
|
||||||
|
RainbowMap::RainbowMap(AudioGrabber *pGrabber, Vulcan121 *vulcan) : VIZ(pGrabber, vulcan) {
|
||||||
|
maxCols = keyboardData.num_cols * keyboardData.num_rows;
|
||||||
|
colours = new rgba[maxCols];
|
||||||
|
for (int i = 0; i < maxCols; ++i) {
|
||||||
|
double ratio = (double) i / maxCols;
|
||||||
|
colours[i] = Color::Generator::rgbFromRatio(ratio, 128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RainbowMap::~RainbowMap() {
|
||||||
|
delete[] colours;
|
||||||
|
delete data;
|
||||||
|
}
|
||||||
|
void RainbowMap::onSetup() {
|
||||||
|
currentColumn = 0;
|
||||||
|
Vulcan121::fadeOutMap(data, 0);
|
||||||
|
keyboard->sendToLEDs({ 0, 0, 0, 0 });
|
||||||
|
grabber->requestMode = AudioGrabber::ReqMode::FFT;
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RainbowMap::onTick(float delta) {
|
||||||
|
deltaElapsed += delta;
|
||||||
|
auto fftData = grabber->fft.getData()->leftChannel;
|
||||||
|
auto val = 0.0;
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
auto avg = fftData[i];
|
||||||
|
if (avg > val)
|
||||||
|
val = avg;
|
||||||
|
}
|
||||||
|
val = VUtils::Math::clamp(val, 0, 255);
|
||||||
|
double avg = deltaMove(val);
|
||||||
|
if (deltaElapsed >= deltaNeeded) {
|
||||||
|
currentColumn++;
|
||||||
|
if (currentColumn >= maxCols)
|
||||||
|
currentColumn = 0;
|
||||||
|
moveRainbow(avg);
|
||||||
|
deltaElapsed -= deltaNeeded;
|
||||||
|
}
|
||||||
|
moveRainbow(avg);
|
||||||
|
lastValue = avg;
|
||||||
|
// we clean up the map self later! :)
|
||||||
|
keyboard->sendLedMap(data, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
double RainbowMap::deltaMove(double val) {
|
||||||
|
auto avg = VUtils::Math::clamp((lastValue + val) * 0.5, 0, 200);
|
||||||
|
if (avg > MAX_PEAK || avg > decayValue) {
|
||||||
|
calcNextDelta(val < MAX_PEAK && firstUnder ? 160 : val);
|
||||||
|
firstUnder = val > MAX_PEAK;
|
||||||
|
decayValue = val;
|
||||||
|
}
|
||||||
|
if (val > MAX_PEAK) {
|
||||||
|
firstUnder = true;
|
||||||
|
}
|
||||||
|
if (avg < 10) {
|
||||||
|
deltaNeeded = 1000000000;
|
||||||
|
}
|
||||||
|
if (decayValue >= 0)
|
||||||
|
decayValue -= 10;
|
||||||
|
return avg;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *RainbowMap::name() {
|
||||||
|
return m_name.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RainbowMap::calcNextDelta(double ratio) {
|
||||||
|
double rRatio = ratio / MAX_PEAK;
|
||||||
|
rRatio = VUtils::Math::clamp(rRatio, 0.05, 1.0);
|
||||||
|
rRatio = (VUtils::Math::easeIn(rRatio) - 1.0) * -1;
|
||||||
|
deltaNeeded = rRatio * MAX_DELTA;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RainbowMap::moveRainbow(double d) {
|
||||||
|
int i = 0;
|
||||||
|
int color = currentColumn;
|
||||||
|
while (i < keyboardData.num_cols) {
|
||||||
|
colours[color].a = (uint8_t) d;
|
||||||
|
updateColumn(i, color);
|
||||||
|
color++;
|
||||||
|
i++;
|
||||||
|
if (color > maxCols - 1)
|
||||||
|
color = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RainbowMap::updateColumn(int col, int colIndex) {
|
||||||
|
auto column = keyboard->getColumn(col);
|
||||||
|
if (!column) {
|
||||||
|
ERR("Column: %d not found", col);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < column->count; ++i) {
|
||||||
|
auto index = column->index[i];
|
||||||
|
data[0].key[index] = colours[colIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
83
src/VulcanoLE/Scripts/Random.cpp
Normal file
83
src/VulcanoLE/Scripts/Random.cpp
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
#include <VulcanoLE/Scripts/Random.h>
|
||||||
|
#include <VUtils/Logging.h>
|
||||||
|
#include <VUtils/Math.h>
|
||||||
|
|
||||||
|
namespace VIZ {
|
||||||
|
Random::Random(AudioGrabber *pGrabber, Vulcan121 *vulcan) : VIZ(pGrabber, vulcan) {
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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() {
|
||||||
|
return "Random";
|
||||||
|
}
|
||||||
|
|
||||||
|
Random::~Random() {
|
||||||
|
delete map;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,8 +14,8 @@ namespace VIZ {
|
||||||
// find largest bass ~43hz (44100 / FFT_SIZE) range
|
// 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 = 1; i < 4; ++i) {
|
||||||
if (data[ i ] > val) {
|
if (data[i] > val) {
|
||||||
val = data[ i ];
|
val = data[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
double newVal = (val + lastVal) / 2.0;
|
double newVal = (val + lastVal) / 2.0;
|
||||||
|
@ -27,7 +27,7 @@ namespace VIZ {
|
||||||
colors[1].a = newVal;
|
colors[1].a = newVal;
|
||||||
colors[2].a = newVal;
|
colors[2].a = newVal;
|
||||||
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[0].key[key] = colors[colorInd];
|
||||||
x++;
|
x++;
|
||||||
if (x > split) {
|
if (x > split) {
|
||||||
colorInd++;
|
colorInd++;
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include <VulcanoLE/Scripts/PoliceLike.h>
|
#include <VulcanoLE/Scripts/PoliceLike.h>
|
||||||
#include <VUtils/Logging.h>
|
#include <VUtils/Logging.h>
|
||||||
#include <VulcanoLE/Scripts/RainbowLine.h>
|
#include <VulcanoLE/Scripts/RainbowLine.h>
|
||||||
|
#include <VulcanoLE/Scripts/Random.h>
|
||||||
|
#include <VulcanoLE/Scripts/RainbowMap.h>
|
||||||
|
|
||||||
namespace VIZ {
|
namespace VIZ {
|
||||||
void VisPlugins::init(HIDHelper *hidHelper, AudioGrabber *audioGrabber) {
|
void VisPlugins::init(HIDHelper *hidHelper, AudioGrabber *audioGrabber) {
|
||||||
|
@ -13,10 +15,13 @@ namespace VIZ {
|
||||||
viz[1] = new Loudness(grabber, keyboard);
|
viz[1] = new Loudness(grabber, keyboard);
|
||||||
viz[2] = new PoliceLike(grabber, keyboard);
|
viz[2] = new PoliceLike(grabber, keyboard);
|
||||||
viz[3] = new RainbowLine(grabber, keyboard);
|
viz[3] = new RainbowLine(grabber, keyboard);
|
||||||
|
viz[4] = new Random(grabber, keyboard);
|
||||||
|
viz[5] = new RainbowMap(grabber, keyboard);
|
||||||
currentVis = viz[mode];
|
currentVis = viz[mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisPlugins::onStartup() {
|
void VisPlugins::onStartup() {
|
||||||
|
std::lock_guard<std::mutex> lockGuard(guard);
|
||||||
if (!keyboard->sendInitSequence()) {
|
if (!keyboard->sendInitSequence()) {
|
||||||
ERR("FAILED TO INIT KEYBOARD")
|
ERR("FAILED TO INIT KEYBOARD")
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -26,14 +31,25 @@ namespace VIZ {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisPlugins::onTick() {
|
void VisPlugins::onTick() {
|
||||||
|
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();
|
||||||
currentVis->onTick(delta);
|
currentVis->onTick(delta);
|
||||||
usleep(1000);
|
frames++;
|
||||||
|
#ifdef DEBUG
|
||||||
|
auto fps = std::chrono::duration_cast<ms>(stop - frameStart).count();
|
||||||
|
if (fps > 1000) {
|
||||||
|
frameStart = stop;
|
||||||
|
DBG("FPS: %.2f, time-needed: %d ms", double(frames) / (int) fps * 1000.0, (int) fps)
|
||||||
|
frames = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// DBG("Time Needed %f ms", delta / 1000)
|
||||||
start = stop;
|
start = stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisPlugins::onShutdown() {
|
void VisPlugins::onShutdown() {
|
||||||
|
std::lock_guard<std::mutex> lockGuard(guard);
|
||||||
int16_t r = env->getAsInt("shutdown_color_red", 0);
|
int16_t r = env->getAsInt("shutdown_color_red", 0);
|
||||||
int16_t g = env->getAsInt("shutdown_color_green", 0);
|
int16_t g = env->getAsInt("shutdown_color_green", 0);
|
||||||
int16_t b = env->getAsInt("shutdown_color_blue", 150);
|
int16_t b = env->getAsInt("shutdown_color_blue", 150);
|
||||||
|
@ -50,6 +66,7 @@ namespace VIZ {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisPlugins::setCurrentMode(int m) {
|
void VisPlugins::setCurrentMode(int m) {
|
||||||
|
std::lock_guard<std::mutex> lockGuard(guard);
|
||||||
if (m < 1 || m > VIZSIZE) {
|
if (m < 1 || m > VIZSIZE) {
|
||||||
ERR("Mode Setting Failed >> Mode is not in the available range 1 - %d", VIZSIZE)
|
ERR("Mode Setting Failed >> Mode is not in the available range 1 - %d", VIZSIZE)
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in a new issue