public async override ValueTask DisposeAsync()
        {
            await _bufferStream.DisposeAsync();

            await _clip.DisposeAsync();

            await base.DisposeAsync();
        }
        public override async ValueTask DisposeAsync()
        {
            await _clip.DisposeAsync();

            await _stream.DisposeAsync();

            await base.DisposeAsync();
        }
        protected override async Task <int> DoReadAsync(byte[] outputBuffer, int offset, int count, CancellationToken cancellationToken)
        {
            void _complete()
            {
                _ = _currentClip.DisposeAsync();
                _playlist.Remove(_currentClip);
                _currentClip = null;
                OnClipCompleted?.Invoke(this, _currentClip);
            }

            // Check if the current song should be skipped
            if (_shouldSkip)
            {
                _shouldSkip = false;
                _complete();
            }

            // Check if any items need to start preparing
            if (_playlist.Count(p => (p.IsReady || p.IsPreparing) && p != _currentClip) == 0)
            {
                var toPrepare = _playlist.FirstOrDefault(p => !p.IsReady && !p.IsPreparing);
                if (toPrepare != null)
                {
                    _ = toPrepare.PrepareAsync();
                }
            }

            // Set the current clip to the next clip if necessary
            if (_currentClip == null)
            {
                _currentClip = _playlist.FirstOrDefault(p => p.IsReady);
            }

            if (_currentClip == null)
            {
                return(0);
            }

            var byteCount = await _currentClip.ReadAsync(outputBuffer, offset, count, cancellationToken);

            if (byteCount == 0)
            {
                _complete();
            }

            return(byteCount);
        }