Compare commits

...

42 Commits
v1.0 ... master

Author SHA1 Message Date
008fd65c08
Update README.md 2021-08-05 23:43:58 +02:00
f7f30f36c7 Removed old MSI build-target 2019-12-03 15:48:03 +01:00
e62259ca63 Updated RGB.NET; added Steel-Series 2019-12-01 18:13:58 +01:00
b30bb88762 Bumped version number to 1.3.1.1 2019-06-09 19:28:59 +02:00
6ad2e861a0 Changed Settings-Serialization to work with hte new double-values in colors.
Fixes #50
2019-06-09 19:25:02 +02:00
967dde1d3d Bumped version number to 1.3.1 2019-05-26 14:34:49 +02:00
7216eae954 Updated RGB.NET 2019-05-26 14:34:39 +02:00
e94c0723c5 Updated nuget-packages 2019-01-06 10:26:44 +01:00
5dfc02eeab Bumped version-number to 1.3 2018-10-06 12:01:34 +02:00
806dd3aae7 Fixed default value of the background brush 2018-10-06 12:00:13 +02:00
dde304b007 Added setting to change the background-brush 2018-10-06 11:53:58 +02:00
7eac420a8e Updated RGB.NET 2018-10-06 11:53:35 +02:00
7cf179421f Merge branch 'master' of https://github.com/DarthAffe/KeyboardAudioVisualizer 2018-07-15 17:34:46 +02:00
b54e01a96b Bumped version-number to 1.2 2018-07-15 17:34:41 +02:00
7b6c2c184e Updated RGB.NET and included new device-types 2018-07-15 17:33:21 +02:00
8f53049bdf Updated audio capturing to make use of the SingleBlockRead-event 2018-07-15 17:17:42 +02:00
edwardbaeg
0ec92488e4 Fix typo (#4) 2018-03-10 09:46:24 +01:00
4141a5dd36 Bumped version number to 1.1 2018-02-09 23:21:15 +01:00
2ad4a6f333 Increased max update-rate 2018-02-09 23:19:09 +01:00
4543dcb787 Added volume prescaling-option to resolve #3 2018-02-09 22:45:56 +01:00
1cef641c62 Fixed a render bug in the gradient editor 2018-02-09 21:23:38 +01:00
940d2eb631 Added a workaround for the weird windows taskbar-closewindow behavior 2018-02-09 21:12:33 +01:00
8d1488db24 Added another workaround for fake-suround devices 2018-02-09 21:12:07 +01:00
3c76269c65 Fixed wrong gradient-selection 2018-02-09 21:11:31 +01:00
f42334aa2b Fixed error when creating a new configuration 2018-02-04 17:32:40 +01:00
2f9286e39d Added color-settings to resolve #2 2018-02-04 15:48:11 +01:00
d4b4071f71 Updated all dependencies 2018-01-29 13:45:10 +01:00
cb8146c594 Minor changes 2018-01-29 13:16:28 +01:00
1af8bb67c3 Added something (a workaround?) to prevent problems with fake 7.1 drivers 2017-09-30 22:05:06 +02:00
51213e31c2 Merge branch 'master' of https://github.com/DarthAffe/KeyboardAudioVisualizer 2017-09-30 17:01:19 +02:00
b2e81cdbaf Bumped version number 2017-09-30 17:01:15 +02:00
f1cad012c3 Added default-values to readme 2017-09-29 17:20:44 +02:00
a8c34ada40 Merge branch 'master' of https://github.com/DarthAffe/KeyboardAudioVisualizer 2017-09-29 16:56:13 +02:00
e35e70d0bb Added value-indicator for sliders 2017-09-29 16:56:00 +02:00
c72e8497ab Made disable-state of sliders more obvious 2017-09-29 11:18:19 +02:00
0ccc1f3dbb Added Tooltips 2017-09-29 11:17:47 +02:00
4dc9377e75 Typos fixed 2017-09-29 10:36:15 +02:00
07e4221877 Added migration for old configs 2017-09-29 10:17:31 +02:00
9d4aa2728c Updated everything to work with RGB.NETs new decorators; Added dropdowns to change the visualization on devices; Changed configuration to be saved as json (converter for the old format is still needed) 2017-09-25 21:27:50 +02:00
44c0cc59fa Reimplemented specific brushes as decorators 2017-09-06 14:42:33 +02:00
9b84bfcba3 Added novation-device-support and improved lightbar handling 2017-08-20 10:29:48 +02:00
adf3fc5fd3 Updated readme 2017-08-13 17:29:04 +02:00
68 changed files with 3574 additions and 800 deletions

3
.gitignore vendored
View File

@ -286,6 +286,3 @@ __pycache__/
*.btm.cs
*.odx.cs
*.xsd.cs
/NuGet.Config
/NuGet.Config
/KeyboardAudioVisualizer/NuGet.Config

View File

@ -7,7 +7,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>

View File

@ -1,10 +1,16 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Hardcodet.Wpf.TaskbarNotification;
using KeyboardAudioVisualizer.AudioProcessing;
using KeyboardAudioVisualizer.Configuration;
using KeyboardAudioVisualizer.Helper;
using KeyboardAudioVisualizer.Legacy;
using Newtonsoft.Json;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
using Settings = KeyboardAudioVisualizer.Configuration.Settings;
namespace KeyboardAudioVisualizer
{
@ -12,7 +18,7 @@ namespace KeyboardAudioVisualizer
{
#region Constants
private const string PATH_SETTINGS = "Settings.xml";
private const string PATH_SETTINGS = "Settings.json";
#endregion
@ -22,10 +28,6 @@ namespace KeyboardAudioVisualizer
#endregion
#region Constructors
#endregion
#region Methods
protected override void OnStartup(StartupEventArgs e)
@ -34,18 +36,38 @@ namespace KeyboardAudioVisualizer
try
{
ToolTipService.ShowDurationProperty.OverrideMetadata(typeof(DependencyObject), new FrameworkPropertyMetadata(int.MaxValue));
_taskbarIcon = (TaskbarIcon)FindResource("TaskbarIcon");
_taskbarIcon.DoubleClickCommand = ApplicationManager.Instance.OpenConfigurationCommand;
Settings settings = SerializationHelper.LoadObjectFromFile<Settings>(PATH_SETTINGS);
//Settings settings = SerializationHelper.LoadObjectFromFile<Settings>(PATH_SETTINGS);
Settings settings = null;
try { settings = JsonConvert.DeserializeObject<Settings>(File.ReadAllText(PATH_SETTINGS), new ColorSerializer()); }
catch (Exception ex)
{
Console.WriteLine(ex.Message);
/* File doesn't exist or is corrupt - just create a new one. */
}
if (settings == null)
settings = ConfigurationMigrator.MigrateOldConfig();
if (settings == null)
{
settings = new Settings();
settings = new Settings
{
Version = Settings.CURRENT_VERSION,
Background = new LinearGradient(new GradientStop(0.5, new Color(64, 0, 0, 0)))
};
_taskbarIcon.ShowBalloonTip("Keyboard Audio-Visualizer is starting in the tray!", "Click on the icon to open the configuration.", BalloonIcon.Info);
}
else if (settings.Version != Settings.CURRENT_VERSION)
ConfigurationUpdates.PerformOn(settings);
ApplicationManager.Instance.Settings = settings;
AudioProcessor.Initialize();
AudioVisualizationFactory.Initialize();
ApplicationManager.Instance.InitializeDevices();
}
catch (Exception ex)
@ -62,7 +84,8 @@ namespace KeyboardAudioVisualizer
{
base.OnExit(e);
SerializationHelper.SaveObjectToFile(ApplicationManager.Instance.Settings, PATH_SETTINGS);
File.WriteAllText(PATH_SETTINGS, JsonConvert.SerializeObject(ApplicationManager.Instance.Settings, new ColorSerializer()));
ConfigurationMigrator.CleanupOldConfigs();
}
#endregion

View File

@ -1,7 +1,9 @@
using System.Windows;
using System.Collections.Generic;
using System.Windows;
using KeyboardAudioVisualizer.AudioProcessing;
using KeyboardAudioVisualizer.Brushes;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.Configuration;
using KeyboardAudioVisualizer.Decorators;
using KeyboardAudioVisualizer.Helper;
using KeyboardAudioVisualizer.UI;
using RGB.NET.Brushes;
@ -10,12 +12,20 @@ using RGB.NET.Core;
using RGB.NET.Devices.CoolerMaster;
using RGB.NET.Devices.Corsair;
using RGB.NET.Devices.Logitech;
using RGB.NET.Devices.Novation;
using RGB.NET.Devices.Razer;
using RGB.NET.Devices.SteelSeries;
using RGB.NET.Groups;
using Point = RGB.NET.Core.Point;
using GetDecoratorFunc = System.Func<KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider.VisualizationType, KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider.IVisualizationProvider, RGB.NET.Core.IBrushDecorator>;
namespace KeyboardAudioVisualizer
{
public class ApplicationManager
{
#region Constants
#endregion
#region Properties & Fields
public static ApplicationManager Instance { get; } = new ApplicationManager();
@ -24,6 +34,12 @@ namespace KeyboardAudioVisualizer
public Settings Settings { get; set; }
public ObservableDictionary<VisualizationIndex, IVisualizationProvider> Visualizations { get; } = new ObservableDictionary<VisualizationIndex, IVisualizationProvider>();
private readonly Dictionary<VisualizationIndex, IEnumerable<(ILedGroup group, GetDecoratorFunc getDecoratorFunc)>> _groups = new Dictionary<VisualizationIndex, IEnumerable<(ILedGroup group, GetDecoratorFunc getDecoratorFunc)>>();
public TimerUpdateTrigger UpdateTrigger { get; } = new TimerUpdateTrigger();
#endregion
#region Commands
@ -48,78 +64,133 @@ namespace KeyboardAudioVisualizer
{
RGBSurface surface = RGBSurface.Instance;
surface.UpdateFrequency = 1 / MathHelper.Clamp(Settings.UpdateRate, 1, 40);
surface.UpdateMode = UpdateMode.Continuous;
UpdateTrigger.UpdateFrequency = 1.0 / MathHelper.Clamp(Settings.UpdateRate, 1, 60);
surface.RegisterUpdateTrigger(UpdateTrigger);
surface.LoadDevices(CorsairDeviceProvider.Instance);
surface.LoadDevices(LogitechDeviceProvider.Instance);
surface.LoadDevices(CoolerMasterDeviceProvider.Instance);
LoadDevices(surface, CorsairDeviceProvider.Instance);
LoadDevices(surface, CoolerMasterDeviceProvider.Instance);
LoadDevices(surface, NovationDeviceProvider.Instance);
LoadDevices(surface, RazerDeviceProvider.Instance);
LoadDevices(surface, LogitechDeviceProvider.Instance);
LoadDevices(surface, SteelSeriesDeviceProvider.Instance);
surface.AlignDevices();
ILedGroup background = new ListLedGroup(surface.Leds);
background.Brush = new SolidColorBrush(new Color(64, 0, 0, 0)); //TODO DarthAffe 06.08.2017: A-Channel gives some kind of blur - settings!
background.Brush = new LinearGradientBrush(Settings.Background);
//TODO DarthAffe 03.08.2017: Changeable, Settings etc.
foreach (IRGBDevice device in surface.Devices)
LinearGradient primaryGradient = Settings[VisualizationIndex.Primary].Gradient;
LinearGradient secondaryGradient = Settings[VisualizationIndex.Secondary].Gradient;
LinearGradient tertiaryGradient = Settings[VisualizationIndex.Tertiary].Gradient;
List<(ILedGroup, GetDecoratorFunc)> primaryGroups = new List<(ILedGroup, GetDecoratorFunc)>();
List<(ILedGroup, GetDecoratorFunc)> secondaryGroups = new List<(ILedGroup, GetDecoratorFunc)>();
List<(ILedGroup, GetDecoratorFunc)> tertiaryGroups = new List<(ILedGroup, GetDecoratorFunc)>();
foreach (IRGBDevice device in RGBSurface.Instance.Devices)
switch (device.DeviceInfo.DeviceType)
{
case RGBDeviceType.Keyboard:
//TODO DarthAffe 05.08.2017: Lighbar-support has to be better in RGB.NET
if (device[new CorsairLedId(device, CorsairLedIds.Lightbar1)] != null)
case RGBDeviceType.Keypad:
case RGBDeviceType.LedMatrix:
ListLedGroup primary = new ListLedGroup(device);
LightbarSpecialPart lightbar = device.GetSpecialDevicePart<LightbarSpecialPart>();
if (lightbar != null)
{
ILedGroup lightbarLeft = new ListLedGroup(new CorsairLedId(device, CorsairLedIds.Lightbar1), new CorsairLedId(device, CorsairLedIds.Lightbar2),
new CorsairLedId(device, CorsairLedIds.Lightbar3), new CorsairLedId(device, CorsairLedIds.Lightbar4),
new CorsairLedId(device, CorsairLedIds.Lightbar5), new CorsairLedId(device, CorsairLedIds.Lightbar6),
new CorsairLedId(device, CorsairLedIds.Lightbar7), new CorsairLedId(device, CorsairLedIds.Lightbar8),
new CorsairLedId(device, CorsairLedIds.Lightbar9));
ILedGroup lightbarCenter = new ListLedGroup(new CorsairLedId(device, CorsairLedIds.Lightbar10));
ILedGroup lightbarRight = new ListLedGroup(new CorsairLedId(device, CorsairLedIds.Lightbar11), new CorsairLedId(device, CorsairLedIds.Lightbar12),
new CorsairLedId(device, CorsairLedIds.Lightbar13), new CorsairLedId(device, CorsairLedIds.Lightbar14),
new CorsairLedId(device, CorsairLedIds.Lightbar15), new CorsairLedId(device, CorsairLedIds.Lightbar16),
new CorsairLedId(device, CorsairLedIds.Lightbar17), new CorsairLedId(device, CorsairLedIds.Lightbar18),
new CorsairLedId(device, CorsairLedIds.Lightbar19));
ListLedGroup primary = new ListLedGroup(device);
primary.RemoveLeds(lightbarLeft.GetLeds());
primary.RemoveLeds(lightbarCenter.GetLeds());
primary.RemoveLeds(lightbarRight.GetLeds());
primary.RemoveLeds(lightbar.Leds);
IGradient keyboardLevelGradient = new LinearGradient(new GradientStop(0, new Color(0, 0, 255)), new GradientStop(1, new Color(255, 0, 0)));
lightbarLeft.Brush = new LevelBarBrush(AudioProcessor.Instance.TertiaryVisualizationProvider, keyboardLevelGradient, LevelBarDirection.Left, 0);
lightbarRight.Brush = new LevelBarBrush(AudioProcessor.Instance.TertiaryVisualizationProvider, keyboardLevelGradient, LevelBarDirection.Right, 1);
lightbarCenter.Brush = new BeatBrush(AudioProcessor.Instance.SecondaryVisualizationProvider, new Color(255, 255, 255));
ILedGroup lightbarLeft = new ListLedGroup(lightbar.Left);
lightbarLeft.Brush = new LinearGradientBrush(new Point(1.0, 0.5), new Point(0.0, 0.5), tertiaryGradient);
tertiaryGroups.Add((lightbarLeft, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Left, 0)));
primary.Brush = new FrequencyBarsBrush(AudioProcessor.Instance.PrimaryVisualizationProvider, new RainbowGradient(300, -14));
ILedGroup lightbarRight = new ListLedGroup(lightbar.Right);
lightbarRight.Brush = new LinearGradientBrush(tertiaryGradient);
tertiaryGroups.Add((lightbarRight, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Right, 1)));
ILedGroup lightbarCenter = new ListLedGroup(lightbar.Center);
lightbarCenter.Brush = new LinearGradientBrush(secondaryGradient);
secondaryGroups.Add((lightbarCenter, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer)));
}
else
new ListLedGroup(device).Brush = new FrequencyBarsBrush(AudioProcessor.Instance.PrimaryVisualizationProvider, new RainbowGradient(300, -14));
//new ListLedGroup(device).Brush = new BeatBrush(AudioProcessor.Instance.PrimaryVisualizationProvider, new Color(255, 255, 255));
//{
// ILedGroup left1 = new RectangleLedGroup(new Rectangle(device.Location.X, device.Location.Y, device.Size.Width / 2.0, device.Size.Height));
// ILedGroup right1 = new RectangleLedGroup(new Rectangle(device.Location.X + (device.Size.Width / 2.0), device.Location.Y, device.Size.Width / 2.0, device.Size.Height));
// IGradient levelGradient = new LinearGradient(new GradientStop(0, new Color(0, 0, 255)), new GradientStop(1, new Color(255, 0, 0)));
// left1.Brush = new LevelBarBrush(AudioProcessor.Instance.TertiaryVisualizationProvider, levelGradient, LevelBarDirection.Left, 0);
// right1.Brush = new LevelBarBrush(AudioProcessor.Instance.TertiaryVisualizationProvider, levelGradient, LevelBarDirection.Right, 1);
//}
primary.Brush = new LinearGradientBrush(primaryGradient);
primaryGroups.Add((primary, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Horizontal, 0, primaryGradient)));
break;
case RGBDeviceType.Mousepad:
case RGBDeviceType.LedStripe:
case RGBDeviceType.HeadsetStand:
ILedGroup left = new RectangleLedGroup(new Rectangle(device.Location.X, device.Location.Y, device.Size.Width / 2.0, device.Size.Height));
ILedGroup right = new RectangleLedGroup(new Rectangle(device.Location.X + (device.Size.Width / 2.0), device.Location.Y, device.Size.Width / 2.0, device.Size.Height));
left.Brush = new LinearGradientBrush(new Point(0.5, 1), new Point(0.5, 0), tertiaryGradient);
tertiaryGroups.Add((left, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Top, 0)));
IGradient mousepadLevelGradient = new LinearGradient(new GradientStop(0, new Color(0, 0, 255)), new GradientStop(1, new Color(255, 0, 0)));
left.Brush = new LevelBarBrush(AudioProcessor.Instance.TertiaryVisualizationProvider, mousepadLevelGradient, LevelBarDirection.Top, 0);
right.Brush = new LevelBarBrush(AudioProcessor.Instance.TertiaryVisualizationProvider, mousepadLevelGradient, LevelBarDirection.Top, 1);
ILedGroup right = new RectangleLedGroup(new Rectangle(device.Location.X + (device.Size.Width / 2.0), device.Location.Y, device.Size.Width / 2.0, device.Size.Height));
right.Brush = new LinearGradientBrush(new Point(0.5, 1), new Point(0.5, 0), tertiaryGradient);
tertiaryGroups.Add((right, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Top, 1)));
break;
case RGBDeviceType.Mouse:
case RGBDeviceType.Headset:
case RGBDeviceType.Speaker:
case RGBDeviceType.Fan:
case RGBDeviceType.GraphicsCard:
case RGBDeviceType.DRAM:
case RGBDeviceType.Mainboard:
ILedGroup deviceGroup = new ListLedGroup(device);
deviceGroup.Brush = new BeatBrush(AudioProcessor.Instance.SecondaryVisualizationProvider, new Color(255, 255, 255));
deviceGroup.Brush = new LinearGradientBrush(secondaryGradient);
secondaryGroups.Add((deviceGroup, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer)));
break;
}
surface.Updating += args => AudioProcessor.Instance.Update();
_groups[VisualizationIndex.Primary] = primaryGroups;
_groups[VisualizationIndex.Secondary] = secondaryGroups;
_groups[VisualizationIndex.Tertiary] = tertiaryGroups;
ApplyVisualization(VisualizationIndex.Primary, Settings[VisualizationIndex.Primary].SelectedVisualization);
ApplyVisualization(VisualizationIndex.Secondary, Settings[VisualizationIndex.Secondary].SelectedVisualization);
ApplyVisualization(VisualizationIndex.Tertiary, Settings[VisualizationIndex.Tertiary].SelectedVisualization);
surface.Updating += args => AudioVisualizationFactory.Instance.Update();
}
private void LoadDevices(RGBSurface surface, IRGBDeviceProvider deviceProvider)
{
surface.LoadDevices(deviceProvider, RGBDeviceType.Keyboard | RGBDeviceType.LedMatrix
| RGBDeviceType.Mousepad | RGBDeviceType.LedStripe
| RGBDeviceType.Mouse | RGBDeviceType.Headset
| RGBDeviceType.HeadsetStand);
}
//TODO DarthAffe 12.09.2017: This is just a big mess - is this worth to rework before arge?
public void ApplyVisualization(VisualizationIndex visualizationIndex, VisualizationType visualizationType)
{
IVisualizationProvider visualizer = AudioVisualizationFactory.Instance.CreateVisualizationProvider(visualizationIndex, visualizationType);
Visualizations[visualizationIndex] = visualizer;
foreach ((ILedGroup group, GetDecoratorFunc getDecoratorFunc) in _groups[visualizationIndex])
{
group.Brush.RemoveAllDecorators();
if (visualizer != null)
{
IBrushDecorator decorator = getDecoratorFunc(visualizationType, visualizer);
if (decorator != null)
group.Brush.AddDecorator(decorator);
}
}
}
private IBrushDecorator CreateDecorator(VisualizationType visualizationType, IVisualizationProvider visualizationProvider, LevelBarDirection direction = LevelBarDirection.Top, int dataIndex = 0, LinearGradient gradient = null)
{
if (visualizationType == VisualizationType.FrequencyBars)
return new FrequencyBarsDecorator(visualizationProvider);
if (visualizationType == VisualizationType.Level)
return new LevelBarDecorator(visualizationProvider, direction, dataIndex, gradient);
if (visualizationType == VisualizationType.Beat)
return new BeatDecorator(visualizationProvider);
return null;
}
private void OpenConfiguration()
@ -130,8 +201,8 @@ namespace KeyboardAudioVisualizer
private void Exit()
{
RGBSurface.Instance.Dispose();
AudioProcessor.Instance.Dispose();
try { AudioVisualizationFactory.Instance?.Dispose(); } catch { }
try { RGBSurface.Instance?.Dispose(); } catch { }
Application.Current.Shutdown();
}

View File

@ -0,0 +1,111 @@
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace KeyboardAudioVisualizer.Attached
{
public static class SliderValue
{
#region Properties & Fields
// ReSharper disable InconsistentNaming
public static readonly DependencyProperty UnitProperty = DependencyProperty.RegisterAttached(
"Unit", typeof(string), typeof(SliderValue), new PropertyMetadata(default(string)));
public static void SetUnit(DependencyObject element, string value) => element.SetValue(UnitProperty, value);
public static string GetUnit(DependencyObject element) => (string)element.GetValue(UnitProperty);
public static readonly DependencyProperty IsShownProperty = DependencyProperty.RegisterAttached(
"IsShown", typeof(bool), typeof(SliderValue), new PropertyMetadata(default(bool), IsShownChanged));
public static void SetIsShown(DependencyObject element, bool value) => element.SetValue(IsShownProperty, value);
public static bool GetIsShown(DependencyObject element) => (bool)element.GetValue(IsShownProperty);
public static readonly DependencyProperty BorderBrushProperty = DependencyProperty.RegisterAttached(
"BorderBrush", typeof(Brush), typeof(SliderValue), new PropertyMetadata(default(Brush)));
public static void SetBorderBrush(DependencyObject element, Brush value) => element.SetValue(BorderBrushProperty, value);
public static Brush GetBorderBrush(DependencyObject element) => (Brush)element.GetValue(BorderBrushProperty);
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.RegisterAttached(
"Background", typeof(Brush), typeof(SliderValue), new PropertyMetadata(default(Brush)));
public static void SetBackground(DependencyObject element, Brush value) => element.SetValue(BackgroundProperty, value);
public static Brush GetBackground(DependencyObject element) => (Brush)element.GetValue(BackgroundProperty);
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.RegisterAttached(
"Foreground", typeof(Brush), typeof(SliderValue), new PropertyMetadata(default(Brush)));
public static void SetForeground(DependencyObject element, Brush value) => element.SetValue(ForegroundProperty, value);
public static Brush GetForeground(DependencyObject element) => (Brush)element.GetValue(ForegroundProperty);
public static readonly DependencyProperty FontProperty = DependencyProperty.RegisterAttached(
"Font", typeof(FontFamily), typeof(SliderValue), new PropertyMetadata(default(FontFamily)));
public static void SetFont(DependencyObject element, FontFamily value) => element.SetValue(FontProperty, value);
public static FontFamily GetFont(DependencyObject element) => (FontFamily)element.GetValue(FontProperty);
public static readonly DependencyProperty FontSizeProperty = DependencyProperty.RegisterAttached(
"FontSize", typeof(double), typeof(SliderValue), new PropertyMetadata(default(double)));
public static void SetFontSize(DependencyObject element, double value) => element.SetValue(FontSizeProperty, value);
public static double GetFontSize(DependencyObject element) => (double)element.GetValue(FontSizeProperty);
// ReSharper enable InconsistentNaming
#endregion
#region Methods
private static void IsShownChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (!(dependencyObject is Slider slider)) return;
if (dependencyPropertyChangedEventArgs.NewValue as bool? == true)
{
slider.MouseEnter += SliderOnMouseEnter;
slider.MouseLeave += SliderOnMouseLeave;
}
else
{
slider.MouseEnter -= SliderOnMouseEnter;
slider.MouseLeave -= SliderOnMouseLeave;
RemoveAdorner(slider);
}
}
private static void SliderOnMouseEnter(object sender, MouseEventArgs mouseEventArgs)
{
if (!(sender is Slider slider)) return;
AdornerLayer.GetAdornerLayer(slider)?.Add(new SliderValueAdorner(slider, GetUnit(slider))
{
BorderBrush = GetBorderBrush(slider),
Background = GetBackground(slider),
Foreground = GetForeground(slider),
Font = GetFont(slider),
FontSize = GetFontSize(slider)
});
}
private static void SliderOnMouseLeave(object sender, MouseEventArgs mouseEventArgs)
{
if (!(sender is Slider slider)) return;
RemoveAdorner(slider);
}
private static void RemoveAdorner(Slider slider)
{
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(slider);
Adorner adorner = adornerLayer?.GetAdorners(slider)?.FirstOrDefault(x => x is SliderValueAdorner);
if (adorner != null)
{
adornerLayer.Remove(adorner);
(adorner as SliderValueAdorner)?.Cleanup();
}
}
#endregion
}
}

