示例#1
0
        public override async Task <IOutputStream> Load(PlaylistItem playlistItem, bool immidiate)
        {
            if (!this.IsStarted)
            {
                await this.Start().ConfigureAwait(false);
            }
            //TODO: Why do we do this? Multiple streams for the same file are valid.
            if (this.StreamFactory.HasActiveStream(playlistItem.FileName))
            {
                Logger.Write(this, LogLevel.Warn, "The stream is already loaded: {0} => {1}", playlistItem.Id, playlistItem.FileName);
            }
            Logger.Write(this, LogLevel.Debug, "Loading stream: {0} => {1}", playlistItem.Id, playlistItem.FileName);
            var flags = BassFlags.Default;

            if (this.Float)
            {
                flags |= BassFlags.Float;
            }
            var stream = this.StreamFactory.CreateInteractiveStream(playlistItem, immidiate, flags);

            if (stream.IsEmpty)
            {
                return(null);
            }
            var outputStream = new BassOutputStream(this, this.PipelineManager, stream, playlistItem);

            outputStream.InitializeComponent(this.Core);
            this.OnLoaded(outputStream);
            return(outputStream);
        }
 public override bool Add(BassOutputStream stream)
 {
     if (this.Queue.Contains(stream.ChannelHandle))
     {
         Logger.Write(this, LogLevel.Debug, "Stream is already enqueued: {0}", stream.ChannelHandle);
         return(false);
     }
     Logger.Write(this, LogLevel.Debug, "Adding stream to the queue: {0}", stream.ChannelHandle);
     //If there's nothing in the queue then we're starting.
     if (this.Queue.Count() == 0)
     {
         var flags = default(BassCrossfadeFlags);
         if (this.Behaviour.Start)
         {
             flags = BassCrossfadeFlags.FadeIn;
         }
         else
         {
             flags = BassCrossfadeFlags.None;
         }
         BassUtils.OK(BassCrossfade.ChannelEnqueue(stream.ChannelHandle, flags));
         return(true);
     }
     BassUtils.OK(BassCrossfade.ChannelEnqueue(stream.ChannelHandle));
     return(true);
 }
示例#3
0
        public override Task <IOutputStream> Duplicate(IOutputStream stream)
        {
            var outputStream = stream as BassOutputStream;

            if (outputStream.Provider.Flags.HasFlag(BassStreamProviderFlags.Serial))
            {
                Logger.Write(this, LogLevel.Warn, "Cannot duplicate stream for file \"{0}\" with serial provider.", stream.FileName);
                return(null);
            }
            var flags = BassFlags.Default;

            if (this.Float)
            {
                flags |= BassFlags.Float;
            }
            var result = this.StreamFactory.CreateBasicStream(stream.PlaylistItem, flags);

            if (result.IsEmpty)
            {
                return(null);
            }
            outputStream = new BassOutputStream(this, this.PipelineManager, result, stream.PlaylistItem);
            outputStream.InitializeComponent(this.Core);
#if NET40
            return(TaskEx.FromResult <IOutputStream>(outputStream));
#else
            return(Task.FromResult <IOutputStream>(outputStream));
#endif
        }
示例#4
0
 protected virtual bool TryCalculateReplayGain(BassOutputStream stream, out float gain, out float peak, out ReplayGainMode mode)
 {
     Logger.Write(this, LogLevel.Debug, "Attempting to calculate track replay gain for file \"{0}\".", stream.FileName);
     try
     {
         var info = default(ReplayGainInfo);
         if (BassReplayGain.Process(stream.ChannelHandle, out info))
         {
             Logger.Write(this, LogLevel.Debug, "Calculated track replay gain for file \"{0}\": {1}dB", stream.FileName, ReplayGainEffect.GetVolume(info.gain));
             gain = info.gain;
             peak = info.peak;
             mode = ReplayGainMode.Track;
             return(true);
         }
         else
         {
             Logger.Write(this, LogLevel.Warn, "Failed to calculate track replay gain for file \"{0}\".", stream.FileName);
         }
     }
     catch (Exception e)
     {
         Logger.Write(this, LogLevel.Warn, "Failed to calculate track replay gain for file \"{0}\": {1}", stream.FileName, e.Message);
     }
     gain = 0;
     peak = 0;
     mode = ReplayGainMode.None;
     return(false);
 }
        public override int Position(BassOutputStream stream)
        {
            var count          = default(int);
            var channelHandles = BassCrossfade.GetChannels(out count);

            return(channelHandles.IndexOf(stream.ChannelHandle));
        }
        protected virtual async Task CreatePipeline(BassOutputStream stream)
