Exemplo n.º 1
0
        private int prepareStream(Stream data, bool quick)
        {
            //encapsulate incoming stream with async buffer if it isn't already.
            dataStream = data as AsyncBufferStream ?? new AsyncBufferStream(data, quick ? 8 : -1);

            fileCallbacks = new FileCallbacks(new DataStreamFileProcedures(dataStream));

            BassFlags flags  = Preview ? 0 : BassFlags.Decode | BassFlags.Prescan;
            int       stream = Bass.CreateStream(StreamSystem.NoBuffer, flags, fileCallbacks.Callbacks, fileCallbacks.Handle);

            if (!Preview)
            {
                // We assign the BassFlags.Decode streams to the device "bass_nodevice" to prevent them from getting
                // cleaned up during a Bass.Free call. This is necessary for seamless switching between audio devices.
                // Further, we provide the flag BassFlags.FxFreeSource such that freeing the stream also frees
                // all parent decoding streams.
                const int bass_nodevice = 0x20000;

                Bass.ChannelSetDevice(stream, bass_nodevice);
                tempoAdjustStream = BassFx.TempoCreate(stream, BassFlags.Decode | BassFlags.FxFreeSource);
                Bass.ChannelSetDevice(tempoAdjustStream, bass_nodevice);
                stream = BassFx.ReverseCreate(tempoAdjustStream, 5f, BassFlags.Default | BassFlags.FxFreeSource);

                Bass.ChannelSetAttribute(stream, ChannelAttribute.TempoUseQuickAlgorithm, 1);
                Bass.ChannelSetAttribute(stream, ChannelAttribute.TempoOverlapMilliseconds, 4);
                Bass.ChannelSetAttribute(stream, ChannelAttribute.TempoSequenceMilliseconds, 30);
            }

            return(stream);
        }
Exemplo n.º 2
0
        private void LoadSong()
        {
            BassFlags Flags = Preview ? 0 : (BassFlags.Decode | BassFlags.Prescan);

            // Create a new stream
            CleanStream = Bass.CreateStream(AudioFile.Content, 0, AudioFile.Size, Flags);

            if (!Preview)
            {
                Stream = BassFx.TempoCreate(CleanStream, BassFlags.Decode | BassFlags.FxFreeSource);

                TempoStream = Stream;

                Stream = BassFx.ReverseCreate(Stream, 5f, BassFlags.FxFreeSource);

                Bass.ChannelSetAttribute(Stream, ChannelAttribute.TempoUseQuickAlgorithm, 1);
                Bass.ChannelSetAttribute(Stream, ChannelAttribute.TempoOverlapMilliseconds, 4);
                Bass.ChannelSetAttribute(Stream, ChannelAttribute.TempoSequenceMilliseconds, 30);
            }
            else
            {
                Stream = CleanStream;
            }


            Length = (Bass.ChannelBytes2Seconds(Stream, Bass.ChannelGetLength(Stream)) * 1000);
            Bass.ChannelGetAttribute(Stream, ChannelAttribute.Frequency, out InitialFrequency);

            SetDirection(false);
        }
Exemplo n.º 3
0
        public AudioTrackBass(Stream data, bool quick = false)
        {
            Preview = quick;

            BassFlags flags = Preview ? 0 : (BassFlags.Decode | BassFlags.Prescan);

            if (data == null)
                throw new ArgumentNullException(@"Data couldn't be loaded!");
            //encapsulate incoming stream with async buffer if it isn't already.
            dataStream = data as AsyncBufferStream ?? new AsyncBufferStream(data, quick ? 8 : -1);

            procs = new DataStreamFileProcedures(dataStream);

            audioStreamPrefilter = Bass.CreateStream(StreamSystem.NoBuffer, flags, procs.BassProcedures, IntPtr.Zero);

            if (Preview)
                activeStream = audioStreamPrefilter;
            else
            {
                activeStream = BassFx.TempoCreate(audioStreamPrefilter, BassFlags.Decode);
                activeStream = BassFx.ReverseCreate(activeStream, 5f, BassFlags.Default);

                Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoUseQuickAlgorithm, 1);
                Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoOverlapMilliseconds, 4);
                Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoSequenceMilliseconds, 30);
            }

            Length = (Bass.ChannelBytes2Seconds(activeStream, Bass.ChannelGetLength(activeStream)) * 1000);
            Bass.ChannelGetAttribute(activeStream, ChannelAttribute.Frequency, out initialFrequency);
        }