View File

@ -0,0 +1,99 @@
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using Point = System.Windows.Point;
namespace KeyboardAudioVisualizer.Attached
{
public class SliderValueAdorner : System.Windows.Documents.Adorner
{
#region Properties & Fields
private readonly string _unit;
private readonly Slider _slider;
private readonly Thumb _thumb;
private readonly RepeatButton _decreaseRepeatButton;
public Brush BorderBrush { get; set; } = Brushes.Black;
public Brush Background { get; set; } = Brushes.Black;
public Brush Foreground { get; set; } = Brushes.White;
public FontFamily Font { get; set; } = new FontFamily("Verdana");
public double FontSize { get; set; } = 14;
#endregion
#region Constructors
public SliderValueAdorner(UIElement adornedElement, string unit)
: base(adornedElement)
{
this._unit = unit;
_slider = (Slider)adornedElement;
Track track = (Track)_slider.Template.FindName("PART_Track", _slider);
_thumb = track.Thumb;
_decreaseRepeatButton = track.DecreaseRepeatButton;
_decreaseRepeatButton.SizeChanged += OnButtonSizeChanged;
}
#endregion
#region Methods
public void Cleanup()
{
_decreaseRepeatButton.SizeChanged -= OnButtonSizeChanged;
}
private void OnButtonSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs) => InvalidateVisual();
protected override void OnRender(DrawingContext drawingContext)
{
double offset = _decreaseRepeatButton.ActualWidth + (_thumb.ActualWidth / 2.0);
FormattedText text = new FormattedText(GetText(), CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface(Font, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal), FontSize, Foreground);
Geometry border = CreateBorder(offset, text.Width, text.Height);
drawingContext.DrawGeometry(Background, new Pen(BorderBrush, 1), border);
drawingContext.DrawText(text, new Point(offset - (text.Width / 2.0), -26));
}
private string GetText()
{
string valueText = _slider.Value.ToString();
if (!string.IsNullOrWhiteSpace(_unit))
valueText += " " + _unit;
return valueText;
}
private Geometry CreateBorder(double offset, double width, double height)
{
double halfWidth = width / 2.0;
PathGeometry borderGeometry = new PathGeometry();
PathFigure border = new PathFigure
{
StartPoint = new Point(offset, 0),
IsClosed = true,
IsFilled = true
};
border.Segments.Add(new LineSegment(new Point(offset + 4, -6), true));
border.Segments.Add(new LineSegment(new Point(offset + 4 + halfWidth, -6), true));
border.Segments.Add(new LineSegment(new Point(offset + 4 + halfWidth, -10 - height), true));
border.Segments.Add(new LineSegment(new Point(offset - 4 - halfWidth, -10 - height), true));
border.Segments.Add(new LineSegment(new Point(offset - 4 - halfWidth, -6), true));
border.Segments.Add(new LineSegment(new Point(offset - 4, -6), true));
borderGeometry.Figures.Add(border);
return borderGeometry;
}
#endregion
}
}

View File

@ -0,0 +1,22 @@
using System;
namespace KeyboardAudioVisualizer.Attributes
{
public class DisplayNameAttribute : Attribute
{
#region Properties & Fields
public string DisplayName { get; set; }
#endregion
#region Constructors
public DisplayNameAttribute(string displayName)
{
this.DisplayName = displayName;
}
#endregion
}
}

View File

@ -0,0 +1,23 @@
using System;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.Attributes
{
public class VisualizerForAttribute : Attribute
{
#region Properties & Fields
public RGBDeviceType VisualizerFor { get; set; }
#endregion
#region Constructors
public VisualizerForAttribute(RGBDeviceType visualizerFor)
{
this.VisualizerFor = visualizerFor;
}
#endregion
}
}

View File

@ -13,6 +13,8 @@ namespace KeyboardAudioVisualizer.AudioCapture
public int Size => _capacity;
public float? Prescale { get; set; } = null;
#endregion
#region Constructors
@ -29,51 +31,68 @@ namespace KeyboardAudioVisualizer.AudioCapture
#region Methods
public void Put(float left, float right)
{
_currentIndex++;
if (_currentIndex >= _capacity) _currentIndex = 0;
_bufferLeft[_currentIndex] = left;
_bufferRight[_currentIndex] = right;
}
public void Put(float[] src, int offset, int count)
{
lock (_bufferLeft)
if ((count & 1) != 0) return; // we expect stereo-data to be an even amount of values
if (count > _capacity)
{
if ((count & 1) != 0) return; // we expect stereo-data to be an even amount of values
offset += count - _capacity;
count = _capacity;
}
if (count > _capacity)
for (int i = 0; i < count; i += 2)
{
_currentIndex++;
if (_currentIndex >= _capacity) _currentIndex = 0;
if (Prescale.HasValue)
{
offset += count - _capacity;
count = _capacity;
_bufferLeft[_currentIndex] = src[offset + i] / Prescale.Value;
_bufferRight[_currentIndex] = src[offset + i + 1] / Prescale.Value;
}
for (int i = 0; i < count; i += 2)
else
{
_currentIndex++;
if (_currentIndex >= _capacity) _currentIndex = 0;
_bufferLeft[_currentIndex] = src[offset + i];
_bufferRight[_currentIndex] = src[offset + i + 1];
}
}
}
public void CopyLeftInto(ref float[] data, int offset)
public void CopyLeftInto(ref float[] data, int offset) => CopyLeftInto(ref data, offset, Math.Min(data.Length, _capacity));
public void CopyLeftInto(ref float[] data, int offset, int count)
{
lock (_bufferLeft)
for (int i = 0; i < _capacity; i++)
data[offset + i] = _bufferLeft[(_currentIndex + i) % _capacity];
int bufferOffset = _capacity - count;
for (int i = 0; i < count; i++)
data[offset + i] = _bufferLeft[(_currentIndex + (bufferOffset + i)) % _capacity];
}
public void CopyRightInto(ref float[] data, int offset)
public void CopyRightInto(ref float[] data, int offset) => CopyRightInto(ref data, offset, Math.Min(data.Length, _capacity));
public void CopyRightInto(ref float[] data, int offset, int count)
{
lock (_bufferLeft)
for (int i = 0; i < _capacity; i++)
data[offset + i] = _bufferRight[(_currentIndex + i) % _capacity];
int bufferOffset = _capacity - count;
for (int i = 0; i < count; i++)
data[offset + i] = _bufferRight[(_currentIndex + (bufferOffset + i)) % _capacity];
}
public void CopyMixInto(ref float[] data, int offset)
public void CopyMixInto(ref float[] data, int offset) => CopyMixInto(ref data, offset, Math.Min(data.Length, _capacity));
public void CopyMixInto(ref float[] data, int offset, int count)
{
lock (_bufferLeft)
for (int i = 0; i < _capacity; i++)
{
int index = (_currentIndex + i) % _capacity;
data[offset + i] = (_bufferLeft[index] + _bufferRight[index]) / 2f;
}
int bufferOffset = _capacity - count;
for (int i = 0; i < count; i++)
{
int index = (_currentIndex + (bufferOffset + i)) % _capacity;
data[offset + i] = (_bufferLeft[index] + _bufferRight[index]) / 2f;
}
}
#endregion

View File

@ -1,4 +1,6 @@
using CSCore;
using System;
using CSCore;
using CSCore.CoreAudioAPI;
using CSCore.SoundIn;
using CSCore.Streams;
@ -10,11 +12,12 @@ namespace KeyboardAudioVisualizer.AudioCapture
private WasapiCapture _capture;
private SoundInSource _soundInSource;
private IWaveSource _source;
private SingleBlockNotificationStream _stream;
private readonly float[] _readBuffer = new float[2048];
private AudioEndpointVolume _audioEndpointVolume;
public int SampleRate => _soundInSource?.WaveFormat?.SampleRate ?? -1;
public float MasterVolume => _audioEndpointVolume.MasterVolumeLevelScalar;
#endregion
@ -28,15 +31,34 @@ namespace KeyboardAudioVisualizer.AudioCapture
public void Initialize()
{
_capture = new WasapiLoopbackCapture();
MMDevice captureDevice = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console);
WaveFormat deviceFormat = captureDevice.DeviceFormat;
_audioEndpointVolume = AudioEndpointVolume.FromDevice(captureDevice);
//DarthAffe 07.02.2018: This is a really stupid workaround to (hopefully) finally fix the surround driver issues
for (int i = 1; i < 13; i++)
try { _capture = new WasapiLoopbackCapture(100, new WaveFormat(deviceFormat.SampleRate, deviceFormat.BitsPerSample, i)); } catch { /* We're just trying ... */ }
if (_capture == null)
throw new NullReferenceException("Failed to initialize WasapiLoopbackCapture");
_capture.Initialize();
_soundInSource = new SoundInSource(_capture) { FillWithZeros = false };
_source = _soundInSource.WaveFormat.SampleRate == 44100
? _soundInSource.ToStereo()
: _soundInSource.ChangeSampleRate(44100).ToStereo();
_stream = _soundInSource.WaveFormat.SampleRate == 44100
? new SingleBlockNotificationStream(_soundInSource.ToStereo().ToSampleSource())
: new SingleBlockNotificationStream(_soundInSource.ChangeSampleRate(44100).ToStereo().ToSampleSource());
_stream = new SingleBlockNotificationStream(_source.ToSampleSource());
_stream.SingleBlockRead += StreamOnSingleBlockRead;
_soundInSource.DataAvailable += OnSoundDataAvailable;
_source = _stream.ToWaveSource();
byte[] buffer = new byte[_source.WaveFormat.BytesPerSecond / 2];
_soundInSource.DataAvailable += (s, aEvent) =>
{
while ((_source.Read(buffer, 0, buffer.Length)) > 0) ;
};
_capture.Start();
}
@ -47,12 +69,8 @@ namespace KeyboardAudioVisualizer.AudioCapture
_capture?.Dispose();
}
private void OnSoundDataAvailable(object sender, DataAvailableEventArgs dataAvailableEventArgs)
{
int readCount;
while ((readCount = _stream.Read(_readBuffer, 0, _readBuffer.Length)) > 0)
DataAvailable?.Invoke(_readBuffer, 0, readCount);
}
private void StreamOnSingleBlockRead(object sender, SingleBlockReadEventArgs singleBlockReadEventArgs)
=> DataAvailable?.Invoke(singleBlockReadEventArgs.Left, singleBlockReadEventArgs.Right);
#endregion
}

View File

