/// <summary>
        /// Initializes a new instance of the BASSSoundEffect class.
        /// </summary>
        /// <param name="uv">The Ultraviolet context.</param>
        /// <param name="filename">The filename of the sample to load.</param>
        public BASSSoundEffect(UltravioletContext uv, String filename)
            : base(uv)
        {
            var fileSystemService = FileSystemService.Create();
            var fileData          = default(Byte[]);

            using (var stream = fileSystemService.OpenRead(filename))
            {
                fileData = new Byte[stream.Length];
                stream.Read(fileData, 0, fileData.Length);
            }

            sample = BASSNative.SampleLoad(fileData, 0, (UInt32)fileData.Length, UInt16.MaxValue, 0);
            if (!BASSUtil.IsValidHandle(sample))
            {
                throw new BASSException();
            }

            if (!BASSNative.SampleGetInfo(sample, out this.sampleInfo))
            {
                throw new BASSException();
            }

            this.data = Marshal.AllocHGlobal((int)sampleInfo.length);
            if (!BASSNative.SampleGetData(sample, this.data))
            {
                throw new BASSException();
            }
        }
Beispiel #2
0
            /// <summary>
            /// Creates a BASS stream that represents the song.
            /// </summary>
            /// <param name="flags">The flags to apply to the stream that is created.</param>
            /// <returns>The handle to the BASS stream that was created.</returns>
            public UInt32 CreateInstance(UInt32 flags)
            {
                var fileSystemService = FileSystemService.Create();

                var instance   = fileSystemService.OpenRead(file);
                var instanceID = this.nextInstanceID++;

                instances.Add(instanceID, instance);

                var stream = 0u;

                try
                {
                    var procs = new BASS_FILEPROCS(fnClose, fnLength, fnRead, fnSeek);

                    unsafe
                    {
                        stream = BASSNative.StreamCreateFileUser(1, BASSNative.BASS_STREAM_DECODE, &procs, new IntPtr((int)instanceID));
                        if (!BASSUtil.IsValidHandle(stream))
                        {
                            throw new BASSException();
                        }
                    }
                }
                catch
                {
                    instance.Dispose();
                    instances.Remove(instanceID);
                    throw;
                }

                return(stream);
            }
        /// <inheritdoc/>
        public override void SlideVolume(Single volume, TimeSpan time)
        {
            Contract.EnsureNotDisposed(this, Disposed);

            EnsureChannelIsValid();

            BASSUtil.SlideVolume(stream, volume, time);
        }
        /// <inheritdoc/>
        public override void SlidePan(Single pan, TimeSpan time)
        {
            Contract.EnsureNotDisposed(this, Disposed);

            EnsureChannelIsValid();

            BASSUtil.SlidePan(stream, pan, time);
        }
Beispiel #5
0
        /// <inheritdoc/>
        public override void SlidePitch(Single pitch, TimeSpan time)
        {
            Contract.EnsureNotDisposed(this, Disposed);

            EnsureChannelIsValid();

            PromoteToStream(0f);
            BASSUtil.SlidePitch(channel, pitch, time);
        }
        /// <summary>
        /// Plays the specified song.
        /// </summary>
        private Boolean PlayInternal(Song song, Single volume, Single pitch, Single pan, TimeSpan?loopStart, TimeSpan?loopLength)
        {
            Ultraviolet.ValidateResource(song);

            Stop();

            stream = ((BASSSong)song).CreateStream(BASSNative.BASS_STREAM_DECODE);
            stream = BASSFXNative.TempoCreate(stream, BASSNative.BASS_FX_FREESOURCE | BASSNative.BASS_STREAM_AUTOFREE);
            if (!BASSUtil.IsValidHandle(stream))
            {
                throw new BASSException();
            }

            var autoloop = loopStart.HasValue && !loopLength.HasValue;
            var syncloop = loopStart.HasValue && !autoloop;

            BASSUtil.SetIsLooping(stream, autoloop);
            BASSUtil.SetVolume(stream, MathUtil.Clamp(volume, 0f, 1f));
            BASSUtil.SetPitch(stream, MathUtil.Clamp(pitch, -1f, 1f));
            BASSUtil.SetPan(stream, MathUtil.Clamp(pan, -1f, 1f));

            if (loopStart > TimeSpan.Zero && loopLength <= TimeSpan.Zero)
            {
                throw new ArgumentException(nameof(loopLength));
            }

            if (syncloop)
            {
                var loopStartInBytes = BASSNative.ChannelSeconds2Bytes(stream, loopStart.Value.TotalSeconds);
                var loopEndInBytes   = BASSNative.ChannelSeconds2Bytes(stream, (loopStart + loopLength).Value.TotalSeconds);
                syncLoopDelegate = SyncLoop;
                syncLoop         = BASSNative.ChannelSetSync(stream, BASSSync.SYNC_POS, loopEndInBytes, syncLoopDelegate, new IntPtr((Int32)loopStartInBytes));
                if (syncLoop == 0)
                {
                    throw new BASSException();
                }
            }

            syncEndDelegate = SyncEnd;
            syncEnd         = BASSNative.ChannelSetSync(stream, BASSSync.SYNC_END, 0, syncEndDelegate, IntPtr.Zero);
            if (syncEnd == 0)
            {
                throw new BASSException();
            }

            if (!BASSNative.ChannelPlay(stream, true))
            {
                throw new BASSException();
            }

            OnStateChanged();
            OnSongStarted();

            return(true);
        }
