1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2026-01-02 10:43:31 +00:00

Merge pull request #292 from SpoinkyNL/development

1.8.0.0
This commit is contained in:
Robert Beekman 2017-01-26 11:12:50 +01:00 committed by GitHub
commit 507265b274
118 changed files with 4784 additions and 3291 deletions

View File

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14 # Visual Studio 14
VisualStudioVersion = 14.0.25123.0 VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Artemis", "Artemis\Artemis.csproj", "{ED9997A2-E54C-4E9F-9350-62BE672C3ABE}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Artemis", "Artemis\Artemis.csproj", "{ED9997A2-E54C-4E9F-9350-62BE672C3ABE}"
EndProject EndProject

View File

@ -56,6 +56,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
@ -65,6 +66,7 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<ManifestCertificateThumbprint>EAC088BE27A2DE790AE6F37A020409F4A1B5EC0E</ManifestCertificateThumbprint> <ManifestCertificateThumbprint>EAC088BE27A2DE790AE6F37A020409F4A1B5EC0E</ManifestCertificateThumbprint>
@ -108,6 +110,7 @@
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath> <OutputPath>bin\x64\Release\</OutputPath>
@ -118,6 +121,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup /> <PropertyGroup />
<PropertyGroup> <PropertyGroup>
@ -131,16 +135,16 @@
<HintPath>..\packages\Betwixt.1.4.1\lib\net35\Betwixt.dll</HintPath> <HintPath>..\packages\Betwixt.1.4.1\lib\net35\Betwixt.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Caliburn.Micro, Version=3.0.2.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL"> <Reference Include="Caliburn.Micro, Version=3.0.3.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL">
<HintPath>..\packages\Caliburn.Micro.Core.3.0.2\lib\net45\Caliburn.Micro.dll</HintPath> <HintPath>..\packages\Caliburn.Micro.Core.3.0.3\lib\net45\Caliburn.Micro.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Caliburn.Micro.Platform, Version=3.0.2.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL"> <Reference Include="Caliburn.Micro.Platform, Version=3.0.3.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL">
<HintPath>..\packages\Caliburn.Micro.3.0.2\lib\net45\Caliburn.Micro.Platform.dll</HintPath> <HintPath>..\packages\Caliburn.Micro.3.0.3\lib\net45\Caliburn.Micro.Platform.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Caliburn.Micro.Platform.Core, Version=3.0.2.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL"> <Reference Include="Caliburn.Micro.Platform.Core, Version=3.0.3.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL">
<HintPath>..\packages\Caliburn.Micro.3.0.2\lib\net45\Caliburn.Micro.Platform.Core.dll</HintPath> <HintPath>..\packages\Caliburn.Micro.3.0.3\lib\net45\Caliburn.Micro.Platform.Core.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL"> <Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
@ -151,8 +155,12 @@
<HintPath>..\packages\Colore.5.1.0\lib\net35\Corale.Colore.dll</HintPath> <HintPath>..\packages\Colore.5.1.0\lib\net35\Corale.Colore.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="CUE.NET, Version=1.1.0.2, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="CSCore, Version=1.1.5992.18249, Culture=neutral, PublicKeyToken=5a08f2b6f4415dea, processorArchitecture=MSIL">
<HintPath>..\packages\CUE.NET.1.1.0.2\lib\net45\CUE.NET.dll</HintPath> <HintPath>..\packages\CSCore.1.1.0\lib\net35-client\CSCore.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="CUE.NET, Version=1.1.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\CUE.NET.1.1.1\lib\net45\CUE.NET.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="DeltaCompressionDotNet, Version=1.1.0.0, Culture=neutral, PublicKeyToken=1d14d6e5194e7f4a, processorArchitecture=MSIL"> <Reference Include="DeltaCompressionDotNet, Version=1.1.0.0, Culture=neutral, PublicKeyToken=1d14d6e5194e7f4a, processorArchitecture=MSIL">
@ -168,7 +176,7 @@
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="DynamicExpresso.Core, Version=1.3.3.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="DynamicExpresso.Core, Version=1.3.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DynamicExpresso.Core.1.3.3.4\lib\net40\DynamicExpresso.Core.dll</HintPath> <HintPath>..\packages\DynamicExpresso.Core.1.3.3.5\lib\net40\DynamicExpresso.Core.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="GongSolutions.Wpf.DragDrop, Version=0.1.4.3, Culture=neutral, PublicKeyToken=d19974ea350ccea1, processorArchitecture=MSIL"> <Reference Include="GongSolutions.Wpf.DragDrop, Version=0.1.4.3, Culture=neutral, PublicKeyToken=d19974ea350ccea1, processorArchitecture=MSIL">
@ -180,15 +188,15 @@
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\squirrel.windows.1.4.4\lib\Net45\ICSharpCode.SharpZipLib.dll</HintPath> <HintPath>..\packages\squirrel.windows.1.5.1\lib\Net45\ICSharpCode.SharpZipLib.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="log4net, Version=1.2.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL"> <Reference Include="log4net, Version=2.0.7.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.5\lib\net45-full\log4net.dll</HintPath> <HintPath>..\packages\log4net.2.0.7\lib\net45-full\log4net.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="MahApps.Metro, Version=1.3.0.0, Culture=neutral, PublicKeyToken=f4fb5a3c4d1e5b4f, processorArchitecture=MSIL"> <Reference Include="MahApps.Metro, Version=1.4.1.0, Culture=neutral, PublicKeyToken=f4fb5a3c4d1e5b4f, processorArchitecture=MSIL">
<HintPath>..\packages\MahApps.Metro.1.3.0\lib\net45\MahApps.Metro.dll</HintPath> <HintPath>..\packages\MahApps.Metro.1.4.1\lib\net45\MahApps.Metro.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> <Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
@ -211,10 +219,6 @@
<HintPath>..\packages\MoonSharp.2.0.0.0\lib\net40-client\MoonSharp.Interpreter.dll</HintPath> <HintPath>..\packages\MoonSharp.2.0.0.0\lib\net40-client\MoonSharp.Interpreter.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="NAudio, Version=1.7.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.1.7.3\lib\net35\NAudio.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private> <Private>True</Private>
@ -240,11 +244,15 @@
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\squirrel.windows.1.4.4\lib\Net45\NuGet.Squirrel.dll</HintPath> <HintPath>..\packages\squirrel.windows.1.5.1\lib\Net45\NuGet.Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Open.WinKeyboardHook, Version=1.0.11.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Open.WinKeyboardHook.1.0.11\lib\net45\Open.WinKeyboardHook.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Process.NET, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Process.NET, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Process.NET.1.0.5\lib\Process.NET.dll</HintPath> <HintPath>..\packages\Process.NET.1.0.8\lib\Process.NET.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="SharpDX, Version=3.1.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL"> <Reference Include="SharpDX, Version=3.1.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
@ -263,8 +271,8 @@
<HintPath>..\packages\SpotifyAPI-NET.2.12.0\lib\SpotifyAPI.dll</HintPath> <HintPath>..\packages\SpotifyAPI-NET.2.12.0\lib\SpotifyAPI.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Squirrel, Version=1.4.3.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Squirrel, Version=1.5.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\squirrel.windows.1.4.4\lib\Net45\Squirrel.dll</HintPath> <HintPath>..\packages\squirrel.windows.1.5.1\lib\Net45\Squirrel.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
@ -278,7 +286,7 @@
<Reference Include="System.Web" /> <Reference Include="System.Web" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\MahApps.Metro.1.3.0\lib\net45\System.Windows.Interactivity.dll</HintPath> <HintPath>..\packages\MahApps.Metro.1.4.1\lib\net45\System.Windows.Interactivity.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
@ -294,10 +302,6 @@
<HintPath>..\packages\WpfExceptionViewer.1.0.0.0\lib\VioletTape.WpfExceptionViewer.dll</HintPath> <HintPath>..\packages\WpfExceptionViewer.1.0.0.0\lib\VioletTape.WpfExceptionViewer.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="VirtualInput, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\VirtualInput.1.0.1\lib\net20\VirtualInput.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
@ -350,12 +354,15 @@
<Compile Include="DeviceProviders\Logitech\Utilities\OrionUtilities.cs" /> <Compile Include="DeviceProviders\Logitech\Utilities\OrionUtilities.cs" />
<Compile Include="DeviceProviders\Razer\BlackWidow.cs" /> <Compile Include="DeviceProviders\Razer\BlackWidow.cs" />
<Compile Include="DeviceProviders\Razer\Utilities\RazerUtilities.cs" /> <Compile Include="DeviceProviders\Razer\Utilities\RazerUtilities.cs" />
<Compile Include="Managers\MigrationManager.cs" />
<Compile Include="Managers\ModuleManager.cs" /> <Compile Include="Managers\ModuleManager.cs" />
<Compile Include="Managers\DeviceManager.cs" /> <Compile Include="Managers\DeviceManager.cs" />
<Compile Include="Managers\LoopManager.cs" /> <Compile Include="Managers\LoopManager.cs" />
<Compile Include="Managers\LuaManager.cs" /> <Compile Include="Managers\LuaManager.cs" />
<Compile Include="Managers\MainManager.cs" /> <Compile Include="Managers\MainManager.cs" />
<Compile Include="Managers\ProfileManager.cs" /> <Compile Include="Managers\PreviewManager.cs" />
<Compile Include="Models\LayerEditorModel.cs" />
<Compile Include="Models\ProfileEditorModel.cs" />
<Compile Include="Modules\Abstract\ModuleDataModel.cs" /> <Compile Include="Modules\Abstract\ModuleDataModel.cs" />
<Compile Include="Modules\Abstract\ModuleModel.cs" /> <Compile Include="Modules\Abstract\ModuleModel.cs" />
<Compile Include="Modules\Abstract\ModuleSettings.cs" /> <Compile Include="Modules\Abstract\ModuleSettings.cs" />
@ -489,9 +496,21 @@
<Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\DX9ScreenCapture.cs" /> <Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\DX9ScreenCapture.cs" />
<Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\IScreenCapture.cs" /> <Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\IScreenCapture.cs" />
<Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\ScreenCaptureManager.cs" /> <Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\ScreenCaptureManager.cs" />
<Compile Include="Profiles\Layers\Types\Audio\AudioCapturing\AudioCaptureManager.cs" /> <Compile Include="Profiles\Layers\Types\ConicalBrush\ConicalBrushPropertiesModel.cs" />
<Compile Include="Profiles\Layers\Types\Audio\AudioCapturing\FftEventArgs.cs" /> <Compile Include="Profiles\Layers\Types\ConicalBrush\ConicalBrushPropertiesView.xaml.cs">
<Compile Include="Profiles\Layers\Types\Audio\AudioCapturing\SampleAggregator.cs" /> <DependentUpon>ConicalBrushPropertiesView.xaml</DependentUpon>
</Compile>
<Compile Include="Profiles\Layers\Types\ConicalBrush\ConicalBrushPropertiesViewModel.cs" />
<Compile Include="Profiles\Layers\Types\ConicalBrush\ConicalBrushType.cs" />
<Compile Include="Profiles\Layers\Types\ConicalBrush\Drawing\ConicalGradientDrawer.cs" />
<Compile Include="Profiles\Layers\Types\Audio\AudioCapturing\AudioCapture.cs" />
<Compile Include="Managers\AudioCaptureManager.cs" />
<Compile Include="Events\AudioDeviceChangedEventArgs.cs" />
<Compile Include="Profiles\Layers\Types\Audio\AudioCapturing\BaseSpectrumProvider.cs" />
<Compile Include="Profiles\Layers\Types\Audio\AudioCapturing\ISpectrumProvider.cs" />
<Compile Include="Profiles\Layers\Types\Audio\AudioCapturing\LineSpectrum.cs" />
<Compile Include="Profiles\Layers\Types\Audio\AudioCapturing\SingleSpectrum.cs" />
<Compile Include="Profiles\Layers\Types\Audio\AudioCapturing\SpectrumBase.cs" />
<Compile Include="Profiles\Layers\Types\Audio\AudioPropertiesModel.cs" /> <Compile Include="Profiles\Layers\Types\Audio\AudioPropertiesModel.cs" />
<Compile Include="Profiles\Layers\Types\Audio\AudioPropertiesView.xaml.cs"> <Compile Include="Profiles\Layers\Types\Audio\AudioPropertiesView.xaml.cs">
<DependentUpon>AudioPropertiesView.xaml</DependentUpon> <DependentUpon>AudioPropertiesView.xaml</DependentUpon>
@ -618,6 +637,7 @@
<Compile Include="Utilities\Converters\NinjectCustomConverter.cs" /> <Compile Include="Utilities\Converters\NinjectCustomConverter.cs" />
<Compile Include="Utilities\Converters\ValueConverters.cs" /> <Compile Include="Utilities\Converters\ValueConverters.cs" />
<Compile Include="Utilities\DataReaders\DllManager.cs" /> <Compile Include="Utilities\DataReaders\DllManager.cs" />
<Compile Include="Utilities\EditorHelper.cs" />
<Compile Include="Utilities\ExtensionMethods.cs" /> <Compile Include="Utilities\ExtensionMethods.cs" />
<Compile Include="Utilities\GameState\GameDataReceivedEventArgs.cs" /> <Compile Include="Utilities\GameState\GameDataReceivedEventArgs.cs" />
<Compile Include="Utilities\GameState\GameStateWebServer.cs" /> <Compile Include="Utilities\GameState\GameStateWebServer.cs" />
@ -649,17 +669,16 @@
<Compile Include="Modules\Games\Witcher3\Witcher3ViewModel.cs" /> <Compile Include="Modules\Games\Witcher3\Witcher3ViewModel.cs" />
<Compile Include="ViewModels\Profiles\LayerTweenViewModel.cs" /> <Compile Include="ViewModels\Profiles\LayerTweenViewModel.cs" />
<Compile Include="ViewModels\Profiles\Events\EventPropertiesViewModel.cs" /> <Compile Include="ViewModels\Profiles\Events\EventPropertiesViewModel.cs" />
<Compile Include="ViewModels\Profiles\ProfileViewModel.cs" />
<Compile Include="Profiles\Layers\Types\Keyboard\KeyboardPropertiesViewModel.cs" /> <Compile Include="Profiles\Layers\Types\Keyboard\KeyboardPropertiesViewModel.cs" />
<Compile Include="ViewModels\Profiles\LayerConditionViewModel.cs" /> <Compile Include="ViewModels\Profiles\LayerConditionViewModel.cs" />
<Compile Include="ViewModels\Profiles\LayerDynamicPropertiesViewModel.cs" /> <Compile Include="ViewModels\Profiles\LayerDynamicPropertiesViewModel.cs" />
<Compile Include="ViewModels\Profiles\LayerEditorViewModel.cs" /> <Compile Include="ViewModels\LayerEditorViewModel.cs" />
<Compile Include="Profiles\Layers\Abstract\LayerPropertiesViewModel.cs" /> <Compile Include="Profiles\Layers\Abstract\LayerPropertiesViewModel.cs" />
<Compile Include="Profiles\Layers\Types\Headset\HeadsetPropertiesViewModel.cs" /> <Compile Include="Profiles\Layers\Types\Headset\HeadsetPropertiesViewModel.cs" />
<Compile Include="Profiles\Layers\Types\Folder\FolderPropertiesViewModel.cs" /> <Compile Include="Profiles\Layers\Types\Folder\FolderPropertiesViewModel.cs" />
<Compile Include="Profiles\Layers\Types\Mouse\MousePropertiesViewModel.cs" /> <Compile Include="Profiles\Layers\Types\Mouse\MousePropertiesViewModel.cs" />
<Compile Include="ViewModels\OverlaysViewModel.cs" /> <Compile Include="ViewModels\OverlaysViewModel.cs" />
<Compile Include="ViewModels\Profiles\ProfileEditorViewModel.cs" /> <Compile Include="ViewModels\ProfileEditorViewModel.cs" />
<Compile Include="ViewModels\ShellViewModel.cs" /> <Compile Include="ViewModels\ShellViewModel.cs" />
<Compile Include="ViewModels\WelcomeViewModel.cs" /> <Compile Include="ViewModels\WelcomeViewModel.cs" />
<Compile Include="Views\DebugView.xaml.cs"> <Compile Include="Views\DebugView.xaml.cs">
@ -704,7 +723,7 @@
<Compile Include="Views\Profiles\LayerDynamicPropertiesView.xaml.cs"> <Compile Include="Views\Profiles\LayerDynamicPropertiesView.xaml.cs">
<DependentUpon>LayerDynamicPropertiesView.xaml</DependentUpon> <DependentUpon>LayerDynamicPropertiesView.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\Profiles\LayerEditorView.xaml.cs"> <Compile Include="Views\LayerEditorView.xaml.cs">
<DependentUpon>LayerEditorView.xaml</DependentUpon> <DependentUpon>LayerEditorView.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Profiles\Layers\Types\Mouse\MousePropertiesView.xaml.cs"> <Compile Include="Profiles\Layers\Types\Mouse\MousePropertiesView.xaml.cs">
@ -716,7 +735,7 @@
<Compile Include="Views\Profiles\LayerTweenView.xaml.cs"> <Compile Include="Views\Profiles\LayerTweenView.xaml.cs">
<DependentUpon>LayerTweenView.xaml</DependentUpon> <DependentUpon>LayerTweenView.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\Profiles\ProfileEditorView.xaml.cs"> <Compile Include="Views\ProfileEditorView.xaml.cs">
<DependentUpon>ProfileEditorView.xaml</DependentUpon> <DependentUpon>ProfileEditorView.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\ShellView.xaml.cs"> <Compile Include="Views\ShellView.xaml.cs">
@ -855,6 +874,10 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Page> </Page>
<Page Include="Profiles\Layers\Types\ConicalBrush\ConicalBrushPropertiesView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Profiles\Layers\Types\Audio\AudioPropertiesView.xaml"> <Page Include="Profiles\Layers\Types\Audio\AudioPropertiesView.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
@ -947,7 +970,7 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Views\Profiles\LayerEditorView.xaml"> <Page Include="Views\LayerEditorView.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Page> </Page>
@ -963,7 +986,7 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Views\Profiles\ProfileEditorView.xaml"> <Page Include="Views\ProfileEditorView.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
@ -1010,12 +1033,12 @@
<Folder Include="Resources\Lua\" /> <Folder Include="Resources\Lua\" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\CUE.NET.1.1.0.2\build\net45\CUE.NET.targets" Condition="Exists('..\packages\CUE.NET.1.1.0.2\build\net45\CUE.NET.targets')" /> <Import Project="..\packages\CUE.NET.1.1.1\build\net45\CUE.NET.targets" Condition="Exists('..\packages\CUE.NET.1.1.1\build\net45\CUE.NET.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup> <PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('..\packages\CUE.NET.1.1.0.2\build\net45\CUE.NET.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CUE.NET.1.1.0.2\build\net45\CUE.NET.targets'))" /> <Error Condition="!Exists('..\packages\CUE.NET.1.1.1\build\net45\CUE.NET.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CUE.NET.1.1.1\build\net45\CUE.NET.targets'))" />
</Target> </Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
@ -9,6 +10,7 @@ using Artemis.Settings;
using Artemis.Utilities; using Artemis.Utilities;
using Artemis.Utilities.Converters; using Artemis.Utilities.Converters;
using Artemis.Utilities.DataReaders; using Artemis.Utilities.DataReaders;
using Artemis.Utilities.Keyboard;
using Artemis.ViewModels; using Artemis.ViewModels;
using Caliburn.Micro; using Caliburn.Micro;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -29,6 +31,7 @@ namespace Artemis
Initialize(); Initialize();
BindSpecialValues(); BindSpecialValues();
KeyboardHook.SetupKeyboardHook();
} }
private void BindSpecialValues() private void BindSpecialValues()
@ -91,6 +94,7 @@ namespace Artemis
JsonConvert.DefaultSettings = () => settings; JsonConvert.DefaultSettings = () => settings;
//TODO DarthAffe 17.12.2016: Is this the right location for this? //TODO DarthAffe 17.12.2016: Is this the right location for this?
//TODO Move to Mainmanager and make disposable
ActiveWindowHelper.Initialize(); ActiveWindowHelper.Initialize();
} }

View File

@ -21,7 +21,7 @@ namespace Artemis.DAL
{ {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly string ProfileFolder = public static readonly string ProfileFolder =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Artemis\profiles"; Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Artemis\profiles";
private static bool _installedDefaults; private static bool _installedDefaults;
@ -89,7 +89,7 @@ namespace Artemis.DAL
} }
File.WriteAllText(path + $@"\{prof.Slug}.json", json); File.WriteAllText(path + $@"\{prof.Slug}.json", json);
Logger.Trace("Saved profile {0}/{1}/{2}", prof.KeyboardSlug, prof.GameName, prof.Name); Logger.Debug("Saved profile {0}/{1}/{2}", prof.KeyboardSlug, prof.GameName, prof.Name);
} }
} }
@ -177,7 +177,7 @@ namespace Artemis.DAL
} }
} }
private static List<ProfileModel> ReadProfiles(string subDirectory) public static List<ProfileModel> ReadProfiles(string subDirectory)
{ {
var profiles = new List<ProfileModel>(); var profiles = new List<ProfileModel>();
var directory = ProfileFolder + "/" + subDirectory; var directory = ProfileFolder + "/" + subDirectory;

View File

@ -34,7 +34,7 @@ namespace Artemis.DeviceProviders.Corsair
public override void Disable() public override void Disable()
{ {
throw new NotImplementedException("Can only disable a keyboard"); throw new NotSupportedException("Can only disable a keyboard");
} }
public override void UpdateDevice(Bitmap bitmap) public override void UpdateDevice(Bitmap bitmap)
@ -45,7 +45,7 @@ namespace Artemis.DeviceProviders.Corsair
throw new ArgumentException("Bitmap must be a perfect square"); throw new ArgumentException("Bitmap must be a perfect square");
var leds = CueSDK.HeadsetSDK.Leds.Count(); var leds = CueSDK.HeadsetSDK.Leds.Count();
var step = (double) bitmap.Width/leds; var step = (double)bitmap.Width / leds;
var ledIndex = 0; var ledIndex = 0;
// Color each LED according to one of the pixels // Color each LED according to one of the pixels
@ -53,7 +53,7 @@ namespace Artemis.DeviceProviders.Corsair
{ {
var col = ledIndex == 0 var col = ledIndex == 0
? bitmap.GetPixel(0, 0) ? bitmap.GetPixel(0, 0)
: bitmap.GetPixel((int) ((ledIndex + 1)*step - 1), (int) ((ledIndex + 1)*step - 1)); : bitmap.GetPixel((int)((ledIndex + 1) * step - 1), (int)((ledIndex + 1) * step - 1));
corsairLed.Color = col; corsairLed.Color = col;
ledIndex++; ledIndex++;

View File

@ -136,8 +136,8 @@ namespace Artemis.DeviceProviders.Corsair
public override KeyMatch? GetKeyPosition(Keys keyCode) public override KeyMatch? GetKeyPosition(Keys keyCode)
{ {
var widthMultiplier = Width/_keyboard.Brush.RenderedRectangle.Width; var widthMultiplier = Width / _keyboard.Brush.RenderedRectangle.Width;
var heightMultiplier = Height/_keyboard.Brush.RenderedRectangle.Height; var heightMultiplier = Height / _keyboard.Brush.RenderedRectangle.Height;
CorsairLed cueLed = null; CorsairLed cueLed = null;
try try
@ -154,7 +154,7 @@ namespace Artemis.DeviceProviders.Corsair
return null; return null;
var center = cueLed.LedRectangle.GetCenter(); var center = cueLed.LedRectangle.GetCenter();
return new KeyMatch(keyCode, (int) (center.X*widthMultiplier), (int) (center.Y*heightMultiplier)); return new KeyMatch(keyCode, (int)(center.X * widthMultiplier), (int)(center.Y * heightMultiplier));
} }
} }
} }

View File