@ -2,11 +2,12 @@
namespace KeyboardAudioVisualizer.AudioCapture
{
public delegate void AudioData(float[] data, int offset, int count);
public delegate void AudioData(float left, float right);
public interface IAudioInput : IDisposable
{
int SampleRate { get; }
float MasterVolume { get; }
event AudioData DataAvailable;

View File

@ -0,0 +1,21 @@
namespace KeyboardAudioVisualizer.AudioProcessing
{
public abstract class AbstractAudioProcessor : IAudioProcessor
{
#region Properties & Fields
public bool IsActive { get; set; } = true;
#endregion
#region Methods
public abstract void Initialize();
public abstract void Update();
public virtual void Dispose() { }
#endregion
}
}

View File

@ -1,82 +0,0 @@
using System;
using KeyboardAudioVisualizer.AudioCapture;
using KeyboardAudioVisualizer.AudioProcessing.Equalizer;
using KeyboardAudioVisualizer.AudioProcessing.Spectrum;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
namespace KeyboardAudioVisualizer.AudioProcessing
{
public class AudioProcessor : IDisposable
{
#region Properties & Fields
public static AudioProcessor Instance { get; private set; }
private AudioBuffer _audioBuffer;
private IAudioInput _audioInput;
private ISpectrumProvider _spectrumProvider;
public IVisualizationProvider PrimaryVisualizationProvider { get; private set; }
public IVisualizationProvider SecondaryVisualizationProvider { get; private set; }
public IVisualizationProvider TertiaryVisualizationProvider { get; private set; }
#endregion
#region Constructors
private AudioProcessor() { }
#endregion
#region Methods
public void Update()
{
_spectrumProvider.Update();
PrimaryVisualizationProvider?.Update();
SecondaryVisualizationProvider?.Update();
TertiaryVisualizationProvider?.Update();
}
public static void Initialize()
{
if (Instance != null) return;
Instance = new AudioProcessor();
Instance.InitializeInstance();
}
private void InitializeInstance()
{
_audioInput = new CSCoreAudioInput();
_audioInput.Initialize();
_audioBuffer = new AudioBuffer(4096); // Working with ~93ms -
_audioInput.DataAvailable += (data, offset, count) => _audioBuffer.Put(data, offset, count);
_spectrumProvider = new FourierSpectrumProvider(_audioBuffer);
_spectrumProvider.Initialize();
//TODO DarthAffe 13.08.2017: Refactore Settings-stuff to work with multiple providers
MultiBandEqualizer equalizer = new MultiBandEqualizer();
ApplicationManager.Instance.Settings.EqualizerConfiguration.LoadInto(equalizer);
equalizer.PropertyChanged += (sender, args) => ApplicationManager.Instance.Settings.EqualizerConfiguration.SaveFrom(equalizer);
PrimaryVisualizationProvider = new FrequencyBarsVisualizationProvider(ApplicationManager.Instance.Settings.FrequencyBarsVisualizationProviderConfiguration, _spectrumProvider) { Equalizer = equalizer };
//PrimaryVisualizationProvider = new BeatVisualizationProvider(new BeatVisualizationProviderConfiguration(), _spectrumProvider);
PrimaryVisualizationProvider.Initialize();
SecondaryVisualizationProvider = new BeatVisualizationProvider(ApplicationManager.Instance.Settings.BeatVisualizationProviderConfiguration, _spectrumProvider);
SecondaryVisualizationProvider.Initialize();
TertiaryVisualizationProvider = new LevelVisualizationProvider(ApplicationManager.Instance.Settings.LevelVisualizationProviderConfiguration, _audioBuffer);
TertiaryVisualizationProvider.Initialize();
}
public void Dispose() => _audioInput.Dispose();
#endregion
}
}

View File

@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using KeyboardAudioVisualizer.AudioCapture;
using KeyboardAudioVisualizer.AudioProcessing.Equalizer;
using KeyboardAudioVisualizer.AudioProcessing.Spectrum;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.Helper;
namespace KeyboardAudioVisualizer.AudioProcessing
{
public class AudioVisualizationFactory : IDisposable
{
#region Properties & Fields
public static AudioVisualizationFactory Instance { get; private set; }
private IAudioInput _audioInput;
private AudioBuffer _audioBuffer;
private readonly List<IAudioProcessor> _processors = new List<IAudioProcessor>();
#endregion
#region Constructors
private AudioVisualizationFactory() { }
#endregion
#region Methods
public void Update()
{
if (ApplicationManager.Instance.Settings.EnableAudioPrescale)
_audioBuffer.Prescale = _audioInput.MasterVolume;
else
_audioBuffer.Prescale = null;
foreach (IAudioProcessor processor in _processors.Where(x => x.IsActive))
processor.Update();
}
public static void Initialize()
{
if (Instance != null) return;
Instance = new AudioVisualizationFactory();
Instance.InitializeInstance();
}
private void InitializeInstance()
{
_audioInput = new CSCoreAudioInput();
_audioInput.Initialize();
_audioBuffer = new AudioBuffer(4096); // Working with ~93ms -
_audioInput.DataAvailable += (left, right) => _audioBuffer.Put(left, right);
_processors.Add(new FourierSpectrumProvider(_audioBuffer));
foreach (IAudioProcessor processor in _processors)
processor.Initialize();
}
private T GetAudioProcessor<T>() => (T)_processors.FirstOrDefault(x => x.GetType() == typeof(T));
public IVisualizationProvider CreateVisualizationProvider(VisualizationIndex visualizationIndex, VisualizationType visualizationType)
{
IVisualizationProvider visualizationProvider = default;
switch (visualizationType)
{
case VisualizationType.FrequencyBars:
MultiBandEqualizer equalizer = new MultiBandEqualizer();
ApplicationManager.Instance.Settings[visualizationIndex].EqualizerConfiguration.LoadInto(equalizer);
equalizer.PropertyChanged += (sender, args) => ApplicationManager.Instance.Settings[visualizationIndex].EqualizerConfiguration.SaveFrom(equalizer);
visualizationProvider = new FrequencyBarsVisualizationProvider(ApplicationManager.Instance.Settings[visualizationIndex].GetConfiguration<FrequencyBarsVisualizationProviderConfiguration>(visualizationType), GetAudioProcessor<FourierSpectrumProvider>()) { Equalizer = equalizer };
break;
case VisualizationType.Level:
visualizationProvider = new LevelVisualizationProvider(ApplicationManager.Instance.Settings[visualizationIndex].GetConfiguration<LevelVisualizationProviderConfiguration>(visualizationType), _audioBuffer);
break;
case VisualizationType.Beat:
visualizationProvider = new BeatVisualizationProvider(ApplicationManager.Instance.Settings[visualizationIndex].GetConfiguration<BeatVisualizationProviderConfiguration>(visualizationType), GetAudioProcessor<FourierSpectrumProvider>());
break;
}
visualizationProvider?.Initialize();
return visualizationProvider;
}
public void Dispose() => _audioInput.Dispose();
#endregion
}
}

View File

@ -1,7 +1,11 @@
namespace KeyboardAudioVisualizer.AudioProcessing
using System;
namespace KeyboardAudioVisualizer.AudioProcessing
{
public interface IAudioProcessor
public interface IAudioProcessor : IDisposable
{
bool IsActive { get; set; }
void Initialize();
void Update();
}

View File

@ -5,7 +5,7 @@ using MathNet.Numerics.IntegralTransforms;
namespace KeyboardAudioVisualizer.AudioProcessing.Spectrum
{
public class FourierSpectrumProvider : ISpectrumProvider
public class FourierSpectrumProvider : AbstractAudioProcessor, ISpectrumProvider
{
#region Properties & Fields
@ -31,7 +31,7 @@ namespace KeyboardAudioVisualizer.AudioProcessing.Spectrum
#region Methods
public void Initialize()
public override void Initialize()
{
_hamming = Window.Hamming(_audioBuffer.Size);
_sampleData = new float[_audioBuffer.Size];
@ -40,7 +40,7 @@ namespace KeyboardAudioVisualizer.AudioProcessing.Spectrum
_spectrum = new float[_usableDataLength];
}
public void Update()
public override void Update()
{
_audioBuffer.CopyMixInto(ref _sampleData, 0);

View File

@ -2,7 +2,6 @@
using KeyboardAudioVisualizer.AudioProcessing.Equalizer;
using KeyboardAudioVisualizer.AudioProcessing.Spectrum;
using KeyboardAudioVisualizer.Configuration;
using KeyboardAudioVisualizer.Helper;
namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
{
@ -79,7 +78,7 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
#endregion
public class FrequencyBarsVisualizationProvider : IVisualizationProvider
public class FrequencyBarsVisualizationProvider : AbstractAudioProcessor, IVisualizationProvider
{
#region Properties & Fields
@ -109,7 +108,7 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
#region Methods
public void Initialize() => RecalculateConfigValues(null);
public override void Initialize() => RecalculateConfigValues(null);
private void RecalculateConfigValues(string changedPropertyName)
{
@ -123,7 +122,7 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
_emphasiseFactor = (0.75 * (1 + _configuration.EmphasisePeaks));
}
public void Update()
public override void Update()
{
ISpectrum spectrum = GetSpectrum();
if (spectrum == null) return;

View File

@ -2,9 +2,12 @@
namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
{
public interface IVisualizationProvider : IAudioProcessor
public interface IVisualizationProvider
{
IConfiguration Configuration { get; }
float[] VisualizationData { get; }
void Initialize();
void Update();
}
}

View File

@ -1,6 +1,4 @@
using System;
using System.Linq;
using KeyboardAudioVisualizer.AudioProcessing.Spectrum;
using KeyboardAudioVisualizer.AudioProcessing.Spectrum;
using KeyboardAudioVisualizer.Configuration;
using KeyboardAudioVisualizer.Helper;
@ -15,48 +13,14 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
#endregion
// Port of https://github.com/kctess5/Processing-Beat-Detection
public class BeatVisualizationProvider : IVisualizationProvider
public class BeatVisualizationProvider : AbstractAudioProcessor, IVisualizationProvider
{
#region Properties & Fields
private readonly BeatVisualizationProviderConfiguration _configuration;
private readonly ISpectrumProvider _specturProvider;
private int _beatBands = 30; //Number of bands to montiter, higher for more accuracy, lower for speed
private int _longTermAverageSamples = 60; //gets average volume over a period of time
private int _shortTermAverageSamples = 1; //average volume over a shorter "instantanious" time
private int _deltaArraySamples = 300; //number of energy deltas between long & short average to sum together
private int _beatAverageSamples = 100;
private int _beatCounterArraySamples = 400;
private int _maxTime = 200;
private float _predictiveInfluenceConstant = 0.1f;
private float _predictiveInfluence;
private int _cyclePerBeatIntensity;
private float[][] _deltaArray;
private float[][] _shortAverageArray;
private float[][] _longAverageArray;
private float[] _globalAverageArray;
private int[] _beatCounterArray;
private int[] _beatSpread;
private int _beatCounterPosition;
private int _cyclesPerBeat;
private int _longPosition;
private int _shortPosition;
private int _deltaPosition;
private int[] _count;
private float[] _totalLong;
private float[] _totalShort;
private float[] _delta;
private float[] _c;
private int _beat;
private int _beatCounter;
private float[] _beatAverage;
private float _totalBeat;
private int _beatPosition;
private float _totalGlobal;
private float _threshold;
private float _standardDeviation;
private RingBuffer[] _history;
public IConfiguration Configuration => _configuration;
public float[] VisualizationData { get; } = new float[1];
@ -75,183 +39,32 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
#region Methods
public void Initialize()
public override void Initialize()
{
_deltaArray = new float[_deltaArraySamples][];
for (int i = 0; i < _deltaArray.Length; i++)
_deltaArray[i] = new float[_beatBands];
_shortAverageArray = new float[_shortTermAverageSamples][];
for (int i = 0; i < _shortAverageArray.Length; i++)
_shortAverageArray[i] = new float[_beatBands];
_longAverageArray = new float[_longTermAverageSamples / _shortTermAverageSamples][];
for (int i = 0; i < _longAverageArray.Length; i++)
_longAverageArray[i] = new float[_beatBands];
_globalAverageArray = new float[_longTermAverageSamples];
_beatCounterArray = new int[_beatCounterArraySamples];
_beatSpread = new int[_maxTime];
_count = new int[_beatBands];
_totalLong = new float[_beatBands];
_totalShort = new float[_beatBands];
_delta = new float[_beatBands];
_c = new float[_beatBands]; //multiplier used to determain threshold
_beatAverage = new float[_beatAverageSamples];
_history = new RingBuffer[64];
for (int i = 0; i < _history.Length; i++)
_history[i] = new RingBuffer(32);
}
public void Update()
public override void Update()
{
ISpectrum spectrum = _specturProvider.GetLogarithmicSpectrum(60, minFrequency: 60);
VisualizationData[0] = 0;
if (_shortPosition >= _shortTermAverageSamples) _shortPosition = 0; //Resets incremental variables
if (_longPosition >= (_longTermAverageSamples / _shortTermAverageSamples)) _longPosition = 0;
if (_deltaPosition >= _deltaArraySamples) _deltaPosition = 0;
if (_beatPosition >= _beatAverageSamples) _beatPosition = 0;
/////////////////////////////////////Calculate short and long term array averages///////////////////////////////////////////////////////////////////////////////////////////////////////////
for (int i = 0; i < _beatBands; i++)
ISpectrum spectrum = _specturProvider.GetLogarithmicSpectrum(64);
for (int i = 0; i < 32; i++)
{
_shortAverageArray[_shortPosition][i] = spectrum[i].Average; //stores the average intensity between the freq. bounds to the short term array
_totalLong[i] = 0;
_totalShort[i] = 0;
for (int j = 0; j < (_longTermAverageSamples / _shortTermAverageSamples); j++)
_totalLong[i] += _longAverageArray[j][i]; //adds up all the values in both of these arrays, for averaging
for (int j = 0; j < _shortTermAverageSamples; j++)
_totalShort[i] += _shortAverageArray[j][i];
}
float currentEnergy = spectrum[i].Average;
float averageEnergy = _history[i].Average;
_history[i].Put(currentEnergy);
///////////////////////////////////////////Find wideband frequency average intensity/////////////////////////////////////////////////////////////////////////////////////////////////////
_totalGlobal = 0;
_globalAverageArray[_longPosition] = spectrum[0, 2000].Average(x => x.Average);
for (int j = 0; j < _longTermAverageSamples; j++)
_totalGlobal += _globalAverageArray[j];
_totalGlobal = _totalGlobal / _longTermAverageSamples;
//////////////////////////////////Populate long term average array//////////////////////////////////////////////////////////////////////////////////////////////////////////////
if ((_shortPosition % _shortTermAverageSamples) == 0)
{ //every time the short array is completely new it is added to long array
for (int i = 0; i < _beatBands; i++)
_longAverageArray[_longPosition][i] = _totalShort[i]; //increases speed of program, but is the same as if each individual value was stored in long array
_longPosition++;
}
/////////////////////////////////////////Find index of variation for each band///////////////////////////////////////////////////////////////////////////////////////////////////////
for (int i = 0; i < _beatBands; i++)
{
_totalLong[i] = _totalLong[i] / (float)_longTermAverageSamples / (float)_shortTermAverageSamples;
_delta[i] = 0;
_deltaArray[_deltaPosition][i] = (float)Math.Pow(Math.Abs(_totalLong[i] - _totalShort[i]), 2);
for (int j = 0; j < _deltaArraySamples; j++)
_delta[i] += _deltaArray[j][i];
_delta[i] /= _deltaArraySamples;
///////////////////////////////////////////Find local beats/////////////////////////////////////////////////////////////////////////////////////////////////////
_c[i] = (float)((1.3 + MathHelper.Clamp(Map(_delta[i], 0, 3000, 0, 0.4), 0, 0.4) //delta is usually bellow 2000
+ Map(MathHelper.Clamp(Math.Pow(_totalLong[i], 0.5), 0, 6), 0, 20, 0.3, 0) //possibly comment this out, adds weight to the lower end
+ Map(MathHelper.Clamp(_count[i], 0, 15), 0, 15, 1, 0))
- Map(MathHelper.Clamp(_count[i], 30, 200), 30, 200, 0, 0.75));
if ((_cyclePerBeatIntensity / _standardDeviation) > 3.5)
if (currentEnergy > (35 * averageEnergy))
{
_predictiveInfluence = (float)(_predictiveInfluenceConstant * (1 - (Math.Cos(_beatCounter * (Math.PI * Math.PI)) / _cyclesPerBeat)));
_predictiveInfluence *= (float)Map(MathHelper.Clamp(_cyclePerBeatIntensity / _standardDeviation, 3.5, 20), 3.5, 15, 1, 6);
if (_cyclesPerBeat > 10)
_c[i] += _predictiveInfluence;
VisualizationData[0] = 1;
break;
}
}
_beat = 0;
for (int i = 0; i < _beatBands; i++)
{
if ((_totalShort[i] > (_totalLong[i] * _c[i])) & (_count[i] > 7))
{ //If beat is detected
if ((_count[i] > 12) & (_count[i] < 200))
{
_beatCounterArray[_beatCounterPosition % _beatCounterArraySamples] = _count[i];
_beatCounterPosition++;
}
_count[i] = 0; //resets counter
}
}
/////////////////////////////////////////Figure out # of beats, and average///////////////////////////////////////////////////////////////////////////////////////////////////////
for (int i = 0; i < _beatBands; i++)
if (_count[i] < 2)
_beat++; //If there has been a recent beat in a band add to the global beat value
_beatAverage[_beatPosition] = _beat;
for (int j = 0; j < _beatAverageSamples; j++)
_totalBeat += _beatAverage[j];
_totalBeat = _totalBeat / _beatAverageSamples;
/////////////////////////////////////////////////find global beat///////////////////////////////////////////////////////////////////////////////////////////////
_c[0] = (float)(3.25 + Map(MathHelper.Clamp(_beatCounter, 0, 5), 0, 5, 5, 0));
if (_cyclesPerBeat > 10)
_c[0] = (float)(_c[0] + (0.75 * (1 - (Math.Cos(_beatCounter * (Math.PI * Math.PI)) / _cyclesPerBeat))));
_threshold = (float)MathHelper.Clamp((_c[0] * _totalBeat) + Map(MathHelper.Clamp(_totalGlobal, 0, 2), 0, 2, 4, 0), 5, 1000);
if ((_beat > _threshold) & (_beatCounter > 5))
{
VisualizationData[0] = 1;
_beatCounter = 0;
}
else
VisualizationData[0] = 0;
/////////////////////////////////////////////////////Calculate beat spreads///////////////////////////////////////////////////////////////////////////////////////////
//average = beatCounterArraySamples/200 !!!
for (int i = 0; i < _maxTime; i++)
_beatSpread[i] = 0;
for (int i = 0; i < _beatCounterArraySamples; i++)
_beatSpread[_beatCounterArray[i]]++;
_cyclesPerBeat = Mode(_beatCounterArray);
if (_cyclesPerBeat < 20)
_cyclesPerBeat *= 2;
_cyclePerBeatIntensity = _beatSpread.Max();
_standardDeviation = 0;
for (int i = 0; i < _maxTime; i++)
_standardDeviation += (float)Math.Pow((_beatCounterArraySamples / _maxTime) - _beatSpread[i], 2);
_standardDeviation = (float)Math.Pow(_standardDeviation / _maxTime, 0.5);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
_shortPosition++;
_deltaPosition++;
for (int i = 0; i < _beatBands; i++) _count[i]++;
_beatCounter++;
_beatPosition++;
}
private int Mode(int[] array)
{
int[] modeMap = new int[array.Length];
int maxEl = array[0];
int maxCount = 1;
for (int i = 0; i < array.Length; i++)
{
int el = array[i];
if (modeMap[el] == 0)
modeMap[el] = 1;
else
modeMap[el]++;
if (modeMap[el] > maxCount)
{
maxEl = el;
maxCount = modeMap[el];
}
}
return maxEl;
}
private static double Map(double value, double oldMin, double oldMax, double newMin, double newMax) => newMin + ((newMax - newMin) * ((value - oldMin) / (oldMax - oldMin)));
#endregion
}
}

View File

@ -46,7 +46,7 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
#endregion
public class LevelVisualizationProvider : IVisualizationProvider
public class LevelVisualizationProvider : AbstractAudioProcessor, IVisualizationProvider
{
#region Properties & Fields
@ -78,11 +78,11 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
#region Methods
public void Initialize()
public override void Initialize()
{
_sampleDataLeft = new float[_audioBuffer.Size];
_sampleDataRight = new float[_audioBuffer.Size];
_sampleDataMix = new float[_audioBuffer.Size];
_sampleDataLeft = new float[2048];
_sampleDataRight = new float[2048];
_sampleDataMix = new float[2048];
RecalculateConfigValues(null);
}
@ -112,7 +112,7 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
}
}
public void Update()
public override void Update()
{
_audioBuffer.CopyLeftInto(ref _sampleDataLeft, 0);
_audioBuffer.CopyRightInto(ref _sampleDataRight, 0);

View File

@ -0,0 +1,19 @@
using KeyboardAudioVisualizer.Attributes;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
{
public enum VisualizationType
{
None,
[VisualizerFor(RGBDeviceType.Keyboard | RGBDeviceType.LedMatrix)]
[DisplayName("Frequency Bars")]
FrequencyBars,
[VisualizerFor(RGBDeviceType.Keyboard | RGBDeviceType.LedMatrix | RGBDeviceType.LedStripe | RGBDeviceType.Mousepad)]
Level,
Beat,
}
}

View File

@ -1,36 +0,0 @@
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.Brushes
{
public class BeatBrush : AbstractBrush
{
#region Properties & Fields
private readonly IVisualizationProvider _visualizationProvider;
private readonly Color _color;
#endregion
#region Constructors
public BeatBrush(IVisualizationProvider visualizationProvider, Color color)
{
this._visualizationProvider = visualizationProvider;
this._color = color;
}
#endregion
#region Methods
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget)
{
Color color = new Color(_color);
color.APercent *= _visualizationProvider.VisualizationData[0];
return color;
}
#endregion
}
}

View File

@ -1,70 +0,0 @@
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using RGB.NET.Brushes;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.Brushes
{
public class LevelBarBrush : LinearGradientBrush
{
#region Properties & Fields
private readonly IVisualizationProvider _visualizationProvider;
public LevelBarDirection Direction { get; set; }
public int DataIndex { get; set; }
#endregion
#region Constructors
public LevelBarBrush(IVisualizationProvider visualizationProvider, IGradient gradient, LevelBarDirection direction, int dataIndex)
: base(gradient)
{
this._visualizationProvider = visualizationProvider;
this.Direction = direction;
this.DataIndex = dataIndex;
}
#endregion
#region Methods
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget)
{
double offset = CalculateOffset(rectangle, renderTarget);
return offset < _visualizationProvider.VisualizationData[DataIndex] ? Gradient.GetColor(offset) : Color.Transparent;
}
private double CalculateOffset(Rectangle rectangle, BrushRenderTarget renderTarget)
{
switch (Direction)
{
case LevelBarDirection.Left:
return (rectangle.Size.Width - renderTarget.Rectangle.Center.X) / rectangle.Size.Width;
case LevelBarDirection.Right:
return renderTarget.Rectangle.Center.X / rectangle.Size.Width;
case LevelBarDirection.Top:
return (rectangle.Size.Height - renderTarget.Rectangle.Center.Y) / rectangle.Size.Height;
case LevelBarDirection.Bottom:
return renderTarget.Rectangle.Center.Y / rectangle.Size.Height;
default:
return -1;
}
}
#endregion
}
#region Data
public enum LevelBarDirection
{
Left, Right, Top, Bottom
}
#endregion
}

View File

@ -0,0 +1,47 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.Configuration
{
public class ColorSerializer : JsonConverter
{
#region Methods
public override bool CanConvert(Type objectType) => objectType == typeof(Color);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (!(value is Color color)) return;
writer.WriteStartObject();
writer.WritePropertyName("A");
writer.WriteValue(color.A);
writer.WritePropertyName("R");
writer.WriteValue(color.R);
writer.WritePropertyName("G");
writer.WriteValue(color.G);
writer.WritePropertyName("B");
writer.WriteValue(color.B);
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jsonObject = JObject.Load(reader);
if (jsonObject.Property("A").Value.ToObject<double>() > 1.0) //DarthAffe 09.06.2019: Convert old Settings
return new Color(jsonObject.Property("A").Value.ToObject<byte>(),
jsonObject.Property("R").Value.ToObject<byte>(),
jsonObject.Property("G").Value.ToObject<byte>(),
jsonObject.Property("B").Value.ToObject<byte>());
else
return new Color(jsonObject.Property("A").Value.ToObject<double>(),
jsonObject.Property("R").Value.ToObject<double>(),
jsonObject.Property("G").Value.ToObject<double>(),
jsonObject.Property("B").Value.ToObject<double>());
}
#endregion
}
}

View File

@ -2,6 +2,6 @@
namespace KeyboardAudioVisualizer.Configuration
{
// DarthAffe 05.08.2017: Marker interface
public interface IConfiguration : INotifyPropertyChanged { }
public interface IConfiguration : INotifyPropertyChanged
{ }
}

View File

@ -1,24 +1,121 @@
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using System;
using System.Collections.Generic;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.Helper;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.Configuration
{
public class Settings
{
#region General
#region Constants
public double UpdateRate { get; set; } = 40.0;
public const int CURRENT_VERSION = 1;
#endregion
#region AudioProcessing
#region Properties & Fields
public int Version { get; set; } = 0;
public double UpdateRate { get; set; } = 40.0;
public bool EnableAudioPrescale { get; set; } = false;
public LinearGradient Background { get; set; }
public Dictionary<VisualizationIndex, VisualizationSettings> Visualizations { get; set; } = new Dictionary<VisualizationIndex, VisualizationSettings>();
public VisualizationSettings this[VisualizationIndex visualizationIndex]
{
get
{
if (!Visualizations.TryGetValue(visualizationIndex, out VisualizationSettings settings))
Visualizations[visualizationIndex] = (settings = new VisualizationSettings(visualizationIndex));
return settings;
}
}
#endregion
}
public class VisualizationSettings
{
#region Properties & Fields
public VisualizationType SelectedVisualization { get; set; }
public LinearGradient Gradient { get; set; }
public EqualizerConfiguration EqualizerConfiguration { get; set; } = new EqualizerConfiguration();
public FrequencyBarsVisualizationProviderConfiguration FrequencyBarsVisualizationProviderConfiguration { get; set; } = new FrequencyBarsVisualizationProviderConfiguration();
public FrequencyBarsVisualizationProviderConfiguration FrequencyBarsConfiguration { get; set; } = new FrequencyBarsVisualizationProviderConfiguration();
public LevelVisualizationProviderConfiguration LevelConfiguration { get; set; } = new LevelVisualizationProviderConfiguration();
public BeatVisualizationProviderConfiguration BeatConfiguration { get; set; } = new BeatVisualizationProviderConfiguration();
public LevelVisualizationProviderConfiguration LevelVisualizationProviderConfiguration { get; set; } = new LevelVisualizationProviderConfiguration();
public IConfiguration this[VisualizationType visualizationType]
{
get
{
switch (visualizationType)
{
case VisualizationType.None:
return null;
public BeatVisualizationProviderConfiguration BeatVisualizationProviderConfiguration { get; set; } = new BeatVisualizationProviderConfiguration();
case VisualizationType.FrequencyBars:
return FrequencyBarsConfiguration;
case VisualizationType.Level:
return LevelConfiguration;
case VisualizationType.Beat:
return BeatConfiguration;
default:
throw new ArgumentOutOfRangeException(nameof(visualizationType), visualizationType, null);
}
}
}
#endregion
#region Constructors
public VisualizationSettings(VisualizationIndex visualizationIndex)
{
switch (visualizationIndex)
{
case VisualizationIndex.Primary:
SelectedVisualization = VisualizationType.FrequencyBars;
Gradient = new LinearGradient(new GradientStop(0, HSVColor.Create(300, 1, 1)),
new GradientStop(0.20, HSVColor.Create(225, 1, 1)),
new GradientStop(0.35, HSVColor.Create(180, 1, 1)),
new GradientStop(0.50, HSVColor.Create(135, 1, 1)),
new GradientStop(0.65, HSVColor.Create(90, 1, 1)),
new GradientStop(0.80, HSVColor.Create(45, 1, 1)),
new GradientStop(0.95, HSVColor.Create(0, 1, 1)));
break;
case VisualizationIndex.Secondary:
SelectedVisualization = VisualizationType.Beat;
Gradient = new LinearGradient(new GradientStop(0.5, new Color(255, 255, 255)));
break;
case VisualizationIndex.Tertiary:
SelectedVisualization = VisualizationType.Level;
Gradient = new LinearGradient(new GradientStop(0, new Color(0, 0, 255)),
new GradientStop(1, new Color(255, 0, 0)));
break;
}
}
#endregion
#region Methods
public T GetConfiguration<T>(VisualizationType visualizationType)
where T : IConfiguration, new() => (T)this[visualizationType];
#endregion
}

View File

@ -0,0 +1,483 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using RGB.NET.Core;
using Color = RGB.NET.Core.Color;
using Point = System.Windows.Point;
using Rectangle = System.Windows.Shapes.Rectangle;
using WpfColor = System.Windows.Media.Color;
namespace KeyboardAudioVisualizer.Controls
{
[TemplatePart(Name = "PART_Selector", Type = typeof(Panel))]
[TemplatePart(Name = "PART_SliderAlpha", Type = typeof(Slider))]
[TemplatePart(Name = "PART_SliderRed", Type = typeof(Slider))]
[TemplatePart(Name = "PART_SliderGreen", Type = typeof(Slider))]
[TemplatePart(Name = "PART_SliderBlue", Type = typeof(Slider))]
[TemplatePart(Name = "PART_SliderHue", Type = typeof(Slider))]
[TemplatePart(Name = "PART_SliderSaturation", Type = typeof(Slider))]
[TemplatePart(Name = "PART_SliderValue", Type = typeof(Slider))]
[TemplatePart(Name = "PART_Preview", Type = typeof(Rectangle))]
public class ColorSelector : Control
{
#region Properties & Fields
private bool _ignorePropertyChanged;
private bool _dragSelector;
private byte _a;
private byte _r;
private byte _g;
private byte _b;
private double _hue;
private double _saturation;
private double _value;
private Panel _selector;
private Rectangle _selectorColor;
private Grid _selectorGrip;
private Slider _sliderAlpha;
private Slider _sliderRed;
private Slider _sliderGreen;
private Slider _sliderBlue;
private Slider _sliderHue;
private Slider _sliderSaturation;
private Slider _sliderValue;
private Rectangle _preview;
private SolidColorBrush _previewBrush;
private SolidColorBrush _selectorBrush;
private LinearGradientBrush _alphaBrush;
private LinearGradientBrush _redBrush;
private LinearGradientBrush _greenBrush;
private LinearGradientBrush _blueBrush;
private LinearGradientBrush _hueBrush;
private LinearGradientBrush _saturationBrush;
private LinearGradientBrush _valueBrush;
#endregion
#region DependencyProperties
public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register(
"SelectedColor", typeof(Color), typeof(ColorSelector), new FrameworkPropertyMetadata(new Color(255, 0, 0),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
SelectedColorChanged));
public Color SelectedColor
{
get => (Color)GetValue(SelectedColorProperty);
set => SetValue(SelectedColorProperty, value);
}
#endregion
#region Methods
public override void OnApplyTemplate()
{
if ((_selector = GetTemplateChild("PART_Selector") as Panel) != null)
{
_selectorBrush = new SolidColorBrush();
_selectorColor = new Rectangle
{
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Stretch,
SnapsToDevicePixels = true,
StrokeThickness = 0,
Fill = _selectorBrush
};
_selector.Children.Add(_selectorColor);
Rectangle selectorWhite = new Rectangle
{
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Stretch,
SnapsToDevicePixels = true,
StrokeThickness = 0,
Fill = new LinearGradientBrush(WpfColor.FromRgb(255, 255, 255), WpfColor.FromArgb(0, 255, 255, 255), new Point(0, 0.5), new Point(1, 0.5))
};
_selector.Children.Add(selectorWhite);
Rectangle selectorBlack = new Rectangle
{
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Stretch,
SnapsToDevicePixels = true,
StrokeThickness = 0,
Fill = new LinearGradientBrush(WpfColor.FromRgb(0, 0, 0), WpfColor.FromArgb(0, 0, 0, 0), new Point(0.5, 1), new Point(0.5, 0))
};
_selector.Children.Add(selectorBlack);
_selectorGrip = new Grid
{
VerticalAlignment = VerticalAlignment.Bottom,
HorizontalAlignment = HorizontalAlignment.Left,
SnapsToDevicePixels = true
};
_selectorGrip.Children.Add(new Ellipse
{
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
SnapsToDevicePixels = true,
Stroke = new SolidColorBrush(WpfColor.FromRgb(0, 0, 0)),
StrokeThickness = 2,
Fill = null,
Width = 12,
Height = 12
});
_selectorGrip.Children.Add(new Ellipse
{
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
SnapsToDevicePixels = true,
Stroke = new SolidColorBrush(WpfColor.FromRgb(255, 255, 255)),
StrokeThickness = 1,
Fill = null,
Width = 10,
Height = 10
});
_selector.Children.Add(_selectorGrip);
_selector.SizeChanged += (sender, args) => UpdateSelector();
_selector.MouseLeftButtonDown += (sender, args) =>
{
_dragSelector = true;
UpdateSelectorValue(args.GetPosition(_selector));
};
_selector.MouseEnter += (sender, args) =>
{
if (args.LeftButton == MouseButtonState.Pressed)
{
_dragSelector = true;
UpdateSelectorValue(args.GetPosition(_selector));
}
};
_selector.MouseLeftButtonUp += (sender, args) => _dragSelector = false;
_selector.MouseLeave += (sender, args) => _dragSelector = false;
_selector.MouseMove += (sender, args) => UpdateSelectorValue(args.GetPosition(_selector));
_selector.ClipToBounds = true;
}
if ((_sliderAlpha = GetTemplateChild("PART_SliderAlpha") as Slider) != null)
{
_alphaBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),
new GradientStop(new WpfColor(), 1) }));
_sliderAlpha.Background = _alphaBrush;
_sliderAlpha.ValueChanged += AChanged;
}
if ((_sliderRed = GetTemplateChild("PART_SliderRed") as Slider) != null)
{
_redBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),
new GradientStop(new WpfColor(), 1) }));
_sliderRed.Background = _redBrush;
_sliderRed.ValueChanged += RChanged;
}
if ((_sliderGreen = GetTemplateChild("PART_SliderGreen") as Slider) != null)
{
_greenBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),
new GradientStop(new WpfColor(), 1) }));
_sliderGreen.Background = _greenBrush;
_sliderGreen.ValueChanged += GChanged;
}
if ((_sliderBlue = GetTemplateChild("PART_SliderBlue") as Slider) != null)
{
_blueBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),
new GradientStop(new WpfColor(), 1) }));
_sliderBlue.Background = _blueBrush;
_sliderBlue.ValueChanged += BChanged;
}
if ((_sliderHue = GetTemplateChild("PART_SliderHue") as Slider) != null)
{
_hueBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),
new GradientStop(new WpfColor(), 1.0 / 6.0),
new GradientStop(new WpfColor(), 2.0 / 6.0),
new GradientStop(new WpfColor(), 3.0 / 6.0),
new GradientStop(new WpfColor(), 4.0 / 6.0),
new GradientStop(new WpfColor(), 5.0 / 6.0),
new GradientStop(new WpfColor(), 1) }));
_sliderHue.Background = _hueBrush;
_sliderHue.ValueChanged += HueChanged;
}
if ((_sliderSaturation = GetTemplateChild("PART_SliderSaturation") as Slider) != null)
{
_saturationBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),
new GradientStop(new WpfColor(), 1) }));
_sliderSaturation.Background = _saturationBrush;
_sliderSaturation.ValueChanged += SaturationChanged;
}
if ((_sliderValue = GetTemplateChild("PART_SliderValue") as Slider) != null)
{
_valueBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),
new GradientStop(new WpfColor(), 1) }));
_sliderValue.Background = _valueBrush;
_sliderValue.ValueChanged += ValueChanged;
}
if ((_preview = GetTemplateChild("PART_Preview") as Rectangle) != null)
{
_previewBrush = new SolidColorBrush();
_preview.Fill = _previewBrush;
}
SetColor(SelectedColor);
}
private static void SelectedColorChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (!(dependencyObject is ColorSelector cs) || !(dependencyPropertyChangedEventArgs.NewValue is Color color)) return;
cs.SetColor(color);
}
private void SetColor(Color color)
{
if (_ignorePropertyChanged) return;
SetA(color);
SetRGB(color);
SetHSV(color);
UpdateSelector();
UpdateUIColors();
}
private void AChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
{
if (_ignorePropertyChanged) return;
_a = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue);
Color color = new Color(_a, _r, _g, _b);
UpdateSelectedColor(color);
UpdateUIColors();
UpdateSelector();
}
private void RChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
{
if (_ignorePropertyChanged) return;
_r = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue);
RGBChanged();
}
private void GChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
{
if (_ignorePropertyChanged) return;
_g = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue);
RGBChanged();
}
private void BChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
{
if (_ignorePropertyChanged) return;
_b = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue);
RGBChanged();
}
private void RGBChanged()
{
Color color = new Color(_a, _r, _g, _b);
UpdateSelectedColor(color);
SetHSV(color);
UpdateUIColors();
UpdateSelector();
}
private void HueChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
{
if (_ignorePropertyChanged) return;
_hue = routedPropertyChangedEventArgs.NewValue.Clamp(0, 360);
HSVChanged();
}
private void SaturationChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
{
if (_ignorePropertyChanged) return;
_saturation = routedPropertyChangedEventArgs.NewValue.Clamp(0, 1);
HSVChanged();
}
private void ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
{
if (_ignorePropertyChanged) return;
_value = routedPropertyChangedEventArgs.NewValue.Clamp(0, 1);
HSVChanged();
}
private void HSVChanged()
{
Color color = HSVColor.Create(_a, _hue, _saturation, _value);
UpdateSelectedColor(color);
SetRGB(color);
UpdateUIColors();
UpdateSelector();
}
private void SetA(Color color)
{
_ignorePropertyChanged = true;
_a = color.GetA();
if (_sliderAlpha != null)
_sliderAlpha.Value = _a;
_ignorePropertyChanged = false;
}
private void SetRGB(Color color)
{
_ignorePropertyChanged = true;
_r = color.GetR();
if (_sliderRed != null)
_sliderRed.Value = _r;
_g = color.GetG();
if (_sliderGreen != null)
_sliderGreen.Value = _g;
_b = color.GetB();
if (_sliderBlue != null)
_sliderBlue.Value = _b;
_ignorePropertyChanged = false;
}
private void SetHSV(Color color)
{
_ignorePropertyChanged = true;
_hue = color.GetHue();
if (_sliderHue != null)
_sliderHue.Value = _hue;
_saturation = color.GetSaturation();
if (_sliderSaturation != null)
_sliderSaturation.Value = _saturation;
_value = color.GetValue();
if (_sliderValue != null)
_sliderValue.Value = _value;
_ignorePropertyChanged = false;
}
private void UpdateSelectedColor(Color color)
{
_ignorePropertyChanged = true;
SelectedColor = color;
_ignorePropertyChanged = false;
}
private void UpdateSelector()
{
if (_selector == null) return;
double selectorX = (_selector.ActualWidth * _saturation) - (_selectorGrip.ActualWidth / 2);
double selectorY = (_selector.ActualHeight * _value) - (_selectorGrip.ActualHeight / 2);
if (!double.IsNaN(selectorX) && !double.IsNaN(selectorY))
_selectorGrip.Margin = new Thickness(selectorX, 0, 0, selectorY);
}
private void UpdateSelectorValue(Point mouseLocation)
{
if (!_dragSelector) return;
double saturation = mouseLocation.X / _selector.ActualWidth;
double value = 1 - (mouseLocation.Y / _selector.ActualHeight);
if (!double.IsNaN(saturation) && !double.IsNaN(value))
{
_saturation = saturation;
_value = value;
HSVChanged();
}
}
private void UpdateUIColors()
{
Color hueColor = HSVColor.Create(_hue, 1, 1);
if (_previewBrush != null)
_previewBrush.Color = WpfColor.FromArgb(_a, _r, _g, _b);
if (_selectorBrush != null)
_selectorBrush.Color = WpfColor.FromRgb(hueColor.GetR(), hueColor.GetG(), hueColor.GetB());
if (_alphaBrush != null)
{
_alphaBrush.GradientStops[0].Color = WpfColor.FromArgb(0, _r, _g, _b);
_alphaBrush.GradientStops[1].Color = WpfColor.FromArgb(255, _r, _g, _b);
}
if (_redBrush != null)
{
_redBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, 0, _g, _b);
_redBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, 255, _g, _b);
}
if (_greenBrush != null)
{
_greenBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, _r, 0, _b);
_greenBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, _r, 255, _b);
}
if (_blueBrush != null)
{
_blueBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, _r, _g, 0);
_blueBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, _r, _g, 255);
}
if (_hueBrush != null)
{
Color referenceColor1 = HSVColor.Create(0, _saturation, _value);
Color referenceColor2 = HSVColor.Create(60, _saturation, _value);
Color referenceColor3 = HSVColor.Create(120, _saturation, _value);
Color referenceColor4 = HSVColor.Create(180, _saturation, _value);
Color referenceColor5 = HSVColor.Create(240, _saturation, _value);
Color referenceColor6 = HSVColor.Create(300, _saturation, _value);
_hueBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, referenceColor1.GetR(), referenceColor1.GetG(), referenceColor1.GetB());
_hueBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, referenceColor2.GetR(), referenceColor2.GetG(), referenceColor2.GetB());
_hueBrush.GradientStops[2].Color = WpfColor.FromArgb(_a, referenceColor3.GetR(), referenceColor3.GetG(), referenceColor3.GetB());
_hueBrush.GradientStops[3].Color = WpfColor.FromArgb(_a, referenceColor4.GetR(), referenceColor4.GetG(), referenceColor4.GetB());
_hueBrush.GradientStops[4].Color = WpfColor.FromArgb(_a, referenceColor5.GetR(), referenceColor5.GetG(), referenceColor5.GetB());
_hueBrush.GradientStops[5].Color = WpfColor.FromArgb(_a, referenceColor6.GetR(), referenceColor6.GetG(), referenceColor6.GetB());
_hueBrush.GradientStops[6].Color = WpfColor.FromArgb(_a, referenceColor1.GetR(), referenceColor1.GetG(), referenceColor1.GetB());
}
if (_saturationBrush != null)
{
Color referenceColor = HSVColor.Create(_hue, 1, _value);
_saturationBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, 255, 255, 255);
_saturationBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, referenceColor.GetR(), referenceColor.GetG(), referenceColor.GetB());
}
if (_valueBrush != null)
{
Color referenceColor = HSVColor.Create(_hue, _saturation, 1);
_valueBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, 0, 0, 0);
_valueBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, referenceColor.GetR(), referenceColor.GetG(), referenceColor.GetB());
}
}
#endregion
}
}

