diff --git a/KeyboardAudioVisualizer/App.xaml.cs b/KeyboardAudioVisualizer/App.xaml.cs index ee0e71f..d5aab26 100644 --- a/KeyboardAudioVisualizer/App.xaml.cs +++ b/KeyboardAudioVisualizer/App.xaml.cs @@ -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(PATH_SETTINGS); + //Settings settings = SerializationHelper.LoadObjectFromFile(PATH_SETTINGS); + Settings settings = null; + try { settings = JsonConvert.DeserializeObject(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 diff --git a/KeyboardAudioVisualizer/ApplicationManager.cs b/KeyboardAudioVisualizer/ApplicationManager.cs index 3cf44bf..7db2abb 100644 --- a/KeyboardAudioVisualizer/ApplicationManager.cs +++ b/KeyboardAudioVisualizer/ApplicationManager.cs @@ -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; 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 Visualizations { get; } = new ObservableDictionary(); + + private readonly Dictionary> _groups = new Dictionary>(); + #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(); } diff --git a/KeyboardAudioVisualizer/Attributes/DisplayNameAttribute.cs b/KeyboardAudioVisualizer/Attributes/DisplayNameAttribute.cs new file mode 100644 index 0000000..ce79b7e --- /dev/null +++ b/KeyboardAudioVisualizer/Attributes/DisplayNameAttribute.cs @@ -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 + } +} diff --git a/KeyboardAudioVisualizer/Attributes/VisualizerForAttribute.cs b/KeyboardAudioVisualizer/Attributes/VisualizerForAttribute.cs new file mode 100644 index 0000000..8ef8d40 --- /dev/null +++ b/KeyboardAudioVisualizer/Attributes/VisualizerForAttribute.cs @@ -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 + } +} diff --git a/KeyboardAudioVisualizer/AudioProcessing/AbstractAudioProcessor.cs b/KeyboardAudioVisualizer/AudioProcessing/AbstractAudioProcessor.cs new file mode 100644 index 0000000..296f5a0 --- /dev/null +++ b/KeyboardAudioVisualizer/AudioProcessing/AbstractAudioProcessor.cs @@ -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 + } +} diff --git a/KeyboardAudioVisualizer/AudioProcessing/AudioProcessor.cs b/KeyboardAudioVisualizer/AudioProcessing/AudioProcessor.cs deleted file mode 100644 index ff770d1..0000000 --- a/KeyboardAudioVisualizer/AudioProcessing/AudioProcessor.cs +++ /dev/null @@ -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 - } -} diff --git a/KeyboardAudioVisualizer/AudioProcessing/AudioVisualizationFactory.cs b/KeyboardAudioVisualizer/AudioProcessing/AudioVisualizationFactory.cs new file mode 100644 index 0000000..0e50147 --- /dev/null +++ b/KeyboardAudioVisualizer/AudioProcessing/AudioVisualizationFactory.cs @@ -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 _processors = new List(); + + #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)_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(visualizationType), GetAudioProcessor()) { Equalizer = equalizer }; + break; + + case VisualizationType.Level: + visualizationProvider = new LevelVisualizationProvider(ApplicationManager.Instance.Settings[visualizationIndex].GetConfiguration(visualizationType), _audioBuffer); + break; + + case VisualizationType.Beat: + visualizationProvider = new BeatVisualizationProvider(ApplicationManager.Instance.Settings[visualizationIndex].GetConfiguration(visualizationType), GetAudioProcessor()); + break; + } + + visualizationProvider?.Initialize(); + return visualizationProvider; + } + + public void Dispose() => _audioInput.Dispose(); + + #endregion + } +} diff --git a/KeyboardAudioVisualizer/AudioProcessing/IAudioProcessor.cs b/KeyboardAudioVisualizer/AudioProcessing/IAudioProcessor.cs index e91dccd..5a2c4af 100644 --- a/KeyboardAudioVisualizer/AudioProcessing/IAudioProcessor.cs +++ b/KeyboardAudioVisualizer/AudioProcessing/IAudioProcessor.cs @@ -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(); } diff --git a/KeyboardAudioVisualizer/AudioProcessing/Spectrum/FourierSpectrumProvider.cs b/KeyboardAudioVisualizer/AudioProcessing/Spectrum/FourierSpectrumProvider.cs index 83bb895..1ebeeb6 100644 --- a/KeyboardAudioVisualizer/AudioProcessing/Spectrum/FourierSpectrumProvider.cs +++ b/KeyboardAudioVisualizer/AudioProcessing/Spectrum/FourierSpectrumProvider.cs @@ -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); diff --git a/KeyboardAudioVisualizer/AudioProcessing/VisualizationPRovider/FrequencyBarsVisualizationProvider.cs b/KeyboardAudioVisualizer/AudioProcessing/VisualizationPRovider/FrequencyBarsVisualizationProvider.cs index d337352..e76f02e 100644 --- a/KeyboardAudioVisualizer/AudioProcessing/VisualizationPRovider/FrequencyBarsVisualizationProvider.cs +++ b/KeyboardAudioVisualizer/AudioProcessing/VisualizationPRovider/FrequencyBarsVisualizationProvider.cs @@ -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; diff --git a/KeyboardAudioVisualizer/AudioProcessing/VisualizationPRovider/IVisualizationProvider.cs b/KeyboardAudioVisualizer/AudioProcessing/VisualizationPRovider/IVisualizationProvider.cs index 1e5c0d8..de2177c 100644 --- a/KeyboardAudioVisualizer/AudioProcessing/VisualizationPRovider/IVisualizationProvider.cs +++ b/KeyboardAudioVisualizer/AudioProcessing/VisualizationPRovider/IVisualizationProvider.cs @@ -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(); } } diff --git a/KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/BeatVisualizationProvider.cs b/KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/BeatVisualizationProvider.cs index 1efec20..25175a7 100644 --- a/KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/BeatVisualizationProvider.cs +++ b/KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/BeatVisualizationProvider.cs @@ -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); diff --git a/KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/LevelVisualizationProvider.cs b/KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/LevelVisualizationProvider.cs index 6e2a8f6..e71535c 100644 --- a/KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/LevelVisualizationProvider.cs +++ b/KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/LevelVisualizationProvider.cs @@ -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); diff --git a/KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/VisualizationType.cs b/KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/VisualizationType.cs new file mode 100644 index 0000000..1a8a7b1 --- /dev/null +++ b/KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/VisualizationType.cs @@ -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, + } +} diff --git a/KeyboardAudioVisualizer/Configuration/AbstractConfiguration.cs b/KeyboardAudioVisualizer/Configuration/AbstractConfiguration.cs index e918473..10ecc23 100644 --- a/KeyboardAudioVisualizer/Configuration/AbstractConfiguration.cs +++ b/KeyboardAudioVisualizer/Configuration/AbstractConfiguration.cs @@ -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(ref T storage, T value, [CallerMemberName] string propertyName = null) diff --git a/KeyboardAudioVisualizer/Configuration/IConfiguration.cs b/KeyboardAudioVisualizer/Configuration/IConfiguration.cs index ac4755f..1d62075 100644 --- a/KeyboardAudioVisualizer/Configuration/IConfiguration.cs +++ b/KeyboardAudioVisualizer/Configuration/IConfiguration.cs @@ -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; } + } } diff --git a/KeyboardAudioVisualizer/Configuration/Settings.cs b/KeyboardAudioVisualizer/Configuration/Settings.cs index 4881aee..a60e45a 100644 --- a/KeyboardAudioVisualizer/Configuration/Settings.cs +++ b/KeyboardAudioVisualizer/Configuration/Settings.cs @@ -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 Visualizations { get; set; } = new Dictionary(); - #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(VisualizationType visualizationType) + where T : IConfiguration, new() => (T)this[visualizationType]; #endregion } diff --git a/KeyboardAudioVisualizer/Converter/VisualizationProviderDisplayNameConverter.cs b/KeyboardAudioVisualizer/Converter/VisualizationProviderDisplayNameConverter.cs new file mode 100644 index 0000000..d312662 --- /dev/null +++ b/KeyboardAudioVisualizer/Converter/VisualizationProviderDisplayNameConverter.cs @@ -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()?.DisplayName ?? visualizationType.ToString(); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException(); + + #endregion + } +} diff --git a/KeyboardAudioVisualizer/Converter/VisualizationToLastChildFillConverter.cs b/KeyboardAudioVisualizer/Converter/VisualizationToLastChildFillConverter.cs new file mode 100644 index 0000000..12181f1 --- /dev/null +++ b/KeyboardAudioVisualizer/Converter/VisualizationToLastChildFillConverter.cs @@ -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 + } +} diff --git a/KeyboardAudioVisualizer/Converter/VisualizationTypeSelectableConverter.cs b/KeyboardAudioVisualizer/Converter/VisualizationTypeSelectableConverter.cs new file mode 100644 index 0000000..e7db1fc --- /dev/null +++ b/KeyboardAudioVisualizer/Converter/VisualizationTypeSelectableConverter.cs @@ -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), typeof(IEnumerable))] + public class VisualizationTypeSelectableConverter : IValueConverter + { + #region Methods + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (!(value is IEnumerable visualizationProviders) || !(parameter is RGBDeviceType targetDevice)) return new List(); + return visualizationProviders.Where(x => x.GetAttribute()?.VisualizerFor.HasFlag(targetDevice) ?? true).ToList(); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException(); + + #endregion + } +} diff --git a/KeyboardAudioVisualizer/Decorators/BeatDecorator.cs b/KeyboardAudioVisualizer/Decorators/BeatDecorator.cs index b4fe90a..d90a2a8 100644 --- a/KeyboardAudioVisualizer/Decorators/BeatDecorator.cs +++ b/KeyboardAudioVisualizer/Decorators/BeatDecorator.cs @@ -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]; diff --git a/KeyboardAudioVisualizer/Decorators/FrequencyBarsDecorator.cs b/KeyboardAudioVisualizer/Decorators/FrequencyBarsDecorator.cs index d398089..ebb9d32 100644 --- a/KeyboardAudioVisualizer/Decorators/FrequencyBarsDecorator.cs +++ b/KeyboardAudioVisualizer/Decorators/FrequencyBarsDecorator.cs @@ -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); diff --git a/KeyboardAudioVisualizer/Decorators/LevelBarDecorator.cs b/KeyboardAudioVisualizer/Decorators/LevelBarDecorator.cs index ded9918..748eb83 100644 --- a/KeyboardAudioVisualizer/Decorators/LevelBarDecorator.cs +++ b/KeyboardAudioVisualizer/Decorators/LevelBarDecorator.cs @@ -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 diff --git a/KeyboardAudioVisualizer/Helper/EnumExtension.cs b/KeyboardAudioVisualizer/Helper/EnumExtension.cs new file mode 100644 index 0000000..4884e0f --- /dev/null +++ b/KeyboardAudioVisualizer/Helper/EnumExtension.cs @@ -0,0 +1,15 @@ +using System; +using System.Linq; + +namespace KeyboardAudioVisualizer.Helper +{ + public static class EnumExtension + { + #region Methods + + public static T GetAttribute(this Enum e) + where T : Attribute => e.GetType().GetMember(e.ToString()).FirstOrDefault()?.GetCustomAttributes(typeof(T), false).FirstOrDefault() as T; + + #endregion + } +} diff --git a/KeyboardAudioVisualizer/Helper/ExceptionExtension.cs b/KeyboardAudioVisualizer/Helper/ExceptionExtension.cs index 1931e57..c6b7962 100644 --- a/KeyboardAudioVisualizer/Helper/ExceptionExtension.cs +++ b/KeyboardAudioVisualizer/Helper/ExceptionExtension.cs @@ -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); diff --git a/KeyboardAudioVisualizer/Helper/ObservableDictionary.cs b/KeyboardAudioVisualizer/Helper/ObservableDictionary.cs new file mode 100644 index 0000000..48eba1b --- /dev/null +++ b/KeyboardAudioVisualizer/Helper/ObservableDictionary.cs @@ -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 : AbstractBindable, IDictionary, INotifyCollectionChanged + { + #region Constants + + private const string INDEXER_NAME = "Item[]"; + + #endregion + + #region Properties & Fields + + private readonly IList _values; + private readonly IDictionary _indexMap; + + private readonly SimpleMonitor _monitor = new SimpleMonitor(); + + #endregion + + #region Constructor + + public ObservableDictionary() + { + _values = new List(); + _indexMap = new Dictionary(); + } + + public ObservableDictionary(IDictionary dictionary) + { + _values = new List(); + _indexMap = new Dictionary(); + + int idx = 0; + foreach (KeyValuePair kvp in dictionary) + { + _indexMap.Add(kvp.Key, idx); + _values.Add(kvp.Value); + + idx++; + } + } + + public ObservableDictionary(int capacity) + { + _values = new List(capacity); + _indexMap = new Dictionary(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 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 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 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 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 Keys => _indexMap.Keys; + + public ICollection Values => _values; + + public TValue this[TKey key] + { + get + { + int index = _indexMap[key]; + return _values[index]; + } + set => ChangeItem(key, value); + } + + #endregion + + #region ICollection> Members + + public void Clear() => RemoveAllItems(); + + public int Count => _indexMap.Count; + + void ICollection>.Add(KeyValuePair item) => Add(item.Key, item.Value); + + bool ICollection>.Contains(KeyValuePair item) => _indexMap.ContainsKey(item.Key) && _values[_indexMap[item.Key]].Equals(item.Value); + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + foreach (KeyValuePair kvp in _indexMap) + { + array[arrayIndex] = new KeyValuePair(kvp.Key, _values[kvp.Value]); + arrayIndex++; + } + } + + bool ICollection>.IsReadOnly => false; + + bool ICollection>.Remove(KeyValuePair item) => RemoveItem(item); + + #endregion + + #region IEnumerable> Members + + public IEnumerator> GetEnumerator() + { + foreach (KeyValuePair kvp in _indexMap) + { + KeyValuePair pair = new KeyValuePair(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(key, value), index)); + + protected void OnCollectionChanged(NotifyCollectionChangedAction action, TKey key, TValue oldValue, TValue newValue, int index) + { + KeyValuePair newPair = new KeyValuePair(key, newValue); + KeyValuePair oldPair = new KeyValuePair(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; + } + } +} diff --git a/KeyboardAudioVisualizer/Helper/VisualizationIndex.cs b/KeyboardAudioVisualizer/Helper/VisualizationIndex.cs new file mode 100644 index 0000000..a5ec9fe --- /dev/null +++ b/KeyboardAudioVisualizer/Helper/VisualizationIndex.cs @@ -0,0 +1,9 @@ +namespace KeyboardAudioVisualizer.Helper +{ + public enum VisualizationIndex + { + Primary, + Secondary, + Tertiary + } +} diff --git a/KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj b/KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj index b58bec1..40be445 100644 --- a/KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj +++ b/KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj @@ -25,6 +25,7 @@ DEBUG;TRACE prompt 4 + latest x86 @@ -34,6 +35,7 @@ TRACE prompt 4 + latest Resources\Icon.ico @@ -48,6 +50,9 @@ ..\packages\MathNet.Numerics.3.20.0\lib\net40\MathNet.Numerics.dll + + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + ..\packages\RGB.NET.Brushes.1.0.0\lib\net45\RGB.NET.Brushes.dll True @@ -108,10 +113,13 @@ Code + + - + + @@ -129,6 +137,10 @@ + + + + @@ -143,12 +155,16 @@ + + + + ConfigurationWindow.xaml @@ -160,7 +176,7 @@ - + Code diff --git a/KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj.DotSettings b/KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj.DotSettings new file mode 100644 index 0000000..58ad6c8 --- /dev/null +++ b/KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file diff --git a/KeyboardAudioVisualizer/Helper/SerializationHelper.cs b/KeyboardAudioVisualizer/Legacy/SerializationHelper.cs similarity index 97% rename from KeyboardAudioVisualizer/Helper/SerializationHelper.cs rename to KeyboardAudioVisualizer/Legacy/SerializationHelper.cs index 4b64b0c..5515ff6 100644 --- a/KeyboardAudioVisualizer/Helper/SerializationHelper.cs +++ b/KeyboardAudioVisualizer/Legacy/SerializationHelper.cs @@ -2,7 +2,7 @@ using System.Xml; using System.Xml.Serialization; -namespace KeyboardAudioVisualizer.Helper +namespace KeyboardAudioVisualizer.Legacy { public static class SerializationHelper { diff --git a/KeyboardAudioVisualizer/Legacy/Settings.cs b/KeyboardAudioVisualizer/Legacy/Settings.cs new file mode 100644 index 0000000..c08f758 --- /dev/null +++ b/KeyboardAudioVisualizer/Legacy/Settings.cs @@ -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 + } +} diff --git a/KeyboardAudioVisualizer/UI/ConfigurationViewModel.cs b/KeyboardAudioVisualizer/UI/ConfigurationViewModel.cs index 3a6c812..e36fc6d 100644 --- a/KeyboardAudioVisualizer/UI/ConfigurationViewModel.cs +++ b/KeyboardAudioVisualizer/UI/ConfigurationViewModel.cs @@ -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 diff --git a/KeyboardAudioVisualizer/UI/ConfigurationWindow.xaml b/KeyboardAudioVisualizer/UI/ConfigurationWindow.xaml index c469e9c..a71a464 100644 --- a/KeyboardAudioVisualizer/UI/ConfigurationWindow.xaml +++ b/KeyboardAudioVisualizer/UI/ConfigurationWindow.xaml @@ -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 @@ + + + + + + + + + + + + + + @@ -34,7 +53,7 @@ - + @@ -44,26 +63,26 @@ - + - - + + - + @@ -73,26 +92,26 @@ - + - + - + @@ -102,20 +121,20 @@ - + - + @@ -129,7 +148,7 @@ - + diff --git a/KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualizer.cs b/KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualizer.cs index 62dd797..2e1abf2 100644 --- a/KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualizer.cs +++ b/KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualizer.cs @@ -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++) diff --git a/KeyboardAudioVisualizer/packages.config b/KeyboardAudioVisualizer/packages.config index b05adf4..07424e5 100644 --- a/KeyboardAudioVisualizer/packages.config +++ b/KeyboardAudioVisualizer/packages.config @@ -3,6 +3,7 @@ +