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)

This commit is contained in:
Darth Affe 2017-09-25 21:27:50 +02:00
parent 44c0cc59fa
commit 9d4aa2728c
36 changed files with 962 additions and 167 deletions

View File

@ -1,10 +1,12 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Windows;
using Hardcodet.Wpf.TaskbarNotification;
using KeyboardAudioVisualizer.AudioProcessing;
using KeyboardAudioVisualizer.Configuration;
using KeyboardAudioVisualizer.Helper;
using Newtonsoft.Json;
namespace KeyboardAudioVisualizer
{
@ -12,7 +14,7 @@ namespace KeyboardAudioVisualizer
{
#region Constants
private const string PATH_SETTINGS = "Settings.xml";
private const string PATH_SETTINGS = "Settings.json";
#endregion
@ -22,10 +24,6 @@ namespace KeyboardAudioVisualizer
#endregion
#region Constructors
#endregion
#region Methods
protected override void OnStartup(StartupEventArgs e)
@ -37,7 +35,14 @@ namespace KeyboardAudioVisualizer
_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)); }
catch (Exception ex)
{
Console.WriteLine(ex.Message);
/* File doesn't exist or is corrupt - just create a new one. */
}
if (settings == null)
{
settings = new Settings();
@ -45,7 +50,7 @@ namespace KeyboardAudioVisualizer
}
ApplicationManager.Instance.Settings = settings;
AudioProcessor.Initialize();
AudioVisualizationFactory.Initialize();
ApplicationManager.Instance.InitializeDevices();
}
catch (Exception ex)
@ -62,7 +67,7 @@ namespace KeyboardAudioVisualizer
{
base.OnExit(e);
SerializationHelper.SaveObjectToFile(ApplicationManager.Instance.Settings, PATH_SETTINGS);
File.WriteAllText(PATH_SETTINGS, JsonConvert.SerializeObject(ApplicationManager.Instance.Settings));
}
#endregion

View File