@ -34,7 +34,7 @@ namespace Artemis.DeviceProviders.Corsair
public override void Disable() public override void Disable()
{ {
throw new NotImplementedException("Can only disable a keyboard"); throw new NotSupportedException("Can only disable a keyboard");
} }
public override void UpdateDevice(Bitmap bitmap) public override void UpdateDevice(Bitmap bitmap)
@ -45,7 +45,7 @@ namespace Artemis.DeviceProviders.Corsair
throw new ArgumentException("Bitmap must be a perfect square"); throw new ArgumentException("Bitmap must be a perfect square");
var leds = CueSDK.MouseSDK.Leds.Count(); var leds = CueSDK.MouseSDK.Leds.Count();
var step = (double) bitmap.Width/leds; var step = (double)bitmap.Width / leds;
var ledIndex = 0; var ledIndex = 0;
// Color each LED according to one of the pixels // Color each LED according to one of the pixels
@ -53,7 +53,7 @@ namespace Artemis.DeviceProviders.Corsair
{ {
var col = ledIndex == 0 var col = ledIndex == 0
? bitmap.GetPixel(0, 0) ? bitmap.GetPixel(0, 0)
: bitmap.GetPixel((int) ((ledIndex + 1)*step - 1), (int) ((ledIndex + 1)*step - 1)); : bitmap.GetPixel((int)((ledIndex + 1) * step - 1), (int)((ledIndex + 1) * step - 1));
corsairLed.Color = col; corsairLed.Color = col;
ledIndex++; ledIndex++;

View File

@ -34,7 +34,7 @@ namespace Artemis.DeviceProviders.Corsair
public override void Disable() public override void Disable()
{ {
throw new NotImplementedException("Can only disable a keyboard"); throw new NotSupportedException("Can only disable a keyboard");
} }
public override void UpdateDevice(Bitmap bitmap) public override void UpdateDevice(Bitmap bitmap)
@ -44,8 +44,8 @@ namespace Artemis.DeviceProviders.Corsair
if (bitmap.Width != bitmap.Height) if (bitmap.Width != bitmap.Height)
throw new ArgumentException("Bitmap must be a perfect square"); throw new ArgumentException("Bitmap must be a perfect square");
var yStep = (double) bitmap.Width/4; var yStep = (double)bitmap.Width / 4;
var xStep = (double) bitmap.Width/6; var xStep = (double)bitmap.Width / 6;
// This approach will break if any mousemats with different LED amounts are released, for now it will do. // This approach will break if any mousemats with different LED amounts are released, for now it will do.
var ledIndex = 0; var ledIndex = 0;
@ -57,23 +57,23 @@ namespace Artemis.DeviceProviders.Corsair
if (ledIndex < 5) if (ledIndex < 5)
{ {
col = ledIndex == 0 col = ledIndex == 0
? bitmap.GetPixel(0, (int) (ledIndex*yStep)) ? bitmap.GetPixel(0, (int)(ledIndex * yStep))
: bitmap.GetPixel(0, (int) (ledIndex*yStep) - 1); : bitmap.GetPixel(0, (int)(ledIndex * yStep) - 1);
} }
// Bottom // Bottom
else if (ledIndex < 10) else if (ledIndex < 10)
{ {
// Start at index 1 because the corner belongs to the left side // Start at index 1 because the corner belongs to the left side
var zoneIndex = ledIndex - 4; var zoneIndex = ledIndex - 4;
col = bitmap.GetPixel((int) (zoneIndex*xStep), bitmap.Height - 1); col = bitmap.GetPixel((int)(zoneIndex * xStep), bitmap.Height - 1);
} }
// Right side // Right side
else else
{ {
var zoneIndex = ledIndex - 10; var zoneIndex = ledIndex - 10;
col = zoneIndex == 4 col = zoneIndex == 4
? bitmap.GetPixel(bitmap.Height - 1, bitmap.Height - (int) (zoneIndex*yStep)) ? bitmap.GetPixel(bitmap.Height - 1, bitmap.Height - (int)(zoneIndex * yStep))
: bitmap.GetPixel(bitmap.Height - 1, bitmap.Height - 1 - (int) (zoneIndex*yStep)); : bitmap.GetPixel(bitmap.Height - 1, bitmap.Height - 1 - (int)(zoneIndex * yStep));
} }
corsairLed.Color = col; corsairLed.Color = col;

View File

@ -27,17 +27,11 @@ namespace Artemis.DeviceProviders
public abstract void Enable(); public abstract void Enable();
public abstract void DrawBitmap(Bitmap bitmap); public abstract void DrawBitmap(Bitmap bitmap);
/// <summary>
/// Returns a bitmap matching the keyboard's dimensions
/// </summary>
/// <returns></returns>
public Bitmap KeyboardBitmap() => new Bitmap(Width, Height);
/// <summary> /// <summary>
/// Returns a bitmap matching the keyboard's dimensions using the provided scale /// Returns a bitmap matching the keyboard's dimensions using the provided scale
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public Bitmap KeyboardBitmap(int scale) => new Bitmap(Width*scale, Height*scale); public Bitmap KeyboardBitmap(int scale = 4) => new Bitmap(Width*scale, Height*scale);
public Rect KeyboardRectangle(int scale = 4) => new Rect(new Size(Width*scale, Height*scale)); public Rect KeyboardRectangle(int scale = 4) => new Rect(new Size(Width*scale, Height*scale));
@ -89,13 +83,12 @@ namespace Artemis.DeviceProviders
public override void UpdateDevice(Bitmap bitmap) public override void UpdateDevice(Bitmap bitmap)
{ {
throw new NotImplementedException("KeyboardProvider doesn't implement UpdateDevice, use DrawBitmap instead."); throw new NotSupportedException("KeyboardProvider doesn't implement UpdateDevice, use DrawBitmap instead.");
} }
public override bool TryEnable() public override bool TryEnable()
{ {
throw new NotImplementedException( throw new NotSupportedException("KeyboardProvider doesn't implement TryEnable, use CanEnableAsync instead.");
"KeyboardProvider doesn't implement TryEnable, use CanEnableAsync instead.");
} }
/// <summary> /// <summary>

View File

@ -49,7 +49,7 @@ namespace Artemis.DeviceProviders.Logitech
public override void Disable() public override void Disable()
{ {
throw new NotImplementedException("Can only disable a keyboard"); throw new NotSupportedException("Can only disable a keyboard");
} }
} }
} }

View File

@ -0,0 +1,17 @@
using System;
using CSCore.CoreAudioAPI;
namespace Artemis.Events
{
public class AudioDeviceChangedEventArgs : EventArgs
{
public AudioDeviceChangedEventArgs(MMDevice defaultPlayback, MMDevice defaultRecording)
{
DefaultPlayback = defaultPlayback;
DefaultRecording = defaultRecording;
}
public MMDevice DefaultPlayback { get; }
public MMDevice DefaultRecording { get; }
}
}

View File

@ -1,4 +1,6 @@
using Artemis.DeviceProviders; using Artemis.DeviceProviders;
using Artemis.Managers;
using Artemis.Models;
using Artemis.Modules.Abstract; using Artemis.Modules.Abstract;
using Artemis.Profiles.Layers.Interfaces; using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Types.Audio.AudioCapturing; using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
@ -8,7 +10,6 @@ using Artemis.Utilities.DataReaders;
using Artemis.Utilities.GameState; using Artemis.Utilities.GameState;
using Artemis.ViewModels; using Artemis.ViewModels;
using Artemis.ViewModels.Abstract; using Artemis.ViewModels.Abstract;
using Artemis.ViewModels.Profiles;
using Ninject.Extensions.Conventions; using Ninject.Extensions.Conventions;
using Ninject.Modules; using Ninject.Modules;
@ -18,10 +19,16 @@ namespace Artemis.InjectionModules
{ {
public override void Load() public override void Load()
{ {
#region Models
Bind<ProfileEditorModel>().ToSelf();
Bind<LayerEditorModel>().ToSelf();
#endregion
#region ViewModels #region ViewModels
Bind<ShellViewModel>().ToSelf().InSingletonScope(); Bind<ShellViewModel>().ToSelf().InSingletonScope();
Bind<ProfileViewModel>().ToSelf();
Bind<ProfileEditorViewModel>().ToSelf(); Bind<ProfileEditorViewModel>().ToSelf();
Bind<DebugViewModel>().ToSelf().InSingletonScope(); Bind<DebugViewModel>().ToSelf().InSingletonScope();
Kernel.Bind(x => Kernel.Bind(x =>
@ -98,9 +105,6 @@ namespace Artemis.InjectionModules
.InheritedFrom<ILayerType>() .InheritedFrom<ILayerType>()
.BindToSelf()); .BindToSelf());
// Type helpers
Bind<AudioCaptureManager>().ToSelf().InSingletonScope();
#endregion #endregion
#region Lua #region Lua

View File

@ -11,8 +11,10 @@ namespace Artemis.InjectionModules
Bind<LoopManager>().ToSelf().InSingletonScope(); Bind<LoopManager>().ToSelf().InSingletonScope();
Bind<DeviceManager>().ToSelf().InSingletonScope(); Bind<DeviceManager>().ToSelf().InSingletonScope();
Bind<ModuleManager>().ToSelf().InSingletonScope(); Bind<ModuleManager>().ToSelf().InSingletonScope();
Bind<ProfileManager>().ToSelf().InSingletonScope(); Bind<PreviewManager>().ToSelf().InSingletonScope();
Bind<LuaManager>().ToSelf().InSingletonScope(); Bind<LuaManager>().ToSelf().InSingletonScope();
Bind<AudioCaptureManager>().ToSelf().InSingletonScope();
Bind<MigrationManager>().ToSelf().InSingletonScope();
} }
} }
} }

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using Artemis.Events;
using Artemis.Profiles.Layers.Types.Audio;
using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
using CSCore.CoreAudioAPI;
using Ninject.Extensions.Logging;
namespace Artemis.Managers
{
public class AudioCaptureManager
{
private readonly List<AudioCapture> _audioCaptures;
private MMDevice _lastDefaultPlayback;
private MMDevice _lastDefaultRecording;
public AudioCaptureManager(ILogger logger)
{
Logger = logger;
_audioCaptures = new List<AudioCapture>();
_lastDefaultPlayback = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
_lastDefaultRecording = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);
var defaultDeviceTimer = new Timer(1000);
defaultDeviceTimer.Elapsed += DefaultDeviceTimerOnElapsed;
defaultDeviceTimer.Start();
}
public event EventHandler<AudioDeviceChangedEventArgs> AudioDeviceChanged;
private void DefaultDeviceTimerOnElapsed(object sender, ElapsedEventArgs e)
{
var defaultPlayback = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
var defaultRecording = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);
if (defaultPlayback?.DeviceID == _lastDefaultPlayback?.DeviceID &&
defaultRecording?.DeviceID == _lastDefaultRecording?.DeviceID)
return;
_lastDefaultPlayback = defaultPlayback;
_lastDefaultRecording = defaultRecording;
OnAudioDeviceChanged(new AudioDeviceChangedEventArgs(_lastDefaultPlayback, _lastDefaultRecording));
}
public AudioCapture GetAudioCapture(MMDevice device, MmDeviceType type)
{
// Return existing audio capture if found
var audioCapture = _audioCaptures.FirstOrDefault(a => a.Device.DeviceID == device.DeviceID);
if (audioCapture != null)
return audioCapture;
// Else create a new one and return that
var newAudioCapture = new AudioCapture(Logger, device, type);
_audioCaptures.Add(newAudioCapture);
return newAudioCapture;
}
public ILogger Logger { get; set; }
protected virtual void OnAudioDeviceChanged(AudioDeviceChangedEventArgs e)
{
AudioDeviceChanged?.Invoke(this, e);
}
}
}

View File

