Implemented OSD
Implemented mute/unmute input and output actions Changed MockDevice to EmulatorDevice with UI for easier testing
This commit is contained in:
parent
ff1e1ca74c
commit
28c25c8b43
@ -16,8 +16,8 @@ Because of the second requirement, a simple media keys HID device does not suffi
|
||||
- by changing the Windows default output device
|
||||
- by running a VoiceMeeter macro
|
||||
2. Corresponding LEDs to indicate the currently active device
|
||||
3. OSD
|
||||
4. API / plugins to use extra knobs and buttons for other purposes
|
||||
3. ✔ OSD
|
||||
4. ✔ API / plugins to use extra knobs and buttons for other purposes
|
||||
|
||||
## Developing
|
||||
The hardware side uses an Arduino sketch to communicate the hardware state over the serial port.
|
||||
|
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.Base
|
||||
{
|
||||
public class BaseDeviceSettings
|
||||
{
|
||||
public Guid? DeviceId { get; set; }
|
||||
|
||||
// TODO more options, like positioning and style
|
||||
public bool OSD { get; set; } = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<UserControl x:Class="MassiveKnob.Plugin.CoreAudio.Base.BaseDeviceSettingsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:base="clr-namespace:MassiveKnob.Plugin.CoreAudio.Base"
|
||||
xmlns:coreAudio="clr-namespace:MassiveKnob.Plugin.CoreAudio"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="200" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance base:BaseDeviceSettingsViewModel}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Text="{x:Static coreAudio:Strings.SettingPlaybackDevice}" />
|
||||
<ComboBox Margin="0,4,0,0" ItemsSource="{Binding PlaybackDevices}" SelectedItem="{Binding SelectedDevice}" DisplayMemberPath="DisplayName" />
|
||||
|
||||
<CheckBox Margin="0,8,0,0" IsChecked="{Binding OSD}">
|
||||
<TextBlock Text="{x:Static coreAudio:Strings.SettingOSD}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</UserControl>
|
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.Base
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for BaseDeviceSettingsView.xaml
|
||||
/// </summary>
|
||||
public partial class BaseDeviceSettingsView : UserControl
|
||||
{
|
||||
public BaseDeviceSettingsView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -7,11 +7,22 @@ using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using AudioSwitcher.AudioApi;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.Settings
|
||||
namespace MassiveKnob.Plugin.CoreAudio.Base
|
||||
{
|
||||
public class BaseDeviceSettingsViewModel<T> : BaseDeviceSettingsViewModel where T : BaseDeviceSettings
|
||||
{
|
||||
protected new T Settings => (T)base.Settings;
|
||||
|
||||
public BaseDeviceSettingsViewModel(T settings) : base(settings)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class BaseDeviceSettingsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private readonly BaseDeviceSettings settings;
|
||||
protected readonly BaseDeviceSettings Settings;
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private IList<PlaybackDeviceViewModel> playbackDevices;
|
||||
@ -37,7 +48,21 @@ namespace MassiveKnob.Plugin.CoreAudio.Settings
|
||||
return;
|
||||
|
||||
selectedDevice = value;
|
||||
settings.DeviceId = selectedDevice?.Id;
|
||||
Settings.DeviceId = selectedDevice?.Id;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool OSD
|
||||
{
|
||||
get => Settings.OSD;
|
||||
set
|
||||
{
|
||||
if (value == Settings.OSD)
|
||||
return;
|
||||
|
||||
Settings.OSD = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
@ -46,7 +71,7 @@ namespace MassiveKnob.Plugin.CoreAudio.Settings
|
||||
|
||||
public BaseDeviceSettingsViewModel(BaseDeviceSettings settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
Settings = settings;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Windows.Controls;
|
||||
using AudioSwitcher.AudioApi;
|
||||
using MassiveKnob.Plugin.CoreAudio.OSD;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.GetMuted
|
||||
{
|
||||
public class DeviceGetMutedAction : IMassiveKnobAction
|
||||
{
|
||||
public Guid ActionId { get; } = new Guid("86646ca7-f472-4c5a-8d0f-7e5d2d162ab9");
|
||||
public MassiveKnobActionType ActionType { get; } = MassiveKnobActionType.OutputDigital;
|
||||
public string Name { get; } = Strings.GetMutedName;
|
||||
public string Description { get; } = Strings.GetMutedDescription;
|
||||
|
||||
|
||||
public IMassiveKnobActionInstance Create()
|
||||
{
|
||||
return new Instance();
|
||||
}
|
||||
|
||||
|
||||
private class Instance : IMassiveKnobActionInstance
|
||||
{
|
||||
private IMassiveKnobActionContext actionContext;
|
||||
private DeviceGetMutedActionSettings settings;
|
||||
private IDevice playbackDevice;
|
||||
private IDisposable deviceChanged;
|
||||
|
||||
|
||||
public void Initialize(IMassiveKnobActionContext context)
|
||||
{
|
||||
actionContext = context;
|
||||
settings = context.GetSettings<DeviceGetMutedActionSettings>();
|
||||
ApplySettings();
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
deviceChanged?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
private void ApplySettings()
|
||||
{
|
||||
if (playbackDevice != null && playbackDevice.Id == settings.DeviceId)
|
||||
return;
|
||||
|
||||
var coreAudioController = CoreAudioControllerInstance.Acquire();
|
||||
playbackDevice = settings.DeviceId.HasValue ? coreAudioController.GetDevice(settings.DeviceId.Value) : null;
|
||||
|
||||
deviceChanged?.Dispose();
|
||||
deviceChanged = playbackDevice?.MuteChanged.Subscribe(MuteChanged);
|
||||
}
|
||||
|
||||
|
||||
public UserControl CreateSettingsControl()
|
||||
{
|
||||
var viewModel = new DeviceGetMutedActionSettingsViewModel(settings);
|
||||
viewModel.PropertyChanged += (sender, args) =>
|
||||
{
|
||||
if (!viewModel.IsSettingsProperty(args.PropertyName))
|
||||
return;
|
||||
|
||||
actionContext.SetSettings(settings);
|
||||
ApplySettings();
|
||||
};
|
||||
|
||||
return new DeviceGetMutedActionSettingsView(viewModel);
|
||||
}
|
||||
|
||||
|
||||
private void MuteChanged(DeviceMuteChangedArgs args)
|
||||
{
|
||||
actionContext.SetDigitalOutput(settings.Inverted ? !args.IsMuted : args.IsMuted);
|
||||
|
||||
if (settings.OSD)
|
||||
OSDManager.Show(args.Device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using MassiveKnob.Plugin.CoreAudio.Base;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.GetMuted
|
||||
{
|
||||
public class DeviceGetMutedActionSettings : BaseDeviceSettings
|
||||
{
|
||||
public bool Inverted { get; set; }
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<UserControl x:Class="MassiveKnob.Plugin.CoreAudio.Settings.DeviceVolumeActionSettingsView"
|
||||
<UserControl x:Class="MassiveKnob.Plugin.CoreAudio.GetMuted.DeviceGetMutedActionSettingsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:settings="clr-namespace:MassiveKnob.Plugin.CoreAudio.Settings"
|
||||
xmlns:getMuted="clr-namespace:MassiveKnob.Plugin.CoreAudio.GetMuted"
|
||||
xmlns:base="clr-namespace:MassiveKnob.Plugin.CoreAudio.Base"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="200" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance settings:DeviceVolumeActionSettingsViewModel}">
|
||||
d:DataContext="{d:DesignInstance getMuted:DeviceGetMutedActionSettingsViewModel}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock>Playback device</TextBlock>
|
||||
<ComboBox ItemsSource="{Binding PlaybackDevices}" SelectedItem="{Binding SelectedDevice}" DisplayMemberPath="DisplayName" />
|
||||
<base:BaseDeviceSettingsView />
|
||||
</StackPanel>
|
||||
</UserControl>
|
@ -0,0 +1,14 @@
|
||||
namespace MassiveKnob.Plugin.CoreAudio.GetMuted
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DeviceGetMutedActionSettingsView.xaml
|
||||
/// </summary>
|
||||
public partial class DeviceGetMutedActionSettingsView
|
||||
{
|
||||
public DeviceGetMutedActionSettingsView(DeviceGetMutedActionSettingsViewModel viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
using MassiveKnob.Plugin.CoreAudio.Base;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.GetMuted
|
||||
{
|
||||
public class DeviceGetMutedActionSettingsViewModel : BaseDeviceSettingsViewModel<DeviceGetMutedActionSettings>
|
||||
{
|
||||
// ReSharper disable UnusedMember.Global - used by WPF Binding
|
||||
public bool Inverted
|
||||
{
|
||||
get => Settings.Inverted;
|
||||
set
|
||||
{
|
||||
if (value == Settings.Inverted)
|
||||
return;
|
||||
|
||||
Settings.Inverted = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
// ReSharper restore UnusedMember.Global
|
||||
|
||||
|
||||
// ReSharper disable once SuggestBaseTypeForParameter - by design
|
||||
public DeviceGetMutedActionSettingsViewModel(DeviceGetMutedActionSettings settings) : base(settings)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Windows.Controls;
|
||||
using AudioSwitcher.AudioApi;
|
||||
using MassiveKnob.Plugin.CoreAudio.OSD;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.GetVolume
|
||||
{
|
||||
public class DeviceGetVolumeAction : IMassiveKnobAction
|
||||
{
|
||||
public Guid ActionId { get; } = new Guid("6ebf91af-8240-4a75-9729-c6a1eb60dcba");
|
||||
public MassiveKnobActionType ActionType { get; } = MassiveKnobActionType.OutputAnalog;
|
||||
public string Name { get; } = Strings.GetVolumeName;
|
||||
public string Description { get; } = Strings.GetVolumeDescription;
|
||||
|
||||
|
||||
public IMassiveKnobActionInstance Create()
|
||||
{
|
||||
return new Instance();
|
||||
}
|
||||
|
||||
|
||||
private class Instance : IMassiveKnobActionInstance
|
||||
{
|
||||
private IMassiveKnobActionContext actionContext;
|
||||
private DeviceGetVolumeActionSettings settings;
|
||||
private IDevice playbackDevice;
|
||||
private IDisposable deviceChanged;
|
||||
|
||||
|
||||
public void Initialize(IMassiveKnobActionContext context)
|
||||
{
|
||||
actionContext = context;
|
||||
settings = context.GetSettings<DeviceGetVolumeActionSettings>();
|
||||
ApplySettings();
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
deviceChanged?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
private void ApplySettings()
|
||||
{
|
||||
if (playbackDevice != null && playbackDevice.Id == settings.DeviceId)
|
||||
return;
|
||||
|
||||
var coreAudioController = CoreAudioControllerInstance.Acquire();
|
||||
playbackDevice = settings.DeviceId.HasValue ? coreAudioController.GetDevice(settings.DeviceId.Value) : null;
|
||||
|
||||
deviceChanged?.Dispose();
|
||||
deviceChanged = playbackDevice?.VolumeChanged.Subscribe(VolumeChanged);
|
||||
}
|
||||
|
||||
|
||||
public UserControl CreateSettingsControl()
|
||||
{
|
||||
var viewModel = new DeviceGetVolumeActionSettingsViewModel(settings);
|
||||
viewModel.PropertyChanged += (sender, args) =>
|
||||
{
|
||||
if (!viewModel.IsSettingsProperty(args.PropertyName))
|
||||
return;
|
||||
|
||||
actionContext.SetSettings(settings);
|
||||
ApplySettings();
|
||||
};
|
||||
|
||||
return new DeviceGetVolumeActionSettingsView(viewModel);
|
||||
}
|
||||
|
||||
|
||||
private void VolumeChanged(DeviceVolumeChangedArgs args)
|
||||
{
|
||||
actionContext.SetAnalogOutput((byte)args.Volume);
|
||||
|
||||
if (settings.OSD)
|
||||
OSDManager.Show(args.Device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
using MassiveKnob.Plugin.CoreAudio.Base;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.GetVolume
|
||||
{
|
||||
public class DeviceGetVolumeActionSettings : BaseDeviceSettings
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
<UserControl x:Class="MassiveKnob.Plugin.CoreAudio.GetVolume.DeviceGetVolumeActionSettingsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:getVolume="clr-namespace:MassiveKnob.Plugin.CoreAudio.GetVolume"
|
||||
xmlns:base="clr-namespace:MassiveKnob.Plugin.CoreAudio.Base"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="200" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance getVolume:DeviceGetVolumeActionSettingsViewModel}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<base:BaseDeviceSettingsView />
|
||||
</StackPanel>
|
||||
</UserControl>
|
@ -0,0 +1,16 @@
|
||||
using MassiveKnob.Plugin.CoreAudio.SetMuted;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.GetVolume
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DeviceGetVolumeActionSettingsView.xaml
|
||||
/// </summary>
|
||||
public partial class DeviceGetVolumeActionSettingsView
|
||||
{
|
||||
public DeviceGetVolumeActionSettingsView(DeviceGetVolumeActionSettingsViewModel viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using MassiveKnob.Plugin.CoreAudio.Base;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.GetVolume
|
||||
{
|
||||
public class DeviceGetVolumeActionSettingsViewModel : BaseDeviceSettingsViewModel<DeviceGetVolumeActionSettings>
|
||||
{
|
||||
// ReSharper disable once SuggestBaseTypeForParameter - by design
|
||||
public DeviceGetVolumeActionSettingsViewModel(DeviceGetVolumeActionSettings settings) : base(settings)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -35,6 +35,7 @@
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Xaml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
@ -45,17 +46,43 @@
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Actions\DeviceVolumeAction.cs" />
|
||||
<Compile Include="Base\BaseDeviceSettingsView.xaml.cs">
|
||||
<DependentUpon>BaseDeviceSettingsView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="GetMuted\DeviceGetMutedAction.cs" />
|
||||
<Compile Include="GetMuted\DeviceGetMutedActionSettings.cs" />
|
||||
<Compile Include="GetMuted\DeviceGetMutedActionSettingsView.xaml.cs">
|
||||
<DependentUpon>DeviceGetMutedActionSettingsView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="GetMuted\DeviceGetMutedActionSettingsViewModel.cs" />
|
||||
<Compile Include="GetVolume\DeviceGetVolumeAction.cs" />
|
||||
<Compile Include="OSD\OSDWindow.xaml.cs">
|
||||
<DependentUpon>OSDWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="OSD\OSDManager.cs" />
|
||||
<Compile Include="OSD\OSDWindowViewModel.cs" />
|
||||
<Compile Include="SetMuted\DeviceSetMutedAction.cs" />
|
||||
<Compile Include="SetMuted\DeviceSetMutedActionSettings.cs" />
|
||||
<Compile Include="SetMuted\DeviceSetMutedActionSettingsView.xaml.cs">
|
||||
<DependentUpon>DeviceSetMutedActionSettingsView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SetMuted\DeviceSetMutedActionSettingsViewModel.cs" />
|
||||
<Compile Include="SetVolume\DeviceSetVolumeAction.cs" />
|
||||
<Compile Include="CoreAudioControllerInstance.cs" />
|
||||
<Compile Include="GetVolume\DeviceGetVolumeActionSettingsViewModel.cs" />
|
||||
<Compile Include="GetVolume\DeviceGetVolumeActionSettingsView.xaml.cs">
|
||||
<DependentUpon>DeviceGetVolumeActionSettingsView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MassiveKnobCoreAudioPlugin.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Settings\DeviceVolumeActionSettings.cs" />
|
||||
<Compile Include="Settings\DeviceVolumeActionSettingsView.xaml.cs">
|
||||
<DependentUpon>DeviceVolumeActionSettingsView.xaml</DependentUpon>
|
||||
<Compile Include="GetVolume\DeviceGetVolumeActionSettings.cs" />
|
||||
<Compile Include="SetVolume\DeviceSetVolumeActionSettings.cs" />
|
||||
<Compile Include="SetVolume\DeviceSetVolumeActionSettingsView.xaml.cs">
|
||||
<DependentUpon>DeviceSetVolumeActionSettingsView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Settings\BaseDeviceSettings.cs" />
|
||||
<Compile Include="Settings\BaseDeviceSettingsViewModel.cs" />
|
||||
<Compile Include="Settings\DeviceVolumeActionSettingsViewModel.cs" />
|
||||
<Compile Include="Base\BaseDeviceSettings.cs" />
|
||||
<Compile Include="Base\BaseDeviceSettingsViewModel.cs" />
|
||||
<Compile Include="SetVolume\DeviceSetVolumeActionSettingsViewModel.cs" />
|
||||
<Compile Include="Strings.Designer.cs">
|
||||
<DependentUpon>Strings.resx</DependentUpon>
|
||||
<AutoGen>True</AutoGen>
|
||||
@ -72,21 +99,49 @@
|
||||
<PackageReference Include="AudioSwitcher.AudioApi.CoreAudio">
|
||||
<Version>4.0.0-alpha5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Reactive">
|
||||
<Version>5.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Threading.Tasks.Extensions">
|
||||
<Version>4.5.4</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Strings.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="Settings\DeviceVolumeActionSettingsView.xaml">
|
||||
<Page Include="Base\BaseDeviceSettingsView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="GetMuted\DeviceGetMutedActionSettingsView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="GetVolume\DeviceGetVolumeActionSettingsView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="OSD\OSDWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="OSD\SpeakerIcon.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="SetMuted\DeviceSetMutedActionSettingsView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="SetVolume\DeviceSetVolumeActionSettingsView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MassiveKnob.Plugin.CoreAudio.Actions;
|
||||
using MassiveKnob.Plugin.CoreAudio.GetMuted;
|
||||
using MassiveKnob.Plugin.CoreAudio.GetVolume;
|
||||
using MassiveKnob.Plugin.CoreAudio.SetMuted;
|
||||
using MassiveKnob.Plugin.CoreAudio.SetVolume;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio
|
||||
{
|
||||
@ -9,14 +12,17 @@ namespace MassiveKnob.Plugin.CoreAudio
|
||||
public class MassiveKnobCoreAudioPlugin : IMassiveKnobActionPlugin
|
||||
{
|
||||
public Guid PluginId { get; } = new Guid("eaa5d3f8-8f9b-4a4b-8e29-827228d23e95");
|
||||
public string Name { get; } = "Windows Core Audio";
|
||||
public string Description { get; } = "Included with Massive Knob by default. Provides volume control per device and default device switching.";
|
||||
public string Name { get; } = Strings.PluginName;
|
||||
public string Description { get; } = Strings.PluginDescription;
|
||||
public string Author { get; } = "Mark van Renswoude <mark@x2software.net>";
|
||||
public string Url { get; } = "https://www.github.com/MvRens/MassiveKnob/";
|
||||
|
||||
public IEnumerable<IMassiveKnobAction> Actions { get; } = new IMassiveKnobAction[]
|
||||
{
|
||||
new DeviceVolumeAction()
|
||||
new DeviceSetVolumeAction(),
|
||||
new DeviceGetVolumeAction(),
|
||||
new DeviceSetMutedAction(),
|
||||
new DeviceGetMutedAction()
|
||||
};
|
||||
|
||||
|
||||
|
50
Windows/MassiveKnob.Plugin.CoreAudio/OSD/OSDManager.cs
Normal file
50
Windows/MassiveKnob.Plugin.CoreAudio/OSD/OSDManager.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using AudioSwitcher.AudioApi;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.OSD
|
||||
{
|
||||
public static class OSDManager
|
||||
{
|
||||
private const int OSDTimeout = 2500;
|
||||
|
||||
private static OSDWindowViewModel windowViewModel;
|
||||
private static Window window;
|
||||
private static Timer hideTimer;
|
||||
|
||||
public static void Show(IDevice device)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (window == null)
|
||||
{
|
||||
windowViewModel = new OSDWindowViewModel();
|
||||
window = new OSDWindow(windowViewModel);
|
||||
|
||||
hideTimer = new Timer(state =>
|
||||
{
|
||||
Hide();
|
||||
}, null, OSDTimeout, Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
hideTimer.Change(OSDTimeout, Timeout.Infinite);
|
||||
|
||||
windowViewModel.SetDevice(device);
|
||||
window.Show();
|
||||
});
|
||||
}
|
||||
|
||||
private static void Hide()
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
window?.Close();
|
||||
window = null;
|
||||
windowViewModel = null;
|
||||
|
||||
hideTimer?.Dispose();
|
||||
hideTimer = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
55
Windows/MassiveKnob.Plugin.CoreAudio/OSD/OSDWindow.xaml
Normal file
55
Windows/MassiveKnob.Plugin.CoreAudio/OSD/OSDWindow.xaml
Normal file
@ -0,0 +1,55 @@
|
||||
<Window x:Class="MassiveKnob.Plugin.CoreAudio.OSD.OSDWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:osd="clr-namespace:MassiveKnob.Plugin.CoreAudio.OSD"
|
||||
mc:Ignorable="d"
|
||||
Title="Massive Knob OSD" Height="60" Width="360"
|
||||
WindowStartupLocation="Manual" WindowStyle="None" AllowsTransparency="True" ShowInTaskbar="False" Topmost="True"
|
||||
Loaded="OSDWindow_OnLoaded" Closing="OSDWindow_OnClosing"
|
||||
d:DataContext="{d:DesignInstance osd:OSDWindowViewModel}">
|
||||
<Window.Triggers>
|
||||
<EventTrigger RoutedEvent="Window.Loaded">
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.250" FillBehavior="HoldEnd" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</EventTrigger>
|
||||
</Window.Triggers>
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="SpeakerIcon.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<!-- ReSharper disable once Xaml.RedundantResource - used in runtime -->
|
||||
<Storyboard x:Key="CloseStoryboard" Completed="CloseStoryboard_Completed">
|
||||
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.250" FillBehavior="HoldEnd" />
|
||||
</Storyboard>
|
||||
|
||||
<Style TargetType="DockPanel" x:Key="OSDWindow">
|
||||
<Setter Property="Background" Value="#2d2d30" />
|
||||
</Style>
|
||||
<Style TargetType="TextBlock" x:Key="DeviceName">
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="Margin" Value="8,4,8,4" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="TextTrimming" Value="CharacterEllipsis"></Setter>
|
||||
</Style>
|
||||
<Style x:Key="SpeakerIconStyle">
|
||||
<Setter Property="Control.Margin" Value="8,4,8,4" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<DockPanel Style="{StaticResource OSDWindow}">
|
||||
<TextBlock DockPanel.Dock="Top" Text="{Binding DeviceName}" Style="{StaticResource DeviceName}"></TextBlock>
|
||||
<ContentControl DockPanel.Dock="Left" Content="{StaticResource SpeakerIcon}" Style="{StaticResource SpeakerIconStyle}" />
|
||||
<Canvas Width="300" Height="20" Margin="8,0,8,0">
|
||||
<Line X1="0" X2="300" Y1="10" Y2="10" Stroke="#80FFFFFF" StrokeThickness="2" />
|
||||
<Line X1="{Binding VolumeIndicatorLeft}" X2="{Binding VolumeIndicatorLeft}" Y1="0" Y2="20" Stroke="White" StrokeThickness="2" />
|
||||
</Canvas>
|
||||
</DockPanel>
|
||||
</Window>
|
49
Windows/MassiveKnob.Plugin.CoreAudio/OSD/OSDWindow.xaml.cs
Normal file
49
Windows/MassiveKnob.Plugin.CoreAudio/OSD/OSDWindow.xaml.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.OSD
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for OSDWindow.xaml
|
||||
/// </summary>
|
||||
public partial class OSDWindow
|
||||
{
|
||||
private bool closeStoryBoardCompleted;
|
||||
|
||||
|
||||
public OSDWindow(OSDWindowViewModel viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
|
||||
private void OSDWindow_OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var desktopArea = Screen.PrimaryScreen.WorkingArea;
|
||||
|
||||
Left = (desktopArea.Width - Width) / 2;
|
||||
Top = desktopArea.Bottom - Height - 25;
|
||||
}
|
||||
|
||||
|
||||
private void OSDWindow_OnClosing(object sender, CancelEventArgs e)
|
||||
{
|
||||
if (closeStoryBoardCompleted)
|
||||
return;
|
||||
|
||||
((Storyboard)FindResource("CloseStoryboard")).Begin(this);
|
||||
e.Cancel = true;
|
||||
}
|
||||
|
||||
|
||||
private void CloseStoryboard_Completed(object sender, EventArgs e)
|
||||
{
|
||||
closeStoryBoardCompleted = true;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.RightsManagement;
|
||||
using System.Windows;
|
||||
using AudioSwitcher.AudioApi;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.OSD
|
||||
{
|
||||
public class OSDWindowViewModel : INotifyPropertyChanged
|
||||
{
|
||||
// ReSharper disable UnusedMember.Global - used by WPF Binding
|
||||
private string deviceName;
|
||||
public string DeviceName
|
||||
{
|
||||
get => deviceName;
|
||||
set
|
||||
{
|
||||
if (value == deviceName)
|
||||
return;
|
||||
|
||||
deviceName = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private int volume;
|
||||
public int Volume
|
||||
{
|
||||
get => volume;
|
||||
set
|
||||
{
|
||||
if (value == volume)
|
||||
return;
|
||||
|
||||
volume = value;
|
||||
OnPropertyChanged();
|
||||
OnDependantPropertyChanged(nameof(VolumeLowVisibility));
|
||||
OnDependantPropertyChanged(nameof(VolumeMediumVisibility));
|
||||
OnDependantPropertyChanged(nameof(VolumeHighVisibility));
|
||||
OnDependantPropertyChanged(nameof(VolumeIndicatorLeft));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool isMuted;
|
||||
public bool IsMuted
|
||||
{
|
||||
get => isMuted;
|
||||
set
|
||||
{
|
||||
if (value == isMuted)
|
||||
return;
|
||||
|
||||
isMuted = value;
|
||||
OnPropertyChanged();
|
||||
OnDependantPropertyChanged(nameof(IsMutedVisibility));
|
||||
OnDependantPropertyChanged(nameof(IsNotMutedVisibility));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Visibility IsMutedVisibility => IsMuted ? Visibility.Visible : Visibility.Collapsed;
|
||||
public Visibility IsNotMutedVisibility => IsMuted ? Visibility.Collapsed : Visibility.Visible;
|
||||
public Visibility VolumeLowVisibility => Volume > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
public Visibility VolumeMediumVisibility => Volume > 33 ? Visibility.Visible : Visibility.Collapsed;
|
||||
public Visibility VolumeHighVisibility => Volume > 66 ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
public int VolumeIndicatorLeft => Volume * 3;
|
||||
// ReSharper enable UnusedMember.Global
|
||||
|
||||
|
||||
public void SetDevice(IDevice device)
|
||||
{
|
||||
DeviceName = device.FullName;
|
||||
Volume = (int)device.Volume;
|
||||
IsMuted = device.IsMuted;
|
||||
}
|
||||
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
protected virtual void OnDependantPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
52
Windows/MassiveKnob.Plugin.CoreAudio/OSD/SpeakerIcon.xaml
Normal file
52
Windows/MassiveKnob.Plugin.CoreAudio/OSD/SpeakerIcon.xaml
Normal file
@ -0,0 +1,52 @@
|
||||
<!--
|
||||
AI saved to SVG, converted to XAML using Inkscape, then modified manually to provide
|
||||
interactivity. Be aware of this when overwriting this file.
|
||||
-->
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<Viewbox
|
||||
xmlns:osd="clr-namespace:MassiveKnob.Plugin.CoreAudio.OSD"
|
||||
Stretch="Uniform"
|
||||
x:Key="SpeakerIcon"
|
||||
d:DataContext="{d:DesignInstance osd:OSDWindowViewModel}">
|
||||
<Canvas Width="256" Height="256">
|
||||
<Polygon Visibility="{Binding IsNotMutedVisibility}" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Points=" 133.5,215.101 61.75,168 8.75,168 8.75,88 61.75,88 133.5,40.899 " Name="Speaker_1_" FillRule="NonZero" StrokeThickness="12" Stroke="#FFFFFFFF" StrokeMiterLimit="10" StrokeLineJoin="Round"/>
|
||||
<Path Visibility="{Binding VolumeLowVisibility}" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="Low" StrokeThickness="12" Stroke="#FFFFFFFF" StrokeMiterLimit="10" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round">
|
||||
<Path.Data>
|
||||
<PathGeometry Figures=" M166.806 86c0 0 12.528 15.833 12.528 40.167s-12.528 43.823-12.528 43.823" FillRule="NonZero"/>
|
||||
</Path.Data>
|
||||
</Path>
|
||||
<Path Visibility="{Binding VolumeMediumVisibility}" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="Medium" StrokeThickness="12" Stroke="#FFFFFFFF" StrokeMiterLimit="10" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round">
|
||||
<Path.Data>
|
||||
<PathGeometry Figures=" M188.479 57c0 0 21.183 26.769 21.183 67.91c0 41.141-21.183 74.089-21.183 74.089" FillRule="NonZero"/>
|
||||
</Path.Data>
|
||||
</Path>
|
||||
<Path Visibility="{Binding VolumeHighVisibility}" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="High" StrokeThickness="12" Stroke="#FFFFFFFF" StrokeMiterLimit="10" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round">
|
||||
<Path.Data>
|
||||
<PathGeometry Figures=" M216.737 35.517c0 0 27.944 35.316 27.944 89.593s-27.944 97.75-27.944 97.75" FillRule="NonZero"/>
|
||||
</Path.Data>
|
||||
</Path>
|
||||
|
||||
<Path Visibility="{Binding IsMutedVisibility}" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path4561" Fill="#FFFFFFFF">
|
||||
<Path.Data>
|
||||
<PathGeometry Figures="M160.503 221.101c-1.717 0-3.421-0.732-4.608-2.153L10.395 44.746c-2.125-2.543-1.785-6.327 0.759-8.451 c2.544-2.125 6.328-1.784 8.451 0.759l145.5 174.201c2.124 2.544 1.784 6.327-0.759 8.452 C163.224 220.644 161.859 221.101 160.503 221.101z" FillRule="NonZero"/>
|
||||
</Path.Data>
|
||||
</Path>
|
||||
<Path Visibility="{Binding IsMutedVisibility}" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path4563" Fill="#FFFFFFFF">
|
||||
<Path.Data>
|
||||
<PathGeometry Figures="M127.5 203.984l-62.458-41C64.064 162.342 62.92 162 61.75 162h-47V94h28.967L33.694 82H8.75c-3.313 0-6 2.687-6 6v80 c0 3.313 2.687 6 6 6h51.207l70.25 46.116c0.997 0.654 2.144 0.984 3.293 0.984c0.979 0 1.958-0.238 2.85-0.72 c1.94-1.048 3.15-3.075 3.15-5.28v-6.423l-12-14.367V203.984z" FillRule="NonZero"/>
|
||||
</Path.Data>
|
||||
</Path>
|
||||
<Path Visibility="{Binding IsMutedVisibility}" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path4565" Fill="#FFFFFFFF">
|
||||
<Path.Data>
|
||||
<PathGeometry Figures="M127.5 52.016v104.856l12 14.367V40.899c0-2.205-1.21-4.232-3.15-5.28c-1.939-1.047-4.299-0.947-6.143 0.264L63.19 79.877 l7.744 9.271L127.5 52.016z" FillRule="NonZero"/>
|
||||
</Path.Data>
|
||||
</Path>
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</ResourceDictionary>
|
||||
|
48
Windows/MassiveKnob.Plugin.CoreAudio/Resources/Muted.svg
Normal file
48
Windows/MassiveKnob.Plugin.CoreAudio/Resources/Muted.svg
Normal file
@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="256px"
|
||||
height="256px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve">
|
||||
<g id="Speaker" display="none">
|
||||
|
||||
<polygon id="Speaker_1_" display="inline" fill="none" stroke="#000000" stroke-width="12" stroke-linejoin="round" stroke-miterlimit="10" points="
|
||||
133.5,215.101 61.75,168 8.75,168 8.75,88 61.75,88 133.5,40.899 "/>
|
||||
|
||||
<path id="Low" display="inline" fill="none" stroke="#000000" stroke-width="12" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
|
||||
M166.806,86c0,0,12.528,15.833,12.528,40.167s-12.528,43.823-12.528,43.823"/>
|
||||
|
||||
<path id="Medium" display="inline" fill="none" stroke="#000000" stroke-width="12" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
|
||||
M188.479,57c0,0,21.183,26.769,21.183,67.91c0,41.141-21.183,74.089-21.183,74.089"/>
|
||||
|
||||
<path id="High" display="inline" fill="none" stroke="#000000" stroke-width="12" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
|
||||
M216.737,35.517c0,0,27.944,35.316,27.944,89.593s-27.944,97.75-27.944,97.75"/>
|
||||
</g>
|
||||
<g id="Speaker_outline" display="none">
|
||||
<path display="inline" d="M133.5,221.101c-1.149,0-2.296-0.33-3.293-0.984L59.957,174H8.75c-3.313,0-6-2.687-6-6V88
|
||||
c0-3.313,2.687-6,6-6h51.207l70.25-46.116c1.844-1.211,4.203-1.312,6.143-0.264c1.94,1.047,3.15,3.075,3.15,5.28v174.201
|
||||
c0,2.205-1.21,4.232-3.15,5.28C135.458,220.862,134.479,221.101,133.5,221.101z M14.75,162h47c1.17,0,2.314,0.342,3.292,0.984
|
||||
l62.458,41V52.016l-62.458,41C64.064,93.658,62.92,94,61.75,94h-47V162z"/>
|
||||
<path display="inline" d="M166.8,175.99c-1.11,0-2.234-0.309-3.238-0.954c-2.788-1.792-3.595-5.504-1.803-8.291
|
||||
c0.109-0.173,11.575-18.406,11.575-40.579c0-21.992-11.121-36.301-11.233-36.443c-2.057-2.599-1.616-6.372,0.982-8.428
|
||||
c2.598-2.057,6.371-1.617,8.428,0.982c0.564,0.713,13.823,17.77,13.823,43.89c0,25.799-12.931,46.21-13.481,47.067
|
||||
C170.706,175.018,168.773,175.99,166.8,175.99z"/>
|
||||
<path display="inline" d="M188.473,205c-1.111,0-2.234-0.309-3.239-0.954c-2.787-1.792-3.594-5.504-1.802-8.292
|
||||
c0.199-0.311,20.229-32.06,20.229-70.844c0-38.607-19.688-63.935-19.888-64.187c-2.057-2.599-1.616-6.372,0.981-8.428
|
||||
c2.602-2.057,6.373-1.616,8.429,0.982c0.918,1.16,22.478,28.897,22.478,71.633c0,42.416-21.231,75.928-22.136,77.334
|
||||
C192.379,204.027,190.446,205,188.473,205z"/>
|
||||
<path display="inline" d="M216.731,228.861c-1.11,0-2.234-0.309-3.238-0.954c-2.788-1.791-3.595-5.504-1.803-8.291
|
||||
c0.267-0.418,26.991-42.736,26.991-94.506c0-51.593-26.383-85.533-26.649-85.87c-2.057-2.599-1.616-6.372,0.982-8.428
|
||||
c2.599-2.057,6.371-1.616,8.428,0.982c1.194,1.509,29.239,37.593,29.239,93.316c0,55.402-27.717,99.16-28.897,100.995
|
||||
C220.638,227.889,218.705,228.861,216.731,228.861z"/>
|
||||
</g>
|
||||
<g id="Muted">
|
||||
<path d="M160.503,221.101c-1.717,0-3.421-0.732-4.608-2.153L10.395,44.746c-2.125-2.543-1.785-6.327,0.759-8.451
|
||||
c2.544-2.125,6.328-1.784,8.451,0.759l145.5,174.201c2.124,2.544,1.784,6.327-0.759,8.452
|
||||
C163.224,220.644,161.859,221.101,160.503,221.101z"/>
|
||||
<path d="M127.5,203.984l-62.458-41C64.064,162.342,62.92,162,61.75,162h-47V94h28.967L33.694,82H8.75c-3.313,0-6,2.687-6,6v80
|
||||
c0,3.313,2.687,6,6,6h51.207l70.25,46.116c0.997,0.654,2.144,0.984,3.293,0.984c0.979,0,1.958-0.238,2.85-0.72
|
||||
c1.94-1.048,3.15-3.075,3.15-5.28v-6.423l-12-14.367V203.984z"/>
|
||||
<path d="M127.5,52.016v104.856l12,14.367V40.899c0-2.205-1.21-4.232-3.15-5.28c-1.939-1.047-4.299-0.947-6.143,0.264L63.19,79.877
|
||||
l7.744,9.271L127.5,52.016z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
441
Windows/MassiveKnob.Plugin.CoreAudio/Resources/OSDIcon.ai
Normal file
441
Windows/MassiveKnob.Plugin.CoreAudio/Resources/OSDIcon.ai
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using AudioSwitcher.AudioApi;
|
||||
using MassiveKnob.Plugin.CoreAudio.OSD;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.SetMuted
|
||||
{
|
||||
public class DeviceSetMutedAction : IMassiveKnobAction
|
||||
{
|
||||
public Guid ActionId { get; } = new Guid("032eb405-a1df-4178-b2d5-6cf556305a8c");
|
||||
public MassiveKnobActionType ActionType { get; } = MassiveKnobActionType.InputDigital;
|
||||
public string Name { get; } = Strings.SetMutedName;
|
||||
public string Description { get; } = Strings.SetMutedDescription;
|
||||
|
||||
|
||||
public IMassiveKnobActionInstance Create()
|
||||
{
|
||||
return new Instance();
|
||||
}
|
||||
|
||||
|
||||
private class Instance : IMassiveKnobDigitalAction
|
||||
{
|
||||
private IMassiveKnobActionContext actionContext;
|
||||
private DeviceSetMutedActionSettings settings;
|
||||
private IDevice playbackDevice;
|
||||
|
||||
|
||||
public void Initialize(IMassiveKnobActionContext context)
|
||||
{
|
||||
actionContext = context;
|
||||
settings = context.GetSettings<DeviceSetMutedActionSettings>();
|
||||
ApplySettings();
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
private void ApplySettings()
|
||||
{
|
||||
var coreAudioController = CoreAudioControllerInstance.Acquire();
|
||||
playbackDevice = settings.DeviceId.HasValue ? coreAudioController.GetDevice(settings.DeviceId.Value) : null;
|
||||
}
|
||||
|
||||
|
||||
public UserControl CreateSettingsControl()
|
||||
{
|
||||
var viewModel = new DeviceSetMutedActionSettingsViewModel(settings);
|
||||
viewModel.PropertyChanged += (sender, args) =>
|
||||
{
|
||||
if (!viewModel.IsSettingsProperty(args.PropertyName))
|
||||
return;
|
||||
|
||||
actionContext.SetSettings(settings);
|
||||
ApplySettings();
|
||||
};
|
||||
|
||||
return new DeviceSetMutedActionSettingsView(viewModel);
|
||||
}
|
||||
|
||||
|
||||
public async ValueTask DigitalChanged(bool on)
|
||||
{
|
||||
if (playbackDevice == null)
|
||||
return;
|
||||
|
||||
if (settings.Toggle)
|
||||
{
|
||||
if (!on)
|
||||
return;
|
||||
|
||||
await playbackDevice.SetMuteAsync(!playbackDevice.IsMuted);
|
||||
}
|
||||
else
|
||||
await playbackDevice.SetMuteAsync(settings.SetInverted ? !on : on);
|
||||
|
||||
|
||||
if (settings.OSD)
|
||||
OSDManager.Show(playbackDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using MassiveKnob.Plugin.CoreAudio.Base;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.SetMuted
|
||||
{
|
||||
public class DeviceSetMutedActionSettings : BaseDeviceSettings
|
||||
{
|
||||
public bool Toggle { get; set; }
|
||||
public bool SetInverted { get; set;}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
<UserControl x:Class="MassiveKnob.Plugin.CoreAudio.SetMuted.DeviceSetMutedActionSettingsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:setMuted="clr-namespace:MassiveKnob.Plugin.CoreAudio.SetMuted"
|
||||
xmlns:base="clr-namespace:MassiveKnob.Plugin.CoreAudio.Base"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="200" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance setMuted:DeviceSetMutedActionSettingsViewModel}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<base:BaseDeviceSettingsView />
|
||||
</StackPanel>
|
||||
</UserControl>
|
@ -0,0 +1,14 @@
|
||||
namespace MassiveKnob.Plugin.CoreAudio.SetMuted
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DeviceSetMutedActionSettingsView.xaml
|
||||
/// </summary>
|
||||
public partial class DeviceSetMutedActionSettingsView
|
||||
{
|
||||
public DeviceSetMutedActionSettingsView(DeviceSetMutedActionSettingsViewModel viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
using MassiveKnob.Plugin.CoreAudio.Base;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.SetMuted
|
||||
{
|
||||
public class DeviceSetMutedActionSettingsViewModel : BaseDeviceSettingsViewModel<DeviceSetMutedActionSettings>
|
||||
{
|
||||
// ReSharper disable UnusedMember.Global - used by WPF Binding
|
||||
public bool Toggle
|
||||
{
|
||||
get => Settings.Toggle;
|
||||
set
|
||||
{
|
||||
if (value == Settings.Toggle)
|
||||
return;
|
||||
|
||||
Settings.Toggle = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool SetInverted
|
||||
{
|
||||
get => Settings.SetInverted;
|
||||
set
|
||||
{
|
||||
if (value == Settings.SetInverted)
|
||||
return;
|
||||
|
||||
Settings.SetInverted = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
// ReSharper restore UnusedMember.Global
|
||||
|
||||
|
||||
// ReSharper disable once SuggestBaseTypeForParameter - by design
|
||||
public DeviceSetMutedActionSettingsViewModel(DeviceSetMutedActionSettings settings) : base(settings)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -2,16 +2,16 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using AudioSwitcher.AudioApi;
|
||||
using MassiveKnob.Plugin.CoreAudio.Settings;
|
||||
using MassiveKnob.Plugin.CoreAudio.OSD;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.Actions
|
||||
namespace MassiveKnob.Plugin.CoreAudio.SetVolume
|
||||
{
|
||||
public class DeviceVolumeAction : IMassiveKnobAction
|
||||
public class DeviceSetVolumeAction : IMassiveKnobAction
|
||||
{
|
||||
public Guid ActionId { get; } = new Guid("aabd329c-8be5-4d1e-90ab-5114143b21dd");
|
||||
public MassiveKnobActionType ActionType { get; } = MassiveKnobActionType.InputAnalog;
|
||||
public string Name { get; } = "Set volume";
|
||||
public string Description { get; } = "Sets the volume for the selected device, regardless of the current default device.";
|
||||
public string Name { get; } = Strings.SetVolumeName;
|
||||
public string Description { get; } = Strings.SetVolumeDescription;
|
||||
|
||||
|
||||
public IMassiveKnobActionInstance Create()
|
||||
@ -23,14 +23,14 @@ namespace MassiveKnob.Plugin.CoreAudio.Actions
|
||||
private class Instance : IMassiveKnobAnalogAction
|
||||
{
|
||||
private IMassiveKnobActionContext actionContext;
|
||||
private DeviceVolumeActionSettings settings;
|
||||
private DeviceSetVolumeActionSettings settings;
|
||||
private IDevice playbackDevice;
|
||||
|
||||
|
||||
public void Initialize(IMassiveKnobActionContext context)
|
||||
{
|
||||
actionContext = context;
|
||||
settings = context.GetSettings<DeviceVolumeActionSettings>();
|
||||
settings = context.GetSettings<DeviceSetVolumeActionSettings>();
|
||||
ApplySettings();
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ namespace MassiveKnob.Plugin.CoreAudio.Actions
|
||||
|
||||
public UserControl CreateSettingsControl()
|
||||
{
|
||||
var viewModel = new DeviceVolumeActionSettingsViewModel(settings);
|
||||
var viewModel = new DeviceSetVolumeActionSettingsViewModel(settings);
|
||||
viewModel.PropertyChanged += (sender, args) =>
|
||||
{
|
||||
if (!viewModel.IsSettingsProperty(args.PropertyName))
|
||||
@ -59,7 +59,7 @@ namespace MassiveKnob.Plugin.CoreAudio.Actions
|
||||
ApplySettings();
|
||||
};
|
||||
|
||||
return new DeviceVolumeActionSettingsView(viewModel);
|
||||
return new DeviceSetVolumeActionSettingsView(viewModel);
|
||||
}
|
||||
|
||||
|
||||
@ -69,6 +69,9 @@ namespace MassiveKnob.Plugin.CoreAudio.Actions
|
||||
return;
|
||||
|
||||
await playbackDevice.SetVolumeAsync(value);
|
||||
|
||||
if (settings.OSD)
|
||||
OSDManager.Show(playbackDevice);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using MassiveKnob.Plugin.CoreAudio.Base;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.SetVolume
|
||||
{
|
||||
public class DeviceSetVolumeActionSettings : BaseDeviceSettings
|
||||
{
|
||||
// TODO OSD
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
<UserControl x:Class="MassiveKnob.Plugin.CoreAudio.SetVolume.DeviceSetVolumeActionSettingsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:base="clr-namespace:MassiveKnob.Plugin.CoreAudio.Base"
|
||||
xmlns:setVolume="clr-namespace:MassiveKnob.Plugin.CoreAudio.SetVolume"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="200" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance setVolume:DeviceSetVolumeActionSettingsViewModel}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<base:BaseDeviceSettingsView />
|
||||
</StackPanel>
|
||||
</UserControl>
|
@ -0,0 +1,14 @@
|
||||
namespace MassiveKnob.Plugin.CoreAudio.SetVolume
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DeviceSetVolumeActionSettingsView.xaml
|
||||
/// </summary>
|
||||
public partial class DeviceSetVolumeActionSettingsView
|
||||
{
|
||||
public DeviceSetVolumeActionSettingsView(DeviceSetVolumeActionSettingsViewModel viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using MassiveKnob.Plugin.CoreAudio.Base;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.SetVolume
|
||||
{
|
||||
public class DeviceSetVolumeActionSettingsViewModel : BaseDeviceSettingsViewModel<DeviceSetVolumeActionSettings>
|
||||
{
|
||||
// ReSharper disable once SuggestBaseTypeForParameter - by design
|
||||
public DeviceSetVolumeActionSettingsViewModel(DeviceSetVolumeActionSettings settings) : base(settings)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MassiveKnob.Plugin.CoreAudio.Settings
|
||||
{
|
||||
public class BaseDeviceSettings
|
||||
{
|
||||
public Guid? DeviceId { get; set; }
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
namespace MassiveKnob.Plugin.CoreAudio.Settings
|
||||
{
|
||||
public class DeviceVolumeActionSettings : BaseDeviceSettings
|
||||
{
|
||||
// TODO OSD
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
namespace MassiveKnob.Plugin.CoreAudio.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DeviceVolumeActionSettingsView.xaml
|
||||
/// </summary>
|
||||
public partial class DeviceVolumeActionSettingsView
|
||||
{
|
||||
public DeviceVolumeActionSettingsView(DeviceVolumeActionSettingsViewModel viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
namespace MassiveKnob.Plugin.CoreAudio.Settings
|
||||
{
|
||||
public class DeviceVolumeActionSettingsViewModel : BaseDeviceSettingsViewModel
|
||||
{
|
||||
// ReSharper disable once SuggestBaseTypeForParameter - by design
|
||||
public DeviceVolumeActionSettingsViewModel(DeviceVolumeActionSettings settings) : base(settings)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
160
Windows/MassiveKnob.Plugin.CoreAudio/Strings.Designer.cs
generated
160
Windows/MassiveKnob.Plugin.CoreAudio/Strings.Designer.cs
generated
@ -22,7 +22,7 @@ namespace MassiveKnob.Plugin.CoreAudio {
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Strings {
|
||||
public class Strings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
@ -36,7 +36,7 @@ namespace MassiveKnob.Plugin.CoreAudio {
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MassiveKnob.Plugin.CoreAudio.Strings", typeof(Strings).Assembly);
|
||||
@ -51,7 +51,7 @@ namespace MassiveKnob.Plugin.CoreAudio {
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
@ -63,7 +63,7 @@ namespace MassiveKnob.Plugin.CoreAudio {
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0}.
|
||||
/// </summary>
|
||||
internal static string DeviceDisplayNameActive {
|
||||
public static string DeviceDisplayNameActive {
|
||||
get {
|
||||
return ResourceManager.GetString("DeviceDisplayNameActive", resourceCulture);
|
||||
}
|
||||
@ -72,7 +72,7 @@ namespace MassiveKnob.Plugin.CoreAudio {
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} (Disabled).
|
||||
/// </summary>
|
||||
internal static string DeviceDisplayNameDisabled {
|
||||
public static string DeviceDisplayNameDisabled {
|
||||
get {
|
||||
return ResourceManager.GetString("DeviceDisplayNameDisabled", resourceCulture);
|
||||
}
|
||||
@ -81,7 +81,7 @@ namespace MassiveKnob.Plugin.CoreAudio {
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} (Inactive).
|
||||
/// </summary>
|
||||
internal static string DeviceDisplayNameInactive {
|
||||
public static string DeviceDisplayNameInactive {
|
||||
get {
|
||||
return ResourceManager.GetString("DeviceDisplayNameInactive", resourceCulture);
|
||||
}
|
||||
@ -90,7 +90,7 @@ namespace MassiveKnob.Plugin.CoreAudio {
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} (Not present).
|
||||
/// </summary>
|
||||
internal static string DeviceDisplayNameNotPresent {
|
||||
public static string DeviceDisplayNameNotPresent {
|
||||
get {
|
||||
return ResourceManager.GetString("DeviceDisplayNameNotPresent", resourceCulture);
|
||||
}
|
||||
@ -99,10 +99,154 @@ namespace MassiveKnob.Plugin.CoreAudio {
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} (Unplugged).
|
||||
/// </summary>
|
||||
internal static string DeviceDisplayNameUnplugged {
|
||||
public static string DeviceDisplayNameUnplugged {
|
||||
get {
|
||||
return ResourceManager.GetString("DeviceDisplayNameUnplugged", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sets the digital output to the muted state for the selected device, regardless of the current default device..
|
||||
/// </summary>
|
||||
public static string GetMutedDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("GetMutedDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Muted / unmuted.
|
||||
/// </summary>
|
||||
public static string GetMutedName {
|
||||
get {
|
||||
return ResourceManager.GetString("GetMutedName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sets the analog output to the volume for the selected device, regardless of the current default device..
|
||||
/// </summary>
|
||||
public static string GetVolumeDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("GetVolumeDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Volume.
|
||||
/// </summary>
|
||||
public static string GetVolumeName {
|
||||
get {
|
||||
return ResourceManager.GetString("GetVolumeName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Included with Massive Knob by default. Provides volume control per device and default device switching..
|
||||
/// </summary>
|
||||
public static string PluginDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("PluginDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Windows Core Audio.
|
||||
/// </summary>
|
||||
public static string PluginName {
|
||||
get {
|
||||
return ResourceManager.GetString("PluginName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Toggles the muted state for the selected device, regardless of the current default device..
|
||||
/// </summary>
|
||||
public static string SetMutedDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("SetMutedDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Mute / unmute.
|
||||
/// </summary>
|
||||
public static string SetMutedName {
|
||||
get {
|
||||
return ResourceManager.GetString("SetMutedName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Inverted (muted is off).
|
||||
/// </summary>
|
||||
public static string SettingGetMutedInverted {
|
||||
get {
|
||||
return ResourceManager.GetString("SettingGetMutedInverted", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show On-Screen Display.
|
||||
/// </summary>
|
||||
public static string SettingOSD {
|
||||
get {
|
||||
return ResourceManager.GetString("SettingOSD", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Playback device.
|
||||
/// </summary>
|
||||
public static string SettingPlaybackDevice {
|
||||
get {
|
||||
return ResourceManager.GetString("SettingPlaybackDevice", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Inverted (off is muted).
|
||||
/// </summary>
|
||||
public static string SettingSetMutedSetInverted {
|
||||
get {
|
||||
return ResourceManager.GetString("SettingSetMutedSetInverted", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Set mute depending on value (eg. switch).
|
||||
/// </summary>
|
||||
public static string SettingSetMutedToggleFalse {
|
||||
get {
|
||||
return ResourceManager.GetString("SettingSetMutedToggleFalse", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Toggle mute when input turns on (eg. push button).
|
||||
/// </summary>
|
||||
public static string SettingSetMutedToggleTrue {
|
||||
get {
|
||||
return ResourceManager.GetString("SettingSetMutedToggleTrue", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sets the volume for the selected device to the value of the analog input, regardless of the current default device..
|
||||
/// </summary>
|
||||
public static string SetVolumeDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("SetVolumeDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Volume.
|
||||
/// </summary>
|
||||
public static string SetVolumeName {
|
||||
get {
|
||||
return ResourceManager.GetString("SetVolumeName", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,4 +132,52 @@
|
||||
<data name="DeviceDisplayNameUnplugged" xml:space="preserve">
|
||||
<value>{0} (Unplugged)</value>
|
||||
</data>
|
||||
<data name="GetMutedDescription" xml:space="preserve">
|
||||
<value>Sets the digital output to the muted state for the selected device, regardless of the current default device.</value>
|
||||
</data>
|
||||
<data name="GetMutedName" xml:space="preserve">
|
||||
<value>Muted / unmuted</value>
|
||||
</data>
|
||||
<data name="GetVolumeDescription" xml:space="preserve">
|
||||
<value>Sets the analog output to the volume for the selected device, regardless of the current default device.</value>
|
||||
</data>
|
||||
<data name="GetVolumeName" xml:space="preserve">
|
||||
<value>Volume</value>
|
||||
</data>
|
||||
<data name="PluginDescription" xml:space="preserve">
|
||||
<value>Included with Massive Knob by default. Provides volume control per device and default device switching.</value>
|
||||
</data>
|
||||
<data name="PluginName" xml:space="preserve">
|
||||
<value>Windows Core Audio</value>
|
||||
</data>
|
||||
<data name="SetMutedDescription" xml:space="preserve">
|
||||
<value>Toggles the muted state for the selected device, regardless of the current default device.</value>
|
||||
</data>
|
||||
<data name="SetMutedName" xml:space="preserve">
|
||||
<value>Mute / unmute</value>
|
||||
</data>
|
||||
<data name="SettingGetMutedInverted" xml:space="preserve">
|
||||
<value>Inverted (muted is off)</value>
|
||||
</data>
|
||||
<data name="SettingOSD" xml:space="preserve">
|
||||
<value>Show On-Screen Display</value>
|
||||
</data>
|
||||
<data name="SettingPlaybackDevice" xml:space="preserve">
|
||||
<value>Playback device</value>
|
||||
</data>
|
||||
<data name="SettingSetMutedSetInverted" xml:space="preserve">
|
||||
<value>Inverted (off is muted)</value>
|
||||
</data>
|
||||
<data name="SettingSetMutedToggleFalse" xml:space="preserve">
|
||||
<value>Set mute depending on value (eg. switch)</value>
|
||||
</data>
|
||||
<data name="SettingSetMutedToggleTrue" xml:space="preserve">
|
||||
<value>Toggle mute when input turns on (eg. push button)</value>
|
||||
</data>
|
||||
<data name="SetVolumeDescription" xml:space="preserve">
|
||||
<value>Sets the volume for the selected device to the value of the analog input, regardless of the current default device.</value>
|
||||
</data>
|
||||
<data name="SetVolumeName" xml:space="preserve">
|
||||
<value>Volume</value>
|
||||
</data>
|
||||
</root>
|
@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Windows.Controls;
|
||||
using MassiveKnob.Plugin.EmulatorDevice.Settings;
|
||||
|
||||
namespace MassiveKnob.Plugin.EmulatorDevice.Devices
|
||||
{
|
||||
public class EmulatorDevice : IMassiveKnobDevice
|
||||
{
|
||||
public Guid DeviceId { get; } = new Guid("e1a4977a-abf4-4c75-a17d-fd8d3a8451ff");
|
||||
public string Name { get; } = "Mock device";
|
||||
public string Description { get; } = "Emulates the actual device but does not communicate with anything.";
|
||||
|
||||
public IMassiveKnobDeviceInstance Create()
|
||||
{
|
||||
return new Instance();
|
||||
}
|
||||
|
||||
|
||||
private class Instance : IMassiveKnobDeviceInstance
|
||||
{
|
||||
private IMassiveKnobDeviceContext deviceContext;
|
||||
private EmulatorDeviceSettings settings;
|
||||
|
||||
private DeviceSpecs reportedSpecs;
|
||||
private EmulatorDeviceWindow window;
|
||||
private EmulatorDeviceWindowViewModel windowViewModel;
|
||||
|
||||
|
||||
public void Initialize(IMassiveKnobDeviceContext context)
|
||||
{
|
||||
deviceContext = context;
|
||||
settings = deviceContext.GetSettings<EmulatorDeviceSettings>();
|
||||
|
||||
windowViewModel = new EmulatorDeviceWindowViewModel(settings, context);
|
||||
window = new EmulatorDeviceWindow(windowViewModel);
|
||||
ApplySettings();
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
window.Close();
|
||||
}
|
||||
|
||||
|
||||
private void ApplySettings()
|
||||
{
|
||||
if (settings.AnalogInputCount != reportedSpecs.AnalogInputCount ||
|
||||
settings.DigitalInputCount != reportedSpecs.DigitalInputCount ||
|
||||
settings.AnalogOutputCount != reportedSpecs.AnalogOutputCount ||
|
||||
settings.DigitalOutputCount != reportedSpecs.DigitalOutputCount)
|
||||
{
|
||||
reportedSpecs = new DeviceSpecs(
|
||||
settings.AnalogInputCount, settings.DigitalInputCount,
|
||||
settings.AnalogOutputCount, settings.DigitalOutputCount);
|
||||
|
||||
deviceContext.Connected(reportedSpecs);
|
||||
}
|
||||
|
||||
windowViewModel.ApplySettings();
|
||||
window.Show();
|
||||
}
|
||||
|
||||
|
||||
public UserControl CreateSettingsControl()
|
||||
{
|
||||
var viewModel = new EmulatorDeviceSettingsViewModel(settings);
|
||||
viewModel.PropertyChanged += (sender, args) =>
|
||||
{
|
||||
deviceContext.SetSettings(settings);
|
||||
ApplySettings();
|
||||
};
|
||||
|
||||
return new EmulatorDeviceSettingsView(viewModel);
|
||||
}
|
||||
|
||||
|
||||
public void SetAnalogOutput(int analogOutputIndex, byte value)
|
||||
{
|
||||
if (analogOutputIndex >= windowViewModel.AnalogOutputCount)
|
||||
return;
|
||||
|
||||
windowViewModel.AnalogOutputs[analogOutputIndex].AnalogValue = value;
|
||||
}
|
||||
|
||||
|
||||
public void SetDigitalOutput(int digitalOutputIndex, bool @on)
|
||||
{
|
||||
if (digitalOutputIndex >= windowViewModel.DigitalOutputCount)
|
||||
return;
|
||||
|
||||
windowViewModel.DigitalOutputs[digitalOutputIndex].DigitalValue = on;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
<Window x:Class="MassiveKnob.Plugin.EmulatorDevice.Devices.EmulatorDeviceWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:devices="clr-namespace:MassiveKnob.Plugin.EmulatorDevice.Devices"
|
||||
mc:Ignorable="d"
|
||||
Title="Massive Knob - Device Emulator" Height="400" Width="300"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
WindowStyle="ToolWindow"
|
||||
Topmost="True"
|
||||
d:DataContext="{d:DesignInstance devices:EmulatorDeviceWindowViewModelDesignTime, IsDesignTimeCreatable=True}">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<Style TargetType="DockPanel" x:Key="Row">
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBlock" x:Key="Label">
|
||||
<Setter Property="Margin" Value="4,4,8,4" />
|
||||
<Setter Property="DockPanel.Dock" Value="Left" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="Value">
|
||||
<Setter Property="Control.Margin" Value="4,4,8,4" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel>
|
||||
<ItemsControl ItemsSource="{Binding AnalogInputs}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DockPanel Style="{StaticResource Row}">
|
||||
<TextBlock Text="{Binding DisplayName}" Style="{StaticResource Label}" />
|
||||
<Slider Minimum="0" Maximum="100" Value="{Binding AnalogValue}" Style="{StaticResource Value}" />
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<ItemsControl ItemsSource="{Binding DigitalInputs}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DockPanel Style="{StaticResource Row}">
|
||||
<TextBlock Text="{Binding DisplayName}" Style="{StaticResource Label}" />
|
||||
<CheckBox IsChecked="{Binding DigitalValue}" Style="{StaticResource Value}" />
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<ItemsControl ItemsSource="{Binding AnalogOutputs}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DockPanel Style="{StaticResource Row}">
|
||||
<TextBlock Text="{Binding DisplayName}" Style="{StaticResource Label}" />
|
||||
<TextBlock Text="{Binding AnalogValue}" Style="{StaticResource Value}" />
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<ItemsControl ItemsSource="{Binding DigitalOutputs}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DockPanel Style="{StaticResource Row}">
|
||||
<TextBlock Text="{Binding DisplayName}" Style="{StaticResource Label}" />
|
||||
<TextBlock Text="{Binding DigitalValueDisplayText}" Style="{StaticResource Value}" />
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Window>
|
@ -0,0 +1,14 @@
|
||||
namespace MassiveKnob.Plugin.EmulatorDevice.Devices
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for EmulatorDeviceWindow.xaml
|
||||
/// </summary>
|
||||
public partial class EmulatorDeviceWindow
|
||||
{
|
||||
public EmulatorDeviceWindow(EmulatorDeviceWindowViewModel viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,264 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using MassiveKnob.Plugin.EmulatorDevice.Settings;
|
||||
|
||||
namespace MassiveKnob.Plugin.EmulatorDevice.Devices
|
||||
{
|
||||
public class EmulatorDeviceWindowViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private readonly EmulatorDeviceSettings settings;
|
||||
private readonly IMassiveKnobDeviceContext context;
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
|
||||
// ReSharper disable UnusedMember.Global - used by WPF Binding
|
||||
private int analogInputCount;
|
||||
public int AnalogInputCount
|
||||
{
|
||||
get => analogInputCount;
|
||||
set
|
||||
{
|
||||
if (value == analogInputCount)
|
||||
return;
|
||||
|
||||
analogInputCount = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
AnalogInputs = Enumerable.Range(0, AnalogInputCount)
|
||||
.Select(i => new InputOutputViewModel(context, MassiveKnobActionType.InputAnalog, i))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private IList<InputOutputViewModel> analogInputs;
|
||||
public IList<InputOutputViewModel> AnalogInputs
|
||||
{
|
||||
get => analogInputs;
|
||||
set
|
||||
{
|
||||
analogInputs = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int digitalInputCount;
|
||||
public int DigitalInputCount
|
||||
{
|
||||
get => digitalInputCount;
|
||||
set
|
||||
{
|
||||
if (value == digitalInputCount)
|
||||
return;
|
||||
|
||||
digitalInputCount = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
DigitalInputs = Enumerable.Range(0, DigitalInputCount)
|
||||
.Select(i => new InputOutputViewModel(context, MassiveKnobActionType.InputDigital, i))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private IList<InputOutputViewModel> digitalInputs;
|
||||
public IList<InputOutputViewModel> DigitalInputs
|
||||
{
|
||||
get => digitalInputs;
|
||||
set
|
||||
{
|
||||
digitalInputs = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int analogOutputCount;
|
||||
public int AnalogOutputCount
|
||||
{
|
||||
get => analogOutputCount;
|
||||
set
|
||||
{
|
||||
if (value == analogOutputCount)
|
||||
return;
|
||||
|
||||
analogOutputCount = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
AnalogOutputs = Enumerable.Range(0, AnalogOutputCount)
|
||||
.Select(i => new InputOutputViewModel(context, MassiveKnobActionType.OutputAnalog, i))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private IList<InputOutputViewModel> analogOutputs;
|
||||
public IList<InputOutputViewModel> AnalogOutputs
|
||||
{
|
||||
get => analogOutputs;
|
||||
set
|
||||
{
|
||||
analogOutputs = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int digitalOutputCount;
|
||||
public int DigitalOutputCount
|
||||
{
|
||||
get => digitalOutputCount;
|
||||
set
|
||||
{
|
||||
if (value == digitalOutputCount)
|
||||
return;
|
||||
|
||||
digitalOutputCount = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
DigitalOutputs = Enumerable.Range(0, DigitalOutputCount)
|
||||
.Select(i => new InputOutputViewModel(context, MassiveKnobActionType.OutputDigital, i))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private IList<InputOutputViewModel> digitalOutputs;
|
||||
public IList<InputOutputViewModel> DigitalOutputs
|
||||
{
|
||||
get => digitalOutputs;
|
||||
set
|
||||
{
|
||||
digitalOutputs = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
// ReSharper restore UnusedMember.Global
|
||||
|
||||
|
||||
|
||||
public EmulatorDeviceWindowViewModel(EmulatorDeviceSettings settings, IMassiveKnobDeviceContext context)
|
||||
{
|
||||
this.settings = settings;
|
||||
this.context = context;
|
||||
|
||||
ApplySettings();
|
||||
}
|
||||
|
||||
|
||||
public void ApplySettings()
|
||||
{
|
||||
AnalogInputCount = settings.AnalogInputCount;
|
||||
DigitalInputCount = settings.DigitalInputCount;
|
||||
AnalogOutputCount = settings.AnalogOutputCount;
|
||||
DigitalOutputCount = settings.DigitalOutputCount;
|
||||
}
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
|
||||
public class InputOutputViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private readonly IMassiveKnobDeviceContext context;
|
||||
public MassiveKnobActionType ActionType { get; }
|
||||
public int Index { get; }
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (ActionType)
|
||||
{
|
||||
case MassiveKnobActionType.InputAnalog:
|
||||
return $"Analog input #{Index + 1}";
|
||||
|
||||
case MassiveKnobActionType.InputDigital:
|
||||
return $"Digital input #{Index + 1}";
|
||||
|
||||
case MassiveKnobActionType.OutputAnalog:
|
||||
return $"Analog output #{Index + 1}";
|
||||
|
||||
case MassiveKnobActionType.OutputDigital:
|
||||
return $"Digital output #{Index + 1}";
|
||||
|
||||
default:
|
||||
return (Index + 1).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private byte analogValue;
|
||||
public byte AnalogValue
|
||||
{
|
||||
get => analogValue;
|
||||
set
|
||||
{
|
||||
analogValue = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
if (ActionType == MassiveKnobActionType.InputAnalog)
|
||||
// Context can be null in DesignTime
|
||||
context?.AnalogChanged(Index, analogValue);
|
||||
}
|
||||
}
|
||||
|
||||
private bool digitalValue;
|
||||
public bool DigitalValue
|
||||
{
|
||||
get => digitalValue;
|
||||
set
|
||||
{
|
||||
digitalValue = value;
|
||||
OnPropertyChanged();
|
||||
OnDependantPropertyChanged("DigitalValueDisplayText");
|
||||
|
||||
if (ActionType == MassiveKnobActionType.InputDigital)
|
||||
context?.DigitalChanged(Index, digitalValue);
|
||||
}
|
||||
}
|
||||
|
||||
public string DigitalValueDisplayText => DigitalValue ? "On" : "Off";
|
||||
|
||||
|
||||
public InputOutputViewModel(IMassiveKnobDeviceContext context, MassiveKnobActionType actionType, int index)
|
||||
{
|
||||
this.context = context;
|
||||
ActionType = actionType;
|
||||
Index = index;
|
||||
}
|
||||
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
protected void OnDependantPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class EmulatorDeviceWindowViewModelDesignTime : EmulatorDeviceWindowViewModel
|
||||
{
|
||||
public EmulatorDeviceWindowViewModelDesignTime() : base(
|
||||
new EmulatorDeviceSettings
|
||||
{
|
||||
AnalogInputCount = 2,
|
||||
DigitalInputCount = 2,
|
||||
AnalogOutputCount = 2,
|
||||
DigitalOutputCount = 2
|
||||
}, null)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -7,8 +7,8 @@
|
||||
<ProjectGuid>{674DE974-B134-4DB5-BFAF-7BC3D05E16DE}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MassiveKnob.Plugin.MockDevice</RootNamespace>
|
||||
<AssemblyName>MassiveKnob.Plugin.MockDevice</AssemblyName>
|
||||
<RootNamespace>MassiveKnob.Plugin.EmulatorDevice</RootNamespace>
|
||||
<AssemblyName>MassiveKnob.Plugin.EmulatorDevice</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
@ -45,14 +45,18 @@
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Devices\MockDevice.cs" />
|
||||
<Compile Include="MassiveKnobMockDevicePlugin.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Settings\MockDeviceSettingsView.xaml.cs">
|
||||
<DependentUpon>MockDeviceSettingsView.xaml</DependentUpon>
|
||||
<Compile Include="Devices\EmulatorDevice.cs" />
|
||||
<Compile Include="Devices\EmulatorDeviceWindow.xaml.cs">
|
||||
<DependentUpon>EmulatorDeviceWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Settings\MockDeviceSettings.cs" />
|
||||
<Compile Include="Settings\MockDeviceSettingsViewModel.cs" />
|
||||
<Compile Include="Devices\EmulatorDeviceWindowViewModel.cs" />
|
||||
<Compile Include="MassiveKnobEmulatorDevicePlugin.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Settings\EmulatorDeviceSettingsView.xaml.cs">
|
||||
<DependentUpon>EmulatorDeviceSettingsView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Settings\EmulatorDeviceSettings.cs" />
|
||||
<Compile Include="Settings\EmulatorDeviceSettingsViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MassiveKnob.Plugin\MassiveKnob.Plugin.csproj">
|
||||
@ -62,7 +66,11 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Page Include="Settings\MockDeviceSettingsView.xaml">
|
||||
<Page Include="Devices\EmulatorDeviceWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Settings\EmulatorDeviceSettingsView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MassiveKnob.Plugin.MockDevice
|
||||
namespace MassiveKnob.Plugin.EmulatorDevice
|
||||
{
|
||||
[MassiveKnobPlugin]
|
||||
public class MassiveKnobMockDevicePlugin : IMassiveKnobDevicePlugin
|
||||
public class MassiveKnobEmulatorDevicePlugin : IMassiveKnobDevicePlugin
|
||||
{
|
||||
public Guid PluginId { get; } = new Guid("85f04232-d70f-494c-94a2-41452591ffb3");
|
||||
public string Name { get; } = "Mock Device";
|
||||
@ -14,7 +14,7 @@ namespace MassiveKnob.Plugin.MockDevice
|
||||
|
||||
public IEnumerable<IMassiveKnobDevice> Devices { get; } = new IMassiveKnobDevice[]
|
||||
{
|
||||
new Devices.MockDevice()
|
||||
new EmulatorDevice.Devices.EmulatorDevice()
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
namespace MassiveKnob.Plugin.EmulatorDevice.Settings
|
||||
{
|
||||
public class EmulatorDeviceSettings
|
||||
{
|
||||
public int AnalogInputCount { get; set; } = 2;
|
||||
public int DigitalInputCount { get; set; } = 2;
|
||||
public int AnalogOutputCount { get; set; } = 2;
|
||||
public int DigitalOutputCount { get; set; } = 2;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<UserControl x:Class="MassiveKnob.Plugin.EmulatorDevice.Settings.EmulatorDeviceSettingsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:settings="clr-namespace:MassiveKnob.Plugin.EmulatorDevice.Settings"
|
||||
mc:Ignorable="d" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance Type=settings:EmulatorDeviceSettingsViewModel}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Margin="4">Analog inputs</TextBlock>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Margin="4" Width="150" HorizontalAlignment="Left" Text="{Binding AnalogInputCount}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Margin="4">Digital inputs</TextBlock>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Margin="4" Width="150" HorizontalAlignment="Left" Text="{Binding DigitalInputCount}" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Margin="4">Analog outputs</TextBlock>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Margin="4" Width="150" HorizontalAlignment="Left" Text="{Binding AnalogOutputCount}" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Margin="4">Digital outputs</TextBlock>
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Margin="4" Width="150" HorizontalAlignment="Left" Text="{Binding DigitalOutputCount}" />
|
||||
</Grid>
|
||||
</UserControl>
|
@ -0,0 +1,15 @@
|
||||
namespace MassiveKnob.Plugin.EmulatorDevice.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for EmulatorDeviceSettingsView.xaml
|
||||
/// </summary>
|
||||
public partial class EmulatorDeviceSettingsView
|
||||
{
|
||||
public EmulatorDeviceSettingsView(EmulatorDeviceSettingsViewModel settingsViewModel)
|
||||
{
|
||||
DataContext = settingsViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MassiveKnob.Plugin.EmulatorDevice.Settings
|
||||
{
|
||||
public class EmulatorDeviceSettingsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private readonly EmulatorDeviceSettings settings;
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
|
||||
// ReSharper disable UnusedMember.Global - used by WPF Binding
|
||||
public int AnalogInputCount
|
||||
{
|
||||
get => settings.AnalogInputCount;
|
||||
set
|
||||
{
|
||||
if (value == settings.AnalogInputCount)
|
||||
return;
|
||||
|
||||
settings.AnalogInputCount = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int DigitalInputCount
|
||||
{
|
||||
get => settings.DigitalInputCount;
|
||||
set
|
||||
{
|
||||
if (value == settings.DigitalInputCount)
|
||||
return;
|
||||
|
||||
settings.DigitalInputCount = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int AnalogOutputCount
|
||||
{
|
||||
get => settings.AnalogOutputCount;
|
||||
set
|
||||
{
|
||||
if (value == settings.AnalogOutputCount)
|
||||
return;
|
||||
|
||||
settings.AnalogOutputCount = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int DigitalOutputCount
|
||||
{
|
||||
get => settings.DigitalOutputCount;
|
||||
set
|
||||
{
|
||||
if (value == settings.DigitalOutputCount)
|
||||
return;
|
||||
|
||||
settings.DigitalOutputCount = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
// ReSharper restore UnusedMember.Global
|
||||
|
||||
|
||||
public EmulatorDeviceSettingsViewModel(EmulatorDeviceSettings settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Windows.Controls;
|
||||
using MassiveKnob.Plugin.MockDevice.Settings;
|
||||
|
||||
namespace MassiveKnob.Plugin.MockDevice.Devices
|
||||
{
|
||||
public class MockDevice : IMassiveKnobDevice
|
||||
{
|
||||
public Guid DeviceId { get; } = new Guid("e1a4977a-abf4-4c75-a17d-fd8d3a8451ff");
|
||||
public string Name { get; } = "Mock device";
|
||||
public string Description { get; } = "Emulates the actual device but does not communicate with anything.";
|
||||
|
||||
public IMassiveKnobDeviceInstance Create()
|
||||
{
|
||||
return new Instance();
|
||||
}
|
||||
|
||||
|
||||
private class Instance : IMassiveKnobDeviceInstance
|
||||
{
|
||||
private IMassiveKnobDeviceContext deviceContext;
|
||||
private MockDeviceSettings settings;
|
||||
private Timer inputChangeTimer;
|
||||
|
||||
private int reportedAnalogInputCount;
|
||||
private int reportedDigitalInputCount;
|
||||
private readonly Random random = new Random();
|
||||
|
||||
|
||||
public void Initialize(IMassiveKnobDeviceContext context)
|
||||
{
|
||||
deviceContext = context;
|
||||
settings = deviceContext.GetSettings<MockDeviceSettings>();
|
||||
|
||||
ApplySettings();
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
inputChangeTimer?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
private void ApplySettings()
|
||||
{
|
||||
if (settings.AnalogCount != reportedAnalogInputCount ||
|
||||
settings.DigitalCount != reportedDigitalInputCount)
|
||||
{
|
||||
deviceContext.Connected(new DeviceSpecs(settings.AnalogCount, settings.DigitalCount, 0, 0));
|
||||
|
||||
reportedAnalogInputCount = settings.AnalogCount;
|
||||
reportedDigitalInputCount = settings.DigitalCount;
|
||||
}
|
||||
|
||||
|
||||
var interval = TimeSpan.FromSeconds(Math.Max(settings.Interval, 1));
|
||||
|
||||
if (inputChangeTimer == null)
|
||||
inputChangeTimer = new Timer(Tick, null, interval, interval);
|
||||
else
|
||||
inputChangeTimer.Change(interval, interval);
|
||||
}
|
||||
|
||||
|
||||
public UserControl CreateSettingsControl()
|
||||
{
|
||||
var viewModel = new MockDeviceSettingsViewModel(settings);
|
||||
viewModel.PropertyChanged += (sender, args) =>
|
||||
{
|
||||
deviceContext.SetSettings(settings);
|
||||
ApplySettings();
|
||||
};
|
||||
|
||||
return new MockDeviceSettingsView(viewModel);
|
||||
}
|
||||
|
||||
|
||||
private void Tick(object state)
|
||||
{
|
||||
var totalInputCount = reportedAnalogInputCount + reportedDigitalInputCount;
|
||||
if (totalInputCount == 0)
|
||||
return;
|
||||
|
||||
var changeInput = random.Next(0, totalInputCount);
|
||||
|
||||
if (changeInput < reportedAnalogInputCount)
|
||||
deviceContext.AnalogChanged(changeInput, (byte)random.Next(0, 101));
|
||||
else
|
||||
deviceContext.DigitalChanged(changeInput - reportedAnalogInputCount, random.Next(0, 2) == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
namespace MassiveKnob.Plugin.MockDevice.Settings
|
||||
{
|
||||
public class MockDeviceSettings
|
||||
{
|
||||
public int AnalogCount { get; set; } = 3;
|
||||
public int DigitalCount { get; set; } = 1;
|
||||
public int Interval { get; set; } = 5;
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<UserControl x:Class="MassiveKnob.Plugin.MockDevice.Settings.MockDeviceSettingsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:settings="clr-namespace:MassiveKnob.Plugin.MockDevice.Settings"
|
||||
mc:Ignorable="d" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance Type=settings:MockDeviceSettingsViewModel}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Margin="4">Number of analog inputs</TextBlock>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Margin="4" Width="150" HorizontalAlignment="Left" Text="{Binding AnalogCount}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Margin="4">Number of digital inputs</TextBlock>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Margin="4" Width="150" HorizontalAlignment="Left" Text="{Binding DigitalCount}" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Margin="4">Random change interval (seconds)</TextBlock>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Margin="4" Width="150" HorizontalAlignment="Left" Text="{Binding Interval}" />
|
||||
</Grid>
|
||||
</UserControl>
|
@ -1,15 +0,0 @@
|
||||
namespace MassiveKnob.Plugin.MockDevice.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MockDeviceSettingsView.xaml
|
||||
/// </summary>
|
||||
public partial class MockDeviceSettingsView
|
||||
{
|
||||
public MockDeviceSettingsView(MockDeviceSettingsViewModel settingsViewModel)
|
||||
{
|
||||
DataContext = settingsViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MassiveKnob.Plugin.MockDevice.Settings
|
||||
{
|
||||
public class MockDeviceSettingsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private readonly MockDeviceSettings settings;
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
|
||||
// ReSharper disable UnusedMember.Global - used by WPF Binding
|
||||
public int AnalogCount
|
||||
{
|
||||
get => settings.AnalogCount;
|
||||
set
|
||||
{
|
||||
if (value == settings.AnalogCount)
|
||||
return;
|
||||
|
||||
settings.AnalogCount = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int DigitalCount
|
||||
{
|
||||
get => settings.DigitalCount;
|
||||
set
|
||||
{
|
||||
if (value == settings.DigitalCount)
|
||||
return;
|
||||
|
||||
settings.DigitalCount = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int Interval
|
||||
{
|
||||
get => settings.Interval;
|
||||
set
|
||||
{
|
||||
if (value == settings.Interval)
|
||||
return;
|
||||
|
||||
settings.Interval = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
// ReSharper restore UnusedMember.Global
|
||||
|
||||
|
||||
public MockDeviceSettingsViewModel(MockDeviceSettings settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
@ -59,6 +59,17 @@ namespace MassiveKnob.Plugin.SerialDevice.Devices
|
||||
|
||||
return new SerialDeviceSettingsView(viewModel);
|
||||
}
|
||||
|
||||
|
||||
public void SetAnalogOutput(int analogOutputIndex, byte value)
|
||||
{
|
||||
// TODO Support SetAnalogOutput
|
||||
}
|
||||
|
||||
public void SetDigitalOutput(int digitalOutputIndex, bool @on)
|
||||
{
|
||||
// TODO Support SetDigitalOutput
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -174,6 +174,7 @@ namespace MassiveKnob.Plugin.SerialDevice.Worker
|
||||
|
||||
if ((char) response == 'H')
|
||||
{
|
||||
// TODO support multiple I/O's
|
||||
var knobCount = serialPort.ReadByte();
|
||||
if (knobCount > -1)
|
||||
{
|
||||
|
@ -19,5 +19,19 @@ namespace MassiveKnob.Plugin
|
||||
/// determined by the UserControl. Return null to indicate there are no settings for this device.
|
||||
/// </summary>
|
||||
UserControl CreateSettingsControl();
|
||||
|
||||
/// <summary>
|
||||
/// Called when the state of an analog output should be changed.
|
||||
/// </summary>
|
||||
/// <param name="analogOutputIndex">The index of the analog output to set.</param>
|
||||
/// <param name="value">The analog value in the range of 0 to 100.</param>
|
||||
void SetAnalogOutput(int analogOutputIndex, byte value);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the state of a digital output should be changed.
|
||||
/// </summary>
|
||||
/// <param name="digitalOutputIndex">The index of the digital output to set.</param>
|
||||
/// <param name="on">Whether the signal is on or off.</param>
|
||||
void SetDigitalOutput(int digitalOutputIndex, bool on);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassiveKnob.Plugin", "Massi
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassiveKnob.Plugin.CoreAudio", "MassiveKnob.Plugin.CoreAudio\MassiveKnob.Plugin.CoreAudio.csproj", "{5BD5E2F2-9923-4F74-AC69-ACDA0B847937}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassiveKnob.Plugin.MockDevice", "MassiveKnob.Plugin.MockDevice\MassiveKnob.Plugin.MockDevice.csproj", "{674DE974-B134-4DB5-BFAF-7BC3D05E16DE}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassiveKnob.Plugin.EmulatorDevice", "MassiveKnob.Plugin.EmulatorDevice\MassiveKnob.Plugin.EmulatorDevice.csproj", "{674DE974-B134-4DB5-BFAF-7BC3D05E16DE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassiveKnob.Plugin.SerialDevice", "MassiveKnob.Plugin.SerialDevice\MassiveKnob.Plugin.SerialDevice.csproj", "{FC0D22D8-5F1B-4D85-8269-FA4837CDE3A2}"
|
||||
EndProject
|
||||
|
@ -1,2 +1,4 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String></wpf:ResourceDictionary>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OSD/@EntryIndexedValue">OSD</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=OSD/@EntryIndexedValue">OSD</s:String></wpf:ResourceDictionary>
|
@ -97,7 +97,7 @@ namespace MassiveKnob.Model
|
||||
public MassiveKnobActionInfo GetAction(MassiveKnobActionType actionType, int index)
|
||||
{
|
||||
var list = GetActionMappingList(actionType);
|
||||
return index >= list.Count ? null : list[index].ActionInfo;
|
||||
return index >= list.Count ? null : list[index]?.ActionInfo;
|
||||
}
|
||||
|
||||
|
||||
@ -244,7 +244,7 @@ namespace MassiveKnob.Model
|
||||
if (index >= list.Count)
|
||||
return;
|
||||
|
||||
if (list[index].Context != context)
|
||||
if (list[index]?.Context != context)
|
||||
throw new InvalidOperationException("Caller must be the active action to retrieve the settings");
|
||||
|
||||
lock (settingsLock)
|
||||
@ -273,7 +273,7 @@ namespace MassiveKnob.Model
|
||||
return;
|
||||
|
||||
var mapping = GetActionMappingList(MassiveKnobActionType.InputAnalog);
|
||||
if (mapping == null || analogInputIndex >= mapping.Count)
|
||||
if (mapping == null || analogInputIndex >= mapping.Count || mapping[analogInputIndex] == null)
|
||||
return;
|
||||
|
||||
if (mapping[analogInputIndex].ActionInfo.Instance is IMassiveKnobAnalogAction analogAction)
|
||||
@ -286,8 +286,8 @@ namespace MassiveKnob.Model
|
||||
if (context != activeDeviceContext)
|
||||
return;
|
||||
|
||||
var mapping = GetActionMappingList(MassiveKnobActionType.InputAnalog);
|
||||
if (mapping == null || digitalInputIndex >= mapping.Count)
|
||||
var mapping = GetActionMappingList(MassiveKnobActionType.InputDigital);
|
||||
if (mapping == null || digitalInputIndex >= mapping.Count || mapping[digitalInputIndex] == null)
|
||||
return;
|
||||
|
||||
if (mapping[digitalInputIndex].ActionInfo.Instance is IMassiveKnobDigitalAction digitalAction)
|
||||
@ -295,6 +295,38 @@ namespace MassiveKnob.Model
|
||||
}
|
||||
|
||||
|
||||
public void SetAnalogOutput(IMassiveKnobActionContext context, IMassiveKnobAction action, int index, byte value)
|
||||
{
|
||||
if (activeDevice == null)
|
||||
return;
|
||||
|
||||
var list = GetActionMappingList(action.ActionType);
|
||||
if (index >= list.Count)
|
||||
return;
|
||||
|
||||
if (list[index]?.Context != context)
|
||||
return;
|
||||
|
||||
activeDevice.Instance.SetAnalogOutput(index, value);
|
||||
}
|
||||
|
||||
|
||||
public void SetDigitalOutput(IMassiveKnobActionContext context, IMassiveKnobAction action, int index, bool on)
|
||||
{
|
||||
if (activeDevice == null)
|
||||
return;
|
||||
|
||||
var list = GetActionMappingList(action.ActionType);
|
||||
if (index >= list.Count)
|
||||
return;
|
||||
|
||||
if (list[index]?.Context != context)
|
||||
return;
|
||||
|
||||
activeDevice.Instance.SetDigitalOutput(index, on);
|
||||
}
|
||||
|
||||
|
||||
private List<ActionMapping> GetActionMappingList(MassiveKnobActionType actionType)
|
||||
{
|
||||
switch (actionType)
|
||||
@ -534,15 +566,15 @@ namespace MassiveKnob.Model
|
||||
}
|
||||
|
||||
|
||||
public void SetDigitalOutput(bool on)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public void SetAnalogOutput(byte value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
owner.SetAnalogOutput(this, action, index, value);
|
||||
}
|
||||
|
||||
|
||||
public void SetDigitalOutput(bool on)
|
||||
{
|
||||
owner.SetDigitalOutput(this, action, index, on);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,10 +17,9 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBlock" x:Key="SubHeader">
|
||||
<Setter Property="Background" Value="LightSlateGray" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="Padding" Value="5" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Margin" Value="0,0,0,4" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="StackPanel" x:Key="Content">
|
||||
|
@ -26,10 +26,10 @@
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<StackPanel Orientation="Vertical" Style="{StaticResource Content}">
|
||||
<TextBlock Text="{Binding DisplayName}" Style="{StaticResource SubHeader}"></TextBlock>
|
||||
|
||||
<StackPanel Orientation="Vertical" Style="{StaticResource Content}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<ComboBox
|
||||
ItemsSource="{Binding Actions}"
|
||||
SelectedItem="{Binding SelectedAction}"
|
||||
|
@ -30,7 +30,7 @@
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<!--
|
||||
|
||||
|
@ -24,7 +24,7 @@ namespace MassiveKnob.ViewModel
|
||||
? $"Output #{index + 1}"
|
||||
: $"Input #{index + 1}";
|
||||
|
||||
public IList<ActionViewModel> Actions => settingsViewModel.Actions;
|
||||
public IList<ActionViewModel> Actions { get; }
|
||||
|
||||
|
||||
public ActionViewModel SelectedAction
|
||||
@ -66,6 +66,9 @@ namespace MassiveKnob.ViewModel
|
||||
this.actionType = actionType;
|
||||
this.index = index;
|
||||
|
||||
|
||||
Actions = settingsViewModel.Actions.Where(a => a.RepresentsNull || a.Action.ActionType == actionType).ToList();
|
||||
|
||||
var actionInfo = orchestrator.GetAction(actionType, index);
|
||||
|
||||
selectedAction = actionInfo != null
|
||||
|
@ -10,6 +10,7 @@ using MassiveKnob.Plugin;
|
||||
|
||||
namespace MassiveKnob.ViewModel
|
||||
{
|
||||
// TODO better design-time version
|
||||
public class SettingsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private readonly IMassiveKnobOrchestrator orchestrator;
|
||||
@ -65,8 +66,10 @@ namespace MassiveKnob.ViewModel
|
||||
{
|
||||
specs = value;
|
||||
OnPropertyChanged();
|
||||
OnOtherPropertyChanged("AnalogInputVisibility");
|
||||
OnOtherPropertyChanged("DigitalInputVisibility");
|
||||
OnDependantPropertyChanged("AnalogInputVisibility");
|
||||
OnDependantPropertyChanged("DigitalInputVisibility");
|
||||
OnDependantPropertyChanged("AnalogOutputVisibility");
|
||||
OnDependantPropertyChanged("DigitalOutputVisibility");
|
||||
|
||||
AnalogInputs = Enumerable
|
||||
.Range(0, specs?.AnalogInputCount ?? 0)
|
||||
@ -75,6 +78,14 @@ namespace MassiveKnob.ViewModel
|
||||
DigitalInputs = Enumerable
|
||||
.Range(0, specs?.DigitalInputCount ?? 0)
|
||||
.Select(i => new InputOutputViewModel(this, orchestrator, MassiveKnobActionType.InputDigital, i));
|
||||
|
||||
AnalogOutputs = Enumerable
|
||||
.Range(0, specs?.AnalogOutputCount ?? 0)
|
||||
.Select(i => new InputOutputViewModel(this, orchestrator, MassiveKnobActionType.OutputAnalog, i));
|
||||
|
||||
DigitalOutputs = Enumerable
|
||||
.Range(0, specs?.DigitalOutputCount ?? 0)
|
||||
.Select(i => new InputOutputViewModel(this, orchestrator, MassiveKnobActionType.OutputDigital, i));
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,7 +186,7 @@ namespace MassiveKnob.ViewModel
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
protected virtual void OnOtherPropertyChanged(string propertyName)
|
||||
protected virtual void OnDependantPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user