#endif
        {
            if (this.Semaphore == null)
            {
#if NET40
                return(TaskEx.FromResult(false));
#else
                return;
#endif
            }
#if NET40
            if (!this.Semaphore.Wait(SYNCHRONIZE_PIPELINE_TIMEOUT))
#else
            if (!await this.Semaphore.WaitAsync(SYNCHRONIZE_PIPELINE_TIMEOUT).ConfigureAwait(false))
#endif
            {
                throw new InvalidOperationException(string.Format("{0} is already locked.", this.GetType().Name));
            }
            try
            {
                this.CreatePipelineCore(stream);
            }
            finally
            {
                this.Semaphore.Release();
            }
#if NET40
            return(TaskEx.FromResult(false));
#endif
        }
 protected virtual void CreatePipelineCore(BassOutputStream stream)
 {
     this.Pipeline = this.PipelineFactory.CreatePipeline(stream);
     this.Pipeline.Input.Add(stream);
     this.Pipeline.Error += this.OnError;
     this.OnCreated();
 }
示例#8
0
        public static bool Remove(BassOutputStream stream)
        {
            Prune();
            var reference = default(WeakReference <BassOutputStream>);

            return(Streams.TryRemove(stream.FileName, out reference));
        }
 public BassAsioStreamOutput(BassAsioStreamOutputBehaviour behaviour, BassOutputStream stream)
     : this()
 {
     this.Behaviour = behaviour;
     if (BassUtils.GetChannelDsdRaw(stream.ChannelHandle))
     {
         this.Rate   = BassUtils.GetChannelDsdRate(stream.ChannelHandle);
         this.Flags |= BassFlags.DSDRaw;
     }
     else
     {
         if (behaviour.Output.Rate == stream.Rate)
         {
             this.Rate = stream.Rate;
         }
         else if (!behaviour.Output.EnforceRate && BassAsioDevice.Info.SupportedRates.Contains(stream.Rate))
         {
             this.Rate = stream.Rate;
         }
         else
         {
             Logger.Write(this, LogLevel.Debug, "The requested output rate is either enforced or the device does not support the stream's rate: {0} => {1}", stream.Rate, behaviour.Output.Rate);
             this.Rate = behaviour.Output.Rate;
         }
         if (behaviour.Output.Float)
         {
             this.Flags |= BassFlags.Float;
         }
     }
     this.Channels = stream.Channels;
 }
示例#10
0
 protected static void OnActiveChanged(BassOutputStream sender)
 {
     if (ActiveChanged == null)
     {
         return;
     }
     ActiveChanged(sender, EventArgs.Empty);
 }
示例#11
0
 public BassWasapiStreamOutput(BassWasapiStreamOutputBehaviour behaviour, BassOutputStream stream)
     : this()
 {
     this.Behaviour = behaviour;
     this.Rate      = behaviour.Output.Rate;
     this.Channels  = BassWasapiDevice.Info.Outputs;
     //WASAPI requires BASS_SAMPLE_FLOAT so don't bother respecting the output's Float setting.
     this.Flags = BassFlags.Decode | BassFlags.Float;
 }
示例#12
0
        protected virtual void Remove(BassOutputStream stream)
        {
            var effect = default(ReplayGainEffect);

            if (this.Effects.TryRemove(stream, out effect))
            {
                effect.Dispose();
            }
        }
 public BassReplayGainStreamComponent(BassReplayGainBehaviour behaviour, BassOutputStream stream)
 {
     this.Behaviour = behaviour;
     this.Rate      = behaviour.Output.Rate;
     this.Channels  = stream.Channels;
     this.Flags     = BassFlags.Decode;
     if (this.Behaviour.Output.Float)
     {
         this.Flags |= BassFlags.Float;
     }
 }
 public override bool Add(BassOutputStream stream)
 {
     if (this.Queue.Contains(stream.ChannelHandle))
     {
         Logger.Write(this, LogLevel.Debug, "Stream is already enqueued: {0}", stream.ChannelHandle);
         return(false);
     }
     Logger.Write(this, LogLevel.Debug, "Adding stream to the queue: {0}", stream.ChannelHandle);
     BassUtils.OK(BassGapless.ChannelEnqueue(stream.ChannelHandle));
     return(true);
 }
