/// <summary> /// Creates an <see cref="IInputSource"/> object for a given mediaitem. /// </summary> /// <param name="resourceLocator">Locator instance to the media item to create the input source for.</param> /// <param name="mimeType">Mime type of the media item, if present. May be <c>null</c>.</param> /// <returns>Input source object for the given <paramref name="resourceLocator"/> or <c>null</c>, if no input source /// could be created.</returns> public IInputSource CreateInputSource(IResourceLocator resourceLocator, string mimeType) { if (!CanPlay(resourceLocator, mimeType)) { return(null); } IInputSource result; _accessor = resourceLocator.CreateAccessor(); AudioCDResourceAccessor acdra = _accessor as AudioCDResourceAccessor; if (acdra != null) { result = BassCDTrackInputSource.Create(acdra.Drive, acdra.TrackNo); } else { string filePath = _accessor.ResourcePathName; // Network streams INetworkResourceAccessor netra = _accessor as INetworkResourceAccessor; if (netra != null) { result = BassWebStreamInputSource.Create(netra.URL); } // CDDA else if (URLUtils.IsCDDA(filePath)) { ILocalFsResourceAccessor lfra = _accessor as ILocalFsResourceAccessor; if (lfra == null) { return(null); } using (lfra.EnsureLocalFileSystemAccess()) result = BassFsCDTrackInputSource.Create(lfra.LocalFileSystemPath); } else { // Filesystem resources IFileSystemResourceAccessor fsra = _accessor as IFileSystemResourceAccessor; if (fsra == null) { return(null); } if (URLUtils.IsMODFile(filePath)) { result = BassMODFileInputSource.Create(fsra); } else { result = BassAudioFileInputSource.Create(fsra); } } } Log.Debug("InputSourceFactory: Creating input source for media resource '{0}' of type '{1}'", _accessor, result.GetType()); return(result); }
/// <summary> /// Creates an <see cref="IInputSource"/> object for a given mediaitem. /// </summary> /// <param name="resourceLocator">Locator instance to the media item to create the input source for.</param> /// <param name="mimeType">Mime type of the media item, if present. May be <c>null</c>.</param> /// <returns>Input source object for the given <paramref name="resourceLocator"/> or <c>null</c>, if no input source /// could be created.</returns> public IInputSource CreateInputSource(IResourceLocator resourceLocator, string mimeType) { if (!CanPlay(resourceLocator, mimeType)) { return(null); } IInputSource result; using (IResourceAccessor accessor = resourceLocator.CreateAccessor()) { AudioCDResourceAccessor acdra = accessor as AudioCDResourceAccessor; if (acdra != null) { result = BassCDTrackInputSource.Create(acdra.Drive, acdra.TrackNo); } else { string filePath = accessor.ResourcePathName; if (URLUtils.IsCDDA(filePath)) { ILocalFsResourceAccessor lfra = accessor as ILocalFsResourceAccessor; if (lfra == null) { return(null); } result = BassFsCDTrackInputSource.Create(lfra.LocalFileSystemPath); } else { IFileSystemResourceAccessor fsra = accessor as ILocalFsResourceAccessor; if (fsra == null) { return(null); } if (URLUtils.IsMODFile(filePath)) { result = BassMODFileInputSource.Create(fsra); } else { result = BassAudioFileInputSource.Create(fsra); } } // TODO: Handle web streams when we have resource accessors for web URLs: BassWebStreamInputSource.Create(...); } Log.Debug("InputSourceFactory: Creating input source for media resource '{0}' of type '{1}'", accessor, result.GetType()); } return(result); }
/// <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); } }
/// <summary> /// Moves to the next available input source. /// </summary> /// <remarks> /// This method blocks the calling thread as long as the switching to the new input source lasts. This includes /// the crossfading duration (if crossfading is done) or the fading out (if no crossfading is done). /// </remarks> protected void MoveToNextInputSource_Sync() { // TODO: Insert gap between tracks if we are in playback mode Normal IInputSource inputSource = PeekNextInputSource(); if (_playbackSession != null) { BassCDTrackInputSource bcdtisNew = inputSource as BassCDTrackInputSource; IInputSource currentInputSource = _playbackSession.CurrentInputSource; BassCDTrackInputSource bcdtisOld = currentInputSource as BassCDTrackInputSource; if (bcdtisOld != null && bcdtisNew != null) { // Special treatment for CD drives: If the new input source is from the same audio CD drive, we must take the stream over if (bcdtisOld.SwitchTo(bcdtisNew)) { _playbackSession.IsAwaitingNextInputSource = false; ClearNextInputSource(); return; } } // TODO: Trigger crossfading if CF is configured _playbackSession.End(_internalState == InternalPlaybackState.Playing); // Only wait for fade out when we are playing } _internalState = InternalPlaybackState.Playing; if (inputSource == null) { Log.Debug("No more input sources available."); } else { Log.Debug("Playing next input source '{0}'", inputSource); } if (_playbackSession != null) { if (_playbackSession.InitializeWithNewInputSource(inputSource)) { _playbackSession.Play(); ClearNextInputSource(); return; } _playbackSession.Dispose(); _playbackSession = null; } if (inputSource == null) { Ended(); return; } _playbackSession = PlaybackSession.Create(_controller); if (_playbackSession == null) { _internalState = InternalPlaybackState.Stopped; return; } _playbackSession.Play(); _internalState = InternalPlaybackState.Playing; _controller.StateReady(); }