View File

@ -4,12 +4,12 @@ using System.Windows.Controls;
namespace KeyboardAudioVisualizer.Controls
{
public class Formular : Panel
public class Form : Panel
{
#region DependencyProperties
// ReSharper disable InconsistentNaming
public static readonly DependencyProperty RowHeightProperty = DependencyProperty.Register("RowHeight", typeof(double), typeof(Formular),
public static readonly DependencyProperty RowHeightProperty = DependencyProperty.Register("RowHeight", typeof(double), typeof(Form),
new FrameworkPropertyMetadata(24.0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
public double RowHeight
@ -22,7 +22,7 @@ namespace KeyboardAudioVisualizer.Controls
}
}
public static readonly DependencyProperty LabelWidthProperty = DependencyProperty.Register("LabelWidth", typeof(double), typeof(Formular),
public static readonly DependencyProperty LabelWidthProperty = DependencyProperty.Register("LabelWidth", typeof(double), typeof(Form),
new FrameworkPropertyMetadata(100.0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
public double LabelWidth
@ -35,7 +35,7 @@ namespace KeyboardAudioVisualizer.Controls
}
}
public static readonly DependencyProperty ElementSpacingProperty = DependencyProperty.Register("ElementSpacing", typeof(double), typeof(Formular),
public static readonly DependencyProperty ElementSpacingProperty = DependencyProperty.Register("ElementSpacing", typeof(double), typeof(Form),
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
public double ElementSpacing
@ -44,7 +44,7 @@ namespace KeyboardAudioVisualizer.Controls
set => SetValue(ElementSpacingProperty, value);
}
public static readonly DependencyProperty RowSpacingProperty = DependencyProperty.Register("RowSpacing", typeof(double), typeof(Formular),
public static readonly DependencyProperty RowSpacingProperty = DependencyProperty.Register("RowSpacing", typeof(double), typeof(Form),
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
public double RowSpacing
@ -59,25 +59,25 @@ namespace KeyboardAudioVisualizer.Controls
#region AttachedProperties
// ReSharper disable InconsistentNaming
public static readonly DependencyProperty IsLabelProperty = DependencyProperty.RegisterAttached("IsLabel", typeof(bool), typeof(Formular),
public static readonly DependencyProperty IsLabelProperty = DependencyProperty.RegisterAttached("IsLabel", typeof(bool), typeof(Form),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
public static void SetIsLabel(UIElement element, bool value) => element.SetValue(IsLabelProperty, value);
public static bool GetIsLabel(UIElement element) => (bool)element.GetValue(IsLabelProperty);
public static readonly DependencyProperty LineBreaksProperty = DependencyProperty.RegisterAttached("LineBreaks", typeof(int), typeof(Formular),
public static readonly DependencyProperty LineBreaksProperty = DependencyProperty.RegisterAttached("LineBreaks", typeof(int), typeof(Form),
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
public static void SetLineBreaks(UIElement element, int value) => element.SetValue(LineBreaksProperty, value);
public static int GetLineBreaks(UIElement element) => (int)element.GetValue(LineBreaksProperty);
public static readonly DependencyProperty RowSpanProperty = DependencyProperty.RegisterAttached("RowSpan", typeof(int), typeof(Formular),
public static readonly DependencyProperty RowSpanProperty = DependencyProperty.RegisterAttached("RowSpan", typeof(int), typeof(Form),
new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
public static void SetRowSpan(DependencyObject element, int value) => element.SetValue(RowSpanProperty, value);
public static int GetRowSpan(DependencyObject element) => (int)element.GetValue(RowSpanProperty);
public static readonly DependencyProperty FillProperty = DependencyProperty.RegisterAttached("Fill", typeof(bool), typeof(Formular),
public static readonly DependencyProperty FillProperty = DependencyProperty.RegisterAttached("Fill", typeof(bool), typeof(Form),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
public static void SetFill(DependencyObject element, bool value) => element.SetValue(FillProperty, value);
@ -92,7 +92,7 @@ namespace KeyboardAudioVisualizer.Controls
{
if (InternalChildren.Count == 0) return new Size(0, 0);
FormularLayout layout = new FormularLayout(RowHeight, LabelWidth, ElementSpacing, RowSpacing);
FormLayout layout = new FormLayout(RowHeight, LabelWidth, ElementSpacing, RowSpacing);
foreach (UIElement child in InternalChildren)
{
@ -107,7 +107,7 @@ namespace KeyboardAudioVisualizer.Controls
{
if (InternalChildren.Count == 0) return new Size(0, 0);
FormularLayout layout = new FormularLayout(RowHeight, LabelWidth, ElementSpacing, RowSpacing);
FormLayout layout = new FormLayout(RowHeight, LabelWidth, ElementSpacing, RowSpacing);
foreach (UIElement child in InternalChildren)
child.Arrange(layout.AddElement(child, finalSize.Width));
@ -119,7 +119,7 @@ namespace KeyboardAudioVisualizer.Controls
#region Data
private class FormularLayout
private class FormLayout
{
#region Properties & Fields
@ -140,7 +140,7 @@ namespace KeyboardAudioVisualizer.Controls
#region Constructors
public FormularLayout(double rowHeight, double labelWidth, double elementSpacing, double rowSpacing)
public FormLayout(double rowHeight, double labelWidth, double elementSpacing, double rowSpacing)
{
this._rowHeight = rowHeight;
this._labelWidth = labelWidth;

View File

@ -0,0 +1,424 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
using Color = System.Windows.Media.Color;
using Point = System.Windows.Point;
using Size = System.Windows.Size;
using Rectangle = System.Windows.Shapes.Rectangle;
using GradientStop = RGB.NET.Brushes.Gradients.GradientStop;
namespace KeyboardAudioVisualizer.Controls
{
[TemplatePart(Name = "PART_Gradient", Type = typeof(Canvas))]
[TemplatePart(Name = "PART_Stops", Type = typeof(Canvas))]
public class GradientEditor : Control
{
#region Properties & Fields
private Canvas _gradientContainer;
private Canvas _stopContainer;
private readonly List<Rectangle> _previewRectangles = new List<Rectangle>();
private readonly Dictionary<GradientStop, ContentControl> _stops = new Dictionary<GradientStop, ContentControl>();
private ContentControl _draggingStop;
private AdornerLayer _adornerLayer;
private ColorPickerAdorner _adorner;
private Window _window;
#endregion
#region DepdencyProperties
public static readonly DependencyProperty GradientProperty = DependencyProperty.Register(
"Gradient", typeof(LinearGradient), typeof(GradientEditor), new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnGradientChanged));
public LinearGradient Gradient
{
get => (LinearGradient)GetValue(GradientProperty);
set => SetValue(GradientProperty, value);
}
public static readonly DependencyProperty GradientStopStyleProperty = DependencyProperty.Register(
"GradientStopStyle", typeof(Style), typeof(GradientEditor), new PropertyMetadata(default(Style)));
public Style GradientStopStyle
{
get => (Style)GetValue(GradientStopStyleProperty);
set => SetValue(GradientStopStyleProperty, value);
}
public static readonly DependencyProperty SelectedStopProperty = DependencyProperty.Register(
"SelectedStop", typeof(GradientStop), typeof(GradientEditor), new PropertyMetadata(default(GradientStop), SelectedStopChanged));
public GradientStop SelectedStop
{
get => (GradientStop)GetValue(SelectedStopProperty);
set => SetValue(SelectedStopProperty, value);
}
public static readonly DependencyProperty ColorSelectorTemplateProperty = DependencyProperty.Register(
"ColorSelectorTemplate", typeof(DataTemplate), typeof(GradientEditor), new PropertyMetadata(default(DataTemplate)));
public DataTemplate ColorSelectorTemplate
{
get => (DataTemplate)GetValue(ColorSelectorTemplateProperty);
set => SetValue(ColorSelectorTemplateProperty, value);
}
public static readonly DependencyProperty CanAddOrDeleteStopsProperty = DependencyProperty.Register(
"CanAddOrDeleteStops", typeof(bool), typeof(GradientEditor), new PropertyMetadata(true));
public bool CanAddOrDeleteStops
{
get => (bool)GetValue(CanAddOrDeleteStopsProperty);
set => SetValue(CanAddOrDeleteStopsProperty, value);
}
#endregion
#region AttachedProperties
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.RegisterAttached(
"IsSelected", typeof(bool), typeof(GradientEditor), new PropertyMetadata(default(bool)));
public static void SetIsSelected(DependencyObject element, bool value) => element.SetValue(IsSelectedProperty, value);
public static bool GetIsSelected(DependencyObject element) => (bool)element.GetValue(IsSelectedProperty);
#endregion
#region Constructors
public GradientEditor()
{
if (Gradient == null)
Gradient = new LinearGradient();
}
#endregion
#region Methods
public override void OnApplyTemplate()
{
if ((_gradientContainer = GetTemplateChild("PART_Gradient") as Canvas) != null)
{
_gradientContainer.SizeChanged += (sender, args) => UpdateGradientPreview();
_gradientContainer.MouseDown += GradientContainerOnMouseDown;
}
if ((_stopContainer = GetTemplateChild("PART_Stops") as Canvas) != null)
_stopContainer.SizeChanged += (sender, args) => UpdateGradientStops();
_adornerLayer = AdornerLayer.GetAdornerLayer(this);
_window = Window.GetWindow(this);
if (_window != null)
{
_window.PreviewMouseDown += WindowMouseDown;
_window.PreviewKeyDown += (sender, args) =>
{
if (args.Key == Key.Escape)
SelectedStop = null;
};
}
UpdateGradientPreview();
UpdateGradientStops();
}
private void UpdateGradientPreview()
{
if ((_gradientContainer == null) || (Gradient == null)) return;
List<GradientStop> gradientStops = Gradient.GradientStops.OrderBy(x => x.Offset).ToList();
if (gradientStops.Count == 0)
UpdatePreviewRectangleCount(gradientStops.Count);
else if (gradientStops.Count == 1)
{
UpdatePreviewRectangleCount(gradientStops.Count);
GradientStop firstStop = gradientStops[0];
UpdatePreviewRectangle(_previewRectangles[0], _gradientContainer.ActualWidth, _gradientContainer.ActualHeight, 0, 1, firstStop.Color, firstStop.Color);
}
else
{
UpdatePreviewRectangleCount(gradientStops.Count + 1);
GradientStop firstStop = gradientStops[0];
UpdatePreviewRectangle(_previewRectangles[0], _gradientContainer.ActualWidth, _gradientContainer.ActualHeight, 0, firstStop.Offset, firstStop.Color, firstStop.Color);
for (int i = 0; i < (gradientStops.Count - 1); i++)
{
GradientStop stop = gradientStops[i];
GradientStop nextStop = gradientStops[i + 1];
Rectangle rect = _previewRectangles[i + 1];
UpdatePreviewRectangle(rect, _gradientContainer.ActualWidth, _gradientContainer.ActualHeight, stop.Offset, nextStop.Offset, stop.Color, nextStop.Color);
}
GradientStop lastStop = gradientStops[gradientStops.Count - 1];
UpdatePreviewRectangle(_previewRectangles[_previewRectangles.Count - 1], _gradientContainer.ActualWidth, _gradientContainer.ActualHeight, lastStop.Offset, 1, lastStop.Color, lastStop.Color);
}
}
private void UpdatePreviewRectangle(Rectangle rect, double referenceWidth, double referenceHeight, double from, double to,
RGB.NET.Core.Color startColor, RGB.NET.Core.Color endColor)
{
rect.Fill = new LinearGradientBrush(Color.FromArgb(startColor.GetA(), startColor.GetR(), startColor.GetG(), startColor.GetB()),
Color.FromArgb(endColor.GetA(), endColor.GetR(), endColor.GetG(), endColor.GetB()),
new Point(0, 0.5), new Point(1, 0.5));
//DarthAffe 09.02.2018: Forced rounding to prevent render issues on resize
Canvas.SetLeft(rect, Math.Floor(referenceWidth * from.Clamp(0, 1)));
rect.Width = Math.Ceiling(referenceWidth * (to.Clamp(0, 1) - from.Clamp(0, 1)));
Canvas.SetTop(rect, 0);
rect.Height = referenceHeight;
}
private void UpdatePreviewRectangleCount(int gradientCount)
{
int countDiff = gradientCount - _previewRectangles.Count;
if (countDiff > 0)
for (int i = 0; i < countDiff; i++)
{
Rectangle rect = new Rectangle { VerticalAlignment = VerticalAlignment.Stretch };
_previewRectangles.Add(rect);
_gradientContainer.Children.Add(rect);
}
if (countDiff < 0)
for (int i = 0; i < Math.Abs(countDiff); i++)
{
int index = _previewRectangles.Count - i - 1;
Rectangle rect = _previewRectangles[index];
_previewRectangles.RemoveAt(index);
_gradientContainer.Children.Remove(rect);
}
}
private void UpdateGradientStops()
{
if (Gradient == null) return;
List<GradientStop> gradientStops = Gradient.GradientStops.OrderBy(x => x.Offset).ToList();
UpdateGradientStopsCount(gradientStops);
foreach (GradientStop stop in gradientStops)
UpdateGradientStop(_stops[stop], _stopContainer.ActualWidth, _stopContainer.ActualHeight, stop);
}
private void UpdateGradientStop(ContentControl control, double referenceWidth, double referenceHeight, GradientStop stop)
{
control.Background = new SolidColorBrush(Color.FromArgb(stop.Color.GetA(), stop.Color.GetR(), stop.Color.GetG(), stop.Color.GetB()));
Canvas.SetLeft(control, (referenceWidth * stop.Offset.Clamp(0, 1)) - (control.Width / 2.0));
Canvas.SetTop(control, 0);
control.Height = referenceHeight;
}
private void UpdateGradientStopsCount(List<GradientStop> gradientStops)
{
foreach (GradientStop stop in gradientStops)
{
if (!_stops.ContainsKey(stop))
{
ContentControl control = new ContentControl
{
VerticalAlignment = VerticalAlignment.Stretch,
Style = GradientStopStyle,
Content = stop
};
control.MouseDown += GradientStopOnMouseDown;
_stops.Add(stop, control);
_stopContainer.Children.Add(control);
}
}
List<GradientStop> stopsToRemove = new List<GradientStop>();
foreach (KeyValuePair<GradientStop, ContentControl> stopPair in _stops)
if (!gradientStops.Contains(stopPair.Key))
{
ContentControl control = stopPair.Value;
control.MouseDown -= GradientStopOnMouseDown;
stopsToRemove.Add(stopPair.Key);
_stopContainer.Children.Remove(control);
}
foreach (GradientStop stop in stopsToRemove)
_stops.Remove(stop);
}
private void AttachGradient(AbstractGradient gradient) => gradient.GradientChanged += GradientChanged;
private void DetachGradient(AbstractGradient gradient) => gradient.GradientChanged -= GradientChanged;
private void GradientChanged(object o, EventArgs eventArgs)
{
UpdateGradientPreview();
UpdateGradientStops();
}
private static void OnGradientChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (!(dependencyObject is GradientEditor ge)) return;
if (dependencyPropertyChangedEventArgs.OldValue is AbstractGradient oldGradient)
ge.DetachGradient(oldGradient);
if (dependencyPropertyChangedEventArgs.NewValue is AbstractGradient newGradient)
ge.AttachGradient(newGradient);
}
private void GradientContainerOnMouseDown(object o, MouseButtonEventArgs mouseButtonEventArgs)
{
if ((mouseButtonEventArgs.ChangedButton != MouseButton.Left) || (Gradient == null) || !CanAddOrDeleteStops) return;
double offset = mouseButtonEventArgs.GetPosition(_gradientContainer).X / _gradientContainer.ActualWidth;
RGB.NET.Core.Color color = Gradient.GetColor(offset);
GradientStop newStop = new GradientStop(offset, color);
Gradient.GradientStops.Add(newStop);
SelectedStop = newStop;
}
private void GradientStopOnMouseDown(object o, MouseButtonEventArgs mouseButtonEventArgs)
{
if (!((o as ContentControl)?.Content is GradientStop stop) || (Gradient == null)) return;
if (mouseButtonEventArgs.ChangedButton == MouseButton.Right)
{
if (CanAddOrDeleteStops)
Gradient.GradientStops.Remove(stop);
}
else if (mouseButtonEventArgs.ChangedButton == MouseButton.Left)
{
SelectedStop = stop;
_draggingStop = (ContentControl)o;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (_draggingStop?.Content is GradientStop stop)
{
double location = e.GetPosition(_gradientContainer).X;
stop.Offset = (location / _gradientContainer.ActualWidth).Clamp(0, 1);
}
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
_draggingStop = null;
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
_draggingStop = null;
}
private static void SelectedStopChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (!(dependencyObject is GradientEditor gradientEditor)) return;
if (gradientEditor._adorner != null)
gradientEditor._adornerLayer.Remove(gradientEditor._adorner);
if (dependencyPropertyChangedEventArgs.OldValue is GradientStop oldStop)
{
if (gradientEditor._stops.TryGetValue(oldStop, out ContentControl oldcontrol))
SetIsSelected(oldcontrol, false);
}
if (dependencyPropertyChangedEventArgs.NewValue is GradientStop stop)
{
ContentControl stopContainer = gradientEditor._stops[stop];
SetIsSelected(stopContainer, true);
if (gradientEditor._adornerLayer != null)
{
ContentControl contentControl = new ContentControl
{
ContentTemplate = gradientEditor.ColorSelectorTemplate,
Content = stop
};
ColorPickerAdorner adorner = new ColorPickerAdorner(stopContainer, contentControl);
gradientEditor._adorner = adorner;
gradientEditor._adornerLayer.Add(adorner);
}
}
}
private void WindowMouseDown(object o, MouseButtonEventArgs mouseButtonEventArgs)
{
if ((_adorner != null) && (VisualTreeHelper.HitTest(_adorner, mouseButtonEventArgs.GetPosition(_adorner)) == null))
SelectedStop = null;
}
#endregion
}
public class ColorPickerAdorner : Adorner
{
#region Properties & Fields
private readonly VisualCollection _visualChildren;
private readonly FrameworkElement _colorSelector;
protected override int VisualChildrenCount => 1;
protected override Visual GetVisualChild(int index) => _colorSelector;
#endregion
#region Constructors
public ColorPickerAdorner(UIElement adornedElement, FrameworkElement colorSelector)
: base(adornedElement)
{
this._colorSelector = colorSelector;
_visualChildren = new VisualCollection(this) { colorSelector };
}
#endregion
#region Methods
protected override Size ArrangeOverride(Size finalSize)
{
Window referenceWindow = Window.GetWindow(AdornedElement);
Point referenceLocation = AdornedElement.TranslatePoint(new Point(0, 0), referenceWindow);
double referenceWidth = ((FrameworkElement)AdornedElement).ActualWidth / 2.0;
double referenceHeight = ((FrameworkElement)AdornedElement).Height;
double referenceX = referenceLocation.X + referenceWidth;
double halfWidth = finalSize.Width / 2.0;
double maxOffset = referenceWindow.Width - halfWidth;
double offset = (referenceX < halfWidth ? referenceX
: (((referenceX + (referenceWidth * 2)) > maxOffset)
? halfWidth - ((maxOffset - referenceX) - (referenceWidth * 2))
: halfWidth));
_colorSelector.Arrange(new Rect(new Point(referenceWidth - offset, referenceHeight), finalSize));
return _colorSelector.RenderSize;
}
protected override Size MeasureOverride(Size constraint)
{
_colorSelector.Measure(constraint);
return _colorSelector.DesiredSize;
}
#endregion
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Globalization;
using System.Windows.Data;
using KeyboardAudioVisualizer.Attributes;
using KeyboardAudioVisualizer.Helper;
using VisualizationType = KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider.VisualizationType;
namespace KeyboardAudioVisualizer.Converter
{
[ValueConversion(typeof(VisualizationType), typeof(string))]
public class VisualizationProviderDisplayNameConverter : IValueConverter
{
#region Methods
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is VisualizationType visualizationType)) return null;
return visualizationType.GetAttribute<DisplayNameAttribute>()?.DisplayName ?? visualizationType.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();
#endregion
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Globalization;
using System.Windows.Data;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.UI.Visualization;
namespace KeyboardAudioVisualizer.Converter
{
[ValueConversion(typeof(IVisualizationProvider), typeof(bool))]
public class VisualizationToLastChildFillConverter : IValueConverter
{
#region Methods
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is FrequencyBarsVisualizationProvider;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();
#endregion
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Data;
using KeyboardAudioVisualizer.Attributes;
using KeyboardAudioVisualizer.Helper;
using RGB.NET.Core;
using VisualizationType = KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider.VisualizationType;
namespace KeyboardAudioVisualizer.Converter
{
[ValueConversion(typeof(IEnumerable<VisualizationType>), typeof(IEnumerable<VisualizationType>))]
public class VisualizationTypeSelectableConverter : IValueConverter
{
#region Methods
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is IEnumerable<VisualizationType> visualizationProviders) || !(parameter is RGBDeviceType targetDevice)) return new List<VisualizationType>();
return visualizationProviders.Where(x => x.GetAttribute<VisualizerForAttribute>()?.VisualizerFor.HasFlag(targetDevice) ?? true).ToList();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();
#endregion
}
}

View File

@ -0,0 +1,31 @@
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.Decorators
{
public class BeatDecorator : AbstractUpdateAwareDecorator, IBrushDecorator
{
#region Properties & Fields
private readonly IVisualizationProvider _visualizationProvider;
#endregion
#region Constructors
public BeatDecorator(IVisualizationProvider visualizationProvider)
{
this._visualizationProvider = visualizationProvider;
}
#endregion
#region Methods
protected override void Update(double deltaTime) => _visualizationProvider.Update();
#endregion
public Color ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, Color color) => color.SetA(color.A * _visualizationProvider.VisualizationData[0]);
}
}

View File

@ -1,14 +1,12 @@
using System;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using RGB.NET.Brushes;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
using Color = RGB.NET.Core.Color;
using Rectangle = RGB.NET.Core.Rectangle;
namespace KeyboardAudioVisualizer.Brushes
namespace KeyboardAudioVisualizer.Decorators
{
public class FrequencyBarsBrush : LinearGradientBrush
public class FrequencyBarsDecorator : AbstractUpdateAwareDecorator, IBrushDecorator
{
#region Properties & Fields
@ -18,8 +16,7 @@ namespace KeyboardAudioVisualizer.Brushes
#region Constructors
public FrequencyBarsBrush(IVisualizationProvider visualizationProvider, IGradient gradient)
: base(gradient)
public FrequencyBarsDecorator(IVisualizationProvider visualizationProvider)
{
this._visualizationProvider = visualizationProvider;
}
@ -28,13 +25,15 @@ namespace KeyboardAudioVisualizer.Brushes
#region Methods
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget)
protected override void Update(double deltaTime) => _visualizationProvider.Update();
public Color ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, Color color)
{
int barSampleIndex = (int)Math.Floor(_visualizationProvider.VisualizationData.Length * (renderTarget.Point.X / (rectangle.Location.X + rectangle.Size.Width)));
int barSampleIndex = Math.Min(_visualizationProvider.VisualizationData.Length, (int)Math.Floor(_visualizationProvider.VisualizationData.Length * (renderTarget.Point.X / (rectangle.Location.X + rectangle.Size.Width))));
double curBarHeight = 1.0 - Math.Max(0f, _visualizationProvider.VisualizationData[barSampleIndex]);
double verticalPos = (renderTarget.Point.Y / rectangle.Size.Height);
return curBarHeight <= verticalPos ? base.GetColorAtPoint(rectangle, renderTarget) : Color.Transparent;
return curBarHeight > verticalPos ? color.SetA(0) : color;
}
#endregion

View File

@ -0,0 +1,105 @@
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.Decorators
{
public class LevelBarDecorator : AbstractUpdateAwareDecorator, IBrushDecorator
{
#region Properties & Fields
private readonly IVisualizationProvider _visualizationProvider;
public LevelBarDirection Direction { get; set; }
public int DataIndex { get; set; }
public LinearGradient Gradient { get; set; }
#endregion
#region Constructors
public LevelBarDecorator(IVisualizationProvider visualizationProvider, LevelBarDirection direction, int dataIndex, LinearGradient gradient)
{
this._visualizationProvider = visualizationProvider;
this.Direction = direction;
this.DataIndex = dataIndex;
this.Gradient = gradient;
}
#endregion
#region Methods
protected override void Update(double deltaTime) => _visualizationProvider.Update();
public Color ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, Color color)
{
double offset = CalculateOffset(rectangle, renderTarget);
if (Direction == LevelBarDirection.Horizontal)
{
if (offset < 0)
{
offset = (-offset * 2);
if (offset >= _visualizationProvider.VisualizationData[0])
return color.SetA(0);
else
return Gradient.GetColor(offset);
}
else
{
offset *= 2;
if (offset >= _visualizationProvider.VisualizationData[1])
return color.SetA(0);
else
return Gradient.GetColor(offset);
}
}
else
{
if (offset >= _visualizationProvider.VisualizationData[DataIndex])
return color.SetA(0);
}
return color;
}
private double CalculateOffset(Rectangle rectangle, BrushRenderTarget renderTarget)
{
switch (Direction)
{
case LevelBarDirection.Left:
return (rectangle.Size.Width - renderTarget.Rectangle.Center.X) / rectangle.Size.Width;
case LevelBarDirection.Right:
return renderTarget.Rectangle.Center.X / rectangle.Size.Width;
case LevelBarDirection.Top:
return (rectangle.Size.Height - renderTarget.Rectangle.Center.Y) / rectangle.Size.Height;
case LevelBarDirection.Bottom:
return renderTarget.Rectangle.Center.Y / rectangle.Size.Height;
case LevelBarDirection.Horizontal:
return (renderTarget.Rectangle.Center.X / rectangle.Size.Width) - 0.5;
default:
return -1;
}
}
#endregion
}
#region Data
public enum LevelBarDirection
{
Left, Right, Top, Bottom,
//HACK DarthAffe 12.09.2017: Just a bad workaround ...
Horizontal
}
#endregion
}

View File

@ -0,0 +1,15 @@
using System;
using System.Linq;
namespace KeyboardAudioVisualizer.Helper
{
public static class EnumExtension
{
#region Methods
public static T GetAttribute<T>(this Enum e)
where T : Attribute => e.GetType().GetMember(e.ToString()).FirstOrDefault()?.GetCustomAttributes(typeof(T), false).FirstOrDefault() as T;
#endregion
}
}

View File

@ -10,6 +10,8 @@ namespace KeyboardAudioVisualizer.Helper
{
if (ex == null) return string.Empty;
message += ex.Message;
if (ex.InnerException != null)
message += "\r\nInnerException: " + GetFullMessage(ex.InnerException);

View File

@ -0,0 +1,309 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using RGB.NET.Core;
// Taken from https://codereview.stackexchange.com/questions/116562/custom-implementation-of-observabledictionary
namespace KeyboardAudioVisualizer.Helper
{
public class ObservableDictionary<TKey, TValue> : AbstractBindable, IDictionary<TKey, TValue>, INotifyCollectionChanged
{
#region Constants
private const string INDEXER_NAME = "Item[]";
#endregion
#region Properties & Fields
private readonly IList<TValue> _values;
private readonly IDictionary<TKey, int> _indexMap;
private readonly SimpleMonitor _monitor = new SimpleMonitor();
#endregion
#region Constructor
public ObservableDictionary()
{
_values = new List<TValue>();
_indexMap = new Dictionary<TKey, int>();
}
public ObservableDictionary(IDictionary<TKey, TValue> dictionary)
{
_values = new List<TValue>();
_indexMap = new Dictionary<TKey, int>();
int idx = 0;
foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
{
_indexMap.Add(kvp.Key, idx);
_values.Add(kvp.Value);
idx++;
}
}
public ObservableDictionary(int capacity)
{
_values = new List<TValue>(capacity);
_indexMap = new Dictionary<TKey, int>(capacity);
}
#endregion
#region Virtual Add/Remove/Change Control Methods
protected virtual void AddItem(TKey key, TValue value)
{
CheckReentrancy();
int index = _values.Count;
_indexMap.Add(key, index);
_values.Add(value);
OnPropertyChanged(nameof(Count));
OnPropertyChanged(nameof(Keys));
OnPropertyChanged(nameof(Values));
OnPropertyChanged(INDEXER_NAME);
OnCollectionChanged(NotifyCollectionChangedAction.Add, key, value, index);
}
protected virtual bool RemoveItem(TKey key)
{
CheckReentrancy();
int index = _indexMap[key];
TValue value = _values[index];
if (_indexMap.Remove(key))
{
_values.RemoveAt(index);
List<TKey> keys = _indexMap.Keys.ToList();
foreach (TKey existingKey in keys)
{
if (_indexMap[existingKey] > index)
_indexMap[existingKey]--;
}
OnPropertyChanged(nameof(Count));
OnPropertyChanged(nameof(Keys));
OnPropertyChanged(nameof(Values));
OnPropertyChanged(INDEXER_NAME);
OnCollectionChanged(NotifyCollectionChangedAction.Remove, key, value, index);
return true;
}
return false;
}
protected virtual bool RemoveItem(KeyValuePair<TKey, TValue> item)
{
CheckReentrancy();
if (_indexMap.ContainsKey(item.Key) && _values[_indexMap[item.Key]].Equals(item.Value))
{
int index = _indexMap[item.Key];
TValue value = _values[index];
_indexMap.Remove(item.Key);
_values.RemoveAt(index);
List<TKey> keys = _indexMap.Keys.ToList();
foreach (TKey existingKey in keys)
{
if (_indexMap[existingKey] > index)
_indexMap[existingKey]--;
}
OnPropertyChanged(nameof(Count));
OnPropertyChanged(nameof(Keys));
OnPropertyChanged(nameof(Values));
OnPropertyChanged(INDEXER_NAME);
OnCollectionChanged(NotifyCollectionChangedAction.Remove, item.Key, item.Value, index);
return true;
}
return false;
}
protected virtual void RemoveAllItems()
{
CheckReentrancy();
_values.Clear();
_indexMap.Clear();
OnPropertyChanged(nameof(Count));
OnPropertyChanged(nameof(Keys));
OnPropertyChanged(nameof(Values));
OnPropertyChanged(INDEXER_NAME);
OnCollectionChanged(NotifyCollectionChangedAction.Reset);
}
protected virtual void ChangeItem(TKey key, TValue newValue)
{
CheckReentrancy();
if (!_indexMap.ContainsKey(key))
AddItem(key, newValue);
else
{
int index = _indexMap[key];
TValue oldValue = _values[index];
_values[index] = newValue;
OnPropertyChanged(nameof(Values));
OnPropertyChanged(INDEXER_NAME);
OnCollectionChanged(NotifyCollectionChangedAction.Replace, key, oldValue, newValue, index);
}
}
protected IDisposable BlockReentrancy()
{
_monitor.Enter();
return (IDisposable)_monitor;
}
protected void CheckReentrancy()
{
// ISSUE: reference to a compiler-generated field
// ISSUE: reference to a compiler-generated field
if (_monitor.Busy && CollectionChanged != null && CollectionChanged.GetInvocationList().Length > 1)
throw new InvalidOperationException("ObservableCollectionReentrancyNotAllowed");
}
#endregion
#region IDictionary<TKey,TValue> Members
public void Add(TKey key, TValue value) => AddItem(key, value);
public bool ContainsKey(TKey key) => _indexMap.ContainsKey(key);
public bool Remove(TKey key) => RemoveItem(key);
public bool TryGetValue(TKey key, out TValue value)
{
if (_indexMap.TryGetValue(key, out int index))
{
value = _values[index];
return true;
}
else
{
value = default;
return false;
}
}
public ICollection<TKey> Keys => _indexMap.Keys;
public ICollection<TValue> Values => _values;
public TValue this[TKey key]
{
get
{
int index = _indexMap[key];
return _values[index];
}
set => ChangeItem(key, value);
}
#endregion
#region ICollection<KeyValuePair<TKey,TValue>> Members
public void Clear() => RemoveAllItems();
public int Count => _indexMap.Count;
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) => _indexMap.ContainsKey(item.Key) && _values[_indexMap[item.Key]].Equals(item.Value);
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
foreach (KeyValuePair<TKey, int> kvp in _indexMap)
{
array[arrayIndex] = new KeyValuePair<TKey, TValue>(kvp.Key, _values[kvp.Value]);
arrayIndex++;
}
}
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) => RemoveItem(item);
#endregion
#region IEnumerable<KeyValuePair<TKey,TValue>> Members
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
foreach (KeyValuePair<TKey, int> kvp in _indexMap)
{
KeyValuePair<TKey, TValue> pair = new KeyValuePair<TKey, TValue>(kvp.Key, _values[kvp.Value]);
yield return pair;
}
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#endregion
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler handler = CollectionChanged;
using (BlockReentrancy())
handler?.Invoke(this, e);
}
protected void OnCollectionChanged(NotifyCollectionChangedAction action) => OnCollectionChanged(new NotifyCollectionChangedEventArgs(action));
protected void OnCollectionChanged(NotifyCollectionChangedAction action, TKey key, TValue value, int index) => OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, new KeyValuePair<TKey, TValue>(key, value), index));
protected void OnCollectionChanged(NotifyCollectionChangedAction action, TKey key, TValue oldValue, TValue newValue, int index)
{
KeyValuePair<TKey, TValue> newPair = new KeyValuePair<TKey, TValue>(key, newValue);
KeyValuePair<TKey, TValue> oldPair = new KeyValuePair<TKey, TValue>(key, oldValue);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newPair, oldPair, index));
}
#endregion
private class SimpleMonitor : IDisposable
{
private int _busyCount;
public bool Busy => _busyCount > 0;
public void Enter() => _busyCount = _busyCount + 1;
public void Dispose() => _busyCount = _busyCount - 1;
}
}
}

View File

@ -0,0 +1,68 @@
using System.Linq;
namespace KeyboardAudioVisualizer.Helper
{
public class RingBuffer
{
#region Properties & Fields
private readonly int _capacity;
private readonly float[] _buffer;
private int _currentIndex;
public int Size => _capacity;
public float Average => _buffer.Average();
public float Min => _buffer.Min();
public float Max => _buffer.Max();
public float Sum => _buffer.Sum();
#endregion
#region Constructors
public RingBuffer(int capacity)
{
this._capacity = capacity;
_buffer = new float[capacity];
}
#endregion
#region Methods
public void Put(float value) => Put(new[] { value }, 0, 1);
public void Put(float[] src, int offset, int count)
{
lock (_buffer)
{
if (count > _capacity)
{
offset += count - _capacity;
count = _capacity;
}
for (int i = 0; i < count; i++)
{
_currentIndex++;
if (_currentIndex >= _capacity) _currentIndex = 0;
_buffer[_currentIndex] = src[offset + i];
}
}
}
public void CopyInto(ref float[] data, int offset) => CopyInto(ref data, offset, _capacity);
public void CopyInto(ref float[] data, int offset, int count)
{
lock (_buffer)
for (int i = _capacity - count; i < count; i++)
data[offset + i] = _buffer[(_currentIndex + i) % _capacity];
}
#endregion
}
}

View File

@ -0,0 +1,9 @@
namespace KeyboardAudioVisualizer.Helper
{
public enum VisualizationIndex
{
Primary,
Secondary,
Tertiary
}
}

View File

@ -25,6 +25,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
@ -34,47 +35,75 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Resources\Icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="CSCore, Version=1.2.1.1, Culture=neutral, PublicKeyToken=5a08f2b6f4415dea, processorArchitecture=MSIL">
<HintPath>..\packages\CSCore.1.2.1.1\lib\net35-client\CSCore.dll</HintPath>
<Reference Include="CSCore, Version=1.2.1.2, Culture=neutral, PublicKeyToken=5a08f2b6f4415dea, processorArchitecture=MSIL">
<HintPath>..\packages\CSCore.1.2.1.2\lib\net35-client\CSCore.dll</HintPath>
</Reference>
<Reference Include="Hardcodet.Wpf.TaskbarNotification, Version=1.0.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Hardcodet.NotifyIcon.Wpf.1.0.8\lib\net451\Hardcodet.Wpf.TaskbarNotification.dll</HintPath>
</Reference>
<Reference Include="MathNet.Numerics, Version=3.20.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MathNet.Numerics.3.20.0\lib\net40\MathNet.Numerics.dll</HintPath>
<Reference Include="HidSharp, Version=2.0.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\HidSharp.2.0.5\lib\net35\HidSharp.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Brushes">
<HintPath>..\packages\RGB.NET.Brushes.1.0.0\lib\net45\RGB.NET.Brushes.dll</HintPath>
<Private>True</Private>
<Reference Include="Interop.AuraServiceLib, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Devices.Asus.0.1.31\lib\net45\Interop.AuraServiceLib.dll</HintPath>
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="RGB.NET.Core">
<HintPath>..\packages\RGB.NET.Core.1.0.0\lib\net45\RGB.NET.Core.dll</HintPath>
<Private>True</Private>
<Reference Include="MathNet.Numerics, Version=4.7.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MathNet.Numerics.4.7.0\lib\net461\MathNet.Numerics.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Devices.CoolerMaster">
<HintPath>..\packages\RGB.NET.Devices.CoolerMaster.1.0.0\lib\net45\RGB.NET.Devices.CoolerMaster.dll</HintPath>
<Private>True</Private>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Devices.Corsair, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Devices.Corsair.1.0.0\lib\net45\RGB.NET.Devices.Corsair.dll</HintPath>
<Reference Include="RGB.NET.Brushes, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Brushes.0.1.31\lib\net45\RGB.NET.Brushes.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Devices.Logitech, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Devices.Logitech.1.0.0\lib\net45\RGB.NET.Devices.Logitech.dll</HintPath>
<Reference Include="RGB.NET.Core, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Core.0.1.31\lib\net45\RGB.NET.Core.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Groups, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Groups.1.0.0\lib\net45\RGB.NET.Groups.dll</HintPath>
<Reference Include="RGB.NET.Decorators, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Decorators.0.1.31\lib\net45\RGB.NET.Decorators.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Devices.Asus, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Devices.Asus.0.1.31\lib\net45\RGB.NET.Devices.Asus.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Devices.CoolerMaster, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Devices.CoolerMaster.0.1.31\lib\net45\RGB.NET.Devices.CoolerMaster.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Devices.Corsair, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Devices.Corsair.0.1.31\lib\net45\RGB.NET.Devices.Corsair.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Devices.Logitech, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Devices.Logitech.0.1.31\lib\net45\RGB.NET.Devices.Logitech.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Devices.Novation, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Devices.Novation.0.1.31\lib\net45\RGB.NET.Devices.Novation.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Devices.Razer, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Devices.Razer.0.1.31\lib\net45\RGB.NET.Devices.Razer.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Devices.SteelSeries, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Devices.SteelSeries.0.1.31\lib\netstandard2.0\RGB.NET.Devices.SteelSeries.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Groups, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Groups.0.1.31\lib\net45\RGB.NET.Groups.dll</HintPath>
</Reference>
<Reference Include="Sanford.Multimedia.Midi, Version=6.6.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sanford.Multimedia.Midi.6.6.0\lib\net20\Sanford.Multimedia.Midi.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Management" />
<Reference Include="System.Numerics" />
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll</HintPath>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
@ -97,15 +126,20 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Attached\SliderValue.cs" />
<Compile Include="Attached\SliderValueAdorner.cs" />
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="ApplicationManager.cs" />
<Compile Include="Attributes\DisplayNameAttribute.cs" />
<Compile Include="Attributes\VisualizerForAttribute.cs" />
<Compile Include="AudioCapture\AudioBuffer.cs" />
<Compile Include="AudioCapture\CSCoreAudioInput.cs" />
<Compile Include="AudioCapture\IAudioInput.cs" />
<Compile Include="AudioProcessing\AudioProcessor.cs" />
<Compile Include="AudioProcessing\AbstractAudioProcessor.cs" />
<Compile Include="AudioProcessing\AudioVisualizationFactory.cs" />
<Compile Include="AudioProcessing\Equalizer\EqualizerBand.cs" />
<Compile Include="AudioProcessing\Equalizer\IEqualizer.cs" />
<Compile Include="AudioProcessing\Equalizer\MultiBandEqualizer.cs" />
@ -123,13 +157,20 @@
<Compile Include="AudioProcessing\VisualizationProvider\IVisualizationProvider.cs" />
<Compile Include="AudioProcessing\VisualizationProvider\LevelVisualizationProvider.cs" />
<Compile Include="AudioProcessing\Spectrum\RawSpectrumProvider.cs" />
<Compile Include="Brushes\BeatBrush.cs" />
<Compile Include="Brushes\FrequencyBarsBrush.cs" />
<Compile Include="Brushes\LevelBarBrush.cs" />
<Compile Include="AudioProcessing\VisualizationProvider\VisualizationType.cs" />
<Compile Include="Configuration\ColorSerializer.cs" />
<Compile Include="Controls\ColorSelector.cs" />
<Compile Include="Controls\GradientEditor.cs" />
<Compile Include="Converter\VisualizationProviderDisplayNameConverter.cs" />
<Compile Include="Converter\VisualizationToLastChildFillConverter.cs" />
<Compile Include="Converter\VisualizationTypeSelectableConverter.cs" />
<Compile Include="Decorators\BeatDecorator.cs" />
<Compile Include="Decorators\FrequencyBarsDecorator.cs" />
<Compile Include="Decorators\LevelBarDecorator.cs" />
<Compile Include="Configuration\AbstractConfiguration.cs" />
<Compile Include="Configuration\EqualizerConfiguration.cs" />
<Compile Include="Configuration\IConfiguration.cs" />
<Compile Include="Controls\Formular.cs" />
<Compile Include="Controls\Form.cs" />
<Compile Include="Controls\ImageButton.cs" />
<Compile Include="Converter\BoolToVisibilityConverter.cs" />
<Compile Include="Converter\EqualizerBandsToPointsConverter.cs" />
@ -137,12 +178,19 @@
<Compile Include="Converter\OffsetToPosXConverter.cs" />
<Compile Include="Converter\ValueToPosYConverter.cs" />
<Compile Include="Helper\ActionCommand.cs" />
<Compile Include="Helper\EnumExtension.cs" />
<Compile Include="Helper\ExceptionExtension.cs" />
<Compile Include="Helper\FrequencyHelper.cs" />
<Compile Include="Helper\MathHelper.cs" />
<Compile Include="Helper\ObservableDictionary.cs" />
<Compile Include="Helper\RingBuffer.cs" />
<Compile Include="Helper\VisualizationIndex.cs" />
<Compile Include="Helper\WPFHelper.cs" />
<Compile Include="Configuration\Settings.cs" />
<Compile Include="Controls\BlurredDecorationWindow.cs" />
<Compile Include="Legacy\ConfigurationMigrator.cs" />
<Compile Include="Legacy\ConfigurationUpdates.cs" />
<Compile Include="Legacy\Settings.cs" />
<Compile Include="Styles\CachedResourceDictionary.cs" />
<Compile Include="UI\ConfigurationWindow.xaml.cs">
<DependentUpon>ConfigurationWindow.xaml</DependentUpon>
@ -154,7 +202,7 @@
<Compile Include="UI\Visualization\LevelVisualizer.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Helper\SerializationHelper.cs" />
<Compile Include="Legacy\SerializationHelper.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
@ -173,7 +221,6 @@
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<None Include="NuGet.Config" />
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
@ -199,6 +246,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\ColorSelector.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\FrameworkElement.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@ -211,6 +262,14 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\GradientEditor.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\Slider.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\ImageButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@ -227,7 +286,7 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\Formular.xaml">
<Page Include="Styles\Form.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
@ -275,15 +334,21 @@
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\RGB.NET.Devices.Corsair.1.0.0\build\net45\RGB.NET.Devices.Corsair.targets" Condition="Exists('..\packages\RGB.NET.Devices.Corsair.1.0.0\build\net45\RGB.NET.Devices.Corsair.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\RGB.NET.Devices.Corsair.1.0.0\build\net45\RGB.NET.Devices.Corsair.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\RGB.NET.Devices.Corsair.1.0.0\build\net45\RGB.NET.Devices.Corsair.targets'))" />
<Error Condition="!Exists('..\packages\RGB.NET.Devices.CoolerMaster.1.0.0\build\net45\RGB.NET.Devices.CoolerMaster.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\RGB.NET.Devices.CoolerMaster.1.0.0\build\net45\RGB.NET.Devices.CoolerMaster.targets'))" />
<Error Condition="!Exists('..\packages\RGB.NET.Devices.Logitech.1.0.0\build\net45\RGB.NET.Devices.Logitech.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\RGB.NET.Devices.Logitech.1.0.0\build\net45\RGB.NET.Devices.Logitech.targets'))" />
<Error Condition="!Exists('..\packages\RGB.NET.Resources.Novation.0.1.0\build\RGB.NET.Resources.Novation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\RGB.NET.Resources.Novation.0.1.0\build\RGB.NET.Resources.Novation.targets'))" />
<Error Condition="!Exists('..\packages\RGB.NET.Resources.CoolerMaster.0.2.0\build\RGB.NET.Resources.CoolerMaster.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\RGB.NET.Resources.CoolerMaster.0.2.0\build\RGB.NET.Resources.CoolerMaster.targets'))" />
<Error Condition="!Exists('..\packages\RGB.NET.Resources.Corsair.0.3.0.234\build\RGB.NET.Resources.Corsair.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\RGB.NET.Resources.Corsair.0.3.0.234\build\RGB.NET.Resources.Corsair.targets'))" />
<Error Condition="!Exists('..\packages\RGB.NET.Resources.Logitech.0.3.0\build\RGB.NET.Resources.Logitech.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\RGB.NET.Resources.Logitech.0.3.0\build\RGB.NET.Resources.Logitech.targets'))" />
<Error Condition="!Exists('..\packages\RGB.NET.Resources.Razer.0.3.2.4\build\RGB.NET.Resources.Razer.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\RGB.NET.Resources.Razer.0.3.2.4\build\RGB.NET.Resources.Razer.targets'))" />
<Error Condition="!Exists('..\packages\RGB.NET.Resources.Asus.0.3.0\build\RGB.NET.Resources.Asus.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\RGB.NET.Resources.Asus.0.3.0\build\RGB.NET.Resources.Asus.targets'))" />
</Target>
<Import Project="..\packages\RGB.NET.Devices.CoolerMaster.1.0.0\build\net45\RGB.NET.Devices.CoolerMaster.targets" Condition="Exists('..\packages\RGB.NET.Devices.CoolerMaster.1.0.0\build\net45\RGB.NET.Devices.CoolerMaster.targets')" />
<Import Project="..\packages\RGB.NET.Devices.Logitech.1.0.0\build\net45\RGB.NET.Devices.Logitech.targets" Condition="Exists('..\packages\RGB.NET.Devices.Logitech.1.0.0\build\net45\RGB.NET.Devices.Logitech.targets')" />
<Import Project="..\packages\RGB.NET.Resources.Novation.0.1.0\build\RGB.NET.Resources.Novation.targets" Condition="Exists('..\packages\RGB.NET.Resources.Novation.0.1.0\build\RGB.NET.Resources.Novation.targets')" />
<Import Project="..\packages\RGB.NET.Resources.CoolerMaster.0.2.0\build\RGB.NET.Resources.CoolerMaster.targets" Condition="Exists('..\packages\RGB.NET.Resources.CoolerMaster.0.2.0\build\RGB.NET.Resources.CoolerMaster.targets')" />
<Import Project="..\packages\RGB.NET.Resources.Corsair.0.3.0.234\build\RGB.NET.Resources.Corsair.targets" Condition="Exists('..\packages\RGB.NET.Resources.Corsair.0.3.0.234\build\RGB.NET.Resources.Corsair.targets')" />
<Import Project="..\packages\RGB.NET.Resources.Logitech.0.3.0\build\RGB.NET.Resources.Logitech.targets" Condition="Exists('..\packages\RGB.NET.Resources.Logitech.0.3.0\build\RGB.NET.Resources.Logitech.targets')" />
<Import Project="..\packages\RGB.NET.Resources.Razer.0.3.2.4\build\RGB.NET.Resources.Razer.targets" Condition="Exists('..\packages\RGB.NET.Resources.Razer.0.3.2.4\build\RGB.NET.Resources.Razer.targets')" />
<Import Project="..\packages\RGB.NET.Resources.Asus.0.3.0\build\RGB.NET.Resources.Asus.targets" Condition="Exists('..\packages\RGB.NET.Resources.Asus.0.3.0\build\RGB.NET.Resources.Asus.targets')" />
</Project>

View File

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp71</s:String></wpf:ResourceDictionary>

View File

@ -0,0 +1,56 @@
using System.IO;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.Helper;
namespace KeyboardAudioVisualizer.Legacy
{
public static class ConfigurationMigrator
{
#region Constants
private const string PATH_V1_SETTINGS = "Settings.xml";
#endregion
#region Constructors
#endregion
#region Methods
public static Configuration.Settings MigrateOldConfig()
{
if (!File.Exists(PATH_V1_SETTINGS)) return null;
try
{
Settings oldSettings = SerializationHelper.LoadObjectFromFile<Settings>(PATH_V1_SETTINGS);
Configuration.Settings settings = new Configuration.Settings { UpdateRate = oldSettings.UpdateRate };
settings[VisualizationIndex.Primary].SelectedVisualization = VisualizationType.FrequencyBars;
settings[VisualizationIndex.Primary].FrequencyBarsConfiguration = oldSettings.FrequencyBarsVisualizationProviderConfiguration;
settings[VisualizationIndex.Primary].EqualizerConfiguration = oldSettings.EqualizerConfiguration;
settings[VisualizationIndex.Secondary].SelectedVisualization = VisualizationType.Beat;
settings[VisualizationIndex.Secondary].BeatConfiguration = oldSettings.BeatVisualizationProviderConfiguration;
settings[VisualizationIndex.Tertiary].SelectedVisualization = VisualizationType.Level;
settings[VisualizationIndex.Tertiary].LevelConfiguration = oldSettings.LevelVisualizationProviderConfiguration;
return settings;
}
catch
{
return null;
}
}
public static void CleanupOldConfigs()
{
if (File.Exists(PATH_V1_SETTINGS))
File.Delete(PATH_V1_SETTINGS);
}
#endregion
}
}

View File

@ -0,0 +1,37 @@
using KeyboardAudioVisualizer.Helper;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.Legacy
{
public static class ConfigurationUpdates
{
#region Methods
public static void PerformOn(Configuration.Settings settings)
{
if (settings.Version < 1)
UpdateTo1(settings);
}
private static void UpdateTo1(Configuration.Settings settings)
{
settings.Visualizations[VisualizationIndex.Primary].Gradient = new LinearGradient(new GradientStop(0, HSVColor.Create(300, 1, 1)),
new GradientStop(0.20, HSVColor.Create(225, 1, 1)),
new GradientStop(0.35, HSVColor.Create(180, 1, 1)),
new GradientStop(0.50, HSVColor.Create(135, 1, 1)),
new GradientStop(0.65, HSVColor.Create(90, 1, 1)),
new GradientStop(0.80, HSVColor.Create(45, 1, 1)),
new GradientStop(0.95, HSVColor.Create(0, 1, 1)));
settings.Visualizations[VisualizationIndex.Secondary].Gradient = new LinearGradient(new GradientStop(0.5, new Color(255, 255, 255)));
settings.Visualizations[VisualizationIndex.Tertiary].Gradient = new LinearGradient(new GradientStop(0, new Color(0, 0, 255)),
new GradientStop(1, new Color(255, 0, 0)));
settings.Version = 1;
}
#endregion
}
}

View File

@ -2,7 +2,7 @@
using System.Xml;
using System.Xml.Serialization;
namespace KeyboardAudioVisualizer.Helper
namespace KeyboardAudioVisualizer.Legacy
{
public static class SerializationHelper
{

View File

@ -0,0 +1,26 @@
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.Configuration;
namespace KeyboardAudioVisualizer.Legacy
{
public class Settings
{
#region General
public double UpdateRate { get; set; } = 40.0;
#endregion
#region AudioProcessing
public EqualizerConfiguration EqualizerConfiguration { get; set; } = new EqualizerConfiguration();
public FrequencyBarsVisualizationProviderConfiguration FrequencyBarsVisualizationProviderConfiguration { get; set; } = new FrequencyBarsVisualizationProviderConfiguration();
public LevelVisualizationProviderConfiguration LevelVisualizationProviderConfiguration { get; set; } = new LevelVisualizationProviderConfiguration();
public BeatVisualizationProviderConfiguration BeatVisualizationProviderConfiguration { get; set; } = new BeatVisualizationProviderConfiguration();
#endregion
}
}

View File

@ -1,6 +1,4 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
@ -12,7 +10,7 @@ using System.Windows;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("KeyboardAudioVisualizer")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyCopyright("Copyright © Wyrez 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@ -51,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.3.1.1")]
[assembly: AssemblyFileVersion("1.3.1.1")]

View File

@ -8,11 +8,14 @@
<styles:CachedResourceDictionary.MergedDictionaries>
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/BlurredDecorationWindow.xaml" />
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/ImageButton.xaml" />
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/Formular.xaml" />
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/Form.xaml" />
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/GroupBox.xaml" />
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/ToolTip.xaml" />
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/ComboBox.xaml" />
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/Button.xaml" />
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/Slider.xaml" />
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/ColorSelector.xaml" />
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/GradientEditor.xaml" />
</styles:CachedResourceDictionary.MergedDictionaries>
<converter:EqualsToBoolConverter x:Key="EqualsToBoolConverter" />
@ -21,11 +24,12 @@
<Style TargetType="{x:Type controls:BlurredDecorationWindow}" BasedOn="{StaticResource StyleBlurredDecorationWindow}" />
<Style TargetType="{x:Type ui:ConfigurationWindow}" BasedOn="{StaticResource StyleBlurredDecorationWindow}" />
<Style TargetType="{x:Type controls:ImageButton}" BasedOn="{StaticResource StyleImageButton}" />
<Style TargetType="{x:Type controls:Formular}" BasedOn="{StaticResource StyleFormular}" />
<Style TargetType="{x:Type controls:Form}" BasedOn="{StaticResource StyleForm}" />
<Style TargetType="GroupBox" BasedOn="{StaticResource StyleGroupBoxBox}" />
<Style TargetType="ToolTip" BasedOn="{StaticResource StyleToolTip}" />
<Style TargetType="ComboBox" BasedOn="{StaticResource StyleComboBox}" />
<Style TargetType="Button" BasedOn="{StaticResource StyleButton}" />
<Style TargetType="Slider" BasedOn="{StaticResource StyleSlider}" />
</styles:CachedResourceDictionary>

View File

@ -0,0 +1,305 @@
<styles:CachedResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:KeyboardAudioVisualizer.Controls"
xmlns:styles="clr-namespace:KeyboardAudioVisualizer.Styles">
<DrawingBrush x:Key="BrushChessboard"
TileMode="Tile"
Viewport="0,0,16,16"
ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="#FF808080"
Geometry="M5,5 L0,5 0,10 5,10 5,5 10,5 10,0 5,0 Z"/>
</DrawingBrush.Drawing>
</DrawingBrush>
<DrawingBrush x:Key="BrushChessboardSmall"
TileMode="Tile"
Viewport="0,0,8,8"
ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="#FF808080"
Geometry="M5,5 L0,5 0,10 5,10 5,5 10,5 10,0 5,0 Z"/>
</DrawingBrush.Drawing>
</DrawingBrush>
<Style x:Key="StyleSliderLabel"
TargetType="TextBlock">
<Setter Property="TextAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style x:Key="StyleSliderValue"
TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style x:Key="StyleThumbSlider"
TargetType="Thumb">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Height" Value="NaN" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<Border VerticalAlignment="Stretch"
Width="4"
SnapsToDevicePixels="True"
Opacity="0.66"
BorderThickness="1"
Background="#FFFFFFFF"
BorderBrush="#FF000000" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="StyleSlider"
TargetType="Slider">
<Setter Property="Focusable" Value="False" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="#FF000000" />
<Setter Property="IsMoveToPointEnabled" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Slider">
<Border SnapsToDevicePixels="True"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="#FFFFFFFF">
<Border Margin="1"
SnapsToDevicePixels="True"
Background="{StaticResource BrushChessboardSmall}">
<Border SnapsToDevicePixels="True"
Background="{TemplateBinding Background}">
<Grid x:Name="GridTrackParent" SnapsToDevicePixels="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Track x:Name="PART_Track"
Grid.Row="1"
Width="{Binding ActualWidth, ElementName=GridTrackParent}"
Height="{Binding ActualHeight, ElementName=GridTrackParent}">
<Track.DecreaseRepeatButton>
<RepeatButton Visibility="Hidden" Command="Slider.DecreaseLarge" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource StyleThumbSlider}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Visibility="Hidden" Command="Slider.IncreaseLarge" />
</Track.IncreaseRepeatButton>
</Track>
</Grid>
</Border>
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="StyleColorSelector"
TargetType="{x:Type controls:ColorSelector}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Padding" Value="0" />
<Setter Property="Width" Value="504" />
<Setter Property="Height" Value="232" />
<Setter Property="FontSize" Value="13" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:ColorSelector}">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<DockPanel Margin="{TemplateBinding Padding}">
<Border DockPanel.Dock="Left"
SnapsToDevicePixels="True"
Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}"
BorderThickness="1"
BorderBrush="#FF000000"
Background="#FFFFFFFF">
<Grid x:Name="PART_Selector"
Margin="1"
SnapsToDevicePixels="True" />
</Border>
<Slider DockPanel.Dock="Left"
Margin="8,0"
Height="24"
Orientation="Horizontal"
Style="{StaticResource StyleSlider}"
Background="{Binding Background, ElementName=PART_SliderHue}"
Minimum="{Binding Minimum, ElementName=PART_SliderHue}"
Maximum="{Binding Maximum, ElementName=PART_SliderHue}"
Value="{Binding Value, ElementName=PART_SliderHue}">
<Slider.LayoutTransform>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="90" />
</Slider.LayoutTransform>
</Slider>
<DockPanel>
<Grid DockPanel.Dock="Bottom">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="12" />
<RowDefinition Height="Auto" />
<RowDefinition Height="8" />
<RowDefinition Height="Auto" />
<RowDefinition Height="8" />
<RowDefinition Height="Auto" />
<RowDefinition Height="12" />
<RowDefinition Height="Auto" />
<RowDefinition Height="8" />
<RowDefinition Height="Auto" />
<RowDefinition Height="8" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="4" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="4" />
<ColumnDefinition Width="28" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0"
Style="{StaticResource StyleSliderLabel}"
Text="Alpha:" />
<Slider x:Name="PART_SliderAlpha"
Grid.Row="0" Grid.Column="2"
Orientation="Horizontal"
Style="{StaticResource StyleSlider}"
Minimum="0"
Maximum="255" />
<TextBlock Grid.Row="0" Grid.Column="4"
Style="{StaticResource StyleSliderValue}"
Text="{Binding Value, ElementName=PART_SliderAlpha, StringFormat=##0}" />
<TextBlock Grid.Row="2" Grid.Column="0"
Style="{StaticResource StyleSliderLabel}"
Text="Hue:" />
<Slider x:Name="PART_SliderHue"
Grid.Row="2" Grid.Column="2"
Orientation="Horizontal"
Style="{StaticResource StyleSlider}"
IsSnapToTickEnabled="False"
Minimum="0"
Maximum="360" />
<TextBlock Grid.Row="2" Grid.Column="4"
Style="{StaticResource StyleSliderValue}"
Text="{Binding Value, ElementName=PART_SliderHue, StringFormat=##0}" />
<TextBlock Grid.Row="4" Grid.Column="0"
Style="{StaticResource StyleSliderLabel}"
Text="Sat:" />
<Slider x:Name="PART_SliderSaturation"
Grid.Row="4" Grid.Column="2"
Orientation="Horizontal"
Style="{StaticResource StyleSlider}"
IsSnapToTickEnabled="False"
Minimum="0"
Maximum="1" />
<TextBlock Grid.Row="4" Grid.Column="4"
Style="{StaticResource StyleSliderValue}"
Text="{Binding Value, ElementName=PART_SliderSaturation, StringFormat=0.00}" />
<TextBlock Grid.Row="6" Grid.Column="0"
Style="{StaticResource StyleSliderLabel}"
Text="Value:" />
<Slider x:Name="PART_SliderValue"
Grid.Row="6" Grid.Column="2"
Orientation="Horizontal"
Style="{StaticResource StyleSlider}"
IsSnapToTickEnabled="False"
Minimum="0"
Maximum="1" />
<TextBlock Grid.Row="6" Grid.Column="4"
Style="{StaticResource StyleSliderValue}"
Text="{Binding Value, ElementName=PART_SliderValue, StringFormat=0.00}" />
<TextBlock Grid.Row="8" Grid.Column="0"
Style="{StaticResource StyleSliderLabel}"
Text="Red:" />
<Slider x:Name="PART_SliderRed"
Grid.Row="8" Grid.Column="2"
Orientation="Horizontal"
Style="{StaticResource StyleSlider}"
TickFrequency="1"
IsSnapToTickEnabled="True"
Minimum="0"
Maximum="255" />
<TextBlock Grid.Row="8" Grid.Column="4"
Style="{StaticResource StyleSliderValue}"
Text="{Binding Value, ElementName=PART_SliderRed, StringFormat=000}" />
<TextBlock Grid.Row="10" Grid.Column="0"
Style="{StaticResource StyleSliderLabel}"
Text="Green:" />
<Slider x:Name="PART_SliderGreen"
Grid.Row="10" Grid.Column="2"
Orientation="Horizontal"
Style="{StaticResource StyleSlider}"
TickFrequency="1"
IsSnapToTickEnabled="True"
Minimum="0"
Maximum="255" />
<TextBlock Grid.Row="10" Grid.Column="4"
Style="{StaticResource StyleSliderValue}"
Text="{Binding Value, ElementName=PART_SliderGreen, StringFormat=000}" />
<TextBlock Grid.Row="12" Grid.Column="0"
Style="{StaticResource StyleSliderLabel}"
Text="Blue:" />
<Slider x:Name="PART_SliderBlue"
Grid.Row="12" Grid.Column="2"
Orientation="Horizontal"
Style="{StaticResource StyleSlider}"
TickFrequency="1"
IsSnapToTickEnabled="True"
Minimum="0"
Maximum="255" />
<TextBlock Grid.Row="12" Grid.Column="4"
Style="{StaticResource StyleSliderValue}"
Text="{Binding Value, ElementName=PART_SliderBlue, StringFormat=000}" />
</Grid>
<Border HorizontalAlignment="Stretch"
Margin="0,0,0,4"
SnapsToDevicePixels="True"
BorderThickness="1"
BorderBrush="#FF000000"
Background="#FFFFFFFF">
<Border Margin="1"
SnapsToDevicePixels="True"
Background="{StaticResource BrushChessboard}">
<Rectangle x:Name="PART_Preview"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
SnapsToDevicePixels="True" />
</Border>
</Border>
</DockPanel>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type controls:ColorSelector}" BasedOn="{StaticResource StyleColorSelector}" />
</styles:CachedResourceDictionary>

View File

@ -7,9 +7,9 @@
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml" />
</styles:CachedResourceDictionary.MergedDictionaries>
<Style x:Key="StyleFormular"
<Style x:Key="StyleForm"
BasedOn="{StaticResource StyleFrameworkElement}"
TargetType="{x:Type controls:Formular}">
TargetType="{x:Type controls:Form}">
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="LabelWidth" Value="112" />
@ -17,7 +17,7 @@
<Setter Property="RowSpacing" Value="8" />
</Style>
<Style x:Key="StyleLabelFormular"
<Style x:Key="StyleLabelForm"
TargetType="Label">
<Setter Property="Foreground" Value="{StaticResource BrushForeground}" />
<Setter Property="FontSize" Value="{StaticResource FontSizeDefault}" />
@ -44,7 +44,7 @@
</Setter>
</Style>
<Style x:Key="StyleTextBlockFormular"
<Style x:Key="StyleTextBlockForm"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource BrushForeground}" />
<Setter Property="FontSize" Value="{StaticResource FontSizeDefault}" />
@ -53,7 +53,7 @@
<Setter Property="TextAlignment" Value="Left" />
</Style>
<Style x:Key="StyleListBoxItemFormular"
<Style x:Key="StyleListBoxItemForm"
TargetType="ListBoxItem">
<Setter Property="MinWidth" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
@ -77,7 +77,7 @@
</Setter>
</Style>
<Style x:Key="StyleListBoxFormular"
<Style x:Key="StyleListBoxForm"
TargetType="ListBox">
<Setter Property="Focusable" Value="False" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
@ -92,7 +92,7 @@
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="ItemContainerStyle" Value="{StaticResource StyleListBoxItemFormular}" />
<Setter Property="ItemContainerStyle" Value="{StaticResource StyleListBoxItemForm}" />
</Style>
</styles:CachedResourceDictionary>

View File

@ -0,0 +1,93 @@
<styles:CachedResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:KeyboardAudioVisualizer.Controls"
xmlns:styles="clr-namespace:KeyboardAudioVisualizer.Styles">
<styles:CachedResourceDictionary.MergedDictionaries>
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/ColorSelector.xaml" />
</styles:CachedResourceDictionary.MergedDictionaries>
<Style x:Key="StyleGradientStop"
TargetType="ContentControl">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Width" Value="12" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="#FFFFFFFF" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type GradientStop}">
<Grid>
<Path Stretch="Fill"
Data="M 0.5,0 L 0,0.25 L 0,1 L 1,1 L 1,0.25 Z"
StrokeThickness="0"
Fill="{StaticResource BrushChessboardSmall}" />
<Path Stretch="Fill"
Data="M 0.5,0 L 0,0.25 L 0,1 L 1,1 L 1,0.25 Z"
Stroke="{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=ContentControl}}"
StrokeThickness="{Binding BorderThickness, RelativeSource={RelativeSource AncestorType=ContentControl}}"
Fill="{Binding Background, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding (controls:GradientEditor.IsSelected), RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="BorderBrush" Value="#FF808080" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="StyleGradientEditor"
TargetType="{x:Type controls:GradientEditor}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Height" Value="60" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="GradientStopStyle" Value="{StaticResource StyleGradientStop}" />
<Setter Property="ColorSelectorTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type GradientStop}">
<GroupBox>
<controls:ColorSelector Foreground="#FFFFFFFF" SelectedColor="{Binding Color}" />
</GroupBox>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:GradientEditor}">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<DockPanel>
<Border DockPanel.Dock="Bottom"
Height="16">
<Canvas x:Name="PART_Stops"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="Transparent" />
</Border>
<Border Background="{StaticResource BrushChessboard}">
<Canvas x:Name="PART_Gradient"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="Transparent" />
</Border>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type controls:GradientEditor}" BasedOn="{StaticResource StyleGradientEditor}" />
</styles:CachedResourceDictionary>

View File

@ -0,0 +1,31 @@
<styles:CachedResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:styles="clr-namespace:KeyboardAudioVisualizer.Styles"
xmlns:attached="clr-namespace:KeyboardAudioVisualizer.Attached">
<styles:CachedResourceDictionary.MergedDictionaries>
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/Theme.xaml" />
</styles:CachedResourceDictionary.MergedDictionaries>
<Style x:Key="StyleSlider"
BasedOn="{StaticResource {x:Type Slider}}"
TargetType="Slider">
<!-- ReSharper disable Xaml.RedundantStyledValue - -->
<Setter Property="attached:SliderValue.IsShown" Value="True" />
<Setter Property="attached:SliderValue.Background" Value="{StaticResource BrushTooltipBackground}" />
<Setter Property="attached:SliderValue.BorderBrush" Value="{StaticResource BrushTooltipBorder}" />
<Setter Property="attached:SliderValue.Foreground" Value="{StaticResource BrushTooltipForeground}" />
<Setter Property="attached:SliderValue.FontSize" Value="{StaticResource FontSizeTooltip}" />
<Setter Property="attached:SliderValue.Font" Value="pack://application:,,,/Resources/#Cinzel" />
<!-- ReSharper restore Xaml.RedundantStyledValue -->
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.2" />
</Trigger>
</Style.Triggers>
</Style>
</styles:CachedResourceDictionary>

View File

@ -13,14 +13,14 @@
<DataTemplate DataType="{x:Type visualizationProvider:BeatVisualizationProviderConfiguration}">
<Grid>
<Grid.Resources>
<Style BasedOn="{StaticResource StyleLabelFormular}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockFormular}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxFormular}" TargetType="ListBox" />
<Style BasedOn="{StaticResource StyleLabelForm}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockForm}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxForm}" TargetType="ListBox" />
</Grid.Resources>
<controls:Formular LabelWidth="240">
<Label controls:Formular.IsLabel="True" Content="No configuration available ..." />
</controls:Formular>
<controls:Form LabelWidth="240">
<Label controls:Form.IsLabel="True" Content="No configuration available ..." />
</controls:Form>
</Grid>
</DataTemplate>

View File

@ -3,7 +3,8 @@
xmlns:styles="clr-namespace:KeyboardAudioVisualizer.Styles"
xmlns:visualizationProvider="clr-namespace:KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider"
xmlns:controls="clr-namespace:KeyboardAudioVisualizer.Controls"
xmlns:system="clr-namespace:System;assembly=mscorlib">
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:attached="clr-namespace:KeyboardAudioVisualizer.Attached">
<styles:CachedResourceDictionary.MergedDictionaries>
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml" />
@ -13,9 +14,9 @@
<DataTemplate DataType="{x:Type visualizationProvider:FrequencyBarsVisualizationProviderConfiguration}">
<Grid>
<Grid.Resources>
<Style BasedOn="{StaticResource StyleLabelFormular}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockFormular}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxFormular}" TargetType="ListBox" />
<Style BasedOn="{StaticResource StyleLabelForm}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockForm}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxForm}" TargetType="ListBox" />
<ObjectDataProvider x:Key="SpectrumModes" MethodName="GetValues" ObjectType="{x:Type system:Enum}">
<ObjectDataProvider.MethodParameters>
@ -38,50 +39,62 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<controls:Formular Grid.Column="0">
<Label controls:Formular.IsLabel="True" Content="Spectrum:" />
<ComboBox controls:Formular.Fill="True"
<controls:Form Grid.Column="0">
<Label controls:Form.IsLabel="True" Content="Spectrum:" />
<ComboBox controls:Form.Fill="True"
ItemsSource="{Binding Source={StaticResource SpectrumModes}}"
SelectedItem="{Binding SpectrumMode}"/>
SelectedItem="{Binding SpectrumMode}"
ToolTip="Defines how the spectrum is grouped together.&#x0a; - Linear: Each bar represents the same range of frequencies.&#x0a; - Logarithmic: The higher the frequencies get the wider the range of grouped frequencies.&#x0a; - Gamma: Uses a configurable Gamma-Value to group the frequencies." />
<Label controls:Formular.IsLabel="True" Content="Value:" />
<ComboBox controls:Formular.Fill="True"
<Label controls:Form.IsLabel="True" Content="Value:" />
<ComboBox controls:Form.Fill="True"
ItemsSource="{Binding Source={StaticResource ValueModes}}"
SelectedItem="{Binding ValueMode}"/>
SelectedItem="{Binding ValueMode}"
ToolTip="Defines how the value of a bar is calculated.&#x0a; - Sum: Sums the power of all frequencies grouped in the bar using all available data.&#x0a; - Max: Uses the maximum value in each group making sure peaks are caught well.&#x0a; - Average: Uses the average of all frequencies grouped in the bar. " />
<Label controls:Formular.IsLabel="True" Content="Bars:" />
<Slider controls:Formular.Fill="True" Minimum="1" Maximum="96" IsSnapToTickEnabled="True" TickFrequency="1" TickPlacement="BottomRight"
Value="{Binding Bars}" />
</controls:Formular>
<Label controls:Form.IsLabel="True" Content="Bars:" />
<Slider controls:Form.Fill="True" Minimum="1" Maximum="96" IsSnapToTickEnabled="True" TickFrequency="1" TickPlacement="BottomRight"
Value="{Binding Bars}"
ToolTip="The number of bars used to represent the spectrum.." />
</controls:Form>
<controls:Formular Grid.Column="2">
<Label controls:Formular.IsLabel="True" Content="Min Freq.:" />
<Slider controls:Formular.Fill="True" Minimum="0" Maximum="22100" IsSnapToTickEnabled="True" TickFrequency="10" TickPlacement="None"
Value="{Binding MinFrequency}" />
<controls:Form Grid.Column="2">
<Label controls:Form.IsLabel="True" Content="Min Freq.:" />
<Slider controls:Form.Fill="True" Minimum="0" Maximum="22100" IsSnapToTickEnabled="True" TickFrequency="10" TickPlacement="None"
Value="{Binding MinFrequency}"
attached:SliderValue.Unit="Hz"
ToolTip="The minimum frequency used in the graph." />
<Label controls:Formular.IsLabel="True" Content="Max Freq.:" />
<Slider controls:Formular.Fill="True" Minimum="0" Maximum="22100" IsSnapToTickEnabled="True" TickFrequency="10" TickPlacement="None"
Value="{Binding MaxFrequency}" />
<Label controls:Form.IsLabel="True" Content="Max Freq.:" />
<Slider controls:Form.Fill="True" Minimum="0" Maximum="22100" IsSnapToTickEnabled="True" TickFrequency="10" TickPlacement="None"
Value="{Binding MaxFrequency}"
attached:SliderValue.Unit="Hz"
ToolTip="The maximum frequency used in the graph." />
<Label controls:Formular.IsLabel="True" Content="Gamma:" />
<Slider controls:Formular.Fill="True" Minimum="1" Maximum="6" IsSnapToTickEnabled="True" TickFrequency="0.25" TickPlacement="BottomRight"
<Label controls:Form.IsLabel="True" Content="Gamma:" />
<Slider controls:Form.Fill="True" Minimum="1" Maximum="6" IsSnapToTickEnabled="True" TickFrequency="0.25" TickPlacement="BottomRight"
IsEnabled="{Binding SpectrumMode, Converter={StaticResource EqualsToBoolConverter}, ConverterParameter={x:Static visualizationProvider:SpectrumMode.Gamma}}"
Value="{Binding Gamma}" />
</controls:Formular>
Value="{Binding Gamma}"
ToolTip="Only used if 'Gamma' is selected as spectrum!&#x0a;Higher values cause more compression of high frequencies." />
</controls:Form>
<controls:Formular Grid.Column="4">
<Label controls:Formular.IsLabel="True" controls:Formular.LineBreaks="1" Content="Reference:" />
<Slider controls:Formular.Fill="True" Minimum="1" Maximum="240" IsSnapToTickEnabled="True" TickFrequency="1" TickPlacement="None"
Value="{Binding ReferenceLevel}" />
<controls:Form Grid.Column="4">
<Label controls:Form.IsLabel="True" controls:Form.LineBreaks="1" Content="Reference:" />
<Slider controls:Form.Fill="True" Minimum="1" Maximum="240" IsSnapToTickEnabled="True" TickFrequency="1" TickPlacement="None"
Value="{Binding ReferenceLevel}"
attached:SliderValue.Unit="dB"
ToolTip="The reference value used to calculate the power of each bar.&#x0a;(You can read this as 'scaling')" />
<Label controls:Formular.IsLabel="True" controls:Formular.LineBreaks="1" Content="Smoothing:" />
<Slider controls:Formular.Fill="True" Minimum="1" Maximum="10" IsSnapToTickEnabled="True" TickFrequency="0.5" TickPlacement="BottomRight"
Value="{Binding Smoothing}" />
<Label controls:Form.IsLabel="True" controls:Form.LineBreaks="1" Content="Smoothing:" />
<Slider controls:Form.Fill="True" Minimum="1" Maximum="10" IsSnapToTickEnabled="True" TickFrequency="0.5" TickPlacement="BottomRight"
Value="{Binding Smoothing}"
ToolTip="Smooths the graph to prevent flickering.&#x0a;Low values will cause a hectic fast plot, high values a slow one without peaks." />
<Label controls:Formular.IsLabel="True" controls:Formular.LineBreaks="1" Content="Emphasize:" />
<Slider controls:Formular.Fill="True" Minimum="0" Maximum="2" IsSnapToTickEnabled="True" TickFrequency="0.05" TickPlacement="BottomRight"
Value="{Binding EmphasisePeaks}" />
</controls:Formular>
<Label controls:Form.IsLabel="True" controls:Form.LineBreaks="1" Content="Emphasize:" />
<Slider controls:Form.Fill="True" Minimum="0" Maximum="2" IsSnapToTickEnabled="True" TickFrequency="0.05" TickPlacement="BottomRight"
Value="{Binding EmphasisePeaks}"
ToolTip="Emphasizes peaks. The higher the value, the bigger the difference between a 'loud-bar' and a 'quite-bar'." />
</controls:Form>
</Grid>
</DataTemplate>

View File

@ -13,9 +13,9 @@
<DataTemplate DataType="{x:Type visualizationProvider:LevelVisualizationProviderConfiguration}">
<Grid>
<Grid.Resources>
<Style BasedOn="{StaticResource StyleLabelFormular}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockFormular}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxFormular}" TargetType="ListBox" />
<Style BasedOn="{StaticResource StyleLabelForm}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockForm}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxForm}" TargetType="ListBox" />
<ObjectDataProvider x:Key="ConversionModes" MethodName="GetValues" ObjectType="{x:Type system:Enum}">
<ObjectDataProvider.MethodParameters>
@ -32,24 +32,27 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<controls:Formular Grid.Column="0">
<Label controls:Formular.IsLabel="True" Content="Calculation:" />
<ComboBox controls:Formular.Fill="True"
<controls:Form Grid.Column="0">
<Label controls:Form.IsLabel="True" Content="Calculation:" />
<ComboBox controls:Form.Fill="True"
ItemsSource="{Binding Source={StaticResource ConversionModes}}"
SelectedItem="{Binding ConversionMode}"/>
</controls:Formular>
SelectedItem="{Binding ConversionMode}"
ToolTip="Defines how the RMS of the audio is plotted.&#x0a;Exponential has the widest range of peaks while linear has the least." />
</controls:Form>
<controls:Formular Grid.Column="2">
<Label controls:Formular.IsLabel="True" Content="Scale:" />
<Slider controls:Formular.Fill="True" Minimum="1" Maximum="20" IsSnapToTickEnabled="True" TickFrequency="0.5" TickPlacement="BottomRight"
Value="{Binding Scale}" />
</controls:Formular>
<controls:Form Grid.Column="2">
<Label controls:Form.IsLabel="True" Content="Scale:" />
<Slider controls:Form.Fill="True" Minimum="1" Maximum="40" IsSnapToTickEnabled="True" TickFrequency="0.5" TickPlacement="BottomRight"
Value="{Binding Scale}"
ToolTip="Scales the whole graph." />
</controls:Form>
<controls:Formular Grid.Column="4">
<Label controls:Formular.IsLabel="True" controls:Formular.LineBreaks="1" Content="Smoothing:" />
<Slider controls:Formular.Fill="True" Minimum="1" Maximum="10" IsSnapToTickEnabled="True" TickFrequency="0.5" TickPlacement="BottomRight"
Value="{Binding Smoothing}" />
</controls:Formular>
<controls:Form Grid.Column="4">
<Label controls:Form.IsLabel="True" controls:Form.LineBreaks="1" Content="Smoothing:" />
<Slider controls:Form.Fill="True" Minimum="1" Maximum="10" IsSnapToTickEnabled="True" TickFrequency="0.5" TickPlacement="BottomRight"
Value="{Binding Smoothing}"
ToolTip="Smooths the plot to prevent flickering.&#x0a;Low values will cause a hectic fast plot, high values a slow one without peaks." />
</controls:Form>
</Grid>
</DataTemplate>

View File

@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Reflection;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.Helper;
using RGB.NET.Core;
@ -14,16 +15,56 @@ namespace KeyboardAudioVisualizer.UI
public double UpdateRate
{
get => 1.0 / RGBSurface.Instance.UpdateFrequency;
get => 1.0 / ApplicationManager.Instance.UpdateTrigger.UpdateFrequency;
set
{
double val = MathHelper.Clamp(value, 1, 40);
double val = MathHelper.Clamp(value, 1, 60);
ApplicationManager.Instance.Settings.UpdateRate = val;
RGBSurface.Instance.UpdateFrequency = 1.0 / val;
ApplicationManager.Instance.UpdateTrigger.UpdateFrequency = 1.0 / val;
OnPropertyChanged();
}
}
public bool EnableAudioPrescale
{
get => ApplicationManager.Instance.Settings.EnableAudioPrescale;
set
{
ApplicationManager.Instance.Settings.EnableAudioPrescale = value;
OnPropertyChanged();
}
}
public VisualizationType SelectedPrimaryVisualization
{
get => ApplicationManager.Instance.Settings[VisualizationIndex.Primary].SelectedVisualization;
set
{
ApplicationManager.Instance.Settings[VisualizationIndex.Primary].SelectedVisualization = value;
ApplicationManager.Instance.ApplyVisualization(VisualizationIndex.Primary, value);
}
}
public VisualizationType SelectedSecondaryVisualization
{
get => ApplicationManager.Instance.Settings[VisualizationIndex.Secondary].SelectedVisualization;
set
{
ApplicationManager.Instance.Settings[VisualizationIndex.Secondary].SelectedVisualization = value;
ApplicationManager.Instance.ApplyVisualization(VisualizationIndex.Secondary, value);
}
}
public VisualizationType SelectedTertiaryVisualization
{
get => ApplicationManager.Instance.Settings[VisualizationIndex.Tertiary].SelectedVisualization;
set
{
ApplicationManager.Instance.Settings[VisualizationIndex.Tertiary].SelectedVisualization = value;
ApplicationManager.Instance.ApplyVisualization(VisualizationIndex.Tertiary, value);
}
}
#endregion
#region Commands

View File

@ -7,12 +7,18 @@
xmlns:controls="clr-namespace:KeyboardAudioVisualizer.Controls"
xmlns:styles="clr-namespace:KeyboardAudioVisualizer.Styles"
xmlns:core="clr-namespace:RGB.NET.Core;assembly=RGB.NET.Core"
xmlns:audioProcessing="clr-namespace:KeyboardAudioVisualizer.AudioProcessing"
xmlns:keyboardAudioVisualizer="clr-namespace:KeyboardAudioVisualizer"
xmlns:converter="clr-namespace:KeyboardAudioVisualizer.Converter"
xmlns:visualizationProvider="clr-namespace:KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:attached="clr-namespace:KeyboardAudioVisualizer.Attached"
xmlns:helper="clr-namespace:KeyboardAudioVisualizer.Helper"
mc:Ignorable="d"
Title="Keyboard Audio-Visualizer # Configuration"
Icon="pack://application:,,,/KeyboardAudioVisualizer;component/Resources/Icon.ico"
IconCommand="{Binding OpenHomepageCommand}"
Width="1280" Height="720">
Width="1280" Height="720"
Closed="ConfigurationWindow_OnClosed">
<controls:BlurredDecorationWindow.Resources>
<styles:CachedResourceDictionary>
@ -25,6 +31,20 @@
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/UI/Configuration/BeatConfiguration.xaml" />
<styles:CachedResourceDictionary Source="/KeyboardAudioVisualizer;component/UI/Visualization/BeatVisualization.xaml" />
</styles:CachedResourceDictionary.MergedDictionaries>
<converter:VisualizationTypeSelectableConverter x:Key="VisualizationProviderSelectableConverter" />
<converter:VisualizationProviderDisplayNameConverter x:Key="VisualizationProviderDisplayNameConverter" />
<converter:VisualizationToLastChildFillConverter x:Key="VisualizationToLastChildFillConverter" />
<DataTemplate x:Key="DataTemplateVisualizationSelection" DataType="{x:Type visualizationProvider:VisualizationType}">
<TextBlock Text="{Binding Converter={StaticResource VisualizationProviderDisplayNameConverter}}" />
</DataTemplate>
<ObjectDataProvider x:Key="DataProviderVisualizationTypes" ObjectType="{x:Type sys:Enum}" MethodName="GetValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="visualizationProvider:VisualizationType" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</styles:CachedResourceDictionary>
</controls:BlurredDecorationWindow.Resources>
@ -34,140 +54,180 @@
<TabControl Style="{StaticResource StyleTabControlNavigation}">
<TabItem Header="Keyboard">
<DockPanel>
<GroupBox DockPanel.Dock="Top">
<controls:Formular>
<controls:Formular.Resources>
<Style BasedOn="{StaticResource StyleLabelFormular}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockFormular}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxFormular}" TargetType="ListBox" />
</controls:Formular.Resources>
<AdornerDecorator>
<DockPanel LastChildFill="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Primary], Converter={StaticResource VisualizationToLastChildFillConverter}}">
<GroupBox DockPanel.Dock="Top">
<controls:GradientEditor Gradient="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Settings[(helper:VisualizationIndex)Primary].Gradient}"
ToolTip="Defines the gradient that's drawed on the device. Usage:&#x0a; Left click inside the preview to add a new stop.&#x0a; Left-click stop to change the color or move it.&#x0a; Right-click stop to remove it.&#x0a;" />
</GroupBox>
<Label controls:Formular.IsLabel="True" Content="Visualization: " />
<ComboBox controls:Formular.Fill="True" SelectedIndex="0">
<ComboBox.Items>
<ComboBoxItem>Frequency Bars</ComboBoxItem>
</ComboBox.Items>
</ComboBox>
</controls:Formular>
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<controls:Form>
<controls:Form.Resources>
<Style BasedOn="{StaticResource StyleLabelForm}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockForm}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxForm}" TargetType="ListBox" />
</controls:Form.Resources>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static audioProcessing:AudioProcessor.Instance}, Path=PrimaryVisualizationProvider.Configuration}" />
</GroupBox>
<Label controls:Form.IsLabel="True" Content="Visualization: " />
<ComboBox controls:Form.Fill="True" SelectedIndex="0"
ItemTemplate="{StaticResource DataTemplateVisualizationSelection}"
ItemsSource="{Binding Source={StaticResource DataProviderVisualizationTypes},
Converter={StaticResource VisualizationProviderSelectableConverter}, ConverterParameter={x:Static core:RGBDeviceType.Keyboard}}"
SelectedItem="{Binding SelectedPrimaryVisualization}"
ToolTip="The visualization shown on the device.&#x0a; - Frequency Bars: Shows vertical bars representing the frequencies of the song.&#x0a; - Level: Shows two horizontal bars representing the loudness of the song (left and right).&#x0a; - Beat: Shows a flash to the beat of the song (this doesn't work too well right now :()" />
</controls:Form>
</GroupBox>
<GroupBox Margin="0,8,0,0">
<ContentControl Content="{Binding Source={x:Static audioProcessing:AudioProcessor.Instance}, Path=PrimaryVisualizationProvider}" />
</GroupBox>
</DockPanel>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Primary].Configuration}" />
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Primary]}" Tag="0" />
</GroupBox>
</DockPanel>
</AdornerDecorator>
</TabItem>
<TabItem Header="Mouse/Headset">
<DockPanel LastChildFill="False">
<GroupBox DockPanel.Dock="Top">
<controls:Formular>
<controls:Formular.Resources>
<Style BasedOn="{StaticResource StyleLabelFormular}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockFormular}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxFormular}" TargetType="ListBox" />
</controls:Formular.Resources>
<AdornerDecorator>
<DockPanel LastChildFill="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Secondary], Converter={StaticResource VisualizationToLastChildFillConverter}}">
<GroupBox DockPanel.Dock="Top">
<controls:GradientEditor Gradient="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Settings[(helper:VisualizationIndex)Secondary].Gradient}"
CanAddOrDeleteStops="False" />
</GroupBox>
<Label controls:Formular.IsLabel="True" Content="Visualization: " />
<ComboBox controls:Formular.Fill="True" SelectedIndex="0">
<ComboBox.Items>
<ComboBoxItem>Beat-Detection</ComboBoxItem>
</ComboBox.Items>
</ComboBox>
</controls:Formular>
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<controls:Form>
<controls:Form.Resources>
<Style BasedOn="{StaticResource StyleLabelForm}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockForm}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxForm}" TargetType="ListBox" />
</controls:Form.Resources>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static audioProcessing:AudioProcessor.Instance}, Path=SecondaryVisualizationProvider.Configuration}" />
</GroupBox>
<Label controls:Form.IsLabel="True" Content="Visualization: " />
<ComboBox controls:Form.Fill="True" SelectedIndex="0"
ItemTemplate="{StaticResource DataTemplateVisualizationSelection}"
ItemsSource="{Binding Source={StaticResource DataProviderVisualizationTypes},
Converter={StaticResource VisualizationProviderSelectableConverter}, ConverterParameter={x:Static core:RGBDeviceType.Mouse}}"
SelectedItem="{Binding SelectedSecondaryVisualization}" />
</controls:Form>
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static audioProcessing:AudioProcessor.Instance}, Path=SecondaryVisualizationProvider}" />
</GroupBox>
</DockPanel>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Secondary].Configuration}" />
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Secondary]}" Tag="1" />
</GroupBox>
</DockPanel>
</AdornerDecorator>
</TabItem>
<TabItem Header="Lightbar/Mousepad">
<DockPanel LastChildFill="False">
<GroupBox DockPanel.Dock="Top">
<controls:Formular>
<controls:Formular.Resources>
<Style BasedOn="{StaticResource StyleLabelFormular}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockFormular}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxFormular}" TargetType="ListBox" />
</controls:Formular.Resources>
<AdornerDecorator>
<DockPanel LastChildFill="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Tertiary], Converter={StaticResource VisualizationToLastChildFillConverter}}">
<GroupBox DockPanel.Dock="Top">
<controls:GradientEditor Gradient="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Settings[(helper:VisualizationIndex)Tertiary].Gradient}" />
</GroupBox>
<Label controls:Formular.IsLabel="True" Content="Visualization: " />
<ComboBox controls:Formular.Fill="True" SelectedIndex="0">
<ComboBox.Items>
<ComboBoxItem>Level</ComboBoxItem>
</ComboBox.Items>
</ComboBox>
</controls:Formular>
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<controls:Form>
<controls:Form.Resources>
<Style BasedOn="{StaticResource StyleLabelForm}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockForm}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxForm}" TargetType="ListBox" />
</controls:Form.Resources>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static audioProcessing:AudioProcessor.Instance}, Path=TertiaryVisualizationProvider.Configuration}" />
</GroupBox>
<Label controls:Form.IsLabel="True" Content="Visualization: " />
<ComboBox controls:Form.Fill="True" SelectedIndex="0"
ItemTemplate="{StaticResource DataTemplateVisualizationSelection}"
ItemsSource="{Binding Source={StaticResource DataProviderVisualizationTypes},
Converter={StaticResource VisualizationProviderSelectableConverter}, ConverterParameter={x:Static core:RGBDeviceType.Mousepad}}"
SelectedItem="{Binding SelectedTertiaryVisualization}" />
</controls:Form>
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static audioProcessing:AudioProcessor.Instance}, Path=TertiaryVisualizationProvider}" />
</GroupBox>
</DockPanel>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Tertiary].Configuration}" />
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Tertiary]}" Tag="2" />
</GroupBox>
</DockPanel>
</AdornerDecorator>
</TabItem>
<TabItem Header="Settings">
<GroupBox VerticalAlignment="Top">
<StackPanel Orientation="Vertical">
<Grid>
<Grid.Resources>
<Style BasedOn="{StaticResource StyleLabelFormular}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockFormular}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxFormular}" TargetType="ListBox" />
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="8" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="8" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<AdornerDecorator>
<DockPanel LastChildFill="False">
<DockPanel.Resources>
<Style BasedOn="{StaticResource StyleLabelForm}" TargetType="Label" />
<Style BasedOn="{StaticResource StyleTextBlockForm}" TargetType="TextBlock" />
<Style BasedOn="{StaticResource StyleListBoxForm}" TargetType="ListBox" />
</DockPanel.Resources>
<controls:Formular Grid.Column="0">
<Label controls:Formular.IsLabel="True" Content="Version:" />
<GroupBox DockPanel.Dock="Top">
<controls:Form>
<Label controls:Form.IsLabel="True" Content="Version" />
<TextBlock Text="{Binding Version}" />
<Label controls:Formular.IsLabel="True" Content="Update-Rate" />
<Slider Minimum="1" Maximum="40" controls:Formular.Fill="True" IsSnapToTickEnabled="True" TickFrequency="1" TickPlacement="BottomRight"
Value="{Binding UpdateRate}" />
<Label controls:Form.IsLabel="True" Content="Update-Rate" />
<Slider Minimum="1" Maximum="60" controls:Form.Fill="True" IsSnapToTickEnabled="True" TickFrequency="1" TickPlacement="BottomRight"
Value="{Binding UpdateRate}"
attached:SliderValue.Unit="FPS"
ToolTip="Defines how fast the data is updated.&#x0a;Low values can reduce CPU-usage but will cause stuttering.&#x0a;Values above 40 will only affect the internal calculations and wont make the keyboard update faster.&#x0a;It's not recommended to select a value > 40." />
<Label controls:Formular.LineBreaks="1" controls:Formular.IsLabel="True" Content="Devices:" />
</controls:Formular>
</Grid>
<Label controls:Form.IsLabel="True" Content="Fix Volume" />
<CheckBox VerticalAlignment="Center"
IsChecked="{Binding EnableAudioPrescale}"
ToolTip="Scales the audio signal inverse to the OS-master-volume.&#x0a;This might (depending on the system) lead to decrased performance&#x0a; -> only activate it if you need it." />
<!-- TODO DarthAffe 05.08.2017: Fix the formular to support that use-case -->
<ItemsControl VerticalAlignment="Top" HorizontalAlignment="Left" Margin="120,-22,0,0" ItemsSource="{Binding Source={x:Static core:RGBSurface.Instance}, Path=Devices}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource StyleTextBlockFormular}">
<TextBlock.Text>
<MultiBinding StringFormat="> {0} {1} ({2})">
<Binding Path="DeviceInfo.Manufacturer" />
<Binding Path="DeviceInfo.Model" />
<Binding Path="DeviceInfo.DeviceType" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</GroupBox>
</controls:Form>
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<StackPanel Orientation="Vertical">
<Border HorizontalAlignment="Left" Width="111">
<Label HorizontalAlignment="Right"
Content="Background" />
</Border>
<controls:GradientEditor Margin="120,-18,0,0"
Gradient="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Settings.Background}"
ToolTip="Defines the gradient used as the background. Use transparency to create some kind of blur. Usage:&#x0a; Left click inside the preview to add a new stop.&#x0a; Left-click stop to change the color or move it.&#x0a; Right-click stop to remove it.&#x0a;" />
</StackPanel>
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<StackPanel Orientation="Vertical">
<Border HorizontalAlignment="Left" Width="111">
<Label HorizontalAlignment="Right"
Content="Devices" />
</Border>
<ItemsControl VerticalAlignment="Top" HorizontalAlignment="Left" Margin="120,-18,0,0" ItemsSource="{Binding Source={x:Static core:RGBSurface.Instance}, Path=Devices}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource StyleTextBlockForm}">
<TextBlock.Text>
<MultiBinding StringFormat="> {0} {1} ({2})">
<Binding Path="DeviceInfo.Manufacturer" />
<Binding Path="DeviceInfo.Model" />
<Binding Path="DeviceInfo.DeviceType" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</GroupBox>
</DockPanel>
</AdornerDecorator>
</TabItem>
</TabControl>