示例#15
0
 public BassAsioStreamOutput(BassAsioStreamOutputBehaviour behaviour, BassOutputStream stream)
     : this()
 {
     this.Behaviour = behaviour;
     this.Rate      = behaviour.Output.Rate;
     this.Channels  = BassAsioDevice.Info.Outputs;
     this.Flags     = BassFlags.Decode;
     if (this.Behaviour.Output.Float)
     {
         this.Flags |= BassFlags.Float;
     }
 }
 public override bool Remove(BassOutputStream stream, Action <BassOutputStream> callBack)
 {
     if (!this.Queue.Contains(stream.ChannelHandle))
     {
         Logger.Write(this, LogLevel.Debug, "Stream is not enqueued: {0}", stream.ChannelHandle);
         return(false);
     }
     Logger.Write(this, LogLevel.Debug, "Removing stream from the queue: {0}", stream.ChannelHandle);
     BassUtils.OK(BassGapless.ChannelRemove(stream.ChannelHandle));
     callBack(stream);
     return(true);
 }
示例#17
0
        public static bool Get(string fileName, out BassOutputStream stream)
        {
            Prune();
            var reference = default(WeakReference <BassOutputStream>);

            if (Streams.TryGetValue(fileName, out reference))
            {
                stream = reference.Target;
                return(true);
            }
            stream = null;
            return(false);
        }
        protected virtual async Task WithPipeline(BassOutputStream stream, Action <IBassStreamPipeline> action)
        {
            if (this.Pipeline == null)
            {
                this.CreatePipelineCore(stream);
            }
            else if (!this.Pipeline.Input.CheckFormat(stream))
            {
                Logger.Write(this, LogLevel.Debug, "Current pipeline cannot accept stream, shutting it down: {0}", stream.ChannelHandle);
                this.FreePipelineCore();
                await this.WithPipeline(stream, action).ConfigureAwait(false);

                return;
            }
            action(this.Pipeline);
        }
        public override bool CheckFormat(BassOutputStream stream)
        {
            var rate     = default(int);
            var channels = default(int);

            if (BassUtils.GetChannelDsdRaw(stream.ChannelHandle))
            {
                rate     = BassUtils.GetChannelDsdRate(stream.ChannelHandle);
                channels = BassUtils.GetChannelCount(stream.ChannelHandle);
            }
            else
            {
                rate     = BassUtils.GetChannelPcmRate(stream.ChannelHandle);
                channels = BassUtils.GetChannelCount(stream.ChannelHandle);
            }
            return(this.Rate == rate && this.Channels == channels);
        }
示例#20
0
        protected virtual void CreatePipeline(BassOutputStream stream, out IBassStreamInput input, out IEnumerable <IBassStreamComponent> components, out IBassStreamOutput output)
        {
            var e = new CreatingPipelineEventArgs(this.QueryPipeline(), stream);

            this.OnCreatingPipeline(e);
            input      = e.Input;
            components = e.Components;
            output     = e.Output;
            if (input == null)
            {
                throw new NotImplementedException("Failed to locate a suitable pipeline input.");
            }
            if (output == null)
            {
                throw new NotImplementedException("Failed to locate a suitable pipeline output.");
            }
        }