@ -3,13 +3,10 @@ using System.Drawing;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers;
using System.Windows.Media;
using Artemis.DeviceProviders; using Artemis.DeviceProviders;
using Artemis.ViewModels; using Artemis.ViewModels;
using Ninject.Extensions.Logging; using Ninject.Extensions.Logging;
using Color = System.Drawing.Color; using Color = System.Drawing.Color;
using Timer = System.Timers.Timer;
namespace Artemis.Managers namespace Artemis.Managers
{ {
@ -21,7 +18,8 @@ namespace Artemis.Managers
private readonly DebugViewModel _debugViewModel; private readonly DebugViewModel _debugViewModel;
private readonly DeviceManager _deviceManager; private readonly DeviceManager _deviceManager;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly Timer _loopTimer; //private readonly Timer _loopTimer;
private readonly Task _loopTask;
private readonly ModuleManager _moduleManager; private readonly ModuleManager _moduleManager;
public LoopManager(ILogger logger, ModuleManager moduleManager, DeviceManager deviceManager, public LoopManager(ILogger logger, ModuleManager moduleManager, DeviceManager deviceManager,
@ -33,10 +31,7 @@ namespace Artemis.Managers
_debugViewModel = debugViewModel; _debugViewModel = debugViewModel;
// Setup timers // Setup timers
_loopTimer = new Timer(40); _loopTask = Task.Factory.StartNew(ProcessLoop);
_loopTimer.Elapsed += LoopTimerOnElapsed;
_loopTimer.Start();
_logger.Info("Intialized LoopManager"); _logger.Info("Intialized LoopManager");
} }
@ -49,20 +44,30 @@ namespace Artemis.Managers
public void Dispose() public void Dispose()
{ {
_loopTimer.Stop(); _loopTask.Dispose();
_loopTimer.Dispose();
} }
private void LoopTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs) private void ProcessLoop()
{ {
try //TODO DarthAffe 14.01.2017: A stop-condition and a real cleanup instead of just aborting might be better
while (true)
{ {
Render(); try
} {
catch (Exception e) long preUpdateTicks = DateTime.Now.Ticks;
{
_logger.Warn(e, "Exception in render loop"); Render();
int sleep = (int)(40f - ((DateTime.Now.Ticks - preUpdateTicks) / 10000f));
if (sleep > 0)
Thread.Sleep(sleep);
}
catch (Exception e)
{
_logger.Warn(e, "Exception in render loop");
}
} }
// ReSharper disable once FunctionNeverReturns
} }
public Task StartAsync() public Task StartAsync()
@ -93,7 +98,7 @@ namespace Artemis.Managers
if (_moduleManager.ActiveModule == null) if (_moduleManager.ActiveModule == null)
{ {
var lastModule = _moduleManager.GetLastModule(); var lastModule = _moduleManager.GetLastModule();
if (lastModule == null) if (lastModule == null || !lastModule.Settings.IsEnabled)
{ {
_logger.Debug("Cancel LoopManager start, no module"); _logger.Debug("Cancel LoopManager start, no module");
return; return;
@ -152,10 +157,11 @@ namespace Artemis.Managers
var headsets = _deviceManager.HeadsetProviders.Where(m => m.CanUse).ToList(); var headsets = _deviceManager.HeadsetProviders.Where(m => m.CanUse).ToList();
var generics = _deviceManager.GenericProviders.Where(m => m.CanUse).ToList(); var generics = _deviceManager.GenericProviders.Where(m => m.CanUse).ToList();
var mousemats = _deviceManager.MousematProviders.Where(m => m.CanUse).ToList(); var mousemats = _deviceManager.MousematProviders.Where(m => m.CanUse).ToList();
var keyboardOnly = !mice.Any() && !headsets.Any() && !generics.Any() && !mousemats.Any(); var keyboardOnly = !mice.Any() && !headsets.Any() && !generics.Any() && !mousemats.Any();
// Setup the frame for this tick // Setup the frame for this tick
using (var frame = new RenderFrame(_deviceManager.ActiveKeyboard)) using (var frame = new RenderFrame(_deviceManager.ActiveKeyboard, mice.Any(), headsets.Any(), generics.Any(), mousemats.Any()))
{ {
if (renderModule.IsInitialized) if (renderModule.IsInitialized)
renderModule.Render(frame, keyboardOnly); renderModule.Render(frame, keyboardOnly);
@ -197,45 +203,53 @@ namespace Artemis.Managers
public class RenderFrame : IDisposable public class RenderFrame : IDisposable
{ {
public RenderFrame(KeyboardProvider keyboard) public RenderFrame(KeyboardProvider keyboard, bool renderMice, bool renderHeadsets, bool renderGenerics, bool renderMousemats)
{ {
if (keyboard == null) if (keyboard == null)
return; return;
KeyboardBitmap = keyboard.KeyboardBitmap(4); KeyboardBitmap = keyboard.KeyboardBitmap();
KeyboardBitmap.SetResolution(96, 96); KeyboardBitmap.SetResolution(96, 96);
MouseBitmap = new Bitmap(40, 40);
MouseBitmap.SetResolution(96, 96);
HeadsetBitmap = new Bitmap(40, 40);
HeadsetBitmap.SetResolution(96, 96);
GenericBitmap = new Bitmap(40, 40);
GenericBitmap.SetResolution(96, 96);
MousematBitmap = new Bitmap(40, 40);
MousematBitmap.SetResolution(96, 96);
using (var g = Graphics.FromImage(KeyboardBitmap)) using (var g = Graphics.FromImage(KeyboardBitmap))
{ {
g.Clear(Color.Black); g.Clear(Color.Black);
} }
using (var g = Graphics.FromImage(MouseBitmap))
if (renderMice)
{ {
g.Clear(Color.Black); MouseBitmap = new Bitmap(10, 10);
MouseBitmap.SetResolution(96, 96);
using (var g = Graphics.FromImage(MouseBitmap))
{
g.Clear(Color.Black);
}
} }
using (var g = Graphics.FromImage(HeadsetBitmap)) if (renderHeadsets)
{ {
g.Clear(Color.Black); HeadsetBitmap = new Bitmap(10, 10);
HeadsetBitmap.SetResolution(96, 96);
using (var g = Graphics.FromImage(HeadsetBitmap))
{
g.Clear(Color.Black);
}
} }
using (var g = Graphics.FromImage(GenericBitmap)) if (renderGenerics)
{ {
g.Clear(Color.Black); GenericBitmap = new Bitmap(10, 10);
GenericBitmap.SetResolution(96, 96);
using (var g = Graphics.FromImage(GenericBitmap))
{
g.Clear(Color.Black);
}
} }
using (var g = Graphics.FromImage(MousematBitmap)) if (renderMousemats)
{ {
g.Clear(Color.Black); MousematBitmap = new Bitmap(10, 10);
MousematBitmap.SetResolution(96, 96);
using (var g = Graphics.FromImage(MousematBitmap))
{
g.Clear(Color.Black);
}
} }
} }

View File

@ -19,17 +19,19 @@ namespace Artemis.Managers
/// </summary> /// </summary>
public class MainManager : IDisposable public class MainManager : IDisposable
{ {
private readonly MigrationManager _migrationManager;
private readonly Timer _processTimer; private readonly Timer _processTimer;
public MainManager(ILogger logger, LoopManager loopManager, DeviceManager deviceManager, public MainManager(ILogger logger, LoopManager loopManager, DeviceManager deviceManager,
ModuleManager moduleManager, ProfileManager profileManager, PipeServer pipeServer, ModuleManager moduleManager, PreviewManager previewManager, MigrationManager migrationManager,
GameStateWebServer gameStateWebServer) PipeServer pipeServer, GameStateWebServer gameStateWebServer)
{ {
_migrationManager = migrationManager;
Logger = logger; Logger = logger;
LoopManager = loopManager; LoopManager = loopManager;
DeviceManager = deviceManager; DeviceManager = deviceManager;
ModuleManager = moduleManager; ModuleManager = moduleManager;
ProfileManager = profileManager; PreviewManager = previewManager;
PipeServer = pipeServer; PipeServer = pipeServer;
_processTimer = new Timer(1000); _processTimer = new Timer(1000);
@ -64,7 +66,7 @@ namespace Artemis.Managers
public LoopManager LoopManager { get; } public LoopManager LoopManager { get; }
public DeviceManager DeviceManager { get; set; } public DeviceManager DeviceManager { get; set; }
public ModuleManager ModuleManager { get; set; } public ModuleManager ModuleManager { get; set; }
public ProfileManager ProfileManager { get; set; } public PreviewManager PreviewManager { get; set; }
public PipeServer PipeServer { get; set; } public PipeServer PipeServer { get; set; }
public GameStateWebServer GameStateWebServer { get; set; } public GameStateWebServer GameStateWebServer { get; set; }
@ -105,11 +107,13 @@ namespace Artemis.Managers
/// <summary> /// <summary>
/// Loads the last active effect and starts the program /// Loads the last active effect and starts the program
/// </summary> /// </summary>
public void EnableProgram() public async void EnableProgram()
{ {
Logger.Debug("Enabling program"); Logger.Debug("Enabling program");
ProgramEnabled = true; ProgramEnabled = true;
LoopManager.StartAsync(); await LoopManager.StartAsync();
_migrationManager.MigrateProfiles();
RaiseEnabledChangedEvent(new EnabledChangedEventArgs(ProgramEnabled)); RaiseEnabledChangedEvent(new EnabledChangedEventArgs(ProgramEnabled));
} }
@ -119,6 +123,9 @@ namespace Artemis.Managers
public void DisableProgram() public void DisableProgram()
{ {
Logger.Debug("Disabling program"); Logger.Debug("Disabling program");
foreach (var overlayModule in ModuleManager.OverlayModules)
if (overlayModule.Settings.IsEnabled)
overlayModule.Dispose();
LoopManager.Stop(); LoopManager.Stop();
ProgramEnabled = false; ProgramEnabled = false;
RaiseEnabledChangedEvent(new EnabledChangedEventArgs(ProgramEnabled)); RaiseEnabledChangedEvent(new EnabledChangedEventArgs(ProgramEnabled));
@ -148,7 +155,7 @@ namespace Artemis.Managers
ModuleManager.DisableProcessBoundModule(); ModuleManager.DisableProcessBoundModule();
// If the currently active effect is a no longer running game, get rid of it. // If the currently active effect is a no longer running game, get rid of it.
if (!processes.Any(p => p.ProcessName == module.ProcessName && p.HasExited == false)) if (!processes.Any(p => module.ProcessNames.Contains(p.ProcessName) && !p.HasExited))
{ {
Logger.Info("Disabling process bound module because process stopped: {0}", module.Name); Logger.Info("Disabling process bound module because process stopped: {0}", module.Name);
ModuleManager.DisableProcessBoundModule(); ModuleManager.DisableProcessBoundModule();
@ -157,7 +164,7 @@ namespace Artemis.Managers
// Look for running games, stopping on the first one that's found. // Look for running games, stopping on the first one that's found.
var newModule = ModuleManager.ProcessModules.Where(g => g.Settings.IsEnabled && g.Settings.IsEnabled) var newModule = ModuleManager.ProcessModules.Where(g => g.Settings.IsEnabled && g.Settings.IsEnabled)
.FirstOrDefault(g => processes.Any(p => p.ProcessName == g.ProcessName && p.HasExited == false)); .FirstOrDefault(g => processes.Any(p => g.ProcessNames.Contains(p.ProcessName) && !p.HasExited));
if (newModule == null || module == newModule) if (newModule == null || module == newModule)
return; return;

View File

@ -0,0 +1,40 @@
using System.IO;
using Artemis.DAL;
namespace Artemis.Managers
{
public class MigrationManager
{
private readonly DeviceManager _deviceManager;
public MigrationManager(DeviceManager deviceManager)
{
_deviceManager = deviceManager;
}
/// <summary>
/// Migrates old versions of profiles to new versions
/// </summary>
public void MigrateProfiles()
{
// 1.8.0.0 - Rename WindowsProfile to GeneralProfile
foreach (var keyboardProvider in _deviceManager.KeyboardProviders)
{
var folder = ProfileProvider.ProfileFolder + "/" + keyboardProvider.Slug + "/WindowsProfile";
if (!Directory.Exists(folder))
continue;
// Get all the profiles
var profiles = ProfileProvider.ReadProfiles(keyboardProvider.Slug + "/WindowsProfile");
foreach (var profile in profiles)
{
// Change their GameName and save, effectively moving them to the new folder
profile.GameName = "GeneralProfile";
ProfileProvider.AddOrUpdate(profile);
}
// Delete the old profiles
Directory.Delete(folder, true);
}
}
}
}

View File

@ -169,6 +169,8 @@ namespace Artemis.Managers
/// </summary> /// </summary>
public void DisableProcessBoundModule() public void DisableProcessBoundModule()
{ {
if (ActiveModule == null)
return;
if (!ActiveModule.IsBoundToProcess) if (!ActiveModule.IsBoundToProcess)
{ {
_logger.Warn("Active module {0} is not process bound but is being disabled as if it is.", _logger.Warn("Active module {0} is not process bound but is being disabled as if it is.",
@ -176,10 +178,11 @@ namespace Artemis.Managers
return; return;
} }
if (GetLastModule() == null) var lastModule = GetLastModule();
if (lastModule == null || !lastModule.Settings.IsEnabled)
ClearActiveModule(); ClearActiveModule();
else else
ChangeActiveModule(GetLastModule()); ChangeActiveModule(lastModule);
} }
protected virtual void RaiseEffectChangedEvent(ModuleChangedEventArgs e) protected virtual void RaiseEffectChangedEvent(ModuleChangedEventArgs e)

View File

@ -9,7 +9,7 @@ using Ninject.Extensions.Logging;
namespace Artemis.Managers namespace Artemis.Managers
{ {
public class ProfileManager public class PreviewManager
{ {
private readonly DeviceManager _deviceManager; private readonly DeviceManager _deviceManager;
private readonly GeneralSettings _generalSettings; private readonly GeneralSettings _generalSettings;
@ -17,7 +17,7 @@ namespace Artemis.Managers
private readonly LoopManager _loopManager; private readonly LoopManager _loopManager;
private readonly ModuleManager _moduleManager; private readonly ModuleManager _moduleManager;
public ProfileManager(ILogger logger, ModuleManager moduleManager, DeviceManager deviceManager, public PreviewManager(ILogger logger, ModuleManager moduleManager, DeviceManager deviceManager,
LoopManager loopManager) LoopManager loopManager)
{ {
_logger = logger; _logger = logger;
@ -32,7 +32,7 @@ namespace Artemis.Managers
profilePreviewTimer.Elapsed += SetupProfilePreview; profilePreviewTimer.Elapsed += SetupProfilePreview;
profilePreviewTimer.Start(); profilePreviewTimer.Start();
_logger.Info("Intialized ProfileManager"); _logger.Info("Intialized PreviewManager");
} }
public List<ModuleViewModel> PreviewViewModules { get; set; } public List<ModuleViewModel> PreviewViewModules { get; set; }
@ -76,8 +76,13 @@ namespace Artemis.Managers
return; return;
_logger.Debug("Deactivate profile preview"); _logger.Debug("Deactivate profile preview");
// Save the profile the editor was using
var activePreview = PreviewViewModules.FirstOrDefault(p => p.IsModuleActive);
activePreview?.ProfileEditor?.SaveSelectedProfile();
var lastModule = _moduleManager.GetLastModule(); var lastModule = _moduleManager.GetLastModule();
if (lastModule != null) if (lastModule != null && lastModule.Settings.IsEnabled)
_moduleManager.ChangeActiveModule(lastModule); _moduleManager.ChangeActiveModule(lastModule);
else else
_moduleManager.ClearActiveModule(); _moduleManager.ClearActiveModule();

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Artemis.Models
{
public class LayerEditorModel
{
}
}

View File

@ -0,0 +1,367 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Artemis.DAL;
using Artemis.Managers;
using Artemis.Modules.Abstract;
using Artemis.Profiles;
using Artemis.Profiles.Layers.Models;
using Artemis.Profiles.Layers.Types.Folder;
using Artemis.Properties;
using Artemis.Services;
using Artemis.Utilities;
using Artemis.ViewModels;
using Ninject.Parameters;
namespace Artemis.Models
{
public class ProfileEditorModel : IDisposable
{
private readonly DeviceManager _deviceManager;
private readonly LuaManager _luaManager;
private readonly DialogService _dialogService;
private readonly WindowService _windowService;
private FileSystemWatcher _watcher;
private ModuleModel _luaModule;
public ProfileEditorModel(WindowService windowService, MetroDialogService dialogService,
DeviceManager deviceManager, LuaManager luaManager)
{
_windowService = windowService;
_dialogService = dialogService;
_deviceManager = deviceManager;
_luaManager = luaManager;
}
#region Layers
/// <summary>
/// Opens a new LayerEditorView for the given layer
/// </summary>
/// <param name="layer">The layer to open the view for</param>
/// <param name="dataModel">The datamodel to bind the editor to</param>
public void EditLayer(LayerModel layer, ModuleDataModel dataModel)
{
IParameter[] args =
{
new ConstructorArgument("dataModel", dataModel),
new ConstructorArgument("layer", layer)
};
_windowService.ShowDialog<LayerEditorViewModel>(args);
// If the layer was a folder, but isn't anymore, assign it's children to it's parent.
if (layer.LayerType is FolderType || !layer.Children.Any())
return;
while (layer.Children.Any())
{
var child = layer.Children[0];
layer.Children.Remove(child);
if (layer.Parent != null)
{
layer.Parent.Children.Add(child);
layer.Parent.FixOrder();
}
else
{
layer.Profile.Layers.Add(child);
layer.Profile.FixOrder();
}
}
}
/// <summary>
/// Removes the given layer from the profile
/// </summary>
/// <param name="layer">The layer to remove</param>
/// <param name="profileModel">The profile to remove it from</param>
public void RemoveLayer(LayerModel layer, ProfileModel profileModel)
{
if (layer == null)
return;
if (layer.Parent != null)
{
var parent = layer.Parent;
layer.Parent.Children.Remove(layer);
parent.FixOrder();
}
else if (layer.Profile != null)
{
var profile = layer.Profile;
layer.Profile.Layers.Remove(layer);
profile.FixOrder();
}
// Extra cleanup in case of a wonky layer that has no parent
if (profileModel.Layers.Contains(layer))
profileModel.Layers.Remove(layer);
}
#endregion
#region Profiles
public async Task<ProfileModel> AddProfile(ModuleModel moduleModel)
{
if (_deviceManager.ActiveKeyboard == null)
{
_dialogService.ShowMessageBox("Cannot add profile.",
"To add a profile, please select a keyboard in the options menu first.");
return null;
}
var name = await GetValidProfileName("Name profile", "Please enter a unique name for your new profile");
// User cancelled
if (name == null)
return null;
var profile = new ProfileModel
{
Name = name,
KeyboardSlug = _deviceManager.ActiveKeyboard.Slug,
Width = _deviceManager.ActiveKeyboard.Width,
Height = _deviceManager.ActiveKeyboard.Height,
GameName = moduleModel.Name
};
if (!ProfileProvider.IsProfileUnique(profile))
{
var overwrite = await _dialogService.ShowQuestionMessageBox("Overwrite existing profile",
"A profile with this name already exists for this game. Would you like to overwrite it?");
if (!overwrite.Value)
return null;
}
ProfileProvider.AddOrUpdate(profile);
return profile;
}
public async Task RenameProfile(ProfileModel profileModel)
{
var name = await GetValidProfileName("Rename profile", "Please enter a unique new profile name");
// User cancelled
if (name == null)
return;
var doRename = await MakeProfileUnique(profileModel, name, profileModel.Name);
if (!doRename)
return;
ProfileProvider.RenameProfile(profileModel, profileModel.Name);
}
public async Task<ProfileModel> DuplicateProfile(ProfileModel selectedProfile)
{
var newProfile = GeneralHelpers.Clone(selectedProfile);
var name = await GetValidProfileName("Duplicate profile", "Please enter a unique new profile name");
// User cancelled
if (name == null)
return null;
var doRename = await MakeProfileUnique(newProfile, name, newProfile.Name);
if (!doRename)
return null;
// Make sure it's not default, in case of copying a default profile
newProfile.IsDefault = false;
ProfileProvider.AddOrUpdate(newProfile);
return newProfile;
}
public async Task<bool> DeleteProfile(ProfileModel selectedProfile, ModuleModel moduleModel)
{
var confirm = await _dialogService.ShowQuestionMessageBox("Delete profile",
$"Are you sure you want to delete the profile named: {selectedProfile.Name}?\n\n" +
"This cannot be undone.");
if (!confirm.Value)
return false;
var defaultProfile = ProfileProvider.GetProfile(_deviceManager.ActiveKeyboard, moduleModel, "Default");
var deleteProfile = selectedProfile;
moduleModel.ChangeProfile(defaultProfile);
ProfileProvider.DeleteProfile(deleteProfile);
return true;
}
public async Task<ProfileModel> ImportProfile(ModuleModel moduleModel)
{
var dialog = new OpenFileDialog {Filter = "Artemis profile (*.json)|*.json"};
var result = dialog.ShowDialog();
if (result != DialogResult.OK)
return null;
var profileModel = ProfileProvider.LoadProfileIfValid(dialog.FileName);
if (profileModel == null)
{
_dialogService.ShowErrorMessageBox("Oh noes, the profile you provided is invalid. " +
"If this keeps happening, please make an issue on GitHub and provide the profile.");
return null;
}
// Verify the game
if (profileModel.GameName != moduleModel.Name)
{
_dialogService.ShowErrorMessageBox(
$"Oh oops! This profile is ment for {profileModel.GameName}, not {moduleModel.Name} :c");
return null;
}
// Verify the keyboard
var deviceManager = _deviceManager;
if (profileModel.KeyboardSlug != deviceManager.ActiveKeyboard.Slug)
{
var adjustKeyboard = await _dialogService.ShowQuestionMessageBox("Profile not made for this keyboard",
$"Watch out, this profile wasn't ment for this keyboard, but for the {profileModel.KeyboardSlug}. " +
"You can still import it but you'll probably have to do some adjusting\n\n" +
"Continue?");
if (!adjustKeyboard.Value)
return null;
// Resize layers that are on the full keyboard width
profileModel.ResizeLayers(deviceManager.ActiveKeyboard);
// Put layers back into the canvas if they fell outside it
profileModel.FixBoundaries(deviceManager.ActiveKeyboard.KeyboardRectangle(1));
// Setup profile metadata to match the new keyboard
profileModel.KeyboardSlug = deviceManager.ActiveKeyboard.Slug;
profileModel.Width = deviceManager.ActiveKeyboard.Width;
profileModel.Height = deviceManager.ActiveKeyboard.Height;
}
var name = await GetValidProfileName("Rename profile", "Please enter a unique new profile name");
// User cancelled
if (name == null)
return null;
var doRename = await MakeProfileUnique(profileModel, name, profileModel.Name);
if (!doRename)
return null;
profileModel.IsDefault = false;
ProfileProvider.AddOrUpdate(profileModel);
return profileModel;
}
public void ChangeProfileByName(ModuleModel moduleModel, string profileName)
{
if (string.IsNullOrEmpty(profileName))
profileName = "Default";
moduleModel.ChangeProfile(ProfileProvider.GetProfile(_deviceManager.ActiveKeyboard, moduleModel, profileName));
}
private async Task<string> GetValidProfileName(string title, string text)
{
var name = await _dialogService.ShowInputDialog(title, text);
// Null when the user cancelled
if (name == null)
return null;
if (name.Length >= 2)
return name;
_dialogService.ShowMessageBox("Invalid profile name",
"Please provide a valid profile name that's longer than 2 symbols");
return await GetValidProfileName(title, text);
}
private async Task<bool> MakeProfileUnique(ProfileModel profileModel, string name, string oldName)
{
profileModel.Name = name;
if (ProfileProvider.IsProfileUnique(profileModel))
return true;
name = await GetValidProfileName("Rename profile", "Please enter a unique new profile name");
if (name != null)
return await MakeProfileUnique(profileModel, name, oldName);
// If cancelled, restore old name and stop
profileModel.Name = oldName;
return false;
}
#endregion
#region LUA
public void OpenLuaEditor(ModuleModel moduleModel)
{
// Clean up old environment
DisposeLuaWatcher();
// Create a temp file
var fileName = Guid.NewGuid() + ".lua";
var file = File.Create(Path.GetTempPath() + fileName);
file.Dispose();
// Add instructions to LUA script if it's a new file
if (string.IsNullOrEmpty(moduleModel.ProfileModel.LuaScript))
moduleModel.ProfileModel.LuaScript = Encoding.UTF8.GetString(Resources.lua_placeholder);
File.WriteAllText(Path.GetTempPath() + fileName, moduleModel.ProfileModel.LuaScript);
// Watch the file for changes
_luaModule = moduleModel;
_watcher = new FileSystemWatcher(Path.GetTempPath(), fileName);
_watcher.Changed += LuaFileChanged;
_watcher.EnableRaisingEvents = true;
_watcher.Path = Path.GetTempPath();
_watcher.Filter = fileName;
// Open the temp file with the default editor
System.Diagnostics.Process.Start(Path.GetTempPath() + fileName);
}
private void LuaFileChanged(object sender, FileSystemEventArgs args)
{
if (_luaModule == null)
{
DisposeLuaWatcher();
return;
}
if (args.ChangeType != WatcherChangeTypes.Changed)
return;
lock (_luaModule)
{
using (var fs = new FileStream(args.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var sr = new StreamReader(fs))
{
_luaModule.ProfileModel.LuaScript = sr.ReadToEnd();
}
}
ProfileProvider.AddOrUpdate(_luaModule.ProfileModel);
_luaManager.SetupLua(_luaModule.ProfileModel);
}
}
private void DisposeLuaWatcher()
{
if (_watcher == null) return;
_watcher.Changed -= LuaFileChanged;
_watcher.Dispose();
_watcher = null;
}
public void Dispose()
{
DisposeLuaWatcher();
}
#endregion
#region Rendering
#endregion
}
}

View File

@ -79,15 +79,17 @@ namespace Artemis.Modules.Abstract
public List<LayerModel> PreviewLayers { get; set; } public List<LayerModel> PreviewLayers { get; set; }
/// <summary> /// <summary>
/// The process the module is bound to /// The processes the module is bound to
/// </summary> /// </summary>
public string ProcessName { get; protected set; } public List<string> ProcessNames { get; protected set; } = new List<string>();
/// <summary> /// <summary>
/// The currently active profile of the module /// The currently active profile of the module
/// </summary> /// </summary>
public ProfileModel ProfileModel { get; protected set; } public ProfileModel ProfileModel { get; protected set; }
public bool IsGeneral => !IsOverlay && !IsBoundToProcess;
#endregion #endregion
#region Base methods #region Base methods
@ -98,7 +100,13 @@ namespace Artemis.Modules.Abstract
return; return;
ProfileModel = profileModel; ProfileModel = profileModel;
ProfileModel?.Activate(_luaManager); if (!IsOverlay)
ProfileModel?.Activate(_luaManager);
if (ProfileModel != null)
{
Settings.LastProfile = ProfileModel.Name;
Settings.Save();
}
RaiseProfileChangedEvent(new ProfileChangedEventArgs(ProfileModel)); RaiseProfileChangedEvent(new ProfileChangedEventArgs(ProfileModel));
} }
@ -157,25 +165,37 @@ namespace Artemis.Modules.Abstract
ProfileModel?.DrawLayers(g, layers, DrawType.Keyboard, DataModel, keyboardRect, preview); ProfileModel?.DrawLayers(g, layers, DrawType.Keyboard, DataModel, keyboardRect, preview);
} }
// Render mice layer-by-layer // Render mice layer-by-layer
var devRec = new Rect(0, 0, 40, 40); var devRec = new Rect(0, 0, 10, 10);
using (var g = Graphics.FromImage(frame.MouseBitmap)) if (frame.MouseBitmap != null)
{ {
ProfileModel?.DrawLayers(g, layers, DrawType.Mouse, DataModel, devRec, preview); using (var g = Graphics.FromImage(frame.MouseBitmap))
{
ProfileModel?.DrawLayers(g, layers, DrawType.Mouse, DataModel, devRec, preview);
}
} }
// Render headsets layer-by-layer // Render headsets layer-by-layer
using (var g = Graphics.FromImage(frame.HeadsetBitmap)) if (frame.HeadsetBitmap != null)
{ {
ProfileModel?.DrawLayers(g, layers, DrawType.Headset, DataModel, devRec, preview); using (var g = Graphics.FromImage(frame.HeadsetBitmap))
{
ProfileModel?.DrawLayers(g, layers, DrawType.Headset, DataModel, devRec, preview);
}
} }
// Render generic devices layer-by-layer // Render generic devices layer-by-layer
using (var g = Graphics.FromImage(frame.GenericBitmap)) if (frame.GenericBitmap != null)
{ {
ProfileModel?.DrawLayers(g, layers, DrawType.Generic, DataModel, devRec, preview); using (var g = Graphics.FromImage(frame.GenericBitmap))
{
ProfileModel?.DrawLayers(g, layers, DrawType.Generic, DataModel, devRec, preview);
}
} }
// Render mousemats layer-by-layer // Render mousemats layer-by-layer
using (var g = Graphics.FromImage(frame.MousematBitmap)) if (frame.MousematBitmap != null)
{ {
ProfileModel?.DrawLayers(g, layers, DrawType.Mousemat, DataModel, devRec, preview); using (var g = Graphics.FromImage(frame.MousematBitmap))
{
ProfileModel?.DrawLayers(g, layers, DrawType.Mousemat, DataModel, devRec, preview);
}
} }
// Trace debugging // Trace debugging
@ -185,6 +205,10 @@ namespace Artemis.Modules.Abstract
_lastTrace = DateTime.Now; _lastTrace = DateTime.Now;
var dmJson = JsonConvert.SerializeObject(DataModel, Formatting.Indented); var dmJson = JsonConvert.SerializeObject(DataModel, Formatting.Indented);
Logger.Trace("Effect datamodel as JSON: \r\n{0}", dmJson); Logger.Trace("Effect datamodel as JSON: \r\n{0}", dmJson);
if (layers == null)
return;
Logger.Trace("Effect {0} has to render {1} layers", Name, layers.Count); Logger.Trace("Effect {0} has to render {1} layers", Name, layers.Count);
foreach (var renderLayer in layers) foreach (var renderLayer in layers)
Logger.Trace("- Layer name: {0}, layer type: {1}", renderLayer.Name, renderLayer.LayerType); Logger.Trace("- Layer name: {0}, layer type: {1}", renderLayer.Name, renderLayer.LayerType);

View File

@ -1,8 +1,9 @@
using Artemis.Events; using Artemis.DAL;
using Artemis.Events;
using Artemis.Managers; using Artemis.Managers;
using Artemis.Services; using Artemis.Services;
using Artemis.Settings; using Artemis.Settings;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels;
using Caliburn.Micro; using Caliburn.Micro;
using Ninject; using Ninject;
using Ninject.Extensions.Logging; using Ninject.Extensions.Logging;
@ -12,24 +13,33 @@ namespace Artemis.Modules.Abstract
{ {
public abstract class ModuleViewModel : Screen public abstract class ModuleViewModel : Screen
{ {
private readonly ModuleManager _moduleManager;
private readonly MainManager _mainManager; private readonly MainManager _mainManager;
private readonly IKernel _kernel; private readonly ModuleManager _moduleManager;
private readonly GeneralSettings _generalSettings;
private ModuleSettings _settings; private ModuleSettings _settings;
private GeneralSettings _generalSettings;
public ModuleViewModel(MainManager mainManager, ModuleModel moduleModel, IKernel kernel) public ModuleViewModel(MainManager mainManager, ModuleModel moduleModel, IKernel kernel)
{ {
_mainManager = mainManager; _mainManager = mainManager;
_kernel = kernel;
_moduleManager = mainManager.ModuleManager; _moduleManager = mainManager.ModuleManager;
_generalSettings = DAL.SettingsProvider.Load<GeneralSettings>(); _generalSettings = SettingsProvider.Load<GeneralSettings>();
ModuleModel = moduleModel; ModuleModel = moduleModel;
Settings = moduleModel.Settings; Settings = moduleModel.Settings;
_mainManager.EnabledChanged += MainManagerOnEnabledChanged; _mainManager.EnabledChanged += MainManagerOnEnabledChanged;
_moduleManager.EffectChanged += ModuleManagerOnModuleChanged; _moduleManager.EffectChanged += ModuleManagerOnModuleChanged;
// ReSharper disable once VirtualMemberCallInConstructor
if (!UsesProfileEditor)
return;
IParameter[] args =
{
new ConstructorArgument("mainManager", _mainManager),
new ConstructorArgument("moduleModel", ModuleModel),
new ConstructorArgument("lastProfile", Settings.LastProfile)
};
ProfileEditor = kernel.Get<ProfileEditorViewModel>(args);
} }
public ProfileEditorViewModel ProfileEditor { get; set; } public ProfileEditorViewModel ProfileEditor { get; set; }
@ -58,7 +68,7 @@ namespace Artemis.Modules.Abstract
{ {
get get
{ {
if (ModuleModel.IsBoundToProcess || ModuleModel.IsBoundToProcess) if (!ModuleModel.IsGeneral)
return Settings.IsEnabled; return Settings.IsEnabled;
return _generalSettings.LastModule == ModuleModel.Name; return _generalSettings.LastModule == ModuleModel.Name;
} }
@ -81,7 +91,7 @@ namespace Artemis.Modules.Abstract
private void UpdatedEnabledSetting() private void UpdatedEnabledSetting()
{ {
if (ModuleModel.IsBoundToProcess || ModuleModel.IsOverlay) if (!ModuleModel.IsGeneral || (_moduleManager.ActiveModule != null && !_moduleManager.ActiveModule.IsGeneral || Settings.IsEnabled == IsModuleActive))
return; return;
Settings.IsEnabled = IsModuleActive; Settings.IsEnabled = IsModuleActive;
@ -96,7 +106,7 @@ namespace Artemis.Modules.Abstract
NotifyOfPropertyChange(() => Settings); NotifyOfPropertyChange(() => Settings);
// On process-bound modules, only set the module model // On process-bound modules, only set the module model
if (ModuleModel.IsBoundToProcess || ModuleModel.IsOverlay) if (!ModuleModel.IsGeneral)
{ {
NotifyOfPropertyChange(() => IsModuleActive); NotifyOfPropertyChange(() => IsModuleActive);
NotifyOfPropertyChange(() => IsModuleEnabled); NotifyOfPropertyChange(() => IsModuleEnabled);
@ -142,24 +152,13 @@ namespace Artemis.Modules.Abstract
protected override void OnActivate() protected override void OnActivate()
{ {
base.OnActivate(); base.OnActivate();
ProfileEditor?.OnActivate();
if (!UsesProfileEditor)
return;
IParameter[] args =
{
new ConstructorArgument("mainManager", _mainManager),
new ConstructorArgument("moduleModel", ModuleModel),
new ConstructorArgument("lastProfile", Settings.LastProfile)
};
ProfileEditor = _kernel.Get<ProfileEditorViewModel>(args);
} }
protected override void OnDeactivate(bool close) protected override void OnDeactivate(bool close)
{ {
base.OnDeactivate(close); base.OnDeactivate(close);
ProfileEditor?.Dispose(); ProfileEditor?.OnDeactivate(close);
ProfileEditor = null;
} }
} }
} }

View File

@ -28,7 +28,7 @@ namespace Artemis.Modules.Games.CounterStrike
Settings = SettingsProvider.Load<CounterStrikeSettings>(); Settings = SettingsProvider.Load<CounterStrikeSettings>();
DataModel = new CounterStrikeDataModel(); DataModel = new CounterStrikeDataModel();
ProcessName = "csgo"; ProcessNames.Add("csgo");
FindGameDir(); FindGameDir();
PlaceConfigFile(); PlaceConfigFile();

View File

@ -48,7 +48,7 @@
cal:Message.Attach="[Event LostFocus] = [Action PlaceConfigFile]" /> cal:Message.Attach="[Event LostFocus] = [Action PlaceConfigFile]" />
<Button x:Name="BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944" <Button x:Name="BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944"
HorizontalAlignment="Right" Width="25" HorizontalAlignment="Right" Width="25"
Style="{DynamicResource SquareButtonStyle}" Height="26" Margin="0,-2,0,0" /> Style="{DynamicResource SquareButtonStyle}" Height="26" />
</Grid> </Grid>
</StackPanel> </StackPanel>

View File

@ -24,7 +24,7 @@ namespace Artemis.Modules.Games.Dota2
Settings = SettingsProvider.Load<Dota2Settings>(); Settings = SettingsProvider.Load<Dota2Settings>();
DataModel = new Dota2DataModel(); DataModel = new Dota2DataModel();
ProcessName = "dota2"; ProcessNames.Add("dota2");
FindGameDir(); FindGameDir();
PlaceConfigFile(); PlaceConfigFile();

View File

@ -47,7 +47,7 @@
cal:Message.Attach="[Event LostFocus] = [Action PlaceConfigFile]" /> cal:Message.Attach="[Event LostFocus] = [Action PlaceConfigFile]" />
<Button x:Name="BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944" <Button x:Name="BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944"
HorizontalAlignment="Right" Width="25" HorizontalAlignment="Right" Width="25"
Style="{DynamicResource SquareButtonStyle}" Height="26" Margin="0,-2,0,0" /> Style="{DynamicResource SquareButtonStyle}" Height="26" />
</Grid> </Grid>
</StackPanel> </StackPanel>

View File

@ -17,10 +17,17 @@ namespace Artemis.Modules.Games.EurotruckSimulator2
UserData.RegisterType<IEts2Truck>(); UserData.RegisterType<IEts2Truck>();
} }
public TruckSimulatorGameName GameName { get; set; }
public IEts2Game Game { get; set; } public IEts2Game Game { get; set; }
public IEts2Job Job { get; set; } public IEts2Job Job { get; set; }
public IEts2Navigation Navigation { get; set; } public IEts2Navigation Navigation { get; set; }
public IEts2Trailer Trailer { get; set; } public IEts2Trailer Trailer { get; set; }
public IEts2Truck Truck { get; set; } public IEts2Truck Truck { get; set; }
public enum TruckSimulatorGameName
{
EuroTruckSimulator2,
AmericanTruckSimulator
}
} }
} }

View File

@ -7,6 +7,8 @@ using Artemis.Modules.Games.EurotruckSimulator2.Data;
using Artemis.Properties; using Artemis.Properties;
using Artemis.Services; using Artemis.Services;
using Artemis.Utilities; using Artemis.Utilities;
using Artemis.Utilities.Memory;
using static Artemis.Modules.Games.EurotruckSimulator2.EurotruckSimulator2DataModel;
namespace Artemis.Modules.Games.EurotruckSimulator2 namespace Artemis.Modules.Games.EurotruckSimulator2
{ {
@ -22,9 +24,11 @@ namespace Artemis.Modules.Games.EurotruckSimulator2
Settings = SettingsProvider.Load<EurotruckSimulator2Settings>(); Settings = SettingsProvider.Load<EurotruckSimulator2Settings>();
DataModel = new EurotruckSimulator2DataModel(); DataModel = new EurotruckSimulator2DataModel();
ProcessName = "eurotrucks2"; ProcessNames.Add("eurotrucks2");
ProcessNames.Add("amtrucks");
FindGameDir(); FindEts2GameDir();
FindAtsGameDir();
} }
public override string Name => "EurotruckSimulator2"; public override string Name => "EurotruckSimulator2";
@ -33,9 +37,18 @@ namespace Artemis.Modules.Games.EurotruckSimulator2
public override void Update() public override void Update()
{ {
var etsProcess = MemoryHelpers.GetProcessIfRunning("eurotrucks2");
var atsProcess = MemoryHelpers.GetProcessIfRunning("amtrucks");
if (etsProcess == null && atsProcess == null)
return;
var dataModel = (EurotruckSimulator2DataModel) DataModel; var dataModel = (EurotruckSimulator2DataModel) DataModel;
var telemetryData = Ets2TelemetryDataReader.Instance.Read(); var telemetryData = Ets2TelemetryDataReader.Instance.Read();
dataModel.GameName = etsProcess != null
? TruckSimulatorGameName.EuroTruckSimulator2
: TruckSimulatorGameName.AmericanTruckSimulator;
dataModel.Game = telemetryData.Game; dataModel.Game = telemetryData.Game;
dataModel.Job = telemetryData.Job; dataModel.Job = telemetryData.Job;
dataModel.Navigation = telemetryData.Navigation; dataModel.Navigation = telemetryData.Navigation;
@ -43,7 +56,7 @@ namespace Artemis.Modules.Games.EurotruckSimulator2
dataModel.Truck = telemetryData.Truck; dataModel.Truck = telemetryData.Truck;
} }
public void FindGameDir() public void FindEts2GameDir()
{ {
// Demo is also supported but resides in a different directory, the full game can also be 64-bits // Demo is also supported but resides in a different directory, the full game can also be 64-bits
var dir = GeneralHelpers.FindSteamGame(@"\Euro Truck Simulator 2\bin\win_x64\eurotrucks2.exe") ?? var dir = GeneralHelpers.FindSteamGame(@"\Euro Truck Simulator 2\bin\win_x64\eurotrucks2.exe") ??
@ -53,19 +66,34 @@ namespace Artemis.Modules.Games.EurotruckSimulator2
if (string.IsNullOrEmpty(dir)) if (string.IsNullOrEmpty(dir))
return; return;
((EurotruckSimulator2Settings) Settings).GameDirectory = dir; ((EurotruckSimulator2Settings) Settings).Ets2GameDirectory = dir;
Settings.Save(); Settings.Save();
if (!File.Exists(dir + "/plugins/ets2-telemetry-server.dll")) if (!File.Exists(dir + "/plugins/ets2-telemetry-server.dll"))
PlacePlugin(); PlaceTruckSimulatorPlugin(dir, "eurotrucks2.exe");
} }
public void PlacePlugin() public void FindAtsGameDir()
{ {
if (((EurotruckSimulator2Settings) Settings).GameDirectory == string.Empty) // Demo is also supported but resides in a different directory, the full game can also be 32-bits (I think)
var dir = GeneralHelpers.FindSteamGame(@"\American Truck Simulator\bin\win_x64\amtrucks.exe") ??
GeneralHelpers.FindSteamGame(@"\American Truck Simulator\bin\win_x86\amtrucks.exe") ??
GeneralHelpers.FindSteamGame(@"\American Truck Simulator Demo\bin\win_x64\amtrucks.exe");
if (string.IsNullOrEmpty(dir))
return; return;
var path = ((EurotruckSimulator2Settings) Settings).GameDirectory; ((EurotruckSimulator2Settings) Settings).AtsGameDirectory = dir;
Settings.Save();
if (!File.Exists(dir + "/plugins/ets2-telemetry-server.dll"))
PlaceTruckSimulatorPlugin(dir, "amtrucks.exe");
}
public void PlaceTruckSimulatorPlugin(string path, string game)
{
if (string.IsNullOrEmpty(path))
return;
// Ensure the selected directory exists // Ensure the selected directory exists
if (!Directory.Exists(path)) if (!Directory.Exists(path))
@ -73,11 +101,15 @@ namespace Artemis.Modules.Games.EurotruckSimulator2
_dialogService.ShowErrorMessageBox($"Directory '{path}' not found."); _dialogService.ShowErrorMessageBox($"Directory '{path}' not found.");
return; return;
} }
// Ensure it's the ETS2 directory by looking for the executable // Ensure it's the proper directory by looking for the executable
if (!File.Exists(path + "/eurotrucks2.exe")) if (!File.Exists(path + "/" + game))
{ {
_dialogService.ShowErrorMessageBox("Please select a valid Eurotruck Simulator 2 directory\n\n" + if (game == "eurotrucks2.exe")
@"By default ETS2 is in \SteamApps\common\Euro Truck Simulator 2\bin\win_x64"); _dialogService.ShowErrorMessageBox("Please select a valid Eurotruck Simulator 2 directory\n\n" +
@"By default ETS2 is in \SteamApps\common\Euro Truck Simulator 2\bin\win_x64");
else
_dialogService.ShowErrorMessageBox("Please select a valid American Truck Simulator directory\n\n" +
@"By default ATS is in \SteamApps\common\American Truck Simulator\bin\win_x64");
return; return;
} }
@ -92,11 +124,11 @@ namespace Artemis.Modules.Games.EurotruckSimulator2
else else
File.WriteAllBytes(path + "/plugins/ets2-telemetry-server.dll", Resources.ets2_telemetry_server_x86); File.WriteAllBytes(path + "/plugins/ets2-telemetry-server.dll", Resources.ets2_telemetry_server_x86);
Logger?.Debug("Installed ETS2 plugin in {0}", path); Logger?.Debug("Installed Truck Simulator plugin in {0}", path);
} }
catch (Exception e) catch (Exception e)
{ {
Logger?.Error(e, "Failed to install ETS2 plugin in {0}", path); Logger?.Error(e, "Failed to install Truck Simulator plugin in {0}", path);
throw; throw;
} }
} }

View File

@ -4,6 +4,7 @@ namespace Artemis.Modules.Games.EurotruckSimulator2
{ {
public class EurotruckSimulator2Settings : ModuleSettings public class EurotruckSimulator2Settings : ModuleSettings
{ {
public string GameDirectory { get; set; } public string Ets2GameDirectory { get; set; }
public string AtsGameDirectory { get; set; }
} }
} }

View File

@ -17,6 +17,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
@ -39,7 +40,7 @@
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Bottom" <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Bottom"
TextWrapping="Wrap" HorizontalAlignment="Left" FontFamily="Segoe UI Semibold" TextWrapping="Wrap" HorizontalAlignment="Left" FontFamily="Segoe UI Semibold"
TextAlignment="Justify" Margin="5,0,0,10"> TextAlignment="Justify" Margin="5,0,0,10">
The Euro Truck Simulator 2 module uses code from the The Truck Simulator module uses code from the
<Hyperlink RequestNavigate="Hyperlink_RequestNavigate" <Hyperlink RequestNavigate="Hyperlink_RequestNavigate"
NavigateUri="https://github.com/Funbit/ets2-telemetry-server"> NavigateUri="https://github.com/Funbit/ets2-telemetry-server">
ETS2 Telemetry Web Server ETS2 Telemetry Web Server
@ -56,26 +57,41 @@
</StackPanel> </StackPanel>
</Grid> </Grid>
<StackPanel Grid.Row="2" <Grid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
Grid.Column="0" <!-- ETS2 dir -->
Grid.ColumnSpan="2" Margin="0,0,1,0"> <Grid.ColumnDefinitions>
<ColumnDefinition />
<Label FontSize="20" HorizontalAlignment="Left" Content="Euro Truck Simulator 2 directory" /> <ColumnDefinition />
<Grid> </Grid.ColumnDefinitions>
<TextBox x:Name="GameDirectory" Height="23" TextWrapping="Wrap" Margin="5,0,30,0" <StackPanel Margin="0,0,1,0" Grid.Column="0">
Text="{Binding Path=Settings.GameDirectory, Mode=TwoWay}" <Label FontSize="16" HorizontalAlignment="Left" Content="Euro Truck Simulator 2 directory" />
cal:Message.Attach="[Event LostFocus] = [Action PlacePlugin]" /> <Grid>
<Button x:Name="BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944" <TextBox x:Name="Ets2GameDirectory" Height="23" TextWrapping="Wrap" Margin="5,0,30,0"
HorizontalAlignment="Right" Width="25" Text="{Binding Path=Settings.Ets2GameDirectory, Mode=TwoWay}"
Style="{DynamicResource SquareButtonStyle}" Height="26" Margin="0,-2,0,0" /> cal:Message.Attach="[Event LostFocus] = [Action Ets2PlacePlugin]" />
</Grid> <Button x:Name="Ets2BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944"
</StackPanel> HorizontalAlignment="Right" Width="25"
Style="{DynamicResource SquareButtonStyle}" Height="26" />
</Grid>
</StackPanel>
<!-- ATS dir -->
<StackPanel Margin="0,0,1,0" Grid.Column="1">
<Label FontSize="16" HorizontalAlignment="Left" Content="American Truck Simulator directory" />
<Grid>
<TextBox x:Name="AtsGameDirectory" Height="23" TextWrapping="Wrap" Margin="5,0,30,0"
Text="{Binding Path=Settings.AtsGameDirectory, Mode=TwoWay}"
cal:Message.Attach="[Event LostFocus] = [Action AtsPlacePlugin]" />
<Button x:Name="AtsBrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944"
HorizontalAlignment="Right" Width="25"
Style="{DynamicResource SquareButtonStyle}" Height="26" />
</Grid>
</StackPanel>
</Grid>
<!-- Profile editor --> <!-- Profile editor -->
<ContentControl Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" x:Name="ProfileEditor" Margin="0,0,-20,0" /> <ContentControl Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" x:Name="ProfileEditor" Margin="0,0,-20,0" />
<!-- Buttons --> <!-- Buttons -->
<StackPanel Grid.Column="0" Grid.Row="4" Orientation="Horizontal" VerticalAlignment="Bottom"> <StackPanel Grid.Column="0" Grid.Row="5" Orientation="Horizontal" VerticalAlignment="Bottom">
<Button x:Name="ResetSettings" Content="Reset effect" VerticalAlignment="Top" Width="100" <Button x:Name="ResetSettings" Content="Reset effect" VerticalAlignment="Top" Width="100"
Style="{DynamicResource SquareButtonStyle}" /> Style="{DynamicResource SquareButtonStyle}" />
<Button x:Name="SaveSettings" Content="Save changes" VerticalAlignment="Top" Width="100" <Button x:Name="SaveSettings" Content="Save changes" VerticalAlignment="Top" Width="100"

View File

@ -1,5 +1,4 @@
using System.IO; using System.Windows.Forms;
using System.Windows.Forms;
using Artemis.Managers; using Artemis.Managers;
using Artemis.Modules.Abstract; using Artemis.Modules.Abstract;
using Ninject; using Ninject;
@ -12,24 +11,53 @@ namespace Artemis.Modules.Games.EurotruckSimulator2
[Named(nameof(EurotruckSimulator2Model))] ModuleModel moduleModel, [Named(nameof(EurotruckSimulator2Model))] ModuleModel moduleModel,
IKernel kernel) : base(mainManager, moduleModel, kernel) IKernel kernel) : base(mainManager, moduleModel, kernel)
{ {
DisplayName = "ETS 2"; DisplayName = "Truck Simulator";
} }
public override bool UsesProfileEditor => true; public override bool UsesProfileEditor => true;
public new EurotruckSimulator2Settings Settings { get; set; }
public void BrowseDirectory() public void Ets2BrowseDirectory()
{ {
var dialog = new FolderBrowserDialog {SelectedPath = Settings.GameDirectory}; var settings = (EurotruckSimulator2Settings) Settings;
var model = (EurotruckSimulator2Model) ModuleModel;
var dialog = new FolderBrowserDialog {SelectedPath = settings.Ets2GameDirectory};
var result = dialog.ShowDialog(); var result = dialog.ShowDialog();
if (result != DialogResult.OK) if (result != DialogResult.OK)
return; return;
Settings.GameDirectory = Path.GetDirectoryName(dialog.SelectedPath); settings.Ets2GameDirectory = dialog.SelectedPath;
NotifyOfPropertyChange(() => Settings); NotifyOfPropertyChange(() => Settings);
Settings.Save(); Settings.Save();
model.PlaceTruckSimulatorPlugin(settings.Ets2GameDirectory, "eurotrucks2.exe");
}
((EurotruckSimulator2Model) ModuleModel).PlacePlugin(); public void AtsBrowseDirectory()
{
var settings = (EurotruckSimulator2Settings) Settings;
var model = (EurotruckSimulator2Model)ModuleModel;
var dialog = new FolderBrowserDialog {SelectedPath = settings.AtsGameDirectory};
var result = dialog.ShowDialog();
if (result != DialogResult.OK)
return;
settings.AtsGameDirectory = dialog.SelectedPath;
NotifyOfPropertyChange(() => Settings);
Settings.Save();
model.PlaceTruckSimulatorPlugin(settings.AtsGameDirectory, "amtrucks.exe");
}
public void Ets2PlacePlugin()
{
var ets2Path = ((EurotruckSimulator2Settings)Settings).Ets2GameDirectory;
((EurotruckSimulator2Model)ModuleModel).PlaceTruckSimulatorPlugin(ets2Path, "eurotrucks2.exe");
}
public void AtsPlacePlugin()
{
var atsPath = ((EurotruckSimulator2Settings)Settings).AtsGameDirectory;
((EurotruckSimulator2Model)ModuleModel).PlaceTruckSimulatorPlugin(atsPath, "amtrucks.exe");
} }
} }
} }

View File

@ -20,7 +20,7 @@ namespace Artemis.Modules.Games.GtaV
Settings = SettingsProvider.Load<GtaVSettings>(); Settings = SettingsProvider.Load<GtaVSettings>();
DataModel = new GtaVDataModel(); DataModel = new GtaVDataModel();
ProcessName = "GTA5"; ProcessNames.Add("GTA5");
} }
public override string Name => "GTAV"; public override string Name => "GTAV";

View File

@ -15,7 +15,7 @@ namespace Artemis.Modules.Games.LightFx
{ {
Settings = SettingsProvider.Load<LightFxSettings>(); Settings = SettingsProvider.Load<LightFxSettings>();
DataModel = new LightFxDataModel(); DataModel = new LightFxDataModel();
ProcessName = "LoL"; ProcessNames.Add("LoL");
// This model can enable itself by changing its process name to the currently running Light FX game. // This model can enable itself by changing its process name to the currently running Light FX game.
pipeServer.PipeMessage += PipeServerOnPipeMessage; pipeServer.PipeMessage += PipeServerOnPipeMessage;
@ -43,7 +43,9 @@ namespace Artemis.Modules.Games.LightFx
} }
// Setup process name // Setup process name
ProcessName = Path.GetFileNameWithoutExtension(((LightFxDataModel) DataModel).LightFxState.game); var processName = Path.GetFileNameWithoutExtension(((LightFxDataModel) DataModel).LightFxState.game);
if (!ProcessNames.Contains(processName))
ProcessNames.Add(processName);
} }
public override void Update() public override void Update()

View File

@ -38,7 +38,7 @@ namespace Artemis.Modules.Games.Overwatch
Settings = SettingsProvider.Load<OverwatchSettings>(); Settings = SettingsProvider.Load<OverwatchSettings>();
DataModel = new OverwatchDataModel(); DataModel = new OverwatchDataModel();
ProcessName = "Overwatch"; ProcessNames.Add("Overwatch");
LoadOverwatchCharacters(); LoadOverwatchCharacters();
FindOverwatch(); FindOverwatch();

View File

@ -66,7 +66,7 @@
cal:Message.Attach="[Event LostFocus] = [Action PlaceDll]" /> cal:Message.Attach="[Event LostFocus] = [Action PlaceDll]" />
<Button x:Name="BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944" <Button x:Name="BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944"
HorizontalAlignment="Right" Width="25" HorizontalAlignment="Right" Width="25"
Style="{DynamicResource SquareButtonStyle}" Height="26" Margin="0,-2,0,0" /> Style="{DynamicResource SquareButtonStyle}" Height="26" />
</Grid> </Grid>
</StackPanel> </StackPanel>

View File

@ -11,7 +11,7 @@ namespace Artemis.Modules.Games.ProjectCars
{ {
Settings = SettingsProvider.Load<ProjectCarsSettings>(); Settings = SettingsProvider.Load<ProjectCarsSettings>();
DataModel = new ProjectCarsDataModel(); DataModel = new ProjectCarsDataModel();
ProcessName = "pCARS64"; ProcessNames.Add("pCARS64");
} }
public override string Name => "ProjectCars"; public override string Name => "ProjectCars";

View File

@ -18,7 +18,7 @@ namespace Artemis.Modules.Games.RocketLeague
{ {
Settings = SettingsProvider.Load<RocketLeagueSettings>(); Settings = SettingsProvider.Load<RocketLeagueSettings>();
DataModel = new RocketLeagueDataModel(); DataModel = new RocketLeagueDataModel();
ProcessName = "RocketLeague"; ProcessNames.Add("RocketLeague");
// Generate a new offset when the game is updated // Generate a new offset when the game is updated
//var offset = new GamePointersCollection //var offset = new GamePointersCollection
@ -55,17 +55,20 @@ namespace Artemis.Modules.Games.RocketLeague
Updater.GetPointers(); Updater.GetPointers();
_pointer = SettingsProvider.Load<OffsetSettings>().RocketLeague; _pointer = SettingsProvider.Load<OffsetSettings>().RocketLeague;
var tempProcess = MemoryHelpers.GetProcessIfRunning(ProcessName);
if (tempProcess == null)
return;
_memory = new Memory(tempProcess);
base.Enable(); base.Enable();
} }
public override void Update() public override void Update()
{ {
if (_memory == null)
{
var tempProcess = MemoryHelpers.GetProcessIfRunning(ProcessNames[0]);
if (tempProcess == null)
return;
_memory = new Memory(tempProcess);
}
if (ProfileModel == null || DataModel == null || _memory == null) if (ProfileModel == null || DataModel == null || _memory == null)
return; return;

View File

@ -32,8 +32,6 @@ namespace Artemis.Modules.Games.RocketLeague
} }
} }
public RocketLeagueModel RocketLeagueModel { get; set; }
private void SetVersionText() private void SetVersionText()
{ {
if (!SettingsProvider.Load<GeneralSettings>().EnablePointersUpdate) if (!SettingsProvider.Load<GeneralSettings>().EnablePointersUpdate)

View File

@ -22,7 +22,7 @@ namespace Artemis.Modules.Games.TheDivision
Settings = SettingsProvider.Load<TheDivisionSettings>(); Settings = SettingsProvider.Load<TheDivisionSettings>();
DataModel = new TheDivisionDataModel(); DataModel = new TheDivisionDataModel();
ProcessName = "TheDivision"; ProcessNames.Add("TheDivision");
} }
public override string Name => "TheDivision"; public override string Name => "TheDivision";

View File

@ -30,7 +30,7 @@ namespace Artemis.Modules.Games.UnrealTournament
Settings = SettingsProvider.Load<UnrealTournamentSettings>(); Settings = SettingsProvider.Load<UnrealTournamentSettings>();
DataModel = new UnrealTournamentDataModel(); DataModel = new UnrealTournamentDataModel();
ProcessName = "UE4-Win64-Shipping"; ProcessNames.Add("UE4-Win64-Shipping");
_killTimer = new Timer(3500); _killTimer = new Timer(3500);
_killTimer.Elapsed += KillTimerOnElapsed; _killTimer.Elapsed += KillTimerOnElapsed;

View File

@ -50,7 +50,7 @@
cal:Message.Attach="[Event LostFocus] = [Action PlaceFiles]" /> cal:Message.Attach="[Event LostFocus] = [Action PlaceFiles]" />
<Button x:Name="BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944" <Button x:Name="BrowseDirectory" Content="..." RenderTransformOrigin="-0.039,-0.944"
HorizontalAlignment="Right" Width="25" HorizontalAlignment="Right" Width="25"
Style="{DynamicResource SquareButtonStyle}" Height="26" Margin="0,-2,0,0" /> Style="{DynamicResource SquareButtonStyle}" Height="26" />
</Grid> </Grid>
</StackPanel> </StackPanel>

View File

@ -22,7 +22,7 @@ namespace Artemis.Modules.Games.Witcher3
Settings = SettingsProvider.Load<Witcher3Settings>(); Settings = SettingsProvider.Load<Witcher3Settings>();
DataModel = new Witcher3DataModel(); DataModel = new Witcher3DataModel();
ProcessName = "witcher3"; ProcessNames.Add("witcher3");
} }
public override string Name => "Witcher3"; public override string Name => "Witcher3";

View File

@ -21,7 +21,7 @@ namespace Artemis.Modules.Games.WoW
{ {
Settings = SettingsProvider.Load<WoWSettings>(); Settings = SettingsProvider.Load<WoWSettings>();
DataModel = new WoWDataModel(); DataModel = new WoWDataModel();
ProcessName = "Wow-64"; ProcessNames.Add("Wow-64");
// Currently WoW is locked behind a hidden trigger (obviously not that hidden since you're reading this) // Currently WoW is locked behind a hidden trigger (obviously not that hidden since you're reading this)
// It is using memory reading and lets first try to contact Blizzard // It is using memory reading and lets first try to contact Blizzard
@ -72,19 +72,17 @@ namespace Artemis.Modules.Games.WoW
_process = null; _process = null;
} }
public override void Enable()
{
var tempProcess = MemoryHelpers.GetProcessIfRunning(ProcessName);
if (tempProcess == null)
return;
_process = new ProcessSharp(tempProcess, MemoryType.Remote);
base.Enable();
}
public override void Update() public override void Update()
{ {
if (_process == null)
{
var tempProcess = MemoryHelpers.GetProcessIfRunning(ProcessNames[0]);
if (tempProcess == null)
return;
_process = new ProcessSharp(tempProcess, MemoryType.Remote);
}
if (ProfileModel == null || DataModel == null || _process == null) if (ProfileModel == null || DataModel == null || _process == null)
return; return;

View File

@ -15,6 +15,7 @@ namespace Artemis.Modules.General.GeneralProfile
CurrentTime = new CurrentTime(); CurrentTime = new CurrentTime();
Keyboard = new KbDataModel(); Keyboard = new KbDataModel();
ActiveWindow = new ActiveWindow(); ActiveWindow = new ActiveWindow();
Audio = new Audio();
} }
public CpuDataModel Cpu { get; set; } public CpuDataModel Cpu { get; set; }
@ -24,6 +25,23 @@ namespace Artemis.Modules.General.GeneralProfile
public CurrentTime CurrentTime { get; set; } public CurrentTime CurrentTime { get; set; }
public KbDataModel Keyboard { get; set; } public KbDataModel Keyboard { get; set; }
public ActiveWindow ActiveWindow { get; set; } public ActiveWindow ActiveWindow { get; set; }
public Audio Audio { get; set; }
}
[MoonSharpUserData]
public class Audio
{
public float Volume { get; set; }
public AudioDevice Recording { get; set; } = new AudioDevice();
public AudioDevice Playback { get; set; } = new AudioDevice();
}
[MoonSharpUserData]
public class AudioDevice
{
public float OverallPeak { get; set; }
public float LeftPeak { get; set; }
public float RightPeak { get; set; }
} }
[MoonSharpUserData] [MoonSharpUserData]

