/// <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); }
/// <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); } }
/// <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); } }