Beispiel #7
0
        /// <summary>
        /// Gets a value indicating whether the specified channel is looping.
        /// </summary>
        /// <param name="handle">The handle of the channel to evaluate.</param>
        /// <returns>true if the channel is looping; otherwise, false.</returns>
        public static Boolean GetIsLooping(UInt32 handle)
        {
            var flags = BASSNative.ChannelFlags(handle, 0, 0);

            if (!BASSUtil.IsValidValue(flags))
            {
                throw new BASSException();
            }

            return((flags & BASSNative.BASS_SAMPLE_LOOP) == BASSNative.BASS_SAMPLE_LOOP);
        }
Beispiel #8
0
        /// <summary>
        /// Sets a value indicating whether the specified channel is looping.
        /// </summary>
        /// <param name="handle">The handle of the channel to modify.</param>
        /// <param name="looping">A value indicating whether the channel is looping.</param>
        public static void SetIsLooping(UInt32 handle, Boolean looping)
        {
            var flags = looping ?
                        BASSNative.ChannelFlags(handle, BASSNative.BASS_SAMPLE_LOOP, BASSNative.BASS_SAMPLE_LOOP) :
                        BASSNative.ChannelFlags(handle, 0, BASSNative.BASS_SAMPLE_LOOP);

            if (!BASSUtil.IsValidValue(flags))
            {
                throw new BASSException();
            }
        }
Beispiel #9
0
        /// <summary>
        /// Sets the position of the specified channel.
        /// </summary>
        /// <param name="handle">The handle of the channel to modify.</param>
        /// <param name="position">The position in seconds to which to set the channel.</param>
        public static void SetPositionInSeconds(UInt32 handle, Double position)
        {
            var positionInBytes = BASSNative.ChannelSeconds2Bytes(handle, position);

            if (!BASSUtil.IsValidValue(positionInBytes))
            {
                throw new BASSException();
            }

            if (!BASSNative.ChannelSetPosition(handle, positionInBytes, 0))
            {
                throw new BASSException();
            }
        }
        /// <inheritdoc/>
        public override void Play()
        {
            Contract.EnsureNotDisposed(this, Disposed);

            var channel = BASSNative.SampleGetChannel(sample, false);

            if (!BASSUtil.IsValidHandle(channel))
            {
                throw new BASSException();
            }

            if (!BASSNative.ChannelPlay(channel, true))
            {
                throw new BASSException();
            }
        }