View File

@ -7,9 +7,11 @@ using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.DAL; using Artemis.DAL;
using Artemis.Events;
using Artemis.Managers; using Artemis.Managers;
using Artemis.Modules.Abstract; using Artemis.Modules.Abstract;
using Artemis.Utilities; using Artemis.Utilities;
using CSCore.CoreAudioAPI;
using Newtonsoft.Json; using Newtonsoft.Json;
using SpotifyAPI.Local; using SpotifyAPI.Local;
@ -17,19 +19,19 @@ namespace Artemis.Modules.General.GeneralProfile
{ {
public class GeneralProfileModel : ModuleModel public class GeneralProfileModel : ModuleModel
{ {
private List<PerformanceCounter> _cores;
private int _cpuFrames;
private DateTime _lastMusicUpdate; private DateTime _lastMusicUpdate;
private PerformanceCounter _overallCpu;
private SpotifyLocalAPI _spotify; private SpotifyLocalAPI _spotify;
private bool _spotifySetupBusy; private bool _spotifySetupBusy;
public GeneralProfileModel(DeviceManager deviceManager, LuaManager luaManager) : base(deviceManager, luaManager) public GeneralProfileModel(DeviceManager deviceManager, LuaManager luaManager,
AudioCaptureManager audioCaptureManager) : base(deviceManager, luaManager)
{ {
_lastMusicUpdate = DateTime.Now; _lastMusicUpdate = DateTime.Now;
Settings = SettingsProvider.Load<GeneralProfileSettings>(); Settings = SettingsProvider.Load<GeneralProfileSettings>();
DataModel = new GeneralProfileDataModel(); DataModel = new GeneralProfileDataModel();
audioCaptureManager.AudioDeviceChanged += AudioDeviceChanged;
} }
public override string Name => "GeneralProfile"; public override string Name => "GeneralProfile";
@ -40,6 +42,7 @@ namespace Artemis.Modules.General.GeneralProfile
{ {
SetupCpu(); SetupCpu();
SetupSpotify(); SetupSpotify();
SetupAudio();
base.Enable(); base.Enable();
} }
@ -52,6 +55,7 @@ namespace Artemis.Modules.General.GeneralProfile
UpdateDay(dataModel); UpdateDay(dataModel);
UpdateKeyStates(dataModel); UpdateKeyStates(dataModel);
UpdateActiveWindow(dataModel); UpdateActiveWindow(dataModel);
UpdateAudio(dataModel);
} }
#region Current Time #region Current Time
@ -67,8 +71,63 @@ namespace Artemis.Modules.General.GeneralProfile
#endregion #endregion
#region Audio
private MMDevice _defaultRecording;
private MMDevice _defaultPlayback;
private AudioMeterInformation _recordingInfo;
private AudioMeterInformation _playbackInfo;
private void SetupAudio()
{
_defaultRecording = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);
_defaultPlayback = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
if (_defaultRecording != null)
_recordingInfo = AudioMeterInformation.FromDevice(_defaultRecording);
if (_defaultPlayback != null)
_playbackInfo = AudioMeterInformation.FromDevice(_defaultPlayback);
}
private void AudioDeviceChanged(object sender, AudioDeviceChangedEventArgs e)
{
_defaultRecording = e.DefaultRecording;
_defaultPlayback = e.DefaultPlayback;
if (_defaultRecording != null)
_recordingInfo = AudioMeterInformation.FromDevice(_defaultRecording);
if (_defaultPlayback != null)
_playbackInfo = AudioMeterInformation.FromDevice(_defaultPlayback);
}
private void UpdateAudio(GeneralProfileDataModel dataModel)
{
// Update microphone, only bother with OverallPeak
if (_defaultRecording != null)
dataModel.Audio.Recording.OverallPeak = _recordingInfo.PeakValue;
if (_defaultPlayback == null)
return;
// Update volume if a default device is found
dataModel.Audio.Volume = AudioEndpointVolume.FromDevice(_defaultPlayback).GetMasterVolumeLevelScalar();
// Update speakers, only do overall, left and right for now
// TODO: When adding list support lets do all channels
var peakValues = _playbackInfo.GetChannelsPeakValues();
dataModel.Audio.Playback.OverallPeak = _playbackInfo.PeakValue;
dataModel.Audio.Playback.LeftPeak = peakValues[0];
dataModel.Audio.Playback.LeftPeak = peakValues[1];
}
#endregion
#region CPU #region CPU
private List<PerformanceCounter> _cores;
private int _cpuFrames;
private PerformanceCounter _overallCpu;
private void SetupCpu() private void SetupCpu()
{ {
try try

View File

@ -1,5 +1,6 @@
using Artemis.Modules.Abstract; using Artemis.Modules.Abstract;
using Artemis.Modules.General.GeneralProfile; using Artemis.Modules.General.GeneralProfile;
using MoonSharp.Interpreter;
namespace Artemis.Modules.Overlays.OverlayProfile namespace Artemis.Modules.Overlays.OverlayProfile
{ {
@ -7,9 +8,17 @@ namespace Artemis.Modules.Overlays.OverlayProfile
{ {
public OverlayProfileDataModel() public OverlayProfileDataModel()
{ {
GeneralDataModel = new GeneralProfileDataModel(); Keyboard = new KbDataModel();
Audio = new Audio();
} }
public GeneralProfileDataModel GeneralDataModel { get; set; } public KbDataModel Keyboard { get; set; }
public Audio Audio { get; set; }
}
[MoonSharpUserData]
public class Audio
{
public float Volume { get; set; }
} }
} }

View File

@ -1,32 +1,65 @@
using Artemis.DAL; using Artemis.DAL;
using Artemis.Events;
using Artemis.Managers; using Artemis.Managers;
using Artemis.Modules.Abstract; using Artemis.Modules.Abstract;
using Artemis.Modules.General.GeneralProfile; using Artemis.Modules.General.GeneralProfile;
using Ninject; using CSCore.CoreAudioAPI;
namespace Artemis.Modules.Overlays.OverlayProfile namespace Artemis.Modules.Overlays.OverlayProfile
{ {
public class OverlayProfileModel : ModuleModel public class OverlayProfileModel : ModuleModel
{ {
private readonly GeneralProfileModel _generalProfileModel; private AudioEndpointVolume _endPointVolume;
public OverlayProfileModel(DeviceManager deviceManager, LuaManager luaManager, public OverlayProfileModel(DeviceManager deviceManager, LuaManager luaManager,
[Named(nameof(GeneralProfileModel))] ModuleModel generalProfileModel) : base(deviceManager, luaManager) AudioCaptureManager audioCaptureManager) : base(deviceManager, luaManager)
{ {
_generalProfileModel = (GeneralProfileModel) generalProfileModel;
Settings = SettingsProvider.Load<OverlayProfileSettings>(); Settings = SettingsProvider.Load<OverlayProfileSettings>();
DataModel = new OverlayProfileDataModel(); DataModel = new OverlayProfileDataModel();
var defaultPlayback = MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
if (defaultPlayback != null)
_endPointVolume = AudioEndpointVolume.FromDevice(defaultPlayback);
audioCaptureManager.AudioDeviceChanged += OnAudioDeviceChanged;
Enable();
} }
public override string Name => "OverlayProfile"; public override string Name => "OverlayProfile";
public override bool IsOverlay => true; public override bool IsOverlay => true;
public override bool IsBoundToProcess => false; public override bool IsBoundToProcess => false;
private void OnAudioDeviceChanged(object sender, AudioDeviceChangedEventArgs e)
{
if (e.DefaultPlayback != null)
_endPointVolume = AudioEndpointVolume.FromDevice(e.DefaultPlayback);
}
public override void Update() public override void Update()
{ {
// TODO: Find a clean way to update the parent profile model if (!Settings.IsEnabled)
((OverlayProfileDataModel) DataModel).GeneralDataModel = return;
(GeneralProfileDataModel) _generalProfileModel.DataModel;
var dataModel = (OverlayProfileDataModel) DataModel;
dataModel.Keyboard.NumLock = ((ushort) GeneralProfileModel.GetKeyState(0x90) & 0xffff) != 0;
dataModel.Keyboard.CapsLock = ((ushort) GeneralProfileModel.GetKeyState(0x14) & 0xffff) != 0;
dataModel.Keyboard.ScrollLock = ((ushort) GeneralProfileModel.GetKeyState(0x91) & 0xffff) != 0;
if (_endPointVolume != null)
dataModel.Audio.Volume = _endPointVolume.GetMasterVolumeLevelScalar();
}
public override void Render(RenderFrame frame, bool keyboardOnly)
{
if (Settings.IsEnabled)
base.Render(frame, keyboardOnly);
}
public override void Dispose()
{
PreviewLayers = null;
} }
} }
} }

View File

@ -1,5 +1,6 @@
using System.Windows.Media; using System.Windows.Media;
using Artemis.Profiles.Layers.Models; using Artemis.Profiles.Layers.Models;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
using Caliburn.Micro; using Caliburn.Micro;

View File

@ -27,17 +27,17 @@ namespace Artemis.Profiles.Layers.Animations
layerModel.AnimationProgress = progress; layerModel.AnimationProgress = progress;
} }
public void Draw(LayerModel layerModel, DrawingContext c) public void Draw(LayerModel layerModel, DrawingContext c, int drawScale)
{ {
if (layerModel.Brush == null) if (layerModel.Brush == null)
return; return;
// Set up variables for this frame // Set up variables for this frame
var rect = layerModel.Properties.Contain var rect = layerModel.Properties.Contain
? layerModel.LayerRect() ? layerModel.LayerRect(drawScale)
: layerModel.Properties.PropertiesRect(); : layerModel.Properties.PropertiesRect(drawScale);
var clip = layerModel.LayerRect(); var clip = layerModel.LayerRect(drawScale);
// Take an offset of 4 to allow layers to slightly leave their bounds // Take an offset of 4 to allow layers to slightly leave their bounds
var progress = (6.0 - layerModel.AnimationProgress)*10.0; var progress = (6.0 - layerModel.AnimationProgress)*10.0;

View File

@ -12,7 +12,7 @@ namespace Artemis.Profiles.Layers.Animations
{ {
} }
public void Draw(LayerModel layerModel, DrawingContext c) public void Draw(LayerModel layerModel, DrawingContext c, int drawScale)
{ {
} }

View File

@ -27,17 +27,17 @@ namespace Artemis.Profiles.Layers.Animations
layerModel.AnimationProgress = progress; layerModel.AnimationProgress = progress;
} }
public void Draw(LayerModel layerModel, DrawingContext c) public void Draw(LayerModel layerModel, DrawingContext c, int drawScale)
{ {
if (layerModel.Brush == null) if (layerModel.Brush == null)
return; return;
// Set up variables for this frame // Set up variables for this frame
var rect = layerModel.Properties.Contain var rect = layerModel.Properties.Contain
? layerModel.LayerRect() ? layerModel.LayerRect(drawScale)
: layerModel.Properties.PropertiesRect(); : layerModel.Properties.PropertiesRect(drawScale);
var clip = layerModel.LayerRect(); var clip = layerModel.LayerRect(drawScale);
// Can't meddle with the original brush because it's frozen. // Can't meddle with the original brush because it's frozen.
var brush = layerModel.Brush.Clone(); var brush = layerModel.Brush.Clone();

View File

@ -14,29 +14,29 @@ namespace Artemis.Profiles.Layers.Animations
var progress = layerModel.AnimationProgress; var progress = layerModel.AnimationProgress;
if (MustExpire(layerModel)) if (MustExpire(layerModel))
progress = 0; progress = 0;
progress = progress + layerModel.Properties.AnimationSpeed*2; progress = progress + layerModel.Properties.AnimationSpeed * 2 / 4 * layerModel.LayerType.DrawScale;
// If not previewing, store the animation progress in the actual model for the next frame // If not previewing, store the animation progress in the actual model for the next frame
if (updateAnimations) if (updateAnimations)
layerModel.AnimationProgress = progress; layerModel.AnimationProgress = progress;
} }
public void Draw(LayerModel layerModel, DrawingContext c) public void Draw(LayerModel layerModel, DrawingContext c, int drawScale)
{ {
if (layerModel.Brush == null) if (layerModel.Brush == null)
return; return;
// Set up variables for this frame // Set up variables for this frame
var rect = layerModel.Properties.Contain var rect = layerModel.Properties.Contain
? layerModel.LayerRect() ? layerModel.LayerRect(drawScale)
: layerModel.Properties.PropertiesRect(); : layerModel.Properties.PropertiesRect(drawScale);
var s1 = new Rect(new Point(rect.X, rect.Y + layerModel.AnimationProgress), var s1 = new Rect(new Point(rect.X, rect.Y + layerModel.AnimationProgress),
new Size(rect.Width, rect.Height)); new Size(rect.Width, rect.Height));
var s2 = new Rect(new Point(s1.X, s1.Y - rect.Height), var s2 = new Rect(new Point(s1.X, s1.Y - rect.Height),
new Size(rect.Width, rect.Height + .5)); new Size(rect.Width, rect.Height + .5));
var clip = layerModel.LayerRect(); var clip = layerModel.LayerRect(drawScale);
c.PushClip(new RectangleGeometry(clip)); c.PushClip(new RectangleGeometry(clip));
c.DrawRectangle(layerModel.Brush, null, s1); c.DrawRectangle(layerModel.Brush, null, s1);
@ -46,7 +46,8 @@ namespace Artemis.Profiles.Layers.Animations
public bool MustExpire(LayerModel layer) public bool MustExpire(LayerModel layer)
{ {
return layer.AnimationProgress + layer.Properties.AnimationSpeed*2 >= layer.Properties.Height*4; return layer.AnimationProgress + layer.Properties.AnimationSpeed * 2 >=
layer.Properties.Height * layer.LayerType.DrawScale;
} }
} }
} }

