mirror of
https://github.com/LanZhan-Harmony/WindowsMusicPlayer-TheUntamedMusicPlayer.git
synced 2026-05-06 19:20:18 +08:00
更新灵动词岛
This commit is contained in:
@@ -42,7 +42,7 @@
|
||||
</ContentDialog.Resources>
|
||||
|
||||
<ContentDialog.Title>
|
||||
<Grid Width="490">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
@@ -54,12 +54,11 @@
|
||||
<ToggleSwitch x:Name="IsEqualizerOnSwitch"
|
||||
Grid.Column="2"
|
||||
Margin="0,-4,0,0"
|
||||
FlowDirection="RightToLeft"
|
||||
IsOn="{x:Bind IsEqualizerOn, Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
</ContentDialog.Title>
|
||||
|
||||
<StackPanel Width="486" Spacing="8">
|
||||
<StackPanel Spacing="8">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock x:Uid="EqualizerDialog_Preset" VerticalAlignment="Center"/>
|
||||
<ComboBox IsEnabled="{x:Bind IsEqualizerOn, Mode=OneWay}"/>
|
||||
|
||||
@@ -268,6 +268,7 @@ public partial class MusicPlayer
|
||||
_logger.ZLogInformation($"Bass初始化失败: {Bass.LastError}");
|
||||
return;
|
||||
}
|
||||
|
||||
LoadBassPlugins(); // 加载Bass插件
|
||||
|
||||
// 设置同步回调
|
||||
@@ -687,6 +688,11 @@ public partial class MusicPlayer
|
||||
}
|
||||
}
|
||||
|
||||
public void NotifyLyricContentChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(CurrentLyricContent));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放
|
||||
/// </summary>
|
||||
@@ -1262,7 +1268,7 @@ public partial class MusicPlayer
|
||||
/// 更新当前歌词索引和状态
|
||||
/// </summary>
|
||||
/// <param name="currentTime">当前播放时间(毫秒)</param>
|
||||
private void UpdateCurrentLyricIndex(double currentTime)
|
||||
public void UpdateCurrentLyricIndex(double currentTime)
|
||||
{
|
||||
if (CurrentLyric.Count == 0)
|
||||
{
|
||||
@@ -1749,6 +1755,8 @@ public partial class MusicPlayer
|
||||
Bass.Free();
|
||||
BassWasapi.Free();
|
||||
_smtcManager?.Dispose();
|
||||
_syncEndCallback -= OnPlayBackEnded;
|
||||
_syncFailCallback -= OnPlaybackFailed;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:genTemplate="http://schemas.microsoft.com/appx/developer/templatestudio"
|
||||
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
|
||||
IgnorableNamespaces="uap rescap genTemplate uap3">
|
||||
IgnorableNamespaces="uap rescap uap3">
|
||||
|
||||
<Identity
|
||||
Name="C19A3696.28439CB846862"
|
||||
@@ -94,13 +93,4 @@
|
||||
<uap:Capability Name="musicLibrary"/>
|
||||
<uap3:Capability Name="backgroundMediaPlayback"/>
|
||||
</Capabilities>
|
||||
|
||||
<genTemplate:Metadata>
|
||||
<genTemplate:Item Name="generator" Value="Template Studio"/>
|
||||
<genTemplate:Item Name="wizardVersion" Version="v5.5" />
|
||||
<genTemplate:Item Name="projectType" Value="NavView" />
|
||||
<genTemplate:Item Name="framework" Value="MVVMToolkit" />
|
||||
<genTemplate:Item Name="platform" Value="WinUI" />
|
||||
<genTemplate:Item Name="appmodel" Value="Desktop" />
|
||||
</genTemplate:Metadata>
|
||||
</Package>
|
||||
|
||||
@@ -48,10 +48,6 @@
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference
|
||||
Include="CommunityToolkit.Labs.WinUI.MarqueeText"
|
||||
Version="0.1.250828-build.2223"
|
||||
/>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250402" />
|
||||
<PackageReference
|
||||
@@ -67,7 +63,7 @@
|
||||
<PackageReference Include="MemoryPack" Version="1.21.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.250907003" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.250916003" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.0" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||
<PackageReference Include="WinUIEx" Version="2.8.0" />
|
||||
@@ -89,30 +85,6 @@
|
||||
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||
<HasPackageAndPublishMenu>True</HasPackageAndPublishMenu>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||
<DebugType>full</DebugType>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<NoWarn>CS8981</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<DebugType>full</DebugType>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<NoWarn>CS8981</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm64'">
|
||||
<DebugType>full</DebugType>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<NoWarn>CS8981</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
|
||||
<DebugType>full</DebugType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DebugType>full</DebugType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm64'">
|
||||
<DebugType>full</DebugType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PRIResource Update="Strings\zh-cn\Resources.resw">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
@@ -123,10 +95,13 @@
|
||||
<AppxBundlePlatforms>x86|x64|arm64</AppxBundlePlatforms>
|
||||
<PublishSelfContained>True</PublishSelfContained>
|
||||
<WindowsAppSDKSelfContained>True</WindowsAppSDKSelfContained>
|
||||
<PublishReadyToRun>True</PublishReadyToRun>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<PublishAot>True</PublishAot>
|
||||
<PublishTrimmed>True</PublishTrimmed>
|
||||
<PublishAot Condition="'$(Configuration)' == 'Debug'">False</PublishAot>
|
||||
<PublishAot Condition="'$(Configuration)' != 'Debug'">True</PublishAot>
|
||||
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
|
||||
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
|
||||
<JsonSerializerIsReflectionEnabledByDefault>True</JsonSerializerIsReflectionEnabledByDefault>
|
||||
<PackageCertificateKeyFile>The Untamed Music Player_TemporaryKey.pfx</PackageCertificateKeyFile>
|
||||
<AppxPackageSigningEnabled>False</AppxPackageSigningEnabled>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:labs="using:CommunityToolkit.Labs.WinUI.MarqueeTextRns"
|
||||
xmlns:local="using:The_Untamed_Music_Player.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:model="using:The_Untamed_Music_Player.Models"
|
||||
@@ -13,22 +12,17 @@
|
||||
<Border x:Name="AnimatedBorder"
|
||||
Height="55"
|
||||
HorizontalAlignment="Center"
|
||||
Background="Black" CornerRadius="30"
|
||||
PointerMoved="AnimatedBorder_PointerMoved"
|
||||
PointerPressed="AnimatedBorder_PointerPressed"
|
||||
PointerReleased="AnimatedBorder_PointerReleased">
|
||||
<labs:MarqueeText x:Name="LyricContent"
|
||||
Width="{x:Bind GetTextBlockWidth(model:Data.MusicPlayer.CurrentLyricContent), Mode=OneWay}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Behavior="Bouncing" Direction="Left"
|
||||
FontFamily="{x:Bind model:Settings.FontFamily}"
|
||||
FontSize="32" Foreground="#FDFDFD"
|
||||
Loaded="LyricContent_Loaded"
|
||||
SizeChanged="LyricContentTextBlock_SizeChanged"
|
||||
Text="{x:Bind model:Data.MusicPlayer.CurrentLyricContent, Mode=OneWay}"/>
|
||||
Background="Black" CornerRadius="30">
|
||||
<TextBlock x:Name="LyricContent"
|
||||
Width="{x:Bind GetTextBlockWidth(model:Data.MusicPlayer.CurrentLyricContent), Mode=OneWay}"
|
||||
Height="{x:Bind GetTextBlockHeight(model:Data.MusicPlayer.CurrentLyricContent), Mode=OneWay}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{x:Bind model:Settings.FontFamily}"
|
||||
FontSize="32" Foreground="#FDFDFD"
|
||||
MaxLines="2"
|
||||
SizeChanged="LyricContentTextBlock_SizeChanged"
|
||||
Text="{x:Bind model:Data.MusicPlayer.CurrentLyricContent, Mode=OneWay}"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Window.SystemBackdrop>
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
using CommunityToolkit.Labs.WinUI.MarqueeTextRns;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using The_Untamed_Music_Player.Helpers;
|
||||
using The_Untamed_Music_Player.Models;
|
||||
using The_Untamed_Music_Player.Services;
|
||||
using The_Untamed_Music_Player.ViewModels;
|
||||
using Windows.Foundation;
|
||||
using Windows.System;
|
||||
using Windows.UI.WindowManagement;
|
||||
using WinRT.Interop;
|
||||
using WinUIEx;
|
||||
using ZLogger;
|
||||
@@ -23,10 +20,8 @@ public sealed partial class DesktopLyricWindow : WindowEx, IDisposable
|
||||
private readonly ILogger _logger = LoggingService.CreateLogger<DesktopLyricWindow>();
|
||||
private readonly nint _hWnd;
|
||||
|
||||
private bool _isDragging = false; // 检测是否在拖动的变量
|
||||
private readonly int _maxTextBlockWidth;
|
||||
private bool _isMouseOverBorder = false; // 检测鼠标是否在窗口上的变量
|
||||
|
||||
private POINT _lastPointerPosition;
|
||||
private DispatcherTimer? _updateTimer250ms; // 用于周期性检查鼠标位置和置顶的计时器
|
||||
|
||||
private Storyboard? _currentStoryboard;
|
||||
@@ -34,24 +29,43 @@ public sealed partial class DesktopLyricWindow : WindowEx, IDisposable
|
||||
{
|
||||
FontSize = 32,
|
||||
FontFamily = Settings.FontFamily,
|
||||
MaxLines = 2,
|
||||
};
|
||||
|
||||
public DesktopLyricViewModel ViewModel { get; }
|
||||
|
||||
public DesktopLyricWindow()
|
||||
{
|
||||
ViewModel = App.GetService<DesktopLyricViewModel>();
|
||||
InitializeComponent();
|
||||
Title = "DesktopLyricWindowTitle".GetLocalized();
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
InitializeComponent();
|
||||
SetTitleBar(AnimatedBorder);
|
||||
|
||||
_hWnd = WindowNative.GetWindowHandle(this); // 获取窗口句柄
|
||||
|
||||
var presenter = OverlappedPresenter.Create();
|
||||
presenter.SetBorderAndTitleBar(false, false);
|
||||
AppWindow.SetPresenter(presenter);
|
||||
|
||||
MakeWindowClickThrough(true);
|
||||
SetWindowProperty();
|
||||
SetTopmost(true);
|
||||
|
||||
var workArea = DisplayArea
|
||||
.GetFromWindowId(AppWindow.Id, DisplayAreaFallback.Nearest)
|
||||
.WorkArea; // 获取屏幕工作区大小
|
||||
var screenWidth = workArea.Width;
|
||||
_maxTextBlockWidth = screenWidth - 50;
|
||||
var screenHeight = workArea.Height;
|
||||
var y = screenHeight - screenHeight * 140 / 1080; // 计算窗口位置,使其位于屏幕下方
|
||||
this.SetWindowSize(screenWidth, 60);
|
||||
this.CenterOnScreen(null, null); // 设置窗口位置
|
||||
var currentPosition = AppWindow.Position;
|
||||
|
||||
// 将窗口移动到新的位置
|
||||
this.Move(currentPosition.X, y);
|
||||
|
||||
InitMousePositionTimer();
|
||||
Closed += Window_Closed;
|
||||
Data.MusicPlayer.NotifyLyricContentChanged();
|
||||
}
|
||||
|
||||
private void SetWindowProperty()
|
||||
{
|
||||
const int GWL_EXSTYLE = -20;
|
||||
const int WS_EX_TOOLWINDOW = 0x00000080;
|
||||
const int WS_EX_APPWINDOW = 0x00040000;
|
||||
@@ -60,24 +74,19 @@ public sealed partial class DesktopLyricWindow : WindowEx, IDisposable
|
||||
exStyle &= ~WS_EX_APPWINDOW; // 移除应用窗口样式
|
||||
SetWindowLong(_hWnd, GWL_EXSTYLE, exStyle);
|
||||
|
||||
SetTopmost(true);
|
||||
|
||||
var workArea = DisplayArea
|
||||
.GetFromWindowId(AppWindow.Id, DisplayAreaFallback.Nearest)
|
||||
.WorkArea; // 获取屏幕工作区大小
|
||||
var screenHeight = workArea.Height;
|
||||
var y = screenHeight - screenHeight * 140 / 1080; // 计算窗口位置,使其位于屏幕下方
|
||||
|
||||
this.SetWindowSize(1000, 100);
|
||||
this.CenterOnScreen(null, null); // 设置窗口位置
|
||||
|
||||
var currentPosition = AppWindow.Position;
|
||||
|
||||
// 将窗口移动到新的位置
|
||||
this.Move(currentPosition.X, y);
|
||||
|
||||
InitMousePositionTimer();
|
||||
Closed += Window_Closed;
|
||||
const int GWL_STYLE = -16;
|
||||
const int WS_CAPTION = 0x00C00000;
|
||||
const int WS_SYSMENU = 0x00080000;
|
||||
const int WS_MINIMIZEBOX = 0x00020000;
|
||||
const int WS_MAXIMIZEBOX = 0x00010000;
|
||||
const int WS_THICKFRAME = 0x00040000;
|
||||
var style = GetWindowLong(_hWnd, GWL_STYLE);
|
||||
style &= ~WS_CAPTION; // 去掉标题栏
|
||||
style &= ~WS_SYSMENU; // 去掉系统菜单(包含关闭菜单)
|
||||
style &= ~WS_MINIMIZEBOX; // 去掉最小化按钮
|
||||
style &= ~WS_MAXIMIZEBOX; // 去掉最大化按钮
|
||||
style &= ~WS_THICKFRAME; // 去掉可调整大小的边框(如果不需要保留调整大小)
|
||||
SetWindowLong(_hWnd, GWL_STYLE, style);
|
||||
}
|
||||
|
||||
private void InitMousePositionTimer()
|
||||
@@ -93,7 +102,6 @@ public sealed partial class DesktopLyricWindow : WindowEx, IDisposable
|
||||
|
||||
// 获取当前鼠标位置
|
||||
GetCursorPos(out var mousePoint);
|
||||
|
||||
// 获取AnimatedBorder在屏幕上的位置
|
||||
var borderRect = GetElementScreenRect(AnimatedBorder);
|
||||
|
||||
@@ -104,8 +112,7 @@ public sealed partial class DesktopLyricWindow : WindowEx, IDisposable
|
||||
&& mousePoint.Y >= borderRect.Top
|
||||
&& mousePoint.Y <= borderRect.Bottom;
|
||||
|
||||
// 如果鼠标状态改变
|
||||
if (isOverBorder != _isMouseOverBorder)
|
||||
if (isOverBorder != _isMouseOverBorder) // 如果鼠标状态改变
|
||||
{
|
||||
_isMouseOverBorder = isOverBorder;
|
||||
MakeWindowClickThrough(!isOverBorder);
|
||||
@@ -167,75 +174,28 @@ public sealed partial class DesktopLyricWindow : WindowEx, IDisposable
|
||||
SetWindowPos(_hWnd, position, 0, 0, 0, 0, flags);
|
||||
}
|
||||
|
||||
private void AnimatedBorder_PointerPressed(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
_isDragging = true;
|
||||
GetCursorPos(out var point);
|
||||
_lastPointerPosition = point;
|
||||
(sender as Border)!.CapturePointer(e.Pointer);
|
||||
}
|
||||
|
||||
private void AnimatedBorder_PointerMoved(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (!_isDragging)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GetCursorPos(out var point);
|
||||
|
||||
// 计算移动差值
|
||||
var deltaX = point.X - _lastPointerPosition.X;
|
||||
var deltaY = point.Y - _lastPointerPosition.Y;
|
||||
|
||||
// 更新窗口位置
|
||||
this.Move(AppWindow.Position.X + deltaX, AppWindow.Position.Y + deltaY);
|
||||
|
||||
_lastPointerPosition = point;
|
||||
}
|
||||
|
||||
private void AnimatedBorder_PointerReleased(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (_isDragging)
|
||||
{
|
||||
_isDragging = false;
|
||||
(sender as Border)!.ReleasePointerCapture(e.Pointer);
|
||||
}
|
||||
}
|
||||
|
||||
private void LyricContent_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = "TEST测试",
|
||||
FontSize = 32,
|
||||
FontFamily = Settings.FontFamily,
|
||||
};
|
||||
textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
(sender as MarqueeText)!.Height = textBlock.DesiredSize.Height;
|
||||
}
|
||||
|
||||
private double GetTextBlockWidth(string currentLyricContent)
|
||||
{
|
||||
LyricContent.StopMarquee();
|
||||
if (currentLyricContent == "")
|
||||
{
|
||||
return 140;
|
||||
return 100;
|
||||
}
|
||||
_measureTextBlock.Text = currentLyricContent;
|
||||
_measureTextBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
var width = _measureTextBlock.DesiredSize.Width;
|
||||
if (width > 700)
|
||||
return Math.Min(width, _maxTextBlockWidth);
|
||||
}
|
||||
|
||||
private double GetTextBlockHeight(string currentLyricContent)
|
||||
{
|
||||
if (currentLyricContent == "")
|
||||
{
|
||||
// 在UI线程上延迟0.5秒后调用 StartMarquee
|
||||
Task.Delay(500)
|
||||
.ContinueWith(_ =>
|
||||
{
|
||||
// 确保在UI线程上下文中执行
|
||||
DispatcherQueue.TryEnqueue(() => LyricContent.StartMarquee());
|
||||
});
|
||||
return 37;
|
||||
}
|
||||
return Math.Min(width, 700);
|
||||
_measureTextBlock.Text = currentLyricContent;
|
||||
_measureTextBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
var height = _measureTextBlock.DesiredSize.Height;
|
||||
return height;
|
||||
}
|
||||
|
||||
private void LyricContentTextBlock_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
@@ -244,20 +204,42 @@ public sealed partial class DesktopLyricWindow : WindowEx, IDisposable
|
||||
{
|
||||
_currentStoryboard?.Stop();
|
||||
_currentStoryboard?.Children.Clear();
|
||||
var newWidth = e.NewSize.Width;
|
||||
var widthAnimation = new DoubleAnimation
|
||||
{
|
||||
From = e.PreviousSize.Width + 50,
|
||||
To = newWidth > 140 ? newWidth + 50 : 190,
|
||||
Duration = TimeSpan.FromMilliseconds(300),
|
||||
EnableDependentAnimation = true,
|
||||
EasingFunction = new BackEase { EasingMode = EasingMode.EaseOut, Amplitude = 0.8 },
|
||||
};
|
||||
Storyboard.SetTarget(widthAnimation, AnimatedBorder);
|
||||
Storyboard.SetTargetProperty(widthAnimation, "Width");
|
||||
_currentStoryboard = new Storyboard();
|
||||
_currentStoryboard.Children.Add(widthAnimation);
|
||||
_currentStoryboard.Begin();
|
||||
|
||||
var oldWidth = e.PreviousSize.Width + 50;
|
||||
var newWidth = Math.Max(e.NewSize.Width + 50, 150);
|
||||
var oldHeight = e.PreviousSize.Height + 20;
|
||||
var newHeight = e.NewSize.Height + 20;
|
||||
|
||||
if (Math.Abs(oldWidth - newWidth) > 1e-3)
|
||||
{
|
||||
var widthAmplitude = oldWidth - newWidth > 800 ? 0.1 : 0.8; // 避免宽度减小到负数
|
||||
var widthAnimation = CreateDoubleAnimation(oldWidth, newWidth, widthAmplitude);
|
||||
Storyboard.SetTarget(widthAnimation, AnimatedBorder);
|
||||
Storyboard.SetTargetProperty(widthAnimation, "Width");
|
||||
_currentStoryboard.Children.Add(widthAnimation);
|
||||
}
|
||||
|
||||
if (Math.Abs(oldHeight - newHeight) > 1e-3)
|
||||
{
|
||||
var heightAnimation = CreateDoubleAnimation(oldHeight, newHeight, 0.8);
|
||||
Storyboard.SetTarget(heightAnimation, AnimatedBorder);
|
||||
Storyboard.SetTargetProperty(heightAnimation, "Height");
|
||||
_currentStoryboard.Children.Add(heightAnimation);
|
||||
}
|
||||
|
||||
if (_currentStoryboard.Children.Count > 0)
|
||||
{
|
||||
if (newHeight - oldHeight > 1e-3) // 如果新高度更大, 先调整高度, 再启动动画
|
||||
{
|
||||
this.SetWindowSize(_maxTextBlockWidth, newHeight + 5);
|
||||
}
|
||||
_currentStoryboard.Begin();
|
||||
if (oldHeight - newHeight > 1e-3) // 如果新高度更小, 动画结束后再调整高度
|
||||
{
|
||||
this.SetWindowSize(_maxTextBlockWidth, newHeight + 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -265,6 +247,22 @@ public sealed partial class DesktopLyricWindow : WindowEx, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private static DoubleAnimation CreateDoubleAnimation(double from, double to, double amplitude)
|
||||
{
|
||||
return new DoubleAnimation
|
||||
{
|
||||
From = from,
|
||||
To = to,
|
||||
Duration = TimeSpan.FromMilliseconds(300),
|
||||
EnableDependentAnimation = true,
|
||||
EasingFunction = new BackEase
|
||||
{
|
||||
EasingMode = EasingMode.EaseOut,
|
||||
Amplitude = amplitude,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private void Window_Closed(object sender, WindowEventArgs args)
|
||||
{
|
||||
Data.RootPlayBarViewModel?.IsDesktopLyricWindowStarted = false;
|
||||
|
||||
@@ -338,7 +338,6 @@
|
||||
<Button x:Name="FullScreenButton" x:Uid="PlayBar_FullScreen"
|
||||
AccessKey="U"
|
||||
Click="{x:Bind ViewModel.FullScreenButton_Click}"
|
||||
IsEnabled="{x:Bind ViewModel.Availability, Mode=OneWay}"
|
||||
Style="{StaticResource MenuButtonStyle}">
|
||||
<Button.KeyboardAccelerators>
|
||||
<KeyboardAccelerator Key="F11"/>
|
||||
@@ -352,7 +351,6 @@
|
||||
<Button x:Uid="PlayBar_DesktopLyric"
|
||||
AccessKey="M"
|
||||
Click="{x:Bind ViewModel.DesktopLyricButton_Click}"
|
||||
IsEnabled="{x:Bind ViewModel.Availability, Mode=OneWay}"
|
||||
Style="{StaticResource MenuButtonStyle}">
|
||||
<Button.KeyboardAccelerators>
|
||||
<KeyboardAccelerator Key="M" Modifiers="Control"/>
|
||||
@@ -554,7 +552,6 @@
|
||||
AccessKey="U"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource UntamedFontFamily},
|
||||
Glyph=}"
|
||||
IsEnabled="{x:Bind ViewModel.Availability, Mode=OneWay}"
|
||||
KeyTipPlacementMode="Left">
|
||||
<MenuFlyoutItem.KeyboardAccelerators>
|
||||
<KeyboardAccelerator Key="F11"/>
|
||||
|
||||
@@ -65,15 +65,13 @@
|
||||
|
||||
<!-- 动画 -->
|
||||
<StackPanel.ChildrenTransitions>
|
||||
<EntranceThemeTransition FromVerticalOffset="50" IsStaggeringEnabled="True"/>
|
||||
<RepositionThemeTransition IsStaggeringEnabled="False"/>
|
||||
<RepositionThemeTransition/>
|
||||
</StackPanel.ChildrenTransitions>
|
||||
|
||||
<!-- 库 -->
|
||||
<StackPanel Spacing="2">
|
||||
<StackPanel.ChildrenTransitions>
|
||||
<EntranceThemeTransition FromVerticalOffset="50" IsStaggeringEnabled="True"/>
|
||||
<RepositionThemeTransition IsStaggeringEnabled="False"/>
|
||||
<RepositionThemeTransition/>
|
||||
</StackPanel.ChildrenTransitions>
|
||||
|
||||
<TextBlock x:Uid="Settings_Library"
|
||||
@@ -162,8 +160,7 @@
|
||||
<!-- 播放 -->
|
||||
<StackPanel Spacing="2">
|
||||
<StackPanel.ChildrenTransitions>
|
||||
<EntranceThemeTransition FromVerticalOffset="50" IsStaggeringEnabled="True"/>
|
||||
<RepositionThemeTransition IsStaggeringEnabled="False"/>
|
||||
<RepositionThemeTransition/>
|
||||
</StackPanel.ChildrenTransitions>
|
||||
<TextBlock x:Uid="Settings_Play"
|
||||
Margin="0,12,0,5"
|
||||
@@ -179,8 +176,7 @@
|
||||
<!-- 歌词 -->
|
||||
<StackPanel Spacing="2">
|
||||
<StackPanel.ChildrenTransitions>
|
||||
<EntranceThemeTransition FromVerticalOffset="50" IsStaggeringEnabled="True"/>
|
||||
<RepositionThemeTransition IsStaggeringEnabled="False"/>
|
||||
<RepositionThemeTransition/>
|
||||
</StackPanel.ChildrenTransitions>
|
||||
<TextBlock x:Uid="Settings_Lyric"
|
||||
Margin="0,12,0,5"
|
||||
@@ -218,8 +214,7 @@
|
||||
<!-- 个性化 -->
|
||||
<StackPanel Spacing="2">
|
||||
<StackPanel.ChildrenTransitions>
|
||||
<EntranceThemeTransition FromVerticalOffset="50" IsStaggeringEnabled="True"/>
|
||||
<RepositionThemeTransition IsStaggeringEnabled="False"/>
|
||||
<RepositionThemeTransition/>
|
||||
</StackPanel.ChildrenTransitions>
|
||||
<TextBlock x:Uid="Settings_Personalization"
|
||||
Margin="0,12,0,5"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="The_Untamed_Music_Player.app"/>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
# WASAPI独占模式使用指南
|
||||
|
||||
## 功能概述
|
||||
|
||||
MusicPlayer类现已支持WASAPI独占模式播放,提供更低延迟和更高质量的音频输出。
|
||||
|
||||
## 新增功能
|
||||
|
||||
### 1. 独占模式属性
|
||||
- `IsExclusiveMode`: bool类型属性,控制是否启用WASAPI独占模式
|
||||
|
||||
### 2. 模式切换方法
|
||||
- `SwitchExclusiveMode()`: 在播放过程中动态切换独占模式和共享模式
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 启用独占模式
|
||||
```csharp
|
||||
// 设置独占模式
|
||||
musicPlayer.IsExclusiveMode = true;
|
||||
|
||||
// 或者动态切换
|
||||
musicPlayer.SwitchExclusiveMode();
|
||||
```
|
||||
|
||||
### 检查当前模式
|
||||
```csharp
|
||||
if (musicPlayer.IsExclusiveMode)
|
||||
{
|
||||
// 当前处于独占模式
|
||||
Console.WriteLine("正在使用WASAPI独占模式");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 当前处于共享模式
|
||||
Console.WriteLine("正在使用标准共享模式");
|
||||
}
|
||||
```
|
||||
|
||||
## 技术细节
|
||||
|
||||
### 独占模式特点
|
||||
1. **更低延迟**: 绕过Windows音频混合器,直接与音频驱动通信
|
||||
2. **更高音质**: 避免系统重采样和音频处理
|
||||
3. **独占访问**: 应用程序独占音频设备,其他应用无法同时播放音频
|
||||
|
||||
### 实现原理
|
||||
1. 在独占模式下,使用WASAPI接口直接控制音频设备
|
||||
2. 音频数据流通过WASAPI回调函数`WasapiProc`传输
|
||||
3. 音量控制使用`BassWasapi.SetVolume`而非Bass的ChannelSetAttribute
|
||||
4. 支持播放中动态切换模式,保持播放位置连续性
|
||||
|
||||
### 兼容性处理
|
||||
- 如果独占模式初始化失败,自动回退到共享模式
|
||||
- 所有现有功能(播放、暂停、音量调节、变速等)在两种模式下都完全兼容
|
||||
- 无缝支持在线音频流和本地文件
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **独占访问**: 启用独占模式时,其他应用程序将无法播放音频
|
||||
2. **设备兼容性**: 某些音频设备可能不支持独占模式
|
||||
3. **驱动要求**: 需要支持WASAPI的音频驱动程序
|
||||
4. **自动回退**: 如果独占模式不可用,系统会自动回退到共享模式
|
||||
|
||||
## 错误处理
|
||||
|
||||
系统会自动处理以下情况:
|
||||
- WASAPI设备不可用时自动回退到Bass默认播放
|
||||
- 独占模式初始化失败时的错误恢复
|
||||
- 模式切换时的状态保持和位置恢复
|
||||
|
||||
## 示例代码
|
||||
|
||||
```csharp
|
||||
// 创建播放器实例
|
||||
var player = new MusicPlayer();
|
||||
|
||||
// 设置独占模式
|
||||
player.IsExclusiveMode = true;
|
||||
|
||||
// 播放音乐(将自动使用独占模式)
|
||||
player.PlaySongByInfo(songInfo);
|
||||
|
||||
// 运行时切换模式
|
||||
player.SwitchExclusiveMode(); // 切换到共享模式
|
||||
player.SwitchExclusiveMode(); // 切换回独占模式
|
||||
```
|
||||
|
||||
## 日志监控
|
||||
|
||||
系统会记录以下关键事件:
|
||||
- WASAPI初始化成功/失败
|
||||
- 独占模式启动成功/失败
|
||||
- 模式切换操作
|
||||
- 设备信息和错误状态
|
||||
|
||||
查看日志以了解WASAPI运行状态和故障排除信息。
|
||||
Reference in New Issue
Block a user