WPF中MediaPlayer音乐淡入淡出效果失效问题排查
问题原因分析及修正方案
一、Play方法仅执行初始音量设置的原因
你设置的音量递增步长0.000005过小,假设目标Volume设为1.0,从0.005涨到1.0需要执行约199000次循环,每次延迟20ms,总耗时超过1小时——肉眼完全察觉不到音量变化,看起来就像只执行了初始的currentVolume = 0.005代码。
二、Stop方法无法停止音乐的原因
- 循环条件错误:WPF的
MediaPlayer.Volume会自动将值限制在0-1区间,当你尝试将音量减到负数时,它会强制保持为0。此时循环条件MediaPlayer.Volume >= 0永远为真,会无限执行减音量操作,永远到不了调用MediaPlayer.Stop()的步骤。 - 淡出步长过小:即使循环条件正确,
0.00005的步长也需要极长时间才能完成淡出,用户会误以为音乐没停止。 - Dispatcher调用未指定优先级:未明确指定
Dispatcher.BeginInvoke的优先级,可能导致回调被延迟处理。
三、修正后的代码
using System; using System.Windows; using System.Windows.Media; namespace Summer { public class AudioPlayer { public enum Repetition { ONCE, REPEAT } private readonly MediaPlayer _mediaPlayer = new MediaPlayer(); // 标记是否正在淡入/淡出,避免并发操作冲突 private bool _isFading = false; public string? SoundPath { get; set; } // 限制音量范围在0-1之间,避免无效值 private double _volume = 1.0; public double Volume { get => _volume; set => _volume = Math.Clamp(value, 0.0, 1.0); } public void Play(Repetition repetition) { if (string.IsNullOrEmpty(SoundPath)) return; // 先终止当前播放(如果存在),再启动新播放 StopAsync().Wait(); _mediaPlayer.Open(new Uri(SoundPath, UriKind.Relative)); _mediaPlayer.MediaEnded -= RepeatSound; if (repetition == Repetition.REPEAT) _mediaPlayer.MediaEnded += RepeatSound; _ = FadeInAsync(); } private async Task FadeInAsync() { if (_isFading) return; _isFading = true; double currentVolume = 0.01; _mediaPlayer.Volume = currentVolume; _mediaPlayer.Play(); // 调整步长为0.02,总淡入时长约1秒,符合正常听觉体验 const double step = 0.02; while (currentVolume < Volume) { currentVolume = Math.Min(currentVolume + step, Volume); _mediaPlayer.Volume = currentVolume; await Task.Delay(20); } _isFading = false; } private void RepeatSound(object sender, EventArgs args) { _mediaPlayer.Position = TimeSpan.Zero; _mediaPlayer.Play(); } public async Task StopAsync() { if (_isFading) return; _isFading = true; // 调整淡出步长为0.02,总淡出时长约1秒 const double step = 0.02; while (_mediaPlayer.Volume > 0) { double newVolume = _mediaPlayer.Volume - step; _mediaPlayer.Volume = Math.Max(newVolume, 0.0); await Task.Delay(20); } _mediaPlayer.Stop(); _mediaPlayer.MediaEnded -= RepeatSound; _isFading = false; } } }
四、关键修正点说明
- 新增
_isFading标记,防止淡入淡出操作并发执行导致异常。 - 调整淡入淡出步长为
0.02,总时长约1秒,符合常规听觉体验。 - 修正Stop方法的循环逻辑,用
Math.Max确保音量不会低于0,保证循环能正常结束。 - 将Stop方法改为
async Task,简化异步逻辑,同时确保操作在UI线程执行(调用时需保证在UI线程,若不在可自行包裹Dispatcher调用)。 - 给
Volume属性增加范围限制,避免设置无效值。 - Play方法中先终止当前播放,再启动新音频,避免播放冲突。
内容的提问来源于stack exchange,提问作者alEN
相关产品推荐
相关产品推荐