View File

@ -14,29 +14,29 @@ namespace Artemis.Profiles.Layers.Animations
var progress = layerModel.AnimationProgress; var progress = layerModel.AnimationProgress;
if (MustExpire(layerModel)) if (MustExpire(layerModel))
progress = 0; progress = 0;
progress = progress + layerModel.Properties.AnimationSpeed*2; progress = progress + layerModel.Properties.AnimationSpeed * 2 / 4 * layerModel.LayerType.DrawScale;
// If not previewing, store the animation progress in the actual model for the next frame // If not previewing, store the animation progress in the actual model for the next frame
if (updateAnimations) if (updateAnimations)
layerModel.AnimationProgress = progress; layerModel.AnimationProgress = progress;
} }
public void Draw(LayerModel layerModel, DrawingContext c) public void Draw(LayerModel layerModel, DrawingContext c, int drawScale)
{ {
if (layerModel.Brush == null) if (layerModel.Brush == null)
return; return;
// Set up variables for this frame // Set up variables for this frame
var rect = layerModel.Properties.Contain var rect = layerModel.Properties.Contain
? layerModel.LayerRect() ? layerModel.LayerRect(drawScale)
: layerModel.Properties.PropertiesRect(); : layerModel.Properties.PropertiesRect(drawScale);
var s1 = new Rect(new Point(rect.X - layerModel.AnimationProgress, rect.Y), var s1 = new Rect(new Point(rect.X - layerModel.AnimationProgress, rect.Y),
new Size(rect.Width + .5, rect.Height)); new Size(rect.Width + .5, rect.Height));
var s2 = new Rect(new Point(s1.X + rect.Width, rect.Y), var s2 = new Rect(new Point(s1.X + rect.Width, rect.Y),
new Size(rect.Width, rect.Height)); new Size(rect.Width, rect.Height));
var clip = layerModel.LayerRect(); var clip = layerModel.LayerRect(drawScale);
c.PushClip(new RectangleGeometry(clip)); c.PushClip(new RectangleGeometry(clip));
c.DrawRectangle(layerModel.Brush, null, s1); c.DrawRectangle(layerModel.Brush, null, s1);
@ -46,7 +46,8 @@ namespace Artemis.Profiles.Layers.Animations
public bool MustExpire(LayerModel layer) public bool MustExpire(LayerModel layer)
{ {
return layer.AnimationProgress + layer.Properties.AnimationSpeed*2 >= layer.Properties.Width*4; return layer.AnimationProgress + layer.Properties.AnimationSpeed * 2 >=
layer.Properties.Width * layer.LayerType.DrawScale;
} }
} }
} }

View File

@ -14,29 +14,29 @@ namespace Artemis.Profiles.Layers.Animations
var progress = layerModel.AnimationProgress; var progress = layerModel.AnimationProgress;
if (MustExpire(layerModel)) if (MustExpire(layerModel))
progress = 0; progress = 0;
progress = progress + layerModel.Properties.AnimationSpeed*2; progress = progress + layerModel.Properties.AnimationSpeed * 2 / 4 * layerModel.LayerType.DrawScale;
// If not previewing, store the animation progress in the actual model for the next frame // If not previewing, store the animation progress in the actual model for the next frame
if (updateAnimations) if (updateAnimations)
layerModel.AnimationProgress = progress; layerModel.AnimationProgress = progress;
} }
public void Draw(LayerModel layerModel, DrawingContext c) public void Draw(LayerModel layerModel, DrawingContext c, int drawScale)
{ {
if (layerModel.Brush == null) if (layerModel.Brush == null)
return; return;
// Set up variables for this frame // Set up variables for this frame
var rect = layerModel.Properties.Contain var rect = layerModel.Properties.Contain
? layerModel.LayerRect() ? layerModel.LayerRect(drawScale)
: layerModel.Properties.PropertiesRect(); : layerModel.Properties.PropertiesRect(drawScale);
var s1 = new Rect(new Point(rect.X + layerModel.AnimationProgress, rect.Y), var s1 = new Rect(new Point(rect.X + layerModel.AnimationProgress, rect.Y),
new Size(rect.Width, rect.Height)); new Size(rect.Width, rect.Height));
var s2 = new Rect(new Point(s1.X - rect.Width, rect.Y), var s2 = new Rect(new Point(s1.X - rect.Width, rect.Y),
new Size(rect.Width + .5, rect.Height)); new Size(rect.Width + .5, rect.Height));
var clip = layerModel.LayerRect(); var clip = layerModel.LayerRect(drawScale);
c.PushClip(new RectangleGeometry(clip)); c.PushClip(new RectangleGeometry(clip));
c.DrawRectangle(layerModel.Brush, null, s1); c.DrawRectangle(layerModel.Brush, null, s1);
@ -46,7 +46,8 @@ namespace Artemis.Profiles.Layers.Animations
public bool MustExpire(LayerModel layer) public bool MustExpire(LayerModel layer)
{ {
return layer.AnimationProgress + layer.Properties.AnimationSpeed*2 >= layer.Properties.Width*4; return layer.AnimationProgress + layer.Properties.AnimationSpeed * 2 >=
layer.Properties.Width * layer.LayerType.DrawScale;
} }
} }
} }

View File

@ -14,28 +14,28 @@ namespace Artemis.Profiles.Layers.Animations
var progress = layerModel.AnimationProgress; var progress = layerModel.AnimationProgress;
if (MustExpire(layerModel)) if (MustExpire(layerModel))
progress = 0; progress = 0;
progress = progress + layerModel.Properties.AnimationSpeed*2; progress = progress + layerModel.Properties.AnimationSpeed * 2 / 4 * layerModel.LayerType.DrawScale;
// If not previewing, store the animation progress in the actual model for the next frame // If not previewing, store the animation progress in the actual model for the next frame
if (updateAnimations) if (updateAnimations)
layerModel.AnimationProgress = progress; layerModel.AnimationProgress = progress;
} }
public void Draw(LayerModel layerModel, DrawingContext c) public void Draw(LayerModel layerModel, DrawingContext c, int drawScale)
{ {
if (layerModel.Brush == null) if (layerModel.Brush == null)
return; return;
// Set up variables for this frame // Set up variables for this frame
var rect = layerModel.Properties.Contain var rect = layerModel.Properties.Contain
? layerModel.LayerRect() ? layerModel.LayerRect(drawScale)
: layerModel.Properties.PropertiesRect(); : layerModel.Properties.PropertiesRect(drawScale);
var s1 = new Rect(new Point(rect.X, rect.Y - layerModel.AnimationProgress), var s1 = new Rect(new Point(rect.X, rect.Y - layerModel.AnimationProgress),
new Size(rect.Width, rect.Height + .5)); new Size(rect.Width, rect.Height + .5));
var s2 = new Rect(new Point(s1.X, s1.Y + rect.Height), new Size(rect.Width, rect.Height)); var s2 = new Rect(new Point(s1.X, s1.Y + rect.Height), new Size(rect.Width, rect.Height));
var clip = layerModel.LayerRect(); var clip = layerModel.LayerRect(drawScale);
c.PushClip(new RectangleGeometry(clip)); c.PushClip(new RectangleGeometry(clip));
c.DrawRectangle(layerModel.Brush, null, s1); c.DrawRectangle(layerModel.Brush, null, s1);
@ -45,7 +45,8 @@ namespace Artemis.Profiles.Layers.Animations
public bool MustExpire(LayerModel layer) public bool MustExpire(LayerModel layer)
{ {
return layer.AnimationProgress + layer.Properties.AnimationSpeed*2 >= layer.Properties.Height*4; return layer.AnimationProgress + layer.Properties.AnimationSpeed * 2 >=
layer.Properties.Height * layer.LayerType.DrawScale;
} }
} }
} }

View File

@ -10,7 +10,7 @@ namespace Artemis.Profiles.Layers.Interfaces
string Name { get; } string Name { get; }
void Update(LayerModel layerModel, bool updateAnimations); void Update(LayerModel layerModel, bool updateAnimations);
void Draw(LayerModel layerModel, DrawingContext c); void Draw(LayerModel layerModel, DrawingContext c, int drawScale);
bool MustExpire(LayerModel layerModel); bool MustExpire(LayerModel layerModel);
} }
} }

View File

@ -2,7 +2,7 @@
using Artemis.Modules.Abstract; using Artemis.Modules.Abstract;
using Artemis.Profiles.Layers.Abstract; using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Models; using Artemis.Profiles.Layers.Models;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Artemis.Profiles.Layers.Interfaces namespace Artemis.Profiles.Layers.Interfaces
@ -28,6 +28,12 @@ namespace Artemis.Profiles.Layers.Interfaces
[JsonIgnore] [JsonIgnore]
DrawType DrawType { get; } DrawType DrawType { get; }
/// <summary>
/// Gets the scale on which the layer should be drawn
/// </summary>
[JsonIgnore]
int DrawScale { get; }
/// <summary> /// <summary>
/// The the thumbnail for this layer type /// The the thumbnail for this layer type
/// </summary> /// </summary>

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Globalization;
using Artemis.Modules.Abstract; using Artemis.Modules.Abstract;
using Artemis.Utilities; using Artemis.Utilities;
using DynamicExpresso; using DynamicExpresso;
@ -8,6 +9,7 @@ namespace Artemis.Profiles.Layers.Models
public class LayerConditionModel public class LayerConditionModel
{ {
private readonly Interpreter _interpreter; private readonly Interpreter _interpreter;
private object _lastValue;
public LayerConditionModel() public LayerConditionModel()
{ {
@ -23,39 +25,92 @@ namespace Artemis.Profiles.Layers.Models
{ {
lock (subject) lock (subject)
{ {
if (string.IsNullOrEmpty(Field) || string.IsNullOrEmpty(Value) || string.IsNullOrEmpty(Type)) if (string.IsNullOrEmpty(Field) || string.IsNullOrEmpty(Type))
return false; return false;
var inspect = GeneralHelpers.GetPropertyValue(subject, Field); var inspect = GeneralHelpers.GetPropertyValue(subject, Field);
if (inspect == null) if (inspect == null)
{
_lastValue = null;
return false; return false;
// Put the subject in a list, allowing Dynamic Linq to be used.
if (Type == "String")
{
return _interpreter.Eval<bool>($"subject.{Field}.ToLower(){Operator}(value)",
new Parameter("subject", subject.GetType(), subject),
new Parameter("value", Value.ToLower()));
} }
Parameter rightParam = null; bool returnValue;
switch (Type) if (Operator == "changed" || Operator == "decreased" || Operator == "increased")
{ returnValue = EvaluateEventOperator(subject, inspect);
case "Enum": else
var enumType = GeneralHelpers.GetPropertyValue(subject, Field).GetType(); returnValue = EvaluateOperator(subject);
rightParam = new Parameter("value", Enum.Parse(enumType, Value));
break;
case "Boolean":
rightParam = new Parameter("value", bool.Parse(Value));
break;
case "Int32":
rightParam = new Parameter("value", int.Parse(Value));
break;
}
return _interpreter.Eval<bool>($"subject.{Field} {Operator} value", _lastValue = inspect;
new Parameter("subject", subject.GetType(), subject), rightParam); return returnValue;
} }
} }
private bool EvaluateEventOperator(ModuleDataModel subject, object inspect)
{
// DynamicExpresso doesn't want a null so when it was previously null (should only happen first iteration)
// return false since that would be the only possible outcome
if (_lastValue == null)
return false;
// Assign the right parameter
var rightParam = new Parameter("value", _lastValue);
// Come up with the proper operator
var changeOperator = "";
if (Operator == "changed")
changeOperator = "!=";
else if (Operator == "decreased")
changeOperator = "<";
else if (Operator == "increased")
changeOperator = ">";
// Evaluate the result and store it
var returnValue = _interpreter.Eval<bool>($"subject.{Field} {changeOperator} value",
new Parameter("subject", subject.GetType(), subject), rightParam);
// Set the last value to the new value
_lastValue = inspect;
// Return the evaluated result
return returnValue;
}
private bool EvaluateOperator(ModuleDataModel subject)
{
// Since _lastValue won't be used, rely on Value to not be null
if (string.IsNullOrEmpty(Value))
return false;
// Put the subject in a list, allowing Dynamic Linq to be used.
if (Type == "String")
{
return _interpreter.Eval<bool>($"subject.{Field}.ToLower(){Operator}(value)",
new Parameter("subject", subject.GetType(), subject),
new Parameter("value", Value.ToLower()));
}
Parameter rightParam = null;
switch (Type)
{
case "Enum":
var enumType = GeneralHelpers.GetPropertyValue(subject, Field).GetType();
rightParam = new Parameter("value", Enum.Parse(enumType, Value));
break;
case "Boolean":
rightParam = new Parameter("value", bool.Parse(Value));
break;
case "Int32":
rightParam = new Parameter("value", int.Parse(Value));
break;
case "Single":
// Parse commas as decimals
rightParam = new Parameter("value", float.Parse(Value.Replace(",", "."),
CultureInfo.InvariantCulture));
break;
}
return _interpreter.Eval<bool>($"subject.{Field} {Operator} value",
new Parameter("subject", subject.GetType(), subject), rightParam);
}
} }
} }

