public int CalculateSamplesNeeded()
        {
            long currentWriteTime = Stopwatch.GetTimestamp();
            bool isInitializing   = _lastWriteTime == 0;
            bool detectedUnderrun = false;

            if (!isInitializing)
            {
                double elapsedSeconds = (currentWriteTime - _lastWriteTime) / (double)Stopwatch.Frequency;
                // Due to rounding errors this doesn't work well in audio throttle mode unless enough time has passed
                if (elapsedSeconds >= 0.001)
                {
                    _remainingSamples -= (int)Math.Round(elapsedSeconds * Sound.SampleRate);
                    if (_remainingSamples < 0)
                    {
                        _remainingSamples = 0;
                        _sound.OnUnderrun();
                        detectedUnderrun = true;
                    }
                    _lastWriteTime = currentWriteTime;
                }
            }
            else
            {
                _lastWriteTime = currentWriteTime;
            }
            int samplesNeeded = BufferSizeSamples - _remainingSamples;

            if (isInitializing || detectedUnderrun)
            {
                _sound.HandleInitializationOrUnderrun(detectedUnderrun, ref samplesNeeded);
            }
            return(samplesNeeded);
        }
        public int CalculateSamplesNeeded()
        {
            long currentWriteTime = Stopwatch.GetTimestamp();
            int  playCursor       = _deviceBuffer.CurrentPlayPosition;
            int  writeCursor      = _deviceBuffer.CurrentWritePosition;
            bool isInitializing   = _actualWriteOffsetBytes == -1;
            bool detectedUnderrun = false;

            if (!isInitializing)
            {
                double elapsedSeconds    = (currentWriteTime - _lastWriteTime) / (double)Stopwatch.Frequency;
                double bufferSizeSeconds = (double)BufferSizeSamples / Sound.SampleRate;
                int    cursorDelta       = CircularDistance(_lastWriteCursor, writeCursor, BufferSizeBytes);
                cursorDelta            += BufferSizeBytes * (int)Math.Round((elapsedSeconds - (cursorDelta / (double)(Sound.SampleRate * Sound.BlockAlign))) / bufferSizeSeconds);
                _filledBufferSizeBytes -= cursorDelta;
                if (_filledBufferSizeBytes < 0)
                {
                    _sound.OnUnderrun();
                    detectedUnderrun = true;
                }
            }
            if (isInitializing || detectedUnderrun)
            {
                _actualWriteOffsetBytes = writeCursor;
                _filledBufferSizeBytes  = 0;
            }
            int samplesNeeded = CircularDistance(_actualWriteOffsetBytes, playCursor, BufferSizeBytes) / Sound.BlockAlign;

            if (isInitializing || detectedUnderrun)
            {
                _sound.HandleInitializationOrUnderrun(detectedUnderrun, ref samplesNeeded);
            }
            _lastWriteTime   = currentWriteTime;
            _lastWriteCursor = writeCursor;
            return(samplesNeeded);
        }