Exemplo n.º 4
0
        public TempoDecoder(Decoder decoder, Resolution BufferKind = Resolution.Short)
            : base(BufferKind)
        {
            this.decoder = decoder;

            Handle = BassFx.TempoCreate(decoder.Handle, BassFlags.Float | BufferKind.ToBassFlag());
        }
Exemplo n.º 5
0
        /// <summary>
        /// Constructs a new <see cref="TrackBass"/> from provided audio data.
        /// </summary>
        /// <param name="data">The sample data stream.</param>
        /// <param name="quick">If true, the track will not be fully loaded, and should only be used for preview purposes.  Defaults to false.</param>
        public TrackBass(Stream data, bool quick = false)
        {
            EnqueueAction(() =>
            {
                Preview = quick;

                if (data == null)
                {
                    throw new ArgumentNullException(nameof(data));
                }
                //encapsulate incoming stream with async buffer if it isn't already.
                dataStream = data as AsyncBufferStream ?? new AsyncBufferStream(data, quick ? 8 : -1);

                procedures = CreateDataStreamFileProcedures(dataStream);

                if (!RuntimeInfo.SupportsIL)
                {
                    pinnedProcedures = GCHandle.Alloc(procedures, GCHandleType.Pinned);
                }

                BassFlags flags = Preview ? 0 : BassFlags.Decode | BassFlags.Prescan | BassFlags.Float;
                activeStream    = Bass.CreateStream(StreamSystem.NoBuffer, flags, procedures.BassProcedures, RuntimeInfo.SupportsIL ? IntPtr.Zero : GCHandle.ToIntPtr(pinnedProcedures));

                if (!Preview)
                {
                    // We assign the BassFlags.Decode streams to the device "bass_nodevice" to prevent them from getting
                    // cleaned up during a Bass.Free call. This is necessary for seamless switching between audio devices.
                    // Further, we provide the flag BassFlags.FxFreeSource such that freeing the activeStream also frees
                    // all parent decoding streams.
                    const int bass_nodevice = 0x20000;

                    Bass.ChannelSetDevice(activeStream, bass_nodevice);
                    tempoAdjustStream = BassFx.TempoCreate(activeStream, BassFlags.Decode | BassFlags.FxFreeSource);
                    Bass.ChannelSetDevice(activeStream, bass_nodevice);
                    activeStream = BassFx.ReverseCreate(tempoAdjustStream, 5f, BassFlags.Default | BassFlags.FxFreeSource);

                    Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoUseQuickAlgorithm, 1);
                    Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoOverlapMilliseconds, 4);
                    Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoSequenceMilliseconds, 30);
                }

                // will be -1 in case of an error
                double seconds = Bass.ChannelBytes2Seconds(activeStream, Bass.ChannelGetLength(activeStream));

                bool success = seconds >= 0;

                if (success)
                {
                    Length = seconds * 1000;

                    Bass.ChannelGetAttribute(activeStream, ChannelAttribute.Frequency, out float frequency);
                    initialFrequency = frequency;
                    bitrate          = (int)Bass.ChannelGetAttribute(activeStream, ChannelAttribute.Bitrate);

                    isLoaded = true;
                }
            });

            InvalidateState();
        }
Exemplo n.º 6
0
        private void LoadFile(string filename)
        {
            if (filename != string.Empty)
            {
                if (Bass.ChannelIsActive(fxStream) == PlaybackState.Playing)
                {
                    StopPlayback();
                }

                Bass.StreamFree(audioStream);
                Bass.StreamFree(fxStream);

                audioStream = Bass.CreateStream(filename, 0, 0, BassFlags.Decode | BassFlags.Prescan);
                audioLength = Bass.ChannelGetLength(audioStream);
                if (audioStream != 0 && Bass.LastError == Errors.OK)
                {
                    fxStream = BassFx.TempoCreate(audioStream, BassFlags.FxFreeSource);
                    if (fxStream != 0 && Bass.LastError == Errors.OK)
                    {
                        bassboost = new PeakEQ(fxStream, 0, 3);
                        bassboost.UpdateBand(bassboost.AddBand(30), bassAmount.Value * 2);
                        bassboost.UpdateBand(bassboost.AddBand(50), bassAmount.Value);

                        playButton.IsEnabled = true;
                        saveButton.IsEnabled = true;

                        return;
                    }
                }

                MessageBox.Show(this, "Чот нихуя");
            }
        }