View File

@ -1,9 +1,16 @@
using KeyboardAudioVisualizer.Controls;
using System;
using KeyboardAudioVisualizer.Controls;
namespace KeyboardAudioVisualizer.UI
{
public partial class ConfigurationWindow : BlurredDecorationWindow
{
public ConfigurationWindow() => InitializeComponent();
//DarthAffe 07.02.2018: This prevents the applicaiton from not shutting down and crashing afterwards if 'close' is selected in the taskbar-context-menu
private void ConfigurationWindow_OnClosed(object sender, EventArgs e)
{
ApplicationManager.Instance.ExitCommand.Execute(null);
}
}
}

View File

@ -25,7 +25,9 @@
</Style>
<DataTemplate DataType="{x:Type visualizationProvider:BeatVisualizationProvider}">
<visualization:BeatVisualizer Style="{StaticResource StyleBeatVisualizer}" VisualizationProvider="{Binding}" />
<visualization:BeatVisualizer Style="{StaticResource StyleBeatVisualizer}"
VisualizationProvider="{Binding}"
VisualizationIndex="{Binding Tag, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
</styles:CachedResourceDictionary>

View File

@ -4,14 +4,20 @@ using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.Helper;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
using Color = System.Windows.Media.Color;
using Point = System.Windows.Point;
namespace KeyboardAudioVisualizer.UI.Visualization
{
public class BeatVisualizer : Control
{
#region Properties & Fields
private LinearGradient _gradient;
#endregion
#region DependencyProperties
// ReSharper disable InconsistentNaming
@ -24,6 +30,15 @@ namespace KeyboardAudioVisualizer.UI.Visualization
set => SetValue(VisualizationProviderProperty, value);
}
public static readonly DependencyProperty VisualizationIndexProperty = DependencyProperty.Register(
"VisualizationIndex", typeof(VisualizationIndex?), typeof(BeatVisualizer), new PropertyMetadata(null, VisualizationIndexChanged));
public VisualizationIndex? VisualizationIndex
{
get => (VisualizationIndex?)GetValue(VisualizationIndexProperty);
set => SetValue(VisualizationIndexProperty, value);
}
public static readonly DependencyProperty BrushProperty = DependencyProperty.Register(
"Brush", typeof(Brush), typeof(BeatVisualizer), new PropertyMetadata(default(Brush)));
@ -52,7 +67,7 @@ namespace KeyboardAudioVisualizer.UI.Visualization
RGBSurface.Instance.Updated += args => Dispatcher.BeginInvoke(new Action(Update), DispatcherPriority.Normal);
//TODO DarthAffe 12.08.2017: Create brush from config
Brush = new SolidColorBrush(Color.FromRgb(255, 255, 255));
}
#endregion
@ -75,6 +90,37 @@ namespace KeyboardAudioVisualizer.UI.Visualization
}
}
private static void VisualizationIndexChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (!(dependencyObject is BeatVisualizer visualizer)) return;
visualizer.UpdateGradient();
}
private void UpdateGradient()
{
void GradientChanged(object sender, EventArgs args) => UpdateColor();
if (_gradient != null)
_gradient.GradientChanged -= GradientChanged;
_gradient = VisualizationIndex.HasValue ? ApplicationManager.Instance.Settings[VisualizationIndex.Value].Gradient : null;
if (_gradient != null)
_gradient.GradientChanged += GradientChanged;
UpdateColor();
}
private void UpdateColor()
{
if (_gradient == null) return;
GradientStopCollection gradientStops = new GradientStopCollection();
foreach (RGB.NET.Brushes.Gradients.GradientStop stop in _gradient.GradientStops)
gradientStops.Add(new System.Windows.Media.GradientStop(System.Windows.Media.Color.FromArgb(stop.Color.GetA(), stop.Color.GetR(), stop.Color.GetG(), stop.Color.GetB()), stop.Offset));
Brush = new LinearGradientBrush(gradientStops, new System.Windows.Point(0, 0.5), new System.Windows.Point(1, 0.5));
}
#endregion
}
}