View File

@ -26,61 +26,88 @@ namespace Artemis.Profiles.Layers.Models
_heightTweener = new Tweener<float>((float) layerModel.Height, (float) layerModel.Height, 0); _heightTweener = new Tweener<float>((float) layerModel.Height, (float) layerModel.Height, 0);
_opacityTweener = new Tweener<float>((float) layerModel.Opacity, (float) layerModel.Opacity, 0); _opacityTweener = new Tweener<float>((float) layerModel.Opacity, (float) layerModel.Opacity, 0);
StoreCurrentValues(); _x = (float)_layerModel.X;
_y = (float)_layerModel.Y;
_width = (float)_layerModel.Width;
_height = (float)_layerModel.Height;
_opacity = (float)_layerModel.Opacity;
} }
public void Update() public void Update()
{ {
UpdateWidth();
UpdateHeight();
UpdateOpacity();
}
private void UpdateWidth()
{
if (_layerModel.Properties.WidthEaseTime < 0.001)
return;
// Width // Width
if (Math.Abs(_layerModel.Width - _width) > 0.001) if (Math.Abs(_layerModel.Width - _width) > 0.001)
{ {
var widthFunc = GetEaseFunction(_layerModel.Properties.WidthEase); var widthFunc = GetEaseFunction(_layerModel.Properties.WidthEase);
var widthSpeed = _layerModel.Properties.WidthEaseTime; var widthSpeed = _layerModel.Properties.WidthEaseTime;
_xTweener = new Tweener<float>(_xTweener.Value, (float) _layerModel.X, widthSpeed, widthFunc); _xTweener = new Tweener<float>(_xTweener.Value, (float)_layerModel.X, widthSpeed, widthFunc);
_widthTweener = new Tweener<float>(_widthTweener.Value, (float) _layerModel.Width, widthSpeed, widthFunc); _widthTweener = new Tweener<float>(_widthTweener.Value, (float)_layerModel.Width, widthSpeed, widthFunc);
} }
_xTweener.Update(40);
_widthTweener.Update(40);
_x = (float) _layerModel.X;
_width = (float)_layerModel.Width;
_layerModel.X = _xTweener.Value;
_layerModel.Width = _widthTweener.Value;
}
private void UpdateHeight()
{
if (_layerModel.Properties.HeightEaseTime < 0.001)
return;
// Height // Height
if (Math.Abs(_layerModel.Height - _height) > 0.001) if (Math.Abs(_layerModel.Height - _height) > 0.001)
{ {
var heightFunc = GetEaseFunction(_layerModel.Properties.HeightEase); var heightFunc = GetEaseFunction(_layerModel.Properties.HeightEase);
var heightSpeed = _layerModel.Properties.HeightEaseTime; var heightSpeed = _layerModel.Properties.HeightEaseTime;
_yTweener = new Tweener<float>(_y, (float) _layerModel.Y, heightSpeed, heightFunc); _yTweener = new Tweener<float>(_y, (float)_layerModel.Y, heightSpeed, heightFunc);
_heightTweener = new Tweener<float>(_height, (float) _layerModel.Height, heightSpeed, heightFunc); _heightTweener = new Tweener<float>(_height, (float)_layerModel.Height, heightSpeed, heightFunc);
} }
_yTweener.Update(40);
_heightTweener.Update(40);
_y = (float)_layerModel.Y;
_height = (float)_layerModel.Height;
_layerModel.Y = _yTweener.Value;
_layerModel.Height = _heightTweener.Value;
}
private void UpdateOpacity()
{
if (_layerModel.Properties.OpacityEaseTime < 0.001)
return;
// Opacity // Opacity
if (Math.Abs(_layerModel.Opacity - _opacity) > 0.001) if (Math.Abs(_layerModel.Opacity - _opacity) > 0.001)
{ {
_opacityTweener = new Tweener<float>(_opacity, (float) _layerModel.Opacity, _opacityTweener = new Tweener<float>(_opacity, (float)_layerModel.Opacity,
_layerModel.Properties.OpacityEaseTime, GetEaseFunction(_layerModel.Properties.OpacityEase)); _layerModel.Properties.OpacityEaseTime, GetEaseFunction(_layerModel.Properties.OpacityEase));
} }
_xTweener.Update(40);
_yTweener.Update(40);
_widthTweener.Update(40);
_heightTweener.Update(40);
_opacityTweener.Update(40); _opacityTweener.Update(40);
StoreCurrentValues(); _opacity = (float)_layerModel.Opacity;
_layerModel.X = _xTweener.Value;
_layerModel.Y = _yTweener.Value;
_layerModel.Width = _widthTweener.Value;
_layerModel.Height = _heightTweener.Value;
_layerModel.Opacity = _opacityTweener.Value; _layerModel.Opacity = _opacityTweener.Value;
} }
private void StoreCurrentValues()
{
_x = (float) _layerModel.X;
_y = (float) _layerModel.Y;
_width = (float) _layerModel.Width;
_height = (float) _layerModel.Height;
_opacity = (float) _layerModel.Opacity;
}
private static EaseFunc GetEaseFunction(string functionName) private static EaseFunc GetEaseFunction(string functionName)
{ {
switch (functionName) switch (functionName)

View File

@ -1,4 +1,5 @@
using Artemis.Profiles.Layers.Abstract; using Artemis.Profiles.Layers.Abstract;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.AmbientLight namespace Artemis.Profiles.Layers.Types.AmbientLight

View File

@ -13,7 +13,7 @@ using Artemis.Profiles.Layers.Types.AmbientLight.Model.Extensions;
using Artemis.Profiles.Layers.Types.AmbientLight.ScreenCapturing; using Artemis.Profiles.Layers.Types.AmbientLight.ScreenCapturing;
using Artemis.Properties; using Artemis.Properties;
using Artemis.Utilities; using Artemis.Utilities;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Artemis.Profiles.Layers.Types.AmbientLight namespace Artemis.Profiles.Layers.Types.AmbientLight
@ -25,6 +25,7 @@ namespace Artemis.Profiles.Layers.Types.AmbientLight
public string Name => "Keyboard - Ambient Light"; public string Name => "Keyboard - Ambient Light";
public bool ShowInEdtor => true; public bool ShowInEdtor => true;
public DrawType DrawType => DrawType.Keyboard; public DrawType DrawType => DrawType.Keyboard;
public int DrawScale => 4;
[JsonIgnore] private AmbienceCreatorType? _lastAmbienceCreatorType; [JsonIgnore] private AmbienceCreatorType? _lastAmbienceCreatorType;
@ -76,11 +77,7 @@ namespace Artemis.Profiles.Layers.Types.AmbientLight
public void Draw(LayerModel layerModel, DrawingContext c) public void Draw(LayerModel layerModel, DrawingContext c)
{ {
var rect = new Rect(layerModel.Properties.X*4, var rect = layerModel.LayerRect(DrawScale);
layerModel.Properties.Y*4,
layerModel.Properties.Width*4,
layerModel.Properties.Height*4);
c.DrawRectangle(((AmbientLightPropertiesModel) layerModel.Properties).AmbientLightBrush, null, rect); c.DrawRectangle(((AmbientLightPropertiesModel) layerModel.Properties).AmbientLightBrush, null, rect);
} }

View File

@ -0,0 +1,216 @@
using System;
using System.Linq;
using System.Timers;
using CSCore;
using CSCore.CoreAudioAPI;
using CSCore.DSP;
using CSCore.SoundIn;
using CSCore.SoundOut;
using CSCore.Streams;
using Ninject.Extensions.Logging;
namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
{
public class AudioCapture
{
private const FftSize FftSize = CSCore.DSP.FftSize.Fft1024;
private readonly Timer _volumeTimer;
private readonly double[] _volumeValues;
private readonly Timer _disableTimer;
private bool _mayStop;
private SingleSpectrum _singleSpectrum;
private ISoundIn _soundIn;
private GainSource _source;
private BasicSpectrumProvider _spectrumProvider;
private GainSource _volume;
private int _volumeIndex;
public AudioCapture(ILogger logger, MMDevice device, MmDeviceType type)
{
Logger = logger;
Device = device;
Type = type;
DesiredAverage = 0.75;
_volumeValues = new double[5];
_volumeIndex = 0;
_disableTimer = new Timer(1000);
_disableTimer.Elapsed += CheckStop;
_volumeTimer = new Timer(200);
_volumeTimer.Elapsed += VolumeTimerOnElapsed;
}
public ILogger Logger { get; }
public MMDevice Device { get; }
public MmDeviceType Type { get; }
public double DesiredAverage { get; set; }
public float Volume
{
get { return _volume.Volume; }
set { _volume.Volume = value; }
}
public bool Running { get; set; }
private void VolumeTimerOnElapsed(object sender, ElapsedEventArgs e)
{
if (Volume <= 0)
Volume = 1;
var currentValue = _singleSpectrum.GetValue();
if (currentValue == null)
return;
_volumeValues[_volumeIndex] = currentValue.Value;
if (_volumeIndex == 4)
{
_volumeIndex = 0;
}
else
{
_volumeIndex++;
return;
}
var averageVolume = _volumeValues.Average();
// Don't adjust when there is virtually no audio
if (averageVolume < 0.01)
return;
// Don't bother when the volume with within a certain marigin
if (averageVolume > DesiredAverage - 0.1 && averageVolume < DesiredAverage + 0.1)
return;
if (averageVolume < DesiredAverage && Volume < 50)
{
Logger.Trace("averageVolume:{0} | DesiredAverage:{1} | Volume:{2} so increase.", currentValue,
DesiredAverage, Volume);
Volume++;
}
else if (Volume > 1)
{
Logger.Trace("averageVolume:{0} | DesiredAverage:{1} | Volume:{2} so decrease.", currentValue,
DesiredAverage, Volume);
Volume--;
}
}
public LineSpectrum GetLineSpectrum(int barCount, ScalingStrategy scalingStrategy)
{
if (_spectrumProvider == null)
return null;
return new LineSpectrum(FftSize)
{
SpectrumProvider = _spectrumProvider,
UseAverage = true,
BarCount = barCount,
IsXLogScale = true,
ScalingStrategy = scalingStrategy
};
}
/// <summary>
/// Keeps the audio capture active, when not called for longer than 1 sec the capture will
/// stop capturing until Pulse is called again
/// </summary>
public void Pulse()
{
_mayStop = false;
if (!Running)
Start();
}
private void CheckStop(object sender, ElapsedEventArgs e)
{
if (_mayStop)
{
Logger.Debug("Stopping idle audio capture for device: {0}", Device?.FriendlyName ?? "default");
Stop();
}
else
_mayStop = true;
}
private void Start()
{
Logger.Debug("Starting audio capture for device: {0}", Device?.FriendlyName ?? "default");
try
{
Stop();
if (Type == MmDeviceType.Input)
{
_soundIn = Device != null
? new WasapiCapture {Device = Device}
: new WasapiCapture();
}
else
{
_soundIn = Device != null
? new WasapiLoopbackCapture {Device = Device}
: new WasapiLoopbackCapture();
}
_soundIn.Initialize();
var soundInSource = new SoundInSource(_soundIn);
_source = soundInSource.ToSampleSource().AppendSource(x => new GainSource(x), out _volume);
// create a spectrum provider which provides fft data based on some input
_spectrumProvider = new BasicSpectrumProvider(_source.WaveFormat.Channels, _source.WaveFormat.SampleRate,
FftSize);
// the SingleBlockNotificationStream is used to intercept the played samples
var notificationSource = new SingleBlockNotificationStream(_source);
// pass the intercepted samples as input data to the spectrumprovider (which will calculate a fft based on them)
notificationSource.SingleBlockRead += (s, a) => _spectrumProvider.Add(a.Left, a.Right);
var waveSource = notificationSource.ToWaveSource(16);
// We need to read from our source otherwise SingleBlockRead is never called and our spectrum provider is not populated
var buffer = new byte[waveSource.WaveFormat.BytesPerSecond / 2];
soundInSource.DataAvailable += (s, aEvent) =>
{
while (waveSource.Read(buffer, 0, buffer.Length) > 0)
{
}
};
_singleSpectrum = new SingleSpectrum(FftSize, _spectrumProvider);
_mayStop = false;
_disableTimer.Start();
_volumeTimer.Start();
_soundIn.Start();
Running = true;
}
catch (Exception e)
{
Logger.Warn(e, "Failed to start WASAPI audio capture");
}
}
private void Stop()
{
Running = false;
if (_soundIn != null)
{
_soundIn.Stop();
_soundIn.Dispose();
_soundIn = null;
}
if (_source != null)
{
_source.Dispose();
_source = null;
}
_disableTimer.Stop();
_volumeTimer.Stop();
}
}
}

View File

@ -1,133 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NAudio.CoreAudioApi;
using NAudio.Dsp;
using NAudio.Wave;
using Ninject.Extensions.Logging;
namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
{
public class AudioCaptureManager
{
private readonly SampleAggregator _sampleAggregator = new SampleAggregator(1024);
private readonly WasapiLoopbackCapture _waveIn;
private Complex[] _fft;
private DateTime _lastAudioUpdate;
private DateTime _lastRequest;
public AudioCaptureManager(ILogger logger)
{
Logger = logger;
Device = new MMDeviceEnumerator().EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active).FirstOrDefault();
_sampleAggregator.FftCalculated += FftCalculated;
_sampleAggregator.PerformFFT = true;
// Start listening for sound data
_waveIn = new WasapiLoopbackCapture();
_waveIn.DataAvailable += OnDataAvailable;
}
public ILogger Logger { get; set; }
public MMDevice Device { get; set; }
public bool Running { get; set; }
public void Start()
{
if (Running)
return;
try
{
_waveIn.StartRecording();
Running = true;
}
catch (Exception e)
{
Logger.Warn(e, "Failed to start WASAPI audio capture");
}
}
public void Stop()
{
if (!Running)
return;
try
{
_waveIn.StopRecording();
Running = false;
}
catch (Exception e)
{
Logger.Warn(e, "Failed to start WASAPI audio capture");
}
}
private void FftCalculated(object sender, FftEventArgs e)
{
_fft = e.Result;
}
private void OnDataAvailable(object sender, WaveInEventArgs e)
{
if (DateTime.Now - _lastAudioUpdate < TimeSpan.FromMilliseconds(40))
return;
if (DateTime.Now - _lastRequest > TimeSpan.FromSeconds(5))
{
Stop();
return;
}
_lastAudioUpdate = DateTime.Now;
var buffer = e.Buffer;
var bytesRecorded = e.BytesRecorded;
var bufferIncrement = _waveIn.WaveFormat.BlockAlign;
for (var index = 0; index < bytesRecorded; index += bufferIncrement)
{
var sample32 = BitConverter.ToSingle(buffer, index);
_sampleAggregator.Add(sample32);
}
}
public List<byte> GetSpectrumData(int lines)
{
_lastRequest = DateTime.Now;
if (!Running)
Start();
var spectrumData = new List<byte>();
if (_fft == null)
return spectrumData;
int x;
var b0 = 0;
for (x = 0; x < lines; x++)
{
float peak = 0;
var b1 = (int) Math.Pow(2, x*10.0/(lines - 1));
if (b1 > 1023)
b1 = 1023;
if (b1 <= b0)
b1 = b0 + 1;
for (; b0 < b1; b0++)
if (peak < _fft[1 + b0].X)
peak = _fft[1 + b0].X;
var y = (int) (Math.Sqrt(peak)*3*255 - 4);
if (y > 255)
y = 255;
if (y < 0)
y = 0;
spectrumData.Add((byte) y);
}
return spectrumData;
}
}
}

View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using CSCore.DSP;
namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
{
/// <summary>
/// BasicSpectrumProvider
/// </summary>
public class BasicSpectrumProvider : FftProvider, ISpectrumProvider
{
private readonly List<object> _contexts = new List<object>();
private readonly int _sampleRate;
public BasicSpectrumProvider(int channels, int sampleRate, FftSize fftSize)
: base(channels, fftSize)
{
if (sampleRate <= 0)
throw new ArgumentOutOfRangeException("sampleRate");
_sampleRate = sampleRate;
}
public int GetFftBandIndex(float frequency)
{
var fftSize = (int) FftSize;
var f = _sampleRate / 2.0;
// ReSharper disable once PossibleLossOfFraction
return (int) (frequency / f * (fftSize / 2));
}
public bool GetFftData(float[] fftResultBuffer, object context)
{
if (_contexts.Contains(context))
return false;
_contexts.Add(context);
GetFftData(fftResultBuffer);
return true;
}
public override void Add(float[] samples, int count)
{
base.Add(samples, count);
if (count > 0)
_contexts.Clear();
}
public override void Add(float left, float right)
{
base.Add(left, right);
_contexts.Clear();
}
}
}

View File

@ -1,17 +0,0 @@
using System;
using System.Diagnostics;
using NAudio.Dsp;
namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
{
public class FftEventArgs : EventArgs
{
[DebuggerStepThrough]
public FftEventArgs(Complex[] result)
{
Result = result;
}
public Complex[] Result { get; private set; }
}
}

View File

@ -0,0 +1,8 @@
namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
{
public interface ISpectrumProvider
{
bool GetFftData(float[] fftBuffer, object context);
int GetFftBandIndex(float frequency);
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using CSCore.DSP;
namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
{
public class LineSpectrum : SpectrumBase
{
private int _barCount;
public LineSpectrum(FftSize fftSize)
{
FftSize = fftSize;
}
public int BarCount
{
get { return _barCount; }
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException("value");
_barCount = value;
SpectrumResolution = value;
UpdateFrequencyMapping();
}
}
public List<double> GetLineValues(double height)
{
var fftBuffer = new float[(int) FftSize];
// get the fft result from the spectrum provider
if (!SpectrumProvider.GetFftData(fftBuffer, this))
return null;
var spectrumPoints = CalculateSpectrumPoints(height, fftBuffer);
return spectrumPoints?.Select(s => s.Value).ToList();
}
}
}

View File

@ -1,52 +0,0 @@
using System;
using NAudio.Dsp;
namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
{
public class SampleAggregator
{
private readonly FftEventArgs fftArgs;
// This Complex is NAudio's own!
private readonly Complex[] fftBuffer;
private readonly int fftLength;
private readonly int m;
private int fftPos;
public SampleAggregator(int fftLength)
{
if (!IsPowerOfTwo(fftLength))
throw new ArgumentException("FFT Length must be a power of two");
m = (int) Math.Log(fftLength, 2.0);
this.fftLength = fftLength;
fftBuffer = new Complex[fftLength];
fftArgs = new FftEventArgs(fftBuffer);
}
public bool PerformFFT { get; set; }
// FFT
public event EventHandler<FftEventArgs> FftCalculated;
private bool IsPowerOfTwo(int x)
{
return (x & (x - 1)) == 0;
}
public void Add(float value)
{
if (PerformFFT && FftCalculated != null)
{
// Remember the window function! There are many others as well.
fftBuffer[fftPos].X = (float) (value * FastFourierTransform.HammingWindow(fftPos, fftLength));
fftBuffer[fftPos].Y = 0; // This is always zero with audio.
fftPos++;
if (fftPos >= fftLength)
{
fftPos = 0;
FastFourierTransform.FFT(true, m, fftBuffer);
FftCalculated(this, fftArgs);
}
}
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using Artemis.Profiles.Layers.Models;
using CSCore.DSP;
namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
{
public sealed class SingleSpectrum : SpectrumBase
{
public SingleSpectrum(FftSize fftSize, ISpectrumProvider spectrumProvider)
{
SpectrumProvider = spectrumProvider;
SpectrumResolution = 1;
FftSize = fftSize;
UpdateFrequencyMapping();
}
public double? GetValue()
{
var fftBuffer = new float[(int)FftSize];
// get the fft result from the spectrum provider
if (SpectrumProvider == null || !SpectrumProvider.GetFftData(fftBuffer, this))
return null;
var spectrumPoints = CalculateSpectrumPoints(1, fftBuffer);
return spectrumPoints[0].Value;
}
}
}

View File

@ -0,0 +1,189 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using CSCore;
using CSCore.DSP;
namespace Artemis.Profiles.Layers.Types.Audio.AudioCapturing
{
public class SpectrumBase
{
private const int ScaleFactorLinear = 9;
protected const int ScaleFactorSqr = 2;
protected const double MinDbValue = -90;
protected const double MaxDbValue = 0;
protected const double DbScale = MaxDbValue - MinDbValue;
private int _fftSize;
private bool _isXLogScale;
private int _maxFftIndex;
private int _maximumFrequency = 20000;
private int _maximumFrequencyIndex;
private int _minimumFrequency = 20; //Default spectrum from 20Hz to 20kHz
private int _minimumFrequencyIndex;
private int[] _spectrumIndexMax;
private int[] _spectrumLogScaleIndexMax;
private ISpectrumProvider _spectrumProvider;
protected int SpectrumResolution;
public int MaximumFrequency
{
get { return _maximumFrequency; }
set
{
if (value <= MinimumFrequency)
throw new ArgumentOutOfRangeException("value",
"Value must not be less or equal the MinimumFrequency.");
_maximumFrequency = value;
UpdateFrequencyMapping();
}
}
public int MinimumFrequency
{
get { return _minimumFrequency; }
set
{
if (value < 0)
throw new ArgumentOutOfRangeException("value");
_minimumFrequency = value;
UpdateFrequencyMapping();
}
}
[Browsable(false)]
public ISpectrumProvider SpectrumProvider
{
get { return _spectrumProvider; }
set
{
if (value == null)
throw new ArgumentNullException("value");
_spectrumProvider = value;
}
}
public bool IsXLogScale
{
get { return _isXLogScale; }
set
{
_isXLogScale = value;
UpdateFrequencyMapping();
}
}
public ScalingStrategy ScalingStrategy { get; set; }
public bool UseAverage { get; set; }
[Browsable(false)]
public FftSize FftSize
{
get { return (FftSize) _fftSize; }
protected set
{
if ((int) Math.Log((int) value, 2) % 1 != 0)
throw new ArgumentOutOfRangeException("value");
_fftSize = (int) value;
_maxFftIndex = _fftSize / 2 - 1;
}
}
protected virtual void UpdateFrequencyMapping()
{
_maximumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(MaximumFrequency) + 1, _maxFftIndex);
_minimumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(MinimumFrequency), _maxFftIndex);
var actualResolution = SpectrumResolution;
var indexCount = _maximumFrequencyIndex - _minimumFrequencyIndex;
var linearIndexBucketSize = Math.Round(indexCount / (double) actualResolution, 3);
_spectrumIndexMax = _spectrumIndexMax.CheckBuffer(actualResolution, true);
_spectrumLogScaleIndexMax = _spectrumLogScaleIndexMax.CheckBuffer(actualResolution, true);
var maxLog = Math.Log(actualResolution, actualResolution);
for (var i = 1; i < actualResolution; i++)
{
var logIndex =
(int) ((maxLog - Math.Log(actualResolution + 1 - i, actualResolution + 1)) * indexCount) +
_minimumFrequencyIndex;
_spectrumIndexMax[i - 1] = _minimumFrequencyIndex + (int) (i * linearIndexBucketSize);
_spectrumLogScaleIndexMax[i - 1] = logIndex;
}
if (actualResolution > 0)
_spectrumIndexMax[_spectrumIndexMax.Length - 1] =
_spectrumLogScaleIndexMax[_spectrumLogScaleIndexMax.Length - 1] = _maximumFrequencyIndex;
}
protected virtual SpectrumPointData[] CalculateSpectrumPoints(double maxValue, float[] fftBuffer)
{
var dataPoints = new List<SpectrumPointData>();
double value0 = 0, value = 0;
double lastValue = 0;
var actualMaxValue = maxValue;
var spectrumPointIndex = 0;
for (var i = _minimumFrequencyIndex; i <= _maximumFrequencyIndex; i++)
{
switch (ScalingStrategy)
{
case ScalingStrategy.Decibel:
value0 = (20 * Math.Log10(fftBuffer[i]) - MinDbValue) / DbScale * actualMaxValue;
break;
case ScalingStrategy.Linear:
value0 = fftBuffer[i] * ScaleFactorLinear * actualMaxValue;
break;
case ScalingStrategy.Sqrt:
value0 = Math.Sqrt(fftBuffer[i]) * ScaleFactorSqr * actualMaxValue;
break;
}
var recalc = true;
value = Math.Max(0, Math.Max(value0, value));
while (spectrumPointIndex <= _spectrumIndexMax.Length - 1 &&
i ==
(IsXLogScale
? _spectrumLogScaleIndexMax[spectrumPointIndex]
: _spectrumIndexMax[spectrumPointIndex]))
{
if (!recalc)
value = lastValue;
if (value > maxValue)
value = maxValue;
if (UseAverage && spectrumPointIndex > 0)
value = (lastValue + value) / 2.0;
dataPoints.Add(new SpectrumPointData {SpectrumPointIndex = spectrumPointIndex, Value = value});
lastValue = value;
value = 0.0;
spectrumPointIndex++;
recalc = false;
}
//value = 0;
}
return dataPoints.ToArray();
}
[DebuggerDisplay("{Value}")]
protected struct SpectrumPointData
{
public int SpectrumPointIndex;
public double Value;
}
}
}

View File

@ -9,9 +9,23 @@ namespace Artemis.Profiles.Layers.Types.Audio
{ {
} }
public int Sensitivity { get; set; } [DefaultValue(MmDeviceType.Ouput)]
public double FadeSpeed { get; set; } public MmDeviceType DeviceType { get; set; }
[DefaultValue("Default")]
public string Device { get; set; }
[DefaultValue(Direction.BottomToTop)]
public Direction Direction { get; set; } public Direction Direction { get; set; }
[DefaultValue(ScalingStrategy.Decibel)]
public ScalingStrategy ScalingStrategy { get; set; }
}
public enum MmDeviceType
{
[Description("Ouput")] Ouput,
[Description("Input")] Input
} }
public enum Direction public enum Direction
@ -21,4 +35,11 @@ namespace Artemis.Profiles.Layers.Types.Audio
[Description("Left to right")] LeftToRight, [Description("Left to right")] LeftToRight,
[Description("Right to left")] RightToLeft [Description("Right to left")] RightToLeft
} }
public enum ScalingStrategy
{
[Description("Decibel")] Decibel,
[Description("Linear")] Linear,
[Description("Square root")] Sqrt
}
} }