Exemplo n.º 7
0
        private int prepareStream(Stream data, bool quick)
        {
            switch (data)
            {
            case MemoryStream _:
            case UnmanagedMemoryStream _:
            case AsyncBufferStream _:
                // Buffering memory stream is definitely unworthy.
                dataStream = data;
                break;

            default:
                // It would be most likely a FileStream.
                // Consider to use RandomAccess to optimise in favor of FileStream in .NET 6
                dataStream = new AsyncBufferStream(data, quick ? 8 : -1);
                break;
            }

            fileCallbacks = new FileCallbacks(new DataStreamFileProcedures(dataStream));

            BassFlags flags = (Preview ? 0 : BassFlags.Decode | BassFlags.Prescan);

            // While this shouldn't cause issues, we've had a small subset of users reporting issues on windows.
            // To keep things working let's only apply to other platforms until we know more.
            // See https://github.com/ppy/osu/issues/18652.
            if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows)
            {
                flags |= BassFlags.AsyncFile;
            }

            int stream = Bass.CreateStream(StreamSystem.NoBuffer, flags, fileCallbacks.Callbacks, fileCallbacks.Handle);

            bitrate = (int)Math.Round(Bass.ChannelGetAttribute(stream, ChannelAttribute.Bitrate));

            if (!Preview)
            {
                // We assign the BassFlags.Decode streams to the device "bass_nodevice" to prevent them from getting
                // cleaned up during a Bass.Free call. This is necessary for seamless switching between audio devices.
                // Further, we provide the flag BassFlags.FxFreeSource such that freeing the stream also frees
                // all parent decoding streams.
                const int bass_nodevice = 0x20000;

                Bass.ChannelSetDevice(stream, bass_nodevice);
                tempoAdjustStream = BassFx.TempoCreate(stream, BassFlags.Decode | BassFlags.FxFreeSource);
                Bass.ChannelSetDevice(tempoAdjustStream, bass_nodevice);
                stream = BassFx.ReverseCreate(tempoAdjustStream, 5f, BassFlags.Default | BassFlags.FxFreeSource | BassFlags.Decode);

                Bass.ChannelSetAttribute(stream, ChannelAttribute.TempoUseQuickAlgorithm, 1);
                Bass.ChannelSetAttribute(stream, ChannelAttribute.TempoOverlapMilliseconds, 4);
                Bass.ChannelSetAttribute(stream, ChannelAttribute.TempoSequenceMilliseconds, 30);
            }

            return(stream);
        }
Exemplo n.º 8
0
        public void OpenFile(string filePath)
        {
            Unload();

            streamHandle = Bass.CreateStream(filePath, Flags: BassFlags.Decode | BassFlags.Prescan);
            activeHandle = BassFx.TempoCreate(streamHandle, BassFlags.FxFreeSource);

            syncHandle = Bass.ChannelSetSync(activeHandle, SyncFlags.End, 0, SyncProcedureEndStream);
            Bass.ChannelSetAttribute(activeHandle, ChannelAttribute.TempoUseQuickAlgorithm, 1);
            ApplyEffects();

            status = PlayerStatus.FileLoaded;
        }
Exemplo n.º 9
0
 public override void Connect(IBassStreamComponent previous)
 {
     this.Rate          = previous.Rate;
     this.Channels      = previous.Channels;
     this.ChannelHandle = BassFx.TempoCreate(previous.ChannelHandle, previous.Flags);
     if (this.ChannelHandle == 0)
     {
         BassUtils.Throw();
     }
     if (this.IsActive)
     {
         this.Update();
     }
 }
