mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-31 09:43:46 +00:00
Fixed audio layer CPU usage
This commit is contained in:
parent
56c5a41033
commit
04a13050dc
@ -431,6 +431,7 @@
|
|||||||
<Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\DX9ScreenCapture.cs" />
|
<Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\DX9ScreenCapture.cs" />
|
||||||
<Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\IScreenCapture.cs" />
|
<Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\IScreenCapture.cs" />
|
||||||
<Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\ScreenCaptureManager.cs" />
|
<Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\ScreenCaptureManager.cs" />
|
||||||
|
<Compile Include="Profiles\Layers\Types\Audio\AudioCapturing\AudioCaptureManager.cs" />
|
||||||
<Compile Include="Profiles\Layers\Types\Audio\AudioPropertiesModel.cs" />
|
<Compile Include="Profiles\Layers\Types\Audio\AudioPropertiesModel.cs" />
|
||||||
<Compile Include="Profiles\Layers\Types\Audio\AudioPropertiesView.xaml.cs">
|
<Compile Include="Profiles\Layers\Types\Audio\AudioPropertiesView.xaml.cs">
|
||||||
<DependentUpon>AudioPropertiesView.xaml</DependentUpon>
|
<DependentUpon>AudioPropertiesView.xaml</DependentUpon>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ using Artemis.Profiles.Layers.Conditions;
|
|||||||
using Artemis.Profiles.Layers.Interfaces;
|
using Artemis.Profiles.Layers.Interfaces;
|
||||||
using Artemis.Profiles.Layers.Types.AmbientLight;
|
using Artemis.Profiles.Layers.Types.AmbientLight;
|
||||||
using Artemis.Profiles.Layers.Types.Audio;
|
using Artemis.Profiles.Layers.Types.Audio;
|
||||||
|
using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
|
||||||
using Artemis.Profiles.Layers.Types.Folder;
|
using Artemis.Profiles.Layers.Types.Folder;
|
||||||
using Artemis.Profiles.Layers.Types.Generic;
|
using Artemis.Profiles.Layers.Types.Generic;
|
||||||
using Artemis.Profiles.Layers.Types.Headset;
|
using Artemis.Profiles.Layers.Types.Headset;
|
||||||
@ -48,6 +49,9 @@ namespace Artemis.InjectionModules
|
|||||||
Bind<KeyPressType>().ToSelf();
|
Bind<KeyPressType>().ToSelf();
|
||||||
Bind<AudioType>().ToSelf();
|
Bind<AudioType>().ToSelf();
|
||||||
Bind<AmbientLightType>().ToSelf();
|
Bind<AmbientLightType>().ToSelf();
|
||||||
|
|
||||||
|
// Type helpers
|
||||||
|
Bind<AudioCaptureManager>().ToSelf().InSingletonScope();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,134 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Artemis.Modules.Effects.AudioVisualizer.Utilities;
|
||||||
|
using NAudio.CoreAudioApi;
|
||||||
|
using NAudio.Dsp;
|
||||||
|
using NAudio.Wave;
|
||||||
|
using Ninject.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
|
||||||
|
{
|
||||||
|
public class AudioCaptureManager
|
||||||
|
{
|
||||||
|
private readonly SampleAggregator _sampleAggregator = new SampleAggregator(1024);
|
||||||
|
private readonly WasapiLoopbackCapture _waveIn;
|
||||||
|
private Complex[] _fft;
|
||||||
|
private DateTime _lastAudioUpdate;
|
||||||
|
private DateTime _lastRequest;
|
||||||
|
|
||||||
|
public AudioCaptureManager(ILogger logger)
|
||||||
|
{
|
||||||
|
Logger = logger;
|
||||||
|
Device = new MMDeviceEnumerator().EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active).FirstOrDefault();
|
||||||
|
|
||||||
|
_sampleAggregator.FftCalculated += FftCalculated;
|
||||||
|
_sampleAggregator.PerformFFT = true;
|
||||||
|
|
||||||
|
// Start listening for sound data
|
||||||
|
_waveIn = new WasapiLoopbackCapture();
|
||||||
|
_waveIn.DataAvailable += OnDataAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger Logger { get; set; }
|
||||||
|
public MMDevice Device { get; set; }
|
||||||
|
|
||||||
|
public bool Running { get; set; }
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
if (Running)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_waveIn.StartRecording();
|
||||||
|
Running = true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Warn(e, "Failed to start WASAPI audio capture");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
if (!Running)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_waveIn.StopRecording();
|
||||||
|
Running = false;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Warn(e, "Failed to start WASAPI audio capture");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FftCalculated(object sender, FftEventArgs e)
|
||||||
|
{
|
||||||
|
_fft = e.Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDataAvailable(object sender, WaveInEventArgs e)
|
||||||
|
{
|
||||||
|
if (DateTime.Now - _lastAudioUpdate < TimeSpan.FromMilliseconds(40))
|
||||||
|
return;
|
||||||
|
if (DateTime.Now - _lastRequest > TimeSpan.FromSeconds(5))
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastAudioUpdate = DateTime.Now;
|
||||||
|
|
||||||
|
var buffer = e.Buffer;
|
||||||
|
var bytesRecorded = e.BytesRecorded;
|
||||||
|
var bufferIncrement = _waveIn.WaveFormat.BlockAlign;
|
||||||
|
|
||||||
|
for (var index = 0; index < bytesRecorded; index += bufferIncrement)
|
||||||
|
{
|
||||||
|
var sample32 = BitConverter.ToSingle(buffer, index);
|
||||||
|
_sampleAggregator.Add(sample32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<byte> GetSpectrumData(int lines)
|
||||||
|
{
|
||||||
|
_lastRequest = DateTime.Now;
|
||||||
|
if (!Running)
|
||||||
|
Start();
|
||||||
|
|
||||||
|
var spectrumData = new List<byte>();
|
||||||
|
|
||||||
|
if (_fft == null)
|
||||||
|
return spectrumData;
|
||||||
|
|
||||||
|
int x;
|
||||||
|
var b0 = 0;
|
||||||
|
|
||||||
|
for (x = 0; x < lines; x++)
|
||||||
|
{
|
||||||
|
float peak = 0;
|
||||||
|
var b1 = (int) Math.Pow(2, x*10.0/(lines - 1));
|
||||||
|
if (b1 > 1023)
|
||||||
|
b1 = 1023;
|
||||||
|
if (b1 <= b0)
|
||||||
|
b1 = b0 + 1;
|
||||||
|
for (; b0 < b1; b0++)
|
||||||
|
if (peak < _fft[1 + b0].X)
|
||||||
|
peak = _fft[1 + b0].X;
|
||||||
|
var y = (int) (Math.Sqrt(peak)*3*255 - 4);
|
||||||
|
if (y > 255)
|
||||||
|
y = 255;
|
||||||
|
if (y < 0)
|
||||||
|
y = 0;
|
||||||
|
spectrumData.Add((byte) y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return spectrumData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,60 +4,35 @@ using System.Linq;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using Artemis.Models.Interfaces;
|
using Artemis.Models.Interfaces;
|
||||||
using Artemis.Modules.Effects.AudioVisualizer.Utilities;
|
|
||||||
using Artemis.Profiles.Layers.Abstract;
|
using Artemis.Profiles.Layers.Abstract;
|
||||||
using Artemis.Profiles.Layers.Interfaces;
|
using Artemis.Profiles.Layers.Interfaces;
|
||||||
using Artemis.Profiles.Layers.Models;
|
using Artemis.Profiles.Layers.Models;
|
||||||
|
using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
|
||||||
using Artemis.Properties;
|
using Artemis.Properties;
|
||||||
using Artemis.Utilities;
|
using Artemis.Utilities;
|
||||||
using Artemis.ViewModels.Profiles;
|
using Artemis.ViewModels.Profiles;
|
||||||
using NAudio.CoreAudioApi;
|
|
||||||
using NAudio.Wave;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using NLog;
|
|
||||||
|
|
||||||
namespace Artemis.Profiles.Layers.Types.Audio
|
namespace Artemis.Profiles.Layers.Types.Audio
|
||||||
{
|
{
|
||||||
internal class AudioType : ILayerType
|
internal class AudioType : ILayerType
|
||||||
{
|
{
|
||||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
|
||||||
private readonly List<LayerModel> _audioLayers = new List<LayerModel>();
|
private readonly List<LayerModel> _audioLayers = new List<LayerModel>();
|
||||||
private readonly MMDevice _device;
|
|
||||||
private readonly IKernel _kernel;
|
private readonly IKernel _kernel;
|
||||||
private readonly SampleAggregator _sampleAggregator = new SampleAggregator(1024);
|
|
||||||
private readonly WasapiLoopbackCapture _waveIn;
|
|
||||||
private DateTime _lastAudioUpdate;
|
|
||||||
private DateTime _lastUpdate;
|
private DateTime _lastUpdate;
|
||||||
private int _lines;
|
private int _lines;
|
||||||
private string _previousSettings;
|
private string _previousSettings;
|
||||||
|
|
||||||
public AudioType(IKernel kernel)
|
public AudioType(IKernel kernel, AudioCaptureManager audioCaptureManager)
|
||||||
{
|
{
|
||||||
_kernel = kernel;
|
_kernel = kernel;
|
||||||
_device = new MMDeviceEnumerator()
|
AudioCaptureManager = audioCaptureManager;
|
||||||
.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active).FirstOrDefault();
|
|
||||||
|
|
||||||
_sampleAggregator.FftCalculated += FftCalculated;
|
|
||||||
_sampleAggregator.PerformFFT = true;
|
|
||||||
|
|
||||||
// Start listening for sound data
|
|
||||||
_waveIn = new WasapiLoopbackCapture();
|
|
||||||
_waveIn.DataAvailable += OnDataAvailable;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_waveIn.StartRecording();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.Warn(e, "Failed to start WASAPI audio capture");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public List<byte> SpectrumData { get; set; } = new List<byte>();
|
public AudioCaptureManager AudioCaptureManager { get; set; }
|
||||||
|
|
||||||
public string Name => "Keyboard - Audio visualization";
|
public string Name => "Keyboard - Audio visualization";
|
||||||
public bool ShowInEdtor => true;
|
public bool ShowInEdtor => true;
|
||||||
@ -78,7 +53,7 @@ namespace Artemis.Profiles.Layers.Types.Audio
|
|||||||
|
|
||||||
public void Draw(LayerModel layerModel, DrawingContext c)
|
public void Draw(LayerModel layerModel, DrawingContext c)
|
||||||
{
|
{
|
||||||
lock (SpectrumData)
|
lock (_audioLayers)
|
||||||
{
|
{
|
||||||
foreach (var audioLayer in _audioLayers)
|
foreach (var audioLayer in _audioLayers)
|
||||||
{
|
{
|
||||||
@ -104,21 +79,28 @@ namespace Artemis.Profiles.Layers.Types.Audio
|
|||||||
|
|
||||||
public void Update(LayerModel layerModel, IDataModel dataModel, bool isPreview = false)
|
public void Update(LayerModel layerModel, IDataModel dataModel, bool isPreview = false)
|
||||||
{
|
{
|
||||||
if ((_device == null) || isPreview)
|
layerModel.ApplyProperties(true);
|
||||||
|
if (isPreview)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lock (SpectrumData)
|
lock (_audioLayers)
|
||||||
{
|
{
|
||||||
SetupLayers(layerModel);
|
SetupLayers(layerModel);
|
||||||
|
var spectrumData = AudioCaptureManager.GetSpectrumData(_lines);
|
||||||
|
if (!spectrumData.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
if (SpectrumData.Any())
|
var settings = (AudioPropertiesModel) layerModel.Properties;
|
||||||
|
switch (settings.Direction)
|
||||||
{
|
{
|
||||||
var settings = (AudioPropertiesModel) layerModel.Properties;
|
case Direction.TopToBottom:
|
||||||
if ((settings.Direction == Direction.TopToBottom) || (settings.Direction == Direction.BottomToTop))
|
case Direction.BottomToTop:
|
||||||
ApplyVertical(settings);
|
ApplyVertical(spectrumData, settings);
|
||||||
else if ((settings.Direction == Direction.LeftToRight) ||
|
break;
|
||||||
(settings.Direction == Direction.RightToLeft))
|
case Direction.LeftToRight:
|
||||||
ApplyHorizontal(settings);
|
case Direction.RightToLeft:
|
||||||
|
ApplyHorizontal(spectrumData, settings);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,14 +125,14 @@ namespace Artemis.Profiles.Layers.Types.Audio
|
|||||||
return new AudioPropertiesViewModel(layerEditorViewModel);
|
return new AudioPropertiesViewModel(layerEditorViewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyVertical(AudioPropertiesModel settings)
|
private void ApplyVertical(List<byte> spectrumData, AudioPropertiesModel settings)
|
||||||
{
|
{
|
||||||
var index = 0;
|
var index = 0;
|
||||||
foreach (var audioLayer in _audioLayers)
|
foreach (var audioLayer in _audioLayers)
|
||||||
{
|
{
|
||||||
int height;
|
int height;
|
||||||
if (SpectrumData.Count > index)
|
if (spectrumData.Count > index)
|
||||||
height = (int) Math.Round(SpectrumData[index]/2.55);
|
height = (int) Math.Round(spectrumData[index]/2.55);
|
||||||
else
|
else
|
||||||
height = 0;
|
height = 0;
|
||||||
|
|
||||||
@ -174,14 +156,14 @@ namespace Artemis.Profiles.Layers.Types.Audio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyHorizontal(AudioPropertiesModel settings)
|
private void ApplyHorizontal(List<byte> spectrumData, AudioPropertiesModel settings)
|
||||||
{
|
{
|
||||||
var index = 0;
|
var index = 0;
|
||||||
foreach (var audioLayer in _audioLayers)
|
foreach (var audioLayer in _audioLayers)
|
||||||
{
|
{
|
||||||
int width;
|
int width;
|
||||||
if (SpectrumData.Count > index)
|
if (spectrumData.Count > index)
|
||||||
width = (int) Math.Round(SpectrumData[index]/2.55);
|
width = (int) Math.Round(spectrumData[index]/2.55);
|
||||||
else
|
else
|
||||||
width = 0;
|
width = 0;
|
||||||
|
|
||||||
@ -308,57 +290,5 @@ namespace Artemis.Profiles.Layers.Types.Audio
|
|||||||
layer.Update(null, false, true);
|
layer.Update(null, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void FftCalculated(object sender, FftEventArgs e)
|
|
||||||
{
|
|
||||||
if (_lines == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock (SpectrumData)
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
var b0 = 0;
|
|
||||||
|
|
||||||
SpectrumData.Clear();
|
|
||||||
for (x = 0; x < _lines; x++)
|
|
||||||
{
|
|
||||||
float peak = 0;
|
|
||||||
var b1 = (int) Math.Pow(2, x*10.0/(_lines - 1));
|
|
||||||
if (b1 > 1023)
|
|
||||||
b1 = 1023;
|
|
||||||
if (b1 <= b0)
|
|
||||||
b1 = b0 + 1;
|
|
||||||
for (; b0 < b1; b0++)
|
|
||||||
if (peak < e.Result[1 + b0].X)
|
|
||||||
peak = e.Result[1 + b0].X;
|
|
||||||
var y = (int) (Math.Sqrt(peak)*3*255 - 4);
|
|
||||||
if (y > 255)
|
|
||||||
y = 255;
|
|
||||||
if (y < 0)
|
|
||||||
y = 0;
|
|
||||||
SpectrumData.Add((byte) y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Check how often this is called
|
|
||||||
private void OnDataAvailable(object sender, WaveInEventArgs e)
|
|
||||||
{
|
|
||||||
if (DateTime.Now - _lastAudioUpdate < TimeSpan.FromMilliseconds(40))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_lastAudioUpdate = DateTime.Now;
|
|
||||||
|
|
||||||
var buffer = e.Buffer;
|
|
||||||
var bytesRecorded = e.BytesRecorded;
|
|
||||||
var bufferIncrement = _waveIn.WaveFormat.BlockAlign;
|
|
||||||
|
|
||||||
for (var index = 0; index < bytesRecorded; index += bufferIncrement)
|
|
||||||
{
|
|
||||||
var sample32 = BitConverter.ToSingle(buffer, index);
|
|
||||||
_sampleAggregator.Add(sample32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user