1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Fixed audio layer not picking up device change

This commit is contained in:
SpoinkyNL 2017-01-23 19:01:34 +01:00
parent eeb5c8aa90
commit ab17119fbb
8 changed files with 187 additions and 61 deletions

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Timers;
using Artemis.Events;
using Artemis.Profiles.Layers.Types.Audio;
using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
using CSCore.CoreAudioAPI;
using Ninject.Extensions.Logging;
@ -19,8 +20,8 @@ namespace Artemis.Managers
{
Logger = logger;
_audioCaptures = new List<AudioCapture>();
_lastDefaultPlayback = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
_lastDefaultRecording = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);
_lastDefaultPlayback = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
_lastDefaultRecording = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);
var defaultDeviceTimer = new Timer(1000);
defaultDeviceTimer.Elapsed += DefaultDeviceTimerOnElapsed;
@ -31,11 +32,11 @@ namespace Artemis.Managers
private void DefaultDeviceTimerOnElapsed(object sender, ElapsedEventArgs e)
{
var defaultPlayback = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
var defaultRecording = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);
var defaultPlayback = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
var defaultRecording = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);
if (defaultPlayback.DeviceID == _lastDefaultPlayback.DeviceID &&
defaultRecording.DeviceID == _lastDefaultRecording.DeviceID)
if (defaultPlayback?.DeviceID == _lastDefaultPlayback?.DeviceID &&
defaultRecording?.DeviceID == _lastDefaultRecording?.DeviceID)
return;
_lastDefaultPlayback = defaultPlayback;
@ -43,7 +44,7 @@ namespace Artemis.Managers
OnAudioDeviceChanged(new AudioDeviceChangedEventArgs(_lastDefaultPlayback, _lastDefaultRecording));
}
public AudioCapture GetAudioCapture(MMDevice device)
public AudioCapture GetAudioCapture(MMDevice device, MmDeviceType type)
{
// Return existing audio capture if found
var audioCapture = _audioCaptures.FirstOrDefault(a => a.Device.DeviceID == device.DeviceID);
@ -51,7 +52,7 @@ namespace Artemis.Managers
return audioCapture;
// Else create a new one and return that
var newAudioCapture = new AudioCapture(Logger, device);
var newAudioCapture = new AudioCapture(Logger, device, type);
_audioCaptures.Add(newAudioCapture);
return newAudioCapture;
}

View File

