/// <summary>
        /// Callback function for the outputstream.
        /// </summary>
        /// <param name="streamHandle">Bass stream handle that requests sample data.</param>
        /// <param name="buffer">Buffer to write the sampledata in.</param>
        /// <param name="requestedBytes">Requested number of bytes.</param>
        /// <param name="userData"></param>
        /// <returns>Number of bytes read.</returns>
        private int OutputStreamWriteProc(int streamHandle, IntPtr buffer, int requestedBytes, IntPtr userData)
        {
            if (_deviceState == DeviceState.Stopped)
            {
                return((int)BASSStreamProc.BASS_STREAMPROC_END);
            }
            if (_outputStreamEnded)
            {
                return((int)BASSStreamProc.BASS_STREAMPROC_END);
            }
            int read = _inputStream.Read(buffer, requestedBytes);

            if (read == -1)
            {
                // We're done!

                // Play silence until playback has stopped to avoid any buffer underruns.
                read = _silence.Write(buffer, requestedBytes);

                // Set a flag so we call HandleOutputStreamEnded() only once.
                _outputStreamEnded = true;

                // Our input stream is finished, wait for device to end playback
                HandleOutputStreamAboutToEnd();
            }
            return(read);
        }
예제 #2
0
        /// <summary>
        /// Bufferupdate thread executor method.
        /// </summary>
        private void ThreadBufferUpdate()
        {
            try
            {
                while (true)
                {
                    _notifyBufferUpdateThread.WaitOne();

                    // Test for _terminated after waiting for our notify event. The TerminateBufferUpdateThread() method
                    // will first set the _terminated flag before it sets the notify event.
                    if (_terminated)
                    {
                        return;
                    }

                    if (_inputStreamInitialized)
                    {
                        int requestedSamples = _buffer.Space;
                        if (requestedSamples > 0)
                        {
                            if (_readData.Length < requestedSamples)
                            {
                                Array.Resize(ref _readData, requestedSamples);
                            }

                            int samplesRead = _inputStream.Read(_readData, requestedSamples);
                            if (samplesRead > 0)
                            {
                                _buffer.Write(_readData, samplesRead);
                            }
                            else if (samplesRead == -1)
                            {
                                _streamEnded = true;
                            }
                        }
                    }
                    _updateThreadFinished.Set();
                }
            }
            catch (Exception e)
            {
                ServiceRegistration.Get <ILogger>().Error("Exception in bufferupdate thread", e);
            }
        }
예제 #3
0
        /// <summary>
        /// Callback function for the outputstream.
        /// </summary>
        /// <param name="streamHandle">Bass stream handle that requests sample data.</param>
        /// <param name="buffer">Buffer to write the sampledata to.</param>
        /// <param name="requestedBytes">Requested number of bytes.</param>
        /// <param name="userData"></param>
        /// <returns>Number of bytes read.</returns>
        private int OutputStreamWriteProc(int streamHandle, IntPtr buffer, int requestedBytes, IntPtr userData)
        {
            IInputSource inputSource;

            lock (_syncObj)
            {
                if (_state == SessionState.Reset)
                {
                    return(0);
                }
                inputSource = _currentInputSource;
                if (inputSource == null)
                {
                    _state = SessionState.Ended;
                    return((int)BASSStreamProc.BASS_STREAMPROC_END);
                }
            }

            try
            {
                BassStream stream = inputSource.OutputStream;
                int        read   = stream.Read(buffer, requestedBytes);

                bool doCheckNextInputSource = false;
                lock (_syncObj)
                    if (!_isAwaitingNextInputSource && stream.GetPosition() > stream.Length.Subtract(REQUEST_NEXT_ITEM_THRESHOLD))
                    { // Near end of the stream - make sure that next input source is available
                        _isAwaitingNextInputSource = true;
                        doCheckNextInputSource     = true;
                    }
                if (doCheckNextInputSource)
                {
                    _playbackProcessor.CheckInputSourceAvailable();
                }

                if (read > 0)
                {
                    // Normal case, we have finished
                    return(read);
                }

                // Old buffer ran out of samples - either we can get another valid input source below or we are finished. End wait state.
                _isAwaitingNextInputSource = false;

                // Nothing could be read from old input source. Second try: Next input source.
                IInputSource newInputSource = _playbackProcessor.PeekNextInputSource();

                // Special treatment for CD drives: If the new input source is from the same audio CD drive, we must take the stream over
                BassCDTrackInputSource bcdtisOld = inputSource as BassCDTrackInputSource;
                BassCDTrackInputSource bcdtisNew = newInputSource as BassCDTrackInputSource;
                if (bcdtisOld != null && bcdtisNew != null)
                {
                    if (bcdtisOld.SwitchTo(bcdtisNew))
                    {
                        _playbackProcessor.ClearNextInputSource();
                        return(OutputStreamWriteProc(streamHandle, buffer, requestedBytes, userData));
                    }
                }

                lock (_syncObj)
                {
                    _currentInputSource = null;
                    _controller.ScheduleDisposeObject_Async(inputSource);
                    if (newInputSource == null)
                    {
                        _state = SessionState.Ended;
                        return((int)BASSStreamProc.BASS_STREAMPROC_END);
                    }
                }

                if (!MatchesInputSource(newInputSource))
                { // The next available input source is not compatible, so end our stream. The playback processor will start a new playback session later.
                    lock (_syncObj)
                        _state = SessionState.Ended;
                    return((int)BASSStreamProc.BASS_STREAMPROC_END);
                }
                _playbackProcessor.ClearNextInputSource(); // Should be the contents of newInputSource
                lock (_syncObj)
                {
                    _currentInputSource = newInputSource;
                    _state = SessionState.Playing;
                }

                // Next try
                return(OutputStreamWriteProc(streamHandle, buffer, requestedBytes, userData));
            }
            catch (Exception)
            {
                // We might come here due to a race condition. To avoid that, we would have to employ a new manual locking mechanism
                // to avoid that during the execution of this method, no methods are called from outside which change our
                // streams/partner instances.
                return(0);
            }
        }