Beispiel #11
0
        /// <summary>
        /// Plays a sound effect.
        /// </summary>
        private Boolean PlayInternal(SoundEffect soundEffect, Single volume, Single pitch, Single pan, Boolean loop = false)
        {
            Contract.EnsureNotDisposed(this, Disposed);

            // Stop any sound that's already playing.
            Stop();

            // Retrieve the sample data from the sound effect.
            Ultraviolet.ValidateResource(soundEffect);
            var bassfx = (BASSSoundEffect)soundEffect;
            var sample = bassfx.GetSampleData(out this.sampleData, out this.sampleInfo);

            // Get a channel on which to play the sample.
            channel = BASSNative.SampleGetChannel(sample, true);
            if (!BASSUtil.IsValidHandle(channel))
            {
                var error = BASSNative.ErrorGetCode();
                if (error == BASSNative.BASS_ERROR_NOCHAN)
                {
                    return(false);
                }

                throw new BASSException(error);
            }

            // Set the channel's attributes.
            if (pitch == 0)
            {
                BASSUtil.SetIsLooping(channel, loop);
                BASSUtil.SetVolume(channel, MathUtil.Clamp(volume, 0f, 1f));
                BASSUtil.SetPan(channel, MathUtil.Clamp(pan, -1f, 1f));
            }
            else
            {
                PromoteToStream(volume, MathUtil.Clamp(pitch, -1f, 1f), pan, loop);
            }

            // Play the channel.
            if (!BASSNative.ChannelPlay(channel, true))
            {
                throw new BASSException();
            }

            this.playing = soundEffect;

            return(true);
        }
        /// <inheritdoc/>
        public override void Play(Single volume, Single pitch, Single pan)
        {
            Contract.EnsureNotDisposed(this, Disposed);

            var channel = 0u;

            if (pitch == 0)
            {
                channel = BASSNative.SampleGetChannel(sample, false);
                if (!BASSUtil.IsValidHandle(channel))
                {
                    throw new BASSException();
                }
            }
            else
            {
                var stream = BASSNative.StreamCreate(sampleInfo.freq, sampleInfo.chans, sampleInfo.flags | BASSNative.BASS_STREAM_DECODE, BASSNative.STREAMPROC_PUSH, IntPtr.Zero);
                if (!BASSUtil.IsValidHandle(stream))
                {
                    throw new BASSException();
                }

                var pushed = BASSNative.StreamPutData(stream, data, sampleInfo.length);
                if (!BASSUtil.IsValidValue(pushed))
                {
                    throw new BASSException();
                }

                stream = BASSFXNative.TempoCreate(stream, BASSNative.BASS_FX_FREESOURCE | BASSNative.BASS_STREAM_AUTOFREE);
                if (!BASSUtil.IsValidHandle(stream))
                {
                    throw new BASSException();
                }

                channel = stream;

                BASSUtil.SetPitch(channel, MathUtil.Clamp(pitch, -1f, 1f));
            }

            BASSUtil.SetVolume(channel, MathUtil.Clamp(volume, 0f, 1f));
            BASSUtil.SetPan(channel, MathUtil.Clamp(pan, -1f, 1f));

            if (!BASSNative.ChannelPlay(channel, false))
            {
                throw new BASSException();
            }
        }
Beispiel #13
0
        /// <summary>
        /// Gets the duration of the specified channel in seconds.
        /// </summary>
        /// <param name="handle">The handle of the channel to evaluate.</param>
        /// <returns>The duration of the specified channel in seconds.</returns>
        public static Double GetDurationInSeconds(UInt32 handle)
        {
            var length = BASSNative.ChannelGetLength(handle, 0);

            if (!BASSUtil.IsValidValue(length))
            {
                throw new BASSException();
            }

            var seconds = BASSNative.ChannelBytes2Seconds(handle, length);

            if (seconds < 0)
            {
                throw new BASSException();
            }

            return(seconds);
        }
Beispiel #14
0
        /// <summary>
        /// Gets the position of the specified channel.
        /// </summary>
        /// <param name="handle">The handle of the channel to evaluate.</param>
        /// <returns>The current position of the channel in seconds.</returns>
        public static Double GetPositionInSeconds(UInt32 handle)
        {
            var position = BASSNative.ChannelGetPosition(handle, 0);

            if (!BASSUtil.IsValidValue(position))
            {
                throw new BASSException();
            }

            var seconds = BASSNative.ChannelBytes2Seconds(handle, position);

            if (seconds < 0)
            {
                throw new BASSException();
            }

            return(seconds);
        }
Beispiel #15
0
        /// <summary>
        /// Initializes a new instance of the BASSSong class.
        /// </summary>
        /// <param name="uv">The Ultraviolet context.</param>
        /// <param name="file">The path to the file from which to stream the song.</param>
        public BASSSong(UltravioletContext uv, String file)
            : base(uv)
        {
            Contract.RequireNotEmpty(file, nameof(file));

            this.file = file;

            var stream = CreateStream(BASSNative.BASS_STREAM_DECODE);

            tags = new SongTagCollection();
            ReadTagsFromStream(stream);

            var duration = BASSUtil.GetDurationInSeconds(stream);

            if (!BASSNative.StreamFree(stream))
            {
                throw new BASSException();
            }

            this.duration = TimeSpan.FromSeconds(duration);
        }
