From 04a13050dcd60d7d12757a406240a20e58bb9e79 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Thu, 8 Dec 2016 14:46:55 +0100 Subject: [PATCH] Fixed audio layer CPU usage --- Artemis/Artemis/Artemis.csproj | 1 + .../InjectionModules/ProfileModules.cs | 4 + .../AudioCapturing/AudioCaptureManager.cs | 134 ++++++++++++++++++ .../Profiles/Layers/Types/Audio/AudioType.cs | 126 ++++------------ 4 files changed, 167 insertions(+), 98 deletions(-) create mode 100644 Artemis/Artemis/Profiles/Layers/Types/Audio/AudioCapturing/AudioCaptureManager.cs diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj index 8b5e5e92a..7bfc60802 100644 --- a/Artemis/Artemis/Artemis.csproj +++ b/Artemis/Artemis/Artemis.csproj @@ -431,6 +431,7 @@ + AudioPropertiesView.xaml diff --git a/Artemis/Artemis/InjectionModules/ProfileModules.cs b/Artemis/Artemis/InjectionModules/ProfileModules.cs index bca7fa868..8e91b68b8 100644 --- a/Artemis/Artemis/InjectionModules/ProfileModules.cs +++ b/Artemis/Artemis/InjectionModules/ProfileModules.cs @@ -3,6 +3,7 @@ using Artemis.Profiles.Layers.Conditions; using Artemis.Profiles.Layers.Interfaces; using Artemis.Profiles.Layers.Types.AmbientLight; using Artemis.Profiles.Layers.Types.Audio; +using Artemis.Profiles.Layers.Types.Audio.AudioCapturing; using Artemis.Profiles.Layers.Types.Folder; using Artemis.Profiles.Layers.Types.Generic; using Artemis.Profiles.Layers.Types.Headset; @@ -48,6 +49,9 @@ namespace Artemis.InjectionModules Bind().ToSelf(); Bind().ToSelf(); Bind().ToSelf(); + + // Type helpers + Bind().ToSelf().InSingletonScope(); } } } \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioCapturing/AudioCaptureManager.cs b/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioCapturing/AudioCaptureManager.cs new file mode 100644 index 000000000..8d1df47a1 --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioCapturing/AudioCaptureManager.cs @@ -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 GetSpectrumData(int lines) + { + _lastRequest = DateTime.Now; + if (!Running) + Start(); + + var spectrumData = new List(); + + 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; + } + } +} \ 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 e566def44..02e0ec205 100644 --- a/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioType.cs +++ b/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioType.cs @@ -4,60 +4,35 @@ using System.Linq; using System.Windows; using System.Windows.Media; using Artemis.Models.Interfaces; -using Artemis.Modules.Effects.AudioVisualizer.Utilities; using Artemis.Profiles.Layers.Abstract; using Artemis.Profiles.Layers.Interfaces; using Artemis.Profiles.Layers.Models; +using Artemis.Profiles.Layers.Types.Audio.AudioCapturing; using Artemis.Properties; using Artemis.Utilities; using Artemis.ViewModels.Profiles; -using NAudio.CoreAudioApi; -using NAudio.Wave; using Newtonsoft.Json; using Ninject; -using NLog; namespace Artemis.Profiles.Layers.Types.Audio { internal class AudioType : ILayerType { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly List _audioLayers = new List(); - private readonly MMDevice _device; private readonly IKernel _kernel; - private readonly SampleAggregator _sampleAggregator = new SampleAggregator(1024); - private readonly WasapiLoopbackCapture _waveIn; - private DateTime _lastAudioUpdate; + private DateTime _lastUpdate; private int _lines; private string _previousSettings; - public AudioType(IKernel kernel) + public AudioType(IKernel kernel, AudioCaptureManager audioCaptureManager) { _kernel = kernel; - _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; - - try - { - _waveIn.StartRecording(); - } - catch (Exception e) - { - Logger.Warn(e, "Failed to start WASAPI audio capture"); - throw; - } + AudioCaptureManager = audioCaptureManager; } [JsonIgnore] - public List SpectrumData { get; set; } = new List(); + public AudioCaptureManager AudioCaptureManager { get; set; } public string Name => "Keyboard - Audio visualization"; public bool ShowInEdtor => true; @@ -78,7 +53,7 @@ namespace Artemis.Profiles.Layers.Types.Audio public void Draw(LayerModel layerModel, DrawingContext c) { - lock (SpectrumData) + lock (_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) { - if ((_device == null) || isPreview) + layerModel.ApplyProperties(true); + if (isPreview) return; - lock (SpectrumData) + lock (_audioLayers) { 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; - if ((settings.Direction == Direction.TopToBottom) || (settings.Direction == Direction.BottomToTop)) - ApplyVertical(settings); - else if ((settings.Direction == Direction.LeftToRight) || - (settings.Direction == Direction.RightToLeft)) - ApplyHorizontal(settings); + case Direction.TopToBottom: + case Direction.BottomToTop: + ApplyVertical(spectrumData, settings); + break; + case Direction.LeftToRight: + case Direction.RightToLeft: + ApplyHorizontal(spectrumData, settings); + break; } } } @@ -143,14 +125,14 @@ namespace Artemis.Profiles.Layers.Types.Audio return new AudioPropertiesViewModel(layerEditorViewModel); } - private void ApplyVertical(AudioPropertiesModel settings) + private void ApplyVertical(List spectrumData, AudioPropertiesModel settings) { var index = 0; foreach (var audioLayer in _audioLayers) { int height; - if (SpectrumData.Count > index) - height = (int) Math.Round(SpectrumData[index]/2.55); + if (spectrumData.Count > index) + height = (int) Math.Round(spectrumData[index]/2.55); else height = 0; @@ -174,14 +156,14 @@ namespace Artemis.Profiles.Layers.Types.Audio } } - private void ApplyHorizontal(AudioPropertiesModel settings) + private void ApplyHorizontal(List spectrumData, AudioPropertiesModel settings) { var index = 0; foreach (var audioLayer in _audioLayers) { int width; - if (SpectrumData.Count > index) - width = (int) Math.Round(SpectrumData[index]/2.55); + if (spectrumData.Count > index) + width = (int) Math.Round(spectrumData[index]/2.55); else width = 0; @@ -308,57 +290,5 @@ namespace Artemis.Profiles.Layers.Types.Audio 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); - } - } } } \ No newline at end of file