View File

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

View File

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

View File

@ -1,42 +1,50 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Media;
using Artemis.Events;
using Artemis.Managers;
using Artemis.Modules.Abstract; using Artemis.Modules.Abstract;
using Artemis.Profiles.Layers.Abstract; using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Animations;
using Artemis.Profiles.Layers.Interfaces; using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models; using Artemis.Profiles.Layers.Models;
using Artemis.Profiles.Layers.Types.Audio.AudioCapturing; using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
using Artemis.Properties; using Artemis.Properties;
using Artemis.Utilities; using Artemis.Utilities;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels;
using Newtonsoft.Json; using CSCore.CoreAudioAPI;
using Ninject;
namespace Artemis.Profiles.Layers.Types.Audio namespace Artemis.Profiles.Layers.Types.Audio
{ {
public class AudioType : ILayerType public class AudioType : ILayerType
{ {
private readonly List<LayerModel> _audioLayers = new List<LayerModel>(); private readonly AudioCaptureManager _audioCaptureManager;
private readonly IKernel _kernel; private AudioCapture _audioCapture;
private DateTime _lastUpdate;
private int _lines; private int _lines;
private string _previousSettings; private LineSpectrum _lineSpectrum;
private List<double> _lineValues;
private AudioPropertiesModel _properties;
private bool _subscribed;
public AudioType(IKernel kernel, AudioCaptureManager audioCaptureManager) public AudioType(AudioCaptureManager audioCaptureManager)
{ {
_kernel = kernel; _audioCaptureManager = audioCaptureManager;
AudioCaptureManager = audioCaptureManager;
} }
[JsonIgnore] private void SubscribeToAudioChange()
public AudioCaptureManager AudioCaptureManager { get; set; } {
if (_subscribed)
return;
_audioCaptureManager.AudioDeviceChanged += OnAudioDeviceChanged;
_subscribed = true;
}
public string Name => "Keyboard - Audio visualization"; public string Name => "Keyboard - Audio visualization";
public bool ShowInEdtor => true; public bool ShowInEdtor => true;
public DrawType DrawType => DrawType.Keyboard; public DrawType DrawType => DrawType.Keyboard;
public int DrawScale => 4;
public ImageSource DrawThumbnail(LayerModel layer) public ImageSource DrawThumbnail(LayerModel layer)
{ {
@ -53,56 +61,109 @@ namespace Artemis.Profiles.Layers.Types.Audio
public void Draw(LayerModel layerModel, DrawingContext c) public void Draw(LayerModel layerModel, DrawingContext c)
{ {
lock (_audioLayers) if (_lineValues == null)
return;
var parentX = layerModel.X;
var parentY = layerModel.Y;
var direction = ((AudioPropertiesModel) layerModel.Properties).Direction;
// Create a geometry that will be formed by all the bars
var barGeometry = new GeometryGroup();
switch (direction)
{ {
foreach (var audioLayer in _audioLayers) case Direction.TopToBottom:
{ for (var index = 0; index < _lineValues.Count; index++)
// This is cheating but it ensures that the brush is drawn across the entire main-layer {
var oldWidth = audioLayer.Properties.Width; var clipRect = new Rect((parentX + index) * 4, parentY * 4, 4, _lineValues[index] * 4);
var oldHeight = audioLayer.Properties.Height; var barRect = new RectangleGeometry(clipRect);
var oldX = audioLayer.Properties.X; barGeometry.Children.Add(barRect);
var oldY = audioLayer.Properties.Y; }
break;
audioLayer.Properties.Width = layerModel.Properties.Width; case Direction.BottomToTop:
audioLayer.Properties.Height = layerModel.Properties.Height; for (var index = 0; index < _lineValues.Count; index++)
audioLayer.Properties.X = layerModel.Properties.X; {
audioLayer.Properties.Y = layerModel.Properties.Y; var clipRect = new Rect((parentX + index) * 4, parentY * 4, 4, _lineValues[index] * 4);
audioLayer.LayerType.Draw(audioLayer, c); clipRect.Y = clipRect.Y + layerModel.Height * 4 - clipRect.Height;
var barRect = new RectangleGeometry(clipRect);
audioLayer.Properties.Width = oldWidth; barGeometry.Children.Add(barRect);
audioLayer.Properties.Height = oldHeight; }
audioLayer.Properties.X = oldX; break;
audioLayer.Properties.Y = oldY; case Direction.LeftToRight:
} for (var index = 0; index < _lineValues.Count; index++)
{
var clipRect = new Rect((parentX + index) * 4, parentY * 4, 4, _lineValues[index] * 4);
var barRect = new RectangleGeometry(clipRect);
barGeometry.Children.Add(barRect);
}
break;
default:
for (var index = 0; index < _lineValues.Count; index++)
{
var clipRect = new Rect((parentX + index) * 4, parentY * 4, 4, _lineValues[index] * 4);
var barRect = new RectangleGeometry(clipRect);
barGeometry.Children.Add(barRect);
}
break;
} }
// Push the created geometry
c.PushClip(barGeometry);
BrushDraw(layerModel, c);
c.Pop();
} }
public void Update(LayerModel layerModel, ModuleDataModel dataModel, bool isPreview = false) public void Update(LayerModel layerModel, ModuleDataModel dataModel, bool isPreview = false)
{ {
layerModel.ApplyProperties(true); layerModel.ApplyProperties(true);
if (isPreview) var newProperties = (AudioPropertiesModel) layerModel.Properties;
if (_properties == null)
_properties = newProperties;
SubscribeToAudioChange();
if (_audioCapture == null || newProperties.Device != _properties.Device ||
newProperties.DeviceType != _properties.DeviceType)
{
var device = GetMmDevice();
if (device != null)
_audioCapture = _audioCaptureManager.GetAudioCapture(device, newProperties.DeviceType);
}
_properties = newProperties;
if (_audioCapture == null)
return; return;
lock (_audioLayers) _audioCapture.Pulse();
{
SetupLayers(layerModel);
var spectrumData = AudioCaptureManager.GetSpectrumData(_lines);
if (!spectrumData.Any())
return;
var settings = (AudioPropertiesModel) layerModel.Properties; var direction = ((AudioPropertiesModel) layerModel.Properties).Direction;
switch (settings.Direction)
{ int currentLines;
case Direction.TopToBottom: double currentHeight;
case Direction.BottomToTop: if (direction == Direction.BottomToTop || direction == Direction.TopToBottom)
ApplyVertical(spectrumData, settings); {
break; currentLines = (int) layerModel.Width;
case Direction.LeftToRight: currentHeight = layerModel.Height;
case Direction.RightToLeft:
ApplyHorizontal(spectrumData, settings);
break;
}
} }
else
{
currentLines = (int) layerModel.Height;
currentHeight = layerModel.Width;
}
if (_lines != currentLines || _lineSpectrum == null)
{
_lines = currentLines;
_lineSpectrum = _audioCapture.GetLineSpectrum(_lines, ScalingStrategy.Decibel);
if (_lineSpectrum == null)
return;
}
var newLineValues = _lineSpectrum?.GetLineValues(currentHeight);
if (newLineValues != null)
_lineValues = newLineValues;
} }
public void SetupProperties(LayerModel layerModel) public void SetupProperties(LayerModel layerModel)
@ -112,8 +173,10 @@ namespace Artemis.Profiles.Layers.Types.Audio
layerModel.Properties = new AudioPropertiesModel(layerModel.Properties) layerModel.Properties = new AudioPropertiesModel(layerModel.Properties)
{ {
FadeSpeed = 0.2, DeviceType = MmDeviceType.Ouput,
Sensitivity = 2 Device = "Default",
Direction = Direction.BottomToTop,
ScalingStrategy = ScalingStrategy.Decibel
}; };
} }
@ -125,170 +188,65 @@ namespace Artemis.Profiles.Layers.Types.Audio
return new AudioPropertiesViewModel(layerEditorViewModel); return new AudioPropertiesViewModel(layerEditorViewModel);
} }
private void ApplyVertical(List<byte> spectrumData, AudioPropertiesModel settings) public void BrushDraw(LayerModel layerModel, DrawingContext c)
{ {
var index = 0; // If an animation is present, let it handle the drawing
foreach (var audioLayer in _audioLayers) if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation))
{ {
int height; layerModel.LayerAnimation.Draw(layerModel, c, DrawScale);
if (spectrumData.Count > index)
height = (int) Math.Round(spectrumData[index]/2.55);
else
height = 0;
// Apply Sensitivity setting
height = height*settings.Sensitivity;
var newHeight = settings.Height/100.0*height;
if (newHeight >= audioLayer.Properties.Height)
audioLayer.Properties.Height = newHeight;
else
audioLayer.Properties.Height = audioLayer.Properties.Height - settings.FadeSpeed;
if (audioLayer.Properties.Height < 0)
audioLayer.Properties.Height = 0;
// Reverse the direction if settings require it
if (settings.Direction == Direction.BottomToTop)
audioLayer.Properties.Y = settings.Y + (settings.Height - audioLayer.Properties.Height);
FakeUpdate(settings, audioLayer);
index++;
}
}
private void ApplyHorizontal(List<byte> spectrumData, AudioPropertiesModel settings)
{
var index = 0;
foreach (var audioLayer in _audioLayers)
{
int width;
if (spectrumData.Count > index)
width = (int) Math.Round(spectrumData[index]/2.55);
else
width = 0;
// Apply Sensitivity setting
width = width*settings.Sensitivity;
var newWidth = settings.Width/100.0*width;
if (newWidth >= audioLayer.Properties.Width)
audioLayer.Properties.Width = newWidth;
else
audioLayer.Properties.Width = audioLayer.Properties.Width - settings.FadeSpeed;
if (audioLayer.Properties.Width < 0)
audioLayer.Properties.Width = 0;
audioLayer.Properties.Brush = settings.Brush;
audioLayer.Properties.Contain = false;
// Reverse the direction if settings require it
if (settings.Direction == Direction.RightToLeft)
audioLayer.Properties.X = settings.X + (settings.Width - audioLayer.Properties.Width);
FakeUpdate(settings, audioLayer);
index++;
}
}
/// <summary>
/// Updates the layer manually faking the width and height for a properly working animation
/// </summary>
/// <param name="settings"></param>
/// <param name="audioLayer"></param>
private static void FakeUpdate(LayerPropertiesModel settings, LayerModel audioLayer)
{
// Call the regular update
audioLayer.LayerType?.Update(audioLayer, null);
// Store the original height and width
var oldHeight = audioLayer.Properties.Height;
var oldWidth = audioLayer.Properties.Width;
// Fake the height and width and update the animation
audioLayer.Properties.Width = settings.Width;
audioLayer.Properties.Height = settings.Height;
audioLayer.LastRender = DateTime.Now;
audioLayer.LayerAnimation?.Update(audioLayer, true);
// Restore the height and width
audioLayer.Properties.Height = oldHeight;
audioLayer.Properties.Width = oldWidth;
}
/// <summary>
/// Sets up the inner layers when the settings have changed
/// </summary>
/// <param name="layerModel"></param>
private void SetupLayers(LayerModel layerModel)
{
// Checking on settings update is expensive, only do it every second
if (DateTime.Now - _lastUpdate < TimeSpan.FromSeconds(1))
return; return;
_lastUpdate = DateTime.Now; }
var settings = (AudioPropertiesModel) layerModel.Properties; // Otherwise draw the rectangle with its layer.AppliedProperties dimensions and brush
var currentSettings = JsonConvert.SerializeObject(settings, Formatting.Indented); var rect = layerModel.Properties.Contain
var currentType = _audioLayers.FirstOrDefault()?.LayerAnimation?.GetType(); ? layerModel.LayerRect()
: new Rect(layerModel.Properties.X * 4, layerModel.Properties.Y * 4,
layerModel.Properties.Width * 4, layerModel.Properties.Height * 4);
if (currentSettings == _previousSettings && (layerModel.LayerAnimation.GetType() == currentType)) var clip = layerModel.LayerRect(DrawScale);
// Can't meddle with the original brush because it's frozen.
var brush = layerModel.Brush.Clone();
brush.Opacity = layerModel.Opacity;
c.PushClip(new RectangleGeometry(clip));
c.DrawRectangle(brush, null, rect);
c.Pop();
}
private void OnAudioDeviceChanged(object sender, AudioDeviceChangedEventArgs e)
{
if (_properties == null || _properties.Device != "Default")
return; return;
_previousSettings = JsonConvert.SerializeObject(settings, Formatting.Indented); if (_properties.DeviceType == MmDeviceType.Input)
_audioLayers.Clear();
switch (settings.Direction)
{ {
case Direction.TopToBottom: if (e.DefaultRecording != null)
case Direction.BottomToTop: _audioCapture = _audioCaptureManager.GetAudioCapture(e.DefaultRecording, MmDeviceType.Input);
SetupVertical(layerModel);
break;
case Direction.LeftToRight:
case Direction.RightToLeft:
SetupHorizontal(layerModel);
break;
default:
throw new ArgumentOutOfRangeException();
} }
else
{
if (e.DefaultPlayback != null)
_audioCapture = _audioCaptureManager.GetAudioCapture(e.DefaultPlayback, MmDeviceType.Ouput);
}
_lines = 0;
} }
private void SetupVertical(LayerModel layerModel) private MMDevice GetMmDevice()
{ {
_lines = (int) layerModel.Properties.Width; if (_properties == null)
for (var i = 0; i < _lines; i++) return null;
{
var layer = LayerModel.CreateLayer();
layer.Properties.X = layerModel.Properties.X + i;
layer.Properties.Y = layerModel.Properties.Y;
layer.Properties.Width = 1;
layer.Properties.Height = 0;
layer.Properties.AnimationSpeed = layerModel.Properties.AnimationSpeed;
layer.Properties.Brush = layerModel.Properties.Brush;
layer.Properties.Contain = false;
layer.LayerAnimation = (ILayerAnimation) _kernel.Get(layerModel.LayerAnimation.GetType());
_audioLayers.Add(layer); if (_properties.DeviceType == MmDeviceType.Input)
layer.Update(null, false, true); return _properties.Device == "Default"
} ? MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia)
} : MMDeviceEnumerator.EnumerateDevices(DataFlow.Capture)
.FirstOrDefault(d => d.FriendlyName == _properties.Device);
private void SetupHorizontal(LayerModel layerModel) return _properties.Device == "Default"
{ ? MMDeviceEnumerator.TryGetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia)
_lines = (int) layerModel.Properties.Height; : MMDeviceEnumerator.EnumerateDevices(DataFlow.Render)
for (var i = 0; i < _lines; i++) .FirstOrDefault(d => d.FriendlyName == _properties.Device);
{
var layer = LayerModel.CreateLayer();
layer.Properties.X = layerModel.Properties.X;
layer.Properties.Y = layerModel.Properties.Y + i;
layer.Properties.Width = 0;
layer.Properties.Height = 1;
layer.Properties.AnimationSpeed = layerModel.Properties.AnimationSpeed;
layer.Properties.Brush = layerModel.Properties.Brush;
layer.Properties.Contain = false;
layer.LayerAnimation = (ILayerAnimation) _kernel.Get(layerModel.LayerAnimation.GetType());
_audioLayers.Add(layer);
layer.Update(null, false, true);
}
} }
} }
} }

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Windows.Media;
using Artemis.Profiles.Layers.Models;
namespace Artemis.Profiles.Layers.Types.ConicalBrush
{
public class ConicalBrushPropertiesModel : LayerPropertiesModel
{
#region Properties & Fields
public IList<Tuple<double, Color>> GradientStops { get; set; }
#endregion
#region Constructors
public ConicalBrushPropertiesModel(LayerPropertiesModel properties = null)
: base(properties)
{ }
#endregion
}
}

View File

@ -0,0 +1,80 @@
<UserControl x:Class="Artemis.Profiles.Layers.Types.ConicalBrush.ConicalBrushPropertiesView"
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:controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:ncore="http://schemas.ncore.com/wpf/xaml/colorbox"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<!-- Colors -->
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<!-- Animation -->
<TextBlock Grid.Row="0" Grid.Column="0" Margin="10" FontSize="13.333" Text="Animation:"
VerticalAlignment="Center"
Height="18" />
<ComboBox Grid.Row="0" Grid.Column="1" Margin="10,10,10,0" x:Name="LayerAnimations" VerticalAlignment="Top"
Height="22">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name, Mode=OneWay}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Animation Speed -->
<TextBlock Grid.Row="0" Grid.Column="2" Margin="10" FontSize="13.333" Text="Animation speed:"
VerticalAlignment="Center" Height="18" />
<Slider x:Name="RotationSpeed" Grid.Row="0" Grid.Column="3" VerticalAlignment="Center"
TickPlacement="None" TickFrequency="0.05"
Value="{Binding Path=LayerModel.Properties.AnimationSpeed, Mode=TwoWay}" Minimum="0.05" Maximum="3"
SmallChange="0" IsSnapToTickEnabled="True" Margin="10,12,10,2" Height="24" />
<!-- ClippingType -->
<TextBlock Grid.Row="1" Grid.Column="0" Margin="10, 13, 10, 10" FontSize="13.333" Text="Clipping type:"
VerticalAlignment="Center" Height="23" />
<controls:ToggleSwitch IsChecked="{Binding Path=LayerModel.Properties.Contain, Mode=TwoWay}"
Grid.Row="1"
Grid.Column="1" OnLabel="Contain" OffLabel="Cut-off" Margin="10,1,5,1"
VerticalAlignment="Center"
Height="36" />
<!-- Colors -->
<StackPanel Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Orientation="Horizontal" x:Name="ShowBrush">
<TextBlock Margin="10,13,10,0" FontSize="13.333" Text="Color(s):"
VerticalAlignment="Top" Height="18" Width="130" />
<Border Margin="10" BorderBrush="{StaticResource ControlBorderBrush}"
BorderThickness="1" SnapsToDevicePixels="True" ToolTip="Click to edit">
<ncore:ColorBox Brush="{Binding Brush, Mode=TwoWay}" ShowNone="False" Height="24" Width="134"
VerticalAlignment="Top" ShowLinear="True" ShowRadial="False" ShowSolid="False" />
</Border>
</StackPanel>
<!-- Dynamic -->
<Label Grid.Row="3" Grid.Column="0" FontSize="20" HorizontalAlignment="Left"
Content="Dynamic" Width="97" VerticalAlignment="Bottom" />
<!-- Dynamic property views -->
<ContentControl Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="4" x:Name="HeightProperties" />
<ContentControl Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="4" x:Name="WidthProperties" />
<ContentControl Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="4" x:Name="OpacityProperties" />
<ContentControl Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="4" x:Name="LayerTweenViewModel" />
</Grid>
</UserControl>

View File