Beispiel #16
0
        /// <summary>
        /// Creates a BASS stream that represents the song.
        /// </summary>
        /// <param name="flags">The flags to apply to the stream that is created.</param>
        /// <returns>The handle to the BASS stream that was created.</returns>
        internal UInt32 CreateStream(UInt32 flags)
        {
            if (FileSystemService.Source == null)
            {
                var stream = BASSNative.StreamCreateFile(file, flags);
                if (!BASSUtil.IsValidHandle(stream))
                {
                    throw new BASSException();
                }

                return(stream);
            }
            else
            {
                if (instanceManager == null)
                {
                    instanceManager = new BASSSongInstanceManager(file);
                }
                return(instanceManager.CreateInstance(flags));
            }
        }
Beispiel #17
0
        /// <summary>
        /// Promotes the current channel to a stream.
        /// This is necessary if the pitch is shifted, because BASS_FX only works on streams.
        /// </summary>
        /// <param name="volume">The stream's initial volume.</param>
        /// <param name="pitch">The stream's initial pitch.</param>
        /// <param name="pan">The stream's initial pan.</param>
        /// <param name="loop">A value indicating whether to loop the stream.</param>
        private Boolean PromoteToStream(Single volume, Single pitch, Single pan, Boolean loop)
        {
            if (BASSUtil.IsValidHandle(stream))
            {
                return(false);
            }

            // If the channel is currently playing, pause it.
            var playing = (State == PlaybackState.Playing);

            if (playing)
            {
                if (!BASSNative.ChannelPause(channel))
                {
                    throw new BASSException();
                }
            }

            // Get the current position of the playing channel so that we can advance the stream to match.
            var streampos = (uint)BASSNative.ChannelGetPosition(channel, 0);

            if (!BASSUtil.IsValidValue(streampos))
            {
                throw new BASSException();
            }

            // Create a process for streaming data from our sample into the new stream.
            sampleDataPosition   = (int)streampos;
            sampleDataLength     = (int)sampleInfo.length;
            sampleDataStreamProc = new StreamProc((handle, buffer, length, user) =>
            {
                if (sampleDataPosition >= sampleDataLength)
                {
                    sampleDataPosition = 0;
                }

                var byteCount = Math.Min(length, (uint)sampleDataLength - streampos);
                unsafe
                {
                    byte *pBufferSrc = (byte *)(sampleData + sampleDataPosition).ToPointer();
                    byte *pBufferDst = (byte *)(buffer).ToPointer();
                    for (int i = 0; i < byteCount; i++)
                    {
                        *pBufferDst++ = *pBufferSrc++;
                    }
                }
                sampleDataPosition += (int)byteCount;

                return(streampos >= sampleDataLength ?
                       byteCount | BASSNative.BASS_STREAMPROC_END :
                       byteCount);
            });

            // Create a decoding stream based on our sample channel.
            stream = BASSNative.StreamCreate(sampleInfo.freq, sampleInfo.chans, sampleInfo.flags | BASSNative.BASS_STREAM_DECODE, sampleDataStreamProc, IntPtr.Zero);
            if (!BASSUtil.IsValidHandle(stream))
            {
                throw new BASSException();
            }

            // Create an FX stream to shift the sound effect's pitch.
            stream = BASSFXNative.TempoCreate(stream, BASSNative.BASS_FX_FREESOURCE);
            if (!BASSUtil.IsValidHandle(stream))
            {
                throw new BASSException();
            }

            // Set the new stream's attributes.
            BASSUtil.SetVolume(stream, volume);
            BASSUtil.SetPitch(stream, pitch);
            BASSUtil.SetPan(stream, pan);
            BASSUtil.SetIsLooping(stream, loop);

            // Stop the old channel and switch to the stream.
            if (!BASSNative.ChannelStop(channel))
            {
                throw new BASSException();
            }
            channel = stream;

            // If we were previously playing, play the stream.
            if (playing)
            {
                if (!BASSNative.ChannelPlay(channel, false))
                {
                    throw new BASSException();
                }
            }

            promoted = true;
            return(true);
        }