Слайдер не работает после смены трека NAudio - PullRequest
0 голосов
/ 06 февраля 2019

У меня есть ползунок в качестве трекбара для временной шкалы саундтрека. Воспроизведение звукового сопровождения из Интернета с помощью NAudion.Весь код используется из примера NAudio WPF. Я изменил только модификаторы доступности. В первый раз, когда я начинаю воспроизведение, первый трек все работает хорошо. Но если я переключаюсь на следующий трек, слайдер все еще в начале. И меняется только если я нажимаю Pause, затем Play.

Для полного понимания:

Первый трек - слайдер работает и перемещается.

Переход к следующему треку - слайдер в начале и не двигается.Но он начинает двигаться после нажатия Pause, затем Play.Он сразу перемещается в то место, где в данный момент идет воспроизведение, и продолжает нормальную работу.И так с каждой следующей дорожкой.

Код виртуальной машины для 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 Ответ

0 голосов
/ 07 февраля 2019

Похоже, у вас может быть проблема многопоточности с вашим таймером.Последовательность событий выглядит следующим образом:

Первая дорожка

  1. PlayFromUrl () вызывает Play (), которая запускает воспроизведение файла, а запускает таймер.
  2. Ползунок обновляется, как и ожидалось

Второй трек

  1. При вызове PlayFromUrl () он:
  2. Calls Stop () (который останавливаетсяwavePlayer и останавливает таймер)
  3. вызывает Play () (который запускает wavePlayer, а запускает таймер)
  4. Затем Возникает событие wavePlayer.PlaybackStopped (из-за вашего более раннего вызова wavePlayer.Stop ()), который вызывает WavePlayerOnPlaybackStopped (), который останавливает таймер.

Важным моментом здесь является порядок вызова Play () и WavePlayerOnPlaybackStopped ().Весьма вероятно, что события происходят в указанном выше порядке - поскольку wavePlayer вызывает событие PlaybackStopped в другом потоке.

Вкратце - WavePlayerOnPlaybackStopped () равен , останавливая ваш таймер после Play () запустил it, поэтому ваш слайдер не обновляется.Нажмите Pause, затем Play перезапустит таймер, поэтому ползунок начинает обновляться после паузы.

Вы можете проверить это, временно закомментировав код в WavePlayerOnPlaybackStopped (), что должно устранить проблему - хотя ваш ползунок будетне сбрасывается в ноль, когда дорожка достигает конца или останавливается.

ПРИМЕЧАНИЕ. Причина задержки между вызовом wavePlayer.Stop () и вызванным событием wavePlayer.PlaybackStopped связана с тем, что nAudio использует выделенный поток дляобрабатывать воспроизведение.Когда вы вызываете Stop (), он должен завершить обработку текущего аудиобуфера перед его фактической остановкой, что в большинстве случаев приводит к задержке в несколько миллисекунд.

Это можно увидеть в действии в * 1049 WaveOutEvent.* метод: https://github.com/naudio/NAudio/blob/master/NAudio/Wave/WaveOutputs/WaveOutEvent.cs#L147

...