@ -0,0 +1,12 @@
using System.Windows.Controls;
namespace Artemis.Profiles.Layers.Types.ConicalBrush
{
public partial class ConicalBrushPropertiesView : UserControl
{
public ConicalBrushPropertiesView()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,70 @@
using System.Linq;
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces;
using Artemis.Utilities;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Caliburn.Micro;
namespace Artemis.Profiles.Layers.Types.ConicalBrush
{
public class ConicalBrushPropertiesViewModel : LayerPropertiesViewModel
{
#region Properties & Fields
private ILayerAnimation _selectedLayerAnimation;
public BindableCollection<GeneralHelpers.PropertyCollection> DataModelProps { get; set; }
public BindableCollection<ILayerAnimation> LayerAnimations { get; set; }
public LayerDynamicPropertiesViewModel HeightProperties { get; set; }
public LayerDynamicPropertiesViewModel WidthProperties { get; set; }
public LayerDynamicPropertiesViewModel OpacityProperties { get; set; }
public LayerTweenViewModel LayerTweenViewModel { get; set; }
public ILayerAnimation SelectedLayerAnimation
{
get { return _selectedLayerAnimation; }
set
{
if (Equals(value, _selectedLayerAnimation)) return;
_selectedLayerAnimation = value;
NotifyOfPropertyChange(() => SelectedLayerAnimation);
}
}
#endregion
#region Constructors
public ConicalBrushPropertiesViewModel(LayerEditorViewModel editorVm)
: base(editorVm)
{
LayerAnimations = new BindableCollection<ILayerAnimation>(editorVm.LayerAnimations);
HeightProperties = new LayerDynamicPropertiesViewModel("Height", editorVm);
WidthProperties = new LayerDynamicPropertiesViewModel("Width", editorVm);
OpacityProperties = new LayerDynamicPropertiesViewModel("Opacity", editorVm);
LayerTweenViewModel = new LayerTweenViewModel(editorVm);
SelectedLayerAnimation =
LayerAnimations.FirstOrDefault(l => l.Name == editorVm.ProposedLayer.LayerAnimation?.Name) ??
LayerAnimations.First(l => l.Name == "None");
}
#endregion
#region Methods
public override void ApplyProperties()
{
HeightProperties.Apply(LayerModel);
WidthProperties.Apply(LayerModel);
OpacityProperties.Apply(LayerModel);
LayerModel.Properties.Brush = Brush;
LayerModel.LayerAnimation = SelectedLayerAnimation;
}
#endregion
}
}

View File

@ -0,0 +1,132 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Artemis.Modules.Abstract;
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Animations;
using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models;
using Artemis.Profiles.Layers.Types.ConicalBrush.Drawing;
using Artemis.ViewModels;
namespace Artemis.Profiles.Layers.Types.ConicalBrush
{
public class ConicalBrushType : ILayerType
{
#region Properties & Fields
private ConicalGradientDrawer _conicalGradientDrawer;
private ConicalGradientDrawer _conicalGradientDrawerThumbnail;
public string Name => "Conical Brush";
public bool ShowInEdtor => true;
public DrawType DrawType => DrawType.Keyboard;
public int DrawScale => 4;
#endregion
public ConicalBrushType()
{
_conicalGradientDrawer = new ConicalGradientDrawer();
_conicalGradientDrawerThumbnail = new ConicalGradientDrawer(18, 18);
}
#region Methods
public ImageSource DrawThumbnail(LayerModel layer)
{
_conicalGradientDrawerThumbnail.GradientStops = GetGradientStops(layer.Brush).Select(x => new Tuple<double, Color>(x.Offset, x.Color)).ToList();
_conicalGradientDrawerThumbnail.Update();
Rect thumbnailRect = new Rect(0, 0, 18, 18);
DrawingVisual visual = new DrawingVisual();
using (DrawingContext c = visual.RenderOpen())
if (_conicalGradientDrawerThumbnail.Brush != null)
c.DrawRectangle(_conicalGradientDrawerThumbnail.Brush.Clone(), new Pen(new SolidColorBrush(Colors.White), 1), thumbnailRect);
DrawingImage image = new DrawingImage(visual.Drawing);
return image;
}
public void Draw(LayerModel layerModel, DrawingContext c)
{
ConicalBrushPropertiesModel properties = layerModel.Properties as ConicalBrushPropertiesModel;
if (properties == null) return;
Brush origBrush = layerModel.Brush;
_conicalGradientDrawer.GradientStops = GetGradientStops(layerModel.Brush).Select(x => new Tuple<double, Color>(x.Offset, x.Color)).ToList();
_conicalGradientDrawer.Update();
layerModel.Brush = _conicalGradientDrawer.Brush;
// If an animation is present, let it handle the drawing
if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation))
{
layerModel.LayerAnimation.Draw(layerModel, c, DrawScale);
return;
}
// Otherwise draw the rectangle with its layer.AppliedProperties dimensions and brush
Rect rect = layerModel.Properties.Contain
? layerModel.LayerRect(DrawScale)
: new Rect(layerModel.Properties.X * DrawScale, layerModel.Properties.Y * DrawScale,
layerModel.Properties.Width * DrawScale, layerModel.Properties.Height * DrawScale);
Rect clip = layerModel.LayerRect();
// Can't meddle with the original brush because it's frozen.
Brush brush = layerModel.Brush.Clone();
brush.Opacity = layerModel.Opacity;
c.PushClip(new RectangleGeometry(clip));
c.DrawRectangle(brush, null, rect);
c.Pop();
layerModel.Brush = origBrush;
}
public void Update(LayerModel layerModel, ModuleDataModel dataModel, bool isPreview = false)
{
layerModel.ApplyProperties(true);
if (isPreview || dataModel == null)
return;
// If not previewing, apply dynamic properties according to datamodel
foreach (DynamicPropertiesModel dynamicProperty in layerModel.Properties.DynamicProperties)
dynamicProperty.ApplyProperty(dataModel, layerModel);
}
public void SetupProperties(LayerModel layerModel)
{
if (layerModel.Properties is ConicalBrushPropertiesModel)
return;
layerModel.Properties = new ConicalBrushPropertiesModel(layerModel.Properties);
}
public LayerPropertiesViewModel SetupViewModel(LayerEditorViewModel layerEditorViewModel, LayerPropertiesViewModel layerPropertiesViewModel)
{
return (layerPropertiesViewModel as ConicalBrushPropertiesViewModel) ?? new ConicalBrushPropertiesViewModel(layerEditorViewModel);
}
private GradientStopCollection GetGradientStops(Brush brush)
{
LinearGradientBrush linearBrush = brush as LinearGradientBrush;
if (linearBrush != null)
return linearBrush.GradientStops;
RadialGradientBrush radialBrush = brush as RadialGradientBrush;
if (radialBrush != null)
return radialBrush.GradientStops;
SolidColorBrush solidBrush = brush as SolidColorBrush;
if (solidBrush != null)
return new GradientStopCollection(new[] { new GradientStop(solidBrush.Color, 0), new GradientStop(solidBrush.Color, 1) });
return null;
}
#endregion
}
}

View File

@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Brush = System.Windows.Media.Brush;
using Color = System.Windows.Media.Color;
namespace Artemis.Profiles.Layers.Types.ConicalBrush.Drawing
{
public class ConicalGradientDrawer
{
#region Constants
private static readonly double ORIGIN = Math.Atan2(-1, 0);
#endregion
#region Properties & Fields
private int _width;
private int _height;
private bool _isDirty = true;
private int _lastGradientHash;
private WriteableBitmap _bitmap;
private IList<Tuple<double, Color>> _gradientStops;
public IList<Tuple<double, Color>> GradientStops
{
set
{
int hash = GetHash(value);
if (_lastGradientHash != hash)
{
_gradientStops = FixGradientStops(value);
_lastGradientHash = hash;
_isDirty = true;
}
}
}
private PointF _center = new PointF(0.5f, 0.5f);
public PointF Center
{
get { return _center; }
set
{
if (_center != value)
{
_center = value;
_isDirty = true;
}
}
}
public Brush Brush { get; private set; }
#endregion
#region Constructors
public ConicalGradientDrawer()
: this(100, 100)
{ }
public ConicalGradientDrawer(int width, int height)
{
this._width = width;
this._height = height;
}
#endregion
#region Methods
public void Update(bool force = false)
{
if (!_isDirty && !force) return;
if (_bitmap == null)
CreateBrush();
unsafe
{
_bitmap.Lock();
byte* buffer = (byte*)_bitmap.BackBuffer.ToPointer();
for (int y = 0; y < _height; y++)
for (int x = 0; x < _width; x++)
{
int offset = (((y * _width) + x) * 4);
double gradientOffset = CalculateGradientOffset(x, y, _width * Center.X, _height * Center.Y);
GetColor(_gradientStops, gradientOffset,
ref buffer[offset + 3], ref buffer[offset + 2],
ref buffer[offset + 1], ref buffer[offset]);
}
_bitmap.AddDirtyRect(new Int32Rect(0, 0, _width, _height));
_bitmap.Unlock();
}
_isDirty = false;
}
private void CreateBrush()
{
_bitmap = new WriteableBitmap(_width, _height, 96, 96, PixelFormats.Bgra32, null);
Brush = new ImageBrush(_bitmap) { Stretch = Stretch.UniformToFill };
}
private double CalculateGradientOffset(double x, double y, double centerX, double centerY)
{
double angle = Math.Atan2(y - centerY, x - centerX) - ORIGIN;
if (angle < 0) angle += Math.PI * 2;
return angle / (Math.PI * 2);
}
private static void GetColor(IList<Tuple<double, Color>> gradientStops, double offset, ref byte colA, ref byte colR, ref byte colG, ref byte colB)
{
if (gradientStops.Count == 0)
{
colA = 0;
colR = 0;
colG = 0;
colB = 0;
return;
}
if (gradientStops.Count == 1)
{
Color color = gradientStops.First().Item2;
colA = color.A;
colR = color.R;
colG = color.G;
colB = color.B;
return;
}
Tuple<double, Color> beforeStop = null;
double afterOffset = -1;
Color afterColor = default(Color);
for (int i = 0; i < gradientStops.Count; i++)
{
Tuple<double, Color> gradientStop = gradientStops[i];
double o = gradientStop.Item1;
if (o <= offset)
beforeStop = gradientStop;
if (o >= offset)
{
afterOffset = gradientStop.Item1;
afterColor = gradientStop.Item2;
break;
}
}
double beforeOffset = beforeStop.Item1;
Color beforeColor = beforeStop.Item2;
double blendFactor = 0f;
if (beforeOffset != afterOffset)
blendFactor = ((offset - beforeOffset) / (afterOffset - beforeOffset));
colA = (byte)((afterColor.A - beforeColor.A) * blendFactor + beforeColor.A);
colR = (byte)((afterColor.R - beforeColor.R) * blendFactor + beforeColor.R);
colG = (byte)((afterColor.G - beforeColor.G) * blendFactor + beforeColor.G);
colB = (byte)((afterColor.B - beforeColor.B) * blendFactor + beforeColor.B);
}
private static IList<Tuple<double, Color>> FixGradientStops(IList<Tuple<double, Color>> gradientStops)
{
if (gradientStops == null) return new List<Tuple<double, Color>>();
List<Tuple<double, Color>> stops = gradientStops.OrderBy(x => x.Item1).ToList();
Tuple<double, Color> firstStop = stops.First();
if (firstStop.Item1 > 0)
stops.Insert(0, new Tuple<double, Color>(0, firstStop.Item2));
Tuple<double, Color> lastStop = stops.Last();
if (lastStop.Item1 < 1)
stops.Add(new Tuple<double, Color>(1, lastStop.Item2));
return stops;
}
private static int GetHash(IList<Tuple<double, Color>> sequence)
{
unchecked
{
return sequence.Aggregate(487, (current, item) => (((current * 31) + item.Item1.GetHashCode()) * 31) + item.Item2.GetHashCode());
}
}
#endregion
}
}

View File

@ -1,4 +1,5 @@
using Artemis.Profiles.Layers.Abstract; using Artemis.Profiles.Layers.Abstract;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Folder namespace Artemis.Profiles.Layers.Types.Folder

View File

@ -6,6 +6,7 @@ using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models; using Artemis.Profiles.Layers.Models;
using Artemis.Properties; using Artemis.Properties;
using Artemis.Utilities; using Artemis.Utilities;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Folder namespace Artemis.Profiles.Layers.Types.Folder
@ -16,6 +17,7 @@ namespace Artemis.Profiles.Layers.Types.Folder
public bool ShowInEdtor => false; public bool ShowInEdtor => false;
// FolderType pretents to be a keyboard so it's children get drawn // FolderType pretents to be a keyboard so it's children get drawn
public DrawType DrawType => DrawType.Keyboard; public DrawType DrawType => DrawType.Keyboard;
public int DrawScale => 1;
public ImageSource DrawThumbnail(LayerModel layer) public ImageSource DrawThumbnail(LayerModel layer)
{ {

View File

@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using Artemis.Profiles.Layers.Abstract; using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces; using Artemis.Profiles.Layers.Interfaces;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
using Caliburn.Micro; using Caliburn.Micro;

View File

@ -8,6 +8,7 @@ using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models; using Artemis.Profiles.Layers.Models;
using Artemis.Properties; using Artemis.Properties;
using Artemis.Utilities; using Artemis.Utilities;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Generic namespace Artemis.Profiles.Layers.Types.Generic
@ -17,6 +18,7 @@ namespace Artemis.Profiles.Layers.Types.Generic
public string Name => "Generic (Logitech)"; public string Name => "Generic (Logitech)";
public bool ShowInEdtor => false; public bool ShowInEdtor => false;
public DrawType DrawType => DrawType.Generic; public DrawType DrawType => DrawType.Generic;
public int DrawScale => 1;
public ImageSource DrawThumbnail(LayerModel layer) public ImageSource DrawThumbnail(LayerModel layer)
{ {
@ -34,12 +36,12 @@ namespace Artemis.Profiles.Layers.Types.Generic
// If an animation is present, let it handle the drawing // If an animation is present, let it handle the drawing
if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation)) if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation))
{ {
layerModel.LayerAnimation.Draw(layerModel, c); layerModel.LayerAnimation.Draw(layerModel, c, DrawScale);
return; return;
} }
// Otherwise draw the rectangle with its applied dimensions and brush // Otherwise draw the rectangle with its applied dimensions and brush
var rect = layerModel.LayerRect(); var rect = layerModel.LayerRect(DrawScale);
// Can't meddle with the original brush because it's frozen. // Can't meddle with the original brush because it's frozen.
var brush = layerModel.Brush.Clone(); var brush = layerModel.Brush.Clone();

View File

@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using Artemis.Profiles.Layers.Abstract; using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces; using Artemis.Profiles.Layers.Interfaces;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
using Caliburn.Micro; using Caliburn.Micro;

View File

@ -8,6 +8,7 @@ using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models; using Artemis.Profiles.Layers.Models;
using Artemis.Properties; using Artemis.Properties;
using Artemis.Utilities; using Artemis.Utilities;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Headset namespace Artemis.Profiles.Layers.Types.Headset
@ -17,6 +18,7 @@ namespace Artemis.Profiles.Layers.Types.Headset
public string Name => "Headset"; public string Name => "Headset";
public bool ShowInEdtor => false; public bool ShowInEdtor => false;
public DrawType DrawType => DrawType.Headset; public DrawType DrawType => DrawType.Headset;
public int DrawScale => 1;
public ImageSource DrawThumbnail(LayerModel layer) public ImageSource DrawThumbnail(LayerModel layer)
{ {
@ -34,12 +36,12 @@ namespace Artemis.Profiles.Layers.Types.Headset
// If an animation is present, let it handle the drawing // If an animation is present, let it handle the drawing
if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation)) if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation))
{ {
layerModel.LayerAnimation.Draw(layerModel, c); layerModel.LayerAnimation.Draw(layerModel, c, DrawScale);
return; return;
} }
// Otherwise draw the rectangle with its applied dimensions and brush // Otherwise draw the rectangle with its applied dimensions and brush
var rect = layerModel.LayerRect(); var rect = layerModel.LayerRect(DrawScale);
// Can't meddle with the original brush because it's frozen. // Can't meddle with the original brush because it's frozen.
var brush = layerModel.Brush.Clone(); var brush = layerModel.Brush.Clone();

View File

@ -1,4 +1,5 @@
using Artemis.Profiles.Layers.Abstract; using Artemis.Profiles.Layers.Abstract;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.KeyPress namespace Artemis.Profiles.Layers.Types.KeyPress

View File

@ -13,6 +13,7 @@ using Artemis.Profiles.Layers.Models;
using Artemis.Properties; using Artemis.Properties;
using Artemis.Utilities; using Artemis.Utilities;
using Artemis.Utilities.Keyboard; using Artemis.Utilities.Keyboard;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.KeyPress namespace Artemis.Profiles.Layers.Types.KeyPress
@ -22,6 +23,7 @@ namespace Artemis.Profiles.Layers.Types.KeyPress
private readonly DeviceManager _deviceManager; private readonly DeviceManager _deviceManager;
private List<LayerModel> _keyPressLayers; private List<LayerModel> _keyPressLayers;
private LayerModel _layerModel; private LayerModel _layerModel;
public int DrawScale => 4;
public KeyPressType(DeviceManager deviceManager) public KeyPressType(DeviceManager deviceManager)
{ {

View File

@ -3,6 +3,7 @@ using System.Windows.Forms;
using Artemis.Profiles.Layers.Abstract; using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces; using Artemis.Profiles.Layers.Interfaces;
using Artemis.Utilities; using Artemis.Utilities;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
using Caliburn.Micro; using Caliburn.Micro;

View File

@ -5,6 +5,7 @@ using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Animations; using Artemis.Profiles.Layers.Animations;
using Artemis.Profiles.Layers.Interfaces; using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models; using Artemis.Profiles.Layers.Models;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Keyboard namespace Artemis.Profiles.Layers.Types.Keyboard
@ -14,6 +15,7 @@ namespace Artemis.Profiles.Layers.Types.Keyboard
public string Name => "Keyboard"; public string Name => "Keyboard";
public bool ShowInEdtor => true; public bool ShowInEdtor => true;
public DrawType DrawType => DrawType.Keyboard; public DrawType DrawType => DrawType.Keyboard;
public int DrawScale => 4;
public ImageSource DrawThumbnail(LayerModel layer) public ImageSource DrawThumbnail(LayerModel layer)
{ {
@ -38,7 +40,7 @@ namespace Artemis.Profiles.Layers.Types.Keyboard
// If an animation is present, let it handle the drawing // If an animation is present, let it handle the drawing
if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation)) if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation))
{ {
layerModel.LayerAnimation.Draw(layerModel, c); layerModel.LayerAnimation.Draw(layerModel, c, DrawScale);
return; return;
} }
@ -48,7 +50,7 @@ namespace Artemis.Profiles.Layers.Types.Keyboard
: new Rect(layerModel.Properties.X*4, layerModel.Properties.Y*4, : new Rect(layerModel.Properties.X*4, layerModel.Properties.Y*4,
layerModel.Properties.Width*4, layerModel.Properties.Height*4); layerModel.Properties.Width*4, layerModel.Properties.Height*4);
var clip = layerModel.LayerRect(); var clip = layerModel.LayerRect(DrawScale);
// Can't meddle with the original brush because it's frozen. // Can't meddle with the original brush because it's frozen.
var brush = layerModel.Brush.Clone(); var brush = layerModel.Brush.Clone();

View File

@ -9,6 +9,7 @@ using Artemis.Profiles.Layers.Models;
using Artemis.Profiles.Layers.Types.Keyboard; using Artemis.Profiles.Layers.Types.Keyboard;
using Artemis.Properties; using Artemis.Properties;
using Artemis.Utilities; using Artemis.Utilities;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.KeyboardGif namespace Artemis.Profiles.Layers.Types.KeyboardGif
@ -18,6 +19,7 @@ namespace Artemis.Profiles.Layers.Types.KeyboardGif
public string Name => "Keyboard - GIF"; public string Name => "Keyboard - GIF";
public bool ShowInEdtor => true; public bool ShowInEdtor => true;
public DrawType DrawType => DrawType.Keyboard; public DrawType DrawType => DrawType.Keyboard;
public int DrawScale => 4;
public ImageSource DrawThumbnail(LayerModel layer) public ImageSource DrawThumbnail(LayerModel layer)
{ {

View File

@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using Artemis.Profiles.Layers.Abstract; using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces; using Artemis.Profiles.Layers.Interfaces;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
using Caliburn.Micro; using Caliburn.Micro;

View File

@ -8,6 +8,7 @@ using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models; using Artemis.Profiles.Layers.Models;
using Artemis.Properties; using Artemis.Properties;
using Artemis.Utilities; using Artemis.Utilities;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Mouse namespace Artemis.Profiles.Layers.Types.Mouse
@ -17,6 +18,7 @@ namespace Artemis.Profiles.Layers.Types.Mouse
public string Name => "Mouse"; public string Name => "Mouse";
public bool ShowInEdtor => false; public bool ShowInEdtor => false;
public DrawType DrawType => DrawType.Mouse; public DrawType DrawType => DrawType.Mouse;
public int DrawScale => 1;
public ImageSource DrawThumbnail(LayerModel layer) public ImageSource DrawThumbnail(LayerModel layer)
{ {
@ -36,12 +38,12 @@ namespace Artemis.Profiles.Layers.Types.Mouse
// If an animation is present, let it handle the drawing // If an animation is present, let it handle the drawing
if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation)) if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation))
{ {
layerModel.LayerAnimation.Draw(layerModel, c); layerModel.LayerAnimation.Draw(layerModel, c, DrawScale);
return; return;
} }
// Otherwise draw the rectangle with its applied dimensions and brush // Otherwise draw the rectangle with its applied dimensions and brush
var rect = layerModel.LayerRect(); var rect = layerModel.LayerRect(DrawScale);
// Can't meddle with the original brush because it's frozen. // Can't meddle with the original brush because it's frozen.
var brush = layerModel.Brush.Clone(); var brush = layerModel.Brush.Clone();

View File

@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using Artemis.Profiles.Layers.Abstract; using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces; using Artemis.Profiles.Layers.Interfaces;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
using Caliburn.Micro; using Caliburn.Micro;

View File

@ -8,6 +8,7 @@ using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models; using Artemis.Profiles.Layers.Models;
using Artemis.Properties; using Artemis.Properties;
using Artemis.Utilities; using Artemis.Utilities;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles; using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Mousemat namespace Artemis.Profiles.Layers.Types.Mousemat
@ -17,6 +18,7 @@ namespace Artemis.Profiles.Layers.Types.Mousemat
public string Name => "Mousemat"; public string Name => "Mousemat";
public bool ShowInEdtor => false; public bool ShowInEdtor => false;
public DrawType DrawType => DrawType.Mousemat; public DrawType DrawType => DrawType.Mousemat;
public int DrawScale => 1;
public ImageSource DrawThumbnail(LayerModel layer) public ImageSource DrawThumbnail(LayerModel layer)
{ {
@ -34,12 +36,12 @@ namespace Artemis.Profiles.Layers.Types.Mousemat
// If an animation is present, let it handle the drawing // If an animation is present, let it handle the drawing
if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation)) if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation))
{ {
layerModel.LayerAnimation.Draw(layerModel, c); layerModel.LayerAnimation.Draw(layerModel, c, DrawScale);
return; return;
} }
// Otherwise draw the rectangle with its applied dimensions and brush // Otherwise draw the rectangle with its applied dimensions and brush
var rect = layerModel.LayerRect(); var rect = layerModel.LayerRect(1);
// Can't meddle with the original brush because it's frozen. // Can't meddle with the original brush because it's frozen.
var brush = layerModel.Brush.Clone(); var brush = layerModel.Brush.Clone();

View File

@ -108,8 +108,10 @@ namespace Artemis.Profiles
Rect rect, bool preview) Rect rect, bool preview)
{ {
renderLayers = renderLayers.Where(rl => rl.LayerType.DrawType == drawType).ToList(); renderLayers = renderLayers.Where(rl => rl.LayerType.DrawType == drawType).ToList();
if (!renderLayers.Any())
return;
var visual = new DrawingVisual(); var visual = new DrawingVisual();
var layerModels = renderLayers.ToList();
using (var c = visual.RenderOpen()) using (var c = visual.RenderOpen())
{ {
// Setup the DrawingVisual's size // Setup the DrawingVisual's size
@ -117,12 +119,12 @@ namespace Artemis.Profiles
c.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, rect); c.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, rect);
// Update the layers // Update the layers
foreach (var layerModel in layerModels) foreach (var layerModel in renderLayers)
layerModel.Update(dataModel, preview, true); layerModel.Update(dataModel, preview, true);
RaiseDeviceUpdatedEvent(new ProfileDeviceEventsArg(drawType, dataModel, preview, null)); RaiseDeviceUpdatedEvent(new ProfileDeviceEventsArg(drawType, dataModel, preview, null));
// Draw the layers // Draw the layers
foreach (var layerModel in layerModels) foreach (var layerModel in renderLayers)
layerModel.Draw(dataModel, c, preview, true); layerModel.Draw(dataModel, c, preview, true);
RaiseDeviceDrawnEvent(new ProfileDeviceEventsArg(drawType, dataModel, preview, c)); RaiseDeviceDrawnEvent(new ProfileDeviceEventsArg(drawType, dataModel, preview, c));
@ -190,8 +192,7 @@ namespace Artemis.Profiles
public void Activate(LuaManager luaManager) public void Activate(LuaManager luaManager)
{ {
if (!Equals(luaManager.ProfileModel, this) || luaManager.ProfileModel.LuaScript != LuaScript) luaManager.SetupLua(this);
luaManager.SetupLua(this);
} }
public void Deactivate(LuaManager luaManager) public void Deactivate(LuaManager luaManager)

View File

@ -53,7 +53,7 @@ using System.Windows;
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.1.0")] [assembly: AssemblyVersion("1.8.0.0")]
[assembly: AssemblyFileVersion("1.7.1.0")] [assembly: AssemblyFileVersion("1.8.0.0")]
[assembly: InternalsVisibleTo("Artemis.Explorables")] [assembly: InternalsVisibleTo("Artemis.Explorables")]

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Artemis.Utilities
{
public static class EditorHelper
{
}
}

Some files were not shown because too many files have changed in this diff Show More