View File

@ -80,7 +80,8 @@
Margin="0,-4,0,0" Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" Content="Enable Equalizer"
Foreground="{StaticResource BrushForeground}"
IsChecked="{Binding Path=Equalizer.IsEnabled, RelativeSource={RelativeSource TemplatedParent}}" />
IsChecked="{Binding Path=Equalizer.IsEnabled, RelativeSource={RelativeSource TemplatedParent}}"
ToolTip="Enables the Equalizer.&#x0a;The Equalier Allows to finetune the graph by slective increasing/decresing the value.&#x0a;You can add new pivots by rightclicking on the visualization-window.&#x0a;Existing pivots can be deleted by rightclicking on them or moved by leftclicking and dragging around." />
<Button VerticalAlignment="Center" Margin="12,0,0,0"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Equalizer.IsEnabled, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=True}"

View File

@ -30,9 +30,12 @@
</Style>
<DataTemplate DataType="{x:Type visualizationProvider:FrequencyBarsVisualizationProvider}">
<Grid>
<visualization:FrequencyBarsVisualizer Style="{StaticResource StyleFrequencyBarsVisualizer}" VisualizationProvider="{Binding}" />
<visualization:EqualizerVisualizer Style="{StaticResource StyleEqualizerVisualizer}" Equalizer="{Binding Equalizer}" />
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<visualization:FrequencyBarsVisualizer Style="{StaticResource StyleFrequencyBarsVisualizer}"
VisualizationProvider="{Binding}"
VisualizationIndex="{Binding Tag, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
<visualization:EqualizerVisualizer Style="{StaticResource StyleEqualizerVisualizer}"
Equalizer="{Binding Equalizer}" />
</Grid>
</DataTemplate>

