更改轨道 NAudio 后滑块不起作用
Slider do not work after changing track NAudio
我将滑块作为配乐的轨道栏 timeline.Soundtrack 使用 NAudion 从 Web 播放。使用 NAudio WPF example.I 的所有代码仅更改了可访问性 modifiers.First 当我开始播放第一首曲目时一切正常 good.But 如果我切换到下一首曲目,滑块仍在 start.And 仅更改如果我点击暂停然后播放。
完整理解:
第一首曲目 - 滑块工作并移动。
切换到下一首曲目 - 滑块在开头并且不移动。但它在按暂停后开始移动,然后按播放。它立即移动到当前正在播放的位置并继续正常运行。接下来的每首曲目也是如此。
PlayerUserControl 的 VM 代码:
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;
}
}
您的计时器似乎存在多线程问题。事件的顺序似乎是:
第一首曲目
- PlayFromUrl() 调用 Play() 开始播放文件,启动 计时器。
- 滑块按预期更新
第二首曲目
- 当您调用 PlayFromUrl() 时:
- 调用 Stop()(停止 wavePlayer,停止 计时器)
- 调用 Play()(启动 wavePlayer,启动 计时器)
- Then wavePlayer.PlaybackStopped 事件被引发(由于您之前调用了 wavePlayer.Stop()) ,它调用 WavePlayerOnPlaybackStopped(),停止 计时器。
这里的重点是调用 Play() 和 WavePlayerOnPlaybackStopped() 的顺序。很可能事件按上述顺序发生 - 因为 wavePlayer 在另一个线程上引发了 PlaybackStopped 事件。
简而言之 - WavePlayerOnPlaybackStopped() 正在停止 你的计时器 after Play() started 它,这就是您的滑块未更新的原因。按暂停然后播放将重新启动计时器,这就是滑块在暂停后开始更新的原因。
您可以通过暂时注释掉 WavePlayerOnPlaybackStopped() 中的代码来对此进行测试,这应该可以解决问题 - 尽管当曲目到达终点或停止时您的滑块不会重置为零。
注意:调用 wavePlayer.Stop() 和引发 wavePlayer.PlaybackStopped 事件之间出现延迟的原因是 nAudio 使用专用线程处理播放。当您调用 Stop() 时,它必须在实际停止之前完成对当前音频缓冲区的处理——这在大多数情况下会导致几毫秒的延迟。
您可以在 WaveOutEvent 的 DoPlayback
方法中看到这一点:https://github.com/naudio/NAudio/blob/master/NAudio/Wave/WaveOutputs/WaveOutEvent.cs#L147
我将滑块作为配乐的轨道栏 timeline.Soundtrack 使用 NAudion 从 Web 播放。使用 NAudio WPF example.I 的所有代码仅更改了可访问性 modifiers.First 当我开始播放第一首曲目时一切正常 good.But 如果我切换到下一首曲目,滑块仍在 start.And 仅更改如果我点击暂停然后播放。
完整理解:
第一首曲目 - 滑块工作并移动。
切换到下一首曲目 - 滑块在开头并且不移动。但它在按暂停后开始移动,然后按播放。它立即移动到当前正在播放的位置并继续正常运行。接下来的每首曲目也是如此。
PlayerUserControl 的 VM 代码:
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;
}
}
您的计时器似乎存在多线程问题。事件的顺序似乎是:
第一首曲目
- PlayFromUrl() 调用 Play() 开始播放文件,启动 计时器。
- 滑块按预期更新
第二首曲目
- 当您调用 PlayFromUrl() 时:
- 调用 Stop()(停止 wavePlayer,停止 计时器)
- 调用 Play()(启动 wavePlayer,启动 计时器)
- Then wavePlayer.PlaybackStopped 事件被引发(由于您之前调用了 wavePlayer.Stop()) ,它调用 WavePlayerOnPlaybackStopped(),停止 计时器。
这里的重点是调用 Play() 和 WavePlayerOnPlaybackStopped() 的顺序。很可能事件按上述顺序发生 - 因为 wavePlayer 在另一个线程上引发了 PlaybackStopped 事件。
简而言之 - WavePlayerOnPlaybackStopped() 正在停止 你的计时器 after Play() started 它,这就是您的滑块未更新的原因。按暂停然后播放将重新启动计时器,这就是滑块在暂停后开始更新的原因。
您可以通过暂时注释掉 WavePlayerOnPlaybackStopped() 中的代码来对此进行测试,这应该可以解决问题 - 尽管当曲目到达终点或停止时您的滑块不会重置为零。
注意:调用 wavePlayer.Stop() 和引发 wavePlayer.PlaybackStopped 事件之间出现延迟的原因是 nAudio 使用专用线程处理播放。当您调用 Stop() 时,它必须在实际停止之前完成对当前音频缓冲区的处理——这在大多数情况下会导致几毫秒的延迟。
您可以在 WaveOutEvent 的 DoPlayback
方法中看到这一点:https://github.com/naudio/NAudio/blob/master/NAudio/Wave/WaveOutputs/WaveOutEvent.cs#L147