@ -1,5 +1,9 @@
using System.Windows;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using KeyboardAudioVisualizer.AudioProcessing;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.Configuration;
using KeyboardAudioVisualizer.Decorators;
using KeyboardAudioVisualizer.Helper;
@ -13,11 +17,15 @@ using RGB.NET.Devices.Corsair.SpecialParts;
using RGB.NET.Devices.Logitech;
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();
@ -26,6 +34,10 @@ 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)>>();
#endregion
#region Commands
@ -50,7 +62,7 @@ namespace KeyboardAudioVisualizer
{
RGBSurface surface = RGBSurface.Instance;
surface.UpdateFrequency = 1 / MathHelper.Clamp(Settings.UpdateRate, 1, 40);
surface.UpdateFrequency = 1.0 / MathHelper.Clamp(Settings.UpdateRate, 1, 40);
surface.UpdateMode = UpdateMode.Continuous;
surface.LoadDevices(CorsairDeviceProvider.Instance);
@ -60,8 +72,10 @@ namespace KeyboardAudioVisualizer
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!
//TODO DarthAffe 03.08.2017: Changeable, Settings etc.
foreach (IRGBDevice device in surface.Devices)
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:
@ -77,44 +91,84 @@ namespace KeyboardAudioVisualizer
ILedGroup lightbarLeft = new ListLedGroup(lightbar.Left);
lightbarLeft.Brush = new LinearGradientBrush(keyboardLevelGradient);
lightbarLeft.Brush.AddDecorator(new LevelBarDecorator(AudioProcessor.Instance.TertiaryVisualizationProvider, LevelBarDirection.Left, 0));
tertiaryGroups.Add((lightbarLeft, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Left, 0)));
ILedGroup lightbarRight = new ListLedGroup(lightbar.Right);
lightbarRight.Brush = new LinearGradientBrush(keyboardLevelGradient);
lightbarRight.Brush.AddDecorator(new LevelBarDecorator(AudioProcessor.Instance.TertiaryVisualizationProvider, LevelBarDirection.Right, 1));
tertiaryGroups.Add((lightbarRight, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Right, 1)));
ILedGroup lightbarCenter = new ListLedGroup(lightbar.Center);
lightbarCenter.Brush = new SolidColorBrush(new Color(255, 255, 255));
lightbarCenter.Brush.AddDecorator(new BeatDecorator(AudioProcessor.Instance.SecondaryVisualizationProvider));
secondaryGroups.Add((lightbarCenter, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer)));
}
primary.Brush = new LinearGradientBrush(new RainbowGradient(300, -14));
primary.Brush.AddDecorator(new FrequencyBarsDecorator(AudioProcessor.Instance.PrimaryVisualizationProvider));
primaryGroups.Add((primary, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Horizontal)));
break;
case RGBDeviceType.Mousepad:
case RGBDeviceType.LedStripe:
IGradient mousepadLevelGradient = new LinearGradient(new GradientStop(0, new Color(0, 0, 255)), new GradientStop(1, new Color(255, 0, 0)));
ILedGroup left = new RectangleLedGroup(new Rectangle(device.Location.X, 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), mousepadLevelGradient);
left.Brush.AddDecorator(new LevelBarDecorator(AudioProcessor.Instance.TertiaryVisualizationProvider, LevelBarDirection.Top, 0));
tertiaryGroups.Add((left, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Top, 0)));
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), mousepadLevelGradient);
right.Brush.AddDecorator(new LevelBarDecorator(AudioProcessor.Instance.TertiaryVisualizationProvider, LevelBarDirection.Top, 1));
tertiaryGroups.Add((right, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Top, 1)));
break;
case RGBDeviceType.Mouse:
case RGBDeviceType.Headset:
ILedGroup deviceGroup = new ListLedGroup(device);
deviceGroup.Brush = new SolidColorBrush(new Color(255, 255, 255));
deviceGroup.Brush.AddDecorator(new BeatDecorator(AudioProcessor.Instance.SecondaryVisualizationProvider));
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();
}
//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)
{
if (visualizationType == VisualizationType.FrequencyBars)
return new FrequencyBarsDecorator(visualizationProvider);
if (visualizationType == VisualizationType.Level)
return new LevelBarDecorator(visualizationProvider, direction, dataIndex);
if (visualizationType == VisualizationType.Beat)
return new BeatDecorator(visualizationProvider);
return null;
}
private void OpenConfiguration()
@ -125,8 +179,8 @@ namespace KeyboardAudioVisualizer
private void Exit()
{
RGBSurface.Instance.Dispose();
AudioProcessor.Instance.Dispose();
RGBSurface.Instance?.Dispose();
AudioVisualizationFactory.Instance?.Dispose();
Application.Current.Shutdown();
}

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