Exemplo n.º 10
0
        public TrackBass(Stream data, bool quick = false)
        {
            PendingActions.Enqueue(() =>
            {
                Preview = quick;

                if (data == null)
                {
                    throw new ArgumentNullException(nameof(data));
                }
                //encapsulate incoming stream with async buffer if it isn't already.
                dataStream = data as AsyncBufferStream ?? new AsyncBufferStream(data, quick ? 8 : -1);

                var procs = new DataStreamFileProcedures(dataStream);

                BassFlags flags = Preview ? 0 : BassFlags.Decode | BassFlags.Prescan;
                activeStream    = Bass.CreateStream(StreamSystem.NoBuffer, flags, procs.BassProcedures, IntPtr.Zero);

                if (!Preview)
                {
                    // We assign the BassFlags.Decode streams to the device "bass_nodevice" to prevent them from getting
                    // cleaned up during a Bass.Free call. This is necessary for seamless switching between audio devices.
                    // Further, we provide the flag BassFlags.FxFreeSource such that freeing the activeStream also frees
                    // all parent decoding streams.
                    const int bass_nodevice = 0x20000;

                    Bass.ChannelSetDevice(activeStream, bass_nodevice);
                    tempoAdjustStream = BassFx.TempoCreate(activeStream, BassFlags.Decode | BassFlags.FxFreeSource);
                    Bass.ChannelSetDevice(activeStream, bass_nodevice);
                    activeStream = BassFx.ReverseCreate(tempoAdjustStream, 5f, BassFlags.Default | BassFlags.FxFreeSource);

                    Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoUseQuickAlgorithm, 1);
                    Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoOverlapMilliseconds, 4);
                    Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoSequenceMilliseconds, 30);
                }

                Length = Bass.ChannelBytes2Seconds(activeStream, Bass.ChannelGetLength(activeStream)) * 1000;

                float frequency;
                Bass.ChannelGetAttribute(activeStream, ChannelAttribute.Frequency, out frequency);
                initialFrequency = frequency;
                bitrate          = (int)Bass.ChannelGetAttribute(activeStream, ChannelAttribute.Bitrate);

                isLoaded = true;
                OnLoaded?.Invoke(this);
            });

            InvalidateState();
        }
Exemplo n.º 11
0
        internal AudioStream(AudioManager manager, string path, ResourceContainer resourceContainer) : base(manager)
        {
            this.path = path;

            decodeStream = Bass.CreateStream(path, 0, 0, BassFlags.Decode | BassFlags.Prescan);
            if (decodeStream == 0)
            {
                Trace.WriteLine($"Failed to load audio stream ({path}): {Bass.LastError}");
                return;
            }

            stream = BassFx.TempoCreate(decodeStream, BassFlags.Default);
            Bass.ChannelSetAttribute(stream, ChannelAttribute.TempoUseQuickAlgorithm, 1);
            Bass.ChannelSetAttribute(stream, ChannelAttribute.TempoOverlapMilliseconds, 4);
            Bass.ChannelSetAttribute(stream, ChannelAttribute.TempoSequenceMilliseconds, 30);

            Channel = stream;
        }
Exemplo n.º 12
0
        /// <summary>
        /// Loads the File Channel with FX.
        /// </summary>
        protected override int OnLoad(string FileName)
        {
            var h = Bass.CreateStream(FileName, Flags: BassFlags.Decode);

            if (h == 0)
            {
                return(0);
            }

            h = BassFx.TempoCreate(h, BassFlags.Decode | BassFlags.FxFreeSource);

            if (h == 0)
            {
                return(0);
            }

            _tempoHandle = h;

            return(BassFx.ReverseCreate(h, 2, BassFlags.FxFreeSource));
        }