示例#21
0
        public override async Task <IOutputStream> Load(PlaylistItem playlistItem, bool immidiate)
        {
            if (!this.IsStarted)
            {
                await this.Start().ConfigureAwait(false);
            }
            Logger.Write(this, LogLevel.Debug, "Loading stream: {0} => {1}", playlistItem.Id, playlistItem.FileName);
            var stream = await this.StreamFactory.CreateStream(playlistItem, immidiate).ConfigureAwait(false);

            if (stream.IsEmpty)
            {
                return(null);
            }
            var outputStream = new BassOutputStream(this, this.PipelineManager, stream.Provider, playlistItem, stream.ChannelHandle);

            outputStream.InitializeComponent(this.Core);
            return(outputStream);
        }
 public BassCrossfadeStreamInput(BassCrossfadeStreamInputBehaviour behaviour, BassOutputStream stream)
 {
     this.Behaviour = behaviour;
     this.Channels  = stream.Channels;
     this.Flags     = BassFlags.Decode;
     if (BassUtils.GetChannelDsdRaw(stream.ChannelHandle))
     {
         throw new InvalidOperationException("Cannot apply effects to DSD streams.");
     }
     else
     {
         this.Rate = stream.Rate;
         if (behaviour.Output.Float)
         {
             this.Flags |= BassFlags.Float;
         }
     }
 }
 public BassGaplessStreamInput(BassGaplessStreamInputBehaviour behaviour, BassOutputStream stream)
 {
     this.Behaviour = behaviour;
     this.Channels  = stream.Channels;
     this.Flags     = BassFlags.Decode;
     if (BassUtils.GetChannelDsdRaw(stream.ChannelHandle))
     {
         this.Rate   = BassUtils.GetChannelDsdRate(stream.ChannelHandle);
         this.Flags |= BassFlags.DSDRaw;
     }
     else
     {
         this.Rate = stream.Rate;
         if (behaviour.Output.Float)
         {
             this.Flags |= BassFlags.Float;
         }
     }
 }
示例#24
0
        protected virtual void Add(BassOutputStream stream)
        {
            if (!FileSystemHelper.IsLocalPath(stream.FileName))
            {
                return;
            }
            if (BassUtils.GetChannelDsdRaw(stream.ChannelHandle))
            {
                return;
            }
            var gain = default(float);
            var peak = default(float);
            var mode = default(ReplayGainMode);

            if (!this.TryGetReplayGain(stream, out gain, out mode))
            {
                if (this.OnDemand)
                {
                    //TODO: Bad .Result
                    using (var duplicated = this.Output.Duplicate(stream).Result as BassOutputStream)
                    {
                        if (duplicated == null)
                        {
                            Logger.Write(this, LogLevel.Warn, "Failed to duplicate stream for file \"{0}\", cannot calculate.", stream.FileName);
                            return;
                        }
                        if (!this.TryCalculateReplayGain(duplicated, out gain, out peak, out mode))
                        {
                            return;
                        }
                    }
                    this.Dispatch(() => this.UpdateMetaData(stream, gain, peak, mode));
                }
                else
                {
                    return;
                }
            }
            var effect = new ReplayGainEffect(stream.ChannelHandle, gain, mode);

            effect.Activate();
            this.Effects.Add(stream, effect);
        }
 public BassWasapiStreamOutput(BassWasapiStreamOutputBehaviour behaviour, BassOutputStream stream)
     : this()
 {
     this.Behaviour = behaviour;
     if (behaviour.Output.Rate == stream.Rate)
     {
         this.Rate = stream.Rate;
     }
     else if (!behaviour.Output.EnforceRate && BassWasapiDevice.Info.SupportedRates.Contains(stream.Rate))
     {
         this.Rate = stream.Rate;
     }
     else
     {
         Logger.Write(this, LogLevel.Debug, "The requested output rate is either enforced or the device does not support the stream's rate: {0} => {1}", stream.Rate, behaviour.Output.Rate);
         this.Rate = behaviour.Output.Rate;
     }
     this.Channels = BassWasapiDevice.Info.Outputs;
     //WASAPI requires BASS_SAMPLE_FLOAT so don't bother respecting the output's Float setting.
     this.Flags = BassFlags.Decode | BassFlags.Float;
 }
        public async Task WithPipelineExclusive(BassOutputStream stream, Action <IBassStreamPipeline> action)
        {
            if (this.Semaphore == null)
            {
                return;
            }
#if NET40
            if (!this.Semaphore.Wait(SYNCHRONIZE_PIPELINE_TIMEOUT))
#else
            if (!await this.Semaphore.WaitAsync(SYNCHRONIZE_PIPELINE_TIMEOUT).ConfigureAwait(false))
#endif
            {
                throw new InvalidOperationException(string.Format("{0} is already locked.", this.GetType().Name));
            }
            try
            {
                await this.WithPipeline(stream, action).ConfigureAwait(false);
            }
            finally
            {
                this.Semaphore.Release();
            }
        }
