更改曲目 NAudio 后滑块不起作用
Posted
技术标签:
【中文标题】更改曲目 NAudio 后滑块不起作用【英文标题】:Slider do not work after changing track NAudio 【发布时间】:2019-02-06 14:58:15 【问题描述】:我有滑块作为音轨时间线的轨迹栏。使用 NAudio 从 Web 播放音轨。使用 NAudio WPF 示例中的所有代码。我只更改了可访问性修饰符。第一次开始播放第一首曲目时一切正常。但如果我更改为下一个曲目,滑块仍处于启动状态。只有在单击暂停然后播放时才更改。
为了完全理解:
第一个轨道 - 滑块工作和移动。
切换到下一个曲目 - 滑块在开头并且不移动。但它在按下暂停后开始移动,然后播放。它立即移动到当前播放的位置并继续正常操作。接下来的每首曲目也是如此。
PlayerUserControl 的虚拟机代码:
public class AudioControlVM : ViewModelBase, IDisposable
private AudioModel _currentSong;
public AudioModel CurrentSong get return _currentSong; set _currentSong = value; RaisePropertyChanged("CurrentSong");
private string inputPath, songName;
private string defaultDecompressionFormat;
public IWavePlayer wavePlayer get; set;
private WaveStream reader;
public RelayCommand PlayCommand get; set;
public RelayCommand PauseCommand get; set;
public RelayCommand StopCommand get; set;
public DispatcherTimer timer = new DispatcherTimer();
private double sliderPosition;
private readonly ObservableCollection<string> inputPathHistory;
private string lastPlayed;
public AudioControlVM()
inputPathHistory = new ObservableCollection<string>();
PlayCommand = new RelayCommand(() => Play());
PauseCommand = new RelayCommand(() => Pause());
StopCommand = new RelayCommand(Stop, () => !IsStopped);
timer.Interval = TimeSpan.FromMilliseconds(500);
timer.Tick += TimerOnTick;
public bool IsPlaying => wavePlayer != null && wavePlayer.PlaybackState == PlaybackState.Playing;
public bool IsStopped => wavePlayer == null || wavePlayer.PlaybackState == PlaybackState.Stopped;
public IEnumerable<string> InputPathHistory => inputPathHistory;
const double SliderMax = 10.0;
private void TimerOnTick(object sender, EventArgs eventArgs)
if (reader != null)
sliderPosition = reader.Position * SliderMax / reader.Length;
RaisePropertyChanged("SliderPosition");
public double SliderPosition
get => sliderPosition;
set
if (sliderPosition != value)
sliderPosition = value;
if (reader != null)
var pos = (long)(reader.Length * sliderPosition / SliderMax);
reader.Position = pos; // media foundation will worry about block align for us
RaisePropertyChanged("SliderPosition");
private bool TryOpenInputFile(string file)
bool isValid = false;
try
using (var tempReader = new MediaFoundationReader(file))
DefaultDecompressionFormat = tempReader.WaveFormat.ToString();
InputPath = file;
isValid = true;
catch (Exception e)
return isValid;
public string DefaultDecompressionFormat
get => defaultDecompressionFormat;
set
defaultDecompressionFormat = value;
RaisePropertyChanged("DefaultDecompressionFormat");
public string SongName get => songName; set
songName = value;
RaisePropertyChanged("SongName");
public string InputPath
get => inputPath;
set
if (inputPath != value)
inputPath = value;
AddToHistory(value);
RaisePropertyChanged("InputPath");
private void AddToHistory(string value)
if (!inputPathHistory.Contains(value))
inputPathHistory.Add(value);
public void Stop()
if (wavePlayer != null)
wavePlayer.Stop();
public void Pause()
if (wavePlayer != null)
wavePlayer.Pause();
RaisePropertyChanged("IsPlaying");
RaisePropertyChanged("IsStopped");
public void Play()
if (String.IsNullOrEmpty(InputPath))
return;
if (wavePlayer == null)
CreatePlayer();
if (lastPlayed != inputPath && reader != null)
reader.Dispose();
reader = null;
if (reader == null)
reader = new MediaFoundationReader(inputPath);
lastPlayed = inputPath;
wavePlayer.Init(reader);
wavePlayer.Play();
RaisePropertyChanged("IsPlaying");
RaisePropertyChanged("IsStopped");
timer.Start();
private void CreatePlayer()
wavePlayer = new WaveOutEvent();
wavePlayer.PlaybackStopped += WavePlayerOnPlaybackStopped;
RaisePropertyChanged("wavePlayer");
private void WavePlayerOnPlaybackStopped(object sender, StoppedEventArgs stoppedEventArgs)
if (reader != null)
SliderPosition = 0;
//reader.Position = 0;
timer.Stop();
if (stoppedEventArgs.Exception != null)
RaisePropertyChanged("IsPlaying");
RaisePropertyChanged("IsStopped");
public void PlayFromUrl(string url, string songname)
Stop();
inputPath = url;
SongName = songname;
Play();
public void Dispose()
wavePlayer?.Dispose();
reader?.Dispose();
播放器的 XAML:
<Grid>
<StackPanel Orientation="Horizontal">
<Button Content="Play" Command="Binding PlayCommand" VerticalAlignment="Center" Width="75" />
<Button Content="Pause" Command="Binding PauseCommand" VerticalAlignment="Center" Width="75" />
<Button Content="Stop" Command="Binding PlayCommand" VerticalAlignment="Center" Width="75" />
<Slider VerticalAlignment="Center" Value="Binding SliderPosition, Mode=TwoWay" Maximum="10" Width="400" />
<TextBlock Text="Binding SongName, FallbackValue=Test" Foreground="White"/>
</StackPanel>
</Grid>
</UserControl>
为新轨道发送数据的 VM 代码:
public class AudioModel
public string Artist get; set;
public string SongName get; set;
public int Duration get; set;
public string URL get; set;
public RelayCommand PlayThisAudioCommand
get;
private set;
public AudioModel()
PlayThisAudioCommand = new RelayCommand(() => PlayThis());
private void PlayThis()
if (URL != null)
TestVM.AudioConrol.PlayFromUrl(URL, SongName);
else;
【问题讨论】:
【参考方案1】:看来您的计时器可能存在多线程问题。事件的顺序似乎是:
第一首曲目
-
PlayFromUrl() 调用 Play() 开始播放文件,并启动计时器。
滑块按预期更新
第二轨
-
当您调用 PlayFromUrl() 时:
调用 Stop()(停止 wavePlayer,并停止计时器)
调用 Play()(启动 wavePlayer,并启动计时器)
然后引发 wavePlayer.PlaybackStopped 事件(由于您之前调用了 wavePlayer.Stop()),该事件调用 WavePlayerOnPlaybackStopped(),后者停止 计时器。
这里的重点是调用 Play() 和 WavePlayerOnPlaybackStopped() 的顺序。事件很可能按上述顺序发生 - 因为 wavePlayer 在另一个线程上引发 PlaybackStopped 事件。
简而言之 - WavePlayerOnPlaybackStopped() stopping 你的计时器 在 Play() 开始 它,这就是为什么你的滑块不是更新。按 Pause 然后 Play 将重新启动计时器,这就是滑块在暂停后开始更新的原因。
您可以通过暂时注释掉 WavePlayerOnPlaybackStopped() 中的代码来测试这一点,这应该可以解决问题 - 尽管当轨道到达终点或停止时您的滑块不会重置为零。
注意:调用 wavePlayer.Stop() 和引发 wavePlayer.PlaybackStopped 事件之间的延迟的原因是由于 nAudio 使用专用线程来处理播放。当您调用 Stop() 时,它必须在实际停止之前完成当前音频缓冲区的处理 - 在大多数情况下会导致几毫秒的延迟。
您可以在 WaveOutEvent 的 DoPlayback
方法中看到这一点:https://github.com/naudio/NAudio/blob/master/NAudio/Wave/WaveOutputs/WaveOutEvent.cs#L147
【讨论】:
谢谢!现在一切正常!刚刚删除 WavePlayerOnPlaybackStopped()以上是关于更改曲目 NAudio 后滑块不起作用的主要内容,如果未能解决你的问题,请参考以下文章