/// <summary> /// Releases any BASS resources being held by the player. /// </summary> private void Release() { if (stream != 0) { if (!BASSNative.StreamFree(stream)) { throw new BASSException(); } stream = 0; } if (sample != 0) { if (!BASSNative.SampleFree(sample)) { throw new BASSException(); } sample = 0; } channel = 0; promoted = false; playing = null; }
/// <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); }
/// <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(); } }
/// <summary> /// Slides the pan of the specified channel. /// </summary> /// <param name="handle">The handle that represents the channel to slide.</param> /// <param name="pan">The channel's new pan.</param> /// <param name="time">The time over which to perform the slide.</param> public static void SlidePan(UInt32 handle, Single pan, TimeSpan time) { if (!BASSNative.ChannelSlideAttribute(handle, BASSAttrib.ATTRIB_PAN, pan, (uint)time.TotalMilliseconds)) { throw new BASSException(); } }
/// <summary> /// Sets the pitch of the specified channel. /// </summary> /// <param name="handle">The handle that represents the channel to adjust.</param> /// <param name="pitch">The channel's new pitch.</param> public static void SetPitch(UInt32 handle, Single pitch) { if (BASSNative.ChannelIsSliding(handle, BASSAttrib.ATTRIB_TEMPO_PITCH)) { if (!BASSNative.ChannelSlideAttribute(handle, BASSAttrib.ATTRIB_TEMPO_PITCH, pitch * SemitonesPerOctave, 0)) { throw new BASSException(); } } else { if (!BASSNative.ChannelSetAttribute(handle, BASSAttrib.ATTRIB_TEMPO_PITCH, pitch * SemitonesPerOctave)) { throw new BASSException(); } } var tempo = (Math.Pow(2.0, pitch) - 1.0) * 100.0; if (BASSNative.ChannelIsSliding(handle, BASSAttrib.ATTRIB_TEMPO)) { if (!BASSNative.ChannelSlideAttribute(handle, BASSAttrib.ATTRIB_TEMPO, (float)tempo, 0)) { throw new BASSException(); } } else { if (!BASSNative.ChannelSetAttribute(handle, BASSAttrib.ATTRIB_TEMPO, (float)tempo)) { throw new BASSException(); } } }
/// <summary> /// Performs custom looping when a loop range is specified. /// </summary> private void SyncLoop(UInt32 handle, UInt32 channel, UInt32 data, IntPtr user) { if (!BASSNative.ChannelSetPosition(channel, (UInt32)user, 0)) { throw new BASSException(); } }
/// <summary> /// Updates the global volume of streams to match the subsystem's current settings. /// </summary> private void UpdateStreamVolume() { var volumeStream = (audioMuted || songsMuted) ? 0 : (uint)(10000 * audioMasterVolume * songsMasterVolume); if (!BASSNative.SetConfig(BASSConfig.CONFIG_GVOL_STREAM, volumeStream)) { throw new BASSException(); } }
/// <summary> /// Updates the global volume of samples to match the subsystem's current settings. /// </summary> private void UpdateSampleVolume() { var volumeSample = (audioMuted || soundEffectsMuted) ? 0 : (uint)(10000 * audioMasterVolume * soundEffectsMasterVolume); if (!BASSNative.SetConfig(BASSConfig.CONFIG_GVOL_SAMPLE, volumeSample)) { throw new BASSException(); } }
/// <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); }
/// <summary> /// Suspends all audio output. /// </summary> public void Suspend() { Contract.EnsureNotDisposed(this, Disposed); if (!BASSNative.Pause()) { throw new BASSException(); } suspended = true; }
/// <summary> /// Resumes audio output after a call to <see cref="Suspend"/>. /// </summary> public void Resume() { Contract.EnsureNotDisposed(this, Disposed); if (!BASSNative.Start()) { throw new BASSException(); } suspended = false; }
/// <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(); } }
/// <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); }
/// <summary> /// Gets the pan of the specified channel. /// </summary> /// <param name="handle">The handle that represents the channel to evaluate.</param> /// <returns>The channel's pan.</returns> public static Single GetPan(UInt32 handle) { unsafe { Single value; if (!BASSNative.ChannelGetAttribute(handle, BASSAttrib.ATTRIB_PAN, &value)) { throw new BASSException(); } return(value); } }
/// <summary> /// Gets the pitch of the specified channel. /// </summary> /// <param name="handle">The handle that represents the channel to evaluate.</param> /// <returns>The channel's pitch.</returns> public static Single GetPitch(UInt32 handle) { unsafe { Single value; if (!BASSNative.ChannelGetAttribute(handle, BASSAttrib.ATTRIB_TEMPO_PITCH, &value)) { throw new BASSException(); } return(value / SemitonesPerOctave); } }
/// <summary> /// Reads any supported tags which are contained by the specified stream. /// </summary> private void ReadTagsFromStream(UInt32 stream) { var tagsOgg = default(IDictionary <String, String>); if (BASSNative.ChannelGetTags_Ogg(stream, out tagsOgg)) { foreach (var tagOgg in tagsOgg) { tags.Add(tagOgg.Key, tagOgg.Value); } } }
/// <inheritdoc/> public override void Resume() { Contract.EnsureNotDisposed(this, Disposed); if (State == PlaybackState.Paused) { if (!BASSNative.ChannelPlay(stream, false)) { throw new BASSException(); } OnStateChanged(); } }
/// <inheritdoc/> public override void Stop() { Contract.EnsureNotDisposed(this, Disposed); if (State != PlaybackState.Stopped) { if (!BASSNative.ChannelStop(channel)) { throw new BASSException(); } } Release(); }
/// <inheritdoc/> public override void Pause() { Contract.EnsureNotDisposed(this, Disposed); EnsureChannelIsValid(); if (State == PlaybackState.Playing) { if (!BASSNative.ChannelPause(channel)) { throw new BASSException(); } } }
/// <summary> /// Slides the pitch of the specified channel. /// </summary> /// <param name="handle">The handle that represents the channel to slide.</param> /// <param name="pitch">The channel's new pitch.</param> /// <param name="time">The time over which to perform the slide.</param> public static void SlidePitch(UInt32 handle, Single pitch, TimeSpan time) { if (!BASSNative.ChannelSlideAttribute(handle, BASSAttrib.ATTRIB_TEMPO_PITCH, pitch * SemitonesPerOctave, (uint)time.TotalMilliseconds)) { throw new BASSException(); } var tempo = (Math.Pow(2.0, pitch) - 1.0) * 100.0; if (!BASSNative.ChannelSlideAttribute(handle, BASSAttrib.ATTRIB_TEMPO, (float)tempo, (uint)time.TotalMilliseconds)) { throw new BASSException(); } }
/// <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(); } }
/// <summary> /// Releases resources associated with the object. /// </summary> /// <param name="disposing">true if the object is being disposed; false if the object is being finalized.</param> protected override void Dispose(Boolean disposing) { if (!BASSNative.Free()) { throw new BASSException(); } if (disposing && !Ultraviolet.Disposed) { Ultraviolet.Messages.Unsubscribe(this); } base.Dispose(disposing); }
public BASSUltravioletAudio(UltravioletContext uv) : base(uv) { var device = -1; var freq = 44100u; if (!BASSNative.Init(device, freq, 0, IntPtr.Zero, IntPtr.Zero)) { throw new BASSException(); } uv.Messages.Subscribe(this, UltravioletMessages.ApplicationSuspending); uv.Messages.Subscribe(this, UltravioletMessages.ApplicationResumed); }
/// <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(); } }
/// <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(); } }
/// <summary> /// Sets the volume of the specified channel. /// </summary> /// <param name="handle">The handle that represents the channel to adjust.</param> /// <param name="volume">The channel's new volume./</param> public static void SetVolume(UInt32 handle, Single volume) { if (BASSNative.ChannelIsSliding(handle, BASSAttrib.ATTRIB_VOL)) { if (!BASSNative.ChannelSlideAttribute(handle, BASSAttrib.ATTRIB_VOL, volume, 0)) { throw new BASSException(); } } else { if (!BASSNative.ChannelSetAttribute(handle, BASSAttrib.ATTRIB_VOL, volume)) { throw new BASSException(); } } }
/// <summary> /// Sets the pan of the specified channel. /// </summary> /// <param name="handle">The handle that represents the channel to adjust.</param> /// <param name="pan">The channel's new pan.</param> public static void SetPan(UInt32 handle, Single pan) { if (BASSNative.ChannelIsSliding(handle, BASSAttrib.ATTRIB_PAN)) { if (!BASSNative.ChannelSlideAttribute(handle, BASSAttrib.ATTRIB_PAN, pan, 0)) { throw new BASSException(); } } else { if (!BASSNative.ChannelSetAttribute(handle, BASSAttrib.ATTRIB_PAN, pan)) { throw new BASSException(); } } }
/// <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); }
/// <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); }