@ -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,91 @@
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()
{
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 += (data, offset, count) => _audioBuffer.Put(data, offset, count);
_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,7 @@
using KeyboardAudioVisualizer.AudioProcessing.Equalizer;
using KeyboardAudioVisualizer.AudioProcessing.Spectrum;
using KeyboardAudioVisualizer.Configuration;
using KeyboardAudioVisualizer.Helper;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
{
@ -79,7 +79,7 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
#endregion
public class FrequencyBarsVisualizationProvider : IVisualizationProvider
public class FrequencyBarsVisualizationProvider : AbstractAudioProcessor, IVisualizationProvider
{
#region Properties & Fields
@ -93,6 +93,9 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
public IConfiguration Configuration => _configuration;
public float[] VisualizationData { get; private set; }
public string DisplayName => "Spectrometer";
public RGBDeviceType VisualizerFor => RGBDeviceType.Keyboard | RGBDeviceType.LedMatrix;
#endregion
#region Constructors
@ -109,7 +112,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 +126,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

@ -3,6 +3,7 @@ using System.Linq;
using KeyboardAudioVisualizer.AudioProcessing.Spectrum;
using KeyboardAudioVisualizer.Configuration;
using KeyboardAudioVisualizer.Helper;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
{
@ -16,7 +17,7 @@ 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
@ -61,6 +62,9 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
public IConfiguration Configuration => _configuration;
public float[] VisualizationData { get; } = new float[1];
public string DisplayName => "Beat";
public RGBDeviceType VisualizerFor => (RGBDeviceType)0xFF;
#endregion
#region Constructors
@ -75,7 +79,7 @@ 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++)
@ -100,7 +104,7 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
_beatAverage = new float[_beatAverageSamples];
}
public void Update()
public override void Update()
{
ISpectrum spectrum = _specturProvider.GetLogarithmicSpectrum(60, minFrequency: 60);

View File

@ -3,6 +3,7 @@ using System.Linq;
using KeyboardAudioVisualizer.AudioCapture;
using KeyboardAudioVisualizer.Configuration;
using KeyboardAudioVisualizer.Helper;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
{
@ -46,7 +47,7 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
#endregion
public class LevelVisualizationProvider : IVisualizationProvider
public class LevelVisualizationProvider : AbstractAudioProcessor, IVisualizationProvider
{
#region Properties & Fields
@ -62,6 +63,10 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
public IConfiguration Configuration => _configuration;
public float[] VisualizationData { get; } = new float[3];
public string DisplayName => "Level";
public RGBDeviceType VisualizerFor => RGBDeviceType.Keyboard | RGBDeviceType.LedMatrix | RGBDeviceType.LedStripe | RGBDeviceType.Mousepad;
#endregion
#region Constructors
@ -78,7 +83,7 @@ namespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider
#region Methods
public void Initialize()
public override void Initialize()
{
_sampleDataLeft = new float[_audioBuffer.Size];
_sampleDataRight = new float[_audioBuffer.Size];
@ -112,7 +117,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

@ -7,6 +7,17 @@ namespace KeyboardAudioVisualizer.Configuration
{
public class AbstractConfiguration : AbstractBindable, IConfiguration, INotifyPropertyChanged
{
#region Properties & Fields
private IBrush _brush;
public IBrush Brush
{
get => _brush;
set => SetProperty(ref _brush, value);
}
#endregion
#region Methods
protected override bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)

View File

@ -1,7 +1,10 @@
using System.ComponentModel;
using RGB.NET.Core;
namespace KeyboardAudioVisualizer.Configuration
{
// DarthAffe 05.08.2017: Marker interface
public interface IConfiguration : INotifyPropertyChanged { }
public interface IConfiguration : INotifyPropertyChanged
{
IBrush Brush { get; set; }
}
}

View File

@ -1,24 +1,95 @@
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using System;
using System.Collections.Generic;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.Helper;
namespace KeyboardAudioVisualizer.Configuration
{
public class Settings
{
#region General
#region Properties & Fields
public double UpdateRate { get; set; } = 40.0;
#endregion
public Dictionary<VisualizationIndex, VisualizationSettings> Visualizations { get; set; } = new Dictionary<VisualizationIndex, VisualizationSettings>();
#region AudioProcessing
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 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;
break;
case VisualizationIndex.Secondary:
SelectedVisualization = VisualizationType.Beat;
break;
case VisualizationIndex.Tertiary:
SelectedVisualization = VisualizationType.Level;
break;
}
}
#endregion
#region Methods
public T GetConfiguration<T>(VisualizationType visualizationType)
where T : IConfiguration, new() => (T)this[visualizationType];
#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

@ -3,7 +3,7 @@ using RGB.NET.Core;
namespace KeyboardAudioVisualizer.Decorators
{
public class BeatDecorator : AbstractDecorator, IBrushDecorator
public class BeatDecorator : AbstractUpdateAwareDecorator, IBrushDecorator
{
#region Properties & Fields
@ -22,6 +22,8 @@ namespace KeyboardAudioVisualizer.Decorators
#region Methods
protected override void Update(double deltaTime) => _visualizationProvider.Update();
public void ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, ref Color color)
{
color.APercent *= _visualizationProvider.VisualizationData[0];

View File

@ -6,7 +6,7 @@ using Rectangle = RGB.NET.Core.Rectangle;
namespace KeyboardAudioVisualizer.Decorators
{
public class FrequencyBarsDecorator : AbstractDecorator, IBrushDecorator
public class FrequencyBarsDecorator : AbstractUpdateAwareDecorator, IBrushDecorator
{
#region Properties & Fields
@ -25,9 +25,11 @@ namespace KeyboardAudioVisualizer.Decorators
#region Methods
protected override void Update(double deltaTime) => _visualizationProvider.Update();
public void ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, ref 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);

View File

@ -3,7 +3,7 @@ using RGB.NET.Core;
namespace KeyboardAudioVisualizer.Decorators
{
public class LevelBarDecorator : AbstractDecorator, IBrushDecorator
public class LevelBarDecorator : AbstractUpdateAwareDecorator, IBrushDecorator
{
#region Properties & Fields
@ -26,12 +26,32 @@ namespace KeyboardAudioVisualizer.Decorators
#region Methods
protected override void Update(double deltaTime) => _visualizationProvider.Update();
public void ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, ref Color color)
{
double offset = CalculateOffset(rectangle, renderTarget);
if (offset >= _visualizationProvider.VisualizationData[DataIndex])
color.A = 0;
if (Direction == LevelBarDirection.Horizontal)
{
if (offset < 0)
{
offset = (-offset * 2);
if (offset >= _visualizationProvider.VisualizationData[0])
color.A = 0;
}
else
{
offset *= 2;
if (offset >= _visualizationProvider.VisualizationData[1])
color.A = 0;
}
}
else
{
if (offset >= _visualizationProvider.VisualizationData[DataIndex])
color.A = 0;
}
}
private double CalculateOffset(Rectangle rectangle, BrushRenderTarget renderTarget)
@ -50,6 +70,9 @@ namespace KeyboardAudioVisualizer.Decorators
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;
}
@ -62,7 +85,10 @@ namespace KeyboardAudioVisualizer.Decorators
public enum LevelBarDirection
{
Left, Right, Top, Bottom
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,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,6 +35,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Resources\Icon.ico</ApplicationIcon>
@ -48,6 +50,9 @@
<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>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.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>
@ -108,10 +113,13 @@
<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" />
@ -129,6 +137,10 @@
<Compile Include="AudioProcessing\VisualizationProvider\IVisualizationProvider.cs" />
<Compile Include="AudioProcessing\VisualizationProvider\LevelVisualizationProvider.cs" />
<Compile Include="AudioProcessing\Spectrum\RawSpectrumProvider.cs" />
<Compile Include="AudioProcessing\VisualizationProvider\VisualizationType.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" />
@ -143,12 +155,16 @@
<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\VisualizationIndex.cs" />
<Compile Include="Helper\WPFHelper.cs" />
<Compile Include="Configuration\Settings.cs" />
<Compile Include="Controls\BlurredDecorationWindow.cs" />
<Compile Include="Legacy\Settings.cs" />
<Compile Include="Styles\CachedResourceDictionary.cs" />
<Compile Include="UI\ConfigurationWindow.xaml.cs">
<DependentUpon>ConfigurationWindow.xaml</DependentUpon>
@ -160,7 +176,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>

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

@ -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,7 @@
using System;
using System.Diagnostics;
using System.Reflection;
using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;
using KeyboardAudioVisualizer.Helper;
using RGB.NET.Core;
@ -24,6 +25,36 @@ namespace KeyboardAudioVisualizer.UI
}
}
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,7 +7,12 @@
xmlns:controls="clr-namespace:KeyboardAudioVisualizer.Controls"
xmlns:styles="clr-namespace:KeyboardAudioVisualizer.Styles"
xmlns:core="clr-namespace:RGB.NET.Core;assembly=RGB.NET.Core"
xmlns:keyboardAudioVisualizer="clr-namespace:KeyboardAudioVisualizer"
xmlns:audioProcessing="clr-namespace:KeyboardAudioVisualizer.AudioProcessing"
xmlns:converter="clr-namespace:KeyboardAudioVisualizer.Converter"
xmlns:visualizationProvider="clr-namespace:KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider"
xmlns:helper="clr-namespace:KeyboardAudioVisualizer.Helper"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="Keyboard Audio-Visualizer # Configuration"
Icon="pack://application:,,,/KeyboardAudioVisualizer;component/Resources/Icon.ico"
@ -25,6 +30,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,7 +53,7 @@
<TabControl Style="{StaticResource StyleTabControlNavigation}">
<TabItem Header="Keyboard">
<DockPanel>
<DockPanel LastChildFill="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Primary], Converter={StaticResource VisualizationToLastChildFillConverter}}">
<GroupBox DockPanel.Dock="Top">
<controls:Formular>
<controls:Formular.Resources>
@ -44,26 +63,26 @@
</controls:Formular.Resources>
<Label controls:Formular.IsLabel="True" Content="Visualization: " />
<ComboBox controls:Formular.Fill="True" SelectedIndex="0">
<ComboBox.Items>
<ComboBoxItem>Frequency Bars</ComboBoxItem>
</ComboBox.Items>
</ComboBox>
<ComboBox controls:Formular.Fill="True" SelectedIndex="0"
ItemTemplate="{StaticResource DataTemplateVisualizationSelection}"
ItemsSource="{Binding Source={StaticResource DataProviderVisualizationTypes},
Converter={StaticResource VisualizationProviderSelectableConverter}, ConverterParameter={x:Static core:RGBDeviceType.Keyboard}}"
SelectedItem="{Binding SelectedPrimaryVisualization}" />
</controls:Formular>
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static audioProcessing:AudioProcessor.Instance}, Path=PrimaryVisualizationProvider.Configuration}" />
<ContentControl Content="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Primary].Configuration}" />
</GroupBox>
<GroupBox Margin="0,8,0,0">
<ContentControl Content="{Binding Source={x:Static audioProcessing:AudioProcessor.Instance}, Path=PrimaryVisualizationProvider}" />
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Primary]}" />
</GroupBox>
</DockPanel>
</TabItem>
<TabItem Header="Mouse/Headset">
<DockPanel LastChildFill="False">
<DockPanel LastChildFill="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Secondary], Converter={StaticResource VisualizationToLastChildFillConverter}}">
<GroupBox DockPanel.Dock="Top">
<controls:Formular>
<controls:Formular.Resources>
@ -73,26 +92,26 @@
</controls:Formular.Resources>
<Label controls:Formular.IsLabel="True" Content="Visualization: " />
<ComboBox controls:Formular.Fill="True" SelectedIndex="0">
<ComboBox.Items>
<ComboBoxItem>Beat-Detection</ComboBoxItem>
</ComboBox.Items>
</ComboBox>
<ComboBox controls:Formular.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:Formular>
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static audioProcessing:AudioProcessor.Instance}, Path=SecondaryVisualizationProvider.Configuration}" />
<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 audioProcessing:AudioProcessor.Instance}, Path=SecondaryVisualizationProvider}" />
<ContentControl Content="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Secondary]}" />
</GroupBox>
</DockPanel>
</TabItem>
<TabItem Header="Lightbar/Mousepad">
<DockPanel LastChildFill="False">
<DockPanel LastChildFill="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Tertiary], Converter={StaticResource VisualizationToLastChildFillConverter}}">
<GroupBox DockPanel.Dock="Top">
<controls:Formular>
<controls:Formular.Resources>
@ -102,20 +121,20 @@
</controls:Formular.Resources>
<Label controls:Formular.IsLabel="True" Content="Visualization: " />
<ComboBox controls:Formular.Fill="True" SelectedIndex="0">
<ComboBox.Items>
<ComboBoxItem>Level</ComboBoxItem>
</ComboBox.Items>
</ComboBox>
<ComboBox controls:Formular.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:Formular>
</GroupBox>
<GroupBox Margin="0,8,0,0" DockPanel.Dock="Top">
<ContentControl Content="{Binding Source={x:Static audioProcessing:AudioProcessor.Instance}, Path=TertiaryVisualizationProvider.Configuration}" />
<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 audioProcessing:AudioProcessor.Instance}, Path=TertiaryVisualizationProvider}" />
<ContentControl Content="{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Tertiary]}" />
</GroupBox>
</DockPanel>
</TabItem>

View File

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

View File

@ -88,6 +88,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++)

View File

@ -3,6 +3,7 @@
<package id="CSCore" version="1.2.1.1" 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="Newtonsoft.Json" version="10.0.3" 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" />