View File

@ -5,6 +5,7 @@ using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.Helper;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
using Color = System.Windows.Media.Color;
@ -27,12 +28,22 @@ namespace KeyboardAudioVisualizer.UI.Visualization
get => (IVisualizationProvider)GetValue(VisualizationProviderProperty);
set => SetValue(VisualizationProviderProperty, value);
}
public static readonly DependencyProperty VisualizationIndexProperty = DependencyProperty.Register(
"VisualizationIndex", typeof(VisualizationIndex?), typeof(FrequencyBarsVisualizer), new PropertyMetadata(null, VisualizationIndexChanged));
public VisualizationIndex? VisualizationIndex
{
get => (VisualizationIndex?)GetValue(VisualizationIndexProperty);
set => SetValue(VisualizationIndexProperty, value);
}
// ReSharper restore InconsistentNaming
#endregion
#region Properties & Fields
private IGradient _gradient;
private LinearGradient _gradient;
private Panel _panel;
private Rectangle[] _bars = new Rectangle[0];
@ -44,9 +55,6 @@ namespace KeyboardAudioVisualizer.UI.Visualization
{
RGBSurface.Instance.Updated += args => Dispatcher.BeginInvoke(new Action(Update), DispatcherPriority.Normal);
SizeChanged += (sender, args) => UpdateSizes();
//TODO DarthAffe 12.08.2017: Get gradient from config
_gradient = new RainbowGradient(300, -14);
}
#endregion
@ -65,8 +73,7 @@ namespace KeyboardAudioVisualizer.UI.Visualization
private static void VisualizationProviderChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
FrequencyBarsVisualizer visualizer = dependencyObject as FrequencyBarsVisualizer;
if (visualizer == null) return;
if (!(dependencyObject is FrequencyBarsVisualizer visualizer)) return;
void ConfigurationOnPropertyChanged(object sender, PropertyChangedEventArgs args) => visualizer.ConfigurationChanged(args.PropertyName);
@ -77,6 +84,26 @@ namespace KeyboardAudioVisualizer.UI.Visualization
newVisualizationProvider.Configuration.PropertyChanged += ConfigurationOnPropertyChanged;
}
private static void VisualizationIndexChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (!(dependencyObject is FrequencyBarsVisualizer visualizer)) return;
visualizer.UpdateGradient();
}
private void UpdateGradient()
{
void GradientChanged(object sender, EventArgs args) => UpdateColors();
if (_gradient != null)
_gradient.GradientChanged -= GradientChanged;
_gradient = VisualizationIndex.HasValue ? ApplicationManager.Instance.Settings[VisualizationIndex.Value].Gradient : null;
if (_gradient != null)
_gradient.GradientChanged += GradientChanged;
UpdateColors();
}
private void ConfigurationChanged(string changedPropertyName)
{
if ((changedPropertyName == null) || (changedPropertyName == nameof(FrequencyBarsVisualizationProviderConfiguration.Bars)))
@ -88,6 +115,8 @@ namespace KeyboardAudioVisualizer.UI.Visualization
if (_panel == null) return;
_panel.Children.Clear();
if (VisualizationProvider == null) return;
_bars = new Rectangle[((FrequencyBarsVisualizationProviderConfiguration)VisualizationProvider.Configuration).Bars];
for (int i = 0; i < _bars.Length; i++)
@ -117,10 +146,12 @@ namespace KeyboardAudioVisualizer.UI.Visualization
private void UpdateColors()
{
if (_gradient == null) return;
for (int i = 0; i < _bars.Length; i++)
{
RGB.NET.Core.Color color = _gradient.GetColor((double)i / _bars.Length);
_bars[i].Fill = new SolidColorBrush(Color.FromRgb(color.R, color.G, color.B));
_bars[i].Fill = new SolidColorBrush(Color.FromRgb(color.GetR(), color.GetG(), color.GetB()));
}
}

View File

@ -63,7 +63,9 @@
</Style>
<DataTemplate DataType="{x:Type visualizationProvider:LevelVisualizationProvider}">
<visualization:LevelVisualizer Style="{StaticResource StyleLevelVisualizer}" VisualizationProvider="{Binding}" />
<visualization:LevelVisualizer Style="{StaticResource StyleLevelVisualizer}"
VisualizationProvider="{Binding}"
VisualizationIndex="{Binding Tag, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
</styles:CachedResourceDictionary>

View File

@ -1,17 +1,27 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.Helper;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
using Color = System.Windows.Media.Color;
using GradientStop = RGB.NET.Brushes.Gradients.GradientStop;
using Point = System.Windows.Point;
namespace KeyboardAudioVisualizer.UI.Visualization
{
public class LevelVisualizer : Control
{
#region Properties & Fields
private LinearGradient _gradient;
#endregion
#region DependencyProperties
// ReSharper disable InconsistentNaming
@ -24,6 +34,15 @@ namespace KeyboardAudioVisualizer.UI.Visualization
set => SetValue(VisualizationProviderProperty, value);
}
public static readonly DependencyProperty VisualizationIndexProperty = DependencyProperty.Register(
"VisualizationIndex", typeof(VisualizationIndex?), typeof(LevelVisualizer), new PropertyMetadata(null, VisualizationIndexChanged));
public VisualizationIndex? VisualizationIndex
{
get => (VisualizationIndex?)GetValue(VisualizationIndexProperty);
set => SetValue(VisualizationIndexProperty, value);
}
public static readonly DependencyProperty BrushLeftProperty = DependencyProperty.Register(
"BrushLeft", typeof(Brush), typeof(LevelVisualizer), new PropertyMetadata(default(Brush)));
@ -68,10 +87,6 @@ namespace KeyboardAudioVisualizer.UI.Visualization
public LevelVisualizer()
{
RGBSurface.Instance.Updated += args => Dispatcher.BeginInvoke(new Action(Update), DispatcherPriority.Normal);
//TODO DarthAffe 12.08.2017: Create brush from config
BrushLeft = new LinearGradientBrush(Color.FromRgb(255, 0, 0), Color.FromRgb(0, 0, 255), new Point(0, 0.5), new Point(1, 0.5));
BrushRight = new LinearGradientBrush(Color.FromRgb(0, 0, 255), Color.FromRgb(255, 0, 0), new Point(0, 0.5), new Point(1, 0.5));
}
#endregion
@ -92,6 +107,38 @@ namespace KeyboardAudioVisualizer.UI.Visualization
SizeRight = horizontalSizeRight;
}
private void SetBrushes()
{
if (_gradient == null) return;
GradientStopCollection gradientStops = new GradientStopCollection();
foreach (GradientStop stop in _gradient.GradientStops)
gradientStops.Add(new System.Windows.Media.GradientStop(Color.FromArgb(stop.Color.GetA(), stop.Color.GetR(), stop.Color.GetG(), stop.Color.GetB()), stop.Offset));
BrushLeft = new LinearGradientBrush(gradientStops, new Point(1, 0.5), new Point(0, 0.5));
BrushRight = new LinearGradientBrush(gradientStops, new Point(0, 0.5), new Point(1, 0.5));
}
private static void VisualizationIndexChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (!(dependencyObject is LevelVisualizer visualizer)) return;
visualizer.UpdateGradient();
}
private void UpdateGradient()
{
void GradientChanged(object sender, EventArgs args) => SetBrushes();
if (_gradient != null)
_gradient.GradientChanged -= GradientChanged;
_gradient = VisualizationIndex.HasValue ? ApplicationManager.Instance.Settings[VisualizationIndex.Value].Gradient : null;
if (_gradient != null)
_gradient.GradientChanged += GradientChanged;
SetBrushes();
}
#endregion
}
}

