├── debian ├── copyright ├── compat ├── source │ └── format ├── rules ├── changelog └── control ├── .gitmodules ├── KeyboardVisualizerQT ├── KeyboardVisualizer.icns ├── KeyboardVisualizer.ico ├── KeyboardVisualizer.png ├── resources.qrc ├── KeyboardVisualizer.desktop ├── KeyboardVisDlg.h ├── main.cpp ├── keyboardvisualizer.ui └── KeyboardVisDlg.cpp ├── .github └── FUNDING.yml ├── KeyboardVisualizerCommon ├── chuck_fft.h ├── hsv.h ├── VisualizerDefines.h ├── hsv.cpp ├── Visualizer.h ├── chuck_fft.c └── Visualizer.cpp ├── scripts └── build-appimage.sh ├── KeyboardVisualizer.pro ├── README.md ├── .gitlab-ci.yml └── LICENSE /debian/copyright: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | export QT_SELECT := qt5 3 | 4 | %: 5 | dh $@ --parallel 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "OpenRGB"] 2 | path = OpenRGB 3 | url = https://gitlab.com/CalcProgrammer1/OpenRGB 4 | -------------------------------------------------------------------------------- /KeyboardVisualizerQT/KeyboardVisualizer.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CalcProgrammer1/KeyboardVisualizer/HEAD/KeyboardVisualizerQT/KeyboardVisualizer.icns -------------------------------------------------------------------------------- /KeyboardVisualizerQT/KeyboardVisualizer.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CalcProgrammer1/KeyboardVisualizer/HEAD/KeyboardVisualizerQT/KeyboardVisualizer.ico -------------------------------------------------------------------------------- /KeyboardVisualizerQT/KeyboardVisualizer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CalcProgrammer1/KeyboardVisualizer/HEAD/KeyboardVisualizerQT/KeyboardVisualizer.png -------------------------------------------------------------------------------- /KeyboardVisualizerQT/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | KeyboardVisualizer.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | keyboardvisualizer (4.01.0) UNRELEASED; urgency=medium 2 | 3 | * Builds from git master. See git history for more information. 4 | 5 | -- Adam Honse Sun, 12 Apr 2020 22:57:34 -0500 6 | -------------------------------------------------------------------------------- /KeyboardVisualizerQT/KeyboardVisualizer.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Encoding=UTF-8 4 | Name=Keyboard Visualizer 5 | Comment=Control RGB lighting 6 | Exec=KeyboardVisualizer 7 | Icon=KeyboardVisualizer 8 | Terminal=false 9 | Categories=Utility; 10 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: keyboardvisualizer 2 | Maintainer: Adam Honse 3 | Section: misc 4 | Priority: optional 5 | Standards-Version: 3.9.2 6 | Build-Depends: 7 | debhelper (>= 9), 8 | pkg-config, 9 | qtbase5-dev, 10 | qtbase5-dev-tools, 11 | qt5-qmake, 12 | libopenal-dev 13 | Homepage: https://gitlab.com/CalcProgrammer1/KeyboardVisualizer 14 | 15 | Package: keyboardvisualizer 16 | Architecture: any 17 | Depends: 18 | ${shlibs:Depends}, 19 | ${misc:Depends}, 20 | Recommends: 21 | openrgb 22 | Description: Audio visualizer for OpenRGB devices 23 | Keyboard Visualizer for OpenRGB devices -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: CalcProgrammer1 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /KeyboardVisualizerCommon/chuck_fft.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // name: chuck_fft.h 3 | // desc: fft impl - based on CARL distribution 4 | // 5 | // authors: code from San Diego CARL package 6 | // Ge Wang (gewang@cs.princeton.edu) 7 | // Perry R. Cook (prc@cs.princeton.edu) 8 | // date: 11.27.2003 9 | //----------------------------------------------------------------------------- 10 | #ifndef __CHUCK_FFT_H__ 11 | #define __CHUCK_FFT_H__ 12 | 13 | 14 | // complex type 15 | typedef struct { float re ; float im ; } complex; 16 | 17 | // complex absolute value 18 | #define cmp_abs(x) ( sqrt( (x).re * (x).re + (x).im * (x).im ) ) 19 | 20 | #define FFT_FORWARD 1 21 | #define FFT_INVERSE 0 22 | 23 | // c linkage 24 | #if ( defined( __cplusplus ) || defined( _cplusplus ) ) 25 | extern "C" { 26 | #endif 27 | 28 | // make the window 29 | void hanning( float * window, unsigned long length ); 30 | void hamming( float * window, unsigned long length ); 31 | void blackman( float * window, unsigned long length ); 32 | // apply the window 33 | void apply_window( float * data, float * window, unsigned long length ); 34 | 35 | // real fft, N must be power of 2 36 | void rfft( float * x, long N, unsigned int forward ); 37 | // complex fft, NC must be power of 2 38 | void cfft( float * x, long NC, unsigned int forward ); 39 | 40 | // c linkage 41 | #if ( defined( __cplusplus ) || defined( _cplusplus ) ) 42 | } 43 | #endif 44 | 45 | #endif 46 | 47 | -------------------------------------------------------------------------------- /KeyboardVisualizerCommon/hsv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * RGB <--> HSV conversion in integer arithmetics, to be used on Windows. 3 | * Copyright (c) 2013 Martin Mitas 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the "Software"), 7 | * to deal in the Software without restriction, including without limitation 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | * and/or sell copies of the Software, and to permit persons to whom the 10 | * Software is furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | * IN THE SOFTWARE. 22 | */ 23 | 24 | #ifndef HSV_H 25 | #define HSV_H 26 | 27 | #define HUE_DEGREE 1 28 | 29 | typedef struct hsv_tag hsv_t; 30 | struct hsv_tag { 31 | unsigned int hue; /* 0 ... (360*HUE_DEGREE - 1) */ 32 | unsigned char saturation; /* 0 ... 255 */ 33 | unsigned char value; /* 0 ... 255 */ 34 | }; 35 | 36 | 37 | void rgb2hsv(unsigned int rgb, hsv_t* hsv); 38 | 39 | unsigned int hsv2rgb(hsv_t* hsv); 40 | 41 | 42 | #endif /* HSV_H */ 43 | -------------------------------------------------------------------------------- /KeyboardVisualizerQT/KeyboardVisDlg.h: -------------------------------------------------------------------------------- 1 | #ifndef KEYBOARDVISDLG_H 2 | #define KEYBOARDVISDLG_H 3 | 4 | #include "KeyboardVisualizerCommon/Visualizer.h" 5 | #include "ui_keyboardvisualizer.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace Ui 12 | { 13 | class KeyboardVisDlg; 14 | } 15 | 16 | class Ui::KeyboardVisDlg : public QMainWindow 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | explicit KeyboardVisDlg(QWidget *parent = 0); 22 | ~KeyboardVisDlg(); 23 | 24 | void show(); 25 | 26 | void SetVisualizer(Visualizer* v); 27 | void StartMinimized(boolean startmin); 28 | 29 | public slots: 30 | void UpdateOpenRGBClientList(); 31 | 32 | private slots: 33 | void update(); 34 | 35 | void show_hide(); 36 | 37 | void on_lineEdit_Background_Brightness_textChanged(const QString &arg1); 38 | 39 | void on_lineEdit_Amplitude_textChanged(const QString &arg1); 40 | 41 | void on_lineEdit_Average_Size_textChanged(const QString &arg1); 42 | 43 | void on_lineEdit_Decay_textChanged(const QString &arg1); 44 | 45 | void on_lineEdit_Delay_textChanged(const QString &arg1); 46 | 47 | void on_lineEdit_Normalization_Offset_textChanged(const QString &arg1); 48 | 49 | void on_lineEdit_Normalization_Scale_textChanged(const QString &arg1); 50 | 51 | void on_lineEdit_Animation_Speed_textChanged(const QString &arg1); 52 | 53 | void on_comboBox_FFT_Window_Mode_currentIndexChanged(int index); 54 | 55 | void on_comboBox_Background_Mode_currentIndexChanged(int index); 56 | 57 | void on_comboBox_Foreground_Mode_currentIndexChanged(int index); 58 | 59 | void on_comboBox_Single_Color_Mode_currentIndexChanged(int index); 60 | 61 | void on_comboBox_Average_Mode_currentIndexChanged(int index); 62 | 63 | void on_pushButton_Save_Settings_clicked(); 64 | 65 | void on_checkBox_Reactive_Background_clicked(bool checked); 66 | 67 | void on_comboBox_Audio_Device_currentIndexChanged(int index); 68 | 69 | void on_lineEdit_Filter_Constant_textChanged(const QString &arg1); 70 | 71 | void on_checkBox_Silent_Background_clicked(bool checked); 72 | 73 | void on_lineEdit_Background_Timeout_textChanged(const QString &arg1); 74 | 75 | void on_button_Connect_clicked(); 76 | 77 | void on_button_Disconnect_clicked(QObject *); 78 | 79 | void on_button_Enabled_clicked(QObject *); 80 | 81 | private: 82 | Ui::KeyboardVisualizerDlg *ui; 83 | QSystemTrayIcon* trayIcon; 84 | QTimer* timer; 85 | QGraphicsScene* scene; 86 | QImage* image; 87 | QPixmap pixmap; 88 | }; 89 | #endif // KEYBOARDVISDLG_H 90 | -------------------------------------------------------------------------------- /scripts/build-appimage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #-----------------------------------------------------------------------# 4 | # Keyboard Visualizer AppImage Build Script # 5 | #-----------------------------------------------------------------------# 6 | 7 | set -x 8 | set -e 9 | 10 | #-----------------------------------------------------------------------# 11 | # Build in a temporary directory to keep the system clean # 12 | # Use RAM disk if possible (if available and not building on a CI # 13 | # system like Travis) # 14 | #-----------------------------------------------------------------------# 15 | if [ "$CI" == "" ] && [ -d /dev/shm ]; then 16 | TEMP_BASE=/dev/shm 17 | else 18 | TEMP_BASE=/tmp 19 | fi 20 | 21 | BUILD_DIR=$(mktemp -d -p "$TEMP_BASE" appimage-build-XXXXXX) 22 | 23 | #-----------------------------------------------------------------------# 24 | # This checks the Architecture of the system to work out if we're # 25 | # building on i386 or x86_64 and saves for later use # 26 | #-----------------------------------------------------------------------# 27 | 28 | if [ ${DEB_HOST_ARCH:0:1} == ${DEB_HOST_GNU_CPU:0:1} ]; then 29 | ARCH="$DEB_HOST_ARCH" 30 | else 31 | ARCH="$DEB_HOST_GNU_CPU" 32 | fi 33 | echo Inputs: "$DEB_HOST_ARCH" "$DEB_HOST_GNU_CPU" 34 | echo Calculated: "$ARCH" 35 | 36 | #-----------------------------------------------------------------------# 37 | # Make sure to clean up build dir, even if errors occur # 38 | #-----------------------------------------------------------------------# 39 | cleanup () { 40 | if [ -d "$BUILD_DIR" ]; then 41 | rm -rf "$BUILD_DIR" 42 | fi 43 | } 44 | trap cleanup EXIT 45 | 46 | #-----------------------------------------------------------------------# 47 | # Store repo root as variable # 48 | #-----------------------------------------------------------------------# 49 | REPO_ROOT=$(readlink -f $(dirname $(dirname $0))) 50 | OLD_CWD=$(readlink -f .) 51 | 52 | #-----------------------------------------------------------------------# 53 | # Switch to build dir # 54 | #-----------------------------------------------------------------------# 55 | pushd "$BUILD_DIR" 56 | 57 | #-----------------------------------------------------------------------# 58 | # Configure build files with qmake # 59 | # we need to explicitly set the install prefix, as qmake's default is # 60 | # /usr/local for some reason... # 61 | #-----------------------------------------------------------------------# 62 | qmake "$REPO_ROOT" 63 | 64 | #-----------------------------------------------------------------------# 65 | # Build project and install files into AppDir # 66 | #-----------------------------------------------------------------------# 67 | make -j$(nproc) TARGET="$TARGET" 68 | make install INSTALL_ROOT=AppDir 69 | 70 | #-----------------------------------------------------------------------# 71 | # Make them executable # 72 | #-----------------------------------------------------------------------# 73 | chmod +x "$REPO_ROOT"/OpenRGB/scripts/tools/linuxdeploy*.AppImage 74 | 75 | #-----------------------------------------------------------------------# 76 | # Make sure Qt plugin finds QML sources so it can deploy the imported # 77 | # files # 78 | export QML_SOURCES_PATHS="$REPO_ROOT"/src 79 | 80 | "$REPO_ROOT"/OpenRGB/scripts/tools/linuxdeploy-"$ARCH".AppImage --appimage-extract-and-run --appdir AppDir -e KeyboardVisualizer -i "$REPO_ROOT"/KeyboardVisualizerQT/KeyboardVisualizer.png -d "$REPO_ROOT"/KeyboardVisualizerQT/KeyboardVisualizer.desktop 81 | "$REPO_ROOT"/OpenRGB/scripts/tools/linuxdeploy-plugin-qt-"$ARCH".AppImage --appimage-extract-and-run --appdir AppDir 82 | "$REPO_ROOT"/OpenRGB/scripts/tools/linuxdeploy-"$ARCH".AppImage --appimage-extract-and-run --appdir AppDir --output appimage 83 | 84 | #-----------------------------------------------------------------------# 85 | # Move built AppImage back into original CWD # 86 | #-----------------------------------------------------------------------# 87 | mv Keyboard_Visualizer*.AppImage "$OLD_CWD" 88 | 89 | -------------------------------------------------------------------------------- /KeyboardVisualizerCommon/VisualizerDefines.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------*\ 2 | | Common Definitions for Keyboard Visualizer | 3 | | | 4 | | Adam Honse (calcprogrammer1@gmail.com), 12/11/2016 | 5 | \*---------------------------------------------------------*/ 6 | 7 | #ifndef VISUALIZER_DEFINES_H 8 | #define VISUALIZER_DEFINES_H 9 | 10 | /*---------------------------------------------------------*\ 11 | | Project Includes | 12 | \*---------------------------------------------------------*/ 13 | #include "RGBController.h" 14 | 15 | /*---------------------------------------------------------*\ 16 | | Define Windows types for non-Windows builds | 17 | \*---------------------------------------------------------*/ 18 | #ifndef _WIN32 19 | #include 20 | 21 | typedef unsigned int COLORREF; 22 | typedef unsigned char BYTE; 23 | typedef bool boolean; 24 | #define RGB(r, g, b) (((unsigned char)r) | ((unsigned char)g << 8) | ((unsigned char)b << 16)) 25 | #define GetRValue(rgb) ((unsigned char) (rgb)) 26 | #define GetGValue(rgb) ((unsigned char) ((rgb) >> 8)) 27 | #define GetBValue(rgb) ((unsigned char) ((rgb) >> 16)) 28 | #define Sleep(ms) (usleep(ms * 1000)) 29 | #define LPSTR char * 30 | #define strtok_s strtok_r 31 | #endif 32 | 33 | #define RGB2BGR(a_ulColor) (a_ulColor & 0xFF000000) | ((a_ulColor & 0xFF0000) >> 16) | (a_ulColor & 0x00FF00) | ((a_ulColor & 0x0000FF) << 16) 34 | 35 | //Special purpose row indices 36 | #define ROW_IDX_BAR_GRAPH 0 37 | #define ROW_IDX_SINGLE_COLOR 1 38 | #define ROW_IDX_SPECTROGRAPH_TOP 2 39 | 40 | #define SPECTROGRAPH_COLS (256) 41 | #define SPECTROGRAPH_END (SPECTROGRAPH_COLS - 1) 42 | #define SPECTROGRAPH_ROWS (64 - ROW_IDX_SPECTROGRAPH_TOP) 43 | 44 | typedef unsigned int VISUALIZER_PATTERN; 45 | enum 46 | { 47 | //Static Solid Colors 48 | VISUALIZER_PATTERN_SOLID_BLACK, 49 | VISUALIZER_PATTERN_SOLID_WHITE, 50 | VISUALIZER_PATTERN_SOLID_RED, 51 | VISUALIZER_PATTERN_SOLID_ORANGE, 52 | VISUALIZER_PATTERN_SOLID_YELLOW, 53 | VISUALIZER_PATTERN_SOLID_GREEN, 54 | VISUALIZER_PATTERN_SOLID_CYAN, 55 | VISUALIZER_PATTERN_SOLID_BLUE, 56 | VISUALIZER_PATTERN_SOLID_PURPLE, 57 | //Static Color Patterns 58 | VISUALIZER_PATTERN_STATIC_GREEN_YELLOW_RED, 59 | VISUALIZER_PATTERN_STATIC_GREEN_WHITE_RED, 60 | VISUALIZER_PATTERN_STATIC_BLUE_CYAN_WHITE, 61 | VISUALIZER_PATTERN_STATIC_RED_WHITE_BLUE, 62 | VISUALIZER_PATTERN_STATIC_RAINBOW, 63 | VISUALIZER_PATTERN_STATIC_RAINBOW_INVERSE, 64 | //Animated Patterns 65 | VISUALIZER_PATTERN_ANIM_RAINBOW_SINUSOIDAL, 66 | VISUALIZER_PATTERN_ANIM_RAINBOW_HSV, 67 | VISUALIZER_PATTERN_ANIM_COLOR_WHEEL, 68 | VISUALIZER_PATTERN_ANIM_COLOR_WHEEL_2, 69 | VISUALIZER_PATTERN_ANIM_SPECTRUM_CYCLE, 70 | VISUALIZER_PATTERN_ANIM_SINUSOIDAL_CYCLE, 71 | 72 | //Number of Patterns 73 | VISUALIZER_NUM_PATTERNS 74 | }; 75 | 76 | const char * const visualizer_pattern_labels[VISUALIZER_NUM_PATTERNS] = 77 | { 78 | "Black", 79 | "White", 80 | "Red", 81 | "Orange", 82 | "Yellow", 83 | "Green", 84 | "Cyan", 85 | "Blue", 86 | "Purple", 87 | "Green/Yellow/Red", 88 | "Green/White/Red", 89 | "Blue/Cyan/White", 90 | "Red/White/Blue", 91 | "Rainbow Bars", 92 | "Rainbow Bars Inverse", 93 | "Original", 94 | "Rainbow", 95 | "Color Wheel", 96 | "Color Wheel 2", 97 | "Spectrum Cycle", 98 | "Sinusoidal Cycle" 99 | }; 100 | 101 | typedef unsigned int VISUALIZER_SINGLE_COLOR; 102 | enum 103 | { 104 | //Basic Colors 105 | VISUALIZER_SINGLE_COLOR_BLACK, 106 | VISUALIZER_SINGLE_COLOR_WHITE, 107 | VISUALIZER_SINGLE_COLOR_RED, 108 | VISUALIZER_SINGLE_COLOR_ORANGE, 109 | VISUALIZER_SINGLE_COLOR_YELLOW, 110 | VISUALIZER_SINGLE_COLOR_GREEN, 111 | VISUALIZER_SINGLE_COLOR_CYAN, 112 | VISUALIZER_SINGLE_COLOR_BLUE, 113 | VISUALIZER_SINGLE_COLOR_PURPLE, 114 | 115 | VISUALIZER_SINGLE_COLOR_BACKGROUND, 116 | VISUALIZER_SINGLE_COLOR_FOLLOW_BACKGROUND, 117 | VISUALIZER_SINGLE_COLOR_FOLLOW_FOREGROUND, 118 | 119 | //Number of Single Color Modes 120 | VISUALIZER_NUM_SINGLE_COLOR 121 | }; 122 | 123 | const char * const visualizer_single_color_labels[VISUALIZER_NUM_SINGLE_COLOR] = 124 | { 125 | "Black", 126 | "White", 127 | "Red", 128 | "Orange", 129 | "Yellow", 130 | "Green", 131 | "Cyan", 132 | "Blue", 133 | "Purple", 134 | "Background", 135 | "Follow Background", 136 | "Follow Foreground" 137 | }; 138 | 139 | typedef struct 140 | { 141 | RGBColor pixels[64][256]; 142 | } vis_pixels; 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /KeyboardVisualizerCommon/hsv.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * RGB <--> HSV conversion in integer arithmetics, to be used on Windows. 3 | * Copyright (c) 2013 Martin Mitas 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the "Software"), 7 | * to deal in the Software without restriction, including without limitation 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | * and/or sell copies of the Software, and to permit persons to whom the 10 | * Software is furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | * IN THE SOFTWARE. 22 | */ 23 | 24 | #include "hsv.h" 25 | 26 | 27 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) 28 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) 29 | 30 | #define MIN3(a,b,c) MIN((a), MIN((b), (c))) 31 | #define MAX3(a,b,c) MAX((a), MAX((b), (c))) 32 | 33 | #define RGBGetRValue(rgb) (rgb & 0x000000FF) 34 | #define RGBGetGValue(rgb) ((rgb >> 8) & 0x000000FF) 35 | #define RGBGetBValue(rgb) ((rgb >> 16) & 0x000000FF) 36 | 37 | #define ToRGBColor(r, g, b) ((b << 16) | (g << 8) | (r)) 38 | 39 | void rgb2hsv(unsigned int rgb, hsv_t* hsv) 40 | { 41 | int r = RGBGetRValue(rgb); 42 | int g = RGBGetGValue(rgb); 43 | int b = RGBGetBValue(rgb); 44 | int m = MIN3(r, g, b); 45 | int M = MAX3(r, g, b); 46 | int delta = M - m; 47 | 48 | if (delta == 0) { 49 | /* Achromatic case (i.e. grayscale) */ 50 | hsv->hue = -1; /* undefined */ 51 | hsv->saturation = 0; 52 | } 53 | else { 54 | int h; 55 | 56 | if (r == M) 57 | h = ((g - b) * 60 * HUE_DEGREE) / delta; 58 | else if (g == M) 59 | h = ((b - r) * 60 * HUE_DEGREE) / delta + 120 * HUE_DEGREE; 60 | else /*if(b == M)*/ 61 | h = ((r - g) * 60 * HUE_DEGREE) / delta + 240 * HUE_DEGREE; 62 | 63 | if (h < 0) 64 | h += 360 * HUE_DEGREE; 65 | 66 | hsv->hue = h; 67 | 68 | /* The constatnt 8 is tuned to statistically cause as little 69 | * tolerated mismatches as possible in RGB -> HSV -> RGB conversion. 70 | * (See the unit test at the bottom of this file.) 71 | */ 72 | hsv->saturation = (256 * delta - 8) / M; 73 | } 74 | hsv->value = M; 75 | } 76 | 77 | unsigned int hsv2rgb(hsv_t* hsv) 78 | { 79 | unsigned char r = 0; 80 | unsigned char g = 0; 81 | unsigned char b = 0; 82 | 83 | if (hsv->saturation == 0) { 84 | r = g = b = hsv->value; 85 | } 86 | else { 87 | int h = (hsv->hue)%360; 88 | int s = hsv->saturation; 89 | int v = hsv->value; 90 | int i = h / (60 * HUE_DEGREE); 91 | int p = (256 * v - s*v) / 256; 92 | 93 | if (i & 1) { 94 | int q = (256 * 60 * HUE_DEGREE*v - h*s*v + 60 * HUE_DEGREE*s*v*i) / (256 * 60 * HUE_DEGREE); 95 | switch (i) { 96 | case 1: r = q; g = v; b = p; break; 97 | case 3: r = p; g = q; b = v; break; 98 | case 5: r = v; g = p; b = q; break; 99 | } 100 | } 101 | else { 102 | int t = (256 * 60 * HUE_DEGREE*v + h*s*v - 60 * HUE_DEGREE*s*v*(i + 1)) / (256 * 60 * HUE_DEGREE); 103 | switch (i) { 104 | case 0: r = v; g = t; b = p; break; 105 | case 2: r = p; g = v; b = t; break; 106 | case 4: r = t; g = p; b = v; break; 107 | } 108 | } 109 | } 110 | 111 | return ToRGBColor(r, g, b); 112 | } 113 | 114 | 115 | #ifdef TEST 116 | 117 | #include 118 | 119 | #define DIFF(a,b) ((a) >= (b) ? (a) - (b) : (b) - (a)) 120 | 121 | int 122 | main(int argc, char** argv) 123 | { 124 | int r0, g0, b0; 125 | hsv_t hsv; 126 | int r1, g1, b1; 127 | int ok = 0, fuzzy = 0, ko = 0, total; 128 | 129 | for (r0 = 0; r0 < 256; r0++) { 130 | for (g0 = 0; g0 < 256; g0++) { 131 | for (b0 = 0; b0 < 256; b0++) { 132 | COLORREF tmp; 133 | 134 | rgb2hsv(RGB(r0, g0, b0), &hsv); 135 | tmp = hsv2rgb(&hsv); 136 | r1 = GetRValue(tmp); 137 | g1 = GetGValue(tmp); 138 | b1 = GetBValue(tmp); 139 | 140 | if (r0 == r1 && b0 == b1 && g0 == g1) 141 | ok++; 142 | else if (DIFF(r0, r1) <= 1 && DIFF(b0, b1) <= 1 && DIFF(g0, g1) <= 1) 143 | fuzzy++; 144 | else 145 | ko++; 146 | } 147 | } 148 | } 149 | 150 | total = ok + fuzzy + ko; 151 | 152 | printf("Test results:\n"); 153 | printf(" * exact matches: %8d (%5.2f%%)\n", ok, (100.0 * ok) / total); 154 | printf(" * fuzzy matches: %8d (%5.2f%%)\n", fuzzy, (100.0 * fuzzy) / total); 155 | printf(" * mismatches: %8d (%5.2f%%)\n", ko, (100.0 * ko) / total); 156 | 157 | return (ko == 0 ? 0 : 1); 158 | } 159 | #endif /* TEST */ 160 | -------------------------------------------------------------------------------- /KeyboardVisualizerCommon/Visualizer.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------*\ 2 | | Definitions for Keyboard Visualizer | 3 | | | 4 | | Adam Honse (calcprogrammer1@gmail.com), 12/11/2016 | 5 | \*---------------------------------------------------------*/ 6 | 7 | #ifndef VISUALIZER_H 8 | #define VISUALIZER_H 9 | 10 | /*---------------------------------------------------------*\ 11 | | C/C++ Standard Library Includes | 12 | \*---------------------------------------------------------*/ 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | /*---------------------------------------------------------*\ 21 | | Project Includes | 22 | \*---------------------------------------------------------*/ 23 | #include "VisualizerDefines.h" 24 | #include "chuck_fft.h" 25 | #include "hsv.h" 26 | #include "net_port.h" 27 | 28 | /*---------------------------------------------------------*\ 29 | | OpenRGB SDK Includes | 30 | \*---------------------------------------------------------*/ 31 | #include "OpenRGB.h" 32 | #include "NetworkClient.h" 33 | #include "RGBController.h" 34 | 35 | /*---------------------------------------------------------*\ 36 | | Audio Library Includes | 37 | \*---------------------------------------------------------*/ 38 | #ifdef _WIN32 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #endif 46 | 47 | #ifdef __linux__ 48 | #include 49 | #include 50 | #endif 51 | 52 | #ifdef __APPLE__ 53 | #include 54 | #include 55 | #endif 56 | 57 | typedef struct 58 | { 59 | int x_count; 60 | int y_count; 61 | int * x_index; 62 | int * y_index; 63 | } ZoneIndexType; 64 | 65 | typedef struct 66 | { 67 | RGBController * controller_ptr; 68 | bool enabled; 69 | std::vector zones; 70 | } ControllerSettingsType; 71 | 72 | typedef struct 73 | { 74 | NetworkClient * client_ptr; 75 | std::vector controller_settings; 76 | } ClientSettingsType; 77 | 78 | class Visualizer 79 | { 80 | public: 81 | Visualizer(); 82 | 83 | //Initializes the visualizer 84 | void Initialize(); 85 | 86 | //Initialize the audio devices list 87 | void InitAudioDeviceList(); 88 | 89 | //Function to start thread 90 | void StartThread(); 91 | 92 | //Thread Functions 93 | void LEDUpdateThreadFunction(); 94 | void VisThreadFunction(); 95 | 96 | //Connect to OpenRGB 97 | NetworkClient *OpenRGBConnect(const char *ip, unsigned short port); 98 | 99 | //Disconnect from OpenRGB 100 | void OpenRGBDisconnect(NetworkClient * client); 101 | 102 | //Called when settings changed 103 | void OnSettingsChanged(); 104 | 105 | //Send Settings to network clients 106 | void SendSettings(); 107 | 108 | //Change Audio Device 109 | void ChangeAudioDevice(); 110 | 111 | //Update function 112 | void Update(); 113 | 114 | //Draw Pattern 115 | void DrawPattern(VISUALIZER_PATTERN pattern, int bright, vis_pixels *pixels); 116 | 117 | //Shut Down 118 | void Shutdown(); 119 | 120 | //Save Settings File 121 | void SaveSettings(); 122 | 123 | //Compute normalization line 124 | void SetNormalization(float offset, float scale); 125 | 126 | //Calculated FFT 127 | float fft[256]; 128 | 129 | //Amplitude of input waveform 130 | int amplitude; 131 | int avg_mode; 132 | int avg_size; 133 | int window_mode; 134 | int decay; 135 | int delay; 136 | unsigned int audio_device_idx; 137 | 138 | //Flag to update UI 139 | bool update_ui; 140 | 141 | //Visualizer Background 142 | vis_pixels pixels_bg; 143 | 144 | //Visualizer Foreground 145 | vis_pixels pixels_fg; 146 | 147 | //Visualizer Image 1 148 | vis_pixels pixels_vs1; 149 | 150 | //Visualizer Image 2 151 | vis_pixels pixels_vs2; 152 | 153 | //Visualizer Output Image Pointer 154 | vis_pixels *pixels_out; 155 | 156 | //Visualizer Render Image Pointer 157 | vis_pixels *pixels_render; 158 | 159 | //Background Variables 160 | float anim_speed; 161 | int bkgd_bright; 162 | int bkgd_mode; 163 | bool reactive_bkgd; 164 | bool silent_bkgd; 165 | unsigned int background_timeout; 166 | unsigned int background_timer; 167 | 168 | //Single Color Mode 169 | int single_color_mode; 170 | 171 | //Normalization Offset and Scale 172 | float nrml_ofst; 173 | float nrml_scl; 174 | 175 | //Filter Constant 176 | float filter_constant; 177 | 178 | //Foreground Variables 179 | int frgd_mode; 180 | 181 | //Audio Device List 182 | std::vector audio_devices; 183 | 184 | //OpenRGB SDK 185 | std::vector rgb_clients; 186 | std::vector rgb_controllers; 187 | 188 | std::vector rgb_client_settings; 189 | 190 | void UpdateClientSettings(); 191 | void RegisterClientInfoChangeCallback(NetClientCallback new_callback, void * new_callback_arg); 192 | void ClientInfoChanged(); 193 | 194 | private: 195 | #ifdef WIN32 196 | //WASAPI objects if building for Windows 197 | IMMDeviceEnumerator *pMMDeviceEnumerator; 198 | std::vector pMMDevices; 199 | std::vector isCapture; 200 | IMMDeviceCollection *pMMDeviceCollection; 201 | IAudioClient *pAudioClient; 202 | IAudioCaptureClient *pAudioCaptureClient; 203 | WAVEFORMATEX *waveformat; 204 | #else 205 | //Audio Device Pointer 206 | ALCdevice *device; 207 | #endif 208 | 209 | //Threads 210 | std::thread * LEDUpdateThread; 211 | std::thread * VisThread; 212 | 213 | //Background Step 214 | float bkgd_step; 215 | 216 | //FFT Variables 217 | float win_hanning[256]; 218 | float win_hamming[256]; 219 | float win_blackman[256]; 220 | float fft_tmp[512]; 221 | 222 | //Settings Changed Flag 223 | bool settings_changed; 224 | 225 | //Application Running Flag 226 | bool running; 227 | 228 | //Shutting Down Flag 229 | bool shutdown_flag; 230 | 231 | //Audio Sample Buffer 232 | unsigned char buffer[256]; 233 | 234 | void DrawSingleColorForeground(float amplitude, vis_pixels *fg_pixels, vis_pixels *out_pixels); 235 | 236 | std::mutex ClientInfoChangeMutex; 237 | std::vector ClientInfoChangeCallbacks; 238 | std::vector ClientInfoChangeCallbackArgs; 239 | }; 240 | 241 | #endif 242 | -------------------------------------------------------------------------------- /KeyboardVisualizerCommon/chuck_fft.c: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // name: chuck_fft.c 3 | // desc: fft impl - based on CARL distribution 4 | // 5 | // authors: code from San Diego CARL package 6 | // Ge Wang (gewang@cs.princeton.edu) 7 | // Perry R. Cook (prc@cs.princeton.edu) 8 | // date: 11.27.2003 9 | //----------------------------------------------------------------------------- 10 | #include "chuck_fft.h" 11 | #include 12 | #include 13 | 14 | 15 | 16 | 17 | //----------------------------------------------------------------------------- 18 | // name: hanning() 19 | // desc: make window 20 | //----------------------------------------------------------------------------- 21 | void hanning(float * window, unsigned long length) 22 | { 23 | unsigned long i; 24 | double pi, phase = 0, delta; 25 | 26 | pi = 4.*atan(1.0); 27 | delta = 2 * pi / (double)length; 28 | 29 | for (i = 0; i < length; i++) 30 | { 31 | window[i] = (float)(0.5 * (1.0 - cos(phase))); 32 | phase += delta; 33 | } 34 | } 35 | 36 | 37 | 38 | 39 | //----------------------------------------------------------------------------- 40 | // name: hamming() 41 | // desc: make window 42 | //----------------------------------------------------------------------------- 43 | void hamming(float * window, unsigned long length) 44 | { 45 | unsigned long i; 46 | double pi, phase = 0, delta; 47 | 48 | pi = 4.*atan(1.0); 49 | delta = 2 * pi / (double)length; 50 | 51 | for (i = 0; i < length; i++) 52 | { 53 | window[i] = (float)(0.54 - .46*cos(phase)); 54 | phase += delta; 55 | } 56 | } 57 | 58 | 59 | 60 | //----------------------------------------------------------------------------- 61 | // name: blackman() 62 | // desc: make window 63 | //----------------------------------------------------------------------------- 64 | void blackman(float * window, unsigned long length) 65 | { 66 | unsigned long i; 67 | double pi, phase = 0, delta; 68 | 69 | pi = 4.*atan(1.0); 70 | delta = 2 * pi / (double)length; 71 | 72 | for (i = 0; i < length; i++) 73 | { 74 | window[i] = (float)(0.42 - .5*cos(phase) + .08*cos(2 * phase)); 75 | phase += delta; 76 | } 77 | } 78 | 79 | 80 | 81 | 82 | //----------------------------------------------------------------------------- 83 | // name: apply_window() 84 | // desc: apply a window to data 85 | //----------------------------------------------------------------------------- 86 | void apply_window(float * data, float * window, unsigned long length) 87 | { 88 | unsigned long i; 89 | 90 | for (i = 0; i < length; i++) 91 | data[i] *= window[i]; 92 | } 93 | 94 | static float PI; 95 | static float TWOPI; 96 | void bit_reverse(float * x, long N); 97 | 98 | //----------------------------------------------------------------------------- 99 | // name: rfft() 100 | // desc: real value fft 101 | // 102 | // these routines from the CARL software, spect.c 103 | // check out the CARL CMusic distribution for more source code 104 | // 105 | // if forward is true, rfft replaces 2*N real data points in x with N complex 106 | // values representing the positive frequency half of their Fourier spectrum, 107 | // with x[1] replaced with the real part of the Nyquist frequency value. 108 | // 109 | // if forward is false, rfft expects x to contain a positive frequency 110 | // spectrum arranged as before, and replaces it with 2*N real values. 111 | // 112 | // N MUST be a power of 2. 113 | // 114 | //----------------------------------------------------------------------------- 115 | void rfft(float * x, long N, unsigned int forward) 116 | { 117 | static int first = 1; 118 | float c1, c2, h1r, h1i, h2r, h2i, wr, wi, wpr, wpi, temp, theta; 119 | float xr, xi; 120 | long i, i1, i2, i3, i4, N2p1; 121 | 122 | if (first) 123 | { 124 | PI = (float)(4.*atan(1.)); 125 | TWOPI = (float)(8.*atan(1.)); 126 | first = 0; 127 | } 128 | 129 | theta = PI / N; 130 | wr = 1.; 131 | wi = 0.; 132 | c1 = 0.5; 133 | 134 | if (forward) 135 | { 136 | c2 = -0.5; 137 | cfft(x, N, forward); 138 | xr = x[0]; 139 | xi = x[1]; 140 | } 141 | else 142 | { 143 | c2 = 0.5; 144 | theta = -theta; 145 | xr = x[1]; 146 | xi = 0.; 147 | x[1] = 0.; 148 | } 149 | 150 | wpr = (float)(-2.*pow(sin(0.5*theta), 2.)); 151 | wpi = (float)sin(theta); 152 | N2p1 = (N << 1) + 1; 153 | 154 | for (i = 0; i <= N >> 1; i++) 155 | { 156 | i1 = i << 1; 157 | i2 = i1 + 1; 158 | i3 = N2p1 - i2; 159 | i4 = i3 + 1; 160 | if (i == 0) 161 | { 162 | h1r = c1*(x[i1] + xr); 163 | h1i = c1*(x[i2] - xi); 164 | h2r = -c2*(x[i2] + xi); 165 | h2i = c2*(x[i1] - xr); 166 | x[i1] = h1r + wr*h2r - wi*h2i; 167 | x[i2] = h1i + wr*h2i + wi*h2r; 168 | xr = h1r - wr*h2r + wi*h2i; 169 | xi = -h1i + wr*h2i + wi*h2r; 170 | } 171 | else 172 | { 173 | h1r = c1*(x[i1] + x[i3]); 174 | h1i = c1*(x[i2] - x[i4]); 175 | h2r = -c2*(x[i2] + x[i4]); 176 | h2i = c2*(x[i1] - x[i3]); 177 | x[i1] = h1r + wr*h2r - wi*h2i; 178 | x[i2] = h1i + wr*h2i + wi*h2r; 179 | x[i3] = h1r - wr*h2r + wi*h2i; 180 | x[i4] = -h1i + wr*h2i + wi*h2r; 181 | } 182 | 183 | wr = (temp = wr)*wpr - wi*wpi + wr; 184 | wi = wi*wpr + temp*wpi + wi; 185 | } 186 | 187 | if (forward) 188 | x[1] = xr; 189 | else 190 | cfft(x, N, forward); 191 | } 192 | 193 | 194 | 195 | 196 | //----------------------------------------------------------------------------- 197 | // name: cfft() 198 | // desc: complex value fft 199 | // 200 | // these routines from CARL software, spect.c 201 | // check out the CARL CMusic distribution for more software 202 | // 203 | // cfft replaces float array x containing NC complex values (2*NC float 204 | // values alternating real, imagininary, etc.) by its Fourier transform 205 | // if forward is true, or by its inverse Fourier transform ifforward is 206 | // false, using a recursive Fast Fourier transform method due to 207 | // Danielson and Lanczos. 208 | // 209 | // NC MUST be a power of 2. 210 | // 211 | //----------------------------------------------------------------------------- 212 | void cfft(float * x, long NC, unsigned int forward) 213 | { 214 | float wr, wi, wpr, wpi, theta, scale; 215 | long mmax, ND, m, i, j, delta; 216 | ND = NC << 1; 217 | bit_reverse(x, ND); 218 | 219 | for (mmax = 2; mmax < ND; mmax = delta) 220 | { 221 | delta = mmax << 1; 222 | theta = TWOPI / (forward ? mmax : -mmax); 223 | wpr = (float)(-2.*pow(sin(0.5*theta), 2.)); 224 | wpi = (float)sin(theta); 225 | wr = 1.; 226 | wi = 0.; 227 | 228 | for (m = 0; m < mmax; m += 2) 229 | { 230 | register float rtemp, itemp; 231 | for (i = m; i < ND; i += delta) 232 | { 233 | j = i + mmax; 234 | rtemp = wr*x[j] - wi*x[j + 1]; 235 | itemp = wr*x[j + 1] + wi*x[j]; 236 | if (j >= 511 || i >= 511) 237 | { 238 | j = 0; 239 | } 240 | x[j] = x[i] - rtemp; 241 | x[j + 1] = x[i + 1] - itemp; 242 | x[i] += rtemp; 243 | x[i + 1] += itemp; 244 | } 245 | 246 | wr = (rtemp = wr)*wpr - wi*wpi + wr; 247 | wi = wi*wpr + rtemp*wpi + wi; 248 | } 249 | } 250 | 251 | // scale output 252 | scale = (float)(forward ? 1. / ND : 2.); 253 | { 254 | register float *xi = x, *xe = x + ND; 255 | while (xi < xe) 256 | *xi++ *= scale; 257 | } 258 | } 259 | 260 | 261 | 262 | 263 | //----------------------------------------------------------------------------- 264 | // name: bit_reverse() 265 | // desc: bitreverse places float array x containing N/2 complex values 266 | // into bit-reversed order 267 | //----------------------------------------------------------------------------- 268 | void bit_reverse(float * x, long N) 269 | { 270 | float rtemp, itemp; 271 | long i, j, m; 272 | for (i = j = 0; i < N; i += 2, j += m) 273 | { 274 | if (j > i) 275 | { 276 | rtemp = x[j]; itemp = x[j + 1]; /* complex exchange */ 277 | x[j] = x[i]; x[j + 1] = x[i + 1]; 278 | x[i] = rtemp; x[i + 1] = itemp; 279 | } 280 | 281 | for (m = N >> 1; m >= 2 && j >= m; m >>= 1) 282 | j -= m; 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /KeyboardVisualizer.pro: -------------------------------------------------------------------------------- 1 | #-----------------------------------------------------------------------# 2 | # Keyboard Visualizer 4.x QMake Project # 3 | # # 4 | # Adam Honse (CalcProgrammer1) 5/15/2020 # 5 | #-----------------------------------------------------------------------# 6 | 7 | #-----------------------------------------------------------------------# 8 | # Qt Configuration # 9 | #-----------------------------------------------------------------------# 10 | QT += \ 11 | core \ 12 | gui \ 13 | 14 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 15 | 16 | #-----------------------------------------------------------------------# 17 | # Application Configuration # 18 | #-----------------------------------------------------------------------# 19 | VERSION = 4.01 20 | TARGET = KeyboardVisualizer 21 | TEMPLATE = app 22 | CONFIG += no_batch 23 | 24 | #-----------------------------------------------------------------------# 25 | # Automatically generated build information # 26 | #-----------------------------------------------------------------------# 27 | win32:BUILDDATE = $$system(date /t) 28 | unix:BUILDDATE = $$system(date -R) 29 | GIT_COMMIT_ID = $$system(git --git-dir $$_PRO_FILE_PWD_/.git --work-tree $$_PRO_FILE_PWD_ rev-parse HEAD) 30 | GIT_COMMIT_DATE = $$system(git --git-dir $$_PRO_FILE_PWD_/.git --work-tree $$_PRO_FILE_PWD_ show -s --format=%ci HEAD) 31 | GIT_BRANCH = $$system(git --git-dir $$_PRO_FILE_PWD_/.git --work-tree $$_PRO_FILE_PWD_ rev-parse --abbrev-ref HEAD) 32 | 33 | DEFINES += \ 34 | VERSION_STRING=\\"\"\"$$VERSION\\"\"\" \ 35 | BUILDDATE_STRING=\\"\"\"$$BUILDDATE\\"\"\" \ 36 | GIT_COMMIT_ID=\\"\"\"$$GIT_COMMIT_ID\\"\"\" \ 37 | GIT_COMMIT_DATE=\\"\"\"$$GIT_COMMIT_DATE\\"\"\" \ 38 | GIT_BRANCH=\\"\"\"$$GIT_BRANCH\\"\"\" \ 39 | 40 | #-----------------------------------------------------------------------# 41 | # Keyboard Visualizer # 42 | #-----------------------------------------------------------------------# 43 | INCLUDEPATH += \ 44 | KeyboardVisualizerCommon/ \ 45 | KeyboardVisualizerQT/ \ 46 | 47 | HEADERS += \ 48 | KeyboardVisualizerCommon/chuck_fft.h \ 49 | KeyboardVisualizerCommon/hsv.h \ 50 | KeyboardVisualizerCommon/Visualizer.h \ 51 | KeyboardVisualizerCommon/VisualizerDefines.h \ 52 | KeyboardVisualizerQT/KeyboardVisDlg.h \ 53 | 54 | SOURCES += \ 55 | KeyboardVisualizerCommon/hsv.cpp \ 56 | KeyboardVisualizerCommon/Visualizer.cpp \ 57 | KeyboardVisualizerCommon/chuck_fft.c \ 58 | KeyboardVisualizerQT/KeyboardVisDlg.cpp \ 59 | KeyboardVisualizerQT/main.cpp \ 60 | 61 | RESOURCES += \ 62 | KeyboardVisualizerQT/resources.qrc 63 | 64 | FORMS += \ 65 | KeyboardVisualizerQT/keyboardvisualizer.ui 66 | 67 | DISTFILES += \ 68 | 69 | #-----------------------------------------------------------------------# 70 | # OpenRGB SDK # 71 | #-----------------------------------------------------------------------# 72 | INCLUDEPATH += \ 73 | OpenRGB/ \ 74 | OpenRGB/net_port/ \ 75 | OpenRGB/RGBController/ \ 76 | 77 | HEADERS += \ 78 | OpenRGB/NetworkClient.h \ 79 | OpenRGB/NetworkProtocol.h \ 80 | OpenRGB/net_port/net_port.h \ 81 | OpenRGB/RGBController/RGBController.h \ 82 | OpenRGB/RGBController/RGBController_Network.h \ 83 | 84 | SOURCES += \ 85 | OpenRGB/NetworkClient.cpp \ 86 | OpenRGB/net_port/net_port.cpp \ 87 | OpenRGB/RGBController/RGBController.cpp \ 88 | OpenRGB/RGBController/RGBController_Network.cpp \ 89 | 90 | #-----------------------------------------------------------------------# 91 | # Windows-specific Configuration # 92 | #-----------------------------------------------------------------------# 93 | win32:contains(QMAKE_TARGET.arch, x86_64) { 94 | LIBS += \ 95 | -lws2_32 \ 96 | -lole32 \ 97 | } 98 | 99 | win32:contains(QMAKE_TARGET.arch, x86) { 100 | LIBS += \ 101 | -lws2_32 \ 102 | -lole32 \ 103 | } 104 | 105 | win32:DEFINES -= \ 106 | UNICODE 107 | 108 | win32:DEFINES += \ 109 | _MBCS \ 110 | WIN32 \ 111 | _CRT_SECURE_NO_WARNINGS \ 112 | _WINSOCK_DEPRECATED_NO_WARNINGS \ 113 | WIN32_LEAN_AND_MEAN 114 | 115 | win32:RC_ICONS += \ 116 | KeyboardVisualizerQT/KeyboardVisualizer.ico 117 | 118 | #-------------------------------------------------------------------# 119 | # Windows GitLab CI Configuration # 120 | #-------------------------------------------------------------------# 121 | win32:CONFIG(debug, debug|release) { 122 | win32:DESTDIR = debug 123 | } 124 | 125 | win32:CONFIG(release, debug|release) { 126 | win32:DESTDIR = release 127 | } 128 | 129 | win32:OBJECTS_DIR = _intermediate_$$DESTDIR/.obj 130 | win32:MOC_DIR = _intermediate_$$DESTDIR/.moc 131 | win32:RCC_DIR = _intermediate_$$DESTDIR/.qrc 132 | win32:UI_DIR = _intermediate_$$DESTDIR/.ui 133 | 134 | #-----------------------------------------------------------------------# 135 | # Linux-specific Configuration # 136 | #-----------------------------------------------------------------------# 137 | unix:!macx { 138 | LIBS += -lopenal 139 | 140 | #-------------------------------------------------------------------# 141 | # Set up install paths # 142 | # These install paths are used for AppImage and .deb packaging # 143 | #-------------------------------------------------------------------# 144 | isEmpty(PREFIX) { 145 | PREFIX = /usr 146 | } 147 | 148 | target.path=$$PREFIX/bin/ 149 | desktop.path=$$PREFIX/share/applications/ 150 | desktop.files+=KeyboardVisualizerQT/KeyboardVisualizer.desktop 151 | pixmap.path=$$PREFIX/share/pixmaps/ 152 | pixmap.files+=KeyboardVisualizerQT/KeyboardVisualizer.png 153 | INSTALLS += target desktop pixmap 154 | } 155 | 156 | #-----------------------------------------------------------------------# 157 | # MacOS-specific Configuration # 158 | #-----------------------------------------------------------------------# 159 | macx: { 160 | CONFIG += c++14 161 | LIBS += -framework OpenAL 162 | } 163 | 164 | macx:ICON += \ 165 | KeyboardVisualizerQT/KeyboardVisualizer.icns 166 | -------------------------------------------------------------------------------- /KeyboardVisualizerQT/main.cpp: -------------------------------------------------------------------------------- 1 | #include "KeyboardVisDlg.h" 2 | #include 3 | #include "KeyboardVisualizerCommon/Visualizer.h" 4 | #include "KeyboardVisualizerCommon/VisualizerDefines.h" 5 | 6 | #if defined(_WIN32) || defined(_WIN64) 7 | /* We are on Windows */ 8 | # define strtok_r strtok_s 9 | #endif 10 | 11 | #ifndef WIN32 12 | #include 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | Visualizer vis; 20 | static boolean startminimized; 21 | 22 | void parse_argument_string(char * argument, char * value) 23 | { 24 | //Strip off new line characters if present 25 | argument = strtok(argument, "\r\n"); 26 | value = strtok(value, "\r\n"); 27 | 28 | if(argument) 29 | { 30 | if (strcmp(argument, "startminimized") == 0) 31 | { 32 | startminimized = true; 33 | } 34 | else if (strcmp(argument, "amplitude") == 0) 35 | { 36 | vis.amplitude = atoi(value); 37 | } 38 | else if (strcmp(argument, "bkgd_bright") == 0) 39 | { 40 | vis.bkgd_bright = atoi(value); 41 | } 42 | else if (strcmp(argument, "avg_size") == 0) 43 | { 44 | vis.avg_size = atoi(value); 45 | if (vis.avg_size < 1) 46 | { 47 | vis.avg_size = 1; 48 | } 49 | else if (vis.avg_size > 128) 50 | { 51 | vis.avg_size = 128; 52 | } 53 | } 54 | else if (strcmp(argument, "decay") == 0) 55 | { 56 | vis.decay = atoi(value); 57 | } 58 | else if (strcmp(argument, "delay") == 0) 59 | { 60 | vis.delay = atoi(value); 61 | } 62 | else if (strcmp(argument, "nrml_ofst") == 0) 63 | { 64 | vis.nrml_ofst = strtod(value, NULL); 65 | } 66 | else if (strcmp(argument, "nrml_scl") == 0) 67 | { 68 | vis.nrml_scl = strtod(value, NULL); 69 | } 70 | else if (strcmp(argument, "fltr_const") == 0) 71 | { 72 | vis.filter_constant = strtod(value, NULL); 73 | if (vis.filter_constant > 1.0f) 74 | { 75 | vis.filter_constant = 1.0f; 76 | } 77 | else if (vis.filter_constant < 0.0f) 78 | { 79 | vis.filter_constant = 0.0f; 80 | } 81 | } 82 | else if (strcmp(argument, "anim_speed") == 0) 83 | { 84 | vis.anim_speed = strtod(value, NULL); 85 | } 86 | else if (strcmp(argument, "window_mode") == 0) 87 | { 88 | if ((atoi(value) >= 0) && (atoi(value) <= 4)) 89 | { 90 | vis.window_mode = atoi(value); 91 | } 92 | } 93 | else if (strcmp(argument, "bkgd_mode") == 0) 94 | { 95 | if ((atoi(value) >= 0) && (atoi(value) <= VISUALIZER_NUM_PATTERNS)) 96 | { 97 | vis.bkgd_mode = atoi(value); 98 | } 99 | } 100 | else if (strcmp(argument, "frgd_mode") == 0) 101 | { 102 | if ((atoi(value) >= 0) && (atoi(value) <= VISUALIZER_NUM_PATTERNS)) 103 | { 104 | vis.frgd_mode = atoi(value); 105 | } 106 | } 107 | else if (strcmp(argument, "single_color_mode") == 0) 108 | { 109 | if ((atoi(value) >= 0) && (atoi(value) <= VISUALIZER_NUM_SINGLE_COLOR)) 110 | { 111 | vis.single_color_mode = atoi(value); 112 | } 113 | } 114 | else if (strcmp(argument, "avg_mode") == 0) 115 | { 116 | if ((atoi(value) >= 0) && (atoi(value) <= 1)) 117 | { 118 | vis.avg_mode = atoi(value); 119 | } 120 | } 121 | else if (strcmp(argument, "reactive_bkgd") == 0) 122 | { 123 | vis.reactive_bkgd = atoi(value); 124 | 125 | if ((vis.silent_bkgd == true) && (vis.reactive_bkgd == true)) 126 | { 127 | vis.silent_bkgd = false; 128 | } 129 | } 130 | else if (strcmp(argument, "silent_bkgd") == 0) 131 | { 132 | vis.silent_bkgd = atoi(value); 133 | 134 | if ((vis.silent_bkgd == true) && (vis.reactive_bkgd == true)) 135 | { 136 | vis.reactive_bkgd = false; 137 | } 138 | } 139 | else if (strcmp(argument, "background_timeout") == 0) 140 | { 141 | vis.background_timeout = atoi(value); 142 | vis.background_timer = 0; 143 | } 144 | else if (strcmp(argument, "audio_device_idx") == 0) 145 | { 146 | vis.audio_device_idx = atoi(value); 147 | vis.ChangeAudioDevice(); 148 | } 149 | } 150 | } 151 | 152 | bool parse_command_line(int argc, char *argv[]) 153 | { 154 | for(int i = 1; i < argc; i++) 155 | { 156 | char* argument; 157 | char* value; 158 | 159 | argument = strtok_r(argv[i], "=", &value); 160 | 161 | if (strcmp(argument, "help") == 0) 162 | { 163 | printf("\r\n"); 164 | printf("Available command line commands:\r\n"); 165 | printf(" help - Display this message and quit\r\n"); 166 | //printf(" nogui - Run visualizer with no GUI\r\n"); 167 | printf(" startminimized - Start in system tray\r\n"); 168 | printf("\r\n"); 169 | printf("Available command line arguments:\r\n"); 170 | printf(" Usage: argument1=value1 argument2=value2 ...\r\n"); 171 | printf("\r\n"); 172 | printf(" amplitude - Adjust the amplitude of the visualizer\r\n"); 173 | printf(" bkgd_bright - Adjust the background brightness\r\n"); 174 | printf(" avg_size - Number of points to average\r\n"); 175 | printf(" decay - Percentage of value to decay every step\r\n"); 176 | printf(" delay - Milliseconds between each device update\r\n"); 177 | printf(" nrml_ofst - Normalization offset, floating point value\r\n"); 178 | printf(" nrml_scl - Normalization scale, floating point value\r\n"); 179 | printf(" fltr_const - Low pass filter constant, floating point value 0-1\r\n"); 180 | printf(" window_mode - FFT windowing mode selector, values are:\r\n"); 181 | printf(" - 0: No windowing\r\n"); 182 | printf(" - 1: Hanning window\r\n"); 183 | printf(" - 2: Hamming window\r\n"); 184 | printf(" - 3: Blackman window\r\n"); 185 | printf(" bkgd_mode - Background mode, values are:\r\n"); 186 | for (int i = 0; i < VISUALIZER_NUM_PATTERNS; i++) 187 | { 188 | if (i < 10) 189 | { 190 | printf(" - %d %s\r\n", i, visualizer_pattern_labels[i]); 191 | } 192 | else 193 | { 194 | printf(" - %d %s\r\n", i, visualizer_pattern_labels[i]); 195 | } 196 | } 197 | printf(" frgd_mode - Foreground mode, values are:\r\n"); 198 | for (int i = 0; i < VISUALIZER_NUM_PATTERNS; i++) 199 | { 200 | if (i < 10) 201 | { 202 | printf(" - %d %s\r\n", i, visualizer_pattern_labels[i]); 203 | } 204 | else 205 | { 206 | printf(" - %d %s\r\n", i, visualizer_pattern_labels[i]); 207 | } 208 | } 209 | printf(" single_color_mode - Single color mode, values are:\r\n"); 210 | for (int i = 0; i < VISUALIZER_NUM_SINGLE_COLOR; i++) 211 | { 212 | if (i < 10) 213 | { 214 | printf(" - %d %s\r\n", i, visualizer_single_color_labels[i]); 215 | } 216 | else 217 | { 218 | printf(" - %d %s\r\n", i, visualizer_single_color_labels[i]); 219 | } 220 | } 221 | printf(" avg_mode - Visualizer averaging mode\r\n"); 222 | printf(" - 0: Binning\r\n"); 223 | printf(" - 1: Low-pass filtering\r\n"); 224 | printf(" anim_speed - Animation Speed (percent)\r\n"); 225 | return false; 226 | } 227 | 228 | parse_argument_string(argument, value); 229 | } 230 | return true; 231 | } 232 | 233 | void parse_settings_file(const char * filename) 234 | { 235 | std::ifstream infile; 236 | 237 | //Open settings file 238 | infile.open(filename); 239 | 240 | if (infile.good()) 241 | { 242 | for (std::string line; std::getline(infile, line); ) 243 | { 244 | if (line == "") 245 | { 246 | continue; 247 | } 248 | if ((line[0] != ';') && (line[0] != '#') && (line[0] != '/')) 249 | { 250 | char * argument; 251 | char * value; 252 | 253 | value = (char *)line.c_str(); 254 | 255 | argument = strtok_r(value, "=", &value); 256 | 257 | parse_argument_string(argument, value); 258 | } 259 | } 260 | } 261 | } 262 | 263 | int main(int argc, char *argv[]) 264 | { 265 | QApplication a(argc, argv); 266 | 267 | startminimized = false; 268 | 269 | //Initialize Visualizer 270 | vis.Initialize(); 271 | 272 | //Parse Settings File 273 | parse_settings_file("settings.txt"); 274 | 275 | //Parse Command Line 276 | if (!parse_command_line(argc, argv)) 277 | { 278 | return 0; 279 | } 280 | 281 | //Start Visualizer Threads 282 | vis.StartThread(); 283 | 284 | //Create Dialog 285 | Ui::KeyboardVisDlg dlg; 286 | dlg.StartMinimized(startminimized); 287 | dlg.SetVisualizer(&vis); 288 | dlg.show(); 289 | 290 | return a.exec(); 291 | } 292 | -------------------------------------------------------------------------------- /KeyboardVisualizerQT/keyboardvisualizer.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | KeyboardVisualizerDlg 4 | 5 | 6 | 7 | 0 8 | 0 9 | 600 10 | 600 11 | 12 | 13 | 14 | Keyboard Visualizer 15 | 16 | 17 | 18 | 19 | 20 | 21 | FFT Window Mode 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 0 30 | 0 31 | 32 | 33 | 34 | 35 | 256 36 | 64 37 | 38 | 39 | 40 | 41 | 256 42 | 64 43 | 44 | 45 | 46 | Qt::ScrollBarAlwaysOff 47 | 48 | 49 | Qt::ScrollBarAlwaysOff 50 | 51 | 52 | false 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | QComboBox::AdjustToMinimumContentsLength 69 | 70 | 71 | 72 | 73 | 74 | 75 | Visualization Preview 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | Normalization Offset 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | Average Size 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 1 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | Save Settings 116 | 117 | 118 | 119 | 120 | 121 | 122 | QComboBox::AdjustToMinimumContentsLength 123 | 124 | 125 | 126 | 127 | 128 | 129 | Normalization Scale 130 | 131 | 132 | 133 | 134 | 135 | 136 | Delay (ms) 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | Background Brightness 151 | 152 | 153 | 154 | 155 | 156 | 157 | Background Mode 158 | 159 | 160 | 161 | 162 | 163 | 164 | Decay (% per step) 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | Silent Background 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | Reactive Background 185 | 186 | 187 | 188 | 189 | 190 | 191 | QComboBox::AdjustToMinimumContentsLength 192 | 193 | 194 | 195 | 196 | 197 | 198 | Audio Device 199 | 200 | 201 | 202 | 203 | 204 | 205 | QComboBox::AdjustToMinimumContentsLength 206 | 207 | 208 | 209 | 210 | 211 | 212 | QComboBox::AdjustToMinimumContentsLength 213 | 214 | 215 | 216 | 217 | 218 | 219 | Background Timeout 220 | 221 | 222 | 223 | 224 | 225 | 226 | Filter Constant 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | Average Mode 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | QComboBox::AdjustToMinimumContentsLength 247 | 248 | 249 | 250 | 251 | 252 | 253 | IP: 254 | 255 | 256 | 257 | 258 | 259 | 260 | Amplitude (%) 261 | 262 | 263 | 264 | 265 | 266 | 267 | Single Color Mode 268 | 269 | 270 | 271 | 272 | 273 | 274 | Animation Speed 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | Foreground Mode 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | Port 301 | 302 | 303 | 304 | 305 | 306 | 307 | Connect 308 | 309 | 310 | 311 | 312 | 313 | 314 | https://gitlab.com/CalcProgrammer1/KeyboardVisualizer 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Keyboard Visualizer ![Icon](https://raw.githubusercontent.com/CalcProgrammer1/KeyboardVisualizer/master/KeyboardVisualizerQT/KeyboardVisualizer.png) 2 | 3 | ## OpenRGB Audio Visualizer for Windows, Linux, and MacOS 4 | 5 | Keyboard Visualizer turns your OpenRGB-supported RGB setup into a spectacular light show for your music. Keyboard Visualizer is able to listen to any input or loopback audio device on your computer so it can visualize audio from any music player, website, game, or other application. 6 | 7 | To use Keyboard Visualizer with your setup, you must first install OpenRGB and set it up to detect all of your RGB devices. OpenRGB can be found here: 8 | 9 | https://gitlab.com/CalcProgrammer1/OpenRGB 10 | 11 | ## What about the old Keyboard Visualizer with Razer Chroma SDK, Corsair SDK, etc? 12 | 13 | Development on this version has stalled as I'm not interested in supporting a multitude of proprietary SDKs that change constantly and only work on Windows. This version remains as Keyboard Visualizer 3.x and downloads are still available in the Release submodule. I will accept pull requests for this version if others wish to keep it alive, but I plan to focus primarily on the OpenRGB version (4.x) going forwards. 14 | 15 | # Download/Build 16 | 17 | ## Windows 18 | * Pre-built binaries are available under the Releases section on GitLab. 19 | 20 | * If you wish to build the application yourself: 21 | 22 | 1. Download the latest Visual Studio Community Edition and Qt Creator. 23 | 2. git clone https://gitlab.com/CalcProgrammer1/KeyboardVisualizer 24 | 3. git submodule update --init --recursive 25 | 4. Open the KeyboardVisualizer.pro project in Qt Creator. 26 | 5. Use the MSVC compiler kit, either 32- or 64-bit, to build the application. 27 | 6. Run the project from Qt Creator. If you want to use your custom build standalone, download the latest matching Release package and replace the OpenRGB.exe in it with your new build. 28 | 29 | ## Linux 30 | * Pre-built binaries are not currently available for Linux 31 | 32 | * You can build the project using Qt Creator or on the command line. The commands listed here work for Debian-based distros. 33 | 34 | 1. sudo apt install build-essential qtcreator qt5-default libopenal-dev 35 | 2. git clone https://gitlab.com/CalcProgrammer1/KeyboardVisualizer 36 | 3. cd KeyboardVisualizer 37 | 4. git submodule update --init --recursive 38 | 5. qmake KeyboardVisualizer.pro 39 | 6. make -j8 40 | 41 | * Run the application with ./KeyboardVisualizer 42 | 43 | # Settings 44 | 45 | There are many settings you can tweak to make Keyboard Visualizer work with your audio setup and to personalize it to your liking. 46 | 47 | * Amplitude - How reactive the visualizer is to sound. If it is not responding, or is responding with a low level, 48 | increase this value. 49 | * Background Brightness - How bright the background layer is (0-100%) 50 | * Average Size - How many bars of input to combine, increase for a less detailed output that may look better on 51 | devices with low LED counts. 52 | * Decay - How much of the previous value is retained from step to step. Higher values make the visualizer less 53 | flickery but seem slower to react, while low values are fast and flickery. (0-100%) 54 | * Delay - How many milliseconds to wait between each device update. Lower values give a higher "frame rate" 55 | but can cause flicker or other issues on certain setups. 56 | * Normalization Offset - Adjusts amplitude of bass frequencies, increase if your song lacks bass and decrease if 57 | your song is bass heavy 58 | * Normalization Scale - Amount to scale up each consecutive frequency bar over normalization offset, to amplify 59 | higher frequencies. Increase if the right side of the visualizer is low. 60 | * Filter Constant - Amount of signal to blend per update, 0-1 floating point. A value of 1 disables filtering 61 | and updates the spectrum every cycle, a value between 0 and 1 blends the new value with the 62 | previous value, slowing the response. A value of 0 holds the existing value and ignores 63 | new samples. 64 | * FFT Window Mode - Select between None, Hamming, Hanning, and Blackman windowing functions 65 | (see https://en.wikipedia.org/wiki/Window_function for the math behind FFT windowing) 66 | * Background Mode - Select between various static and animated background colors and patterns 67 | * Foreground Mode - Select between various static foreground colors and patterns 68 | * Single Color Mode - Select between various color options for devices that use the single color effect 69 | * Average Mode - Select between discrete bars (binning) or a smoothed line (low pass) output 70 | * Animation Speed - Speed of background and foreground animated patterns, default 100, can be negative 71 | to reverse the direction of the pattern, can be increased to speed up the pattern or 72 | decreased to slow it down. 73 | * Filter Constant - A low pass filter constant between 0 and 1. A value less than 1 slows the response of the 74 | visualization, which makes for a less jumpy, smoother effect the lower the value is. 75 | * Background Timeout - How long to wait (in multiples of 10 milliseconds) before fading the background in. 76 | 77 | # Command Line Options and Settings File 78 | 79 | Keyboard Visualizer allows you to save your custom settings in two different ways. You can pass parameters in on the command line (which is useful for creating shortcuts) or you can save a settings file in the same directory that the .exe is stored called settings.txt. The parameters are as follows: 80 | 81 | Available command line commands: 82 | help - Display this message and quit 83 | startminimized - Start in system tray 84 | 85 | Available command line arguments: 86 | Usage: argument1=value1 argument2=value2 ... 87 | 88 | amplitude - Adjust the amplitude of the visualizer 89 | bkgd_bright - Adjust the background brightness 90 | avg_size - Number of points to average 91 | decay - Percentage of value to decay every step 92 | delay - Milliseconds between each device update 93 | nrml_ofst - Normalization offset, floating point value 94 | nrml_scl - Normalization scale, floating point value 95 | fltr_const - Low pass filter constant, floating point value 0-1 96 | window_mode - FFT windowing mode selector, values are: 97 | - 0: No windowing 98 | - 1: Hanning window 99 | - 2: Hamming window 100 | - 3: Blackman window 101 | bkgd_mode - Background mode, values are: 102 | - 0 Black 103 | - 1 White 104 | - 2 Red 105 | - 3 Orange 106 | - 4 Yellow 107 | - 5 Green 108 | - 6 Cyan 109 | - 7 Blue 110 | - 8 Purple 111 | - 9 Green/Yellow/Red 112 | - 10 Green/White/Red 113 | - 11 Blue/Cyan/White 114 | - 12 Red/White/Blue 115 | - 13 Rainbow Bars 116 | - 14 Rainbow Bars Inverse 117 | - 15 Original 118 | - 16 Rainbow 119 | - 17 Color Wheel 120 | - 18 Spectrum Cycle 121 | frgd_mode - Foreground mode, values are: 122 | - 0 Black 123 | - 1 White 124 | - 2 Red 125 | - 3 Orange 126 | - 4 Yellow 127 | - 5 Green 128 | - 6 Cyan 129 | - 7 Blue 130 | - 8 Purple 131 | - 9 Green/Yellow/Red 132 | - 10 Green/White/Red 133 | - 11 Blue/Cyan/White 134 | - 12 Red/White/Blue 135 | - 13 Rainbow Bars 136 | - 14 Rainbow Bars Inverse 137 | - 15 Original 138 | - 16 Rainbow 139 | - 17 Color Wheel 140 | - 18 Spectrum Cycle 141 | single_color_mode - Single color mode, values are: 142 | - 0 Black 143 | - 1 White 144 | - 2 Red 145 | - 3 Orange 146 | - 4 Yellow 147 | - 5 Green 148 | - 6 Cyan 149 | - 7 Blue 150 | - 8 Purple 151 | - 9 Background 152 | - 10 Follow Background 153 | - 11 Follow Foreground 154 | avg_mode - Visualizer averaging mode 155 | - 0: Binning 156 | - 1: Low-pass filtering 157 | anim_speed - Animation Speed (percent) 158 | server - Configure this instance as a server for synchronization 159 | - Takes what port to serve on as argument, i.e. server=1234 160 | client - Configure this instance as a client for synchronization 161 | - Takes the IP/hostname of the server and port as arguments, 162 | - i.e. client=192.168.1.100,1234 163 | 164 | # Available Visual Effects 165 | 166 | * Spectrograph - Shows vertical bars for each frequency in the audio 167 | * Bar - Shows a single bar representing bass frequencies 168 | * Single Color - Brightness and color represent strength of bass frequencies 169 | 170 | # Donations 171 | 172 | * I created this project for fun mostly, and Razer's support of sending free products for me to integrate is awesome, but several people have asked if I'd accept donations. I've gotten back into cryptocurrency and mining recently so if you want to send some Bitcoin, Dogecoin, or Ethereum my way you can do so with the addresses below. I don't want to bother with PayPal and its ugly fees and I detest online advertising and block all ads on my PCs so I would never put ads on my videos or programs. 173 | 174 | * Bitcoin: 1N83YPu7btXYadPS1neB9zX7X1QTdpyZQ 175 | 176 | * Dogecoin: D7nqJZ3dyfCMRvS6J5jpoxerpteUG5z7Pm 177 | 178 | * Ethereum: 0xb5023019E6789af4887aED2056E471DF0f4c8e93 179 | 180 | * Monero: 44p8QfKTHxA65PeY1HabFsaZnayVu36h2eNHsTVcHzg5EeUDszmK91aTaAc4otCRdL4644gMzatb85bWyZB7vfK78siS962 181 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # swy: some useful references; the MSVC part of the CI script is based on the one from bind9, by Michał Kępień: 2 | # https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/gcp/windows-containers/blob/master/cookbooks/preinstalled-software/README.md 3 | # https://gitlab.isc.org/isc-projects/bind9/commit/facc6a051fcac70fbbc61cb92a37be8c3e4db5ec#587d266bb27a4dc3022bbed44dfa19849df3044c_718_731 4 | # https://www.kittell.net/code/powershell-unix-sed-equivalent-change-text-file/ 5 | # https://powershell.org/forums/topic/how-to-use-ansi-vt100-formatting-in-powershell-ooh-pretty-colors/ 6 | 7 | #-----------------------------------------------------------------------# 8 | # OpenRGB GitLab CI Configuration # 9 | #-----------------------------------------------------------------------# 10 | variables: 11 | GIT_SUBMODULE_STRATEGY: recursive 12 | 13 | .shared_windows_runners: 14 | tags: 15 | - shared-windows 16 | - windows 17 | - windows-1809 18 | 19 | stages: 20 | - build 21 | 22 | before_script: 23 | - echo "started by ${GITLAB_USER_NAME}" 24 | 25 | #reusable templates 26 | .ccache_init: &ccache_init 27 | before_script: 28 | - apt update 29 | - apt install -y build-essential qtcreator qt5-default libopenal-dev pkgconf wget git 30 | 31 | #-----------------------------------------------------------------------# 32 | # Linux (AppImage) 32-bit Build Target # 33 | #-----------------------------------------------------------------------# 34 | build_linux_32: 35 | <<: *ccache_init 36 | image: i386/ubuntu:bionic 37 | stage: build 38 | script: 39 | - export $(dpkg-architecture) 40 | - ./scripts/build-appimage.sh 41 | 42 | artifacts: 43 | paths: 44 | - Keyboard_Visualizer-i386.AppImage 45 | expire_in: 30 days 46 | 47 | #-----------------------------------------------------------------------# 48 | # Linux (AppImage) 64-bit Build Target # 49 | #-----------------------------------------------------------------------# 50 | build_linux_64: 51 | <<: *ccache_init 52 | image: ubuntu:bionic 53 | stage: build 54 | script: 55 | - export $(dpkg-architecture) 56 | - ./scripts/build-appimage.sh 57 | 58 | artifacts: 59 | paths: 60 | - Keyboard_Visualizer-x86_64.AppImage 61 | expire_in: 30 days 62 | 63 | #-----------------------------------------------------------------------# 64 | # Linux (.deb) 32-bit Build Target # 65 | #-----------------------------------------------------------------------# 66 | build_linux_deb32: 67 | <<: *ccache_init 68 | image: i386/ubuntu:bionic 69 | stage: build 70 | script: 71 | - apt install -y debhelper 72 | - dpkg-architecture -l 73 | - dpkg-buildpackage --target-arch i386 -us -B 74 | - rm -v ../keyboardvisualizer-dbgsym*.ddeb 75 | - mv -v ../keyboardvisualizer*.deb ./ 76 | 77 | artifacts: 78 | paths: 79 | - keyboardvisualizer*.deb 80 | exclude: 81 | - keyboardvisualizer-dbgsym*.deb 82 | expire_in: 30 days 83 | 84 | #-----------------------------------------------------------------------# 85 | # Linux (.deb) 64-bit Build Target # 86 | #-----------------------------------------------------------------------# 87 | build_linux_deb64: 88 | <<: *ccache_init 89 | image: ubuntu:bionic 90 | stage: build 91 | script: 92 | - apt install -y debhelper 93 | - dpkg-architecture -l 94 | - dpkg-buildpackage -us -B 95 | - rm -v ../keyboardvisualizer-dbgsym*.ddeb 96 | - mv -v ../keyboardvisualizer*.deb ./ 97 | 98 | artifacts: 99 | paths: 100 | - keyboardvisualizer*.deb 101 | exclude: 102 | - keyboardvisualizer-dbgsym*.deb 103 | expire_in: 30 days 104 | 105 | #-----------------------------------------------------------------------# 106 | # Windows (32-bit) Build Target # 107 | #-----------------------------------------------------------------------# 108 | build_windows_32: 109 | extends: 110 | - .shared_windows_runners 111 | stage: build 112 | script: 113 | - $esc = "$([char]27)" 114 | - $count = 0 115 | - function _unix_tmsec_ { [int64](([datetime]::UtcNow)-(get-date "1/1/1970")).TotalSeconds } 116 | - function _fold_start_ { param( [string]$TEXT_TAG ) $t=_unix_tmsec_; $global:count += 1; Write-Host -NoNewLine "`r`n`r`nsection_start:${t}:sect_${count}`r${esc}[0K${esc}[33m${TEXT_TAG}${esc}[39m`r`n"; } 117 | - function _fold_final_ { $t=_unix_tmsec_; Write-Host -NoNewLine "`r`n`r`nsection_end:${t}:sect_${count}`r${esc}[0K`r`n" ; } 118 | 119 | 120 | - _fold_start_ 'configuring the msvc environment variables' 121 | - Push-Location "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Auxiliary/Build" 122 | - '& cmd.exe /C "vcvarsall.bat x86 & set" | Foreach-Object { if ($_ -match "(.*?)=(.*)") { Set-Item -force -path "Env:\$($matches[1])" -value "$($matches[2])" } }' 123 | - Pop-Location 124 | - _fold_final_ 125 | 126 | - _fold_start_ 'downloading precompiled versions of qtbase, qttools (for windeployqt) and jom (for a more parallel nmake)' 127 | - mkdir _qt 128 | - mkdir _qt_download 129 | - Push-Location _qt_download 130 | - curl.exe -LJ -o qt-base.7z 'https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/qt5_5150/qt.qt5.5150.win32_msvc2019/5.15.0-0-202005150700qtbase-Windows-Windows_10-MSVC2019-Windows-Windows_10-X86.7z' 131 | - curl.exe -LJ -o qt-tools.7z 'https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/qt5_5150/qt.qt5.5150.win32_msvc2019/5.15.0-0-202005150700qttools-Windows-Windows_10-MSVC2019-Windows-Windows_10-X86.7z' 132 | - curl.exe -LJ -o qt-jom.zip 'https://download.qt.io/official_releases/jom/jom.zip' 133 | - _fold_final_ 134 | 135 | - _fold_start_ 'extracting the downloaded qt binaries' 136 | - 7z x qt-base.7z '-o../_qt' -y 137 | - 7z x qt-tools.7z '-o../_qt' -y 138 | - 7z x qt-jom.zip '-o../_qt' -y 139 | - _fold_final_ 140 | 141 | - _fold_start_ 'turn the qt install from enterprise to foss; remove the licensing checks' 142 | - ${qconfig-pri-folder} = '..\_qt\5.15.0\msvc2019\mkspecs\qconfig.pri' 143 | - (Get-Content ${qconfig-pri-folder}).replace('QT_EDITION = Enterprise', 'QT_EDITION = OpenSource') | Set-Content ${qconfig-pri-folder} 144 | - (Get-Content ${qconfig-pri-folder}).replace('QT_LICHECK = licheck.exe', '') | Set-Content ${qconfig-pri-folder} 145 | - Pop-Location 146 | - _fold_final_ 147 | 148 | - _fold_start_ 'run qmake and generate the msvc nmake makefile' 149 | - mkdir _build; cd _build 150 | - ..\_qt\5.15.0\msvc2019\bin\qmake ..\KeyboardVisualizer.pro 151 | - _fold_final_ 152 | 153 | - _fold_start_ 'start the actual build with jom instead of nmake; for speed' 154 | - ..\_qt\jom 155 | - _fold_final_ 156 | 157 | - _fold_start_ 'run windeployqt to automatically copy the needed dll files' 158 | - ..\_qt\5.15.0\msvc2019\bin\windeployqt --no-angle --no-translations --no-opengl-sw --no-system-d3d-compiler --no-compiler-runtime --no-webkit2 .\release\ 159 | - _fold_final_ 160 | 161 | - _fold_start_ 'compressing the release folder so that we can upload it as artifact' 162 | - mv release 'Keyboard Visualizer Windows 32-bit' 163 | - ${datetime} = Get-Date ([datetime]::UtcNow) -Format "yyyy-MM-ddTHH-mm-ss" 164 | - ${revision} = ((git rev-parse --short HEAD) | Out-String).Trim() 165 | - ${rversion} = (((Get-Content '..\KeyboardVisualizer.pro' | Select-String -Pattern "VERSION =") | Out-String).Trim().Split("="))[1].Trim() 166 | - 7z a -mx9 -r -y "KeyboardVisualizer_${rversion}_32_${revision}_nightly_${datetime}.7z" 'Keyboard Visualizer Windows 32-bit' 167 | - _fold_final_ 168 | # cache: 169 | # key: same-key 170 | # paths: 171 | # - C:\vcpkg\installed\ 172 | artifacts: 173 | paths: 174 | - _build/*.7z 175 | expire_in: 30 days 176 | 177 | #-----------------------------------------------------------------------# 178 | # Windows (64-bit) Build Target # 179 | #-----------------------------------------------------------------------# 180 | build_windows_64: 181 | extends: 182 | - .shared_windows_runners 183 | stage: build 184 | script: 185 | - $esc = "$([char]27)" 186 | - $count = 0 187 | - function _unix_tmsec_ { [int64](([datetime]::UtcNow)-(get-date "1/1/1970")).TotalSeconds } 188 | - function _fold_start_ { param( [string]$TEXT_TAG ) $t=_unix_tmsec_; $global:count += 1; Write-Host -NoNewLine "`r`n`r`nsection_start:${t}:sect_${count}`r${esc}[0K${esc}[33m${TEXT_TAG}${esc}[39m`r`n"; } 189 | - function _fold_final_ { $t=_unix_tmsec_; Write-Host -NoNewLine "`r`n`r`nsection_end:${t}:sect_${count}`r${esc}[0K`r`n" ; } 190 | 191 | 192 | - _fold_start_ 'configuring the msvc environment variables' 193 | - Push-Location "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Auxiliary/Build" 194 | - '& cmd.exe /C "vcvarsall.bat x64 & set" | Foreach-Object { if ($_ -match "(.*?)=(.*)") { Set-Item -force -path "Env:\$($matches[1])" -value "$($matches[2])" } }' 195 | - Pop-Location 196 | - _fold_final_ 197 | 198 | - _fold_start_ 'downloading precompiled versions of qtbase, qttools (for windeployqt) and jom (for a more parallel nmake)' 199 | - mkdir _qt 200 | - mkdir _qt_download 201 | - Push-Location _qt_download 202 | - curl.exe -LJ -o qt-base.7z 'https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/qt5_5150/qt.qt5.5150.win64_msvc2019_64/5.15.0-0-202005150700qtbase-Windows-Windows_10-MSVC2019-Windows-Windows_10-X86_64.7z' 203 | - curl.exe -LJ -o qt-tools.7z 'https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/qt5_5150/qt.qt5.5150.win64_msvc2019_64/5.15.0-0-202005150700qttools-Windows-Windows_10-MSVC2019-Windows-Windows_10-X86_64.7z' 204 | - curl.exe -LJ -o qt-jom.zip 'https://download.qt.io/official_releases/jom/jom.zip' 205 | - _fold_final_ 206 | 207 | - _fold_start_ 'extracting the downloaded qt binaries' 208 | - 7z x qt-base.7z '-o../_qt' -y 209 | - 7z x qt-tools.7z '-o../_qt' -y 210 | - 7z x qt-jom.zip '-o../_qt' -y 211 | - _fold_final_ 212 | 213 | - _fold_start_ 'turn the qt install from enterprise to foss; remove the licensing checks' 214 | - ${qconfig-pri-folder} = '..\_qt\5.15.0\msvc2019_64\mkspecs\qconfig.pri' 215 | - (Get-Content ${qconfig-pri-folder}).replace('QT_EDITION = Enterprise', 'QT_EDITION = OpenSource') | Set-Content ${qconfig-pri-folder} 216 | - (Get-Content ${qconfig-pri-folder}).replace('QT_LICHECK = licheck.exe', '') | Set-Content ${qconfig-pri-folder} 217 | - Pop-Location 218 | - _fold_final_ 219 | 220 | - _fold_start_ 'run qmake and generate the msvc nmake makefile' 221 | - mkdir _build; cd _build 222 | - ..\_qt\5.15.0\msvc2019_64\bin\qmake ..\KeyboardVisualizer.pro 223 | - _fold_final_ 224 | 225 | - _fold_start_ 'start the actual build with jom instead of nmake; for speed' 226 | - ..\_qt\jom 227 | - _fold_final_ 228 | 229 | - _fold_start_ 'run windeployqt to automatically copy the needed dll files' 230 | - ..\_qt\5.15.0\msvc2019_64\bin\windeployqt --no-angle --no-translations --no-opengl-sw --no-system-d3d-compiler --no-compiler-runtime --no-webkit2 .\release\ 231 | - _fold_final_ 232 | 233 | - _fold_start_ 'compressing the release folder so that we can upload it as artifact' 234 | - mv release 'Keyboard Visualizer Windows 64-bit' 235 | - ${datetime} = Get-Date ([datetime]::UtcNow) -Format "yyyy-MM-ddTHH-mm-ss" 236 | - ${revision} = ((git rev-parse --short HEAD) | Out-String).Trim() 237 | - ${rversion} = (((Get-Content '..\KeyboardVisualizer.pro' | Select-String -Pattern "VERSION =") | Out-String).Trim().Split("="))[1].Trim() 238 | - 7z a -mx9 -r -y "KeyboardVisualizer_${rversion}_64_${revision}_nightly_${datetime}.7z" 'Keyboard Visualizer Windows 64-bit' 239 | - _fold_final_ 240 | # cache: 241 | # key: same-key 242 | # paths: 243 | # - C:\vcpkg\installed\ 244 | artifacts: 245 | paths: 246 | - _build/*.7z 247 | expire_in: 30 days 248 | 249 | #-----------------------------------------------------------------------# 250 | # MacOS Build Target # 251 | #-----------------------------------------------------------------------# 252 | build_macos_arm64: 253 | tags: 254 | - macos 255 | stage: build 256 | script: 257 | - eval $(/opt/homebrew/bin/brew shellenv) 258 | - qmake KeyboardVisualizer.pro 259 | - make -j16 260 | - macdeployqt KeyboardVisualizer.app -codesign=OpenRGB 261 | 262 | artifacts: 263 | paths: 264 | - KeyboardVisualizer.app 265 | expire_in: 30 days 266 | 267 | rules: 268 | - if: '$CI_PROJECT_PATH == "CalcProgrammer1/KeyboardVisualizer"' 269 | when: on_success 270 | - if: '$BUILD_MACOS =~ /.+/' 271 | when: on_success 272 | 273 | build_macos_intel: 274 | tags: 275 | - macos 276 | stage: build 277 | script: 278 | - eval $(/usr/local/bin/brew shellenv) 279 | - arch -x86_64 /usr/local/bin/qmake KeyboardVisualizer.pro 280 | - arch -x86_64 make -j16 281 | - arch -x86_64 macdeployqt KeyboardVisualizer.app -codesign=OpenRGB 282 | 283 | artifacts: 284 | paths: 285 | - KeyboardVisualizer.app 286 | expire_in: 30 days 287 | 288 | rules: 289 | - if: '$CI_PROJECT_PATH == "CalcProgrammer1/KeyboardVisualizer"' 290 | when: on_success 291 | - if: '$BUILD_MACOS =~ /.+/' 292 | when: on_success -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | OpenAuraSDK 294 | Copyright (C) 2019 Adam Honse 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /KeyboardVisualizerQT/KeyboardVisDlg.cpp: -------------------------------------------------------------------------------- 1 | #include "KeyboardVisDlg.h" 2 | #include "KeyboardVisualizerCommon/Visualizer.h" 3 | #include "KeyboardVisualizerCommon/VisualizerDefines.h" 4 | #include "ui_keyboardvisualizer.h" 5 | 6 | #include 7 | #include 8 | 9 | Visualizer* vis_ptr; 10 | boolean startminimized; 11 | boolean firstrun; 12 | 13 | using namespace Ui; 14 | 15 | static void UpdateOpenRGBClientListCallback(void * this_ptr) 16 | { 17 | KeyboardVisDlg * this_obj = (KeyboardVisDlg *)this_ptr; 18 | 19 | QMetaObject::invokeMethod(this_obj, "UpdateOpenRGBClientList", Qt::QueuedConnection); 20 | } 21 | 22 | KeyboardVisDlg::KeyboardVisDlg(QWidget *parent) : QMainWindow(parent), ui(new KeyboardVisualizerDlg) 23 | { 24 | startminimized = false; 25 | firstrun = true; 26 | 27 | ui->setupUi(this); 28 | 29 | std::string titleString = "Keyboard Visualizer "; 30 | titleString.append(VERSION_STRING); 31 | setWindowTitle(titleString.c_str()); 32 | 33 | QIcon icon(":KeyboardVisualizer.png"); 34 | setWindowIcon(icon); 35 | 36 | QAction* actionExit = new QAction( "Show/Hide", this ); 37 | connect( actionExit, SIGNAL( triggered() ), this, SLOT( show_hide() )); 38 | 39 | QMenu* myTrayIconMenu = new QMenu( this ); 40 | myTrayIconMenu->addAction( actionExit ); 41 | 42 | trayIcon = new QSystemTrayIcon(this); 43 | trayIcon->setIcon(icon); 44 | trayIcon->setToolTip("Keyboard Visualizer"); 45 | trayIcon->setContextMenu(myTrayIconMenu); 46 | trayIcon->show(); 47 | 48 | ui->lineEdit_IP->setText("127.0.0.1"); 49 | ui->lineEdit_Port->setText(QString::number(OPENRGB_SDK_PORT)); 50 | } 51 | 52 | void KeyboardVisDlg::show() 53 | { 54 | QMainWindow::show(); 55 | } 56 | 57 | KeyboardVisDlg::~KeyboardVisDlg() 58 | { 59 | delete ui; 60 | } 61 | 62 | void KeyboardVisDlg::show_hide() 63 | { 64 | if(isHidden()) 65 | { 66 | timer->start(15); 67 | show(); 68 | } 69 | else 70 | { 71 | hide(); 72 | timer->stop(); 73 | } 74 | } 75 | 76 | void KeyboardVisDlg::update() 77 | { 78 | if (startminimized && firstrun) 79 | { 80 | firstrun = false; 81 | hide(); 82 | timer->stop(); 83 | } 84 | 85 | for(int x = 0; x < 256; x++) 86 | { 87 | for(int y = 0; y < 64; y++) 88 | { 89 | COLORREF input = vis_ptr->pixels_out->pixels[y][x]; 90 | COLORREF bgr = RGB(GetBValue(input), GetGValue(input), GetRValue(input)); 91 | image->setPixel(x, y, bgr); 92 | } 93 | } 94 | 95 | pixmap.convertFromImage(*image); 96 | scene->clear(); 97 | scene->addPixmap(pixmap); 98 | 99 | ui->graphicsView_Visualization_Preview->show(); 100 | 101 | if(vis_ptr->update_ui) 102 | { 103 | vis_ptr->update_ui = false; 104 | 105 | ui->lineEdit_Normalization_Offset->setText(QString::number(vis_ptr->nrml_ofst)); 106 | ui->lineEdit_Normalization_Scale->setText(QString::number(vis_ptr->nrml_scl)); 107 | ui->lineEdit_Animation_Speed->setText(QString::number(vis_ptr->anim_speed)); 108 | ui->lineEdit_Filter_Constant->setText(QString::number(vis_ptr->filter_constant)); 109 | 110 | ui->lineEdit_Amplitude->setText(QString::number(vis_ptr->amplitude)); 111 | ui->lineEdit_Background_Brightness->setText(QString::number(vis_ptr->bkgd_bright)); 112 | ui->lineEdit_Average_Size->setText(QString::number(vis_ptr->avg_size)); 113 | ui->lineEdit_Decay->setText(QString::number(vis_ptr->decay)); 114 | ui->lineEdit_Delay->setText(QString::number(vis_ptr->delay)); 115 | ui->lineEdit_Background_Timeout->setText(QString::number(vis_ptr->background_timeout)); 116 | 117 | ui->comboBox_Average_Mode->blockSignals(true); 118 | ui->comboBox_Average_Mode->setCurrentIndex(vis_ptr->avg_mode); 119 | ui->comboBox_Average_Mode->blockSignals(false); 120 | 121 | ui->comboBox_Background_Mode->blockSignals(true); 122 | ui->comboBox_Background_Mode->setCurrentIndex(vis_ptr->bkgd_mode); 123 | ui->comboBox_Background_Mode->blockSignals(false); 124 | 125 | ui->comboBox_FFT_Window_Mode->blockSignals(true); 126 | ui->comboBox_FFT_Window_Mode->setCurrentIndex(vis_ptr->window_mode); 127 | ui->comboBox_FFT_Window_Mode->blockSignals(false); 128 | 129 | ui->comboBox_Foreground_Mode->blockSignals(true); 130 | ui->comboBox_Foreground_Mode->setCurrentIndex(vis_ptr->frgd_mode); 131 | ui->comboBox_Foreground_Mode->blockSignals(false); 132 | 133 | ui->comboBox_Single_Color_Mode->blockSignals(true); 134 | ui->comboBox_Single_Color_Mode->setCurrentIndex(vis_ptr->single_color_mode); 135 | ui->comboBox_Single_Color_Mode->blockSignals(false); 136 | 137 | ui->checkBox_Reactive_Background->setChecked(vis_ptr->reactive_bkgd); 138 | ui->checkBox_Silent_Background->setChecked(vis_ptr->silent_bkgd); 139 | } 140 | } 141 | 142 | void KeyboardVisDlg::SetVisualizer(Visualizer* v) 143 | { 144 | vis_ptr = v; 145 | 146 | vis_ptr->RegisterClientInfoChangeCallback(UpdateOpenRGBClientListCallback, this); 147 | 148 | ui->lineEdit_Normalization_Offset->setText(QString::number(vis_ptr->nrml_ofst)); 149 | ui->lineEdit_Normalization_Scale->setText(QString::number(vis_ptr->nrml_scl)); 150 | ui->lineEdit_Animation_Speed->setText(QString::number(vis_ptr->anim_speed)); 151 | ui->lineEdit_Filter_Constant->setText(QString::number(vis_ptr->filter_constant)); 152 | 153 | ui->lineEdit_Amplitude->setText(QString::number(vis_ptr->amplitude)); 154 | ui->lineEdit_Background_Brightness->setText(QString::number(vis_ptr->bkgd_bright)); 155 | ui->lineEdit_Average_Size->setText(QString::number(vis_ptr->avg_size)); 156 | ui->lineEdit_Decay->setText(QString::number(vis_ptr->decay)); 157 | ui->lineEdit_Delay->setText(QString::number(vis_ptr->delay)); 158 | ui->lineEdit_Background_Timeout->setText(QString::number(vis_ptr->background_timeout)); 159 | 160 | ui->comboBox_FFT_Window_Mode->blockSignals(true); 161 | ui->comboBox_FFT_Window_Mode->addItem("None"); 162 | ui->comboBox_FFT_Window_Mode->addItem("Hanning"); 163 | ui->comboBox_FFT_Window_Mode->addItem("Hamming"); 164 | ui->comboBox_FFT_Window_Mode->addItem("Blackman"); 165 | ui->comboBox_FFT_Window_Mode->setCurrentIndex(vis_ptr->window_mode); 166 | ui->comboBox_FFT_Window_Mode->blockSignals(false); 167 | 168 | ui->comboBox_Background_Mode->blockSignals(true); 169 | for(int i = 0; i < VISUALIZER_NUM_PATTERNS; i++) 170 | { 171 | ui->comboBox_Background_Mode->addItem(visualizer_pattern_labels[i]); 172 | } 173 | ui->comboBox_Background_Mode->setCurrentIndex(vis_ptr->bkgd_mode); 174 | ui->comboBox_Background_Mode->blockSignals(false); 175 | 176 | ui->comboBox_Foreground_Mode->blockSignals(true); 177 | for(int i = 0; i < VISUALIZER_NUM_PATTERNS; i++) 178 | { 179 | ui->comboBox_Foreground_Mode->addItem(visualizer_pattern_labels[i]); 180 | } 181 | ui->comboBox_Foreground_Mode->setCurrentIndex(vis_ptr->frgd_mode); 182 | ui->comboBox_Foreground_Mode->blockSignals(false); 183 | 184 | ui->comboBox_Single_Color_Mode->blockSignals(true); 185 | for(int i = 0; i < VISUALIZER_NUM_SINGLE_COLOR; i++) 186 | { 187 | ui->comboBox_Single_Color_Mode->addItem(visualizer_single_color_labels[i]); 188 | } 189 | ui->comboBox_Single_Color_Mode->setCurrentIndex(vis_ptr->single_color_mode); 190 | ui->comboBox_Single_Color_Mode->blockSignals(false); 191 | 192 | ui->comboBox_Average_Mode->blockSignals(true); 193 | ui->comboBox_Average_Mode->addItem("Binning"); 194 | ui->comboBox_Average_Mode->addItem("Low Pass"); 195 | ui->comboBox_Average_Mode->setCurrentIndex(vis_ptr->avg_mode); 196 | ui->comboBox_Average_Mode->blockSignals(false); 197 | 198 | ui->comboBox_Audio_Device->blockSignals(true); 199 | for(unsigned int i = 0; i < vis_ptr->audio_devices.size(); i++) 200 | { 201 | ui->comboBox_Audio_Device->addItem(vis_ptr->audio_devices[i]); 202 | } 203 | ui->comboBox_Audio_Device->setCurrentIndex(vis_ptr->audio_device_idx); 204 | ui->comboBox_Audio_Device->blockSignals(false); 205 | 206 | ui->checkBox_Reactive_Background->setChecked(vis_ptr->reactive_bkgd); 207 | ui->checkBox_Silent_Background->setChecked(vis_ptr->silent_bkgd); 208 | 209 | timer = new QTimer(this); 210 | connect(timer, SIGNAL(timeout()), this, SLOT(update())); 211 | timer->start(15); 212 | 213 | image = new QImage(256, 64, QImage::Format_RGB32); 214 | scene = new QGraphicsScene(this); 215 | ui->graphicsView_Visualization_Preview->setScene(scene); 216 | } 217 | 218 | void KeyboardVisDlg::StartMinimized(boolean startmin) 219 | { 220 | startminimized = startmin; 221 | } 222 | 223 | void KeyboardVisDlg::on_lineEdit_Amplitude_textChanged(const QString &arg1) 224 | { 225 | vis_ptr->amplitude = arg1.toInt(); 226 | vis_ptr->OnSettingsChanged(); 227 | } 228 | 229 | void KeyboardVisDlg::on_lineEdit_Background_Brightness_textChanged(const QString &arg1) 230 | { 231 | vis_ptr->bkgd_bright = arg1.toInt(); 232 | vis_ptr->OnSettingsChanged(); 233 | } 234 | 235 | void Ui::KeyboardVisDlg::on_lineEdit_Average_Size_textChanged(const QString &arg1) 236 | { 237 | vis_ptr->avg_size = arg1.toInt(); 238 | vis_ptr->OnSettingsChanged(); 239 | } 240 | 241 | void Ui::KeyboardVisDlg::on_lineEdit_Decay_textChanged(const QString &arg1) 242 | { 243 | vis_ptr->decay = arg1.toInt(); 244 | vis_ptr->OnSettingsChanged(); 245 | } 246 | 247 | void Ui::KeyboardVisDlg::on_lineEdit_Delay_textChanged(const QString &arg1) 248 | { 249 | vis_ptr->delay = arg1.toInt(); 250 | vis_ptr->OnSettingsChanged(); 251 | } 252 | 253 | void Ui::KeyboardVisDlg::on_lineEdit_Normalization_Offset_textChanged(const QString &arg1) 254 | { 255 | vis_ptr->nrml_ofst = arg1.toFloat(); 256 | vis_ptr->SetNormalization(vis_ptr->nrml_ofst, vis_ptr->nrml_scl); 257 | vis_ptr->OnSettingsChanged(); 258 | } 259 | 260 | void Ui::KeyboardVisDlg::on_lineEdit_Normalization_Scale_textChanged(const QString &arg1) 261 | { 262 | vis_ptr->nrml_scl = arg1.toFloat(); 263 | vis_ptr->SetNormalization(vis_ptr->nrml_ofst, vis_ptr->nrml_scl); 264 | vis_ptr->OnSettingsChanged(); 265 | } 266 | 267 | void Ui::KeyboardVisDlg::on_lineEdit_Animation_Speed_textChanged(const QString &arg1) 268 | { 269 | vis_ptr->anim_speed = arg1.toFloat(); 270 | vis_ptr->OnSettingsChanged(); 271 | } 272 | 273 | void Ui::KeyboardVisDlg::on_comboBox_FFT_Window_Mode_currentIndexChanged(int index) 274 | { 275 | vis_ptr->window_mode = index; 276 | vis_ptr->OnSettingsChanged(); 277 | } 278 | 279 | void Ui::KeyboardVisDlg::on_comboBox_Background_Mode_currentIndexChanged(int index) 280 | { 281 | vis_ptr->bkgd_mode = index; 282 | vis_ptr->OnSettingsChanged(); 283 | } 284 | 285 | void Ui::KeyboardVisDlg::on_comboBox_Foreground_Mode_currentIndexChanged(int index) 286 | { 287 | vis_ptr->frgd_mode = index; 288 | vis_ptr->OnSettingsChanged(); 289 | } 290 | 291 | void Ui::KeyboardVisDlg::on_comboBox_Single_Color_Mode_currentIndexChanged(int index) 292 | { 293 | vis_ptr->single_color_mode = index; 294 | vis_ptr->OnSettingsChanged(); 295 | } 296 | 297 | void Ui::KeyboardVisDlg::on_comboBox_Average_Mode_currentIndexChanged(int index) 298 | { 299 | vis_ptr->avg_mode = index; 300 | vis_ptr->OnSettingsChanged(); 301 | } 302 | 303 | void Ui::KeyboardVisDlg::on_pushButton_Save_Settings_clicked() 304 | { 305 | vis_ptr->SaveSettings(); 306 | } 307 | 308 | void Ui::KeyboardVisDlg::on_checkBox_Reactive_Background_clicked(bool checked) 309 | { 310 | vis_ptr->reactive_bkgd = checked; 311 | 312 | if (vis_ptr->reactive_bkgd == true) 313 | { 314 | vis_ptr->silent_bkgd = false; 315 | ui->checkBox_Silent_Background->setChecked(false); 316 | } 317 | vis_ptr->OnSettingsChanged(); 318 | } 319 | 320 | void Ui::KeyboardVisDlg::on_comboBox_Audio_Device_currentIndexChanged(int index) 321 | { 322 | if((unsigned int)index != vis_ptr->audio_device_idx) 323 | { 324 | vis_ptr->audio_device_idx = index; 325 | vis_ptr->ChangeAudioDevice(); 326 | } 327 | } 328 | 329 | void Ui::KeyboardVisDlg::on_lineEdit_Filter_Constant_textChanged(const QString &arg1) 330 | { 331 | vis_ptr->filter_constant = arg1.toFloat(); 332 | if(vis_ptr->filter_constant > 1.0f) 333 | { 334 | vis_ptr->filter_constant = 1.0f; 335 | } 336 | else if(vis_ptr->filter_constant < 0.0f) 337 | { 338 | vis_ptr->filter_constant = 0.0f; 339 | } 340 | vis_ptr->OnSettingsChanged(); 341 | } 342 | 343 | void Ui::KeyboardVisDlg::on_checkBox_Silent_Background_clicked(bool checked) 344 | { 345 | vis_ptr->silent_bkgd = checked; 346 | 347 | if (vis_ptr->silent_bkgd == true) 348 | { 349 | vis_ptr->reactive_bkgd = false; 350 | ui->checkBox_Reactive_Background->setChecked(false); 351 | } 352 | 353 | vis_ptr->OnSettingsChanged(); 354 | } 355 | 356 | void Ui::KeyboardVisDlg::on_lineEdit_Background_Timeout_textChanged(const QString &arg1) 357 | { 358 | vis_ptr->background_timeout = arg1.toInt(); 359 | 360 | if (vis_ptr->update_ui == false) 361 | { 362 | vis_ptr->background_timer = 0; 363 | } 364 | 365 | vis_ptr->OnSettingsChanged(); 366 | } 367 | 368 | class NetworkClientPointer : public QObject 369 | { 370 | public: 371 | NetworkClient * net_client; 372 | }; 373 | 374 | class EnableCheckboxArg : public QObject 375 | { 376 | public: 377 | ControllerSettingsType * settings_ptr; 378 | }; 379 | 380 | void Ui::KeyboardVisDlg::UpdateOpenRGBClientList() 381 | { 382 | ui->tree_Devices->clear(); 383 | 384 | //OpenRGB device list 385 | ui->tree_Devices->setColumnCount(2); 386 | ui->tree_Devices->header()->setStretchLastSection(false); 387 | ui->tree_Devices->header()->setSectionResizeMode(0, QHeaderView::Stretch); 388 | ui->tree_Devices->setColumnWidth(1, 100); 389 | ui->tree_Devices->setHeaderLabels(QStringList() << "Connected Clients" << ""); 390 | 391 | /*-----------------------------------------------------*\ 392 | | Create a signal mapper for Disconnect buttons | 393 | \*-----------------------------------------------------*/ 394 | QSignalMapper* signalMapper = new QSignalMapper(this); 395 | connect(signalMapper, SIGNAL(mapped(QObject *)), this, SLOT(on_button_Disconnect_clicked(QObject *))); 396 | 397 | /*-----------------------------------------------------*\ 398 | | Create a signal mapper for Enabled checkboxes | 399 | \*-----------------------------------------------------*/ 400 | QSignalMapper* checkMapper = new QSignalMapper(this); 401 | connect(checkMapper, SIGNAL(mapped(QObject *)), this, SLOT(on_button_Enabled_clicked(QObject *))); 402 | 403 | /*-----------------------------------------------------*\ 404 | | Loop through all active clients and populate list | 405 | \*-----------------------------------------------------*/ 406 | for(int client_idx = 0; client_idx < vis_ptr->rgb_clients.size(); client_idx++) 407 | { 408 | QTreeWidgetItem* new_top_item = new QTreeWidgetItem(ui->tree_Devices); 409 | new_top_item->setText(0, QString::fromStdString(vis_ptr->rgb_clients[client_idx]->GetIP())); 410 | 411 | /*-----------------------------------------------------*\ 412 | | Create a Disconnect button for this client and map it | 413 | \*-----------------------------------------------------*/ 414 | QPushButton* new_button = new QPushButton( "Disconnect" ); 415 | ui->tree_Devices->setItemWidget(new_top_item, 1, new_button); 416 | 417 | connect(new_button, SIGNAL(clicked()), signalMapper, SLOT(map())); 418 | 419 | NetworkClientPointer * new_arg = new NetworkClientPointer(); 420 | new_arg->net_client = vis_ptr->rgb_clients[client_idx]; 421 | 422 | signalMapper->setMapping(new_button, new_arg); 423 | 424 | /*-----------------------------------------------------*\ 425 | | Loop through all devices on this client | 426 | \*-----------------------------------------------------*/ 427 | for(int controller_idx = 0; controller_idx < vis_ptr->rgb_clients[client_idx]->server_controllers.size(); controller_idx++) 428 | { 429 | QTreeWidgetItem* new_item = new QTreeWidgetItem(new_top_item); 430 | new_item->setText(0, QString::fromStdString(vis_ptr->rgb_clients[client_idx]->server_controllers[controller_idx]->name)); 431 | 432 | /*-----------------------------------------------------*\ 433 | | Create an Enabled checkbox for this device and map it | 434 | \*-----------------------------------------------------*/ 435 | QCheckBox* new_check = new QCheckBox( "Enabled" ); 436 | ui->tree_Devices->setItemWidget(new_item, 1, new_check); 437 | 438 | if(controller_idx < vis_ptr->rgb_client_settings[client_idx]->controller_settings.size()) 439 | { 440 | new_check->setChecked(vis_ptr->rgb_client_settings[client_idx]->controller_settings[controller_idx]->enabled); 441 | 442 | connect(new_check, SIGNAL(clicked()), checkMapper, SLOT(map())); 443 | EnableCheckboxArg * check_arg = new EnableCheckboxArg(); 444 | check_arg->settings_ptr = vis_ptr->rgb_client_settings[client_idx]->controller_settings[controller_idx]; 445 | checkMapper->setMapping(new_check, check_arg); 446 | } 447 | 448 | /*-----------------------------------------------------*\ 449 | | Loop through all zones on this device | 450 | \*-----------------------------------------------------*/ 451 | for(int zone_idx = 0; zone_idx < vis_ptr->rgb_clients[client_idx]->server_controllers[controller_idx]->zones.size(); zone_idx++) 452 | { 453 | QTreeWidgetItem* new_child = new QTreeWidgetItem(); 454 | 455 | std::string zone_str = vis_ptr->rgb_clients[client_idx]->server_controllers[controller_idx]->zones[zone_idx].name + ", "; 456 | zone_str.append(std::to_string(vis_ptr->rgb_clients[client_idx]->server_controllers[controller_idx]->zones[zone_idx].leds_count)); 457 | zone_str.append(" LEDs, "); 458 | 459 | switch(vis_ptr->rgb_clients[client_idx]->server_controllers[controller_idx]->zones[zone_idx].type) 460 | { 461 | case ZONE_TYPE_SINGLE: 462 | zone_str.append("Single"); 463 | break; 464 | 465 | case ZONE_TYPE_LINEAR: 466 | zone_str.append("Linear"); 467 | break; 468 | 469 | case ZONE_TYPE_MATRIX: 470 | zone_str.append("Matrix"); 471 | break; 472 | } 473 | 474 | new_child->setText(0, QString::fromStdString(zone_str)); 475 | 476 | 477 | new_item->addChild(new_child); 478 | } 479 | } 480 | } 481 | } 482 | 483 | void Ui::KeyboardVisDlg::on_button_Disconnect_clicked(QObject * arg) 484 | { 485 | NetworkClient * disconnect_client = ((NetworkClientPointer *)arg)->net_client; 486 | 487 | vis_ptr->OpenRGBDisconnect(disconnect_client); 488 | } 489 | 490 | void Ui::KeyboardVisDlg::on_button_Enabled_clicked(QObject * arg) 491 | { 492 | EnableCheckboxArg * checkbox_arg = ((EnableCheckboxArg *)arg); 493 | 494 | if(checkbox_arg->settings_ptr->enabled) 495 | { 496 | checkbox_arg->settings_ptr->enabled = false; 497 | } 498 | else 499 | { 500 | checkbox_arg->settings_ptr->enabled = true; 501 | } 502 | } 503 | 504 | void Ui::KeyboardVisDlg::on_button_Connect_clicked() 505 | { 506 | unsigned short port = std::stoi(ui->lineEdit_Port->text().toStdString()); 507 | std::string ip = ui->lineEdit_IP->text().toStdString(); 508 | 509 | NetworkClient * new_client = vis_ptr->OpenRGBConnect(ip.c_str(), port); 510 | } 511 | -------------------------------------------------------------------------------- /KeyboardVisualizerCommon/Visualizer.cpp: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------*\ 2 | | Processing Code for Keyboard Visualizer | 3 | | | 4 | | Adam Honse (calcprogrammer1@gmail.com), 12/11/2016 | 5 | \*---------------------------------------------------------*/ 6 | 7 | #include "Visualizer.h" 8 | 9 | #ifndef TRUE 10 | #define TRUE 1 11 | #define FALSE 0 12 | #endif 13 | 14 | /*---------------------------------------------------------*\ 15 | | Global variables | 16 | \*---------------------------------------------------------*/ 17 | int ledstrip_sections_size = 1; 18 | int matrix_setup_pos; 19 | int matrix_setup_size; 20 | float fft_nrml[256]; 21 | float fft_fltr[256]; 22 | bool ledstrip_mirror_x = false; 23 | bool ledstrip_mirror_y = false; 24 | bool ledstrip_single_color = false; 25 | int ledstrip_rotate_x = 0; 26 | 27 | /*---------------------------------------------------------*\ 28 | | Visualizer class implementation | 29 | \*---------------------------------------------------------*/ 30 | Visualizer::Visualizer() 31 | { 32 | 33 | } 34 | 35 | void Visualizer::InitAudioDeviceList() 36 | { 37 | #ifdef WIN32 38 | IMMDevice* pEndpoint; 39 | IPropertyStore* pProps; 40 | PROPVARIANT* varName; 41 | 42 | //If using WASAPI, start WASAPI loopback capture device 43 | CoInitializeEx(NULL, COINIT_MULTITHREADED); 44 | CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); 45 | 46 | for (int i = 0; i < pMMDevices.size(); i++) 47 | { 48 | pMMDevices[i]->Release(); 49 | if (i != 0) 50 | { 51 | delete audio_devices[i]; 52 | } 53 | } 54 | 55 | pMMDevices.clear(); 56 | audio_devices.clear(); 57 | isCapture.clear(); 58 | 59 | //Enumerate default audio output 60 | //pMMDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pEndpoint); 61 | //audio_devices.push_back("Default Loopback Device"); 62 | //pMMDevices.push_back(pEndpoint); 63 | //isCapture.push_back(false); 64 | 65 | //Enumerate audio outputs 66 | pMMDeviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pMMDeviceCollection); 67 | 68 | if (pMMDeviceCollection != NULL) 69 | { 70 | UINT count; 71 | pMMDeviceCollection->GetCount(&count); 72 | for (UINT i = 0; i < count; i++) 73 | { 74 | varName = new PROPVARIANT(); 75 | 76 | //Query the item from the list 77 | pMMDeviceCollection->Item(i, &pEndpoint); 78 | 79 | //Open property store for the given item 80 | pEndpoint->OpenPropertyStore(STGM_READ, &pProps); 81 | 82 | //Get the friendly device name string 83 | pProps->GetValue(PKEY_Device_FriendlyName, varName); 84 | 85 | if (varName->pwszVal != NULL) 86 | { 87 | int len = wcslen(varName->pwszVal) + 1; 88 | char* new_device = new char[len + 11]; 89 | wcstombs(new_device, varName->pwszVal, len); 90 | strncat(new_device, " (Loopback)", len); 91 | audio_devices.push_back(new_device); 92 | pMMDevices.push_back(pEndpoint); 93 | isCapture.push_back(false); 94 | } 95 | delete varName; 96 | pProps->Release(); 97 | } 98 | } 99 | pMMDeviceCollection->Release(); 100 | 101 | //Enumerate audio inputs 102 | pMMDeviceEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &pMMDeviceCollection); 103 | 104 | if (pMMDeviceCollection != NULL) 105 | { 106 | UINT count; 107 | pMMDeviceCollection->GetCount(&count); 108 | for (UINT i = 0; i < count; i++) 109 | { 110 | varName = new PROPVARIANT(); 111 | 112 | //Query the item from the list 113 | pMMDeviceCollection->Item(i, &pEndpoint); 114 | 115 | //Open property store for the given item 116 | pEndpoint->OpenPropertyStore(STGM_READ, &pProps); 117 | 118 | //Get the friendly device name string 119 | pProps->GetValue(PKEY_Device_FriendlyName, varName); 120 | 121 | if (varName->pwszVal != NULL) 122 | { 123 | int len = wcslen(varName->pwszVal) + 1; 124 | char* new_device = new char[len]; 125 | wcstombs(new_device, varName->pwszVal, len); 126 | audio_devices.push_back(new_device); 127 | pMMDevices.push_back(pEndpoint); 128 | isCapture.push_back(true); 129 | } 130 | delete varName; 131 | pProps->Release(); 132 | } 133 | } 134 | pMMDeviceEnumerator->Release(); 135 | #else 136 | //If using OpenAL, start OpenAL capture on default capture device 137 | ALCchar* devices; 138 | devices = (ALCchar *) alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); 139 | 140 | //Loop through detected capture devices and stop at the configured one 141 | char devicestring[512]; 142 | char *devicep = devicestring; 143 | for(int i = 0; i < 512; i++) 144 | { 145 | *devicep = *devices; 146 | if(*devicep == '\0') 147 | { 148 | if(strlen(devicestring) > 0) 149 | { 150 | char* new_device = new char[strlen(devicestring) + 1]; 151 | strcpy(new_device, devicestring); 152 | audio_devices.push_back(new_device); 153 | } 154 | 155 | 156 | i = 0; 157 | devices++; 158 | if(*devicep == '\0' && *devicestring == '\0') 159 | { 160 | break; 161 | } 162 | 163 | devicep = devicestring; 164 | } 165 | else 166 | { 167 | devices++; 168 | devicep++; 169 | } 170 | } 171 | #endif 172 | } 173 | 174 | void Visualizer::ChangeAudioDevice() 175 | { 176 | #ifdef WIN32 177 | if (pAudioClient != NULL) 178 | { 179 | pAudioClient->Stop(); 180 | pAudioClient->Release(); 181 | pAudioClient = NULL; 182 | } 183 | 184 | if (audio_device_idx < audio_devices.size()) 185 | { 186 | IMMDevice* pMMDevice = pMMDevices[audio_device_idx]; 187 | pMMDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient); 188 | 189 | pAudioClient->GetMixFormat(&waveformat); 190 | 191 | if (isCapture[audio_device_idx]) 192 | { 193 | pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 0, 0, waveformat, 0); 194 | } 195 | else 196 | { 197 | pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, 0, 0, waveformat, 0); 198 | } 199 | 200 | pAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&pAudioCaptureClient); 201 | 202 | pAudioClient->Start(); 203 | } 204 | else if (audio_devices.size() > 0) 205 | { 206 | audio_device_idx = 0; 207 | ChangeAudioDevice(); 208 | } 209 | #else 210 | if(device != NULL) 211 | { 212 | alcCaptureStop(device); 213 | } 214 | 215 | if(audio_device_idx < audio_devices.size()) 216 | { 217 | device = alcCaptureOpenDevice(audio_devices[audio_device_idx], 10000, AL_FORMAT_MONO8, 2048); 218 | alcCaptureStart(device); 219 | } 220 | else if(audio_devices.size() > 0) 221 | { 222 | audio_device_idx = 0; 223 | ChangeAudioDevice(); 224 | } 225 | #endif 226 | } 227 | 228 | void Visualizer::Initialize() 229 | { 230 | InitAudioDeviceList(); 231 | 232 | background_timer = 0; 233 | background_timeout = 120; 234 | 235 | amplitude = 100; 236 | anim_speed = 100.0f; 237 | avg_mode = 0; 238 | avg_size = 8; 239 | bkgd_step = 0; 240 | bkgd_bright = 100; 241 | bkgd_mode = VISUALIZER_PATTERN_ANIM_RAINBOW_SINUSOIDAL; 242 | delay = 25; 243 | window_mode = 1; 244 | decay = 80; 245 | frgd_mode = VISUALIZER_PATTERN_STATIC_GREEN_YELLOW_RED; 246 | single_color_mode = VISUALIZER_SINGLE_COLOR_FOLLOW_FOREGROUND; 247 | reactive_bkgd = false; 248 | audio_device_idx = 0; 249 | filter_constant = 1.0f; 250 | 251 | settings_changed = false; 252 | update_ui = false; 253 | shutdown_flag = false; 254 | 255 | hanning(win_hanning, 256); 256 | hamming(win_hamming, 256); 257 | blackman(win_blackman, 256); 258 | 259 | nrml_ofst = 0.04f; 260 | nrml_scl = 0.5f; 261 | 262 | pixels_render = &pixels_vs1; 263 | pixels_out = &pixels_vs2; 264 | 265 | ChangeAudioDevice(); 266 | SetNormalization(nrml_ofst, nrml_scl); 267 | } 268 | 269 | void Visualizer::SaveSettings() 270 | { 271 | std::ofstream outfile; 272 | char filename[2048]; 273 | char out_str[1024]; 274 | 275 | //Check background flags, they both should not be set 276 | if ((silent_bkgd == TRUE) && (reactive_bkgd == TRUE)) 277 | { 278 | silent_bkgd = FALSE; 279 | } 280 | 281 | //Set filename 282 | strcpy(filename, "settings.txt"); 283 | 284 | //Open settings file 285 | outfile.open(filename); 286 | 287 | //Save Amplitude 288 | snprintf(out_str, 1024, "amplitude=%d\r\n", amplitude); 289 | outfile.write(out_str, strlen(out_str)); 290 | 291 | //Save Background Brightness 292 | snprintf(out_str, 1024, "bkgd_bright=%d\r\n", bkgd_bright); 293 | outfile.write(out_str, strlen(out_str)); 294 | 295 | //Save Average Size 296 | snprintf(out_str, 1024, "avg_size=%d\r\n", avg_size); 297 | outfile.write(out_str, strlen(out_str)); 298 | 299 | //Save Decay 300 | snprintf(out_str, 1024, "decay=%d\r\n", decay); 301 | outfile.write(out_str, strlen(out_str)); 302 | 303 | //Save Delay 304 | snprintf(out_str, 1024, "delay=%d\r\n", delay); 305 | outfile.write(out_str, strlen(out_str)); 306 | 307 | //Save Normalization Offset 308 | snprintf(out_str, 1024, "nrml_ofst=%f\r\n", nrml_ofst); 309 | outfile.write(out_str, strlen(out_str)); 310 | 311 | //Save Normalization Scale 312 | snprintf(out_str, 1024, "nrml_scl=%f\r\n", nrml_scl); 313 | outfile.write(out_str, strlen(out_str)); 314 | 315 | //Save Filter Constant 316 | snprintf(out_str, 1024, "fltr_const=%f\r\n", filter_constant); 317 | outfile.write(out_str, strlen(out_str)); 318 | 319 | //Save Window Mode 320 | snprintf(out_str, 1024, "window_mode=%d\r\n", window_mode); 321 | outfile.write(out_str, strlen(out_str)); 322 | 323 | //Save Background Mode 324 | snprintf(out_str, 1024, "bkgd_mode=%d\r\n", bkgd_mode); 325 | outfile.write(out_str, strlen(out_str)); 326 | 327 | //Save Foreground Mode 328 | snprintf(out_str, 1024, "frgd_mode=%d\r\n", frgd_mode); 329 | outfile.write(out_str, strlen(out_str)); 330 | 331 | //Save Single Color Mode 332 | snprintf(out_str, 1024, "single_color_mode=%d\r\n", single_color_mode); 333 | outfile.write(out_str, strlen(out_str)); 334 | 335 | //Save Averaging Mode 336 | snprintf(out_str, 1024, "avg_mode=%d\r\n", avg_mode); 337 | outfile.write(out_str, strlen(out_str)); 338 | 339 | //Save Animation Speed 340 | snprintf(out_str, 1024, "anim_speed=%f\r\n", anim_speed); 341 | outfile.write(out_str, strlen(out_str)); 342 | 343 | //Save Reactive Background Flag 344 | snprintf(out_str, 1024, "reactive_bkgd=%d\r\n", reactive_bkgd); 345 | outfile.write(out_str, strlen(out_str)); 346 | 347 | //Save Silent Background Flag 348 | snprintf(out_str, 1024, "silent_bkgd=%d\r\n", silent_bkgd); 349 | outfile.write(out_str, strlen(out_str)); 350 | 351 | //Save Background Timeout 352 | snprintf(out_str, 1024, "background_timeout=%d\r\n", background_timeout); 353 | outfile.write(out_str, strlen(out_str)); 354 | 355 | //Save Audio Device Index 356 | snprintf(out_str, 1024, "audio_device_idx=%d\r\n", audio_device_idx); 357 | outfile.write(out_str, strlen(out_str)); 358 | 359 | //Close Output File 360 | outfile.close(); 361 | } 362 | 363 | void Visualizer::SetNormalization(float offset, float scale) 364 | { 365 | for (int i = 0; i < 256; i++) 366 | { 367 | fft[i] = 0.0f; 368 | fft_nrml[i] = offset + (scale * (i / 256.0f)); 369 | } 370 | } 371 | 372 | void Visualizer::OnSettingsChanged() 373 | { 374 | settings_changed = true; 375 | } 376 | 377 | void Visualizer::Update() 378 | { 379 | float fft_tmp[512]; 380 | 381 | for (int i = 0; i < 256; i++) 382 | { 383 | //Clear the buffers 384 | fft_tmp[i] = 0; 385 | 386 | //Decay previous values 387 | fft[i] = fft[i] * (((float)decay) / 100.0f); 388 | } 389 | 390 | #ifdef WIN32 391 | unsigned int buffer_pos = 0; 392 | static float input_wave[512]; 393 | 394 | unsigned int nextPacketSize = 1; 395 | unsigned int flags; 396 | 397 | while (nextPacketSize > 0) 398 | { 399 | float *buf; 400 | if (pAudioCaptureClient != NULL) 401 | { 402 | pAudioCaptureClient->GetBuffer((BYTE**)&buf, &nextPacketSize, (DWORD *)&flags, NULL, NULL); 403 | 404 | if (buf == NULL && nextPacketSize > 0) 405 | { 406 | pAudioClient->Stop(); 407 | pAudioCaptureClient->Release(); 408 | pAudioClient->Release(); 409 | pAudioCaptureClient = NULL; 410 | pAudioClient = NULL; 411 | } 412 | else 413 | { 414 | for (unsigned int i = 0; i < nextPacketSize; i += 4) 415 | { 416 | for (int j = 0; j < 255; j++) 417 | { 418 | input_wave[2 * j] = input_wave[2 * (j + 1)]; 419 | input_wave[(2 * j) + 1] = input_wave[2 * j]; 420 | } 421 | 422 | float avg_buf = (buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]) / 4; 423 | input_wave[510] = avg_buf * 2.0f * amplitude; 424 | input_wave[511] = input_wave[510]; 425 | } 426 | 427 | buffer_pos += nextPacketSize / 4; 428 | pAudioCaptureClient->ReleaseBuffer(nextPacketSize); 429 | } 430 | } 431 | } 432 | 433 | memcpy(fft_tmp, input_wave, sizeof(input_wave)); 434 | #else 435 | //Only update FFT if there are at least 256 samples in the sample buffer 436 | int samples; 437 | 438 | if(device != NULL) 439 | { 440 | do 441 | { 442 | alcGetIntegerv(device, ALC_CAPTURE_SAMPLES, 1, &samples); 443 | Sleep(1); 444 | } while (samples < 512); 445 | } 446 | 447 | //Capture 256 audio samples 448 | alcCaptureSamples(device, (ALCvoid *)buffer, 256); 449 | 450 | //Scale the input into the FFT processing array 451 | for (int i = 0; i < 512; i++) 452 | { 453 | fft_tmp[i] = (buffer[i / 2] - 128.0f) * (amplitude / 128.0f); 454 | } 455 | #endif 456 | 457 | //Apply selected window 458 | switch (window_mode) 459 | { 460 | case 0: 461 | break; 462 | 463 | case 1: 464 | apply_window(fft_tmp, win_hanning, 256); 465 | break; 466 | 467 | case 2: 468 | apply_window(fft_tmp, win_hamming, 256); 469 | break; 470 | 471 | case 3: 472 | apply_window(fft_tmp, win_blackman, 256); 473 | break; 474 | 475 | default: 476 | break; 477 | } 478 | 479 | //Run the FFT calculation 480 | rfft(fft_tmp, 256, 1); 481 | 482 | fft_tmp[0] = fft_tmp[2]; 483 | 484 | apply_window(fft_tmp, fft_nrml, 256); 485 | 486 | //Compute FFT magnitude 487 | for (int i = 0; i < 128; i += 2) 488 | { 489 | float fftmag; 490 | 491 | //Compute magnitude from real and imaginary components of FFT and apply simple LPF 492 | fftmag = (float)sqrt((fft_tmp[i] * fft_tmp[i]) + (fft_tmp[i + 1] * fft_tmp[i + 1])); 493 | 494 | //Apply a slight logarithmic filter to minimize noise from very low amplitude frequencies 495 | fftmag = ( 0.5f * log10(1.1f * fftmag) ) + ( 0.9f * fftmag ); 496 | 497 | //Limit FFT magnitude to 1.0 498 | if (fftmag > 1.0f) 499 | { 500 | fftmag = 1.0f; 501 | } 502 | 503 | //Update to new values only if greater than previous values 504 | if (fftmag > fft[i*2]) 505 | { 506 | fft[i*2] = fftmag;; 507 | } 508 | 509 | //Prevent from going negative 510 | if (fft[i*2] < 0.0f) 511 | { 512 | fft[i*2] = 0.0f; 513 | } 514 | 515 | //Set odd indexes to match their corresponding even index, as the FFT input array uses two indices for one value (real+imaginary) 516 | fft[(i * 2) + 1] = fft[i * 2]; 517 | fft[(i * 2) + 2] = fft[i * 2]; 518 | fft[(i * 2) + 3] = fft[i * 2]; 519 | } 520 | 521 | if (avg_mode == 0) 522 | { 523 | //Apply averaging over given number of values 524 | int k; 525 | float sum1 = 0; 526 | float sum2 = 0; 527 | for (k = 0; k < avg_size; k++) 528 | { 529 | sum1 += fft[k]; 530 | sum2 += fft[255 - k]; 531 | } 532 | //Compute averages for end bars 533 | sum1 = sum1 / k; 534 | sum2 = sum2 / k; 535 | for (k = 0; k < avg_size; k++) 536 | { 537 | fft[k] = sum1; 538 | fft[255 - k] = sum2; 539 | } 540 | for (int i = 0; i < (256 - avg_size); i += avg_size) 541 | { 542 | float sum = 0; 543 | for (int j = 0; j < avg_size; j += 1) 544 | { 545 | sum += fft[i + j]; 546 | } 547 | 548 | float avg = sum / avg_size; 549 | 550 | for (int j = 0; j < avg_size; j += 1) 551 | { 552 | fft[i + j] = avg; 553 | } 554 | } 555 | } 556 | else if(avg_mode == 1) 557 | { 558 | for (int i = 0; i < avg_size; i++) 559 | { 560 | float sum1 = 0; 561 | float sum2 = 0; 562 | int j; 563 | for (j = 0; j <= i + avg_size; j++) 564 | { 565 | sum1 += fft[j]; 566 | sum2 += fft[255 - j]; 567 | } 568 | fft[i] = sum1 / j; 569 | fft[255 - i] = sum2 / j; 570 | } 571 | for (int i = avg_size; i < 256 - avg_size; i++) 572 | { 573 | float sum = 0; 574 | for (int j = 1; j <= avg_size; j++) 575 | { 576 | sum += fft[i - j]; 577 | sum += fft[i + j]; 578 | } 579 | sum += fft[i]; 580 | 581 | fft[i] = sum / (2 * avg_size + 1); 582 | } 583 | } 584 | for(int i = 0; i < 256; i++) 585 | { 586 | fft_fltr[i] = fft_fltr[i] + (filter_constant * (fft[i] - fft_fltr[i])); 587 | } 588 | } 589 | 590 | void Visualizer::StartThread() 591 | { 592 | //Set application running flag to TRUE before starting threads 593 | running = true; 594 | 595 | VisThread = new std::thread(&Visualizer::VisThreadFunction, this); 596 | } 597 | 598 | void Visualizer::Shutdown() 599 | { 600 | //Initialize a fade-out by setting shutdown flag to TRUE and resetting timer 601 | shutdown_flag = TRUE; 602 | background_timer = 0; 603 | 604 | //Wait for fade-out to complete before returning 605 | while (running == true) 606 | { 607 | Sleep(50); 608 | } 609 | } 610 | 611 | void DrawSolidColor(int bright, RGBColor color, vis_pixels *pixels) 612 | { 613 | bright = (int)(bright * (255.0f / 100.0f)); 614 | for (int x = 0; x < 256; x++) 615 | { 616 | for (int y = 0; y < 64; y++) 617 | { 618 | pixels->pixels[y][x] = RGB(((bright * GetRValue(color)) / 256), ((bright * GetGValue(color)) / 256), ((bright * GetBValue(color)) / 256)); 619 | } 620 | } 621 | } 622 | 623 | void DrawVerticalBars(int bright, RGBColor * colors, int num_colors, vis_pixels *pixels) 624 | { 625 | bright = (int)(bright * (255.0f / 100.0f)); 626 | for (int x = 0; x < 256; x++) 627 | { 628 | for (int y = 0; y < 64; y++) 629 | { 630 | int idx = (int)((float)x * ((float)num_colors / 255.0f)); 631 | pixels->pixels[y][x] = RGB(((bright * GetRValue(colors[idx])) / 256), ((bright * GetGValue(colors[idx])) / 256), ((bright * GetBValue(colors[idx])) / 256)); 632 | } 633 | } 634 | } 635 | 636 | void DrawHorizontalBars(int bright, RGBColor * colors, int num_colors, vis_pixels *pixels) 637 | { 638 | bright = (int)(bright * (255.0f / 100.0f)); 639 | for (int x = 0; x < 256; x++) 640 | { 641 | for (int y = 0; y < 64; y++) 642 | { 643 | if (y == ROW_IDX_BAR_GRAPH) 644 | { 645 | if (x < 128) 646 | { 647 | int idx = (int)(num_colors - ((float)x * ((float)num_colors / 128.0f))); 648 | if (idx >= num_colors) 649 | { 650 | idx = num_colors - 1; 651 | } 652 | pixels->pixels[y][x] = RGB(((bright * GetRValue(colors[idx])) / 256), ((bright * GetGValue(colors[idx])) / 256), ((bright * GetBValue(colors[idx])) / 256)); 653 | } 654 | else 655 | { 656 | int idx = (int)(((float)(x - 128) * ((float)num_colors / 128.0f))); 657 | pixels->pixels[y][x] = RGB(((bright * GetRValue(colors[idx])) / 256), ((bright * GetGValue(colors[idx])) / 256), ((bright * GetBValue(colors[idx])) / 256)); 658 | } 659 | } 660 | else 661 | { 662 | int idx = (int)(num_colors - ((float)y * ((float)num_colors / 63.0f))); 663 | pixels->pixels[y][x] = RGB(((bright * GetRValue(colors[idx])) / 256), ((bright * GetGValue(colors[idx])) / 256), ((bright * GetBValue(colors[idx])) / 256)); 664 | } 665 | } 666 | } 667 | } 668 | 669 | void DrawRainbowSinusoidal(int bright, float bkgd_step, vis_pixels *pixels) 670 | { 671 | bright = (int)(bright * (255.0f / 100.0f)); 672 | for (int x = 0; x < 256; x++) 673 | { 674 | for (int y = 0; y < 64; y++) 675 | { 676 | int red = (int)(127 * (sin(((((int)((x * (360 / 255.0f)) - bkgd_step) % 360) / 360.0f) * 2 * 3.14f)) + 1)); 677 | int grn = (int)(127 * (sin(((((int)((x * (360 / 255.0f)) - bkgd_step) % 360) / 360.0f) * 2 * 3.14f) - (6.28f / 3)) + 1)); 678 | int blu = (int)(127 * (sin(((((int)((x * (360 / 255.0f)) - bkgd_step) % 360) / 360.0f) * 2 * 3.14f) + (6.28f / 3)) + 1)); 679 | pixels->pixels[y][x] = RGB(((bright * red) / 256), ((bright * grn) / 256), ((bright * blu) / 256)); 680 | } 681 | } 682 | } 683 | 684 | void DrawRainbow(int bright, float bkgd_step, vis_pixels *pixels) 685 | { 686 | bright = (int)(bright * (255.0f / 100.0f)); 687 | for (int x = 0; x < 256; x++) 688 | { 689 | for (int y = 0; y < 64; y++) 690 | { 691 | int hsv_h = ((int)(bkgd_step + (256 - x)) % 360); 692 | hsv_t hsv = { 0, 0, 0 }; 693 | hsv.hue = hsv_h; 694 | hsv.saturation = 255; 695 | hsv.value = (unsigned char)bright; 696 | pixels->pixels[y][x] = hsv2rgb(&hsv); 697 | } 698 | } 699 | } 700 | 701 | void DrawColorWheel(int bright, float bkgd_step, int center_x, int center_y, vis_pixels *pixels) 702 | { 703 | bright = (int)(bright * (255.0f / 100.0f)); 704 | for (int x = 0; x < 256; x++) 705 | { 706 | for (int y = 0; y < 64; y++) 707 | { 708 | float hue = (float)(bkgd_step + (int)(180 + atan2(y - center_y, x - center_x) * (180.0 / 3.14159)) % 360); 709 | hsv_t hsv2 = { 0, 0, 0 }; 710 | hsv2.hue = (int)hue; 711 | hsv2.saturation = 255; 712 | hsv2.value = (unsigned char)bright; 713 | pixels->pixels[y][x] = hsv2rgb(&hsv2); 714 | } 715 | } 716 | } 717 | 718 | void DrawSpectrumCycle(int bright, float bkgd_step, vis_pixels *pixels) 719 | { 720 | bright = (int)(bright * (255.0f / 100.0f)); 721 | hsv_t hsv2 = { 0, 0, 0 }; 722 | hsv2.hue = (int)bkgd_step; 723 | hsv2.saturation = 255; 724 | hsv2.value = (unsigned char)bright; 725 | RGBColor color = hsv2rgb(&hsv2); 726 | 727 | for (int x = 0; x < 256; x++) 728 | { 729 | for (int y = 0; y < 64; y++) 730 | { 731 | pixels->pixels[y][x] = color; 732 | } 733 | } 734 | } 735 | 736 | void DrawSinusoidalCycle(int bright, float bkgd_step, vis_pixels *pixels) 737 | { 738 | RGBColor color; 739 | bright = (int)(bright * (255.0f / 100.0f)); 740 | int red = (int)(127 * (sin(((((int)(((360 / 255.0f)) - bkgd_step) % 360) / 360.0f) * 2 * 3.14f)) + 1)); 741 | int grn = (int)(127 * (sin(((((int)(((360 / 255.0f)) - bkgd_step) % 360) / 360.0f) * 2 * 3.14f) - (6.28f / 3)) + 1)); 742 | int blu = (int)(127 * (sin(((((int)(((360 / 255.0f)) - bkgd_step) % 360) / 360.0f) * 2 * 3.14f) + (6.28f / 3)) + 1)); 743 | color = RGB(((bright * red) / 256), ((bright * grn) / 256), ((bright * blu) / 256)); 744 | 745 | for (int x = 0; x < 256; x++) 746 | { 747 | for (int y = 0; y < 64; y++) 748 | { 749 | pixels->pixels[y][x] = color; 750 | } 751 | } 752 | } 753 | void DrawSingleColorBackground(float amplitude, vis_pixels *bg_pixels, vis_pixels *out_pixels) 754 | { 755 | if (amplitude >= 1.0f) 756 | { 757 | amplitude = 1.0f; 758 | } 759 | else if (amplitude <= 0.0f) 760 | { 761 | amplitude = 0.0f; 762 | } 763 | 764 | for (int x = 0; x < 256; x++) 765 | { 766 | int in_color = bg_pixels->pixels[ROW_IDX_SINGLE_COLOR][x]; 767 | int out_color = RGB(((amplitude * GetRValue(in_color))), ((amplitude * GetGValue(in_color))), ((amplitude * GetBValue(in_color)))); 768 | out_pixels->pixels[ROW_IDX_SINGLE_COLOR][x] = out_color; 769 | } 770 | } 771 | 772 | void Visualizer::DrawSingleColorForeground(float amplitude, vis_pixels *fg_pixels, vis_pixels *out_pixels) 773 | { 774 | if (amplitude >= 1.0f) 775 | { 776 | amplitude = 1.0f; 777 | } 778 | else if (amplitude <= 0.0f) 779 | { 780 | amplitude = 0.0f; 781 | } 782 | 783 | int idx = (int)(64.0f - (amplitude * 62.0f)); 784 | int in_color = fg_pixels->pixels[idx][0]; 785 | int out_color = RGB(((amplitude * GetRValue(in_color))), ((amplitude * GetGValue(in_color))), ((amplitude * GetBValue(in_color)))); 786 | for (int x = 0; x < 256; x++) 787 | { 788 | if (frgd_mode >= VISUALIZER_PATTERN_ANIM_RAINBOW_SINUSOIDAL) 789 | { 790 | in_color = fg_pixels->pixels[ROW_IDX_SINGLE_COLOR][x]; 791 | out_color = RGB(((amplitude * GetRValue(in_color))), ((amplitude * GetGValue(in_color))), ((amplitude * GetBValue(in_color)))); 792 | } 793 | 794 | out_pixels->pixels[ROW_IDX_SINGLE_COLOR][x] = out_color; 795 | } 796 | } 797 | 798 | void DrawSingleColorStatic(float amplitude, RGBColor in_color, vis_pixels *out_pixels) 799 | { 800 | if (amplitude >= 1.0f) 801 | { 802 | amplitude = 1.0f; 803 | } 804 | else if (amplitude <= 0.0f) 805 | { 806 | amplitude = 0.0f; 807 | } 808 | 809 | int out_color = RGB(((amplitude * GetRValue(in_color))), ((amplitude * GetGValue(in_color))), ((amplitude * GetBValue(in_color)))); 810 | for (int x = 0; x < 256; x++) 811 | { 812 | out_pixels->pixels[ROW_IDX_SINGLE_COLOR][x] = out_color; 813 | } 814 | } 815 | 816 | void Visualizer::DrawPattern(VISUALIZER_PATTERN pattern, int bright, vis_pixels *pixels) 817 | { 818 | switch (pattern) 819 | { 820 | case VISUALIZER_PATTERN_SOLID_BLACK: 821 | DrawSolidColor(bright, 0x00000000, pixels); 822 | break; 823 | 824 | case VISUALIZER_PATTERN_SOLID_WHITE: 825 | DrawSolidColor(bright, 0x00FFFFFF, pixels); 826 | break; 827 | 828 | case VISUALIZER_PATTERN_SOLID_RED: 829 | DrawSolidColor(bright, 0x000000FF, pixels); 830 | break; 831 | 832 | case VISUALIZER_PATTERN_SOLID_ORANGE: 833 | DrawSolidColor(bright, 0x000040FF, pixels); 834 | break; 835 | 836 | case VISUALIZER_PATTERN_SOLID_YELLOW: 837 | DrawSolidColor(bright, 0x0000FFFF, pixels); 838 | break; 839 | 840 | case VISUALIZER_PATTERN_SOLID_GREEN: 841 | DrawSolidColor(bright, 0x0000FF00, pixels); 842 | break; 843 | 844 | case VISUALIZER_PATTERN_SOLID_CYAN: 845 | DrawSolidColor(bright, 0x00FFFF00, pixels); 846 | break; 847 | 848 | case VISUALIZER_PATTERN_SOLID_BLUE: 849 | DrawSolidColor(bright, 0x00FF0000, pixels); 850 | break; 851 | 852 | case VISUALIZER_PATTERN_SOLID_PURPLE: 853 | DrawSolidColor(bright, 0x00FF0040, pixels); 854 | break; 855 | 856 | case VISUALIZER_PATTERN_STATIC_GREEN_YELLOW_RED: 857 | { 858 | RGBColor colors[] = { 0x0000FF00, 0x0000FFFF, 0x000000FF }; 859 | DrawHorizontalBars(bright, colors, 3, pixels); 860 | } 861 | break; 862 | 863 | case VISUALIZER_PATTERN_STATIC_GREEN_WHITE_RED: 864 | { 865 | RGBColor colors[] = { 0x0000FF00, 0x00FFFFFF, 0x000000FF }; 866 | DrawHorizontalBars(bright, colors, 3, pixels); 867 | } 868 | break; 869 | 870 | case VISUALIZER_PATTERN_STATIC_BLUE_CYAN_WHITE: 871 | { 872 | RGBColor colors[] = { 0x00FF0000, 0x00FFFF00, 0x00FFFFFF }; 873 | DrawHorizontalBars(bright, colors, 3, pixels); 874 | } 875 | break; 876 | 877 | case VISUALIZER_PATTERN_STATIC_RED_WHITE_BLUE: 878 | { 879 | RGBColor colors[] = { 0x000000FF, 0x00FFFFFF, 0x00FF0000 }; 880 | DrawHorizontalBars(bright, colors, 3, pixels); 881 | } 882 | break; 883 | 884 | case VISUALIZER_PATTERN_STATIC_RAINBOW: 885 | { 886 | RGBColor colors[] = { 0x000000FF, 0x0000FFFF, 0x0000FF00, 0x00FFFF00, 0x00FF0000, 0x00FF00FF }; 887 | DrawHorizontalBars(bright, colors, 6, pixels); 888 | } 889 | break; 890 | 891 | case VISUALIZER_PATTERN_STATIC_RAINBOW_INVERSE: 892 | { 893 | RGBColor colors[] = { 0x00FF00FF, 0x00FF0000, 0x00FFFF00, 0x0000FF00, 0x0000FFFF, 0x000000FF }; 894 | DrawHorizontalBars(bright, colors, 6, pixels); 895 | } 896 | break; 897 | 898 | case VISUALIZER_PATTERN_ANIM_RAINBOW_SINUSOIDAL: 899 | DrawRainbowSinusoidal(bright, bkgd_step, pixels); 900 | break; 901 | 902 | case VISUALIZER_PATTERN_ANIM_RAINBOW_HSV: 903 | DrawRainbow(bright, bkgd_step, pixels); 904 | break; 905 | 906 | case VISUALIZER_PATTERN_ANIM_COLOR_WHEEL: 907 | DrawColorWheel(bright, bkgd_step, 128, 32, pixels); 908 | break; 909 | 910 | case VISUALIZER_PATTERN_ANIM_COLOR_WHEEL_2: 911 | DrawColorWheel(bright, bkgd_step, 128, 64, pixels); 912 | break; 913 | 914 | case VISUALIZER_PATTERN_ANIM_SPECTRUM_CYCLE: 915 | DrawSpectrumCycle(bright, bkgd_step, pixels); 916 | break; 917 | 918 | case VISUALIZER_PATTERN_ANIM_SINUSOIDAL_CYCLE: 919 | DrawSinusoidalCycle(bright, bkgd_step, pixels); 920 | break; 921 | } 922 | } 923 | 924 | void Visualizer::VisThreadFunction() 925 | { 926 | while (running == true) 927 | { 928 | Update(); 929 | 930 | //Overflow background step 931 | if (bkgd_step >= 360.0f) bkgd_step = 0.0f; 932 | if (bkgd_step < 0.0f) bkgd_step = 360.0f; 933 | 934 | //Draw active background 935 | DrawPattern(bkgd_mode, bkgd_bright, &pixels_bg); 936 | 937 | //Draw active foreground 938 | DrawPattern(frgd_mode, 100, &pixels_fg); 939 | 940 | float brightness = fft_fltr[5]; 941 | 942 | //If music isn't playing, fade in the single color LEDs after 2 seconds 943 | background_timer++; 944 | 945 | if (shutdown_flag == true) 946 | { 947 | if (background_timer >= background_timeout) 948 | { 949 | brightness = 0.0f; 950 | running = false; 951 | } 952 | else 953 | { 954 | brightness = ((background_timeout - background_timer) / (1.0f * background_timeout)); 955 | } 956 | } 957 | else if (background_timeout > 0) 958 | { 959 | for (int i = 0; i < 128; i++) 960 | { 961 | if (fft_fltr[2 * i] >= 0.0001f) 962 | { 963 | background_timer = 0; 964 | } 965 | } 966 | if (background_timer >= background_timeout) 967 | { 968 | if (background_timer >= (3 * background_timeout)) 969 | { 970 | background_timer = (3 * background_timeout); 971 | } 972 | brightness = (background_timer - background_timeout) / (2.0f * background_timeout); 973 | } 974 | } 975 | 976 | //Loop through all 256x64 pixels in visualization image 977 | for (int x = 0; x < 256; x++) 978 | { 979 | for (int y = 0; y < 64; y++) 980 | { 981 | //Draw Spectrograph Foreground 982 | if (fft_fltr[x] >((1 / 64.0f)*(64.0f - y))) 983 | { 984 | if (shutdown_flag == true) 985 | { 986 | int in_color = pixels_fg.pixels[y][x]; 987 | pixels_render->pixels[y][x] = RGB(((brightness * GetRValue(in_color))), ((brightness * GetGValue(in_color))), ((brightness * GetBValue(in_color)))); 988 | } 989 | else 990 | { 991 | pixels_render->pixels[y][x] = pixels_fg.pixels[y][x]; 992 | } 993 | } 994 | else 995 | { 996 | if(reactive_bkgd || silent_bkgd) 997 | { 998 | if (!silent_bkgd || ((background_timer >= background_timeout) && (background_timeout > 0))) 999 | { 1000 | int in_color = pixels_bg.pixels[y][x]; 1001 | pixels_render->pixels[y][x] = RGB(((brightness * GetRValue(in_color))), ((brightness * GetGValue(in_color))), ((brightness * GetBValue(in_color)))); 1002 | } 1003 | else 1004 | { 1005 | pixels_render->pixels[y][x] = RGB(0, 0, 0); 1006 | } 1007 | } 1008 | else 1009 | { 1010 | pixels_render->pixels[y][x] = pixels_bg.pixels[y][x]; 1011 | } 1012 | 1013 | } 1014 | 1015 | //Draw Bar Graph Foreground 1016 | if (y == ROW_IDX_BAR_GRAPH) 1017 | { 1018 | if (x < 128) 1019 | { 1020 | if ((fft_fltr[5] - 0.05f) >((1 / 128.0f)*(127-x))) 1021 | { 1022 | if (shutdown_flag == true) 1023 | { 1024 | int in_color = pixels_fg.pixels[y][x]; 1025 | pixels_render->pixels[y][x] = RGB(((brightness * GetRValue(in_color))), ((brightness * GetGValue(in_color))), ((brightness * GetBValue(in_color)))); 1026 | } 1027 | else 1028 | { 1029 | pixels_render->pixels[y][x] = pixels_fg.pixels[y][x]; 1030 | } 1031 | } 1032 | else 1033 | { 1034 | if (reactive_bkgd || silent_bkgd) 1035 | { 1036 | if (!silent_bkgd || ((background_timer >= background_timeout) && (background_timeout > 0))) 1037 | { 1038 | int in_color = pixels_bg.pixels[y][x]; 1039 | pixels_render->pixels[y][x] = RGB(((brightness * GetRValue(in_color))), ((brightness * GetGValue(in_color))), ((brightness * GetBValue(in_color)))); 1040 | } 1041 | else 1042 | { 1043 | pixels_render->pixels[y][x] = RGB(0, 0, 0); 1044 | } 1045 | } 1046 | else 1047 | { 1048 | pixels_render->pixels[y][x] = pixels_bg.pixels[y][x]; 1049 | } 1050 | } 1051 | } 1052 | else 1053 | { 1054 | if ((fft_fltr[5] - 0.05f) >((1 / 128.0f)*((x-128)))) 1055 | { 1056 | if (shutdown_flag == true) 1057 | { 1058 | int in_color = pixels_fg.pixels[y][x]; 1059 | pixels_render->pixels[y][x] = RGB(((brightness * GetRValue(in_color))), ((brightness * GetGValue(in_color))), ((brightness * GetBValue(in_color)))); 1060 | } 1061 | else 1062 | { 1063 | pixels_render->pixels[y][x] = pixels_fg.pixels[y][x]; 1064 | } 1065 | } 1066 | else 1067 | { 1068 | if (reactive_bkgd || silent_bkgd) 1069 | { 1070 | if (!silent_bkgd || (background_timer >= background_timeout)) 1071 | { 1072 | int in_color = pixels_bg.pixels[y][x]; 1073 | pixels_render->pixels[y][x] = RGB(((brightness * GetRValue(in_color))), ((brightness * GetGValue(in_color))), ((brightness * GetBValue(in_color)))); 1074 | } 1075 | else 1076 | { 1077 | pixels_render->pixels[y][x] = RGB(0, 0, 0); 1078 | } 1079 | } 1080 | else 1081 | { 1082 | pixels_render->pixels[y][x] = pixels_bg.pixels[y][x]; 1083 | } 1084 | } 1085 | } 1086 | } 1087 | } 1088 | } 1089 | 1090 | if (single_color_mode == VISUALIZER_SINGLE_COLOR_FOLLOW_BACKGROUND) 1091 | { 1092 | brightness = (bkgd_bright / 100.0f) * brightness; 1093 | } 1094 | 1095 | if ((background_timeout <= 0 ) || (background_timer < background_timeout)) 1096 | { 1097 | //Draw brightness based visualizer for single LED devices 1098 | switch (single_color_mode) 1099 | { 1100 | case VISUALIZER_SINGLE_COLOR_BLACK: 1101 | DrawSingleColorStatic(brightness, 0x00000000, pixels_render); 1102 | break; 1103 | 1104 | case VISUALIZER_SINGLE_COLOR_WHITE: 1105 | DrawSingleColorStatic(brightness, 0x00FFFFFF, pixels_render); 1106 | break; 1107 | 1108 | case VISUALIZER_SINGLE_COLOR_RED: 1109 | DrawSingleColorStatic(brightness, 0x000000FF, pixels_render); 1110 | break; 1111 | 1112 | case VISUALIZER_SINGLE_COLOR_ORANGE: 1113 | DrawSingleColorStatic(brightness, 0x000080FF, pixels_render); 1114 | break; 1115 | 1116 | case VISUALIZER_SINGLE_COLOR_YELLOW: 1117 | DrawSingleColorStatic(brightness, 0x0000FFFF, pixels_render); 1118 | break; 1119 | 1120 | case VISUALIZER_SINGLE_COLOR_GREEN: 1121 | DrawSingleColorStatic(brightness, 0x0000FF00, pixels_render); 1122 | break; 1123 | 1124 | case VISUALIZER_SINGLE_COLOR_CYAN: 1125 | DrawSingleColorStatic(brightness, 0x00FFFF00, pixels_render); 1126 | break; 1127 | 1128 | case VISUALIZER_SINGLE_COLOR_BLUE: 1129 | DrawSingleColorStatic(brightness, 0x00FF0000, pixels_render); 1130 | break; 1131 | 1132 | case VISUALIZER_SINGLE_COLOR_PURPLE: 1133 | DrawSingleColorStatic(brightness, 0x00FF00FF, pixels_render); 1134 | break; 1135 | 1136 | case VISUALIZER_SINGLE_COLOR_BACKGROUND: 1137 | //Intentionally do nothing, leave the background unmodified 1138 | break; 1139 | 1140 | case VISUALIZER_SINGLE_COLOR_FOLLOW_BACKGROUND: 1141 | DrawSingleColorBackground(brightness, &pixels_bg, pixels_render); 1142 | break; 1143 | 1144 | case VISUALIZER_SINGLE_COLOR_FOLLOW_FOREGROUND: 1145 | DrawSingleColorForeground(brightness, &pixels_fg, pixels_render); 1146 | break; 1147 | } 1148 | } 1149 | 1150 | //Swap buffers 1151 | if (pixels_render == &pixels_vs1) 1152 | { 1153 | pixels_render = &pixels_vs2; 1154 | pixels_out = &pixels_vs1; 1155 | } 1156 | else 1157 | { 1158 | pixels_render = &pixels_vs1; 1159 | pixels_out = &pixels_vs2; 1160 | } 1161 | 1162 | //Increment background step 1163 | bkgd_step = bkgd_step += (anim_speed / 100.0f); 1164 | 1165 | //Wait 15ms (~60fps) 1166 | Sleep(15); 1167 | } 1168 | } 1169 | 1170 | static void UpdateOpenRGBClientListCallback(void * this_ptr) 1171 | { 1172 | Visualizer * this_obj = (Visualizer *)this_ptr; 1173 | 1174 | this_obj->UpdateClientSettings(); 1175 | } 1176 | 1177 | void Visualizer::RegisterClientInfoChangeCallback(NetClientCallback new_callback, void * new_callback_arg) 1178 | { 1179 | ClientInfoChangeCallbacks.push_back(new_callback); 1180 | ClientInfoChangeCallbackArgs.push_back(new_callback_arg); 1181 | } 1182 | 1183 | void Visualizer::ClientInfoChanged() 1184 | { 1185 | ClientInfoChangeMutex.lock(); 1186 | 1187 | /*-------------------------------------------------*\ 1188 | | Client info has changed, call the callbacks | 1189 | \*-------------------------------------------------*/ 1190 | for(unsigned int callback_idx = 0; callback_idx < ClientInfoChangeCallbacks.size(); callback_idx++) 1191 | { 1192 | ClientInfoChangeCallbacks[callback_idx](ClientInfoChangeCallbackArgs[callback_idx]); 1193 | } 1194 | 1195 | ClientInfoChangeMutex.unlock(); 1196 | } 1197 | 1198 | void Visualizer::UpdateClientSettings() 1199 | { 1200 | /*-----------------------------------------------------*\ 1201 | | Loop through all clients and make sure each has an | 1202 | | associated settings | 1203 | \*-----------------------------------------------------*/ 1204 | for(unsigned int client_idx = 0; client_idx < rgb_clients.size(); client_idx++) 1205 | { 1206 | if(client_idx < rgb_client_settings.size()) 1207 | { 1208 | /*-----------------------------------------------------*\ 1209 | | If the client settings at this index matches the | 1210 | | controller at this index, continue | 1211 | \*-----------------------------------------------------*/ 1212 | if(rgb_client_settings[client_idx]->client_ptr == rgb_clients[client_idx]) 1213 | { 1214 | continue; 1215 | } 1216 | /*-----------------------------------------------------*\ 1217 | | Otherwise, search the rest of the client settings list| 1218 | | to see if the client settings for this controller has | 1219 | | been misplaced | 1220 | \*-----------------------------------------------------*/ 1221 | else 1222 | { 1223 | bool found_match = false; 1224 | 1225 | for(unsigned int search_client_idx = client_idx; search_client_idx < rgb_client_settings.size(); search_client_idx++) 1226 | { 1227 | /*-----------------------------------------------------*\ 1228 | | If the client settings at this index matches the | 1229 | | controller at this index, swap the settings at the | 1230 | | original position for those at the search position | 1231 | \*-----------------------------------------------------*/ 1232 | if(rgb_client_settings[search_client_idx]->client_ptr == rgb_clients[client_idx]) 1233 | { 1234 | ClientSettingsType* tmp_settings = rgb_client_settings[search_client_idx]; 1235 | 1236 | rgb_client_settings[search_client_idx] = rgb_client_settings[client_idx]; 1237 | 1238 | rgb_client_settings[client_idx] = tmp_settings; 1239 | 1240 | found_match = true; 1241 | 1242 | break; 1243 | } 1244 | } 1245 | 1246 | /*-----------------------------------------------------*\ 1247 | | If a matching settings was not found, create a new one| 1248 | \*-----------------------------------------------------*/ 1249 | if(!found_match) 1250 | { 1251 | ClientSettingsType* new_settings = new ClientSettingsType(); 1252 | 1253 | new_settings->client_ptr = rgb_clients[client_idx]; 1254 | 1255 | rgb_client_settings.insert(rgb_client_settings.begin() + client_idx, new_settings); 1256 | } 1257 | } 1258 | } 1259 | else 1260 | { 1261 | /*-----------------------------------------------------*\ 1262 | | If the settings list is smaller than the client index | 1263 | | that means this client wasn't found in the existing | 1264 | | list. Create a new settings for this client | 1265 | \*-----------------------------------------------------*/ 1266 | 1267 | ClientSettingsType* new_settings = new ClientSettingsType(); 1268 | 1269 | new_settings->client_ptr = rgb_clients[client_idx]; 1270 | 1271 | rgb_client_settings.insert(rgb_client_settings.begin() + client_idx, new_settings); 1272 | } 1273 | } 1274 | 1275 | /*-----------------------------------------------------*\ 1276 | | At this point, the clients and settings lists should | 1277 | | line up. If the settings list is longer than the | 1278 | | clients list, delete the extras as they are unused | 1279 | \*-----------------------------------------------------*/ 1280 | for(unsigned int settings_idx = rgb_clients.size(); settings_idx < rgb_client_settings.size(); settings_idx++) 1281 | { 1282 | rgb_client_settings.pop_back(); 1283 | } 1284 | 1285 | /*-----------------------------------------------------*\ 1286 | | Now go through each client and make sure all the | 1287 | | controller settings entries line up | 1288 | \*-----------------------------------------------------*/ 1289 | for(unsigned int client_idx = 0; client_idx < rgb_clients.size(); client_idx++) 1290 | { 1291 | std::vector & controllers = rgb_clients[client_idx]->server_controllers; 1292 | std::vector & controller_settings = rgb_client_settings[client_idx]->controller_settings; 1293 | 1294 | for(unsigned int controller_idx = 0; controller_idx < controllers.size(); controller_idx++) 1295 | { 1296 | if(controller_idx < controller_settings.size()) 1297 | { 1298 | /*-----------------------------------------------------*\ 1299 | | If the controller settings at this index matches the | 1300 | | controller at this index, continue | 1301 | \*-----------------------------------------------------*/ 1302 | if(controller_settings[controller_idx]->controller_ptr == controllers[controller_idx]) 1303 | { 1304 | continue; 1305 | } 1306 | /*-----------------------------------------------------*\ 1307 | | Otherwise, search the rest of the client settings list| 1308 | | to see if the client settings for this controller has | 1309 | | been misplaced | 1310 | \*-----------------------------------------------------*/ 1311 | else 1312 | { 1313 | bool found_match = false; 1314 | 1315 | for(unsigned int search_controller_idx = controller_idx; search_controller_idx < controller_settings.size(); search_controller_idx++) 1316 | { 1317 | /*-----------------------------------------------------*\ 1318 | | If the client settings at this index matches the | 1319 | | controller at this index, swap the settings at the | 1320 | | original position for those at the search position | 1321 | \*-----------------------------------------------------*/ 1322 | if(controller_settings[search_controller_idx]->controller_ptr == controllers[controller_idx]) 1323 | { 1324 | ControllerSettingsType* tmp_settings = controller_settings[search_controller_idx]; 1325 | 1326 | controller_settings[search_controller_idx] = controller_settings[controller_idx]; 1327 | 1328 | controller_settings[controller_idx] = tmp_settings; 1329 | 1330 | found_match = true; 1331 | 1332 | break; 1333 | } 1334 | } 1335 | 1336 | /*-----------------------------------------------------*\ 1337 | | If a matching settings was not found, create a new one| 1338 | \*-----------------------------------------------------*/ 1339 | if(!found_match) 1340 | { 1341 | ControllerSettingsType* new_settings = new ControllerSettingsType(); 1342 | 1343 | new_settings->controller_ptr = controllers[controller_idx]; 1344 | new_settings->enabled = false; 1345 | 1346 | for(int mode_idx = 0; mode_idx < controllers[controller_idx]->modes.size(); mode_idx++) 1347 | { 1348 | if(controllers[controller_idx]->modes[mode_idx].name == "Direct") 1349 | { 1350 | new_settings->enabled = true; 1351 | } 1352 | } 1353 | 1354 | controller_settings.insert(controller_settings.begin() + controller_idx, new_settings); 1355 | } 1356 | } 1357 | } 1358 | else 1359 | { 1360 | /*-----------------------------------------------------*\ 1361 | | If the settings list is smaller than the client index | 1362 | | that means this client wasn't found in the existing | 1363 | | list. Create a new settings for this client | 1364 | \*-----------------------------------------------------*/ 1365 | 1366 | ControllerSettingsType* new_settings = new ControllerSettingsType(); 1367 | 1368 | new_settings->controller_ptr = controllers[controller_idx]; 1369 | new_settings->enabled = false; 1370 | 1371 | for(int mode_idx = 0; mode_idx < controllers[controller_idx]->modes.size(); mode_idx++) 1372 | { 1373 | if(controllers[controller_idx]->modes[mode_idx].name == "Direct") 1374 | { 1375 | new_settings->enabled = true; 1376 | } 1377 | } 1378 | 1379 | controller_settings.insert(controller_settings.begin() + controller_idx, new_settings); 1380 | } 1381 | } 1382 | 1383 | /*-----------------------------------------------------*\ 1384 | | At this point, the clients and settings lists should | 1385 | | line up. If the settings list is longer than the | 1386 | | clients list, delete the extras as they are unused | 1387 | \*-----------------------------------------------------*/ 1388 | for(unsigned int settings_idx = controllers.size(); settings_idx < controller_settings.size(); settings_idx++) 1389 | { 1390 | controller_settings.pop_back(); 1391 | } 1392 | } 1393 | 1394 | ClientInfoChanged(); 1395 | } 1396 | 1397 | static bool started = false; 1398 | NetworkClient * Visualizer::OpenRGBConnect(const char * ip, unsigned short port) 1399 | { 1400 | NetworkClient * rgb_client = new NetworkClient(rgb_controllers); 1401 | 1402 | rgb_clients.push_back(rgb_client); 1403 | 1404 | std::string titleString = "Keyboard Visualizer "; 1405 | titleString.append(VERSION_STRING); 1406 | 1407 | rgb_client->SetIP(ip); 1408 | rgb_client->SetName(titleString.c_str()); 1409 | rgb_client->SetPort(port); 1410 | 1411 | rgb_client->RegisterClientInfoChangeCallback(UpdateOpenRGBClientListCallback, this); 1412 | 1413 | rgb_client->StartClient(); 1414 | 1415 | if(!started) 1416 | { 1417 | started = true; 1418 | LEDUpdateThread = new std::thread(&Visualizer::LEDUpdateThreadFunction, this); 1419 | } 1420 | 1421 | return(rgb_client); 1422 | } 1423 | 1424 | void Visualizer::OpenRGBDisconnect(NetworkClient * client) 1425 | { 1426 | started = false; 1427 | LEDUpdateThread->join(); 1428 | 1429 | client->StopClient(); 1430 | 1431 | for(unsigned int client_idx = 0; client_idx < rgb_clients.size(); client_idx++) 1432 | { 1433 | if(client == rgb_clients[client_idx]) 1434 | { 1435 | rgb_clients.erase(rgb_clients.begin() + client_idx); 1436 | rgb_client_settings.erase(rgb_client_settings.begin() + client_idx); 1437 | break; 1438 | } 1439 | } 1440 | 1441 | if(!started) 1442 | { 1443 | started = true; 1444 | LEDUpdateThread = new std::thread(&Visualizer::LEDUpdateThreadFunction, this); 1445 | } 1446 | } 1447 | 1448 | static void SetupMatrixGrid(int x_count, int y_count, int * x_idx_list, int * y_idx_list) 1449 | { 1450 | for(int x = 0; x < x_count; x++) 1451 | { 1452 | if(x_count < 10) 1453 | { 1454 | x_idx_list[x] = (int)((x * (SPECTROGRAPH_COLS / (x_count))) + (0.5f * (SPECTROGRAPH_COLS / (x_count)))); 1455 | } 1456 | else if(x < ((x_count) / 2)) 1457 | { 1458 | x_idx_list[x] = (int)((x * (SPECTROGRAPH_COLS / (x_count - 1))) + (0.5f * (SPECTROGRAPH_COLS / (x_count - 1)))); 1459 | } 1460 | else 1461 | { 1462 | x_idx_list[x] = (int)((x * (SPECTROGRAPH_COLS / (x_count - 1))) - (0.5f * (SPECTROGRAPH_COLS / (x_count - 1)))); 1463 | } 1464 | 1465 | } 1466 | for(int y = 0; y < y_count; y++) 1467 | { 1468 | y_idx_list[y] = (int)(ROW_IDX_SPECTROGRAPH_TOP + (y * (SPECTROGRAPH_ROWS / y_count)) + (0.5f * (SPECTROGRAPH_ROWS / y_count))); 1469 | } 1470 | } 1471 | 1472 | static void SetupLinearGrid(int x_count, int * x_idx_list) 1473 | { 1474 | if((x_count % 2) == 0) 1475 | { 1476 | //Even number of LEDs 1477 | for(int x = 0; x < x_count; x++) 1478 | { 1479 | x_idx_list[x] = (int)((float)x * (256.0f / (float)x_count)) + (128.0f / (float)x_count); 1480 | } 1481 | } 1482 | else 1483 | { 1484 | //Odd number of LEDs 1485 | for(int x = 0; x < x_count; x++) 1486 | { 1487 | if (x == (x_count / 2)) 1488 | { 1489 | x_idx_list[x] = 128; 1490 | } 1491 | else if (x < ((x_count / 2) + 1)) 1492 | { 1493 | x_idx_list[x] = (x_count / 2) + ((x + 1) * (256 / (x_count + 1))); 1494 | } 1495 | else 1496 | { 1497 | x_idx_list[x] = ((x_count / 2) + 1) + (x * (256 / (x_count + 1))); 1498 | } 1499 | 1500 | } 1501 | } 1502 | } 1503 | 1504 | void Visualizer::LEDUpdateThreadFunction() 1505 | { 1506 | while(started) 1507 | { 1508 | for(unsigned int client_idx = 0; client_idx < rgb_clients.size(); client_idx++) 1509 | { 1510 | rgb_clients[client_idx]->ControllerListMutex.lock(); 1511 | 1512 | if(client_idx < rgb_client_settings.size()) 1513 | { 1514 | if(rgb_client_settings[client_idx]->client_ptr == rgb_clients[client_idx]) 1515 | { 1516 | std::vector & controllers = rgb_clients[client_idx]->server_controllers; 1517 | std::vector & controllers_settings = rgb_client_settings[client_idx]->controller_settings; 1518 | 1519 | for(unsigned int controller_idx = 0; controller_idx < controllers.size(); controller_idx++) 1520 | { 1521 | if(controller_idx < controllers_settings.size()) 1522 | { 1523 | if(controllers_settings[controller_idx]->controller_ptr == controllers[controller_idx]) 1524 | { 1525 | RGBController * controller = controllers[controller_idx]; 1526 | ControllerSettingsType * controller_settings = controllers_settings[controller_idx]; 1527 | 1528 | if(controller_settings->enabled) 1529 | { 1530 | for(unsigned int zone_idx = 0; zone_idx < controller->zones.size(); zone_idx++) 1531 | { 1532 | int x_count = controller->zones[zone_idx].leds_count; 1533 | int y_count = 0; 1534 | zone_type type = controller->zones[zone_idx].type; 1535 | ZoneIndexType * zone_index_map = NULL; 1536 | bool index_map_found = false; 1537 | 1538 | // If matrix type and matrix mapping is valid, get X and Y count 1539 | if(type == ZONE_TYPE_MATRIX) 1540 | { 1541 | if(controller->zones[zone_idx].matrix_map != NULL) 1542 | { 1543 | x_count = controller->zones[zone_idx].matrix_map->width; 1544 | y_count = controller->zones[zone_idx].matrix_map->height; 1545 | } 1546 | else 1547 | { 1548 | type = ZONE_TYPE_SINGLE; 1549 | } 1550 | } 1551 | 1552 | // Search all the zone index maps 1553 | for(int i = 0; i < controller_settings->zones.size(); i++) 1554 | { 1555 | zone_index_map = &controller_settings->zones[i]; 1556 | 1557 | if((zone_index_map->x_count == x_count) && (zone_index_map->y_count == y_count)) 1558 | { 1559 | index_map_found = true; 1560 | break; 1561 | } 1562 | } 1563 | 1564 | // If the index map doesn't exist for this zone, create it 1565 | if(index_map_found == false) 1566 | { 1567 | ZoneIndexType * new_index_map = new ZoneIndexType(); 1568 | new_index_map->x_count = x_count; 1569 | new_index_map->y_count = y_count; 1570 | 1571 | if(type == ZONE_TYPE_MATRIX) 1572 | { 1573 | new_index_map->x_index = new int[x_count]; 1574 | new_index_map->y_index = new int[y_count]; 1575 | 1576 | SetupMatrixGrid(x_count, y_count, new_index_map->x_index, new_index_map->y_index); 1577 | } 1578 | else if(type == ZONE_TYPE_LINEAR) 1579 | { 1580 | new_index_map->x_index = new int[x_count]; 1581 | 1582 | SetupLinearGrid(x_count, new_index_map->x_index); 1583 | } 1584 | 1585 | controller_settings->zones.push_back(*new_index_map); 1586 | 1587 | zone_index_map = &controller_settings->zones[controller_settings->zones.size() - 1]; 1588 | } 1589 | 1590 | switch (controller->zones[zone_idx].type) 1591 | { 1592 | case ZONE_TYPE_MATRIX: 1593 | for (int y = 0; y < y_count; y++) 1594 | { 1595 | for (int x = 0; x < x_count; x++) 1596 | { 1597 | unsigned int map_idx = (y * x_count) + x; 1598 | unsigned int color_idx = controller->zones[zone_idx].matrix_map->map[map_idx]; 1599 | if( color_idx != 0xFFFFFFFF ) 1600 | { 1601 | controller->zones[zone_idx].colors[color_idx] = pixels_out->pixels[zone_index_map->y_index[y]][zone_index_map->x_index[x]]; 1602 | } 1603 | } 1604 | } 1605 | break; 1606 | 1607 | case ZONE_TYPE_SINGLE: 1608 | for (int r = 0; r < x_count; r++) 1609 | { 1610 | controller->zones[zone_idx].colors[r] = pixels_out->pixels[ROW_IDX_SINGLE_COLOR][0]; 1611 | } 1612 | break; 1613 | 1614 | case ZONE_TYPE_LINEAR: 1615 | for (int x = 0; x < x_count; x++) 1616 | { 1617 | controller->zones[zone_idx].colors[x] = pixels_out->pixels[ROW_IDX_BAR_GRAPH][zone_index_map->x_index[x]]; 1618 | } 1619 | break; 1620 | } 1621 | } 1622 | controller->DeviceUpdateLEDs(); 1623 | } 1624 | } 1625 | } 1626 | } 1627 | } 1628 | } 1629 | 1630 | rgb_clients[client_idx]->ControllerListMutex.unlock(); 1631 | } 1632 | Sleep(delay); 1633 | } 1634 | } 1635 | --------------------------------------------------------------------------------