jfversluis / Plugin.Maui.Audio

Plugin.Maui.Audio provides the ability to play audio inside a .NET MAUI application

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

AudioPlayer Duration and CurrentPosition return 0 until playing

MohamedNoordin opened this issue · comments

var audioPlayer = AudioManager.Current.CreatePlayer(stream);
Console.WriteLine(audioPlayer.Duration); // 0
Console.WriteLine(audioPlayer.CurrentPosition); // 0
audioPlayer.Play();
// ...
Console.WriteLine(audioPlayer.Duration); // 8.35...
Console.WriteLine(audioPlayer.CurrentPosition); // 1.57...

AudioPlayer Duration and CurrentPosition properties always return 0 until AudioPlayer.Play() is called. However, the playback starts without problems, the playback can seek, rewind/fast-forward without problems.

That is interesting! I have just tested on macOS and that is behaving. What platform are you seeing this issue on?

I'm not sure why this behavior is observed. Here is my full code:

    [ObservableObject]
    public partial class AudioPlayerViewModel
    {
        private Stream speechStream;
        private IAudioPlayer audioPlayer;

        [ObservableProperty]
        private Voice voice;

        [ObservableProperty]
        private string currentDateTime;

        [ObservableProperty]
        private string currentPositionFormatted;

        [ObservableProperty]
        private double duration;

        [ObservableProperty]
        private double currentPosition;

        public AudioPlayerViewModel(Stream speechStream, Voice voice)
        {
            this.speechStream = speechStream;
            this.Voice = voice;
            this.CurrentDateTime = DateTime.Now.ToString("MM.dd.yy, hh:mm");
        }

        [RelayCommand]
        private async Task InitializeAsync()
        {
            audioPlayer = AudioManager.Current.CreatePlayer(speechStream);
                   Debug.WriteLine(audioPlayer.Duration.ToString()); // 0
            Debug.WriteLine(audioPlayer.CurrentPosition.ToString()); // 0

            audioPlayer.Play();
            Debug.WriteLine(audioPlayer.Duration.ToString()); // Still 0 even after playback
            Debug.WriteLine(audioPlayer.CurrentPosition.ToString()); // 0

            UpdatePlaybackPosition();
        }

        [RelayCommand]
        private async Task PlayPauseAsync()
        {
            if (audioPlayer is not null)
            {
                // Stopped/paused
                if (!audioPlayer.IsPlaying)
                {
                    audioPlayer.Play();
            Debug.WriteLine(audioPlayer.Duration.ToString()); // Shows actual duration
            Debug.WriteLine(audioPlayer.CurrentPosition.ToString()); // Shows current position

                    UpdatePlaybackPosition();
                }
                else // Playing
                {
                    audioPlayer.Pause();
                }
            }
            else
            {
                audioPlayer = AudioManager.Current.CreatePlayer(speechStream);
                Debug.WriteLine(audioPlayer.Duration.ToString()); // 0
                Debug.WriteLine(audioPlayer.CurrentPosition.ToString()); // 0

                audioPlayer.Play();
                Debug.WriteLine(audioPlayer.Duration.ToString()); // Also 0
                Debug.WriteLine(audioPlayer.CurrentPosition.ToString()); // 0

                UpdatePlaybackPosition();
            }
        }
    }
...

I'm currently testing this code on Windows 11. Maybe it takes some time to initialize values.

I can confirm that this is the Windows behavior. As the OP said, the duration is zero when a Windows program using AudioPlayer is first started.

FYI, the initial Duration is not zero on Android, so I guess this is strictly a Windows issue.

I notice in the AudioPlayer.Windows.cs code that the Windows Duration value is calculated thus (line 13):

public double Duration => player.PlaybackSession.NaturalDuration.TotalSeconds;

After a bit of googling there is a StackOverflow issue that links to the MediaPlayer documentation that states that "NaturalDuration cannot be determined until after MediaOpened has occurred." I'm guessing that happens asynchronously from the creation of the Windows MediaPlayer (this is not the exact property being read by AudioPlayer, but I'm guessing it's the same issue).

I also noticed if Duration is checked sometime later that it is no longer zero, even if the AudioPlayer has not been put in play mode yet. This leads me to believe that there this is a delay in opening the media after the Windows MediaPlayer has been created.

I'm not sure what the best approach is as far as fixing this issue. Maybe the AudioManager should be waiting for the Windows MediaPlayer to signal a MediaOpened event before returning the AudioPlayer for use? There is also on OpenAsync method that is accessible from the MediaPlayer Source property. Maybe that should be called before the AudioManager returns the AudioPlayer?

As a work around, I would suggest putting some kind of delay in a Windows program before actually using the AudioPlayer to return Duration.

Also, maybe the Windows MediaPlayer should be exposed as a public Property so that programmers have the option to work with it directly if they want to do some kind of workaround in cases like this one.