├── 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 
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 |
--------------------------------------------------------------------------------