Path: blob/master/Sanford.Multimedia.Midi/Messages/ChannelMessage.cs
180 views
#region License /* Copyright (c) 2005 Leslie Sanford * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #endregion #region Contact /* * Leslie Sanford * Email: [email protected] */ #endregion using System; using System.ComponentModel; using System.Diagnostics; namespace Sanford.Multimedia.Midi { #region Channel Command Types /// <summary> /// Defines constants for ChannelMessage types. /// </summary> public enum ChannelCommand { /// <summary> /// Represents the note-off command type. /// </summary> NoteOff = 0x80, /// <summary> /// Represents the note-on command type. /// </summary> NoteOn = 0x90, /// <summary> /// Represents the poly pressure (aftertouch) command type. /// </summary> PolyPressure = 0xA0, /// <summary> /// Represents the controller command type. /// </summary> Controller = 0xB0, /// <summary> /// Represents the program change command type. /// </summary> ProgramChange = 0xC0, /// <summary> /// Represents the channel pressure (aftertouch) command /// type. /// </summary> ChannelPressure = 0xD0, /// <summary> /// Represents the pitch wheel command type. /// </summary> PitchWheel = 0xE0 } #endregion #region Controller Types /// <summary> /// Defines constants for controller types. /// </summary> public enum ControllerType { /// <summary> /// The Bank Select coarse. /// </summary> BankSelect, /// <summary> /// The Modulation Wheel coarse. /// </summary> ModulationWheel, /// <summary> /// The Breath Control coarse. /// </summary> BreathControl, /// <summary> /// The Foot Pedal coarse. /// </summary> FootPedal = 4, /// <summary> /// The Portamento Time coarse. /// </summary> PortamentoTime, /// <summary> /// The Data Entry Slider coarse. /// </summary> DataEntrySlider, /// <summary> /// The Volume coarse. /// </summary> Volume, /// <summary> /// The Balance coarse. /// </summary> Balance, /// <summary> /// The Pan position coarse. /// </summary> Pan = 10, /// <summary> /// The Expression coarse. /// </summary> Expression, /// <summary> /// The Effect Control 1 coarse. /// </summary> EffectControl1, /// <summary> /// The Effect Control 2 coarse. /// </summary> EffectControl2, /// <summary> /// The General Puprose Slider 1 /// </summary> GeneralPurposeSlider1 = 16, /// <summary> /// The General Puprose Slider 2 /// </summary> GeneralPurposeSlider2, /// <summary> /// The General Puprose Slider 3 /// </summary> GeneralPurposeSlider3, /// <summary> /// The General Puprose Slider 4 /// </summary> GeneralPurposeSlider4, /// <summary> /// The Bank Select fine. /// </summary> BankSelectFine = 32, /// <summary> /// The Modulation Wheel fine. /// </summary> ModulationWheelFine, /// <summary> /// The Breath Control fine. /// </summary> BreathControlFine, /// <summary> /// The Foot Pedal fine. /// </summary> FootPedalFine = 36, /// <summary> /// The Portamento Time fine. /// </summary> PortamentoTimeFine, /// <summary> /// The Data Entry Slider fine. /// </summary> DataEntrySliderFine, /// <summary> /// The Volume fine. /// </summary> VolumeFine, /// <summary> /// The Balance fine. /// </summary> BalanceFine, /// <summary> /// The Pan position fine. /// </summary> PanFine = 42, /// <summary> /// The Expression fine. /// </summary> ExpressionFine, /// <summary> /// The Effect Control 1 fine. /// </summary> EffectControl1Fine, /// <summary> /// The Effect Control 2 fine. /// </summary> EffectControl2Fine, /// <summary> /// The Hold Pedal 1. /// </summary> HoldPedal1 = 64, /// <summary> /// The Portamento. /// </summary> Portamento, /// <summary> /// The Sustenuto Pedal. /// </summary> SustenutoPedal, /// <summary> /// The Soft Pedal. /// </summary> SoftPedal, /// <summary> /// The Legato Pedal. /// </summary> LegatoPedal, /// <summary> /// The Hold Pedal 2. /// </summary> HoldPedal2, /// <summary> /// The Sound Variation. /// </summary> SoundVariation, /// <summary> /// The Sound Timbre. /// </summary> SoundTimbre, /// <summary> /// The Sound Release Time. /// </summary> SoundReleaseTime, /// <summary> /// The Sound Attack Time. /// </summary> SoundAttackTime, /// <summary> /// The Sound Brightness. /// </summary> SoundBrightness, /// <summary> /// The Sound Control 6. /// </summary> SoundControl6, /// <summary> /// The Sound Control 7. /// </summary> SoundControl7, /// <summary> /// The Sound Control 8. /// </summary> SoundControl8, /// <summary> /// The Sound Control 9. /// </summary> SoundControl9, /// <summary> /// The Sound Control 10. /// </summary> SoundControl10, /// <summary> /// The General Purpose Button 1. /// </summary> GeneralPurposeButton1, /// <summary> /// The General Purpose Button 2. /// </summary> GeneralPurposebtn_startNote, /// <summary> /// The General Purpose Button 3. /// </summary> GeneralPurposeButton3, /// <summary> /// The General Purpose Button 4. /// </summary> GeneralPurposeButton4, /// <summary> /// The Effects Level. /// </summary> EffectsLevel = 91, /// <summary> /// The Tremelo Level. /// </summary> TremeloLevel, /// <summary> /// The Chorus Level. /// </summary> ChorusLevel, /// <summary> /// The Celeste Level. /// </summary> CelesteLevel, /// <summary> /// The Phaser Level. /// </summary> PhaserLevel, /// <summary> /// The Data Button Increment. /// </summary> DataButtonIncrement, /// <summary> /// The Data Button Decrement. /// </summary> DataButtonDecrement, /// <summary> /// The NonRegistered Parameter Fine. /// </summary> NonRegisteredParameterFine, /// <summary> /// The NonRegistered Parameter Coarse. /// </summary> NonRegisteredParameterCoarse, /// <summary> /// The Registered Parameter Fine. /// </summary> RegisteredParameterFine, /// <summary> /// The Registered Parameter Coarse. /// </summary> RegisteredParameterCoarse, /// <summary> /// The All Sound Off. /// </summary> AllSoundOff = 120, /// <summary> /// The All Controllers Off. /// </summary> AllControllersOff, /// <summary> /// The Local Keyboard. /// </summary> LocalKeyboard, /// <summary> /// The All Notes Off. /// </summary> AllNotesOff, /// <summary> /// The Omni Mode Off. /// </summary> OmniModeOff, /// <summary> /// The Omni Mode On. /// </summary> OmniModeOn, /// <summary> /// The Mono Operation. /// </summary> MonoOperation, /// <summary> /// The Poly Operation. /// </summary> PolyOperation } #endregion /// <summary> /// Represents MIDI channel messages. /// </summary> [ImmutableObject(true)] public sealed class ChannelMessage : ShortMessage { #region ChannelEventArgs Members #region Constants // // Bit manipulation constants. // private const int MidiChannelMask = ~15; private const int CommandMask = ~240; /// <summary> /// Maximum value allowed for MIDI channels. /// </summary> public const int MidiChannelMaxValue = 15; #endregion #region Construction /// <summary> /// Initializes a new instance of the ChannelEventArgs class with the /// specified command, MIDI channel, and data 1 values. /// </summary> /// <param name="command"> /// The command value. /// </param> /// <param name="midiChannel"> /// The MIDI channel. /// </param> /// <param name="data1"> /// The data 1 value. /// </param> /// <exception cref="ArgumentOutOfRangeException"> /// If midiChannel is less than zero or greater than 15. Or if /// data1 is less than zero or greater than 127. /// </exception> public ChannelMessage(ChannelCommand command, int midiChannel, int data1) { msg = 0; msg = PackCommand(msg, command); msg = PackMidiChannel(msg, midiChannel); msg = PackData1(msg, data1); #region Ensure Debug.Assert(Command == command); Debug.Assert(MidiChannel == midiChannel); Debug.Assert(Data1 == data1); #endregion } /// <summary> /// Initializes a new instance of the ChannelEventArgs class with the /// specified command, MIDI channel, data 1, and data 2 values. /// </summary> /// <param name="command"> /// The command value. /// </param> /// <param name="midiChannel"> /// The MIDI channel. /// </param> /// <param name="data1"> /// The data 1 value. /// </param> /// <param name="data2"> /// The data 2 value. /// </param> /// <exception cref="ArgumentOutOfRangeException"> /// If midiChannel is less than zero or greater than 15. Or if /// data1 or data 2 is less than zero or greater than 127. /// </exception> public ChannelMessage(ChannelCommand command, int midiChannel, int data1, int data2) { msg = 0; msg = PackCommand(msg, command); msg = PackMidiChannel(msg, midiChannel); msg = PackData1(msg, data1); msg = PackData2(msg, data2); #region Ensure Debug.Assert(Command == command); Debug.Assert(MidiChannel == midiChannel); Debug.Assert(Data1 == data1); Debug.Assert(Data2 == data2); #endregion } internal ChannelMessage(int message) { this.msg = message; } #endregion #region Methods /// <summary> /// Returns a value for the current ChannelEventArgs suitable for use in /// hashing algorithms. /// </summary> /// <returns> /// A hash code for the current ChannelEventArgs. /// </returns> public override int GetHashCode() { return msg; } /// <summary> /// Determines whether two ChannelEventArgs instances are equal. /// </summary> /// <param name="obj"> /// The ChannelMessageEventArgs to compare with the current ChannelEventArgs. /// </param> /// <returns> /// <b>true</b> if the specified object is equal to the current /// ChannelMessageEventArgs; otherwise, <b>false</b>. /// </returns> public override bool Equals(object obj) { #region Guard if(!(obj is ChannelMessage)) { return false; } #endregion ChannelMessage e = (ChannelMessage)obj; return this.msg == e.msg; } /// <summary> /// Returns a value indicating how many bytes are used for the /// specified ChannelCommand. /// </summary> /// <param name="command"> /// The ChannelCommand value to test. /// </param> /// <returns> /// The number of bytes used for the specified ChannelCommand. /// </returns> internal static int DataBytesPerType(ChannelCommand command) { int result; if(command == ChannelCommand.ChannelPressure || command == ChannelCommand.ProgramChange) { result = 1; } else { result = 2; } return result; } /// <summary> /// Unpacks the command value from the specified integer channel /// message. /// </summary> /// <param name="message"> /// The message to unpack. /// </param> /// <returns> /// The command value for the packed message. /// </returns> internal static ChannelCommand UnpackCommand(int message) { return (ChannelCommand)(message & DataMask & MidiChannelMask); } /// <summary> /// Unpacks the MIDI channel from the specified integer channel /// message. /// </summary> /// <param name="message"> /// The message to unpack. /// </param> /// <returns> /// The MIDI channel for the pack message. /// </returns> internal static int UnpackMidiChannel(int message) { return message & DataMask & CommandMask; } /// <summary> /// Packs the MIDI channel into the specified integer message. /// </summary> /// <param name="message"> /// The message into which the MIDI channel is packed. /// </param> /// <param name="midiChannel"> /// The MIDI channel to pack into the message. /// </param> /// <returns> /// An integer message. /// </returns> /// <exception cref="ArgumentOutOfRangeException"> /// If midiChannel is less than zero or greater than 15. /// </exception> internal static int PackMidiChannel(int message, int midiChannel) { #region Preconditons if(midiChannel < 0 || midiChannel > MidiChannelMaxValue) { throw new ArgumentOutOfRangeException("midiChannel", midiChannel, "MIDI channel out of range."); } #endregion return (message & MidiChannelMask) | midiChannel; } /// <summary> /// Packs the command value into an integer message. /// </summary> /// <param name="message"> /// The message into which the command is packed. /// </param> /// <param name="command"> /// The command value to pack into the message. /// </param> /// <returns> /// An integer message. /// </returns> internal static int PackCommand(int message, ChannelCommand command) { return (message & CommandMask) | (int)command; } #endregion #region Properties /// <summary> /// Gets the channel command value. /// </summary> public ChannelCommand Command { get { return UnpackCommand(msg); } } /// <summary> /// Gets the MIDI channel. /// </summary> public int MidiChannel { get { return UnpackMidiChannel(msg); } } /// <summary> /// Gets the first data value. /// </summary> public int Data1 { get { return UnpackData1(msg); } } /// <summary> /// Gets the second data value. /// </summary> public int Data2 { get { return UnpackData2(msg); } } /// <summary> /// Gets the EventType. /// </summary> public override MessageType MessageType { get { return MessageType.Channel; } } #endregion #endregion } }