From ba80ef9848d892bbea761617c2d346c08a96dc75 Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sun, 21 Aug 2016 13:11:07 +0200 Subject: [PATCH] First attempt to rewrite Effects --- Brushes/AbstractBrush.cs | 3 +- Brushes/IBrush.cs | 3 +- Brushes/ImageBrush.cs | 2 + Brushes/LinearGradientBrush.cs | 2 + Brushes/ProfileBrush.cs | 4 +- Brushes/RadialGradientBrush.cs | 2 + Brushes/RandomColorBrush.cs | 2 + Brushes/SolidColorBrush.cs | 2 + CUE.NET.csproj | 7 +- Devices/Generic/AbstractCueDevice.cs | 113 ++---------------- Devices/Generic/Enums/UpdateMode.cs | 6 - Devices/Headset/CorsairHeadset.cs | 24 ++-- Devices/Keyboard/CorsairKeyboard.cs | 44 ++++--- .../Keyboard/Extensions/KeyGroupExtension.cs | 14 +-- .../{BaseKeyGroup.cs => AbstractKeyGroup.cs} | 7 +- Devices/Keyboard/Keys/IKeyGroup.cs | 3 +- Devices/Keyboard/Keys/ListKeyGroup.cs | 4 +- Devices/Keyboard/Keys/RectangleKeyGroup.cs | 4 +- Devices/Mouse/CorsairMouse.cs | 24 ++-- ...stractEffect.cs => AbstractBrushEffect.cs} | 40 +++---- Effects/AbstractEffectTarget.cs | 71 +++++++++++ Effects/AbstractKeyGroupEffect.cs | 54 +++++++++ Effects/EffectTimeContainer.cs | 7 +- Effects/FlashEffect.cs | 45 ++----- Effects/IEffect.cs | 39 +++--- Effects/IEffectTarget.cs | 31 +++++ Effects/RippleEffect.cs | 37 ++++++ .../AudioAnalyzerExample.cs | 14 ++- .../AudioSpectrumBrush.cs | 14 ++- .../AudioSpectrumEffect.cs | 41 ------- .../Example_AudioAnalyzer_full.csproj | 1 - .../SongBeatEffect.cs | 14 +-- 32 files changed, 353 insertions(+), 325 deletions(-) rename Devices/Keyboard/Keys/{BaseKeyGroup.cs => AbstractKeyGroup.cs} (87%) rename Effects/{AbstractEffect.cs => AbstractBrushEffect.cs} (55%) create mode 100644 Effects/AbstractEffectTarget.cs create mode 100644 Effects/AbstractKeyGroupEffect.cs create mode 100644 Effects/IEffectTarget.cs create mode 100644 Effects/RippleEffect.cs delete mode 100644 Examples/AudioAnalyzer/Example_AudioAnalyzer_full/AudioSpectrumEffect.cs diff --git a/Brushes/AbstractBrush.cs b/Brushes/AbstractBrush.cs index 649caba..8e3c085 100644 --- a/Brushes/AbstractBrush.cs +++ b/Brushes/AbstractBrush.cs @@ -2,6 +2,7 @@ using System.Drawing; using CUE.NET.Devices.Keyboard.Enums; +using CUE.NET.Effects; using CUE.NET.Helper; namespace CUE.NET.Brushes @@ -9,7 +10,7 @@ namespace CUE.NET.Brushes /// /// Represents a basic brush. /// - public abstract class AbstractBrush : IBrush + public abstract class AbstractBrush : AbstractEffectTarget, IBrush { #region Properties & Fields diff --git a/Brushes/IBrush.cs b/Brushes/IBrush.cs index bcb1099..094f7ff 100644 --- a/Brushes/IBrush.cs +++ b/Brushes/IBrush.cs @@ -2,13 +2,14 @@ using System.Drawing; using CUE.NET.Devices.Keyboard.Enums; +using CUE.NET.Effects; namespace CUE.NET.Brushes { /// /// Represents a basic brush. /// - public interface IBrush + public interface IBrush : IEffectTarget { /// /// Gets or sets the calculation mode used for the rectangle/points used for color-selection in brushes. diff --git a/Brushes/ImageBrush.cs b/Brushes/ImageBrush.cs index 7319389..2f458fb 100644 --- a/Brushes/ImageBrush.cs +++ b/Brushes/ImageBrush.cs @@ -39,6 +39,8 @@ namespace CUE.NET.Brushes #region Properties & Fields + protected override IBrush EffectTarget => this; + /// /// Gets or sets the image drawn by the brush. If null it will default to full transparent. /// diff --git a/Brushes/LinearGradientBrush.cs b/Brushes/LinearGradientBrush.cs index 25c7121..55264c0 100644 --- a/Brushes/LinearGradientBrush.cs +++ b/Brushes/LinearGradientBrush.cs @@ -18,6 +18,8 @@ namespace CUE.NET.Brushes { #region Properties & Fields + protected override IBrush EffectTarget => this; + /// /// Gets or sets the start point (as percentage in the range [0..1]) of the gradient drawn by the brush. (default: 0f, 0.5f) /// diff --git a/Brushes/ProfileBrush.cs b/Brushes/ProfileBrush.cs index 815672b..b3cdb4d 100644 --- a/Brushes/ProfileBrush.cs +++ b/Brushes/ProfileBrush.cs @@ -11,7 +11,9 @@ namespace CUE.NET.Brushes public class ProfileBrush : AbstractBrush { #region Properties & Fields - + + protected override IBrush EffectTarget => this; + private Dictionary _keyLights; #endregion diff --git a/Brushes/RadialGradientBrush.cs b/Brushes/RadialGradientBrush.cs index 0b97af3..8a87c54 100644 --- a/Brushes/RadialGradientBrush.cs +++ b/Brushes/RadialGradientBrush.cs @@ -16,6 +16,8 @@ namespace CUE.NET.Brushes { #region Properties & Fields + protected override IBrush EffectTarget => this; + /// /// Gets or sets the center point (as percentage in the range [0..1]) around which the brush should be drawn. /// diff --git a/Brushes/RandomColorBrush.cs b/Brushes/RandomColorBrush.cs index 5037854..dd427eb 100644 --- a/Brushes/RandomColorBrush.cs +++ b/Brushes/RandomColorBrush.cs @@ -15,6 +15,8 @@ namespace CUE.NET.Brushes { #region Properties & Fields + protected override IBrush EffectTarget => this; + private Random _random = new Random(); #endregion diff --git a/Brushes/SolidColorBrush.cs b/Brushes/SolidColorBrush.cs index 1ad65bd..d315199 100644 --- a/Brushes/SolidColorBrush.cs +++ b/Brushes/SolidColorBrush.cs @@ -12,6 +12,8 @@ namespace CUE.NET.Brushes { #region Properties & Fields + protected override IBrush EffectTarget => this; + /// /// Gets or sets the color drawn by the brush. /// diff --git a/CUE.NET.csproj b/CUE.NET.csproj index a378353..cf69886 100644 --- a/CUE.NET.csproj +++ b/CUE.NET.csproj @@ -58,6 +58,10 @@ + + + + @@ -67,7 +71,6 @@ - @@ -76,7 +79,7 @@ - + diff --git a/Devices/Generic/AbstractCueDevice.cs b/Devices/Generic/AbstractCueDevice.cs index 499b591..66d2d66 100644 --- a/Devices/Generic/AbstractCueDevice.cs +++ b/Devices/Generic/AbstractCueDevice.cs @@ -10,7 +10,6 @@ using System.Threading; using System.Threading.Tasks; using CUE.NET.Devices.Generic.Enums; using CUE.NET.Devices.Generic.EventArgs; -using CUE.NET.Effects; using CUE.NET.Native; namespace CUE.NET.Devices.Generic @@ -27,7 +26,7 @@ namespace CUE.NET.Devices.Generic /// public IDeviceInfo DeviceInfo { get; } - private UpdateMode _updateMode = UpdateMode.AutoOnEffect; + private UpdateMode _updateMode = UpdateMode.Manual; /// /// Gets or sets the update-mode for the device. /// @@ -51,16 +50,6 @@ namespace CUE.NET.Devices.Generic /// protected Dictionary Leds { get; } = new Dictionary(); - /// - /// Indicates if the device has an active effect to deal with. - /// - protected abstract bool HasEffect { get; } - - /// - /// - /// - protected LinkedList Effects { get; } = new LinkedList(); - private CancellationTokenSource _updateTokenSource; private CancellationToken _updateToken; private Task _updateTask; @@ -139,9 +128,6 @@ namespace CUE.NET.Devices.Generic case UpdateMode.Manual: shouldRun = false; break; - case UpdateMode.AutoOnEffect: - shouldRun = HasEffect; - break; case UpdateMode.Continuous: shouldRun = true; break; @@ -184,8 +170,8 @@ namespace CUE.NET.Devices.Generic { OnUpdating(); + DeviceUpdateEffects(); DeviceUpdate(); - UpdateEffects(); ICollection ledsToUpdate = (flushLeds ? Leds : Leds.Where(x => x.Value.IsDirty)).Select(x => new LedUpateRequest(x.Key, x.Value.RequestedColor)).ToList(); @@ -198,53 +184,15 @@ namespace CUE.NET.Devices.Generic } /// - /// Empty method which can be overwritten to perform device specific updates. + /// Performs device specific updates. /// - protected virtual void DeviceUpdate() - { } - - private void UpdateEffects() - { - List effectsToRemove = new List(); - lock (Effects) - { - long currentTicks = DateTime.Now.Ticks; - foreach (EffectTimeContainer effect in Effects.OrderBy(x => x.ZIndex)) - { - try - { - float deltaTime; - if (effect.TicksAtLastUpdate < 0) - { - effect.TicksAtLastUpdate = currentTicks; - deltaTime = 0f; - } - else - deltaTime = (currentTicks - effect.TicksAtLastUpdate) / 10000000f; - - effect.TicksAtLastUpdate = currentTicks; - effect.Effect.Update(deltaTime); - - ApplyEffect(effect.Effect); - - if (effect.Effect.IsDone) - effectsToRemove.Add(effect.Effect); - } - // ReSharper disable once CatchAllClause - catch (Exception ex) { OnException(ex); } - } - } - - foreach (IEffect effect in effectsToRemove) - DetachEffect(effect); - } + protected abstract void DeviceUpdate(); /// - /// Applies the given effect to the device LEDs. + /// Performs device specific updates effect-updates. /// - /// The effect to apply. - protected abstract void ApplyEffect(IEffect effect); - + protected abstract void DeviceUpdateEffects(); + private void UpdateLeds(ICollection updateRequests) { updateRequests = updateRequests.Where(x => x.Color != Color.Transparent).ToList(); @@ -276,53 +224,6 @@ namespace CUE.NET.Devices.Generic OnLedsUpdated(updateRequests); } - /// - /// Attaches the given effect. - /// - /// The effect to attach. - /// true if the effect could be attached; otherwise, false. - public bool AttachEffect(IEffect effect) - { - bool retVal = false; - lock (Effects) - { - if (effect != null && Effects.All(x => x.Effect != effect)) - { - effect.OnAttach(); - Effects.AddLast(new EffectTimeContainer(effect, -1)); - retVal = true; - } - } - - CheckUpdateLoop(); - return retVal; - } - - /// - /// Detaches the given effect. - /// - /// The effect to detached. - /// true if the effect could be detached; otherwise, false. - public bool DetachEffect(IEffect effect) - { - bool retVal = false; - lock (Effects) - { - if (effect != null) - { - EffectTimeContainer val = Effects.FirstOrDefault(x => x.Effect == effect); - if (val != null) - { - effect.OnDetach(); - Effects.Remove(val); - retVal = true; - } - } - } - CheckUpdateLoop(); - return retVal; - } - /// /// Resets all loaded LEDs back to default. /// diff --git a/Devices/Generic/Enums/UpdateMode.cs b/Devices/Generic/Enums/UpdateMode.cs index 6dc10a1..24be91d 100644 --- a/Devices/Generic/Enums/UpdateMode.cs +++ b/Devices/Generic/Enums/UpdateMode.cs @@ -12,12 +12,6 @@ namespace CUE.NET.Devices.Generic.Enums /// Manual, - /// - /// The device will perform automatic updates at the rate set in - /// as long as an is attached. - /// - AutoOnEffect, - /// /// The device will perform automatic updates at the rate set in . /// diff --git a/Devices/Headset/CorsairHeadset.cs b/Devices/Headset/CorsairHeadset.cs index b674634..37590b9 100644 --- a/Devices/Headset/CorsairHeadset.cs +++ b/Devices/Headset/CorsairHeadset.cs @@ -5,11 +5,9 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Drawing; using System.Linq; using CUE.NET.Devices.Generic; using CUE.NET.Devices.Headset.Enums; -using CUE.NET.Effects; namespace CUE.NET.Devices.Headset { @@ -43,11 +41,6 @@ namespace CUE.NET.Devices.Headset /// public CorsairHeadsetDeviceInfo HeadsetDeviceInfo { get; } - /// - /// Gets a value indicating if the headset has an active effect to deal with or not. - /// - protected override bool HasEffect => false; - /// /// Gets a read-only collection containing all LEDs of the headset. /// @@ -72,19 +65,22 @@ namespace CUE.NET.Devices.Headset #region Methods - protected override void ApplyEffect(IEffect effect) - { - //TODO DarthAffe 18.10.2015: How should brushes be applied to headsets? - foreach (CorsairLed led in effect.LedList) - led.Color = effect.EffectBrush.GetColorAtPoint(new RectangleF(0, 0, 2, 2), new PointF(1, 1)); - } - private void InitializeLeds() { GetLed((int)CorsairHeadsetLedId.LeftLogo); GetLed((int)CorsairHeadsetLedId.RightLogo); } + protected override void DeviceUpdate() + { + //TODO DarthAffe 21.08.2016: Create something fancy for headsets + } + + protected override void DeviceUpdateEffects() + { + //TODO DarthAffe 21.08.2016: Create something fancy for headsets + } + #region IEnumerable /// diff --git a/Devices/Keyboard/CorsairKeyboard.cs b/Devices/Keyboard/CorsairKeyboard.cs index d0cc226..edb9342 100644 --- a/Devices/Keyboard/CorsairKeyboard.cs +++ b/Devices/Keyboard/CorsairKeyboard.cs @@ -106,18 +106,6 @@ namespace CUE.NET.Devices.Keyboard /// public int ZIndex { get; set; } = 0; - /// - /// Gets a value indicating if the keyboard has an active effect to deal with or not. - /// - protected override bool HasEffect - { - get - { - lock (Effects) - return Effects.Any(); - } - } - #endregion #region Constructors @@ -156,13 +144,9 @@ namespace CUE.NET.Devices.Keyboard } } - protected override void ApplyEffect(IEffect effect) + protected override void DeviceUpdateEffects() { - if (effect == null) return; - - //TODO DarthAffe 18.10.2015: This is really dirty and might have a really negative performance impact - find a better solution. - IEnumerable keys = effect.LedList?.Select(x => this.FirstOrDefault(y => y.Led == x)); - ApplyBrush((keys ?? this).ToList(), effect.EffectBrush); + UpdateEffects(); } // ReSharper disable once MemberCanBeMadeStatic.Local - idc @@ -200,6 +184,30 @@ namespace CUE.NET.Devices.Keyboard #endregion + public void UpdateEffects() + { + lock (_keyGroups) + { + foreach (IKeyGroup keyGroup in _keyGroups) + { + keyGroup.UpdateEffects(); + keyGroup.Brush.UpdateEffects(); + } + } + + Brush.UpdateEffects(); + } + + public void AddEffect(IEffect effect) + { + throw new NotSupportedException("Effects can't be applied directly to the keyboard. Add it to the Brush or create a keygroup instead."); + } + + public void RemoveEffect(IEffect effect) + { + throw new NotSupportedException("Effects can't be applied directly to the keyboard. Add it to the Brush or create a keygroup instead."); + } + /// /// Attaches the given keygroup. /// diff --git a/Devices/Keyboard/Extensions/KeyGroupExtension.cs b/Devices/Keyboard/Extensions/KeyGroupExtension.cs index 629974b..3114a5a 100644 --- a/Devices/Keyboard/Extensions/KeyGroupExtension.cs +++ b/Devices/Keyboard/Extensions/KeyGroupExtension.cs @@ -13,11 +13,11 @@ namespace CUE.NET.Devices.Keyboard.Extensions public static class KeyGroupExtension { /// - /// Converts the given to a . + /// Converts the given to a . /// - /// The to convert. + /// The to convert. /// The converted . - public static ListKeyGroup ToSimpleKeyGroup(this BaseKeyGroup keyGroup) + public static ListKeyGroup ToSimpleKeyGroup(this AbstractKeyGroup keyGroup) { ListKeyGroup simpleKeyGroup = keyGroup as ListKeyGroup; if (simpleKeyGroup == null) @@ -34,7 +34,7 @@ namespace CUE.NET.Devices.Keyboard.Extensions /// The base keygroup. /// The ids of the keys to exclude. /// The new . - public static ListKeyGroup Exclude(this BaseKeyGroup keyGroup, params CorsairKeyboardKeyId[] keyIds) + public static ListKeyGroup Exclude(this AbstractKeyGroup keyGroup, params CorsairKeyboardKeyId[] keyIds) { ListKeyGroup simpleKeyGroup = keyGroup.ToSimpleKeyGroup(); foreach (CorsairKeyboardKeyId keyId in keyIds) @@ -48,7 +48,7 @@ namespace CUE.NET.Devices.Keyboard.Extensions /// The base keygroup. /// The keys to exclude. /// The new . - public static ListKeyGroup Exclude(this BaseKeyGroup keyGroup, params CorsairKey[] keyIds) + public static ListKeyGroup Exclude(this AbstractKeyGroup keyGroup, params CorsairKey[] keyIds) { ListKeyGroup simpleKeyGroup = keyGroup.ToSimpleKeyGroup(); foreach (CorsairKey key in keyIds) @@ -62,7 +62,7 @@ namespace CUE.NET.Devices.Keyboard.Extensions /// /// The keygroup to attach. /// true if the keygroup could be attached; otherwise, false. - public static bool Attach(this BaseKeyGroup keyGroup) + public static bool Attach(this AbstractKeyGroup keyGroup) { return keyGroup.Keyboard?.AttachKeyGroup(keyGroup) ?? false; } @@ -72,7 +72,7 @@ namespace CUE.NET.Devices.Keyboard.Extensions /// /// The keygroup to attach. /// true if the keygroup could be detached; otherwise, false. - public static bool Detach(this BaseKeyGroup keyGroup) + public static bool Detach(this AbstractKeyGroup keyGroup) { return keyGroup.Keyboard?.DetachKeyGroup(keyGroup) ?? false; } diff --git a/Devices/Keyboard/Keys/BaseKeyGroup.cs b/Devices/Keyboard/Keys/AbstractKeyGroup.cs similarity index 87% rename from Devices/Keyboard/Keys/BaseKeyGroup.cs rename to Devices/Keyboard/Keys/AbstractKeyGroup.cs index fa8874c..51927ca 100644 --- a/Devices/Keyboard/Keys/BaseKeyGroup.cs +++ b/Devices/Keyboard/Keys/AbstractKeyGroup.cs @@ -4,13 +4,14 @@ using System.Linq; using CUE.NET.Brushes; using CUE.NET.Devices.Generic; using CUE.NET.Devices.Keyboard.Extensions; +using CUE.NET.Effects; namespace CUE.NET.Devices.Keyboard.Keys { /// /// Represents a basic keygroup. /// - public abstract class BaseKeyGroup : IKeyGroup + public abstract class AbstractKeyGroup : AbstractEffectTarget, IKeyGroup { #region Properties & Fields @@ -39,11 +40,11 @@ namespace CUE.NET.Devices.Keyboard.Keys #region Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The keyboard this keygroup belongs to. /// Specifies whether this group should be automatically attached or not. - protected BaseKeyGroup(CorsairKeyboard keyboard, bool autoAttach = true) + protected AbstractKeyGroup(CorsairKeyboard keyboard, bool autoAttach = true) { this.Keyboard = keyboard; diff --git a/Devices/Keyboard/Keys/IKeyGroup.cs b/Devices/Keyboard/Keys/IKeyGroup.cs index 58891b2..0f60529 100644 --- a/Devices/Keyboard/Keys/IKeyGroup.cs +++ b/Devices/Keyboard/Keys/IKeyGroup.cs @@ -4,10 +4,11 @@ using System.Collections.Generic; using CUE.NET.Brushes; using CUE.NET.Devices.Generic; +using CUE.NET.Effects; namespace CUE.NET.Devices.Keyboard.Keys { - public interface IKeyGroup + public interface IKeyGroup : IEffectTarget { /// /// Gets a read-only collection containing the keys from this group. diff --git a/Devices/Keyboard/Keys/ListKeyGroup.cs b/Devices/Keyboard/Keys/ListKeyGroup.cs index 009d0bf..b5bb241 100644 --- a/Devices/Keyboard/Keys/ListKeyGroup.cs +++ b/Devices/Keyboard/Keys/ListKeyGroup.cs @@ -9,10 +9,12 @@ namespace CUE.NET.Devices.Keyboard.Keys /// /// Represents a keygroup containing arbitrary keys. /// - public class ListKeyGroup : BaseKeyGroup + public class ListKeyGroup : AbstractKeyGroup { #region Properties & Fields + protected override IKeyGroup EffectTarget => this; + /// /// Gets the list containing the keys of this keygroup. /// diff --git a/Devices/Keyboard/Keys/RectangleKeyGroup.cs b/Devices/Keyboard/Keys/RectangleKeyGroup.cs index e02caba..c2a175c 100644 --- a/Devices/Keyboard/Keys/RectangleKeyGroup.cs +++ b/Devices/Keyboard/Keys/RectangleKeyGroup.cs @@ -13,10 +13,12 @@ namespace CUE.NET.Devices.Keyboard.Keys /// /// Represents a keygroup containing keys which physically lay inside a rectangle. /// - public class RectangleKeyGroup : BaseKeyGroup + public class RectangleKeyGroup : AbstractKeyGroup { #region Properties & Fields + protected override IKeyGroup EffectTarget => this; + private IList _keyCache; private RectangleF _rectangle; diff --git a/Devices/Mouse/CorsairMouse.cs b/Devices/Mouse/CorsairMouse.cs index 1b181c3..59d51e7 100644 --- a/Devices/Mouse/CorsairMouse.cs +++ b/Devices/Mouse/CorsairMouse.cs @@ -5,11 +5,9 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Drawing; using System.Linq; using CUE.NET.Devices.Generic; using CUE.NET.Devices.Mouse.Enums; -using CUE.NET.Effects; using CUE.NET.Exceptions; namespace CUE.NET.Devices.Mouse @@ -44,11 +42,6 @@ namespace CUE.NET.Devices.Mouse /// public CorsairMouseDeviceInfo MouseDeviceInfo { get; } - /// - /// Gets a value indicating if the mouse has an active effect to deal with or not. - /// - protected override bool HasEffect => false; - /// /// Gets a read-only collection containing all LEDs of the mouse. /// @@ -74,13 +67,6 @@ namespace CUE.NET.Devices.Mouse #region Methods - protected override void ApplyEffect(IEffect effect) - { - //TODO DarthAffe 18.10.2015: How should brushes be applied to mice? - foreach (CorsairLed led in effect.LedList) - led.Color = effect.EffectBrush.GetColorAtPoint(new RectangleF(0, 0, 2, 2), new PointF(1, 1)); - } - private void InitializeLeds() { switch (MouseDeviceInfo.PhysicalLayout) @@ -108,6 +94,16 @@ namespace CUE.NET.Devices.Mouse } } + protected override void DeviceUpdate() + { + //TODO DarthAffe 21.08.2016: Create something fancy for mice + } + + protected override void DeviceUpdateEffects() + { + //TODO DarthAffe 21.08.2016: Create something fancy for mice + } + #region IEnumerable /// diff --git a/Effects/AbstractEffect.cs b/Effects/AbstractBrushEffect.cs similarity index 55% rename from Effects/AbstractEffect.cs rename to Effects/AbstractBrushEffect.cs index c3ea829..3290c34 100644 --- a/Effects/AbstractEffect.cs +++ b/Effects/AbstractBrushEffect.cs @@ -1,38 +1,26 @@ // ReSharper disable MemberCanBePrivate.Global -using System.Collections.Generic; using CUE.NET.Brushes; -using CUE.NET.Devices.Generic; namespace CUE.NET.Effects { /// - /// Represents a basic effect. + /// Represents a basic effect targeting an . /// - public abstract class AbstractEffect : IEffect + public abstract class AbstractBrushEffect : IEffect { #region Properties & Fields - /// - /// Gets or sets the list of LEDSs to which the effect applies. - /// - public IEnumerable LedList { get; set; } - - /// - /// Gets the brush which is drawn by the effect. - /// - public abstract IBrush EffectBrush { get; } - - /// - /// Gets or sets the z-index of the brush to allow ordering them before drawing. (lowest first) (default: 0) - /// - public int ZIndex { get; set; } = 0; - /// /// Gets or sets if this effect has finished all of his work. /// public bool IsDone { get; protected set; } + /// + /// Gets the this effect is targeting. + /// + protected IBrush Brush { get; private set; } + #endregion #region Methods @@ -46,14 +34,20 @@ namespace CUE.NET.Effects /// /// Hook which is called when the effect is attached to a device. /// - public virtual void OnAttach() - { } + /// The this effect is attached to. + public virtual void OnAttach(IBrush target) + { + Brush = target; + } /// /// Hook which is called when the effect is detached from a device. /// - public virtual void OnDetach() - { } + /// The this effect is detached from. + public virtual void OnDetach(IBrush target) + { + Brush = null; + } #endregion } diff --git a/Effects/AbstractEffectTarget.cs b/Effects/AbstractEffectTarget.cs new file mode 100644 index 0000000..5ba8315 --- /dev/null +++ b/Effects/AbstractEffectTarget.cs @@ -0,0 +1,71 @@ +// ReSharper disable MemberCanBePrivate.Global + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace CUE.NET.Effects +{ + public abstract class AbstractEffectTarget : IEffectTarget + where T : IEffectTarget + { + #region Properties & Fields + + private IList _effectTimes = new List(); + protected IList> Effects => _effectTimes.Select(x => x.Effect).Cast>().ToList(); + protected abstract T EffectTarget { get; } + + #endregion + + #region Methods + + public void UpdateEffects() + { + lock (Effects) + { + for (int i = _effectTimes.Count - 1; i >= 0; i--) + { + EffectTimeContainer effectTime = _effectTimes[i]; + long currentTicks = DateTime.Now.Ticks; + + float deltaTime; + if (effectTime.TicksAtLastUpdate < 0) + { + effectTime.TicksAtLastUpdate = currentTicks; + deltaTime = 0f; + } + else + deltaTime = (currentTicks - effectTime.TicksAtLastUpdate) / 10000000f; + + effectTime.TicksAtLastUpdate = currentTicks; + effectTime.Effect.Update(deltaTime); + + + if (effectTime.Effect.IsDone) + _effectTimes.RemoveAt(i); + } + } + } + + public void AddEffect(IEffect effect) + { + if (_effectTimes.All(x => x.Effect != effect)) + { + effect.OnAttach(EffectTarget); + _effectTimes.Add(new EffectTimeContainer(effect, -1)); + } + } + + public void RemoveEffect(IEffect effect) + { + EffectTimeContainer effectTimeToRemove = _effectTimes.FirstOrDefault(x => x.Effect == effect); + if (effectTimeToRemove != null) + { + effect.OnDetach(EffectTarget); + _effectTimes.Remove(effectTimeToRemove); + } + } + + #endregion + } +} diff --git a/Effects/AbstractKeyGroupEffect.cs b/Effects/AbstractKeyGroupEffect.cs new file mode 100644 index 0000000..71a67ba --- /dev/null +++ b/Effects/AbstractKeyGroupEffect.cs @@ -0,0 +1,54 @@ +// ReSharper disable MemberCanBePrivate.Global + +using CUE.NET.Devices.Keyboard.Keys; + +namespace CUE.NET.Effects +{ + /// + /// Represents a basic effect targeting an . + /// + public abstract class AbstractKeyGroupEffect : IEffect + { + #region Properties & Fields + + /// + /// Gets or sets if this effect has finished all of his work. + /// + public bool IsDone { get; protected set; } + + /// + /// Gets the this effect is targeting. + /// + protected IKeyGroup KeyGroup { get; private set; } + + #endregion + + #region Methods + + /// + /// Updates the effect. + /// + /// The elapsed time (in seconds) since the last update. + public abstract void Update(float deltaTime); + + /// + /// Hook which is called when the effect is attached to a device. + /// + /// The this effect is attached to. + public virtual void OnAttach(IKeyGroup target) + { + KeyGroup = target; + } + + /// + /// Hook which is called when the effect is detached from a device. + /// + /// The this effect is detached from. + public virtual void OnDetach(IKeyGroup target) + { + KeyGroup = null; + } + + #endregion + } +} diff --git a/Effects/EffectTimeContainer.cs b/Effects/EffectTimeContainer.cs index a1c5c8a..25450fa 100644 --- a/Effects/EffectTimeContainer.cs +++ b/Effects/EffectTimeContainer.cs @@ -13,18 +13,13 @@ namespace CUE.NET.Effects /// /// Gets or sets the wrapped effect. /// - public IEffect Effect { get; set; } + public IEffect Effect { get; } /// /// Gets or sets the tick-count from the last time the effect was updated. /// public long TicksAtLastUpdate { get; set; } - /// - /// Gets the z-index of the effect. - /// - public int ZIndex => Effect?.ZIndex ?? 0; - #endregion #region Constructors diff --git a/Effects/FlashEffect.cs b/Effects/FlashEffect.cs index 64ee818..da21172 100644 --- a/Effects/FlashEffect.cs +++ b/Effects/FlashEffect.cs @@ -3,7 +3,6 @@ // ReSharper disable UnusedMember.Global using System; -using System.Drawing; using CUE.NET.Brushes; namespace CUE.NET.Effects @@ -11,15 +10,10 @@ namespace CUE.NET.Effects /// /// Represents an effect which allows to flash an brush by modifying his opacity. /// - public class FlashEffect : AbstractEffect + public class FlashEffect : AbstractBrushEffect { #region Properties & Fields - /// - /// Gets the brush which is drawn by the effect. - /// - public override IBrush EffectBrush { get; } - /// /// Gets or sets the attack-time (in seconds) of the effect. (default: 0.2f)
/// This is close to a synthesizer envelope. (See as reference) @@ -71,27 +65,6 @@ namespace CUE.NET.Effects #endregion - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The color from which a should be created and used by this effect. - public FlashEffect(Color flashColor) - : this(new SolidColorBrush(flashColor)) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The brush which should be used by this effect, - public FlashEffect(IBrush effectBrush) - { - this.EffectBrush = effectBrush; - } - - #endregion - #region Methods /// @@ -106,7 +79,7 @@ namespace CUE.NET.Effects if (_currentPhase == ADSRPhase.Attack) if (_currentPhaseValue > 0f) - EffectBrush.Opacity = Math.Min(1f, (Attack - _currentPhaseValue) / Attack) * AttackValue; + Brush.Opacity = Math.Min(1f, (Attack - _currentPhaseValue) / Attack) * AttackValue; else { _currentPhaseValue = Decay; @@ -115,7 +88,7 @@ namespace CUE.NET.Effects if (_currentPhase == ADSRPhase.Decay) if (_currentPhaseValue > 0f) - EffectBrush.Opacity = SustainValue + (Math.Min(1f, _currentPhaseValue / Decay) * (AttackValue - SustainValue)); + Brush.Opacity = SustainValue + (Math.Min(1f, _currentPhaseValue / Decay) * (AttackValue - SustainValue)); else { _currentPhaseValue = Sustain; @@ -124,7 +97,7 @@ namespace CUE.NET.Effects if (_currentPhase == ADSRPhase.Sustain) if (_currentPhaseValue > 0f) - EffectBrush.Opacity = SustainValue; + Brush.Opacity = SustainValue; else { _currentPhaseValue = Release; @@ -133,7 +106,7 @@ namespace CUE.NET.Effects if (_currentPhase == ADSRPhase.Release) if (_currentPhaseValue > 0f) - EffectBrush.Opacity = Math.Min(1f, _currentPhaseValue / Release) * SustainValue; + Brush.Opacity = Math.Min(1f, _currentPhaseValue / Release) * SustainValue; else { _currentPhaseValue = Interval; @@ -142,7 +115,7 @@ namespace CUE.NET.Effects if (_currentPhase == ADSRPhase.Pause) if (_currentPhaseValue > 0f) - EffectBrush.Opacity = 0f; + Brush.Opacity = 0f; else { if (++_repetitionCount >= Repetitions && Repetitions > 0) @@ -155,14 +128,14 @@ namespace CUE.NET.Effects /// /// Resets the effect. /// - public override void OnAttach() + public override void OnAttach(IBrush brush) { - base.OnAttach(); + base.OnAttach(brush); _currentPhase = ADSRPhase.Attack; _currentPhaseValue = Attack; _repetitionCount = 0; - EffectBrush.Opacity = 0f; + brush.Opacity = 0f; } #endregion diff --git a/Effects/IEffect.cs b/Effects/IEffect.cs index 4cce82c..eb4a663 100644 --- a/Effects/IEffect.cs +++ b/Effects/IEffect.cs @@ -1,10 +1,6 @@ // ReSharper disable UnusedMember.Global // ReSharper disable UnusedMemberInSuper.Global -using System.Collections.Generic; -using CUE.NET.Brushes; -using CUE.NET.Devices.Generic; - namespace CUE.NET.Effects { /// @@ -15,22 +11,7 @@ namespace CUE.NET.Effects #region Properties & Fields /// - /// Gets or sets the list of LEDs to which the effect applies. - /// - IEnumerable LedList { get; set; } - - /// - /// Gets the brush which is drawn by the effect. - /// - IBrush EffectBrush { get; } - - /// - /// Gets or sets the z-index of the effect to allow ordering them before drawing. (lowest first) (default: 0) - /// - int ZIndex { get; set; } - - /// - /// Gets or sets if this effect has finished all of his work. + /// Gets if this effect has finished all of his work. /// bool IsDone { get; } @@ -44,15 +25,29 @@ namespace CUE.NET.Effects /// The elapsed time (in seconds) since the last update. void Update(float deltaTime); + #endregion + } + + /// + /// Represents a basic effect. + /// + /// The type of this effect can be attached to. + public interface IEffect : IEffect + where T : IEffectTarget + { + #region Methods + /// /// Hook which is called when the effect is attached to a device. /// - void OnAttach(); + /// The this effect is attached to. + void OnAttach(T target); /// /// Hook which is called when the effect is detached from a device. /// - void OnDetach(); + /// The this effect is detached from. + void OnDetach(T target); #endregion } diff --git a/Effects/IEffectTarget.cs b/Effects/IEffectTarget.cs new file mode 100644 index 0000000..ba08ae2 --- /dev/null +++ b/Effects/IEffectTarget.cs @@ -0,0 +1,31 @@ +namespace CUE.NET.Effects +{ + /// + /// Represents a basic effect-target. + /// + /// The type this target represents. + public interface IEffectTarget + where T : IEffectTarget + { + #region Methods + + /// + /// Updates all effects added to this target. + /// + void UpdateEffects(); + + /// + /// Adds an affect. + /// + /// The effect to add. + void AddEffect(IEffect effect); + + /// + /// Removes an effect + /// + /// The effect to remove. + void RemoveEffect(IEffect effect); + + #endregion + } +} diff --git a/Effects/RippleEffect.cs b/Effects/RippleEffect.cs new file mode 100644 index 0000000..721c4bd --- /dev/null +++ b/Effects/RippleEffect.cs @@ -0,0 +1,37 @@ +using System; +using System.Drawing; +using CUE.NET.Brushes; + +namespace CUE.NET.Effects +{ + public class RippleEffect : AbstractEffect + { + #region Properties & Fields + + private RippleBrush _brush = new RippleBrush(); + public override IBrush EffectBrush => _brush; + + #endregion + + #region Constructors + + #endregion + + #region Methods + + public override void Update(float deltaTime) + { + throw new NotImplementedException(); + } + + #endregion + + private class RippleBrush : AbstractBrush + { + public override Color GetColorAtPoint(RectangleF rectangle, PointF point) + { + return FinalizeColor(Color.Black); + } + } + } +} diff --git a/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/AudioAnalyzerExample.cs b/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/AudioAnalyzerExample.cs index ae559e3..2c4ca16 100644 --- a/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/AudioAnalyzerExample.cs +++ b/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/AudioAnalyzerExample.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Drawing; using CUE.NET; using CUE.NET.Brushes; +using CUE.NET.Devices.Generic.Enums; using CUE.NET.Devices.Keyboard; +using CUE.NET.Devices.Keyboard.Keys; using CUE.NET.Exceptions; using CUE.NET.Gradients; using Example_AudioAnalyzer_full.TakeAsIs; @@ -60,18 +62,22 @@ namespace Example_AudioAnalyzer_full public void Run() { - // Add a lack background. We want this to be semi-transparent to add some sort of fade-effect - this will smooth everything out a bit + _keyboard.UpdateMode = UpdateMode.Continuous; + // Add a black background. We want this to be semi-transparent to add some sort of fade-effect - this will smooth everything out a bit // Note that this isn't a 'real effect' since it's update-rate dependent. A real effect would do always the same thing not mather how fast the keyboard updates. _keyboard.Brush = new SolidColorBrush(Color.FromArgb(96, 0, 0, 0)); // Add our song-beat-effect. Remember to uncomment the update in the spectrum effect if you want to remove this. - _keyboard.AttachEffect(new SongBeatEffect(_soundDataProcessor, Color.FromArgb(127, 164, 164, 164))); + ListKeyGroup songBeatGroup = new ListKeyGroup(_keyboard, _keyboard); + songBeatGroup.Brush = new SolidColorBrush(Color.FromArgb(127, 164, 164, 164)); + songBeatGroup.Brush.AddEffect(new SongBeatEffect(_soundDataProcessor)); // Add our spectrum-effect using the soundDataProcessor and a rainbow from purple to red as gradient - _keyboard.AttachEffect(new AudioSpectrumEffect(_soundDataProcessor, new RainbowGradient(300, -14))); + ListKeyGroup spectrumGroup = new ListKeyGroup(_keyboard, _keyboard); + spectrumGroup.Brush = new AudioSpectrumBrush(_soundDataProcessor, new RainbowGradient(300, -14)); // Hook onto the keyboard update and process data _keyboard.Updating += (sender, args) => _soundDataProcessor.Process(); - + // If you don't like rainbows replace the gradient with anything you like. For example: //_keyboard.AttachEffect(new AudioSpectrumEffect(_soundDataProcessor, new LinearGradient(new GradientStop(0f, Color.Blue), new GradientStop(1f, Color.Red)))); diff --git a/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/AudioSpectrumBrush.cs b/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/AudioSpectrumBrush.cs index 933d2f5..2149bc2 100644 --- a/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/AudioSpectrumBrush.cs +++ b/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/AudioSpectrumBrush.cs @@ -11,16 +11,18 @@ namespace Example_AudioAnalyzer_full { #region Properties & Fields - public float[] BarData { get; set; } + private SoundDataProcessor _soundDataProcessor; #endregion #region Constructors // default values for start/end are fine - public AudioSpectrumBrush(IGradient gradient) + public AudioSpectrumBrush(SoundDataProcessor soundDataProcessor, IGradient gradient) : base(gradient) - { } + { + this._soundDataProcessor = soundDataProcessor; + } #endregion @@ -28,11 +30,11 @@ namespace Example_AudioAnalyzer_full public override Color GetColorAtPoint(RectangleF rectangle, PointF point) { - if (BarData == null) return Color.Transparent; + if (_soundDataProcessor?.BarValues == null) return Color.Transparent; // This logic is also stolen from AterialDawn - int barSampleIndex = (int)Math.Floor(BarData.Length * (point.X / (rectangle.X + rectangle.Width))); // Calculate bar sampling index - float curBarHeight = 1f - Utility.Clamp(BarData[barSampleIndex], 0f, 1f); // Invert this value since the keyboard is laid out with topleft being point 0,0 + int barSampleIndex = (int)Math.Floor(_soundDataProcessor.BarValues.Length * (point.X / (rectangle.X + rectangle.Width))); // Calculate bar sampling index + float curBarHeight = 1f - Utility.Clamp(_soundDataProcessor.BarValues[barSampleIndex], 0f, 1f); // Invert this value since the keyboard is laid out with topleft being point 0,0 float verticalPos = (point.Y / rectangle.Height); // If the barHeight is lower than the vertical pos currently calculated return the brush value. Otherwise do nothing by returning transparent. diff --git a/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/AudioSpectrumEffect.cs b/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/AudioSpectrumEffect.cs deleted file mode 100644 index 022641c..0000000 --- a/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/AudioSpectrumEffect.cs +++ /dev/null @@ -1,41 +0,0 @@ -using CUE.NET.Brushes; -using CUE.NET.Effects; -using CUE.NET.Gradients; -using Example_AudioAnalyzer_full.TakeAsIs; - -namespace Example_AudioAnalyzer_full -{ - public class AudioSpectrumEffect : AbstractEffect - { - #region Properties & Fields - - private SoundDataProcessor _dataProcessor; - - private AudioSpectrumBrush _audioSpectrumBrush; - public override IBrush EffectBrush => _audioSpectrumBrush; - - #endregion - - #region Constructors - - public AudioSpectrumEffect(SoundDataProcessor dataProcessor, IGradient gradient) - { - this._dataProcessor = dataProcessor; - _audioSpectrumBrush = new AudioSpectrumBrush(gradient); - - // Give this effect a high Z-Index to keep it in the foreground - ZIndex = 10; - } - - #endregion - - #region Methods - - public override void Update(float deltaTime) - { - _audioSpectrumBrush.BarData = _dataProcessor.BarValues; - } - - #endregion - } -} diff --git a/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/Example_AudioAnalyzer_full.csproj b/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/Example_AudioAnalyzer_full.csproj index 6ba9d6d..8bdfa2e 100644 --- a/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/Example_AudioAnalyzer_full.csproj +++ b/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/Example_AudioAnalyzer_full.csproj @@ -62,7 +62,6 @@ - diff --git a/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/SongBeatEffect.cs b/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/SongBeatEffect.cs index 42b54bf..9dfeb96 100644 --- a/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/SongBeatEffect.cs +++ b/Examples/AudioAnalyzer/Example_AudioAnalyzer_full/SongBeatEffect.cs @@ -1,11 +1,9 @@ -using System.Drawing; -using CUE.NET.Brushes; -using CUE.NET.Effects; +using CUE.NET.Effects; using Example_AudioAnalyzer_full.TakeAsIs; namespace Example_AudioAnalyzer_full { - public class SongBeatEffect : AbstractEffect + public class SongBeatEffect : AbstractBrushEffect { #region Constants @@ -22,17 +20,13 @@ namespace Example_AudioAnalyzer_full private float _currentEffect = -1f; - private SolidColorBrush _brush; - public override IBrush EffectBrush => _brush; - #endregion #region Constructors - public SongBeatEffect(SoundDataProcessor dataProcessor, Color color) + public SongBeatEffect(SoundDataProcessor dataProcessor) { this._dataProcessor = dataProcessor; - _brush = new SolidColorBrush(color); } #endregion @@ -48,7 +42,7 @@ namespace Example_AudioAnalyzer_full _currentEffect -= deltaTime; // and set the current brush-opacity - _brush.Opacity = _currentEffect > 0f ? (_currentEffect / FLASH_DURATION) : 0f; + Brush.Opacity = _currentEffect > 0f ? (_currentEffect / FLASH_DURATION) : 0f; } #endregion