更改曲目 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 后滑块不起作用的主要内容,如果未能解决你的问题,请参考以下文章

引导滑块不起作用

JQuery Month 滑块不起作用

通过ajax加载时jquery滑块不起作用

在百里香弹簧靴中,动态旋转木马滑块不起作用

无限滚动加载更多信息后,滑动滑块不起作用

以编程方式创建 UIView 后滑块没有响应