Exemplo n.º 13
0
        /// <summary>
        /// Constructs a new <see cref="TrackBass"/> from provided audio data.
        /// </summary>
        /// <param name="data">The sample data stream.</param>
        /// <param name="quick">If true, the track will not be fully loaded, and should only be used for preview purposes.  Defaults to false.</param>
        public TrackBass(Stream data, bool quick = false)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            // todo: support this internally to match the underlying Track implementation (which can support this).
            const float tempo_minimum_supported = 0.05f;

            AggregateTempo.ValueChanged += t =>
            {
                if (t.NewValue < tempo_minimum_supported)
                {
                    throw new ArgumentException($"{nameof(TrackBass)} does not support {nameof(Tempo)} specifications below {tempo_minimum_supported}. Use {nameof(Frequency)} instead.");
                }
            };

            EnqueueAction(() =>
            {
                Preview = quick;

                //encapsulate incoming stream with async buffer if it isn't already.
                dataStream = data as AsyncBufferStream ?? new AsyncBufferStream(data, quick ? 8 : -1);

                fileCallbacks = new FileCallbacks(new DataStreamFileProcedures(dataStream));

                BassFlags flags = Preview ? 0 : BassFlags.Decode | BassFlags.Prescan;
                activeStream    = Bass.CreateStream(StreamSystem.NoBuffer, flags, fileCallbacks.Callbacks, fileCallbacks.Handle);

                if (!Preview)
                {
                    // We assign the BassFlags.Decode streams to the device "bass_nodevice" to prevent them from getting
                    // cleaned up during a Bass.Free call. This is necessary for seamless switching between audio devices.
                    // Further, we provide the flag BassFlags.FxFreeSource such that freeing the activeStream also frees
                    // all parent decoding streams.
                    const int bass_nodevice = 0x20000;

                    Bass.ChannelSetDevice(activeStream, bass_nodevice);
                    tempoAdjustStream = BassFx.TempoCreate(activeStream, BassFlags.Decode | BassFlags.FxFreeSource);
                    Bass.ChannelSetDevice(tempoAdjustStream, bass_nodevice);
                    activeStream = BassFx.ReverseCreate(tempoAdjustStream, 5f, BassFlags.Default | BassFlags.FxFreeSource);

                    Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoUseQuickAlgorithm, 1);
                    Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoOverlapMilliseconds, 4);
                    Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoSequenceMilliseconds, 30);
                }

                // will be -1 in case of an error
                double seconds = Bass.ChannelBytes2Seconds(activeStream, byteLength = Bass.ChannelGetLength(activeStream));

                bool success = seconds >= 0;

                if (success)
                {
                    Length = seconds * 1000;

                    // Bass does not allow seeking to the end of the track, so the last available position is 1 sample before.
                    lastSeekablePosition = Bass.ChannelBytes2Seconds(activeStream, byteLength - BYTES_PER_SAMPLE) * 1000;

                    Bass.ChannelGetAttribute(activeStream, ChannelAttribute.Frequency, out float frequency);
                    initialFrequency = frequency;
                    bitrate          = (int)Bass.ChannelGetAttribute(activeStream, ChannelAttribute.Bitrate);

                    stopCallback = new SyncCallback((a, b, c, d) => RaiseFailed());
                    endCallback  = new SyncCallback((a, b, c, d) =>
                    {
                        if (!Looping)
                        {
                            RaiseCompleted();
                        }
                    });

                    Bass.ChannelSetSync(activeStream, SyncFlags.Stop, 0, stopCallback.Callback, stopCallback.Handle);
                    Bass.ChannelSetSync(activeStream, SyncFlags.End, 0, endCallback.Callback, endCallback.Handle);

                    isLoaded = true;
                }
            });

            InvalidateState();
        }
Exemplo n.º 14
0
        void OpenFile()
        {
            if (!_ofd.ShowDialog().Value)
            {
                return;
            }

            // free decode bpm stream and resources
            BassFx.BPMFree(_bpmchan);

            // free tempo, stream, music & bpm/beat callbacks
            Bass.StreamFree(_chan);
            Bass.MusicFree(_chan);

            // create decode channel
            _chan = Bass.CreateStream(_ofd.FileName, 0, 0, BassFlags.Decode);

            if (_chan == 0)
            {
                _chan = Bass.MusicLoad(_ofd.FileName, 0, 0, BassFlags.MusicRamp | BassFlags.Prescan | BassFlags.Decode, 0);
            }

            if (_chan == 0)
            {
                // not a WAV/MP3 or MOD
                Status = "Click Here to Open and Play a File...";
                MessageBox.Show("Selected file couldn't be loaded!");
                return;
            }

            // get channel info
            Bass.ChannelGetInfo(_chan, out _info);

            // create a new stream - decoded & resampled :)
            if ((_chan = BassFx.TempoCreate(_chan, BassFlags.Loop | BassFlags.FxFreeSource)) == 0)
            {
                Status = "Click Here to Open and Play a File...";
                MessageBox.Show("Couldn't create a resampled stream!");
                Bass.StreamFree(_chan);
                Bass.MusicFree(_chan);
                return;
            }

            // update the button to show the loaded file name (without path)
            Status = Path.GetFileName(_ofd.FileName);

            SampleRate = _info.Frequency;
            OnPropertyChanged(nameof(MinSampleRate));
            OnPropertyChanged(nameof(MaxSampleRate));

            // update tempo view
            Tempo = 0;

            // set the callback bpm and beat
            IsBpmPeriod = _isBpmPeriod;

            IsBeatPosition = _isBeatPosition;

            // play new created stream
            Bass.ChannelPlay(_chan);

            // create bpmChan stream and get bpm value for BpmPeriod seconds from current position
            var pos    = Bass.ChannelBytes2Seconds(_chan, Bass.ChannelGetPosition(_chan));
            var maxpos = Bass.ChannelBytes2Seconds(_chan, Bass.ChannelGetLength(_chan));

            DecodingBPM(true, pos, pos + BpmPeriod >= maxpos ? maxpos - 1 : pos + BpmPeriod, _ofd.FileName);
        }
