A game framework written with osu! in mind.
at master 89 lines 4.0 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using ManagedBass; 6 7namespace osu.Framework.Audio 8{ 9 /// <summary> 10 /// A helper class for translating relative frequency values to absolute hertz values based on the initial channel frequency. 11 /// Also handles zero frequency value by requesting the component to pause the channel and maintain that until it's set back from zero. 12 /// </summary> 13 internal class BassRelativeFrequencyHandler 14 { 15 private int channel; 16 private float initialFrequency; 17 18 /// <summary> 19 /// Invoked when frequency changes from non-zero to zero via <see cref="SetFrequency"/>. 20 /// Allows the component using this instance to pause instead of changing frequency to zero 21 /// (which is not supported in BASS). 22 /// </summary> 23 public Action FrequencyChangedToZero; 24 25 /// <summary> 26 /// Invoked when frequency changes from zero to non-zero via <see cref="SetFrequency"/>. 27 /// Allows the component using this instance to revert any changes in its state 28 /// done in <see cref="FrequencyChangedToZero"/>. 29 /// </summary> 30 public Action FrequencyChangedFromZero; 31 32 /// <summary> 33 /// Whether the last <see cref="SetFrequency"/> call specified a zero relative frequency. 34 /// </summary> 35 public bool IsFrequencyZero { get; private set; } 36 37 /// <summary> 38 /// Sets the component's BASS channel handle. 39 /// </summary> 40 /// <param name="channel">The channel handle.</param> 41 public void SetChannel(int channel) 42 { 43 if (channel == 0) 44 throw new ArgumentException("Invalid channel handle specified.", nameof(channel)); 45 46 this.channel = channel; 47 IsFrequencyZero = false; 48 49 Bass.ChannelGetAttribute(this.channel, ChannelAttribute.Frequency, out initialFrequency); 50 } 51 52 /// <summary> 53 /// Sets the channel's frequency based on the given <paramref name="relativeFrequency"/>. 54 /// </summary> 55 /// <remarks> 56 /// Callers should ensure to <see cref="SetChannel"/> first before attempting to change channel frequency. 57 /// </remarks> 58 /// <param name="relativeFrequency">The desired frequency value, relative to the channel's initial frequency.</param> 59 /// <example> 60 /// A <c>SetFrequency(0.5)</c> call is equivalent to the following ManagedBASS call: 61 /// <code>BASS.ChannelSetAttribute(ChannelAttribute.Frequency, channel, initialFrequency * 0.5);</code> 62 /// </example> 63 public void SetFrequency(double relativeFrequency) 64 { 65 if (channel == 0) 66 throw new InvalidOperationException("Attempted to set the channel frequency without calling SetChannel() first."); 67 68 // http://bass.radio42.com/help/html/ff7623f0-6e9f-6be8-c8a7-17d3a6dc6d51.htm (BASS_ATTRIB_FREQ's description) 69 // Above documentation shows the frequency limits which the constants (min_bass_freq, max_bass_freq) came from. 70 const int min_bass_freq = 100; 71 const int max_bass_freq = 100000; 72 73 int channelFrequency = (int)Math.Clamp(Math.Abs(initialFrequency * relativeFrequency), min_bass_freq, max_bass_freq); 74 Bass.ChannelSetAttribute(channel, ChannelAttribute.Frequency, channelFrequency); 75 76 // Maintain internal pause on zero frequency due to BASS not supporting them (0 is took for original rate in BASS API) 77 if (!IsFrequencyZero && relativeFrequency == 0) 78 { 79 FrequencyChangedToZero?.Invoke(); 80 IsFrequencyZero = true; 81 } 82 else if (IsFrequencyZero && relativeFrequency > 0) 83 { 84 IsFrequencyZero = false; 85 FrequencyChangedFromZero?.Invoke(); 86 } 87 } 88 } 89}