diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj
index bc09abb0e..4642e480e 100644
--- a/Artemis/Artemis/Artemis.csproj
+++ b/Artemis/Artemis/Artemis.csproj
@@ -503,7 +503,7 @@
-
+
diff --git a/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs b/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs
index 59b4889fa..60b146fdb 100644
--- a/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs
+++ b/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs
@@ -27,17 +27,11 @@ namespace Artemis.DeviceProviders
public abstract void Enable();
public abstract void DrawBitmap(Bitmap bitmap);
- ///
- /// Returns a bitmap matching the keyboard's dimensions
- ///
- ///
- public Bitmap KeyboardBitmap() => new Bitmap(Width, Height);
-
///
/// Returns a bitmap matching the keyboard's dimensions using the provided scale
///
///
- public Bitmap KeyboardBitmap(int scale) => new Bitmap(Width*scale, Height*scale);
+ public Bitmap KeyboardBitmap(int scale = 4) => new Bitmap(Width*scale, Height*scale);
public Rect KeyboardRectangle(int scale = 4) => new Rect(new Size(Width*scale, Height*scale));
diff --git a/Artemis/Artemis/InjectionModules/BaseModules.cs b/Artemis/Artemis/InjectionModules/BaseModules.cs
index eb4bc0bad..7447fb67d 100644
--- a/Artemis/Artemis/InjectionModules/BaseModules.cs
+++ b/Artemis/Artemis/InjectionModules/BaseModules.cs
@@ -1,4 +1,5 @@
using Artemis.DeviceProviders;
+using Artemis.Managers;
using Artemis.Models;
using Artemis.Modules.Abstract;
using Artemis.Profiles.Layers.Interfaces;
@@ -104,9 +105,6 @@ namespace Artemis.InjectionModules
.InheritedFrom()
.BindToSelf());
- // Type helpers
- Bind().ToSelf().InSingletonScope();
-
#endregion
#region Lua
diff --git a/Artemis/Artemis/InjectionModules/ManagerModules.cs b/Artemis/Artemis/InjectionModules/ManagerModules.cs
index b01f7fab9..6f6b1c1b3 100644
--- a/Artemis/Artemis/InjectionModules/ManagerModules.cs
+++ b/Artemis/Artemis/InjectionModules/ManagerModules.cs
@@ -13,6 +13,7 @@ namespace Artemis.InjectionModules
Bind().ToSelf().InSingletonScope();
Bind().ToSelf().InSingletonScope();
Bind().ToSelf().InSingletonScope();
+ Bind().ToSelf().InSingletonScope();
}
}
}
\ No newline at end of file
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioCapturing/AudioCaptureManager.cs b/Artemis/Artemis/Managers/AudioCaptureManager.cs
similarity index 94%
rename from Artemis/Artemis/Profiles/Layers/Types/Audio/AudioCapturing/AudioCaptureManager.cs
rename to Artemis/Artemis/Managers/AudioCaptureManager.cs
index de8ccfa2a..52e063838 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioCapturing/AudioCaptureManager.cs
+++ b/Artemis/Artemis/Managers/AudioCaptureManager.cs
@@ -3,10 +3,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Timers;
using Artemis.Events;
+using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
using CSCore.CoreAudioAPI;
using Ninject.Extensions.Logging;
-namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
+namespace Artemis.Managers
{
public class AudioCaptureManager
{
diff --git a/Artemis/Artemis/Managers/LoopManager.cs b/Artemis/Artemis/Managers/LoopManager.cs
index 0066a1394..10500499a 100644
--- a/Artemis/Artemis/Managers/LoopManager.cs
+++ b/Artemis/Artemis/Managers/LoopManager.cs
@@ -52,8 +52,8 @@ namespace Artemis.Managers
//TODO DarthAffe 14.01.2017: A stop-condition and a real cleanup instead of just aborting might be better
while (true)
{
- try
- {
+// try
+// {
long preUpdateTicks = DateTime.Now.Ticks;
Render();
@@ -61,11 +61,11 @@ namespace Artemis.Managers
int sleep = (int)(40f - ((DateTime.Now.Ticks - preUpdateTicks) / 10000f));
if (sleep > 0)
Thread.Sleep(sleep);
- }
- catch (Exception e)
- {
- _logger.Warn(e, "Exception in render loop");
- }
+// }
+// catch (Exception e)
+// {
+// _logger.Warn(e, "Exception in render loop");
+// }
}
// ReSharper disable once FunctionNeverReturns
}
@@ -207,19 +207,19 @@ namespace Artemis.Managers
if (keyboard == null)
return;
- KeyboardBitmap = keyboard.KeyboardBitmap(4);
+ KeyboardBitmap = keyboard.KeyboardBitmap();
KeyboardBitmap.SetResolution(96, 96);
- MouseBitmap = new Bitmap(40, 40);
+ MouseBitmap = new Bitmap(10, 10);
MouseBitmap.SetResolution(96, 96);
- HeadsetBitmap = new Bitmap(40, 40);
+ HeadsetBitmap = new Bitmap(10, 10);
HeadsetBitmap.SetResolution(96, 96);
- GenericBitmap = new Bitmap(40, 40);
+ GenericBitmap = new Bitmap(10, 10);
GenericBitmap.SetResolution(96, 96);
- MousematBitmap = new Bitmap(40, 40);
+ MousematBitmap = new Bitmap(10, 10);
MousematBitmap.SetResolution(96, 96);
using (var g = Graphics.FromImage(KeyboardBitmap))
diff --git a/Artemis/Artemis/Modules/Abstract/ModuleModel.cs b/Artemis/Artemis/Modules/Abstract/ModuleModel.cs
index 7f4209637..9320684b6 100644
--- a/Artemis/Artemis/Modules/Abstract/ModuleModel.cs
+++ b/Artemis/Artemis/Modules/Abstract/ModuleModel.cs
@@ -163,7 +163,7 @@ namespace Artemis.Modules.Abstract
ProfileModel?.DrawLayers(g, layers, DrawType.Keyboard, DataModel, keyboardRect, preview);
}
// Render mice layer-by-layer
- var devRec = new Rect(0, 0, 40, 40);
+ var devRec = new Rect(0, 0, 10, 10);
using (var g = Graphics.FromImage(frame.MouseBitmap))
{
ProfileModel?.DrawLayers(g, layers, DrawType.Mouse, DataModel, devRec, preview);
@@ -183,7 +183,7 @@ namespace Artemis.Modules.Abstract
{
ProfileModel?.DrawLayers(g, layers, DrawType.Mousemat, DataModel, devRec, preview);
}
-
+
// Trace debugging
if (DateTime.Now.AddSeconds(-2) <= _lastTrace || Logger == null)
return;
diff --git a/Artemis/Artemis/Modules/General/GeneralProfile/GeneralProfileDataModel.cs b/Artemis/Artemis/Modules/General/GeneralProfile/GeneralProfileDataModel.cs
index 70e9dcd46..443be1c79 100644
--- a/Artemis/Artemis/Modules/General/GeneralProfile/GeneralProfileDataModel.cs
+++ b/Artemis/Artemis/Modules/General/GeneralProfile/GeneralProfileDataModel.cs
@@ -40,14 +40,8 @@ namespace Artemis.Modules.General.GeneralProfile
public class AudioDevice
{
public float OverallPeak { get; set; }
- public float Channel1Peak { get; set; }
- public float Channel2Peak { get; set; }
- public float Channel3Peak { get; set; }
- public float Channel4Peak { get; set; }
- public float Channel5Peak { get; set; }
- public float Channel6Peak { get; set; }
- public float Channel7Peak { get; set; }
- public float Channel8Peak { get; set; }
+ public float LeftPeak { get; set; }
+ public float RightPeak { get; set; }
}
[MoonSharpUserData]
diff --git a/Artemis/Artemis/Modules/General/GeneralProfile/GeneralProfileModel.cs b/Artemis/Artemis/Modules/General/GeneralProfile/GeneralProfileModel.cs
index 587741268..e0c0a5e60 100644
--- a/Artemis/Artemis/Modules/General/GeneralProfile/GeneralProfileModel.cs
+++ b/Artemis/Artemis/Modules/General/GeneralProfile/GeneralProfileModel.cs
@@ -10,7 +10,6 @@ using Artemis.DAL;
using Artemis.Events;
using Artemis.Managers;
using Artemis.Modules.Abstract;
-using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
using Artemis.Utilities;
using CSCore.CoreAudioAPI;
using Newtonsoft.Json;
@@ -20,7 +19,6 @@ namespace Artemis.Modules.General.GeneralProfile
{
public class GeneralProfileModel : ModuleModel
{
- private readonly AudioCaptureManager _audioCaptureManager;
private DateTime _lastMusicUpdate;
private SpotifyLocalAPI _spotify;
private bool _spotifySetupBusy;
@@ -28,7 +26,6 @@ namespace Artemis.Modules.General.GeneralProfile
public GeneralProfileModel(DeviceManager deviceManager, LuaManager luaManager,
AudioCaptureManager audioCaptureManager) : base(deviceManager, luaManager)
{
- _audioCaptureManager = audioCaptureManager;
_lastMusicUpdate = DateTime.Now;
Settings = SettingsProvider.Load();
@@ -93,31 +90,26 @@ namespace Artemis.Modules.General.GeneralProfile
private void UpdateAudio(GeneralProfileDataModel dataModel)
{
-
- var recording = AudioMeterInformation.FromDevice(_defaultRecording);
- var playback = AudioMeterInformation.FromDevice(_defaultPlayback);
+ // Update microphone, only bother with OverallPeak
+ if (_defaultRecording != null)
+ {
+ var recording = AudioMeterInformation.FromDevice(_defaultRecording);
+ dataModel.Audio.Recording.OverallPeak = recording.PeakValue;
+ }
+
+ if (_defaultPlayback == null)
+ return;
+
+ // Update volume if a default device is found
dataModel.Audio.Volume = AudioEndpointVolume.FromDevice(_defaultPlayback).GetMasterVolumeLevelScalar();
- dataModel.Audio.Recording.OverallPeak = recording.PeakValue;
- for (var i = 0; i < recording.GetChannelsPeakValues(recording.MeteringChannelCount).Length; i++)
- {
- // Only support up to 8 channels until lists are supported natively
- if (i > 7)
- break;
-
- var peakValue = recording.GetChannelsPeakValues(recording.MeteringChannelCount)[i];
- typeof(AudioDevice).GetProperty($"Channel{i + 1}Peak").SetValue(dataModel.Audio.Recording, peakValue);
- }
+ // Update speakers, only do overall, left and right for now
+ // TODO: When adding list support lets do all channels
+ var playback = AudioMeterInformation.FromDevice(_defaultPlayback);
+ var peakValues = playback.GetChannelsPeakValues();
dataModel.Audio.Playback.OverallPeak = playback.PeakValue;
- for (var i = 0; i < playback.GetChannelsPeakValues(playback.MeteringChannelCount).Length; i++)
- {
- // Only support up to 8 channels until lists are supported natively
- if (i > 7)
- break;
-
- var peakValue = playback.GetChannelsPeakValues(playback.MeteringChannelCount)[i];
- typeof(AudioDevice).GetProperty($"Channel{i + 1}Peak").SetValue(dataModel.Audio.Playback, peakValue);
- }
+ dataModel.Audio.Playback.LeftPeak = peakValues[0];
+ dataModel.Audio.Playback.LeftPeak = peakValues[1];
}
#endregion
diff --git a/Artemis/Artemis/Profiles/Layers/Models/TweenModel.cs b/Artemis/Artemis/Profiles/Layers/Models/TweenModel.cs
index 29e679164..70bb2b63f 100644
--- a/Artemis/Artemis/Profiles/Layers/Models/TweenModel.cs
+++ b/Artemis/Artemis/Profiles/Layers/Models/TweenModel.cs
@@ -24,51 +24,87 @@ namespace Artemis.Profiles.Layers.Models
_yTweener = new Tweener((float) layerModel.Y, (float) layerModel.Y, 0);
_widthTweener = new Tweener((float) layerModel.Width, (float) layerModel.Width, 0);
_heightTweener = new Tweener((float) layerModel.Height, (float) layerModel.Height, 0);
- _opacityTweener = new Tweener((float) layerModel.Opacity, (float) layerModel.Opacity, 0);
-
- StoreCurrentValues();
+ _opacityTweener = new Tweener((float) layerModel.Opacity, (float) layerModel.Opacity, 0);
+
+ _x = (float)_layerModel.X;
+ _y = (float)_layerModel.Y;
+ _width = (float)_layerModel.Width;
+ _height = (float)_layerModel.Height;
+ _opacity = (float)_layerModel.Opacity;
}
public void Update()
{
- // Width
+ UpdateWidth();
+ UpdateHeight();
+ UpdateOpacity();
+ }
+
+ private void UpdateWidth()
+ {
+ if (Math.Abs(_layerModel.Properties.WidthEaseTime) < 0.001)
+ return;
+
+ // Width
if (Math.Abs(_layerModel.Width - _width) > 0.001)
{
var widthFunc = GetEaseFunction(_layerModel.Properties.WidthEase);
var widthSpeed = _layerModel.Properties.WidthEaseTime;
- _xTweener = new Tweener(_xTweener.Value, (float) _layerModel.X, widthSpeed, widthFunc);
- _widthTweener = new Tweener(_widthTweener.Value, (float) _layerModel.Width, widthSpeed, widthFunc);
- }
+ _xTweener = new Tweener(_xTweener.Value, (float)_layerModel.X, widthSpeed, widthFunc);
+ _widthTweener = new Tweener(_widthTweener.Value, (float)_layerModel.Width, widthSpeed, widthFunc);
+ }
+
+ _xTweener.Update(40);
+ _widthTweener.Update(40);
+
+ _x = (float) _layerModel.X;
+ _width = (float)_layerModel.Width;
+
+ _layerModel.X = _xTweener.Value;
+ _layerModel.Width = _widthTweener.Value;
+ }
+
+ private void UpdateHeight()
+ {
+ if (Math.Abs(_layerModel.Properties.HeightEaseTime) < 0.001)
+ return;
// Height
if (Math.Abs(_layerModel.Height - _height) > 0.001)
{
var heightFunc = GetEaseFunction(_layerModel.Properties.HeightEase);
var heightSpeed = _layerModel.Properties.HeightEaseTime;
- _yTweener = new Tweener(_y, (float) _layerModel.Y, heightSpeed, heightFunc);
- _heightTweener = new Tweener(_height, (float) _layerModel.Height, heightSpeed, heightFunc);
- }
+ _yTweener = new Tweener(_y, (float)_layerModel.Y, heightSpeed, heightFunc);
+ _heightTweener = new Tweener(_height, (float)_layerModel.Height, heightSpeed, heightFunc);
+ }
+
+ _yTweener.Update(40);
+ _heightTweener.Update(40);
+
+ _y = (float)_layerModel.Y;
+ _height = (float)_layerModel.Height;
+
+ _layerModel.Y = _yTweener.Value;
+ _layerModel.Height = _heightTweener.Value;
+ }
+
+ private void UpdateOpacity()
+ {
+ if (Math.Abs(_layerModel.Properties.OpacityEaseTime) < 0.001)
+ return;
// Opacity
if (Math.Abs(_layerModel.Opacity - _opacity) > 0.001)
{
- _opacityTweener = new Tweener(_opacity, (float) _layerModel.Opacity,
+ _opacityTweener = new Tweener(_opacity, (float)_layerModel.Opacity,
_layerModel.Properties.OpacityEaseTime, GetEaseFunction(_layerModel.Properties.OpacityEase));
}
- _xTweener.Update(40);
- _yTweener.Update(40);
- _widthTweener.Update(40);
- _heightTweener.Update(40);
_opacityTweener.Update(40);
- StoreCurrentValues();
+ _opacity = (float)_layerModel.Opacity;
- _layerModel.X = _xTweener.Value;
- _layerModel.Y = _yTweener.Value;
- _layerModel.Width = _widthTweener.Value;
- _layerModel.Height = _heightTweener.Value;
_layerModel.Opacity = _opacityTweener.Value;
}
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioCapturing/AudioCapture.cs b/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioCapturing/AudioCapture.cs
index 179bd584e..29d488632 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioCapturing/AudioCapture.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioCapturing/AudioCapture.cs
@@ -12,9 +12,11 @@ namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
{
public class AudioCapture
{
- private const FftSize FftSize = CSCore.DSP.FftSize.Fft4096;
+ private const FftSize FftSize = CSCore.DSP.FftSize.Fft1024;
private readonly Timer _volumeTimer;
private readonly double[] _volumeValues;
+ private readonly Timer _disableTimer;
+ private bool _mayStop;
private SingleSpectrum _singleSpectrum;
private WasapiLoopbackCapture _soundIn;
private GainSource _source;
@@ -30,11 +32,12 @@ namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
_volumeValues = new double[5];
_volumeIndex = 0;
+ _disableTimer = new Timer(1000);
+ _disableTimer.Elapsed += CheckStop;
_volumeTimer = new Timer(200);
_volumeTimer.Elapsed += VolumeTimerOnElapsed;
- Start();
}
-
+
public ILogger Logger { get; }
public MMDevice Device { get; }
public double DesiredAverage { get; set; }
@@ -45,6 +48,8 @@ namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
set { _volume.Volume = value; }
}
+ public bool Running { get; set; }
+
private void VolumeTimerOnElapsed(object sender, ElapsedEventArgs e)
{
if (Volume <= 0)
@@ -100,12 +105,36 @@ namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
};
}
+ ///
+ /// Keeps the audio capture active, when not called for longer than 1 sec the capture will
+ /// stop capturing until Pulse is called again
+ ///
+ public void Pulse()
+ {
+ _mayStop = false;
+ if (!Running)
+ Start();
+ }
+
+ private void CheckStop(object sender, ElapsedEventArgs e)
+ {
+ if (_mayStop)
+ {
+ Logger.Debug("Stopping idle audio capture for device: {0}", Device?.FriendlyName ?? "default");
+ Stop();
+ }
+ else
+ _mayStop = true;
+ }
+
private void Start()
{
Logger.Debug("Starting audio capture for device: {0}", Device?.FriendlyName ?? "default");
try
{
+ Stop();
+
_soundIn = new WasapiLoopbackCapture();
_soundIn.Initialize();
// Not sure if this null check is needed but doesnt hurt
@@ -135,14 +164,39 @@ namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
};
_singleSpectrum = new SingleSpectrum(FftSize, _spectrumProvider);
+ _mayStop = false;
+ _disableTimer.Start();
_volumeTimer.Start();
_soundIn.Start();
+
+ Running = true;
}
catch (Exception e)
{
Logger.Warn(e, "Failed to start WASAPI audio capture");
}
}
+
+ private void Stop()
+ {
+ Running = false;
+
+
+ if (_soundIn != null)
+ {
+ _soundIn.Stop();
+ _soundIn.Dispose();
+ _soundIn = null;
+ }
+ if (_source != null)
+ {
+ _source.Dispose();
+ _source = null;
+ }
+
+ _disableTimer.Stop();
+ _volumeTimer.Stop();
+ }
}
}
\ No newline at end of file
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioType.cs b/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioType.cs
index 2e52bff3c..f7e0a5c16 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioType.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioType.cs
@@ -5,6 +5,7 @@ using System.Threading;
using System.Windows;
using System.Windows.Media;
using Artemis.Events;
+using Artemis.Managers;
using Artemis.Modules.Abstract;
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Animations;
@@ -122,6 +123,7 @@ namespace Artemis.Profiles.Layers.Types.Audio
public void Update(LayerModel layerModel, ModuleDataModel dataModel, bool isPreview = false)
{
layerModel.ApplyProperties(true);
+ _audioCapture.Pulse();
var direction = ((AudioPropertiesModel) layerModel.Properties).Direction;
diff --git a/Artemis/Artemis/Utilities/ImageUtilities.cs b/Artemis/Artemis/Utilities/ImageUtilities.cs
index d8ac1122d..ad33435cf 100644
--- a/Artemis/Artemis/Utilities/ImageUtilities.cs
+++ b/Artemis/Artemis/Utilities/ImageUtilities.cs
@@ -4,11 +4,15 @@ using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
+using PixelFormat = System.Drawing.Imaging.PixelFormat;
+using Point = System.Drawing.Point;
namespace Artemis.Utilities
{
public class ImageUtilities
{
+ private static RenderTargetBitmap _rBmp;
+
///
/// Resize the image to the specified width and height.
///
@@ -56,21 +60,26 @@ namespace Artemis.Utilities
public static Bitmap DrawingVisualToBitmap(DrawingVisual visual, Rect rect)
{
- // TODO: Improve performance by dividing by 4 here
- var bmp = new RenderTargetBitmap((int) rect.Width, (int) rect.Height, 96, 96, PixelFormats.Pbgra32);
- bmp.Render(visual);
+ var width = (int) rect.Width;
+ var height = (int) rect.Height;
- var encoder = new BmpBitmapEncoder();
- encoder.Frames.Add(BitmapFrame.Create(bmp));
+ // RenderTargetBitmap construction is expensive, only do it when needed
+ if (_rBmp?.PixelHeight != height || _rBmp?.PixelWidth != width)
+ _rBmp = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
- Bitmap bitmap;
- using (var stream = new MemoryStream())
- {
- encoder.Save(stream);
- bitmap = new Bitmap(stream);
- }
+ _rBmp.Render(visual);
+ return GetBitmap(_rBmp);
+ }
- return bitmap;
+ private static Bitmap GetBitmap(BitmapSource source)
+ {
+ var bmp = new Bitmap(source.PixelWidth, source.PixelHeight, PixelFormat.Format32bppPArgb);
+ bmp.SetResolution(96, 96);
+ var data = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.WriteOnly,
+ PixelFormat.Format32bppPArgb);
+ source.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride);
+ bmp.UnlockBits(data);
+ return bmp;
}
///