Exemplo n.º 15
0
        /// <summary>
        /// Constructs a new <see cref="TrackBass"/> from provided audio data.
        /// </summary>
        /// <param name="data">The sample data stream.</param>
        /// <param name="quick">If true, the track will not be fully loaded, and should only be used for preview purposes.  Defaults to false.</param>
        public TrackBass(Stream data, bool quick = false)
        {
            EnqueueAction(() =>
            {
                Preview = quick;

                if (data == null)
                {
                    throw new ArgumentNullException(nameof(data));
                }
                //encapsulate incoming stream with async buffer if it isn't already.
                dataStream = data as AsyncBufferStream ?? new AsyncBufferStream(data, quick ? 8 : -1);

                fileCallbacks = new FileCallbacks(new DataStreamFileProcedures(dataStream));

                BassFlags flags = Preview ? 0 : BassFlags.Decode | BassFlags.Prescan;
                activeStream    = Bass.CreateStream(StreamSystem.NoBuffer, flags, fileCallbacks.Callbacks, fileCallbacks.Handle);

                if (!Preview)
                {
                    // We assign the BassFlags.Decode streams to the device "bass_nodevice" to prevent them from getting
                    // cleaned up during a Bass.Free call. This is necessary for seamless switching between audio devices.
                    // Further, we provide the flag BassFlags.FxFreeSource such that freeing the activeStream also frees
                    // all parent decoding streams.
                    const int bass_nodevice = 0x20000;

                    Bass.ChannelSetDevice(activeStream, bass_nodevice);
                    tempoAdjustStream = BassFx.TempoCreate(activeStream, BassFlags.Decode | BassFlags.FxFreeSource);
                    Bass.ChannelSetDevice(activeStream, bass_nodevice);
                    activeStream = BassFx.ReverseCreate(tempoAdjustStream, 5f, BassFlags.Default | BassFlags.FxFreeSource);

                    Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoUseQuickAlgorithm, 1);
                    Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoOverlapMilliseconds, 4);
                    Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoSequenceMilliseconds, 30);
                }

                // will be -1 in case of an error
                double seconds = Bass.ChannelBytes2Seconds(activeStream, Bass.ChannelGetLength(activeStream));

                bool success = seconds >= 0;

                if (success)
                {
                    Length = seconds * 1000;

                    Bass.ChannelGetAttribute(activeStream, ChannelAttribute.Frequency, out float frequency);
                    initialFrequency = frequency;
                    bitrate          = (int)Bass.ChannelGetAttribute(activeStream, ChannelAttribute.Bitrate);

                    stopCallback = new SyncCallback((a, b, c, d) => RaiseFailed());
                    endCallback  = new SyncCallback((a, b, c, d) =>
                    {
                        if (!Looping)
                        {
                            RaiseCompleted();
                        }
                    });

                    Bass.ChannelSetSync(activeStream, SyncFlags.Stop, 0, stopCallback.Callback, stopCallback.Handle);
                    Bass.ChannelSetSync(activeStream, SyncFlags.End, 0, endCallback.Callback, endCallback.Handle);

                    isLoaded = true;
                }
            });

            InvalidateState();
        }