[
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n**/Properties/launchSettings.json\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# TODO: Comment the next line if you want to checkin your web deploy settings\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Typescript v1 declaration files\ntypings/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n# CodeRush\n.cr/\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n"
  },
  {
    "path": "KeyboardAudioVisualizer/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.6.1\" />\n    </startup>\n  <runtime>\n    <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n      <dependentAssembly>\n        <assemblyIdentity name=\"System.ValueTuple\" publicKeyToken=\"cc7b13ffcd2ddd51\" culture=\"neutral\" />\n        <bindingRedirect oldVersion=\"0.0.0.0-4.0.3.0\" newVersion=\"4.0.3.0\" />\n      </dependentAssembly>\n    </assemblyBinding>\n  </runtime>\n</configuration>"
  },
  {
    "path": "KeyboardAudioVisualizer/App.xaml",
    "content": "﻿<Application x:Class=\"KeyboardAudioVisualizer.App\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:tb=\"http://www.hardcodet.net/taskbar\"\n             xmlns:keyboardAudioVisualizer=\"clr-namespace:KeyboardAudioVisualizer\"\n             xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n             ShutdownMode=\"OnExplicitShutdown\">\n    <Application.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Resources/KeyboardAudioVisualizer.xaml\" />\n            </ResourceDictionary.MergedDictionaries>\n\n            <tb:TaskbarIcon x:Key=\"TaskbarIcon\"\n                            IconSource=\"Resources/Icon.ico\"\n                            ToolTip=\"Keyboard Audio-Visualizer\"\n                            MenuActivation=\"RightClick\">\n                <tb:TaskbarIcon.ContextMenu>\n                    <ContextMenu>\n                        <MenuItem Header=\"Open Configuration\" Command=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=OpenConfigurationCommand}\" />\n                        <MenuItem Header=\"Exit\" Command=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=ExitCommand}\" />\n                    </ContextMenu>\n                </tb:TaskbarIcon.ContextMenu>\n            </tb:TaskbarIcon>\n        </ResourceDictionary>\n    </Application.Resources>\n</Application>\n"
  },
  {
    "path": "KeyboardAudioVisualizer/App.xaml.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Windows;\nusing System.Windows.Controls;\nusing Hardcodet.Wpf.TaskbarNotification;\nusing KeyboardAudioVisualizer.AudioProcessing;\nusing KeyboardAudioVisualizer.Configuration;\nusing KeyboardAudioVisualizer.Helper;\nusing KeyboardAudioVisualizer.Legacy;\nusing Newtonsoft.Json;\nusing RGB.NET.Brushes.Gradients;\nusing RGB.NET.Core;\nusing Settings = KeyboardAudioVisualizer.Configuration.Settings;\n\nnamespace KeyboardAudioVisualizer\n{\n    public partial class App : Application\n    {\n        #region Constants\n\n        private const string PATH_SETTINGS = \"Settings.json\";\n\n        #endregion\n\n        #region Properties & Fields\n\n        private TaskbarIcon _taskbarIcon;\n\n        #endregion\n\n        #region Methods\n\n        protected override void OnStartup(StartupEventArgs e)\n        {\n            base.OnStartup(e);\n\n            try\n            {\n                ToolTipService.ShowDurationProperty.OverrideMetadata(typeof(DependencyObject), new FrameworkPropertyMetadata(int.MaxValue));\n\n                _taskbarIcon = (TaskbarIcon)FindResource(\"TaskbarIcon\");\n                _taskbarIcon.DoubleClickCommand = ApplicationManager.Instance.OpenConfigurationCommand;\n\n                //Settings settings = SerializationHelper.LoadObjectFromFile<Settings>(PATH_SETTINGS);\n                Settings settings = null;\n                try { settings = JsonConvert.DeserializeObject<Settings>(File.ReadAllText(PATH_SETTINGS), new ColorSerializer()); }\n                catch (Exception ex)\n                {\n                    Console.WriteLine(ex.Message);\n                    /* File doesn't exist or is corrupt - just create a new one. */\n                }\n\n                if (settings == null)\n                    settings = ConfigurationMigrator.MigrateOldConfig();\n\n                if (settings == null)\n                {\n                    settings = new Settings\n                    {\n                        Version = Settings.CURRENT_VERSION,\n                        Background = new LinearGradient(new GradientStop(0.5, new Color(64, 0, 0, 0)))\n                    };\n                    _taskbarIcon.ShowBalloonTip(\"Keyboard Audio-Visualizer is starting in the tray!\", \"Click on the icon to open the configuration.\", BalloonIcon.Info);\n                }\n                else if (settings.Version != Settings.CURRENT_VERSION)\n                    ConfigurationUpdates.PerformOn(settings);\n\n                ApplicationManager.Instance.Settings = settings;\n\n                AudioVisualizationFactory.Initialize();\n                ApplicationManager.Instance.InitializeDevices();\n            }\n            catch (Exception ex)\n            {\n                File.WriteAllText(\"error.log\", $\"[{DateTime.Now:G}] Exception!\\r\\n\\r\\nMessage:\\r\\n{ex.GetFullMessage()}\\r\\n\\r\\nStackTrace:\\r\\n{ex.StackTrace}\\r\\n\\r\\n\");\n                MessageBox.Show(\"An error occured while starting the Keyboard Audio-Visualizer.\\r\\nPlease double check if SDK-support for your devices is enabled.\\r\\nMore information can be found in the error.log file in the application directory.\", \"Can't start Keyboard Audio-Visualizer.\");\n\n                try { ApplicationManager.Instance.ExitCommand.Execute(null); }\n                catch { Environment.Exit(0); }\n            }\n        }\n\n        protected override void OnExit(ExitEventArgs e)\n        {\n            base.OnExit(e);\n\n            File.WriteAllText(PATH_SETTINGS, JsonConvert.SerializeObject(ApplicationManager.Instance.Settings, new ColorSerializer()));\n            ConfigurationMigrator.CleanupOldConfigs();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/ApplicationManager.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Windows;\nusing KeyboardAudioVisualizer.AudioProcessing;\nusing KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing KeyboardAudioVisualizer.Configuration;\nusing KeyboardAudioVisualizer.Decorators;\nusing KeyboardAudioVisualizer.Helper;\nusing KeyboardAudioVisualizer.UI;\nusing RGB.NET.Brushes;\nusing RGB.NET.Brushes.Gradients;\nusing RGB.NET.Core;\nusing RGB.NET.Devices.CoolerMaster;\nusing RGB.NET.Devices.Corsair;\nusing RGB.NET.Devices.Logitech;\nusing RGB.NET.Devices.Novation;\nusing RGB.NET.Devices.Razer;\nusing RGB.NET.Devices.SteelSeries;\nusing RGB.NET.Groups;\nusing Point = RGB.NET.Core.Point;\nusing GetDecoratorFunc = System.Func<KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider.VisualizationType, KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider.IVisualizationProvider, RGB.NET.Core.IBrushDecorator>;\n\nnamespace KeyboardAudioVisualizer\n{\n    public class ApplicationManager\n    {\n        #region Constants\n        #endregion\n\n        #region Properties & Fields\n\n        public static ApplicationManager Instance { get; } = new ApplicationManager();\n\n        private ConfigurationWindow _configurationWindow;\n\n        public Settings Settings { get; set; }\n\n        public ObservableDictionary<VisualizationIndex, IVisualizationProvider> Visualizations { get; } = new ObservableDictionary<VisualizationIndex, IVisualizationProvider>();\n\n        private readonly Dictionary<VisualizationIndex, IEnumerable<(ILedGroup group, GetDecoratorFunc getDecoratorFunc)>> _groups = new Dictionary<VisualizationIndex, IEnumerable<(ILedGroup group, GetDecoratorFunc getDecoratorFunc)>>();\n\n        public TimerUpdateTrigger UpdateTrigger { get; } = new TimerUpdateTrigger();\n\n        #endregion\n\n        #region Commands\n\n        private ActionCommand _openConfiguration;\n        public ActionCommand OpenConfigurationCommand => _openConfiguration ?? (_openConfiguration = new ActionCommand(OpenConfiguration));\n\n        private ActionCommand _exitCommand;\n        public ActionCommand ExitCommand => _exitCommand ?? (_exitCommand = new ActionCommand(Exit));\n\n        #endregion\n\n        #region Constructors\n\n        private ApplicationManager() { }\n\n        #endregion\n\n        #region Methods\n\n        public void InitializeDevices()\n        {\n            RGBSurface surface = RGBSurface.Instance;\n\n            UpdateTrigger.UpdateFrequency = 1.0 / MathHelper.Clamp(Settings.UpdateRate, 1, 60);\n            surface.RegisterUpdateTrigger(UpdateTrigger);\n\n            LoadDevices(surface, CorsairDeviceProvider.Instance);\n            LoadDevices(surface, CoolerMasterDeviceProvider.Instance);\n            LoadDevices(surface, NovationDeviceProvider.Instance);\n            LoadDevices(surface, RazerDeviceProvider.Instance);\n            LoadDevices(surface, LogitechDeviceProvider.Instance);\n            LoadDevices(surface, SteelSeriesDeviceProvider.Instance);\n\n            surface.AlignDevices();\n\n            ILedGroup background = new ListLedGroup(surface.Leds);\n            background.Brush = new LinearGradientBrush(Settings.Background);\n\n            LinearGradient primaryGradient = Settings[VisualizationIndex.Primary].Gradient;\n            LinearGradient secondaryGradient = Settings[VisualizationIndex.Secondary].Gradient;\n            LinearGradient tertiaryGradient = Settings[VisualizationIndex.Tertiary].Gradient;\n\n            List<(ILedGroup, GetDecoratorFunc)> primaryGroups = new List<(ILedGroup, GetDecoratorFunc)>();\n            List<(ILedGroup, GetDecoratorFunc)> secondaryGroups = new List<(ILedGroup, GetDecoratorFunc)>();\n            List<(ILedGroup, GetDecoratorFunc)> tertiaryGroups = new List<(ILedGroup, GetDecoratorFunc)>();\n            foreach (IRGBDevice device in RGBSurface.Instance.Devices)\n                switch (device.DeviceInfo.DeviceType)\n                {\n                    case RGBDeviceType.Keyboard:\n                    case RGBDeviceType.Keypad:\n                    case RGBDeviceType.LedMatrix:\n                        ListLedGroup primary = new ListLedGroup(device);\n\n                        LightbarSpecialPart lightbar = device.GetSpecialDevicePart<LightbarSpecialPart>();\n                        if (lightbar != null)\n                        {\n                            primary.RemoveLeds(lightbar.Leds);\n\n                            ILedGroup lightbarLeft = new ListLedGroup(lightbar.Left);\n                            lightbarLeft.Brush = new LinearGradientBrush(new Point(1.0, 0.5), new Point(0.0, 0.5), tertiaryGradient);\n                            tertiaryGroups.Add((lightbarLeft, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Left, 0)));\n\n                            ILedGroup lightbarRight = new ListLedGroup(lightbar.Right);\n                            lightbarRight.Brush = new LinearGradientBrush(tertiaryGradient);\n                            tertiaryGroups.Add((lightbarRight, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Right, 1)));\n\n                            ILedGroup lightbarCenter = new ListLedGroup(lightbar.Center);\n                            lightbarCenter.Brush = new LinearGradientBrush(secondaryGradient);\n                            secondaryGroups.Add((lightbarCenter, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer)));\n                        }\n\n                        primary.Brush = new LinearGradientBrush(primaryGradient);\n                        primaryGroups.Add((primary, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Horizontal, 0, primaryGradient)));\n                        break;\n\n                    case RGBDeviceType.Mousepad:\n                    case RGBDeviceType.LedStripe:\n                    case RGBDeviceType.HeadsetStand:\n                        ILedGroup left = new RectangleLedGroup(new Rectangle(device.Location.X, device.Location.Y, device.Size.Width / 2.0, device.Size.Height));\n                        left.Brush = new LinearGradientBrush(new Point(0.5, 1), new Point(0.5, 0), tertiaryGradient);\n                        tertiaryGroups.Add((left, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Top, 0)));\n\n                        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));\n                        right.Brush = new LinearGradientBrush(new Point(0.5, 1), new Point(0.5, 0), tertiaryGradient);\n                        tertiaryGroups.Add((right, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Top, 1)));\n                        break;\n\n                    case RGBDeviceType.Mouse:\n                    case RGBDeviceType.Headset:\n                    case RGBDeviceType.Speaker:\n                    case RGBDeviceType.Fan:\n                    case RGBDeviceType.GraphicsCard:\n                    case RGBDeviceType.DRAM:\n                    case RGBDeviceType.Mainboard:\n                        ILedGroup deviceGroup = new ListLedGroup(device);\n                        deviceGroup.Brush = new LinearGradientBrush(secondaryGradient);\n                        secondaryGroups.Add((deviceGroup, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer)));\n                        break;\n                }\n\n            _groups[VisualizationIndex.Primary] = primaryGroups;\n            _groups[VisualizationIndex.Secondary] = secondaryGroups;\n            _groups[VisualizationIndex.Tertiary] = tertiaryGroups;\n\n            ApplyVisualization(VisualizationIndex.Primary, Settings[VisualizationIndex.Primary].SelectedVisualization);\n            ApplyVisualization(VisualizationIndex.Secondary, Settings[VisualizationIndex.Secondary].SelectedVisualization);\n            ApplyVisualization(VisualizationIndex.Tertiary, Settings[VisualizationIndex.Tertiary].SelectedVisualization);\n\n            surface.Updating += args => AudioVisualizationFactory.Instance.Update();\n        }\n\n        private void LoadDevices(RGBSurface surface, IRGBDeviceProvider deviceProvider)\n        {\n            surface.LoadDevices(deviceProvider, RGBDeviceType.Keyboard | RGBDeviceType.LedMatrix\n                                              | RGBDeviceType.Mousepad | RGBDeviceType.LedStripe\n                                              | RGBDeviceType.Mouse | RGBDeviceType.Headset\n                                              | RGBDeviceType.HeadsetStand);\n        }\n\n        //TODO DarthAffe 12.09.2017: This is just a big mess - is this worth to rework before arge?\n        public void ApplyVisualization(VisualizationIndex visualizationIndex, VisualizationType visualizationType)\n        {\n            IVisualizationProvider visualizer = AudioVisualizationFactory.Instance.CreateVisualizationProvider(visualizationIndex, visualizationType);\n            Visualizations[visualizationIndex] = visualizer;\n\n            foreach ((ILedGroup group, GetDecoratorFunc getDecoratorFunc) in _groups[visualizationIndex])\n            {\n                group.Brush.RemoveAllDecorators();\n\n                if (visualizer != null)\n                {\n                    IBrushDecorator decorator = getDecoratorFunc(visualizationType, visualizer);\n                    if (decorator != null)\n                        group.Brush.AddDecorator(decorator);\n                }\n            }\n        }\n\n        private IBrushDecorator CreateDecorator(VisualizationType visualizationType, IVisualizationProvider visualizationProvider, LevelBarDirection direction = LevelBarDirection.Top, int dataIndex = 0, LinearGradient gradient = null)\n        {\n            if (visualizationType == VisualizationType.FrequencyBars)\n                return new FrequencyBarsDecorator(visualizationProvider);\n\n            if (visualizationType == VisualizationType.Level)\n                return new LevelBarDecorator(visualizationProvider, direction, dataIndex, gradient);\n\n            if (visualizationType == VisualizationType.Beat)\n                return new BeatDecorator(visualizationProvider);\n\n            return null;\n        }\n\n        private void OpenConfiguration()\n        {\n            if (_configurationWindow == null) _configurationWindow = new ConfigurationWindow();\n            _configurationWindow.Show();\n        }\n\n        private void Exit()\n        {\n            try { AudioVisualizationFactory.Instance?.Dispose(); } catch { }\n            try { RGBSurface.Instance?.Dispose(); } catch { }\n            Application.Current.Shutdown();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Attached/SliderValue.cs",
    "content": "﻿using System.Linq;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\n\nnamespace KeyboardAudioVisualizer.Attached\n{\n    public static class SliderValue\n    {\n        #region Properties & Fields\n        // ReSharper disable InconsistentNaming\n\n        public static readonly DependencyProperty UnitProperty = DependencyProperty.RegisterAttached(\n            \"Unit\", typeof(string), typeof(SliderValue), new PropertyMetadata(default(string)));\n\n        public static void SetUnit(DependencyObject element, string value) => element.SetValue(UnitProperty, value);\n        public static string GetUnit(DependencyObject element) => (string)element.GetValue(UnitProperty);\n\n        public static readonly DependencyProperty IsShownProperty = DependencyProperty.RegisterAttached(\n            \"IsShown\", typeof(bool), typeof(SliderValue), new PropertyMetadata(default(bool), IsShownChanged));\n\n        public static void SetIsShown(DependencyObject element, bool value) => element.SetValue(IsShownProperty, value);\n        public static bool GetIsShown(DependencyObject element) => (bool)element.GetValue(IsShownProperty);\n\n        public static readonly DependencyProperty BorderBrushProperty = DependencyProperty.RegisterAttached(\n            \"BorderBrush\", typeof(Brush), typeof(SliderValue), new PropertyMetadata(default(Brush)));\n\n        public static void SetBorderBrush(DependencyObject element, Brush value) => element.SetValue(BorderBrushProperty, value);\n        public static Brush GetBorderBrush(DependencyObject element) => (Brush)element.GetValue(BorderBrushProperty);\n\n        public static readonly DependencyProperty BackgroundProperty = DependencyProperty.RegisterAttached(\n            \"Background\", typeof(Brush), typeof(SliderValue), new PropertyMetadata(default(Brush)));\n\n        public static void SetBackground(DependencyObject element, Brush value) => element.SetValue(BackgroundProperty, value);\n        public static Brush GetBackground(DependencyObject element) => (Brush)element.GetValue(BackgroundProperty);\n\n        public static readonly DependencyProperty ForegroundProperty = DependencyProperty.RegisterAttached(\n            \"Foreground\", typeof(Brush), typeof(SliderValue), new PropertyMetadata(default(Brush)));\n\n        public static void SetForeground(DependencyObject element, Brush value) => element.SetValue(ForegroundProperty, value);\n        public static Brush GetForeground(DependencyObject element) => (Brush)element.GetValue(ForegroundProperty);\n\n        public static readonly DependencyProperty FontProperty = DependencyProperty.RegisterAttached(\n            \"Font\", typeof(FontFamily), typeof(SliderValue), new PropertyMetadata(default(FontFamily)));\n\n        public static void SetFont(DependencyObject element, FontFamily value) => element.SetValue(FontProperty, value);\n        public static FontFamily GetFont(DependencyObject element) => (FontFamily)element.GetValue(FontProperty);\n\n        public static readonly DependencyProperty FontSizeProperty = DependencyProperty.RegisterAttached(\n            \"FontSize\", typeof(double), typeof(SliderValue), new PropertyMetadata(default(double)));\n\n        public static void SetFontSize(DependencyObject element, double value) => element.SetValue(FontSizeProperty, value);\n        public static double GetFontSize(DependencyObject element) => (double)element.GetValue(FontSizeProperty);\n\n        // ReSharper enable InconsistentNaming\n        #endregion\n\n        #region Methods\n\n        private static void IsShownChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)\n        {\n            if (!(dependencyObject is Slider slider)) return;\n\n            if (dependencyPropertyChangedEventArgs.NewValue as bool? == true)\n            {\n                slider.MouseEnter += SliderOnMouseEnter;\n                slider.MouseLeave += SliderOnMouseLeave;\n            }\n            else\n            {\n                slider.MouseEnter -= SliderOnMouseEnter;\n                slider.MouseLeave -= SliderOnMouseLeave;\n                RemoveAdorner(slider);\n            }\n        }\n\n        private static void SliderOnMouseEnter(object sender, MouseEventArgs mouseEventArgs)\n        {\n            if (!(sender is Slider slider)) return;\n            AdornerLayer.GetAdornerLayer(slider)?.Add(new SliderValueAdorner(slider, GetUnit(slider))\n            {\n                BorderBrush = GetBorderBrush(slider),\n                Background = GetBackground(slider),\n                Foreground = GetForeground(slider),\n                Font = GetFont(slider),\n                FontSize = GetFontSize(slider)\n            });\n        }\n\n        private static void SliderOnMouseLeave(object sender, MouseEventArgs mouseEventArgs)\n        {\n            if (!(sender is Slider slider)) return;\n            RemoveAdorner(slider);\n        }\n\n        private static void RemoveAdorner(Slider slider)\n        {\n            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(slider);\n            Adorner adorner = adornerLayer?.GetAdorners(slider)?.FirstOrDefault(x => x is SliderValueAdorner);\n            if (adorner != null)\n            {\n                adornerLayer.Remove(adorner);\n                (adorner as SliderValueAdorner)?.Cleanup();\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Attached/SliderValueAdorner.cs",
    "content": "﻿using System.Globalization;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Media;\nusing Point = System.Windows.Point;\n\nnamespace KeyboardAudioVisualizer.Attached\n{\n    public class SliderValueAdorner : System.Windows.Documents.Adorner\n    {\n        #region Properties & Fields\n\n        private readonly string _unit;\n        private readonly Slider _slider;\n        private readonly Thumb _thumb;\n        private readonly RepeatButton _decreaseRepeatButton;\n\n        public Brush BorderBrush { get; set; } = Brushes.Black;\n        public Brush Background { get; set; } = Brushes.Black;\n        public Brush Foreground { get; set; } = Brushes.White;\n        public FontFamily Font { get; set; } = new FontFamily(\"Verdana\");\n        public double FontSize { get; set; } = 14;\n\n        #endregion\n\n        #region Constructors\n\n        public SliderValueAdorner(UIElement adornedElement, string unit)\n            : base(adornedElement)\n        {\n            this._unit = unit;\n\n            _slider = (Slider)adornedElement;\n            Track track = (Track)_slider.Template.FindName(\"PART_Track\", _slider);\n\n            _thumb = track.Thumb;\n            _decreaseRepeatButton = track.DecreaseRepeatButton;\n            _decreaseRepeatButton.SizeChanged += OnButtonSizeChanged;\n        }\n\n        #endregion\n\n        #region Methods\n\n        public void Cleanup()\n        {\n            _decreaseRepeatButton.SizeChanged -= OnButtonSizeChanged;\n        }\n\n        private void OnButtonSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs) => InvalidateVisual();\n\n        protected override void OnRender(DrawingContext drawingContext)\n        {\n            double offset = _decreaseRepeatButton.ActualWidth + (_thumb.ActualWidth / 2.0);\n\n            FormattedText text = new FormattedText(GetText(), CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface(Font, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal), FontSize, Foreground);\n            Geometry border = CreateBorder(offset, text.Width, text.Height);\n\n            drawingContext.DrawGeometry(Background, new Pen(BorderBrush, 1), border);\n            drawingContext.DrawText(text, new Point(offset - (text.Width / 2.0), -26));\n        }\n\n        private string GetText()\n        {\n            string valueText = _slider.Value.ToString();\n            if (!string.IsNullOrWhiteSpace(_unit))\n                valueText += \" \" + _unit;\n\n            return valueText;\n        }\n\n        private Geometry CreateBorder(double offset, double width, double height)\n        {\n            double halfWidth = width / 2.0;\n\n            PathGeometry borderGeometry = new PathGeometry();\n            PathFigure border = new PathFigure\n            {\n                StartPoint = new Point(offset, 0),\n                IsClosed = true,\n                IsFilled = true\n            };\n\n            border.Segments.Add(new LineSegment(new Point(offset + 4, -6), true));\n            border.Segments.Add(new LineSegment(new Point(offset + 4 + halfWidth, -6), true));\n            border.Segments.Add(new LineSegment(new Point(offset + 4 + halfWidth, -10 - height), true));\n            border.Segments.Add(new LineSegment(new Point(offset - 4 - halfWidth, -10 - height), true));\n            border.Segments.Add(new LineSegment(new Point(offset - 4 - halfWidth, -6), true));\n            border.Segments.Add(new LineSegment(new Point(offset - 4, -6), true));\n\n            borderGeometry.Figures.Add(border);\n\n            return borderGeometry;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Attributes/DisplayNameAttribute.cs",
    "content": "﻿using System;\n\nnamespace KeyboardAudioVisualizer.Attributes\n{\n    public class DisplayNameAttribute : Attribute\n    {\n        #region Properties & Fields\n\n        public string DisplayName { get; set; }\n\n        #endregion\n\n        #region Constructors\n\n        public DisplayNameAttribute(string displayName)\n        {\n            this.DisplayName = displayName;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Attributes/VisualizerForAttribute.cs",
    "content": "﻿using System;\nusing RGB.NET.Core;\n\nnamespace KeyboardAudioVisualizer.Attributes\n{\n    public class VisualizerForAttribute : Attribute\n    {\n        #region Properties & Fields\n\n        public RGBDeviceType VisualizerFor { get; set; }\n\n        #endregion\n\n        #region Constructors\n\n        public VisualizerForAttribute(RGBDeviceType visualizerFor)\n        {\n            this.VisualizerFor = visualizerFor;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioCapture/AudioBuffer.cs",
    "content": "﻿using System;\n\nnamespace KeyboardAudioVisualizer.AudioCapture\n{\n    public class AudioBuffer\n    {\n        #region Properties & Fields\n\n        private readonly int _capacity;\n        private readonly float[] _bufferLeft;\n        private readonly float[] _bufferRight;\n        private int _currentIndex;\n\n        public int Size => _capacity;\n\n        public float? Prescale { get; set; } = null;\n\n        #endregion\n\n        #region Constructors\n\n        public AudioBuffer(int capacity)\n        {\n            this._capacity = capacity;\n\n            _bufferLeft = new float[capacity];\n            _bufferRight = new float[capacity];\n        }\n\n        #endregion\n\n        #region Methods\n\n        public void Put(float left, float right)\n        {\n            _currentIndex++;\n            if (_currentIndex >= _capacity) _currentIndex = 0;\n\n            _bufferLeft[_currentIndex] = left;\n            _bufferRight[_currentIndex] = right;\n        }\n\n        public void Put(float[] src, int offset, int count)\n        {\n            if ((count & 1) != 0) return; // we expect stereo-data to be an even amount of values\n\n            if (count > _capacity)\n            {\n                offset += count - _capacity;\n                count = _capacity;\n            }\n\n            for (int i = 0; i < count; i += 2)\n            {\n                _currentIndex++;\n                if (_currentIndex >= _capacity) _currentIndex = 0;\n\n                if (Prescale.HasValue)\n                {\n                    _bufferLeft[_currentIndex] = src[offset + i] / Prescale.Value;\n                    _bufferRight[_currentIndex] = src[offset + i + 1] / Prescale.Value;\n                }\n                else\n                {\n                    _bufferLeft[_currentIndex] = src[offset + i];\n                    _bufferRight[_currentIndex] = src[offset + i + 1];\n                }\n            }\n        }\n\n        public void CopyLeftInto(ref float[] data, int offset) => CopyLeftInto(ref data, offset, Math.Min(data.Length, _capacity));\n        public void CopyLeftInto(ref float[] data, int offset, int count)\n        {\n            int bufferOffset = _capacity - count;\n            for (int i = 0; i < count; i++)\n                data[offset + i] = _bufferLeft[(_currentIndex + (bufferOffset + i)) % _capacity];\n        }\n\n        public void CopyRightInto(ref float[] data, int offset) => CopyRightInto(ref data, offset, Math.Min(data.Length, _capacity));\n        public void CopyRightInto(ref float[] data, int offset, int count)\n        {\n            int bufferOffset = _capacity - count;\n            for (int i = 0; i < count; i++)\n                data[offset + i] = _bufferRight[(_currentIndex + (bufferOffset + i)) % _capacity];\n        }\n\n        public void CopyMixInto(ref float[] data, int offset) => CopyMixInto(ref data, offset, Math.Min(data.Length, _capacity));\n        public void CopyMixInto(ref float[] data, int offset, int count)\n        {\n            int bufferOffset = _capacity - count;\n            for (int i = 0; i < count; i++)\n            {\n                int index = (_currentIndex + (bufferOffset + i)) % _capacity;\n                data[offset + i] = (_bufferLeft[index] + _bufferRight[index]) / 2f;\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioCapture/CSCoreAudioInput.cs",
    "content": "﻿using System;\nusing CSCore;\nusing CSCore.CoreAudioAPI;\nusing CSCore.SoundIn;\nusing CSCore.Streams;\n\nnamespace KeyboardAudioVisualizer.AudioCapture\n{\n    public class CSCoreAudioInput : IAudioInput\n    {\n        #region Properties & Fields\n\n        private WasapiCapture _capture;\n        private SoundInSource _soundInSource;\n        private IWaveSource _source;\n        private SingleBlockNotificationStream _stream;\n        private AudioEndpointVolume _audioEndpointVolume;\n\n        public int SampleRate => _soundInSource?.WaveFormat?.SampleRate ?? -1;\n        public float MasterVolume => _audioEndpointVolume.MasterVolumeLevelScalar;\n\n        #endregion\n\n        #region Event\n\n        public event AudioData DataAvailable;\n\n        #endregion\n\n        #region Methods\n\n        public void Initialize()\n        {\n            MMDevice captureDevice = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console);\n            WaveFormat deviceFormat = captureDevice.DeviceFormat;\n            _audioEndpointVolume = AudioEndpointVolume.FromDevice(captureDevice);\n\n            //DarthAffe 07.02.2018: This is a really stupid workaround to (hopefully) finally fix the surround driver issues\n            for (int i = 1; i < 13; i++)\n                try { _capture = new WasapiLoopbackCapture(100, new WaveFormat(deviceFormat.SampleRate, deviceFormat.BitsPerSample, i)); } catch { /* We're just trying ... */ }\n\n            if (_capture == null)\n                throw new NullReferenceException(\"Failed to initialize WasapiLoopbackCapture\");\n\n            _capture.Initialize();\n\n            _soundInSource = new SoundInSource(_capture) { FillWithZeros = false };\n            _source = _soundInSource.WaveFormat.SampleRate == 44100\n                          ? _soundInSource.ToStereo()\n                          : _soundInSource.ChangeSampleRate(44100).ToStereo();\n\n            _stream = new SingleBlockNotificationStream(_source.ToSampleSource());\n            _stream.SingleBlockRead += StreamOnSingleBlockRead;\n\n            _source = _stream.ToWaveSource();\n\n            byte[] buffer = new byte[_source.WaveFormat.BytesPerSecond / 2];\n            _soundInSource.DataAvailable += (s, aEvent) =>\n                                            {\n                                                while ((_source.Read(buffer, 0, buffer.Length)) > 0) ;\n                                            };\n\n            _capture.Start();\n        }\n\n        public void Dispose()\n        {\n            _capture?.Stop();\n            _capture?.Dispose();\n        }\n\n        private void StreamOnSingleBlockRead(object sender, SingleBlockReadEventArgs singleBlockReadEventArgs)\n            => DataAvailable?.Invoke(singleBlockReadEventArgs.Left, singleBlockReadEventArgs.Right);\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioCapture/IAudioInput.cs",
    "content": "﻿using System;\n\nnamespace KeyboardAudioVisualizer.AudioCapture\n{\n    public delegate void AudioData(float left, float right);\n\n    public interface IAudioInput : IDisposable\n    {\n        int SampleRate { get; }\n        float MasterVolume { get; }\n\n        event AudioData DataAvailable;\n\n        void Initialize();\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/AbstractAudioProcessor.cs",
    "content": "﻿namespace KeyboardAudioVisualizer.AudioProcessing\n{\n    public abstract class AbstractAudioProcessor : IAudioProcessor\n    {\n        #region Properties & Fields\n\n        public bool IsActive { get; set; } = true;\n\n        #endregion\n\n        #region Methods\n\n        public abstract void Initialize();\n\n        public abstract void Update();\n\n        public virtual void Dispose() { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/AudioVisualizationFactory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing KeyboardAudioVisualizer.AudioCapture;\nusing KeyboardAudioVisualizer.AudioProcessing.Equalizer;\nusing KeyboardAudioVisualizer.AudioProcessing.Spectrum;\nusing KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing KeyboardAudioVisualizer.Helper;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing\n{\n    public class AudioVisualizationFactory : IDisposable\n    {\n        #region Properties & Fields\n\n        public static AudioVisualizationFactory Instance { get; private set; }\n\n        private IAudioInput _audioInput;\n        private AudioBuffer _audioBuffer;\n        private readonly List<IAudioProcessor> _processors = new List<IAudioProcessor>();\n\n        #endregion\n\n        #region Constructors\n\n        private AudioVisualizationFactory() { }\n\n        #endregion\n\n        #region Methods\n\n        public void Update()\n        {\n            if (ApplicationManager.Instance.Settings.EnableAudioPrescale)\n                _audioBuffer.Prescale = _audioInput.MasterVolume;\n            else\n                _audioBuffer.Prescale = null;\n\n            foreach (IAudioProcessor processor in _processors.Where(x => x.IsActive))\n                processor.Update();\n        }\n\n        public static void Initialize()\n        {\n            if (Instance != null) return;\n\n            Instance = new AudioVisualizationFactory();\n            Instance.InitializeInstance();\n        }\n\n        private void InitializeInstance()\n        {\n            _audioInput = new CSCoreAudioInput();\n            _audioInput.Initialize();\n\n            _audioBuffer = new AudioBuffer(4096); // Working with ~93ms - \n            _audioInput.DataAvailable += (left, right) => _audioBuffer.Put(left, right);\n\n            _processors.Add(new FourierSpectrumProvider(_audioBuffer));\n\n            foreach (IAudioProcessor processor in _processors)\n                processor.Initialize();\n        }\n\n        private T GetAudioProcessor<T>() => (T)_processors.FirstOrDefault(x => x.GetType() == typeof(T));\n\n        public IVisualizationProvider CreateVisualizationProvider(VisualizationIndex visualizationIndex, VisualizationType visualizationType)\n        {\n            IVisualizationProvider visualizationProvider = default;\n            switch (visualizationType)\n            {\n                case VisualizationType.FrequencyBars:\n                    MultiBandEqualizer equalizer = new MultiBandEqualizer();\n                    ApplicationManager.Instance.Settings[visualizationIndex].EqualizerConfiguration.LoadInto(equalizer);\n                    equalizer.PropertyChanged += (sender, args) => ApplicationManager.Instance.Settings[visualizationIndex].EqualizerConfiguration.SaveFrom(equalizer);\n                    visualizationProvider = new FrequencyBarsVisualizationProvider(ApplicationManager.Instance.Settings[visualizationIndex].GetConfiguration<FrequencyBarsVisualizationProviderConfiguration>(visualizationType), GetAudioProcessor<FourierSpectrumProvider>()) { Equalizer = equalizer };\n                    break;\n\n                case VisualizationType.Level:\n                    visualizationProvider = new LevelVisualizationProvider(ApplicationManager.Instance.Settings[visualizationIndex].GetConfiguration<LevelVisualizationProviderConfiguration>(visualizationType), _audioBuffer);\n                    break;\n\n                case VisualizationType.Beat:\n                    visualizationProvider = new BeatVisualizationProvider(ApplicationManager.Instance.Settings[visualizationIndex].GetConfiguration<BeatVisualizationProviderConfiguration>(visualizationType), GetAudioProcessor<FourierSpectrumProvider>());\n                    break;\n            }\n\n            visualizationProvider?.Initialize();\n            return visualizationProvider;\n        }\n\n        public void Dispose() => _audioInput.Dispose();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/Equalizer/EqualizerBand.cs",
    "content": "﻿using KeyboardAudioVisualizer.Helper;\nusing RGB.NET.Core;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.Equalizer\n{\n    public class EqualizerBand : AbstractBindable\n    {\n        #region Properties & Fields\n\n        private float _offset;\n        public float Offset\n        {\n            get => _offset;\n            set\n            {\n                if (!IsFixedOffset)\n                    SetProperty(ref _offset, float.IsNaN(value) ? 0 : MathHelper.Clamp(value, 0, 1));\n            }\n        }\n\n        private float _value;\n        public float Value\n        {\n            get => _value;\n            set => SetProperty(ref _value, float.IsNaN(value) ? 0 : MathHelper.Clamp(value, -1, 1));\n        }\n\n        public bool IsFixedOffset { get; set; }\n\n        #endregion\n\n        #region Constructors\n\n        public EqualizerBand() : this(0) { }\n\n        public EqualizerBand(float offset, float value = 0, bool fixedOffset = false)\n        {\n            this.Offset = offset;\n            this.Value = value;\n            this.IsFixedOffset = fixedOffset;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/Equalizer/IEqualizer.cs",
    "content": "﻿using System.Collections.ObjectModel;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.Equalizer\n{\n    public interface IEqualizer\n    {\n        bool IsEnabled { get; set; }\n\n        ObservableCollection<EqualizerBand> Bands { get; }\n        \n        float[] CalculateValues(int count);\n\n        EqualizerBand AddBand(float offset, float modification);\n        void RemoveBandBand(EqualizerBand band);\n\n        void Reset();\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/Equalizer/MultiBandEqualizer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\nusing RGB.NET.Core;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.Equalizer\n{\n    public class MultiBandEqualizer : AbstractBindable, IEqualizer\n    {\n        #region Properties & Fields\n\n        public ObservableCollection<EqualizerBand> Bands { get; } = new ObservableCollection<EqualizerBand>();\n\n        private readonly Dictionary<int, float[]> _values = new Dictionary<int, float[]>();\n\n        private bool _isEnabled;\n        public bool IsEnabled\n        {\n            get => _isEnabled;\n            set => SetProperty(ref _isEnabled, value);\n        }\n\n        #endregion\n\n        #region Constructors\n\n        public MultiBandEqualizer()\n        {\n            Reset();\n        }\n\n        #endregion\n\n        #region Methods\n\n        public EqualizerBand AddBand(float offset, float modification) => AddBand(offset, modification, false);\n\n        public EqualizerBand AddBand(float offset, float modification, bool isFixedFrequency)\n        {\n            EqualizerBand band = new EqualizerBand(offset, modification, isFixedFrequency);\n            band.PropertyChanged += (sender, args) => InvalidateCache();\n            Bands.Add(band);\n\n            InvalidateCache();\n\n            return band;\n        }\n\n        public void RemoveBandBand(EqualizerBand band)\n        {\n            if (!band.IsFixedOffset)\n                Bands.Remove(band);\n\n            InvalidateCache();\n        }\n\n        public void Reset()\n        {\n            Bands.Clear();\n            AddBand(0, 0, true);\n            AddBand(1, 0, true);\n        }\n\n        public float[] CalculateValues(int count)\n        {\n            if (!_values.TryGetValue(count, out float[] values))\n            {\n                values = RecalculateValues(count);\n                _values[count] = values;\n            }\n            return values;\n        }\n\n        private float[] RecalculateValues(int count)\n        {\n            float[] values = new float[count];\n\n            List<EqualizerBand> orderedBands = Bands.OrderBy(x => x.Offset).ToList();\n            if (orderedBands.Count < 2) return values;\n\n            for (int i = 0; i < count; i++)\n            {\n                float offset = (i / (float)count);\n                EqualizerBand bandBefore = orderedBands.Last(n => n.Offset <= offset);\n                EqualizerBand bandAfter = orderedBands.First(n => n.Offset >= offset);\n                offset = (bandAfter.Offset <= 0) || (Math.Abs(bandAfter.Offset - bandBefore.Offset) < 0.0001)\n                    ? 0 : (offset - bandBefore.Offset) / (bandAfter.Offset - bandBefore.Offset);\n                float value = (float)((3.0 * (offset * offset)) - (2.0 * (offset * offset * offset)));\n                values[i] = bandBefore.Value + (value * (bandAfter.Value - bandBefore.Value));\n            }\n\n            return values;\n        }\n\n        private void InvalidateCache()\n        {\n            _values.Clear();\n\n            // ReSharper disable once ExplicitCallerInfoArgument\n            OnPropertyChanged(nameof(Bands));\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/IAudioProcessor.cs",
    "content": "﻿using System;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing\n{\n    public interface IAudioProcessor : IDisposable\n    {\n        bool IsActive { get; set; }\n\n        void Initialize();\n        void Update();\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/Spectrum/AbstractSpectrum.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.Spectrum\n{\n    public abstract class AbstractSpectrum : ISpectrum\n    {\n        #region Properties & Fields\n\n        protected Band[] Bands { get; set; }\n        public int BandCount => Bands.Length;\n\n        public Band this[int index] => Bands[index];\n        public Band this[float frequency] => Bands.FirstOrDefault(band => (band.LowerFrequency <= frequency) && (band.UpperFrequency >= frequency));\n        public Band[] this[float minFrequency, float maxFrequency] => Bands.Where(band => (band.LowerFrequency > minFrequency) && (band.UpperFrequency < maxFrequency)).ToArray();\n\n        #endregion\n\n        #region Methods\n\n        public IEnumerator<Band> GetEnumerator() => Bands.AsEnumerable().GetEnumerator();\n        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/Spectrum/Band.cs",
    "content": "﻿using System.Linq;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.Spectrum\n{\n    public class Band\n    {\n        #region Properties & Fields\n\n        private readonly float[] _data;\n        private readonly float _resolution;\n\n        public float LowerFrequency { get; }\n        public float UpperFrequency { get; }\n        public float CenterFrequency { get; }\n\n        private float? _average = null;\n        public float Average => _average ?? (_average = _data.Average()).Value;\n\n        private float? _min = null;\n        public float Min => _min ?? (_min = _data.Min()).Value;\n\n        private float? _max = null;\n        public float Max => _max ?? (_max = _data.Max()).Value;\n\n        private float? _sum = null;\n        public float Sum => _sum ?? (_sum = _data.Sum()).Value;\n\n        public float this[int index] => _data[index];\n        public float this[float frequency] => _data[(int)((frequency - LowerFrequency) / _resolution)];\n\n        #endregion\n\n        #region Constructors\n\n        public Band(float lowerFrequency, float upperFrequency, float[] data)\n        {\n            this.LowerFrequency = lowerFrequency;\n            this.UpperFrequency = upperFrequency;\n            this.CenterFrequency = (LowerFrequency + UpperFrequency) / 2f; //TODO DarthAffe 12.08.2017: Is this valid for logarithmic scaling?\n            this._data = data;\n\n            _resolution = (UpperFrequency - LowerFrequency) / data.Length;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/Spectrum/FourierSpectrumProvider.cs",
    "content": "﻿using System;\nusing KeyboardAudioVisualizer.AudioCapture;\nusing MathNet.Numerics;\nusing MathNet.Numerics.IntegralTransforms;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.Spectrum\n{\n    public class FourierSpectrumProvider : AbstractAudioProcessor, ISpectrumProvider\n    {\n        #region Properties & Fields\n\n        private readonly AudioBuffer _audioBuffer;\n\n        private float[] _sampleData;\n        private double[] _hamming;\n        private Complex32[] _complexBuffer;\n\n        private float[] _spectrum;\n        private int _usableDataLength;\n\n        #endregion\n\n        #region Constructors\n\n        public FourierSpectrumProvider(AudioBuffer audioBuffer)\n        {\n            this._audioBuffer = audioBuffer;\n        }\n\n        #endregion\n\n        #region Methods\n\n        public override void Initialize()\n        {\n            _hamming = Window.Hamming(_audioBuffer.Size);\n            _sampleData = new float[_audioBuffer.Size];\n            _complexBuffer = new Complex32[_audioBuffer.Size];\n            _usableDataLength = (_audioBuffer.Size / 2) + 1;\n            _spectrum = new float[_usableDataLength];\n        }\n\n        public override void Update()\n        {\n            _audioBuffer.CopyMixInto(ref _sampleData, 0);\n\n            ApplyHamming(ref _sampleData);\n            CreateSpectrum(ref _sampleData);\n        }\n\n        private void ApplyHamming(ref float[] data)\n        {\n            for (int i = 0; i < data.Length; i++)\n                data[i] = (float)(data[i] * _hamming[i]);\n        }\n\n        private void CreateSpectrum(ref float[] data)\n        {\n            for (int i = 0; i < data.Length; i++)\n                _complexBuffer[i] = new Complex32(data[i], 0);\n\n            Fourier.Forward(_complexBuffer, FourierOptions.NoScaling);\n\n            for (int i = 0; i < _spectrum.Length; i++)\n            {\n                Complex32 fourierData = _complexBuffer[i];\n                _spectrum[i] = (float)Math.Sqrt(fourierData.Real * fourierData.Real) + (fourierData.Imaginary * fourierData.Imaginary);\n            }\n        }\n\n        public ISpectrum GetLinearSpectrum(int bands = 64, float minFrequency = -1, float maxFrequency = -1) => new LinearSpectrum(_spectrum, bands, minFrequency, maxFrequency);\n\n        public ISpectrum GetLogarithmicSpectrum(int bands = 12, float minFrequency = -1, float maxFrequency = -1) => new LogarithmicSpectrum(_spectrum, bands, minFrequency, maxFrequency);\n\n        public ISpectrum GetGammaSpectrum(int bands = 64, float gamma = 2, float minFrequency = -1, float maxFrequency = -1) => new GammaSpectrum(_spectrum, bands, gamma, minFrequency, maxFrequency);\n\n        public ISpectrum GetRawSpectrum() => new RawSpectrumProvider(_spectrum);\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/Spectrum/GammaSpectrum.cs",
    "content": "﻿using System;\nusing KeyboardAudioVisualizer.Helper;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.Spectrum\n{\n    public class GammaSpectrum : AbstractSpectrum\n    {\n        #region Constructors\n\n        public GammaSpectrum(float[] data, int bands, float gamma = 2, float minFrequency = -1, float maxFrequency = -1)\n        {\n            int dataReferenceCount = (data.Length - 1) * 2;\n\n            int fromIndex = minFrequency < 0 ? 0 : MathHelper.Clamp(FrequencyHelper.GetIndexOfFrequency(minFrequency, dataReferenceCount), 0, data.Length - 1 - bands); // -bands since we need at least enough data to get our bands\n            int toIndex = maxFrequency < 0 ? data.Length - 1 : MathHelper.Clamp(FrequencyHelper.GetIndexOfFrequency(maxFrequency, dataReferenceCount), fromIndex, data.Length - 1);\n\n            int usableSourceData = Math.Max(bands, (toIndex - fromIndex) + 1);\n\n            Bands = new Band[bands];\n\n            int index = fromIndex;\n            for (int i = 0; i < Bands.Length; i++)\n            {\n                int count = Math.Max(1, (((int)(Math.Pow((i + 1f) / Bands.Length, gamma) * usableSourceData))) - index);\n\n                float[] bandData = new float[count];\n                Array.Copy(data, index, bandData, 0, count);\n                Bands[i] = new Band(FrequencyHelper.GetFrequencyOfIndex(index, dataReferenceCount),\n                                    FrequencyHelper.GetFrequencyOfIndex(index + count, dataReferenceCount),\n                                    bandData);\n\n                index += count;\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/Spectrum/ISpectrum.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.Spectrum\n{\n    public interface ISpectrum : IEnumerable<Band>\n    {\n        int BandCount { get; }\n\n        Band this[int index] { get; }\n        Band this[float frequency] { get; }\n        Band[] this[float minFrequency, float maxFrequency] { get; }\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/Spectrum/ISpectrumProvider.cs",
    "content": "﻿namespace KeyboardAudioVisualizer.AudioProcessing.Spectrum\n{\n    public interface ISpectrumProvider : IAudioProcessor\n    {\n        ISpectrum GetLinearSpectrum(int bands = 64, float minFrequency = -1, float maxFrequency = -1);\n        ISpectrum GetLogarithmicSpectrum(int bands = 1, float minFrequency = -1, float maxFrequency = -1);\n        ISpectrum GetGammaSpectrum(int bands = 1, float gamma = 2, float minFrequency = -1, float maxFrequency = -1);\n        ISpectrum GetRawSpectrum();\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/Spectrum/LinearSpectrum.cs",
    "content": "﻿using System;\nusing KeyboardAudioVisualizer.Helper;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.Spectrum\n{\n    public class LinearSpectrum : AbstractSpectrum\n    {\n        #region Constructors\n\n        public LinearSpectrum(float[] data, int bands, float minFrequency = -1, float maxFrequency = -1)\n        {\n            int dataReferenceCount = (data.Length - 1) * 2;\n\n            int fromIndex = minFrequency < 0 ? 0 : MathHelper.Clamp(FrequencyHelper.GetIndexOfFrequency(minFrequency, dataReferenceCount), 0, data.Length - 1 - bands); // -bands since we need at least enough data to get our bands\n            int toIndex = maxFrequency < 0 ? data.Length - 1 : MathHelper.Clamp(FrequencyHelper.GetIndexOfFrequency(maxFrequency, dataReferenceCount), fromIndex, data.Length - 1);\n\n            int usableSourceData = Math.Max(bands, (toIndex - fromIndex) + 1);\n\n            Bands = new Band[bands];\n\n            double frequenciesPerBand = (double)usableSourceData / bands;\n            double frequencyCounter = 0;\n\n            int index = fromIndex;\n            for (int i = 0; i < Bands.Length; i++)\n            {\n                frequencyCounter += frequenciesPerBand;\n                int count = (int)frequencyCounter;\n\n                float[] bandData = new float[count];\n                Array.Copy(data, index, bandData, 0, count);\n                Bands[i] = new Band(FrequencyHelper.GetFrequencyOfIndex(index, dataReferenceCount),\n                                    FrequencyHelper.GetFrequencyOfIndex(index + count, dataReferenceCount),\n                                    bandData);\n\n                index += count;\n                frequencyCounter -= count;\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/Spectrum/LogarithmicSpectrum.cs",
    "content": "﻿using System;\nusing KeyboardAudioVisualizer.Helper;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.Spectrum\n{\n    public class LogarithmicSpectrum : AbstractSpectrum\n    {\n        #region Constructors\n\n        public LogarithmicSpectrum(float[] data, int bands, float minFrequency = -1, float maxFrequency = -1)\n        {\n            int dataReferenceCount = (data.Length - 1) * 2;\n\n            int fromIndex = minFrequency < 0 ? 0 : MathHelper.Clamp(FrequencyHelper.GetIndexOfFrequency(minFrequency, dataReferenceCount), 0, data.Length - 1 - bands); // -bands since we need at least enough data to get our bands\n            int toIndex = maxFrequency < 0 ? data.Length - 1 : MathHelper.Clamp(FrequencyHelper.GetIndexOfFrequency(maxFrequency, dataReferenceCount), fromIndex, data.Length - 1);\n\n            int usableSourceData = Math.Max(bands, (toIndex - fromIndex) + 1);\n\n            Bands = new Band[bands];\n\n            double ratio = Math.Pow(usableSourceData, 1.0 / bands);\n            double calculation = 1;\n\n            int index = fromIndex;\n            for (int i = 0; i < Bands.Length; i++)\n            {\n                calculation *= ratio;\n                int count = Math.Max(1, ((int)calculation) - index);\n\n                float[] bandData = new float[count];\n                Array.Copy(data, index, bandData, 0, count);\n                Bands[i] = new Band(FrequencyHelper.GetFrequencyOfIndex(index, dataReferenceCount),\n                                    FrequencyHelper.GetFrequencyOfIndex(index + count, dataReferenceCount),\n                                    bandData);\n\n                index += count;\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/Spectrum/RawSpectrumProvider.cs",
    "content": "﻿using KeyboardAudioVisualizer.Helper;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.Spectrum\n{\n    public class RawSpectrumProvider : AbstractSpectrum\n    {\n        #region Constructors\n\n        public RawSpectrumProvider(float[] data)\n        {\n            int dataReferenceCount = (data.Length - 1) * 2;\n\n            Bands = new Band[data.Length];\n\n            for (int i = 0; i < Bands.Length; i++)\n                Bands[i] = new Band(FrequencyHelper.GetFrequencyOfIndex(i, dataReferenceCount), FrequencyHelper.GetFrequencyOfIndex(i, dataReferenceCount), new[] { data[i] });\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/VisualizationPRovider/FrequencyBarsVisualizationProvider.cs",
    "content": "﻿using System;\nusing KeyboardAudioVisualizer.AudioProcessing.Equalizer;\nusing KeyboardAudioVisualizer.AudioProcessing.Spectrum;\nusing KeyboardAudioVisualizer.Configuration;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider\n{\n    #region Configuration\n\n    public enum ValueMode { Max, Average, Sum }\n    public enum SpectrumMode { Gamma, Logarithmic, Linear }\n\n    public class FrequencyBarsVisualizationProviderConfiguration : AbstractConfiguration\n    {\n        private ValueMode _valueMode = ValueMode.Sum;\n        public ValueMode ValueMode\n        {\n            get => _valueMode;\n            set => SetProperty(ref _valueMode, value);\n        }\n\n        private SpectrumMode _spectrumMode = SpectrumMode.Logarithmic;\n        public SpectrumMode SpectrumMode\n        {\n            get => _spectrumMode;\n            set => SetProperty(ref _spectrumMode, value);\n        }\n\n        private int _bars = 48;\n        public int Bars\n        {\n            get => _bars;\n            set => SetProperty(ref _bars, value);\n        }\n\n        private double _smoothing = 3;\n        public double Smoothing\n        {\n            get => _smoothing;\n            set => SetProperty(ref _smoothing, value);\n        }\n\n        private double _minFrequency = 60;\n        public double MinFrequency\n        {\n            get => _minFrequency;\n            set => SetProperty(ref _minFrequency, value);\n        }\n\n        private double _maxFrequency = 15000;\n        public double MaxFrequency\n        {\n            get => _maxFrequency;\n            set => SetProperty(ref _maxFrequency, value);\n        }\n\n        private double _referenceLevel = 90;\n        public double ReferenceLevel\n        {\n            get => _referenceLevel;\n            set => SetProperty(ref _referenceLevel, value);\n        }\n\n        private double _emphasisePeaks = 0.5f;\n        public double EmphasisePeaks\n        {\n            get => _emphasisePeaks;\n            set => SetProperty(ref _emphasisePeaks, value);\n        }\n\n        private int _gamma = 2;\n        public int Gamma\n        {\n            get => _gamma;\n            set => SetProperty(ref _gamma, value);\n        }\n    }\n\n    #endregion\n\n    public class FrequencyBarsVisualizationProvider : AbstractAudioProcessor, IVisualizationProvider\n    {\n        #region Properties & Fields\n\n        private readonly FrequencyBarsVisualizationProviderConfiguration _configuration;\n        private readonly ISpectrumProvider _spectrumProvider;\n\n        private double _smoothingFactor;\n        private double _emphasiseFactor;\n\n        public IEqualizer Equalizer { get; set; }\n        public IConfiguration Configuration => _configuration;\n        public float[] VisualizationData { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public FrequencyBarsVisualizationProvider(FrequencyBarsVisualizationProviderConfiguration configuration, ISpectrumProvider spectrumProvider)\n        {\n            this._configuration = configuration;\n            this._spectrumProvider = spectrumProvider;\n\n            configuration.PropertyChanged += (sender, args) => RecalculateConfigValues(args.PropertyName);\n        }\n\n        #endregion\n\n        #region Methods\n\n        public override void Initialize() => RecalculateConfigValues(null);\n\n        private void RecalculateConfigValues(string changedPropertyName)\n        {\n            if ((changedPropertyName == null) || (changedPropertyName == nameof(FrequencyBarsVisualizationProviderConfiguration.Bars)))\n                VisualizationData = new float[_configuration.Bars];\n\n            if ((changedPropertyName == null) || (changedPropertyName == nameof(FrequencyBarsVisualizationProviderConfiguration.Smoothing)))\n                _smoothingFactor = Math.Log10(_configuration.Smoothing);\n\n            if ((changedPropertyName == null) || (changedPropertyName == nameof(FrequencyBarsVisualizationProviderConfiguration.EmphasisePeaks)))\n                _emphasiseFactor = (0.75 * (1 + _configuration.EmphasisePeaks));\n        }\n\n        public override void Update()\n        {\n            ISpectrum spectrum = GetSpectrum();\n            if (spectrum == null) return;\n\n            float[] equalizerValues = Equalizer?.IsEnabled == true ? Equalizer.CalculateValues(spectrum.BandCount) : null;\n\n            for (int i = 0; i < spectrum.BandCount; i++)\n            {\n                double binPower = GetBandValue(spectrum[i]);\n\n                if (equalizerValues != null)\n                {\n                    float equalizerValue = equalizerValues[i];\n                    equalizerValue *= 10; //TODO DarthAffe 13.08.2017: Equalizer-Scale through setting?\n                    if (Math.Abs(equalizerValue) > 0.000001)\n                    {\n                        bool lower = equalizerValue < 0;\n                        equalizerValue = 1 + (equalizerValue * equalizerValue);\n                        binPower *= lower ? 1f / equalizerValue : equalizerValue;\n                    }\n                }\n\n                binPower = Math.Max(0, 20 * Math.Log10(binPower));\n\n                binPower = Math.Max(0, binPower);\n                binPower /= _configuration.ReferenceLevel;\n                if (_configuration.EmphasisePeaks > 0.001)\n                    binPower = Math.Pow(binPower, 1 + _configuration.EmphasisePeaks) * _emphasiseFactor;\n\n                if (i < VisualizationData.Length)\n                {\n                    VisualizationData[i] = (float)((VisualizationData[i] * _smoothingFactor) + (binPower * (1.0 - _smoothingFactor)));\n                    if (float.IsNaN(VisualizationData[i])) VisualizationData[i] = 0;\n                }\n            }\n        }\n\n        private ISpectrum GetSpectrum()\n        {\n            switch (_configuration.SpectrumMode)\n            {\n                case SpectrumMode.Gamma:\n                    return _spectrumProvider.GetGammaSpectrum(_configuration.Bars, _configuration.Gamma, (float)_configuration.MinFrequency, (float)_configuration.MaxFrequency);\n                case SpectrumMode.Logarithmic:\n                    return _spectrumProvider.GetLogarithmicSpectrum(_configuration.Bars, (float)_configuration.MinFrequency, (float)_configuration.MaxFrequency);\n                case SpectrumMode.Linear:\n                    return _spectrumProvider.GetLinearSpectrum(_configuration.Bars, (float)_configuration.MinFrequency, (float)_configuration.MaxFrequency);\n                default:\n                    return null;\n            }\n        }\n\n        private float GetBandValue(Band band)\n        {\n            switch (_configuration.ValueMode)\n            {\n                case ValueMode.Max: return band.Max;\n                case ValueMode.Average: return band.Average;\n                case ValueMode.Sum: return band.Sum;\n                default: return 0;\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/VisualizationPRovider/IVisualizationProvider.cs",
    "content": "﻿using KeyboardAudioVisualizer.Configuration;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider\n{\n    public interface IVisualizationProvider\n    {\n        IConfiguration Configuration { get; }\n        float[] VisualizationData { get; }\n\n        void Initialize();\n        void Update();\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/BeatVisualizationProvider.cs",
    "content": "﻿using KeyboardAudioVisualizer.AudioProcessing.Spectrum;\nusing KeyboardAudioVisualizer.Configuration;\nusing KeyboardAudioVisualizer.Helper;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider\n{\n    #region Configuration\n\n    public class BeatVisualizationProviderConfiguration : AbstractConfiguration\n    {\n        //TODO DarthAffe 12.08.2017: Check if there is something usefull to configure here\n    }\n\n    #endregion\n\n    public class BeatVisualizationProvider : AbstractAudioProcessor, IVisualizationProvider\n    {\n        #region Properties & Fields\n\n        private readonly BeatVisualizationProviderConfiguration _configuration;\n        private readonly ISpectrumProvider _specturProvider;\n\n        private RingBuffer[] _history;\n\n        public IConfiguration Configuration => _configuration;\n        public float[] VisualizationData { get; } = new float[1];\n\n        #endregion\n\n        #region Constructors\n\n        public BeatVisualizationProvider(BeatVisualizationProviderConfiguration configuration, ISpectrumProvider specturProvider)\n        {\n            this._configuration = configuration;\n            this._specturProvider = specturProvider;\n        }\n\n        #endregion\n\n        #region Methods\n\n        public override void Initialize()\n        {\n            _history = new RingBuffer[64];\n            for (int i = 0; i < _history.Length; i++)\n                _history[i] = new RingBuffer(32);\n        }\n\n        public override void Update()\n        {\n            VisualizationData[0] = 0;\n\n            ISpectrum spectrum = _specturProvider.GetLogarithmicSpectrum(64);\n            for (int i = 0; i < 32; i++)\n            {\n                float currentEnergy = spectrum[i].Average;\n                float averageEnergy = _history[i].Average;\n                _history[i].Put(currentEnergy);\n\n                if (currentEnergy > (35 * averageEnergy))\n                {\n                    VisualizationData[0] = 1;\n                    break;\n                }\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/LevelVisualizationProvider.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing KeyboardAudioVisualizer.AudioCapture;\nusing KeyboardAudioVisualizer.Configuration;\nusing KeyboardAudioVisualizer.Helper;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider\n{\n    #region Configuration\n\n    public enum ConversionMode\n    {\n        Linear, Logarithmic, Exponential\n    }\n\n    public class LevelVisualizationProviderConfiguration : AbstractConfiguration\n    {\n        private ConversionMode _conversionMode = ConversionMode.Logarithmic;\n        public ConversionMode ConversionMode\n        {\n            get => _conversionMode;\n            set => SetProperty(ref _conversionMode, value);\n        }\n\n        private double _smoothing = 3;\n        public double Smoothing\n        {\n            get => _smoothing;\n            set => SetProperty(ref _smoothing, value);\n        }\n\n        private double _scale = 8;\n        public double Scale\n        {\n            get => _scale;\n            set => SetProperty(ref _scale, value);\n        }\n\n        private double _referenceLevel = 90;\n        public double ReferenceLevel\n        {\n            get => _referenceLevel;\n            set => SetProperty(ref _referenceLevel, value);\n        }\n    }\n\n    #endregion\n\n    public class LevelVisualizationProvider : AbstractAudioProcessor, IVisualizationProvider\n    {\n        #region Properties & Fields\n\n        private readonly LevelVisualizationProviderConfiguration _configuration;\n        private readonly AudioBuffer _audioBuffer;\n\n        private float[] _sampleDataLeft;\n        private float[] _sampleDataRight;\n        private float[] _sampleDataMix;\n        private double _smoothingFactor;\n        private double _scalingFactor;\n\n        public IConfiguration Configuration => _configuration;\n        public float[] VisualizationData { get; } = new float[3];\n\n        #endregion\n\n        #region Constructors\n\n        public LevelVisualizationProvider(LevelVisualizationProviderConfiguration configuration, AudioBuffer audioBuffer)\n        {\n            this._configuration = configuration;\n            this._audioBuffer = audioBuffer;\n\n            configuration.PropertyChanged += (sender, args) => RecalculateConfigValues(args.PropertyName);\n        }\n\n        #endregion\n\n        #region Methods\n\n        public override void Initialize()\n        {\n            _sampleDataLeft = new float[2048];\n            _sampleDataRight = new float[2048];\n            _sampleDataMix = new float[2048];\n\n            RecalculateConfigValues(null);\n        }\n\n        private void RecalculateConfigValues(string changedPropertyName)\n        {\n            if ((changedPropertyName == null) || (changedPropertyName == nameof(LevelVisualizationProviderConfiguration.Smoothing)))\n                _smoothingFactor = Math.Log10(MathHelper.Clamp(_configuration.Smoothing, 0.001, 9.5));\n\n            if ((changedPropertyName == null) || (changedPropertyName == nameof(LevelVisualizationProviderConfiguration.Scale))\n                                              || (changedPropertyName == nameof(LevelVisualizationProviderConfiguration.ConversionMode)))\n            {\n                switch (_configuration.ConversionMode)\n                {\n                    case ConversionMode.Linear:\n                        _scalingFactor = _configuration.Scale / 2.5f;\n                        break;\n                    case ConversionMode.Logarithmic:\n                        _scalingFactor = _configuration.Scale * 2.5f;\n                        break;\n                    case ConversionMode.Exponential:\n                        _scalingFactor = _configuration.Scale;\n                        break;\n                    default:\n                        throw new ArgumentOutOfRangeException();\n                }\n            }\n        }\n\n        public override void Update()\n        {\n            _audioBuffer.CopyLeftInto(ref _sampleDataLeft, 0);\n            _audioBuffer.CopyRightInto(ref _sampleDataRight, 0);\n            _audioBuffer.CopyMixInto(ref _sampleDataMix, 0);\n\n            float levelLeft = Convert(GetRms(ref _sampleDataLeft));\n            float levelRight = Convert(GetRms(ref _sampleDataRight));\n            float levelMix = Convert(GetRms(ref _sampleDataMix));\n\n            UpdateData(0, levelLeft);\n            UpdateData(1, levelRight);\n            UpdateData(2, levelMix);\n        }\n\n        private float GetRms(ref float[] data) => (float)Math.Sqrt(data.Average(x => x * x));\n\n        private float Convert(float level)\n        {\n            // DarthAffe 12.08.2017: The naming here is a bit off, but as long as it loos good :p\n            switch (_configuration.ConversionMode)\n            {\n                case ConversionMode.Exponential:\n                    return level * level;\n\n                case ConversionMode.Logarithmic:\n                    return (float)Math.Max(0, (Math.Pow(_configuration.ReferenceLevel, level) - 1) / _configuration.ReferenceLevel);\n\n                default: return level;\n            }\n        }\n\n        private void UpdateData(int index, float level)\n        {\n            VisualizationData[index] = (float)((VisualizationData[index] * _smoothingFactor) + (level * _scalingFactor * (1.0 - _smoothingFactor)));\n            if (double.IsNaN(VisualizationData[index])) VisualizationData[index] = 0;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/AudioProcessing/VisualizationProvider/VisualizationType.cs",
    "content": "﻿using KeyboardAudioVisualizer.Attributes;\nusing RGB.NET.Core;\n\nnamespace KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider\n{\n    public enum VisualizationType\n    {\n        None,\n\n        [VisualizerFor(RGBDeviceType.Keyboard | RGBDeviceType.LedMatrix)]\n        [DisplayName(\"Frequency Bars\")]\n        FrequencyBars,\n\n        [VisualizerFor(RGBDeviceType.Keyboard | RGBDeviceType.LedMatrix | RGBDeviceType.LedStripe | RGBDeviceType.Mousepad)]\n        Level,\n\n        Beat,\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Configuration/AbstractConfiguration.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Runtime.CompilerServices;\nusing RGB.NET.Core;\n\nnamespace KeyboardAudioVisualizer.Configuration\n{\n    public class AbstractConfiguration : AbstractBindable, IConfiguration, INotifyPropertyChanged\n    {\n        #region Methods\n\n        protected override bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)\n        {\n            if ((typeof(T) == typeof(double)) || (typeof(T) == typeof(float)))\n            {\n                if (Math.Abs((double)(object)storage - (double)(object)value) < 0.000001) return false;\n            }\n            else\n            {\n                if (Equals(storage, value)) return false;\n            }\n\n            storage = value;\n            // ReSharper disable once ExplicitCallerInfoArgument\n            OnPropertyChanged(propertyName);\n            return true;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Configuration/ColorSerializer.cs",
    "content": "﻿using System;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing RGB.NET.Core;\n\nnamespace KeyboardAudioVisualizer.Configuration\n{\n    public class ColorSerializer : JsonConverter\n    {\n        #region Methods\n\n        public override bool CanConvert(Type objectType) => objectType == typeof(Color);\n\n        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)\n        {\n            if (!(value is Color color)) return;\n\n            writer.WriteStartObject();\n            writer.WritePropertyName(\"A\");\n            writer.WriteValue(color.A);\n            writer.WritePropertyName(\"R\");\n            writer.WriteValue(color.R);\n            writer.WritePropertyName(\"G\");\n            writer.WriteValue(color.G);\n            writer.WritePropertyName(\"B\");\n            writer.WriteValue(color.B);\n            writer.WriteEndObject();\n        }\n\n        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)\n        {\n            JObject jsonObject = JObject.Load(reader);\n            if (jsonObject.Property(\"A\").Value.ToObject<double>() > 1.0) //DarthAffe 09.06.2019: Convert old Settings\n                return new Color(jsonObject.Property(\"A\").Value.ToObject<byte>(),\n                                 jsonObject.Property(\"R\").Value.ToObject<byte>(),\n                                 jsonObject.Property(\"G\").Value.ToObject<byte>(),\n                                 jsonObject.Property(\"B\").Value.ToObject<byte>());\n            else\n                return new Color(jsonObject.Property(\"A\").Value.ToObject<double>(),\n                                 jsonObject.Property(\"R\").Value.ToObject<double>(),\n                                 jsonObject.Property(\"G\").Value.ToObject<double>(),\n                                 jsonObject.Property(\"B\").Value.ToObject<double>());\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Configuration/EqualizerConfiguration.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing KeyboardAudioVisualizer.AudioProcessing.Equalizer;\n\nnamespace KeyboardAudioVisualizer.Configuration\n{\n    public class EqualizerConfiguration : AbstractConfiguration\n    {\n        #region Properties & Fields\n\n        public bool IsEnabled { get; set; } = false;\n\n        public List<EqualizerBand> Bands { get; set; } = new List<EqualizerBand>();\n\n        #endregion\n\n        #region Methods\n\n        public void LoadInto(IEqualizer equalizer)\n        {\n            equalizer.IsEnabled = IsEnabled;\n\n            foreach (EqualizerBand band in Bands)\n            {\n                if (band.IsFixedOffset)\n                {\n                    EqualizerBand bandToUpdate = equalizer.Bands.FirstOrDefault(b => b.IsFixedOffset && (Math.Abs(b.Offset - band.Offset) < 0.01));\n                    if (bandToUpdate != null)\n                        bandToUpdate.Value = band.Value;\n                }\n                else\n                    equalizer.AddBand(band.Offset, band.Value);\n            }\n        }\n\n        public void SaveFrom(IEqualizer equalizer)\n        {\n            IsEnabled = equalizer.IsEnabled;\n\n            Bands.Clear();\n            foreach (EqualizerBand band in equalizer.Bands)\n                Bands.Add(new EqualizerBand(band.Offset, band.Value, band.IsFixedOffset));\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Configuration/IConfiguration.cs",
    "content": "﻿using System.ComponentModel;\n\nnamespace KeyboardAudioVisualizer.Configuration\n{\n    public interface IConfiguration : INotifyPropertyChanged\n    { }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Configuration/Settings.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing KeyboardAudioVisualizer.Helper;\nusing RGB.NET.Brushes.Gradients;\nusing RGB.NET.Core;\n\nnamespace KeyboardAudioVisualizer.Configuration\n{\n    public class Settings\n    {\n        #region Constants\n\n        public const int CURRENT_VERSION = 1;\n\n        #endregion\n\n        #region Properties & Fields\n\n        public int Version { get; set; } = 0;\n\n        public double UpdateRate { get; set; } = 40.0;\n\n        public bool EnableAudioPrescale { get; set; } = false;\n\n        public LinearGradient Background { get; set; }\n\n        public Dictionary<VisualizationIndex, VisualizationSettings> Visualizations { get; set; } = new Dictionary<VisualizationIndex, VisualizationSettings>();\n\n        public VisualizationSettings this[VisualizationIndex visualizationIndex]\n        {\n            get\n            {\n                if (!Visualizations.TryGetValue(visualizationIndex, out VisualizationSettings settings))\n                    Visualizations[visualizationIndex] = (settings = new VisualizationSettings(visualizationIndex));\n                return settings;\n            }\n        }\n\n        #endregion\n    }\n\n    public class VisualizationSettings\n    {\n        #region Properties & Fields\n\n        public VisualizationType SelectedVisualization { get; set; }\n\n        public LinearGradient Gradient { get; set; }\n\n        public EqualizerConfiguration EqualizerConfiguration { get; set; } = new EqualizerConfiguration();\n\n        public FrequencyBarsVisualizationProviderConfiguration FrequencyBarsConfiguration { get; set; } = new FrequencyBarsVisualizationProviderConfiguration();\n        public LevelVisualizationProviderConfiguration LevelConfiguration { get; set; } = new LevelVisualizationProviderConfiguration();\n        public BeatVisualizationProviderConfiguration BeatConfiguration { get; set; } = new BeatVisualizationProviderConfiguration();\n\n        public IConfiguration this[VisualizationType visualizationType]\n        {\n            get\n            {\n                switch (visualizationType)\n                {\n                    case VisualizationType.None:\n                        return null;\n\n                    case VisualizationType.FrequencyBars:\n                        return FrequencyBarsConfiguration;\n\n                    case VisualizationType.Level:\n                        return LevelConfiguration;\n\n                    case VisualizationType.Beat:\n                        return BeatConfiguration;\n\n                    default:\n                        throw new ArgumentOutOfRangeException(nameof(visualizationType), visualizationType, null);\n                }\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        public VisualizationSettings(VisualizationIndex visualizationIndex)\n        {\n            switch (visualizationIndex)\n            {\n                case VisualizationIndex.Primary:\n                    SelectedVisualization = VisualizationType.FrequencyBars;\n                    Gradient = new LinearGradient(new GradientStop(0, HSVColor.Create(300, 1, 1)),\n                                                  new GradientStop(0.20, HSVColor.Create(225, 1, 1)),\n                                                  new GradientStop(0.35, HSVColor.Create(180, 1, 1)),\n                                                  new GradientStop(0.50, HSVColor.Create(135, 1, 1)),\n                                                  new GradientStop(0.65, HSVColor.Create(90, 1, 1)),\n                                                  new GradientStop(0.80, HSVColor.Create(45, 1, 1)),\n                                                  new GradientStop(0.95, HSVColor.Create(0, 1, 1)));\n                    break;\n\n                case VisualizationIndex.Secondary:\n                    SelectedVisualization = VisualizationType.Beat;\n                    Gradient = new LinearGradient(new GradientStop(0.5, new Color(255, 255, 255)));\n                    break;\n\n                case VisualizationIndex.Tertiary:\n                    SelectedVisualization = VisualizationType.Level;\n                    Gradient = new LinearGradient(new GradientStop(0, new Color(0, 0, 255)),\n                                                  new GradientStop(1, new Color(255, 0, 0)));\n                    break;\n            }\n        }\n\n        #endregion\n\n        #region Methods\n\n        public T GetConfiguration<T>(VisualizationType visualizationType)\n            where T : IConfiguration, new() => (T)this[visualizationType];\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Controls/BlurredDecorationWindow.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Windows.Media;\n\nnamespace KeyboardAudioVisualizer.Controls\n{\n    [TemplatePart(Name = \"PART_Decoration\", Type = typeof(FrameworkElement))]\n    [TemplatePart(Name = \"PART_Content\", Type = typeof(FrameworkElement))]\n    [TemplatePart(Name = \"PART_CloseButton\", Type = typeof(Button))]\n    [TemplatePart(Name = \"PART_MinimizeButton\", Type = typeof(Button))]\n    [TemplatePart(Name = \"PART_IconButton\", Type = typeof(Button))]\n    public class BlurredDecorationWindow : Window\n    {\n        #region DependencyProperties\n        // ReSharper disable InconsistentNaming\n\n        public static readonly DependencyProperty BackgroundImageProperty = DependencyProperty.Register(\n            \"BackgroundImage\", typeof(ImageSource), typeof(BlurredDecorationWindow), new PropertyMetadata(default(ImageSource)));\n\n        public ImageSource BackgroundImage\n        {\n            get => (ImageSource)GetValue(BackgroundImageProperty);\n            set => SetValue(BackgroundImageProperty, value);\n        }\n\n        public static readonly DependencyProperty DecorationHeightProperty = DependencyProperty.Register(\n            \"DecorationHeight\", typeof(double), typeof(BlurredDecorationWindow), new PropertyMetadata(20.0));\n\n        public double DecorationHeight\n        {\n            get => (double)GetValue(DecorationHeightProperty);\n            set => SetValue(DecorationHeightProperty, value);\n        }\n\n        public static readonly DependencyProperty IconToolTipProperty = DependencyProperty.Register(\n            \"IconToolTip\", typeof(string), typeof(BlurredDecorationWindow), new PropertyMetadata(default(string)));\n\n        public string IconToolTip\n        {\n            get => (string)GetValue(IconToolTipProperty);\n            set => SetValue(IconToolTipProperty, value);\n        }\n\n        public static readonly DependencyProperty IconCommandProperty = DependencyProperty.Register(\n            \"IconCommand\", typeof(ICommand), typeof(BlurredDecorationWindow), new PropertyMetadata(default(ICommand)));\n\n        public ICommand IconCommand\n        {\n            get => (ICommand)GetValue(IconCommandProperty);\n            set => SetValue(IconCommandProperty, value);\n        }\n\n        // ReSharper restore InconsistentNaming\n        #endregion\n\n        #region Constructors\n\n        static BlurredDecorationWindow()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(BlurredDecorationWindow), new FrameworkPropertyMetadata(typeof(BlurredDecorationWindow)));\n        }\n\n        #endregion\n\n        #region Methods\n\n        public override void OnApplyTemplate()\n        {\n            base.OnApplyTemplate();\n\n            FrameworkElement decoration = GetTemplateChild(\"PART_Decoration\") as FrameworkElement;\n            if (decoration != null)\n                decoration.MouseLeftButtonDown += (sender, args) => DragMove();\n\n            Button closeButton = GetTemplateChild(\"PART_CloseButton\") as Button;\n            if (closeButton != null)\n                closeButton.Click += (sender, args) => ApplicationManager.Instance.ExitCommand.Execute(null);\n\n            Button minimizeButton = GetTemplateChild(\"PART_MinimizeButton\") as Button;\n            if (minimizeButton != null)\n                minimizeButton.Click += (sender, args) => Hide();\n\n            Button iconButton = GetTemplateChild(\"PART_IconButton\") as Button;\n            if (iconButton != null)\n                iconButton.Click += (sender, args) => IconCommand?.Execute(null);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Controls/ColorSelector.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Shapes;\nusing RGB.NET.Core;\nusing Color = RGB.NET.Core.Color;\nusing Point = System.Windows.Point;\nusing Rectangle = System.Windows.Shapes.Rectangle;\nusing WpfColor = System.Windows.Media.Color;\n\nnamespace KeyboardAudioVisualizer.Controls\n{\n    [TemplatePart(Name = \"PART_Selector\", Type = typeof(Panel))]\n    [TemplatePart(Name = \"PART_SliderAlpha\", Type = typeof(Slider))]\n    [TemplatePart(Name = \"PART_SliderRed\", Type = typeof(Slider))]\n    [TemplatePart(Name = \"PART_SliderGreen\", Type = typeof(Slider))]\n    [TemplatePart(Name = \"PART_SliderBlue\", Type = typeof(Slider))]\n    [TemplatePart(Name = \"PART_SliderHue\", Type = typeof(Slider))]\n    [TemplatePart(Name = \"PART_SliderSaturation\", Type = typeof(Slider))]\n    [TemplatePart(Name = \"PART_SliderValue\", Type = typeof(Slider))]\n    [TemplatePart(Name = \"PART_Preview\", Type = typeof(Rectangle))]\n    public class ColorSelector : Control\n    {\n        #region Properties & Fields\n\n        private bool _ignorePropertyChanged;\n        private bool _dragSelector;\n\n        private byte _a;\n        private byte _r;\n        private byte _g;\n        private byte _b;\n        private double _hue;\n        private double _saturation;\n        private double _value;\n\n        private Panel _selector;\n        private Rectangle _selectorColor;\n        private Grid _selectorGrip;\n        private Slider _sliderAlpha;\n        private Slider _sliderRed;\n        private Slider _sliderGreen;\n        private Slider _sliderBlue;\n        private Slider _sliderHue;\n        private Slider _sliderSaturation;\n        private Slider _sliderValue;\n        private Rectangle _preview;\n\n        private SolidColorBrush _previewBrush;\n        private SolidColorBrush _selectorBrush;\n        private LinearGradientBrush _alphaBrush;\n        private LinearGradientBrush _redBrush;\n        private LinearGradientBrush _greenBrush;\n        private LinearGradientBrush _blueBrush;\n        private LinearGradientBrush _hueBrush;\n        private LinearGradientBrush _saturationBrush;\n        private LinearGradientBrush _valueBrush;\n\n        #endregion\n\n        #region DependencyProperties\n\n        public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register(\n            \"SelectedColor\", typeof(Color), typeof(ColorSelector), new FrameworkPropertyMetadata(new Color(255, 0, 0),\n                                                                                                 FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,\n                                                                                                 SelectedColorChanged));\n\n        public Color SelectedColor\n        {\n            get => (Color)GetValue(SelectedColorProperty);\n            set => SetValue(SelectedColorProperty, value);\n        }\n\n        #endregion\n\n        #region Methods\n\n        public override void OnApplyTemplate()\n        {\n            if ((_selector = GetTemplateChild(\"PART_Selector\") as Panel) != null)\n            {\n                _selectorBrush = new SolidColorBrush();\n                _selectorColor = new Rectangle\n                {\n                    VerticalAlignment = VerticalAlignment.Stretch,\n                    HorizontalAlignment = HorizontalAlignment.Stretch,\n                    SnapsToDevicePixels = true,\n                    StrokeThickness = 0,\n                    Fill = _selectorBrush\n                };\n                _selector.Children.Add(_selectorColor);\n\n                Rectangle selectorWhite = new Rectangle\n                {\n                    VerticalAlignment = VerticalAlignment.Stretch,\n                    HorizontalAlignment = HorizontalAlignment.Stretch,\n                    SnapsToDevicePixels = true,\n                    StrokeThickness = 0,\n                    Fill = new LinearGradientBrush(WpfColor.FromRgb(255, 255, 255), WpfColor.FromArgb(0, 255, 255, 255), new Point(0, 0.5), new Point(1, 0.5))\n                };\n                _selector.Children.Add(selectorWhite);\n\n                Rectangle selectorBlack = new Rectangle\n                {\n                    VerticalAlignment = VerticalAlignment.Stretch,\n                    HorizontalAlignment = HorizontalAlignment.Stretch,\n                    SnapsToDevicePixels = true,\n                    StrokeThickness = 0,\n                    Fill = new LinearGradientBrush(WpfColor.FromRgb(0, 0, 0), WpfColor.FromArgb(0, 0, 0, 0), new Point(0.5, 1), new Point(0.5, 0))\n                };\n                _selector.Children.Add(selectorBlack);\n\n                _selectorGrip = new Grid\n                {\n                    VerticalAlignment = VerticalAlignment.Bottom,\n                    HorizontalAlignment = HorizontalAlignment.Left,\n                    SnapsToDevicePixels = true\n                };\n                _selectorGrip.Children.Add(new Ellipse\n                {\n                    VerticalAlignment = VerticalAlignment.Center,\n                    HorizontalAlignment = HorizontalAlignment.Center,\n                    SnapsToDevicePixels = true,\n                    Stroke = new SolidColorBrush(WpfColor.FromRgb(0, 0, 0)),\n                    StrokeThickness = 2,\n                    Fill = null,\n                    Width = 12,\n                    Height = 12\n                });\n                _selectorGrip.Children.Add(new Ellipse\n                {\n                    VerticalAlignment = VerticalAlignment.Center,\n                    HorizontalAlignment = HorizontalAlignment.Center,\n                    SnapsToDevicePixels = true,\n                    Stroke = new SolidColorBrush(WpfColor.FromRgb(255, 255, 255)),\n                    StrokeThickness = 1,\n                    Fill = null,\n                    Width = 10,\n                    Height = 10\n                });\n                _selector.Children.Add(_selectorGrip);\n\n                _selector.SizeChanged += (sender, args) => UpdateSelector();\n                _selector.MouseLeftButtonDown += (sender, args) =>\n                                                 {\n                                                     _dragSelector = true;\n                                                     UpdateSelectorValue(args.GetPosition(_selector));\n                                                 };\n                _selector.MouseEnter += (sender, args) =>\n                                        {\n                                            if (args.LeftButton == MouseButtonState.Pressed)\n                                            {\n                                                _dragSelector = true;\n                                                UpdateSelectorValue(args.GetPosition(_selector));\n                                            }\n                                        };\n                _selector.MouseLeftButtonUp += (sender, args) => _dragSelector = false;\n                _selector.MouseLeave += (sender, args) => _dragSelector = false;\n                _selector.MouseMove += (sender, args) => UpdateSelectorValue(args.GetPosition(_selector));\n                _selector.ClipToBounds = true;\n            }\n\n            if ((_sliderAlpha = GetTemplateChild(\"PART_SliderAlpha\") as Slider) != null)\n            {\n                _alphaBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),\n                                                                                         new GradientStop(new WpfColor(), 1) }));\n                _sliderAlpha.Background = _alphaBrush;\n                _sliderAlpha.ValueChanged += AChanged;\n            }\n\n            if ((_sliderRed = GetTemplateChild(\"PART_SliderRed\") as Slider) != null)\n            {\n                _redBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),\n                                                                                       new GradientStop(new WpfColor(), 1) }));\n                _sliderRed.Background = _redBrush;\n                _sliderRed.ValueChanged += RChanged;\n            }\n\n            if ((_sliderGreen = GetTemplateChild(\"PART_SliderGreen\") as Slider) != null)\n            {\n                _greenBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),\n                                                                                         new GradientStop(new WpfColor(), 1) }));\n                _sliderGreen.Background = _greenBrush;\n                _sliderGreen.ValueChanged += GChanged;\n            }\n\n            if ((_sliderBlue = GetTemplateChild(\"PART_SliderBlue\") as Slider) != null)\n            {\n                _blueBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),\n                                                                                        new GradientStop(new WpfColor(), 1) }));\n                _sliderBlue.Background = _blueBrush;\n                _sliderBlue.ValueChanged += BChanged;\n            }\n\n            if ((_sliderHue = GetTemplateChild(\"PART_SliderHue\") as Slider) != null)\n            {\n                _hueBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),\n                                                                                       new GradientStop(new WpfColor(), 1.0 / 6.0),\n                                                                                       new GradientStop(new WpfColor(), 2.0 / 6.0),\n                                                                                       new GradientStop(new WpfColor(), 3.0 / 6.0),\n                                                                                       new GradientStop(new WpfColor(), 4.0 / 6.0),\n                                                                                       new GradientStop(new WpfColor(), 5.0 / 6.0),\n                                                                                       new GradientStop(new WpfColor(), 1) }));\n                _sliderHue.Background = _hueBrush;\n                _sliderHue.ValueChanged += HueChanged;\n            }\n\n            if ((_sliderSaturation = GetTemplateChild(\"PART_SliderSaturation\") as Slider) != null)\n            {\n                _saturationBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),\n                                                                                              new GradientStop(new WpfColor(), 1) }));\n                _sliderSaturation.Background = _saturationBrush;\n                _sliderSaturation.ValueChanged += SaturationChanged;\n            }\n\n            if ((_sliderValue = GetTemplateChild(\"PART_SliderValue\") as Slider) != null)\n            {\n                _valueBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0),\n                                                                                         new GradientStop(new WpfColor(), 1) }));\n                _sliderValue.Background = _valueBrush;\n                _sliderValue.ValueChanged += ValueChanged;\n            }\n\n            if ((_preview = GetTemplateChild(\"PART_Preview\") as Rectangle) != null)\n            {\n                _previewBrush = new SolidColorBrush();\n                _preview.Fill = _previewBrush;\n            }\n\n            SetColor(SelectedColor);\n        }\n\n        private static void SelectedColorChanged(DependencyObject dependencyObject,\n                                                 DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)\n        {\n            if (!(dependencyObject is ColorSelector cs) || !(dependencyPropertyChangedEventArgs.NewValue is Color color)) return;\n            cs.SetColor(color);\n        }\n\n        private void SetColor(Color color)\n        {\n            if (_ignorePropertyChanged) return;\n\n            SetA(color);\n            SetRGB(color);\n            SetHSV(color);\n\n            UpdateSelector();\n            UpdateUIColors();\n        }\n\n        private void AChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)\n        {\n            if (_ignorePropertyChanged) return;\n\n            _a = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue);\n            Color color = new Color(_a, _r, _g, _b);\n            UpdateSelectedColor(color);\n            UpdateUIColors();\n            UpdateSelector();\n        }\n\n        private void RChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)\n        {\n            if (_ignorePropertyChanged) return;\n\n            _r = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue);\n            RGBChanged();\n        }\n\n        private void GChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)\n        {\n            if (_ignorePropertyChanged) return;\n\n            _g = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue);\n            RGBChanged();\n        }\n\n        private void BChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)\n        {\n            if (_ignorePropertyChanged) return;\n\n            _b = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue);\n            RGBChanged();\n        }\n\n        private void RGBChanged()\n        {\n            Color color = new Color(_a, _r, _g, _b);\n            UpdateSelectedColor(color);\n            SetHSV(color);\n            UpdateUIColors();\n            UpdateSelector();\n        }\n\n        private void HueChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)\n        {\n            if (_ignorePropertyChanged) return;\n\n            _hue = routedPropertyChangedEventArgs.NewValue.Clamp(0, 360);\n            HSVChanged();\n        }\n\n        private void SaturationChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)\n        {\n            if (_ignorePropertyChanged) return;\n\n            _saturation = routedPropertyChangedEventArgs.NewValue.Clamp(0, 1);\n            HSVChanged();\n        }\n\n        private void ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)\n        {\n            if (_ignorePropertyChanged) return;\n\n            _value = routedPropertyChangedEventArgs.NewValue.Clamp(0, 1);\n            HSVChanged();\n        }\n\n        private void HSVChanged()\n        {\n            Color color = HSVColor.Create(_a, _hue, _saturation, _value);\n            UpdateSelectedColor(color);\n            SetRGB(color);\n            UpdateUIColors();\n            UpdateSelector();\n        }\n\n        private void SetA(Color color)\n        {\n            _ignorePropertyChanged = true;\n\n            _a = color.GetA();\n            if (_sliderAlpha != null)\n                _sliderAlpha.Value = _a;\n\n            _ignorePropertyChanged = false;\n        }\n\n        private void SetRGB(Color color)\n        {\n            _ignorePropertyChanged = true;\n\n            _r = color.GetR();\n            if (_sliderRed != null)\n                _sliderRed.Value = _r;\n\n            _g = color.GetG();\n            if (_sliderGreen != null)\n                _sliderGreen.Value = _g;\n\n            _b = color.GetB();\n            if (_sliderBlue != null)\n                _sliderBlue.Value = _b;\n\n            _ignorePropertyChanged = false;\n        }\n\n        private void SetHSV(Color color)\n        {\n            _ignorePropertyChanged = true;\n\n            _hue = color.GetHue();\n            if (_sliderHue != null)\n                _sliderHue.Value = _hue;\n\n            _saturation = color.GetSaturation();\n            if (_sliderSaturation != null)\n                _sliderSaturation.Value = _saturation;\n\n            _value = color.GetValue();\n            if (_sliderValue != null)\n                _sliderValue.Value = _value;\n\n            _ignorePropertyChanged = false;\n        }\n\n        private void UpdateSelectedColor(Color color)\n        {\n            _ignorePropertyChanged = true;\n\n            SelectedColor = color;\n\n            _ignorePropertyChanged = false;\n        }\n\n        private void UpdateSelector()\n        {\n            if (_selector == null) return;\n\n            double selectorX = (_selector.ActualWidth * _saturation) - (_selectorGrip.ActualWidth / 2);\n            double selectorY = (_selector.ActualHeight * _value) - (_selectorGrip.ActualHeight / 2);\n            if (!double.IsNaN(selectorX) && !double.IsNaN(selectorY))\n                _selectorGrip.Margin = new Thickness(selectorX, 0, 0, selectorY);\n        }\n\n        private void UpdateSelectorValue(Point mouseLocation)\n        {\n            if (!_dragSelector) return;\n\n            double saturation = mouseLocation.X / _selector.ActualWidth;\n            double value = 1 - (mouseLocation.Y / _selector.ActualHeight);\n            if (!double.IsNaN(saturation) && !double.IsNaN(value))\n            {\n                _saturation = saturation;\n                _value = value;\n                HSVChanged();\n            }\n        }\n\n        private void UpdateUIColors()\n        {\n            Color hueColor = HSVColor.Create(_hue, 1, 1);\n\n            if (_previewBrush != null)\n                _previewBrush.Color = WpfColor.FromArgb(_a, _r, _g, _b);\n\n            if (_selectorBrush != null)\n                _selectorBrush.Color = WpfColor.FromRgb(hueColor.GetR(), hueColor.GetG(), hueColor.GetB());\n\n            if (_alphaBrush != null)\n            {\n                _alphaBrush.GradientStops[0].Color = WpfColor.FromArgb(0, _r, _g, _b);\n                _alphaBrush.GradientStops[1].Color = WpfColor.FromArgb(255, _r, _g, _b);\n            }\n\n            if (_redBrush != null)\n            {\n                _redBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, 0, _g, _b);\n                _redBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, 255, _g, _b);\n            }\n\n            if (_greenBrush != null)\n            {\n                _greenBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, _r, 0, _b);\n                _greenBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, _r, 255, _b);\n            }\n\n            if (_blueBrush != null)\n            {\n                _blueBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, _r, _g, 0);\n                _blueBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, _r, _g, 255);\n            }\n\n            if (_hueBrush != null)\n            {\n                Color referenceColor1 = HSVColor.Create(0, _saturation, _value);\n                Color referenceColor2 = HSVColor.Create(60, _saturation, _value);\n                Color referenceColor3 = HSVColor.Create(120, _saturation, _value);\n                Color referenceColor4 = HSVColor.Create(180, _saturation, _value);\n                Color referenceColor5 = HSVColor.Create(240, _saturation, _value);\n                Color referenceColor6 = HSVColor.Create(300, _saturation, _value);\n\n                _hueBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, referenceColor1.GetR(), referenceColor1.GetG(), referenceColor1.GetB());\n                _hueBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, referenceColor2.GetR(), referenceColor2.GetG(), referenceColor2.GetB());\n                _hueBrush.GradientStops[2].Color = WpfColor.FromArgb(_a, referenceColor3.GetR(), referenceColor3.GetG(), referenceColor3.GetB());\n                _hueBrush.GradientStops[3].Color = WpfColor.FromArgb(_a, referenceColor4.GetR(), referenceColor4.GetG(), referenceColor4.GetB());\n                _hueBrush.GradientStops[4].Color = WpfColor.FromArgb(_a, referenceColor5.GetR(), referenceColor5.GetG(), referenceColor5.GetB());\n                _hueBrush.GradientStops[5].Color = WpfColor.FromArgb(_a, referenceColor6.GetR(), referenceColor6.GetG(), referenceColor6.GetB());\n                _hueBrush.GradientStops[6].Color = WpfColor.FromArgb(_a, referenceColor1.GetR(), referenceColor1.GetG(), referenceColor1.GetB());\n            }\n\n            if (_saturationBrush != null)\n            {\n                Color referenceColor = HSVColor.Create(_hue, 1, _value);\n\n                _saturationBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, 255, 255, 255);\n                _saturationBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, referenceColor.GetR(), referenceColor.GetG(), referenceColor.GetB());\n            }\n\n            if (_valueBrush != null)\n            {\n                Color referenceColor = HSVColor.Create(_hue, _saturation, 1);\n\n                _valueBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, 0, 0, 0);\n                _valueBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, referenceColor.GetR(), referenceColor.GetG(), referenceColor.GetB());\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Controls/Form.cs",
    "content": "﻿using System;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace KeyboardAudioVisualizer.Controls\n{\n    public class Form : Panel\n    {\n        #region DependencyProperties\n        // ReSharper disable InconsistentNaming\n\n        public static readonly DependencyProperty RowHeightProperty = DependencyProperty.Register(\"RowHeight\", typeof(double), typeof(Form),\n            new FrameworkPropertyMetadata(24.0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));\n\n        public double RowHeight\n        {\n            get => (double)GetValue(RowHeightProperty);\n            set\n            {\n                if (value < 0) throw new ArgumentOutOfRangeException(nameof(RowHeight), \"Row height can't be negative\");\n                SetValue(RowHeightProperty, value);\n            }\n        }\n\n        public static readonly DependencyProperty LabelWidthProperty = DependencyProperty.Register(\"LabelWidth\", typeof(double), typeof(Form),\n            new FrameworkPropertyMetadata(100.0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));\n\n        public double LabelWidth\n        {\n            get => (double)GetValue(LabelWidthProperty);\n            set\n            {\n                if (value < 0) throw new ArgumentOutOfRangeException(nameof(RowHeight), \"Label width can't be negative\");\n                SetValue(LabelWidthProperty, value);\n            }\n        }\n\n        public static readonly DependencyProperty ElementSpacingProperty = DependencyProperty.Register(\"ElementSpacing\", typeof(double), typeof(Form),\n            new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));\n\n        public double ElementSpacing\n        {\n            get => (double)GetValue(ElementSpacingProperty);\n            set => SetValue(ElementSpacingProperty, value);\n        }\n\n        public static readonly DependencyProperty RowSpacingProperty = DependencyProperty.Register(\"RowSpacing\", typeof(double), typeof(Form),\n            new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));\n\n        public double RowSpacing\n        {\n            get => (double)GetValue(RowSpacingProperty);\n            set => SetValue(RowSpacingProperty, value);\n        }\n\n        // ReSharper restore InconsistentNaming\n        #endregion\n\n        #region AttachedProperties\n        // ReSharper disable InconsistentNaming\n\n        public static readonly DependencyProperty IsLabelProperty = DependencyProperty.RegisterAttached(\"IsLabel\", typeof(bool), typeof(Form),\n            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));\n\n        public static void SetIsLabel(UIElement element, bool value) => element.SetValue(IsLabelProperty, value);\n        public static bool GetIsLabel(UIElement element) => (bool)element.GetValue(IsLabelProperty);\n\n        public static readonly DependencyProperty LineBreaksProperty = DependencyProperty.RegisterAttached(\"LineBreaks\", typeof(int), typeof(Form),\n            new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));\n\n        public static void SetLineBreaks(UIElement element, int value) => element.SetValue(LineBreaksProperty, value);\n        public static int GetLineBreaks(UIElement element) => (int)element.GetValue(LineBreaksProperty);\n\n        public static readonly DependencyProperty RowSpanProperty = DependencyProperty.RegisterAttached(\"RowSpan\", typeof(int), typeof(Form),\n            new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));\n\n        public static void SetRowSpan(DependencyObject element, int value) => element.SetValue(RowSpanProperty, value);\n        public static int GetRowSpan(DependencyObject element) => (int)element.GetValue(RowSpanProperty);\n\n        public static readonly DependencyProperty FillProperty = DependencyProperty.RegisterAttached(\"Fill\", typeof(bool), typeof(Form),\n            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));\n\n        public static void SetFill(DependencyObject element, bool value) => element.SetValue(FillProperty, value);\n        public static bool GetFill(DependencyObject element) => (bool)element.GetValue(FillProperty);\n\n        // ReSharper restore InconsistentNaming\n        #endregion\n\n        #region Methods\n\n        protected override Size MeasureOverride(Size availableSize)\n        {\n            if (InternalChildren.Count == 0) return new Size(0, 0);\n\n            FormLayout layout = new FormLayout(RowHeight, LabelWidth, ElementSpacing, RowSpacing);\n\n            foreach (UIElement child in InternalChildren)\n            {\n                child.Measure(availableSize);\n                layout.AddElement(child, 0);\n            }\n\n            return new Size(layout.Width, layout.Height);\n        }\n\n        protected override Size ArrangeOverride(Size finalSize)\n        {\n            if (InternalChildren.Count == 0) return new Size(0, 0);\n\n            FormLayout layout = new FormLayout(RowHeight, LabelWidth, ElementSpacing, RowSpacing);\n\n            foreach (UIElement child in InternalChildren)\n                child.Arrange(layout.AddElement(child, finalSize.Width));\n\n            return new Size(finalSize.Width, layout.Height);\n        }\n\n        #endregion\n\n        #region Data\n\n        private class FormLayout\n        {\n            #region Properties & Fields\n\n            private readonly double _rowHeight;\n            private readonly double _labelWidth;\n            private readonly double _elementSpacing;\n            private readonly double _rowSpacing;\n\n            private double _currentRowWidth;\n\n            private int _newRows = 0;\n            private int _rows = -1;\n            private double _currentMaxWidth;\n            public double Width => Math.Max((Math.Max(_currentMaxWidth, _currentRowWidth) - _elementSpacing), 0);\n            public double Height => ((_rows + 1) * _rowHeight) + (_rows * _rowSpacing);\n\n            #endregion\n\n            #region Constructors\n\n            public FormLayout(double rowHeight, double labelWidth, double elementSpacing, double rowSpacing)\n            {\n                this._rowHeight = rowHeight;\n                this._labelWidth = labelWidth;\n                this._elementSpacing = elementSpacing;\n                this._rowSpacing = rowSpacing;\n            }\n\n            #endregion\n\n            #region Methods\n\n            public Rect AddElement(UIElement element, double targetWidth)\n            {\n                bool isLabel = GetIsLabel(element);\n                int lineBreaks = GetLineBreaks(element);\n                int rowSpan = GetRowSpan(element);\n\n                double elementWidth = isLabel ? _labelWidth : element.DesiredSize.Width;\n                double height = _rowHeight;\n\n                if (_newRows > 0)\n                {\n                    AddLineBreaks(_newRows);\n                    _newRows = 0;\n                }\n\n                if (lineBreaks > 0) AddLineBreaks(lineBreaks);\n                else if (isLabel) AddLineBreaks(1);\n                else if (_rows < 0) _rows = 0;\n\n                if (!isLabel && (_currentRowWidth < _labelWidth))\n                    _currentRowWidth = _labelWidth + _elementSpacing;\n\n                if (rowSpan > 1)\n                {\n                    height = (rowSpan * _rowHeight) + ((rowSpan - 1) * _rowSpacing);\n                    _newRows = Math.Max(_newRows, rowSpan - 1);\n                }\n\n                if (element is FrameworkElement fe)\n                    fe.MaxHeight = height;\n\n                double width = elementWidth;\n                if ((targetWidth >= 1) && GetFill(element))\n                    width = targetWidth - _currentRowWidth;\n\n                Rect rect = new Rect(new Point(_currentRowWidth, (_rows * _rowHeight) + (_rows * _rowSpacing)), new Size(width, height));\n\n                _currentRowWidth += width + _elementSpacing;\n\n                return rect;\n            }\n\n            private void AddLineBreaks(int count)\n            {\n                if (count <= 0) return;\n\n                _currentMaxWidth = Math.Max(_currentMaxWidth, _currentRowWidth);\n\n                _currentRowWidth = 0;\n                _rows += count;\n            }\n\n            #endregion\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Controls/GradientEditor.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing RGB.NET.Brushes.Gradients;\nusing RGB.NET.Core;\nusing Color = System.Windows.Media.Color;\nusing Point = System.Windows.Point;\nusing Size = System.Windows.Size;\nusing Rectangle = System.Windows.Shapes.Rectangle;\nusing GradientStop = RGB.NET.Brushes.Gradients.GradientStop;\n\nnamespace KeyboardAudioVisualizer.Controls\n{\n    [TemplatePart(Name = \"PART_Gradient\", Type = typeof(Canvas))]\n    [TemplatePart(Name = \"PART_Stops\", Type = typeof(Canvas))]\n    public class GradientEditor : Control\n    {\n        #region Properties & Fields\n\n        private Canvas _gradientContainer;\n        private Canvas _stopContainer;\n        private readonly List<Rectangle> _previewRectangles = new List<Rectangle>();\n        private readonly Dictionary<GradientStop, ContentControl> _stops = new Dictionary<GradientStop, ContentControl>();\n        private ContentControl _draggingStop;\n        private AdornerLayer _adornerLayer;\n        private ColorPickerAdorner _adorner;\n        private Window _window;\n\n        #endregion\n\n        #region DepdencyProperties\n\n        public static readonly DependencyProperty GradientProperty = DependencyProperty.Register(\n            \"Gradient\", typeof(LinearGradient), typeof(GradientEditor), new FrameworkPropertyMetadata(null,\n                                                                                                      FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,\n                                                                                                      OnGradientChanged));\n\n        public LinearGradient Gradient\n        {\n            get => (LinearGradient)GetValue(GradientProperty);\n            set => SetValue(GradientProperty, value);\n        }\n\n        public static readonly DependencyProperty GradientStopStyleProperty = DependencyProperty.Register(\n            \"GradientStopStyle\", typeof(Style), typeof(GradientEditor), new PropertyMetadata(default(Style)));\n\n        public Style GradientStopStyle\n        {\n            get => (Style)GetValue(GradientStopStyleProperty);\n            set => SetValue(GradientStopStyleProperty, value);\n        }\n\n        public static readonly DependencyProperty SelectedStopProperty = DependencyProperty.Register(\n            \"SelectedStop\", typeof(GradientStop), typeof(GradientEditor), new PropertyMetadata(default(GradientStop), SelectedStopChanged));\n\n        public GradientStop SelectedStop\n        {\n            get => (GradientStop)GetValue(SelectedStopProperty);\n            set => SetValue(SelectedStopProperty, value);\n        }\n\n        public static readonly DependencyProperty ColorSelectorTemplateProperty = DependencyProperty.Register(\n            \"ColorSelectorTemplate\", typeof(DataTemplate), typeof(GradientEditor), new PropertyMetadata(default(DataTemplate)));\n\n        public DataTemplate ColorSelectorTemplate\n        {\n            get => (DataTemplate)GetValue(ColorSelectorTemplateProperty);\n            set => SetValue(ColorSelectorTemplateProperty, value);\n        }\n\n        public static readonly DependencyProperty CanAddOrDeleteStopsProperty = DependencyProperty.Register(\n            \"CanAddOrDeleteStops\", typeof(bool), typeof(GradientEditor), new PropertyMetadata(true));\n\n        public bool CanAddOrDeleteStops\n        {\n            get => (bool)GetValue(CanAddOrDeleteStopsProperty);\n            set => SetValue(CanAddOrDeleteStopsProperty, value);\n        }\n\n        #endregion\n\n        #region AttachedProperties\n\n        public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.RegisterAttached(\n            \"IsSelected\", typeof(bool), typeof(GradientEditor), new PropertyMetadata(default(bool)));\n\n        public static void SetIsSelected(DependencyObject element, bool value) => element.SetValue(IsSelectedProperty, value);\n        public static bool GetIsSelected(DependencyObject element) => (bool)element.GetValue(IsSelectedProperty);\n\n        #endregion\n\n        #region Constructors\n\n        public GradientEditor()\n        {\n            if (Gradient == null)\n                Gradient = new LinearGradient();\n        }\n\n        #endregion\n\n        #region Methods\n\n        public override void OnApplyTemplate()\n        {\n            if ((_gradientContainer = GetTemplateChild(\"PART_Gradient\") as Canvas) != null)\n            {\n                _gradientContainer.SizeChanged += (sender, args) => UpdateGradientPreview();\n                _gradientContainer.MouseDown += GradientContainerOnMouseDown;\n            }\n\n            if ((_stopContainer = GetTemplateChild(\"PART_Stops\") as Canvas) != null)\n                _stopContainer.SizeChanged += (sender, args) => UpdateGradientStops();\n\n            _adornerLayer = AdornerLayer.GetAdornerLayer(this);\n            _window = Window.GetWindow(this);\n            if (_window != null)\n            {\n                _window.PreviewMouseDown += WindowMouseDown;\n                _window.PreviewKeyDown += (sender, args) =>\n                {\n                    if (args.Key == Key.Escape)\n                        SelectedStop = null;\n                };\n            }\n\n            UpdateGradientPreview();\n            UpdateGradientStops();\n        }\n\n        private void UpdateGradientPreview()\n        {\n            if ((_gradientContainer == null) || (Gradient == null)) return;\n\n            List<GradientStop> gradientStops = Gradient.GradientStops.OrderBy(x => x.Offset).ToList();\n            if (gradientStops.Count == 0)\n                UpdatePreviewRectangleCount(gradientStops.Count);\n            else if (gradientStops.Count == 1)\n            {\n                UpdatePreviewRectangleCount(gradientStops.Count);\n                GradientStop firstStop = gradientStops[0];\n                UpdatePreviewRectangle(_previewRectangles[0], _gradientContainer.ActualWidth, _gradientContainer.ActualHeight, 0, 1, firstStop.Color, firstStop.Color);\n            }\n            else\n            {\n                UpdatePreviewRectangleCount(gradientStops.Count + 1);\n\n                GradientStop firstStop = gradientStops[0];\n                UpdatePreviewRectangle(_previewRectangles[0], _gradientContainer.ActualWidth, _gradientContainer.ActualHeight, 0, firstStop.Offset, firstStop.Color, firstStop.Color);\n                for (int i = 0; i < (gradientStops.Count - 1); i++)\n                {\n                    GradientStop stop = gradientStops[i];\n                    GradientStop nextStop = gradientStops[i + 1];\n                    Rectangle rect = _previewRectangles[i + 1];\n                    UpdatePreviewRectangle(rect, _gradientContainer.ActualWidth, _gradientContainer.ActualHeight, stop.Offset, nextStop.Offset, stop.Color, nextStop.Color);\n                }\n                GradientStop lastStop = gradientStops[gradientStops.Count - 1];\n                UpdatePreviewRectangle(_previewRectangles[_previewRectangles.Count - 1], _gradientContainer.ActualWidth, _gradientContainer.ActualHeight, lastStop.Offset, 1, lastStop.Color, lastStop.Color);\n            }\n        }\n\n        private void UpdatePreviewRectangle(Rectangle rect, double referenceWidth, double referenceHeight, double from, double to,\n                                            RGB.NET.Core.Color startColor, RGB.NET.Core.Color endColor)\n        {\n            rect.Fill = new LinearGradientBrush(Color.FromArgb(startColor.GetA(), startColor.GetR(), startColor.GetG(), startColor.GetB()),\n                                                Color.FromArgb(endColor.GetA(), endColor.GetR(), endColor.GetG(), endColor.GetB()),\n                                                new Point(0, 0.5), new Point(1, 0.5));\n\n            //DarthAffe 09.02.2018: Forced rounding to prevent render issues on resize\n            Canvas.SetLeft(rect, Math.Floor(referenceWidth * from.Clamp(0, 1)));\n            rect.Width = Math.Ceiling(referenceWidth * (to.Clamp(0, 1) - from.Clamp(0, 1)));\n\n            Canvas.SetTop(rect, 0);\n            rect.Height = referenceHeight;\n        }\n\n        private void UpdatePreviewRectangleCount(int gradientCount)\n        {\n            int countDiff = gradientCount - _previewRectangles.Count;\n            if (countDiff > 0)\n                for (int i = 0; i < countDiff; i++)\n                {\n                    Rectangle rect = new Rectangle { VerticalAlignment = VerticalAlignment.Stretch };\n                    _previewRectangles.Add(rect);\n                    _gradientContainer.Children.Add(rect);\n                }\n\n            if (countDiff < 0)\n                for (int i = 0; i < Math.Abs(countDiff); i++)\n                {\n                    int index = _previewRectangles.Count - i - 1;\n                    Rectangle rect = _previewRectangles[index];\n                    _previewRectangles.RemoveAt(index);\n                    _gradientContainer.Children.Remove(rect);\n                }\n        }\n\n        private void UpdateGradientStops()\n        {\n            if (Gradient == null) return;\n\n            List<GradientStop> gradientStops = Gradient.GradientStops.OrderBy(x => x.Offset).ToList();\n            UpdateGradientStopsCount(gradientStops);\n            foreach (GradientStop stop in gradientStops)\n                UpdateGradientStop(_stops[stop], _stopContainer.ActualWidth, _stopContainer.ActualHeight, stop);\n        }\n\n        private void UpdateGradientStop(ContentControl control, double referenceWidth, double referenceHeight, GradientStop stop)\n        {\n            control.Background = new SolidColorBrush(Color.FromArgb(stop.Color.GetA(), stop.Color.GetR(), stop.Color.GetG(), stop.Color.GetB()));\n\n            Canvas.SetLeft(control, (referenceWidth * stop.Offset.Clamp(0, 1)) - (control.Width / 2.0));\n\n            Canvas.SetTop(control, 0);\n            control.Height = referenceHeight;\n        }\n\n        private void UpdateGradientStopsCount(List<GradientStop> gradientStops)\n        {\n            foreach (GradientStop stop in gradientStops)\n            {\n                if (!_stops.ContainsKey(stop))\n                {\n                    ContentControl control = new ContentControl\n                    {\n                        VerticalAlignment = VerticalAlignment.Stretch,\n                        Style = GradientStopStyle,\n                        Content = stop\n                    };\n                    control.MouseDown += GradientStopOnMouseDown;\n                    _stops.Add(stop, control);\n                    _stopContainer.Children.Add(control);\n                }\n            }\n\n            List<GradientStop> stopsToRemove = new List<GradientStop>();\n            foreach (KeyValuePair<GradientStop, ContentControl> stopPair in _stops)\n                if (!gradientStops.Contains(stopPair.Key))\n                {\n                    ContentControl control = stopPair.Value;\n                    control.MouseDown -= GradientStopOnMouseDown;\n                    stopsToRemove.Add(stopPair.Key);\n                    _stopContainer.Children.Remove(control);\n                }\n\n            foreach (GradientStop stop in stopsToRemove)\n                _stops.Remove(stop);\n        }\n\n        private void AttachGradient(AbstractGradient gradient) => gradient.GradientChanged += GradientChanged;\n        private void DetachGradient(AbstractGradient gradient) => gradient.GradientChanged -= GradientChanged;\n\n        private void GradientChanged(object o, EventArgs eventArgs)\n        {\n            UpdateGradientPreview();\n            UpdateGradientStops();\n        }\n\n        private static void OnGradientChanged(DependencyObject dependencyObject,\n                                              DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)\n        {\n            if (!(dependencyObject is GradientEditor ge)) return;\n\n            if (dependencyPropertyChangedEventArgs.OldValue is AbstractGradient oldGradient)\n                ge.DetachGradient(oldGradient);\n\n            if (dependencyPropertyChangedEventArgs.NewValue is AbstractGradient newGradient)\n                ge.AttachGradient(newGradient);\n        }\n\n        private void GradientContainerOnMouseDown(object o, MouseButtonEventArgs mouseButtonEventArgs)\n        {\n            if ((mouseButtonEventArgs.ChangedButton != MouseButton.Left) || (Gradient == null) || !CanAddOrDeleteStops) return;\n\n            double offset = mouseButtonEventArgs.GetPosition(_gradientContainer).X / _gradientContainer.ActualWidth;\n            RGB.NET.Core.Color color = Gradient.GetColor(offset);\n            GradientStop newStop = new GradientStop(offset, color);\n            Gradient.GradientStops.Add(newStop);\n            SelectedStop = newStop;\n        }\n\n        private void GradientStopOnMouseDown(object o, MouseButtonEventArgs mouseButtonEventArgs)\n        {\n            if (!((o as ContentControl)?.Content is GradientStop stop) || (Gradient == null)) return;\n\n            if (mouseButtonEventArgs.ChangedButton == MouseButton.Right)\n            {\n                if (CanAddOrDeleteStops)\n                    Gradient.GradientStops.Remove(stop);\n            }\n            else if (mouseButtonEventArgs.ChangedButton == MouseButton.Left)\n            {\n                SelectedStop = stop;\n                _draggingStop = (ContentControl)o;\n            }\n        }\n\n        protected override void OnMouseMove(MouseEventArgs e)\n        {\n            base.OnMouseMove(e);\n\n            if (_draggingStop?.Content is GradientStop stop)\n            {\n                double location = e.GetPosition(_gradientContainer).X;\n                stop.Offset = (location / _gradientContainer.ActualWidth).Clamp(0, 1);\n            }\n        }\n\n        protected override void OnMouseLeave(MouseEventArgs e)\n        {\n            base.OnMouseLeave(e);\n\n            _draggingStop = null;\n        }\n\n        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)\n        {\n            base.OnMouseLeftButtonUp(e);\n\n            _draggingStop = null;\n        }\n\n        private static void SelectedStopChanged(DependencyObject dependencyObject,\n                                                DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)\n        {\n            if (!(dependencyObject is GradientEditor gradientEditor)) return;\n\n            if (gradientEditor._adorner != null)\n                gradientEditor._adornerLayer.Remove(gradientEditor._adorner);\n\n            if (dependencyPropertyChangedEventArgs.OldValue is GradientStop oldStop)\n            {\n                if (gradientEditor._stops.TryGetValue(oldStop, out ContentControl oldcontrol))\n                    SetIsSelected(oldcontrol, false);\n            }\n\n            if (dependencyPropertyChangedEventArgs.NewValue is GradientStop stop)\n            {\n                ContentControl stopContainer = gradientEditor._stops[stop];\n                SetIsSelected(stopContainer, true);\n\n                if (gradientEditor._adornerLayer != null)\n                {\n                    ContentControl contentControl = new ContentControl\n                    {\n                        ContentTemplate = gradientEditor.ColorSelectorTemplate,\n                        Content = stop\n                    };\n\n                    ColorPickerAdorner adorner = new ColorPickerAdorner(stopContainer, contentControl);\n                    gradientEditor._adorner = adorner;\n                    gradientEditor._adornerLayer.Add(adorner);\n                }\n            }\n        }\n\n        private void WindowMouseDown(object o, MouseButtonEventArgs mouseButtonEventArgs)\n        {\n            if ((_adorner != null) && (VisualTreeHelper.HitTest(_adorner, mouseButtonEventArgs.GetPosition(_adorner)) == null))\n                SelectedStop = null;\n        }\n\n        #endregion\n    }\n\n    public class ColorPickerAdorner : Adorner\n    {\n        #region Properties & Fields\n\n        private readonly VisualCollection _visualChildren;\n        private readonly FrameworkElement _colorSelector;\n        protected override int VisualChildrenCount => 1;\n        protected override Visual GetVisualChild(int index) => _colorSelector;\n\n        #endregion\n\n        #region Constructors\n\n        public ColorPickerAdorner(UIElement adornedElement, FrameworkElement colorSelector)\n            : base(adornedElement)\n        {\n            this._colorSelector = colorSelector;\n\n            _visualChildren = new VisualCollection(this) { colorSelector };\n        }\n\n        #endregion\n\n        #region Methods\n\n        protected override Size ArrangeOverride(Size finalSize)\n        {\n            Window referenceWindow = Window.GetWindow(AdornedElement);\n            Point referenceLocation = AdornedElement.TranslatePoint(new Point(0, 0), referenceWindow);\n\n            double referenceWidth = ((FrameworkElement)AdornedElement).ActualWidth / 2.0;\n            double referenceHeight = ((FrameworkElement)AdornedElement).Height;\n            double referenceX = referenceLocation.X + referenceWidth;\n            double halfWidth = finalSize.Width / 2.0;\n            double maxOffset = referenceWindow.Width - halfWidth;\n\n            double offset = (referenceX < halfWidth ? referenceX\n                                                    : (((referenceX + (referenceWidth * 2)) > maxOffset)\n                                                        ? halfWidth - ((maxOffset - referenceX) - (referenceWidth * 2))\n                                                        : halfWidth));\n\n            _colorSelector.Arrange(new Rect(new Point(referenceWidth - offset, referenceHeight), finalSize));\n            return _colorSelector.RenderSize;\n        }\n\n        protected override Size MeasureOverride(Size constraint)\n        {\n            _colorSelector.Measure(constraint);\n            return _colorSelector.DesiredSize;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Controls/ImageButton.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\n\nnamespace KeyboardAudioVisualizer.Controls\n{\n    public class ImageButton : Button\n    {\n        #region Properties & Fields\n        // ReSharper disable InconsistentNaming\n\n        public static readonly DependencyProperty ImageProperty = DependencyProperty.Register(\n            \"Image\", typeof(ImageSource), typeof(ImageButton), new PropertyMetadata(default(ImageSource)));\n\n        public ImageSource Image\n        {\n            get => (ImageSource)GetValue(ImageProperty);\n            set => SetValue(ImageProperty, value);\n        }\n\n        public static readonly DependencyProperty HoverImageProperty = DependencyProperty.Register(\n            \"HoverImage\", typeof(ImageSource), typeof(ImageButton), new PropertyMetadata(default(ImageSource)));\n\n        public ImageSource HoverImage\n        {\n            get => (ImageSource)GetValue(HoverImageProperty);\n            set => SetValue(HoverImageProperty, value);\n        }\n\n        public static readonly DependencyProperty PressedImageProperty = DependencyProperty.Register(\n            \"PressedImage\", typeof(ImageSource), typeof(ImageButton), new PropertyMetadata(default(ImageSource)));\n\n        public ImageSource PressedImage\n        {\n            get => (ImageSource)GetValue(PressedImageProperty);\n            set => SetValue(PressedImageProperty, value);\n        }\n\n        // ReSharper restore InconsistentNaming\n        #endregion\n\n        #region Constructors\n\n        static ImageButton()\n        {\n            DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Converter/BoolToVisibilityConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Data;\n\nnamespace KeyboardAudioVisualizer.Converter\n{\n    [ValueConversion(typeof(bool), typeof(Visibility))]\n    public class BoolToVisibilityConverter : IValueConverter\n    {\n        #region Methods\n\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n            => (value as bool?) == true ? Visibility.Visible\n            : (string.Equals(parameter?.ToString(), \"true\", StringComparison.OrdinalIgnoreCase) ? Visibility.Hidden : Visibility.Collapsed);\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => value as Visibility? == Visibility.Visible;\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Converter/EqualizerBandsToPointsConverter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Globalization;\nusing System.Linq;\nusing System.Windows;\nusing System.Windows.Data;\nusing System.Windows.Media;\nusing KeyboardAudioVisualizer.AudioProcessing.Equalizer;\n\nnamespace KeyboardAudioVisualizer.Converter\n{\n    public class EqualizerBandsToPointsConverter : IMultiValueConverter\n    {\n        #region Methods\n\n        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)\n        {\n            PointCollection points = new PointCollection();\n\n            if ((values.Length != 4) || (parameter == DependencyProperty.UnsetValue)\n                || (values[0] == null) || (values[0] == DependencyProperty.UnsetValue)\n                || (values[1] == null) || (values[1] == DependencyProperty.UnsetValue) //HACK DarthAffe 13.08.2017: I need this only to update the binding\n                || (values[2] == null) || (values[2] == DependencyProperty.UnsetValue)\n                || (values[3] == null) || (values[3] == DependencyProperty.UnsetValue))\n                return points;\n\n            IEqualizer equalizer = (IEqualizer)values[0];\n            double width = (double)values[2];\n            double height = (double)values[3];\n            int valueCount = int.Parse(parameter.ToString());\n            double halfHeight = height / 2.0;\n\n            List<(float offset, float value)> pointValues = equalizer.Bands.Select(b => (b.Offset, b.Value)).ToList();\n            float[] calculatedValues = equalizer.CalculateValues(valueCount);\n            for (int i = 0; i < calculatedValues.Length; i++)\n                pointValues.Add(((float)i / calculatedValues.Length, calculatedValues[i]));\n\n            foreach ((float offset, float value) in pointValues.OrderBy(x => x.offset))\n                points.Add(new Point(offset * width, GetPosY(value, halfHeight)));\n\n            return points;\n        }\n\n        private double GetPosY(float offset, double halfHeight)\n        {\n            if (offset < 0)\n                return halfHeight + (-offset * halfHeight);\n\n            if (offset > 0)\n                return halfHeight - (offset * halfHeight);\n\n            return halfHeight;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Converter/EqualsToBoolConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows.Data;\n\nnamespace KeyboardAudioVisualizer.Converter\n{\n    [ValueConversion(typeof(object), typeof(bool))]\n    public class EqualsToBoolConverter : IValueConverter\n    {\n        #region Methods\n\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => Equals(value, parameter);\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Converter/OffsetToPosXConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Data;\n\nnamespace KeyboardAudioVisualizer.Converter\n{\n    public class OffsetToPosXConverter : IMultiValueConverter\n    {\n        #region Methods\n\n        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)\n        {\n            if ((values.Length != 2) || (parameter == DependencyProperty.UnsetValue)\n                || (values[0] == null) || (values[0] == DependencyProperty.UnsetValue)\n                || (values[1] == null) || (values[1] == DependencyProperty.UnsetValue))\n                return 0;\n\n            float offset = (float)values[0];\n            double width = (double)values[1];\n            double correction = double.Parse(parameter.ToString());\n\n            return (offset * width) - correction;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Converter/ValueToPosYConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Data;\n\nnamespace KeyboardAudioVisualizer.Converter\n{\n    public class ValueToPosYConverter : IMultiValueConverter\n    {\n        #region Methods\n\n        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)\n        {\n            if ((values.Length != 2) || (parameter == DependencyProperty.UnsetValue)\n                || (values[0] == null) || (values[0] == DependencyProperty.UnsetValue)\n                || (values[1] == null) || (values[1] == DependencyProperty.UnsetValue))\n                return 0;\n\n            float offset = (float)values[0];\n            double height = (double)values[1];\n            double correction = double.Parse(parameter.ToString());\n\n            double halfHeight = height / 2.0;\n            \n            if (offset < 0)\n                return (halfHeight + (-offset * halfHeight)) - correction;\n\n            if (offset > 0)\n                return (halfHeight - (offset * halfHeight)) - correction;\n\n            return halfHeight - correction;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Converter/VisualizationProviderDisplayNameConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows.Data;\nusing KeyboardAudioVisualizer.Attributes;\nusing KeyboardAudioVisualizer.Helper;\nusing VisualizationType = KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider.VisualizationType;\n\nnamespace KeyboardAudioVisualizer.Converter\n{\n    [ValueConversion(typeof(VisualizationType), typeof(string))]\n    public class VisualizationProviderDisplayNameConverter : IValueConverter\n    {\n        #region Methods\n\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (!(value is VisualizationType visualizationType)) return null;\n            return visualizationType.GetAttribute<DisplayNameAttribute>()?.DisplayName ?? visualizationType.ToString();\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Converter/VisualizationToLastChildFillConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows.Data;\nusing KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing KeyboardAudioVisualizer.UI.Visualization;\n\nnamespace KeyboardAudioVisualizer.Converter\n{\n    [ValueConversion(typeof(IVisualizationProvider), typeof(bool))]\n    public class VisualizationToLastChildFillConverter : IValueConverter\n    {\n        #region Methods\n\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            return value is FrequencyBarsVisualizationProvider;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Converter/VisualizationTypeSelectableConverter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Windows.Data;\nusing KeyboardAudioVisualizer.Attributes;\nusing KeyboardAudioVisualizer.Helper;\nusing RGB.NET.Core;\nusing VisualizationType = KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider.VisualizationType;\n\nnamespace KeyboardAudioVisualizer.Converter\n{\n    [ValueConversion(typeof(IEnumerable<VisualizationType>), typeof(IEnumerable<VisualizationType>))]\n    public class VisualizationTypeSelectableConverter : IValueConverter\n    {\n        #region Methods\n\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (!(value is IEnumerable<VisualizationType> visualizationProviders) || !(parameter is RGBDeviceType targetDevice)) return new List<VisualizationType>();\n            return visualizationProviders.Where(x => x.GetAttribute<VisualizerForAttribute>()?.VisualizerFor.HasFlag(targetDevice) ?? true).ToList();\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Decorators/BeatDecorator.cs",
    "content": "﻿using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing RGB.NET.Core;\n\nnamespace KeyboardAudioVisualizer.Decorators\n{\n    public class BeatDecorator : AbstractUpdateAwareDecorator, IBrushDecorator\n    {\n        #region Properties & Fields\n\n        private readonly IVisualizationProvider _visualizationProvider;\n\n        #endregion\n\n        #region Constructors\n\n        public BeatDecorator(IVisualizationProvider visualizationProvider)\n        {\n            this._visualizationProvider = visualizationProvider;\n        }\n\n        #endregion\n\n        #region Methods\n\n        protected override void Update(double deltaTime) => _visualizationProvider.Update();\n\n        #endregion\n\n        public Color ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, Color color) => color.SetA(color.A * _visualizationProvider.VisualizationData[0]);\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Decorators/FrequencyBarsDecorator.cs",
    "content": "﻿using System;\nusing KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing RGB.NET.Core;\nusing Color = RGB.NET.Core.Color;\nusing Rectangle = RGB.NET.Core.Rectangle;\n\nnamespace KeyboardAudioVisualizer.Decorators\n{\n    public class FrequencyBarsDecorator : AbstractUpdateAwareDecorator, IBrushDecorator\n    {\n        #region Properties & Fields\n\n        private readonly IVisualizationProvider _visualizationProvider;\n\n        #endregion\n\n        #region Constructors\n\n        public FrequencyBarsDecorator(IVisualizationProvider visualizationProvider)\n        {\n            this._visualizationProvider = visualizationProvider;\n        }\n\n        #endregion\n\n        #region Methods\n\n        protected override void Update(double deltaTime) => _visualizationProvider.Update();\n\n        public Color ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, Color color)\n        {\n            int barSampleIndex = Math.Min(_visualizationProvider.VisualizationData.Length, (int)Math.Floor(_visualizationProvider.VisualizationData.Length * (renderTarget.Point.X / (rectangle.Location.X + rectangle.Size.Width))));\n            double curBarHeight = 1.0 - Math.Max(0f, _visualizationProvider.VisualizationData[barSampleIndex]);\n            double verticalPos = (renderTarget.Point.Y / rectangle.Size.Height);\n\n            return curBarHeight > verticalPos ? color.SetA(0) : color;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Decorators/LevelBarDecorator.cs",
    "content": "﻿using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing RGB.NET.Brushes.Gradients;\nusing RGB.NET.Core;\n\nnamespace KeyboardAudioVisualizer.Decorators\n{\n    public class LevelBarDecorator : AbstractUpdateAwareDecorator, IBrushDecorator\n    {\n        #region Properties & Fields\n\n        private readonly IVisualizationProvider _visualizationProvider;\n        public LevelBarDirection Direction { get; set; }\n        public int DataIndex { get; set; }\n        public LinearGradient Gradient { get; set; }\n\n        #endregion\n\n        #region Constructors\n\n        public LevelBarDecorator(IVisualizationProvider visualizationProvider, LevelBarDirection direction, int dataIndex, LinearGradient gradient)\n        {\n            this._visualizationProvider = visualizationProvider;\n            this.Direction = direction;\n            this.DataIndex = dataIndex;\n            this.Gradient = gradient;\n        }\n\n        #endregion\n\n        #region Methods\n\n        protected override void Update(double deltaTime) => _visualizationProvider.Update();\n\n        public Color ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, Color color)\n        {\n            double offset = CalculateOffset(rectangle, renderTarget);\n\n            if (Direction == LevelBarDirection.Horizontal)\n            {\n                \n                if (offset < 0)\n                {\n                    offset = (-offset * 2);\n                    if (offset >= _visualizationProvider.VisualizationData[0])\n                        return color.SetA(0);\n                    else\n                        return Gradient.GetColor(offset);\n                }\n                else\n                {\n                    offset *= 2;\n                    if (offset >= _visualizationProvider.VisualizationData[1])\n                        return color.SetA(0);\n                    else\n                        return Gradient.GetColor(offset);\n                }\n            }\n            else\n            {\n                if (offset >= _visualizationProvider.VisualizationData[DataIndex])\n                    return color.SetA(0);\n            }\n\n            return color;\n        }\n\n        private double CalculateOffset(Rectangle rectangle, BrushRenderTarget renderTarget)\n        {\n            switch (Direction)\n            {\n                case LevelBarDirection.Left:\n                    return (rectangle.Size.Width - renderTarget.Rectangle.Center.X) / rectangle.Size.Width;\n\n                case LevelBarDirection.Right:\n                    return renderTarget.Rectangle.Center.X / rectangle.Size.Width;\n\n                case LevelBarDirection.Top:\n                    return (rectangle.Size.Height - renderTarget.Rectangle.Center.Y) / rectangle.Size.Height;\n\n                case LevelBarDirection.Bottom:\n                    return renderTarget.Rectangle.Center.Y / rectangle.Size.Height;\n\n                case LevelBarDirection.Horizontal:\n                    return (renderTarget.Rectangle.Center.X / rectangle.Size.Width) - 0.5;\n\n                default:\n                    return -1;\n            }\n        }\n\n        #endregion\n    }\n\n    #region Data\n\n    public enum LevelBarDirection\n    {\n        Left, Right, Top, Bottom,\n\n        //HACK DarthAffe 12.09.2017: Just a bad workaround ...\n        Horizontal\n    }\n\n    #endregion\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Helper/ActionCommand.cs",
    "content": "﻿using System;\nusing System.Windows.Input;\n\nnamespace KeyboardAudioVisualizer.Helper\n{\n    public class ActionCommand : ICommand\n    {\n        #region Properties & Fields\n\n        private readonly Func<bool> _canExecute;\n        private readonly Action _command;\n\n        #endregion\n\n        #region Events\n\n        public event EventHandler CanExecuteChanged;\n\n        #endregion\n\n        #region Constructors\n\n        public ActionCommand(Action command, Func<bool> canExecute = null)\n        {\n            this._command = command;\n            this._canExecute = canExecute;\n        }\n\n        #endregion\n\n        #region Methods\n\n        public bool CanExecute(object parameter)\n        {\n            return _canExecute?.Invoke() ?? true;\n        }\n\n        public void Execute(object parameter)\n        {\n            _command?.Invoke();\n        }\n\n        public void RaiseCanExecuteChanged()\n        {\n            CanExecuteChanged?.Invoke(this, new EventArgs());\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Helper/EnumExtension.cs",
    "content": "﻿using System;\nusing System.Linq;\n\nnamespace KeyboardAudioVisualizer.Helper\n{\n    public static class EnumExtension\n    {\n        #region Methods\n\n        public static T GetAttribute<T>(this Enum e)\n            where T : Attribute => e.GetType().GetMember(e.ToString()).FirstOrDefault()?.GetCustomAttributes(typeof(T), false).FirstOrDefault() as T;\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Helper/ExceptionExtension.cs",
    "content": "﻿using System;\n\nnamespace KeyboardAudioVisualizer.Helper\n{\n    public static class ExceptionExtension\n    {\n        #region Methods\n\n        public static string GetFullMessage(this Exception ex, string message = \"\")\n        {\n            if (ex == null) return string.Empty;\n\n            message += ex.Message;\n\n            if (ex.InnerException != null)\n                message += \"\\r\\nInnerException: \" + GetFullMessage(ex.InnerException);\n\n            return message;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Helper/FrequencyHelper.cs",
    "content": "﻿using System;\n\nnamespace KeyboardAudioVisualizer.Helper\n{\n    public static class FrequencyHelper\n    {\n        #region Constants\n\n        private const int SAMPLE_RATE = 44100; // Right now this is fixed\n\n        #endregion\n\n        #region Methods\n\n        public static float GetFrequencyOfIndex(int index, int count) => index * ((float)SAMPLE_RATE / count);\n        public static int GetIndexOfFrequency(float frequency, int count) => (int)(frequency / ((float)SAMPLE_RATE / count));\n\n        public static double CalculatedBAForFrequency(float frequency)\n        {\n            double ra = (Math.Pow(12194, 2) * Math.Pow(frequency, 4)) / ((Math.Pow(frequency, 2) + Math.Pow(20.6, 2)) * Math.Sqrt((Math.Pow(frequency, 2) + Math.Pow(107.7, 2)) * (Math.Pow(frequency, 2) + Math.Pow(737.9, 2))) * (Math.Pow(frequency, 2) + Math.Pow(12194, 2)));\n            return (20 * Math.Log10(ra)) + 2;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Helper/MathHelper.cs",
    "content": "﻿using System;\n\nnamespace KeyboardAudioVisualizer.Helper\n{\n    public static class MathHelper\n    {\n        #region Methods\n\n        public static double Clamp(double value, double min, double max) => Math.Max(min, Math.Min(max, value));\n        public static float Clamp(float value, float min, float max) => (float)Clamp((double)value, min, max);\n        public static int Clamp(int value, int min, int max) => Math.Max(min, Math.Min(max, value));\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Helper/ObservableDictionary.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.Specialized;\nusing System.Linq;\nusing RGB.NET.Core;\n\n// Taken from https://codereview.stackexchange.com/questions/116562/custom-implementation-of-observabledictionary\nnamespace KeyboardAudioVisualizer.Helper\n{\n    public class ObservableDictionary<TKey, TValue> : AbstractBindable, IDictionary<TKey, TValue>, INotifyCollectionChanged\n    {\n        #region Constants\n\n        private const string INDEXER_NAME = \"Item[]\";\n\n        #endregion\n\n        #region Properties & Fields\n\n        private readonly IList<TValue> _values;\n        private readonly IDictionary<TKey, int> _indexMap;\n\n        private readonly SimpleMonitor _monitor = new SimpleMonitor();\n\n        #endregion\n\n        #region Constructor\n\n        public ObservableDictionary()\n        {\n            _values = new List<TValue>();\n            _indexMap = new Dictionary<TKey, int>();\n        }\n\n        public ObservableDictionary(IDictionary<TKey, TValue> dictionary)\n        {\n            _values = new List<TValue>();\n            _indexMap = new Dictionary<TKey, int>();\n\n            int idx = 0;\n            foreach (KeyValuePair<TKey, TValue> kvp in dictionary)\n            {\n                _indexMap.Add(kvp.Key, idx);\n                _values.Add(kvp.Value);\n\n                idx++;\n            }\n        }\n\n        public ObservableDictionary(int capacity)\n        {\n            _values = new List<TValue>(capacity);\n            _indexMap = new Dictionary<TKey, int>(capacity);\n        }\n\n        #endregion\n\n        #region Virtual Add/Remove/Change Control Methods\n\n        protected virtual void AddItem(TKey key, TValue value)\n        {\n            CheckReentrancy();\n\n            int index = _values.Count;\n            _indexMap.Add(key, index);\n            _values.Add(value);\n\n            OnPropertyChanged(nameof(Count));\n            OnPropertyChanged(nameof(Keys));\n            OnPropertyChanged(nameof(Values));\n            OnPropertyChanged(INDEXER_NAME);\n            OnCollectionChanged(NotifyCollectionChangedAction.Add, key, value, index);\n        }\n\n        protected virtual bool RemoveItem(TKey key)\n        {\n            CheckReentrancy();\n\n            int index = _indexMap[key];\n            TValue value = _values[index];\n\n            if (_indexMap.Remove(key))\n            {\n                _values.RemoveAt(index);\n\n                List<TKey> keys = _indexMap.Keys.ToList();\n\n                foreach (TKey existingKey in keys)\n                {\n                    if (_indexMap[existingKey] > index)\n                        _indexMap[existingKey]--;\n                }\n\n                OnPropertyChanged(nameof(Count));\n                OnPropertyChanged(nameof(Keys));\n                OnPropertyChanged(nameof(Values));\n                OnPropertyChanged(INDEXER_NAME);\n                OnCollectionChanged(NotifyCollectionChangedAction.Remove, key, value, index);\n\n                return true;\n            }\n\n            return false;\n        }\n\n        protected virtual bool RemoveItem(KeyValuePair<TKey, TValue> item)\n        {\n            CheckReentrancy();\n\n            if (_indexMap.ContainsKey(item.Key) && _values[_indexMap[item.Key]].Equals(item.Value))\n            {\n                int index = _indexMap[item.Key];\n                TValue value = _values[index];\n\n                _indexMap.Remove(item.Key);\n                _values.RemoveAt(index);\n\n                List<TKey> keys = _indexMap.Keys.ToList();\n\n                foreach (TKey existingKey in keys)\n                {\n                    if (_indexMap[existingKey] > index)\n                        _indexMap[existingKey]--;\n                }\n\n                OnPropertyChanged(nameof(Count));\n                OnPropertyChanged(nameof(Keys));\n                OnPropertyChanged(nameof(Values));\n                OnPropertyChanged(INDEXER_NAME);\n                OnCollectionChanged(NotifyCollectionChangedAction.Remove, item.Key, item.Value, index);\n\n                return true;\n            }\n\n            return false;\n        }\n\n        protected virtual void RemoveAllItems()\n        {\n\n            CheckReentrancy();\n            _values.Clear();\n            _indexMap.Clear();\n\n            OnPropertyChanged(nameof(Count));\n            OnPropertyChanged(nameof(Keys));\n            OnPropertyChanged(nameof(Values));\n            OnPropertyChanged(INDEXER_NAME);\n            OnCollectionChanged(NotifyCollectionChangedAction.Reset);\n        }\n\n        protected virtual void ChangeItem(TKey key, TValue newValue)\n        {\n\n            CheckReentrancy();\n\n            if (!_indexMap.ContainsKey(key))\n                AddItem(key, newValue);\n            else\n            {\n                int index = _indexMap[key];\n                TValue oldValue = _values[index];\n                _values[index] = newValue;\n\n                OnPropertyChanged(nameof(Values));\n                OnPropertyChanged(INDEXER_NAME);\n                OnCollectionChanged(NotifyCollectionChangedAction.Replace, key, oldValue, newValue, index);\n            }\n        }\n\n        protected IDisposable BlockReentrancy()\n        {\n            _monitor.Enter();\n            return (IDisposable)_monitor;\n        }\n\n        protected void CheckReentrancy()\n        {\n            // ISSUE: reference to a compiler-generated field\n            // ISSUE: reference to a compiler-generated field\n            if (_monitor.Busy && CollectionChanged != null && CollectionChanged.GetInvocationList().Length > 1)\n                throw new InvalidOperationException(\"ObservableCollectionReentrancyNotAllowed\");\n        }\n\n\n        #endregion\n\n        #region IDictionary<TKey,TValue> Members\n\n        public void Add(TKey key, TValue value) => AddItem(key, value);\n\n        public bool ContainsKey(TKey key) => _indexMap.ContainsKey(key);\n\n        public bool Remove(TKey key) => RemoveItem(key);\n\n        public bool TryGetValue(TKey key, out TValue value)\n        {\n            if (_indexMap.TryGetValue(key, out int index))\n            {\n                value = _values[index];\n                return true;\n            }\n            else\n            {\n                value = default;\n                return false;\n            }\n        }\n\n\n        public ICollection<TKey> Keys => _indexMap.Keys;\n\n        public ICollection<TValue> Values => _values;\n\n        public TValue this[TKey key]\n        {\n            get\n            {\n                int index = _indexMap[key];\n                return _values[index];\n            }\n            set => ChangeItem(key, value);\n        }\n\n        #endregion\n\n        #region ICollection<KeyValuePair<TKey,TValue>> Members\n\n        public void Clear() => RemoveAllItems();\n\n        public int Count => _indexMap.Count;\n\n        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);\n\n        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) => _indexMap.ContainsKey(item.Key) && _values[_indexMap[item.Key]].Equals(item.Value);\n\n        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)\n        {\n            foreach (KeyValuePair<TKey, int> kvp in _indexMap)\n            {\n                array[arrayIndex] = new KeyValuePair<TKey, TValue>(kvp.Key, _values[kvp.Value]);\n                arrayIndex++;\n            }\n        }\n\n        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;\n\n        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) => RemoveItem(item);\n\n        #endregion\n\n        #region IEnumerable<KeyValuePair<TKey,TValue>> Members\n\n        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()\n        {\n            foreach (KeyValuePair<TKey, int> kvp in _indexMap)\n            {\n                KeyValuePair<TKey, TValue> pair = new KeyValuePair<TKey, TValue>(kvp.Key, _values[kvp.Value]);\n                yield return pair;\n            }\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();\n\n        #endregion\n\n        #region INotifyCollectionChanged Members\n\n        public event NotifyCollectionChangedEventHandler CollectionChanged;\n\n        protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)\n        {\n            NotifyCollectionChangedEventHandler handler = CollectionChanged;\n\n            using (BlockReentrancy())\n                handler?.Invoke(this, e);\n        }\n\n        protected void OnCollectionChanged(NotifyCollectionChangedAction action) => OnCollectionChanged(new NotifyCollectionChangedEventArgs(action));\n\n        protected void OnCollectionChanged(NotifyCollectionChangedAction action, TKey key, TValue value, int index) => OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, new KeyValuePair<TKey, TValue>(key, value), index));\n\n        protected void OnCollectionChanged(NotifyCollectionChangedAction action, TKey key, TValue oldValue, TValue newValue, int index)\n        {\n            KeyValuePair<TKey, TValue> newPair = new KeyValuePair<TKey, TValue>(key, newValue);\n            KeyValuePair<TKey, TValue> oldPair = new KeyValuePair<TKey, TValue>(key, oldValue);\n\n            OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newPair, oldPair, index));\n        }\n\n        #endregion\n\n        private class SimpleMonitor : IDisposable\n        {\n            private int _busyCount;\n\n            public bool Busy => _busyCount > 0;\n\n            public void Enter() => _busyCount = _busyCount + 1;\n\n            public void Dispose() => _busyCount = _busyCount - 1;\n        }\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Helper/RingBuffer.cs",
    "content": "﻿using System.Linq;\n\nnamespace KeyboardAudioVisualizer.Helper\n{\n    public class RingBuffer\n    {\n        #region Properties & Fields\n\n        private readonly int _capacity;\n        private readonly float[] _buffer;\n        private int _currentIndex;\n\n        public int Size => _capacity;\n\n        public float Average => _buffer.Average();\n        public float Min => _buffer.Min();\n        public float Max => _buffer.Max();\n        public float Sum => _buffer.Sum();\n\n        #endregion\n\n        #region Constructors\n\n        public RingBuffer(int capacity)\n        {\n            this._capacity = capacity;\n\n            _buffer = new float[capacity];\n        }\n\n        #endregion\n\n        #region Methods\n\n        public void Put(float value) => Put(new[] { value }, 0, 1);\n\n        public void Put(float[] src, int offset, int count)\n        {\n            lock (_buffer)\n            {\n                if (count > _capacity)\n                {\n                    offset += count - _capacity;\n                    count = _capacity;\n                }\n\n                for (int i = 0; i < count; i++)\n                {\n                    _currentIndex++;\n                    if (_currentIndex >= _capacity) _currentIndex = 0;\n\n                    _buffer[_currentIndex] = src[offset + i];\n                }\n            }\n        }\n\n\n        public void CopyInto(ref float[] data, int offset) => CopyInto(ref data, offset, _capacity);\n        public void CopyInto(ref float[] data, int offset, int count)\n        {\n            lock (_buffer)\n                for (int i = _capacity - count; i < count; i++)\n                    data[offset + i] = _buffer[(_currentIndex + i) % _capacity];\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Helper/VisualizationIndex.cs",
    "content": "﻿namespace KeyboardAudioVisualizer.Helper\n{\n    public enum VisualizationIndex\n    {\n        Primary,\n        Secondary,\n        Tertiary\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Helper/WPFHelper.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Media;\n\nnamespace KeyboardAudioVisualizer.Helper\n{\n    public static class WPFHelper\n    {\n        #region Methods\n\n        public static T GetVisualChild<T>(this DependencyObject parent)\n            where T : Visual\n        {\n            T child = default(T);\n\n            int numVisuals = VisualTreeHelper.GetChildrenCount(parent);\n            for (int i = 0; i < numVisuals; i++)\n            {\n                Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);\n                child = v as T;\n\n                if (child == null)\n                    child = GetVisualChild<T>(v);\n\n                if (child != null)\n                    break;\n            }\n            return child;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{0AC4E8B1-4D4D-447F-B9FD-38A74ED1F243}</ProjectGuid>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>KeyboardAudioVisualizer</RootNamespace>\n    <AssemblyName>KeyboardAudioVisualizer</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\n    <WarningLevel>4</WarningLevel>\n    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>\n    <NuGetPackageImportStamp>\n    </NuGetPackageImportStamp>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>x86</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <LangVersion>latest</LangVersion>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>x86</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <LangVersion>latest</LangVersion>\n  </PropertyGroup>\n  <PropertyGroup>\n    <ApplicationIcon>Resources\\Icon.ico</ApplicationIcon>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"CSCore, Version=1.2.1.2, Culture=neutral, PublicKeyToken=5a08f2b6f4415dea, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\CSCore.1.2.1.2\\lib\\net35-client\\CSCore.dll</HintPath>\n    </Reference>\n    <Reference Include=\"Hardcodet.Wpf.TaskbarNotification, Version=1.0.5.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Hardcodet.NotifyIcon.Wpf.1.0.8\\lib\\net451\\Hardcodet.Wpf.TaskbarNotification.dll</HintPath>\n    </Reference>\n    <Reference Include=\"HidSharp, Version=2.0.5.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\HidSharp.2.0.5\\lib\\net35\\HidSharp.dll</HintPath>\n    </Reference>\n    <Reference Include=\"Interop.AuraServiceLib, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\RGB.NET.Devices.Asus.0.1.31\\lib\\net45\\Interop.AuraServiceLib.dll</HintPath>\n      <EmbedInteropTypes>True</EmbedInteropTypes>\n    </Reference>\n    <Reference Include=\"MathNet.Numerics, Version=4.7.0.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\MathNet.Numerics.4.7.0\\lib\\net461\\MathNet.Numerics.dll</HintPath>\n    </Reference>\n    <Reference Include=\"Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Newtonsoft.Json.12.0.1\\lib\\net45\\Newtonsoft.Json.dll</HintPath>\n    </Reference>\n    <Reference Include=\"RGB.NET.Brushes, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\RGB.NET.Brushes.0.1.31\\lib\\net45\\RGB.NET.Brushes.dll</HintPath>\n    </Reference>\n    <Reference Include=\"RGB.NET.Core, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\RGB.NET.Core.0.1.31\\lib\\net45\\RGB.NET.Core.dll</HintPath>\n    </Reference>\n    <Reference Include=\"RGB.NET.Decorators, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\RGB.NET.Decorators.0.1.31\\lib\\net45\\RGB.NET.Decorators.dll</HintPath>\n    </Reference>\n    <Reference Include=\"RGB.NET.Devices.Asus, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\RGB.NET.Devices.Asus.0.1.31\\lib\\net45\\RGB.NET.Devices.Asus.dll</HintPath>\n    </Reference>\n    <Reference Include=\"RGB.NET.Devices.CoolerMaster, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\RGB.NET.Devices.CoolerMaster.0.1.31\\lib\\net45\\RGB.NET.Devices.CoolerMaster.dll</HintPath>\n    </Reference>\n    <Reference Include=\"RGB.NET.Devices.Corsair, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\RGB.NET.Devices.Corsair.0.1.31\\lib\\net45\\RGB.NET.Devices.Corsair.dll</HintPath>\n    </Reference>\n    <Reference Include=\"RGB.NET.Devices.Logitech, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\RGB.NET.Devices.Logitech.0.1.31\\lib\\net45\\RGB.NET.Devices.Logitech.dll</HintPath>\n    </Reference>\n    <Reference Include=\"RGB.NET.Devices.Novation, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\RGB.NET.Devices.Novation.0.1.31\\lib\\net45\\RGB.NET.Devices.Novation.dll</HintPath>\n    </Reference>\n    <Reference Include=\"RGB.NET.Devices.Razer, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\RGB.NET.Devices.Razer.0.1.31\\lib\\net45\\RGB.NET.Devices.Razer.dll</HintPath>\n    </Reference>\n    <Reference Include=\"RGB.NET.Devices.SteelSeries, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\RGB.NET.Devices.SteelSeries.0.1.31\\lib\\netstandard2.0\\RGB.NET.Devices.SteelSeries.dll</HintPath>\n    </Reference>\n    <Reference Include=\"RGB.NET.Groups, Version=0.1.31.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\RGB.NET.Groups.0.1.31\\lib\\net45\\RGB.NET.Groups.dll</HintPath>\n    </Reference>\n    <Reference Include=\"Sanford.Multimedia.Midi, Version=6.6.0.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Sanford.Multimedia.Midi.6.6.0\\lib\\net20\\Sanford.Multimedia.Midi.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Drawing\" />\n    <Reference Include=\"System.Management\" />\n    <Reference Include=\"System.Numerics\" />\n    <Reference Include=\"System.Runtime.Serialization\" />\n    <Reference Include=\"System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.ValueTuple.4.5.0\\lib\\net461\\System.ValueTuple.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xaml\">\n      <RequiredTargetFramework>4.0</RequiredTargetFramework>\n    </Reference>\n    <Reference Include=\"Transitionals\">\n      <HintPath>..\\libs\\Transitionals.dll</HintPath>\n    </Reference>\n    <Reference Include=\"WindowsBase\" />\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ApplicationDefinition Include=\"App.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </ApplicationDefinition>\n    <Compile Include=\"Attached\\SliderValue.cs\" />\n    <Compile Include=\"Attached\\SliderValueAdorner.cs\" />\n    <Compile Include=\"App.xaml.cs\">\n      <DependentUpon>App.xaml</DependentUpon>\n      <SubType>Code</SubType>\n    </Compile>\n    <Compile Include=\"ApplicationManager.cs\" />\n    <Compile Include=\"Attributes\\DisplayNameAttribute.cs\" />\n    <Compile Include=\"Attributes\\VisualizerForAttribute.cs\" />\n    <Compile Include=\"AudioCapture\\AudioBuffer.cs\" />\n    <Compile Include=\"AudioCapture\\CSCoreAudioInput.cs\" />\n    <Compile Include=\"AudioCapture\\IAudioInput.cs\" />\n    <Compile Include=\"AudioProcessing\\AbstractAudioProcessor.cs\" />\n    <Compile Include=\"AudioProcessing\\AudioVisualizationFactory.cs\" />\n    <Compile Include=\"AudioProcessing\\Equalizer\\EqualizerBand.cs\" />\n    <Compile Include=\"AudioProcessing\\Equalizer\\IEqualizer.cs\" />\n    <Compile Include=\"AudioProcessing\\Equalizer\\MultiBandEqualizer.cs\" />\n    <Compile Include=\"AudioProcessing\\IAudioProcessor.cs\" />\n    <Compile Include=\"AudioProcessing\\Spectrum\\Band.cs\" />\n    <Compile Include=\"AudioProcessing\\Spectrum\\AbstractSpectrum.cs\" />\n    <Compile Include=\"AudioProcessing\\Spectrum\\GammaSpectrum.cs\" />\n    <Compile Include=\"AudioProcessing\\Spectrum\\ISpectrum.cs\" />\n    <Compile Include=\"AudioProcessing\\Spectrum\\ISpectrumProvider.cs\" />\n    <Compile Include=\"AudioProcessing\\Spectrum\\FourierSpectrumProvider.cs\" />\n    <Compile Include=\"AudioProcessing\\Spectrum\\LogarithmicSpectrum.cs\" />\n    <Compile Include=\"AudioProcessing\\Spectrum\\LinearSpectrum.cs\" />\n    <Compile Include=\"AudioProcessing\\VisualizationProvider\\BeatVisualizationProvider.cs\" />\n    <Compile Include=\"AudioProcessing\\VisualizationProvider\\FrequencyBarsVisualizationProvider.cs\" />\n    <Compile Include=\"AudioProcessing\\VisualizationProvider\\IVisualizationProvider.cs\" />\n    <Compile Include=\"AudioProcessing\\VisualizationProvider\\LevelVisualizationProvider.cs\" />\n    <Compile Include=\"AudioProcessing\\Spectrum\\RawSpectrumProvider.cs\" />\n    <Compile Include=\"AudioProcessing\\VisualizationProvider\\VisualizationType.cs\" />\n    <Compile Include=\"Configuration\\ColorSerializer.cs\" />\n    <Compile Include=\"Controls\\ColorSelector.cs\" />\n    <Compile Include=\"Controls\\GradientEditor.cs\" />\n    <Compile Include=\"Converter\\VisualizationProviderDisplayNameConverter.cs\" />\n    <Compile Include=\"Converter\\VisualizationToLastChildFillConverter.cs\" />\n    <Compile Include=\"Converter\\VisualizationTypeSelectableConverter.cs\" />\n    <Compile Include=\"Decorators\\BeatDecorator.cs\" />\n    <Compile Include=\"Decorators\\FrequencyBarsDecorator.cs\" />\n    <Compile Include=\"Decorators\\LevelBarDecorator.cs\" />\n    <Compile Include=\"Configuration\\AbstractConfiguration.cs\" />\n    <Compile Include=\"Configuration\\EqualizerConfiguration.cs\" />\n    <Compile Include=\"Configuration\\IConfiguration.cs\" />\n    <Compile Include=\"Controls\\Form.cs\" />\n    <Compile Include=\"Controls\\ImageButton.cs\" />\n    <Compile Include=\"Converter\\BoolToVisibilityConverter.cs\" />\n    <Compile Include=\"Converter\\EqualizerBandsToPointsConverter.cs\" />\n    <Compile Include=\"Converter\\EqualsToBoolConverter.cs\" />\n    <Compile Include=\"Converter\\OffsetToPosXConverter.cs\" />\n    <Compile Include=\"Converter\\ValueToPosYConverter.cs\" />\n    <Compile Include=\"Helper\\ActionCommand.cs\" />\n    <Compile Include=\"Helper\\EnumExtension.cs\" />\n    <Compile Include=\"Helper\\ExceptionExtension.cs\" />\n    <Compile Include=\"Helper\\FrequencyHelper.cs\" />\n    <Compile Include=\"Helper\\MathHelper.cs\" />\n    <Compile Include=\"Helper\\ObservableDictionary.cs\" />\n    <Compile Include=\"Helper\\RingBuffer.cs\" />\n    <Compile Include=\"Helper\\VisualizationIndex.cs\" />\n    <Compile Include=\"Helper\\WPFHelper.cs\" />\n    <Compile Include=\"Configuration\\Settings.cs\" />\n    <Compile Include=\"Controls\\BlurredDecorationWindow.cs\" />\n    <Compile Include=\"Legacy\\ConfigurationMigrator.cs\" />\n    <Compile Include=\"Legacy\\ConfigurationUpdates.cs\" />\n    <Compile Include=\"Legacy\\Settings.cs\" />\n    <Compile Include=\"Styles\\CachedResourceDictionary.cs\" />\n    <Compile Include=\"UI\\ConfigurationWindow.xaml.cs\">\n      <DependentUpon>ConfigurationWindow.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"UI\\ConfigurationViewModel.cs\" />\n    <Compile Include=\"UI\\Visualization\\BeatVisualizer.cs\" />\n    <Compile Include=\"UI\\Visualization\\EqualizerVisualizer.cs\" />\n    <Compile Include=\"UI\\Visualization\\FrequencyBarsVisualizer.cs\" />\n    <Compile Include=\"UI\\Visualization\\LevelVisualizer.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Legacy\\SerializationHelper.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\">\n      <SubType>Code</SubType>\n    </Compile>\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Properties\\Settings.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DependentUpon>Settings.settings</DependentUpon>\n      <DesignTimeSharedInput>True</DesignTimeSharedInput>\n    </Compile>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n      <SubType>Designer</SubType>\n    </EmbeddedResource>\n    <None Include=\"packages.config\">\n      <SubType>Designer</SubType>\n    </None>\n    <None Include=\"Properties\\Settings.settings\">\n      <Generator>SettingsSingleFileGenerator</Generator>\n      <LastGenOutput>Settings.Designer.cs</LastGenOutput>\n    </None>\n    <Resource Include=\"Resources\\navigation.ttf\" />\n    <Resource Include=\"Resources\\font.ttf\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Resource Include=\"Resources\\Icon.ico\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Page Include=\"Resources\\KeyboardAudioVisualizer.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\BlurredDecorationWindow.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\ColorSelector.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\FrameworkElement.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\ComboBox.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\Button.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\GradientEditor.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\Slider.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\ImageButton.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\Theme.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\Navigation.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\GroupBox.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\Form.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"Styles\\ToolTip.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"UI\\ConfigurationWindow.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"UI\\Configuration\\BeatConfiguration.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"UI\\Configuration\\LevelConfiguration.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"UI\\Configuration\\FrequencyBarsConfiguration.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"UI\\Visualization\\BeatVisualization.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"UI\\Visualization\\EqualizerVisualization.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"UI\\Visualization\\FrequencyBarsVisualization.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"UI\\Visualization\\LevelVisualization.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n  </ItemGroup>\n  <ItemGroup>\n    <Resource Include=\"Resources\\background.png\" />\n    <Resource Include=\"Resources\\close.png\" />\n    <Resource Include=\"Resources\\minimize.png\" />\n  </ItemGroup>\n  <ItemGroup />\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <Target Name=\"EnsureNuGetPackageBuildImports\" BeforeTargets=\"PrepareForBuild\">\n    <PropertyGroup>\n      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>\n    </PropertyGroup>\n    <Error Condition=\"!Exists('..\\packages\\RGB.NET.Resources.Novation.0.1.0\\build\\RGB.NET.Resources.Novation.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\RGB.NET.Resources.Novation.0.1.0\\build\\RGB.NET.Resources.Novation.targets'))\" />\n    <Error Condition=\"!Exists('..\\packages\\RGB.NET.Resources.CoolerMaster.0.2.0\\build\\RGB.NET.Resources.CoolerMaster.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\RGB.NET.Resources.CoolerMaster.0.2.0\\build\\RGB.NET.Resources.CoolerMaster.targets'))\" />\n    <Error Condition=\"!Exists('..\\packages\\RGB.NET.Resources.Corsair.0.3.0.234\\build\\RGB.NET.Resources.Corsair.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\RGB.NET.Resources.Corsair.0.3.0.234\\build\\RGB.NET.Resources.Corsair.targets'))\" />\n    <Error Condition=\"!Exists('..\\packages\\RGB.NET.Resources.Logitech.0.3.0\\build\\RGB.NET.Resources.Logitech.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\RGB.NET.Resources.Logitech.0.3.0\\build\\RGB.NET.Resources.Logitech.targets'))\" />\n    <Error Condition=\"!Exists('..\\packages\\RGB.NET.Resources.Razer.0.3.2.4\\build\\RGB.NET.Resources.Razer.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\RGB.NET.Resources.Razer.0.3.2.4\\build\\RGB.NET.Resources.Razer.targets'))\" />\n    <Error Condition=\"!Exists('..\\packages\\RGB.NET.Resources.Asus.0.3.0\\build\\RGB.NET.Resources.Asus.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\RGB.NET.Resources.Asus.0.3.0\\build\\RGB.NET.Resources.Asus.targets'))\" />\n  </Target>\n  <Import Project=\"..\\packages\\RGB.NET.Resources.Novation.0.1.0\\build\\RGB.NET.Resources.Novation.targets\" Condition=\"Exists('..\\packages\\RGB.NET.Resources.Novation.0.1.0\\build\\RGB.NET.Resources.Novation.targets')\" />\n  <Import Project=\"..\\packages\\RGB.NET.Resources.CoolerMaster.0.2.0\\build\\RGB.NET.Resources.CoolerMaster.targets\" Condition=\"Exists('..\\packages\\RGB.NET.Resources.CoolerMaster.0.2.0\\build\\RGB.NET.Resources.CoolerMaster.targets')\" />\n  <Import Project=\"..\\packages\\RGB.NET.Resources.Corsair.0.3.0.234\\build\\RGB.NET.Resources.Corsair.targets\" Condition=\"Exists('..\\packages\\RGB.NET.Resources.Corsair.0.3.0.234\\build\\RGB.NET.Resources.Corsair.targets')\" />\n  <Import Project=\"..\\packages\\RGB.NET.Resources.Logitech.0.3.0\\build\\RGB.NET.Resources.Logitech.targets\" Condition=\"Exists('..\\packages\\RGB.NET.Resources.Logitech.0.3.0\\build\\RGB.NET.Resources.Logitech.targets')\" />\n  <Import Project=\"..\\packages\\RGB.NET.Resources.Razer.0.3.2.4\\build\\RGB.NET.Resources.Razer.targets\" Condition=\"Exists('..\\packages\\RGB.NET.Resources.Razer.0.3.2.4\\build\\RGB.NET.Resources.Razer.targets')\" />\n  <Import Project=\"..\\packages\\RGB.NET.Resources.Asus.0.3.0\\build\\RGB.NET.Resources.Asus.targets\" Condition=\"Exists('..\\packages\\RGB.NET.Resources.Asus.0.3.0\\build\\RGB.NET.Resources.Asus.targets')\" />\n</Project>"
  },
  {
    "path": "KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj.DotSettings",
    "content": "﻿<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\">\n\t<s:String x:Key=\"/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue\">CSharp71</s:String></wpf:ResourceDictionary>"
  },
  {
    "path": "KeyboardAudioVisualizer/Legacy/ConfigurationMigrator.cs",
    "content": "﻿using System.IO;\nusing KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing KeyboardAudioVisualizer.Helper;\n\nnamespace KeyboardAudioVisualizer.Legacy\n{\n    public static class ConfigurationMigrator\n    {\n        #region Constants\n\n        private const string PATH_V1_SETTINGS = \"Settings.xml\";\n\n        #endregion\n\n        #region Constructors\n\n        #endregion\n\n        #region Methods\n\n        public static Configuration.Settings MigrateOldConfig()\n        {\n            if (!File.Exists(PATH_V1_SETTINGS)) return null;\n\n            try\n            {\n                Settings oldSettings = SerializationHelper.LoadObjectFromFile<Settings>(PATH_V1_SETTINGS);\n                Configuration.Settings settings = new Configuration.Settings { UpdateRate = oldSettings.UpdateRate };\n\n                settings[VisualizationIndex.Primary].SelectedVisualization = VisualizationType.FrequencyBars;\n                settings[VisualizationIndex.Primary].FrequencyBarsConfiguration = oldSettings.FrequencyBarsVisualizationProviderConfiguration;\n                settings[VisualizationIndex.Primary].EqualizerConfiguration = oldSettings.EqualizerConfiguration;\n\n                settings[VisualizationIndex.Secondary].SelectedVisualization = VisualizationType.Beat;\n                settings[VisualizationIndex.Secondary].BeatConfiguration = oldSettings.BeatVisualizationProviderConfiguration;\n\n                settings[VisualizationIndex.Tertiary].SelectedVisualization = VisualizationType.Level;\n                settings[VisualizationIndex.Tertiary].LevelConfiguration = oldSettings.LevelVisualizationProviderConfiguration;\n\n                return settings;\n            }\n            catch\n            {\n                return null;\n            }\n        }\n\n        public static void CleanupOldConfigs()\n        {\n            if (File.Exists(PATH_V1_SETTINGS))\n                File.Delete(PATH_V1_SETTINGS);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Legacy/ConfigurationUpdates.cs",
    "content": "﻿using KeyboardAudioVisualizer.Helper;\nusing RGB.NET.Brushes.Gradients;\nusing RGB.NET.Core;\n\nnamespace KeyboardAudioVisualizer.Legacy\n{\n    public static class ConfigurationUpdates\n    {\n        #region Methods\n\n        public static void PerformOn(Configuration.Settings settings)\n        {\n            if (settings.Version < 1)\n                UpdateTo1(settings);\n        }\n\n        private static void UpdateTo1(Configuration.Settings settings)\n        {\n            settings.Visualizations[VisualizationIndex.Primary].Gradient = new LinearGradient(new GradientStop(0, HSVColor.Create(300, 1, 1)),\n                                                                                              new GradientStop(0.20, HSVColor.Create(225, 1, 1)),\n                                                                                              new GradientStop(0.35, HSVColor.Create(180, 1, 1)),\n                                                                                              new GradientStop(0.50, HSVColor.Create(135, 1, 1)),\n                                                                                              new GradientStop(0.65, HSVColor.Create(90, 1, 1)),\n                                                                                              new GradientStop(0.80, HSVColor.Create(45, 1, 1)),\n                                                                                              new GradientStop(0.95, HSVColor.Create(0, 1, 1)));\n\n            settings.Visualizations[VisualizationIndex.Secondary].Gradient = new LinearGradient(new GradientStop(0.5, new Color(255, 255, 255)));\n\n            settings.Visualizations[VisualizationIndex.Tertiary].Gradient = new LinearGradient(new GradientStop(0, new Color(0, 0, 255)),\n                                                                                               new GradientStop(1, new Color(255, 0, 0)));\n\n            settings.Version = 1;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Legacy/SerializationHelper.cs",
    "content": "﻿using System.IO;\nusing System.Xml;\nusing System.Xml.Serialization;\n\nnamespace KeyboardAudioVisualizer.Legacy\n{\n    public static class SerializationHelper\n    {\n        #region Methods\n\n        public static void SaveObjectToFile<T>(T serializableObject, string path)\n        {\n            if (serializableObject == null) return;\n\n            try\n            {\n                XmlDocument xmlDocument = new XmlDocument();\n                XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());\n                using (MemoryStream stream = new MemoryStream())\n                {\n                    serializer.Serialize(stream, serializableObject);\n                    stream.Seek(0, SeekOrigin.Begin);\n                    xmlDocument.Load(stream);\n                    xmlDocument.Save(path);\n                }\n            }\n            catch {/* Catch'em all */}\n        }\n\n        public static T LoadObjectFromFile<T>(string fileName)\n        {\n            if (string.IsNullOrEmpty(fileName)) return default(T);\n\n            try\n            {\n                XmlDocument xmlDocument = new XmlDocument();\n                xmlDocument.Load(fileName);\n                string xmlString = xmlDocument.OuterXml;\n\n                using (StringReader sr = new StringReader(xmlString))\n                {\n                    XmlSerializer serializer = new XmlSerializer(typeof(T));\n                    using (XmlReader reader = new XmlTextReader(sr))\n                        return (T)serializer.Deserialize(reader);\n                }\n            }\n            catch {/* Catch'em all */}\n\n            return default(T);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Legacy/Settings.cs",
    "content": "﻿using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing KeyboardAudioVisualizer.Configuration;\n\nnamespace KeyboardAudioVisualizer.Legacy\n{\n    public class Settings\n    {\n        #region General\n\n        public double UpdateRate { get; set; } = 40.0;\n\n        #endregion\n\n        #region AudioProcessing\n\n        public EqualizerConfiguration EqualizerConfiguration { get; set; } = new EqualizerConfiguration();\n\n        public FrequencyBarsVisualizationProviderConfiguration FrequencyBarsVisualizationProviderConfiguration { get; set; } = new FrequencyBarsVisualizationProviderConfiguration();\n\n        public LevelVisualizationProviderConfiguration LevelVisualizationProviderConfiguration { get; set; } = new LevelVisualizationProviderConfiguration();\n\n        public BeatVisualizationProviderConfiguration BeatVisualizationProviderConfiguration { get; set; } = new BeatVisualizationProviderConfiguration();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Windows;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"KeyboardAudioVisualizer\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"KeyboardAudioVisualizer\")]\n[assembly: AssemblyCopyright(\"Copyright © Wyrez 2017\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n//In order to begin building localizable applications, set\n//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file\n//inside a <PropertyGroup>.  For example, if you are using US english\n//in your source files, set the <UICulture> to en-US.  Then uncomment\n//the NeutralResourceLanguage attribute below.  Update the \"en-US\" in\n//the line below to match the UICulture setting in the project file.\n\n//[assembly: NeutralResourcesLanguage(\"en-US\", UltimateResourceFallbackLocation.Satellite)]\n\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\n                                     //(used if a resource is not found in the page,\n                                     // or application resource dictionaries)\n    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\n                                              //(used if a resource is not found in the page,\n                                              // app, or any theme specific resource dictionaries)\n)]\n\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.3.1.1\")]\n[assembly: AssemblyFileVersion(\"1.3.1.1\")]\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace KeyboardAudioVisualizer.Properties\n{\n\n\n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"4.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources\n    {\n\n        private static global::System.Resources.ResourceManager resourceMan;\n\n        private static global::System.Globalization.CultureInfo resourceCulture;\n\n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources()\n        {\n        }\n\n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager\n        {\n            get\n            {\n                if ((resourceMan == null))\n                {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"KeyboardAudioVisualizer.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n\n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture\n        {\n            get\n            {\n                return resourceCulture;\n            }\n            set\n            {\n                resourceCulture = value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n</root>"
  },
  {
    "path": "KeyboardAudioVisualizer/Properties/Settings.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace KeyboardAudioVisualizer.Properties\n{\n\n\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator\", \"11.0.0.0\")]\n    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase\n    {\n\n        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));\n\n        public static Settings Default\n        {\n            get\n            {\n                return defaultInstance;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Properties/Settings.settings",
    "content": "﻿<?xml version='1.0' encoding='utf-8'?>\n<SettingsFile xmlns=\"uri:settings\" CurrentProfile=\"(Default)\">\n  <Profiles>\n    <Profile Name=\"(Default)\" />\n  </Profiles>\n  <Settings />\n</SettingsFile>"
  },
  {
    "path": "KeyboardAudioVisualizer/Resources/KeyboardAudioVisualizer.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                    xmlns:ui=\"clr-namespace:KeyboardAudioVisualizer.UI\"\n                                    xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n                                    xmlns:controls=\"clr-namespace:KeyboardAudioVisualizer.Controls\"\n                                    xmlns:converter=\"clr-namespace:KeyboardAudioVisualizer.Converter\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/BlurredDecorationWindow.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/ImageButton.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Form.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/GroupBox.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/ToolTip.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/ComboBox.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Button.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Slider.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/ColorSelector.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/GradientEditor.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <converter:EqualsToBoolConverter x:Key=\"EqualsToBoolConverter\" />\n    <converter:BoolToVisibilityConverter x:Key=\"BoolToVisibilityConverter\" />\n\n    <Style TargetType=\"{x:Type controls:BlurredDecorationWindow}\" BasedOn=\"{StaticResource StyleBlurredDecorationWindow}\" />\n    <Style TargetType=\"{x:Type ui:ConfigurationWindow}\" BasedOn=\"{StaticResource StyleBlurredDecorationWindow}\" />\n    <Style TargetType=\"{x:Type controls:ImageButton}\" BasedOn=\"{StaticResource StyleImageButton}\" />\n    <Style TargetType=\"{x:Type controls:Form}\" BasedOn=\"{StaticResource StyleForm}\" />\n    <Style TargetType=\"GroupBox\" BasedOn=\"{StaticResource StyleGroupBoxBox}\" />\n    <Style TargetType=\"ToolTip\" BasedOn=\"{StaticResource StyleToolTip}\" />\n    <Style TargetType=\"ComboBox\" BasedOn=\"{StaticResource StyleComboBox}\" />\n    <Style TargetType=\"Button\" BasedOn=\"{StaticResource StyleButton}\" />\n    <Style TargetType=\"Slider\" BasedOn=\"{StaticResource StyleSlider}\" />\n\n</styles:CachedResourceDictionary>\n\n\n\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/BlurredDecorationWindow.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                    xmlns:controls=\"clr-namespace:KeyboardAudioVisualizer.Controls\"\n                                    xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/ImageButton.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <Style x:Key=\"StyleImageButtonWindow\"\n           BasedOn=\"{StaticResource StyleImageButtonWithOpacity}\"\n           TargetType=\"{x:Type controls:ImageButton}\">\n        <Setter Property=\"Padding\" Value=\"4\" />\n        <Setter Property=\"Margin\" Value=\"4\" />\n        <Setter Property=\"VerticalAlignment\" Value=\"Top\" />\n        <Setter Property=\"Width\" Value=\"24\" />\n        <Setter Property=\"Height\" Value=\"24\" />\n    </Style>\n\n    <Style x:Key=\"StyleBlurredDecorationWindow\" \n           TargetType=\"{x:Type controls:BlurredDecorationWindow}\">\n        <Setter Property=\"WindowStyle\" Value=\"None\" />\n        <Setter Property=\"ResizeMode\" Value=\"CanResize\" />\n        <Setter Property=\"AllowsTransparency\" Value=\"True\" />\n        <Setter Property=\"HorizontalContentAlignment\" Value=\"Stretch\" />\n        <Setter Property=\"VerticalContentAlignment\" Value=\"Stretch\" />\n        <Setter Property=\"BorderThickness\" Value=\"1,0,1,1\" />\n        <Setter Property=\"DecorationHeight\" Value=\"80\" />\n        <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushWindowBorder}\" />\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushWindowBackground}\" />\n        <Setter Property=\"BackgroundImage\" Value=\"pack://application:,,,/Resources/background.png\" />\n        <Setter Property=\"MinWidth\" Value=\"256\" />\n        <Setter Property=\"MinHeight\" Value=\"144\" />\n        <Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\n        <Setter Property=\"FontSize\" Value=\"{StaticResource FontSizeDefault}\" />\n        <Setter Property=\"FontFamily\" Value=\"pack://application:,,,/Resources/#Cinzel\" />\n\n        <Setter Property=\"WindowChrome.WindowChrome\">\n            <Setter.Value>\n                <WindowChrome CaptionHeight=\"0\" ResizeBorderThickness=\"5\" />\n            </Setter.Value>\n        </Setter>\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type controls:BlurredDecorationWindow}\">\n                    <Grid Background=\"{TemplateBinding Background}\">\n                        <Grid Margin=\"-12,-12,-12,0\">\n                            <Viewbox HorizontalAlignment=\"Center\" Stretch=\"UniformToFill\">\n                                <Image Source=\"{TemplateBinding BackgroundImage}\" />\n                            </Viewbox>\n\n                            <Border Name=\"BlurImage\">\n                                <Border.OpacityMask>\n                                    <VisualBrush TileMode=\"None\" Stretch=\"None\" AlignmentX=\"Center\" AlignmentY=\"Center\">\n                                        <VisualBrush.Visual>\n                                            <Grid Width=\"{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Border}}\" \n                                                  Height=\"{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Border}}\">\n                                                <Grid.RowDefinitions>\n                                                    <RowDefinition Height=\"12\" />\n                                                    <RowDefinition Height=\"92\" />\n                                                    <RowDefinition Height=\"*\" />\n                                                </Grid.RowDefinitions>\n                                                <Rectangle Fill=\"Black\" Grid.Row=\"0\" Grid.RowSpan=\"2\" />\n                                                <Rectangle Fill=\"Transparent\" Grid.Row=\"2\" />\n                                            </Grid>\n                                        </VisualBrush.Visual>\n                                    </VisualBrush>\n                                </Border.OpacityMask>\n\n                                <Border.Effect>\n                                    <BlurEffect Radius=\"30\" />\n                                </Border.Effect>\n\n                                <Viewbox HorizontalAlignment=\"Center\" Stretch=\"UniformToFill\">\n                                    <Image Source=\"{TemplateBinding BackgroundImage}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\" />\n                                </Viewbox>\n                            </Border>\n                        </Grid>\n\n                        <Border BorderBrush=\"{TemplateBinding BorderBrush}\"\n                                BorderThickness=\"{TemplateBinding BorderThickness}\">\n                            <DockPanel LastChildFill=\"True\">\n                                <Border x:Name=\"PART_Decoration\" \n                                        DockPanel.Dock=\"Top\"\n                                        HorizontalAlignment=\"Stretch\"\n                                        Height=\"{TemplateBinding DecorationHeight}\"\n                                        Background=\"{TemplateBinding BorderBrush}\">\n                                    <DockPanel HorizontalAlignment=\"Stretch\" Margin=\"4\" LastChildFill=\"False\">\n                                        <controls:ImageButton x:Name=\"PART_CloseButton\"\n                                                              DockPanel.Dock=\"Right\"\n                                                              Style=\"{StaticResource StyleImageButtonWindow}\"\n                                                              Image=\"pack://application:,,,/Resources/close.png\" \n                                                              ToolTip=\"Close\" />\n\n                                        <controls:ImageButton x:Name=\"PART_MinimizeButton\"\n                                                              DockPanel.Dock=\"Right\"\n                                                              Style=\"{StaticResource StyleImageButtonWindow}\"\n                                                              Image=\"pack://application:,,,/Resources/minimize.png\" \n                                                              ToolTip=\"Minimize\" />\n\n                                        <controls:ImageButton x:Name=\"PART_IconButton\"\n                                                              DockPanel.Dock=\"Left\" \n                                                              VerticalAlignment=\"Center\"\n                                                              Margin=\"8\"\n                                                              Style=\"{StaticResource StyleImageButtonWithOpacity}\"\n                                                              Image=\"{TemplateBinding Icon}\"\n                                                              ToolTip=\"https://github.com/DarthAffe/KeyboardAudioVisualizer\" />\n                                    </DockPanel>\n                                </Border>\n\n                                <ContentPresenter x:Name=\"PART_Content\" />\n                            </DockPanel>\n                        </Border>\n                    </Grid>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n</styles:CachedResourceDictionary>\n    \n    \n    \n    "
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/Button.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                    xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <Style x:Key=\"StyleButton\" \n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"Button\">\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushButtonForeground}\" />\n        <Setter Property=\"FontSize\" Value=\"{StaticResource FontSizeDefault}\" />\n        <Setter Property=\"FontFamily\" Value=\"pack://application:,,,/Resources/#Cinzel\" />\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushButtonBackground}\" />\n        <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushButtonBorder}\" />\n        <Setter Property=\"BorderThickness\" Value=\"2\" />\n        <Setter Property=\"Padding\" Value=\"4,2\" />\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"Button\">\n                    <Border BorderThickness=\"{TemplateBinding BorderThickness}\" \n                            BorderBrush=\"{TemplateBinding BorderBrush}\" \n                            Background=\"{TemplateBinding Background}\">\n                        <ContentPresenter Margin=\"{TemplateBinding Padding}\" />\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        \n        <Style.Triggers>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushButtonHover}\" />\n            </Trigger>\n        </Style.Triggers>\n    </Style>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/CachedResourceDictionary.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Windows;\n\nnamespace KeyboardAudioVisualizer.Styles\n{\n    public class CachedResourceDictionary : ResourceDictionary\n    {\n        #region Properties & Fields\n\n        // ReSharper disable InconsistentNaming\n        private static readonly List<string> _cachedDictionaries = new List<string>();\n        private static readonly ResourceDictionary _innerDictionary = new ResourceDictionary();\n        // ReSharper restore \n\n        public new Uri Source\n        {\n            get => null;\n            set\n            {\n                lock (_innerDictionary)\n                {\n                    UpdateCache(value);\n\n                    MergedDictionaries.Clear();\n                    MergedDictionaries.Add(_innerDictionary);\n                }\n            }\n        }\n\n        #endregion\n\n        #region Methods\n\n        private static void UpdateCache(Uri source)\n        {\n            string uriPath = source.OriginalString;\n            if (_cachedDictionaries.Contains(uriPath)) return;\n\n            _cachedDictionaries.Add(uriPath);\n\n            ResourceDictionary newDictionary = new ResourceDictionary { Source = new Uri(uriPath, source.IsAbsoluteUri ? UriKind.Absolute : UriKind.Relative) };\n            CopyDictionaryEntries(newDictionary, _innerDictionary);\n        }\n\n        private static void CopyDictionaryEntries(IDictionary source, IDictionary target)\n        {\n            foreach (object key in source.Keys)\n                if (!target.Contains(key))\n                    target.Add(key, source[key]);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/ColorSelector.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                 xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                 xmlns:controls=\"clr-namespace:KeyboardAudioVisualizer.Controls\"\n                                 xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\">\n\n    <DrawingBrush x:Key=\"BrushChessboard\"\n                  TileMode=\"Tile\" \n                  Viewport=\"0,0,16,16\" \n                  ViewportUnits=\"Absolute\">\n        <DrawingBrush.Drawing>\n            <GeometryDrawing Brush=\"#FF808080\" \n                             Geometry=\"M5,5 L0,5 0,10 5,10 5,5 10,5 10,0 5,0 Z\"/>\n        </DrawingBrush.Drawing>\n    </DrawingBrush>\n\n    <DrawingBrush x:Key=\"BrushChessboardSmall\"\n                  TileMode=\"Tile\" \n                  Viewport=\"0,0,8,8\" \n                  ViewportUnits=\"Absolute\">\n        <DrawingBrush.Drawing>\n            <GeometryDrawing Brush=\"#FF808080\" \n                             Geometry=\"M5,5 L0,5 0,10 5,10 5,5 10,5 10,0 5,0 Z\"/>\n        </DrawingBrush.Drawing>\n    </DrawingBrush>\n\n    <Style x:Key=\"StyleSliderLabel\"\n           TargetType=\"TextBlock\">\n        <Setter Property=\"TextAlignment\" Value=\"Right\" />\n        <Setter Property=\"VerticalAlignment\" Value=\"Center\" />\n    </Style>\n\n    <Style x:Key=\"StyleSliderValue\"\n           TargetType=\"TextBlock\">\n        <Setter Property=\"VerticalAlignment\" Value=\"Center\" />\n    </Style>\n\n    <Style x:Key=\"StyleThumbSlider\"\n           TargetType=\"Thumb\">\n        <Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\n        <Setter Property=\"OverridesDefaultStyle\" Value=\"True\" />\n        <Setter Property=\"VerticalAlignment\" Value=\"Stretch\" />\n        <Setter Property=\"Height\" Value=\"NaN\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"Thumb\">\n                    <Border VerticalAlignment=\"Stretch\"\n                            Width=\"4\"\n                            SnapsToDevicePixels=\"True\"\n                            Opacity=\"0.66\"\n                            BorderThickness=\"1\" \n                            Background=\"#FFFFFFFF\" \n                            BorderBrush=\"#FF000000\" />\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <Style x:Key=\"StyleSlider\"\n           TargetType=\"Slider\">\n\n        <Setter Property=\"Focusable\" Value=\"False\" />\n        <Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\n        <Setter Property=\"OverridesDefaultStyle\" Value=\"True\" />\n        <Setter Property=\"BorderThickness\" Value=\"1\" />\n        <Setter Property=\"BorderBrush\" Value=\"#FF000000\" />\n        <Setter Property=\"IsMoveToPointEnabled\" Value=\"True\" />\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"Slider\">\n                    <Border SnapsToDevicePixels=\"True\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            Background=\"#FFFFFFFF\">\n                        <Border Margin=\"1\"\n                                SnapsToDevicePixels=\"True\"\n                                Background=\"{StaticResource BrushChessboardSmall}\">\n                            <Border SnapsToDevicePixels=\"True\"\n                                    Background=\"{TemplateBinding Background}\">\n                                <Grid x:Name=\"GridTrackParent\" SnapsToDevicePixels=\"True\">\n                                    <Grid.RowDefinitions>\n                                        <RowDefinition Height=\"Auto\" />\n                                        <RowDefinition Height=\"Auto\" MinHeight=\"{TemplateBinding MinHeight}\" />\n                                        <RowDefinition Height=\"Auto\" />\n                                    </Grid.RowDefinitions>\n\n                                    <Track x:Name=\"PART_Track\" \n                                           Grid.Row=\"1\" \n                                           Width=\"{Binding ActualWidth, ElementName=GridTrackParent}\"\n                                           Height=\"{Binding ActualHeight, ElementName=GridTrackParent}\">\n                                        <Track.DecreaseRepeatButton>\n                                            <RepeatButton Visibility=\"Hidden\" Command=\"Slider.DecreaseLarge\" />\n                                        </Track.DecreaseRepeatButton>\n\n                                        <Track.Thumb>\n                                            <Thumb Style=\"{StaticResource StyleThumbSlider}\" />\n                                        </Track.Thumb>\n\n                                        <Track.IncreaseRepeatButton>\n                                            <RepeatButton Visibility=\"Hidden\" Command=\"Slider.IncreaseLarge\" />\n                                        </Track.IncreaseRepeatButton>\n                                    </Track>\n                                </Grid>\n                            </Border>\n                        </Border>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <Style x:Key=\"StyleColorSelector\"\n           TargetType=\"{x:Type controls:ColorSelector}\">\n\n        <Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\n        <Setter Property=\"OverridesDefaultStyle\" Value=\"True\" />\n        <Setter Property=\"Padding\" Value=\"0\" />\n        <Setter Property=\"Width\" Value=\"504\" />\n        <Setter Property=\"Height\" Value=\"232\" />\n        <Setter Property=\"FontSize\" Value=\"13\" />\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type controls:ColorSelector}\">\n                    <Border BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            Background=\"{TemplateBinding Background}\">\n                        <DockPanel Margin=\"{TemplateBinding Padding}\">\n                            <Border DockPanel.Dock=\"Left\"\n                                    SnapsToDevicePixels=\"True\"\n                                    Width=\"{Binding ActualHeight, RelativeSource={RelativeSource Self}}\"\n                                    BorderThickness=\"1\"\n                                    BorderBrush=\"#FF000000\"\n                                    Background=\"#FFFFFFFF\">\n                                <Grid x:Name=\"PART_Selector\"\n                                      Margin=\"1\"\n                                      SnapsToDevicePixels=\"True\" />\n                            </Border>\n\n                            <Slider DockPanel.Dock=\"Left\"\n                                    Margin=\"8,0\"\n                                    Height=\"24\"\n                                    Orientation=\"Horizontal\"\n                                    Style=\"{StaticResource StyleSlider}\"\n                                    Background=\"{Binding Background, ElementName=PART_SliderHue}\"\n                                    Minimum=\"{Binding Minimum, ElementName=PART_SliderHue}\"\n                                    Maximum=\"{Binding Maximum, ElementName=PART_SliderHue}\"\n                                    Value=\"{Binding Value, ElementName=PART_SliderHue}\">\n                                <Slider.LayoutTransform>\n                                    <RotateTransform CenterX=\"0.5\" CenterY=\"0.5\" Angle=\"90\" />\n                                </Slider.LayoutTransform>\n                            </Slider>\n\n                            <DockPanel>\n                                <Grid DockPanel.Dock=\"Bottom\">\n                                    <Grid.RowDefinitions>\n                                        <RowDefinition Height=\"Auto\" />\n                                        <RowDefinition Height=\"12\" />\n                                        <RowDefinition Height=\"Auto\" />\n                                        <RowDefinition Height=\"8\" />\n                                        <RowDefinition Height=\"Auto\" />\n                                        <RowDefinition Height=\"8\" />\n                                        <RowDefinition Height=\"Auto\" />\n                                        <RowDefinition Height=\"12\" />\n                                        <RowDefinition Height=\"Auto\" />\n                                        <RowDefinition Height=\"8\" />\n                                        <RowDefinition Height=\"Auto\" />\n                                        <RowDefinition Height=\"8\" />\n                                        <RowDefinition Height=\"Auto\" />\n                                    </Grid.RowDefinitions>\n\n                                    <Grid.ColumnDefinitions>\n                                        <ColumnDefinition Width=\"Auto\" />\n                                        <ColumnDefinition Width=\"4\" />\n                                        <ColumnDefinition Width=\"*\" />\n                                        <ColumnDefinition Width=\"4\" />\n                                        <ColumnDefinition Width=\"28\" />\n                                    </Grid.ColumnDefinitions>\n\n                                    <TextBlock Grid.Row=\"0\" Grid.Column=\"0\"\n                                               Style=\"{StaticResource StyleSliderLabel}\" \n                                               Text=\"Alpha:\" />\n                                    <Slider x:Name=\"PART_SliderAlpha\" \n                                            Grid.Row=\"0\" Grid.Column=\"2\"\n                                            Orientation=\"Horizontal\" \n                                            Style=\"{StaticResource StyleSlider}\"\n                                            Minimum=\"0\" \n                                            Maximum=\"255\" />\n                                    <TextBlock Grid.Row=\"0\" Grid.Column=\"4\"\n                                               Style=\"{StaticResource StyleSliderValue}\" \n                                               Text=\"{Binding Value, ElementName=PART_SliderAlpha, StringFormat=##0}\" />\n\n                                    <TextBlock Grid.Row=\"2\" Grid.Column=\"0\"\n                                               Style=\"{StaticResource StyleSliderLabel}\"\n                                               Text=\"Hue:\" />\n                                    <Slider x:Name=\"PART_SliderHue\"\n                                            Grid.Row=\"2\" Grid.Column=\"2\"\n                                            Orientation=\"Horizontal\"\n                                            Style=\"{StaticResource StyleSlider}\"\n                                            IsSnapToTickEnabled=\"False\"\n                                            Minimum=\"0\"\n                                            Maximum=\"360\" />\n                                    <TextBlock Grid.Row=\"2\" Grid.Column=\"4\"\n                                               Style=\"{StaticResource StyleSliderValue}\"\n                                               Text=\"{Binding Value, ElementName=PART_SliderHue, StringFormat=##0}\" />\n\n                                    <TextBlock Grid.Row=\"4\" Grid.Column=\"0\"\n                                               Style=\"{StaticResource StyleSliderLabel}\"\n                                               Text=\"Sat:\" />\n                                    <Slider x:Name=\"PART_SliderSaturation\"\n                                            Grid.Row=\"4\" Grid.Column=\"2\"\n                                            Orientation=\"Horizontal\"\n                                            Style=\"{StaticResource StyleSlider}\"\n                                            IsSnapToTickEnabled=\"False\"\n                                            Minimum=\"0\"\n                                            Maximum=\"1\" />\n                                    <TextBlock Grid.Row=\"4\" Grid.Column=\"4\"\n                                               Style=\"{StaticResource StyleSliderValue}\"\n                                               Text=\"{Binding Value, ElementName=PART_SliderSaturation, StringFormat=0.00}\" />\n\n                                    <TextBlock Grid.Row=\"6\" Grid.Column=\"0\"\n                                               Style=\"{StaticResource StyleSliderLabel}\"\n                                               Text=\"Value:\" />\n                                    <Slider x:Name=\"PART_SliderValue\" \n                                            Grid.Row=\"6\" Grid.Column=\"2\"\n                                            Orientation=\"Horizontal\"\n                                            Style=\"{StaticResource StyleSlider}\"\n                                            IsSnapToTickEnabled=\"False\"\n                                            Minimum=\"0\"\n                                            Maximum=\"1\" />\n                                    <TextBlock Grid.Row=\"6\" Grid.Column=\"4\"\n                                               Style=\"{StaticResource StyleSliderValue}\"\n                                               Text=\"{Binding Value, ElementName=PART_SliderValue, StringFormat=0.00}\" />\n\n                                    <TextBlock Grid.Row=\"8\" Grid.Column=\"0\"\n                                               Style=\"{StaticResource StyleSliderLabel}\" \n                                               Text=\"Red:\" />\n                                    <Slider x:Name=\"PART_SliderRed\"\n                                            Grid.Row=\"8\" Grid.Column=\"2\"\n                                            Orientation=\"Horizontal\" \n                                            Style=\"{StaticResource StyleSlider}\"\n                                            TickFrequency=\"1\"\n                                            IsSnapToTickEnabled=\"True\"\n                                            Minimum=\"0\"\n                                            Maximum=\"255\" />\n                                    <TextBlock Grid.Row=\"8\" Grid.Column=\"4\"\n                                               Style=\"{StaticResource StyleSliderValue}\" \n                                               Text=\"{Binding Value, ElementName=PART_SliderRed, StringFormat=000}\" />\n\n                                    <TextBlock Grid.Row=\"10\" Grid.Column=\"0\"\n                                               Style=\"{StaticResource StyleSliderLabel}\" \n                                               Text=\"Green:\" />\n                                    <Slider x:Name=\"PART_SliderGreen\"\n                                            Grid.Row=\"10\" Grid.Column=\"2\"\n                                            Orientation=\"Horizontal\"\n                                            Style=\"{StaticResource StyleSlider}\"\n                                            TickFrequency=\"1\" \n                                            IsSnapToTickEnabled=\"True\"\n                                            Minimum=\"0\"\n                                            Maximum=\"255\" />\n                                    <TextBlock Grid.Row=\"10\" Grid.Column=\"4\"\n                                               Style=\"{StaticResource StyleSliderValue}\" \n                                               Text=\"{Binding Value, ElementName=PART_SliderGreen, StringFormat=000}\" />\n\n                                    <TextBlock Grid.Row=\"12\" Grid.Column=\"0\"\n                                               Style=\"{StaticResource StyleSliderLabel}\"\n                                               Text=\"Blue:\" />\n                                    <Slider x:Name=\"PART_SliderBlue\" \n                                            Grid.Row=\"12\" Grid.Column=\"2\"\n                                            Orientation=\"Horizontal\" \n                                            Style=\"{StaticResource StyleSlider}\"\n                                            TickFrequency=\"1\" \n                                            IsSnapToTickEnabled=\"True\"\n                                            Minimum=\"0\" \n                                            Maximum=\"255\" />\n                                    <TextBlock Grid.Row=\"12\" Grid.Column=\"4\"\n                                               Style=\"{StaticResource StyleSliderValue}\" \n                                               Text=\"{Binding Value, ElementName=PART_SliderBlue, StringFormat=000}\" />\n                                </Grid>\n\n                                <Border HorizontalAlignment=\"Stretch\"\n                                        Margin=\"0,0,0,4\"\n                                        SnapsToDevicePixels=\"True\"\n                                        BorderThickness=\"1\" \n                                        BorderBrush=\"#FF000000\"\n                                        Background=\"#FFFFFFFF\">\n                                    <Border Margin=\"1\"\n                                            SnapsToDevicePixels=\"True\"\n                                            Background=\"{StaticResource BrushChessboard}\">\n                                        <Rectangle x:Name=\"PART_Preview\" \n                                                   HorizontalAlignment=\"Stretch\" \n                                                   VerticalAlignment=\"Stretch\"\n                                                   SnapsToDevicePixels=\"True\" />\n                                    </Border>\n                                </Border>\n                            </DockPanel>\n                        </DockPanel>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <Style TargetType=\"{x:Type controls:ColorSelector}\" BasedOn=\"{StaticResource StyleColorSelector}\" />\n\n</styles:CachedResourceDictionary>"
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/ComboBox.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                    xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <Style x:Key=\"StyleComboBoxItem\" \n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"{x:Type ComboBoxItem}\">\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushComboBoxForeground}\"/>\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type ComboBoxItem}\">\n                    <Border Name=\"Border\"\n                            Padding=\"2\"\n                            SnapsToDevicePixels=\"true\">\n                        <ContentPresenter />\n                    </Border>\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"IsHighlighted\" Value=\"true\">\n                            <Setter TargetName=\"Border\" Property=\"Background\" Value=\"{StaticResource BrushComboBoxHover}\"/>\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <ControlTemplate x:Key=\"ComboBoxToggleButton\" \n                     TargetType=\"{x:Type ToggleButton}\">\n        <Border x:Name=\"Border\" \n                CornerRadius=\"0\"\n                Background=\"{StaticResource BrushComboBoxBackground}\"\n                BorderBrush=\"{StaticResource BrushComboBoxBorder}\"\n                BorderThickness=\"1\">\n            <Grid>\n                <Grid.ColumnDefinitions>\n                    <ColumnDefinition />\n                    <ColumnDefinition Width=\"20\" />\n                </Grid.ColumnDefinitions>\n\n                <Border Grid.Column=\"0\"\n                        CornerRadius=\"0\" \n                        Margin=\"1\" \n                        Background=\"Transparent\" \n                        BorderBrush=\"{StaticResource BrushComboBoxBorder}\"\n                        BorderThickness=\"0,0,1,0\" />\n\n                <Path x:Name=\"Arrow\"\n                  Grid.Column=\"1\"     \n                  Fill=\"{StaticResource BrushComboBoxForeground}\"\n                  HorizontalAlignment=\"Center\"\n                  VerticalAlignment=\"Center\"\n                  Data=\"M0,0 L0,2 L4,6 L8,2 L8,0 L4,4 z\" />\n            </Grid>\n        </Border>\n\n        <ControlTemplate.Triggers>\n            <Trigger Property=\"ToggleButton.IsMouseOver\" Value=\"true\">\n                <Setter TargetName=\"Border\" Property=\"Background\" Value=\"{StaticResource BrushComboBoxHover}\" />\n            </Trigger>\n        </ControlTemplate.Triggers>\n    </ControlTemplate>\n\n    <ControlTemplate x:Key=\"ComboBoxTextBox\" \n                     TargetType=\"{x:Type TextBox}\">\n        <Border x:Name=\"PART_ContentHost\" \n                Focusable=\"False\" \n                Background=\"{TemplateBinding Background}\" />\n    </ControlTemplate>\n\n    <Style x:Key=\"StyleComboBox\" \n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"{x:Type ComboBox}\" >\n        <Setter Property=\"ScrollViewer.HorizontalScrollBarVisibility\" Value=\"Auto\"/>\n        <Setter Property=\"ScrollViewer.VerticalScrollBarVisibility\" Value=\"Auto\"/>\n        <Setter Property=\"ScrollViewer.CanContentScroll\" Value=\"true\"/>\n        <Setter Property=\"MinWidth\" Value=\"120\"/>\n        <Setter Property=\"MinHeight\" Value=\"20\"/>\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushComboBoxForeground}\"/>\n        <Setter Property=\"ItemContainerStyle\" Value=\"{StaticResource StyleComboBoxItem}\" />\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type ComboBox}\">\n                    <Grid>\n                        <ToggleButton Name=\"ToggleButton\" \n                                      Template=\"{StaticResource ComboBoxToggleButton}\"\n                                      Focusable=\"False\"\n                                      IsChecked=\"{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}\"\n                                      ClickMode=\"Press\">\n                        </ToggleButton>\n                        <ContentPresenter Name=\"ContentSite\" IsHitTestVisible=\"False\"  Content=\"{TemplateBinding SelectionBoxItem}\"\n                                          ContentTemplate=\"{TemplateBinding SelectionBoxItemTemplate}\"\n                                          ContentTemplateSelector=\"{TemplateBinding ItemTemplateSelector}\"\n                                          Margin=\"6,3,26,3\"\n                                          VerticalAlignment=\"Center\"\n                                          HorizontalAlignment=\"Left\" />\n                        <Popup Name=\"Popup\"\n                               Placement=\"Bottom\"\n                               IsOpen=\"{TemplateBinding IsDropDownOpen}\"\n                               AllowsTransparency=\"True\" \n                               Focusable=\"False\"\n                               PopupAnimation=\"Slide\">\n\n                            <Grid Name=\"DropDown\"\n                                  SnapsToDevicePixels=\"True\"                \n                                  MinWidth=\"{TemplateBinding ActualWidth}\"\n                                  MaxHeight=\"{TemplateBinding MaxDropDownHeight}\">\n                                <Border x:Name=\"DropDownBorder\"\n                                        Background=\"{StaticResource BrushComboBoxBackgroundPopup}\"\n                                        BorderThickness=\"1\"\n                                        BorderBrush=\"{StaticResource BrushComboBoxBorder}\"/>\n                                <ScrollViewer Margin=\"4,6,4,6\" SnapsToDevicePixels=\"True\">\n                                    <StackPanel IsItemsHost=\"True\" KeyboardNavigation.DirectionalNavigation=\"Contained\" />\n                                </ScrollViewer>\n                            </Grid>\n                        </Popup>\n                    </Grid>\n\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"HasItems\" Value=\"False\">\n                            <Setter TargetName=\"DropDownBorder\" Property=\"MinHeight\" Value=\"95\"/>\n                        </Trigger>\n                        <Trigger Property=\"IsGrouping\" Value=\"True\">\n                            <Setter Property=\"ScrollViewer.CanContentScroll\" Value=\"False\"/>\n                        </Trigger>\n                        <Trigger SourceName=\"Popup\" Property=\"Popup.AllowsTransparency\" Value=\"True\">\n                            <Setter TargetName=\"DropDownBorder\" Property=\"CornerRadius\" Value=\"0\"/>\n                            <Setter TargetName=\"DropDownBorder\" Property=\"Margin\" Value=\"0,2,0,0\"/>\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/Form.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                    xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n                                    xmlns:controls=\"clr-namespace:KeyboardAudioVisualizer.Controls\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <Style x:Key=\"StyleForm\" \n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"{x:Type controls:Form}\">\n        <Setter Property=\"VerticalAlignment\" Value=\"Top\" />\n        <Setter Property=\"HorizontalAlignment\" Value=\"Stretch\" />\n        <Setter Property=\"LabelWidth\" Value=\"112\" />\n        <Setter Property=\"ElementSpacing\" Value=\"8\" />\n        <Setter Property=\"RowSpacing\" Value=\"8\" />\n    </Style>\n\n    <Style x:Key=\"StyleLabelForm\"\n           TargetType=\"Label\">\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushForeground}\" />\n        <Setter Property=\"FontSize\" Value=\"{StaticResource FontSizeDefault}\" />\n        <Setter Property=\"FontWeight\" Value=\"Bold\" />\n        <Setter Property=\"FontFamily\" Value=\"pack://application:,,,/Resources/#Cinzel\" />\n        <Setter Property=\"VerticalContentAlignment\" Value=\"Center\" />\n        <Setter Property=\"VerticalAlignment\" Value=\"Center\" />\n        <Setter Property=\"HorizontalAlignment\" Value=\"Right\" />\n\n        <!-- DarthAffe 04.08.2017: Quite a botch, but it works ... -->\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"Label\">\n                    <TextBlock HorizontalAlignment=\"{TemplateBinding HorizontalAlignment}\"\n                               VerticalAlignment=\"{TemplateBinding VerticalAlignment}\"\n                               FontFamily=\"{TemplateBinding FontFamily}\"\n                               FontSize=\"{TemplateBinding FontSize}\"\n                               FontStyle=\"{TemplateBinding FontStyle}\"\n                               FontWeight=\"{TemplateBinding FontWeight}\"\n                               Foreground=\"{TemplateBinding Foreground}\"\n                               Text=\"{TemplateBinding Content}\" />\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <Style x:Key=\"StyleTextBlockForm\"\n           TargetType=\"TextBlock\">\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushForeground}\" />\n        <Setter Property=\"FontSize\" Value=\"{StaticResource FontSizeDefault}\" />\n        <Setter Property=\"FontFamily\" Value=\"pack://application:,,,/Resources/#Cinzel\" />\n        <Setter Property=\"VerticalAlignment\" Value=\"Center\" />\n        <Setter Property=\"TextAlignment\" Value=\"Left\" />\n    </Style>\n\n    <Style x:Key=\"StyleListBoxItemForm\"\n           TargetType=\"ListBoxItem\">\n        <Setter Property=\"MinWidth\" Value=\"0\" />\n        <Setter Property=\"HorizontalContentAlignment\" Value=\"Stretch\" />\n        <Setter Property=\"VerticalContentAlignment\" Value=\"Stretch\" />\n        <Setter Property=\"Padding\" Value=\"0,2\" />\n        <Setter Property=\"Background\" Value=\"Transparent\" />\n        <Setter Property=\"BorderThickness\" Value=\"0\" />\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"ListBoxItem\">\n                    <Border x:Name=\"Border\"\n                            Background=\"{TemplateBinding Background}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\">\n                        <ContentPresenter Margin=\"{TemplateBinding Padding}\"\n                                          HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\n                                          VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" />\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <Style x:Key=\"StyleListBoxForm\"           \n           TargetType=\"ListBox\">\n        <Setter Property=\"Focusable\" Value=\"False\" />\n        <Setter Property=\"ScrollViewer.HorizontalScrollBarVisibility\" Value=\"Disabled\" />\n        <Setter Property=\"ScrollViewer.VerticalScrollBarVisibility\" Value=\"Auto\" />\n        <Setter Property=\"ScrollViewer.CanContentScroll\" Value=\"true\" />\n        <Setter Property=\"MinHeight\" Value=\"0\" />\n        <Setter Property=\"MinWidth\" Value=\"0\" />\n        <Setter Property=\"Padding\" Value=\"0\" />\n        <Setter Property=\"HorizontalContentAlignment\" Value=\"Stretch\" />\n        <Setter Property=\"VerticalContentAlignment\" Value=\"Stretch\" />\n        <Setter Property=\"HorizontalAlignment\" Value=\"Stretch\" />\n        <Setter Property=\"VerticalAlignment\" Value=\"Stretch\" />\n        <Setter Property=\"Background\" Value=\"Transparent\" />\n        <Setter Property=\"BorderThickness\" Value=\"0\" />\n        <Setter Property=\"ItemContainerStyle\" Value=\"{StaticResource StyleListBoxItemForm}\" />\n    </Style>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/FrameworkElement.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                    xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\">\n\n    <Style x:Key=\"StyleFrameworkElement\" TargetType=\"FrameworkElement\">\n        <Setter Property=\"OverridesDefaultStyle\" Value=\"True\" />\n        <Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\n        <Setter Property=\"UseLayoutRounding\" Value=\"True\" />\n        <Setter Property=\"Validation.ErrorTemplate\" Value=\"{x:Null}\" />\n    </Style>\n\n</styles:CachedResourceDictionary>\n\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/GradientEditor.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                 xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                 xmlns:controls=\"clr-namespace:KeyboardAudioVisualizer.Controls\"\n                                 xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/ColorSelector.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <Style x:Key=\"StyleGradientStop\"\n           TargetType=\"ContentControl\">\n\n        <Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\n        <Setter Property=\"Width\" Value=\"12\" />\n        <Setter Property=\"BorderThickness\" Value=\"1\" />\n        <Setter Property=\"BorderBrush\" Value=\"#FFFFFFFF\" />\n\n        <Setter Property=\"ContentTemplate\">\n            <Setter.Value>\n                <DataTemplate DataType=\"{x:Type GradientStop}\">\n                    <Grid>\n                        <Path Stretch=\"Fill\"\n                              Data=\"M 0.5,0 L 0,0.25 L 0,1 L 1,1 L 1,0.25 Z\"\n                              StrokeThickness=\"0\"\n                              Fill=\"{StaticResource BrushChessboardSmall}\" />\n                        <Path Stretch=\"Fill\"\n                              Data=\"M 0.5,0 L 0,0.25 L 0,1 L 1,1 L 1,0.25 Z\"\n                              Stroke=\"{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=ContentControl}}\" \n                              StrokeThickness=\"{Binding BorderThickness, RelativeSource={RelativeSource AncestorType=ContentControl}}\"\n                              Fill=\"{Binding Background, RelativeSource={RelativeSource AncestorType=ContentControl}}\" />\n                    </Grid>\n                </DataTemplate>\n            </Setter.Value>\n        </Setter>\n\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding (controls:GradientEditor.IsSelected), RelativeSource={RelativeSource Self}}\" Value=\"True\">\n                <Setter Property=\"BorderBrush\" Value=\"#FF808080\" />\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"StyleGradientEditor\"\n           TargetType=\"{x:Type controls:GradientEditor}\">\n\n        <Setter Property=\"SnapsToDevicePixels\" Value=\"True\" />\n        <Setter Property=\"OverridesDefaultStyle\" Value=\"True\" />\n        <Setter Property=\"Height\" Value=\"60\" />\n        <Setter Property=\"Background\" Value=\"Transparent\" />\n        <Setter Property=\"BorderThickness\" Value=\"0\" />\n        <Setter Property=\"GradientStopStyle\" Value=\"{StaticResource StyleGradientStop}\" />\n\n        <Setter Property=\"ColorSelectorTemplate\">\n            <Setter.Value>\n                <DataTemplate DataType=\"{x:Type GradientStop}\">\n                    <GroupBox>\n                        <controls:ColorSelector Foreground=\"#FFFFFFFF\" SelectedColor=\"{Binding Color}\" />\n                    </GroupBox>\n                </DataTemplate>\n            </Setter.Value>\n        </Setter>\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type controls:GradientEditor}\">\n                    <Border BorderThickness=\"{TemplateBinding BorderThickness}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            Background=\"{TemplateBinding Background}\">\n\n                        <DockPanel>\n                            <Border DockPanel.Dock=\"Bottom\"\n                                    Height=\"16\">\n                                <Canvas x:Name=\"PART_Stops\" \n                                        VerticalAlignment=\"Stretch\" \n                                        HorizontalAlignment=\"Stretch\"\n                                        Background=\"Transparent\" />\n                            </Border>\n\n                            <Border Background=\"{StaticResource BrushChessboard}\">\n                                <Canvas x:Name=\"PART_Gradient\"\n                                        VerticalAlignment=\"Stretch\" \n                                        HorizontalAlignment=\"Stretch\"\n                                        Background=\"Transparent\" />\n                            </Border>\n                        </DockPanel>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <Style TargetType=\"{x:Type controls:GradientEditor}\" BasedOn=\"{StaticResource StyleGradientEditor}\" />\n\n</styles:CachedResourceDictionary>"
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/GroupBox.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                    xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <Style x:Key=\"StyleGroupBoxBox\" \n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"GroupBox\">\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushForeground}\" />\n        <Setter Property=\"FontSize\" Value=\"{StaticResource FontSizeDefault}\" />\n        <Setter Property=\"FontFamily\" Value=\"pack://application:,,,/Resources/#Cinzel\" />\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushBoxBackground}\" />\n        <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushBoxBorder}\" />\n        <Setter Property=\"BorderThickness\" Value=\"2\" />\n        <Setter Property=\"Padding\" Value=\"6,8\" />\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"GroupBox\">\n                    <Border BorderThickness=\"{TemplateBinding BorderThickness}\" \n                            BorderBrush=\"{TemplateBinding BorderBrush}\" \n                            Background=\"{TemplateBinding Background}\">\n                        <ContentPresenter Margin=\"{TemplateBinding Padding}\" />\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/ImageButton.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                    xmlns:buttons=\"clr-namespace:KeyboardAudioVisualizer.Controls\"\n                                    xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <Style x:Key=\"StyleImageButton\"\n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"{x:Type buttons:ImageButton}\">\n        <Setter Property=\"Padding\" Value=\"0\" />\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type buttons:ImageButton}\">\n                    <Grid Background=\"Transparent\">\n                        <Image x:Name=\"ImageNormal\" Margin=\"{TemplateBinding Padding}\" \n                               Source=\"{TemplateBinding Image}\" Stretch=\"Uniform\" />\n                        <Image x:Name=\"ImageHover\" Margin=\"{TemplateBinding Padding}\"\n                               Opacity=\"0\"\n                               Source=\"{TemplateBinding HoverImage}\" Stretch=\"Uniform\" />\n                        <Image x:Name=\"ImagePressed\" Margin=\"{TemplateBinding Padding}\"\n                               Opacity=\"0\"\n                               Source=\"{TemplateBinding PressedImage}\" Stretch=\"Uniform\" />\n                    </Grid>\n\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                            <Trigger.EnterActions>\n                                <BeginStoryboard>\n                                    <Storyboard TargetName=\"ImageHover\">\n                                        <DoubleAnimation Storyboard.TargetProperty=\"Opacity\" To=\"1.0\" Duration=\"0:0:0.150\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.EnterActions>\n                            <Trigger.ExitActions>\n                                <BeginStoryboard>\n                                    <Storyboard TargetName=\"ImageHover\">\n                                        <DoubleAnimation Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.250\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.ExitActions>\n                        </Trigger>\n                        <Trigger Property=\"IsPressed\" Value=\"True\">\n                            <Trigger.EnterActions>\n                                <BeginStoryboard>\n                                    <Storyboard TargetName=\"ImagePressed\">\n                                        <DoubleAnimation Storyboard.TargetProperty=\"Opacity\" To=\"1.0\" Duration=\"0:0:0.150\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.EnterActions>\n                            <Trigger.ExitActions>\n                                <BeginStoryboard>\n                                    <Storyboard TargetName=\"ImagePressed\">\n                                        <DoubleAnimation Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.250\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.ExitActions>\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n\n    </Style>\n\n    <Style x:Key=\"StyleImageButtonWithOpacity\" \n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"{x:Type buttons:ImageButton}\">\n        <Setter Property=\"Opacity\" Value=\"0.66\" />\n        <Setter Property=\"Padding\" Value=\"0\" />\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type buttons:ImageButton}\">\n                    <Grid Background=\"Transparent\">\n                        <Image x:Name=\"ImageNormal\" Margin=\"{TemplateBinding Padding}\" \n                               Source=\"{TemplateBinding Image}\" Stretch=\"Uniform\" />\n                    </Grid>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n\n        <Style.Triggers>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                <Trigger.EnterActions>\n                    <BeginStoryboard>\n                        <Storyboard>\n                            <DoubleAnimation Storyboard.TargetProperty=\"Opacity\" To=\"1.0\" Duration=\"0:0:0.150\" />\n                        </Storyboard>\n                    </BeginStoryboard>\n                </Trigger.EnterActions>\n                <Trigger.ExitActions>\n                    <BeginStoryboard>\n                        <Storyboard>\n                            <DoubleAnimation Storyboard.TargetProperty=\"Opacity\" Duration=\"0:0:0.250\" />\n                        </Storyboard>\n                    </BeginStoryboard>\n                </Trigger.ExitActions>\n            </Trigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"StyleImageButtonMenu\"\n           BasedOn=\"{StaticResource StyleImageButton}\"\n           TargetType=\"{x:Type buttons:ImageButton}\">\n        <Setter Property=\"Width\" Value=\"180\" />\n        <Setter Property=\"Height\" Value=\"90\" />\n        <Setter Property=\"Margin\" Value=\"0,16\" />\n        <Setter Property=\"HorizontalAlignment\" Value=\"Center\" />\n    </Style>\n\n</styles:CachedResourceDictionary>\n    \n    \n    \n    "
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/Navigation.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                    xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n                                    xmlns:controls=\"clr-namespace:Transitionals.Controls;assembly=Transitionals\"\n                                    xmlns:transitions=\"clr-namespace:Transitionals.Transitions;assembly=Transitionals\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <transitions:FadeTransition x:Key=\"TransitionFade\" Duration=\"0:0:0.200\" />\n\n    <Style x:Key=\"StyleTabControlNavigation\"\n           TargetType=\"TabControl\">\n\n        <Setter Property=\"BorderThickness\" Value=\"0\" />\n        <Setter Property=\"Background\" Value=\"Transparent\" />\n        <Setter Property=\"Margin\" Value=\"0,-72,0,0\" />\n        <Setter Property=\"HorizontalContentAlignment\" Value=\"Stretch\" />\n        <Setter Property=\"VerticalContentAlignment\" Value=\"Stretch\" />\n\n        <!-- DarthAffe 04.08.2017: The TabControl is a shitty one to style - if unused parts are removed it throws exceptions ... -->\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"TabControl\">\n                    <Grid ClipToBounds=\"true\" SnapsToDevicePixels=\"true\" KeyboardNavigation.TabNavigation=\"Local\">\n                        <Grid.ColumnDefinitions>\n                            <ColumnDefinition x:Name=\"ColumnDefinition0\" />\n                            <ColumnDefinition x:Name=\"ColumnDefinition1\" Width=\"0\" />\n                        </Grid.ColumnDefinitions>\n                        <Grid.RowDefinitions>\n                            <RowDefinition x:Name=\"RowDefinition0\" Height=\"Auto\" />\n                            <RowDefinition x:Name=\"RowDefinition1\" Height=\"*\" />\n                        </Grid.RowDefinitions>\n                        <TabPanel x:Name=\"HeaderPanel\" \n                                  Grid.Row=\"0\" \n                                  Grid.Column=\"0\" \n                                  Margin=\"82,2,2,0\" \n                                  Panel.ZIndex=\"1\"\n                                  IsItemsHost=\"true\" \n                                  KeyboardNavigation.TabIndex=\"1\" />\n                        <Border x:Name=\"ContentPanel\" \n                                Grid.Column=\"0\" \n                                Grid.Row=\"1\" \n                                Margin=\"4\"\n                                BorderBrush=\"{TemplateBinding BorderBrush}\" \n                                BorderThickness=\"{TemplateBinding BorderThickness}\" \n                                Background=\"{TemplateBinding Background}\" \n                                KeyboardNavigation.DirectionalNavigation=\"Contained\" \n                                KeyboardNavigation.TabIndex=\"2\" \n                                KeyboardNavigation.TabNavigation=\"Local\">\n                            <controls:TransitionElement x:Name=\"PART_SelectedContentHost\" \n                                                        Margin=\"{TemplateBinding Padding}\" \n                                                        SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\"\n                                                        Transition=\"{StaticResource TransitionFade}\" \n                                                        Content=\"{TemplateBinding SelectedContent}\" />\n                        </Border>\n                    </Grid>\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"TabStripPlacement\" Value=\"Bottom\">\n                            <Setter Property=\"Grid.Row\" TargetName=\"HeaderPanel\" Value=\"1\" />\n                            <Setter Property=\"Grid.Row\" TargetName=\"ContentPanel\" Value=\"0\" />\n                            <Setter Property=\"Height\" TargetName=\"RowDefinition0\" Value=\"*\" />\n                            <Setter Property=\"Height\" TargetName=\"RowDefinition1\" Value=\"Auto\" />\n                            <Setter Property=\"Margin\" TargetName=\"HeaderPanel\" Value=\"2,0,2,2\" />\n                        </Trigger>\n                        <Trigger Property=\"TabStripPlacement\" Value=\"Left\">\n                            <Setter Property=\"Grid.Row\" TargetName=\"HeaderPanel\" Value=\"0\" />\n                            <Setter Property=\"Grid.Row\" TargetName=\"ContentPanel\" Value=\"0\" />\n                            <Setter Property=\"Grid.Column\" TargetName=\"HeaderPanel\" Value=\"0\" />\n                            <Setter Property=\"Grid.Column\" TargetName=\"ContentPanel\" Value=\"1\" />\n                            <Setter Property=\"Width\" TargetName=\"ColumnDefinition0\" Value=\"Auto\" />\n                            <Setter Property=\"Width\" TargetName=\"ColumnDefinition1\" Value=\"*\" />\n                            <Setter Property=\"Height\" TargetName=\"RowDefinition0\" Value=\"*\" />\n                            <Setter Property=\"Height\" TargetName=\"RowDefinition1\" Value=\"0\" />\n                            <Setter Property=\"Margin\" TargetName=\"HeaderPanel\" Value=\"2,2,0,2\" />\n                        </Trigger>\n                        <Trigger Property=\"TabStripPlacement\" Value=\"Right\">\n                            <Setter Property=\"Grid.Row\" TargetName=\"HeaderPanel\" Value=\"0\" />\n                            <Setter Property=\"Grid.Row\" TargetName=\"ContentPanel\" Value=\"0\" />\n                            <Setter Property=\"Grid.Column\" TargetName=\"HeaderPanel\" Value=\"1\" />\n                            <Setter Property=\"Grid.Column\" TargetName=\"ContentPanel\" Value=\"0\" />\n                            <Setter Property=\"Width\" TargetName=\"ColumnDefinition0\" Value=\"*\" />\n                            <Setter Property=\"Width\" TargetName=\"ColumnDefinition1\" Value=\"Auto\" />\n                            <Setter Property=\"Height\" TargetName=\"RowDefinition0\" Value=\"*\" />\n                            <Setter Property=\"Height\" TargetName=\"RowDefinition1\" Value=\"0\" />\n                            <Setter Property=\"Margin\" TargetName=\"HeaderPanel\" Value=\"0,2,2,2\" />\n                        </Trigger>\n                        <Trigger Property=\"IsEnabled\" Value=\"false\">\n                            <Setter Property=\"Foreground\" Value=\"{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}\" />\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <Style x:Key=\"StyleTabItemNavigation\" \n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"TabItem\">\n\n        <Setter Property=\"Height\" Value=\"72\" />\n        <Setter Property=\"FontSize\" Value=\"{StaticResource FontSizeNavigation}\" />\n        <Setter Property=\"FontFamily\" Value=\"pack://application:,,,/Resources/#Cinzel Decorative\" />\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"TabItem\">\n                    <Border Margin=\"8,0\"\n                            Padding=\"4\" \n                            IsHitTestVisible=\"True\"\n                            BorderThickness=\"0\"\n                            Background=\"Transparent\">\n                        <Grid>\n                            <TextBlock x:Name=\"TxtBlurred\" \n                                       VerticalAlignment=\"Center\" \n                                       HorizontalAlignment=\"Center\" \n                                       FontWeight=\"Black\" \n                                       Opacity=\"0\"\n                                       Foreground=\"{StaticResource BrushNavigationItemBlur}\"\n                                       Text=\"{TemplateBinding Header}\">\n                                <TextBlock.Effect>\n                                    <BlurEffect Radius=\"{StaticResource DoubleNavigationBlurRadius}\" />\n                                </TextBlock.Effect>\n                            </TextBlock>\n                            <TextBlock VerticalAlignment=\"Center\" \n                                       HorizontalAlignment=\"Center\" \n                                       Foreground=\"{StaticResource BrushNavigationItem}\"\n                                       Text=\"{TemplateBinding Header}\" />\n                        </Grid>\n                    </Border>\n\n                    <ControlTemplate.Triggers>\n                        <!--<DataTrigger Binding=\"{Binding RelativeSource={RelativeSource PreviousData}}\" Value=\"{x:Null}\">\n                            <Setter Property=\"Margin\" Value=\"80,0,0,0\" />\n                        </DataTrigger>-->\n                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                            <Trigger.EnterActions>\n                                <BeginStoryboard>\n                                    <Storyboard TargetName=\"TxtBlurred\">\n                                        <DoubleAnimation Storyboard.TargetProperty=\"Opacity\" To=\"0.5\" Duration=\"0:0:0.150\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.EnterActions>\n                            <Trigger.ExitActions>\n                                <BeginStoryboard>\n                                    <Storyboard TargetName=\"TxtBlurred\">\n                                        <DoubleAnimation Storyboard.TargetProperty=\"Opacity\" Duration=\"0:0:0.250\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.ExitActions>\n                        </Trigger>\n                        <Trigger Property=\"IsSelected\" Value=\"True\">\n                            <Trigger.EnterActions>\n                                <BeginStoryboard>\n                                    <Storyboard TargetName=\"TxtBlurred\">\n                                        <DoubleAnimation Storyboard.TargetProperty=\"Opacity\" To=\"1.0\" Duration=\"0:0:0.150\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.EnterActions>\n                            <Trigger.ExitActions>\n                                <BeginStoryboard>\n                                    <Storyboard TargetName=\"TxtBlurred\">\n                                        <DoubleAnimation Storyboard.TargetProperty=\"Opacity\" Duration=\"0:0:0.250\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.ExitActions>\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <Style BasedOn=\"{StaticResource StyleTabItemNavigation}\" TargetType=\"TabItem\" />\n\n</styles:CachedResourceDictionary>\n    \n    \n    \n    "
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/Slider.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                    xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n                                    xmlns:attached=\"clr-namespace:KeyboardAudioVisualizer.Attached\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <Style x:Key=\"StyleSlider\" \n           BasedOn=\"{StaticResource {x:Type Slider}}\"\n           TargetType=\"Slider\">\n        \n        <!-- ReSharper disable Xaml.RedundantStyledValue -  -->\n        <Setter Property=\"attached:SliderValue.IsShown\" Value=\"True\" />\n        <Setter Property=\"attached:SliderValue.Background\" Value=\"{StaticResource BrushTooltipBackground}\" />\n        <Setter Property=\"attached:SliderValue.BorderBrush\" Value=\"{StaticResource BrushTooltipBorder}\" />\n        <Setter Property=\"attached:SliderValue.Foreground\" Value=\"{StaticResource BrushTooltipForeground}\" />\n        <Setter Property=\"attached:SliderValue.FontSize\" Value=\"{StaticResource FontSizeTooltip}\" />\n        <Setter Property=\"attached:SliderValue.Font\" Value=\"pack://application:,,,/Resources/#Cinzel\" />\n        <!-- ReSharper restore Xaml.RedundantStyledValue -->\n\n        <Style.Triggers>\n            <Trigger Property=\"IsEnabled\" Value=\"False\">\n                <Setter Property=\"Opacity\" Value=\"0.2\" />\n            </Trigger>\n        </Style.Triggers>\n    </Style>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/Theme.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:sys=\"clr-namespace:System;assembly=mscorlib\"\n                    xmlns:presentationOptions=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation/options\">\n    <!-- ### Colors ### -->\n    <Color x:Key=\"ColorGainsboro\">#FFDCDCDC</Color>\n    <Color x:Key=\"ColorBuckinghamGray\">#FF2A2A2A</Color>\n    <Color x:Key=\"ColorBuckinghamGrayTransparent\">#B82A2A2A</Color>\n    <Color x:Key=\"ColorJetBlack\">#111111</Color>\n    <Color x:Key=\"ColorJetBlackTransparent\">#B8111111</Color>\n    <Color x:Key=\"ColorJetBlackSuperTransparent\">#60111111</Color>\n    <Color x:Key=\"ColorBlackTransparent\">#50000000</Color>\n    <Color x:Key=\"ColorBananaYellow\">#FFE135</Color>\n\n    <!-- ### Brushes ### -->\n    <SolidColorBrush x:Key=\"BrushBackground\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorJetBlack}\" />\n    <SolidColorBrush x:Key=\"BrushForeground\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorGainsboro}\" />\n\n    <!-- Window -->\n    <SolidColorBrush x:Key=\"BrushWindowBorder\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorBlackTransparent}\" />\n    <SolidColorBrush x:Key=\"BrushWindowBackground\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorJetBlack}\" />\n\n    <!-- Box -->\n    <SolidColorBrush x:Key=\"BrushBoxBorder\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorJetBlackTransparent}\" />\n    <SolidColorBrush x:Key=\"BrushBoxBackground\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorJetBlackSuperTransparent}\" />\n\n    <!-- ToolTip -->\n    <SolidColorBrush x:Key=\"BrushTooltipForeground\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorGainsboro}\" />\n    <SolidColorBrush x:Key=\"BrushTooltipBorder\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorJetBlack}\" />\n    <SolidColorBrush x:Key=\"BrushTooltipBackground\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorJetBlackTransparent}\" />\n\n    <!-- GroupBox -->\n    <SolidColorBrush x:Key=\"BrushComboBoxForeground\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorGainsboro}\" />\n    <SolidColorBrush x:Key=\"BrushComboBoxBackground\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorBuckinghamGrayTransparent}\" />\n    <SolidColorBrush x:Key=\"BrushComboBoxBackgroundPopup\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorBuckinghamGray}\" />\n    <SolidColorBrush x:Key=\"BrushComboBoxBorder\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorJetBlackTransparent}\" />\n    <SolidColorBrush x:Key=\"BrushComboBoxHover\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorJetBlack}\" />\n\n    <!-- Button -->\n    <SolidColorBrush x:Key=\"BrushButtonForeground\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorGainsboro}\" />\n    <SolidColorBrush x:Key=\"BrushButtonBackground\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorBuckinghamGrayTransparent}\" />\n    <SolidColorBrush x:Key=\"BrushButtonBorder\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorJetBlackTransparent}\" />\n    <SolidColorBrush x:Key=\"BrushButtonHover\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorJetBlack}\" />\n\n    <!-- Navigation -->\n    <SolidColorBrush x:Key=\"BrushNavigationItem\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorGainsboro}\" />\n    <SolidColorBrush x:Key=\"BrushNavigationItemBlur\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorBananaYellow}\" />\n\n    <!-- Equalizer -->\n    <SolidColorBrush x:Key=\"BrushEqualizerForeground\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorGainsboro}\" />\n    <SolidColorBrush x:Key=\"BrushEqualizerBorder\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorBuckinghamGrayTransparent}\" />\n    <SolidColorBrush x:Key=\"BrushEqualizerBackground\" presentationOptions:Freeze=\"True\" Color=\"{StaticResource ColorJetBlackTransparent}\" />\n\n\n    <!-- ### Border'n'stuff ### -->\n    <sys:Double x:Key=\"DoubleNavigationBlurRadius\">40</sys:Double>\n\n\n    <!-- ### Fonts ### -->\n    <sys:Double x:Key=\"FontSizeDefault\">14</sys:Double>\n    <sys:Double x:Key=\"FontSizeTooltip\">14</sys:Double>\n    <sys:Double x:Key=\"FontSizeNavigation\">22</sys:Double>\n</ResourceDictionary>\n"
  },
  {
    "path": "KeyboardAudioVisualizer/Styles/ToolTip.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                    xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <Style x:Key=\"StyleToolTip\" \n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"{x:Type ToolTip}\">\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushTooltipForeground}\" />\n        <Setter Property=\"FontSize\" Value=\"{StaticResource FontSizeTooltip}\" />\n        <Setter Property=\"FontFamily\" Value=\"pack://application:,,,/Resources/#Cinzel\" />\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type ToolTip}\">\n                    <Border BorderThickness=\"1\" \n                            BorderBrush=\"{StaticResource BrushTooltipBorder}\" \n                            Background=\"{StaticResource BrushTooltipBackground}\">\n                        <ContentPresenter Margin=\"6,4\" />\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/UI/Configuration/BeatConfiguration.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                 xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                 xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n                                 xmlns:visualizationProvider=\"clr-namespace:KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider\"\n                                 xmlns:controls=\"clr-namespace:KeyboardAudioVisualizer.Controls\"\n                                 xmlns:system=\"clr-namespace:System;assembly=mscorlib\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <DataTemplate DataType=\"{x:Type visualizationProvider:BeatVisualizationProviderConfiguration}\">\n        <Grid>\n            <Grid.Resources>\n                <Style BasedOn=\"{StaticResource StyleLabelForm}\" TargetType=\"Label\" />\n                <Style BasedOn=\"{StaticResource StyleTextBlockForm}\" TargetType=\"TextBlock\" />\n                <Style BasedOn=\"{StaticResource StyleListBoxForm}\" TargetType=\"ListBox\" />\n            </Grid.Resources>\n\n            <controls:Form LabelWidth=\"240\">\n                <Label controls:Form.IsLabel=\"True\" Content=\"No configuration available ...\" />\n            </controls:Form>\n\n        </Grid>\n    </DataTemplate>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/UI/Configuration/FrequencyBarsConfiguration.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                 xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                 xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n                                 xmlns:visualizationProvider=\"clr-namespace:KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider\"\n                                 xmlns:controls=\"clr-namespace:KeyboardAudioVisualizer.Controls\"\n                                 xmlns:system=\"clr-namespace:System;assembly=mscorlib\"\n                                 xmlns:attached=\"clr-namespace:KeyboardAudioVisualizer.Attached\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <DataTemplate DataType=\"{x:Type visualizationProvider:FrequencyBarsVisualizationProviderConfiguration}\">\n        <Grid>\n            <Grid.Resources>\n                <Style BasedOn=\"{StaticResource StyleLabelForm}\" TargetType=\"Label\" />\n                <Style BasedOn=\"{StaticResource StyleTextBlockForm}\" TargetType=\"TextBlock\" />\n                <Style BasedOn=\"{StaticResource StyleListBoxForm}\" TargetType=\"ListBox\" />\n\n                <ObjectDataProvider x:Key=\"SpectrumModes\" MethodName=\"GetValues\" ObjectType=\"{x:Type system:Enum}\">\n                    <ObjectDataProvider.MethodParameters>\n                        <x:Type TypeName=\"visualizationProvider:SpectrumMode\"/>\n                    </ObjectDataProvider.MethodParameters>\n                </ObjectDataProvider>\n\n                <ObjectDataProvider x:Key=\"ValueModes\" MethodName=\"GetValues\" ObjectType=\"{x:Type system:Enum}\">\n                    <ObjectDataProvider.MethodParameters>\n                        <x:Type TypeName=\"visualizationProvider:ValueMode\"/>\n                    </ObjectDataProvider.MethodParameters>\n                </ObjectDataProvider>\n            </Grid.Resources>\n\n            <Grid.ColumnDefinitions>\n                <ColumnDefinition Width=\"*\" />\n                <ColumnDefinition Width=\"8\" />\n                <ColumnDefinition Width=\"*\" />\n                <ColumnDefinition Width=\"8\" />\n                <ColumnDefinition Width=\"*\" />\n            </Grid.ColumnDefinitions>\n\n            <controls:Form Grid.Column=\"0\">\n                <Label controls:Form.IsLabel=\"True\" Content=\"Spectrum:\" />\n                <ComboBox controls:Form.Fill=\"True\"\n                          ItemsSource=\"{Binding Source={StaticResource SpectrumModes}}\"\n                          SelectedItem=\"{Binding SpectrumMode}\"\n                          ToolTip=\"Defines how the spectrum is grouped together.&#x0a; - Linear: Each bar represents the same range of frequencies.&#x0a; - Logarithmic: The higher the frequencies get the wider the range of grouped frequencies.&#x0a; - Gamma: Uses a configurable Gamma-Value to group the frequencies.\" />\n\n                <Label controls:Form.IsLabel=\"True\" Content=\"Value:\" />\n                <ComboBox controls:Form.Fill=\"True\"\n                          ItemsSource=\"{Binding Source={StaticResource ValueModes}}\"\n                          SelectedItem=\"{Binding ValueMode}\"\n                          ToolTip=\"Defines how the value of a bar is calculated.&#x0a; - Sum: Sums the power of all frequencies grouped in the bar using all available data.&#x0a; - Max: Uses the maximum value in each group making sure peaks are caught well.&#x0a; - Average: Uses the average of all frequencies grouped in the bar. \" />\n\n                <Label controls:Form.IsLabel=\"True\" Content=\"Bars:\" />\n                <Slider controls:Form.Fill=\"True\" Minimum=\"1\" Maximum=\"96\" IsSnapToTickEnabled=\"True\" TickFrequency=\"1\" TickPlacement=\"BottomRight\"\n                        Value=\"{Binding Bars}\"\n                        ToolTip=\"The number of bars used to represent the spectrum..\" />\n            </controls:Form>\n\n            <controls:Form Grid.Column=\"2\">\n                <Label controls:Form.IsLabel=\"True\" Content=\"Min Freq.:\" />\n                <Slider controls:Form.Fill=\"True\" Minimum=\"0\" Maximum=\"22100\" IsSnapToTickEnabled=\"True\" TickFrequency=\"10\" TickPlacement=\"None\"\n                        Value=\"{Binding MinFrequency}\"\n                        attached:SliderValue.Unit=\"Hz\"\n                        ToolTip=\"The minimum frequency used in the graph.\" />\n\n                <Label controls:Form.IsLabel=\"True\" Content=\"Max Freq.:\" />\n                <Slider controls:Form.Fill=\"True\" Minimum=\"0\" Maximum=\"22100\" IsSnapToTickEnabled=\"True\" TickFrequency=\"10\" TickPlacement=\"None\"\n                        Value=\"{Binding MaxFrequency}\"\n                        attached:SliderValue.Unit=\"Hz\"\n                        ToolTip=\"The maximum frequency used in the graph.\" />\n\n                <Label controls:Form.IsLabel=\"True\" Content=\"Gamma:\" />\n                <Slider controls:Form.Fill=\"True\" Minimum=\"1\" Maximum=\"6\" IsSnapToTickEnabled=\"True\" TickFrequency=\"0.25\" TickPlacement=\"BottomRight\"\n                        IsEnabled=\"{Binding SpectrumMode, Converter={StaticResource EqualsToBoolConverter}, ConverterParameter={x:Static visualizationProvider:SpectrumMode.Gamma}}\"\n                        Value=\"{Binding Gamma}\"\n                        ToolTip=\"Only used if 'Gamma' is selected as spectrum!&#x0a;Higher values cause more compression of high frequencies.\" />\n            </controls:Form>\n\n            <controls:Form Grid.Column=\"4\">\n                <Label controls:Form.IsLabel=\"True\" controls:Form.LineBreaks=\"1\" Content=\"Reference:\" />\n                <Slider controls:Form.Fill=\"True\" Minimum=\"1\" Maximum=\"240\" IsSnapToTickEnabled=\"True\" TickFrequency=\"1\" TickPlacement=\"None\"\n                        Value=\"{Binding ReferenceLevel}\"\n                        attached:SliderValue.Unit=\"dB\"\n                        ToolTip=\"The reference value used to calculate the power of each bar.&#x0a;(You can read this as 'scaling')\" />\n\n                <Label controls:Form.IsLabel=\"True\" controls:Form.LineBreaks=\"1\" Content=\"Smoothing:\" />\n                <Slider controls:Form.Fill=\"True\" Minimum=\"1\" Maximum=\"10\" IsSnapToTickEnabled=\"True\" TickFrequency=\"0.5\" TickPlacement=\"BottomRight\"\n                        Value=\"{Binding Smoothing}\"\n                        ToolTip=\"Smooths the graph to prevent flickering.&#x0a;Low values will cause a hectic fast plot, high values a slow one without peaks.\" />\n\n                <Label controls:Form.IsLabel=\"True\" controls:Form.LineBreaks=\"1\" Content=\"Emphasize:\" />\n                <Slider controls:Form.Fill=\"True\" Minimum=\"0\" Maximum=\"2\" IsSnapToTickEnabled=\"True\" TickFrequency=\"0.05\" TickPlacement=\"BottomRight\"\n                        Value=\"{Binding EmphasisePeaks}\"\n                        ToolTip=\"Emphasizes peaks. The higher the value, the bigger the difference between a 'loud-bar' and a 'quite-bar'.\" />\n            </controls:Form>\n        </Grid>\n    </DataTemplate>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/UI/Configuration/LevelConfiguration.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                 xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                 xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n                                 xmlns:visualizationProvider=\"clr-namespace:KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider\"\n                                 xmlns:controls=\"clr-namespace:KeyboardAudioVisualizer.Controls\"\n                                 xmlns:system=\"clr-namespace:System;assembly=mscorlib\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <DataTemplate DataType=\"{x:Type visualizationProvider:LevelVisualizationProviderConfiguration}\">\n        <Grid>\n            <Grid.Resources>\n                <Style BasedOn=\"{StaticResource StyleLabelForm}\" TargetType=\"Label\" />\n                <Style BasedOn=\"{StaticResource StyleTextBlockForm}\" TargetType=\"TextBlock\" />\n                <Style BasedOn=\"{StaticResource StyleListBoxForm}\" TargetType=\"ListBox\" />\n\n                <ObjectDataProvider x:Key=\"ConversionModes\" MethodName=\"GetValues\" ObjectType=\"{x:Type system:Enum}\">\n                    <ObjectDataProvider.MethodParameters>\n                        <x:Type TypeName=\"visualizationProvider:ConversionMode\"/>\n                    </ObjectDataProvider.MethodParameters>\n                </ObjectDataProvider>\n            </Grid.Resources>\n\n            <Grid.ColumnDefinitions>\n                <ColumnDefinition Width=\"*\" />\n                <ColumnDefinition Width=\"8\" />\n                <ColumnDefinition Width=\"*\" />\n                <ColumnDefinition Width=\"8\" />\n                <ColumnDefinition Width=\"*\" />\n            </Grid.ColumnDefinitions>\n\n            <controls:Form Grid.Column=\"0\">\n                <Label controls:Form.IsLabel=\"True\" Content=\"Calculation:\" />\n                <ComboBox controls:Form.Fill=\"True\"\n                          ItemsSource=\"{Binding Source={StaticResource ConversionModes}}\"\n                          SelectedItem=\"{Binding ConversionMode}\"\n                          ToolTip=\"Defines how the RMS of the audio is plotted.&#x0a;Exponential has the widest range of peaks while linear has the least.\" />\n            </controls:Form>\n\n            <controls:Form Grid.Column=\"2\">\n                <Label controls:Form.IsLabel=\"True\" Content=\"Scale:\" />\n                <Slider controls:Form.Fill=\"True\" Minimum=\"1\" Maximum=\"40\" IsSnapToTickEnabled=\"True\" TickFrequency=\"0.5\" TickPlacement=\"BottomRight\"\n                        Value=\"{Binding Scale}\"\n                        ToolTip=\"Scales the whole graph.\" />\n            </controls:Form>\n\n            <controls:Form Grid.Column=\"4\">\n                <Label controls:Form.IsLabel=\"True\" controls:Form.LineBreaks=\"1\" Content=\"Smoothing:\" />\n                <Slider controls:Form.Fill=\"True\" Minimum=\"1\" Maximum=\"10\" IsSnapToTickEnabled=\"True\" TickFrequency=\"0.5\" TickPlacement=\"BottomRight\"\n                        Value=\"{Binding Smoothing}\"\n                        ToolTip=\"Smooths the plot to prevent flickering.&#x0a;Low values will cause a hectic fast plot, high values a slow one without peaks.\" />\n            </controls:Form>\n        </Grid>\n    </DataTemplate>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/UI/ConfigurationViewModel.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.Reflection;\nusing KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing KeyboardAudioVisualizer.Helper;\nusing RGB.NET.Core;\n\nnamespace KeyboardAudioVisualizer.UI\n{\n    public class ConfigurationViewModel : AbstractBindable\n    {\n        #region Properties & Fields\n\n        public Version Version => Assembly.GetEntryAssembly().GetName().Version;\n\n        public double UpdateRate\n        {\n            get => 1.0 / ApplicationManager.Instance.UpdateTrigger.UpdateFrequency;\n            set\n            {\n                double val = MathHelper.Clamp(value, 1, 60);\n                ApplicationManager.Instance.Settings.UpdateRate = val;\n                ApplicationManager.Instance.UpdateTrigger.UpdateFrequency = 1.0 / val;\n                OnPropertyChanged();\n            }\n        }\n\n        public bool EnableAudioPrescale\n        {\n            get => ApplicationManager.Instance.Settings.EnableAudioPrescale;\n            set\n            {\n                ApplicationManager.Instance.Settings.EnableAudioPrescale = value;\n                OnPropertyChanged();\n            }\n        }\n\n        public VisualizationType SelectedPrimaryVisualization\n        {\n            get => ApplicationManager.Instance.Settings[VisualizationIndex.Primary].SelectedVisualization;\n            set\n            {\n                ApplicationManager.Instance.Settings[VisualizationIndex.Primary].SelectedVisualization = value;\n                ApplicationManager.Instance.ApplyVisualization(VisualizationIndex.Primary, value);\n            }\n        }\n\n        public VisualizationType SelectedSecondaryVisualization\n        {\n            get => ApplicationManager.Instance.Settings[VisualizationIndex.Secondary].SelectedVisualization;\n            set\n            {\n                ApplicationManager.Instance.Settings[VisualizationIndex.Secondary].SelectedVisualization = value;\n                ApplicationManager.Instance.ApplyVisualization(VisualizationIndex.Secondary, value);\n            }\n        }\n\n        public VisualizationType SelectedTertiaryVisualization\n        {\n            get => ApplicationManager.Instance.Settings[VisualizationIndex.Tertiary].SelectedVisualization;\n            set\n            {\n                ApplicationManager.Instance.Settings[VisualizationIndex.Tertiary].SelectedVisualization = value;\n                ApplicationManager.Instance.ApplyVisualization(VisualizationIndex.Tertiary, value);\n            }\n        }\n\n        #endregion\n\n        #region Commands\n\n        private ActionCommand _openHomepageCommand;\n        public ActionCommand OpenHomepageCommand => _openHomepageCommand ?? (_openHomepageCommand = new ActionCommand(OpenHomepage));\n\n        #endregion\n\n        #region Constructors\n\n        #endregion\n\n        #region Methods\n\n        private void OpenHomepage() => Process.Start(\"https://github.com/DarthAffe/KeyboardAudioVisualizer\");\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/UI/ConfigurationWindow.xaml",
    "content": "﻿<controls:BlurredDecorationWindow x:Class=\"KeyboardAudioVisualizer.UI.ConfigurationWindow\"\n                                  xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                  xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                  xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n                                  xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n                                  xmlns:ui=\"clr-namespace:KeyboardAudioVisualizer.UI\"\n                                  xmlns:controls=\"clr-namespace:KeyboardAudioVisualizer.Controls\"\n                                  xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n                                  xmlns:core=\"clr-namespace:RGB.NET.Core;assembly=RGB.NET.Core\"\n                                  xmlns:keyboardAudioVisualizer=\"clr-namespace:KeyboardAudioVisualizer\"\n                                  xmlns:converter=\"clr-namespace:KeyboardAudioVisualizer.Converter\"\n                                  xmlns:visualizationProvider=\"clr-namespace:KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider\"\n                                  xmlns:sys=\"clr-namespace:System;assembly=mscorlib\"\n                                  xmlns:attached=\"clr-namespace:KeyboardAudioVisualizer.Attached\"\n                                  xmlns:helper=\"clr-namespace:KeyboardAudioVisualizer.Helper\"\n                                  mc:Ignorable=\"d\"\n                                  Title=\"Keyboard Audio-Visualizer # Configuration\" \n                                  Icon=\"pack://application:,,,/KeyboardAudioVisualizer;component/Resources/Icon.ico\"\n                                  IconCommand=\"{Binding OpenHomepageCommand}\"\n                                  Width=\"1280\"  Height=\"720\"\n                                  Closed=\"ConfigurationWindow_OnClosed\">\n\n    <controls:BlurredDecorationWindow.Resources>\n        <styles:CachedResourceDictionary>\n            <styles:CachedResourceDictionary.MergedDictionaries>\n                <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Navigation.xaml\" />\n                <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/UI/Configuration/FrequencyBarsConfiguration.xaml\" />\n                <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/UI/Visualization/FrequencyBarsVisualization.xaml\" />\n                <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/UI/Configuration/LevelConfiguration.xaml\" />\n                <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/UI/Visualization/LevelVisualization.xaml\" />\n                <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/UI/Configuration/BeatConfiguration.xaml\" />\n                <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/UI/Visualization/BeatVisualization.xaml\" />\n            </styles:CachedResourceDictionary.MergedDictionaries>\n\n            <converter:VisualizationTypeSelectableConverter x:Key=\"VisualizationProviderSelectableConverter\" />\n            <converter:VisualizationProviderDisplayNameConverter x:Key=\"VisualizationProviderDisplayNameConverter\" />\n            <converter:VisualizationToLastChildFillConverter x:Key=\"VisualizationToLastChildFillConverter\" />\n\n            <DataTemplate x:Key=\"DataTemplateVisualizationSelection\" DataType=\"{x:Type visualizationProvider:VisualizationType}\">\n                <TextBlock Text=\"{Binding Converter={StaticResource VisualizationProviderDisplayNameConverter}}\" />\n            </DataTemplate>\n\n            <ObjectDataProvider x:Key=\"DataProviderVisualizationTypes\" ObjectType=\"{x:Type sys:Enum}\" MethodName=\"GetValues\">\n                <ObjectDataProvider.MethodParameters>\n                    <x:Type TypeName=\"visualizationProvider:VisualizationType\" />\n                </ObjectDataProvider.MethodParameters>\n            </ObjectDataProvider>\n        </styles:CachedResourceDictionary>\n    </controls:BlurredDecorationWindow.Resources>\n\n    <controls:BlurredDecorationWindow.DataContext>\n        <ui:ConfigurationViewModel />\n    </controls:BlurredDecorationWindow.DataContext>\n\n    <TabControl Style=\"{StaticResource StyleTabControlNavigation}\">\n        <TabItem Header=\"Keyboard\">\n            <AdornerDecorator>\n                <DockPanel LastChildFill=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Primary], Converter={StaticResource VisualizationToLastChildFillConverter}}\">\n                    <GroupBox DockPanel.Dock=\"Top\">\n                        <controls:GradientEditor Gradient=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Settings[(helper:VisualizationIndex)Primary].Gradient}\"\n                                                 ToolTip=\"Defines the gradient that's drawed on the device. Usage:&#x0a;   Left click inside the preview to add a new stop.&#x0a;   Left-click stop to change the color or move it.&#x0a;   Right-click stop to remove it.&#x0a;\" />\n                    </GroupBox>\n\n                    <GroupBox Margin=\"0,8,0,0\" DockPanel.Dock=\"Top\">\n                        <controls:Form>\n                            <controls:Form.Resources>\n                                <Style BasedOn=\"{StaticResource StyleLabelForm}\" TargetType=\"Label\" />\n                                <Style BasedOn=\"{StaticResource StyleTextBlockForm}\" TargetType=\"TextBlock\" />\n                                <Style BasedOn=\"{StaticResource StyleListBoxForm}\" TargetType=\"ListBox\" />\n                            </controls:Form.Resources>\n\n                            <Label controls:Form.IsLabel=\"True\" Content=\"Visualization: \" />\n                            <ComboBox controls:Form.Fill=\"True\" SelectedIndex=\"0\" \n                                      ItemTemplate=\"{StaticResource DataTemplateVisualizationSelection}\"\n                                      ItemsSource=\"{Binding Source={StaticResource DataProviderVisualizationTypes},\n                                                            Converter={StaticResource VisualizationProviderSelectableConverter}, ConverterParameter={x:Static core:RGBDeviceType.Keyboard}}\"\n                                      SelectedItem=\"{Binding SelectedPrimaryVisualization}\"\n                                      ToolTip=\"The visualization shown on the device.&#x0a; - Frequency Bars: Shows vertical bars representing the frequencies of the song.&#x0a; - Level: Shows two horizontal bars representing the loudness of the song (left and right).&#x0a; - Beat: Shows a flash to the beat of the song (this doesn't work too well right now :()\" />\n                        </controls:Form>\n                    </GroupBox>\n\n                    <GroupBox Margin=\"0,8,0,0\" DockPanel.Dock=\"Top\">\n                        <ContentControl Content=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Primary].Configuration}\" />\n                    </GroupBox>\n\n                    <GroupBox Margin=\"0,8,0,0\" DockPanel.Dock=\"Top\">\n                        <ContentControl Content=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Primary]}\" Tag=\"0\" />\n                    </GroupBox>\n                </DockPanel>\n            </AdornerDecorator>\n        </TabItem>\n\n        <TabItem Header=\"Mouse/Headset\">\n            <AdornerDecorator>\n                <DockPanel LastChildFill=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Secondary], Converter={StaticResource VisualizationToLastChildFillConverter}}\">\n                    <GroupBox DockPanel.Dock=\"Top\">\n                        <controls:GradientEditor Gradient=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Settings[(helper:VisualizationIndex)Secondary].Gradient}\"\n                                                 CanAddOrDeleteStops=\"False\" />\n                    </GroupBox>\n\n                    <GroupBox Margin=\"0,8,0,0\" DockPanel.Dock=\"Top\">\n                        <controls:Form>\n                            <controls:Form.Resources>\n                                <Style BasedOn=\"{StaticResource StyleLabelForm}\" TargetType=\"Label\" />\n                                <Style BasedOn=\"{StaticResource StyleTextBlockForm}\" TargetType=\"TextBlock\" />\n                                <Style BasedOn=\"{StaticResource StyleListBoxForm}\" TargetType=\"ListBox\" />\n                            </controls:Form.Resources>\n\n                            <Label controls:Form.IsLabel=\"True\" Content=\"Visualization: \" />\n                            <ComboBox controls:Form.Fill=\"True\" SelectedIndex=\"0\" \n                                      ItemTemplate=\"{StaticResource DataTemplateVisualizationSelection}\"\n                                      ItemsSource=\"{Binding Source={StaticResource DataProviderVisualizationTypes},\n                                                            Converter={StaticResource VisualizationProviderSelectableConverter}, ConverterParameter={x:Static core:RGBDeviceType.Mouse}}\"\n                                      SelectedItem=\"{Binding SelectedSecondaryVisualization}\" />\n                        </controls:Form>\n                    </GroupBox>\n\n                    <GroupBox Margin=\"0,8,0,0\" DockPanel.Dock=\"Top\">\n                        <ContentControl Content=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Secondary].Configuration}\" />\n                    </GroupBox>\n\n                    <GroupBox Margin=\"0,8,0,0\" DockPanel.Dock=\"Top\">\n                        <ContentControl Content=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Secondary]}\" Tag=\"1\" />\n                    </GroupBox>\n                </DockPanel>\n            </AdornerDecorator>\n        </TabItem>\n\n        <TabItem Header=\"Lightbar/Mousepad\">\n            <AdornerDecorator>\n                <DockPanel LastChildFill=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Tertiary], Converter={StaticResource VisualizationToLastChildFillConverter}}\">\n                    <GroupBox DockPanel.Dock=\"Top\">\n                        <controls:GradientEditor Gradient=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Settings[(helper:VisualizationIndex)Tertiary].Gradient}\" />\n                    </GroupBox>\n\n                    <GroupBox Margin=\"0,8,0,0\" DockPanel.Dock=\"Top\">\n                        <controls:Form>\n                            <controls:Form.Resources>\n                                <Style BasedOn=\"{StaticResource StyleLabelForm}\" TargetType=\"Label\" />\n                                <Style BasedOn=\"{StaticResource StyleTextBlockForm}\" TargetType=\"TextBlock\" />\n                                <Style BasedOn=\"{StaticResource StyleListBoxForm}\" TargetType=\"ListBox\" />\n                            </controls:Form.Resources>\n\n                            <Label controls:Form.IsLabel=\"True\" Content=\"Visualization: \" />\n                            <ComboBox controls:Form.Fill=\"True\" SelectedIndex=\"0\" \n                                      ItemTemplate=\"{StaticResource DataTemplateVisualizationSelection}\"\n                                      ItemsSource=\"{Binding Source={StaticResource DataProviderVisualizationTypes},\n                                                            Converter={StaticResource VisualizationProviderSelectableConverter}, ConverterParameter={x:Static core:RGBDeviceType.Mousepad}}\"\n                                      SelectedItem=\"{Binding SelectedTertiaryVisualization}\" />\n                        </controls:Form>\n                    </GroupBox>\n\n                    <GroupBox Margin=\"0,8,0,0\" DockPanel.Dock=\"Top\">\n                        <ContentControl Content=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Tertiary].Configuration}\" />\n                    </GroupBox>\n\n                    <GroupBox Margin=\"0,8,0,0\" DockPanel.Dock=\"Top\">\n                        <ContentControl Content=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Visualizations[(helper:VisualizationIndex)Tertiary]}\" Tag=\"2\" />\n                    </GroupBox>\n                </DockPanel>\n            </AdornerDecorator>\n        </TabItem>\n\n        <TabItem Header=\"Settings\">\n            <AdornerDecorator>\n                <DockPanel LastChildFill=\"False\">\n                    <DockPanel.Resources>\n                        <Style BasedOn=\"{StaticResource StyleLabelForm}\" TargetType=\"Label\" />\n                        <Style BasedOn=\"{StaticResource StyleTextBlockForm}\" TargetType=\"TextBlock\" />\n                        <Style BasedOn=\"{StaticResource StyleListBoxForm}\" TargetType=\"ListBox\" />\n                    </DockPanel.Resources>\n\n                    <GroupBox DockPanel.Dock=\"Top\">\n                        <controls:Form>\n                            <Label controls:Form.IsLabel=\"True\" Content=\"Version\" />\n                            <TextBlock Text=\"{Binding Version}\" />\n\n                            <Label controls:Form.IsLabel=\"True\" Content=\"Update-Rate\" />\n                            <Slider Minimum=\"1\" Maximum=\"60\" controls:Form.Fill=\"True\"  IsSnapToTickEnabled=\"True\" TickFrequency=\"1\" TickPlacement=\"BottomRight\"\n                                    Value=\"{Binding UpdateRate}\"\n                                    attached:SliderValue.Unit=\"FPS\"\n                                    ToolTip=\"Defines how fast the data is updated.&#x0a;Low values can reduce CPU-usage but will cause stuttering.&#x0a;Values above 40 will only affect the internal calculations and wont make the keyboard update faster.&#x0a;It's not recommended to select a value > 40.\" />\n\n                            <Label controls:Form.IsLabel=\"True\" Content=\"Fix Volume\" />\n                            <CheckBox VerticalAlignment=\"Center\"\n                                      IsChecked=\"{Binding EnableAudioPrescale}\"\n                                      ToolTip=\"Scales the audio signal inverse to the OS-master-volume.&#x0a;This might (depending on the system) lead to decrased performance&#x0a;   -> only activate it if you need it.\" />\n\n                        </controls:Form>\n                    </GroupBox>\n\n                    <GroupBox Margin=\"0,8,0,0\" DockPanel.Dock=\"Top\">\n                        <StackPanel Orientation=\"Vertical\">\n                            <Border HorizontalAlignment=\"Left\" Width=\"111\">\n                                <Label HorizontalAlignment=\"Right\" \n                                       Content=\"Background\" />\n                            </Border>\n\n                            <controls:GradientEditor Margin=\"120,-18,0,0\" \n                                                     Gradient=\"{Binding Source={x:Static keyboardAudioVisualizer:ApplicationManager.Instance}, Path=Settings.Background}\"\n                                                     ToolTip=\"Defines the gradient used as the background. Use transparency to create some kind of blur. Usage:&#x0a;   Left click inside the preview to add a new stop.&#x0a;   Left-click stop to change the color or move it.&#x0a;   Right-click stop to remove it.&#x0a;\" />\n                        </StackPanel>\n                    </GroupBox>\n\n                    <GroupBox Margin=\"0,8,0,0\" DockPanel.Dock=\"Top\">\n                        <StackPanel Orientation=\"Vertical\">\n                            <Border HorizontalAlignment=\"Left\" Width=\"111\">\n                                <Label HorizontalAlignment=\"Right\" \n                                       Content=\"Devices\" />\n                            </Border>\n\n                            <ItemsControl VerticalAlignment=\"Top\" HorizontalAlignment=\"Left\" Margin=\"120,-18,0,0\" ItemsSource=\"{Binding Source={x:Static core:RGBSurface.Instance}, Path=Devices}\">\n                                <ItemsControl.ItemTemplate>\n                                    <DataTemplate>\n                                        <TextBlock Style=\"{StaticResource StyleTextBlockForm}\">\n                                            <TextBlock.Text>\n                                                <MultiBinding StringFormat=\">  {0} {1} ({2})\">\n                                                    <Binding Path=\"DeviceInfo.Manufacturer\" />\n                                                    <Binding Path=\"DeviceInfo.Model\" />\n                                                    <Binding Path=\"DeviceInfo.DeviceType\" />\n                                                </MultiBinding>\n                                            </TextBlock.Text>\n                                        </TextBlock>\n                                    </DataTemplate>\n                                </ItemsControl.ItemTemplate>\n                            </ItemsControl>\n                        </StackPanel>\n                    </GroupBox>\n                </DockPanel>\n            </AdornerDecorator>\n        </TabItem>\n    </TabControl>\n\n</controls:BlurredDecorationWindow>\n"
  },
  {
    "path": "KeyboardAudioVisualizer/UI/ConfigurationWindow.xaml.cs",
    "content": "﻿using System;\nusing KeyboardAudioVisualizer.Controls;\n\nnamespace KeyboardAudioVisualizer.UI\n{\n    public partial class ConfigurationWindow : BlurredDecorationWindow\n    {\n        public ConfigurationWindow() => InitializeComponent();\n\n        //DarthAffe 07.02.2018: This prevents the applicaiton from not shutting down and crashing afterwards if 'close' is selected in the taskbar-context-menu\n        private void ConfigurationWindow_OnClosed(object sender, EventArgs e)\n        {\n            ApplicationManager.Instance.ExitCommand.Execute(null);\n        }\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/UI/Visualization/BeatVisualization.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                 xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                 xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n                                 xmlns:visualizationProvider=\"clr-namespace:KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider\"\n                                 xmlns:visualization=\"clr-namespace:KeyboardAudioVisualizer.UI.Visualization\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <Style x:Key=\"StyleBeatVisualizer\" \n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"{x:Type visualization:BeatVisualizer}\">\n        <Setter Property=\"Height\" Value=\"64\" />\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type visualization:BeatVisualizer}\">\n                    <Border Background=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Brush}\"\n                            Opacity=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BeatValue}\" />\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <DataTemplate DataType=\"{x:Type visualizationProvider:BeatVisualizationProvider}\">\n        <visualization:BeatVisualizer Style=\"{StaticResource StyleBeatVisualizer}\" \n                                      VisualizationProvider=\"{Binding}\"\n                                      VisualizationIndex=\"{Binding Tag, RelativeSource={RelativeSource AncestorType=ContentControl}}\"  />\n    </DataTemplate>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/UI/Visualization/BeatVisualizer.cs",
    "content": "﻿using System;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Threading;\nusing KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing KeyboardAudioVisualizer.Helper;\nusing RGB.NET.Brushes.Gradients;\nusing RGB.NET.Core;\n\nnamespace KeyboardAudioVisualizer.UI.Visualization\n{\n    public class BeatVisualizer : Control\n    {\n        #region Properties & Fields\n\n        private LinearGradient _gradient;\n\n        #endregion\n\n        #region DependencyProperties\n        // ReSharper disable InconsistentNaming\n\n        public static readonly DependencyProperty VisualizationProviderProperty = DependencyProperty.Register(\n            \"VisualizationProvider\", typeof(IVisualizationProvider), typeof(BeatVisualizer), new PropertyMetadata(default(IVisualizationProvider)));\n\n        public IVisualizationProvider VisualizationProvider\n        {\n            get => (IVisualizationProvider)GetValue(VisualizationProviderProperty);\n            set => SetValue(VisualizationProviderProperty, value);\n        }\n\n        public static readonly DependencyProperty VisualizationIndexProperty = DependencyProperty.Register(\n            \"VisualizationIndex\", typeof(VisualizationIndex?), typeof(BeatVisualizer), new PropertyMetadata(null, VisualizationIndexChanged));\n\n        public VisualizationIndex? VisualizationIndex\n        {\n            get => (VisualizationIndex?)GetValue(VisualizationIndexProperty);\n            set => SetValue(VisualizationIndexProperty, value);\n        }\n\n        public static readonly DependencyProperty BrushProperty = DependencyProperty.Register(\n            \"Brush\", typeof(Brush), typeof(BeatVisualizer), new PropertyMetadata(default(Brush)));\n\n        public Brush Brush\n        {\n            get => (Brush)GetValue(BrushProperty);\n            set => SetValue(BrushProperty, value);\n        }\n\n        public static readonly DependencyProperty BeatValueProperty = DependencyProperty.Register(\n            \"BeatValue\", typeof(float), typeof(BeatVisualizer), new PropertyMetadata(default(float)));\n\n        public float BeatValue\n        {\n            get => (float)GetValue(BeatValueProperty);\n            set => SetValue(BeatValueProperty, value);\n        }\n\n        // ReSharper restore InconsistentNaming\n        #endregion\n\n        #region Constructors\n\n        public BeatVisualizer()\n        {\n            RGBSurface.Instance.Updated += args => Dispatcher.BeginInvoke(new Action(Update), DispatcherPriority.Normal);\n\n            //TODO DarthAffe 12.08.2017: Create brush from config\n\n        }\n\n        #endregion\n\n        #region Methods\n\n        private void Update()\n        {\n            IVisualizationProvider visualizationProvider = VisualizationProvider;\n            if ((visualizationProvider == null) || (Visibility != Visibility.Visible)) return;\n\n            if (visualizationProvider.VisualizationData[0] > 0.9)\n                BeatValue = 1f;\n            else if (BeatValue > 0.01f)\n            {\n                float newValue = BeatValue * 0.625f;\n                if (newValue > 0.01f)\n                    BeatValue = newValue;\n                else BeatValue = 0;\n            }\n        }\n\n        private static void VisualizationIndexChanged(DependencyObject dependencyObject,\n                                                      DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)\n        {\n            if (!(dependencyObject is BeatVisualizer visualizer)) return;\n            visualizer.UpdateGradient();\n        }\n\n        private void UpdateGradient()\n        {\n            void GradientChanged(object sender, EventArgs args) => UpdateColor();\n            if (_gradient != null)\n                _gradient.GradientChanged -= GradientChanged;\n\n            _gradient = VisualizationIndex.HasValue ? ApplicationManager.Instance.Settings[VisualizationIndex.Value].Gradient : null;\n            if (_gradient != null)\n                _gradient.GradientChanged += GradientChanged;\n\n            UpdateColor();\n        }\n\n        private void UpdateColor()\n        {\n            if (_gradient == null) return;\n\n            GradientStopCollection gradientStops = new GradientStopCollection();\n            foreach (RGB.NET.Brushes.Gradients.GradientStop stop in _gradient.GradientStops)\n                gradientStops.Add(new System.Windows.Media.GradientStop(System.Windows.Media.Color.FromArgb(stop.Color.GetA(), stop.Color.GetR(), stop.Color.GetG(), stop.Color.GetB()), stop.Offset));\n\n            Brush = new LinearGradientBrush(gradientStops, new System.Windows.Point(0, 0.5), new System.Windows.Point(1, 0.5));\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/UI/Visualization/EqualizerVisualization.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                 xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                 xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n                                 xmlns:visualization=\"clr-namespace:KeyboardAudioVisualizer.UI.Visualization\"\n                                 xmlns:equalizer=\"clr-namespace:KeyboardAudioVisualizer.AudioProcessing.Equalizer\"\n                                 xmlns:converter=\"clr-namespace:KeyboardAudioVisualizer.Converter\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <converter:OffsetToPosXConverter x:Key=\"OffsetToPosXConverter\" />\n    <converter:ValueToPosYConverter x:Key=\"ValueToPosYConverter\" />\n    <converter:EqualizerBandsToPointsConverter x:Key=\"EqualizerBandsToPointsConverter\" />\n\n    <Style x:Key=\"StyleEqualizerVisualizer\"\n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"{x:Type visualization:EqualizerVisualizer}\">\n        <Setter Property=\"SnapsToDevicePixels\" Value=\"False\" />\n        <Setter Property=\"UseLayoutRounding\" Value=\"False\" />\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type visualization:EqualizerVisualizer}\">\n                    <Grid>\n                        <Grid x:Name=\"Container\" Background=\"Transparent\"\n                              Visibility=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Equalizer.IsEnabled, Converter={StaticResource BoolToVisibilityConverter}}\">\n                            <Polyline x:Name=\"PART_Line\" Stroke=\"White\" StrokeThickness=\"2\" StrokeLineJoin=\"Round\">\n                                <Polyline.Points>\n                                    <MultiBinding Converter=\"{StaticResource EqualizerBandsToPointsConverter}\" ConverterParameter=\"120\">\n                                        <Binding RelativeSource=\"{RelativeSource TemplatedParent}\" Path=\"Equalizer\" />\n                                        <Binding RelativeSource=\"{RelativeSource TemplatedParent}\" Path=\"Equalizer.Bands\" />\n                                        <Binding Path=\"ActualWidth\" ElementName=\"Container\" />\n                                        <Binding Path=\"ActualHeight\" ElementName=\"Container\" />\n                                    </MultiBinding>\n                                </Polyline.Points>\n                            </Polyline>\n\n                            <ItemsControl x:Name=\"PART_Grips\" Opacity=\"0\"\n                                          ItemsSource=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Equalizer.Bands}\">\n                                <ItemsControl.ItemsPanel>\n                                    <ItemsPanelTemplate>\n                                        <Canvas />\n                                    </ItemsPanelTemplate>\n                                </ItemsControl.ItemsPanel>\n\n                                <ItemsControl.ItemTemplate>\n                                    <DataTemplate DataType=\"equalizer:EqualizerBand\">\n                                        <Border Width=\"24\" Height=\"24\" CornerRadius=\"12\"\n                                                BorderThickness=\"3\" BorderBrush=\"{StaticResource BrushEqualizerBorder}\"\n                                                Background=\"{StaticResource BrushEqualizerBackground}\" />\n                                    </DataTemplate>\n                                </ItemsControl.ItemTemplate>\n\n                                <ItemsControl.ItemContainerStyle>\n                                    <Style TargetType=\"ContentPresenter\">\n                                        <Setter Property=\"Canvas.Left\">\n                                            <Setter.Value>\n                                                <MultiBinding Converter=\"{StaticResource OffsetToPosXConverter}\" ConverterParameter=\"12\">\n                                                    <Binding Path=\"Offset\" />\n                                                    <Binding Path=\"ActualWidth\" ElementName=\"PART_Grips\" />\n                                                </MultiBinding>\n                                            </Setter.Value>\n                                        </Setter>\n                                        <Setter Property=\"Canvas.Top\">\n                                            <Setter.Value>\n                                                <MultiBinding Converter=\"{StaticResource ValueToPosYConverter}\" ConverterParameter=\"12\">\n                                                    <Binding Path=\"Value\" />\n                                                    <Binding Path=\"ActualHeight\" ElementName=\"PART_Grips\" />\n                                                </MultiBinding>\n                                            </Setter.Value>\n                                        </Setter>\n                                    </Style>\n                                </ItemsControl.ItemContainerStyle>\n                            </ItemsControl>\n                        </Grid>\n\n                        <StackPanel VerticalAlignment=\"Top\" HorizontalAlignment=\"Left\"\n                                    Margin=\"0,-4,0,0\" Orientation=\"Horizontal\">\n                            <CheckBox VerticalAlignment=\"Center\" Content=\"Enable Equalizer\"\n                                      Foreground=\"{StaticResource BrushForeground}\"\n                                      IsChecked=\"{Binding Path=Equalizer.IsEnabled, RelativeSource={RelativeSource TemplatedParent}}\"\n                                      ToolTip=\"Enables the Equalizer.&#x0a;The Equalier Allows to finetune the graph by slective increasing/decresing the value.&#x0a;You can add new pivots by rightclicking on the visualization-window.&#x0a;Existing pivots can be deleted by rightclicking on them or moved by leftclicking and dragging around.\" />\n\n                            <Button VerticalAlignment=\"Center\" Margin=\"12,0,0,0\" \n                                    Visibility=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Equalizer.IsEnabled, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=True}\"\n                                    Content=\"Reset\" Command=\"{TemplateBinding ResetCommand}\" />\n                        </StackPanel>\n                    </Grid>\n\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                            <Trigger.EnterActions>\n                                <BeginStoryboard>\n                                    <Storyboard>\n                                        <DoubleAnimation Duration=\"0:0:0.2\" To=\"1\"\n                                                         Storyboard.TargetName=\"PART_Grips\" Storyboard.TargetProperty=\"Opacity\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.EnterActions>\n                            <Trigger.ExitActions>\n                                <BeginStoryboard>\n                                    <Storyboard>\n                                        <DoubleAnimation Duration=\"0:0:0.25\" To=\"0\"\n                                                         Storyboard.TargetName=\"PART_Grips\" Storyboard.TargetProperty=\"Opacity\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.ExitActions>\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/UI/Visualization/EqualizerVisualizer.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing KeyboardAudioVisualizer.AudioProcessing.Equalizer;\nusing KeyboardAudioVisualizer.Helper;\n\nnamespace KeyboardAudioVisualizer.UI.Visualization\n{\n    [TemplatePart(Name = \"PART_Grips\", Type = typeof(ItemsControl))]\n    public class EqualizerVisualizer : Control\n    {\n        #region DependencyProperties\n        // ReSharper disable InconsistentNaming\n\n        public static readonly DependencyProperty EqualizerProperty = DependencyProperty.Register(\n            \"Equalizer\", typeof(IEqualizer), typeof(EqualizerVisualizer), new PropertyMetadata(default(IEqualizer)));\n\n        public IEqualizer Equalizer\n        {\n            get => (IEqualizer)GetValue(EqualizerProperty);\n            set => SetValue(EqualizerProperty, value);\n        }\n\n        public static readonly DependencyProperty ResetCommandProperty = DependencyProperty.Register(\n            \"ResetCommand\", typeof(ICommand), typeof(EqualizerVisualizer), new PropertyMetadata(null));\n\n        public ICommand ResetCommand\n        {\n            get => (ICommand)GetValue(ResetCommandProperty);\n            set => SetValue(ResetCommandProperty, value);\n        }\n\n        // ReSharper restore InconsistentNaming\n        #endregion\n\n        #region Properties & Fields\n\n        private ItemsControl _grips;\n        private EqualizerBand _draggingBand;\n\n        #endregion\n\n        #region Constructors\n\n        public EqualizerVisualizer()\n        {\n            ResetCommand = new ActionCommand(() => Equalizer?.Reset());\n        }\n\n        #endregion\n\n        #region Methods\n\n        public override void OnApplyTemplate()\n        {\n            base.OnApplyTemplate();\n\n            _grips = GetTemplateChild(\"PART_Grips\") as ItemsControl;\n        }\n\n        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)\n        {\n            base.OnMouseLeftButtonDown(e);\n\n            EqualizerBand band = GetClickedBand();\n            if (band != null)\n                _draggingBand = band;\n        }\n\n        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)\n        {\n            base.OnMouseLeftButtonUp(e);\n\n            _draggingBand = null;\n        }\n\n        protected override void OnMouseLeave(MouseEventArgs e)\n        {\n            base.OnMouseLeave(e);\n\n            _draggingBand = null;\n        }\n\n        protected override void OnMouseMove(MouseEventArgs e)\n        {\n            base.OnMouseMove(e);\n\n            if (_draggingBand == null) return;\n\n            UpdateBand(_draggingBand, e.GetPosition(_grips));\n        }\n\n        protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)\n        {\n            base.OnMouseRightButtonDown(e);\n\n            if (_grips == null) return;\n\n            EqualizerBand band = GetClickedBand();\n            if (band == null)\n            {\n                EqualizerBand newBand = Equalizer.AddBand(0, 0);\n                UpdateBand(newBand, e.GetPosition(_grips));\n            }\n            else\n                Equalizer.RemoveBandBand(band);\n        }\n\n        private void UpdateBand(EqualizerBand band, Point position)\n        {\n            double halfHeight = _grips.ActualHeight / 2.0;\n\n            band.Offset = (float)(position.X / _grips.ActualWidth);\n            band.Value = (float)(-(position.Y - halfHeight) / halfHeight);\n        }\n\n        private EqualizerBand GetClickedBand()\n        {\n            ItemsPresenter itemsPresenter = _grips.GetVisualChild<ItemsPresenter>();\n            if (itemsPresenter == null) return null;\n\n            Panel panel = VisualTreeHelper.GetChild(itemsPresenter, 0) as Panel;\n            if (panel == null) return null;\n\n            foreach (UIElement element in panel.Children)\n                if (element.IsMouseOver)\n                {\n                    EqualizerBand band = ((element as ContentPresenter)?.Content as EqualizerBand);\n                    if (band != null) return band;\n                }\n\n            return null;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualization.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                 xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                 xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n                                 xmlns:visualizationProvider=\"clr-namespace:KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider\"\n                                 xmlns:visualization=\"clr-namespace:KeyboardAudioVisualizer.UI.Visualization\"\n                                 xmlns:converter=\"clr-namespace:KeyboardAudioVisualizer.Converter\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/UI/Visualization/EqualizerVisualization.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <converter:OffsetToPosXConverter x:Key=\"OffsetToPosXConverter\" />\n    <converter:ValueToPosYConverter x:Key=\"ValueToPosYConverter\" />\n\n    <Style x:Key=\"StyleFrequencyBarsVisualizer\" \n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"{x:Type visualization:FrequencyBarsVisualizer}\">\n        <Setter Property=\"SnapsToDevicePixels\" Value=\"False\" />\n        <Setter Property=\"UseLayoutRounding\" Value=\"False\" />\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type visualization:FrequencyBarsVisualizer}\">\n                    <StackPanel x:Name=\"PART_BarsPanel\" Orientation=\"Horizontal\" />\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <DataTemplate DataType=\"{x:Type visualizationProvider:FrequencyBarsVisualizationProvider}\">\n        <Grid VerticalAlignment=\"Stretch\" HorizontalAlignment=\"Stretch\">\n            <visualization:FrequencyBarsVisualizer Style=\"{StaticResource StyleFrequencyBarsVisualizer}\" \n                                                   VisualizationProvider=\"{Binding}\"\n                                                   VisualizationIndex=\"{Binding Tag, RelativeSource={RelativeSource AncestorType=ContentControl}}\" />\n            <visualization:EqualizerVisualizer Style=\"{StaticResource StyleEqualizerVisualizer}\" \n                                               Equalizer=\"{Binding Equalizer}\" />\n        </Grid>\n    </DataTemplate>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualizer.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Threading;\nusing KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing KeyboardAudioVisualizer.Helper;\nusing RGB.NET.Brushes.Gradients;\nusing RGB.NET.Core;\nusing Color = System.Windows.Media.Color;\nusing Rectangle = System.Windows.Shapes.Rectangle;\n\nnamespace KeyboardAudioVisualizer.UI.Visualization\n{\n    [TemplatePart(Name = \"PART_BarsPanel\", Type = typeof(Panel))]\n    public class FrequencyBarsVisualizer : Control\n    {\n        #region DependencyProperties\n        // ReSharper disable InconsistentNaming\n\n        public static readonly DependencyProperty VisualizationProviderProperty = DependencyProperty.Register(\n            \"VisualizationProvider\", typeof(IVisualizationProvider), typeof(FrequencyBarsVisualizer),\n            new PropertyMetadata(default(IVisualizationProvider), VisualizationProviderChanged));\n\n        public IVisualizationProvider VisualizationProvider\n        {\n            get => (IVisualizationProvider)GetValue(VisualizationProviderProperty);\n            set => SetValue(VisualizationProviderProperty, value);\n        }\n\n        public static readonly DependencyProperty VisualizationIndexProperty = DependencyProperty.Register(\n            \"VisualizationIndex\", typeof(VisualizationIndex?), typeof(FrequencyBarsVisualizer), new PropertyMetadata(null, VisualizationIndexChanged));\n\n        public VisualizationIndex? VisualizationIndex\n        {\n            get => (VisualizationIndex?)GetValue(VisualizationIndexProperty);\n            set => SetValue(VisualizationIndexProperty, value);\n        }\n\n        // ReSharper restore InconsistentNaming\n        #endregion\n\n        #region Properties & Fields\n\n        private LinearGradient _gradient;\n        private Panel _panel;\n        private Rectangle[] _bars = new Rectangle[0];\n\n        #endregion\n\n        #region Constructors\n\n        public FrequencyBarsVisualizer()\n        {\n            RGBSurface.Instance.Updated += args => Dispatcher.BeginInvoke(new Action(Update), DispatcherPriority.Normal);\n            SizeChanged += (sender, args) => UpdateSizes();\n        }\n\n        #endregion\n\n        #region Methods\n\n        public override void OnApplyTemplate()\n        {\n            base.OnApplyTemplate();\n\n            _panel = GetTemplateChild(\"PART_BarsPanel\") as Panel;\n\n            InitializeBars();\n        }\n\n        private static void VisualizationProviderChanged(DependencyObject dependencyObject,\n            DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)\n        {\n            if (!(dependencyObject is FrequencyBarsVisualizer visualizer)) return;\n\n            void ConfigurationOnPropertyChanged(object sender, PropertyChangedEventArgs args) => visualizer.ConfigurationChanged(args.PropertyName);\n\n            if (dependencyPropertyChangedEventArgs.OldValue is IVisualizationProvider oldVisualizationProvider)\n                oldVisualizationProvider.Configuration.PropertyChanged -= ConfigurationOnPropertyChanged;\n\n            if (dependencyPropertyChangedEventArgs.NewValue is IVisualizationProvider newVisualizationProvider)\n                newVisualizationProvider.Configuration.PropertyChanged += ConfigurationOnPropertyChanged;\n        }\n\n        private static void VisualizationIndexChanged(DependencyObject dependencyObject,\n                                                      DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)\n        {\n            if (!(dependencyObject is FrequencyBarsVisualizer visualizer)) return;\n            visualizer.UpdateGradient();\n        }\n\n        private void UpdateGradient()\n        {\n            void GradientChanged(object sender, EventArgs args) => UpdateColors();\n            if (_gradient != null)\n                _gradient.GradientChanged -= GradientChanged;\n\n            _gradient = VisualizationIndex.HasValue ? ApplicationManager.Instance.Settings[VisualizationIndex.Value].Gradient : null;\n            if (_gradient != null)\n                _gradient.GradientChanged += GradientChanged;\n\n            UpdateColors();\n        }\n\n        private void ConfigurationChanged(string changedPropertyName)\n        {\n            if ((changedPropertyName == null) || (changedPropertyName == nameof(FrequencyBarsVisualizationProviderConfiguration.Bars)))\n                InitializeBars();\n        }\n\n        private void InitializeBars()\n        {\n            if (_panel == null) return;\n\n            _panel.Children.Clear();\n            if (VisualizationProvider == null) return;\n\n            _bars = new Rectangle[((FrequencyBarsVisualizationProviderConfiguration)VisualizationProvider.Configuration).Bars];\n\n            for (int i = 0; i < _bars.Length; i++)\n            {\n                _bars[i] = new Rectangle { VerticalAlignment = VerticalAlignment.Bottom, Width = 0 };\n                _panel.Children.Add(_bars[i]);\n            }\n\n            UpdateSizes();\n            UpdateColors();\n        }\n\n        private void UpdateSizes()\n        {\n            if (_bars.Length == 0) return;\n\n            double barSpacing = ActualWidth / _bars.Length;\n            double barWidth = (barSpacing * 3.0) / 4.0;\n            double margin = barSpacing - barWidth;\n\n            for (int i = 0; i < _bars.Length; i++)\n            {\n                _bars[i].Width = barWidth;\n                _bars[i].Margin = new Thickness(margin / 2, 0, margin / 2, 0);\n            }\n        }\n\n        private void UpdateColors()\n        {\n            if (_gradient == null) return;\n\n            for (int i = 0; i < _bars.Length; i++)\n            {\n                RGB.NET.Core.Color color = _gradient.GetColor((double)i / _bars.Length);\n                _bars[i].Fill = new SolidColorBrush(Color.FromRgb(color.GetR(), color.GetG(), color.GetB()));\n            }\n        }\n\n        private void Update()\n        {\n            IVisualizationProvider visualizationProvider = VisualizationProvider;\n            if ((visualizationProvider == null) || (Visibility != Visibility.Visible)) return;\n\n            int count = Math.Min(_bars.Length, visualizationProvider.VisualizationData.Length);\n            for (int i = 0; i < count; i++)\n            {\n                _bars[i].Height = (float)((_bars[i].Height * 0.5) + ((ActualHeight * visualizationProvider.VisualizationData[i]) * (1.0 - 0.5)));\n                if (double.IsNaN(_bars[i].Height)) _bars[i].Height = 0;\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/UI/Visualization/LevelVisualization.xaml",
    "content": "﻿<styles:CachedResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                                 xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                                 xmlns:styles=\"clr-namespace:KeyboardAudioVisualizer.Styles\"\n                                 xmlns:visualizationProvider=\"clr-namespace:KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider\"\n                                 xmlns:visualization=\"clr-namespace:KeyboardAudioVisualizer.UI.Visualization\">\n\n    <styles:CachedResourceDictionary.MergedDictionaries>\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/FrameworkElement.xaml\" />\n        <styles:CachedResourceDictionary Source=\"/KeyboardAudioVisualizer;component/Styles/Theme.xaml\" />\n    </styles:CachedResourceDictionary.MergedDictionaries>\n\n    <Style x:Key=\"StyleLevelVisualizer\" \n           BasedOn=\"{StaticResource StyleFrameworkElement}\"\n           TargetType=\"{x:Type visualization:LevelVisualizer}\">\n        <Setter Property=\"Height\" Value=\"64\" />\n        \n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type visualization:LevelVisualizer}\">\n                    <Grid>\n                        <Grid.ColumnDefinitions>\n                            <ColumnDefinition Width=\"*\" />\n                            <ColumnDefinition Width=\"*\" />\n                        </Grid.ColumnDefinitions>\n\n                        <Border Grid.Column=\"0\" BorderThickness=\"0,0,1,0\" BorderBrush=\"White\">\n                            <Border x:Name=\"BrdLeft\" Background=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BrushLeft}\">\n                                <Border.OpacityMask>\n                                    <VisualBrush>\n                                        <VisualBrush.Visual>\n                                            <Border Width=\"{Binding ElementName=BrdLeft, Path=ActualWidth}\"\n                                                    Height=\"{Binding ElementName=BrdLeft, Path=ActualHeight}\"\n                                                    Background=\"Transparent\">\n                                                <Rectangle HorizontalAlignment=\"Right\" Fill=\"Black\" \n                                                           Width=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SizeLeft}\" />\n                                            </Border>\n                                        </VisualBrush.Visual>\n                                    </VisualBrush>\n                                </Border.OpacityMask>\n                            </Border>\n                        </Border>\n\n                        <Border Grid.Column=\"1\" BorderThickness=\"1,0,0,0\" BorderBrush=\"White\">\n                            <Border  x:Name=\"BrdRight\" Background=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BrushRight}\">\n                                <Border.OpacityMask>\n                                    <VisualBrush>\n                                        <VisualBrush.Visual>\n                                            <Border Width=\"{Binding ElementName=BrdRight, Path=ActualWidth}\"\n                                                    Height=\"{Binding ElementName=BrdRight, Path=ActualHeight}\"\n                                                    Background=\"Transparent\">\n                                                <Rectangle HorizontalAlignment=\"Left\" Fill=\"Black\"\n                                                           Width=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SizeRight}\" />\n                                            </Border>\n                                        </VisualBrush.Visual>\n                                    </VisualBrush>\n                                </Border.OpacityMask>\n                            </Border>\n                        </Border>\n                    </Grid>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <DataTemplate DataType=\"{x:Type visualizationProvider:LevelVisualizationProvider}\">\n        <visualization:LevelVisualizer Style=\"{StaticResource StyleLevelVisualizer}\" \n                                       VisualizationProvider=\"{Binding}\"\n                                       VisualizationIndex=\"{Binding Tag, RelativeSource={RelativeSource AncestorType=ContentControl}}\"  />\n    </DataTemplate>\n\n</styles:CachedResourceDictionary>\n    "
  },
  {
    "path": "KeyboardAudioVisualizer/UI/Visualization/LevelVisualizer.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Threading;\nusing KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider;\nusing KeyboardAudioVisualizer.Helper;\nusing RGB.NET.Brushes.Gradients;\nusing RGB.NET.Core;\nusing Color = System.Windows.Media.Color;\nusing GradientStop = RGB.NET.Brushes.Gradients.GradientStop;\nusing Point = System.Windows.Point;\n\nnamespace KeyboardAudioVisualizer.UI.Visualization\n{\n    public class LevelVisualizer : Control\n    {\n        #region Properties & Fields\n\n        private LinearGradient _gradient;\n\n        #endregion\n\n        #region DependencyProperties\n        // ReSharper disable InconsistentNaming\n\n        public static readonly DependencyProperty VisualizationProviderProperty = DependencyProperty.Register(\n            \"VisualizationProvider\", typeof(IVisualizationProvider), typeof(LevelVisualizer), new PropertyMetadata(default(IVisualizationProvider)));\n\n        public IVisualizationProvider VisualizationProvider\n        {\n            get => (IVisualizationProvider)GetValue(VisualizationProviderProperty);\n            set => SetValue(VisualizationProviderProperty, value);\n        }\n\n        public static readonly DependencyProperty VisualizationIndexProperty = DependencyProperty.Register(\n            \"VisualizationIndex\", typeof(VisualizationIndex?), typeof(LevelVisualizer), new PropertyMetadata(null, VisualizationIndexChanged));\n\n        public VisualizationIndex? VisualizationIndex\n        {\n            get => (VisualizationIndex?)GetValue(VisualizationIndexProperty);\n            set => SetValue(VisualizationIndexProperty, value);\n        }\n\n        public static readonly DependencyProperty BrushLeftProperty = DependencyProperty.Register(\n            \"BrushLeft\", typeof(Brush), typeof(LevelVisualizer), new PropertyMetadata(default(Brush)));\n\n        public Brush BrushLeft\n        {\n            get => (Brush)GetValue(BrushLeftProperty);\n            set => SetValue(BrushLeftProperty, value);\n        }\n\n        public static readonly DependencyProperty BrushRightProperty = DependencyProperty.Register(\n            \"BrushRight\", typeof(Brush), typeof(LevelVisualizer), new PropertyMetadata(default(Brush)));\n\n        public Brush BrushRight\n        {\n            get => (Brush)GetValue(BrushRightProperty);\n            set => SetValue(BrushRightProperty, value);\n        }\n\n        public static readonly DependencyProperty SizeLeftProperty = DependencyProperty.Register(\n            \"SizeLeft\", typeof(int), typeof(LevelVisualizer), new PropertyMetadata(default(int)));\n\n        public int SizeLeft\n        {\n            get => (int)GetValue(SizeLeftProperty);\n            set => SetValue(SizeLeftProperty, value);\n        }\n\n        public static readonly DependencyProperty SizeRightProperty = DependencyProperty.Register(\n            \"SizeRight\", typeof(int), typeof(LevelVisualizer), new PropertyMetadata(default(int)));\n\n        public int SizeRight\n        {\n            get => (int)GetValue(SizeRightProperty);\n            set => SetValue(SizeRightProperty, value);\n        }\n\n        // ReSharper restore InconsistentNaming\n        #endregion\n\n        #region Constructors\n\n        public LevelVisualizer()\n        {\n            RGBSurface.Instance.Updated += args => Dispatcher.BeginInvoke(new Action(Update), DispatcherPriority.Normal);\n        }\n\n        #endregion\n\n        #region Methods\n\n        private void Update()\n        {\n            IVisualizationProvider visualizationProvider = VisualizationProvider;\n            if ((visualizationProvider == null) || (Visibility != Visibility.Visible)) return;\n\n            int horizontalSizeLeft = (int)(visualizationProvider.VisualizationData[0] * (ActualWidth / 2));\n            if (Math.Abs(SizeLeft - horizontalSizeLeft) > 1)\n                SizeLeft = horizontalSizeLeft;\n\n            int horizontalSizeRight = (int)(visualizationProvider.VisualizationData[1] * (ActualWidth / 2));\n            if (Math.Abs(SizeRight - horizontalSizeRight) > 1)\n                SizeRight = horizontalSizeRight;\n        }\n\n        private void SetBrushes()\n        {\n            if (_gradient == null) return;\n\n            GradientStopCollection gradientStops = new GradientStopCollection();\n            foreach (GradientStop stop in _gradient.GradientStops)\n                gradientStops.Add(new System.Windows.Media.GradientStop(Color.FromArgb(stop.Color.GetA(), stop.Color.GetR(), stop.Color.GetG(), stop.Color.GetB()), stop.Offset));\n\n            BrushLeft = new LinearGradientBrush(gradientStops, new Point(1, 0.5), new Point(0, 0.5));\n            BrushRight = new LinearGradientBrush(gradientStops, new Point(0, 0.5), new Point(1, 0.5));\n        }\n\n        private static void VisualizationIndexChanged(DependencyObject dependencyObject,\n                                                      DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)\n        {\n            if (!(dependencyObject is LevelVisualizer visualizer)) return;\n            visualizer.UpdateGradient();\n        }\n\n        private void UpdateGradient()\n        {\n            void GradientChanged(object sender, EventArgs args) => SetBrushes();\n            if (_gradient != null)\n                _gradient.GradientChanged -= GradientChanged;\n\n            _gradient = VisualizationIndex.HasValue ? ApplicationManager.Instance.Settings[VisualizationIndex.Value].Gradient : null;\n            if (_gradient != null)\n                _gradient.GradientChanged += GradientChanged;\n\n            SetBrushes();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyboardAudioVisualizer/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"CSCore\" version=\"1.2.1.2\" targetFramework=\"net461\" />\n  <package id=\"Hardcodet.NotifyIcon.Wpf\" version=\"1.0.8\" targetFramework=\"net461\" />\n  <package id=\"HidSharp\" version=\"2.0.5\" targetFramework=\"net461\" />\n  <package id=\"MathNet.Numerics\" version=\"4.7.0\" targetFramework=\"net461\" />\n  <package id=\"Newtonsoft.Json\" version=\"12.0.1\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Brushes\" version=\"0.1.31\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Core\" version=\"0.1.31\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Decorators\" version=\"0.1.31\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Devices.Asus\" version=\"0.1.31\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Devices.CoolerMaster\" version=\"0.1.31\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Devices.Corsair\" version=\"0.1.31\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Devices.Logitech\" version=\"0.1.31\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Devices.Novation\" version=\"0.1.31\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Devices.Razer\" version=\"0.1.31\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Devices.SteelSeries\" version=\"0.1.31\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Groups\" version=\"0.1.31\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Resources.Asus\" version=\"0.3.0\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Resources.CoolerMaster\" version=\"0.2.0\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Resources.Corsair\" version=\"0.3.0.234\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Resources.Logitech\" version=\"0.3.0\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Resources.Novation\" version=\"0.1.0\" targetFramework=\"net461\" />\n  <package id=\"RGB.NET.Resources.Razer\" version=\"0.3.2.4\" targetFramework=\"net461\" />\n  <package id=\"Sanford.Multimedia.Midi\" version=\"6.6.0\" targetFramework=\"net461\" />\n  <package id=\"Sanford.Multimedia.Midi.Standard\" version=\"6.6.0\" targetFramework=\"net461\" />\n  <package id=\"System.Management\" version=\"4.5.0\" targetFramework=\"net461\" />\n  <package id=\"System.ValueTuple\" version=\"4.5.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "KeyboardAudioVisualizer.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 15\nVisualStudioVersion = 15.0.26430.15\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"KeyboardAudioVisualizer\", \"KeyboardAudioVisualizer\\KeyboardAudioVisualizer.csproj\", \"{0AC4E8B1-4D4D-447F-B9FD-38A74ED1F243}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"libs\", \"libs\", \"{9A053D4E-5BC6-4179-8F82-9B5C8C7E4127}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tlibs\\Transitionals.dll = libs\\Transitionals.dll\n\tEndProjectSection\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{0AC4E8B1-4D4D-447F-B9FD-38A74ED1F243}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{0AC4E8B1-4D4D-447F-B9FD-38A74ED1F243}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{0AC4E8B1-4D4D-447F-B9FD-38A74ED1F243}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{0AC4E8B1-4D4D-447F-B9FD-38A74ED1F243}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "KeyboardAudioVisualizer.sln.DotSettings",
    "content": "﻿<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\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=KeyboardAudioVisualizer_002EAnnotations/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    {description}\n    Copyright (C) {year}  {fullname}\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  {signature of Ty Coon}, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "NuGet.Config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n\n  <packageSources>\n    <add key=\"RGB.NET\" value=\"http://nuget.arge.be/v3/index.json\" />\n  </packageSources>\n\n  <activePackageSource>\n    <add key=\"All\" value=\"(Aggregate source)\" />\n  </activePackageSource>\n\n</configuration>\n"
  },
  {
    "path": "README.md",
    "content": "This software is no longer actively developed.\n\nConsider checking out [Artemis](https://github.com/Artemis-RGB/Artemis) for a even more feature rich replacement.\n\n# KeyboardAudioVisualizer\nIt's colorful - I like it!\n\n**Example-Video:**   \n[![Example-Video](https://img.youtube.com/vi/mby2NYN0V1o/0.jpg)](https://www.youtube.com/watch?v=mby2NYN0V1o)\n\n## Visualizations\n#### Keyboard\n- **\"Frequency Bars\"** - Simple spectrum visualizer.\n- **\"Level\"** - Shows the overall volume.\n- **\"Beat detection\"** - Pulses to the beat of the music. (This isn't working really well right now, depending on the music. But in general not satisfying, sorry.)\n#### Mouse/Headset\n- **\"Beat detection\"** - Pulses to the beat of the music. (This isn't working really well right now, depending on the music. But in general not satisfying, sorry.)\n#### Mousepad/Lightbar (K95 Platinum)\n- **\"Level\"** - Shows the overall volume.\n- **\"Beat detection\"** - Pulses to the beat of the music. (This isn't working really well right now, depending on the music. But in general not satisfying, sorry.)\n\n## Supported devices\n- All Corsair RGB-devices.\n- Logitech G910 and G610 with physical EU layout. (Untested but should work)\n- (In theory every device with SDK support could be include, open an issue if you want to help with increasing the range of supported devices!)\n\n## Settings\n#### Frequency Bars\n- **Spectrum:** The way the spectrum is grouped together. Values are: _(default: Logarithmic)_\n  - **_Linear_**: Each bar represents the same range of frequencies. Most of the time this doesn't look good since low frequencies are underrepresented.\n  - **_Logarithmic_**: The higher the frequencies get the wider the range of grouped frequencies. This is close to the way humans perceive sound and therfore most of the time looks quite good as long as the range of used frequencies is big enough.\n  - **_Gamma_**: While Gamma-correction is known from image-processing it still applies quite well to audio-data, grouping not as extreme as logarithmic but still better than linear. The used Gamma-value can be configured.\n- **Value:** The way the value of a frequency bar is determined. Values are: _(default: Sum)_\n  - **_Sum_**: Sums the power of all frequencies grouped in the bar using all available data. Combining this with logarithmic grouping gives the most realistic representation.\n  - **_Max_**: Uses the maximum value in each group making sure peaks are caught well. This works quite good with gamma-grouping.\n  - **_Average_**: Uses the average of all frequencies grouped in the bar. This smooths out the whole graph quite a lot.\n- **Bars:** The number of bars used to represent the spectrum. _(default: 48)_\n- **Min Freq.:** The minimum frequency used in the graph. This value shouldn't be modified. _(default: 60)_\n- **Max Freq.:** The maximum frequency used in the graph. This value can be lowered to increase the value of lower frequencies. Using high values might lead to death bars if the audio is mastered with an low-pass filter cutting high frequencies. _(default: 15000)_\n- **Gamma:** The correction value used for gamma-grouping (disabled if any other grouping is selected). High values lead to a stronger compression of high frequencies. _(default: 2)_\n- **Reference:** The reference value used to calculate the power of each bar. Adjust this to your audio volume. Low volume -> low reference, high volume -> higher reference. _(default: 90)_\n- **Smoothing:** Smooths the graph to prevent flickering. Low values will cause a hectic fast plot, high values a slow one without peaks. _(default: 3)_\n- **Emphasize:** Emphasizes peaks. The higher the value, the bigger the difference between a \"loud-bar\" and a \"quiet-bar\". _(default: 0.5)_\n   \n**Equalizer**   \nAllows to finetune the graph by slective increasing/decresing the value.\nYou can add new pivots by rightclicking on the visualization-window.   \nExisting pivots can be deleted by rightclicking on them or moved by leftclicking and dragging around.   \n\n\n#### Beat detection\n_No configuration right now_\n\n#### Level\n- **Calculation:** Defines how the RMS of the audio is plotted. Values are _Linear_, _Logarithmic_ and _Exponential_. The used range of the plott increases in that order (exponential has the widest range of peaks). _(default: Logarithmic)_\n\n- **Scale:** Scales the whole graph. Use this to to fit your audio volume. _(default: 3)_\n\n- **Smoothing:** Smooths the plot to prevent flickering. Low values will cause a hectic fast plot, high values a slow one without peaks. _(default: 8)_\n"
  }
]