View File

@ -1,13 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CSCore" version="1.2.1.1" targetFramework="net461" />
<package id="CSCore" version="1.2.1.2" targetFramework="net461" />
<package id="Hardcodet.NotifyIcon.Wpf" version="1.0.8" targetFramework="net461" />
<package id="MathNet.Numerics" version="3.20.0" targetFramework="net461" />
<package id="RGB.NET.Brushes" version="1.0.0" targetFramework="net461" />
<package id="RGB.NET.Core" version="1.0.0" targetFramework="net461" />
<package id="RGB.NET.Devices.CoolerMaster" version="1.0.0" targetFramework="net461" />
<package id="RGB.NET.Devices.Corsair" version="1.0.0" targetFramework="net461" />
<package id="RGB.NET.Devices.Logitech" version="1.0.0" targetFramework="net461" />
<package id="RGB.NET.Groups" version="1.0.0" targetFramework="net461" />
<package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
<package id="HidSharp" version="2.0.5" targetFramework="net461" />
<package id="MathNet.Numerics" version="4.7.0" targetFramework="net461" />
<package id="Newtonsoft.Json" version="12.0.1" targetFramework="net461" />
<package id="RGB.NET.Brushes" version="0.1.31" targetFramework="net461" />
<package id="RGB.NET.Core" version="0.1.31" targetFramework="net461" />
<package id="RGB.NET.Decorators" version="0.1.31" targetFramework="net461" />
<package id="RGB.NET.Devices.Asus" version="0.1.31" targetFramework="net461" />
<package id="RGB.NET.Devices.CoolerMaster" version="0.1.31" targetFramework="net461" />
<package id="RGB.NET.Devices.Corsair" version="0.1.31" targetFramework="net461" />
<package id="RGB.NET.Devices.Logitech" version="0.1.31" targetFramework="net461" />
<package id="RGB.NET.Devices.Novation" version="0.1.31" targetFramework="net461" />
<package id="RGB.NET.Devices.Razer" version="0.1.31" targetFramework="net461" />
<package id="RGB.NET.Devices.SteelSeries" version="0.1.31" targetFramework="net461" />
<package id="RGB.NET.Groups" version="0.1.31" targetFramework="net461" />
<package id="RGB.NET.Resources.Asus" version="0.3.0" targetFramework="net461" />
<package id="RGB.NET.Resources.CoolerMaster" version="0.2.0" targetFramework="net461" />
<package id="RGB.NET.Resources.Corsair" version="0.3.0.234" targetFramework="net461" />
<package id="RGB.NET.Resources.Logitech" version="0.3.0" targetFramework="net461" />
<package id="RGB.NET.Resources.Novation" version="0.1.0" targetFramework="net461" />
<package id="RGB.NET.Resources.Razer" version="0.3.2.4" targetFramework="net461" />
<package id="Sanford.Multimedia.Midi" version="6.6.0" targetFramework="net461" />
<package id="Sanford.Multimedia.Midi.Standard" version="6.6.0" targetFramework="net461" />
<package id="System.Management" version="4.5.0" targetFramework="net461" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net461" />
</packages>

12
NuGet.Config Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="RGB.NET" value="http://nuget.arge.be/v3/index.json" />
</packageSources>
<activePackageSource>
<add key="All" value="(Aggregate source)" />
</activePackageSource>
</configuration>

View File

@ -1,2 +1,59 @@
This software is no longer actively developed.
Consider checking out [Artemis](https://github.com/Artemis-RGB/Artemis) for a even more feature rich replacement.
# KeyboardAudioVisualizer
It's colorful - I like it!
**Example-Video:**
[![Example-Video](https://img.youtube.com/vi/mby2NYN0V1o/0.jpg)](https://www.youtube.com/watch?v=mby2NYN0V1o)
## Visualizations
#### Keyboard
- **"Frequency Bars"** - Simple spectrum visualizer.
- **"Level"** - Shows the overall volume.
- **"Beat detection"** - Pulses to the beat of the music. (This isn't working really well right now, depending on the music. But in general not satisfying, sorry.)
#### Mouse/Headset
- **"Beat detection"** - Pulses to the beat of the music. (This isn't working really well right now, depending on the music. But in general not satisfying, sorry.)
#### Mousepad/Lightbar (K95 Platinum)
- **"Level"** - Shows the overall volume.
- **"Beat detection"** - Pulses to the beat of the music. (This isn't working really well right now, depending on the music. But in general not satisfying, sorry.)
## Supported devices
- All Corsair RGB-devices.
- Logitech G910 and G610 with physical EU layout. (Untested but should work)
- (In theory every device with SDK support could be include, open an issue if you want to help with increasing the range of supported devices!)
## Settings
#### Frequency Bars
- **Spectrum:** The way the spectrum is grouped together. Values are: _(default: Logarithmic)_
- **_Linear_**: Each bar represents the same range of frequencies. Most of the time this doesn't look good since low frequencies are underrepresented.
- **_Logarithmic_**: The higher the frequencies get the wider the range of grouped frequencies. This is close to the way humans perceive sound and therfore most of the time looks quite good as long as the range of used frequencies is big enough.
- **_Gamma_**: While Gamma-correction is known from image-processing it still applies quite well to audio-data, grouping not as extreme as logarithmic but still better than linear. The used Gamma-value can be configured.
- **Value:** The way the value of a frequency bar is determined. Values are: _(default: Sum)_
- **_Sum_**: Sums the power of all frequencies grouped in the bar using all available data. Combining this with logarithmic grouping gives the most realistic representation.
- **_Max_**: Uses the maximum value in each group making sure peaks are caught well. This works quite good with gamma-grouping.
- **_Average_**: Uses the average of all frequencies grouped in the bar. This smooths out the whole graph quite a lot.
- **Bars:** The number of bars used to represent the spectrum. _(default: 48)_
- **Min Freq.:** The minimum frequency used in the graph. This value shouldn't be modified. _(default: 60)_
- **Max Freq.:** The maximum frequency used in the graph. This value can be lowered to increase the value of lower frequencies. Using high values might lead to death bars if the audio is mastered with an low-pass filter cutting high frequencies. _(default: 15000)_
- **Gamma:** The correction value used for gamma-grouping (disabled if any other grouping is selected). High values lead to a stronger compression of high frequencies. _(default: 2)_
- **Reference:** The reference value used to calculate the power of each bar. Adjust this to your audio volume. Low volume -> low reference, high volume -> higher reference. _(default: 90)_
- **Smoothing:** Smooths the graph to prevent flickering. Low values will cause a hectic fast plot, high values a slow one without peaks. _(default: 3)_
- **Emphasize:** Emphasizes peaks. The higher the value, the bigger the difference between a "loud-bar" and a "quiet-bar". _(default: 0.5)_
**Equalizer**
Allows to finetune the graph by slective increasing/decresing the value.
You can add new pivots by rightclicking on the visualization-window.
Existing pivots can be deleted by rightclicking on them or moved by leftclicking and dragging around.
#### Beat detection
_No configuration right now_
#### Level
- **Calculation:** Defines how the RMS of the audio is plotted. Values are _Linear_, _Logarithmic_ and _Exponential_. The used range of the plott increases in that order (exponential has the widest range of peaks). _(default: Logarithmic)_
- **Scale:** Scales the whole graph. Use this to to fit your audio volume. _(default: 3)_
- **Smoothing:** Smooths the plot to prevent flickering. Low values will cause a hectic fast plot, high values a slow one without peaks. _(default: 8)_