示例#27
0
        public IBassStreamPipeline CreatePipeline(BassOutputStream stream)
        {
            var input      = default(IBassStreamInput);
            var components = default(IEnumerable <IBassStreamComponent>);
            var output     = default(IBassStreamOutput);

            this.CreatePipeline(stream, out input, out components, out output);
            var pipeline = new BassStreamPipeline(
                input,
                components,
                output
                );

            try
            {
                pipeline.Connect();
                if (Logger.IsDebugEnabled(this))
                {
                    if (components.Any())
                    {
                        Logger.Write(
                            this,
                            LogLevel.Debug,
                            "Connected pipeline: {0}",
                            string.Join(" => ", pipeline.All.Select(component => string.Format("\"{0}\"", component.GetType().Name)))
                            );
                    }
                }
            }
            catch (Exception e)
            {
                Logger.Write(this, LogLevel.Error, "Failed to create pipeline: {0}", e.Message);
                pipeline.Dispose();
                throw;
            }
            return(pipeline);
        }
 public override bool Remove(BassOutputStream stream, Action <BassOutputStream> callBack)
 {
     if (!this.Queue.Contains(stream.ChannelHandle))
     {
         Logger.Write(this, LogLevel.Debug, "Stream is not enqueued: {0}", stream.ChannelHandle);
         return(false);
     }
     Logger.Write(this, LogLevel.Debug, "Removing stream from the queue: {0}", stream.ChannelHandle);
     //If there's only one stream in the queue then we're stopping.
     //Block so the fade out behaviour can be applied before Reset is called.
     if (this.Queue.Count() == 1)
     {
         BassUtils.OK(BassCrossfade.StreamReset(this.Behaviour.Stop));
         callBack(stream);
         return(true);
     }
     //Fork so fade out doesn't block the next track being enqueued.
     this.Dispatch(() =>
     {
         BassUtils.OK(BassCrossfade.ChannelRemove(stream.ChannelHandle));
         callBack(stream);
     });
     return(true);
 }
 protected virtual Task CreatePipeline(BassOutputStream stream)
示例#30
0
        protected virtual async Task UpdateMetaData(BassOutputStream stream, float gain, float peak, ReplayGainMode mode)
        {
            var names = new HashSet <string>();

            lock (stream.PlaylistItem.MetaDatas)
            {
                var metaDatas = stream.PlaylistItem.MetaDatas.ToDictionary(
                    element => element.Name,
                    StringComparer.OrdinalIgnoreCase
                    );
                var metaDataItem = default(MetaDataItem);
                if (gain != 0)
                {
                    var name = default(string);
                    switch (mode)
                    {
                    case ReplayGainMode.Album:
                        name = CommonMetaData.ReplayGainAlbumGain;
                        break;

                    case ReplayGainMode.Track:
                        name = CommonMetaData.ReplayGainTrackGain;
                        break;
                    }
                    if (!metaDatas.TryGetValue(name, out metaDataItem))
                    {
                        metaDataItem = new MetaDataItem(name, MetaDataItemType.Tag);
                        stream.PlaylistItem.MetaDatas.Add(metaDataItem);
                    }
                    metaDataItem.Value = Convert.ToString(gain);
                    names.Add(name);
                }
                if (peak != 0)
                {
                    var name = default(string);
                    switch (mode)
                    {
                    case ReplayGainMode.Album:
                        name = CommonMetaData.ReplayGainAlbumPeak;
                        break;

                    case ReplayGainMode.Track:
                        name = CommonMetaData.ReplayGainTrackPeak;
                        break;
                    }
                    if (!metaDatas.TryGetValue(name, out metaDataItem))
                    {
                        metaDataItem = new MetaDataItem(name, MetaDataItemType.Tag);
                        stream.PlaylistItem.MetaDatas.Add(metaDataItem);
                    }
                    metaDataItem.Value = Convert.ToString(peak);
                    names.Add(name);
                }
            }
            await this.MetaDataManager.Save(
                new[] { stream.PlaylistItem },
                this.WriteTags,
                false,
                names.ToArray()
                ).ConfigureAwait(false);
        }