@ -91,7 +91,7 @@ namespace Artemis.Modules.Abstract
private void UpdatedEnabledSetting()
{
if (!ModuleModel.IsGeneral || !_moduleManager.ActiveModule.IsGeneral || Settings.IsEnabled == IsModuleActive)
if (!ModuleModel.IsGeneral || (_moduleManager.ActiveModule != null && !_moduleManager.ActiveModule.IsGeneral || Settings.IsEnabled == IsModuleActive))
return;
Settings.IsEnabled = IsModuleActive;

View File

@ -80,29 +80,32 @@ namespace Artemis.Modules.General.GeneralProfile
private void SetupAudio()
{
_defaultRecording = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);
_recordingInfo = AudioMeterInformation.FromDevice(_defaultRecording);
_defaultPlayback = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
_playbackInfo = AudioMeterInformation.FromDevice(_defaultPlayback);
_defaultRecording = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);
_defaultPlayback = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
if (_defaultRecording != null)
_recordingInfo = AudioMeterInformation.FromDevice(_defaultRecording);
if (_defaultPlayback != null)
_playbackInfo = AudioMeterInformation.FromDevice(_defaultPlayback);
}
private void AudioDeviceChanged(object sender, AudioDeviceChangedEventArgs e)
{
_defaultRecording = e.DefaultRecording;
_recordingInfo = AudioMeterInformation.FromDevice(_defaultRecording);
_defaultPlayback = e.DefaultPlayback;
_playbackInfo = AudioMeterInformation.FromDevice(_defaultPlayback);
if (_defaultRecording != null)
_recordingInfo = AudioMeterInformation.FromDevice(_defaultRecording);
if (_defaultPlayback != null)
_playbackInfo = AudioMeterInformation.FromDevice(_defaultPlayback);
}
private void UpdateAudio(GeneralProfileDataModel dataModel)
{
// Update microphone, only bother with OverallPeak
if (_defaultRecording != null)
{
dataModel.Audio.Recording.OverallPeak = _recordingInfo.PeakValue;
}
if (_defaultPlayback == null)
return;

View File

@ -17,8 +17,10 @@ namespace Artemis.Modules.Overlays.OverlayProfile
Settings = SettingsProvider.Load<OverlayProfileSettings>();
DataModel = new OverlayProfileDataModel();
var defaultPlayback = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
_endPointVolume = AudioEndpointVolume.FromDevice(defaultPlayback);
var defaultPlayback = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
if (defaultPlayback != null)
_endPointVolume = AudioEndpointVolume.FromDevice(defaultPlayback);
audioCaptureManager.AudioDeviceChanged += OnAudioDeviceChanged;
Enable();
@ -30,7 +32,8 @@ namespace Artemis.Modules.Overlays.OverlayProfile
private void OnAudioDeviceChanged(object sender, AudioDeviceChangedEventArgs e)
{
_endPointVolume = AudioEndpointVolume.FromDevice(e.DefaultPlayback);
if (e.DefaultPlayback != null)
_endPointVolume = AudioEndpointVolume.FromDevice(e.DefaultPlayback);
}
public override void Update()

View File

@ -5,6 +5,7 @@ using CSCore;
using CSCore.CoreAudioAPI;
using CSCore.DSP;
using CSCore.SoundIn;
using CSCore.SoundOut;
using CSCore.Streams;
using Ninject.Extensions.Logging;
@ -18,16 +19,17 @@ namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
private readonly Timer _disableTimer;
private bool _mayStop;
private SingleSpectrum _singleSpectrum;
private WasapiLoopbackCapture _soundIn;
private ISoundIn _soundIn;
private GainSource _source;
private BasicSpectrumProvider _spectrumProvider;
private GainSource _volume;
private int _volumeIndex;
public AudioCapture(ILogger logger, MMDevice device)
public AudioCapture(ILogger logger, MMDevice device, MmDeviceType type)
{
Logger = logger;
Device = device;
Type = type;
DesiredAverage = 0.75;
_volumeValues = new double[5];
@ -40,6 +42,7 @@ namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
public ILogger Logger { get; }
public MMDevice Device { get; }
public MmDeviceType Type { get; }
public double DesiredAverage { get; set; }
public float Volume
@ -95,6 +98,8 @@ namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
public LineSpectrum GetLineSpectrum(int barCount, ScalingStrategy scalingStrategy)
{
if (_spectrumProvider == null)
return null;
return new LineSpectrum(FftSize)
{
SpectrumProvider = _spectrumProvider,
@ -135,11 +140,20 @@ namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
{
Stop();
_soundIn = new WasapiLoopbackCapture();
if (Type == MmDeviceType.Input)
{
_soundIn = Device != null
? new WasapiCapture {Device = Device}
: new WasapiCapture();
}
else
{
_soundIn = Device != null
? new WasapiLoopbackCapture {Device = Device}
: new WasapiLoopbackCapture();
}
_soundIn.Initialize();
// Not sure if this null check is needed but doesnt hurt
if (Device != null)
_soundIn.Device = Device;
var soundInSource = new SoundInSource(_soundIn);
_source = soundInSource.ToSampleSource().AppendSource(x => new GainSource(x), out _volume);
@ -182,7 +196,7 @@ namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
{
Running = false;
if (_soundIn != null)
{
_soundIn.Stop();

View File

@ -11,6 +11,13 @@
d:DesignHeight="600" d:DesignWidth="500">
<UserControl.Resources>
<converters:EnumDescriptionConverter x:Key="HEnumDescriptionConverter" />
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type system:Enum}"
x:Key="MmDeviceTypeEnumValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="properties:MmDeviceType" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type system:Enum}"
x:Key="DirectionEnumValues">
@ -54,22 +61,23 @@
TickPlacement="None" TickFrequency="0.05"
Value="{Binding Path=LayerModel.Properties.AnimationSpeed, Mode=TwoWay}" Minimum="0.05" Maximum="3"
SmallChange="0" IsSnapToTickEnabled="True" Margin="10,12,10,2" Height="24" />
<!-- Volume sensitivity -->
<TextBlock Grid.Row="1" Grid.Column="0" Margin="10,13,10,10" FontSize="13.333" Text="Volume sensitivity:"
Height="18" VerticalAlignment="Top" />
<Slider x:Name="Scale" Grid.Row="1" Grid.Column="1" VerticalAlignment="Top"
Value="{Binding Path=LayerModel.Properties.Sensitivity, Mode=TwoWay}" Margin="10,12,10,2" Height="24"
TickPlacement="BottomRight" TickFrequency="1" Minimum="1" Maximum="10" SmallChange="1"
IsSnapToTickEnabled="True" />
<!-- Fade-out speed -->
<TextBlock Grid.Row="1" Grid.Column="2" Margin="10,13,10,10" FontSize="13.333" Text="Fade-out speed:"
<!-- Device type -->
<TextBlock Grid.Row="1" Grid.Column="0" Margin="10,13,10,10" FontSize="13.333" Text="Device type:"
Height="18" VerticalAlignment="Top" />
<ComboBox Grid.Row="1" Grid.Column="1" ItemsSource="{Binding Source={StaticResource MmDeviceTypeEnumValues}}"
Margin="10,10,10,0" SelectedItem="{Binding Path=DeviceType}"
VerticalAlignment="Top" Height="22">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource HEnumDescriptionConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Device -->
<TextBlock Grid.Row="1" Grid.Column="2" Margin="10,13,10,10" FontSize="13.333" Text="Device:"
VerticalAlignment="Top" Height="18" />
<Slider Grid.Row="1" Grid.Column="3" VerticalAlignment="Top"
Value="{Binding Path=LayerModel.Properties.FadeSpeed, Mode=TwoWay}" Margin="10,12,10,2"
Height="24" TickPlacement="BottomRight" TickFrequency="0.1" Minimum="0.1" Maximum="1" SmallChange="0.1"
IsSnapToTickEnabled="True" />
<ComboBox Grid.Row="1" Grid.Column="3" x:Name="Devices" Margin="10,10,10,0" VerticalAlignment="Top" />
<!-- Colors -->
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal" x:Name="ShowBrush">

View File

@ -2,8 +2,8 @@
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Caliburn.Micro;
using CSCore.CoreAudioAPI;
namespace Artemis.Profiles.Layers.Types.Audio
{
@ -14,12 +14,18 @@ namespace Artemis.Profiles.Layers.Types.Audio
public AudioPropertiesViewModel(LayerEditorViewModel editorVm) : base(editorVm)
{
LayerAnimations = new BindableCollection<ILayerAnimation>(editorVm.LayerAnimations);
Devices = new BindableCollection<string>();
SelectedLayerAnimation =
LayerAnimations.FirstOrDefault(l => l.Name == editorVm.ProposedLayer.LayerAnimation?.Name) ??
LayerAnimations.First(l => l.Name == "None");
SetupAudioSelection();
if (SelectedDevice == null)
SelectedDevice = Devices.First();
}
public BindableCollection<ILayerAnimation> LayerAnimations { get; set; }
public BindableCollection<string> Devices { get; set; }
public ILayerAnimation SelectedLayerAnimation
{
@ -32,6 +38,45 @@ namespace Artemis.Profiles.Layers.Types.Audio
}
}
public MmDeviceType DeviceType
{
get { return ((AudioPropertiesModel) LayerModel.Properties).DeviceType; }
set
{
if (value == ((AudioPropertiesModel) LayerModel.Properties).DeviceType) return;
((AudioPropertiesModel) LayerModel.Properties).DeviceType = value;
SetupAudioSelection();
SelectedDevice = Devices.First();
NotifyOfPropertyChange(() => DeviceType);
}
}
public string SelectedDevice
{
get { return ((AudioPropertiesModel) LayerModel.Properties).Device; }
set
{
if (value == ((AudioPropertiesModel) LayerModel.Properties).Device) return;
((AudioPropertiesModel) LayerModel.Properties).Device = value;
NotifyOfPropertyChange(() => SelectedDevice);
}
}
private void SetupAudioSelection()
{
var properties = (AudioPropertiesModel) LayerModel.Properties;
Devices.Clear();
Devices.Add("Default");
// Select the proper devices and make sure they are unique
Devices.AddRange(properties.DeviceType == MmDeviceType.Input
? MMDeviceEnumerator.EnumerateDevices(DataFlow.Capture, DeviceState.Active)
.Select(d => d.FriendlyName).GroupBy(d => d).Select(g => g.First())
: MMDeviceEnumerator.EnumerateDevices(DataFlow.Render, DeviceState.Active)
.Select(d => d.FriendlyName).GroupBy(d => d).Select(g => g.First()));
}
public override void ApplyProperties()
{
LayerModel.Properties.Brush = Brush;

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Artemis.Events;
@ -15,7 +13,6 @@ using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
using Artemis.Properties;
using Artemis.Utilities;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using CSCore.CoreAudioAPI;
namespace Artemis.Profiles.Layers.Types.Audio
@ -27,22 +24,21 @@ namespace Artemis.Profiles.Layers.Types.Audio
private int _lines;
private LineSpectrum _lineSpectrum;
private List<double> _lineValues;
private AudioPropertiesModel _properties;
private bool _subscribed;
public AudioType(AudioCaptureManager audioCaptureManager)
{
_audioCaptureManager = audioCaptureManager;
// TODO: Setup according to settings
_audioCapture = _audioCaptureManager.GetAudioCapture(MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Multimedia));
_audioCaptureManager.AudioDeviceChanged += OnAudioDeviceChanged;
}
private void OnAudioDeviceChanged(object sender, AudioDeviceChangedEventArgs e)
private void SubscribeToAudioChange()
{
// TODO: Check if layer must use default
// TODO: Check recording type
_audioCapture = _audioCaptureManager.GetAudioCapture(e.DefaultPlayback);
_lines = 0;
if (_subscribed)
return;
_audioCaptureManager.AudioDeviceChanged += OnAudioDeviceChanged;
_subscribed = true;
}
public string Name => "Keyboard - Audio visualization";
@ -73,7 +69,7 @@ namespace Artemis.Profiles.Layers.Types.Audio
var direction = ((AudioPropertiesModel) layerModel.Properties).Direction;
// Create a geometry that will be formed by all the bars
GeometryGroup barGeometry = new GeometryGroup();
var barGeometry = new GeometryGroup();
switch (direction)
{
@ -121,6 +117,25 @@ namespace Artemis.Profiles.Layers.Types.Audio
public void Update(LayerModel layerModel, ModuleDataModel dataModel, bool isPreview = false)
{
layerModel.ApplyProperties(true);
var newProperties = (AudioPropertiesModel) layerModel.Properties;
if (_properties == null)
_properties = newProperties;
SubscribeToAudioChange();
if (_audioCapture == null || newProperties.Device != _properties.Device ||
newProperties.DeviceType != _properties.DeviceType)
{
var device = GetMmDevice();
if (device != null)
_audioCapture = _audioCaptureManager.GetAudioCapture(device, newProperties.DeviceType);
}
_properties = newProperties;
if (_audioCapture == null)
return;
_audioCapture.Pulse();
var direction = ((AudioPropertiesModel) layerModel.Properties).Direction;
@ -142,6 +157,8 @@ namespace Artemis.Profiles.Layers.Types.Audio
{
_lines = currentLines;
_lineSpectrum = _audioCapture.GetLineSpectrum(_lines, ScalingStrategy.Decibel);
if (_lineSpectrum == null)
return;
}
var newLineValues = _lineSpectrum?.GetLineValues(currentHeight);
@ -183,8 +200,8 @@ namespace Artemis.Profiles.Layers.Types.Audio
// Otherwise draw the rectangle with its layer.AppliedProperties dimensions and brush
var rect = layerModel.Properties.Contain
? layerModel.LayerRect()
: new Rect(layerModel.Properties.X*4, layerModel.Properties.Y*4,
layerModel.Properties.Width*4, layerModel.Properties.Height*4);
: new Rect(layerModel.Properties.X * 4, layerModel.Properties.Y * 4,
layerModel.Properties.Width * 4, layerModel.Properties.Height * 4);
var clip = layerModel.LayerRect(DrawScale);
@ -196,5 +213,40 @@ namespace Artemis.Profiles.Layers.Types.Audio
c.DrawRectangle(brush, null, rect);
c.Pop();
}
private void OnAudioDeviceChanged(object sender, AudioDeviceChangedEventArgs e)
{
if (_properties == null || _properties.Device != "Default")
return;
if (_properties.DeviceType == MmDeviceType.Input)
{
if (e.DefaultRecording != null)
_audioCapture = _audioCaptureManager.GetAudioCapture(e.DefaultRecording, MmDeviceType.Input);
}
else
{
if (e.DefaultPlayback != null)
_audioCapture = _audioCaptureManager.GetAudioCapture(e.DefaultPlayback, MmDeviceType.Ouput);
}
_lines = 0;
}
private MMDevice GetMmDevice()
{
if (_properties == null)
return null;
if (_properties.DeviceType == MmDeviceType.Input)
return _properties.Device == "Default"
? MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia)
: MMDeviceEnumerator.EnumerateDevices(DataFlow.Capture)
.FirstOrDefault(d => d.FriendlyName == _properties.Device);
return _properties.Device == "Default"
? MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia)
: MMDeviceEnumerator.EnumerateDevices(DataFlow.Render)
.FirstOrDefault(d => d.FriendlyName == _properties.Device);
}
}
}